From f212d7783c52463ef1be59f98d48a8e2f24d575c Mon Sep 17 00:00:00 2001 From: RektInator <7003455+RektInator@users.noreply.github.com> Date: Wed, 16 Dec 2020 20:44:46 +0100 Subject: [PATCH] Initial commit --- .github/ISSUE_TEMPLATE/bug_report.md | 35 + .github/ISSUE_TEMPLATE/feature_request.md | 24 + .gitignore | 153 + .gitmodules | 16 + LICENSE | 674 ++ README.md | 137 + appveyor.yml | 20 + dep/bin/steam_api.lib | Bin 0 -> 284684 bytes dep/libtomcrypt.lua | 43 + dep/libtommath.lua | 34 + dep/steam_api.lua | 22 + dep/steam_api/isteamapplist.h | 63 + dep/steam_api/isteamapps.h | 176 + dep/steam_api/isteamappticket.h | 28 + dep/steam_api/isteamclient.h | 520 + dep/steam_api/isteamcontroller.h | 440 + dep/steam_api/isteamfriends.h | 636 ++ dep/steam_api/isteamgamecoordinator.h | 75 + dep/steam_api/isteamgameserver.h | 387 + dep/steam_api/isteamgameserverstats.h | 101 + dep/steam_api/isteamhtmlsurface.h | 453 + dep/steam_api/isteamhttp.h | 210 + dep/steam_api/isteaminventory.h | 382 + dep/steam_api/isteammasterserverupdater.h | 1 + dep/steam_api/isteammatchmaking.h | 751 ++ dep/steam_api/isteammusic.h | 67 + dep/steam_api/isteammusicremote.h | 129 + dep/steam_api/isteamnetworking.h | 306 + dep/steam_api/isteamps3overlayrenderer.h | 91 + dep/steam_api/isteamremotestorage.h | 681 ++ dep/steam_api/isteamscreenshots.h | 116 + dep/steam_api/isteamugc.h | 484 + dep/steam_api/isteamunifiedmessages.h | 63 + dep/steam_api/isteamuser.h | 369 + dep/steam_api/isteamuserstats.h | 476 + dep/steam_api/isteamutils.h | 264 + dep/steam_api/isteamvideo.h | 71 + dep/steam_api/matchmakingtypes.h | 251 + dep/steam_api/steam_api.h | 394 + dep/steam_api/steam_api_flat.h | 816 ++ dep/steam_api/steam_api_internal.h | 327 + dep/steam_api/steam_gameserver.h | 243 + dep/steam_api/steamclientpublic.h | 1235 +++ dep/steam_api/steamencryptedappticket.h | 32 + dep/steam_api/steamhttpenums.h | 98 + dep/steam_api/steamps3params.h | 112 + dep/steam_api/steamtypes.h | 181 + dep/steam_api/steamuniverse.h | 27 + dep/zlib.lua | 39 + dep/zstd.lua | 37 + generate.bat | 5 + plutonium_logo.jpg | Bin 0 -> 32511 bytes premake5.lua | 123 + src/CODO.lua | 34 + src/CODO/Assets/LocalizeEntry.cpp | 63 + src/CODO/Assets/LocalizeEntry.hpp | 33 + src/CODO/Assets/StringTable.cpp | 229 + src/CODO/Assets/StringTable.hpp | 33 + src/CODO/CODO.cpp | 140 + src/CODO/CODO.hpp | 45 + src/CODO/Functions.hpp | 32 + src/CODO/Structs.hpp | 3444 +++++++ src/CODO/Zone.cpp | 298 + src/CODO/Zone.hpp | 69 + src/CODO/stdafx.cpp | 9 + src/CODO/stdafx.hpp | 29 + src/IW3.lua | 35 + src/IW3/Assets/ClipMap.cpp | 215 + src/IW3/Assets/ClipMap.hpp | 21 + src/IW3/Assets/ComWorld.cpp | 18 + src/IW3/Assets/ComWorld.hpp | 18 + src/IW3/Assets/FxEffectDef.cpp | 54 + src/IW3/Assets/FxEffectDef.hpp | 21 + src/IW3/Assets/GameWorldMp.cpp | 20 + src/IW3/Assets/GameWorldMp.hpp | 21 + src/IW3/Assets/GfxImage.cpp | 54 + src/IW3/Assets/GfxImage.hpp | 22 + src/IW3/Assets/GfxWorld.cpp | 353 + src/IW3/Assets/GfxWorld.hpp | 21 + src/IW3/Assets/LoadedSound.cpp | 92 + src/IW3/Assets/LoadedSound.hpp | 21 + src/IW3/Assets/MapEnts.cpp | 261 + src/IW3/Assets/MapEnts.hpp | 21 + src/IW3/Assets/Material.cpp | 249 + src/IW3/Assets/Material.hpp | 22 + src/IW3/Assets/Sound.cpp | 34 + src/IW3/Assets/Sound.hpp | 21 + src/IW3/Assets/Techset.cpp | 449 + src/IW3/Assets/Techset.hpp | 27 + src/IW3/Assets/XAnimParts.cpp | 76 + src/IW3/Assets/XAnimParts.hpp | 21 + src/IW3/Assets/XModel.cpp | 146 + src/IW3/Assets/XModel.hpp | 21 + src/IW3/Functions.hpp | 38 + src/IW3/IW3.cpp | 365 + src/IW3/IW3.hpp | 70 + src/IW3/Structs.hpp | 1930 ++++ src/IW3/stdafx.cpp | 10 + src/IW3/stdafx.hpp | 21 + src/IW4.lua | 36 + src/IW4/Assets/AddonMapEnts.cpp | 147 + src/IW4/Assets/AddonMapEnts.hpp | 35 + src/IW4/Assets/ClipMap.cpp | 631 ++ src/IW4/Assets/ClipMap.hpp | 35 + src/IW4/Assets/ComWorld.cpp | 131 + src/IW4/Assets/ComWorld.hpp | 35 + src/IW4/Assets/FontDef.cpp | 101 + src/IW4/Assets/FontDef.hpp | 34 + src/IW4/Assets/FxEffectDef.cpp | 403 + src/IW4/Assets/FxEffectDef.hpp | 41 + src/IW4/Assets/FxWorld.cpp | 209 + src/IW4/Assets/FxWorld.hpp | 35 + src/IW4/Assets/GameWorldMp.cpp | 26 + src/IW4/Assets/GameWorldMp.hpp | 24 + src/IW4/Assets/GameWorldSp.cpp | 119 + src/IW4/Assets/GameWorldSp.hpp | 35 + src/IW4/Assets/GfxImage.cpp | 423 + src/IW4/Assets/GfxImage.hpp | 40 + src/IW4/Assets/GfxWorld.cpp | 850 ++ src/IW4/Assets/GfxWorld.hpp | 37 + src/IW4/Assets/LightDef.cpp | 125 + src/IW4/Assets/LightDef.hpp | 38 + src/IW4/Assets/LoadedSound.cpp | 260 + src/IW4/Assets/LoadedSound.hpp | 38 + src/IW4/Assets/LocalizeEntry.cpp | 72 + src/IW4/Assets/LocalizeEntry.hpp | 38 + src/IW4/Assets/MapEnts.cpp | 403 + src/IW4/Assets/MapEnts.hpp | 42 + src/IW4/Assets/Material.cpp | 563 ++ src/IW4/Assets/Material.hpp | 39 + src/IW4/Assets/PhysCollmap.cpp | 270 + src/IW4/Assets/PhysCollmap.hpp | 37 + src/IW4/Assets/PhysPreset.cpp | 60 + src/IW4/Assets/PhysPreset.hpp | 33 + src/IW4/Assets/PixelShader.cpp | 85 + src/IW4/Assets/PixelShader.hpp | 37 + src/IW4/Assets/RawFile.cpp | 133 + src/IW4/Assets/RawFile.hpp | 35 + src/IW4/Assets/Sound.cpp | 245 + src/IW4/Assets/Sound.hpp | 38 + src/IW4/Assets/SoundCurve.cpp | 57 + src/IW4/Assets/SoundCurve.hpp | 33 + src/IW4/Assets/StringTable.cpp | 222 + src/IW4/Assets/StringTable.hpp | 33 + src/IW4/Assets/StructuredDataDef.cpp | 294 + src/IW4/Assets/StructuredDataDef.hpp | 65 + src/IW4/Assets/Techset.cpp | 593 ++ src/IW4/Assets/Techset.hpp | 39 + src/IW4/Assets/TracerDef.cpp | 62 + src/IW4/Assets/TracerDef.hpp | 33 + src/IW4/Assets/VertexDecl.cpp | 74 + src/IW4/Assets/VertexDecl.hpp | 36 + src/IW4/Assets/VertexShader.cpp | 81 + src/IW4/Assets/VertexShader.hpp | 38 + src/IW4/Assets/WeaponDef.cpp | 85 + src/IW4/Assets/WeaponDef.hpp | 47 + src/IW4/Assets/XAnimParts.cpp | 272 + src/IW4/Assets/XAnimParts.hpp | 36 + src/IW4/Assets/XSurface.cpp | 287 + src/IW4/Assets/XSurface.hpp | 39 + src/IW4/Assets/Xmodel.cpp | 537 + src/IW4/Assets/Xmodel.hpp | 44 + src/IW4/Functions.hpp | 35 + src/IW4/IW4.cpp | 874 ++ src/IW4/IW4.hpp | 58 + src/IW4/Structs.hpp | 3864 +++++++ src/IW4/Zone.cpp | 408 + src/IW4/Zone.hpp | 75 + src/IW4/stdafx.cpp | 9 + src/IW4/stdafx.hpp | 34 + src/IW5.lua | 37 + src/IW5/Assets/AttachmentDef.cpp | 522 + src/IW5/Assets/AttachmentDef.hpp | 35 + src/IW5/Assets/ClipMap.cpp | 1011 ++ src/IW5/Assets/ClipMap.hpp | 36 + src/IW5/Assets/ComWorld.cpp | 160 + src/IW5/Assets/ComWorld.hpp | 35 + src/IW5/Assets/FontDef.cpp | 190 + src/IW5/Assets/FontDef.hpp | 37 + src/IW5/Assets/FxEffectDef.cpp | 743 ++ src/IW5/Assets/FxEffectDef.hpp | 42 + src/IW5/Assets/FxWorld.cpp | 284 + src/IW5/Assets/FxWorld.hpp | 36 + src/IW5/Assets/GfxImage.cpp | 435 + src/IW5/Assets/GfxImage.hpp | 45 + src/IW5/Assets/GfxWorld.cpp | 1293 +++ src/IW5/Assets/GfxWorld.hpp | 37 + src/IW5/Assets/GlassWorld.cpp | 200 + src/IW5/Assets/GlassWorld.hpp | 38 + src/IW5/Assets/LeaderBoardDef.cpp | 82 + src/IW5/Assets/LeaderBoardDef.hpp | 33 + src/IW5/Assets/LightDef.cpp | 139 + src/IW5/Assets/LightDef.hpp | 36 + src/IW5/Assets/LoadedSound.cpp | 250 + src/IW5/Assets/LoadedSound.hpp | 35 + src/IW5/Assets/LocalizeEntry.cpp | 60 + src/IW5/Assets/LocalizeEntry.hpp | 33 + src/IW5/Assets/MapEnts.cpp | 285 + src/IW5/Assets/MapEnts.hpp | 39 + src/IW5/Assets/Material.cpp | 613 ++ src/IW5/Assets/Material.hpp | 35 + src/IW5/Assets/MenuDef.cpp | 76 + src/IW5/Assets/MenuDef.hpp | 34 + src/IW5/Assets/PhysCollmap.cpp | 264 + src/IW5/Assets/PhysCollmap.hpp | 37 + src/IW5/Assets/PhysPreset.cpp | 60 + src/IW5/Assets/PhysPreset.hpp | 33 + src/IW5/Assets/PixelShader.cpp | 127 + src/IW5/Assets/PixelShader.hpp | 35 + src/IW5/Assets/RawFile.cpp | 133 + src/IW5/Assets/RawFile.hpp | 35 + src/IW5/Assets/ScriptFile.cpp | 158 + src/IW5/Assets/ScriptFile.hpp | 35 + src/IW5/Assets/Sound.cpp | 332 + src/IW5/Assets/Sound.hpp | 38 + src/IW5/Assets/SoundCurve.cpp | 57 + src/IW5/Assets/SoundCurve.hpp | 33 + src/IW5/Assets/StringTable.cpp | 223 + src/IW5/Assets/StringTable.hpp | 33 + src/IW5/Assets/StructuredDataDef.cpp | 424 + src/IW5/Assets/StructuredDataDef.hpp | 71 + src/IW5/Assets/Techset.cpp | 482 + src/IW5/Assets/Techset.hpp | 41 + src/IW5/Assets/TracerDef.cpp | 62 + src/IW5/Assets/TracerDef.hpp | 33 + src/IW5/Assets/VertexDecl.cpp | 100 + src/IW5/Assets/VertexDecl.hpp | 36 + src/IW5/Assets/VertexShader.cpp | 134 + src/IW5/Assets/VertexShader.hpp | 36 + src/IW5/Assets/WeaponDef.cpp | 2336 +++++ src/IW5/Assets/WeaponDef.hpp | 41 + src/IW5/Assets/XAnimParts.cpp | 646 ++ src/IW5/Assets/XAnimParts.hpp | 43 + src/IW5/Assets/XModel.cpp | 452 + src/IW5/Assets/XModel.hpp | 42 + src/IW5/Assets/XSurface.cpp | 274 + src/IW5/Assets/XSurface.hpp | 40 + src/IW5/Functions.hpp | 32 + src/IW5/IW5.cpp | 174 + src/IW5/IW5.hpp | 48 + src/IW5/Patches/AssetHandler.cpp | 541 + src/IW5/Patches/AssetHandler.hpp | 77 + src/IW5/Patches/FFCompression.cpp | 429 + src/IW5/Patches/FFCompression.hpp | 82 + src/IW5/Structs.hpp | 4909 +++++++++ src/IW5/Zone.cpp | 296 + src/IW5/Zone.hpp | 77 + src/IW5/stdafx.cpp | 9 + src/IW5/stdafx.hpp | 28 + src/ImgPak.lua | 41 + src/ZoneTool.lua | 64 + src/ZoneTool/DllProxy.cpp | 145 + src/ZoneTool/ZoneTool.cpp | 607 ++ src/ZoneTool/ZoneTool.hpp | 26 + src/ZoneTool/dllmain.cpp | 27 + src/ZoneTool/stdafx.cpp | 9 + src/ZoneTool/stdafx.hpp | 47 + src/ZoneUtils.lua | 38 + src/ZoneUtils/CSV.cpp | 305 + src/ZoneUtils/CSV.hpp | 58 + src/ZoneUtils/IAsset.hpp | 40 + src/ZoneUtils/IPatch.hpp | 17 + src/ZoneUtils/Json.hpp | 10623 ++++++++++++++++++++ src/ZoneUtils/Linker.hpp | 35 + src/ZoneUtils/Utils/BinaryDumper.cpp | 13 + src/ZoneUtils/Utils/BinaryDumper.hpp | 567 ++ src/ZoneUtils/Utils/Expressions.hpp | 172 + src/ZoneUtils/Utils/FileReader.cpp | 26 + src/ZoneUtils/Utils/FileReader.hpp | 72 + src/ZoneUtils/Utils/FileSystem.cpp | 309 + src/ZoneUtils/Utils/FileSystem.hpp | 51 + src/ZoneUtils/Utils/Function.hpp | 54 + src/ZoneUtils/Utils/Memory.cpp | 83 + src/ZoneUtils/Utils/Memory.hpp | 161 + src/ZoneUtils/Utils/PakFile.cpp | 63 + src/ZoneUtils/Utils/PakFile.hpp | 27 + src/ZoneUtils/Utils/Swizzle.cpp | 10 + src/ZoneUtils/Utils/Swizzle.hpp | 6 + src/ZoneUtils/Zone/PrivateKey.hpp | 307 + src/ZoneUtils/Zone/Zone.cpp | 13 + src/ZoneUtils/Zone/Zone.hpp | 55 + src/ZoneUtils/Zone/ZoneBuffer.cpp | 441 + src/ZoneUtils/Zone/ZoneBuffer.hpp | 356 + src/ZoneUtils/Zone/ZoneMemory.hpp | 110 + src/ZoneUtils/ZoneUtils.hpp | 314 + src/ZoneUtils/stdafx.cpp | 9 + src/ZoneUtils/stdafx.hpp | 23 + src/imgpak/main.cpp | 288 + src/imgpak/stdafx.cpp | 1 + src/imgpak/stdafx.hpp | 5 + tools/premake5.exe | Bin 0 -> 1362432 bytes 291 files changed, 72238 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 LICENSE create mode 100644 README.md create mode 100644 appveyor.yml create mode 100644 dep/bin/steam_api.lib create mode 100644 dep/libtomcrypt.lua create mode 100644 dep/libtommath.lua create mode 100644 dep/steam_api.lua create mode 100644 dep/steam_api/isteamapplist.h create mode 100644 dep/steam_api/isteamapps.h create mode 100644 dep/steam_api/isteamappticket.h create mode 100644 dep/steam_api/isteamclient.h create mode 100644 dep/steam_api/isteamcontroller.h create mode 100644 dep/steam_api/isteamfriends.h create mode 100644 dep/steam_api/isteamgamecoordinator.h create mode 100644 dep/steam_api/isteamgameserver.h create mode 100644 dep/steam_api/isteamgameserverstats.h create mode 100644 dep/steam_api/isteamhtmlsurface.h create mode 100644 dep/steam_api/isteamhttp.h create mode 100644 dep/steam_api/isteaminventory.h create mode 100644 dep/steam_api/isteammasterserverupdater.h create mode 100644 dep/steam_api/isteammatchmaking.h create mode 100644 dep/steam_api/isteammusic.h create mode 100644 dep/steam_api/isteammusicremote.h create mode 100644 dep/steam_api/isteamnetworking.h create mode 100644 dep/steam_api/isteamps3overlayrenderer.h create mode 100644 dep/steam_api/isteamremotestorage.h create mode 100644 dep/steam_api/isteamscreenshots.h create mode 100644 dep/steam_api/isteamugc.h create mode 100644 dep/steam_api/isteamunifiedmessages.h create mode 100644 dep/steam_api/isteamuser.h create mode 100644 dep/steam_api/isteamuserstats.h create mode 100644 dep/steam_api/isteamutils.h create mode 100644 dep/steam_api/isteamvideo.h create mode 100644 dep/steam_api/matchmakingtypes.h create mode 100644 dep/steam_api/steam_api.h create mode 100644 dep/steam_api/steam_api_flat.h create mode 100644 dep/steam_api/steam_api_internal.h create mode 100644 dep/steam_api/steam_gameserver.h create mode 100644 dep/steam_api/steamclientpublic.h create mode 100644 dep/steam_api/steamencryptedappticket.h create mode 100644 dep/steam_api/steamhttpenums.h create mode 100644 dep/steam_api/steamps3params.h create mode 100644 dep/steam_api/steamtypes.h create mode 100644 dep/steam_api/steamuniverse.h create mode 100644 dep/zlib.lua create mode 100644 dep/zstd.lua create mode 100644 generate.bat create mode 100644 plutonium_logo.jpg create mode 100644 premake5.lua create mode 100644 src/CODO.lua create mode 100644 src/CODO/Assets/LocalizeEntry.cpp create mode 100644 src/CODO/Assets/LocalizeEntry.hpp create mode 100644 src/CODO/Assets/StringTable.cpp create mode 100644 src/CODO/Assets/StringTable.hpp create mode 100644 src/CODO/CODO.cpp create mode 100644 src/CODO/CODO.hpp create mode 100644 src/CODO/Functions.hpp create mode 100644 src/CODO/Structs.hpp create mode 100644 src/CODO/Zone.cpp create mode 100644 src/CODO/Zone.hpp create mode 100644 src/CODO/stdafx.cpp create mode 100644 src/CODO/stdafx.hpp create mode 100644 src/IW3.lua create mode 100644 src/IW3/Assets/ClipMap.cpp create mode 100644 src/IW3/Assets/ClipMap.hpp create mode 100644 src/IW3/Assets/ComWorld.cpp create mode 100644 src/IW3/Assets/ComWorld.hpp create mode 100644 src/IW3/Assets/FxEffectDef.cpp create mode 100644 src/IW3/Assets/FxEffectDef.hpp create mode 100644 src/IW3/Assets/GameWorldMp.cpp create mode 100644 src/IW3/Assets/GameWorldMp.hpp create mode 100644 src/IW3/Assets/GfxImage.cpp create mode 100644 src/IW3/Assets/GfxImage.hpp create mode 100644 src/IW3/Assets/GfxWorld.cpp create mode 100644 src/IW3/Assets/GfxWorld.hpp create mode 100644 src/IW3/Assets/LoadedSound.cpp create mode 100644 src/IW3/Assets/LoadedSound.hpp create mode 100644 src/IW3/Assets/MapEnts.cpp create mode 100644 src/IW3/Assets/MapEnts.hpp create mode 100644 src/IW3/Assets/Material.cpp create mode 100644 src/IW3/Assets/Material.hpp create mode 100644 src/IW3/Assets/Sound.cpp create mode 100644 src/IW3/Assets/Sound.hpp create mode 100644 src/IW3/Assets/Techset.cpp create mode 100644 src/IW3/Assets/Techset.hpp create mode 100644 src/IW3/Assets/XAnimParts.cpp create mode 100644 src/IW3/Assets/XAnimParts.hpp create mode 100644 src/IW3/Assets/XModel.cpp create mode 100644 src/IW3/Assets/XModel.hpp create mode 100644 src/IW3/Functions.hpp create mode 100644 src/IW3/IW3.cpp create mode 100644 src/IW3/IW3.hpp create mode 100644 src/IW3/Structs.hpp create mode 100644 src/IW3/stdafx.cpp create mode 100644 src/IW3/stdafx.hpp create mode 100644 src/IW4.lua create mode 100644 src/IW4/Assets/AddonMapEnts.cpp create mode 100644 src/IW4/Assets/AddonMapEnts.hpp create mode 100644 src/IW4/Assets/ClipMap.cpp create mode 100644 src/IW4/Assets/ClipMap.hpp create mode 100644 src/IW4/Assets/ComWorld.cpp create mode 100644 src/IW4/Assets/ComWorld.hpp create mode 100644 src/IW4/Assets/FontDef.cpp create mode 100644 src/IW4/Assets/FontDef.hpp create mode 100644 src/IW4/Assets/FxEffectDef.cpp create mode 100644 src/IW4/Assets/FxEffectDef.hpp create mode 100644 src/IW4/Assets/FxWorld.cpp create mode 100644 src/IW4/Assets/FxWorld.hpp create mode 100644 src/IW4/Assets/GameWorldMp.cpp create mode 100644 src/IW4/Assets/GameWorldMp.hpp create mode 100644 src/IW4/Assets/GameWorldSp.cpp create mode 100644 src/IW4/Assets/GameWorldSp.hpp create mode 100644 src/IW4/Assets/GfxImage.cpp create mode 100644 src/IW4/Assets/GfxImage.hpp create mode 100644 src/IW4/Assets/GfxWorld.cpp create mode 100644 src/IW4/Assets/GfxWorld.hpp create mode 100644 src/IW4/Assets/LightDef.cpp create mode 100644 src/IW4/Assets/LightDef.hpp create mode 100644 src/IW4/Assets/LoadedSound.cpp create mode 100644 src/IW4/Assets/LoadedSound.hpp create mode 100644 src/IW4/Assets/LocalizeEntry.cpp create mode 100644 src/IW4/Assets/LocalizeEntry.hpp create mode 100644 src/IW4/Assets/MapEnts.cpp create mode 100644 src/IW4/Assets/MapEnts.hpp create mode 100644 src/IW4/Assets/Material.cpp create mode 100644 src/IW4/Assets/Material.hpp create mode 100644 src/IW4/Assets/PhysCollmap.cpp create mode 100644 src/IW4/Assets/PhysCollmap.hpp create mode 100644 src/IW4/Assets/PhysPreset.cpp create mode 100644 src/IW4/Assets/PhysPreset.hpp create mode 100644 src/IW4/Assets/PixelShader.cpp create mode 100644 src/IW4/Assets/PixelShader.hpp create mode 100644 src/IW4/Assets/RawFile.cpp create mode 100644 src/IW4/Assets/RawFile.hpp create mode 100644 src/IW4/Assets/Sound.cpp create mode 100644 src/IW4/Assets/Sound.hpp create mode 100644 src/IW4/Assets/SoundCurve.cpp create mode 100644 src/IW4/Assets/SoundCurve.hpp create mode 100644 src/IW4/Assets/StringTable.cpp create mode 100644 src/IW4/Assets/StringTable.hpp create mode 100644 src/IW4/Assets/StructuredDataDef.cpp create mode 100644 src/IW4/Assets/StructuredDataDef.hpp create mode 100644 src/IW4/Assets/Techset.cpp create mode 100644 src/IW4/Assets/Techset.hpp create mode 100644 src/IW4/Assets/TracerDef.cpp create mode 100644 src/IW4/Assets/TracerDef.hpp create mode 100644 src/IW4/Assets/VertexDecl.cpp create mode 100644 src/IW4/Assets/VertexDecl.hpp create mode 100644 src/IW4/Assets/VertexShader.cpp create mode 100644 src/IW4/Assets/VertexShader.hpp create mode 100644 src/IW4/Assets/WeaponDef.cpp create mode 100644 src/IW4/Assets/WeaponDef.hpp create mode 100644 src/IW4/Assets/XAnimParts.cpp create mode 100644 src/IW4/Assets/XAnimParts.hpp create mode 100644 src/IW4/Assets/XSurface.cpp create mode 100644 src/IW4/Assets/XSurface.hpp create mode 100644 src/IW4/Assets/Xmodel.cpp create mode 100644 src/IW4/Assets/Xmodel.hpp create mode 100644 src/IW4/Functions.hpp create mode 100644 src/IW4/IW4.cpp create mode 100644 src/IW4/IW4.hpp create mode 100644 src/IW4/Structs.hpp create mode 100644 src/IW4/Zone.cpp create mode 100644 src/IW4/Zone.hpp create mode 100644 src/IW4/stdafx.cpp create mode 100644 src/IW4/stdafx.hpp create mode 100644 src/IW5.lua create mode 100644 src/IW5/Assets/AttachmentDef.cpp create mode 100644 src/IW5/Assets/AttachmentDef.hpp create mode 100644 src/IW5/Assets/ClipMap.cpp create mode 100644 src/IW5/Assets/ClipMap.hpp create mode 100644 src/IW5/Assets/ComWorld.cpp create mode 100644 src/IW5/Assets/ComWorld.hpp create mode 100644 src/IW5/Assets/FontDef.cpp create mode 100644 src/IW5/Assets/FontDef.hpp create mode 100644 src/IW5/Assets/FxEffectDef.cpp create mode 100644 src/IW5/Assets/FxEffectDef.hpp create mode 100644 src/IW5/Assets/FxWorld.cpp create mode 100644 src/IW5/Assets/FxWorld.hpp create mode 100644 src/IW5/Assets/GfxImage.cpp create mode 100644 src/IW5/Assets/GfxImage.hpp create mode 100644 src/IW5/Assets/GfxWorld.cpp create mode 100644 src/IW5/Assets/GfxWorld.hpp create mode 100644 src/IW5/Assets/GlassWorld.cpp create mode 100644 src/IW5/Assets/GlassWorld.hpp create mode 100644 src/IW5/Assets/LeaderBoardDef.cpp create mode 100644 src/IW5/Assets/LeaderBoardDef.hpp create mode 100644 src/IW5/Assets/LightDef.cpp create mode 100644 src/IW5/Assets/LightDef.hpp create mode 100644 src/IW5/Assets/LoadedSound.cpp create mode 100644 src/IW5/Assets/LoadedSound.hpp create mode 100644 src/IW5/Assets/LocalizeEntry.cpp create mode 100644 src/IW5/Assets/LocalizeEntry.hpp create mode 100644 src/IW5/Assets/MapEnts.cpp create mode 100644 src/IW5/Assets/MapEnts.hpp create mode 100644 src/IW5/Assets/Material.cpp create mode 100644 src/IW5/Assets/Material.hpp create mode 100644 src/IW5/Assets/MenuDef.cpp create mode 100644 src/IW5/Assets/MenuDef.hpp create mode 100644 src/IW5/Assets/PhysCollmap.cpp create mode 100644 src/IW5/Assets/PhysCollmap.hpp create mode 100644 src/IW5/Assets/PhysPreset.cpp create mode 100644 src/IW5/Assets/PhysPreset.hpp create mode 100644 src/IW5/Assets/PixelShader.cpp create mode 100644 src/IW5/Assets/PixelShader.hpp create mode 100644 src/IW5/Assets/RawFile.cpp create mode 100644 src/IW5/Assets/RawFile.hpp create mode 100644 src/IW5/Assets/ScriptFile.cpp create mode 100644 src/IW5/Assets/ScriptFile.hpp create mode 100644 src/IW5/Assets/Sound.cpp create mode 100644 src/IW5/Assets/Sound.hpp create mode 100644 src/IW5/Assets/SoundCurve.cpp create mode 100644 src/IW5/Assets/SoundCurve.hpp create mode 100644 src/IW5/Assets/StringTable.cpp create mode 100644 src/IW5/Assets/StringTable.hpp create mode 100644 src/IW5/Assets/StructuredDataDef.cpp create mode 100644 src/IW5/Assets/StructuredDataDef.hpp create mode 100644 src/IW5/Assets/Techset.cpp create mode 100644 src/IW5/Assets/Techset.hpp create mode 100644 src/IW5/Assets/TracerDef.cpp create mode 100644 src/IW5/Assets/TracerDef.hpp create mode 100644 src/IW5/Assets/VertexDecl.cpp create mode 100644 src/IW5/Assets/VertexDecl.hpp create mode 100644 src/IW5/Assets/VertexShader.cpp create mode 100644 src/IW5/Assets/VertexShader.hpp create mode 100644 src/IW5/Assets/WeaponDef.cpp create mode 100644 src/IW5/Assets/WeaponDef.hpp create mode 100644 src/IW5/Assets/XAnimParts.cpp create mode 100644 src/IW5/Assets/XAnimParts.hpp create mode 100644 src/IW5/Assets/XModel.cpp create mode 100644 src/IW5/Assets/XModel.hpp create mode 100644 src/IW5/Assets/XSurface.cpp create mode 100644 src/IW5/Assets/XSurface.hpp create mode 100644 src/IW5/Functions.hpp create mode 100644 src/IW5/IW5.cpp create mode 100644 src/IW5/IW5.hpp create mode 100644 src/IW5/Patches/AssetHandler.cpp create mode 100644 src/IW5/Patches/AssetHandler.hpp create mode 100644 src/IW5/Patches/FFCompression.cpp create mode 100644 src/IW5/Patches/FFCompression.hpp create mode 100644 src/IW5/Structs.hpp create mode 100644 src/IW5/Zone.cpp create mode 100644 src/IW5/Zone.hpp create mode 100644 src/IW5/stdafx.cpp create mode 100644 src/IW5/stdafx.hpp create mode 100644 src/ImgPak.lua create mode 100644 src/ZoneTool.lua create mode 100644 src/ZoneTool/DllProxy.cpp create mode 100644 src/ZoneTool/ZoneTool.cpp create mode 100644 src/ZoneTool/ZoneTool.hpp create mode 100644 src/ZoneTool/dllmain.cpp create mode 100644 src/ZoneTool/stdafx.cpp create mode 100644 src/ZoneTool/stdafx.hpp create mode 100644 src/ZoneUtils.lua create mode 100644 src/ZoneUtils/CSV.cpp create mode 100644 src/ZoneUtils/CSV.hpp create mode 100644 src/ZoneUtils/IAsset.hpp create mode 100644 src/ZoneUtils/IPatch.hpp create mode 100644 src/ZoneUtils/Json.hpp create mode 100644 src/ZoneUtils/Linker.hpp create mode 100644 src/ZoneUtils/Utils/BinaryDumper.cpp create mode 100644 src/ZoneUtils/Utils/BinaryDumper.hpp create mode 100644 src/ZoneUtils/Utils/Expressions.hpp create mode 100644 src/ZoneUtils/Utils/FileReader.cpp create mode 100644 src/ZoneUtils/Utils/FileReader.hpp create mode 100644 src/ZoneUtils/Utils/FileSystem.cpp create mode 100644 src/ZoneUtils/Utils/FileSystem.hpp create mode 100644 src/ZoneUtils/Utils/Function.hpp create mode 100644 src/ZoneUtils/Utils/Memory.cpp create mode 100644 src/ZoneUtils/Utils/Memory.hpp create mode 100644 src/ZoneUtils/Utils/PakFile.cpp create mode 100644 src/ZoneUtils/Utils/PakFile.hpp create mode 100644 src/ZoneUtils/Utils/Swizzle.cpp create mode 100644 src/ZoneUtils/Utils/Swizzle.hpp create mode 100644 src/ZoneUtils/Zone/PrivateKey.hpp create mode 100644 src/ZoneUtils/Zone/Zone.cpp create mode 100644 src/ZoneUtils/Zone/Zone.hpp create mode 100644 src/ZoneUtils/Zone/ZoneBuffer.cpp create mode 100644 src/ZoneUtils/Zone/ZoneBuffer.hpp create mode 100644 src/ZoneUtils/Zone/ZoneMemory.hpp create mode 100644 src/ZoneUtils/ZoneUtils.hpp create mode 100644 src/ZoneUtils/stdafx.cpp create mode 100644 src/ZoneUtils/stdafx.hpp create mode 100644 src/imgpak/main.cpp create mode 100644 src/imgpak/stdafx.cpp create mode 100644 src/imgpak/stdafx.hpp create mode 100644 tools/premake5.exe diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..cf758e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: RektInator + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**General information:** + - OS: [e.g. Windows 7] + - Version: [e.g. 1.0.118] + - Linker: [e.g. IW4] + +**Crash dump** +Link referring to the crashdump here (if there is a crashdump) + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..483c8b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE]" +labels: enhancement, feature +assignees: RektInator + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**General information** + - Linker: [e.g. IW4, IW5] # the linkers that are involved in the feature request + - Type: [e.g. Asset Support, Dumping, Porting, General, QoL (features that make your life easier)] # type of feature request + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81d0321 --- /dev/null +++ b/.gitignore @@ -0,0 +1,153 @@ +### Windows + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Shortcuts +*.lnk + +### OSX + +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Visual Studio + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +build + +# Visual Studio 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +### IDA +*.id0 +*.id1 +*.id2 +*.nam +*.til + +### Custom user files +# User scripts +user*.bat + +## Dependencies +dep/libtomcrypt/ +dep/libtommath/ +dep/zlib/ +dep/zstd/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c04cdbb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,16 @@ +[submodule "dep/libtomcrypt"] + path = dep/libtomcrypt + url = https://github.com/libtom/libtomcrypt + branch = master +[submodule "dep/libtommath"] + path = dep/libtommath + url = https://github.com/libtom/libtommath + branch = master +[submodule "dep/zlib"] + path = dep/zlib + url = https://github.com/madler/zlib.git + branch = master +[submodule "dep/zstd"] + path = dep/zstd + url = https://github.com/facebook/zstd.git + branch = master diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b075ff --- /dev/null +++ b/README.md @@ -0,0 +1,137 @@ +[![Build status](https://ci.appveyor.com/api/projects/status/wknvjcd9nsn29dep?svg=true)](https://ci.appveyor.com/project/RektInator/zonetool) +![license](https://img.shields.io/github/license/ZoneTool/zonetool.svg) +![stars](https://img.shields.io/github/stars/ZoneTool/zonetool.svg) +![GitHub forks](https://img.shields.io/github/forks/ZoneTool/zonetool) +![GitHub issues](https://img.shields.io/github/issues/ZoneTool/zonetool) +

Plutonium + +# zonetool +zonetool, a fastfile linker for various Call of Duty titles. + +## Folder structure +Call of Duty\ +| - zone_source\ +| - zonetool\ +| - zonetool.exe\ +| - zonetool.dll + +## Usage +Simply put the output DLL in your game directory and run zonetool.exe (For IW3, rename the DLL to zoneiw3.dll) + +## Commands +``buildzone `` - builds the specified zone.\ +``loadzone `` - loads the specified zone into memory.\ +``dumpzone `` - dumps all assets from the specified zone. + +## Supported asset types +The following asset types can be linked by ZoneTool: + +| Asset Type | IW4 | IW5 | +|-------------|-----|-----| +| PhysPreset | âœ”ï¸ | âœ”ï¸ | +| PhysCollmap | âœ”ï¸ | âœ”ï¸ | +| XAnimParts | âœ”ï¸ | âœ”ï¸ | +| XModelSurfs | âœ”ï¸ | âœ”ï¸ | +| XModel | âœ”ï¸ | âœ”ï¸ | +| Material | âœ”ï¸ | âœ”ï¸ | +| PixelShader | âœ”ï¸ | âœ”ï¸ | +| VertexShader | âœ”ï¸ | âœ”ï¸ | +| VertexDecl | âœ”ï¸ | âœ”ï¸ | +| Techset | âœ”ï¸ | âœ”ï¸ | +| Image | âœ”ï¸ | âœ”ï¸ | +| Sound | âœ”ï¸ | âœ”ï¸ | +| SndCurve | âœ”ï¸ | âœ”ï¸ | +| LoadedSound | âœ”ï¸ | âœ”ï¸ | +| CollisionMap | âœ”ï¸ | âœ”ï¸ | +| ComMap | âœ”ï¸ | âœ”ï¸ | +| GlassMap | âœ”ï¸ | âœ”ï¸ | +| MapEnts | âœ”ï¸ | âœ”ï¸ | +| FxMap | âœ”ï¸ | âœ”ï¸ | +| GfxMap | âœ”ï¸ | âœ”ï¸ | +| Font | âœ”ï¸ | âœ”ï¸ | +| MenuFile | ⌠| ⌠| +| Menu | ⌠| ⌠| +| Localize | âœ”ï¸ | âœ”ï¸ | +| Attachment | - | âœ”ï¸ | +| Weapon | âœ”ï¸ | âœ”ï¸ | +| FxEffectDef | âœ”ï¸ | âœ”ï¸ | +| ImpactFx | ⌠| ⌠| +| RawFile | âœ”ï¸ | âœ”ï¸ | +| ScriptFile | - | âœ”ï¸ | +| StringTable | âœ”ï¸ | âœ”ï¸ | +| LeaderBoardDef | âœ”ï¸ | âœ”ï¸ | +| StructuredDataDef | âœ”ï¸ | âœ”ï¸ | +| Tracer | âœ”ï¸ | âœ”ï¸ | +| Vehicle | ⌠| ⌠| +| AddonMapEnts | ⌠| ⌠| + +## Supported assets for cross-engine porting +The following asset types can be ported across different games: + +| Asset Type | Supported? | +|-------------|------------| +| PhysPreset | âœ”ï¸ | +| PhysCollmap | âœ”ï¸ | +| XAnimParts | âœ”ï¸ | +| XModelSurfs | âœ”ï¸ | +| XModel | âœ”ï¸ | +| Material | âœ”ï¸ | +| PixelShader | âœ”ï¸ | +| VertexShader | âœ”ï¸ | +| VertexDecl | âœ”ï¸ | +| Techset | âœ”ï¸ | +| Image | âœ”ï¸ | +| Sound | âœ”ï¸ | +| SndCurve | âœ”ï¸ | +| LoadedSound | âœ”ï¸ | +| CollisionMap | âœ”ï¸ | +| ComMap | âœ”ï¸ | +| GlassMap | âœ”ï¸ | +| MapEnts | âœ”ï¸ | +| FxMap | âœ”ï¸ | +| GfxMap | âœ”ï¸ | +| Font | ⌠| +| MenuFile | ⌠| +| Menu | ⌠| +| Localize | âœ”ï¸ | +| Attachment | âœ”ï¸ | +| Weapon | âœ”ï¸ | +| FxEffectDef | âœ”ï¸ | +| ImpactFx | ⌠| +| RawFile | âœ”ï¸ | +| ScriptFile | âœ”ï¸ | +| StringTable | âœ”ï¸ | +| LeaderBoardDef | ⌠| +| StructuredDataDef | âœ”ï¸ | +| Tracer | âœ”ï¸ | +| Vehicle | ⌠| +| AddonMapEnts | ⌠| + +## Supported clients +ZoneTool generated fastfiles are compatible with the following clients: +* IW4x (IW4 client) +* Plutonium (IW5 client) +* Oxygen (IW5 client) + +## Credits +Special thanks to the following people: +* Laupetin +* NTAuthority +* momo5502 +* TheApadayo +* localhost +* X3RX35 +* homura +* Sofika +* Gamecheat13 + +## Discord +Join our discord server at https://discord.gg/a6JM2Tv or https://discord.gg/plutonium + +## Donate +If you like my work, feel free to contribute! + +bitcoin: 17YZtqKcFP4WiwMRZB5AE57QR4oa3fnFAM\ +eth: 0xf4f73463861eD8Ba72ac422B237c53B720c6608A + +[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JF352E6E7TL8N) \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..43597f0 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,20 @@ +version: 1.0.{build} +skip_tags: true +image: Visual Studio 2019 +configuration: release +before_build: +- git submodule update --init --recursive +- ps: tools\premake5.exe vs2019 --set-version="$env:APPVEYOR_BUILD_VERSION" +build: + project: build/zonetool.sln + verbosity: minimal +test: off +artifacts: +- path: build/bin/*.dll +- path: build/bin/*.pdb +deploy: +- provider: GitHub + auth_token: + secure: IyUTCaq8tOpcOwDfqa2bH2UCEgPNoLpgqZtJOetSchzZcs1rJT8b/oQ/DTq2ecwc + on: + branch: master \ No newline at end of file diff --git a/dep/bin/steam_api.lib b/dep/bin/steam_api.lib new file mode 100644 index 0000000000000000000000000000000000000000..c188c64e4137eb16d10de25c05c4ab4aaea049e6 GIT binary patch literal 284684 zcmeFaeS9QEwLV^5Jt86^A|fIpA|kFUEV!(&B9fPV$(m$ClHEl_!(^tD3_F>LGn4FQ zMdT_fqL+(YL@pvCA|fIpA|fIpDk3Vbh$|u@A|fIxA|fKL-{(|y_w-A>P5Ax(GoR1% z>CDVI&pCDKrK_r|s$X_wu~C{jYNvzuP5<^oZ9UvTVtrM;wy=eo6T8@TG^n?ojdn z2|F>iLyhr|^fLa@ImXYug7H5-D)EO~87Dk^pTr-l_#XHtWC{ORBJr0!87KVnW{F46 zVw~{gkrI!sXI$W3T!BZSNBGCp5`Vb}`oN!0m3aIV#tDDlDa1Lr0)N^p@zfyWgolSE z{c; z)0n^ii4q6jz#@n?hv=(3LJU5#EM0%KR+hPQ(>B zdbvdR@vK1TX-RZ#1rMK1L2eKlkoAa67N2P`Go&^IK&XHz&m%7IBAaggmx38&pG@d2cd@WERoPFsfWfm836c+Vo{6W$MB z3GYQaK{&Nv;{BK4d*Hp`AiVE1i4P(Dg!f!4@k8Vd;rmsI+xB8U;roaM;Rh#4+=@IQ zd~c1!4|jnc@Qa-#esnAI30omU_|f4Kx4{R(_imTC^*-weMI7Ra0;A@ zEAZ13CGG^Dz-_n!cWsfl`)1}7ezv2;wv(Ap_~pG4Tfs~CF}@@G{2__&?83KaH38${S=Fa+9^eSlZIEd<)5 zR{#f~?h~Gm`cK$@o5Zd+vjSoFYa|Xh0dl|#?hApo=mo&bPL{yEu|U|PCh>CU681&A zBk%;Sz)Och+>I-+KRy$lzoW!%r?3KHFZd?Vk1OzEv}uGFq0J*a4>|(y{dvG1X#WVW zgnh!UhfCnzT_Egtfy9fS0uQkF*%G_&i}V36UoP>&Gg*PK=N=)TwyrX+5> zjQNC{?v%K3Uwj5`8kYFRxy&bg9c>-q8#_pR9d(rO;QkW7L0uvI4zh&%AC`EazcG{cy0*K*Gzz*<-;O`*8 z4uu_{aFE0^AA&!?4!21ZuEFJuLAU;!61I zQi;c(!0*6g$Rom|7fbx@ZIB24dY!}*r!k*^ajU@Z;U7?V2HIhQk2aZ5I91{qyW)Gm z-z>o&Vg0XDtl@U!Mh^AG|{1AG^WsZjAr( zk`Vjg3fztRjKJ==0>3y@z#rPm1V%aY31hHL7+NiH+>Oj9tUpWQtxqzaaKf$<#~p>= zf#IhlMowlvVG#ZhjwwqVdnxk?8!iliG;aWo-CyF(iSm``{!@{(}OBNAgDLVAGV8Hu+bo`knnCEkK`64o9nG5T)A0hrq! zVh&ee_CbkF?_)lp{)EKa5f?%e=^)IGOKcfsKH=>3w5@A#9%mb<|VY(7t-)y;f?t+)d72S_w8g+C~ptF}t49%2Q;%5O`o zT8;0K?yjRG)*yX^mB=4L-!_RAXE2}8y)p!R=?1z&ybf^{cnnwI=*1F)`y&5=zBLjn zICy|JJtDCh`h+#dN%U`KK4Abdgyp+StUelYz$)+(R_-P7rYd-Vo_i!-e>n3Ay~r!V z;UV4t{|HAgi6c*7KH-SdB?iudJ)mb$;tks&54`b$5PRYZ9DSw4QKvDV@Os3Du<9y_ zBN02ovil{LUkLw!*TN3r&{ha=9||0DpTyG3nNK)unZ&Xo=mSfUPQoGZi*VRY0{k17 zG6~f2ZvfvOm-yC=j1w-}OX8F0Hxe#=o5W|(*CcGk?}Q(nE^+Z{#tEOgUgFc}8xt#th0J!q85L37U=*J0M_Bi8&%h8StAWfG8*Igb0vAz!Y_>Cc! z;tG5RJ_z8q?*KP!miYQz@DupKJt2m01ui*G;?wwzu5f$tqHam$6^1%8O~CIIdq0(Y*F_!;B~KMt`KaV1=FbBIH61-@`! zh+}aD?nL<_{0wr0&yGu6dN=CT-K;Qhro?L8qX~n%NWAG1RuI?^S70s1yo7<>CHnVg z1;QG%(S$+tX9;UCekBZ^BGGp>D-bq-gRmaF0+3n1829kqCGI+qal+60LtKX|aMuL_ zh0$x6z`7-@Kv=s)qW?Vj0rYiC49&0tVZ+&wKT2ZcCRQK}A-#m*trEwKvVs8mH^%_$ zu8k?1>&2b%1i+2%Jj^btFW50dpM{D z#ilE(LR`(;Q?J#6Qfsuns#+f}R@XIyhQ(i%f`(hfp}$^o;>`-HIm0m6{Y(j}xmE=& znQwNsjHrcgRdG5P2^w>@SjMbL1vU3b&=P^{8|tpq%9YxrIhPdTYTnp|l?{~La??zk zm$tC3YgMYQoT03RaiG{LO-&a!T0;=~n$mD*ml^2le8NbqXtA{_s0EEmsjt=w8WY8m zrJabAlUK9Yq0&9lDz;pcl90DBw~K8j{5dU)m9Ci?SJ$Peg>NuuZK^jqC_qUC3-@p^ zU8knM-Y8DGW@tvoVqv7z2!dL3s_vYhDGfdQP)|>>TJ5RN)>`&PASJBkL=&{3*r=hN z4g}36!m+wu-)LsmsHpk7N2X?5<@%;t-$YlnN;leOPqp3*%4Qn|HMO0%;;kMX9Wrxg zL@j*lYL$sfP!6NxTEuf&7Av^Hw;J_o)%jkORIqTbTh-$j5Fu${L*?zSG#v_`d}Gzk=age2q_HX`lptsm6 zuB=zf+V&~R?X0kh6Wx$TYkj>^3WkGHy z?540Pgf*NlRH2NDp%;;xo@wBoZ6(Tys`%izAX+=qs!SKFBgN^NYA}q_YDuk5Nn@jH zwly^Zr%|s)E2WKrv5ZnM^YSX@?qIS~OURm>&daJ8SJcXmd|62qTTii83aUhCPjy~a z#Yi<{b+OrxN>pnGE9(u^d{e5`@+#);zGinYh2iU9*QkjxEvI7YDr0Qjsx(D|YNE;r zs7t51De=rwRP4NJ4R#?M1 zR4vZaxDgXvF06TR6>k_e8XpBzX4eh(o4i$ut9VgjhNkMZU_A!Z)TcI)=4CaEqnqk0 zizT?aqDC#WjWjQ=;T4|tm2n%bOw8-2F~l7zayBJy;-!#P8eo;7C>2|ljz(lbqnNQ3 z&Js~KZ766o>$Tz_%AO|NI$;ebMt|;{Sz!%l+fSOIaCbD6*0ajAnMTWr)Te}PoZa(% zwQ{i89#hF0i`#fHAu0+(bE=|i&vP!Pb!-;G+;fbyG?vzJwBT0eQU;`oji1^^(UR#Z zdoDsr4I6u+bg#xhS3e5NSy5@)tcl-EO?S>pQbW%!Tlzutv1)ErU))IA@_Odbp;e9g z>`X^BtC>`B{37$#{n zQA;UmY@y33wJi-_LQcbkHovD@tT_fHA!_DxE-%Cw3!6FJN(T|zN(IS`7IjaB68RDf znmy|(wJLL!*1U+KZf-I|o7K@+=*66EwO%ZfCs`F^T{J0bY@sYjCq+6|HtN$O_1T8@ zSxX{88a11pwu!h17}FFxs8H0{>T6Qhu&?HnlB}?X6Wyu48r{ppsC#54Fg;+4qNG#O z*bsRg&Qh(#_zm|3OI=rLxU-MeDNvo;i1a$Kv>_ND(m(QvBBn^tXgyZ##GV*JEhK57 zqQ(|=tLc7S-cV^xS?Dve8b(EuF{xZ>1-+GGwQg&$tlAo@s8%DGY^ut@W0nmQgO=o| zRrLG=LAf$J-I)QEj^09lu`wBRWOP^#i;!1l*~4$pOfeEFC zL*F5^kyFyxK(-0dO?a4Y&*6lqg-`n1K_? zrM7N|7UPoP*5OD-1U6%Bjn%MP%9bFhYe}O)Ph&~m9;irSRhoT>nHSgaQcF>v!QD-! z!L@4VRowV3amjd-TT2)R1rrV+vQfj4!-shyOL%1{kNDU49ntG*1O;^uU zu{B(;Pg_Eg7uWFi2gSLd17d}|hM96DbXX2rT}z2d$zTHkk^FJBvsGG(VMay6KZ139 z?L>&BHPJ9CB4_bjvB{C_gWt(j@@LaaL*g{d7 zAEJtE^RY*zqT@$R&@Uv591Vq(9B56$sk6a49v@fa^WC9(6YI0-T**5!Ro_Gdm6>9BG}w%pZrm!2%xVQCH%jFS+QAW&6Bhb|+N5^= zG%g={J$Fm?v?v`6)pT~m_v`xli?zvF{j7GJkes-Zx34L_9-ghyrhvZM`e9=psKk}L z>V~Zki`OiftZYj|J*B14rM_BsWpW6UYbc-t^|HB;YIT$rfiDu#Q#SutZPvDJ$1>nn?M^#-0NQ1vtQ zy6tN2b~FXoU?Mlala%c$my@Y*V9#k7e5Z{Eb3um&R7yr0Vi9G3eSCc0Fq%jvI-D3x zs2KQ>-mE;zG72*87HE9kB4%dj%=#dWq0s%RJ{<>gBpv#IS@ZS9>TK||e8`e?=7Tgn zQpI+(crE+FazoA3nK!+dqo4)RPbVN*j?O%g{YTLbJsl?$G@bcDqgp)C!@AXQ@y*lm zMoVV!2`#gz+U5z)WP^~?FtE!l_QHTeX-i2PSZM;1tho#3M$SNhXjf8>DONn_dTve`Ur;%Gagkw_NkOFjC5`xO!P123DxPdpS z2joBp9h%seP{+|i=aDDBVl9J}UYuCb3Sze$LZ8zxut!mn<)Fj7O)42|L|K*;ch2Xe zlF>%3EMg=c@HBM`V$vlkt771f;zaZqaB3$5lLizz1`EkH(wPLw(n zC6$uFMwH@?Y^anBHX@f~!RW6{S9IgkWL_w>3|1niI2oofxtg_4eE zAd?0iPsg6*%FhBl$?@nt8J?{M?O-N8IkgE1c{C}BPeQvRs~zV;!dhfI$I+1oxv1Hq zukawpq4z+xAI-6_k0g$Vs?h^=dg$v`7aS;@?e}*LJ`K|uZD*_DdWNTAS;63LJ)BZ* zJ{|ii4rdR-{ee%zzJkHoI`)5*uwAy^cpCPV94;PI>6pW?@Vnk^skWr`?3U2AlgaB1 zTIg%&R!Qf27-BI&M$>Lhp1p9)ES43xCn}|6w-Gin9U4h%*{$^9wjjDh=&86TC9P$* z66cQN42aB-MBZ3uK_ZPXHS(i-f--9M6jA@EQzJ-IQ3(Zbzm<+~wTk zl~S_XkVRiAby%Pksu_FoC@$Iwg@@Z?m>CZ?&=W*WVzkm$RxE1K4P5f1QrOBVJnd@E z*K~)P<(ZQzR(^7$Gx~&vJG*5(l}|)$rLAeThQ+Fr+C>0)6Op);SI$#97nexf$~!Vu z)Gm3+yBkUySux8nf}(;d-l9ETx|>{@Jg;Q55!WBNx_}lkJBLOT&7@u@mV|Q8)ND<6 ztYkV_89$TSomdsCrx%jIP_ZLh;&i7484ES7)>drB?2lWypa4eVR$e-?f>w;XH-HHZ zBRjeV%OXHtsN zc?4axa;KS)QnRJunAy7aiCxaAP}s;xJ4WiW<#6#6n)|L&$#C9n+J;`aLz~*;)NJ-h z(P#?|cQ%2J#x$BnYdF(2I+4Mtg-XdAnNi#m!b6SvB&J~9+9;i>i#_8wFr7J}nMlLP zj-<%GopRnN%^S-a8DYJrSen9CgjGEqRr0Ksvz67wT3P!vCY^+&bCH_S&d}_5wbGp0 zfHS#qto01FTu$Xlsb{n#*MWq3L76PAn<--_jCr~tojyB>(FbWT<%4sIwYr^J#JcUd zO0ena7*0`iYJarSa?E5!l0)TAZT1GuQlo-HNVVGx()p~goTBK|{`yL@GG3|TOdY4v ztuUWt=+rvS26Aga6?Ubr(IN(Ac$}Ro)so05tp;tQRWvmzlkpseLrbgGHCmKIxs*|- z6e{(Mmf{U}v=KgK&@CU0`bNtmv*T1##)Fhqr)26>jrLNW#tfo$UO-ZF1V-EIYR!eG zR?QfNB7KYdqBD;T*^5L3~i-Ue;Y zb-e$Y)QxuIxTdODvwNPr>A(Reo(?^E8W|j?Cc|O$ATf->LJGWz%Q^fS&5Sh)DYhZC z;>XqDc3AP9rUqMdv=&ffiPp**%h{NgxKcq-%Y|a<nS4KRhp{cu<>c^mUf9^N?gl}grx+PH^6rB$%<=v#d%SkFy|Gt-04_gN^Bht zw=8Wm?cQJmoCR>6{@TE4UweK@zF!yJ_=j{MkOGlS=pT`g?i8pnwZ9oovP zYwh9~ZQ37?drn+bEmKMDqGi<8T0|tVZz5q&cMoqHQmL5u6Xb@&V^jP&6)k@_4H9cx z^CqvLmX7`H9QEuU7;XbBsnI`zaArp~GU~gkhstd4( z=SGy2r(?HGp|y`v8TpNs|LuZr+QXBIut{5Mb=?e|%$+bdg4bx-@6kFntsUI{i>0ZA z9Sk?DEv;HfYXcF)-5BTNubl`-CEt09A?)>?Qgl&?`vw9j~|jTb4ku+Yd}V4OI# z9>D8?dNb-lmlHnp)h2YW=1UbBqq@oNAWl^=DqDK;6mT1b$$lcO;lJfD?k;(4($-o{ z<(2DARUDEai%6)VJPUsrrh$|tdVOrnS(_O?{ z-*ugWh$$hbVnP88n^oF0X*nuLaOyNP?D2ULNq$`;8X3?jscfY38wAc-E@U-~y;Z!? zyV=6o^VR^TMOA!=*otDKicSt5bTvv-SQ(Gg2R9og7os@m)l_y;k)StGti(&EHw3{( z93|@nLLZ(OtxrJyaQ;^~$|slDjRn~b1SNvl}HoP%jos*_SG7G#ku0T!OL zoQf&D$si4wxu{R9orp>S4(v90Xx39%YS+zfENU?ff}-q+&OMB!RjdQW#zq{2iF0Fd z^md~$JENN{6s#&~6)Sv=t5|O^qkj{69N z)pDP)QIcVb1yx*8{lI(UbM?x2hp{NCY{4CR@@0Om7)2u`nPD3YTAQ#&3foAqNxN1a zI&^3V?`77mZ`X_KdGQ*}h&8^EYTB6uy;@6eg;vAGcI@;SaXl|qa|SalFo*8}rgzv- zZ_{o5ZK@Ev;uodm~GyVP-4Qc4Wkjyvc&*%$`>=+K|4ttE6Gl zT$PHfqS4ksakK0u7`AGpjbt=3>?0)%4Qp7ajWr)~_g4IcCV!0@26h|)(pIHoK*k$I z?e6KeWOGcXXs|Wfs7y{`DN845QRo;ftghELw&``9%!I^PPQ$>Cr4W|YJxS)2w1E{1 ze+~VoBx5XTW{cM_8CITWXfrAXe$3v*+1I4tX@^`1xiO`d!3ql3`r$S~hvibEnz1)9 z+pLsmI?{Yhb6!lxLk~U-+w~)Jv6zyFI(gt3$Do6{g|oUcEZtH_D_MzB({M;4DWKLZ z&T3!75h_awwKgn^T?}{RW;b_#WDH|wrEl|*hDQ0r?NUrphnMRNxYjztiT+Z z#ZXndqb=dcShqsSU<0Kie4K8W!b|dJ70ld2gR5w<*VwY9o0lzhCEX|0*RX8@AdSmg6?RzUHPxN2X>cCiHXvdEeU#R=@k}^%?C=EzcZ@TX``LIBRTm zQYj3jt*n%jRkR<*`$UR4p?N$_;P^($(w6n)r7Nv!@f|WbUAV=m%+{$ae z0iXu*-)ug7SQg#uLq{j#%U$P_vT3%FUv{KhVESlTo4X67&(IyInBGuRRAiOjOL zYgq*YcXV&)D1%i>1{)&3BApJiIj3f@L*1hA<|tZJhkBn^G4qERw*WJ0O@p<_Mkg7s z(8*X>Jvurx*3$^^{DJ-rAG~QIr4v?hB9aM0%%!!^-iz0$n8;09b}Qioo9N49130bU zsB2$ACK8jjr_;AvrWZd2@+~p$$xA8OZD26Dwt;sLw#1`k9FnYi;-*NMN>^>MR!(G_ zIV~Ac2R=H=M!XvfO@KHDy8{CXU6sY&U?ONZCLu4YVjOBz=*?GnDW@Z?MnT1me37Iw z9I`|hrjSXP-o~XSk0z%Pp@Ch?*Y z+SXFuW|J9W&{bIs&r+3H`Sw{n4KyW2C9Pr&6P;ZiGE_3Mo3vC`((UGOaTB#m6e?2^ z3`!~+@~+f_8RiPk*uvWGO3mElX%tl4VKh4+*J-HOkx4jfHKIj3m1 zMb9F-CW9JQe`(jVi(;R)lu@(Wp}t0?5j5B14Q~^bpxhHQ!guZJCP@@ljU;v-y2hKB z3vLBrS7Bhpl4FT1QK+iy#f^}t;TwugGF4t{?v@yoGB&WdkzyCUC0;C9GVet)C|J1R zm86c^MneI~`lZqB)S{uuw^~%T{aLNBns*e!jE*Huv=AcsR%uO%%Q}`<%h;hNvVr&9 z2aU15n)aPHIVn90X4qQg`X=2r(XPY_NL|%Vu(c)iY$#69sNwu{v>S24 zbyJ{i3Gs$-pXVJ>;0vT4kD%0bGKw@and=GvZHKk4DxHS zFaoRDo#K`<6e}82)Kx21>ysUaPNn8-$2o9GD-Ll>ipQx}_RZ7rR_f_fgEJjwk}3mr zI)r0(+MY#vO}({nk<02L4$dnL%~rL~+Y@P0=~yic2QxI+Q(lNArLNWDdRkCqI1eYE zqJ)N?9qm~Eir$`jwXT~6Nf4)m^_*ywXcH9PUyp++g3Z`I+~%<7yII?4wQUAAlP)+} zHE0{H4%8`Ioa<>)T+d7QK6MLatD|~Nv$bR`b#4&Im{L)13;Bx?K6(MjBiWQnYfgEj z(KK3HRh^%iT8J^VV#XG>f`=2ocs$S$r*`2h^8~O($7}&h%R4oYQ8Dv_Jly@!St9ak z3Om&OmT_{4ky2X0iXzn}ZO&G8T{DWiLe|U}9v!=GxL;F3GptE@Gc!g<#j<|#EyLTE zHna9jVb4)R`&NStYg^jPN^9t_I-;U`k4J_!A#UcS6Nt)XObgf1w+ch2>%V1eXjIMi za>m>;RiS3K1Lv^oQB3s818J4bc0NUEC9{o$tXnEl3JUIU1m3PoZ#PV*lJ7bN1verX zPBN7d5QD#2K{eNk&86xr-p^;{N(m~s(5JyV{@F%tZLK=r*J~&}{&q{FEwnLlTh_$bZe{F@ZCMjzu0lG+SfypMGKyC^RHvyD_$T z&D(N2FRtKCG>?fE!;nil1qFB5;X?w=`+q3{3RwlCe3nFqBpWG8Gg_6btIP!Y-+uD;896(Gs}1fLu>hSyOAEQ&4cD zdS{3YzN&=voMDm-yPv40gq$g1E2rrQ?>uQJtzeZ^g#wVWuPu!lc^77z%QoQ*;GrIA-KqYl#}Zepi{`%R8!B^7M2 z>&TXqRIr6bd9=PVdL6RCJ(aM6vz>q9#32V_t%iac|pr(jCF9>T(vlT@%F z+86=HRz(wKYVb7jYUYk;bHZv)N;CDyO$8E8CdBkS;i){P$NKFmlM>c*MhlVLNYl~^ z)>yz?Y|>&1o^E|M_!uHu2!i$8UupIZ&{C0nhI-Z*lJXxqrDLqVdo1X9$%hskU=wn)&xwW{8Y`-+2Sk)V+aE%v5j!zH1s z>anbW5iPe|BJbc}Le{{zVslW!`p-4RxuTqxc3@WM7%U9n+ylJ-(t$UYw6Rf-!j(_T z+8Ob7(4ePUamst)Xm35CV&fmcG=i(gQr5<}VJgrr#YUyKHg;vv7ak>hYi#p_rQp;=wYr65swKE%89kT@_ zcV)e#JyIkY8Tj9pHn6V8yIM!;H~`qCs3wICoIUlK`O!Km){NY60sn9W4K)H!_XxKYa zZCNwp5MEI*f|?PmES6f@wQI=&0XK3=W*c-1pKRcj>*axZ*`+YUL{`UOK|DJXk1Wg! zZIX#l=ol=l8R@OiidDx3%n6Xp2g-Rh-s!S`;P{Dq8-Ysi3qmiad@w>g&~1**3pQMawTn zZ1s6*!AhNx*0Q3sSmrC*?xti-j3UFkv24TYCMXlaT29Pep~hg3s$=SN@>*uZsY7ms zhL(L*fK7dvy>zWGDQP`xqgZR9#81~9d9(6bW{fIgUTfZ^g`GHcb3*MhmL!F=mKDzk zn>gi+0$_DCC97q`iX`#&WVe`Rq_wP=$gDN7XPa1$nMJOE6TBR zDNz$&yxL#;(wY=!N<70$n>ujM#W7G~cV_#erj=+5b3c4{n4md!i2n;d1AtzZgP?2^i16lnZ$N=hx8^y4SYC)ZC3lH!$e}cBSAeED!uqo z+lfxlC1v%D*qx8=Gd(p8TWRenkO^v~lHLYt2c7(lwX^|tm(6NddnGI=RE+%Hb@7zA z6aK7(1$aG))0Tv>dNd~|@#Yr8#g>JbPQ5ey0b4%n} zPC?I&I}v48`0k`pJaE$<$dE9g*3w()Mscep?ARle*U+=CkOeMGgPl7ouV?Nl)=EK@ z9CwXOUPI4L8M?aI?62Te!Oeh{6KOm4iP)>P^j5;Y1F5aTB>aqVmN{WZr>D1sQbGDN z=uD6ihbB9(VPqc>9cb+Pp9Q^B-G+g@lF^2E^F*m{$X<-V-4@n!%CkZGYNh%#MuWze zw!k|jkLg`)J=LPaI7OkN=kKXcqwEKR!KSFkK0Sk)FkY$Z zXDJhbvZ!nAirO6SR6;}IR=ss<7CR`D)L4MqNyLe|-Qi>K^ zDEJ*lDy8Ah?i#5Cy**OxUQO5BK^Vl+azlTvcB zrdGAs!y<)75S0ddvmB-?qlEJ|rZF@nF02cn4NLSI&aUQstwe1{6{q};wP-hVPqA7} zKiXT6s+*ErX1P$4dF`}SG#>HWrsyFA?WTzj?x4r=ZYf>>P3L~((;^Gh{})U>l-+I# zcaT)FY28;5=7oD!jfi3^ih5a_Ef%R9OWsfz)GSe6um}F+_dv*sq4lpL9gBs$OQG*( zBYCx+m3X^Bt=vR0UWsLR^-bY)oi-KD@&@L-8=?1l^1MpK-`A9mqxWlb+9;~#%iB%6 znx@Q4_w?7t$LGbid9j;7*NWQ2@;rH4p=UX&ykxrFkT=!1F_g`}dkffsy1U)H&tdB5 zhxkrqGM#5Yox;76M)i1fh{`ICe%F>6H(?!T-g@Z2hBhxRb6ieR=^H(f*;=72N8U=Z z%(Q9pa_O9-A|hS~y1*`ii>{-^ysN5$qIEd0p0?W?Q0HgCb)y4yce{C?RY^9P&NHN~ zCp$H&d$dDOutSQ@?C1SgMZo4lo}~w?ny_WDz2nYuOLrC5^X7dBQ=+BLYoucr?ej!B zEZ!4S+>|<~+3n2?j}v{7&gkEc@^BU18X*ehamAOD) z-NHeVUQWT3Hy1jXC(DcW&@%h6aFZrYPt0pW=^&|O%Nq^-QxWEcd-}!em&6X6krKL^ z&8Vd_#IYkCi-o*Pq5ozhd9`#4)1GvT`$5r!iKQkIbKI^ifTnXl@@WY}(6K9drnEOJ zYFO4xYsSUo+w3?kO&br&)U49-9p?$8h43uElYDYifpKysPt_xb4pb^ENw*8CxbhZ5 z=M{8$*`9D3*Rjw#N^4p34usC?i1P|P#wRwm5Stzr>2w_}=3R|4!pUe}J=dbGo1tCg z9W}w2?F~u4xnFTb5{;yTt+H^4N4;RS4~ph4|hTw~L+A zE!?ks8lSFfdBz@kT8-7WSc}r22}%UTJT2xdZ(z>55hhGeo>xi7HOv>vWUR8BYy?ex zPn)>noOM!kv6c5wm0N0adEK3O;l$99^}O?{gjrBaJ zBkxt{y~RvkuV+QA)R>>aD`c>EPA<~3HeH>lnECVmgy}Gv$ZMtRS=c{J-C{bwRdvqN zu^0J-BBRI=rbhJLCD^TIvipbYV3p)y&+sU{8mx-l0~HDBIt=%kV|FP*M8 ziQ{;kYF%iHXyba@DMyDM2Aj}KPXN>Vif7M2L?2y;iUh%xKb8TmxlCT z%2JIwMyqMZ6Gv;><&+X_gI2PvDBar=M;~NEnc{O1)$wKRh9T5YW~D_H>>BE$MbD#+ z`JQ5{G!?}zzsg!I6mzIg)D>^Xj%C_M;7UgoKg3@lX0-m4jwPNL#Dm&qv)IU=!=;f* zuvCL$GYB6~k4`l(Up;`knvzN{8uN)oT?060rFT|wWLM_bSPP+xbkJnb6nbvogi0jV z$7{Ov`I7#zt%GfeO{{5%6mu47n`~Mj)?HU?u}m1u{1QrsK@z*U=pgHrv$ayX;$UNujB-{+GN7Wa z7XQ&bJS?OsSLq-ag@n+dnv0*s|KZWyL&erqN?$9JWRM1iDl_@>3uE*0*UU#5pv;%? zM@m!VOys~rRLg{N{Bu?hMNfQ{32s6) zDWTBTRF5=4Qb@~@E95E;DI{H{0!l?WA*V0zIiai{?Q)?GVf**2CL+`~)I+zD{5dKK zvstl&_f7vDvh;}89A>_gtNJia(2bJoIUzS z&Tjn!XZJnKSrz~H{*$vM|KM!Tzi@Ul{y*yx&W?PNv-OX0b}z8xQO>T$?-$|sQ~%7_ zDUWlu)89Ee2iW{4&IX_2Y#3Z?{tB5VINK5W`{KGCzcv2G+2g>C;J6LH-w58V2N&1@ zuP(5=mK4~7hZfj5hZWd<%L?q+LkjG)r3JPtJ}*D0!0y54%NG~eage#{H3fDtbe7O^J&-2-- zxL)`IpKaUKXN#WevwOf<+skLyyb3%A`E2>KefIFqK0E0-K06NA-X%W!Hon^q?t5PD zvti(UukqQ`VxR2|p0jrM*$%+z2f!v|ciqKjtM~EQiSXwRTpx$r*|2}<9zNR-xNT2x z0!Jr?Xz>iwHRr?8~>kaR({lHdwc}(y#VPu2VD67 zc<^q+_fLHS@%cE?3*E)wzYj9kLgw@feO5jfdG|@5%|PZ+{J$su-wN3?-i4i z@B2|E-itiI=S%SaQ~3Y1_rX5WeJy@J`-eWOe&1(%-3C8!J?RHX%dN=A@A+&O{I>Hi ze0J-P@ZDCQ9ggcIkh>kf-3OTif8w(%fk%D{+1ruFz=`<(@HzKe8% z=gM2)H)L*x{O$Pdpx+>Wz`Yy3KLLE_!Pld1M%e}iVQaVj3T$v6lojA@uRu8h?mqze z|NH{mwts=$yla77vwMM^fbZ^m0m>0@^2<<`UQ%GSJ@EU>QFit%uqS|_mloLFz!3Cy zg#IbJfp4z@>jzGJG0NVH@Z0mi4?GFo8rp=z!GArtE`T3TLFVke3v6H5TMphcUszy! z?1|ri#n8R+Ue1mJmj4QE-hG_S{Fbxh@8_)h*PLC4?+U*|yNLgv#P>_?;cWlkqpd^x zdEG;tJp{Rn@Oh61IXm_@oLz(8@5c3feBSZ^XWPJcAmq*l$D`juIsYc|6gc);pY3#= z&o+M*F}?wLg3pV;hH?R%kMHlg9_0p@g3e_(`s~h|e6}yH!?>RN4V1gDqrJfO5nT6w z5cz`qIQMrbzxVsB@Br!s{y!bpx!=MTzPs;B(775{;Dl>X|GtdxzXCh>|1Mmo@cD_$ zP!29e8+H-O7jWz6eKvXp+L238mVu)$_1T%9Mm_ip+D+i-t59!%Yrlvx3jQhRZu^wa z4#4Nruk_htUqBtj=NfqTz8qy8pO=I4{?GbsJ@BE=p(ix0X-ZYl6MTOY zJ|7ABeV&VZ#&b|^fU|bNeFUFZKLd4dN5l}<3;)g83D5M|#J@PZ3-`5K7I-xZDj-k6a~r<92LGRr@1Ob;()&lm0oSYk z3BJdXUw?zIkD)#OE8>sOx8nbcANASW@cni8?zAV6=MRJL_o$!GKw0(+?9>A8k@$Zz z{(p!U*hM=O*q*?+hjRZE(hA&*{||g7zQgag`~mu~HxAp2c0~RAH~jh+(!|iVZ%3WQ z^;mq@kMFkO^S)0aJ+QazA>1Q?V|OmFdx6XF{oqchOMgM0fqMt|)(!4+{(&|D-*0<% zf$a_7Px(9ULy$S+5wt(RX@AB&3~3m~@0Y{=(r2M=0#`hUb^^MW{F1YMfb;L>Y@v$$t(LO#{u%iCe2iJ0QM(Jk;3pD$>@)@I}>%3K37r_P|>;JXvvfwDF4vp?Z_ zH)PL)Om_=)2RH!VUkaYBtI&q6F0gN}M0v#jM|Hv88l-0>@}v*3T!FgO4ITo1n|U4T z7qIwfd1bjUbbr;vOpf?Da?fCwJHzFOtm9TZ%QSb+PSApw6$lkvUK7#-B*P=Wh3VGnZ zLwt7mQk0*=a4*I0d*S~}{8#-TVwbZ{_Ut zA91$&V$QDr6x!oYqb%V6y|3Zyyf32f|>$I}TWVE&8}$ zK^qP3_ua(V8Rwz>1{z;QUlzZea68)MA9MDD8_~xF_5tUG;5hH6sMEj!&^;fvj)yOI zUc%XxpXY25u*+4Po%}_7{yzQ(mVX`n-|IPh;uD;;fC7BpavN-103U$IuHH*n*}Ia>;x^BsH!Hs65u2Auc&fU_asIM}`mX}@Y4(g^H`7;U)_ z{q~!2KLO@`!r6mJ%i~|;Y!o;Mu{s~I+~F43{Vwidu=&7e5u1Yz3G(3PD>yq8xbF+NuK_>6?>FQ3ad6&^ygGBB!0teK z-DR-AE_oCBkie5`3v73Mzkfg4)ir2m@!MitPr>!-KFDllpM;fh!=t2Hy|iyV?$n z-N;e6{D1R;@B0t%-}1ZpFZiAOXZ%tA7k(-K41bdUgI~cv&#&ZP;D6$OkL{EPet z{QLZR{xyCR|0chm|As%o|IWAZpYn(J@A<|2Q~YLR>~;LB{09DYehL3He~|x!e7i^%@^@k@dNpY_ZDxBx7O?PR(pfqG2WZJG4D9< zcyGYl;PrdM-rKw(ujswmd#g9<9qXOot@qY>&tNZNJFsV>^?xDThdrO|&30xxvEA6N z>}6~(wjbMtJ(s}_m}O|w(j zvFv?p3){d(**rUmjj(m>L{?!_xHGj_lby;|usJr%dfA)VajcKMhplF7*ebS?)z}QH zvp2B;Hpqt9F>EahSebRP9@fuFY$F?H4falUGJ7w33){ptvv;v~vq|=LHo@M(j%WYH zx>=D`*(dq={5<|K{(t;J{&9W*zleW=f0WPiIer>Hoqvd*!q4UJ<7e{^@^knH`2X<# zfAN#}JfGq3r^gM1}l!e7IW;H&u>zKp+)AH>)4F8)S-G(VId$zRXkz?bu*_$q!FKZFnPH}P)X z%lr9jc@ICB_wl8C1^=3NnfFEStKLoCHQtxKFL_scU-7Q;uJ^9>zTjQyeck(}_c`wd z?+Wkp-sRqP-i_WjyyN*>`6z!Ie=}dl$M|u4J)hzMpWqw#Fh7P5@e%$OUgnd$$dBbE zegYrox3M3xAF-|M$LuHU4t6{H0sB6?mHm?amfg$lX1`+hvEQJ_@N4!9b|?E8`#Jk5 z+s5u<_p?jb7ue_7)`wY90UCM4?-(%OZZ?c=&*V(twgy^FmIz0YeCq^lIL;H|uTkro1_?^;|eq4zxRVfI({2lg2I9s50di2ad0z#e3evcIrDvqu>BeD4YN5B4|qID3w_ zyZ0pfJ7eCz*&g0M*;8y!ui*WQ{fWKE+tu67_Vzq)H}6^A4&F1p=X=lgcJy}gUf|7p zr+Dx1-sPR-o$S5S+v08Z4&ZNhNlUci0+a=t%*1%Ez&5#NvR#dqb;=o!aFJ%X_*Rn&{YuFKNIoqGTo*lpzv4hwW_DXg*JCePcEoKL@SFyv`x$OVg|FW~$ znd}Vq0ro%aeD*Q+5%xj$-|R!|{p>V$I{P>~i+z+`$Sz-eY$LDo z3O|wW;UDC`%zvr>68}a1p8nqcYy21cFYsUJ@9Xd5FZTEH5B6W{ALcLfkMNiJhx>>6 zhxm*9SNpH?_xE4!@8^3Ack%z+ZT@q&`On?vKX;q|+-?4IxB36|ZnMPq*;ps%g6L)O z9F!hEMUVMAT83&6&eFQBu2jn9$h6ze+ADG51nIYhr=M_$pe9ipB(-DE4lmsinW0+J$Hxk?MK*a9csM zg}GF@St!=(lmE@kh=VG?jSVT?vz2POubi-{k_tB;i5CkS>EwG%^&%mWIPV7gsPuvu zeOYCjW8FwZZ+{QHGO~NVuT~B=t2iOo*QCuiH4&9SnOY&#Gmo=wOJc`O-Zdf7TP@jX z`sTi=vl4@m9 zfWkd5sywF>RxL$G;6~k3E!m1<1Dmm$=+QSyQ`ncfu2$JRTA9|A1CS@?tF@$Yi9_$8 zPH!gEIND#t_QWZ1()duZQJhu_hHOZ-`uBliW24x5S8Mgu8;#i+>}6FM3}3O>g?6S< zuTQL5K-T<){PG(w4S=u7OXm?xR1bL0^ zkj|Bk$qG^CH}D!AMYYX}hR+$ScH3LXIuI4NV!M2R_N9VWIo|4Ni4{#%kIaVKLanl+ zLsk#sqb979Vs3J}xKZp^wwO-Hj)*sh+B0rowpl6JoggXd{j~C}QZ|1FgVv^c!^vOC zNKa_cf?J&!Dd9#{Yfh<}v#b*s7FqewPE1q+?EJ?m0;(HGjx*iSN>$~!R#8QED}?J> zm8v~|)>q0wU6+)w5i>O&=t7DHrf;Yl$9EVC5d=3B8?-Gx49MzwePiBmT+hd75@Zge zb2%9Tb(1jS6U)hs(Kc!9aIH^D9p&r2+bxdhS|r3f=3m>W;BC!{yl5vD{)Y{FP;Ogx zPzhT_yit|fGwcmlcRAafl$x;15=YDut6sQ|zPod zXQrSaQzLCzR0itwZe)j2GSX_Ge0M-D_f=(6YCSnj z3tNwb*i?N}SE3Xtx9QVw%RntFOfmsH7|xu`F1n15%~UK1gxV?=Yqf?mF=)=TI^>!?VsE6BJ52SyvC&Gv-H&`uah! zuFs(6NgZQI5^`4OQtPNptkSMSXr!QYmRbxusgzZY=Mxc%cxUMpC{0U+C?&*b=4Pah zDFX)|+Op0`q{N9=uIFcba&CtmM!VyQ6Bj!YUR`NojG%9qtr~~d07IdG*y`ec( z$JyrqQvy$O&s|?KrS84RgL=Wj3coW?(22-Un80DXLmy zuu|Gsv$QHB)0m}q^^!UacxH)iFK)ke#SkY-|LS5>{;LhW?C207rnXEz_F;kyw=LY3 zyUNp*nw~J0lf8+xGeOPPqev`HHSm&?U@|w3*)@V%J}jP_i#tAaz^7J{QV^xxt%rn8 zZFPRME<6qbYESHzMW8vr4#GEKr4vy3&*E602}nTF4)fBC7%-)T#5Kij(sRS-bOMDCO>Y z`+a;}bCbj9Dp))k%bitjh|XAde;uRS^4fgM*Uta3^ucuEd_GYUlOk<5l~Wr@QJg1P zqt|HrHt#xX;XZy*vruE4F`Elg;5N^Me;`>4eUPJ2)x zPae^5$5zv5E^rXXS+1RJP2!a4$swFyte9s_BoGQ>wN$Au{|q86CR?xeYkDk66s5DT z*CZup68mbUI&S+8zS87c+b0teBZ^vAtzMg-uFo3FNTMYaJ!Q%yvj*J= z6Xmr_7P6=)o7|2xdn?V7EbM0KR(DGCuv5!g_q*8$N=zOe2o0+x6JJBByJqrj9E&ze(G)oOhc zDsRl1D=bNAOw*tvjz@ko;w{EzS#iS(U&w5f?4_|6a#qgvC+r!uR%{MRv#nrFajq!q znNgY!+BXW|p3q;ZZR~0d)teP%olQ74wyNGOPRY-7jME~>(@n*O*{t%}Ns?yGh@3V@ z-itRc8WVvUk0E7#0G}j;3{5%Pc-G}7dy*{uv@2$~-^en6dq~h*pGC(5MW&R0PMRbP zg)NQ=se$@je%>m_Wl4Zw+AtL;CWDexXrx-09Kj1KoC-ZA%Mci2ROIqVmwJKLj7r%g zW}<3kG^yr9L%IhKqbpXa)%8YY3m&Nzt5_?5)hI@9!HNR2vR+c$+>>#F96drA!780# zWwC@7(?&Z|*QAHc79|43ge-CXc_kynu9N+ zE3I}S7MG1tcfCBX>+;;9_XFxQU#jBIYC> zynVLP2%2d0#FDEXYW(o#Wz|y}igPBDs6W{?iSbllZ6d!GDqc!Q{X(<~({8{8Cq$NIcgZpG%(6c#}X z)28ICqD7C0IJsz+YhlXAK}z*8WKCUyat4&qoca zQm@&G(;4F-!*DRs2%1yj>+SL#+=LIqv(=#Oy!h0CeTDYZv+WdenYR?(IO-i(RcLoy zShO4+_}?6p^ZQ~$hpAjkW0AQ0q8L|MGjTaMV_jv zwC5x(N_hwFgw8kzDU^5jDC|4yhDwU4hN97~#-_AoYayaMiL~W0RSVJ$7v?NTSH6-f zDoGtW-QP91AfuU?OjoPYmS~tvWs7>CQD)HmrcE}?0dWQnxk7C#p1EUIDO?PLonET0 zf1)Um$t(??CbZ}&-@>$|)<_lGEU*kl?zHPPA%l1tCx#&la{)U!X@5?!x-fOLm*}&L zwrODm7-pg^anOx+aN!UlV~FH2JtCi#lM2ZUMN{cqhBC6&=9uOpgcrK?Tp35?=}cJ4 zvjW?@%t?%xUD6i^q7yfW*gb*m#|y@ciM%$iAXY_1YB;A>iuYHryWPnl^_tp@Bd8}L zlwoUOB*uuN(h6uOjXorunq`!x)lqgSu{6pqCg+8n3w5$Nv&J_mY-`QYf3KB=yJG!y z)7Yz^IS6`e5=aeWv`CafYV|e}bS#~bIOPT{a&U6Ov0z5rt_3raw-?Mva&(^F9hw=- ziv5Oi-``lbn0XS5%tmHQ^fq6!I93+UHX4`*N{|>eLUGY}^;XKrbnWwKyW- z-dJ0_ie82}!=x-a%(Ld8OF8T|nwY@A+UPAMi442tW+d&HE5cJZW+CcG8@=tfqpU_B zMlMvO)KUS)2?55)2nFGmi!|zJSZt#hiArL$(AJ5~C~hK&QV0cfMQ5lKDm%kz(V@Xr zeOLrDwruIKr9Z%(R9M9SRq3(z;~orscd!>d@L2 zsK^C@m^e@*A|WnLtbjP(nXpgP#X_Xgfm6|g;b_t3NL{&FG3l3BNJHJW0L%5=q$wHk zx=_1P_!Py}MksFQrGZ1jZ{?GzL`oq}%gn_3P35ECv~0UHP_@mM-L$mywjC8^$4jMR z^SZA|Jz9M)D8ZQ#UF(pasQyt2b}})HHD9rYsdfwpbi15WuVzg6oRpJWihfx-lSoEJ+TtP!uGoxLCEsVrGhzweq+%_2UXV7$ zP)n7+4d-R4ia9t*V&dH1PGq$Yj`D7Z>~Y{02j{xVxjHOJ9t&!hVWXniI?P@~exrvSQ95r$EJ4Ju#;grLh1lK~!!h*NEc?kSL7iyj@X$Kq~h&0}Wkt)eB9ky#`2wGz6dtqJVDv$X;c7pK2s z1)ky{p`7m|i&ccQiW4gcCL5~hmb_B3ne8mmv=6jCTMownvzsWHW3|8#2N`%tPhTN0Eg2kw$IXN=`q zc9+2Ja1+s;3T<;Je;hPA?Aom;>0pJ!VXWD%G|?bZt3~MCq!{!Cn--=_#YxN$J}t+U zTWYMy9j&w$lHT=|W@WrmRV}GenM@VssL9Ah84I+d!`$c*X8R+paKV{9ilzMzWz7gHX97|(@-U_LSH@7w z&P^QqoGhv@#kPHk^Vs-bWj?EMlfAm0%0S)D3+?4m;|LbF{o! z(KippXn=$zsbvQTBT-!%x@o6Nd`FQ7y^V;KV#?sjX%Y&K;b%-)Hs-Mm6W;2Ea+DCs zC4})Y?2Nc7;MQ)TkNOK*XVC?r-(oq{`dsoelnalMRZcr3#ME;h34yRF*Y#pTNjx2m zZ;)bfV!p}!&S8!DHNsXU=9*fx%mf<2@zAjCn09621o;0GX*UaL7S~E z<Xe{+0^uhr;IBLTS#T-CWU-97O}dct2VE@(ZJAfkya#h&yS+f!Azv$(Cp9_ z<~C;=HOCf$h=shdJ&UER|CEtYs@rDId#)v9>lxaHAngtY%VI@Q_U8H`cS4ln3NU$^6?q)XrnOUzU-^ z;kw-&f;{sA4mEM~Mj@(&8i^(~s8Pp2od=0gpq5BREK%E)!!GhXC#mTsrcZsQt_cd> zR2{1*WmBkJ#FIHIIP+VGmNX7G(9*EVE# z*-ZoG!gQ8XI|XUMP^XMpcbtA;3RgxCM~Glp--eP z488JMYsyAKM?^$DRjyO9$3DS7f}=ciEk-iz1K7J671Tr_SGQqFMOqHAaAk~G+6{@R zUZZ4Sq=F-T*G?o>DVf^>=-A5x}yo7 z4TKORF1v)O42i?&Y%a;pis|^dH~{F_&@-E4H80g0>ZTrZcUZB8F}8R&k}@5%?5aDM z!u@ctYg8*SG*zz!eHajcw)t>6r!|n~5_l)uCHqK44A|(Jj1v2%YA@xL7h; zf^0Af!WFerV}1q?`Ek^eg^|{tcNfc(f%?4Q$na{hT$MhlQwpOrwxDh$&YduG2@%Cc z?8p{fsoRb7x>ct-MPlX69R&8QTherC7ArO{o(awtcP< z4#h<)K-sLEJfq5kCZd7mfjA^<7$vKrd^d}5mQHPPksA#fIRC>jRbqsR!m^IueY0Xt z*Hs-O53FZ-iC5W-b%(D{7z~1P6LT|7B`Ll2W=tHdKqI-4$|O9VRW)Zqkm|}%Ur!N7 z9^)xDo{<&9eM8EV(PUcTS6FVaw4zo)qpMi- z(H5247P^Zq%%9B<)iB1B+(+T5s>fF`H#KJK`t&n(LJ&JcWTA}KH02jfNy~lBuuf=iVj-VwA#2^0 z$sruojdHF$6e}VX(s=g_y{)OQwtiR>{l4bqNVd$gG>9SPZJ8wxe1x#ltXqkP@0 zV4;JF!%3@fAT~PHK#9VpvGn^GKuEVU)ZV^{!GN}MrJvxVu##P(PKFN=?FQdQG<2kz zY?1NP0FH<0ot?(J3Z^qtPxx`PG2c^cm8O)m`4iLD)uJ6&%Jipy&O`wBz{ty<21<%t z7?*K-?R2)adGvUn9&xs#U$YiH0@jgn`&vZ-9(?VO(tf%#5D-%UI z@f-Dr66GlIiz4F^zh>SLCAah!t60x0PRJtK(9`x$$Zs4%L^rvKV*K)w_~!(9LYpdJ zyF(fl#gG{VbP^90>B#^8SKN1iM{*ojgQNmQB`Rl$%AzDvcjWPcB9ZQHDTE~!hm+;7 zn%f-!<6G=N8x99K$T`Y6CplZrSx$10bG9tYww&Z7=bW?6|5f+Q?#%3TbroiypWi3l z0oOhCy1Kf$y1Ke5`Q~|)G)<+fogYBEvD#eU*laH?w-&L(Z?12&(KXm>2m8@nyS;X4 zWo5Pr{vq1iZ?_k5odR#^^sdk@qxLAdAIwUYuLmT!tod*lcgQfMCmNJnUqHdxrJTg9 z>BZqMZSD9l;AZL)0J+Uw!*Hii${^FlL4r#$?q^kEEUbkG_T)l8Q@RBc+cfF!AZ^7K z_Z^dl_V%`97N-#&leXm_ojp^agO_V;uW~|UYEPm=$TH^UysWX~vo>|e+POYv{7X1DhB$h3`R^5*vY#}^ zR>=n^4IQ7$8BCL9FkLcsi%qm7rYB=wO*H?x@v5$<%aC&#f$ySk0zMGd6%oel} z5|}i0e3nvO*gcNb6ecCcv>o#?)ZAIr8Z|6utI6!&3gk8%RE zys7q#seWDZ6wfc-;x9-5ByU47`C7syJp}|ORzofxI`@=N2}JWn4M5sI@*s~2jQNrl z&@@z%>@{poKC><-EE84{BG;!hkS6iZRlt;gL;!%y9J<5;m_D zmHa1?`F=uT#?x^0VU~6mkcJhNTLI$3M2^wEuPA|}MqqU?t}h7yC2XMyO|RX>f>+*G zKom`8REd2trj$d$ha_;)c)*;DXrb^Q5x~gA6IKD@{fq?4>`OHzJ8XnN`8dP?azc^{ z`H_I|UvdMw7Aievq%d5a%E{oj^u|5z2NW!Y(Rn>6&K^zzG<-S#@R`yMj?b4x{`w~g z$G_Qt;Yl|aDfl`kxyQ@WeIvA*Q(xAD+^73uTmZN~z>tSLn0E^4HqD+P485`*E&{U= z&l8qd$tv;J(FJ=kPI}VINoWuz*ZXMdmpHJiq`l+gg&6D!7WW8TJ<17?aHfw~NUBk) z3!z}glo_=m(MOTG$AVk64IOho}Ybr;Ow9I}H)``7iu=g+4H#PYskxQLQMk?uU zZ1S(!F0=pY>YJ>lAzmQ#ngQL|TcVRM*t%i38Z&2FP{Usr#&~nqz>Tw@ZSvsGRIC#; zx51{qhRH`wtP!qRe9dtx7WNpn7cj)7l|_mLnxuwxeIKoH506{JzTqE*lCez}4-rt> zF_)!myQN-7`pK2}U@MAyA>tN1#HU9raYGDE+}6BG!-ML}VR#j<20Z z<>Q4&rr`(b4#sSe_M%!$5%!r$6oiPYnX-gJLR3vl*mQC%5Vp_cBG>n5nOGR zJ~Gs&AN1lK+tWTvRdzZet##y-!G!)F9mKos9l(C{gUg#a}@6t1GlR?bV6?I%i$Zy;i%UMz+ zMN7y=_4(u&QG4g9@TDI(t@-7p#kfaxd3~R4Au>3-sKm2E9TT0>43I)uM(Su1keZAF z8gN`?hqD?}oa`7+@_mfhv{|Dr26!*iX^wmJdO_F+aZD7!**!|%ElTZWv&fZFON_ON zilvp11f)CgF+FU>6I)|k-+MDQAx=l>$ZS*$+|BrMc!erWdq|oW`5RGZ*FLdvx*k$e z=Db0XmT>TmE@NMe4;p=Aq$^`Xe4$bk`rQcJElgs5?JfMdfwmSl7%aQQT zpOg-D(PB&bs0d7Pai{+RD%%Nh@ZJecASu4hI5!F?l7L**aFad?Je)(P0LbLFVb?yf zBi_f;LfcayHksB!(7E_gKLv|K%tYJV4uUk8lz{EQ_}!&#+wBa-!EduDF^q&&!UnIwqtNn?%}9fQQI!@gR>i#xqc$6VK*8h1ziLmzqC(A;{61Y#zG;tuPD}{&egt1 z%eC+_yQf1>rWCF}@utq4W_*?hUMkz~dxn}Vh9}U_l#|-pPxDEl-^xt zab+G7cxe!4@YPtL%8T*Vo^K=6_*$3_cq5Ko#|g{nVVlO6HrLpfI7svkk*d{*vrP3T z{(YjBd1~YtPtwWK$tpWpKg;NCBxIMITqtQ*$_&|SZ>e>$FS8_}9|fqx zPBB0iQt(C&`!g3i-%o>J28wP@fgVAJ4NZIf;Ei2UDnQJicRUY>2v-0e%wFYGqp{9 zgUsk{b5fxR9t$LuHvY_dKq@#AtaPdPhm-b4Ucrv7Zzt(9T8NW0Oxyt{!wx9@C7dEC zVH^rnQc@HV8Tx`u{eggKoUO~@<9X7@CTkWno-A;4fqpG;x#RQWPL-u-z^lPNbBxP> zm@503R9NykKzbKflY}&-!^rh8sR@!Nz5)tJ$UPcBHtb!0=1YRU%h$sWtcd%9 zYr)Xe-4XPmr@De>*uJRV`uu2`Jjgrr3-JOn%1AyQ!!kGs1`;654P}N|?fY8xYAFr> z7NCFz!nJ@)<7PjOmUR6h`{pH=b20nH7332#35UE)UBx~bF2yL61TO%bI|2|%7ZG67 zb`@_tq~8?{&wTarZs@$4&GG^&bp+R2yr)@aaOQ%A0?;r z4EijhC{ZMAdodnFZ$dZq;txZcwo^vJCWiBh zBtDOXp>*m{3}BK3%Kv%XqYJeZy%nLQBpBWn3dpejISS58O8D5xq)3k-LTI4#>yr-D zH`%th#!KNgwnkZ7C}3)tUi-p=Fhv5JPAkITPkJR-u&2`fRTl>x4%VcTeL1rbcMlos zGEZmPg%uufszAkV+ehlO74*jLr8XrsNwb%33X+jE2N9O%2=y?96P3inDP$(;A^bia zMUK{#>Uxe~j!=>$^CpTD0>2IeW6>xBW&X+#CFe&W$yhudt+qVh&vd1=g%h}UCi4Up zJkEE7?5)G!sb<_6U1a6^5_!h=_DrPkb1=F1Llbc9f$*e=2b-#PF6^PeOrvtz*k2}uu*d} zw!d~6q5L3tJVp^;$&aIShuDu1qJTbC`a^rXsEIg(Q%$!WV4z<5U)pG>nz`wF&?xe)^+)!}c+1 z(J!53zo*ki_Z+ew$y}L?g2W!u_>{S~7bZb^cnR%3Vr2w~$QM6`8@CbZohsk3eOo=e zt)9GGKibgNa64{}epTQg=K{Si4SH2R0{xAXkkO1)J_CmUw3qk`=}TrLd=xOyrcE?* z!8nkz0sz&ml)pj`;Z{N3CxeTRS}j1cIWzGNV%ZknD77ie`p8E=VU`{k5z!8RdYLJh z&Xi}8S0H6vu>F+0u$@+-JxW$DW;plep#Tl5(J(#HRRSV7rbVNW93!x_Dfg+M4@G%O z+nNwhX=8hzpa3%P;EOxX`NIJL0e7B@jFB7$t6(Z1Su3ex4$?empuAe?7U<7qZBuOzE)i7)na#njSB7|Zv zKaece+0M(oU>{kCl2U4dAQ!KWLw)u<+7zf*MGz|`QWLCZy32GUMN_%hvR=P+qT^y984(m zHjA~ScXFf^eWauUh^XHr+eVIlu8zhLzmqG+p!){t@H`%Ue^Vc!TM@)G@hUu!POCOQ zoz&9Kh6pq@U%n2_=CYY*zm8z!9p^4E+ngS3lIC=oAhSJaA&bFNV^dN>-@|kDIt$E9 zs+f)`SD|aI8;BubL)PrwqmEe8lQMc9r6awZK1x4>dU1E$cg1Gs`HmfNG3e}~QuT#J z&VB3dWnX@Ypn@1J2o)XbK&LYbA9U~WGw`UJw!JSvW`;;ki}AOq?!FZCLFMX{{qG;g zqd>~_)?vQ`Lw~f5m+CT2gsFl=C^1nFyiA~2ctj7G2w^G0+@^38mQWssc-&3SV^E?v zJ}`+y^7Ap<*_mKF-!YK@E>UG^hwRQJVi9>b3gZ((L{I(nqf>51XgS%WfikqE^;$$- zGcu%YPxm2syy$+fmY)hx5^sgBx-T+_vjtypJTgzD#8_uPK~r*6^5&=>9#r`R38Zn5 z*Dw_3ac5!5X4$YAjgHQTt!S8(_*HN+w=hyw<>;SD?MvYW(;q2H4*3oTF5k3R_lfu=oOsz1}B-56$mqV7)1GSiOSI-a$cZH@WPZJh$t{ z%Ctt#o!}ZhIdPtclE!p=jRs*?3_R_?k8purSWb3FoBX%My+tQD){#bRWM;%Rf@6Jf zh;Vgu;}77|@0u?7F)@a8-5|3fQmNhK83fESYB6cu-;S9fC#{n*42&C!2AeeIykePV zi7%eF@{;z5ahM*p3BF4YCGAs&W38EI0Rc)qgxBz5jvs2;ZE+Vxa0brPQ-O#~e!-md z^Q)*J(uK=O1_ooF8HRB9=$Zj1?38fI{hGHhd>hCm8w-gBTn$l&Z?2kaQtBiUbOK^N zn*@WVbKqZtWH-S1h^C(e&(;mVJns)F@axyj`<4<1%ot&9+ zo?w%P=8F&Pfgdx^J;Sq~w}%e06X^_&Ki!d(NeAL1Jmt8W;I>gfp@x3jG4oZy!b?8u zXL8aIdl9#;(pHR#nZajO;tGOB#_mL=nf6}eGifXPqKPY;?;rBpD@L>tP=wRM;U-ih zeCN)hISF-m=EsA+N{0~<(@UUuR#xxvUFIr1nIWMH1-yY-bnNAY`6jhYM}_xH9VY3ZZ9iKh zksDe`Nk#J4@NWnWp+YG`j!5c(AT`6b8`2h$8dTi;Wqbghk6I2>qeK%i2W=YHOncT? z@0;heX-Y@3+aTDxF=E%PLT7>z3)Ns|`*Jp7fo`%TTiPxT7y8hCrS7nX+7ss8u-jQe zOTa9@_WWoBcQ#^Jk%h-3$pL>|;3l0+qDg;}$tjgoTrza{Fegb@tO07~6u}8xN=K2Q z>>VDpUmw`>kES0aOT(0W^Q31N_fs9l7MKjG&>IQU;79IUC7--^fcd^jNEakw5M4n+ z0A+WI?oxJPqQ|Ba50I(YAi#Jkpj=c~uD7lCMq)03H*M`o;OY1{pZlEeBqEg=dooo_ zJVlOPrl;W=cah}kD4nYetyVlpRIG&YtH_j)3V&w#aGEu{2q@I5>L+mK?CetT1(kg9 z>k~*O&YGxzy1t!>zm#Yq^pwgYwG)X4OQPP8Y&m z?EGundr~{wMzYk)rmZmPvT3^%cJSbIfDl!aqn%^*vd#ON#EEh($)G`o*reqq674x7 zf(!_1pFTVB!a(EYerIsFkDwt`XBK^(q-`PS?u1-Dj}*#9^UQ#L@{Ezrm}IykDU{^9 zrlB(pOG=&oq$3dO__#B1ri~@cGHupT3=JhJT1cPL3n@`KH#EA#h|q?(HjNtPdL+7A zH!n#iqoGJ#7^g331Sqiqg0G_vQVc=OFf}j@cPN{ky!B={K;mcBn2l!v0(gn)`Yd0A z`r$Z4n1p#;O^MO=LQ))VEeyLuoWq9F!6L-*04&>92Mw7{+Yxx1Zi7X=6s~=$-aR#PM zJJ@G*3&99}-@|5n;9k-xGxfOb`m{PiNaw zM3D2&ecEsj61j0QoxrQK0mW*j`xjL=+o#tWUp*JKmae!f?lLQ*f#Z;YSyivGVb??h&Nag)J zS&Z$-(Dlez#iffAQmA06TCjF2*;opOyIdw}<9hQvhBE@QT}O~yQ=1h6E@5Yg z+oZYqi+kgU;5ONHU@{1&o-f#Y$f05 zVJ``j)8FPU1Ql-6y|_ekfdp;q;8bIKEu;t1Widrq9)yR8%}xMjW&sqgLS55mX7?L3 zSVgkErSTqWv+a>jVFodpYz__=gHdOfA>_)sd#RtkW@fmSZ&b=R*a+!J76*_`8=5?8 zg_>}mbV#|eAn9{)c(gEDCIY3jFihs3A?VihZASZfxhZQz22GB3zL9fd%Qq{ti8C$8 z5}lFa%)dJYBh#j|=CDmm6!h>q#^g>VLb=gQ8$h;@N|MZi?VwOva?&V6Zstp*K&D5E zWKra3!Chh>!%wK3ANM>-n@8e?X~Tt z`OW#c$-ms$?e$)E+ifRLoxA(w=@WOIJ9%5~zZ1z%_nb)o;SIN}+;G!e=iLvy?$I~Q z&D}g_{=4DMZg^$9^SqY*ac*uAe|by#L-OanbGI(s8Bx^9^X^Lj>GAl>yYRpFC%?D} zf8INH;+;vm)5)*r9)-W$0D5UV`u)9gx4rX5{Q3B~htZF7kIW6~g>#R)^`ZDJeUbiq z1pfE-w?1|?LVoaJygj<@MH{!@w$fO8t@+o&|50P=NFSJ+dyx6tt@!^3o4@7g%-u^z zXW)JNTi-YLzym2k>O=n#z#He%Vc$D9XCUvUzcB+ON;jpyIYn*oi@8agd#1mk-#>@` zFZqwYJ~#J@I>Y zvsu2`mG)i`uv1;mH)f^Xld_5gl2%&wzsyQImHwXEy(j%4`LkyJN_hG#kgidGojIGT zwKv={XO`*84Wz?fZbo?XTze70A-H9o##&jkr#(s{t1^3fZbsFb^vpfwNX`Js4bZ0y z64)nB80z(T=3F@rQYJTA9L4%{QkLkkoG!<57FcfbVBwgOn<>FAcji<%4${&OGB{@4 zE5{%q=~jDNURFs3KG=c86_p?JQxn~$F z-rf`>kK@_fq`R!RtmX2{;65|=6?BDIN;C5(sKZJaeYpqBw_ncxH-$+|?A(P7k$8SYdyaAO4&k38SB%#UiOw zGR``z-(~RQHjAPcUkQ4dxU?k7nPTiVa9MMB4Eo}%W3?;*)w9K@>f{sWk9kf8(Q#{1 z?!@$521l*DV65`z84Oesvz=7_JT4jGtn%kOQPjx??nM0pgMji_FTw+Y>=C1l70 z^6!O|wL_{_avE3Qo8nFiFO*{9n55TpjK+PDL6nIJrRN(`*65Ki8uxY<3C9ri3^`7d zuY_)UaS=s}Vq|UPti&+WV&Qhfm-w)7Oi|CxS%*$}4Y=~sQY6Fa3zOs6ftXpkLx6{4 zj(V0Hydf{hokc9TodE4);0UL0U*<$nCm*UT;mU^ut=Opjcl(u;lE^~LONAK9fUWU=%Q;wis z4mdm*bT4_@&@X?~RKFZ>cJLVoLn|xX z{m(favRXODovB`4f-6aGi2dZaIK}PxuaF_Cl|v^_rkj5L=I-^7saMY8nfTgs`Mq)p zhmVVMCr_jbDUb9rkiM!K(t0_1BE8_t9(>c}M086=kw`?eVq$ePSK^C>alT-Sq~p{ zG=#Nskh8&GKf!k`JkoO#^?Y(KTRzx}hGUFY;yENAVvv9uReFm10VObl{0N^6sF65q z3wmSop*#|fA^N#-?1&VT^kFHE|L-wLZ59c~5beBhe&IJ*6lGakrHV#9KW80&5W2As z1bFJ?&AHSaSn$a-faOYp1+RGAT;%?a zA5E>C;Uf2UQ!IlpNk4Ur`;udgQ@lsS;i#7z4o?)_Ie)lCQAkmJY%;>^(^q*UwQ_{> z^7af4+N9H>3Z!g@6pmm^M;(Jk;VjHO37%TH!@bq&PtY8De5~UmM1PXz#H5eOeO@0)&ruuZsL}eY8n=Is zLodDnV;d2*7Nt``<}h}UxgiVUdO51&a;hbR)lxm}@SD68W4%_SYQ~+^Uup(OXP2Xv z|4A@nF4FxVN4sGtBcGEA{(@-XuM1-oMrw5PRP#ScFfcpMbuWg57fVrPnd9Cs9eQz? zG~oHkP+N_h_4+0rLBa-;fOVLk0{vR&?D>!2aUevFgOX#AL^d32gR($;WI2*ydjZn9 zw6aJ!TNe(IG&{UrV2Bz8v)_G`69qCGBKuh%>C}2Xq$kc1)z1mH+k7;GVet@o*gKSW zE)O90lVm0s9=w^Mztrp0#se-!^Tl0$xLBBte-Uzq-xHozy;sqn{=^r*wU3&f4mG zD6?~YDuX6TN4S8ZkR>LCc22mR{L}m>vK*9Q|G= zeFK#SU{vG;=Xw7O2FY?TKr+!GVvz=&U1W1b&h6_a8*^38FemBHWbkCN!c^o4_j3NT z7z7i;0;%F+6g0^*BKMQT!eP!KpUt3H36L9f*Lb#@!64Wxz)c+_h%7Rm**Rr;1RI3WtzXWgFq}xdZ&g<=bCUlG8H&o0%%k?bz*MEq(az%%%nE0>zD1$S=R^J{0av(H^G#9=_NjTEcK#;`5=PH{vjE9jI68<2C4!<}J6ycmw+N8rUf$>V zkpD@-6=wZ>s{l)@16OW;xLd;+obg3`n*a$y%<`kp|0LiAxBq>+1VO$+d7gg$CjlBR zujF?~(A01j&qZu~rw~IOwg7im{aq3qR6r{~1NomM^TBA-?-pP%<)iXc^FIkNaJLQL z$!m~a=40^w+e&g)dcX;t6POPvyJ)Rc@jUKGtHxn*{`{Zi5s($SytEki;>r=D+-~`E9u%mMJD)LbYSJv6r{(AUh!VTJ z8T7+mUMliG31o!Jg!c=6B)LtgoH;oe79irJ_7{DK#-lEB_2s#c|4D#=%+vYr+0}kZADiG0c}Kie&l}=aEC+kt0hR1d#TCclB|ZL;&`5R{wDzv&LV%e z4hb#*qEWaM(IGF7Bma{C6{m}TO^0eTHWf(c%_MyuT^=(`4)eJ!L*2a=7Wu>v)dem!y4kcIqCUL76)QR(&7V`;nj){8vPc< zwp8T|hv>ZwqBK~MK#?tu1$JlxC@Y`Dxijx?F^DE9Tyy2ps`7{P{rxtB1`aMN6tNoa z;ax=e^APnph1*4c$AiT;P1;q?S;oKX$5IzZ9JkYy8}ax2D5f)1PwcNl^HFrg`=n@w z!#ebxgD>=Q_V)!ysGMHqmBZYz{ed4t4N>Xbdig^?g7xj~sN-6^>hOa{s9u zMVVOWIpXkNQU;|z<+;4ndQHSI0jN$4*mI2MhZ(1EDH619{ty5le9 zNJ<`YsF5(SzJDb{Qp3)|-LCz$7zMozY{huwQk)*O#Bw(D-$?LGp?m;j_irVLNNStV zax;PIYKdXA%HK(ltcLYiM2x2WdkKzA7Q4c7*2@dy=l=&KmWxPAgVbOJNZMKAETw-` zAfou2;&Q4J%Rfo5AOgjB!iKw1e?<=C6Z zoBFRuKvibSXfbg+_rFOnwaBxw5?-r(JHdGm{#}A(cwrEa_sf`~*E){9o2v5tha5{| zsa$aMco;4CpK?6b!!j&mkydWFef+=Fh$w+gK`pC!#7Xjh%MsOZmgXe+e|RJ-!C)t} z$EEpe2(GGrAIfOu|K+i)hTUkq$AHnw4Ce>?KORes76o^jeL#ewj-8wHvd`U+&8B6u zJf(L>@Rb@{bFdb5uJ&s4Lpw49adUK)!Qu$4piX2Hn}ULLgfV@TDjovmmkjHAjwQu40mOz#lu-5k6`d1 zYNL0!8CzXelUISet9&Gj<&9%#c4AL*%TR~4$j#KF{Aes^>b&iC+%(Yb+@t+??4sU| z2YY3KE&BE3IH}SN%&iQX77~Z0a$GJpS_$T)<}oaiQKL^=;8u)_sTP8eYIz~(&@&Wu z`dA(f32oR%$f*^bVZxrOmI`N6KaNLKr%mmMcBc$@kN2Q3p3}=w^}MN!t?~pOL!Il` zaj#7gJx?q_K_c2R&x*}^ytav=q18O!*^S^;p^(DDrlelY+*$oe0!%n_)QpHbt3O$e zh*(x&j#j^LJLFSzNKO=>=;z5{(~;NdsRArXvL%`;BTSX?IPN3pmB!Nqco5e80hF7k z%Ml&3+&n{vq`KTNw!5!QtLV{+t7182&NKJ z$4eq^FM6H_Nr?BIR2HR$i`V0gCx1SVWHCS@=Nv2cBIe~R zkB6MO8KY#rZ#Lu9Ua2TATom0o8Lp*h*ojdpqH~-+pqKlwj27bLM!qbpM!oBBe0hZz z%MwC1j4^2yf5kcCe9N!&Vjgod5ssv#IQFRSg!?DvkGuha<3OndQ2>cs+uQUCc1eqiq&BMnB%?{1Q@QTr_>ta z{y8txq904#oh?42o)hk*v*g9l42DA__Us}}{|Gw1dUU;G)zBT)vLDSL?vAVPG!9QK zy)``V#jpW)(~JsJ&k7f_alwnhZY(8>MUR6yd0zD6*oRC6a9lUzLDl`Av)LMQBu>_7 z&lKFcxsS&~_8NlFR&eLtABL3RsCs2Fhs;uB!fSanmxCb%<-w7Z*tRo^ST8lqDqbl; zlgaMgp6_e!g^f>(h4Wsod9dKo9fEvN1!Vt9 z91N;feB(}F>pmpLA-XV#4A|r?A*4|AHs&@XJ5@0;5_7)?)ADs( zxQxR4f@^^_V=H1g&79OXJXp>nokoGYdIfX#e9J>04$zgTe|3J;jE7MT_B?kFw&}wX zFTl@Ol^dMhddZ6b3jUz_%Hh;8UORsy-po#(lx)4c~I$s>Zqs7F09P>jj z**;c^hR(#bS#+Eff1C^vteSOPe-B5^fXcU^oN{5(w5yxjCqG_>$!?QnQ>K<6E^_e` zWN4DouxZZqM7WQUOP?siwGkZ9ywP@90Hf!UW9`!`lur_(3Ew!52H_B=>1aDbr8qiO zBc1bXdO|fiF8k;w%g|9o!~71SP8&VCZunduGGYw}My*S@Y+;|LLj!K@V>2vn((3ihcr!m=f@p{|qDuv(qUVc4hRN^n z1wIs3qtIN{#S@1`P8sySP>!Z3x@xX)w})RO!%@pS9d0-NVi^uHZxL*{R}896!*=nrvw zLNM+hV`bHlwSb$MukxTV#U)cwsVE`m9Ix?Lz?H8q!Lb^QI=g!&iCZ({ac;Lv?yU1c z)C;?tF}5j00T!HcvzQ-(%w zS2#SsbnEkN8Z>qq>%rvkQQQkm-!8+1TMY}S>^Z%5&*Xdo-=V~_6e5#fr+27OAzQkATVESC&T{%;88(Z>oNx-NLOp+6RM(I2Xz1viDhNt&Q-Gq? z3@1;dVxG6hFG0$G)Q==loh$K`D~BZ5X@1LIm{j^YAN6L#Le%`?bnB1lQ6g{Ch|-mo zg41&_-KO&#q!Z0M_1G+?Rxch6QGYAyQ6|iab9~NWr&EN+kLwYxAup$iuPJ4per_3b zKXC+fxQj;(rl407I>zh%lX`SG2qI>|94u# zW}h$^tx{3;IZgXB9u!L>YlJtv0EG1&Ao*nL9@0S!fj(>)JS%IcZ zaJ4hU*_6K`#Ntf9s-GvvPH-=Ff_DkgSfj0`nPP4oe^rI&xTW~rDl{w6UQ~Q2rIj4+ z4)oWAXx0yApY>^Ra1#6;KaREWo>9JutzX!x^%Cs3O`*5AzwSYktvgwZ-H;mf1urSZ zgZ_pG3)L2^1%t!mM)|*g(}Tg(6)XdxpA}{uzt@9eBc$vGjOn6A!$m3mmKTk2uNkh? zp;I_4zpcPhhewFR@;e?Z6n|rwLZ-{=u;|j)&ynLqN3WKD*NesE*9tA5sv3o}p?*(* zgxnf+ND8NC-zUUTpQXUb%I^!&kd7;HO{issJ~0%>!0}0 zG=_`g!2oZHR;L&XfYi1eWB>oD2TuaV99x&7F7^RDo~Vk)aT`FDO8-oOr%{_9l&eDd z=L$rZqh7C+MS6*Lco&z!qrdPVS_%iq#tbPzsYN`3cMFml6}R61(u2yDZd!@)Y@rwg zsmHJfApX+ zGU8%Ot*hssBuJc0(XO7e4*f>)bN}o?LYerFL-%uc1OMX1Gz?888WLl&C8FxJ7demo zze?~J;VB2Bo-f9t`8N+1NW)QB7&B8pi1cQK*~i;B+-^=r!^(RA|v z4?iB<%kR^UJsZ}bN8+%oUI8Tk=||FByU;*sr}pVn*H51+Oj4~_{!4%**%-mbAt#^UG^w9LpO?$Y#}MrOpuwHBV2y}n_M{B3yq;YPFA+d zIrI`_0g_umQa_7Dz5sSv2CJAA7z2K9$JsW$`U zvM+4FYRZn2(Cwgnvzs!ezCc(#?_5^4hq09F@LF)0(Qjd?*X60;B<|r(1hAEBkvQ({ z%_B;vC&{|7-rMtV5$}(hcQsq^6eC5+^az@UN0P?N6QfL6Q)PRc-3$9AWHHun*ylh;54hE^waQ4^Z zMQBQeMva5hJ5S(ozzE5TOf5O^m`K3v$#i?5b3;`th0}dc z_F}PeGF?q|+_!Sl`xGIfTI9u{XUMDcR31lTNMQl4hZOpC6P z=X>!i_vyVDcJCY{q&qZOe>*i)_~hO$zTgP>F2{qb!(FPhgTy-(dB1 zs<<3GKD}6ckq%c2^)SOO$29c{IOn)^qdZu*E76UwP)Xe@R(lkuQ;m$oiVMPRX&@sxLMRS4Hk{9)o+-h{&T`0Ho&;<1Ia{MYO&jR;?q=+4pC( zc#J?V?vDG#lIv=n!<^aAdQrh4w-|JA-nevOv6fe?4r_*Dh|a0dTnzf%nPe4e8RBlw zUoOEzW=t7l)JV9vf>%h9P%sk}dzvj(s>Z{;8hxb{PYDpU{4zfFS4nZymMfgAWAthX zn%v2yJawCvL)^LQH7ZP$?05?gSn9#WS%3FRaNR$SM?vLXf{~wj35pi%K14>0w&Owd z?AV-bz92yZJzUT1aPh#4QY7&~pK6*T1^x~a1Xn&mao&q15vHtfpk8S{>#zH?G;Tw29$V99JRH|3>R;HzaQ0pw|H@Z7#6sb_e&;CEzg`?y>S$5 zbS|qX+Z?}IN5NzQ9m9hXXlj0)Jdrv-@_8UPA?2G#!A9GQd5GVB$`jAMC+&F^;cJ zAyr;?j$N4|hd#uEq%q9kAXwG!rBdZtczJaiXg<_~28yySOU0p!=)U2@JSd@X8)bZSWaER*F(4loj zFFJG=-FEKzu}ppab>U(5*uDS{c6ldrv|5CFB^x`iq)w|^T?y1$p9P^_Gu&JZcob!iQPlJ~Z|0DvpFF8AzkpKDhQpgl`6@a-?t@-pdsNn`%v!nT$FsN_^}5JESb(AB z3b*4Q_)*lj<8yY{bw83-l-7eYwRp9yQE=5OnX^RRB*K)LrpjnI zz48%$G|B3@w6RiEavk0ex|8@wKMq^Rr78yI70gHZF_4}|NN`jPfmKTgx8r@Z8wamo z^(Y*c5cxaa>_$O;4zd6!NT%w1F!$si<3NCi_*6nL)$=u3g=ZZ1u6KhMAL~Ryxz?R- z2K{_E{2VU_hL2-0&~(tFHP^vXt5vyO=HtCcYSmth*7yW3lJc!z%?$>_Cps~_aU2du z)6+}UmBLsbpX9(mPYC&CqUHp%UO(B1VubT#d|*RpZC&Q-anGEDeu^83i5{W5M+y?f zpEXp<<2)aq>PA9K7LUh~8KQFh6Stdu8jrwz(W%#7%x?1OJc^oZL2f4A!XqeG6lxxD z*>^sJM*t64(bUtfPVNNznI7tO+~9JS=4bKr?Fn`TNzli5=UY`*GWz#7P;?-w%sRLcJonT56xq z;0Q3e($icRC%>(N{Tf#QPo?YMJCB=)YQlgWg}@<^;!(;$l^EhVh+zjSJEE3NpD6^->{G zbA?-}U+cy&G^rp6MnP57$%C(R(jQWUN_vx4oCY-yxcH^7cVM_QjkB(U3yf{?4K4&G zrkaQ+hp>{+VD)S;eu}qAag>FLsON->>HfwtB>iYR3cIU#NQQ&Zu#MYR*cZETH+7s) zPN<5q%3=DZqhKnze$~zxS9Ri>1(*`Au;o=n{%}^%w+OJ1->npgUV0o>5XDY>s}RXr zJc3A-J}c4VacFwV_4#dOcyLu<%)$2jXoM&9sFB(Y2Up@?KyT>_)1*ej&C|EDXclpW zfY*n+ly+{f1`ix}3;7)^l2rL;I73w@YN+GFz1;ZDBVvNhK(=M|or>8fzKg|nK0@Wx zsT37NNUe1oUiEt+&)@CEu|BX7TlL}Lye8kn;=w*qqdaq2Jib?fBZ1UKE~U=wGE>i% z<9>|Z(!5=Rs^7hYC)=H?lo6q#1aa>*zfXZFDZ~RciQHu4nTu7uZe+YG-><+$HF*jw zsujx*D6rH~rHGU4AM{~Kl{IQ5PgM}9>!PDUE7@Eu%@6rdUFgMEf*x(Ng^5z5U{1L2 zkfS(JfT5NbE`H{RM&kvZBq;!giR>O+%}+C<-!B&}8> za!3tjX#Oz;BD0Qa$s)v6+kU44Q62h*d!zT`3M{R7kQ@Z7t}brJ`UxMR)p(fN8QqM% zQE~uOuQSeG{Yf7#D&BI8*f@#$DK9p}7*3BGm2GEE1N^iA%aw4jzMZLzIBU*pH2~*( z`xzgi8ul&jw)1Bt^ovSaExVk2{G1N~ov1PbQh9wl_N$A~$3L&cf;4KiS<2kq=PyWb z)gUQcq|7f$F;tco&j0pHEQ&_IOP-uCBQB1#Q`$Am`QLt-#gqhKCK|5~Ugkzs91#d-ja96huTlFWi3k9v_Yxv>dk|{W-@1_30Nj=<&`t#$_Zc>!7>N;UC{Gkhj&9qJ+HlrHqFLHayAGt6r zp#u2cKBZ&bila_teZ@sq{ILtea=%;SOfWZQ?{^`fOQESO)H5578)=Hm`x6ewwIJ#R zS9)PR^TAlYf6Aa(?so=<`>5$LzrSC~4z;bvohbi|K|;IeLeSj_S8xU0D=bqr3eMvD za|Xr57S(B|V?b2|+>ZJe3<8RdqeBQ_)WZqKI-!c&f63r52pUTTHmL0iW_A1(L!D-T z0(;KKJ;X;8Kqw)yhpVh$Z@(9k@m-Y@Tn?+h zb)li0FcFB;5}lU!EI_G~FMsDklDJlmL2EfI8#*cfy$21|;tOe8nSBzjmQ+q_{DTVz zPGkd=E629Jfn9#koek~P<8Z7P@?ZR;8%Mm)!qCbH=Ue(G7mCeruov}x;#w<5{@ICz zBCWBtUZU8=o9Y@T`RWozZSpsbM?}4{&%I%|+59WdS2v z{lT4{=5D%quHB;J2^3emVg7c>Xm{u`V4(UX)nkk}1UIk<_OR1#Tv(VdOoLi3I0QF( z5tNj;QX}9_4mWWKTCf89-C-;0cS3CSRPW+i<&y&!W$_>>8mdo;SWevTmq5}=DHkL7 zU>?x`Y85+euZDP6&SHNEi^6ys8jc6F%4_- zRCCtY!x$7soi+Lk(M~h!kXg7IcZ*9$EvJlydW#Rs#<<_dd9Kml+Ni40xSj6d434dh ziwN$;t+cHh3N+=Dq~!~T=MhdkxS38*5t(;9NP9U`B3X^c;X_@7mGnp@o@P8W$rdZm znL~Ccmh@2^Ch4T<^^D`Dl5UY7%^|uR3`mlbhkTT=zrY;rd~x>mt!^Z{@j=}-BtaaL6n9(H`f+ zfR`Tqol^IN8V46~@^}u1Valu{Pc56wTs*-^ANTnKgg0~oq*Uxz&XIr0aq4>p=HiKY z3~=yPmA-o%`~I^*?MXA#TETX4TUF2TJ&y6c5EP%Br#Nhpv6bwlRY!W-G18ZS?o)Df zH*on3$r;eOtR|m|L#}QI?Wa0vUx630>ZrL}=cg4Y_b4A_692flJAn1D8t&Eb@`TGF z_4ERUA!%2fej3|rVF%aVv&DS0Yx*9Ceeitn{uz1d<33F}vAwcJJh;CR`Ll|YSEGKkG~V0CIiimD!<_H%wj%A#!Qmn$>Z*1 z)J4(6s;i3|?{jh#FZBoYYZXSLJ(rgVNY55rWx7Y8t!Y_0d=<*<5HJM7sP zU{yaqN8i-&N#(pS;aaMv9J=Ibp!$Ld)x};^!wm_yPrh(Mi)`XnNRE;A0vt#++=y^8 z{h~6uFhyu!e^`Nfd0mpJIR$VQskHnfs` z+L0gYMWFoBGRhQPP+|_M={jz#DaPZDGPZ_ zPWnNwZL;gqwMlhrpIcogN+`oF8HEPOc2GU~kfVNbmU>3D)iQQwZQV7YP6z90G+AN{ zSKKvDGctbnlyZ9PnDG1jxZf!nqFPzI$3go!SYM|yv_CMb>Yke(HrL*1@3%;2*==$< I%LOj~KOyw`y#N3J literal 0 HcmV?d00001 diff --git a/dep/libtomcrypt.lua b/dep/libtomcrypt.lua new file mode 100644 index 0000000..6530536 --- /dev/null +++ b/dep/libtomcrypt.lua @@ -0,0 +1,43 @@ +libtomcrypt = {} + +function libtomcrypt:include() + includedirs { + path.join(DependencyFolder(), "libtomcrypt/src/headers") + } +end + +function libtomcrypt:link() + self:include() + links { + "libtomcrypt" + } +end + +function libtomcrypt:project() + local folder = DependencyFolder(); + + project "libtomcrypt" + + location "%{wks.location}/dep" + kind "StaticLib" + language "C" + + files { + path.join(folder, "libtomcrypt/src/**.h"), + path.join(folder, "libtomcrypt/src/**.c") + } + + defines { + "_CRT_SECURE_NO_WARNINGS", + "_CRT_NONSTDC_NO_DEPRECATE", + "LTC_SOURCE", + "LTC_NO_TEST", + "LTC_NO_PROTOTYPES" + } + + self:include() + libtommath:include() + + -- Disable warnings. They do not have any value to us since it is not our code. + warnings "off" +end diff --git a/dep/libtommath.lua b/dep/libtommath.lua new file mode 100644 index 0000000..e3b5404 --- /dev/null +++ b/dep/libtommath.lua @@ -0,0 +1,34 @@ +libtommath = {} + +function libtommath:include() + includedirs { + path.join(DependencyFolder(), "libtommath") + } +end + +function libtommath:link() + self:include() + links { + "libtommath" + } +end + +function libtommath:project() + local folder = DependencyFolder(); + + project "libtommath" + + location "%{wks.location}/dep" + kind "StaticLib" + language "C" + + files { + path.join(folder, "libtommath/*.h"), + path.join(folder, "libtommath/*.c") + } + + self:include() + + -- Disable warnings. They do not have any value to us since it is not our code. + warnings "off" +end diff --git a/dep/steam_api.lua b/dep/steam_api.lua new file mode 100644 index 0000000..0e775b1 --- /dev/null +++ b/dep/steam_api.lua @@ -0,0 +1,22 @@ +SteamApi = {} + +function SteamApi:include() + includedirs { + path.join(DependencyFolder(), "steam_api") + } +end + +function SteamApi:link() + self:include() + + syslibdirs { + path.join(DependencyFolder(), "bin") + } + + links { + "steam_api" + } +end + +function SteamApi:project() +end diff --git a/dep/steam_api/isteamapplist.h b/dep/steam_api/isteamapplist.h new file mode 100644 index 0000000..d678909 --- /dev/null +++ b/dep/steam_api/isteamapplist.h @@ -0,0 +1,63 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to app data in Steam +// +//============================================================================= + +#ifndef ISTEAMAPPLIST_H +#define ISTEAMAPPLIST_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" +#include "steamtypes.h" + +//----------------------------------------------------------------------------- +// Purpose: This is a restricted interface that can only be used by previously approved apps, +// contact your Steam Account Manager if you believe you need access to this API. +// This interface lets you detect installed apps for the local Steam client, useful for debugging tools +// to offer lists of apps to debug via Steam. +//----------------------------------------------------------------------------- +class ISteamAppList +{ +public: + virtual uint32 GetNumInstalledApps() = 0; + virtual uint32 GetInstalledApps( AppId_t *pvecAppID, uint32 unMaxAppIDs ) = 0; + + virtual int GetAppName( AppId_t nAppID, OUT_STRING() char *pchName, int cchNameMax ) = 0; // returns -1 if no name was found + virtual int GetAppInstallDir( AppId_t nAppID, char *pchDirectory, int cchNameMax ) = 0; // returns -1 if no dir was found + + virtual int GetAppBuildId( AppId_t nAppID ) = 0; // return the buildid of this app, may change at any time based on backend updates to the game +}; + +#define STEAMAPPLIST_INTERFACE_VERSION "STEAMAPPLIST_INTERFACE_VERSION001" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + + +//--------------------------------------------------------------------------------- +// Purpose: Sent when a new app is installed +//--------------------------------------------------------------------------------- +DEFINE_CALLBACK( SteamAppInstalled_t, k_iSteamAppListCallbacks + 1 ); + CALLBACK_MEMBER( 0, AppId_t, m_nAppID ) // ID of the app that installs +END_DEFINE_CALLBACK_1() + + +//--------------------------------------------------------------------------------- +// Purpose: Sent when an app is uninstalled +//--------------------------------------------------------------------------------- +DEFINE_CALLBACK( SteamAppUninstalled_t, k_iSteamAppListCallbacks + 2 ); + CALLBACK_MEMBER( 0, AppId_t, m_nAppID ) // ID of the app that installs +END_DEFINE_CALLBACK_1() + + +#pragma pack( pop ) +#endif // ISTEAMAPPLIST_H diff --git a/dep/steam_api/isteamapps.h b/dep/steam_api/isteamapps.h new file mode 100644 index 0000000..9a97b4a --- /dev/null +++ b/dep/steam_api/isteamapps.h @@ -0,0 +1,176 @@ +//====== 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 + +const int k_cubAppProofOfPurchaseKeyMax = 240; // max supported length of a legacy cd key + + +//----------------------------------------------------------------------------- +// 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; + + // returns the Unix time of the purchase of the app + virtual uint32 GetEarliestPurchaseUnixTime( AppId_t nAppID ) = 0; + + // Checks if the user is subscribed to the current app through a free weekend + // This function will return false for users who have a retail or other type of license + // Before using, please ask your Valve technical contact how to package and secure your free weekened + virtual bool BIsSubscribedFromFreeWeekend() = 0; + + // Returns the number of DLC pieces for the running app + virtual int GetDLCCount() = 0; + + // Returns metadata for DLC by index, of range [0, GetDLCCount()] + virtual bool BGetDLCDataByIndex( int iDLC, AppId_t *pAppID, bool *pbAvailable, char *pchName, int cchNameBufferSize ) = 0; + + // Install/Uninstall control for optional DLC + virtual void InstallDLC( AppId_t nAppID ) = 0; + virtual void UninstallDLC( AppId_t nAppID ) = 0; + + // Request legacy cd-key for yourself or owned DLC. If you are interested in this + // data then make sure you provide us with a list of valid keys to be distributed + // to users when they purchase the game, before the game ships. + // You'll receive an AppProofOfPurchaseKeyResponse_t callback when + // the key is available (which may be immediately). + virtual void RequestAppProofOfPurchaseKey( AppId_t nAppID ) = 0; + + virtual bool GetCurrentBetaName( char *pchName, int cchNameBufferSize ) = 0; // returns current beta branch name, 'public' is the default branch + virtual bool MarkContentCorrupt( bool bMissingFilesOnly ) = 0; // signal Steam that game files seems corrupt or missing + virtual uint32 GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots ) = 0; // return installed depots in mount order + + // returns current app install folder for AppID, returns folder name length + virtual uint32 GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize ) = 0; + virtual bool BIsAppInstalled( AppId_t appID ) = 0; // returns true if that app is installed (not necessarily owned) + + virtual CSteamID GetAppOwner() = 0; // returns the SteamID of the original owner. If different from current user, it's borrowed + + // Returns the associated launch param if the game is run via steam://run///?param1=value1;param2=value2;param3=value3 etc. + // Parameter names starting with the character '@' are reserved for internal use and will always return and empty string. + // Parameter names starting with an underscore '_' are reserved for steam features -- they can be queried by the game, + // but it is advised that you not param names beginning with an underscore for your own features. + virtual const char *GetLaunchQueryParam( const char *pchKey ) = 0; + + // get download progress for optional DLC + virtual bool GetDlcDownloadProgress( AppId_t nAppID, uint64 *punBytesDownloaded, uint64 *punBytesTotal ) = 0; + + // return the buildid of this app, may change at any time based on backend updates to the game + virtual int GetAppBuildId() = 0; + + // Request all proof of purchase keys for the calling appid and asociated DLC. + // A series of AppProofOfPurchaseKeyResponse_t callbacks will be sent with + // appropriate appid values, ending with a final callback where the m_nAppId + // member is k_uAppIdInvalid (zero). + virtual void RequestAllProofOfPurchaseKeys() = 0; + + CALL_RESULT( FileDetailsResult_t ) + virtual SteamAPICall_t GetFileDetails( const char* pszFileName ) = 0; +}; + +#define STEAMAPPS_INTERFACE_VERSION "STEAMAPPS_INTERFACE_VERSION008" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif +//----------------------------------------------------------------------------- +// 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 +}; + + +//----------------------------------------------------------------------------- +// Purpose: possible results when registering an activation code +//----------------------------------------------------------------------------- +enum ERegisterActivationCodeResult +{ + k_ERegisterActivationCodeResultOK = 0, + k_ERegisterActivationCodeResultFail = 1, + k_ERegisterActivationCodeResultAlreadyRegistered = 2, + k_ERegisterActivationCodeResultTimeout = 3, + k_ERegisterActivationCodeAlreadyOwned = 4, +}; + + +//----------------------------------------------------------------------------- +// Purpose: response to RegisterActivationCode() +//----------------------------------------------------------------------------- +struct RegisterActivationCodeResponse_t +{ + enum { k_iCallback = k_iSteamAppsCallbacks + 8 }; + ERegisterActivationCodeResult m_eResult; + uint32 m_unPackageRegistered; // package that was registered. Only set on success +}; + + +//--------------------------------------------------------------------------------- +// Purpose: posted after the user gains executes a steam url with query parameters +// such as steam://run///?param1=value1;param2=value2;param3=value3; etc +// while the game is already running. The new params can be queried +// with GetLaunchQueryParam. +//--------------------------------------------------------------------------------- +struct NewLaunchQueryParameters_t +{ + enum { k_iCallback = k_iSteamAppsCallbacks + 14 }; +}; + + +//----------------------------------------------------------------------------- +// Purpose: response to RequestAppProofOfPurchaseKey/RequestAllProofOfPurchaseKeys +// for supporting third-party CD keys, or other proof-of-purchase systems. +//----------------------------------------------------------------------------- +struct AppProofOfPurchaseKeyResponse_t +{ + enum { k_iCallback = k_iSteamAppsCallbacks + 21 }; + EResult m_eResult; + uint32 m_nAppID; + uint32 m_cchKeyLength; + char m_rgchKey[k_cubAppProofOfPurchaseKeyMax]; +}; + + +//----------------------------------------------------------------------------- +// Purpose: response to GetFileDetails +//----------------------------------------------------------------------------- +struct FileDetailsResult_t +{ + enum { k_iCallback = k_iSteamAppsCallbacks + 23 }; + EResult m_eResult; + uint64 m_ulFileSize; // original file size in bytes + uint8 m_FileSHA[20]; // original file SHA1 hash + uint32 m_unFlags; // +}; + + +#pragma pack( pop ) +#endif // ISTEAMAPPS_H diff --git a/dep/steam_api/isteamappticket.h b/dep/steam_api/isteamappticket.h new file mode 100644 index 0000000..21fb9e1 --- /dev/null +++ b/dep/steam_api/isteamappticket.h @@ -0,0 +1,28 @@ +//====== Copyright 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: a private, but well versioned, interface to get at critical bits +// of a steam3 appticket - consumed by the simple drm wrapper to let it +// ask about ownership with greater confidence. +// +//============================================================================= + +#ifndef ISTEAMAPPTICKET_H +#define ISTEAMAPPTICKET_H +#pragma once + +//----------------------------------------------------------------------------- +// Purpose: hand out a reasonable "future proof" view of an app ownership ticket +// the raw (signed) buffer, and indices into that buffer where the appid and +// steamid are located. the sizes of the appid and steamid are implicit in +// (each version of) the interface - currently uin32 appid and uint64 steamid +//----------------------------------------------------------------------------- +class ISteamAppTicket +{ +public: + virtual uint32 GetAppOwnershipTicketData( uint32 nAppID, void *pvBuffer, uint32 cbBufferLength, uint32 *piAppId, uint32 *piSteamId, uint32 *piSignature, uint32 *pcbSignature ) = 0; +}; + +#define STEAMAPPTICKET_INTERFACE_VERSION "STEAMAPPTICKET_INTERFACE_VERSION001" + + +#endif // ISTEAMAPPTICKET_H diff --git a/dep/steam_api/isteamclient.h b/dep/steam_api/isteamclient.h new file mode 100644 index 0000000..f007c63 --- /dev/null +++ b/dep/steam_api/isteamclient.h @@ -0,0 +1,520 @@ +//====== 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" + +// Define compile time assert macros to let us validate the structure sizes. +#define VALVE_COMPILE_TIME_ASSERT( pred ) typedef char compile_time_assert_type[(pred) ? 1 : -1]; + +#ifndef REFERENCE +#define REFERENCE(arg) ((void)arg) +#endif + +#if ( defined(STEAM_API_EXPORTS) || defined(STEAM_API_NODLL) ) && !defined(API_GEN) +#define STEAM_PRIVATE_API( ... ) __VA_ARGS__ +#elif defined(STEAM_API_EXPORTS) && defined(API_GEN) +#define STEAM_PRIVATE_API( ... ) +#else +#define STEAM_PRIVATE_API( ... ) protected: __VA_ARGS__ public: +#endif + +#if defined(__linux__) || defined(__APPLE__) +// The 32-bit version of gcc has the alignment requirement for uint64 and double set to +// 4 meaning that even with #pragma pack(8) these types will only be four-byte aligned. +// The 64-bit version of gcc has the alignment requirement for these types set to +// 8 meaning that unless we use #pragma pack(4) our structures will get bigger. +// The 64-bit structure packing has to match the 32-bit structure packing for each platform. +#define VALVE_CALLBACK_PACK_SMALL +#else +#define VALVE_CALLBACK_PACK_LARGE +#endif + +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error ??? +#endif + +typedef struct ValvePackingSentinel_t +{ + uint32 m_u32; + uint64 m_u64; + uint16 m_u16; + double m_d; +} ValvePackingSentinel_t; + +#pragma pack( pop ) + + +#if defined(VALVE_CALLBACK_PACK_SMALL) +VALVE_COMPILE_TIME_ASSERT( sizeof(ValvePackingSentinel_t) == 24 ) +#elif defined(VALVE_CALLBACK_PACK_LARGE) +VALVE_COMPILE_TIME_ASSERT( sizeof(ValvePackingSentinel_t) == 32 ) +#else +#error ??? +#endif + + +// 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 *); +extern "C" typedef uint32 ( *SteamAPI_CheckCallbackRegistered_t )( int iCallbackNum ); +#if defined( __SNC__ ) + #pragma diag_suppress=1700 // warning 1700: class "%s" has virtual functions but non-virtual destructor +#endif + +// interface predec +class ISteamUser; +class ISteamGameServer; +class ISteamFriends; +class ISteamUtils; +class ISteamMatchmaking; +class ISteamContentServer; +class ISteamMatchmakingServers; +class ISteamUserStats; +class ISteamApps; +class ISteamNetworking; +class ISteamRemoteStorage; +class ISteamScreenshots; +class ISteamMusic; +class ISteamMusicRemote; +class ISteamGameServerStats; +class ISteamPS3OverlayRender; +class ISteamHTTP; +class ISteamUnifiedMessages; +class ISteamController; +class ISteamUGC; +class ISteamAppList; +class ISteamHTMLSurface; +class ISteamInventory; +class ISteamVideo; + +//----------------------------------------------------------------------------- +// 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 these APIs if you have a more complex versioning scheme, +// or if you want to implement a multiplexed gameserver where a single process +// is handling multiple games at once with independent gameserver SteamIDs. +//----------------------------------------------------------------------------- +class ISteamClient +{ +public: + // Creates a communication pipe to the Steam client. + // NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling + virtual HSteamPipe CreateSteamPipe() = 0; + + // Releases a previously created communications pipe + // NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling + 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 + // NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling + virtual HSteamUser ConnectToGlobalUser( HSteamPipe hSteamPipe ) = 0; + + // used by game servers, create a steam user that won't be shared with anyone else + // NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling + virtual HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType ) = 0; + + // removes an allocated user + // NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling + 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 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; + + // user screenshots + virtual ISteamScreenshots *GetISteamScreenshots( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // Deprecated. Applications should use SteamAPI_RunCallbacks() or SteamGameServer_RunCallbacks() instead. + STEAM_PRIVATE_API( 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; + + // Trigger global shutdown for the DLL + virtual bool BShutdownIfAllPipesClosed() = 0; + + // Expose HTTP interface + virtual ISteamHTTP *GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // Exposes the ISteamUnifiedMessages interface + virtual ISteamUnifiedMessages *GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // Exposes the ISteamController interface + virtual ISteamController *GetISteamController( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // Exposes the ISteamUGC interface + virtual ISteamUGC *GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns app list interface, only available on specially registered apps + virtual ISteamAppList *GetISteamAppList( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // Music Player + virtual ISteamMusic *GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // Music Player Remote + virtual ISteamMusicRemote *GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion) = 0; + + // html page display + virtual ISteamHTMLSurface *GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion) = 0; + + // Helper functions for internal Steam usage + STEAM_PRIVATE_API( virtual void DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess( void (*)() ) = 0; ) + STEAM_PRIVATE_API( virtual void DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess( void (*)() ) = 0; ) + STEAM_PRIVATE_API( virtual void Set_SteamAPI_CCheckCallbackRegisteredInProcess( SteamAPI_CheckCallbackRegistered_t func ) = 0; ) + + // inventory + virtual ISteamInventory *GetISteamInventory( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // Video + virtual ISteamVideo *GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; +}; + + +#define STEAMCLIENT_INTERFACE_VERSION "SteamClient017" + +//----------------------------------------------------------------------------- +// 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_iClientDepotBuilderCallbacks = 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 }; +enum { k_iClientScreenshotsCallbacks = 2200 }; +enum { k_iSteamScreenshotsCallbacks = 2300 }; +enum { k_iClientAudioCallbacks = 2400 }; +enum { k_iClientUnifiedMessagesCallbacks = 2500 }; +enum { k_iSteamStreamLauncherCallbacks = 2600 }; +enum { k_iClientControllerCallbacks = 2700 }; +enum { k_iSteamControllerCallbacks = 2800 }; +enum { k_iClientParentalSettingsCallbacks = 2900 }; +enum { k_iClientDeviceAuthCallbacks = 3000 }; +enum { k_iClientNetworkDeviceManagerCallbacks = 3100 }; +enum { k_iClientMusicCallbacks = 3200 }; +enum { k_iClientRemoteClientManagerCallbacks = 3300 }; +enum { k_iClientUGCCallbacks = 3400 }; +enum { k_iSteamStreamClientCallbacks = 3500 }; +enum { k_IClientProductBuilderCallbacks = 3600 }; +enum { k_iClientShortcutsCallbacks = 3700 }; +enum { k_iClientRemoteControlManagerCallbacks = 3800 }; +enum { k_iSteamAppListCallbacks = 3900 }; +enum { k_iSteamMusicCallbacks = 4000 }; +enum { k_iSteamMusicRemoteCallbacks = 4100 }; +enum { k_iClientVRCallbacks = 4200 }; +enum { k_iClientGameNotificationCallbacks = 4300 }; +enum { k_iSteamGameNotificationCallbacks = 4400 }; +enum { k_iSteamHTMLSurfaceCallbacks = 4500 }; +enum { k_iClientVideoCallbacks = 4600 }; +enum { k_iClientInventoryCallbacks = 4700 }; +enum { k_iClientBluetoothManagerCallbacks = 4800 }; + +//----------------------------------------------------------------------------- +// The CALLBACK macros are for client side callback logging enabled with +// log_callback +// Do not change any of these. +//----------------------------------------------------------------------------- + +#ifdef STEAM_CALLBACK_INSPECTION_ENABLED + +#define DEFINE_CALLBACK( callbackname, callbackid ) \ +struct callbackname { \ + typedef callbackname SteamCallback_t; \ + enum { k_iCallback = callbackid }; \ + static callbackname *GetNullPointer() { return 0; } \ + static const char *GetCallbackName() { return #callbackname; } \ + static uint32 GetCallbackID() { return callbackname::k_iCallback; } + +#define CALLBACK_MEMBER( varidx, vartype, varname ) \ + public: vartype varname ; \ + static void GetMemberVar_##varidx( unsigned int &varOffset, unsigned int &varSize, uint32 &varCount, const char **pszName, const char **pszType ) { \ + varOffset = (unsigned int)(size_t)&GetNullPointer()->varname; \ + varSize = sizeof( vartype ); \ + varCount = 1; \ + *pszName = #varname; *pszType = #vartype; } + +#define CALLBACK_ARRAY( varidx, vartype, varname, varcount ) \ + public: vartype varname [ varcount ]; \ + static void GetMemberVar_##varidx( unsigned int &varOffset, unsigned int &varSize, uint32 &varCount, const char **pszName, const char **pszType ) { \ + varOffset = (unsigned int)(size_t)&GetNullPointer()->varname[0]; \ + varSize = sizeof( vartype ); \ + varCount = varcount; \ + *pszName = #varname; *pszType = #vartype; } + + +#define END_CALLBACK_INTERNAL_BEGIN( numvars ) \ + static uint32 GetNumMemberVariables() { return numvars; } \ + static bool GetMemberVariable( uint32 index, uint32 &varOffset, uint32 &varSize, uint32 &varCount, const char **pszName, const char **pszType ) { \ + switch ( index ) { default : return false; + + +#define END_CALLBACK_INTERNAL_SWITCH( varidx ) case varidx : GetMemberVar_##varidx( varOffset, varSize, varCount, pszName, pszType ); return true; + +#define END_CALLBACK_INTERNAL_END() }; } }; + +#define END_DEFINE_CALLBACK_0() \ + static uint32 GetNumMemberVariables() { return 0; } \ + static bool GetMemberVariable( uint32 index, uint32 &varOffset, uint32 &varSize, uint32 &varCount, const char **pszName, const char **pszType ) { REFERENCE( pszType ); REFERENCE( pszName ); REFERENCE( varCount ); REFERENCE( varSize ); REFERENCE( varOffset ); REFERENCE( index ); return false; } \ + }; + +#else + +#define DEFINE_CALLBACK( callbackname, callbackid ) struct callbackname { typedef callbackname SteamCallback_t; enum { k_iCallback = callbackid }; +#define CALLBACK_MEMBER( varidx, vartype, varname ) public: vartype varname ; +#define CALLBACK_ARRAY( varidx, vartype, varname, varcount ) public: vartype varname [ varcount ]; +#define END_CALLBACK_INTERNAL_BEGIN( numvars ) +#define END_CALLBACK_INTERNAL_SWITCH( varidx ) +#define END_CALLBACK_INTERNAL_END() }; +#define END_DEFINE_CALLBACK_0() }; + +#endif + +#define END_DEFINE_CALLBACK_1() \ + END_CALLBACK_INTERNAL_BEGIN( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_2() \ + END_CALLBACK_INTERNAL_BEGIN( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_3() \ + END_CALLBACK_INTERNAL_BEGIN( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_4() \ + END_CALLBACK_INTERNAL_BEGIN( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_5() \ + END_CALLBACK_INTERNAL_BEGIN( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_END() + + +#define END_DEFINE_CALLBACK_6() \ + END_CALLBACK_INTERNAL_BEGIN( 6 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_7() \ + END_CALLBACK_INTERNAL_BEGIN( 7 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_SWITCH( 6 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_8() \ + END_CALLBACK_INTERNAL_BEGIN( 8 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_SWITCH( 6 ) \ + END_CALLBACK_INTERNAL_SWITCH( 7 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_9() \ + END_CALLBACK_INTERNAL_BEGIN( 9 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_SWITCH( 6 ) \ + END_CALLBACK_INTERNAL_SWITCH( 7 ) \ + END_CALLBACK_INTERNAL_SWITCH( 8 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_10() \ + END_CALLBACK_INTERNAL_BEGIN( 10 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_SWITCH( 6 ) \ + END_CALLBACK_INTERNAL_SWITCH( 7 ) \ + END_CALLBACK_INTERNAL_SWITCH( 8 ) \ + END_CALLBACK_INTERNAL_SWITCH( 9 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_11() \ + END_CALLBACK_INTERNAL_BEGIN( 11 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_SWITCH( 6 ) \ + END_CALLBACK_INTERNAL_SWITCH( 7 ) \ + END_CALLBACK_INTERNAL_SWITCH( 8 ) \ + END_CALLBACK_INTERNAL_SWITCH( 9 ) \ + END_CALLBACK_INTERNAL_SWITCH( 10 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_12() \ + END_CALLBACK_INTERNAL_BEGIN( 12 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_SWITCH( 6 ) \ + END_CALLBACK_INTERNAL_SWITCH( 7 ) \ + END_CALLBACK_INTERNAL_SWITCH( 8 ) \ + END_CALLBACK_INTERNAL_SWITCH( 9 ) \ + END_CALLBACK_INTERNAL_SWITCH( 10 ) \ + END_CALLBACK_INTERNAL_SWITCH( 11 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_13() \ + END_CALLBACK_INTERNAL_BEGIN( 13 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_SWITCH( 6 ) \ + END_CALLBACK_INTERNAL_SWITCH( 7 ) \ + END_CALLBACK_INTERNAL_SWITCH( 8 ) \ + END_CALLBACK_INTERNAL_SWITCH( 9 ) \ + END_CALLBACK_INTERNAL_SWITCH( 10 ) \ + END_CALLBACK_INTERNAL_SWITCH( 11 ) \ + END_CALLBACK_INTERNAL_SWITCH( 12 ) \ + END_CALLBACK_INTERNAL_END() + +#define END_DEFINE_CALLBACK_14() \ + END_CALLBACK_INTERNAL_BEGIN( 14 ) \ + END_CALLBACK_INTERNAL_SWITCH( 0 ) \ + END_CALLBACK_INTERNAL_SWITCH( 1 ) \ + END_CALLBACK_INTERNAL_SWITCH( 2 ) \ + END_CALLBACK_INTERNAL_SWITCH( 3 ) \ + END_CALLBACK_INTERNAL_SWITCH( 4 ) \ + END_CALLBACK_INTERNAL_SWITCH( 5 ) \ + END_CALLBACK_INTERNAL_SWITCH( 6 ) \ + END_CALLBACK_INTERNAL_SWITCH( 7 ) \ + END_CALLBACK_INTERNAL_SWITCH( 8 ) \ + END_CALLBACK_INTERNAL_SWITCH( 9 ) \ + END_CALLBACK_INTERNAL_SWITCH( 10 ) \ + END_CALLBACK_INTERNAL_SWITCH( 11 ) \ + END_CALLBACK_INTERNAL_SWITCH( 12 ) \ + END_CALLBACK_INTERNAL_SWITCH( 13 ) \ + END_CALLBACK_INTERNAL_END() + +#endif // ISTEAMCLIENT_H diff --git a/dep/steam_api/isteamcontroller.h b/dep/steam_api/isteamcontroller.h new file mode 100644 index 0000000..c39a48b --- /dev/null +++ b/dep/steam_api/isteamcontroller.h @@ -0,0 +1,440 @@ +//====== Copyright 1996-2013, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to valve controller +// +//============================================================================= + +#ifndef ISTEAMCONTROLLER_H +#define ISTEAMCONTROLLER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +#define STEAM_CONTROLLER_MAX_COUNT 16 + +#define STEAM_CONTROLLER_MAX_ANALOG_ACTIONS 16 + +#define STEAM_CONTROLLER_MAX_DIGITAL_ACTIONS 128 + +#define STEAM_CONTROLLER_MAX_ORIGINS 8 + +// When sending an option to a specific controller handle, you can send to all controllers via this command +#define STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS UINT64_MAX + +#define STEAM_CONTROLLER_MIN_ANALOG_ACTION_DATA -1.0f +#define STEAM_CONTROLLER_MAX_ANALOG_ACTION_DATA 1.0f + +enum ESteamControllerPad +{ + k_ESteamControllerPad_Left, + k_ESteamControllerPad_Right +}; + +enum EControllerSource +{ + k_EControllerSource_None, + k_EControllerSource_LeftTrackpad, + k_EControllerSource_RightTrackpad, + k_EControllerSource_Joystick, + k_EControllerSource_ABXY, + k_EControllerSource_Switch, + k_EControllerSource_LeftTrigger, + k_EControllerSource_RightTrigger, + k_EControllerSource_Gyro, + k_EControllerSource_CenterTrackpad, // PS4 + k_EControllerSource_RightJoystick, // Traditional Controllers + k_EControllerSource_DPad, // Traditional Controllers + k_EControllerSource_Count +}; + +enum EControllerSourceMode +{ + k_EControllerSourceMode_None, + k_EControllerSourceMode_Dpad, + k_EControllerSourceMode_Buttons, + k_EControllerSourceMode_FourButtons, + k_EControllerSourceMode_AbsoluteMouse, + k_EControllerSourceMode_RelativeMouse, + k_EControllerSourceMode_JoystickMove, + k_EControllerSourceMode_JoystickMouse, + k_EControllerSourceMode_JoystickCamera, + k_EControllerSourceMode_ScrollWheel, + k_EControllerSourceMode_Trigger, + k_EControllerSourceMode_TouchMenu, + k_EControllerSourceMode_MouseJoystick, + k_EControllerSourceMode_MouseRegion, + k_EControllerSourceMode_RadialMenu, + k_EControllerSourceMode_SingleButton, + k_EControllerSourceMode_Switches +}; + +enum EControllerActionOrigin +{ + // Steam Controller + k_EControllerActionOrigin_None, + k_EControllerActionOrigin_A, + k_EControllerActionOrigin_B, + k_EControllerActionOrigin_X, + k_EControllerActionOrigin_Y, + k_EControllerActionOrigin_LeftBumper, + k_EControllerActionOrigin_RightBumper, + k_EControllerActionOrigin_LeftGrip, + k_EControllerActionOrigin_RightGrip, + k_EControllerActionOrigin_Start, + k_EControllerActionOrigin_Back, + k_EControllerActionOrigin_LeftPad_Touch, + k_EControllerActionOrigin_LeftPad_Swipe, + k_EControllerActionOrigin_LeftPad_Click, + k_EControllerActionOrigin_LeftPad_DPadNorth, + k_EControllerActionOrigin_LeftPad_DPadSouth, + k_EControllerActionOrigin_LeftPad_DPadWest, + k_EControllerActionOrigin_LeftPad_DPadEast, + k_EControllerActionOrigin_RightPad_Touch, + k_EControllerActionOrigin_RightPad_Swipe, + k_EControllerActionOrigin_RightPad_Click, + k_EControllerActionOrigin_RightPad_DPadNorth, + k_EControllerActionOrigin_RightPad_DPadSouth, + k_EControllerActionOrigin_RightPad_DPadWest, + k_EControllerActionOrigin_RightPad_DPadEast, + k_EControllerActionOrigin_LeftTrigger_Pull, + k_EControllerActionOrigin_LeftTrigger_Click, + k_EControllerActionOrigin_RightTrigger_Pull, + k_EControllerActionOrigin_RightTrigger_Click, + k_EControllerActionOrigin_LeftStick_Move, + k_EControllerActionOrigin_LeftStick_Click, + k_EControllerActionOrigin_LeftStick_DPadNorth, + k_EControllerActionOrigin_LeftStick_DPadSouth, + k_EControllerActionOrigin_LeftStick_DPadWest, + k_EControllerActionOrigin_LeftStick_DPadEast, + k_EControllerActionOrigin_Gyro_Move, + k_EControllerActionOrigin_Gyro_Pitch, + k_EControllerActionOrigin_Gyro_Yaw, + k_EControllerActionOrigin_Gyro_Roll, + + // PS4 Dual Shock + k_EControllerActionOrigin_PS4_X, + k_EControllerActionOrigin_PS4_Circle, + k_EControllerActionOrigin_PS4_Triangle, + k_EControllerActionOrigin_PS4_Square, + k_EControllerActionOrigin_PS4_LeftBumper, + k_EControllerActionOrigin_PS4_RightBumper, + k_EControllerActionOrigin_PS4_Options, //Start + k_EControllerActionOrigin_PS4_Share, //Back + k_EControllerActionOrigin_PS4_LeftPad_Touch, + k_EControllerActionOrigin_PS4_LeftPad_Swipe, + k_EControllerActionOrigin_PS4_LeftPad_Click, + k_EControllerActionOrigin_PS4_LeftPad_DPadNorth, + k_EControllerActionOrigin_PS4_LeftPad_DPadSouth, + k_EControllerActionOrigin_PS4_LeftPad_DPadWest, + k_EControllerActionOrigin_PS4_LeftPad_DPadEast, + k_EControllerActionOrigin_PS4_RightPad_Touch, + k_EControllerActionOrigin_PS4_RightPad_Swipe, + k_EControllerActionOrigin_PS4_RightPad_Click, + k_EControllerActionOrigin_PS4_RightPad_DPadNorth, + k_EControllerActionOrigin_PS4_RightPad_DPadSouth, + k_EControllerActionOrigin_PS4_RightPad_DPadWest, + k_EControllerActionOrigin_PS4_RightPad_DPadEast, + k_EControllerActionOrigin_PS4_CenterPad_Touch, + k_EControllerActionOrigin_PS4_CenterPad_Swipe, + k_EControllerActionOrigin_PS4_CenterPad_Click, + k_EControllerActionOrigin_PS4_CenterPad_DPadNorth, + k_EControllerActionOrigin_PS4_CenterPad_DPadSouth, + k_EControllerActionOrigin_PS4_CenterPad_DPadWest, + k_EControllerActionOrigin_PS4_CenterPad_DPadEast, + k_EControllerActionOrigin_PS4_LeftTrigger_Pull, + k_EControllerActionOrigin_PS4_LeftTrigger_Click, + k_EControllerActionOrigin_PS4_RightTrigger_Pull, + k_EControllerActionOrigin_PS4_RightTrigger_Click, + k_EControllerActionOrigin_PS4_LeftStick_Move, + k_EControllerActionOrigin_PS4_LeftStick_Click, + k_EControllerActionOrigin_PS4_LeftStick_DPadNorth, + k_EControllerActionOrigin_PS4_LeftStick_DPadSouth, + k_EControllerActionOrigin_PS4_LeftStick_DPadWest, + k_EControllerActionOrigin_PS4_LeftStick_DPadEast, + k_EControllerActionOrigin_PS4_RightStick_Move, + k_EControllerActionOrigin_PS4_RightStick_Click, + k_EControllerActionOrigin_PS4_RightStick_DPadNorth, + k_EControllerActionOrigin_PS4_RightStick_DPadSouth, + k_EControllerActionOrigin_PS4_RightStick_DPadWest, + k_EControllerActionOrigin_PS4_RightStick_DPadEast, + k_EControllerActionOrigin_PS4_DPad_North, + k_EControllerActionOrigin_PS4_DPad_South, + k_EControllerActionOrigin_PS4_DPad_West, + k_EControllerActionOrigin_PS4_DPad_East, + k_EControllerActionOrigin_PS4_Gyro_Move, + k_EControllerActionOrigin_PS4_Gyro_Pitch, + k_EControllerActionOrigin_PS4_Gyro_Yaw, + k_EControllerActionOrigin_PS4_Gyro_Roll, + + // XBox One + k_EControllerActionOrigin_XBoxOne_A, + k_EControllerActionOrigin_XBoxOne_B, + k_EControllerActionOrigin_XBoxOne_X, + k_EControllerActionOrigin_XBoxOne_Y, + k_EControllerActionOrigin_XBoxOne_LeftBumper, + k_EControllerActionOrigin_XBoxOne_RightBumper, + k_EControllerActionOrigin_XBoxOne_Menu, //Start + k_EControllerActionOrigin_XBoxOne_View, //Back + k_EControllerActionOrigin_XBoxOne_LeftTrigger_Pull, + k_EControllerActionOrigin_XBoxOne_LeftTrigger_Click, + k_EControllerActionOrigin_XBoxOne_RightTrigger_Pull, + k_EControllerActionOrigin_XBoxOne_RightTrigger_Click, + k_EControllerActionOrigin_XBoxOne_LeftStick_Move, + k_EControllerActionOrigin_XBoxOne_LeftStick_Click, + k_EControllerActionOrigin_XBoxOne_LeftStick_DPadNorth, + k_EControllerActionOrigin_XBoxOne_LeftStick_DPadSouth, + k_EControllerActionOrigin_XBoxOne_LeftStick_DPadWest, + k_EControllerActionOrigin_XBoxOne_LeftStick_DPadEast, + k_EControllerActionOrigin_XBoxOne_RightStick_Move, + k_EControllerActionOrigin_XBoxOne_RightStick_Click, + k_EControllerActionOrigin_XBoxOne_RightStick_DPadNorth, + k_EControllerActionOrigin_XBoxOne_RightStick_DPadSouth, + k_EControllerActionOrigin_XBoxOne_RightStick_DPadWest, + k_EControllerActionOrigin_XBoxOne_RightStick_DPadEast, + k_EControllerActionOrigin_XBoxOne_DPad_North, + k_EControllerActionOrigin_XBoxOne_DPad_South, + k_EControllerActionOrigin_XBoxOne_DPad_West, + k_EControllerActionOrigin_XBoxOne_DPad_East, + + // XBox 360 + k_EControllerActionOrigin_XBox360_A, + k_EControllerActionOrigin_XBox360_B, + k_EControllerActionOrigin_XBox360_X, + k_EControllerActionOrigin_XBox360_Y, + k_EControllerActionOrigin_XBox360_LeftBumper, + k_EControllerActionOrigin_XBox360_RightBumper, + k_EControllerActionOrigin_XBox360_Start, //Start + k_EControllerActionOrigin_XBox360_Back, //Back + k_EControllerActionOrigin_XBox360_LeftTrigger_Pull, + k_EControllerActionOrigin_XBox360_LeftTrigger_Click, + k_EControllerActionOrigin_XBox360_RightTrigger_Pull, + k_EControllerActionOrigin_XBox360_RightTrigger_Click, + k_EControllerActionOrigin_XBox360_LeftStick_Move, + k_EControllerActionOrigin_XBox360_LeftStick_Click, + k_EControllerActionOrigin_XBox360_LeftStick_DPadNorth, + k_EControllerActionOrigin_XBox360_LeftStick_DPadSouth, + k_EControllerActionOrigin_XBox360_LeftStick_DPadWest, + k_EControllerActionOrigin_XBox360_LeftStick_DPadEast, + k_EControllerActionOrigin_XBox360_RightStick_Move, + k_EControllerActionOrigin_XBox360_RightStick_Click, + k_EControllerActionOrigin_XBox360_RightStick_DPadNorth, + k_EControllerActionOrigin_XBox360_RightStick_DPadSouth, + k_EControllerActionOrigin_XBox360_RightStick_DPadWest, + k_EControllerActionOrigin_XBox360_RightStick_DPadEast, + k_EControllerActionOrigin_XBox360_DPad_North, + k_EControllerActionOrigin_XBox360_DPad_South, + k_EControllerActionOrigin_XBox360_DPad_West, + k_EControllerActionOrigin_XBox360_DPad_East, + + // SteamController V2 + k_EControllerActionOrigin_SteamV2_A, + k_EControllerActionOrigin_SteamV2_B, + k_EControllerActionOrigin_SteamV2_X, + k_EControllerActionOrigin_SteamV2_Y, + k_EControllerActionOrigin_SteamV2_LeftBumper, + k_EControllerActionOrigin_SteamV2_RightBumper, + k_EControllerActionOrigin_SteamV2_LeftGrip, + k_EControllerActionOrigin_SteamV2_RightGrip, + k_EControllerActionOrigin_SteamV2_LeftGrip_Upper, + k_EControllerActionOrigin_SteamV2_RightGrip_Upper, + k_EControllerActionOrigin_SteamV2_LeftBumper_Pressure, + k_EControllerActionOrigin_SteamV2_RightBumper_Pressure, + k_EControllerActionOrigin_SteamV2_LeftGrip_Pressure, + k_EControllerActionOrigin_SteamV2_RightGrip_Pressure, + k_EControllerActionOrigin_SteamV2_LeftGrip_Upper_Pressure, + k_EControllerActionOrigin_SteamV2_RightGrip_Upper_Pressure, + k_EControllerActionOrigin_SteamV2_Start, + k_EControllerActionOrigin_SteamV2_Back, + k_EControllerActionOrigin_SteamV2_LeftPad_Touch, + k_EControllerActionOrigin_SteamV2_LeftPad_Swipe, + k_EControllerActionOrigin_SteamV2_LeftPad_Click, + k_EControllerActionOrigin_SteamV2_LeftPad_Pressure, + k_EControllerActionOrigin_SteamV2_LeftPad_DPadNorth, + k_EControllerActionOrigin_SteamV2_LeftPad_DPadSouth, + k_EControllerActionOrigin_SteamV2_LeftPad_DPadWest, + k_EControllerActionOrigin_SteamV2_LeftPad_DPadEast, + k_EControllerActionOrigin_SteamV2_RightPad_Touch, + k_EControllerActionOrigin_SteamV2_RightPad_Swipe, + k_EControllerActionOrigin_SteamV2_RightPad_Click, + k_EControllerActionOrigin_SteamV2_RightPad_Pressure, + k_EControllerActionOrigin_SteamV2_RightPad_DPadNorth, + k_EControllerActionOrigin_SteamV2_RightPad_DPadSouth, + k_EControllerActionOrigin_SteamV2_RightPad_DPadWest, + k_EControllerActionOrigin_SteamV2_RightPad_DPadEast, + k_EControllerActionOrigin_SteamV2_LeftTrigger_Pull, + k_EControllerActionOrigin_SteamV2_LeftTrigger_Click, + k_EControllerActionOrigin_SteamV2_RightTrigger_Pull, + k_EControllerActionOrigin_SteamV2_RightTrigger_Click, + k_EControllerActionOrigin_SteamV2_LeftStick_Move, + k_EControllerActionOrigin_SteamV2_LeftStick_Click, + k_EControllerActionOrigin_SteamV2_LeftStick_DPadNorth, + k_EControllerActionOrigin_SteamV2_LeftStick_DPadSouth, + k_EControllerActionOrigin_SteamV2_LeftStick_DPadWest, + k_EControllerActionOrigin_SteamV2_LeftStick_DPadEast, + k_EControllerActionOrigin_SteamV2_Gyro_Move, + k_EControllerActionOrigin_SteamV2_Gyro_Pitch, + k_EControllerActionOrigin_SteamV2_Gyro_Yaw, + k_EControllerActionOrigin_SteamV2_Gyro_Roll, + + k_EControllerActionOrigin_Count +}; + +enum ESteamControllerLEDFlag +{ + k_ESteamControllerLEDFlag_SetColor, + k_ESteamControllerLEDFlag_RestoreUserDefault +}; + +// ControllerHandle_t is used to refer to a specific controller. +// This handle will consistently identify a controller, even if it is disconnected and re-connected +typedef uint64 ControllerHandle_t; + + +// These handles are used to refer to a specific in-game action or action set +// All action handles should be queried during initialization for performance reasons +typedef uint64 ControllerActionSetHandle_t; +typedef uint64 ControllerDigitalActionHandle_t; +typedef uint64 ControllerAnalogActionHandle_t; + +#pragma pack( push, 1 ) + +struct ControllerAnalogActionData_t +{ + // Type of data coming from this action, this will match what got specified in the action set + EControllerSourceMode eMode; + + // The current state of this action; will be delta updates for mouse actions + float x, y; + + // Whether or not this action is currently available to be bound in the active action set + bool bActive; +}; + +struct ControllerDigitalActionData_t +{ + // The current state of this action; will be true if currently pressed + bool bState; + + // Whether or not this action is currently available to be bound in the active action set + bool bActive; +}; + +struct ControllerMotionData_t +{ + // Sensor-fused absolute rotation; will drift in heading + float rotQuatX; + float rotQuatY; + float rotQuatZ; + float rotQuatW; + + // Positional acceleration + float posAccelX; + float posAccelY; + float posAccelZ; + + // Angular velocity + float rotVelX; + float rotVelY; + float rotVelZ; +}; + +#pragma pack( pop ) + + +//----------------------------------------------------------------------------- +// Purpose: Native Steam controller support API +//----------------------------------------------------------------------------- +class ISteamController +{ +public: + + // Init and Shutdown must be called when starting/ending use of this interface + virtual bool Init() = 0; + virtual bool Shutdown() = 0; + + // Synchronize API state with the latest Steam Controller inputs available. This + // is performed automatically by SteamAPI_RunCallbacks, but for the absolute lowest + // possible latency, you call this directly before reading controller state. + virtual void RunFrame() = 0; + + // Enumerate currently connected controllers + // handlesOut should point to a STEAM_CONTROLLER_MAX_COUNT sized array of ControllerHandle_t handles + // Returns the number of handles written to handlesOut + virtual int GetConnectedControllers( ControllerHandle_t *handlesOut ) = 0; + + // Invokes the Steam overlay and brings up the binding screen + // Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode + virtual bool ShowBindingPanel( ControllerHandle_t controllerHandle ) = 0; + + // ACTION SETS + // Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls. + virtual ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName ) = 0; + + // Reconfigure the controller to use the specified action set (ie 'Menu', 'Walk' or 'Drive') + // This is cheap, and can be safely called repeatedly. It's often easier to repeatedly call it in + // your state loops, instead of trying to place it in all of your state transitions. + virtual void ActivateActionSet( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle ) = 0; + virtual ControllerActionSetHandle_t GetCurrentActionSet( ControllerHandle_t controllerHandle ) = 0; + + // ACTIONS + // Lookup the handle for a digital action. Best to do this once on startup, and store the handles for all future API calls. + virtual ControllerDigitalActionHandle_t GetDigitalActionHandle( const char *pszActionName ) = 0; + + // Returns the current state of the supplied digital game action + virtual ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle ) = 0; + + // Get the origin(s) for a digital action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action. + // originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles + virtual int GetDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin *originsOut ) = 0; + + // Lookup the handle for an analog action. Best to do this once on startup, and store the handles for all future API calls. + virtual ControllerAnalogActionHandle_t GetAnalogActionHandle( const char *pszActionName ) = 0; + + // Returns the current state of these supplied analog game action + virtual ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle ) = 0; + + // Get the origin(s) for an analog action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action. + // originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles + virtual int GetAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin *originsOut ) = 0; + + virtual void StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction ) = 0; + + // Trigger a haptic pulse on a controller + virtual void TriggerHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec ) = 0; + + // Trigger a pulse with a duty cycle of usDurationMicroSec / usOffMicroSec, unRepeat times. + // nFlags is currently unused and reserved for future use. + virtual void TriggerRepeatedHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags ) = 0; + + // Tigger a vibration event on supported controllers. + virtual void TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed ) = 0; + + // Set the controller LED color on supported controllers. + virtual void SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags ) = 0; + + // Returns the associated gamepad index for the specified controller, if emulating a gamepad + virtual int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle ) = 0; + + // Returns the associated controller handle for the specified emulated gamepad + virtual ControllerHandle_t GetControllerForGamepadIndex( int nIndex ) = 0; + + // Returns raw motion data from the specified controller + virtual ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle ) = 0; + + // Attempt to display origins of given action in the controller HUD, for the currently active action set + // Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode + virtual bool ShowDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition ) = 0; + virtual bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition ) = 0; + + // Returns a localized string (from Steam's language setting) for the specified origin + virtual const char *GetStringForActionOrigin( EControllerActionOrigin eOrigin ) = 0; + + // Get a local path to art for on-screen glyph for a particular origin + virtual const char *GetGlyphForActionOrigin( EControllerActionOrigin eOrigin ) = 0; +}; + +#define STEAMCONTROLLER_INTERFACE_VERSION "SteamController005" + +#endif // ISTEAMCONTROLLER_H diff --git a/dep/steam_api/isteamfriends.h b/dep/steam_api/isteamfriends.h new file mode 100644 index 0000000..29827b6 --- /dev/null +++ b/dep/steam_api/isteamfriends.h @@ -0,0 +1,636 @@ +//====== Copyright (C) 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, // this doesn't get stored; the user has just done an Ignore on an friendship invite + k_EFriendRelationshipRequestRecipient = 2, + k_EFriendRelationshipFriend = 3, + k_EFriendRelationshipRequestInitiator = 4, + k_EFriendRelationshipIgnored = 5, // this is stored; the user has explicit blocked this other user from comments/chat/etc + k_EFriendRelationshipIgnoredFriend = 6, + k_EFriendRelationshipSuggested_DEPRECATED = 7, // was used by the original implementation of the facebook linking feature, but now unused. + + // keep this updated + k_EFriendRelationshipMax = 8, +}; + +// maximum length of friend group name (not including terminating nul!) +const int k_cchMaxFriendsGroupName = 64; + +// maximum number of groups a single user is allowed +const int k_cFriendsGroupLimit = 100; + +// friends group identifier type +typedef int16 FriendsGroupID_t; + +// invalid friends group identifier constant +const FriendsGroupID_t k_FriendsGroupID_Invalid = -1; + +const int k_cEnumerateFollowersMax = 50; + + +//----------------------------------------------------------------------------- +// 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_EPersonaStateLookingToTrade = 5, // Online, trading + k_EPersonaStateLookingToPlay = 6, // Online, wanting to play + 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_EFriendFlagSuggested = 0x800, // not used + k_EFriendFlagChatMember = 0x1000, + k_EFriendFlagAll = 0xFFFF, +}; + + +// friend game played information +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif +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, +}; + +//----------------------------------------------------------------------------- +// Purpose: user restriction flags +//----------------------------------------------------------------------------- +enum EUserRestriction +{ + k_nUserRestrictionNone = 0, // no known chat/content restriction + k_nUserRestrictionUnknown = 1, // we don't know yet (user offline) + k_nUserRestrictionAnyChat = 2, // user is not allowed to (or can't) send/recv any chat + k_nUserRestrictionVoiceChat = 4, // user is not allowed to (or can't) send/recv voice chat + k_nUserRestrictionGroupChat = 8, // user is not allowed to (or can't) send/recv group chat + k_nUserRestrictionRating = 16, // user is too young according to rating in current region + k_nUserRestrictionGameInvites = 32, // user cannot send or recv game invites (e.g. mobile) + k_nUserRestrictionTrading = 64, // user cannot participate in trading (console, mobile) +}; + +//----------------------------------------------------------------------------- +// Purpose: information about user sessions +//----------------------------------------------------------------------------- +struct FriendSessionStateInfo_t +{ + uint32 m_uiOnlineSessionInstances; + uint8 m_uiPublishedToFriendsSessionInstance; +}; + + + +// size limit on chat room or member metadata +const uint32 k_cubChatMetadataMax = 8192; + +// size limits on Rich Presence data +enum { k_cchMaxRichPresenceKeys = 20 }; +enum { k_cchMaxRichPresenceKeyLength = 64 }; +enum { k_cchMaxRichPresenceValueLength = 256 }; + +// These values are passed as parameters to the store +enum EOverlayToStoreFlag +{ + k_EOverlayToStoreFlag_None = 0, + k_EOverlayToStoreFlag_AddToCart = 1, + k_EOverlayToStoreFlag_AddToCartAndShow = 2, +}; + +//----------------------------------------------------------------------------- +// 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. + // Changes take place locally immediately, and a PersonaStateChange_t is posted, presuming success. + // + // The final results are available through the return value SteamAPICall_t, using SetPersonaNameResponse_t. + // + // If the name change fails to happen on the server, then an additional global PersonaStateChange_t will be posted + // to change the name back, in addition to the SetPersonaNameResponse_t callback. + CALL_RESULT( SetPersonaNameResponse_t ) + virtual SteamAPICall_t 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; + + // returns true if the friend is actually in a game, and fills in pFriendGameInfo with an extra details + virtual bool GetFriendGamePlayed( CSteamID steamIDFriend, OUT_STRUCT() 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; + // friends steam level + virtual int GetFriendSteamLevel( CSteamID steamIDFriend ) = 0; + + // Returns nickname the current user has set for the specified player. Returns NULL if the no nickname has been set for that player. + virtual const char *GetPlayerNickname( CSteamID steamIDPlayer ) = 0; + + // friend grouping (tag) apis + // returns the number of friends groups + virtual int GetFriendsGroupCount() = 0; + // returns the friends group ID for the given index (invalid indices return k_FriendsGroupID_Invalid) + virtual FriendsGroupID_t GetFriendsGroupIDByIndex( int iFG ) = 0; + // returns the name for the given friends group (NULL in the case of invalid friends group IDs) + virtual const char *GetFriendsGroupName( FriendsGroupID_t friendsGroupID ) = 0; + // returns the number of members in a given friends group + virtual int GetFriendsGroupMembersCount( FriendsGroupID_t friendsGroupID ) = 0; + // gets up to nMembersCount members of the given friends group, if fewer exist than requested those positions' SteamIDs will be invalid + virtual void GetFriendsGroupMembersList( FriendsGroupID_t friendsGroupID, OUT_ARRAY_CALL(nMembersCount, GetFriendsGroupMembersCount, friendsGroupID ) CSteamID *pOutSteamIDMembers, int nMembersCount ) = 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; + // returns the most recent information we have about what's happening in a clan + virtual bool GetClanActivityCounts( CSteamID steamIDClan, int *pnOnline, int *pnInGame, int *pnChatting ) = 0; + // for clans a user is a member of, they will have reasonably up-to-date information, but for others you'll have to download the info to have the latest + virtual SteamAPICall_t DownloadClanActivityCounts( ARRAY_COUNT(cClansToRequest) CSteamID *psteamIDClans, int cClansToRequest ) = 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 + // note that the current user must be in a lobby to retrieve CSteamIDs of other users in that lobby + // 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", "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 + // "jointrade" - opens a window to a Steam Trading session that was started with the ISteamEconomy/StartTrade Web API + // "stats" - opens the overlay web browser to the specified user's stats + // "achievements" - opens the overlay web browser to the specified user's achievements + // "friendadd" - opens the overlay in minimal mode prompting the user to add the target user as a friend + // "friendremove" - opens the overlay in minimal mode prompting the user to remove the target friend + // "friendrequestaccept" - opens the overlay in minimal mode prompting the user to accept an incoming friend invite + // "friendrequestignore" - opens the overlay in minimal mode prompting the user to ignore an incoming friend invite + 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, EOverlayToStoreFlag eFlag ) = 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. + virtual void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby ) = 0; + + // gets the small (32x32) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set + virtual int GetSmallFriendAvatar( CSteamID steamIDFriend ) = 0; + + // gets the medium (64x64) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set + virtual int GetMediumFriendAvatar( CSteamID steamIDFriend ) = 0; + + // gets the large (184x184) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set + // returns -1 if this image has yet to be loaded, in this case wait for a AvatarImageLoaded_t callback and then call this again + virtual int GetLargeFriendAvatar( CSteamID steamIDFriend ) = 0; + + // requests information about a user - persona name & avatar + // if bRequireNameOnly is set, then the avatar of a user isn't downloaded + // - it's a lot slower to download avatars and churns the local cache, so if you don't need avatars, don't request them + // if returns true, it means that data is being requested, and a PersonaStateChanged_t callback will be posted when it's retrieved + // if returns false, it means that we already have all the details about that user, and functions can be called immediately + virtual bool RequestUserInformation( CSteamID steamIDUser, bool bRequireNameOnly ) = 0; + + // requests information about a clan officer list + // when complete, data is returned in ClanOfficerListResponse_t call result + // this makes available the calls below + // you can only ask about clans that a user is a member of + // note that this won't download avatars automatically; if you get an officer, + // and no avatar image is available, call RequestUserInformation( steamID, false ) to download the avatar + CALL_RESULT( ClanOfficerListResponse_t ) + virtual SteamAPICall_t RequestClanOfficerList( CSteamID steamIDClan ) = 0; + + // iteration of clan officers - can only be done when a RequestClanOfficerList() call has completed + + // returns the steamID of the clan owner + virtual CSteamID GetClanOwner( CSteamID steamIDClan ) = 0; + // returns the number of officers in a clan (including the owner) + virtual int GetClanOfficerCount( CSteamID steamIDClan ) = 0; + // returns the steamID of a clan officer, by index, of range [0,GetClanOfficerCount) + virtual CSteamID GetClanOfficerByIndex( CSteamID steamIDClan, int iOfficer ) = 0; + // if current user is chat restricted, he can't send or receive any text/voice chat messages. + // the user can't see custom avatars. But the user can be online and send/recv game invites. + // a chat restricted user can't add friends or join any groups. + virtual uint32 GetUserRestrictions() = 0; + + // Rich Presence data is automatically shared between friends who are in the same game + // Each user has a set of Key/Value pairs + // Note the following limits: k_cchMaxRichPresenceKeys, k_cchMaxRichPresenceKeyLength, k_cchMaxRichPresenceValueLength + // There are two magic keys: + // "status" - a UTF-8 string that will show up in the 'view game info' dialog in the Steam friends list + // "connect" - a UTF-8 string that contains the command-line for how a friend can connect to a game + // GetFriendRichPresence() returns an empty string "" if no value is set + // SetRichPresence() to a NULL or an empty string deletes the key + // You can iterate the current set of keys for a friend with GetFriendRichPresenceKeyCount() + // and GetFriendRichPresenceKeyByIndex() (typically only used for debugging) + virtual bool SetRichPresence( const char *pchKey, const char *pchValue ) = 0; + virtual void ClearRichPresence() = 0; + virtual const char *GetFriendRichPresence( CSteamID steamIDFriend, const char *pchKey ) = 0; + virtual int GetFriendRichPresenceKeyCount( CSteamID steamIDFriend ) = 0; + virtual const char *GetFriendRichPresenceKeyByIndex( CSteamID steamIDFriend, int iKey ) = 0; + // Requests rich presence for a specific user. + virtual void RequestFriendRichPresence( CSteamID steamIDFriend ) = 0; + + // rich invite support + // if the target accepts the invite, the pchConnectString gets added to the command-line for launching the game + // if the game is already running, a GameRichPresenceJoinRequested_t callback is posted containing the connect string + // invites can only be sent to friends + virtual bool InviteUserToGame( CSteamID steamIDFriend, const char *pchConnectString ) = 0; + + // recently-played-with friends iteration + // this iterates the entire list of users recently played with, across games + // GetFriendCoplayTime() returns as a unix time + virtual int GetCoplayFriendCount() = 0; + virtual CSteamID GetCoplayFriend( int iCoplayFriend ) = 0; + virtual int GetFriendCoplayTime( CSteamID steamIDFriend ) = 0; + virtual AppId_t GetFriendCoplayGame( CSteamID steamIDFriend ) = 0; + + // chat interface for games + // this allows in-game access to group (clan) chats from in the game + // the behavior is somewhat sophisticated, because the user may or may not be already in the group chat from outside the game or in the overlay + // use ActivateGameOverlayToUser( "chat", steamIDClan ) to open the in-game overlay version of the chat + CALL_RESULT( JoinClanChatRoomCompletionResult_t ) + virtual SteamAPICall_t JoinClanChatRoom( CSteamID steamIDClan ) = 0; + virtual bool LeaveClanChatRoom( CSteamID steamIDClan ) = 0; + virtual int GetClanChatMemberCount( CSteamID steamIDClan ) = 0; + virtual CSteamID GetChatMemberByIndex( CSteamID steamIDClan, int iUser ) = 0; + virtual bool SendClanChatMessage( CSteamID steamIDClanChat, const char *pchText ) = 0; + virtual int GetClanChatMessage( CSteamID steamIDClanChat, int iMessage, void *prgchText, int cchTextMax, EChatEntryType *peChatEntryType, OUT_STRUCT() CSteamID *psteamidChatter ) = 0; + virtual bool IsClanChatAdmin( CSteamID steamIDClanChat, CSteamID steamIDUser ) = 0; + + // interact with the Steam (game overlay / desktop) + virtual bool IsClanChatWindowOpenInSteam( CSteamID steamIDClanChat ) = 0; + virtual bool OpenClanChatWindowInSteam( CSteamID steamIDClanChat ) = 0; + virtual bool CloseClanChatWindowInSteam( CSteamID steamIDClanChat ) = 0; + + // peer-to-peer chat interception + // this is so you can show P2P chats inline in the game + virtual bool SetListenForFriendsMessages( bool bInterceptEnabled ) = 0; + virtual bool ReplyToFriendMessage( CSteamID steamIDFriend, const char *pchMsgToSend ) = 0; + virtual int GetFriendMessage( CSteamID steamIDFriend, int iMessageID, void *pvData, int cubData, EChatEntryType *peChatEntryType ) = 0; + + // following apis + CALL_RESULT( FriendsGetFollowerCount_t ) + virtual SteamAPICall_t GetFollowerCount( CSteamID steamID ) = 0; + CALL_RESULT( FriendsIsFollowing_t ) + virtual SteamAPICall_t IsFollowing( CSteamID steamID ) = 0; + CALL_RESULT( FriendsEnumerateFollowingList_t ) + virtual SteamAPICall_t EnumerateFollowingList( uint32 unStartIndex ) = 0; +}; + +#define STEAMFRIENDS_INTERFACE_VERSION "SteamFriends015" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +//----------------------------------------------------------------------------- +// 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 = 0x0001, + k_EPersonaChangeStatus = 0x0002, + k_EPersonaChangeComeOnline = 0x0004, + k_EPersonaChangeGoneOffline = 0x0008, + k_EPersonaChangeGamePlayed = 0x0010, + k_EPersonaChangeGameServer = 0x0020, + k_EPersonaChangeAvatar = 0x0040, + k_EPersonaChangeJoinedSource= 0x0080, + k_EPersonaChangeLeftSource = 0x0100, + k_EPersonaChangeRelationshipChanged = 0x0200, + k_EPersonaChangeNameFirstSet = 0x0400, + k_EPersonaChangeFacebookInfo = 0x0800, + k_EPersonaChangeNickname = 0x1000, + k_EPersonaChangeSteamLevel = 0x2000, +}; + + +//----------------------------------------------------------------------------- +// 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; + + // The friend they did the join via (will be invalid if not directly via a friend) + // + // On PS3, the friend will be invalid if this was triggered by a PSN invite via the XMB, but + // the account type will be console user so you can tell at least that this was from a PSN friend + // rather than a Steam friend. + CSteamID m_steamIDFriend; +}; + + +//----------------------------------------------------------------------------- +// Purpose: called when an avatar is loaded in from a previous GetLargeFriendAvatar() call +// if the image wasn't already available +//----------------------------------------------------------------------------- +struct AvatarImageLoaded_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 34 }; + CSteamID m_steamID; // steamid the avatar has been loaded for + int m_iImage; // the image index of the now loaded image + int m_iWide; // width of the loaded image + int m_iTall; // height of the loaded image +}; + + +//----------------------------------------------------------------------------- +// Purpose: marks the return of a request officer list call +//----------------------------------------------------------------------------- +struct ClanOfficerListResponse_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 35 }; + CSteamID m_steamIDClan; + int m_cOfficers; + uint8 m_bSuccess; +}; + + +//----------------------------------------------------------------------------- +// Purpose: callback indicating updated data about friends rich presence information +//----------------------------------------------------------------------------- +struct FriendRichPresenceUpdate_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 36 }; + CSteamID m_steamIDFriend; // friend who's rich presence has changed + AppId_t m_nAppID; // the appID of the game (should always be the current game) +}; + + +//----------------------------------------------------------------------------- +// Purpose: called when the user tries to join a game from their friends list +// rich presence will have been set with the "connect" key which is set here +//----------------------------------------------------------------------------- +struct GameRichPresenceJoinRequested_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 37 }; + CSteamID m_steamIDFriend; // the friend they did the join via (will be invalid if not directly via a friend) + char m_rgchConnect[k_cchMaxRichPresenceValueLength]; +}; + + +//----------------------------------------------------------------------------- +// Purpose: a chat message has been received for a clan chat the game has joined +//----------------------------------------------------------------------------- +struct GameConnectedClanChatMsg_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 38 }; + CSteamID m_steamIDClanChat; + CSteamID m_steamIDUser; + int m_iMessageID; +}; + + +//----------------------------------------------------------------------------- +// Purpose: a user has joined a clan chat +//----------------------------------------------------------------------------- +struct GameConnectedChatJoin_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 39 }; + CSteamID m_steamIDClanChat; + CSteamID m_steamIDUser; +}; + + +//----------------------------------------------------------------------------- +// Purpose: a user has left the chat we're in +//----------------------------------------------------------------------------- +struct GameConnectedChatLeave_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 40 }; + CSteamID m_steamIDClanChat; + CSteamID m_steamIDUser; + bool m_bKicked; // true if admin kicked + bool m_bDropped; // true if Steam connection dropped +}; + + +//----------------------------------------------------------------------------- +// Purpose: a DownloadClanActivityCounts() call has finished +//----------------------------------------------------------------------------- +struct DownloadClanActivityCountsResult_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 41 }; + bool m_bSuccess; +}; + + +//----------------------------------------------------------------------------- +// Purpose: a JoinClanChatRoom() call has finished +//----------------------------------------------------------------------------- +struct JoinClanChatRoomCompletionResult_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 42 }; + CSteamID m_steamIDClanChat; + EChatRoomEnterResponse m_eChatRoomEnterResponse; +}; + +//----------------------------------------------------------------------------- +// Purpose: a chat message has been received from a user +//----------------------------------------------------------------------------- +struct GameConnectedFriendChatMsg_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 43 }; + CSteamID m_steamIDUser; + int m_iMessageID; +}; + + +struct FriendsGetFollowerCount_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 44 }; + EResult m_eResult; + CSteamID m_steamID; + int m_nCount; +}; + + +struct FriendsIsFollowing_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 45 }; + EResult m_eResult; + CSteamID m_steamID; + bool m_bIsFollowing; +}; + + +struct FriendsEnumerateFollowingList_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 46 }; + EResult m_eResult; + CSteamID m_rgSteamID[ k_cEnumerateFollowersMax ]; + int32 m_nResultsReturned; + int32 m_nTotalResultCount; +}; + +//----------------------------------------------------------------------------- +// Purpose: reports the result of an attempt to change the user's persona name +//----------------------------------------------------------------------------- +struct SetPersonaNameResponse_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 47 }; + + bool m_bSuccess; // true if name change succeeded completely. + bool m_bLocalSuccess; // true if name change was retained locally. (We might not have been able to communicate with Steam) + EResult m_result; // detailed result code +}; + + +#pragma pack( pop ) + +#endif // ISTEAMFRIENDS_H diff --git a/dep/steam_api/isteamgamecoordinator.h b/dep/steam_api/isteamgamecoordinator.h new file mode 100644 index 0000000..5ab0637 --- /dev/null +++ b/dep/steam_api/isteamgamecoordinator.h @@ -0,0 +1,75 @@ +//====== Copyright ©, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to the game coordinator for this application +// +//============================================================================= + +#ifndef ISTEAMGAMECOORDINATOR +#define ISTEAMGAMECOORDINATOR +#ifdef _WIN32 +#pragma once +#endif + +#include "steamtypes.h" +#include "steamclientpublic.h" + + +// list of possible return values from the ISteamGameCoordinator API +enum EGCResults +{ + k_EGCResultOK = 0, + k_EGCResultNoMessage = 1, // There is no message in the queue + k_EGCResultBufferTooSmall = 2, // The buffer is too small for the requested message + k_EGCResultNotLoggedOn = 3, // The client is not logged onto Steam + k_EGCResultInvalidMessage = 4, // Something was wrong with the message being sent with SendMessage +}; + + +//----------------------------------------------------------------------------- +// Purpose: Functions for sending and receiving messages from the Game Coordinator +// for this application +//----------------------------------------------------------------------------- +class ISteamGameCoordinator +{ +public: + + // sends a message to the Game Coordinator + virtual EGCResults SendMessage( uint32 unMsgType, const void *pubData, uint32 cubData ) = 0; + + // returns true if there is a message waiting from the game coordinator + virtual bool IsMessageAvailable( uint32 *pcubMsgSize ) = 0; + + // fills the provided buffer with the first message in the queue and returns k_EGCResultOK or + // returns k_EGCResultNoMessage if there is no message waiting. pcubMsgSize is filled with the message size. + // If the provided buffer is not large enough to fit the entire message, k_EGCResultBufferTooSmall is returned + // and the message remains at the head of the queue. + virtual EGCResults RetrieveMessage( uint32 *punMsgType, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize ) = 0; + +}; +#define STEAMGAMECOORDINATOR_INTERFACE_VERSION "SteamGameCoordinator001" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +// callback notification - A new message is available for reading from the message queue +struct GCMessageAvailable_t +{ + enum { k_iCallback = k_iSteamGameCoordinatorCallbacks + 1 }; + uint32 m_nMessageSize; +}; + +// callback notification - A message failed to make it to the GC. It may be down temporarily +struct GCMessageFailed_t +{ + enum { k_iCallback = k_iSteamGameCoordinatorCallbacks + 2 }; +}; + +#pragma pack( pop ) + +#endif // ISTEAMGAMECOORDINATOR diff --git a/dep/steam_api/isteamgameserver.h b/dep/steam_api/isteamgameserver.h new file mode 100644 index 0000000..e19f1dd --- /dev/null +++ b/dep/steam_api/isteamgameserver.h @@ -0,0 +1,387 @@ +//====== 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" + +#define MASTERSERVERUPDATERPORT_USEGAMESOCKETSHARE ((uint16)-1) + +//----------------------------------------------------------------------------- +// Purpose: Functions for authenticating users via Steam to play on a game server +//----------------------------------------------------------------------------- +class ISteamGameServer +{ +public: + +// +// Basic server data. These properties, if set, must be set before before calling LogOn. They +// may not be changed after logged in. +// + + /// This is called by SteamGameServer_Init, and you will usually not need to call it directly + virtual bool InitGameServer( uint32 unIP, uint16 usGamePort, uint16 usQueryPort, uint32 unFlags, AppId_t nGameAppId, const char *pchVersionString ) = 0; + + /// Game product identifier. This is currently used by the master server for version checking purposes. + /// It's a required field, but will eventually will go away, and the AppID will be used for this purpose. + virtual void SetProduct( const char *pszProduct ) = 0; + + /// Description of the game. This is a required field and is displayed in the steam server browser....for now. + /// This is a required field, but it will go away eventually, as the data should be determined from the AppID. + virtual void SetGameDescription( const char *pszGameDescription ) = 0; + + /// If your game is a "mod," pass the string that identifies it. The default is an empty string, meaning + /// this application is the original game, not a mod. + /// + /// @see k_cbMaxGameServerGameDir + virtual void SetModDir( const char *pszModDir ) = 0; + + /// Is this is a dedicated server? The default value is false. + virtual void SetDedicatedServer( bool bDedicated ) = 0; + +// +// Login +// + + /// Begin process to login to a persistent game server account + /// + /// You need to register for callbacks to determine the result of this operation. + /// @see SteamServersConnected_t + /// @see SteamServerConnectFailure_t + /// @see SteamServersDisconnected_t + virtual void LogOn( const char *pszToken ) = 0; + + /// Login to a generic, anonymous account. + /// + /// Note: in previous versions of the SDK, this was automatically called within SteamGameServer_Init, + /// but this is no longer the case. + virtual void LogOnAnonymous() = 0; + + /// Begin process of logging game server out of steam + virtual void LogOff() = 0; + + // status functions + virtual bool BLoggedOn() = 0; + virtual bool BSecure() = 0; + virtual CSteamID GetSteamID() = 0; + + /// Returns true if the master server has requested a restart. + /// Only returns true once per request. + virtual bool WasRestartRequested() = 0; + +// +// Server state. These properties may be changed at any time. +// + + /// Max player count that will be reported to server browser and client queries + virtual void SetMaxPlayerCount( int cPlayersMax ) = 0; + + /// Number of bots. Default value is zero + virtual void SetBotPlayerCount( int cBotplayers ) = 0; + + /// Set the name of server as it will appear in the server browser + /// + /// @see k_cbMaxGameServerName + virtual void SetServerName( const char *pszServerName ) = 0; + + /// Set name of map to report in the server browser + /// + /// @see k_cbMaxGameServerName + virtual void SetMapName( const char *pszMapName ) = 0; + + /// Let people know if your server will require a password + virtual void SetPasswordProtected( bool bPasswordProtected ) = 0; + + /// Spectator server. The default value is zero, meaning the service + /// is not used. + virtual void SetSpectatorPort( uint16 unSpectatorPort ) = 0; + + /// Name of the spectator server. (Only used if spectator port is nonzero.) + /// + /// @see k_cbMaxGameServerMapName + virtual void SetSpectatorServerName( const char *pszSpectatorServerName ) = 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; + + /// 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 + /// + /// @see k_cbMaxGameServerTags + virtual void SetGameTags( const char *pchGameTags ) = 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) + /// + /// @see k_cbMaxGameServerGameData + virtual void SetGameData( const char *pchGameData ) = 0; + + /// Region identifier. This is an optional field, the default value is empty, meaning the "world" region + virtual void SetRegion( const char *pszRegion ) = 0; + +// +// Player list management / authentication +// + + // 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; + + // New auth system APIs - do not mix with the old auth system APIs. + // ---------------------------------------------------------------- + + // Retrieve ticket to be sent to the entity who wishes to authenticate you ( using BeginAuthSession API ). + // pcbTicket retrieves the length of the actual ticket. + virtual HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) = 0; + + // Authenticate ticket ( from GetAuthSessionTicket ) 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 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; + + // 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; + + + // these two functions s are deprecated, and will not return results + // they will be removed in a future version of the SDK + virtual void GetGameplayStats( ) = 0; + CALL_RESULT( GSReputation_t ) + virtual SteamAPICall_t GetServerReputation() = 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; + +// These are in GameSocketShare mode, where instead of ISteamGameServer 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; + +// +// Control heartbeats / advertisement with master server +// + + // 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 EnableHeartbeats( 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; + + // Force a heartbeat to steam at the next opportunity + virtual void ForceHeartbeat() = 0; + + // associate this game server with this clan for the purposes of computing player compat + CALL_RESULT( AssociateWithClanResult_t ) + virtual SteamAPICall_t AssociateWithClan( CSteamID steamIDClan ) = 0; + + // ask if any of the current players dont want to play with this new player - or vice versa + CALL_RESULT( ComputeNewPlayerCompatibilityResult_t ) + virtual SteamAPICall_t ComputeNewPlayerCompatibility( CSteamID steamIDNewPlayer ) = 0; + +}; + +#define STEAMGAMESERVER_INTERFACE_VERSION "SteamGameServer012" + +// 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 +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + + +// client has been approved to connect to this game server +struct GSClientApprove_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 1 }; + CSteamID m_SteamID; // SteamID of approved player + CSteamID m_OwnerSteamID; // SteamID of original owner for game license +}; + + +// 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) +}; + +// Sent as a reply to AssociateWithClan() +struct AssociateWithClanResult_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 10 }; + EResult m_eResult; // Result of the call; +}; + +// Sent as a reply to ComputeNewPlayerCompatibility() +struct ComputeNewPlayerCompatibilityResult_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 11 }; + EResult m_eResult; // Result of the call; + int m_cPlayersThatDontLikeCandidate; + int m_cPlayersThatCandidateDoesntLike; + int m_cClanPlayersThatDontLikeCandidate; + CSteamID m_SteamIDCandidate; +}; + + +#pragma pack( pop ) + +#endif // ISTEAMGAMESERVER_H diff --git a/dep/steam_api/isteamgameserverstats.h b/dep/steam_api/isteamgameserverstats.h new file mode 100644 index 0000000..e7922c9 --- /dev/null +++ b/dep/steam_api/isteamgameserverstats.h @@ -0,0 +1,101 @@ +//====== 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 + CALL_RESULT( GSStatsReceived_t ) + 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. + CALL_RESULT( GSStatsStored_t ) + virtual SteamAPICall_t StoreUserStats( CSteamID steamIDUser ) = 0; +}; + +#define STEAMGAMESERVERSTATS_INTERFACE_VERSION "SteamGameServerStats001" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +//----------------------------------------------------------------------------- +// 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/dep/steam_api/isteamhtmlsurface.h b/dep/steam_api/isteamhtmlsurface.h new file mode 100644 index 0000000..ccfc6af --- /dev/null +++ b/dep/steam_api/isteamhtmlsurface.h @@ -0,0 +1,453 @@ +//====== Copyright 1996-2013, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to display html pages in a texture +// +//============================================================================= + +#ifndef ISTEAMHTMLSURFACE_H +#define ISTEAMHTMLSURFACE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +typedef uint32 HHTMLBrowser; +const uint32 INVALID_HTMLBROWSER = 0; + +//----------------------------------------------------------------------------- +// Purpose: Functions for displaying HTML pages and interacting with them +//----------------------------------------------------------------------------- +class ISteamHTMLSurface +{ +public: + virtual ~ISteamHTMLSurface() {} + + // Must call init and shutdown when starting/ending use of the interface + virtual bool Init() = 0; + virtual bool Shutdown() = 0; + + // Create a browser object for display of a html page, when creation is complete the call handle + // will return a HTML_BrowserReady_t callback for the HHTMLBrowser of your new browser. + // The user agent string is a substring to be added to the general user agent string so you can + // identify your client on web servers. + // The userCSS string lets you apply a CSS style sheet to every displayed page, leave null if + // you do not require this functionality. + // + // YOU MUST HAVE IMPLEMENTED HANDLERS FOR HTML_BrowserReady_t, HTML_StartRequest_t, + // HTML_JSAlert_t, HTML_JSConfirm_t, and HTML_FileOpenDialog_t! See the CALLBACKS + // section of this interface (AllowStartRequest, etc) for more details. If you do + // not implement these callback handlers, the browser may appear to hang instead of + // navigating to new pages or triggering javascript popups. + // + CALL_RESULT( HTML_BrowserReady_t ) + virtual SteamAPICall_t CreateBrowser( const char *pchUserAgent, const char *pchUserCSS ) = 0; + + // Call this when you are done with a html surface, this lets us free the resources being used by it + virtual void RemoveBrowser( HHTMLBrowser unBrowserHandle ) = 0; + + // Navigate to this URL, results in a HTML_StartRequest_t as the request commences + virtual void LoadURL( HHTMLBrowser unBrowserHandle, const char *pchURL, const char *pchPostData ) = 0; + + // Tells the surface the size in pixels to display the surface + virtual void SetSize( HHTMLBrowser unBrowserHandle, uint32 unWidth, uint32 unHeight ) = 0; + + // Stop the load of the current html page + virtual void StopLoad( HHTMLBrowser unBrowserHandle ) = 0; + // Reload (most likely from local cache) the current page + virtual void Reload( HHTMLBrowser unBrowserHandle ) = 0; + // navigate back in the page history + virtual void GoBack( HHTMLBrowser unBrowserHandle ) = 0; + // navigate forward in the page history + virtual void GoForward( HHTMLBrowser unBrowserHandle ) = 0; + + // add this header to any url requests from this browser + virtual void AddHeader( HHTMLBrowser unBrowserHandle, const char *pchKey, const char *pchValue ) = 0; + // run this javascript script in the currently loaded page + virtual void ExecuteJavascript( HHTMLBrowser unBrowserHandle, const char *pchScript ) = 0; + + enum EHTMLMouseButton + { + eHTMLMouseButton_Left = 0, + eHTMLMouseButton_Right = 1, + eHTMLMouseButton_Middle = 2, + }; + + // Mouse click and mouse movement commands + virtual void MouseUp( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton ) = 0; + virtual void MouseDown( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton ) = 0; + virtual void MouseDoubleClick( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton ) = 0; + // x and y are relative to the HTML bounds + virtual void MouseMove( HHTMLBrowser unBrowserHandle, int x, int y ) = 0; + // nDelta is pixels of scroll + virtual void MouseWheel( HHTMLBrowser unBrowserHandle, int32 nDelta ) = 0; + + enum EMouseCursor + { + dc_user = 0, + dc_none, + dc_arrow, + dc_ibeam, + dc_hourglass, + dc_waitarrow, + dc_crosshair, + dc_up, + dc_sizenw, + dc_sizese, + dc_sizene, + dc_sizesw, + dc_sizew, + dc_sizee, + dc_sizen, + dc_sizes, + dc_sizewe, + dc_sizens, + dc_sizeall, + dc_no, + dc_hand, + dc_blank, // don't show any custom cursor, just use your default + dc_middle_pan, + dc_north_pan, + dc_north_east_pan, + dc_east_pan, + dc_south_east_pan, + dc_south_pan, + dc_south_west_pan, + dc_west_pan, + dc_north_west_pan, + dc_alias, + dc_cell, + dc_colresize, + dc_copycur, + dc_verticaltext, + dc_rowresize, + dc_zoomin, + dc_zoomout, + dc_help, + dc_custom, + + dc_last, // custom cursors start from this value and up + }; + + enum EHTMLKeyModifiers + { + k_eHTMLKeyModifier_None = 0, + k_eHTMLKeyModifier_AltDown = 1 << 0, + k_eHTMLKeyModifier_CtrlDown = 1 << 1, + k_eHTMLKeyModifier_ShiftDown = 1 << 2, + }; + + // keyboard interactions, native keycode is the virtual key code value from your OS + virtual void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers ) = 0; + virtual void KeyUp( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers ) = 0; + // cUnicodeChar is the unicode character point for this keypress (and potentially multiple chars per press) + virtual void KeyChar( HHTMLBrowser unBrowserHandle, uint32 cUnicodeChar, EHTMLKeyModifiers eHTMLKeyModifiers ) = 0; + + // programmatically scroll this many pixels on the page + virtual void SetHorizontalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll ) = 0; + virtual void SetVerticalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll ) = 0; + + // tell the html control if it has key focus currently, controls showing the I-beam cursor in text controls amongst other things + virtual void SetKeyFocus( HHTMLBrowser unBrowserHandle, bool bHasKeyFocus ) = 0; + + // open the current pages html code in the local editor of choice, used for debugging + virtual void ViewSource( HHTMLBrowser unBrowserHandle ) = 0; + // copy the currently selected text on the html page to the local clipboard + virtual void CopyToClipboard( HHTMLBrowser unBrowserHandle ) = 0; + // paste from the local clipboard to the current html page + virtual void PasteFromClipboard( HHTMLBrowser unBrowserHandle ) = 0; + + // find this string in the browser, if bCurrentlyInFind is true then instead cycle to the next matching element + virtual void Find( HHTMLBrowser unBrowserHandle, const char *pchSearchStr, bool bCurrentlyInFind, bool bReverse ) = 0; + // cancel a currently running find + virtual void StopFind( HHTMLBrowser unBrowserHandle ) = 0; + + // return details about the link at position x,y on the current page + virtual void GetLinkAtPosition( HHTMLBrowser unBrowserHandle, int x, int y ) = 0; + + // set a webcookie for the hostname in question + virtual void SetCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath = "/", RTime32 nExpires = 0, bool bSecure = false, bool bHTTPOnly = false ) = 0; + + // Zoom the current page by flZoom ( from 0.0 to 2.0, so to zoom to 120% use 1.2 ), zooming around point X,Y in the page (use 0,0 if you don't care) + virtual void SetPageScaleFactor( HHTMLBrowser unBrowserHandle, float flZoom, int nPointX, int nPointY ) = 0; + + // Enable/disable low-resource background mode, where javascript and repaint timers are throttled, resources are + // more aggressively purged from memory, and audio/video elements are paused. When background mode is enabled, + // all HTML5 video and audio objects will execute ".pause()" and gain the property "._steam_background_paused = 1". + // When background mode is disabled, any video or audio objects with that property will resume with ".play()". + virtual void SetBackgroundMode( HHTMLBrowser unBrowserHandle, bool bBackgroundMode ) = 0; + + // CALLBACKS + // + // These set of functions are used as responses to callback requests + // + + // You MUST call this in response to a HTML_StartRequest_t callback + // Set bAllowed to true to allow this navigation, false to cancel it and stay + // on the current page. You can use this feature to limit the valid pages + // allowed in your HTML surface. + virtual void AllowStartRequest( HHTMLBrowser unBrowserHandle, bool bAllowed ) = 0; + + // You MUST call this in response to a HTML_JSAlert_t or HTML_JSConfirm_t callback + // Set bResult to true for the OK option of a confirm, use false otherwise + virtual void JSDialogResponse( HHTMLBrowser unBrowserHandle, bool bResult ) = 0; + + // You MUST call this in response to a HTML_FileOpenDialog_t callback + IGNOREATTR() + virtual void FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, const char **pchSelectedFiles ) = 0; +}; + +#define STEAMHTMLSURFACE_INTERFACE_VERSION "STEAMHTMLSURFACE_INTERFACE_VERSION_003" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + + +//----------------------------------------------------------------------------- +// Purpose: The browser is ready for use +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_BrowserReady_t, k_iSteamHTMLSurfaceCallbacks + 1 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // this browser is now fully created and ready to navigate to pages +END_DEFINE_CALLBACK_1() + + +//----------------------------------------------------------------------------- +// Purpose: the browser has a pending paint +//----------------------------------------------------------------------------- +DEFINE_CALLBACK(HTML_NeedsPaint_t, k_iSteamHTMLSurfaceCallbacks + 2) +CALLBACK_MEMBER(0, HHTMLBrowser, unBrowserHandle) // the browser that needs the paint +CALLBACK_MEMBER(1, const char *, pBGRA ) // a pointer to the B8G8R8A8 data for this surface, valid until SteamAPI_RunCallbacks is next called +CALLBACK_MEMBER(2, uint32, unWide) // the total width of the pBGRA texture +CALLBACK_MEMBER(3, uint32, unTall) // the total height of the pBGRA texture +CALLBACK_MEMBER(4, uint32, unUpdateX) // the offset in X for the damage rect for this update +CALLBACK_MEMBER(5, uint32, unUpdateY) // the offset in Y for the damage rect for this update +CALLBACK_MEMBER(6, uint32, unUpdateWide) // the width of the damage rect for this update +CALLBACK_MEMBER(7, uint32, unUpdateTall) // the height of the damage rect for this update +CALLBACK_MEMBER(8, uint32, unScrollX) // the page scroll the browser was at when this texture was rendered +CALLBACK_MEMBER(9, uint32, unScrollY) // the page scroll the browser was at when this texture was rendered +CALLBACK_MEMBER(10, float, flPageScale) // the page scale factor on this page when rendered +CALLBACK_MEMBER(11, uint32, unPageSerial) // incremented on each new page load, you can use this to reject draws while navigating to new pages +END_DEFINE_CALLBACK_12() + + +//----------------------------------------------------------------------------- +// Purpose: The browser wanted to navigate to a new page +// NOTE - you MUST call AllowStartRequest in response to this callback +//----------------------------------------------------------------------------- +DEFINE_CALLBACK(HTML_StartRequest_t, k_iSteamHTMLSurfaceCallbacks + 3) +CALLBACK_MEMBER(0, HHTMLBrowser, unBrowserHandle) // the handle of the surface navigating +CALLBACK_MEMBER(1, const char *, pchURL) // the url they wish to navigate to +CALLBACK_MEMBER(2, const char *, pchTarget) // the html link target type (i.e _blank, _self, _parent, _top ) +CALLBACK_MEMBER(3, const char *, pchPostData ) // any posted data for the request +CALLBACK_MEMBER(4, bool, bIsRedirect) // true if this was a http/html redirect from the last load request +END_DEFINE_CALLBACK_5() + + +//----------------------------------------------------------------------------- +// Purpose: The browser has been requested to close due to user interaction (usually from a javascript window.close() call) +//----------------------------------------------------------------------------- +DEFINE_CALLBACK(HTML_CloseBrowser_t, k_iSteamHTMLSurfaceCallbacks + 4) +CALLBACK_MEMBER(0, HHTMLBrowser, unBrowserHandle) // the handle of the surface +END_DEFINE_CALLBACK_1() + + +//----------------------------------------------------------------------------- +// Purpose: the browser is navigating to a new url +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_URLChanged_t, k_iSteamHTMLSurfaceCallbacks + 5 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface navigating +CALLBACK_MEMBER( 1, const char *, pchURL ) // the url they wish to navigate to +CALLBACK_MEMBER( 2, const char *, pchPostData ) // any posted data for the request +CALLBACK_MEMBER( 3, bool, bIsRedirect ) // true if this was a http/html redirect from the last load request +CALLBACK_MEMBER( 4, const char *, pchPageTitle ) // the title of the page +CALLBACK_MEMBER( 5, bool, bNewNavigation ) // true if this was from a fresh tab and not a click on an existing page +END_DEFINE_CALLBACK_6() + + +//----------------------------------------------------------------------------- +// Purpose: A page is finished loading +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_FinishedRequest_t, k_iSteamHTMLSurfaceCallbacks + 6 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchURL ) // +CALLBACK_MEMBER( 2, const char *, pchPageTitle ) // +END_DEFINE_CALLBACK_3() + + +//----------------------------------------------------------------------------- +// Purpose: a request to load this url in a new tab +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_OpenLinkInNewTab_t, k_iSteamHTMLSurfaceCallbacks + 7 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchURL ) // +END_DEFINE_CALLBACK_2() + + +//----------------------------------------------------------------------------- +// Purpose: the page has a new title now +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_ChangedTitle_t, k_iSteamHTMLSurfaceCallbacks + 8 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchTitle ) // +END_DEFINE_CALLBACK_2() + + +//----------------------------------------------------------------------------- +// Purpose: results from a search +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_SearchResults_t, k_iSteamHTMLSurfaceCallbacks + 9 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, uint32, unResults ) // +CALLBACK_MEMBER( 2, uint32, unCurrentMatch ) // +END_DEFINE_CALLBACK_3() + + +//----------------------------------------------------------------------------- +// Purpose: page history status changed on the ability to go backwards and forward +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_CanGoBackAndForward_t, k_iSteamHTMLSurfaceCallbacks + 10 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, bool, bCanGoBack ) // +CALLBACK_MEMBER( 2, bool, bCanGoForward ) // +END_DEFINE_CALLBACK_3() + + +//----------------------------------------------------------------------------- +// Purpose: details on the visibility and size of the horizontal scrollbar +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_HorizontalScroll_t, k_iSteamHTMLSurfaceCallbacks + 11 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, uint32, unScrollMax ) // +CALLBACK_MEMBER( 2, uint32, unScrollCurrent ) // +CALLBACK_MEMBER( 3, float, flPageScale ) // +CALLBACK_MEMBER( 4, bool , bVisible ) // +CALLBACK_MEMBER( 5, uint32, unPageSize ) // +END_DEFINE_CALLBACK_6() + + +//----------------------------------------------------------------------------- +// Purpose: details on the visibility and size of the vertical scrollbar +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_VerticalScroll_t, k_iSteamHTMLSurfaceCallbacks + 12 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, uint32, unScrollMax ) // +CALLBACK_MEMBER( 2, uint32, unScrollCurrent ) // +CALLBACK_MEMBER( 3, float, flPageScale ) // +CALLBACK_MEMBER( 4, bool, bVisible ) // +CALLBACK_MEMBER( 5, uint32, unPageSize ) // +END_DEFINE_CALLBACK_6() + + +//----------------------------------------------------------------------------- +// Purpose: response to GetLinkAtPosition call +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_LinkAtPosition_t, k_iSteamHTMLSurfaceCallbacks + 13 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, uint32, x ) // NOTE - Not currently set +CALLBACK_MEMBER( 2, uint32, y ) // NOTE - Not currently set +CALLBACK_MEMBER( 3, const char *, pchURL ) // +CALLBACK_MEMBER( 4, bool, bInput ) // +CALLBACK_MEMBER( 5, bool, bLiveLink ) // +END_DEFINE_CALLBACK_6() + + + +//----------------------------------------------------------------------------- +// Purpose: show a Javascript alert dialog, call JSDialogResponse +// when the user dismisses this dialog (or right away to ignore it) +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_JSAlert_t, k_iSteamHTMLSurfaceCallbacks + 14 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchMessage ) // +END_DEFINE_CALLBACK_2() + + +//----------------------------------------------------------------------------- +// Purpose: show a Javascript confirmation dialog, call JSDialogResponse +// when the user dismisses this dialog (or right away to ignore it) +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_JSConfirm_t, k_iSteamHTMLSurfaceCallbacks + 15 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchMessage ) // +END_DEFINE_CALLBACK_2() + + +//----------------------------------------------------------------------------- +// Purpose: when received show a file open dialog +// then call FileLoadDialogResponse with the file(s) the user selected. +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_FileOpenDialog_t, k_iSteamHTMLSurfaceCallbacks + 16 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchTitle ) // +CALLBACK_MEMBER( 2, const char *, pchInitialFile ) // +END_DEFINE_CALLBACK_3() + + +//----------------------------------------------------------------------------- +// Purpose: a new html window has been created +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_NewWindow_t, k_iSteamHTMLSurfaceCallbacks + 21 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the current surface +CALLBACK_MEMBER( 1, const char *, pchURL ) // the page to load +CALLBACK_MEMBER( 2, uint32, unX ) // the x pos into the page to display the popup +CALLBACK_MEMBER( 3, uint32, unY ) // the y pos into the page to display the popup +CALLBACK_MEMBER( 4, uint32, unWide ) // the total width of the pBGRA texture +CALLBACK_MEMBER( 5, uint32, unTall ) // the total height of the pBGRA texture +CALLBACK_MEMBER( 6, HHTMLBrowser, unNewWindow_BrowserHandle ) // the handle of the new window surface +END_DEFINE_CALLBACK_7() + + +//----------------------------------------------------------------------------- +// Purpose: change the cursor to display +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_SetCursor_t, k_iSteamHTMLSurfaceCallbacks + 22 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, uint32, eMouseCursor ) // the EMouseCursor to display +END_DEFINE_CALLBACK_2() + + +//----------------------------------------------------------------------------- +// Purpose: informational message from the browser +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_StatusText_t, k_iSteamHTMLSurfaceCallbacks + 23 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchMsg ) // the EMouseCursor to display +END_DEFINE_CALLBACK_2() + + +//----------------------------------------------------------------------------- +// Purpose: show a tooltip +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_ShowToolTip_t, k_iSteamHTMLSurfaceCallbacks + 24 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchMsg ) // the EMouseCursor to display +END_DEFINE_CALLBACK_2() + + +//----------------------------------------------------------------------------- +// Purpose: update the text of an existing tooltip +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_UpdateToolTip_t, k_iSteamHTMLSurfaceCallbacks + 25 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +CALLBACK_MEMBER( 1, const char *, pchMsg ) // the EMouseCursor to display +END_DEFINE_CALLBACK_2() + + +//----------------------------------------------------------------------------- +// Purpose: hide the tooltip you are showing +//----------------------------------------------------------------------------- +DEFINE_CALLBACK( HTML_HideToolTip_t, k_iSteamHTMLSurfaceCallbacks + 26 ) +CALLBACK_MEMBER( 0, HHTMLBrowser, unBrowserHandle ) // the handle of the surface +END_DEFINE_CALLBACK_1() + + +#pragma pack( pop ) + + +#endif // ISTEAMHTMLSURFACE_H diff --git a/dep/steam_api/isteamhttp.h b/dep/steam_api/isteamhttp.h new file mode 100644 index 0000000..8fab537 --- /dev/null +++ b/dep/steam_api/isteamhttp.h @@ -0,0 +1,210 @@ +//====== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to http client +// +//============================================================================= + +#ifndef ISTEAMHTTP_H +#define ISTEAMHTTP_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" +#include "steamhttpenums.h" + +// Handle to a HTTP Request handle +typedef uint32 HTTPRequestHandle; +#define INVALID_HTTPREQUEST_HANDLE 0 + +typedef uint32 HTTPCookieContainerHandle; +#define INVALID_HTTPCOOKIE_HANDLE 0 + +//----------------------------------------------------------------------------- +// Purpose: interface to http client +//----------------------------------------------------------------------------- +class ISteamHTTP +{ +public: + + // Initializes a new HTTP request, returning a handle to use in further operations on it. Requires + // the method (GET or POST) and the absolute URL for the request. Both http and https are supported, + // so this string must start with http:// or https:// and should look like http://store.steampowered.com/app/250/ + // or such. + virtual HTTPRequestHandle CreateHTTPRequest( EHTTPMethod eHTTPRequestMethod, const char *pchAbsoluteURL ) = 0; + + // Set a context value for the request, which will be returned in the HTTPRequestCompleted_t callback after + // sending the request. This is just so the caller can easily keep track of which callbacks go with which request data. + virtual bool SetHTTPRequestContextValue( HTTPRequestHandle hRequest, uint64 ulContextValue ) = 0; + + // Set a timeout in seconds for the HTTP request, must be called prior to sending the request. Default + // timeout is 60 seconds if you don't call this. Returns false if the handle is invalid, or the request + // has already been sent. + virtual bool SetHTTPRequestNetworkActivityTimeout( HTTPRequestHandle hRequest, uint32 unTimeoutSeconds ) = 0; + + // Set a request header value for the request, must be called prior to sending the request. Will + // return false if the handle is invalid or the request is already sent. + virtual bool SetHTTPRequestHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, const char *pchHeaderValue ) = 0; + + // Set a GET or POST parameter value on the request, which is set will depend on the EHTTPMethod specified + // when creating the request. Must be called prior to sending the request. Will return false if the + // handle is invalid or the request is already sent. + virtual bool SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, const char *pchParamName, const char *pchParamValue ) = 0; + + // Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on + // asynchronous response via callback. + // + // Note: If the user is in offline mode in Steam, then this will add a only-if-cached cache-control + // header and only do a local cache lookup rather than sending any actual remote request. + virtual bool SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle ) = 0; + + // Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on + // asynchronous response via callback for completion, and listen for HTTPRequestHeadersReceived_t and + // HTTPRequestDataReceived_t callbacks while streaming. + virtual bool SendHTTPRequestAndStreamResponse( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle ) = 0; + + // Defers a request you have sent, the actual HTTP client code may have many requests queued, and this will move + // the specified request to the tail of the queue. Returns false on invalid handle, or if the request is not yet sent. + virtual bool DeferHTTPRequest( HTTPRequestHandle hRequest ) = 0; + + // Prioritizes a request you have sent, the actual HTTP client code may have many requests queued, and this will move + // the specified request to the head of the queue. Returns false on invalid handle, or if the request is not yet sent. + virtual bool PrioritizeHTTPRequest( HTTPRequestHandle hRequest ) = 0; + + // Checks if a response header is present in a HTTP response given a handle from HTTPRequestCompleted_t, also + // returns the size of the header value if present so the caller and allocate a correctly sized buffer for + // GetHTTPResponseHeaderValue. + virtual bool GetHTTPResponseHeaderSize( HTTPRequestHandle hRequest, const char *pchHeaderName, uint32 *unResponseHeaderSize ) = 0; + + // Gets header values from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the + // header is not present or if your buffer is too small to contain it's value. You should first call + // BGetHTTPResponseHeaderSize to check for the presence of the header and to find out the size buffer needed. + virtual bool GetHTTPResponseHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, uint8 *pHeaderValueBuffer, uint32 unBufferSize ) = 0; + + // Gets the size of the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the + // handle is invalid. + virtual bool GetHTTPResponseBodySize( HTTPRequestHandle hRequest, uint32 *unBodySize ) = 0; + + // Gets the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the + // handle is invalid or is to a streaming response, or if the provided buffer is not the correct size. Use BGetHTTPResponseBodySize first to find out + // the correct buffer size to use. + virtual bool GetHTTPResponseBodyData( HTTPRequestHandle hRequest, uint8 *pBodyDataBuffer, uint32 unBufferSize ) = 0; + + // Gets the body data from a streaming HTTP response given a handle from HTTPRequestDataReceived_t. Will return false if the + // handle is invalid or is to a non-streaming response (meaning it wasn't sent with SendHTTPRequestAndStreamResponse), or if the buffer size and offset + // do not match the size and offset sent in HTTPRequestDataReceived_t. + virtual bool GetHTTPStreamingResponseBodyData( HTTPRequestHandle hRequest, uint32 cOffset, uint8 *pBodyDataBuffer, uint32 unBufferSize ) = 0; + + // Releases an HTTP response handle, should always be called to free resources after receiving a HTTPRequestCompleted_t + // callback and finishing using the response. + virtual bool ReleaseHTTPRequest( HTTPRequestHandle hRequest ) = 0; + + // Gets progress on downloading the body for the request. This will be zero unless a response header has already been + // received which included a content-length field. For responses that contain no content-length it will report + // zero for the duration of the request as the size is unknown until the connection closes. + virtual bool GetHTTPDownloadProgressPct( HTTPRequestHandle hRequest, float *pflPercentOut ) = 0; + + // Sets the body for an HTTP Post request. Will fail and return false on a GET request, and will fail if POST params + // have already been set for the request. Setting this raw body makes it the only contents for the post, the pchContentType + // parameter will set the content-type header for the request so the server may know how to interpret the body. + virtual bool SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, const char *pchContentType, uint8 *pubBody, uint32 unBodyLen ) = 0; + + // Creates a cookie container handle which you must later free with ReleaseCookieContainer(). If bAllowResponsesToModify=true + // than any response to your requests using this cookie container may add new cookies which may be transmitted with + // future requests. If bAllowResponsesToModify=false than only cookies you explicitly set will be sent. This API is just for + // during process lifetime, after steam restarts no cookies are persisted and you have no way to access the cookie container across + // repeat executions of your process. + virtual HTTPCookieContainerHandle CreateCookieContainer( bool bAllowResponsesToModify ) = 0; + + // Release a cookie container you are finished using, freeing it's memory + virtual bool ReleaseCookieContainer( HTTPCookieContainerHandle hCookieContainer ) = 0; + + // Adds a cookie to the specified cookie container that will be used with future requests. + virtual bool SetCookie( HTTPCookieContainerHandle hCookieContainer, const char *pchHost, const char *pchUrl, const char *pchCookie ) = 0; + + // Set the cookie container to use for a HTTP request + virtual bool SetHTTPRequestCookieContainer( HTTPRequestHandle hRequest, HTTPCookieContainerHandle hCookieContainer ) = 0; + + // Set the extra user agent info for a request, this doesn't clobber the normal user agent, it just adds the extra info on the end + virtual bool SetHTTPRequestUserAgentInfo( HTTPRequestHandle hRequest, const char *pchUserAgentInfo ) = 0; + + // Set that https request should require verified SSL certificate via machines certificate trust store + virtual bool SetHTTPRequestRequiresVerifiedCertificate( HTTPRequestHandle hRequest, bool bRequireVerifiedCertificate ) = 0; + + // Set an absolute timeout on the HTTP request, this is just a total time timeout different than the network activity timeout + // which can bump everytime we get more data + virtual bool SetHTTPRequestAbsoluteTimeoutMS( HTTPRequestHandle hRequest, uint32 unMilliseconds ) = 0; + + // Check if the reason the request failed was because we timed it out (rather than some harder failure) + virtual bool GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut ) = 0; +}; + +#define STEAMHTTP_INTERFACE_VERSION "STEAMHTTP_INTERFACE_VERSION002" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +struct HTTPRequestCompleted_t +{ + enum { k_iCallback = k_iClientHTTPCallbacks + 1 }; + + // Handle value for the request that has completed. + HTTPRequestHandle m_hRequest; + + // Context value that the user defined on the request that this callback is associated with, 0 if + // no context value was set. + uint64 m_ulContextValue; + + // This will be true if we actually got any sort of response from the server (even an error). + // It will be false if we failed due to an internal error or client side network failure. + bool m_bRequestSuccessful; + + // Will be the HTTP status code value returned by the server, k_EHTTPStatusCode200OK is the normal + // OK response, if you get something else you probably need to treat it as a failure. + EHTTPStatusCode m_eStatusCode; + + uint32 m_unBodySize; // Same as GetHTTPResponseBodySize() +}; + + +struct HTTPRequestHeadersReceived_t +{ + enum { k_iCallback = k_iClientHTTPCallbacks + 2 }; + + // Handle value for the request that has received headers. + HTTPRequestHandle m_hRequest; + + // Context value that the user defined on the request that this callback is associated with, 0 if + // no context value was set. + uint64 m_ulContextValue; +}; + +struct HTTPRequestDataReceived_t +{ + enum { k_iCallback = k_iClientHTTPCallbacks + 3 }; + + // Handle value for the request that has received data. + HTTPRequestHandle m_hRequest; + + // Context value that the user defined on the request that this callback is associated with, 0 if + // no context value was set. + uint64 m_ulContextValue; + + + // Offset to provide to GetHTTPStreamingResponseBodyData to get this chunk of data + uint32 m_cOffset; + + // Size to provide to GetHTTPStreamingResponseBodyData to get this chunk of data + uint32 m_cBytesReceived; +}; + + +#pragma pack( pop ) + +#endif // ISTEAMHTTP_H \ No newline at end of file diff --git a/dep/steam_api/isteaminventory.h b/dep/steam_api/isteaminventory.h new file mode 100644 index 0000000..692c62d --- /dev/null +++ b/dep/steam_api/isteaminventory.h @@ -0,0 +1,382 @@ +//====== Copyright © 1996-2014 Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to Steam Inventory +// +//============================================================================= + +#ifndef ISTEAMINVENTORY_H +#define ISTEAMINVENTORY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + + +// Every individual instance of an item has a globally-unique ItemInstanceID. +// This ID is unique to the combination of (player, specific item instance) +// and will not be transferred to another player or re-used for another item. +typedef uint64 SteamItemInstanceID_t; + +static const SteamItemInstanceID_t k_SteamItemInstanceIDInvalid = (SteamItemInstanceID_t)~0; + +// Types of items in your game are identified by a 32-bit "item definition number". +// Valid definition numbers are between 1 and 999999999; numbers less than or equal to +// zero are invalid, and numbers greater than or equal to one billion (1x10^9) are +// reserved for internal Steam use. +typedef int32 SteamItemDef_t; + + +enum ESteamItemFlags +{ + // Item status flags - these flags are permanently attached to specific item instances + k_ESteamItemNoTrade = 1 << 0, // This item is account-locked and cannot be traded or given away. + + // Action confirmation flags - these flags are set one time only, as part of a result set + k_ESteamItemRemoved = 1 << 8, // The item has been destroyed, traded away, expired, or otherwise invalidated + k_ESteamItemConsumed = 1 << 9, // The item quantity has been decreased by 1 via ConsumeItem API. + + // All other flag bits are currently reserved for internal Steam use at this time. + // Do not assume anything about the state of other flags which are not defined here. +}; + +struct SteamItemDetails_t +{ + SteamItemInstanceID_t m_itemId; + SteamItemDef_t m_iDefinition; + uint16 m_unQuantity; + uint16 m_unFlags; // see ESteamItemFlags +}; + +typedef int32 SteamInventoryResult_t; + +static const SteamInventoryResult_t k_SteamInventoryResultInvalid = -1; + + +//----------------------------------------------------------------------------- +// Purpose: Steam Inventory query and manipulation API +//----------------------------------------------------------------------------- +class ISteamInventory +{ +public: + + // INVENTORY ASYNC RESULT MANAGEMENT + // + // Asynchronous inventory queries always output a result handle which can be used with + // GetResultStatus, GetResultItems, etc. A SteamInventoryResultReady_t callback will + // be triggered when the asynchronous result becomes ready (or fails). + // + + // Find out the status of an asynchronous inventory result handle. Possible values: + // k_EResultPending - still in progress + // k_EResultOK - done, result ready + // k_EResultExpired - done, result ready, maybe out of date (see DeserializeResult) + // k_EResultInvalidParam - ERROR: invalid API call parameters + // k_EResultServiceUnavailable - ERROR: service temporarily down, you may retry later + // k_EResultLimitExceeded - ERROR: operation would exceed per-user inventory limits + // k_EResultFail - ERROR: unknown / generic error + METHOD_DESC(Find out the status of an asynchronous inventory result handle.) + virtual EResult GetResultStatus( SteamInventoryResult_t resultHandle ) = 0; + + // Copies the contents of a result set into a flat array. The specific + // contents of the result set depend on which query which was used. + METHOD_DESC(Copies the contents of a result set into a flat array. The specific contents of the result set depend on which query which was used.) + virtual bool GetResultItems( SteamInventoryResult_t resultHandle, + OUT_ARRAY_COUNT( punOutItemsArraySize,Output array) SteamItemDetails_t *pOutItemsArray, + uint32 *punOutItemsArraySize ) = 0; + + // In combination with GetResultItems, you can use GetResultItemProperty to retrieve + // dynamic string properties for a given item returned in the result set. + // + // Property names are always composed of ASCII letters, numbers, and/or underscores. + // + // Pass a NULL pointer for pchPropertyName to get a comma - separated list of available + // property names. + // + // If pchValueBuffer is NULL, *punValueBufferSize will contain the + // suggested buffer size. Otherwise it will be the number of bytes actually copied + // to pchValueBuffer. If the results do not fit in the given buffer, partial + // results may be copied. + virtual bool GetResultItemProperty( SteamInventoryResult_t resultHandle, + uint32 unItemIndex, + const char *pchPropertyName, + OUT_STRING_COUNT( punValueBufferSizeOut ) char *pchValueBuffer, uint32 *punValueBufferSizeOut ) = 0; + + // Returns the server time at which the result was generated. Compare against + // the value of IClientUtils::GetServerRealTime() to determine age. + METHOD_DESC(Returns the server time at which the result was generated. Compare against the value of IClientUtils::GetServerRealTime() to determine age.) + virtual uint32 GetResultTimestamp( SteamInventoryResult_t resultHandle ) = 0; + + // Returns true if the result belongs to the target steam ID, false if the + // result does not. This is important when using DeserializeResult, to verify + // that a remote player is not pretending to have a different user's inventory. + METHOD_DESC(Returns true if the result belongs to the target steam ID or false if the result does not. This is important when using DeserializeResult to verify that a remote player is not pretending to have a different users inventory.) + virtual bool CheckResultSteamID( SteamInventoryResult_t resultHandle, CSteamID steamIDExpected ) = 0; + + // Destroys a result handle and frees all associated memory. + METHOD_DESC(Destroys a result handle and frees all associated memory.) + virtual void DestroyResult( SteamInventoryResult_t resultHandle ) = 0; + + + // INVENTORY ASYNC QUERY + // + + // Captures the entire state of the current user's Steam inventory. + // You must call DestroyResult on this handle when you are done with it. + // Returns false and sets *pResultHandle to zero if inventory is unavailable. + // Note: calls to this function are subject to rate limits and may return + // cached results if called too frequently. It is suggested that you call + // this function only when you are about to display the user's full inventory, + // or if you expect that the inventory may have changed. + METHOD_DESC(Captures the entire state of the current users Steam inventory.) + virtual bool GetAllItems( SteamInventoryResult_t *pResultHandle ) = 0; + + + // Captures the state of a subset of the current user's Steam inventory, + // identified by an array of item instance IDs. The results from this call + // can be serialized and passed to other players to "prove" that the current + // user owns specific items, without exposing the user's entire inventory. + // For example, you could call GetItemsByID with the IDs of the user's + // currently equipped cosmetic items and serialize this to a buffer, and + // then transmit this buffer to other players upon joining a game. + METHOD_DESC(Captures the state of a subset of the current users Steam inventory identified by an array of item instance IDs.) + virtual bool GetItemsByID( SteamInventoryResult_t *pResultHandle, ARRAY_COUNT( unCountInstanceIDs ) const SteamItemInstanceID_t *pInstanceIDs, uint32 unCountInstanceIDs ) = 0; + + + // RESULT SERIALIZATION AND AUTHENTICATION + // + // Serialized result sets contain a short signature which can't be forged + // or replayed across different game sessions. A result set can be serialized + // on the local client, transmitted to other players via your game networking, + // and deserialized by the remote players. This is a secure way of preventing + // hackers from lying about posessing rare/high-value items. + + // Serializes a result set with signature bytes to an output buffer. Pass + // NULL as an output buffer to get the required size via punOutBufferSize. + // The size of a serialized result depends on the number items which are being + // serialized. When securely transmitting items to other players, it is + // recommended to use "GetItemsByID" first to create a minimal result set. + // Results have a built-in timestamp which will be considered "expired" after + // an hour has elapsed. See DeserializeResult for expiration handling. + virtual bool SerializeResult( SteamInventoryResult_t resultHandle, OUT_BUFFER_COUNT(punOutBufferSize) void *pOutBuffer, uint32 *punOutBufferSize ) = 0; + + // Deserializes a result set and verifies the signature bytes. Returns false + // if bRequireFullOnlineVerify is set but Steam is running in Offline mode. + // Otherwise returns true and then delivers error codes via GetResultStatus. + // + // The bRESERVED_MUST_BE_FALSE flag is reserved for future use and should not + // be set to true by your game at this time. + // + // DeserializeResult has a potential soft-failure mode where the handle status + // is set to k_EResultExpired. GetResultItems() still succeeds in this mode. + // The "expired" result could indicate that the data may be out of date - not + // just due to timed expiration (one hour), but also because one of the items + // in the result set may have been traded or consumed since the result set was + // generated. You could compare the timestamp from GetResultTimestamp() to + // ISteamUtils::GetServerRealTime() to determine how old the data is. You could + // simply ignore the "expired" result code and continue as normal, or you + // could challenge the player with expired data to send an updated result set. + virtual bool DeserializeResult( SteamInventoryResult_t *pOutResultHandle, BUFFER_COUNT(punOutBufferSize) const void *pBuffer, uint32 unBufferSize, bool bRESERVED_MUST_BE_FALSE = false ) = 0; + + + // INVENTORY ASYNC MODIFICATION + // + + // GenerateItems() creates one or more items and then generates a SteamInventoryCallback_t + // notification with a matching nCallbackContext parameter. This API is only intended + // for prototyping - it is only usable by Steam accounts that belong to the publisher group + // for your game. + // If punArrayQuantity is not NULL, it should be the same length as pArrayItems and should + // describe the quantity of each item to generate. + virtual bool GenerateItems( SteamInventoryResult_t *pResultHandle, ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength ) = 0; + + // GrantPromoItems() checks the list of promotional items for which the user may be eligible + // and grants the items (one time only). On success, the result set will include items which + // were granted, if any. If no items were granted because the user isn't eligible for any + // promotions, this is still considered a success. + METHOD_DESC(GrantPromoItems() checks the list of promotional items for which the user may be eligible and grants the items (one time only).) + virtual bool GrantPromoItems( SteamInventoryResult_t *pResultHandle ) = 0; + + // AddPromoItem() / AddPromoItems() are restricted versions of GrantPromoItems(). Instead of + // scanning for all eligible promotional items, the check is restricted to a single item + // definition or set of item definitions. This can be useful if your game has custom UI for + // showing a specific promo item to the user. + virtual bool AddPromoItem( SteamInventoryResult_t *pResultHandle, SteamItemDef_t itemDef ) = 0; + virtual bool AddPromoItems( SteamInventoryResult_t *pResultHandle, ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, uint32 unArrayLength ) = 0; + + // ConsumeItem() removes items from the inventory, permanently. They cannot be recovered. + // Not for the faint of heart - if your game implements item removal at all, a high-friction + // UI confirmation process is highly recommended. + METHOD_DESC(ConsumeItem() removes items from the inventory permanently.) + virtual bool ConsumeItem( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemConsume, uint32 unQuantity ) = 0; + + // ExchangeItems() is an atomic combination of item generation and consumption. + // It can be used to implement crafting recipes or transmutations, or items which unpack + // themselves into other items (e.g., a chest). + // Exchange recipes are defined in the ItemDef, and explicitly list the required item + // types and resulting generated type. + // Exchange recipes are evaluated atomically by the Inventory Service; if the supplied + // components do not match the recipe, or do not contain sufficient quantity, the + // exchange will fail. + virtual bool ExchangeItems( SteamInventoryResult_t *pResultHandle, + ARRAY_COUNT(unArrayGenerateLength) const SteamItemDef_t *pArrayGenerate, ARRAY_COUNT(unArrayGenerateLength) const uint32 *punArrayGenerateQuantity, uint32 unArrayGenerateLength, + ARRAY_COUNT(unArrayDestroyLength) const SteamItemInstanceID_t *pArrayDestroy, ARRAY_COUNT(unArrayDestroyLength) const uint32 *punArrayDestroyQuantity, uint32 unArrayDestroyLength ) = 0; + + + // TransferItemQuantity() is intended for use with items which are "stackable" (can have + // quantity greater than one). It can be used to split a stack into two, or to transfer + // quantity from one stack into another stack of identical items. To split one stack into + // two, pass k_SteamItemInstanceIDInvalid for itemIdDest and a new item will be generated. + virtual bool TransferItemQuantity( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemIdSource, uint32 unQuantity, SteamItemInstanceID_t itemIdDest ) = 0; + + + // TIMED DROPS AND PLAYTIME CREDIT + // + + // Deprecated. Calling this method is not required for proper playtime accounting. + METHOD_DESC( Deprecated method. Playtime accounting is performed on the Steam servers. ) + virtual void SendItemDropHeartbeat() = 0; + + // Playtime credit must be consumed and turned into item drops by your game. Only item + // definitions which are marked as "playtime item generators" can be spawned. The call + // will return an empty result set if there is not enough playtime credit for a drop. + // Your game should call TriggerItemDrop at an appropriate time for the user to receive + // new items, such as between rounds or while the player is dead. Note that players who + // hack their clients could modify the value of "dropListDefinition", so do not use it + // to directly control rarity. + // See your Steamworks configuration to set playtime drop rates for individual itemdefs. + // The client library will suppress too-frequent calls to this method. + METHOD_DESC(Playtime credit must be consumed and turned into item drops by your game.) + virtual bool TriggerItemDrop( SteamInventoryResult_t *pResultHandle, SteamItemDef_t dropListDefinition ) = 0; + + + // IN-GAME TRADING + // + // TradeItems() implements limited in-game trading of items, if you prefer not to use + // the overlay or an in-game web browser to perform Steam Trading through the website. + // You should implement a UI where both players can see and agree to a trade, and then + // each client should call TradeItems simultaneously (+/- 5 seconds) with matching + // (but reversed) parameters. The result is the same as if both players performed a + // Steam Trading transaction through the web. Each player will get an inventory result + // confirming the removal or quantity changes of the items given away, and the new + // item instance id numbers and quantities of the received items. + // (Note: new item instance IDs are generated whenever an item changes ownership.) + virtual bool TradeItems( SteamInventoryResult_t *pResultHandle, CSteamID steamIDTradePartner, + ARRAY_COUNT(nArrayGiveLength) const SteamItemInstanceID_t *pArrayGive, ARRAY_COUNT(nArrayGiveLength) const uint32 *pArrayGiveQuantity, uint32 nArrayGiveLength, + ARRAY_COUNT(nArrayGetLength) const SteamItemInstanceID_t *pArrayGet, ARRAY_COUNT(nArrayGetLength) const uint32 *pArrayGetQuantity, uint32 nArrayGetLength ) = 0; + + + // ITEM DEFINITIONS + // + // Item definitions are a mapping of "definition IDs" (integers between 1 and 1000000) + // to a set of string properties. Some of these properties are required to display items + // on the Steam community web site. Other properties can be defined by applications. + // Use of these functions is optional; there is no reason to call LoadItemDefinitions + // if your game hardcodes the numeric definition IDs (eg, purple face mask = 20, blue + // weapon mod = 55) and does not allow for adding new item types without a client patch. + // + + // LoadItemDefinitions triggers the automatic load and refresh of item definitions. + // Every time new item definitions are available (eg, from the dynamic addition of new + // item types while players are still in-game), a SteamInventoryDefinitionUpdate_t + // callback will be fired. + METHOD_DESC(LoadItemDefinitions triggers the automatic load and refresh of item definitions.) + virtual bool LoadItemDefinitions() = 0; + + // GetItemDefinitionIDs returns the set of all defined item definition IDs (which are + // defined via Steamworks configuration, and not necessarily contiguous integers). + // If pItemDefIDs is null, the call will return true and *punItemDefIDsArraySize will + // contain the total size necessary for a subsequent call. Otherwise, the call will + // return false if and only if there is not enough space in the output array. + virtual bool GetItemDefinitionIDs( + OUT_ARRAY_COUNT(punItemDefIDsArraySize,List of item definition IDs) SteamItemDef_t *pItemDefIDs, + DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize ) = 0; + + // GetItemDefinitionProperty returns a string property from a given item definition. + // Note that some properties (for example, "name") may be localized and will depend + // on the current Steam language settings (see ISteamApps::GetCurrentGameLanguage). + // Property names are always composed of ASCII letters, numbers, and/or underscores. + // Pass a NULL pointer for pchPropertyName to get a comma - separated list of available + // property names. If pchValueBuffer is NULL, *punValueBufferSize will contain the + // suggested buffer size. Otherwise it will be the number of bytes actually copied + // to pchValueBuffer. If the results do not fit in the given buffer, partial + // results may be copied. + virtual bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPropertyName, + OUT_STRING_COUNT(punValueBufferSizeOut) char *pchValueBuffer, uint32 *punValueBufferSizeOut ) = 0; + + // Request the list of "eligible" promo items that can be manually granted to the given + // user. These are promo items of type "manual" that won't be granted automatically. + // An example usage of this is an item that becomes available every week. + CALL_RESULT( SteamInventoryEligiblePromoItemDefIDs_t ) + virtual SteamAPICall_t RequestEligiblePromoItemDefinitionsIDs( CSteamID steamID ) = 0; + + // After handling a SteamInventoryEligiblePromoItemDefIDs_t call result, use this + // function to pull out the list of item definition ids that the user can be + // manually granted via the AddPromoItems() call. + virtual bool GetEligiblePromoItemDefinitionIDs( + CSteamID steamID, + OUT_ARRAY_COUNT(punItemDefIDsArraySize,List of item definition IDs) SteamItemDef_t *pItemDefIDs, + DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize ) = 0; +}; + +#define STEAMINVENTORY_INTERFACE_VERSION "STEAMINVENTORY_INTERFACE_V002" + + +// SteamInventoryResultReady_t callbacks are fired whenever asynchronous +// results transition from "Pending" to "OK" or an error state. There will +// always be exactly one callback per handle. +struct SteamInventoryResultReady_t +{ + enum { k_iCallback = k_iClientInventoryCallbacks + 0 }; + SteamInventoryResult_t m_handle; + EResult m_result; +}; + + +// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems +// successfully returns a result which is newer / fresher than the last +// known result. (It will not trigger if the inventory hasn't changed, +// or if results from two overlapping calls are reversed in flight and +// the earlier result is already known to be stale/out-of-date.) +// The normal ResultReady callback will still be triggered immediately +// afterwards; this is an additional notification for your convenience. +struct SteamInventoryFullUpdate_t +{ + enum { k_iCallback = k_iClientInventoryCallbacks + 1 }; + SteamInventoryResult_t m_handle; +}; + + +// A SteamInventoryDefinitionUpdate_t callback is triggered whenever +// item definitions have been updated, which could be in response to +// LoadItemDefinitions() or any other async request which required +// a definition update in order to process results from the server. +struct SteamInventoryDefinitionUpdate_t +{ + enum { k_iCallback = k_iClientInventoryCallbacks + 2 }; +}; + +// Returned +struct SteamInventoryEligiblePromoItemDefIDs_t +{ + enum { k_iCallback = k_iClientInventoryCallbacks + 3 }; + EResult m_result; + CSteamID m_steamID; + int m_numEligiblePromoItemDefs; + bool m_bCachedData; // indicates that the data was retrieved from the cache and not the server +}; + + +#pragma pack( pop ) + + +#endif // ISTEAMCONTROLLER_H diff --git a/dep/steam_api/isteammasterserverupdater.h b/dep/steam_api/isteammasterserverupdater.h new file mode 100644 index 0000000..4be0ca5 --- /dev/null +++ b/dep/steam_api/isteammasterserverupdater.h @@ -0,0 +1 @@ +#error "This file isn't used any more" diff --git a/dep/steam_api/isteammatchmaking.h b/dep/steam_api/isteammatchmaking.h new file mode 100644 index 0000000..837d98b --- /dev/null +++ b/dep/steam_api/isteammatchmaking.h @@ -0,0 +1,751 @@ +//====== 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_ELobbyTypePrivate = 0, // only way to join the lobby is to invite to someone else + 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. Lobby results are sorted from closest to farthest. +enum ELobbyDistanceFilter +{ + k_ELobbyDistanceFilterClose, // only lobbies in the same immediate region will be returned + k_ELobbyDistanceFilterDefault, // only lobbies in the same region or near by regions + 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 + } + } + */ + // + CALL_RESULT( LobbyMatchList_t ) + 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; + + virtual void AddRequestLobbyListCompatibleMembersFilter( CSteamID steamIDLobby ) = 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 point + // a LobbyEnter_t callback will also be received (since the local user is joining their own lobby) + CALL_RESULT( LobbyCreated_t ) + 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 + CALL_RESULT( LobbyEnter_t ) + 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()) + // note that the current user must be in a lobby to retrieve CSteamIDs of other users in that lobby + 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, OUT_STRUCT() 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 + // results will be returned by a LobbyDataUpdate_t callback + // if the specified lobby doesn't exist, LobbyDataUpdate_t::m_bSuccess will be set to false + 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, OUT_STRUCT() 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; + + // link two lobbies for the purposes of checking player compatibility + // you must be the lobby owner of both lobbies + virtual bool SetLinkedLobby( CSteamID steamIDLobby, CSteamID steamIDLobbyDependent ) = 0; + +#ifdef _PS3 + // 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 void CheckForPSNGameBootInvite( unsigned int iGameBootAttributes ) = 0; +#endif + CALL_BACK( LobbyChatUpdate_t ) +}; +#define STEAMMATCHMAKING_INTERFACE_VERSION "SteamMatchMaking009" + + +//----------------------------------------------------------------------------- +// 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, ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestFriendsServerList( AppId_t iApp, ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestFavoritesServerList( AppId_t iApp, ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestHistoryServerList( AppId_t iApp, ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestSpectatorServerList( AppId_t iApp, ARRAY_COUNT(nFilters) 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 filter operation codes that go in the key part of MatchMakingKeyValuePair_t should be one of these: + + "map" + - Server passes the filter if the server is playing the specified map. + "gamedataand" + - Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains all of the + specified strings. The value field is a comma-delimited list of strings to match. + "gamedataor" + - Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains at least one of the + specified strings. The value field is a comma-delimited list of strings to match. + "gamedatanor" + - Server passes the filter if the server's game data (ISteamGameServer::SetGameData) does not contain any + of the specified strings. The value field is a comma-delimited list of strings to check. + "gametagsand" + - Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) contains all + of the specified strings. The value field is a comma-delimited list of strings to check. + "gametagsnor" + - Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) does not contain any + of the specified strings. The value field is a comma-delimited list of strings to check. + "and" (x1 && x2 && ... && xn) + "or" (x1 || x2 || ... || xn) + "nand" !(x1 && x2 && ... && xn) + "nor" !(x1 || x2 || ... || xn) + - Performs Boolean operation on the following filters. The operand to this filter specifies + the "size" of the Boolean inputs to the operation, in Key/value pairs. (The keyvalue + pairs must immediately follow, i.e. this is a prefix logical operator notation.) + In the simplest case where Boolean expressions are not nested, this is simply + the number of operands. + + For example, to match servers on a particular map or with a particular tag, would would + use these filters. + + ( server.map == "cp_dustbowl" || server.gametags.contains("payload") ) + "or", "2" + "map", "cp_dustbowl" + "gametagsand", "payload" + + If logical inputs are nested, then the operand specifies the size of the entire + "length" of its operands, not the number of immediate children. + + ( server.map == "cp_dustbowl" || ( server.gametags.contains("payload") && !server.gametags.contains("payloadrace") ) ) + "or", "4" + "map", "cp_dustbowl" + "and", "2" + "gametagsand", "payload" + "gametagsnor", "payloadrace" + + Unary NOT can be achieved using either "nand" or "nor" with a single operand. + + "addr" + - Server passes the filter if the server's query address matches the specified IP or IP:port. + "gameaddr" + - Server passes the filter if the server's game address matches the specified IP or IP:port. + + The following filter operations ignore the "value" part of MatchMakingKeyValuePair_t + + "dedicated" + - Server passes the filter if it passed true to SetDedicatedServer. + "secure" + - Server passes the filter if the server is VAC-enabled. + "notfull" + - Server passes the filter if the player count is less than the reported max player count. + "hasplayers" + - Server passes the filter if the player count is greater than zero. + "noplayers" + - Server passes the filter if it doesn't have any players. + "linux" + - Server passes the filter if it's a linux 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 ISteamGameServer::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) +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +//----------------------------------------------------------------------------- +// 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 + AccountID_t m_unAccountId; +}; + + +//----------------------------------------------------------------------------- +// 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 + uint64 m_ulGameID; // GameID 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 + uint8 m_bSuccess; // true if we lobby data was successfully changed; + // will only be false if RequestLobbyData() was called on a lobby that no longer exists +}; + + +//----------------------------------------------------------------------------- +// 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 lobby has been joined and is ready for use +// a LobbyEnter_t callback will also be received (since the local user is joining their own lobby) +//----------------------------------------------------------------------------- +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 }; + + +//----------------------------------------------------------------------------- +// Purpose: Result of CheckForPSNGameBootInvite +// 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 PSNGameBootInviteResult_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 15 }; + + bool m_bGameBootInviteExists; + CSteamID m_steamIDLobby; // Should be valid if m_bGameBootInviteExists == true +}; + + +//----------------------------------------------------------------------------- +// Purpose: Result of our request to create a Lobby +// m_eResult == k_EResultOK on success +// at this point, the lobby has been joined and is ready for use +// a LobbyEnter_t callback will also be received (since the local user is joining their own lobby) +//----------------------------------------------------------------------------- +struct FavoritesListAccountsUpdated_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 16 }; + + EResult m_eResult; +}; + +#pragma pack( pop ) + + +#endif // ISTEAMMATCHMAKING diff --git a/dep/steam_api/isteammusic.h b/dep/steam_api/isteammusic.h new file mode 100644 index 0000000..779a4c2 --- /dev/null +++ b/dep/steam_api/isteammusic.h @@ -0,0 +1,67 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ + +#ifndef ISTEAMMUSIC_H +#define ISTEAMMUSIC_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +enum AudioPlayback_Status +{ + AudioPlayback_Undefined = 0, + AudioPlayback_Playing = 1, + AudioPlayback_Paused = 2, + AudioPlayback_Idle = 3 +}; + + +//----------------------------------------------------------------------------- +// Purpose: Functions to control music playback in the steam client +//----------------------------------------------------------------------------- +class ISteamMusic +{ +public: + virtual bool BIsEnabled() = 0; + virtual bool BIsPlaying() = 0; + + virtual AudioPlayback_Status GetPlaybackStatus() = 0; + + virtual void Play() = 0; + virtual void Pause() = 0; + virtual void PlayPrevious() = 0; + virtual void PlayNext() = 0; + + // volume is between 0.0 and 1.0 + virtual void SetVolume( float flVolume ) = 0; + virtual float GetVolume() = 0; + +}; + +#define STEAMMUSIC_INTERFACE_VERSION "STEAMMUSIC_INTERFACE_VERSION001" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + + +DEFINE_CALLBACK( PlaybackStatusHasChanged_t, k_iSteamMusicCallbacks + 1 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( VolumeHasChanged_t, k_iSteamMusicCallbacks + 2 ) + CALLBACK_MEMBER( 0, float, m_flNewVolume ) +END_DEFINE_CALLBACK_1() + +#pragma pack( pop ) + + +#endif // #define ISTEAMMUSIC_H diff --git a/dep/steam_api/isteammusicremote.h b/dep/steam_api/isteammusicremote.h new file mode 100644 index 0000000..ea29a7d --- /dev/null +++ b/dep/steam_api/isteammusicremote.h @@ -0,0 +1,129 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ + +#ifndef ISTEAMMUSICREMOTE_H +#define ISTEAMMUSICREMOTE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" +#include "isteammusic.h" + +#define k_SteamMusicNameMaxLength 255 +#define k_SteamMusicPNGMaxLength 65535 + + +class ISteamMusicRemote +{ +public: + // Service Definition + virtual bool RegisterSteamMusicRemote( const char *pchName ) = 0; + virtual bool DeregisterSteamMusicRemote() = 0; + virtual bool BIsCurrentMusicRemote() = 0; + virtual bool BActivationSuccess( bool bValue ) = 0; + + virtual bool SetDisplayName( const char *pchDisplayName ) = 0; + virtual bool SetPNGIcon_64x64( void *pvBuffer, uint32 cbBufferLength ) = 0; + + // Abilities for the user interface + virtual bool EnablePlayPrevious(bool bValue) = 0; + virtual bool EnablePlayNext( bool bValue ) = 0; + virtual bool EnableShuffled( bool bValue ) = 0; + virtual bool EnableLooped( bool bValue ) = 0; + virtual bool EnableQueue( bool bValue ) = 0; + virtual bool EnablePlaylists( bool bValue ) = 0; + + // Status + virtual bool UpdatePlaybackStatus( AudioPlayback_Status nStatus ) = 0; + virtual bool UpdateShuffled( bool bValue ) = 0; + virtual bool UpdateLooped( bool bValue ) = 0; + virtual bool UpdateVolume( float flValue ) = 0; // volume is between 0.0 and 1.0 + + // Current Entry + virtual bool CurrentEntryWillChange() = 0; + virtual bool CurrentEntryIsAvailable( bool bAvailable ) = 0; + virtual bool UpdateCurrentEntryText( const char *pchText ) = 0; + virtual bool UpdateCurrentEntryElapsedSeconds( int nValue ) = 0; + virtual bool UpdateCurrentEntryCoverArt( void *pvBuffer, uint32 cbBufferLength ) = 0; + virtual bool CurrentEntryDidChange() = 0; + + // Queue + virtual bool QueueWillChange() = 0; + virtual bool ResetQueueEntries() = 0; + virtual bool SetQueueEntry( int nID, int nPosition, const char *pchEntryText ) = 0; + virtual bool SetCurrentQueueEntry( int nID ) = 0; + virtual bool QueueDidChange() = 0; + + // Playlist + virtual bool PlaylistWillChange() = 0; + virtual bool ResetPlaylistEntries() = 0; + virtual bool SetPlaylistEntry( int nID, int nPosition, const char *pchEntryText ) = 0; + virtual bool SetCurrentPlaylistEntry( int nID ) = 0; + virtual bool PlaylistDidChange() = 0; +}; + +#define STEAMMUSICREMOTE_INTERFACE_VERSION "STEAMMUSICREMOTE_INTERFACE_VERSION001" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + + +DEFINE_CALLBACK( MusicPlayerRemoteWillActivate_t, k_iSteamMusicRemoteCallbacks + 1) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( MusicPlayerRemoteWillDeactivate_t, k_iSteamMusicRemoteCallbacks + 2 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( MusicPlayerRemoteToFront_t, k_iSteamMusicRemoteCallbacks + 3 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( MusicPlayerWillQuit_t, k_iSteamMusicRemoteCallbacks + 4 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( MusicPlayerWantsPlay_t, k_iSteamMusicRemoteCallbacks + 5 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( MusicPlayerWantsPause_t, k_iSteamMusicRemoteCallbacks + 6 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( MusicPlayerWantsPlayPrevious_t, k_iSteamMusicRemoteCallbacks + 7 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( MusicPlayerWantsPlayNext_t, k_iSteamMusicRemoteCallbacks + 8 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( MusicPlayerWantsShuffled_t, k_iSteamMusicRemoteCallbacks + 9 ) + CALLBACK_MEMBER( 0, bool, m_bShuffled ) +END_DEFINE_CALLBACK_1() + +DEFINE_CALLBACK( MusicPlayerWantsLooped_t, k_iSteamMusicRemoteCallbacks + 10 ) + CALLBACK_MEMBER(0, bool, m_bLooped ) +END_DEFINE_CALLBACK_1() + +DEFINE_CALLBACK( MusicPlayerWantsVolume_t, k_iSteamMusicCallbacks + 11 ) + CALLBACK_MEMBER(0, float, m_flNewVolume) +END_DEFINE_CALLBACK_1() + +DEFINE_CALLBACK( MusicPlayerSelectsQueueEntry_t, k_iSteamMusicCallbacks + 12 ) + CALLBACK_MEMBER(0, int, nID ) +END_DEFINE_CALLBACK_1() + +DEFINE_CALLBACK( MusicPlayerSelectsPlaylistEntry_t, k_iSteamMusicCallbacks + 13 ) + CALLBACK_MEMBER(0, int, nID ) +END_DEFINE_CALLBACK_1() + +DEFINE_CALLBACK( MusicPlayerWantsPlayingRepeatStatus_t, k_iSteamMusicRemoteCallbacks + 14 ) + CALLBACK_MEMBER(0, int, m_nPlayingRepeatStatus ) +END_DEFINE_CALLBACK_1() + +#pragma pack( pop ) + + + +#endif // #define ISTEAMMUSICREMOTE_H diff --git a/dep/steam_api/isteamnetworking.h b/dep/steam_api/isteamnetworking.h new file mode 100644 index 0000000..8f70819 --- /dev/null +++ b/dep/steam_api/isteamnetworking.h @@ -0,0 +1,306 @@ +//====== 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 + k_EP2PSessionErrorMax = 5 +}; + +// 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 +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif +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 + // + // nChannel is a routing number you can use to help route message to different systems - you'll have to call ReadP2PPacket() + // with the same channel number in order to retrieve the data on the other end + // using different channels to talk to the same user will still use the same underlying p2p connection, saving on resources + virtual bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData, EP2PSend eP2PSendType, int nChannel = 0 ) = 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, int nChannel = 0 ) = 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, int nChannel = 0 ) = 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; + + // call CloseP2PChannelWithUser() when you're done talking to a user on a specific channel. Once all channels + // open channels to a user have been closed, the open session to the user will be closed and new data from this + // user will trigger a P2PSessionRequest_t callback + virtual bool CloseP2PChannelWithUser( CSteamID steamIDRemote, int nChannel ) = 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; + + // Allow P2P connections to fall back to being relayed through the Steam servers if a direct connection + // or NAT-traversal cannot be established. Only applies to connections created after setting this value, + // or to existing connections that need to automatically reconnect after this value is set. + // + // P2P packet relay is allowed by default + virtual bool AllowP2PPacketRelay( bool bAllow ) = 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 "SteamNetworking005" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +// 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/dep/steam_api/isteamps3overlayrenderer.h b/dep/steam_api/isteamps3overlayrenderer.h new file mode 100644 index 0000000..4e07d4a --- /dev/null +++ b/dep/steam_api/isteamps3overlayrenderer.h @@ -0,0 +1,91 @@ +//====== Copyright © 1996-2010, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface the game must provide Steam with on PS3 in order for the +// Steam overlay to render. +// +//============================================================================= + +#ifndef ISTEAMPS3OVERLAYRENDERER_H +#define ISTEAMPS3OVERLAYRENDERER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "cell/pad.h" + +//----------------------------------------------------------------------------- +// Purpose: Enum for supported gradient directions +//----------------------------------------------------------------------------- +enum EOverlayGradientDirection +{ + k_EOverlayGradientHorizontal = 1, + k_EOverlayGradientVertical = 2, + k_EOverlayGradientNone = 3, +}; + +// Helpers for fetching individual color components from ARGB packed DWORD colors Steam PS3 overlay renderer uses. +#define STEAM_COLOR_RED( color ) \ + (int)(((color)>>16)&0xff) + +#define STEAM_COLOR_GREEN( color ) \ + (int)(((color)>>8)&0xff) + +#define STEAM_COLOR_BLUE( color ) \ + (int)((color)&0xff) + +#define STEAM_COLOR_ALPHA( color ) \ + (int)(((color)>>24)&0xff) + + +//----------------------------------------------------------------------------- +// Purpose: Interface the game must expose to Steam for rendering +//----------------------------------------------------------------------------- +class ISteamPS3OverlayRenderHost +{ +public: + + // Interface for game engine to implement which Steam requires to render. + + // Draw a textured rect. This may use only part of the texture and will pass texture coords, it will also possibly request a gradient and will specify colors for vertexes. + virtual void DrawTexturedRect( int x0, int y0, int x1, int y1, float u0, float v0, float u1, float v1, int32 iTextureID, DWORD colorStart, DWORD colorEnd, EOverlayGradientDirection eDirection ) = 0; + + // Load a RGBA texture for Steam, or update a previously loaded one. Updates may be partial. You must not evict or remove this texture once Steam has uploaded it. + virtual void LoadOrUpdateTexture( int32 iTextureID, bool bIsFullTexture, int x0, int y0, uint32 uWidth, uint32 uHeight, int32 iBytes, char *pData ) = 0; + + // Delete a texture Steam previously uploaded + virtual void DeleteTexture( int32 iTextureID ) = 0; + + // Delete all previously uploaded textures + virtual void DeleteAllTextures() = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Interface Steam exposes for the game to tell it when to render, etc. +//----------------------------------------------------------------------------- +class ISteamPS3OverlayRender +{ +public: + + // Call once at startup to initialize the Steam overlay and pass it your host interface ptr + virtual bool BHostInitialize( uint32 unScreenWidth, uint32 unScreenHeight, uint32 unRefreshRate, ISteamPS3OverlayRenderHost *pRenderHost, void *CellFontLib ) = 0; + + // Call this once a frame when you are ready for the Steam overlay to render (ie, right before flipping buffers, after all your rendering) + virtual void Render() = 0; + + // Call this everytime you read input on PS3. + // + // If this returns true, then the overlay is active and has consumed the input, your game + // should then ignore all the input until BHandleCellPadData once again returns false, which + // will mean the overlay is deactivated. + virtual bool BHandleCellPadData( const CellPadData &padData ) = 0; + + // Call this if you detect no controllers connected or that the XMB is intercepting input + // + // This is important to clear input state for the overlay, so keys left down during XMB activation + // are not continued to be processed. + virtual bool BResetInputState() = 0; +}; + + +#endif // ISTEAMPS3OVERLAYRENDERER_H \ No newline at end of file diff --git a/dep/steam_api/isteamremotestorage.h b/dep/steam_api/isteamremotestorage.h new file mode 100644 index 0000000..3ac2871 --- /dev/null +++ b/dep/steam_api/isteamremotestorage.h @@ -0,0 +1,681 @@ +//====== 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: Defines the largest allowed file size. Cloud files cannot be written +// in a single chunk over 100MB (and cannot be over 200MB total.) +//----------------------------------------------------------------------------- +const uint32 k_unMaxCloudFileChunkSize = 100 * 1024 * 1024; + + +//----------------------------------------------------------------------------- +// Purpose: Structure that contains an array of const char * strings and the number of those strings +//----------------------------------------------------------------------------- +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif +struct SteamParamStringArray_t +{ + const char ** m_ppStrings; + int32 m_nNumStrings; +}; +#pragma pack( pop ) + +// A handle to a piece of user generated content +typedef uint64 UGCHandle_t; +typedef uint64 PublishedFileUpdateHandle_t; +typedef uint64 PublishedFileId_t; +const PublishedFileId_t k_PublishedFileIdInvalid = 0; +const UGCHandle_t k_UGCHandleInvalid = 0xffffffffffffffffull; +const PublishedFileUpdateHandle_t k_PublishedFileUpdateHandleInvalid = 0xffffffffffffffffull; + +// Handle for writing to Steam Cloud +typedef uint64 UGCFileWriteStreamHandle_t; +const UGCFileWriteStreamHandle_t k_UGCFileStreamHandleInvalid = 0xffffffffffffffffull; + +const uint32 k_cchPublishedDocumentTitleMax = 128 + 1; +const uint32 k_cchPublishedDocumentDescriptionMax = 8000; +const uint32 k_cchPublishedDocumentChangeDescriptionMax = 8000; +const uint32 k_unEnumeratePublishedFilesMaxResults = 50; +const uint32 k_cchTagListMax = 1024 + 1; +const uint32 k_cchFilenameMax = 260; +const uint32 k_cchPublishedFileURLMax = 256; + + +enum ERemoteStoragePlatform +{ + k_ERemoteStoragePlatformNone = 0, + k_ERemoteStoragePlatformWindows = (1 << 0), + k_ERemoteStoragePlatformOSX = (1 << 1), + k_ERemoteStoragePlatformPS3 = (1 << 2), + k_ERemoteStoragePlatformLinux = (1 << 3), + k_ERemoteStoragePlatformReserved2 = (1 << 4), + + k_ERemoteStoragePlatformAll = 0xffffffff +}; + +enum ERemoteStoragePublishedFileVisibility +{ + k_ERemoteStoragePublishedFileVisibilityPublic = 0, + k_ERemoteStoragePublishedFileVisibilityFriendsOnly = 1, + k_ERemoteStoragePublishedFileVisibilityPrivate = 2, +}; + + +enum EWorkshopFileType +{ + k_EWorkshopFileTypeFirst = 0, + + k_EWorkshopFileTypeCommunity = 0, // normal Workshop item that can be subscribed to + k_EWorkshopFileTypeMicrotransaction = 1, // Workshop item that is meant to be voted on for the purpose of selling in-game + k_EWorkshopFileTypeCollection = 2, // a collection of Workshop or Greenlight items + k_EWorkshopFileTypeArt = 3, // artwork + k_EWorkshopFileTypeVideo = 4, // external video + k_EWorkshopFileTypeScreenshot = 5, // screenshot + k_EWorkshopFileTypeGame = 6, // Greenlight game entry + k_EWorkshopFileTypeSoftware = 7, // Greenlight software entry + k_EWorkshopFileTypeConcept = 8, // Greenlight concept + k_EWorkshopFileTypeWebGuide = 9, // Steam web guide + k_EWorkshopFileTypeIntegratedGuide = 10, // application integrated guide + k_EWorkshopFileTypeMerch = 11, // Workshop merchandise meant to be voted on for the purpose of being sold + k_EWorkshopFileTypeControllerBinding = 12, // Steam Controller bindings + k_EWorkshopFileTypeSteamworksAccessInvite = 13, // internal + k_EWorkshopFileTypeSteamVideo = 14, // Steam video + k_EWorkshopFileTypeGameManagedItem = 15, // managed completely by the game, not the user, and not shown on the web + + // Update k_EWorkshopFileTypeMax if you add values. + k_EWorkshopFileTypeMax = 16 + +}; + +enum EWorkshopVote +{ + k_EWorkshopVoteUnvoted = 0, + k_EWorkshopVoteFor = 1, + k_EWorkshopVoteAgainst = 2, + k_EWorkshopVoteLater = 3, +}; + +enum EWorkshopFileAction +{ + k_EWorkshopFileActionPlayed = 0, + k_EWorkshopFileActionCompleted = 1, +}; + +enum EWorkshopEnumerationType +{ + k_EWorkshopEnumerationTypeRankedByVote = 0, + k_EWorkshopEnumerationTypeRecent = 1, + k_EWorkshopEnumerationTypeTrending = 2, + k_EWorkshopEnumerationTypeFavoritesOfFriends = 3, + k_EWorkshopEnumerationTypeVotedByFriends = 4, + k_EWorkshopEnumerationTypeContentByFriends = 5, + k_EWorkshopEnumerationTypeRecentFromFollowedUsers = 6, +}; + +enum EWorkshopVideoProvider +{ + k_EWorkshopVideoProviderNone = 0, + k_EWorkshopVideoProviderYoutube = 1 +}; + + +enum EUGCReadAction +{ + // Keeps the file handle open unless the last byte is read. You can use this when reading large files (over 100MB) in sequential chunks. + // If the last byte is read, this will behave the same as k_EUGCRead_Close. Otherwise, it behaves the same as k_EUGCRead_ContinueReading. + // This value maintains the same behavior as before the EUGCReadAction parameter was introduced. + k_EUGCRead_ContinueReadingUntilFinished = 0, + + // Keeps the file handle open. Use this when using UGCRead to seek to different parts of the file. + // When you are done seeking around the file, make a final call with k_EUGCRead_Close to close it. + k_EUGCRead_ContinueReading = 1, + + // Frees the file handle. Use this when you're done reading the content. + // To read the file from Steam again you will need to call UGCDownload again. + k_EUGCRead_Close = 2, +}; + + +//----------------------------------------------------------------------------- +// 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 FileRead( const char *pchFile, void *pvData, int32 cubDataToRead ) = 0; + + CALL_RESULT( RemoteStorageFileWriteAsyncComplete_t ) + virtual SteamAPICall_t FileWriteAsync( const char *pchFile, const void *pvData, uint32 cubData ) = 0; + + CALL_RESULT( RemoteStorageFileReadAsyncComplete_t ) + virtual SteamAPICall_t FileReadAsync( const char *pchFile, uint32 nOffset, uint32 cubToRead ) = 0; + virtual bool FileReadAsyncComplete( SteamAPICall_t hReadCall, void *pvBuffer, uint32 cubToRead ) = 0; + + virtual bool FileForget( const char *pchFile ) = 0; + virtual bool FileDelete( const char *pchFile ) = 0; + CALL_RESULT( RemoteStorageFileShareResult_t ) + virtual SteamAPICall_t FileShare( const char *pchFile ) = 0; + virtual bool SetSyncPlatforms( const char *pchFile, ERemoteStoragePlatform eRemoteStoragePlatform ) = 0; + + // file operations that cause network IO + virtual UGCFileWriteStreamHandle_t FileWriteStreamOpen( const char *pchFile ) = 0; + virtual bool FileWriteStreamWriteChunk( UGCFileWriteStreamHandle_t writeHandle, const void *pvData, int32 cubData ) = 0; + virtual bool FileWriteStreamClose( UGCFileWriteStreamHandle_t writeHandle ) = 0; + virtual bool FileWriteStreamCancel( UGCFileWriteStreamHandle_t writeHandle ) = 0; + + // file information + virtual bool FileExists( const char *pchFile ) = 0; + virtual bool FilePersisted( const char *pchFile ) = 0; + virtual int32 GetFileSize( const char *pchFile ) = 0; + virtual int64 GetFileTimestamp( const char *pchFile ) = 0; + virtual ERemoteStoragePlatform GetSyncPlatforms( const char *pchFile ) = 0; + + // iteration + virtual int32 GetFileCount() = 0; + virtual const char *GetFileNameAndSize( int iFile, int32 *pnFileSizeInBytes ) = 0; + + // configuration management + virtual bool GetQuota( uint64 *pnTotalBytes, uint64 *puAvailableBytes ) = 0; + virtual bool IsCloudEnabledForAccount() = 0; + virtual bool IsCloudEnabledForApp() = 0; + virtual void SetCloudEnabledForApp( bool bEnabled ) = 0; + + // user generated content + + // Downloads a UGC file. A priority value of 0 will download the file immediately, + // otherwise it will wait to download the file until all downloads with a lower priority + // value are completed. Downloads with equal priority will occur simultaneously. + CALL_RESULT( RemoteStorageDownloadUGCResult_t ) + virtual SteamAPICall_t UGCDownload( UGCHandle_t hContent, uint32 unPriority ) = 0; + + // Gets the amount of data downloaded so far for a piece of content. pnBytesExpected can be 0 if function returns false + // or if the transfer hasn't started yet, so be careful to check for that before dividing to get a percentage + virtual bool GetUGCDownloadProgress( UGCHandle_t hContent, int32 *pnBytesDownloaded, int32 *pnBytesExpected ) = 0; + + // Gets metadata for a file after it has been downloaded. This is the same metadata given in the RemoteStorageDownloadUGCResult_t call result + virtual bool GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID, OUT_STRING() char **ppchName, int32 *pnFileSizeInBytes, OUT_STRUCT() CSteamID *pSteamIDOwner ) = 0; + + // After download, gets the content of the file. + // Small files can be read all at once by calling this function with an offset of 0 and cubDataToRead equal to the size of the file. + // Larger files can be read in chunks to reduce memory usage (since both sides of the IPC client and the game itself must allocate + // enough memory for each chunk). Once the last byte is read, the file is implicitly closed and further calls to UGCRead will fail + // unless UGCDownload is called again. + // For especially large files (anything over 100MB) it is a requirement that the file is read in chunks. + virtual int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 cOffset, EUGCReadAction eAction ) = 0; + + // Functions to iterate through UGC that has finished downloading but has not yet been read via UGCRead() + virtual int32 GetCachedUGCCount() = 0; + virtual UGCHandle_t GetCachedUGCHandle( int32 iCachedContent ) = 0; + + // The following functions are only necessary on the Playstation 3. On PC & Mac, the Steam client will handle these operations for you + // On Playstation 3, the game controls which files are stored in the cloud, via FilePersist, FileFetch, and FileForget. + +#if defined(_PS3) || defined(_SERVER) + // Connect to Steam and get a list of files in the Cloud - results in a RemoteStorageAppSyncStatusCheck_t callback + virtual void GetFileListFromServer() = 0; + // Indicate this file should be downloaded in the next sync + virtual bool FileFetch( const char *pchFile ) = 0; + // Indicate this file should be persisted in the next sync + virtual bool FilePersist( const char *pchFile ) = 0; + // Pull any requested files down from the Cloud - results in a RemoteStorageAppSyncedClient_t callback + virtual bool SynchronizeToClient() = 0; + // Upload any requested files to the Cloud - results in a RemoteStorageAppSyncedServer_t callback + virtual bool SynchronizeToServer() = 0; + // Reset any fetch/persist/etc requests + virtual bool ResetFileRequestState() = 0; +#endif + + // publishing UGC + CALL_RESULT( RemoteStoragePublishFileProgress_t ) + virtual SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags, EWorkshopFileType eWorkshopFileType ) = 0; + virtual PublishedFileUpdateHandle_t CreatePublishedFileUpdateRequest( PublishedFileId_t unPublishedFileId ) = 0; + virtual bool UpdatePublishedFileFile( PublishedFileUpdateHandle_t updateHandle, const char *pchFile ) = 0; + virtual bool UpdatePublishedFilePreviewFile( PublishedFileUpdateHandle_t updateHandle, const char *pchPreviewFile ) = 0; + virtual bool UpdatePublishedFileTitle( PublishedFileUpdateHandle_t updateHandle, const char *pchTitle ) = 0; + virtual bool UpdatePublishedFileDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchDescription ) = 0; + virtual bool UpdatePublishedFileVisibility( PublishedFileUpdateHandle_t updateHandle, ERemoteStoragePublishedFileVisibility eVisibility ) = 0; + virtual bool UpdatePublishedFileTags( PublishedFileUpdateHandle_t updateHandle, SteamParamStringArray_t *pTags ) = 0; + CALL_RESULT( RemoteStorageUpdatePublishedFileResult_t ) + virtual SteamAPICall_t CommitPublishedFileUpdate( PublishedFileUpdateHandle_t updateHandle ) = 0; + // Gets published file details for the given publishedfileid. If unMaxSecondsOld is greater than 0, + // cached data may be returned, depending on how long ago it was cached. A value of 0 will force a refresh. + // A value of k_WorkshopForceLoadPublishedFileDetailsFromCache will use cached data if it exists, no matter how old it is. + CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t ) + virtual SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId, uint32 unMaxSecondsOld ) = 0; + CALL_RESULT( RemoteStorageDeletePublishedFileResult_t ) + virtual SteamAPICall_t DeletePublishedFile( PublishedFileId_t unPublishedFileId ) = 0; + // enumerate the files that the current user published with this app + CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t ) + virtual SteamAPICall_t EnumerateUserPublishedFiles( uint32 unStartIndex ) = 0; + CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t ) + virtual SteamAPICall_t SubscribePublishedFile( PublishedFileId_t unPublishedFileId ) = 0; + CALL_RESULT( RemoteStorageEnumerateUserSubscribedFilesResult_t ) + virtual SteamAPICall_t EnumerateUserSubscribedFiles( uint32 unStartIndex ) = 0; + CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t ) + virtual SteamAPICall_t UnsubscribePublishedFile( PublishedFileId_t unPublishedFileId ) = 0; + virtual bool UpdatePublishedFileSetChangeDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchChangeDescription ) = 0; + CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t ) + virtual SteamAPICall_t GetPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId ) = 0; + CALL_RESULT( RemoteStorageUpdateUserPublishedItemVoteResult_t ) + virtual SteamAPICall_t UpdateUserPublishedItemVote( PublishedFileId_t unPublishedFileId, bool bVoteUp ) = 0; + CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t ) + virtual SteamAPICall_t GetUserPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId ) = 0; + CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t ) + virtual SteamAPICall_t EnumerateUserSharedWorkshopFiles( CSteamID steamId, uint32 unStartIndex, SteamParamStringArray_t *pRequiredTags, SteamParamStringArray_t *pExcludedTags ) = 0; + CALL_RESULT( RemoteStoragePublishFileProgress_t ) + virtual SteamAPICall_t PublishVideo( EWorkshopVideoProvider eVideoProvider, const char *pchVideoAccount, const char *pchVideoIdentifier, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags ) = 0; + CALL_RESULT( RemoteStorageSetUserPublishedFileActionResult_t ) + virtual SteamAPICall_t SetUserPublishedFileAction( PublishedFileId_t unPublishedFileId, EWorkshopFileAction eAction ) = 0; + CALL_RESULT( RemoteStorageEnumeratePublishedFilesByUserActionResult_t ) + virtual SteamAPICall_t EnumeratePublishedFilesByUserAction( EWorkshopFileAction eAction, uint32 unStartIndex ) = 0; + // this method enumerates the public view of workshop files + CALL_RESULT( RemoteStorageEnumerateWorkshopFilesResult_t ) + virtual SteamAPICall_t EnumeratePublishedWorkshopFiles( EWorkshopEnumerationType eEnumerationType, uint32 unStartIndex, uint32 unCount, uint32 unDays, SteamParamStringArray_t *pTags, SteamParamStringArray_t *pUserTags ) = 0; + + CALL_RESULT( RemoteStorageDownloadUGCResult_t ) + virtual SteamAPICall_t UGCDownloadToLocation( UGCHandle_t hContent, const char *pchLocation, uint32 unPriority ) = 0; +}; + +#define STEAMREMOTESTORAGE_INTERFACE_VERSION "STEAMREMOTESTORAGE_INTERFACE_VERSION014" + + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +//----------------------------------------------------------------------------- +// Purpose: sent when the local file cache is fully synced with the server for an app +// That means that an application can be started and has all latest files +//----------------------------------------------------------------------------- +struct RemoteStorageAppSyncedClient_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 1 }; + AppId_t m_nAppID; + EResult m_eResult; + int m_unNumDownloads; +}; + +//----------------------------------------------------------------------------- +// Purpose: sent when the server is fully synced with the local file cache for an app +// That means that we can shutdown Steam and our data is stored on the server +//----------------------------------------------------------------------------- +struct RemoteStorageAppSyncedServer_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 2 }; + AppId_t m_nAppID; + EResult m_eResult; + int m_unNumUploads; +}; + +//----------------------------------------------------------------------------- +// Purpose: Status of up and downloads during a sync session +// +//----------------------------------------------------------------------------- +struct RemoteStorageAppSyncProgress_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 3 }; + char m_rgchCurrentFile[k_cchFilenameMax]; // Current file being transferred + AppId_t m_nAppID; // App this info relates to + uint32 m_uBytesTransferredThisChunk; // Bytes transferred this chunk + double m_dAppPercentComplete; // Percent complete that this app's transfers are + bool m_bUploading; // if false, downloading +}; + +// +// IMPORTANT! k_iClientRemoteStorageCallbacks + 4 is used, see iclientremotestorage.h +// + + +//----------------------------------------------------------------------------- +// Purpose: Sent after we've determined the list of files that are out of sync +// with the server. +//----------------------------------------------------------------------------- +struct RemoteStorageAppSyncStatusCheck_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 5 }; + AppId_t m_nAppID; + EResult m_eResult; +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to FileShare() +//----------------------------------------------------------------------------- +struct RemoteStorageFileShareResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 7 }; + EResult m_eResult; // The result of the operation + UGCHandle_t m_hFile; // The handle that can be shared with users and features + char m_rgchFilename[k_cchFilenameMax]; // The name of the file that was shared +}; + + +// k_iClientRemoteStorageCallbacks + 8 is deprecated! Do not reuse + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to PublishFile() +//----------------------------------------------------------------------------- +struct RemoteStoragePublishFileResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 9 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; + bool m_bUserNeedsToAcceptWorkshopLegalAgreement; +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to DeletePublishedFile() +//----------------------------------------------------------------------------- +struct RemoteStorageDeletePublishedFileResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 11 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to EnumerateUserPublishedFiles() +//----------------------------------------------------------------------------- +struct RemoteStorageEnumerateUserPublishedFilesResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 12 }; + EResult m_eResult; // The result of the operation. + int32 m_nResultsReturned; + int32 m_nTotalResultCount; + PublishedFileId_t m_rgPublishedFileId[ k_unEnumeratePublishedFilesMaxResults ]; +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to SubscribePublishedFile() +//----------------------------------------------------------------------------- +struct RemoteStorageSubscribePublishedFileResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 13 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to EnumerateSubscribePublishedFiles() +//----------------------------------------------------------------------------- +struct RemoteStorageEnumerateUserSubscribedFilesResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 14 }; + EResult m_eResult; // The result of the operation. + int32 m_nResultsReturned; + int32 m_nTotalResultCount; + PublishedFileId_t m_rgPublishedFileId[ k_unEnumeratePublishedFilesMaxResults ]; + uint32 m_rgRTimeSubscribed[ k_unEnumeratePublishedFilesMaxResults ]; +}; + +#if defined(VALVE_CALLBACK_PACK_SMALL) + VALVE_COMPILE_TIME_ASSERT( sizeof( RemoteStorageEnumerateUserSubscribedFilesResult_t ) == (1 + 1 + 1 + 50 + 100) * 4 ); +#elif defined(VALVE_CALLBACK_PACK_LARGE) + VALVE_COMPILE_TIME_ASSERT( sizeof( RemoteStorageEnumerateUserSubscribedFilesResult_t ) == (1 + 1 + 1 + 50 + 100) * 4 + 4 ); +#else +#warning You must first include isteamclient.h +#endif + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to UnsubscribePublishedFile() +//----------------------------------------------------------------------------- +struct RemoteStorageUnsubscribePublishedFileResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 15 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to CommitPublishedFileUpdate() +//----------------------------------------------------------------------------- +struct RemoteStorageUpdatePublishedFileResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 16 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; + bool m_bUserNeedsToAcceptWorkshopLegalAgreement; +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to UGCDownload() +//----------------------------------------------------------------------------- +struct RemoteStorageDownloadUGCResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 17 }; + EResult m_eResult; // The result of the operation. + UGCHandle_t m_hFile; // The handle to the file that was attempted to be downloaded. + AppId_t m_nAppID; // ID of the app that created this file. + int32 m_nSizeInBytes; // The size of the file that was downloaded, in bytes. + char m_pchFileName[k_cchFilenameMax]; // The name of the file that was downloaded. + uint64 m_ulSteamIDOwner; // Steam ID of the user who created this content. +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to GetPublishedFileDetails() +//----------------------------------------------------------------------------- +struct RemoteStorageGetPublishedFileDetailsResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 18 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; + AppId_t m_nCreatorAppID; // ID of the app that created this file. + AppId_t m_nConsumerAppID; // ID of the app that will consume this file. + char m_rgchTitle[k_cchPublishedDocumentTitleMax]; // title of document + char m_rgchDescription[k_cchPublishedDocumentDescriptionMax]; // description of document + UGCHandle_t m_hFile; // The handle of the primary file + UGCHandle_t m_hPreviewFile; // The handle of the preview file + uint64 m_ulSteamIDOwner; // Steam ID of the user who created this content. + uint32 m_rtimeCreated; // time when the published file was created + uint32 m_rtimeUpdated; // time when the published file was last updated + ERemoteStoragePublishedFileVisibility m_eVisibility; + bool m_bBanned; + char m_rgchTags[k_cchTagListMax]; // comma separated list of all tags associated with this file + bool m_bTagsTruncated; // whether the list of tags was too long to be returned in the provided buffer + char m_pchFileName[k_cchFilenameMax]; // The name of the primary file + int32 m_nFileSize; // Size of the primary file + int32 m_nPreviewFileSize; // Size of the preview file + char m_rgchURL[k_cchPublishedFileURLMax]; // URL (for a video or a website) + EWorkshopFileType m_eFileType; // Type of the file + bool m_bAcceptedForUse; // developer has specifically flagged this item as accepted in the Workshop +}; + + +struct RemoteStorageEnumerateWorkshopFilesResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 19 }; + EResult m_eResult; + int32 m_nResultsReturned; + int32 m_nTotalResultCount; + PublishedFileId_t m_rgPublishedFileId[ k_unEnumeratePublishedFilesMaxResults ]; + float m_rgScore[ k_unEnumeratePublishedFilesMaxResults ]; + AppId_t m_nAppId; + uint32 m_unStartIndex; +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of GetPublishedItemVoteDetails +//----------------------------------------------------------------------------- +struct RemoteStorageGetPublishedItemVoteDetailsResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 20 }; + EResult m_eResult; + PublishedFileId_t m_unPublishedFileId; + int32 m_nVotesFor; + int32 m_nVotesAgainst; + int32 m_nReports; + float m_fScore; +}; + + +//----------------------------------------------------------------------------- +// Purpose: User subscribed to a file for the app (from within the app or on the web) +//----------------------------------------------------------------------------- +struct RemoteStoragePublishedFileSubscribed_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 21 }; + PublishedFileId_t m_nPublishedFileId; // The published file id + AppId_t m_nAppID; // ID of the app that will consume this file. +}; + +//----------------------------------------------------------------------------- +// Purpose: User unsubscribed from a file for the app (from within the app or on the web) +//----------------------------------------------------------------------------- +struct RemoteStoragePublishedFileUnsubscribed_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 22 }; + PublishedFileId_t m_nPublishedFileId; // The published file id + AppId_t m_nAppID; // ID of the app that will consume this file. +}; + + +//----------------------------------------------------------------------------- +// Purpose: Published file that a user owns was deleted (from within the app or the web) +//----------------------------------------------------------------------------- +struct RemoteStoragePublishedFileDeleted_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 23 }; + PublishedFileId_t m_nPublishedFileId; // The published file id + AppId_t m_nAppID; // ID of the app that will consume this file. +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to UpdateUserPublishedItemVote() +//----------------------------------------------------------------------------- +struct RemoteStorageUpdateUserPublishedItemVoteResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 24 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; // The published file id +}; + + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to GetUserPublishedItemVoteDetails() +//----------------------------------------------------------------------------- +struct RemoteStorageUserVoteDetails_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 25 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; // The published file id + EWorkshopVote m_eVote; // what the user voted +}; + +struct RemoteStorageEnumerateUserSharedWorkshopFilesResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 26 }; + EResult m_eResult; // The result of the operation. + int32 m_nResultsReturned; + int32 m_nTotalResultCount; + PublishedFileId_t m_rgPublishedFileId[ k_unEnumeratePublishedFilesMaxResults ]; +}; + +struct RemoteStorageSetUserPublishedFileActionResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 27 }; + EResult m_eResult; // The result of the operation. + PublishedFileId_t m_nPublishedFileId; // The published file id + EWorkshopFileAction m_eAction; // the action that was attempted +}; + +struct RemoteStorageEnumeratePublishedFilesByUserActionResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 28 }; + EResult m_eResult; // The result of the operation. + EWorkshopFileAction m_eAction; // the action that was filtered on + int32 m_nResultsReturned; + int32 m_nTotalResultCount; + PublishedFileId_t m_rgPublishedFileId[ k_unEnumeratePublishedFilesMaxResults ]; + uint32 m_rgRTimeUpdated[ k_unEnumeratePublishedFilesMaxResults ]; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Called periodically while a PublishWorkshopFile is in progress +//----------------------------------------------------------------------------- +struct RemoteStoragePublishFileProgress_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 29 }; + double m_dPercentFile; + bool m_bPreview; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Called when the content for a published file is updated +//----------------------------------------------------------------------------- +struct RemoteStoragePublishedFileUpdated_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 30 }; + PublishedFileId_t m_nPublishedFileId; // The published file id + AppId_t m_nAppID; // ID of the app that will consume this file. + uint64 m_ulUnused; // not used anymore +}; + +//----------------------------------------------------------------------------- +// Purpose: Called when a FileWriteAsync completes +//----------------------------------------------------------------------------- +struct RemoteStorageFileWriteAsyncComplete_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 31 }; + EResult m_eResult; // result +}; + +//----------------------------------------------------------------------------- +// Purpose: Called when a FileReadAsync completes +//----------------------------------------------------------------------------- +struct RemoteStorageFileReadAsyncComplete_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 32 }; + SteamAPICall_t m_hFileReadAsync; // call handle of the async read which was made + EResult m_eResult; // result + uint32 m_nOffset; // offset in the file this read was at + uint32 m_cubRead; // amount read - will the <= the amount requested +}; + +#pragma pack( pop ) + + +#endif // ISTEAMREMOTESTORAGE_H diff --git a/dep/steam_api/isteamscreenshots.h b/dep/steam_api/isteamscreenshots.h new file mode 100644 index 0000000..6095705 --- /dev/null +++ b/dep/steam_api/isteamscreenshots.h @@ -0,0 +1,116 @@ +//====== Copyright � 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: public interface to user remote file storage in Steam +// +//============================================================================= + +#ifndef ISTEAMSCREENSHOTS_H +#define ISTEAMSCREENSHOTS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +const uint32 k_nScreenshotMaxTaggedUsers = 32; +const uint32 k_nScreenshotMaxTaggedPublishedFiles = 32; +const int k_cubUFSTagTypeMax = 255; +const int k_cubUFSTagValueMax = 255; + +// Required with of a thumbnail provided to AddScreenshotToLibrary. If you do not provide a thumbnail +// one will be generated. +const int k_ScreenshotThumbWidth = 200; + +// Handle is valid for the lifetime of your process and no longer +typedef uint32 ScreenshotHandle; +#define INVALID_SCREENSHOT_HANDLE 0 + +enum EVRScreenshotType +{ + k_EVRScreenshotType_None = 0, + k_EVRScreenshotType_Mono = 1, + k_EVRScreenshotType_Stereo = 2, + k_EVRScreenshotType_MonoCubemap = 3, + k_EVRScreenshotType_MonoPanorama = 4, + k_EVRScreenshotType_StereoPanorama = 5 +}; + +//----------------------------------------------------------------------------- +// Purpose: Functions for adding screenshots to the user's screenshot library +//----------------------------------------------------------------------------- +class ISteamScreenshots +{ +public: + // Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format. + // The return value is a handle that is valid for the duration of the game process and can be used to apply tags. + virtual ScreenshotHandle WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight ) = 0; + + // Adds a screenshot to the user's screenshot library from disk. If a thumbnail is provided, it must be 200 pixels wide and the same aspect ratio + // as the screenshot, otherwise a thumbnail will be generated if the user uploads the screenshot. The screenshots must be in either JPEG or TGA format. + // The return value is a handle that is valid for the duration of the game process and can be used to apply tags. + // JPEG, TGA, and PNG formats are supported. + virtual ScreenshotHandle AddScreenshotToLibrary( const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight ) = 0; + + // Causes the Steam overlay to take a screenshot. If screenshots are being hooked by the game then a ScreenshotRequested_t callback is sent back to the game instead. + virtual void TriggerScreenshot() = 0; + + // Toggles whether the overlay handles screenshots when the user presses the screenshot hotkey, or the game handles them. If the game is hooking screenshots, + // then the ScreenshotRequested_t callback will be sent if the user presses the hotkey, and the game is expected to call WriteScreenshot or AddScreenshotToLibrary + // in response. + virtual void HookScreenshots( bool bHook ) = 0; + + // Sets metadata about a screenshot's location (for example, the name of the map) + virtual bool SetLocation( ScreenshotHandle hScreenshot, const char *pchLocation ) = 0; + + // Tags a user as being visible in the screenshot + virtual bool TagUser( ScreenshotHandle hScreenshot, CSteamID steamID ) = 0; + + // Tags a published file as being visible in the screenshot + virtual bool TagPublishedFile( ScreenshotHandle hScreenshot, PublishedFileId_t unPublishedFileID ) = 0; + + // Returns true if the app has hooked the screenshot + virtual bool IsScreenshotsHooked() = 0; + + // Adds a VR screenshot to the user's screenshot library from disk in the supported type. + // pchFilename should be the normal 2D image used in the library view + // pchVRFilename should contain the image that matches the correct type + // The return value is a handle that is valid for the duration of the game process and can be used to apply tags. + // JPEG, TGA, and PNG formats are supported. + virtual ScreenshotHandle AddVRScreenshotToLibrary( EVRScreenshotType eType, const char *pchFilename, const char *pchVRFilename ) = 0; +}; + +#define STEAMSCREENSHOTS_INTERFACE_VERSION "STEAMSCREENSHOTS_INTERFACE_VERSION003" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif +//----------------------------------------------------------------------------- +// Purpose: Screenshot successfully written or otherwise added to the library +// and can now be tagged +//----------------------------------------------------------------------------- +struct ScreenshotReady_t +{ + enum { k_iCallback = k_iSteamScreenshotsCallbacks + 1 }; + ScreenshotHandle m_hLocal; + EResult m_eResult; +}; + +//----------------------------------------------------------------------------- +// Purpose: Screenshot has been requested by the user. Only sent if +// HookScreenshots() has been called, in which case Steam will not take +// the screenshot itself. +//----------------------------------------------------------------------------- +struct ScreenshotRequested_t +{ + enum { k_iCallback = k_iSteamScreenshotsCallbacks + 2 }; +}; + +#pragma pack( pop ) + +#endif // ISTEAMSCREENSHOTS_H + diff --git a/dep/steam_api/isteamugc.h b/dep/steam_api/isteamugc.h new file mode 100644 index 0000000..3d973f4 --- /dev/null +++ b/dep/steam_api/isteamugc.h @@ -0,0 +1,484 @@ +//====== Copyright 1996-2013, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to steam ugc +// +//============================================================================= + +#ifndef ISTEAMUGC_H +#define ISTEAMUGC_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + + +typedef uint64 UGCQueryHandle_t; +typedef uint64 UGCUpdateHandle_t; + + +const UGCQueryHandle_t k_UGCQueryHandleInvalid = 0xffffffffffffffffull; +const UGCUpdateHandle_t k_UGCUpdateHandleInvalid = 0xffffffffffffffffull; + + +// Matching UGC types for queries +enum EUGCMatchingUGCType +{ + k_EUGCMatchingUGCType_Items = 0, // both mtx items and ready-to-use items + k_EUGCMatchingUGCType_Items_Mtx = 1, + k_EUGCMatchingUGCType_Items_ReadyToUse = 2, + k_EUGCMatchingUGCType_Collections = 3, + k_EUGCMatchingUGCType_Artwork = 4, + k_EUGCMatchingUGCType_Videos = 5, + k_EUGCMatchingUGCType_Screenshots = 6, + k_EUGCMatchingUGCType_AllGuides = 7, // both web guides and integrated guides + k_EUGCMatchingUGCType_WebGuides = 8, + k_EUGCMatchingUGCType_IntegratedGuides = 9, + k_EUGCMatchingUGCType_UsableInGame = 10, // ready-to-use items and integrated guides + k_EUGCMatchingUGCType_ControllerBindings = 11, + k_EUGCMatchingUGCType_GameManagedItems = 12, // game managed items (not managed by users) + k_EUGCMatchingUGCType_All = ~0, // return everything +}; + +// Different lists of published UGC for a user. +// If the current logged in user is different than the specified user, then some options may not be allowed. +enum EUserUGCList +{ + k_EUserUGCList_Published, + k_EUserUGCList_VotedOn, + k_EUserUGCList_VotedUp, + k_EUserUGCList_VotedDown, + k_EUserUGCList_WillVoteLater, + k_EUserUGCList_Favorited, + k_EUserUGCList_Subscribed, + k_EUserUGCList_UsedOrPlayed, + k_EUserUGCList_Followed, +}; + +// Sort order for user published UGC lists (defaults to creation order descending) +enum EUserUGCListSortOrder +{ + k_EUserUGCListSortOrder_CreationOrderDesc, + k_EUserUGCListSortOrder_CreationOrderAsc, + k_EUserUGCListSortOrder_TitleAsc, + k_EUserUGCListSortOrder_LastUpdatedDesc, + k_EUserUGCListSortOrder_SubscriptionDateDesc, + k_EUserUGCListSortOrder_VoteScoreDesc, + k_EUserUGCListSortOrder_ForModeration, +}; + +// Combination of sorting and filtering for queries across all UGC +enum EUGCQuery +{ + k_EUGCQuery_RankedByVote = 0, + k_EUGCQuery_RankedByPublicationDate = 1, + k_EUGCQuery_AcceptedForGameRankedByAcceptanceDate = 2, + k_EUGCQuery_RankedByTrend = 3, + k_EUGCQuery_FavoritedByFriendsRankedByPublicationDate = 4, + k_EUGCQuery_CreatedByFriendsRankedByPublicationDate = 5, + k_EUGCQuery_RankedByNumTimesReported = 6, + k_EUGCQuery_CreatedByFollowedUsersRankedByPublicationDate = 7, + k_EUGCQuery_NotYetRated = 8, + k_EUGCQuery_RankedByTotalVotesAsc = 9, + k_EUGCQuery_RankedByVotesUp = 10, + k_EUGCQuery_RankedByTextSearch = 11, + k_EUGCQuery_RankedByTotalUniqueSubscriptions = 12, + k_EUGCQuery_RankedByPlaytimeTrend = 13, + k_EUGCQuery_RankedByTotalPlaytime = 14, + k_EUGCQuery_RankedByAveragePlaytimeTrend = 15, + k_EUGCQuery_RankedByLifetimeAveragePlaytime = 16, + k_EUGCQuery_RankedByPlaytimeSessionsTrend = 17, + k_EUGCQuery_RankedByLifetimePlaytimeSessions = 18, +}; + +enum EItemUpdateStatus +{ + k_EItemUpdateStatusInvalid = 0, // The item update handle was invalid, job might be finished, listen too SubmitItemUpdateResult_t + k_EItemUpdateStatusPreparingConfig = 1, // The item update is processing configuration data + k_EItemUpdateStatusPreparingContent = 2, // The item update is reading and processing content files + k_EItemUpdateStatusUploadingContent = 3, // The item update is uploading content changes to Steam + k_EItemUpdateStatusUploadingPreviewFile = 4, // The item update is uploading new preview file image + k_EItemUpdateStatusCommittingChanges = 5 // The item update is committing all changes +}; + +enum EItemState +{ + k_EItemStateNone = 0, // item not tracked on client + k_EItemStateSubscribed = 1, // current user is subscribed to this item. Not just cached. + k_EItemStateLegacyItem = 2, // item was created with ISteamRemoteStorage + k_EItemStateInstalled = 4, // item is installed and usable (but maybe out of date) + k_EItemStateNeedsUpdate = 8, // items needs an update. Either because it's not installed yet or creator updated content + k_EItemStateDownloading = 16, // item update is currently downloading + k_EItemStateDownloadPending = 32, // DownloadItem() was called for this item, content isn't available until DownloadItemResult_t is fired +}; + +enum EItemStatistic +{ + k_EItemStatistic_NumSubscriptions = 0, + k_EItemStatistic_NumFavorites = 1, + k_EItemStatistic_NumFollowers = 2, + k_EItemStatistic_NumUniqueSubscriptions = 3, + k_EItemStatistic_NumUniqueFavorites = 4, + k_EItemStatistic_NumUniqueFollowers = 5, + k_EItemStatistic_NumUniqueWebsiteViews = 6, + k_EItemStatistic_ReportScore = 7, + k_EItemStatistic_NumSecondsPlayed = 8, + k_EItemStatistic_NumPlaytimeSessions = 9, + k_EItemStatistic_NumComments = 10, + k_EItemStatistic_NumSecondsPlayedDuringTimePeriod = 11, + k_EItemStatistic_NumPlaytimeSessionsDuringTimePeriod = 12, +}; + +enum EItemPreviewType +{ + k_EItemPreviewType_Image = 0, // standard image file expected (e.g. jpg, png, gif, etc.) + k_EItemPreviewType_YouTubeVideo = 1, // video id is stored + k_EItemPreviewType_Sketchfab = 2, // model id is stored + k_EItemPreviewType_EnvironmentMap_HorizontalCross = 3, // standard image file expected - cube map in the layout + // +---+---+-------+ + // | |Up | | + // +---+---+---+---+ + // | L | F | R | B | + // +---+---+---+---+ + // | |Dn | | + // +---+---+---+---+ + k_EItemPreviewType_EnvironmentMap_LatLong = 4, // standard image file expected + k_EItemPreviewType_ReservedMax = 255, // you can specify your own types above this value +}; + +const uint32 kNumUGCResultsPerPage = 50; +const uint32 k_cchDeveloperMetadataMax = 5000; + +// Details for a single published file/UGC +struct SteamUGCDetails_t +{ + PublishedFileId_t m_nPublishedFileId; + EResult m_eResult; // The result of the operation. + EWorkshopFileType m_eFileType; // Type of the file + AppId_t m_nCreatorAppID; // ID of the app that created this file. + AppId_t m_nConsumerAppID; // ID of the app that will consume this file. + char m_rgchTitle[k_cchPublishedDocumentTitleMax]; // title of document + char m_rgchDescription[k_cchPublishedDocumentDescriptionMax]; // description of document + uint64 m_ulSteamIDOwner; // Steam ID of the user who created this content. + uint32 m_rtimeCreated; // time when the published file was created + uint32 m_rtimeUpdated; // time when the published file was last updated + uint32 m_rtimeAddedToUserList; // time when the user added the published file to their list (not always applicable) + ERemoteStoragePublishedFileVisibility m_eVisibility; // visibility + bool m_bBanned; // whether the file was banned + bool m_bAcceptedForUse; // developer has specifically flagged this item as accepted in the Workshop + bool m_bTagsTruncated; // whether the list of tags was too long to be returned in the provided buffer + char m_rgchTags[k_cchTagListMax]; // comma separated list of all tags associated with this file + // file/url information + UGCHandle_t m_hFile; // The handle of the primary file + UGCHandle_t m_hPreviewFile; // The handle of the preview file + char m_pchFileName[k_cchFilenameMax]; // The cloud filename of the primary file + int32 m_nFileSize; // Size of the primary file + int32 m_nPreviewFileSize; // Size of the preview file + char m_rgchURL[k_cchPublishedFileURLMax]; // URL (for a video or a website) + // voting information + uint32 m_unVotesUp; // number of votes up + uint32 m_unVotesDown; // number of votes down + float m_flScore; // calculated score + // collection details + uint32 m_unNumChildren; +}; + +//----------------------------------------------------------------------------- +// Purpose: Steam UGC support API +//----------------------------------------------------------------------------- +class ISteamUGC +{ +public: + + // Query UGC associated with a user. Creator app id or consumer app id must be valid and be set to the current running app. unPage should start at 1. + virtual UGCQueryHandle_t CreateQueryUserUGCRequest( AccountID_t unAccountID, EUserUGCList eListType, EUGCMatchingUGCType eMatchingUGCType, EUserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage ) = 0; + + // Query for all matching UGC. Creator app id or consumer app id must be valid and be set to the current running app. unPage should start at 1. + virtual UGCQueryHandle_t CreateQueryAllUGCRequest( EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage ) = 0; + + // Query for the details of the given published file ids (the RequestUGCDetails call is deprecated and replaced with this) + virtual UGCQueryHandle_t CreateQueryUGCDetailsRequest( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs ) = 0; + + // Send the query to Steam + CALL_RESULT( SteamUGCQueryCompleted_t ) + virtual SteamAPICall_t SendQueryUGCRequest( UGCQueryHandle_t handle ) = 0; + + // Retrieve an individual result after receiving the callback for querying UGC + virtual bool GetQueryUGCResult( UGCQueryHandle_t handle, uint32 index, SteamUGCDetails_t *pDetails ) = 0; + virtual bool GetQueryUGCPreviewURL( UGCQueryHandle_t handle, uint32 index, OUT_STRING_COUNT(cchURLSize) char *pchURL, uint32 cchURLSize ) = 0; + virtual bool GetQueryUGCMetadata( UGCQueryHandle_t handle, uint32 index, OUT_STRING_COUNT(cchMetadatasize) char *pchMetadata, uint32 cchMetadatasize ) = 0; + virtual bool GetQueryUGCChildren( UGCQueryHandle_t handle, uint32 index, PublishedFileId_t* pvecPublishedFileID, uint32 cMaxEntries ) = 0; + virtual bool GetQueryUGCStatistic( UGCQueryHandle_t handle, uint32 index, EItemStatistic eStatType, uint64 *pStatValue ) = 0; + virtual uint32 GetQueryUGCNumAdditionalPreviews( UGCQueryHandle_t handle, uint32 index ) = 0; + virtual bool GetQueryUGCAdditionalPreview( UGCQueryHandle_t handle, uint32 index, uint32 previewIndex, OUT_STRING_COUNT(cchURLSize) char *pchURLOrVideoID, uint32 cchURLSize, OUT_STRING_COUNT(cchURLSize) char *pchOriginalFileName, uint32 cchOriginalFileNameSize, EItemPreviewType *pPreviewType ) = 0; + virtual uint32 GetQueryUGCNumKeyValueTags( UGCQueryHandle_t handle, uint32 index ) = 0; + virtual bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint32 index, uint32 keyValueTagIndex, OUT_STRING_COUNT(cchKeySize) char *pchKey, uint32 cchKeySize, OUT_STRING_COUNT(cchValueSize) char *pchValue, uint32 cchValueSize ) = 0; + + // Release the request to free up memory, after retrieving results + virtual bool ReleaseQueryUGCRequest( UGCQueryHandle_t handle ) = 0; + + // Options to set for querying UGC + virtual bool AddRequiredTag( UGCQueryHandle_t handle, const char *pTagName ) = 0; + virtual bool AddExcludedTag( UGCQueryHandle_t handle, const char *pTagName ) = 0; + virtual bool SetReturnOnlyIDs( UGCQueryHandle_t handle, bool bReturnOnlyIDs ) = 0; + virtual bool SetReturnKeyValueTags( UGCQueryHandle_t handle, bool bReturnKeyValueTags ) = 0; + virtual bool SetReturnLongDescription( UGCQueryHandle_t handle, bool bReturnLongDescription ) = 0; + virtual bool SetReturnMetadata( UGCQueryHandle_t handle, bool bReturnMetadata ) = 0; + virtual bool SetReturnChildren( UGCQueryHandle_t handle, bool bReturnChildren ) = 0; + virtual bool SetReturnAdditionalPreviews( UGCQueryHandle_t handle, bool bReturnAdditionalPreviews ) = 0; + virtual bool SetReturnTotalOnly( UGCQueryHandle_t handle, bool bReturnTotalOnly ) = 0; + virtual bool SetReturnPlaytimeStats( UGCQueryHandle_t handle, uint32 unDays ) = 0; + virtual bool SetLanguage( UGCQueryHandle_t handle, const char *pchLanguage ) = 0; + virtual bool SetAllowCachedResponse( UGCQueryHandle_t handle, uint32 unMaxAgeSeconds ) = 0; + + // Options only for querying user UGC + virtual bool SetCloudFileNameFilter( UGCQueryHandle_t handle, const char *pMatchCloudFileName ) = 0; + + // Options only for querying all UGC + virtual bool SetMatchAnyTag( UGCQueryHandle_t handle, bool bMatchAnyTag ) = 0; + virtual bool SetSearchText( UGCQueryHandle_t handle, const char *pSearchText ) = 0; + virtual bool SetRankedByTrendDays( UGCQueryHandle_t handle, uint32 unDays ) = 0; + virtual bool AddRequiredKeyValueTag( UGCQueryHandle_t handle, const char *pKey, const char *pValue ) = 0; + + // DEPRECATED - Use CreateQueryUGCDetailsRequest call above instead! + virtual SteamAPICall_t RequestUGCDetails( PublishedFileId_t nPublishedFileID, uint32 unMaxAgeSeconds ) = 0; + + // Steam Workshop Creator API + CALL_RESULT( CreateItemResult_t ) + virtual SteamAPICall_t CreateItem( AppId_t nConsumerAppId, EWorkshopFileType eFileType ) = 0; // create new item for this app with no content attached yet + + virtual UGCUpdateHandle_t StartItemUpdate( AppId_t nConsumerAppId, PublishedFileId_t nPublishedFileID ) = 0; // start an UGC item update. Set changed properties before commiting update with CommitItemUpdate() + + virtual bool SetItemTitle( UGCUpdateHandle_t handle, const char *pchTitle ) = 0; // change the title of an UGC item + virtual bool SetItemDescription( UGCUpdateHandle_t handle, const char *pchDescription ) = 0; // change the description of an UGC item + virtual bool SetItemUpdateLanguage( UGCUpdateHandle_t handle, const char *pchLanguage ) = 0; // specify the language of the title or description that will be set + virtual bool SetItemMetadata( UGCUpdateHandle_t handle, const char *pchMetaData ) = 0; // change the metadata of an UGC item (max = k_cchDeveloperMetadataMax) + virtual bool SetItemVisibility( UGCUpdateHandle_t handle, ERemoteStoragePublishedFileVisibility eVisibility ) = 0; // change the visibility of an UGC item + virtual bool SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags ) = 0; // change the tags of an UGC item + virtual bool SetItemContent( UGCUpdateHandle_t handle, const char *pszContentFolder ) = 0; // update item content from this local folder + virtual bool SetItemPreview( UGCUpdateHandle_t handle, const char *pszPreviewFile ) = 0; // change preview image file for this item. pszPreviewFile points to local image file, which must be under 1MB in size + virtual bool RemoveItemKeyValueTags( UGCUpdateHandle_t handle, const char *pchKey ) = 0; // remove any existing key-value tags with the specified key + virtual bool AddItemKeyValueTag( UGCUpdateHandle_t handle, const char *pchKey, const char *pchValue ) = 0; // add new key-value tags for the item. Note that there can be multiple values for a tag. + virtual bool AddItemPreviewFile( UGCUpdateHandle_t handle, const char *pszPreviewFile, EItemPreviewType type ) = 0; // add preview file for this item. pszPreviewFile points to local file, which must be under 1MB in size + virtual bool AddItemPreviewVideo( UGCUpdateHandle_t handle, const char *pszVideoID ) = 0; // add preview video for this item + virtual bool UpdateItemPreviewFile( UGCUpdateHandle_t handle, uint32 index, const char *pszPreviewFile ) = 0; // updates an existing preview file for this item. pszPreviewFile points to local file, which must be under 1MB in size + virtual bool UpdateItemPreviewVideo( UGCUpdateHandle_t handle, uint32 index, const char *pszVideoID ) = 0; // updates an existing preview video for this item + virtual bool RemoveItemPreview( UGCUpdateHandle_t handle, uint32 index ) = 0; // remove a preview by index starting at 0 (previews are sorted) + + CALL_RESULT( SubmitItemUpdateResult_t ) + virtual SteamAPICall_t SubmitItemUpdate( UGCUpdateHandle_t handle, const char *pchChangeNote ) = 0; // commit update process started with StartItemUpdate() + virtual EItemUpdateStatus GetItemUpdateProgress( UGCUpdateHandle_t handle, uint64 *punBytesProcessed, uint64* punBytesTotal ) = 0; + + // Steam Workshop Consumer API + CALL_RESULT( SetUserItemVoteResult_t ) + virtual SteamAPICall_t SetUserItemVote( PublishedFileId_t nPublishedFileID, bool bVoteUp ) = 0; + CALL_RESULT( GetUserItemVoteResult_t ) + virtual SteamAPICall_t GetUserItemVote( PublishedFileId_t nPublishedFileID ) = 0; + CALL_RESULT( UserFavoriteItemsListChanged_t ) + virtual SteamAPICall_t AddItemToFavorites( AppId_t nAppId, PublishedFileId_t nPublishedFileID ) = 0; + CALL_RESULT( UserFavoriteItemsListChanged_t ) + virtual SteamAPICall_t RemoveItemFromFavorites( AppId_t nAppId, PublishedFileId_t nPublishedFileID ) = 0; + CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t ) + virtual SteamAPICall_t SubscribeItem( PublishedFileId_t nPublishedFileID ) = 0; // subscribe to this item, will be installed ASAP + CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t ) + virtual SteamAPICall_t UnsubscribeItem( PublishedFileId_t nPublishedFileID ) = 0; // unsubscribe from this item, will be uninstalled after game quits + virtual uint32 GetNumSubscribedItems() = 0; // number of subscribed items + virtual uint32 GetSubscribedItems( PublishedFileId_t* pvecPublishedFileID, uint32 cMaxEntries ) = 0; // all subscribed item PublishFileIDs + + // get EItemState flags about item on this client + virtual uint32 GetItemState( PublishedFileId_t nPublishedFileID ) = 0; + + // get info about currently installed content on disc for items that have k_EItemStateInstalled set + // if k_EItemStateLegacyItem is set, pchFolder contains the path to the legacy file itself (not a folder) + virtual bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, OUT_STRING_COUNT( cchFolderSize ) char *pchFolder, uint32 cchFolderSize, uint32 *punTimeStamp ) = 0; + + // get info about pending update for items that have k_EItemStateNeedsUpdate set. punBytesTotal will be valid after download started once + virtual bool GetItemDownloadInfo( PublishedFileId_t nPublishedFileID, uint64 *punBytesDownloaded, uint64 *punBytesTotal ) = 0; + + // download new or update already installed item. If function returns true, wait for DownloadItemResult_t. If the item is already installed, + // then files on disk should not be used until callback received. If item is not subscribed to, it will be cached for some time. + // If bHighPriority is set, any other item download will be suspended and this item downloaded ASAP. + virtual bool DownloadItem( PublishedFileId_t nPublishedFileID, bool bHighPriority ) = 0; + + // game servers can set a specific workshop folder before issuing any UGC commands. + // This is helpful if you want to support multiple game servers running out of the same install folder + virtual bool BInitWorkshopForGameServer( DepotId_t unWorkshopDepotID, const char *pszFolder ) = 0; + + // SuspendDownloads( true ) will suspend all workshop downloads until SuspendDownloads( false ) is called or the game ends + virtual void SuspendDownloads( bool bSuspend ) = 0; + + // usage tracking + CALL_RESULT( StartPlaytimeTrackingResult_t ) + virtual SteamAPICall_t StartPlaytimeTracking( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs ) = 0; + CALL_RESULT( StopPlaytimeTrackingResult_t ) + virtual SteamAPICall_t StopPlaytimeTracking( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs ) = 0; + CALL_RESULT( StopPlaytimeTrackingResult_t ) + virtual SteamAPICall_t StopPlaytimeTrackingForAllItems() = 0; + + // parent-child relationship or dependency management + CALL_RESULT( AddUGCDependencyResult_t ) + virtual SteamAPICall_t AddDependency( PublishedFileId_t nParentPublishedFileID, PublishedFileId_t nChildPublishedFileID ) = 0; + CALL_RESULT( RemoveUGCDependencyResult_t ) + virtual SteamAPICall_t RemoveDependency( PublishedFileId_t nParentPublishedFileID, PublishedFileId_t nChildPublishedFileID ) = 0; +}; + +#define STEAMUGC_INTERFACE_VERSION "STEAMUGC_INTERFACE_VERSION010" + +//----------------------------------------------------------------------------- +// Purpose: Callback for querying UGC +//----------------------------------------------------------------------------- +struct SteamUGCQueryCompleted_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 1 }; + UGCQueryHandle_t m_handle; + EResult m_eResult; + uint32 m_unNumResultsReturned; + uint32 m_unTotalMatchingResults; + bool m_bCachedData; // indicates whether this data was retrieved from the local on-disk cache +}; + + +//----------------------------------------------------------------------------- +// Purpose: Callback for requesting details on one piece of UGC +//----------------------------------------------------------------------------- +struct SteamUGCRequestUGCDetailsResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 2 }; + SteamUGCDetails_t m_details; + bool m_bCachedData; // indicates whether this data was retrieved from the local on-disk cache +}; + + +//----------------------------------------------------------------------------- +// Purpose: result for ISteamUGC::CreateItem() +//----------------------------------------------------------------------------- +struct CreateItemResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 3 }; + EResult m_eResult; + PublishedFileId_t m_nPublishedFileId; // new item got this UGC PublishFileID + bool m_bUserNeedsToAcceptWorkshopLegalAgreement; +}; + + +//----------------------------------------------------------------------------- +// Purpose: result for ISteamUGC::SubmitItemUpdate() +//----------------------------------------------------------------------------- +struct SubmitItemUpdateResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 4 }; + EResult m_eResult; + bool m_bUserNeedsToAcceptWorkshopLegalAgreement; +}; + + +//----------------------------------------------------------------------------- +// Purpose: a Workshop item has been installed or updated +//----------------------------------------------------------------------------- +struct ItemInstalled_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 5 }; + AppId_t m_unAppID; + PublishedFileId_t m_nPublishedFileId; +}; + + +//----------------------------------------------------------------------------- +// Purpose: result of DownloadItem(), existing item files can be accessed again +//----------------------------------------------------------------------------- +struct DownloadItemResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 6 }; + AppId_t m_unAppID; + PublishedFileId_t m_nPublishedFileId; + EResult m_eResult; +}; + +//----------------------------------------------------------------------------- +// Purpose: result of AddItemToFavorites() or RemoveItemFromFavorites() +//----------------------------------------------------------------------------- +struct UserFavoriteItemsListChanged_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 7 }; + PublishedFileId_t m_nPublishedFileId; + EResult m_eResult; + bool m_bWasAddRequest; +}; + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to SetUserItemVote() +//----------------------------------------------------------------------------- +struct SetUserItemVoteResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 8 }; + PublishedFileId_t m_nPublishedFileId; + EResult m_eResult; + bool m_bVoteUp; +}; + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to GetUserItemVote() +//----------------------------------------------------------------------------- +struct GetUserItemVoteResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 9 }; + PublishedFileId_t m_nPublishedFileId; + EResult m_eResult; + bool m_bVotedUp; + bool m_bVotedDown; + bool m_bVoteSkipped; +}; + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to StartPlaytimeTracking() +//----------------------------------------------------------------------------- +struct StartPlaytimeTrackingResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 10 }; + EResult m_eResult; +}; + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to StopPlaytimeTracking() +//----------------------------------------------------------------------------- +struct StopPlaytimeTrackingResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 11 }; + EResult m_eResult; +}; + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to AddDependency +//----------------------------------------------------------------------------- +struct AddUGCDependencyResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 12 }; + EResult m_eResult; + PublishedFileId_t m_nPublishedFileId; + PublishedFileId_t m_nChildPublishedFileId; +}; + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to RemoveDependency +//----------------------------------------------------------------------------- +struct RemoveUGCDependencyResult_t +{ + enum { k_iCallback = k_iClientUGCCallbacks + 13 }; + EResult m_eResult; + PublishedFileId_t m_nPublishedFileId; + PublishedFileId_t m_nChildPublishedFileId; +}; + + +#pragma pack( pop ) + +#endif // ISTEAMUGC_H diff --git a/dep/steam_api/isteamunifiedmessages.h b/dep/steam_api/isteamunifiedmessages.h new file mode 100644 index 0000000..684f4e8 --- /dev/null +++ b/dep/steam_api/isteamunifiedmessages.h @@ -0,0 +1,63 @@ +//====== Copyright � 1996-2007, Valve Corporation, All rights reserved. ======= +// +// Purpose: Interface to unified messages client +// +// You should not need to use this interface except if your product is using a language other than C++. +// Contact your Steam Tech contact for more details. +// +//============================================================================= + +#ifndef ISTEAMUNIFIEDMESSAGES_H +#define ISTEAMUNIFIEDMESSAGES_H +#ifdef _WIN32 +#pragma once +#endif + +typedef uint64 ClientUnifiedMessageHandle; + +class ISteamUnifiedMessages +{ +public: + static const ClientUnifiedMessageHandle k_InvalidUnifiedMessageHandle = 0; + + // Sends a service method (in binary serialized form) using the Steam Client. + // Returns a unified message handle (k_InvalidUnifiedMessageHandle if could not send the message). + virtual ClientUnifiedMessageHandle SendMethod( const char *pchServiceMethod, const void *pRequestBuffer, uint32 unRequestBufferSize, uint64 unContext ) = 0; + + // Gets the size of the response and the EResult. Returns false if the response is not ready yet. + virtual bool GetMethodResponseInfo( ClientUnifiedMessageHandle hHandle, uint32 *punResponseSize, EResult *peResult ) = 0; + + // Gets a response in binary serialized form (and optionally release the corresponding allocated memory). + virtual bool GetMethodResponseData( ClientUnifiedMessageHandle hHandle, void *pResponseBuffer, uint32 unResponseBufferSize, bool bAutoRelease ) = 0; + + // Releases the message and its corresponding allocated memory. + virtual bool ReleaseMethod( ClientUnifiedMessageHandle hHandle ) = 0; + + // Sends a service notification (in binary serialized form) using the Steam Client. + // Returns true if the notification was sent successfully. + virtual bool SendNotification( const char *pchServiceNotification, const void *pNotificationBuffer, uint32 unNotificationBufferSize ) = 0; +}; + +#define STEAMUNIFIEDMESSAGES_INTERFACE_VERSION "STEAMUNIFIEDMESSAGES_INTERFACE_VERSION001" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +struct SteamUnifiedMessagesSendMethodResult_t +{ + enum { k_iCallback = k_iClientUnifiedMessagesCallbacks + 1 }; + ClientUnifiedMessageHandle m_hHandle; // The handle returned by SendMethod(). + uint64 m_unContext; // Context provided when calling SendMethod(). + EResult m_eResult; // The result of the method call. + uint32 m_unResponseSize; // The size of the response. +}; + +#pragma pack( pop ) + +#endif // ISTEAMUNIFIEDMESSAGES_H diff --git a/dep/steam_api/isteamuser.h b/dep/steam_api/isteamuser.h new file mode 100644 index 0000000..0ea2bb8 --- /dev/null +++ b/dep/steam_api/isteamuser.h @@ -0,0 +1,369 @@ +//====== Copyright (c) 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 +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif +struct CallbackMsg_t +{ + HSteamUser m_hSteamUser; + int m_iCallback; + uint8 *m_pubParam; + int m_cubParam; +}; +#pragma pack( pop ) + + +//----------------------------------------------------------------------------- +// 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 size of captured audio data that is available from GetVoice. + // Most applications will only use compressed data and should ignore the other + // parameters, which exist primarily for backwards compatibility. See comments + // below for further explanation of "uncompressed" data. + virtual EVoiceResult GetAvailableVoice( uint32 *pcbCompressed, uint32 *pcbUncompressed_Deprecated = 0, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated = 0 ) = 0; + + // --------------------------------------------------------------------------- + // NOTE: "uncompressed" audio is a deprecated feature and should not be used + // by most applications. It is raw single-channel 16-bit PCM wave data which + // may have been run through preprocessing filters and/or had silence removed, + // so the uncompressed audio could have a shorter duration than you expect. + // There may be no data at all during long periods of silence. Also, fetching + // uncompressed audio will cause GetVoice to discard any leftover compressed + // audio, so you must fetch both types at once. Finally, GetAvailableVoice is + // not precisely accurate when the uncompressed size is requested. So if you + // really need to use uncompressed audio, you should call GetVoice frequently + // with two very large (20kb+) output buffers instead of trying to allocate + // perfectly-sized buffers. But most applications should ignore all of these + // details and simply leave the "uncompressed" parameters as NULL/zero. + // --------------------------------------------------------------------------- + + // Read captured audio data from the microphone buffer. This should be called + // at least once per frame, and preferably every few milliseconds, to keep the + // microphone input delay as low as possible. Most applications will only use + // compressed data and should pass NULL/zero for the "uncompressed" parameters. + // Compressed data can be transmitted by your application and decoded into raw + // using the DecompressVoice function below. + virtual EVoiceResult GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed_Deprecated = false, void *pUncompressedDestBuffer_Deprecated = 0, uint32 cbUncompressedDestBufferSize_Deprecated = 0, uint32 *nUncompressBytesWritten_Deprecated = 0, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated = 0 ) = 0; + + // Decodes the compressed voice data returned by GetVoice. The output data is + // raw single-channel 16-bit PCM audio. The decoder supports any sample rate + // from 11025 to 48000; see GetVoiceOptimalSampleRate() below for details. + // If the output buffer is not large enough, then *nBytesWritten will be set + // to the required buffer size, and k_EVoiceResultBufferTooSmall is returned. + // It is suggested to start with a 20kb buffer and reallocate as necessary. + virtual EVoiceResult DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, uint32 nDesiredSampleRate ) = 0; + + // This returns the native sample rate of the Steam voice decompressor; using + // this sample rate for DecompressVoice will perform the least CPU processing. + // However, the final audio quality will depend on how well the audio device + // (and/or your application's audio output SDK) deals with lower sample rates. + // You may find that you get the best audio output quality when you ignore + // this function and use the native sample rate of your audio output device, + // which is usually 48000 or 44100. + virtual uint32 GetVoiceOptimalSampleRate() = 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; + + // returns true if this users looks like they are behind a NAT device. Only valid once the user has connected to steam + // (i.e a SteamServersConnected_t has been issued) and may not catch all forms of NAT. + virtual bool BIsBehindNAT() = 0; + + // set data to be replicated to friends so that they can join your game + // CSteamID steamIDGameServer - the steamID of the game server, received from the game server by the client + // uint32 unIPServer, uint16 usPortServer - the IP address of the game server + virtual void AdvertiseGame( CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer ) = 0; + + // Requests a ticket encrypted with an app specific shared key + // pDataToInclude, cbDataToInclude will be encrypted into the ticket + // ( This is asynchronous, you must wait for the ticket to be completed by the server ) + CALL_RESULT( EncryptedAppTicketResponse_t ) + virtual SteamAPICall_t RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToInclude ) = 0; + + // retrieve a finished ticket + virtual bool GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) = 0; + + // Trading Card badges data access + // if you only have one set of cards, the series will be 1 + // the user has can have two different badges for a series; the regular (max level 5) and the foil (max level 1) + virtual int GetGameBadgeLevel( int nSeries, bool bFoil ) = 0; + + // gets the Steam Level of the user, as shown on their profile + virtual int GetPlayerSteamLevel() = 0; + + // Requests a URL which authenticates an in-game browser for store check-out, + // and then redirects to the specified URL. As long as the in-game browser + // accepts and handles session cookies, Steam microtransaction checkout pages + // will automatically recognize the user instead of presenting a login page. + // The result of this API call will be a StoreAuthURLResponse_t callback. + // NOTE: The URL has a very short lifetime to prevent history-snooping attacks, + // so you should only call this API when you are about to launch the browser, + // or else immediately navigate to the result URL using a hidden browser window. + // NOTE 2: The resulting authorization cookie has an expiration time of one day, + // so it would be a good idea to request and visit a new auth URL every 12 hours. + CALL_RESULT( StoreAuthURLResponse_t ) + virtual SteamAPICall_t RequestStoreAuthURL( const char *pchRedirectURL ) = 0; + + // gets whether the users phone number is verified + virtual bool BIsPhoneVerified() = 0; + + // gets whether the user has two factor enabled on their account + virtual bool BIsTwoFactorEnabled() = 0; + + // gets whether the users phone number is identifying + virtual bool BIsPhoneIdentifying() = 0; + + // gets whether the users phone number is awaiting (re)verification + virtual bool BIsPhoneRequiringVerification() = 0; + +}; + +#define STEAMUSER_INTERFACE_VERSION "SteamUser019" + + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +//----------------------------------------------------------------------------- +// 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; + bool m_bStillRetrying; +}; + + +//----------------------------------------------------------------------------- +// 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; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Signaled whenever licenses change +//----------------------------------------------------------------------------- +struct LicensesUpdated_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 25 }; +}; + + +//----------------------------------------------------------------------------- +// callback for BeginAuthSession +//----------------------------------------------------------------------------- +struct ValidateAuthTicketResponse_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 43 }; + CSteamID m_SteamID; + EAuthSessionResponse m_eAuthSessionResponse; + CSteamID m_OwnerSteamID; // different from m_SteamID if borrowed +}; + + +//----------------------------------------------------------------------------- +// 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 +}; + + +//----------------------------------------------------------------------------- +// Purpose: Result from RequestEncryptedAppTicket +//----------------------------------------------------------------------------- +struct EncryptedAppTicketResponse_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 54 }; + + EResult m_eResult; +}; + +//----------------------------------------------------------------------------- +// callback for GetAuthSessionTicket +//----------------------------------------------------------------------------- +struct GetAuthSessionTicketResponse_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 63 }; + HAuthTicket m_hAuthTicket; + EResult m_eResult; +}; + + +//----------------------------------------------------------------------------- +// Purpose: sent to your game in response to a steam://gamewebcallback/ command +//----------------------------------------------------------------------------- +struct GameWebCallback_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 64 }; + char m_szURL[256]; +}; + +//----------------------------------------------------------------------------- +// Purpose: sent to your game in response to ISteamUser::RequestStoreAuthURL +//----------------------------------------------------------------------------- +struct StoreAuthURLResponse_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 65 }; + char m_szURL[512]; +}; + + + +#pragma pack( pop ) + +#endif // ISTEAMUSER_H diff --git a/dep/steam_api/isteamuserstats.h b/dep/steam_api/isteamuserstats.h new file mode 100644 index 0000000..29ae38b --- /dev/null +++ b/dep/steam_api/isteamuserstats.h @@ -0,0 +1,476 @@ +//====== 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" +#include "isteamremotestorage.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, + k_ELeaderboardDataRequestUsers = 3 +}; + +// 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() +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +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 + UGCHandle_t m_hUGC; // handle for UGC attached to the 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 + CALL_BACK( UserStatsReceived_t ) + 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 ISteamUtils::GetImageRGBA(), or 0 if none set. + // A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconFetched_t callback + // which will notify you when the bits are ready. If the callback still returns zero, then there is no image set for the + // specified achievement. + virtual int GetAchievementIcon( const char *pchName ) = 0; + + // Get general attributes for an achievement. Accepts the following keys: + // - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8) + // - "hidden" for retrieving if an achievement is hidden (returns "0" when not hidden, "1" when hidden) + 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; + + // Used for iterating achievements. In general games should not need these functions because they should have a + // list of existing achievements compiled into them + virtual uint32 GetNumAchievements() = 0; + // Get achievement name iAchievement in [0,GetNumAchievements) + virtual const char *GetAchievementName( uint32 iAchievement ) = 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 + CALL_RESULT( UserStatsReceived_t ) + 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 + CALL_RESULT(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 + CALL_RESULT( 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 + CALL_RESULT( LeaderboardScoresDownloaded_t ) + virtual SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd ) = 0; + // as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers + // if a user doesn't have a leaderboard entry, they won't be included in the result + // a max of 100 users can be downloaded at a time, with only one outstanding call at a time + METHOD_DESC(Downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers) + CALL_RESULT( LeaderboardScoresDownloaded_t ) + virtual SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLeaderboard, + ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID *prgUsers, int cUsers ) = 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 + CALL_RESULT( LeaderboardScoreUploaded_t ) + virtual SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount ) = 0; + + // Attaches a piece of user generated content the user's entry on a leaderboard. + // hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare(). + // This call is asynchronous, with the result returned in LeaderboardUGCSet_t. + CALL_RESULT( LeaderboardUGCSet_t ) + virtual SteamAPICall_t AttachLeaderboardUGC( SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC ) = 0; + + // Retrieves the number of players currently playing your game (online + offline) + // This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t + CALL_RESULT( NumberOfCurrentPlayers_t ) + virtual SteamAPICall_t GetNumberOfCurrentPlayers() = 0; + + // Requests that Steam fetch data on the percentage of players who have received each achievement + // for the game globally. + // This call is asynchronous, with the result returned in GlobalAchievementPercentagesReady_t. + CALL_RESULT( GlobalAchievementPercentagesReady_t ) + virtual SteamAPICall_t RequestGlobalAchievementPercentages() = 0; + + // Get the info on the most achieved achievement for the game, returns an iterator index you can use to fetch + // the next most achieved afterwards. Will return -1 if there is no data on achievement + // percentages (ie, you haven't called RequestGlobalAchievementPercentages and waited on the callback). + virtual int GetMostAchievedAchievementInfo( char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved ) = 0; + + // Get the info on the next most achieved achievement for the game. Call this after GetMostAchievedAchievementInfo or another + // GetNextMostAchievedAchievementInfo call passing the iterator from the previous call. Returns -1 after the last + // achievement has been iterated. + virtual int GetNextMostAchievedAchievementInfo( int iIteratorPrevious, char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved ) = 0; + + // Returns the percentage of users who have achieved the specified achievement. + virtual bool GetAchievementAchievedPercent( const char *pchName, float *pflPercent ) = 0; + + // Requests global stats data, which is available for stats marked as "aggregated". + // This call is asynchronous, with the results returned in GlobalStatsReceived_t. + // nHistoryDays specifies how many days of day-by-day history to retrieve in addition + // to the overall totals. The limit is 60. + CALL_RESULT( GlobalStatsReceived_t ) + virtual SteamAPICall_t RequestGlobalStats( int nHistoryDays ) = 0; + + // Gets the lifetime totals for an aggregated stat + virtual bool GetGlobalStat( const char *pchStatName, int64 *pData ) = 0; + virtual bool GetGlobalStat( const char *pchStatName, double *pData ) = 0; + + // Gets history for an aggregated stat. pData will be filled with daily values, starting with today. + // So when called, pData[0] will be today, pData[1] will be yesterday, and pData[2] will be two days ago, + // etc. cubData is the size in bytes of the pubData buffer. Returns the number of + // elements actually set. + virtual int32 GetGlobalStatHistory( const char *pchStatName, ARRAY_COUNT(cubData) int64 *pData, uint32 cubData ) = 0; + virtual int32 GetGlobalStatHistory( const char *pchStatName, ARRAY_COUNT(cubData) double *pData, uint32 cubData ) = 0; + +#ifdef _PS3 + // Call to kick off installation of the PS3 trophies. This call is asynchronous, and the results will be returned in a PS3TrophiesInstalled_t + // callback. + virtual bool InstallPS3Trophies() = 0; + + // Returns the amount of space required at boot to install trophies. This value can be used when comparing the amount of space needed + // by the game to the available space value passed to the game at boot. The value is set during InstallPS3Trophies(). + virtual uint64 GetTrophySpaceRequiredBeforeInstall() = 0; + + // On PS3, user stats & achievement progress through Steam must be stored with the user's saved game data. + // At startup, before calling RequestCurrentStats(), you must pass the user's stats data to Steam via this method. + // If you do not have any user data, call this function with pvData = NULL and cubData = 0 + virtual bool SetUserStatsData( const void *pvData, uint32 cubData ) = 0; + + // Call to get the user's current stats data. You should retrieve this data after receiving successful UserStatsReceived_t & UserStatsStored_t + // callbacks, and store the data with the user's save game data. You can call this method with pvData = NULL and cubData = 0 to get the required + // buffer size. + virtual bool GetUserStatsData( void *pvData, uint32 cubData, uint32 *pcubWritten ) = 0; +#endif +}; + +#define STEAMUSERSTATS_INTERFACE_VERSION "STEAMUSERSTATS_INTERFACE_VERSION011" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +//----------------------------------------------------------------------------- +// 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 SteamUtils()->GetImageRGBA(), 0 means no image is set for the achievement +}; + + +//----------------------------------------------------------------------------- +// Purpose: Callback indicating that global achievement percentages are fetched +//----------------------------------------------------------------------------- +struct GlobalAchievementPercentagesReady_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 10 }; + + uint64 m_nGameID; // Game this is for + EResult m_eResult; // Result of the operation +}; + + +//----------------------------------------------------------------------------- +// Purpose: call result indicating UGC has been uploaded, returned as a result of SetLeaderboardUGC() +//----------------------------------------------------------------------------- +struct LeaderboardUGCSet_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 11 }; + EResult m_eResult; // The result of the operation + SteamLeaderboard_t m_hSteamLeaderboard; // the leaderboard handle that was +}; + + +//----------------------------------------------------------------------------- +// Purpose: callback indicating that PS3 trophies have been installed +//----------------------------------------------------------------------------- +struct PS3TrophiesInstalled_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 12 }; + uint64 m_nGameID; // Game these stats are for + EResult m_eResult; // The result of the operation + uint64 m_ulRequiredDiskSpace; // If m_eResult is k_EResultDiskFull, will contain the amount of space needed to install trophies + +}; + + +//----------------------------------------------------------------------------- +// Purpose: callback indicating global stats have been received. +// Returned as a result of RequestGlobalStats() +//----------------------------------------------------------------------------- +struct GlobalStatsReceived_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 12 }; + uint64 m_nGameID; // Game global stats were requested for + EResult m_eResult; // The result of the request +}; + +#pragma pack( pop ) + + +#endif // ISTEAMUSER_H diff --git a/dep/steam_api/isteamutils.h b/dep/steam_api/isteamutils.h new file mode 100644 index 0000000..e331fa6 --- /dev/null +++ b/dep/steam_api/isteamutils.h @@ -0,0 +1,264 @@ +//====== 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 +}; + + +// Input modes for the Big Picture gamepad text entry +enum EGamepadTextInputMode +{ + k_EGamepadTextInputModeNormal = 0, + k_EGamepadTextInputModePassword = 1 +}; + + +// Controls number of allowed lines for the Big Picture gamepad text entry +enum EGamepadTextInputLineMode +{ + k_EGamepadTextInputLineModeSingleLine = 0, + k_EGamepadTextInputLineModeMultipleLines = 1 +}; + + +// 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. Number of seconds since January 1, 1970, GMT (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; + + // Deprecated. Applications should use SteamAPI_RunCallbacks() instead. Game servers do not need to call this function. + STEAM_PRIVATE_API( 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 an executable file has been signed using the public key set on the signing tab + // of the partner site, for example to refuse to load modified executable files. + // The result is returned in CheckFileSignature_t. + // k_ECheckFileSignatureNoSignaturesFoundForThisApp - This app has not been configured on the signing tab of the partner site to enable this function. + // k_ECheckFileSignatureNoSignaturesFoundForThisFile - This file is not listed on the signing tab for the partner site. + // k_ECheckFileSignatureFileNotFound - The file does not exist on disk. + // k_ECheckFileSignatureInvalidSignature - The file exists, and the signing tab has been set for this file, but the file is either not signed or the signature does not match. + // k_ECheckFileSignatureValidSignature - The file is signed and the signature is valid. + CALL_RESULT( CheckFileSignature_t ) + virtual SteamAPICall_t CheckFileSignature( const char *szFileName ) = 0; + + // Activates the Big Picture text input dialog which only supports gamepad input + virtual bool ShowGamepadTextInput( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32 unCharMax, const char *pchExistingText ) = 0; + + // Returns previously entered text & length + virtual uint32 GetEnteredGamepadTextLength() = 0; + virtual bool GetEnteredGamepadTextInput( char *pchText, uint32 cchText ) = 0; + + // returns the language the steam client is running in, you probably want ISteamApps::GetCurrentGameLanguage instead, this is for very special usage cases + virtual const char *GetSteamUILanguage() = 0; + + // returns true if Steam itself is running in VR mode + virtual bool IsSteamRunningInVR() = 0; + + // Sets the inset of the overlay notification from the corner specified by SetOverlayNotificationPosition. + virtual void SetOverlayNotificationInset( int nHorizontalInset, int nVerticalInset ) = 0; + + // returns true if Steam & the Steam Overlay are running in Big Picture mode + // Games much be launched through the Steam client to enable the Big Picture overlay. During development, + // a game can be added as a non-steam game to the developers library to test this feature + virtual bool IsSteamInBigPictureMode() = 0; + + // ask SteamUI to create and render its OpenVR dashboard + virtual void StartVRDashboard() = 0; + + // Returns true if the HMD content will be streamed via Steam In-Home Streaming + virtual bool IsVRHeadsetStreamingEnabled() = 0; + + // Set whether the HMD content will be streamed via Steam In-Home Streaming + // If this is set to true, then the scene in the HMD headset will be streamed, and remote input will not be allowed. + // If this is set to false, then the application window will be streamed instead, and remote input will be allowed. + // The default is true unless "VRHeadsetStreaming" "0" is in the extended appinfo for a game. + // (this is useful for games that have asymmetric multiplayer gameplay) + virtual void SetVRHeadsetStreamingEnabled( bool bEnabled ) = 0; +}; + +#define STEAMUTILS_INTERFACE_VERSION "SteamUtils009" + + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + +//----------------------------------------------------------------------------- +// 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; + int m_iCallback; + uint32 m_cubParam; +}; + + +//----------------------------------------------------------------------------- +// 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; +}; + + +// k_iSteamUtilsCallbacks + 13 is taken + + +//----------------------------------------------------------------------------- +// Big Picture gamepad text input has been closed +//----------------------------------------------------------------------------- +struct GamepadTextInputDismissed_t +{ + enum { k_iCallback = k_iSteamUtilsCallbacks + 14 }; + bool m_bSubmitted; // true if user entered & accepted text (Call ISteamUtils::GetEnteredGamepadTextInput() for text), false if canceled input + uint32 m_unSubmittedText; +}; + +// k_iSteamUtilsCallbacks + 15 is taken + +#pragma pack( pop ) + +#endif // ISTEAMUTILS_H diff --git a/dep/steam_api/isteamvideo.h b/dep/steam_api/isteamvideo.h new file mode 100644 index 0000000..32eeb59 --- /dev/null +++ b/dep/steam_api/isteamvideo.h @@ -0,0 +1,71 @@ +//====== Copyright © 1996-2014 Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to Steam Video +// +//============================================================================= + +#ifndef ISTEAMVIDEO_H +#define ISTEAMVIDEO_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +// callbacks +#if defined( VALVE_CALLBACK_PACK_SMALL ) +#pragma pack( push, 4 ) +#elif defined( VALVE_CALLBACK_PACK_LARGE ) +#pragma pack( push, 8 ) +#else +#error isteamclient.h must be included +#endif + + + + +//----------------------------------------------------------------------------- +// Purpose: Steam Video API +//----------------------------------------------------------------------------- +class ISteamVideo +{ +public: + + // Get a URL suitable for streaming the given Video app ID's video + virtual void GetVideoURL( AppId_t unVideoAppID ) = 0; + + // returns true if user is uploading a live broadcast + virtual bool IsBroadcasting( int *pnNumViewers ) = 0; + + // Get the OPF Details for 360 Video Playback + CALL_BACK( GetOPFSettingsResult_t ) + virtual void GetOPFSettings( AppId_t unVideoAppID ) = 0; + virtual bool GetOPFStringForApp( AppId_t unVideoAppID, char *pchBuffer, int32 *pnBufferSize ) = 0; +}; + +#define STEAMVIDEO_INTERFACE_VERSION "STEAMVIDEO_INTERFACE_V002" + +DEFINE_CALLBACK( BroadcastUploadStart_t, k_iClientVideoCallbacks + 4 ) +END_DEFINE_CALLBACK_0() + +DEFINE_CALLBACK( BroadcastUploadStop_t, k_iClientVideoCallbacks + 5 ) + CALLBACK_MEMBER( 0, EBroadcastUploadResult, m_eResult ) +END_DEFINE_CALLBACK_1() + +DEFINE_CALLBACK( GetVideoURLResult_t, k_iClientVideoCallbacks + 11 ) + CALLBACK_MEMBER( 0, EResult, m_eResult ) + CALLBACK_MEMBER( 1, AppId_t, m_unVideoAppID ) + CALLBACK_MEMBER( 2, char, m_rgchURL[256] ) +END_DEFINE_CALLBACK_3() + + +DEFINE_CALLBACK( GetOPFSettingsResult_t, k_iClientVideoCallbacks + 24 ) + CALLBACK_MEMBER( 0, EResult, m_eResult ) + CALLBACK_MEMBER( 1, AppId_t, m_unVideoAppID ) +END_DEFINE_CALLBACK_2() + + +#pragma pack( pop ) + + +#endif // ISTEAMVIDEO_H diff --git a/dep/steam_api/matchmakingtypes.h b/dep/steam_api/matchmakingtypes.h new file mode 100644 index 0000000..e52cfc6 --- /dev/null +++ b/dep/steam_api/matchmakingtypes.h @@ -0,0 +1,251 @@ +//========= 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 + +// +// Max size (in bytes of UTF-8 data, not in characters) of server fields, including null terminator. +// WARNING: These cannot be changed easily, without breaking clients using old interfaces. +// +const int k_cbMaxGameServerGameDir = 32; +const int k_cbMaxGameServerMapName = 32; +const int k_cbMaxGameServerGameDescription = 64; +const int k_cbMaxGameServerName = 64; +const int k_cbMaxGameServerTags = 128; +const int k_cbMaxGameServerGameData = 2048; + +/// Store key/value pair used in matchmaking queries. +/// +/// Actually, the name Key/Value is a bit misleading. The "key" is better +/// understood as "filter operation code" and the "value" is the operand to this +/// filter operation. The meaning of the operand depends upon the filter. +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! + m_szKey[ sizeof( m_szKey ) - 1 ] = '\0'; + strncpy( m_szValue, pchValue, sizeof(m_szValue) ); + m_szValue[ sizeof( m_szValue ) - 1 ] = '\0'; + } + 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: + + servernetadr_t() : m_usConnectionPort( 0 ), m_usQueryPort( 0 ), m_unIP( 0 ) {} + + void Init( unsigned int ip, uint16 usQueryPort, uint16 usConnectionPort ); +#ifdef NETADR_H + 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 netadr_t servernetadr_t::GetIPAndQueryPort() +{ + return netadr_t( m_unIP, m_usQueryPort ); +} +#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; +#ifdef VALVE_BIG_ENDIAN + _snprintf (s[nBuf], sizeof( s[nBuf] ), "%u.%u.%u.%u:%i", (int)(ipByte[0]), (int)(ipByte[1]), (int)(ipByte[2]), (int)(ipByte[3]), usPort ); +#else + _snprintf (s[nBuf], sizeof( s[nBuf] ), "%u.%u.%u.%u:%i", (int)(ipByte[3]), (int)(ipByte[2]), (int)(ipByte[1]), (int)(ipByte[0]), usPort ); +#endif + 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[k_cbMaxGameServerGameDir]; ///< current game directory + char m_szMap[k_cbMaxGameServerMapName]; ///< current map + char m_szGameDescription[k_cbMaxGameServerGameDescription]; ///< game description + uint32 m_nAppID; ///< Steam App ID of this server + int m_nPlayers; ///< total number of players currently on the server. INCLUDES BOTS!! + 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: + + /// Game server name + char m_szServerName[k_cbMaxGameServerName]; + + // For data added after SteamMatchMaking001 add it here +public: + /// the tags this server exposes + char m_szGameTags[k_cbMaxGameServerTags]; + + /// steamID of the game server - invalid if it's doesn't have one (old server, or not connected to Steam) + CSteamID m_steamID; +}; + + +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 ) ); + m_szServerName[ sizeof( m_szServerName ) - 1 ] = '\0'; +} + + +#endif // MATCHMAKINGTYPES_H diff --git a/dep/steam_api/steam_api.h b/dep/steam_api/steam_api.h new file mode 100644 index 0000000..e3a31ae --- /dev/null +++ b/dep/steam_api/steam_api.h @@ -0,0 +1,394 @@ +//====== 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" +#include "isteamscreenshots.h" +#include "isteammusic.h" +#include "isteammusicremote.h" +#include "isteamhttp.h" +#include "isteamunifiedmessages.h" +#include "isteamcontroller.h" +#include "isteamugc.h" +#include "isteamapplist.h" +#include "isteamhtmlsurface.h" +#include "isteaminventory.h" +#include "isteamvideo.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 +// +//----------------------------------------------------------------------------------------------------------------------------------------------------------// + + +// SteamAPI_Init must be called before using any other API functions. If it fails, an +// error message will be output to the debugger (or stderr) with further information. +S_API bool S_CALLTYPE SteamAPI_Init(); + +// SteamAPI_Shutdown should be called during process shutdown if possible. +S_API void S_CALLTYPE SteamAPI_Shutdown(); + +// SteamAPI_RestartAppIfNecessary ensures that your executable was launched through Steam. +// +// Returns true if the current process should terminate. Steam is now re-launching your application. +// +// Returns false if no action needs to be taken. This means that 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 if false is returned. +// +// NOTE: If you use the Steam DRM wrapper on your primary executable file, this check is unnecessary +// since the DRM wrapper will ensure that your application was launched properly through Steam. +S_API bool S_CALLTYPE SteamAPI_RestartAppIfNecessary( uint32 unOwnAppID ); + +// Many Steam API functions allocate a small amount of thread-local memory for parameter storage. +// SteamAPI_ReleaseCurrentThreadMemory() will free API memory associated with the calling thread. +// This function is also called automatically by SteamAPI_RunCallbacks(), so a single-threaded +// program never needs to explicitly call this function. +S_API void S_CALLTYPE SteamAPI_ReleaseCurrentThreadMemory(); + + +// crash dump recording functions +S_API void S_CALLTYPE SteamAPI_WriteMiniDump( uint32 uStructuredExceptionCode, void* pvExceptionInfo, uint32 uBuildID ); +S_API void S_CALLTYPE SteamAPI_SetMiniDumpComment( const char *pchMsg ); + + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// Global accessors for Steamworks C++ APIs. See individual isteam*.h files for details. +// You should not cache the results of these accessors or pass the result pointers across +// modules! Different modules may be compiled against different SDK header versions, and +// the interface pointers could therefore be different across modules. Every line of code +// which calls into a Steamworks API should retrieve the interface from a global accessor. +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +#if !defined( STEAM_API_EXPORTS ) +inline ISteamClient *SteamClient(); +inline ISteamUser *SteamUser(); +inline ISteamFriends *SteamFriends(); +inline ISteamUtils *SteamUtils(); +inline ISteamMatchmaking *SteamMatchmaking(); +inline ISteamUserStats *SteamUserStats(); +inline ISteamApps *SteamApps(); +inline ISteamNetworking *SteamNetworking(); +inline ISteamMatchmakingServers *SteamMatchmakingServers(); +inline ISteamRemoteStorage *SteamRemoteStorage(); +inline ISteamScreenshots *SteamScreenshots(); +inline ISteamHTTP *SteamHTTP(); +inline ISteamUnifiedMessages *SteamUnifiedMessages(); +inline ISteamController *SteamController(); +inline ISteamUGC *SteamUGC(); +inline ISteamAppList *SteamAppList(); +inline ISteamMusic *SteamMusic(); +inline ISteamMusicRemote *SteamMusicRemote(); +inline ISteamHTMLSurface *SteamHTMLSurface(); +inline ISteamInventory *SteamInventory(); +inline ISteamVideo *SteamVideo(); +#endif // VERSION_SAFE_STEAM_API_INTERFACES + + +// CSteamAPIContext encapsulates the Steamworks API global accessors into +// a single object. This is DEPRECATED and only remains for compatibility. +class CSteamAPIContext +{ +public: + // DEPRECATED - there is no benefit to using this over the global accessors + CSteamAPIContext() { Clear(); } + void Clear(); + bool Init(); + ISteamClient* SteamClient() const { return m_pSteamClient; } + ISteamUser* SteamUser() const { return m_pSteamUser; } + ISteamFriends* SteamFriends() const { return m_pSteamFriends; } + ISteamUtils* SteamUtils() const { return m_pSteamUtils; } + ISteamMatchmaking* SteamMatchmaking() const { return m_pSteamMatchmaking; } + ISteamUserStats* SteamUserStats() const { return m_pSteamUserStats; } + ISteamApps* SteamApps() const { return m_pSteamApps; } + ISteamMatchmakingServers* SteamMatchmakingServers() const { return m_pSteamMatchmakingServers; } + ISteamNetworking* SteamNetworking() const { return m_pSteamNetworking; } + ISteamRemoteStorage* SteamRemoteStorage() const { return m_pSteamRemoteStorage; } + ISteamScreenshots* SteamScreenshots() const { return m_pSteamScreenshots; } + ISteamHTTP* SteamHTTP() const { return m_pSteamHTTP; } + ISteamUnifiedMessages* SteamUnifiedMessages() const { return m_pSteamUnifiedMessages; } + ISteamController* SteamController() const { return m_pController; } + ISteamUGC* SteamUGC() const { return m_pSteamUGC; } + ISteamAppList* SteamAppList() const { return m_pSteamAppList; } + ISteamMusic* SteamMusic() const { return m_pSteamMusic; } + ISteamMusicRemote* SteamMusicRemote() const { return m_pSteamMusicRemote; } + ISteamHTMLSurface* SteamHTMLSurface() const { return m_pSteamHTMLSurface; } + ISteamInventory* SteamInventory() const { return m_pSteamInventory; } + ISteamVideo* SteamVideo() const { return m_pSteamVideo; } + // DEPRECATED - there is no benefit to using this over the global accessors +private: + ISteamClient *m_pSteamClient; + 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; + ISteamScreenshots *m_pSteamScreenshots; + ISteamHTTP *m_pSteamHTTP; + ISteamUnifiedMessages *m_pSteamUnifiedMessages; + ISteamController *m_pController; + ISteamUGC *m_pSteamUGC; + ISteamAppList *m_pSteamAppList; + ISteamMusic *m_pSteamMusic; + ISteamMusicRemote *m_pSteamMusicRemote; + ISteamHTMLSurface *m_pSteamHTMLSurface; + ISteamInventory *m_pSteamInventory; + ISteamVideo *m_pSteamVideo; +}; + + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// steam callback and call-result helpers +// +// The following macros and classes are used to register your application for +// callbacks and call-results, which are delivered in a predictable manner. +// +// STEAM_CALLBACK macros are meant for use inside of a C++ class definition. +// They map a Steam notification callback directly to a class member function +// which is automatically prototyped as "void func( callback_type *pParam )". +// +// CCallResult is used with specific Steam APIs that return "result handles". +// The handle can be passed to a CCallResult object's Set function, along with +// an object pointer and member-function pointer. The member function will +// be executed once the results of the Steam API call are available. +// +// CCallback and CCallbackManual classes can be used instead of STEAM_CALLBACK +// macros if you require finer control over registration and unregistration. +// +// Callbacks and call-results are queued automatically and are only +// delivered/executed when your application calls SteamAPI_RunCallbacks(). +//----------------------------------------------------------------------------------------------------------------------------------------------------------// + +// SteamAPI_RunCallbacks is safe to call from multiple threads simultaneously, +// but if you choose to do this, callback code could be executed on any thread. +// One alternative is to call SteamAPI_RunCallbacks from the main thread only, +// and call SteamAPI_ReleaseCurrentThreadMemory regularly on other threads. +S_API void S_CALLTYPE SteamAPI_RunCallbacks(); + + +// Declares a callback member function plus a helper member variable which +// registers the callback on object creation and unregisters on destruction. +// The optional fourth 'var' param exists only for backwards-compatibility +// and can be ignored. +#define STEAM_CALLBACK( thisclass, func, .../*callback_type, [deprecated] var*/ ) \ + _STEAM_CALLBACK_SELECT( ( __VA_ARGS__, 4, 3 ), ( /**/, thisclass, func, __VA_ARGS__ ) ) + +// Declares a callback function and a named CCallbackManual variable which +// has Register and Unregister functions instead of automatic registration. +#define STEAM_CALLBACK_MANUAL( thisclass, func, callback_type, var ) \ + CCallbackManual< thisclass, callback_type > var; void func( callback_type *pParam ) + + +// Internal functions used by the utility CCallback objects to receive callbacks +S_API void S_CALLTYPE SteamAPI_RegisterCallback( class CCallbackBase *pCallback, int iCallback ); +S_API void S_CALLTYPE SteamAPI_UnregisterCallback( class CCallbackBase *pCallback ); +// Internal functions used by the utility CCallResult objects to receive async call results +S_API void S_CALLTYPE SteamAPI_RegisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall ); +S_API void S_CALLTYPE SteamAPI_UnregisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall ); + + +//----------------------------------------------------------------------------- +// Purpose: base for callbacks and call results - internal implementation detail +//----------------------------------------------------------------------------- +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; + +private: + CCallbackBase( const CCallbackBase& ); + CCallbackBase& operator=( const CCallbackBase& ); +}; + +//----------------------------------------------------------------------------- +// Purpose: templated base for callbacks - internal implementation detail +//----------------------------------------------------------------------------- +template< int sizeof_P > +class CCallbackImpl : protected CCallbackBase +{ +public: + ~CCallbackImpl() { if ( m_nCallbackFlags & k_ECallbackFlagsRegistered ) SteamAPI_UnregisterCallback( this ); } + void SetGameserverFlag() { m_nCallbackFlags |= k_ECallbackFlagsGameServer; } + +protected: + virtual void Run( void *pvParam ) = 0; + virtual void Run( void *pvParam, bool /*bIOFailure*/, SteamAPICall_t /*hSteamAPICall*/ ) { Run( pvParam ); } + virtual int GetCallbackSizeBytes() { return sizeof_P; } +}; + + +//----------------------------------------------------------------------------- +// 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(); + ~CCallResult(); + + void Set( SteamAPICall_t hAPICall, T *p, func_t func ); + bool IsActive() const; + void Cancel(); + + void SetGameserverFlag() { m_nCallbackFlags |= k_ECallbackFlagsGameServer; } +private: + virtual void Run( void *pvParam ); + virtual void Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall ); + virtual 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, +// bGameserver = listen for gameserver callbacks instead of client callbacks +//----------------------------------------------------------------------------- +template< class T, class P, bool bGameserver = false > +class CCallback : public CCallbackImpl< sizeof( P ) > +{ +public: + typedef void (T::*func_t)(P*); + + // NOTE: If you can't provide the correct parameters at construction time, you should + // use the CCallbackManual callback object (STEAM_CALLBACK_MANUAL macro) instead. + CCallback( T *pObj, func_t func ); + + void Register( T *pObj, func_t func ); + void Unregister(); + +protected: + virtual void Run( void *pvParam ); + + T *m_pObj; + func_t m_Func; +}; + + +//----------------------------------------------------------------------------- +// Purpose: subclass of CCallback which allows default-construction in +// an unregistered state; you must call Register manually +//----------------------------------------------------------------------------- +template< class T, class P, bool bGameServer = false > +class CCallbackManual : public CCallback< T, P, bGameServer > +{ +public: + CCallbackManual() : CCallback< T, P, bGameServer >( NULL, NULL ) {} + + // Inherits public Register and Unregister functions from base class +}; + + + +#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 +//----------------------------------------------------------------------------------------------------------------------------------------------------------// + +// SteamAPI_IsSteamRunning() returns true if Steam is currently running +S_API bool S_CALLTYPE SteamAPI_IsSteamRunning(); + +// Pumps out all the steam messages, calling registered callbacks. +// NOT THREADSAFE - do not call from multiple threads simultaneously. +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. +// DEPRECATED - implementation is Windows only, and the path returned is a UTF-8 string which must be converted to UTF-16 for use with Win32 APIs +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(); + + +#if defined( VERSION_SAFE_STEAM_API_INTERFACES ) +// exists only for backwards compat with code written against older SDKs +S_API bool S_CALLTYPE SteamAPI_InitSafe(); +#endif + +#include "steam_api_internal.h" + +#endif // STEAM_API_H diff --git a/dep/steam_api/steam_api_flat.h b/dep/steam_api/steam_api_flat.h new file mode 100644 index 0000000..7d35cfe --- /dev/null +++ b/dep/steam_api/steam_api_flat.h @@ -0,0 +1,816 @@ +//====== Copyright (c) 1996-2014, Valve Corporation, All rights reserved. ======= +// +// Purpose: Header for flatted SteamAPI. Use this for binding to other languages. +// This file is auto-generated, do not edit it. +// +//============================================================================= + +#ifndef STEAMAPIFLAT_H +#define STEAMAPIFLAT_H +#ifdef _WIN32 +#pragma once +#endif + +#include + + +typedef unsigned char uint8; +typedef unsigned char uint8; +typedef signed char int8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +typedef int64 lint64; +typedef uint64 ulint64; +typedef uint8 Salt_t[8]; +typedef uint64 GID_t; +typedef uint64 JobID_t; +typedef GID_t TxnID_t; +typedef uint32 PackageId_t; +typedef uint32 BundleId_t; +typedef uint32 AppId_t; +typedef uint64 AssetClassId_t; +typedef uint32 PhysicalItemId_t; +typedef uint32 DepotId_t; +typedef uint32 RTime32; +typedef uint32 CellID_t; +typedef uint64 SteamAPICall_t; +typedef uint32 AccountID_t; +typedef uint32 PartnerId_t; +typedef uint64 ManifestId_t; +typedef uint32 HAuthTicket; +typedef void * BREAKPAD_HANDLE; +typedef char compile_time_assert_type[1]; +typedef int32 HSteamPipe; +typedef int32 HSteamUser; +typedef int16 FriendsGroupID_t; +typedef void * HServerListRequest; +typedef int HServerQuery; +typedef uint64 UGCHandle_t; +typedef uint64 PublishedFileUpdateHandle_t; +typedef uint64 PublishedFileId_t; +typedef uint64 UGCFileWriteStreamHandle_t; +typedef char compile_time_assert_type[1]; +typedef uint64 SteamLeaderboard_t; +typedef uint64 SteamLeaderboardEntries_t; +typedef uint32 SNetSocket_t; +typedef uint32 SNetListenSocket_t; +typedef uint32 ScreenshotHandle; +typedef uint32 HTTPRequestHandle; +typedef uint32 HTTPCookieContainerHandle; +typedef uint64 ClientUnifiedMessageHandle; +typedef uint64 ControllerHandle_t; +typedef uint64 ControllerActionSetHandle_t; +typedef uint64 ControllerDigitalActionHandle_t; +typedef uint64 ControllerAnalogActionHandle_t; +typedef uint64 UGCQueryHandle_t; +typedef uint64 UGCUpdateHandle_t; +typedef uint32 HHTMLBrowser; +typedef uint64 SteamItemInstanceID_t; +typedef int32 SteamItemDef_t; +typedef int32 SteamInventoryResult_t; +// OpenVR Constants +int const_k_iSteamUserCallbacks = 100; +int const_k_iSteamGameServerCallbacks = 200; +int const_k_iSteamFriendsCallbacks = 300; +int const_k_iSteamBillingCallbacks = 400; +int const_k_iSteamMatchmakingCallbacks = 500; +int const_k_iSteamContentServerCallbacks = 600; +int const_k_iSteamUtilsCallbacks = 700; +int const_k_iClientFriendsCallbacks = 800; +int const_k_iClientUserCallbacks = 900; +int const_k_iSteamAppsCallbacks = 1000; +int const_k_iSteamUserStatsCallbacks = 1100; +int const_k_iSteamNetworkingCallbacks = 1200; +int const_k_iClientRemoteStorageCallbacks = 1300; +int const_k_iClientDepotBuilderCallbacks = 1400; +int const_k_iSteamGameServerItemsCallbacks = 1500; +int const_k_iClientUtilsCallbacks = 1600; +int const_k_iSteamGameCoordinatorCallbacks = 1700; +int const_k_iSteamGameServerStatsCallbacks = 1800; +int const_k_iSteam2AsyncCallbacks = 1900; +int const_k_iSteamGameStatsCallbacks = 2000; +int const_k_iClientHTTPCallbacks = 2100; +int const_k_iClientScreenshotsCallbacks = 2200; +int const_k_iSteamScreenshotsCallbacks = 2300; +int const_k_iClientAudioCallbacks = 2400; +int const_k_iClientUnifiedMessagesCallbacks = 2500; +int const_k_iSteamStreamLauncherCallbacks = 2600; +int const_k_iClientControllerCallbacks = 2700; +int const_k_iSteamControllerCallbacks = 2800; +int const_k_iClientParentalSettingsCallbacks = 2900; +int const_k_iClientDeviceAuthCallbacks = 3000; +int const_k_iClientNetworkDeviceManagerCallbacks = 3100; +int const_k_iClientMusicCallbacks = 3200; +int const_k_iClientRemoteClientManagerCallbacks = 3300; +int const_k_iClientUGCCallbacks = 3400; +int const_k_iSteamStreamClientCallbacks = 3500; +int const_k_IClientProductBuilderCallbacks = 3600; +int const_k_iClientShortcutsCallbacks = 3700; +int const_k_iClientRemoteControlManagerCallbacks = 3800; +int const_k_iSteamAppListCallbacks = 3900; +int const_k_iSteamMusicCallbacks = 4000; +int const_k_iSteamMusicRemoteCallbacks = 4100; +int const_k_iClientVRCallbacks = 4200; +int const_k_iClientGameNotificationCallbacks = 4300; +int const_k_iSteamGameNotificationCallbacks = 4400; +int const_k_iSteamHTMLSurfaceCallbacks = 4500; +int const_k_iClientVideoCallbacks = 4600; +int const_k_iClientInventoryCallbacks = 4700; +int const_k_iClientBluetoothManagerCallbacks = 4800; +int const_k_cchPersonaNameMax = 128; +int const_k_cwchPersonaNameMax = 32; +int const_k_cchMaxRichPresenceKeys = 20; +int const_k_cchMaxRichPresenceKeyLength = 64; +int const_k_cchMaxRichPresenceValueLength = 256; +int const_k_cchStatNameMax = 128; +int const_k_cchLeaderboardNameMax = 128; +int const_k_cLeaderboardDetailsMax = 64; +unsigned long const_k_InvalidUnifiedMessageHandle = 0; +unsigned long const_k_SteamItemInstanceIDInvalid = 0xffffffff; +int const_k_SteamInventoryResultInvalid = -1; + + + +// OpenVR Enums +// OpenVR Structs + + + +S_API HSteamPipe SteamAPI_ISteamClient_CreateSteamPipe(intptr_t instancePtr); +S_API bool SteamAPI_ISteamClient_BReleaseSteamPipe(intptr_t instancePtr, HSteamPipe hSteamPipe); +S_API HSteamUser SteamAPI_ISteamClient_ConnectToGlobalUser(intptr_t instancePtr, HSteamPipe hSteamPipe); +S_API HSteamUser SteamAPI_ISteamClient_CreateLocalUser(intptr_t instancePtr, HSteamPipe * phSteamPipe, EAccountType eAccountType); +S_API void SteamAPI_ISteamClient_ReleaseUser(intptr_t instancePtr, HSteamPipe hSteamPipe, HSteamUser hUser); +S_API class ISteamUser * SteamAPI_ISteamClient_GetISteamUser(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamGameServer * SteamAPI_ISteamClient_GetISteamGameServer(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API void SteamAPI_ISteamClient_SetLocalIPBinding(intptr_t instancePtr, uint32 unIP, uint16 usPort); +S_API class ISteamFriends * SteamAPI_ISteamClient_GetISteamFriends(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamUtils * SteamAPI_ISteamClient_GetISteamUtils(intptr_t instancePtr, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamMatchmaking * SteamAPI_ISteamClient_GetISteamMatchmaking(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamMatchmakingServers * SteamAPI_ISteamClient_GetISteamMatchmakingServers(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API void * SteamAPI_ISteamClient_GetISteamGenericInterface(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamUserStats * SteamAPI_ISteamClient_GetISteamUserStats(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamGameServerStats * SteamAPI_ISteamClient_GetISteamGameServerStats(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamApps * SteamAPI_ISteamClient_GetISteamApps(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamNetworking * SteamAPI_ISteamClient_GetISteamNetworking(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamRemoteStorage * SteamAPI_ISteamClient_GetISteamRemoteStorage(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamScreenshots * SteamAPI_ISteamClient_GetISteamScreenshots(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API uint32 SteamAPI_ISteamClient_GetIPCCallCount(intptr_t instancePtr); +S_API void SteamAPI_ISteamClient_SetWarningMessageHook(intptr_t instancePtr, SteamAPIWarningMessageHook_t pFunction); +S_API bool SteamAPI_ISteamClient_BShutdownIfAllPipesClosed(intptr_t instancePtr); +S_API class ISteamHTTP * SteamAPI_ISteamClient_GetISteamHTTP(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamUnifiedMessages * SteamAPI_ISteamClient_GetISteamUnifiedMessages(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamController * SteamAPI_ISteamClient_GetISteamController(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamUGC * SteamAPI_ISteamClient_GetISteamUGC(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamAppList * SteamAPI_ISteamClient_GetISteamAppList(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamMusic * SteamAPI_ISteamClient_GetISteamMusic(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamMusicRemote * SteamAPI_ISteamClient_GetISteamMusicRemote(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamHTMLSurface * SteamAPI_ISteamClient_GetISteamHTMLSurface(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamInventory * SteamAPI_ISteamClient_GetISteamInventory(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API class ISteamVideo * SteamAPI_ISteamClient_GetISteamVideo(intptr_t instancePtr, HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char * pchVersion); +S_API HSteamUser SteamAPI_ISteamUser_GetHSteamUser(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUser_BLoggedOn(intptr_t instancePtr); +S_API uint64 SteamAPI_ISteamUser_GetSteamID(intptr_t instancePtr); +S_API int SteamAPI_ISteamUser_InitiateGameConnection(intptr_t instancePtr, void * pAuthBlob, int cbMaxAuthBlob, class CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer, bool bSecure); +S_API void SteamAPI_ISteamUser_TerminateGameConnection(intptr_t instancePtr, uint32 unIPServer, uint16 usPortServer); +S_API void SteamAPI_ISteamUser_TrackAppUsageEvent(intptr_t instancePtr, class CGameID gameID, int eAppUsageEvent, const char * pchExtraInfo); +S_API bool SteamAPI_ISteamUser_GetUserDataFolder(intptr_t instancePtr, char * pchBuffer, int cubBuffer); +S_API void SteamAPI_ISteamUser_StartVoiceRecording(intptr_t instancePtr); +S_API void SteamAPI_ISteamUser_StopVoiceRecording(intptr_t instancePtr); +S_API EVoiceResult SteamAPI_ISteamUser_GetAvailableVoice(intptr_t instancePtr, uint32 * pcbCompressed, uint32 * pcbUncompressed_Deprecated, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated); +S_API EVoiceResult SteamAPI_ISteamUser_GetVoice(intptr_t instancePtr, bool bWantCompressed, void * pDestBuffer, uint32 cbDestBufferSize, uint32 * nBytesWritten, bool bWantUncompressed_Deprecated, void * pUncompressedDestBuffer_Deprecated, uint32 cbUncompressedDestBufferSize_Deprecated, uint32 * nUncompressBytesWritten_Deprecated, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated); +S_API EVoiceResult SteamAPI_ISteamUser_DecompressVoice(intptr_t instancePtr, const void * pCompressed, uint32 cbCompressed, void * pDestBuffer, uint32 cbDestBufferSize, uint32 * nBytesWritten, uint32 nDesiredSampleRate); +S_API uint32 SteamAPI_ISteamUser_GetVoiceOptimalSampleRate(intptr_t instancePtr); +S_API HAuthTicket SteamAPI_ISteamUser_GetAuthSessionTicket(intptr_t instancePtr, void * pTicket, int cbMaxTicket, uint32 * pcbTicket); +S_API EBeginAuthSessionResult SteamAPI_ISteamUser_BeginAuthSession(intptr_t instancePtr, const void * pAuthTicket, int cbAuthTicket, class CSteamID steamID); +S_API void SteamAPI_ISteamUser_EndAuthSession(intptr_t instancePtr, class CSteamID steamID); +S_API void SteamAPI_ISteamUser_CancelAuthTicket(intptr_t instancePtr, HAuthTicket hAuthTicket); +S_API EUserHasLicenseForAppResult SteamAPI_ISteamUser_UserHasLicenseForApp(intptr_t instancePtr, class CSteamID steamID, AppId_t appID); +S_API bool SteamAPI_ISteamUser_BIsBehindNAT(intptr_t instancePtr); +S_API void SteamAPI_ISteamUser_AdvertiseGame(intptr_t instancePtr, class CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer); +S_API SteamAPICall_t SteamAPI_ISteamUser_RequestEncryptedAppTicket(intptr_t instancePtr, void * pDataToInclude, int cbDataToInclude); +S_API bool SteamAPI_ISteamUser_GetEncryptedAppTicket(intptr_t instancePtr, void * pTicket, int cbMaxTicket, uint32 * pcbTicket); +S_API int SteamAPI_ISteamUser_GetGameBadgeLevel(intptr_t instancePtr, int nSeries, bool bFoil); +S_API int SteamAPI_ISteamUser_GetPlayerSteamLevel(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamUser_RequestStoreAuthURL(intptr_t instancePtr, const char * pchRedirectURL); +S_API bool SteamAPI_ISteamUser_BIsPhoneVerified(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUser_BIsTwoFactorEnabled(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUser_BIsPhoneIdentifying(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUser_BIsPhoneRequiringVerification(intptr_t instancePtr); +S_API const char * SteamAPI_ISteamFriends_GetPersonaName(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamFriends_SetPersonaName(intptr_t instancePtr, const char * pchPersonaName); +S_API EPersonaState SteamAPI_ISteamFriends_GetPersonaState(intptr_t instancePtr); +S_API int SteamAPI_ISteamFriends_GetFriendCount(intptr_t instancePtr, int iFriendFlags); +S_API uint64 SteamAPI_ISteamFriends_GetFriendByIndex(intptr_t instancePtr, int iFriend, int iFriendFlags); +S_API EFriendRelationship SteamAPI_ISteamFriends_GetFriendRelationship(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API EPersonaState SteamAPI_ISteamFriends_GetFriendPersonaState(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API const char * SteamAPI_ISteamFriends_GetFriendPersonaName(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API bool SteamAPI_ISteamFriends_GetFriendGamePlayed(intptr_t instancePtr, class CSteamID steamIDFriend, struct FriendGameInfo_t * pFriendGameInfo); +S_API const char * SteamAPI_ISteamFriends_GetFriendPersonaNameHistory(intptr_t instancePtr, class CSteamID steamIDFriend, int iPersonaName); +S_API int SteamAPI_ISteamFriends_GetFriendSteamLevel(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API const char * SteamAPI_ISteamFriends_GetPlayerNickname(intptr_t instancePtr, class CSteamID steamIDPlayer); +S_API int SteamAPI_ISteamFriends_GetFriendsGroupCount(intptr_t instancePtr); +S_API FriendsGroupID_t SteamAPI_ISteamFriends_GetFriendsGroupIDByIndex(intptr_t instancePtr, int iFG); +S_API const char * SteamAPI_ISteamFriends_GetFriendsGroupName(intptr_t instancePtr, FriendsGroupID_t friendsGroupID); +S_API int SteamAPI_ISteamFriends_GetFriendsGroupMembersCount(intptr_t instancePtr, FriendsGroupID_t friendsGroupID); +S_API void SteamAPI_ISteamFriends_GetFriendsGroupMembersList(intptr_t instancePtr, FriendsGroupID_t friendsGroupID, class CSteamID * pOutSteamIDMembers, int nMembersCount); +S_API bool SteamAPI_ISteamFriends_HasFriend(intptr_t instancePtr, class CSteamID steamIDFriend, int iFriendFlags); +S_API int SteamAPI_ISteamFriends_GetClanCount(intptr_t instancePtr); +S_API uint64 SteamAPI_ISteamFriends_GetClanByIndex(intptr_t instancePtr, int iClan); +S_API const char * SteamAPI_ISteamFriends_GetClanName(intptr_t instancePtr, class CSteamID steamIDClan); +S_API const char * SteamAPI_ISteamFriends_GetClanTag(intptr_t instancePtr, class CSteamID steamIDClan); +S_API bool SteamAPI_ISteamFriends_GetClanActivityCounts(intptr_t instancePtr, class CSteamID steamIDClan, int * pnOnline, int * pnInGame, int * pnChatting); +S_API SteamAPICall_t SteamAPI_ISteamFriends_DownloadClanActivityCounts(intptr_t instancePtr, class CSteamID * psteamIDClans, int cClansToRequest); +S_API int SteamAPI_ISteamFriends_GetFriendCountFromSource(intptr_t instancePtr, class CSteamID steamIDSource); +S_API uint64 SteamAPI_ISteamFriends_GetFriendFromSourceByIndex(intptr_t instancePtr, class CSteamID steamIDSource, int iFriend); +S_API bool SteamAPI_ISteamFriends_IsUserInSource(intptr_t instancePtr, class CSteamID steamIDUser, class CSteamID steamIDSource); +S_API void SteamAPI_ISteamFriends_SetInGameVoiceSpeaking(intptr_t instancePtr, class CSteamID steamIDUser, bool bSpeaking); +S_API void SteamAPI_ISteamFriends_ActivateGameOverlay(intptr_t instancePtr, const char * pchDialog); +S_API void SteamAPI_ISteamFriends_ActivateGameOverlayToUser(intptr_t instancePtr, const char * pchDialog, class CSteamID steamID); +S_API void SteamAPI_ISteamFriends_ActivateGameOverlayToWebPage(intptr_t instancePtr, const char * pchURL); +S_API void SteamAPI_ISteamFriends_ActivateGameOverlayToStore(intptr_t instancePtr, AppId_t nAppID, EOverlayToStoreFlag eFlag); +S_API void SteamAPI_ISteamFriends_SetPlayedWith(intptr_t instancePtr, class CSteamID steamIDUserPlayedWith); +S_API void SteamAPI_ISteamFriends_ActivateGameOverlayInviteDialog(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API int SteamAPI_ISteamFriends_GetSmallFriendAvatar(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API int SteamAPI_ISteamFriends_GetMediumFriendAvatar(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API int SteamAPI_ISteamFriends_GetLargeFriendAvatar(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API bool SteamAPI_ISteamFriends_RequestUserInformation(intptr_t instancePtr, class CSteamID steamIDUser, bool bRequireNameOnly); +S_API SteamAPICall_t SteamAPI_ISteamFriends_RequestClanOfficerList(intptr_t instancePtr, class CSteamID steamIDClan); +S_API uint64 SteamAPI_ISteamFriends_GetClanOwner(intptr_t instancePtr, class CSteamID steamIDClan); +S_API int SteamAPI_ISteamFriends_GetClanOfficerCount(intptr_t instancePtr, class CSteamID steamIDClan); +S_API uint64 SteamAPI_ISteamFriends_GetClanOfficerByIndex(intptr_t instancePtr, class CSteamID steamIDClan, int iOfficer); +S_API uint32 SteamAPI_ISteamFriends_GetUserRestrictions(intptr_t instancePtr); +S_API bool SteamAPI_ISteamFriends_SetRichPresence(intptr_t instancePtr, const char * pchKey, const char * pchValue); +S_API void SteamAPI_ISteamFriends_ClearRichPresence(intptr_t instancePtr); +S_API const char * SteamAPI_ISteamFriends_GetFriendRichPresence(intptr_t instancePtr, class CSteamID steamIDFriend, const char * pchKey); +S_API int SteamAPI_ISteamFriends_GetFriendRichPresenceKeyCount(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API const char * SteamAPI_ISteamFriends_GetFriendRichPresenceKeyByIndex(intptr_t instancePtr, class CSteamID steamIDFriend, int iKey); +S_API void SteamAPI_ISteamFriends_RequestFriendRichPresence(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API bool SteamAPI_ISteamFriends_InviteUserToGame(intptr_t instancePtr, class CSteamID steamIDFriend, const char * pchConnectString); +S_API int SteamAPI_ISteamFriends_GetCoplayFriendCount(intptr_t instancePtr); +S_API uint64 SteamAPI_ISteamFriends_GetCoplayFriend(intptr_t instancePtr, int iCoplayFriend); +S_API int SteamAPI_ISteamFriends_GetFriendCoplayTime(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API AppId_t SteamAPI_ISteamFriends_GetFriendCoplayGame(intptr_t instancePtr, class CSteamID steamIDFriend); +S_API SteamAPICall_t SteamAPI_ISteamFriends_JoinClanChatRoom(intptr_t instancePtr, class CSteamID steamIDClan); +S_API bool SteamAPI_ISteamFriends_LeaveClanChatRoom(intptr_t instancePtr, class CSteamID steamIDClan); +S_API int SteamAPI_ISteamFriends_GetClanChatMemberCount(intptr_t instancePtr, class CSteamID steamIDClan); +S_API uint64 SteamAPI_ISteamFriends_GetChatMemberByIndex(intptr_t instancePtr, class CSteamID steamIDClan, int iUser); +S_API bool SteamAPI_ISteamFriends_SendClanChatMessage(intptr_t instancePtr, class CSteamID steamIDClanChat, const char * pchText); +S_API int SteamAPI_ISteamFriends_GetClanChatMessage(intptr_t instancePtr, class CSteamID steamIDClanChat, int iMessage, void * prgchText, int cchTextMax, EChatEntryType * peChatEntryType, class CSteamID * psteamidChatter); +S_API bool SteamAPI_ISteamFriends_IsClanChatAdmin(intptr_t instancePtr, class CSteamID steamIDClanChat, class CSteamID steamIDUser); +S_API bool SteamAPI_ISteamFriends_IsClanChatWindowOpenInSteam(intptr_t instancePtr, class CSteamID steamIDClanChat); +S_API bool SteamAPI_ISteamFriends_OpenClanChatWindowInSteam(intptr_t instancePtr, class CSteamID steamIDClanChat); +S_API bool SteamAPI_ISteamFriends_CloseClanChatWindowInSteam(intptr_t instancePtr, class CSteamID steamIDClanChat); +S_API bool SteamAPI_ISteamFriends_SetListenForFriendsMessages(intptr_t instancePtr, bool bInterceptEnabled); +S_API bool SteamAPI_ISteamFriends_ReplyToFriendMessage(intptr_t instancePtr, class CSteamID steamIDFriend, const char * pchMsgToSend); +S_API int SteamAPI_ISteamFriends_GetFriendMessage(intptr_t instancePtr, class CSteamID steamIDFriend, int iMessageID, void * pvData, int cubData, EChatEntryType * peChatEntryType); +S_API SteamAPICall_t SteamAPI_ISteamFriends_GetFollowerCount(intptr_t instancePtr, class CSteamID steamID); +S_API SteamAPICall_t SteamAPI_ISteamFriends_IsFollowing(intptr_t instancePtr, class CSteamID steamID); +S_API SteamAPICall_t SteamAPI_ISteamFriends_EnumerateFollowingList(intptr_t instancePtr, uint32 unStartIndex); +S_API uint32 SteamAPI_ISteamUtils_GetSecondsSinceAppActive(intptr_t instancePtr); +S_API uint32 SteamAPI_ISteamUtils_GetSecondsSinceComputerActive(intptr_t instancePtr); +S_API EUniverse SteamAPI_ISteamUtils_GetConnectedUniverse(intptr_t instancePtr); +S_API uint32 SteamAPI_ISteamUtils_GetServerRealTime(intptr_t instancePtr); +S_API const char * SteamAPI_ISteamUtils_GetIPCountry(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUtils_GetImageSize(intptr_t instancePtr, int iImage, uint32 * pnWidth, uint32 * pnHeight); +S_API bool SteamAPI_ISteamUtils_GetImageRGBA(intptr_t instancePtr, int iImage, uint8 * pubDest, int nDestBufferSize); +S_API bool SteamAPI_ISteamUtils_GetCSERIPPort(intptr_t instancePtr, uint32 * unIP, uint16 * usPort); +S_API uint8 SteamAPI_ISteamUtils_GetCurrentBatteryPower(intptr_t instancePtr); +S_API uint32 SteamAPI_ISteamUtils_GetAppID(intptr_t instancePtr); +S_API void SteamAPI_ISteamUtils_SetOverlayNotificationPosition(intptr_t instancePtr, ENotificationPosition eNotificationPosition); +S_API bool SteamAPI_ISteamUtils_IsAPICallCompleted(intptr_t instancePtr, SteamAPICall_t hSteamAPICall, bool * pbFailed); +S_API ESteamAPICallFailure SteamAPI_ISteamUtils_GetAPICallFailureReason(intptr_t instancePtr, SteamAPICall_t hSteamAPICall); +S_API bool SteamAPI_ISteamUtils_GetAPICallResult(intptr_t instancePtr, SteamAPICall_t hSteamAPICall, void * pCallback, int cubCallback, int iCallbackExpected, bool * pbFailed); +S_API uint32 SteamAPI_ISteamUtils_GetIPCCallCount(intptr_t instancePtr); +S_API void SteamAPI_ISteamUtils_SetWarningMessageHook(intptr_t instancePtr, SteamAPIWarningMessageHook_t pFunction); +S_API bool SteamAPI_ISteamUtils_IsOverlayEnabled(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUtils_BOverlayNeedsPresent(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamUtils_CheckFileSignature(intptr_t instancePtr, const char * szFileName); +S_API bool SteamAPI_ISteamUtils_ShowGamepadTextInput(intptr_t instancePtr, EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char * pchDescription, uint32 unCharMax, const char * pchExistingText); +S_API uint32 SteamAPI_ISteamUtils_GetEnteredGamepadTextLength(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUtils_GetEnteredGamepadTextInput(intptr_t instancePtr, char * pchText, uint32 cchText); +S_API const char * SteamAPI_ISteamUtils_GetSteamUILanguage(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUtils_IsSteamRunningInVR(intptr_t instancePtr); +S_API void SteamAPI_ISteamUtils_SetOverlayNotificationInset(intptr_t instancePtr, int nHorizontalInset, int nVerticalInset); +S_API bool SteamAPI_ISteamUtils_IsSteamInBigPictureMode(intptr_t instancePtr); +S_API void SteamAPI_ISteamUtils_StartVRDashboard(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUtils_IsVRHeadsetStreamingEnabled(intptr_t instancePtr); +S_API void SteamAPI_ISteamUtils_SetVRHeadsetStreamingEnabled(intptr_t instancePtr, bool bEnabled); +S_API int SteamAPI_ISteamMatchmaking_GetFavoriteGameCount(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMatchmaking_GetFavoriteGame(intptr_t instancePtr, int iGame, AppId_t * pnAppID, uint32 * pnIP, uint16 * pnConnPort, uint16 * pnQueryPort, uint32 * punFlags, uint32 * pRTime32LastPlayedOnServer); +S_API int SteamAPI_ISteamMatchmaking_AddFavoriteGame(intptr_t instancePtr, AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQueryPort, uint32 unFlags, uint32 rTime32LastPlayedOnServer); +S_API bool SteamAPI_ISteamMatchmaking_RemoveFavoriteGame(intptr_t instancePtr, AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQueryPort, uint32 unFlags); +S_API SteamAPICall_t SteamAPI_ISteamMatchmaking_RequestLobbyList(intptr_t instancePtr); +S_API void SteamAPI_ISteamMatchmaking_AddRequestLobbyListStringFilter(intptr_t instancePtr, const char * pchKeyToMatch, const char * pchValueToMatch, ELobbyComparison eComparisonType); +S_API void SteamAPI_ISteamMatchmaking_AddRequestLobbyListNumericalFilter(intptr_t instancePtr, const char * pchKeyToMatch, int nValueToMatch, ELobbyComparison eComparisonType); +S_API void SteamAPI_ISteamMatchmaking_AddRequestLobbyListNearValueFilter(intptr_t instancePtr, const char * pchKeyToMatch, int nValueToBeCloseTo); +S_API void SteamAPI_ISteamMatchmaking_AddRequestLobbyListFilterSlotsAvailable(intptr_t instancePtr, int nSlotsAvailable); +S_API void SteamAPI_ISteamMatchmaking_AddRequestLobbyListDistanceFilter(intptr_t instancePtr, ELobbyDistanceFilter eLobbyDistanceFilter); +S_API void SteamAPI_ISteamMatchmaking_AddRequestLobbyListResultCountFilter(intptr_t instancePtr, int cMaxResults); +S_API void SteamAPI_ISteamMatchmaking_AddRequestLobbyListCompatibleMembersFilter(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API uint64 SteamAPI_ISteamMatchmaking_GetLobbyByIndex(intptr_t instancePtr, int iLobby); +S_API SteamAPICall_t SteamAPI_ISteamMatchmaking_CreateLobby(intptr_t instancePtr, ELobbyType eLobbyType, int cMaxMembers); +S_API SteamAPICall_t SteamAPI_ISteamMatchmaking_JoinLobby(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API void SteamAPI_ISteamMatchmaking_LeaveLobby(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API bool SteamAPI_ISteamMatchmaking_InviteUserToLobby(intptr_t instancePtr, class CSteamID steamIDLobby, class CSteamID steamIDInvitee); +S_API int SteamAPI_ISteamMatchmaking_GetNumLobbyMembers(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API uint64 SteamAPI_ISteamMatchmaking_GetLobbyMemberByIndex(intptr_t instancePtr, class CSteamID steamIDLobby, int iMember); +S_API const char * SteamAPI_ISteamMatchmaking_GetLobbyData(intptr_t instancePtr, class CSteamID steamIDLobby, const char * pchKey); +S_API bool SteamAPI_ISteamMatchmaking_SetLobbyData(intptr_t instancePtr, class CSteamID steamIDLobby, const char * pchKey, const char * pchValue); +S_API int SteamAPI_ISteamMatchmaking_GetLobbyDataCount(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API bool SteamAPI_ISteamMatchmaking_GetLobbyDataByIndex(intptr_t instancePtr, class CSteamID steamIDLobby, int iLobbyData, char * pchKey, int cchKeyBufferSize, char * pchValue, int cchValueBufferSize); +S_API bool SteamAPI_ISteamMatchmaking_DeleteLobbyData(intptr_t instancePtr, class CSteamID steamIDLobby, const char * pchKey); +S_API const char * SteamAPI_ISteamMatchmaking_GetLobbyMemberData(intptr_t instancePtr, class CSteamID steamIDLobby, class CSteamID steamIDUser, const char * pchKey); +S_API void SteamAPI_ISteamMatchmaking_SetLobbyMemberData(intptr_t instancePtr, class CSteamID steamIDLobby, const char * pchKey, const char * pchValue); +S_API bool SteamAPI_ISteamMatchmaking_SendLobbyChatMsg(intptr_t instancePtr, class CSteamID steamIDLobby, const void * pvMsgBody, int cubMsgBody); +S_API int SteamAPI_ISteamMatchmaking_GetLobbyChatEntry(intptr_t instancePtr, class CSteamID steamIDLobby, int iChatID, class CSteamID * pSteamIDUser, void * pvData, int cubData, EChatEntryType * peChatEntryType); +S_API bool SteamAPI_ISteamMatchmaking_RequestLobbyData(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API void SteamAPI_ISteamMatchmaking_SetLobbyGameServer(intptr_t instancePtr, class CSteamID steamIDLobby, uint32 unGameServerIP, uint16 unGameServerPort, class CSteamID steamIDGameServer); +S_API bool SteamAPI_ISteamMatchmaking_GetLobbyGameServer(intptr_t instancePtr, class CSteamID steamIDLobby, uint32 * punGameServerIP, uint16 * punGameServerPort, class CSteamID * psteamIDGameServer); +S_API bool SteamAPI_ISteamMatchmaking_SetLobbyMemberLimit(intptr_t instancePtr, class CSteamID steamIDLobby, int cMaxMembers); +S_API int SteamAPI_ISteamMatchmaking_GetLobbyMemberLimit(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API bool SteamAPI_ISteamMatchmaking_SetLobbyType(intptr_t instancePtr, class CSteamID steamIDLobby, ELobbyType eLobbyType); +S_API bool SteamAPI_ISteamMatchmaking_SetLobbyJoinable(intptr_t instancePtr, class CSteamID steamIDLobby, bool bLobbyJoinable); +S_API uint64 SteamAPI_ISteamMatchmaking_GetLobbyOwner(intptr_t instancePtr, class CSteamID steamIDLobby); +S_API bool SteamAPI_ISteamMatchmaking_SetLobbyOwner(intptr_t instancePtr, class CSteamID steamIDLobby, class CSteamID steamIDNewOwner); +S_API bool SteamAPI_ISteamMatchmaking_SetLinkedLobby(intptr_t instancePtr, class CSteamID steamIDLobby, class CSteamID steamIDLobbyDependent); +S_API void SteamAPI_ISteamMatchmakingServerListResponse_ServerResponded(intptr_t instancePtr, HServerListRequest hRequest, int iServer); +S_API void SteamAPI_ISteamMatchmakingServerListResponse_ServerFailedToRespond(intptr_t instancePtr, HServerListRequest hRequest, int iServer); +S_API void SteamAPI_ISteamMatchmakingServerListResponse_RefreshComplete(intptr_t instancePtr, HServerListRequest hRequest, EMatchMakingServerResponse response); +S_API void SteamAPI_ISteamMatchmakingPingResponse_ServerResponded(intptr_t instancePtr, class gameserveritem_t & server); +S_API void SteamAPI_ISteamMatchmakingPingResponse_ServerFailedToRespond(intptr_t instancePtr); +S_API void SteamAPI_ISteamMatchmakingPlayersResponse_AddPlayerToList(intptr_t instancePtr, const char * pchName, int nScore, float flTimePlayed); +S_API void SteamAPI_ISteamMatchmakingPlayersResponse_PlayersFailedToRespond(intptr_t instancePtr); +S_API void SteamAPI_ISteamMatchmakingPlayersResponse_PlayersRefreshComplete(intptr_t instancePtr); +S_API void SteamAPI_ISteamMatchmakingRulesResponse_RulesResponded(intptr_t instancePtr, const char * pchRule, const char * pchValue); +S_API void SteamAPI_ISteamMatchmakingRulesResponse_RulesFailedToRespond(intptr_t instancePtr); +S_API void SteamAPI_ISteamMatchmakingRulesResponse_RulesRefreshComplete(intptr_t instancePtr); +S_API HServerListRequest SteamAPI_ISteamMatchmakingServers_RequestInternetServerList(intptr_t instancePtr, AppId_t iApp, struct MatchMakingKeyValuePair_t ** ppchFilters, uint32 nFilters, class ISteamMatchmakingServerListResponse * pRequestServersResponse); +S_API HServerListRequest SteamAPI_ISteamMatchmakingServers_RequestLANServerList(intptr_t instancePtr, AppId_t iApp, class ISteamMatchmakingServerListResponse * pRequestServersResponse); +S_API HServerListRequest SteamAPI_ISteamMatchmakingServers_RequestFriendsServerList(intptr_t instancePtr, AppId_t iApp, struct MatchMakingKeyValuePair_t ** ppchFilters, uint32 nFilters, class ISteamMatchmakingServerListResponse * pRequestServersResponse); +S_API HServerListRequest SteamAPI_ISteamMatchmakingServers_RequestFavoritesServerList(intptr_t instancePtr, AppId_t iApp, struct MatchMakingKeyValuePair_t ** ppchFilters, uint32 nFilters, class ISteamMatchmakingServerListResponse * pRequestServersResponse); +S_API HServerListRequest SteamAPI_ISteamMatchmakingServers_RequestHistoryServerList(intptr_t instancePtr, AppId_t iApp, struct MatchMakingKeyValuePair_t ** ppchFilters, uint32 nFilters, class ISteamMatchmakingServerListResponse * pRequestServersResponse); +S_API HServerListRequest SteamAPI_ISteamMatchmakingServers_RequestSpectatorServerList(intptr_t instancePtr, AppId_t iApp, struct MatchMakingKeyValuePair_t ** ppchFilters, uint32 nFilters, class ISteamMatchmakingServerListResponse * pRequestServersResponse); +S_API void SteamAPI_ISteamMatchmakingServers_ReleaseRequest(intptr_t instancePtr, HServerListRequest hServerListRequest); +S_API class gameserveritem_t * SteamAPI_ISteamMatchmakingServers_GetServerDetails(intptr_t instancePtr, HServerListRequest hRequest, int iServer); +S_API void SteamAPI_ISteamMatchmakingServers_CancelQuery(intptr_t instancePtr, HServerListRequest hRequest); +S_API void SteamAPI_ISteamMatchmakingServers_RefreshQuery(intptr_t instancePtr, HServerListRequest hRequest); +S_API bool SteamAPI_ISteamMatchmakingServers_IsRefreshing(intptr_t instancePtr, HServerListRequest hRequest); +S_API int SteamAPI_ISteamMatchmakingServers_GetServerCount(intptr_t instancePtr, HServerListRequest hRequest); +S_API void SteamAPI_ISteamMatchmakingServers_RefreshServer(intptr_t instancePtr, HServerListRequest hRequest, int iServer); +S_API HServerQuery SteamAPI_ISteamMatchmakingServers_PingServer(intptr_t instancePtr, uint32 unIP, uint16 usPort, class ISteamMatchmakingPingResponse * pRequestServersResponse); +S_API HServerQuery SteamAPI_ISteamMatchmakingServers_PlayerDetails(intptr_t instancePtr, uint32 unIP, uint16 usPort, class ISteamMatchmakingPlayersResponse * pRequestServersResponse); +S_API HServerQuery SteamAPI_ISteamMatchmakingServers_ServerRules(intptr_t instancePtr, uint32 unIP, uint16 usPort, class ISteamMatchmakingRulesResponse * pRequestServersResponse); +S_API void SteamAPI_ISteamMatchmakingServers_CancelServerQuery(intptr_t instancePtr, HServerQuery hServerQuery); +S_API bool SteamAPI_ISteamRemoteStorage_FileWrite(intptr_t instancePtr, const char * pchFile, const void * pvData, int32 cubData); +S_API int32 SteamAPI_ISteamRemoteStorage_FileRead(intptr_t instancePtr, const char * pchFile, void * pvData, int32 cubDataToRead); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_FileWriteAsync(intptr_t instancePtr, const char * pchFile, const void * pvData, uint32 cubData); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_FileReadAsync(intptr_t instancePtr, const char * pchFile, uint32 nOffset, uint32 cubToRead); +S_API bool SteamAPI_ISteamRemoteStorage_FileReadAsyncComplete(intptr_t instancePtr, SteamAPICall_t hReadCall, void * pvBuffer, uint32 cubToRead); +S_API bool SteamAPI_ISteamRemoteStorage_FileForget(intptr_t instancePtr, const char * pchFile); +S_API bool SteamAPI_ISteamRemoteStorage_FileDelete(intptr_t instancePtr, const char * pchFile); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_FileShare(intptr_t instancePtr, const char * pchFile); +S_API bool SteamAPI_ISteamRemoteStorage_SetSyncPlatforms(intptr_t instancePtr, const char * pchFile, ERemoteStoragePlatform eRemoteStoragePlatform); +S_API UGCFileWriteStreamHandle_t SteamAPI_ISteamRemoteStorage_FileWriteStreamOpen(intptr_t instancePtr, const char * pchFile); +S_API bool SteamAPI_ISteamRemoteStorage_FileWriteStreamWriteChunk(intptr_t instancePtr, UGCFileWriteStreamHandle_t writeHandle, const void * pvData, int32 cubData); +S_API bool SteamAPI_ISteamRemoteStorage_FileWriteStreamClose(intptr_t instancePtr, UGCFileWriteStreamHandle_t writeHandle); +S_API bool SteamAPI_ISteamRemoteStorage_FileWriteStreamCancel(intptr_t instancePtr, UGCFileWriteStreamHandle_t writeHandle); +S_API bool SteamAPI_ISteamRemoteStorage_FileExists(intptr_t instancePtr, const char * pchFile); +S_API bool SteamAPI_ISteamRemoteStorage_FilePersisted(intptr_t instancePtr, const char * pchFile); +S_API int32 SteamAPI_ISteamRemoteStorage_GetFileSize(intptr_t instancePtr, const char * pchFile); +S_API int64 SteamAPI_ISteamRemoteStorage_GetFileTimestamp(intptr_t instancePtr, const char * pchFile); +S_API ERemoteStoragePlatform SteamAPI_ISteamRemoteStorage_GetSyncPlatforms(intptr_t instancePtr, const char * pchFile); +S_API int32 SteamAPI_ISteamRemoteStorage_GetFileCount(intptr_t instancePtr); +S_API const char * SteamAPI_ISteamRemoteStorage_GetFileNameAndSize(intptr_t instancePtr, int iFile, int32 * pnFileSizeInBytes); +S_API bool SteamAPI_ISteamRemoteStorage_GetQuota(intptr_t instancePtr, uint64 * pnTotalBytes, uint64 * puAvailableBytes); +S_API bool SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount(intptr_t instancePtr); +S_API bool SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp(intptr_t instancePtr); +S_API void SteamAPI_ISteamRemoteStorage_SetCloudEnabledForApp(intptr_t instancePtr, bool bEnabled); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_UGCDownload(intptr_t instancePtr, UGCHandle_t hContent, uint32 unPriority); +S_API bool SteamAPI_ISteamRemoteStorage_GetUGCDownloadProgress(intptr_t instancePtr, UGCHandle_t hContent, int32 * pnBytesDownloaded, int32 * pnBytesExpected); +S_API bool SteamAPI_ISteamRemoteStorage_GetUGCDetails(intptr_t instancePtr, UGCHandle_t hContent, AppId_t * pnAppID, char ** ppchName, int32 * pnFileSizeInBytes, class CSteamID * pSteamIDOwner); +S_API int32 SteamAPI_ISteamRemoteStorage_UGCRead(intptr_t instancePtr, UGCHandle_t hContent, void * pvData, int32 cubDataToRead, uint32 cOffset, EUGCReadAction eAction); +S_API int32 SteamAPI_ISteamRemoteStorage_GetCachedUGCCount(intptr_t instancePtr); +S_API UGCHandle_t SteamAPI_ISteamRemoteStorage_GetCachedUGCHandle(intptr_t instancePtr, int32 iCachedContent); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_PublishWorkshopFile(intptr_t instancePtr, const char * pchFile, const char * pchPreviewFile, AppId_t nConsumerAppId, const char * pchTitle, const char * pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, struct SteamParamStringArray_t * pTags, EWorkshopFileType eWorkshopFileType); +S_API PublishedFileUpdateHandle_t SteamAPI_ISteamRemoteStorage_CreatePublishedFileUpdateRequest(intptr_t instancePtr, PublishedFileId_t unPublishedFileId); +S_API bool SteamAPI_ISteamRemoteStorage_UpdatePublishedFileFile(intptr_t instancePtr, PublishedFileUpdateHandle_t updateHandle, const char * pchFile); +S_API bool SteamAPI_ISteamRemoteStorage_UpdatePublishedFilePreviewFile(intptr_t instancePtr, PublishedFileUpdateHandle_t updateHandle, const char * pchPreviewFile); +S_API bool SteamAPI_ISteamRemoteStorage_UpdatePublishedFileTitle(intptr_t instancePtr, PublishedFileUpdateHandle_t updateHandle, const char * pchTitle); +S_API bool SteamAPI_ISteamRemoteStorage_UpdatePublishedFileDescription(intptr_t instancePtr, PublishedFileUpdateHandle_t updateHandle, const char * pchDescription); +S_API bool SteamAPI_ISteamRemoteStorage_UpdatePublishedFileVisibility(intptr_t instancePtr, PublishedFileUpdateHandle_t updateHandle, ERemoteStoragePublishedFileVisibility eVisibility); +S_API bool SteamAPI_ISteamRemoteStorage_UpdatePublishedFileTags(intptr_t instancePtr, PublishedFileUpdateHandle_t updateHandle, struct SteamParamStringArray_t * pTags); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_CommitPublishedFileUpdate(intptr_t instancePtr, PublishedFileUpdateHandle_t updateHandle); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_GetPublishedFileDetails(intptr_t instancePtr, PublishedFileId_t unPublishedFileId, uint32 unMaxSecondsOld); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_DeletePublishedFile(intptr_t instancePtr, PublishedFileId_t unPublishedFileId); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_EnumerateUserPublishedFiles(intptr_t instancePtr, uint32 unStartIndex); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_SubscribePublishedFile(intptr_t instancePtr, PublishedFileId_t unPublishedFileId); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_EnumerateUserSubscribedFiles(intptr_t instancePtr, uint32 unStartIndex); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_UnsubscribePublishedFile(intptr_t instancePtr, PublishedFileId_t unPublishedFileId); +S_API bool SteamAPI_ISteamRemoteStorage_UpdatePublishedFileSetChangeDescription(intptr_t instancePtr, PublishedFileUpdateHandle_t updateHandle, const char * pchChangeDescription); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_GetPublishedItemVoteDetails(intptr_t instancePtr, PublishedFileId_t unPublishedFileId); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_UpdateUserPublishedItemVote(intptr_t instancePtr, PublishedFileId_t unPublishedFileId, bool bVoteUp); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_GetUserPublishedItemVoteDetails(intptr_t instancePtr, PublishedFileId_t unPublishedFileId); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_EnumerateUserSharedWorkshopFiles(intptr_t instancePtr, class CSteamID steamId, uint32 unStartIndex, struct SteamParamStringArray_t * pRequiredTags, struct SteamParamStringArray_t * pExcludedTags); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_PublishVideo(intptr_t instancePtr, EWorkshopVideoProvider eVideoProvider, const char * pchVideoAccount, const char * pchVideoIdentifier, const char * pchPreviewFile, AppId_t nConsumerAppId, const char * pchTitle, const char * pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, struct SteamParamStringArray_t * pTags); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_SetUserPublishedFileAction(intptr_t instancePtr, PublishedFileId_t unPublishedFileId, EWorkshopFileAction eAction); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_EnumeratePublishedFilesByUserAction(intptr_t instancePtr, EWorkshopFileAction eAction, uint32 unStartIndex); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_EnumeratePublishedWorkshopFiles(intptr_t instancePtr, EWorkshopEnumerationType eEnumerationType, uint32 unStartIndex, uint32 unCount, uint32 unDays, struct SteamParamStringArray_t * pTags, struct SteamParamStringArray_t * pUserTags); +S_API SteamAPICall_t SteamAPI_ISteamRemoteStorage_UGCDownloadToLocation(intptr_t instancePtr, UGCHandle_t hContent, const char * pchLocation, uint32 unPriority); +S_API bool SteamAPI_ISteamUserStats_RequestCurrentStats(intptr_t instancePtr); +S_API bool SteamAPI_ISteamUserStats_GetStat(intptr_t instancePtr, const char * pchName, int32 * pData); +S_API bool SteamAPI_ISteamUserStats_GetStat0(intptr_t instancePtr, const char * pchName, float * pData); +S_API bool SteamAPI_ISteamUserStats_SetStat(intptr_t instancePtr, const char * pchName, int32 nData); +S_API bool SteamAPI_ISteamUserStats_SetStat0(intptr_t instancePtr, const char * pchName, float fData); +S_API bool SteamAPI_ISteamUserStats_UpdateAvgRateStat(intptr_t instancePtr, const char * pchName, float flCountThisSession, double dSessionLength); +S_API bool SteamAPI_ISteamUserStats_GetAchievement(intptr_t instancePtr, const char * pchName, bool * pbAchieved); +S_API bool SteamAPI_ISteamUserStats_SetAchievement(intptr_t instancePtr, const char * pchName); +S_API bool SteamAPI_ISteamUserStats_ClearAchievement(intptr_t instancePtr, const char * pchName); +S_API bool SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime(intptr_t instancePtr, const char * pchName, bool * pbAchieved, uint32 * punUnlockTime); +S_API bool SteamAPI_ISteamUserStats_StoreStats(intptr_t instancePtr); +S_API int SteamAPI_ISteamUserStats_GetAchievementIcon(intptr_t instancePtr, const char * pchName); +S_API const char * SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(intptr_t instancePtr, const char * pchName, const char * pchKey); +S_API bool SteamAPI_ISteamUserStats_IndicateAchievementProgress(intptr_t instancePtr, const char * pchName, uint32 nCurProgress, uint32 nMaxProgress); +S_API uint32 SteamAPI_ISteamUserStats_GetNumAchievements(intptr_t instancePtr); +S_API const char * SteamAPI_ISteamUserStats_GetAchievementName(intptr_t instancePtr, uint32 iAchievement); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_RequestUserStats(intptr_t instancePtr, class CSteamID steamIDUser); +S_API bool SteamAPI_ISteamUserStats_GetUserStat(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, int32 * pData); +S_API bool SteamAPI_ISteamUserStats_GetUserStat0(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, float * pData); +S_API bool SteamAPI_ISteamUserStats_GetUserAchievement(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, bool * pbAchieved); +S_API bool SteamAPI_ISteamUserStats_GetUserAchievementAndUnlockTime(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, bool * pbAchieved, uint32 * punUnlockTime); +S_API bool SteamAPI_ISteamUserStats_ResetAllStats(intptr_t instancePtr, bool bAchievementsToo); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_FindOrCreateLeaderboard(intptr_t instancePtr, const char * pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_FindLeaderboard(intptr_t instancePtr, const char * pchLeaderboardName); +S_API const char * SteamAPI_ISteamUserStats_GetLeaderboardName(intptr_t instancePtr, SteamLeaderboard_t hSteamLeaderboard); +S_API int SteamAPI_ISteamUserStats_GetLeaderboardEntryCount(intptr_t instancePtr, SteamLeaderboard_t hSteamLeaderboard); +S_API ELeaderboardSortMethod SteamAPI_ISteamUserStats_GetLeaderboardSortMethod(intptr_t instancePtr, SteamLeaderboard_t hSteamLeaderboard); +S_API ELeaderboardDisplayType SteamAPI_ISteamUserStats_GetLeaderboardDisplayType(intptr_t instancePtr, SteamLeaderboard_t hSteamLeaderboard); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_DownloadLeaderboardEntries(intptr_t instancePtr, SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_DownloadLeaderboardEntriesForUsers(intptr_t instancePtr, SteamLeaderboard_t hSteamLeaderboard, class CSteamID * prgUsers, int cUsers); +S_API bool SteamAPI_ISteamUserStats_GetDownloadedLeaderboardEntry(intptr_t instancePtr, SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, struct LeaderboardEntry_t * pLeaderboardEntry, int32 * pDetails, int cDetailsMax); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_UploadLeaderboardScore(intptr_t instancePtr, SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 * pScoreDetails, int cScoreDetailsCount); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_AttachLeaderboardUGC(intptr_t instancePtr, SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_GetNumberOfCurrentPlayers(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_RequestGlobalAchievementPercentages(intptr_t instancePtr); +S_API int SteamAPI_ISteamUserStats_GetMostAchievedAchievementInfo(intptr_t instancePtr, char * pchName, uint32 unNameBufLen, float * pflPercent, bool * pbAchieved); +S_API int SteamAPI_ISteamUserStats_GetNextMostAchievedAchievementInfo(intptr_t instancePtr, int iIteratorPrevious, char * pchName, uint32 unNameBufLen, float * pflPercent, bool * pbAchieved); +S_API bool SteamAPI_ISteamUserStats_GetAchievementAchievedPercent(intptr_t instancePtr, const char * pchName, float * pflPercent); +S_API SteamAPICall_t SteamAPI_ISteamUserStats_RequestGlobalStats(intptr_t instancePtr, int nHistoryDays); +S_API bool SteamAPI_ISteamUserStats_GetGlobalStat(intptr_t instancePtr, const char * pchStatName, int64 * pData); +S_API bool SteamAPI_ISteamUserStats_GetGlobalStat0(intptr_t instancePtr, const char * pchStatName, double * pData); +S_API int32 SteamAPI_ISteamUserStats_GetGlobalStatHistory(intptr_t instancePtr, const char * pchStatName, int64 * pData, uint32 cubData); +S_API int32 SteamAPI_ISteamUserStats_GetGlobalStatHistory0(intptr_t instancePtr, const char * pchStatName, double * pData, uint32 cubData); +S_API bool SteamAPI_ISteamApps_BIsSubscribed(intptr_t instancePtr); +S_API bool SteamAPI_ISteamApps_BIsLowViolence(intptr_t instancePtr); +S_API bool SteamAPI_ISteamApps_BIsCybercafe(intptr_t instancePtr); +S_API bool SteamAPI_ISteamApps_BIsVACBanned(intptr_t instancePtr); +S_API const char * SteamAPI_ISteamApps_GetCurrentGameLanguage(intptr_t instancePtr); +S_API const char * SteamAPI_ISteamApps_GetAvailableGameLanguages(intptr_t instancePtr); +S_API bool SteamAPI_ISteamApps_BIsSubscribedApp(intptr_t instancePtr, AppId_t appID); +S_API bool SteamAPI_ISteamApps_BIsDlcInstalled(intptr_t instancePtr, AppId_t appID); +S_API uint32 SteamAPI_ISteamApps_GetEarliestPurchaseUnixTime(intptr_t instancePtr, AppId_t nAppID); +S_API bool SteamAPI_ISteamApps_BIsSubscribedFromFreeWeekend(intptr_t instancePtr); +S_API int SteamAPI_ISteamApps_GetDLCCount(intptr_t instancePtr); +S_API bool SteamAPI_ISteamApps_BGetDLCDataByIndex(intptr_t instancePtr, int iDLC, AppId_t * pAppID, bool * pbAvailable, char * pchName, int cchNameBufferSize); +S_API void SteamAPI_ISteamApps_InstallDLC(intptr_t instancePtr, AppId_t nAppID); +S_API void SteamAPI_ISteamApps_UninstallDLC(intptr_t instancePtr, AppId_t nAppID); +S_API void SteamAPI_ISteamApps_RequestAppProofOfPurchaseKey(intptr_t instancePtr, AppId_t nAppID); +S_API bool SteamAPI_ISteamApps_GetCurrentBetaName(intptr_t instancePtr, char * pchName, int cchNameBufferSize); +S_API bool SteamAPI_ISteamApps_MarkContentCorrupt(intptr_t instancePtr, bool bMissingFilesOnly); +S_API uint32 SteamAPI_ISteamApps_GetInstalledDepots(intptr_t instancePtr, AppId_t appID, DepotId_t * pvecDepots, uint32 cMaxDepots); +S_API uint32 SteamAPI_ISteamApps_GetAppInstallDir(intptr_t instancePtr, AppId_t appID, char * pchFolder, uint32 cchFolderBufferSize); +S_API bool SteamAPI_ISteamApps_BIsAppInstalled(intptr_t instancePtr, AppId_t appID); +S_API uint64 SteamAPI_ISteamApps_GetAppOwner(intptr_t instancePtr); +S_API const char * SteamAPI_ISteamApps_GetLaunchQueryParam(intptr_t instancePtr, const char * pchKey); +S_API bool SteamAPI_ISteamApps_GetDlcDownloadProgress(intptr_t instancePtr, AppId_t nAppID, uint64 * punBytesDownloaded, uint64 * punBytesTotal); +S_API int SteamAPI_ISteamApps_GetAppBuildId(intptr_t instancePtr); +S_API void SteamAPI_ISteamApps_RequestAllProofOfPurchaseKeys(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamApps_GetFileDetails(intptr_t instancePtr, const char * pszFileName); +S_API bool SteamAPI_ISteamNetworking_SendP2PPacket(intptr_t instancePtr, class CSteamID steamIDRemote, const void * pubData, uint32 cubData, EP2PSend eP2PSendType, int nChannel); +S_API bool SteamAPI_ISteamNetworking_IsP2PPacketAvailable(intptr_t instancePtr, uint32 * pcubMsgSize, int nChannel); +S_API bool SteamAPI_ISteamNetworking_ReadP2PPacket(intptr_t instancePtr, void * pubDest, uint32 cubDest, uint32 * pcubMsgSize, class CSteamID * psteamIDRemote, int nChannel); +S_API bool SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser(intptr_t instancePtr, class CSteamID steamIDRemote); +S_API bool SteamAPI_ISteamNetworking_CloseP2PSessionWithUser(intptr_t instancePtr, class CSteamID steamIDRemote); +S_API bool SteamAPI_ISteamNetworking_CloseP2PChannelWithUser(intptr_t instancePtr, class CSteamID steamIDRemote, int nChannel); +S_API bool SteamAPI_ISteamNetworking_GetP2PSessionState(intptr_t instancePtr, class CSteamID steamIDRemote, struct P2PSessionState_t * pConnectionState); +S_API bool SteamAPI_ISteamNetworking_AllowP2PPacketRelay(intptr_t instancePtr, bool bAllow); +S_API SNetListenSocket_t SteamAPI_ISteamNetworking_CreateListenSocket(intptr_t instancePtr, int nVirtualP2PPort, uint32 nIP, uint16 nPort, bool bAllowUseOfPacketRelay); +S_API SNetSocket_t SteamAPI_ISteamNetworking_CreateP2PConnectionSocket(intptr_t instancePtr, class CSteamID steamIDTarget, int nVirtualPort, int nTimeoutSec, bool bAllowUseOfPacketRelay); +S_API SNetSocket_t SteamAPI_ISteamNetworking_CreateConnectionSocket(intptr_t instancePtr, uint32 nIP, uint16 nPort, int nTimeoutSec); +S_API bool SteamAPI_ISteamNetworking_DestroySocket(intptr_t instancePtr, SNetSocket_t hSocket, bool bNotifyRemoteEnd); +S_API bool SteamAPI_ISteamNetworking_DestroyListenSocket(intptr_t instancePtr, SNetListenSocket_t hSocket, bool bNotifyRemoteEnd); +S_API bool SteamAPI_ISteamNetworking_SendDataOnSocket(intptr_t instancePtr, SNetSocket_t hSocket, void * pubData, uint32 cubData, bool bReliable); +S_API bool SteamAPI_ISteamNetworking_IsDataAvailableOnSocket(intptr_t instancePtr, SNetSocket_t hSocket, uint32 * pcubMsgSize); +S_API bool SteamAPI_ISteamNetworking_RetrieveDataFromSocket(intptr_t instancePtr, SNetSocket_t hSocket, void * pubDest, uint32 cubDest, uint32 * pcubMsgSize); +S_API bool SteamAPI_ISteamNetworking_IsDataAvailable(intptr_t instancePtr, SNetListenSocket_t hListenSocket, uint32 * pcubMsgSize, SNetSocket_t * phSocket); +S_API bool SteamAPI_ISteamNetworking_RetrieveData(intptr_t instancePtr, SNetListenSocket_t hListenSocket, void * pubDest, uint32 cubDest, uint32 * pcubMsgSize, SNetSocket_t * phSocket); +S_API bool SteamAPI_ISteamNetworking_GetSocketInfo(intptr_t instancePtr, SNetSocket_t hSocket, class CSteamID * pSteamIDRemote, int * peSocketStatus, uint32 * punIPRemote, uint16 * punPortRemote); +S_API bool SteamAPI_ISteamNetworking_GetListenSocketInfo(intptr_t instancePtr, SNetListenSocket_t hListenSocket, uint32 * pnIP, uint16 * pnPort); +S_API ESNetSocketConnectionType SteamAPI_ISteamNetworking_GetSocketConnectionType(intptr_t instancePtr, SNetSocket_t hSocket); +S_API int SteamAPI_ISteamNetworking_GetMaxPacketSize(intptr_t instancePtr, SNetSocket_t hSocket); +S_API ScreenshotHandle SteamAPI_ISteamScreenshots_WriteScreenshot(intptr_t instancePtr, void * pubRGB, uint32 cubRGB, int nWidth, int nHeight); +S_API ScreenshotHandle SteamAPI_ISteamScreenshots_AddScreenshotToLibrary(intptr_t instancePtr, const char * pchFilename, const char * pchThumbnailFilename, int nWidth, int nHeight); +S_API void SteamAPI_ISteamScreenshots_TriggerScreenshot(intptr_t instancePtr); +S_API void SteamAPI_ISteamScreenshots_HookScreenshots(intptr_t instancePtr, bool bHook); +S_API bool SteamAPI_ISteamScreenshots_SetLocation(intptr_t instancePtr, ScreenshotHandle hScreenshot, const char * pchLocation); +S_API bool SteamAPI_ISteamScreenshots_TagUser(intptr_t instancePtr, ScreenshotHandle hScreenshot, class CSteamID steamID); +S_API bool SteamAPI_ISteamScreenshots_TagPublishedFile(intptr_t instancePtr, ScreenshotHandle hScreenshot, PublishedFileId_t unPublishedFileID); +S_API bool SteamAPI_ISteamScreenshots_IsScreenshotsHooked(intptr_t instancePtr); +S_API ScreenshotHandle SteamAPI_ISteamScreenshots_AddVRScreenshotToLibrary(intptr_t instancePtr, EVRScreenshotType eType, const char * pchFilename, const char * pchVRFilename); +S_API bool SteamAPI_ISteamMusic_BIsEnabled(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusic_BIsPlaying(intptr_t instancePtr); +S_API AudioPlayback_Status SteamAPI_ISteamMusic_GetPlaybackStatus(intptr_t instancePtr); +S_API void SteamAPI_ISteamMusic_Play(intptr_t instancePtr); +S_API void SteamAPI_ISteamMusic_Pause(intptr_t instancePtr); +S_API void SteamAPI_ISteamMusic_PlayPrevious(intptr_t instancePtr); +S_API void SteamAPI_ISteamMusic_PlayNext(intptr_t instancePtr); +S_API void SteamAPI_ISteamMusic_SetVolume(intptr_t instancePtr, float flVolume); +S_API float SteamAPI_ISteamMusic_GetVolume(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_RegisterSteamMusicRemote(intptr_t instancePtr, const char * pchName); +S_API bool SteamAPI_ISteamMusicRemote_DeregisterSteamMusicRemote(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_BIsCurrentMusicRemote(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_BActivationSuccess(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_SetDisplayName(intptr_t instancePtr, const char * pchDisplayName); +S_API bool SteamAPI_ISteamMusicRemote_SetPNGIcon_64x64(intptr_t instancePtr, void * pvBuffer, uint32 cbBufferLength); +S_API bool SteamAPI_ISteamMusicRemote_EnablePlayPrevious(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_EnablePlayNext(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_EnableShuffled(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_EnableLooped(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_EnableQueue(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_EnablePlaylists(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_UpdatePlaybackStatus(intptr_t instancePtr, AudioPlayback_Status nStatus); +S_API bool SteamAPI_ISteamMusicRemote_UpdateShuffled(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_UpdateLooped(intptr_t instancePtr, bool bValue); +S_API bool SteamAPI_ISteamMusicRemote_UpdateVolume(intptr_t instancePtr, float flValue); +S_API bool SteamAPI_ISteamMusicRemote_CurrentEntryWillChange(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_CurrentEntryIsAvailable(intptr_t instancePtr, bool bAvailable); +S_API bool SteamAPI_ISteamMusicRemote_UpdateCurrentEntryText(intptr_t instancePtr, const char * pchText); +S_API bool SteamAPI_ISteamMusicRemote_UpdateCurrentEntryElapsedSeconds(intptr_t instancePtr, int nValue); +S_API bool SteamAPI_ISteamMusicRemote_UpdateCurrentEntryCoverArt(intptr_t instancePtr, void * pvBuffer, uint32 cbBufferLength); +S_API bool SteamAPI_ISteamMusicRemote_CurrentEntryDidChange(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_QueueWillChange(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_ResetQueueEntries(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_SetQueueEntry(intptr_t instancePtr, int nID, int nPosition, const char * pchEntryText); +S_API bool SteamAPI_ISteamMusicRemote_SetCurrentQueueEntry(intptr_t instancePtr, int nID); +S_API bool SteamAPI_ISteamMusicRemote_QueueDidChange(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_PlaylistWillChange(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_ResetPlaylistEntries(intptr_t instancePtr); +S_API bool SteamAPI_ISteamMusicRemote_SetPlaylistEntry(intptr_t instancePtr, int nID, int nPosition, const char * pchEntryText); +S_API bool SteamAPI_ISteamMusicRemote_SetCurrentPlaylistEntry(intptr_t instancePtr, int nID); +S_API bool SteamAPI_ISteamMusicRemote_PlaylistDidChange(intptr_t instancePtr); +S_API HTTPRequestHandle SteamAPI_ISteamHTTP_CreateHTTPRequest(intptr_t instancePtr, EHTTPMethod eHTTPRequestMethod, const char * pchAbsoluteURL); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestContextValue(intptr_t instancePtr, HTTPRequestHandle hRequest, uint64 ulContextValue); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestNetworkActivityTimeout(intptr_t instancePtr, HTTPRequestHandle hRequest, uint32 unTimeoutSeconds); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestHeaderValue(intptr_t instancePtr, HTTPRequestHandle hRequest, const char * pchHeaderName, const char * pchHeaderValue); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestGetOrPostParameter(intptr_t instancePtr, HTTPRequestHandle hRequest, const char * pchParamName, const char * pchParamValue); +S_API bool SteamAPI_ISteamHTTP_SendHTTPRequest(intptr_t instancePtr, HTTPRequestHandle hRequest, SteamAPICall_t * pCallHandle); +S_API bool SteamAPI_ISteamHTTP_SendHTTPRequestAndStreamResponse(intptr_t instancePtr, HTTPRequestHandle hRequest, SteamAPICall_t * pCallHandle); +S_API bool SteamAPI_ISteamHTTP_DeferHTTPRequest(intptr_t instancePtr, HTTPRequestHandle hRequest); +S_API bool SteamAPI_ISteamHTTP_PrioritizeHTTPRequest(intptr_t instancePtr, HTTPRequestHandle hRequest); +S_API bool SteamAPI_ISteamHTTP_GetHTTPResponseHeaderSize(intptr_t instancePtr, HTTPRequestHandle hRequest, const char * pchHeaderName, uint32 * unResponseHeaderSize); +S_API bool SteamAPI_ISteamHTTP_GetHTTPResponseHeaderValue(intptr_t instancePtr, HTTPRequestHandle hRequest, const char * pchHeaderName, uint8 * pHeaderValueBuffer, uint32 unBufferSize); +S_API bool SteamAPI_ISteamHTTP_GetHTTPResponseBodySize(intptr_t instancePtr, HTTPRequestHandle hRequest, uint32 * unBodySize); +S_API bool SteamAPI_ISteamHTTP_GetHTTPResponseBodyData(intptr_t instancePtr, HTTPRequestHandle hRequest, uint8 * pBodyDataBuffer, uint32 unBufferSize); +S_API bool SteamAPI_ISteamHTTP_GetHTTPStreamingResponseBodyData(intptr_t instancePtr, HTTPRequestHandle hRequest, uint32 cOffset, uint8 * pBodyDataBuffer, uint32 unBufferSize); +S_API bool SteamAPI_ISteamHTTP_ReleaseHTTPRequest(intptr_t instancePtr, HTTPRequestHandle hRequest); +S_API bool SteamAPI_ISteamHTTP_GetHTTPDownloadProgressPct(intptr_t instancePtr, HTTPRequestHandle hRequest, float * pflPercentOut); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestRawPostBody(intptr_t instancePtr, HTTPRequestHandle hRequest, const char * pchContentType, uint8 * pubBody, uint32 unBodyLen); +S_API HTTPCookieContainerHandle SteamAPI_ISteamHTTP_CreateCookieContainer(intptr_t instancePtr, bool bAllowResponsesToModify); +S_API bool SteamAPI_ISteamHTTP_ReleaseCookieContainer(intptr_t instancePtr, HTTPCookieContainerHandle hCookieContainer); +S_API bool SteamAPI_ISteamHTTP_SetCookie(intptr_t instancePtr, HTTPCookieContainerHandle hCookieContainer, const char * pchHost, const char * pchUrl, const char * pchCookie); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestCookieContainer(intptr_t instancePtr, HTTPRequestHandle hRequest, HTTPCookieContainerHandle hCookieContainer); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestUserAgentInfo(intptr_t instancePtr, HTTPRequestHandle hRequest, const char * pchUserAgentInfo); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestRequiresVerifiedCertificate(intptr_t instancePtr, HTTPRequestHandle hRequest, bool bRequireVerifiedCertificate); +S_API bool SteamAPI_ISteamHTTP_SetHTTPRequestAbsoluteTimeoutMS(intptr_t instancePtr, HTTPRequestHandle hRequest, uint32 unMilliseconds); +S_API bool SteamAPI_ISteamHTTP_GetHTTPRequestWasTimedOut(intptr_t instancePtr, HTTPRequestHandle hRequest, bool * pbWasTimedOut); +S_API ClientUnifiedMessageHandle SteamAPI_ISteamUnifiedMessages_SendMethod(intptr_t instancePtr, const char * pchServiceMethod, const void * pRequestBuffer, uint32 unRequestBufferSize, uint64 unContext); +S_API bool SteamAPI_ISteamUnifiedMessages_GetMethodResponseInfo(intptr_t instancePtr, ClientUnifiedMessageHandle hHandle, uint32 * punResponseSize, EResult * peResult); +S_API bool SteamAPI_ISteamUnifiedMessages_GetMethodResponseData(intptr_t instancePtr, ClientUnifiedMessageHandle hHandle, void * pResponseBuffer, uint32 unResponseBufferSize, bool bAutoRelease); +S_API bool SteamAPI_ISteamUnifiedMessages_ReleaseMethod(intptr_t instancePtr, ClientUnifiedMessageHandle hHandle); +S_API bool SteamAPI_ISteamUnifiedMessages_SendNotification(intptr_t instancePtr, const char * pchServiceNotification, const void * pNotificationBuffer, uint32 unNotificationBufferSize); +S_API bool SteamAPI_ISteamController_Init(intptr_t instancePtr); +S_API bool SteamAPI_ISteamController_Shutdown(intptr_t instancePtr); +S_API void SteamAPI_ISteamController_RunFrame(intptr_t instancePtr); +S_API int SteamAPI_ISteamController_GetConnectedControllers(intptr_t instancePtr, ControllerHandle_t * handlesOut); +S_API bool SteamAPI_ISteamController_ShowBindingPanel(intptr_t instancePtr, ControllerHandle_t controllerHandle); +S_API ControllerActionSetHandle_t SteamAPI_ISteamController_GetActionSetHandle(intptr_t instancePtr, const char * pszActionSetName); +S_API void SteamAPI_ISteamController_ActivateActionSet(intptr_t instancePtr, ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle); +S_API ControllerActionSetHandle_t SteamAPI_ISteamController_GetCurrentActionSet(intptr_t instancePtr, ControllerHandle_t controllerHandle); +S_API ControllerDigitalActionHandle_t SteamAPI_ISteamController_GetDigitalActionHandle(intptr_t instancePtr, const char * pszActionName); +S_API struct ControllerDigitalActionData_t SteamAPI_ISteamController_GetDigitalActionData(intptr_t instancePtr, ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle); +S_API int SteamAPI_ISteamController_GetDigitalActionOrigins(intptr_t instancePtr, ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin * originsOut); +S_API ControllerAnalogActionHandle_t SteamAPI_ISteamController_GetAnalogActionHandle(intptr_t instancePtr, const char * pszActionName); +S_API struct ControllerAnalogActionData_t SteamAPI_ISteamController_GetAnalogActionData(intptr_t instancePtr, ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle); +S_API int SteamAPI_ISteamController_GetAnalogActionOrigins(intptr_t instancePtr, ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin * originsOut); +S_API void SteamAPI_ISteamController_StopAnalogActionMomentum(intptr_t instancePtr, ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction); +S_API void SteamAPI_ISteamController_TriggerHapticPulse(intptr_t instancePtr, ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec); +S_API void SteamAPI_ISteamController_TriggerRepeatedHapticPulse(intptr_t instancePtr, ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags); +S_API void SteamAPI_ISteamController_TriggerVibration(intptr_t instancePtr, ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed); +S_API void SteamAPI_ISteamController_SetLEDColor(intptr_t instancePtr, ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags); +S_API int SteamAPI_ISteamController_GetGamepadIndexForController(intptr_t instancePtr, ControllerHandle_t ulControllerHandle); +S_API ControllerHandle_t SteamAPI_ISteamController_GetControllerForGamepadIndex(intptr_t instancePtr, int nIndex); +S_API struct ControllerMotionData_t SteamAPI_ISteamController_GetMotionData(intptr_t instancePtr, ControllerHandle_t controllerHandle); +S_API bool SteamAPI_ISteamController_ShowDigitalActionOrigins(intptr_t instancePtr, ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition); +S_API bool SteamAPI_ISteamController_ShowAnalogActionOrigins(intptr_t instancePtr, ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition); +S_API const char * SteamAPI_ISteamController_GetStringForActionOrigin(intptr_t instancePtr, EControllerActionOrigin eOrigin); +S_API const char * SteamAPI_ISteamController_GetGlyphForActionOrigin(intptr_t instancePtr, EControllerActionOrigin eOrigin); +S_API UGCQueryHandle_t SteamAPI_ISteamUGC_CreateQueryUserUGCRequest(intptr_t instancePtr, AccountID_t unAccountID, EUserUGCList eListType, EUGCMatchingUGCType eMatchingUGCType, EUserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage); +S_API UGCQueryHandle_t SteamAPI_ISteamUGC_CreateQueryAllUGCRequest(intptr_t instancePtr, EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage); +S_API UGCQueryHandle_t SteamAPI_ISteamUGC_CreateQueryUGCDetailsRequest(intptr_t instancePtr, PublishedFileId_t * pvecPublishedFileID, uint32 unNumPublishedFileIDs); +S_API SteamAPICall_t SteamAPI_ISteamUGC_SendQueryUGCRequest(intptr_t instancePtr, UGCQueryHandle_t handle); +S_API bool SteamAPI_ISteamUGC_GetQueryUGCResult(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index, struct SteamUGCDetails_t * pDetails); +S_API bool SteamAPI_ISteamUGC_GetQueryUGCPreviewURL(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index, char * pchURL, uint32 cchURLSize); +S_API bool SteamAPI_ISteamUGC_GetQueryUGCMetadata(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index, char * pchMetadata, uint32 cchMetadatasize); +S_API bool SteamAPI_ISteamUGC_GetQueryUGCChildren(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index, PublishedFileId_t * pvecPublishedFileID, uint32 cMaxEntries); +S_API bool SteamAPI_ISteamUGC_GetQueryUGCStatistic(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index, EItemStatistic eStatType, uint64 * pStatValue); +S_API uint32 SteamAPI_ISteamUGC_GetQueryUGCNumAdditionalPreviews(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index); +S_API bool SteamAPI_ISteamUGC_GetQueryUGCAdditionalPreview(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index, uint32 previewIndex, char * pchURLOrVideoID, uint32 cchURLSize, char * pchOriginalFileName, uint32 cchOriginalFileNameSize, EItemPreviewType * pPreviewType); +S_API uint32 SteamAPI_ISteamUGC_GetQueryUGCNumKeyValueTags(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index); +S_API bool SteamAPI_ISteamUGC_GetQueryUGCKeyValueTag(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 index, uint32 keyValueTagIndex, char * pchKey, uint32 cchKeySize, char * pchValue, uint32 cchValueSize); +S_API bool SteamAPI_ISteamUGC_ReleaseQueryUGCRequest(intptr_t instancePtr, UGCQueryHandle_t handle); +S_API bool SteamAPI_ISteamUGC_AddRequiredTag(intptr_t instancePtr, UGCQueryHandle_t handle, const char * pTagName); +S_API bool SteamAPI_ISteamUGC_AddExcludedTag(intptr_t instancePtr, UGCQueryHandle_t handle, const char * pTagName); +S_API bool SteamAPI_ISteamUGC_SetReturnOnlyIDs(intptr_t instancePtr, UGCQueryHandle_t handle, bool bReturnOnlyIDs); +S_API bool SteamAPI_ISteamUGC_SetReturnKeyValueTags(intptr_t instancePtr, UGCQueryHandle_t handle, bool bReturnKeyValueTags); +S_API bool SteamAPI_ISteamUGC_SetReturnLongDescription(intptr_t instancePtr, UGCQueryHandle_t handle, bool bReturnLongDescription); +S_API bool SteamAPI_ISteamUGC_SetReturnMetadata(intptr_t instancePtr, UGCQueryHandle_t handle, bool bReturnMetadata); +S_API bool SteamAPI_ISteamUGC_SetReturnChildren(intptr_t instancePtr, UGCQueryHandle_t handle, bool bReturnChildren); +S_API bool SteamAPI_ISteamUGC_SetReturnAdditionalPreviews(intptr_t instancePtr, UGCQueryHandle_t handle, bool bReturnAdditionalPreviews); +S_API bool SteamAPI_ISteamUGC_SetReturnTotalOnly(intptr_t instancePtr, UGCQueryHandle_t handle, bool bReturnTotalOnly); +S_API bool SteamAPI_ISteamUGC_SetReturnPlaytimeStats(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 unDays); +S_API bool SteamAPI_ISteamUGC_SetLanguage(intptr_t instancePtr, UGCQueryHandle_t handle, const char * pchLanguage); +S_API bool SteamAPI_ISteamUGC_SetAllowCachedResponse(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 unMaxAgeSeconds); +S_API bool SteamAPI_ISteamUGC_SetCloudFileNameFilter(intptr_t instancePtr, UGCQueryHandle_t handle, const char * pMatchCloudFileName); +S_API bool SteamAPI_ISteamUGC_SetMatchAnyTag(intptr_t instancePtr, UGCQueryHandle_t handle, bool bMatchAnyTag); +S_API bool SteamAPI_ISteamUGC_SetSearchText(intptr_t instancePtr, UGCQueryHandle_t handle, const char * pSearchText); +S_API bool SteamAPI_ISteamUGC_SetRankedByTrendDays(intptr_t instancePtr, UGCQueryHandle_t handle, uint32 unDays); +S_API bool SteamAPI_ISteamUGC_AddRequiredKeyValueTag(intptr_t instancePtr, UGCQueryHandle_t handle, const char * pKey, const char * pValue); +S_API SteamAPICall_t SteamAPI_ISteamUGC_RequestUGCDetails(intptr_t instancePtr, PublishedFileId_t nPublishedFileID, uint32 unMaxAgeSeconds); +S_API SteamAPICall_t SteamAPI_ISteamUGC_CreateItem(intptr_t instancePtr, AppId_t nConsumerAppId, EWorkshopFileType eFileType); +S_API UGCUpdateHandle_t SteamAPI_ISteamUGC_StartItemUpdate(intptr_t instancePtr, AppId_t nConsumerAppId, PublishedFileId_t nPublishedFileID); +S_API bool SteamAPI_ISteamUGC_SetItemTitle(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pchTitle); +S_API bool SteamAPI_ISteamUGC_SetItemDescription(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pchDescription); +S_API bool SteamAPI_ISteamUGC_SetItemUpdateLanguage(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pchLanguage); +S_API bool SteamAPI_ISteamUGC_SetItemMetadata(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pchMetaData); +S_API bool SteamAPI_ISteamUGC_SetItemVisibility(intptr_t instancePtr, UGCUpdateHandle_t handle, ERemoteStoragePublishedFileVisibility eVisibility); +S_API bool SteamAPI_ISteamUGC_SetItemTags(intptr_t instancePtr, UGCUpdateHandle_t updateHandle, const struct SteamParamStringArray_t * pTags); +S_API bool SteamAPI_ISteamUGC_SetItemContent(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pszContentFolder); +S_API bool SteamAPI_ISteamUGC_SetItemPreview(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pszPreviewFile); +S_API bool SteamAPI_ISteamUGC_RemoveItemKeyValueTags(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pchKey); +S_API bool SteamAPI_ISteamUGC_AddItemKeyValueTag(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pchKey, const char * pchValue); +S_API bool SteamAPI_ISteamUGC_AddItemPreviewFile(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pszPreviewFile, EItemPreviewType type); +S_API bool SteamAPI_ISteamUGC_AddItemPreviewVideo(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pszVideoID); +S_API bool SteamAPI_ISteamUGC_UpdateItemPreviewFile(intptr_t instancePtr, UGCUpdateHandle_t handle, uint32 index, const char * pszPreviewFile); +S_API bool SteamAPI_ISteamUGC_UpdateItemPreviewVideo(intptr_t instancePtr, UGCUpdateHandle_t handle, uint32 index, const char * pszVideoID); +S_API bool SteamAPI_ISteamUGC_RemoveItemPreview(intptr_t instancePtr, UGCUpdateHandle_t handle, uint32 index); +S_API SteamAPICall_t SteamAPI_ISteamUGC_SubmitItemUpdate(intptr_t instancePtr, UGCUpdateHandle_t handle, const char * pchChangeNote); +S_API EItemUpdateStatus SteamAPI_ISteamUGC_GetItemUpdateProgress(intptr_t instancePtr, UGCUpdateHandle_t handle, uint64 * punBytesProcessed, uint64 * punBytesTotal); +S_API SteamAPICall_t SteamAPI_ISteamUGC_SetUserItemVote(intptr_t instancePtr, PublishedFileId_t nPublishedFileID, bool bVoteUp); +S_API SteamAPICall_t SteamAPI_ISteamUGC_GetUserItemVote(intptr_t instancePtr, PublishedFileId_t nPublishedFileID); +S_API SteamAPICall_t SteamAPI_ISteamUGC_AddItemToFavorites(intptr_t instancePtr, AppId_t nAppId, PublishedFileId_t nPublishedFileID); +S_API SteamAPICall_t SteamAPI_ISteamUGC_RemoveItemFromFavorites(intptr_t instancePtr, AppId_t nAppId, PublishedFileId_t nPublishedFileID); +S_API SteamAPICall_t SteamAPI_ISteamUGC_SubscribeItem(intptr_t instancePtr, PublishedFileId_t nPublishedFileID); +S_API SteamAPICall_t SteamAPI_ISteamUGC_UnsubscribeItem(intptr_t instancePtr, PublishedFileId_t nPublishedFileID); +S_API uint32 SteamAPI_ISteamUGC_GetNumSubscribedItems(intptr_t instancePtr); +S_API uint32 SteamAPI_ISteamUGC_GetSubscribedItems(intptr_t instancePtr, PublishedFileId_t * pvecPublishedFileID, uint32 cMaxEntries); +S_API uint32 SteamAPI_ISteamUGC_GetItemState(intptr_t instancePtr, PublishedFileId_t nPublishedFileID); +S_API bool SteamAPI_ISteamUGC_GetItemInstallInfo(intptr_t instancePtr, PublishedFileId_t nPublishedFileID, uint64 * punSizeOnDisk, char * pchFolder, uint32 cchFolderSize, uint32 * punTimeStamp); +S_API bool SteamAPI_ISteamUGC_GetItemDownloadInfo(intptr_t instancePtr, PublishedFileId_t nPublishedFileID, uint64 * punBytesDownloaded, uint64 * punBytesTotal); +S_API bool SteamAPI_ISteamUGC_DownloadItem(intptr_t instancePtr, PublishedFileId_t nPublishedFileID, bool bHighPriority); +S_API bool SteamAPI_ISteamUGC_BInitWorkshopForGameServer(intptr_t instancePtr, DepotId_t unWorkshopDepotID, const char * pszFolder); +S_API void SteamAPI_ISteamUGC_SuspendDownloads(intptr_t instancePtr, bool bSuspend); +S_API SteamAPICall_t SteamAPI_ISteamUGC_StartPlaytimeTracking(intptr_t instancePtr, PublishedFileId_t * pvecPublishedFileID, uint32 unNumPublishedFileIDs); +S_API SteamAPICall_t SteamAPI_ISteamUGC_StopPlaytimeTracking(intptr_t instancePtr, PublishedFileId_t * pvecPublishedFileID, uint32 unNumPublishedFileIDs); +S_API SteamAPICall_t SteamAPI_ISteamUGC_StopPlaytimeTrackingForAllItems(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamUGC_AddDependency(intptr_t instancePtr, PublishedFileId_t nParentPublishedFileID, PublishedFileId_t nChildPublishedFileID); +S_API SteamAPICall_t SteamAPI_ISteamUGC_RemoveDependency(intptr_t instancePtr, PublishedFileId_t nParentPublishedFileID, PublishedFileId_t nChildPublishedFileID); +S_API uint32 SteamAPI_ISteamAppList_GetNumInstalledApps(intptr_t instancePtr); +S_API uint32 SteamAPI_ISteamAppList_GetInstalledApps(intptr_t instancePtr, AppId_t * pvecAppID, uint32 unMaxAppIDs); +S_API int SteamAPI_ISteamAppList_GetAppName(intptr_t instancePtr, AppId_t nAppID, char * pchName, int cchNameMax); +S_API int SteamAPI_ISteamAppList_GetAppInstallDir(intptr_t instancePtr, AppId_t nAppID, char * pchDirectory, int cchNameMax); +S_API int SteamAPI_ISteamAppList_GetAppBuildId(intptr_t instancePtr, AppId_t nAppID); +S_API void SteamAPI_ISteamHTMLSurface_DestructISteamHTMLSurface(intptr_t instancePtr); +S_API bool SteamAPI_ISteamHTMLSurface_Init(intptr_t instancePtr); +S_API bool SteamAPI_ISteamHTMLSurface_Shutdown(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamHTMLSurface_CreateBrowser(intptr_t instancePtr, const char * pchUserAgent, const char * pchUserCSS); +S_API void SteamAPI_ISteamHTMLSurface_RemoveBrowser(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_LoadURL(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, const char * pchURL, const char * pchPostData); +S_API void SteamAPI_ISteamHTMLSurface_SetSize(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, uint32 unWidth, uint32 unHeight); +S_API void SteamAPI_ISteamHTMLSurface_StopLoad(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_Reload(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_GoBack(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_GoForward(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_AddHeader(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, const char * pchKey, const char * pchValue); +S_API void SteamAPI_ISteamHTMLSurface_ExecuteJavascript(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, const char * pchScript); +S_API void SteamAPI_ISteamHTMLSurface_MouseUp(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, ISteamHTMLSurface::EHTMLMouseButton eMouseButton); +S_API void SteamAPI_ISteamHTMLSurface_MouseDown(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, ISteamHTMLSurface::EHTMLMouseButton eMouseButton); +S_API void SteamAPI_ISteamHTMLSurface_MouseDoubleClick(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, ISteamHTMLSurface::EHTMLMouseButton eMouseButton); +S_API void SteamAPI_ISteamHTMLSurface_MouseMove(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, int x, int y); +S_API void SteamAPI_ISteamHTMLSurface_MouseWheel(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, int32 nDelta); +S_API void SteamAPI_ISteamHTMLSurface_KeyDown(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, ISteamHTMLSurface::EHTMLKeyModifiers eHTMLKeyModifiers); +S_API void SteamAPI_ISteamHTMLSurface_KeyUp(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, ISteamHTMLSurface::EHTMLKeyModifiers eHTMLKeyModifiers); +S_API void SteamAPI_ISteamHTMLSurface_KeyChar(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, uint32 cUnicodeChar, ISteamHTMLSurface::EHTMLKeyModifiers eHTMLKeyModifiers); +S_API void SteamAPI_ISteamHTMLSurface_SetHorizontalScroll(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll); +S_API void SteamAPI_ISteamHTMLSurface_SetVerticalScroll(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll); +S_API void SteamAPI_ISteamHTMLSurface_SetKeyFocus(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, bool bHasKeyFocus); +S_API void SteamAPI_ISteamHTMLSurface_ViewSource(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_CopyToClipboard(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_PasteFromClipboard(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_Find(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, const char * pchSearchStr, bool bCurrentlyInFind, bool bReverse); +S_API void SteamAPI_ISteamHTMLSurface_StopFind(intptr_t instancePtr, HHTMLBrowser unBrowserHandle); +S_API void SteamAPI_ISteamHTMLSurface_GetLinkAtPosition(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, int x, int y); +S_API void SteamAPI_ISteamHTMLSurface_SetCookie(intptr_t instancePtr, const char * pchHostname, const char * pchKey, const char * pchValue, const char * pchPath, RTime32 nExpires, bool bSecure, bool bHTTPOnly); +S_API void SteamAPI_ISteamHTMLSurface_SetPageScaleFactor(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, float flZoom, int nPointX, int nPointY); +S_API void SteamAPI_ISteamHTMLSurface_SetBackgroundMode(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, bool bBackgroundMode); +S_API void SteamAPI_ISteamHTMLSurface_AllowStartRequest(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, bool bAllowed); +S_API void SteamAPI_ISteamHTMLSurface_JSDialogResponse(intptr_t instancePtr, HHTMLBrowser unBrowserHandle, bool bResult); +S_API EResult SteamAPI_ISteamInventory_GetResultStatus(intptr_t instancePtr, SteamInventoryResult_t resultHandle); +S_API bool SteamAPI_ISteamInventory_GetResultItems(intptr_t instancePtr, SteamInventoryResult_t resultHandle, struct SteamItemDetails_t * pOutItemsArray, uint32 * punOutItemsArraySize); +S_API bool SteamAPI_ISteamInventory_GetResultItemProperty(intptr_t instancePtr, SteamInventoryResult_t resultHandle, uint32 unItemIndex, const char * pchPropertyName, char * pchValueBuffer, uint32 * punValueBufferSizeOut); +S_API uint32 SteamAPI_ISteamInventory_GetResultTimestamp(intptr_t instancePtr, SteamInventoryResult_t resultHandle); +S_API bool SteamAPI_ISteamInventory_CheckResultSteamID(intptr_t instancePtr, SteamInventoryResult_t resultHandle, class CSteamID steamIDExpected); +S_API void SteamAPI_ISteamInventory_DestroyResult(intptr_t instancePtr, SteamInventoryResult_t resultHandle); +S_API bool SteamAPI_ISteamInventory_GetAllItems(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle); +S_API bool SteamAPI_ISteamInventory_GetItemsByID(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, const SteamItemInstanceID_t * pInstanceIDs, uint32 unCountInstanceIDs); +S_API bool SteamAPI_ISteamInventory_SerializeResult(intptr_t instancePtr, SteamInventoryResult_t resultHandle, void * pOutBuffer, uint32 * punOutBufferSize); +S_API bool SteamAPI_ISteamInventory_DeserializeResult(intptr_t instancePtr, SteamInventoryResult_t * pOutResultHandle, const void * pBuffer, uint32 unBufferSize, bool bRESERVED_MUST_BE_FALSE); +S_API bool SteamAPI_ISteamInventory_GenerateItems(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, const SteamItemDef_t * pArrayItemDefs, const uint32 * punArrayQuantity, uint32 unArrayLength); +S_API bool SteamAPI_ISteamInventory_GrantPromoItems(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle); +S_API bool SteamAPI_ISteamInventory_AddPromoItem(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, SteamItemDef_t itemDef); +S_API bool SteamAPI_ISteamInventory_AddPromoItems(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, const SteamItemDef_t * pArrayItemDefs, uint32 unArrayLength); +S_API bool SteamAPI_ISteamInventory_ConsumeItem(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, SteamItemInstanceID_t itemConsume, uint32 unQuantity); +S_API bool SteamAPI_ISteamInventory_ExchangeItems(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, const SteamItemDef_t * pArrayGenerate, const uint32 * punArrayGenerateQuantity, uint32 unArrayGenerateLength, const SteamItemInstanceID_t * pArrayDestroy, const uint32 * punArrayDestroyQuantity, uint32 unArrayDestroyLength); +S_API bool SteamAPI_ISteamInventory_TransferItemQuantity(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, SteamItemInstanceID_t itemIdSource, uint32 unQuantity, SteamItemInstanceID_t itemIdDest); +S_API void SteamAPI_ISteamInventory_SendItemDropHeartbeat(intptr_t instancePtr); +S_API bool SteamAPI_ISteamInventory_TriggerItemDrop(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, SteamItemDef_t dropListDefinition); +S_API bool SteamAPI_ISteamInventory_TradeItems(intptr_t instancePtr, SteamInventoryResult_t * pResultHandle, class CSteamID steamIDTradePartner, const SteamItemInstanceID_t * pArrayGive, const uint32 * pArrayGiveQuantity, uint32 nArrayGiveLength, const SteamItemInstanceID_t * pArrayGet, const uint32 * pArrayGetQuantity, uint32 nArrayGetLength); +S_API bool SteamAPI_ISteamInventory_LoadItemDefinitions(intptr_t instancePtr); +S_API bool SteamAPI_ISteamInventory_GetItemDefinitionIDs(intptr_t instancePtr, SteamItemDef_t * pItemDefIDs, uint32 * punItemDefIDsArraySize); +S_API bool SteamAPI_ISteamInventory_GetItemDefinitionProperty(intptr_t instancePtr, SteamItemDef_t iDefinition, const char * pchPropertyName, char * pchValueBuffer, uint32 * punValueBufferSizeOut); +S_API SteamAPICall_t SteamAPI_ISteamInventory_RequestEligiblePromoItemDefinitionsIDs(intptr_t instancePtr, class CSteamID steamID); +S_API bool SteamAPI_ISteamInventory_GetEligiblePromoItemDefinitionIDs(intptr_t instancePtr, class CSteamID steamID, SteamItemDef_t * pItemDefIDs, uint32 * punItemDefIDsArraySize); +S_API void SteamAPI_ISteamVideo_GetVideoURL(intptr_t instancePtr, AppId_t unVideoAppID); +S_API bool SteamAPI_ISteamVideo_IsBroadcasting(intptr_t instancePtr, int * pnNumViewers); +S_API void SteamAPI_ISteamVideo_GetOPFSettings(intptr_t instancePtr, AppId_t unVideoAppID); +S_API bool SteamAPI_ISteamVideo_GetOPFStringForApp(intptr_t instancePtr, AppId_t unVideoAppID, char * pchBuffer, int32 * pnBufferSize); +S_API bool SteamAPI_ISteamGameServer_InitGameServer(intptr_t instancePtr, uint32 unIP, uint16 usGamePort, uint16 usQueryPort, uint32 unFlags, AppId_t nGameAppId, const char * pchVersionString); +S_API void SteamAPI_ISteamGameServer_SetProduct(intptr_t instancePtr, const char * pszProduct); +S_API void SteamAPI_ISteamGameServer_SetGameDescription(intptr_t instancePtr, const char * pszGameDescription); +S_API void SteamAPI_ISteamGameServer_SetModDir(intptr_t instancePtr, const char * pszModDir); +S_API void SteamAPI_ISteamGameServer_SetDedicatedServer(intptr_t instancePtr, bool bDedicated); +S_API void SteamAPI_ISteamGameServer_LogOn(intptr_t instancePtr, const char * pszToken); +S_API void SteamAPI_ISteamGameServer_LogOnAnonymous(intptr_t instancePtr); +S_API void SteamAPI_ISteamGameServer_LogOff(intptr_t instancePtr); +S_API bool SteamAPI_ISteamGameServer_BLoggedOn(intptr_t instancePtr); +S_API bool SteamAPI_ISteamGameServer_BSecure(intptr_t instancePtr); +S_API uint64 SteamAPI_ISteamGameServer_GetSteamID(intptr_t instancePtr); +S_API bool SteamAPI_ISteamGameServer_WasRestartRequested(intptr_t instancePtr); +S_API void SteamAPI_ISteamGameServer_SetMaxPlayerCount(intptr_t instancePtr, int cPlayersMax); +S_API void SteamAPI_ISteamGameServer_SetBotPlayerCount(intptr_t instancePtr, int cBotplayers); +S_API void SteamAPI_ISteamGameServer_SetServerName(intptr_t instancePtr, const char * pszServerName); +S_API void SteamAPI_ISteamGameServer_SetMapName(intptr_t instancePtr, const char * pszMapName); +S_API void SteamAPI_ISteamGameServer_SetPasswordProtected(intptr_t instancePtr, bool bPasswordProtected); +S_API void SteamAPI_ISteamGameServer_SetSpectatorPort(intptr_t instancePtr, uint16 unSpectatorPort); +S_API void SteamAPI_ISteamGameServer_SetSpectatorServerName(intptr_t instancePtr, const char * pszSpectatorServerName); +S_API void SteamAPI_ISteamGameServer_ClearAllKeyValues(intptr_t instancePtr); +S_API void SteamAPI_ISteamGameServer_SetKeyValue(intptr_t instancePtr, const char * pKey, const char * pValue); +S_API void SteamAPI_ISteamGameServer_SetGameTags(intptr_t instancePtr, const char * pchGameTags); +S_API void SteamAPI_ISteamGameServer_SetGameData(intptr_t instancePtr, const char * pchGameData); +S_API void SteamAPI_ISteamGameServer_SetRegion(intptr_t instancePtr, const char * pszRegion); +S_API bool SteamAPI_ISteamGameServer_SendUserConnectAndAuthenticate(intptr_t instancePtr, uint32 unIPClient, const void * pvAuthBlob, uint32 cubAuthBlobSize, class CSteamID * pSteamIDUser); +S_API uint64 SteamAPI_ISteamGameServer_CreateUnauthenticatedUserConnection(intptr_t instancePtr); +S_API void SteamAPI_ISteamGameServer_SendUserDisconnect(intptr_t instancePtr, class CSteamID steamIDUser); +S_API bool SteamAPI_ISteamGameServer_BUpdateUserData(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchPlayerName, uint32 uScore); +S_API HAuthTicket SteamAPI_ISteamGameServer_GetAuthSessionTicket(intptr_t instancePtr, void * pTicket, int cbMaxTicket, uint32 * pcbTicket); +S_API EBeginAuthSessionResult SteamAPI_ISteamGameServer_BeginAuthSession(intptr_t instancePtr, const void * pAuthTicket, int cbAuthTicket, class CSteamID steamID); +S_API void SteamAPI_ISteamGameServer_EndAuthSession(intptr_t instancePtr, class CSteamID steamID); +S_API void SteamAPI_ISteamGameServer_CancelAuthTicket(intptr_t instancePtr, HAuthTicket hAuthTicket); +S_API EUserHasLicenseForAppResult SteamAPI_ISteamGameServer_UserHasLicenseForApp(intptr_t instancePtr, class CSteamID steamID, AppId_t appID); +S_API bool SteamAPI_ISteamGameServer_RequestUserGroupStatus(intptr_t instancePtr, class CSteamID steamIDUser, class CSteamID steamIDGroup); +S_API void SteamAPI_ISteamGameServer_GetGameplayStats(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamGameServer_GetServerReputation(intptr_t instancePtr); +S_API uint32 SteamAPI_ISteamGameServer_GetPublicIP(intptr_t instancePtr); +S_API bool SteamAPI_ISteamGameServer_HandleIncomingPacket(intptr_t instancePtr, const void * pData, int cbData, uint32 srcIP, uint16 srcPort); +S_API int SteamAPI_ISteamGameServer_GetNextOutgoingPacket(intptr_t instancePtr, void * pOut, int cbMaxOut, uint32 * pNetAdr, uint16 * pPort); +S_API void SteamAPI_ISteamGameServer_EnableHeartbeats(intptr_t instancePtr, bool bActive); +S_API void SteamAPI_ISteamGameServer_SetHeartbeatInterval(intptr_t instancePtr, int iHeartbeatInterval); +S_API void SteamAPI_ISteamGameServer_ForceHeartbeat(intptr_t instancePtr); +S_API SteamAPICall_t SteamAPI_ISteamGameServer_AssociateWithClan(intptr_t instancePtr, class CSteamID steamIDClan); +S_API SteamAPICall_t SteamAPI_ISteamGameServer_ComputeNewPlayerCompatibility(intptr_t instancePtr, class CSteamID steamIDNewPlayer); +S_API SteamAPICall_t SteamAPI_ISteamGameServerStats_RequestUserStats(intptr_t instancePtr, class CSteamID steamIDUser); +S_API bool SteamAPI_ISteamGameServerStats_GetUserStat(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, int32 * pData); +S_API bool SteamAPI_ISteamGameServerStats_GetUserStat0(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, float * pData); +S_API bool SteamAPI_ISteamGameServerStats_GetUserAchievement(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, bool * pbAchieved); +S_API bool SteamAPI_ISteamGameServerStats_SetUserStat(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, int32 nData); +S_API bool SteamAPI_ISteamGameServerStats_SetUserStat0(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, float fData); +S_API bool SteamAPI_ISteamGameServerStats_UpdateUserAvgRateStat(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName, float flCountThisSession, double dSessionLength); +S_API bool SteamAPI_ISteamGameServerStats_SetUserAchievement(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName); +S_API bool SteamAPI_ISteamGameServerStats_ClearUserAchievement(intptr_t instancePtr, class CSteamID steamIDUser, const char * pchName); +S_API SteamAPICall_t SteamAPI_ISteamGameServerStats_StoreUserStats(intptr_t instancePtr, class CSteamID steamIDUser); +#endif // STEAMAPIFLAT_H + + diff --git a/dep/steam_api/steam_api_internal.h b/dep/steam_api/steam_api_internal.h new file mode 100644 index 0000000..ed0423b --- /dev/null +++ b/dep/steam_api/steam_api_internal.h @@ -0,0 +1,327 @@ +//====== Copyright 1996-2015, Valve Corporation, All rights reserved. ======= +// +// Purpose: Internal private Steamworks API declarations and definitions +// +//============================================================================= + +#ifndef STEAM_API_INTERNAL_H +#define STEAM_API_INTERNAL_H + +S_API HSteamUser SteamAPI_GetHSteamUser(); +S_API void * S_CALLTYPE SteamInternal_ContextInit( void *pContextInitData ); +S_API void * S_CALLTYPE SteamInternal_CreateInterface( const char *ver ); + +#if !defined( STEAM_API_EXPORTS ) + +inline void S_CALLTYPE SteamInternal_OnContextInit( void* p ) +{ + ((CSteamAPIContext*)p)->Clear(); + if ( SteamAPI_GetHSteamPipe() ) + ((CSteamAPIContext*)p)->Init(); +} +inline CSteamAPIContext& SteamInternal_ModuleContext() +{ + // SteamInternal_ContextInit takes a base pointer for the equivalent of + // struct { void (*pFn)(void* pCtx); uintp counter; CSteamAPIContext ctx; } + // Do not change layout of 2 + sizeof... or add non-pointer aligned data! + // NOTE: declaring "static CSteamAPIConext" creates a large function + // which queries the initialization status of the object. We know that + // it is pointer-aligned and fully memset with zeros, so just alias a + // static buffer of the appropriate size and call it a CSteamAPIContext. + static void* s_CallbackCounterAndContext[ 2 + sizeof(CSteamAPIContext)/sizeof(void*) ] = { (void*)&SteamInternal_OnContextInit, 0 }; + return *(CSteamAPIContext*)SteamInternal_ContextInit( s_CallbackCounterAndContext ); +} + +inline ISteamClient *SteamClient() { return SteamInternal_ModuleContext().SteamClient(); } +inline ISteamUser *SteamUser() { return SteamInternal_ModuleContext().SteamUser(); } +inline ISteamFriends *SteamFriends() { return SteamInternal_ModuleContext().SteamFriends(); } +inline ISteamUtils *SteamUtils() { return SteamInternal_ModuleContext().SteamUtils(); } +inline ISteamMatchmaking *SteamMatchmaking() { return SteamInternal_ModuleContext().SteamMatchmaking(); } +inline ISteamUserStats *SteamUserStats() { return SteamInternal_ModuleContext().SteamUserStats(); } +inline ISteamApps *SteamApps() { return SteamInternal_ModuleContext().SteamApps(); } +inline ISteamMatchmakingServers *SteamMatchmakingServers() { return SteamInternal_ModuleContext().SteamMatchmakingServers(); } +inline ISteamNetworking *SteamNetworking() { return SteamInternal_ModuleContext().SteamNetworking(); } +inline ISteamRemoteStorage *SteamRemoteStorage() { return SteamInternal_ModuleContext().SteamRemoteStorage(); } +inline ISteamScreenshots *SteamScreenshots() { return SteamInternal_ModuleContext().SteamScreenshots(); } +inline ISteamHTTP *SteamHTTP() { return SteamInternal_ModuleContext().SteamHTTP(); } +inline ISteamUnifiedMessages *SteamUnifiedMessages() { return SteamInternal_ModuleContext().SteamUnifiedMessages(); } +inline ISteamController *SteamController() { return SteamInternal_ModuleContext().SteamController(); } +inline ISteamUGC *SteamUGC() { return SteamInternal_ModuleContext().SteamUGC(); } +inline ISteamAppList *SteamAppList() { return SteamInternal_ModuleContext().SteamAppList(); } +inline ISteamMusic *SteamMusic() { return SteamInternal_ModuleContext().SteamMusic(); } +inline ISteamMusicRemote *SteamMusicRemote() { return SteamInternal_ModuleContext().SteamMusicRemote(); } +inline ISteamHTMLSurface *SteamHTMLSurface() { return SteamInternal_ModuleContext().SteamHTMLSurface(); } +inline ISteamInventory *SteamInventory() { return SteamInternal_ModuleContext().SteamInventory(); } +inline ISteamVideo *SteamVideo() { return SteamInternal_ModuleContext().SteamVideo(); } + +#endif // !defined( STEAM_API_EXPORTS ) + + +inline void CSteamAPIContext::Clear() +{ + m_pSteamClient = NULL; + 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; + m_pSteamHTTP = NULL; + m_pSteamScreenshots = NULL; + m_pSteamMusic = NULL; + m_pSteamUnifiedMessages = NULL; + m_pController = NULL; + m_pSteamUGC = NULL; + m_pSteamAppList = NULL; + m_pSteamMusic = NULL; + m_pSteamMusicRemote = NULL; + m_pSteamHTMLSurface = NULL; + m_pSteamInventory = NULL; +} + + +// This function must be declared inline in the header so the module using steam_api.dll gets the version names they want. +inline bool CSteamAPIContext::Init() +{ + HSteamUser hSteamUser = SteamAPI_GetHSteamUser(); + HSteamPipe hSteamPipe = SteamAPI_GetHSteamPipe(); + if ( !hSteamPipe ) + return false; + + m_pSteamClient = (ISteamClient*) SteamInternal_CreateInterface( STEAMCLIENT_INTERFACE_VERSION ); + if ( !m_pSteamClient ) + return false; + + m_pSteamUser = m_pSteamClient->GetISteamUser( hSteamUser, hSteamPipe, STEAMUSER_INTERFACE_VERSION ); + if ( !m_pSteamUser ) + return false; + + m_pSteamFriends = m_pSteamClient->GetISteamFriends( hSteamUser, hSteamPipe, STEAMFRIENDS_INTERFACE_VERSION ); + if ( !m_pSteamFriends ) + return false; + + m_pSteamUtils = m_pSteamClient->GetISteamUtils( hSteamPipe, STEAMUTILS_INTERFACE_VERSION ); + if ( !m_pSteamUtils ) + return false; + + m_pSteamMatchmaking = m_pSteamClient->GetISteamMatchmaking( hSteamUser, hSteamPipe, STEAMMATCHMAKING_INTERFACE_VERSION ); + if ( !m_pSteamMatchmaking ) + return false; + + m_pSteamMatchmakingServers = m_pSteamClient->GetISteamMatchmakingServers( hSteamUser, hSteamPipe, STEAMMATCHMAKINGSERVERS_INTERFACE_VERSION ); + if ( !m_pSteamMatchmakingServers ) + return false; + + m_pSteamUserStats = m_pSteamClient->GetISteamUserStats( hSteamUser, hSteamPipe, STEAMUSERSTATS_INTERFACE_VERSION ); + if ( !m_pSteamUserStats ) + return false; + + m_pSteamApps = m_pSteamClient->GetISteamApps( hSteamUser, hSteamPipe, STEAMAPPS_INTERFACE_VERSION ); + if ( !m_pSteamApps ) + return false; + + m_pSteamNetworking = m_pSteamClient->GetISteamNetworking( hSteamUser, hSteamPipe, STEAMNETWORKING_INTERFACE_VERSION ); + if ( !m_pSteamNetworking ) + return false; + + m_pSteamRemoteStorage = m_pSteamClient->GetISteamRemoteStorage( hSteamUser, hSteamPipe, STEAMREMOTESTORAGE_INTERFACE_VERSION ); + if ( !m_pSteamRemoteStorage ) + return false; + + m_pSteamScreenshots = m_pSteamClient->GetISteamScreenshots( hSteamUser, hSteamPipe, STEAMSCREENSHOTS_INTERFACE_VERSION ); + if ( !m_pSteamScreenshots ) + return false; + + m_pSteamHTTP = m_pSteamClient->GetISteamHTTP( hSteamUser, hSteamPipe, STEAMHTTP_INTERFACE_VERSION ); + if ( !m_pSteamHTTP ) + return false; + + m_pSteamUnifiedMessages = m_pSteamClient->GetISteamUnifiedMessages( hSteamUser, hSteamPipe, STEAMUNIFIEDMESSAGES_INTERFACE_VERSION ); + if ( !m_pSteamUnifiedMessages ) + return false; + + m_pController = m_pSteamClient->GetISteamController( hSteamUser, hSteamPipe, STEAMCONTROLLER_INTERFACE_VERSION ); + if ( !m_pController ) + return false; + + m_pSteamUGC = m_pSteamClient->GetISteamUGC( hSteamUser, hSteamPipe, STEAMUGC_INTERFACE_VERSION ); + if ( !m_pSteamUGC ) + return false; + + m_pSteamAppList = m_pSteamClient->GetISteamAppList( hSteamUser, hSteamPipe, STEAMAPPLIST_INTERFACE_VERSION ); + if ( !m_pSteamAppList ) + return false; + + m_pSteamMusic = m_pSteamClient->GetISteamMusic( hSteamUser, hSteamPipe, STEAMMUSIC_INTERFACE_VERSION ); + if ( !m_pSteamMusic ) + return false; + + m_pSteamMusicRemote = m_pSteamClient->GetISteamMusicRemote( hSteamUser, hSteamPipe, STEAMMUSICREMOTE_INTERFACE_VERSION ); + if ( !m_pSteamMusicRemote ) + return false; + + m_pSteamHTMLSurface = m_pSteamClient->GetISteamHTMLSurface( hSteamUser, hSteamPipe, STEAMHTMLSURFACE_INTERFACE_VERSION ); + if ( !m_pSteamHTMLSurface ) + return false; + + m_pSteamInventory = m_pSteamClient->GetISteamInventory( hSteamUser, hSteamPipe, STEAMINVENTORY_INTERFACE_VERSION ); + if ( !m_pSteamInventory ) + return false; + + m_pSteamVideo = m_pSteamClient->GetISteamVideo( hSteamUser, hSteamPipe, STEAMVIDEO_INTERFACE_VERSION ); + if ( !m_pSteamVideo ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// The following macros are implementation details, not intended for public use +//----------------------------------------------------------------------------- +#define _STEAM_CALLBACK_AUTO_HOOK( thisclass, func, param ) +#define _STEAM_CALLBACK_HELPER( _1, _2, SELECTED, ... ) _STEAM_CALLBACK_##SELECTED +#define _STEAM_CALLBACK_SELECT( X, Y ) _STEAM_CALLBACK_HELPER X Y +#define _STEAM_CALLBACK_3( extra_code, thisclass, func, param ) \ + struct CCallbackInternal_ ## func : private CCallbackImpl< sizeof( param ) > { \ + CCallbackInternal_ ## func () { extra_code SteamAPI_RegisterCallback( this, param::k_iCallback ); } \ + CCallbackInternal_ ## func ( const CCallbackInternal_ ## func & ) { extra_code SteamAPI_RegisterCallback( this, param::k_iCallback ); } \ + CCallbackInternal_ ## func & operator=( const CCallbackInternal_ ## func & ) { return *this; } \ + private: virtual void Run( void *pvParam ) { _STEAM_CALLBACK_AUTO_HOOK( thisclass, func, param ) \ + thisclass *pOuter = reinterpret_cast( reinterpret_cast(this) - offsetof( thisclass, m_steamcallback_ ## func ) ); \ + pOuter->func( reinterpret_cast( pvParam ) ); \ + } \ + } m_steamcallback_ ## func ; void func( param *pParam ) +#define _STEAM_CALLBACK_4( _, thisclass, func, param, var ) \ + CCallback< thisclass, param > var; void func( param *pParam ) + + +//----------------------------------------------------------------------------- +// 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 > +inline CCallResult::CCallResult() +{ + m_hAPICall = k_uAPICallInvalid; + m_pObj = NULL; + m_Func = NULL; + m_iCallback = P::k_iCallback; +} + +template< class T, class P > +inline void CCallResult::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 ); +} + +template< class T, class P > +inline bool CCallResult::IsActive() const +{ + return (m_hAPICall != k_uAPICallInvalid); +} + +template< class T, class P > +inline void CCallResult::Cancel() +{ + if ( m_hAPICall != k_uAPICallInvalid ) + { + SteamAPI_UnregisterCallResult( this, m_hAPICall ); + m_hAPICall = k_uAPICallInvalid; + } + +} + +template< class T, class P > +inline CCallResult::~CCallResult() +{ + Cancel(); +} + +template< class T, class P > +inline void CCallResult::Run( void *pvParam ) +{ + m_hAPICall = k_uAPICallInvalid; // caller unregisters for us + (m_pObj->*m_Func)((P *)pvParam, false); +} + +template< class T, class P > +inline void CCallResult::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); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: maps a steam callback to a class member function +// template params: T = local class, P = parameter struct, +// bGameserver = listen for gameserver callbacks instead of client callbacks +//----------------------------------------------------------------------------- +template< class T, class P, bool bGameserver > +inline CCallback< T, P, bGameserver >::CCallback( T *pObj, func_t func ) + : m_pObj( NULL ), m_Func( NULL ) +{ + if ( bGameserver ) + { + this->SetGameserverFlag(); + } + Register( pObj, func ); +} + +template< class T, class P, bool bGameserver > +inline void CCallback< T, P, bGameserver >::Register( T *pObj, func_t func ) +{ + if ( !pObj || !func ) + return; + + if ( this->m_nCallbackFlags & CCallbackBase::k_ECallbackFlagsRegistered ) + Unregister(); + + m_pObj = pObj; + m_Func = func; + // SteamAPI_RegisterCallback sets k_ECallbackFlagsRegistered + SteamAPI_RegisterCallback( this, P::k_iCallback ); +} + +template< class T, class P, bool bGameserver > +inline void CCallback< T, P, bGameserver >::Unregister() +{ + // SteamAPI_UnregisterCallback removes k_ECallbackFlagsRegistered + SteamAPI_UnregisterCallback( this ); +} + +template< class T, class P, bool bGameserver > +inline void CCallback< T, P, bGameserver >::Run( void *pvParam ) +{ + (m_pObj->*m_Func)((P *)pvParam); +} + + +#if defined(USE_BREAKPAD_HANDLER) || defined(STEAM_API_EXPORTS) +// 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 S_CALLTYPE SteamAPI_UseBreakpadCrashHandler( char const *pchVersion, char const *pchDate, char const *pchTime, bool bFullMemoryDumps, void *pvContext, PFNPreMinidumpCallback m_pfnPreMinidumpCallback ); +S_API void S_CALLTYPE SteamAPI_SetBreakpadAppID( uint32 unAppID ); +#endif + + +#endif // STEAM_API_INTERNAL_H diff --git a/dep/steam_api/steam_gameserver.h b/dep/steam_api/steam_gameserver.h new file mode 100644 index 0000000..c6bdd52 --- /dev/null +++ b/dep/steam_api/steam_gameserver.h @@ -0,0 +1,243 @@ +//====== 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 "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 +}; + +// Initialize ISteamGameServer interface object, and set server properties which may not be changed. +// +// After calling this function, you should set any additional server parameters, and then +// call ISteamGameServer::LogOnAnonymous() or ISteamGameServer::LogOn() +// +// - usSteamPort is the local port used to communicate with the steam servers. +// - usGamePort is the port that clients will connect to for gameplay. +// - usQueryPort is the port that will manage server browser related duties and info +// pings from clients. 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 isteamgameserver.h. +// - The version string is usually in the form x.x.x.x, and is used by the master server to detect when the +// server is out of date. (Only servers with the latest version will be listed.) + +inline bool SteamGameServer_Init( uint32 unIP, uint16 usSteamPort, uint16 usGamePort, uint16 usQueryPort, EServerMode eServerMode, const char *pchVersionString ); + +S_API void SteamGameServer_Shutdown(); +S_API void SteamGameServer_RunCallbacks(); + +// Most Steam API functions allocate some amount of thread-local memory for +// parameter storage. Calling SteamGameServer_ReleaseCurrentThreadMemory() +// will free all API-related memory associated with the calling thread. +// This memory is released automatically by SteamGameServer_RunCallbacks(), +// so single-threaded servers do not need to explicitly call this function. +inline void SteamGameServer_ReleaseCurrentThreadMemory(); + +S_API bool SteamGameServer_BSecure(); +S_API uint64 SteamGameServer_GetSteamID(); + + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// Global accessors for game server C++ APIs. See individual isteam*.h files for details. +// You should not cache the results of these accessors or pass the result pointers across +// modules! Different modules may be compiled against different SDK header versions, and +// the interface pointers could therefore be different across modules. Every line of code +// which calls into a Steamworks API should retrieve the interface from a global accessor. +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +#if !defined( STEAM_API_EXPORTS ) +inline ISteamClient *SteamGameServerClient(); +inline ISteamGameServer *SteamGameServer(); +inline ISteamUtils *SteamGameServerUtils(); +inline ISteamNetworking *SteamGameServerNetworking(); +inline ISteamGameServerStats *SteamGameServerStats(); +inline ISteamHTTP *SteamGameServerHTTP(); +inline ISteamInventory *SteamGameServerInventory(); +inline ISteamUGC *SteamGameServerUGC(); +inline ISteamApps *SteamGameServerApps(); +#endif + +class CSteamGameServerAPIContext +{ +public: + CSteamGameServerAPIContext() { Clear(); } + inline void Clear(); + inline bool Init(); + + ISteamClient *SteamClient() const { return m_pSteamClient; } + ISteamGameServer *SteamGameServer() const { return m_pSteamGameServer; } + ISteamUtils *SteamGameServerUtils() const { return m_pSteamGameServerUtils; } + ISteamNetworking *SteamGameServerNetworking() const { return m_pSteamGameServerNetworking; } + ISteamGameServerStats *SteamGameServerStats() const { return m_pSteamGameServerStats; } + ISteamHTTP *SteamHTTP() const { return m_pSteamHTTP; } + ISteamInventory *SteamInventory() const { return m_pSteamInventory; } + ISteamUGC *SteamUGC() const { return m_pSteamUGC; } + ISteamApps *SteamApps() const { return m_pSteamApps; } + +private: + ISteamClient *m_pSteamClient; + ISteamGameServer *m_pSteamGameServer; + ISteamUtils *m_pSteamGameServerUtils; + ISteamNetworking *m_pSteamGameServerNetworking; + ISteamGameServerStats *m_pSteamGameServerStats; + ISteamHTTP *m_pSteamHTTP; + ISteamInventory *m_pSteamInventory; + ISteamUGC *m_pSteamUGC; + ISteamApps *m_pSteamApps; +}; + + +// Older SDKs exported this global pointer, but it is no longer supported. +// You should use SteamGameServerClient() or CSteamGameServerAPIContext to +// safely access the ISteamClient APIs from your game server application. +//S_API ISteamClient *g_pSteamClientGameServer; + +// SteamGameServer_InitSafe has been replaced with SteamGameServer_Init and +// is no longer supported. Use SteamGameServer_Init instead. +//S_API void S_CALLTYPE SteamGameServer_InitSafe(); + + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// These macros are similar to the STEAM_CALLBACK_* macros in steam_api.h, but only trigger for gameserver callbacks +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +#define STEAM_GAMESERVER_CALLBACK( thisclass, func, /*callback_type, [deprecated] var*/... ) \ + _STEAM_CALLBACK_SELECT( ( __VA_ARGS__, GS, 3 ), ( this->SetGameserverFlag();, thisclass, func, __VA_ARGS__ ) ) + +#define STEAM_GAMESERVER_CALLBACK_MANUAL( thisclass, func, callback_type, var ) \ + CCallbackManual< thisclass, callback_type, true > var; void func( callback_type *pParam ) + + +#define _STEAM_CALLBACK_GS( _, 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 S_CALLTYPE SteamGameServer_GetHSteamPipe(); +S_API HSteamUser S_CALLTYPE SteamGameServer_GetHSteamUser(); +S_API bool S_CALLTYPE SteamInternal_GameServer_Init( uint32 unIP, uint16 usPort, uint16 usGamePort, uint16 usQueryPort, EServerMode eServerMode, const char *pchVersionString ); + + +#if !defined( STEAM_API_EXPORTS ) +inline void S_CALLTYPE SteamGameServerInternal_OnContextInit( void* p ) +{ + ((CSteamGameServerAPIContext*)p)->Clear(); + if ( SteamGameServer_GetHSteamPipe() ) + ((CSteamGameServerAPIContext*)p)->Init(); +} +inline CSteamGameServerAPIContext& SteamGameServerInternal_ModuleContext() +{ + // SteamInternal_ContextInit takes a base pointer for the equivalent of + // struct { void (*pFn)(void* pCtx); uintp counter; CSteamAPIContext ctx; } + // Do not change layout of 2 + sizeof... or add non-pointer aligned data! + // NOTE: declaring "static CSteamAPIConext" creates a large function + // which queries the initialization status of the object. We know that + // it is pointer-aligned and fully memset with zeros, so just alias a + // static buffer of the appropriate size and call it a CSteamAPIContext. + static void* s_CallbackCounterAndContext[2 + sizeof( CSteamGameServerAPIContext ) / sizeof( void* )] = { (void*)&SteamGameServerInternal_OnContextInit, 0 }; + return *(CSteamGameServerAPIContext*)SteamInternal_ContextInit( s_CallbackCounterAndContext ); +} +inline ISteamClient *SteamGameServerClient() { return SteamGameServerInternal_ModuleContext().SteamClient(); } +inline ISteamGameServer *SteamGameServer() { return SteamGameServerInternal_ModuleContext().SteamGameServer(); } +inline ISteamUtils *SteamGameServerUtils() { return SteamGameServerInternal_ModuleContext().SteamGameServerUtils(); } +inline ISteamNetworking *SteamGameServerNetworking() { return SteamGameServerInternal_ModuleContext().SteamGameServerNetworking(); } +inline ISteamGameServerStats *SteamGameServerStats() { return SteamGameServerInternal_ModuleContext().SteamGameServerStats(); } +inline ISteamHTTP *SteamGameServerHTTP() { return SteamGameServerInternal_ModuleContext().SteamHTTP(); } +inline ISteamInventory *SteamGameServerInventory() { return SteamGameServerInternal_ModuleContext().SteamInventory(); } +inline ISteamUGC *SteamGameServerUGC() { return SteamGameServerInternal_ModuleContext().SteamUGC(); } +inline ISteamApps *SteamGameServerApps() { return SteamGameServerInternal_ModuleContext().SteamApps(); } +#endif // !defined( STEAM_API_EXPORTS ) + + +inline void CSteamGameServerAPIContext::Clear() +{ + m_pSteamClient = NULL; + m_pSteamGameServer = NULL; + m_pSteamGameServerUtils = NULL; + m_pSteamGameServerNetworking = NULL; + m_pSteamGameServerStats = NULL; + m_pSteamHTTP = NULL; + m_pSteamInventory = NULL; + m_pSteamUGC = NULL; + m_pSteamApps = NULL; +} + +// This function must be declared inline in the header so the module using steam_api.dll gets the version names they want. +inline bool CSteamGameServerAPIContext::Init() +{ + HSteamUser hSteamUser = SteamGameServer_GetHSteamUser(); + HSteamPipe hSteamPipe = SteamGameServer_GetHSteamPipe(); + if ( !hSteamPipe ) + return false; + + m_pSteamClient = (ISteamClient*) SteamInternal_CreateInterface( STEAMCLIENT_INTERFACE_VERSION ); + if ( !m_pSteamClient ) + return false; + + m_pSteamGameServer = m_pSteamClient->GetISteamGameServer( hSteamUser, hSteamPipe, STEAMGAMESERVER_INTERFACE_VERSION ); + if ( !m_pSteamGameServer ) + return false; + + m_pSteamGameServerUtils = m_pSteamClient->GetISteamUtils( hSteamPipe, STEAMUTILS_INTERFACE_VERSION ); + if ( !m_pSteamGameServerUtils ) + return false; + + m_pSteamGameServerNetworking = m_pSteamClient->GetISteamNetworking( hSteamUser, hSteamPipe, STEAMNETWORKING_INTERFACE_VERSION ); + if ( !m_pSteamGameServerNetworking ) + return false; + + m_pSteamGameServerStats = m_pSteamClient->GetISteamGameServerStats( hSteamUser, hSteamPipe, STEAMGAMESERVERSTATS_INTERFACE_VERSION ); + if ( !m_pSteamGameServerStats ) + return false; + + m_pSteamHTTP = m_pSteamClient->GetISteamHTTP( hSteamUser, hSteamPipe, STEAMHTTP_INTERFACE_VERSION ); + if ( !m_pSteamHTTP ) + return false; + + m_pSteamInventory = m_pSteamClient->GetISteamInventory( hSteamUser, hSteamPipe, STEAMINVENTORY_INTERFACE_VERSION ); + if ( !m_pSteamInventory ) + return false; + + m_pSteamUGC = m_pSteamClient->GetISteamUGC( hSteamUser, hSteamPipe, STEAMUGC_INTERFACE_VERSION ); + if ( !m_pSteamUGC ) + return false; + + m_pSteamApps = m_pSteamClient->GetISteamApps( hSteamUser, hSteamPipe, STEAMAPPS_INTERFACE_VERSION ); + if ( !m_pSteamApps ) + return false; + + return true; +} + + +inline bool SteamGameServer_Init( uint32 unIP, uint16 usSteamPort, uint16 usGamePort, uint16 usQueryPort, EServerMode eServerMode, const char *pchVersionString ) +{ + if ( !SteamInternal_GameServer_Init( unIP, usSteamPort, usGamePort, usQueryPort, eServerMode, pchVersionString ) ) + return false; + + return true; +} + + +inline void SteamGameServer_ReleaseCurrentThreadMemory() +{ + SteamAPI_ReleaseCurrentThreadMemory(); +} + +#endif // STEAM_GAMESERVER_H diff --git a/dep/steam_api/steamclientpublic.h b/dep/steam_api/steamclientpublic.h new file mode 100644 index 0000000..23b7581 --- /dev/null +++ b/dep/steam_api/steamclientpublic.h @@ -0,0 +1,1235 @@ +//========= 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" +#include "steamuniverse.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 + k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side + k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account + k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid + k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first + k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files + k_EResultIllegalPassword = 61, // The requested new password is not legal + k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer ) + k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure + k_EResultCannotUseOldPassword = 64, // The requested new password is not legal + k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid + k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent + k_EResultHardwareNotCapableOfIPT = 67, // + k_EResultIPTInitError = 68, // + k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user + k_EResultFacebookQueryError = 70, // Facebook query returned an error + k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired + k_EResultIPLoginRestrictionFailed = 72, + k_EResultAccountLockedDown = 73, + k_EResultAccountLogonDeniedVerifiedEmailRequired = 74, + k_EResultNoMatchingURL = 75, + k_EResultBadResponse = 76, // parse failure, missing field, etc. + k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password + k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range + k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen + k_EResultDisabled = 80, // The requested service has been configured to be unavailable + k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid ! + k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action + k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted + k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent + k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login + k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted + k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker + k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch + k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match + k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners + k_EResultNotModified = 91, // data not modified + k_EResultNoMobileDevice = 92, // the account does not have a mobile device associated with it + k_EResultTimeNotSynced = 93, // the time presented is out of range or tolerance + k_EResultSmsCodeFailed = 94, // SMS code failure (no match, none pending, etc.) + k_EResultAccountLimitExceeded = 95, // Too many accounts access this resource + k_EResultAccountActivityLimitExceeded = 96, // Too many changes to this account + k_EResultPhoneActivityLimitExceeded = 97, // Too many changes to this phone + k_EResultRefundToWallet = 98, // Cannot refund to payment method, must use wallet + k_EResultEmailSendFailure = 99, // Cannot send an email + k_EResultNotSettled = 100, // Can't perform operation till payment has settled + k_EResultNeedCaptcha = 101, // Needs to provide a valid captcha + k_EResultGSLTDenied = 102, // a game server login token owned by this token's owner has been banned + k_EResultGSOwnerDenied = 103, // game server owner is denied for other reason (account lock, community ban, vac ban, missing phone) + k_EResultInvalidItemType = 104, // the type of thing we were requested to act on is invalid + k_EResultIPBanned = 105, // the ip address has been banned from taking this action + k_EResultGSLTExpired = 106, // this token has expired from disuse; can be reset for use + k_EResultInsufficientFunds = 107, // user doesn't have enough wallet funds to complete the action + k_EResultTooManyPending = 108, // There are too many of this thing pending already +}; + +// 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, + k_EVoiceResultRestricted = 6, + k_EVoiceResultUnsupportedCodec = 7, + k_EVoiceResultReceiverOutOfDate = 8, + k_EVoiceResultReceiverDidNotAnswer = 9, + +}; + +// Result codes to GSHandleClientDeny/Kick +enum EDenyReason +{ + 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, +}; + +// return type of GetAuthSessionTicket +typedef uint32 HAuthTicket; +const HAuthTicket k_HAuthTicketInvalid = 0; + +// results from BeginAuthSession +enum EBeginAuthSessionResult +{ + 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 +}; + +// Callback values for callback ValidateAuthTicketResponse_t which is a response to BeginAuthSession +enum EAuthSessionResponse +{ + 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. + k_EAuthSessionResponsePublisherIssuedBan = 9, // The user is banned for this game. The ban came via the web api and not VAC +}; + +// results from UserHasLicenseForApp +enum EUserHasLicenseForAppResult +{ + 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 +}; + + +// 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_EAccountTypeConsoleUser = 9, // Fake SteamID for local PSN account on PS3 or Live account on 360, etc. + k_EAccountTypeAnonUser = 10, + + // Max of 16 items in this field + k_EAccountTypeMax +}; + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +enum EAppReleaseState +{ + k_EAppReleaseState_Unknown = 0, // unknown, required appinfo or license info is missing + k_EAppReleaseState_Unavailable = 1, // even if user 'just' owns it, can see game at all + k_EAppReleaseState_Prerelease = 2, // can be purchased and is visible in games list, nothing else. Common appInfo section released + k_EAppReleaseState_PreloadOnly = 3, // owners can preload app, not play it. AppInfo fully released. + k_EAppReleaseState_Released = 4, // owners can download and play app. +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +enum EAppOwnershipFlags +{ + k_EAppOwnershipFlags_None = 0x0000, // unknown + k_EAppOwnershipFlags_OwnsLicense = 0x0001, // owns license for this game + k_EAppOwnershipFlags_FreeLicense = 0x0002, // not paid for game + k_EAppOwnershipFlags_RegionRestricted = 0x0004, // owns app, but not allowed to play in current region + k_EAppOwnershipFlags_LowViolence = 0x0008, // only low violence version + k_EAppOwnershipFlags_InvalidPlatform = 0x0010, // app not supported on current platform + k_EAppOwnershipFlags_SharedLicense = 0x0020, // license was granted by authorized local device + k_EAppOwnershipFlags_FreeWeekend = 0x0040, // owned by a free weekend licenses + k_EAppOwnershipFlags_RetailLicense = 0x0080, // has a retail license for game, (CD-Key etc) + k_EAppOwnershipFlags_LicenseLocked = 0x0100, // shared license is locked (in use) by other user + k_EAppOwnershipFlags_LicensePending = 0x0200, // owns app, but transaction is still pending. Can't install or play + k_EAppOwnershipFlags_LicenseExpired = 0x0400, // doesn't own app anymore since license expired + k_EAppOwnershipFlags_LicensePermanent = 0x0800, // permanent license, not borrowed, or guest or freeweekend etc + k_EAppOwnershipFlags_LicenseRecurring = 0x1000, // Recurring license, user is charged periodically + k_EAppOwnershipFlags_LicenseCanceled = 0x2000, // Mark as canceled, but might be still active if recurring + k_EAppOwnershipFlags_AutoGrant = 0x4000, // Ownership is based on any kind of autogrant license + k_EAppOwnershipFlags_PendingGift = 0x8000, // user has pending gift to redeem + k_EAppOwnershipFlags_RentalNotActivated = 0x10000, // Rental hasn't been activated yet + k_EAppOwnershipFlags_Rental = 0x20000, // Is a rental + k_EAppOwnershipFlags_SiteLicense = 0x40000, // Is from a site license +}; + + +//----------------------------------------------------------------------------- +// Purpose: designed as flags to allow filters masks +//----------------------------------------------------------------------------- +enum EAppType +{ + k_EAppType_Invalid = 0x000, // unknown / invalid + k_EAppType_Game = 0x001, // playable game, default type + k_EAppType_Application = 0x002, // software application + k_EAppType_Tool = 0x004, // SDKs, editors & dedicated servers + k_EAppType_Demo = 0x008, // game demo + k_EAppType_Media_DEPRECATED = 0x010, // legacy - was used for game trailers, which are now just videos on the web + k_EAppType_DLC = 0x020, // down loadable content + k_EAppType_Guide = 0x040, // game guide, PDF etc + k_EAppType_Driver = 0x080, // hardware driver updater (ATI, Razor etc) + k_EAppType_Config = 0x100, // hidden app used to config Steam features (backpack, sales, etc) + k_EAppType_Hardware = 0x200, // a hardware device (Steam Machine, Steam Controller, Steam Link, etc.) + k_EAppType_Franchise = 0x400, // A hub for collections of multiple apps, eg films, series, games + k_EAppType_Video = 0x800, // A video component of either a Film or TVSeries (may be the feature, an episode, preview, making-of, etc) + k_EAppType_Plugin = 0x1000, // Plug-in types for other Apps + k_EAppType_Music = 0x2000, // Music files + k_EAppType_Series = 0x4000, // Container app for video series + + k_EAppType_Shortcut = 0x40000000, // just a shortcut, client side only + k_EAppType_DepotOnly = 0x80000000, // placeholder since depots and apps share the same namespace +}; + + + +//----------------------------------------------------------------------------- +// 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, + + // max, for sanity checks + k_ESteamUserStatTypeMAX +}; + + +//----------------------------------------------------------------------------- +// 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 (deprecated, should be treated as ChatMsg) + //k_EChatEntryTypeLobbyGameStart = 5, // lobby game is starting (dead - listen for LobbyGameCreated_t callback instead) + k_EChatEntryTypeLeftConversation = 6, // user has left the conversation ( closed chat window ) + // Above are previous FriendMsgType entries, now merged into more generic chat entry types + k_EChatEntryTypeEntered = 7, // user has entered the conversation (used in multi-user chat and group chat) + k_EChatEntryTypeWasKicked = 8, // user was kicked (data: 64-bit steamid of actor performing the kick) + k_EChatEntryTypeWasBanned = 9, // user was banned (data: 64-bit steamid of actor performing the ban) + k_EChatEntryTypeDisconnected = 10, // user disconnected + k_EChatEntryTypeHistoricalChat = 11, // a chat message from user's chat history or offilne message + //k_EChatEntryTypeReserved1 = 12, // No longer used + //k_EChatEntryTypeReserved2 = 13, // No longer used + k_EChatEntryTypeLinkBlocked = 14, // a link was removed by the chat filter. +}; + + +//----------------------------------------------------------------------------- +// 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) + k_EChatRoomEnterResponseClanDisabled = 8, // Attempt to join a clan chat when the clan is locked or disabled + k_EChatRoomEnterResponseCommunityBan = 9, // Attempt to join a chat when the user has a community lock on their account + k_EChatRoomEnterResponseMemberBlockedYou = 10, // Join failed - some member in the chat has blocked you from joining + k_EChatRoomEnterResponseYouBlockedMember = 11, // Join failed - you have blocked some member already in the chat + // k_EChatRoomEnterResponseNoRankingDataLobby = 12, // No longer used + // k_EChatRoomEnterResponseNoRankingDataUser = 13, // No longer used + // k_EChatRoomEnterResponseRankOutOfRange = 14, // No longer used +}; + + +typedef void (*PFNLegacyKeyRegistration)( const char *pchCDKey, const char *pchInstallPath ); +typedef bool (*PFNLegacyKeyInstalled)(); + +const unsigned int k_unSteamAccountIDMask = 0xFFFFFFFF; +const unsigned int k_unSteamAccountInstanceMask = 0x000FFFFF; +// we allow 3 simultaneous user account instances right now, 1= desktop, 2 = console, 4 = web, 0 = all +const unsigned int k_unSteamUserDesktopInstance = 1; +const unsigned int k_unSteamUserConsoleInstance = 2; +const unsigned int k_unSteamUserWebInstance = 4; + +// 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, + k_EMarketingMessageFlagsPlatformLinux = 1 << 3, + + //aggregate flags + k_EMarketingMessageFlagsPlatformRestrictions = + k_EMarketingMessageFlagsPlatformWindows | + k_EMarketingMessageFlagsPlatformMac | + k_EMarketingMessageFlagsPlatformLinux, +}; + + + +//----------------------------------------------------------------------------- +// 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, +}; + + +//----------------------------------------------------------------------------- +// Purpose: Broadcast upload result details +//----------------------------------------------------------------------------- +enum EBroadcastUploadResult +{ + k_EBroadcastUploadResultNone = 0, // broadcast state unknown + k_EBroadcastUploadResultOK = 1, // broadcast was good, no problems + k_EBroadcastUploadResultInitFailed = 2, // broadcast init failed + k_EBroadcastUploadResultFrameFailed = 3, // broadcast frame upload failed + k_EBroadcastUploadResultTimeout = 4, // broadcast upload timed out + k_EBroadcastUploadResultBandwidthExceeded = 5, // broadcast send too much data + k_EBroadcastUploadResultLowFPS = 6, // broadcast FPS too low + k_EBroadcastUploadResultMissingKeyFrames = 7, // broadcast sending not enough key frames + k_EBroadcastUploadResultNoConnection = 8, // broadcast client failed to connect to relay + k_EBroadcastUploadResultRelayFailed = 9, // relay dropped the upload + k_EBroadcastUploadResultSettingsChanged = 10, // the client changed broadcast settings + k_EBroadcastUploadResultMissingAudio = 11, // client failed to send audio data + k_EBroadcastUploadResultTooFarBehind = 12, // clients was too slow uploading + k_EBroadcastUploadResultTranscodeBehind = 13, // server failed to keep up with transcode +}; + + +//----------------------------------------------------------------------------- +// Purpose: codes for well defined launch options +//----------------------------------------------------------------------------- +enum ELaunchOptionType +{ + k_ELaunchOptionType_None = 0, // unknown what launch option does + k_ELaunchOptionType_Default = 1, // runs the game, app, whatever in default mode + k_ELaunchOptionType_SafeMode = 2, // runs the game in safe mode + k_ELaunchOptionType_Multiplayer = 3, // runs the game in multiplayer mode + k_ELaunchOptionType_Config = 4, // runs config tool for this game + k_ELaunchOptionType_OpenVR = 5, // runs game in VR mode using OpenVR + k_ELaunchOptionType_Server = 6, // runs dedicated server for this game + k_ELaunchOptionType_Editor = 7, // runs game editor + k_ELaunchOptionType_Manual = 8, // shows game manual + k_ELaunchOptionType_Benchmark = 9, // runs game benchmark + k_ELaunchOptionType_Option1 = 10, // generic run option, uses description field for game name + k_ELaunchOptionType_Option2 = 11, // generic run option, uses description field for game name + k_ELaunchOptionType_Option3 = 12, // generic run option, uses description field for game name + k_ELaunchOptionType_OculusVR = 13, // runs game in VR mode using the Oculus SDK + k_ELaunchOptionType_OpenVROverlay = 14, // runs an OpenVR dashboard overlay + k_ELaunchOptionType_OSVR = 15, // runs game in VR mode using the OSVR SDK + + + k_ELaunchOptionType_Dialog = 1000, // show launch options dialog +}; + + +//----------------------------------------------------------------------------- +// Purpose: true if this launch option is any of the vr launching types +//----------------------------------------------------------------------------- +static inline bool BIsVRLaunchOptionType( const ELaunchOptionType eType ) +{ + return eType == k_ELaunchOptionType_OpenVR + || eType == k_ELaunchOptionType_OpenVROverlay + || eType == k_ELaunchOptionType_OculusVR + || eType == k_ELaunchOptionType_OSVR; +} + + +//----------------------------------------------------------------------------- +// Purpose: code points for VR HMD vendors and models +// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN A DATABASE +//----------------------------------------------------------------------------- +enum EVRHMDType +{ + k_eEVRHMDType_None = -1, // unknown vendor and model + + k_eEVRHMDType_Unknown = 0, // unknown vendor and model + + k_eEVRHMDType_HTC_Dev = 1, // original HTC dev kits + k_eEVRHMDType_HTC_VivePre = 2, // htc vive pre + k_eEVRHMDType_HTC_Vive = 3, // htc vive consumer release + + k_eEVRHMDType_HTC_Unknown = 20, // unknown htc hmd + + k_eEVRHMDType_Oculus_DK1 = 21, // Oculus DK1 + k_eEVRHMDType_Oculus_DK2 = 22, // Oculus DK2 + k_eEVRHMDType_Oculus_Rift = 23, // Oculus rift + + k_eEVRHMDType_Oculus_Unknown = 40, // // Oculus unknown HMD +}; + + +//----------------------------------------------------------------------------- +// Purpose: true if this is from an Oculus HMD +//----------------------------------------------------------------------------- +static inline bool BIsOculusHMD( EVRHMDType eType ) +{ + return eType == k_eEVRHMDType_Oculus_DK1 || eType == k_eEVRHMDType_Oculus_DK2 || eType == k_eEVRHMDType_Oculus_Rift || eType == k_eEVRHMDType_Oculus_Unknown; +} + + +//----------------------------------------------------------------------------- +// Purpose: true if this is from an Vive HMD +//----------------------------------------------------------------------------- +static inline bool BIsViveHMD( EVRHMDType eType ) +{ + return eType == k_eEVRHMDType_HTC_Dev || eType == k_eEVRHMDType_HTC_VivePre || eType == k_eEVRHMDType_HTC_Vive || eType == k_eEVRHMDType_HTC_Unknown; +} + + +#pragma pack( push, 1 ) + +#define CSTEAMID_DEFINED + +// 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 ) && ( unAccountInstance > k_unSteamUserWebInstance ) ) ); // 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 ); + } +#ifdef INT64_DIFFERENT_FROM_INT64_T + CSteamID( uint64_t ulSteamID ) + { + SetFromUint64( (uint64)ulSteamID ); + } +#endif + + + //----------------------------------------------------------------------------- + // 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 || eAccountType == k_EAccountTypeGameServer ) + { + m_steamid.m_comp.m_unAccountInstance = 0; + } + else + { + // by default we pick the desktop instance + m_steamid.m_comp.m_unAccountInstance = k_unSteamUserDesktopInstance; + } + } + + + //----------------------------------------------------------------------------- + // 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 & k_unSteamAccountIDMask ); // account ID is low 32 bits + m_steamid.m_comp.m_unAccountInstance = ( ( ulIdentifier >> 32 ) & k_unSteamAccountInstanceMask ); // 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; + } + + + //----------------------------------------------------------------------------- + // Purpose: Clear all fields, leaving an invalid ID. + //----------------------------------------------------------------------------- + void Clear() + { + 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; + } + + +#if defined( INCLUDED_STEAM2_USERID_STRUCTS ) + //----------------------------------------------------------------------------- + // 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 = k_unSteamUserDesktopInstance; // Steam2 only knew desktop instances + } + + //----------------------------------------------------------------------------- + // 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? (Either persistent or anonymous) + //----------------------------------------------------------------------------- + bool BGameServerAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeGameServer || m_steamid.m_comp.m_EAccountType == k_EAccountTypeAnonGameServer; + } + + //----------------------------------------------------------------------------- + // Purpose: Is this a persistent (not anonymous) game server account id? + //----------------------------------------------------------------------------- + bool BPersistentGameServerAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeGameServer; + } + + //----------------------------------------------------------------------------- + // Purpose: Is this an anonymous game server account id? + //----------------------------------------------------------------------------- + bool BAnonGameServerAccount() const + { + return 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 || m_steamid.m_comp.m_EAccountType == k_EAccountTypeConsoleUser; + } + + + //----------------------------------------------------------------------------- + // 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; + } + + //----------------------------------------------------------------------------- + // Purpose: Is this a faked up Steam ID for a PSN friend account? + //----------------------------------------------------------------------------- + bool BConsoleUserAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeConsoleUser; + } + + // simple accessors + void SetAccountID( uint32 unAccountID ) { m_steamid.m_comp.m_unAccountID = unAccountID; } + void SetAccountInstance( uint32 unInstance ){ m_steamid.m_comp.m_unAccountInstance = unInstance; } + void ClearIndividualInstance() { if ( BIndividualAccount() ) m_steamid.m_comp.m_unAccountInstance = 0; } + bool HasNoIndividualInstance() const { return BIndividualAccount() && (m_steamid.m_comp.m_unAccountInstance==0); } + AccountID_t 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 ); + // SetFromString allows many partially-correct strings, constraining how + // we might be able to change things in the future. + // SetFromStringStrict requires the exact string forms that we support + // and is preferred when the caller knows it's safe to be strict. + // Returns whether the string parsed correctly. + bool SetFromStringStrict( 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 + { +#ifdef VALVE_BIG_ENDIAN + EUniverse m_EUniverse : 8; // universe this account belongs to + unsigned int m_EAccountType : 4; // type of account - can't show as EAccountType, due to signed / unsigned difference + unsigned int m_unAccountInstance : 20; // dynamic instance ID + uint32 m_unAccountID : 32; // unique account identifier +#else + uint32 m_unAccountID : 32; // unique account identifier + unsigned int m_unAccountInstance : 20; // dynamic instance ID + 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 +#endif + } 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 > k_unSteamUserWebInstance ) + 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; + } + + if ( m_steamid.m_comp.m_EAccountType == k_EAccountTypeGameServer ) + { + if ( m_steamid.m_comp.m_unAccountID == 0 ) + return false; + // Any limit on instances? We use them for local users and bots + } + return true; +} + +// generic invalid CSteamID +#define k_steamIDNil CSteamID() + +// This steamID comes from a user game connection to an out of date GS that hasnt implemented the protocol +// to provide its steamID +#define k_steamIDOutofDateGS CSteamID( 0, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ) +// This steamID comes from a user game connection to an sv_lan GS +#define k_steamIDLanModeGS CSteamID( 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. +#define k_steamIDNotInitYetGS CSteamID( 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 +#define k_steamIDNonSteamGS CSteamID( 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; + } +#ifdef INT64_DIFFERENT_FROM_INT64_T + CGameID( uint64_t ulGameID ) + { + m_ulGameID = (uint64)ulGameID; + } +#endif + + 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]; + V_FileBase( pchModPath, rgchModDir, sizeof( rgchModDir ) ); + CRC32_t crc32; + CRC32_Init( &crc32 ); + CRC32_ProcessBuffer( &crc32, rgchModDir, V_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 ); + if ( pchExePath ) + CRC32_ProcessBuffer( &crc32, pchExePath, V_strlen( pchExePath ) ); + if ( pchAppName ) + CRC32_ProcessBuffer( &crc32, pchAppName, V_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, V_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; + } + + void Set( uint64 ulGameID ) + { + m_ulGameID = 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; + + case k_EGameIDTypeGameMod: + return m_gameID.m_nAppID != k_uAppIdInvalid && m_gameID.m_nModID & 0x80000000; + + case k_EGameIDTypeShortcut: + return (m_gameID.m_nModID & 0x80000000) != 0; + + case k_EGameIDTypeP2P: + return m_gameID.m_nAppID == k_uAppIdInvalid && m_gameID.m_nModID & 0x80000000; + + 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 + { +#ifdef VALVE_BIG_ENDIAN + unsigned int m_nModID : 32; + unsigned int m_nType : 8; + unsigned int m_nAppID : 24; +#else + unsigned int m_nAppID : 24; + unsigned int m_nType : 8; + unsigned int m_nModID : 32; +#endif + }; + + 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/dep/steam_api/steamencryptedappticket.h b/dep/steam_api/steamencryptedappticket.h new file mode 100644 index 0000000..48c63b4 --- /dev/null +++ b/dep/steam_api/steamencryptedappticket.h @@ -0,0 +1,32 @@ +//========= Copyright © 1996-2010, Valve LLC, All rights reserved. ============ +// +// Purpose: utilities to decode/decrypt a ticket from the +// ISteamUser::RequestEncryptedAppTicket, ISteamUser::GetEncryptedAppTicket API +// +// To use: declare CSteamEncryptedAppTicket, then call BDecryptTicket +// if BDecryptTicket returns true, other accessors are valid +// +//============================================================================= + +#include "steam_api.h" + +static const int k_nSteamEncryptedAppTicketSymmetricKeyLen = 32; + + +S_API bool SteamEncryptedAppTicket_BDecryptTicket( const uint8 *rgubTicketEncrypted, uint32 cubTicketEncrypted, + uint8 *rgubTicketDecrypted, uint32 *pcubTicketDecrypted, + const uint8 rgubKey[k_nSteamEncryptedAppTicketSymmetricKeyLen], int cubKey ); + +S_API bool SteamEncryptedAppTicket_BIsTicketForApp( uint8 *rgubTicketDecrypted, uint32 cubTicketDecrypted, AppId_t nAppID ); + +S_API RTime32 SteamEncryptedAppTicket_GetTicketIssueTime( uint8 *rgubTicketDecrypted, uint32 cubTicketDecrypted ); + +S_API void SteamEncryptedAppTicket_GetTicketSteamID( uint8 *rgubTicketDecrypted, uint32 cubTicketDecrypted, CSteamID *psteamID ); + +S_API AppId_t SteamEncryptedAppTicket_GetTicketAppID( uint8 *rgubTicketDecrypted, uint32 cubTicketDecrypted ); + +S_API bool SteamEncryptedAppTicket_BUserOwnsAppInTicket( uint8 *rgubTicketDecrypted, uint32 cubTicketDecrypted, AppId_t nAppID ); + +S_API bool SteamEncryptedAppTicket_BUserIsVacBanned( uint8 *rgubTicketDecrypted, uint32 cubTicketDecrypted ); + +S_API const uint8 *SteamEncryptedAppTicket_GetUserVariableData( uint8 *rgubTicketDecrypted, uint32 cubTicketDecrypted, uint32 *pcubUserData ); \ No newline at end of file diff --git a/dep/steam_api/steamhttpenums.h b/dep/steam_api/steamhttpenums.h new file mode 100644 index 0000000..d95f195 --- /dev/null +++ b/dep/steam_api/steamhttpenums.h @@ -0,0 +1,98 @@ +//====== Copyright © 1996-2010, Valve Corporation, All rights reserved. ======= +// +// Purpose: HTTP related enums, stuff that is shared by both clients and servers, and our +// UI projects goes here. +// +//============================================================================= + +#ifndef STEAMHTTPENUMS_H +#define STEAMHTTPENUMS_H +#ifdef _WIN32 +#pragma once +#endif + +// HTTP related types + +// This enum is used in client API methods, do not re-number existing values. +enum EHTTPMethod +{ + k_EHTTPMethodInvalid = 0, + k_EHTTPMethodGET, + k_EHTTPMethodHEAD, + k_EHTTPMethodPOST, + k_EHTTPMethodPUT, + k_EHTTPMethodDELETE, + k_EHTTPMethodOPTIONS, + k_EHTTPMethodPATCH, + + // The remaining HTTP methods are not yet supported, per rfc2616 section 5.1.1 only GET and HEAD are required for + // a compliant general purpose server. We'll likely add more as we find uses for them. + + // k_EHTTPMethodTRACE, + // k_EHTTPMethodCONNECT +}; + + +// HTTP Status codes that the server can send in response to a request, see rfc2616 section 10.3 for descriptions +// of each of these. +enum EHTTPStatusCode +{ + // Invalid status code (this isn't defined in HTTP, used to indicate unset in our code) + k_EHTTPStatusCodeInvalid = 0, + + // Informational codes + k_EHTTPStatusCode100Continue = 100, + k_EHTTPStatusCode101SwitchingProtocols = 101, + + // Success codes + k_EHTTPStatusCode200OK = 200, + k_EHTTPStatusCode201Created = 201, + k_EHTTPStatusCode202Accepted = 202, + k_EHTTPStatusCode203NonAuthoritative = 203, + k_EHTTPStatusCode204NoContent = 204, + k_EHTTPStatusCode205ResetContent = 205, + k_EHTTPStatusCode206PartialContent = 206, + + // Redirection codes + k_EHTTPStatusCode300MultipleChoices = 300, + k_EHTTPStatusCode301MovedPermanently = 301, + k_EHTTPStatusCode302Found = 302, + k_EHTTPStatusCode303SeeOther = 303, + k_EHTTPStatusCode304NotModified = 304, + k_EHTTPStatusCode305UseProxy = 305, + //k_EHTTPStatusCode306Unused = 306, (used in old HTTP spec, now unused in 1.1) + k_EHTTPStatusCode307TemporaryRedirect = 307, + + // Error codes + k_EHTTPStatusCode400BadRequest = 400, + k_EHTTPStatusCode401Unauthorized = 401, // You probably want 403 or something else. 401 implies you're sending a WWW-Authenticate header and the client can sent an Authorization header in response. + k_EHTTPStatusCode402PaymentRequired = 402, // This is reserved for future HTTP specs, not really supported by clients + k_EHTTPStatusCode403Forbidden = 403, + k_EHTTPStatusCode404NotFound = 404, + k_EHTTPStatusCode405MethodNotAllowed = 405, + k_EHTTPStatusCode406NotAcceptable = 406, + k_EHTTPStatusCode407ProxyAuthRequired = 407, + k_EHTTPStatusCode408RequestTimeout = 408, + k_EHTTPStatusCode409Conflict = 409, + k_EHTTPStatusCode410Gone = 410, + k_EHTTPStatusCode411LengthRequired = 411, + k_EHTTPStatusCode412PreconditionFailed = 412, + k_EHTTPStatusCode413RequestEntityTooLarge = 413, + k_EHTTPStatusCode414RequestURITooLong = 414, + k_EHTTPStatusCode415UnsupportedMediaType = 415, + k_EHTTPStatusCode416RequestedRangeNotSatisfiable = 416, + k_EHTTPStatusCode417ExpectationFailed = 417, + k_EHTTPStatusCode4xxUnknown = 418, // 418 is reserved, so we'll use it to mean unknown + k_EHTTPStatusCode429TooManyRequests = 429, + + // Server error codes + k_EHTTPStatusCode500InternalServerError = 500, + k_EHTTPStatusCode501NotImplemented = 501, + k_EHTTPStatusCode502BadGateway = 502, + k_EHTTPStatusCode503ServiceUnavailable = 503, + k_EHTTPStatusCode504GatewayTimeout = 504, + k_EHTTPStatusCode505HTTPVersionNotSupported = 505, + k_EHTTPStatusCode5xxUnknown = 599, +}; + +#endif // STEAMHTTPENUMS_H \ No newline at end of file diff --git a/dep/steam_api/steamps3params.h b/dep/steam_api/steamps3params.h new file mode 100644 index 0000000..c0741b4 --- /dev/null +++ b/dep/steam_api/steamps3params.h @@ -0,0 +1,112 @@ +//====== Copyright 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef STEAMPS3PARAMS_H +#define STEAMPS3PARAMS_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// PlayStation 3 initialization parameters +// +// The following structure must be passed to when loading steam_api_ps3.prx +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +#define STEAM_PS3_PATH_MAX 1055 +#define STEAM_PS3_SERVICE_ID_MAX 32 +#define STEAM_PS3_COMMUNICATION_ID_MAX 10 +#define STEAM_PS3_COMMUNICATION_SIG_MAX 160 +#define STEAM_PS3_LANGUAGE_MAX 64 +#define STEAM_PS3_REGION_CODE_MAX 16 +#define STEAM_PS3_CURRENT_PARAMS_VER 2 +struct SteamPS3Params_t +{ + uint32 m_unVersion; // set to STEAM_PS3_CURRENT_PARAMS_VER + + void *pReserved; + uint32 m_nAppId; // set to your game's appid + + char m_rgchInstallationPath[ STEAM_PS3_PATH_MAX ]; // directory containing latest steam prx's and sdata. Can be read only (BDVD) + char m_rgchSystemCache[ STEAM_PS3_PATH_MAX ]; // temp working cache, not persistent + char m_rgchGameData[ STEAM_PS3_PATH_MAX ]; // persistent game data path for storing user data + char m_rgchNpServiceID[ STEAM_PS3_SERVICE_ID_MAX ]; + char m_rgchNpCommunicationID[ STEAM_PS3_COMMUNICATION_ID_MAX ]; + char m_rgchNpCommunicationSig[ STEAM_PS3_COMMUNICATION_SIG_MAX ]; + + // Language should be one of the following. must be zero terminated + // danish + // dutch + // english + // finnish + // french + // german + // italian + // korean + // norwegian + // polish + // portuguese + // russian + // schinese + // spanish + // swedish + // tchinese + char m_rgchSteamLanguage[ STEAM_PS3_LANGUAGE_MAX ]; + + // region codes are "SCEA", "SCEE", "SCEJ". must be zero terminated + char m_rgchRegionCode[ STEAM_PS3_REGION_CODE_MAX ]; + + // Should be SYS_TTYP3 through SYS_TTYP10, if it's 0 then Steam won't spawn a + // thread to read console input at all. Using this let's you use Steam console commands + // like: profile_on, profile_off, profile_dump, mem_stats, mem_validate. + unsigned int m_cSteamInputTTY; + + struct Ps3netInit_t + { + bool m_bNeedInit; + void *m_pMemory; + int m_nMemorySize; + int m_flags; + } m_sysNetInitInfo; + + struct Ps3jpgInit_t + { + bool m_bNeedInit; + } m_sysJpgInitInfo; + + struct Ps3pngInit_t + { + bool m_bNeedInit; + } m_sysPngInitInfo; + + struct Ps3sysutilUserInfo_t + { + bool m_bNeedInit; + } m_sysSysUtilUserInfo; + + bool m_bIncludeNewsPage; +}; + + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// PlayStation 3 memory structure +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +#define STEAMPS3_MALLOC_INUSE 0x53D04A51 +#define STEAMPS3_MALLOC_SYSTEM 0x0D102C48 +#define STEAMPS3_MALLOC_OK 0xFFD04A51 +struct SteamPS3Memory_t +{ + bool m_bSingleAllocation; // If true, Steam will request one 6MB allocation and use the returned memory for all future allocations + // If false, Steam will make call malloc for each allocation + + // required function pointers + void* (*m_pfMalloc)(size_t); + void* (*m_pfRealloc)(void *, size_t); + void (*m_pfFree)(void *); + size_t (*m_pUsable_size)(void*); +}; + + +#endif // STEAMPS3PARAMS_H diff --git a/dep/steam_api/steamtypes.h b/dep/steam_api/steamtypes.h new file mode 100644 index 0000000..f74df1a --- /dev/null +++ b/dep/steam_api/steamtypes.h @@ -0,0 +1,181 @@ +//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +//============================================================================= + +#ifndef STEAMTYPES_H +#define STEAMTYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#define S_CALLTYPE __cdecl + +// 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 + +// Make sure VALVE_BIG_ENDIAN gets set on PS3, may already be set previously in Valve internal code. +#if !defined(VALVE_BIG_ENDIAN) && defined(_PS3) +#define VALVE_BIG_ENDIAN +#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; + +typedef int64 lint64; +typedef uint64 ulint64; + +#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; + +// [u]int64 are actually defined as 'long long' and gcc 64-bit +// doesn't automatically consider them the same as 'long int'. +// Changing the types for [u]int64 is complicated by +// there being many definitions, so we just +// define a 'long int' here and use it in places that would +// otherwise confuse the compiler. +typedef long int lint64; +typedef unsigned long int ulint64; + +#ifdef X64BITS +typedef long long intp; +typedef unsigned long long uintp; +#else +typedef int intp; +typedef unsigned int uintp; +#endif + +#endif // else _WIN32 + +#ifdef API_GEN +# define CLANG_ATTR(ATTR) __attribute__((annotate( ATTR ))) +#else +# define CLANG_ATTR(ATTR) +#endif + +#define METHOD_DESC(DESC) CLANG_ATTR( "desc:" #DESC ";" ) +#define IGNOREATTR() CLANG_ATTR( "ignore" ) +#define OUT_STRUCT() CLANG_ATTR( "out_struct: ;" ) +#define OUT_STRING() CLANG_ATTR( "out_string: ;" ) +#define OUT_ARRAY_CALL(COUNTER,FUNCTION,PARAMS) CLANG_ATTR( "out_array_call:" #COUNTER "," #FUNCTION "," #PARAMS ";" ) +#define OUT_ARRAY_COUNT(COUNTER, DESC) CLANG_ATTR( "out_array_count:" #COUNTER ";desc:" #DESC ) +#define ARRAY_COUNT(COUNTER) CLANG_ATTR( "array_count:" #COUNTER ";" ) +#define ARRAY_COUNT_D(COUNTER, DESC) CLANG_ATTR( "array_count:" #COUNTER ";desc:" #DESC ) +#define BUFFER_COUNT(COUNTER) CLANG_ATTR( "buffer_count:" #COUNTER ";" ) +#define OUT_BUFFER_COUNT(COUNTER) CLANG_ATTR( "out_buffer_count:" #COUNTER ";" ) +#define OUT_STRING_COUNT(COUNTER) CLANG_ATTR( "out_string_count:" #COUNTER ";" ) +#define DESC(DESC) CLANG_ATTR("desc:" #DESC ";") +#define CALL_RESULT(RESULT_TYPE) CLANG_ATTR("callresult:" #RESULT_TYPE ";") +#define CALL_BACK(RESULT_TYPE) CLANG_ATTR("callback:" #RESULT_TYPE ";") + +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 uint64 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; + +const JobID_t k_JobIDNil = 0xffffffffffffffffull; + +// 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; + +typedef uint32 BundleId_t; +const BundleId_t k_uBundleIdInvalid = 0; + +// 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; + +typedef uint64 AssetClassId_t; +const AssetClassId_t k_ulAssetClassIdInvalid = 0x0; + +typedef uint32 PhysicalItemId_t; +const PhysicalItemId_t k_uPhysicalItemIdInvalid = 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; + +typedef uint32 AccountID_t; + +typedef uint32 PartnerId_t; +const PartnerId_t k_uPartnerIdInvalid = 0; + +// ID for a depot content manifest +typedef uint64 ManifestId_t; +const ManifestId_t k_uManifestIdInvalid = 0; + + + +#endif // STEAMTYPES_H diff --git a/dep/steam_api/steamuniverse.h b/dep/steam_api/steamuniverse.h new file mode 100644 index 0000000..dd384dc --- /dev/null +++ b/dep/steam_api/steamuniverse.h @@ -0,0 +1,27 @@ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +//============================================================================= + +#ifndef STEAMUNIVERSE_H +#define STEAMUNIVERSE_H +#ifdef _WIN32 +#pragma once +#endif + + +// 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, // no such universe anymore + k_EUniverseMax +}; + + +#endif // STEAMUNIVERSE_H diff --git a/dep/zlib.lua b/dep/zlib.lua new file mode 100644 index 0000000..0dc202f --- /dev/null +++ b/dep/zlib.lua @@ -0,0 +1,39 @@ +zlib = {} + +function zlib:include() + includedirs { + path.join(DependencyFolder(), "zlib") + } +end + +function zlib:link() + self:include() + links { + "zlib" + } +end + +function zlib:project() + local folder = DependencyFolder(); + + project "zlib" + + location "%{wks.location}/dep" + kind "StaticLib" + language "C" + + files { + path.join(folder, "zlib/*.h"), + path.join(folder, "zlib/*.c") + } + + defines { + "_CRT_SECURE_NO_WARNINGS", + "_CRT_NONSTDC_NO_DEPRECATE" + } + + self:include() + + -- Disable warnings. They do not have any value to us since it is not our code. + warnings "off" +end diff --git a/dep/zstd.lua b/dep/zstd.lua new file mode 100644 index 0000000..4616060 --- /dev/null +++ b/dep/zstd.lua @@ -0,0 +1,37 @@ +zstd = {} + +function zstd:include() + local folder = DependencyFolder(); + + includedirs { + path.join(folder, "zstd/lib"), + path.join(folder, "zstd/lib/common") + } +end + +function zstd:link() + self:include() + links { + "zstd" + } +end + +function zstd:project() + local folder = DependencyFolder(); + + project "zstd" + + location "%{wks.location}/dep" + kind "StaticLib" + language "C" + + files { + path.join(folder, "zstd/lib/**.h"), + path.join(folder, "zstd/lib/**.c") + } + + self:include() + + -- Disable warnings. They do not have any value to us since it is not our code. + warnings "off" +end diff --git a/generate.bat b/generate.bat new file mode 100644 index 0000000..3e68b64 --- /dev/null +++ b/generate.bat @@ -0,0 +1,5 @@ +@echo off +echo Updating submodules +git submodule update --init --recursive + +tools\premake5.exe vs2019 \ No newline at end of file diff --git a/plutonium_logo.jpg b/plutonium_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e9bd5671bf833cb45bcb8c7e0223a394f0b85198 GIT binary patch literal 32511 zcmbTdcRXBO`!~87j21$4qs~MP(PN^^t&3g~Jxa8wql-?;7`+BjXNU+Q(TU!B)Mzn! z5Q!e$J97V?=RM~=|D1EiXU%5qvaa&IzU#WyUar1fVS$_K%4*609v&W`iu(bsW`PF) zF%c1%h>#cz29uBwlakZiASWXuXQaMGNyEa##>&D3fw02_x!F1IKp_yGd%SmqgvG?f z*tp>`k|NTAqGBS~55XfLAt5ItXSi{LL4*UsA@cwEbJYS+5#tGgQbBl}06rBShzjqj z17HIHJVKnb*8=~~2M-@aKu828CLtxmT~Kusz{dlD@CiVKgaib*tAlXo0Rk#QY7Suq zqFcI8z?>d5B4G)6#8AbGR$9HUJ1|je&u|h_I(i01rrTWHJiL5j;u7~H;rAc>qok~& zs-~`QU}$9g=&^~7t=&_52S+D_m$#3vpMO9^WYmk7uU;b)lafMvxCWvkAmk7xqE^raKk>N5DH29ZqnMCa(Mkdp z)!U)9_WVjp2NPSoy?d?NU(NpCDHi_!OSAtf_P_O-0mwmkIOl<=06Aa>$&(*SYNBoe z@Z=-^)8uJ%HRK6tbT#pjJZL;68XY_hRo(&2qvCEx$W4{S*c-rY8mq8uo?LvS9vV=U zl|10W$ME-kJ=XLzgh}TEn|2?AKs18mK{lDU%G3aag z{#ER{6OG9arN(uD9Ii)m$WO@;sC-Zq!y7Kq(W&1A@Yq$0?FAk9e$$ zoV{S~9#b>`wNNfzmZ!3(mbTfS(?H)r2FV){5nUc#LA=Hec6Uhh6gaeH&#~A`aH$mV zjNA;L%qlkWlzfj8vU{onlJ9{fB+O*>MOAm#uD&Aw#t5;2X0g_-#P!2Cq)TTr2L=B4 z?8cd`!-t;*%M_27LJ^S!{*!a_N7y$60{a0gu;F_}bBx>A8gHP97TT&|5TmOGrSSrvT>y z4YmK%8rOa`(HaLTb>(wGG}fsooKYDt_&DR4XzJiwqyD}3KW_Yg9C3C{$K(>^|21Sj z5{I(Ceigd*I_?oXcsv+18V@O~ffSY}`JaQ5z-@pgxl#uoC(gA`uA3%)VrpXi|NQZv zPI+=1)bOr7_!r2X0@onI4<-EDFt(D3m+N=_plKAnW~bgql1h0W7mRAugQ=I76*i#lEfB*2FOC&qA}95JGke zp`_s?0HRkWl)L3Vx?O4hO;X#Pl`=d9dKYG>;N#LGN@#SMsoK4Fz`wjEFFn4v7Eht>Yoq~e0>4ps2 z`n#3yzHjOsQ`oI=ihy-^Z($Tct{{BVJ=nrym@STRYpe7AqsBtp@3Dq&)E>lp#42vfDJ=8Rx zC^7uU-JJ}%_}8aNa2~ymvrd4f5>K5Fz~Kd{c#EbI7e%;;#`%i(#{I+{O>3`%7E6fXC@s3E*x^P3gd?-6?oI zDo!baPQfu07VDAadj-G?XwW!FD)Y7~NcMmjdxu@;0uFKs0T)<_4Fe;DS~%P9r`qpt zfg_QK+AJHkyWn@ju71NO3}~4Eyq~;^saBFh@++ZovN?TAE$2#rYt0o#;mOf!J(^BoDCu!4=S?_4>l}9P zzPr2pvW#k%M>|96)2Hw|YOp_pviGP`$-~+KVi?Mk5?ry&xL#cNN#rJrdLkYpLk|?X z1E&E7^uES93FBZQ`3?+3bnA9omSb>%t*bQ~`a!i7=B@x`7-k#}>CSl|e}gEQi3iQ$ zC|`*51{m_Q2pP~IVFj9>dQ_T3Uvbo+)Pe&@A-*osOczPS#e?$?4iPxKMHbN1=7$2j z@)%%rDW2q-nBqy4d0TLJbHx!04i%mElW6|ESG0Dr?jY>;3P7};}zCIchch#RnsI|!cikipW$D4YvO*#Ar>dUJc$yHrjoCNR76nhi7!yu;AEBj3^TwVzg^(+@KZ9+Fm$gVT~MJqrb=SiNhd-& z0(AhP=2;_)-)2!gWPs?sGke~r4bAIjIrIK=aYI!vzNiY)>dA=){90FRSkZ`$vAhCW z9#p(G1kuY{yF8iXA{EuM@%7@TodG2Lu_pipSDX_wf)fULX>7n^V+;!-3iK&o8o$gU zZMTOtmkT`-GM<2FU5-fpX$9rQCw@?T0YrqqSI|)ggQvi;(At?XQ4$ODZl6B{!rV#O z+VDSeCb#gZ4Z4UHGGVF1I`S>u)s*Yo4|wzdt2t0Y7I~=Z3WdspS=oXN;ZE(#=6H$*DO7Mv#*>@CvXXxe9F{iEmP(vFesKkmy_iXxgL&JI{H#UZC#-s}Y zLMa+NZIHOYtEV%HeCCyr){!kQ-KscfC3tIT#u({%I#rG0k-m881vIDXf*=oN-fsmo zW&{N^SYG*j>^+*=qA+zrt`=8b-eX+<1013H%;@mcnz{0HN|P$`0&Tp+MG2}(VqHQ= zP5lZbsn8kTZ`ZKIQQ6-Z===+R+(dh$lcC5Or33QNxSn=zbm9W-8tynK1L~Uk?{fde z?)ChugvM8gFVekguh zEY`75=7T ziA?R_D{~tB@?q-kCx&blpn@3l3b0LzZNW!;)0>{Xc>jw*`dj|Kx&A=P$JdEOkFZz@ z^bs%>Sm0n{m?2-hR43s5IC}}->v(3>9FLfW4`_G49R?==R70wfuMf!N%i%WKYE7{w;DJiDYFN{UGq5bTA9Eo~p<4lsQ^ZfpHTT=LzF$Ne0IyOXkYX{?vY@ko56v;xr7Ki0rq`!zPMV2SHar z)H%!YtJR4os;ilJtbuAs?DA?4m=VX#*(ojEJI017=4pl%C%#rU=fZkqB!@SCYYpc* z`TNJn6VT15=+&;z<`l_Z@X6Gc)cELsn^$x8ChHWlCy&Lbo?oay$C=8E%W1fPRa+w+ zqH}y^(ojH1S-4jkk(EfH!2OE)UaC^8n7t2&p#k-yYyj|7lX>5ILvYQP(U4(&JS-IW z13I83hd=Itu-P6hV(Eo|{7*`^q0jKWG{*p>xh29fLI6YWa5Vx2>KT?~+4xcS3vBU_GyHwP z^Y(Aq&3VgPaW(_*Vk#vlDCD{Gpp0zq&O0@HZdnr+y&um+L{#F1N)E3p3+qhZV=_iM znk3Og>S+?IqC3@%(v|dVYTI}VF;L_aS~b*Do=!Ozh9Y-Hq_8%WCjENCSe(S=Akm?# z*U6DV1V+Q!8ZW~<8Ow_`s3tIlV@UJ~F|!)!#_YVEVx^CHJB5m>NEJNsc`;5w$x$-z zlV5t)nJDtLsynJXJ>cl%;?pY{O>!}8Tk*2aK}w9!Uk@~^@iBYcOi%@>!>7wAw#dp0 zXXhm8WWMo;L_ymr`-xe3&13}2BdRmq&ol_azDsislaIzT1$OX4vqk#3wXOIS5e~}PN#CvFX0KzNkr;1q zm{nh@W1z9lR$_JpMtD86$ui61uUy@rKUnC`lqUb={fOv^e9DR=02C za!sM@N)`V;ZpDNMKUt5|n}Y(s%1egxp_`rtgR6_nYCKB}f;;tt{GvkZ$?FzSop>st z!?zIo6MDs6S5%j@sXw9OYZ2K?h1(w4S?jEIR9xWPW8tz!nc~MYZLX!aQ!h%rxoWG` zwr#UTGXx4du zuXsd$dYQD8%VK|KCeP(UaOdlAH9;4&p-wXoN3b>i8Ikmk{Ki4_WNqE}&nk^=e)&(T zu(86iytf8oo%nBjZua?awuB3h?`hphwK;Au`MNFW{Sc}UHHgl{pH~Wj-gzmN^|9BO zTgnsB7LjBYdH?P92kLghxv!mfo^mUB(#B_{NG}uY$00VIDhv7(#yqP>l~<)99`S+5 zs0<7{O_flUct*xU6NM+U@8(fsqVn7_^)@*HCFDuog39;DgD8*n!WD)CjHtLagO@}^ zTd`py-h*0U~AJ{>K~r-L>n0WQcaeCdf0yceVdCZyr)G!2s5Os+7jd}U_6skxs+UlNzdevlciG$LY7uiW`YSq z@t{DK?NP6!47vWt_%bSer>M5rT1e7dqNkc{EdxMW?fYJY$m)ZF9RYT^Q02M4;CV5O zi@^w{8;WQB$QGNGG$ceJB)Op_BL7B9iGc#y_Y!c0_X73=!2SK$mawGpCfJi(4bll+ zErkYbS)s(>c;T#DQxFJK0xnN~wT7FwIc)uUL2x!9>`e+h2xV^Jvj>b)S+eqmE$p$L zYCYYOEv-Y^P{@YoOK6VRK$_m2vxtarh9(l&O)cVQKtR@dY>EMj>EnAMHJz4l0EV&; z^*h)))~EPsl@!(`I)b?srrokK5Zkb4oa3^?Qxc>*Z9bgYxiPOyPE_^5<8BGV*t+Xds1w)oM zm4ifM;rUomkb`-FJ1i9p--v6mI{0(cIUe&?*IH%fm|0%Z9s~)!tP>*KlZFCl z89prB9#pi3b`m1=B8v!?F_0ERJ|$)2jM}e(1`ip*7`;R$J9l26r8xxQ|vG+nT;TD~}y`l!QuaAZW5K5B%^U{-l1r(ZVN-+r_t-IiT{+(Ucs zqF52}z`^(?X@XG}bQvQp$bS!ZBZf?iY+H|yO+B8w>Oq1){Pt4P(?G+VyT~VIK4>&O z|4Hz;zqgHx;J{JQ06ibWMRDQ%TAL%Z@R`0oOP_^h6?aPZgjc70^h`;Xpzhv7RyFaE z-lKxvbqwnW9H|KLDI$Kqo}|vPj^WmUBeRXb>IA_Ph`u9?u)#iZRe@DX$2c~{wx~)x zG1iLTrNB;$ItzMKxFVH;)F=0TR#e5E_E1&OKsZo`vm_k4p>;D*XAROROkc$t7n|bM z9yXd`bA%bdN^l8+#UB@~2Vkb(St1`ugd^7!K4j&c%#oNJYM< z`Ww0l4~0>C){Ys3I`W+Fp1Jq&&#m(LNELtm1;sf?jr*|bj z_sATB2-{m(+LSmOl-T=v@CZsMu*(DiOt102In0zcl{sQvx@b=uKi6D;yKY4b3jPi)(s~ah(||;Y&j8UPDxL^{ zW^_uA#1m^dR#qC4YwBUc#^wS?m(swvRWWvZXx>k4=%$A}7HM9&jix4Ev9&s+F(V4o zHiV({@RE$Maon^biK-71(B_gQBqRrAqy39(c>>Aw`WB8y{r7(>Wmtbu_~A|yds*cl z%%+aEHy-uahF<}sE2*3^JuU~5!jy0jih@s@J#4JhSEWT1FQLQ{7LXY(cVuFDEdL9+ zB%`63a7t(zt6jrxqKG&fe)n=*2Rvh96UUOn%oePKhwSv_HeX89l~DMMN1NLqYIDBvw(SL1-@`O(YC z^5Fw9dqhOGBy{9dzw?vCcC0%MM5xuK%L9c1W!a)arLqh=U)@1=(zd|)rdwNohf`2i z5Y@*~`0+h->MdXUQvLb!+MQF^3-cD8tw}T5bSP z*wlt$vs=~y3Ia-31!)h9V!6;ePv5Sva*T24Au1?AhnoReR)`i5;2F%bv+ozEPZuMi z#dJHxR$kr;5i6TJ-hDIcLnPXhnKUb6SVzUg!N`bEjlhe2kMHLz5kK@Jg@g0JDaGul zxpEpS2-X%9iWu@^=vEZX(UOB3F_Q98&*I&oSqI`pRO$f4wh8Ani?zXP!xs;Pl5WQ} z7s0!LWfpDj`sl@!Q6**zrx};Vp|Hlfr&6KN*m)_BjmHLM1qHTsKGJhD!qEeUl6thS zp^2W#U^6Q=i?{yxiH|UM^6jL;LmOUDd?!8277j;r0a(&QhD-W+O;UyTj))!wyMNxk zeg^8n3kp2IPEmR|O{U$6GPwU7?cUCAwf;`#xH!h^h{ArnA9Y6^V`GkeF}H9}a|dr3 zj0&gJs)gxAm53-~qukp87johjX;dI&eRt4e^q~LC(*u)7UHK2+xTW)@KoX}VSw7Fk zCa>z=9+*$O_4JX^j1DyoHsvR7)m=7nc{9lIc~Wf3bR6K+5;L|@w6Oj43W%ZqPBE3YCd=i@ifiyD!RzL$T;A)1Tu?LnS*RJQjLDR=BM`Py=2pzq$|kp zFR~bP6tWcIno&Q%RWU|#kG4kAxEtU}Zb_pVlO4OrxiiLtl9`Z~ZikO09?z)pGW3GN z4rRd04~?hf^?!Q&c2L-Oc*b#X!9S6I{KS{xMzQ6$Y2Ho7KSGxe4$-V+JAD$pjap&OV$Eu=Dr}H0hCq}wj-Nz-g$i=4-U<}O`f^^&UsmnGppwQ`QG#K-^ zq9XO^7rz6&*QeTlEQr)3Sptj_-=@rt6dv#qzuwk5gLkWMfg`z)4`^m$-$35`d=l?l z)z14{U65{c?C?7lyblxtvObb!;&kTT2PM_dr*fS$k z{7nyJL;jD?&H-qnnqMGZD{4fjw!CBgmj~(XbeH2n>8HjXR{-%dzS)pP_OHEQ z0{?EgSF=Vh+t1#3okjPXt7z9Ffyd{?4B15PvKF_J>uIJLF+mG;L$Kw z#Avm}nB#_Vr5okkRzUdlECGdr^^p5}a5=%HVw_7*BqoSzFeqyoBb+O_VG$q9GM742 zU!AC3l#R<4b0>Xju;wbtAMbtYb(RLO>5#Y0=@4C}d9b6&c8H~atJtG$-SoWxqvVg+ zgLl$R0Y+bQbPElsAP2<+1hFZj8{cVb8^-dRQ-Af1e%CXSsLOnMQ3QstzNmD0&NL+o z)7_lWSN%z4U}jq(Z3W!Vcxmo6RhTxXWljNW3F4KG$W9q|wqUFM>XtFI(q*SZp|fJO zCq>#%H@6h^2##8*kxdbq^%!k%kS6h7>G;F+BGYMdST!V1^At z(mxbWYeb|AvaH{(dW@Dc5C!OWKw0>Wb)I)QM$E*qPZ`=R`v3ZA)0nt9G)Y2sM|n*r z>Ie&Q_rzP?%82*V?QPo(&}VbTunl31%t7hPCZG*noV6^%C_}w9+KeVO7-t!0Sw@A+ z)Q@_%ixPS1DVb@XOc6lXCd+8u-i_C%|4eSHa#Cs)C$VtOQ?2Z28~s^~KVHJBtquwM zgvWSLkY_|k_QRmqP;cpx zty!%v^%t#!rS(~URgxPIovo#eKdJ1@J^)c4r!GLxFe-=dGltt^WN&b8?rTm$8x!Zr z;~4mYb*J)Vz~UL5T-z7HR+*O>SAb}b$*qltRMY`Rjn%c>$QTzKXf~_Um{MPO;yir? z*vqEOfAb@Cek(C^v}`c;GjWA3$`!^pfY&M`o8lPE#(hD4U-$~BiMy<_-PP69Qpx;M zw}pJwrm$gN*09*?rBPXvp=w91d|QpfrDxMqbR>YOEphC|7;=;$`XxNMdgZ8ia^T~J zxp7n@|GigLw;X8_+Gxod6G2g+DOPlt`J5aHqsslxyi=SkOlgmsUz!F4m)_|8lCUiB z)VNCJ$dgk2Fplb6GDm5wX-3exHqQM0gZL-K%3tU{d_p*s_hDv6tPiHTn-nnyOBg;Gv`AV^O$PTanQ9;3WIH$HMozi) zpLY)m;7Y(t`{+!Gb>x%EXI|yjZZCYATbE=cRu@`J zbKCkY?W#(=ZTS6;PuW?gEOk!%m||i-`G$!9>pQR%XpuX-Okzm@jWRapn_KNNC!aLa zSMAhXCd%WiPZAKW)cdMw`*+DhI21#Jdp8E*#U2#uhZS3S-71N9VcHVmjNTuE1;y- zVW?|Z6XKCDCgTqk{}l|`v501itSoL+SUi5_`e9$JRCd(9#no(`!yoMUL!P0fEO6;% zoL7uxJ6*=pD4)b`QINDEXTF#wEhH>f{-+;f#et@^^#K>_v=GG>#LEd7ND!XQF*(qt@=MdH zX{!AkHYpbKkx^wZEOxEJrHaA3KkDN|eoVGCH9uOOg<+=-%EKv$){(?%YF5TkRt&_nF*6g9v{Kt8aRq!* z7LxKfU^K4&?dUDu7(vOL44s1*O+{*$4~AeMmBv1aH;N=Fa%pA1LCZ(0wE9-Lj_mmf zI5pD8tb&G!=35a-n*sJyRX*-rlm#36Fp&9z;6;Rf$y=TvH=c!1~reRnsI9xbh;ZLdMEB3c0 zV`D#WhIOCGEkE{5naMMZplnoQ;I1Z;TxJ?&~7`-ii-0ryRCDXNdNuzMEf5D*S zFune%oGX>c@R{UoEj%ckMKo4^NCe)qa^d7pJ97`O>+8MSvn&=Yh{ioIL zWf2TnNG2@|#PPFDrga?&%^ag%JyA_qGMWj+%U+Me2tG}6J6$*r!#EduPENY;M;YFJ z-39cDH#p{*%L+T+z1Rt9tC3|j>6+P6?ITmUC9+Nlq6jN1Qu_F$m{!(}%Y!UzIf6=?xn}cI-ZU7jkn!0xXhb zGK#Z@J{;5oE>sLT+~OP1D0ABVXUIZrg?YB=e$p}dBqCAAV555ew@+!k`fSBLR@{r2 zh6Qu!$dN|j8w1+KPHrn3AG0ccbFjSJyC8hjybIgos2tV*Nyp!Yw&Z8?R6%T?WaM;a zAT@mIUCMjy9rp+!M6N5Rr@_Hhndptt&9q(@V#wY=01wL`b&~dUyYc6s^DlQX^}l{& zve09zu@`5}d%R@6+1lxqv4Kg6+K0uqUw@^LiGOJI81M2pS!!T@Yp=P}dnWkCHOcJV z#meuka~y9^Je1|MD0!;tGLIO+mn((DYmH_$C)Qg^m7J$ENI)vIMpK*Ae=Oau`sORB z09k=hnbsReGjd)IPQ@=cjy^~C?o0bG!+*)?emg2l$yC~Xi*C{rC@d_H-fh^5Ym_-f z?lTuuO$U*HcS;THzyT{uOv3hvDOn0hVn*}p!h?{`f+WG5st zNB1;bre*E<2iBCi4SMjS9o)n+pwUapT%x&OS#HR-URNgPFzBVM)ZiMMxj1L#*EaHp zg!?jd;uSr9J=@FZIC_&Tk7~^;V1cM2RiL|wRy3_X^Vbys(+GHe1@!p5^_P9~?m?hy zr|s(+N{>0VcRjLQ<$mRAp^YX@*%3eK7ld@<=GD&CJ;u&Y^^;WU&PczXsQg^5{xuvh z{uLJ8pQ+L$_rur^$bMdYpNREs3D1Pu?CE85StzEQf75@zHDa{txnP*IMiI5#>LPf} zL*FUGV8;8PI;jvAQ_=n4he`z`u9S)I%EwvsrSjpoC7$2MP$ z)suXyLnSp%vOuA3HHP5AFSlBSue4RGNk6y^tj7qa~mS;}t@A%k6cv4VofE_feWzMd=mr@(S1!zE{<7 z1qkaZd{@7?0tPR`^DkC65r0UfPbfWqhP-ttaXEGl76}f-Hs&1ZOwzUo9}btWt(&s1 zFIG*-?NeO=({%atmrW1QHO=;?5W=?o(f5&SWkc}yScFBSMO0DVgk|0p5Xs+O0HLdu zJ3c9XeixAC7^48w>$08uF$Hbfw*-(~0X}L|`<@{fxpwF!cfD`bK*;IMGF6T5mP;2p znBBW+KP->K=&k@-a|yX9xdSRQ-_&~tW&7R5aa2P=hT0o(8)lDn*R0>`1&=x&xb55a zo(*g~cWU^3Fe>+hg-%YQ%yMLZ>vXvO6g?nW>uQjPH84pM)%VXIz zF9_|K`<%#p^Kr@~e$g_-Sp}N(Rd6V+%)~rg5gjVW*5)UNG1!&JyvQjxpMBQ`p3C`_ ztt>}w6g82g_zNY}rwSc|FnwNq>e;Wxsh^>^Qfcj z8M+V4?BIcLtyATNz-OtmdWqA)g>3E7A$L2aDz1P#kl_5@b2>pkMoj<-RGHw*&Isv zy?tqbdjK>z7htHRN6uFuklUW<7cvdcDG|vf1Pa2-|fH^ zerhil<-@>pVoZoy5LJ;VI*+`OiYNi@#hOG}Fn~!ZJ~;QNJiguEX~u zA>Q-0O>hCisPP8p-B%B@YWLip_%X1a+voJN#hC9RFRG_hCuvs9Okb@?Uf$k1S&3__ zE4`1fe{W)rm&0Y7#9C|RacW?$?7IN2nyZkcG`u#q2q(cF$3ogi5_pWiaJ8ihz)Y12_db0nLkuf97(sjq@jhDi{ z!0kX|oAHSK^3~Bg?<A!4g^**m6)*160Y_y~9j1zVCS}fJv0cj^ zN`gEkjD!NW1%#v3d3lw%INGv!^Rsw`xWe_xd-SDQtv%?}llF zWqp{TuI|@@7(r+jiJseC4w4)3LTDZkgc9*s0?MLS}pL+7SPdU1SA?I%FiU z^&Q5TRn}DPrD_x*)+9Pri3KBRV4o6t_y&lGds#!*-|fUj-FdF|VcwA`wpbu$T)x|x zIrnxt-5p|X5Cpe-7KYt@TqiY6hDaSdh*8n^HJ9yL+ejkppb||1hh>36{kJSO>$$_E z88Jn(r2NYvilb{jMl$Y?XX1@RmZkz5XC|n8eZIfBG%$U;gPg| zlfo2~6=2^dPWh_lwCQD#P094E#hlr;L4z0R$UF8$dDc4i6;Ex8;wRhj%; z{1F{s{E8%m^4O!x-6cKbhq!6`c8r@g9CX1cJav(0T-Cw06XoaM@T%t=N1UV`W4|6^ ze$wN4K1nd+U54EZ!T5WdW4xM6$+>r(S5H~MuSd$7{YW-X&RfKjFf$tdA7&KjG#CvM zsYm_1s$8X=eci*n{P4{560xB%da^rDLHrI>EnMY#L1fTYarqVndiDl$% zMn5~k?|F^6HJc{c4a^@1No(+FFOi*C!JY8Fod;s?>HNmc|2DO~_uwqP=g z`peW|?$SX`UZ*O&Fp_iyp8*uA>>m7P4YzsJnwb57eN=_cj(CMjm7vK0t@jy4YnW4Mm*+vsJHzZb~4xt zmzp zxuJnmo%sa0>h=rSb1ZE;^>Amqc`1`YEhJM%&Z5bi|&?IMSO%hg=(aLRE8k-g{nx9P~DR7P9!* zWLs|U;DOEN!i7^?Y@&;o)^}6q<4;d83$|B4{~?^Lr0dS33RqU6Hf%ERdsepgy6!x@ z_rsb^(x97G;>YD%77Kc*-xs-FPn$CcS@~mEjB28xs)u3H>U8`)i5v1)z%xW#2h%E5 ztn>R_S-H>B(%rvVWE2juyXS*&1!eXAkiENU%7!}=8=rOs-wIh33>#VpxVWW!KE7pm z7_Dpf;k!v#7|qYzK-=YmPW>vm4O|^FrF3asb#9_^e!D*B!dLvElT={gRC_YGV{`8L z$GQvQ3unO@_6`q|5DH6HzL=0NG!X~G2OrMv0m!zh6Tyb&UdG`?i}C{*jR!C0&!KMi zUUI&0TY}S6?)^cJ)27%XdJ*~MlppfgIFk)%`rU{-bF7J2n@y*V_u0^|Z^kLBXJdoR zyf2&?o1JM1ca?d|Wqy4z+Uonk?TDeRS1T8HF)laoq_1AK_?lreRA7}J3{Sml>B*lK zPFgBniiQ9!Q?Ty8i@h18!AfW_T z^p;0mdj0USaNhr?-TT7lhU&A)a(c+?xsc_q203Dhg&SL?8+0MNlVvIvEx0=KI`y7C znKE9amRn?;x&pT1F8juRl&H<3?XCcHr2&2OpG(c9GSeh;%1{bv1&)Ji#3^#kl{vd= z^tp^w)rs-sboVAt?%N4mb;?DP*7QZo)o8BcY^}xyb}oSFeMPYTq+?ts*{6Z>#t1=dU4IHizi>!Zg+eDLp@j zkdA$AoL1?$Vv|Fwk#UD2Jcujv3Q%J`p0vzVOzUd0%qv}< znqPu<=fArUy>O0Z+v5MYyMB(Uu~fPOMwC2eE7(uR(ssGd7uI@nI)-LDH|456yu>8i z96HVJz^T-{Iet{EeQdNinRsS;HmRF4QDWLgneNu*e+CzSyZuTp&i6#5+-c(qFui$P zqWwr5{#c&u?r(b5ix3^>yXr5ual45NvMmhvbHSGZcZ=#E!L;L>56+C+6XtjZs+*x_ z5tm9HlYaz?HdFsNXj#V#1Iy6bb7hs~$MD<6ZyXsPm#fsDNR=mwy710FJ3NsYa?Is; z1b0rwd5%*aCV}0iQT$T;(jewWl0rTtx)~@Swh%?fA@Y#Kz4fwZL-QA z2?tKyS>Ii@vXGSfpZ_q0OdYy)6kCuico!Y+oe#PZJLZ*5`dDx>*ZY=U1XJ;9#??3K zdpR_9=e?o4A?mRew|~AFifeGz|Q>vP#@9Ei5BdqbF#F6waGq|Gx>P~3~6m&ceBC_~7NyAtm7mVOgl zTgHNM15OFjQ*}3DmOmbB8Cz}gAz7Uc2y6`o9mo&dD0?gyCu`_!GbGOY;x-=BP2?@U z5Wp@_DNEgX7*}BAQrGEkXtdcSIvrxJt+-n^{IXJc*Z{XwVAQy#d{WZ1i+J-Kn%j?y zo|E&J$HrN>wF=?2GN(gX9EGbX$k>J4K7m$#vMD~7Yp9VE%Xq$IIBe^RTW}G(#Z~hh z&ol0WMp0jgZxBdPrHSNHe$wn{+u2{>b6hqzkIEPl*}%Pr%gu#Sd(WGGAA^;a{4S@9 ze$*T@rcU+C8ElgON-UT*vp>lhEmP02 z-ud$O<6>FXK>Ifvwim*W)t`St>@^RT1p9`tyM15ZYaW_B@5>O(7Id?UW1L%-n>rZu zvAFZu^sO+&D%~{a+3qE6+9I?2RH-nCiVN}M;j?bOg>Rd}v5%X)!3UR$sQZ@2$*hNW z-Oju%cYPI<@1dt2TP#4sM%!kcKk}DZr>C=MD>9?lTouX!_Bu;{uU13X)JVNH6*^rl z&XZoU7=+8_mn}SsE@v>M)&I0nkRC+bE0_0yXRsERG31=zl9X6>tvScNt$cIIq3P87 zOgP#B8fo**_7Xj%jeVCo)U7i<_q>7&JVKtcVODyO<22z~1bLYpuTrQbr)g}AU>}oZ z%Lly{%uLwMx&k;%pUl5U93b6(eGR@-D?YS3eeV<+)l}%}Vm7=@SV(!zaH00GQHsNo z%!iB!$C|N%E8vB4Vw94^xa-?1pmc!OD~D2Judsf%Vq?uzLiABa^NYIzr6#|=tGc<+ z*kNj1W`hf=T%0bM$Hm6)N`r9_%zoW-9wf)Kj%{unb&Whq5)nG<&q?Vnpjb*~)>}>d zd{W-}T0BtPe*Z#C`3I{rC)PO6uSBb=Va#fmD3i3)P0HW+X1QwN(E(8{b5xh}czfU4 z*OAUzGjXGC@qk4Ur|JRYB1Q+->_}6GV&XMq393LtO~ZsnfCr7CNqNPhb)f1@AUOg*iJ z0iW@*SJpwrlKAgC#Y&zXwGJo$Vo3uLytAS#$4d_u|9Wh!{nDc8rDC?@QPd0xt&%;1 zL!}(ia8Y}qL`1Qv$(=4s4$Hw5c^C|-%8g1?V`oh#DnhcTv|QeBe0I?9RTE1hJG$V& zFWMhZt%$aQcp3Kjl3yZD-MNaO0kxgPEfJV|rF_nf>^Z|n~jY;tuuexsAtlul!;|4O{f zPH@V=AZD6_Ys>8OKi#SkJ>5GkY^7$q$+OI1GM6|`#Gm^In?cV<$cjTYu z4_a5cXh1u3`y@I2p|` z-YtEGrLUL5O(eoH2S_*(Rj+Tiww$;N3HQRGGGF*V(HMAdIwTa^z>?-&{f#+Ys2^Y`h@sPG-Qn1So;|MeEjpZJ1-CVCS;lW`fws-DLUz117K{FHX~ugC#Nn0} zjz_3K?j9JgQYM4{DB%+FDAY*8F`8$XVD$xsHp`IOht2Zd%W&rq?LD|?1!+v5*{6?) z#P{$XJ3d4&i-;r?EbVmM|2%C+(hhk14}=J)34TMcA2G7b8i6XdyB$%Y*8H^X@#nqm z+16#0ta&RcV^kN2!>`GIv(Js2d zQQJ@3Q3GiOs%cO@82l8D5If)5nUJgzpe3F*lBQNqMe}0-GfF7-QA)+Fa@ZPiKzo81 zS#T0Wr8i{gzqbPQL4RC{D5dgz(uK56cC=1({}5wI^fF~^O1)fH&7B^n_WKf_V}Zx> zw;=d^WXS8X$}sICwJ$+bG1GaOp9w#uT3!?7J5nkqi+QUb1RcSeCzNl#e_5FoZy`4b z?c}J};-A@&2%>78eq(p;*t=k!JB)KUIhEqKQPmi+NzTEkMx!Xf<8iI8P-557h3^$Z z%869u_pr}cV!WjH!M^=#i+vf*y*uC$eA4Z&ult+n7iyssQ+^`fln7KYhbi+kT(CRI zVke2A!73j+t54kGhx4C_4u1&BQW5DO45wOw%HhQO;i;Dejd*yZhrRv_xvQj8I(xLd zc*{{k)Mg($8gM&w#}Qp!YtO8RAQ_(5AOMay*TQg`| zRJ}IdoV$DSObm8yd}xbWqWe1SNb4}QP|u3WG-GLG?>b2$%gTH64~UL8pzi+5b`UW{ zIC@q=CxrCA;ARH%_f>dzl_l;DbElocRPcSx+|_!*8ufBE(XFSR;KlVlocX9`vqf9s z_dL;+A`{&i5QQGp2M4gSqRw7K@`e>J4#k~&_h9eE9hLJpCndZh`%MOv0LW@RxO+IS za)M0Hx0i`p(k?fI>vFm#G#?40e8UUbfN+yCVy_U(C>8}py7ffoYzf%+>mfnv$! z?t1StW;Q)$G>H}ILq+H>N-jPE$$pEM5!Ef^9A?`|xC9;WCZfG%k#&7>AT;Q8$P-u9 zEq(uj$D%flZz4z_MSTp&O`;Z@BF|y2Y&eKznHx7w`M46L3}gJDi;je~(j}LD>W&RF zi0)>=;+dt6OJ0U)1>d5q%r00~mh0b@Yr4|V8*A0C3qgjvtBu3Je zQpB2of}HDP?dL2cbtFdo?L-CsTi9A0zMRQYygQTq*Py9^Af9t@Z3n5a6FpO@ z{zr;v(ZM~YR`}!ebhQ7z)E*PSMLLhmdc{&y0(9a3l#{`aRxNSq3)zw%P+GziI}te$aAGYRa@b~P&8?+;6* z+1S54-Kjku&i#T-__O%lKVULKwgq3=HgA)_&Fn41_XLxZ{OcUH?Qy;LNi47>E@$MO zy7IAFGG&Zlz%#1pcjR*~pDb?&rCHngjJY`R?YE3M8JuZ{;!EeAk z9ux+B{1k*MAw9AwabFYuZFVT%fn-iO?YI_u(wlLYtq22EM3a7(u-i<#*MAquY&+a= zcDno1Chn7BT<4|bWYO)6VDn1%VvZyFeRACTR4a_LO8KXPt!4KnQ@RL^W5bcQ)9CpR ziEQkL=$@z6MeRw_27Q=X-rt3lL+XSf_1|(C&P*OWZQdxp| z6n}1KHbWoj7#@qFcRjt@*}QMEX)wbPdiP?L&dvi!QmAn^+PhAFHfemHPIryky`_>q zn)x{3Nq#M*8YfS&q<4V2rPRDS+;y!b@tAIKi8u*cMSQrsdL5(@l;@VD?e14fhvSf= zb}S#>A@pPrXFVO{@;TvbI0$e2v!7AY8yUlVSP}HiB)0`4CbJjgwqj|sVAFDB!vbT8 zM-RiTBHeiQadV2{)we&E>z|`^;wNca3K@^S0hSoozO(b&Lm0Ww7=@L~0qxM04s-8< znR5Bi`(UJ2)u{r-d*QSNx0}_Q0me7?O+5o#C9tCXu(qFcl_IB#NDB#c(fRAsYb{?3A*Ud9UC5^o ztVibRJE+p)UQg?61o;#G} zJlehtO1Wkdc8KNu`wwX3KtVguHGZMTF2kJ|L>rON>3;oQYwL;w6{m4}FemHhvp8LI ztFg7;+uW2Hq;1fjQ>0X#p-=74vVX}G?d4b0SN0Dmgebzy`=``iihkY2Zdf3-?r=kF34nesb>q&%JJ?oeR~LuWt+xPeM#zTb1AW*zg-Mj*2UfbnHLAS^?kC9hD2D{d71}CmpD2r9unx zb-o3H?xs!Mt7cy(qMLpypUA@kmH~HH9Xd(XqgUOaJ;+ylz6^A6`ot~-0qGvgkC2DwVQyr`-Mvo@ls{~Webkd@%-WSoB(R-x*ujo`A+Xjsh|=hYiMwVp=bC2{X}KByl&Cj$v(9H|pYN($%VG_= z3MSf82<5z=D53mH@MEv#Se$6jq!unH^}}%n8PR{p)*`=JlcV~L>pTJG&tN01pzm+O5s^gIJd$2jenrydt)YK zqd(z@m4T|6)@LHVdtke2DLL*`(1HefOwHl(GD8;uR8TlC`jX#w+VIo@ld*Rl6t3}- zc~xk+awy}H=t`f=YG)wP&HlQ4@6<{aCZXLjxFgO{ZeqF^WW8KF$C##wc`c5nD47}T z7yc64sJ*JLB=X<9Cg(fEH z;xo2A`RYo0Cc~+uhXKu(KN6Q;|Gl1#V}C>je?|d)poaJ7{%qm0=$KoXCmx_59D^udzghbJ9C>98Wuah_V>W7C) z41E;W@;Ya`6_f&$5YrL%M^y4G;=n6dgcyhJ@z;p)N6(#>G>{$hNTnQNrd~@^EV>3Z}Q1!K?owaX<^E$KIG``5nN~jF&3L-$aF^WrX z*I?fQF1`Id(%beqble1pWFchQV{t(~h3n+U3(&&+S8`D}@0sXRzlh0zf_ZS5+yleR zdAcpE^_~Oevc*^{!P32~*fa_n?u0ncs=0^;2?Jk_{&?Z%Fq1gvHR)XeeTD3yRt#xl zBDW0A4CNo8=!H!3^hIFDwSO-xi$ac`(^&cWONK^P1M0|H;E+({exZhLpKjfuxfbqe z_{Yhz<-4DC1W)pF9Ew(vzQZn+pMs-Y6v|Gy6<_!?Lkrt(LfuT??TC(`{u<}(xSa60 zT_&_x(sfs@nKe^xU)^E4Uh=J{JF1&ZQ)^GMHd^CYJIyP|>>H5$^ceEiDzI5>Sg7kM zdiIjt&?Gm+#Q8%aW~4R}3nU>Tx^-NL?lBGYF==A#hvPDtS3YeMX{BFZ0VCX{cm3#H zyW%Zl4bss0AJ>EteU5EfStnL(Wt>UX*Jsa*5u+rLgS^=27GCdfs{~WDb-QDmV)+T~ zBq$9W&9!MDm=f^C9wQa@ObsyyjQBa96cJ`76B9#qB1{m3JY9<0ZVTo!S$4Dn)fGpR zdPWgxg2S^!iN233Flap{Lj#F4CtA4dgaiDNYw8sSgf+yGPi>-fp-Zxv818jDh|E;@ zlhFi?;9XMAg7{Q+M#$ym+IiWhdy+dUj+STYL`JW4FT|`BOM1TPmcDsk z-oXkVLSi<>>0~qePAWHQ1w-L0cs-&xJrpj^vl23(i!v$0>3PjbZtew4dz0_F+x$C* zp8`$;|9E|iVK+WsK8sFMtrl4_I675V^bJLY(a%T3m-leHU$nGjYPL*Ww zeBb_+W_x2+ROH~$eBAoWtqHQN`)AX+Q#GQ8%&eY~46whMuAmFFwDPM4AkdNEwXmM~ z_j@@`i_G4EUzySBp0{7wM{V`R=!sLn9vY}Tw`M8oSLGbnvN+m62fGGLUNo-y-ANm#GB=8oVnVi(`PDQ%kB zkHyg&%rg3Np|F>ztq{1zh>E}3*|_LVIc zx$maZB5YLa;!z#z2E+ z1%345MlZ2wMR`xwW@6EWD{in+eGuFECEak7)}!o#qT9WyqpeOZGZ{1y+fyL_aSJ|c zO{cbYBvPjI`H9{apitxzupd9VsTwJ7&D`UD8(lC`j)a*bXjh7`@*U@Ab#jwxxeH=S zIUe7jwo4^n*eCJsPkJVU88g@-v5(&B3>~AljT2p?tEiir$7S65*E4_k@y&4)6;!@r zv%`)2JFLbbT!&B~bz*|MlF6`2$-K5n`u#|Pd$z`fgiitAk^>!baUhl<{XDIw^RJk} ztY-`@brAF|_?lZP&9&h$+NCC0B`N6ztH}#_vrLGtc6R3Bd)w{t>+|<0?mupD%d*3R zSMS-T{sGl^8QQ9p)?L%XgE8DtyLxZn6-_X$GkVk27#2Jb13pKMbt{>c8wbTUlIAJYndy#G4>xFtO8 z6Hrpg*7)^+X=E~8-J!db{Opb{$wk)sQI1*Atx-V4$YL|yc+=v6>lSL;>yV}`AX_rN zz~0T!NGys1YeAVvts}FH=D;WcT%IGILCS1CRBaHVjB8 zQJLeUpu9TSl_`z;tin83l68N`Bw%StmGD1=ZtSELZ=`#0TU*EI*QW-q)HB;s8Vg7-iVY-0IE54PF zJ%rXxlvD>OGu4BX#$we7eL>*stx~p8x3t4RI%ut`7ge;{<>5qq|Ki9gtwWQ(m0)U!J_(VA^L;Pq$3)n&&qv@EJ@mK)@8Uyt2#-8NF@m{4C;S6Sy6 zP-jZ1|4oGcF0iNB-J2@+!XtN7J9Qe(TG9K+3f~N9JL}ZBYNZyI=9U ze?T0h0zpK(RGr^`R0t>f@>MWAJw7}mYg~=d>*+9JIJ!^c{u_nmY^}XF3GtEO-7&eZ zI+oIC3@`ojh10wZBtKwzcX&kB_S!*}!NLIP*SD+cgeTqry_~HrSiBFIx0uw@i#;sA zCg(0YQ5Q619UycRQqG2n<|@N!Yo@4#^r)hC@A;e>^dsU_W{K8oEPt0IABz#)_&mhi z_azzR?$YcX1G5a4B}!zO5}l3hcFt~}FG!IvRKDQO$h5ym!q3C_9xE-So>{U6>&{Nu zK6GyMH$Emb^W*1JE?@eg?s2dtKZRsn5xBca=57QZ zJiX-|xy7qc=%m3R6z2k5hJQd$On{ZM$&#hf?a5c<5`~5WL^Ilqwk|TGug^@mWu-~%gwYfz0*033N8S6jIX#e0bADL-RWL`Nm#R!z0a8zj z|GQ~`-N3ka|LH_=nMta($zrC*=s z#VQ(CU(|kC_qgN8r4>G^j7w;Ddk@spofi)l!}~JiWCQF~hpD_PESO+#3gV zH}$W+tu@*f*Iy%>S*xmz7#34@6ts>Oe4CL%6U1QhwhAyejXWD}-s3XN8w1Mw*{#-z zXu0yyyOz24STml3VNCQU-C}2yKr%#-(TP9?cB$w6lVhEq)Ki%K1!<3pV(p^3&n54h zyL6Sz%7JnMd@%<*reqpN%Z?vIg;$~7zX;m$@H_6}`|iL+*s-@F>I1(y%%%1+NAAPa z)j!TTg(aVj5E{)d1k3iCF?*b$)lCuknYlo%GRw-pvv$&0yNIj5EVicCB)*AN-&J+5 zFtzTH*rPqv42W7!gmNAz^Gd*2S+b)4L<~DB$oHQl9_sIQug*K2OBCz76<#qaOoA6i zA7mgS5IvWU>0h)gu@LB8+I-49FguZeUP4hU?E>J1j15Ye!cq>%{x9 zVggSy@sq3~(7|#Cd~ea@#m&>Y+0cS_l^uw6Ytl|uDO4#8CDV@N{CvaG!c&6^Ox``( z(@(6`$4kQ3QCjAJ#1=#wXHc2Ret6!N@w(vFQS!yd_jEroYX~?9JCfw_gCq&GCvRaZ z_4{-K)s2Qksu}Ee2GPHDirWv;oWuh9Zp%=Alhg8lQtq6#tPeT`bS5>#OF7ipX!gpq z$93E~5{@Wm=)MqZm^Y9gymY_oq*0S<>Zy!{^{CYdE`L0G^sXj9_Ox%doP}ATpudT( zbG1IrwlnGG=T!Yy9PK-k$1(8=_q(y!8^)t~q5Ej8J8284CER5xwUaD4z@0cuF9(hW19pkZ3Qc+ z1oI?JCR7;Q%H{j!m`>HuyfU^&@O+u@ z20hiiH+-yxQAM;l5)KH$@J|A(*u}Z64&5ta6(z?C{ zys+NCbxhtOtf@Mni8YJZGPDgvdL9hOLuPiIW!pD21H}_-GX@g`(N`ZE9wL!8^weN? zqPOOgmC)lJhznuIV6Ic!&f zMnQ6F^<``tOR}Ft;{YUGr35kuwNKM5@<4;A;a;}hO^JiwcpKqc zB3qQJIe!h$rP8!~J`H??LPWGT$r&*#U3EY_ydiz!q5kGN0YmClvK!E^&2JX&J@e1v zC-1$IHUq}}BBztS%k-NCAOleHb@Y}}AG?Ek$Q1k}cBKui*gr3(H39iS9uu_Jm)~@z zBKz%_hYj4HW{!x^-iors9KUn*6YVKwosa6}bPN0So=z=bn!dh$t%#9QqQP@(S2Xvp3^x46jRy;JE|E>hNnXEIRdePh`F{JOOOj`r?7uLu!S4{F^ z^Z!WodP)`Z6O4goS0^=gv&cD*Fwg-)EMT8EOS5bJ^R1U)T+31j`+1?KHggv>*E@BO z>q#O{b^qa0^-_WUK>Oz$6>Qm)2_*iLEGD!yxE_0|Ttple~AAE|w2z0JGeHzEs)@=Sw ze-iX^@Cn2#vsc}JNK^k;y4GL<(tpyzg2<@MOklCQT0@Wh*>)Yhm%vPF{N$8sH)`0v z(1HBb5Cdx|y$32xR3LN8eOrut+(K;XLj%2+U@C!#C>dXc+8W*k_Q~!2sSOuE2+>ldL52l~^DMRh{Z3m{Auk z{CC`Q)IBMt?Yw}ik7iH2{ybCkJZnOx-yyJMttek`?7ReMt8g>^RNxEqFHu#nr_7#K z=^l8xvi9%zb^6&$<%(3z!plJg?dvH+Tdt*VltiTr?shQ(Kjbv!lB<@I8&GQYxTRV& zkzvv9^;lV0X(*~kPf0AqCYS9+b{)kbIw3JOD<8(WDEkf^Slk09{RC1+vqL*WjvRDT zXm+zia=h8x>I_DWkz(U1*#XnO9x@3_1+!)C8}bvb>i*=wOU$&Atp3eFv(w;@&0b0~ zX5#MPQj6fNn`T8R_?rpbOgl&aYj0@n;ewZz7@o-9?}H2HiN|2T%+PCeC4w+NVW z5lBB9KX)s5QyAyN+AKXZ{kaC(T@%etKO}@e$vTWv)Muxl*h5Scks*2-yGC|-xt-!V zg?@!HMWmEbDb}6>kwAEVNnTO$V`gCHJ1BHM99@F!KW6(Iy|7S*4U+z_q#!QopczMuE<#*HiN(9A< zl}??7CBjv_6l-|ujP$lC^+)72K3v}jo*7douXDLE#kSb0u77P0A7BF!%k*IsuF*qK zyBrXXNoe(p>VZ9+{f)*N6ls0jVECdU$%}6n5-q%Y+H+U$0p~)+xL7~dFx@NM2@%@L zi$_`CjyCuW+$PH8id!sgIgwOqTxI;11$jT&r0VdSR)-PDodjP6cf=_rC?mf@oXl|WTE;t1pJKx{rXldz13-SmHivR6Y?5O zrhH8*V}-;ol@uE~Xo_IPk+*S1CF3Tqjr+%pKUYOtvKy>1GrR}}TOz@DAdK|%dKO39 zC!x#@vCF;TP1%;B9!S{@8RjU<0k@D)X^@_~^oYil!YZX_40KDj85+yJ?9O2Fav=8W zu&b`0hzPwq|A^=@Idjq~Ru<-uKI=N-cMwePoTtF51xqk`m9TIiLZXhDLj?dQdH*Aq0cZ--`nS@u;T#K*1dzFZu_T?qLP7)Vsv2!tmhruM0oAM(C29IXjLJ3sgo5CtS zOf_x@2}|{h0=L!K=^zRXyg(uFH4p-~wRbg*nz`h!r3-I=DExzyyEmc?XiDdfvy+ZX zuUBX-ufw*H;mC$5gAuV6-y^r2|MEnv&yB1yX8Mvv{>=A6h^IM(vB;bU){b;Tu;*_o zq-`RiXxg+)upps);BD!hZRsARD#E1qsL#{wn8u%^t&*+gbkPv7x9h|ry<3dmzk!b^ zJpYzg_qbQhT>}}5WY)gjd~F6i8UwAv0b%i}GZC*6c`q9CdFO+lh1Alt)^QfcJ`Ek} z17&z<(mOWhGaGXnF31tDfDAus0!~dTJ_y_V`Nl;i!?f0JyTy{yv(5vR5LsZJ&#}=9 z7$I(5JRG~k#xf#86HyFaL=?k@VAhRyAasx@I%E8h7E3Nlp)L`Y;k*?%R3i&dO$Y~> z2Y^5q`Jwxiua}>{Yg2Z`rH7#TvBK9<@y{S)_|9(XbMO+~SSTR{7~v~%;_4O*?j2Fl zu?qFY-bo$rC4T-z3#dJE^&J)ihozqXQJ8E5?HDQ}gTZ|a9zY>1{AB4$NJ{_u8VZw3}ccMB1mVyzTmW6}jrB-FO!;ewF|Oq#CKTGHpH1pA8=IAe}Zf zI;7a=+&#kj&@rrR%b-{aqJq>AcrX@}hbUikFWvti(DO(y?p-~yNSg$KaeMY$-r3wC zVSy)Vtx^Iu$tZsLU<6<_(Ar^r3H~fd{Y_%hIlC3skl>~8a4Eqp=id*Z9j8$H5)a%28QXJ$vvUl*;%M7|I*`w{VDGpgtEp>fs?ijG`q#?dx*{THo8tn+rZzRL5fpr~91h zg)Y8Xq5EB{cv|ZtN&>yJ>MBfYimADqX-ZemSef`Y;FMGTDo|3^ zeH;qdW;`01wT+&$vq@C=VHIz*iKUgvfh7FZ%d`(7r%w%;(N+zE)f|=9&8Y5jETn|t zhR9Ynki|rDFks`i7IB;qLls%SqV942*i7VCR3lgE7CU?%LCPRiywBZO6#8*{f$(pK zwo9dbg;9yHFk1q$;}VJUZYj2gw!9#b39#h3=!8BWj0WAI{muOT@!=!J13mgD5p=waOuY2?TNq)eIGx77(D-~N zy;{V4JI&+#q<3$Z+1)ePdIn*PtkjVz9++Rof6a>@tCWhjD|-hYnlQ3~FWEt-sc4G* zp)wQ&<~U0t`<7yxoF{aAoK8^9P(lx+yMZOp#LXkR3a?AxQkc#<)v#=nqmfYWrcBqjZkIj8b{NML{3I}5LqQ|D+YppRF`7YOF`ZnX9Y_` zjT)O#vQB&whMVIsuNx?N(Y`tiqyFQZ(4BBn%-U}ZqWuuE?TYPYI(v-uT5_vBal$ur zW!41tI1y*R*Ovb+MtJs4h(RhS#f5fVam zfb7sL3W;4-9;Xa9cc8zQj?u<{t|5@(fk`JJJS(w2;BS#MA_poX)#wq^13rg zOF)C)$&bv36g%eEVQelw2^N^LcWSES&uV3D1bgXPV^!4n4xwD{y$3inFaD#RX=HG& z6#~;n22Zh!Y8B@M?neK1L)sh*4Eny}v6qUT;IOA2YWUXaEFXEWswj;Tr2d%Vfkq0# zHNm8Z$Evf|fC)4FJX``XPyt_GD1U&FwTGl-nrAe?u$fYcJBhK}M`!drmW*Wh6mjS2 z<4BmYD1;O+_%?iOv(U#siMZ(PvQ)+XE{hvNHzqOQ=^zmbg+1y6h4SHc^XP4!>L`C{ zNjFRk`0Ad3l;gwCm!Y74qm-c;KRQ9F>}r6s^xiTiI38|^aU3=ovfy#`J(opxog3B% zmZq(1ZAJfOky00iPzT!oY6w{!+CZKlGS@F?6etgGsB(iT))Rt7hp}bk$gnp6t|rCX zzv=uL<+V!jbBt(};Uwg9I=0V2koAPLP9#)>5sn^$sT-VVzQbPvr~DF~sEs{3uPN4(2m?x{j3YWOn>P8BMAJ+fJBCI5)FrS`b#1s_vF`W>;8y% z90uPGV*Xe{MT|AZjs}7@a4U0FxCEgqZqIw{75iK=M#gf+)GdLbUs}8~*ivcF zC8vkMyEq~eP$CUiS6_*SG4rPa7u2$;aK5cjK2iF0G`C@25s|iSd1wh(6m$e7bekxr z=`rg-sZ5~hds*N}7yL@~rs=zs2)M}KOhy^$foF3B7RFGVXq(YWDih#I8(_7U4-TZv z2hO1v0(wK!oCa!-NtuZWEcyuopk02|yi9Bj0%p<@^pHG2zu1Hrytn6v0aRup+TlD} zVX#c5A@1F{2Q>=*Tc4Ra0cZkppjEPaDxR}lKA=8)bSyqoC4P_+s4qN)+EtJ^=dwJg z*3k9@n4&6!z&IE;KS4>Iv_Ch3+b(|-KKAC5j6ibk5YJNqKq)_wex#_%Olkm(=HZ$- z4a#J~E)yGFi2xYMmtce-aBBgZPk5avMY)?PD7Fd%BmM{NV_5}5IotjFB@br_frlR7 z41OvA=t(7z$rwsy3gsaYu+V((CoOif2UFipnJ+cBX0U_fJFX9$fr=9J3^Vg(Ibagdk^h5 z2K5dWs7LNDL#|b^tPlQX#7SW)`sEL`^Ui) zF0W?~s)`z-_sTd`a!Qsjo^+3UY-KAbIOG-Spzfm-X>qEqUQ=+Pg6 zSVP^qjGau`%g}nrn!~F_5N zJviS#tgI)502Urbyi!G5o;n6HFTWUnx{q_L`|vMf5Ud3is%xce168a{PB ze~0=k3{i!5`>)`GmqF&GO^|rLNyOD?78H^t>is&%Kd~80e!3A;zfA zm%3>mipffw*B@uMVRk-!Ua7$Ut#|OZ%qeK#SwgT7!X4@u$5O3mxmo8YTVy*I2I41g zYsIEqam{pn3D9pDQf{R;X;pofPKlCsCrt;7VAF1)pO7wgqJc#uq{Znjlf}Z-MCy(Q;>@I~ULGZ5H zI932Lk@(|O%hXt3=flr%LPgO-Ag*18{7T3X+{uT?#SfbiT}K}l*^3n!G4faGqkeYA zU7nhbgF$#Dq0VGG3Qz5l2XT>p)<6SfoL~A8%18;Ih0GlSaQ&a8vjG6-5*DZi)#v4J zCI*uu05(#}ydmZX;J-{!V&@?b2Kc8)HBl;~>ZB!LWt0=Q9k_LTGesVa1_eNj5&)sH z$`H3*1>ygL=ELb+^s0ZY&Ohk?B9P@1=);7)st`(!JX(o8xH|b^O{vzh3AF=dBTx@R zdLX${Vgn6vF|*i#C$%)?gN@kPGSLNW{$n=;^F!^jgYm~u|D97A1s1OVfma@2&VxyV z1(GvS?C=M=%h*I@;sb4*23nXrJ%C@*>frHl;_B*!@i3*Hi3slEI$lDA86Ot zAvz6}7+eu!Y=klQ$|%Ii`B8GIcx}5ekKU3(Ka9zh$gHDWF0=rZOjU(s&RG^0ESU54 z1~wjAs_dSq-}f(mGwI$`yd6d7o~7QvhyBd~C^x9q#kI4!n*)m-8@N7PR2YBj^KtZ6 z%lLlR212nfuHZ(s+Jj; zT`2`UHk5(P=yS0o3B)IvEv|p|Mu#AZ+txOF-s))?;hbpwoU&DlUV3db$5BY-o9h}O z`1uvg`6;Ii9+(~pcsOb`b4$MH+LLCfz8DvI zjyc6uy5bo5l}zJXW+J#MlED>bnv1|DtJdhfl`q;WwalMg{_vwFfNeeEk?Uh-comtq zt_Y_hE8-=3u)27?p!1{2mq5g4Lh*ChiV=cf)IGL!SV455_ByN^GVVb2P3py z3IFoQCZFH4f24lX)sH@#D0QBrSWkZxn&ZAgN%OI{4IF8DEde`W_WzFAlOXQ?2ZqMu z6w6l{mil;s_JhBkGgEcoW3N>XoU?i_F(=)cM-o(8mC0kuBaGHqGbZd(LNaq6t!u0l zdO~`08Ifz#@7_3fQ|j4T{LrdS+{VMyWmA;zHn5iNu<~L$@*j!u+_xClTB413G~t5J z(?{B68TrP-&=Q1?kOn0Lzf70OPZyay@WN%DK5#zgU>|mV+chAI{Eb)UZ4MG)0*6&w z>70a=EHVB_=$cC`@LT1WEhYE8$Zkt)iJ^ENp;-fZ6zT&`_r;jO^>MrSl$<{oZRH(U zm_=&K8V&)EfLyW3go+9Ypf|>OHMP}5wZ!ub1~YirASBd3U7cwP>+N*>$8jn(`}|P* znrt~dw%j3&N5Fh!zWKjA%n!5h|7HS^tp}FZe}Dg7vH{l};Qyq|1H|$JAeJ^?9|6-& z|FB|$?S>cxH2#&K{|VCn-{D_b^J1~f%><2W{%_#~>>g5(O(HTHFo7_+03K1B#9-pF zf2j_&G{68OA+Pi!!fB+(TAS!l#8G%+gc`U>z4`4C=k9Rmb+Wdjm znD2pK8mNf=k3suCfd%AES#^ku9+0+$@lgg3Wx&3~1cYpkU#S(?p%5*sk`_Qcdg3Aw zez(gxh7y%V)bON^d*$XM@Kk|O4~adf5BS8t6p0TsiCW_T;r9Q1Xa73_name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).localize; + } + + void ILocalizeEntry::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ILocalizeEntry::load_depending(IZone* zone) + { + } + + std::string ILocalizeEntry::name() + { + return this->name_; + } + + std::int32_t ILocalizeEntry::type() + { + return localize; + } + + void ILocalizeEntry::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->localizedString = buf->write_str(data->localizedString); + dest->name = buf->write_str(data->name); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ILocalizeEntry::dump(LocalizeEntry* asset) + { + } +} diff --git a/src/CODO/Assets/LocalizeEntry.hpp b/src/CODO/Assets/LocalizeEntry.hpp new file mode 100644 index 0000000..7e75437 --- /dev/null +++ b/src/CODO/Assets/LocalizeEntry.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool::CODO +{ + class ILocalizeEntry : public IAsset + { + private: + std::string name_; + LocalizeEntry* asset_; + + public: + ILocalizeEntry(); + ~ILocalizeEntry(); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(LocalizeEntry* asset); + }; +} diff --git a/src/CODO/Assets/StringTable.cpp b/src/CODO/Assets/StringTable.cpp new file mode 100644 index 0000000..0dfb3d6 --- /dev/null +++ b/src/CODO/Assets/StringTable.cpp @@ -0,0 +1,229 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool::CODO +{ + // LEGACY ZONETOOL CODE, FIX ME! + class CSV + { + protected: + std::string _name; + std::vector> _data; + + public: + CSV(std::string name, char sep = ',') + : _name(name) + { + auto fp = FileSystem::FileOpen(name, "r"s); + + if (fp) + { + long len = FileSystem::FileSize(fp); + auto buf = std::make_unique(len + 1); + memset(buf.get(), 0, len + 1); + fread(buf.get(), len, 1, fp); + fclose(fp); + + std::vector rows = split(buf.get(), '\n'); + + for (auto& row : rows) + { + // Replace literal characters + std::size_t pos; + while ((pos = row.find("\\n")) != std::string::npos) + { + row.replace(pos, 2, "\n"); + } + + while ((pos = row.find("\\t")) != std::string::npos) + { + row.replace(pos, 2, "\t"); + } + + _data.push_back(split(row, sep)); + } + } + } + + std::string entry(std::size_t row, std::size_t column) + { + return _data[row][column]; + } + + std::size_t rows() + { + return _data.size(); + } + + std::size_t columns(std::size_t row) + { + return _data[row].size(); + } + + std::size_t max_columns() + { + std::size_t _max = 0; + + for (std::size_t row = 0; row < this->rows(); row++) + { + if (_max < this->columns(row)) + _max = this->columns(row); + } + + return _max; + } + + void clear() + { + for (std::size_t i = 0; i < _data.size(); i++) + { + for (std::size_t j = 0; j < _data[i].size(); j++) + _data[i][j].clear(); + + _data[i].clear(); + } + + _data.clear(); + } + }; + + int StringTable_Hash(const char* string) + { + int hash = 0; + char* data = _strdup(string); + + while (*data != 0) + { + hash = tolower(*data) + (31 * hash); + data++; + } + + return hash; + } + + StringTable* StringTable_Parse(std::string name, ZoneMemory* mem) + { + auto table = std::make_unique(name); + auto stringtable = mem->Alloc(); + + stringtable->name = _strdup(name.c_str()); + stringtable->rows = table->rows(); + stringtable->columns = table->max_columns(); + stringtable->strings = mem->Alloc(stringtable->rows * stringtable->columns); + + for (int row = 0; row < table->rows(); row++) + { + for (int col = 0; col < table->columns(row); col++) + { + int entry = (row * stringtable->columns) + col; + stringtable->strings[entry].string = strdup(table->entry(row, col).c_str()); + stringtable->strings[entry].hash = StringTable_Hash(stringtable->strings[entry].string); + } + } + + return stringtable; + } + + IStringTable::IStringTable() + { + } + + IStringTable::~IStringTable() + { + } + + void IStringTable::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).stringtable; + + if (FileSystem::FileExists(name)) + { + ZONETOOL_INFO("Parsing stringtable %s...", name.data()); + this->asset_ = StringTable_Parse(name, mem); + } + } + + void IStringTable::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IStringTable::load_depending(IZone* zone) + { + } + + std::string IStringTable::name() + { + return this->name_; + } + + std::int32_t IStringTable::type() + { + return stringtable; + } + + void IStringTable::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + // TODO + + StringTable; + + dest->name = buf->write_str(this->name()); + + if (data->strings) + { + buf->align(3); + auto destStrings = buf->write(data->strings, data->columns * data->rows); + + if (data->columns * data->rows > 0) + { + for (int i = 0; i < data->columns * data->rows; i++) + { + destStrings[i].string = buf->write_str(data->strings[i].string); + } + } + + ZoneBuffer::clear_pointer(&dest->strings); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IStringTable::dump(StringTable* asset) + { + std::string path = asset->name; + + auto file = FileSystem::FileOpen(path, "w"s); + + for (int row = 0; row < asset->rows; row++) + { + for (int column = 0; column < asset->columns; column++) + { + fprintf( + file, + "%s%s", + (asset->strings[(row * asset->columns) + column].string) + ? asset->strings[(row * asset->columns) + column].string + : "", + (column == asset->columns - 1) ? "\n" : "," + ); + } + } + + FileSystem::FileClose(file); + } +} diff --git a/src/CODO/Assets/StringTable.hpp b/src/CODO/Assets/StringTable.hpp new file mode 100644 index 0000000..5ed49af --- /dev/null +++ b/src/CODO/Assets/StringTable.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool::CODO +{ + class IStringTable : public IAsset + { + private: + std::string name_; + StringTable* asset_; + + public: + IStringTable(); + ~IStringTable(); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(StringTable* asset); + }; +} diff --git a/src/CODO/CODO.cpp b/src/CODO/CODO.cpp new file mode 100644 index 0000000..b23222f --- /dev/null +++ b/src/CODO/CODO.cpp @@ -0,0 +1,140 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include + +namespace ZoneTool::CODO +{ + const char* Linker::version() + { + return "CODO"; + } + + bool Linker::is_used() + { + return false; + } + + const char* Linker::GetAssetName(XAssetType type, XAssetHeader header) + { + // todo + if (type == image) + { + return header.gfximage->name; + } + if (type == menu) + { + // + } + else + { + return header.rawfile->name; + } + + return ""; + } + + void Linker::startup() + { + if (this->is_used()) + { + // todo + } + } + + std::shared_ptr Linker::alloc_zone(const std::string& zone) + { + // allocate zone + auto ptr = std::make_shared(zone, this); + return ptr; + } + + std::shared_ptr Linker::alloc_buffer() + { + auto ptr = std::make_shared(); + ptr->init_streams(8); + return ptr; + } + + void Linker::load_zone(const std::string& name) + { + ZONETOOL_INFO("Loading zone \"%s\"...", name.data()); + + XZoneInfo zone = {name.data(), 20, 0}; + // DB_LoadXAssets(&zone, 1, 0); + + ZONETOOL_INFO("Zone \"%s\" loaded.", name.data()); + } + + void Linker::unload_zones() + { + } + + bool Linker::is_valid_asset_type(const std::string& type) + { + return this->type_to_int(type) >= 0; + } + + std::int32_t Linker::type_to_int(std::string type) + { + auto xassettypes = reinterpret_cast(0x00799278); + + for (std::int32_t i = 0; i < max; i++) + { + if (xassettypes[i] == type) + return i; + } + + return -1; + } + + std::string Linker::type_to_string(std::int32_t type) + { + auto xassettypes = reinterpret_cast(0x00799278); + return xassettypes[type]; + } + + bool Linker::supports_building() + { + return false; + } + + bool Linker::supports_version(const zone_target_version version) + { + return false; + } + + void Linker::dump_zone(const std::string& name) + { + //is_dumping_complete = false; + //is_dumping = true; + //currentDumpingZone = name; + load_zone(name); + + //while (!is_dumping_complete) + //{ + // Sleep(1); + //} + } + + void Linker::verify_zone(const std::string& name) + { + //isVerifying = true; + //currentDumpingZone = name; + load_zone(name); + } + + Linker::Linker() + { + } + + Linker::~Linker() + { + } +} diff --git a/src/CODO/CODO.hpp b/src/CODO/CODO.hpp new file mode 100644 index 0000000..26af6a2 --- /dev/null +++ b/src/CODO/CODO.hpp @@ -0,0 +1,45 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include +#include "Functions.hpp" +#include "Structs.hpp" +#include "../IW5/Structs.hpp" +#include "Zone.hpp" + +namespace ZoneTool::CODO +{ + using GfxImageFileHeader = IW5::GfxImageFileHeader; + + class Linker : public ILinker + { + public: + Linker(); + ~Linker(); + + const char* version() override; + bool is_used() override; + void startup() override; + std::shared_ptr alloc_zone(const std::string& zone) override; + std::shared_ptr alloc_buffer() override; + void load_zone(const std::string& name) override; + void unload_zones() override; + bool is_valid_asset_type(const std::string& type) override; + std::int32_t type_to_int(std::string type) override; + std::string type_to_string(std::int32_t type) override; + bool supports_building() override; + bool supports_version(const zone_target_version version) override; + + void dump_zone(const std::string& name) override; + void verify_zone(const std::string& name) override; + + static const char* GetAssetName(XAssetType type, XAssetHeader header); + }; +} diff --git a/src/CODO/Functions.hpp b/src/CODO/Functions.hpp new file mode 100644 index 0000000..56e190e --- /dev/null +++ b/src/CODO/Functions.hpp @@ -0,0 +1,32 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool::CODO +{ + union XAssetHeader; + + // TODO: fix offsets! + static Function DB_FindXAssetHeader = 0x407930; + static Function DB_LoadXAssets = 0x4E5930; + + typedef int (__cdecl * DB_GetXAssetSizeHandler_t)(); + static DB_GetXAssetSizeHandler_t* DB_GetXAssetSizeHandlers = (DB_GetXAssetSizeHandler_t*)0x799488; + + static const char* SL_ConvertToString(std::uint16_t index) + { + return Memory::func(0x004EC1D0)(index); + } + + static short SL_AllocString(const std::string& string) + { + return Memory::func(0x00436B40)( + string.data(), 1, string.size() + 1); + } +} diff --git a/src/CODO/Structs.hpp b/src/CODO/Structs.hpp new file mode 100644 index 0000000..30302d7 --- /dev/null +++ b/src/CODO/Structs.hpp @@ -0,0 +1,3444 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool::CODO +{ + enum FxElemType : char + { + FX_ELEM_TYPE_SPRITE_BILLBOARD = 0x0, + FX_ELEM_TYPE_SPRITE_ORIENTED = 0x1, + FX_ELEM_TYPE_TAIL = 0x2, + FX_ELEM_TYPE_TRAIL = 0x3, + FX_ELEM_TYPE_CLOUD = 0x4, + FX_ELEM_TYPE_SPARKCLOUD = 0x5, + FX_ELEM_TYPE_SPARKFOUNTAIN = 0x6, + FX_ELEM_TYPE_MODEL = 0x7, + FX_ELEM_TYPE_OMNI_LIGHT = 0x8, + FX_ELEM_TYPE_SPOT_LIGHT = 0x9, + FX_ELEM_TYPE_SOUND = 0xA, + FX_ELEM_TYPE_DECAL = 0xB, + FX_ELEM_TYPE_RUNNER = 0xC, + FX_ELEM_TYPE_COUNT = 0xD, + FX_ELEM_TYPE_LAST_SPRITE = 0x3, + FX_ELEM_TYPE_LAST_DRAWN = 0x9, + }; + + enum XAssetType : std::int32_t + { + physpreset, + phys_collmap, + xanim, + xmodelsurfs, + xmodel, + material, + pixelshader, + vertexshader, + vertexdecl, + techset, + image, + sound, + sndcurve, + loaded_sound, + col_map_sp, + col_map_mp, + com_map, + game_map_sp, + game_map_mp, + map_ents, + fx_map, + gfx_map, + lightdef, + ui_map, + // not used + font, + menufile, + menu, + localize, + weapon, + snddriverglobals, + // not used + fx, + impactfx, + aitype, + // not used + mptype, + // not used + character, + // not used + xmodelalias, + // not used + rawfile, + stringtable, + leaderboarddef, + structureddatadef, + tracer, + vehicle, + addon_map_ents, + max, + }; + + typedef float vec4_t[4]; + typedef float vec3_t[3]; + typedef float vec2_t[2]; + + template + struct VecInternal + { + float data[N]; + }; + + struct RawFile + { + const char* name; + int compressedLen; + int len; + const char* buffer; + }; + +#pragma pack(push, 4) + struct PhysPreset + { + const char* name; + int type; + float mass; + float bounce; + float friction; + float bulletForceScale; + float explosiveForceScale; + const char* sndAliasPrefix; + float piecesSpreadFraction; + float piecesUpwardVelocity; + bool tempDefaultToCylinder; + }; +#pragma pack(pop) + + + struct XModelAngle + { + short x; + short y; + short z; + short base; + }; + + struct XModelTagPos + { + float x; + float y; + float z; + }; + + struct DObjAnimMat + { + float quat[4]; + float trans[3]; + float transWeight; + }; + +#pragma pack(push, 4) + struct MaterialStreamRouting + { + char source; + char dest; + }; + + struct VertexDecl + { + const char* name; + char streamCount; + bool hasOptionalSource; + char pad[2]; + MaterialStreamRouting streams[13]; + void* declarations[16]; + }; +#pragma pack(pop) + + struct PixelShader + { + const char* name; + void* shader; + DWORD* bytecode; + short codeLen; + }; + + struct VertexShader + { + const char* name; + void* shader; + DWORD* bytecode; + short codeLen; + }; + + struct MaterialArgumentCodeConst + { + unsigned __int16 index; + char firstRow; + char rowCount; + }; + + union MaterialArgumentDef + { + float* literalConst; + MaterialArgumentCodeConst codeConst; + unsigned int codeSampler; + unsigned int nameHash; + }; + + struct ShaderArgumentDef + { + short type; + short dest; + MaterialArgumentDef u; + }; +#pragma pack(push, 4) + + struct MaterialPass + { + VertexDecl* vertexDecl; + VertexShader* vertexShader; + PixelShader* pixelShader; + char perPrimArgCount; + char perObjArgCount; + char stableArgCount; + char customSamplerFlags; + ShaderArgumentDef* argumentDef; + }; +#pragma pack(pop) + struct MaterialTechniqueHeader + { + char* name; + short unk; + short numPasses; + }; + + struct MaterialTechnique + { + MaterialTechniqueHeader hdr; + MaterialPass pass[1]; + }; + + struct MaterialTechniqueSet + { + const char* name; + int pad; + MaterialTechniqueSet* remappedTechniques; + MaterialTechnique* techniques[48]; + }; + + struct MaterialConstantDef + { + int nameHash; + char name[12]; + float literal[4]; + }; + + struct GfxStateBits + { + unsigned int loadBits[2]; + }; + + struct GfxImageLoadDef // actually a IDirect3DTexture* but this is easier + { + char mipLevels; + char flags; + short dimensions[3]; + int format; // usually the compression Magic + int dataSize; // set to zero to load from IWD + char* texture; // texture + }; + + struct GfxImage + { + GfxImageLoadDef* texture; + char mapType; // 5 is cube, 4 is 3d, 3 is 2d + char semantic; + char category; + char flags; + int cardMemory; + int dataLen1; + int dataLen2; + short height; + short width; + short depth; + bool loaded; + char pad; + char* name; + }; + + struct MaterialImage + { + unsigned int typeHash; // asset hash of type + char firstCharacter; // first character of image name + char secondLastCharacter; // second-last character of image name (maybe only in CoD4?!) + char sampleState; + char semantic; + GfxImage* image; // Image* actually + }; + + struct Material + { + const char* name; // 0 + char gameFlags; + char sortKey; + unsigned char animationX; // 6 // amount of animation frames in X + unsigned char animationY; // 7 // amount of animation frames in Y + unsigned int subRendererIndex; // 0x00 //+8 + unsigned int rendererIndex; // 12 // only for 3D models + int unknown; + unsigned int surfaceTypeBits; //+20 + char stateBitsEntry[48]; // 32 // 0xFF + char numMaps; + char constantCount; + char stateBitsCount; + char stateFlags; // 0x03 + unsigned short cameraRegion; // 0x04 + MaterialTechniqueSet* techniqueSet; // '2d' techset + MaterialImage* maps; // map references + MaterialConstantDef* constantTable; + GfxStateBits* stateMap; // might be NULL, need to test + }; + + //vec3_t should be from idTech3, has to do with camera angles + typedef float vec_t; + typedef vec_t vec3_t[3]; + + struct Bounds + { + vec3_t midPoint; + vec3_t halfSize; + + void compute() + { + compute(midPoint, halfSize); + } + + void compute(vec3_t mins, vec3_t maxs) + { + for (int i = 0; i < 3; ++i) + { + this->halfSize[i] = (maxs[i] - mins[i]) / 2; + this->midPoint[i] = this->halfSize[i] + mins[i]; + } + } + }; + + struct XBoneInfo + { + union + { + Bounds packedBounds; + float bounds[2][3]; + }; + + float radiusSquared; + }; + + struct Face + { + unsigned short v1; + unsigned short v2; + unsigned short v3; + }; + + // XModel + struct XSurfaceVertexInfo + { + __int16 vertCount[4]; + unsigned __int16* vertsBlend; + + /* + Count... Ok, here we go... + + (((vertCount[2] << 2) + vertCount[2]) + + ((vertCount[3] << 3) - vertCount[3]) + + ((vertCount[1] << 1) + vertCount[1]) + + vertCount[0]) << 1*/ + }; + + union GfxColor + { + unsigned int packed; + char array[4]; + }; + + union PackedTexCoords + { + unsigned int packed; + }; + + union PackedUnitVec + { + unsigned int packed; + }; + + struct GfxPackedVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + PackedTexCoords texCoord; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct XSurfaceCollisionAabb + { + unsigned __int16 mins[3]; + unsigned __int16 maxs[3]; + }; + + struct XSurfaceCollisionNode + { + XSurfaceCollisionAabb aabb; + unsigned __int16 childBeginIndex; + unsigned __int16 childCount; + }; + + struct XSurfaceCollisionLeaf + { + unsigned __int16 triangleBeginIndex; + }; + + struct XSurfaceCollisionTree + { + float trans[3]; + float scale[3]; + unsigned int nodeCount; + XSurfaceCollisionNode* nodes; + unsigned int leafCount; + XSurfaceCollisionLeaf* leafs; + }; + + struct XRigidVertList + { + unsigned __int16 boneOffset; + unsigned __int16 vertCount; + unsigned __int16 triOffset; + unsigned __int16 triCount; + XSurfaceCollisionTree* collisionTree; + }; + + struct XSurface + { + char tileMode; + bool deformed; + unsigned short vertCount; + unsigned short triCount; + unsigned char streamHandle; + char zoneHandle; + unsigned __int16 baseTriIndex; + unsigned __int16 baseVertIndex; + Face* triIndices; + XSurfaceVertexInfo vertexInfo; + GfxPackedVertex* verticies; + int vertListCount; + XRigidVertList* rigidVertLists; + int partBits[6]; + }; + + struct ModelSurface + { + char* name; + XSurface* xSurficies; + short xSurficiesCount; + //This is a copy of the count, the actual count is loaded from the parent XModelStreamInfo + short _pad; + int partBits[6]; // partbits etc + }; + + struct XSurfaceLod + { + float dist; + short numSurfacesInLod; + short surfIndex; + ModelSurface* surfaces; + char pad3[32]; + }; + + struct XModelCollTri_s + { + float plane[4]; + float svec[4]; + float tvec[4]; + }; + + struct XModelCollSurf_s + { + XModelCollTri_s* tris; + int numCollTris; + Bounds bounds; + int boneIdx; + int contents; + int surfFlags; + }; + + struct PhysCollmap; + + struct XModel + { + char* name; + char numBones; + char numRootBones; + unsigned char numSurfaces; + char lodRampType; + float scale; + unsigned int noScalePartBits[6]; + short* boneNames; + unsigned char* parentList; + XModelAngle* tagAngles; + XModelTagPos* tagPositions; + char* partClassification; + DObjAnimMat* animMatrix; + Material** materials; + XSurfaceLod lods[4]; + char maxLoadedLod; + char numLods; + char collLod; + char flags; + XModelCollSurf_s* colSurf; + int numColSurfs; + int contents; + XBoneInfo* boneInfo; + float radius; + Bounds bounds; + int memUsage; + bool bad; + char pad[3]; + PhysPreset* physPreset; + PhysCollmap* physCollmap; + }; // total size 304 + + enum weapFireType_t : int + { + WEAPON_FIRETYPE_FULLAUTO = 0x0, + WEAPON_FIRETYPE_SINGLESHOT = 0x1, + WEAPON_FIRETYPE_BURSTFIRE2 = 0x2, + WEAPON_FIRETYPE_BURSTFIRE3 = 0x3, + WEAPON_FIRETYPE_BURSTFIRE4 = 0x4, + WEAPON_FIRETYPE_DOUBLE_BARREL = 0x5, + WEAPON_FIRETYPE_MAX + }; + + enum weapInventoryType_t : int + { + WEAPINVENTORY_PRIMARY = 0, + WEAPINVENTORY_OFFHAND = 1, + WEAPINVENTORY_ITEM = 2, + WEAPINVENTORY_ALTMODE = 3, + WEAPINVENTORY_EXCLUSIVE = 4, + WEAPINVENTORY_SCAVENGER = 5, + WEAPINVENTORY_MAX + }; + + enum PenetrateType + { + PENETRATE_TYPE_NONE = 0x0, + PENETRATE_TYPE_SMALL = 0x1, + PENETRATE_TYPE_MEDIUM = 0x2, + PENETRATE_TYPE_LARGE = 0x3, + PENETRATE_TYPE_COUNT = 0x4 + }; + + enum activeReticleType_t : int + { + VEH_ACTIVE_RETICLE_NONE = 0, + VEH_ACTIVE_RETICLE_PIP_ON_A_STICK = 1, + VEH_ACTIVE_RETICLE_BOUNCING_DIAMOND = 2, + VEH_ACTIVE_RETICLE_MAX + }; + + enum weapType_t : int + { + WEAPTYPE_BULLET = 0, + WEAPTYPE_GRENADE = 1, + WEAPTYPE_PROJECTILE = 2, + WEAPTYPE_RIOTSHIELD = 3, + WEAPTYPE_MAX + }; + + enum weapClass_t : int + { + WEAPCLASS_RIFLE = 0, + WEAPCLASS_SNIPER = 1, + WEAPCLASS_MG = 2, + WEAPCLASS_SMG = 3, + WEAPCLASS_SPREAD = 4, + WEAPCLASS_PISTOL = 5, + WEAPCLASS_GRENADE = 6, + WEAPCLASS_ROCKETLAUNCHER = 7, + WEAPCLASS_TURRET = 8, + WEAPCLASS_THROWINGKNIFE = 9, + WEAPCLASS_NON_PLAYER = 10, + WEAPCLASS_ITEM = 11, + WEAPCLASS_MAX + }; + + enum OffhandClass : int + { + OFFHAND_CLASS_NONE = 0, + OFFHAND_CLASS_FRAG_GRENADE = 1, + OFFHAND_CLASS_SMOKE_GRENADE = 2, + OFFHAND_CLASS_FLASH_GRENADE = 3, + OFFHAND_CLASS_MAX + }; + + enum playerAnimType_t : int + { + PLAER_ANIM_TYPE_NONE = 0x0, + PLAER_ANIM_TYPE_OTHER = 0x1, + PLAER_ANIM_TYPE_PISTOL = 0x2, + PLAER_ANIM_TYPE_SMG = 0x3, + PLAER_ANIM_TYPE_AUTORIFLE = 0x4, + PLAER_ANIM_TYPE_MG = 0x5, + PLAER_ANIM_TYPE_SNIPER = 0x6, + PLAER_ANIM_TYPE_ROCKETLAUNCHER = 0x7, + PLAER_ANIM_TYPE_EXPLOSIVE = 0x8, + PLAER_ANIM_TYPE_GRENADE = 0x9, + PLAER_ANIM_TYPE_TURRET = 0xA, + PLAER_ANIM_TYPE_C4 = 0xB, + PLAER_ANIM_TYPE_M203 = 0xC, + PLAER_ANIM_TYPE_HOLD = 0xD, + PLAER_ANIM_TYPE_BRIEFCASE = 0xE, + PLAER_ANIM_TYPE_RIOTSHIELD = 0xF, + PLAER_ANIM_TYPE_LAPTOP = 0x10, + PLAER_ANIM_TYPE_THROWINGKNIFE = 0x11 + }; + + enum weapProjExplosion_t + { + WEAPPROJEXP_GRENADE = 0x0, + WEAPPROJEXP_ROCKET = 0x1, + WEAPPROJEXP_FLASHBANG = 0x2, + WEAPPROJEXP_NONE = 0x3, + WEAPPROJEXP_DUD = 0x4, + WEAPPROJEXP_SMOKE = 0x5, + WEAPPROJEXP_HEAVY = 0x6, + WEAPPROJEXP_NUM = 0x7 + }; + + enum WeapStickinessType + { + WEAPSTICKINESS_NONE = 0x0, + WEAPSTICKINESS_ALL = 0x1, + WEAPSTICKINESS_ALL_ORIENT = 0x2, + WEAPSTICKINESS_GROUND = 0x3, + WEAPSTICKINESS_GROUND_WITH_YAW = 0x4, + WEAPSTICKINESS_KNIFE = 0x5, + WEAPSTICKINESS_COUNT = 0x6 + }; + + enum weaponIconRatioType_t + { + WEAPON_ICON_RATIO_1TO1 = 0x0, + WEAPON_ICON_RATIO_2TO1 = 0x1, + WEAPON_ICON_RATIO_4TO1 = 0x2, + WEAPON_ICON_RATIO_COUNT = 0x3 + }; + + enum ammoCounterClipType_t + { + AMMO_COUNTER_CLIP_NONE = 0x0, + AMMO_COUNTER_CLIP_MAGAZINE = 0x1, + AMMO_COUNTER_CLIP_SHORTMAGAZINE = 0x2, + AMMO_COUNTER_CLIP_SHOTGUN = 0x3, + AMMO_COUNTER_CLIP_ROCKET = 0x4, + AMMO_COUNTER_CLIP_BELTFED = 0x5, + AMMO_COUNTER_CLIP_ALTWEAPON = 0x6, + AMMO_COUNTER_CLIP_COUNT = 0x7 + }; + + enum weapOverlayReticle_t + { + WEAPOVERLAYRETICLE_NONE = 0x0, + WEAPOVERLAYRETICLE_CROSSHAIR = 0x1, + WEAPOVERLAYRETICLE_NUM = 0x2 + }; + + enum weapOverlayInterface_t + { + WEAPOVERLAYINTERFACE_NONE = 0x0, + WEAPOVERLAYINTERFACE_JAVELIN = 0x1, + WEAPOVERLAYINTERFACE_TURRETSCOPE = 0x2, + WEAPOVERLAYINTERFACECOUNT = 0x3 + }; + + enum weapStance_t + { + WEAPSTANCE_STAND = 0x0, + WEAPSTANCE_DUCK = 0x1, + WEAPSTANCE_PRONE = 0x2, + WEAPSTANCE_NUM = 0x3 + }; + + enum ImpactType + { + IMPACT_TYPE_NONE = 0, + IMPACT_TYPE_BULLET_SMALL = 1, + IMPACT_TYPE_BULLET_LARGE = 2, + IMPACT_TYPE_BULLET_AP = 3, + IMPACT_TYPE_SHOTGUN_FMJ = 4, + IMPACT_TYPE_SHOTGUN = 5, + IMPACT_TYPE_GRENADE_BOUNCE = 7, + IMPACT_TYPE_GRENADE_EXPLODE = 8, + IMPACT_TYPE_ROCKET_EXPLODE = 9, + IMPACT_TYPE_PROJECTILE_DUD = 10, + IMPACT_TYPE_MAX + }; + + enum guidedMissileType_t + { + MISSILE_GUIDANCE_NONE = 0x0, + MISSILE_GUIDANCE_SIDEWINDER = 0x1, + MISSILE_GUIDANCE_HELLFIRE = 0x2, + MISSILE_GUIDANCE_JAVELIN = 0x3, + MISSILE_GUIDANCE_MAX + }; + + // Check knots in FF later! +#pragma pack(push, 4) + struct SndCurve + { + const char* filename; + const char* name; + unsigned __int16 knotCount; + vec2_t knots[16]; + }; +#pragma pack(pop) + + // Loaded sound +#pragma pack(push, 4) + struct LoadedSoundStruct + { + int waveFormat; + int unknown1; + int dataLength; + int sampleRate; + int bitPerChannel; + int channelCount; + int unknown3; + int blockAlign; + int unknown5; + char* soundData; + }; +#pragma pack(pop) + + struct LoadedSound + { + const char* name; + LoadedSoundStruct sound; + }; + + // Sounds + struct SpeakerLevels + { + int speaker; + int numLevels; + float levels[2]; + }; + + struct ChannelMap + { + int entryCount; // how many entries are used + SpeakerLevels speakers[6]; + }; + + struct SpeakerMap + { + bool isDefault; + char _pad[3]; + const char* name; + ChannelMap channelMaps[2][2]; + }; + + enum snd_alias_type_t : char + { + SAT_UNKNOWN = 0x0, + SAT_LOADED = 0x1, + SAT_STREAMED = 0x2, + SAT_PRIMED = 0x3, + SAT_COUNT = 0x4, + }; + + struct StreamFileNamePacked + { + unsigned __int64 offset; + unsigned __int64 length; + }; + + struct StreamFileNameRaw + { + const char* dir; + const char* name; + }; + + union StreamFileInfo + { + StreamFileNameRaw raw; + StreamFileNamePacked packed; + }; + + struct StreamFileName + { + unsigned __int16 isLocalized; + unsigned __int16 fileIndex; + StreamFileInfo info; + }; + + /*struct StreamedSound + { + StreamFileName filename; + unsigned int totalMsec; + };*/ + struct StreamedSound + { + const char* dir; + const char* name; + }; + + struct PrimedSound + { + StreamFileName filename; + LoadedSound* loadedPart; + int dataOffset; + int totalSize; + unsigned int primedCrc; + }; + + union SoundData + { + LoadedSound* loadSnd; // SoundFile->type == SAT_LOADED + StreamedSound streamSnd; // SoundFile->type == SAT_STREAMED + //PrimedSound primedSnd; // SoundFile->type == SAT_PRIMED + }; + + struct SoundFile // 0x10 + { + char type; + bool exists; + char _pad[2]; + SoundData sound; + }; +#pragma pack(push, 4) + struct snd_alias_t + { + const char* aliasName; + const char* subtitle; + const char* secondaryAliasName; + const char* chainAliasName; + const char* mixerGroup; + SoundFile* soundFile; + int sequence; + float volMin; + float volMax; + float pitchMin; + float pitchMax; + float distMin; + float distMax; + int flags; + float slavePercentage; + float probability; + float lfePercentage; + float centerPercentage; + int startDelay; + int pad2; + SndCurve* volumeFalloffCurve; + float envelopMin; + float envelopMax; + float envelopPercentage; + SpeakerMap* speakerMap; + }; +#pragma pack(pop) + struct snd_alias_list_t + { + union + { + const char* aliasName; + const char* name; + }; + + snd_alias_t* head; + int count; + }; + + union snd_alias_list_name + { + const char* name; + snd_alias_list_t* asset; + }; + + // Tracers + struct TracerDef + { + const char* name; + Material* material; + unsigned int drawInterval; + float speed; + float beamLength; + float beamWidth; + float screwRadius; + float screwDist; + float colors[5][4]; + }; + + struct WeaponDef + { + union + { + struct + { + const char* szOverlayName; + XModel** gunXModel; + XModel* handXModel; + const char** szXAnimsRightHanded; + const char** szXAnimsLeftHanded; + const char* szModeName; + unsigned __int16* notetrackSoundMapKeys; + unsigned __int16* notetrackSoundMapValues; + unsigned __int16* notetrackRumbleMapKeys; + unsigned __int16* notetrackRumbleMapValues; + int playerAnimType; + weapType_t weapType; + weapClass_t weapClass; + PenetrateType penetrateType; + weapInventoryType_t inventoryType; + weapFireType_t fireType; + OffhandClass offhandClass; + weapStance_t stance; + void* viewFlashEffect; // FxEffectDef + void* worldFlashEffect; // FxEffectDef + snd_alias_list_t* pickupSound; + snd_alias_list_t* pickupSoundPlayer; + snd_alias_list_t* ammoPickupSound; + snd_alias_list_t* ammoPickupSoundPlayer; + snd_alias_list_t* projectileSound; + snd_alias_list_t* pullbackSound; + snd_alias_list_t* pullbackSoundPlayer; + snd_alias_list_t* fireSound; + snd_alias_list_t* fireSoundPlayer; + snd_alias_list_t* fireSoundPlayerAkimbo; + snd_alias_list_t* fireLoopSound; + snd_alias_list_t* fireLoopSoundPlayer; + snd_alias_list_t* fireStopSound; + snd_alias_list_t* fireStopSoundPlayer; + snd_alias_list_t* fireLastSound; + snd_alias_list_t* fireLastSoundPlayer; + snd_alias_list_t* emptyFireSound; + snd_alias_list_t* emptyFireSoundPlayer; + snd_alias_list_t* meleeSwipeSound; + snd_alias_list_t* meleeSwipeSoundPlayer; + snd_alias_list_t* meleeHitSound; + snd_alias_list_t* meleeMissSound; + snd_alias_list_t* rechamberSound; + snd_alias_list_t* rechamberSoundPlayer; + snd_alias_list_t* reloadSound; + snd_alias_list_t* reloadSoundPlayer; + snd_alias_list_t* reloadEmptySound; + snd_alias_list_t* reloadEmptySoundPlayer; + snd_alias_list_t* reloadStartSound; + snd_alias_list_t* reloadStartSoundPlayer; + snd_alias_list_t* reloadEndSound; + snd_alias_list_t* reloadEndSoundPlayer; + snd_alias_list_t* detonateSound; + snd_alias_list_t* detonateSoundPlayer; + snd_alias_list_t* nightVisionWearSound; + snd_alias_list_t* nightVisionWearSoundPlayer; + snd_alias_list_t* nightVisionRemoveSound; + snd_alias_list_t* nightVisionRemoveSoundPlayer; + snd_alias_list_t* altSwitchSound; + snd_alias_list_t* altSwitchSoundPlayer; + snd_alias_list_t* raiseSound; + snd_alias_list_t* raiseSoundPlayer; + snd_alias_list_t* firstRaiseSound; + snd_alias_list_t* firstRaiseSoundPlayer; + snd_alias_list_t* putawaySound; + snd_alias_list_t* putawaySoundPlayer; + snd_alias_list_t* scanSound; + }; + + char _portpad0[268]; + }; + + snd_alias_list_t** bounceSound; + + union + { + struct + { + void* viewShellEjectEffect; + void* worldShellEjectEffect; + void* viewLastShotEjectEffect; + void* worldLastShotEjectEffect; + Material* reticleCenter; + Material* reticleSide; + int iReticleCenterSize; + int iReticleSideSize; + int iReticleMinOfs; + activeReticleType_t activeReticleType; + float vStandMove[3]; + float vStandRot[3]; + float strafeMove[3]; + float strafeRot[3]; + float vDuckedOfs[3]; + float vDuckedMove[3]; + float vDuckedRot[3]; + float vProneOfs[3]; + float vProneMove[3]; + float vProneRot[3]; + float fPosMoveRate; + float fPosProneMoveRate; + float fStandMoveMinSpeed; + float fDuckedMoveMinSpeed; + float fProneMoveMinSpeed; + float fPosRotRate; + float fPosProneRotRate; + float fStandRotMinSpeed; + float fDuckedRotMinSpeed; + float fProneRotMinSpeed; + XModel** worldModel; + XModel* worldClipModel; + XModel* rocketModel; + XModel* knifeModel; + XModel* worldKnifeModel; + Material* hudIcon; + weaponIconRatioType_t hudIconRatio; + Material* pickupIcon; + weaponIconRatioType_t pickupIconRatio; + Material* ammoCounterIcon; + weaponIconRatioType_t ammoCounterIconRatio; + ammoCounterClipType_t ammoCounterClip; + int iStartAmmo; + const char* szAmmoName; + int iAmmoIndex; + const char* szClipName; + int iClipIndex; + int iMaxAmmo; + int shotCount; + const char* szSharedAmmoCapName; + int iSharedAmmoCapIndex; + int iSharedAmmoCap; + int damage; + int playerDamage; + int iMeleeDamage; + int iDamageType; + int iFireDelay; + int iMeleeDelay; + int meleeChargeDelay; + int iDetonateDelay; + int iRechamberTime; + int rechamberTimeOneHanded; + int iRechamberBoltTime; + int iHoldFireTime; + int iDetonateTime; + int iMeleeTime; + int meleeChargeTime; + int iReloadTime; + int reloadShowRocketTime; + int iReloadEmptyTime; + int iReloadAddTime; + int iReloadStartTime; + int iReloadStartAddTime; + int iReloadEndTime; + int iDropTime; + int iRaiseTime; + int iAltDropTime; + int quickDropTime; + int quickRaiseTime; + int iBreachRaiseTime; + int iEmptyRaiseTime; + int iEmptyDropTime; + int sprintInTime; + int sprintLoopTime; + int sprintOutTime; + int stunnedTimeBegin; + int stunnedTimeLoop; + int stunnedTimeEnd; + int nightVisionWearTime; + int nightVisionWearTimeFadeOutEnd; + int nightVisionWearTimePowerUp; + int nightVisionRemoveTime; + int nightVisionRemoveTimePowerDown; + int nightVisionRemoveTimeFadeInStart; + int fuseTime; + int aiFuseTime; + }; + + char _portpad1[464]; + }; + + union + { + struct + { + float autoAimRange; + float aimAssistRange; + float aimAssistRangeAds; + float aimPadding; + float enemyCrosshairRange; + float moveSpeedScale; + float adsMoveSpeedScale; + float sprintDurationScale; + float fAdsZoomInFrac; + float fAdsZoomOutFrac; + Material* overlayMaterial; + Material* overlayMaterialLowRes; + Material* overlayMaterialEMP; + Material* overlayMaterialEMPLowRes; + weapOverlayReticle_t overlayReticle; + int overlayInterface; + float overlayWidth; + float overlayHeight; + float overlayWidthSplitscreen; + float overlayHeightSplitscreen; + float fAdsBobFactor; + float fAdsViewBobMult; + float fHipSpreadStandMin; + float fHipSpreadDuckedMin; + float fHipSpreadProneMin; + float hipSpreadStandMax; + float hipSpreadDuckedMax; + float hipSpreadProneMax; + float fHipSpreadDecayRate; + float fHipSpreadFireAdd; + float fHipSpreadTurnAdd; + float fHipSpreadMoveAdd; + float fHipSpreadDuckedDecay; + float fHipSpreadProneDecay; + float fHipReticleSidePos; + float fAdsIdleAmount; + float fHipIdleAmount; + }; + + char _portpad6[148]; + }; + + union + { + struct + { + float adsIdleSpeed; + float hipIdleSpeed; + float fIdleCrouchFactor; + float fIdleProneFactor; + float fGunMaxPitch; + float fGunMaxYaw; + float swayMaxAngle; + float swayLerpSpeed; + float swayPitchScale; + float swayYawScale; + float swayHorizScale; + float swayVertScale; + float swayShellShockScale; + float adsSwayMaxAngle; + float adsSwayLerpSpeed; + float adsSwayPitchScale; + float adsSwayYawScale; + float adsSwayHorizScale; + float adsSwayVertScale; + float adsViewErrorMin; + float adsViewErrorMax; + }; + + char _portpad2[84]; + }; + + union + { + struct + { + PhysCollmap* physCollmap; + float dualWieldViewModelOffset; + weaponIconRatioType_t killIconRatio; + int iReloadAmmoAdd; + int iReloadStartAdd; + int ammoDropStockMin; + int ammoDropClipPercentMin; + int ammoDropClipPercentMax; + int iExplosionRadius; + int iExplosionRadiusMin; + int iExplosionInnerDamage; + int iExplosionOuterDamage; + float damageConeAngle; + float bulletExplDmgMult; + float bulletExplRadiusMult; + int iProjectileSpeed; + int iProjectileSpeedUp; + int iProjectileSpeedForward; + int iProjectileActivateDist; + float projLifetime; + float timeToAccelerate; + float projectileCurvature; + XModel* projectileModel; + int projExplosion; + void* projExplosionEffect; + void* projDudEffect; + snd_alias_list_t* projExplosionSound; + snd_alias_list_t* projDudSound; + WeapStickinessType stickiness; + float lowAmmoWarningThreshold; + float ricochetChance; + float* parallelBounce; + float* perpendicularBounce; + }; + + char _portpad3[132]; + }; + + union + { + struct + { + void* projTrailEffect; + void* projBeaconEffect; + float vProjectileColor[3]; + guidedMissileType_t guidedMissileType; + float maxSteeringAccel; + int projIgnitionDelay; + void* projIgnitionEffect; + snd_alias_list_t* projIgnitionSound; + float fAdsAimPitch; + float fAdsCrosshairInFrac; + float fAdsCrosshairOutFrac; + int adsGunKickReducedKickBullets; + float adsGunKickReducedKickPercent; + float fAdsGunKickPitchMin; + float fAdsGunKickPitchMax; + float fAdsGunKickYawMin; + float fAdsGunKickYawMax; + float fAdsGunKickAccel; + float fAdsGunKickSpeedMax; + float fAdsGunKickSpeedDecay; + float fAdsGunKickStaticDecay; + float fAdsViewKickPitchMin; + float fAdsViewKickPitchMax; + float fAdsViewKickYawMin; + float fAdsViewKickYawMax; + float fAdsViewScatterMin; + float fAdsViewScatterMax; + float fAdsSpread; + int hipGunKickReducedKickBullets; + float hipGunKickReducedKickPercent; + float fHipGunKickPitchMin; + float fHipGunKickPitchMax; + float fHipGunKickYawMin; + float fHipGunKickYawMax; + float fHipGunKickAccel; + float fHipGunKickSpeedMax; + float fHipGunKickSpeedDecay; + float fHipGunKickStaticDecay; + float fHipViewKickPitchMin; + float fHipViewKickPitchMax; + float fHipViewKickYawMin; + float fHipViewKickYawMax; + float fHipViewScatterMin; + float fHipViewScatterMax; + float fightDist; + float maxDist; + const char* accuracyGraphName[2]; + float (*originalAccuracyGraphKnots[2])[2]; + unsigned __int16 originalAccuracyGraphKnotCount[2]; + int iPositionReloadTransTime; + float leftArc; + float rightArc; + float topArc; + float bottomArc; + float accuracy; + float aiSpread; + float playerSpread; + float minTurnSpeed[2]; + float maxTurnSpeed[2]; + float pitchConvergenceTime; + float yawConvergenceTime; + float suppressTime; + float maxRange; + float fAnimHorRotateInc; + float fPlayerPositionDist; + const char* szUseHintString; + const char* dropHintString; + int iUseHintStringIndex; + int dropHintStringIndex; + float horizViewJitter; + float vertViewJitter; + float scanSpeed; + float scanAccel; + int scanPauseTime; + const char* szScript; + float fOOPosAnimLength[2]; + int minDamage; + int minPlayerDamage; + float fMaxDamageRange; + float fMinDamageRange; + float destabilizationRateTime; + float destabilizationCurvatureMax; + int destabilizeDistance; + float* locationDamageMultipliers; + const char* fireRumble; + const char* meleeImpactRumble; + TracerDef* tracerType; + float turretScopeZoomRate; + float turretScopeZoomMin; + float turretScopeZoomMax; + float turretOverheatUpRate; + float turretOverheatDownRate; + float turretOverheatPenalty; + }; + + char _portpad4[400]; + }; + + union + { + struct + { + snd_alias_list_t* turretOverheatSound; + void* turretOverheatEffect; + const char* turretBarrelSpinRumble; + float turretBarrelSpinSpeed; + float turretBarrelSpinUpTime; + float turretBarrelSpinDownTime; + snd_alias_list_t* turretBarrelSpinMaxSnd; + snd_alias_list_t* turretBarrelSpinUpSnd[4]; + snd_alias_list_t* turretBarrelSpinDownSnd[4]; + snd_alias_list_t* missileConeSoundAlias; + snd_alias_list_t* missileConeSoundAliasAtBase; + float missileConeSoundRadiusAtTop; + float missileConeSoundRadiusAtBase; + float missileConeSoundHeight; + float missileConeSoundOriginOffset; + float missileConeSoundVolumescaleAtCore; + float missileConeSoundVolumescaleAtEdge; + float missileConeSoundVolumescaleCoreSize; + float missileConeSoundPitchAtTop; + float missileConeSoundPitchAtBottom; + float missileConeSoundPitchTopSize; + float missileConeSoundPitchBottomSize; + float missileConeSoundCrossfadeTopSize; + float missileConeSoundCrossfadeBottomSize; + bool sharedAmmo; + bool lockonSupported; + bool requireLockonToFire; + bool bigExplosion; + bool noAdsWhenMagEmpty; + bool avoidDropCleanup; + bool inheritsPerks; + bool crosshairColorChange; + bool bRifleBullet; + bool armorPiercing; + bool bBoltAction; + bool aimDownSight; + bool bRechamberWhileAds; + bool bBulletExplosiveDamage; + bool bCookOffHold; + bool bClipOnly; + bool noAmmoPickup; + bool adsFireOnly; + bool cancelAutoHolsterWhenEmpty; + bool disableSwitchToWhenEmpty; + bool suppressAmmoReserveDisplay; + bool laserSightDuringNightvision; + bool markableViewmodel; + bool noDualWield; + bool flipKillIcon; + bool bNoPartialReload; + bool bSegmentedReload; + bool blocksProne; + bool silenced; + bool isRollingGrenade; + bool projExplosionEffectForceNormalUp; + bool bProjImpactExplode; + bool stickToPlayers; + bool hasDetonator; + bool disableFiring; + bool timedDetonation; + bool rotate; + bool holdButtonToThrow; + bool freezeMovementWhenFiring; + bool thermalScope; + bool altModeSameWeapon; + bool turretBarrelSpinEnabled; + bool missileConeSoundEnabled; + bool missileConeSoundPitchshiftEnabled; + bool missileConeSoundCrossfadeEnabled; + bool offhandHoldIsCancelable; + + void test() + { + sizeof(*this); + } + }; + + char _portpad5[168]; + }; + }; + + struct WeaponCompleteDef + { + union + { + struct + { + const char* szInternalName; + WeaponDef* weapDef; + const char* szDisplayName; + unsigned __int16* hideTags; + }; + + char _portpad0[16]; + }; + + const char** szXAnims; + + union + { + struct + { + float fAdsZoomFov; + int iAdsTransInTime; + int iAdsTransOutTime; + int iClipSize; + ImpactType impactType; + int iFireTime; + }; + + char _portpad1[24]; + }; + + union + { + struct + { + weaponIconRatioType_t dpadIconRatio; + float penetrateMultiplier; + float fAdsViewKickCenterSpeed; + float fHipViewKickCenterSpeed; + const char* szAltWeaponName; + unsigned int altWeaponIndex; + int iAltRaiseTime; + }; + + char _portpad2[28]; + }; + + union + { + struct + { + Material* killIcon; + Material* dpadIcon; + int fireAnimLength; + int iFirstRaiseTime; + }; + + char _portpad3[16]; + }; + + union + { + struct + { + int ammoDropStockMax; + float adsDofStart; + float adsDofEnd; + unsigned __int16 accuracyGraphKnotCount[2]; + float (*accuracyGraphKnots[2])[2]; + bool motionTracker; + bool enhanced; + bool dpadIconShowsAmmo; + }; + + char _portpad4[28]; + }; + }; + + struct Glyph + { + unsigned __int16 letter; + char x0; + char y0; + char dx; + char pixelWidth; + char pixelHeight; + float s0; + float t0; + float s1; + float t1; + }; + +#pragma pack(push, 4) + struct cplane_s + { + float normal[3]; + float dist; + char type; + char signbits; + }; +#pragma pack(pop) + + /* 1003 */ + struct Font_s + { + const char* fontName; + int pixelHeight; + int glyphCount; + Material* material; + Material* glowMaterial; + Glyph* glyphs; + }; + + struct cbrushside_t + { + cplane_s* plane; + unsigned int materialNum; + /*unsigned __int16 materialNum; + char firstAdjacentSideOffset; + char edgeCount;*/ + }; + + // ClipMap + typedef char cbrushedge_t; + + struct cbrush_t + { + unsigned __int16 numsides; + unsigned __int16 glassPieceIndex; + cbrushside_t* sides; + cbrushedge_t* edge; + __int16 axialMaterialNum[2][3]; + char firstAdjacentSideOffsets[2][3]; + char edgeCount[2][3]; + }; + + struct BrushWrapper + { + float mins[3]; + float maxs[3]; + unsigned int numPlaneSide; + cbrushside_t* side; + char* edge; + __int16 axialMaterialNum[2][3]; + __int16 firstAdjacentSideOffsets[2][3]; + int numEdge; + cplane_s* plane; + }; + + //struct BrushWrapper + //{ + // Bounds bounds; // 24 + // cbrush_t brush; // 36 + // cbrushside_t *side; // 4 + // int totalEdgeCount; // 4 + // cplane_s *plane; // 4 + // short numPlaneSide; // 2 + // char *edge; // 4 + // int numEdge; // 4 + //}; // 82 bytes total + + struct PhysGeomInfo + { + BrushWrapper* brush; + int type; + float orientation[3][3]; + Bounds bounds; + }; + + struct PhysMass + { + float centerOfMass[3]; + float momentsOfInertia[3]; + float productsOfInertia[3]; + // int contents; + }; + + struct PhysCollmap + { + const char* name; + unsigned int numInfo; + PhysGeomInfo* info; + PhysMass mass; + Bounds bounds; + }; + + struct G_GlassName + { + char* nameStr; + unsigned __int16 name; + unsigned __int16 pieceCount; + unsigned __int16* pieceIndices; + }; + +#pragma pack(push, 2) + struct G_GlassPiece + { + unsigned __int16 damageTaken; + unsigned __int16 collapseTime; + int lastStateChangeTime; + char impactDir; + char impactPos[2]; + }; +#pragma pack(pop) + + struct G_GlassData + { + G_GlassPiece* glassPieces; + unsigned int pieceCount; + unsigned __int16 damageToWeaken; + unsigned __int16 damageToDestroy; + unsigned int glassNameCount; + G_GlassName* glassNames; + char pad[108]; + }; + + struct GameWorldMp + { + const char* name; + G_GlassData* g_glassData; + }; + + struct GameWorldSp + { + const char* name; + char useless_sp_shit[48]; + G_GlassData* g_glassData; + }; + + // FxWorld +#pragma pack(push, 4) + + struct FxGlassDef + { + float halfThickness; + float texVecs[2][2]; + GfxColor color; + Material* material; + Material* materialShattered; + PhysPreset* physPreset; + }; + + struct FxSpatialFrame + { + float quat[4]; + float origin[3]; + }; + + union FxGlassPiecePlace + { + struct + { + FxSpatialFrame frame; + float radius; + }; + + unsigned int nextFree; + }; + + struct FxGlassPieceState + { + float texCoordOrigin[2]; + unsigned int supportMask; + unsigned __int16 initIndex; + unsigned __int16 geoDataStart; + unsigned __int16 lightingIndex; + char defIndex; + char pad[3]; + char vertCount; + char holeDataCount; + char crackDataCount; + char fanDataCount; + unsigned __int16 flags; + float areaX2; + }; + + struct FxGlassPieceDynamics + { + int fallTime; + __int32 physObjId; + __int32 physJointId; + float vel[3]; + float avel[3]; + }; + + struct FxGlassVertex + { + __int16 x; + __int16 y; + }; + + struct FxGlassHoleHeader + { + unsigned __int16 uniqueVertCount; + char touchVert; + char pad[1]; + }; + + struct FxGlassCrackHeader + { + unsigned __int16 uniqueVertCount; + char beginVertIndex; + char endVertIndex; + }; + + union FxGlassGeometryData + { + FxGlassVertex vert; + FxGlassHoleHeader hole; + FxGlassCrackHeader crack; + char asBytes[4]; + __int16 anonymous[2]; + }; + + struct FxGlassInitPieceState //Note, on MW3 this is missing 4 bytes, just not sure whats missing yet + { + FxSpatialFrame frame; + float radius; + float texCoordOrigin[2]; + unsigned int supportMask; + //float areaX2; // Commented out a random thing so the size fits. Most probably wrong since it was random. + unsigned __int16 lightingIndex; + char defIndex; + char vertCount; + char fanDataCount; + char pad[1]; + }; + + struct FxGlassSystem + { + int time; // 4 + int prevTime; // 4 + unsigned int defCount; // 4 + unsigned int pieceLimit; // 4 + unsigned int pieceWordCount; // 4 + unsigned int initPieceCount; // 4 + unsigned int cellCount; // 4 + unsigned int activePieceCount; // 4 + unsigned int firstFreePiece; // 4 + unsigned int geoDataLimit; // 4 + unsigned int geoDataCount; // 4 + unsigned int initGeoDataCount; // 4 + FxGlassDef* defs; // 4 + FxGlassPiecePlace* piecePlaces; // 4 + FxGlassPieceState* pieceStates; // 4 + FxGlassPieceDynamics* pieceDynamics; // 4 + FxGlassGeometryData* geoData; // 4 + unsigned int* isInUse; // 4 + unsigned int* cellBits; // 4 + char* visData; // 4 + VecInternal<3>* linkOrg; + float* halfThickness; // 4 + unsigned __int16* lightingHandles; // 4 + FxGlassInitPieceState* initPieceStates; // 4 + FxGlassGeometryData* initGeoData; // 4 + bool needToCompactData; // 1 + char initCount; + short pad; + float effectChanceAccum; // 4 + int lastPieceDeletionTime; // 4 + }; + + struct FxWorld + { + char* name; + FxGlassSystem glassSys; + }; +#pragma pack(pop) + + // MapEnts + struct TriggerModel + { + int contents; + unsigned short hullCount; + unsigned short firstHull; + }; + + struct TriggerHull + { + Bounds bounds; + int contents; + unsigned short slabCount; + unsigned short firstSlab; + }; + + struct TriggerSlab + { + vec3_t dir; + float midPoint; + float halfSize; + }; + + struct MapTriggers + { + int modelCount; + TriggerModel* models; // sizeof 8 + int hullCount; + TriggerHull* hulls; // sizeof 32 + int slabCount; + TriggerSlab* slabs; // sizeof 20 + }; +#pragma pack(push, 1) + struct Stage + { + char* stageName; + float offset[3]; + int flags; + }; + + struct MapEnts + { + const char* name; // 0 + const char* entityString; // 4 + int numEntityChars; // 8 + MapTriggers trigger; // 12 + Stage* stageNames; // 36 + char stageCount; // 40 + char pad[3]; + }; +#pragma pack(pop) + +#pragma pack(push, 1) + struct ComPrimaryLight + { + union + { + char _portpad0[28]; + + struct + { + char type; + char canUseShadowMap; + char exponent; + char unused; + float color[3]; + float dir[3]; + }; + }; + + union + { + char _portpad1[40]; + + struct + { + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + float cosHalfFovExpanded; + float rotationLimit; + float translationLimit; + const char* defName; + }; + }; + }; +#pragma pack(pop) + + struct ComWorld + { + const char* name; + int isInUse; + unsigned int primaryLightCount; + ComPrimaryLight* primaryLights; + }; + + + union XAnimIndices + { + char* _1; + unsigned __int16* _2; + void* data; + }; + + union XAnimDynamicFrames + { + char (*_1)[3]; + unsigned __int16 (*_2)[3]; + }; + + union XAnimDynamicIndices + { + char _1[1]; + unsigned __int16 _2[1]; + }; + + struct XAnimPartTransFrames + { + float mins[3]; + float size[3]; + XAnimDynamicFrames frames; + XAnimDynamicIndices indices; + }; + + union XAnimPartTransData + { + XAnimPartTransFrames frames; + float frame0[3]; + }; + + struct XAnimPartTrans + { + unsigned __int16 size; + char smallTrans; + __declspec(align(2)) XAnimPartTransData u; + }; + + struct XAnimDeltaPartQuatDataFrames2 + { + __int16* frames; + char indices[1]; + }; + + union XAnimDeltaPartQuatData2 + { + XAnimDeltaPartQuatDataFrames2 frames; + __int16 frame0[2]; + }; + + struct XAnimDeltaPartQuat2 + { + unsigned __int16 size; + XAnimDeltaPartQuatData2 u; + }; + + struct XAnimDeltaPartQuatDataFrames + { + __int16 (*frames)[2]; + XAnimDynamicIndices indices; + }; + + union XAnimDeltaPartQuatData + { + XAnimDeltaPartQuatDataFrames frames; + __int16 frame0[2]; + }; + + struct XAnimDeltaPartQuat + { + unsigned __int16 size; + __declspec(align(4)) XAnimDeltaPartQuatData u; + }; + + struct XAnimDeltaPart + { + XAnimPartTrans* trans; + XAnimDeltaPartQuat2* quat2; + XAnimDeltaPartQuat* quat; + }; + +#pragma pack(push, 4) + struct XAnimNotifyInfo + { + short name; + float time; + }; + + struct XAnimParts + { + char* name; // 0 + unsigned short dataByteCount; // 4 + unsigned short dataShortCount; // 6 + unsigned short dataIntCount; // 8 + unsigned short randomDataByteCount; // 10 - 0xA + unsigned short randomDataIntCount; // 12 - 0xC + unsigned short framecount; // 14 - 0xE + char flags; // 16 + unsigned char boneCount[10]; // 17 + char notetrackCount; // 27 + bool bLoop; // 28 + bool bDelta; // 29 + char assetType; // 30 + char ikType; // 31 + unsigned int randomDataShortCount; // 32 - 0x20 + unsigned int indexcount; // 36 - 0x24 + float framerate; // 40 - 0x28 + float frequency; // 44 - 0x2C + unsigned short* tagnames; // 48 - 0x30 + char* dataByte; // 52 - 0x34 + short* dataShort; // 56 - 0x38 + int* dataInt; // 60 - 0x3C + short* randomDataShort; // 64 - 0x40 + char* randomDataByte; // 68 - 0x44 + int* randomDataInt; // 72 - 0x48 + XAnimIndices indices; // 76 - 0x4C + XAnimNotifyInfo* notetracks; // 80 - 0x50 + XAnimDeltaPart* delta; // 84 - 0x54 + }; +#pragma pack(pop) + + // Localized Strings + struct LocalizeEntry + { + const char* localizedString; + const char* name; + }; + + // Stringtables + struct StringTableCell + { + char* string; // 0 + int hash; // 4 + }; + + struct StringTable + { + const char* name; // 0 + int columns; // 4 + int rows; // 8 + StringTableCell* strings; // 12 + }; + + // Fx + struct FxEffectDef; + + /* struct FxElemMarkVisuals + { + Material *materials[2]; + };*/ + struct FxImpactEntry + { + FxEffectDef* nonflesh[31]; + FxEffectDef* flesh[4]; + }; + + union FxEffectDefRef + { + FxEffectDef* handle; + const char* name; + }; + + union FxElemVisuals + { + const void* anonymous; + Material* material; + XModel* xmodel; + FxEffectDefRef* effectDef; + const char* soundName; + }; + + typedef Material* FxElemMarkVisuals[2]; + + union FxElemDefVisuals + { + FxElemVisuals instance; + FxElemVisuals* array; + FxElemMarkVisuals* markArray; + }; + + struct FxTrailVertex + { + float pos[2]; + float normal[2]; + float texCoord; + }; + + struct FxTrailDef + { + int scrollTimeMsec; + int repeatDist; + float invSplitDist; + float invSplitArcDist; + float invSplitTime; + int vertCount; + FxTrailVertex* verts; + int indCount; + unsigned __int16* inds; + }; + + struct FxSparkFountainDef + { + float gravity; + float bounceFrac; + float bounceRand; + float sparkSpacing; + float sparkLength; + int sparkCount; + float loopTime; + float velMin; + float velMax; + float velConeFrac; + float restSpeed; + float boostTime; + float boostFactor; + }; + + union FxElemExtendedDefPtr + { + FxTrailDef* trailDef; + FxSparkFountainDef* sparkFountain; + char* unknownDef; + }; + + struct FxSpawnDefLooping + { + int intervalMsec; + int count; + }; + + struct FxIntRange + { + int base; + int amplitude; + }; + + struct FxFloatRange + { + float base; + float amplitude; + }; + + struct FxSpawnDefOneShot + { + FxIntRange count; + }; + + union FxSpawnDef + { + FxSpawnDefLooping looping; + FxSpawnDefOneShot oneShot; + }; + + struct FxElemAtlas + { + char behavior; + char index; + char fps; + char loopCount; + char colIndexBits; + char rowIndexBits; + __int16 entryCount; + }; + + struct FxElemVec3Range + { + float base[3]; + float amplitude[3]; + }; + + struct FxElemVelStateInFrame + { + FxElemVec3Range velocity; + FxElemVec3Range totalDelta; + }; + + const struct FxElemVelStateSample + { + FxElemVelStateInFrame local; + FxElemVelStateInFrame world; + }; + + struct FxElemVisualState + { + char color[4]; + float rotationDelta; + float rotationTotal; + float size[2]; + float scale; + }; + + const struct FxElemVisStateSample + { + FxElemVisualState base; + FxElemVisualState amplitude; + }; + + struct FxElemDef + { + int flags; + FxSpawnDef spawn; + FxFloatRange spawnRange; + FxFloatRange fadeInRange; + FxFloatRange fadeOutRange; + float spawnFrustumCullRadius; + FxIntRange spawnDelayMsec; + FxIntRange lifeSpanMsec; + FxFloatRange spawnOrigin[3]; + FxFloatRange spawnOffsetRadius; + FxFloatRange spawnOffsetHeight; + FxFloatRange spawnAngles[3]; + FxFloatRange angularVelocity[3]; + FxFloatRange initialRotation; + FxFloatRange gravity; + FxFloatRange reflectionFactor; + FxElemAtlas atlas; + char elemType; + char visualCount; + char velIntervalCount; + char visStateIntervalCount; + FxElemVelStateSample* velSamples; + FxElemVisStateSample* visSamples; + FxElemDefVisuals visuals; + Bounds collBounds; + FxEffectDefRef* effectOnImpact; + FxEffectDefRef* effectOnDeath; + FxEffectDefRef* effectEmitted; + FxFloatRange emitDist; + FxFloatRange emitDistVariance; + FxElemExtendedDefPtr extended; + char sortOrder; + char lightingFrac; + char useItemClip; + char fadeInfo; + }; + + struct FxEffectDef + { + const char* name; + int flags; + int totalSize; + int msecLoopingLife; + int elemDefCountLooping; + int elemDefCountOneShot; + int elemDefCountEmission; + FxElemDef* elemDefs; + }; + + // GfxWorld +#pragma pack(push, 4) + struct GfxLightImage + { + GfxImage* image; + char samplerState; + }; +#pragma pack(pop) + + struct GfxLightDef + { + const char* name; + GfxLightImage attenuation; + int lmapLookupStart; + }; + + struct GfxSky + { + int skySurfCount; + std::uint32_t* skyStartSurfs; + GfxImage* skyImage; + char skySamplerState; + char pad[3]; + }; + + struct GfxWorldDpvsPlanes + { + int cellCount; + cplane_s* planes; + unsigned __int16* nodes; + unsigned char* sceneEntCellBits; //Size = cellCount << 11 + }; + + struct GfxAabbTree + { + union + { + Bounds bounds; + + struct + { + float mins[3]; // 12 + float maxs[3]; // 12 + }; + }; + + int unkn; + unsigned __int16 childCount; // 2 + unsigned __int16 surfaceCount; // 2 + unsigned __int16 startSurfIndex; // 2 + unsigned __int16 smodelIndexCount; // 2 + unsigned __int16* smodelIndexes; // 4 + int childrenOffset; // 4 + }; // Size: 0x2C + + struct GfxCellTree + { + // Best struct ever + GfxAabbTree* aabbtree; + }; + + struct GfxPortalWritable + { + bool isQueued; + bool isAncestor; + char recursionDepth; + char hullPointCount; + //float(*hullPoints)[2]; + }; + + struct DpvsPlane + { + float coeffs[4]; + char side[3]; + }; + + struct GfxPortal // Needs to be investigated + { + GfxPortalWritable writable; // 4 + DpvsPlane plane; // 20 + int unknown1; + float (*vertices)[3]; + short unknown2; + char vertexCount; + //char unknown2[2]; + float hullAxis[2][3]; + }; + +#pragma pack(push, 4) + struct GfxCell + { + union + { + Bounds bounds; + + struct + { + float mins[3]; + float maxs[3]; + }; + }; + + int portalCount; + GfxPortal* portals; + char reflectionProbeCount; + char* reflectionProbes; + }; +#pragma pack(pop) + + struct GfxCell_IW5 + { + float mins[3]; + float maxs[3]; + int portalCount; + GfxPortal* portals; + char reflectionProbeCount; + char* reflectionProbes; + char reflectionProbeReferenceCount; + char* reflectionProbeReferences; + }; + + struct GfxReflectionProbe + { + float offset[3]; + }; + + typedef char GfxTexture[0x04]; + + struct GfxLightmapArray + { + GfxImage* primary; + GfxImage* secondary; + }; + + struct GfxWorldVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + float texCoord[2]; + float lmapCoord[2]; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct GfxWorldVertexData + { + GfxWorldVertex* vertices; + void* worldVb; // D3DVertexBuffer + }; + + struct GfxWorldVertexLayerData + { + char* data; + void* layerVb; // D3DVertexBuffer + }; + + struct GfxWorldDraw + { + union + { + char _portpad0[16]; + + struct + { + unsigned int reflectionProbeCount; // 4 + GfxImage* * reflectionImages; // 4 + GfxReflectionProbe* reflectionProbes; // 4 + GfxTexture* reflectionProbeTextures; //Count = reflectionProbeCount // 4 + }; + }; + + union + { + char _portpad1[56]; + + struct + { + int lightmapCount; // 4 + GfxLightmapArray* lightmaps; // 4 + GfxTexture* lightmapPrimaryTextures; //Count = lightmapCount // 4 + GfxTexture* lightmapSecondaryTextures; //Count = lightmapCount // 4 + GfxImage* skyImage; // 4 + GfxImage* outdoorImage; // 4 + unsigned int vertexCount; // 4 + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + int indexCount; + unsigned __int16* indices; + }; + }; + }; + + struct GfxWorldDraw_IW5 + { + union + { + char _portpad0[16]; + + struct + { + unsigned int reflectionProbeCount; // 4 + GfxImage* * reflectionImages; // 4 + GfxReflectionProbe* reflectionProbes; // 4 + GfxTexture* reflectionProbeTextures; //Count = reflectionProbeCount // 4 + }; + }; + + char cancer[12]; + + union + { + char _portpad1[56]; + + struct + { + int lightmapCount; // 4 + GfxLightmapArray* lightmaps; // 4 + GfxTexture* lightmapPrimaryTextures; //Count = lightmapCount // 4 + GfxTexture* lightmapSecondaryTextures; //Count = lightmapCount // 4 + GfxImage* skyImage; // 4 + GfxImage* outdoorImage; // 4 + unsigned int vertexCount; // 4 + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + int indexCount; + unsigned __int16* indices; + }; + }; + }; + + struct GfxLightGridEntry + { + unsigned __int16 colorsIndex; + char primaryLightIndex; + char needsTrace; + }; + + struct GfxLightGridColors + { + char rgb[56][3]; + }; + + struct GfxLightGrid + { + bool hasLightRegions; // 4 + unsigned int sunPrimaryLightIndex; // 4 + unsigned __int16 mins[3]; // 6 + unsigned __int16 maxs[3]; // 6 + unsigned int rowAxis; // 4 + unsigned int colAxis; // 4 + unsigned __int16* rowDataStart; + // Size: (varGfxLightGrid->maxs[varGfxLightGrid->rowAxis] - varGfxLightGrid->mins[varGfxLightGrid->rowAxis] + 1) * 2 + unsigned int rawRowDataSize; + char* rawRowData; + unsigned int entryCount; + GfxLightGridEntry* entries; + unsigned int colorCount; + GfxLightGridColors* colors; + }; + + struct GfxBrushModelWritable + { + union + { + Bounds bounds; + + struct + { + float mins[3]; + float maxs[3]; + }; + }; + + float mip1radiusSq; + }; + + struct GfxBrushModel + { + GfxBrushModelWritable writable; + + union + { + Bounds bounds; + + struct + { + float mins[3]; + float maxs[3]; + }; + }; + + unsigned int surfaceCount; + unsigned int startSurfIndex; + }; + + struct MaterialMemory + { + Material* material; + int memory; + }; + + struct sunflare_t + { + bool hasValidData; + Material* spriteMaterial; + Material* flareMaterial; + float spriteSize; + float flareMinSize; + float flareMinDot; + float flareMaxSize; + float flareMaxDot; + float flareMaxAlpha; + int flareFadeInTime; + int flareFadeOutTime; + float blindMinDot; + float blindMaxDot; + float blindMaxDarken; + int blindFadeInTime; + int blindFadeOutTime; + float glareMinDot; + float glareMaxDot; + float glareMaxLighten; + int glareFadeInTime; + int glareFadeOutTime; + float sunFxPosition[3]; + }; + + struct XModelDrawInfo + { + unsigned __int16 lod; + unsigned __int16 surfId; + }; + + struct GfxSceneDynModel + { + XModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct BModelDrawInfo + { + unsigned __int16 surfId; + }; + + struct GfxSceneDynBrush + { + BModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct GfxShadowGeometry + { + unsigned __int16 surfaceCount; + unsigned __int16 smodelCount; + unsigned __int16* sortedSurfIndex; + unsigned __int16* smodelIndex; + }; + + struct GfxLightRegionAxis + { + float dir[3]; + float midPoint; + float halfSize; + }; + + struct GfxLightRegionHull + { + float kdopMidPoint[9]; + float kdopHalfSize[9]; + unsigned int axisCount; + GfxLightRegionAxis* axis; + }; + + struct GfxLightRegion + { + unsigned int hullCount; + GfxLightRegionHull* hulls; + }; + + struct GfxStaticModelInst + { + float mins[3]; + float maxs[3]; + float lightingOrigin[3]; + }; + + struct srfTriangles_t + { + int vertexLayerData; + int firstVertex; + unsigned __int16 vertexCount; + unsigned __int16 triCount; + int baseIndex; + }; + + struct GfxSurface + { + srfTriangles_t tris; + Material* material; + char lightmapIndex; + char reflectionProbeIndex; + char primaryLightIndex; + bool castsSunShadow; + }; + + struct GfxCullGroup + { + float mins[3]; + float maxs[3]; + //int surfaceCount; + //int startSurfIndex; + }; + + struct GfxDrawSurfFields + { + __int64 _bf0; + }; + + union GfxDrawSurf + { + GfxDrawSurfFields fields; + unsigned __int64 packed; + }; + +#pragma pack(push, 4) + struct GfxPackedPlacement + { + float origin[3]; + float axis[3][3]; + float scale; + }; + + struct GfxStaticModelDrawInst + { + GfxPackedPlacement placement; + XModel* model; + unsigned __int16 smodelCacheIndex[4]; + float cullDist; + char reflectionProbeIndex; + char primaryLightIndex; + unsigned __int16 lightingHandle; + char flags; + }; + + struct GfxWorldDpvsDynamic + { + unsigned int dynEntClientWordCount[2]; + unsigned int dynEntClientCount[2]; + unsigned int* dynEntCellBits[2]; + char* dynEntVisData[2][3]; + }; + +#pragma pack(pop) + + struct GfxWorldDpvsStatic + { + unsigned int smodelCount; + unsigned int staticSurfaceCount; + unsigned int staticSurfaceCountNoDecal; + unsigned int litOpaqueSurfsBegin; + unsigned int litOpaqueSurfsEnd; + unsigned int litTransSurfsBegin; + unsigned int litTransSurfsEnd; + unsigned int shadowCasterSurfsBegin; + unsigned int shadowCasterSurfsEnd; + unsigned int emissiveSurfsBegin; + unsigned int emissiveSurfsEnd; + unsigned int smodelVisDataCount; + unsigned int surfaceVisDataCount; + char* smodelVisData[3]; + char* surfaceVisData[3]; + unsigned __int16* sortedSurfIndex; + GfxStaticModelInst* smodelInsts; + GfxSurface* surfaces; + GfxCullGroup* cullGroups; + GfxStaticModelDrawInst* smodelDrawInsts; + GfxDrawSurf* surfaceMaterials; + unsigned int* surfaceCastsSunShadow; + volatile int usageCount; + }; + + struct GfxHeroLight + { + char type; + char pad[3]; + float color[3]; + float dir[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + int exponent; + }; + + struct GfxCellTreeCount + { + int aabbTreeCount; + }; + +#pragma pack(push, 4) + struct GfxWorld + { + const char* name; // 4 + const char* baseName; // 4 + int planeCount; // 4 + int nodeCount; // 4 // = 16 + int indexCount; // 4 + unsigned int skyCount; // 4 + GfxSky* skies; // 4 + int sunPrimaryLightIndex; // 4 // = 32 + int primaryLightCount; // 4 + int primaryLightEnvCount; // 4 + char unknown1[12]; // 16 // = 56 // Sortkeys. Don't know which ones though + GfxWorldDpvsPlanes dpvsPlanes; // 16 + GfxCellTreeCount* aabbTreeCounts; // Size: 4 * dpvsPlanes.cellCount // 4 + GfxCellTree* aabbTree; // 4 + GfxCell* cells; // 4 // = 80 + GfxWorldDraw worldDraw; // 72 + GfxLightGrid lightGrid; // 56 // = 208 + int modelCount; // 4 + GfxBrushModel* models; // 4 // = 216 + union + { + Bounds bounds; + + struct + { + float mins[3]; // 12 + float maxs[3]; // 12 + }; + }; + + unsigned int checksum; // 4 + int materialMemoryCount; // 4 // = 248 + MaterialMemory* materialMemory; // 4 + sunflare_t sun; // 96 // = 348 + float outdoorLookupMatrix[4][4]; // 64 + GfxImage* outdoorImage; // 4 // = 416 + unsigned int* cellCasterBits[2]; // 8 + GfxSceneDynModel* sceneDynModel; // 4 + GfxSceneDynBrush* sceneDynBrush; // 4 // = 432 + unsigned char* primaryLightEntityShadowVis; + unsigned int* primaryLightDynEntShadowVis[2]; + char* primaryLightForModelDynEnt; + GfxShadowGeometry* shadowGeom; + GfxLightRegion* lightRegion; + GfxWorldDpvsStatic dpvs; + GfxWorldDpvsDynamic dpvsDyn; + unsigned int mapVtxChecksum; + unsigned int heroLightCount; + GfxHeroLight* heroLights; + char fogTypesAllowed; + char pad2[3]; + }; +#pragma pack(pop) + +#pragma pack(push, 4) + struct cStaticModel_s + { + XModel* xmodel; + float origin[3]; + float invScaledAxis[3][3]; + float absmin[3]; + float absmax[3]; + }; + + struct dmaterial_t + { + char* material; + int surfaceFlags; + int contentFlags; + }; + + struct cNode_t + { + cplane_s* plane; + __int16 children[2]; + }; + + struct cLeaf_t + { + unsigned __int16 firstCollAabbIndex; // + 0 + unsigned __int16 collAabbCount; // + 2 + int brushContents; // + 6 + int terrainContents; // + 10 + float mins[3]; // + 22 + float maxs[3]; // + 34 + int leafBrushNode; // + 38 + }; + + struct cLeafBrushNodeLeaf_t + { + unsigned __int16* brushes; + }; + + struct cLeafBrushNodeChildren_t + { + unsigned __int16 childOffset[6]; + }; + + union cLeafBrushNodeData_t + { + cLeafBrushNodeLeaf_t leaf; + cLeafBrushNodeChildren_t children; + }; + + struct cLeafBrushNode_s + { + char axis; + short leafBrushCount; + int contents; + cLeafBrushNodeData_t data; + }; + + struct CollisionBorder + { + float distEq[3]; + float zBase; + float zSlope; + float start; + float length; + }; + + struct CollisionPartition + { + char triCount; + char borderCount; + int firstTri; + CollisionBorder* borders; + }; + + union CollisionAabbTreeIndex + { + int firstChildIndex; + int partitionIndex; + }; + + struct CollisionAabbTree + { + float origin[3]; + unsigned __int16 materialIndex; + unsigned __int16 childCount; + float halfSize[3]; + CollisionAabbTreeIndex u; + }; + + enum DynEntityType + { + DYNENT_TYPE_INVALID = 0x0, + DYNENT_TYPE_CLUTTER = 0x1, + DYNENT_TYPE_DESTRUCT = 0x2, + DYNENT_TYPE_COUNT = 0x3, + }; + + struct GfxPlacement + { + float quat[4]; + float origin[3]; + }; + + struct DynEntityDef + { + DynEntityType type; + GfxPlacement pose; + XModel* xModel; + unsigned __int16 brushModel; + unsigned __int16 physicsBrushModel; + void* destroyFx; // FxEffectDef + PhysPreset* physPreset; + int health; + PhysMass mass; + //char *unknown; + }; + + struct DynEntityPose + { + GfxPlacement pose; + float radius; + }; + + struct DynEntityClient + { + int physObjId; + unsigned __int16 flags; + unsigned __int16 lightingHandle; + int health; + }; + + struct DynEntityColl + { + unsigned __int16 sector; + unsigned __int16 nextEntInSector; + float linkMins[2]; + float linkMaxs[2]; + }; + + struct unknownInternalClipMapStruct1 + { + int planeCount; + cplane_s* planes; + unsigned int numMaterials; + dmaterial_t* materials; + unsigned int numBrushSides; + cbrushside_t* brushsides; + unsigned int numBrushEdges; + cbrushedge_t* brushEdges; + unsigned int leafbrushNodesCount; + cLeafBrushNode_s* leafbrushNodes; + unsigned int numLeafBrushes; + unsigned __int16* leafbrushes; + unsigned __int16 numBrushes; + cbrush_t* brushes; + char* unknown1; //Size = ((numBrushes << 1) + numBrushes) << 3 + unsigned int* leafsurfaces; //Count = numBrushes + }; + + struct unknownInternalClipMapStruct2 + { + char* unknownString; + char unknown[0x10]; + }; + + struct unknownInternalClipMapStruct3 + { + char _pad[28]; + unknownInternalClipMapStruct1* unkArrayPtr; + char _pad2[40]; + }; + + struct cmodel_t + { + union + { + char _portpad0[28]; + + struct + { + Bounds bounds; + float radius; + }; + }; + + union + { + char _portpad1[40]; + + struct + { + cLeaf_t leaf; + }; + }; + }; + + struct SModelAabbNode + { + Bounds bounds; + short firstChild; + short childCount; + }; + + struct DynEntityDef_IW5 + { + DynEntityType type; + GfxPlacement pose; + XModel* xModel; + unsigned __int16 brushModel; + unsigned __int16 physicsBrushModel; + void* destroyFx; // FxEffectDef + PhysPreset* physPreset; + int health; + void* hinge; + PhysMass mass; + }; + + struct cmodel_t_IW5 + { + Bounds bounds; + float radius; + void* info; + cLeaf_t leaf; + }; + + struct clipMap_t + { + const char* name; // 4 + bool isInUse; // 4 + int numCPlanes; // +8 + cplane_s* cPlanes; // sizeof 20, +12 + int numStaticModels; // +16 + cStaticModel_s* staticModelList; // sizeof 76, +20 + int numMaterials; // +24 + dmaterial_t* materials; // sizeof 12 with a string (possibly name?), +28 + int numCBrushSides; // +32 + cbrushside_t* cBrushSides; // sizeof 8, +36 + int numCBrushEdges; // +40 NOT USED IN T5 + cbrushedge_t* cBrushEdges; // +44 NOT USED IN T5 + int numCNodes; // +48 + cNode_t* cNodes; // sizeof 8, +52 + int numCLeaf; // +56 + cLeaf_t* cLeaf; // +60 + int numCLeafBrushNodes; // +64 + cLeafBrushNode_s* cLeafBrushNodes; // +68 + int numLeafBrushes; // +72 + short* leafBrushes; // +76 + int numLeafSurfaces; // +80 + int* leafSurfaces; // +84 + int numVerts; // +88 + VecInternal<3>* verts; // +92 + int numTriIndices; // +96 + short* triIndices; // +100 + char* triEdgeIsWalkable; // +104 + int numCollisionBorders; // +108 + CollisionBorder* collisionBorders; // sizeof 28, +112 + int numCollisionPartitions; // +116 + CollisionPartition* collisionPartitions; // sizeof 12, +120 + int numCollisionAABBTrees; // +124 + CollisionAabbTree* collisionAABBTrees; // sizeof 32, +128 + int numCModels; // +132 + cmodel_t* cModels; // sizeof 68, +136 + short numBrushes; // +140 + short pad2; // +142 + cbrush_t* brushes; // sizeof 36, +144 + Bounds* brushBounds; // same count as cBrushes, +148 + int* brushContents; // same count as cBrushes, +152 + MapEnts* mapEnts; // +156 + short smodelNodeCount; // +160 + short pad3; + SModelAabbNode* smodelNodes; // +164 + unsigned __int16 dynEntCount[2]; + DynEntityDef* dynEntDefList[2]; + DynEntityPose* dynEntPoseList[2]; + DynEntityClient* dynEntClientList[2]; + DynEntityColl* dynEntCollList[2]; + unsigned int checksum; + char unknown5[48]; + }; // +256 +#pragma pack(pop) + + union XAssetHeader + { + RawFile* rawfile; + VertexDecl* vertexdecl; + PixelShader* pixelshader; + VertexShader* vertexshader; + MaterialTechniqueSet* techset; + GfxImage* gfximage; + GfxImage* image; + Material* material; + PhysPreset* physpreset; + PhysCollmap* physcollmap; + PhysCollmap* phys_collmap; + XAnimParts* xanimparts; + XAnimParts* xanim; + ModelSurface* xsurface; + ModelSurface* xmodelsurfs; + clipMap_t* clipmap; + clipMap_t* col_map_mp; + GfxWorld* gfxworld; + GfxWorld* gfx_map; + GameWorldMp* gameworldmp; + GameWorldSp* gameworldsp; + GameWorldMp* game_map_mp; + GameWorldSp* game_map_sp; + FxWorld* fxworld; + FxWorld* fx_map; + MapEnts* mapents; + MapEnts* map_ents; + XModel* xmodel; + StringTable* stringtable; + ComWorld* comworld; + ComWorld* com_map; + LocalizeEntry* localize; + SndCurve* soundcurve; + SndCurve* sndcurve; + TracerDef* tracer; + //leaderboarddef* leaderboard; + Font_s* font; + WeaponCompleteDef* weapon; + FxEffectDef* fx; + snd_alias_list_t* sound; + LoadedSound* loadedsound; + LoadedSound* loaded_sound; + //structureddatadefset* structureddatadef; + //menudef_t* menu; + GfxLightDef* lightdef; + }; + + enum MaterialTechniqueType + { + TECHNIQUE_DEPTH_PREPASS = 0x0, + TECHNIQUE_BUILD_FLOAT_Z = 0x1, + TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2, + TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3, + TECHNIQUE_UNLIT = 0x4, + TECHNIQUE_EMISSIVE = 0x5, + TECHNIQUE_EMISSIVE_DFOG = 0x6, + TECHNIQUE_EMISSIVE_SHADOW = 0x7, + TECHNIQUE_EMISSIVE_SHADOW_DFOG = 0x8, + TECHNIQUE_LIT_BEGIN = 0x9, + TECHNIQUE_LIT = 0x9, + TECHNIQUE_LIT_DFOG = 0xA, + TECHNIQUE_LIT_SUN = 0xB, + TECHNIQUE_LIT_SUN_DFOG = 0xC, + TECHNIQUE_LIT_SUN_SHADOW = 0xD, + TECHNIQUE_LIT_SUN_SHADOW_DFOG = 0xE, + TECHNIQUE_LIT_SPOT = 0xF, + TECHNIQUE_LIT_SPOT_DFOG = 0x10, + TECHNIQUE_LIT_SPOT_SHADOW = 0x11, + TECHNIQUE_LIT_SPOT_SHADOW_DFOG = 0x12, + TECHNIQUE_LIT_OMNI = 0x13, + TECHNIQUE_LIT_OMNI_DFOG = 0x14, + TECHNIQUE_LIT_OMNI_SHADOW = 0x15, + TECHNIQUE_LIT_OMNI_SHADOW_DFOG = 0x16, + TECHNIQUE_LIT_INSTANCED = 0x17, + TECHNIQUE_LIT_INSTANCED_DFOG = 0x18, + TECHNIQUE_LIT_INSTANCED_SUN = 0x19, + TECHNIQUE_LIT_INSTANCED_SUN_DFOG = 0x1A, + TECHNIQUE_LIT_INSTANCED_SUN_SHADOW = 0x1B, + TECHNIQUE_LIT_INSTANCED_SUN_SHADOW_DFOG = 0x1C, + TECHNIQUE_LIT_INSTANCED_SPOT = 0x1D, + TECHNIQUE_LIT_INSTANCED_SPOT_DFOG = 0x1E, + TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW = 0x1F, + TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW_DFOG = 0x20, + TECHNIQUE_LIT_INSTANCED_OMNI = 0x21, + TECHNIQUE_LIT_INSTANCED_OMNI_DFOG = 0x22, + TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW = 0x23, + TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW_DFOG = 0x24, + TECHNIQUE_LIT_END = 0x25, + TECHNIQUE_LIGHT_SPOT = 0x25, + TECHNIQUE_LIGHT_OMNI = 0x26, + TECHNIQUE_LIGHT_SPOT_SHADOW = 0x27, + TECHNIQUE_FAKELIGHT_NORMAL = 0x28, + TECHNIQUE_FAKELIGHT_VIEW = 0x29, + TECHNIQUE_SUNLIGHT_PREVIEW = 0x2A, + TECHNIQUE_CASE_TEXTURE = 0x2B, + TECHNIQUE_WIREFRAME_SOLID = 0x2C, + TECHNIQUE_WIREFRAME_SHADED = 0x2D, + TECHNIQUE_DEBUG_BUMPMAP = 0x2E, + TECHNIQUE_DEBUG_BUMPMAP_INSTANCED = 0x2F, + TECHNIQUE_COUNT = 0x30, + TECHNIQUE_TOTAL_COUNT = 0x31, + TECHNIQUE_NONE = 0x32, + }; + + enum $949C82572B6A70952C455E749D3B3D56 + { + CONST_SRC_CODE_MAYBE_DIRTY_PS_BEGIN = 0x0, + CONST_SRC_CODE_LIGHT_POSITION = 0x0, + CONST_SRC_CODE_LIGHT_DIFFUSE = 0x1, + CONST_SRC_CODE_LIGHT_SPECULAR = 0x2, + CONST_SRC_CODE_LIGHT_SPOTDIR = 0x3, + CONST_SRC_CODE_LIGHT_SPOTFACTORS = 0x4, + CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT = 0x5, + CONST_SRC_CODE_PARTICLE_CLOUD_COLOR = 0x6, + CONST_SRC_CODE_GAMETIME = 0x7, + CONST_SRC_CODE_MAYBE_DIRTY_PS_END = 0x8, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_BEGIN = 0x8, + CONST_SRC_CODE_PIXEL_COST_FRACS = 0x8, + CONST_SRC_CODE_PIXEL_COST_DECODE = 0x9, + CONST_SRC_CODE_FILTER_TAP_0 = 0xA, + CONST_SRC_CODE_FILTER_TAP_1 = 0xB, + CONST_SRC_CODE_FILTER_TAP_2 = 0xC, + CONST_SRC_CODE_FILTER_TAP_3 = 0xD, + CONST_SRC_CODE_FILTER_TAP_4 = 0xE, + CONST_SRC_CODE_FILTER_TAP_5 = 0xF, + CONST_SRC_CODE_FILTER_TAP_6 = 0x10, + CONST_SRC_CODE_FILTER_TAP_7 = 0x11, + CONST_SRC_CODE_COLOR_MATRIX_R = 0x12, + CONST_SRC_CODE_COLOR_MATRIX_G = 0x13, + CONST_SRC_CODE_COLOR_MATRIX_B = 0x14, + CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET = 0x15, + CONST_SRC_CODE_RENDER_TARGET_SIZE = 0x16, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_END = 0x17, + CONST_SRC_CODE_FIXED_PS_BEGIN = 0x17, + CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR = 0x17, + CONST_SRC_CODE_DOF_EQUATION_SCENE = 0x18, + CONST_SRC_CODE_DOF_LERP_SCALE = 0x19, + CONST_SRC_CODE_DOF_LERP_BIAS = 0x1A, + CONST_SRC_CODE_DOF_ROW_DELTA = 0x1B, + CONST_SRC_CODE_MOTION_MATRIX_X = 0x1C, + CONST_SRC_CODE_MOTION_MATRIX_Y = 0x1D, + CONST_SRC_CODE_MOTION_MATRIX_W = 0x1E, + CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION = 0x1F, + CONST_SRC_CODE_SHADOWMAP_SCALE = 0x20, + CONST_SRC_CODE_ZNEAR = 0x21, + CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE = 0x22, + CONST_SRC_CODE_DEBUG_BUMPMAP = 0x23, + CONST_SRC_CODE_MATERIAL_COLOR = 0x24, + CONST_SRC_CODE_FOG = 0x25, + CONST_SRC_CODE_FOG_COLOR_LINEAR = 0x26, + CONST_SRC_CODE_FOG_COLOR_GAMMA = 0x27, + CONST_SRC_CODE_FOG_SUN_CONSTS = 0x28, + CONST_SRC_CODE_FOG_SUN_COLOR_LINEAR = 0x29, + CONST_SRC_CODE_FOG_SUN_COLOR_GAMMA = 0x2A, + CONST_SRC_CODE_FOG_SUN_DIR = 0x2B, + CONST_SRC_CODE_GLOW_SETUP = 0x2C, + CONST_SRC_CODE_GLOW_APPLY = 0x2D, + CONST_SRC_CODE_COLOR_BIAS = 0x2E, + CONST_SRC_CODE_COLOR_TINT_BASE = 0x2F, + CONST_SRC_CODE_COLOR_TINT_DELTA = 0x30, + CONST_SRC_CODE_COLOR_TINT_QUADRATIC_DELTA = 0x31, + CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS = 0x32, + CONST_SRC_CODE_ENVMAP_PARMS = 0x33, + CONST_SRC_CODE_SUN_SHADOWMAP_PIXEL_ADJUST = 0x34, + CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST = 0x35, + CONST_SRC_CODE_COMPOSITE_FX_DISTORTION = 0x36, + CONST_SRC_CODE_POSTFX_FADE_EFFECT = 0x37, + CONST_SRC_CODE_VIEWPORT_DIMENSIONS = 0x38, + CONST_SRC_CODE_FRAMEBUFFER_READ = 0x39, + CONST_SRC_CODE_FIXED_PS_END = 0x3A, + CONST_SRC_CODE_NON_PS_BEGIN = 0x3A, + CONST_SRC_CODE_BASE_LIGHTING_COORDS = 0x3A, + CONST_SRC_CODE_LIGHT_PROBE_AMBIENT = 0x3B, + CONST_SRC_CODE_NEARPLANE_ORG = 0x3C, + CONST_SRC_CODE_NEARPLANE_DX = 0x3D, + CONST_SRC_CODE_NEARPLANE_DY = 0x3E, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE = 0x3F, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET = 0x40, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX0 = 0x41, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX1 = 0x42, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX2 = 0x43, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR0 = 0x44, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR1 = 0x45, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR2 = 0x46, + CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM0 = 0x47, + CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM1 = 0x48, + CONST_SRC_CODE_DEPTH_FROM_CLIP = 0x49, + CONST_SRC_CODE_CODE_MESH_ARG_0 = 0x4A, + CONST_SRC_CODE_CODE_MESH_ARG_1 = 0x4B, + CONST_SRC_CODE_CODE_MESH_ARG_LAST = 0x4B, + CONST_SRC_CODE_NON_PS_END = 0x4C, + CONST_SRC_CODE_COUNT_FLOAT4 = 0x4C, + CONST_SRC_FIRST_CODE_MATRIX = 0x4C, + CONST_SRC_CODE_VIEW_MATRIX = 0x4C, + CONST_SRC_CODE_INVERSE_VIEW_MATRIX = 0x4D, + CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX = 0x4E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX = 0x4F, + CONST_SRC_CODE_PROJECTION_MATRIX = 0x50, + CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX = 0x51, + CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX = 0x52, + CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX = 0x53, + CONST_SRC_CODE_VIEW_PROJECTION_MATRIX = 0x54, + CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX = 0x55, + CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x56, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x57, + CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX = 0x58, + CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX = 0x59, + CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x5A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x5B, + CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5C, + CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5D, + CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5F, + CONST_SRC_CODE_WORLD_MATRIX0 = 0x60, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX0 = 0x61, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0 = 0x62, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX0 = 0x63, + CONST_SRC_CODE_WORLD_VIEW_MATRIX0 = 0x64, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX0 = 0x65, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX0 = 0x66, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX0 = 0x67, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x68, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x69, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x6A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x6B, + CONST_SRC_CODE_WORLD_MATRIX1 = 0x6C, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX1 = 0x6D, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX1 = 0x6E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX1 = 0x6F, + CONST_SRC_CODE_WORLD_VIEW_MATRIX1 = 0x70, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX1 = 0x71, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX1 = 0x72, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX1 = 0x73, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x74, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x75, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x76, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x77, + CONST_SRC_CODE_WORLD_MATRIX2 = 0x78, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX2 = 0x79, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX2 = 0x7A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX2 = 0x7B, + CONST_SRC_CODE_WORLD_VIEW_MATRIX2 = 0x7C, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX2 = 0x7D, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX2 = 0x7E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX2 = 0x7F, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x80, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x81, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x82, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x83, + CONST_SRC_TOTAL_COUNT = 0x84, + CONST_SRC_NONE = 0x85, + }; +} + +namespace IW3 +{ + enum MaterialTechniqueType + { + TECHNIQUE_DEPTH_PREPASS = 0x0, + TECHNIQUE_BUILD_FLOAT_Z = 0x1, + TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2, + TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3, + TECHNIQUE_UNLIT = 0x4, + TECHNIQUE_EMISSIVE = 0x5, + TECHNIQUE_EMISSIVE_SHADOW = 0x6, + TECHNIQUE_LIT_BEGIN = 0x7, + TECHNIQUE_LIT = 0x7, + TECHNIQUE_LIT_SUN = 0x8, + TECHNIQUE_LIT_SUN_SHADOW = 0x9, + TECHNIQUE_LIT_SPOT = 0xA, + TECHNIQUE_LIT_SPOT_SHADOW = 0xB, + TECHNIQUE_LIT_OMNI = 0xC, + TECHNIQUE_LIT_OMNI_SHADOW = 0xD, + TECHNIQUE_LIT_INSTANCED = 0xE, + TECHNIQUE_LIT_INSTANCED_SUN = 0xF, + TECHNIQUE_LIT_INSTANCED_SUN_SHADOW = 0x10, + TECHNIQUE_LIT_INSTANCED_SPOT = 0x11, + TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW = 0x12, + TECHNIQUE_LIT_INSTANCED_OMNI = 0x13, + TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW = 0x14, + TECHNIQUE_LIT_END = 0x15, + TECHNIQUE_LIGHT_SPOT = 0x15, + TECHNIQUE_LIGHT_OMNI = 0x16, + TECHNIQUE_LIGHT_SPOT_SHADOW = 0x17, + TECHNIQUE_FAKELIGHT_NORMAL = 0x18, + TECHNIQUE_FAKELIGHT_VIEW = 0x19, + TECHNIQUE_SUNLIGHT_PREVIEW = 0x1A, + TECHNIQUE_CASE_TEXTURE = 0x1B, + TECHNIQUE_WIREFRAME_SOLID = 0x1C, + TECHNIQUE_WIREFRAME_SHADED = 0x1D, + TECHNIQUE_SHADOWCOOKIE_CASTER = 0x1E, + TECHNIQUE_SHADOWCOOKIE_RECEIVER = 0x1F, + TECHNIQUE_DEBUG_BUMPMAP = 0x20, + TECHNIQUE_DEBUG_BUMPMAP_INSTANCED = 0x21, + TECHNIQUE_COUNT = 0x22, + TECHNIQUE_TOTAL_COUNT = 0x23, + TECHNIQUE_NONE = 0x24, + }; + + enum $D8CAED6A392807092A83572483F14017 + { + CONST_SRC_CODE_MAYBE_DIRTY_PS_BEGIN = 0x0, + CONST_SRC_CODE_LIGHT_POSITION = 0x0, + CONST_SRC_CODE_LIGHT_DIFFUSE = 0x1, + CONST_SRC_CODE_LIGHT_SPECULAR = 0x2, + CONST_SRC_CODE_LIGHT_SPOTDIR = 0x3, + CONST_SRC_CODE_LIGHT_SPOTFACTORS = 0x4, + CONST_SRC_CODE_NEARPLANE_ORG = 0x5, + CONST_SRC_CODE_NEARPLANE_DX = 0x6, + CONST_SRC_CODE_NEARPLANE_DY = 0x7, + CONST_SRC_CODE_SHADOW_PARMS = 0x8, + CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET = 0x9, + CONST_SRC_CODE_RENDER_TARGET_SIZE = 0xA, + CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT = 0xB, + CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR = 0xC, + CONST_SRC_CODE_DOF_EQUATION_SCENE = 0xD, + CONST_SRC_CODE_DOF_LERP_SCALE = 0xE, + CONST_SRC_CODE_DOF_LERP_BIAS = 0xF, + CONST_SRC_CODE_DOF_ROW_DELTA = 0x10, + CONST_SRC_CODE_PARTICLE_CLOUD_COLOR = 0x11, + CONST_SRC_CODE_GAMETIME = 0x12, + CONST_SRC_CODE_MAYBE_DIRTY_PS_END = 0x13, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_BEGIN = 0x13, + CONST_SRC_CODE_PIXEL_COST_FRACS = 0x13, + CONST_SRC_CODE_PIXEL_COST_DECODE = 0x14, + CONST_SRC_CODE_FILTER_TAP_0 = 0x15, + CONST_SRC_CODE_FILTER_TAP_1 = 0x16, + CONST_SRC_CODE_FILTER_TAP_2 = 0x17, + CONST_SRC_CODE_FILTER_TAP_3 = 0x18, + CONST_SRC_CODE_FILTER_TAP_4 = 0x19, + CONST_SRC_CODE_FILTER_TAP_5 = 0x1A, + CONST_SRC_CODE_FILTER_TAP_6 = 0x1B, + CONST_SRC_CODE_FILTER_TAP_7 = 0x1C, + CONST_SRC_CODE_COLOR_MATRIX_R = 0x1D, + CONST_SRC_CODE_COLOR_MATRIX_G = 0x1E, + CONST_SRC_CODE_COLOR_MATRIX_B = 0x1F, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_END = 0x20, + CONST_SRC_CODE_NEVER_DIRTY_PS_BEGIN = 0x20, + CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION = 0x20, + CONST_SRC_CODE_SHADOWMAP_SCALE = 0x21, + CONST_SRC_CODE_ZNEAR = 0x22, + CONST_SRC_CODE_SUN_POSITION = 0x23, + CONST_SRC_CODE_SUN_DIFFUSE = 0x24, + CONST_SRC_CODE_SUN_SPECULAR = 0x25, + CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE = 0x26, + CONST_SRC_CODE_DEBUG_BUMPMAP = 0x27, + CONST_SRC_CODE_MATERIAL_COLOR = 0x28, + CONST_SRC_CODE_FOG = 0x29, + CONST_SRC_CODE_FOG_COLOR = 0x2A, + CONST_SRC_CODE_GLOW_SETUP = 0x2B, + CONST_SRC_CODE_GLOW_APPLY = 0x2C, + CONST_SRC_CODE_COLOR_BIAS = 0x2D, + CONST_SRC_CODE_COLOR_TINT_BASE = 0x2E, + CONST_SRC_CODE_COLOR_TINT_DELTA = 0x2F, + CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS = 0x30, + CONST_SRC_CODE_ENVMAP_PARMS = 0x31, + CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST = 0x32, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE = 0x33, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET = 0x34, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX = 0x35, + CONST_SRC_CODE_DEPTH_FROM_CLIP = 0x36, + CONST_SRC_CODE_CODE_MESH_ARG_0 = 0x37, + CONST_SRC_CODE_CODE_MESH_ARG_1 = 0x38, + CONST_SRC_CODE_CODE_MESH_ARG_LAST = 0x38, + CONST_SRC_CODE_BASE_LIGHTING_COORDS = 0x39, + CONST_SRC_CODE_NEVER_DIRTY_PS_END = 0x3A, + CONST_SRC_CODE_COUNT_FLOAT4 = 0x3A, + CONST_SRC_FIRST_CODE_MATRIX = 0x3A, + CONST_SRC_CODE_WORLD_MATRIX = 0x3A, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX = 0x3B, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX = 0x3C, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX = 0x3D, + CONST_SRC_CODE_VIEW_MATRIX = 0x3E, + CONST_SRC_CODE_INVERSE_VIEW_MATRIX = 0x3F, + CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX = 0x40, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX = 0x41, + CONST_SRC_CODE_PROJECTION_MATRIX = 0x42, + CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX = 0x43, + CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX = 0x44, + CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX = 0x45, + CONST_SRC_CODE_WORLD_VIEW_MATRIX = 0x46, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX = 0x47, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX = 0x48, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX = 0x49, + CONST_SRC_CODE_VIEW_PROJECTION_MATRIX = 0x4A, + CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX = 0x4B, + CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x4C, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x4D, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX = 0x4E, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX = 0x4F, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX = 0x50, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX = 0x51, + CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX = 0x52, + CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX = 0x53, + CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x54, + CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x55, + CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x56, + CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x57, + CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x58, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x59, + CONST_SRC_TOTAL_COUNT = 0x5A, + CONST_SRC_NONE = 0x5B, + }; +} diff --git a/src/CODO/Zone.cpp b/src/CODO/Zone.cpp new file mode 100644 index 0000000..a678650 --- /dev/null +++ b/src/CODO/Zone.cpp @@ -0,0 +1,298 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool::CODO +{ + IAsset* Zone::find_asset(std::int32_t type, const std::string& name) + { + for (auto idx = 0u; idx < m_assets.size(); idx++) + { + if (m_assets[idx]->type() == type && m_assets[idx]->name() == name) + { + return m_assets[idx].get(); + } + } + + return nullptr; + } + + void* Zone::get_asset_pointer(std::int32_t type, const std::string& name) + { + for (auto idx = 0u; idx < m_assets.size(); idx++) + { + if (m_assets[idx]->type() == type && m_assets[idx]->name() == name) + { + auto ptr = reinterpret_cast((3 << 28) | ((this->m_assetbase + (8 * idx) + 4) & 0x0FFFFFFF) + + 1); + // ZONETOOL_INFO("Asset pointer is %u", ptr); + return ptr; + } + } + + return nullptr; + } + + void Zone::add_asset_of_type_by_pointer(std::int32_t type, void* pointer) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW4::Zone::AddAssetOfTypePtr"); +#endif + + // don't add asset if it already exists + for (std::size_t idx = 0; idx < m_assets.size(); idx++) + { + if (m_assets[idx]->type() == type && m_assets[idx]->pointer() == pointer) + { + return; + } + } + +#define DECLARE_ASSET(__type__, __interface__) \ + if (type == __type__) \ + { \ + auto asset = std::make_shared < __interface__ >(); \ + asset->init(pointer, this->m_zonemem); \ + asset->load_depending(this); \ + m_assets.push_back(asset); \ + } + + // declare asset interfaces + // DECLARE_ASSET(xmodelsurfs, IXSurface); + // DECLARE_ASSET(image, IGfxImage); + // DECLARE_ASSET(pixelshader, IPixelShader); + // DECLARE_ASSET(vertexshader, IVertexShader); + // DECLARE_ASSET(vertexdecl, IVertexDecl); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + void Zone::add_asset_of_type(std::int32_t type, const std::string& name) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW4::Zone::AddAssetOfType"); +#endif + + // don't add asset if it already exists + if (get_asset_pointer(type, name)) + { + return; + } + +#define DECLARE_ASSET(__type__, __interface__) \ + if (type == __type__) \ + { \ + auto asset = std::make_shared < __interface__ >(); \ + asset->init(name, this->m_zonemem.get()); \ + asset->load_depending(this); \ + m_assets.push_back(asset); \ + } + + // declare asset interfaces + // DECLARE_ASSET(xanim, IXAnimParts); + // DECLARE_ASSET(pixelshader, IPixelShader); + // DECLARE_ASSET(vertexdecl, IVertexDecl); + // DECLARE_ASSET(vertexshader, IVertexShader); + // DECLARE_ASSET(techset, ITechset); + // DECLARE_ASSET(image, IGfxImage); + // DECLARE_ASSET(material, IMaterial) + // DECLARE_ASSET(xmodelsurfs, IXSurface); + // DECLARE_ASSET(xmodel, IXModel); + // DECLARE_ASSET(map_ents, IMapEnts); + // DECLARE_ASSET(rawfile, IRawFile); + // DECLARE_ASSET(com_map, IComWorld); + // DECLARE_ASSET(font, IFontDef); + DECLARE_ASSET(localize, ILocalizeEntry); + // DECLARE_ASSET(physpreset, IPhysPreset); + // DECLARE_ASSET(phys_collmap, IPhysCollmap); + DECLARE_ASSET(stringtable, IStringTable); + // DECLARE_ASSET(sound, ISound); + // DECLARE_ASSET(loaded_sound, ILoadedSound); + // DECLARE_ASSET(sndcurve, ISoundCurve); + // DECLARE_ASSET(game_map_mp, IGameWorldMp); + // DECLARE_ASSET(game_map_sp, IGameWorldSp); + // DECLARE_ASSET(fx_map, IFxWorld); + // DECLARE_ASSET(tracer, ITracerDef); + // DECLARE_ASSET(gfx_map, IGfxWorld); + // DECLARE_ASSET(col_map_mp, IClipMap); + // DECLARE_ASSET(fx, IFxEffectDef); + // DECLARE_ASSET(lightdef, ILightDef); + //DECLARE_ASSET(weapon, IWeaponDef); //still a work in progress, the CPP file isnt complete + //DECLARE_ASSET(structureddatadef, IStructuredDataDef); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + void Zone::add_asset_of_type(const std::string& type, const std::string& name) + { + std::int32_t itype = m_linker->type_to_int(type); + this->add_asset_of_type(itype, name); + } + + std::int32_t Zone::get_type_by_name(const std::string& type) + { + return m_linker->type_to_int(type); + } + + void Zone::build(ZoneBuffer* buf) + { + auto startTime = GetTickCount64(); + + // make a folder in main, for the map images + std::filesystem::create_directories("main\\" + this->name_ + "\\images"); + + ZONETOOL_INFO("Compiling fastfile \"%s\"...", this->name_.data()); + + constexpr std::size_t num_streams = 8; + XZoneMemory mem; + + std::size_t headersize = sizeof XZoneMemory; + memset(&mem, 0, headersize); + + auto zone = buf->at>(); + + // write zone header + buf->write(&mem); + + std::uintptr_t pad = 0xFFFFFFFF; + std::uintptr_t zero = 0; + + // write asset types to header + for (auto i = 0u; i < m_assets.size(); i++) + { + m_assets[i]->prepare(buf, this->m_zonemem.get()); + } + + // write scriptstring count + std::uint32_t stringcount = buf->scriptstring_count(); + buf->write(&stringcount); + buf->write(stringcount > 0 ? (&pad) : (&zero)); + + // write asset count + std::uint32_t asset_count = m_assets.size(); + buf->write(&asset_count); + buf->write(asset_count > 0 ? (&pad) : (&zero)); + + // push stream + buf->push_stream(3); + START_LOG_STREAM; + + if (stringcount) + { + // write pointer for every scriptstring + for (std::size_t idx = 0; idx < stringcount; idx++) + { + buf->write(&pad); + } + + // write scriptstrings + buf->align(3); + for (std::size_t idx = 0; idx < stringcount; idx++) + { + buf->write_str(buf->get_scriptstring(idx)); + } + } + + buf->pop_stream(); + buf->push_stream(3); + + // align buffer + buf->align(3); + + // set asset ptr base + this->m_assetbase = buf->stream_offset(3); + + // write asset types to header + for (auto i = 0u; i < asset_count; i++) + { + // write asset data to zone + auto type = m_assets[i]->type(); + buf->write(&type); + buf->write(&pad); + } + + // write assets + for (auto& asset : m_assets) + { + // push stream + buf->push_stream(0); + buf->align(3); + + // write asset + asset->write(this, buf); + + // pop stream + buf->pop_stream(); + } + + // pop stream + END_LOG_STREAM; + buf->pop_stream(); + + // update zone header + zone->size = buf->size() - headersize; + zone->externalsize = 0; + + // Update stream data + for (int i = 0; i < num_streams; i++) + { + zone->streams[i] = buf->stream_offset(i); + } + + // Dump zone to disk (DEBUGGING PURPOSES) + buf->save("debug\\" + this->name_ + ".zone"); + + // Compress buffer + auto buf_compressed = buf->compress_zlib(); + + // Generate FF header + auto header = this->m_zonemem->Alloc(); + strcpy(header->header, "IWffu100"); + header->version = 423; + header->allowOnlineUpdate = 0; + + // Encrypt fastfile + // ZoneBuffer encrypted(buf_compressed); + // encrypted.encrypt(); + + // Save fastfile + ZoneBuffer fastfile(buf_compressed.size() + 21); + fastfile.init_streams(1); + fastfile.write_stream(header, 21); + + fastfile.write(buf_compressed.data(), buf_compressed.size()); + + fastfile.save("zone\\english\\" + this->name_ + ".ff"); + + ZONETOOL_INFO("Successfully compiled fastfile \"%s\"!", this->name_.data()); + ZONETOOL_INFO("Compiling took %u msec.", GetTickCount64() - startTime); + + // this->m_linker->UnloadZones(); + } + + Zone::Zone(std::string name, ILinker* linker) + { + currentzone = name; + + this->m_linker = linker; + this->name_ = name; + + this->m_zonemem = std::make_shared(MAX_ZONE_SIZE); + } + + Zone::~Zone() + { + // wipe all assets + m_assets.clear(); + } +} diff --git a/src/CODO/Zone.hpp b/src/CODO/Zone.hpp new file mode 100644 index 0000000..f79f5ca --- /dev/null +++ b/src/CODO/Zone.hpp @@ -0,0 +1,69 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +extern std::string currentzone; + +// #include "Assets/RawFile.hpp" +// #include "Assets/PhysPreset.hpp" +// #include "Assets/WeaponDef.hpp" +// #include "Assets/VertexDecl.hpp" +// #include "Assets/PixelShader.hpp" +// #include "Assets/VertexShader.hpp" +// #include "Assets/Techset.hpp" +// #include "Assets/GfxImage.hpp" +// #include "Assets/Material.hpp" +// #include "Assets/XSurface.hpp" +// #include "Assets/Sound.hpp" +// #include "Assets/LoadedSound.hpp" +// #include "Assets/SoundCurve.hpp" +// #include "Assets/FontDef.hpp" +// #include "Assets/PhysCollmap.hpp" +// #include "Assets/Xmodel.hpp" +// #include "Assets/GameWorldMp.hpp" +// #include "Assets/GameWorldSp.hpp" +// #include "Assets/FxWorld.hpp" +// #include "Assets/MapEnts.hpp" +// #include "Assets/ComWorld.hpp" +// #include "Assets/XAnimParts.hpp" +#include "Assets/StringTable.hpp" +#include "Assets/LocalizeEntry.hpp" +// #include "Assets/TracerDef.hpp" +// #include "Assets/GfxWorld.hpp" +// #include "Assets/ClipMap.hpp" +// #include "Assets/FxEffectDef.hpp" +// #include "Assets/LightDef.hpp" + +namespace ZoneTool::CODO +{ + class Zone : public IZone + { + private: + std::uintptr_t m_assetbase; + std::string name_; + ILinker* m_linker; + std::vector> m_assets; + std::shared_ptr m_zonemem; + + public: + Zone(std::string name, ILinker* linker); + ~Zone(); + + IAsset* find_asset(std::int32_t type, const std::string& name) override; + void* Zone::get_asset_pointer(std::int32_t type, const std::string& name) override; + + void add_asset_of_type_by_pointer(std::int32_t type, void* pointer) override; + + void add_asset_of_type(std::int32_t type, const std::string& name) override; + void add_asset_of_type(const std::string& type, const std::string& name) override; + std::int32_t get_type_by_name(const std::string& type) override; + + void build(ZoneBuffer* buf) override; + }; +} diff --git a/src/CODO/stdafx.cpp b/src/CODO/stdafx.cpp new file mode 100644 index 0000000..a55cad6 --- /dev/null +++ b/src/CODO/stdafx.cpp @@ -0,0 +1,9 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" diff --git a/src/CODO/stdafx.hpp b/src/CODO/stdafx.hpp new file mode 100644 index 0000000..8c80879 --- /dev/null +++ b/src/CODO/stdafx.hpp @@ -0,0 +1,29 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS + +#include + +#undef min +#undef max +#undef add + +#include +#include +#include +#include + +// Namespaces +using namespace std::literals; +using namespace string_literals; + +#include "CODO.hpp" diff --git a/src/IW3.lua b/src/IW3.lua new file mode 100644 index 0000000..588ada4 --- /dev/null +++ b/src/IW3.lua @@ -0,0 +1,35 @@ +IW3 = {} + +function IW3:include() + includedirs { + path.join(ProjectFolder(), "IW3") + } +end + +function IW3:link() + self:include() + links { + "IW3" + } +end + +function IW3:project() + local folder = ProjectFolder(); + + project "IW3" + kind "StaticLib" + language "C++" + + pchheader "stdafx.hpp" + pchsource(path.join(folder, "IW3/stdafx.cpp")) + + files { + path.join(folder, "IW3/**.h"), + path.join(folder, "IW3/**.hpp"), + path.join(folder, "IW3/**.cpp") + } + + self:include() + IW4:include() + ZoneUtils:include() +end \ No newline at end of file diff --git a/src/IW3/Assets/ClipMap.cpp b/src/IW3/Assets/ClipMap.cpp new file mode 100644 index 0000000..0c2b7e9 --- /dev/null +++ b/src/IW3/Assets/ClipMap.cpp @@ -0,0 +1,215 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: momo5502 (https://github.com/momo5502) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW4/Assets/ClipMap.hpp" +#include "ClipMap.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + void IClipMap::dump(clipMap_t* asset, ZoneMemory* mem) + { + if (!asset) return; + + auto* iw4_clipmap = mem->Alloc(); + memset(iw4_clipmap, 0, sizeof IW4::clipMap_t); + + // convert clipmap to IW4 format + iw4_clipmap->name = asset->name; + iw4_clipmap->isInUse = asset->isInUse; + + iw4_clipmap->numCPlanes = asset->planeCount; + iw4_clipmap->cPlanes = (IW4::cplane_s*)asset->planes; + + iw4_clipmap->numStaticModels = (int)asset->numStaticModels; + iw4_clipmap->staticModelList = mem->Alloc(iw4_clipmap->numStaticModels); + for (unsigned int i = 0; i < asset->numStaticModels; ++i) + { + std::memcpy(&iw4_clipmap->staticModelList[i], &asset->staticModelList[i].xmodel, + sizeof(IW4::cStaticModel_s)); + iw4_clipmap->staticModelList[i].absBounds.compute(); + } + + iw4_clipmap->numMaterials = (int)asset->numMaterials; + iw4_clipmap->materials = mem->Alloc(iw4_clipmap->numMaterials); + for (auto i = 0u; i < iw4_clipmap->numMaterials; i++) + { + iw4_clipmap->materials[i].material = mem->StrDup(asset->materials[i].material); + iw4_clipmap->materials[i].contentFlags = asset->materials[i].contentFlags; + iw4_clipmap->materials[i].surfaceFlags = asset->materials[i].surfaceFlags; + } + + iw4_clipmap->numCBrushSides = (int)asset->numBrushSides; + iw4_clipmap->cBrushSides = mem->Alloc(iw4_clipmap->numCBrushSides); + + std::unordered_map mapped_brush_sides; + + for (unsigned int i = 0; i < asset->numBrushSides; ++i) + { + mapped_brush_sides[&asset->brushsides[i]] = &iw4_clipmap->cBrushSides[i]; + + iw4_clipmap->cBrushSides[i].plane = (IW4::cplane_s*)asset->brushsides[i].plane; + iw4_clipmap->cBrushSides[i].materialNum = asset->brushsides[i].materialNum; + iw4_clipmap->cBrushSides[i].firstAdjacentSideOffset = (char)asset->brushsides[i].firstAdjacentSideOffset; + iw4_clipmap->cBrushSides[i].edgeCount = asset->brushsides[i].edgeCount; + } + + iw4_clipmap->numCBrushEdges = (int)asset->numBrushEdges; + iw4_clipmap->cBrushEdges = (IW4::cbrushedge_t*)asset->brushEdges; + + iw4_clipmap->numCNodes = (int)asset->numNodes; + iw4_clipmap->cNodes = (IW4::cNode_t*)asset->nodes; + + iw4_clipmap->numCLeaf = (int)asset->numLeafs; + iw4_clipmap->cLeaf = mem->Alloc(iw4_clipmap->numCLeaf); + for (unsigned int i = 0; i < asset->numLeafs; ++i) + { + std::memcpy(&iw4_clipmap->cLeaf[i], &asset->leafs[i], sizeof(IW4::cLeaf_t)); + iw4_clipmap->cLeaf[i].bounds.compute(); + } + + iw4_clipmap->numCLeafBrushNodes = (int)asset->leafbrushNodesCount; + iw4_clipmap->cLeafBrushNodes = (IW4::cLeafBrushNode_s*)asset->leafbrushNodes; + + iw4_clipmap->numLeafBrushes = (int)asset->numLeafBrushes; + iw4_clipmap->leafBrushes = (short*)asset->leafbrushes; + + iw4_clipmap->numLeafSurfaces = (int)asset->numLeafSurfaces; + iw4_clipmap->leafSurfaces = (int*)asset->leafsurfaces; + + iw4_clipmap->numVerts = (int)asset->vertCount; + iw4_clipmap->verts = (IW4::VecInternal<3>*)asset->verts; + + iw4_clipmap->numTriIndices = asset->triCount; + iw4_clipmap->triIndices = (short*)asset->triIndices; + iw4_clipmap->triEdgeIsWalkable = asset->triEdgeIsWalkable; + + iw4_clipmap->numCollisionBorders = asset->borderCount; + iw4_clipmap->collisionBorders = (IW4::CollisionBorder*)asset->borders; + + iw4_clipmap->numCollisionPartitions = asset->partitionCount; + iw4_clipmap->collisionPartitions = (IW4::CollisionPartition*)asset->partitions; + + iw4_clipmap->numCollisionAABBTrees = asset->aabbTreeCount; + iw4_clipmap->collisionAABBTrees = mem->Alloc(iw4_clipmap->numCollisionAABBTrees); + for (int i = 0; i < asset->aabbTreeCount; ++i) + { + std::memcpy(&iw4_clipmap->collisionAABBTrees[i].origin, &asset->aabbTrees[i].origin, 12); + std::memcpy(&iw4_clipmap->collisionAABBTrees[i].halfSize, &asset->aabbTrees[i].halfSize, 12); + iw4_clipmap->collisionAABBTrees[i].materialIndex = asset->aabbTrees[i].materialIndex; + iw4_clipmap->collisionAABBTrees[i].childCount = asset->aabbTrees[i].childCount; + iw4_clipmap->collisionAABBTrees[i].u.firstChildIndex = asset->aabbTrees[i].u.firstChildIndex; + } + + // cmodels! + iw4_clipmap->numCModels = (int)asset->numSubModels; + iw4_clipmap->cModels = mem->Alloc(iw4_clipmap->numCModels); + for (unsigned int i = 0; i < asset->numSubModels; ++i) + { + std::memcpy(&iw4_clipmap->cModels[i], &asset->cmodels[i], sizeof(IW4::cmodel_t)); + iw4_clipmap->cModels[i].bounds.compute(); + iw4_clipmap->cModels[i].leaf.bounds.compute(); + } + + iw4_clipmap->numBrushes = (short)asset->numBrushes; + iw4_clipmap->brushes = mem->Alloc(iw4_clipmap->numBrushes); + iw4_clipmap->brushBounds = mem->Alloc(iw4_clipmap->numBrushes); + iw4_clipmap->brushContents = mem->Alloc(iw4_clipmap->numBrushes); + for (unsigned int i = 0; i < asset->numBrushes; ++i) + { + std::memcpy(&iw4_clipmap->brushes[i].axialMaterialNum, &asset->brushes[i].axialMaterialNum, + sizeof(iw4_clipmap->brushes[i].axialMaterialNum)); + std::memcpy(&iw4_clipmap->brushes[i].firstAdjacentSideOffsets, + &asset->brushes[i].firstAdjacentSideOffsets, + sizeof(iw4_clipmap->brushes[i].firstAdjacentSideOffsets)); + std::memcpy(&iw4_clipmap->brushes[i].edgeCount, &asset->brushes[i].edgeCount, + sizeof(iw4_clipmap->brushes[i].edgeCount)); + + iw4_clipmap->brushes[i].numsides = asset->brushes[i].numsides; + iw4_clipmap->brushes[i].sides = mapped_brush_sides.find(asset->brushes[i].sides)->second; + iw4_clipmap->brushes[i].edge = asset->brushes[i].baseAdjacentSide; + iw4_clipmap->brushes[i].numsides = asset->brushes[i].numsides; + + iw4_clipmap->brushBounds[i].compute(asset->brushes[i].mins, asset->brushes[i].maxs); + + iw4_clipmap->brushContents[i] = asset->brushes[i].contents; + } + + iw4_clipmap->smodelNodeCount = 1; + iw4_clipmap->smodelNodes = mem->Alloc(); + if (asset->numStaticModels == 0) + { + iw4_clipmap->smodelNodes[0].bounds.halfSize[0] = -131072.000f; + iw4_clipmap->smodelNodes[0].bounds.halfSize[1] = -131072.000f; + iw4_clipmap->smodelNodes[0].bounds.halfSize[2] = -131072.000f; + } + else + { + float maxs[3]; + float mins[3]; + + maxs[0] = asset->staticModelList[0].absmax[0]; + maxs[1] = asset->staticModelList[1].absmax[1]; + maxs[2] = asset->staticModelList[2].absmax[2]; + + mins[0] = asset->staticModelList[0].absmin[0]; + mins[1] = asset->staticModelList[1].absmin[1]; + mins[2] = asset->staticModelList[2].absmin[2]; + + for (unsigned int i = 1; i < asset->numStaticModels; i++) + { + maxs[0] = max(maxs[0], asset->staticModelList[i].absmax[0]); + maxs[1] = max(maxs[1], asset->staticModelList[i].absmax[1]); + maxs[2] = max(maxs[2], asset->staticModelList[i].absmax[2]); + + mins[0] = min(mins[0], asset->staticModelList[i].absmin[0]); + mins[1] = min(mins[1], asset->staticModelList[i].absmin[1]); + mins[2] = min(mins[2], asset->staticModelList[i].absmin[2]); + } + + iw4_clipmap->smodelNodes[0].bounds.compute(mins, maxs); + iw4_clipmap->smodelNodes[0].childCount = static_cast(asset->numStaticModels); + iw4_clipmap->smodelNodes[0].firstChild = 0; + } + + iw4_clipmap->mapEnts = mem->Alloc(); // asset->mapEnts; + memcpy(iw4_clipmap->mapEnts, asset->mapEnts, sizeof MapEnts); + + iw4_clipmap->mapEnts->stageCount = 1; + iw4_clipmap->mapEnts->stageNames = mem->Alloc(); + iw4_clipmap->mapEnts->stageNames[0].stageName = mem->StrDup("stage 0"); + iw4_clipmap->mapEnts->stageNames[0].triggerIndex = 0x400; + iw4_clipmap->mapEnts->stageNames[0].sunPrimaryLightIndex = 0x1; + + iw4_clipmap->dynEntCount[0] = asset->dynEntCount[0]; + iw4_clipmap->dynEntCount[1] = asset->dynEntCount[1]; + + iw4_clipmap->dynEntDefList[0] = (IW4::DynEntityDef*)asset->dynEntDefList[0]; + iw4_clipmap->dynEntDefList[1] = (IW4::DynEntityDef*)asset->dynEntDefList[1]; + + iw4_clipmap->dynEntPoseList[0] = (IW4::DynEntityPose*)asset->dynEntPoseList[0]; + iw4_clipmap->dynEntPoseList[1] = (IW4::DynEntityPose*)asset->dynEntPoseList[1]; + + iw4_clipmap->dynEntClientList[0] = (IW4::DynEntityClient*)asset->dynEntClientList[0]; + iw4_clipmap->dynEntClientList[1] = (IW4::DynEntityClient*)asset->dynEntClientList[1]; + + iw4_clipmap->dynEntCollList[0] = (IW4::DynEntityColl*)asset->dynEntCollList[0]; + iw4_clipmap->dynEntCollList[1] = (IW4::DynEntityColl*)asset->dynEntCollList[1]; + + iw4_clipmap->checksum = asset->checksum; + + //iw4_clipmap->stageCount = 1; + //iw4_clipmap-> = mem->Alloc(); + + + IW4::IClipMap::dump(iw4_clipmap); + } + } +} diff --git a/src/IW3/Assets/ClipMap.hpp b/src/IW3/Assets/ClipMap.hpp new file mode 100644 index 0000000..fc2c88c --- /dev/null +++ b/src/IW3/Assets/ClipMap.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: momo5502 (https://github.com/momo5502) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IClipMap + { + public: + static void dump(clipMap_t* asset, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Assets/ComWorld.cpp b/src/IW3/Assets/ComWorld.cpp new file mode 100644 index 0000000..48200bb --- /dev/null +++ b/src/IW3/Assets/ComWorld.cpp @@ -0,0 +1,18 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW4/Assets/ComWorld.hpp" + +namespace ZoneTool::IW3 +{ + void IComWorld::dump(ComWorld* asset, [[maybe_unused]] ZoneMemory* mem) + { + IW4::IComWorld::dump(reinterpret_cast(asset)); + } +} diff --git a/src/IW3/Assets/ComWorld.hpp b/src/IW3/Assets/ComWorld.hpp new file mode 100644 index 0000000..f2f7cf7 --- /dev/null +++ b/src/IW3/Assets/ComWorld.hpp @@ -0,0 +1,18 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool::IW3 +{ + class IComWorld + { + public: + static void dump(ComWorld* asset, ZoneMemory* mem); + }; +} \ No newline at end of file diff --git a/src/IW3/Assets/FxEffectDef.cpp b/src/IW3/Assets/FxEffectDef.cpp new file mode 100644 index 0000000..f5545d5 --- /dev/null +++ b/src/IW3/Assets/FxEffectDef.cpp @@ -0,0 +1,54 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW4/Assets/FxEffectDef.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + void IFxEffectDef::dump(FxEffectDef* asset, ZoneMemory* mem) + { + const auto iw4_fx = mem->Alloc(); + memcpy(iw4_fx, asset, sizeof IW4::FxEffectDef); + + const auto elem_count = iw4_fx->elemDefCountEmission + iw4_fx->elemDefCountOneShot + iw4_fx->elemDefCountLooping; + iw4_fx->elemDefs = mem->Alloc(elem_count); + + for (auto i = 0; i < elem_count; i++) + { + memcpy(&iw4_fx->elemDefs[i], &asset->elemDefs[i], sizeof IW4::FxElemDef); + + if (iw4_fx->elemDefs[i].elemType >= 5) + { + iw4_fx->elemDefs[i].elemType += 2; + } + + iw4_fx->elemDefs[i].collBounds.compute(iw4_fx->elemDefs[i].collBounds.midPoint, iw4_fx->elemDefs[i].collBounds.halfSize); + + if (iw4_fx->elemDefs[i].elemType == 3 && iw4_fx->elemDefs[i].extended.trailDef) + { + iw4_fx->elemDefs[i].extended.trailDef = mem->Alloc(); + iw4_fx->elemDefs[i].extended.trailDef->scrollTimeMsec = asset->elemDefs[i].trailDef->scrollTimeMsec; + iw4_fx->elemDefs[i].extended.trailDef->repeatDist = asset->elemDefs[i].trailDef->repeatDist; + iw4_fx->elemDefs[i].extended.trailDef->vertCount = asset->elemDefs[i].trailDef->vertCount; + iw4_fx->elemDefs[i].extended.trailDef->verts = reinterpret_cast(asset->elemDefs[i].trailDef->verts); + iw4_fx->elemDefs[i].extended.trailDef->indCount = asset->elemDefs[i].trailDef->indCount; + iw4_fx->elemDefs[i].extended.trailDef->inds = asset->elemDefs[i].trailDef->inds; + } + else + { + iw4_fx->elemDefs[i].extended.trailDef = nullptr; + } + } + + IW4::IFxEffectDef::dump(iw4_fx); + } + } +} diff --git a/src/IW3/Assets/FxEffectDef.hpp b/src/IW3/Assets/FxEffectDef.hpp new file mode 100644 index 0000000..e8787a5 --- /dev/null +++ b/src/IW3/Assets/FxEffectDef.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IFxEffectDef + { + public: + static void dump(FxEffectDef* asset, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Assets/GameWorldMp.cpp b/src/IW3/Assets/GameWorldMp.cpp new file mode 100644 index 0000000..d9814b0 --- /dev/null +++ b/src/IW3/Assets/GameWorldMp.cpp @@ -0,0 +1,20 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + void IGameWorldMp::dump(GameWorldMp* asset) + { + // lol, GameWorldMp contains no data in IW3 + } + } +} diff --git a/src/IW3/Assets/GameWorldMp.hpp b/src/IW3/Assets/GameWorldMp.hpp new file mode 100644 index 0000000..dd42f8a --- /dev/null +++ b/src/IW3/Assets/GameWorldMp.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IGameWorldMp + { + public: + static void dump(GameWorldMp* asset); + }; + } +} diff --git a/src/IW3/Assets/GfxImage.cpp b/src/IW3/Assets/GfxImage.cpp new file mode 100644 index 0000000..a2b739b --- /dev/null +++ b/src/IW3/Assets/GfxImage.cpp @@ -0,0 +1,54 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW4/Assets/GfxImage.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + IW4::GfxImage* IGfxImage::GenerateIW4Image(GfxImage* image, ZoneMemory* mem) + { + auto* iw4_image = mem->Alloc(); + + // copy image data + iw4_image->mapType = image->mapType; + iw4_image->semantic = image->semantic; + iw4_image->category = image->category; + iw4_image->dataLen1 = image->cardMemory.platform[0]; + iw4_image->dataLen2 = image->cardMemory.platform[1]; + iw4_image->width = image->width; + iw4_image->height = image->height; + iw4_image->depth = image->depth; + iw4_image->pad = image->delayLoadPixels; + iw4_image->name = (char*)image->name; + + // alloc texture + iw4_image->texture = (IW4::GfxImageLoadDef*)image->texture.loadDef; + + return iw4_image; + } + + void IGfxImage::dump(GfxImage* asset, ZoneMemory* mem) + { + if (!asset) + { + return; + } + + ZONETOOL_INFO("dumping map image %s", asset->name); + + // alloc IW4 image + auto* iw4_image = IGfxImage::GenerateIW4Image(asset, mem); + + // dump IW4 image + IW4::IGfxImage::dump(iw4_image); + } + } +} diff --git a/src/IW3/Assets/GfxImage.hpp b/src/IW3/Assets/GfxImage.hpp new file mode 100644 index 0000000..afb084c --- /dev/null +++ b/src/IW3/Assets/GfxImage.hpp @@ -0,0 +1,22 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IGfxImage + { + public: + static IW4::GfxImage* GenerateIW4Image(GfxImage* image, ZoneMemory* mem); + static void dump(GfxImage* asset, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Assets/GfxWorld.cpp b/src/IW3/Assets/GfxWorld.cpp new file mode 100644 index 0000000..cfd3581 --- /dev/null +++ b/src/IW3/Assets/GfxWorld.cpp @@ -0,0 +1,353 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW4/Assets/GfxWorld.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + void IGfxWorld::dump(GfxWorld* world, ZoneMemory* mem) + { + if (!world) return; + + IW4::GfxSky sky; + IW4::GfxWorld map; + ZeroMemory(&sky, sizeof(sky)); + ZeroMemory(&map, sizeof(map)); + + map.name = world->name; + map.baseName = world->baseName; + map.planeCount = world->planeCount; + map.nodeCount = world->nodeCount; + map.surfaceCount = world->surfaceCount; + + map.skyCount = 1; + map.skies = &sky; + + sky.skyImage = (IW4::GfxImage*)world->skyImage; + sky.skySamplerState = world->skySamplerState & 0xFF; + sky.skyStartSurfs = (uint32_t*)world->skyStartSurfs; + sky.skySurfCount = world->skySurfCount; + + map.sunPrimaryLightIndex = world->sunPrimaryLightIndex; + map.primaryLightCount = world->primaryLightCount; + + // map.dpvsPlanes = world->dpvsPlanes; + memcpy(&map.dpvsPlanes, &world->dpvsPlanes, sizeof world->dpvsPlanes); + + // AABBTree data is stored as part of the cells. + // However, in IW4 it's not, so we have to extract the data + if (world->cells) + { + map.aabbTreeCounts = mem->Alloc(world->dpvsPlanes.cellCount); + map.aabbTree = mem->Alloc(world->dpvsPlanes.cellCount); + map.cells = mem->Alloc(world->dpvsPlanes.cellCount); + + for (int i = 0; i < world->dpvsPlanes.cellCount; ++i) + { + map.aabbTreeCounts[i].aabbTreeCount = world->cells[i].aabbTreeCount; + + map.cells[i].bounds.compute(world->cells[i].mins, world->cells[i].maxs); // Verified + map.cells[i].portalCount = world->cells[i].portalCount; + map.cells[i].reflectionProbeCount = world->cells[i].reflectionProbeCount; + map.cells[i].reflectionProbes = world->cells[i].reflectionProbes; + + if (world->cells[i].aabbTree) + { + map.aabbTree[i].aabbtree = mem->Alloc(world->cells[i].aabbTreeCount); + std::memcpy(map.aabbTree[i].aabbtree, world->cells[i].aabbTree, sizeof(IW4::GfxAabbTree) * world->cells[i].aabbTreeCount); + + for (int j = 0; j < world->cells[i].aabbTreeCount; ++j) + { + static_assert(sizeof IW4::GfxAabbTree == sizeof IW3::GfxAabbTree, "Size mismatch"); + map.aabbTree[i].aabbtree[j].bounds.compute(world->cells[i].aabbTree[j].mins, world->cells[i].aabbTree[j].maxs); // Verified + } + } + + if (world->cells[i].portals) + { + map.cells[i].portals = mem->Alloc(world->cells[i].portalCount); + + // Map all portals, so we have them ready for the next loop (might be unnecessary, as they are mapped at runtime) + std::unordered_map portalMap = { { nullptr, nullptr } }; + for (int j = 0; j < world->cells[i].portalCount; ++j) + { + portalMap[&world->cells[i].portals[j]] = &map.cells[i].portals[j]; + } + + for (int j = 0; j < world->cells[i].portalCount; ++j) + { + IW3::GfxPortal* portal = &world->cells[i].portals[j]; + IW4::GfxPortal* destPortal = &map.cells[i].portals[j]; + + destPortal->cellIndex = static_cast(portal->cell - world->cells); + if (destPortal->cellIndex >= static_cast(world->dpvsPlanes.cellCount)) + { + ZONETOOL_FATAL("Unable to calculate cell index. This should not happen!\n"); + destPortal->cellIndex = 0; + } + + destPortal->vertices = portal->vertices; + destPortal->vertexCount = portal->vertexCount; + + destPortal->writable.isQueued = portal->writable.isQueued; + destPortal->writable.isAncestor = portal->writable.isAncestor; + destPortal->writable.recursionDepth = portal->writable.recursionDepth; + destPortal->writable.hullPointCount = portal->writable.hullPointCount; + destPortal->writable.hullPoints = portal->writable.hullPoints; + + if (portalMap.find(portal->writable.queuedParent) != portalMap.end()) + { + destPortal->writable.queuedParent = portalMap[portal->writable.queuedParent]; + } + else + { + if (portal->writable.queuedParent) ZONETOOL_ERROR("Unmapped portal. This shouldn't happen. Nulling it...\n"); + destPortal->writable.queuedParent = nullptr; + } + + std::memcpy(destPortal->plane.coeffs, portal->plane.coeffs, sizeof(destPortal->plane.coeffs)); + std::memcpy(destPortal->hullAxis, portal->hullAxis, sizeof(destPortal->hullAxis)); + } + } + } + } + + map.draw.reflectionProbeCount = world->reflectionProbeCount; + map.draw.reflectionProbeTextures = (IW4::GfxTexture*)world->reflectionProbeTextures; + map.draw.lightmapCount = world->lightmapCount; + map.draw.lightmaps = mem->Alloc(world->lightmapCount); // (IW4::GfxLightmapArray*)world->lightmaps; + + for (auto i = 0; i < world->lightmapCount; i++) + { + if (world->lightmaps[i].primary) + { + IGfxImage::dump(world->lightmaps[i].primary, mem); + map.draw.lightmaps[i].primary = IGfxImage::GenerateIW4Image(world->lightmaps[i].primary, mem); + } + + if (world->lightmaps[i].secondary) + { + IGfxImage::dump(world->lightmaps[i].secondary, mem); + map.draw.lightmaps[i].secondary = IGfxImage::GenerateIW4Image(world->lightmaps[i].secondary, mem); + } + } + + map.draw.lightmapPrimaryTextures = (IW4::GfxTexture*)world->lightmapPrimaryTextures; + map.draw.lightmapSecondaryTextures = (IW4::GfxTexture*)world->lightmapSecondaryTextures; + map.draw.skyImage = IGfxImage::GenerateIW4Image(world->skyImage, mem); + map.draw.outdoorImage = IGfxImage::GenerateIW4Image(world->outdoorImage, mem); + map.draw.vertexCount = world->vertexCount; + memcpy(&map.draw.vd, &world->vd, sizeof world->vd); + map.draw.vertexLayerDataSize = world->vertexLayerDataSize; + memcpy(&map.draw.vld, &world->vld, sizeof world->vld); + map.draw.indexCount = world->indexCount; + + // Split reflection images and probes + if (world->reflectionProbes) + { + map.draw.reflectionImages = mem->Alloc(world->reflectionProbeCount); + map.draw.reflectionProbes = mem->Alloc(world->reflectionProbeCount); + + for (unsigned int i = 0; i < world->reflectionProbeCount; ++i) + { + map.draw.reflectionImages[i] = IGfxImage::GenerateIW4Image(world->reflectionProbes[i].reflectionImage, mem); + IGfxImage::dump(world->reflectionProbes[i].reflectionImage, mem); + + std::memcpy(map.draw.reflectionProbes[i].offset, world->reflectionProbes[i].origin, sizeof(map.draw.reflectionProbes[i].offset)); + } + } + + memcpy(&map.lightGrid, &world->lightGrid, sizeof world->lightGrid); + + assert(sizeof IW3::GfxBrushModel == 56); + assert(sizeof IW4::GfxBrushModel == 60); + + map.bounds.compute(world->mins, world->maxs); + + map.checksum = world->checksum; + map.materialMemoryCount = world->materialMemoryCount; + map.materialMemory = (IW4::MaterialMemory*)world->materialMemory; + memcpy(&map.sun, &world->sun, sizeof world->sun); + + std::memcpy(map.outdoorLookupMatrix, world->outdoorLookupMatrix, sizeof(map.outdoorLookupMatrix)); + map.outdoorImage = map.draw.outdoorImage; // (IW4::GfxImage*)world->outdoorImage; + + IGfxImage::dump(world->outdoorImage, mem); + + map.cellCasterBits[0] = world->cellCasterBits; + map.cellCasterBits[1] = world->cellCasterBits; // This mustn't be null! + + map.sceneDynModel = (IW4::GfxSceneDynModel*)world->sceneDynModel; + map.sceneDynBrush = (IW4::GfxSceneDynBrush*)world->sceneDynBrush; + + map.primaryLightEntityShadowVis = (unsigned char*)world->primaryLightEntityShadowVis; + map.primaryLightDynEntShadowVis[0] = world->primaryLightDynEntShadowVis[0]; + map.primaryLightDynEntShadowVis[1] = world->primaryLightDynEntShadowVis[1]; + map.primaryLightForModelDynEnt = world->nonSunPrimaryLightForModelDynEnt; + + map.shadowGeom = (IW4::GfxShadowGeometry*)world->shadowGeom; + map.lightRegion = (IW4::GfxLightRegion*)world->lightRegion; + + map.dpvs.smodelCount = world->dpvs.smodelCount; + map.dpvs.staticSurfaceCount = world->dpvs.staticSurfaceCount; + map.dpvs.staticSurfaceCountNoDecal = world->dpvs.staticSurfaceCountNoDecal; + + // Not sure if correct + // update: slightly more sure but not much lol + map.dpvs.litOpaqueSurfsBegin = world->dpvs.litSurfsBegin; + map.dpvs.litOpaqueSurfsEnd = world->dpvs.decalSurfsEnd; + + // these don't exist in iw3 so skip + map.dpvs.litTransSurfsBegin = world->dpvs.decalSurfsEnd; + map.dpvs.litTransSurfsEnd = world->dpvs.decalSurfsEnd; + + // Skip those as well + map.dpvs.shadowCasterSurfsBegin = world->dpvs.decalSurfsEnd; + map.dpvs.shadowCasterSurfsEnd = world->dpvs.decalSurfsEnd; + + map.dpvs.emissiveSurfsBegin = world->dpvs.emissiveSurfsBegin; + map.dpvs.emissiveSurfsEnd = world->dpvs.emissiveSurfsEnd; + map.dpvs.smodelVisDataCount = world->dpvs.smodelVisDataCount; + map.dpvs.surfaceVisDataCount = world->dpvs.surfaceVisDataCount; + + std::memcpy(map.dpvs.smodelVisData, world->dpvs.smodelVisData, sizeof(map.dpvs.smodelVisData)); + std::memcpy(map.dpvs.surfaceVisData, world->dpvs.surfaceVisData, sizeof(map.dpvs.surfaceVisData)); + + map.dpvs.sortedSurfIndex = world->dpvs.sortedSurfIndex; + + if (world->dpvs.smodelInsts) + { + map.dpvs.smodelInsts = mem->Alloc(world->dpvs.smodelCount); + + for (unsigned int i = 0; i < world->dpvs.smodelCount; ++i) + { + map.dpvs.smodelInsts[i].bounds.compute(world->dpvs.smodelInsts[i].mins, world->dpvs.smodelInsts[i].maxs); // Verified + + // I guess the sun is always a good lighting source ;) + map.dpvs.smodelInsts[i].lightingOrigin[0] = world->sun.sunFxPosition[0]; + map.dpvs.smodelInsts[i].lightingOrigin[1] = world->sun.sunFxPosition[1]; + map.dpvs.smodelInsts[i].lightingOrigin[2] = world->sun.sunFxPosition[2]; + } + } + + if (world->dpvs.surfaces) + { + map.dpvs.surfaces = mem->Alloc(world->surfaceCount); + map.dpvs.surfacesBounds = mem->Alloc(world->surfaceCount); + + assert(sizeof(IW3::srfTriangles_t) == sizeof(IW4::srfTriangles_t)); + + for (auto i = 0; i < world->surfaceCount; ++i) + { + std::memcpy(&map.dpvs.surfaces[i].tris, &world->dpvs.surfaces[i].tris, sizeof(IW4::srfTriangles_t)); + map.dpvs.surfaces[i].material = (IW4::Material*)world->dpvs.surfaces[i].material; + map.dpvs.surfaces[i].lightmapIndex = world->dpvs.surfaces[i].lightmapIndex; + map.dpvs.surfaces[i].reflectionProbeIndex = world->dpvs.surfaces[i].reflectionProbeIndex; + map.dpvs.surfaces[i].primaryLightIndex = world->dpvs.surfaces[i].primaryLightIndex; + map.dpvs.surfaces[i].flags = world->dpvs.surfaces[i].flags; + + map.dpvs.surfacesBounds[i].bounds.compute(world->dpvs.surfaces[i].bounds[0], world->dpvs.surfaces[i].bounds[1]); // Verified + + assert(map.dpvs.surfaces[i].material != nullptr); + } + } + + if (world->dpvs.smodelDrawInsts) + { + map.dpvs.smodelDrawInsts = mem->Alloc(world->dpvs.smodelCount); + + for (unsigned int i = 0; i < world->dpvs.smodelCount; ++i) + { + std::memcpy(&map.dpvs.smodelDrawInsts[i].placement, &world->dpvs.smodelDrawInsts[i].placement, sizeof(GfxPackedPlacement)); + std::memcpy(map.dpvs.smodelDrawInsts[i].cacheId, world->dpvs.smodelDrawInsts[i].smodelCacheIndex, sizeof(map.dpvs.smodelDrawInsts[i].cacheId)); + + map.dpvs.smodelDrawInsts[i].model = (IW4::XModel*)world->dpvs.smodelDrawInsts[i].model; + map.dpvs.smodelDrawInsts[i].cullDist = static_cast(world->dpvs.smodelDrawInsts[i].cullDist); + map.dpvs.smodelDrawInsts[i].reflectionProbeIndex = world->dpvs.smodelDrawInsts[i].reflectionProbeIndex; + map.dpvs.smodelDrawInsts[i].primaryLightIndex = world->dpvs.smodelDrawInsts[i].primaryLightIndex; + map.dpvs.smodelDrawInsts[i].lightingHandle = world->dpvs.smodelDrawInsts[i].lightingHandle; + map.dpvs.smodelDrawInsts[i].flags = world->dpvs.smodelDrawInsts[i].flags; + + // This has been moved + if (world->dpvs.smodelInsts) map.dpvs.smodelDrawInsts[i].groundLighting.packed = world->dpvs.smodelInsts[i].groundLighting.packed; + } + } + + map.dpvs.surfaceMaterials = (IW4::GfxDrawSurf*)world->dpvs.surfaceMaterials; + map.dpvs.surfaceCastsSunShadow = world->dpvs.surfaceCastsSunShadow; + map.dpvs.usageCount = world->dpvs.usageCount; + + memcpy(&map.dpvsDyn, &world->dpvsDyn, sizeof world->dpvsDyn); + + // Should we set that to true? :O + map.fogTypesAllowed = 3; // iw4_credits has 0x3 + + map.sortKeyLitDecal = 0x6; + map.sortKeyEffectDecal = 0x27; + map.sortKeyEffectAuto = 0x30; + map.sortKeyDistortion = 0x2b; + + // sort models + map.modelCount = world->modelCount; + if (world->models) + { + map.models = mem->Alloc(world->modelCount); + + std::vector models; + models.resize(world->modelCount); + + for (auto i = 0; i < world->modelCount; ++i) + { + models[i].writable.bounds.compute(world->models[i].writable.mins, world->models[i].writable.maxs); // Irrelevant, runtime data + models[i].bounds.compute(world->models[i].bounds[0], world->models[i].bounds[1]); // Verified + + auto* half_size = models[i].bounds.halfSize; + models[i].radius = std::sqrt(std::pow(half_size[0], 2) + std::pow(half_size[1], 2) + std::pow(half_size[2], 2)); + + models[i].surfaceCount = world->models[i].surfaceCount; + models[i].startSurfIndex = world->models[i].startSurfIndex; + models[i].surfaceCountNoDecal = world->models[i].surfaceCountNoDecal; + } + + std::sort(models.begin(), models.end(), [](const IW4::GfxBrushModel& m1, const IW4::GfxBrushModel& m2) + { + return m1.startSurfIndex > m2.startSurfIndex; + }); + + std::memcpy(map.models, models.data(), sizeof(IW4::GfxBrushModel) * world->modelCount); + } + + // sort triangles & vertices + auto tri_index = 0; + map.draw.indices = mem->Alloc(map.draw.indexCount); + + for (auto i = 0; i < map.surfaceCount; i++) + { + auto* surface = &map.dpvs.surfaces[i]; + + // triangles + std::memcpy(&map.draw.indices[tri_index], &world->indices[surface->tris.baseIndex], surface->tris.triCount * 6); + surface->tris.baseIndex = tri_index; + tri_index += surface->tris.triCount * 3; + } + + if (tri_index != map.draw.indexCount) + { + ZONETOOL_WARNING("Warning: Didn't sort all indicies for draw"); + } + + // Specify that it's a custom map + map.checksum = 0xDEADBEEF; + + IW4::IGfxWorld::dump(&map); + } + } +} diff --git a/src/IW3/Assets/GfxWorld.hpp b/src/IW3/Assets/GfxWorld.hpp new file mode 100644 index 0000000..c44ef35 --- /dev/null +++ b/src/IW3/Assets/GfxWorld.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IGfxWorld + { + public: + static void dump(GfxWorld* asset, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Assets/LoadedSound.cpp b/src/IW3/Assets/LoadedSound.cpp new file mode 100644 index 0000000..7b46518 --- /dev/null +++ b/src/IW3/Assets/LoadedSound.cpp @@ -0,0 +1,92 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + void ILoadedSound::dump(LoadedSound* asset, ZoneMemory* mem) + { + if (asset->struct1.waveFormat != 1) + { + ZONETOOL_ERROR("Audio format other than PCM currently not supported. Sound: %s\n", asset->name); + return; + } + + const auto fp = FileSystem::FileOpen("loaded_sound\\"s + asset->name, "wb"); + if (!fp) + { + return; + } + + // --- RIF HEADER + // ChunkID + char chunkID[] = { 'R', 'I', 'F', 'F' }; + fwrite(chunkID, 4, 1, fp); + + // ChunkSize + int subchunk1Size = 16; + int subchunk2Size = asset->struct1.dataLength; + int chunkSize = 4 + (8 + subchunk1Size) + (8 + subchunk2Size); + fwrite(&chunkSize, 4, 1, fp); + + // Format + char format[] = { 'W', 'A', 'V', 'E' }; + fwrite(format, 4, 1, fp); + + + // --- FMT SUBCHUNK + // Subchunk1ID + char subchunk1ID[] = { 'f', 'm', 't', ' ' }; + fwrite(subchunk1ID, 4, 1, fp); + + // Subchunk1Size + fwrite(&subchunk1Size, 4, 1, fp); + + // AudioFormat + short audioFormat = asset->struct1.waveFormat; + fwrite(&audioFormat, 2, 1, fp); + + // NumChannels + short numChannels = asset->struct1.channelCount; + fwrite(&numChannels, 2, 1, fp); + + // SampleRate + int sampleRate = asset->struct1.sampleRate; + fwrite(&sampleRate, 4, 1, fp); + + // ByteRate + int byteRate = asset->struct1.sampleRate * asset->struct1.channelCount * asset->struct1.bitPerChannel / 8; + fwrite(&byteRate, 4, 1, fp); + + // BlockAlign + short blockAlign = asset->struct1.blockAlign; + fwrite(&blockAlign, 2, 1, fp); + + // BitsPerSample + short bitsPerSample = asset->struct1.bitPerChannel; + fwrite(&bitsPerSample, 2, 1, fp); + + + // --- DATA SUBCHUNK + // Subchunk2ID + char subchunk2ID[] = { 'd', 'a', 't', 'a' }; + fwrite(subchunk2ID, 4, 1, fp); + + // Subchunk2Size + fwrite(&subchunk2Size, 4, 1, fp); + + // Data + fwrite(asset->struct1.soundData, asset->struct1.dataLength, 1, fp); + + FileSystem::FileClose(fp); + } + } +} diff --git a/src/IW3/Assets/LoadedSound.hpp b/src/IW3/Assets/LoadedSound.hpp new file mode 100644 index 0000000..00bfbd4 --- /dev/null +++ b/src/IW3/Assets/LoadedSound.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class ILoadedSound + { + public: + static void dump(LoadedSound* asset, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Assets/MapEnts.cpp b/src/IW3/Assets/MapEnts.cpp new file mode 100644 index 0000000..e74617d --- /dev/null +++ b/src/IW3/Assets/MapEnts.cpp @@ -0,0 +1,261 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: momo5502 (https://github.com/momo5502) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW4/Assets/MapEnts.hpp" +#include "MapEnts.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + bool StartsWith(const std::string& haystack, const std::string& needle) + { + return (haystack.size() >= needle.size() && !strncmp(needle.data(), haystack.data(), needle.size())); + } + + std::string StrToLower(std::string input) + { + std::transform(input.begin(), input.end(), input.begin(), ::tolower); + return input; + } + + class Entities + { + public: + Entities() + { + }; + + Entities(const char* string, size_t lenPlusOne) : Entities(std::string(string, lenPlusOne - 1)) + { + } + + Entities(std::string buffer) : Entities() { this->parse(buffer); }; + + Entities(const Entities& obj) : entities(obj.entities) + { + }; + + std::string build() + { + std::string entityString; + + for (auto& entity : this->entities) + { + entityString.append("{\n"); + + for (auto& property : entity) + { + entityString.push_back('"'); + entityString.append(property.first); + entityString.append("\" \""); + entityString.append(property.second); + entityString.append("\"\n"); + } + + entityString.append("}\n"); + } + + return entityString; + } + + std::vector getModels() + { + std::vector models; + + for (auto& entity : this->entities) + { + if (entity.find("model") != entity.end()) + { + std::string model = entity["model"]; + + if (!model.empty() && model[0] != '*' && model[0] != '?') // Skip brushmodels + { + if (std::find(models.begin(), models.end(), model) == models.end()) + { + models.push_back(model); + } + } + } + } + + return models; + } + + void deleteTriggers() + { + for (auto i = this->entities.begin(); i != this->entities.end();) + { + if (i->find("classname") != i->end()) + { + std::string classname = (*i)["classname"]; + if (StartsWith(classname, "trigger_")) + { + i = this->entities.erase(i); + continue; + } + } + + ++i; + } + } + + void deleteWeapons(bool keepTurrets) + { + for (auto i = this->entities.begin(); i != this->entities.end();) + { + if (i->find("weaponinfo") != i->end() || (i->find("targetname") != i->end() && (*i)["targetname"] == + "oldschool_pickup"s)) + { + if (!keepTurrets || i->find("classname") == i->end() || (*i)["classname"] != "misc_turret"s) + { + i = this->entities.erase(i); + continue; + } + } + + ++i; + } + } + + void convertTurrets() + { + for (auto& entity : this->entities) + { + if (entity.find("classname") != entity.end()) + { + if (entity["classname"] == "misc_turret"s) + { + entity["weaponinfo"] = "turret_minigun_mp"; + entity["model"] = "weapon_minigun"; + } + } + } + } + + private: + enum + { + PARSE_AWAIT_KEY, + PARSE_READ_KEY, + PARSE_AWAIT_VALUE, + PARSE_READ_VALUE, + }; + + std::vector> entities; + + void parse(std::string buffer) + { + int parseState = 0; + std::string key; + std::string value; + std::unordered_map entity; + + for (unsigned int i = 0; i < buffer.size(); ++i) + { + const char character = buffer[i]; + if (character == '{') + { + entity.clear(); + } + + switch (character) + { + case '{': + { + entity.clear(); + break; + } + + case '}': + { + this->entities.push_back(entity); + entity.clear(); + break; + } + + case '"': + { + if (parseState == PARSE_AWAIT_KEY) + { + key.clear(); + parseState = PARSE_READ_KEY; + } + else if (parseState == PARSE_READ_KEY) + { + parseState = PARSE_AWAIT_VALUE; + } + else if (parseState == PARSE_AWAIT_VALUE) + { + value.clear(); + parseState = PARSE_READ_VALUE; + } + else if (parseState == PARSE_READ_VALUE) + { + entity[StrToLower(key)] = value; + parseState = PARSE_AWAIT_KEY; + } + else + { + throw std::runtime_error("Parsing error!"); + } + break; + } + + default: + { + if (parseState == PARSE_READ_KEY) key.push_back(character); + else if (parseState == PARSE_READ_VALUE) value.push_back(character); + + break; + } + } + } + } + }; + + void AdaptEntities(IW4::MapEnts* ents, ZoneMemory* mem) + { + std::string entString(ents->entityString, ents->numEntityChars - 1); + + Entities mapEnts(entString); + mapEnts.deleteTriggers(); + mapEnts.deleteWeapons(/*true*/false); // For now delete turrets, as we can't write weapons + mapEnts.convertTurrets(); + entString = mapEnts.build(); + + ents->numEntityChars = entString.size() + 1; + ents->entityString = mem->Alloc(ents->numEntityChars); + std::memcpy((char*)ents->entityString, entString.data(), ents->numEntityChars); + } + + void IMapEnts::dump(MapEnts* asset, ZoneMemory* mem) + { + if (!asset) return; + + auto* mapents = mem->Alloc(); + memset(mapents, 0, sizeof IW4::MapEnts); + + mapents->name = asset->name; + mapents->entityString = asset->entityString; + mapents->numEntityChars = asset->numEntityChars; + + mapents->stageCount = 1; + mapents->stageNames = mem->Alloc(); + mapents->stageNames[0].stageName = mem->StrDup("stage 0"); + mapents->stageNames[0].triggerIndex = 0x400; + mapents->stageNames[0].sunPrimaryLightIndex = 0x1; + + // Remove unsupported stuff + AdaptEntities(mapents, mem); + + IW4::IMapEnts::dump(mapents); + } + } +} diff --git a/src/IW3/Assets/MapEnts.hpp b/src/IW3/Assets/MapEnts.hpp new file mode 100644 index 0000000..1ff7671 --- /dev/null +++ b/src/IW3/Assets/MapEnts.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: momo5502 (https://github.com/momo5502) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IMapEnts + { + public: + static void dump(MapEnts* asset, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Assets/Material.cpp b/src/IW3/Assets/Material.cpp new file mode 100644 index 0000000..a4c6bf1 --- /dev/null +++ b/src/IW3/Assets/Material.cpp @@ -0,0 +1,249 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +// Material parsing +#define MATERIAL_INT(entry) \ + mat->entry = matdata[#entry].get(); + +#define MATERIAL_DUMP_INT(entry) \ + matdata[#entry] = mat->entry; + +#define MATERIAL_DUMP_STRING(entry) \ + matdata[#entry] = std::string(mat->entry); + +#define MATERIAL_DUMP_MATIMG_ARRAY(entry,size) \ + nlohmann::json matimg##entry; \ + for (int i = 0; i < size; i++) \ + { \ + nlohmann::json img##entry; \ + img##entry["image"] = mat->entry[i].image->name; \ + img##entry["semantic"] = (int)mat->entry[i].semantic; \ + img##entry["sampleState"] = (int)mat->entry[i].sampleState; \ + img##entry["lastCharacter"] = (char)mat->entry[i].secondLastCharacter; \ + img##entry["firstCharacter"] = (char)mat->entry[i].firstCharacter; \ + img##entry["typeHash"] = (unsigned int)mat->entry[i].typeHash; \ + matimg##entry[i] = img##entry; \ + } \ + matdata[#entry] = matimg##entry; + +#define MATERIAL_DUMP_CONST_ARRAY(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + nlohmann::json cent##entry; \ + cent##entry["name"] = mat->entry[i].name; \ + nlohmann::json centliteral##entry; \ + centliteral##entry[0] = mat->entry[i].literal[0]; \ + centliteral##entry[1] = mat->entry[i].literal[1]; \ + centliteral##entry[2] = mat->entry[i].literal[2]; \ + centliteral##entry[3] = mat->entry[i].literal[3]; \ + cent##entry["nameHash"] = mat->entry[i].nameHash; \ + cent##entry["literal"] = centliteral##entry; \ + carr##entry[i] = cent##entry; \ + } \ + matdata[#entry] = carr##entry; + +#define MATERIAL_DUMP_STATE_MAP(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + nlohmann::json cent##entry; \ + cent##entry[0] = mat->entry[i].loadBits[0]; \ + cent##entry[1] = mat->entry[i].loadBits[1]; \ + carr##entry[i] = cent##entry; \ + } \ + matdata[#entry] = carr##entry; + +#define MATERIAL_DUMP_BITS_ENTRY(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + carr##entry[i] = mat->entry[i]; \ + } \ + matdata[#entry] = carr##entry; + + +namespace ZoneTool +{ + namespace IW3 + { + void IMaterial::dump_statebits(Material* mat) + { + if (mat && mat->techniqueSet) + { + ITechset::dump_statebits(va("iw3/%s", mat->techniqueSet->name), mat->stateBitsEntry); + } + } + + //std::map mapped_keys + //{ + // { 0, 43 }, + // { 3, 0 }, + // { 4, 1 }, + // { 5, 2 }, + // { 6, 3 }, + // { 7, 4 }, + // { 8, 5 }, + // { 9, 6 }, + // { 10, 7 }, + // { 11, 8 }, + // { 12, 9 }, + // { 24, 13 }, + // { 38, 24 }, + // { 39, 25 }, + // { 40, 26 }, + // { 41, 27 }, + // { 42, 28 }, + // { 43, 29 }, + // { 48, 48 }, + // { 58, 51 }, + // { 59, 33 }, + //}; + + std::map mapped_keys + { + { 0, 43 }, + { 3, 0 }, + { 4, 1 }, + { 5, 2 }, + { 9, 6 }, // not sure! + { 10, 7 }, + { 11, 8 }, // not sure! + { 12, 0 }, + { 24, 9 }, + { 38, 28 }, // not sure! + { 39, 29 }, + { 43, 29 }, // was 47 but that crashes, not sure! + { 48, 48 }, + { 59, 53 }, + }; + + void IMaterial::dump(Material* mat, ZoneMemory* mem) + { + if (mat) + { + dump_statebits(mat); + + auto path = "materials\\"s + mat->name; + + auto file = FileSystem::FileOpen(path, "wb"); + if (!file) + { + return; + } + + nlohmann::json matdata; + + MATERIAL_DUMP_STRING(name); + + if (mat->techniqueSet) + { + matdata["techniqueSet->name"] = va("iw3/%s", mat->techniqueSet->name); + } + + MATERIAL_DUMP_INT(gameFlags); + // MATERIAL_DUMP_INT(sortKey); + MATERIAL_DUMP_INT(animationX); + MATERIAL_DUMP_INT(animationY); + + auto key_itr = mapped_keys.find(mat->sortKey); + if (key_itr != mapped_keys.end()) + { + matdata["sortKey"] = key_itr->second; + } + else + { + matdata["sortKey"] = mat->sortKey; + ZONETOOL_WARNING("[%s]: sortKey %u is not mapped!", mat->name, mat->sortKey); + } + + matdata["unknown"] = 0; + + MATERIAL_DUMP_INT(surfaceTypeBits); + MATERIAL_DUMP_INT(stateFlags); + MATERIAL_DUMP_INT(cameraRegion); + + MATERIAL_DUMP_CONST_ARRAY(constantTable, mat->constantCount); + MATERIAL_DUMP_STATE_MAP(stateMap, mat->stateBitsCount); + + nlohmann::json material_images; + for (int i = 0; i < mat->numMaps; i++) + { + nlohmann::json image; + + // watermap + if (mat->maps[i].semantic == 11) + { + water_t* waterData = reinterpret_cast(mat->maps[i].image); + + image["image"] = waterData->image->name; + + nlohmann::json waterdata; + waterdata["floatTime"] = waterData->writable.floatTime; + waterdata["codeConstant"][0] = waterData->codeConstant[0]; + waterdata["codeConstant"][1] = waterData->codeConstant[1]; + waterdata["codeConstant"][2] = waterData->codeConstant[2]; + waterdata["codeConstant"][3] = waterData->codeConstant[3]; + waterdata["M"] = waterData->M; + waterdata["N"] = waterData->N; + waterdata["Lx"] = waterData->Lx; + waterdata["Lz"] = waterData->Lz; + waterdata["gravity"] = waterData->gravity; + waterdata["windvel"] = waterData->windvel; + waterdata["winddir"][0] = waterData->winddir[0]; + waterdata["winddir"][1] = waterData->winddir[1]; + waterdata["amplitude"] = waterData->amplitude; + + nlohmann::json waterComplexData; + nlohmann::json wTerm; + + for (int i = 0; i < waterData->M * waterData->N; i++) + { + nlohmann::json complexdata; + nlohmann::json curWTerm; + + complexdata["real"] = waterData->H0[i].real; + complexdata["imag"] = waterData->H0[i].imag; + + curWTerm[i] = waterData->wTerm[i]; + + waterComplexData[i] = complexdata; + } + + waterdata["complex"] = waterComplexData; + waterdata["wTerm"] = wTerm; + + image["waterinfo"] = waterdata; + } + else + { + image["image"] = mat->maps[i].image->name; + } + + image["semantic"] = mat->maps[i].semantic; + image["sampleState"] = mat->maps[i].sampleState; + image["lastCharacter"] = mat->maps[i].secondLastCharacter; + image["firstCharacter"] = mat->maps[i].firstCharacter; + image["typeHash"] = mat->maps[i].typeHash; + + // add image data to material + material_images[i] = image; + } + matdata["maps"] = material_images; + + auto assetData = matdata.dump(4); + + // write data to disk + fwrite(&assetData[0], assetData.size(), 1, file); + FileSystem::FileClose(file); + } + } + } +} diff --git a/src/IW3/Assets/Material.hpp b/src/IW3/Assets/Material.hpp new file mode 100644 index 0000000..81d6435 --- /dev/null +++ b/src/IW3/Assets/Material.hpp @@ -0,0 +1,22 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IMaterial + { + public: + static void dump(Material* asset, ZoneMemory* mem); + static void dump_statebits(Material* mat); + }; + } +} diff --git a/src/IW3/Assets/Sound.cpp b/src/IW3/Assets/Sound.cpp new file mode 100644 index 0000000..16e03bc --- /dev/null +++ b/src/IW3/Assets/Sound.cpp @@ -0,0 +1,34 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW4/Assets/Sound.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + void ISound::dump(snd_alias_list_t* asset, ZoneMemory* mem) + { + const auto iw4_asset = mem->Alloc(); // new IW4::snd_alias_list_t; + memcpy(iw4_asset, asset, sizeof snd_alias_list_t); + + iw4_asset->head = mem->Alloc(iw4_asset->count); + memset(iw4_asset->head, 0, sizeof IW4::snd_alias_t * iw4_asset->count); + + for (auto i = 0; i < asset->count; i++) + { + memcpy(&iw4_asset->head[i], &asset->head[i], 16); + memcpy(&iw4_asset->head[i].soundFile, &asset->head[i].soundFile, sizeof snd_alias_t - 16 - 20); + memcpy(&iw4_asset->head[i].volumeFalloffCurve, &asset->head[i].volumeFalloffCurve, 20); + } + + IW4::ISound::dump(iw4_asset); + } + } +} diff --git a/src/IW3/Assets/Sound.hpp b/src/IW3/Assets/Sound.hpp new file mode 100644 index 0000000..42e251c --- /dev/null +++ b/src/IW3/Assets/Sound.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class ISound + { + public: + static void dump(snd_alias_list_t* asset, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Assets/Techset.cpp b/src/IW3/Assets/Techset.cpp new file mode 100644 index 0000000..5891969 --- /dev/null +++ b/src/IW3/Assets/Techset.cpp @@ -0,0 +1,449 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +#include "../IW4/Assets/VertexDecl.hpp" +#include "../IW4/Assets/VertexShader.hpp" +#include "../IW4/Assets/PixelShader.hpp" +#include "IW4/Assets/Techset.hpp" + +static const unsigned int crcTable[] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +std::uint32_t Crc32(void* buffer, const std::size_t size, const std::uint32_t initialCrc) +{ + auto curPtr = reinterpret_cast(buffer); + auto remaining = size; + auto crc = ~initialCrc; + + for (; remaining--; ++curPtr) + { + crc = (crc >> 8) ^ crcTable[(crc ^ *curPtr) & 0xFF]; + } + + return (~crc); +} + +namespace ZoneTool +{ + namespace IW3 + { + std::string GenerateNameForVertexDecl(MaterialVertexDeclaration* vertexDecl) + { + // base name + crc32 + auto name = "iw3_vertexdecl_"s; + auto crc32 = Crc32(vertexDecl->routing.data, sizeof MaterialVertexDeclaration, 0); + + // append crc32 to vertexdecl name + name += std::to_string(crc32); + + // return generated name + return name; + } + + IW4::VertexDecl* ITechset::dump_vertex_decl(const std::string& name, MaterialVertexDeclaration* vertex, ZoneMemory* mem) + { + // convert to IW4 + auto* asset = mem->Alloc(); + + asset->name = mem->StrDup(va("iw3/%s", name.data())); + + asset->hasOptionalSource = vertex->hasOptionalSource; + asset->streamCount = vertex->streamCount; + + memcpy(asset->streams, vertex->routing.data, sizeof asset->streams); + memcpy(asset->declarations, vertex->routing.decl, sizeof(void*) * 16); + + for (auto i = 0; i < asset->streamCount; i++) + { + if (asset->streams[i].dest >= 4) + { + asset->streams[i].dest += 1; + } + } + + // shit out a warning + if (asset->streamCount > 13) + { + ZONETOOL_ERROR("Vertexdecl %s has more than 13 streams.", name.data()); + } + + // lets pray it works + IW4::IVertexDecl::dump(asset); + + return asset; + } + + IW4::VertexShader* ITechset::dump_vertex_shader(MaterialVertexShader* shader, ZoneMemory* mem) + { + // convert to IW4 + auto* asset = mem->Alloc(); + + asset->name = mem->StrDup(va("iw3/%s", shader->name)); + asset->shader = shader->prog.vs; + asset->codeLen = shader->prog.loadDef.programSize; + asset->bytecode = PDWORD(shader->prog.loadDef.program); + + // dump shader + IW4::IVertexShader::dump(asset); + + return asset; + } + + IW4::PixelShader* ITechset::dump_pixel_shader(MaterialPixelShader* shader, ZoneMemory* mem) + { + // convert to IW4 + auto* asset = mem->Alloc(); + + asset->name = mem->StrDup(va("iw3/%s", shader->name)); + asset->shader = shader->prog.ps; + asset->codeLen = shader->prog.loadDef.programSize; + asset->bytecode = PDWORD(shader->prog.loadDef.program); + + // dump shader + IW4::IPixelShader::dump(asset); + + return asset; + } + + std::unordered_map iw3_technique_map = + { + {IW3::TECHNIQUE_DEPTH_PREPASS, IW4::TECHNIQUE_DEPTH_PREPASS}, + {IW3::TECHNIQUE_BUILD_FLOAT_Z, IW4::TECHNIQUE_BUILD_FLOAT_Z}, + {IW3::TECHNIQUE_BUILD_SHADOWMAP_DEPTH, IW4::TECHNIQUE_BUILD_SHADOWMAP_DEPTH}, + {IW3::TECHNIQUE_BUILD_SHADOWMAP_COLOR, IW4::TECHNIQUE_BUILD_SHADOWMAP_COLOR}, + {IW3::TECHNIQUE_UNLIT, IW4::TECHNIQUE_UNLIT}, + {IW3::TECHNIQUE_EMISSIVE, IW4::TECHNIQUE_EMISSIVE}, + {IW3::TECHNIQUE_EMISSIVE_SHADOW, IW4::TECHNIQUE_EMISSIVE_SHADOW}, + {IW3::TECHNIQUE_LIT_BEGIN, IW4::TECHNIQUE_LIT_BEGIN}, + {IW3::TECHNIQUE_LIT, IW4::TECHNIQUE_LIT}, + {IW3::TECHNIQUE_LIT_SUN, IW4::TECHNIQUE_LIT_SUN}, + {IW3::TECHNIQUE_LIT_SUN_SHADOW, IW4::TECHNIQUE_LIT_SUN_SHADOW}, + {IW3::TECHNIQUE_LIT_SPOT, IW4::TECHNIQUE_LIT_SPOT}, + {IW3::TECHNIQUE_LIT_SPOT_SHADOW, IW4::TECHNIQUE_LIT_SPOT_SHADOW}, + {IW3::TECHNIQUE_LIT_OMNI, IW4::TECHNIQUE_LIT_OMNI}, + {IW3::TECHNIQUE_LIT_OMNI_SHADOW, IW4::TECHNIQUE_LIT_OMNI_SHADOW}, + {IW3::TECHNIQUE_LIT_INSTANCED, IW4::TECHNIQUE_LIT_INSTANCED}, + {IW3::TECHNIQUE_LIT_INSTANCED_SUN, IW4::TECHNIQUE_LIT_INSTANCED_SUN}, + {IW3::TECHNIQUE_LIT_INSTANCED_SUN_SHADOW, IW4::TECHNIQUE_LIT_INSTANCED_SUN_SHADOW}, + {IW3::TECHNIQUE_LIT_INSTANCED_SPOT, IW4::TECHNIQUE_LIT_INSTANCED_SPOT}, + {IW3::TECHNIQUE_LIT_INSTANCED_OMNI, IW4::TECHNIQUE_LIT_INSTANCED_OMNI}, + {IW3::TECHNIQUE_LIT_END, IW4::TECHNIQUE_LIT_END}, + {IW3::TECHNIQUE_LIGHT_SPOT, IW4::TECHNIQUE_LIGHT_SPOT}, + {IW3::TECHNIQUE_LIGHT_OMNI, IW4::TECHNIQUE_LIGHT_OMNI}, + {IW3::TECHNIQUE_LIGHT_SPOT_SHADOW, IW4::TECHNIQUE_LIGHT_SPOT_SHADOW}, + {IW3::TECHNIQUE_FAKELIGHT_NORMAL, IW4::TECHNIQUE_FAKELIGHT_NORMAL}, + {IW3::TECHNIQUE_FAKELIGHT_VIEW, IW4::TECHNIQUE_FAKELIGHT_VIEW}, + {IW3::TECHNIQUE_SUNLIGHT_PREVIEW, IW4::TECHNIQUE_SUNLIGHT_PREVIEW}, + {IW3::TECHNIQUE_CASE_TEXTURE, IW4::TECHNIQUE_CASE_TEXTURE}, + {IW3::TECHNIQUE_WIREFRAME_SOLID, IW4::TECHNIQUE_WIREFRAME_SOLID}, + {IW3::TECHNIQUE_WIREFRAME_SHADED, IW4::TECHNIQUE_WIREFRAME_SHADED}, + {IW3::TECHNIQUE_SHADOWCOOKIE_CASTER, 0xFFFFFFFF}, + {IW3::TECHNIQUE_SHADOWCOOKIE_RECEIVER, 0xFFFFFFFF}, + {IW3::TECHNIQUE_DEBUG_BUMPMAP, IW4::TECHNIQUE_DEBUG_BUMPMAP}, + {IW3::TECHNIQUE_DEBUG_BUMPMAP_INSTANCED, IW4::TECHNIQUE_DEBUG_BUMPMAP_INSTANCED}, + {IW3::TECHNIQUE_COUNT, IW4::TECHNIQUE_COUNT}, + {IW3::TECHNIQUE_TOTAL_COUNT, IW4::TECHNIQUE_TOTAL_COUNT}, + {IW3::TECHNIQUE_NONE, IW4::TECHNIQUE_NONE}, + }; + + std::unordered_map iw3_code_const_map = + { + {IW3::CONST_SRC_CODE_LIGHT_POSITION, IW4::CONST_SRC_CODE_LIGHT_POSITION}, + {IW3::CONST_SRC_CODE_LIGHT_DIFFUSE, IW4::CONST_SRC_CODE_LIGHT_DIFFUSE}, + {IW3::CONST_SRC_CODE_LIGHT_SPECULAR, IW4::CONST_SRC_CODE_LIGHT_SPECULAR}, + {IW3::CONST_SRC_CODE_LIGHT_SPOTDIR, IW4::CONST_SRC_CODE_LIGHT_SPOTDIR}, + {IW3::CONST_SRC_CODE_LIGHT_SPOTFACTORS, IW4::CONST_SRC_CODE_LIGHT_SPOTFACTORS}, + {IW3::CONST_SRC_CODE_NEARPLANE_ORG, IW4::CONST_SRC_CODE_NEARPLANE_ORG}, + {IW3::CONST_SRC_CODE_NEARPLANE_DX, IW4::CONST_SRC_CODE_NEARPLANE_DX}, + {IW3::CONST_SRC_CODE_NEARPLANE_DY, IW4::CONST_SRC_CODE_NEARPLANE_DY}, + + {IW3::CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET, IW4::CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET}, + {IW3::CONST_SRC_CODE_RENDER_TARGET_SIZE, IW4::CONST_SRC_CODE_RENDER_TARGET_SIZE}, + {IW3::CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT, IW4::CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT}, + { + IW3::CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR, + IW4::CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR + }, + {IW3::CONST_SRC_CODE_DOF_EQUATION_SCENE, IW4::CONST_SRC_CODE_DOF_EQUATION_SCENE}, + {IW3::CONST_SRC_CODE_DOF_LERP_SCALE, IW4::CONST_SRC_CODE_DOF_LERP_SCALE}, + {IW3::CONST_SRC_CODE_DOF_LERP_BIAS, IW4::CONST_SRC_CODE_DOF_LERP_BIAS}, + {IW3::CONST_SRC_CODE_DOF_ROW_DELTA, IW4::CONST_SRC_CODE_DOF_ROW_DELTA}, + {IW3::CONST_SRC_CODE_PARTICLE_CLOUD_COLOR, IW4::CONST_SRC_CODE_PARTICLE_CLOUD_COLOR}, + {IW3::CONST_SRC_CODE_GAMETIME, IW4::CONST_SRC_CODE_GAMETIME}, + + {IW3::CONST_SRC_CODE_PIXEL_COST_FRACS, IW4::CONST_SRC_CODE_PIXEL_COST_FRACS}, + {IW3::CONST_SRC_CODE_PIXEL_COST_DECODE, IW4::CONST_SRC_CODE_PIXEL_COST_DECODE}, + {IW3::CONST_SRC_CODE_FILTER_TAP_0, IW4::CONST_SRC_CODE_FILTER_TAP_0}, + {IW3::CONST_SRC_CODE_FILTER_TAP_1, IW4::CONST_SRC_CODE_FILTER_TAP_1}, + {IW3::CONST_SRC_CODE_FILTER_TAP_2, IW4::CONST_SRC_CODE_FILTER_TAP_2}, + {IW3::CONST_SRC_CODE_FILTER_TAP_3, IW4::CONST_SRC_CODE_FILTER_TAP_3}, + {IW3::CONST_SRC_CODE_FILTER_TAP_4, IW4::CONST_SRC_CODE_FILTER_TAP_4}, + {IW3::CONST_SRC_CODE_FILTER_TAP_5, IW4::CONST_SRC_CODE_FILTER_TAP_5}, + {IW3::CONST_SRC_CODE_FILTER_TAP_6, IW4::CONST_SRC_CODE_FILTER_TAP_6}, + {IW3::CONST_SRC_CODE_FILTER_TAP_7, IW4::CONST_SRC_CODE_FILTER_TAP_7}, + {IW3::CONST_SRC_CODE_COLOR_MATRIX_R, IW4::CONST_SRC_CODE_COLOR_MATRIX_R}, + {IW3::CONST_SRC_CODE_COLOR_MATRIX_G, IW4::CONST_SRC_CODE_COLOR_MATRIX_G}, + {IW3::CONST_SRC_CODE_COLOR_MATRIX_B, IW4::CONST_SRC_CODE_COLOR_MATRIX_B}, + + {IW3::CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION, IW4::CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION}, + {IW3::CONST_SRC_CODE_SHADOWMAP_SCALE, IW4::CONST_SRC_CODE_SHADOWMAP_SCALE}, + {IW3::CONST_SRC_CODE_ZNEAR, IW4::CONST_SRC_CODE_ZNEAR}, + {IW3::CONST_SRC_CODE_SUN_POSITION, IW4::CONST_SRC_CODE_LIGHT_POSITION}, + {IW3::CONST_SRC_CODE_SUN_DIFFUSE, IW4::CONST_SRC_CODE_LIGHT_DIFFUSE}, + {IW3::CONST_SRC_CODE_SUN_SPECULAR, IW4::CONST_SRC_CODE_LIGHT_SPECULAR}, + {IW3::CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE, IW4::CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE}, + {IW3::CONST_SRC_CODE_DEBUG_BUMPMAP, IW4::CONST_SRC_CODE_DEBUG_BUMPMAP}, + {IW3::CONST_SRC_CODE_MATERIAL_COLOR, IW4::CONST_SRC_CODE_MATERIAL_COLOR}, + {IW3::CONST_SRC_CODE_FOG, IW4::CONST_SRC_CODE_FOG}, + {IW3::CONST_SRC_CODE_FOG_COLOR, IW4::CONST_SRC_CODE_FOG_COLOR_LINEAR}, + {IW3::CONST_SRC_CODE_GLOW_SETUP, IW4::CONST_SRC_CODE_GLOW_SETUP}, + {IW3::CONST_SRC_CODE_GLOW_APPLY, IW4::CONST_SRC_CODE_GLOW_APPLY}, + {IW3::CONST_SRC_CODE_COLOR_BIAS, IW4::CONST_SRC_CODE_COLOR_BIAS}, + {IW3::CONST_SRC_CODE_COLOR_TINT_BASE, IW4::CONST_SRC_CODE_COLOR_TINT_BASE}, + {IW3::CONST_SRC_CODE_COLOR_TINT_DELTA, IW4::CONST_SRC_CODE_COLOR_TINT_DELTA}, + {IW3::CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS, IW4::CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS}, + {IW3::CONST_SRC_CODE_ENVMAP_PARMS, IW4::CONST_SRC_CODE_ENVMAP_PARMS}, + {IW3::CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST, IW4::CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST}, + {IW3::CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE, IW4::CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE}, + {IW3::CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET, IW4::CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET}, + {IW3::CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX, IW4::CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX0}, + {IW3::CONST_SRC_CODE_DEPTH_FROM_CLIP, IW4::CONST_SRC_CODE_DEPTH_FROM_CLIP}, + {IW3::CONST_SRC_CODE_CODE_MESH_ARG_0, IW4::CONST_SRC_CODE_CODE_MESH_ARG_0}, + {IW3::CONST_SRC_CODE_CODE_MESH_ARG_1, IW4::CONST_SRC_CODE_CODE_MESH_ARG_1}, + {IW3::CONST_SRC_CODE_CODE_MESH_ARG_LAST, IW4::CONST_SRC_CODE_CODE_MESH_ARG_LAST}, + {IW3::CONST_SRC_CODE_BASE_LIGHTING_COORDS, IW4::CONST_SRC_CODE_BASE_LIGHTING_COORDS}, + + {IW3::CONST_SRC_CODE_COUNT_FLOAT4, IW4::CONST_SRC_CODE_COUNT_FLOAT4}, + {IW3::CONST_SRC_FIRST_CODE_MATRIX, IW4::CONST_SRC_FIRST_CODE_MATRIX}, + {IW3::CONST_SRC_CODE_WORLD_MATRIX, IW4::CONST_SRC_CODE_WORLD_MATRIX0}, + {IW3::CONST_SRC_CODE_INVERSE_WORLD_MATRIX, IW4::CONST_SRC_CODE_INVERSE_WORLD_MATRIX0}, + {IW3::CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX, IW4::CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0}, + {IW3::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX, IW4::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX0}, + {IW3::CONST_SRC_CODE_VIEW_MATRIX, IW4::CONST_SRC_CODE_VIEW_MATRIX}, + {IW3::CONST_SRC_CODE_INVERSE_VIEW_MATRIX, IW4::CONST_SRC_CODE_INVERSE_VIEW_MATRIX}, + {IW3::CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX, IW4::CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX}, + {IW3::CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX, IW4::CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX}, + {IW3::CONST_SRC_CODE_PROJECTION_MATRIX, IW4::CONST_SRC_CODE_PROJECTION_MATRIX}, + {IW3::CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX, IW4::CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX}, + {IW3::CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX, IW4::CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX}, + { + IW3::CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX, + IW4::CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX + }, + {IW3::CONST_SRC_CODE_WORLD_VIEW_MATRIX, IW4::CONST_SRC_CODE_WORLD_VIEW_MATRIX0}, + {IW3::CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX, IW4::CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX0}, + {IW3::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX, IW4::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX0}, + { + IW3::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX, + IW4::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX0 + }, + {IW3::CONST_SRC_CODE_VIEW_PROJECTION_MATRIX, IW4::CONST_SRC_CODE_VIEW_PROJECTION_MATRIX}, + {IW3::CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX, IW4::CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX}, + { + IW3::CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX, + IW4::CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX + }, + { + IW3::CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX, + IW4::CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX + }, + {IW3::CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX, IW4::CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX0}, + { + IW3::CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX, + IW4::CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX0 + }, + { + IW3::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX, + IW4::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 + }, + { + IW3::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX, + IW4::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 + }, + {IW3::CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX, IW4::CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX}, + {IW3::CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX, IW4::CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX}, + {IW3::CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX, IW4::CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX}, + { + IW3::CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX, + IW4::CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX + }, + {IW3::CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX, IW4::CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX}, + { + IW3::CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX, + IW4::CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX + }, + { + IW3::CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX, + IW4::CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX + }, + { + IW3::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX, + IW4::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX + }, + }; + + void ITechset::dump_statebits(const std::string& techset, char* statebits) + { + char iw4_statebits[48]; + memset(iw4_statebits, 0xFF, sizeof iw4_statebits); + + for (int i = 0; i < 34; i++) + { + const auto itr = iw3_technique_map.find(i); + if (itr != iw3_technique_map.end()) + { + if (itr->second >= 0) + { + iw4_statebits[itr->second] = statebits[i]; + + if (iw4_statebits[itr->second] >= 7) + { + iw4_statebits[itr->second] += 1; + } + + if (itr->second >= 5 && itr->second <= 36) + { + iw4_statebits[itr->second + 1] = iw4_statebits[itr->second]; + } + } + } + } + + IW4::ITechset::dump_statebits(techset, iw4_statebits); + } + + void ITechset::dump(MaterialTechniqueSet* asset, ZoneMemory* mem) + { + auto* iw4_techset = mem->Alloc(); + + iw4_techset->name = mem->StrDup(va("iw3/%s", asset->name)); + iw4_techset->pad = asset->pad; + + for (int i = 0; i < 34; i++) + { + const auto itr = iw3_technique_map.find(i); + if (itr != iw3_technique_map.end()) + { + if (asset->techniques[i] && itr->second >= 0) + { + const auto size = sizeof(IW4::MaterialTechniqueHeader) + (sizeof(IW4::MaterialPass) * asset->techniques[i]->hdr.numPasses); + iw4_techset->techniques[itr->second] = mem->ManualAlloc(size); + + if (itr->second >= 5 && itr->second <= 36) + { + iw4_techset->techniques[itr->second + 1] = iw4_techset->techniques[itr->second]; + } + + memcpy(iw4_techset->techniques[itr->second], asset->techniques[i], size); + + auto& iw3_technique = asset->techniques[i]; + auto& technique = iw4_techset->techniques[itr->second]; + + technique->hdr.name = mem->StrDup(va("iw3/%s", technique->hdr.name)); + + if ((technique->hdr.flags & 0x10) == 0x10) + { + technique->hdr.flags &= ~0x10; + technique->hdr.flags |= 0x40; + } + + for (short pass = 0; pass < technique->hdr.passCount; pass++) + { + auto* iw3_pass_def = &iw3_technique->pass[pass]; + auto* pass_def = &technique->pass[pass]; + + auto vertex_decl_name = GenerateNameForVertexDecl(iw3_pass_def->vertexDecl); + + if (iw3_pass_def->pixelShader) pass_def->pixelShader = dump_pixel_shader(iw3_pass_def->pixelShader, mem); + if (iw3_pass_def->vertexDecl) pass_def->vertexDecl = dump_vertex_decl(vertex_decl_name, iw3_pass_def->vertexDecl, mem); + if (iw3_pass_def->vertexShader) pass_def->vertexShader = dump_vertex_shader(iw3_pass_def->vertexShader, mem); + + const auto arg_count = pass_def->perPrimArgCount + pass_def->perObjArgCount + pass_def->stableArgCount; + if (arg_count > 0) + { + pass_def->argumentDef = mem->Alloc(arg_count); + memcpy(pass_def->argumentDef, iw3_pass_def->args, sizeof(IW4::ShaderArgumentDef) * arg_count); + } + + for (auto arg = 0; arg < pass_def->perPrimArgCount + pass_def->perObjArgCount + pass_def->stableArgCount; arg++) + { + auto* arg_def = &pass_def->argumentDef[arg]; + if (arg_def->type == 3 || arg_def->type == 5) + { + if (iw3_code_const_map.find(arg_def->u.codeConst.index) != iw3_code_const_map.end()) + { + arg_def->u.codeConst.index = iw3_code_const_map[arg_def->u.codeConst.index]; + } + else + { + if (IsDebuggerPresent()) + { + __debugbreak(); + } + else + { + ZONETOOL_WARNING("Missing const mapping for constant %u!", arg_def->u.codeConst.index); + } + } + } + } + } + } + } + } + + IW4::ITechset::dump(iw4_techset); + } + } +} diff --git a/src/IW3/Assets/Techset.hpp b/src/IW3/Assets/Techset.hpp new file mode 100644 index 0000000..c1899b1 --- /dev/null +++ b/src/IW3/Assets/Techset.hpp @@ -0,0 +1,27 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once +#include "IW4/Structs.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + class ITechset + { + public: + static void dumpTechniquePass(MaterialTechnique* asset); + static void dump(MaterialTechniqueSet* asset, ZoneMemory* mem); + static IW4::VertexDecl* dump_vertex_decl(const std::string& name, MaterialVertexDeclaration* vertex, ZoneMemory* mem); + static IW4::VertexShader* dump_vertex_shader(MaterialVertexShader* shader, ZoneMemory* mem); + static IW4::PixelShader* dump_pixel_shader(MaterialPixelShader* shader, ZoneMemory* mem); + static void dump_statebits(const std::string& techset, char* statebits); + }; + } +} diff --git a/src/IW3/Assets/XAnimParts.cpp b/src/IW3/Assets/XAnimParts.cpp new file mode 100644 index 0000000..e3fe8fb --- /dev/null +++ b/src/IW3/Assets/XAnimParts.cpp @@ -0,0 +1,76 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW4/Assets/XAnimParts.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + void IXAnimParts::dump(XAnimParts* anim, ZoneMemory* mem) + { + if (anim) + { + auto asset = mem->Alloc(); + +#define XAE_CopyElement(name) asset->name = anim->name + + XAE_CopyElement(name); + XAE_CopyElement(dataByteCount); + XAE_CopyElement(dataShortCount); + XAE_CopyElement(dataIntCount); + XAE_CopyElement(randomDataByteCount); + XAE_CopyElement(randomDataIntCount); + XAE_CopyElement(framecount); + + asset->flags = 0; + if (anim->bLoop) + { + asset->flags |= IW4::ANIM_LOOP; + } + if (anim->bDelta) + { + asset->flags |= IW4::ANIM_DELTA; + } + + for (auto i = 0; i < 10; i++) + { + XAE_CopyElement(boneCount[i]); + } + + XAE_CopyElement(notifyCount); + XAE_CopyElement(assetType); + XAE_CopyElement(isDefault); + XAE_CopyElement(randomDataShortCount); + XAE_CopyElement(indexcount); + XAE_CopyElement(framerate); + XAE_CopyElement(frequency); + XAE_CopyElement(tagnames); + XAE_CopyElement(dataByte); + XAE_CopyElement(dataShort); + XAE_CopyElement(dataInt); + XAE_CopyElement(randomDataShort); + XAE_CopyElement(randomDataByte); + XAE_CopyElement(randomDataInt); + XAE_CopyElement(indices.data); + asset->notify = reinterpret_cast(anim->notify); + + if (anim->delta) + { + asset->delta = mem->Alloc(); + asset->delta->quat = reinterpret_cast(anim->delta->quat); + asset->delta->trans = reinterpret_cast(anim->delta->trans); + } + + // dump asset + IW4::IXAnimParts::dump(asset, SL_ConvertToString); + } + } + } +} diff --git a/src/IW3/Assets/XAnimParts.hpp b/src/IW3/Assets/XAnimParts.hpp new file mode 100644 index 0000000..ed45ed2 --- /dev/null +++ b/src/IW3/Assets/XAnimParts.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IXAnimParts + { + public: + static void dump(XAnimParts* anim, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Assets/XModel.cpp b/src/IW3/Assets/XModel.cpp new file mode 100644 index 0000000..af3b95a --- /dev/null +++ b/src/IW3/Assets/XModel.cpp @@ -0,0 +1,146 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW4/Assets/Xmodel.hpp" +#include "../IW4/Assets/XSurface.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + IW4::XSurface* GenerateIW4Surface(XSurface* asset, ZoneMemory* mem) + { + // allocate IW4 XSurface structure + const auto xsurface = mem->Alloc(); + + xsurface->tileMode = asset->tileMode; + xsurface->deformed = asset->deformed; + xsurface->vertCount = asset->vertCount; + xsurface->triCount = asset->triCount; + xsurface->zoneHandle = asset->zoneHandle; + xsurface->baseTriIndex = asset->baseTriIndex; + xsurface->baseVertIndex = asset->baseVertIndex; + xsurface->triIndices = reinterpret_cast(asset->triIndices); + memcpy(&xsurface->vertexInfo, &asset->vertInfo, sizeof IW4::XSurfaceVertexInfo); + xsurface->verticies = reinterpret_cast(asset->verts0); + xsurface->vertListCount = asset->vertListCount; + xsurface->rigidVertLists = reinterpret_cast(asset->vertList); + memcpy(&xsurface->partBits, &asset->partBits, sizeof(int[4])); + + return xsurface; + } + + IW4::XModel* GenerateIW4Model(XModel* asset, ZoneMemory* mem) + { + // allocate IW4 XModel structure + const auto xmodel = mem->Alloc(); + + // copy data over + xmodel->name = const_cast(asset->name); + xmodel->numBones = asset->numBones; + xmodel->numRootBones = asset->numRootBones; + xmodel->numSurfaces = asset->numsurfs; + xmodel->lodRampType = asset->lodRampType; + xmodel->scale = 1.0f; + memset(xmodel->noScalePartBits, 0, sizeof(int) * 6); + xmodel->parentList = reinterpret_cast(asset->parentList); + xmodel->boneNames = reinterpret_cast(asset->boneNames); + + xmodel->tagAngles = reinterpret_cast(asset->quats); + xmodel->tagPositions = reinterpret_cast(asset->trans); + + xmodel->partClassification = asset->partClassification; + xmodel->animMatrix = reinterpret_cast(asset->baseMat); + xmodel->materials = reinterpret_cast(asset->materialHandles); + + // convert level of detail data + for (int i = 0; i < asset->numLods; i++) + { + xmodel->lods[i].dist = asset->lodInfo[i].dist; + xmodel->lods[i].numSurfacesInLod = asset->lodInfo[i].numsurfs; + xmodel->lods[i].surfIndex = asset->lodInfo[i].surfIndex; + memcpy(xmodel->lods[i].partBits, asset->lodInfo[i].partBits, sizeof(int[4])); + memcpy(&xmodel->lods[i].lod, &asset->lodInfo[i].lod, 3); + + // generate ModelSurface object + xmodel->lods[i].surfaces = mem->Alloc();; + + xmodel->lods[i].surfaces->name = mem->StrDup(va("zonetool_%s_%u", xmodel->name, i).data()); + xmodel->lods[i].surfaces->numsurfs = xmodel->lods[i].numSurfacesInLod; + memcpy(xmodel->lods[i].surfaces->partBits, asset->lodInfo[i].partBits, sizeof(int[4])); + + // allocate xsurficies + xmodel->lods[i].surfaces->surfs = mem->Alloc(xmodel->lods[i].numSurfacesInLod); + + // loop through surfaces in current Level-of-Detail + for (int surf = xmodel->lods[i].surfIndex; surf < + xmodel->lods[i].surfIndex + xmodel->lods[i].numSurfacesInLod; surf++) + { + // generate iw4 surface + const auto surface = GenerateIW4Surface(&asset->surfs[surf], mem); + + // copy XSurface into iw4 structure + memcpy( + &xmodel->lods[i].surfaces->surfs[surf - xmodel->lods[i].surfIndex], + surface, + sizeof IW4::XSurface + ); + } + } + + xmodel->numLods = asset->numLods; + xmodel->collLod = asset->collLod; + xmodel->flags = asset->flags; + + xmodel->colSurf = reinterpret_cast(asset->collSurfs); + xmodel->numColSurfs = asset->numCollSurfs; + xmodel->contents = asset->contents; + + // convert colsurf bounds + for (int i = 0; i < xmodel->numColSurfs; i++) + { + xmodel->colSurf[i].bounds.compute(); + } + + // convert boneinfo + xmodel->boneInfo = mem->Alloc(xmodel->numBones); + for (int i = 0; i < xmodel->numBones; i++) + { + memcpy(&xmodel->boneInfo[i].bounds, &asset->boneInfo[i].bounds, sizeof(Bounds)); + + xmodel->boneInfo[i].packedBounds.compute(); + xmodel->boneInfo[i].radiusSquared = asset->boneInfo[i].radiusSquared; + } + + xmodel->radius = asset->radius; + xmodel->memUsage = asset->memUsage; + xmodel->bad = asset->bad; + xmodel->physPreset = reinterpret_cast(asset->physPreset); + + xmodel->bounds.compute(asset->mins, asset->maxs); + + return xmodel; + } + + void IXModel::dump(XModel* asset, ZoneMemory* mem) + { + // generate iw4 model + auto iw4_model = GenerateIW4Model(asset, mem); + + // dump iw4 model + IW4::IXModel::dump(iw4_model, SL_ConvertToString); + + // dump all xsurfaces + for (int i = 0; i < iw4_model->numLods; i++) + { + IW4::IXSurface::dump(iw4_model->lods[i].surfaces); + } + } + } +} diff --git a/src/IW3/Assets/XModel.hpp b/src/IW3/Assets/XModel.hpp new file mode 100644 index 0000000..5c3d8be --- /dev/null +++ b/src/IW3/Assets/XModel.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + class IXModel + { + public: + static void dump(XModel* asset, ZoneMemory* mem); + }; + } +} diff --git a/src/IW3/Functions.hpp b/src/IW3/Functions.hpp new file mode 100644 index 0000000..38ca8db --- /dev/null +++ b/src/IW3/Functions.hpp @@ -0,0 +1,38 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +//!!!!!!!!!!!!!!!!! +//!!!!!!!TODO!!!!!! +//!!!CHANGE OFFSETS +//!!!!!!!!!!!!!!!!! + +namespace ZoneTool +{ + namespace IW3 + { + union XAssetHeader; + + static Function DB_FindXAssetHeader = 0x489570; + static Function DB_LoadXAssets; //add offset + + typedef int (__cdecl * DB_GetXAssetSizeHandler_t)(); + static DB_GetXAssetSizeHandler_t* DB_GetXAssetSizeHandlers = (DB_GetXAssetSizeHandler_t*)0x726A10; + + static const char* SL_ConvertToString(std::uint16_t index) + { + return reinterpret_cast(*reinterpret_cast(0x14E8A04) + 12 * index + 4); + } + + static short SL_AllocString(const std::string& string) + { + // TODO + } + } +} diff --git a/src/IW3/IW3.cpp b/src/IW3/IW3.cpp new file mode 100644 index 0000000..fa6b7a9 --- /dev/null +++ b/src/IW3/IW3.cpp @@ -0,0 +1,365 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +// #include "ZoneTool.hpp" + +namespace ZoneTool +{ + namespace IW3 + { + bool isDumping = false; + bool isVerifying = false; + auto currentDumpingZone = ""s; + + Linker::Linker() + { + } + + Linker::~Linker() + { + } + + const char* Linker::version() + { + return "CoD4"; + } + + bool Linker::is_used() + { + return !strncmp(reinterpret_cast(0x006CF584), this->version(), 4); + } + + typedef void*(__cdecl * Dvar_RegisterBool_t)(const char*, bool, unsigned int, const char*); + Dvar_RegisterBool_t Dvar_RegisterBool = (Dvar_RegisterBool_t)0x56C600; + + void* Linker::Dedicated_RegisterDvarBool(const char* name, bool defaultValue, unsigned int flags, + const char* description) + { + return Dvar_RegisterBool(name, true, 0x2000, description); + } + + void** DB_XAssetPool = (void**)0x7265E0; + unsigned int* g_poolSize = (unsigned int*)0x7263A0; + + void* DB_FindXAssetHeader_Unsafe(const XAssetType type, const std::string& name) + { + const static auto DB_FindXAssetHeader_Internal = 0x4892A0; + const auto name_ptr = name.data(); + const auto type_int = static_cast(type); + + const XAsset* asset_header = nullptr; + + __asm + { + mov edi, name_ptr; + push type_int; + call DB_FindXAssetHeader_Internal; + add esp, 4; + mov asset_header, eax; + } + + return (asset_header) ? asset_header->ptr.data : nullptr; + } + + const char* Linker::GetAssetName(XAsset* asset) + { + // todo + if (asset->type == image) + { + return asset->ptr.image->name; + } + if (asset->type == menu) + { + // return asset->ptr.menu->name; + } + else + { + return asset->ptr.rawfile->name; + } + + return ""; + } + + void Linker::HandleAsset(XAsset* asset) + { + static std::shared_ptr memory; + static std::vector> referencedAssets; + + if (!memory) + { + memory = std::make_shared(1024 * 1024 * 128); // 128mb + } + + // nice meme + if (isVerifying) + { + // print asset name to console + ZONETOOL_INFO("Loading asset \"%s\" of type %s.", Linker::GetAssetName(asset), reinterpret_cast( +0x00726840)[asset->type]); + } + +#define DECLARE_ASSET(__TYPE__, __ASSET__) \ + if (asset->type == __TYPE__) \ + { \ + __ASSET__::dump(asset->ptr.__TYPE__, memory.get()); \ + } + + // fastfile name + auto fastfile = static_cast(*(const char**)0xE344CC); + + if (asset->type == rawfile && GetAssetName(asset) == currentDumpingZone) + { + for (auto& ref : referencedAssets) + { + if (ref.second.length() <= 1 || ref.first == XAssetType::loaded_sound) + { + continue; + } + + const auto asset_name = &ref.second[1]; + const auto ref_asset = DB_FindXAssetHeader_Unsafe(ref.first, asset_name); + + if (ref_asset == nullptr) + { + ZONETOOL_ERROR("Could not find referenced asset \"%s\"!", asset_name); + continue; + } + + XAsset asset; + asset.type = ref.first; + asset.ptr.data = ref_asset; + + ZONETOOL_INFO("Dumping additional asset \"%s\" because it is referenced by %s.", asset_name, currentDumpingZone.data()); + + HandleAsset(&asset); + } + + ZONETOOL_INFO("Zone \"%s\" dumped.", &fastfile[0]); + + // clear referenced assets array because we are done dumping + referencedAssets.clear(); + + // free memory + memory->Free(); + memory = nullptr; + + FileSystem::SetFastFile(""); + isDumping = false; + isVerifying = false; + } + + // dump shit + if (isDumping) + { + FileSystem::SetFastFile(fastfile); + + // check if the asset is a reference asset + if (GetAssetName(asset)[0] == ',') + { + referencedAssets.push_back({asset->type, GetAssetName(asset)}); + } + else + { + // DECLARE_ASSET(image, IGfxImage); + DECLARE_ASSET(xmodel, IXModel); + DECLARE_ASSET(material, IMaterial); + DECLARE_ASSET(xanim, IXAnimParts); + DECLARE_ASSET(techset, ITechset); + DECLARE_ASSET(loaded_sound, ILoadedSound); + DECLARE_ASSET(sound, ISound); + DECLARE_ASSET(fx, IFxEffectDef); + DECLARE_ASSET(gfx_map, IGfxWorld); + DECLARE_ASSET(col_map_mp, IClipMap); + DECLARE_ASSET(map_ents, IMapEnts); + DECLARE_ASSET(com_map, IComWorld); + } + } + } + + void* Linker::DB_AddXAsset(XAsset* asset, int unk) + { + HandleAsset(asset); + + // call original function + return Memory::func(0x489B00)(asset, unk); + } + + void* ReallocateAssetPool(uint32_t type, unsigned int newSize) + { + int elSize = DB_GetXAssetSizeHandlers[type](); + + void* poolEntry = malloc(newSize * elSize); + DB_XAssetPool[type] = poolEntry; + g_poolSize[type] = newSize; + + return poolEntry; + } + + void* ReallocateAssetPoolM(uint32_t type, int multiplier) + { + int elSize = DB_GetXAssetSizeHandlers[type](); + int newSize = multiplier * g_poolSize[type]; + + void* poolEntry = malloc(newSize * elSize); + DB_XAssetPool[type] = poolEntry; + g_poolSize[type] = newSize; + + return poolEntry; + } + + void Com_PrintfHook(int channel, const char* data, int unk) + { + printf(data); + } + + void Linker::startup() + { + if (this->is_used()) + { + // Realloc asset pools + ReallocateAssetPoolM(localize, 2); + ReallocateAssetPoolM(material, 2); + ReallocateAssetPoolM(font, 2); + ReallocateAssetPoolM(image, 2); + ReallocateAssetPoolM(techset, 2); + ReallocateAssetPoolM(fx, 4); + ReallocateAssetPoolM(xanim, 2); + ReallocateAssetPoolM(xmodel, 2); + ReallocateAssetPoolM(physpreset, 2); + ReallocateAssetPoolM(weapon, 2); + ReallocateAssetPoolM(game_map_sp, 2); + ReallocateAssetPoolM(game_map_mp, 2); + ReallocateAssetPoolM(map_ents, 5); + ReallocateAssetPoolM(com_map, 5); + ReallocateAssetPoolM(col_map_mp, 5); + ReallocateAssetPoolM(gfx_map, 5); + ReallocateAssetPoolM(rawfile, 2); + ReallocateAssetPoolM(loaded_sound, 2); + ReallocateAssetPoolM(sound, 2); + ReallocateAssetPoolM(stringtable, 2); + + // Asset dump hook + Memory(0x00489E72).call(DB_AddXAsset); + + // Always use dedicated mode + Memory(0x4FEA9E).call(Dedicated_RegisterDvarBool); + Memory(0x4FEAC2).call(Dedicated_RegisterDvarBool); + Memory(0x4FFE37).call(Dedicated_RegisterDvarBool); + Memory(0x4FFE5D).call(Dedicated_RegisterDvarBool); + + // Don't touch image data + Memory(0x616E9C).nop(3); + + // idc if you can't initialise PunkBuster + Memory(0x5776DF).nop(5); + Memory(0x5776EC).nop(5); + + // Initialise console_mp.log + Memory(0x4FCBA3).nop(2); + + // We don't need recommended settings + Memory(0x4FE99A).set(0xEB); + Memory(0x4FE993).nop(7); + + // We do not need to load the config_mp.cfg + Memory(0x55EEA6).set(0xEB); + + // Don't give a frametime warning + Memory(0x4FFD9D).nop(5); + + // Disabling loadedsound touching + Memory(0x4794C2).nop(5); + + // No huffmann message + Memory(0x507982).nop(5); + + // Disable console window + Memory(0x0046CE55).nop(5); + + // Obtain console output from IW3 + Memory(0x4FCC00).call(Com_PrintfHook); + } + } + + std::shared_ptr Linker::alloc_zone(const std::string& zone) + { + ZONETOOL_ERROR("AllocZone called but IW3 is not intended to compile zones!"); + return nullptr; + } + + std::shared_ptr Linker::alloc_buffer() + { + ZONETOOL_ERROR("AllocBuffer called but IW3 is not intended to compile zones!"); + return nullptr; + } + + void Linker::load_zone(const std::string& name) + { + static XZoneInfo zone; + zone.zone = _strdup(&name[0]); + zone.loadFlags = 0; + zone.unloadFlags = 0; + + Memory::func(0x48A2B0)(&zone, 1, 0); + } + + void Linker::unload_zones() + { + } + + bool Linker::is_valid_asset_type(const std::string& type) + { + return this->type_to_int(type) >= 0; + } + + std::int32_t Linker::type_to_int(std::string type) + { + auto xassettypes = reinterpret_cast(0x00726840); + + for (std::int32_t i = 0; i < max; i++) + { + if (xassettypes[i] == type) + return i; + } + + return -1; + } + + std::string Linker::type_to_string(std::int32_t type) + { + auto xassettypes = reinterpret_cast(0x00726840); + return xassettypes[type]; + } + + bool Linker::supports_building() + { + return false; + } + + bool Linker::supports_version(const zone_target_version version) + { + return version == zone_target_version::iw3_alpha_253 || version == zone_target_version::iw3_alpha_290 || + version == zone_target_version::iw3_alpha_328; + } + + void Linker::dump_zone(const std::string& name) + { + isDumping = true; + currentDumpingZone = name; + load_zone(name); + } + + void Linker::verify_zone(const std::string& name) + { + isVerifying = true; + currentDumpingZone = name; + load_zone(name); + } + } +} diff --git a/src/IW3/IW3.hpp b/src/IW3/IW3.hpp new file mode 100644 index 0000000..daef362 --- /dev/null +++ b/src/IW3/IW3.hpp @@ -0,0 +1,70 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include +#include "Functions.hpp" +#include "Structs.hpp" + +#include "Assets/XModel.hpp" +#include "Assets/Material.hpp" +#include "Assets/XAnimParts.hpp" +#include "Assets/Techset.hpp" +#include "Assets/GfxWorld.hpp" +#include "Assets/GfxImage.hpp" +#include "Assets/GameWorldMp.hpp" +#include "Assets/LoadedSound.hpp" +#include "Assets/Sound.hpp" +#include "Assets/FxEffectDef.hpp" +#include "Assets/ClipMap.hpp" +#include "Assets/MapEnts.hpp" +#include "Assets/ComWorld.hpp" + +// oh nee toch niet + +namespace ZoneTool +{ + namespace IW3 + { + struct XAsset + { + XAssetType type; + XAssetHeader ptr; + }; + + class Linker : public ILinker + { + public: + Linker(); + ~Linker(); + + const char* version() override; + bool is_used() override; + void startup() override; + std::shared_ptr alloc_zone(const std::string& zone) override; + std::shared_ptr alloc_buffer() override; + void load_zone(const std::string& name) override; + void unload_zones() override; + bool is_valid_asset_type(const std::string& type) override; + std::int32_t type_to_int(std::string type) override; + std::string type_to_string(std::int32_t type) override; + bool supports_building() override; + bool supports_version(const zone_target_version version) override; + + void dump_zone(const std::string& name) override; + void verify_zone(const std::string& name) override; + + static void* Dedicated_RegisterDvarBool(const char* name, bool defaultValue, unsigned int flags, + const char* description); + static void* DB_AddXAsset(XAsset* asset, int unk); + static const char* GetAssetName(XAsset* asset); + static void HandleAsset(XAsset* asset); + }; + } +} diff --git a/src/IW3/Structs.hpp b/src/IW3/Structs.hpp new file mode 100644 index 0000000..4296063 --- /dev/null +++ b/src/IW3/Structs.hpp @@ -0,0 +1,1930 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW3 + { + enum XAssetType : std::int32_t + { + xmodelpieces, + physpreset, + xanim, + xmodel, + material, + techset, + image, + sound, + sndcurve, + loaded_sound, + col_map_sp, + col_map_mp, + com_map, + game_map_sp, + game_map_mp, + map_ents, + gfx_map, + lightdef, + ui_map, + // not used + font, + menufile, + menu, + localize, + weapon, + snddriverglobals, + // not used + fx, + impactfx, + aitype, + // not used + mptype, + // not used + character, + // not used + xmodelalias, + // not used + rawfile, + stringtable, + max, + }; + + typedef float vec4_t[4]; + typedef float vec3_t[3]; + typedef float vec2_t[2]; + + template + struct VecInternal + { + float data[N]; + }; + + enum MapType + { + MAPTYPE_NONE = 0x0, + MAPTYPE_INVALID1 = 0x1, + MAPTYPE_INVALID2 = 0x2, + MAPTYPE_2D = 0x3, + MAPTYPE_3D = 0x4, + MAPTYPE_CUBE = 0x5, + MAPTYPE_COUNT = 0x6, + }; + + struct Picmip + { + char platform[2]; + }; + + struct CardMemory + { + int platform[2]; + }; + + struct GfxImageLoadDef + { + char levelCount; + char flags; + __int16 dimensions[3]; + int format; + int resourceSize; + char data[1]; + }; + + union GfxTexture + { + /*IDirect3DBaseTexture9 *basemap; + IDirect3DTexture9 *map; + IDirect3DVolumeTexture9 *volmap; + IDirect3DCubeTexture9 *cubemap;*/ + GfxImageLoadDef* loadDef; + void* data; + }; + + struct GfxImage + { + MapType mapType; + GfxTexture texture; + Picmip picmip; + bool noPicmip; + char semantic; + char track; + CardMemory cardMemory; + unsigned __int16 width; + unsigned __int16 height; + unsigned __int16 depth; + char category; + bool delayLoadPixels; + const char* name; + }; + + enum file_image_flags_t + { + IMG_FLAG_NOPICMIP = 0x1, + IMG_FLAG_NOMIPMAPS = 0x2, + IMG_FLAG_CUBEMAP = 0x4, + IMG_FLAG_VOLMAP = 0x8, + IMG_FLAG_STREAMING = 0x10, + IMG_FLAG_LEGACY_NORMALS = 0x20, + IMG_FLAG_CLAMP_U = 0x40, + IMG_FLAG_CLAMP_V = 0x80, + IMG_FLAG_DYNAMIC = 0x10000, + IMG_FLAG_RENDER_TARGET = 0x20000, + IMG_FLAG_SYSTEMMEM = 0x40000, + }; + + struct GfxImageFileHeader + { + char tag[3]; + char version; + char format; + char flags; + short dimensions[3]; + int fileSizeForPicmip[4]; + }; + + struct MaterialConstantDef + { + unsigned int nameHash; + char name[12]; + vec4_t literal; + }; + + struct GfxStateBits + { + unsigned int loadBits[2]; + }; + + struct WaterWritable + { + float floatTime; + }; + + struct complex_s + { + float real; + float imag; + }; + + struct water_t + { + WaterWritable writable; + complex_s* H0; + float* wTerm; + int M; + int N; + float Lx; + float Lz; + float gravity; + float windvel; + float winddir[2]; + float amplitude; + float codeConstant[4]; + GfxImage* image; + }; + + /* MaterialTextureDef->semantic */ +#define TS_2D 0x0 +#define TS_FUNCTION 0x1 +#define TS_COLOR_MAP 0x2 +#define TS_UNUSED_1 0x3 +#define TS_UNUSED_2 0x4 +#define TS_NORMAL_MAP 0x5 +#define TS_UNUSED_3 0x6 +#define TS_UNUSED_4 0x7 +#define TS_SPECULAR_MAP 0x8 +#define TS_UNUSED_5 0x9 +#define TS_UNUSED_6 0xA +#define TS_WATER_MAP 0xB + + union MaterialTextureDefInfo + { + GfxImage* image; // MaterialTextureDef->semantic != TS_WATER_MAP + water_t* water; // MaterialTextureDef->semantic == TS_WATER_MAP + }; + + struct MaterialTextureDef + { + unsigned int typeHash; + char firstCharacter; + char secondLastCharacter; + char sampleState; + char semantic; + GfxImage* image; + }; + + struct GfxDrawSurfFields + { + unsigned __int64 objectId : 16; + unsigned __int64 reflectionProbeIndex : 8; + unsigned __int64 customIndex : 5; + unsigned __int64 materialSortedIndex : 11; + unsigned __int64 prepass : 2; + unsigned __int64 primaryLightIndex : 8; + unsigned __int64 surfType : 4; + unsigned __int64 primarySortKey : 6; + unsigned __int64 unused : 4; + }; + + union GfxDrawSurf + { + GfxDrawSurfFields fields; + unsigned long long packed; + }; + + struct GfxVertexShaderLoadDef + { + unsigned int* program; + unsigned __int16 programSize; + unsigned __int16 loadForRenderer; + }; + + struct MaterialVertexShaderProgram + { + void* vs; + GfxVertexShaderLoadDef loadDef; + }; + + struct MaterialVertexShader + { + const char* name; + MaterialVertexShaderProgram prog; + }; + + struct GfxPixelShaderLoadDef + { + unsigned int* program; + unsigned __int16 programSize; + unsigned __int16 loadForRenderer; + }; + + struct MaterialPixelShaderProgram + { + void* ps; + GfxPixelShaderLoadDef loadDef; + }; + + struct MaterialPixelShader + { + const char* name; + MaterialPixelShaderProgram prog; + }; + + struct MaterialArgumentCodeConst + { + unsigned __int16 index; + char firstRow; + char rowCount; + }; + + union MaterialArgumentDef + { + const float* literalConst; + MaterialArgumentCodeConst codeConst; + unsigned int codeSampler; + unsigned int nameHash; + }; + + struct MaterialShaderArgument + { + unsigned __int16 type; + unsigned __int16 dest; + MaterialArgumentDef u; + }; + + struct MaterialStreamRouting + { + char source; + char dest; + }; + + struct MaterialVertexStreamRouting + { + MaterialStreamRouting data[16]; + void* decl[16]; + }; + + struct MaterialVertexDeclaration + { + char streamCount; + bool hasOptionalSource; + bool isLoaded; + char pad[1]; + MaterialVertexStreamRouting routing; + }; + + struct MaterialPass + { + MaterialVertexDeclaration* vertexDecl; + MaterialVertexShader* vertexShader; + MaterialPixelShader* pixelShader; + char perPrimArgCount; + char perObjArgCount; + char stableArgCount; + char customSamplerFlags; + MaterialShaderArgument* args; + }; + + struct MaterialTechniqueHdr + { + const char* name; + unsigned __int16 flags; + unsigned __int16 numPasses; + }; + + struct MaterialTechnique + { + MaterialTechniqueHdr hdr; + MaterialPass pass[1]; + }; + + struct MaterialTechniqueSet + { + const char* name; + + union + { + int pad; + + struct + { + char worldVertFormat; + bool hasBeenUploaded; + char unused[1]; + }; + }; + + MaterialTechniqueSet* remappedTechniqueSet; + MaterialTechnique* techniques[34]; + }; + +#pragma pack(push, 4) + struct MaterialInfo + { + const char* name; + char gameFlags; + char sortKey; + char textureAtlasRowCount; + char textureAtlasColumnCount; + GfxDrawSurf drawSurf; + unsigned int surfaceTypeBits; + unsigned __int16 hashIndex; + }; +#pragma pack(pop) + + //enum MaterialTechniqueTypeMeme + //{ + // TECHNIQUE_DEPTH_PREPASS = 0x0, + // TECHNIQUE_BUILD_FLOAT_Z = 0x1, + // TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2, + // TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3, + // TECHNIQUE_UNLIT = 0x4, + // TECHNIQUE_EMISSIVE = 0x5, + // TECHNIQUE_EMISSIVE_SHADOW = 0x6, + // TECHNIQUE_LIT_BEGIN = 0x7, + // TECHNIQUE_LIT = 0x7, + // TECHNIQUE_LIT_SUN = 0x8, + // TECHNIQUE_LIT_SUN_SHADOW = 0x9, + // TECHNIQUE_LIT_SPOT = 0xA, + // TECHNIQUE_LIT_SPOT_SHADOW = 0xB, + // TECHNIQUE_LIT_OMNI = 0xC, + // TECHNIQUE_LIT_OMNI_SHADOW = 0xD, + // TECHNIQUE_LIT_INSTANCED = 0xE, + // TECHNIQUE_LIT_INSTANCED_SUN = 0xF, + // TECHNIQUE_LIT_INSTANCED_SUN_SHADOW = 0x10, + // TECHNIQUE_LIT_INSTANCED_SPOT = 0x11, + // TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW = 0x12, + // TECHNIQUE_LIT_INSTANCED_OMNI = 0x13, + // TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW = 0x14, + // TECHNIQUE_LIT_END = 0x15, + // TECHNIQUE_LIGHT_SPOT = 0x15, + // TECHNIQUE_LIGHT_OMNI = 0x16, + // TECHNIQUE_LIGHT_SPOT_SHADOW = 0x17, + // TECHNIQUE_FAKELIGHT_NORMAL = 0x18, + // TECHNIQUE_FAKELIGHT_VIEW = 0x19, + // TECHNIQUE_SUNLIGHT_PREVIEW = 0x1A, + // TECHNIQUE_CASE_TEXTURE = 0x1B, + // TECHNIQUE_WIREFRAME_SOLID = 0x1C, + // TECHNIQUE_WIREFRAME_SHADED = 0x1D, + // TECHNIQUE_SHADOWCOOKIE_CASTER = 0x1E, + // TECHNIQUE_SHADOWCOOKIE_RECEIVER = 0x1F, + // TECHNIQUE_DEBUG_BUMPMAP = 0x20, + // TECHNIQUE_DEBUG_BUMPMAP_INSTANCED = 0x21, + // //TECHNIQUE_COUNT = 0x22 + //}; + + struct infoParm_t + { + const char* name; + int clearSolid; + int surfaceFlags; + int contents; + int toolFlags; + }; + + struct Material + { + const char* name; + char gameFlags; + char sortKey; + char textureAtlasRowCount; + char textureAtlasColumnCount; + GfxDrawSurf drawSurf; + unsigned int surfaceTypeBits; + unsigned __int16 hashIndex; + unsigned char animationX; + unsigned char animationY; + // MaterialInfo info; + char stateBitsEntry[34]; + char numMaps; + char constantCount; + char stateBitsCount; + char stateFlags; + char cameraRegion; + MaterialTechniqueSet* techniqueSet; + MaterialTextureDef* maps; + MaterialConstantDef* constantTable; + GfxStateBits* stateMap; + }; + + struct cplane_s + { + float normal[3]; + float dist; + char type; + char signbits; + char pad[2]; + }; + +#pragma pack(push, 2) + struct cbrushside_t + { + cplane_s* plane; + unsigned int materialNum; + __int16 firstAdjacentSideOffset; + char edgeCount; + }; +#pragma pack(pop) + + struct DObjAnimMat + { + float quat[4]; + float trans[3]; + float transWeight; + }; + + struct XSurfaceVertexInfo + { + short vertCount[4]; + unsigned short* vertsBlend; + }; + + union GfxColor + { + unsigned int packed; + char array[4]; + }; + + union PackedTexCoords + { + unsigned int packed; + }; + + union PackedUnitVec + { + unsigned int packed; + char array[4]; + }; + + struct GfxPackedVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + PackedTexCoords texCoord; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct XSurfaceCollisionAabb + { + unsigned short mins[3]; + unsigned short maxs[3]; + }; + + struct XSurfaceCollisionNode + { + XSurfaceCollisionAabb aabb; + unsigned short childBeginIndex; + unsigned short childCount; + }; + + struct XSurfaceCollisionLeaf + { + unsigned short triangleBeginIndex; + }; + + struct XSurfaceCollisionTree + { + float trans[3]; + float scale[3]; + unsigned int nodeCount; + XSurfaceCollisionNode* nodes; + unsigned int leafCount; + XSurfaceCollisionLeaf* leafs; + }; + + struct XRigidVertList + { + unsigned short boneOffset; + unsigned short vertCount; + unsigned short triOffset; + unsigned short triCount; + XSurfaceCollisionTree* collisionTree; + }; + + struct XSurface + { + char tileMode; + bool deformed; + unsigned __int16 vertCount; + unsigned __int16 triCount; + char zoneHandle; + unsigned __int16 baseTriIndex; + unsigned __int16 baseVertIndex; + unsigned __int16* triIndices; + XSurfaceVertexInfo vertInfo; + GfxPackedVertex* verts0; + unsigned int vertListCount; + XRigidVertList* vertList; + int partBits[4]; + }; + + struct BrushWrapper + { + float mins[3]; + int contents; + float maxs[3]; + unsigned int numsides; + cbrushside_t* sides; + __int16 axialMaterialNum[2][3]; + char* baseAdjacentSide; + __int16 firstAdjacentSideOffsets[2][3]; + char edgeCount[2][3]; + int totalEdgeCount; + cplane_s* planes; + }; + + struct PhysMass + { + float centerOfMass[3]; + float momentsOfInertia[3]; + float productsOfInertia[3]; + }; + + struct PhysGeomInfo + { + BrushWrapper* brush; + int type; + float orientation[3][3]; + float offset[3]; + float halfLengths[3]; + }; + + struct PhysGeomList + { + unsigned int count; + PhysGeomInfo* geoms; + PhysMass mass; + }; + + struct XBoneInfo + { + float bounds[2][3]; + float offset[3]; + float radiusSquared; + }; + + struct XModelHighMipBounds + { + float mins[3]; + float maxs[3]; + }; + + struct XModelStreamInfo + { + XModelHighMipBounds* highMipBounds; + }; + + struct XModelCollTri_s + { + float plane[4]; + float svec[4]; + float tvec[4]; + }; + + struct XModelCollSurf_s + { + XModelCollTri_s* collTris; + int numCollTris; + float mins[3]; + float maxs[3]; + int boneIdx; + int contents; + int surfFlags; + }; + + struct XModelLodInfo + { + float dist; + unsigned __int16 numsurfs; + unsigned __int16 surfIndex; + int partBits[4]; + char lod; + char smcIndexPlusOne; + char smcAllocBits; + char unused; + }; + +#pragma pack(push, 4) + struct PhysPreset + { + const char* name; + int type; + float mass; + float bounce; + float friction; + float bulletForceScale; + float explosiveForceScale; + const char* sndAliasPrefix; + float piecesSpreadFraction; + float piecesUpwardVelocity; + char tempDefaultToCylinder; + }; +#pragma pack(pop) + + struct XModel + { + const char* name; + char numBones; + char numRootBones; + unsigned char numsurfs; + char lodRampType; + unsigned __int16* boneNames; + char* parentList; + __int16* quats; + float* trans; + char* partClassification; + DObjAnimMat* baseMat; + XSurface* surfs; + Material** materialHandles; + XModelLodInfo lodInfo[4]; + XModelCollSurf_s* collSurfs; + int numCollSurfs; + int contents; + XBoneInfo* boneInfo; + float radius; + float mins[3]; + float maxs[3]; + __int16 numLods; + __int16 collLod; + XModelStreamInfo streamInfo; + int memUsage; + char flags; + bool bad; + PhysPreset* physPreset; + PhysGeomList* physGeoms; + }; + + union XAnimIndices + { + char* _1; + unsigned __int16* _2; + void* data; + }; + + union XAnimDynamicFrames + { + char (*_1)[3]; + unsigned __int16 (*_2)[3]; + }; + + union XAnimDynamicIndices + { + char _1[1]; + unsigned __int16 _2[1]; + }; + +#pragma pack(push, 4) + struct XAnimPartTransFrames + { + float mins[3]; + float size[3]; + XAnimDynamicFrames frames; + XAnimDynamicIndices indices; + }; + + union XAnimPartTransData + { + XAnimPartTransFrames frames; + float frame0[3]; + }; + + struct XAnimPartTrans + { + unsigned __int16 size; + char smallTrans; + __declspec(align(2)) XAnimPartTransData u; + }; + + struct XAnimDeltaPartQuatDataFrames + { + __int16 (*frames)[2]; + XAnimDynamicIndices indices; + }; + + union XAnimDeltaPartQuatData + { + XAnimDeltaPartQuatDataFrames frames; + __int16 frame0[2]; + }; + + struct XAnimDeltaPartQuat + { + unsigned __int16 size; + __declspec(align(4)) XAnimDeltaPartQuatData u; + }; + + struct XAnimDeltaPart + { + XAnimPartTrans* trans; + XAnimDeltaPartQuat* quat; + }; + + struct XAnimNotifyInfo + { + short name; + float time; + }; + +#ifdef __cplusplus + enum XAnimPartType + { + PART_TYPE_NO_QUAT = 0x0, + PART_TYPE_SIMPLE_QUAT = 0x1, + PART_TYPE_NORMAL_QUAT = 0x2, + PART_TYPE_PRECISION_QUAT = 0x3, + PART_TYPE_SIMPLE_QUAT_NO_SIZE = 0x4, + PART_TYPE_NORMAL_QUAT_NO_SIZE = 0x5, + PART_TYPE_PRECISION_QUAT_NO_SIZE = 0x6, + PART_TYPE_SMALL_TRANS = 0x7, + PART_TYPE_TRANS = 0x8, + PART_TYPE_TRANS_NO_SIZE = 0x9, + PART_TYPE_NO_TRANS = 0xA, + PART_TYPE_ALL = 0xB, + PART_TYPE_COUNT = 0xC, + }; +#endif + +#pragma pack(pop) + + struct XAnimParts + { + char* name; // 0 + unsigned short dataByteCount; // 4 + unsigned short dataShortCount; // 6 + unsigned short dataIntCount; // 8 + unsigned short randomDataByteCount; // 10 - 0xA + unsigned short randomDataIntCount; // 12 - 0xC + unsigned short framecount; // 14 - 0xE + char bLoop; // 16 + char bDelta; // 31 + unsigned char boneCount[10]; // 17 + char notifyCount; // 27 + char assetType; // 30 + bool isDefault; + unsigned int randomDataShortCount; // 32 - 0x20 + unsigned int indexcount; // 36 - 0x24 + float framerate; // 40 - 0x28 + float frequency; // 44 - 0x2C + unsigned short* tagnames; // 48 - 0x30 + char* dataByte; // 52 - 0x34 + short* dataShort; // 56 - 0x38 + int* dataInt; // 60 - 0x3C + short* randomDataShort; // 64 - 0x40 + char* randomDataByte; // 68 - 0x44 + int* randomDataInt; // 72 - 0x48 + XAnimIndices indices; // 76 - 0x4C + XAnimNotifyInfo* notify; // 80 - 0x50 + XAnimDeltaPart* delta; // 84 - 0x54 + }; + + struct GfxStreamingAabbTree + { + unsigned __int16 firstItem; + unsigned __int16 itemCount; + unsigned __int16 firstChild; + unsigned __int16 childCount; + float mins[3]; + float maxs[3]; + }; + + struct GfxWorldStreamInfo + { + int aabbTreeCount; + // GfxStreamingAabbTree *aabbTrees; + // int leafRefCount; + // int *leafRefs; + }; + + struct GfxWorldVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + float texCoord[2]; + float lmapCoord[2]; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct GfxWorldVertexData + { + GfxWorldVertex* vertices; + void/*IDirect3DVertexBuffer9*/ * worldVb; + }; + + struct GfxWorldVertexLayerData + { + char* data; + void/*IDirect3DVertexBuffer9*/ * layerVb; + }; + + struct SunLightParseParams + { + char name[64]; + float ambientScale; + float ambientColor[3]; + float diffuseFraction; + float sunLight; + float sunColor[3]; + float diffuseColor[3]; + char diffuseColorHasBeenSet; + float angles[3]; + }; + + struct __declspec(align(4)) GfxLightImage + { + GfxImage* image; + char samplerState; + }; + + struct GfxLightDef + { + const char* name; + GfxLightImage attenuation; + int lmapLookupStart; + }; + + struct GfxLight + { + char type; + char canUseShadowMap; + char unused[2]; + float color[3]; + float dir[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + int exponent; + unsigned int spotShadowIndex; + GfxLightDef* def; + }; + + struct GfxReflectionProbe + { + float origin[3]; + GfxImage* reflectionImage; + }; + + struct GfxWorldDpvsPlanes + { + int cellCount; + cplane_s* planes; + unsigned __int16* nodes; + unsigned int* sceneEntCellBits; + }; + + struct GfxAabbTree + { + float mins[3]; + float maxs[3]; + unsigned __int16 childCount; + unsigned __int16 surfaceCount; + unsigned __int16 startSurfIndex; + unsigned __int16 surfaceCountNoDecal; + unsigned __int16 startSurfIndexNoDecal; + unsigned __int16 smodelIndexCount; + unsigned __int16* smodelIndexes; + int childrenOffset; + }; + + struct GfxPortal; + + struct GfxPortalWritable + { + char isQueued; + char isAncestor; + char recursionDepth; + char hullPointCount; + float (*hullPoints)[2]; + GfxPortal* queuedParent; + }; + + struct DpvsPlane + { + float coeffs[4]; + char side[3]; + char pad; + }; + + struct GfxCell; + + struct GfxPortal + { + GfxPortalWritable writable; + DpvsPlane plane; + GfxCell* cell; + float (*vertices)[3]; + char vertexCount; + float hullAxis[2][3]; + }; + + struct GfxCell + { + float mins[3]; + float maxs[3]; + int aabbTreeCount; + GfxAabbTree* aabbTree; + int portalCount; + GfxPortal* portals; + int cullGroupCount; + int* cullGroups; + char reflectionProbeCount; + char* reflectionProbes; + }; + + struct GfxLightmapArray + { + GfxImage* primary; + GfxImage* secondary; + }; + + struct GfxLightGridEntry + { + unsigned __int16 colorsIndex; + char primaryLightIndex; + char needsTrace; + }; + + struct GfxLightGridColors + { + char rgb[56][3]; + }; + + struct GfxLightGrid + { + char hasLightRegions; + unsigned int sunPrimaryLightIndex; + unsigned __int16 mins[3]; + unsigned __int16 maxs[3]; + unsigned int rowAxis; + unsigned int colAxis; + unsigned __int16* rowDataStart; + unsigned int rawRowDataSize; + char* rawRowData; + unsigned int entryCount; + GfxLightGridEntry* entries; + unsigned int colorCount; + GfxLightGridColors* colors; + }; + + struct GfxBrushModelWritable + { + float mins[3]; + float maxs[3]; + }; + + struct __declspec(align(4)) GfxBrushModel + { + GfxBrushModelWritable writable; + float bounds[2][3]; + unsigned __int16 surfaceCount; + unsigned __int16 startSurfIndex; + unsigned __int16 surfaceCountNoDecal; + }; + + struct MaterialMemory + { + Material* material; + int memory; + }; + + struct sunflare_t + { + char hasValidData; + Material* spriteMaterial; + Material* flareMaterial; + float spriteSize; + float flareMinSize; + float flareMinDot; + float flareMaxSize; + float flareMaxDot; + float flareMaxAlpha; + int flareFadeInTime; + int flareFadeOutTime; + float blindMinDot; + float blindMaxDot; + float blindMaxDarken; + int blindFadeInTime; + int blindFadeOutTime; + float glareMinDot; + float glareMaxDot; + float glareMaxLighten; + int glareFadeInTime; + int glareFadeOutTime; + float sunFxPosition[3]; + }; + + struct XModelDrawInfo + { + unsigned __int16 lod; + unsigned __int16 surfId; + }; + + struct GfxSceneDynModel + { + XModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct BModelDrawInfo + { + unsigned __int16 surfId; + }; + + struct GfxSceneDynBrush + { + BModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct GfxShadowGeometry + { + unsigned __int16 surfaceCount; + unsigned __int16 smodelCount; + unsigned __int16* sortedSurfIndex; + unsigned __int16* smodelIndex; + }; + + struct GfxLightRegionAxis + { + float dir[3]; + float midPoint; + float halfSize; + }; + + struct GfxLightRegionHull + { + float kdopMidPoint[9]; + float kdopHalfSize[9]; + unsigned int axisCount; + GfxLightRegionAxis* axis; + }; + + struct GfxLightRegion + { + unsigned int hullCount; + GfxLightRegionHull* hulls; + }; + + struct GfxStaticModelInst + { + float mins[3]; + float maxs[3]; + GfxColor groundLighting; + }; + + struct srfTriangles_t + { + int vertexLayerData; + int firstVertex; + unsigned __int16 vertexCount; + unsigned __int16 triCount; + int baseIndex; + }; + + struct GfxSurface + { + srfTriangles_t tris; + Material* material; + char lightmapIndex; + char reflectionProbeIndex; + char primaryLightIndex; + char flags; + float bounds[2][3]; + }; + + struct GfxCullGroup + { + float mins[3]; + float maxs[3]; + int surfaceCount; + int startSurfIndex; + }; + + struct GfxPackedPlacement + { + float origin[3]; + vec3_t axis[3]; + float scale; + }; + + struct __declspec(align(4)) GfxStaticModelDrawInst + { + float cullDist; + GfxPackedPlacement placement; + XModel* model; + unsigned __int16 smodelCacheIndex[4]; + char reflectionProbeIndex; + char primaryLightIndex; + unsigned __int16 lightingHandle; + char flags; + }; + + struct GfxWorldDpvsStatic + { + unsigned int smodelCount; + unsigned int staticSurfaceCount; + unsigned int staticSurfaceCountNoDecal; + unsigned int litSurfsBegin; + unsigned int litSurfsEnd; + unsigned int decalSurfsBegin; + unsigned int decalSurfsEnd; + unsigned int emissiveSurfsBegin; + unsigned int emissiveSurfsEnd; + unsigned int smodelVisDataCount; + unsigned int surfaceVisDataCount; + char* smodelVisData[3]; + char* surfaceVisData[3]; + unsigned int* lodData; + unsigned __int16* sortedSurfIndex; + GfxStaticModelInst* smodelInsts; + GfxSurface* surfaces; + GfxCullGroup* cullGroups; + GfxStaticModelDrawInst* smodelDrawInsts; + GfxDrawSurf* surfaceMaterials; + unsigned int* surfaceCastsSunShadow; + volatile int usageCount; + }; + + struct GfxWorldDpvsDynamic + { + unsigned int dynEntClientWordCount[2]; + unsigned int dynEntClientCount[2]; + unsigned int* dynEntCellBits[2]; + char* dynEntVisData[2][3]; + }; + + struct GfxWorld + { + const char* name; + const char* baseName; + int planeCount; + int nodeCount; + int indexCount; + unsigned __int16* indices; + int surfaceCount; + GfxWorldStreamInfo streamInfo; + int skySurfCount; + int* skyStartSurfs; + GfxImage* skyImage; + char skySamplerState; + unsigned int vertexCount; + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + SunLightParseParams sunParse; + GfxLight* sunLight; + float sunColorFromBsp[3]; + unsigned int sunPrimaryLightIndex; + unsigned int primaryLightCount; + int cullGroupCount; + unsigned int reflectionProbeCount; + GfxReflectionProbe* reflectionProbes; + GfxTexture* reflectionProbeTextures; + GfxWorldDpvsPlanes dpvsPlanes; + int cellBitsCount; + GfxCell* cells; + int lightmapCount; + GfxLightmapArray* lightmaps; + GfxLightGrid lightGrid; + GfxTexture* lightmapPrimaryTextures; + GfxTexture* lightmapSecondaryTextures; + int modelCount; + GfxBrushModel* models; + float mins[3]; + float maxs[3]; + unsigned int checksum; + int materialMemoryCount; + MaterialMemory* materialMemory; + sunflare_t sun; + float outdoorLookupMatrix[4][4]; + GfxImage* outdoorImage; + unsigned int* cellCasterBits; + GfxSceneDynModel* sceneDynModel; + GfxSceneDynBrush* sceneDynBrush; + unsigned int* primaryLightEntityShadowVis; + unsigned int* primaryLightDynEntShadowVis[2]; + char* nonSunPrimaryLightForModelDynEnt; + GfxShadowGeometry* shadowGeom; + GfxLightRegion* lightRegion; + GfxWorldDpvsStatic dpvs; + GfxWorldDpvsDynamic dpvsDyn; + }; + + struct cStaticModelWritable + { + unsigned __int16 nextModelInWorldSector; + }; + + struct cStaticModel_s + { + cStaticModelWritable writable; + XModel* xmodel; + float origin[3]; + float invScaledAxis[3][3]; + float absmin[3]; + float absmax[3]; + }; + + struct dmaterial_t + { + char material[64]; + int surfaceFlags; + int contentFlags; + }; + + struct cNode_t + { + cplane_s* plane; + __int16 children[2]; + }; + +#pragma pack(push, 4) + struct cLeaf_t + { + unsigned __int16 firstCollAabbIndex; + unsigned __int16 collAabbCount; + int brushContents; + int terrainContents; + float mins[3]; + float maxs[3]; + int leafBrushNode; + __int16 cluster; + }; +#pragma pack(pop) + + struct cLeafBrushNodeLeaf_t + { + unsigned __int16* brushes; + }; + + struct cLeafBrushNodeChildren_t + { + float dist; + float range; + unsigned __int16 childOffset[2]; + }; + + union cLeafBrushNodeData_t + { + cLeafBrushNodeLeaf_t leaf; + cLeafBrushNodeChildren_t children; + }; + + struct cLeafBrushNode_s + { + char axis; + __int16 leafBrushCount; + int contents; + cLeafBrushNodeData_t data; + }; + + struct CollisionBorder + { + float distEq[3]; + float zBase; + float zSlope; + float start; + float length; + }; + + struct CollisionPartition + { + char triCount; + char borderCount; + int firstTri; + CollisionBorder* borders; + }; + + union CollisionAabbTreeIndex + { + int firstChildIndex; + int partitionIndex; + }; + + struct CollisionAabbTree + { + float origin[3]; + float halfSize[3]; + unsigned __int16 materialIndex; + unsigned __int16 childCount; + CollisionAabbTreeIndex u; + }; + + /* 860 */ + struct cmodel_t + { + float mins[3]; + float maxs[3]; + float radius; + cLeaf_t leaf; + }; + + /* 861 */ +#pragma pack(push, 16) + struct cbrush_t + { + float mins[3]; + int contents; + float maxs[3]; + unsigned int numsides; + cbrushside_t* sides; + __int16 axialMaterialNum[2][3]; + char* baseAdjacentSide; + __int16 firstAdjacentSideOffsets[2][3]; + char edgeCount[2][3]; + char pad[8]; + }; +#pragma pack(pop) + + struct Bounds + { + vec3_t midPoint; + vec3_t halfSize; + }; + + struct TriggerModel + { + int contents; + unsigned __int16 hullCount; + unsigned __int16 firstHull; + }; + + /* 2376 */ + struct TriggerHull + { + Bounds bounds; + int contents; + unsigned __int16 slabCount; + unsigned __int16 firstSlab; + }; + + /* 2377 */ + struct TriggerSlab + { + float dir[3]; + float midPoint; + float halfSize; + }; + + /* 2378 */ + struct MapTriggers + { + unsigned int count; + TriggerModel* models; + unsigned int hullCount; + TriggerHull* hulls; + unsigned int slabCount; + TriggerSlab* slabs; + }; + + struct MapEnts + { + const char* name; + char* entityString; + int numEntityChars; // The structure actually ends here... + MapTriggers trigger; // Pretty sure that's not correct. + // this goes on for a while but we don't need any of it + }; + + struct GfxPlacement + { + float quat[4]; + float origin[3]; + }; + + struct FxEffectDef_Placeholder + { + const char* name; + }; + + struct DynEntityDef + { + int type; + GfxPlacement pose; + XModel* xModel; + unsigned __int16 brushModel; + unsigned __int16 physicsBrushModel; + FxEffectDef_Placeholder* destroyFx; + /*XModelPieces*/ + void* destroyPieces; + PhysPreset* physPreset; + int health; + PhysMass mass; + int contents; + }; + + struct clipMap_t + { + const char* name; + int isInUse; + int planeCount; + cplane_s* planes; + unsigned int numStaticModels; + cStaticModel_s* staticModelList; + unsigned int numMaterials; + dmaterial_t* materials; + unsigned int numBrushSides; + cbrushside_t* brushsides; + unsigned int numBrushEdges; + char* brushEdges; + unsigned int numNodes; + cNode_t* nodes; + unsigned int numLeafs; + cLeaf_t* leafs; + unsigned int leafbrushNodesCount; + cLeafBrushNode_s* leafbrushNodes; + unsigned int numLeafBrushes; + unsigned __int16* leafbrushes; + unsigned int numLeafSurfaces; + unsigned int* leafsurfaces; + unsigned int vertCount; + float (*verts)[3]; + int triCount; + unsigned __int16* triIndices; + char* triEdgeIsWalkable; + int borderCount; + CollisionBorder* borders; + int partitionCount; + CollisionPartition* partitions; + int aabbTreeCount; + CollisionAabbTree* aabbTrees; + unsigned int numSubModels; + cmodel_t* cmodels; + unsigned __int16 numBrushes; + cbrush_t* brushes; + int numClusters; + int clusterBytes; + char* visibility; + int vised; + MapEnts* mapEnts; + cbrush_t* box_brush; + cmodel_t box_model; + unsigned __int16 dynEntCount[2]; + DynEntityDef* dynEntDefList[2]; + /*DynEntityPose*/ + void* dynEntPoseList[2]; + /*DynEntityClient*/ + void* dynEntClientList[2]; + /*DynEntityColl*/ + void* dynEntCollList[2]; + unsigned int checksum; + }; + + struct RawFile + { + const char* name; + int len; + const char* buffer; + }; + + struct ComPrimaryLight + { + char type; + char canUseShadowMap; + char exponent; + char unused; + float color[3]; + float dir[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + float cosHalfFovExpanded; + float rotationLimit; + float translationLimit; + const char* defName; + }; + + struct ComWorld + { + const char* name; + int isInUse; + unsigned int primaryLightCount; + ComPrimaryLight* primaryLights; + }; + + struct FxSpawnDefLooping + { + int intervalMsec; + int count; + }; + + struct FxIntRange + { + int base; + int amplitude; + }; + + struct FxSpawnDefOneShot + { + FxIntRange count; + }; + + union FxSpawnDef + { + FxSpawnDefLooping looping; + FxSpawnDefOneShot oneShot; + }; + + struct FxFloatRange + { + float base; + float amplitude; + }; + + struct FxElemAtlas + { + char behavior; + char index; + char fps; + char loopCount; + char colIndexBits; + char rowIndexBits; + __int16 entryCount; + }; + + struct FxElemVec3Range + { + float base[3]; + float amplitude[3]; + }; + + struct FxElemVelStateInFrame + { + FxElemVec3Range velocity; + FxElemVec3Range totalDelta; + }; + + const struct FxElemVelStateSample + { + FxElemVelStateInFrame local; + FxElemVelStateInFrame world; + }; + + struct FxElemVisualState + { + char color[4]; + float rotationDelta; + float rotationTotal; + float size[2]; + float scale; + }; + + const struct FxElemVisStateSample + { + FxElemVisualState base; + FxElemVisualState amplitude; + }; + + struct FxEffectDef; + + union FxEffectDefRef + { + FxEffectDef* handle; + const char* name; + }; + + union FxElemVisuals + { + const void* anonymous; + Material* material; + XModel* model; + FxEffectDefRef effectDef; + const char* soundName; + }; + + struct FxElemMarkVisuals + { + Material* materials[2]; + }; + + union FxElemDefVisuals + { + FxElemMarkVisuals* markArray; + FxElemVisuals* array; + FxElemVisuals instance; + }; + + struct FxTrailVertex + { + float pos[2]; + float normal[2]; + float texCoord; + }; + + struct FxTrailDef + { + int scrollTimeMsec; + int repeatDist; + int splitDist; + int vertCount; + FxTrailVertex* verts; + int indCount; + unsigned __int16* inds; + }; + + const struct FxElemDef + { + int flags; + FxSpawnDef spawn; + FxFloatRange spawnRange; + FxFloatRange fadeInRange; + FxFloatRange fadeOutRange; + float spawnFrustumCullRadius; + FxIntRange spawnDelayMsec; + FxIntRange lifeSpanMsec; + FxFloatRange spawnOrigin[3]; + FxFloatRange spawnOffsetRadius; + FxFloatRange spawnOffsetHeight; + FxFloatRange spawnAngles[3]; + FxFloatRange angularVelocity[3]; + FxFloatRange initialRotation; + FxFloatRange gravity; + FxFloatRange reflectionFactor; + FxElemAtlas atlas; + char elemType; + char visualCount; + char velIntervalCount; + char visStateIntervalCount; + FxElemVelStateSample* velSamples; + FxElemVisStateSample* visSamples; + FxElemDefVisuals visuals; + Bounds collBounds; + FxEffectDefRef effectOnImpact; + FxEffectDefRef effectOnDeath; + FxEffectDefRef effectEmitted; + FxFloatRange emitDist; + FxFloatRange emitDistVariance; + FxTrailDef* trailDef; + char sortOrder; + char lightingFrac; + char useItemClip; + char unused[1]; + }; + + struct FxEffectDef + { + const char* name; + int flags; + int totalSize; + int msecLoopingLife; + int elemDefCountLooping; + int elemDefCountOneShot; + int elemDefCountEmission; + FxElemDef* elemDefs; + }; + + enum FxElemType : char + { + FX_ELEM_TYPE_SPRITE_BILLBOARD = 0x0, + FX_ELEM_TYPE_SPRITE_ORIENTED = 0x1, + FX_ELEM_TYPE_TAIL = 0x2, + FX_ELEM_TYPE_TRAIL = 0x3, + FX_ELEM_TYPE_CLOUD = 0x4, + FX_ELEM_TYPE_MODEL = 0x5, + FX_ELEM_TYPE_OMNI_LIGHT = 0x6, + FX_ELEM_TYPE_SPOT_LIGHT = 0x7, + FX_ELEM_TYPE_SOUND = 0x8, + FX_ELEM_TYPE_DECAL = 0x9, + FX_ELEM_TYPE_RUNNER = 0xA, + FX_ELEM_TYPE_COUNT = 0xB, + FX_ELEM_TYPE_LAST_SPRITE = 0x3, + FX_ELEM_TYPE_LAST_DRAWN = 0x7, + }; + + struct GameWorldMp + { + const char* name; + }; + + // Loaded sound +#pragma pack(push, 4) + struct LoadedSoundStruct + { + int waveFormat; + int unknown1; + int dataLength; + int sampleRate; + int bitPerChannel; + int channelCount; + int unknown3; + int blockAlign; + int unknown5; + char* soundData; + }; +#pragma pack(pop) + + struct LoadedSound + { + const char* name; + LoadedSoundStruct struct1; + }; + + // Sounds + struct SpeakerLevels + { + int speaker; + int numLevels; + float levels[2]; + }; + struct ChannelMap + { + int entryCount; // how many entries are used + SpeakerLevels speakers[6]; + }; + struct SpeakerMap + { + bool isDefault; + char _pad[3]; + const char* name; + ChannelMap channelMaps[2][2]; + }; + enum snd_alias_type_t : char + { + SAT_UNKNOWN = 0x0, + SAT_LOADED = 0x1, + SAT_STREAMED = 0x2, + SAT_PRIMED = 0x3, + SAT_COUNT = 0x4, + }; + struct StreamFileNamePacked + { + unsigned __int64 offset; + unsigned __int64 length; + }; + struct StreamFileNameRaw + { + const char* dir; + const char* name; + }; + union StreamFileInfo + { + StreamFileNameRaw raw; + StreamFileNamePacked packed; + }; + struct StreamFileName + { + unsigned __int16 isLocalized; + unsigned __int16 fileIndex; + StreamFileInfo info; + }; + /*struct StreamedSound + { + StreamFileName filename; + unsigned int totalMsec; + };*/ + struct StreamedSound + { + const char* dir; + const char* name; + }; + struct PrimedSound + { + StreamFileName filename; + LoadedSound* loadedPart; + int dataOffset; + int totalSize; + unsigned int primedCrc; + }; + union SoundData + { + LoadedSound* loadSnd; // SoundFile->type == SAT_LOADED + StreamedSound streamSnd; // SoundFile->type == SAT_STREAMED + //PrimedSound primedSnd; // SoundFile->type == SAT_PRIMED + }; + struct SoundFile // 0x10 + { + char type; + char _pad[2]; + bool exists; + SoundData sound; + }; +#pragma pack(push, 4) + struct SndCurve + { + const char* filename; + unsigned __int16 knotCount; + vec2_t knots[16]; + }; + const struct snd_alias_t + { + const char* aliasName; + const char* subtitle; + const char* secondaryAliasName; + const char* chainAliasName; + SoundFile* soundFile; + int sequence; + float volMin; + float volMax; + float pitchMin; + float pitchMax; + float distMin; + float distMax; + int flags; + float slavePercentage; + float probability; + float lfePercentage; + float centerPercentage; + int startDelay; + SndCurve* volumeFalloffCurve; + float envelopMin; + float envelopMax; + float envelopPercentage; + SpeakerMap* speakerMap; + }; +#pragma push(pop) + struct snd_alias_list_t + { + const char* aliasName; + snd_alias_t* head; + int count; + }; + + union XAssetHeader + { + void* data; + // XModelPieces *xmodelPieces; + PhysPreset* physPreset; + XAnimParts* xanim; + XModel* xmodel; + Material* material; + // MaterialPixelShader *pixelShader; + // MaterialVertexShader *vertexShader; + MaterialTechniqueSet* techset; + GfxImage* image; + snd_alias_list_t *sound; + // SndCurve *sndCurve; + clipMap_t* clipMap; + clipMap_t* col_map_mp; + ComWorld* com_map; + // GameWorldSp *gameWorldSp; + GameWorldMp* gameWorldMp; + MapEnts* mapEnts; + MapEnts* map_ents; + GfxWorld* gfxWorld; + GfxWorld* gfx_map; + GfxLightDef* lightDef; + LoadedSound* loaded_sound; + // Font_s *font; + // MenuList *menuList; + // menuDef_t *menu; + // LocalizeEntry *localize; + // WeaponDef *weapon; + // SndDriverGlobals *sndDriverGlobals; + FxEffectDef* fx; + // FxImpactTable *impactFx; + RawFile* rawfile; + // StringTable *stringTable; + }; + } +} diff --git a/src/IW3/stdafx.cpp b/src/IW3/stdafx.cpp new file mode 100644 index 0000000..34ba508 --- /dev/null +++ b/src/IW3/stdafx.cpp @@ -0,0 +1,10 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + \ No newline at end of file diff --git a/src/IW3/stdafx.hpp b/src/IW3/stdafx.hpp new file mode 100644 index 0000000..bbef6a1 --- /dev/null +++ b/src/IW3/stdafx.hpp @@ -0,0 +1,21 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#define _CRT_SECURE_NO_WARNINGS +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +using namespace std::string_literals; + +#include "IW3.hpp" +#include "IW4/Structs.hpp" \ No newline at end of file diff --git a/src/IW4.lua b/src/IW4.lua new file mode 100644 index 0000000..2085e65 --- /dev/null +++ b/src/IW4.lua @@ -0,0 +1,36 @@ +IW4 = {} + +function IW4:include() + includedirs { + path.join(ProjectFolder(), "IW4") + } +end + +function IW4:link() + self:include() + links { + "IW4" + } +end + +function IW4:project() + local folder = ProjectFolder(); + + project "IW4" + kind "StaticLib" + language "C++" + + pchheader "stdafx.hpp" + pchsource(path.join(folder, "IW4/stdafx.cpp")) + + files { + path.join(folder, "IW4/**.h"), + path.join(folder, "IW4/**.hpp"), + path.join(folder, "IW4/**.cpp") + } + + self:include() + ZoneUtils:include() + IW5:include() + zlib:include() +end \ No newline at end of file diff --git a/src/IW4/Assets/AddonMapEnts.cpp b/src/IW4/Assets/AddonMapEnts.cpp new file mode 100644 index 0000000..a23a0db --- /dev/null +++ b/src/IW4/Assets/AddonMapEnts.cpp @@ -0,0 +1,147 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "ZoneUtils/Utils/BinaryDumper.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + AddonMapEnts* IAddonMapEnts::parse(std::string name, ZoneMemory* mem) + { + // check if we can open a filepointer + if (!FileSystem::FileExists(name)) + { + return nullptr; + } + + auto file = FileSystem::FileOpen(name, "rb"); + + // let them know that we're parsing a custom mapents file + ZONETOOL_INFO("Parsing addon mapents \"%s\"...", name.c_str()); + + // alloc mapents + auto ents = mem->Alloc(); + + ents->name = mem->StrDup(name); + ents->numEntityChars = FileSystem::FileSize(file) + 1; + + ents->entityString = mem->Alloc(ents->numEntityChars); + memset((char*)ents->entityString, 0, ents->numEntityChars); + fread((char*)ents->entityString, ents->numEntityChars - 1, 1, file); + +#ifdef CONVERT_IW5_MAPENTS + // convert the mapents! + IMapEnts::convert_ents(reinterpret_cast(ents), mem); +#endif + + // close filepointer + FileSystem::FileClose(file); + + AssetReader triggerReader(mem); + AssetReader stageReader(mem); + + if (triggerReader.open(name + ".triggers")) + { + ents->trigger.modelCount = triggerReader.read_int(); + ents->trigger.models = triggerReader.read_array(); + + ents->trigger.hullCount = triggerReader.read_int(); + ents->trigger.hulls = triggerReader.read_array(); + + ents->trigger.slabCount = triggerReader.read_int(); + ents->trigger.slabs = triggerReader.read_array(); + } + + triggerReader.close(); + + // return mapents + return ents; + } + + void IAddonMapEnts::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".mapents"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data()).addon_map_ents; + } + } + + void IAddonMapEnts::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IAddonMapEnts::load_depending(IZone* zone) + { + + } + + std::string IAddonMapEnts::name() + { + return this->name_; + } + + std::int32_t IAddonMapEnts::type() + { + return addon_map_ents; + } + + void IAddonMapEnts::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->entityString) + { + buf->align(0); + buf->write(data->entityString, data->numEntityChars); + ZoneBuffer::clear_pointer(&dest->entityString); + } + + IMapEnts::write_triggers(zone, buf, &dest->trigger); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IAddonMapEnts::dump(AddonMapEnts* asset) + { + auto* file = FileSystem::FileOpen(asset->name, "wb"); + + if (file) + { + fwrite(asset->entityString, asset->numEntityChars, 1, file); + FileSystem::FileClose(file); + } + + AssetDumper trigger_dumper; + if (trigger_dumper.open(asset->name + ".triggers"s)) + { + trigger_dumper.dump_int(asset->trigger.modelCount); + trigger_dumper.dump_array(asset->trigger.models, asset->trigger.modelCount); + + trigger_dumper.dump_int(asset->trigger.hullCount); + trigger_dumper.dump_array(asset->trigger.hulls, asset->trigger.hullCount); + + trigger_dumper.dump_int(asset->trigger.slabCount); + trigger_dumper.dump_array(asset->trigger.slabs, asset->trigger.slabCount); + + trigger_dumper.close(); + } + } + } +} diff --git a/src/IW4/Assets/AddonMapEnts.hpp b/src/IW4/Assets/AddonMapEnts.hpp new file mode 100644 index 0000000..81b56ff --- /dev/null +++ b/src/IW4/Assets/AddonMapEnts.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IAddonMapEnts : public IAsset + { + private: + std::string name_; + AddonMapEnts* asset_ = nullptr; + + public: + AddonMapEnts* parse(std::string name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(AddonMapEnts* asset); + }; + } +} diff --git a/src/IW4/Assets/ClipMap.cpp b/src/IW4/Assets/ClipMap.cpp new file mode 100644 index 0000000..5841f24 --- /dev/null +++ b/src/IW4/Assets/ClipMap.cpp @@ -0,0 +1,631 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../iw5/Assets/ClipMap.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + clipMap_t* IClipMap::parse(const std::string& name, ZoneMemory* mem) + { + auto* iw5_colmap = IW5::IClipMap::parse(name, mem); + + if (!iw5_colmap) + { + return nullptr; + } + + // allocate collision map + auto* colmap = mem->Alloc(); + + // copy data from IW5 to IW4 + colmap->name = iw5_colmap->name; + colmap->isInUse = iw5_colmap->isInUse; + + colmap->numCPlanes = iw5_colmap->info.numCPlanes; + colmap->cPlanes = (cplane_s*)iw5_colmap->info.cPlanes; + + colmap->numStaticModels = iw5_colmap->numStaticModels; + colmap->staticModelList = (cStaticModel_s*)iw5_colmap->staticModelList; + + colmap->numMaterials = iw5_colmap->info.numMaterials; + colmap->materials = (dmaterial_t*)iw5_colmap->info.materials; + + colmap->numCBrushSides = iw5_colmap->info.numCBrushSides; + colmap->cBrushSides = (cbrushside_t*)iw5_colmap->info.cBrushSides; + + colmap->numCBrushEdges = iw5_colmap->info.numCBrushEdges; + colmap->cBrushEdges = (cbrushedge_t*)iw5_colmap->info.cBrushEdges; + + colmap->numCNodes = iw5_colmap->numCNodes; + colmap->cNodes = (cNode_t*)iw5_colmap->cNodes; + + colmap->numCLeaf = iw5_colmap->numCLeaf; + colmap->cLeaf = (cLeaf_t*)iw5_colmap->cLeaf; + + colmap->numCLeafBrushNodes = iw5_colmap->info.numCLeafBrushNodes; + colmap->cLeafBrushNodes = (cLeafBrushNode_s*)iw5_colmap->info.cLeafBrushNodes; + + colmap->numLeafBrushes = iw5_colmap->info.numLeafBrushes; + colmap->leafBrushes = iw5_colmap->info.leafBrushes; + + // leafSurfaces todo! + + colmap->numVerts = iw5_colmap->numVerts; + colmap->verts = (VecInternal<3>*)iw5_colmap->verts; + + colmap->numTriIndices = iw5_colmap->numTriIndices; + colmap->triIndices = iw5_colmap->triIndices; + colmap->triEdgeIsWalkable = iw5_colmap->triEdgeIsWalkable; + + colmap->numCollisionBorders = iw5_colmap->numCollisionBorders; + colmap->collisionBorders = (CollisionBorder*)iw5_colmap->collisionBorders; + + colmap->numCollisionPartitions = iw5_colmap->numCollisionPartitions; + colmap->collisionPartitions = (CollisionPartition*)iw5_colmap->collisionPartitions; + + colmap->numCollisionAABBTrees = iw5_colmap->numCollisionAABBTrees; + colmap->collisionAABBTrees = (CollisionAabbTree*)iw5_colmap->collisionAABBTrees; + + // cmodels! + colmap->numCModels = iw5_colmap->numCModels; + colmap->cModels = new cmodel_t[colmap->numCModels]; + memset(colmap->cModels, 0, sizeof(cmodel_t) * colmap->numCModels); + + for (int i = 0; i < colmap->numCModels; i++) + { + memcpy(colmap->cModels[i]._portpad0, iw5_colmap->cModels[i]._portpad0, 28); + memcpy(colmap->cModels[i]._portpad1, iw5_colmap->cModels[i]._portpad1, 40); + } + + colmap->numBrushes = iw5_colmap->info.numBrushes; + colmap->brushes = (cbrush_t*)iw5_colmap->info.brushes; + colmap->brushBounds = (Bounds*)iw5_colmap->info.brushBounds; + colmap->brushContents = iw5_colmap->info.brushContents; + + colmap->mapEnts = (MapEnts*)iw5_colmap->mapEnts; + + colmap->smodelNodeCount = iw5_colmap->smodelNodeCount; + colmap->smodelNodes = (SModelAabbNode*)iw5_colmap->smodelNodes; + + // return converted colmap + return colmap; + } + + void IClipMap::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data()).clipmap; + } + } + + void IClipMap::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IClipMap::load_depending(IZone* zone) + { + auto* data = this->asset_; + + if (data->staticModelList) + { + for (auto i = 0u; i < data->numStaticModels; i++) + { + if (data->staticModelList[i].xmodel) + { + zone->add_asset_of_type(xmodel, data->staticModelList[i].xmodel->name); + } + } + } + + if (data->dynEntDefList[0]) + { + for (auto i = 0u; i < data->dynEntCount[0]; i++) + { + if (data->dynEntDefList[0][i].xModel) + { + zone->add_asset_of_type(xmodel, data->dynEntDefList[0][i].xModel->name); + } + if (data->dynEntDefList[0][i].destroyFx) + { + // zone->AddAssetOfType(fx, data->dynEntDefList[0][i].destroyFx->name); + } + if (data->dynEntDefList[0][i].physPreset) + { + zone->add_asset_of_type(physpreset, data->dynEntDefList[0][i].physPreset->name); + } + } + } + + if (data->dynEntDefList[1]) + { + for (auto i = 0u; i < data->dynEntCount[1]; i++) + { + if (data->dynEntDefList[1][i].xModel) + { + zone->add_asset_of_type(xmodel, data->dynEntDefList[1][i].xModel->name); + } + if (data->dynEntDefList[1][i].destroyFx) + { + // zone->AddAssetOfType(fx, data->dynEntDefList[1][i].destroyFx->name); + } + if (data->dynEntDefList[1][i].physPreset) + { + zone->add_asset_of_type(physpreset, data->dynEntDefList[1][i].physPreset->name); + } + } + } + + if (data->mapEnts) + { + zone->add_asset_of_type(map_ents, this->asset_->mapEnts->name); + } + } + + std::string IClipMap::name() + { + return this->name_; + } + + std::int32_t IClipMap::type() + { + return col_map_sp; + } + + void IClipMap::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->cPlanes) + { + cplane_s* dest_cplanes = nullptr; + dest->cPlanes = buf->write_s(3, data->cPlanes, data->numCPlanes, sizeof cplane_s, &dest_cplanes); + } + + if (data->staticModelList) + { + buf->align(3); + auto* static_model = buf->write(data->staticModelList, data->numStaticModels); + + for (auto i = 0; i < data->numStaticModels; i++) + { + if (data->staticModelList[i].xmodel) + { + static_model[i].xmodel = reinterpret_cast(zone->get_asset_pointer( + xmodel, data->staticModelList[i].xmodel->name)); + } + } + + ZoneBuffer::clear_pointer(&data->staticModelList); + } + + if (data->materials) + { + dmaterial_t* dmaterial; + dest->materials = buf->write_s(3, data->materials, data->numMaterials, sizeof dmaterial_t, &dmaterial); + + if (dest->materials == reinterpret_cast(-1)) + { + for (auto i = 0; i < data->numMaterials; i++) + { + if (data->materials[i].material) + { + dmaterial[i].material = buf->write_str(data->materials[i].material); + } + } + } + } + + if (data->cBrushSides) + { + cbrushside_t* brush_side; + dest->cBrushSides = buf->write_s(3, data->cBrushSides, data->numCBrushSides, sizeof cbrushside_t, + &brush_side); + + if (dest->cBrushSides == reinterpret_cast(-1)) + { + for (auto i = 0; i < data->numCBrushSides; i++) + { + if (data->cBrushSides[i].plane) + { + // should use zone pointer, no need to convert + brush_side[i].plane = buf->write_s(3, data->cBrushSides[i].plane); + } + + if (zone->get_target() != zone_target::pc) + { + endian_convert(&brush_side[i].plane); + endian_convert(&brush_side[i].materialNum); + } + } + } + } + + if (data->cBrushEdges) + { + dest->cBrushEdges = buf->write_s(0, data->cBrushEdges, data->numCBrushEdges); + } + + if (data->cNodes) + { + buf->align(3); + auto* node = buf->write(data->cNodes, data->numCNodes); + + for (auto i = 0; i < data->numCNodes; i++) + { + if (data->cNodes[i].plane) + { + // should use zone pointer, no need to convert + node[i].plane = buf->write_s(3, data->cNodes[i].plane); + } + } + + ZoneBuffer::clear_pointer(&dest->cNodes); + } + + if (data->cLeaf) + { + buf->align(3); + auto* dest_leaf = buf->write(data->cLeaf, data->numCLeaf); + ZoneBuffer::clear_pointer(&dest->cLeaf); + } + + if (data->leafBrushes) + { + short* dest_leaf_brushes = nullptr; + dest->leafBrushes = buf->write_s(1, data->leafBrushes, data->numLeafBrushes, sizeof (short), &dest_leaf_brushes); + } + + if (data->cLeafBrushNodes) + { + cLeafBrushNode_s* leaf_brush_node = nullptr; + dest->cLeafBrushNodes = buf->write_s(3, data->cLeafBrushNodes, data->numCLeafBrushNodes, + sizeof cLeafBrushNode_s, &leaf_brush_node); + + if (dest->cLeafBrushNodes == reinterpret_cast(-1)) + { + for (auto i = 0; i < data->numCLeafBrushNodes; i++) + { + if (data->cLeafBrushNodes[i].leafBrushCount > 0 && data->cLeafBrushNodes[i].data.leaf.brushes) + { + unsigned short* dest_leaf_brushes = nullptr; + leaf_brush_node[i].data.leaf.brushes = buf->write_s( + 1, data->cLeafBrushNodes[i].data.leaf.brushes, data->cLeafBrushNodes[i].leafBrushCount, sizeof (unsigned short), &dest_leaf_brushes); + } + } + } + } + + if (data->verts) + { + buf->align(3); + auto* dest_verts = buf->write(data->verts, data->numVerts); + ZoneBuffer::clear_pointer(&dest->verts); + } + + if (data->triIndices) + { + buf->align(1); + auto* dest_tri_indices = buf->write(data->triIndices, data->numTriIndices * 3); + ZoneBuffer::clear_pointer(&dest->triIndices); + } + + if (data->triEdgeIsWalkable) + { + buf->align(0); + buf->write(data->triEdgeIsWalkable, 4 * ((3 * data->numTriIndices + 31) >> 5)); + ZoneBuffer::clear_pointer(&dest->triEdgeIsWalkable); + } + + if (data->collisionBorders) + { + buf->align(3); + auto* dest_borders = buf->write_p(data->collisionBorders, data->numCollisionBorders); + ZoneBuffer::clear_pointer(&dest->collisionBorders); + } + + if (data->collisionPartitions) + { + buf->align(3); + auto* collision_partition = buf->write(data->collisionPartitions, data->numCollisionPartitions); + + for (auto i = 0; i < data->numCollisionPartitions; i++) + { + if (data->collisionPartitions[i].borders) + { + collision_partition[i].borders = buf->write_s(3, data->collisionPartitions[i].borders); + } + } + + ZoneBuffer::clear_pointer(&dest->collisionPartitions); + } + + if (data->collisionAABBTrees) + { + buf->align(15); + auto* dest_aabb_trees = buf->write(data->collisionAABBTrees, data->numCollisionAABBTrees); + ZoneBuffer::clear_pointer(&dest->collisionAABBTrees); + } + + if (data->cModels) + { + buf->align(3); + auto* dest_c_models = buf->write(data->cModels, data->numCModels); + ZoneBuffer::clear_pointer(&dest->cModels); + } + + // brushes + if (data->brushes) + { + cbrush_t* brush = nullptr; + dest->brushes = buf->write_s(127, data->brushes, data->numBrushes, sizeof cbrush_t, &brush); + + if (dest->brushes == reinterpret_cast(-1)) + { + for (auto i = 0; i < data->numBrushes; i++) + { + if (data->brushes[i].sides) + { + cbrushside_t* side = nullptr; + brush[i].sides = buf->write_s(3, data->brushes[i].sides, 1, sizeof cbrushside_t, &side); + + if (brush[i].sides == reinterpret_cast(-1) && side) + { + if (side->plane) + { + // should use zone pointer + side->plane = buf->write_s(3, side->plane); + } + } + } + + if (data->brushes[i].edge) + { + // should use zone pointer + brush[i].edge = buf->write_s(0, data->brushes[i].edge); + } + } + } + } + + // brushBounds + if (data->brushBounds) + { + Bounds* dest_bounds = nullptr; + dest->brushBounds = buf->write_s(127, data->brushBounds, data->numBrushes, sizeof Bounds, &dest_bounds); + } + + // brushContents + if (data->brushContents) + { + int* destBrushContents = nullptr; + dest->brushContents = buf->write_s(3, data->brushContents, data->numBrushes, sizeof (int), &destBrushContents); + } + + if (data->smodelNodes) + { + buf->align(3); + auto* dest_smodels = buf->write(data->smodelNodes, data->smodelNodeCount); + ZoneBuffer::clear_pointer(&dest->smodelNodes); + } + + if (data->mapEnts) + { + dest->mapEnts = reinterpret_cast(zone->get_asset_pointer(map_ents, this->name())); + } + + if (data->dynEntDefList[0]) + { + buf->align(3); + auto* dyn_entity_def = buf->write(data->dynEntDefList[0], data->dynEntCount[0]); + + for (std::uint16_t i = 0; i < data->dynEntCount[0]; i++) + { + if (data->dynEntDefList[0][i].xModel) + { + dyn_entity_def[i].xModel = reinterpret_cast(zone->get_asset_pointer( + xmodel, data->dynEntDefList[0][i].xModel->name)); + } + if (data->dynEntDefList[0][i].destroyFx) + { + // dyn_entity_def[i].destroyFx = reinterpret_cast(zone->GetAssetPointer(fx, data->dynEntDefList[0][i].destroyFx->name)); + } + if (data->dynEntDefList[0][i].physPreset) + { + dyn_entity_def[i].physPreset = reinterpret_cast(zone->get_asset_pointer( + physpreset, data->dynEntDefList[0][i].physPreset->name)); + } + + /*if (data->dynEntDefList[0][i].hinge) + { + dyn_entity_def[i].hinge = buf->write_s(3, dyn_entity_def[i].hinge, 1); + }*/ + } + + ZoneBuffer::clear_pointer(&dest->dynEntDefList[0]); + } + + if (data->dynEntDefList[1]) + { + buf->align(3); + auto* dyn_entity_def = buf->write(data->dynEntDefList[1], data->dynEntCount[1]); + + for (std::uint16_t i = 0; i < data->dynEntCount[1]; i++) + { + if (data->dynEntDefList[1][i].xModel) + { + dyn_entity_def[i].xModel = reinterpret_cast(zone->get_asset_pointer( + xmodel, data->dynEntDefList[1][i].xModel->name)); + } + if (data->dynEntDefList[1][i].destroyFx) + { + // dyn_entity_def[i].destroyFx = reinterpret_cast(zone->GetAssetPointer(fx, data->dynEntDefList[1][i].destroyFx->name)); + } + if (data->dynEntDefList[1][i].physPreset) + { + dyn_entity_def[i].physPreset = reinterpret_cast(zone->get_asset_pointer( + physpreset, data->dynEntDefList[1][i].physPreset->name)); + } + + /*if (data->dynEntDefList[1][i].hinge) + { + dyn_entity_def[i].hinge = buf->write_s(3, dyn_entity_def[i].hinge, 1); + }*/ + } + + ZoneBuffer::clear_pointer(&dest->dynEntDefList[1]); + } + + buf->push_stream(2); + + if (data->dynEntPoseList[0]) + { + buf->align(3); + buf->write(data->dynEntPoseList[0], data->dynEntCount[0]); + ZoneBuffer::clear_pointer(&dest->dynEntPoseList[0]); + } + + if (data->dynEntPoseList[1]) + { + buf->align(3); + buf->write(data->dynEntPoseList[1], data->dynEntCount[1]); + ZoneBuffer::clear_pointer(&dest->dynEntPoseList[1]); + } + + if (data->dynEntClientList[0]) + { + buf->align(3); + buf->write(data->dynEntClientList[0], data->dynEntCount[0]); + ZoneBuffer::clear_pointer(&dest->dynEntClientList[0]); + } + + if (data->dynEntClientList[1]) + { + buf->align(3); + buf->write(data->dynEntClientList[1], data->dynEntCount[1]); + ZoneBuffer::clear_pointer(&dest->dynEntClientList[1]); + } + + if (data->dynEntCollList[0]) + { + buf->align(3); + buf->write(data->dynEntCollList[0], data->dynEntCount[0]); + ZoneBuffer::clear_pointer(&dest->dynEntCollList[0]); + } + + if (data->dynEntCollList[1]) + { + buf->align(3); + buf->write(data->dynEntCollList[1], data->dynEntCount[1]); + ZoneBuffer::clear_pointer(&dest->dynEntCollList[1]); + } + + buf->pop_stream(); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IClipMap::dump(clipMap_t* asset) + { + auto* iw5_clipmap = new IW5::clipMap_t; + memset(iw5_clipmap, 0, sizeof IW5::clipMap_t); + + // convert clipmap to IW5 format + iw5_clipmap->name = asset->name; + iw5_clipmap->isInUse = asset->isInUse; + + iw5_clipmap->info.numCPlanes = asset->numCPlanes; + iw5_clipmap->info.cPlanes = (IW5::cplane_s*)asset->cPlanes; + + iw5_clipmap->numStaticModels = asset->numStaticModels; + iw5_clipmap->staticModelList = (IW5::cStaticModel_s*)asset->staticModelList; + + iw5_clipmap->info.numMaterials = asset->numMaterials; + iw5_clipmap->info.materials = (IW5::dmaterial_t*)asset->materials; + + iw5_clipmap->info.numCBrushSides = asset->numCBrushSides; + iw5_clipmap->info.cBrushSides = (IW5::cbrushside_t*)asset->cBrushSides; + + iw5_clipmap->info.numCBrushEdges = asset->numCBrushEdges; + iw5_clipmap->info.cBrushEdges = (IW5::cbrushedge_t*)asset->cBrushEdges; + + iw5_clipmap->numCNodes = asset->numCNodes; + iw5_clipmap->cNodes = (IW5::cNode_t*)asset->cNodes; + + iw5_clipmap->numCLeaf = asset->numCLeaf; + iw5_clipmap->cLeaf = (IW5::cLeaf_t*)asset->cLeaf; + + iw5_clipmap->info.numCLeafBrushNodes = asset->numCLeafBrushNodes; + iw5_clipmap->info.cLeafBrushNodes = (IW5::cLeafBrushNode_s*)asset->cLeafBrushNodes; + + iw5_clipmap->info.numLeafBrushes = asset->numLeafBrushes; + iw5_clipmap->info.leafBrushes = asset->leafBrushes; + + // leafSurfaces todo! + + iw5_clipmap->numVerts = asset->numVerts; + iw5_clipmap->verts = (IW5::VecInternal<3>*)asset->verts; + + iw5_clipmap->numTriIndices = asset->numTriIndices; + iw5_clipmap->triIndices = asset->triIndices; + iw5_clipmap->triEdgeIsWalkable = asset->triEdgeIsWalkable; + + iw5_clipmap->numCollisionBorders = asset->numCollisionBorders; + iw5_clipmap->collisionBorders = (IW5::CollisionBorder*)asset->collisionBorders; + + iw5_clipmap->numCollisionPartitions = asset->numCollisionPartitions; + iw5_clipmap->collisionPartitions = (IW5::CollisionPartition*)asset->collisionPartitions; + + iw5_clipmap->numCollisionAABBTrees = asset->numCollisionAABBTrees; + iw5_clipmap->collisionAABBTrees = (IW5::CollisionAabbTree*)asset->collisionAABBTrees; + + // cmodels! + iw5_clipmap->numCModels = asset->numCModels; + iw5_clipmap->cModels = new IW5::cmodel_t[iw5_clipmap->numCModels]; + memset(iw5_clipmap->cModels, 0, sizeof(IW5::cmodel_t) * iw5_clipmap->numCModels); + + for (int i = 0; i < iw5_clipmap->numCModels; i++) + { + memcpy(iw5_clipmap->cModels[i]._portpad0, asset->cModels[i]._portpad0, 28); + memcpy(iw5_clipmap->cModels[i]._portpad1, asset->cModels[i]._portpad1, 40); + } + + iw5_clipmap->info.numBrushes = asset->numBrushes; + iw5_clipmap->info.brushes = (IW5::cbrush_t*)asset->brushes; + iw5_clipmap->info.brushBounds = (IW5::Bounds*)asset->brushBounds; + iw5_clipmap->info.brushContents = asset->brushContents; + + iw5_clipmap->mapEnts = (IW5::MapEnts*)asset->mapEnts; + iw5_clipmap->stageCount = asset->mapEnts->stageCount; + iw5_clipmap->stages = (IW5::Stage*)asset->mapEnts->stageNames; + + iw5_clipmap->smodelNodeCount = asset->smodelNodeCount; + iw5_clipmap->smodelNodes = (IW5::SModelAabbNode*)asset->smodelNodes; + + // dump clipmap + IW5::IClipMap::dump(iw5_clipmap); + + // free memory_ + delete[] iw5_clipmap->cModels; + delete iw5_clipmap; + } + } +} diff --git a/src/IW4/Assets/ClipMap.hpp b/src/IW4/Assets/ClipMap.hpp new file mode 100644 index 0000000..2ec0907 --- /dev/null +++ b/src/IW4/Assets/ClipMap.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IClipMap : public IAsset + { + private: + std::string name_; + clipMap_t* asset_ = nullptr; + + public: + clipMap_t* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(clipMap_t* asset); + }; + } +} diff --git a/src/IW4/Assets/ComWorld.cpp b/src/IW4/Assets/ComWorld.cpp new file mode 100644 index 0000000..88b224b --- /dev/null +++ b/src/IW4/Assets/ComWorld.cpp @@ -0,0 +1,131 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../iw5/Assets/ComWorld.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + ComWorld* IComWorld::parse(const std::string& name, ZoneMemory* mem) + { + auto* iw5_comworld = IW5::IComWorld::parse(name, mem); + + if (!iw5_comworld) + { + return nullptr; + } + + // fixup data + auto* light_array = mem->Alloc(iw5_comworld->primaryLightCount); + + // convert structure + for (auto i = 0u; i < iw5_comworld->primaryLightCount; i++) + { + memcpy(light_array[i]._portpad0, iw5_comworld->primaryLights[i]._portpad0, 28); + memcpy(light_array[i]._portpad1, iw5_comworld->primaryLights[i]._portpad1, 40); + } + + // set pointer + iw5_comworld->primaryLights = (IW5::ComPrimaryLight*)light_array; + + // asset is the exact same so just cast to the correct type here + return (ComWorld*)iw5_comworld; + } + + void IComWorld::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data()).comworld; + } + } + + void IComWorld::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IComWorld::load_depending(IZone* zone) + { + auto* asset = this->asset_; + + for (auto i = 0u; i < asset->primaryLightCount; i++) + { + if (asset->primaryLights[i].defName) + { + zone->add_asset_of_type(lightdef, asset->primaryLights[i].defName); + } + } + } + + std::string IComWorld::name() + { + return this->name_; + } + + std::int32_t IComWorld::type() + { + return com_map; + } + + void IComWorld::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + + dest->name = buf->write_str(this->name()); + + if (data->primaryLights) + { + buf->align(3); + auto* primary_light = buf->write(data->primaryLights, data->primaryLightCount); + + for (auto i = 0u; i < data->primaryLightCount; i++) + { + if (data->primaryLights[i].defName) + { + primary_light[i].defName = buf->write_str(data->primaryLights[i].defName); + } + } + } + + buf->pop_stream(); + } + + void IComWorld::dump(ComWorld* asset) + { + // alloc comworld + auto* iw5_comworld = new IW5::ComWorld; + memcpy(iw5_comworld, asset, sizeof ComWorld); + + // alloc lights + iw5_comworld->primaryLights = new IW5::ComPrimaryLight[iw5_comworld->primaryLightCount]; + memset(iw5_comworld->primaryLights, 0, sizeof(IW5::ComPrimaryLight) * iw5_comworld->primaryLightCount); + + // copy data + for (unsigned int i = 0; i < iw5_comworld->primaryLightCount; i++) + { + memcpy(iw5_comworld->primaryLights[i]._portpad0, asset->primaryLights[i]._portpad0, 28); + memcpy(iw5_comworld->primaryLights[i]._portpad1, asset->primaryLights[i]._portpad1, 40); + } + + // dump comworld + IW5::IComWorld::dump(iw5_comworld); + + // free memory_ + delete[] iw5_comworld->primaryLights; + delete[] iw5_comworld; + } + } +} diff --git a/src/IW4/Assets/ComWorld.hpp b/src/IW4/Assets/ComWorld.hpp new file mode 100644 index 0000000..802d698 --- /dev/null +++ b/src/IW4/Assets/ComWorld.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IComWorld : public IAsset + { + private: + std::string name_; + ComWorld* asset_ = nullptr; + + public: + ComWorld* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(ComWorld* asset); + }; + } +} diff --git a/src/IW4/Assets/FontDef.cpp b/src/IW4/Assets/FontDef.cpp new file mode 100644 index 0000000..cc4b277 --- /dev/null +++ b/src/IW4/Assets/FontDef.cpp @@ -0,0 +1,101 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/FontDef.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void IFontDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data()).font; + } + } + + void IFontDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IFontDef::load_depending(IZone* zone) + { + auto* data = this->asset_; + + if (data->material) + { + zone->add_asset_of_type(material, data->material->name); + } + + if (data->glowMaterial) + { + zone->add_asset_of_type(material, data->glowMaterial->name); + } + } + + std::string IFontDef::name() + { + return this->name_; + } + + std::int32_t IFontDef::type() + { + return font; + } + + void IFontDef::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->fontName = buf->write_str(this->name()); + + if (data->material) + { + dest->material = reinterpret_cast( + zone->get_asset_pointer(material, data->material->name) + ); + } + + if (data->glowMaterial) + { + dest->glowMaterial = reinterpret_cast( + zone->get_asset_pointer(material, data->glowMaterial->name) + ); + } + + if (data->glyphs) + { + buf->align(3); + auto* dest_glyphs = buf->write(data->glyphs, data->glyphCount); + ZoneBuffer::clear_pointer(&dest->glyphs); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + Font_s* IFontDef::parse(const std::string& name, ZoneMemory* mem) + { + return reinterpret_cast(IW5::IFontDef::parse(name, mem)); + } + + void IFontDef::dump(Font_s* asset) + { + IW5::IFontDef::dump(reinterpret_cast(asset)); + } + } +} diff --git a/src/IW4/Assets/FontDef.hpp b/src/IW4/Assets/FontDef.hpp new file mode 100644 index 0000000..c70c1aa --- /dev/null +++ b/src/IW4/Assets/FontDef.hpp @@ -0,0 +1,34 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IFontDef : public IAsset + { + private: + std::string name_; + Font_s* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(Font_s* asset); + static Font_s* parse(const std::string& name, ZoneMemory* mem); + }; + } +} diff --git a/src/IW4/Assets/FxEffectDef.cpp b/src/IW4/Assets/FxEffectDef.cpp new file mode 100644 index 0000000..06700b5 --- /dev/null +++ b/src/IW4/Assets/FxEffectDef.cpp @@ -0,0 +1,403 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/FxEffectDef.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void IFxEffectDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).fx; + } + } + + void IFxEffectDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + FxEffectDef* IFxEffectDef::parse(const std::string& name, ZoneMemory* mem) + { + const auto iw5_fx = IW5::IFxEffectDef::parse(name, mem); + + if (!iw5_fx) + { + return nullptr; + } + + const auto fx = mem->Alloc(); + + memcpy(fx, iw5_fx, sizeof FxEffectDef); + + // transform elem defs + const auto elem_count = fx->elemDefCountLooping + fx->elemDefCountEmission + fx->elemDefCountOneShot; + fx->elemDefs = mem->Alloc(elem_count); + for (auto i = 0; i < elem_count; i++) + { + memcpy(&fx->elemDefs[i], &iw5_fx->elemDefs[i], sizeof FxElemDef); + + if (fx->elemDefs[i].extended.trailDef) + { + // iw5 feature, unsupported on iw4 + if (fx->elemDefs[i].elemType == 9) + { + fx->elemDefs[i].extended.trailDef = nullptr; + } + } + } + + return fx; + } + + void IFxEffectDef::load_depending(IZone* zone) + { + auto* data = this->asset_; + + auto load_fx_elem_visuals = [zone](FxElemDef* def, FxElemDefVisuals* vis) + { + if (def->elemType == 11) + { + for (auto i = 0; i < def->visualCount; i++) + { + if (vis->markArray[i]) + { + if (vis->markArray[i][0]) + { + zone->add_asset_of_type(material, vis->markArray[i][0]->name); + } + if (vis->markArray[i][1]) + { + zone->add_asset_of_type(material, vis->markArray[i][1]->name); + } + } + } + } + else if (def->visualCount > 1) + { + for (auto i = 0; i < def->visualCount; i++) + { + if (def->elemType == 12 && vis->array[i].effectDef) + { + zone->add_asset_of_type(fx, vis->array[i].effectDef->name); + } + else if (def->elemType == 10 && vis->array[i].soundName) + { + zone->add_asset_of_type(sound, vis->array[i].soundName); + } + else if (def->elemType == 7 && vis->array[i].xmodel) + { + zone->add_asset_of_type(xmodel, vis->array[i].xmodel->name); + } + else + { + if (def->elemType != 8 && vis->array[i].material) + { + zone->add_asset_of_type(material, vis->array[i].material->name); + } + } + } + } + else + { + if (def->elemType == 12 && vis->instance.effectDef) + { + zone->add_asset_of_type(fx, vis->instance.effectDef->name); + } + else if (def->elemType == 10 && vis->instance.soundName) + { + zone->add_asset_of_type(sound, vis->instance.soundName); + } + else if (def->elemType == 7 && vis->instance.xmodel) + { + zone->add_asset_of_type(xmodel, vis->instance.xmodel->name); + } + else + { + if (def->elemType != 8 && vis->instance.material) + { + zone->add_asset_of_type(material, vis->instance.material->name); + } + } + } + }; + + // Loop through frames + for (auto i = 0; i < data->elemDefCountEmission + data->elemDefCountLooping + data->elemDefCountOneShot; i++) + { + auto* def = &data->elemDefs[i]; + + // Sub-FX effects + if (def->effectEmitted) + { + zone->add_asset_of_type(fx, def->effectEmitted->name); + } + if (def->effectOnDeath) + { + zone->add_asset_of_type(fx, def->effectOnDeath->name); + } + if (def->effectOnImpact) + { + zone->add_asset_of_type(fx, def->effectOnImpact->name); + } + + // Visuals + load_fx_elem_visuals(def, &def->visuals); + } + } + + std::string IFxEffectDef::name() + { + return this->name_; + } + + std::int32_t IFxEffectDef::type() + { + return fx; + } + + void IFxEffectDef::write_fx_elem_visuals(IZone* zone, ZoneBuffer* buf, FxElemDef* def, + FxElemVisuals* dest) + { + auto* data = dest; + + switch (def->elemType) + { + case FX_ELEM_TYPE_RUNNER: + { + buf->write_str(data->effectDef->name); + ZoneBuffer::clear_pointer(&dest->effectDef); + break; + } + case FX_ELEM_TYPE_SOUND: + { + if (data->soundName) + { + buf->write_str(data->soundName); + ZoneBuffer::clear_pointer(&dest->soundName); + } + break; + } + case FX_ELEM_TYPE_SPOT_LIGHT: + { + dest->anonymous = (data->anonymous) + ? zone->get_asset_pointer(lightdef, ((GfxLightDef*)data->anonymous)->name) + : nullptr; + break; + } + case FX_ELEM_TYPE_MODEL: + { + dest->xmodel = (data->xmodel) + ? reinterpret_cast(zone->get_asset_pointer(xmodel, data->xmodel->name)) + : nullptr; + break; + } + default: + { + if (def->elemType != FX_ELEM_TYPE_OMNI_LIGHT) + { + dest->material = (data->material) + ? reinterpret_cast(zone->get_asset_pointer( + material, data->material->name)) + : nullptr; + } + } + } + } + + void IFxEffectDef::write_fx_elem_def_visuals(IZone* zone, ZoneBuffer* buf, FxElemDef* def, + FxElemDefVisuals* dest) + { + auto* data = dest; + + if (def->elemType == FX_ELEM_TYPE_DECAL) + { + if (data->markArray) + { + auto destvisuals = buf->write(data->markArray, def->visualCount); + + for (int i = 0; i < def->visualCount; i++) + { + destvisuals[i][0] = (data->markArray[i][0]) + ? reinterpret_cast(zone->get_asset_pointer( + material, data->markArray[i][0]->name)) + : nullptr; + destvisuals[i][1] = (data->markArray[i][1]) + ? reinterpret_cast(zone->get_asset_pointer( + material, data->markArray[i][1]->name)) + : nullptr; + } + } + } + else if (def->visualCount > 1) + { + auto* vis = buf->write(data->array, def->visualCount); + + for (auto i = 0; i < def->visualCount; i++) + { + write_fx_elem_visuals(zone, buf, def, &vis[i]); + } + } + else + { + write_fx_elem_visuals(zone, buf, def, &dest->instance); + } + } + + void IFxEffectDef::write_fx_elem_def(IZone* zone, ZoneBuffer* buf, FxElemDef* dest) + { + auto* data = dest; + + if (data->velSamples) + { + buf->align(3); + buf->write(data->velSamples, data->velIntervalCount + 1); + ZoneBuffer::clear_pointer(&dest->velSamples); + } + + if (data->visSamples) + { + buf->align(3); + buf->write(data->visSamples, data->visStateIntervalCount + 1); + ZoneBuffer::clear_pointer(&dest->visSamples); + } + + write_fx_elem_def_visuals(zone, buf, data, &dest->visuals); + + if (data->effectOnImpact) + { + buf->write_str_raw(data->effectOnImpact->name); + ZoneBuffer::clear_pointer(&dest->effectOnImpact); + } + + if (data->effectOnDeath) + { + buf->write_str_raw(data->effectOnDeath->name); + ZoneBuffer::clear_pointer(&dest->effectOnDeath); + } + + if (data->effectEmitted) + { + buf->write_str_raw(data->effectEmitted->name); + ZoneBuffer::clear_pointer(&dest->effectEmitted); + } + + if (data->extended.trailDef) + { + if (data->elemType == FX_ELEM_TYPE_TRAIL) + { + if (data->extended.trailDef) + { + buf->align(3); + buf->write(data->extended.trailDef, sizeof(FxTrailDef)); + + if (data->extended.trailDef->verts) + { + buf->align(3); + buf->write(data->extended.trailDef->verts, data->extended.trailDef->vertCount); + } + + if (data->extended.trailDef->inds) + { + buf->align(1); + buf->write(data->extended.trailDef->inds, data->extended.trailDef->indCount); + } + + ZoneBuffer::clear_pointer(&dest->extended.trailDef); + } + } + else if (data->elemType == FX_ELEM_TYPE_SPARKFOUNTAIN) + { + if (data->extended.sparkFountain) + { + buf->align(3); + buf->write(data->extended.sparkFountain); + ZoneBuffer::clear_pointer(&dest->extended.sparkFountain); + } + } + else if (data->elemType == FX_ELEM_TYPE_SPOT_LIGHT) + { + if (data->extended.unknownDef) + { + buf->align(3); + buf->write_stream(data->extended.unknownDef, 24); + ZoneBuffer::clear_pointer(&dest->extended.unknownDef); + } + } + else + { + if (data->extended.unknownDef) + { + buf->align(1); + buf->write_stream(data->extended.unknownDef, sizeof(BYTE)); + ZoneBuffer::clear_pointer(&dest->extended.unknownDef); + } + } + } + } + + void IFxEffectDef::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->elemDefs) + { + buf->align(3); + auto* fx_elem_def = buf->write(data->elemDefs, + data->elemDefCountEmission + data->elemDefCountLooping + data-> + elemDefCountOneShot); + + for (std::int32_t i = 0; i < (data->elemDefCountEmission + data->elemDefCountLooping + data-> + elemDefCountOneShot); i++) + { + write_fx_elem_def(zone, buf, &fx_elem_def[i]); + } + + ZoneBuffer::clear_pointer(&dest->elemDefs); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IFxEffectDef::dump(FxEffectDef* asset) + { + auto* iw5_fx = new IW5::FxEffectDef; + memcpy(iw5_fx, asset, sizeof FxEffectDef); + memset(iw5_fx->pad, 0, sizeof iw5_fx->pad); + + // alloc elemdefs + const auto elem_def_count = iw5_fx->elemDefCountEmission + iw5_fx->elemDefCountLooping + iw5_fx->elemDefCountOneShot; + iw5_fx->elemDefs = new IW5::FxElemDef[elem_def_count]; + + // transform elemdefs to iw5 format + for (auto i = 0; i < elem_def_count; i++) + { + memcpy(&iw5_fx->elemDefs[i], &asset->elemDefs[i], sizeof IW4::FxElemDef); + iw5_fx->elemDefs[i].pad = 0; + } + + IW5::IFxEffectDef::dump(iw5_fx); + + delete[] iw5_fx->elemDefs; + delete iw5_fx; + } + } +} diff --git a/src/IW4/Assets/FxEffectDef.hpp b/src/IW4/Assets/FxEffectDef.hpp new file mode 100644 index 0000000..a8b32e2 --- /dev/null +++ b/src/IW4/Assets/FxEffectDef.hpp @@ -0,0 +1,41 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IFxEffectDef : public IAsset + { + private: + std::string name_; + FxEffectDef* asset_ = nullptr; + + static void write_fx_elem_def_visuals(IZone* zone, ZoneBuffer* buf, FxElemDef* def, + FxElemDefVisuals* dest); + static void write_fx_elem_def(IZone* zone, ZoneBuffer* buf, FxElemDef* dest); + static void write_fx_elem_visuals(IZone* zone, ZoneBuffer* buf, FxElemDef* def, + FxElemVisuals* dest); + + FxEffectDef* parse(const std::string& name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(FxEffectDef* asset); + }; + } +} diff --git a/src/IW4/Assets/FxWorld.cpp b/src/IW4/Assets/FxWorld.cpp new file mode 100644 index 0000000..a89fff0 --- /dev/null +++ b/src/IW4/Assets/FxWorld.cpp @@ -0,0 +1,209 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW5/Assets/FxWorld.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + FxWorld* IFxWorld::parse(const std::string& name, ZoneMemory* mem) + { + auto* iw5_fxworld = IW5::IFxWorld::parse(name, mem); + + if (!iw5_fxworld) + { + return nullptr; + } + + // asset is actually the same so just cast + return reinterpret_cast(iw5_fxworld); + } + + void IFxWorld::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data()).fxworld; + } + } + + void IFxWorld::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IFxWorld::load_depending(IZone* zone) + { + auto* data = this->asset_; + if (data->glassSys.defs) + { + for (unsigned int i = 0; i < data->glassSys.defCount; i++) + { + if (data->glassSys.defs[i].physPreset) + { + zone->add_asset_of_type(physpreset, data->glassSys.defs[i].physPreset->name); + } + if (data->glassSys.defs[i].material) + { + zone->add_asset_of_type(material, data->glassSys.defs[i].material->name); + } + if (data->glassSys.defs[i].materialShattered) + { + zone->add_asset_of_type(material, data->glassSys.defs[i].materialShattered->name); + } + } + } + } + + std::string IFxWorld::name() + { + return this->name_; + } + + std::int32_t IFxWorld::type() + { + return fx_map; + } + + void IFxWorld::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->glassSys.defs) + { + buf->align(3); + auto* glass_def = buf->write(data->glassSys.defs, data->glassSys.defCount); + + for (std::uint32_t i = 0; i < data->glassSys.defCount; i++) + { + if (data->glassSys.defs[i].physPreset) + { + glass_def[i].physPreset = reinterpret_cast(zone->get_asset_pointer( + physpreset, data->glassSys.defs[i].physPreset->name)); + } + if (data->glassSys.defs[i].material) + { + glass_def[i].material = reinterpret_cast(zone->get_asset_pointer( + material, data->glassSys.defs[i].material->name)); + } + if (data->glassSys.defs[i].materialShattered) + { + glass_def[i].materialShattered = reinterpret_cast(zone->get_asset_pointer( + material, data->glassSys.defs[i].materialShattered->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->glassSys.defs); + } + + buf->push_stream(2); + if (data->glassSys.piecePlaces) + { + buf->align(3); + buf->write(data->glassSys.piecePlaces, data->glassSys.pieceLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.piecePlaces); + } + + if (data->glassSys.pieceStates) + { + buf->align(3); + buf->write(data->glassSys.pieceStates, data->glassSys.pieceLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.pieceStates); + } + + if (data->glassSys.pieceDynamics) + { + buf->align(3); + buf->write(data->glassSys.pieceDynamics, data->glassSys.pieceLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.pieceDynamics); + } + + if (data->glassSys.geoData) + { + buf->align(3); + buf->write(data->glassSys.geoData, data->glassSys.geoDataLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.geoData); + } + + if (data->glassSys.isInUse) + { + buf->align(3); + buf->write(data->glassSys.isInUse, data->glassSys.pieceWordCount); + ZoneBuffer::clear_pointer(&dest->glassSys.isInUse); + } + + if (data->glassSys.cellBits) + { + buf->align(3); + buf->write(data->glassSys.cellBits, data->glassSys.pieceWordCount * data->glassSys.cellCount); + ZoneBuffer::clear_pointer(&dest->glassSys.cellBits); + } + + if (data->glassSys.visData) + { + buf->align(15); + buf->write(data->glassSys.visData, (data->glassSys.pieceLimit + 15) & 0xFFFFFFF0); + ZoneBuffer::clear_pointer(&dest->glassSys.visData); + } + + if (data->glassSys.linkOrg) + { + buf->align(3); + buf->write(data->glassSys.linkOrg, data->glassSys.pieceLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.linkOrg); + } + + if (data->glassSys.halfThickness) + { + buf->align(15); + buf->write(data->glassSys.halfThickness, (data->glassSys.pieceLimit + 3) & 0xFFFFFFFC); + ZoneBuffer::clear_pointer(&dest->glassSys.halfThickness); + } + buf->pop_stream(); + + if (data->glassSys.lightingHandles) + { + buf->align(1); + buf->write(data->glassSys.lightingHandles, data->glassSys.initPieceCount); + ZoneBuffer::clear_pointer(&dest->glassSys.lightingHandles); + } + + if (data->glassSys.initPieceStates) + { + buf->align(3); + buf->write(data->glassSys.initPieceStates, data->glassSys.initPieceCount); + ZoneBuffer::clear_pointer(&dest->glassSys.initPieceStates); + } + + if (data->glassSys.initGeoData) + { + buf->align(3); + buf->write(data->glassSys.initGeoData, data->glassSys.initGeoDataCount); + ZoneBuffer::clear_pointer(&dest->glassSys.initGeoData); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IFxWorld::dump(FxWorld* asset) + { + IW5::IFxWorld::dump((IW5::FxWorld*)asset); + } + } +} diff --git a/src/IW4/Assets/FxWorld.hpp b/src/IW4/Assets/FxWorld.hpp new file mode 100644 index 0000000..344c389 --- /dev/null +++ b/src/IW4/Assets/FxWorld.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IFxWorld : public IAsset + { + private: + std::string name_; + FxWorld* asset_ = nullptr; + + FxWorld* parse(const std::string& name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(FxWorld* asset); + }; + } +} diff --git a/src/IW4/Assets/GameWorldMp.cpp b/src/IW4/Assets/GameWorldMp.cpp new file mode 100644 index 0000000..91046bd --- /dev/null +++ b/src/IW4/Assets/GameWorldMp.cpp @@ -0,0 +1,26 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW5/Assets/GlassWorld.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + std::int32_t IGameWorldMp::type() + { + return game_map_mp; + } + + void IGameWorldMp::dump(GameWorldMp* asset) + { + IGlassWorld::dump(reinterpret_cast(asset)); + } + } +} diff --git a/src/IW4/Assets/GameWorldMp.hpp b/src/IW4/Assets/GameWorldMp.hpp new file mode 100644 index 0000000..89b5346 --- /dev/null +++ b/src/IW4/Assets/GameWorldMp.hpp @@ -0,0 +1,24 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include "../IW5/Assets/GlassWorld.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + class IGameWorldMp : public IW5::IGlassWorld + { + public: + std::int32_t type() override; + static void dump(GameWorldMp* asset); + }; + } +} diff --git a/src/IW4/Assets/GameWorldSp.cpp b/src/IW4/Assets/GameWorldSp.cpp new file mode 100644 index 0000000..a6bb458 --- /dev/null +++ b/src/IW4/Assets/GameWorldSp.cpp @@ -0,0 +1,119 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void IGameWorldSp::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; + auto* mp_asset = IGameWorldMp::parse(name, mem); + + if (!mp_asset) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).game_map_sp; + } + else + { + // generate sp asset based on mp one + this->asset_ = mem->Alloc(); + this->asset_->name = mp_asset->name; + this->asset_->g_glassData = reinterpret_cast(mp_asset->g_glassData); + } + } + + void IGameWorldSp::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IGameWorldSp::load_depending(IZone* zone) + { + } + + std::string IGameWorldSp::name() + { + return this->name_; + } + + void IGameWorldSp::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + assert(sizeof GameWorldSp, 56); + assert(sizeof G_GlassData, 128); + assert(sizeof G_GlassPiece, 12); + assert(sizeof G_GlassName, 12); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->g_glassData) + { + buf->align(3); + + auto* glass_data = data->g_glassData; + auto* dest_glass_data = buf->write(glass_data); + + if (glass_data->glassPieces) + { + buf->align(3); + buf->write(glass_data->glassPieces, glass_data->pieceCount); + ZoneBuffer::clear_pointer(&dest_glass_data->glassPieces); + } + if (glass_data->glassNames) + { + buf->align(3); + const auto namedest = buf->write(glass_data->glassNames, glass_data->glassNameCount); + + for (unsigned int i = 0; i < glass_data->glassNameCount; i++) + { + namedest[i].nameStr = buf->write_str(glass_data->glassNames[i].nameStr); + + if (glass_data->glassNames[i].pieceCount) + { + buf->align(1); + buf->write(glass_data->glassNames[i].pieceIndices, glass_data->glassNames[i].pieceCount); + ZoneBuffer::clear_pointer(&glass_data->glassNames[i].pieceIndices); + } + } + + ZoneBuffer::clear_pointer(&dest_glass_data->glassNames); + } + + ZoneBuffer::clear_pointer(&dest->g_glassData); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + std::int32_t IGameWorldSp::type() + { + return game_map_sp; + } + + void IGameWorldSp::dump(GameWorldSp* asset) + { + auto* mp_asset = new GameWorldMp; + memset(mp_asset, 0, sizeof GameWorldMp); + + mp_asset->name = asset->name; + mp_asset->g_glassData = asset->g_glassData; + + IGameWorldMp::dump(mp_asset); + + delete mp_asset; + } + } +} diff --git a/src/IW4/Assets/GameWorldSp.hpp b/src/IW4/Assets/GameWorldSp.hpp new file mode 100644 index 0000000..8facec4 --- /dev/null +++ b/src/IW4/Assets/GameWorldSp.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include "../IW5/Assets/GlassWorld.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + class IGameWorldSp : public IAsset + { + private: + std::string name_; + GameWorldSp* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(GameWorldSp* asset); + }; + } +} diff --git a/src/IW4/Assets/GfxImage.cpp b/src/IW4/Assets/GfxImage.cpp new file mode 100644 index 0000000..5ab655a --- /dev/null +++ b/src/IW4/Assets/GfxImage.cpp @@ -0,0 +1,423 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/GfxImage.hpp" + + +namespace ZoneTool +{ + namespace IW4 + { + std::string IGfxImage::clean_name(const std::string& name) + { + auto newName = name; + + for (auto i = 0u; i < name.size(); i++) + { + switch (newName[i]) + { + case '*': + newName[i] = '_'; + break; + } + } + + return newName; + } + + GfxImage* IGfxImage::parse(const std::string& name, ZoneMemory* mem) + { + auto path = "images\\" + this->clean_name(name) + ".ffimg"; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + auto fp = FileSystem::FileOpen(path, "rb"); + if (!fp) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing GfxImage \"%s\"...", name.data()); + + auto reader = FileSystem::ToReader(fp); + + auto img = mem->Alloc(); + img->mapType = reader->Read(); + img->semantic = reader->Read(); + img->category = reader->Read(); + img->flags = reader->Read(); + img->cardMemory = reader->Read(); + img->dataLen1 = reader->Read(); + img->dataLen2 = reader->Read(); + img->height = reader->Read(); + img->width = reader->Read(); + img->depth = reader->Read(); + img->loaded = reader->Read(); + img->name = mem->StrDup(reader->ReadString()); + + auto loaddef = mem->Alloc(); + loaddef->mipLevels = reader->Read(); + loaddef->flags = reader->Read(); + loaddef->dimensions[0] = reader->Read(); + loaddef->dimensions[1] = reader->Read(); + loaddef->dimensions[2] = reader->Read(); + loaddef->format = reader->Read(); + loaddef->dataSize = reader->Read(); + + GfxImageLoadDef* finalLoaddef = nullptr; + + if (loaddef->dataSize > 4) + { + finalLoaddef = mem->ManualAlloc( + sizeof GfxImageLoadDef + + (loaddef->dataSize - 4)); + memcpy(finalLoaddef, loaddef, sizeof GfxImageLoadDef); + reader->ReadManual(&finalLoaddef->texture, loaddef->dataSize, 1); + } + else + { + finalLoaddef = loaddef; + } + + + img->texture = finalLoaddef; + + FileSystem::FileClose(fp); + + return img; + } + + void IGfxImage::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + this->isMapImage = (this->name_.size() >= 6) + ? ((this->name_.substr(0, 6) == "*light" || this->name_.substr(0, 6) == "*refle" || + this->name_ == "$outdoor") + ? true + : false) + : false; + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data()).gfximage; + } + } + + void IGfxImage::init(void* asset, ZoneMemory* mem) + { + this->asset_ = reinterpret_cast(asset); + this->name_ = this->asset_->name; + this->isMapImage = (this->name_.size() >= 6) + ? ((this->name_.substr(0, 6) == "*light" || this->name_.substr(0, 6) == "*refle" || + this->name_ == "$outdoor") + ? true + : false) + : false; + + auto parsed = this->parse(this->name_, mem); + if (parsed) + { + this->asset_ = parsed; + } + } + + void IGfxImage::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IGfxImage::load_depending(IZone* zone) + { + } + + std::string IGfxImage::name() + { + return this->name_; + } + + std::int32_t IGfxImage::type() + { + return image; + } + + void IGfxImage::write(IZone* zone, ZoneBuffer* buf) + { + if (zone->get_target() == zone_target::pc) + { + IW5::IGfxImage::dump_iwi(this->name()); + + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + // set loaded to false + dest->loaded = false; + + buf->push_stream(0); + if (data->texture) + { + buf->align(3); + + auto desttext = buf->at(); + buf->write_stream(data->texture, sizeof GfxImageLoadDef - sizeof std::uintptr_t); + + if (isMapImage && desttext->dataSize) + { + buf->write_stream(&data->texture->texture, data->texture->dataSize); + } + else + { + desttext->dataSize = 0; + } + + ZoneBuffer::clear_pointer(&dest->texture); + } + buf->pop_stream(); + + END_LOG_STREAM; + buf->pop_stream(); + } + else + { + alpha::GfxImage alpha_image = {}; + + // transform iwi + if (!FileSystem::FileExists(va("images/%s.iwi", this->name().data())) && !this->isMapImage) + { + ZONETOOL_FATAL("Image %s is missing!", this->name().data()); + } + + std::vector pixels; + auto fp = FileSystem::FileOpen(va("images/%s.iwi", this->name().data()), "rb"); + if (fp || this->isMapImage) + { + if (!this->isMapImage) + { + auto file_size = FileSystem::FileSize(fp); + auto img_data = FileSystem::ReadBytes(fp, file_size); + + auto iwi_header = (GfxImageFileHeader*)img_data.data(); + + sizeof GfxImageFileHeader; + + auto pixel_data = img_data.data() + 32; + auto pixel_data_size = img_data.size() - 32; + + pixels.resize(pixel_data_size); + memcpy(&pixels[0], pixel_data, pixel_data_size); + + // add image to imagepak + + + // zone images + alpha_image.cached = false; + alpha_image.cardMemory.platform[0] = pixels.size(); + + // pakfile images + //alpha_image.cached = true; + //alpha_image.cardMemory.platform[0] = 0; + //alpha_image.streams[0].width = iwi_header->dimensions[0]; + //alpha_image.streams[0].height = iwi_header->dimensions[1]; + //alpha_image.streams[0].pixelSize = 1; + //buf->add_image(pixels); + + alpha_image.format = 0x1A200154; + alpha_image.width = iwi_header->dimensions[0]; + alpha_image.height = iwi_header->dimensions[1]; + alpha_image.depth = iwi_header->dimensions[2]; + alpha_image.levelCount = 1; + alpha_image.mapType = 3; + alpha_image.category = 3; + } + else + { + pixels.resize(this->asset_->texture->dataSize); + memcpy(&pixels[0], &this->asset_->texture->texture, pixels.size()); + + alpha_image.cached = false; + alpha_image.cardMemory.platform[0] = pixels.size(); + if (this->name().starts_with("*refle")) + { + alpha_image.format = 0x18280186; + alpha_image.mapType = 5; + alpha_image.semantic = 1; + alpha_image.category = 1; + } + else if (this->name().starts_with("*light")) + { + alpha_image.format = 0x2800017A; + alpha_image.mapType = 3; + alpha_image.semantic = 1; + alpha_image.category = 2; + } + else if (this->name() == "$outdoor") + { + alpha_image.format = 0x28000102; + alpha_image.mapType = 3; + alpha_image.semantic = 1; + alpha_image.category = 1; + } + else + { + ZONETOOL_FATAL("you goofed"); + } + alpha_image.width = this->asset_->width; + alpha_image.height = this->asset_->height; + alpha_image.depth = this->asset_->depth; + alpha_image.levelCount = 1; + } + + } + else + { + ZONETOOL_FATAL("Cannot open image %s!", this->name().data()); + } + + + auto data = &alpha_image; + auto dest = buf->write(data); + + buf->push_stream(3); + + dest->name = buf->write_str(this->name()); + + buf->push_stream(1); + if (data->cardMemory.platform[0]) + { + buf->align(4095); + buf->write(pixels.data(), pixels.size()); + ZoneBuffer::clear_pointer(&dest->pixels); + } + else + { + dest->pixels = nullptr; + } + buf->pop_stream(); + + buf->pop_stream(); + + endian_convert(&dest->name); + endian_convert(&dest->format); + endian_convert(&dest->width); + endian_convert(&dest->height); + endian_convert(&dest->depth); + endian_convert(&dest->pixels); + endian_convert(&dest->cardMemory.platform[0]); + for (auto i = 0; i < 4; i++) + { + endian_convert(&dest->streams[i].width); + endian_convert(&dest->streams[i].height); + endian_convert(&dest->streams[i].pixelSize); + } + } + } + + // Legacy cancer code + void fwritestr(FILE* file, const char* str) + { + if (!str) + return; + while (*str) + { + fwrite(str, 1, 1, file); + str++; + } + fwrite(str, 1, 1, file); + } + + void fwriteint(FILE* file, int value) + { + int _val = value; + fwrite(&_val, 4, 1, file); + } + + void fwriteuint(FILE* file, unsigned int value) + { + unsigned int _val = value; + fwrite(&_val, 4, 1, file); + } + + void fwritechar(FILE* file, char c) + { + char _val = c; + fwrite(&_val, 1, 1, file); + } + + char cleanAssetName[50]; + + char* ClearAssetName(char* name, int maxSize = 50) + { + int size = strnlen(name, maxSize); + char* newName = cleanAssetName; + memset(newName, 0, size + 1); + strncpy(newName, name, maxSize); + for (int i = 0; i < size; i++) + { + switch (newName[i]) + { + case '*': + newName[i] = '_'; + break; + } + } + return newName; + } + + void IGfxImage::dump(GfxImage* asset) + { + if (asset->texture && asset->texture->dataSize) + { + char* newName = ClearAssetName((char*)asset->name); + auto fp = FileSystem::FileOpen("images/"s + newName + ".ffImg"s, "wb"); + + if (!fp) return; + +#define fwstr(_str) fwritestr(fp, _str) +#define fwint(_int) fwriteint(fp, _int) +#define fwchr(_chr) fwritechar(fp, _chr) +#define frstr() freadstr(fp) +#define frint() freadint(fp) +#define frchr() freadchar(fp) + + // Header + fwchr(asset->mapType); + fwchr(asset->semantic); + fwchr(asset->category); + fwchr(asset->flags); + fwint((int)asset->cardMemory); + fwint(asset->dataLen1); + fwint(asset->dataLen2); + fwint(asset->height); + fwint(asset->width); + fwint(asset->depth); + fwstr(asset->name); + + // LoadDef + fwchr(asset->texture->mipLevels); + fwchr(asset->texture->flags); + fwint(asset->texture->dimensions[0]); + fwint(asset->texture->dimensions[1]); + fwint(asset->texture->dimensions[2]); + fwint(asset->texture->format); + fwint(asset->texture->dataSize); + + fwrite(&asset->texture->texture, 1, asset->texture->dataSize, fp); + + FileSystem::FileClose(fp); + } + } + } +} diff --git a/src/IW4/Assets/GfxImage.hpp b/src/IW4/Assets/GfxImage.hpp new file mode 100644 index 0000000..7618d75 --- /dev/null +++ b/src/IW4/Assets/GfxImage.hpp @@ -0,0 +1,40 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IGfxImage : public IAsset + { + private: + std::string name_; + GfxImage* asset_ = nullptr; + bool isMapImage; + + std::string clean_name(const std::string& name); + GfxImage* parse(const std::string& name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void init(void* asset, ZoneMemory* mem) override; + + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + void* pointer() override { return asset_; } + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(GfxImage* asset); + }; + } +} diff --git a/src/IW4/Assets/GfxWorld.cpp b/src/IW4/Assets/GfxWorld.cpp new file mode 100644 index 0000000..891e486 --- /dev/null +++ b/src/IW4/Assets/GfxWorld.cpp @@ -0,0 +1,850 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW5/Assets/GfxWorld.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + /*legacy zonetool code, refactor me!*/ + GfxWorld* IGfxWorld::parse(const std::string& name, ZoneMemory* mem) + { + auto iw5_gfxmap = IW5::IGfxWorld::parse(name, mem); + + if (!iw5_gfxmap) + { + return nullptr; + } + + // convert to IW4 format + auto gfxmap = mem->Alloc(); + + // copy struct data + memcpy(&gfxmap->name, &iw5_gfxmap->name, Difference(&gfxmap->cells, &gfxmap->name)); + + // allocate cells + gfxmap->cells = new GfxCell[gfxmap->dpvsPlanes.cellCount]; + memset(gfxmap->cells, 0, sizeof(GfxCell) * gfxmap->dpvsPlanes.cellCount); + + // copy cell data + for (int i = 0; i < gfxmap->dpvsPlanes.cellCount; i++) + { + memcpy(&gfxmap->cells[i], &iw5_gfxmap->cells[i], sizeof GfxCell); + } + + // copy draw data + memcpy(gfxmap->draw._portpad0, iw5_gfxmap->worldDraw._portpad0, 16); + memcpy(gfxmap->draw._portpad1, iw5_gfxmap->worldDraw._portpad1, 56); + + // copy remaining GfxWorld data + memcpy(&gfxmap->lightGrid, &iw5_gfxmap->lightGrid, + Difference(&gfxmap->fogTypesAllowed + 1, &gfxmap->lightGrid)); + + // return converted gfxmap + return gfxmap; + } + + IGfxWorld::IGfxWorld() + { + } + + IGfxWorld::~IGfxWorld() + { + } + + void IGfxWorld::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data()).gfxworld; + } + } + + void IGfxWorld::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IGfxWorld::load_depending(IZone* zone) + { + auto* data = this->asset_; + + // Skies + if (data->skyCount) + { + for (unsigned int i = 0; i < data->skyCount; i++) + { + if (data->skies[i].skyImage) + { + zone->add_asset_of_type(image, data->skies[i].skyImage->name); + } + } + } + + // ReflectionImages + if (data->draw.reflectionImages) + { + for (unsigned int i = 0; i < data->draw.reflectionProbeCount; i++) + { + if (data->draw.reflectionImages[i]) + { + zone->add_asset_of_type(image, data->draw.reflectionImages[i]->name); + } + } + } + + // Lightmaps + if (data->draw.lightmaps) + { + for (int i = 0; i < data->draw.lightmapCount; i++) + { + if (data->draw.lightmaps[i].primary) + { + zone->add_asset_of_type(image, data->draw.lightmaps[i].primary->name); + } + + if (data->draw.lightmaps[i].secondary) + { + zone->add_asset_of_type(image, data->draw.lightmaps[i].secondary->name); + } + } + } + + // SkyImage (Unused?) + if (data->draw.skyImage) + { + zone->add_asset_of_type(image, data->draw.skyImage->name); + } + + // OutdoorImage (Unused?) + if (data->draw.outdoorImage) + { + zone->add_asset_of_type(image, data->draw.outdoorImage->name); + } + + if (zone->get_target() != zone_target::pc) + { + return; + } + + // MaterialMemory + if (data->materialMemory) + { + for (int i = 0; i < data->materialMemoryCount; i++) + { + if (data->materialMemory[i].material) + { + zone->add_asset_of_type(material, data->materialMemory[i].material->name); + } + } + } + + // Sunflare_t + if (data->sun.spriteMaterial) + { + zone->add_asset_of_type(material, data->sun.spriteMaterial->name); + } + + if (data->sun.flareMaterial) + { + zone->add_asset_of_type(material, data->sun.flareMaterial->name); + } + + // OutdoorImage + if (data->outdoorImage) + { + zone->add_asset_of_type(image, data->outdoorImage->name); + } + + // Dpvs.Surfaces + if (data->dpvs.surfaces) + { + for (int i = 0; i < data->surfaceCount; i++) + { + if (data->dpvs.surfaces[i].material) + { + zone->add_asset_of_type(material, data->dpvs.surfaces[i].material->name); + } + } + } + + if (data->dpvs.smodelDrawInsts) + { + for (unsigned int i = 0; i < data->dpvs.smodelCount; i++) + { + if (data->dpvs.smodelDrawInsts[i].model) + { + zone->add_asset_of_type(xmodel, data->dpvs.smodelDrawInsts[i].model->name); + } + } + } + } + + std::string IGfxWorld::name() + { + return this->name_; + } + + std::int32_t IGfxWorld::type() + { + return gfx_map; + } + + void IGfxWorld::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + if (data->name) + { + dest->name = buf->write_str(this->name()); + } + if (data->baseName) + { + dest->baseName = buf->write_str(this->name()); + } + + if (data->skies) + { + buf->align(3); + auto skiesArray = buf->write(data->skies, data->skyCount); + + for (std::uint32_t i = 0; i < data->skyCount; i++) + { + if (data->skies[i].skyStartSurfs) + { + buf->align(3); + buf->write_p(data->skies[i].skyStartSurfs, data->skies[i].skySurfCount); + ZoneBuffer::clear_pointer(&skiesArray[i].skyStartSurfs); + } + + if (data->skies[i].skyImage) + { + skiesArray[i].skyImage = reinterpret_cast(zone->get_asset_pointer( + image, data->skies[i].skyImage->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->skies); + } + + if (dest->dpvsPlanes.planes) + { + dest->dpvsPlanes.planes = buf->write_s(3, data->dpvsPlanes.planes, data->planeCount); + } + + if (dest->dpvsPlanes.nodes) + { + buf->align(1); + buf->write_p(data->dpvsPlanes.nodes, data->nodeCount); + ZoneBuffer::clear_pointer(&dest->dpvsPlanes.nodes); + } + + buf->push_stream(2); + if (dest->dpvsPlanes.sceneEntCellBits) + { + buf->align(3); + buf->write(data->dpvsPlanes.sceneEntCellBits, data->dpvsPlanes.cellCount << 11); + ZoneBuffer::clear_pointer(&dest->dpvsPlanes.sceneEntCellBits); + } + buf->pop_stream(); + + if (data->aabbTreeCounts) + { + buf->align(3); + buf->write_p(data->aabbTreeCounts, data->dpvsPlanes.cellCount); + ZoneBuffer::clear_pointer(&dest->aabbTreeCounts); + } + + if (data->aabbTree) + { + buf->align(127); + auto cell_tree = buf->write_p(data->aabbTree, data->dpvsPlanes.cellCount); + + for (std::int32_t i = 0; i < data->dpvsPlanes.cellCount; i++) + { + if (data->aabbTree[i].aabbtree) + { + buf->align(3); + auto gfx_aabb_tree = buf->write_p(data->aabbTree[i].aabbtree, + data->aabbTreeCounts[i].aabbTreeCount); + + for (std::int32_t i2 = 0; i2 < data->aabbTreeCounts[i].aabbTreeCount; i2++) + { + if (data->aabbTree[i].aabbtree[i2].smodelIndexes) + { + gfx_aabb_tree[i2].smodelIndexes = buf->write_s( + 1, data->aabbTree[i].aabbtree[i2].smodelIndexes, + data->aabbTree[i].aabbtree[i2].smodelIndexCount); + } + } + + ZoneBuffer::clear_pointer(&cell_tree[i].aabbtree); + } + } + + ZoneBuffer::clear_pointer(&dest->aabbTree); + } + + if (data->cells) + { + buf->align(3); + auto gfx_cell = buf->write(data->cells, data->dpvsPlanes.cellCount); + + for (std::int32_t i = 0; i < data->dpvsPlanes.cellCount; i++) + { + if (data->cells[i].portals) + { + buf->align(3); + auto gfx_portal = buf->write(data->cells[i].portals, data->cells[i].portalCount); + + for (std::int32_t i2 = 0; i2 < data->cells[i].portalCount; i2++) + { + if (data->cells[i].portals[i2].vertices) + { + buf->align(3); + buf->write(data->cells[i].portals[i2].vertices, data->cells[i].portals[i2].vertexCount); + ZoneBuffer::clear_pointer(&gfx_portal[i2].vertices); + } + } + + ZoneBuffer::clear_pointer(&gfx_cell[i].portals); + } + + if (data->cells[i].reflectionProbes) + { + buf->align(0); + buf->write(data->cells[i].reflectionProbes, data->cells[i].reflectionProbeCount); + ZoneBuffer::clear_pointer(&gfx_cell[i].reflectionProbes); + } + + ZoneBuffer::clear_pointer(&dest->cells); + } + } + + if (data->draw.reflectionImages) + { + buf->align(3); + auto reflectionProbes = buf->write(data->draw.reflectionImages, + data->draw.reflectionProbeCount); + + for (std::uint64_t i = 0; i < data->draw.reflectionProbeCount; i++) + { + if (reflectionProbes[i]) + { + reflectionProbes[i] = reinterpret_cast(zone->get_asset_pointer( + image, data->draw.reflectionImages[i]->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->draw.reflectionImages); + } + + if (data->draw.reflectionProbes) + { + buf->align(3); + buf->write(data->draw.reflectionProbes, data->draw.reflectionProbeCount); + ZoneBuffer::clear_pointer(&dest->draw.reflectionProbes); + } + + buf->push_stream(2); + if (data->draw.reflectionProbeTextures) + { + buf->align(3); + buf->write(data->draw.reflectionProbeTextures, data->draw.reflectionProbeCount); + ZoneBuffer::clear_pointer(&dest->draw.reflectionProbeTextures); + } + buf->pop_stream(); + + if (data->draw.lightmaps) + { + buf->align(3); + auto gfx_lightmap_array = buf->write(data->draw.lightmaps, data->draw.lightmapCount); + + for (std::int32_t i = 0; i < data->draw.lightmapCount; i++) + { + if (data->draw.lightmaps[i].primary) + { + gfx_lightmap_array[i].primary = reinterpret_cast(zone->get_asset_pointer( + image, data->draw.lightmaps[i].primary->name)); + } + + if (data->draw.lightmaps[i].secondary) + { + gfx_lightmap_array[i].secondary = reinterpret_cast(zone->get_asset_pointer( + image, data->draw.lightmaps[i].secondary->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->draw.lightmaps); + } + + buf->push_stream(2); + if (data->draw.lightmapPrimaryTextures) + { + buf->align(3); + buf->write_p(data->draw.lightmapPrimaryTextures, data->draw.lightmapCount); + ZoneBuffer::clear_pointer(&dest->draw.lightmapPrimaryTextures); + } + + if (data->draw.lightmapSecondaryTextures) + { + buf->align(3); + buf->write_p(data->draw.lightmapSecondaryTextures, data->draw.lightmapCount); + ZoneBuffer::clear_pointer(&dest->draw.lightmapSecondaryTextures); + } + buf->pop_stream(); + + if (data->draw.skyImage) + { + dest->draw.skyImage = reinterpret_cast(zone->get_asset_pointer( + image, data->draw.skyImage->name)); + } + + if (data->draw.outdoorImage) + { + dest->draw.outdoorImage = reinterpret_cast(zone->get_asset_pointer( + image, data->draw.outdoorImage->name)); + } + + if (data->draw.vd.vertices) + { + buf->align(3); + buf->write_p(data->draw.vd.vertices, data->draw.vertexCount); + ZoneBuffer::clear_pointer(&dest->draw.vd.vertices); + } + + if (data->draw.vld.data) + { + buf->align(0); + buf->write_p(data->draw.vld.data, data->draw.vertexLayerDataSize); + ZoneBuffer::clear_pointer(&dest->draw.vld.data); + } + + if (data->draw.indices) + { + buf->align(1); + buf->write_p(data->draw.indices, data->draw.indexCount); + ZoneBuffer::clear_pointer(&dest->draw.indices); + } + + if (data->lightGrid.rowDataStart) + { + buf->align(1); + buf->write_p(data->lightGrid.rowDataStart, + data->lightGrid.maxs[data->lightGrid.rowAxis] - data->lightGrid.mins[data + ->lightGrid.rowAxis] + + 1); + ZoneBuffer::clear_pointer(&dest->lightGrid.rowDataStart); + } + + if (data->lightGrid.rawRowData) + { + buf->align(0); + buf->write_p(data->lightGrid.rawRowData, data->lightGrid.rawRowDataSize); + ZoneBuffer::clear_pointer(&dest->lightGrid.rawRowData); + } + + if (data->lightGrid.entries) + { + buf->align(3); + buf->write(data->lightGrid.entries, data->lightGrid.entryCount); + ZoneBuffer::clear_pointer(&dest->lightGrid.entries); + } + + if (data->lightGrid.colors) + { + buf->align(3); + buf->write(data->lightGrid.colors, data->lightGrid.colorCount); + ZoneBuffer::clear_pointer(&dest->lightGrid.colors); + } + + if (data->models) + { + buf->align(3); + buf->write(data->models, data->modelCount); + ZoneBuffer::clear_pointer(&dest->models); + } + + if (data->materialMemory) + { + buf->align(3); + auto memory = buf->write(data->materialMemory, data->materialMemoryCount); + + for (std::int32_t i = 0; i < data->materialMemoryCount; i++) + { + memory[i].material = reinterpret_cast(zone->get_asset_pointer( + material, data->materialMemory[i].material->name)); + } + + ZoneBuffer::clear_pointer(&dest->materialMemory); + } + + if (data->sun.spriteMaterial) + { + dest->sun.spriteMaterial = reinterpret_cast(zone->get_asset_pointer( + material, data->sun.spriteMaterial->name)); + } + if (data->sun.flareMaterial) + { + dest->sun.flareMaterial = reinterpret_cast(zone->get_asset_pointer( + material, data->sun.flareMaterial->name)); + } + + if (data->outdoorImage) + { + dest->outdoorImage = reinterpret_cast(zone->get_asset_pointer(image, data->outdoorImage->name) + ); + } + + buf->push_stream(2); + if (data->cellCasterBits[0]) + { + buf->align(3); + buf->write(data->cellCasterBits[0], + data->dpvsPlanes.cellCount * ((data->dpvsPlanes.cellCount + 31) >> 5)); + ZoneBuffer::clear_pointer(&dest->cellCasterBits[0]); + } + + if (data->cellCasterBits[1]) + { + buf->align(3); + buf->write(data->cellCasterBits[1], (data->dpvsPlanes.cellCount + 31) >> 5); + ZoneBuffer::clear_pointer(&dest->cellCasterBits[1]); + } + + if (data->sceneDynModel) + { + buf->align(3); + buf->write(data->sceneDynModel, data->dpvsDyn.dynEntClientCount[0]); + ZoneBuffer::clear_pointer(&dest->sceneDynModel); + } + + if (data->sceneDynBrush) + { + buf->align(3); + buf->write(data->sceneDynBrush, data->dpvsDyn.dynEntClientCount[1]); + ZoneBuffer::clear_pointer(&dest->sceneDynBrush); + } + + if (data->primaryLightEntityShadowVis) + { + buf->align(3); + buf->write(data->primaryLightEntityShadowVis, + (data->primaryLightCount - data->sunPrimaryLightIndex - 1) << 15); + ZoneBuffer::clear_pointer(&dest->primaryLightEntityShadowVis); + } + + if (data->primaryLightDynEntShadowVis[0]) + { + buf->align(3); + buf->write(data->primaryLightDynEntShadowVis[0], + data->dpvsDyn.dynEntClientCount[0] * (data->primaryLightCount - data->sunPrimaryLightIndex - + 1)); + ZoneBuffer::clear_pointer(&dest->primaryLightDynEntShadowVis[0]); + } + + if (data->primaryLightDynEntShadowVis[1]) + { + buf->align(3); + buf->write(data->primaryLightDynEntShadowVis[1], + data->dpvsDyn.dynEntClientCount[1] * (data->primaryLightCount - data->sunPrimaryLightIndex - + 1)); + ZoneBuffer::clear_pointer(&dest->primaryLightDynEntShadowVis[1]); + } + + if (data->primaryLightForModelDynEnt) + { + buf->align(0); + buf->write(data->primaryLightForModelDynEnt, data->dpvsDyn.dynEntClientCount[0]); + ZoneBuffer::clear_pointer(&dest->primaryLightForModelDynEnt); + } + buf->pop_stream(); + + if (data->shadowGeom) + { + buf->align(3); + auto shadow_geometry = buf->write(data->shadowGeom, data->primaryLightCount); + + for (std::int32_t i = 0; i < data->primaryLightCount; i++) + { + if (data->shadowGeom[i].sortedSurfIndex) + { + buf->align(1); + buf->write_p(data->shadowGeom[i].sortedSurfIndex, data->shadowGeom[i].surfaceCount); + ZoneBuffer::clear_pointer(&shadow_geometry[i].sortedSurfIndex); + } + if (data->shadowGeom[i].smodelIndex) + { + buf->align(1); + buf->write_p(data->shadowGeom[i].smodelIndex, data->shadowGeom[i].smodelCount); + ZoneBuffer::clear_pointer(&shadow_geometry[i].smodelIndex); + } + } + + ZoneBuffer::clear_pointer(&dest->shadowGeom); + } + + if (data->lightRegion) + { + buf->align(3); + auto light_region = buf->write(data->lightRegion, data->primaryLightCount); + + for (std::int32_t i = 0; i < data->primaryLightCount; i++) + { + if (data->lightRegion[i].hulls) + { + buf->align(3); + auto light_region_hull = buf->write(data->lightRegion[i].hulls, data->lightRegion[i].hullCount); + + for (std::uint32_t i2 = 0; i2 < data->lightRegion[i].hullCount; i2++) + { + if (data->lightRegion[i].hulls[i2].axis) + { + buf->align(3); + buf->write(data->lightRegion[i].hulls[i2].axis, + data->lightRegion[i].hulls[i2].axisCount); + ZoneBuffer::clear_pointer(&light_region_hull[i2].axis); + } + } + + ZoneBuffer::clear_pointer(&light_region[i].hulls); + } + } + + ZoneBuffer::clear_pointer(&dest->lightRegion); + } + + buf->push_stream(2); + if (data->dpvs.smodelVisData[0]) + { + buf->align(0); + buf->write(data->dpvs.smodelVisData[0], data->dpvs.smodelCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelVisData[0]); + } + + if (data->dpvs.smodelVisData[1]) + { + buf->align(0); + buf->write(data->dpvs.smodelVisData[1], data->dpvs.smodelCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelVisData[1]); + } + + if (data->dpvs.smodelVisData[2]) + { + buf->align(0); + buf->write(data->dpvs.smodelVisData[2], data->dpvs.smodelCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelVisData[2]); + } + + if (data->dpvs.surfaceVisData[0]) + { + buf->align(0); + buf->write(data->dpvs.surfaceVisData[0], data->dpvs.staticSurfaceCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfaceVisData[0]); + } + + if (data->dpvs.surfaceVisData[1]) + { + buf->align(0); + buf->write(data->dpvs.surfaceVisData[1], data->dpvs.staticSurfaceCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfaceVisData[1]); + } + + if (data->dpvs.surfaceVisData[2]) + { + buf->align(0); + buf->write(data->dpvs.surfaceVisData[2], data->dpvs.staticSurfaceCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfaceVisData[2]); + } + buf->pop_stream(); + + if (data->dpvs.sortedSurfIndex) + { + buf->align(1); + buf->write_p(data->dpvs.sortedSurfIndex, + data->dpvs.staticSurfaceCount + data->dpvs.staticSurfaceCountNoDecal); + ZoneBuffer::clear_pointer(&dest->dpvs.sortedSurfIndex); + } + + if (data->dpvs.smodelInsts) + { + buf->align(3); + buf->write(data->dpvs.smodelInsts, data->dpvs.smodelCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelInsts); + } + + if (data->dpvs.surfaces) + { + buf->align(3); + auto surface = buf->write(data->dpvs.surfaces, data->surfaceCount); + + for (std::int32_t i = 0; i < data->surfaceCount; i++) + { + if (data->dpvs.surfaces[i].material) + { + surface[i].material = reinterpret_cast(zone->get_asset_pointer( + material, data->dpvs.surfaces[i].material->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->dpvs.surfaces); + } + + if (data->dpvs.surfacesBounds) + { + buf->align(3); + buf->write(data->dpvs.surfacesBounds, data->surfaceCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfacesBounds); + } + + if (data->dpvs.smodelDrawInsts) + { + buf->align(3); + auto static_model_draw_inst = buf->write(data->dpvs.smodelDrawInsts, data->dpvs.smodelCount); + + for (std::uint32_t i = 0; i < data->dpvs.smodelCount; i++) + { + if (data->dpvs.smodelDrawInsts[i].model) + { + static_model_draw_inst[i].model = reinterpret_cast(zone->get_asset_pointer( + xmodel, data->dpvs.smodelDrawInsts[i].model->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->dpvs.smodelDrawInsts); + } + + buf->push_stream(2); + if (data->dpvs.surfaceMaterials) + { + buf->align(7); + buf->write(data->dpvs.surfaceMaterials, data->surfaceCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelDrawInsts); + } + + if (data->dpvs.surfaceCastsSunShadow) + { + buf->align(127); + buf->write(data->dpvs.surfaceCastsSunShadow, data->dpvs.surfaceVisDataCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfaceCastsSunShadow); + } + + if (data->dpvsDyn.dynEntCellBits[0]) + { + buf->align(3); + buf->write(data->dpvsDyn.dynEntCellBits[0], + data->dpvsDyn.dynEntClientWordCount[0] * data->dpvsPlanes.cellCount); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntCellBits[0]); + } + + if (data->dpvsDyn.dynEntCellBits[1]) + { + buf->align(3); + buf->write(data->dpvsDyn.dynEntCellBits[1], + data->dpvsDyn.dynEntClientWordCount[1] * data->dpvsPlanes.cellCount); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntCellBits[1]); + } + + if (data->dpvsDyn.dynEntVisData[0][0]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[0][0], 32 * data->dpvsDyn.dynEntClientWordCount[0]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[0][0]); + } + + if (data->dpvsDyn.dynEntVisData[1][0]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[1][0], 32 * data->dpvsDyn.dynEntClientWordCount[1]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[1][0]); + } + + if (data->dpvsDyn.dynEntVisData[0][1]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[0][1], 32 * data->dpvsDyn.dynEntClientWordCount[0]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[0][1]); + } + + if (data->dpvsDyn.dynEntVisData[1][1]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[1][1], 32 * data->dpvsDyn.dynEntClientWordCount[1]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[1][1]); + } + + if (data->dpvsDyn.dynEntVisData[0][2]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[0][2], 32 * data->dpvsDyn.dynEntClientWordCount[0]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[0][2]); + } + + if (data->dpvsDyn.dynEntVisData[1][2]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[1][2], 32 * data->dpvsDyn.dynEntClientWordCount[1]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[1][2]); + } + buf->pop_stream(); + + if (data->heroLights) + { + buf->align(3); + buf->write(data->heroLights, data->heroLightCount); + ZoneBuffer::clear_pointer(&dest->heroLights); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IGfxWorld::dump(GfxWorld* asset) + { + // convert asset to IW5 format + auto* iw5_asset = new IW5::GfxWorld; + memset(iw5_asset, 0, sizeof IW5::GfxWorld); + + // copy struct data + memcpy(&iw5_asset->name, &asset->name, Difference(&iw5_asset->cells, &iw5_asset->name)); + + // allocate cells + iw5_asset->cells = new IW5::GfxCell[iw5_asset->dpvsPlanes.cellCount]; + memset(iw5_asset->cells, 0, sizeof(IW5::GfxCell) * iw5_asset->dpvsPlanes.cellCount); + + // copy cell data + for (int i = 0; i < iw5_asset->dpvsPlanes.cellCount; i++) + { + memcpy(&iw5_asset->cells[i], &asset->cells[i], sizeof GfxCell); + } + + // copy draw data + memcpy(iw5_asset->worldDraw._portpad0, asset->draw._portpad0, 16); + memcpy(iw5_asset->worldDraw._portpad1, asset->draw._portpad1, 56); + + // copy remaining GfxWorld data + memcpy(&iw5_asset->lightGrid, &asset->lightGrid, + Difference(&iw5_asset->fogTypesAllowed + 1, &iw5_asset->lightGrid)); + + // dump asset + IW5::IGfxWorld::dump(iw5_asset); + + // free memory_ + delete[] iw5_asset->cells; + delete iw5_asset; + } + } +} diff --git a/src/IW4/Assets/GfxWorld.hpp b/src/IW4/Assets/GfxWorld.hpp new file mode 100644 index 0000000..283695d --- /dev/null +++ b/src/IW4/Assets/GfxWorld.hpp @@ -0,0 +1,37 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IGfxWorld : public IAsset + { + private: + std::string name_; + GfxWorld* asset_ = nullptr; + + public: + GfxWorld* parse(const std::string& name, ZoneMemory* mem); + IGfxWorld(); + ~IGfxWorld(); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(GfxWorld* asset); + }; + } +} diff --git a/src/IW4/Assets/LightDef.cpp b/src/IW4/Assets/LightDef.cpp new file mode 100644 index 0000000..e9b8f9f --- /dev/null +++ b/src/IW4/Assets/LightDef.cpp @@ -0,0 +1,125 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW5/Assets/LightDef.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + ILightDef::ILightDef() + { + } + + ILightDef::~ILightDef() + { + } + + GfxLightDef* ILightDef::parse(const std::string& name, ZoneMemory* mem) + { + // parse iw5 lightdef + auto iw5_asset = IW5::ILightDef::parse(name, mem); + + if (!iw5_asset) + { + return nullptr; + } + + // fixup iw4 structure + auto asset = mem->Alloc(); + memcpy(asset, iw5_asset, sizeof GfxLightDef); + asset->lmapLookupStart, iw5_asset->lmapLookupStart; + + // return converted asset + return asset; + } + + void ILightDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).lightdef; + } + } + + void ILightDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ILightDef::load_depending(IZone* zone) + { + auto asset = this->asset_; + + if (asset->attenuation.image) + { + zone->add_asset_of_type(image, asset->attenuation.image->name); + } + } + + std::string ILightDef::name() + { + return this->name_; + } + + std::int32_t ILightDef::type() + { + return lightdef; + } + + void ILightDef::write(IZone* zone, ZoneBuffer* buf) + { + assert(sizeof(GfxLightDef), 16); + + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->attenuation.image) + { + dest->attenuation.image = reinterpret_cast(zone->get_asset_pointer( + image, data->attenuation.image->name)); + } + + if (zone->get_target() != zone_target::pc) + { + endian_convert(&dest->name); + endian_convert(&dest->attenuation.image); + endian_convert(&dest->attenuation.samplerState); + endian_convert(&dest->lmapLookupStart); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ILightDef::dump(GfxLightDef* asset) + { + // allocate memory_ for the asset + auto iw5_asset = new IW5::GfxLightDef; + + // copy data + memcpy(iw5_asset, asset, sizeof GfxLightDef); + memset(&iw5_asset->cucoloris, 0, sizeof GfxLightImage); + iw5_asset->lmapLookupStart = asset->lmapLookupStart; + + // dump data + IW5::ILightDef::dump(iw5_asset); + + // free memory_ + delete iw5_asset; + } + } +} diff --git a/src/IW4/Assets/LightDef.hpp b/src/IW4/Assets/LightDef.hpp new file mode 100644 index 0000000..fabe558 --- /dev/null +++ b/src/IW4/Assets/LightDef.hpp @@ -0,0 +1,38 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class ILightDef : public IAsset + { + private: + std::string name_; + GfxLightDef* asset_ = nullptr; + + public: + ILightDef(); + ~ILightDef(); + + GfxLightDef* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(GfxLightDef* asset); + }; + } +} diff --git a/src/IW4/Assets/LoadedSound.cpp b/src/IW4/Assets/LoadedSound.cpp new file mode 100644 index 0000000..0997ae3 --- /dev/null +++ b/src/IW4/Assets/LoadedSound.cpp @@ -0,0 +1,260 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + ILoadedSound::ILoadedSound() + { + } + + ILoadedSound::~ILoadedSound() + { + } + + LoadedSound* ILoadedSound::parse(const std::string& name, ZoneMemory* mem) + { + auto path = "loaded_sound/" + name; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing loaded_sound \"%s\"...", name.data()); + + auto fp = FileSystem::FileOpen(path, "rb"); + auto result = mem->Alloc(); + + unsigned int chunkIDBuffer; + unsigned int chunkSize; + + fread(&chunkIDBuffer, 4, 1, fp); + if (chunkIDBuffer != 0x46464952) // RIFF + { + ZONETOOL_ERROR("%s: Invalid RIFF Header.", name.c_str()); + fclose(fp); + return nullptr; + } + + fread(&chunkSize, 4, 1, fp); + fread(&chunkIDBuffer, 4, 1, fp); + + if (chunkIDBuffer != 0x45564157) // WAVE + { + ZONETOOL_ERROR("%s: Invalid WAVE Header.", name.c_str()); + fclose(fp); + return nullptr; + } + + char* data; + + while (!result->sound.data && !feof(fp)) + { + fread(&chunkIDBuffer, 4, 1, fp); + fread(&chunkSize, 4, 1, fp); + switch (chunkIDBuffer) + { + case 0x20746D66: // fmt + if (chunkSize >= 16) + { + short format; + fread(&format, 2, 1, fp); + if (format != 1 && format != 17) + { + ZONETOOL_ERROR("%s: Invalid wave format %i.", name.c_str(), format); + fclose(fp); + return nullptr; + } + result->sound.info.format = format; + + short numChannels; + fread(&numChannels, 2, 1, fp); + result->sound.info.channels = numChannels; + + int sampleRate; + fread(&sampleRate, 4, 1, fp); + result->sound.info.rate = sampleRate; + + int byteRate; + fread(&byteRate, 4, 1, fp); + + short blockAlign; + fread(&blockAlign, 2, 1, fp); + result->sound.info.block_size = blockAlign; + + short bitPerSample; + fread(&bitPerSample, 2, 1, fp); + result->sound.info.bits = bitPerSample; + + if (chunkSize > 16) + { + fseek(fp, chunkSize - 16, SEEK_CUR); + } + } + break; + + case 0x61746164: // data + result->sound.info.data_len = chunkSize; + data = (char*)malloc(result->sound.info.data_len); + fread(data, 1, result->sound.info.data_len, fp); + result->sound.data = data; + break; + + default: + if (chunkSize > 0) + { + fseek(fp, chunkSize, SEEK_CUR); + } + break; + } + } + + if (!result->sound.data) + { + ZONETOOL_ERROR("%s: Could not read sounddata.", name.c_str()); + fclose(fp); + return nullptr; + } + + result->name = mem->StrDup(name); + + FileSystem::FileClose(fp); + return result; + } + + void ILoadedSound::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + ZONETOOL_WARNING("Sound %s not found, it will probably sound like a motorboat ingame!", name.data()); + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).loadedsound; + } + } + + void ILoadedSound::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ILoadedSound::load_depending(IZone* zone) + { + } + + std::string ILoadedSound::name() + { + return this->name_; + } + + std::int32_t ILoadedSound::type() + { + return loaded_sound; + } + + void ILoadedSound::write(IZone* zone, ZoneBuffer* buf) + { + sizeof LoadedSound; + + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + buf->push_stream(0); + + if (data->sound.data) + { + buf->align(0); + buf->write(data->sound.data, data->sound.info.data_len); + ZoneBuffer::clear_pointer(&dest->sound.data); + } + + buf->pop_stream(); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ILoadedSound::dump(LoadedSound* asset) + { + auto sound = asset; + auto fp = FileSystem::FileOpen("loaded_sound/"s + asset->name, "wb"); + + if (fp) + { + char chunkID[] = {'R', 'I', 'F', 'F'}; + fwrite(chunkID, 4, 1, fp); + + // ChunkSize + int subchunk1Size = 16; + int subchunk2Size = sound->sound.info.data_len; + int chunkSize = 4 + (8 + subchunk1Size) + (8 + subchunk2Size); + fwrite(&chunkSize, 4, 1, fp); + + // Format + char format[] = {'W', 'A', 'V', 'E'}; + fwrite(format, 4, 1, fp); + + + // --- FMT SUBCHUNK + // Subchunk1ID + char subchunk1ID[] = {'f', 'm', 't', ' '}; + fwrite(subchunk1ID, 4, 1, fp); + + // Subchunk1Size + fwrite(&subchunk1Size, 4, 1, fp); + + // AudioFormat + short audioFormat = sound->sound.info.format; + fwrite(&audioFormat, 2, 1, fp); + + // NumChannels + short numChannels = sound->sound.info.channels; + fwrite(&numChannels, 2, 1, fp); + + // SampleRate + int sampleRate = sound->sound.info.rate; + fwrite(&sampleRate, 4, 1, fp); + + // ByteRate + int byteRate = sound->sound.info.rate * sound->sound.info.channels * sound->sound.info.bits / 8; + fwrite(&byteRate, 4, 1, fp); + + // BlockAlign + short blockAlign = sound->sound.info.block_size; + fwrite(&blockAlign, 2, 1, fp); + + // BitsPerSample + short bitsPerSample = sound->sound.info.bits; + fwrite(&bitsPerSample, 2, 1, fp); + + + // --- DATA SUBCHUNK + // Subchunk2ID + char subchunk2ID[] = {'d', 'a', 't', 'a'}; + fwrite(subchunk2ID, 4, 1, fp); + + // Subchunk2Size + fwrite(&subchunk2Size, 4, 1, fp); + + // Data + fwrite(sound->sound.data, sound->sound.info.data_len, 1, fp); + } + + FileSystem::FileClose(fp); + } + } +} diff --git a/src/IW4/Assets/LoadedSound.hpp b/src/IW4/Assets/LoadedSound.hpp new file mode 100644 index 0000000..36ad393 --- /dev/null +++ b/src/IW4/Assets/LoadedSound.hpp @@ -0,0 +1,38 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class ILoadedSound : public IAsset + { + private: + std::string name_; + LoadedSound* asset_ = nullptr; + + public: + ILoadedSound(); + ~ILoadedSound(); + + LoadedSound* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(LoadedSound* asset); + }; + } +} diff --git a/src/IW4/Assets/LocalizeEntry.cpp b/src/IW4/Assets/LocalizeEntry.cpp new file mode 100644 index 0000000..220d94b --- /dev/null +++ b/src/IW4/Assets/LocalizeEntry.cpp @@ -0,0 +1,72 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + ILocalizeEntry::ILocalizeEntry() + { + } + + ILocalizeEntry::~ILocalizeEntry() + { + } + + void ILocalizeEntry::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).localize; + } + + void ILocalizeEntry::init(void* asset, ZoneMemory* mem) + { + this->asset_ = reinterpret_cast(asset); + this->name_ = this->asset_->name; + } + + void ILocalizeEntry::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ILocalizeEntry::load_depending(IZone* zone) + { + } + + std::string ILocalizeEntry::name() + { + return this->name_; + } + + std::int32_t ILocalizeEntry::type() + { + return localize; + } + + void ILocalizeEntry::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->localizedString = buf->write_str(data->localizedString); + dest->name = buf->write_str(data->name); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ILocalizeEntry::dump(LocalizeEntry* asset) + { + } + } +} diff --git a/src/IW4/Assets/LocalizeEntry.hpp b/src/IW4/Assets/LocalizeEntry.hpp new file mode 100644 index 0000000..4e514d0 --- /dev/null +++ b/src/IW4/Assets/LocalizeEntry.hpp @@ -0,0 +1,38 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class ILocalizeEntry : public IAsset + { + private: + std::string name_; + LocalizeEntry* asset_ = nullptr; + + public: + ILocalizeEntry(); + ~ILocalizeEntry(); + + void init(const std::string& name, ZoneMemory* mem) override; + void init(void* asset, ZoneMemory* mem) override; + + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(LocalizeEntry* asset); + }; + } +} diff --git a/src/IW4/Assets/MapEnts.cpp b/src/IW4/Assets/MapEnts.cpp new file mode 100644 index 0000000..bd4f47c --- /dev/null +++ b/src/IW4/Assets/MapEnts.cpp @@ -0,0 +1,403 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "ZoneUtils/Utils/BinaryDumper.hpp" +#include "../IW5/Assets/MapEnts.hpp" + +namespace ZoneTool +{ + namespace IW4 + { +#ifdef CONVERT_IW5_MAPENTS + std::unordered_map key_conversion = + { + {"1", "pl#"}, + {"1668", "classname"}, + {"1669", "origin"}, + {"1670", "model"}, + {"1671", "spawnflags"}, + {"1672", "target"}, + {"1673", "targetname"}, + {"1676", "dmg"}, + {"1677", "angles"}, + {"1679", "script_linkname"}, + {"1705", "intensity"}, + {"1774", "script_noteworthy"}, + {"1775", "speed"}, + {"1776", "lookahead"}, + {"1782", "radius"}, + {"1783", "height"}, + {"1788", "script_speed"}, + {"1983", "animscript"}, + {"1987", "ambient"}, + {"1989", "sunlight"}, + {"1990", "suncolor"}, + {"1991", "sundirection"}, + {"2009", "script_exploder"}, + {"2328", "script_linkto"}, + {"2369", "destructible_type"}, + {"2810", "sunRadiosity"}, + {"2811", "skycolor"}, + {"2812", "skylight"}, + {"2813", "_color"}, + {"2814", "ltOrigin"}, + {"2815", "gndLt"}, + {"2816", "sound_csv_include"}, + {"2817", "csv_include"}, + {"2818", "precache_script"}, + {"2820", "maxbounces"}, + {"2821", "radiosityScale"}, + {"2823", "def"}, + {"2824", "exponent"}, + {"2825", "fov_inner"}, + {"2826", "fov_outer"}, + {"2827", "__smorigin"}, + {"2828", "__smangles"}, + {"2829", "__smname"}, + {"2830", "__smid"}, + {"3717", "script_destruct_collision"}, + {"4630", "script_bombmode_original"}, + {"7712", "modelscale"}, + {"7876", "script_accel"}, + {"10338", "script_targetoffset_z"}, + {"10396", "script_airspeed"}, + // we don't need this one for MP maps + // { "11039", "animation" }, + {"11848", "script_gameobjectname"}, + {"11996", "script_label"}, + }; + std::unordered_map key_conversion_reversed; + + void IMapEnts::convert_ents(MapEnts* ents, ZoneMemory* mem) + { + ZONETOOL_INFO("Converting mapents!"); + + std::string new_ents_string; + std::string current_entity; + std::string last_key; + bool is_valid_entity; + bool is_parsing_key; + + // make sure key_conversion_reversed is prepared + key_conversion_reversed.clear(); + + // prepare key_conversion_reversed + for (auto& key : key_conversion) + { + key_conversion_reversed[key.second] = key.first; + } + + // parse expressions + ExpressionParser parser(ents->entityString); + + // + std::string expression = parser.Parse(true); + while (!expression.empty()) + { + // convert token to lower + std::transform(expression.begin(), expression.end(), expression.begin(), tolower); + + // start parsing new entity + if (expression == "{"s) + { + // clear data from previous entity + current_entity.clear(); + is_valid_entity = true; + is_parsing_key = true; + + // add expression to current expression buffer + current_entity += expression + "\n"; + } + // finalize current entity + else if (expression == "}"s) + { + // add expression to current expression buffer + current_entity += expression + "\n"; + + // check if the entity we're about to add to the mapents is valid + if (is_valid_entity) + { + // add current expression to entity buffer + new_ents_string += current_entity; + } + else + { + ZONETOOL_INFO("Not adding entity because it contains invalid keys! Data was %s", ¤t_entity[0]); + } + } + // check key values + else + { + if (is_parsing_key) + { + auto itr1 = key_conversion.find(expression); + auto itr2 = key_conversion_reversed.find(expression); + + // checks if the key is unknown to both iw4 and iw5 + if (itr1 == key_conversion.end() && itr2 == key_conversion_reversed.end()) + { + ZONETOOL_INFO("Unknown mapents key \"%s\". Removing entity from mapents...", &expression[0] +); + + // remove current entity from mapents file + is_valid_entity = false; + } + // when converting the key + else if (itr1 != key_conversion.end()) + { + current_entity += "\"" + itr1->second + "\""; + last_key = itr1->second; + } + // when the key is already in iw4 format + else + { + current_entity += "\"" + expression + "\""; + last_key = expression; + } + } + else + { + // check if we actually need the current key/value combo + if ( + (last_key == "classname"s && expression.length() >= 5 && expression.substr(0, 5) == "node_"s) + || + (last_key == "targetname"s && expression == "delete_on_load"s) + ) + { + // remove current entity from mapents file + is_valid_entity = false; + } + + // add expression to current expression buffer + current_entity += " \"" + expression + "\"\n"; + } + + // invert parsing key state + is_parsing_key = !is_parsing_key; + } + + // parse next expression + expression = parser.Parse(true); + } + + // ZONETOOL_INFO("Entity string is %s", newEntsString.data()); + + // replace mapents if needed + if (!new_ents_string.empty()) + { + ents->numEntityChars = new_ents_string.size() + 1; + ents->entityString = mem->Alloc(ents->numEntityChars); + memset((void*)ents->entityString, 0, ents->numEntityChars); + memcpy((void*)ents->entityString, &new_ents_string[0], ents->numEntityChars - 1); + } + } +#endif + + MapEnts* IMapEnts::parse(std::string name, ZoneMemory* mem) + { + // check if we can open a filepointer + if (!FileSystem::FileExists(name + ".ents")) + { + return nullptr; + } + + auto* file = FileSystem::FileOpen(name + ".ents", "rb"); + + // let them know that we're parsing a custom mapents file + ZONETOOL_INFO("Parsing mapents \"%s\"...", name.c_str()); + + // alloc mapents + auto* ents = mem->Alloc(); + + ents->name = mem->StrDup(name); + ents->numEntityChars = FileSystem::FileSize(file); + + ents->entityString = mem->Alloc(ents->numEntityChars + 1); + memset((char*)ents->entityString, 0, ents->numEntityChars); + + fread((char*)ents->entityString, ents->numEntityChars, 1, file); + ((char*)ents->entityString)[ents->numEntityChars] = '\0'; + +#ifdef CONVERT_IW5_MAPENTS + // convert the mapents! + this->convert_ents(ents, mem); +#endif + + // close filepointer + FileSystem::FileClose(file); + + AssetReader trigger_reader(mem); + AssetReader stage_reader(mem); + + if (trigger_reader.open(name + ".ents.triggers")) + { + ents->trigger.modelCount = trigger_reader.read_int(); + ents->trigger.models = trigger_reader.read_array(); + + ents->trigger.hullCount = trigger_reader.read_int(); + ents->trigger.hulls = trigger_reader.read_array(); + + ents->trigger.slabCount = trigger_reader.read_int(); + ents->trigger.slabs = trigger_reader.read_array(); + } + + if (stage_reader.open(name + ".ents.stages")) + { + ZONETOOL_INFO("Parsing entity stages..."); + + ents->stageCount = stage_reader.read_int(); + if (ents->stageCount) + { + ents->stageNames = stage_reader.read_array(); + + for (auto i = 0; i < ents->stageCount; i++) + { + ents->stageNames[i].stageName = stage_reader.read_string(); + } + } + } + + stage_reader.close(); + trigger_reader.close(); + + // return mapents + return ents; + } + + void IMapEnts::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data()).mapents; + } + } + + void IMapEnts::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IMapEnts::load_depending(IZone* zone) + { + // IW5::IMapEnts::load_depending_internal(zone, this->asset_->entityString); + } + + std::string IMapEnts::name() + { + return this->name_; + } + + std::int32_t IMapEnts::type() + { + return map_ents; + } + + void IMapEnts::write_triggers(IZone* zone, ZoneBuffer* buf, MapTriggers* dest) + { + if (dest->models) + { + dest->models = buf->write_s(3, dest->models, dest->modelCount); + } + + if (dest->hulls) + { + dest->hulls = buf->write_s(3, dest->hulls, dest->hullCount); + } + + if (dest->slabs) + { + dest->slabs = buf->write_s(3, dest->slabs, dest->slabCount); + } + } + + void IMapEnts::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->entityString) + { + buf->align(0); + buf->write(data->entityString, data->numEntityChars); + ZoneBuffer::clear_pointer(&dest->entityString); + } + + write_triggers(zone, buf, &dest->trigger); + + if (data->stageNames) + { + buf->align(3); + auto* stage = buf->write(data->stageNames, data->stageCount); + + for (auto i = 0; i < data->stageCount; i++) + { + if (data->stageNames[i].stageName) + { + buf->write_str(data->stageNames[i].stageName); + ZoneBuffer::clear_pointer(&stage[i].stageName); + } + } + + ZoneBuffer::clear_pointer(&dest->stageNames); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IMapEnts::dump(MapEnts* asset) + { + auto* file = FileSystem::FileOpen(asset->name + ".ents"s, "wb"); + + if (file) + { + fwrite(asset->entityString, asset->numEntityChars, 1, file); + FileSystem::FileClose(file); + } + + AssetDumper trigger_dumper; + if (trigger_dumper.open(asset->name + ".ents.triggers"s)) + { + trigger_dumper.dump_int(asset->trigger.modelCount); + trigger_dumper.dump_array(asset->trigger.models, asset->trigger.modelCount); + + trigger_dumper.dump_int(asset->trigger.hullCount); + trigger_dumper.dump_array(asset->trigger.hulls, asset->trigger.hullCount); + + trigger_dumper.dump_int(asset->trigger.slabCount); + trigger_dumper.dump_array(asset->trigger.slabs, asset->trigger.slabCount); + + trigger_dumper.close(); + } + + AssetDumper stage_dumper; + if (stage_dumper.open(asset->name + ".ents.stages"s)) + { + stage_dumper.dump_int(asset->stageCount); + if (asset->stageCount) + { + stage_dumper.dump_array(asset->stageNames, asset->stageCount); + + for (auto i = 0; i < asset->stageCount; i++) + { + stage_dumper.dump_string(asset->stageNames[i].stageName); + } + } + } + stage_dumper.close(); + } + } +} diff --git a/src/IW4/Assets/MapEnts.hpp b/src/IW4/Assets/MapEnts.hpp new file mode 100644 index 0000000..828e157 --- /dev/null +++ b/src/IW4/Assets/MapEnts.hpp @@ -0,0 +1,42 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IMapEnts : public IAsset + { + private: + std::string name_; + MapEnts* asset_ = nullptr; + + public: +#ifdef CONVERT_IW5_MAPENTS + static void convert_ents(MapEnts* ents, ZoneMemory* mem); +#endif + + MapEnts* parse(std::string name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(MapEnts* asset); + + // sadly, this cannot be moved to a CPP file. + static void write_triggers(IZone* zone, ZoneBuffer* buf, MapTriggers* dest); + }; + } +} diff --git a/src/IW4/Assets/Material.cpp b/src/IW4/Assets/Material.cpp new file mode 100644 index 0000000..6fe8555 --- /dev/null +++ b/src/IW4/Assets/Material.cpp @@ -0,0 +1,563 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + // watermap structures, same across most games. + struct WaterWritable + { + float floatTime; + }; + + struct complex_s + { + float real; + float imag; + }; + + struct water_t + { + WaterWritable writable; + complex_s* H0; + float* wTerm; + int M; + int N; + float Lx; + float Lz; + float gravity; + float windvel; + float winddir[2]; + float amplitude; + float codeConstant[4]; + GfxImage* image; + }; + +#define MATERIAL_DUMP_STRING(entry) \ + matdata[#entry] = std::string(asset->entry); + +#define MATERIAL_DUMP_INT(entry) \ + matdata[#entry] = asset->entry; + +#define MATERIAL_INT(entry) \ + mat->entry = matdata[#entry].get(); + +#define STATEBITENTRYNUM 48 + +#define MATERIAL_DUMP_BITS_ENTRY(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + carr##entry[i] = asset->entry[i]; \ + } \ + matdata[#entry] = carr##entry; + +#define MATERIAL_DUMP_CONST_ARRAY(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + nlohmann::json cent##entry; \ + cent##entry["name"] = asset->entry[i].name; \ + cent##entry["nameHash"] = asset->entry[i].nameHash; \ + nlohmann::json centliteral##entry; \ + centliteral##entry[0] = asset->entry[i].literal[0]; \ + centliteral##entry[1] = asset->entry[i].literal[1]; \ + centliteral##entry[2] = asset->entry[i].literal[2]; \ + centliteral##entry[3] = asset->entry[i].literal[3]; \ + cent##entry["literal"] = centliteral##entry; \ + carr##entry[i] = cent##entry; \ + } \ + matdata[#entry] = carr##entry; + + +#define MATERIAL_DUMP_STATE_MAP(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + nlohmann::json cent##entry; \ + cent##entry[0] = asset->entry[i].loadBits[0]; \ + cent##entry[1] = asset->entry[i].loadBits[1]; \ + carr##entry[i] = cent##entry; \ + } \ + matdata[#entry] = carr##entry; + + // Legacy zonetool code: REFACTOR ME! + enum IWI_COMPRESSION_e + { + IWI_INVALID = 0x0, + IWI_ARGB = 0x1, + IWI_RGB8 = 0x2, + IWI_DXT1 = 0xB, + IWI_DXT3 = 0xC, + IWI_DXT5 = 0xD, + } IWI_COMPRESSION; + + enum MaterialMapHashes + { + HASH_COLORMAP = 0xa0ab1041, + HASH_DETAILMAP = 0xeb529b4d, + HASH_SPECULARMAP = 0x34ecccb3, + HASH_NORMALMAP = 0x59d30d0f + }; + + enum MaterialSemantic + { + SEMANTIC_2D = 0x0, + SEMANTIC_FUNCTION = 0x1, + SEMANTIC_COLOR_MAP = 0x2, + SEMANTIC_NORMAL_MAP = 0x5, + SEMANTIC_SPECULAR_MAP = 0x8, + SEMANTIC_WATER_MAP = 0xB + }; + + typedef struct + { + char magic[3]; //IWi + char version; // 8 + int flags; + short format; // see above + short xsize; + short ysize; + short depth; + int mipAddr4; + int mipAddr3; + int mipAddr2; + int mipAddr1; + } _IWI; + + // Image parsing + _IWI* LoadIWIHeader(std::string name, ZoneMemory* mem) + { + auto path = "images\\" + name + ".iwi"; + + if (FileSystem::FileExists(path)) + { + auto buf = mem->Alloc<_IWI>(); + + auto fp = FileSystem::FileOpen(path, "rb"s); + if (fp) + { + fread(buf, sizeof(_IWI), 1, fp); + } + FileSystem::FileClose(fp); + + return buf; + } // ZONETOOL_ERROR("Image \"%s\" does not exist!", name.c_str()); + + return nullptr; + } + + GfxImageLoadDef* GenerateLoadDef(GfxImage* image, short iwi_format, ZoneMemory* mem) + { + auto texture = mem->Alloc(); + + switch (iwi_format) + { + case IWI_ARGB: + texture->format = 21; + break; + case IWI_RGB8: + texture->format = 20; + break; + case IWI_DXT1: + texture->format = 0x31545844; + break; + case IWI_DXT3: + texture->format = 0x33545844; + break; + case IWI_DXT5: + texture->format = 0x35545844; + break; + } + texture->dimensions[0] = image->width; + texture->dimensions[1] = image->height; + texture->dimensions[2] = image->depth; + + return texture; + } + + GfxImage* Image_Parse(const char* name, char semantic, char category, char flags, + ZoneMemory* mem) + { + _IWI* buf = LoadIWIHeader(name, mem); + + if (buf) + { + auto ret = mem->Alloc(); + + ret->height = buf->xsize; + ret->width = buf->ysize; + ret->depth = buf->depth; + ret->dataLen1 = buf->mipAddr4 - 32; + ret->dataLen2 = buf->mipAddr4 - 32; + ret->name = strdup(name); + ret->semantic = semantic; + ret->category = category; + ret->flags = flags; + ret->mapType = 3; // hope that works lol + + ret->texture = GenerateLoadDef(ret, buf->format, mem); + + return ret; + } + + return DB_FindXAssetHeader(image, name).gfximage; + } + + MaterialImage* Material_ParseMaps(const std::string& material, nlohmann::json& matdata, + ZoneMemory* mem) + { + auto mat = mem->Alloc(matdata.size()); + + for (std::size_t i = 0; i < matdata.size(); i++) + { + mat[i].firstCharacter = matdata[i]["firstCharacter"].get(); + mat[i].secondLastCharacter = matdata[i]["lastCharacter"].get(); + mat[i].sampleState = matdata[i]["sampleState"].get(); + mat[i].semantic = matdata[i]["semantic"].get(); + mat[i].typeHash = matdata[i]["typeHash"].get(); + + std::string img = matdata[i]["image"].get(); + mat[i].image = Image_Parse(img.data(), mat[i].semantic, 0, 0, mem); + + if (img.empty()) + { + MessageBoxA(nullptr, &va("Image name for material %s seems to be empty!", &material[0])[0], nullptr, + 0); + } + } + + return mat; + } + + unsigned int R_HashString(const char* string) + { + unsigned int hash = 0; + + while (*string) + { + hash = (*string | 0x20) ^ (33 * hash); + string++; + } + + return hash; + } + + __declspec(noinline) Material* IMaterial::parse(std::string name, ZoneMemory* mem) + { + auto path = "materials\\" + name; + auto file = FileSystem::FileOpen(path, "rb"s); + if (!file) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing material \"%s\"...", name.c_str()); + + auto size = FileSystem::FileSize(file); + auto bytes = FileSystem::ReadBytes(file, size); + FileSystem::FileClose(file); + + nlohmann::json matdata = nlohmann::json::parse(bytes); + + auto mat = mem->Alloc(); + mat->name = mem->StrDup(name); + + MATERIAL_INT(gameFlags); + MATERIAL_INT(sortKey); + MATERIAL_INT(animationX); + MATERIAL_INT(animationY); + + mat->surfaceTypeBits = matdata["surfaceTypeBits"].get(); + mat->stateFlags = matdata["stateFlags"].get(); + mat->cameraRegion = matdata["cameraRegion"].get(); + + std::string techset = matdata["techniqueSet->name"].get(); + + if (!techset.empty()) + { + mat->techniqueSet = DB_FindXAssetHeader(XAssetType::techset, techset.data()).techset; + } + + auto maps = matdata["maps"]; + if (!maps.empty()) + { + mat->maps = Material_ParseMaps(mat->name, maps, mem); + } + + mat->numMaps = maps.size(); + + auto constantTable = matdata["constantTable"]; + if (!constantTable.empty()) + { + auto constant_def = mem->Alloc(constantTable.size()); + for (auto i = 0; i < constantTable.size(); i++) + { + strncpy(constant_def[i].name, constantTable[i]["name"].get().c_str(), 11); + constant_def[i].name[11] = '\0'; + constant_def[i].nameHash = constantTable[i]["nameHash"].get(); + constant_def[i].literal[0] = constantTable[i]["literal"][0].get(); + constant_def[i].literal[1] = constantTable[i]["literal"][1].get(); + constant_def[i].literal[2] = constantTable[i]["literal"][2].get(); + constant_def[i].literal[3] = constantTable[i]["literal"][3].get(); + } + mat->constantTable = constant_def; + } + else + { + mat->constantTable = nullptr; + } + mat->constantCount = constantTable.size(); + + auto stateMap = matdata["stateMap"]; + if (!stateMap.empty()) + { + auto stateBits = mem->Alloc(stateMap.size()); + for (auto i = 0; i < stateMap.size(); i++) + { + stateBits[i].loadBits[0] = stateMap[i][0].get(); + stateBits[i].loadBits[1] = stateMap[i][1].get(); + } + mat->stateMap = stateBits; + } + else + { + mat->stateMap = nullptr; + } + mat->stateBitsCount = stateMap.size(); + + if (mat->techniqueSet) + { + auto statebits = ITechset::parse_statebits(mat->techniqueSet->name, mem); + memcpy(mat->stateBitsEntry, statebits, sizeof mat->stateBitsEntry); + } + + return mat; + } + + IMaterial::IMaterial() + { + } + + IMaterial::~IMaterial() + { + } + + void IMaterial::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data()).material; + } + } + + void IMaterial::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IMaterial::load_depending(IZone* zone) + { + auto data = this->asset_; + + if (data->techniqueSet) + { + zone->add_asset_of_type(techset, data->techniqueSet->name); + } + + for (char i = 0; i < data->numMaps; i++) + { + if (data->maps[i].image) + { + // use pointer rather than name here + zone->add_asset_of_type_by_pointer(image, data->maps[i].image); + } + } + } + + std::string IMaterial::name() + { + return this->name_; + } + + std::int32_t IMaterial::type() + { + return material; + } + + void IMaterial::write(IZone* zone, ZoneBuffer* buf) + { + auto dest = buf->at(); + auto data = this->asset_; + + buf->write(data); + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->techniqueSet) + { + dest->techniqueSet = reinterpret_cast(zone->get_asset_pointer( + techset, data->techniqueSet->name)); + } + + if (data->maps) + { + buf->align(3); + auto destmaps = buf->write(data->maps, data->numMaps); + + for (int i = 0; i < data->numMaps; i++) + { + if (data->maps[i].semantic == 11) + { + ZONETOOL_ERROR("Watermaps are not supported."); + destmaps[i].image = nullptr; + } + else + { + if (data->maps[i].image) + { + destmaps[i].image = reinterpret_cast(zone->get_asset_pointer( + image, data->maps[i].image->name)); + } + } + } + + ZoneBuffer::clear_pointer(&dest->maps); + } + + if (data->constantTable) + { + dest->constantTable = buf->write_s(15, data->constantTable, data->constantCount); + } + + if (data->stateMap) + { + dest->stateMap = buf->write_s(3, data->stateMap, data->stateBitsCount); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IMaterial::dump(Material* asset) + { + if (asset && asset->techniqueSet) + { + ITechset::dump_statebits(asset->techniqueSet->name, asset->stateBitsEntry); + } + + nlohmann::json matdata; + + MATERIAL_DUMP_STRING(name); + + if (asset->techniqueSet) + { + MATERIAL_DUMP_STRING(techniqueSet->name); + } + + MATERIAL_DUMP_INT(gameFlags); + MATERIAL_DUMP_INT(sortKey); + MATERIAL_DUMP_INT(animationX); + MATERIAL_DUMP_INT(animationY); + + MATERIAL_DUMP_INT(unknown); + MATERIAL_DUMP_INT(surfaceTypeBits); + MATERIAL_DUMP_INT(stateFlags); + MATERIAL_DUMP_INT(cameraRegion); + + MATERIAL_DUMP_CONST_ARRAY(constantTable, asset->constantCount); + MATERIAL_DUMP_STATE_MAP(stateMap, asset->stateBitsCount); + + nlohmann::json material_images; + for (int i = 0; i < asset->numMaps; i++) + { + nlohmann::json image; + + // watermap + if (asset->maps[i].semantic == 11) + { + ZONETOOL_INFO("Dumping water data for image %s\n", asset->name); + + water_t* waterData = reinterpret_cast(asset->maps[i].image); + + image["image"] = waterData->image->name; + + nlohmann::json waterdata; + waterdata["floatTime"] = waterData->writable.floatTime; + waterdata["codeConstant"][0] = waterData->codeConstant[0]; + waterdata["codeConstant"][1] = waterData->codeConstant[1]; + waterdata["codeConstant"][2] = waterData->codeConstant[2]; + waterdata["codeConstant"][3] = waterData->codeConstant[3]; + waterdata["M"] = waterData->M; + waterdata["N"] = waterData->N; + waterdata["Lx"] = waterData->Lx; + waterdata["Lz"] = waterData->Lz; + waterdata["gravity"] = waterData->gravity; + waterdata["windvel"] = waterData->windvel; + waterdata["winddir"][0] = waterData->winddir[0]; + waterdata["winddir"][1] = waterData->winddir[1]; + waterdata["amplitude"] = waterData->amplitude; + + nlohmann::json waterComplexData; + nlohmann::json wTerm; + + for (int i = 0; i < waterData->M * waterData->N; i++) + { + nlohmann::json complexdata; + nlohmann::json curWTerm; + + complexdata["real"] = waterData->H0[i].real; + complexdata["imag"] = waterData->H0[i].imag; + + curWTerm[i] = waterData->wTerm[i]; + + waterComplexData[i] = complexdata; + } + + waterdata["complex"] = waterComplexData; + waterdata["wTerm"] = wTerm; + + image["waterinfo"] = waterdata; + } + else + { + image["image"] = asset->maps[i].image->name; + } + + image["semantic"] = asset->maps[i].semantic; + image["sampleState"] = asset->maps[i].sampleState; + image["lastCharacter"] = asset->maps[i].secondLastCharacter; + image["firstCharacter"] = asset->maps[i].firstCharacter; + image["typeHash"] = asset->maps[i].typeHash; + + // add image data to material + material_images[i] = image; + } + matdata["maps"] = material_images; + + std::string assetstr = matdata.dump(4); + + auto assetPath = "materials\\"s + asset->name; + + auto fileAsset = FileSystem::FileOpen(assetPath, "wb"); + + if (fileAsset) + { + fwrite(assetstr.c_str(), assetstr.size(), 1, fileAsset); + FileSystem::FileClose(fileAsset); + } + } + } +} diff --git a/src/IW4/Assets/Material.hpp b/src/IW4/Assets/Material.hpp new file mode 100644 index 0000000..f4c2bb3 --- /dev/null +++ b/src/IW4/Assets/Material.hpp @@ -0,0 +1,39 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IMaterial : public IAsset + { + private: + std::string name_; + Material* asset_ = nullptr; + + Material* parse(std::string name, ZoneMemory* mem); + + public: + IMaterial(); + ~IMaterial(); + + void FixStatebits(IZone* zone); + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(Material* asset); + }; + } +} diff --git a/src/IW4/Assets/PhysCollmap.cpp b/src/IW4/Assets/PhysCollmap.cpp new file mode 100644 index 0000000..40f36d3 --- /dev/null +++ b/src/IW4/Assets/PhysCollmap.cpp @@ -0,0 +1,270 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + PhysCollmap* IPhysCollmap::parse(const std::string& name, ZoneMemory* mem) + { + auto version = 4; + auto path = "physcollmap/" + name + ".cme4"; + + if (!FileSystem::FileExists(path)) + { + version = 3; + path = "physcollmap/" + name + ".cme3"; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + } + + AssetReader read(mem); + + if (!read.open(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing phys_collmap \"%s\"...", name.c_str()); + + auto* phys_collmap = read.read_array(); + phys_collmap->name = mem->StrDup(name); + + if (read.read_int()) + { + phys_collmap->info = read.read_array(); + + for (auto i = 0u; i < phys_collmap->numInfo; i++) + { + if (read.read_int()) + { + phys_collmap->info[i].brush = read.read_array(); + + if (version == 4) + { + if (read.read_int()) + { + phys_collmap->info[i].brush->plane = read.read_array(); + } + } + + if (read.read_int()) + { + phys_collmap->info[i].brush->side = read.read_array(); + + for (auto j = 0u; j < phys_collmap->info[i].brush->numPlaneSide; j++) + { + if (read.read_int()) + { + phys_collmap->info[i].brush->side[j].plane = read.read_array(); + } + } + } + + if (read.read_int()) + { + phys_collmap->info[i].brush->edge = read.read_array(); + } + + if (version == 3) + { + if (read.read_int()) + { + phys_collmap->info[i].brush->plane = read.read_array(); + } + } + } + } + } + + return phys_collmap; + } + + void IPhysCollmap::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).physcollmap; + } + } + + void IPhysCollmap::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IPhysCollmap::load_depending(IZone* zone) + { + } + + std::string IPhysCollmap::name() + { + return this->name_; + } + + std::int32_t IPhysCollmap::type() + { + return phys_collmap; + } + + void IPhysCollmap::write_brush_wrapper(IZone* zone, ZoneBuffer* buf, BrushWrapper* data) + { + auto* dest = buf->write(data); + + if (data->side) + { + buf->align(3); + auto* cbrushside = buf->write(data->side, data->numPlaneSide); + + for (auto i = 0; i < data->numPlaneSide; i++) + { + cbrushside[i].plane = buf->write_s(3, data->side[i].plane); + } + + ZoneBuffer::clear_pointer(&dest->side); + } + + if (data->edge) + { + buf->align(0); + buf->write(data->edge, data->numEdge); + ZoneBuffer::clear_pointer(&dest->edge); + } + + if (data->plane) + { + dest->plane = buf->write_s(3, data->plane, data->numPlaneSide); + } + } + + void IPhysCollmap::write_phys_geom_info(IZone* zone, ZoneBuffer* buf, PhysGeomInfo* dest) + { + auto* data = dest; + + if (data->brush) + { + buf->align(3); + this->write_brush_wrapper(zone, buf, data->brush); + ZoneBuffer::clear_pointer(&dest->brush); + } + } + + void IPhysCollmap::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + + dest->name = buf->write_str(this->name()); + + if (data->info) + { + buf->align(3); + auto* phys_geom_info = buf->write(data->info, data->numInfo); + + for (int i = 0; i < data->numInfo; i++) + { + this->write_phys_geom_info(zone, buf, &phys_geom_info[i]); + } + } + + buf->pop_stream(); + } + + void IPhysCollmap::dump(PhysCollmap* asset) + { + const auto path = "physcollmap/" + std::string(asset->name) + ".cme4"; + + AssetDumper dump; + dump.open(path.data()); + dump.dump_array(asset, 1); + + if (asset->info) + { + dump.dump_int(1); + dump.dump_array(asset->info, asset->numInfo); + + for (auto i = 0; i < asset->numInfo; i++) + { + if (asset->info[i].brush) + { + dump.dump_int(1); + dump.dump_array(asset->info[i].brush, 1); + + if (asset->info[i].brush->plane) + { + dump.dump_int(1); + dump.dump_array(asset->info[i].brush->plane, asset->info[i].brush->numPlaneSide); + + // printf("phys_collmap \"%s\"[%i] has %i collision planes.\n", asset->name, i, asset->info[i].brush->numPlaneSide); + } + else + { + dump.dump_int(0); + } + + if (asset->info[i].brush->side) + { + // printf("phys_collmap \"%s\"[%i] has %i collision sides.\n", asset->name, i, asset->info[i].brush->numPlaneSide); + + dump.dump_int(1); + dump.dump_array(asset->info[i].brush->side, asset->info[i].brush->numPlaneSide); + + for (int j = 0; j < asset->info[i].brush->numPlaneSide; j++) + { + if (asset->info[i].brush->side[j].plane) + { + dump.dump_int(1); + dump.dump_array(asset->info[i].brush->side[j].plane, 1); + } + else + { + dump.dump_int(0); + } + } + } + else + { + dump.dump_int(0); + } + + if (asset->info[i].brush->edge) + { + // printf("phys_collmap \"%s\"[%i] has %i collision edges.\n", asset->name, i, asset->info[i].brush->numEdge); + + dump.dump_int(1); + dump.dump_array(asset->info[i].brush->edge, asset->info[i].brush->numEdge); + } + else + { + dump.dump_int(0); + } + } + else + { + dump.dump_int(0); + } + } + } + else + { + dump.dump_int(0); + } + + dump.close(); + } + } +} diff --git a/src/IW4/Assets/PhysCollmap.hpp b/src/IW4/Assets/PhysCollmap.hpp new file mode 100644 index 0000000..cd9e6d2 --- /dev/null +++ b/src/IW4/Assets/PhysCollmap.hpp @@ -0,0 +1,37 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IPhysCollmap : public IAsset + { + private: + std::string name_; + PhysCollmap* asset_ = nullptr; + + public: + PhysCollmap* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write_brush_wrapper(IZone* zone, ZoneBuffer* buf, BrushWrapper* data); + void write_phys_geom_info(IZone* zone, ZoneBuffer* buf, PhysGeomInfo* dest); + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(PhysCollmap* asset); + }; + } +} diff --git a/src/IW4/Assets/PhysPreset.cpp b/src/IW4/Assets/PhysPreset.cpp new file mode 100644 index 0000000..bd00b48 --- /dev/null +++ b/src/IW4/Assets/PhysPreset.cpp @@ -0,0 +1,60 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void IPhysPreset::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).physpreset; + } + + void IPhysPreset::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IPhysPreset::load_depending(IZone* zone) + { + } + + std::string IPhysPreset::name() + { + return this->name_; + } + + std::int32_t IPhysPreset::type() + { + return physpreset; + } + + void IPhysPreset::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + + dest->name = buf->write_str(this->name()); + + if (data->sndAliasPrefix) + { + dest->sndAliasPrefix = buf->write_str(data->sndAliasPrefix); + } + + buf->pop_stream(); + } + + void IPhysPreset::dump(PhysPreset* asset) + { + } + } +} diff --git a/src/IW4/Assets/PhysPreset.hpp b/src/IW4/Assets/PhysPreset.hpp new file mode 100644 index 0000000..fc7c564 --- /dev/null +++ b/src/IW4/Assets/PhysPreset.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IPhysPreset : public IAsset + { + private: + std::string name_; + PhysPreset* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(PhysPreset* asset); + }; + } +} diff --git a/src/IW4/Assets/PixelShader.cpp b/src/IW4/Assets/PixelShader.cpp new file mode 100644 index 0000000..c9f9768 --- /dev/null +++ b/src/IW4/Assets/PixelShader.cpp @@ -0,0 +1,85 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/PixelShader.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + PixelShader* IPixelShader::parse(const std::string& name, ZoneMemory* mem, bool preferLocal) + { + return reinterpret_cast(IW5::IPixelShader::parse(name, mem, preferLocal)); + } + + void IPixelShader::init(void* asset, ZoneMemory* mem) + { + this->asset_ = reinterpret_cast(asset); + this->name_ = this->asset_->name + "_IW5"s; + } + + void IPixelShader::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).pixelshader; + + if (DB_IsXAssetDefault(this->type(), this->name().data())) + { + ZONETOOL_FATAL("PixelShader %s not found.", &name[0]); + } + } + } + + void IPixelShader::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IPixelShader::load_depending(IZone* zone) + { + } + + std::string IPixelShader::name() + { + return this->name_; + } + + std::int32_t IPixelShader::type() + { + return pixelshader; + } + + void IPixelShader::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->bytecode) + { + dest->bytecode = buf->write_s(3, data->bytecode, data->codeLen); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IPixelShader::dump(PixelShader* asset) + { + return IW5::IPixelShader::dump(reinterpret_cast(asset)); + } + } +} diff --git a/src/IW4/Assets/PixelShader.hpp b/src/IW4/Assets/PixelShader.hpp new file mode 100644 index 0000000..cecf824 --- /dev/null +++ b/src/IW4/Assets/PixelShader.hpp @@ -0,0 +1,37 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IPixelShader : public IAsset + { + private: + std::string name_; + PixelShader* asset_ = nullptr; + + public: + static PixelShader* parse(const std::string& name, ZoneMemory* mem, + bool preferLocal = false); + + void init(void* asset, ZoneMemory* mem) override; + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(PixelShader* asset); + }; + } +} diff --git a/src/IW4/Assets/RawFile.cpp b/src/IW4/Assets/RawFile.cpp new file mode 100644 index 0000000..3f7f32d --- /dev/null +++ b/src/IW4/Assets/RawFile.cpp @@ -0,0 +1,133 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include + +#define ZONETOOL_BRANDING "Compiled using ZoneTool by RektInator." + +namespace ZoneTool +{ + namespace IW4 + { + RawFile* IRawFile::parse(const std::string& name, ZoneMemory* mem) + { + if (FileSystem::FileExists(name)) + { + auto* rawfile = mem->Alloc(); + rawfile->name = mem->StrDup(name); + + auto* file = FileSystem::FileOpen(name, "rb"s); + if (file) + { + const auto size = FileSystem::FileSize(file); + auto data = FileSystem::ReadBytes(file, size); + + ZoneBuffer buf(data); + auto compressed = buf.compress_zlib(); + + rawfile->len = size; + rawfile->compressedLen = compressed.size(); + rawfile->buffer = mem->Alloc(size); + memcpy( + const_cast(rawfile->buffer), + compressed.data(), + size); + + FileSystem::FileClose(file); + } + + return rawfile; + } + + return nullptr; + } + + void IRawFile::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = parse(name, mem); + + if (name == FileSystem::GetFastFile()) + { + this->asset_ = mem->Alloc(); + this->asset_->name = mem->StrDup(name); + this->asset_->buffer = mem->StrDup(ZONETOOL_BRANDING); + this->asset_->len = strlen(this->asset_->buffer); + } + else if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data()).rawfile; + } + } + + void IRawFile::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IRawFile::load_depending(IZone* zone) + { + } + + std::string IRawFile::name() + { + return this->name_; + } + + std::int32_t IRawFile::type() + { + return rawfile; + } + + void IRawFile::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->buffer) + { + buf->align(0); + buf->write(data->buffer, data->compressedLen ? data->compressedLen : data->len + 1); + ZoneBuffer::clear_pointer(&dest->buffer); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IRawFile::dump(RawFile* asset) + { + auto* fp = FileSystem::FileOpen(asset->name, "wb"); + + if (fp) + { + if (asset->compressedLen > 0) + { + std::vector uncompressed; + uncompressed.resize(asset->len); + + auto status = uncompress(uncompressed.data(), (uLongf*)&asset->len, (Bytef*)asset->buffer, + asset->compressedLen); + + fwrite(uncompressed.data(), uncompressed.size(), 1, fp); + } + else if (asset->len > 0) + { + fwrite(asset->buffer, asset->len, 1, fp); + } + } + + FileSystem::FileClose(fp); + } + } +} diff --git a/src/IW4/Assets/RawFile.hpp b/src/IW4/Assets/RawFile.hpp new file mode 100644 index 0000000..b896834 --- /dev/null +++ b/src/IW4/Assets/RawFile.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IRawFile : public IAsset + { + private: + std::string name_; + RawFile* asset_ = nullptr; + + RawFile* parse(const std::string& name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(RawFile* asset); + }; + } +} diff --git a/src/IW4/Assets/Sound.cpp b/src/IW4/Assets/Sound.cpp new file mode 100644 index 0000000..5d1f14d --- /dev/null +++ b/src/IW4/Assets/Sound.cpp @@ -0,0 +1,245 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/Sound.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + snd_alias_list_t* ISound::parse(const std::string& name, ZoneMemory* mem) + { + auto* iw5_asset = IW5::ISound::parse(name, mem); + + if (!iw5_asset) + { + return nullptr; + } + + auto* asset = mem->Alloc(); + memset(asset, 0, sizeof snd_alias_list_t); + + asset->name = iw5_asset->name; + asset->count = iw5_asset->count; + + asset->head = mem->Alloc(asset->count); + memset(asset->head, 0, sizeof(snd_alias_t) * asset->count); + + for (auto i = 0; i < asset->count; i++) + { + auto* current_iw4 = &asset->head[i]; + auto* current_iw5 = &iw5_asset->head[i]; + + memcpy(current_iw4, current_iw5, 36); + memcpy(¤t_iw4->pitchMin, ¤t_iw5->pitchMin, 24); + + if (current_iw5->masterPercentage == 0.0f || current_iw5->slavePercentage > current_iw5->masterPercentage) + { + current_iw4->___u15.slavePercentage = current_iw5->slavePercentage; + } + else + { + current_iw4->___u15.masterPercentage = current_iw5->masterPercentage; + } + + memcpy(¤t_iw4->probability, ¤t_iw5->probability, 36); + } + + return asset; + } + + void ISound::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).sound; + } + } + + void ISound::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ISound::load_depending(IZone* zone) + { + auto* data = this->asset_; + + for (auto i = 0u; i < data->count; i++) + { + auto* head = &data->head[i]; + + if (head->volumeFalloffCurve) + { + zone->add_asset_of_type(sndcurve, head->volumeFalloffCurve->filename); + } + + if (head->soundFile) + { + if (head->soundFile->type == SAT_LOADED) + { + zone->add_asset_of_type(loaded_sound, head->soundFile->sound.loadSnd->name); + } + } + } + } + + std::string ISound::name() + { + return this->name_; + } + + std::int32_t ISound::type() + { + return sound; + } + + void ISound::write_soundfile(IZone* zone, ZoneBuffer* buf, SoundFile* data) + { + auto* dest = buf->write(data); + + if (data->type == SAT_STREAMED || data->type == SAT_PRIMED) + { + if (data->sound.streamSnd.dir) + { + dest->sound.streamSnd.dir = buf->write_str(data->sound.streamSnd.dir); + } + + if (data->sound.streamSnd.name) + { + dest->sound.streamSnd.name = buf->write_str(data->sound.streamSnd.name); + } + } + else if (data->type == SAT_LOADED) + { + if (data->sound.loadSnd) + { + dest->sound.loadSnd = static_cast(zone->get_asset_pointer( + loaded_sound, data->sound.loadSnd->name)); + } + } + } + + void ISound::write_head(IZone* zone, ZoneBuffer* buf, snd_alias_t* dest) + { + auto* data = dest; + + if (data->aliasName) + { + dest->aliasName = buf->write_str(data->aliasName); + } + + if (data->subtitle) + { + dest->subtitle = buf->write_str(data->subtitle); + } + + if (data->secondaryAliasName) + { + dest->secondaryAliasName = buf->write_str(data->secondaryAliasName); + } + + if (data->chainAliasName) + { + dest->chainAliasName = buf->write_str(data->chainAliasName); + } + + if (data->mixerGroup) + { + dest->mixerGroup = buf->write_str(data->mixerGroup); + } + + if (data->soundFile) + { + buf->align(3); + write_soundfile(zone, buf, data->soundFile); + ZoneBuffer::clear_pointer(&dest->soundFile); + } + + if (data->volumeFalloffCurve) + { + dest->volumeFalloffCurve = static_cast(zone->get_asset_pointer( + sndcurve, data->volumeFalloffCurve->filename)); + } + + if (data->speakerMap) + { + buf->align(3); + auto* speaker_map = buf->write(data->speakerMap); + + if (speaker_map->name) + { + speaker_map->name = buf->write_str(speaker_map->name); + } + + ZoneBuffer::clear_pointer(&dest->speakerMap); + } + } + + void ISound::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (dest->head) + { + buf->align(3); + auto* dest_sound = buf->write(data->head, data->count); + + for (auto i = 0; i < data->count; i++) + { + write_head(zone, buf, &dest_sound[i]); + } + + ZoneBuffer::clear_pointer(&dest->head); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ISound::dump(snd_alias_list_t* asset) + { + auto* iw5_asset = new IW5::snd_alias_list_t; + memset(iw5_asset, 0, sizeof IW5::snd_alias_list_t); + + iw5_asset->name = asset->name; + iw5_asset->count = asset->count; + + iw5_asset->head = new IW5::snd_alias_t[iw5_asset->count]; + memset(iw5_asset->head, 0, sizeof(IW5::snd_alias_t) * iw5_asset->count); + + for (auto i = 0; i < iw5_asset->count; i++) + { + auto* current_iw4 = &asset->head[i]; + auto* current_iw5 = &iw5_asset->head[i]; + + memcpy(current_iw5, &asset->head[i], 36); + current_iw5->volModIndex = 0x12; + memcpy(¤t_iw5->pitchMin, ¤t_iw4->pitchMin, 24); + current_iw5->masterPriority = 2; + current_iw5->masterPercentage = current_iw4->___u15.masterPercentage; + current_iw5->slavePercentage = current_iw4->___u15.slavePercentage; + memcpy(¤t_iw5->probability, ¤t_iw4->probability, 36); + } + + IW5::ISound::dump(iw5_asset); + + delete[] iw5_asset->head; + delete iw5_asset; + } + } +} diff --git a/src/IW4/Assets/Sound.hpp b/src/IW4/Assets/Sound.hpp new file mode 100644 index 0000000..cd5b23a --- /dev/null +++ b/src/IW4/Assets/Sound.hpp @@ -0,0 +1,38 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class ISound : public IAsset + { + private: + std::string name_; + snd_alias_list_t* asset_ = nullptr; + + static void write_soundfile(IZone* zone, ZoneBuffer* buf, SoundFile* dest); + static void write_head(IZone* zone, ZoneBuffer* buf, snd_alias_t* dest); + + public: + static snd_alias_list_t* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(snd_alias_list_t* asset); + }; + } +} diff --git a/src/IW4/Assets/SoundCurve.cpp b/src/IW4/Assets/SoundCurve.cpp new file mode 100644 index 0000000..319f19d --- /dev/null +++ b/src/IW4/Assets/SoundCurve.cpp @@ -0,0 +1,57 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void ISoundCurve::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).soundcurve; + } + + void ISoundCurve::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ISoundCurve::load_depending(IZone* zone) + { + } + + std::string ISoundCurve::name() + { + return this->name_; + } + + std::int32_t ISoundCurve::type() + { + return sndcurve; + } + + void ISoundCurve::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->filename = buf->write_str(this->name()); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ISoundCurve::dump(SndCurve* asset) + { + } + } +} diff --git a/src/IW4/Assets/SoundCurve.hpp b/src/IW4/Assets/SoundCurve.hpp new file mode 100644 index 0000000..d472bec --- /dev/null +++ b/src/IW4/Assets/SoundCurve.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class ISoundCurve : public IAsset + { + private: + std::string name_; + SndCurve* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(SndCurve* asset); + }; + } +} diff --git a/src/IW4/Assets/StringTable.cpp b/src/IW4/Assets/StringTable.cpp new file mode 100644 index 0000000..a3a9a37 --- /dev/null +++ b/src/IW4/Assets/StringTable.cpp @@ -0,0 +1,222 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + // LEGACY ZONETOOL CODE, FIX ME! + class CSV + { + protected: + std::string _name; + std::vector> _data; + + public: + CSV(std::string name, char sep = ',') + : _name(name) + { + auto fp = FileSystem::FileOpen(name, "r"s); + + if (fp) + { + long len = FileSystem::FileSize(fp); + auto buf = std::make_unique(len + 1); + memset(buf.get(), 0, len + 1); + fread(buf.get(), len, 1, fp); + fclose(fp); + + std::vector rows = split(std::string(buf.get()), '\n'); + + for (auto& row : rows) + { + // Replace literal characters + std::size_t pos; + while ((pos = row.find("\\n")) != std::string::npos) + { + row.replace(pos, 2, "\n"); + } + + while ((pos = row.find("\\t")) != std::string::npos) + { + row.replace(pos, 2, "\t"); + } + + _data.push_back(split(row, sep)); + } + } + } + + std::string entry(std::size_t row, std::size_t column) + { + return _data[row][column]; + } + + std::size_t rows() + { + return _data.size(); + } + + std::size_t columns(std::size_t row) + { + return _data[row].size(); + } + + std::size_t max_columns() + { + std::size_t _max = 0; + + for (std::size_t row = 0; row < this->rows(); row++) + { + if (_max < this->columns(row)) + _max = this->columns(row); + } + + return _max; + } + + void clear() + { + for (std::size_t i = 0; i < _data.size(); i++) + { + for (std::size_t j = 0; j < _data[i].size(); j++) + _data[i][j].clear(); + + _data[i].clear(); + } + + _data.clear(); + } + }; + + int StringTable_Hash(const char* string) + { + int hash = 0; + char* data = _strdup(string); + + while (*data != 0) + { + hash = tolower(*data) + (31 * hash); + data++; + } + + return hash; + } + + StringTable* StringTable_Parse(std::string name, ZoneMemory* mem) + { + auto table = std::make_unique(name); + auto stringtable = mem->Alloc(); + + stringtable->name = mem->StrDup(name.c_str()); + stringtable->rows = table->rows(); + stringtable->columns = table->max_columns(); + stringtable->strings = mem->Alloc(stringtable->rows * stringtable->columns); + + for (int row = 0; row < table->rows(); row++) + { + for (int col = 0; col < table->columns(row); col++) + { + int entry = (row * stringtable->columns) + col; + stringtable->strings[entry].string = mem->StrDup(table->entry(row, col).c_str()); + stringtable->strings[entry].hash = StringTable_Hash(stringtable->strings[entry].string); + } + } + + return stringtable; + } + + void IStringTable::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).stringtable; + + if (FileSystem::FileExists(name)) + { + ZONETOOL_INFO("Parsing stringtable %s...", name.data()); + this->asset_ = StringTable_Parse(name, mem); + } + } + + void IStringTable::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IStringTable::load_depending(IZone* zone) + { + } + + std::string IStringTable::name() + { + return this->name_; + } + + std::int32_t IStringTable::type() + { + return stringtable; + } + + void IStringTable::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->strings) + { + buf->align(3); + auto* dest_strings = buf->write(data->strings, data->columns * data->rows); + + if (data->columns * data->rows > 0) + { + for (auto i = 0; i < data->columns * data->rows; i++) + { + if (data->strings[i].string) + { + dest_strings[i].string = buf->write_str(data->strings[i].string); + } + } + } + + ZoneBuffer::clear_pointer(&dest->strings); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IStringTable::dump(StringTable* asset) + { + const std::string path = asset->name; + auto* file = FileSystem::FileOpen(path, "w"s); + + for (auto row = 0; row < asset->rows; row++) + { + for (auto column = 0; column < asset->columns; column++) + { + fprintf( + file, + "%s%s", + (asset->strings[(row * asset->columns) + column].string) + ? asset->strings[(row * asset->columns) + column].string + : "", + (column == asset->columns - 1) ? "\n" : "," + ); + } + } + + FileSystem::FileClose(file); + } + } +} diff --git a/src/IW4/Assets/StringTable.hpp b/src/IW4/Assets/StringTable.hpp new file mode 100644 index 0000000..4f180ef --- /dev/null +++ b/src/IW4/Assets/StringTable.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IStringTable : public IAsset + { + private: + std::string name_; + StringTable* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(StringTable* asset); + }; + } +} diff --git a/src/IW4/Assets/StructuredDataDef.cpp b/src/IW4/Assets/StructuredDataDef.cpp new file mode 100644 index 0000000..8cf880d --- /dev/null +++ b/src/IW4/Assets/StructuredDataDef.cpp @@ -0,0 +1,294 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void IStructuredDataDef::add_entry(const enum_type type, const int stat_index_offset, const std::string& entry_name, ZoneMemory* mem) + { + const auto new_entry = mem->Alloc(); + new_entry->name = _strdup(entry_name.data()); + new_entry->statIndexOffset = stat_index_offset; + newEntries[type][newEntries[type].size()] = new_entry; + } + + bool check_alphabetical_order(std::string entry1, std::string entry2) + { + std::transform(entry1.begin(), entry1.end(), entry1.begin(), tolower); + std::transform(entry2.begin(), entry2.end(), entry2.begin(), tolower); + + const auto len = std::min(entry1.size(), entry2.size()); + const auto e1_l = entry1.data(); + const auto e2_l = entry2.data(); + + for (int i = 0; i < len; i++) + { + if (*(e1_l + i) > *(e2_l + i)) + return false; + if (*(e1_l + i) < *(e2_l + i)) + return true; + } + + return entry1.size() <= entry2.size(); + } + + void IStructuredDataDef::patch_enum_with_map(StructuredDataDefSet* data, enum_type enumIndex, + std::map map, ZoneMemory* mem) + { + const auto current_enum = &data->defs->enums[enumIndex]; + + // Check if new enum has already been built + if (newIndexCount[enumIndex]) + { + current_enum->entryCount = newIndexCount[enumIndex]; + current_enum->entries = newIndices[enumIndex]; + return; + } + + // Check if new weapons share the same index or if it is invalid + for (std::size_t i = 0; i < map.size(); i++) + { + if (map[i]->statIndexOffset < 1) + { + ZONETOOL_ERROR("Invalid index (%d) for entry %s\n ", map[i]->statIndexOffset, map[i]->name); + } + + for (std::size_t j = 0; j < map.size(); j++) + { + if (i == j) continue; + + if (map[i]->statIndexOffset == map[j]->statIndexOffset) + { + ZONETOOL_ERROR("Index duplication (%d) for entries %s and %s\n", map[i]->statIndexOffset, map[i] +->name, map[j]->name); + } + } + } + + // Define new amount of indices + newIndexCount[enumIndex] = current_enum->entryCount + map.size(); + + // Find last cac index + auto last_cac_index = 0; + for (auto i = 0; i < current_enum->entryCount; i++) + { + if (current_enum->entries[i].index > last_cac_index) + { + last_cac_index = current_enum->entries[i].index; + } + } + + // Create new enum + newIndices[enumIndex] = mem->ManualAlloc(sizeof(StructuredDataEnumEntry) * (current_enum->entryCount + map.size() + 1)); + memcpy(newIndices[enumIndex], current_enum->entries, sizeof(StructuredDataEnumEntry) * current_enum->entryCount); + + // Apply new weapons to enum + for (std::size_t i = 0; i < map.size(); i++) + { + auto entry_pos = 0; + + for (; entry_pos < current_enum->entryCount + i; entry_pos++) + { + if (!strcmp(map[i]->name, newIndices[enumIndex][entry_pos].name)) + { + ZONETOOL_FATAL("Duplicate playerdatadef entry found: %s", map[i]->name); + } + + // Search weapon position + if (check_alphabetical_order(map[i]->name, (char*)newIndices[enumIndex][entry_pos].name)) + { + break; + } + } + + for (int j = current_enum->entryCount + i; j > entry_pos; j--) + { + newIndices[enumIndex][j] = newIndices[enumIndex][j - 1]; + } + + newIndices[enumIndex][entry_pos].index = map[i]->statIndexOffset + last_cac_index; + newIndices[enumIndex][entry_pos].name = map[i]->name; + } + + // Apply stuff to current player data + current_enum->entryCount = newIndexCount[enumIndex]; + current_enum->entries = newIndices[enumIndex]; + } + + // Adds new entries to the structuredDataDef + void IStructuredDataDef::manipulate(StructuredDataDefSet* data, ZoneMemory* mem) + { + // clear existing data if present + for (auto i = 0; i < enum_type::count; i++) + { + if (!newEntries[i].empty()) + { + newEntries[i].clear(); + } + } + + // Patch mp/playerdata.def if needed + if (!strcmp(data->name, "mp/playerdata.def")) + { + // add new entries here + add_entry(enum_type::weapons, 1, "cm901", mem); + + ZONETOOL_INFO("Statfiles patched."); + } + + // Increment version + data->defs->version += 1; + + // Patch enums + for (auto i = 0; i < enum_type::count; i++) + { + if (!newEntries[i].empty()) + { + patch_enum_with_map(data, static_cast(i), newEntries[i], mem); + } + } + } + + void IStructuredDataDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).structureddatadef; + + memset(newIndices, 0, sizeof(StructuredDataEnumEntry*) * enum_type::count); + memset(newIndexCount, 0, sizeof(int) * enum_type::count); + + for (int i = 0; i < enum_type::count; i++) + { + newEntries[i].clear(); + } + + // touch the asset + manipulate(this->asset_, mem); + } + + void IStructuredDataDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IStructuredDataDef::load_depending(IZone* zone) + { + } + + std::string IStructuredDataDef::name() + { + return this->name_; + } + + std::int32_t IStructuredDataDef::type() + { + return structureddatadef; + } + + void IStructuredDataDef::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->defs) + { + buf->align(3); + auto defs = buf->write(data->defs, data->defCount); + + for (unsigned int i = 0; i < data->defCount; i++) + { + auto curdef = &data->defs[i]; + + if (curdef->enums) + { + buf->align(3); + auto enums = buf->write(curdef->enums, curdef->enumCount); + + for (int j = 0; j < curdef->enumCount; j++) + { + if (enums[j].entries) + { + buf->align(3); + auto entries = buf->write(enums[j].entries, enums[j].entryCount); + + for (int k = 0; k < enums[j].entryCount; k++) + { + if (entries[k].name) + { + buf->write_str(entries[k].name); + ZoneBuffer::clear_pointer(&entries[k].name); + } + } + + ZoneBuffer::clear_pointer(&enums[j].entries); + } + } + + ZoneBuffer::clear_pointer(&defs[i].enums); + } + + if (curdef->structs) + { + buf->align(3); + auto structs = buf->write(curdef->structs, curdef->structCount); + + for (int j = 0; j < curdef->structCount; j++) + { + if (structs[j].properties) + { + buf->align(3); + auto props = buf->write(structs[j].properties, structs[j].propertyCount); + + for (int k = 0; k < structs[j].propertyCount; k++) + { + if (props[k].name) + { + buf->write_str(props[k].name); + ZoneBuffer::clear_pointer(&props[k].name); + } + } + + ZoneBuffer::clear_pointer(&structs[j].properties); + } + } + + ZoneBuffer::clear_pointer(&defs[i].structs); + } + + if (curdef->indexedArrays) + { + buf->align(3); + buf->write(curdef->indexedArrays, curdef->indexedArrayCount); + ZoneBuffer::clear_pointer(&defs[i].indexedArrays); + } + + if (curdef->enumedArrays) + { + buf->align(3); + buf->write(curdef->enumedArrays, curdef->enumedArrayCount); + ZoneBuffer::clear_pointer(&defs[i].enumedArrays); + } + } + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IStructuredDataDef::dump(StructuredDataDefSet* asset) + { + } + } +} diff --git a/src/IW4/Assets/StructuredDataDef.hpp b/src/IW4/Assets/StructuredDataDef.hpp new file mode 100644 index 0000000..d65e770 --- /dev/null +++ b/src/IW4/Assets/StructuredDataDef.hpp @@ -0,0 +1,65 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + struct newEnumEntry + { + char* name; + int statIndexOffset; + }; + + enum enum_type + { + features, + weapons, + attachments, + challenges, + camos, + perks, + killstreaks, + accolades, + cardicons, + cardtitles, + cardnameplates, + teams, + gametypes, + count, + }; + + class IStructuredDataDef : public IAsset + { + private: + StructuredDataEnumEntry* newIndices[enum_type::count]; + int newIndexCount[enum_type::count]; + std::map newEntries[enum_type::count]; + + std::string name_; + StructuredDataDefSet* asset_ = nullptr; + + void add_entry(enum_type type, int stat_index_offset, const std::string& entry_name, ZoneMemory* mem); + void patch_enum_with_map(StructuredDataDefSet* data, enum_type enumIndex, std::map map, ZoneMemory* mem); + void manipulate(StructuredDataDefSet* data, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(StructuredDataDefSet* asset); + }; + } +} diff --git a/src/IW4/Assets/Techset.cpp b/src/IW4/Assets/Techset.cpp new file mode 100644 index 0000000..db02ec2 --- /dev/null +++ b/src/IW4/Assets/Techset.cpp @@ -0,0 +1,593 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include +#include "IW5/Assets/Techset.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + std::unordered_map constant_map = + { + { IW5::CONST_SRC_CODE_LIGHT_POSITION, CONST_SRC_CODE_LIGHT_POSITION }, + { IW5::CONST_SRC_CODE_LIGHT_DIFFUSE, CONST_SRC_CODE_LIGHT_DIFFUSE }, + { IW5::CONST_SRC_CODE_LIGHT_SPECULAR, CONST_SRC_CODE_LIGHT_SPECULAR }, + { IW5::CONST_SRC_CODE_LIGHT_SPOTDIR, CONST_SRC_CODE_LIGHT_SPOTDIR }, + { IW5::CONST_SRC_CODE_LIGHT_SPOTFACTORS, CONST_SRC_CODE_LIGHT_SPOTFACTORS }, + { IW5::CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT, CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT }, + { IW5::CONST_SRC_CODE_PARTICLE_CLOUD_COLOR, CONST_SRC_CODE_PARTICLE_CLOUD_COLOR }, + { IW5::CONST_SRC_CODE_GAMETIME, CONST_SRC_CODE_GAMETIME }, + //{ IW5::CONST_SRC_CODE_EYEOFFSET, CONST_SRC_CODE_EYEOFFSET }, + //{ IW5::CONST_SRC_CODE_COLOR_SATURATION_R, CONST_SRC_CODE_COLOR_SATURATION_R }, + //{ IW5::CONST_SRC_CODE_COLOR_SATURATION_G, CONST_SRC_CODE_COLOR_SATURATION_G }, + //{ IW5::CONST_SRC_CODE_COLOR_SATURATION_B, CONST_SRC_CODE_COLOR_SATURATION_B }, + { IW5::CONST_SRC_CODE_PIXEL_COST_FRACS, CONST_SRC_CODE_PIXEL_COST_FRACS }, + { IW5::CONST_SRC_CODE_PIXEL_COST_DECODE, CONST_SRC_CODE_PIXEL_COST_DECODE }, + { IW5::CONST_SRC_CODE_FILTER_TAP_0, CONST_SRC_CODE_FILTER_TAP_0 }, + { IW5::CONST_SRC_CODE_FILTER_TAP_1, CONST_SRC_CODE_FILTER_TAP_1 }, + { IW5::CONST_SRC_CODE_FILTER_TAP_2, CONST_SRC_CODE_FILTER_TAP_2 }, + { IW5::CONST_SRC_CODE_FILTER_TAP_3, CONST_SRC_CODE_FILTER_TAP_3 }, + { IW5::CONST_SRC_CODE_FILTER_TAP_4, CONST_SRC_CODE_FILTER_TAP_4 }, + { IW5::CONST_SRC_CODE_FILTER_TAP_5, CONST_SRC_CODE_FILTER_TAP_5 }, + { IW5::CONST_SRC_CODE_FILTER_TAP_6, CONST_SRC_CODE_FILTER_TAP_6 }, + { IW5::CONST_SRC_CODE_FILTER_TAP_7, CONST_SRC_CODE_FILTER_TAP_7 }, + { IW5::CONST_SRC_CODE_COLOR_MATRIX_R, CONST_SRC_CODE_COLOR_MATRIX_R }, + { IW5::CONST_SRC_CODE_COLOR_MATRIX_G, CONST_SRC_CODE_COLOR_MATRIX_G }, + { IW5::CONST_SRC_CODE_COLOR_MATRIX_B, CONST_SRC_CODE_COLOR_MATRIX_B }, + { IW5::CONST_SRC_CODE_RENDER_TARGET_SIZE, CONST_SRC_CODE_RENDER_TARGET_SIZE }, + { IW5::CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET, CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET}, + //{ IW5::CONST_SRC_CODE_RENDER_SOURCE_SIZE, CONST_SRC_CODE_RENDER_SOURCE_SIZE }, + { IW5::CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR, CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR }, + { IW5::CONST_SRC_CODE_DOF_EQUATION_SCENE, CONST_SRC_CODE_DOF_EQUATION_SCENE }, + { IW5::CONST_SRC_CODE_DOF_LERP_SCALE, CONST_SRC_CODE_DOF_LERP_SCALE }, + { IW5::CONST_SRC_CODE_DOF_LERP_BIAS, CONST_SRC_CODE_DOF_LERP_BIAS }, + { IW5::CONST_SRC_CODE_DOF_ROW_DELTA, CONST_SRC_CODE_DOF_ROW_DELTA }, + { IW5::CONST_SRC_CODE_MOTION_MATRIX_X, CONST_SRC_CODE_MOTION_MATRIX_X }, + { IW5::CONST_SRC_CODE_MOTION_MATRIX_Y, CONST_SRC_CODE_MOTION_MATRIX_Y }, + { IW5::CONST_SRC_CODE_MOTION_MATRIX_W, CONST_SRC_CODE_MOTION_MATRIX_W }, + { IW5::CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION, CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION }, + { IW5::CONST_SRC_CODE_SHADOWMAP_SCALE, CONST_SRC_CODE_SHADOWMAP_SCALE }, + { IW5::CONST_SRC_CODE_ZNEAR, CONST_SRC_CODE_ZNEAR }, + { IW5::CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE, CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE }, + { IW5::CONST_SRC_CODE_DEBUG_BUMPMAP, CONST_SRC_CODE_DEBUG_BUMPMAP }, + { IW5::CONST_SRC_CODE_MATERIAL_COLOR, CONST_SRC_CODE_MATERIAL_COLOR }, + { IW5::CONST_SRC_CODE_FOG, CONST_SRC_CODE_FOG }, + { IW5::CONST_SRC_CODE_FOG_COLOR_LINEAR, CONST_SRC_CODE_FOG_COLOR_LINEAR }, + { IW5::CONST_SRC_CODE_FOG_COLOR_GAMMA, CONST_SRC_CODE_FOG_COLOR_GAMMA }, + { IW5::CONST_SRC_CODE_FOG_SUN_CONSTS, CONST_SRC_CODE_FOG_SUN_CONSTS }, + { IW5::CONST_SRC_CODE_FOG_SUN_COLOR_LINEAR, CONST_SRC_CODE_FOG_SUN_COLOR_LINEAR }, + { IW5::CONST_SRC_CODE_FOG_SUN_COLOR_GAMMA, CONST_SRC_CODE_FOG_SUN_COLOR_GAMMA }, + { IW5::CONST_SRC_CODE_FOG_SUN_DIR, CONST_SRC_CODE_FOG_SUN_DIR }, + { IW5::CONST_SRC_CODE_GLOW_SETUP, CONST_SRC_CODE_GLOW_SETUP }, + { IW5::CONST_SRC_CODE_GLOW_APPLY, CONST_SRC_CODE_GLOW_APPLY }, + { IW5::CONST_SRC_CODE_COLOR_BIAS, CONST_SRC_CODE_COLOR_BIAS }, + { IW5::CONST_SRC_CODE_COLOR_TINT_BASE, CONST_SRC_CODE_COLOR_TINT_BASE }, + { IW5::CONST_SRC_CODE_COLOR_TINT_DELTA, CONST_SRC_CODE_COLOR_TINT_DELTA }, + { IW5::CONST_SRC_CODE_COLOR_TINT_QUADRATIC_DELTA, CONST_SRC_CODE_COLOR_TINT_QUADRATIC_DELTA }, + { IW5::CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS, CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS }, + { IW5::CONST_SRC_CODE_ENVMAP_PARMS, CONST_SRC_CODE_ENVMAP_PARMS }, + { IW5::CONST_SRC_CODE_SUN_SHADOWMAP_PIXEL_ADJUST, CONST_SRC_CODE_SUN_SHADOWMAP_PIXEL_ADJUST }, + { IW5::CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST, CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST }, + { IW5::CONST_SRC_CODE_COMPOSITE_FX_DISTORTION, CONST_SRC_CODE_COMPOSITE_FX_DISTORTION }, + { IW5::CONST_SRC_CODE_POSTFX_FADE_EFFECT, CONST_SRC_CODE_POSTFX_FADE_EFFECT }, + { IW5::CONST_SRC_CODE_VIEWPORT_DIMENSIONS, CONST_SRC_CODE_VIEWPORT_DIMENSIONS }, + { IW5::CONST_SRC_CODE_FRAMEBUFFER_READ, CONST_SRC_CODE_FRAMEBUFFER_READ }, + //{ IW5::CONST_SRC_CODE_THERMAL_COLOR_OFFSET, CONST_SRC_CODE_THERMAL_COLOR_OFFSET }, + //{ IW5::CONST_SRC_CODE_PLAYLIST_POPULATION_PARAMS, CONST_SRC_CODE_PLAYLIST_POPULATION_PARAMS }, + { IW5::CONST_SRC_CODE_BASE_LIGHTING_COORDS, CONST_SRC_CODE_BASE_LIGHTING_COORDS }, + { IW5::CONST_SRC_CODE_LIGHT_PROBE_AMBIENT, CONST_SRC_CODE_LIGHT_PROBE_AMBIENT }, + { IW5::CONST_SRC_CODE_NEARPLANE_ORG, CONST_SRC_CODE_NEARPLANE_ORG }, + { IW5::CONST_SRC_CODE_NEARPLANE_DX, CONST_SRC_CODE_NEARPLANE_DX }, + { IW5::CONST_SRC_CODE_NEARPLANE_DY, CONST_SRC_CODE_NEARPLANE_DY }, + { IW5::CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE, CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE }, + { IW5::CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET, CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET }, + { IW5::CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX0, CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX0 }, + { IW5::CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX1, CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX1 }, + { IW5::CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX2, CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX2 }, + { IW5::CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR0, CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR0 }, + { IW5::CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR1, CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR1 }, + { IW5::CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR2, CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR2 }, + { IW5::CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM0, CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM0 }, + { IW5::CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM1, CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM1 }, + { IW5::CONST_SRC_CODE_DEPTH_FROM_CLIP, CONST_SRC_CODE_DEPTH_FROM_CLIP }, + { IW5::CONST_SRC_CODE_CODE_MESH_ARG_0, CONST_SRC_CODE_CODE_MESH_ARG_0 }, + { IW5::CONST_SRC_CODE_CODE_MESH_ARG_1, CONST_SRC_CODE_CODE_MESH_ARG_1 }, + { IW5::CONST_SRC_CODE_VIEW_MATRIX, CONST_SRC_CODE_VIEW_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_VIEW_MATRIX, CONST_SRC_CODE_INVERSE_VIEW_MATRIX }, + { IW5::CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX, CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX, CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX }, + { IW5::CONST_SRC_CODE_PROJECTION_MATRIX, CONST_SRC_CODE_PROJECTION_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX, CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX }, + { IW5::CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX, CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX, CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX }, + { IW5::CONST_SRC_CODE_VIEW_PROJECTION_MATRIX, CONST_SRC_CODE_VIEW_PROJECTION_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX, CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX }, + { IW5::CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX, CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX, CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX }, + { IW5::CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX, CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX, CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX }, + { IW5::CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX, CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX, CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX }, + { IW5::CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX, CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX, CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX, CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX }, + { IW5::CONST_SRC_CODE_WORLD_MATRIX0, CONST_SRC_CODE_WORLD_MATRIX0 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_MATRIX0, CONST_SRC_CODE_INVERSE_WORLD_MATRIX0 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0, CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX0, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX0 }, + { IW5::CONST_SRC_CODE_WORLD_VIEW_MATRIX0, CONST_SRC_CODE_WORLD_VIEW_MATRIX0 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX0, CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX0 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX0, CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX0 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX0, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX0 }, + { IW5::CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX0, CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX0 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX0, CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX0 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0, CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 }, + { IW5::CONST_SRC_CODE_WORLD_MATRIX1, CONST_SRC_CODE_WORLD_MATRIX1 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_MATRIX1, CONST_SRC_CODE_INVERSE_WORLD_MATRIX1 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX1, CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX1 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX1, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX1 }, + { IW5::CONST_SRC_CODE_WORLD_VIEW_MATRIX1, CONST_SRC_CODE_WORLD_VIEW_MATRIX1 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX1, CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX1 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX1, CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX1 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX1, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX1 }, + { IW5::CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX1, CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX1 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX1, CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX1 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1, CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1 }, + { IW5::CONST_SRC_CODE_WORLD_MATRIX2, CONST_SRC_CODE_WORLD_MATRIX2 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_MATRIX2, CONST_SRC_CODE_INVERSE_WORLD_MATRIX2 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX2, CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX2 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX2, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX2 }, + { IW5::CONST_SRC_CODE_WORLD_VIEW_MATRIX2, CONST_SRC_CODE_WORLD_VIEW_MATRIX2 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX2, CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX2 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX2, CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX2 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX2, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX2 }, + { IW5::CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX2, CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX2 }, + { IW5::CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX2, CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX2 }, + { IW5::CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2, CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2 }, + { IW5::CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2, CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2 }, + { IW5::CONST_SRC_TOTAL_COUNT, CONST_SRC_TOTAL_COUNT }, + { IW5::CONST_SRC_NONE, CONST_SRC_NONE }, + }; + std::unordered_map constant_map_reversed; + + MaterialTechniqueSet* ITechset::parse(const std::string& name, ZoneMemory* mem) + { + const auto iw5_techset = IW5::ITechset::parse(name, mem); + + if (!iw5_techset) + { + return nullptr; + } + + auto* asset = mem->Alloc(); + memcpy(asset, iw5_techset, 12); + memset(asset->techniques, 0, sizeof asset->techniques); + + // port techniques to the correct index + for (int i = 0; i < 54; i++) + { + auto dest_index = i; + if (i >= (46 + 6)) + { + dest_index -= 6; + } + else if (i >= (40 + 5)) + { + dest_index -= 5; + } + else if (i >= (31 + 4)) + { + dest_index -= 4; + } + else if (i >= (19 + 2)) + { + dest_index -= 2; + } + + if (iw5_techset->techniques[i]) + { + const auto technique_size = sizeof(MaterialTechnique) + (sizeof(MaterialPass) * iw5_techset->techniques[i]->hdr.passCount); + asset->techniques[dest_index] = mem->ManualAlloc(technique_size); + memcpy(asset->techniques[dest_index], iw5_techset->techniques[i], technique_size); + + auto& technique = asset->techniques[dest_index]; + for (short pass = 0; pass < technique->hdr.passCount; pass++) + { + auto* pass_def = &technique->pass[pass]; + + const auto arg_count = pass_def->perObjArgCount + pass_def->perPrimArgCount + pass_def->stableArgCount; + if (arg_count) + { + pass_def->argumentDef = mem->Alloc(arg_count); + memcpy(pass_def->argumentDef, iw5_techset->techniques[i]->pass[pass].argumentDef, sizeof(ShaderArgumentDef) * arg_count); + } + + for (auto arg = 0; arg < arg_count; arg++) + { + auto* arg_def = &pass_def->argumentDef[arg]; + + if (arg_def->type > 2) + { + arg_def->type--; + } + + if (arg_def->type == 3 || arg_def->type == 5) + { + const auto itr = constant_map.find(arg_def->u.codeConst.index); + if (itr != constant_map.end()) + { + arg_def->u.codeConst.index = itr->second; + } + else + { + ZONETOOL_WARNING("code constant %u is not mapped for technique %s!", arg_def->u.codeConst.index, technique->hdr.name); + + if (arg_def->u.codeConst.index >= 66) + { + arg_def->u.codeConst.index -= 8; + } + else if (arg_def->u.codeConst.index >= 37) + { + arg_def->u.codeConst.index -= 6; + } + else if (arg_def->u.codeConst.index >= 26) + { + arg_def->u.codeConst.index -= 5; + } + } + } + } + } + } + } + + return asset; + } + + void ITechset::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(this->name_, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), &this->name()[0]).techset; + + if (DB_IsXAssetDefault(this->type(), this->name().data())) + { + ZONETOOL_FATAL("Techset %s not found.", &name[0]); + } + } + } + + void ITechset::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ITechset::load_depending(IZone* zone) + { + auto* data = this->asset_; + + for (auto technique = 0; technique < 48; technique++) + { + if (data->techniques[technique]) + { + for (auto pass = 0; pass < data->techniques[technique]->hdr.passCount; pass++) + { + auto& techniquePass = data->techniques[technique]->pass[pass]; + + if (techniquePass.vertexDecl) + { + zone->add_asset_of_type(vertexdecl, techniquePass.vertexDecl->name); + } + if (techniquePass.vertexShader) + { + zone->add_asset_of_type(vertexshader, techniquePass.vertexShader->name); + } + if (techniquePass.pixelShader) + { + zone->add_asset_of_type(pixelshader, techniquePass.pixelShader->name); + } + } + } + } + } + + std::string ITechset::name() + { + return this->name_; + } + + std::int32_t ITechset::type() + { + return techset; + } + + void ITechset::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + dest->remappedTechniques = static_cast(zone->get_asset_pointer(techset, this->name())); + + for (std::int32_t technique = 0; technique < 48; technique++) + { + if (!data->techniques[technique]) + { + continue; + } + + buf->align(3); + + auto* technique_header = buf->write(&data->techniques[technique]->hdr); + auto* technique_passes = buf->write(data->techniques[technique]->pass, technique_header->passCount); + + for (std::int32_t pass = 0; pass < technique_header->passCount; pass++) + { + if (technique_passes[pass].vertexDecl) + { + technique_passes[pass].vertexDecl = + reinterpret_cast(zone->get_asset_pointer( + vertexdecl, technique_passes[pass].vertexDecl->name)); + } + + if (technique_passes[pass].vertexShader) + { + technique_passes[pass].vertexShader = + reinterpret_cast(zone->get_asset_pointer( + vertexshader, technique_passes[pass].vertexShader->name)); + } + + if (technique_passes[pass].pixelShader) + { + technique_passes[pass].pixelShader = + reinterpret_cast(zone->get_asset_pointer( + pixelshader, technique_passes[pass].pixelShader->name)); + } + + if (technique_passes[pass].argumentDef) + { + buf->align(3); + auto* dest_args = buf->write(technique_passes[pass].argumentDef, + technique_passes[pass].perPrimArgCount + + technique_passes[pass].perObjArgCount + + technique_passes[pass].stableArgCount); + + for (auto arg = 0; arg < + technique_passes[pass].perPrimArgCount + + technique_passes[pass].perObjArgCount + + technique_passes[pass].stableArgCount; arg++) + { + auto* cur_arg = &technique_passes[pass].argumentDef[arg]; + + if (cur_arg->type == 1 || cur_arg->type == 7) + { + if (dest_args[arg].u.literalConst) + { + dest_args[arg].u.literalConst = buf->write_s(3, dest_args[arg].u.literalConst, 4); + } + } + } + + ZoneBuffer::clear_pointer(&technique_passes[pass].argumentDef); + } + } + + buf->write_str(technique_header->name); + + ZoneBuffer::clear_pointer(&technique_header->name); + ZoneBuffer::clear_pointer(&dest->techniques[technique]); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + char* ITechset::parse_statebits(const std::string& techset, ZoneMemory* mem) + { + auto* iw5_statebits = IW5::ITechset::parse_statebits(techset, mem); + auto* statebits = mem->Alloc(48); + memset(statebits, 0xFF, 48); + + for (int i = 0; i < 54; i++) + { + auto dest_index = i; + if (i >= (46 + 6)) + { + dest_index -= 6; + } + else if (i >= (40 + 5)) + { + dest_index -= 5; + } + else if (i >= (31 + 4)) + { + dest_index -= 4; + } + else if (i >= (19 + 2)) + { + dest_index -= 2; + } + + statebits[dest_index] = iw5_statebits[i]; + } + + return statebits; + } + + void ITechset::dump_statebits(const std::string& techset, char* statebits) + { + char iw5_statebits[54]; + memset(iw5_statebits, 0xFF, sizeof iw5_statebits); + + for (int i = 0; i < 48; i++) + { + auto dest_index = i; + + if (i >= 46) + { + dest_index += 6; + } + else if (i >= 40) + { + dest_index += 5; + } + else if (i >= 31) + { + dest_index += 4; + } + else if (i >= 19) + { + dest_index += 2; + } + + iw5_statebits[dest_index] = statebits[i]; + } + + iw5_statebits[19] = iw5_statebits[17]; + iw5_statebits[20] = iw5_statebits[18]; + iw5_statebits[33] = iw5_statebits[35]; + iw5_statebits[34] = iw5_statebits[36]; + iw5_statebits[44] = iw5_statebits[43]; + + IW5::ITechset::dump_statebits(techset, iw5_statebits); + } + + void ITechset::dump(MaterialTechniqueSet* asset) + { + if (constant_map_reversed.empty()) + { + for (auto& const_map : constant_map) + { + constant_map_reversed[const_map.second] = const_map.first; + } + } + + auto* iw5_techset = new IW5::MaterialTechniqueSet; + memset(iw5_techset, 0, sizeof IW5::MaterialTechniqueSet); + + iw5_techset->name = asset->name; + iw5_techset->pad = asset->pad; + + for (int i = 0; i < 48; i++) + { + auto dest_index = i; + + if (i >= 46) + { + dest_index += 6; + } + else if (i >= 40) + { + dest_index += 5; + } + else if (i >= 31) + { + dest_index += 4; + } + else if (i >= 19) + { + dest_index += 2; + } + + if (asset->techniques[i]) + { + const auto size = sizeof IW5::MaterialTechniqueHeader + (sizeof(MaterialPass) * asset->techniques[i]->hdr.passCount); + iw5_techset->techniques[dest_index] = reinterpret_cast( + new char[size]); + + memcpy(iw5_techset->techniques[dest_index], asset->techniques[i], size); + + auto& technique = iw5_techset->techniques[dest_index]; + for (short pass = 0; pass < technique->hdr.passCount; pass++) + { + const auto pass_def = &technique->pass[pass]; + + const auto arg_count = pass_def->perPrimArgCount + pass_def->perObjArgCount + pass_def->stableArgCount; + if (arg_count > 0) + { + pass_def->argumentDef = new IW5::ShaderArgumentDef[arg_count]; + memcpy(pass_def->argumentDef, asset->techniques[i]->pass[pass].argumentDef, sizeof(IW5::ShaderArgumentDef) * arg_count); + } + + for (auto arg = 0; arg < arg_count; arg++) + { + auto arg_def = &pass_def->argumentDef[arg]; + + if (arg_def->type > 1) + { + arg_def->type++; + } + + if (arg_def->type == 4 || arg_def->type == 6) + { + const auto itr = constant_map_reversed.find(arg_def->u.codeConst.index); + if (itr != constant_map_reversed.end()) + { + arg_def->u.codeConst.index = itr->second; + } + else + { + ZONETOOL_WARNING("code constant %u is not mapped for technique %s!", arg_def->u.codeConst.index, technique->hdr.name); + + if (arg_def->u.codeConst.index >= 58) + { + arg_def->u.codeConst.index += 8; + } + else if (arg_def->u.codeConst.index >= 31) + { + arg_def->u.codeConst.index += 6; + } + else if (arg_def->u.codeConst.index >= 21) + { + arg_def->u.codeConst.index += 5; + } + } + } + } + } + } + } + + // yeet + iw5_techset->techniques[19] = iw5_techset->techniques[17]; + iw5_techset->techniques[20] = iw5_techset->techniques[18]; + iw5_techset->techniques[33] = iw5_techset->techniques[35]; + iw5_techset->techniques[34] = iw5_techset->techniques[36]; + iw5_techset->techniques[44] = iw5_techset->techniques[43]; + + // IW5::ITechset::dump_technique_data(iw5_techset, false); + IW5::ITechset::dump(iw5_techset); + + for (int i = 0; i < 54; i++) + { + if (i == 19 || i == 20 || i == 33 || i == 34 || i == 44) continue; + + if (iw5_techset->techniques[i]) + { + for (short pass = 0; pass < iw5_techset->techniques[i]->hdr.passCount; pass++) + { + delete iw5_techset->techniques[i]->pass[pass].argumentDef; + } + delete iw5_techset->techniques[i]; + } + } + + delete iw5_techset; + } + } +} diff --git a/src/IW4/Assets/Techset.hpp b/src/IW4/Assets/Techset.hpp new file mode 100644 index 0000000..6576177 --- /dev/null +++ b/src/IW4/Assets/Techset.hpp @@ -0,0 +1,39 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class ITechset : public IAsset + { + private: + std::string name_; + MaterialTechniqueSet* asset_ = nullptr; + + public: + static MaterialTechniqueSet* parse(const std::string& name, ZoneMemory* mem); + static char* parse_statebits(const std::string& techset, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + void* pointer() override { return asset_; } + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump_statebits(const std::string& techset, char* statebits); + static void dump(MaterialTechniqueSet* asset); + }; + } +} +#pragma once diff --git a/src/IW4/Assets/TracerDef.cpp b/src/IW4/Assets/TracerDef.cpp new file mode 100644 index 0000000..8a24731 --- /dev/null +++ b/src/IW4/Assets/TracerDef.cpp @@ -0,0 +1,62 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void ITracerDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).tracer; + } + + void ITracerDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ITracerDef::load_depending(IZone* zone) + { + } + + std::string ITracerDef::name() + { + return this->name_; + } + + std::int32_t ITracerDef::type() + { + return tracer; + } + + void ITracerDef::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->material) + { + dest->material = static_cast(zone->get_asset_pointer(material, data->material->name)); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ITracerDef::dump(TracerDef* asset) + { + } + } +} diff --git a/src/IW4/Assets/TracerDef.hpp b/src/IW4/Assets/TracerDef.hpp new file mode 100644 index 0000000..129308b --- /dev/null +++ b/src/IW4/Assets/TracerDef.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class ITracerDef : public IAsset + { + private: + std::string name_; + TracerDef* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(TracerDef* asset); + }; + } +} diff --git a/src/IW4/Assets/VertexDecl.cpp b/src/IW4/Assets/VertexDecl.cpp new file mode 100644 index 0000000..195d908 --- /dev/null +++ b/src/IW4/Assets/VertexDecl.cpp @@ -0,0 +1,74 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/VertexDecl.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + VertexDecl* IVertexDecl::parse(const std::string& name, ZoneMemory* mem, bool preferLocal) + { + return reinterpret_cast(IW5::IVertexDecl::parse(name, mem, preferLocal)); + } + + void IVertexDecl::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).vertexdecl; + + if (DB_IsXAssetDefault(this->type(), this->name().data())) + { + ZONETOOL_FATAL("VertexDecl %s not found.", &name[0]); + } + } + } + + void IVertexDecl::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IVertexDecl::load_depending(IZone* zone) + { + } + + std::string IVertexDecl::name() + { + return this->name_; + } + + std::int32_t IVertexDecl::type() + { + return vertexdecl; + } + + void IVertexDecl::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IVertexDecl::dump(VertexDecl* asset) + { + IW5::IVertexDecl::dump(reinterpret_cast(asset)); + } + } +} diff --git a/src/IW4/Assets/VertexDecl.hpp b/src/IW4/Assets/VertexDecl.hpp new file mode 100644 index 0000000..e29a8f0 --- /dev/null +++ b/src/IW4/Assets/VertexDecl.hpp @@ -0,0 +1,36 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IVertexDecl : public IAsset + { + private: + std::string name_; + VertexDecl* asset_ = nullptr; + + public: + static VertexDecl* parse(const std::string& name, ZoneMemory* mem, + bool preferLocal = false); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(VertexDecl* asset); + }; + } +} diff --git a/src/IW4/Assets/VertexShader.cpp b/src/IW4/Assets/VertexShader.cpp new file mode 100644 index 0000000..af8a015 --- /dev/null +++ b/src/IW4/Assets/VertexShader.cpp @@ -0,0 +1,81 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/VertexShader.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + VertexShader* IVertexShader::parse(const std::string& name, ZoneMemory* mem, bool preferLocal) + { + return reinterpret_cast(IW5::IVertexShader::parse(name, mem, preferLocal)); + } + + void IVertexShader::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).vertexshader; + + if (DB_IsXAssetDefault(this->type(), this->name().data())) + { + ZONETOOL_FATAL("VertexShader %s not found.", &name[0]); + } + } + } + + void IVertexShader::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IVertexShader::load_depending(IZone* zone) + { + } + + std::string IVertexShader::name() + { + return this->name_; + } + + std::int32_t IVertexShader::type() + { + return vertexshader; + } + + void IVertexShader::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->bytecode) + { + buf->align(3); + buf->write(data->bytecode, data->codeLen); + ZoneBuffer::clear_pointer(&dest->bytecode); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IVertexShader::dump(VertexShader* asset) + { + IW5::IVertexShader::dump(reinterpret_cast(asset)); + } + } +} diff --git a/src/IW4/Assets/VertexShader.hpp b/src/IW4/Assets/VertexShader.hpp new file mode 100644 index 0000000..17c292e --- /dev/null +++ b/src/IW4/Assets/VertexShader.hpp @@ -0,0 +1,38 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IVertexShader : public IAsset + { + private: + std::string name_; + VertexShader* asset_ = nullptr; + + + public: + static VertexShader* parse(const std::string& name, ZoneMemory* mem, + bool preferLocal = false); + + void init(const std::string& name, ZoneMemory* mem) override; + + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(VertexShader* asset); + }; + } +} diff --git a/src/IW4/Assets/WeaponDef.cpp b/src/IW4/Assets/WeaponDef.cpp new file mode 100644 index 0000000..259ba9a --- /dev/null +++ b/src/IW4/Assets/WeaponDef.cpp @@ -0,0 +1,85 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/WeaponDef.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void IWeaponDef::dump(WeaponCompleteDef* weapon) + { +#ifdef GENERATE_IW5_WEAPONS + // experimental iw5 dump code + auto* iw5_weapon = new IW5::WeaponCompleteDef; + memset(iw5_weapon, 0, sizeof IW5::WeaponCompleteDef); + + // copy weapon data + memcpy(iw5_weapon, weapon, 16); + memcpy(&iw5_weapon->fAdsZoomFov, &weapon->fAdsZoomFov, 24); + memcpy(&iw5_weapon->dpadIconRatio, &weapon->dpadIconRatio, 28); + memcpy(&iw5_weapon->killIcon, &weapon->killIcon, 12); + iw5_weapon->fireAnimLength = weapon->fireAnimLength; + iw5_weapon->fireAnimLengthAkimbo = iw5_weapon->fireAnimLength; + iw5_weapon->iFirstRaiseTime = weapon->iFirstRaiseTime; + iw5_weapon->iFirstRaiseTimeAkimbo = iw5_weapon->iFirstRaiseTime; + iw5_weapon->iAltRaiseTime = weapon->iAltRaiseTime; + iw5_weapon->iAltRaiseTimeAkimbo = iw5_weapon->iAltRaiseTime; + iw5_weapon->iFireTime = weapon->iFireTime; + iw5_weapon->iFireTimeAkimbo = iw5_weapon->iFireTime; + memcpy(&iw5_weapon->ammoDropStockMax, &weapon->killIcon, 28); + + // copy over xanims + iw5_weapon->szXAnims = new const char*[42]; + memset(iw5_weapon->szXAnims, 0, sizeof(const char*) * 42); + memcpy(iw5_weapon->szXAnims, weapon->szXAnims, sizeof(const char*) * 37); + + // alloc weapondef + iw5_weapon->weapDef = new IW5::WeaponDef; + memset(iw5_weapon->weapDef, 0, sizeof IW5::WeaponDef); + +#define CALC_SIZE(__type__, __start_field__, __end_field__) \ + ((std::size_t(&(((__type__*)nullptr)->__end_field__)) - std::size_t(&(((__type__*)nullptr)->__start_field__))) + sizeof(weapon->weapDef->__end_field__)) + +#define COPY_STRUCT_AREA(__type__, __start_field__, __end_field__) \ + static_assert(CALC_SIZE(IW5::WeaponDef, __start_field__, __end_field__) == CALC_SIZE(IW4::WeaponDef, __start_field__, __end_field__)); \ + memcpy(&iw5_weapon->weapDef->__start_field__, &weapon->weapDef->__start_field__, CALC_SIZE(__type__, __start_field__, __end_field__)); + + // copy weapondef data + COPY_STRUCT_AREA(WeaponDef, gunXModel, scanSound); + COPY_STRUCT_AREA(WeaponDef, viewShellEjectEffect, iDamageType); + COPY_STRUCT_AREA(WeaponDef, autoAimRange, fAdsZoomOutFrac); + COPY_STRUCT_AREA(WeaponDef, fAdsBobFactor, fGunMaxYaw); + COPY_STRUCT_AREA(WeaponDef, swayMaxAngle, ricochetChance); + COPY_STRUCT_AREA(WeaponDef, parallelBounce, tracerType); + COPY_STRUCT_AREA(WeaponDef, turretScopeZoomRate, requireLockonToFire); + COPY_STRUCT_AREA(WeaponDef, bigExplosion, aimDownSight); + COPY_STRUCT_AREA(WeaponDef, bRechamberWhileAds, stickToPlayers); + COPY_STRUCT_AREA(WeaponDef, hasDetonator, offhandHoldIsCancelable); + + iw5_weapon->weapDef->bounceSound = reinterpret_cast(weapon->weapDef->bounceSound); + iw5_weapon->weapDef->stowTag = SL_AllocString("tag_stowed_back"); + + // fixup weapon name + iw5_weapon->szInternalName = _strdup(va("iw5_%s", weapon->szInternalName).data()); + iw5_weapon->weapDef->szOverlayName = _strdup(va("iw5_%s", weapon->szInternalName).data()); + + // dump iw5 weapon file + IW5::IWeaponDef::dump(iw5_weapon, SL_ConvertToString); + + // free memory + free((void*)iw5_weapon->szInternalName); + free((void*)iw5_weapon->weapDef->szOverlayName); + delete[] iw5_weapon->weapDef; + delete[] iw5_weapon->szXAnims; + delete iw5_weapon; +#endif + } + } +} diff --git a/src/IW4/Assets/WeaponDef.hpp b/src/IW4/Assets/WeaponDef.hpp new file mode 100644 index 0000000..6b5e0ab --- /dev/null +++ b/src/IW4/Assets/WeaponDef.hpp @@ -0,0 +1,47 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IWeaponDef : public IAsset + { + private: + std::string name_; + WeaponCompleteDef* asset_ = nullptr; + + public: + IWeaponDef(); + ~IWeaponDef(); + + WeaponCompleteDef* parse_weapondef(Json& data, WeaponCompleteDef* baseAsset, + ZoneMemory* mem); + + WeaponCompleteDef* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + + void load_depending_WeaponDef(IZone* zone, WeaponCompleteDef* data); + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + void write_WeaponDef(IZone* zone, ZoneBuffer* buf, WeaponCompleteDef* complete, + WeaponCompleteDef* data); + + static Json dump_weapondef(WeaponCompleteDef* asset); + static Json dump_complete(WeaponCompleteDef* asset); + static void dump(WeaponCompleteDef* asset); + }; + } +} diff --git a/src/IW4/Assets/XAnimParts.cpp b/src/IW4/Assets/XAnimParts.cpp new file mode 100644 index 0000000..9b4fae2 --- /dev/null +++ b/src/IW4/Assets/XAnimParts.cpp @@ -0,0 +1,272 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "../IW5/Assets/XAnimParts.hpp" + +namespace ZoneTool::IW4 +{ + void IXAnimParts::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = reinterpret_cast(IW5::IXAnimParts::parse(name, mem, SL_AllocString)); // + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).xanimparts; + } + } + + void IXAnimParts::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + // fixup scriptstrings + auto xanim = mem->Alloc(); + memcpy(xanim, this->asset_, sizeof XAnimParts); + + // allocate tagnames + if (this->asset_->tagnames) + { + xanim->tagnames = mem->Alloc(xanim->boneCount[9]); + memcpy(xanim->tagnames, this->asset_->tagnames, sizeof(unsigned short) * xanim->boneCount[9]); + + for (auto i = 0; i < xanim->boneCount[9]; i++) + { + if (xanim->tagnames[i]) + { + const std::string bone = SL_ConvertToString(this->asset_->tagnames[i]); + xanim->tagnames[i] = buf->write_scriptstring(bone); + } + } + } + + // allocate notetracks + xanim->notify = mem->Alloc(xanim->notifyCount); + memcpy(xanim->notify, this->asset_->notify, sizeof XAnimNotifyInfo * xanim->notifyCount); + + // touch XAnimNotifyInfo, realloc tagnames + for (auto i = 0; i < xanim->notifyCount; i++) + { + const std::string bone = SL_ConvertToString(this->asset_->notify[i].name); + xanim->notify[i].name = buf->write_scriptstring(bone); + } + + this->asset_ = xanim; + } + + void IXAnimParts::load_depending(IZone* zone) + { + } + + std::string IXAnimParts::name() + { + return this->name_; + } + + std::int32_t IXAnimParts::type() + { + return xanim; + } + + void IXAnimParts::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->tagnames) // tagnames + { + buf->align(1); + buf->write_stream(data->tagnames, sizeof(short), data->boneCount[9]); + dest->tagnames = reinterpret_cast(-1); + } + if (data->notify) // notetracks + { + buf->align(3); + buf->write_stream(data->notify, sizeof(XAnimNotifyInfo), data->notifyCount); + dest->tagnames = reinterpret_cast(-1); + } + + if (data->delta) // XAnimDeltaParts + { + buf->align(3); + auto* partdata = data->delta; + auto* partdest = reinterpret_cast(buf->at()); + buf->write_stream(partdata, sizeof(XAnimDeltaPart), 1); + + if (partdata->trans) + { + buf->align(3); + buf->write_stream(partdata->trans, 4, 1); // not full struct + if (partdata->trans->size) + { + buf->write_stream(&partdata->trans->u, 0x1C, 1); // not full struct + if (data->framecount > 0x100) + buf->write_stream(&partdata->trans->u.frames.indices, + sizeof(short), partdata->trans->size + 1); + else + buf->write_stream(&partdata->trans->u.frames.indices, sizeof(char), + partdata->trans->size + 1); + + if (partdata->trans->u.frames.frames._1) + { + if (partdata->trans->smallTrans) + { + buf->align(0); + buf->write_stream(partdata->trans->u.frames.frames._1, sizeof(char) * 3, + partdata->trans->size + 1); + } + else + { + buf->align(3); + buf->write_stream(partdata->trans->u.frames.frames._2, sizeof(short) * 3, + partdata->trans->size + 1); + } + } + } + else buf->write_stream(partdata->trans->u.frame0, sizeof(float), 3); + partdest->trans = reinterpret_cast(-1); + } + + if (partdata->quat2) + { + ZONETOOL_INFO("Write - Delta:Quat2"); + buf->align(3); + buf->write_stream(partdata->quat2, 4, 1); // not full struct + if (partdata->quat2->size) + { + buf->write_stream(&partdata->quat2->u, 0x4, 1); // not full struct + if (data->framecount > 255) + { + buf->write_stream(&partdata->quat2->u.frames.indices, + sizeof(short), partdata->quat2->size + 1); + } + else + { + buf->write_stream(&partdata->quat2->u.frames.indices, sizeof(char), + partdata->quat2->size + 1); + } + + if (partdata->quat2->u.frames.frames) + { + buf->align(3); + buf->write_stream(partdata->quat2->u.frames.frames, sizeof(short) * 2, + partdata->quat2->size + 1); + } + } + else + { + buf->write_stream(partdata->quat2->u.frame0, sizeof(short) * 2, 1); + } + partdest->quat2 = reinterpret_cast(-1); + } + + if (partdata->quat) + { + buf->align(3); + buf->write_stream(partdata->quat, 4, 1); + + if (partdata->quat->size) + { + buf->write_stream(&partdata->quat->u, 4, 1); // not full struct + + if (data->framecount > 255) + { + buf->write_stream(&partdata->quat->u.frames.indices, + sizeof(short), partdata->quat->size + 1); + } + else + { + buf->write_stream(&partdata->quat->u.frames.indices, sizeof(char), + partdata->quat->size + 1); + } + + if (partdata->quat->u.frames.frames) + { + buf->align(3); + buf->write_stream(partdata->quat->u.frames.frames, sizeof(short) * 4, + partdata->quat->size + 1); + } + } + else + { + buf->write_stream(partdata->quat->u.frame0, sizeof(short) * 4, 1); + } + + partdest->quat = reinterpret_cast(-1); + } + + dest->delta = reinterpret_cast(-1); + } + + if (data->dataByte) // dataByte + { + buf->align(0); + buf->write_stream(data->dataByte, sizeof(char), data->dataByteCount); + dest->dataByte = reinterpret_cast(-1); + } + if (data->dataShort) // dataShort + { + buf->align(1); + buf->write_stream(data->dataShort, sizeof(short), data->dataShortCount); + dest->dataShort = reinterpret_cast(-1); + } + if (data->dataInt) // dataInt + { + buf->align(3); + buf->write_stream(data->dataInt, sizeof(int), data->dataIntCount); + dest->dataInt = reinterpret_cast(-1); + } + if (data->randomDataShort) // randomDataShort + { + buf->align(1); + buf->write_stream(data->randomDataShort, sizeof(short), data->randomDataShortCount); + dest->randomDataShort = reinterpret_cast(-1); + } + if (data->randomDataByte) // randomDataByte + { + buf->align(0); + buf->write_stream(data->randomDataByte, sizeof(char), data->randomDataByteCount); + dest->randomDataByte = reinterpret_cast(-1); + } + if (data->randomDataInt) // randomDataInt + { + buf->align(3); + buf->write_stream(data->randomDataInt, sizeof(int), data->randomDataIntCount); + dest->randomDataInt = reinterpret_cast(-1); + } + + // XAnim indice data + if (data->indices.data) + { + if (data->framecount > 255) + { + buf->align(1); + buf->write_stream(data->indices.data, data->indexcount * 2, 1); + } + else + { + buf->align(0); + buf->write_stream(data->indices.data, data->indexcount, 1); + } + + dest->indices.data = reinterpret_cast(-1); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IXAnimParts::dump(XAnimParts* asset, const std::function& convertToString) + { + IW5::IXAnimParts::dump(reinterpret_cast(asset), convertToString); + } +} diff --git a/src/IW4/Assets/XAnimParts.hpp b/src/IW4/Assets/XAnimParts.hpp new file mode 100644 index 0000000..c6c7b18 --- /dev/null +++ b/src/IW4/Assets/XAnimParts.hpp @@ -0,0 +1,36 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + const char* SL_ConvertToString(std::uint16_t index); + + class IXAnimParts : public IAsset + { + private: + std::string name_; + XAnimParts* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(XAnimParts* asset, + const std::function& convertToString = SL_ConvertToString); + }; + } +} diff --git a/src/IW4/Assets/XSurface.cpp b/src/IW4/Assets/XSurface.cpp new file mode 100644 index 0000000..b767f84 --- /dev/null +++ b/src/IW4/Assets/XSurface.cpp @@ -0,0 +1,287 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + XModelSurfs* IXSurface::parse(const std::string& name, ZoneMemory* mem) + { + AssetReader read(mem); + + if (!read.open("XSurface\\" + name + ".xse")) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing xmodel surface \"%s\"...", name.c_str()); + + auto asset = read.read_array(); + asset->name = read.read_string(); + + asset->surfs = mem->Alloc(asset->numsurfs); + + for (int i = 0; i < asset->numsurfs; i++) + { + asset->surfs[i].tileMode = read.read_int(); + asset->surfs[i].deformed = read.read_int(); + asset->surfs[i].baseTriIndex = read.read_int(); + asset->surfs[i].baseVertIndex = read.read_int(); + + for (int j = 0; j < 6; j++) + { + asset->surfs[i].partBits[j] = read.read_int(); + } + + // vertex + auto vertexInfo = read.read_array(); + memcpy(&asset->surfs[i].vertexInfo, vertexInfo, sizeof XSurfaceVertexInfo); + asset->surfs[i].vertexInfo.vertsBlend = read.read_array(); + + // verticies + asset->surfs[i].vertCount = read.read_int(); + asset->surfs[i].verticies = read.read_array(); + + // triangles + asset->surfs[i].triCount = read.read_int(); + asset->surfs[i].triIndices = read.read_array(); + + // rigidVertLists + asset->surfs[i].vertListCount = read.read_int(); + asset->surfs[i].rigidVertLists = read.read_array(); + for (int vert = 0; vert < asset->surfs[i].vertListCount; vert++) + { + if (asset->surfs[i].rigidVertLists[vert].collisionTree) + { + asset->surfs[i].rigidVertLists[vert].collisionTree = read.read_array(); + + if (asset->surfs[i].rigidVertLists[vert].collisionTree->leafs) + { + asset->surfs[i].rigidVertLists[vert].collisionTree->leafs = read.read_array< + XSurfaceCollisionLeaf>(); + } + + if (asset->surfs[i].rigidVertLists[vert].collisionTree->nodes) + { + asset->surfs[i].rigidVertLists[vert].collisionTree->nodes = read.read_array< + XSurfaceCollisionNode>(); + } + } + else + { + asset->surfs[i].rigidVertLists[vert].collisionTree = nullptr; + } + } + } + + read.close(); + + return asset; + } + + void IXSurface::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data()).xsurface; + } + } + + void IXSurface::init(void* asset, ZoneMemory* mem) + { + this->asset_ = reinterpret_cast(asset); + this->name_ = this->asset_->name; + } + + void IXSurface::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IXSurface::load_depending(IZone* zone) + { + } + + std::string IXSurface::name() + { + return this->name_; + } + + std::int32_t IXSurface::type() + { + return xmodelsurfs; + } + + void IXSurface::write_xsurfaces(IZone* zone, ZoneBuffer* buf, XSurface* data, std::uint16_t count) + { + assert(sizeof XSurface, 64); + assert(sizeof Face, 6); + assert(sizeof XRigidVertList, 12); + assert(sizeof XSurfaceCollisionTree, 40); + assert(sizeof XSurfaceCollisionLeaf, 2); + assert(sizeof XSurfaceCollisionAabb, 12); + assert(sizeof XSurfaceCollisionNode, 16); + assert(sizeof GfxPackedVertex, 32); + assert(sizeof XSurfaceVertexInfo, 12); + + auto* dest = buf->write(data, count); + + for (auto surf = 0u; surf < count; surf++) + { + if (data[surf].vertexInfo.vertsBlend) + { + dest[surf].vertexInfo.vertsBlend = buf->write_s(1, data[surf].vertexInfo.vertsBlend, + data[surf].vertexInfo.vertCount[0] + + (data[surf].vertexInfo.vertCount[1] * 3) + (data[ + surf].vertexInfo.vertCount[2] * 5) + (data[ + surf].vertexInfo.vertCount[3] * 7)); + } + + buf->push_stream(6); + if (data[surf].verticies) + { + dest[surf].verticies = buf->write_s(15, data[surf].verticies, data[surf].vertCount); + } + buf->pop_stream(); + + if (data[surf].rigidVertLists) + { + XRigidVertList* ct = nullptr; + dest[surf].rigidVertLists = buf->write_s(3, data[surf].rigidVertLists, data[surf].vertListCount, + sizeof XRigidVertList, &ct); + + if (dest[surf].rigidVertLists == reinterpret_cast(-1)) + { + for (auto k = 0; k < data[surf].vertListCount; k++) + { + if (ct[k].collisionTree) + { + XSurfaceCollisionTree* entry = nullptr; + ct[k].collisionTree = buf->write_s(3, ct[k].collisionTree, 1, + sizeof XSurfaceCollisionTree, &entry); + + if (ct[k].collisionTree == reinterpret_cast(-1)) + { + if (entry->nodes) + { + entry->nodes = buf->write_s(15, entry->nodes, entry->nodeCount); + } + + if (entry->leafs) + { + entry->leafs = buf->write_s(1, entry->leafs, entry->leafCount); + } + } + } + } + } + } + + buf->push_stream(7); + if (data[surf].triIndices) + { + dest[surf].triIndices = buf->write_s(15, data[surf].triIndices, data[surf].triCount); + } + buf->pop_stream(); + } + } + + void IXSurface::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + assert(sizeof XModelSurfs, 36); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->surfs) + { + buf->align(3); + write_xsurfaces(zone, buf, data->surfs, data->numsurfs); + ZoneBuffer::clear_pointer(&dest->surfs); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IXSurface::dump(XModelSurfs* asset) + { + AssetDumper dump; + dump.open("XSurface\\"s + asset->name + ".xse"); + + if (asset->name == "tntbomb_mp_low10"s) + { + return; + } + + dump.dump_array(asset, 1); + dump.dump_string(asset->name); + + for (auto i = 0u; i < asset->numsurfs; i++) + { + dump.dump_int(asset->surfs[i].tileMode); + dump.dump_int(asset->surfs[i].deformed); + dump.dump_int(asset->surfs[i].baseTriIndex); + dump.dump_int(asset->surfs[i].baseVertIndex); + + for (auto j = 0; j < 6; j++) + { + dump.dump_int(asset->surfs[i].partBits[j]); + } + + // vertex bs + dump.dump_array(&asset->surfs[i].vertexInfo, 1); + dump.dump_array(asset->surfs[i].vertexInfo.vertsBlend, + asset->surfs[i].vertexInfo.vertCount[0] + + (asset->surfs[i].vertexInfo.vertCount[1] * 3) + + (asset->surfs[i].vertexInfo.vertCount[2] * 5) + + (asset->surfs[i].vertexInfo.vertCount[3] * 7) + ); + + dump.dump_int(asset->surfs[i].vertCount); + dump.dump_array(asset->surfs[i].verticies, asset->surfs[i].vertCount); + + dump.dump_int(asset->surfs[i].triCount); + dump.dump_array(asset->surfs[i].triIndices, asset->surfs[i].triCount); + + dump.dump_int(asset->surfs[i].vertListCount); + dump.dump_array(asset->surfs[i].rigidVertLists, asset->surfs[i].vertListCount); + for (auto vert = 0; vert < asset->surfs[i].vertListCount; vert++) + { + if (asset->surfs[i].rigidVertLists[vert].collisionTree) + { + dump.dump_array(asset->surfs[i].rigidVertLists[vert].collisionTree, 1); + + if (asset->surfs[i].rigidVertLists[vert].collisionTree->leafs) + { + dump.dump_array(asset->surfs[i].rigidVertLists[vert].collisionTree->leafs, + asset->surfs[i].rigidVertLists[vert].collisionTree->leafCount); + } + + if (asset->surfs[i].rigidVertLists[vert].collisionTree->nodes) + { + dump.dump_array(asset->surfs[i].rigidVertLists[vert].collisionTree->nodes, + asset->surfs[i].rigidVertLists[vert].collisionTree->nodeCount); + } + } + } + } + + dump.close(); + } + } +} diff --git a/src/IW4/Assets/XSurface.hpp b/src/IW4/Assets/XSurface.hpp new file mode 100644 index 0000000..82797f0 --- /dev/null +++ b/src/IW4/Assets/XSurface.hpp @@ -0,0 +1,39 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IXSurface : public IAsset + { + private: + std::string name_; + XModelSurfs* asset_ = nullptr; + + public: + XModelSurfs* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void init(void* asset, ZoneMemory* mem) override; + + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + void* pointer() override { return asset_; } + std::string name() override; + std::int32_t type() override; + void write_xsurfaces(IZone* zone, ZoneBuffer* buf, XSurface* data, std::uint16_t count); + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(XModelSurfs* asset); + }; + } +} diff --git a/src/IW4/Assets/Xmodel.cpp b/src/IW4/Assets/Xmodel.cpp new file mode 100644 index 0000000..0bf736d --- /dev/null +++ b/src/IW4/Assets/Xmodel.cpp @@ -0,0 +1,537 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW5/Assets/XModel.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + XModel* IXModel::parse(std::string name, ZoneMemory* mem) + { + return reinterpret_cast(IW5::IXModel::parse(name, mem, SL_AllocString)); + } + + void IXModel::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data()).xmodel; + } + } + + void IXModel::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + // fixup scriptstrings + auto xmodel = mem->Alloc(); + memcpy(xmodel, this->asset_, sizeof XModel); + + // allocate bonenames + if (this->asset_->boneNames) + { + xmodel->boneNames = mem->Alloc(xmodel->numBones); + memcpy(xmodel->boneNames, this->asset_->boneNames, sizeof(short) * xmodel->numBones); + + for (int i = 0; i < xmodel->numBones; i++) + { + if (xmodel->boneNames[i]) + { + std::string bone = SL_ConvertToString(this->asset_->boneNames[i]); + xmodel->boneNames[i] = buf->write_scriptstring(bone); + } + } + } + + this->asset_ = xmodel; + } + + void IXModel::load_depending(IZone* zone) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW4::IXModel::load_depending"); +#endif + + auto data = this->asset_; + + // Materials + for (std::int32_t i = 0; i < data->numSurfaces; i++) + { + if (data->materials[i]) + { + zone->add_asset_of_type(material, data->materials[i]->name); + } + } + + // XSurfaces + for (std::int32_t i = 0; i < 4; i++) + { + if (data->lods[i].surfaces) + { + zone->add_asset_of_type(xmodelsurfs, data->lods[i].surfaces->name); + } + } + + // PhysCollmap + if (data->physCollmap) + { + zone->add_asset_of_type(phys_collmap, data->physCollmap->name); + } + + // PhysPreset + if (data->physPreset) + { + zone->add_asset_of_type(physpreset, data->physPreset->name); + } + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + std::string IXModel::name() + { + return this->name_; + } + + std::int32_t IXModel::type() + { + return xmodel; + } + + void IXModel::write(IZone* zone, ZoneBuffer* buf) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW4::IXModel::write"); +#endif + + auto data = this->asset_; + auto dest = buf->write(data); + + assert(sizeof XModel, 304); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->boneNames) + { + buf->align(1); + buf->write(data->boneNames, data->numBones); + ZoneBuffer::clear_pointer(&dest->boneNames); + } + + if (data->parentList) + { + buf->align(0); + buf->write(data->parentList, data->numBones - data->numRootBones); + ZoneBuffer::clear_pointer(&dest->parentList); + } + + if (data->tagAngles) + { + buf->align(1); + buf->write(data->tagAngles, data->numBones - data->numRootBones); + ZoneBuffer::clear_pointer(&dest->tagAngles); + } + + if (data->tagPositions) + { + buf->align(3); + buf->write(data->tagPositions, data->numBones - data->numRootBones); + ZoneBuffer::clear_pointer(&dest->tagPositions); + } + + if (data->partClassification) + { + buf->align(0); + buf->write(data->partClassification, data->numBones); + ZoneBuffer::clear_pointer(&dest->partClassification); + } + + if (data->animMatrix) + { + buf->align(3); + buf->write(data->animMatrix, data->numBones); + ZoneBuffer::clear_pointer(&dest->animMatrix); + } + + if (data->materials) + { + buf->align(3); + + auto destMaterials = buf->write(data->materials, data->numSurfaces); + for (int i = 0; i < data->numSurfaces; i++) + { + destMaterials[i] = reinterpret_cast(zone->get_asset_pointer( + material, data->materials[i]->name)); + } + + ZoneBuffer::clear_pointer(&dest->materials); + } + + for (int i = 0; i < 4; i++) + { + if (!data->lods[i].surfaces) continue; + dest->lods[i].surfaces = reinterpret_cast(zone->get_asset_pointer( + xmodelsurfs, data->lods[i].surfaces->name)); + + //if (data->lods[i].surfaces && dependingSurfaces[i]) + //{ + // buf->push_stream(0); + // buf->align(3); + // dependingSurfaces[i]->write(zone, buf); + // buf->pop_stream(); + + // //ZoneBuffer::clear_pointer(&dest->lods[i].surfaces); + // //delete[] dependingSurfaces[i]; + + // dest->lods[i].numSurfacesInLod = data->lods[i].surfaces->xSurficiesCount; + // dest->lods[i].surfaces = (ModelSurface*)-1; + //} + //else + //{ + // dest->lods[i].surfaces = nullptr; + //} + } + + if (data->colSurf) + { + buf->align(3); + auto destSurf = buf->write(data->colSurf, data->numColSurfs); + + for (int i = 0; i < data->numColSurfs; i++) + { + destSurf[i].tris = buf->write_s(3, data->colSurf[i].tris, data->colSurf[i].numCollTris); + } + + ZoneBuffer::clear_pointer(&dest->colSurf); + } + + if (data->boneInfo) + { + buf->align(3); + buf->write(data->boneInfo, data->numBones); + ZoneBuffer::clear_pointer(&dest->boneInfo); + } + + if (data->physCollmap) + { + dest->physCollmap = reinterpret_cast(zone->get_asset_pointer( + phys_collmap, data->physCollmap->name)); + } + + if (data->physPreset) + { + dest->physPreset = reinterpret_cast(zone->get_asset_pointer( + physpreset, data->physPreset->name)); + } + + END_LOG_STREAM; + buf->pop_stream(); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + +#ifdef GENERATE_IW5_MODELS + bool starts_with(const std::string& input, const std::string& str) + { + return (input.size() >= str.size() && input.substr(0, str.size()) == str); + } + + std::vector attachment_strings + { + "red_dot", + "thermal", + "red_cross", + "acog", + "eotech", + "suppressor", + "reflex", + "foregrip", + "motion", + }; + + bool is_attachment_material(const std::string& material) + { + for (auto& att : attachment_strings) + { + if (material.find(att) != std::string::npos) + { + return true; + } + } + + return false; + } + + std::vector iw5_camos = + { + "autumn", + "blue", + "choco", + "classic", + "d_urban", + "gold", + "hex", + "marine", + "multi", + "red", + "snake", + "snow", + "winter" + }; + + XModel* IXModel::generate_iw5_camo_model(const std::string& camo, XModel* asset) + { + // allocate new model + auto* model = remove_attachments(asset); + + // patch xmodel name + const auto model_name = std::string(asset->name).substr(0, strlen(asset->name) - 10).append(camo).append("_no_attach"); + model->name = _strdup(model_name.data()); + + // patch materials + Material* patched_material = nullptr; + for (auto i = 0u; i < model->numSurfaces; i++) + { + if (!model->materials[i]) + { + continue; + } + + if (strstr(model->materials[i]->name, "blue_tiger")) + { + if (patched_material == nullptr) + { + patched_material = new Material; + memcpy(patched_material, model->materials[i], sizeof Material); + + const auto material_name = std::string(patched_material->name).substr(0, strlen(patched_material->name) - 10).append(camo); + patched_material->name = _strdup(material_name.data()); + + assert(patched_material->maps[patched_material->numMaps - 1].firstCharacter == 'd'); + + if (camo == "gold"s) + { + patched_material->numMaps -= 1; + patched_material->techniqueSet = new MaterialTechniqueSet; + memcpy(patched_material->techniqueSet, model->materials[i]->techniqueSet, sizeof MaterialTechniqueSet); + + auto technique_name = std::string(patched_material->techniqueSet->name); + const auto detail_pos = technique_name.find_first_of("d0"); + technique_name.replace(detail_pos + 2, 2, ""); + + patched_material->techniqueSet->name = _strdup(technique_name.data()); + } + + patched_material->maps = new MaterialImage[patched_material->numMaps]; + memcpy(patched_material->maps, model->materials[i]->maps, sizeof MaterialImage * patched_material->numMaps); + + for (auto map = 0; map < patched_material->numMaps; map++) + { + if (camo == "gold") + { + if (patched_material->maps[map].firstCharacter == 'c') + { + patched_material->maps[map].image = new GfxImage; + memcpy(patched_material->maps[map].image, model->materials[i]->maps[map].image, sizeof GfxImage); + patched_material->maps[map].image->name = "detail_gold_col"; + } + else if (patched_material->maps[map].firstCharacter == 's') + { + patched_material->maps[map].image = new GfxImage; + memcpy(patched_material->maps[map].image, model->materials[i]->maps[map].image, sizeof GfxImage); + patched_material->maps[map].image->name = "~detail_gold_spc-r-42g-42b-42~cf35b1a3"; + } + } + else if (patched_material->maps[map].firstCharacter == 'd' && camo != "gold") + { + patched_material->maps[map].image = new GfxImage; + memcpy(patched_material->maps[map].image, model->materials[i]->maps[map].image, sizeof GfxImage); + patched_material->maps[map].image->name = _strdup(&va("weapon_camo_%s", camo.data())[0]); + } + } + + IMaterial::dump(patched_material); + } + + model->materials[i] = patched_material; + } + } + + return model; + } + + XModel* IXModel::remove_attachments(XModel* asset) + { + // allocate new model + auto* model = new XModel; + memcpy(model, asset, sizeof XModel); + + // + std::vector new_materials{}; + std::vector> new_surfaces{}; + + // remove all materials that we don't need + for (auto i = 0u; i < model->numSurfaces; i++) + { + if (model->materials && model->materials[i]) + { + auto material_name = static_cast(model->materials[i]->name); + + // if the material does not belong to an attachment, add it back to the xmodel + if (!is_attachment_material(material_name)) + { + // add the material + new_materials.push_back(model->materials[i]); + + // add the surface too + for (auto lod = 0; lod < 4; lod++) + { + // check if the surface belongs to the current lod + if (i >= model->lods[lod].surfIndex && i < model->lods[lod].surfIndex + model->lods[lod]. + numSurfacesInLod) + { + // add surface to vector + new_surfaces.emplace_back( + lod, model->lods[lod].surfaces->surfs[i - model->lods[lod].surfIndex]); + } + } + } + } + } + + // make sure everything went according to plan + assert(new_materials.size() == new_surfaces.size()); + + // rebuild material array + model->materials = new Material*[new_materials.size()]; + memcpy(model->materials, new_materials.data(), sizeof(Material*) * new_materials.size()); + + // rebuild lods + auto surface_start_index = 0u; + for (auto lod = 0; lod < 4; lod++) + { + if (!model->lods[lod].surfaces) + { + continue; + } + + std::vector model_surfaces_for_lod{}; + + // loop through every surface that should be re-added to our custom model + for (auto& surface : new_surfaces) + { + // if the lod matches, add it to the queue + if (lod == surface.first) + { + model_surfaces_for_lod.push_back(surface.second); + } + } + + // fix current lod + model->lods[lod].numSurfacesInLod = model_surfaces_for_lod.size(); + model->lods[lod].surfIndex = surface_start_index; + + // alloc new XModelSurfs asset + auto* new_model_surface = new XModelSurfs; + memcpy(new_model_surface, model->lods[lod].surfaces, sizeof XModelSurfs); + + new_model_surface->name = _strdup(va("%s_no_attach", new_model_surface->name).data()); + new_model_surface->numsurfs = model_surfaces_for_lod.size(); + + new_model_surface->surfs = new XSurface[model_surfaces_for_lod.size()]; + memcpy(new_model_surface->surfs, model_surfaces_for_lod.data(), + sizeof(XSurface) * model_surfaces_for_lod.size()); + + // patch surfaces ptr + model->lods[lod].surfaces = new_model_surface; + + // increment surface_start_index for next lod + surface_start_index += model_surfaces_for_lod.size(); + + // dump lod + IXSurface::dump(model->lods[lod].surfaces); + } + + // some logging + ZONETOOL_INFO("%u out of %u surfaces have been removed from model %s.", asset->numSurfaces - new_materials. +size(), asset->numSurfaces, asset->name); + + // fix xmodel settings + model->numSurfaces = new_materials.size(); + if (model->numSurfaces == 0) + { + return nullptr; + } + + // swap xmodel name + model->name = _strdup(va("%s_no_attach", asset->name).data()); + + // return asset + return model; + } +#endif + + void IXModel::dump(XModel* asset, const std::function& convertToString) + { + const auto name = static_cast(asset->name); + +#ifdef GENERATE_IW5_MODELS + // generate attachment-less xmodels + if (starts_with(name, "viewmodel_") || starts_with(name, "weapon_")) + { + if (name.find("blue_tiger") != std::string::npos) + { + for (auto& iw5_camo : iw5_camos) + { + auto* new_model = generate_iw5_camo_model(iw5_camo, asset); + + auto* iw5_model = new IW5::XModel; + memcpy(iw5_model, new_model, sizeof XModel); + iw5_model->flags = 0; + + IW5::IXModel::dump(iw5_model, convertToString); + + delete iw5_model; + } + } + + auto* new_model = remove_attachments(asset); + + if (new_model) + { + auto* iw5_model = new IW5::XModel; + memcpy(iw5_model, new_model, sizeof XModel); + iw5_model->flags = 0; + + IW5::IXModel::dump(iw5_model, convertToString); + + delete iw5_model; + } + } +#endif + + auto* iw5_model = new IW5::XModel; + memcpy(iw5_model, asset, sizeof XModel); + iw5_model->unk = 0; + + // dump regular xmodel + IW5::IXModel::dump(iw5_model, convertToString); + + delete iw5_model; + } + } +} diff --git a/src/IW4/Assets/Xmodel.hpp b/src/IW4/Assets/Xmodel.hpp new file mode 100644 index 0000000..94da6ab --- /dev/null +++ b/src/IW4/Assets/Xmodel.hpp @@ -0,0 +1,44 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + static const char* SL_ConvertToString(std::uint16_t index); + + class IXSurface; + + class IXModel : public IAsset + { + private: + std::string name_; + XModel* asset_ = nullptr; + + XModel* parse_new(const std::string& name, ZoneMemory* mem, const std::string& filename); + XModel* parse(std::string name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + void* pointer() override { return asset_; } + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static XModel* generate_iw5_camo_model(const std::string& camo, XModel* asset); + static XModel* remove_attachments(XModel* asset); + static void dump(XModel* asset, + const std::function& convertToString = SL_ConvertToString); + }; + } +} diff --git a/src/IW4/Functions.hpp b/src/IW4/Functions.hpp new file mode 100644 index 0000000..53bae2f --- /dev/null +++ b/src/IW4/Functions.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + union XAssetHeader; + + static Function DB_FindXAssetHeader = 0x407930; + static Function DB_IsXAssetDefault = 0x48E6A0; + static Function DB_LoadXAssets = 0x4E5930; + + typedef int (__cdecl * DB_GetXAssetSizeHandler_t)(); + static DB_GetXAssetSizeHandler_t* DB_GetXAssetSizeHandlers = (DB_GetXAssetSizeHandler_t*)0x799488; + + static const char* SL_ConvertToString(std::uint16_t index) + { + return Memory::func(0x004EC1D0)(index); + } + + static short SL_AllocString(const std::string& string) + { + return Memory::func(0x00436B40)( + string.data(), 1, string.size() + 1); + } + } +} diff --git a/src/IW4/IW4.cpp b/src/IW4/IW4.cpp new file mode 100644 index 0000000..7fa9cbb --- /dev/null +++ b/src/IW4/IW4.cpp @@ -0,0 +1,874 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include +#include "ZoneTool/ZoneTool.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + bool isDumpingComplete = false; + bool isDumping = false; + bool isVerifying = false; + auto currentDumpingZone = ""s; + + const char* Linker::version() + { + return "IW4"; + } + + bool Linker::is_used() + { + return !strncmp(reinterpret_cast(0x71B85C), this->version(), 3); + } + + void Linker::load_default_zones() + { + static std::vector defaultzones = + { + "code_pre_gfx_mp", + "localized_code_pre_gfx_mp", + "code_post_gfx_mp", + "localized_code_post_gfx_mp", + "common_mp", + }; + + static XZoneInfo zones[16]; + memset(zones, 0, sizeof XZoneInfo * 16); + + // Load our custom zones + for (std::size_t i = 0; i < defaultzones.size(); i++) + { + zones[i].zone = defaultzones[i].c_str(); + zones[i].loadFlags = 0; + zones[i].unloadFlags = 0; + } + + return DB_LoadXAssets(zones, defaultzones.size(), 0); + } + + void Linker::run() + { + // function definitions + typedef void (__cdecl * Win_InitLocalization_t)(int); + typedef void (__cdecl * SL_Init_t)(); + typedef void (__cdecl * Swap_Init_t)(); + typedef void (__cdecl * Com_InitHunkMemory_t)(); + typedef void (__cdecl * Sys_InitializeCriticalSections_t)(); + typedef void (__cdecl * DB_InitThread_t)(); + typedef void (__cdecl * Com_InitDvars_t)(); + typedef void (__cdecl * PMem_Init_t)(); + typedef void (__cdecl * R_RegisterDvars_t)(); + typedef void (__cdecl * FS_Init_t)(); // wrong name, but lazy + typedef void (__cdecl * LargeLocalInit_t)(); // guessed name + typedef void (__cdecl * Cmd_ExecuteSingleCommand_t)(int controller, int a2, const char* cmd); + + SL_Init_t SL_Init = (SL_Init_t)0x4D2280; + Swap_Init_t Swap_Init = (Swap_Init_t)0x47F390; + Com_InitHunkMemory_t Com_InitHunkMemory = (Com_InitHunkMemory_t)0x420830; + Sys_InitializeCriticalSections_t Sys_InitializeCriticalSections = (Sys_InitializeCriticalSections_t) + 0x42F0A0; + Sys_InitializeCriticalSections_t Sys_InitMainThread = (Sys_InitializeCriticalSections_t)0x4301B0; + DB_InitThread_t DB_InitThread = (DB_InitThread_t)0x4E0FB0; + Com_InitDvars_t Com_InitDvars = (Com_InitDvars_t)0x60AD10; + Win_InitLocalization_t Win_InitLocalization = (Win_InitLocalization_t)0x406D10; + PMem_Init_t PMem_Init = (PMem_Init_t)0x64A020; + R_RegisterDvars_t R_RegisterDvars = (R_RegisterDvars_t)0x5196C0; + FS_Init_t FS_Init = (FS_Init_t)0x429080; + LargeLocalInit_t LargeLocalInit = (LargeLocalInit_t)0x4A62A0; + Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand = (Cmd_ExecuteSingleCommand_t)0x609540; + DWORD G_SetupWeaponDef = 0x4E1F30; + DWORD Scr_BeginLoadScripts = 0x4E1ED0; + DWORD Scr_BeginLoadScripts2 = 0x4541F0; + DWORD Scr_InitAllocNodes = 0x4B8740; + + Sys_InitializeCriticalSections(); + Sys_InitMainThread(); + Win_InitLocalization(0); + SL_Init(); + Swap_Init(); + Com_InitHunkMemory(); + PMem_Init(); + DB_InitThread(); + Com_InitDvars(); + R_RegisterDvars(); + + // gsc stuff + __asm call Scr_BeginLoadScripts; + __asm + { + push 0x201A45C; + call Scr_BeginLoadScripts2; + add esp, 4; + } + __asm call Scr_InitAllocNodes; + + LargeLocalInit(); + FS_Init(); + + load_default_zones(); + + // Cmd_RegisterCommands(); + // ZoneTool_LoadZones(nullptr, 0, 0); + + // menu stuff + //DWORD Menu_Setup1 = 0x4454C0; + //DWORD Menu_Setup2 = 0x501BC0; + //__asm call Menu_Setup1; + //__asm call Menu_Setup2; + + // load weapons + __asm call G_SetupWeaponDef; + + while (true) + { + } + } + + const char* Linker::get_asset_name(XAssetType type, XAssetHeader header) + { + // todo + if (type == image) + { + return header.gfximage->name; + } + if (type == menu) + { + // + } + else + { + return header.rawfile->name; + } + + return ""; + } + + void* DB_FindXAssetHeader_Unsafe(const XAssetType type, const std::string& name) + { + const static auto DB_FindXAssetHeader_Internal = 0x5BB1B0; + const auto name_ptr = name.data(); + const auto type_int = static_cast(type); + + const XAsset* asset_header = nullptr; + + __asm + { + mov edi, type_int; + push name_ptr; + call DB_FindXAssetHeader_Internal; + add esp, 4; + mov asset_header, eax; + } + + return (asset_header) ? asset_header->ptr.data : nullptr; + } + + void Linker::DB_AddXAsset(XAssetType type, XAssetHeader header) + { + static std::vector> referencedAssets; + + // nice meme + if (isVerifying) + { + // print asset name to console + ZONETOOL_INFO("Loading asset \"%s\" of type %s.", Linker::get_asset_name(type, header), reinterpret_cast< +char**>(0x00799278)[type]); + } + +#define DECLARE_ASSET(__TYPE__, __ASSET__) \ + if (type == __TYPE__) \ + { \ + __ASSET__::dump(header.__TYPE__); \ + } + + // fastfile name + auto fastfile = static_cast((char*)(*(DWORD*)0x112A680 + 4)); + + // generate CSV for fastfile + static FILE* csvFile = nullptr; + + if (isVerifying || isDumping) + { + FileSystem::SetFastFile(fastfile); + + + // open csv file for dumping + if (!csvFile) + { + csvFile = FileSystem::FileOpen(fastfile + ".csv", "wb"); + } + + // dump assets to disk + if (csvFile) + { + auto xassettypes = reinterpret_cast(0x00799278); + fprintf(csvFile, "%s,%s\n", xassettypes[type], get_asset_name(type, header)); + } + } + + if (isDumping) + { + // check if we're done loading the fastfile + if (type == rawfile && get_asset_name(type, header) == fastfile) + { + for (auto& ref : referencedAssets) + { + if (ref.second.length() <= 1 || ref.first == XAssetType::loaded_sound) + { + continue; + } + + const auto asset_name = &ref.second[1]; + const auto ref_asset = DB_FindXAssetHeader_Unsafe(ref.first, asset_name); + + if (ref_asset == nullptr) + { + ZONETOOL_ERROR("Could not find referenced asset \"%s\"!", asset_name); + continue; + } + + ZONETOOL_INFO("Dumping additional asset \"%s\" because it is referenced by %s.", asset_name, currentDumpingZone.data()); + + XAssetHeader header; + header.data = ref_asset; + + DB_AddXAsset(ref.first, header); + } + + ZONETOOL_INFO("Zone \"%s\" dumped.", &fastfile[0]); + + // clear referenced assets array because we are done dumping + referencedAssets.clear(); + + // clear csv file static variable for next dumps + FileSystem::FileClose(csvFile); + csvFile = nullptr; + + // mark dumping as complete to exit the process if it has been started using the command line + if (currentDumpingZone == fastfile) + { + isDumpingComplete = true; + } + } + + if (get_asset_name(type, header)[0] == ',') + { + referencedAssets.push_back({ type, get_asset_name(type, header) }); + } + else + { + DECLARE_ASSET(phys_collmap, IPhysCollmap); + DECLARE_ASSET(tracer, ITracerDef); + DECLARE_ASSET(xmodelsurfs, IXSurface); + DECLARE_ASSET(xmodel, IXModel); + DECLARE_ASSET(material, IMaterial); + DECLARE_ASSET(xanim, IXAnimParts); + DECLARE_ASSET(techset, ITechset); + DECLARE_ASSET(gfx_map, IGfxWorld); + DECLARE_ASSET(col_map_mp, IClipMap); + DECLARE_ASSET(map_ents, IMapEnts); + DECLARE_ASSET(fx_map, IFxWorld); + DECLARE_ASSET(com_map, IComWorld); + DECLARE_ASSET(sound, ISound); + DECLARE_ASSET(sndcurve, ISoundCurve); + DECLARE_ASSET(loaded_sound, ILoadedSound); + DECLARE_ASSET(rawfile, IRawFile); + DECLARE_ASSET(stringtable, IStringTable); + DECLARE_ASSET(stringtable, IStringTable); + DECLARE_ASSET(vertexdecl, IVertexDecl); + DECLARE_ASSET(pixelshader, IPixelShader); + DECLARE_ASSET(vertexshader, IVertexShader); + DECLARE_ASSET(techset, ITechset); + DECLARE_ASSET(game_map_mp, IGameWorldMp); + DECLARE_ASSET(image, IGfxImage); + DECLARE_ASSET(fx, IFxEffectDef); + DECLARE_ASSET(lightdef, ILightDef); + DECLARE_ASSET(weapon, IWeaponDef); + DECLARE_ASSET(addon_map_ents, IAddonMapEnts); + DECLARE_ASSET(font, IFontDef); + } + } + } + + void __declspec(naked) Linker::DB_AddXAssetStub() + { + // return to original function + __asm + { + // original code + sub esp, 0x14; + mov eax, [esp + 1Ch]; + mov ecx, [eax]; + push ebx; + push ebp; + mov ebp, [esp + 20h]; + + // call our DB_AddXAsset function + pushad; + push ecx; + push ebp; + call DB_AddXAsset; + add esp, 8; + popad; + + // jump back + push 0x005BB65F; + retn; + } + } + + void** DB_XAssetPool = (void**)0x7998A8; + unsigned int* g_poolSize = (unsigned int*)0x7995E8; + + void* ReallocateAssetPool(uint32_t type, unsigned int newSize) + { + int elSize = DB_GetXAssetSizeHandlers[type](); + + void* poolEntry = malloc(newSize * elSize); + DB_XAssetPool[type] = poolEntry; + g_poolSize[type] = newSize; + + return poolEntry; + } + + void* ReallocateAssetPoolM(uint32_t type, int multiplier) + { + int elSize = DB_GetXAssetSizeHandlers[type](); + int newSize = multiplier * g_poolSize[type]; + + void* poolEntry = malloc(newSize * elSize); + DB_XAssetPool[type] = poolEntry; + g_poolSize[type] = newSize; + + return poolEntry; + } + + int readPosition = 0; + + void logStreamPosition(int bytesRead) + { + if (isVerifying) + { + // ZONETOOL_INFO("Read %u bytes. (Currently at position 0x%08X in file)", bytesRead, readPosition - bytesRead); + } + } + + __declspec(naked) void Linker::IncreaseReadPointer() + { + __asm + { + // store read position + add readPosition, ecx; + mov DWORD PTR ds : 0x112a6c4, ecx; + + // log data + pushad; + push ecx; + call logStreamPosition; + add esp, 4; + popad; + + // go back + push 0x00445473; + retn; + } + } + + __declspec(naked) void Linker::IncreaseReadPointer2() + { + __asm + { + // store read position + add readPosition, esi; + + // original code + mov eax, 0x006B4B80; + call eax; + + // log data + pushad; + push esi; + call logStreamPosition; + add esp, 4; + popad; + + // go back + push 0x00470E56; + retn; + } + } + + void Linker::ReadHeader(void* ptr, int size) + { + // reset readPosition + readPosition = 0; + + // read header + return Memory::func(0x00445460)(ptr, size); + } + + void Linker::Load_XSurfaceArray(int shouldLoad, int count) + { + // read the actual count from the varXModelSurfs ptr + auto surface = *reinterpret_cast(0x0112A95C); + + // call original read function with the correct count + return Memory::func(0x004925B0)(shouldLoad, surface->numsurfs); + } + + const char* Linker::GetZonePath(const char* zoneName) + { + static std::string lastZonePath; + static std::vector zonePaths = + { + "zone\\dlc\\", + "zone\\patch\\" + }; + + const std::string zoneFileName = zoneName; + const char* languageName = Memory::func(0x45CBA0)(); + + // Priority 1: localized zone folder + const std::string localizedZonePath = va("zone\\%s\\", languageName, zoneName); + if(std::filesystem::exists(localizedZonePath + zoneFileName)) + { + lastZonePath = localizedZonePath; + return lastZonePath.c_str(); + } + + // Priority 2: custom zone paths + for(auto customZonePath : zonePaths) + { + if (std::filesystem::exists(customZonePath + zoneFileName)) + { + lastZonePath = customZonePath; + return lastZonePath.c_str(); + } + } + + // If no file could be found return the default location. The game will notice itself that there is no fastfile. + lastZonePath = localizedZonePath; + return lastZonePath.c_str(); + } + + void ExitZoneTool() + { + std::exit(0); + } + + void gsc_compile_error(int unk, const char* fmt, ...) + { + char error_message[4096] = {}; + + va_list va; + va_start(va, fmt); + _vsnprintf(error_message, sizeof error_message, fmt, va); + + ZONETOOL_ERROR("script compile error: %s", error_message); + } + + void emit_opcode(int opcode, int a2, int a3) + { + ZONETOOL_INFO("compiling opcode %u", opcode); + } + + auto should_log = false; + void LogFile(const std::string& log) + { + if (!should_log) + { + return; + } + + static auto did_delete = false; + if (!did_delete && std::filesystem::exists("Z:\\loading-zt.txt")) + { + did_delete = true; + std::filesystem::remove("Z:\\loading-zt.txt"); + } + + static auto fp = fopen("Z:\\loading-zt.txt", "a"); + fprintf(fp, log.data()); + fflush(fp); + } + + void __declspec(naked) Load_Stream(bool atStreamStart, char* data, int count) + { + static auto load_stream = 0x470E35; + __asm + { + cmp byte ptr[esp + 4], 0; + jmp load_stream; + } + } + + void Load_StreamHook(bool atStreamStart, char* data, int count) + { + if (atStreamStart) + { + LogFile(va("Load_Stream: Reading %u bytes.\n", count)); + } + + Load_Stream(atStreamStart, data, count); + } + + void __declspec(naked) DB_PushStreamPos(int stream) + { + static auto db_pushstreampos = 0x458A25; + __asm + { + mov eax, ds:0x16e5578 + jmp db_pushstreampos; + } + } + + void DB_PushStreamPosHook(int stream) + { + LogFile(va("DB_PushStreamPos: Setting stream to %u\n", stream)); + DB_PushStreamPos(stream); + } + + void __declspec(naked) DB_PopStreamPos() + { + static auto db_popstreampos = 0x4D1D65; + __asm + { + mov eax, ds:0x16E5548; + jmp db_popstreampos; + } + } + + void DB_PopStreamPosHook() + { + LogFile(va("DB_PopStreamPos: Popped stream\n")); + DB_PopStreamPos(); + } + + void log_align(int align) + { + LogFile(va("DB_AllocStreamPos: Aligning buffer by %i\n", align)); + } + +#undef not + + void __declspec(naked) DB_AllocStreamPosHook() + { + __asm + { + mov ecx, [esp + 4]; + pushad; + push ecx; + call log_align; + add esp, 4; + popad; + mov eax, ds:0x16e5554; + add eax, ecx; + not ecx; + and eax, ecx; + mov ds:0x16e5554, eax; + retn; + } + } + + void Linker::startup() + { + if (this->is_used()) + { + // + Memory(0x470E30).jump(Load_StreamHook); + Memory(0x458A20).jump(DB_PushStreamPosHook); + Memory(0x4D1D60).jump(DB_PopStreamPosHook); + Memory(0x418380).jump(DB_AllocStreamPosHook); + + // for compiling GSC scripts + ZoneTool::register_command("compilescript", [](auto args) + { + // + if (args.size() < 2) + { + ZONETOOL_INFO("Usage: compilescript \n"); + return; + } + + if (FileSystem::FileExists(args[1] + ".gsc")) + { + ZONETOOL_INFO("Compiling script \"%s\"...", args[1].data()); + + auto fp = FileSystem::FileOpen(args[1] + ".gsc", "rb"); + if (fp) + { + const auto file_size = FileSystem::FileSize(fp); + const auto bytes = FileSystem::ReadBytes(fp, file_size); + auto bytes_ptr = bytes.data(); + + // set bytes ptr + Memory(0x1CFEEE8).set(bytes_ptr); + + // patch current thread + Memory(0x1CDE7FC).set(GetCurrentThreadId()); + + // load gsc + Function(0x427D00)(args[1].data(), 0, 0); + + FileSystem::FileClose(fp); + + ZONETOOL_INFO("Successfully compiled script \"%s\"!", args[1].data()); + } + } + else + { + ZONETOOL_ERROR("Cannot find script \"%s\".", args[1].data()); + } + }); + + // dump emitted opcodes + Memory(0x613FD0).jump(emit_opcode); + + // force compiling gsc + Memory(0x427DB4).set(0xEB); + Memory(0x427D22).set(0xEB); + + // + Memory(0x427DED).nop(6); + + // do nothing with online sessions + Memory(0x441650).set(0xC3); + + // temp fix for GSC compiling + Memory(0x434260).jump(gsc_compile_error); + + // Realloc asset pools + ReallocateAssetPoolM(localize, 2); + ReallocateAssetPoolM(material, 2); + ReallocateAssetPoolM(font, 2); + ReallocateAssetPoolM(image, 2); + ReallocateAssetPoolM(techset, 2); + ReallocateAssetPoolM(fx, 2); + ReallocateAssetPoolM(xanim, 2); + ReallocateAssetPoolM(xmodel, 2); + ReallocateAssetPoolM(physpreset, 2); + ReallocateAssetPoolM(weapon, 2); + ReallocateAssetPoolM(game_map_sp, 2); + ReallocateAssetPoolM(game_map_mp, 2); + ReallocateAssetPoolM(map_ents, 5); + ReallocateAssetPoolM(com_map, 5); + ReallocateAssetPoolM(col_map_mp, 5); + ReallocateAssetPoolM(gfx_map, 5); + ReallocateAssetPoolM(fx_map, 5); + ReallocateAssetPoolM(xmodelsurfs, 2); + ReallocateAssetPoolM(vertexshader, 2); + ReallocateAssetPoolM(vertexdecl, 16); + ReallocateAssetPoolM(pixelshader, 2); + ReallocateAssetPoolM(rawfile, 2); + ReallocateAssetPoolM(lightdef, 2); + + // Kill "missing asset" errors from the game to prevent confusion + Memory(0x5BB380).set(0xC3); + + // Kill Com_Error + Memory(0x004B22D0).jump(ExitZoneTool); + + // Tool init func + Memory(0x6BABA1).call(run); + Memory(0x4AA88B).call(printf); + + // r_loadForRenderer + Memory(0x519DDF).set(0x0); + + // dirty disk breakpoint + // Memory(0x4CF7F0).Set(0xCC); + + // delay loading of images, disable it + Memory(0x51F450).set(0xC3); + + // don't remove the 'texture data' pointer from GfxImage + Memory(0x51F4FA).nop(6); + + // needed for the above to make Image_Release not misinterpret the texture data as a D3D texture + Memory(0x51F03D).set(0xEB); + + // don't zero out pixel shaders + Memory(0x505AFB).nop(7); + + // don't zero out vertex shaders + Memory(0x505BDB).nop(7); + + // don't memset vertex declarations (not needed?) + Memory(0x00431B91).nop(5); + + // allow loading of IWffu (unsigned) files + Memory(0x4158D9).set(0xEB); //main function + Memory(0x4A1D97).nop(2); //DB_AuthLoad_InflateInit + + // basic checks (hash jumps, both normal and playlist) + Memory(0x5B97A3).nop(2); + Memory(0x5BA493).nop(2); + + Memory(0x5B991C).nop(2); + Memory(0x5BA60C).nop(2); + + Memory(0x5B97B4).nop(2); + Memory(0x5BA4A4).nop(2); + + // some other, unknown, check + Memory(0x5B9912).set(0xB8); + Memory(0x5B9913).set(1); + + Memory(0x5BA602).set(0xB8); + Memory(0x5BA603).set(1); + + // something related to image loading + Memory(0x54ADB0).set(0xC3); + + // dvar setting function, unknown stuff related to server thread sync + Memory(0x647781).set(0xEB); + + // fs_basegame + Memory(0x6431D1).set("zonetool"); + + // hunk size (was 300 MiB) + Memory(0x64A029).set(0x3F000000); + Memory(0x64A057).set(0x3F000000); // 0x1C200000 + + // allow loading of IWffu (unsigned) files + Memory(0x4158D9).set(0xEB); // main function + Memory(0x4A1D97).nop(2); // DB_AuthLoad_InflateInit + + // basic checks (hash jumps, both normal and playlist) + Memory(0x5B97A3).nop(2); + Memory(0x5BA493).nop(2); + + Memory(0x5B991C).nop(2); + Memory(0x5BA60C).nop(2); + + Memory(0x5B97B4).nop(2); + Memory(0x5BA4A4).nop(2); + + // Disabling loadedsound touching + Memory(0x492EFC).nop(5); + + // weaponfile patches + Memory(0x408228).nop(5); // find asset header + Memory(0x408230).nop(5); // is asset default + Memory(0x40823A).nop(2); // jump + + // menu stuff + // disable the 2 new tokens in ItemParse_rect + Memory(0x640693).set(0xEB); + + // Dont load ASSET_TYPE_MENU anymore, we dont need it. + Memory(0x453406).nop(5); + + // DB_AddXAsset hook + Memory(0x005BB650).jump(DB_AddXAssetStub); + + // Fix fucking XSurface assets + Memory(0x0048E8A5).call(Load_XSurfaceArray); + + // Fastfile debugging + Memory(0x0044546D).jump(IncreaseReadPointer); + Memory(0x00470E51).jump(IncreaseReadPointer2); + Memory(0x004159E2).call(ReadHeader); + + // Load fastfiles from custom zone folders + Memory(0x44DA90).jump(GetZonePath); + } + } + + std::shared_ptr Linker::alloc_zone(const std::string& zone) + { + // Patch current thread + Memory(0x1CDE7FC).set(GetCurrentThreadId()); + + // allocate zone + auto ptr = std::make_shared(zone, this); + + return ptr; + } + + std::shared_ptr Linker::alloc_buffer() + { + auto ptr = std::make_shared(); + ptr->init_streams(8); + return ptr; + } + + void Linker::load_zone(const std::string& name) + { + ZONETOOL_INFO("Loading zone \"%s\"...", name.data()); + + XZoneInfo zone = {name.data(), 20, 0}; + DB_LoadXAssets(&zone, 1, 0); + + ZONETOOL_INFO("Zone \"%s\" loaded.", name.data()); + } + + void Linker::unload_zones() + { + } + + bool Linker::is_valid_asset_type(const std::string& type) + { + return this->type_to_int(type) >= 0; + } + + std::int32_t Linker::type_to_int(std::string type) + { + auto xassettypes = reinterpret_cast(0x00799278); + + for (std::int32_t i = 0; i < max; i++) + { + if (xassettypes[i] == type) + return i; + } + + return -1; + } + + std::string Linker::type_to_string(std::int32_t type) + { + auto xassettypes = reinterpret_cast(0x00799278); + return xassettypes[type]; + } + + bool Linker::supports_building() + { + return true; + } + + bool Linker::supports_version(const zone_target_version version) + { + return version == zone_target_version::iw4_release || version == zone_target_version::iw4_release_console || + version == zone_target_version::iw4_alpha_482 || version == zone_target_version::iw4_alpha_491; + } + + void Linker::dump_zone(const std::string& name) + { + isDumpingComplete = false; + isDumping = true; + currentDumpingZone = name; + load_zone(name); + + while (!isDumpingComplete) + { + Sleep(1); + } + } + + void Linker::verify_zone(const std::string& name) + { + should_log = true; + isVerifying = true; + currentDumpingZone = name; + load_zone(name); + } + } +} diff --git a/src/IW4/IW4.hpp b/src/IW4/IW4.hpp new file mode 100644 index 0000000..b733634 --- /dev/null +++ b/src/IW4/IW4.hpp @@ -0,0 +1,58 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include +#include "Functions.hpp" +#include "Structs.hpp" +#include "../IW5/Structs.hpp" +#include "Zone.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + using GfxImageFileHeader = IW5::GfxImageFileHeader; + + class Linker : public ILinker + { + public: + const char* version() override; + bool is_used() override; + void startup() override; + std::shared_ptr alloc_zone(const std::string& zone) override; + std::shared_ptr alloc_buffer() override; + void load_zone(const std::string& name) override; + void unload_zones() override; + bool is_valid_asset_type(const std::string& type) override; + std::int32_t type_to_int(std::string type) override; + std::string type_to_string(std::int32_t type) override; + bool supports_building() override; + bool supports_version(const zone_target_version version) override; + + void dump_zone(const std::string& name) override; + void verify_zone(const std::string& name) override; + + static void run(); + static void load_default_zones(); + static const char* get_asset_name(XAssetType type, XAssetHeader header); + + static void DB_AddXAsset(XAssetType type, XAssetHeader header); + static void DB_AddXAssetStub(); + static void IncreaseReadPointer(); + static void IncreaseReadPointer2(); + static void ReadHeader(void* ptr, int size); + static void Load_XSurfaceArray(int shouldLoad, int count); + static const char* GetZonePath(const char* zoneName); + + private: + + }; + } +} diff --git a/src/IW4/Structs.hpp b/src/IW4/Structs.hpp new file mode 100644 index 0000000..e846684 --- /dev/null +++ b/src/IW4/Structs.hpp @@ -0,0 +1,3864 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + enum FxElemType : char + { + FX_ELEM_TYPE_SPRITE_BILLBOARD = 0x0, + FX_ELEM_TYPE_SPRITE_ORIENTED = 0x1, + FX_ELEM_TYPE_TAIL = 0x2, + FX_ELEM_TYPE_TRAIL = 0x3, + FX_ELEM_TYPE_CLOUD = 0x4, + FX_ELEM_TYPE_SPARKCLOUD = 0x5, + FX_ELEM_TYPE_SPARKFOUNTAIN = 0x6, + FX_ELEM_TYPE_MODEL = 0x7, + FX_ELEM_TYPE_OMNI_LIGHT = 0x8, + FX_ELEM_TYPE_SPOT_LIGHT = 0x9, + FX_ELEM_TYPE_SOUND = 0xA, + FX_ELEM_TYPE_DECAL = 0xB, + FX_ELEM_TYPE_RUNNER = 0xC, + FX_ELEM_TYPE_COUNT = 0xD, + FX_ELEM_TYPE_LAST_SPRITE = 0x3, + FX_ELEM_TYPE_LAST_DRAWN = 0x9, + }; + + enum XAssetType : std::int32_t + { + physpreset, + phys_collmap, + xanim, + xmodelsurfs, + xmodel, + material, + pixelshader, + vertexshader, + vertexdecl, + techset, + image, + sound, + sndcurve, + loaded_sound, + col_map_sp, + col_map_mp, + com_map, + game_map_sp, + game_map_mp, + map_ents, + fx_map, + gfx_map, + lightdef, + ui_map, + // not used + font, + menufile, + menu, + localize, + weapon, + snddriverglobals, + // not used + fx, + impactfx, + aitype, + // not used + mptype, + // not used + character, + // not used + xmodelalias, + // not used + rawfile, + stringtable, + leaderboarddef, + structureddatadef, + tracer, + vehicle, + addon_map_ents, + max, + }; + + typedef float vec4_t[4]; + typedef float vec3_t[3]; + typedef float vec2_t[2]; + + template + struct VecInternal + { + float data[N]; + }; + + struct RawFile + { + const char* name; + int compressedLen; + int len; + const char* buffer; + }; + +#pragma pack(push, 4) + struct PhysPreset + { + const char* name; + int type; + float mass; + float bounce; + float friction; + float bulletForceScale; + float explosiveForceScale; + const char* sndAliasPrefix; + float piecesSpreadFraction; + float piecesUpwardVelocity; + bool tempDefaultToCylinder; + }; +#pragma pack(pop) + + + struct XModelAngle + { + short x; + short y; + short z; + short base; + }; + + struct XModelTagPos + { + float x; + float y; + float z; + }; + + struct DObjAnimMat + { + float quat[4]; + float trans[3]; + float transWeight; + }; + +#pragma pack(push, 4) + struct MaterialStreamRouting + { + char source; + char dest; + }; + + struct VertexDecl + { + const char* name; + char streamCount; + bool hasOptionalSource; + char pad[2]; + MaterialStreamRouting streams[13]; + void* declarations[16]; + }; +#pragma pack(pop) + + struct PixelShader + { + const char* name; + void* shader; + DWORD* bytecode; + short codeLen; + }; + + struct VertexShader + { + const char* name; + void* shader; + DWORD* bytecode; + short codeLen; + }; + + struct MaterialArgumentCodeConst + { + unsigned __int16 index; + char firstRow; + char rowCount; + }; + + union MaterialArgumentDef + { + float* literalConst; + MaterialArgumentCodeConst codeConst; + unsigned int codeSampler; + unsigned int nameHash; + }; + + struct ShaderArgumentDef + { + short type; + short dest; + MaterialArgumentDef u; + }; +#pragma pack(push, 4) + + struct MaterialPass + { + VertexDecl* vertexDecl; + VertexShader* vertexShader; + PixelShader* pixelShader; + char perPrimArgCount; + char perObjArgCount; + char stableArgCount; + char customSamplerFlags; + ShaderArgumentDef* argumentDef; + }; +#pragma pack(pop) + struct MaterialTechniqueHeader + { + const char* name; + unsigned __int16 flags; + unsigned __int16 passCount; + }; + + struct MaterialTechnique + { + MaterialTechniqueHeader hdr; + MaterialPass pass[1]; + }; + + struct MaterialTechniqueSet + { + const char* name; + int pad; + MaterialTechniqueSet* remappedTechniques; + MaterialTechnique* techniques[48]; + }; + + struct MaterialConstantDef + { + unsigned int nameHash; + char name[12]; + float literal[4]; + }; + + struct GfxStateBits + { + unsigned int loadBits[2]; + }; + + struct GfxImageLoadDef // actually a IDirect3DTexture* but this is easier + { + char mipLevels; + char flags; + short dimensions[3]; + int format; // usually the compression Magic + int dataSize; // set to zero to load from IWD + char* texture; // texture + }; + + struct GfxImage + { + GfxImageLoadDef* texture; + char mapType; // 5 is cube, 4 is 3d, 3 is 2d + char semantic; + char category; + char flags; + int cardMemory; + int dataLen1; + int dataLen2; + short height; + short width; + short depth; + bool loaded; + char pad; + char* name; + }; + + struct MaterialImage + { + unsigned int typeHash; // asset hash of type + char firstCharacter; // first character of image name + char secondLastCharacter; // second-last character of image name (maybe only in CoD4?!) + char sampleState; + char semantic; + GfxImage* image; // Image* actually + }; + + namespace alpha + { + enum MaterialTechniqueType + { + TECHNIQUE_DEPTH_PREPASS = 0x0, + TECHNIQUE_BUILD_FLOAT_Z = 0x1, + TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2, + TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3, + TECHNIQUE_UNLIT = 0x4, + TECHNIQUE_EMISSIVE = 0x5, + TECHNIQUE_EMISSIVE_DFOG = 0x6, + TECHNIQUE_EMISSIVE_SHADOW = 0x7, + TECHNIQUE_EMISSIVE_SHADOW_DFOG = 0x8, + TECHNIQUE_LIT_BEGIN = 0x9, + TECHNIQUE_LIT = 0x9, + TECHNIQUE_LIT_DFOG = 0xA, + TECHNIQUE_LIT_SUN = 0xB, + TECHNIQUE_LIT_SUN_DFOG = 0xC, + TECHNIQUE_LIT_SUN_SHADOW = 0xD, + TECHNIQUE_LIT_SUN_SHADOW_DFOG = 0xE, + TECHNIQUE_LIT_SPOT = 0xF, + TECHNIQUE_LIT_SPOT_DFOG = 0x10, + TECHNIQUE_LIT_SPOT_SHADOW = 0x11, + TECHNIQUE_LIT_SPOT_SHADOW_DFOG = 0x12, + TECHNIQUE_LIT_OMNI = 0x13, + TECHNIQUE_LIT_OMNI_DFOG = 0x14, + TECHNIQUE_LIT_OMNI_SHADOW = 0x15, + TECHNIQUE_LIT_OMNI_SHADOW_DFOG = 0x16, + TECHNIQUE_LIT_END = 0x17, + TECHNIQUE_LIGHT_SPOT = 0x17, + TECHNIQUE_LIGHT_OMNI = 0x18, + TECHNIQUE_LIGHT_SPOT_SHADOW = 0x19, + TECHNIQUE_FAKELIGHT_NORMAL = 0x1A, + TECHNIQUE_FAKELIGHT_VIEW = 0x1B, + TECHNIQUE_SUNLIGHT_PREVIEW = 0x1C, + TECHNIQUE_CASE_TEXTURE = 0x1D, + TECHNIQUE_WIREFRAME_SOLID = 0x1E, + TECHNIQUE_WIREFRAME_SHADED = 0x1F, + TECHNIQUE_DEBUG_BUMPMAP = 0x20, + TECHNIQUE_COUNT = 0x21, + TECHNIQUE_TOTAL_COUNT = 0x22, + TECHNIQUE_NONE = 0x23, + }; + + struct MaterialPass + { + VertexDecl* vertexDecl; + VertexShader* vertexShaderArray[15]; + VertexShader* vertexShader; + PixelShader* pixelShader; + char perPrimArgCount; + char perObjArgCount; + char stableArgCount; + char customSamplerFlags; + char precompiledIndex; + ShaderArgumentDef* args; + }; + + struct MaterialTechnique + { + const char* name; + unsigned __int16 flags; + unsigned __int16 passCount; + MaterialPass passArray[1]; + }; + +#pragma pack(push, 4) + struct MaterialTechniqueSet + { + const char* name; + char worldVertFormat; + char unused[2]; + MaterialTechniqueSet* remappedTechniqueSet; + MaterialTechnique* techniques[33]; + }; +#pragma pack(pop) + + struct D3DTexture + { + //DWORD Common; + //DWORD Fence; + //DWORD BaseFlush; + //DWORD MipFlush; + //DWORD Format; + char unk[52]; + }; + + struct CardMemory + { + int platform[1]; + }; + + struct GfxImageStreamData + { + unsigned __int16 width; + unsigned __int16 height; + unsigned int pixelSize; + }; + +#pragma pack(push, 4) + struct GfxImage + { + D3DTexture texture; + int format; + char mapType; + char semantic; + char category; + CardMemory cardMemory; + unsigned __int16 width; + unsigned __int16 height; + unsigned __int16 depth; + char levelCount; + char cached; + char* pixels; + GfxImageStreamData streams[4]; + const char* name; + }; +#pragma pack(pop) + + struct GfxDrawSurfFields + { + unsigned __int64 objectId : 16; + unsigned __int64 reflectionProbeIndex : 8; + unsigned __int64 hasGfxEntIndex : 1; + unsigned __int64 customIndex : 5; + unsigned __int64 materialSortedIndex : 12; + unsigned __int64 prepass : 2; + unsigned __int64 useHeroLighting : 1; + unsigned __int64 sceneLightIndex : 8; + unsigned __int64 surfType : 4; + unsigned __int64 primarySortKey : 6; + unsigned __int64 unused : 1; + }; + + union GfxDrawSurf + { + GfxDrawSurfFields fields; + unsigned __int64 packed; + }; + + struct __declspec(align(8)) Material + { + const char* name; + char gameFlags; + char sortKey; + char textureAtlasRowCount; + char textureAtlasColumnCount; + GfxDrawSurf drawSurf; + unsigned int surfaceTypeBits; + char stateBitsEntry[33]; + char textureCount; + char constantCount; + char stateBitsCount; + char stateFlags; + char cameraRegion; + char layerCount; + MaterialTechniqueSet* techniqueSet; + MaterialImage* textureTable; + MaterialConstantDef* constantTable; + GfxStateBits* stateBitsTable; + const char** subMaterials; + }; + } + + struct Material + { + const char* name; // 0 + char gameFlags; + char sortKey; + unsigned char animationX; // 6 // amount of animation frames in X + unsigned char animationY; // 7 // amount of animation frames in Y + unsigned int subRendererIndex; // 0x00 //+8 + unsigned int rendererIndex; // 12 // only for 3D models + int unknown; + unsigned int surfaceTypeBits; //+20 + char stateBitsEntry[48]; // 32 // 0xFF + char numMaps; + char constantCount; + char stateBitsCount; + char stateFlags; // 0x03 + unsigned short cameraRegion; // 0x04 + MaterialTechniqueSet* techniqueSet; // '2d' techset + MaterialImage* maps; // map references + MaterialConstantDef* constantTable; + GfxStateBits* stateMap; // might be NULL, need to test + }; + + //vec3_t should be from idTech3, has to do with camera angles + typedef float vec_t; + typedef vec_t vec3_t[3]; + + struct Bounds + { + vec3_t midPoint; + vec3_t halfSize; + + void compute() + { + compute(midPoint, halfSize); + } + + void compute(vec3_t mins, vec3_t maxs) + { + for (int i = 0; i < 3; ++i) + { + this->halfSize[i] = (maxs[i] - mins[i]) / 2; + this->midPoint[i] = this->halfSize[i] + mins[i]; + } + } + }; + + struct XBoneInfo + { + union + { + Bounds packedBounds; + float bounds[2][3]; + }; + + float radiusSquared; + }; + + struct Face + { + unsigned short v1; + unsigned short v2; + unsigned short v3; + }; + + // XModel + struct XSurfaceVertexInfo + { + __int16 vertCount[4]; + unsigned __int16* vertsBlend; + + /* + Count... Ok, here we go... + + (((vertCount[2] << 2) + vertCount[2]) + + ((vertCount[3] << 3) - vertCount[3]) + + ((vertCount[1] << 1) + vertCount[1]) + + vertCount[0]) << 1*/ + }; + + union GfxColor + { + unsigned int packed; + char array[4]; + }; + + union PackedTexCoords + { + unsigned int packed; + }; + + union PackedUnitVec + { + unsigned int packed; + }; + + struct GfxPackedVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + PackedTexCoords texCoord; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct XSurfaceCollisionAabb + { + unsigned __int16 mins[3]; + unsigned __int16 maxs[3]; + }; + + struct XSurfaceCollisionNode + { + XSurfaceCollisionAabb aabb; + unsigned __int16 childBeginIndex; + unsigned __int16 childCount; + }; + + struct XSurfaceCollisionLeaf + { + unsigned __int16 triangleBeginIndex; + }; + + struct XSurfaceCollisionTree + { + float trans[3]; + float scale[3]; + unsigned int nodeCount; + XSurfaceCollisionNode* nodes; + unsigned int leafCount; + XSurfaceCollisionLeaf* leafs; + }; + + struct XRigidVertList + { + unsigned __int16 boneOffset; + unsigned __int16 vertCount; + unsigned __int16 triOffset; + unsigned __int16 triCount; + XSurfaceCollisionTree* collisionTree; + }; + + struct XSurface + { + char tileMode; + bool deformed; + unsigned short vertCount; + unsigned short triCount; + unsigned char streamHandle; + char zoneHandle; + unsigned __int16 baseTriIndex; + unsigned __int16 baseVertIndex; + Face* triIndices; + XSurfaceVertexInfo vertexInfo; + GfxPackedVertex* verticies; + int vertListCount; + XRigidVertList* rigidVertLists; + int partBits[6]; + }; + + namespace alpha + { + struct D3DResource + { + unsigned int Common; + unsigned int ReferenceCount; + unsigned int Fence; + unsigned int ReadFence; + unsigned int Identifier; + unsigned int BaseFlush; + }; + + struct D3DIndexBuffer : D3DResource + { + unsigned int Address; + unsigned int Size; + }; + + union GPUVERTEX_FETCH_CONSTANT + { + unsigned int dword[2]; + }; + + struct D3DVertexBuffer : D3DResource + { + GPUVERTEX_FETCH_CONSTANT Format; + }; + + struct XSurface + { + char tileMode; + char deformed; + unsigned __int16 vertCount; + unsigned __int16 triCount; + Face* triIndices; + XSurfaceVertexInfo vertexInfo; + GfxPackedVertex* verticies; + D3DVertexBuffer vb0; + unsigned int vertListCount; + XRigidVertList* rigidVertLists; + D3DIndexBuffer indexBuffer; + int partBits[5]; + }; + +#pragma pack(push, 4) + struct XModelSurfs + { + const char* name; + XSurface* surfs; + unsigned __int16 numsurfs; + int partBits[5]; + }; +#pragma pack(pop) + + struct XModelLodInfo + { + float dist; + unsigned __int16 numSurfacesInLod; + unsigned __int16 surfIndex; + XModelSurfs* surfaces; + int partBits[5]; + XSurface* surfs; + }; + + struct XModelCollSurf_s + { + Bounds bounds; + int boneIdx; + int contents; + int surfFlags; + }; + } + + struct XModelSurfs + { + const char* name; + XSurface* surfs; + unsigned __int16 numsurfs; + unsigned __int16 pad; + int partBits[6]; + }; + + struct XModelLodInfo + { + float dist; + short numSurfacesInLod; + short surfIndex; + XModelSurfs* surfaces; + int partBits[6]; + XSurface* surfs; + char lod; + char smcBaseIndexPlusOne; + char smcSubIndexMask; + char smcBucket; + }; + + struct XModelCollTri_s + { + float plane[4]; + float svec[4]; + float tvec[4]; + }; + + struct XModelCollSurf_s + { + XModelCollTri_s* tris; + int numCollTris; + Bounds bounds; + int boneIdx; + int contents; + int surfFlags; + }; + + struct PhysCollmap; + + struct XModel + { + char* name; + char numBones; + char numRootBones; + unsigned char numSurfaces; + char lodRampType; + float scale; + unsigned int noScalePartBits[6]; + short* boneNames; + unsigned char* parentList; + XModelAngle* tagAngles; + XModelTagPos* tagPositions; + char* partClassification; + DObjAnimMat* animMatrix; + Material** materials; + XModelLodInfo lods[4]; + char maxLoadedLod; + char numLods; + char collLod; + char flags; + XModelCollSurf_s* colSurf; + int numColSurfs; + int contents; + XBoneInfo* boneInfo; + float radius; + Bounds bounds; + int memUsage; + bool bad; + char pad[3]; + PhysPreset* physPreset; + PhysCollmap* physCollmap; + }; // total size 304 + + namespace alpha + { +#pragma pack(push, 4) + struct XModel + { + const char* name; + char numBones; + char numRootBones; + char numSurfaces; + float scale; + unsigned int noScalePartBits[5]; + __int16* boneNames; + unsigned char* parentList; + XModelAngle* tagAngles; + XModelTagPos* tagPositions; + char* partClassification; + DObjAnimMat* animMatrix; + Material** materials; + XModelLodInfo lods[4]; + char maxLoadedLod; + char numLods; + char collLod; + char flags; + XModelCollSurf_s* colSurf; + int numColSurfs; + int contents; + XBoneInfo* boneInfo; + float radius; + Bounds bounds; + unsigned __int16* invHighMipRadius; + int memUsage; + PhysPreset* physPreset; + PhysCollmap* physCollmap; + }; +#pragma pack(pop) + } + + enum weapFireType_t : int + { + WEAPON_FIRETYPE_FULLAUTO = 0x0, + WEAPON_FIRETYPE_SINGLESHOT = 0x1, + WEAPON_FIRETYPE_BURSTFIRE2 = 0x2, + WEAPON_FIRETYPE_BURSTFIRE3 = 0x3, + WEAPON_FIRETYPE_BURSTFIRE4 = 0x4, + WEAPON_FIRETYPE_DOUBLE_BARREL = 0x5, + WEAPON_FIRETYPE_MAX + }; + + enum weapInventoryType_t : int + { + WEAPINVENTORY_PRIMARY = 0, + WEAPINVENTORY_OFFHAND = 1, + WEAPINVENTORY_ITEM = 2, + WEAPINVENTORY_ALTMODE = 3, + WEAPINVENTORY_EXCLUSIVE = 4, + WEAPINVENTORY_SCAVENGER = 5, + WEAPINVENTORY_MAX + }; + + enum PenetrateType + { + PENETRATE_TYPE_NONE = 0x0, + PENETRATE_TYPE_SMALL = 0x1, + PENETRATE_TYPE_MEDIUM = 0x2, + PENETRATE_TYPE_LARGE = 0x3, + PENETRATE_TYPE_COUNT = 0x4 + }; + + enum activeReticleType_t : int + { + VEH_ACTIVE_RETICLE_NONE = 0, + VEH_ACTIVE_RETICLE_PIP_ON_A_STICK = 1, + VEH_ACTIVE_RETICLE_BOUNCING_DIAMOND = 2, + VEH_ACTIVE_RETICLE_MAX + }; + + enum weapType_t : int + { + WEAPTYPE_BULLET = 0, + WEAPTYPE_GRENADE = 1, + WEAPTYPE_PROJECTILE = 2, + WEAPTYPE_RIOTSHIELD = 3, + WEAPTYPE_MAX + }; + + enum weapClass_t : int + { + WEAPCLASS_RIFLE = 0, + WEAPCLASS_SNIPER = 1, + WEAPCLASS_MG = 2, + WEAPCLASS_SMG = 3, + WEAPCLASS_SPREAD = 4, + WEAPCLASS_PISTOL = 5, + WEAPCLASS_GRENADE = 6, + WEAPCLASS_ROCKETLAUNCHER = 7, + WEAPCLASS_TURRET = 8, + WEAPCLASS_THROWINGKNIFE = 9, + WEAPCLASS_NON_PLAYER = 10, + WEAPCLASS_ITEM = 11, + WEAPCLASS_MAX + }; + + enum OffhandClass : int + { + OFFHAND_CLASS_NONE = 0, + OFFHAND_CLASS_FRAG_GRENADE = 1, + OFFHAND_CLASS_SMOKE_GRENADE = 2, + OFFHAND_CLASS_FLASH_GRENADE = 3, + OFFHAND_CLASS_MAX + }; + + enum playerAnimType_t : int + { + PLAER_ANIM_TYPE_NONE = 0x0, + PLAER_ANIM_TYPE_OTHER = 0x1, + PLAER_ANIM_TYPE_PISTOL = 0x2, + PLAER_ANIM_TYPE_SMG = 0x3, + PLAER_ANIM_TYPE_AUTORIFLE = 0x4, + PLAER_ANIM_TYPE_MG = 0x5, + PLAER_ANIM_TYPE_SNIPER = 0x6, + PLAER_ANIM_TYPE_ROCKETLAUNCHER = 0x7, + PLAER_ANIM_TYPE_EXPLOSIVE = 0x8, + PLAER_ANIM_TYPE_GRENADE = 0x9, + PLAER_ANIM_TYPE_TURRET = 0xA, + PLAER_ANIM_TYPE_C4 = 0xB, + PLAER_ANIM_TYPE_M203 = 0xC, + PLAER_ANIM_TYPE_HOLD = 0xD, + PLAER_ANIM_TYPE_BRIEFCASE = 0xE, + PLAER_ANIM_TYPE_RIOTSHIELD = 0xF, + PLAER_ANIM_TYPE_LAPTOP = 0x10, + PLAER_ANIM_TYPE_THROWINGKNIFE = 0x11 + }; + + enum weapProjExplosion_t + { + WEAPPROJEXP_GRENADE = 0x0, + WEAPPROJEXP_ROCKET = 0x1, + WEAPPROJEXP_FLASHBANG = 0x2, + WEAPPROJEXP_NONE = 0x3, + WEAPPROJEXP_DUD = 0x4, + WEAPPROJEXP_SMOKE = 0x5, + WEAPPROJEXP_HEAVY = 0x6, + WEAPPROJEXP_NUM = 0x7 + }; + + enum WeapStickinessType + { + WEAPSTICKINESS_NONE = 0x0, + WEAPSTICKINESS_ALL = 0x1, + WEAPSTICKINESS_ALL_ORIENT = 0x2, + WEAPSTICKINESS_GROUND = 0x3, + WEAPSTICKINESS_GROUND_WITH_YAW = 0x4, + WEAPSTICKINESS_KNIFE = 0x5, + WEAPSTICKINESS_COUNT = 0x6 + }; + + enum weaponIconRatioType_t + { + WEAPON_ICON_RATIO_1TO1 = 0x0, + WEAPON_ICON_RATIO_2TO1 = 0x1, + WEAPON_ICON_RATIO_4TO1 = 0x2, + WEAPON_ICON_RATIO_COUNT = 0x3 + }; + + enum ammoCounterClipType_t + { + AMMO_COUNTER_CLIP_NONE = 0x0, + AMMO_COUNTER_CLIP_MAGAZINE = 0x1, + AMMO_COUNTER_CLIP_SHORTMAGAZINE = 0x2, + AMMO_COUNTER_CLIP_SHOTGUN = 0x3, + AMMO_COUNTER_CLIP_ROCKET = 0x4, + AMMO_COUNTER_CLIP_BELTFED = 0x5, + AMMO_COUNTER_CLIP_ALTWEAPON = 0x6, + AMMO_COUNTER_CLIP_COUNT = 0x7 + }; + + enum weapOverlayReticle_t + { + WEAPOVERLAYRETICLE_NONE = 0x0, + WEAPOVERLAYRETICLE_CROSSHAIR = 0x1, + WEAPOVERLAYRETICLE_NUM = 0x2 + }; + + enum weapOverlayInterface_t + { + WEAPOVERLAYINTERFACE_NONE = 0x0, + WEAPOVERLAYINTERFACE_JAVELIN = 0x1, + WEAPOVERLAYINTERFACE_TURRETSCOPE = 0x2, + WEAPOVERLAYINTERFACECOUNT = 0x3 + }; + + enum weapStance_t + { + WEAPSTANCE_STAND = 0x0, + WEAPSTANCE_DUCK = 0x1, + WEAPSTANCE_PRONE = 0x2, + WEAPSTANCE_NUM = 0x3 + }; + + enum ImpactType + { + IMPACT_TYPE_NONE = 0, + IMPACT_TYPE_BULLET_SMALL = 1, + IMPACT_TYPE_BULLET_LARGE = 2, + IMPACT_TYPE_BULLET_AP = 3, + IMPACT_TYPE_SHOTGUN_FMJ = 4, + IMPACT_TYPE_SHOTGUN = 5, + IMPACT_TYPE_GRENADE_BOUNCE = 7, + IMPACT_TYPE_GRENADE_EXPLODE = 8, + IMPACT_TYPE_ROCKET_EXPLODE = 9, + IMPACT_TYPE_PROJECTILE_DUD = 10, + IMPACT_TYPE_MAX + }; + + enum guidedMissileType_t + { + MISSILE_GUIDANCE_NONE = 0x0, + MISSILE_GUIDANCE_SIDEWINDER = 0x1, + MISSILE_GUIDANCE_HELLFIRE = 0x2, + MISSILE_GUIDANCE_JAVELIN = 0x3, + MISSILE_GUIDANCE_MAX + }; + + // Check knots in FF later! +#pragma pack(push, 4) + struct SndCurve + { + const char* filename; + unsigned __int16 knotCount; + vec2_t knots[16]; + }; +#pragma pack(pop) + + struct _AILSOUNDINFO + { + int format; + const void* data_ptr; + unsigned int data_len; + unsigned int rate; + int bits; + int channels; + unsigned int samples; + unsigned int block_size; + const void* initial_ptr; + }; + + // Loaded sound +#pragma pack(push, 4) + struct MssSound + { + _AILSOUNDINFO info; + char* data; + }; +#pragma pack(pop) + + struct LoadedSound + { + const char* name; + MssSound sound; + }; + + // Sounds + struct SpeakerLevels + { + int speaker; + int numLevels; + float levels[2]; + }; + + struct ChannelMap + { + int entryCount; // how many entries are used + SpeakerLevels speakers[6]; + }; + + struct SpeakerMap + { + bool isDefault; + char _pad[3]; + const char* name; + ChannelMap channelMaps[2][2]; + }; + + enum snd_alias_type_t : char + { + SAT_UNKNOWN = 0x0, + SAT_LOADED = 0x1, + SAT_STREAMED = 0x2, + SAT_PRIMED = 0x3, + SAT_COUNT = 0x4, + }; + + struct StreamFileNamePacked + { + unsigned __int64 offset; + unsigned __int64 length; + }; + + struct StreamFileNameRaw + { + const char* dir; + const char* name; + }; + + union StreamFileInfo + { + StreamFileNameRaw raw; + StreamFileNamePacked packed; + }; + + struct StreamFileName + { + unsigned __int16 isLocalized; + unsigned __int16 fileIndex; + StreamFileInfo info; + }; + + /*struct StreamedSound + { + StreamFileName filename; + unsigned int totalMsec; + };*/ + struct StreamedSound + { + const char* dir; + const char* name; + }; + + struct PrimedSound + { + StreamFileName filename; + LoadedSound* loadedPart; + int dataOffset; + int totalSize; + unsigned int primedCrc; + }; + + union SoundData + { + LoadedSound* loadSnd; // SoundFile->type == SAT_LOADED + StreamedSound streamSnd; // SoundFile->type == SAT_STREAMED + //PrimedSound primedSnd; // SoundFile->type == SAT_PRIMED + }; + + struct SoundFile // 0x10 + { + char type; + bool exists; + char _pad[2]; + SoundData sound; + }; +#pragma pack(push, 4) + union $C8D87EB0090687D323381DFB7A82089C + { + float slavePercentage; + float masterPercentage; + }; + + struct snd_alias_t + { + const char* aliasName; + const char* subtitle; + const char* secondaryAliasName; + const char* chainAliasName; + const char* mixerGroup; + SoundFile* soundFile; + int sequence; + float volMin; + float volMax; + float pitchMin; + float pitchMax; + float distMin; + float distMax; + float velocityMin; + int flags; + $C8D87EB0090687D323381DFB7A82089C ___u15; + float probability; + float lfePercentage; + float centerPercentage; + int startDelay; + SndCurve* volumeFalloffCurve; + float envelopMin; + float envelopMax; + float envelopPercentage; + SpeakerMap* speakerMap; + }; +#pragma pack(pop) + struct snd_alias_list_t + { + union + { + const char* aliasName; + const char* name; + }; + + snd_alias_t* head; + int count; + }; + + union snd_alias_list_name + { + const char* name; + snd_alias_list_t* asset; + }; + + // Tracers + struct TracerDef + { + const char* name; + Material* material; + unsigned int drawInterval; + float speed; + float beamLength; + float beamWidth; + float screwRadius; + float screwDist; + float colors[5][4]; + }; + + struct WeaponDef + { + const char* szOverlayName; + XModel** gunXModel; + XModel* handXModel; + const char** szXAnimsRightHanded; + const char** szXAnimsLeftHanded; + const char* szModeName; + unsigned __int16* notetrackSoundMapKeys; + unsigned __int16* notetrackSoundMapValues; + unsigned __int16* notetrackRumbleMapKeys; + unsigned __int16* notetrackRumbleMapValues; + int playerAnimType; + weapType_t weapType; + weapClass_t weapClass; + PenetrateType penetrateType; + weapInventoryType_t inventoryType; + weapFireType_t fireType; + OffhandClass offhandClass; + weapStance_t stance; + void* viewFlashEffect; + void* worldFlashEffect; + union + { + struct + { + snd_alias_list_t* pickupSound; + snd_alias_list_t* pickupSoundPlayer; + snd_alias_list_t* ammoPickupSound; + snd_alias_list_t* ammoPickupSoundPlayer; + snd_alias_list_t* projectileSound; + snd_alias_list_t* pullbackSound; + snd_alias_list_t* pullbackSoundPlayer; + snd_alias_list_t* fireSound; + snd_alias_list_t* fireSoundPlayer; + snd_alias_list_t* fireSoundPlayerAkimbo; + snd_alias_list_t* fireLoopSound; + snd_alias_list_t* fireLoopSoundPlayer; + snd_alias_list_t* fireStopSound; + snd_alias_list_t* fireStopSoundPlayer; + snd_alias_list_t* fireLastSound; + snd_alias_list_t* fireLastSoundPlayer; + snd_alias_list_t* emptyFireSound; + snd_alias_list_t* emptyFireSoundPlayer; + snd_alias_list_t* meleeSwipeSound; + snd_alias_list_t* meleeSwipeSoundPlayer; + snd_alias_list_t* meleeHitSound; + snd_alias_list_t* meleeMissSound; + snd_alias_list_t* rechamberSound; + snd_alias_list_t* rechamberSoundPlayer; + snd_alias_list_t* reloadSound; + snd_alias_list_t* reloadSoundPlayer; + snd_alias_list_t* reloadEmptySound; + snd_alias_list_t* reloadEmptySoundPlayer; + snd_alias_list_t* reloadStartSound; + snd_alias_list_t* reloadStartSoundPlayer; + snd_alias_list_t* reloadEndSound; + snd_alias_list_t* reloadEndSoundPlayer; + snd_alias_list_t* detonateSound; + snd_alias_list_t* detonateSoundPlayer; + snd_alias_list_t* nightVisionWearSound; + snd_alias_list_t* nightVisionWearSoundPlayer; + snd_alias_list_t* nightVisionRemoveSound; + snd_alias_list_t* nightVisionRemoveSoundPlayer; + snd_alias_list_t* altSwitchSound; + snd_alias_list_t* altSwitchSoundPlayer; + snd_alias_list_t* raiseSound; + snd_alias_list_t* raiseSoundPlayer; + snd_alias_list_t* firstRaiseSound; + snd_alias_list_t* firstRaiseSoundPlayer; + snd_alias_list_t* putawaySound; + snd_alias_list_t* putawaySoundPlayer; + snd_alias_list_t* scanSound; + }; + snd_alias_list_t* sounds[47]; + }; + snd_alias_list_t** bounceSound; + void* viewShellEjectEffect; + void* worldShellEjectEffect; + void* viewLastShotEjectEffect; + void* worldLastShotEjectEffect; + Material* reticleCenter; + Material* reticleSide; + int iReticleCenterSize; + int iReticleSideSize; + int iReticleMinOfs; + activeReticleType_t activeReticleType; + float vStandMove[3]; + float vStandRot[3]; + float strafeMove[3]; + float strafeRot[3]; + float vDuckedOfs[3]; + float vDuckedMove[3]; + float vDuckedRot[3]; + float vProneOfs[3]; + float vProneMove[3]; + float vProneRot[3]; + float fPosMoveRate; + float fPosProneMoveRate; + float fStandMoveMinSpeed; + float fDuckedMoveMinSpeed; + float fProneMoveMinSpeed; + float fPosRotRate; + float fPosProneRotRate; + float fStandRotMinSpeed; + float fDuckedRotMinSpeed; + float fProneRotMinSpeed; + XModel** worldModel; + XModel* worldClipModel; + XModel* rocketModel; + XModel* knifeModel; + XModel* worldKnifeModel; + Material* hudIcon; + weaponIconRatioType_t hudIconRatio; + Material* pickupIcon; + weaponIconRatioType_t pickupIconRatio; + Material* ammoCounterIcon; + weaponIconRatioType_t ammoCounterIconRatio; + ammoCounterClipType_t ammoCounterClip; + int iStartAmmo; + const char* szAmmoName; + int iAmmoIndex; + const char* szClipName; + int iClipIndex; + int iMaxAmmo; + int shotCount; + const char* szSharedAmmoCapName; + int iSharedAmmoCapIndex; + int iSharedAmmoCap; + int damage; + int playerDamage; + int iMeleeDamage; + int iDamageType; + int iFireDelay; + int iMeleeDelay; + int meleeChargeDelay; + int iDetonateDelay; + int iRechamberTime; + int rechamberTimeOneHanded; + int iRechamberBoltTime; + int iHoldFireTime; + int iDetonateTime; + int iMeleeTime; + int meleeChargeTime; + int iReloadTime; + int reloadShowRocketTime; + int iReloadEmptyTime; + int iReloadAddTime; + int iReloadStartTime; + int iReloadStartAddTime; + int iReloadEndTime; + int iDropTime; + int iRaiseTime; + int iAltDropTime; + int quickDropTime; + int quickRaiseTime; + int iBreachRaiseTime; + int iEmptyRaiseTime; + int iEmptyDropTime; + int sprintInTime; + int sprintLoopTime; + int sprintOutTime; + int stunnedTimeBegin; + int stunnedTimeLoop; + int stunnedTimeEnd; + int nightVisionWearTime; + int nightVisionWearTimeFadeOutEnd; + int nightVisionWearTimePowerUp; + int nightVisionRemoveTime; + int nightVisionRemoveTimePowerDown; + int nightVisionRemoveTimeFadeInStart; + int fuseTime; + int aiFuseTime; + float autoAimRange; + float aimAssistRange; + float aimAssistRangeAds; + float aimPadding; + float enemyCrosshairRange; + float moveSpeedScale; + float adsMoveSpeedScale; + float sprintDurationScale; + float fAdsZoomInFrac; + float fAdsZoomOutFrac; + Material* overlayMaterial; + Material* overlayMaterialLowRes; + Material* overlayMaterialEMP; + Material* overlayMaterialEMPLowRes; + weapOverlayReticle_t overlayReticle; + int overlayInterface; + float overlayWidth; + float overlayHeight; + float overlayWidthSplitscreen; + float overlayHeightSplitscreen; + float fAdsBobFactor; + float fAdsViewBobMult; + float fHipSpreadStandMin; + float fHipSpreadDuckedMin; + float fHipSpreadProneMin; + float hipSpreadStandMax; + float hipSpreadDuckedMax; + float hipSpreadProneMax; + float fHipSpreadDecayRate; + float fHipSpreadFireAdd; + float fHipSpreadTurnAdd; + float fHipSpreadMoveAdd; + float fHipSpreadDuckedDecay; + float fHipSpreadProneDecay; + float fHipReticleSidePos; + float fAdsIdleAmount; + float fHipIdleAmount; + float adsIdleSpeed; + float hipIdleSpeed; + float fIdleCrouchFactor; + float fIdleProneFactor; + float fGunMaxPitch; + float fGunMaxYaw; + float swayMaxAngle; + float swayLerpSpeed; + float swayPitchScale; + float swayYawScale; + float swayHorizScale; + float swayVertScale; + float swayShellShockScale; + float adsSwayMaxAngle; + float adsSwayLerpSpeed; + float adsSwayPitchScale; + float adsSwayYawScale; + float adsSwayHorizScale; + float adsSwayVertScale; + float adsViewErrorMin; + float adsViewErrorMax; + PhysCollmap* physCollmap; + float dualWieldViewModelOffset; + weaponIconRatioType_t killIconRatio; + int iReloadAmmoAdd; + int iReloadStartAdd; + int ammoDropStockMin; + int ammoDropClipPercentMin; + int ammoDropClipPercentMax; + int iExplosionRadius; + int iExplosionRadiusMin; + int iExplosionInnerDamage; + int iExplosionOuterDamage; + float damageConeAngle; + float bulletExplDmgMult; + float bulletExplRadiusMult; + int iProjectileSpeed; + int iProjectileSpeedUp; + int iProjectileSpeedForward; + int iProjectileActivateDist; + float projLifetime; + float timeToAccelerate; + float projectileCurvature; + XModel* projectileModel; + int projExplosion; + void* projExplosionEffect; + void* projDudEffect; + snd_alias_list_t* projExplosionSound; + snd_alias_list_t* projDudSound; + WeapStickinessType stickiness; + float lowAmmoWarningThreshold; + float ricochetChance; + float* parallelBounce; + float* perpendicularBounce; + void* projTrailEffect; + void* projBeaconEffect; + float vProjectileColor[3]; + guidedMissileType_t guidedMissileType; + float maxSteeringAccel; + int projIgnitionDelay; + void* projIgnitionEffect; + snd_alias_list_t* projIgnitionSound; + float fAdsAimPitch; + float fAdsCrosshairInFrac; + float fAdsCrosshairOutFrac; + int adsGunKickReducedKickBullets; + float adsGunKickReducedKickPercent; + float fAdsGunKickPitchMin; + float fAdsGunKickPitchMax; + float fAdsGunKickYawMin; + float fAdsGunKickYawMax; + float fAdsGunKickAccel; + float fAdsGunKickSpeedMax; + float fAdsGunKickSpeedDecay; + float fAdsGunKickStaticDecay; + float fAdsViewKickPitchMin; + float fAdsViewKickPitchMax; + float fAdsViewKickYawMin; + float fAdsViewKickYawMax; + float fAdsViewScatterMin; + float fAdsViewScatterMax; + float fAdsSpread; + int hipGunKickReducedKickBullets; + float hipGunKickReducedKickPercent; + float fHipGunKickPitchMin; + float fHipGunKickPitchMax; + float fHipGunKickYawMin; + float fHipGunKickYawMax; + float fHipGunKickAccel; + float fHipGunKickSpeedMax; + float fHipGunKickSpeedDecay; + float fHipGunKickStaticDecay; + float fHipViewKickPitchMin; + float fHipViewKickPitchMax; + float fHipViewKickYawMin; + float fHipViewKickYawMax; + float fHipViewScatterMin; + float fHipViewScatterMax; + float fightDist; + float maxDist; + const char* accuracyGraphName[2]; + float(*originalAccuracyGraphKnots[2])[2]; + unsigned __int16 originalAccuracyGraphKnotCount[2]; + int iPositionReloadTransTime; + float leftArc; + float rightArc; + float topArc; + float bottomArc; + float accuracy; + float aiSpread; + float playerSpread; + float minTurnSpeed[2]; + float maxTurnSpeed[2]; + float pitchConvergenceTime; + float yawConvergenceTime; + float suppressTime; + float maxRange; + float fAnimHorRotateInc; + float fPlayerPositionDist; + const char* szUseHintString; + const char* dropHintString; + int iUseHintStringIndex; + int dropHintStringIndex; + float horizViewJitter; + float vertViewJitter; + float scanSpeed; + float scanAccel; + int scanPauseTime; + const char* szScript; + float fOOPosAnimLength[2]; + int minDamage; + int minPlayerDamage; + float fMaxDamageRange; + float fMinDamageRange; + float destabilizationRateTime; + float destabilizationCurvatureMax; + int destabilizeDistance; + float* locationDamageMultipliers; + const char* fireRumble; + const char* meleeImpactRumble; + TracerDef* tracerType; + float turretScopeZoomRate; + float turretScopeZoomMin; + float turretScopeZoomMax; + float turretOverheatUpRate; + float turretOverheatDownRate; + float turretOverheatPenalty; + snd_alias_list_t* turretOverheatSound; + void* turretOverheatEffect; + const char* turretBarrelSpinRumble; + float turretBarrelSpinSpeed; + float turretBarrelSpinUpTime; + float turretBarrelSpinDownTime; + snd_alias_list_t* turretBarrelSpinMaxSnd; + snd_alias_list_t* turretBarrelSpinUpSnd[4]; + snd_alias_list_t* turretBarrelSpinDownSnd[4]; + snd_alias_list_t* missileConeSoundAlias; + snd_alias_list_t* missileConeSoundAliasAtBase; + float missileConeSoundRadiusAtTop; + float missileConeSoundRadiusAtBase; + float missileConeSoundHeight; + float missileConeSoundOriginOffset; + float missileConeSoundVolumescaleAtCore; + float missileConeSoundVolumescaleAtEdge; + float missileConeSoundVolumescaleCoreSize; + float missileConeSoundPitchAtTop; + float missileConeSoundPitchAtBottom; + float missileConeSoundPitchTopSize; + float missileConeSoundPitchBottomSize; + float missileConeSoundCrossfadeTopSize; + float missileConeSoundCrossfadeBottomSize; + bool sharedAmmo; + bool lockonSupported; + bool requireLockonToFire; + bool bigExplosion; + bool noAdsWhenMagEmpty; + bool avoidDropCleanup; + bool inheritsPerks; + bool crosshairColorChange; + bool bRifleBullet; + bool armorPiercing; + bool bBoltAction; + bool aimDownSight; + bool bRechamberWhileAds; + bool bBulletExplosiveDamage; + bool bCookOffHold; + bool bClipOnly; + bool noAmmoPickup; + bool adsFireOnly; + bool cancelAutoHolsterWhenEmpty; + bool disableSwitchToWhenEmpty; + bool suppressAmmoReserveDisplay; + bool laserSightDuringNightvision; + bool markableViewmodel; + bool noDualWield; + bool flipKillIcon; + bool bNoPartialReload; + bool bSegmentedReload; + bool blocksProne; + bool silenced; + bool isRollingGrenade; + bool projExplosionEffectForceNormalUp; + bool bProjImpactExplode; + bool stickToPlayers; + bool hasDetonator; + bool disableFiring; + bool timedDetonation; + bool rotate; + bool holdButtonToThrow; + bool freezeMovementWhenFiring; + bool thermalScope; + bool altModeSameWeapon; + bool turretBarrelSpinEnabled; + bool missileConeSoundEnabled; + bool missileConeSoundPitchshiftEnabled; + bool missileConeSoundCrossfadeEnabled; + bool offhandHoldIsCancelable; + }; + +#pragma pack(push, 4) + struct WeaponCompleteDef + { + const char* szInternalName; + WeaponDef* weapDef; + const char* szDisplayName; + unsigned __int16* hideTags; + const char** szXAnims; + float fAdsZoomFov; + int iAdsTransInTime; + int iAdsTransOutTime; + int iClipSize; + ImpactType impactType; + int iFireTime; + weaponIconRatioType_t dpadIconRatio; + float penetrateMultiplier; + float fAdsViewKickCenterSpeed; + float fHipViewKickCenterSpeed; + const char* szAltWeaponName; + unsigned int altWeaponIndex; + int iAltRaiseTime; + Material* killIcon; + Material* dpadIcon; + int fireAnimLength; + int iFirstRaiseTime; + int ammoDropStockMax; + float adsDofStart; + float adsDofEnd; + unsigned __int16 accuracyGraphKnotCount[2]; + float(*accuracyGraphKnots[2])[2]; + bool motionTracker; + bool enhanced; + bool dpadIconShowsAmmo; + }; +#pragma pack(pop) + + struct Glyph + { + unsigned __int16 letter; + char x0; + char y0; + char dx; + char pixelWidth; + char pixelHeight; + float s0; + float t0; + float s1; + float t1; + }; + +#pragma pack(push, 4) + struct cplane_s + { + float normal[3]; + float dist; + char type; + char signbits; + }; +#pragma pack(pop) + + /* 1003 */ + struct Font_s + { + const char* fontName; + int pixelHeight; + int glyphCount; + Material* material; + Material* glowMaterial; + Glyph* glyphs; + }; + + struct cbrushside_t + { + cplane_s *plane; + unsigned __int16 materialNum; + char firstAdjacentSideOffset; + char edgeCount; + }; + + // ClipMap + typedef char cbrushedge_t; + + struct cbrush_t + { + unsigned __int16 numsides; + unsigned __int16 glassPieceIndex; + cbrushside_t* sides; + cbrushedge_t* edge; + __int16 axialMaterialNum[2][3]; + char firstAdjacentSideOffsets[2][3]; + char edgeCount[2][3]; + }; + + struct BrushWrapper + { + float mins[3]; + float maxs[3]; + unsigned int numPlaneSide; + cbrushside_t* side; + char* edge; + __int16 axialMaterialNum[2][3]; + __int16 firstAdjacentSideOffsets[2][3]; + int numEdge; + cplane_s* plane; + }; + + //struct BrushWrapper + //{ + // Bounds bounds; // 24 + // cbrush_t brush; // 36 + // cbrushside_t *side; // 4 + // int totalEdgeCount; // 4 + // cplane_s *plane; // 4 + // short numPlaneSide; // 2 + // char *edge; // 4 + // int numEdge; // 4 + //}; // 82 bytes total + + struct PhysGeomInfo + { + BrushWrapper* brush; + int type; + float orientation[3][3]; + Bounds bounds; + }; + + struct PhysMass + { + float centerOfMass[3]; + float momentsOfInertia[3]; + float productsOfInertia[3]; + // int contents; + }; + + struct PhysCollmap + { + const char* name; + unsigned int numInfo; + PhysGeomInfo* info; + PhysMass mass; + Bounds bounds; + }; + + struct G_GlassName + { + char* nameStr; + unsigned __int16 name; + unsigned __int16 pieceCount; + unsigned __int16* pieceIndices; + }; + +#pragma pack(push, 2) + struct G_GlassPiece + { + unsigned __int16 damageTaken; + unsigned __int16 collapseTime; + int lastStateChangeTime; + char impactDir; + char impactPos[2]; + }; +#pragma pack(pop) + + struct G_GlassData + { + G_GlassPiece* glassPieces; + unsigned int pieceCount; + unsigned __int16 damageToWeaken; + unsigned __int16 damageToDestroy; + unsigned int glassNameCount; + G_GlassName* glassNames; + char pad[108]; + }; + + struct GameWorldMp + { + const char* name; + G_GlassData* g_glassData; + }; + + struct GameWorldSp + { + const char* name; + char useless_sp_shit[48]; + G_GlassData* g_glassData; + }; + + // FxWorld +#pragma pack(push, 4) + + struct FxGlassDef + { + float halfThickness; + float texVecs[2][2]; + GfxColor color; + Material* material; + Material* materialShattered; + PhysPreset* physPreset; + }; + + struct FxSpatialFrame + { + float quat[4]; + float origin[3]; + }; + + union FxGlassPiecePlace + { + struct + { + FxSpatialFrame frame; + float radius; + }; + + unsigned int nextFree; + }; + + struct FxGlassPieceState + { + float texCoordOrigin[2]; + unsigned int supportMask; + unsigned __int16 initIndex; + unsigned __int16 geoDataStart; + unsigned __int16 lightingIndex; + char defIndex; + char pad[3]; + char vertCount; + char holeDataCount; + char crackDataCount; + char fanDataCount; + unsigned __int16 flags; + float areaX2; + }; + + struct FxGlassPieceDynamics + { + int fallTime; + __int32 physObjId; + __int32 physJointId; + float vel[3]; + float avel[3]; + }; + + struct FxGlassVertex + { + __int16 x; + __int16 y; + }; + + struct FxGlassHoleHeader + { + unsigned __int16 uniqueVertCount; + char touchVert; + char pad[1]; + }; + + struct FxGlassCrackHeader + { + unsigned __int16 uniqueVertCount; + char beginVertIndex; + char endVertIndex; + }; + + union FxGlassGeometryData + { + FxGlassVertex vert; + FxGlassHoleHeader hole; + FxGlassCrackHeader crack; + char asBytes[4]; + __int16 anonymous[2]; + }; + + struct FxGlassInitPieceState //Note, on MW3 this is missing 4 bytes, just not sure whats missing yet + { + FxSpatialFrame frame; + float radius; + float texCoordOrigin[2]; + unsigned int supportMask; + //float areaX2; // Commented out a random thing so the size fits. Most probably wrong since it was random. + unsigned __int16 lightingIndex; + char defIndex; + char vertCount; + char fanDataCount; + char pad[1]; + }; + + struct FxGlassSystem + { + int time; // 4 + int prevTime; // 4 + unsigned int defCount; // 4 + unsigned int pieceLimit; // 4 + unsigned int pieceWordCount; // 4 + unsigned int initPieceCount; // 4 + unsigned int cellCount; // 4 + unsigned int activePieceCount; // 4 + unsigned int firstFreePiece; // 4 + unsigned int geoDataLimit; // 4 + unsigned int geoDataCount; // 4 + unsigned int initGeoDataCount; // 4 + FxGlassDef* defs; // 4 + FxGlassPiecePlace* piecePlaces; // 4 + FxGlassPieceState* pieceStates; // 4 + FxGlassPieceDynamics* pieceDynamics; // 4 + FxGlassGeometryData* geoData; // 4 + unsigned int* isInUse; // 4 + unsigned int* cellBits; // 4 + char* visData; // 4 + VecInternal<3>* linkOrg; + float* halfThickness; // 4 + unsigned __int16* lightingHandles; // 4 + FxGlassInitPieceState* initPieceStates; // 4 + FxGlassGeometryData* initGeoData; // 4 + bool needToCompactData; // 1 + char initCount; + short pad; + float effectChanceAccum; // 4 + int lastPieceDeletionTime; // 4 + }; + + struct FxWorld + { + char* name; + FxGlassSystem glassSys; + }; +#pragma pack(pop) + + // MapEnts + struct TriggerModel + { + int contents; + unsigned short hullCount; + unsigned short firstHull; + }; + + struct TriggerHull + { + Bounds bounds; + int contents; + unsigned short slabCount; + unsigned short firstSlab; + }; + + struct TriggerSlab + { + vec3_t dir; + float midPoint; + float halfSize; + }; + + struct MapTriggers + { + int modelCount; + TriggerModel* models; // sizeof 8 + int hullCount; + TriggerHull* hulls; // sizeof 32 + int slabCount; + TriggerSlab* slabs; // sizeof 20 + }; +#pragma pack(push, 1) + struct Stage + { + char* stageName; + float offset[3]; + unsigned __int16 triggerIndex; + char sunPrimaryLightIndex; + char pad; + }; + + struct MapEnts + { + const char* name; // 0 + const char* entityString; // 4 + int numEntityChars; // 8 + MapTriggers trigger; // 12 + Stage* stageNames; // 36 + char stageCount; // 40 + char pad[3]; + }; +#pragma pack(pop) + +#pragma pack(push, 1) + struct ComPrimaryLight + { + union + { + char _portpad0[28]; + + struct + { + char type; + char canUseShadowMap; + char exponent; + char unused; + float color[3]; + float dir[3]; + }; + }; + + union + { + char _portpad1[40]; + + struct + { + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + float cosHalfFovExpanded; + float rotationLimit; + float translationLimit; + const char* defName; + }; + }; + }; +#pragma pack(pop) + + struct ComWorld + { + const char* name; + int isInUse; + unsigned int primaryLightCount; + ComPrimaryLight* primaryLights; + }; + + + union XAnimIndices + { + char* _1; + unsigned __int16* _2; + void* data; + }; + + union XAnimDynamicFrames + { + char (*_1)[3]; + unsigned __int16 (*_2)[3]; + }; + + union XAnimDynamicIndices + { + char _1[1]; + unsigned __int16 _2[1]; + }; + + struct XAnimPartTransFrames + { + float mins[3]; + float size[3]; + XAnimDynamicFrames frames; + XAnimDynamicIndices indices; + }; + + union XAnimPartTransData + { + XAnimPartTransFrames frames; + float frame0[3]; + }; + + struct XAnimPartTrans + { + unsigned __int16 size; + char smallTrans; + __declspec(align(2)) XAnimPartTransData u; + }; + +#pragma pack(push, 4) + struct XAnimDeltaPartQuatDataFrames2 + { + __int16(*frames)[2]; + XAnimDynamicIndices indices; + }; + + union XAnimDeltaPartQuatData2 + { + XAnimDeltaPartQuatDataFrames2 frames; + __int16 frame0[2]; + }; + + struct XAnimDeltaPartQuat2 + { + unsigned __int16 size; + XAnimDeltaPartQuatData2 u; + }; + + struct XAnimDeltaPartQuatDataFrames + { + __int16(*frames)[4]; + XAnimDynamicIndices indices; + }; + + union XAnimDeltaPartQuatData + { + XAnimDeltaPartQuatDataFrames frames; + __int16 frame0[4]; + }; + + struct XAnimDeltaPartQuat + { + unsigned __int16 size; + XAnimDeltaPartQuatData u; + }; + + struct XAnimDeltaPart + { + XAnimPartTrans* trans; + XAnimDeltaPartQuat2* quat2; + XAnimDeltaPartQuat* quat; + }; + + struct XAnimNotifyInfo + { + short name; + float time; + }; + + enum XAnimPartsFlags + { + ANIM_LOOP = 0x1, + ANIM_DELTA = 0x2, + ANIM_DELTA_3D = 0x4, + }; + + struct XAnimParts + { + char* name; // 0 + unsigned short dataByteCount; // 4 + unsigned short dataShortCount; // 6 + unsigned short dataIntCount; // 8 + unsigned short randomDataByteCount; // 10 - 0xA + unsigned short randomDataIntCount; // 12 - 0xC + unsigned short framecount; // 14 - 0xE + char flags; // 16 + unsigned char boneCount[10]; // 17 + char notifyCount; // 27 + char assetType; // 30 + bool isDefault; // 31 + unsigned int randomDataShortCount; // 32 - 0x20 + unsigned int indexcount; // 36 - 0x24 + float framerate; // 40 - 0x28 + float frequency; // 44 - 0x2C + unsigned short* tagnames; // 48 - 0x30 + char* dataByte; // 52 - 0x34 + short* dataShort; // 56 - 0x38 + int* dataInt; // 60 - 0x3C + short* randomDataShort; // 64 - 0x40 + char* randomDataByte; // 68 - 0x44 + int* randomDataInt; // 72 - 0x48 + XAnimIndices indices; // 76 - 0x4C + XAnimNotifyInfo* notify; // 80 - 0x50 + XAnimDeltaPart* delta; // 84 - 0x54 + }; +#pragma pack(pop) + + // Localized Strings + struct LocalizeEntry + { + const char* localizedString; + const char* name; + }; + + // Stringtables + struct StringTableCell + { + char* string; // 0 + int hash; // 4 + }; + + struct StringTable + { + const char* name; // 0 + int columns; // 4 + int rows; // 8 + StringTableCell* strings; // 12 + }; + + // Fx + struct FxEffectDef; + + /* struct FxElemMarkVisuals + { + Material *materials[2]; + };*/ + struct FxImpactEntry + { + FxEffectDef* nonflesh[31]; + FxEffectDef* flesh[4]; + }; + + union FxEffectDefRef + { + FxEffectDef* handle; + const char* name; + }; + + union FxElemVisuals + { + const void* anonymous; + Material* material; + XModel* xmodel; + FxEffectDefRef* effectDef; + const char* soundName; + }; + + typedef Material* FxElemMarkVisuals[2]; + + union FxElemDefVisuals + { + FxElemVisuals instance; + FxElemVisuals* array; + FxElemMarkVisuals* markArray; + }; + + struct FxTrailVertex + { + float pos[2]; + float normal[2]; + float texCoord; + }; + + struct FxTrailDef + { + int scrollTimeMsec; + int repeatDist; + float invSplitDist; + float invSplitArcDist; + float invSplitTime; + int vertCount; + FxTrailVertex* verts; + int indCount; + unsigned __int16* inds; + }; + + struct FxSparkFountainDef + { + float gravity; + float bounceFrac; + float bounceRand; + float sparkSpacing; + float sparkLength; + int sparkCount; + float loopTime; + float velMin; + float velMax; + float velConeFrac; + float restSpeed; + float boostTime; + float boostFactor; + }; + + union FxElemExtendedDefPtr + { + FxTrailDef* trailDef; + FxSparkFountainDef* sparkFountain; + char* unknownDef; + }; + + struct FxSpawnDefLooping + { + int intervalMsec; + int count; + }; + + struct FxIntRange + { + int base; + int amplitude; + }; + + struct FxFloatRange + { + float base; + float amplitude; + }; + + struct FxSpawnDefOneShot + { + FxIntRange count; + }; + + union FxSpawnDef + { + FxSpawnDefLooping looping; + FxSpawnDefOneShot oneShot; + }; + + struct FxElemAtlas + { + char behavior; + char index; + char fps; + char loopCount; + char colIndexBits; + char rowIndexBits; + __int16 entryCount; + }; + + struct FxElemVec3Range + { + float base[3]; + float amplitude[3]; + }; + + struct FxElemVelStateInFrame + { + FxElemVec3Range velocity; + FxElemVec3Range totalDelta; + }; + + const struct FxElemVelStateSample + { + FxElemVelStateInFrame local; + FxElemVelStateInFrame world; + }; + + struct FxElemVisualState + { + char color[4]; + float rotationDelta; + float rotationTotal; + float size[2]; + float scale; + }; + + const struct FxElemVisStateSample + { + FxElemVisualState base; + FxElemVisualState amplitude; + }; + + struct FxElemDef + { + int flags; + FxSpawnDef spawn; + FxFloatRange spawnRange; + FxFloatRange fadeInRange; + FxFloatRange fadeOutRange; + float spawnFrustumCullRadius; + FxIntRange spawnDelayMsec; + FxIntRange lifeSpanMsec; + FxFloatRange spawnOrigin[3]; + FxFloatRange spawnOffsetRadius; + FxFloatRange spawnOffsetHeight; + FxFloatRange spawnAngles[3]; + FxFloatRange angularVelocity[3]; + FxFloatRange initialRotation; + FxFloatRange gravity; + FxFloatRange reflectionFactor; + FxElemAtlas atlas; + char elemType; + char visualCount; + char velIntervalCount; + char visStateIntervalCount; + FxElemVelStateSample* velSamples; + FxElemVisStateSample* visSamples; + FxElemDefVisuals visuals; + Bounds collBounds; + FxEffectDefRef* effectOnImpact; + FxEffectDefRef* effectOnDeath; + FxEffectDefRef* effectEmitted; + FxFloatRange emitDist; + FxFloatRange emitDistVariance; + FxElemExtendedDefPtr extended; + char sortOrder; + char lightingFrac; + char useItemClip; + char fadeInfo; + }; + + struct FxEffectDef + { + const char* name; + int flags; + int totalSize; + int msecLoopingLife; + int elemDefCountLooping; + int elemDefCountOneShot; + int elemDefCountEmission; + FxElemDef* elemDefs; + }; + + // GfxWorld +#pragma pack(push, 4) + struct GfxLightImage + { + GfxImage* image; + char samplerState; + }; +#pragma pack(pop) + + struct GfxLightDef + { + const char* name; + GfxLightImage attenuation; + int lmapLookupStart; + }; + + struct GfxSky + { + int skySurfCount; + std::uint32_t* skyStartSurfs; + GfxImage* skyImage; + char skySamplerState; + char pad[3]; + }; + + struct GfxWorldDpvsPlanes + { + int cellCount; + cplane_s* planes; + unsigned __int16* nodes; + unsigned char* sceneEntCellBits; //Size = cellCount << 11 + }; + + struct GfxAabbTree + { + union + { + Bounds bounds; + + struct + { + float mins[3]; // 12 + float maxs[3]; // 12 + }; + }; + + int unkn; + unsigned __int16 childCount; // 2 + unsigned __int16 surfaceCount; // 2 + unsigned __int16 startSurfIndex; // 2 + unsigned __int16 smodelIndexCount; // 2 + unsigned __int16* smodelIndexes; // 4 + int childrenOffset; // 4 + }; // Size: 0x2C + + struct GfxCellTree + { + // Best struct ever + GfxAabbTree* aabbtree; + }; + +#pragma pack(push, 4) + struct GfxPortal; + struct GfxPortalWritable + { + bool isQueued; + bool isAncestor; + char recursionDepth; + char hullPointCount; + float(*hullPoints)[2]; + GfxPortal* queuedParent; + }; + + struct DpvsPlane + { + float coeffs[4]; + }; + + struct GfxPortal + { + GfxPortalWritable writable; + DpvsPlane plane; + float(*vertices)[3]; + unsigned __int16 cellIndex; + char vertexCount; + float hullAxis[2][3]; + }; +#pragma pack(pop) + +#pragma pack(push, 4) + struct GfxCell + { + union + { + Bounds bounds; + + struct + { + float mins[3]; + float maxs[3]; + }; + }; + + int portalCount; + GfxPortal* portals; + char reflectionProbeCount; + char* reflectionProbes; + }; +#pragma pack(pop) + + struct GfxCell_IW5 + { + float mins[3]; + float maxs[3]; + int portalCount; + GfxPortal* portals; + char reflectionProbeCount; + char* reflectionProbes; + char reflectionProbeReferenceCount; + char* reflectionProbeReferences; + }; + + struct GfxReflectionProbe + { + float offset[3]; + }; + + typedef char GfxTexture[0x04]; + + struct GfxLightmapArray + { + GfxImage* primary; + GfxImage* secondary; + }; + + struct GfxWorldVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + float texCoord[2]; + float lmapCoord[2]; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct GfxWorldVertexData + { + GfxWorldVertex* vertices; + void* worldVb; // D3DVertexBuffer + }; + + struct GfxWorldVertexLayerData + { + char* data; + void* layerVb; // D3DVertexBuffer + }; + + struct GfxWorldDraw + { + union + { + char _portpad0[16]; + + struct + { + unsigned int reflectionProbeCount; // 4 + GfxImage* * reflectionImages; // 4 + GfxReflectionProbe* reflectionProbes; // 4 + GfxTexture* reflectionProbeTextures; //Count = reflectionProbeCount // 4 + }; + }; + + union + { + char _portpad1[56]; + + struct + { + int lightmapCount; // 4 + GfxLightmapArray* lightmaps; // 4 + GfxTexture* lightmapPrimaryTextures; //Count = lightmapCount // 4 + GfxTexture* lightmapSecondaryTextures; //Count = lightmapCount // 4 + GfxImage* skyImage; // 4 + GfxImage* outdoorImage; // 4 + unsigned int vertexCount; // 4 + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + int indexCount; + unsigned __int16* indices; + }; + }; + }; + + struct GfxWorldDraw_IW5 + { + union + { + char _portpad0[16]; + + struct + { + unsigned int reflectionProbeCount; // 4 + GfxImage* * reflectionImages; // 4 + GfxReflectionProbe* reflectionProbes; // 4 + GfxTexture* reflectionProbeTextures; //Count = reflectionProbeCount // 4 + }; + }; + + char cancer[12]; + + union + { + char _portpad1[56]; + + struct + { + int lightmapCount; // 4 + GfxLightmapArray* lightmaps; // 4 + GfxTexture* lightmapPrimaryTextures; //Count = lightmapCount // 4 + GfxTexture* lightmapSecondaryTextures; //Count = lightmapCount // 4 + GfxImage* skyImage; // 4 + GfxImage* outdoorImage; // 4 + unsigned int vertexCount; // 4 + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + int indexCount; + unsigned __int16* indices; + }; + }; + }; + + struct GfxLightGridEntry + { + unsigned __int16 colorsIndex; + char primaryLightIndex; + char needsTrace; + }; + + struct GfxLightGridColors + { + char rgb[56][3]; + }; + + struct GfxLightGrid + { + bool hasLightRegions; // 4 + unsigned int sunPrimaryLightIndex; // 4 + unsigned __int16 mins[3]; // 6 + unsigned __int16 maxs[3]; // 6 + unsigned int rowAxis; // 4 + unsigned int colAxis; // 4 + unsigned __int16* rowDataStart; + // Size: (varGfxLightGrid->maxs[varGfxLightGrid->rowAxis] - varGfxLightGrid->mins[varGfxLightGrid->rowAxis] + 1) * 2 + unsigned int rawRowDataSize; + char* rawRowData; + unsigned int entryCount; + GfxLightGridEntry* entries; + unsigned int colorCount; + GfxLightGridColors* colors; + }; + + struct GfxBrushModelWritable + { + Bounds bounds; + }; + + struct GfxBrushModel + { + GfxBrushModelWritable writable; + Bounds bounds; + float radius; + unsigned short surfaceCount; + unsigned short startSurfIndex; + unsigned short surfaceCountNoDecal; + }; + + struct MaterialMemory + { + Material* material; + int memory; + }; + + struct sunflare_t + { + bool hasValidData; + Material* spriteMaterial; + Material* flareMaterial; + float spriteSize; + float flareMinSize; + float flareMinDot; + float flareMaxSize; + float flareMaxDot; + float flareMaxAlpha; + int flareFadeInTime; + int flareFadeOutTime; + float blindMinDot; + float blindMaxDot; + float blindMaxDarken; + int blindFadeInTime; + int blindFadeOutTime; + float glareMinDot; + float glareMaxDot; + float glareMaxLighten; + int glareFadeInTime; + int glareFadeOutTime; + float sunFxPosition[3]; + }; + + struct XModelDrawInfo + { + unsigned __int16 lod; + unsigned __int16 surfId; + }; + + struct GfxSceneDynModel + { + XModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct BModelDrawInfo + { + unsigned __int16 surfId; + }; + + struct GfxSceneDynBrush + { + BModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct GfxShadowGeometry + { + unsigned __int16 surfaceCount; + unsigned __int16 smodelCount; + unsigned __int16* sortedSurfIndex; + unsigned __int16* smodelIndex; + }; + + struct GfxLightRegionAxis + { + float dir[3]; + float midPoint; + float halfSize; + }; + + struct GfxLightRegionHull + { + float kdopMidPoint[9]; + float kdopHalfSize[9]; + unsigned int axisCount; + GfxLightRegionAxis* axis; + }; + + struct GfxLightRegion + { + unsigned int hullCount; + GfxLightRegionHull* hulls; + }; + + struct GfxStaticModelInst + { + union + { + Bounds bounds; + + struct + { + float mins[3]; + float maxs[3]; + }; + }; + float lightingOrigin[3]; + }; + + struct srfTriangles_t + { + unsigned int vertexLayerData; + unsigned int firstVertex; + unsigned __int16 vertexCount; + unsigned __int16 triCount; + unsigned int baseIndex; + }; + + struct GfxSurface + { + srfTriangles_t tris; + Material* material; + char lightmapIndex; + char reflectionProbeIndex; + char primaryLightIndex; + char flags; + }; + + struct GfxCullGroup + { + union + { + Bounds bounds; + + struct + { + float mins[3]; + float maxs[3]; + }; + }; + }; + + struct GfxDrawSurfFields + { + unsigned __int64 objectId : 16; + unsigned __int64 reflectionProbeIndex : 8; + unsigned __int64 hasGfxEntIndex : 1; + unsigned __int64 customIndex : 5; + unsigned __int64 materialSortedIndex : 12; + unsigned __int64 prepass : 2; + unsigned __int64 useHeroLighting : 1; + unsigned __int64 sceneLightIndex : 8; + unsigned __int64 surfType : 4; + unsigned __int64 primarySortKey : 6; + unsigned __int64 unused : 1; + }; + + union GfxDrawSurf + { + GfxDrawSurfFields fields; + unsigned __int64 packed; + }; + static_assert(sizeof(GfxDrawSurf) == 8); + +#pragma pack(push, 4) + struct GfxPackedPlacement + { + float origin[3]; + float axis[3][3]; + float scale; + }; + + struct GfxStaticModelDrawInst + { + GfxPackedPlacement placement; + XModel *model; + unsigned __int16 cullDist; + unsigned __int16 lightingHandle; + char reflectionProbeIndex; + char primaryLightIndex; + char flags; + char firstMtlSkinIndex; + GfxColor groundLighting; + unsigned __int16 cacheId[4]; + }; + + struct GfxWorldDpvsDynamic + { + unsigned int dynEntClientWordCount[2]; + unsigned int dynEntClientCount[2]; + unsigned int* dynEntCellBits[2]; + char* dynEntVisData[2][3]; + }; + +#pragma pack(pop) + + struct GfxWorldDpvsStatic + { + unsigned int smodelCount; + unsigned int staticSurfaceCount; + unsigned int staticSurfaceCountNoDecal; + unsigned int litOpaqueSurfsBegin; + unsigned int litOpaqueSurfsEnd; + unsigned int litTransSurfsBegin; + unsigned int litTransSurfsEnd; + unsigned int shadowCasterSurfsBegin; + unsigned int shadowCasterSurfsEnd; + unsigned int emissiveSurfsBegin; + unsigned int emissiveSurfsEnd; + unsigned int smodelVisDataCount; + unsigned int surfaceVisDataCount; + char* smodelVisData[3]; + char* surfaceVisData[3]; + unsigned __int16* sortedSurfIndex; + GfxStaticModelInst* smodelInsts; + GfxSurface* surfaces; + GfxCullGroup* surfacesBounds; + GfxStaticModelDrawInst* smodelDrawInsts; + GfxDrawSurf* surfaceMaterials; + unsigned int* surfaceCastsSunShadow; + volatile int usageCount; + }; + + struct GfxHeroLight + { + char type; + char pad[3]; + float color[3]; + float dir[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + int exponent; + }; + + struct GfxCellTreeCount + { + int aabbTreeCount; + }; + +#pragma pack(push, 4) + struct GfxWorld + { + const char* name; // 4 + const char* baseName; // 4 + int planeCount; // 4 + int nodeCount; // 4 // = 16 + int surfaceCount; // 4 + unsigned int skyCount; // 4 + GfxSky* skies; // 4 + int sunPrimaryLightIndex; // 4 // = 32 + int primaryLightCount; // 4 + unsigned int sortKeyLitDecal; + unsigned int sortKeyEffectDecal; + unsigned int sortKeyEffectAuto; + unsigned int sortKeyDistortion; + GfxWorldDpvsPlanes dpvsPlanes; // 16 + GfxCellTreeCount* aabbTreeCounts; // Size: 4 * dpvsPlanes.cellCount // 4 + GfxCellTree* aabbTree; // 4 + GfxCell* cells; // 4 // = 80 + GfxWorldDraw draw; // 72 + GfxLightGrid lightGrid; // 56 // = 208 + int modelCount; // 4 + GfxBrushModel* models; // 4 // = 216 + union + { + Bounds bounds; + + struct + { + float mins[3]; // 12 + float maxs[3]; // 12 + }; + }; + + unsigned int checksum; // 4 + int materialMemoryCount; // 4 // = 248 + MaterialMemory* materialMemory; // 4 + sunflare_t sun; // 96 // = 348 + float outdoorLookupMatrix[4][4]; // 64 + GfxImage* outdoorImage; // 4 // = 416 + unsigned int* cellCasterBits[2]; // 8 + GfxSceneDynModel* sceneDynModel; // 4 + GfxSceneDynBrush* sceneDynBrush; // 4 // = 432 + unsigned char* primaryLightEntityShadowVis; + unsigned int* primaryLightDynEntShadowVis[2]; + char* primaryLightForModelDynEnt; + GfxShadowGeometry* shadowGeom; + GfxLightRegion* lightRegion; + GfxWorldDpvsStatic dpvs; + GfxWorldDpvsDynamic dpvsDyn; + unsigned int mapVtxChecksum; + unsigned int heroLightCount; + GfxHeroLight* heroLights; + char fogTypesAllowed; + char pad2[3]; + }; +#pragma pack(pop) + + namespace alpha + { + struct GfxWorldVertexData + { + GfxWorldVertex* vertices; + D3DVertexBuffer worldVb; + }; + + struct GfxWorldVertexLayerData + { + char* data; + D3DVertexBuffer layerVb; + }; + + struct GfxWorldDraw + { + unsigned int reflectionProbeCount; + GfxImage** reflectionProbes; + GfxReflectionProbe* reflectionProbeOrigins; + GfxTexture* reflectionProbeTextures; + int lightmapCount; + GfxLightmapArray* lightmaps; + GfxTexture* lightmapPrimaryTextures; + GfxTexture* lightmapSecondaryTextures; + GfxImage* lightmapOverridePrimary; + GfxImage* lightmapOverrideSecondary; + unsigned int vertexCount; + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + unsigned int indexCount; + unsigned __int16* indices; + D3DIndexBuffer indexBuffer; + }; + + struct GfxWorldDpvsStatic + { + unsigned int smodelCount; + unsigned int staticSurfaceCount; + unsigned int litOpaqueSurfsBegin; + unsigned int litOpaqueSurfsEnd; + unsigned int litTransSurfsBegin; + unsigned int litTransSurfsEnd; + unsigned int shadowCasterSurfsBegin; + unsigned int shadowCasterSurfsEnd; + unsigned int emissiveSurfsBegin; + unsigned int emissiveSurfsEnd; + unsigned int smodelVisDataCount; + unsigned int surfaceVisDataCount; + char* smodelVisData[3]; + char* surfaceVisData[3]; + unsigned __int16* sortedSurfIndex; + GfxStaticModelInst* smodelInsts; + GfxSurface* surfaces; + GfxCullGroup* surfacesBounds; + GfxStaticModelDrawInst* smodelDrawInsts; + GfxDrawSurf* surfaceMaterials; + unsigned int* surfaceCastsSunShadow; + volatile int usageCount; + }; + + struct __declspec(align(4)) GfxWorld + { + const char* name; + const char* baseName; + int planeCount; + int nodeCount; + unsigned int surfaceCount; + int skyCount; + GfxSky* skies; + unsigned int lastSunPrimaryLightIndex; + unsigned int primaryLightCount; + unsigned int sortKeyLitDecal; + unsigned int sortKeyEffectDecal; + unsigned int sortKeyEffectAuto; + unsigned int sortKeyDistortion; + GfxWorldDpvsPlanes dpvsPlanes; + GfxCellTreeCount* aabbTreeCounts; + GfxCellTree* aabbTrees; + GfxCell* cells; + GfxWorldDraw draw; + GfxLightGrid lightGrid; + int modelCount; + GfxBrushModel* models; + Bounds bounds; + unsigned int checksum; + int materialMemoryCount; + MaterialMemory* materialMemory; + sunflare_t sun; + float outdoorLookupMatrix[4][4]; + GfxImage* outdoorImage; + unsigned int* cellCasterBits; + unsigned int* cellHasSunLitSurfsBits; + GfxSceneDynModel* sceneDynModel; + GfxSceneDynBrush* sceneDynBrush; + unsigned int* primaryLightEntityShadowVis; + unsigned int* primaryLightDynEntShadowVis[2]; + char* nonSunPrimaryLightForModelDynEnt; + GfxShadowGeometry* shadowGeom; + GfxLightRegion* lightRegion; + GfxWorldDpvsStatic dpvs; + GfxWorldDpvsDynamic dpvsDyn; + unsigned int mapVtxChecksum; + unsigned int heroOnlyLightCount; + GfxHeroLight* heroOnlyLights; + char fogTypesAllowed; + }; + } + +#pragma pack(push, 4) + struct cStaticModel_s + { + XModel* xmodel; + float origin[3]; + float invScaledAxis[3][3]; + Bounds absBounds; + }; + + struct dmaterial_t + { + char* material; + int surfaceFlags; + int contentFlags; + }; + + struct cNode_t + { + cplane_s* plane; + __int16 children[2]; + }; + + struct cLeaf_t + { + unsigned __int16 firstCollAabbIndex; + unsigned __int16 collAabbCount; + int brushContents; + int terrainContents; + Bounds bounds; + int leafBrushNode; + }; + + struct cLeafBrushNodeLeaf_t + { + unsigned __int16* brushes; + }; + + struct cLeafBrushNodeChildren_t + { + unsigned __int16 childOffset[6]; + }; + + union cLeafBrushNodeData_t + { + cLeafBrushNodeLeaf_t leaf; + cLeafBrushNodeChildren_t children; + }; + + struct cLeafBrushNode_s + { + char axis; + short leafBrushCount; + int contents; + cLeafBrushNodeData_t data; + }; + + struct CollisionBorder + { + float distEq[3]; + float zBase; + float zSlope; + float start; + float length; + }; + + struct CollisionPartition + { + char triCount; + char borderCount; + int firstTri; + CollisionBorder* borders; + }; + + union CollisionAabbTreeIndex + { + int firstChildIndex; + int partitionIndex; + }; + + struct CollisionAabbTree + { + float origin[3]; + unsigned __int16 materialIndex; + unsigned __int16 childCount; + float halfSize[3]; + CollisionAabbTreeIndex u; + }; + + enum DynEntityType + { + DYNENT_TYPE_INVALID = 0x0, + DYNENT_TYPE_CLUTTER = 0x1, + DYNENT_TYPE_DESTRUCT = 0x2, + DYNENT_TYPE_COUNT = 0x3, + }; + + struct GfxPlacement + { + float quat[4]; + float origin[3]; + }; + + struct DynEntityDef + { + DynEntityType type; + GfxPlacement pose; + XModel* xModel; + unsigned __int16 brushModel; + unsigned __int16 physicsBrushModel; + void* destroyFx; // FxEffectDef + PhysPreset* physPreset; + int health; + PhysMass mass; + //char *unknown; + }; + + struct DynEntityPose + { + GfxPlacement pose; + float radius; + }; + + struct DynEntityClient + { + int physObjId; + unsigned __int16 flags; + unsigned __int16 lightingHandle; + int health; + }; + + struct DynEntityColl + { + unsigned __int16 sector; + unsigned __int16 nextEntInSector; + float linkMins[2]; + float linkMaxs[2]; + }; + + struct unknownInternalClipMapStruct1 + { + int planeCount; + cplane_s* planes; + unsigned int numMaterials; + dmaterial_t* materials; + unsigned int numBrushSides; + cbrushside_t* brushsides; + unsigned int numBrushEdges; + cbrushedge_t* brushEdges; + unsigned int leafbrushNodesCount; + cLeafBrushNode_s* leafbrushNodes; + unsigned int numLeafBrushes; + unsigned __int16* leafbrushes; + unsigned __int16 numBrushes; + cbrush_t* brushes; + char* unknown1; //Size = ((numBrushes << 1) + numBrushes) << 3 + unsigned int* leafsurfaces; //Count = numBrushes + }; + + struct unknownInternalClipMapStruct2 + { + char* unknownString; + char unknown[0x10]; + }; + + struct unknownInternalClipMapStruct3 + { + char _pad[28]; + unknownInternalClipMapStruct1* unkArrayPtr; + char _pad2[40]; + }; + + struct cmodel_t + { + union + { + char _portpad0[28]; + + struct + { + Bounds bounds; + float radius; + }; + }; + + union + { + char _portpad1[40]; + + struct + { + cLeaf_t leaf; + }; + }; + }; + + struct SModelAabbNode + { + Bounds bounds; + short firstChild; + short childCount; + }; + + struct DynEntityDef_IW5 + { + DynEntityType type; + GfxPlacement pose; + XModel* xModel; + unsigned __int16 brushModel; + unsigned __int16 physicsBrushModel; + void* destroyFx; // FxEffectDef + PhysPreset* physPreset; + int health; + void* hinge; + PhysMass mass; + }; + + struct cmodel_t_IW5 + { + Bounds bounds; + float radius; + void* info; + cLeaf_t leaf; + }; + + struct clipMap_t + { + const char* name; // 4 + int isInUse; // 4 + int numCPlanes; // +8 + cplane_s* cPlanes; // sizeof 20, +12 + int numStaticModels; // +16 + cStaticModel_s* staticModelList; // sizeof 76, +20 + int numMaterials; // +24 + dmaterial_t* materials; // sizeof 12 with a string (possibly name?), +28 + int numCBrushSides; // +32 + cbrushside_t* cBrushSides; // sizeof 8, +36 + int numCBrushEdges; // +40 NOT USED IN T5 + cbrushedge_t* cBrushEdges; // +44 NOT USED IN T5 + int numCNodes; // +48 + cNode_t* cNodes; // sizeof 8, +52 + int numCLeaf; // +56 + cLeaf_t* cLeaf; // +60 + int numCLeafBrushNodes; // +64 + cLeafBrushNode_s* cLeafBrushNodes; // +68 + int numLeafBrushes; // +72 + short* leafBrushes; // +76 + int numLeafSurfaces; // +80 + int* leafSurfaces; // +84 + int numVerts; // +88 + VecInternal<3>* verts; // +92 + int numTriIndices; // +96 + short* triIndices; // +100 + char* triEdgeIsWalkable; // +104 + int numCollisionBorders; // +108 + CollisionBorder* collisionBorders; // sizeof 28, +112 + int numCollisionPartitions; // +116 + CollisionPartition* collisionPartitions; // sizeof 12, +120 + int numCollisionAABBTrees; // +124 + CollisionAabbTree* collisionAABBTrees; // sizeof 32, +128 + int numCModels; // +132 + cmodel_t* cModels; // sizeof 68, +136 + short numBrushes; // +140 + short pad2; // +142 + cbrush_t* brushes; // sizeof 36, +144 + Bounds* brushBounds; // same count as cBrushes, +148 + int* brushContents; // same count as cBrushes, +152 + MapEnts* mapEnts; // +156 + short smodelNodeCount; // +160 + short pad3; + SModelAabbNode* smodelNodes; // +164 + unsigned __int16 dynEntCount[2]; + DynEntityDef* dynEntDefList[2]; + DynEntityPose* dynEntPoseList[2]; + DynEntityClient* dynEntClientList[2]; + DynEntityColl* dynEntCollList[2]; + unsigned int checksum; + char unknown5[48]; + }; // +256 +#pragma pack(pop) + + // StructuredDataDef + enum StructuredDataTypeCategory + { + DATA_INT = 0x0, + DATA_BYTE = 0x1, + DATA_BOOL = 0x2, + DATA_STRING = 0x3, + DATA_ENUM = 0x4, + DATA_STRUCT = 0x5, + DATA_INDEXED_ARRAY = 0x6, + DATA_ENUM_ARRAY = 0x7, + DATA_FLOAT = 0x8, + DATA_SHORT = 0x9, + DATA_COUNT = 0xA, + }; +#pragma pack(push,4) + struct StructuredDataEnumEntry + { + const char* name; + unsigned __int16 index; + }; +#pragma pack(pop) + struct StructuredDataEnum + { + int entryCount; + int reservedEntryCount; + StructuredDataEnumEntry* entries; + }; + + union StructuredDataTypeUnion + { + unsigned int stringDataLength; + int enumIndex; + int structIndex; + int indexedArrayIndex; + int enumedArrayIndex; + }; + + struct StructuredDataType + { + StructuredDataTypeCategory type; + StructuredDataTypeUnion u; + }; +#pragma pack(push,4) + struct StructuredDataStructProperty + { + const char* name; + StructuredDataType item; + int offset; + }; +#pragma pack(pop) + struct StructuredDataStruct + { + int propertyCount; + StructuredDataStructProperty* properties; + int size; + unsigned int bitOffset; + }; + + struct StructuredDataIndexedArray + { + int arraySize; + StructuredDataType elementType; + unsigned int elementSize; + }; + + struct StructuredDataEnumedArray + { + int enumIndex; + StructuredDataType elementType; + unsigned int elementSize; + }; + + struct StructuredDataDef + { + int version; + unsigned int formatChecksum; + int enumCount; + StructuredDataEnum* enums; + int structCount; + StructuredDataStruct* structs; + int indexedArrayCount; + StructuredDataIndexedArray* indexedArrays; + int enumedArrayCount; + StructuredDataEnumedArray* enumedArrays; + StructuredDataType rootType; + unsigned int size; + }; + + struct StructuredDataDefSet + { + const char* name; + unsigned int defCount; + StructuredDataDef* defs; + }; + + struct AddonMapEnts + { + const char* name; + char* entityString; + int numEntityChars; + MapTriggers trigger; + }; + + union XAssetHeader + { + RawFile* rawfile; + VertexDecl* vertexdecl; + PixelShader* pixelshader; + VertexShader* vertexshader; + MaterialTechniqueSet* techset; + GfxImage* gfximage; + GfxImage* image; + Material* material; + PhysPreset* physpreset; + PhysCollmap* physcollmap; + PhysCollmap* phys_collmap; + XAnimParts* xanimparts; + XAnimParts* xanim; + XModelSurfs* xsurface; + XModelSurfs* xmodelsurfs; + clipMap_t* clipmap; + clipMap_t* col_map_mp; + GfxWorld* gfxworld; + GfxWorld* gfx_map; + GameWorldMp* gameworldmp; + GameWorldSp* gameworldsp; + GameWorldMp* game_map_mp; + GameWorldSp* game_map_sp; + FxWorld* fxworld; + FxWorld* fx_map; + MapEnts* mapents; + MapEnts* map_ents; + XModel* xmodel; + StringTable* stringtable; + ComWorld* comworld; + ComWorld* com_map; + LocalizeEntry* localize; + SndCurve* soundcurve; + SndCurve* sndcurve; + TracerDef* tracer; + Font_s* font; + WeaponCompleteDef* weapon; + FxEffectDef* fx; + snd_alias_list_t* sound; + LoadedSound* loadedsound; + LoadedSound* loaded_sound; + StructuredDataDefSet* structureddatadef; + GfxLightDef* lightdef; + AddonMapEnts* addon_map_ents; + void* data; + }; + + enum MaterialTechniqueType + { + TECHNIQUE_DEPTH_PREPASS = 0x0, + TECHNIQUE_BUILD_FLOAT_Z = 0x1, + TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2, + TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3, + TECHNIQUE_UNLIT = 0x4, + TECHNIQUE_EMISSIVE = 0x5, + TECHNIQUE_EMISSIVE_DFOG = 0x6, + TECHNIQUE_EMISSIVE_SHADOW = 0x7, + TECHNIQUE_EMISSIVE_SHADOW_DFOG = 0x8, + TECHNIQUE_LIT_BEGIN = 0x9, + TECHNIQUE_LIT = 0x9, + TECHNIQUE_LIT_DFOG = 0xA, + TECHNIQUE_LIT_SUN = 0xB, + TECHNIQUE_LIT_SUN_DFOG = 0xC, + TECHNIQUE_LIT_SUN_SHADOW = 0xD, + TECHNIQUE_LIT_SUN_SHADOW_DFOG = 0xE, + TECHNIQUE_LIT_SPOT = 0xF, + TECHNIQUE_LIT_SPOT_DFOG = 0x10, + TECHNIQUE_LIT_SPOT_SHADOW = 0x11, + TECHNIQUE_LIT_SPOT_SHADOW_DFOG = 0x12, + TECHNIQUE_LIT_OMNI = 0x13, + TECHNIQUE_LIT_OMNI_DFOG = 0x14, + TECHNIQUE_LIT_OMNI_SHADOW = 0x15, + TECHNIQUE_LIT_OMNI_SHADOW_DFOG = 0x16, + TECHNIQUE_LIT_INSTANCED = 0x17, + TECHNIQUE_LIT_INSTANCED_DFOG = 0x18, + TECHNIQUE_LIT_INSTANCED_SUN = 0x19, + TECHNIQUE_LIT_INSTANCED_SUN_DFOG = 0x1A, + TECHNIQUE_LIT_INSTANCED_SUN_SHADOW = 0x1B, + TECHNIQUE_LIT_INSTANCED_SUN_SHADOW_DFOG = 0x1C, + TECHNIQUE_LIT_INSTANCED_SPOT = 0x1D, + TECHNIQUE_LIT_INSTANCED_SPOT_DFOG = 0x1E, + TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW = 0x1F, + TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW_DFOG = 0x20, + TECHNIQUE_LIT_INSTANCED_OMNI = 0x21, + TECHNIQUE_LIT_INSTANCED_OMNI_DFOG = 0x22, + TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW = 0x23, + TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW_DFOG = 0x24, + TECHNIQUE_LIT_END = 0x25, + TECHNIQUE_LIGHT_SPOT = 0x25, + TECHNIQUE_LIGHT_OMNI = 0x26, + TECHNIQUE_LIGHT_SPOT_SHADOW = 0x27, + TECHNIQUE_FAKELIGHT_NORMAL = 0x28, + TECHNIQUE_FAKELIGHT_VIEW = 0x29, + TECHNIQUE_SUNLIGHT_PREVIEW = 0x2A, + TECHNIQUE_CASE_TEXTURE = 0x2B, + TECHNIQUE_WIREFRAME_SOLID = 0x2C, + TECHNIQUE_WIREFRAME_SHADED = 0x2D, + TECHNIQUE_DEBUG_BUMPMAP = 0x2E, + TECHNIQUE_DEBUG_BUMPMAP_INSTANCED = 0x2F, + TECHNIQUE_COUNT = 0x30, + TECHNIQUE_TOTAL_COUNT = 0x31, + TECHNIQUE_NONE = 0x32, + }; + + enum $949C82572B6A70952C455E749D3B3D56 + { + CONST_SRC_CODE_MAYBE_DIRTY_PS_BEGIN = 0x0, + CONST_SRC_CODE_LIGHT_POSITION = 0x0, + CONST_SRC_CODE_LIGHT_DIFFUSE = 0x1, + CONST_SRC_CODE_LIGHT_SPECULAR = 0x2, + CONST_SRC_CODE_LIGHT_SPOTDIR = 0x3, + CONST_SRC_CODE_LIGHT_SPOTFACTORS = 0x4, + CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT = 0x5, + CONST_SRC_CODE_PARTICLE_CLOUD_COLOR = 0x6, + CONST_SRC_CODE_GAMETIME = 0x7, + CONST_SRC_CODE_MAYBE_DIRTY_PS_END = 0x8, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_BEGIN = 0x8, + CONST_SRC_CODE_PIXEL_COST_FRACS = 0x8, + CONST_SRC_CODE_PIXEL_COST_DECODE = 0x9, + CONST_SRC_CODE_FILTER_TAP_0 = 0xA, + CONST_SRC_CODE_FILTER_TAP_1 = 0xB, + CONST_SRC_CODE_FILTER_TAP_2 = 0xC, + CONST_SRC_CODE_FILTER_TAP_3 = 0xD, + CONST_SRC_CODE_FILTER_TAP_4 = 0xE, + CONST_SRC_CODE_FILTER_TAP_5 = 0xF, + CONST_SRC_CODE_FILTER_TAP_6 = 0x10, + CONST_SRC_CODE_FILTER_TAP_7 = 0x11, + CONST_SRC_CODE_COLOR_MATRIX_R = 0x12, + CONST_SRC_CODE_COLOR_MATRIX_G = 0x13, + CONST_SRC_CODE_COLOR_MATRIX_B = 0x14, + CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET = 0x15, + CONST_SRC_CODE_RENDER_TARGET_SIZE = 0x16, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_END = 0x17, + CONST_SRC_CODE_FIXED_PS_BEGIN = 0x17, + CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR = 0x17, + CONST_SRC_CODE_DOF_EQUATION_SCENE = 0x18, + CONST_SRC_CODE_DOF_LERP_SCALE = 0x19, + CONST_SRC_CODE_DOF_LERP_BIAS = 0x1A, + CONST_SRC_CODE_DOF_ROW_DELTA = 0x1B, + CONST_SRC_CODE_MOTION_MATRIX_X = 0x1C, + CONST_SRC_CODE_MOTION_MATRIX_Y = 0x1D, + CONST_SRC_CODE_MOTION_MATRIX_W = 0x1E, + CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION = 0x1F, + CONST_SRC_CODE_SHADOWMAP_SCALE = 0x20, + CONST_SRC_CODE_ZNEAR = 0x21, + CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE = 0x22, + CONST_SRC_CODE_DEBUG_BUMPMAP = 0x23, + CONST_SRC_CODE_MATERIAL_COLOR = 0x24, + CONST_SRC_CODE_FOG = 0x25, + CONST_SRC_CODE_FOG_COLOR_LINEAR = 0x26, + CONST_SRC_CODE_FOG_COLOR_GAMMA = 0x27, + CONST_SRC_CODE_FOG_SUN_CONSTS = 0x28, + CONST_SRC_CODE_FOG_SUN_COLOR_LINEAR = 0x29, + CONST_SRC_CODE_FOG_SUN_COLOR_GAMMA = 0x2A, + CONST_SRC_CODE_FOG_SUN_DIR = 0x2B, + CONST_SRC_CODE_GLOW_SETUP = 0x2C, + CONST_SRC_CODE_GLOW_APPLY = 0x2D, + CONST_SRC_CODE_COLOR_BIAS = 0x2E, + CONST_SRC_CODE_COLOR_TINT_BASE = 0x2F, + CONST_SRC_CODE_COLOR_TINT_DELTA = 0x30, + CONST_SRC_CODE_COLOR_TINT_QUADRATIC_DELTA = 0x31, + CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS = 0x32, + CONST_SRC_CODE_ENVMAP_PARMS = 0x33, + CONST_SRC_CODE_SUN_SHADOWMAP_PIXEL_ADJUST = 0x34, + CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST = 0x35, + CONST_SRC_CODE_COMPOSITE_FX_DISTORTION = 0x36, + CONST_SRC_CODE_POSTFX_FADE_EFFECT = 0x37, + CONST_SRC_CODE_VIEWPORT_DIMENSIONS = 0x38, + CONST_SRC_CODE_FRAMEBUFFER_READ = 0x39, + CONST_SRC_CODE_FIXED_PS_END = 0x3A, + CONST_SRC_CODE_NON_PS_BEGIN = 0x3A, + CONST_SRC_CODE_BASE_LIGHTING_COORDS = 0x3A, + CONST_SRC_CODE_LIGHT_PROBE_AMBIENT = 0x3B, + CONST_SRC_CODE_NEARPLANE_ORG = 0x3C, + CONST_SRC_CODE_NEARPLANE_DX = 0x3D, + CONST_SRC_CODE_NEARPLANE_DY = 0x3E, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE = 0x3F, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET = 0x40, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX0 = 0x41, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX1 = 0x42, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX2 = 0x43, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR0 = 0x44, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR1 = 0x45, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR2 = 0x46, + CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM0 = 0x47, + CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM1 = 0x48, + CONST_SRC_CODE_DEPTH_FROM_CLIP = 0x49, + CONST_SRC_CODE_CODE_MESH_ARG_0 = 0x4A, + CONST_SRC_CODE_CODE_MESH_ARG_1 = 0x4B, + CONST_SRC_CODE_CODE_MESH_ARG_LAST = 0x4B, + CONST_SRC_CODE_NON_PS_END = 0x4C, + CONST_SRC_CODE_COUNT_FLOAT4 = 0x4C, + CONST_SRC_FIRST_CODE_MATRIX = 0x4C, + CONST_SRC_CODE_VIEW_MATRIX = 0x4C, + CONST_SRC_CODE_INVERSE_VIEW_MATRIX = 0x4D, + CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX = 0x4E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX = 0x4F, + CONST_SRC_CODE_PROJECTION_MATRIX = 0x50, + CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX = 0x51, + CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX = 0x52, + CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX = 0x53, + CONST_SRC_CODE_VIEW_PROJECTION_MATRIX = 0x54, + CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX = 0x55, + CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x56, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x57, + CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX = 0x58, + CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX = 0x59, + CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x5A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x5B, + CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5C, + CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5D, + CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5F, + CONST_SRC_CODE_WORLD_MATRIX0 = 0x60, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX0 = 0x61, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0 = 0x62, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX0 = 0x63, + CONST_SRC_CODE_WORLD_VIEW_MATRIX0 = 0x64, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX0 = 0x65, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX0 = 0x66, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX0 = 0x67, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x68, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x69, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x6A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x6B, + CONST_SRC_CODE_WORLD_MATRIX1 = 0x6C, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX1 = 0x6D, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX1 = 0x6E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX1 = 0x6F, + CONST_SRC_CODE_WORLD_VIEW_MATRIX1 = 0x70, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX1 = 0x71, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX1 = 0x72, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX1 = 0x73, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x74, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x75, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x76, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x77, + CONST_SRC_CODE_WORLD_MATRIX2 = 0x78, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX2 = 0x79, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX2 = 0x7A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX2 = 0x7B, + CONST_SRC_CODE_WORLD_VIEW_MATRIX2 = 0x7C, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX2 = 0x7D, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX2 = 0x7E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX2 = 0x7F, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x80, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x81, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x82, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x83, + CONST_SRC_TOTAL_COUNT = 0x84, + CONST_SRC_NONE = 0x85, + }; + + struct XAsset + { + XAssetType type; + XAssetHeader ptr; + }; + } + + namespace IW3 + { + enum MaterialTechniqueType + { + TECHNIQUE_DEPTH_PREPASS = 0x0, + TECHNIQUE_BUILD_FLOAT_Z = 0x1, + TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2, + TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3, + TECHNIQUE_UNLIT = 0x4, + TECHNIQUE_EMISSIVE = 0x5, + TECHNIQUE_EMISSIVE_SHADOW = 0x6, + TECHNIQUE_LIT_BEGIN = 0x7, + TECHNIQUE_LIT = 0x7, + TECHNIQUE_LIT_SUN = 0x8, + TECHNIQUE_LIT_SUN_SHADOW = 0x9, + TECHNIQUE_LIT_SPOT = 0xA, + TECHNIQUE_LIT_SPOT_SHADOW = 0xB, + TECHNIQUE_LIT_OMNI = 0xC, + TECHNIQUE_LIT_OMNI_SHADOW = 0xD, + TECHNIQUE_LIT_INSTANCED = 0xE, + TECHNIQUE_LIT_INSTANCED_SUN = 0xF, + TECHNIQUE_LIT_INSTANCED_SUN_SHADOW = 0x10, + TECHNIQUE_LIT_INSTANCED_SPOT = 0x11, + TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW = 0x12, + TECHNIQUE_LIT_INSTANCED_OMNI = 0x13, + TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW = 0x14, + TECHNIQUE_LIT_END = 0x15, + TECHNIQUE_LIGHT_SPOT = 0x15, + TECHNIQUE_LIGHT_OMNI = 0x16, + TECHNIQUE_LIGHT_SPOT_SHADOW = 0x17, + TECHNIQUE_FAKELIGHT_NORMAL = 0x18, + TECHNIQUE_FAKELIGHT_VIEW = 0x19, + TECHNIQUE_SUNLIGHT_PREVIEW = 0x1A, + TECHNIQUE_CASE_TEXTURE = 0x1B, + TECHNIQUE_WIREFRAME_SOLID = 0x1C, + TECHNIQUE_WIREFRAME_SHADED = 0x1D, + TECHNIQUE_SHADOWCOOKIE_CASTER = 0x1E, + TECHNIQUE_SHADOWCOOKIE_RECEIVER = 0x1F, + TECHNIQUE_DEBUG_BUMPMAP = 0x20, + TECHNIQUE_DEBUG_BUMPMAP_INSTANCED = 0x21, + TECHNIQUE_COUNT = 0x22, + TECHNIQUE_TOTAL_COUNT = 0x23, + TECHNIQUE_NONE = 0x24, + }; + + enum $D8CAED6A392807092A83572483F14017 + { + CONST_SRC_CODE_MAYBE_DIRTY_PS_BEGIN = 0x0, + CONST_SRC_CODE_LIGHT_POSITION = 0x0, + CONST_SRC_CODE_LIGHT_DIFFUSE = 0x1, + CONST_SRC_CODE_LIGHT_SPECULAR = 0x2, + CONST_SRC_CODE_LIGHT_SPOTDIR = 0x3, + CONST_SRC_CODE_LIGHT_SPOTFACTORS = 0x4, + CONST_SRC_CODE_NEARPLANE_ORG = 0x5, + CONST_SRC_CODE_NEARPLANE_DX = 0x6, + CONST_SRC_CODE_NEARPLANE_DY = 0x7, + CONST_SRC_CODE_SHADOW_PARMS = 0x8, + CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET = 0x9, + CONST_SRC_CODE_RENDER_TARGET_SIZE = 0xA, + CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT = 0xB, + CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR = 0xC, + CONST_SRC_CODE_DOF_EQUATION_SCENE = 0xD, + CONST_SRC_CODE_DOF_LERP_SCALE = 0xE, + CONST_SRC_CODE_DOF_LERP_BIAS = 0xF, + CONST_SRC_CODE_DOF_ROW_DELTA = 0x10, + CONST_SRC_CODE_PARTICLE_CLOUD_COLOR = 0x11, + CONST_SRC_CODE_GAMETIME = 0x12, + CONST_SRC_CODE_MAYBE_DIRTY_PS_END = 0x13, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_BEGIN = 0x13, + CONST_SRC_CODE_PIXEL_COST_FRACS = 0x13, + CONST_SRC_CODE_PIXEL_COST_DECODE = 0x14, + CONST_SRC_CODE_FILTER_TAP_0 = 0x15, + CONST_SRC_CODE_FILTER_TAP_1 = 0x16, + CONST_SRC_CODE_FILTER_TAP_2 = 0x17, + CONST_SRC_CODE_FILTER_TAP_3 = 0x18, + CONST_SRC_CODE_FILTER_TAP_4 = 0x19, + CONST_SRC_CODE_FILTER_TAP_5 = 0x1A, + CONST_SRC_CODE_FILTER_TAP_6 = 0x1B, + CONST_SRC_CODE_FILTER_TAP_7 = 0x1C, + CONST_SRC_CODE_COLOR_MATRIX_R = 0x1D, + CONST_SRC_CODE_COLOR_MATRIX_G = 0x1E, + CONST_SRC_CODE_COLOR_MATRIX_B = 0x1F, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_END = 0x20, + CONST_SRC_CODE_NEVER_DIRTY_PS_BEGIN = 0x20, + CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION = 0x20, + CONST_SRC_CODE_SHADOWMAP_SCALE = 0x21, + CONST_SRC_CODE_ZNEAR = 0x22, + CONST_SRC_CODE_SUN_POSITION = 0x23, + CONST_SRC_CODE_SUN_DIFFUSE = 0x24, + CONST_SRC_CODE_SUN_SPECULAR = 0x25, + CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE = 0x26, + CONST_SRC_CODE_DEBUG_BUMPMAP = 0x27, + CONST_SRC_CODE_MATERIAL_COLOR = 0x28, + CONST_SRC_CODE_FOG = 0x29, + CONST_SRC_CODE_FOG_COLOR = 0x2A, + CONST_SRC_CODE_GLOW_SETUP = 0x2B, + CONST_SRC_CODE_GLOW_APPLY = 0x2C, + CONST_SRC_CODE_COLOR_BIAS = 0x2D, + CONST_SRC_CODE_COLOR_TINT_BASE = 0x2E, + CONST_SRC_CODE_COLOR_TINT_DELTA = 0x2F, + CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS = 0x30, + CONST_SRC_CODE_ENVMAP_PARMS = 0x31, + CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST = 0x32, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE = 0x33, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET = 0x34, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX = 0x35, + CONST_SRC_CODE_DEPTH_FROM_CLIP = 0x36, + CONST_SRC_CODE_CODE_MESH_ARG_0 = 0x37, + CONST_SRC_CODE_CODE_MESH_ARG_1 = 0x38, + CONST_SRC_CODE_CODE_MESH_ARG_LAST = 0x38, + CONST_SRC_CODE_BASE_LIGHTING_COORDS = 0x39, + CONST_SRC_CODE_NEVER_DIRTY_PS_END = 0x3A, + CONST_SRC_CODE_COUNT_FLOAT4 = 0x3A, + CONST_SRC_FIRST_CODE_MATRIX = 0x3A, + CONST_SRC_CODE_WORLD_MATRIX = 0x3A, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX = 0x3B, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX = 0x3C, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX = 0x3D, + CONST_SRC_CODE_VIEW_MATRIX = 0x3E, + CONST_SRC_CODE_INVERSE_VIEW_MATRIX = 0x3F, + CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX = 0x40, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX = 0x41, + CONST_SRC_CODE_PROJECTION_MATRIX = 0x42, + CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX = 0x43, + CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX = 0x44, + CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX = 0x45, + CONST_SRC_CODE_WORLD_VIEW_MATRIX = 0x46, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX = 0x47, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX = 0x48, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX = 0x49, + CONST_SRC_CODE_VIEW_PROJECTION_MATRIX = 0x4A, + CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX = 0x4B, + CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x4C, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x4D, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX = 0x4E, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX = 0x4F, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX = 0x50, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX = 0x51, + CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX = 0x52, + CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX = 0x53, + CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x54, + CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x55, + CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x56, + CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x57, + CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x58, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x59, + CONST_SRC_TOTAL_COUNT = 0x5A, + CONST_SRC_NONE = 0x5B, + }; + } +} diff --git a/src/IW4/Zone.cpp b/src/IW4/Zone.cpp new file mode 100644 index 0000000..a212d91 --- /dev/null +++ b/src/IW4/Zone.cpp @@ -0,0 +1,408 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + IAsset* Zone::find_asset(std::int32_t type, const std::string& name) + { + for (auto idx = 0u; idx < m_assets.size(); idx++) + { + if (m_assets[idx]->type() == type && m_assets[idx]->name() == name) + { + return m_assets[idx].get(); + } + } + + return nullptr; + } + + void* Zone::get_asset_pointer(std::int32_t type, const std::string& name) + { + if (name.empty()) + { + return nullptr; + } + + for (auto idx = 0u; idx < m_assets.size(); idx++) + { + if (m_assets[idx]->type() == type && m_assets[idx]->name() == name) + { + auto ptr = reinterpret_cast((3 << 28) | ((this->m_assetbase + (8 * idx) + 4) & 0x0FFFFFFF) + 1); + return ptr; + } + } + + return nullptr; + } + + void Zone::add_asset_of_type_by_pointer(std::int32_t type, void* pointer) + { + if (!pointer) + { + return; + } + + // don't add asset if it already exists + for (std::size_t idx = 0; idx < m_assets.size(); idx++) + { + if (m_assets[idx]->type() == type && m_assets[idx]->pointer() == pointer) + { + return; + } + } + +#define DECLARE_ASSET(__type__, __interface__) \ + if (type == __type__) \ + { \ + auto asset = std::make_shared < __interface__ >(); \ + asset->init(pointer, this->m_zonemem.get()); \ + asset->load_depending(this); \ + m_assets.push_back(asset); \ + } + + // declare asset interfaces + DECLARE_ASSET(xmodelsurfs, IXSurface); + DECLARE_ASSET(image, IGfxImage); + DECLARE_ASSET(localize, ILocalizeEntry); + } + + void Zone::add_asset_of_type(std::int32_t type, const std::string& name) + { + if (name.empty()) + { + return; + } + + // don't add asset if it already exists + if (get_asset_pointer(type, name)) + { + return; + } + +#define DECLARE_ASSET(__type__, __interface__) \ + if (type == __type__) \ + { \ + auto asset = std::make_shared < __interface__ >(); \ + asset->init(name, this->m_zonemem.get()); \ + asset->load_depending(this); \ + m_assets.push_back(asset); \ + } + + // declare asset interfaces + DECLARE_ASSET(xanim, IXAnimParts); + DECLARE_ASSET(pixelshader, IPixelShader); + DECLARE_ASSET(vertexdecl, IVertexDecl); + DECLARE_ASSET(vertexshader, IVertexShader); + DECLARE_ASSET(techset, ITechset); + DECLARE_ASSET(image, IGfxImage); + DECLARE_ASSET(material, IMaterial) + DECLARE_ASSET(xmodelsurfs, IXSurface); + DECLARE_ASSET(xmodel, IXModel); + DECLARE_ASSET(map_ents, IMapEnts); + DECLARE_ASSET(rawfile, IRawFile); + DECLARE_ASSET(com_map, IComWorld); + DECLARE_ASSET(font, IFontDef); + DECLARE_ASSET(localize, ILocalizeEntry); + DECLARE_ASSET(physpreset, IPhysPreset); + DECLARE_ASSET(phys_collmap, IPhysCollmap); + DECLARE_ASSET(stringtable, IStringTable); + DECLARE_ASSET(sound, ISound); + DECLARE_ASSET(loaded_sound, ILoadedSound); + DECLARE_ASSET(sndcurve, ISoundCurve); + DECLARE_ASSET(game_map_mp, IGameWorldMp); + DECLARE_ASSET(game_map_sp, IGameWorldSp); + DECLARE_ASSET(fx_map, IFxWorld); + DECLARE_ASSET(tracer, ITracerDef); + DECLARE_ASSET(gfx_map, IGfxWorld); + DECLARE_ASSET(col_map_mp, IClipMap); + DECLARE_ASSET(fx, IFxEffectDef); + DECLARE_ASSET(lightdef, ILightDef); + DECLARE_ASSET(structureddatadef, IStructuredDataDef); + DECLARE_ASSET(addon_map_ents, IAddonMapEnts); + } + + void Zone::add_asset_of_type(const std::string& type, const std::string& name) + { + std::int32_t itype = m_linker->type_to_int(type); + this->add_asset_of_type(itype, name); + } + + std::int32_t Zone::get_type_by_name(const std::string& type) + { + return m_linker->type_to_int(type); + } + + void Zone::build(ZoneBuffer* buf) + { + // init streams properly + buf->init_streams(/*target_ == zone_target::pc ? 8 : 6*/8); // use 8 because it always uses a shiftsize of 28, even though console is missing 1-2 streams. + + if (target_ != zone_target::pc) + { + auto pak_version = 269; + if (target_version_ == zone_target_version::iw4_alpha_482) + { + pak_version = 253; + } + + buf->alloc_image_pak(pak_version); + } + + const auto start_time = GetTickCount64(); + + // make a folder in main, for the map images + std::filesystem::create_directories("main\\" + this->name_ + "\\images"); + + ZONETOOL_INFO("Compiling fastfile \"%s\"...", this->name_.data()); + + constexpr std::size_t num_streams = 8; + XZoneMemory mem = {}; + + const auto header_size = sizeof XZoneMemory; + memset(&mem, 0, header_size); + + auto zone = buf->at>(); + + // write zone header + auto mem_ptr = buf->at>(); + + buf->write_stream(&mem, 4, target_ == zone_target::pc ? 10 : target_ == zone_target::ps3 ? 9 : 8); // write correct amount of streams, skip last 2 streams for console + + std::uintptr_t pad = 0xFFFFFFFF; + std::uintptr_t zero = 0; + + // write asset types to header + for (auto i = 0u; i < m_assets.size(); i++) + { + m_assets[i]->prepare(buf, this->m_zonemem.get()); + } + + // write scriptstring count + auto stringcount = buf->scriptstring_count(); + const auto stringcount_ptr = buf->write(&stringcount); + buf->write(stringcount > 0 ? (&pad) : (&zero)); + + // write asset count + auto asset_count = m_assets.size(); + const auto asset_count_ptr = buf->write(&asset_count); + buf->write(asset_count > 0 ? (&pad) : (&zero)); + + if (target_ != zone_target::pc) + { + endian_convert(asset_count_ptr); + endian_convert(stringcount_ptr); + } + + // push stream + buf->push_stream(3); + START_LOG_STREAM; + + if (stringcount) + { + // write pointer for every scriptstring + for (std::size_t idx = 0; idx < stringcount; idx++) + { + buf->write(&pad); + } + + // write scriptstrings + buf->align(3); + for (std::size_t idx = 0; idx < stringcount; idx++) + { + buf->write_str(buf->get_scriptstring(idx)); + } + } + + buf->pop_stream(); + buf->push_stream(3); + + // align buffer + buf->align(3); + + // set asset ptr base + this->m_assetbase = buf->stream_offset(3); + + // write asset types to header + for (auto i = 0u; i < asset_count; i++) + { + // write asset data to zone + auto type = m_assets[i]->type(); + const auto type_ptr = buf->write(&type); + buf->write(&pad); + + if (target_ != zone_target::pc) + { + if (*type_ptr >= 7 && target_ == zone_target::xbox360) + { + *type_ptr -= 2; + } + else if (*type_ptr >= 8 && target_ == zone_target::ps3) + { + *type_ptr -= 1; + } + + endian_convert(type_ptr); + } + } + + // write assets + for (auto& asset : m_assets) + { + // push stream + buf->push_stream(0); + buf->align(3); + + // write asset + asset->write(this, buf); + + // pop stream + buf->pop_stream(); + } + + // pop stream + END_LOG_STREAM; + buf->pop_stream(); + + // update zone header + zone->size = buf->size() - (target_ == zone_target::pc ? 0x28 : target_ == zone_target::ps3 ? 0x24 : 0x20); + zone->externalsize = 0; + + // Update stream data + for (int i = 0; i < (target_ == zone_target::pc ? 8 : target_ == zone_target::ps3 ? 7 : 6); i++) + { + zone->streams[i] = buf->stream_offset(i); + } + + auto streamed_files = buf->stream_files(); + + if (target_ != zone_target::pc) + { + // mem_ptr->streams[5] = 1352; + + endian_convert(&mem_ptr->size); + endian_convert(&mem_ptr->externalsize); + for (auto& stream : mem_ptr->streams) + { + endian_convert(&stream); + } + } + + if (this->get_target() == zone_target::ps3) + { + auto zoneSize = ((buf->size() + (0x10000 - 1)) & ~(0x10000 - 1)); + std::vector pad(zoneSize - buf->size(), 0); + buf->write(pad.data(), zoneSize - buf->size()); + } + + // Dump zone to disk (DEBUGGING PURPOSES) + buf->save("debug\\" + this->name_ + ".zone"); + + // Compress buffer + auto buf_compressed = buf->compress_zlib(this->get_target() == zone_target::ps3 ? true : false); + + // Generate FF header + auto header = this->m_zonemem->Alloc(); + strcpy(header->header, "IWffu100"); + header->version = 276; + header->allowOnlineUpdate = 0; + + if (target_ == zone_target::xbox360 || target_ == zone_target::ps3) + { + auto pak_version = 269; + if (target_version_ == zone_target_version::iw4_alpha_482) + { + pak_version = 253; + } + + header->version = pak_version; + endian_convert(&header->version); + } + + // fix entry count + if (streamed_files.size()) + { + XAssetStreamFile meme = {}; + memset(&meme, 0, sizeof XAssetStreamFile); + + const auto needed_entries = 4 - (streamed_files.size() % 4); + for (auto i = 0u; i < needed_entries; i++) + { + streamed_files.push_back(meme); + } + } + + // Save fastfile + ZoneBuffer fastfile(buf_compressed.size() + ((target_ == zone_target::xbox360 || target_ == zone_target::ps3) ? 37 : 21) + (streamed_files.size() * 12)); + fastfile.init_streams(1); + fastfile.write_stream(header, 21); + + if (target_ == zone_target::xbox360 || target_ == zone_target::ps3) + { + auto language = 1; + auto entry_count = streamed_files.size(); + + endian_convert(&language); + endian_convert(&entry_count); + + fastfile.write_stream(&language, 4); + fastfile.write_stream(&entry_count, 4); + + // write streamed files + for (auto& stream_file : streamed_files) + { + auto file = fastfile.at(); + fastfile.write_stream(&stream_file, sizeof XAssetStreamFile, 1); + + endian_convert(&file->fileIndex); + endian_convert(&file->offset); + endian_convert(&file->offsetEnd); + } + + fastfile.write_stream(&mem_ptr->size, 4); + fastfile.write_stream(&mem_ptr->size, 4); + } + + fastfile.write(buf_compressed.data(), buf_compressed.size()); + + if (target_version_ == zone_target_version::iw4_alpha_482) + { + fastfile.save("zone\\english\\" + this->name_ + ".ffm"); + buf->save_image_pak("imagefile5.pakm"); + } + else + { + fastfile.save("zone\\english\\" + this->name_ + ".ff"); + } + + ZONETOOL_INFO("Successfully compiled fastfile \"%s\"!", this->name_.data()); + ZONETOOL_INFO("Compiling took %u msec.", GetTickCount64() - start_time); + + // this->m_linker->UnloadZones(); + } + + Zone::Zone(std::string name, ILinker* linker) + { + currentzone = name; + + this->m_linker = linker; + this->name_ = name; + + this->m_zonemem = std::make_shared(MAX_ZONE_SIZE); + } + + Zone::~Zone() + { + // wipe all assets + m_assets.clear(); + } + } +} diff --git a/src/IW4/Zone.hpp b/src/IW4/Zone.hpp new file mode 100644 index 0000000..f85962f --- /dev/null +++ b/src/IW4/Zone.hpp @@ -0,0 +1,75 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +extern std::string currentzone; + +#include "Assets/RawFile.hpp" +#include "Assets/PhysPreset.hpp" +#include "Assets/WeaponDef.hpp" +#include "Assets/VertexDecl.hpp" +#include "Assets/PixelShader.hpp" +#include "Assets/VertexShader.hpp" +#include "Assets/Techset.hpp" +#include "Assets/GfxImage.hpp" +#include "Assets/Material.hpp" +#include "Assets/XSurface.hpp" +#include "Assets/Sound.hpp" +#include "Assets/LoadedSound.hpp" +#include "Assets/SoundCurve.hpp" +#include "Assets/FontDef.hpp" +#include "Assets/PhysCollmap.hpp" +#include "Assets/Xmodel.hpp" +#include "Assets/GameWorldMp.hpp" +#include "Assets/GameWorldSp.hpp" +#include "Assets/FxWorld.hpp" +#include "Assets/MapEnts.hpp" +#include "Assets/ComWorld.hpp" +#include "Assets/XAnimParts.hpp" +#include "Assets/StringTable.hpp" +#include "Assets/LocalizeEntry.hpp" +#include "Assets/TracerDef.hpp" +#include "Assets/GfxWorld.hpp" +#include "Assets/ClipMap.hpp" +#include "Assets/FxEffectDef.hpp" +#include "Assets/LightDef.hpp" +#include "Assets/StructuredDataDef.hpp" +#include "Assets/AddonMapEnts.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + class Zone : public IZone + { + private: + std::uintptr_t m_assetbase; + std::string name_; + ILinker* m_linker; + std::vector> m_assets; + std::shared_ptr m_zonemem; + + public: + Zone(std::string name, ILinker* linker); + ~Zone(); + + IAsset* find_asset(std::int32_t type, const std::string& name) override; + void* Zone::get_asset_pointer(std::int32_t type, const std::string& name) override; + + void add_asset_of_type_by_pointer(std::int32_t type, void* pointer) override; + + void add_asset_of_type(std::int32_t type, const std::string& name) override; + void add_asset_of_type(const std::string& type, const std::string& name) override; + std::int32_t get_type_by_name(const std::string& type) override; + + void build(ZoneBuffer* buf) override; + + }; + } +} diff --git a/src/IW4/stdafx.cpp b/src/IW4/stdafx.cpp new file mode 100644 index 0000000..a55cad6 --- /dev/null +++ b/src/IW4/stdafx.cpp @@ -0,0 +1,9 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" diff --git a/src/IW4/stdafx.hpp b/src/IW4/stdafx.hpp new file mode 100644 index 0000000..eebfaef --- /dev/null +++ b/src/IW4/stdafx.hpp @@ -0,0 +1,34 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS + +// IW4-SPECIFIC OPTIONS +// #define GENERATE_IW5_MODELS +// #define GENERATE_IW5_WEAPONS +// #define CONVERT_IW5_MAPENTS + +#include + +#undef min +#undef max +#undef add + +#include +#include +#include +#include + +// Namespaces +using namespace std::literals; +using namespace string_literals; + +#include "IW4.hpp" diff --git a/src/IW5.lua b/src/IW5.lua new file mode 100644 index 0000000..8d8d27a --- /dev/null +++ b/src/IW5.lua @@ -0,0 +1,37 @@ +IW5 = {} + +function IW5:include() + includedirs { + path.join(ProjectFolder(), "IW5") + } +end + +function IW5:link() + self:include() + links { + "IW5" + } +end + +function IW5:project() + local folder = ProjectFolder(); + + project "IW5" + kind "StaticLib" + language "C++" + + pchheader "stdafx.hpp" + pchsource(path.join(folder, "IW5/stdafx.cpp")) + + files { + path.join(folder, "IW5/**.h"), + path.join(folder, "IW5/**.hpp"), + path.join(folder, "IW5/**.cpp") + } + + self:include() + ZoneUtils:include() + + zstd:include() + zlib:include() +end \ No newline at end of file diff --git a/src/IW5/Assets/AttachmentDef.cpp b/src/IW5/Assets/AttachmentDef.cpp new file mode 100644 index 0000000..43aff38 --- /dev/null +++ b/src/IW5/Assets/AttachmentDef.cpp @@ -0,0 +1,522 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + AttachmentDef* IAttachmentDef::parse(const std::string& name, ZoneMemory* mem) + { + if (name == "cheytacscope") + { + auto scope = mem->Alloc(); + auto base = DB_FindXAssetHeader(attachment, /*name.data()*/ "msrscope", 1).attachment; + + memcpy(scope, base, sizeof AttachmentDef); + scope->szInternalName = mem->StrDup(name); + + for (int i = 0; i < 16; i++) + { + scope->worldModels[i] = nullptr; + scope->viewModels[i] = nullptr; + } + + scope->worldModels[0] = DB_FindXAssetHeader(xmodel, "weapon_cheytac_scope", 1).xmodel; + scope->viewModels[0] = DB_FindXAssetHeader(xmodel, "viewmodel_cheytac_scope", 1).xmodel; + + ZONETOOL_INFO("msrscope -> cheytacscope!"); + + return scope; + } + + return nullptr; + } + + void IAttachmentDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).attachment; + } + } + + void IAttachmentDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IAttachmentDef::load_depending(IZone* zone) + { + auto data = this->asset_; + + // if we didn't parse a custom asset and the asset is common: there is no point in writing it. + // make a reference to the original asset instead. + /*if (!this->m_parsed && AssetHandler::IsCommonAsset(this->type(), this->name())) + { + return; + }*/ + + for (std::int32_t i = 0; i < 16; i++) + { + if (data->worldModels && data->worldModels[i]) + { + zone->add_asset_of_type(xmodel, data->worldModels[i]->name); + } + + if (data->viewModels && data->viewModels[i]) + { + zone->add_asset_of_type(xmodel, data->viewModels[i]->name); + } + } + + for (std::int32_t i = 0; i < 8; i++) + { + if (data->reticleViewModels && data->reticleViewModels[i]) + { + zone->add_asset_of_type(xmodel, data->reticleViewModels[i]->name); + } + } + + if (data->ammogeneral) + { + if (data->ammogeneral->tracerType) + { + zone->add_asset_of_type(tracer, data->ammogeneral->tracerType->name); + } + } + + if (data->general) + { + if (data->general->reticleCenter) + { + zone->add_asset_of_type(material, data->general->reticleCenter->name); + } + + if (data->general->reticleSide) + { + zone->add_asset_of_type(material, data->general->reticleSide->name); + } + } + + if (data->ui) + { + if (data->ui->ammoCounterIcon) + { + zone->add_asset_of_type(material, data->ui->ammoCounterIcon->name); + } + + if (data->ui->dpadIcon) + { + zone->add_asset_of_type(material, data->ui->dpadIcon->name); + } + } + + if (data->adsOverlay) + { + if (data->adsOverlay->overlay.shader) + { + zone->add_asset_of_type(material, data->adsOverlay->overlay.shader->name); + } + + if (data->adsOverlay->overlay.shaderLowRes) + { + zone->add_asset_of_type(material, data->adsOverlay->overlay.shaderLowRes->name); + } + + if (data->adsOverlay->overlay.shaderEMP) + { + zone->add_asset_of_type(material, data->adsOverlay->overlay.shaderEMP->name); + } + + if (data->adsOverlay->overlay.shaderEMPLowRes) + { + zone->add_asset_of_type(material, data->adsOverlay->overlay.shaderEMPLowRes->name); + } + } + + /*if (data->projectile) + { + if (data->projectile->projectileModel) + { + zone->AddAssetOfType(xmodel, data->projectile->projectileModel->name); + } + + if (data->projectile->projExplosionEffect) + { + zone->AddAssetOfType(fx, data->projectile->projExplosionEffect->name); + } + + if (data->projectile->projExplosionSound) + { + zone->AddAssetOfType(sound, data->projectile->projExplosionSound->aliasName); + } + + if (data->projectile->projDudEffect) + { + zone->AddAssetOfType(fx, data->projectile->projDudEffect->name); + } + + if (data->projectile->projDudSound) + { + zone->AddAssetOfType(sound, data->projectile->projDudSound->aliasName); + } + + if (data->projectile->projTrailEffect) + { + zone->AddAssetOfType(fx, data->projectile->projTrailEffect->name); + } + + if (data->projectile->projIgnitionEffect) + { + zone->AddAssetOfType(fx, data->projectile->projIgnitionEffect->name); + } + + if (data->projectile->projIgnitionSound) + { + zone->AddAssetOfType(sound, data->projectile->projIgnitionSound->aliasName); + } + }*/ + } + + std::string IAttachmentDef::name() + { + return this->name_; + } + + std::int32_t IAttachmentDef::type() + { + return attachment; + } + + void IAttachmentDef::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->szInternalName = buf->write_str(this->name()); + + if (data->szDisplayName) + { + dest->szDisplayName = buf->write_str(data->szDisplayName); + } + + if (data->worldModels) + { + buf->align(3); + auto models = buf->write(data->worldModels, 16); + + for (std::int32_t i = 0; i < 16; i++) + { + if (models[i]) + { + models[i] = reinterpret_cast(zone->get_asset_pointer(xmodel, models[i]->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->worldModels); + } + + if (data->viewModels) + { + buf->align(3); + auto models = buf->write(data->viewModels, 16); + + for (std::int32_t i = 0; i < 16; i++) + { + if (models[i]) + { + models[i] = reinterpret_cast(zone->get_asset_pointer(xmodel, models[i]->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->viewModels); + } + + if (data->reticleViewModels) + { + buf->align(3); + auto models = buf->write(data->reticleViewModels, 8); + + for (std::int32_t i = 0; i < 8; i++) + { + if (models[i]) + { + models[i] = reinterpret_cast(zone->get_asset_pointer(xmodel, models[i]->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->reticleViewModels); + } + + if (data->ammogeneral) + { + buf->align(3); + auto ammo = buf->write(data->ammogeneral); + + if (ammo->tracerType) + { + ammo->tracerType = reinterpret_cast(zone->get_asset_pointer( + tracer, ammo->tracerType->name)); + } + + ZoneBuffer::clear_pointer(&dest->ammogeneral); + } + + if (data->sight) + { + buf->align(3); + buf->write(data->sight); + ZoneBuffer::clear_pointer(&dest->sight); + } + + if (data->reload) + { + buf->align(1); + buf->write(data->reload); + ZoneBuffer::clear_pointer(&dest->reload); + } + + if (data->addOns) + { + buf->align(1); + buf->write(data->addOns); + ZoneBuffer::clear_pointer(&dest->addOns); + } + + if (data->general) + { + buf->align(3); + auto general = buf->write(data->general); + + if (general->reticleCenter) + { + general->reticleCenter = reinterpret_cast(zone->get_asset_pointer( + material, general->reticleCenter->name)); + } + + if (general->reticleSide) + { + general->reticleSide = reinterpret_cast(zone->get_asset_pointer( + material, general->reticleSide->name)); + } + + ZoneBuffer::clear_pointer(&dest->general); + } + + if (data->ammunition) + { + buf->align(3); + buf->write(data->ammunition); + ZoneBuffer::clear_pointer(&dest->ammunition); + } + + if (data->idleSettings) + { + buf->align(3); + buf->write(data->idleSettings); + ZoneBuffer::clear_pointer(&dest->idleSettings); + } + + if (data->damage) + { + buf->align(3); + buf->write(data->damage); + ZoneBuffer::clear_pointer(&dest->damage); + } + + if (data->locationDamage) + { + buf->align(3); + buf->write(data->damage); + ZoneBuffer::clear_pointer(&dest->damage); + } + + if (data->scopeDriftSettings) + { + buf->align(3); + buf->write(data->scopeDriftSettings); + ZoneBuffer::clear_pointer(&dest->scopeDriftSettings); + } + + if (data->adsSettings) + { + buf->align(3); + buf->write(data->adsSettings); + ZoneBuffer::clear_pointer(&dest->adsSettings); + } + + if (data->adsSettingsMain) + { + buf->align(3); + buf->write(data->adsSettingsMain); + ZoneBuffer::clear_pointer(&dest->adsSettingsMain); + } + + if (data->hipSpread) + { + buf->align(3); + buf->write(data->hipSpread); + ZoneBuffer::clear_pointer(&dest->hipSpread); + } + + if (data->gunKick) + { + buf->align(3); + buf->write(data->gunKick); + ZoneBuffer::clear_pointer(&dest->gunKick); + } + + if (data->viewKick) + { + buf->align(3); + buf->write(data->viewKick); + ZoneBuffer::clear_pointer(&dest->viewKick); + } + + if (data->adsOverlay) + { + buf->align(3); + auto overlay = buf->write(data->adsOverlay); + + if (overlay->overlay.shader) + { + overlay->overlay.shader = reinterpret_cast(zone->get_asset_pointer( + material, overlay->overlay.shader->name)); + } + + if (overlay->overlay.shaderEMP) + { + overlay->overlay.shaderEMP = reinterpret_cast(zone->get_asset_pointer( + material, overlay->overlay.shaderEMP->name)); + } + + if (overlay->overlay.shaderEMPLowRes) + { + overlay->overlay.shaderEMPLowRes = reinterpret_cast(zone->get_asset_pointer( + material, overlay->overlay.shaderEMPLowRes->name)); + } + + if (overlay->overlay.shaderLowRes) + { + overlay->overlay.shaderLowRes = reinterpret_cast(zone->get_asset_pointer( + material, overlay->overlay.shaderLowRes->name)); + } + + ZoneBuffer::clear_pointer(&dest->adsOverlay); + } + + if (data->ui) + { + buf->align(3); + auto ui = buf->write(data->ui); + + if (ui->ammoCounterIcon) + { + ui->ammoCounterIcon = reinterpret_cast(zone->get_asset_pointer( + material, ui->ammoCounterIcon->name)); + } + + if (ui->dpadIcon) + { + ui->dpadIcon = reinterpret_cast(zone->get_asset_pointer(material, ui->dpadIcon->name)); + } + + ZoneBuffer::clear_pointer(&dest->ui); + } + + if (data->rumbles) + { + buf->align(3); + auto rumbles = buf->write(data->rumbles); + + if (rumbles->fireRumble) + { + rumbles->fireRumble = buf->write_str(rumbles->fireRumble); + } + + if (rumbles->meleeImpactRumble) + { + rumbles->meleeImpactRumble = buf->write_str(rumbles->meleeImpactRumble); + } + + ZoneBuffer::clear_pointer(&dest->rumbles); + } + + /*if (data->projectile) + { + buf->align(3); + auto projectile = buf->write(data->projectile); + + if (projectile->projectileModel) + { + projectile->projectileModel = reinterpret_cast(zone->GetAssetPointer(xmodel, projectile->projectileModel->name)); + } + + if (projectile->projExplosionEffect) + { + projectile->projExplosionEffect = reinterpret_cast(zone->GetAssetPointer(fx, projectile->projExplosionEffect->name)); + } + + if (projectile->projExplosionSound) + { + projectile->projExplosionSound = reinterpret_cast(zone->GetAssetPointer(sound, projectile->projExplosionSound->aliasName)); + } + + if (projectile->projDudEffect) + { + projectile->projDudEffect = reinterpret_cast(zone->GetAssetPointer(fx, projectile->projDudEffect->name)); + } + + if (projectile->projDudSound) + { + projectile->projDudSound = reinterpret_cast(zone->GetAssetPointer(sound, projectile->projDudSound->aliasName)); + } + + if (projectile->projTrailEffect) + { + projectile->projTrailEffect = reinterpret_cast(zone->GetAssetPointer(fx, projectile->projTrailEffect->name)); + } + + if (projectile->projIgnitionEffect) + { + projectile->projIgnitionEffect = reinterpret_cast(zone->GetAssetPointer(fx, projectile->projIgnitionEffect->name)); + } + + if (projectile->projIgnitionSound) + { + projectile->projIgnitionSound = reinterpret_cast(zone->GetAssetPointer(sound, projectile->projIgnitionSound->aliasName)); + } + + ZoneBuffer::clear_pointer(&dest->projectile); + }*/ + dest->projectile = nullptr; + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IAttachmentDef::dump(AttachmentDef* asset) + { + //const auto data = asset->ToJson(); + + //std::string path = "attachments\\mp\\"s + asset->szInternalName; + //std::string json = data.dump(4); + + //auto file = FileSystem::FileOpen(path, "w"s); + //fwrite(json.data(), json.size(), 1, file); + //FileSystem::FileClose(file); + } + } +} diff --git a/src/IW5/Assets/AttachmentDef.hpp b/src/IW5/Assets/AttachmentDef.hpp new file mode 100644 index 0000000..ba8e307 --- /dev/null +++ b/src/IW5/Assets/AttachmentDef.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IAttachmentDef : public IAsset + { + private: + std::string name_; + AttachmentDef* asset_ = nullptr; + + AttachmentDef* parse(const std::string& name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(AttachmentDef* asset); + }; + } +} diff --git a/src/IW5/Assets/ClipMap.cpp b/src/IW5/Assets/ClipMap.cpp new file mode 100644 index 0000000..99ee642 --- /dev/null +++ b/src/IW5/Assets/ClipMap.cpp @@ -0,0 +1,1011 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { +#define INFO info-> + + void parse_info(ClipInfo* info, AssetReader& read, ZoneMemory* mem) + { + INFO numCPlanes = read.read_int(); + INFO cPlanes = read.read_array(); + + INFO numMaterials = read.read_int(); + INFO materials = read.read_array(); + for (auto i = 0; i < INFO numMaterials; i++) + { + if (INFO materials[i].material) + { + INFO materials[i].material = read.read_string(); + } + } + + INFO numCBrushSides = read.read_int(); + INFO cBrushSides = read.read_array(); + for (auto i = 0; i < INFO numCBrushSides; i++) + { + if (INFO cBrushSides[i].plane) + { + INFO cBrushSides[i].plane = read.read_array(); + } + } + + INFO numCBrushEdges = read.read_int(); + INFO cBrushEdges = read.read_array(); + + INFO numLeafBrushes = read.read_int(); + INFO leafBrushes = read.read_array(); + + INFO numCLeafBrushNodes = read.read_int(); + INFO cLeafBrushNodes = read.read_array(); + for (auto i = 0; i < INFO numCLeafBrushNodes; i++) + { + if (INFO cLeafBrushNodes[i].leafBrushCount > 0) + { + INFO cLeafBrushNodes[i].data.leaf.brushes = read.read_array(); + } + } + + INFO numBrushes = read.read_int(); + INFO brushes = read.read_array(); + for (auto i = 0; i < INFO numBrushes; i++) + { + if (INFO brushes[i].sides) + { + INFO brushes[i].sides = read.read_array(); + + if (INFO brushes[i].sides->plane) + { + INFO brushes[i].sides->plane = read.read_array(); + } + } + if (INFO brushes[i].edge) + { + INFO brushes[i].edge = read.read_array(); + } + } + + INFO brushBounds = read.read_array(); + INFO brushContents = read.read_array(); + } + +#undef INFO +#define INFO info. + + clipMap_t* IClipMap::parse(const std::string& name, ZoneMemory* mem) + { + auto path = name + ".colmap"; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + AssetReader read(mem); + + if (!read.open(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing colmap \"%s\"...", name.c_str()); + + auto* colmap = mem->Alloc(); + + colmap->name = read.read_string(); + colmap->isInUse = read.read_int(); + + parse_info(&colmap->info, read, mem); + + colmap->numStaticModels = read.read_int(); + colmap->staticModelList = read.read_array(); + for (auto i = 0; i < colmap->numStaticModels; i++) + { + if (colmap->staticModelList[i].xmodel) + { + colmap->staticModelList[i].xmodel = read.read_asset(); + } + } + + colmap->numCNodes = read.read_int(); + colmap->cNodes = read.read_array(); + for (auto i = 0; i < colmap->numCNodes; i++) + { + if (colmap->cNodes[i].plane) + { + colmap->cNodes[i].plane = read.read_array(); + } + } + + colmap->numCLeaf = read.read_int(); + colmap->cLeaf = read.read_array(); + + colmap->numVerts = read.read_int(); + colmap->verts = read.read_array>(); + + colmap->numTriIndices = read.read_int(); + colmap->triIndices = read.read_array(); + colmap->triEdgeIsWalkable = read.read_array(); + + colmap->numCollisionBorders = read.read_int(); + colmap->collisionBorders = read.read_array(); + + colmap->numCollisionPartitions = read.read_int(); + colmap->collisionPartitions = read.read_array(); + for (auto i = 0; i < colmap->numCollisionPartitions; i++) + { + if (colmap->collisionPartitions[i].borders) + { + colmap->collisionPartitions[i].borders = read.read_array(); + } + } + + colmap->numCollisionAABBTrees = read.read_int(); + colmap->collisionAABBTrees = read.read_array(); + + colmap->numCModels = read.read_int(); + colmap->cModels = read.read_array(); + + for (auto i = 0; i < colmap->numCModels; i++) + { + if (colmap->cModels[i].info) + { + colmap->cModels[i].info = mem->Alloc(); + parse_info(colmap->cModels[i].info, read, mem); + } + } + + colmap->mapEnts = read.read_asset(); + colmap->mapEnts->name = colmap->name; + colmap->smodelNodeCount = read.read_int(); + colmap->smodelNodes = read.read_array(); + +#ifdef IW4 + colmap->dynEntCount[0] = read.Int(); + DynEntityDef_IW5* newEntDef = read.Array(); + colmap->dynEntDefList[0] = new DynEntityDef[colmap->dynEntCount[0]]; +#define dynEntDefCopy(_num1, _num2, _item) \ +colmap->dynEntDefList[_num1][_num2]._item = newEntDef[_num2]._item; + + for (Int i = 0; i < colmap->dynEntCount[0]; i++) + { + dynEntDefCopy(0, i, type); + dynEntDefCopy(0, i, pose.quat[0]); + dynEntDefCopy(0, i, pose.quat[1]); + dynEntDefCopy(0, i, pose.quat[2]); + dynEntDefCopy(0, i, pose.quat[3]); + dynEntDefCopy(0, i, pose.origin[0]); + dynEntDefCopy(0, i, pose.origin[1]); + dynEntDefCopy(0, i, pose.origin[2]); + dynEntDefCopy(0, i, health); + dynEntDefCopy(0, i, mass.centerOfMass[0]); + dynEntDefCopy(0, i, mass.centerOfMass[1]); + dynEntDefCopy(0, i, mass.centerOfMass[2]); + dynEntDefCopy(0, i, mass.momentsOfInertia[0]); + dynEntDefCopy(0, i, mass.momentsOfInertia[1]); + dynEntDefCopy(0, i, mass.momentsOfInertia[2]); + dynEntDefCopy(0, i, mass.productsOfInertia[0]); + dynEntDefCopy(0, i, mass.productsOfInertia[1]); + dynEntDefCopy(0, i, mass.productsOfInertia[2]); + dynEntDefCopy(0, i, mass.contents); + dynEntDefCopy(0, i, xModel); + dynEntDefCopy(0, i, physPreset); + colmap->dynEntDefList[0][i].destroyFx = nullptr; + if (colmap->dynEntDefList[0][i].xModel) + { + colmap->dynEntDefList[0][i].xModel = read.Asset(); + } + if (colmap->dynEntDefList[0][i].physPreset) + { + colmap->dynEntDefList[0][i].physPreset = read.Asset(); + } + } + + colmap->dynEntCount[1] = read.Int(); + newEntDef = read.Array(); + colmap->dynEntDefList[1] = new DynEntityDef[colmap->dynEntCount[1]]; + for (Int i = 0; i < colmap->dynEntCount[1]; i++) + { + dynEntDefCopy(1, i, type); + dynEntDefCopy(1, i, pose.quat[0]); + dynEntDefCopy(1, i, pose.quat[1]); + dynEntDefCopy(1, i, pose.quat[2]); + dynEntDefCopy(1, i, pose.quat[3]); + dynEntDefCopy(1, i, pose.origin[0]); + dynEntDefCopy(1, i, pose.origin[1]); + dynEntDefCopy(1, i, pose.origin[2]); + dynEntDefCopy(1, i, health); + dynEntDefCopy(1, i, mass.centerOfMass[0]); + dynEntDefCopy(1, i, mass.centerOfMass[1]); + dynEntDefCopy(1, i, mass.centerOfMass[2]); + dynEntDefCopy(1, i, mass.momentsOfInertia[0]); + dynEntDefCopy(1, i, mass.momentsOfInertia[1]); + dynEntDefCopy(1, i, mass.momentsOfInertia[2]); + dynEntDefCopy(1, i, mass.productsOfInertia[0]); + dynEntDefCopy(1, i, mass.productsOfInertia[1]); + dynEntDefCopy(1, i, mass.productsOfInertia[2]); + dynEntDefCopy(1, i, mass.contents); + colmap->dynEntDefList[1][i].destroyFx = nullptr; + if (colmap->dynEntDefList[1][i].xModel) + { + colmap->dynEntDefList[1][i].xModel = read.Asset(); + } + if (colmap->dynEntDefList[1][i].physPreset) + { + colmap->dynEntDefList[1][i].physPreset = read.Asset(); + } + } + + colmap->dynEntPoseList[0] = new DynEntityPose[colmap->dynEntCount[0]]; + colmap->dynEntPoseList[1] = new DynEntityPose[colmap->dynEntCount[1]]; + colmap->dynEntClientList[0] = new DynEntityClient[colmap->dynEntCount[0]]; + colmap->dynEntClientList[1] = new DynEntityClient[colmap->dynEntCount[1]]; + colmap->dynEntCollList[0] = new DynEntityColl[colmap->dynEntCount[0]]; + colmap->dynEntCollList[1] = new DynEntityColl[colmap->dynEntCount[1]]; +#else + colmap->dynEntDefList[0] = 0; + colmap->dynEntDefList[1] = 0; + colmap->dynEntPoseList[0] = 0; + colmap->dynEntPoseList[1] = 0; + colmap->dynEntClientList[0] = 0; + colmap->dynEntClientList[1] = 0; + colmap->dynEntCollList[0] = 0; + colmap->dynEntCollList[1] = 0; + colmap->dynEntCount[0] = 0; + colmap->dynEntCount[1] = 0; +#endif + + // parse stages + AssetReader stageReader(mem); + if (stageReader.open(name + ".ents.stages")) + { + ZONETOOL_INFO("Parsing entity stages..."); + + colmap->stageCount = stageReader.read_int(); + if (colmap->stageCount) + { + colmap->stages = stageReader.read_array(); + + for (int i = 0; i < colmap->stageCount; i++) + { + colmap->stages[i].name = stageReader.read_string(); + } + } + } + stageReader.close(); + + // copy info into pInfo + colmap->pInfo = &colmap->info; + + /*colmap->pInfo = mem->Alloc(); + memcpy(colmap->pInfo, &colmap->info, sizeof ClipInfo);*/ + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + + return colmap; + } + + void IClipMap::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data(), 1).clipmap; + } + } + + void IClipMap::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IClipMap::load_depending(IZone* zone) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::IClipMap::load_depending"); +#endif + + auto data = this->asset_; + + if (data->staticModelList) + { + for (int i = 0; i < data->numStaticModels; i++) + { + if (data->staticModelList[i].xmodel) + { + zone->add_asset_of_type(xmodel, data->staticModelList[i].xmodel->name); + } + } + } + + //if (data->dynEntDefList[0]) + //{ + // for (int i = 0; i < data->dynEntCount[0]; i++) + // { + // if (data->dynEntDefList[0][i].xModel) + // { + // zone->add_asset_of_type(xmodel, data->dynEntDefList[0][i].xModel->name); + // } + // if (data->dynEntDefList[0][i].destroyFx) + // { + // zone->add_asset_of_type(fx, data->dynEntDefList[0][i].destroyFx->name); + // } + // if (data->dynEntDefList[0][i].physPreset) + // { + // zone->add_asset_of_type(physpreset, data->dynEntDefList[0][i].physPreset->name); + // } + // } + //} + + //if (data->dynEntDefList[1]) + //{ + // for (int i = 0; i < data->dynEntCount[1]; i++) + // { + // if (data->dynEntDefList[1][i].xModel) + // { + // zone->add_asset_of_type(xmodel, data->dynEntDefList[1][i].xModel->name); + // } + // if (data->dynEntDefList[1][i].destroyFx) + // { + // zone->add_asset_of_type(fx, data->dynEntDefList[1][i].destroyFx->name); + // } + // if (data->dynEntDefList[1][i].physPreset) + // { + // zone->add_asset_of_type(physpreset, data->dynEntDefList[1][i].physPreset->name); + // } + // } + //} + + if (data->mapEnts) + { + zone->add_asset_of_type(map_ents, this->asset_->mapEnts->name); + } + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + std::string IClipMap::name() + { + return this->name_; + } + + std::int32_t IClipMap::type() + { + return col_map_mp; + } + + void IClipMap::write_info(IZone* zone, ZoneBuffer* buf, ClipInfo* data, ClipInfo* dest) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::IClipMap::write_info"); +#endif + + if (data->cPlanes) + { + dest->cPlanes = buf->write_s(3, data->cPlanes, data->numCPlanes); + ZONETOOL_INFO("cPlanes pointer is 0x%08X", dest->cPlanes); + } + + if (data->materials) + { + dmaterial_t* dmaterial; + dest->materials = buf->write_s(3, data->materials, data->numMaterials, sizeof dmaterial_t, &dmaterial); + + if (dest->materials == reinterpret_cast(-1)) + { + for (std::int32_t i = 0; i < data->numMaterials; i++) + { + if (data->materials[i].material) + { + dmaterial[i].material = buf->write_str(data->materials[i].material); + } + } + } + ZONETOOL_INFO("materials pointer is 0x%08X", dest->materials); + } + + if (data->cBrushSides) + { + cbrushside_t* brush_side; + dest->cBrushSides = buf->write_s(3, data->cBrushSides, data->numCBrushSides, sizeof cbrushside_t, + &brush_side); + + if (dest->cBrushSides == reinterpret_cast(-1)) + { + for (std::int32_t i = 0; i < data->numCBrushSides; i++) + { + if (data->cBrushSides[i].plane) + { + brush_side[i].plane = buf->write_s(3, data->cBrushSides[i].plane); + } + } + } + ZONETOOL_INFO("cBrushSides pointer is 0x%08X", dest->cBrushSides); + } + + if (data->cBrushEdges) + { + /*buf->write_p(data->cBrushEdges, data->numCBrushEdges); + ZoneBuffer::clear_pointer(&);*/ + dest->cBrushEdges = buf->write_s(0, data->cBrushEdges, data->numCBrushEdges); + ZONETOOL_INFO("cBrushEdges pointer is 0x%08X", dest->cBrushEdges); + } + + if (data->cLeafBrushNodes) + { + cLeafBrushNode_s* leaf_brush_node = nullptr; + dest->cLeafBrushNodes = buf->write_s(3, data->cLeafBrushNodes, data->numCLeafBrushNodes, + sizeof cLeafBrushNode_s, &leaf_brush_node); + + if (dest->cLeafBrushNodes == reinterpret_cast(-1)) + { + for (std::int32_t i = 0; i < data->numCLeafBrushNodes; i++) + { + if (data->cLeafBrushNodes[i].leafBrushCount > 0 && data->cLeafBrushNodes[i].data.leaf.brushes) + { + leaf_brush_node[i].data.leaf.brushes = buf->write_s( + 1, data->cLeafBrushNodes[i].data.leaf.brushes, data->cLeafBrushNodes[i].leafBrushCount); + } + } + } + ZONETOOL_INFO("cLeafBrushNodes pointer is 0x%08X", dest->cLeafBrushNodes); + } + + if (data->leafBrushes) + { + /*buf->align(1); + buf->write(data->leafBrushes, data->numLeafBrushes); + ZoneBuffer::clear_pointer(&dest->leafBrushes);*/ + dest->leafBrushes = buf->write_s(1, data->leafBrushes, data->numLeafBrushes); + ZONETOOL_INFO("leafBrushes pointer is 0x%08X", dest->leafBrushes); + } + + // brushes + if (data->brushes) + { + cbrush_t* brush = nullptr; + dest->brushes = buf->write_s(127, data->brushes, data->numBrushes, sizeof cbrush_t, &brush); + + if (dest->brushes == reinterpret_cast(-1)) + { + for (int i = 0; i < data->numBrushes; i++) + { + if (data->brushes[i].sides) + { + cbrushside_t* side = nullptr; + brush[i].sides = buf->write_s(3, data->brushes[i].sides, 1, sizeof cbrushside_t, &side); + + if (brush[i].sides == (cbrushside_t*)-1 && side) + { + if (side->plane) + { + side->plane = buf->write_s(3, side->plane); + } + } + } + + if (data->brushes[i].edge) + { + brush[i].edge = buf->write_s(0, data->brushes[i].edge); + } + } + } + ZONETOOL_INFO("brushes pointer is 0x%08X", dest->brushes); + } + + // brushBounds + if (data->brushBounds) + { + /*buf->align(127); + buf->write(data->brushBounds, data->numBrushes); + ZoneBuffer::clear_pointer(&dest->brushBounds);*/ + dest->brushBounds = buf->write_s(127, data->brushBounds, data->numBrushes); + ZONETOOL_INFO("brushBounds pointer is 0x%08X", dest->brushBounds); + } + + // brushContents + if (data->brushContents) + { + /*buf->align(3); + buf->write(data->brushContents, data->numBrushes); + ZoneBuffer::clear_pointer(&dest->brushContents);*/ + dest->brushContents = buf->write_s(3, data->brushContents, data->numBrushes); + ZONETOOL_INFO("brushContents pointer is 0x%08X", dest->brushContents); + } + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + void IClipMap::write(IZone* zone, ZoneBuffer* buf) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::IClipMap::write"); +#endif + + auto data = this->asset_; + auto offset = buf->get_zone_offset(); + auto dest = buf->write(data); + + sizeof clipMap_t; + + dest->isPlutoniumMap = 0x13370420; + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + this->write_info(zone, buf, &data->info, &dest->info); + + if (data->pInfo) + { + buf->align(3); + auto destInfo = buf->write(data->pInfo); + this->write_info(zone, buf, data->pInfo, destInfo); + ZoneBuffer::clear_pointer(&dest->pInfo); + } + + if (data->staticModelList) + { + buf->align(3); + auto static_model = buf->write(data->staticModelList, data->numStaticModels); + + for (std::int32_t i = 0; i < data->numStaticModels; i++) + { + if (data->staticModelList[i].xmodel) + { + static_model[i].xmodel = reinterpret_cast(zone->get_asset_pointer( + xmodel, data->staticModelList[i].xmodel->name)); + } + } + + ZoneBuffer::clear_pointer(&data->staticModelList); + } + + if (data->cNodes) + { + buf->align(3); + auto node = buf->write(data->cNodes, data->numCNodes); + + for (std::int32_t i = 0; i < data->numCNodes; i++) + { + if (data->cNodes[i].plane) + { + node[i].plane = buf->write_s(3, data->cNodes[i].plane); + } + } + + ZoneBuffer::clear_pointer(&dest->cNodes); + } + + if (data->cLeaf) + { + buf->align(3); + buf->write(data->cLeaf, data->numCLeaf); + ZoneBuffer::clear_pointer(&dest->cLeaf); + } + + if (data->verts) + { + buf->align(3); + buf->write(data->verts, data->numVerts); + ZoneBuffer::clear_pointer(&dest->verts); + } + + if (data->triIndices) + { + buf->align(1); + buf->write(data->triIndices, data->numTriIndices * 3); + ZoneBuffer::clear_pointer(&dest->triIndices); + } + + if (data->triEdgeIsWalkable) + { + buf->align(0); + buf->write(data->triEdgeIsWalkable, 4 * ((3 * data->numTriIndices + 31) >> 5)); + ZoneBuffer::clear_pointer(&dest->triEdgeIsWalkable); + } + + if (data->collisionBorders) + { + buf->align(3); + buf->write_p(data->collisionBorders, data->numCollisionBorders); + ZoneBuffer::clear_pointer(&dest->collisionBorders); + } + + if (data->collisionPartitions) + { + buf->align(3); + auto collision_partition = buf->write(data->collisionPartitions, data->numCollisionPartitions); + + for (std::int32_t i = 0; i < data->numCollisionPartitions; i++) + { + if (data->collisionPartitions[i].borders) + { + collision_partition[i].borders = buf->write_s(3, data->collisionPartitions[i].borders); + } + } + + ZoneBuffer::clear_pointer(&dest->collisionPartitions); + } + + if (data->collisionAABBTrees) + { + buf->align(15); + buf->write(data->collisionAABBTrees, data->numCollisionAABBTrees); + ZoneBuffer::clear_pointer(&dest->collisionAABBTrees); + } + + if (data->cModels) + { + buf->align(3); + auto destCModels = buf->write(data->cModels, data->numCModels); + + for (std::int32_t i = 0; i < data->numCModels; i++) + { + buf->push_stream(0); + + if (data->cModels[i].info) + { + ClipInfo* destInfo = nullptr; + destCModels[i].info = buf->write_s(3, data->cModels[i].info, 1, sizeof ClipInfo, &destInfo); + + if (destCModels[i].info == reinterpret_cast(-1)) + { + this->write_info(zone, buf, data->cModels[i].info, destInfo); + } + } + + buf->pop_stream(); + } + + ZoneBuffer::clear_pointer(&dest->cModels); + } + + if (data->smodelNodes) + { + buf->align(3); + buf->write(data->smodelNodes, data->smodelNodeCount); + ZoneBuffer::clear_pointer(&dest->smodelNodes); + } + + if (data->mapEnts) + { + dest->mapEnts = reinterpret_cast(zone->get_asset_pointer(map_ents, this->name())); + } + + if (data->stages) + { + buf->align(3); + auto destStages = buf->write(data->stages, data->stageCount); + + for (std::uint8_t i = 0; i < data->stageCount; i++) + { + if (data->stages[i].name) + { + buf->write_str(data->stages[i].name); + ZoneBuffer::clear_pointer(&destStages[i].name); + } + } + + ZoneBuffer::clear_pointer(&dest->stages); + } + + // copy trigger data from mapents + memcpy(&dest->trigger, &data->mapEnts->trigger, sizeof MapTriggers); + + // write triggers + IMapEnts::write_triggers(zone, buf, &dest->trigger); + + if (data->dynEntDefList[0]) + { + buf->align(3); + auto dyn_entity_def = buf->write(data->dynEntDefList[0], data->dynEntCount[0]); + + for (std::uint16_t i = 0; i < data->dynEntCount[0]; i++) + { + if (data->dynEntDefList[0][i].xModel) + { + dyn_entity_def[i].xModel = reinterpret_cast(zone->get_asset_pointer( + xmodel, data->dynEntDefList[0][i].xModel->name)); + } + if (data->dynEntDefList[0][i].destroyFx) + { + dyn_entity_def[i].destroyFx = reinterpret_cast(zone->get_asset_pointer( + fx, data->dynEntDefList[0][i].destroyFx->name)); + } + if (data->dynEntDefList[0][i].physPreset) + { + dyn_entity_def[i].physPreset = reinterpret_cast(zone->get_asset_pointer( + physpreset, data->dynEntDefList[0][i].physPreset->name)); + } + + if (data->dynEntDefList[0][i].hinge) + { + dyn_entity_def[i].hinge = buf->write_s(3, dyn_entity_def[i].hinge, 1); + } + } + + ZoneBuffer::clear_pointer(&dest->dynEntDefList[0]); + } + + if (data->dynEntDefList[1]) + { + buf->align(3); + auto dyn_entity_def = buf->write(data->dynEntDefList[1], data->dynEntCount[1]); + + for (std::uint16_t i = 0; i < data->dynEntCount[1]; i++) + { + if (data->dynEntDefList[1][i].xModel) + { + dyn_entity_def[i].xModel = reinterpret_cast(zone->get_asset_pointer( + xmodel, data->dynEntDefList[1][i].xModel->name)); + } + if (data->dynEntDefList[1][i].destroyFx) + { + dyn_entity_def[i].destroyFx = reinterpret_cast(zone->get_asset_pointer( + fx, data->dynEntDefList[1][i].destroyFx->name)); + } + if (data->dynEntDefList[1][i].physPreset) + { + dyn_entity_def[i].physPreset = reinterpret_cast(zone->get_asset_pointer( + physpreset, data->dynEntDefList[1][i].physPreset->name)); + } + + if (data->dynEntDefList[1][i].hinge) + { + dyn_entity_def[i].hinge = buf->write_s(3, dyn_entity_def[i].hinge, 1); + } + } + + ZoneBuffer::clear_pointer(&dest->dynEntDefList[1]); + } + + buf->push_stream(2); + + if (data->dynEntPoseList[0]) + { + buf->align(3); + buf->write(data->dynEntPoseList[0], data->dynEntCount[0]); + ZoneBuffer::clear_pointer(&dest->dynEntPoseList[0]); + } + + if (data->dynEntPoseList[1]) + { + buf->align(3); + buf->write(data->dynEntPoseList[1], data->dynEntCount[1]); + ZoneBuffer::clear_pointer(&dest->dynEntPoseList[1]); + } + + if (data->dynEntClientList[0]) + { + buf->align(3); + buf->write(data->dynEntClientList[0], data->dynEntCount[0]); + ZoneBuffer::clear_pointer(&dest->dynEntClientList[0]); + } + + if (data->dynEntClientList[1]) + { + buf->align(3); + buf->write(data->dynEntClientList[1], data->dynEntCount[1]); + ZoneBuffer::clear_pointer(&dest->dynEntClientList[1]); + } + + if (data->dynEntCollList[0]) + { + buf->align(3); + buf->write(data->dynEntCollList[0], data->dynEntCount[0]); + ZoneBuffer::clear_pointer(&dest->dynEntCollList[0]); + } + + if (data->dynEntCollList[1]) + { + buf->align(3); + buf->write(data->dynEntCollList[1], data->dynEntCount[1]); + ZoneBuffer::clear_pointer(&dest->dynEntCollList[1]); + } + + buf->pop_stream(); + + END_LOG_STREAM; + buf->pop_stream(); + + encrypt_data(dest, sizeof clipMap_t); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + +#undef INFO +#define INFO info-> + + void dump_info(ClipInfo* info, AssetDumper& write) + { + write.dump_int(INFO numCPlanes); + write.dump_array(INFO cPlanes, INFO numCPlanes); + + write.dump_int(INFO numMaterials); + write.dump_array(INFO materials, INFO numMaterials); + + for (auto i = 0; i < INFO numMaterials; i++) + { + if (INFO materials[i].material) + { + write.dump_string(INFO materials[i].material); + } + } + + write.dump_int(INFO numCBrushSides); + write.dump_array(INFO cBrushSides, INFO numCBrushSides); + + for (auto i = 0; i < INFO numCBrushSides; i++) + { + if (INFO cBrushSides[i].plane) + { + write.dump_array(INFO cBrushSides[i].plane, 1); + } + } + + write.dump_int(INFO numCBrushEdges); + write.dump_array(INFO cBrushEdges, INFO numCBrushEdges); + + write.dump_int(INFO numLeafBrushes); + write.dump_array(INFO leafBrushes, INFO numLeafBrushes); + + write.dump_int(INFO numCLeafBrushNodes); + write.dump_array(INFO cLeafBrushNodes, INFO numCLeafBrushNodes); + + for (auto i = 0; i < INFO numCLeafBrushNodes; i++) + { + if (INFO cLeafBrushNodes[i].leafBrushCount > 0) + { + write.dump_array(INFO cLeafBrushNodes[i].data.leaf.brushes, INFO cLeafBrushNodes[i].leafBrushCount); + } + } + + write.dump_int(INFO numBrushes); + write.dump_array(INFO brushes, INFO numBrushes); + + for (auto i = 0; i < INFO numBrushes; i++) + { + if (INFO brushes[i].sides) + { + write.dump_array(INFO brushes[i].sides, 1); + + if (INFO brushes[i].sides->plane) + { + write.dump_array(INFO brushes[i].sides->plane, 1); + } + } + if (INFO brushes[i].edge) + { + write.dump_array(INFO brushes[i].edge, 1); + } + } + + write.dump_array(INFO brushBounds, INFO numBrushes); + write.dump_array(INFO brushContents, INFO numBrushes); + } + + void IClipMap::dump(clipMap_t* asset) + { + AssetDumper write; + if (!write.open(asset->name + ".colmap"s)) + { + return; + } + + write.dump_string(asset->name); + write.dump_int(asset->isInUse); + + dump_info(&asset->info, write); + + write.dump_int(asset->numStaticModels); + write.dump_array(asset->staticModelList, asset->numStaticModels); + + for (auto i = 0; i < asset->numStaticModels; i++) + { + if (asset->staticModelList[i].xmodel) + { + write.dump_asset(asset->staticModelList[i].xmodel); + } + } + + write.dump_int(asset->numCNodes); + write.dump_array(asset->cNodes, asset->numCNodes); + + for (auto i = 0; i < asset->numCNodes; i++) + { + if (asset->cNodes[i].plane) + { + write.dump_array(asset->cNodes[i].plane, 1); + } + } + + write.dump_int(asset->numCLeaf); + write.dump_array(asset->cLeaf, asset->numCLeaf); + + write.dump_int(asset->numVerts); + write.dump_array(asset->verts, asset->numVerts); + + write.dump_int(asset->numTriIndices); + write.dump_array(asset->triIndices, asset->numTriIndices * 3); + write.dump_array(asset->triEdgeIsWalkable, 4 * ((3 * asset->numTriIndices + 31) >> 5)); + + write.dump_int(asset->numCollisionBorders); + write.dump_array(asset->collisionBorders, asset->numCollisionBorders); + + write.dump_int(asset->numCollisionPartitions); + write.dump_array(asset->collisionPartitions, asset->numCollisionPartitions); + + for (auto i = 0; i < asset->numCollisionPartitions; i++) + { + if (asset->collisionPartitions[i].borders) + { + write.dump_array(asset->collisionPartitions[i].borders, asset->collisionPartitions[i].borderCount); + } + } + + write.dump_int(asset->numCollisionAABBTrees); + write.dump_array(asset->collisionAABBTrees, asset->numCollisionAABBTrees); + + write.dump_int(asset->numCModels); + write.dump_array(asset->cModels, asset->numCModels); + + for (int i = 0; i < asset->numCModels; i++) + { + if (asset->cModels[i].info) + { + dump_info(asset->cModels[i].info, write); + } + } + + write.dump_asset(asset->mapEnts); + + write.dump_int(asset->smodelNodeCount); + write.dump_array(asset->smodelNodes, asset->smodelNodeCount); + + // save file to disk + write.close(); + + // dump stages + if (asset->stages) + { + AssetDumper stageDumper; + + if (stageDumper.open(asset->name + ".ents.stages"s)) + { + stageDumper.dump_int(asset->stageCount); + if (asset->stageCount) + { + stageDumper.dump_array(asset->stages, asset->stageCount); + + for (int i = 0; i < asset->stageCount; i++) + { + stageDumper.dump_string(asset->stages[i].name); + } + } + } + + stageDumper.close(); + } + } + } +} diff --git a/src/IW5/Assets/ClipMap.hpp b/src/IW5/Assets/ClipMap.hpp new file mode 100644 index 0000000..6a834fd --- /dev/null +++ b/src/IW5/Assets/ClipMap.hpp @@ -0,0 +1,36 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IClipMap : public IAsset + { + private: + std::string name_; + clipMap_t* asset_ = nullptr; + + public: + static clipMap_t* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write_info(IZone* zone, ZoneBuffer* buf, ClipInfo* data, ClipInfo* dest); + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(clipMap_t* asset); + }; + } +} diff --git a/src/IW5/Assets/ComWorld.cpp b/src/IW5/Assets/ComWorld.cpp new file mode 100644 index 0000000..c66b6c7 --- /dev/null +++ b/src/IW5/Assets/ComWorld.cpp @@ -0,0 +1,160 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + /*legacy zonetool code, refactor me!*/ +#define PARSE_STRING(entry) \ + if (!commapdata[#entry].is_null()) { \ + asset->entry = _strdup(commapdata[#entry].get().c_str()); \ + } else { asset->entry = nullptr; } +#define PARSE_INT(entry) \ + asset->entry = commapdata[#entry].get() +#define PARSE_FLOAT(entry) \ + asset->entry = commapdata[#entry].get() +#define PARSE_FLOAT_ARRAY(entry,size) \ + for (int i = 0; i < size; i++) \ + asset->entry[i] = commapdata[#entry][i].get(); + + void PrimaryLight_Parse(ComPrimaryLight* asset, nlohmann::json commapdata) + { + PARSE_STRING(defName); + + PARSE_INT(type); + PARSE_INT(canUseShadowMap); + PARSE_INT(canUseShadowMap); + + PARSE_FLOAT_ARRAY(color, 3); + PARSE_FLOAT_ARRAY(dir, 3); + PARSE_FLOAT_ARRAY(origin, 3); + + PARSE_FLOAT(radius); + PARSE_FLOAT(cosHalfFovOuter); + PARSE_FLOAT(cosHalfFovInner); + PARSE_FLOAT(cosHalfFovExpanded); + PARSE_FLOAT(rotationLimit); + PARSE_FLOAT(translationLimit); + } + + ComWorld* IComWorld::parse(const std::string& name, ZoneMemory* mem) + { + auto path = name + ".comworld"; + if (FileSystem::FileExists(path)) + { + ZONETOOL_INFO("Parsing commap \"%s\"...", name.c_str()); + + auto asset = mem->Alloc(); + + auto file = FileSystem::FileOpen(path, "rb"); + auto size = FileSystem::FileSize(file); + auto bytes = FileSystem::ReadBytes(file, size); + FileSystem::FileClose(file); + + nlohmann::json commapdata = nlohmann::json::parse(bytes); + + PARSE_STRING(name); + PARSE_INT(isInUse); + PARSE_INT(primaryLightCount); + + asset->primaryLights = mem->Alloc(asset->primaryLightCount); + + nlohmann::json primaryLights = commapdata["primaryLights"]; + for (int i = 0; i < asset->primaryLightCount; i++) + { + PrimaryLight_Parse(&asset->primaryLights[i], primaryLights[i]); + } + + return asset; + } + + return nullptr; + } + + void IComWorld::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data(), 1).comworld; + } + } + + void IComWorld::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IComWorld::load_depending(IZone* zone) + { + auto asset = this->asset_; + + for (int i = 0; i < asset->primaryLightCount; i++) + { + if (asset->primaryLights[i].defName) + { + zone->add_asset_of_type(lightdef, asset->primaryLights[i].defName); + } + } + } + + std::string IComWorld::name() + { + return this->name_; + } + + std::int32_t IComWorld::type() + { + return com_map; + } + + void IComWorld::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + + dest->name = buf->write_str(this->name()); + + if (data->primaryLights) + { + buf->align(3); + auto* primary_light = buf->write(data->primaryLights, data->primaryLightCount); + + for (auto i = 0u; i < data->primaryLightCount; i++) + { + if (data->primaryLights[i].defName) + { + primary_light[i].defName = buf->write_str(data->primaryLights[i].defName); + } + } + } + + buf->pop_stream(); + + encrypt_data(dest, sizeof ComWorld); + } + + void IComWorld::dump(ComWorld* asset, bool fromIW5) + { + const auto data = asset->ToJson(fromIW5); + + const auto path = asset->name + ".comworld"s; + const auto json = data.dump(4); + + auto* file = FileSystem::FileOpen(path, "w"s); + fwrite(json.data(), json.size(), 1, file); + FileSystem::FileClose(file); + } + } +} diff --git a/src/IW5/Assets/ComWorld.hpp b/src/IW5/Assets/ComWorld.hpp new file mode 100644 index 0000000..d034733 --- /dev/null +++ b/src/IW5/Assets/ComWorld.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IComWorld : public IAsset + { + private: + std::string name_; + ComWorld* asset_ = nullptr; + + public: + static ComWorld* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(ComWorld* asset, bool fromIW5 = true); + }; + } +} diff --git a/src/IW5/Assets/FontDef.cpp b/src/IW5/Assets/FontDef.cpp new file mode 100644 index 0000000..28b3037 --- /dev/null +++ b/src/IW5/Assets/FontDef.cpp @@ -0,0 +1,190 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + IFontDef::IFontDef() + { + } + + IFontDef::~IFontDef() + { + } + + void IFontDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data(), 1).font; + } + } + + void IFontDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IFontDef::load_depending(IZone* zone) + { + auto* data = this->asset_; + + if (data->material) + { + zone->add_asset_of_type(material, data->material->name); + } + + if (data->glowMaterial) + { + zone->add_asset_of_type(material, data->glowMaterial->name); + } + } + + std::string IFontDef::name() + { + return this->name_; + } + + std::int32_t IFontDef::type() + { + return font; + } + + void IFontDef::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->material) + { + dest->material = reinterpret_cast( + zone->get_asset_pointer(material, data->material->name) + ); + } + + if (data->glowMaterial) + { + dest->glowMaterial = reinterpret_cast( + zone->get_asset_pointer(material, data->glowMaterial->name) + ); + } + + if (data->glyphs) + { + buf->align(3); + buf->write(data->glyphs, data->glyphCount); + ZoneBuffer::clear_pointer(&dest->glyphs); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + Font_s* IFontDef::parse(const std::string& name, ZoneMemory* mem) + { + auto path = name; + auto file = FileSystem::FileOpen(path, "rb"s); + if (!file) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing font \"%s\"...", name.c_str()); + + auto size = FileSystem::FileSize(file); + auto bytes = FileSystem::ReadBytes(file, size); + FileSystem::FileClose(file); + + nlohmann::json fontdata = nlohmann::json::parse(bytes); + + auto font = mem->Alloc(); + font->name = strdup(fontdata["fontName"].get().c_str()); + font->glyphCount = fontdata["glyphCount"].get(); + font->pixelHeight = fontdata["pixelHeight"].get(); + + font->material = mem->Alloc(); + font->material->name = strdup(fontdata["material"].get().c_str()); + font->glowMaterial = mem->Alloc(); + font->glowMaterial->name = strdup(fontdata["glowMaterial"].get().c_str()); + + font->glyphs = mem->Alloc(font->glyphCount); + + for (int i = 0; i < font->glyphCount; i++) + { + font->glyphs[i].letter = fontdata["glyphs"][i].get(); + font->glyphs[i].x0 = fontdata["glyphs"][i].get(); + font->glyphs[i].y0 = fontdata["glyphs"][i].get(); + font->glyphs[i].dx = fontdata["glyphs"][i].get(); + font->glyphs[i].pixelWidth = fontdata["glyphs"][i].get(); + font->glyphs[i].pixelHeight = fontdata["glyphs"][i].get(); + font->glyphs[i].s0, fontdata["glyphs"][i].get(); + font->glyphs[i].t0, fontdata["glyphs"][i].get(); + font->glyphs[i].s1, fontdata["glyphs"][i].get(); + font->glyphs[i].t1, fontdata["glyphs"][i].get(); + } + + return font; + } + + void IFontDef::dump(Font_s* asset) + { + nlohmann::json fontdata; + + ZONETOOL_INFO("Dumping font %s", asset->name); + + fontdata["fontName"] = std::string(asset->name); + fontdata["glyphCount"] = asset->glyphCount; + fontdata["material"] = std::string(asset->material->name); + fontdata["glowMaterial"] = std::string(asset->glowMaterial->name); + fontdata["pixelHeight"] = asset->pixelHeight; + + nlohmann::json glyphs; + for (int i = 0; i < asset->glyphCount; i++) + { + nlohmann::json glyph; + + glyph["letter"] = asset->glyphs[i].letter; + glyph["x0"] = asset->glyphs[i].x0; + glyph["y0"] = asset->glyphs[i].y0; + glyph["dx"] = asset->glyphs[i].dx; + glyph["pixelWidth"] = asset->glyphs[i].pixelWidth; + glyph["pixelHeight"] = asset->glyphs[i].pixelHeight; + glyph["s0"] = asset->glyphs[i].s0; + glyph["t0"] = asset->glyphs[i].t0; + glyph["s1"] = asset->glyphs[i].s1; + glyph["t1"] = asset->glyphs[i].t1; + + glyphs.push_back(glyph); + } + + fontdata["glyphs"] = glyphs; + + std::string assetstr = fontdata.dump(4); + + auto assetPath = asset->name; + + auto fileAsset = FileSystem::FileOpen(assetPath, "wb"); + + if (fileAsset) + { + fwrite(assetstr.c_str(), assetstr.size(), 1, fileAsset); + FileSystem::FileClose(fileAsset); + } + } + } +} diff --git a/src/IW5/Assets/FontDef.hpp b/src/IW5/Assets/FontDef.hpp new file mode 100644 index 0000000..cc92de7 --- /dev/null +++ b/src/IW5/Assets/FontDef.hpp @@ -0,0 +1,37 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IFontDef : public IAsset + { + private: + std::string name_; + Font_s* asset_ = nullptr; + + public: + IFontDef(); + ~IFontDef(); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(Font_s* asset); + static Font_s* parse(const std::string& name, ZoneMemory* mem); + }; + } +} diff --git a/src/IW5/Assets/FxEffectDef.cpp b/src/IW5/Assets/FxEffectDef.cpp new file mode 100644 index 0000000..ebf9065 --- /dev/null +++ b/src/IW5/Assets/FxEffectDef.cpp @@ -0,0 +1,743 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void parse_visuals(AssetReader* read, FxElemDef* def, FxElemVisuals* vis) + { + switch (def->elemType) + { + case FX_ELEM_TYPE_RUNNER: + if (vis->effectDef) + { + vis->effectDef = read->read_asset(); + } + break; + case FX_ELEM_TYPE_SOUND: + if (vis->soundName) + { + vis->soundName = read->read_string(); + } + break; + case FX_ELEM_TYPE_SPOT_LIGHT: + if (vis->lightDef) + { + vis->lightDef = read->read_asset(); + } + break; + case FX_ELEM_TYPE_MODEL: + if (vis->xmodel) + { + vis->xmodel = read->read_asset(); + } + break; + default: + if (def->elemType != FX_ELEM_TYPE_OMNI_LIGHT) + { + if (vis->material) + { + vis->material = read->read_asset(); + } + } + } + } + + FxEffectDef* IFxEffectDef::parse(const std::string& name, ZoneMemory* mem) + { + AssetReader read(mem); + if (!read.open("fx\\"s + name + ".fxe")) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing fx \"%s\"...", name.data()); + + const auto asset = read.read_single(); + asset->name = read.read_string(); + asset->elemDefs = read.read_array(); + + for (auto i = 0; i < asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot; i++) + { + auto def = &asset->elemDefs[i]; + + def->velSamples = read.read_array(); + def->visSamples = read.read_array(); + + if (def->elemType == FX_ELEM_TYPE_DECAL) + { + if (def->visuals.markArray) + { + def->visuals.markArray = read.read_array(); + + for (int i = 0; i < def->visualCount; i++) + { + if (def->visuals.markArray[i][0]) + { + def->visuals.markArray[i][0] = read.read_asset(); + } + if (def->visuals.markArray[i][1]) + { + def->visuals.markArray[i][1] = read.read_asset(); + } + } + } + } + else if (def->visualCount > 1) + { + def->visuals.array = read.read_array(); + + for (auto vis = 0; vis < def->visualCount; vis++) + { + parse_visuals(&read, def, &def->visuals.array[vis]); + } + } + else + { + parse_visuals(&read, def, &def->visuals.instance); + } + + def->effectOnImpact = read.read_asset(); + def->effectOnDeath = read.read_asset(); + def->effectEmitted = read.read_asset(); + + if (def->extended.trailDef) + { + if (def->elemType == FX_ELEM_TYPE_TRAIL) + { + def->extended.trailDef = read.read_single(); + + if (def->extended.trailDef->verts) + { + def->extended.trailDef->verts = read.read_array(); + } + + if (def->extended.trailDef->inds) + { + def->extended.trailDef->inds = read.read_array(); + } + } + else if (def->elemType == FX_ELEM_TYPE_SPARKFOUNTAIN) + { + def->extended.sparkFountain = read.read_single(); + } + else if (def->elemType == FX_ELEM_TYPE_SPOT_LIGHT) + { + def->extended.unknownDef = read.read_array(); + } + else + { + def->extended.unknownDef = read.read_single(); + } + } + } + + read.close(); + + return asset; + } + + void IFxEffectDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).fx; + } + } + + void IFxEffectDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IFxEffectDef::load_depending(IZone* zone) + { + auto data = this->asset_; + + auto load_FxElemVisuals = [zone](FxElemDef* def, FxElemDefVisuals* vis) + { + if (def->elemType == 11) + { + for (int i = 0; i < def->visualCount; i++) + { + if (vis->markArray[i]) + { + if (vis->markArray[i][0]) + zone->add_asset_of_type(material, vis->markArray[i][0]->name); + if (vis->markArray[i][1]) + zone->add_asset_of_type(material, vis->markArray[i][1]->name); + } + } + } + else if (def->visualCount > 1) + { + for (int i = 0; i < def->visualCount; i++) + { + if (def->elemType == 12) + zone->add_asset_of_type(fx, vis->array[i].effectDef->name); + else if (def->elemType == 10) + zone->add_asset_of_type(sound, vis->array[i].soundName); + else if (def->elemType == 7) + zone->add_asset_of_type(xmodel, vis->array[i].xmodel->name); + else + { + if (def->elemType != 8) + zone->add_asset_of_type(material, vis->array[i].material->name); + } + } + } + else + { + if (def->elemType == 12) + zone->add_asset_of_type(fx, vis->instance.effectDef->name); + else if (def->elemType == 10) + zone->add_asset_of_type(sound, vis->instance.soundName); + else if (def->elemType == 7) + zone->add_asset_of_type(xmodel, vis->instance.xmodel->name); + else + { + if (def->elemType != 8) + zone->add_asset_of_type(material, vis->instance.material->name); + } + } + }; + + // Loop through frames + for (int i = 0; i < data->elemDefCountEmission + data->elemDefCountLooping + data->elemDefCountOneShot; i++) + { + auto& def = data->elemDefs[i]; + + // Sub-FX effects + if (def.effectEmitted) + zone->add_asset_of_type(fx, def.effectEmitted->name); + if (def.effectOnDeath) + zone->add_asset_of_type(fx, def.effectOnDeath->name); + if (def.effectOnImpact) + zone->add_asset_of_type(fx, def.effectOnImpact->name); + + // Visuals + load_FxElemVisuals(&def, &def.visuals); + } + } + + std::string IFxEffectDef::name() + { + return this->name_; + } + + std::int32_t IFxEffectDef::type() + { + return fx; + } + + void IFxEffectDef::write_fx_elem_visuals(IZone* zone, ZoneBuffer* buf, FxElemDef* def, + FxElemVisuals* dest) + { + auto data = dest; + + switch (def->elemType) + { + case FX_ELEM_TYPE_RUNNER: + { + buf->write_str(data->effectDef->name); + ZoneBuffer::clear_pointer(&dest->effectDef); + break; + } + case FX_ELEM_TYPE_SOUND: + { + if (data->soundName) + { + buf->write_str(data->soundName); + ZoneBuffer::clear_pointer(&dest->soundName); + } + break; + } + case FX_ELEM_TYPE_SPOT_LIGHT: + { + if ((data->anonymous)) dest->anonymous = zone->get_asset_pointer(lightdef, ((GfxLightDef*)data->anonymous)->name); + else dest->anonymous = nullptr; + break; + } + case FX_ELEM_TYPE_MODEL: + { + dest->xmodel = (data->xmodel) + ? reinterpret_cast(zone->get_asset_pointer(xmodel, data->xmodel->name)) + : nullptr; + break; + } + default: + { + if (def->elemType != FX_ELEM_TYPE_OMNI_LIGHT) + { + dest->material = (data->material) + ? reinterpret_cast(zone->get_asset_pointer( + material, data->material->name)) + : nullptr; + } + } + } + } + + void IFxEffectDef::write_fx_elem_def_visuals(IZone* zone, ZoneBuffer* buf, FxElemDef* def, + FxElemDefVisuals* dest) + { + auto data = dest; + + if (def->elemType == FX_ELEM_TYPE_DECAL) + { + if (data->markArray) + { + auto destvisuals = buf->write(data->markArray, def->visualCount); + + for (int i = 0; i < def->visualCount; i++) + { + destvisuals[i][0] = (data->markArray[i][0]) + ? reinterpret_cast(zone->get_asset_pointer( + material, data->markArray[i][0]->name)) + : nullptr; + destvisuals[i][1] = (data->markArray[i][1]) + ? reinterpret_cast(zone->get_asset_pointer( + material, data->markArray[i][1]->name)) + : nullptr; + } + } + } + else if (def->visualCount > 1) + { + auto vis = buf->write(data->array, def->visualCount); + + for (int i = 0; i < def->visualCount; i++) + { + write_fx_elem_visuals(zone, buf, def, &vis[i]); + } + } + else + { + write_fx_elem_visuals(zone, buf, def, &dest->instance); + } + } + + void IFxEffectDef::write_fx_elem_def(IZone* zone, ZoneBuffer* buf, FxElemDef* dest) + { + auto data = dest; + + if (data->velSamples) + { + buf->align(3); + buf->write(data->velSamples, data->velIntervalCount + 1); + ZoneBuffer::clear_pointer(&dest->velSamples); + } + + if (data->visSamples) + { + buf->align(3); + buf->write(data->visSamples, data->visStateIntervalCount + 1); + ZoneBuffer::clear_pointer(&dest->visSamples); + } + + write_fx_elem_def_visuals(zone, buf, data, &dest->visuals); + + if (data->effectOnImpact) + { + buf->write_str_raw(data->effectOnImpact->name); + ZoneBuffer::clear_pointer(&dest->effectOnImpact); + } + + if (data->effectOnDeath) + { + buf->write_str_raw(data->effectOnDeath->name); + ZoneBuffer::clear_pointer(&dest->effectOnDeath); + } + + if (data->effectEmitted) + { + buf->write_str_raw(data->effectEmitted->name); + ZoneBuffer::clear_pointer(&dest->effectEmitted); + } + + if (data->extended.trailDef) + { + if (data->elemType == FX_ELEM_TYPE_TRAIL) + { + if (data->extended.trailDef) + { + buf->align(3); + buf->write(data->extended.trailDef, sizeof(FxTrailDef)); + + if (data->extended.trailDef->verts) + { + buf->align(3); + buf->write(data->extended.trailDef->verts, data->extended.trailDef->vertCount); + } + + if (data->extended.trailDef->inds) + { + buf->align(1); + buf->write(data->extended.trailDef->inds, data->extended.trailDef->indCount); + } + + ZoneBuffer::clear_pointer(&dest->extended.trailDef); + } + } + else if (data->elemType == FX_ELEM_TYPE_SPARKFOUNTAIN) + { + if (data->extended.sparkFountain) + { + buf->align(3); + buf->write(data->extended.sparkFountain); + ZoneBuffer::clear_pointer(&dest->extended.sparkFountain); + } + } + else if (data->elemType == FX_ELEM_TYPE_SPOT_LIGHT) + { + if (data->extended.unknownDef) + { + buf->align(3); + buf->write_stream(data->extended.unknownDef, 24); + ZoneBuffer::clear_pointer(&dest->extended.unknownDef); + } + } + else + { + if (data->extended.unknownDef) + { + buf->align(1); + buf->write_stream(data->extended.unknownDef, sizeof(BYTE)); + ZoneBuffer::clear_pointer(&dest->extended.unknownDef); + } + } + } + } + + void IFxEffectDef::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->elemDefs) + { + buf->align(3); + auto destdef = buf->write(data->elemDefs, + data->elemDefCountEmission + data->elemDefCountLooping + data-> + elemDefCountOneShot); + + for (std::int32_t i = 0; i < (data->elemDefCountEmission + data->elemDefCountLooping + data-> + elemDefCountOneShot); i++) + { + write_fx_elem_def(zone, buf, &destdef[i]); + } + + ZoneBuffer::clear_pointer(&dest->elemDefs); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + std::vector> flagMap = + { + //{ FX_ED_FLAG_LOOPING, "looping" }, + //{ FX_ED_FLAG_USE_RANDOM_COLOR, "useRandColor" }, + //{ FX_ED_FLAG_USE_RANDOM_ALPHA, "useRandAlpha" }, + //{ FX_ED_FLAG_USE_RANDOM_SIZE_0, "useRandSize0" }, + //{ FX_ED_FLAG_USE_RANDOM_SIZE_1, "useRandSize1" }, + //{ FX_ED_FLAG_USE_RANDOM_SCALE, "useRandScale" }, + //{ FX_ED_FLAG_USE_RANDOM_ROTATION_DELTA, "useRandRotDelta" }, + //{ FX_ED_FLAG_MODULATE_COLOR_BY_ALPHA, "modColorByAlpha" }, + //{ FX_ED_FLAG_USE_RANDOM_VELOCITY_0, "useRandVel0" }, + //{ FX_ED_FLAG_USE_RANDOM_VELOCITY_1, "useRandVel1" }, + //{ FX_ED_FLAG_BACKCOMPAT_VELOCITY, "useBackCompatVel" }, + //{ FX_ED_FLAG_ABSOLUTE_VELOCITY_0, "absVel0" }, + //{ FX_ED_FLAG_ABSOLUTE_VELOCITY_1, "absVel1" }, + + // { FX_ED_FLAG_PLAY_ON_TOUCH, "useBackCompatVel" }, + // { FX_ED_FLAG_BACKCOMPAT_VELOCITY, "useBackCompatVel" }, + }; + + std::string GenerateFlagsString(int flags) + { + std::string flagString = ""; + + for (auto i = 0u; i < flagMap.size(); i++) + { + if ((flags & flagMap[i].first) == flagMap[i].first) + { + flagString += flagMap[i].second + " "s; + } + } + + if (!flagString.empty()) + { + flagString = flagString.substr(0, flagString.length() - 1); + } + + return flagString; + } + + void IFxEffectDef::dumpToLegacyFormat(FxEffectDef* asset) + { + auto fp = FileSystem::FileOpen("fx\\"s + asset->name + ".fx_raw"s, "wb"); + if (!fp) + { + return; + } + +#define PRINTFIELDINTERNAL(__IDENT__, __FIELD__, __DATA__) \ + for (int i = 0; i < __IDENT__; i++) { fprintf(fp, "\t"); } \ + fprintf(fp, __FIELD__ " "); \ + fprintf(fp, __DATA__); \ + fprintf(fp, ";\n") + +#define PRINTSTRING(__IDENT__, __FIELD__, __VALUE__) \ + PRINTFIELDINTERNAL(__IDENT__, __FIELD__, &va("\"%s\"", __VALUE__)[0]) + +#define PRINTFLOATRANGE(__IDENT__, __FIELD__, __VALUE__) \ + PRINTFIELDINTERNAL(__IDENT__, __FIELD__, &va("%g %g", __VALUE__.base, __VALUE__.amplitude)[0]) + +#define PRINTFLOAT(__IDENT__, __FIELD__, __VALUE__) \ + PRINTFIELDINTERNAL(__IDENT__, __FIELD__, &va("%g", __VALUE__)[0]) + +#define PRINTINTRANGE(__IDENT__, __FIELD__, __VALUE__) \ + PRINTFIELDINTERNAL(__IDENT__, __FIELD__, &va("%i %i", __VALUE__.base, __VALUE__.amplitude)[0]) + +#define PRINTINT(__IDENT__, __FIELD__, __VALUE__) \ + PRINTFIELDINTERNAL(__IDENT__, __FIELD__, &va("%i", __VALUE__)[0]) + + fprintf(fp, "iwfx 2\n\n"); + for (int i = 0; i < asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot; i + ++) + { + auto elem = &asset->elemDefs[i]; + + fprintf(fp, "{\n"); + + PRINTSTRING(1, "name", &va("elem%i", i)[0]); + /*if (i > asset->elemDefCountEmission && i < asset->elemDefCountEmission + asset->elemDefCountLooping) + { + fprintf(fp_, "\teditorFlags looping;\n"); + }*/ + PRINTSTRING(1, "flags", &GenerateFlagsString(elem->flags)[0]); + PRINTFLOATRANGE(1, "spawnRange", elem->spawnRange); + PRINTFLOATRANGE(1, "fadeInRange", elem->fadeInRange); + PRINTFLOATRANGE(1, "fadeOutRange", elem->fadeOutRange); + PRINTFLOAT(1, "spawnFrustumCullRadius", elem->spawnFrustumCullRadius); + PRINTFIELDINTERNAL(1, "spawnLooping", &va("%i %i", elem->spawn.looping.intervalMsec, elem->spawn.looping +.count)[0]); + PRINTFIELDINTERNAL(1, "spawnOneShot", &va("%i %i", elem->spawn.oneShot.count.base, elem->spawn.oneShot. +count.amplitude)[0]); + PRINTINTRANGE(1, "spawnDelayMsec", elem->spawnDelayMsec); + PRINTINTRANGE(1, "lifeSpanMsec", elem->lifeSpanMsec); + PRINTFLOATRANGE(1, "spawnOrgX", elem->spawnOrigin[0]); + PRINTFLOATRANGE(1, "spawnOrgY", elem->spawnOrigin[1]); + PRINTFLOATRANGE(1, "spawnOrgZ", elem->spawnOrigin[2]); + PRINTFLOATRANGE(1, "spawnOffsetRadius", elem->spawnOffsetRadius); + PRINTFLOATRANGE(1, "spawnOffsetHeight", elem->spawnOffsetHeight); + PRINTFLOATRANGE(1, "spawnAnglePitch", elem->spawnAngles[0]); + PRINTFLOATRANGE(1, "spawnAngleYaw", elem->spawnAngles[1]); + PRINTFLOATRANGE(1, "spawnAngleRoll", elem->spawnAngles[2]); + PRINTFLOATRANGE(1, "angleVelPitch", elem->angularVelocity[0]); + PRINTFLOATRANGE(1, "angleVelYaw", elem->angularVelocity[1]); + PRINTFLOATRANGE(1, "angleVelRoll", elem->angularVelocity[2]); + PRINTFLOATRANGE(1, "initialRot", elem->initialRotation); + PRINTFLOATRANGE(1, "gravity", elem->gravity); + PRINTFLOATRANGE(1, "elasticity", elem->reflectionFactor); // NOT SURE + + for (int g = 0; g < std::min(elem->velIntervalCount + 1, 2); g++) + { +#define DUMPVELGRAPH(__INDEX__, __CHARACTER__) \ + fprintf(fp, "\tvelGraph%i" __CHARACTER__ " 0\n", g); \ + fprintf(fp, "\t{\n"); \ + fprintf(fp, "\t\t{\n"); \ + fprintf(fp, "\t\t\t0 %g\n", elem->velSamples[g].local.velocity.base[__INDEX__]); \ + fprintf(fp, "\t\t\t1 %g\n", elem->velSamples[g].local.velocity.amplitude[__INDEX__]); \ + fprintf(fp, "\t\t}\n"); \ + fprintf(fp, "\t\t{\n"); \ + fprintf(fp, "\t\t\t0 %g\n", elem->velSamples[g].local.totalDelta.base[__INDEX__]); \ + fprintf(fp, "\t\t\t1 %g\n", elem->velSamples[g].local.totalDelta.amplitude[__INDEX__]); \ + fprintf(fp, "\t\t}\n"); \ + fprintf(fp, "\t};\n") + + DUMPVELGRAPH(0, "X"); + DUMPVELGRAPH(1, "Y"); + DUMPVELGRAPH(2, "Z"); + } + + // TODO dump atlas data + PRINTINT(1, "atlastBehavior", elem->atlas.behavior); + PRINTINT(1, "atlasIndex", elem->atlas.index); + PRINTINT(1, "atlasFps", elem->atlas.fps); + PRINTINT(1, "atlasLoopCount", elem->atlas.loopCount); + PRINTINT(1, "atlasColIndexBits", elem->atlas.colIndexBits); + PRINTINT(1, "atlasRowIndexBits", elem->atlas.rowIndexBits); + PRINTINT(1, "atlasEntryCount", elem->atlas.entryCount); + + // TODO dump graphs + PRINTINT(1, "lightingFrac", elem->lightingFrac); + // TODO convert bounds to origin + radius + PRINTSTRING(1, "fxOnImpact", (elem->effectOnImpact) ? elem->effectOnImpact->name : ""); + PRINTSTRING(1, "fxOnDeath", (elem->effectOnDeath) ? elem->effectOnDeath->name : ""); + PRINTINT(1, "sortOrder", elem->sortOrder); + PRINTSTRING(1, "emission", (elem->effectEmitted) ? elem->effectEmitted->name : ""); + // TODO add trail data + PRINTFLOATRANGE(1, "emitDist", elem->emitDist); + PRINTFLOATRANGE(1, "emitDistVariance", elem->emitDistVariance); + + fprintf(fp, "}\n"); + } + + + FileSystem::FileClose(fp); + } + + void dump_visuals(AssetDumper* dump, FxElemDef* def, FxElemVisuals* vis) + { + switch (def->elemType) + { + case FX_ELEM_TYPE_RUNNER: + if (vis->effectDef) + { + dump->dump_asset(vis->effectDef); + } + break; + case FX_ELEM_TYPE_SOUND: + if (vis->soundName) + { + dump->dump_string(vis->soundName); + } + break; + case FX_ELEM_TYPE_SPOT_LIGHT: + if (vis->lightDef) + { + dump->dump_asset(vis->lightDef); + } + break; + case FX_ELEM_TYPE_MODEL: + if (vis->xmodel) + { + dump->dump_asset(vis->xmodel); + } + break; + default: + if (def->elemType != FX_ELEM_TYPE_OMNI_LIGHT) + { + if (vis->material) + { + dump->dump_asset(vis->material); + } + } + } + } + + void IFxEffectDef::dump(FxEffectDef* asset) + { + AssetDumper dump; + + if (!dump.open("fx\\"s + asset->name + ".fxe")) + { + return; + } + + dump.dump_single(asset); + dump.dump_string(asset->name); + dump.dump_array(asset->elemDefs, + asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); + + // dump elemDefs + for (auto i = 0; i < asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot; i + ++) + { + auto def = &asset->elemDefs[i]; + + // dump elem samples + dump.dump_array(def->velSamples, def->velIntervalCount + 1); + dump.dump_array(def->visSamples, def->visStateIntervalCount + 1); + + // dump visuals + if (def->elemType == FX_ELEM_TYPE_DECAL) + { + if (def->visuals.markArray) + { + dump.dump_array(def->visuals.markArray, def->visualCount); + + for (int i = 0; i < def->visualCount; i++) + { + if (def->visuals.markArray[i][0]) + { + dump.dump_asset(def->visuals.markArray[i][0]); + } + if (def->visuals.markArray[i][1]) + { + dump.dump_asset(def->visuals.markArray[i][1]); + } + } + } + } + else if (def->visualCount > 1) + { + dump.dump_array(def->visuals.array, def->visualCount); + for (auto vis = 0; vis < def->visualCount; vis++) + { + dump_visuals(&dump, def, &def->visuals.array[vis]); + } + } + else + { + dump_visuals(&dump, def, &def->visuals.instance); + } + + // dump reference FX defs + dump.dump_asset(def->effectOnImpact); + dump.dump_asset(def->effectOnDeath); + dump.dump_asset(def->effectEmitted); + + // dump extended FX data + if (def->extended.trailDef) + { + if (def->elemType == FX_ELEM_TYPE_TRAIL) + { + dump.dump_single(def->extended.trailDef); + + if (def->extended.trailDef->verts) + { + dump.dump_array(def->extended.trailDef->verts, def->extended.trailDef->vertCount); + } + + if (def->extended.trailDef->inds) + { + dump.dump_array(def->extended.trailDef->inds, def->extended.trailDef->indCount); + } + } + else if (def->elemType == FX_ELEM_TYPE_SPARKFOUNTAIN) + { + dump.dump_single(def->extended.sparkFountain); + } + else if (def->elemType == FX_ELEM_TYPE_SPOT_LIGHT) + { + dump.dump_array(def->extended.unknownDef, 24); + } + else + { + dump.dump_single(def->extended.unknownDef); + } + } + } + + dump.close(); + } + } +} diff --git a/src/IW5/Assets/FxEffectDef.hpp b/src/IW5/Assets/FxEffectDef.hpp new file mode 100644 index 0000000..ad52120 --- /dev/null +++ b/src/IW5/Assets/FxEffectDef.hpp @@ -0,0 +1,42 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IFxEffectDef : public IAsset + { + private: + std::string name_; + FxEffectDef* asset_ = nullptr; + + static void write_fx_elem_def_visuals(IZone* zone, ZoneBuffer* buf, FxElemDef* def, + FxElemDefVisuals* dest); + static void write_fx_elem_def(IZone* zone, ZoneBuffer* buf, FxElemDef* dest); + static void write_fx_elem_visuals(IZone* zone, ZoneBuffer* buf, FxElemDef* def, + FxElemVisuals* dest); + + public: + static FxEffectDef* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dumpToLegacyFormat(FxEffectDef* asset); + static void dump(FxEffectDef* asset); + }; + } +} diff --git a/src/IW5/Assets/FxWorld.cpp b/src/IW5/Assets/FxWorld.cpp new file mode 100644 index 0000000..ca4677a --- /dev/null +++ b/src/IW5/Assets/FxWorld.cpp @@ -0,0 +1,284 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + FxWorld* IFxWorld::parse(const std::string& name, ZoneMemory* mem) + { + const auto path = name + ".fxmap"; + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing FxMap \"%s\" ...", name.data()); + + AssetReader read(mem); + if (!read.open(path)) + { + return nullptr; + } + + FxWorld *asset = read.read_array(); + asset->name = read.read_string(); + + asset->glassSys.defs = read.read_array(); + for (auto i = 0u; i < asset->glassSys.defCount; i++) + { + asset->glassSys.defs[i].material = read.read_asset(); + asset->glassSys.defs[i].materialShattered = read.read_asset(); + asset->glassSys.defs[i].physPreset = read.read_asset(); + } + + asset->glassSys.piecePlaces = read.read_array(); + asset->glassSys.pieceStates = read.read_array(); + asset->glassSys.pieceDynamics = read.read_array(); + asset->glassSys.geoData = read.read_array(); + asset->glassSys.isInUse = read.read_array(); + asset->glassSys.cellBits = read.read_array(); + asset->glassSys.visData = read.read_array(); + asset->glassSys.linkOrg = read.read_array>(); + asset->glassSys.halfThickness = read.read_array(); + asset->glassSys.lightingHandles = read.read_array(); + asset->glassSys.initPieceStates = read.read_array(); + asset->glassSys.initGeoData = read.read_array(); + read.close(); + + return asset; + } + + void IFxWorld::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data(), 1).fxworld; + } + } + + void IFxWorld::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IFxWorld::load_depending(IZone* zone) + { + auto* data = this->asset_; + if (data->glassSys.defs) + { + for (unsigned int i = 0; i < data->glassSys.defCount; i++) + { + if (data->glassSys.defs[i].physPreset) + { + zone->add_asset_of_type(physpreset, data->glassSys.defs[i].physPreset->name); + } + if (data->glassSys.defs[i].material) + { + zone->add_asset_of_type(material, data->glassSys.defs[i].material->name); + } + if (data->glassSys.defs[i].materialShattered) + { + zone->add_asset_of_type(material, data->glassSys.defs[i].materialShattered->name); + } + } + } + } + + std::string IFxWorld::name() + { + return this->name_; + } + + std::int32_t IFxWorld::type() + { + return fx_map; + } + + void IFxWorld::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->glassSys.defs) + { + buf->align(3); + auto* glass_def = buf->write(data->glassSys.defs, data->glassSys.defCount); + + for (std::uint32_t i = 0; i < data->glassSys.defCount; i++) + { + if (data->glassSys.defs[i].physPreset) + { + glass_def[i].physPreset = reinterpret_cast(zone->get_asset_pointer( + physpreset, data->glassSys.defs[i].physPreset->name)); + } + if (data->glassSys.defs[i].material) + { + glass_def[i].material = reinterpret_cast(zone->get_asset_pointer( + material, data->glassSys.defs[i].material->name)); + } + if (data->glassSys.defs[i].materialShattered) + { + glass_def[i].materialShattered = reinterpret_cast(zone->get_asset_pointer( + material, data->glassSys.defs[i].materialShattered->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->glassSys.defs); + } + + buf->push_stream(2); + if (data->glassSys.piecePlaces) + { + buf->align(3); + buf->write(data->glassSys.piecePlaces, data->glassSys.pieceLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.piecePlaces); + } + + if (data->glassSys.pieceStates) + { + buf->align(3); + buf->write(data->glassSys.pieceStates, data->glassSys.pieceLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.pieceStates); + } + + if (data->glassSys.pieceDynamics) + { + buf->align(3); + buf->write(data->glassSys.pieceDynamics, data->glassSys.pieceLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.pieceDynamics); + } + + if (data->glassSys.geoData) + { + buf->align(3); + buf->write(data->glassSys.geoData, data->glassSys.geoDataLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.geoData); + } + + if (data->glassSys.isInUse) + { + buf->align(3); + buf->write(data->glassSys.isInUse, data->glassSys.pieceWordCount); + ZoneBuffer::clear_pointer(&dest->glassSys.isInUse); + } + + if (data->glassSys.cellBits) + { + buf->align(3); + buf->write(data->glassSys.cellBits, data->glassSys.pieceWordCount * data->glassSys.cellCount); + ZoneBuffer::clear_pointer(&dest->glassSys.cellBits); + } + + if (data->glassSys.visData) + { + buf->align(15); + buf->write(data->glassSys.visData, (data->glassSys.pieceLimit + 15) & 0xFFFFFFF0); + ZoneBuffer::clear_pointer(&dest->glassSys.visData); + } + + if (data->glassSys.linkOrg) + { + buf->align(3); + buf->write(data->glassSys.linkOrg, data->glassSys.pieceLimit); + ZoneBuffer::clear_pointer(&dest->glassSys.linkOrg); + } + + if (data->glassSys.halfThickness) + { + buf->align(15); + buf->write(data->glassSys.halfThickness, (data->glassSys.pieceLimit + 3) & 0xFFFFFFFC); + ZoneBuffer::clear_pointer(&dest->glassSys.halfThickness); + } + buf->pop_stream(); + + if (data->glassSys.lightingHandles) + { + buf->align(1); + buf->write(data->glassSys.lightingHandles, data->glassSys.initPieceCount); + ZoneBuffer::clear_pointer(&dest->glassSys.lightingHandles); + } + + if (data->glassSys.initPieceStates) + { + buf->align(3); + buf->write(data->glassSys.initPieceStates, data->glassSys.initPieceCount); + ZoneBuffer::clear_pointer(&dest->glassSys.initPieceStates); + } + + if (data->glassSys.initGeoData) + { + buf->align(3); + buf->write(data->glassSys.initGeoData, data->glassSys.initGeoDataCount); + ZoneBuffer::clear_pointer(&dest->glassSys.initGeoData); + } + + END_LOG_STREAM; + buf->pop_stream(); + + encrypt_data(dest, sizeof FxWorld); + } + + void IFxWorld::dump(FxWorld* asset) + { + AssetDumper write; + + if (!write.open(asset->name + ".fxmap"s)) + { + return; + } + + write.dump_array(asset, 1); + + write.dump_string(asset->name); + + write.dump_array(asset->glassSys.defs, asset->glassSys.defCount); + for (unsigned int i = 0; i < asset->glassSys.defCount; i++) + { + write.dump_asset(asset->glassSys.defs[i].material); + write.dump_asset(asset->glassSys.defs[i].materialShattered); + write.dump_asset(asset->glassSys.defs[i].physPreset); + } + + write.dump_array(asset->glassSys.piecePlaces, asset->glassSys.pieceLimit); + + write.dump_array(asset->glassSys.pieceStates, asset->glassSys.pieceLimit); + + write.dump_array(asset->glassSys.pieceDynamics, asset->glassSys.pieceLimit); + + write.dump_array(asset->glassSys.geoData, asset->glassSys.geoDataLimit); + + write.dump_array(asset->glassSys.isInUse, asset->glassSys.pieceWordCount); + + write.dump_array(asset->glassSys.cellBits, asset->glassSys.pieceWordCount * asset->glassSys.cellCount); + + write.dump_array(asset->glassSys.visData, (asset->glassSys.pieceLimit + 15) & 0xFFFFFFF0); + + write.dump_array(asset->glassSys.linkOrg, asset->glassSys.pieceLimit); + + write.dump_array(asset->glassSys.halfThickness, (asset->glassSys.pieceLimit + 3) & 0xFFFFFFFC); + + write.dump_array(asset->glassSys.lightingHandles, asset->glassSys.initPieceCount); + + write.dump_array(asset->glassSys.initPieceStates, asset->glassSys.initPieceCount); + + write.dump_array(asset->glassSys.initGeoData, asset->glassSys.initGeoDataCount); + + write.close(); + } + } +} diff --git a/src/IW5/Assets/FxWorld.hpp b/src/IW5/Assets/FxWorld.hpp new file mode 100644 index 0000000..ab8c427 --- /dev/null +++ b/src/IW5/Assets/FxWorld.hpp @@ -0,0 +1,36 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IFxWorld : public IAsset + { + private: + std::string name_; + FxWorld* asset_ = nullptr; + GlassWorld* glassmap; + + public: + static FxWorld* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(FxWorld* asset); + }; + } +} diff --git a/src/IW5/Assets/GfxImage.cpp b/src/IW5/Assets/GfxImage.cpp new file mode 100644 index 0000000..ac10518 --- /dev/null +++ b/src/IW5/Assets/GfxImage.cpp @@ -0,0 +1,435 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + IGfxImage::IGfxImage() + { + } + + IGfxImage::~IGfxImage() + { + } + + std::string IGfxImage::clean_name(const std::string& name) + { + auto newName = name; + + for (auto i = 0u; i < name.size(); i++) + { + switch (newName[i]) + { + case '*': + newName[i] = '_'; + break; + } + } + + return newName; + } + + GfxImage* IGfxImage::parse(const std::string& name, ZoneMemory* mem) + { + auto path = "images\\" + this->clean_name(name) + ".ffimg"; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + auto fp = FileSystem::FileOpen(path, "rb"); + if (!fp) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing GfxImage \"%s\"...", name.data()); + + auto reader = FileSystem::ToReader(fp); + + auto img = mem->Alloc(); + img->mapType = reader->Read(); + img->semantic = reader->Read(); + img->category = reader->Read(); + img->flags = reader->Read(); + img->cardMemory = reader->Read(); + img->dataLen1 = reader->Read(); + img->dataLen2 = reader->Read(); + img->height = reader->Read(); + img->width = reader->Read(); + img->depth = reader->Read(); + img->loaded = reader->Read(); + img->name = mem->StrDup(reader->ReadString()); + + auto loaddef = mem->Alloc(); + loaddef->mipLevels = reader->Read(); + loaddef->flags = reader->Read(); + loaddef->dimensions[0] = reader->Read(); + loaddef->dimensions[1] = reader->Read(); + loaddef->dimensions[2] = reader->Read(); + loaddef->format = reader->Read(); + loaddef->dataSize = reader->Read(); + + GfxImageLoadDef* finalLoaddef = nullptr; + + if (loaddef->dataSize > 4) + { + finalLoaddef = mem->ManualAlloc( + sizeof GfxImageLoadDef + + (loaddef->dataSize - 4)); + memcpy(finalLoaddef, loaddef, sizeof GfxImageLoadDef); + reader->ReadManual(&finalLoaddef->texture, loaddef->dataSize, 1); + } + else + { + finalLoaddef = loaddef; + } + + + img->texture = finalLoaddef; + + FileSystem::FileClose(fp); + + return img; + } + + void IGfxImage::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + this->isMapImage = (this->name_.size() >= 6) + ? ((this->name_.substr(0, 6) == "*light" || this->name_.substr(0, 6) == "*refle" || + this->name_ == "$outdoor") + ? true + : false) + : false; + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data(), 1).gfximage; + } + } + + void IGfxImage::init(void* asset, ZoneMemory* mem) + { + this->asset_ = reinterpret_cast(asset); + this->name_ = this->asset_->name; + this->isMapImage = (this->name_.size() >= 6) + ? ((this->name_.substr(0, 6) == "*light" || this->name_.substr(0, 6) == "*refle" || + this->name_ == "$outdoor") + ? true + : false) + : false; + + auto parsed = this->parse(this->name_, mem); + if (parsed) + { + this->asset_ = parsed; + } + } + + void IGfxImage::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IGfxImage::load_depending(IZone* zone) + { + } + + std::string IGfxImage::name() + { + return this->name_; + } + + std::int32_t IGfxImage::type() + { + return image; + } + + struct IW3_GfxImageFileHeader + { + char tag[3]; + char version; + char format; + std::uint8_t flags; + __int16 dimensions[3]; + int fileSizeForPicmip[4]; + }; + + enum class iw3_file_image_flags_t : std::uint8_t + { + IMG_FLAG_NOPICMIP = 0x1, + IMG_FLAG_NOMIPMAPS = 0x2, + IMG_FLAG_CUBEMAP = 0x4, + IMG_FLAG_VOLMAP = 0x8, + IMG_FLAG_STREAMING = 0x10, + IMG_FLAG_LEGACY_NORMALS = 0x20, + IMG_FLAG_CLAMP_U = 0x40, + IMG_FLAG_CLAMP_V = 0x80, + }; + enum class file_image_flags_t : std::uint32_t + { + IMG_FLAG_NOPICMIP = 0x1, + IMG_FLAG_NOMIPMAPS = 0x2, + IMG_FLAG_STREAMING = 0x4, + IMG_FLAG_LEGACY_NORMALS = 0x8, + IMG_FLAG_CLAMP_U = 0x10, + IMG_FLAG_CLAMP_V = 0x20, + IMG_FLAG_ALPHA_WEIGHTED_COLORS = 0x40, + IMG_FLAG_DXTC_APPROX_WEIGHTS = 0x80, + IMG_FLAG_GAMMA_NONE = 0x0, + IMG_FLAG_GAMMA_SRGB = 0x100, + IMG_FLAG_GAMMA_PWL = 0x200, + IMG_FLAG_GAMMA_2 = 0x300, + IMG_FLAG_MAPTYPE_2D = 0x0, + IMG_FLAG_MAPTYPE_CUBE = 0x10000, + IMG_FLAG_MAPTYPE_3D = 0x20000, + IMG_FLAG_MAPTYPE_1D = 0x30000, + IMG_FLAG_NORMALMAP = 0x40000, + IMG_FLAG_INTENSITY_TO_ALPHA = 0x80000, + IMG_FLAG_DYNAMIC = 0x1000000, + IMG_FLAG_RENDER_TARGET = 0x2000000, + IMG_FLAG_SYSTEMMEM = 0x4000000, + }; + + void translate_flags(IW3_GfxImageFileHeader* iw3_header, GfxImageFileHeader* iw5_header, iw3_file_image_flags_t iw3_flag, file_image_flags_t iw5_flag) + { + if ((iw3_header->flags & static_cast(iw3_flag)) == static_cast(iw3_flag)) + { + iw5_header->flags |= static_cast(iw5_flag); + } + } + + void IGfxImage::dump_iwi(const std::string& name) + { + if (FileSystem::FileExists(name + ".iwi") && !std::filesystem::exists( + "main\\iw5_images\\" + name + ".iwi")) + { + auto fp = fopen( + va("main\\%s\\images\\%s.iwi", FileSystem::GetFastFile().data(), name.data()).data(), "wb"); + + if (fp) + { + auto origfp = FileSystem::FileOpen(name + ".iwi", "rb"); + + if (origfp) + { + auto origsize = FileSystem::FileSize(origfp); + auto bytes = FileSystem::ReadBytes(origfp, origsize); + + if (bytes.size() > 3) + { + const auto version = bytes[3]; + + if (version != 8 && version != 9) + { + if (version == 6) + { + constexpr auto iw5_header_size = sizeof(GfxImageFileHeader); + constexpr auto iw3_header_size = sizeof(IW3_GfxImageFileHeader); + + ZONETOOL_INFO("Converting IWI %s from version %u to version %u...", name.data(), version, 8); + + // parse iw3 header + IW3_GfxImageFileHeader iw3_header; + memcpy(&iw3_header, bytes.data(), iw3_header_size); + + // generate iw5 header + GfxImageFileHeader header = {}; + memset(&header, 0, sizeof GfxImageFileHeader); + + memcpy(header.tag, iw3_header.tag, 3); + header.version = 8; + header.format = iw3_header.format; + header.unused = false; + memcpy(header.dimensions, iw3_header.dimensions, sizeof(__int16[3])); + memcpy(header.fileSizeForPicmip, iw3_header.fileSizeForPicmip, sizeof(int[4])); + + // transform iwi flags + translate_flags(&iw3_header, &header, iw3_file_image_flags_t::IMG_FLAG_NOPICMIP, file_image_flags_t::IMG_FLAG_NOPICMIP); + translate_flags(&iw3_header, &header, iw3_file_image_flags_t::IMG_FLAG_NOMIPMAPS, file_image_flags_t::IMG_FLAG_NOMIPMAPS); + translate_flags(&iw3_header, &header, iw3_file_image_flags_t::IMG_FLAG_CUBEMAP, file_image_flags_t::IMG_FLAG_MAPTYPE_CUBE); + translate_flags(&iw3_header, &header, iw3_file_image_flags_t::IMG_FLAG_VOLMAP, file_image_flags_t::IMG_FLAG_MAPTYPE_3D); + translate_flags(&iw3_header, &header, iw3_file_image_flags_t::IMG_FLAG_STREAMING, file_image_flags_t::IMG_FLAG_STREAMING); + translate_flags(&iw3_header, &header, iw3_file_image_flags_t::IMG_FLAG_LEGACY_NORMALS, file_image_flags_t::IMG_FLAG_LEGACY_NORMALS); + translate_flags(&iw3_header, &header, iw3_file_image_flags_t::IMG_FLAG_CLAMP_U, file_image_flags_t::IMG_FLAG_CLAMP_U); + translate_flags(&iw3_header, &header, iw3_file_image_flags_t::IMG_FLAG_CLAMP_V, file_image_flags_t::IMG_FLAG_CLAMP_V); + header.flags |= static_cast(file_image_flags_t::IMG_FLAG_ALPHA_WEIGHTED_COLORS); + header.flags |= static_cast(file_image_flags_t::IMG_FLAG_GAMMA_SRGB); + + for (auto i = 0u; i < 4; i++) + { + header.fileSizeForPicmip[i] += 4; + } + + // write iw5 header + fwrite(&header, iw5_header_size, 1, fp); + + // write iw3 image buffer + fwrite(&bytes[iw3_header_size], bytes.size() - iw3_header_size, 1, fp); + fclose(fp); + } + else + { + ZONETOOL_FATAL("IWI of version %u is not supported for conversion. IWI file was %s.", version, name.data()); + } + + return; + } + } + + // write original data + fwrite(&bytes[0], bytes.size(), 1, fp); + fclose(origfp); + } + + fclose(fp); + } + } + } + + void IGfxImage::write(IZone* zone, ZoneBuffer* buf) + { + dump_iwi(this->name()); + + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + // set loaded to false + dest->loaded = false; + + buf->push_stream(0); + if (data->texture) + { + buf->align(3); + + auto desttext = buf->at(); + buf->write_stream(data->texture, sizeof GfxImageLoadDef - sizeof std::uintptr_t); + + if (isMapImage && desttext->dataSize) + { + buf->write_stream(&data->texture->texture, data->texture->dataSize); + } + else + { + desttext->dataSize = 0; + } + + ZoneBuffer::clear_pointer(&dest->texture); + } + buf->pop_stream(); + + END_LOG_STREAM; + buf->pop_stream(); + } + + // Legacy cancer code + void fwritestr(FILE* file, const char* str) + { + if (!str) + return; + while (*str) + { + fwrite(str, 1, 1, file); + str++; + } + fwrite(str, 1, 1, file); + } + + void fwriteint(FILE* file, int value) + { + int _val = value; + fwrite(&_val, 4, 1, file); + } + + void fwriteuint(FILE* file, unsigned int value) + { + unsigned int _val = value; + fwrite(&_val, 4, 1, file); + } + + void fwritechar(FILE* file, char c) + { + char _val = c; + fwrite(&_val, 1, 1, file); + } + + char cleanAssetName[50]; + + char* ClearAssetName(char* name, int maxSize = 50) + { + int size = strnlen(name, maxSize); + char* newName = cleanAssetName; + memset(newName, 0, size + 1); + strncpy(newName, name, maxSize); + for (int i = 0; i < size; i++) + { + switch (newName[i]) + { + case '*': + newName[i] = '_'; + break; + } + } + return newName; + } + + void IGfxImage::dump(GfxImage* asset) + { + if (asset->texture && asset->texture->dataSize) + { + char* newName = ClearAssetName((char*)asset->name); + auto fp = FileSystem::FileOpen("images/"s + newName + ".ffImg"s, "wb"); + + if (!fp) return; + +#define fwstr(_str) fwritestr(fp, _str) +#define fwint(_int) fwriteint(fp, _int) +#define fwchr(_chr) fwritechar(fp, _chr) +#define frstr() freadstr(fp) +#define frint() freadint(fp) +#define frchr() freadchar(fp) + + // Header + fwchr(asset->mapType); + fwchr(asset->semantic); + fwchr(asset->category); + fwchr(asset->flags); + fwint((int)asset->cardMemory); + fwint(asset->dataLen1); + fwint(asset->dataLen2); + fwint(asset->height); + fwint(asset->width); + fwint(asset->depth); + fwstr(asset->name); + + // LoadDef + fwchr(asset->texture->mipLevels); + fwchr(asset->texture->flags); + fwint(asset->texture->dimensions[0]); + fwint(asset->texture->dimensions[1]); + fwint(asset->texture->dimensions[2]); + fwint(asset->texture->format); + fwint(asset->texture->dataSize); + + fwrite(&asset->texture->texture, 1, asset->texture->dataSize, fp); + + FileSystem::FileClose(fp); + } + } + } +} diff --git a/src/IW5/Assets/GfxImage.hpp b/src/IW5/Assets/GfxImage.hpp new file mode 100644 index 0000000..3e35353 --- /dev/null +++ b/src/IW5/Assets/GfxImage.hpp @@ -0,0 +1,45 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IGfxImage : public IAsset + { + private: + std::string name_; + GfxImage* asset_ = nullptr; + bool isMapImage; + + std::string clean_name(const std::string& name); + GfxImage* parse(const std::string& name, ZoneMemory* mem); + + public: + IGfxImage(); + ~IGfxImage(); + + static void dump_iwi(const std::string& name); + + void init(const std::string& name, ZoneMemory* mem) override; + void init(void* asset, ZoneMemory* mem) override; + + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + void* pointer() override { return asset_; } + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(GfxImage* asset); + }; + } +} diff --git a/src/IW5/Assets/GfxWorld.cpp b/src/IW5/Assets/GfxWorld.cpp new file mode 100644 index 0000000..db15cb1 --- /dev/null +++ b/src/IW5/Assets/GfxWorld.cpp @@ -0,0 +1,1293 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + /*legacy zonetool code, refactor me!*/ + GfxWorld* IGfxWorld::parse(const std::string& name, ZoneMemory* mem) + { + if (!FileSystem::FileExists(name + ".gfxmap")) + { + return nullptr; + } + + // alloc GfxWorld + auto asset = mem->Alloc(); + memset(asset, 0, sizeof(GfxWorld)); + + AssetReader read(mem); + if (!read.open(name + ".gfxmap")) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing GfxWorld \"%s\"...", name.data()); + +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::IGfxWorld::parse"); +#endif + + asset->name = read.read_string(); + asset->baseName = read.read_string(); + + asset->planeCount = read.read_int(); + asset->nodeCount = read.read_int(); + asset->indexCount = read.read_int(); + asset->skyCount = read.read_int(); + + asset->skies = read.read_array(); + for (unsigned int i = 0; i < asset->skyCount; i++) + { + asset->skies[i].skyStartSurfs = read.read_array(); + asset->skies[i].skyImage = read.read_asset(); + } + + asset->sunPrimaryLightIndex = read.read_int(); + asset->primaryLightCount = read.read_int(); + asset->primaryLightEnvCount = read.read_int(); + + char* unknown1 = read.read_array(); + memcpy(asset->unknown1, unknown1, 12); + + // dpvsplanes + asset->dpvsPlanes.cellCount = read.read_int(); + asset->dpvsPlanes.planes = read.read_array(); + asset->dpvsPlanes.nodes = read.read_array(); + asset->dpvsPlanes.sceneEntCellBits = read.read_array(); + + // dpvs + GfxWorldDpvsStatic* dpvs = read.read_array(); + memcpy(&asset->dpvs, dpvs, sizeof(GfxWorldDpvsStatic)); + + asset->dpvs.smodelVisData[0] = read.read_array(); + asset->dpvs.smodelVisData[1] = read.read_array(); + asset->dpvs.smodelVisData[2] = read.read_array(); + asset->dpvs.surfaceVisData[0] = read.read_array(); + asset->dpvs.surfaceVisData[1] = read.read_array(); + asset->dpvs.surfaceVisData[2] = read.read_array(); + asset->dpvs.sortedSurfIndex = read.read_array(); + asset->dpvs.smodelInsts = read.read_array(); + asset->dpvs.surfaces = read.read_array(); + for (int i = 0; i < asset->indexCount; i++) + { + asset->dpvs.surfaces[i].material = read.read_asset(); + } + asset->dpvs.cullGroups = read.read_array(); + asset->dpvs.smodelDrawInsts = read.read_array(); + for (unsigned int i = 0; i < asset->dpvs.smodelCount; i++) + { + asset->dpvs.smodelDrawInsts[i].model = read.read_asset(); + } + asset->dpvs.surfaceMaterials = read.read_array(); + asset->dpvs.surfaceCastsSunShadow = read.read_array(); + + // dpvsDyn + GfxWorldDpvsDynamic* dpvsDyn = read.read_array(); + memcpy(&asset->dpvsDyn, dpvsDyn, sizeof(GfxWorldDpvsDynamic)); + + asset->dpvsDyn.dynEntCellBits[0] = read.read_array(); + asset->dpvsDyn.dynEntCellBits[1] = read.read_array(); + asset->dpvsDyn.dynEntVisData[0][0] = read.read_array(); + asset->dpvsDyn.dynEntVisData[1][0] = read.read_array(); + asset->dpvsDyn.dynEntVisData[0][1] = read.read_array(); + asset->dpvsDyn.dynEntVisData[1][1] = read.read_array(); + asset->dpvsDyn.dynEntVisData[0][2] = read.read_array(); + asset->dpvsDyn.dynEntVisData[1][2] = read.read_array(); + + // aabbTreeCount + asset->aabbTreeCounts = read.read_array(); + + // GfxCellTree + asset->aabbTree = read.read_array(); + for (int i = 0; i < asset->dpvsPlanes.cellCount; i++) + { + asset->aabbTree[i].aabbtree = read.read_array(); + + for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; j++) + { + asset->aabbTree[i].aabbtree[j].smodelIndexes = read.read_array(); + } + } + + // read GFX cells + asset->cells = read.read_array(); + + for (int i = 0; i < asset->dpvsPlanes.cellCount; i++) + { + asset->cells[i].portals = read.read_array(); + for (int j = 0; j < asset->cells[i].portalCount; j++) + { + asset->cells[i].portals[j].vertices = read.read_array(); + } + asset->cells[i].reflectionProbes = read.read_array(); + asset->cells[i].reflectionProbeReferences = read.read_array(); + } + + auto worldDraw = read.read_array(); + memcpy(&asset->worldDraw, worldDraw, sizeof GfxWorldDraw); + + asset->worldDraw.reflectionImages = mem->Alloc(asset->worldDraw.reflectionProbeCount); + // new GfxImage*[asset->draw.reflectionProbeCount]; + memset(asset->worldDraw.reflectionImages, 0, sizeof(GfxImage*) * asset->worldDraw.reflectionProbeCount); + + for (unsigned int i = 0; i < asset->worldDraw.reflectionProbeCount; i++) + { + asset->worldDraw.reflectionImages[i] = read.read_asset(); + } + + asset->worldDraw.reflectionProbes = read.read_array(); + + asset->worldDraw.reflectionProbeReferences = read.read_array(); + asset->worldDraw.reflectionProbeReferenceOrigins = read.read_array(); + + asset->worldDraw.reflectionProbeTextures = read.read_array(); + asset->worldDraw.lightmaps = read.read_array(); + for (int i = 0; i < asset->worldDraw.lightmapCount; i++) + { + asset->worldDraw.lightmaps[i].primary = read.read_asset(); + asset->worldDraw.lightmaps[i].secondary = read.read_asset(); + } + asset->worldDraw.lightmapPrimaryTextures = read.read_array(); + asset->worldDraw.lightmapSecondaryTextures = read.read_array(); + asset->worldDraw.skyImage = read.read_asset(); + asset->worldDraw.outdoorImage = read.read_asset(); + asset->worldDraw.vd.vertices = read.read_array(); + asset->worldDraw.vld.data = read.read_array(); + asset->worldDraw.indices = read.read_array(); + + + // GfxLightGrid + GfxLightGrid* lightGrid = read.read_array(); + memcpy(&asset->lightGrid, lightGrid, sizeof(GfxLightGrid)); + + asset->lightGrid.rowDataStart = read.read_array(); + asset->lightGrid.rawRowData = read.read_array(); + asset->lightGrid.entries = read.read_array(); + asset->lightGrid.colors = read.read_array(); + + // models + asset->modelCount = read.read_int(); + asset->models = read.read_array(); + + // mins/maxs + float* mins = read.read_array(); + memcpy(asset->mins, mins, sizeof(float) * 3); + + float* maxs = read.read_array(); + memcpy(asset->maxs, maxs, sizeof(float) * 3); + + asset->checksum = read.read_int(); + + // materialmemory + asset->materialMemoryCount = read.read_int(); + asset->materialMemory = read.read_array(); + for (int i = 0; i < asset->materialMemoryCount; i++) + { + asset->materialMemory[i].material = read.read_asset(); + } + + // sun data + sunflare_t* sun = read.read_array(); + memcpy(&asset->sun, sun, sizeof(sunflare_t)); + + asset->sun.spriteMaterial = read.read_asset(); + asset->sun.flareMaterial = read.read_asset(); + + // outdoor shit + auto lookupMatrix = read.read_array>(); + memcpy(&asset->outdoorLookupMatrix, lookupMatrix, sizeof(VecInternal<4>) * 4); + + asset->outdoorImage = read.read_asset(); + + // CellcasterBits + asset->cellCasterBits[0] = read.read_array(); + asset->cellCasterBits[1] = read.read_array(); + + // SceneDynModel + asset->sceneDynModel = read.read_array(); + + // SceneDynBrush + asset->sceneDynBrush = read.read_array(); + + // PrimaryLightEntityShadowVis + asset->primaryLightEntityShadowVis = read.read_array(); + + // PrimaryLightDynEntShadowVis + asset->primaryLightDynEntShadowVis[0] = read.read_array(); + asset->primaryLightDynEntShadowVis[1] = read.read_array(); + + // PrimaryLightForModelDynEnt + asset->primaryLightForModelDynEnt = read.read_array(); + + // GfxShadowGeometry + asset->shadowGeom = read.read_array(); + for (int i = 0; i < asset->primaryLightCount; i++) + { + asset->shadowGeom[i].sortedSurfIndex = read.read_array(); + asset->shadowGeom[i].smodelIndex = read.read_array(); + } + + // GfxLightRegion + asset->lightRegion = read.read_array(); + for (int i = 0; i < asset->primaryLightCount; i++) + { + asset->lightRegion[i].hulls = read.read_array(); + for (unsigned int j = 0; j < asset->lightRegion[i].hullCount; j++) + { + asset->lightRegion[i].hulls[j].axis = read.read_array(); + } + } + + asset->mapVtxChecksum = read.read_int(); + + // heroLights + asset->heroLightCount = 0; + asset->heroLights = nullptr; + + asset->fogTypesAllowed = read.read_int(); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + + return asset; + } + + IGfxWorld::IGfxWorld() + { + } + + IGfxWorld::~IGfxWorld() + { + } + + void IGfxWorld::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data(), 1).gfxworld; + } + } + + void IGfxWorld::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IGfxWorld::load_depending(IZone* zone) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::IGfxWorld::load_depending"); +#endif + + auto data = this->asset_; + + // Skies + if (data->skyCount) + { + for (unsigned int i = 0; i < data->skyCount; i++) + { + if (data->skies[i].skyImage) + { + zone->add_asset_of_type(image, data->skies[i].skyImage->name); + } + } + } + + // ReflectionImages + if (data->worldDraw.reflectionImages) + { + for (unsigned int i = 0; i < data->worldDraw.reflectionProbeCount; i++) + { + if (data->worldDraw.reflectionImages[i]) + { + zone->add_asset_of_type(image, data->worldDraw.reflectionImages[i]->name); + } + } + } + + // Lightmaps + if (data->worldDraw.lightmaps) + { + for (int i = 0; i < data->worldDraw.lightmapCount; i++) + { + if (data->worldDraw.lightmaps[i].primary) + { + zone->add_asset_of_type(image, data->worldDraw.lightmaps[i].primary->name); + } + + if (data->worldDraw.lightmaps[i].secondary) + { + zone->add_asset_of_type(image, data->worldDraw.lightmaps[i].secondary->name); + } + } + } + + // SkyImage (Unused?) + if (data->worldDraw.skyImage) + { + zone->add_asset_of_type(image, data->worldDraw.skyImage->name); + } + + // OutdoorImage (Unused?) + if (data->worldDraw.outdoorImage) + { + zone->add_asset_of_type(image, data->worldDraw.outdoorImage->name); + } + + // MaterialMemory + + if (data->materialMemory) + { + for (int i = 0; i < data->materialMemoryCount; i++) + { + if (data->materialMemory[i].material) + { + zone->add_asset_of_type(material, data->materialMemory[i].material->name); + } + } + } + + // Sunflare_t + if (data->sun.spriteMaterial) + { + zone->add_asset_of_type(material, data->sun.spriteMaterial->name); + } + + if (data->sun.flareMaterial) + { + zone->add_asset_of_type(material, data->sun.flareMaterial->name); + } + + // OutdoorImage + if (data->outdoorImage) + { + zone->add_asset_of_type(image, data->outdoorImage->name); + } + + // Dpvs.Surfaces + if (data->dpvs.surfaces) + { + for (int i = 0; i < data->indexCount; i++) + { + if (data->dpvs.surfaces[i].material) + { + zone->add_asset_of_type(material, data->dpvs.surfaces[i].material->name); + } + } + } + + if (data->dpvs.smodelDrawInsts) + { + for (unsigned int i = 0; i < data->dpvs.smodelCount; i++) + { + if (data->dpvs.smodelDrawInsts[i].model) + { + zone->add_asset_of_type(xmodel, data->dpvs.smodelDrawInsts[i].model->name); + } + } + } + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + std::string IGfxWorld::name() + { + return this->name_; + } + + std::int32_t IGfxWorld::type() + { + return gfx_map; + } + + void IGfxWorld::write(IZone* zone, ZoneBuffer* buf) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::IGfxWorld::write"); +#endif + + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + if (data->name) + { + dest->name = buf->write_str(this->name()); + } + if (data->baseName) + { + dest->baseName = buf->write_str(this->name()); + } + + if (data->skies) + { + buf->align(3); + auto skiesArray = buf->write(data->skies, data->skyCount); + + for (std::uint32_t i = 0; i < data->skyCount; i++) + { + if (data->skies[i].skyStartSurfs) + { + buf->align(3); + buf->write_p(data->skies[i].skyStartSurfs, data->skies[i].skySurfCount); + ZoneBuffer::clear_pointer(&skiesArray[i].skyStartSurfs); + } + + if (data->skies[i].skyImage) + { + skiesArray[i].skyImage = reinterpret_cast(zone->get_asset_pointer( + image, data->skies[i].skyImage->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->skies); + } + + if (dest->dpvsPlanes.planes) + { + dest->dpvsPlanes.planes = buf->write_s(3, data->dpvsPlanes.planes, data->planeCount); + } + + if (dest->dpvsPlanes.nodes) + { + buf->align(1); + buf->write_p(data->dpvsPlanes.nodes, data->nodeCount); + ZoneBuffer::clear_pointer(&dest->dpvsPlanes.nodes); + } + + buf->push_stream(2); + if (dest->dpvsPlanes.sceneEntCellBits) + { + buf->align(3); + buf->write(data->dpvsPlanes.sceneEntCellBits, data->dpvsPlanes.cellCount << 11); + ZoneBuffer::clear_pointer(&dest->dpvsPlanes.sceneEntCellBits); + } + buf->pop_stream(); + + if (data->aabbTreeCounts) + { + buf->align(3); + buf->write_p(data->aabbTreeCounts, data->dpvsPlanes.cellCount); + ZoneBuffer::clear_pointer(&dest->aabbTreeCounts); + } + + if (data->aabbTree) + { + buf->align(127); + auto cell_tree = buf->write_p(data->aabbTree, data->dpvsPlanes.cellCount); + + for (std::int32_t i = 0; i < data->dpvsPlanes.cellCount; i++) + { + if (data->aabbTree[i].aabbtree) + { + buf->align(3); + auto gfx_aabb_tree = buf->write_p(data->aabbTree[i].aabbtree, + data->aabbTreeCounts[i].aabbTreeCount); + + for (std::int32_t i2 = 0; i2 < data->aabbTreeCounts[i].aabbTreeCount; i2++) + { + if (data->aabbTree[i].aabbtree[i2].smodelIndexes) + { + gfx_aabb_tree[i2].smodelIndexes = buf->write_s( + 1, data->aabbTree[i].aabbtree[i2].smodelIndexes, + data->aabbTree[i].aabbtree[i2].smodelIndexCount); + } + } + + ZoneBuffer::clear_pointer(&cell_tree[i].aabbtree); + } + } + + ZoneBuffer::clear_pointer(&dest->aabbTree); + } + + if (data->cells) + { + buf->align(3); + auto gfx_cell = buf->write(data->cells, data->dpvsPlanes.cellCount); + + for (std::int32_t i = 0; i < data->dpvsPlanes.cellCount; i++) + { + if (data->cells[i].portals) + { + buf->align(3); + auto gfx_portal = buf->write(data->cells[i].portals, data->cells[i].portalCount); + + for (std::int32_t i2 = 0; i2 < data->cells[i].portalCount; i2++) + { + if (data->cells[i].portals[i2].vertices) + { + buf->align(3); + buf->write(data->cells[i].portals[i2].vertices, data->cells[i].portals[i2].vertexCount); + ZoneBuffer::clear_pointer(&gfx_portal[i2].vertices); + } + } + + ZoneBuffer::clear_pointer(&gfx_cell[i].portals); + } + + if (data->cells[i].reflectionProbes) + { + buf->align(0); + buf->write(data->cells[i].reflectionProbes, data->cells[i].reflectionProbeCount); + ZoneBuffer::clear_pointer(&gfx_cell[i].reflectionProbes); + } + if (data->cells[i].reflectionProbeReferences) + { + buf->align(0); + buf->write(data->cells[i].reflectionProbeReferences, + data->cells[i].reflectionProbeReferenceCount); + ZoneBuffer::clear_pointer(&gfx_cell[i].reflectionProbeReferences); + } + + ZoneBuffer::clear_pointer(&dest->cells); + } + } + + if (data->worldDraw.reflectionImages) + { + buf->align(3); + auto reflectionProbes = buf->write(data->worldDraw.reflectionImages, + data->worldDraw.reflectionProbeCount); + + for (std::uint64_t i = 0; i < data->worldDraw.reflectionProbeCount; i++) + { + if (reflectionProbes[i]) + { + reflectionProbes[i] = reinterpret_cast(zone->get_asset_pointer( + image, data->worldDraw.reflectionImages[i]->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->worldDraw.reflectionImages); + } + + if (data->worldDraw.reflectionProbes) + { + buf->align(3); + buf->write(data->worldDraw.reflectionProbes, data->worldDraw.reflectionProbeCount); + ZoneBuffer::clear_pointer(&dest->worldDraw.reflectionProbes); + } + + buf->push_stream(2); + if (data->worldDraw.reflectionProbeTextures) + { + buf->align(3); + buf->write(data->worldDraw.reflectionProbeTextures, data->worldDraw.reflectionProbeCount); + ZoneBuffer::clear_pointer(&dest->worldDraw.reflectionProbeTextures); + } + buf->pop_stream(); + + if (data->worldDraw.reflectionProbeReferenceOrigins) + { + buf->align(3); + buf->write(data->worldDraw.reflectionProbeReferenceOrigins, + data->worldDraw.reflectionProbeReferenceCount); + ZoneBuffer::clear_pointer(&dest->worldDraw.reflectionProbeReferenceOrigins); + } + if (data->worldDraw.reflectionProbeReferences) + { + buf->align(0); + buf->write(data->worldDraw.reflectionProbeReferences, data->worldDraw.reflectionProbeReferenceCount); + ZoneBuffer::clear_pointer(&dest->worldDraw.reflectionProbeReferences); + } + + if (data->worldDraw.lightmaps) + { + buf->align(3); + auto gfx_lightmap_array = buf->write(data->worldDraw.lightmaps, data->worldDraw.lightmapCount); + + for (std::int32_t i = 0; i < data->worldDraw.lightmapCount; i++) + { + if (data->worldDraw.lightmaps[i].primary) + { + gfx_lightmap_array[i].primary = reinterpret_cast(zone->get_asset_pointer( + image, data->worldDraw.lightmaps[i].primary->name)); + } + + if (data->worldDraw.lightmaps[i].secondary) + { + gfx_lightmap_array[i].secondary = reinterpret_cast(zone->get_asset_pointer( + image, data->worldDraw.lightmaps[i].secondary->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->worldDraw.lightmaps); + } + + buf->push_stream(2); + if (data->worldDraw.lightmapPrimaryTextures) + { + buf->align(3); + buf->write_p(data->worldDraw.lightmapPrimaryTextures, data->worldDraw.lightmapCount); + ZoneBuffer::clear_pointer(&dest->worldDraw.lightmapPrimaryTextures); + } + + if (data->worldDraw.lightmapSecondaryTextures) + { + buf->align(3); + buf->write_p(data->worldDraw.lightmapSecondaryTextures, data->worldDraw.lightmapCount); + ZoneBuffer::clear_pointer(&dest->worldDraw.lightmapSecondaryTextures); + } + buf->pop_stream(); + + if (data->worldDraw.skyImage) + { + dest->worldDraw.skyImage = reinterpret_cast(zone->get_asset_pointer( + image, data->worldDraw.skyImage->name)); + } + + if (data->worldDraw.outdoorImage) + { + dest->worldDraw.outdoorImage = reinterpret_cast(zone->get_asset_pointer( + image, data->worldDraw.outdoorImage->name)); + } + + if (data->worldDraw.vd.vertices) + { + buf->align(3); + buf->write_p(data->worldDraw.vd.vertices, data->worldDraw.vertexCount); + ZoneBuffer::clear_pointer(&dest->worldDraw.vd.vertices); + } + + if (data->worldDraw.vld.data) + { + buf->align(0); + buf->write_p(data->worldDraw.vld.data, data->worldDraw.vertexLayerDataSize); + ZoneBuffer::clear_pointer(&dest->worldDraw.vld.data); + } + + if (data->worldDraw.indices) + { + buf->align(1); + buf->write_p(data->worldDraw.indices, data->worldDraw.indexCount); + ZoneBuffer::clear_pointer(&dest->worldDraw.indices); + } + + if (data->lightGrid.rowDataStart) + { + buf->align(1); + buf->write_p(data->lightGrid.rowDataStart, + data->lightGrid.maxs[data->lightGrid.rowAxis] - data->lightGrid.mins[data + ->lightGrid.rowAxis] + + 1); + ZoneBuffer::clear_pointer(&dest->lightGrid.rowDataStart); + } + + if (data->lightGrid.rawRowData) + { + buf->align(0); + buf->write_p(data->lightGrid.rawRowData, data->lightGrid.rawRowDataSize); + ZoneBuffer::clear_pointer(&dest->lightGrid.rawRowData); + } + + if (data->lightGrid.entries) + { + buf->align(3); + buf->write(data->lightGrid.entries, data->lightGrid.entryCount); + ZoneBuffer::clear_pointer(&dest->lightGrid.entries); + } + + if (data->lightGrid.colors) + { + buf->align(3); + buf->write(data->lightGrid.colors, data->lightGrid.colorCount); + ZoneBuffer::clear_pointer(&dest->lightGrid.colors); + } + + if (data->models) + { + buf->align(3); + buf->write(data->models, data->modelCount); + ZoneBuffer::clear_pointer(&dest->models); + } + + if (data->materialMemory) + { + buf->align(3); + auto memory = buf->write(data->materialMemory, data->materialMemoryCount); + + for (std::int32_t i = 0; i < data->materialMemoryCount; i++) + { + memory[i].material = reinterpret_cast(zone->get_asset_pointer( + material, data->materialMemory[i].material->name)); + } + + ZoneBuffer::clear_pointer(&dest->materialMemory); + } + + if (data->sun.spriteMaterial) + { + dest->sun.spriteMaterial = reinterpret_cast(zone->get_asset_pointer( + material, data->sun.spriteMaterial->name)); + } + if (data->sun.flareMaterial) + { + dest->sun.flareMaterial = reinterpret_cast(zone->get_asset_pointer( + material, data->sun.flareMaterial->name)); + } + + if (data->outdoorImage) + { + dest->outdoorImage = reinterpret_cast(zone->get_asset_pointer(image, data->outdoorImage->name) + ); + } + + buf->push_stream(2); + if (data->cellCasterBits[0]) + { + buf->align(3); + buf->write(data->cellCasterBits[0], + data->dpvsPlanes.cellCount * ((data->dpvsPlanes.cellCount + 31) >> 5)); + ZoneBuffer::clear_pointer(&dest->cellCasterBits[0]); + } + + if (data->cellCasterBits[1]) + { + buf->align(3); + buf->write(data->cellCasterBits[1], (data->dpvsPlanes.cellCount + 31) >> 5); + ZoneBuffer::clear_pointer(&dest->cellCasterBits[1]); + } + + if (data->sceneDynModel) + { + buf->align(3); + buf->write(data->sceneDynModel, data->dpvsDyn.dynEntClientCount[0]); + ZoneBuffer::clear_pointer(&dest->sceneDynModel); + } + + if (data->sceneDynBrush) + { + buf->align(3); + buf->write(data->sceneDynBrush, data->dpvsDyn.dynEntClientCount[1]); + ZoneBuffer::clear_pointer(&dest->sceneDynBrush); + } + + if (data->primaryLightEntityShadowVis) + { + buf->align(3); + buf->write(data->primaryLightEntityShadowVis, + (data->primaryLightCount - data->sunPrimaryLightIndex - 1) << 15); + ZoneBuffer::clear_pointer(&dest->primaryLightEntityShadowVis); + } + + if (data->primaryLightDynEntShadowVis[0]) + { + buf->align(3); + buf->write(data->primaryLightDynEntShadowVis[0], + data->dpvsDyn.dynEntClientCount[0] * (data->primaryLightCount - data->sunPrimaryLightIndex - + 1)); + ZoneBuffer::clear_pointer(&dest->primaryLightDynEntShadowVis[0]); + } + + if (data->primaryLightDynEntShadowVis[1]) + { + buf->align(3); + buf->write(data->primaryLightDynEntShadowVis[1], + data->dpvsDyn.dynEntClientCount[1] * (data->primaryLightCount - data->sunPrimaryLightIndex - + 1)); + ZoneBuffer::clear_pointer(&dest->primaryLightDynEntShadowVis[1]); + } + + if (data->primaryLightForModelDynEnt) + { + buf->align(0); + buf->write(data->primaryLightForModelDynEnt, data->dpvsDyn.dynEntClientCount[0]); + ZoneBuffer::clear_pointer(&dest->primaryLightForModelDynEnt); + } + buf->pop_stream(); + + if (data->shadowGeom) + { + buf->align(3); + auto shadow_geometry = buf->write(data->shadowGeom, data->primaryLightCount); + + for (std::int32_t i = 0; i < data->primaryLightCount; i++) + { + if (data->shadowGeom[i].sortedSurfIndex) + { + buf->align(1); + buf->write_p(data->shadowGeom[i].sortedSurfIndex, data->shadowGeom[i].surfaceCount); + ZoneBuffer::clear_pointer(&shadow_geometry[i].sortedSurfIndex); + } + if (data->shadowGeom[i].smodelIndex) + { + buf->align(1); + buf->write_p(data->shadowGeom[i].smodelIndex, data->shadowGeom[i].smodelCount); + ZoneBuffer::clear_pointer(&shadow_geometry[i].smodelIndex); + } + } + + ZoneBuffer::clear_pointer(&dest->shadowGeom); + } + + if (data->lightRegion) + { + buf->align(3); + auto light_region = buf->write(data->lightRegion, data->primaryLightCount); + + for (std::int32_t i = 0; i < data->primaryLightCount; i++) + { + if (data->lightRegion[i].hulls) + { + buf->align(3); + auto light_region_hull = buf->write(data->lightRegion[i].hulls, data->lightRegion[i].hullCount); + + for (std::uint32_t i2 = 0; i2 < data->lightRegion[i].hullCount; i2++) + { + if (data->lightRegion[i].hulls[i2].axis) + { + buf->align(3); + buf->write(data->lightRegion[i].hulls[i2].axis, + data->lightRegion[i].hulls[i2].axisCount); + ZoneBuffer::clear_pointer(&light_region_hull[i2].axis); + } + } + + ZoneBuffer::clear_pointer(&light_region[i].hulls); + } + } + + ZoneBuffer::clear_pointer(&dest->lightRegion); + } + + buf->push_stream(2); + if (data->dpvs.smodelVisData[0]) + { + buf->align(0); + buf->write(data->dpvs.smodelVisData[0], data->dpvs.smodelCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelVisData[0]); + } + + if (data->dpvs.smodelVisData[1]) + { + buf->align(0); + buf->write(data->dpvs.smodelVisData[1], data->dpvs.smodelCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelVisData[1]); + } + + if (data->dpvs.smodelVisData[2]) + { + buf->align(0); + buf->write(data->dpvs.smodelVisData[2], data->dpvs.smodelCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelVisData[2]); + } + + if (data->dpvs.surfaceVisData[0]) + { + buf->align(0); + buf->write(data->dpvs.surfaceVisData[0], data->dpvs.staticSurfaceCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfaceVisData[0]); + } + + if (data->dpvs.surfaceVisData[1]) + { + buf->align(0); + buf->write(data->dpvs.surfaceVisData[1], data->dpvs.staticSurfaceCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfaceVisData[1]); + } + + if (data->dpvs.surfaceVisData[2]) + { + buf->align(0); + buf->write(data->dpvs.surfaceVisData[2], data->dpvs.staticSurfaceCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfaceVisData[2]); + } + buf->pop_stream(); + + if (data->dpvs.sortedSurfIndex) + { + buf->align(1); + buf->write_p(data->dpvs.sortedSurfIndex, + data->dpvs.staticSurfaceCount + data->dpvs.staticSurfaceCountNoDecal); + ZoneBuffer::clear_pointer(&dest->dpvs.sortedSurfIndex); + } + + if (data->dpvs.smodelInsts) + { + buf->align(3); + buf->write(data->dpvs.smodelInsts, data->dpvs.smodelCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelInsts); + } + + if (data->dpvs.surfaces) + { + buf->align(3); + auto surface = buf->write(data->dpvs.surfaces, data->indexCount); + + for (std::int32_t i = 0; i < data->indexCount; i++) + { + if (data->dpvs.surfaces[i].material) + { + surface[i].material = reinterpret_cast(zone->get_asset_pointer( + material, data->dpvs.surfaces[i].material->name)); + } + + assert(surface[i].material != nullptr); + } + + ZoneBuffer::clear_pointer(&dest->dpvs.surfaces); + } + assert(data->indexCount > 0); + assert(data->dpvs.surfaces != nullptr); + + if (data->dpvs.cullGroups) + { + buf->align(3); + buf->write(data->dpvs.cullGroups, data->indexCount); + ZoneBuffer::clear_pointer(&dest->dpvs.cullGroups); + } + + if (data->dpvs.smodelDrawInsts) + { + buf->align(3); + auto static_model_draw_inst = buf->write(data->dpvs.smodelDrawInsts, data->dpvs.smodelCount); + + for (std::uint32_t i = 0; i < data->dpvs.smodelCount; i++) + { + if (data->dpvs.smodelDrawInsts[i].model) + { + static_model_draw_inst[i].model = reinterpret_cast(zone->get_asset_pointer( + xmodel, data->dpvs.smodelDrawInsts[i].model->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->dpvs.smodelDrawInsts); + } + + buf->push_stream(2); + if (data->dpvs.surfaceMaterials) + { + buf->align(7); + buf->write(data->dpvs.surfaceMaterials, data->indexCount); + ZoneBuffer::clear_pointer(&dest->dpvs.smodelDrawInsts); + } + + if (data->dpvs.surfaceCastsSunShadow) + { + buf->align(127); + buf->write(data->dpvs.surfaceCastsSunShadow, data->dpvs.surfaceVisDataCount); + ZoneBuffer::clear_pointer(&dest->dpvs.surfaceCastsSunShadow); + } + + if (data->dpvsDyn.dynEntCellBits[0]) + { + buf->align(3); + buf->write(data->dpvsDyn.dynEntCellBits[0], + data->dpvsDyn.dynEntClientWordCount[0] * data->dpvsPlanes.cellCount); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntCellBits[0]); + } + + if (data->dpvsDyn.dynEntCellBits[1]) + { + buf->align(3); + buf->write(data->dpvsDyn.dynEntCellBits[1], + data->dpvsDyn.dynEntClientWordCount[1] * data->dpvsPlanes.cellCount); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntCellBits[1]); + } + + if (data->dpvsDyn.dynEntVisData[0][0]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[0][0], 32 * data->dpvsDyn.dynEntClientWordCount[0]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[0][0]); + } + + if (data->dpvsDyn.dynEntVisData[1][0]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[1][0], 32 * data->dpvsDyn.dynEntClientWordCount[1]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[1][0]); + } + + if (data->dpvsDyn.dynEntVisData[0][1]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[0][1], 32 * data->dpvsDyn.dynEntClientWordCount[0]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[0][1]); + } + + if (data->dpvsDyn.dynEntVisData[1][1]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[1][1], 32 * data->dpvsDyn.dynEntClientWordCount[1]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[1][1]); + } + + if (data->dpvsDyn.dynEntVisData[0][2]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[0][2], 32 * data->dpvsDyn.dynEntClientWordCount[0]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[0][2]); + } + + if (data->dpvsDyn.dynEntVisData[1][2]) + { + buf->align(15); + buf->write(data->dpvsDyn.dynEntVisData[1][2], 32 * data->dpvsDyn.dynEntClientWordCount[1]); + ZoneBuffer::clear_pointer(&dest->dpvsDyn.dynEntVisData[1][2]); + } + buf->pop_stream(); + + if (data->heroLights) + { + buf->align(3); + buf->write(data->heroLights, data->heroLightCount); + ZoneBuffer::clear_pointer(&dest->heroLights); + } + + END_LOG_STREAM; + buf->pop_stream(); + + encrypt_data(dest, sizeof GfxWorld); +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + void IGfxWorld::dump(GfxWorld* asset) + { + // dump code + AssetDumper write; + if (!write.open(asset->name + ".gfxmap"s)) + { + return; + } + + write.dump_string(asset->name); + write.dump_string(asset->baseName); + + write.dump_int(asset->planeCount); + write.dump_int(asset->nodeCount); + write.dump_int(asset->indexCount); + write.dump_int(asset->skyCount); + + write.dump_array(asset->skies, asset->skyCount); + for (unsigned int i = 0; i < asset->skyCount; i++) + { + write.dump_array(asset->skies[i].skyStartSurfs, asset->skies[i].skySurfCount); + write.dump_asset(asset->skies[i].skyImage); + } + + write.dump_int(asset->sunPrimaryLightIndex); + write.dump_int(asset->primaryLightCount); + write.dump_int(asset->primaryLightEnvCount); + + write.dump_array(asset->unknown1, 12); + + // dpvsplanes + write.dump_int(asset->dpvsPlanes.cellCount); + write.dump_array(asset->dpvsPlanes.planes, asset->planeCount); + write.dump_array(asset->dpvsPlanes.nodes, asset->nodeCount); + write.dump_array(asset->dpvsPlanes.sceneEntCellBits, asset->dpvsPlanes.cellCount << 11); + + // dpvs + write.dump_array(&asset->dpvs, 1); + write.dump_array(asset->dpvs.smodelVisData[0], asset->dpvs.smodelCount); + write.dump_array(asset->dpvs.smodelVisData[1], asset->dpvs.smodelCount); + write.dump_array(asset->dpvs.smodelVisData[2], asset->dpvs.smodelCount); + write.dump_array(asset->dpvs.surfaceVisData[0], asset->dpvs.staticSurfaceCount); + write.dump_array(asset->dpvs.surfaceVisData[1], asset->dpvs.staticSurfaceCount); + write.dump_array(asset->dpvs.surfaceVisData[2], asset->dpvs.staticSurfaceCount); + write.dump_array(asset->dpvs.sortedSurfIndex, + (asset->dpvs.staticSurfaceCount + asset->dpvs.staticSurfaceCountNoDecal)); + write.dump_array(asset->dpvs.smodelInsts, asset->dpvs.smodelCount); + write.dump_array(asset->dpvs.surfaces, asset->indexCount); + + for (int i = 0; i < asset->indexCount; i++) + { + write.dump_asset(asset->dpvs.surfaces[i].material); + } + + write.dump_array(asset->dpvs.cullGroups, asset->indexCount); + write.dump_array(asset->dpvs.smodelDrawInsts, asset->dpvs.smodelCount); + + for (unsigned int i = 0; i < asset->dpvs.smodelCount; i++) + { + write.dump_asset(asset->dpvs.smodelDrawInsts[i].model); + } + write.dump_array(asset->dpvs.surfaceMaterials, asset->indexCount); + write.dump_array(asset->dpvs.surfaceCastsSunShadow, asset->dpvs.surfaceVisDataCount); + + // dpvsDyn + write.dump_array(&asset->dpvsDyn, 1); + write.dump_array(asset->dpvsDyn.dynEntCellBits[0], + asset->dpvsDyn.dynEntClientWordCount[0] * asset->dpvsPlanes.cellCount); + write.dump_array(asset->dpvsDyn.dynEntCellBits[1], + asset->dpvsDyn.dynEntClientWordCount[1] * asset->dpvsPlanes.cellCount); + write.dump_array(asset->dpvsDyn.dynEntVisData[0][0], asset->dpvsDyn.dynEntClientWordCount[0] * 32); + write.dump_array(asset->dpvsDyn.dynEntVisData[1][0], asset->dpvsDyn.dynEntClientWordCount[1] * 32); + write.dump_array(asset->dpvsDyn.dynEntVisData[0][1], asset->dpvsDyn.dynEntClientWordCount[0] * 32); + write.dump_array(asset->dpvsDyn.dynEntVisData[1][1], asset->dpvsDyn.dynEntClientWordCount[1] * 32); + write.dump_array(asset->dpvsDyn.dynEntVisData[0][2], asset->dpvsDyn.dynEntClientWordCount[0] * 32); + write.dump_array(asset->dpvsDyn.dynEntVisData[1][2], asset->dpvsDyn.dynEntClientWordCount[1] * 32); + + // aabbTreeCount + write.dump_array(asset->aabbTreeCounts, asset->dpvsPlanes.cellCount); + + // GfxCellTree + write.dump_array(asset->aabbTree, asset->dpvsPlanes.cellCount); + for (int i = 0; i < asset->dpvsPlanes.cellCount; i++) + { + write.dump_array(asset->aabbTree[i].aabbtree, asset->aabbTreeCounts[i].aabbTreeCount); + + for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; j++) + { + write.dump_array(asset->aabbTree[i].aabbtree[j].smodelIndexes, + asset->aabbTree[i].aabbtree[j].smodelIndexCount); + } + } + + // read GFX cells + write.dump_array(asset->cells, asset->dpvsPlanes.cellCount); + + for (int i = 0; i < asset->dpvsPlanes.cellCount; i++) + { + write.dump_array(asset->cells[i].portals, asset->cells[i].portalCount); + for (int j = 0; j < asset->cells[i].portalCount; j++) + { + write.dump_array(asset->cells[i].portals[j].vertices, asset->cells[i].portals[j].vertexCount); + } + write.dump_array(asset->cells[i].reflectionProbes, asset->cells[i].reflectionProbeCount); + write.dump_array(asset->cells[i].reflectionProbeReferences, asset->cells[i].reflectionProbeReferenceCount); + } + + // draw + write.dump_array(&asset->worldDraw, 1); + + for (unsigned int i = 0; i < asset->worldDraw.reflectionProbeCount; i++) + { + write.dump_asset(asset->worldDraw.reflectionImages[i]); + } + + write.dump_array(asset->worldDraw.reflectionProbes, asset->worldDraw.reflectionProbeCount); + + write.dump_array(asset->worldDraw.reflectionProbeReferences, asset->worldDraw.reflectionProbeReferenceCount); + write.dump_array(asset->worldDraw.reflectionProbeReferenceOrigins, + asset->worldDraw.reflectionProbeReferenceCount); + + write.dump_array(asset->worldDraw.reflectionProbeTextures, asset->worldDraw.reflectionProbeCount); + write.dump_array(asset->worldDraw.lightmaps, asset->worldDraw.lightmapCount); + + for (int i = 0; i < asset->worldDraw.lightmapCount; i++) + { + write.dump_asset(asset->worldDraw.lightmaps[i].primary); + write.dump_asset(asset->worldDraw.lightmaps[i].secondary); + } + + write.dump_array(asset->worldDraw.lightmapPrimaryTextures, asset->worldDraw.lightmapCount); + write.dump_array(asset->worldDraw.lightmapSecondaryTextures, asset->worldDraw.lightmapCount); + + write.dump_asset(asset->worldDraw.skyImage); + write.dump_asset(asset->worldDraw.outdoorImage); + + write.dump_array(asset->worldDraw.vd.vertices, asset->worldDraw.vertexCount); + write.dump_array(asset->worldDraw.vld.data, asset->worldDraw.vertexLayerDataSize); + write.dump_array(asset->worldDraw.indices, asset->worldDraw.indexCount); + + // GfxLightGrid + write.dump_array(&asset->lightGrid, 1); + write.dump_array(asset->lightGrid.rowDataStart, + (asset->lightGrid.maxs[asset->lightGrid.rowAxis] - asset->lightGrid.mins[asset + ->lightGrid.rowAxis] + + 1)); + write.dump_array(asset->lightGrid.rawRowData, asset->lightGrid.rawRowDataSize); + write.dump_array(asset->lightGrid.entries, asset->lightGrid.entryCount); + write.dump_array(asset->lightGrid.colors, asset->lightGrid.colorCount); + + // models + write.dump_int(asset->modelCount); + write.dump_array(asset->models, asset->modelCount); + + // mins/maxs + write.dump_array(asset->mins, 3); + write.dump_array(asset->maxs, 3); + + write.dump_int(asset->checksum); + + // materialmemory + write.dump_int(asset->materialMemoryCount); + write.dump_array(asset->materialMemory, asset->materialMemoryCount); + for (int i = 0; i < asset->materialMemoryCount; i++) + { + write.dump_asset(asset->materialMemory[i].material); + } + + // sun data + write.dump_array(&asset->sun, 1); + write.dump_asset(asset->sun.spriteMaterial); + write.dump_asset(asset->sun.flareMaterial); + + // outdoor shit + write.dump_array(asset->outdoorLookupMatrix, 4); + write.dump_asset(asset->outdoorImage); + + // CellcasterBits + write.dump_array(asset->cellCasterBits[0], + asset->dpvsPlanes.cellCount * ((asset->dpvsPlanes.cellCount + 31) >> 5)); + write.dump_array(asset->cellCasterBits[1], (asset->dpvsPlanes.cellCount + 31) >> 5); + + // SceneDynModel + write.dump_array(asset->sceneDynModel, asset->dpvsDyn.dynEntClientCount[0]); + + // SceneDynBrush + write.dump_array(asset->sceneDynBrush, asset->dpvsDyn.dynEntClientCount[1]); + + // PrimaryLightEntityShadowVis + write.dump_array(asset->primaryLightEntityShadowVis, + (asset->primaryLightCount - asset->sunPrimaryLightIndex - 1) << 15); + + // PrimaryLightDynEntShadowVis + write.dump_array(asset->primaryLightDynEntShadowVis[0], + asset->dpvsDyn.dynEntClientCount[0] * (asset->primaryLightCount - asset->sunPrimaryLightIndex - + 1)); + write.dump_array(asset->primaryLightDynEntShadowVis[1], + asset->dpvsDyn.dynEntClientCount[1] * (asset->primaryLightCount - asset->sunPrimaryLightIndex - + 1)); + + // PrimaryLightForModelDynEnt + write.dump_array(asset->primaryLightForModelDynEnt, asset->dpvsDyn.dynEntClientCount[0]); + + // GfxShadowGeometry + write.dump_array(asset->shadowGeom, asset->primaryLightCount); + for (int i = 0; i < asset->primaryLightCount; i++) + { + write.dump_array(asset->shadowGeom[i].sortedSurfIndex, asset->shadowGeom[i].surfaceCount); + write.dump_array(asset->shadowGeom[i].smodelIndex, asset->shadowGeom[i].smodelCount); + } + + // GfxLightRegion + write.dump_array(asset->lightRegion, asset->primaryLightCount); + for (int i = 0; i < asset->primaryLightCount; i++) + { + write.dump_array(asset->lightRegion[i].hulls, asset->lightRegion[i].hullCount); + for (unsigned int j = 0; j < asset->lightRegion[i].hullCount; j++) + { + write.dump_array(asset->lightRegion[i].hulls[j].axis, asset->lightRegion[i].hulls[j].axisCount); + } + } + + write.dump_int(asset->mapVtxChecksum); + write.dump_int(asset->fogTypesAllowed); + + // save file to disk! + write.close(); + } + } +} diff --git a/src/IW5/Assets/GfxWorld.hpp b/src/IW5/Assets/GfxWorld.hpp new file mode 100644 index 0000000..744aa46 --- /dev/null +++ b/src/IW5/Assets/GfxWorld.hpp @@ -0,0 +1,37 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IGfxWorld : public IAsset + { + private: + std::string name_; + GfxWorld* asset_ = nullptr; + + public: + static GfxWorld* parse(const std::string& name, ZoneMemory* mem); + IGfxWorld(); + ~IGfxWorld(); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(GfxWorld* asset); + }; + } +} diff --git a/src/IW5/Assets/GlassWorld.cpp b/src/IW5/Assets/GlassWorld.cpp new file mode 100644 index 0000000..d24af51 --- /dev/null +++ b/src/IW5/Assets/GlassWorld.cpp @@ -0,0 +1,200 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + GlassWorld* IGlassWorld::parse(const std::string& name, ZoneMemory* mem) + { + auto path = name + ".glassmap"; + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + // alloc FxWorld + GlassWorld* asset; + + ZONETOOL_INFO("Parsing GlassMap \"%s\" ...", name.data()); + + AssetReader read(mem); + if (!read.open(path)) + { + return nullptr; + } + + asset = read.read_array(); + asset->name = read.read_string(); + asset->g_glassData = read.read_array(); + + if (asset->g_glassData) + { + asset->g_glassData->glassPieces = read.read_array(); + asset->g_glassData->glassNames = read.read_array(); + + for (int i = 0; i < asset->g_glassData->glassNameCount; i++) + { + asset->g_glassData->glassNames[i].nameStr = read.read_string(); + asset->g_glassData->glassNames[i].pieceIndices = read.read_array(); + } + } + + read.close(); + + return asset; + } + + void IGlassWorld::init(void* asset, ZoneMemory* mem) + { + this->asset_ = reinterpret_cast(asset); + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + } + + void IGlassWorld::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data(), 1).glassworld; + } + } + + void IGlassWorld::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IGlassWorld::load_depending(IZone* zone) + { + } + + std::string IGlassWorld::name() + { + return this->name_; + } + + std::int32_t IGlassWorld::type() + { + return glass_map; + } + + void IGlassWorld::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + assert(sizeof GlassWorld, 8); + assert(sizeof G_GlassData, 128); + assert(sizeof G_GlassPiece, 12); + assert(sizeof G_GlassName, 12); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->g_glassData) + { + buf->align(3); + + auto glassdata = data->g_glassData; + auto destdata = buf->write(glassdata); + + if (glassdata->glassPieces) + { + buf->align(3); + buf->write(glassdata->glassPieces, glassdata->pieceCount); + ZoneBuffer::clear_pointer(&destdata->glassPieces); + } + if (glassdata->glassNames) + { + buf->align(3); + auto namedest = buf->write(glassdata->glassNames, glassdata->glassNameCount); + + for (unsigned int i = 0; i < glassdata->glassNameCount; i++) + { + namedest[i].nameStr = buf->write_str(glassdata->glassNames[i].nameStr); + + if (glassdata->glassNames[i].pieceCount) + { + buf->align(1); + buf->write(glassdata->glassNames[i].pieceIndices, glassdata->glassNames[i].pieceCount); + ZoneBuffer::clear_pointer(&glassdata->glassNames[i].pieceIndices); + } + + if (zone->get_target() != zone_target::pc) + { + endian_convert(&namedest[i].name); + endian_convert(&namedest[i].nameStr); + endian_convert(&namedest[i].pieceCount); + endian_convert(&namedest[i].pieceIndices); + } + } + + ZoneBuffer::clear_pointer(&destdata->glassNames); + } + + ZoneBuffer::clear_pointer(&dest->g_glassData); + + if (zone->get_target() != zone_target::pc) + { + endian_convert(&destdata->glassPieces); + endian_convert(&destdata->pieceCount); + endian_convert(&destdata->damageToWeaken); + endian_convert(&destdata->damageToDestroy); + endian_convert(&destdata->glassNameCount); + endian_convert(&destdata->glassNames); + } + } + + if (zone->get_target() != zone_target::pc) + { + endian_convert(&dest->name); + endian_convert(&dest->g_glassData); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IGlassWorld::dump(GlassWorld* asset) + { + auto path = asset->name + ".glassmap"s; + AssetDumper write; + + if (!write.open(path)) + { + return; + } + + write.dump_array(asset, 1); + write.dump_string(asset->name); + + write.dump_array(asset->g_glassData, 1); + + if (asset->g_glassData) + { + write.dump_array(asset->g_glassData->glassPieces, asset->g_glassData->pieceCount); + write.dump_array(asset->g_glassData->glassNames, asset->g_glassData->glassNameCount); + + for (int i = 0; i < asset->g_glassData->glassNameCount; i++) + { + write.dump_string(asset->g_glassData->glassNames[i].nameStr); + write.dump_array(asset->g_glassData->glassNames[i].pieceIndices, + asset->g_glassData->glassNames[i].pieceCount); + } + } + + write.close(); + } + } +} diff --git a/src/IW5/Assets/GlassWorld.hpp b/src/IW5/Assets/GlassWorld.hpp new file mode 100644 index 0000000..a3a8e4f --- /dev/null +++ b/src/IW5/Assets/GlassWorld.hpp @@ -0,0 +1,38 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IGlassWorld : public IAsset + { + private: + std::string name_; + GlassWorld* asset_ = nullptr; + + public: + static GlassWorld* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void init(void* asset, ZoneMemory* mem) override; + + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(GlassWorld* asset); + }; + } +} diff --git a/src/IW5/Assets/LeaderBoardDef.cpp b/src/IW5/Assets/LeaderBoardDef.cpp new file mode 100644 index 0000000..b98d8c7 --- /dev/null +++ b/src/IW5/Assets/LeaderBoardDef.cpp @@ -0,0 +1,82 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void ILeaderBoardDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).leaderboard; + } + + void ILeaderBoardDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ILeaderBoardDef::load_depending(IZone* zone) + { + } + + std::string ILeaderBoardDef::name() + { + return this->name_; + } + + std::int32_t ILeaderBoardDef::type() + { + return leaderboarddef; + } + + void ILeaderBoardDef::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->columns) + { + buf->align(3); + auto* dest_columns = buf->write(data->columns, data->columnCount); + + if (data->columnCount > 0) + { + for (auto i = 0; i < data->columnCount; i++) + { + dest_columns[i].title = buf->write_str(data->columns[i].title); + dest_columns[i].statName = buf->write_str(data->columns[i].statName); + } + } + + ZoneBuffer::clear_pointer(&dest->columns); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ILeaderBoardDef::dump(LeaderBoardDef* asset) + { + const auto data = asset->ToJson(); + + const auto path = "leaderboards\\"s + asset->name; + const auto json = data.dump(4); + + auto* file = FileSystem::FileOpen(path, "w"s); + fwrite(json.data(), json.size(), 1, file); + FileSystem::FileClose(file); + } + } +} diff --git a/src/IW5/Assets/LeaderBoardDef.hpp b/src/IW5/Assets/LeaderBoardDef.hpp new file mode 100644 index 0000000..d6f15ae --- /dev/null +++ b/src/IW5/Assets/LeaderBoardDef.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class ILeaderBoardDef : public IAsset + { + private: + std::string name_; + LeaderBoardDef* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(LeaderBoardDef* asset); + }; + } +} diff --git a/src/IW5/Assets/LightDef.cpp b/src/IW5/Assets/LightDef.cpp new file mode 100644 index 0000000..ff8da99 --- /dev/null +++ b/src/IW5/Assets/LightDef.cpp @@ -0,0 +1,139 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void ILightDef::parseLightImage(GfxLightImage* image, nlohmann::json& data, ZoneMemory* mem) + { + if (data["image"].is_string()) + { + const auto image_name = data["image"].get(); + + if (image_name.size()) + { + image->image = mem->Alloc(); + image->image->name = mem->StrDup(image_name); + } + } + + image->samplerState = data["samplerState"].get(); + } + + GfxLightDef* ILightDef::parse(const std::string& name, ZoneMemory* mem) + { + const auto path = "lights/"s + name + ".lightdef"s; + if (FileSystem::FileExists(path)) + { + ZONETOOL_INFO("Parsing lightdef \"%s\"...", name.c_str()); + + auto asset = mem->Alloc(); + + auto* file = FileSystem::FileOpen(path, "rb"); + const auto size = FileSystem::FileSize(file); + auto bytes = FileSystem::ReadBytes(file, size); + FileSystem::FileClose(file); + + auto data = nlohmann::json::parse(bytes); + + asset->name = mem->StrDup(data["name"].get()); + + // parse light images + parseLightImage(&asset->attenuation, data["attenuation"], mem); + parseLightImage(&asset->cucoloris, data["cucoloris"], mem); + + asset->lmapLookupStart = data["lmapLookupStart"].get(); + + return asset; + } + + return nullptr; + } + + void ILightDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).lightdef; + } + } + + void ILightDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ILightDef::load_depending(IZone* zone) + { + auto* asset = this->asset_; + + if (asset->attenuation.image) + { + zone->add_asset_of_type(image, asset->attenuation.image->name); + } + if (asset->cucoloris.image) + { + zone->add_asset_of_type(image, asset->cucoloris.image->name); + } + } + + std::string ILightDef::name() + { + return this->name_; + } + + std::int32_t ILightDef::type() + { + return lightdef; + } + + void ILightDef::write(IZone* zone, ZoneBuffer* buf) + { + assert(sizeof(GfxLightDef), 24); + + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->attenuation.image) + { + dest->attenuation.image = reinterpret_cast(zone->get_asset_pointer( + image, data->attenuation.image->name)); + } + if (data->cucoloris.image) + { + dest->cucoloris.image = reinterpret_cast(zone->get_asset_pointer( + image, data->cucoloris.image->name)); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ILightDef::dump(GfxLightDef* asset) + { + const auto data = asset->ToJson(); + + const auto path = "lights/"s + asset->name + ".lightdef"s; + const auto json = data.dump(4); + + auto* file = FileSystem::FileOpen(path, "w"s); + fwrite(json.data(), json.size(), 1, file); + FileSystem::FileClose(file); + } + } +} diff --git a/src/IW5/Assets/LightDef.hpp b/src/IW5/Assets/LightDef.hpp new file mode 100644 index 0000000..54b987c --- /dev/null +++ b/src/IW5/Assets/LightDef.hpp @@ -0,0 +1,36 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class ILightDef : public IAsset + { + private: + std::string name_; + GfxLightDef* asset_ = nullptr; + + public: + static void parseLightImage(GfxLightImage* image, nlohmann::json& data, ZoneMemory* mem); + static GfxLightDef* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(GfxLightDef* asset); + }; + } +} diff --git a/src/IW5/Assets/LoadedSound.cpp b/src/IW5/Assets/LoadedSound.cpp new file mode 100644 index 0000000..b4e6480 --- /dev/null +++ b/src/IW5/Assets/LoadedSound.cpp @@ -0,0 +1,250 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + LoadedSound* ILoadedSound::parse(const std::string& name, ZoneMemory* mem) + { + auto path = "loaded_sound/" + name; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing loaded_sound \"%s\"...", name.data()); + + auto fp = FileSystem::FileOpen(path, "rb"); + auto result = mem->Alloc(); + + unsigned int chunkIDBuffer; + unsigned int chunkSize; + + fread(&chunkIDBuffer, 4, 1, fp); + if (chunkIDBuffer != 0x46464952) // RIFF + { + ZONETOOL_FATAL("%s: Invalid RIFF Header.", name.c_str()); + /*fclose(fp_); + return nullptr;*/ + } + + fread(&chunkSize, 4, 1, fp); + fread(&chunkIDBuffer, 4, 1, fp); + + if (chunkIDBuffer != 0x45564157) // WAVE + { + ZONETOOL_FATAL("%s: Invalid WAVE Header.", name.c_str()); + /*fclose(fp_); + return nullptr;*/ + } + + char* data; + + while (!result->sound.data && !feof(fp)) + { + fread(&chunkIDBuffer, 4, 1, fp); + fread(&chunkSize, 4, 1, fp); + switch (chunkIDBuffer) + { + case 0x20746D66: // fmt + if (chunkSize >= 16) + { + short format; + fread(&format, 2, 1, fp); + if (format != 1 && format != 17) + { + ZONETOOL_FATAL("%s: Invalid wave format %i.", name.c_str(), format); + /*fclose(fp_); + return nullptr;*/ + } + result->sound.info.format = format; + + short numChannels; + fread(&numChannels, 2, 1, fp); + result->sound.info.channels = numChannels; + + int sampleRate; + fread(&sampleRate, 4, 1, fp); + result->sound.info.rate = sampleRate; + + int byteRate; + fread(&byteRate, 4, 1, fp); + + short blockAlign; + fread(&blockAlign, 2, 1, fp); + result->sound.info.block_size = blockAlign; + + short bitPerSample; + fread(&bitPerSample, 2, 1, fp); + result->sound.info.bits = bitPerSample; + + if (chunkSize > 16) + { + fseek(fp, chunkSize - 16, SEEK_CUR); + } + } + break; + + case 0x61746164: // data + result->sound.info.data_len = chunkSize; + data = (char*)malloc(result->sound.info.data_len); + fread(data, 1, result->sound.info.data_len, fp); + result->sound.data = data; + break; + + default: + if (chunkSize > 0) + { + fseek(fp, chunkSize, SEEK_CUR); + } + break; + } + } + + if (!result->sound.data) + { + ZONETOOL_FATAL("%s: Could not read sounddata.", name.c_str()); + //fclose(fp_); + //return nullptr; + } + + result->name = mem->StrDup(name); + + FileSystem::FileClose(fp); + return result; + } + + void ILoadedSound::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + ZONETOOL_WARNING("Sound %s not found, it will probably sound like a motorboat ingame!", name.data()); + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).loadedsound; + } + } + + void ILoadedSound::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ILoadedSound::load_depending(IZone* zone) + { + } + + std::string ILoadedSound::name() + { + return this->name_; + } + + std::int32_t ILoadedSound::type() + { + return loaded_sound; + } + + void ILoadedSound::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + buf->push_stream(0); + + if (data->sound.data) + { + buf->align(0); + buf->write(data->sound.data, data->sound.info.data_len); + ZoneBuffer::clear_pointer(&dest->sound.data); + } + + buf->pop_stream(); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ILoadedSound::dump(LoadedSound* asset) + { + auto sound = asset; + auto fp = FileSystem::FileOpen("loaded_sound/"s + asset->name, "wb"); + + if (fp) + { + char chunkID[] = {'R', 'I', 'F', 'F'}; + fwrite(chunkID, 4, 1, fp); + + // ChunkSize + int subchunk1Size = 16; + int subchunk2Size = sound->sound.info.data_len; + int chunkSize = 4 + (8 + subchunk1Size) + (8 + subchunk2Size); + fwrite(&chunkSize, 4, 1, fp); + + // Format + char format[] = {'W', 'A', 'V', 'E'}; + fwrite(format, 4, 1, fp); + + + // --- FMT SUBCHUNK + // Subchunk1ID + char subchunk1ID[] = {'f', 'm', 't', ' '}; + fwrite(subchunk1ID, 4, 1, fp); + + // Subchunk1Size + fwrite(&subchunk1Size, 4, 1, fp); + + // AudioFormat + short audioFormat = sound->sound.info.format; + fwrite(&audioFormat, 2, 1, fp); + + // NumChannels + short numChannels = sound->sound.info.channels; + fwrite(&numChannels, 2, 1, fp); + + // SampleRate + int sampleRate = sound->sound.info.rate; + fwrite(&sampleRate, 4, 1, fp); + + // ByteRate + int byteRate = sound->sound.info.rate * sound->sound.info.channels * sound->sound.info.bits / 8; + fwrite(&byteRate, 4, 1, fp); + + // BlockAlign + short blockAlign = sound->sound.info.block_size; + fwrite(&blockAlign, 2, 1, fp); + + // BitsPerSample + short bitsPerSample = sound->sound.info.bits; + fwrite(&bitsPerSample, 2, 1, fp); + + + // --- DATA SUBCHUNK + // Subchunk2ID + char subchunk2ID[] = {'d', 'a', 't', 'a'}; + fwrite(subchunk2ID, 4, 1, fp); + + // Subchunk2Size + fwrite(&subchunk2Size, 4, 1, fp); + + // Data + fwrite(sound->sound.data, sound->sound.info.data_len, 1, fp); + } + + FileSystem::FileClose(fp); + } + } +} diff --git a/src/IW5/Assets/LoadedSound.hpp b/src/IW5/Assets/LoadedSound.hpp new file mode 100644 index 0000000..0205bc4 --- /dev/null +++ b/src/IW5/Assets/LoadedSound.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class ILoadedSound : public IAsset + { + private: + std::string name_; + LoadedSound* asset_ = nullptr; + + public: + LoadedSound* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(LoadedSound* asset); + }; + } +} diff --git a/src/IW5/Assets/LocalizeEntry.cpp b/src/IW5/Assets/LocalizeEntry.cpp new file mode 100644 index 0000000..fcb32b8 --- /dev/null +++ b/src/IW5/Assets/LocalizeEntry.cpp @@ -0,0 +1,60 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void ILocalizeEntry::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).localize; + } + + void ILocalizeEntry::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ILocalizeEntry::load_depending(IZone* zone) + { + } + + std::string ILocalizeEntry::name() + { + return this->name_; + } + + std::int32_t ILocalizeEntry::type() + { + return localize; + } + + void ILocalizeEntry::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + sizeof LocalizeEntry; + + buf->push_stream(3); + START_LOG_STREAM; + + dest->localizedString = buf->write_str(data->localizedString); + dest->name = buf->write_str(this->name()); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ILocalizeEntry::dump(LocalizeEntry* asset) + { + } + } +} diff --git a/src/IW5/Assets/LocalizeEntry.hpp b/src/IW5/Assets/LocalizeEntry.hpp new file mode 100644 index 0000000..749f10b --- /dev/null +++ b/src/IW5/Assets/LocalizeEntry.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class ILocalizeEntry : public IAsset + { + private: + std::string name_; + LocalizeEntry* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(LocalizeEntry* asset); + }; + } +} diff --git a/src/IW5/Assets/MapEnts.cpp b/src/IW5/Assets/MapEnts.cpp new file mode 100644 index 0000000..66d7a71 --- /dev/null +++ b/src/IW5/Assets/MapEnts.cpp @@ -0,0 +1,285 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + /*legacy zonetool code, refactor me!*/ + MapEnts* IMapEnts::parse(std::string name, ZoneMemory* mem) + { + // check if we can open a filepointer + if (!FileSystem::FileExists(name + ".ents")) + { + return nullptr; + } + + auto* file = FileSystem::FileOpen(name + ".ents", "rb"); + + // let them know that we're parsing a custom mapents file + ZONETOOL_INFO("Parsing mapents \"%s\"...", name.c_str()); + + // alloc mapents + auto ents = mem->Alloc(); + + ents->name = mem->StrDup(name); + ents->numEntityChars = FileSystem::FileSize(file); + + ents->entityString = mem->Alloc(ents->numEntityChars + 1); + memset((char*)ents->entityString, 0, ents->numEntityChars); + + fread((char*)ents->entityString, ents->numEntityChars, 1, file); + ((char*)ents->entityString)[ents->numEntityChars] = '\0'; + + // close filepointer + FileSystem::FileClose(file); + + /*if (!FileSystem::FileExists(name + ".ents.triggers")) + { + return ents; + }*/ + + AssetReader trigger_reader(mem); + if (trigger_reader.open(name + ".ents.triggers")) + { + ents->trigger.modelCount = trigger_reader.read_int(); + ents->trigger.models = trigger_reader.read_array(); + + ents->trigger.hullCount = trigger_reader.read_int(); + ents->trigger.hulls = trigger_reader.read_array(); + + ents->trigger.slabCount = trigger_reader.read_int(); + ents->trigger.slabs = trigger_reader.read_array(); + + trigger_reader.close(); + } + + // return mapents + return ents; + } + + void IMapEnts::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = "maps/"s + (currentzone.substr(0, 3) == "mp_" ? "mp/" : "") + currentzone + ".d3dbsp"; // name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), name.data(), 1).mapents; + } + } + + void IMapEnts::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IMapEnts::load_depending(IZone* zone) + { + load_depending_internal(zone, this->asset_->entityString); + } + + void IMapEnts::load_depending_internal(IZone* zone, const char* entityString) + { + //auto expTreeOrg = Expression::ParseExpression(entityString, false); + //auto expTree = expTreeOrg.get(); + + //while (expTree) + //{ + // if (expTree->ToString() == "model" || expTree->ToString() == "1670") + // { + // // go to next expression + // expTree = expTree->Next(); + + // // model name + // auto modelName = expTree->ToString(); + + // if (modelName[0] != '*' && modelName[0] != '?') + // { + // ZONETOOL_INFO("Adding model \"%s\" because its referenced in the entities file.", modelName.data()); + // zone->AddAssetOfType(XAssetType::xmodel, modelName); + // } + // } + // else if (expTree->ToString() == "weaponinfo" || expTree->ToString() == "1803") + // { + // // go to next expression + // expTree = expTree->Next(); + + // // model name + // auto weaponName = expTree->ToString(); + + // ZONETOOL_INFO("Adding weapon \"%s\" because its referenced in the entities file.", weaponName.data()); + // zone->AddAssetOfType(XAssetType::weapon, weaponName); + // } + // else if (expTree->ToString() == "csv_include" || expTree->ToString() == "2817") + // { + // // go to next expression + // expTree = expTree->Next(); + + // // csv name + // auto csvName = expTree->ToString(); + + // ZONETOOL_INFO("Parsing sub-csv file \"%s\" because its referenced in the entities file.", csvName.data()); + + // // parse CSV... + // auto csv = std::make_unique("radiant\\" + csvName + ".csv"); + // for (auto i = 0; i < csv->Rows(); i++) + // { + // if (csv->Columns(i) >= 2) + // { + // auto assetType = csv->Get(i, 0); + // auto assetName = csv->Get(i, 1); + + // ZONETOOL_INFO("[%s]: Adding sub-asset \"%s\"...", csvName.data(), assetName.data()); + + // auto type = zone->GetTypeByName(assetType); + // zone->AddAssetOfType(type, assetName); + // } + // } + // } + + // expTree = expTree->Next(); + //} + } + + std::string IMapEnts::name() + { + return this->name_; + } + + std::int32_t IMapEnts::type() + { + return map_ents; + } + + void IMapEnts::write_triggers(IZone* zone, ZoneBuffer* buf, MapTriggers* dest) + { + if (dest->models) + { + /*buf->align(3); + buf->write(dest->models, dest->modelCount); + ZoneBuffer::clear_pointer(&dest->models);*/ + dest->models = buf->write_s(3, dest->models, dest->modelCount); + } + + if (dest->hulls) + { + /*buf->align(3); + buf->write(dest->hulls, dest->hullCount); + ZoneBuffer::clear_pointer(&dest->hulls);*/ + dest->hulls = buf->write_s(3, dest->hulls, dest->hullCount); + } + + if (dest->slabs) + { + /*buf->align(3); + buf->write(dest->slabs, dest->slabCount); + ZoneBuffer::clear_pointer(&dest->slabs);*/ + dest->slabs = buf->write_s(3, dest->slabs, dest->slabCount); + } + } + + void IMapEnts::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->entityString) + { + buf->align(0); + buf->write(data->entityString, data->numEntityChars); + ZoneBuffer::clear_pointer(&dest->entityString); + } + + write_triggers(zone, buf, &dest->trigger); + write_triggers(zone, buf, &dest->clientTrigger.trigger); + + if (data->clientTrigger.clientTriggerAabbTree) + { + buf->align(3); + buf->write(data->clientTrigger.clientTriggerAabbTree, data->clientTrigger.numClientTriggerNodes); + ZoneBuffer::clear_pointer(&dest->clientTrigger.clientTriggerAabbTree); + } + if (data->clientTrigger.triggerString) + { + buf->align(0); + buf->write(data->clientTrigger.triggerString, data->clientTrigger.triggerStringLength); + ZoneBuffer::clear_pointer(&dest->clientTrigger.triggerString); + } + if (data->clientTrigger.visionSetTriggers) + { + buf->align(1); + buf->write(data->clientTrigger.visionSetTriggers, data->clientTrigger.trigger.modelCount); + ZoneBuffer::clear_pointer(&dest->clientTrigger.visionSetTriggers); + } + if (data->clientTrigger.triggerType) + { + buf->align(0); + buf->write(data->clientTrigger.triggerType, data->clientTrigger.trigger.modelCount); + ZoneBuffer::clear_pointer(&dest->clientTrigger.triggerType); + } + if (data->clientTrigger.origins) + { + buf->align(3); + buf->write(data->clientTrigger.origins, 3 * data->clientTrigger.trigger.modelCount); + ZoneBuffer::clear_pointer(&dest->clientTrigger.origins); + } + if (data->clientTrigger.scriptDelay) + { + buf->align(3); + buf->write(data->clientTrigger.scriptDelay, data->clientTrigger.trigger.modelCount); + ZoneBuffer::clear_pointer(&dest->clientTrigger.scriptDelay); + } + if (data->clientTrigger.audioTriggers) + { + buf->align(1); + buf->write(data->clientTrigger.audioTriggers, data->clientTrigger.trigger.modelCount); + ZoneBuffer::clear_pointer(&dest->clientTrigger.audioTriggers); + } + + END_LOG_STREAM; + buf->pop_stream(); + + encrypt_data(dest, sizeof MapEnts); + } + + void IMapEnts::dump(MapEnts* asset) + { + if (asset) + { + auto* file = FileSystem::FileOpen(asset->name + ".ents"s, "wb"); + if (file) + { + fwrite(asset->entityString, asset->numEntityChars, 1, file); + FileSystem::FileClose(file); + } + + AssetDumper trigger_dumper; + if (trigger_dumper.open(asset->name + ".ents.triggers"s)) + { + trigger_dumper.dump_int(asset->trigger.modelCount); + trigger_dumper.dump_array(asset->trigger.models, asset->trigger.modelCount); + + trigger_dumper.dump_int(asset->trigger.hullCount); + trigger_dumper.dump_array(asset->trigger.hulls, asset->trigger.hullCount); + + trigger_dumper.dump_int(asset->trigger.slabCount); + trigger_dumper.dump_array(asset->trigger.slabs, asset->trigger.slabCount); + + trigger_dumper.close(); + } + } + } + } +} diff --git a/src/IW5/Assets/MapEnts.hpp b/src/IW5/Assets/MapEnts.hpp new file mode 100644 index 0000000..a18cbe0 --- /dev/null +++ b/src/IW5/Assets/MapEnts.hpp @@ -0,0 +1,39 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IMapEnts : public IAsset + { + private: + std::string name_; + MapEnts* asset_ = nullptr; + + public: + MapEnts* parse(std::string name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + static void load_depending_internal(IZone* zone, const char* entityString); + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(MapEnts* asset); + + // sadly, this cannot be moved to a CPP file. + static void write_triggers(IZone* zone, ZoneBuffer* buf, MapTriggers* dest); + }; + } +} diff --git a/src/IW5/Assets/Material.cpp b/src/IW5/Assets/Material.cpp new file mode 100644 index 0000000..ff97084 --- /dev/null +++ b/src/IW5/Assets/Material.cpp @@ -0,0 +1,613 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include "IW4/Structs.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + std::unordered_map mappedTechniqueSets = + { + // IW4 IW5 + {"wc_l_sm_ua_b0c0q0n0s0p0_nocast", "wc_l_sm_ua_b0c0n0s0p0_nocast"}, + {"wc_ambient_t0c0", "mc_ambient_t0c0"}, + + // mp_favela + {"wc_l_ua_b0c0_nocast", "wc_l_ua_b0c0p0_nocast"}, + {"wc_distortion_scale_ua_zfeather", "wc_distortion_scale_ua"}, + {"wc_l_sm_t0c0d0n0s0p0", "wc_l_sm_t0c0n0s0p0"}, + }; + + struct WaterWritable + { + float floatTime; + }; + + struct complex_s + { + float real; + float imag; + }; + + struct water_t + { + WaterWritable writable; + complex_s* H0; + float* wTerm; + int M; + int N; + float Lx; + float Lz; + float gravity; + float windvel; + float winddir[2]; + float amplitude; + float codeConstant[4]; + GfxImage* image; + }; + +#define MATERIAL_DUMP_STRING(entry) \ + matdata[#entry] = std::string(asset->entry); + +#define MATERIAL_DUMP_INT(entry) \ + matdata[#entry] = asset->entry; + +#define MATERIAL_INT(entry) \ + mat->entry = matdata[#entry].get(); + +#define STATEBITENTRYNUM 48 + +#define MATERIAL_DUMP_BITS_ENTRY(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + carr##entry[i] = asset->entry[i]; \ + } \ + matdata[#entry] = carr##entry; + +#define MATERIAL_INT(entry) \ + mat->entry = matdata[#entry].get(); + +#define MATERIAL_DUMP_CONST_ARRAY(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + nlohmann::json cent##entry; \ + cent##entry["name"] = asset->entry[i].name; \ + cent##entry["nameHash"] = asset->entry[i].nameHash; \ + nlohmann::json centliteral##entry; \ + centliteral##entry[0] = asset->entry[i].literal[0]; \ + centliteral##entry[1] = asset->entry[i].literal[1]; \ + centliteral##entry[2] = asset->entry[i].literal[2]; \ + centliteral##entry[3] = asset->entry[i].literal[3]; \ + cent##entry["literal"] = centliteral##entry; \ + carr##entry[i] = cent##entry; \ + } \ + matdata[#entry] = carr##entry; + + +#define MATERIAL_DUMP_STATE_MAP(entry,size) \ + nlohmann::json carr##entry; \ + for (int i = 0; i < size; i++) \ + { \ + nlohmann::json cent##entry; \ + cent##entry[0] = asset->entry[i].loadBits[0]; \ + cent##entry[1] = asset->entry[i].loadBits[1]; \ + carr##entry[i] = cent##entry; \ + } \ + matdata[#entry] = carr##entry; + + // Legacy zonetool code: REFACTOR ME! + enum IWI_COMPRESSION_e + { + IWI_INVALID = 0x0, + IWI_ARGB = 0x1, + IWI_RGB8 = 0x2, + IWI_DXT1 = 0xB, + IWI_DXT3 = 0xC, + IWI_DXT5 = 0xD, + } IWI_COMPRESSION; + + enum MaterialMapHashes + { + HASH_COLORMAP = 0xa0ab1041, + HASH_DETAILMAP = 0xeb529b4d, + HASH_SPECULARMAP = 0x34ecccb3, + HASH_NORMALMAP = 0x59d30d0f + }; + + enum MaterialSemantic + { + SEMANTIC_2D = 0x0, + SEMANTIC_FUNCTION = 0x1, + SEMANTIC_COLOR_MAP = 0x2, + SEMANTIC_NORMAL_MAP = 0x5, + SEMANTIC_SPECULAR_MAP = 0x8, + SEMANTIC_WATER_MAP = 0xB + }; + + typedef struct + { + char magic[3]; //IWi + char version; // 8 + int flags; + short format; // see above + short xsize; + short ysize; + short depth; + int mipAddr4; + int mipAddr3; + int mipAddr2; + int mipAddr1; + } _IWI; + + // Image parsing + _IWI* LoadIWIHeader(std::string name, ZoneMemory* mem) + { + auto path = "images\\" + name + ".iwi"; + + if (FileSystem::FileExists(path)) + { + auto buf = mem->Alloc<_IWI>(); + + auto fp = FileSystem::FileOpen(path, "rb"s); + if (fp) + { + fread(buf, sizeof(_IWI), 1, fp); + } + FileSystem::FileClose(fp); + + return buf; + } // ZONETOOL_ERROR("Image \"%s\" does not exist!", name.c_str()); + + return nullptr; + } + + GfxImageLoadDef* GenerateLoadDef(GfxImage* image, short iwi_format, ZoneMemory* mem) + { + auto texture = mem->Alloc(); + + switch (iwi_format) + { + case IWI_ARGB: + texture->format = 21; + break; + case IWI_RGB8: + texture->format = 20; + break; + case IWI_DXT1: + texture->format = 0x31545844; + break; + case IWI_DXT3: + texture->format = 0x33545844; + break; + case IWI_DXT5: + texture->format = 0x35545844; + break; + } + texture->dimensions[0] = image->width; + texture->dimensions[1] = image->height; + texture->dimensions[2] = image->depth; + + return texture; + } + + GfxImage* Image_Parse(const char* name, char semantic, char category, char flags, + ZoneMemory* mem) + { + _IWI* buf = LoadIWIHeader(name, mem); + + if (buf) + { + auto ret = mem->Alloc(); + + ret->height = buf->xsize; + ret->width = buf->ysize; + ret->depth = buf->depth; + ret->dataLen1 = buf->mipAddr4 - 32; + ret->dataLen2 = buf->mipAddr4 - 32; + ret->name = strdup(name); + ret->semantic = semantic; + ret->category = category; + ret->flags = flags; + ret->mapType = 3; // hope that works lol + + ret->texture = GenerateLoadDef(ret, buf->format, mem); + + return ret; + } + + return DB_FindXAssetHeader(image, name, 1).gfximage; + } + + MaterialImage* Material_ParseMaps(nlohmann::json& matdata, ZoneMemory* mem) + { + auto mat = mem->Alloc(matdata.size()); + + for (std::size_t i = 0; i < matdata.size(); i++) + { + mat[i].firstCharacter = matdata[i]["firstCharacter"].get(); + mat[i].secondLastCharacter = matdata[i]["lastCharacter"].get(); + mat[i].sampleState = matdata[i]["sampleState"].get(); + mat[i].semantic = matdata[i]["semantic"].get(); + mat[i].typeHash = matdata[i]["typeHash"].get(); + + std::string img = matdata[i]["image"].get(); + mat[i].image = Image_Parse(img.data(), mat[i].semantic, 0, 0, mem); + } + + return mat; + } + + unsigned int R_HashString(const char* string) + { + unsigned int hash = 0; + + while (*string) + { + hash = (*string | 0x20) ^ (33 * hash); + string++; + } + + return hash; + } + + extern std::unordered_map iw3TechniqueMap; + + Material* IMaterial::parse(std::string name, ZoneMemory* mem) + { + auto path = "materials\\" + name; + auto file = FileSystem::FileOpen(path, "rb"s); + if (!file) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing material \"%s\"...", name.c_str()); + + auto size = FileSystem::FileSize(file); + auto bytes = FileSystem::ReadBytes(file, size); + FileSystem::FileClose(file); + + nlohmann::json matdata = nlohmann::json::parse(bytes); + + auto mat = mem->Alloc(); + mat->name = mem->StrDup(name); + + MATERIAL_INT(gameFlags); + MATERIAL_INT(sortKey); + MATERIAL_INT(animationX); + MATERIAL_INT(animationY); + + mat->surfaceTypeBits = matdata["surfaceTypeBits"].get(); + mat->stateFlags = matdata["stateFlags"].get(); + mat->cameraRegion = matdata["cameraRegion"].get(); + + std::string techset = matdata["techniqueSet->name"]; + + if (!techset.empty()) + { + mat->techniqueSet = DB_FindXAssetHeader(XAssetType::techset, techset.data(), 1).techset; + } + + nlohmann::json maps = matdata["maps"]; + if (maps.size()) + { + mat->maps = Material_ParseMaps(maps, mem); + } + + mat->numMaps = maps.size(); + + nlohmann::json constantTable = matdata["constantTable"]; + if (constantTable.size() > 0) + { + auto constant_def = mem->Alloc(constantTable.size()); + for (int i = 0; i < constantTable.size(); i++) + { + strncpy(constant_def[i].name, constantTable[i]["name"].get().c_str(), 11); + constant_def[i].name[11] = '\0'; + if (constantTable[i].find("nameHash") == constantTable[i].end()) + { + ZONETOOL_WARNING( +"IT APPEARS THAT YOU ARE PARSING A MATERIAL FROM AN OLDER VERSION OF ZONETOOL. CONSIDER REDUMPING YOUR MATERIALS."); + constant_def[i].nameHash = R_HashString(constant_def[i].name); + } + else + { + constant_def[i].nameHash = constantTable[i]["nameHash"].get(); + } + constant_def[i].literal[0] = constantTable[i]["literal"][0].get(); + constant_def[i].literal[1] = constantTable[i]["literal"][1].get(); + constant_def[i].literal[2] = constantTable[i]["literal"][2].get(); + constant_def[i].literal[3] = constantTable[i]["literal"][3].get(); + } + mat->constantTable = constant_def; + } + else + { + mat->constantTable = nullptr; + } + mat->constantCount = constantTable.size(); + + + nlohmann::json stateMap = matdata["stateMap"]; + if (stateMap.size() > 0) + { + auto stateBits = mem->Alloc(stateMap.size()); + for (int i = 0; i < stateMap.size(); i++) + { + stateBits[i].loadBits[0] = stateMap[i][0].get(); + stateBits[i].loadBits[1] = stateMap[i][1].get(); + } + mat->stateMap = stateBits; + } + else + { + mat->stateMap = nullptr; + } + mat->stateBitsCount = stateMap.size(); + + if (mat->techniqueSet) + { + auto* statebits = ITechset::parse_statebits(mat->techniqueSet->name, mem); + memcpy(mat->stateBitsEntry, statebits, sizeof mat->stateBitsEntry); + } + + auto max_state_index = 0; + for (auto i = 0; i < 54; i++) + { + if (mat->stateBitsEntry[i] < 0) + { + continue; + } + + if (mat->stateBitsEntry[i] > max_state_index) + { + max_state_index = mat->stateBitsEntry[i]; + } + } + + if (max_state_index >= mat->stateBitsCount) + { + ZONETOOL_FATAL("Material %s is referencing more statebit entries than it has!", mat->name); + } + + if (max_state_index != mat->stateBitsCount - 1) + { + ZONETOOL_INFO("Material %s has %u statebits but only %u are used, removing unused statebits.", mat->name, mat->stateBitsCount, max_state_index + 1); + mat->stateBitsCount = max_state_index + 1; + } + + // why??? + mat->NiceMemeIW = 0; + + return mat; + } + + void IMaterial::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data(), 1).material; + } + } + + void IMaterial::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IMaterial::load_depending(IZone* zone) + { + auto data = this->asset_; + + if (data->techniqueSet) + { + zone->add_asset_of_type(techset, data->techniqueSet->name); + } + + for (char i = 0; i < data->numMaps; i++) + { + if (data->maps[i].image) + { + // use pointer rather than name here + zone->add_asset_of_type_by_pointer(image, data->maps[i].image); + } + } + } + + std::string IMaterial::name() + { + return this->name_; + } + + std::int32_t IMaterial::type() + { + return material; + } + + void IMaterial::write(IZone* zone, ZoneBuffer* buf) + { + auto dest = buf->at(); + auto data = this->asset_; + + buf->write(data); + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->techniqueSet) + { + dest->techniqueSet = reinterpret_cast(zone->get_asset_pointer( + techset, data->techniqueSet->name)); + } + + if (data->maps) + { + buf->align(3); + auto destmaps = buf->write(data->maps, data->numMaps); + + for (int i = 0; i < data->numMaps; i++) + { + if (data->maps[i].semantic == 11) + { + ZONETOOL_FATAL("Watermaps are not supported. Material name: %s", data->name); + destmaps[i].image = nullptr; + } + if (data->maps[i].image) + { + destmaps[i].image = reinterpret_cast(zone->get_asset_pointer( + image, data->maps[i].image->name)); + } + } + + ZoneBuffer::clear_pointer(&dest->maps); + } + + if (data->constantTable) + { + /*buf->align(15); + buf->write( + ZoneBuffer::clear_pointer(&dest->constantTable);*/ + dest->constantTable = buf->write_s(15, data->constantTable, data->constantCount); + } + + if (data->stateMap) + { + /*buf->align(3); + buf->write( + ZoneBuffer::clear_pointer(&dest->stateMap);*/ + dest->stateMap = buf->write_s(3, data->stateMap, data->stateBitsCount); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IMaterial::dump(Material* asset) + { + if (asset && asset->techniqueSet) + { + ITechset::dump_statebits(asset->techniqueSet->name, asset->stateBitsEntry); + } + + nlohmann::json matdata; + + MATERIAL_DUMP_STRING(name); + + if (asset->techniqueSet) + { + MATERIAL_DUMP_STRING(techniqueSet->name); + } + + MATERIAL_DUMP_INT(gameFlags); + MATERIAL_DUMP_INT(sortKey); + MATERIAL_DUMP_INT(animationX); + MATERIAL_DUMP_INT(animationY); + + MATERIAL_DUMP_INT(unknown); + MATERIAL_DUMP_INT(surfaceTypeBits); + MATERIAL_DUMP_INT(stateFlags); + MATERIAL_DUMP_INT(cameraRegion); + + MATERIAL_DUMP_CONST_ARRAY(constantTable, asset->constantCount); + MATERIAL_DUMP_STATE_MAP(stateMap, asset->stateBitsCount); + + nlohmann::json material_images; + for (int i = 0; i < asset->numMaps; i++) + { + nlohmann::json image; + + // watermap + if (asset->maps[i].semantic == 11) + { + ZONETOOL_INFO("Dumping water data for image %s\n", asset->name); + + water_t* waterData = reinterpret_cast(asset->maps[i].image); + + image["image"] = waterData->image->name; + + nlohmann::json waterdata; + waterdata["floatTime"] = waterData->writable.floatTime; + waterdata["codeConstant"][0] = waterData->codeConstant[0]; + waterdata["codeConstant"][1] = waterData->codeConstant[1]; + waterdata["codeConstant"][2] = waterData->codeConstant[2]; + waterdata["codeConstant"][3] = waterData->codeConstant[3]; + waterdata["M"] = waterData->M; + waterdata["N"] = waterData->N; + waterdata["Lx"] = waterData->Lx; + waterdata["Lz"] = waterData->Lz; + waterdata["gravity"] = waterData->gravity; + waterdata["windvel"] = waterData->windvel; + waterdata["winddir"][0] = waterData->winddir[0]; + waterdata["winddir"][1] = waterData->winddir[1]; + waterdata["amplitude"] = waterData->amplitude; + + nlohmann::json waterComplexData; + nlohmann::json wTerm; + + for (int i = 0; i < waterData->M * waterData->N; i++) + { + nlohmann::json complexdata; + nlohmann::json curWTerm; + + complexdata["real"] = waterData->H0[i].real; + complexdata["imag"] = waterData->H0[i].imag; + + curWTerm[i] = waterData->wTerm[i]; + + waterComplexData[i] = complexdata; + } + + waterdata["complex"] = waterComplexData; + waterdata["wTerm"] = wTerm; + + image["waterinfo"] = waterdata; + } + else + { + if (asset->maps[i].image) + { + image["image"] = asset->maps[i].image->name; + } + else + { + image["image"] = ""; + } + } + + image["semantic"] = asset->maps[i].semantic; + image["sampleState"] = asset->maps[i].sampleState; + image["lastCharacter"] = asset->maps[i].secondLastCharacter; + image["firstCharacter"] = asset->maps[i].firstCharacter; + image["typeHash"] = asset->maps[i].typeHash; + + // add image data to material + material_images[i] = image; + } + matdata["maps"] = material_images; + + std::string assetstr = matdata.dump(4); + + auto assetPath = "materials\\"s + asset->name; + + auto fileAsset = FileSystem::FileOpen(assetPath, "wb"); + + if (fileAsset) + { + fwrite(assetstr.c_str(), assetstr.size(), 1, fileAsset); + FileSystem::FileClose(fileAsset); + } + } + } +} diff --git a/src/IW5/Assets/Material.hpp b/src/IW5/Assets/Material.hpp new file mode 100644 index 0000000..1dd52ef --- /dev/null +++ b/src/IW5/Assets/Material.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IMaterial : public IAsset + { + private: + std::string name_; + Material* asset_ = nullptr; + + Material* parse(std::string name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(Material* asset); + }; + } +} diff --git a/src/IW5/Assets/MenuDef.cpp b/src/IW5/Assets/MenuDef.cpp new file mode 100644 index 0000000..98d752e --- /dev/null +++ b/src/IW5/Assets/MenuDef.cpp @@ -0,0 +1,76 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void IMenuDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).menu; + } + + void IMenuDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IMenuDef::load_depending(IZone* zone) + { + } + + std::string IMenuDef::name() + { + return this->name_; + } + + std::int32_t IMenuDef::type() + { + return menu; + } + + void IMenuDef::write_menu_data(IZone* zone, ZoneBuffer* buf, menuData_t* data) + { + auto* dest = buf->write(data); + memset(dest, 0, sizeof menuData_t); + } + + void IMenuDef::write(IZone* zone, ZoneBuffer* buf) + { + // the only purpose for this is to decrease load time of the game... + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + // empty out menu + memset(dest, 0, sizeof menuDef_t); + + // menudata + if (data->data) + { + buf->align(3); + write_menu_data(zone, buf, data->data); + ZoneBuffer::clear_pointer(&dest->data); + } + + // write name + dest->window.name = buf->write_str(this->name()); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IMenuDef::dump(menuDef_t* asset) + { + } + } +} diff --git a/src/IW5/Assets/MenuDef.hpp b/src/IW5/Assets/MenuDef.hpp new file mode 100644 index 0000000..7159808 --- /dev/null +++ b/src/IW5/Assets/MenuDef.hpp @@ -0,0 +1,34 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IMenuDef : public IAsset + { + private: + std::string name_; + menuDef_t* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write_menu_data(IZone* zone, ZoneBuffer* buf, menuData_t* data); + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(menuDef_t* asset); + }; + } +} diff --git a/src/IW5/Assets/PhysCollmap.cpp b/src/IW5/Assets/PhysCollmap.cpp new file mode 100644 index 0000000..6388d8d --- /dev/null +++ b/src/IW5/Assets/PhysCollmap.cpp @@ -0,0 +1,264 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + PhysCollmap* IPhysCollmap::parse(const std::string& name, ZoneMemory* mem) + { + auto version = 4; + auto path = "physcollmap/" + name + ".cme4"; + + if (!FileSystem::FileExists(path)) + { + version = 3; + path = "physcollmap/" + name + ".cme3"; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + } + + AssetReader read(mem); + + if (!read.open(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing phys_collmap \"%s\"...", name.c_str()); + + auto* phys_collmap = read.read_array(); + phys_collmap->name = mem->StrDup(name); + + if (read.read_int()) + { + phys_collmap->info = read.read_array(); + + for (auto i = 0; i < phys_collmap->numInfo; i++) + { + if (read.read_int()) + { + phys_collmap->info[i].brush = read.read_array(); + + if (version == 4) + { + if (read.read_int()) + { + phys_collmap->info[i].brush->plane = read.read_array(); + } + } + + if (read.read_int()) + { + phys_collmap->info[i].brush->side = read.read_array(); + + for (auto j = 0u; j < phys_collmap->info[i].brush->numPlaneSide; j++) + { + if (read.read_int()) + { + phys_collmap->info[i].brush->side[j].plane = read.read_array(); + } + } + } + + if (read.read_int()) + { + phys_collmap->info[i].brush->edge = read.read_array(); + } + + if (version == 3) + { + if (read.read_int()) + { + phys_collmap->info[i].brush->plane = read.read_array(); + } + } + } + } + } + + return phys_collmap; + } + + void IPhysCollmap::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).physcollmap; + } + } + + void IPhysCollmap::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IPhysCollmap::load_depending(IZone* zone) + { + } + + std::string IPhysCollmap::name() + { + return this->name_; + } + + std::int32_t IPhysCollmap::type() + { + return phys_collmap; + } + + void IPhysCollmap::write_brush_wrapper(IZone* zone, ZoneBuffer* buf, BrushWrapper* data) + { + auto* dest = buf->write(data); + + if (data->side) + { + buf->align(3); + auto* destsides = buf->write(data->side, data->numPlaneSide); + + for (auto i = 0; i < data->numPlaneSide; i++) + { + destsides[i].plane = buf->write_s(3, data->side[i].plane); + } + + ZoneBuffer::clear_pointer(&dest->side); + } + + if (data->edge) + { + buf->align(0); + buf->write(data->edge, data->numEdge); + ZoneBuffer::clear_pointer(&dest->edge); + } + + if (data->plane) + { + dest->plane = buf->write_s(3, data->plane, data->numPlaneSide); + } + } + + void IPhysCollmap::write_phys_geom_info(IZone* zone, ZoneBuffer* buf, PhysGeomInfo* dest) + { + auto* data = dest; + + if (data->brush) + { + buf->align(3); + this->write_brush_wrapper(zone, buf, data->brush); + ZoneBuffer::clear_pointer(&dest->brush); + } + } + + void IPhysCollmap::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + + dest->name = buf->write_str(this->name()); + + if (data->info) + { + buf->align(3); + auto* destinfo = buf->write(data->info, data->numInfo); + + for (auto i = 0; i < data->numInfo; i++) + { + this->write_phys_geom_info(zone, buf, &destinfo[i]); + } + } + + buf->pop_stream(); + } + + void IPhysCollmap::dump(PhysCollmap* asset) + { + const auto path = "physcollmap/" + std::string(asset->name) + ".cme4"; + + AssetDumper dump; + dump.open(path.data()); + dump.dump_array(asset, 1); + + if (asset->info) + { + dump.dump_int(1); + dump.dump_array(asset->info, asset->numInfo); + + for (auto i = 0; i < asset->numInfo; i++) + { + if (asset->info[i].brush) + { + dump.dump_int(1); + dump.dump_array(asset->info[i].brush, 1); + + if (asset->info[i].brush->plane) + { + dump.dump_int(1); + dump.dump_array(asset->info[i].brush->plane, asset->info[i].brush->numPlaneSide); + } + else + { + dump.dump_int(0); + } + + if (asset->info[i].brush->side) + { + dump.dump_int(1); + dump.dump_array(asset->info[i].brush->side, asset->info[i].brush->numPlaneSide); + + for (auto j = 0u; j < asset->info[i].brush->numPlaneSide; j++) + { + if (asset->info[i].brush->side[j].plane) + { + dump.dump_int(1); + dump.dump_array(asset->info[i].brush->side[j].plane, 1); + } + else + { + dump.dump_int(0); + } + } + } + else + { + dump.dump_int(0); + } + + if (asset->info[i].brush->edge) + { + dump.dump_int(1); + dump.dump_array(asset->info[i].brush->edge, asset->info[i].brush->numEdge); + } + else + { + dump.dump_int(0); + } + } + else + { + dump.dump_int(0); + } + } + } + else + { + dump.dump_int(0); + } + + dump.close(); + } + } +} diff --git a/src/IW5/Assets/PhysCollmap.hpp b/src/IW5/Assets/PhysCollmap.hpp new file mode 100644 index 0000000..71fec44 --- /dev/null +++ b/src/IW5/Assets/PhysCollmap.hpp @@ -0,0 +1,37 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IPhysCollmap : public IAsset + { + private: + std::string name_; + PhysCollmap* asset_ = nullptr; + + public: + PhysCollmap* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write_brush_wrapper(IZone* zone, ZoneBuffer* buf, BrushWrapper* data); + void write_phys_geom_info(IZone* zone, ZoneBuffer* buf, PhysGeomInfo* dest); + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(PhysCollmap* asset); + }; + } +} diff --git a/src/IW5/Assets/PhysPreset.cpp b/src/IW5/Assets/PhysPreset.cpp new file mode 100644 index 0000000..e9d2da5 --- /dev/null +++ b/src/IW5/Assets/PhysPreset.cpp @@ -0,0 +1,60 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void IPhysPreset::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).physpreset; + } + + void IPhysPreset::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IPhysPreset::load_depending(IZone* zone) + { + } + + std::string IPhysPreset::name() + { + return this->name_; + } + + std::int32_t IPhysPreset::type() + { + return physpreset; + } + + void IPhysPreset::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + + dest->name = buf->write_str(this->name()); + + if (data->sndAliasPrefix) + { + dest->sndAliasPrefix = buf->write_str(data->sndAliasPrefix); + } + + buf->pop_stream(); + } + + void IPhysPreset::dump(PhysPreset* asset) + { + } + } +} diff --git a/src/IW5/Assets/PhysPreset.hpp b/src/IW5/Assets/PhysPreset.hpp new file mode 100644 index 0000000..5b079e0 --- /dev/null +++ b/src/IW5/Assets/PhysPreset.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IPhysPreset : public IAsset + { + private: + std::string name_; + PhysPreset* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(PhysPreset* asset); + }; + } +} diff --git a/src/IW5/Assets/PixelShader.cpp b/src/IW5/Assets/PixelShader.cpp new file mode 100644 index 0000000..992d873 --- /dev/null +++ b/src/IW5/Assets/PixelShader.cpp @@ -0,0 +1,127 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + PixelShader* IPixelShader::parse(const std::string& name, ZoneMemory* mem, bool preferLocal) + { + auto path = "pixelshader\\" + name; + + if (!FileSystem::FileExists(path)) + { + path = "techsets\\" + name + ".pixelshader"; + + AssetReader read(mem); + if (!read.open(path, preferLocal)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing pixelshader \"%s\"...", name.data()); + + auto asset = read.read_array(); + asset->name = read.read_string(); + asset->bytecode = read.read_array(); + read.close(); + + return asset; + } + + ZONETOOL_INFO("Parsing custom DirectX pixelshader \"%s\"...", name.data()); + + auto fp = FileSystem::FileOpen(path, "rb"); + + auto asset = mem->Alloc(); + asset->name = mem->StrDup(name); + asset->codeLen = FileSystem::FileSize(fp); + asset->bytecode = mem->ManualAlloc(asset->codeLen); + asset->shader = nullptr; + fread(asset->bytecode, asset->codeLen, 1, fp); + + FileSystem::FileClose(fp); + + return asset; + } + + void IPixelShader::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).pixelshader; + + if (DB_IsXAssetDefault(this->type(), this->name().data())) + { + ZONETOOL_FATAL("PixelShader %s not found.", &name[0]); + } + } + } + + void IPixelShader::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IPixelShader::load_depending(IZone* zone) + { + } + + std::string IPixelShader::name() + { + return this->name_; + } + + std::int32_t IPixelShader::type() + { + return pixelshader; + } + + void IPixelShader::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->bytecode) + { + dest->bytecode = buf->write_s(3, data->bytecode, data->codeLen); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IPixelShader::dump(PixelShader* asset) + { + if (FileSystem::FileExists("techsets\\"s + asset->name + ".pixelshader"s)) + { + return; + } + + AssetDumper write; + if (!write.open("techsets\\"s + asset->name + ".pixelshader"s)) + { + return; + } + + write.dump_array(asset, 1); + write.dump_string(asset->name); + write.dump_array(asset->bytecode, asset->codeLen); + write.close(); + } + } +} diff --git a/src/IW5/Assets/PixelShader.hpp b/src/IW5/Assets/PixelShader.hpp new file mode 100644 index 0000000..ac4f0fa --- /dev/null +++ b/src/IW5/Assets/PixelShader.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IPixelShader : public IAsset + { + private: + std::string name_; + PixelShader* asset_ = nullptr; + + public: + static PixelShader* parse(const std::string& name, ZoneMemory* mem, bool preferLocal = false); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(PixelShader* asset); + }; + } +} diff --git a/src/IW5/Assets/RawFile.cpp b/src/IW5/Assets/RawFile.cpp new file mode 100644 index 0000000..dfebc90 --- /dev/null +++ b/src/IW5/Assets/RawFile.cpp @@ -0,0 +1,133 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include + +#define ZONETOOL_BRANDING "Compiled using ZoneTool by RektInator." + +namespace ZoneTool +{ + namespace IW5 + { + RawFile* IRawFile::parse(const std::string& name, ZoneMemory* mem) + { + if (FileSystem::FileExists(name)) + { + auto* rawfile = mem->Alloc(); + rawfile->name = mem->StrDup(name); + + auto* file = FileSystem::FileOpen(name, "rb"s); + if (file) + { + const auto size = FileSystem::FileSize(file); + auto data = FileSystem::ReadBytes(file, size); + + ZoneBuffer buf(data); + auto compressed = buf.compress_zlib(); + + rawfile->len = size; + rawfile->compressedLen = compressed.size(); + rawfile->buffer = mem->Alloc(size); + memcpy( + const_cast(rawfile->buffer), + compressed.data(), + size); + + FileSystem::FileClose(file); + } + + return rawfile; + } + + return nullptr; + } + + void IRawFile::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = parse(name, mem); + + if (name == FileSystem::GetFastFile()) + { + this->asset_ = mem->Alloc(); + this->asset_->name = mem->StrDup(name); + this->asset_->buffer = mem->StrDup(ZONETOOL_BRANDING); + this->asset_->len = strlen(this->asset_->buffer); + } + else if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data(), 1).rawfile; + } + } + + void IRawFile::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IRawFile::load_depending(IZone* zone) + { + } + + std::string IRawFile::name() + { + return this->name_; + } + + std::int32_t IRawFile::type() + { + return rawfile; + } + + void IRawFile::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->buffer) + { + buf->align(0); + buf->write(data->buffer, data->compressedLen ? data->compressedLen : data->len + 1); + ZoneBuffer::clear_pointer(&dest->buffer); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IRawFile::dump(RawFile* asset) + { + auto fp = FileSystem::FileOpen(asset->name, "wb"); + + if (fp) + { + if (asset->compressedLen > 0) + { + std::vector uncompressed; + uncompressed.resize(asset->len); + + auto status = uncompress(uncompressed.data(), (uLongf*)&asset->len, (Bytef*)asset->buffer, + asset->compressedLen); + + fwrite(uncompressed.data(), uncompressed.size(), 1, fp); + } + else if (asset->len > 0) + { + fwrite(asset->buffer, asset->len, 1, fp); + } + } + + FileSystem::FileClose(fp); + } + } +} diff --git a/src/IW5/Assets/RawFile.hpp b/src/IW5/Assets/RawFile.hpp new file mode 100644 index 0000000..ee93019 --- /dev/null +++ b/src/IW5/Assets/RawFile.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IRawFile : public IAsset + { + private: + std::string name_; + RawFile* asset_ = nullptr; + + RawFile* parse(const std::string& name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(RawFile* asset); + }; + } +} diff --git a/src/IW5/Assets/ScriptFile.cpp b/src/IW5/Assets/ScriptFile.cpp new file mode 100644 index 0000000..086bfd3 --- /dev/null +++ b/src/IW5/Assets/ScriptFile.cpp @@ -0,0 +1,158 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include + +namespace ZoneTool +{ + namespace IW5 + { + ScriptFile* IScriptFile::parse(const std::string& name, ZoneMemory* mem) + { + if (FileSystem::FileExists(name + ".cgsc") && FileSystem::FileExists(name + ".cgsc.stack")) + { + auto* scriptfile = mem->Alloc(); + scriptfile->name = mem->StrDup(name); + + ZONETOOL_INFO("Parsing scriptfile \"%s\"...", name.data()); + + // parse bytecode + auto* file = FileSystem::FileOpen(name + ".cgsc", "rb"s); + if (file) + { + ZONETOOL_INFO("Parsing scriptfile bytecode for script \"%s\"...", name.data()); + + auto size = FileSystem::FileSize(file); + auto data = FileSystem::ReadBytes(file, size); + + scriptfile->bytecode = mem->Alloc(size); + scriptfile->bytecodeLen = size; + memcpy(scriptfile->bytecode, &data[0], size); + + FileSystem::FileClose(file); + } + + // parse stack + file = FileSystem::FileOpen(name + ".cgsc.stack", "rb"s); + if (file) + { + ZONETOOL_INFO("Parsing scriptfile heap for script \"%s\"...", name.data()); + + const auto size = FileSystem::FileSize(file); + auto data = FileSystem::ReadBytes(file, size); + + ZoneBuffer buf(data); + auto compressed = buf.compress_zlib(); + + scriptfile->len = size; + scriptfile->compressedLen = compressed.size(); + scriptfile->buffer = mem->Alloc(size); + memcpy( + const_cast(scriptfile->buffer), + compressed.data(), + size); + + FileSystem::FileClose(file); + } + + return scriptfile; + } + + return nullptr; + } + + void IScriptFile::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data(), 1).scriptfile; + } + } + + void IScriptFile::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IScriptFile::load_depending(IZone* zone) + { + } + + std::string IScriptFile::name() + { + return this->name_; + } + + std::int32_t IScriptFile::type() + { + return scriptfile; + } + + void IScriptFile::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + buf->push_stream(8); + + if (data->buffer) + { + buf->write(data->buffer, data->compressedLen); + ZoneBuffer::clear_pointer(&dest->buffer); + } + + if (data->bytecode) + { + buf->write(data->bytecode, data->bytecodeLen); + ZoneBuffer::clear_pointer(&dest->bytecode); + } + + buf->pop_stream(); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IScriptFile::dump(ScriptFile* asset) + { + if (!asset) + { + return; + } + + auto* fp1 = FileSystem::FileOpen(va("%s.cgsc", asset->name), "wb"); + auto* fp2 = FileSystem::FileOpen(va("%s.cgsc.stack", asset->name), "wb"); + + if (fp1 && asset->bytecode) + { + fwrite(asset->bytecode, asset->bytecodeLen, 1, fp1); + } + + if (fp2 && asset->buffer) + { + std::vector < std::uint8_t > uncompressed; + uncompressed.resize(asset->len); + + auto status = uncompress(uncompressed.data(), (uLongf*)&asset->len, (Bytef*)asset->buffer, asset->compressedLen); + + fwrite(uncompressed.data(), uncompressed.size(), 1, fp2); + } + + FileSystem::FileClose(fp1); + FileSystem::FileClose(fp2); + } + } +} diff --git a/src/IW5/Assets/ScriptFile.hpp b/src/IW5/Assets/ScriptFile.hpp new file mode 100644 index 0000000..06ea112 --- /dev/null +++ b/src/IW5/Assets/ScriptFile.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IScriptFile : public IAsset + { + private: + std::string name_; + ScriptFile* asset_ = nullptr; + + ScriptFile* parse(const std::string& name, ZoneMemory* mem); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(ScriptFile* asset); + }; + } +} diff --git a/src/IW5/Assets/Sound.cpp b/src/IW5/Assets/Sound.cpp new file mode 100644 index 0000000..036b61c --- /dev/null +++ b/src/IW5/Assets/Sound.cpp @@ -0,0 +1,332 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + snd_alias_list_t* ISound::parse(const std::string& name, ZoneMemory* mem) + { + if (name.empty()) + { + return nullptr; + } + + const auto path = "sounds\\"s + name + ".xss"; + + if (FileSystem::FileExists(path)) + { + AssetReader reader(mem); + if (!reader.open(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing sound %s...", name.c_str()); + + auto* asset = reader.read_single(); + asset->name = reader.read_string(); + asset->head = reader.read_array(); + + for (auto i = 0; i < asset->count; i++) + { + auto* current = &asset->head[i]; + + if (current->aliasName) + { + current->aliasName = reader.read_string(); + } + + if (current->subtitle) + { + current->subtitle = reader.read_string(); + } + + if (current->secondaryAliasName) + { + current->secondaryAliasName = reader.read_string(); + } + + if (current->chainAliasName) + { + current->chainAliasName = reader.read_string(); + } + + if (current->mixerGroup) + { + current->mixerGroup = reader.read_string(); + } + + if (current->soundFile) + { + current->soundFile = reader.read_single(); + + if (current->soundFile->type == SAT_LOADED) + { + current->soundFile->sound.loadSnd = reader.read_asset(); + } + else + { + current->soundFile->sound.streamSnd.name = reader.read_string(); + current->soundFile->sound.streamSnd.dir = reader.read_string(); + } + } + + if (current->volumeFalloffCurve) + { + current->volumeFalloffCurve = reader.read_asset(); + } + + if (current->speakerMap) + { + current->speakerMap = reader.read_single(); + current->speakerMap->name = reader.read_string(); + } + } + + reader.close(); + + return asset; + } + + return nullptr; + } + + void ISound::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).sound; + } + } + + void ISound::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ISound::load_depending(IZone* zone) + { + auto* data = this->asset_; + + for (auto i = 0u; i < data->count; i++) + { + auto* head = &data->head[i]; + + if (head->volumeFalloffCurve) + { + zone->add_asset_of_type(sndcurve, head->volumeFalloffCurve->name); + } + + if (head->soundFile) + { + if (head->soundFile->type == SAT_LOADED) + { + zone->add_asset_of_type(loaded_sound, head->soundFile->sound.loadSnd->name); + } + } + } + } + + std::string ISound::name() + { + return this->name_; + } + + std::int32_t ISound::type() + { + return sound; + } + + void ISound::write_soundfile(IZone* zone, ZoneBuffer* buf, SoundFile* data) + { + auto* dest = buf->write(data); + + if (data->type == SAT_STREAMED || data->type == SAT_PRIMED) + { + if (data->sound.streamSnd.dir) + { + dest->sound.streamSnd.dir = buf->write_str(data->sound.streamSnd.dir); + } + + if (data->sound.streamSnd.name) + { + dest->sound.streamSnd.name = buf->write_str(data->sound.streamSnd.name); + } + } + else if (data->type == SAT_LOADED) + { + if (data->sound.loadSnd) + { + dest->sound.loadSnd = static_cast(zone->get_asset_pointer( + loaded_sound, data->sound.loadSnd->name)); + } + } + } + + void ISound::write_head(IZone* zone, ZoneBuffer* buf, snd_alias_t* dest) + { + auto* data = dest; + + if (data->aliasName) + { + dest->aliasName = buf->write_str(data->aliasName); + } + + if (data->subtitle) + { + dest->subtitle = buf->write_str(data->subtitle); + } + + if (data->secondaryAliasName) + { + dest->secondaryAliasName = buf->write_str(data->secondaryAliasName); + } + + if (data->chainAliasName) + { + dest->chainAliasName = buf->write_str(data->chainAliasName); + } + + if (data->mixerGroup) + { + dest->mixerGroup = buf->write_str(data->mixerGroup); + } + + if (data->soundFile) + { + buf->align(3); + write_soundfile(zone, buf, data->soundFile); + ZoneBuffer::clear_pointer(&dest->soundFile); + } + + if (data->volumeFalloffCurve) + { + dest->volumeFalloffCurve = static_cast(zone->get_asset_pointer( + sndcurve, data->volumeFalloffCurve->name)); + } + + if (data->speakerMap) + { + buf->align(3); + auto* speaker_map = buf->write(data->speakerMap); + + if (speaker_map->name) + { + speaker_map->name = buf->write_str(speaker_map->name); + } + + ZoneBuffer::clear_pointer(&dest->speakerMap); + } + } + + void ISound::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (dest->head) + { + buf->align(3); + auto* dest_sound = buf->write(data->head, data->count); + + for (std::int32_t i = 0; i < data->count; i++) + { + write_head(zone, buf, &dest_sound[i]); + } + + ZoneBuffer::clear_pointer(&dest->head); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ISound::dump(snd_alias_list_t* asset) + { + const auto path = "sounds\\"s + asset->name + ".xss"; + + AssetDumper dump; + if (!dump.open(path)) + { + return; + } + + dump.dump_single(asset); + dump.dump_string(asset->name); + dump.dump_array(asset->head, asset->count); + + for (auto i = 0; i < asset->count; i++) + { + auto* current = &asset->head[i]; + + if (current->aliasName) + { + dump.dump_string(current->aliasName); + } + + if (current->subtitle) + { + dump.dump_string(current->subtitle); + } + + if (current->secondaryAliasName) + { + dump.dump_string(current->secondaryAliasName); + } + + if (current->chainAliasName) + { + dump.dump_string(current->chainAliasName); + } + + if (current->mixerGroup) + { + dump.dump_string(current->mixerGroup); + } + + if (current->soundFile) + { + dump.dump_single(current->soundFile); + + if (current->soundFile->type == SAT_LOADED) + { + dump.dump_asset(current->soundFile->sound.loadSnd); + } + else + { + dump.dump_string(current->soundFile->sound.streamSnd.name); + dump.dump_string(current->soundFile->sound.streamSnd.dir); + } + } + + if (current->volumeFalloffCurve) + { + dump.dump_asset(current->volumeFalloffCurve); + } + + if (current->speakerMap) + { + dump.dump_single(current->speakerMap); + dump.dump_string(current->speakerMap->name); + } + } + + dump.close(); + } + } +} diff --git a/src/IW5/Assets/Sound.hpp b/src/IW5/Assets/Sound.hpp new file mode 100644 index 0000000..8dca792 --- /dev/null +++ b/src/IW5/Assets/Sound.hpp @@ -0,0 +1,38 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class ISound : public IAsset + { + private: + std::string name_; + snd_alias_list_t* asset_ = nullptr; + + static void write_soundfile(IZone* zone, ZoneBuffer* buf, SoundFile* dest); + static void write_head(IZone* zone, ZoneBuffer* buf, snd_alias_t* dest); + + public: + static snd_alias_list_t* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(snd_alias_list_t* asset); + }; + } +} diff --git a/src/IW5/Assets/SoundCurve.cpp b/src/IW5/Assets/SoundCurve.cpp new file mode 100644 index 0000000..47ad21d --- /dev/null +++ b/src/IW5/Assets/SoundCurve.cpp @@ -0,0 +1,57 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void ISoundCurve::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).soundcurve; + } + + void ISoundCurve::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ISoundCurve::load_depending(IZone* zone) + { + } + + std::string ISoundCurve::name() + { + return this->name_; + } + + std::int32_t ISoundCurve::type() + { + return sndcurve; + } + + void ISoundCurve::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->filename = buf->write_str(this->name()); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ISoundCurve::dump(SndCurve* asset) + { + } + } +} diff --git a/src/IW5/Assets/SoundCurve.hpp b/src/IW5/Assets/SoundCurve.hpp new file mode 100644 index 0000000..bf30495 --- /dev/null +++ b/src/IW5/Assets/SoundCurve.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class ISoundCurve : public IAsset + { + private: + std::string name_; + SndCurve* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(SndCurve* asset); + }; + } +} diff --git a/src/IW5/Assets/StringTable.cpp b/src/IW5/Assets/StringTable.cpp new file mode 100644 index 0000000..ff96f79 --- /dev/null +++ b/src/IW5/Assets/StringTable.cpp @@ -0,0 +1,223 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + // LEGACY ZONETOOL CODE, FIX ME! + class CSV + { + protected: + std::string _name; + std::vector> _data; + + public: + CSV(std::string name, char sep = ',') + : _name(name) + { + auto fp = FileSystem::FileOpen(name, "r"s); + + if (fp) + { + long len = FileSystem::FileSize(fp); + auto buf = std::make_unique(len + 1); + memset(buf.get(), 0, len + 1); + fread(buf.get(), len, 1, fp); + fclose(fp); + + std::vector rows = split(std::string(buf.get()), '\n'); + + for (auto& row : rows) + { + // Replace literal characters + std::size_t pos; + while ((pos = row.find("\\n")) != std::string::npos) + { + row.replace(pos, 2, "\n"); + } + + while ((pos = row.find("\\t")) != std::string::npos) + { + row.replace(pos, 2, "\t"); + } + + _data.push_back(split(row, sep)); + } + } + } + + std::string entry(std::size_t row, std::size_t column) + { + return _data[row][column]; + } + + std::size_t rows() + { + return _data.size(); + } + + std::size_t columns(std::size_t row) + { + return _data[row].size(); + } + + std::size_t max_columns() + { + std::size_t _max = 0; + + for (std::size_t row = 0; row < this->rows(); row++) + { + if (_max < this->columns(row)) + _max = this->columns(row); + } + + return _max; + } + + void clear() + { + for (std::size_t i = 0; i < _data.size(); i++) + { + for (std::size_t j = 0; j < _data[i].size(); j++) + _data[i][j].clear(); + + _data[i].clear(); + } + + _data.clear(); + } + }; + + int StringTable_Hash(const char* string) + { + int hash = 0; + char* data = _strdup(string); + + while (*data != 0) + { + hash = tolower(*data) + (31 * hash); + data++; + } + + return hash; + } + + StringTable* StringTable_Parse(std::string name, ZoneMemory* mem) + { + auto table = std::make_unique(name); + auto stringtable = mem->Alloc(); + + stringtable->name = mem->StrDup(name.c_str()); + stringtable->rows = table->rows(); + stringtable->columns = table->max_columns(); + stringtable->strings = mem->Alloc(stringtable->rows * stringtable->columns); + + for (int row = 0; row < table->rows(); row++) + { + for (int col = 0; col < table->columns(row); col++) + { + int entry = (row * stringtable->columns) + col; + stringtable->strings[entry].string = mem->StrDup(table->entry(row, col).c_str()); + stringtable->strings[entry].hash = StringTable_Hash(stringtable->strings[entry].string); + } + } + + return stringtable; + } + + void IStringTable::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).stringtable; + + if (FileSystem::FileExists(name)) + { + ZONETOOL_INFO("Parsing stringtable %s...", name.data()); + this->asset_ = StringTable_Parse(name, mem); + } + } + + void IStringTable::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IStringTable::load_depending(IZone* zone) + { + } + + std::string IStringTable::name() + { + return this->name_; + } + + std::int32_t IStringTable::type() + { + return stringtable; + } + + void IStringTable::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->strings) + { + buf->align(3); + const auto destStrings = buf->write(data->strings, data->columns * data->rows); + + if (data->columns * data->rows > 0) + { + for (int i = 0; i < data->columns * data->rows; i++) + { + if (data->strings[i].string) + { + destStrings[i].string = buf->write_str(data->strings[i].string); + } + } + } + + ZoneBuffer::clear_pointer(&dest->strings); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IStringTable::dump(StringTable* asset) + { + std::string path = asset->name; + + auto file = FileSystem::FileOpen(path, "w"s); + + for (int row = 0; row < asset->rows; row++) + { + for (int column = 0; column < asset->columns; column++) + { + fprintf( + file, + "%s%s", + (asset->strings[(row * asset->columns) + column].string) + ? asset->strings[(row * asset->columns) + column].string + : "", + (column == asset->columns - 1) ? "\n" : "," + ); + } + } + + FileSystem::FileClose(file); + } + } +} diff --git a/src/IW5/Assets/StringTable.hpp b/src/IW5/Assets/StringTable.hpp new file mode 100644 index 0000000..18d8f13 --- /dev/null +++ b/src/IW5/Assets/StringTable.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IStringTable : public IAsset + { + private: + std::string name_; + StringTable* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(StringTable* asset); + }; + } +} diff --git a/src/IW5/Assets/StructuredDataDef.cpp b/src/IW5/Assets/StructuredDataDef.cpp new file mode 100644 index 0000000..058ebbf --- /dev/null +++ b/src/IW5/Assets/StructuredDataDef.cpp @@ -0,0 +1,424 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { +#define min(a, b) (a < b) ? a : b + + void* malloc_n(size_t size) + { + void* ret = malloc(size); + memset(ret, 0, size); + return ret; + } + + void IStructuredDataDef::add_entry(enumType_s type, int statIndexOffset, char* entryName) + { + newEnumEntry* newEntry = (newEnumEntry*)malloc_n(sizeof(newEnumEntry)); + newEntry->name = entryName; + newEntry->statIndexOffset = statIndexOffset; + newEntries[type][newEntries[type].size()] = newEntry; + } + + char* strToLower(char* str) + { + int len = strlen(str); + char* newStr = (char*)malloc_n(len + 1); + + for (int i = 0; i < len; i++) + { + newStr[i] = tolower(str[i]); + } + + return newStr; + } + + bool checkAlphabeticalOrder(char* entry1, char* entry2) + { + char* e1_l = strToLower(entry1); + char* e2_l = strToLower(entry2); + + int len = min(strlen(e1_l), strlen(e2_l)); + + for (int i = 0; i < len; i++) + { + if (*(e1_l + i) > *(e2_l + i)) + return false; + if (*(e1_l + i) < *(e2_l + i)) + return true; + } + + return (strlen(e1_l) <= strlen(e2_l)); + } + + void IStructuredDataDef::patch_enum_with_map(StructuredDataDefSet* data, enumType_s enumIndex, + std::map map) + { + StructuredDataDef* current_def = nullptr; + auto current_version = 0; + + for (auto i = 0; i < data->defCount; i++) + { + if (data->defs[i].version > current_version) + { + current_version = data->defs[i].version; + current_def = &data->defs[i]; + } + } + + StructuredDataEnum* currentEnum = ¤t_def->enums[enumIndex]; + + // Check if new enum has already been built + if (newIndexCount[enumIndex]) + { + currentEnum->entryCount = newIndexCount[enumIndex]; + currentEnum->entries = newIndices[enumIndex]; + return; + } + + // Check if new weapons share the same index or if it is invalid + for (std::size_t i = 0; i < map.size(); i++) + { + if (map[i]->statIndexOffset < 1) + { + ZONETOOL_ERROR("Invalid index (%d) for entry %s\n ", map[i]->statIndexOffset, map[i]->name); + } + + for (std::size_t j = 0; j < map.size(); j++) + { + if (i == j) continue; + + if (map[i]->statIndexOffset == map[j]->statIndexOffset) + { + ZONETOOL_ERROR("Index duplication (%d) for entries %s and %s\n", map[i]->statIndexOffset, map[i] +->name, map[j]->name); + } + } + } + + // Define new amount of indices + newIndexCount[enumIndex] = currentEnum->entryCount + map.size(); + + // Find last cac index + int lastCacIndex = 0; + for (int i = 0; i < currentEnum->entryCount; i++) + { + if (currentEnum->entries[i].index > lastCacIndex) + { + lastCacIndex = currentEnum->entries[i].index; + } + } + + // Create new enum + newIndices[enumIndex] = (StructuredDataEnumEntry*)malloc_n( + sizeof(StructuredDataEnumEntry) * (currentEnum->entryCount + map.size() + 1)); + memcpy(newIndices[enumIndex], currentEnum->entries, + sizeof(StructuredDataEnumEntry) * currentEnum->entryCount); + + // Apply new weapons to enum + for (std::size_t i = 0; i < map.size(); i++) + { + int entryPos = 0; + + for (; entryPos < currentEnum->entryCount + i; entryPos++) + { + if (!strcmp(map[i]->name, newIndices[enumIndex][entryPos].name)) + { + ZONETOOL_ERROR("Duplicate playerdatadef entry found: %s", map[i]->name); + } + + // Search weapon position + if (checkAlphabeticalOrder(map[i]->name, (char*)newIndices[enumIndex][entryPos].name)) + break; + } + + for (int j = currentEnum->entryCount + i; j > entryPos; j--) + { + newIndices[enumIndex][j] = newIndices[enumIndex][j - 1]; + } + + newIndices[enumIndex][entryPos].index = map[i]->statIndexOffset + lastCacIndex; + newIndices[enumIndex][entryPos].name = map[i]->name; + } + + // Apply stuff to current player data + currentEnum->entryCount = newIndexCount[enumIndex]; + currentEnum->entries = newIndices[enumIndex]; + } + + // Adds new entries to the structuredDataDef + void IStructuredDataDef::manipulate(StructuredDataDefSet* data) + { + // clear existing data if present + for (int i = 0; i < ENUM_COUNT; i++) + { + if (newEntries[i].size() > 0) + { + newEntries[i].clear(); + } + } + + // Patch mp/playerdata.def if needed + if (data->name == "mp/playerdata.def"s) + { + // Weapons + add_entry(ENUM_WEAPONS, 1, "iw5_ak74u"); + add_entry(ENUM_WEAPONS2, 1, "iw5_ak74u"); + add_entry(ENUM_WEAPONS, 2, "iw5_cheytac"); + add_entry(ENUM_WEAPONS2, 2, "iw5_cheytac"); + + // Gametypes + // add_entry(ENUM_GAMETYPES, 1, "oneflag"); + // add_entry(ENUM_GAMETYPES, 2, "vip"); + // add_entry(ENUM_GAMETYPES, 3, "gtnw"); + + // Custom camos + //add_entry(ENUM_CAMOS, 1, "plutonium"); + + // Reticles + //add_entry(ENUM_RETICLES, 1, "ret7"); + //add_entry(ENUM_RETICLES, 2, "ret8"); + //add_entry(ENUM_RETICLES, 3, "ret9"); + //add_entry(ENUM_RETICLES, 4, "ret10"); + + // Attachments + // + + ZONETOOL_INFO("Statfiles patched."); + } + else if (data->name == "mp/recipes.def"s) + { + // Weapons + add_entry((enumType_s)0, 1, "iw5_ak74u"); + add_entry((enumType_s)6, 1, "iw5_ak74u"); + add_entry((enumType_s)0, 2, "iw5_cheytac"); + add_entry((enumType_s)6, 2, "iw5_cheytac"); + + //// Custom camos + //add_entry((enumType_s)2, 1, "plutonium"); + + //// Reticles + //add_entry((enumType_s)5, 1, "ret7"); + //add_entry((enumType_s)5, 2, "ret8"); + //add_entry((enumType_s)5, 3, "ret9"); + //add_entry((enumType_s)5, 4, "ret10"); + + // Gametypes + // add_entry((enum_type)9, 1, "oneflag"); + // add_entry((enum_type)9, 2, "vip"); + // add_entry((enum_type)9, 3, "gtnw"); + + ZONETOOL_INFO("Recipes patched."); + } + else if (data->name == "mp/prestigedata.def"s) + { + add_entry((enumType_s)5, 1, "iw5_ak74u"); + add_entry((enumType_s)7, 1, "iw5_ak74u"); + add_entry((enumType_s)5, 2, "iw5_cheytac"); + add_entry((enumType_s)7, 2, "iw5_cheytac"); + } + else if (data->name == "mp/resetdata.def"s) + { + add_entry((enumType_s)0, 1, "iw5_ak74u"); + add_entry((enumType_s)0, 2, "iw5_cheytac"); + } + else if (data->name == "mp/elitecreateaclass.def"s) + { + add_entry((enumType_s)1, 1, "iw5_ak74u"); + add_entry((enumType_s)1, 2, "iw5_cheytac"); + } + else if (data->name == "mp/clientmatchdata.def"s) + { + add_entry((enumType_s)0, 1, "iw5_ak74u"); + add_entry((enumType_s)0, 2, "iw5_cheytac"); + } + else if (data->name == "mp/matchdata.def"s) + { + add_entry((enumType_s)3, 1, "iw5_ak74u"); + add_entry((enumType_s)4, 1, "iw5_ak74u"); + add_entry((enumType_s)3, 2, "iw5_cheytac"); + add_entry((enumType_s)4, 2, "iw5_cheytac"); + } + + // Increment version + // data->defs->version += 1; + + // Patch enums + for (int i = 0; i < ENUM_COUNT; i++) + { + if (newEntries[i].size() > 0) + { + patch_enum_with_map(data, static_cast(i), newEntries[i]); + } + } + } + + void IStructuredDataDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).structureddatadef; + + memset(newIndices, 0, sizeof(StructuredDataEnumEntry*) * ENUM_COUNT); + memset(newIndexCount, 0, sizeof(int) * ENUM_COUNT); + + for (int i = 0; i < ENUM_COUNT; i++) + { + newEntries[i].clear(); + } + + // touch the asset + manipulate(this->asset_); + } + + void IStructuredDataDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IStructuredDataDef::load_depending(IZone* zone) + { + } + + std::string IStructuredDataDef::name() + { + return this->name_; + } + + std::int32_t IStructuredDataDef::type() + { + return structureddatadef; + } + + void IStructuredDataDef::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->defs) + { + buf->align(3); + auto* defs = buf->write(data->defs, data->defCount); + + for (auto i = 0u; i < data->defCount; i++) + { + auto* curdef = &data->defs[i]; + + if (curdef->enums) + { + buf->align(3); + auto* enums = buf->write(curdef->enums, curdef->enumCount); + + for (auto j = 0; j < curdef->enumCount; j++) + { + if (enums[j].entries) + { + buf->align(3); + auto entries = buf->write(enums[j].entries, enums[j].entryCount); + + for (int k = 0; k < enums[j].entryCount; k++) + { + if (entries[k].name) + { + buf->write_str(entries[k].name); + ZoneBuffer::clear_pointer(&entries[k].name); + } + } + + ZoneBuffer::clear_pointer(&enums[j].entries); + } + } + + ZoneBuffer::clear_pointer(&defs[i].enums); + } + + if (curdef->structs) + { + buf->align(3); + auto* structs = buf->write(curdef->structs, curdef->structCount); + + for (int j = 0; j < curdef->structCount; j++) + { + if (structs[j].properties) + { + buf->align(3); + auto* props = buf->write(structs[j].properties, structs[j].propertyCount); + + for (auto k = 0; k < structs[j].propertyCount; k++) + { + if (props[k].name) + { + buf->write_str(props[k].name); + ZoneBuffer::clear_pointer(&props[k].name); + } + } + + ZoneBuffer::clear_pointer(&structs[j].properties); + } + } + + ZoneBuffer::clear_pointer(&defs[i].structs); + } + + if (curdef->indexedArrays) + { + buf->align(3); + buf->write(curdef->indexedArrays, curdef->indexedArrayCount); + ZoneBuffer::clear_pointer(&defs[i].indexedArrays); + } + + if (curdef->enumedArrays) + { + buf->align(3); + buf->write(curdef->enumedArrays, curdef->enumedArrayCount); + ZoneBuffer::clear_pointer(&defs[i].enumedArrays); + } + } + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IStructuredDataDef::dump(StructuredDataDefSet* asset) + { + ZONETOOL_INFO("loading \"%s\"", asset->name); + + if (asset) + { + StructuredDataDef* current_def = nullptr; + auto current_version = 0; + + for (auto i = 0; i < asset->defCount; i++) + { + if (asset->defs[i].version > current_version) + { + current_version = asset->defs[i].version; + current_def = &asset->defs[i]; + } + } + + for (auto i = 0; i < current_def->enumCount; i++) + { + ZONETOOL_INFO("\tenum %i:", i); + for (auto j = 0; j < current_def->enums[i].entryCount; j++) + { + ZONETOOL_INFO("\t\t%i: %s", current_def->enums[i].entries[j].index, current_def->enums[i].entries[j].name); + } + } + } + } + } +} diff --git a/src/IW5/Assets/StructuredDataDef.hpp b/src/IW5/Assets/StructuredDataDef.hpp new file mode 100644 index 0000000..01873e6 --- /dev/null +++ b/src/IW5/Assets/StructuredDataDef.hpp @@ -0,0 +1,71 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + struct newEnumEntry + { + char* name; + int statIndexOffset; + }; + + enum enumType_s + { + ENUM_FEATURES, + ENUM_WEAPONS, + ENUM_WEAPONS2, + // Both weapon enums seem different, though they are similar + ENUM_ATTACHEMENTS, + ENUM_CHALLENGES, + ENUM_CAMOS, + ENUM_RETICLES, + ENUM_PERKS, + ENUM_KILLSTREAKS, + ENUM_ACCOLADES, + ENUM_CARDNAMEPLATES, + ENUM_TEAMS, + ENUM_GAMETYPES, + ENUM_XP_MULTIPLIERS, + ENUM_MAX + }; + +#define ENUM_COUNT ENUM_MAX + + class IStructuredDataDef : public IAsset + { + private: + StructuredDataEnumEntry* newIndices[ENUM_COUNT]; + int newIndexCount[ENUM_COUNT]; + std::map newEntries[ENUM_COUNT]; + + std::string name_; + StructuredDataDefSet* asset_ = nullptr; + + void add_entry(enumType_s type, int statIndexOffset, char* entryName); + + void patch_enum_with_map(StructuredDataDefSet* data, enumType_s enumIndex, std::map map); + + void manipulate(StructuredDataDefSet* data); + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(StructuredDataDefSet* asset); + }; + } +} diff --git a/src/IW5/Assets/Techset.cpp b/src/IW5/Assets/Techset.cpp new file mode 100644 index 0000000..8e2919c --- /dev/null +++ b/src/IW5/Assets/Techset.cpp @@ -0,0 +1,482 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include + +namespace ZoneTool +{ + namespace IW5 + { + MaterialTechnique* ITechset::parse_technique(const std::string& name, ZoneMemory* mem, + std::uint32_t index) + { + const auto path = "techsets\\" + name + ".technique"; + if (!FileSystem::FileExists(path)) + { + ZONETOOL_FATAL("technique \"%s\" is missing.", name.data()); + return nullptr; + } + + AssetReader reader(mem); + if (!reader.open(path)) + { + ZONETOOL_FATAL("technique \"%s\" is missing.", name.data()); + return nullptr; + } + + ZONETOOL_INFO("Parsing technique \"%s\"...", name.data()); + + const auto header = reader.read_single(); + const auto passes = reader.read_array(); + + header->name = reader.read_string(); + + const auto asset = mem->ManualAlloc(sizeof(MaterialTechniqueHeader) + (sizeof(MaterialPass) * header->passCount)); + memcpy(&asset->hdr, header, sizeof MaterialTechniqueHeader); + memcpy(asset->pass, passes, sizeof(MaterialPass) * header->passCount); + + for (short i = 0; i < header->passCount; i++) + { + if (asset->pass[i].pixelShader) + { + asset->pass[i].pixelShader = reader.read_asset(); + } + + if (asset->pass[i].vertexShader) + { + asset->pass[i].vertexShader = reader.read_asset(); + } + + if (asset->pass[i].vertexDecl) + { + asset->pass[i].vertexDecl = reader.read_asset(); + } + + if (asset->pass[i].argumentDef) + { + asset->pass[i].argumentDef = reader.read_array(); + + for (auto arg = 0; arg < asset->pass[i].perObjArgCount + asset->pass[i].perPrimArgCount + asset->pass[i].stableArgCount; arg++) + { + if (asset->pass[i].argumentDef[arg].type == 1 || asset->pass[i].argumentDef[arg].type == 8) + { + asset->pass[i].argumentDef[arg].u.literalConst = reader.read_array(); + } + } + } + } + + reader.close(); + + return asset; + } + + MaterialTechniqueSet* ITechset::parse(const std::string& name, + ZoneMemory* mem) + { + const auto path = "techsets\\" + name + ".techset"; + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + AssetReader reader(mem); + if (!reader.open(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing techset \"%s\"...", name.data()); + + const auto asset = reader.read_single(); + asset->name = reader.read_string(); + + for (auto i = 0u; i < 54; i++) + { + if (asset->techniques[i]) + { + asset->techniques[i] = ITechset::parse_technique(reader.read_string(), mem, i); + } + } + + asset->remappedTechniques = nullptr; + + reader.close(); + + return asset; + } + + void ITechset::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1). + techset; + + if (DB_IsXAssetDefault(this->type(), this->name().data())) + { + ZONETOOL_FATAL("Techset %s not found.", &name[0]); + } + } + } + + void ITechset::prepare(ZoneBuffer* buf, + ZoneMemory* mem) + { + } + + void ITechset::load_depending(IZone* zone) + { + auto data = this->asset_; + + for (std::int32_t technique = 0; technique < 54; technique++) + { + if (data->techniques[technique]) + { + for (std::int32_t pass = 0; pass < data->techniques[technique] + ->hdr.passCount; pass++) + { + auto& techniquePass = data->techniques[technique]->pass[pass]; + + if (techniquePass.vertexDecl) + { + zone->add_asset_of_type(vertexdecl, techniquePass.vertexDecl->name); + } + + if (techniquePass.vertexShader) + { + zone->add_asset_of_type( + vertexshader, techniquePass.vertexShader->name); + } + + if (techniquePass.pixelShader) + { + zone->add_asset_of_type(pixelshader, techniquePass.pixelShader->name); + } + } + } + } + } + + std::string ITechset::name() + { + return this->name_; + } + + std::int32_t ITechset::type() + { + return techset; + } + + void ITechset::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + dest->remappedTechniques = nullptr; + +//#ifdef DEBUG +// if (IsDebuggerPresent() && data->techniques[5] == nullptr && data->techniques[9] == nullptr) +// { +// __debugbreak(); +// } +//#endif +// +// assert(data->techniques[5] != nullptr || data->techniques[9] != nullptr); + + for (auto technique = 0; technique < 54; technique++) + { + if (!data->techniques[technique]) + { + continue; + } + + buf->align(3); + + auto* technique_header = buf->write(&data->techniques[technique]->hdr); + auto* technique_passes = buf->write( + data->techniques[technique]->pass, technique_header->passCount); + + for (auto pass = 0; pass < technique_header->passCount; pass++) + { + if (technique_passes[pass].vertexDecl) + { + technique_passes[pass].vertexDecl = + reinterpret_cast(zone->get_asset_pointer( + vertexdecl, technique_passes[pass].vertexDecl->name)); + } + + if (technique_passes[pass].vertexShader) + { + technique_passes[pass].vertexShader = + reinterpret_cast(zone->get_asset_pointer( + vertexshader, technique_passes[pass].vertexShader->name)); + } + + if (technique_passes[pass].pixelShader) + { + technique_passes[pass].pixelShader = + reinterpret_cast(zone->get_asset_pointer( + pixelshader, technique_passes[pass].pixelShader->name)); + } + + if (technique_passes[pass].argumentDef) + { + buf->align(3); + auto destArgs = buf->write(technique_passes[pass].argumentDef, + technique_passes[pass].perPrimArgCount + + technique_passes[pass].perObjArgCount + + technique_passes[pass].stableArgCount); + + for (auto arg = 0; arg < + technique_passes[pass].perPrimArgCount + + technique_passes[pass].perObjArgCount + + technique_passes[pass].stableArgCount; arg++) + { + auto curArg = &technique_passes[pass].argumentDef[arg]; + + if (curArg->type == 1 || curArg->type == 8) + { + if (destArgs[arg].u.literalConst) + { + destArgs[arg].u.literalConst = buf->write_s( + 3, destArgs[arg].u.literalConst, 4); + } + } + } + + ZoneBuffer::clear_pointer(&technique_passes[pass].argumentDef); + } + } + + buf->write_str(technique_header->name); + ZoneBuffer::clear_pointer(&technique_header->name); + + ZoneBuffer::clear_pointer(&dest->techniques[technique]); + } + + END_LOG_STREAM; + buf->pop_stream(); + + encrypt_data(dest, sizeof MaterialTechniqueSet); + } + + void ITechset::dump_technique(MaterialTechnique* asset) + { + auto path = "techsets\\"s + asset->hdr.name + ".technique"; + + AssetDumper dumper; + if (!dumper.open(path)) + { + return; + } + + dumper.dump_single(&asset->hdr); + dumper.dump_array(asset->pass, asset->hdr.passCount); + + dumper.dump_string(asset->hdr.name); + + for (short i = 0; i < asset->hdr.passCount; i++) + { + if (asset->pass[i].pixelShader) + { + dumper.dump_asset(asset->pass[i].pixelShader); + IPixelShader::dump(asset->pass[i].pixelShader); + } + + if (asset->pass[i].vertexShader) + { + dumper.dump_asset(asset->pass[i].vertexShader); + IVertexShader::dump(asset->pass[i].vertexShader); + } + + if (asset->pass[i].vertexDecl) + { + dumper.dump_asset(asset->pass[i].vertexDecl); + IVertexDecl::dump(asset->pass[i].vertexDecl); + } + + if (asset->pass[i].argumentDef) + { + dumper.dump_array(asset->pass[i].argumentDef, asset->pass[i].perObjArgCount + asset->pass[i].perPrimArgCount + asset->pass[i].stableArgCount); + for (auto arg = 0; arg < asset->pass[i].perObjArgCount + asset->pass[i].perPrimArgCount + asset->pass[i].stableArgCount; arg++) + { + if (asset->pass[i].argumentDef[arg].type == 1 || asset->pass[i].argumentDef[arg].type == 8) + { + dumper.dump_array(asset->pass[i].argumentDef[arg].u.literalConst, 4); + } + } + } + } + + dumper.close(); + } + + char* ITechset::parse_statebits(const std::string& techset, ZoneMemory* mem) + { + auto statebits = mem->Alloc(54); + + auto path = "techsets\\" + techset + ".statebits"; + auto fp = FileSystem::FileOpen(path, "rb"); + + if (fp) + { + fread(statebits, 54, 1, fp); + FileSystem::FileClose(fp); + + return statebits; + } + + ZONETOOL_FATAL("statebits for techset \"%s\" are missing!", techset.data()); + return nullptr; + } + + void ITechset::dump_statebits(const std::string& techset, char* statebits) + { + auto path = "techsets\\" + techset + ".statebits"; + auto fp = FileSystem::FileOpen(path, "wb"); + + if (fp) + { + fwrite(statebits, 54, 1, fp); + FileSystem::FileClose(fp); + } + } + + void ITechset::dump_technique_data(MaterialTechniqueSet* asset, bool is_iw5) + { + std::filesystem::create_directories("techsets/techniques"); + + for (int i = 0; i < 54; i++) + { + if (!asset->techniques[i]) + { + continue; + } + + auto technique = asset->techniques[i]; + + std::vector pass_array; + for (int p = 0; p < technique->hdr.passCount; p++) + { + auto current_pass = &technique->pass[p]; + nlohmann::json pass; + + std::vector arg_array; + + for (int a = 0; a < current_pass->perObjArgCount + current_pass->perPrimArgCount + current_pass->stableArgCount; a++) + { + auto current_arg = ¤t_pass->argumentDef[a]; + nlohmann::json arg; + + arg["type"] = current_arg->type; + + if (current_arg->type == 1 || current_arg->type == 8) + { + arg["value"][0] = current_arg->u.literalConst[0]; + arg["value"][1] = current_arg->u.literalConst[0]; + arg["value"][2] = current_arg->u.literalConst[0]; + arg["value"][3] = current_arg->u.literalConst[0]; + } + else if (current_arg->type == 4 || current_arg->type == 6) + { + arg["firstRow"] = current_arg->u.codeConst.firstRow; + arg["rowCount"] = current_arg->u.codeConst.rowCount; + arg["index"] = current_arg->u.codeConst.index; + } + else + { + arg["value"] = current_arg->u.codeSampler; + } + + arg_array.push_back(arg); + } + + pass["perObjArgCount"] = current_pass->perObjArgCount; + pass["perPrimArgCount"] = current_pass->perPrimArgCount; + pass["stableArgCount"] = current_pass->stableArgCount; + pass["pixelShader"] = current_pass->pixelShader ? current_pass->pixelShader->name : ""; + pass["vertexShader"] = current_pass->vertexShader ? current_pass->vertexShader->name : ""; + pass["vertexDecl"] = current_pass->vertexDecl ? current_pass->vertexDecl->name : ""; + pass["args"] = arg_array; + + pass_array.push_back(pass); + } + + nlohmann::json json; + json["name"] = technique->hdr.name; + json["index"] = i; + json["flags"] = technique->hdr.flags; + json["passCount"] = technique->hdr.passCount; + json["pass"] = pass_array; + + auto meme = json.dump(); + + auto fp = fopen(va("techsets/techniques/%s.%s.json", technique->hdr.name, is_iw5 ? "iw5" : "iw4").data(), "wb"); + fwrite(meme.data(), meme.size(), 1, fp); + fclose(fp); + } + } + + void yeet(MaterialTechniqueSet* asset) + { + auto path = "techsets\\"s + asset->name + ".techset.txt"; + + auto fp = FileSystem::FileOpen(path, "wb"); + + for (auto i = 0u; i < 54; i++) + { + if (asset->techniques[i]) + { + fprintf(fp, "%i: %s\n", i, asset->techniques[i]->hdr.name); + } + else + { + fprintf(fp, "%i: nullptr\n", i); + } + } + + FileSystem::FileClose(fp); + } + + void ITechset::dump(MaterialTechniqueSet* asset) + { + yeet(asset); + + auto path = "techsets\\"s + asset->name + ".techset"; + + AssetDumper dumper; + if (!dumper.open(path)) + { + return; + } + + dumper.dump_single(asset); + dumper.dump_string(asset->name); + + for (auto i = 0u; i < 54; i++) + { + if (asset->techniques[i]) + { + dumper.dump_string(asset->techniques[i]->hdr.name); + ITechset::dump_technique(asset->techniques[i]); + } + } + + dumper.close(); + } + } +} diff --git a/src/IW5/Assets/Techset.hpp b/src/IW5/Assets/Techset.hpp new file mode 100644 index 0000000..778a90e --- /dev/null +++ b/src/IW5/Assets/Techset.hpp @@ -0,0 +1,41 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class ITechset : public IAsset + { + private: + std::string name_; + MaterialTechniqueSet* asset_ = nullptr; + + public: + static MaterialTechnique* parse_technique(const std::string& name, ZoneMemory* mem, + std::uint32_t index); + static MaterialTechniqueSet* parse(const std::string& name, ZoneMemory* mem); + static char* parse_statebits(const std::string& techset, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump_technique_data(MaterialTechniqueSet* asset, bool is_iw5 = true); + static void dump_statebits(const std::string& techset, char* statebits); + static void dump(MaterialTechniqueSet* asset); + static void dump_technique(MaterialTechnique* asset); + }; + } +} diff --git a/src/IW5/Assets/TracerDef.cpp b/src/IW5/Assets/TracerDef.cpp new file mode 100644 index 0000000..5a62319 --- /dev/null +++ b/src/IW5/Assets/TracerDef.cpp @@ -0,0 +1,62 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void ITracerDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).tracer; + } + + void ITracerDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void ITracerDef::load_depending(IZone* zone) + { + } + + std::string ITracerDef::name() + { + return this->name_; + } + + std::int32_t ITracerDef::type() + { + return tracer; + } + + void ITracerDef::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->material) + { + dest->material = reinterpret_cast(zone->get_asset_pointer(material, data->material->name)); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void ITracerDef::dump(TracerDef* asset) + { + } + } +} diff --git a/src/IW5/Assets/TracerDef.hpp b/src/IW5/Assets/TracerDef.hpp new file mode 100644 index 0000000..631ef30 --- /dev/null +++ b/src/IW5/Assets/TracerDef.hpp @@ -0,0 +1,33 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class ITracerDef : public IAsset + { + private: + std::string name_; + TracerDef* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(TracerDef* asset); + }; + } +} diff --git a/src/IW5/Assets/VertexDecl.cpp b/src/IW5/Assets/VertexDecl.cpp new file mode 100644 index 0000000..da02bad --- /dev/null +++ b/src/IW5/Assets/VertexDecl.cpp @@ -0,0 +1,100 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + VertexDecl* IVertexDecl::parse(const std::string& name, ZoneMemory* mem, bool preferLocal) + { + auto path = "techsets\\" + name + ".vertexdecl"; + + AssetReader read(mem); + if (!read.open(path, preferLocal)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing vertexdecl \"%s\"...", name.data()); + + auto asset = read.read_array(); + asset->name = read.read_string(); + read.close(); + + return asset; + } + + void IVertexDecl::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).vertexdecl; + + if (DB_IsXAssetDefault(this->type(), this->name().data())) + { + ZONETOOL_FATAL("VertexDecl %s not found.", &name[0]); + } + } + } + + void IVertexDecl::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IVertexDecl::load_depending(IZone* zone) + { + } + + std::string IVertexDecl::name() + { + return this->name_; + } + + std::int32_t IVertexDecl::type() + { + return vertexdecl; + } + + void IVertexDecl::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IVertexDecl::dump(VertexDecl* asset) + { + if (FileSystem::FileExists("techsets\\"s + asset->name + ".vertexdecl"s)) + { + return; + } + + AssetDumper write; + if (!write.open("techsets\\"s + asset->name + ".vertexdecl"s)) + { + return; + } + + write.dump_array(asset, 1); + write.dump_string(asset->name); + write.close(); + } + } +} diff --git a/src/IW5/Assets/VertexDecl.hpp b/src/IW5/Assets/VertexDecl.hpp new file mode 100644 index 0000000..c65ea99 --- /dev/null +++ b/src/IW5/Assets/VertexDecl.hpp @@ -0,0 +1,36 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IVertexDecl : public IAsset + { + private: + std::string name_; + VertexDecl* asset_ = nullptr; + + public: + static VertexDecl* parse(const std::string& name, ZoneMemory* mem, + bool preferLocal = false); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(VertexDecl* asset); + }; + } +} diff --git a/src/IW5/Assets/VertexShader.cpp b/src/IW5/Assets/VertexShader.cpp new file mode 100644 index 0000000..485bc34 --- /dev/null +++ b/src/IW5/Assets/VertexShader.cpp @@ -0,0 +1,134 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + VertexShader* IVertexShader::parse(const std::string& name, ZoneMemory* mem, bool preferLocal) + { + auto path = "vertexshader\\" + name; + + if (!FileSystem::FileExists(path)) + { + path = "techsets\\" + name + ".vertexshader"; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + AssetReader read(mem); + if (!read.open(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing vertexshader \"%s\"...", name.data()); + + auto asset = read.read_array(); + asset->name = read.read_string(); + asset->bytecode = read.read_array(); + read.close(); + + return asset; + } + + ZONETOOL_INFO("Parsing custom DirectX vertexshader \"%s\"...", name.data()); + + auto fp = FileSystem::FileOpen(path, "rb"); + + auto asset = mem->Alloc(); + asset->name = mem->StrDup(name); + asset->codeLen = FileSystem::FileSize(fp); + asset->bytecode = mem->ManualAlloc(asset->codeLen); + asset->shader = nullptr; + fread(asset->bytecode, asset->codeLen, 1, fp); + + FileSystem::FileClose(fp); + + return asset; + } + + void IVertexShader::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).vertexshader; + + if (DB_IsXAssetDefault(this->type(), this->name().data())) + { + ZONETOOL_FATAL("VertexShader %s not found.", &name[0]); + } + } + } + + void IVertexShader::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IVertexShader::load_depending(IZone* zone) + { + } + + std::string IVertexShader::name() + { + return this->name_; + } + + std::int32_t IVertexShader::type() + { + return vertexshader; + } + + void IVertexShader::write(IZone* zone, ZoneBuffer* buf) + { + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->bytecode) + { + buf->align(3); + buf->write(data->bytecode, data->codeLen); + ZoneBuffer::clear_pointer(&dest->bytecode); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IVertexShader::dump(VertexShader* asset) + { + if (FileSystem::FileExists("techsets\\"s + asset->name + ".vertexshader"s)) + { + return; + } + + AssetDumper write; + if (!write.open("techsets\\"s + asset->name + ".vertexshader"s)) + { + return; + } + + write.dump_array(asset, 1); + write.dump_string(asset->name); + write.dump_array(asset->bytecode, asset->codeLen); + write.close(); + } + } +} diff --git a/src/IW5/Assets/VertexShader.hpp b/src/IW5/Assets/VertexShader.hpp new file mode 100644 index 0000000..4ab28a5 --- /dev/null +++ b/src/IW5/Assets/VertexShader.hpp @@ -0,0 +1,36 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IVertexShader : public IAsset + { + private: + std::string name_; + VertexShader* asset_ = nullptr; + + public: + static VertexShader* parse(const std::string& name, ZoneMemory* mem, + bool preferLocal = false); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(VertexShader* asset); + }; + } +} diff --git a/src/IW5/Assets/WeaponDef.cpp b/src/IW5/Assets/WeaponDef.cpp new file mode 100644 index 0000000..34b7d8f --- /dev/null +++ b/src/IW5/Assets/WeaponDef.cpp @@ -0,0 +1,2336 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + +#define WEAPON_READ_FIELD(__type__, __field__) \ + if (!data[#__field__].is_null()) *(__type__*)(&weapon->__field__) = (__type__)data[#__field__].get<__type__>() + +#define WEAPON_READ_FIELD_ARR(__type__, __field__, __size__) \ + if (!data[#__field__].is_null()) \ + { \ + for (auto idx##__field__ = 0u; idx##__field__ < __size__; idx##__field__++) \ + { \ + *(__type__*)(&weapon->__field__[idx##__field__]) = (__type__)data[#__field__][idx##__field__].get<__type__>(); \ + } \ + } + +#define WEAPON_READ_STRING(__field__) \ + if (!data[#__field__].is_null()) weapon->__field__ = mem->StrDup(data[#__field__].get()) + +#define WEAPON_READ_ASSET(__type__, __datafield__, __field__) \ + if (!data[#__field__].is_null() && data[#__field__].is_string()) \ + { \ + auto asset##__field__ = data[#__field__].get(); \ + if (asset##__field__.empty()) \ + { \ + weapon->__field__ = nullptr; \ + } \ + else \ + { \ + weapon->__field__ = DB_FindXAssetHeader(XAssetType::__type__, asset##__field__.data(), 1).__datafield__; \ + } \ + } + + void parse_overlay(ADSOverlay* weapon, Json& data) + { + WEAPON_READ_ASSET(material, material, shader); + WEAPON_READ_ASSET(material, material, shaderLowRes); + WEAPON_READ_ASSET(material, material, shaderEMP); + WEAPON_READ_ASSET(material, material, shaderEMPLowRes); + WEAPON_READ_FIELD(int, reticle); + WEAPON_READ_FIELD(float, width); + WEAPON_READ_FIELD(float, height); + WEAPON_READ_FIELD(float, widthSplitscreen); + WEAPON_READ_FIELD(float, heightSplitscreen); + } + + void parse_statetimers(StateTimers* weapon, Json& data) + { + WEAPON_READ_FIELD(int, iFireDelay); + WEAPON_READ_FIELD(int, iMeleeDelay); + WEAPON_READ_FIELD(int, meleeChargeDelay); + WEAPON_READ_FIELD(int, iDetonateDelay); + WEAPON_READ_FIELD(int, iRechamberTime); + WEAPON_READ_FIELD(int, rechamberTimeOneHanded); + WEAPON_READ_FIELD(int, iRechamberBoltTime); + WEAPON_READ_FIELD(int, iHoldFireTime); + WEAPON_READ_FIELD(int, iDetonateTime); + WEAPON_READ_FIELD(int, iMeleeTime); + WEAPON_READ_FIELD(int, meleeChargeTime); + WEAPON_READ_FIELD(int, iReloadTime); + WEAPON_READ_FIELD(int, reloadShowRocketTime); + WEAPON_READ_FIELD(int, iReloadEmptyTime); + WEAPON_READ_FIELD(int, iReloadAddTime); + WEAPON_READ_FIELD(int, iReloadStartTime); + WEAPON_READ_FIELD(int, iReloadStartAddTime); + WEAPON_READ_FIELD(int, iReloadEndTime); + WEAPON_READ_FIELD(int, iDropTime); + WEAPON_READ_FIELD(int, iRaiseTime); + WEAPON_READ_FIELD(int, iAltDropTime); + WEAPON_READ_FIELD(int, quickDropTime); + WEAPON_READ_FIELD(int, quickRaiseTime); + WEAPON_READ_FIELD(int, iBreachRaiseTime); + WEAPON_READ_FIELD(int, iEmptyRaiseTime); + WEAPON_READ_FIELD(int, iEmptyDropTime); + WEAPON_READ_FIELD(int, sprintInTime); + WEAPON_READ_FIELD(int, sprintLoopTime); + WEAPON_READ_FIELD(int, sprintOutTime); + WEAPON_READ_FIELD(int, stunnedTimeBegin); + WEAPON_READ_FIELD(int, stunnedTimeLoop); + WEAPON_READ_FIELD(int, stunnedTimeEnd); + WEAPON_READ_FIELD(int, nightVisionWearTime); + WEAPON_READ_FIELD(int, nightVisionWearTimeFadeOutEnd); + WEAPON_READ_FIELD(int, nightVisionWearTimePowerUp); + WEAPON_READ_FIELD(int, nightVisionRemoveTime); + WEAPON_READ_FIELD(int, nightVisionRemoveTimePowerDown); + WEAPON_READ_FIELD(int, nightVisionRemoveTimeFadeInStart); + WEAPON_READ_FIELD(int, fuseTime); + WEAPON_READ_FIELD(int, aiFuseTime); + WEAPON_READ_FIELD(int, blastFrontTime); + WEAPON_READ_FIELD(int, blastRightTime); + WEAPON_READ_FIELD(int, blastBackTime); + WEAPON_READ_FIELD(int, blastLeftTime); + WEAPON_READ_FIELD(int, raiseInterruptableTime); + WEAPON_READ_FIELD(int, firstRaiseInterruptableTime); + WEAPON_READ_FIELD(int, reloadInterruptableTime); + WEAPON_READ_FIELD(int, reloadEmptyInterruptableTime); + WEAPON_READ_FIELD(int, fireInterruptableTime); + } + + WeaponDef* parse_weapondef(Json& data, WeaponCompleteDef* baseAsset, + ZoneMemory* mem) + { + auto weapon = mem->Alloc(); + + if (baseAsset) + { + memcpy(weapon, baseAsset->weapDef, sizeof WeaponDef); + } + + WEAPON_READ_STRING(szOverlayName); + WEAPON_READ_ASSET(xmodel, xmodel, handXModel); + WEAPON_READ_STRING(szModeName); + WEAPON_READ_FIELD(int, playerAnimType); + WEAPON_READ_FIELD(int, weapType); + WEAPON_READ_FIELD(int, weapClass); + WEAPON_READ_FIELD(int, penetrateType); + WEAPON_READ_FIELD(int, inventoryType); + WEAPON_READ_FIELD(int, fireType); + WEAPON_READ_FIELD(int, offhandClass); + WEAPON_READ_FIELD(int, stance); + WEAPON_READ_ASSET(fx, fx, viewFlashEffect); + WEAPON_READ_ASSET(fx, fx, worldFlashEffect); + WEAPON_READ_ASSET(fx, fx, viewShellEjectEffect); + WEAPON_READ_ASSET(fx, fx, worldShellEjectEffect); + WEAPON_READ_ASSET(fx, fx, viewLastShotEjectEffect); + WEAPON_READ_ASSET(fx, fx, worldLastShotEjectEffect); + WEAPON_READ_ASSET(material, material, reticleCenter); + WEAPON_READ_ASSET(material, material, reticleSide); + WEAPON_READ_FIELD(int, iReticleCenterSize); + WEAPON_READ_FIELD(int, iReticleSideSize); + WEAPON_READ_FIELD(int, iReticleMinOfs); + WEAPON_READ_FIELD(int, activeReticleType); + WEAPON_READ_FIELD_ARR(float, vStandMove, 3); + WEAPON_READ_FIELD_ARR(float, vStandRot, 3); + WEAPON_READ_FIELD_ARR(float, strafeMove, 3); + WEAPON_READ_FIELD_ARR(float, strafeRot, 3); + WEAPON_READ_FIELD_ARR(float, vDuckedOfs, 3); + WEAPON_READ_FIELD_ARR(float, vDuckedMove, 3); + WEAPON_READ_FIELD_ARR(float, vDuckedRot, 3); + WEAPON_READ_FIELD_ARR(float, vProneOfs, 3); + WEAPON_READ_FIELD_ARR(float, vProneMove, 3); + WEAPON_READ_FIELD_ARR(float, vProneRot, 3); + WEAPON_READ_FIELD(float, fPosMoveRate); + WEAPON_READ_FIELD(float, fPosProneMoveRate); + WEAPON_READ_FIELD(float, fStandMoveMinSpeed); + WEAPON_READ_FIELD(float, fDuckedMoveMinSpeed); + WEAPON_READ_FIELD(float, fProneMoveMinSpeed); + WEAPON_READ_FIELD(float, fPosRotRate); + WEAPON_READ_FIELD(float, fPosProneRotRate); + WEAPON_READ_FIELD(float, fStandRotMinSpeed); + WEAPON_READ_FIELD(float, fDuckedRotMinSpeed); + WEAPON_READ_FIELD(float, fProneRotMinSpeed); + WEAPON_READ_ASSET(xmodel, xmodel, worldClipModel); + WEAPON_READ_ASSET(xmodel, xmodel, rocketModel); + WEAPON_READ_ASSET(xmodel, xmodel, knifeModel); + WEAPON_READ_ASSET(xmodel, xmodel, worldKnifeModel); + WEAPON_READ_ASSET(material, material, hudIcon); + WEAPON_READ_FIELD(int, hudIconRatio); + WEAPON_READ_ASSET(material, material, pickupIcon); + WEAPON_READ_FIELD(int, pickupIconRatio); + WEAPON_READ_ASSET(material, material, ammoCounterIcon); + WEAPON_READ_FIELD(int, ammoCounterIconRatio); + WEAPON_READ_FIELD(int, ammoCounterClip); + WEAPON_READ_FIELD(int, iStartAmmo); + WEAPON_READ_STRING(szAmmoName); + WEAPON_READ_FIELD(int, iAmmoIndex); + WEAPON_READ_STRING(szClipName); + WEAPON_READ_FIELD(int, iClipIndex); + WEAPON_READ_FIELD(int, iMaxAmmo); + WEAPON_READ_FIELD(int, shotCount); + WEAPON_READ_STRING(szSharedAmmoCapName); + WEAPON_READ_FIELD(int, iSharedAmmoCapIndex); + WEAPON_READ_FIELD(int, iSharedAmmoCap); + WEAPON_READ_FIELD(int, damage); + WEAPON_READ_FIELD(int, playerDamage); + WEAPON_READ_FIELD(int, iMeleeDamage); + WEAPON_READ_FIELD(int, iDamageType); + parse_statetimers(&weapon->stateTimers, data["stateTimers"]); + parse_statetimers(&weapon->akimboStateTimers, data["akimboStateTimers"]); + WEAPON_READ_FIELD(float, autoAimRange); + WEAPON_READ_FIELD(float, aimAssistRange); + WEAPON_READ_FIELD(float, aimAssistRangeAds); + WEAPON_READ_FIELD(float, aimPadding); + WEAPON_READ_FIELD(float, enemyCrosshairRange); + WEAPON_READ_FIELD(float, moveSpeedScale); + WEAPON_READ_FIELD(float, adsMoveSpeedScale); + WEAPON_READ_FIELD(float, sprintDurationScale); + WEAPON_READ_FIELD(float, fAdsZoomInFrac); + WEAPON_READ_FIELD(float, fAdsZoomOutFrac); + parse_overlay(&weapon->overlay, data["overlay"]); + WEAPON_READ_FIELD(int, overlayInterface); + WEAPON_READ_FIELD(float, fAdsBobFactor); + WEAPON_READ_FIELD(float, fAdsViewBobMult); + WEAPON_READ_FIELD(float, fHipSpreadStandMin); + WEAPON_READ_FIELD(float, fHipSpreadDuckedMin); + WEAPON_READ_FIELD(float, fHipSpreadProneMin); + WEAPON_READ_FIELD(float, hipSpreadStandMax); + WEAPON_READ_FIELD(float, hipSpreadDuckedMax); + WEAPON_READ_FIELD(float, hipSpreadProneMax); + WEAPON_READ_FIELD(float, fHipSpreadDecayRate); + WEAPON_READ_FIELD(float, fHipSpreadFireAdd); + WEAPON_READ_FIELD(float, fHipSpreadTurnAdd); + WEAPON_READ_FIELD(float, fHipSpreadMoveAdd); + WEAPON_READ_FIELD(float, fHipSpreadDuckedDecay); + WEAPON_READ_FIELD(float, fHipSpreadProneDecay); + WEAPON_READ_FIELD(float, fHipReticleSidePos); + WEAPON_READ_FIELD(float, fAdsIdleAmount); + WEAPON_READ_FIELD(float, fHipIdleAmount); + WEAPON_READ_FIELD(float, adsIdleSpeed); + WEAPON_READ_FIELD(float, hipIdleSpeed); + WEAPON_READ_FIELD(float, fIdleCrouchFactor); + WEAPON_READ_FIELD(float, fIdleProneFactor); + WEAPON_READ_FIELD(float, fGunMaxPitch); + WEAPON_READ_FIELD(float, fGunMaxYaw); + WEAPON_READ_FIELD(float, adsIdleLerpStartTime); + WEAPON_READ_FIELD(float, adsIdleLerpTime); + WEAPON_READ_FIELD(float, swayMaxAngle); + WEAPON_READ_FIELD(float, swayLerpSpeed); + WEAPON_READ_FIELD(float, swayPitchScale); + WEAPON_READ_FIELD(float, swayYawScale); + WEAPON_READ_FIELD(float, swayHorizScale); + WEAPON_READ_FIELD(float, swayVertScale); + WEAPON_READ_FIELD(float, swayShellShockScale); + WEAPON_READ_FIELD(float, adsSwayMaxAngle); + WEAPON_READ_FIELD(float, adsSwayLerpSpeed); + WEAPON_READ_FIELD(float, adsSwayPitchScale); + WEAPON_READ_FIELD(float, adsSwayYawScale); + WEAPON_READ_FIELD(float, adsSwayHorizScale); + WEAPON_READ_FIELD(float, adsSwayVertScale); + WEAPON_READ_FIELD(float, adsViewErrorMin); + WEAPON_READ_FIELD(float, adsViewErrorMax); + // WEAPON_READ_ASSET(phys_collmap, phys_collmap, physCollmap); + WEAPON_READ_FIELD(float, dualWieldViewModelOffset); + WEAPON_READ_FIELD(int, killIconRatio); + WEAPON_READ_FIELD(int, iReloadAmmoAdd); + WEAPON_READ_FIELD(int, iReloadStartAdd); + WEAPON_READ_FIELD(int, ammoDropStockMin); + WEAPON_READ_FIELD(int, ammoDropClipPercentMin); + WEAPON_READ_FIELD(int, ammoDropClipPercentMax); + WEAPON_READ_FIELD(int, iExplosionRadius); + WEAPON_READ_FIELD(int, iExplosionRadiusMin); + WEAPON_READ_FIELD(int, iExplosionInnerDamage); + WEAPON_READ_FIELD(int, iExplosionOuterDamage); + WEAPON_READ_FIELD(float, damageConeAngle); + WEAPON_READ_FIELD(float, bulletExplDmgMult); + WEAPON_READ_FIELD(float, bulletExplRadiusMult); + WEAPON_READ_FIELD(int, iProjectileSpeed); + WEAPON_READ_FIELD(int, iProjectileSpeedUp); + WEAPON_READ_FIELD(int, iProjectileSpeedForward); + WEAPON_READ_FIELD(int, iProjectileActivateDist); + WEAPON_READ_FIELD(float, projLifetime); + WEAPON_READ_FIELD(float, timeToAccelerate); + WEAPON_READ_FIELD(float, projectileCurvature); + WEAPON_READ_ASSET(xmodel, xmodel, projectileModel); + WEAPON_READ_FIELD(int, projExplosion); + WEAPON_READ_ASSET(fx, fx, projExplosionEffect); + WEAPON_READ_ASSET(fx, fx, projDudEffect); + WEAPON_READ_ASSET(sound, sound, projExplosionSound); + WEAPON_READ_ASSET(sound, sound, projDudSound); + WEAPON_READ_FIELD(int, stickiness); + WEAPON_READ_FIELD(float, lowAmmoWarningThreshold); + WEAPON_READ_FIELD(float, ricochetChance); + WEAPON_READ_FIELD(bool, riotShieldEnableDamage); + WEAPON_READ_FIELD(int, riotShieldHealth); + WEAPON_READ_FIELD(float, riotShieldDamageMult); + WEAPON_READ_ASSET(fx, fx, projTrailEffect); + WEAPON_READ_ASSET(fx, fx, projBeaconEffect); + WEAPON_READ_FIELD(float, vProjectileColor[3]); + WEAPON_READ_FIELD(int, guidedMissileType); + WEAPON_READ_FIELD(float, maxSteeringAccel); + WEAPON_READ_FIELD(int, projIgnitionDelay); + WEAPON_READ_ASSET(fx, fx, projIgnitionEffect); + WEAPON_READ_ASSET(sound, sound, projIgnitionSound); + WEAPON_READ_FIELD(float, fAdsAimPitch); + WEAPON_READ_FIELD(float, fAdsCrosshairInFrac); + WEAPON_READ_FIELD(float, fAdsCrosshairOutFrac); + WEAPON_READ_FIELD(int, adsGunKickReducedKickBullets); + WEAPON_READ_FIELD(float, adsGunKickReducedKickPercent); + WEAPON_READ_FIELD(float, fAdsGunKickPitchMin); + WEAPON_READ_FIELD(float, fAdsGunKickPitchMax); + WEAPON_READ_FIELD(float, fAdsGunKickYawMin); + WEAPON_READ_FIELD(float, fAdsGunKickYawMax); + WEAPON_READ_FIELD(float, fAdsGunKickAccel); + WEAPON_READ_FIELD(float, fAdsGunKickSpeedMax); + WEAPON_READ_FIELD(float, fAdsGunKickSpeedDecay); + WEAPON_READ_FIELD(float, fAdsGunKickStaticDecay); + WEAPON_READ_FIELD(float, fAdsViewKickPitchMin); + WEAPON_READ_FIELD(float, fAdsViewKickPitchMax); + WEAPON_READ_FIELD(float, fAdsViewKickYawMin); + WEAPON_READ_FIELD(float, fAdsViewKickYawMax); + WEAPON_READ_FIELD(float, fAdsViewScatterMin); + WEAPON_READ_FIELD(float, fAdsViewScatterMax); + WEAPON_READ_FIELD(float, fAdsSpread); + WEAPON_READ_FIELD(int, hipGunKickReducedKickBullets); + WEAPON_READ_FIELD(float, hipGunKickReducedKickPercent); + WEAPON_READ_FIELD(float, fHipGunKickPitchMin); + WEAPON_READ_FIELD(float, fHipGunKickPitchMax); + WEAPON_READ_FIELD(float, fHipGunKickYawMin); + WEAPON_READ_FIELD(float, fHipGunKickYawMax); + WEAPON_READ_FIELD(float, fHipGunKickAccel); + WEAPON_READ_FIELD(float, fHipGunKickSpeedMax); + WEAPON_READ_FIELD(float, fHipGunKickSpeedDecay); + WEAPON_READ_FIELD(float, fHipGunKickStaticDecay); + WEAPON_READ_FIELD(float, fHipViewKickPitchMin); + WEAPON_READ_FIELD(float, fHipViewKickPitchMax); + WEAPON_READ_FIELD(float, fHipViewKickYawMin); + WEAPON_READ_FIELD(float, fHipViewKickYawMax); + WEAPON_READ_FIELD(float, fHipViewScatterMin); + WEAPON_READ_FIELD(float, fHipViewScatterMax); + WEAPON_READ_FIELD(float, fightDist); + WEAPON_READ_FIELD(float, maxDist); + // WEAPON_READ_STRING(accuracyGraphName[2]); + WEAPON_READ_FIELD(short, accuracyGraphKnotCount); + WEAPON_READ_FIELD(short, originalAccuracyGraphKnotCount); + WEAPON_READ_FIELD(int, iPositionReloadTransTime); + WEAPON_READ_FIELD(float, leftArc); + WEAPON_READ_FIELD(float, rightArc); + WEAPON_READ_FIELD(float, topArc); + WEAPON_READ_FIELD(float, bottomArc); + WEAPON_READ_FIELD(float, accuracy); + WEAPON_READ_FIELD(float, aiSpread); + WEAPON_READ_FIELD(float, playerSpread); + WEAPON_READ_FIELD(float, minTurnSpeed[2]); + WEAPON_READ_FIELD(float, maxTurnSpeed[2]); + WEAPON_READ_FIELD(float, pitchConvergenceTime); + WEAPON_READ_FIELD(float, yawConvergenceTime); + WEAPON_READ_FIELD(float, suppressTime); + WEAPON_READ_FIELD(float, maxRange); + WEAPON_READ_FIELD(float, fAnimHorRotateInc); + WEAPON_READ_FIELD(float, fPlayerPositionDist); + WEAPON_READ_STRING(szUseHintString); + WEAPON_READ_STRING(dropHintString); + WEAPON_READ_FIELD(int, iUseHintStringIndex); + WEAPON_READ_FIELD(int, dropHintStringIndex); + WEAPON_READ_FIELD(float, horizViewJitter); + WEAPON_READ_FIELD(float, vertViewJitter); + WEAPON_READ_FIELD(float, scanSpeed); + WEAPON_READ_FIELD(float, scanAccel); + WEAPON_READ_FIELD(int, scanPauseTime); + WEAPON_READ_STRING(szScript); + WEAPON_READ_FIELD_ARR(float, fOOPosAnimLength, 2); + WEAPON_READ_FIELD(int, minDamage); + WEAPON_READ_FIELD(int, minPlayerDamage); + WEAPON_READ_FIELD(float, fMaxDamageRange); + WEAPON_READ_FIELD(float, fMinDamageRange); + WEAPON_READ_FIELD(float, destabilizationRateTime); + WEAPON_READ_FIELD(float, destabilizationCurvatureMax); + WEAPON_READ_FIELD(int, destabilizeDistance); + WEAPON_READ_FIELD_ARR(float, locationDamageMultipliers, 20); + WEAPON_READ_STRING(fireRumble); + WEAPON_READ_STRING(meleeImpactRumble); + WEAPON_READ_ASSET(tracer, tracer, tracerType); + WEAPON_READ_FIELD(bool, turretADSEnabled); + WEAPON_READ_FIELD(float, turretADSTime); + WEAPON_READ_FIELD(float, turretFov); + WEAPON_READ_FIELD(float, turretFovADS); + WEAPON_READ_FIELD(float, turretScopeZoomRate); + WEAPON_READ_FIELD(float, turretScopeZoomMin); + WEAPON_READ_FIELD(float, turretScopeZoomMax); + WEAPON_READ_FIELD(float, turretOverheatUpRate); + WEAPON_READ_FIELD(float, turretOverheatDownRate); + WEAPON_READ_FIELD(float, turretOverheatPenalty); + WEAPON_READ_ASSET(sound, sound, turretOverheatSound); + WEAPON_READ_ASSET(fx, fx, turretOverheatEffect); + WEAPON_READ_STRING(turretBarrelSpinRumble); + WEAPON_READ_FIELD(float, turretBarrelSpinSpeed); + WEAPON_READ_FIELD(float, turretBarrelSpinUpTime); + WEAPON_READ_FIELD(float, turretBarrelSpinDownTime); + WEAPON_READ_ASSET(sound, sound, turretBarrelSpinMaxSnd); + WEAPON_READ_ASSET(sound, sound, missileConeSoundAlias); + WEAPON_READ_ASSET(sound, sound, missileConeSoundAliasAtBase); + WEAPON_READ_FIELD(float, missileConeSoundRadiusAtTop); + WEAPON_READ_FIELD(float, missileConeSoundRadiusAtBase); + WEAPON_READ_FIELD(float, missileConeSoundHeight); + WEAPON_READ_FIELD(float, missileConeSoundOriginOffset); + WEAPON_READ_FIELD(float, missileConeSoundVolumescaleAtCore); + WEAPON_READ_FIELD(float, missileConeSoundVolumescaleAtEdge); + WEAPON_READ_FIELD(float, missileConeSoundVolumescaleCoreSize); + WEAPON_READ_FIELD(float, missileConeSoundPitchAtTop); + WEAPON_READ_FIELD(float, missileConeSoundPitchAtBottom); + WEAPON_READ_FIELD(float, missileConeSoundPitchTopSize); + WEAPON_READ_FIELD(float, missileConeSoundPitchBottomSize); + WEAPON_READ_FIELD(float, missileConeSoundCrossfadeTopSize); + WEAPON_READ_FIELD(float, missileConeSoundCrossfadeBottomSize); + WEAPON_READ_FIELD(bool, sharedAmmo); + WEAPON_READ_FIELD(bool, lockonSupported); + WEAPON_READ_FIELD(bool, requireLockonToFire); + WEAPON_READ_FIELD(bool, isAirburstWeapon); + WEAPON_READ_FIELD(bool, bigExplosion); + WEAPON_READ_FIELD(bool, noAdsWhenMagEmpty); + WEAPON_READ_FIELD(bool, avoidDropCleanup); + WEAPON_READ_FIELD(bool, inheritsPerks); + WEAPON_READ_FIELD(bool, crosshairColorChange); + WEAPON_READ_FIELD(bool, bRifleBullet); + WEAPON_READ_FIELD(bool, armorPiercing); + WEAPON_READ_FIELD(bool, bBoltAction); + WEAPON_READ_FIELD(bool, aimDownSight); + WEAPON_READ_FIELD(bool, canHoldBreath); + WEAPON_READ_FIELD(bool, canVariableZoom); + WEAPON_READ_FIELD(bool, bRechamberWhileAds); + WEAPON_READ_FIELD(bool, bBulletExplosiveDamage); + WEAPON_READ_FIELD(bool, bCookOffHold); + WEAPON_READ_FIELD(bool, bClipOnly); + WEAPON_READ_FIELD(bool, noAmmoPickup); + WEAPON_READ_FIELD(bool, adsFireOnly); + WEAPON_READ_FIELD(bool, cancelAutoHolsterWhenEmpty); + WEAPON_READ_FIELD(bool, disableSwitchToWhenEmpty); + WEAPON_READ_FIELD(bool, suppressAmmoReserveDisplay); + WEAPON_READ_FIELD(bool, laserSightDuringNightvision); + WEAPON_READ_FIELD(bool, markableViewmodel); + WEAPON_READ_FIELD(bool, noDualWield); + WEAPON_READ_FIELD(bool, flipKillIcon); + WEAPON_READ_FIELD(bool, bNoPartialReload); + WEAPON_READ_FIELD(bool, bSegmentedReload); + WEAPON_READ_FIELD(bool, blocksProne); + WEAPON_READ_FIELD(bool, silenced); + WEAPON_READ_FIELD(bool, isRollingGrenade); + WEAPON_READ_FIELD(bool, projExplosionEffectForceNormalUp); + WEAPON_READ_FIELD(bool, bProjImpactExplode); + WEAPON_READ_FIELD(bool, stickToPlayers); + WEAPON_READ_FIELD(bool, stickToVehicles); + WEAPON_READ_FIELD(bool, stickToTurrets); + WEAPON_READ_FIELD(bool, hasDetonator); + WEAPON_READ_FIELD(bool, disableFiring); + WEAPON_READ_FIELD(bool, timedDetonation); + WEAPON_READ_FIELD(bool, rotate); + WEAPON_READ_FIELD(bool, holdButtonToThrow); + WEAPON_READ_FIELD(bool, freezeMovementWhenFiring); + WEAPON_READ_FIELD(bool, thermalScope); + WEAPON_READ_FIELD(bool, altModeSameWeapon); + WEAPON_READ_FIELD(bool, turretBarrelSpinEnabled); + WEAPON_READ_FIELD(bool, missileConeSoundEnabled); + WEAPON_READ_FIELD(bool, missileConeSoundPitchshiftEnabled); + WEAPON_READ_FIELD(bool, missileConeSoundCrossfadeEnabled); + WEAPON_READ_FIELD(bool, offhandHoldIsCancelable); + WEAPON_READ_FIELD(bool, doNotAllowAttachmentsToOverrideSpread); + WEAPON_READ_ASSET(xmodel, xmodel, stowOffsetModel); + + // parse stowtag + if (!data["stowTag"].is_null()) + { + auto stowTag = data["stowTag"].get(); + weapon->stowTag = SL_AllocString(stowTag); + } + + + weapon->accuracyGraphName[0] = nullptr; + weapon->accuracyGraphName[1] = nullptr; + weapon->accuracyGraphKnots = nullptr; + weapon->originalAccuracyGraphKnots = nullptr; + weapon->accuracyGraphKnotCount = 0; + weapon->originalAccuracyGraphKnotCount = 0; + weapon->parallelBounce = nullptr; + weapon->perpendicularBounce = nullptr; + + weapon->gunXModel = mem->Alloc(16); + weapon->worldModel = mem->Alloc(16); + + for (int i = 0; i < 16; i++) + { + auto gunmodel = data["gunXModel"][i].get(); + + if (!gunmodel.empty()) + { + weapon->gunXModel[i] = DB_FindXAssetHeader(xmodel, gunmodel.data(), 1).xmodel; + } + } + for (int i = 0; i < 16; i++) + { + auto gunmodel = data["worldXModel"][i].get(); + + if (!gunmodel.empty()) + { + weapon->worldModel[i] = DB_FindXAssetHeader(xmodel, gunmodel.data(), 1).xmodel; + } + } + + weapon->notetrackSoundMapKeys = mem->Alloc(24); + weapon->notetrackSoundMapValues = mem->Alloc(24); + for (int i = 0; i < 24; i++) + { + auto notetrack = data["notetrackSoundMapKeys"][i].get(); + weapon->notetrackSoundMapKeys[i] = SL_AllocString(notetrack); + } + for (int i = 0; i < 24; i++) + { + auto notetrack = data["notetrackSoundMapValues"][i].get(); + weapon->notetrackSoundMapValues[i] = SL_AllocString(notetrack); + } + + weapon->notetrackRumbleMapKeys = mem->Alloc(16); + weapon->notetrackRumbleMapValues = mem->Alloc(16); + for (int i = 0; i < 16; i++) + { + auto notetrack = data["notetrackRumbleMapKeys"][i].get(); + weapon->notetrackRumbleMapKeys[i] = SL_AllocString(notetrack); + } + for (int i = 0; i < 16; i++) + { + auto notetrack = data["notetrackRumbleMapValues"][i].get(); + weapon->notetrackRumbleMapValues[i] = SL_AllocString(notetrack); + } + + for (int i = 0; i < 48; i++) + { + auto sound = data["sounds"][i].get(); + + if (!sound.empty()) + { + weapon->sounds[i] = DB_FindXAssetHeader(XAssetType::sound, sound.data(), 1).sound; + } + } + + return weapon; + } + + WeaponCompleteDef* IWeaponDef::parse(const std::string& name, ZoneMemory* mem) + { + sizeof WeaponCompleteDef; + + auto path = "weapons/mp/" + name; + + if (!FileSystem::FileExists(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing weapon \"%s\"...", name.data()); + + auto weapon = mem->Alloc(); + + // parse json file + auto file = FileSystem::FileOpen(path, "rb"); + auto size = FileSystem::FileSize(file); + auto bytes = FileSystem::ReadBytes(file, size); + FileSystem::FileClose(file); + nlohmann::json data = nlohmann::json::parse(bytes); + + // base asset + auto base = data["baseAsset"].get(); + WeaponCompleteDef* baseAsset = nullptr; + + if (!base.empty()) + { + baseAsset = DB_FindXAssetHeader(XAssetType::weapon, base.data(), 1).weapon; + memcpy(weapon, baseAsset, sizeof WeaponCompleteDef); + } + else + { + ZONETOOL_WARNING("No base asset is defined for weapon %s, stuff might go wrong!", name.data()); + } + + WEAPON_READ_STRING(szInternalName); + WEAPON_READ_STRING(szDisplayName); + WEAPON_READ_FIELD(unsigned int, numAnimOverrides); + WEAPON_READ_FIELD(unsigned int, numSoundOverrides); + WEAPON_READ_FIELD(unsigned int, numFXOverrides); + WEAPON_READ_FIELD(unsigned int, numReloadStateTimerOverrides); + WEAPON_READ_FIELD(unsigned int, numNotetrackOverrides); + WEAPON_READ_FIELD(float, fAdsZoomFov); + WEAPON_READ_FIELD(int, iAdsTransInTime); + WEAPON_READ_FIELD(int, iAdsTransOutTime); + WEAPON_READ_FIELD(int, iClipSize); + WEAPON_READ_FIELD(int, impactType); + WEAPON_READ_FIELD(int, iFireTime); + WEAPON_READ_FIELD(int, iFireTimeAkimbo); + WEAPON_READ_FIELD(int, dpadIconRatio); + WEAPON_READ_FIELD(float, penetrateMultiplier); + WEAPON_READ_FIELD(float, fAdsViewKickCenterSpeed); + WEAPON_READ_FIELD(float, fHipViewKickCenterSpeed); + WEAPON_READ_STRING(szAltWeaponName); + WEAPON_READ_FIELD(int, altWeapon); + WEAPON_READ_FIELD(int, iAltRaiseTime); + WEAPON_READ_FIELD(int, iAltRaiseTimeAkimbo); + WEAPON_READ_ASSET(material, material, killIcon); + WEAPON_READ_ASSET(material, material, dpadIcon); + WEAPON_READ_FIELD(int, fireAnimLength); + WEAPON_READ_FIELD(int, fireAnimLengthAkimbo); + WEAPON_READ_FIELD(int, iFirstRaiseTime); + WEAPON_READ_FIELD(int, iFirstRaiseTimeAkimbo); + WEAPON_READ_FIELD(int, ammoDropStockMax); + WEAPON_READ_FIELD(float, adsDofStart); + WEAPON_READ_FIELD(float, adsDofEnd); + WEAPON_READ_FIELD_ARR(unsigned __int16, accuracyGraphKnotCount, 2); + WEAPON_READ_FIELD(bool, motionTracker); + WEAPON_READ_FIELD(bool, enhanced); + WEAPON_READ_FIELD(bool, dpadIconShowsAmmo); + + // parse weapondef + weapon->weapDef = parse_weapondef(data["weapDef"], baseAsset, mem); + + weapon->hideTags = mem->Alloc(32); + for (int i = 0; i < 32; i++) + { + weapon->hideTags[i] = SL_AllocString(data["hideTags"][i].get()); + } + + weapon->scopes = mem->Alloc(6); + for (int i = 0; i < 6; i++) + { + auto attachment = data["scopes"][i].get(); + + if (!attachment.empty()) + { + weapon->scopes[i] = DB_FindXAssetHeader(XAssetType::attachment, attachment.data(), 1). + attachment; + } + } + weapon->underBarrels = mem->Alloc(3); + for (int i = 0; i < 3; i++) + { + auto attachment = data["underBarrels"][i].get(); + + if (!attachment.empty()) + { + weapon->underBarrels[i] = DB_FindXAssetHeader(XAssetType::attachment, attachment.data(), 1). + attachment; + } + } + weapon->others = mem->Alloc(4); + for (int i = 0; i < 4; i++) + { + auto attachment = data["others"][i].get(); + + if (!attachment.empty()) + { + weapon->others[i] = DB_FindXAssetHeader(XAssetType::attachment, attachment.data(), 1). + attachment; + } + } + + weapon->szXAnims = mem->Alloc(42); + for (int i = 0; i < 42; i++) + { + weapon->szXAnims[i] = mem->StrDup(data["szXAnims"][i].get()); + } + + if (weapon->numAnimOverrides) + { + weapon->animOverrides = mem->Alloc(weapon->numAnimOverrides); + for (int i = 0; i < weapon->numAnimOverrides; i++) + { + weapon->animOverrides[i].altmodeAnim = mem->StrDup( + data["animOverrides"][i]["altmodeAnim"].is_string() + ? data["animOverrides"][i]["altmodeAnim"].get() + : ""); + weapon->animOverrides[i].overrideAnim = mem->StrDup( + data["animOverrides"][i]["overrideAnim"].is_string() + ? data["animOverrides"][i]["overrideAnim"].get() + : ""); + weapon->animOverrides[i].attachment1 = data["animOverrides"][i]["attachment1"].get(); + weapon->animOverrides[i].attachment2 = data["animOverrides"][i]["attachment2"].get(); + weapon->animOverrides[i].altTime = data["animOverrides"][i]["altTime"].get(); + weapon->animOverrides[i].animTime = data["animOverrides"][i]["animTime"].get(); + weapon->animOverrides[i].animTreeType = data["animOverrides"][i]["animTreeType"].get(); + } + } + + if (weapon->numSoundOverrides) + { + weapon->soundOverrides = mem->Alloc(weapon->numSoundOverrides); + for (int i = 0; i < weapon->numSoundOverrides; i++) + { + weapon->soundOverrides[i].altmodeSound = (data["soundOverrides"][i]["altmodeSound"].is_string()) + ? DB_FindXAssetHeader( + sound, + data["soundOverrides"][i]["altmodeSound"] + .get().data(), 1).sound + : nullptr; + weapon->soundOverrides[i].overrideSound = (data["soundOverrides"][i]["overrideSound"].is_string()) + ? DB_FindXAssetHeader( + sound, + data["soundOverrides"][i]["overrideSound"] + .get().data(), 1).sound + : nullptr; + weapon->soundOverrides[i].attachment1 = data["soundOverrides"][i]["attachment1"].get(); + weapon->soundOverrides[i].attachment2 = data["soundOverrides"][i]["attachment2"].get(); + weapon->soundOverrides[i].soundType = data["soundOverrides"][i]["soundType"].get(); + } + } + + if (weapon->numFXOverrides) + { + weapon->fxOverrides = mem->Alloc(weapon->numFXOverrides); + for (int i = 0; i < weapon->numFXOverrides; i++) + { + weapon->fxOverrides[i].altmodeFX = (data["fxOverrides"][i]["altmodeFX"].is_string()) + ? DB_FindXAssetHeader( + fx, data["fxOverrides"][i]["altmodeFX"] + .get().data(), 1).fx + : nullptr; + weapon->fxOverrides[i].overrideFX = (data["fxOverrides"][i]["overrideFX"].is_string()) + ? DB_FindXAssetHeader( + fx, data["fxOverrides"][i]["overrideFX"] + .get().data(), 1).fx + : nullptr; + weapon->fxOverrides[i].attachment1 = data["fxOverrides"][i]["attachment1"].get(); + weapon->fxOverrides[i].attachment2 = data["fxOverrides"][i]["attachment2"].get(); + weapon->fxOverrides[i].fxType = data["fxOverrides"][i]["fxType"].get(); + } + } + + if (weapon->numReloadStateTimerOverrides) + { + weapon->reloadOverrides = mem->Alloc(weapon->numReloadStateTimerOverrides); + for (int i = 0; i < weapon->numReloadStateTimerOverrides; i++) + { + weapon->reloadOverrides[i].attachment = data["attachment"].get(); + weapon->reloadOverrides[i].reloadAddTime = data["reloadAddTime"].get(); + weapon->reloadOverrides[i].reloadEmptyAddTime = data["reloadEmptyAddTime"].get(); + } + } + + if (weapon->numNotetrackOverrides) + { + // TODO + weapon->numNotetrackOverrides = 0; + } + + + + return weapon; + } + + void IWeaponDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).weapon; + } + } + + void IWeaponDef::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + auto weapon = mem->Alloc(); + memcpy(weapon, this->asset_, sizeof WeaponCompleteDef); + + weapon->hideTags = mem->Alloc(32); + memcpy(weapon->hideTags, this->asset_->hideTags, sizeof(short) * 32); + + if (weapon->hideTags) + { + for (int i = 0; i < 32; i++) + { + if (weapon->hideTags[i]) + { + std::string tag = SL_ConvertToString(weapon->hideTags[i]); + weapon->hideTags[i] = buf->write_scriptstring(tag); + } + } + } + + if (weapon->notetrackOverrides) + { + for (int i = 0; i < weapon->numNotetrackOverrides; i++) + { + if (weapon->notetrackOverrides[i].notetrackSoundMapKeys) + { + weapon->notetrackOverrides[i].notetrackSoundMapKeys = mem->Alloc(24); + memcpy(weapon->notetrackOverrides[i].notetrackSoundMapKeys, + this->asset_->notetrackOverrides[i].notetrackSoundMapKeys, sizeof(short) * 24); + + for (int nt = 0; nt < 24; nt++) + { + if (weapon->notetrackOverrides[i].notetrackSoundMapKeys[nt]) + { + std::string tag = SL_ConvertToString( + this->asset_->notetrackOverrides[i].notetrackSoundMapKeys[nt]); + weapon->notetrackOverrides[i].notetrackSoundMapKeys[nt] = buf->write_scriptstring(tag); + } + } + } + + if (weapon->notetrackOverrides[i].notetrackSoundMapValues) + { + weapon->notetrackOverrides[i].notetrackSoundMapValues = mem->Alloc(24); + memcpy(weapon->notetrackOverrides[i].notetrackSoundMapValues, + this->asset_->notetrackOverrides[i].notetrackSoundMapValues, sizeof(short) * 24); + + for (int nt = 0; nt < 24; nt++) + { + if (weapon->notetrackOverrides[i].notetrackSoundMapValues[nt]) + { + std::string tag = SL_ConvertToString( + this->asset_->notetrackOverrides[i].notetrackSoundMapValues[nt]); + weapon->notetrackOverrides[i].notetrackSoundMapValues[nt] = buf-> + write_scriptstring(tag); + } + } + } + } + } + + // weaponDef shit + auto data = mem->Alloc(); // weapon->WeaponDef; + memcpy(data, weapon->weapDef, sizeof WeaponDef); + +#define WEAPON_SCRIPTSTRING_ARRAY(__field__,__count__) \ + if (data->__field__) \ + { \ + data->__field__ = mem->Alloc(__count__); \ + memcpy(data->__field__, this->asset_->weapDef->__field__, sizeof(short) * __count__); \ +\ + for (int nt = 0; nt < __count__; nt++) \ + { \ + std::string tag = SL_ConvertToString(data->__field__[nt]); \ + data->__field__[nt] = buf->write_scriptstring(tag); \ + } \ + } + + WEAPON_SCRIPTSTRING_ARRAY(notetrackSoundMapKeys, 24); + WEAPON_SCRIPTSTRING_ARRAY(notetrackSoundMapValues, 24); + WEAPON_SCRIPTSTRING_ARRAY(notetrackRumbleMapKeys, 16); + WEAPON_SCRIPTSTRING_ARRAY(notetrackRumbleMapValues, 16); + + data->stowTag = buf->write_scriptstring(SL_ConvertToString(data->stowTag)); + + weapon->weapDef = data; + this->asset_ = weapon; + } + + void IWeaponDef::load_depending_WeaponDef(IZone* zone, WeaponDef* data) + { +#define WEAPON_SUBASSET(__field__,__type__,__struct__) \ + if (data->__field__) \ + { \ + zone->add_asset_of_type(__type__, data->__field__->name); \ + } + + for (auto i = 0u; i < 16; i++) + { + if (data->worldModel && data->worldModel[i]) + { + zone->add_asset_of_type(xmodel, data->worldModel[i]->name); + } + if (data->gunXModel && data->gunXModel[i]) + { + zone->add_asset_of_type(xmodel, data->gunXModel[i]->name); + } + } + + if (data->handXModel) + { + zone->add_asset_of_type(xmodel, data->handXModel->name); + } + + WEAPON_SUBASSET(viewFlashEffect, fx, FxEffectDef); + WEAPON_SUBASSET(worldFlashEffect, fx, FxEffectDef); + + if (data->notetrackSoundMapValues) + { + for (auto i = 0; i < 24; i++) + { + if (data->notetrackSoundMapValues[i]) + { + zone->add_asset_of_type(sound, SL_ConvertToString(data->notetrackSoundMapValues[i])); + } + } + } + + for (auto i = 0u; i < 48; i++) + { + WEAPON_SUBASSET(sounds[i], sound, snd_alias_list_t); + } + + if (data->bounceSound) + { + for (auto i = 0u; i < 31; i++) + { + if (data->bounceSound[i]) + { + zone->add_asset_of_type(sound, data->bounceSound[i]->name); + } + } + } + + if (data->rollingSound) + { + for (auto i = 0u; i < 31; i++) + { + if (data->rollingSound[i]) + { + zone->add_asset_of_type(sound, data->rollingSound[i]->name); + } + } + } + + WEAPON_SUBASSET(viewShellEjectEffect, fx, FxEffectDef); + WEAPON_SUBASSET(worldShellEjectEffect, fx, FxEffectDef); + WEAPON_SUBASSET(viewLastShotEjectEffect, fx, FxEffectDef); + WEAPON_SUBASSET(worldLastShotEjectEffect, fx, FxEffectDef); + WEAPON_SUBASSET(reticleCenter, material, Material); + WEAPON_SUBASSET(reticleSide, material, Material); + + WEAPON_SUBASSET(worldClipModel, xmodel, XModel); + WEAPON_SUBASSET(rocketModel, xmodel, XModel); + WEAPON_SUBASSET(knifeModel, xmodel, XModel); + WEAPON_SUBASSET(worldKnifeModel, xmodel, XModel); + + WEAPON_SUBASSET(stowOffsetModel, xmodel, XModel); + + WEAPON_SUBASSET(hudIcon, material, Material); + WEAPON_SUBASSET(pickupIcon, material, Material); + WEAPON_SUBASSET(ammoCounterIcon, material, Material); + + WEAPON_SUBASSET(overlay.shader, material, Material); + WEAPON_SUBASSET(overlay.shaderLowRes, material, Material); + WEAPON_SUBASSET(overlay.shaderEMP, material, Material); + WEAPON_SUBASSET(overlay.shaderEMPLowRes, material, Material); + + WEAPON_SUBASSET(physCollmap, phys_collmap, PhysCollmap); + + WEAPON_SUBASSET(projectileModel, xmodel, XModel); + + WEAPON_SUBASSET(projExplosionEffect, fx, FxEffectDef); + WEAPON_SUBASSET(projDudEffect, fx, FxEffectDef); + + WEAPON_SUBASSET(projExplosionSound, sound, snd_alias_list_t); + WEAPON_SUBASSET(projDudSound, sound, snd_alias_list_t); + + WEAPON_SUBASSET(projTrailEffect, fx, FxEffectDef); + WEAPON_SUBASSET(projBeaconEffect, fx, FxEffectDef); + WEAPON_SUBASSET(projIgnitionEffect, fx, FxEffectDef); + + WEAPON_SUBASSET(projIgnitionSound, sound, snd_alias_list_t); + + WEAPON_SUBASSET(tracerType, tracer, TracerDef); + + WEAPON_SUBASSET(turretOverheatSound, sound, snd_alias_list_t); + WEAPON_SUBASSET(turretOverheatEffect, fx, FxEffectDef); + + WEAPON_SUBASSET(turretBarrelSpinMaxSnd, sound, snd_alias_list_t); + + for (int i = 0; i < 4; i++) + { + WEAPON_SUBASSET(turretBarrelSpinUpSnd[i], sound, snd_alias_list_t); + WEAPON_SUBASSET(turretBarrelSpinDownSnd[i], sound, snd_alias_list_t); + } + + WEAPON_SUBASSET(missileConeSoundAlias, sound, snd_alias_list_t); + WEAPON_SUBASSET(missileConeSoundAliasAtBase, sound, snd_alias_list_t); + } + + void IWeaponDef::load_depending(IZone* zone) + { + auto data = this->asset_; + + for (auto i = 0u; i < 6; i++) + { + if (data->scopes && data->scopes[i]) + { + zone->add_asset_of_type(attachment, this->asset_->scopes[i]->szInternalName); + } + + if (i >= 3) continue; + if (data->others && data->others[i]) + { + zone->add_asset_of_type(attachment, this->asset_->others[i]->szInternalName); + } + + // Projectile attachments require fixing. + // if (i >= 4) continue; + // if (data->attachment2[i]) + // { + // zone->AddAssetOfType(attachment, this->asset_->attachment2[i]->szInternalName); + // } + } + + if (data->soundOverrides) + { + for (int i = 0; i < data->numSoundOverrides; i++) + { + if (data->soundOverrides[i].overrideSound) + { + zone->add_asset_of_type(sound, data->soundOverrides[i].overrideSound->name); + } + + if (data->soundOverrides[i].altmodeSound) + { + zone->add_asset_of_type(sound, data->soundOverrides[i].altmodeSound->name); + } + } + } + + if (data->fxOverrides) + { + for (int i = 0; i < data->numSoundOverrides; i++) + { + if (data->fxOverrides[i].overrideFX) + { + zone->add_asset_of_type(fx, data->fxOverrides[i].overrideFX->name); + } + + if (data->fxOverrides[i].altmodeFX) + { + zone->add_asset_of_type(fx, data->fxOverrides[i].altmodeFX->name); + } + } + } + + if (data->dpadIcon) + { + zone->add_asset_of_type(material, data->dpadIcon->name); + } + + if (data->killIcon) + { + zone->add_asset_of_type(material, data->killIcon->name); + } + + if (data->animOverrides) + { + for (auto i = 0u; i < data->numAnimOverrides; i++) + { + if (data->animOverrides[i].overrideAnim) + { + zone->add_asset_of_type(xanim, data->animOverrides[i].overrideAnim); + } + + if (data->animOverrides[i].altmodeAnim) + { + zone->add_asset_of_type(xanim, data->animOverrides[i].altmodeAnim); + } + } + } + + if (data->szXAnims) + { + for (int i = 0; i < 42; i++) + { + if (data->szXAnims[i]) + { + zone->add_asset_of_type(xanim, data->szXAnims[i]); + } + } + } + + load_depending_WeaponDef(zone, data->weapDef); + } + + std::string IWeaponDef::name() + { + return this->name_; + } + + std::int32_t IWeaponDef::type() + { + return weapon; + } + + void IWeaponDef::write_WeaponDef(IZone* zone, ZoneBuffer* buf, WeaponCompleteDef* complete, + WeaponDef* data) + { + auto dest = buf->write(data); + + if (data->szOverlayName) + { + dest->szOverlayName = buf->write_str(data->szOverlayName); + } + + if (data->gunXModel) + { + buf->align(3); + auto destModels = buf->write(data->gunXModel, 16); + + for (auto i = 0u; i < 16; i++) + { + if (destModels[i]) + { + destModels[i] = reinterpret_cast( + zone->get_asset_pointer(xmodel, destModels[i]->name) + ); + } + } + + ZoneBuffer::clear_pointer(&dest->gunXModel); + } + + // Seems to be writting NULL, should fix at some point + if (data->handXModel) + { + dest->handXModel = reinterpret_cast( + zone->get_asset_pointer(xmodel, data->handXModel->name) + ); + } + + if (data->szXAnimsRightHanded) + { + buf->align(3); + auto strings = buf->write(data->szXAnimsRightHanded, 42); + + for (auto i = 0u; i < 42; i++) + { + if (strings[i]) + { + strings[i] = buf->write_str(strings[i]); + } + } + + ZoneBuffer::clear_pointer(&dest->szXAnimsRightHanded); + } + + if (data->szXAnimsLeftHanded) + { + buf->align(3); + auto strings = buf->write(data->szXAnimsLeftHanded, 42); + + for (auto i = 0u; i < 42; i++) + { + if (strings[i]) + { + strings[i] = buf->write_str(strings[i]); + } + } + + ZoneBuffer::clear_pointer(&dest->szXAnimsLeftHanded); + } + + if (data->szModeName) + { + dest->szModeName = buf->write_str(data->szModeName); + } + +#define WEAPON_SCRIPTSTRING_ARRAY(__field__,__count__) \ + if (data->__field__) \ + { \ + buf->align(1); \ + buf->write(data->__field__,__count__); \ + ZoneBuffer::clear_pointer(&dest->__field__); \ + } + + WEAPON_SCRIPTSTRING_ARRAY(notetrackSoundMapKeys, 24); + WEAPON_SCRIPTSTRING_ARRAY(notetrackSoundMapValues, 24); + WEAPON_SCRIPTSTRING_ARRAY(notetrackRumbleMapKeys, 16); + WEAPON_SCRIPTSTRING_ARRAY(notetrackRumbleMapValues, 16); + +#define WEAPON_SUBASSET(__field__,__type__,__struct__) \ + if (data->__field__) \ + { \ + dest->__field__ = reinterpret_cast<__struct__*>(zone->get_asset_pointer(__type__, data->__field__->name)); \ + } + + WEAPON_SUBASSET(viewFlashEffect, fx, FxEffectDef); + WEAPON_SUBASSET(worldFlashEffect, fx, FxEffectDef); + + for (auto i = 0u; i < 48; i++) + { + if (!data->sounds[i]) continue; + + auto ptr = -1; + + buf->align(3); + buf->write(&ptr); + buf->write_str(data->sounds[i]->name); + ZoneBuffer::clear_pointer(&dest->sounds[i]); + } + + if (data->bounceSound) + { + buf->align(3); + auto destSounds = buf->write(data->bounceSound, 31); + + for (auto i = 0u; i < 31; i++) + { + if (destSounds[i]) + { + destSounds[i] = reinterpret_cast( + zone->get_asset_pointer(sound, destSounds[i]->name) + ); + } + } + + ZoneBuffer::clear_pointer(&dest->bounceSound); + } + + if (data->rollingSound) + { + buf->align(3); + auto destSounds = buf->write(data->rollingSound, 31); + + for (auto i = 0u; i < 31; i++) + { + if (destSounds[i]) + { + destSounds[i] = reinterpret_cast( + zone->get_asset_pointer(sound, destSounds[i]->name) + ); + } + } + + ZoneBuffer::clear_pointer(&dest->rollingSound); + } + + WEAPON_SUBASSET(viewShellEjectEffect, fx, FxEffectDef); + WEAPON_SUBASSET(worldShellEjectEffect, fx, FxEffectDef); + WEAPON_SUBASSET(viewLastShotEjectEffect, fx, FxEffectDef); + WEAPON_SUBASSET(worldLastShotEjectEffect, fx, FxEffectDef); + WEAPON_SUBASSET(reticleCenter, material, Material); + WEAPON_SUBASSET(reticleSide, material, Material); + + if (data->worldModel) + { + buf->align(3); + auto destModels = buf->write(data->worldModel, 16); + + for (auto i = 0u; i < 16; i++) + { + if (destModels[i]) + { + destModels[i] = reinterpret_cast( + zone->get_asset_pointer(xmodel, destModels[i]->name) + ); + } + } + + ZoneBuffer::clear_pointer(&dest->worldModel); + } + + WEAPON_SUBASSET(worldClipModel, xmodel, XModel); + WEAPON_SUBASSET(rocketModel, xmodel, XModel); + WEAPON_SUBASSET(knifeModel, xmodel, XModel); + WEAPON_SUBASSET(worldKnifeModel, xmodel, XModel); + + WEAPON_SUBASSET(stowOffsetModel, xmodel, XModel); + + WEAPON_SUBASSET(hudIcon, material, Material); + WEAPON_SUBASSET(pickupIcon, material, Material); + WEAPON_SUBASSET(ammoCounterIcon, material, Material); + + if (data->szAmmoName) + { + dest->szAmmoName = buf->write_str(data->szAmmoName); + } + + if (data->szClipName) + { + dest->szClipName = buf->write_str(data->szClipName); + } + + if (data->szSharedAmmoCapName) + { + dest->szSharedAmmoCapName = buf->write_str(data->szSharedAmmoCapName); + } + + WEAPON_SUBASSET(overlay.shader, material, Material); + WEAPON_SUBASSET(overlay.shaderLowRes, material, Material); + WEAPON_SUBASSET(overlay.shaderEMP, material, Material); + WEAPON_SUBASSET(overlay.shaderEMPLowRes, material, Material); + + WEAPON_SUBASSET(physCollmap, phys_collmap, PhysCollmap); + + WEAPON_SUBASSET(projectileModel, xmodel, XModel); + + WEAPON_SUBASSET(projExplosionEffect, fx, FxEffectDef); + WEAPON_SUBASSET(projDudEffect, fx, FxEffectDef); + + WEAPON_SUBASSET(projExplosionSound, sound, snd_alias_list_t); + WEAPON_SUBASSET(projDudSound, sound, snd_alias_list_t); + + if (data->parallelBounce) + { + buf->align(3); + buf->write(data->parallelBounce, 124); + ZoneBuffer::clear_pointer(&dest->parallelBounce); + } + + if (data->perpendicularBounce) + { + buf->align(3); + buf->write(data->perpendicularBounce, 124); + ZoneBuffer::clear_pointer(&dest->perpendicularBounce); + } + + WEAPON_SUBASSET(projTrailEffect, fx, FxEffectDef); + WEAPON_SUBASSET(projBeaconEffect, fx, FxEffectDef); + WEAPON_SUBASSET(projIgnitionEffect, fx, FxEffectDef); + + WEAPON_SUBASSET(projIgnitionSound, sound, snd_alias_list_t); + + if (data->accuracyGraphName[0]) + { + dest->accuracyGraphName[0] = buf->write_str(data->accuracyGraphName[0]); + } + + if (data->accuracyGraphKnots) + { + buf->align(3); + buf->write(data->accuracyGraphKnots, data->accuracyGraphKnotCount); + ZoneBuffer::clear_pointer(&dest->accuracyGraphKnots); + } + + if (data->accuracyGraphName[1]) + { + dest->accuracyGraphName[1] = buf->write_str(data->accuracyGraphName[1]); + } + + if (data->originalAccuracyGraphKnots) + { + buf->align(3); + buf->write(data->originalAccuracyGraphKnots, data->originalAccuracyGraphKnotCount); + ZoneBuffer::clear_pointer(&dest->originalAccuracyGraphKnots); + } + + if (data->szUseHintString) + { + dest->szUseHintString = buf->write_str(data->szUseHintString); + } + + if (data->dropHintString) + { + dest->dropHintString = buf->write_str(data->dropHintString); + } + + if (data->szScript) + { + dest->szScript = buf->write_str(data->szScript); + } + + if (data->locationDamageMultipliers) + { + buf->align(3); + buf->write(data->locationDamageMultipliers, 20); + ZoneBuffer::clear_pointer(&dest->locationDamageMultipliers); + } + + if (data->fireRumble) + { + dest->fireRumble = buf->write_str(data->fireRumble); + } + + if (data->meleeImpactRumble) + { + dest->meleeImpactRumble = buf->write_str(data->meleeImpactRumble); + } + + WEAPON_SUBASSET(tracerType, tracer, TracerDef); + + WEAPON_SUBASSET(turretOverheatSound, sound, snd_alias_list_t); + WEAPON_SUBASSET(turretOverheatEffect, fx, FxEffectDef); + + if (data->turretBarrelSpinRumble) + { + dest->turretBarrelSpinRumble = buf->write_str(data->turretBarrelSpinRumble); + } + + WEAPON_SUBASSET(turretBarrelSpinMaxSnd, sound, snd_alias_list_t); + + for (int i = 0; i < 4; i++) + { + WEAPON_SUBASSET(turretBarrelSpinUpSnd[i], sound, snd_alias_list_t); + WEAPON_SUBASSET(turretBarrelSpinDownSnd[i], sound, snd_alias_list_t); + } + + WEAPON_SUBASSET(missileConeSoundAlias, sound, snd_alias_list_t); + WEAPON_SUBASSET(missileConeSoundAliasAtBase, sound, snd_alias_list_t); + } + + void IWeaponDef::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->szInternalName = buf->write_str(this->name()); + + if (data->weapDef) + { + buf->align(3); + write_WeaponDef(zone, buf, data, data->weapDef); + ZoneBuffer::clear_pointer(&dest->weapDef); + } + + if (data->szDisplayName) + { + dest->szDisplayName = buf->write_str(data->szDisplayName); + } + + if (data->hideTags) + { + buf->align(1); + buf->write(data->hideTags, 32); + ZoneBuffer::clear_pointer(&dest->hideTags); + } + +#define WEAPON_ATTACHMENT(__field__,__max__) \ + if (data->__field__) \ + { \ + buf->align(3); \ + auto destAttachments = buf->write(data->__field__,__max__); \ +\ + for (auto i = 0u; i < __max__; i++) \ + { \ + if (destAttachments && destAttachments[i]) \ + { \ + destAttachments[i] = reinterpret_cast( \ + zone->get_asset_pointer(attachment, destAttachments[i]->szInternalName) \ + ); \ + } \ + } \ + \ + ZoneBuffer::clear_pointer(&dest->__field__); \ + } + + WEAPON_ATTACHMENT(scopes, 6); + WEAPON_ATTACHMENT(underBarrels, 3); + WEAPON_ATTACHMENT(others, 4); + + if (data->szXAnims) + { + buf->align(3); + auto destStrings = buf->write(data->szXAnims, 42); + + for (auto i = 0u; i < 42; i++) + { + if (destStrings[i]) + { + destStrings[i] = buf->write_str(destStrings[i]); + } + } + + ZoneBuffer::clear_pointer(&dest->szXAnims); + } + + if (data->animOverrides) + { + buf->align(3); + auto destAnimOverrides = buf->write(data->animOverrides, data->numAnimOverrides); + + for (auto i = 0u; i < data->numAnimOverrides; i++) + { + if (destAnimOverrides[i].overrideAnim) + { + destAnimOverrides[i].overrideAnim = buf->write_str(destAnimOverrides[i].overrideAnim); + } + + if (destAnimOverrides[i].altmodeAnim) + { + destAnimOverrides[i].altmodeAnim = buf->write_str(destAnimOverrides[i].altmodeAnim); + } + } + + ZoneBuffer::clear_pointer(&dest->animOverrides); + } + + if (data->soundOverrides) + { + buf->align(3); + auto destSoundOverrides = buf->write(data->soundOverrides, data->numSoundOverrides); + + for (auto i = 0u; i < data->numSoundOverrides; i++) + { + if (destSoundOverrides[i].overrideSound) + { + auto ptr = -1; + buf->align(3); + buf->write(&ptr); + buf->write_str(destSoundOverrides[i].overrideSound->name); + ZoneBuffer::clear_pointer(&destSoundOverrides[i].overrideSound); + } + + if (destSoundOverrides[i].altmodeSound) + { + auto ptr = -1; + buf->align(3); + buf->write(&ptr); + buf->write_str(destSoundOverrides[i].altmodeSound->name); + ZoneBuffer::clear_pointer(&destSoundOverrides[i].altmodeSound); + } + } + + ZoneBuffer::clear_pointer(&dest->soundOverrides); + } + + if (data->fxOverrides) + { + buf->align(3); + auto destFxOverrides = buf->write(data->fxOverrides, data->numFXOverrides); + + for (auto i = 0u; i < data->numFXOverrides; i++) + { + if (destFxOverrides[i].overrideFX) + { + destFxOverrides[i].overrideFX = reinterpret_cast(zone->get_asset_pointer( + fx, destFxOverrides[i].overrideFX->name + )); + } + + if (destFxOverrides[i].altmodeFX) + { + destFxOverrides[i].altmodeFX = reinterpret_cast(zone->get_asset_pointer( + fx, destFxOverrides[i].altmodeFX->name + )); + } + } + + ZoneBuffer::clear_pointer(&dest->fxOverrides); + } + + if (data->reloadOverrides) + { + buf->align(3); + buf->write(data->reloadOverrides, data->numReloadStateTimerOverrides); + ZoneBuffer::clear_pointer(&dest->reloadOverrides); + } + + if (data->notetrackOverrides) + { + buf->align(3); + auto destNoteTrackOverrides = buf->write(data->notetrackOverrides, data->numNotetrackOverrides); + + for (auto i = 0u; i < data->numNotetrackOverrides; i++) + { + if (destNoteTrackOverrides[i].notetrackSoundMapKeys) + { + buf->align(1); + buf->write(destNoteTrackOverrides[i].notetrackSoundMapKeys, 24); + ZoneBuffer::clear_pointer(&destNoteTrackOverrides[i].notetrackSoundMapKeys); + } + + if (destNoteTrackOverrides[i].notetrackSoundMapValues) + { + buf->align(1); + buf->write(destNoteTrackOverrides[i].notetrackSoundMapValues, 24); + ZoneBuffer::clear_pointer(&destNoteTrackOverrides[i].notetrackSoundMapValues); + } + } + + ZoneBuffer::clear_pointer(&dest->notetrackOverrides); + } + + if (data->szAltWeaponName) + { + dest->szAltWeaponName = buf->write_str(data->szAltWeaponName); + } + + if (data->killIcon) + { + dest->killIcon = reinterpret_cast( + zone->get_asset_pointer(material, dest->killIcon->name) + ); + } + + if (data->dpadIcon) + { + dest->dpadIcon = reinterpret_cast( + zone->get_asset_pointer(material, dest->dpadIcon->name) + ); + } + + if (data->accuracyGraphKnots[0]) + { + buf->align(3); + buf->write(data->accuracyGraphKnots[0], data->accuracyGraphKnotCount[0]); + ZoneBuffer::clear_pointer(&dest->accuracyGraphKnots[0]); + } + + if (data->accuracyGraphKnots[1]) + { + buf->align(3); + buf->write(data->accuracyGraphKnots[1], data->accuracyGraphKnotCount[1]); + ZoneBuffer::clear_pointer(&dest->accuracyGraphKnots[1]); + } + + END_LOG_STREAM; + buf->pop_stream(); + + encrypt_data(dest, sizeof WeaponCompleteDef); + } + +#define WEAPON_DUMP_FIELD(__field__) \ + data[#__field__] = asset->__field__ + +#define WEAPON_DUMP_FIELD_ARR(__field__, __size__) \ + for (auto idx##__field__ = 0; idx##__field__ < __size__; idx##__field__++) \ + { \ + data[#__field__][idx##__field__] = asset->__field__[idx##__field__]; \ + } + +#define WEAPON_DUMP_FIELD_ARR_STR(__field__, __size__) \ + for (auto idx##__field__ = 0; idx##__field__ < __size__; idx##__field__++) \ + { \ + if (asset->__field__[idx##__field__] != nullptr) \ + data[#__field__][idx##__field__] = asset->__field__[idx##__field__]; \ + else \ + data[#__field__][idx##__field__] = ""; \ + } + +#define WEAPON_DUMP_ASSET(__field__) \ + if (asset->__field__) \ + { \ + data[#__field__] = asset->__field__->name; \ + } \ + else \ + { \ + data[#__field__] = ""; \ + } + + Json dump_overlay(ADSOverlay* asset) + { + Json data; + + WEAPON_DUMP_ASSET(shader); + WEAPON_DUMP_ASSET(shaderLowRes); + WEAPON_DUMP_ASSET(shaderEMP); + WEAPON_DUMP_ASSET(shaderEMPLowRes); + WEAPON_DUMP_FIELD(reticle); + WEAPON_DUMP_FIELD(width); + WEAPON_DUMP_FIELD(height); + WEAPON_DUMP_FIELD(widthSplitscreen); + WEAPON_DUMP_FIELD(heightSplitscreen); + + return data; + } + + Json dump_statetimers(StateTimers* asset) + { + Json data; + + WEAPON_DUMP_FIELD(iFireDelay); + WEAPON_DUMP_FIELD(iMeleeDelay); + WEAPON_DUMP_FIELD(meleeChargeDelay); + WEAPON_DUMP_FIELD(iDetonateDelay); + WEAPON_DUMP_FIELD(iRechamberTime); + WEAPON_DUMP_FIELD(rechamberTimeOneHanded); + WEAPON_DUMP_FIELD(iRechamberBoltTime); + WEAPON_DUMP_FIELD(iHoldFireTime); + WEAPON_DUMP_FIELD(iDetonateTime); + WEAPON_DUMP_FIELD(iMeleeTime); + WEAPON_DUMP_FIELD(meleeChargeTime); + WEAPON_DUMP_FIELD(iReloadTime); + WEAPON_DUMP_FIELD(reloadShowRocketTime); + WEAPON_DUMP_FIELD(iReloadEmptyTime); + WEAPON_DUMP_FIELD(iReloadAddTime); + WEAPON_DUMP_FIELD(iReloadStartTime); + WEAPON_DUMP_FIELD(iReloadStartAddTime); + WEAPON_DUMP_FIELD(iReloadEndTime); + WEAPON_DUMP_FIELD(iDropTime); + WEAPON_DUMP_FIELD(iRaiseTime); + WEAPON_DUMP_FIELD(iAltDropTime); + WEAPON_DUMP_FIELD(quickDropTime); + WEAPON_DUMP_FIELD(quickRaiseTime); + WEAPON_DUMP_FIELD(iBreachRaiseTime); + WEAPON_DUMP_FIELD(iEmptyRaiseTime); + WEAPON_DUMP_FIELD(iEmptyDropTime); + WEAPON_DUMP_FIELD(sprintInTime); + WEAPON_DUMP_FIELD(sprintLoopTime); + WEAPON_DUMP_FIELD(sprintOutTime); + WEAPON_DUMP_FIELD(stunnedTimeBegin); + WEAPON_DUMP_FIELD(stunnedTimeLoop); + WEAPON_DUMP_FIELD(stunnedTimeEnd); + WEAPON_DUMP_FIELD(nightVisionWearTime); + WEAPON_DUMP_FIELD(nightVisionWearTimeFadeOutEnd); + WEAPON_DUMP_FIELD(nightVisionWearTimePowerUp); + WEAPON_DUMP_FIELD(nightVisionRemoveTime); + WEAPON_DUMP_FIELD(nightVisionRemoveTimePowerDown); + WEAPON_DUMP_FIELD(nightVisionRemoveTimeFadeInStart); + WEAPON_DUMP_FIELD(fuseTime); + WEAPON_DUMP_FIELD(aiFuseTime); + WEAPON_DUMP_FIELD(blastFrontTime); + WEAPON_DUMP_FIELD(blastRightTime); + WEAPON_DUMP_FIELD(blastBackTime); + WEAPON_DUMP_FIELD(blastLeftTime); + WEAPON_DUMP_FIELD(raiseInterruptableTime); + WEAPON_DUMP_FIELD(firstRaiseInterruptableTime); + WEAPON_DUMP_FIELD(reloadInterruptableTime); + WEAPON_DUMP_FIELD(reloadEmptyInterruptableTime); + WEAPON_DUMP_FIELD(fireInterruptableTime); + + return data; + } + + Json dump_weapondef(WeaponDef* asset, const std::function& convertToString) + { + Json data; + + assert(sizeof(WeaponDef) == 1956); + + for (int i = 0; i < 16; i++) + { + if (asset->gunXModel && asset->gunXModel[i]) + { + data["gunXModel"][i] = asset->gunXModel[i]->name; + } + else + { + data["gunXModel"][i] = ""; + } + + if (asset->worldModel && asset->worldModel[i]) + { + data["worldXModel"][i] = asset->worldModel[i]->name; + } + else + { + data["worldXModel"][i] = ""; + } + } + + for (int i = 0; i < 24; i++) + { + if (asset->notetrackSoundMapKeys && asset->notetrackSoundMapKeys[i]) + { + data["notetrackSoundMapKeys"][i] = convertToString(asset->notetrackSoundMapKeys[i]); + } + else + { + data["notetrackSoundMapKeys"][i] = ""; + } + + if (asset->notetrackSoundMapValues && asset->notetrackSoundMapValues[i]) + { + data["notetrackSoundMapValues"][i] = convertToString(asset->notetrackSoundMapValues[i]); + } + else + { + data["notetrackSoundMapValues"][i] = ""; + } + } + + for (int i = 0; i < 16; i++) + { + if (asset->notetrackRumbleMapKeys && asset->notetrackRumbleMapKeys[i]) + { + data["notetrackRumbleMapKeys"][i] = convertToString(asset->notetrackRumbleMapKeys[i]); + } + else + { + data["notetrackRumbleMapKeys"][i] = ""; + } + + if (asset->notetrackRumbleMapValues && asset->notetrackRumbleMapValues[i]) + { + data["notetrackRumbleMapValues"][i] = convertToString(asset->notetrackRumbleMapValues[i]); + } + else + { + data["notetrackRumbleMapValues"][i] = ""; + } + } + + for (int i = 0; i < 48; i++) + { + if (asset->sounds && asset->sounds[i]) + { + data["sounds"][i] = asset->sounds[i]->name; + } + else + { + data["sounds"][i] = ""; + } + } + + for (int i = 0; i < 31; i++) + { + if (asset->bounceSound && asset->bounceSound[i]) + { + data["bounceSound"][i] = asset->bounceSound[i]->name; + } + else + { + data["bounceSound"][i] = ""; + } + + if (asset->rollingSound && asset->rollingSound[i]) + { + data["rollingSound"][i] = asset->rollingSound[i]->name; + } + else + { + data["rollingSound"][i] = ""; + } + } + + for (int i = 0; i < 4; i++) + { + if (asset->turretBarrelSpinUpSnd && asset->turretBarrelSpinUpSnd[i]) + { + data["turretBarrelSpinUpSnd"][i] = asset->turretBarrelSpinUpSnd[i]->name; + } + else + { + data["turretBarrelSpinUpSnd"][i] = ""; + } + + if (asset->turretBarrelSpinDownSnd && asset->turretBarrelSpinDownSnd[i]) + { + data["turretBarrelSpinDownSnd"][i] = asset->turretBarrelSpinDownSnd[i]->name; + } + else + { + data["turretBarrelSpinDownSnd"][i] = ""; + } + } + + + WEAPON_DUMP_FIELD(szOverlayName); + WEAPON_DUMP_ASSET(handXModel); + WEAPON_DUMP_FIELD(szModeName); + WEAPON_DUMP_FIELD(playerAnimType); + WEAPON_DUMP_FIELD(weapType); + WEAPON_DUMP_FIELD(weapClass); + WEAPON_DUMP_FIELD(penetrateType); + WEAPON_DUMP_FIELD(inventoryType); + WEAPON_DUMP_FIELD(fireType); + WEAPON_DUMP_FIELD(offhandClass); + WEAPON_DUMP_FIELD(stance); + WEAPON_DUMP_ASSET(viewFlashEffect); + WEAPON_DUMP_ASSET(worldFlashEffect); + WEAPON_DUMP_ASSET(viewShellEjectEffect); + WEAPON_DUMP_ASSET(worldShellEjectEffect); + WEAPON_DUMP_ASSET(viewLastShotEjectEffect); + WEAPON_DUMP_ASSET(worldLastShotEjectEffect); + WEAPON_DUMP_ASSET(reticleCenter); + WEAPON_DUMP_ASSET(reticleSide); + WEAPON_DUMP_FIELD(iReticleCenterSize); + WEAPON_DUMP_FIELD(iReticleSideSize); + WEAPON_DUMP_FIELD(iReticleMinOfs); + WEAPON_DUMP_FIELD(activeReticleType); + + WEAPON_DUMP_FIELD_ARR(vStandMove, 3); + WEAPON_DUMP_FIELD_ARR(vStandRot, 3); + WEAPON_DUMP_FIELD_ARR(strafeMove, 3); + WEAPON_DUMP_FIELD_ARR(strafeRot, 3); + WEAPON_DUMP_FIELD_ARR(vDuckedOfs, 3); + WEAPON_DUMP_FIELD_ARR(vDuckedMove, 3); + WEAPON_DUMP_FIELD_ARR(vDuckedRot, 3); + WEAPON_DUMP_FIELD_ARR(vProneOfs, 3); + WEAPON_DUMP_FIELD_ARR(vProneMove, 3); + WEAPON_DUMP_FIELD_ARR(vProneRot, 3); + + WEAPON_DUMP_FIELD(fPosMoveRate); + WEAPON_DUMP_FIELD(fPosProneMoveRate); + WEAPON_DUMP_FIELD(fStandMoveMinSpeed); + WEAPON_DUMP_FIELD(fDuckedMoveMinSpeed); + WEAPON_DUMP_FIELD(fProneMoveMinSpeed); + WEAPON_DUMP_FIELD(fPosRotRate); + WEAPON_DUMP_FIELD(fPosProneRotRate); + WEAPON_DUMP_FIELD(fStandRotMinSpeed); + WEAPON_DUMP_FIELD(fDuckedRotMinSpeed); + WEAPON_DUMP_FIELD(fProneRotMinSpeed); + WEAPON_DUMP_ASSET(worldClipModel); + WEAPON_DUMP_ASSET(rocketModel); + WEAPON_DUMP_ASSET(knifeModel); + WEAPON_DUMP_ASSET(worldKnifeModel); + WEAPON_DUMP_ASSET(hudIcon); + WEAPON_DUMP_FIELD(hudIconRatio); + WEAPON_DUMP_ASSET(pickupIcon); + WEAPON_DUMP_FIELD(pickupIconRatio); + WEAPON_DUMP_ASSET(ammoCounterIcon); + WEAPON_DUMP_FIELD(ammoCounterIconRatio); + WEAPON_DUMP_FIELD(ammoCounterClip); + WEAPON_DUMP_FIELD(iStartAmmo); + WEAPON_DUMP_FIELD(szAmmoName); + WEAPON_DUMP_FIELD(iAmmoIndex); + WEAPON_DUMP_FIELD(szClipName); + WEAPON_DUMP_FIELD(iClipIndex); + WEAPON_DUMP_FIELD(iMaxAmmo); + WEAPON_DUMP_FIELD(shotCount); + WEAPON_DUMP_FIELD(szSharedAmmoCapName); + WEAPON_DUMP_FIELD(iSharedAmmoCapIndex); + WEAPON_DUMP_FIELD(iSharedAmmoCap); + WEAPON_DUMP_FIELD(damage); + WEAPON_DUMP_FIELD(playerDamage); + WEAPON_DUMP_FIELD(iMeleeDamage); + WEAPON_DUMP_FIELD(iDamageType); + + data["stateTimers"] = dump_statetimers(&asset->stateTimers); + data["akimboStateTimers"] = dump_statetimers(&asset->akimboStateTimers); + + WEAPON_DUMP_FIELD(autoAimRange); + WEAPON_DUMP_FIELD(aimAssistRange); + WEAPON_DUMP_FIELD(aimAssistRangeAds); + WEAPON_DUMP_FIELD(aimPadding); + WEAPON_DUMP_FIELD(enemyCrosshairRange); + WEAPON_DUMP_FIELD(moveSpeedScale); + WEAPON_DUMP_FIELD(adsMoveSpeedScale); + WEAPON_DUMP_FIELD(sprintDurationScale); + WEAPON_DUMP_FIELD(fAdsZoomInFrac); + WEAPON_DUMP_FIELD(fAdsZoomOutFrac); + + data["overlay"] = dump_overlay(&asset->overlay); + + WEAPON_DUMP_FIELD(overlayInterface); + WEAPON_DUMP_FIELD(fAdsBobFactor); + WEAPON_DUMP_FIELD(fAdsViewBobMult); + WEAPON_DUMP_FIELD(fHipSpreadStandMin); + WEAPON_DUMP_FIELD(fHipSpreadDuckedMin); + WEAPON_DUMP_FIELD(fHipSpreadProneMin); + WEAPON_DUMP_FIELD(hipSpreadStandMax); + WEAPON_DUMP_FIELD(hipSpreadDuckedMax); + WEAPON_DUMP_FIELD(hipSpreadProneMax); + WEAPON_DUMP_FIELD(fHipSpreadDecayRate); + WEAPON_DUMP_FIELD(fHipSpreadFireAdd); + WEAPON_DUMP_FIELD(fHipSpreadTurnAdd); + WEAPON_DUMP_FIELD(fHipSpreadMoveAdd); + WEAPON_DUMP_FIELD(fHipSpreadDuckedDecay); + WEAPON_DUMP_FIELD(fHipSpreadProneDecay); + WEAPON_DUMP_FIELD(fHipReticleSidePos); + WEAPON_DUMP_FIELD(fAdsIdleAmount); + WEAPON_DUMP_FIELD(fHipIdleAmount); + WEAPON_DUMP_FIELD(adsIdleSpeed); + WEAPON_DUMP_FIELD(hipIdleSpeed); + WEAPON_DUMP_FIELD(fIdleCrouchFactor); + WEAPON_DUMP_FIELD(fIdleProneFactor); + WEAPON_DUMP_FIELD(fGunMaxPitch); + WEAPON_DUMP_FIELD(fGunMaxYaw); + WEAPON_DUMP_FIELD(adsIdleLerpStartTime); + WEAPON_DUMP_FIELD(adsIdleLerpTime); + WEAPON_DUMP_FIELD(swayMaxAngle); + WEAPON_DUMP_FIELD(swayLerpSpeed); + WEAPON_DUMP_FIELD(swayPitchScale); + WEAPON_DUMP_FIELD(swayYawScale); + WEAPON_DUMP_FIELD(swayHorizScale); + WEAPON_DUMP_FIELD(swayVertScale); + WEAPON_DUMP_FIELD(swayShellShockScale); + WEAPON_DUMP_FIELD(adsSwayMaxAngle); + WEAPON_DUMP_FIELD(adsSwayLerpSpeed); + WEAPON_DUMP_FIELD(adsSwayPitchScale); + WEAPON_DUMP_FIELD(adsSwayYawScale); + WEAPON_DUMP_FIELD(adsSwayHorizScale); + WEAPON_DUMP_FIELD(adsSwayVertScale); + WEAPON_DUMP_FIELD(adsViewErrorMin); + WEAPON_DUMP_FIELD(adsViewErrorMax); + WEAPON_DUMP_ASSET(physCollmap); + WEAPON_DUMP_FIELD(dualWieldViewModelOffset); + WEAPON_DUMP_FIELD(killIconRatio); + WEAPON_DUMP_FIELD(iReloadAmmoAdd); + WEAPON_DUMP_FIELD(iReloadStartAdd); + WEAPON_DUMP_FIELD(ammoDropStockMin); + WEAPON_DUMP_FIELD(ammoDropClipPercentMin); + WEAPON_DUMP_FIELD(ammoDropClipPercentMax); + WEAPON_DUMP_FIELD(iExplosionRadius); + WEAPON_DUMP_FIELD(iExplosionRadiusMin); + WEAPON_DUMP_FIELD(iExplosionInnerDamage); + WEAPON_DUMP_FIELD(iExplosionOuterDamage); + WEAPON_DUMP_FIELD(damageConeAngle); + WEAPON_DUMP_FIELD(bulletExplDmgMult); + WEAPON_DUMP_FIELD(bulletExplRadiusMult); + WEAPON_DUMP_FIELD(iProjectileSpeed); + WEAPON_DUMP_FIELD(iProjectileSpeedUp); + WEAPON_DUMP_FIELD(iProjectileSpeedForward); + WEAPON_DUMP_FIELD(iProjectileActivateDist); + WEAPON_DUMP_FIELD(projLifetime); + WEAPON_DUMP_FIELD(timeToAccelerate); + WEAPON_DUMP_FIELD(projectileCurvature); + WEAPON_DUMP_ASSET(projectileModel); + WEAPON_DUMP_FIELD(projExplosion); + WEAPON_DUMP_ASSET(projExplosionEffect); + WEAPON_DUMP_ASSET(projDudEffect); + WEAPON_DUMP_ASSET(projExplosionSound); + WEAPON_DUMP_ASSET(projDudSound); + WEAPON_DUMP_FIELD(stickiness); + WEAPON_DUMP_FIELD(lowAmmoWarningThreshold); + WEAPON_DUMP_FIELD(ricochetChance); + WEAPON_DUMP_FIELD(riotShieldEnableDamage); + WEAPON_DUMP_FIELD(riotShieldHealth); + WEAPON_DUMP_FIELD(riotShieldDamageMult); + + WEAPON_DUMP_ASSET(projTrailEffect); + WEAPON_DUMP_ASSET(projBeaconEffect); + + WEAPON_DUMP_FIELD_ARR(vProjectileColor, 3); + WEAPON_DUMP_FIELD(guidedMissileType); + WEAPON_DUMP_FIELD(maxSteeringAccel); + WEAPON_DUMP_FIELD(projIgnitionDelay); + WEAPON_DUMP_ASSET(projIgnitionEffect); + WEAPON_DUMP_ASSET(projIgnitionSound); + WEAPON_DUMP_FIELD(fAdsAimPitch); + WEAPON_DUMP_FIELD(fAdsCrosshairInFrac); + WEAPON_DUMP_FIELD(fAdsCrosshairOutFrac); + WEAPON_DUMP_FIELD(adsGunKickReducedKickBullets); + WEAPON_DUMP_FIELD(adsGunKickReducedKickPercent); + WEAPON_DUMP_FIELD(fAdsGunKickPitchMin); + WEAPON_DUMP_FIELD(fAdsGunKickPitchMax); + WEAPON_DUMP_FIELD(fAdsGunKickYawMin); + WEAPON_DUMP_FIELD(fAdsGunKickYawMax); + WEAPON_DUMP_FIELD(fAdsGunKickAccel); + WEAPON_DUMP_FIELD(fAdsGunKickSpeedMax); + WEAPON_DUMP_FIELD(fAdsGunKickSpeedDecay); + WEAPON_DUMP_FIELD(fAdsGunKickStaticDecay); + WEAPON_DUMP_FIELD(fAdsViewKickPitchMin); + WEAPON_DUMP_FIELD(fAdsViewKickPitchMax); + WEAPON_DUMP_FIELD(fAdsViewKickYawMin); + WEAPON_DUMP_FIELD(fAdsViewKickYawMax); + WEAPON_DUMP_FIELD(fAdsViewScatterMin); + WEAPON_DUMP_FIELD(fAdsViewScatterMax); + WEAPON_DUMP_FIELD(fAdsSpread); + WEAPON_DUMP_FIELD(hipGunKickReducedKickBullets); + WEAPON_DUMP_FIELD(hipGunKickReducedKickPercent); + WEAPON_DUMP_FIELD(fHipGunKickPitchMin); + WEAPON_DUMP_FIELD(fHipGunKickPitchMax); + WEAPON_DUMP_FIELD(fHipGunKickYawMin); + WEAPON_DUMP_FIELD(fHipGunKickYawMax); + WEAPON_DUMP_FIELD(fHipGunKickAccel); + WEAPON_DUMP_FIELD(fHipGunKickSpeedMax); + WEAPON_DUMP_FIELD(fHipGunKickSpeedDecay); + WEAPON_DUMP_FIELD(fHipGunKickStaticDecay); + WEAPON_DUMP_FIELD(fHipViewKickPitchMin); + WEAPON_DUMP_FIELD(fHipViewKickPitchMax); + WEAPON_DUMP_FIELD(fHipViewKickYawMin); + WEAPON_DUMP_FIELD(fHipViewKickYawMax); + WEAPON_DUMP_FIELD(fHipViewScatterMin); + WEAPON_DUMP_FIELD(fHipViewScatterMax); + WEAPON_DUMP_FIELD(fightDist); + WEAPON_DUMP_FIELD(maxDist); + WEAPON_DUMP_FIELD_ARR_STR(accuracyGraphName, 2); + // vec2_t* accuracyGraphKnots); + // vec2_t* originalAccuracyGraphKnots); + WEAPON_DUMP_FIELD(accuracyGraphKnotCount); + WEAPON_DUMP_FIELD(originalAccuracyGraphKnotCount); + WEAPON_DUMP_FIELD(iPositionReloadTransTime); + WEAPON_DUMP_FIELD(leftArc); + WEAPON_DUMP_FIELD(rightArc); + WEAPON_DUMP_FIELD(topArc); + WEAPON_DUMP_FIELD(bottomArc); + WEAPON_DUMP_FIELD(accuracy); + WEAPON_DUMP_FIELD(aiSpread); + WEAPON_DUMP_FIELD(playerSpread); + WEAPON_DUMP_FIELD_ARR(minTurnSpeed, 2); + WEAPON_DUMP_FIELD_ARR(maxTurnSpeed, 2); + WEAPON_DUMP_FIELD(pitchConvergenceTime); + WEAPON_DUMP_FIELD(yawConvergenceTime); + WEAPON_DUMP_FIELD(suppressTime); + WEAPON_DUMP_FIELD(maxRange); + WEAPON_DUMP_FIELD(fAnimHorRotateInc); + WEAPON_DUMP_FIELD(fPlayerPositionDist); + WEAPON_DUMP_FIELD(szUseHintString); + WEAPON_DUMP_FIELD(dropHintString); + WEAPON_DUMP_FIELD(iUseHintStringIndex); + WEAPON_DUMP_FIELD(dropHintStringIndex); + WEAPON_DUMP_FIELD(horizViewJitter); + WEAPON_DUMP_FIELD(vertViewJitter); + WEAPON_DUMP_FIELD(scanSpeed); + WEAPON_DUMP_FIELD(scanAccel); + WEAPON_DUMP_FIELD(scanPauseTime); + WEAPON_DUMP_FIELD(szScript); + WEAPON_DUMP_FIELD_ARR(fOOPosAnimLength, 2); + WEAPON_DUMP_FIELD(minDamage); + WEAPON_DUMP_FIELD(minPlayerDamage); + WEAPON_DUMP_FIELD(fMaxDamageRange); + WEAPON_DUMP_FIELD(fMinDamageRange); + WEAPON_DUMP_FIELD(destabilizationRateTime); + WEAPON_DUMP_FIELD(destabilizationCurvatureMax); + WEAPON_DUMP_FIELD(destabilizeDistance); + WEAPON_DUMP_FIELD_ARR(locationDamageMultipliers, 20); + WEAPON_DUMP_FIELD(fireRumble); + WEAPON_DUMP_FIELD(meleeImpactRumble); + WEAPON_DUMP_ASSET(tracerType); + WEAPON_DUMP_FIELD(turretADSEnabled); + WEAPON_DUMP_FIELD(turretADSTime); + WEAPON_DUMP_FIELD(turretFov); + WEAPON_DUMP_FIELD(turretFovADS); + WEAPON_DUMP_FIELD(turretScopeZoomRate); + WEAPON_DUMP_FIELD(turretScopeZoomMin); + WEAPON_DUMP_FIELD(turretScopeZoomMax); + WEAPON_DUMP_FIELD(turretOverheatUpRate); + WEAPON_DUMP_FIELD(turretOverheatDownRate); + WEAPON_DUMP_FIELD(turretOverheatPenalty); + WEAPON_DUMP_ASSET(turretOverheatSound); + WEAPON_DUMP_ASSET(turretOverheatEffect); + WEAPON_DUMP_FIELD(turretBarrelSpinRumble); + WEAPON_DUMP_FIELD(turretBarrelSpinSpeed); + WEAPON_DUMP_FIELD(turretBarrelSpinUpTime); + WEAPON_DUMP_FIELD(turretBarrelSpinDownTime); + WEAPON_DUMP_ASSET(turretBarrelSpinMaxSnd); + WEAPON_DUMP_ASSET(missileConeSoundAlias); + WEAPON_DUMP_ASSET(missileConeSoundAliasAtBase); + WEAPON_DUMP_FIELD(missileConeSoundRadiusAtTop); + WEAPON_DUMP_FIELD(missileConeSoundRadiusAtBase); + WEAPON_DUMP_FIELD(missileConeSoundHeight); + WEAPON_DUMP_FIELD(missileConeSoundOriginOffset); + WEAPON_DUMP_FIELD(missileConeSoundVolumescaleAtCore); + WEAPON_DUMP_FIELD(missileConeSoundVolumescaleAtEdge); + WEAPON_DUMP_FIELD(missileConeSoundVolumescaleCoreSize); + WEAPON_DUMP_FIELD(missileConeSoundPitchAtTop); + WEAPON_DUMP_FIELD(missileConeSoundPitchAtBottom); + WEAPON_DUMP_FIELD(missileConeSoundPitchTopSize); + WEAPON_DUMP_FIELD(missileConeSoundPitchBottomSize); + WEAPON_DUMP_FIELD(missileConeSoundCrossfadeTopSize); + WEAPON_DUMP_FIELD(missileConeSoundCrossfadeBottomSize); + WEAPON_DUMP_FIELD(sharedAmmo); + WEAPON_DUMP_FIELD(lockonSupported); + WEAPON_DUMP_FIELD(requireLockonToFire); + WEAPON_DUMP_FIELD(isAirburstWeapon); + WEAPON_DUMP_FIELD(bigExplosion); + WEAPON_DUMP_FIELD(noAdsWhenMagEmpty); + WEAPON_DUMP_FIELD(avoidDropCleanup); + WEAPON_DUMP_FIELD(inheritsPerks); + WEAPON_DUMP_FIELD(crosshairColorChange); + WEAPON_DUMP_FIELD(bRifleBullet); + WEAPON_DUMP_FIELD(armorPiercing); + WEAPON_DUMP_FIELD(bBoltAction); + WEAPON_DUMP_FIELD(aimDownSight); + WEAPON_DUMP_FIELD(canHoldBreath); + WEAPON_DUMP_FIELD(canVariableZoom); + WEAPON_DUMP_FIELD(bRechamberWhileAds); + WEAPON_DUMP_FIELD(bBulletExplosiveDamage); + WEAPON_DUMP_FIELD(bCookOffHold); + WEAPON_DUMP_FIELD(bClipOnly); + WEAPON_DUMP_FIELD(noAmmoPickup); + WEAPON_DUMP_FIELD(adsFireOnly); + WEAPON_DUMP_FIELD(cancelAutoHolsterWhenEmpty); + WEAPON_DUMP_FIELD(disableSwitchToWhenEmpty); + WEAPON_DUMP_FIELD(suppressAmmoReserveDisplay); + WEAPON_DUMP_FIELD(laserSightDuringNightvision); + WEAPON_DUMP_FIELD(markableViewmodel); + WEAPON_DUMP_FIELD(noDualWield); + WEAPON_DUMP_FIELD(flipKillIcon); + WEAPON_DUMP_FIELD(bNoPartialReload); + WEAPON_DUMP_FIELD(bSegmentedReload); + WEAPON_DUMP_FIELD(blocksProne); + WEAPON_DUMP_FIELD(silenced); + WEAPON_DUMP_FIELD(isRollingGrenade); + WEAPON_DUMP_FIELD(projExplosionEffectForceNormalUp); + WEAPON_DUMP_FIELD(bProjImpactExplode); + WEAPON_DUMP_FIELD(stickToPlayers); + WEAPON_DUMP_FIELD(stickToVehicles); + WEAPON_DUMP_FIELD(stickToTurrets); + WEAPON_DUMP_FIELD(hasDetonator); + WEAPON_DUMP_FIELD(disableFiring); + WEAPON_DUMP_FIELD(timedDetonation); + WEAPON_DUMP_FIELD(rotate); + WEAPON_DUMP_FIELD(holdButtonToThrow); + WEAPON_DUMP_FIELD(freezeMovementWhenFiring); + WEAPON_DUMP_FIELD(thermalScope); + WEAPON_DUMP_FIELD(altModeSameWeapon); + WEAPON_DUMP_FIELD(turretBarrelSpinEnabled); + WEAPON_DUMP_FIELD(missileConeSoundEnabled); + WEAPON_DUMP_FIELD(missileConeSoundPitchshiftEnabled); + WEAPON_DUMP_FIELD(missileConeSoundCrossfadeEnabled); + WEAPON_DUMP_FIELD(offhandHoldIsCancelable); + WEAPON_DUMP_FIELD(doNotAllowAttachmentsToOverrideSpread); + data["stowTag"] = convertToString(asset->stowTag); + WEAPON_DUMP_ASSET(stowOffsetModel); + + return data; + } + + Json dump_complete(WeaponCompleteDef* asset, + const std::function& convertToString) + { + Json data; + data["baseAsset"] = asset->szInternalName; + data["weapDef"] = dump_weapondef(asset->weapDef, convertToString); + + for (int i = 0; i < 32; i++) + { + if (asset->hideTags && asset->hideTags[i]) + { + data["hideTags"][i] = convertToString(asset->hideTags[i]); + } + else + { + data["hideTags"][i] = ""; + } + } + + for (int i = 0; i < 6; i++) + { + if (asset->scopes && asset->scopes[i]) + { + data["scopes"][i] = asset->scopes[i]->szInternalName; + } + else + { + data["scopes"][i] = ""; + } + + if (i >= 4) continue; + + if (asset->others && asset->others[i]) + { + data["others"][i] = asset->others[i]->szInternalName; + } + else + { + data["others"][i] = ""; + } + + if (i >= 3) continue; + + if (asset->underBarrels && asset->underBarrels[i]) + { + data["underBarrels"][i] = asset->underBarrels[i]->szInternalName; + } + else + { + data["underBarrels"][i] = ""; + } + } + + for (int i = 0; i < 42; i++) + { + if (asset->szXAnims && asset->szXAnims[i]) + { + data["szXAnims"][i] = asset->szXAnims[i]; + } + else + { + data["szXAnims"][i] = ""; + } + } + + for (int i = 0; i < asset->numAnimOverrides; i++) + { + data["animOverrides"][i]["altmodeAnim"] = (asset->animOverrides[i].altmodeAnim) + ? asset->animOverrides[i].altmodeAnim + : ""; + data["animOverrides"][i]["overrideAnim"] = (asset->animOverrides[i].overrideAnim) + ? asset->animOverrides[i].overrideAnim + : ""; + data["animOverrides"][i]["attachment1"] = asset->animOverrides[i].attachment1; + data["animOverrides"][i]["attachment2"] = asset->animOverrides[i].attachment2; + data["animOverrides"][i]["altTime"] = asset->animOverrides[i].altTime; + data["animOverrides"][i]["animTime"] = asset->animOverrides[i].animTime; + data["animOverrides"][i]["animTreeType"] = asset->animOverrides[i].animTreeType; + } + + for (int i = 0; i < asset->numSoundOverrides; i++) + { + data["soundOverrides"][i]["altmodeSound"] = (asset->soundOverrides[i].altmodeSound) + ? asset->soundOverrides[i].altmodeSound->name + : ""; + data["soundOverrides"][i]["attachment1"] = asset->soundOverrides[i].attachment1; + data["soundOverrides"][i]["attachment2"] = asset->soundOverrides[i].attachment2; + data["soundOverrides"][i]["overrideSound"] = (asset->soundOverrides[i].overrideSound) + ? asset->soundOverrides[i].overrideSound->name + : ""; + data["soundOverrides"][i]["soundType"] = asset->soundOverrides[i].soundType; + } + + for (int i = 0; i < asset->numFXOverrides; i++) + { + data["fxOverrides"][i]["altmodeFX"] = (asset->fxOverrides[i].altmodeFX) + ? asset->fxOverrides[i].altmodeFX->name + : ""; + data["fxOverrides"][i]["attachment1"] = asset->fxOverrides[i].attachment1; + data["fxOverrides"][i]["attachment2"] = asset->fxOverrides[i].attachment2; + data["fxOverrides"][i]["fxType"] = asset->fxOverrides[i].fxType; + data["fxOverrides"][i]["overrideFX"] = (asset->fxOverrides[i].overrideFX) + ? asset->fxOverrides[i].overrideFX->name + : ""; + } + + for (int i = 0; i < asset->numReloadStateTimerOverrides; i++) + { + data["reloadOverrides"][i]["attachment"] = asset->reloadOverrides[i].attachment; + data["reloadOverrides"][i]["reloadAddTime"] = asset->reloadOverrides[i].reloadAddTime; + data["reloadOverrides"][i]["reloadEmptyAddTime"] = asset->reloadOverrides[i].reloadEmptyAddTime; + } + + for (int i = 0; i < asset->numNotetrackOverrides; i++) + { + data["notetrackOverrides"][i]["attachment"] = asset->notetrackOverrides[i].attachment; + + for (int j = 0; j < 24; j++) + { + data["notetrackOverrides"][i]["notetrackSoundMapKeys"][j] = convertToString( + asset->notetrackOverrides[i].notetrackSoundMapKeys[j]); + data["notetrackOverrides"][i]["notetrackSoundMapValues"][j] = convertToString( + asset->notetrackOverrides[i].notetrackSoundMapValues[j]); + } + } + + WEAPON_DUMP_FIELD(szInternalName); + WEAPON_DUMP_FIELD(szDisplayName); + WEAPON_DUMP_FIELD(numAnimOverrides); + WEAPON_DUMP_FIELD(numSoundOverrides); + WEAPON_DUMP_FIELD(numFXOverrides); + WEAPON_DUMP_FIELD(numReloadStateTimerOverrides); + WEAPON_DUMP_FIELD(numNotetrackOverrides); + WEAPON_DUMP_FIELD(fAdsZoomFov); + WEAPON_DUMP_FIELD(iAdsTransInTime); + WEAPON_DUMP_FIELD(iAdsTransOutTime); + WEAPON_DUMP_FIELD(iClipSize); + WEAPON_DUMP_FIELD(impactType); + WEAPON_DUMP_FIELD(iFireTime); + WEAPON_DUMP_FIELD(iFireTimeAkimbo); + WEAPON_DUMP_FIELD(dpadIconRatio); + WEAPON_DUMP_FIELD(penetrateMultiplier); + WEAPON_DUMP_FIELD(fAdsViewKickCenterSpeed); + WEAPON_DUMP_FIELD(fHipViewKickCenterSpeed); + WEAPON_DUMP_FIELD(szAltWeaponName); + WEAPON_DUMP_FIELD(altWeapon); + WEAPON_DUMP_FIELD(iAltRaiseTime); + WEAPON_DUMP_FIELD(iAltRaiseTimeAkimbo); + WEAPON_DUMP_ASSET(killIcon); + WEAPON_DUMP_ASSET(dpadIcon); + WEAPON_DUMP_FIELD(fireAnimLength); + WEAPON_DUMP_FIELD(fireAnimLengthAkimbo); + WEAPON_DUMP_FIELD(iFirstRaiseTime); + WEAPON_DUMP_FIELD(iFirstRaiseTimeAkimbo); + WEAPON_DUMP_FIELD(ammoDropStockMax); + WEAPON_DUMP_FIELD(adsDofStart); + WEAPON_DUMP_FIELD(adsDofEnd); + WEAPON_DUMP_FIELD_ARR(accuracyGraphKnotCount, 2); + // float(*accuracyGraphKnots[2])[2]); + WEAPON_DUMP_FIELD(motionTracker); + WEAPON_DUMP_FIELD(enhanced); + WEAPON_DUMP_FIELD(dpadIconShowsAmmo); + + return data; + } + + void IWeaponDef::dump(WeaponCompleteDef* asset, const std::function& convertToString) + { + std::string path = "weapons/mp/"s + asset->szInternalName; + std::string json = dump_complete(asset, convertToString).dump(4); + + auto file = FileSystem::FileOpen(path, "w"s); + fwrite(json.data(), json.size(), 1, file); + FileSystem::FileClose(file); + } + } +} diff --git a/src/IW5/Assets/WeaponDef.hpp b/src/IW5/Assets/WeaponDef.hpp new file mode 100644 index 0000000..211c7d8 --- /dev/null +++ b/src/IW5/Assets/WeaponDef.hpp @@ -0,0 +1,41 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + extern const char* SL_ConvertToString(std::uint16_t index); + + class IWeaponDef : public IAsset + { + private: + std::string name_; + WeaponCompleteDef* asset_ = nullptr; + + public: + WeaponCompleteDef* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + + void load_depending_WeaponDef(IZone* zone, WeaponDef* data); + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + void write_WeaponDef(IZone* zone, ZoneBuffer* buf, WeaponCompleteDef* complete, + WeaponDef* data); + static void dump(WeaponCompleteDef* asset, + const std::function& convertToString = SL_ConvertToString); + }; + } +} diff --git a/src/IW5/Assets/XAnimParts.cpp b/src/IW5/Assets/XAnimParts.cpp new file mode 100644 index 0000000..7779bba --- /dev/null +++ b/src/IW5/Assets/XAnimParts.cpp @@ -0,0 +1,646 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + XAnimParts* IXAnimParts::parse_xae2(const std::string& name, ZoneMemory* mem, const std::function& allocString) + { + const auto path = "XAnim\\"s + name + ".xae2"; + + AssetReader reader(mem); + if (!reader.open(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing XAE2 file \"%s\"...", name.c_str()); + ZONETOOL_WARNING("You are using an outdated animation! Redump the animations using a newer version of zonetool!"); + + auto* asset = reader.read_single(); + asset->name = reader.read_string(); + + asset->tagnames = mem->Alloc(asset->boneCount[9]); + for (auto bone = 0; bone < asset->boneCount[9]; bone++) + { + asset->tagnames[bone] = allocString(reader.read_string()); + } + + if (asset->dataByte) + { + asset->dataByte = reader.read_array(); + } + if (asset->dataShort) + { + asset->dataShort = reader.read_array(); + } + if (asset->dataInt) + { + asset->dataInt = reader.read_array(); + } + if (asset->randomDataShort) + { + asset->randomDataShort = reader.read_array(); + } + if (asset->randomDataByte) + { + asset->randomDataByte = reader.read_array(); + } + if (asset->randomDataInt) + { + asset->randomDataInt = reader.read_array(); + } + if (asset->indices.data) + { + if (asset->framecount > 255) + { + asset->indices._2 = reader.read_array(); + } + else + { + asset->indices._1 = reader.read_array(); + } + } + + if (asset->notetracks) + { + asset->notetracks = reader.read_array(); + + for (auto i = 0; i < asset->notetrackCount; i++) + { + asset->notetracks[i].name = allocString(reader.read_string()); + } + } + + if (asset->delta) + { + asset->delta = nullptr; + } + + reader.close(); + + return asset; + } + + XAnimParts* IXAnimParts::parse_xae3(const std::string& name, ZoneMemory* mem, const std::function& allocString) + { + const auto path = "XAnim\\"s + name + ".xae3"; + + AssetReader reader(mem); + if (!reader.open(path)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing XAE3 file \"%s\"...", name.c_str()); + + auto* asset = reader.read_single(); + asset->name = reader.read_string(); + + asset->tagnames = mem->Alloc(asset->boneCount[9]); + for (auto bone = 0; bone < asset->boneCount[9]; bone++) + { + asset->tagnames[bone] = allocString(reader.read_string()); + } + + if (asset->dataByte) + { + asset->dataByte = reader.read_array(); + } + if (asset->dataShort) + { + asset->dataShort = reader.read_array(); + } + if (asset->dataInt) + { + asset->dataInt = reader.read_array(); + } + if (asset->randomDataShort) + { + asset->randomDataShort = reader.read_array(); + } + if (asset->randomDataByte) + { + asset->randomDataByte = reader.read_array(); + } + if (asset->randomDataInt) + { + asset->randomDataInt = reader.read_array(); + } + if (asset->indices.data) + { + if (asset->framecount > 255) + { + asset->indices._2 = reader.read_array(); + } + else + { + asset->indices._1 = reader.read_array(); + } + } + + if (asset->notetracks) + { + asset->notetracks = reader.read_array(); + + for (auto i = 0; i < asset->notetrackCount; i++) + { + asset->notetracks[i].name = allocString(reader.read_string()); + } + } + + if (asset->delta) + { + asset->delta = reader.read_single(); + + if (asset->delta->trans) + { + asset->delta->trans = reader.read_raw(); + + if (asset->delta->trans->size) + { + if (asset->delta->trans->u.frames.frames._1) + { + if (asset->delta->trans->smallTrans) + { + asset->delta->trans->u.frames.frames._1 = reader.read_raw(); + } + else + { + asset->delta->trans->u.frames.frames._2 = reader.read_raw(); + } + } + } + } + + if (asset->delta->quat2) + { + asset->delta->quat2 = reader.read_raw(); + + if (asset->delta->quat2->size && asset->delta->quat2->u.frames.frames) + { + asset->delta->quat2->u.frames.frames = reader.read_raw(); + } + } + + if (asset->delta->quat) + { + asset->delta->quat = reader.read_raw(); + + if (asset->delta->quat->size && asset->delta->quat->u.frames.frames) + { + asset->delta->quat->u.frames.frames = reader.read_raw(); + } + } + } + + reader.close(); + + return asset; + } + + XAnimParts* IXAnimParts::parse(const std::string& name, ZoneMemory* mem, const std::function& allocString) + { + auto* parsed = IXAnimParts::parse_xae3(name, mem, allocString); + + if (!parsed) + { + parsed = IXAnimParts::parse_xae2(name, mem, allocString); + } + + return parsed; + } + + void IXAnimParts::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = IXAnimParts::parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data(), 1).xanimparts; + } + } + + void IXAnimParts::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + // fixup scriptstrings + auto xanim = mem->Alloc(); + memcpy(xanim, this->asset_, sizeof XAnimParts); + + // allocate tagnames + if (this->asset_->tagnames) + { + xanim->tagnames = mem->Alloc(xanim->boneCount[9]); + memcpy(xanim->tagnames, this->asset_->tagnames, sizeof(unsigned short) * xanim->boneCount[9]); + + for (int i = 0; i < xanim->boneCount[9]; i++) + { + if (xanim->tagnames[i]) + { + std::string bone = SL_ConvertToString(this->asset_->tagnames[i]); + xanim->tagnames[i] = buf->write_scriptstring(bone); + } + } + } + + // allocate notetracks + xanim->notetracks = mem->Alloc(xanim->notetrackCount); + memcpy(xanim->notetracks, this->asset_->notetracks, sizeof XAnimNotifyInfo * xanim->notetrackCount); + + // touch XAnimNotifyInfo, realloc tagnames + for (int i = 0; i < xanim->notetrackCount; i++) + { + std::string bone = SL_ConvertToString(this->asset_->notetracks[i].name); + xanim->notetracks[i].name = buf->write_scriptstring(bone); + } + + this->asset_ = xanim; + } + + void IXAnimParts::load_depending(IZone* zone) + { + } + + std::string IXAnimParts::name() + { + return this->name_; + } + + std::int32_t IXAnimParts::type() + { + return xanim; + } + + void IXAnimParts::write(IZone* zone, ZoneBuffer* buf) + { + auto* data = this->asset_; + auto* dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->tagnames) // tagnames + { + buf->align(1); + buf->write_stream(data->tagnames, sizeof(short), data->boneCount[9]); + dest->tagnames = reinterpret_cast(-1); + } + if (data->notetracks) // notetracks + { + buf->align(3); + buf->write_stream(data->notetracks, sizeof(XAnimNotifyInfo), data->notetrackCount); + dest->tagnames = reinterpret_cast(-1); + } + + if (data->delta) // XAnimDeltaParts + { + buf->align(3); + auto* partdata = data->delta; + auto* partdest = reinterpret_cast(buf->at()); + buf->write_stream(partdata, sizeof(XAnimDeltaPart), 1); + + if (partdata->trans) + { + buf->align(3); + buf->write_stream(partdata->trans, 4, 1); // not full struct + if (partdata->trans->size) + { + buf->write_stream(&partdata->trans->u, 0x1C, 1); // not full struct + if (data->framecount > 0x100) + buf->write_stream(&partdata->trans->u.frames.indices, + sizeof(short), partdata->trans->size + 1); + else + buf->write_stream(&partdata->trans->u.frames.indices, sizeof(char), + partdata->trans->size + 1); + + if (partdata->trans->u.frames.frames._1) + { + if (partdata->trans->smallTrans) + { + buf->align(0); + buf->write_stream(partdata->trans->u.frames.frames._1, sizeof(char) * 3, + partdata->trans->size + 1); + } + else + { + buf->align(3); + buf->write_stream(partdata->trans->u.frames.frames._2, sizeof(short) * 3, + partdata->trans->size + 1); + } + } + } + else buf->write_stream(partdata->trans->u.frame0, sizeof(float), 3); + partdest->trans = reinterpret_cast(-1); + } + + if (partdata->quat2) + { + buf->align(3); + buf->write_stream(partdata->quat2, 4, 1); // not full struct + + if (partdata->quat2->size) + { + buf->write_stream(&partdata->quat2->u, 0x4, 1); // not full struct + if (data->framecount > 255) + { + buf->write_stream(&partdata->quat2->u.frames.indices, + sizeof(short), partdata->quat2->size + 1); + } + else + { + buf->write_stream(&partdata->quat2->u.frames.indices, sizeof(char), + partdata->quat2->size + 1); + } + + if (partdata->quat2->u.frames.frames) + { + buf->align(3); + buf->write_stream(partdata->quat2->u.frames.frames, sizeof(short) * 2, + partdata->quat2->size + 1); + } + } + else + { + buf->write_stream(partdata->quat2->u.frame0, sizeof(short) * 2, 1); + } + partdest->quat2 = reinterpret_cast(-1); + } + + if (partdata->quat) + { + buf->align(3); + buf->write_stream(partdata->quat, 4, 1); + + if (partdata->quat->size) + { + buf->write_stream(&partdata->quat->u, 4, 1); // not full struct + + if (data->framecount > 255) + { + buf->write_stream(&partdata->quat->u.frames.indices, + sizeof(short), partdata->quat->size + 1); + } + else + { + buf->write_stream(&partdata->quat->u.frames.indices, sizeof(char), + partdata->quat->size + 1); + } + + if (partdata->quat->u.frames.frames) + { + buf->align(3); + buf->write_stream(partdata->quat->u.frames.frames, sizeof(short) * 4, + partdata->quat->size + 1); + } + } + else + { + buf->write_stream(partdata->quat->u.frame0, sizeof(short) * 4, 1); + } + + partdest->quat = reinterpret_cast(-1); + } + + dest->delta = reinterpret_cast(-1); + } + + if (data->dataByte) // dataByte + { + buf->align(0); + buf->write_stream(data->dataByte, sizeof(char), data->dataByteCount); + dest->dataByte = reinterpret_cast(-1); + } + if (data->dataShort) // dataShort + { + buf->align(1); + buf->write_stream(data->dataShort, sizeof(short), data->dataShortCount); + dest->dataShort = reinterpret_cast(-1); + } + if (data->dataInt) // dataInt + { + buf->align(3); + buf->write_stream(data->dataInt, sizeof(int), data->dataIntCount); + dest->dataInt = reinterpret_cast(-1); + } + if (data->randomDataShort) // randomDataShort + { + buf->align(1); + buf->write_stream(data->randomDataShort, sizeof(short), data->randomDataShortCount); + dest->randomDataShort = reinterpret_cast(-1); + } + if (data->randomDataByte) // randomDataByte + { + buf->align(0); + buf->write_stream(data->randomDataByte, sizeof(char), data->randomDataByteCount); + dest->randomDataByte = reinterpret_cast(-1); + } + if (data->randomDataInt) // randomDataInt + { + buf->align(3); + buf->write_stream(data->randomDataInt, sizeof(int), data->randomDataIntCount); + dest->randomDataInt = reinterpret_cast(-1); + } + + // XAnim indice data + if (data->indices.data) + { + if (data->framecount > 255) + { + buf->align(1); + buf->write_stream(data->indices.data, data->indexcount * 2, 1); + } + else + { + buf->align(0); + buf->write_stream(data->indices.data, data->indexcount, 1); + } + + dest->indices.data = reinterpret_cast(-1); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IXAnimParts::dump_xae3(XAnimParts* asset, const std::function& convertToString) + { + const auto path = "XAnim\\"s + asset->name + ".xae3"; + + AssetDumper dump; + if (!dump.open(path)) + { + return; + } + + dump.dump_single(asset); + dump.dump_string(asset->name); + + for (auto bone = 0; bone < asset->boneCount[9]; bone++) + { + dump.dump_string(convertToString(asset->tagnames[bone])); + } + + if (asset->dataByte) + { + dump.dump_array(asset->dataByte, asset->dataByteCount); + } + if (asset->dataShort) + { + dump.dump_array(asset->dataShort, asset->dataShortCount); + } + if (asset->dataInt) + { + dump.dump_array(asset->dataInt, asset->dataIntCount); + } + if (asset->randomDataShort) + { + dump.dump_array(asset->randomDataShort, asset->randomDataShortCount); + } + if (asset->randomDataByte) + { + dump.dump_array(asset->randomDataByte, asset->randomDataByteCount); + } + if (asset->randomDataInt) + { + dump.dump_array(asset->randomDataInt, asset->randomDataIntCount); + } + + if (asset->indices.data) + { + if (asset->framecount > 255) + { + dump.dump_array(asset->indices._2, asset->indexcount); + } + else + { + dump.dump_array(asset->indices._1, asset->indexcount); + } + } + + if (asset->notetracks) + { + dump.dump_array(asset->notetracks, asset->notetrackCount); + + for (auto i = 0; i < asset->notetrackCount; i++) + { + dump.dump_string(convertToString(asset->notetracks[i].name)); + } + } + + if (asset->delta) + { + dump.dump_single(asset->delta); + + if (asset->delta->trans) + { + auto extra_size = 0; + + if (asset->delta->trans->size) + { + if (asset->framecount > 255) + { + extra_size += (asset->delta->trans->size * 2) + 2; + } + else + { + extra_size += asset->delta->trans->size + 1; + } + } + + dump.dump_raw(asset->delta->trans, sizeof(XAnimPartTrans) + extra_size); + + if (asset->delta->trans->size) + { + if (asset->delta->trans->u.frames.frames._1) + { + if (asset->delta->trans->smallTrans) + { + dump.dump_raw(asset->delta->trans->u.frames.frames._1, (3 * asset->delta->trans->size) + 3); + } + else + { + dump.dump_raw(asset->delta->trans->u.frames.frames._2, (6 * asset->delta->trans->size) + 6); + } + } + } + } + + if (asset->delta->quat2) + { + auto extra_size = 0; + + if (asset->delta->quat2->size) + { + if (asset->framecount > 255) + { + extra_size += (asset->delta->quat2->size * 2) + 2; + } + else + { + extra_size += asset->delta->quat2->size + 1; + } + } + else + { + // no extra size required, quat2 data fits inside the frame0 buffer + extra_size += 0; + } + + dump.dump_raw(asset->delta->quat2, sizeof(XAnimDeltaPartQuat2) + extra_size); + + if (asset->delta->quat2->size && asset->delta->quat2->u.frames.frames) + { + dump.dump_raw(asset->delta->quat2->u.frames.frames, (asset->delta->quat2->size * 4) + 4); + } + } + + if (asset->delta->quat) + { + auto extra_size = 0; + + if (asset->delta->quat->size) + { + if (asset->framecount > 255) + { + extra_size += (asset->delta->quat->size * 2) + 2; + } + else + { + extra_size += asset->delta->quat->size + 1; + } + } + else + { + // quat data contains 4 extra bytes + extra_size += 4; + } + + dump.dump_raw(asset->delta->quat, sizeof(XAnimDeltaPartQuat) + extra_size); + + if (asset->delta->quat->size && asset->delta->quat->u.frames.frames) + { + dump.dump_raw(asset->delta->quat->u.frames.frames, (asset->delta->quat->size * 8) + 8); + } + } + } + + dump.close(); + } + + void IXAnimParts::dump(XAnimParts* asset, const std::function& convertToString) + { + IXAnimParts::dump_xae3(asset, convertToString); + } + } +} diff --git a/src/IW5/Assets/XAnimParts.hpp b/src/IW5/Assets/XAnimParts.hpp new file mode 100644 index 0000000..33fa235 --- /dev/null +++ b/src/IW5/Assets/XAnimParts.hpp @@ -0,0 +1,43 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + short SL_AllocString(const std::string& str); + const char* SL_ConvertToString(std::uint16_t index); + + class IXAnimParts : public IAsset + { + private: + std::string name_; + XAnimParts* asset_ = nullptr; + + public: + static XAnimParts* parse_xae2(const std::string& name, ZoneMemory* mem, const std::function& allocString); + static XAnimParts* parse_xae3(const std::string& name, ZoneMemory* mem, const std::function& allocString); + static XAnimParts* parse(const std::string& name, ZoneMemory* mem, const std::function& allocString = SL_AllocString); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump_xae3(XAnimParts* asset, + const std::function& convertToString = SL_ConvertToString); + static void dump(XAnimParts* asset, + const std::function& convertToString = SL_ConvertToString); + }; + } +} diff --git a/src/IW5/Assets/XModel.cpp b/src/IW5/Assets/XModel.cpp new file mode 100644 index 0000000..05a1698 --- /dev/null +++ b/src/IW5/Assets/XModel.cpp @@ -0,0 +1,452 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + XModel* IXModel::parse_new(const std::string& name, ZoneMemory* mem, + const std::string& filename, const std::function& allocString) + { + AssetReader read(mem); + + if (!read.open(filename)) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing xmodel \"%s\"...", name.c_str()); + + const auto asset = read.read_single(); + asset->name = read.read_string(); + + asset->boneNames = read.read_array(); + for (int i = 0; i < asset->numBones; i++) + { + asset->boneNames[i] = allocString(read.read_string()); + } + + asset->parentList = read.read_array(); + asset->tagAngles = read.read_array(); + asset->tagPositions = read.read_array(); + asset->partClassification = read.read_array(); + asset->animMatrix = read.read_array(); + asset->boneInfo = read.read_array(); + + // surfaces + asset->materials = read.read_array(); + for (int i = 0; i < asset->numSurfaces; i++) + { + asset->materials[i] = read.read_asset(); + } + + // lods + for (int i = 0; i < asset->numLods; i++) + { + asset->lods[i].surfaces = read.read_asset(); + } + + // colSurfs + asset->colSurf = read.read_array(); + for (int i = 0; i < asset->numColSurfs; i++) + { + asset->colSurf[i].tris = read.read_array(); + } + + // subassets + asset->physPreset = read.read_asset(); + asset->physCollmap = read.read_asset(); + + return asset; + } + + XModel* IXModel::parse(std::string name, ZoneMemory* mem, const std::function& allocString) + { + return IXModel::parse_new(name, mem, "XModel\\" + name + ".xme6", allocString); + } + + void IXModel::init(const std::string& name, ZoneMemory* mem) + { + this->is_scope_model_ = false; + this->name_ = name; + + if ((name.find("weapon_") != std::string::npos || name.find("viewmodel_") != std::string::npos) && + name.find("_scope") != std::string::npos && + !FileSystem::FileExists("XModel\\" + name + ".xme6")) + { + const auto model = IXModel::parse_new(name, mem, "XModel\\" + name.substr(0, name.size() - 6) + ".xme6", SL_AllocString); + + if (model != nullptr) + { + ZONETOOL_INFO("Raping sniper model to create a scope!"); + + std::vector allocated_surfaces; + std::vector> scope_surfaces; + + for (auto i = 0u; i < model->lods[0].numSurfacesInLod; i++) + { + if (static_cast(model->materials[i]->name).find("scope") != std::string::npos) + { + scope_surfaces.push_back({ model->materials[i], i }); + } + } + + if (scope_surfaces.empty()) + { + ZONETOOL_FATAL("Trying to rebuild scope model failed: No surfaces to build scope model!"); + } + + // alloc surfaces + allocated_surfaces.resize(scope_surfaces.size()); + + // force parse XSurfaces + const auto surfaces = IXSurface::parse(model->lods[0].surfaces->name, mem); + + // copy XSurfaces + for (auto i = 0u; i < scope_surfaces.size(); i++) + { + memcpy(&allocated_surfaces[i], &surfaces->xSurficies[scope_surfaces[i].second], sizeof XSurface); + } + + // rebuild surfaces / model info + model->numLods = 1; + model->numSurfaces = scope_surfaces.size(); + + // kill other lod data + memset(&model->lods[1], 0, sizeof XSurfaceLod * 3); + + // fix materials + for (auto i = 0u; i < scope_surfaces.size(); i++) + { + model->materials[i] = scope_surfaces[i].first; + } + + // rebuild lod + model->lods[0].numSurfacesInLod = allocated_surfaces.size(); + model->lods[0].surfaces->name = mem->StrDup(va("%s_scope", model->lods[0].surfaces->name)); + model->lods[0].surfaces->xSurficiesCount = allocated_surfaces.size(); + model->lods[0].surfaces->xSurficies = new XSurface[allocated_surfaces.size()]; + memcpy(model->lods[0].surfaces->xSurficies, allocated_surfaces.data(), sizeof XSurface * allocated_surfaces.size()); + + // remove all bones besides for tag_scope + //XBoneInfo scope_info = {}; + //auto found_bone = false; + // + //for (auto i = 0u; i < model->numBones; i++) + //{ + // if (SL_ConvertToString(model->boneNames[i]) == "tag_scope"s) + // { + // memcpy(&scope_info, &model->boneInfo[i], sizeof XBoneInfo); + // found_bone = true; + // break; + // } + //} + + //if (found_bone) + //{ + // model->numBones = 0; + // model->numRootBones = 0; + // memcpy(&model->boneInfo[0], &scope_info, sizeof XBoneInfo); + // model->boneNames[0] = SL_AllocString("tag_scope"); + //} + //else + //{ + // ZONETOOL_FATAL("You dun goofed"); + //} + model->numBones = 0; + model->numRootBones = 0; + + // test + model->numColSurfs = 0; + model->numSurfaces = 0; + + for (auto i = 0; i < 4; i++) + { + model->lods[i].numSurfacesInLod = 0; + if (model->lods[i].surfaces) + { + model->lods[i].surfaces->xSurficiesCount = 0; + } + } + + // + this->is_scope_model_ = true; + this->asset_ = model; + } + } + else + { + this->asset_ = this->parse(name, mem); + + // fix tags + //for (auto i = 0u; i < this->asset_->numBones; i++) + //{ + // auto tag_name = std::string(SL_ConvertToString(this->asset_->boneNames[i])); + + // // fix xmodel tags + // if (tag_name.find("tag_") != std::string::npos && + // tag_name.find("_scope") != std::string::npos && + // tag_name.find("thermal") == std::string::npos) + // { + // ZONETOOL_INFO("fixing tag %s -> tag_scope", tag_name.data()); + // this->asset_->boneNames[i] = SL_AllocString("tag_scope"); + // } + //} + + } + + // don't reparse the surfaces + // this->is_scope_model_ = true; + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data(), 1).xmodel; + } + } + + void IXModel::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + // fixup scriptstrings + auto* xmodel = mem->Alloc(); + memcpy(xmodel, this->asset_, sizeof XModel); + + // allocate bonenames + if (this->asset_->boneNames) + { + xmodel->boneNames = mem->Alloc(xmodel->numBones); + memcpy(xmodel->boneNames, this->asset_->boneNames, sizeof(short) * xmodel->numBones); + + for (int i = 0; i < xmodel->numBones; i++) + { + if (xmodel->boneNames[i]) + { + std::string bone = SL_ConvertToString(this->asset_->boneNames[i]); + xmodel->boneNames[i] = buf->write_scriptstring(bone); + } + } + } + + this->asset_ = xmodel; + } + + void IXModel::load_depending(IZone* zone) + { + auto* data = this->asset_; + + // Materials + for (auto i = 0u; i < data->numSurfaces; i++) + { + if (data->materials[i]) + { + zone->add_asset_of_type(material, data->materials[i]->name); + } + } + + // XSurfaces + for (auto i = 0u; i < data->numLods; i++) + { + if (data->lods[i].surfaces) + { + // write by pointer when fucking with scopes + if (is_scope_model_) + { + zone->add_asset_of_type_by_pointer(xmodelsurfs, data->lods[i].surfaces); + } + else + { + zone->add_asset_of_type(xmodelsurfs, data->lods[i].surfaces->name); + } + } + } + + // PhysCollmap + if (data->physCollmap) + { + zone->add_asset_of_type(phys_collmap, data->physCollmap->name); + } + + // PhysPreset + if (data->physPreset) + { + zone->add_asset_of_type(physpreset, data->physPreset->name); + } + } + + std::string IXModel::name() + { + return this->name_; + } + + std::int32_t IXModel::type() + { + return xmodel; + } + + void IXModel::write(IZone* zone, ZoneBuffer* buf) + { + assert(sizeof XModel, 308); + + auto data = this->asset_; + auto dest = buf->write(data); + + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->boneNames) + { + buf->align(1); + buf->write(data->boneNames, data->numBones); + ZoneBuffer::clear_pointer(&dest->boneNames); + } + + if (data->parentList) + { + buf->align(0); + buf->write(data->parentList, data->numBones - data->numRootBones); + ZoneBuffer::clear_pointer(&dest->parentList); + } + + if (data->tagAngles) + { + buf->align(1); + buf->write(data->tagAngles, data->numBones - data->numRootBones); + ZoneBuffer::clear_pointer(&dest->tagAngles); + } + + if (data->tagPositions) + { + buf->align(3); + buf->write(data->tagPositions, data->numBones - data->numRootBones); + ZoneBuffer::clear_pointer(&dest->tagPositions); + } + + if (data->partClassification) + { + buf->align(0); + buf->write(data->partClassification, data->numBones); + ZoneBuffer::clear_pointer(&dest->partClassification); + } + + if (data->animMatrix) + { + buf->align(3); + buf->write(data->animMatrix, data->numBones); + ZoneBuffer::clear_pointer(&dest->animMatrix); + } + + if (data->materials) + { + buf->align(3); + + auto* dest_materials = buf->write(data->materials, data->numSurfaces); + for (int i = 0; i < data->numSurfaces; i++) + { + dest_materials[i] = reinterpret_cast(zone->get_asset_pointer( + material, data->materials[i]->name)); + } + + ZoneBuffer::clear_pointer(&dest->materials); + } + + for (int i = 0; i < 4; i++) + { + if (!data->lods[i].surfaces) continue; + dest->lods[i].surfaces = reinterpret_cast(zone->get_asset_pointer( + xmodelsurfs, data->lods[i].surfaces->name)); + } + + if (data->colSurf) + { + buf->align(3); + auto destSurf = buf->write(data->colSurf, data->numColSurfs); + + for (int i = 0; i < data->numColSurfs; i++) + { + destSurf[i].tris = buf->write_s(3, data->colSurf[i].tris, data->colSurf[i].numCollTris); + } + + ZoneBuffer::clear_pointer(&dest->colSurf); + } + + if (data->boneInfo) + { + buf->align(3); + buf->write(data->boneInfo, data->numBones); + ZoneBuffer::clear_pointer(&dest->boneInfo); + } + + if (data->physPreset) + { + dest->physPreset = reinterpret_cast(zone->get_asset_pointer( + physpreset, data->physPreset->name)); + } + + if (data->physCollmap) + { + dest->physCollmap = reinterpret_cast(zone->get_asset_pointer( + phys_collmap, data->physCollmap->name)); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IXModel::dump(XModel* asset, const std::function& convertToString) + { + AssetDumper dump; + dump.open("XModel\\"s + asset->name + ".xme6"); + + // name + dump.dump_single(asset); + dump.dump_string(asset->name); + + // tags + dump.dump_array(asset->boneNames, asset->numBones); + for (int i = 0; i < asset->numBones; i++) + { + dump.dump_string(convertToString(asset->boneNames[i])); + } + dump.dump_array(asset->parentList, asset->numBones - asset->numRootBones); + dump.dump_array(asset->tagAngles, asset->numBones - asset->numRootBones); + dump.dump_array(asset->tagPositions, asset->numBones - asset->numRootBones); + dump.dump_array(asset->partClassification, asset->numBones); + dump.dump_array(asset->animMatrix, asset->numBones); + dump.dump_array(asset->boneInfo, asset->numBones); + + // surfaces + dump.dump_array(asset->materials, asset->numSurfaces); + for (int i = 0; i < asset->numSurfaces; i++) + { + dump.dump_asset(asset->materials[i]); + } + + // lods + for (int i = 0; i < asset->numLods; i++) + { + dump.dump_asset(asset->lods[i].surfaces); + } + + // colSurfs + dump.dump_array(asset->colSurf, asset->numColSurfs); + for (int i = 0; i < asset->numColSurfs; i++) + { + dump.dump_array(asset->colSurf[i].tris, asset->colSurf[i].numCollTris); + } + + // subassets + dump.dump_asset(asset->physPreset); + dump.dump_asset(asset->physCollmap); + + dump.close(); + } + } +} diff --git a/src/IW5/Assets/XModel.hpp b/src/IW5/Assets/XModel.hpp new file mode 100644 index 0000000..90995c8 --- /dev/null +++ b/src/IW5/Assets/XModel.hpp @@ -0,0 +1,42 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + short SL_AllocString(const std::string& str); + const char* SL_ConvertToString(std::uint16_t index); + + class IXModel : public IAsset + { + private: + std::string name_; + XModel* asset_ = nullptr; + bool is_scope_model_; + + public: + static XModel* parse_new(const std::string& name, ZoneMemory* mem, const std::string& filename, const std::function& allocString = SL_AllocString); + static XModel* parse(std::string name, ZoneMemory* mem, const std::function& allocString = SL_AllocString); + + void init(const std::string& name, ZoneMemory* mem) override; + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + void* pointer() override { return asset_; } + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(XModel* asset, + const std::function& convertToString = SL_ConvertToString); + }; + } +} diff --git a/src/IW5/Assets/XSurface.cpp b/src/IW5/Assets/XSurface.cpp new file mode 100644 index 0000000..d105011 --- /dev/null +++ b/src/IW5/Assets/XSurface.cpp @@ -0,0 +1,274 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + ModelSurface* IXSurface::parse(const std::string& name, ZoneMemory* mem) + { + AssetReader read(mem); + + if (!read.open("XSurface\\" + name + ".xse")) + { + return nullptr; + } + + ZONETOOL_INFO("Parsing xmodel surface \"%s\"...", name.c_str()); + + auto asset = read.read_array(); + asset->name = read.read_string(); + + asset->xSurficies = mem->Alloc(asset->xSurficiesCount); + + for (int i = 0; i < asset->xSurficiesCount; i++) + { + asset->xSurficies[i].tileMode = read.read_int(); + asset->xSurficies[i].deformed = read.read_int(); + asset->xSurficies[i].baseTriIndex = read.read_int(); + asset->xSurficies[i].baseVertIndex = read.read_int(); + + for (int j = 0; j < 6; j++) + { + asset->xSurficies[i].partBits[j] = read.read_int(); + } + + // vertex + auto vertexInfo = read.read_array(); + memcpy(&asset->xSurficies[i].vertexInfo, vertexInfo, sizeof XSurfaceVertexInfo); + asset->xSurficies[i].vertexInfo.vertsBlend = read.read_array(); + + // verticies + asset->xSurficies[i].vertCount = read.read_int(); + asset->xSurficies[i].verticies = read.read_array(); + + // triangles + asset->xSurficies[i].triCount = read.read_int(); + asset->xSurficies[i].triIndices = read.read_array(); + + // rigidVertLists + asset->xSurficies[i].vertListCount = read.read_int(); + asset->xSurficies[i].rigidVertLists = read.read_array(); + for (int vert = 0; vert < asset->xSurficies[i].vertListCount; vert++) + { + if (asset->xSurficies[i].rigidVertLists[vert].collisionTree) + { + asset->xSurficies[i].rigidVertLists[vert].collisionTree = read.read_array(); + + if (asset->xSurficies[i].rigidVertLists[vert].collisionTree->leafs) + { + asset->xSurficies[i].rigidVertLists[vert].collisionTree->leafs = read.read_array< + XSurfaceCollisionLeaf>(); + } + + if (asset->xSurficies[i].rigidVertLists[vert].collisionTree->nodes) + { + asset->xSurficies[i].rigidVertLists[vert].collisionTree->nodes = read.read_array< + XSurfaceCollisionNode>(); + } + } + } + } + + read.close(); + + return asset; + } + + void IXSurface::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = this->parse(name, mem); + + if (!this->asset_) + { + this->asset_ = DB_FindXAssetHeader(this->type(), this->name_.data(), 1).xsurface; + } + } + + void IXSurface::init(void* asset, ZoneMemory* mem) + { + this->asset_ = reinterpret_cast(asset); + this->name_ = this->asset_->name; + } + + void IXSurface::prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + void IXSurface::load_depending(IZone* zone) + { + } + + std::string IXSurface::name() + { + return this->name_; + } + + std::int32_t IXSurface::type() + { + return xmodelsurfs; + } + + void IXSurface::write_xsurfaces(IZone* zone, ZoneBuffer* buf, XSurface* data, + std::int16_t count) + { + assert(sizeof Face, 6); + assert(sizeof XRigidVertList, 12); + assert(sizeof XSurfaceCollisionTree, 40); + + auto* dest = buf->write(data, count); + + for (std::int16_t surf = 0; surf < count; surf++) + { + if (data[surf].vertexInfo.vertsBlend) + { + dest[surf].vertexInfo.vertsBlend = buf->write_s(1, data[surf].vertexInfo.vertsBlend, + data[surf].vertexInfo.vertCount[0] + + (data[surf].vertexInfo.vertCount[1] * 3) + (data[ + surf].vertexInfo.vertCount[2] * 5) + (data[ + surf].vertexInfo.vertCount[3] * 7)); + } + + buf->push_stream(6); + if (data[surf].verticies) + { + dest[surf].verticies = buf->write_s(15, data[surf].verticies, data[surf].vertCount); + } + buf->pop_stream(); + + if (data[surf].rigidVertLists) + { + XRigidVertList* ct = nullptr; + dest[surf].rigidVertLists = buf->write_s(3, data[surf].rigidVertLists, data[surf].vertListCount, + sizeof XRigidVertList, &ct); + + if (dest[surf].rigidVertLists == reinterpret_cast(-1)) + { + for (auto k = 0; k < data[surf].vertListCount; k++) + { + if (ct[k].collisionTree) + { + XSurfaceCollisionTree* entry = nullptr; + ct[k].collisionTree = buf->write_s(3, ct[k].collisionTree, 1, + sizeof XSurfaceCollisionTree, &entry); + + if (ct[k].collisionTree == reinterpret_cast(-1)) + { + if (entry->nodes) + { + entry->nodes = buf->write_s(15, entry->nodes, entry->nodeCount); + } + + if (entry->leafs) + { + entry->leafs = buf->write_s(1, entry->leafs, entry->leafCount); + } + } + } + } + } + } + + buf->push_stream(7); + if (data[surf].triIndices) + { + dest[surf].triIndices = buf->write_s(15, data[surf].triIndices, data[surf].triCount); + } + buf->pop_stream(); + } + } + + void IXSurface::write(IZone* zone, ZoneBuffer* buf) + { + assert(sizeof ModelSurface, 36); + + auto* dest = buf->at(); + auto* data = this->asset_; + + buf->write(data); + buf->push_stream(3); + START_LOG_STREAM; + + dest->name = buf->write_str(this->name()); + + if (data->xSurficies) + { + buf->align(3); + this->write_xsurfaces(zone, buf, data->xSurficies, data->xSurficiesCount); + ZoneBuffer::clear_pointer(&dest->xSurficies); + } + + END_LOG_STREAM; + buf->pop_stream(); + } + + void IXSurface::dump(ModelSurface* asset) + { + AssetDumper dump; + dump.open("XSurface\\"s + asset->name + ".xse"); + + dump.dump_array(asset, 1); + dump.dump_string(asset->name); + + for (int i = 0; i < asset->xSurficiesCount; i++) + { + dump.dump_int(asset->xSurficies[i].tileMode); + dump.dump_int(asset->xSurficies[i].deformed); + dump.dump_int(asset->xSurficies[i].baseTriIndex); + dump.dump_int(asset->xSurficies[i].baseVertIndex); + + for (int j = 0; j < 6; j++) + { + dump.dump_int(asset->xSurficies[i].partBits[j]); + } + + // vertex bs + dump.dump_array(&asset->xSurficies[i].vertexInfo, 1); + dump.dump_array(asset->xSurficies[i].vertexInfo.vertsBlend, + asset->xSurficies[i].vertexInfo.vertCount[0] + + (asset->xSurficies[i].vertexInfo.vertCount[1] * 3) + + (asset->xSurficies[i].vertexInfo.vertCount[2] * 5) + + (asset->xSurficies[i].vertexInfo.vertCount[3] * 7) + ); + + dump.dump_int(asset->xSurficies[i].vertCount); + dump.dump_array(asset->xSurficies[i].verticies, asset->xSurficies[i].vertCount); + + dump.dump_int(asset->xSurficies[i].triCount); + dump.dump_array(asset->xSurficies[i].triIndices, asset->xSurficies[i].triCount); + + dump.dump_int(asset->xSurficies[i].vertListCount); + dump.dump_array(asset->xSurficies[i].rigidVertLists, asset->xSurficies[i].vertListCount); + for (int vert = 0; vert < asset->xSurficies[i].vertListCount; vert++) + { + if (asset->xSurficies[i].rigidVertLists[vert].collisionTree) + { + dump.dump_array(asset->xSurficies[i].rigidVertLists[vert].collisionTree, 1); + + if (asset->xSurficies[i].rigidVertLists[vert].collisionTree->leafs) + { + dump.dump_array(asset->xSurficies[i].rigidVertLists[vert].collisionTree->leafs, + asset->xSurficies[i].rigidVertLists[vert].collisionTree->leafCount); + } + + if (asset->xSurficies[i].rigidVertLists[vert].collisionTree->nodes) + { + dump.dump_array(asset->xSurficies[i].rigidVertLists[vert].collisionTree->nodes, + asset->xSurficies[i].rigidVertLists[vert].collisionTree->nodeCount); + } + } + } + } + + dump.close(); + } + } +} diff --git a/src/IW5/Assets/XSurface.hpp b/src/IW5/Assets/XSurface.hpp new file mode 100644 index 0000000..8bad2e3 --- /dev/null +++ b/src/IW5/Assets/XSurface.hpp @@ -0,0 +1,40 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + class IXSurface : public IAsset + { + private: + std::string name_; + ModelSurface* asset_ = nullptr; + + void write_xsurfaces(IZone* zone, ZoneBuffer* buf, XSurface* data, std::int16_t count); + + public: + static ModelSurface* parse(const std::string& name, ZoneMemory* mem); + + void init(const std::string& name, ZoneMemory* mem) override; + void init(void* asset, ZoneMemory* mem) override; + + void prepare(ZoneBuffer* buf, ZoneMemory* mem) override; + void load_depending(IZone* zone) override; + + void* pointer() override { return asset_; } + std::string name() override; + std::int32_t type() override; + void write(IZone* zone, ZoneBuffer* buffer) override; + + static void dump(ModelSurface* asset); + }; + } +} diff --git a/src/IW5/Functions.hpp b/src/IW5/Functions.hpp new file mode 100644 index 0000000..02c3eb0 --- /dev/null +++ b/src/IW5/Functions.hpp @@ -0,0 +1,32 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + union XAssetHeader; + + static Function DB_FindXAssetHeader = 0x44E770; + static Function DB_IsXAssetDefault = 0x4CA800; + static Function DB_LoadXAssets = 0x44F740; + + static const char* SL_ConvertToString(std::uint16_t index) + { + return reinterpret_cast(*reinterpret_cast(0x01C122A4) + 12 * index + 4); + } + + static short SL_AllocString(const std::string& string) + { + return Memory::func(0x4E75A0)( + string.data(), 1, string.size() + 1); + } + } +} diff --git a/src/IW5/IW5.cpp b/src/IW5/IW5.cpp new file mode 100644 index 0000000..aaf143c --- /dev/null +++ b/src/IW5/IW5.cpp @@ -0,0 +1,174 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + bool is_dumping_complete = false; + bool is_dumping = false; + + const char* Linker::version() + { + return "IW5"; + } + + bool Linker::is_used() + { + return !strncmp(reinterpret_cast(0x770C6C), this->version(), 3); + } + + __declspec(naked) void Linker::MessageLoop() + { + while (true) + { + } + } + + void Linker::startup() + { + // AssetHandler::SetDump(true); + + if (this->is_used()) + { + // Kill original console window + Memory(0x53CB60).set(0xC3); + Memory(0x53C850).set(0xC3); + + // Message loop + Memory(0x53A2E0).jump(MessageLoop); + + // Load patches + RegisterPatch(); + RegisterPatch(); + + // Disable DW + Memory(0x53821C).nop(5); + + // Do not read server config, we don't need it anyways. + Memory(0x4DAEFE).nop(5); + + // Do not interact with the steamapi functions. + Memory(0x599EAE).nop(5); + + // Sys_Error -> printf + Memory(0x539A50).jump(printf); + + // Patch hunk size (Allows us to load more assets) + Memory(0x5340D9).set(0x3F000000); + Memory(0x534110).set(0x3F000000); + + // Replace the default attachment name (the specified default does not exist) + Memory(0x74B0BC).write_string("reflex"); + } + } + + std::shared_ptr Linker::alloc_zone(const std::string& zone) + { + // Patch main thread ID + // Memory(0x01C11BDC).Set(GetCurrentThreadId()); + + auto ptr = std::make_shared(zone, this); + return ptr; + } + + std::shared_ptr Linker::alloc_buffer() + { + auto ptr = std::make_shared(); + ptr->init_streams(9); + + return ptr; + } + + void Linker::load_zone(const std::string& name) + { + ZONETOOL_INFO("Loading zone \"%s\"...", name.data()); + + XZoneInfo zone = {name.data(), 20, 0}; + DB_LoadXAssets(&zone, 1, 0); + + auto timeout = 3000u; + ZONETOOL_INFO("Waiting %u sec for zone \"%s\" to be loaded.", timeout / 1000, name.data()); + Sleep(timeout); + } + + void Linker::unload_zones() + { + ZONETOOL_INFO("Unloading zones..."); + + static XZoneInfo zone = {"", 0, 20}; + DB_LoadXAssets(&zone, 1, 1); + } + + bool Linker::is_valid_asset_type(const std::string& type) + { + return this->type_to_int(type) >= 0; + } + + std::int32_t Linker::type_to_int(std::string type) + { + auto xassettypes = reinterpret_cast(0x7C6208); + + for (std::int32_t i = 0; i < max; i++) + { + if (xassettypes[i] == type) + return i; + } + + return -1; + } + + std::string Linker::type_to_string(std::int32_t type) + { + auto xassettypes = reinterpret_cast(0x7C6208); + return xassettypes[type]; + } + + bool Linker::supports_building() + { + return true; + } + + bool Linker::supports_version(const zone_target_version version) + { + return version == zone_target_version::iw5_release; + } + + void Linker::dump_zone(const std::string& name) + { + is_dumping_complete = false; + is_dumping = true; + + FileSystem::SetFastFile(name); + AssetHandler::SetDump(true); + load_zone(name); + + while (!is_dumping_complete) + { + Sleep(1); + } + } + + void Linker::verify_zone(const std::string& name) + { + AssetHandler::SetVerify(true); + load_zone(name); + AssetHandler::SetVerify(false); + } + + Linker::Linker() + { + } + + Linker::~Linker() + { + } + } +} diff --git a/src/IW5/IW5.hpp b/src/IW5/IW5.hpp new file mode 100644 index 0000000..91d7230 --- /dev/null +++ b/src/IW5/IW5.hpp @@ -0,0 +1,48 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include +#include "Functions.hpp" +#include "Structs.hpp" +#include "Patches/FFCompression.hpp" +#include "Patches/AssetHandler.hpp" +#include "Zone.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + class Linker : public ILinker + { + private: + static void MessageLoop(); + + public: + Linker(); + ~Linker(); + + const char* version() override; + bool is_used() override; + void startup() override; + std::shared_ptr alloc_zone(const std::string& zone) override; + std::shared_ptr alloc_buffer() override; + void load_zone(const std::string& name) override; + void unload_zones() override; + bool is_valid_asset_type(const std::string& type) override; + std::int32_t type_to_int(std::string type) override; + std::string type_to_string(std::int32_t type) override; + bool supports_building() override; + bool supports_version(const zone_target_version version) override; + + void dump_zone(const std::string& name) override; + void verify_zone(const std::string& name) override; + }; + } +} diff --git a/src/IW5/Patches/AssetHandler.cpp b/src/IW5/Patches/AssetHandler.cpp new file mode 100644 index 0000000..49267ce --- /dev/null +++ b/src/IW5/Patches/AssetHandler.cpp @@ -0,0 +1,541 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + extern bool is_dumping_complete; + extern bool is_dumping; + + XAssetEntry AssetHandler::XAssetEntries[AssetEntries]; + std::uint32_t AssetHandler::db_hashmap[AssetEntries]; + + bool AssetHandler::verify = false; + bool AssetHandler::dump = false; + + void** AssetHandler::DB_XAssetPool = (void**)0x7C5E38; + unsigned int* AssetHandler::g_poolSize = (unsigned int*)0x7C5B58; + AssetHandler::DB_GetXAssetSizeHandler_t* AssetHandler::DB_GetXAssetSizeHandlers = (DB_GetXAssetSizeHandler_t*) + 0x7C6430; + + std::unordered_map AssetHandler::StoredAssets; + + // std::shared_ptr < ZoneMemory > mem; + + XAssetHeader AssetHandler::FindXAsset(std::int32_t type, const char* name, std::uint32_t unk) + { + if (StoredAssets.find(name) != StoredAssets.end()) + { + if (StoredAssets[name].type == type) + { + return StoredAssets[name].header; + } + } + + return XAssetHeader(); // DB_FindXAssetHeader(type, name, flags); + } + + void AssetHandler::AddXAsset(std::int32_t type, std::string name, void* pointer) + { + if (StoredAssets.find(name) == StoredAssets.end()) + { + StoredAssets[name] = {static_cast(type), reinterpret_cast(pointer)}; + } + } + + void AssetHandler::SetVerify(bool state) + { + verify = state; + } + + void AssetHandler::SetDump(bool state) + { + dump = state; + } + + const char* GetAssetName(std::int32_t type, void* ptr) + { + if (type == image) + { + return reinterpret_cast(ptr)->name; + } + + if (type == localize) + { + return reinterpret_cast(ptr)->name; + } + + if (type == menu) + { + return reinterpret_cast(ptr)->window.name; + } + + return reinterpret_cast(ptr)->name; + } + + static std::vector> CommonAssets; + + void* DB_FindXAssetHeader_Unsafe(const XAssetType type, const std::string& name) + { + const static auto DB_FindXAssetHeader_Internal = 0x44E540; + const auto name_ptr = name.data(); + const auto type_int = static_cast(type); + + const XAsset* asset_header = nullptr; + + __asm + { + mov edi, name_ptr; + push type_int; + call DB_FindXAssetHeader_Internal; + add esp, 4; + mov asset_header, eax; + } + + return (asset_header) ? asset_header->header.data : nullptr; + } + + void AssetHandler::DB_LogLoadedAsset(void* ptr, std::int32_t type) + { + static std::vector> referencedAssets; + +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::DB_LogLoadedAsset"); +#endif + + auto fastfile = static_cast(reinterpret_cast(*reinterpret_cast(0x1294A00) + + 4)); + + // store all common assets + if (fastfile == "common_mp") + { + CommonAssets.push_back({type, GetAssetName(type, ptr)}); + } + + if (dump) + { + FileSystem::SetFastFile(fastfile); + + static FILE* csvFile = nullptr; + + // open csv file for dumping + if (!csvFile) + { + csvFile = FileSystem::FileOpen(fastfile + ".csv", "wb"); + } + + // dump assets to disk + if (csvFile) + { + auto xassettypes = reinterpret_cast(0x7C6208); + fprintf(csvFile, "%s,%s\n", xassettypes[type], GetAssetName(type, ptr)); + } + + // check if we're done loading the fastfile + if (type == rawfile && GetAssetName(type, ptr) == fastfile) + { + for (auto& asset : referencedAssets) + { + if (asset.second.length() <= 1 || asset.first == XAssetType::loaded_sound) + { + continue; + } + + const auto asset_name = &asset.second[1]; + const auto ref_asset = DB_FindXAssetHeader_Unsafe(asset.first, asset_name); + + if (ref_asset == nullptr) + { + ZONETOOL_ERROR("Could not find referenced asset \"%s\"!", asset_name); + continue; + } + + ZONETOOL_INFO("Dumping additional asset \"%s\" because it is referenced by %s.", asset_name, fastfile.data()); + + DB_LogLoadedAsset(ref_asset, asset.first); + } + + ZONETOOL_INFO("Zone \"%s\" dumped.", &fastfile[0]); + + referencedAssets.clear(); + FileSystem::FileClose(csvFile); + csvFile = nullptr; + + is_dumping_complete = true; + } + + if (GetAssetName(type, ptr)[0] == ',') + { + referencedAssets.push_back({ XAssetType(type), GetAssetName(type, ptr) }); + } + else + { +#define DUMPCASE(__type__,__interface__,__struct__) \ + if (type == __type__) \ + { \ + auto asset = reinterpret_cast<__struct__*>(ptr); \ + __interface__::dump(asset); \ + } + + DUMPCASE(attachment, IAttachmentDef, AttachmentDef); + DUMPCASE(leaderboarddef, ILeaderBoardDef, LeaderBoardDef); + DUMPCASE(material, IMaterial, Material); + + DUMPCASE(com_map, IComWorld, ComWorld); + DUMPCASE(gfx_map, IGfxWorld, GfxWorld); + DUMPCASE(fx_map, IFxWorld, FxWorld); + DUMPCASE(glass_map, IGlassWorld, GlassWorld); + DUMPCASE(col_map_mp, IClipMap, clipMap_t); + DUMPCASE(map_ents, IMapEnts, MapEnts); + DUMPCASE(lightdef, ILightDef, GfxLightDef); + + DUMPCASE(xanim, IXAnimParts, XAnimParts); + DUMPCASE(xmodel, IXModel, XModel); + DUMPCASE(xmodelsurfs, IXSurface, ModelSurface); + DUMPCASE(fx, IFxEffectDef, FxEffectDef); + DUMPCASE(sound, ISound, snd_alias_list_t); + DUMPCASE(stringtable, IStringTable, StringTable); + DUMPCASE(rawfile, IRawFile, RawFile); + DUMPCASE(scriptfile, IScriptFile, ScriptFile); + DUMPCASE(weapon, IWeaponDef, WeaponCompleteDef); + DUMPCASE(image, IGfxImage, GfxImage); + DUMPCASE(phys_collmap, IPhysCollmap, PhysCollmap); + DUMPCASE(loaded_sound, ILoadedSound, LoadedSound); + DUMPCASE(structureddatadef, IStructuredDataDef, StructuredDataDefSet); + + DUMPCASE(techset, ITechset, MaterialTechniqueSet); + DUMPCASE(pixelshader, IPixelShader, PixelShader); + DUMPCASE(vertexshader, IVertexShader, VertexShader); + DUMPCASE(vertexdecl, IVertexDecl, VertexDecl); + + DUMPCASE(font, IFontDef, Font_s); + } + } + else + { + /*std::string fastfile = reinterpret_cast(*reinterpret_cast(0x1294A00) + 4); + if (fastfile.find("mp_") != std::string::npos || fastfile == "common_mp"s) + { + FileSystem::SetFastFile(""); + + // dump everything techset related! + DUMPCASE(material, IMaterial, Material); + DUMPCASE(techset, ITechset, MaterialTechniqueSet); + DUMPCASE(pixelshader, IPixelShader, PixelShader); + DUMPCASE(vertexshader, IVertexShader, VertexShader); + DUMPCASE(vertexdecl, IVertexDecl, VertexDecl); + }*/ + } + + if (verify || FFCompression::ff_version > 1) + { + ZONETOOL_INFO("Loading asset \"%s\" of type %s.", GetAssetName(type, ptr), reinterpret_cast( +0x7C6208)[type]); + } + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + bool AssetHandler::IsCommonAsset(std::int32_t type, const std::string& name) + { + for (auto& commonAsset : CommonAssets) + { + if (commonAsset.first == type && commonAsset.second == name) + { + return true; + } + } + + return false; + } + + __declspec(naked) void AssetHandler::DB_AddXAsset() + { + __asm + { + pushad; + + push ebp; + push ecx; + call AssetHandler::DB_LogLoadedAsset; + add esp, 8; + + popad; + + jmp_back: + mov eax, DWORD PTR[ebp * 4 + 0x7c62c0]; + + push 0x44EFF6; + retn; + } + } + + void* AssetHandler::ReallocateAssetPool(uint32_t type, unsigned int newSize) + { + int elSize = DB_GetXAssetSizeHandlers[type](); + + void* poolEntry = malloc(newSize * elSize); + DB_XAssetPool[type] = poolEntry; + g_poolSize[type] = newSize; + + return poolEntry; + } + + void* AssetHandler::ReallocateAssetPoolM(uint32_t type, int multiplier) + { + int elSize = DB_GetXAssetSizeHandlers[type](); + int newSize = multiplier * g_poolSize[type]; + + void* poolEntry = malloc(newSize * elSize); + DB_XAssetPool[type] = poolEntry; + g_poolSize[type] = newSize; + + return poolEntry; + } + + __declspec(naked) void AssetHandler::FixupAssetLoading() + { + __asm + { + cmp edi, 0; + je asset_null; + + cmp byte ptr[edi + 8], 0; + je asset_continue; + + push 0x44EE2E; + retn; + + asset_continue: + push 0x44EF00; + retn; + + asset_null: + pop edi; + pop esi; + pop ebp; + pop ebx; + pop ecx; + retn; + } + } + + void logEntry(std::uint32_t entry) + { + ZONETOOL_INFO("Hashmap entry is %u.", entry); + } + + __declspec(naked) void TestHashMap() + { + static std::uintptr_t calladdr = 0x44E360; + + __asm + { + call calladdr; + + pushad; + + push eax; + call logEntry; + add esp, 4; + + popad; + + push 0x44F026; + retn; + } + } + + std::uintptr_t initialZoneStreams[9]; + + void InitLogStreamPos() + { + auto g_streamPos = reinterpret_cast(0x016634FC); + + // ZONETOOL_INFO("Setting initial zone stream positions..."); + + for (int i = 0; i < 9; i++) + { + initialZoneStreams[i] = g_streamPos[i]; + } + } + + void LogStreamPos() + { + auto fastfile = static_cast(reinterpret_cast(*reinterpret_cast(0x1294A00) + + 4)); + + auto g_streamPos = reinterpret_cast(0x016634FC); + auto g_streamPosArr = reinterpret_cast(0x01683304); + + if (fastfile.find("mp_") != std::string::npos) + { + for (auto i = 0u; i < 9; i++) + { + // ZONETOOL_INFO("Stream %i: 0x%08X 0x%08X", i, g_streamPos[i], g_streamPosArr[i]); + } + } + } + + __declspec(naked) void ShitOutTheCurrentStreamPositionsStub() + { + static std::uintptr_t DB_AuthLoad_InflateEnd = 0x004368E0; + + __asm + { + // log the fuck out of this shit + pushad; + call LogStreamPos; + popad; + + // call original function + call DB_AuthLoad_InflateEnd; + + // jump back + push 0x00436CAD; + retn; + } + } + + __declspec(naked) void InitStreamsStub() + { + static std::uintptr_t original = 0x00436970; + + __asm + { + pushad; + call InitLogStreamPos; + popad; + + call original; + + push 0x00436C0C; + retn; + } + } + + /*__declspec(naked) void hkRestoreSoundData() + { + static std::uintptr_t origFunc = 0x00436F20; + __asm + { + // call original function first + call origFunc; + + // mov data into the loadedSound struct + + } + }*/ + + AssetHandler::AssetHandler() + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::AssetHandler"); +#endif + + // Alloc zonememory + // mem = std::make_shared < ZoneMemory >(); + + // Stream position logging + Memory(0x00436CA8).jump(ShitOutTheCurrentStreamPositionsStub); + Memory(0x00436C07).jump(InitStreamsStub); + +#ifdef REALLOC_XASSETS + // Empty the arrays + memset(AssetHandler::XAssetEntries, 0, sizeof AssetHandler::XAssetEntries); + memset(AssetHandler::db_hashmap, 0, sizeof AssetHandler::db_hashmap); + + // Hashmap entry checking + Memory(0x44F021).Jump(TestHashMap); + + // Reallocate XAssetEntries + []() + { + for (std::uint32_t addr = 0x401000; addr < 0x700000; addr++) + { + // realloc xassetentries + if (*reinterpret_cast(addr) == 0x14C91D0) + { + // ZONETOOL_INFO("Address 0x%08X patched.", addr); + Memory(addr).Set(XAssetEntries); + } + + // realloc hashmap + if (*reinterpret_cast(addr) == 0x1381FB8) + { + // Memory(addr).Set(db_hashmap); + } + } + + Memory(0x133DA9C).Set(AssetEntries); + }(); + + // Reallocate XAssetEntry poolsize + Memory(0x44E440).Set(AssetEntries); + Memory(0x44EBF4).Set(AssetEntries); + Memory(0x4507F5).Set(AssetEntries); + Memory(0x4508A8).Set(AssetEntries); + Memory(0x450908).Set(AssetEntries); + Memory(0x450D42).Set(AssetEntries); + Memory(0x44EBF2).Set(AssetEntries); + Memory(0x4508A8).Set(AssetEntries); + Memory(0x450908).Set(AssetEntries); + Memory(0x450D50).Set(AssetEntries); + Memory(0x44E3A8).Set(AssetEntries); +#endif + + // Always allocate a default asset, even for map assets. + Memory(0x4505EE).nop(6); + + // Reallocate AssetPools + ReallocateAssetPoolM(techset, 2); + ReallocateAssetPoolM(map_ents, 5); + ReallocateAssetPoolM(com_map, 5); + ReallocateAssetPoolM(col_map_mp, 5); + ReallocateAssetPoolM(gfx_map, 5); + ReallocateAssetPoolM(fx_map, 5); + ReallocateAssetPoolM(glass_map, 5); + ReallocateAssetPoolM(localize, 2); + ReallocateAssetPoolM(material, 2); + ReallocateAssetPoolM(font, 2); + ReallocateAssetPoolM(image, 2); + ReallocateAssetPoolM(fx, 2); + ReallocateAssetPoolM(xanim, 2); + ReallocateAssetPoolM(xmodel, 2); + ReallocateAssetPoolM(xmodelsurfs, 2); + ReallocateAssetPoolM(physpreset, 2); + ReallocateAssetPoolM(loaded_sound, 2); + ReallocateAssetPoolM(sound, 2); + ReallocateAssetPoolM(weapon, 2); + ReallocateAssetPoolM(attachment, 2); + + // DB_AddXAssetHeader hook + Memory(0x44EFEF).jump(DB_AddXAsset); + + // Do not modify loadedsound struct after loading + Memory(0x0043856E).nop(19); + Memory(0x00438556).nop(12); + + // Prevent sound data from getting lost + // Memory(0x00438551).Jump(hkRestoreSoundData); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + AssetHandler::~AssetHandler() + { + } + } +} diff --git a/src/IW5/Patches/AssetHandler.hpp b/src/IW5/Patches/AssetHandler.hpp new file mode 100644 index 0000000..4b0937c --- /dev/null +++ b/src/IW5/Patches/AssetHandler.hpp @@ -0,0 +1,77 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + struct XAsset + { + XAssetType type; + XAssetHeader header; + }; + + struct XAssetEntry + { + XAsset asset; + unsigned int nextHash; + unsigned int nextOverride; + unsigned int nextPoolEntry; + }; + + + class AssetHandler : public IPatch + { + private: + static constexpr std::uint32_t AssetEntries = 86000; + + static XAssetEntry XAssetEntries[AssetEntries]; + static std::uint32_t db_hashmap[AssetEntries]; + + static std::unordered_map StoredAssets; + + static std::unordered_map> DuplicateAssetHandlers; + + static bool verify; + static bool dump; + + typedef int (__cdecl * DB_GetXAssetSizeHandler_t)(); + static DB_GetXAssetSizeHandler_t* DB_GetXAssetSizeHandlers; + + static void** DB_XAssetPool; + static unsigned int* g_poolSize; + + static void DB_LogLoadedAsset(void* ptr, std::int32_t type); + static void DB_AddXAsset(); + + static void* ReallocateAssetPool(uint32_t type, unsigned int newSize); + static void* ReallocateAssetPoolM(uint32_t type, int multiplier); + + static void FixupAssetLoading(); + + static void* DuplicatePixelShader(void* ptr); + static void* DuplicateVertexDecl(void* ptr); + static void* DuplicateVertexShader(void* ptr); + static void* DuplicateTechset(void* ptr); + + public: + + static bool IsCommonAsset(std::int32_t type, const std::string& name); + + static XAssetHeader FindXAsset(std::int32_t type, const char* name, std::uint32_t unk); + static void AddXAsset(std::int32_t type, std::string name, void* pointer); + static void SetVerify(bool state); + static void SetDump(bool state); + + AssetHandler(); + ~AssetHandler(); + }; + } +} diff --git a/src/IW5/Patches/FFCompression.cpp b/src/IW5/Patches/FFCompression.cpp new file mode 100644 index 0000000..9c35a3e --- /dev/null +++ b/src/IW5/Patches/FFCompression.cpp @@ -0,0 +1,429 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + std::int32_t FFCompression::ff_version = 1; + + std::int32_t FFCompression::z_inflateInit(const char* version, db_z_stream_s* strm, int stream_size) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::FFCompression::z_inflateInit"); +#endif + // Allocate zstd context + strm->state = reinterpret_cast(malloc(sizeof db_zstd_context_s)); + memset(strm->state, 0, sizeof db_zstd_context_s); + + // Create pointers needed for decompression + strm->state->DCtx = ZSTD_createDCtx(); + strm->state->DStream = ZSTD_createDStream(); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + + // return ZLIB ok state + return Z_OK; + } + + __declspec(naked) void FFCompression::db_inflateInit() + { + __asm + { + cmp ff_version, 2000; + jl jmp_back; + + // use zstd + push 52; + push esi; + push eax; + call FFCompression::z_inflateInit; + + add esp, 12; + retn; + + // use zlib + jmp_back: + push edi + xor edi, edi + cmp eax, edi + + push 0x5DAF25; + retn; + } + } + + std::int32_t FFCompression::z_inflate(db_z_stream_s* strm) + { + // if there are no bytes available in stream.. + if (strm->avail_in <= 0) + { + return Z_OK; + } + + ZSTD_inBuffer inBuf; + ZSTD_outBuffer outBuf; + + inBuf.src = strm->next_in; + inBuf.size = strm->avail_in; + inBuf.pos = 0; + + outBuf.dst = strm->next_out; + outBuf.size = strm->avail_out; + outBuf.pos = 0; + + auto code = ZSTD_decompressStream(strm->state->DStream, &outBuf, &inBuf); + if (ZSTD_isError(code)) + { + ZONETOOL_ERROR("An error occured while decompressing zone: %s", ZSTD_getErrorName(code)); + return Z_STREAM_ERROR; + } + + strm->next_out += outBuf.pos; + strm->total_out += outBuf.pos; + strm->avail_out -= outBuf.pos; + + strm->next_in += inBuf.pos; + strm->total_in += inBuf.pos; + strm->avail_in -= inBuf.pos; + + return Z_OK; + } + + __declspec(naked) void FFCompression::db_inflate() + { + __asm + { + cmp ff_version, 2000; + jl jmp_back; + + // use zstd + push eax; + call FFCompression::z_inflate; + + add esp, 4; + retn; + + // use zlib + jmp_back: + push ebp; + mov ebp, esp; + and esp, 0x0FFFFFFF8; + + push 0x5DAFE6; + retn; + } + } + + std::int32_t FFCompression::z_inflateEnd(db_z_stream_s* strm) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::FFCompression::z_inflateEnd"); +#endif + + // free decompression stream + ZSTD_freeDCtx(strm->state->DCtx); + ZSTD_freeDStream(strm->state->DStream); + free(strm->state); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + + // return ZLIB ok state + return Z_OK; + } + + __declspec(naked) void FFCompression::db_inflateEnd() + { + __asm + { + cmp ff_version, 2000; + jl jmp_back; + + // use zstd + push eax; + call FFCompression::z_inflateEnd; + + add esp, 4; + retn; + + // use zlib + jmp_back: + push edi; + mov edi, eax; + + test edi, edi; + jz error; + + push 0x5DAED7; + retn; + + error: + push 0x5DAF0D; + retn; + } + } + + void FFCompression::SetFastfileVersion(std::int32_t version) + { + ff_version = version; + } + + __declspec(naked) void FFCompression::DB_ReadXFileVersion() + { + __asm + { + cmp eax, 1; + je ok; + + cmp eax, 1000; + jae ok; + + push 0x436AD7; + retn; + + ok: + push eax; + call FFCompression::SetFastfileVersion; + add esp, 4; + + push 0x436AFA; + retn; + } + } + + __declspec(naked) void FFCompression::XModelSizeStub() + { + __asm + { + cmp ff_version, 2000; + jl originalSize; + + add DWORD PTR ds : 0x16634fc, 372; + jmp goBack; + + originalSize: + add DWORD PTR ds : 0x16634fc, 308; + + goBack: + push 0x0043C50E; + retn; + } + } + + __declspec(naked) void FFCompression::XModelSizeStub2() + { + __asm + { + cmp ff_version, 2000; + jl originalSize; + + mov ecx, 372; + jmp goBack; + + originalSize: + mov ecx, 308; + + goBack: + push 0x0043C4EE; + retn; + } + } + + static char keyBuffer[64]; + + __declspec(naked) void FFCompression::ReadEncryptedKey() + { + static std::uintptr_t DB_ReadXFileUncompressed = 0x00436EA0; + + __asm + { + // read original data + call DB_ReadXFileUncompressed; + + // check if fastfile requires decryption + cmp ff_version, 2000; + jl goBack; + + // save registers + pushad; + + // read encrypted data + mov ecx, 64; + lea eax, keyBuffer; + call DB_ReadXFileUncompressed; + + // restore registers + popad; + + goBack: + // go back + push 0x00436B24; + retn; + } + } + + __declspec(naked) void FFCompression::MaterialSizeStub() + { + __asm + { + cmp ff_version, 2000; + jl originalSize; + + add DWORD PTR ds : 0x16634fc, 0x6C; + jmp goBack; + + originalSize: + add DWORD PTR ds : 0x16634fc, 0x68; + + goBack: + push 0x0043AB1B; + retn; + } + } + + __declspec(naked) void FFCompression::MaterialSizeStub2() + { + __asm + { + cmp ff_version, 2000; + jl originalSize; + + mov ecx, 0x6C; + jmp goBack; + + originalSize: + mov ecx, 0x68; + + goBack: + push 0x0043AB03; + retn; + } + } + + __declspec(naked) void FFCompression::AlignmentHook() + { + __asm + { + cmp ff_version, 2000; + jl originalSize; + + add eax, 7; + jmp goBack; + + originalSize: + add eax, 3; + + goBack: + and eax, 0FFFFFFFCh; + + push 0x00436C4D; + retn; + } + } + + __declspec(naked) void FFCompression::AlignmentHook2() + { + __asm + { + cmp ff_version, 2000; + jl originalSize; + + add eax, 8; + jmp goBack; + + originalSize: + add eax, 3; + + goBack: + and eax, 0FFFFFFFCh; + + push 0x0043FE69; + retn; + } + } + + void FFCompression::DecryptStream(char* data, std::size_t size) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::FFCompression::DecryptStream"); +#endif + + if (ff_version >= 2000) + { + ZONETOOL_INFO("Decrypting data at ptr 0x%08X, size is %u", data, size); + decrypt_data(data, size); + } + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + __declspec(naked) void FFCompression::EncryptedLoadStream() + { + static std::uintptr_t load_stream = 0x00436F20; + + __asm + { + pushad; + push ecx; + push eax; + call load_stream; + call FFCompression::DecryptStream; + add esp, 8; + popad; + + retn; + } + } + + FFCompression::FFCompression() + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::FFCompression"); +#endif + + // Allow loading of unsigned fastfiles + Memory(0x436B3D).nop(2); + + // Compression hooks + Memory(0x5DAF20).jump(db_inflateInit); + Memory(0x5DAED0).jump(db_inflateEnd); + Memory(0x5DAFE0).jump(db_inflate); + + // Fastfile version hook + Memory(0x436AD2).jump(DB_ReadXFileVersion); + + // encrypted load stream hooks + Memory(0x00440CF4).call(EncryptedLoadStream); // col_map_mp + Memory(0x00441AEB).call(EncryptedLoadStream); // com_map + Memory(0x0044075B).call(EncryptedLoadStream); // map_ents + Memory(0x0043F38B).call(EncryptedLoadStream); // fx_map + Memory(0x0044C244).call(EncryptedLoadStream); // gfx_map + Memory(0x00446E6E).call(EncryptedLoadStream); // weapon + Memory(0x0043A92E).call(EncryptedLoadStream); // techset + + // Encryption hooks + // Memory(0x00436B1F).Jump(FFCompression::ReadEncryptedKey); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + FFCompression::~FFCompression() + { + } + } +} diff --git a/src/IW5/Patches/FFCompression.hpp b/src/IW5/Patches/FFCompression.hpp new file mode 100644 index 0000000..ecbd8de --- /dev/null +++ b/src/IW5/Patches/FFCompression.hpp @@ -0,0 +1,82 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include +#include + +namespace ZoneTool +{ + namespace IW5 + { + struct db_zstd_context_s + { + // original context + char pad[24]; + + // zstd context + ZSTD_DCtx* DCtx; + ZSTD_DStream* DStream; + }; + + struct db_z_stream_s + { + char* next_in; + unsigned int avail_in; + unsigned int total_in; + char* next_out; + unsigned int avail_out; + unsigned int total_out; + char* msg; + db_zstd_context_s* state; + char*(__cdecl *zalloc)(char*, unsigned int, unsigned int); + void (__cdecl *zfree)(char*, char*); + char* opaque; + int data_type; + unsigned int adler; + }; + + class FFCompression : public IPatch + { + private: + static std::int32_t z_inflateInit(const char* version, db_z_stream_s* strm, int stream_size); + static void db_inflateInit(); + + static std::int32_t z_inflate(db_z_stream_s* strm); + static void db_inflate(); + + static std::int32_t z_inflateEnd(db_z_stream_s* strm); + static void db_inflateEnd(); + + static void SetFastfileVersion(std::int32_t version); + static void DB_ReadXFileVersion(); + + static void XModelSizeStub(); + static void XModelSizeStub2(); + + static void ReadEncryptedKey(); + + static void MaterialSizeStub(); + static void MaterialSizeStub2(); + + static void AlignmentHook(); + + static void AlignmentHook2(); + + static void DecryptStream(char* data, std::size_t); + static void EncryptedLoadStream(); + + public: + static std::int32_t ff_version; + + FFCompression(); + ~FFCompression(); + }; + } +} diff --git a/src/IW5/Structs.hpp b/src/IW5/Structs.hpp new file mode 100644 index 0000000..db96c28 --- /dev/null +++ b/src/IW5/Structs.hpp @@ -0,0 +1,4909 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW5 + { + enum XAssetType : std::int32_t + { + physpreset, + phys_collmap, + xanim, + xmodelsurfs, + xmodel, + material, + pixelshader, + vertexshader, + vertexdecl, + techset, + image, + sound, + sndcurve, + loaded_sound, + col_map_mp, + com_map, + glass_map, + ai_paths, + vehicle_tracks, + map_ents, + fx_map, + gfx_map, + lightdef, + ui_map, + // not used + font, + menufile, + menu, + localize, + attachment, + weapon, + snddriverglobals, + // not used + fx, + impactfx, + surfacefx, + aitype, + // not used + mptype, + // not used + character, + // not used + xmodelalias, + // not used + rawfile, + scriptfile, + stringtable, + leaderboarddef, + structureddatadef, + tracer, + vehicle, + addon_map_ents, + max, + + col_map_sp = col_map_mp, + }; + + typedef float vec4_t[4]; + typedef float vec3_t[3]; + typedef float vec2_t[2]; + + template + struct VecInternal + { + float data[N]; + }; + + // Localized Strings + struct LocalizeEntry + { + const char* localizedString; + const char* name; + }; + + // Stringtables + struct StringTableCell + { + char* string; // 0 + int hash; // 4 + }; + + struct StringTable + { + const char* name; // 0 + int columns; // 4 + int rows; // 8 + StringTableCell* strings; // 12 + }; + + // RawFile + struct RawFile + { + const char* name; + int compressedLen; + int len; + const char* buffer; + }; + + // Menus + struct MenuEventHandlerSet; + struct Statement_s; + + struct UIFunctionList + { + int totalFunctions; + Statement_s** functions; + }; + + struct StaticDvar + { + void* dvar; + char* dvarName; + }; + + struct StaticDvarList + { + int numStaticDvars; + StaticDvar** staticDvars; + }; + + struct StringList + { + int totalStrings; + const char** strings; + }; + + struct ExpressionSupportingData + { + UIFunctionList uifunctions; + StaticDvarList staticDvarList; + StringList uiStrings; + }; + + enum expDataType + { + VAL_INT = 0x0, + VAL_FLOAT = 0x1, + VAL_STRING = 0x2, + NUM_INTERNAL_DATATYPES = 0x3, + VAL_FUNCTION = 0x3, + NUM_DATATYPES = 0x4, + }; + + struct ExpressionString + { + const char* string; + }; + + union operandInternalDataUnion + { + int intVal; + float floatVal; + ExpressionString stringVal; + Statement_s* function; + }; + +#pragma pack(push, 1) + struct Operand + { + expDataType dataType; + operandInternalDataUnion internals; + }; +#pragma pack(pop) + + union entryInternalData + { + int op; + Operand operand; + }; + + struct expressionEntry + { + int type; + entryInternalData data; + }; + + struct ExpressionPersistentState + { + int flags; + int playerDataKey[4]; + int lastExecuteTime[4]; + Operand lastResult[4]; + }; + + struct Statement_s + { + int numEntries; + expressionEntry* entries; + ExpressionSupportingData* supportingData; + ExpressionPersistentState persistentState; + }; + + struct SetLocalVarData + { + const char* localVarName; + Statement_s* expression; + }; + + struct ConditionalScript + { + MenuEventHandlerSet* eventHandlerSet; + Statement_s* eventExpression; // loads this first + }; + + union EventData + { + const char* unconditionalScript; + ConditionalScript* conditionalScript; + MenuEventHandlerSet* elseScript; + SetLocalVarData* setLocalVarData; + }; + +#pragma pack(push, 4) + struct MenuEventHandler + { + EventData eventData; + char eventType; + }; +#pragma pack(pop) + + struct MenuEventHandlerSet + { + int eventHandlerCount; + MenuEventHandler** eventHandlers; + }; + + struct ItemKeyHandler + { + int key; + MenuEventHandlerSet* action; + ItemKeyHandler* next; + }; + +#pragma pack(push, 4) + struct rectDef_s + { + float x; + float y; + float w; + float h; + char horzAlign; + char vertAlign; + }; +#pragma pack(pop) + + // sizeof = 0xB0 + struct windowDef_t + { + const char* name; + rectDef_s rect; + rectDef_s rectClient; + const char* group; + int style; + int border; + int ownerDraw; + int ownerDrawFlags; + float borderSize; + int staticFlags; + int dynamicFlags; + int nextTime; + float foreColor[4]; + float backColor[4]; + float borderColor[4]; + float outlineColor[4]; + float disableColor[4]; + void* background; + }; + + enum ItemFloatExpressionTarget + { + ITEM_FLOATEXP_TGT_RECT_X = 0x0, + ITEM_FLOATEXP_TGT_RECT_Y = 0x1, + ITEM_FLOATEXP_TGT_RECT_W = 0x2, + ITEM_FLOATEXP_TGT_RECT_H = 0x3, + ITEM_FLOATEXP_TGT_FORECOLOR_R = 0x4, + ITEM_FLOATEXP_TGT_FORECOLOR_G = 0x5, + ITEM_FLOATEXP_TGT_FORECOLOR_B = 0x6, + ITEM_FLOATEXP_TGT_FORECOLOR_RGB = 0x7, + ITEM_FLOATEXP_TGT_FORECOLOR_A = 0x8, + ITEM_FLOATEXP_TGT_GLOWCOLOR_R = 0x9, + ITEM_FLOATEXP_TGT_GLOWCOLOR_G = 0xA, + ITEM_FLOATEXP_TGT_GLOWCOLOR_B = 0xB, + ITEM_FLOATEXP_TGT_GLOWCOLOR_RGB = 0xC, + ITEM_FLOATEXP_TGT_GLOWCOLOR_A = 0xD, + ITEM_FLOATEXP_TGT_BACKCOLOR_R = 0xE, + ITEM_FLOATEXP_TGT_BACKCOLOR_G = 0xF, + ITEM_FLOATEXP_TGT_BACKCOLOR_B = 0x10, + ITEM_FLOATEXP_TGT_BACKCOLOR_RGB = 0x11, + ITEM_FLOATEXP_TGT_BACKCOLOR_A = 0x12, + ITEM_FLOATEXP_TGT__COUNT = 0x13, + }; + + struct ItemFloatExpression + { + ItemFloatExpressionTarget target; + Statement_s* expression; + }; + + struct editFieldDef_s + { + float minVal; + float maxVal; + float stepVal; + float range; + int maxChars; + int maxCharsGotoNext; + int maxPaintChars; + int paintOffset; + }; + + struct multiDef_s + { + const char* dvarList[32]; + const char* dvarStr[32]; + float dvarValue[32]; + int count; + int strDef; + }; + + struct columnInfo_s + { + int xpos; + int ypos; + int width; + int height; + int maxChars; + int alignment; + }; + + // TODO: 4 bytes missing somewhere + struct listBoxDef_s + { + int startPos[1]; + int endPos[1]; + int drawPadding; + float elementWidth; + float elementHeight; + int elementStyle; + int numColumns; + columnInfo_s columnInfo[16]; + MenuEventHandlerSet* onDoubleClick; + int notselectable; + int noScrollBars; + int usePaging; + float selectBorder[4]; + void* selectIcon; + Statement_s* elementHeightExp; + }; + + struct newsTickerDef_s + { + int feedId; + int speed; + int spacing; + }; + + struct textScrollDef_s + { + int startTime; + }; + + union itemDefData_t + { + listBoxDef_s* listBox; + editFieldDef_s* editField; + multiDef_s* multi; + const char* enumDvarName; + newsTickerDef_s* ticker; + textScrollDef_s* scroll; + void* data; + }; + + struct __declspec(align(4)) itemDef_t + { + windowDef_t window; + rectDef_s textRect; + int type; + int dataType; + int alignment; + int fontEnum; + int textAlignMode; + + union + { + float floatArray1[3]; + + struct + { + float textAlignX; + float textAlignY; + float textScale; + }; + }; + + int textStyle; + int gameMsgWindowIndex; + int gameMsgWindowMode; + const char* text; + int itemFlags; + void* parent; + MenuEventHandlerSet* mouseEnterText; + MenuEventHandlerSet* mouseExitText; + MenuEventHandlerSet* mouseEnter; + MenuEventHandlerSet* mouseExit; + MenuEventHandlerSet* action; + MenuEventHandlerSet* accept; + MenuEventHandlerSet* onFocus; + MenuEventHandlerSet* hasFocus; + MenuEventHandlerSet* leaveFocus; + const char* dvar; + const char* dvarTest; + ItemKeyHandler* onKey; + const char* enableDvar; + const char* localVar; + int dvarFlags; + void* focusSound; + float special; + int cursorPos; + itemDefData_t typeData; + int floatExpressionCount; + ItemFloatExpression* floatExpressions; + Statement_s* visibleExp; + Statement_s* disabledExp; + Statement_s* textExp; + Statement_s* materialExp; + float glowColor[4]; + bool decayActive; + int fxBirthTime; + int fxLetterTime; + int fxDecayStartTime; + int fxDecayDuration; + int lastSoundPlayedTime; + Statement_s* textAlignYExp; + }; + + struct menuTransition + { + int transitionType; + int startTime; + float startVal; + float endVal; + float time; + int endTriggerType; + }; + + struct __declspec(align(4)) menuData_t + { + int fullScreen; + int fadeCycle; + + union + { + float floatArray1[4]; + + struct + { + float fadeClamp; + float fadeAmount; + float fadeInAmount; + float blurWorld; + }; + }; + + MenuEventHandlerSet* onOpen; + MenuEventHandlerSet* onCloseRequest; + MenuEventHandlerSet* onClose; + MenuEventHandlerSet* onESC; + MenuEventHandlerSet* onFocusDueToClose; + ItemKeyHandler* onKey; + Statement_s* visibleExp; + const char* allowedBinding; + const char* soundName; + float focusColor[4]; + Statement_s* rectXExp; + Statement_s* rectYExp; + Statement_s* rectWExp; + Statement_s* rectHExp; + Statement_s* openSoundExp; + Statement_s* closeSoundExp; + Statement_s* soundLoopExp; + int cursorItem; + menuTransition scaleTransition; + menuTransition alphaTransition; + menuTransition xTransition; + menuTransition yTransition; + ExpressionSupportingData* expressionData; + char priority; + }; + + struct menuDef_t + { + menuData_t* data; + windowDef_t window; + int itemCount; + itemDef_t** items; + }; + + struct MenuList + { + const char* name; + int menuCount; + menuDef_t** menus; + }; + + // Material stuff + struct GfxImageLoadDef // actually a IDirect3DTexture* but this is easier + { + char mipLevels; + char flags; + short dimensions[3]; + int format; // usually the compression Magic + int dataSize; // set to zero to load from IWD + char* texture; // texture + }; + + struct GfxImage + { + GfxImageLoadDef* texture; + char mapType; // 5 is cube, 4 is 3d, 3 is 2d + char semantic; + char category; + char flags; + int cardMemory; + int dataLen1; + int dataLen2; + short height; + short width; + short depth; + bool loaded; + char pad; + char* name; + }; + + struct MaterialImage + { + unsigned int typeHash; // asset hash of type + char firstCharacter; // first character of image name + char secondLastCharacter; // second-last character of image name (maybe only in CoD4?!) + char sampleState; + char semantic; + GfxImage* image; // Image* actually + + Json ToJson() + { + Json data; + + JSON_ASSET(image, GfxImage); + + JSON_FIELD(firstCharacter); + JSON_FIELD(secondLastCharacter); + JSON_FIELD(sampleState); + JSON_FIELD(semantic); + + return data; + } + }; + + struct VertexDecl + { + const char* name; + int unknown; + char pad[28]; + void* declarations[16]; + }; + + struct PixelShader + { + const char* name; + void* shader; + DWORD* bytecode; + int codeLen; + }; + + struct VertexShader + { + const char* name; + void* shader; + DWORD* bytecode; + int codeLen; + }; + + struct MaterialArgumentCodeConst + { + unsigned __int16 index; + char firstRow; + char rowCount; + }; + + union MaterialArgumentDef + { + float* literalConst; + MaterialArgumentCodeConst codeConst; + unsigned int codeSampler; + unsigned int nameHash; + }; + + struct ShaderArgumentDef + { + short type; + short dest; + MaterialArgumentDef u; + }; +#pragma pack(push, 4) + struct MaterialPass + { + VertexDecl* vertexDecl; + VertexShader* vertexShader; + PixelShader* pixelShader; + char perPrimArgCount; + char perObjArgCount; + char stableArgCount; + char customSamplerFlags; + ShaderArgumentDef* argumentDef; + }; +#pragma pack(pop) + struct MaterialTechniqueHeader + { + const char* name; + unsigned __int16 flags; + unsigned __int16 passCount; + }; + + struct MaterialTechnique + { + MaterialTechniqueHeader hdr; + MaterialPass pass[1]; + }; + + struct MaterialTechniqueSet + { + const char* name; + int pad; + MaterialTechniqueSet* remappedTechniques; + MaterialTechnique* techniques[54]; + }; + + struct MaterialConstantDef + { + unsigned int nameHash; + char name[12]; + float literal[4]; + + Json ToJson() + { + Json data; + + JSON_STRING(name); + JSON_FIELD(nameHash); + JSON_FIELD_ARR(literal, 4); + + return data; + } + }; + + struct GfxStateBits + { + unsigned int loadBits[2]; + + Json ToJson() + { + Json data; + + JSON_FIELD_ARR(loadBits, 2); + + return data; + } + }; + + struct Material + { + const char* name; // 0 + char gameFlags; + char sortKey; + unsigned char animationX; // 6 // amount of animation frames in X + unsigned char animationY; // 7 // amount of animation frames in Y + int subRendererIndex; // 8 // 0x00 + unsigned int rendererIndex; // 12 // only for 3D models + int unknown; + unsigned int surfaceTypeBits; //+20 + char stateBitsEntry[54]; + char numMaps; + char constantCount; + char stateBitsCount; + char stateFlags; // 0x03 + unsigned short cameraRegion; // 0x04 + MaterialTechniqueSet* techniqueSet; // '2d' techset + MaterialImage* maps; // map references + MaterialConstantDef* constantTable; + GfxStateBits* stateMap; // might be NULL, need to test + int NiceMemeIW; + // int fakePointer; + }; + + // XModel + struct XSurfaceVertexInfo + { + __int16 vertCount[4]; + unsigned __int16* vertsBlend; + }; + + union GfxColor + { + unsigned int packed; + char array[4]; + }; + + union PackedTexCoords + { + unsigned int packed; + }; + + union PackedUnitVec + { + unsigned int packed; + }; + + struct GfxPackedVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + PackedTexCoords texCoord; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct XSurfaceCollisionAabb + { + unsigned __int16 mins[3]; + unsigned __int16 maxs[3]; + }; + + struct XSurfaceCollisionNode + { + XSurfaceCollisionAabb aabb; + unsigned __int16 childBeginIndex; + unsigned __int16 childCount; + }; + + struct XSurfaceCollisionLeaf + { + unsigned __int16 triangleBeginIndex; + }; + + struct XSurfaceCollisionTree + { + float trans[3]; + float scale[3]; + unsigned int nodeCount; + XSurfaceCollisionNode* nodes; + unsigned int leafCount; + XSurfaceCollisionLeaf* leafs; + }; + + struct XRigidVertList + { + unsigned __int16 boneOffset; + unsigned __int16 vertCount; + unsigned __int16 triOffset; + unsigned __int16 triCount; + XSurfaceCollisionTree* collisionTree; + }; + + struct unknownUnion + { + GfxPackedVertex* packedVerticies; + char* unknown1; //Size = ((vertCount << 1) + vertCount) << 3 + char* unknown2; //Size = ((vertCount << 2) + vertCount) << 2 + }; + + struct Face + { + unsigned short v1; + unsigned short v2; + unsigned short v3; + }; +#pragma pack(push, 1) + struct XSurfaceCTEntry + { + char pad[24]; + int numNode; + char* node; // el size 16 + int numLeaf; + short* leaf; + }; + + struct XSurfaceCT + { + int pad; + int pad2; + XSurfaceCTEntry* entry; + }; + +#pragma pack(push, 4) + struct XSurface + { + char tileMode; // +0 + bool deformed; // +1 + unsigned short vertCount; // +2 + unsigned short triCount; // +4 + unsigned char streamHandle; // +6 + unsigned char zoneHandle; // +7 + unsigned __int16 baseTriIndex; + unsigned __int16 baseVertIndex; + int unknown; + Face* triIndices; // +12 + XSurfaceVertexInfo vertexInfo; // +16 + GfxPackedVertex* verticies; // +28 + int vertListCount; // +32 + XRigidVertList* rigidVertLists; // +36 + int partBits[6]; // +40 + }; +#pragma pack(pop) + + struct ModelSurface + { + char* name; + XSurface* xSurficies; + short xSurficiesCount; + short _pad; + int partBits[6]; + }; + + struct XSurfaceLod + { + float dist; + short numSurfacesInLod; + short surfIndex; + ModelSurface* surfaces; + int partBits[6]; + XSurface* surfs; + char lod; + char smcBaseIndexPlusOne; + char smcSubIndexMask; + char smcBucket; + }; + + struct XModelCollTri_s + { + float plane[4]; + float svec[4]; + float tvec[4]; + }; + + struct XModelCollSurf_s + { + XModelCollTri_s* tris; + int numCollTris; + float mins[3]; + float maxs[3]; + int boneIdx; + int contents; + int surfFlags; + }; + + struct XBoneInfo + { + float bounds[2][3]; + float radiusSquared; + }; +#pragma pack(push, 4) + struct cplane_s + { + float normal[3]; + float dist; + char type; + char signbits; + // char pad[2]; + + Json ToJson() + { + Json data; + + data["normal"][0] = normal[0]; + data["normal"][1] = normal[1]; + data["normal"][2] = normal[2]; + data["dist"] = dist; + data["type"] = type; + data["signbits"] = signbits; + + return data; + } + }; +#pragma pack(pop) + struct cplane_t + { + char pad[20]; + }; + + struct cbrushside_t + { + cplane_s* plane; + unsigned int materialNum; + /*unsigned __int16 materialNum; + char firstAdjacentSideOffset; + char edgeCount; + + Json ToJson() + { + Json data; + data["plane"] = this->plane->ToJson(); + data["materialNum"] = this->materialNum; + data["firstAdjacentSideOffset"] = this->firstAdjacentSideOffset; + data["edgeCount"] = this->edgeCount; + return data; + }*/ + }; + + struct BrushWrapper + { + float mins[3]; + float maxs[3]; + unsigned int numPlaneSide; + cbrushside_t* side; + char* edge; + __int16 axialMaterialNum[2][3]; + __int16 firstAdjacentSideOffsets[2][3]; + int numEdge; + cplane_s* plane; + }; + + //struct BrushWrapper + //{ + // char pad[24]; // 24 + // short numPlaneSide; // 2 + // short pad2; // 2 + // cbrushside_t* side; // 4 + // char* edge; // 4 + // char pad3[24]; // 24 + // int numEdge; // 4 + // cplane_s* plane; // 4 + //}; // 68 bytes total + struct PhysGeomInfo + { + BrushWrapper* brush; + char pad[64]; + }; + + struct PhysCollmap + { + char* name; + int numInfo; + PhysGeomInfo* info; + char pad2[60]; + }; +#pragma pack(push, 4) + struct PhysPreset + { + const char* name; + int type; + float mass; + float bounce; + float friction; + float bulletForceScale; + float explosiveForceScale; + const char* sndAliasPrefix; + float piecesSpreadFraction; + float piecesUpwardVelocity; + int unknowns[7]; + }; +#pragma pack(pop) + +#pragma pack(push, 1) + // Only those structs are correct. + struct XModelAngle + { + short x; + short y; + short z; + short base; + }; + + struct XModelTagPos + { + float x; + float y; + float z; + }; + + struct DObjAnimMat + { + float quat[4]; + float trans[3]; + float transWeight; + }; + + struct Bounds + { + vec3_t midPoint; + vec3_t halfSize; + }; + + struct XModel + { + char* name; + char numBones; + char numRootBones; + unsigned char numSurfaces; + char lodRampType; + float scale; + unsigned int noScalePartBits[6]; + short* boneNames; + unsigned char* parentList; + XModelAngle* tagAngles; + XModelTagPos* tagPositions; + char* partClassification; + DObjAnimMat* animMatrix; + Material** materials; + XSurfaceLod lods[4]; + char maxLoadedLod; + char numLods; + char collLod; + char flags; + XModelCollSurf_s* colSurf; + int numColSurfs; + int contents; + XBoneInfo* boneInfo; + float radius; + Bounds bounds; + int memUsage; + bool bad; + char pad5[3]; + PhysPreset* physPreset; + PhysCollmap* physCollmap; + int unk; + }; // total size 308 +#pragma pack(pop) + + // Sound curve +#pragma pack(push, 4) + struct SndCurve + { + union + { + const char* filename; + const char* name; + }; + + unsigned __int16 knotCount; + vec2_t knots[16]; + }; +#pragma pack(pop) + + // Loaded sound + struct _AILSOUNDINFO + { + int format; + const void* data_ptr; + unsigned int data_len; + unsigned int rate; + int bits; + int channels; + unsigned int samples; + unsigned int block_size; + const void* initial_ptr; + }; + +#pragma pack(push, 4) + struct MssSound + { + _AILSOUNDINFO info; + char* data; + }; +#pragma pack(pop) + + struct LoadedSound + { + const char* name; + MssSound sound; + }; + + // Sounds + struct SpeakerLevels + { + int speaker; + int numLevels; + float levels[2]; + }; + + struct ChannelMap + { + int entryCount; // how many entries are used + SpeakerLevels speakers[6]; + }; + + struct SpeakerMap + { + bool isDefault; + char _pad[3]; + const char* name; + ChannelMap channelMaps[2][2]; + }; + + enum snd_alias_type_t : char + { + SAT_UNKNOWN = 0x0, + SAT_LOADED = 0x1, + SAT_STREAMED = 0x2, + SAT_PRIMED = 0x3, + SAT_COUNT = 0x4, + }; + + struct StreamFileNamePacked + { + unsigned __int64 offset; + unsigned __int64 length; + }; + + struct StreamFileNameRaw + { + const char* dir; + const char* name; + }; + + union StreamFileInfo + { + StreamFileNameRaw raw; + StreamFileNamePacked packed; + }; + + struct StreamFileName + { + unsigned __int16 isLocalized; + unsigned __int16 fileIndex; + StreamFileInfo info; + }; + + /*struct StreamedSound + { + StreamFileName filename; + unsigned int totalMsec; + };*/ + struct StreamedSound + { + const char* dir; + const char* name; + }; + + struct PrimedSound + { + StreamFileName filename; + LoadedSound* loadedPart; + int dataOffset; + int totalSize; + unsigned int primedCrc; + }; + + union SoundData + { + LoadedSound* loadSnd; // SoundFile->type == SAT_LOADED + StreamedSound streamSnd; // SoundFile->type == SAT_STREAMED + //PrimedSound primedSnd; // SoundFile->type == SAT_PRIMED + }; + + struct SoundFile // 0x10 + { + char type; + char _pad[2]; + bool exists; + SoundData sound; + }; + +#pragma pack(push, 4) + struct snd_alias_t + { + const char* aliasName; + const char* subtitle; + const char* secondaryAliasName; + const char* chainAliasName; + const char* mixerGroup; + SoundFile* soundFile; + int sequence; + float volMin; + float volMax; + int volModIndex; + float pitchMin; + float pitchMax; + float distMin; + float distMax; + float velocityMin; + int flags; + char masterPriority; + float masterPercentage; + float slavePercentage; + float probability; + float lfePercentage; + float centerPercentage; + int startDelay; + SndCurve* volumeFalloffCurve; + float envelopMin; + float envelopMax; + float envelopPercentage; + SpeakerMap* speakerMap; + }; +#pragma pack(pop) + + struct snd_alias_list_t + { + union + { + const char* aliasName; + const char* name; + }; + + snd_alias_t* head; + int count; + }; + + // Tracers + struct TracerDef + { + const char* name; + Material* material; + unsigned int drawInterval; + float speed; + float beamLength; + float beamWidth; + float screwRadius; + float screwDist; + float colors[5][4]; + + Json ToJson() + { + Json data; + + JSON_STRING(name); + JSON_ASSET(material, Material); + JSON_FIELD(drawInterval); + JSON_FIELD(speed); + JSON_FIELD(beamLength); + JSON_FIELD(beamWidth); + JSON_FIELD(screwRadius); + JSON_FIELD(screwDist); + JSON_FIELD(drawInterval); + + for (int i = 0; i < 5; i++) + { + for (int j = 0; j < 4; j++) + { + data["colors"][i][j] = colors[i][j]; + } + } + + return data; + } + }; + + // Weapons + enum weaponIconRatioType_t : int + { + WEAPON_ICON_RATIO_1TO1 = 0x0, + WEAPON_ICON_RATIO_2TO1 = 0x1, + WEAPON_ICON_RATIO_4TO1 = 0x2, + WEAPON_ICON_RATIO_COUNT = 0x3, + }; + + enum ammoCounterClipType_t : int + { + AMMO_COUNTER_CLIP_NONE = 0x0, + AMMO_COUNTER_CLIP_MAGAZINE = 0x1, + AMMO_COUNTER_CLIP_SHORTMAGAZINE = 0x2, + AMMO_COUNTER_CLIP_SHOTGUN = 0x3, + AMMO_COUNTER_CLIP_ROCKET = 0x4, + AMMO_COUNTER_CLIP_BELTFED = 0x5, + AMMO_COUNTER_CLIP_ALTWEAPON = 0x6, + AMMO_COUNTER_CLIP_COUNT = 0x7, + }; + + enum weapProjExplosion_t + { + WEAPPROJEXP_GRENADE = 0x0, + WEAPPROJEXP_ROCKET = 0x1, + WEAPPROJEXP_FLASHBANG = 0x2, + WEAPPROJEXP_NONE = 0x3, + WEAPPROJEXP_DUD = 0x4, + WEAPPROJEXP_SMOKE = 0x5, + WEAPPROJEXP_HEAVY = 0x6, + WEAPPROJEXP_NUM = 0x7, + }; + + struct AdsOverlayStruct + { + Material* adsOverlayShader; + Material* adsOverlayShaderLowRes; + Material* adsOverlayShaderEMP; + Material* adsOverlayShaderEMPLowRes; + int adsOverlayReticle; + float adsOverlayWidth; + float adsOverlayHeight; + float adsOverlayWidthSplitscreen; + float adsOverlayHeightSplitscreeen; + }; + + union XAnimIndices + { + char* _1; + unsigned __int16* _2; + void* data; + }; + + union XAnimDynamicFrames + { + char (*_1)[3]; + unsigned __int16 (*_2)[3]; + }; + + union XAnimDynamicIndices + { + char _1[1]; + unsigned __int16 _2[1]; + }; + + struct XAnimPartTransFrames + { + float mins[3]; + float size[3]; + XAnimDynamicFrames frames; + XAnimDynamicIndices indices; + }; + + union XAnimPartTransData + { + XAnimPartTransFrames frames; + float frame0[3]; + }; + + struct XAnimPartTrans + { + unsigned __int16 size; + char smallTrans; + __declspec(align(2)) XAnimPartTransData u; + }; + +#pragma pack(push, 4) + struct XAnimDeltaPartQuatDataFrames2 + { + __int16(*frames)[2]; + XAnimDynamicIndices indices; + }; + + union XAnimDeltaPartQuatData2 + { + XAnimDeltaPartQuatDataFrames2 frames; + __int16 frame0[2]; + }; + + struct XAnimDeltaPartQuat2 + { + unsigned __int16 size; + XAnimDeltaPartQuatData2 u; + }; + + struct XAnimDeltaPartQuatDataFrames + { + __int16(*frames)[4]; + XAnimIndices indices; + }; + + union XAnimDeltaPartQuatData + { + XAnimDeltaPartQuatDataFrames frames; + __int16 frame0[4]; + }; + + struct XAnimDeltaPartQuat + { + unsigned __int16 size; + XAnimDeltaPartQuatData u; + }; + + struct XAnimDeltaPart + { + XAnimPartTrans* trans; + XAnimDeltaPartQuat2* quat2; + XAnimDeltaPartQuat* quat; + }; + + struct XAnimNotifyInfo + { + short name; + float time; + }; + + struct XAnimParts + { + char* name; // 0 + unsigned short dataByteCount; // 4 + unsigned short dataShortCount; // 6 + unsigned short dataIntCount; // 8 + unsigned short randomDataByteCount; // 10 - 0xA + unsigned short randomDataIntCount; // 12 - 0xC + unsigned short framecount; // 14 - 0xE + char flags; // 16 + unsigned char boneCount[10]; // 17 + char notetrackCount; // 27 + bool bLoop; // 28 + bool bDelta; // 29 + char assetType; // 30 + char ikType; // 31 + unsigned int randomDataShortCount; // 32 - 0x20 + unsigned int indexcount; // 36 - 0x24 + float framerate; // 40 - 0x28 + float frequency; // 44 - 0x2C + unsigned short* tagnames; // 48 - 0x30 + char* dataByte; // 52 - 0x34 + short* dataShort; // 56 - 0x38 + int* dataInt; // 60 - 0x3C + short* randomDataShort; // 64 - 0x40 + char* randomDataByte; // 68 - 0x44 + int* randomDataInt; // 72 - 0x48 + XAnimIndices indices; // 76 - 0x4C + XAnimNotifyInfo* notetracks; // 80 - 0x50 + XAnimDeltaPart* delta; // 84 - 0x54 + }; + + // StructuredDataDef + enum StructuredDataTypeCategory + { + DATA_INT = 0x0, + DATA_BYTE = 0x1, + DATA_BOOL = 0x2, + DATA_STRING = 0x3, + DATA_ENUM = 0x4, + DATA_STRUCT = 0x5, + DATA_INDEXED_ARRAY = 0x6, + DATA_ENUM_ARRAY = 0x7, + DATA_FLOAT = 0x8, + DATA_SHORT = 0x9, + DATA_COUNT = 0xA, + }; +#pragma pack(push,4) + struct StructuredDataEnumEntry + { + const char* name; + unsigned __int16 index; + }; +#pragma pack(pop) + struct StructuredDataEnum + { + int entryCount; + int reservedEntryCount; + StructuredDataEnumEntry* entries; + }; + + union StructuredDataTypeUnion + { + unsigned int stringDataLength; + int enumIndex; + int structIndex; + int indexedArrayIndex; + int enumedArrayIndex; + }; + + struct StructuredDataType + { + StructuredDataTypeCategory type; + StructuredDataTypeUnion u; + }; +#pragma pack(push,4) + struct StructuredDataStructProperty + { + const char* name; + StructuredDataType item; + int offset; + int validation; + }; +#pragma pack(pop) + struct StructuredDataStruct + { + int propertyCount; + StructuredDataStructProperty* properties; + int size; + unsigned int bitOffset; + }; + + struct StructuredDataIndexedArray + { + int arraySize; + StructuredDataType elementType; + unsigned int elementSize; + }; + + struct StructuredDataEnumedArray + { + int enumIndex; + StructuredDataType elementType; + unsigned int elementSize; + }; + + struct StructuredDataDef + { + int version; + unsigned int formatChecksum; + int enumCount; + StructuredDataEnum* enums; + int structCount; + StructuredDataStruct* structs; + int indexedArrayCount; + StructuredDataIndexedArray* indexedArrays; + int enumedArrayCount; + StructuredDataEnumedArray* enumedArrays; + StructuredDataType rootType; + unsigned int size; + }; + + struct StructuredDataDefSet + { + const char* name; + unsigned int defCount; + StructuredDataDef* defs; + }; + + // MapEnts + struct TriggerModel + { + int contents; + unsigned short hullCount; + unsigned short firstHull; + }; + + struct TriggerHull + { + Bounds bounds; + int contents; + unsigned short slabCount; + unsigned short firstSlab; + }; + + struct TriggerSlab + { + vec3_t dir; + float midPoint; + float halfSize; + }; + + struct MapTriggers + { + int modelCount; + TriggerModel* models; // sizeof 8 + int hullCount; + TriggerHull* hulls; // sizeof 32 + int slabCount; + TriggerSlab* slabs; // sizeof 20 + }; + + struct ClientTriggerAabbNode + { + Bounds bounds; + unsigned __int16 firstChild; + unsigned __int16 childCount; + }; + + struct ClientTriggers + { + MapTriggers trigger; + unsigned __int16 numClientTriggerNodes; + ClientTriggerAabbNode* clientTriggerAabbTree; + unsigned int triggerStringLength; + char* triggerString; + __int16* visionSetTriggers; + char* triggerType; + float* origins; + float* scriptDelay; + __int16* audioTriggers; + }; + + struct MapEnts + { + const char* name; + char* entityString; + int numEntityChars; + MapTriggers trigger; + ClientTriggers clientTrigger; + }; +#pragma pack(pop) + + // FxEffectDef + /* FxEffectDef::flags */ +#define FX_ELEM_LOOPING 0x1 +#define FX_ELEM_USE_RAND_COLOR 0x2 +#define FX_ELEM_USE_RAND_ALPHA 0x4 +#define FX_ELEM_USE_RAND_SIZE0 0x8 +#define FX_ELEM_USE_RAND_SIZE1 0x10 +#define FX_ELEM_USE_RAND_SCALE 0x20 +#define FX_ELEM_USE_RAND_ROT_DELTA 0x40 +#define FX_ELEM_MOD_COLOR_BY_ALPHA 0x80 +#define FX_ELEM_USE_RAND_VEL0 0x100 +#define FX_ELEM_USE_RAND_VEL1 0x200 +#define FX_ELEM_USE_BACK_COMPAT_VEL 0x400 +#define FX_ELEM_ABS_VEL0 0x800 +#define FX_ELEM_ABS_VEL1 0x1000 +#define FX_ELEM_PLAY_ON_TOUCH 0x2000 +#define FX_ELEM_PLAY_ON_DEATH 0x4000 +#define FX_ELEM_PLAY_ON_RUN 0x8000 +#define FX_ELEM_BOUNDING_SPHERE 0x10000 +#define FX_ELEM_USE_ITEM_CLIP 0x20000 +#define FX_ELEM_DISABLED 0x80000000 +#define FX_ELEM_DECAL_FADE_IN 0x40000 + + /* FxElemDef::flags */ + //#define FX_ELEM_SPAWN_RELATIVE_TO_EFFECT 0x2 + //#define FX_ELEM_SPAWN_FRUSTUM_CULL 0x4 + //#define FX_ELEM_RUNNER_USES_RAND_ROT 0x8 + //#define FX_ELEM_SPAWN_OFFSET_NONE 0x0 + //#define FX_ELEM_SPAWN_OFFSET_SPHERE 0x10 + //#define FX_ELEM_SPAWN_OFFSET_CYLINDER 0x20 + //#define FX_ELEM_SPAWN_OFFSET_MASK 0x30 + //#define FX_ELEM_RUN_RELATIVE_TO_WORLD 0x0 + //#define FX_ELEM_RUN_RELATIVE_TO_SPAWN 0x40 + //#define FX_ELEM_RUN_RELATIVE_TO_EFFECT 0x80 + //#define FX_ELEM_RUN_RELATIVE_TO_OFFSET 0xC0 + //#define FX_ELEM_RUN_MASK 0xC0 + //#define FX_ELEM_USE_COLLISION 0x100 + //#define FX_ELEM_DIE_ON_TOUCH 0x200 + //#define FX_ELEM_DRAW_PAST_FOG 0x400 + //#define FX_ELEM_DRAW_WITH_VIEWMODEL 0x800 + //#define FX_ELEM_BLOCK_SIGHT 0x1000 + //#define FX_ELEM_DRAW_IN_THERMAL_VIEW_ONLY 0x2000 + //#define FX_ELEM_TRAIL_ORIENT_BY_VELOCITY 0x4000 + //#define FX_ELEM_EMIT_ORIENT_BY_ELEM 0x8000 + //#define FX_ELEM_HAS_VELOCITY_GRAPH_LOCAL 0x1000000 + //#define FX_ELEM_HAS_VELOCITY_GRAPH_WORLD 0x2000000 + //#define FX_ELEM_HAS_GRAVITY 0x4000000 + //#define FX_ELEM_USE_MODEL_PHYSICS 0x8000000 + //#define FX_ELEM_NONUNIFORM_SCALE 0x10000000 + //#define FX_ELEM_CLOUD_SHAPE_CUBE 0x0 + //#define FX_ELEM_CLOUD_SHAPE_SPHERE_LARGE 0x20000000 + //#define FX_ELEM_CLOUD_SHAPE_SPHERE_MEDIUM 0x40000000 + //#define FX_ELEM_CLOUD_SHAPE_SPHERE_SMALL 0x60000000 + //#define FX_ELEM_CLOUD_MASK 0x60000000 + //#define FX_ELEM_FOUNTAIN_DISABLE_COLLISION 0x80000000 + + /* FxElemAtlas::behavior */ +#define FX_ATLAS_START_MASK 0x3 +#define FX_ATLAS_START_FIXED 0x0 +#define FX_ATLAS_START_RANDOM 0x1 +#define FX_ATLAS_START_INDEXED 0x2 +#define FX_ATLAS_PLAY_OVER_LIFE 0x4 +#define FX_ATLAS_LOOP_ONLY_N_TIMES 0x8 + + enum FxElemType : char + { + FX_ELEM_TYPE_SPRITE_BILLBOARD = 0x0, + FX_ELEM_TYPE_SPRITE_ORIENTED = 0x1, + FX_ELEM_TYPE_TAIL = 0x2, + FX_ELEM_TYPE_TRAIL = 0x3, + FX_ELEM_TYPE_CLOUD = 0x4, + FX_ELEM_TYPE_SPARKCLOUD = 0x5, + FX_ELEM_TYPE_SPARKFOUNTAIN = 0x6, + FX_ELEM_TYPE_MODEL = 0x7, + FX_ELEM_TYPE_OMNI_LIGHT = 0x8, + FX_ELEM_TYPE_SPOT_LIGHT = 0x9, + FX_ELEM_TYPE_SOUND = 0xA, + FX_ELEM_TYPE_DECAL = 0xB, + FX_ELEM_TYPE_RUNNER = 0xC, + FX_ELEM_TYPE_COUNT = 0xD, + FX_ELEM_TYPE_LAST_SPRITE = 0x3, + FX_ELEM_TYPE_LAST_DRAWN = 0x9, + }; + + struct FxElemVec3Range + { + float base[3]; + float amplitude[3]; + + Json ToJson() + { + Json data; + + JSON_FIELD_ARR(base, 3); + JSON_FIELD_ARR(amplitude, 3); + + return data; + } + }; + + struct FxIntRange + { + int base; + int amplitude; + + Json ToJson() + { + Json data; + + JSON_FIELD(base); + JSON_FIELD(amplitude); + + return data; + } + }; + + struct FxFloatRange + { + float base; + float amplitude; + + Json ToJson() + { + Json data; + + JSON_FIELD(base); + JSON_FIELD(amplitude); + + return data; + } + }; + + struct FxSpawnDefLooping + { + int intervalMsec; + int count; + + Json ToJson() + { + Json data; + + JSON_FIELD(intervalMsec); + JSON_FIELD(count); + + return data; + } + }; + + struct FxSpawnDefOneShot + { + FxIntRange count; + + Json ToJson() + { + Json data; + + JSON_STRUCT_REF(count); + + return data; + } + }; + + union FxSpawnDef + { + FxSpawnDefLooping looping; + FxSpawnDefOneShot oneShot; + }; + + struct FxEffectDef; + + union FxEffectDefRef + { + FxEffectDef* handle; + const char* name; + }; + + struct GfxLightDef; + union FxElemVisuals + { + const void* anonymous; + GfxLightDef* lightDef; + Material* material; + XModel* xmodel; + FxEffectDefRef* effectDef; + const char* soundName; + }; + + typedef Material* FxElemMarkVisuals[2]; + + union FxElemDefVisuals + { + FxElemVisuals instance; + //If parent FxElemDef::elemType == 0x7, use xmodel + //If parent FxElemDef::elemType == 0xC, use effectDef + //If parent FxElemDef::elemType == 0xA, use soundName + //If parent FxElemDef::elemType != 0x9 || 0x8, use material + + FxElemVisuals* array; //Total count = parent FxElemDef::visualCount + FxElemMarkVisuals* markArray; //Total count = parent FxElemDef::visualCount + }; + + struct FxTrailVertex + { + float pos[2]; + float normal[2]; + float texCoord[2]; + + Json ToJson() + { + Json data; + + JSON_FIELD_ARR(pos, 2); + JSON_FIELD_ARR(normal, 2); + JSON_FIELD_ARR(texCoord, 2); + + return data; + } + }; + + struct FxTrailDef + { + int scrollTimeMsec; + int repeatDist; + float invSplitDist; + float invSplitArcDist; + float invSplitTime; + int vertCount; + FxTrailVertex* verts; + int indCount; + unsigned __int16* inds; + }; + + struct FxSparkFountainDef + { + float gravity; + float bounceFrac; + float bounceRand; + float sparkSpacing; + float sparkLength; + int sparkCount; + float loopTime; + float velMin; + float velMax; + float velConeFrac; + float restSpeed; + float boostTime; + float boostFactor; + }; + + union FxElemExtendedDefPtr + { + char* unknownDef; + FxSparkFountainDef* sparkFountain; + FxTrailDef* trailDef; + }; + + struct FxElemAtlas + { + char behavior; + char index; + char fps; + char loopCount; + char colIndexBits; + char rowIndexBits; + __int16 entryCount; + + Json ToJson() + { + Json data; + + return data; + } + }; + + struct FxElemVelStateInFrame + { + FxElemVec3Range velocity; + FxElemVec3Range totalDelta; + }; + + struct FxElemVelStateSample + { + FxElemVelStateInFrame local; + FxElemVelStateInFrame world; + }; + + struct FxElemVisualState + { + char color[4]; + float rotationDelta; + float rotationTotal; + float size[2]; + float scale; + }; + + struct FxElemVisStateSample + { + FxElemVisualState base; + FxElemVisualState amplitude; + }; + + struct FxFloatRange3 + { + FxFloatRange range[3]; + + Json ToJson() + { + Json data; + + return data; + } + }; + + /* 515 */ + enum FxElemDefFlagTypes + { + FX_FLAG_TYPE_EDITOR = 0x0, + FX_FLAG_TYPE_NATIVE = 0x1, + FX_FLAG_TYPE_ATLAS = 0x2, + FX_FLAG_TYPE_COUNT = 0x3, + }; + + /* 516 */ + enum FxElemDefEditorFlags + { + FX_ED_FLAG_LOOPING = 0x1, + FX_ED_FLAG_USE_RANDOM_COLOR = 0x2, + FX_ED_FLAG_USE_RANDOM_ALPHA = 0x4, + FX_ED_FLAG_USE_RANDOM_SIZE_0 = 0x8, + FX_ED_FLAG_USE_RANDOM_SIZE_1 = 0x10, + FX_ED_FLAG_USE_RANDOM_SCALE = 0x20, + FX_ED_FLAG_USE_RANDOM_ROTATION_DELTA = 0x40, + FX_ED_FLAG_MODULATE_COLOR_BY_ALPHA = 0x80, + FX_ED_FLAG_USE_RANDOM_VELOCITY_0 = 0x100, + FX_ED_FLAG_USE_RANDOM_VELOCITY_1 = 0x200, + FX_ED_FLAG_BACKCOMPAT_VELOCITY = 0x400, + FX_ED_FLAG_ABSOLUTE_VELOCITY_0 = 0x800, + FX_ED_FLAG_ABSOLUTE_VELOCITY_1 = 0x1000, + FX_ED_FLAG_PLAY_ON_TOUCH = 0x2000, + FX_ED_FLAG_PLAY_ON_DEATH = 0x4000, + FX_ED_FLAG_PLAY_ON_RUN = 0x8000, + FX_ED_FLAG_BOUNDING_SPHERE = 0x10000, + FX_ED_FLAG_USE_ITEM_CLIP = 0x20000, + FX_ED_FLAG_FADE_IN_DECAL = 0x40000, + FX_ED_FLAG_DISABLED = 0x80000000, + }; + + enum FxElemDefFlags + { + FX_ELEM_SPAWN_RELATIVE_TO_EFFECT = 0x2, + FX_ELEM_SPAWN_FRUSTUM_CULL = 0x4, + FX_ELEM_RUNNER_USES_RAND_ROT = 0x8, + FX_ELEM_SPAWN_OFFSET_NONE = 0x0, + FX_ELEM_SPAWN_OFFSET_SPHERE = 0x10, + FX_ELEM_SPAWN_OFFSET_CYLINDER = 0x20, + FX_ELEM_SPAWN_OFFSET_MASK = 0x30, + FX_ELEM_RUN_RELATIVE_TO_WORLD = 0x0, + FX_ELEM_RUN_RELATIVE_TO_SPAWN = 0x40, + FX_ELEM_RUN_RELATIVE_TO_EFFECT = 0x80, + FX_ELEM_RUN_RELATIVE_TO_OFFSET = 0xC0, + FX_ELEM_RUN_MASK = 0xC0, + FX_ELEM_USE_COLLISION = 0x100, + FX_ELEM_DIE_ON_TOUCH = 0x200, + FX_ELEM_DRAW_PAST_FOG = 0x400, + FX_ELEM_DRAW_WITH_VIEWMODEL = 0x800, + FX_ELEM_BLOCK_SIGHT = 0x1000, + FX_ELEM_DRAW_IN_THERMAL_VIEW_ONLY = 0x2000, + FX_ELEM_TRAIL_ORIENT_BY_VELOCITY = 0x4000, + FX_ELEM_EMIT_ORIENT_BY_ELEM = 0x8000, + FX_ELEM_HAS_VELOCITY_GRAPH_LOCAL = 0x1000000, + FX_ELEM_HAS_VELOCITY_GRAPH_WORLD = 0x2000000, + FX_ELEM_HAS_GRAVITY = 0x4000000, + FX_ELEM_USE_MODEL_PHYSICS = 0x8000000, + FX_ELEM_NONUNIFORM_SCALE = 0x10000000, + FX_ELEM_CLOUD_SHAPE_CUBE = 0x0, + FX_ELEM_CLOUD_SHAPE_SPHERE_LARGE = 0x20000000, + FX_ELEM_CLOUD_SHAPE_SPHERE_MEDIUM = 0x40000000, + FX_ELEM_CLOUD_SHAPE_SPHERE_SMALL = 0x60000000, + FX_ELEM_CLOUD_SHAPE_MASK = 0x60000000, + FX_ELEM_FOUNTAIN_DISABLE_COLLISION = 0x80000000, + }; + + struct FxElemDef // 0xFC + { + int flags; + FxSpawnDef spawn; + FxFloatRange spawnRange; + FxFloatRange fadeInRange; + FxFloatRange fadeOutRange; + float spawnFrustumCullRadius; + FxIntRange spawnDelayMsec; + FxIntRange lifeSpanMsec; + FxFloatRange spawnOrigin[3]; + FxFloatRange spawnOffsetRadius; + FxFloatRange spawnOffsetHeight; + FxFloatRange spawnAngles[3]; + FxFloatRange angularVelocity[3]; + FxFloatRange initialRotation; + FxFloatRange gravity; + FxFloatRange reflectionFactor; + FxElemAtlas atlas; + char elemType; + char visualCount; + char velIntervalCount; + char visStateIntervalCount; + FxElemVelStateSample* velSamples; // count = velIntervalCount + FxElemVisStateSample* visSamples; // count = visStateIntervalCount + FxElemDefVisuals visuals; + //If elemType is 0xB, then use markVisuals + //If elemType is not 0xB and visualCount == 1, then use visual + //If elemType is not 0xB and visualCount != 1, then use visualsArray + float collMins[3]; + float collMaxs[3]; + FxEffectDefRef* effectOnImpact; + FxEffectDefRef* effectOnDeath; + FxEffectDefRef* effectEmitted; + FxFloatRange emitDist; + FxFloatRange emitDistVariance; + FxElemExtendedDefPtr extended; + //If elemType == 3, then use trailDef + //If elemType == 6, then use sparkFountain + //If elemType != 3 && elemType != 6 use unknownDef (size = 1) + char sortOrder; + char lightingFrac; + char useItemClip; + char fadeInfo; + int pad; // IW5 only + + Json ToJson() + { + Json data; + + JSON_STRUCT_REF(spawnRange); + JSON_STRUCT_REF(fadeInRange); + JSON_STRUCT_REF(fadeOutRange); + JSON_STRUCT_REF(spawnDelayMsec); + JSON_STRUCT_REF(lifeSpanMsec); + JSON_STRUCT_REF(spawnOffsetRadius); + JSON_STRUCT_REF(spawnOffsetHeight); + JSON_STRUCT_REF(initialRotation); + JSON_STRUCT_REF(gravity); + JSON_STRUCT_REF(reflectionFactor); + JSON_STRUCT_REF(atlas); + JSON_STRUCT_REF(emitDist); + JSON_STRUCT_REF(emitDistVariance); + JSON_STRUCT_ARR(spawnOrigin, 3); + JSON_STRUCT_ARR(spawnAngles, 3); + JSON_STRUCT_ARR(angularVelocity, 3); + + return data; + } + }; + + struct FxEffectDef + { + const char* name; + int flags; + int totalSize; + int msecLoopingLife; + int elemDefCountLooping; + int elemDefCountOneShot; + int elemDefCountEmission; + char pad[20]; + FxElemDef* elemDefs; //Count = elemDefCountOneShot + elemDefCountEmission + elemDefCountLooping + + Json ToJson() + { + Json data; + + JSON_STRING(name); + + JSON_FIELD(flags); + JSON_FIELD(totalSize); + JSON_FIELD(msecLoopingLife); + JSON_FIELD(elemDefCountLooping); + JSON_FIELD(elemDefCountOneShot); + JSON_FIELD(elemDefCountEmission); + + JSON_STRUCT_ARR(elemDefs, elemDefCountLooping + elemDefCountOneShot + elemDefCountEmission); + + return data; + } + }; + + // ClipMap +#pragma pack(push, 4) + struct cStaticModel_s + { + XModel* xmodel; + float origin[3]; + float invScaledAxis[3][3]; + float absmin[3]; + float absmax[3]; + }; + + struct dmaterial_t + { + char* material; + int surfaceFlags; + int contentFlags; + }; + + struct cNode_t + { + cplane_s* plane; + __int16 children[2]; + + Json ToJson() + { + Json data; + + if (plane) + { + data["cPlane"] = plane->ToJson(); + } + else + { + data["cPlane"] = nullptr; + } + + data["children"][0] = children[0]; + data["children"][1] = children[1]; + + return data; + } + }; + + struct cLeaf_t + { + unsigned __int16 firstCollAabbIndex; // + 0 + unsigned __int16 collAabbCount; // + 2 + int brushContents; // + 6 + int terrainContents; // + 10 + float mins[3]; // + 22 + float maxs[3]; // + 34 + int leafBrushNode; // + 38 + }; + + struct CollisionBorder + { + float distEq[3]; + float zBase; + float zSlope; + float start; + float length; + }; + + struct cLeafBrushNodeLeaf_t + { + unsigned __int16* brushes; + }; + + struct cLeafBrushNodeChildren_t + { + unsigned __int16 childOffset[6]; + }; + + union cLeafBrushNodeData_t + { + cLeafBrushNodeLeaf_t leaf; + cLeafBrushNodeChildren_t children; + }; + + struct cLeafBrushNode_s + { + char axis; + short leafBrushCount; + int contents; + cLeafBrushNodeData_t data; + }; + + struct CollisionPartition + { + char triCount; + char borderCount; + int firstTri; + CollisionBorder* borders; + }; + + union CollisionAabbTreeIndex + { + int firstChildIndex; + int partitionIndex; + }; + + struct CollisionAabbTree + { + float origin[3]; + unsigned __int16 materialIndex; + unsigned __int16 childCount; + float halfSize[3]; + CollisionAabbTreeIndex u; + }; + + typedef char cbrushedge_t; + + struct cbrush_t + { + unsigned __int16 numsides; + unsigned __int16 glassPieceIndex; + cbrushside_t* sides; + cbrushedge_t* edge; + __int16 axialMaterialNum[2][3]; + char firstAdjacentSideOffsets[2][3]; + char edgeCount[2][3]; + }; + + enum DynEntityType + { + DYNENT_TYPE_INVALID = 0x0, + DYNENT_TYPE_CLUTTER = 0x1, + DYNENT_TYPE_DESTRUCT = 0x2, + DYNENT_TYPE_COUNT = 0x3, + }; + + struct GfxPlacement + { + float quat[4]; + float origin[3]; + }; + + struct PhysMass + { + float centerOfMass[3]; + float momentsOfInertia[3]; + float productsOfInertia[3]; + // int contents; + }; + + struct DynEntityHingeDef + { + float axisOrigin[3]; + float axisDir[3]; + bool isLimited; + float angleMin; + float angleMax; + float momentOfInertia; + float friction; + }; + + struct DynEntityDef_IW4 + { + union + { + struct + { + DynEntityType type; + GfxPlacement pose; + XModel* xModel; + unsigned __int16 brushModel; + unsigned __int16 physicsBrushModel; + FxEffectDef* destroyFx; + PhysPreset* physPreset; + int health; + }; + + char _portpad[52]; + }; + + PhysMass mass; + }; + + struct DynEntityDef + { + union + { + struct + { + DynEntityType type; + GfxPlacement pose; + XModel* xModel; + unsigned __int16 brushModel; + unsigned __int16 physicsBrushModel; + FxEffectDef* destroyFx; + PhysPreset* physPreset; + int health; + }; + + char _portpad[52]; + }; + + DynEntityHingeDef* hinge; + PhysMass mass; + }; + + struct DynEntityPose + { + GfxPlacement pose; + float radius; + }; + + struct DynEntityClient + { + int physObjId; + unsigned __int16 flags; + unsigned __int16 lightingHandle; + int health; + int contents; + }; + + struct DynEntityColl + { + unsigned __int16 sector; + unsigned __int16 nextEntInSector; + float linkMins[2]; + float linkMaxs[2]; + }; + + struct Stage + { + char* name; + float origin[3]; + unsigned __int16 triggerIndex; + char sunPrimaryLightIndex; + }; + + struct SModelAabbNode + { + Bounds bounds; + short firstChild; + short childCount; + }; + + struct ClipInfo + { + int numCPlanes; + cplane_s* cPlanes; + int numMaterials; + dmaterial_t* materials; + int numCBrushSides; + cbrushside_t* cBrushSides; + int numCBrushEdges; + cbrushedge_t* cBrushEdges; + int numCLeafBrushNodes; + cLeafBrushNode_s* cLeafBrushNodes; // cmodels use this? + int numLeafBrushes; + short* leafBrushes; + short numBrushes; + cbrush_t* brushes; + Bounds* brushBounds; + int* brushContents; + }; + + struct cmodel_t + { + union + { + char _portpad0[28]; + + struct + { + Bounds bounds; + float radius; + }; + }; + + ClipInfo* info; + + union + { + char _portpad1[40]; + + struct + { + cLeaf_t leaf; + }; + }; + }; + + struct cmodel_t_IW4 + { + union + { + char _portpad0[28]; + + struct + { + Bounds bounds; + float radius; + }; + }; + + union + { + char _portpad1[40]; + + struct + { + cLeaf_t leaf; + }; + }; + }; + + struct clipMap_t + { + const char* name; + bool isInUse; + char pad1[3]; + ClipInfo info; + ClipInfo* pInfo; + int numStaticModels; + cStaticModel_s* staticModelList; + int numCNodes; + cNode_t* cNodes; + int numCLeaf; + cLeaf_t* cLeaf; + int numVerts; + VecInternal<3>* verts; + int numTriIndices; + short* triIndices; + char* triEdgeIsWalkable; //Size = ((triCount << 1) + triCount + 0x1F) >> 3 << 2 + int numCollisionBorders; + CollisionBorder* collisionBorders; + int numCollisionPartitions; + CollisionPartition* collisionPartitions; + int numCollisionAABBTrees; + CollisionAabbTree* collisionAABBTrees; + int numCModels; + cmodel_t* cModels; + MapEnts* mapEnts; + Stage* stages; + unsigned char stageCount; + char pad2[3]; + MapTriggers trigger; + short smodelNodeCount; + SModelAabbNode* smodelNodes; + unsigned __int16 dynEntCount[2]; + DynEntityDef* dynEntDefList[2]; + DynEntityPose* dynEntPoseList[2]; + DynEntityClient* dynEntClientList[2]; + DynEntityColl* dynEntCollList[2]; + char pad3[20]; + std::uint32_t isPlutoniumMap; + + void test() + { + + } + }; +#pragma pack(pop) + +#pragma pack(push, 1) + struct ComPrimaryLight + { + union + { + char _portpad0[28]; + + struct + { + char type; + char canUseShadowMap; + char exponent; + char unused; + float color[3]; + float dir[3]; + }; + }; + + float up[3]; + + union + { + char _portpad1[40]; + + struct + { + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + float cosHalfFovExpanded; + float rotationLimit; + float translationLimit; + const char* defName; + }; + }; + + + Json ToJson(bool fromIW5 = true) + { + Json data; + + // if the data is being dumped from iw4, just dump the string + /*if (!fromIW5) + {*/ + JSON_STRING(defName); + //} + //else + //{ + // // if it has an empty defname + // if (this->defName && !strlen(this->defName)) + // { + // data["defName"] = "iw5_lightdef_noname"; + // } + // // if it has a defname + // else if (this->defName && strlen(this->defName)) + // { + // data["defName"] = "iw5_"s + this->defName; + // } + // // if it has no defname + // else + // { + // data["defName"] = ""; + // } + //} + + JSON_FIELD_ARR(color, 3); + JSON_FIELD_ARR(dir, 3); + JSON_FIELD_ARR(up, 3); + JSON_FIELD_ARR(origin, 3); + + JSON_FIELD(type); + JSON_FIELD(canUseShadowMap); + JSON_FIELD(exponent); + JSON_FIELD(unused); + JSON_FIELD(radius); + JSON_FIELD(cosHalfFovOuter); + JSON_FIELD(cosHalfFovInner); + JSON_FIELD(cosHalfFovExpanded); + JSON_FIELD(rotationLimit); + JSON_FIELD(translationLimit); + + return data; + } + }; +#pragma pack(pop) + + struct ComWorld + { + const char* name; + int isInUse; + unsigned int primaryLightCount; + ComPrimaryLight* primaryLights; + + Json ToJson(bool fromIW5 = true) + { + Json data; + + JSON_STRING(name); + + JSON_FIELD(isInUse); + JSON_FIELD(primaryLightCount); + + JSON_STRUCT_ARR(primaryLights, this->primaryLightCount); + + for (int idata = 0; idata < this->primaryLightCount; idata++) + { + data["primaryLights"][idata] = this->primaryLights[idata].ToJson(fromIW5); + } + + return data; + } + }; + +#pragma pack(push, 4) + struct GfxLightImage + { + GfxImage* image; + char samplerState; + + Json ToJson() + { + Json data; + + JSON_ASSET(image, GfxImage); + JSON_FIELD(samplerState); + + return data; + } + }; +#pragma pack(pop) + + struct GfxLightDef + { + const char* name; + GfxLightImage attenuation; + GfxLightImage cucoloris; + int lmapLookupStart; + + Json ToJson() + { + Json data; + + JSON_STRING(name); + JSON_STRUCT_REF(attenuation); + JSON_STRUCT_REF(cucoloris); + JSON_FIELD(lmapLookupStart); + + return data; + } + }; + + struct AddonMapEnts + { + const char* name; + char* entityString; + int numEntityChars; + MapTriggers trigger; // See the MapEnts Asset + ClipInfo* info; // See the Collision Map Asset + unsigned int numSubModels; + void* cmodels; // See the Collision Map Asset + void* models; // See the Graphics Map Asset + }; + + struct G_GlassName + { + char* nameStr; + unsigned __int16 name; + unsigned __int16 pieceCount; + unsigned __int16* pieceIndices; + }; + +#pragma pack(push, 2) + struct G_GlassPiece + { + unsigned __int16 damageTaken; + unsigned __int16 collapseTime; + int lastStateChangeTime; + char impactDir; + char impactPos[2]; + }; +#pragma pack(pop) + + struct G_GlassData + { + G_GlassPiece* glassPieces; + unsigned int pieceCount; + unsigned __int16 damageToWeaken; + unsigned __int16 damageToDestroy; + unsigned int glassNameCount; + G_GlassName* glassNames; + char pad[108]; + }; + + struct GlassWorld + { + const char* name; + G_GlassData* g_glassData; + }; + + struct ScriptFile + { + const char* name; + unsigned int compressedLen; + unsigned int len; + unsigned int bytecodeLen; + const char* buffer; + char* bytecode; + }; + +#pragma pack(push, 2) + struct Glyph + { + unsigned __int16 letter; + char x0; + char y0; + char dx; + char pixelWidth; + char pixelHeight; + float s0; + float t0; + float s1; + float t1; + }; +#pragma pack(pop) + + struct Font_s + { + const char* name; // 0 + int pixelHeight; // 4 + int glyphCount; // 8 + Material* material; // 12 + Material* glowMaterial; // 16 + Glyph* glyphs; // 20 + }; + + // GfxMap +#pragma pack(push, 4) + struct GfxSky + { + std::uint32_t skySurfCount; + std::uint32_t* skyStartSurfs; + GfxImage* skyImage; + char skySamplerState; + char pad[3]; + }; + + struct GfxWorldDpvsPlanes + { + int cellCount; + cplane_s* planes; + unsigned __int16* nodes; + unsigned char* sceneEntCellBits; //Size = cellCount << 11 + }; + + struct GfxCellTreeCount + { + int aabbTreeCount; + }; + + struct GfxAabbTree + { + float mins[3]; // 12 + float maxs[3]; // 12 + int unkn; + unsigned __int16 childCount; // 2 + unsigned __int16 surfaceCount; // 2 + unsigned __int16 startSurfIndex; // 2 + unsigned __int16 smodelIndexCount; // 2 + unsigned __int16* smodelIndexes; // 4 + int childrenOffset; // 4 + }; // Size: 0x2C + + struct GfxCellTree + { + // Best struct ever + GfxAabbTree* aabbtree; + }; + + struct GfxPortalWritable + { + bool isQueued; + bool isAncestor; + char recursionDepth; + char hullPointCount; + //float(*hullPoints)[2]; + }; + + struct DpvsPlane + { + float coeffs[4]; + char side[3]; + }; + + struct GfxPortal // Needs to be investigated + { + GfxPortalWritable writable; // 4 + DpvsPlane plane; // 20 + int unknown1; + float (*vertices)[3]; + short unknown2; + char vertexCount; + //char unknown2[2]; + float hullAxis[2][3]; + }; + + struct GfxCell_IW4 + { + float mins[3]; + float maxs[3]; + int portalCount; + GfxPortal* portals; + char reflectionProbeCount; + char* reflectionProbes; + }; + + struct GfxCell + { + float mins[3]; + float maxs[3]; + int portalCount; + GfxPortal* portals; + char reflectionProbeCount; + char* reflectionProbes; + char reflectionProbeReferenceCount; + char* reflectionProbeReferences; + }; + + struct GfxReflectionProbe + { + float offset[3]; + }; + + typedef char GfxTexture[0x04]; + + struct GfxLightmapArray + { + GfxImage* primary; + GfxImage* secondary; + }; + + struct GfxWorldVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + float texCoord[2]; + float lmapCoord[2]; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct GfxWorldVertexData + { + GfxWorldVertex* vertices; + void* worldVb; // D3DVertexBuffer + }; + + struct GfxWorldVertexLayerData + { + char* data; + void* layerVb; // D3DVertexBuffer + }; + + struct GfxReflectionProbeReferenceOrigin + { + float origin[3]; + }; + + struct GfxWorldDraw_IW4 + { + union + { + char _portpad0[16]; + + struct + { + unsigned int reflectionProbeCount; // 4 + GfxImage* * reflectionImages; // 4 + GfxReflectionProbe* reflectionProbes; // 4 + GfxTexture* reflectionProbeTextures; //Count = reflectionProbeCount // 4 + }; + }; + + union + { + char _portpad1[56]; + + struct + { + int lightmapCount; // 4 + GfxLightmapArray* lightmaps; // 4 + GfxTexture* lightmapPrimaryTextures; //Count = lightmapCount // 4 + GfxTexture* lightmapSecondaryTextures; //Count = lightmapCount // 4 + GfxImage* skyImage; // 4 + GfxImage* outdoorImage; // 4 + unsigned int vertexCount; // 4 + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + int indexCount; + unsigned __int16* indices; + }; + }; + }; + + struct GfxWorldDraw + { + union + { + char _portpad0[16]; + + struct + { + unsigned int reflectionProbeCount; // 4 + GfxImage* * reflectionImages; // 4 + GfxReflectionProbe* reflectionProbes; // 4 + GfxTexture* reflectionProbeTextures; //Count = reflectionProbeCount // 4 + }; + }; + + int reflectionProbeReferenceCount; + GfxReflectionProbeReferenceOrigin* reflectionProbeReferenceOrigins; + char* reflectionProbeReferences; + + union + { + char _portpad1[56]; + + struct + { + int lightmapCount; // 4 + GfxLightmapArray* lightmaps; // 4 + GfxTexture* lightmapPrimaryTextures; //Count = lightmapCount // 4 + GfxTexture* lightmapSecondaryTextures; //Count = lightmapCount // 4 + GfxImage* skyImage; // 4 + GfxImage* outdoorImage; // 4 + unsigned int vertexCount; // 4 + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + int indexCount; + unsigned __int16* indices; + }; + }; + }; + + struct GfxLightGridEntry + { + unsigned __int16 colorsIndex; + char primaryLightIndex; + char needsTrace; + }; + + struct GfxLightGridColors + { + char rgb[56][3]; + }; + + struct GfxLightGrid + { + bool hasLightRegions; // 4 + unsigned int sunPrimaryLightIndex; // 4 + unsigned __int16 mins[3]; // 6 + unsigned __int16 maxs[3]; // 6 + unsigned int rowAxis; // 4 + unsigned int colAxis; // 4 + unsigned __int16* rowDataStart; + // Size: (varGfxLightGrid->maxs[varGfxLightGrid->rowAxis] - varGfxLightGrid->mins[varGfxLightGrid->rowAxis] + 1) * 2 + unsigned int rawRowDataSize; + char* rawRowData; + unsigned int entryCount; + GfxLightGridEntry* entries; + unsigned int colorCount; + GfxLightGridColors* colors; + }; + + struct GfxBrushModelWritable + { + float mins[3]; + float maxs[3]; + float mip1radiusSq; + }; + + struct GfxBrushModel + { + GfxBrushModelWritable writable; + float bounds[2][3]; + std::uint32_t surfaceCount; + std::uint32_t startSurfIndex; + }; + + struct MaterialMemory + { + Material* material; + std::uint32_t memory; + }; + + struct sunflare_t + { + bool hasValidData; + Material* spriteMaterial; + Material* flareMaterial; + float spriteSize; + float flareMinSize; + float flareMinDot; + float flareMaxSize; + float flareMaxDot; + float flareMaxAlpha; + int flareFadeInTime; + int flareFadeOutTime; + float blindMinDot; + float blindMaxDot; + float blindMaxDarken; + int blindFadeInTime; + int blindFadeOutTime; + float glareMinDot; + float glareMaxDot; + float glareMaxLighten; + int glareFadeInTime; + int glareFadeOutTime; + float sunFxPosition[3]; + }; + + struct XModelDrawInfo + { + unsigned __int16 lod; + unsigned __int16 surfId; + }; + + struct GfxSceneDynModel + { + XModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct BModelDrawInfo + { + unsigned __int16 surfId; + }; + + struct GfxSceneDynBrush + { + BModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct GfxShadowGeometry + { + unsigned __int16 surfaceCount; + unsigned __int16 smodelCount; + unsigned __int16* sortedSurfIndex; + unsigned __int16* smodelIndex; + }; + + struct GfxLightRegionAxis + { + float dir[3]; + float midPoint; + float halfSize; + }; + + struct GfxLightRegionHull + { + float kdopMidPoint[9]; + float kdopHalfSize[9]; + unsigned int axisCount; + GfxLightRegionAxis* axis; + }; + + struct GfxLightRegion + { + unsigned int hullCount; + GfxLightRegionHull* hulls; + }; + + struct GfxStaticModelInst + { + float mins[3]; + float maxs[3]; + float lightingOrigin[3]; + }; + + struct srfTriangles_t + { + int vertexLayerData; + int firstVertex; + unsigned __int16 vertexCount; + unsigned __int16 triCount; + int baseIndex; + }; + + struct GfxSurface + { + srfTriangles_t tris; + Material* material; + char lightmapIndex; + char reflectionProbeIndex; + char primaryLightIndex; + bool castsSunShadow; + }; + + struct GfxCullGroup + { + float mins[3]; + float maxs[3]; + //int surfaceCount; + //int startSurfIndex; + }; + + struct GfxDrawSurfFields + { + __int64 _bf0; + }; + + union GfxDrawSurf + { + GfxDrawSurfFields fields; + unsigned __int64 packed; + }; + + struct GfxPackedPlacement + { + float origin[3]; + float axis[3][3]; + float scale; + }; + + struct GfxStaticModelDrawInst + { + GfxPackedPlacement placement; + XModel* model; + unsigned __int16 smodelCacheIndex[4]; + float cullDist; + char reflectionProbeIndex; + char primaryLightIndex; + unsigned __int16 lightingHandle; + char flags; + }; + + struct GfxWorldDpvsDynamic + { + unsigned int dynEntClientWordCount[2]; + unsigned int dynEntClientCount[2]; + unsigned int* dynEntCellBits[2]; + char* dynEntVisData[2][3]; + }; + + struct GfxWorldDpvsStatic + { + unsigned int smodelCount; + unsigned int staticSurfaceCount; + unsigned int staticSurfaceCountNoDecal; + unsigned int litOpaqueSurfsBegin; + unsigned int litOpaqueSurfsEnd; + unsigned int litTransSurfsBegin; + unsigned int litTransSurfsEnd; + unsigned int shadowCasterSurfsBegin; + unsigned int shadowCasterSurfsEnd; + unsigned int emissiveSurfsBegin; + unsigned int emissiveSurfsEnd; + unsigned int smodelVisDataCount; + unsigned int surfaceVisDataCount; + char* smodelVisData[3]; + char* surfaceVisData[3]; + unsigned __int16* sortedSurfIndex; + GfxStaticModelInst* smodelInsts; + GfxSurface* surfaces; + GfxCullGroup* cullGroups; + GfxStaticModelDrawInst* smodelDrawInsts; + GfxDrawSurf* surfaceMaterials; + unsigned int* surfaceCastsSunShadow; + volatile int usageCount; + }; + + struct GfxHeroLight + { + char type; + char pad[3]; + float color[3]; + float dir[3]; + float up[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + int exponent; + }; + + struct GfxWorld + { + const char* name; // 4 + const char* baseName; // 4 + std::uint32_t planeCount; // 4 + std::uint32_t nodeCount; // 4 // = 16 + std::uint32_t indexCount; // 4 + std::uint32_t skyCount; // 4 + GfxSky* skies; // 4 + std::uint32_t sunPrimaryLightIndex; // 4 // = 32 + std::uint32_t primaryLightCount; // 4 + std::uint32_t primaryLightEnvCount; // 4 + char unknown1[12]; // 16 // = 56 // Sortkeys. Don't know which ones though + GfxWorldDpvsPlanes dpvsPlanes; // 16 + GfxCellTreeCount* aabbTreeCounts; // Size: 4 * dpvsPlanes.cellCount // 4 + GfxCellTree* aabbTree; // 4 + GfxCell* cells; // 4 // = 80 + GfxWorldDraw worldDraw; // 72 + GfxLightGrid lightGrid; // 56 // = 208 + std::uint32_t modelCount; // 4 + GfxBrushModel* models; // 4 // = 216 + float mins[3]; // 12 + float maxs[3]; // 12 + std::uint32_t checksum; // 4 + std::uint32_t materialMemoryCount; // 4 // = 248 + MaterialMemory* materialMemory; // 4 + sunflare_t sun; // 96 // = 348 + float outdoorLookupMatrix[4][4]; // 64 + GfxImage* outdoorImage; // 4 // = 416 + std::uint32_t* cellCasterBits[2]; // 8 + GfxSceneDynModel* sceneDynModel; // 4 + GfxSceneDynBrush* sceneDynBrush; // 4 // = 432 + unsigned char* primaryLightEntityShadowVis; + std::uint32_t* primaryLightDynEntShadowVis[2]; + char* primaryLightForModelDynEnt; + GfxShadowGeometry* shadowGeom; + GfxLightRegion* lightRegion; + GfxWorldDpvsStatic dpvs; + GfxWorldDpvsDynamic dpvsDyn; + std::uint32_t mapVtxChecksum; + std::uint32_t heroLightCount; + GfxHeroLight* heroLights; + char fogTypesAllowed; + }; +#pragma pack(pop) + + enum weapOverlayReticle_t : int + { + WEAPOVERLAYRETICLE_NONE = 0x0, + WEAPOVERLAYRETICLE_CROSSHAIR = 0x1, + WEAPOVERLAYRETICLE_NUM = 0x2 + }; + + enum weapOverlayInterface_t : int + { + WEAPOVERLAYINTERFACE_NONE = 0x0, + WEAPOVERLAYINTERFACE_JAVELIN = 0x1, + WEAPOVERLAYINTERFACE_TURRETSCOPE = 0x2, + WEAPOVERLAYINTERFACECOUNT = 0x3 + }; + + enum activeReticleType_t : int + { + VEH_ACTIVE_RETICLE_NONE = 0, + VEH_ACTIVE_RETICLE_PIP_ON_A_STICK = 1, + VEH_ACTIVE_RETICLE_BOUNCING_DIAMOND = 2, + VEH_ACTIVE_RETICLE_MAX + }; + + enum WeapStickinessType : int + { + WEAPSTICKINESS_NONE = 0x0, + WEAPSTICKINESS_ALL = 0x1, + WEAPSTICKINESS_ALL_ORIENT = 0x2, + WEAPSTICKINESS_GROUND = 0x3, + WEAPSTICKINESS_GROUND_WITH_YAW = 0x4, + WEAPSTICKINESS_KNIFE = 0x5, + WEAPSTICKINESS_COUNT = 0x6 + }; + + enum guidedMissileType_t : int + { + MISSILE_GUIDANCE_NONE = 0x0, + MISSILE_GUIDANCE_SIDEWINDER = 0x1, + MISSILE_GUIDANCE_HELLFIRE = 0x2, + MISSILE_GUIDANCE_JAVELIN = 0x3, + MISSILE_GUIDANCE_MAX + }; + + union snd_alias_list_name + { + const char* name; + snd_alias_list_t* asset; + }; + + struct ADSOverlay + { + Material* shader; + Material* shaderLowRes; + Material* shaderEMP; + Material* shaderEMPLowRes; + weapOverlayReticle_t reticle; + float width; + float height; + float widthSplitscreen; + float heightSplitscreen; + }; + + typedef BYTE _BYTE; + +#pragma pack(push, 4) + struct StateTimers + { + int iFireDelay; + int iMeleeDelay; + int meleeChargeDelay; + int iDetonateDelay; + int iRechamberTime; + int rechamberTimeOneHanded; + int iRechamberBoltTime; + int iHoldFireTime; + int iDetonateTime; + int iMeleeTime; + int meleeChargeTime; + int iReloadTime; + int reloadShowRocketTime; + int iReloadEmptyTime; + int iReloadAddTime; + int iReloadStartTime; + int iReloadStartAddTime; + int iReloadEndTime; + int iDropTime; + int iRaiseTime; + int iAltDropTime; + int quickDropTime; + int quickRaiseTime; + int iBreachRaiseTime; + int iEmptyRaiseTime; + int iEmptyDropTime; + int sprintInTime; + int sprintLoopTime; + int sprintOutTime; + int stunnedTimeBegin; + int stunnedTimeLoop; + int stunnedTimeEnd; + int nightVisionWearTime; + int nightVisionWearTimeFadeOutEnd; + int nightVisionWearTimePowerUp; + int nightVisionRemoveTime; + int nightVisionRemoveTimePowerDown; + int nightVisionRemoveTimeFadeInStart; + int fuseTime; + int aiFuseTime; + int blastFrontTime; + int blastRightTime; + int blastBackTime; + int blastLeftTime; + int raiseInterruptableTime; + int firstRaiseInterruptableTime; + int reloadInterruptableTime; + int reloadEmptyInterruptableTime; + int fireInterruptableTime; + }; + struct WeaponDef + { + const char* szOverlayName; + XModel** gunXModel; + XModel* handXModel; + const char** szXAnimsRightHanded; + const char** szXAnimsLeftHanded; + const char* szModeName; + __int16* notetrackSoundMapKeys; + __int16* notetrackSoundMapValues; + __int16* notetrackRumbleMapKeys; + __int16* notetrackRumbleMapValues; + int playerAnimType; + int weapType; + int weapClass; + int penetrateType; + int inventoryType; + int fireType; + int offhandClass; + int stance; + FxEffectDef* viewFlashEffect; + FxEffectDef* worldFlashEffect; + union + { + struct + { + snd_alias_list_t* pickupSound; + snd_alias_list_t* pickupSoundPlayer; + snd_alias_list_t* ammoPickupSound; + snd_alias_list_t* ammoPickupSoundPlayer; + snd_alias_list_t* projectileSound; + snd_alias_list_t* pullbackSound; + snd_alias_list_t* pullbackSoundPlayer; + snd_alias_list_t* fireSound; + snd_alias_list_t* fireSoundPlayer; + snd_alias_list_t* fireSoundPlayerAkimbo; + snd_alias_list_t* fireLoopSound; + snd_alias_list_t* fireLoopSoundPlayer; + snd_alias_list_t* fireStopSound; + snd_alias_list_t* fireStopSoundPlayer; + snd_alias_list_t* fireLastSound; + snd_alias_list_t* fireLastSoundPlayer; + snd_alias_list_t* emptyFireSound; + snd_alias_list_t* emptyFireSoundPlayer; + snd_alias_list_t* meleeSwipeSound; + snd_alias_list_t* meleeSwipeSoundPlayer; + snd_alias_list_t* meleeHitSound; + snd_alias_list_t* meleeMissSound; + snd_alias_list_t* rechamberSound; + snd_alias_list_t* rechamberSoundPlayer; + snd_alias_list_t* reloadSound; + snd_alias_list_t* reloadSoundPlayer; + snd_alias_list_t* reloadEmptySound; + snd_alias_list_t* reloadEmptySoundPlayer; + snd_alias_list_t* reloadStartSound; + snd_alias_list_t* reloadStartSoundPlayer; + snd_alias_list_t* reloadEndSound; + snd_alias_list_t* reloadEndSoundPlayer; + snd_alias_list_t* detonateSound; + snd_alias_list_t* detonateSoundPlayer; + snd_alias_list_t* nightVisionWearSound; + snd_alias_list_t* nightVisionWearSoundPlayer; + snd_alias_list_t* nightVisionRemoveSound; + snd_alias_list_t* nightVisionRemoveSoundPlayer; + snd_alias_list_t* altSwitchSound; + snd_alias_list_t* altSwitchSoundPlayer; + snd_alias_list_t* raiseSound; + snd_alias_list_t* raiseSoundPlayer; + snd_alias_list_t* firstRaiseSound; + snd_alias_list_t* firstRaiseSoundPlayer; + snd_alias_list_t* putawaySound; + snd_alias_list_t* putawaySoundPlayer; + snd_alias_list_t* scanSound; + snd_alias_list_t* changeVariableZoomSound; + }; + snd_alias_list_t* sounds[48]; + }; + snd_alias_list_t** bounceSound; + snd_alias_list_t** rollingSound; + FxEffectDef* viewShellEjectEffect; + FxEffectDef* worldShellEjectEffect; + FxEffectDef* viewLastShotEjectEffect; + FxEffectDef* worldLastShotEjectEffect; + Material* reticleCenter; + Material* reticleSide; + int iReticleCenterSize; + int iReticleSideSize; + int iReticleMinOfs; + activeReticleType_t activeReticleType; + float vStandMove[3]; + float vStandRot[3]; + float strafeMove[3]; + float strafeRot[3]; + float vDuckedOfs[3]; + float vDuckedMove[3]; + float vDuckedRot[3]; + float vProneOfs[3]; + float vProneMove[3]; + float vProneRot[3]; + float fPosMoveRate; + float fPosProneMoveRate; + float fStandMoveMinSpeed; + float fDuckedMoveMinSpeed; + float fProneMoveMinSpeed; + float fPosRotRate; + float fPosProneRotRate; + float fStandRotMinSpeed; + float fDuckedRotMinSpeed; + float fProneRotMinSpeed; + XModel** worldModel; + XModel* worldClipModel; + XModel* rocketModel; + XModel* knifeModel; + XModel* worldKnifeModel; + Material* hudIcon; + weaponIconRatioType_t hudIconRatio; + Material* pickupIcon; + weaponIconRatioType_t pickupIconRatio; + Material* ammoCounterIcon; + weaponIconRatioType_t ammoCounterIconRatio; + ammoCounterClipType_t ammoCounterClip; + int iStartAmmo; + const char* szAmmoName; + int iAmmoIndex; + const char* szClipName; + int iClipIndex; + int iMaxAmmo; + int shotCount; + const char* szSharedAmmoCapName; + int iSharedAmmoCapIndex; + int iSharedAmmoCap; + int damage; + int playerDamage; + int iMeleeDamage; + int iDamageType; + StateTimers stateTimers; + StateTimers akimboStateTimers; + float autoAimRange; + float aimAssistRange; + float aimAssistRangeAds; + float aimPadding; + float enemyCrosshairRange; + float moveSpeedScale; + float adsMoveSpeedScale; + float sprintDurationScale; + float fAdsZoomInFrac; + float fAdsZoomOutFrac; + ADSOverlay overlay; + int overlayInterface; + float fAdsBobFactor; + float fAdsViewBobMult; + float fHipSpreadStandMin; + float fHipSpreadDuckedMin; + float fHipSpreadProneMin; + float hipSpreadStandMax; + float hipSpreadDuckedMax; + float hipSpreadProneMax; + float fHipSpreadDecayRate; + float fHipSpreadFireAdd; + float fHipSpreadTurnAdd; + float fHipSpreadMoveAdd; + float fHipSpreadDuckedDecay; + float fHipSpreadProneDecay; + float fHipReticleSidePos; + float fAdsIdleAmount; + float fHipIdleAmount; + float adsIdleSpeed; + float hipIdleSpeed; + float fIdleCrouchFactor; + float fIdleProneFactor; + float fGunMaxPitch; + float fGunMaxYaw; + float adsIdleLerpStartTime; + float adsIdleLerpTime; + float swayMaxAngle; + float swayLerpSpeed; + float swayPitchScale; + float swayYawScale; + float swayHorizScale; + float swayVertScale; + float swayShellShockScale; + float adsSwayMaxAngle; + float adsSwayLerpSpeed; + float adsSwayPitchScale; + float adsSwayYawScale; + float adsSwayHorizScale; + float adsSwayVertScale; + float adsViewErrorMin; + float adsViewErrorMax; + PhysCollmap* physCollmap; + float dualWieldViewModelOffset; + weaponIconRatioType_t killIconRatio; + int iReloadAmmoAdd; + int iReloadStartAdd; + int ammoDropStockMin; + int ammoDropClipPercentMin; + int ammoDropClipPercentMax; + int iExplosionRadius; + int iExplosionRadiusMin; + int iExplosionInnerDamage; + int iExplosionOuterDamage; + float damageConeAngle; + float bulletExplDmgMult; + float bulletExplRadiusMult; + int iProjectileSpeed; + int iProjectileSpeedUp; + int iProjectileSpeedForward; + int iProjectileActivateDist; + float projLifetime; + float timeToAccelerate; + float projectileCurvature; + XModel* projectileModel; + int projExplosion; + FxEffectDef* projExplosionEffect; + FxEffectDef* projDudEffect; + snd_alias_list_t* projExplosionSound; + snd_alias_list_t* projDudSound; + WeapStickinessType stickiness; + float lowAmmoWarningThreshold; + float ricochetChance; + bool riotShieldEnableDamage; + int riotShieldHealth; + float riotShieldDamageMult; + float* parallelBounce; + float* perpendicularBounce; + FxEffectDef* projTrailEffect; + FxEffectDef* projBeaconEffect; + float vProjectileColor[3]; + guidedMissileType_t guidedMissileType; + float maxSteeringAccel; + int projIgnitionDelay; + FxEffectDef* projIgnitionEffect; + snd_alias_list_t* projIgnitionSound; + float fAdsAimPitch; + float fAdsCrosshairInFrac; + float fAdsCrosshairOutFrac; + int adsGunKickReducedKickBullets; + float adsGunKickReducedKickPercent; + float fAdsGunKickPitchMin; + float fAdsGunKickPitchMax; + float fAdsGunKickYawMin; + float fAdsGunKickYawMax; + float fAdsGunKickAccel; + float fAdsGunKickSpeedMax; + float fAdsGunKickSpeedDecay; + float fAdsGunKickStaticDecay; + float fAdsViewKickPitchMin; + float fAdsViewKickPitchMax; + float fAdsViewKickYawMin; + float fAdsViewKickYawMax; + float fAdsViewScatterMin; + float fAdsViewScatterMax; + float fAdsSpread; + int hipGunKickReducedKickBullets; + float hipGunKickReducedKickPercent; + float fHipGunKickPitchMin; + float fHipGunKickPitchMax; + float fHipGunKickYawMin; + float fHipGunKickYawMax; + float fHipGunKickAccel; + float fHipGunKickSpeedMax; + float fHipGunKickSpeedDecay; + float fHipGunKickStaticDecay; + float fHipViewKickPitchMin; + float fHipViewKickPitchMax; + float fHipViewKickYawMin; + float fHipViewKickYawMax; + float fHipViewScatterMin; + float fHipViewScatterMax; + float fightDist; + float maxDist; + const char* accuracyGraphName[2]; + vec2_t* accuracyGraphKnots; + vec2_t* originalAccuracyGraphKnots; + short accuracyGraphKnotCount; + short originalAccuracyGraphKnotCount; + int iPositionReloadTransTime; + float leftArc; + float rightArc; + float topArc; + float bottomArc; + float accuracy; + float aiSpread; + float playerSpread; + float minTurnSpeed[2]; + float maxTurnSpeed[2]; + float pitchConvergenceTime; + float yawConvergenceTime; + float suppressTime; + float maxRange; + float fAnimHorRotateInc; + float fPlayerPositionDist; + const char* szUseHintString; + const char* dropHintString; + int iUseHintStringIndex; + int dropHintStringIndex; + float horizViewJitter; + float vertViewJitter; + float scanSpeed; + float scanAccel; + int scanPauseTime; + const char* szScript; + float fOOPosAnimLength[2]; + int minDamage; + int minPlayerDamage; + float fMaxDamageRange; + float fMinDamageRange; + float destabilizationRateTime; + float destabilizationCurvatureMax; + int destabilizeDistance; + float* locationDamageMultipliers; + const char* fireRumble; + const char* meleeImpactRumble; + TracerDef* tracerType; + bool turretADSEnabled; + float turretADSTime; + float turretFov; + float turretFovADS; + float turretScopeZoomRate; + float turretScopeZoomMin; + float turretScopeZoomMax; + float turretOverheatUpRate; + float turretOverheatDownRate; + float turretOverheatPenalty; + snd_alias_list_t* turretOverheatSound; + FxEffectDef* turretOverheatEffect; + const char* turretBarrelSpinRumble; + float turretBarrelSpinSpeed; + float turretBarrelSpinUpTime; + float turretBarrelSpinDownTime; + snd_alias_list_t* turretBarrelSpinMaxSnd; + snd_alias_list_t* turretBarrelSpinUpSnd[4]; + snd_alias_list_t* turretBarrelSpinDownSnd[4]; + snd_alias_list_t* missileConeSoundAlias; + snd_alias_list_t* missileConeSoundAliasAtBase; + float missileConeSoundRadiusAtTop; + float missileConeSoundRadiusAtBase; + float missileConeSoundHeight; + float missileConeSoundOriginOffset; + float missileConeSoundVolumescaleAtCore; + float missileConeSoundVolumescaleAtEdge; + float missileConeSoundVolumescaleCoreSize; + float missileConeSoundPitchAtTop; + float missileConeSoundPitchAtBottom; + float missileConeSoundPitchTopSize; + float missileConeSoundPitchBottomSize; + float missileConeSoundCrossfadeTopSize; + float missileConeSoundCrossfadeBottomSize; + bool sharedAmmo; + bool lockonSupported; + bool requireLockonToFire; + bool isAirburstWeapon; + bool bigExplosion; + bool noAdsWhenMagEmpty; + bool avoidDropCleanup; + bool inheritsPerks; + bool crosshairColorChange; + bool bRifleBullet; + bool armorPiercing; + bool bBoltAction; + bool aimDownSight; + bool canHoldBreath; + bool canVariableZoom; + bool bRechamberWhileAds; + bool bBulletExplosiveDamage; + bool bCookOffHold; + bool bClipOnly; + bool noAmmoPickup; + bool adsFireOnly; + bool cancelAutoHolsterWhenEmpty; + bool disableSwitchToWhenEmpty; + bool suppressAmmoReserveDisplay; + bool laserSightDuringNightvision; + bool markableViewmodel; + bool noDualWield; + bool flipKillIcon; + bool bNoPartialReload; + bool bSegmentedReload; + bool blocksProne; + bool silenced; + bool isRollingGrenade; + bool projExplosionEffectForceNormalUp; + bool bProjImpactExplode; + bool stickToPlayers; + bool stickToVehicles; + bool stickToTurrets; + bool hasDetonator; + bool disableFiring; + bool timedDetonation; + bool rotate; + bool holdButtonToThrow; + bool freezeMovementWhenFiring; + bool thermalScope; + bool altModeSameWeapon; + bool turretBarrelSpinEnabled; + bool missileConeSoundEnabled; + bool missileConeSoundPitchshiftEnabled; + bool missileConeSoundCrossfadeEnabled; + bool offhandHoldIsCancelable; + bool doNotAllowAttachmentsToOverrideSpread; + unsigned __int16 stowTag; + XModel* stowOffsetModel; + }; +#pragma pack(pop) + + struct AnimOverrideEntry + { + unsigned __int16 attachment1; + unsigned __int16 attachment2; + const char* overrideAnim; + const char* altmodeAnim; + unsigned int animTreeType; + int animTime; + int altTime; + }; + + struct SoundOverrideEntry + { + unsigned __int16 attachment1; + unsigned __int16 attachment2; + snd_alias_list_t* overrideSound; + snd_alias_list_t* altmodeSound; + unsigned int soundType; + }; + + struct FXOverrideEntry + { + unsigned __int16 attachment1; + unsigned __int16 attachment2; + FxEffectDef* overrideFX; + FxEffectDef* altmodeFX; + unsigned int fxType; + }; + + struct ReloadStateTimerEntry + { + int attachment; + int reloadAddTime; + int reloadEmptyAddTime; + // int reloadStartAddTime; + // one of those is wrong, idk which one + }; + + struct NoteTrackToSoundEntry + { + int attachment; + short* notetrackSoundMapKeys; + short* notetrackSoundMapValues; + }; + + struct AttachmentDefUnk + { + char _pad1[16]; + TracerDef* tracer; + char _pad2[4]; + }; + + struct AttAmmoGeneral + { + std::int32_t penetrateType; + float penetrateMultiplier; + std::int32_t impactType; + std::int32_t fireType; + TracerDef* tracerType; + bool rifleBullet; + bool armorPiercing; + char pad[2]; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(penetrateType); + JSON_FIELD(penetrateMultiplier); + JSON_FIELD(impactType); + JSON_FIELD(fireType); + JSON_ASSET(tracerType, TracerDef); + JSON_FIELD(rifleBullet); + JSON_FIELD(armorPiercing); + + return data; + } + }; + + struct AttSight + { + bool aimDownSight; + bool adsFire; + bool rechamberWhileAds; + bool noAdsWhenMagEmpty; + bool canHoldBreath; + bool canVariableZoom; + bool hideRailWithThisScope; + // bool useScopeDrift; + // bool useDualFOV; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(aimDownSight); + JSON_FIELD(adsFire); + JSON_FIELD(rechamberWhileAds); + JSON_FIELD(noAdsWhenMagEmpty); + JSON_FIELD(canHoldBreath); + JSON_FIELD(canVariableZoom); + JSON_FIELD(hideRailWithThisScope); + + return data; + } + }; + + struct AttReload + { + bool noPartialReload; + bool segmentedReload; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(noPartialReload); + JSON_FIELD(segmentedReload); + + return data; + } + }; + + struct AttAddOns + { + bool motionTracker; + bool silenced; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(motionTracker); + JSON_FIELD(silenced); + + return data; + } + }; + + struct AttGeneral + { + bool boltAction; + bool inheritsPerks; + bool reticleSpin45; + char pad[1]; + float enemyCrosshairRange; + Material* reticleCenter; + Material* reticleSide; + int reticleCenterSize; + int reticleSideSize; + float moveSpeedScale; + float adsMoveSpeedScale; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(boltAction); + JSON_FIELD(inheritsPerks); + JSON_FIELD(reticleSpin45); + JSON_FIELD(enemyCrosshairRange); + JSON_ASSET(reticleCenter, Material); + JSON_ASSET(reticleSide, Material); + JSON_FIELD(reticleCenterSize); + JSON_FIELD(reticleSideSize); + JSON_FIELD(moveSpeedScale); + JSON_FIELD(adsMoveSpeedScale); + + return data; + } + }; + + struct AttAmmunition + { + int maxAmmo; + int startAmmo; + int clipSize; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(maxAmmo); + JSON_FIELD(startAmmo); + JSON_FIELD(clipSize); + + return data; + } + }; + + struct AttDamage + { + int damage; + int minDamage; + int meleeDamage; + float maxDamageRange; + float minDamageRange; + int playerDamage; + int minPlayerDamage; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(damage); + JSON_FIELD(minDamage); + JSON_FIELD(meleeDamage); + JSON_FIELD(maxDamageRange); + JSON_FIELD(minDamageRange); + JSON_FIELD(playerDamage); + JSON_FIELD(minPlayerDamage); + + return data; + } + }; + + struct AttLocationDamage + { + float locNone; + float locHelmet; + float locHead; + float locNeck; + float locTorsoUpper; + float locTorsoLower; + float locRightArmUpper; + float locRightArmLower; + float locRightHand; + float locLeftArmUpper; + float locLeftArmLower; + float locLeftHand; + float locRightLegUpper; + float locRightLegLower; + float locRightFoot; + float locLeftLegUpper; + float locLeftLegLower; + float locLeftFoot; + float locGun; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(locNone); + JSON_FIELD(locHelmet); + JSON_FIELD(locHead); + JSON_FIELD(locNeck); + JSON_FIELD(locTorsoUpper); + JSON_FIELD(locTorsoLower); + JSON_FIELD(locRightArmUpper); + JSON_FIELD(locRightArmLower); + JSON_FIELD(locRightHand); + JSON_FIELD(locLeftArmUpper); + JSON_FIELD(locLeftArmLower); + JSON_FIELD(locLeftHand); + JSON_FIELD(locRightLegUpper); + JSON_FIELD(locRightLegLower); + JSON_FIELD(locRightFoot); + JSON_FIELD(locLeftLegUpper); + JSON_FIELD(locLeftLegLower); + JSON_FIELD(locLeftFoot); + JSON_FIELD(locGun); + + return data; + } + }; + + struct AttIdleSettings + { + float hipIdleAmount; + float hipIdleSpeed; + float idleCrouchFactor; + float idleProneFactor; + float adsIdleLerpStartTime; + float adsIdleLerpTime; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(hipIdleAmount); + JSON_FIELD(hipIdleSpeed); + JSON_FIELD(idleCrouchFactor); + JSON_FIELD(idleProneFactor); + JSON_FIELD(adsIdleLerpStartTime); + JSON_FIELD(adsIdleLerpTime); + + return data; + } + }; + + struct AttADSSettings + { + float adsSpread; + float adsAimPitch; + float adsTransInTime; + float adsTransOutTime; + int adsReloadTransTime; + float adsCrosshairInFrac; + float adsCrosshairOutFrac; + float adsZoomFov; + float adsZoomInFrac; + float adsZoomOutFrac; + float adsFovLerpTime; + // float adsBobFactor; + // float adsViewBobMult; + float adsFireRateScale; + float adsDamageRangeScale; + float adsFireAnimFrac; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(adsSpread); + JSON_FIELD(adsAimPitch); + JSON_FIELD(adsTransInTime); + JSON_FIELD(adsTransOutTime); + JSON_FIELD(adsReloadTransTime); + JSON_FIELD(adsCrosshairInFrac); + JSON_FIELD(adsCrosshairOutFrac); + JSON_FIELD(adsZoomFov); + JSON_FIELD(adsZoomInFrac); + JSON_FIELD(adsZoomOutFrac); + JSON_FIELD(adsFovLerpTime); + JSON_FIELD(adsFireRateScale); + JSON_FIELD(adsDamageRangeScale); + JSON_FIELD(adsFireAnimFrac); + + return data; + } + }; + + struct AttScopeDriftSettings + { + float fScopeDriftDelay; + float fScopeDriftLerpInTime; + float fScopeDriftSteadyTime; + float fScopeDriftLerpOutTime; + float fScopeDriftSteadyFactor; + float fScopeDriftUnsteadyFactor; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(fScopeDriftDelay); + JSON_FIELD(fScopeDriftLerpInTime); + JSON_FIELD(fScopeDriftSteadyTime); + JSON_FIELD(fScopeDriftLerpOutTime); + JSON_FIELD(fScopeDriftSteadyFactor); + JSON_FIELD(fScopeDriftUnsteadyFactor); + + return data; + } + }; + + struct AttHipSpread + { + float hipSpreadStandMin; + float hipSpreadDuckedMin; + float hipSpreadProneMin; + float hipSpreadMax; + float hipSpreadDuckedMax; + float hipSpreadProneMax; + float hipSpreadFireAdd; + float hipSpreadTurnAdd; + float hipSpreadMoveAdd; + float hipSpreadDecayRate; + float hipSpreadDuckedDecay; + float hipSpreadProneDecay; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(hipSpreadStandMin); + JSON_FIELD(hipSpreadDuckedMin); + JSON_FIELD(hipSpreadProneMin); + JSON_FIELD(hipSpreadMax); + JSON_FIELD(hipSpreadDuckedMax); + JSON_FIELD(hipSpreadProneMax); + JSON_FIELD(hipSpreadFireAdd); + JSON_FIELD(hipSpreadTurnAdd); + JSON_FIELD(hipSpreadMoveAdd); + JSON_FIELD(hipSpreadDecayRate); + JSON_FIELD(hipSpreadDuckedDecay); + JSON_FIELD(hipSpreadProneDecay); + + return data; + } + }; + + struct AttGunKick + { + int hipGunKickReducedKickBullets; + float hipGunKickReducedKickPercent; + float hipGunKickPitchMin; + float hipGunKickPitchMax; + float hipGunKickYawMin; + float hipGunKickYawMax; + // float hipGunKickMagMin; + float hipGunKickAccel; + float hipGunKickSpeedMax; + float hipGunKickSpeedDecay; + float hipGunKickStaticDecay; + int adsGunKickReducedKickBullets; + float adsGunKickReducedKickPercent; + float adsGunKickPitchMin; + float adsGunKickPitchMax; + float adsGunKickYawMin; + float adsGunKickYawMax; + // float adsGunKickMagMin; + float adsGunKickAccel; + float adsGunKickSpeedMax; + float adsGunKickSpeedDecay; + float adsGunKickStaticDecay; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(hipGunKickReducedKickBullets); + JSON_FIELD(hipGunKickReducedKickPercent); + JSON_FIELD(hipGunKickPitchMin); + JSON_FIELD(hipGunKickPitchMax); + JSON_FIELD(hipGunKickYawMin); + JSON_FIELD(hipGunKickYawMax); + JSON_FIELD(hipGunKickAccel); + JSON_FIELD(hipGunKickSpeedMax); + JSON_FIELD(hipGunKickSpeedDecay); + JSON_FIELD(hipGunKickStaticDecay); + JSON_FIELD(adsGunKickReducedKickBullets); + JSON_FIELD(adsGunKickReducedKickPercent); + JSON_FIELD(adsGunKickPitchMin); + JSON_FIELD(adsGunKickPitchMax); + JSON_FIELD(adsGunKickYawMin); + JSON_FIELD(adsGunKickYawMax); + JSON_FIELD(adsGunKickAccel); + JSON_FIELD(adsGunKickSpeedMax); + JSON_FIELD(adsGunKickSpeedDecay); + JSON_FIELD(adsGunKickStaticDecay); + + return data; + } + }; + + struct AttViewKick + { + float hipViewKickPitchMin; + float hipViewKickPitchMax; + float hipViewKickYawMin; + float hipViewKickYawMax; + // float hipViewKickMagMin; + float hipViewKickCenterSpeed; + float adsViewKickPitchMin; + float adsViewKickPitchMax; + float adsViewKickYawMin; + float adsViewKickYawMax; + // float adsViewKickMagMin; + float adsViewKickCenterSpeed; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_FIELD(hipViewKickPitchMin); + JSON_FIELD(hipViewKickPitchMax); + JSON_FIELD(hipViewKickYawMin); + JSON_FIELD(hipViewKickYawMax); + JSON_FIELD(hipViewKickCenterSpeed); + JSON_FIELD(adsViewKickPitchMin); + JSON_FIELD(adsViewKickPitchMax); + JSON_FIELD(adsViewKickYawMin); + JSON_FIELD(adsViewKickYawMax); + JSON_FIELD(adsViewKickCenterSpeed); + + return data; + } + }; + + struct AttADSOverlay + { + ADSOverlay overlay; + bool hybridToggle; + bool thermalScope; + bool thermalToggle; + bool outlineEnemies; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_ASSET(overlay.shader, Material); + JSON_ASSET(overlay.shaderLowRes, Material); + JSON_ASSET(overlay.shaderEMP, Material); + JSON_ASSET(overlay.shaderEMPLowRes, Material); + + JSON_FIELD(hybridToggle); + JSON_FIELD(thermalScope); + JSON_FIELD(thermalToggle); + JSON_FIELD(outlineEnemies); + + return data; + } + }; + + struct AttUI + { + Material* dpadIcon; + Material* ammoCounterIcon; + weaponIconRatioType_t dpadIconRatio; + weaponIconRatioType_t ammoCounterIconRatio; + ammoCounterClipType_t ammoCounterClip; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_ASSET(dpadIcon, Material); + JSON_ASSET(ammoCounterIcon, Material); + + JSON_FIELD(dpadIconRatio); + JSON_FIELD(ammoCounterIconRatio); + JSON_FIELD(ammoCounterClip); + + return data; + } + }; + + struct AttRumbles + { + const char* fireRumble; + const char* meleeImpactRumble; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + JSON_STRING(fireRumble); + JSON_STRING(meleeImpactRumble); + + return data; + } + }; + + struct AttProjectile + { + int explosionRadius; + int explosionInnerDamage; + int explosionOuterDamage; + float damageConeAngle; + int projectileSpeed; + int projectileSpeedUp; + int projectileActivateDist; + float projectileLifetime; + XModel* projectileModel; + int projExplosionType; + FxEffectDef* projExplosionEffect; + bool projExplosionEffectForceNormalUp; + char pad1[3]; + snd_alias_list_t* projExplosionSound; + FxEffectDef* projDudEffect; + snd_alias_list_t* projDudSound; + bool projImpactExplode; + char pad2[3]; + float destabilizationRateTime; + float destabilizationCurvatureMax; + int destabilizeDistance; + FxEffectDef* projTrailEffect; + int projIgnitionDelay; + FxEffectDef* projIgnitionEffect; + snd_alias_list_t* projIgnitionSound; + + void Parse(Json& data, ZoneMemory* mem) + { + } + + Json ToJson() + { + Json data; + + /*JSON_ASSET(projectileModel, XModel); + + JSON_ASSET(projExplosionSound, snd_alias_list_t); + JSON_ASSET(projDudSound, snd_alias_list_t); + JSON_ASSET(projIgnitionSound, snd_alias_list_t); + + JSON_ASSET(projExplosionEffect, FxEffectDef); + JSON_ASSET(projDudEffect, FxEffectDef); + JSON_ASSET(projTrailEffect, FxEffectDef); + JSON_ASSET(projIgnitionEffect, FxEffectDef);*/ + + JSON_FIELD(explosionRadius); + JSON_FIELD(explosionInnerDamage); + JSON_FIELD(explosionOuterDamage); + JSON_FIELD(damageConeAngle); + JSON_FIELD(projectileSpeed); + JSON_FIELD(projectileSpeedUp); + JSON_FIELD(projectileActivateDist); + JSON_FIELD(projectileLifetime); + JSON_FIELD(projExplosionEffectForceNormalUp); + JSON_FIELD(projImpactExplode); + JSON_FIELD(destabilizationRateTime); + JSON_FIELD(destabilizationCurvatureMax); + JSON_FIELD(destabilizeDistance); + JSON_FIELD(projIgnitionDelay); + + return data; + } + }; + + struct AttachmentDef + { + const char* szInternalName; + const char* szDisplayName; + std::int32_t type; + std::int32_t weaponType; + std::int32_t weapClass; + XModel** worldModels; + XModel** viewModels; + XModel** reticleViewModels; + AttAmmoGeneral* ammogeneral; + AttSight* sight; + AttReload* reload; + AttAddOns* addOns; + AttGeneral* general; + AttAmmunition* ammunition; + AttIdleSettings* idleSettings; + AttDamage* damage; + AttLocationDamage* locationDamage; + AttScopeDriftSettings* scopeDriftSettings; + AttADSSettings* adsSettings; + AttADSSettings* adsSettingsMain; + AttHipSpread* hipSpread; + AttGunKick* gunKick; + AttViewKick* viewKick; + AttADSOverlay* adsOverlay; + AttUI* ui; + AttRumbles* rumbles; + AttProjectile* projectile; + float ammunitionScale; + float damageScale; + float damageScaleMin; + float stateTimersScale; + float fireTimersScale; + float idleSettingsScale; + float adsSettingsScale; + float adsSettingsScaleMain; + float hipSpreadScale; + float gunKickScale; + float viewKickScale; + float viewCenterScale; + int loadIndex; + bool hideIronSightsWithThisAttachment; + bool shareAmmoWithAlt; + char pad[2]; + }; + +#pragma pack(push, 4) + struct WeaponCompleteDef + { + const char* szInternalName; + WeaponDef* weapDef; + const char* szDisplayName; + unsigned __int16* hideTags; + AttachmentDef** scopes; + AttachmentDef** underBarrels; + AttachmentDef** others; + const char** szXAnims; + unsigned int numAnimOverrides; + AnimOverrideEntry* animOverrides; + unsigned int numSoundOverrides; + SoundOverrideEntry* soundOverrides; + unsigned int numFXOverrides; + FXOverrideEntry* fxOverrides; + unsigned int numReloadStateTimerOverrides; + ReloadStateTimerEntry* reloadOverrides; + unsigned int numNotetrackOverrides; + NoteTrackToSoundEntry* notetrackOverrides; + float fAdsZoomFov; + int iAdsTransInTime; + int iAdsTransOutTime; + int iClipSize; + int impactType; + int iFireTime; + int iFireTimeAkimbo; + weaponIconRatioType_t dpadIconRatio; + float penetrateMultiplier; + float fAdsViewKickCenterSpeed; + float fHipViewKickCenterSpeed; + const char* szAltWeaponName; + int altWeapon; + int iAltRaiseTime; + int iAltRaiseTimeAkimbo; + Material* killIcon; + Material* dpadIcon; + int fireAnimLength; + int fireAnimLengthAkimbo; + int iFirstRaiseTime; + int iFirstRaiseTimeAkimbo; + int ammoDropStockMax; + float adsDofStart; + float adsDofEnd; + unsigned __int16 accuracyGraphKnotCount[2]; + float(*accuracyGraphKnots[2])[2]; + bool motionTracker; + bool enhanced; + bool dpadIconShowsAmmo; + }; +#pragma pack(pop) + + // FxWorld +#pragma pack(push, 4) + + struct FxGlassDef + { + float halfThickness; + float texVecs[2][2]; + GfxColor color; + Material* material; + Material* materialShattered; + PhysPreset* physPreset; + }; + + struct FxSpatialFrame + { + float quat[4]; + float origin[3]; + }; + + union FxGlassPiecePlace + { + struct + { + FxSpatialFrame frame; + float radius; + }; + + unsigned int nextFree; + }; + + struct FxGlassPieceState + { + float texCoordOrigin[2]; + unsigned int supportMask; + unsigned __int16 initIndex; + unsigned __int16 geoDataStart; + unsigned __int16 lightingIndex; + char defIndex; + char pad[3]; + char vertCount; + char holeDataCount; + char crackDataCount; + char fanDataCount; + unsigned __int16 flags; + float areaX2; + }; + + struct FxGlassPieceDynamics + { + int fallTime; + __int32 physObjId; + __int32 physJointId; + float vel[3]; + float avel[3]; + }; + + struct FxGlassVertex + { + __int16 x; + __int16 y; + }; + + struct FxGlassHoleHeader + { + unsigned __int16 uniqueVertCount; + char touchVert; + char pad[1]; + }; + + struct FxGlassCrackHeader + { + unsigned __int16 uniqueVertCount; + char beginVertIndex; + char endVertIndex; + }; + + union FxGlassGeometryData + { + FxGlassVertex vert; + FxGlassHoleHeader hole; + FxGlassCrackHeader crack; + char asBytes[4]; + __int16 anonymous[2]; + }; + + struct FxGlassInitPieceState //Note, on MW3 this is missing 4 bytes, just not sure whats missing yet + { + FxSpatialFrame frame; + float radius; + float texCoordOrigin[2]; + unsigned int supportMask; + //float areaX2; // Commented out a random thing so the size fits. Most probably wrong since it was random. + unsigned __int16 lightingIndex; + char defIndex; + char vertCount; + char fanDataCount; + char pad[1]; + }; + + struct FxGlassSystem + { + int time; // 4 + int prevTime; // 4 + unsigned int defCount; // 4 + unsigned int pieceLimit; // 4 + unsigned int pieceWordCount; // 4 + unsigned int initPieceCount; // 4 + unsigned int cellCount; // 4 + unsigned int activePieceCount; // 4 + unsigned int firstFreePiece; // 4 + unsigned int geoDataLimit; // 4 + unsigned int geoDataCount; // 4 + unsigned int initGeoDataCount; // 4 + FxGlassDef* defs; // 4 + FxGlassPiecePlace* piecePlaces; // 4 + FxGlassPieceState* pieceStates; // 4 + FxGlassPieceDynamics* pieceDynamics; // 4 + FxGlassGeometryData* geoData; // 4 + unsigned int* isInUse; // 4 + unsigned int* cellBits; // 4 + char* visData; // 4 + VecInternal<3>* linkOrg; + float* halfThickness; // 4 + unsigned __int16* lightingHandles; // 4 + FxGlassInitPieceState* initPieceStates; // 4 + FxGlassGeometryData* initGeoData; // 4 + bool needToCompactData; // 1 + char initCount; + short pad; + float effectChanceAccum; // 4 + int lastPieceDeletionTime; // 4 + }; + + struct FxWorld + { + char* name; + FxGlassSystem glassSys; + }; + + struct FxImpactEntry + { + FxEffectDef* nonflesh[31]; + FxEffectDef* flesh[4]; + }; + + struct FxImpactTable + { + const char* name; + FxImpactEntry* table; + }; + + enum LbColType + { + LBCOL_TYPE_NUMBER = 0x0, + LBCOL_TYPE_TIME = 0x1, + LBCOL_TYPE_LEVELXP = 0x2, + LBCOL_TYPE_PRESTIGE = 0x3, + LBCOL_TYPE_BIGNUMBER = 0x4, + LBCOL_TYPE_PERCENT = 0x5, + LBCOL_TYPE_TIME_FULL = 0x6, + LBCOL_TYPE_COUNT = 0x7 + }; + + enum LbAggType + { + LBAGG_TYPE_MIN = 0x0, + LBAGG_TYPE_MAX = 0x1, + LBAGG_TYPE_ADD = 0x2, + LBAGG_TYPE_REPLACE = 0x3, + LBAGG_TYPE_COUNT = 0x4 + }; + + enum LbUpdateType + { + LBUPDATE_TYPE_NORMAL = 0x0, + LBUPDATE_TYPE_RANK = 0x1, + LBUPDATE_TYPE_COMBINE = 0x2, + LBUPDATE_TYPE_COUNT = 0x3, + }; + + struct LbColumnDef + { + const char* title; // 0 + int colId; // 4 + int dwColIndex; // 8 + bool hidden; // 12 + const char* statName; // 16 + LbColType type; + int precision; + LbAggType agg; + int uiCalColX; + int uiCalColY; + + Json ToJson() + { + Json data; + + JSON_STRING(title); + JSON_STRING(statName); + + JSON_FIELD(colId); + JSON_FIELD(dwColIndex); + JSON_FIELD(hidden); + JSON_FIELD(type); + JSON_FIELD(precision); + JSON_FIELD(agg); + JSON_FIELD(uiCalColX); + JSON_FIELD(uiCalColY); + + return data; + } + }; + + struct LeaderBoardDef + { + const char* name; + unsigned int id; + int columnCount; + int dwColumnCount; + int xpColId; + LbColumnDef* columns; + LbUpdateType updateType; + int trackTypes; + + Json ToJson() + { + Json data; + + JSON_STRING(name); + + JSON_FIELD(id); + JSON_FIELD(columnCount); + JSON_FIELD(dwColumnCount); + JSON_FIELD(xpColId); + JSON_FIELD(updateType); + JSON_FIELD(trackTypes); + + JSON_STRUCT_ARR(columns, this->columnCount); + + return data; + } + }; +#pragma pack(pop) + + struct GfxImageFileHeader + { + char tag[3]; + char version; + unsigned int flags; + char format; + char unused; + __int16 dimensions[3]; + int fileSizeForPicmip[4]; + }; + + union XAssetHeader + { + RawFile* rawfile; + VertexDecl* vertexdecl; + PixelShader* pixelshader; + VertexShader* vertexshader; + MaterialTechniqueSet* techset; + GfxImage* gfximage; + Material* material; + PhysPreset* physpreset; + PhysCollmap* physcollmap; + XAnimParts* xanimparts; + ModelSurface* xsurface; + clipMap_t* clipmap; + GfxWorld* gfxworld; + MapEnts* mapents; + GlassWorld* glassworld; + FxWorld* fxworld; + XModel* xmodel; + StringTable* stringtable; + ScriptFile* scriptfile; + ComWorld* comworld; + LocalizeEntry* localize; + SndCurve* soundcurve; + TracerDef* tracer; + LeaderBoardDef* leaderboard; + Font_s* font; + AttachmentDef* attachment; + WeaponCompleteDef* weapon; + FxEffectDef* fx; + snd_alias_list_t* sound; + LoadedSound* loadedsound; + StructuredDataDefSet* structureddatadef; + menuDef_t* menu; + GfxLightDef* lightdef; + void* data; + }; + + enum $8C6ECA10AE5C9C0C746CF1CD5CE4FD8D + { + CONST_SRC_CODE_LIGHT_POSITION, + CONST_SRC_CODE_LIGHT_DIFFUSE, + CONST_SRC_CODE_LIGHT_SPECULAR, + CONST_SRC_CODE_LIGHT_SPOTDIR, + CONST_SRC_CODE_LIGHT_SPOTFACTORS, + CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT, + CONST_SRC_CODE_PARTICLE_CLOUD_COLOR, + CONST_SRC_CODE_GAMETIME, + CONST_SRC_CODE_EYEOFFSET, + CONST_SRC_CODE_COLOR_SATURATION_R, + CONST_SRC_CODE_COLOR_SATURATION_G, + CONST_SRC_CODE_COLOR_SATURATION_B, + CONST_SRC_CODE_PIXEL_COST_FRACS, + CONST_SRC_CODE_PIXEL_COST_DECODE, + CONST_SRC_CODE_FILTER_TAP_0, + CONST_SRC_CODE_FILTER_TAP_1, + CONST_SRC_CODE_FILTER_TAP_2, + CONST_SRC_CODE_FILTER_TAP_3, + CONST_SRC_CODE_FILTER_TAP_4, + CONST_SRC_CODE_FILTER_TAP_5, + CONST_SRC_CODE_FILTER_TAP_6, + CONST_SRC_CODE_FILTER_TAP_7, + CONST_SRC_CODE_COLOR_MATRIX_R, + CONST_SRC_CODE_COLOR_MATRIX_G, + CONST_SRC_CODE_COLOR_MATRIX_B, + CONST_SRC_CODE_UNK1, + CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET, + CONST_SRC_CODE_RENDER_TARGET_SIZE, + CONST_SRC_CODE_RENDER_SOURCE_SIZE, + CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR, + CONST_SRC_CODE_DOF_EQUATION_SCENE, + CONST_SRC_CODE_DOF_LERP_SCALE, + CONST_SRC_CODE_DOF_LERP_BIAS, + CONST_SRC_CODE_DOF_ROW_DELTA, + CONST_SRC_CODE_MOTION_MATRIX_X, + CONST_SRC_CODE_MOTION_MATRIX_Y, + CONST_SRC_CODE_MOTION_MATRIX_W, + CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION, + CONST_SRC_CODE_SHADOWMAP_SCALE, + CONST_SRC_CODE_ZNEAR, + CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE, + CONST_SRC_CODE_DEBUG_BUMPMAP, + CONST_SRC_CODE_MATERIAL_COLOR, + CONST_SRC_CODE_FOG, + CONST_SRC_CODE_FOG_COLOR_LINEAR, + CONST_SRC_CODE_FOG_COLOR_GAMMA, + CONST_SRC_CODE_FOG_SUN_CONSTS, + CONST_SRC_CODE_FOG_SUN_COLOR_LINEAR, + CONST_SRC_CODE_FOG_SUN_COLOR_GAMMA, + CONST_SRC_CODE_FOG_SUN_DIR, + CONST_SRC_CODE_GLOW_SETUP, + CONST_SRC_CODE_GLOW_APPLY, + CONST_SRC_CODE_COLOR_BIAS, + CONST_SRC_CODE_COLOR_TINT_BASE, + CONST_SRC_CODE_COLOR_TINT_DELTA, + CONST_SRC_CODE_COLOR_TINT_QUADRATIC_DELTA, + CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS, + CONST_SRC_CODE_ENVMAP_PARMS, + CONST_SRC_CODE_SUN_SHADOWMAP_PIXEL_ADJUST, + CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST, + CONST_SRC_CODE_COMPOSITE_FX_DISTORTION, + CONST_SRC_CODE_POSTFX_FADE_EFFECT, + CONST_SRC_CODE_VIEWPORT_DIMENSIONS, + CONST_SRC_CODE_FRAMEBUFFER_READ, + CONST_SRC_CODE_THERMAL_COLOR_OFFSET, + CONST_SRC_CODE_PLAYLIST_POPULATION_PARAMS, + CONST_SRC_CODE_BASE_LIGHTING_COORDS, + CONST_SRC_CODE_LIGHT_PROBE_AMBIENT, + CONST_SRC_CODE_NEARPLANE_ORG, + CONST_SRC_CODE_NEARPLANE_DX, + CONST_SRC_CODE_NEARPLANE_DY, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX0, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX1, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX2, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR0, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR1, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR2, + CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM0, + CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM1, + CONST_SRC_CODE_DEPTH_FROM_CLIP, + CONST_SRC_CODE_CODE_MESH_ARG_0, + CONST_SRC_CODE_CODE_MESH_ARG_1, + CONST_SRC_CODE_VIEW_MATRIX, + CONST_SRC_CODE_INVERSE_VIEW_MATRIX, + CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX, + CONST_SRC_CODE_PROJECTION_MATRIX, + CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX, + CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX, + CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX, + CONST_SRC_CODE_VIEW_PROJECTION_MATRIX, + CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX, + CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX, + CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX, + CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX, + CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX, + CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX, + CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX, + CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX, + CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX, + CONST_SRC_CODE_WORLD_MATRIX0, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX0, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX0, + CONST_SRC_CODE_WORLD_VIEW_MATRIX0, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX0, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX0, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX0, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX0, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX0, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0, + CONST_SRC_CODE_WORLD_MATRIX1, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX1, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX1, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX1, + CONST_SRC_CODE_WORLD_VIEW_MATRIX1, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX1, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX1, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX1, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX1, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX1, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1, + CONST_SRC_CODE_WORLD_MATRIX2, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX2, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX2, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX2, + CONST_SRC_CODE_WORLD_VIEW_MATRIX2, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX2, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX2, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX2, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX2, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX2, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2, + CONST_SRC_TOTAL_COUNT, + CONST_SRC_NONE, + }; + } +} diff --git a/src/IW5/Zone.cpp b/src/IW5/Zone.cpp new file mode 100644 index 0000000..451090b --- /dev/null +++ b/src/IW5/Zone.cpp @@ -0,0 +1,296 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + void* Zone::get_asset_pointer(std::int32_t type, const std::string& name) + { + if (name.empty()) + { + return nullptr; + } + + for (std::size_t idx = 0; idx < m_assets.size(); idx++) + { + if (m_assets[idx]->type() == type && m_assets[idx]->name() == name) + { + auto ptr = reinterpret_cast((3 << 28) | ((this->m_assetbase + (8 * idx) + 4) & 0x0FFFFFFF) + 1); + return ptr; + } + } + + return nullptr; + } + + void Zone::add_asset_of_type_by_pointer(std::int32_t type, void* pointer) + { + if (!pointer) + { + return; + } + + // don't add asset if it already exists + for (std::size_t idx = 0; idx < m_assets.size(); idx++) + { + if (m_assets[idx]->type() == type && m_assets[idx]->pointer() == pointer) + { + return; + } + } + +#define DECLARE_ASSET(__type__, __interface__) \ + if (type == __type__) \ + { \ + auto asset = std::make_shared < __interface__ >(); \ + asset->init(pointer, this->m_zonemem.get()); \ + asset->load_depending(this); \ + m_assets.push_back(asset); \ + } + + // declare asset interfaces + DECLARE_ASSET(xmodelsurfs, IXSurface); + DECLARE_ASSET(image, IGfxImage); + DECLARE_ASSET(glass_map, IGlassWorld); + } + + void Zone::add_asset_of_type(std::int32_t type, const std::string& name) + { + if (name.empty()) + { + return; + } + + // don't add asset if it already exists + if (get_asset_pointer(type, name)) + { + return; + } + +#define DECLARE_ASSET(__type__, __interface__) \ + if (type == __type__) \ + { \ + auto asset = std::make_shared < __interface__ >(); \ + asset->init(name, this->m_zonemem.get()); \ + asset->load_depending(this); \ + m_assets.push_back(asset); \ + } + + // declare asset interfaces + DECLARE_ASSET(xanim, IXAnimParts); + DECLARE_ASSET(pixelshader, IPixelShader); + DECLARE_ASSET(vertexdecl, IVertexDecl); + DECLARE_ASSET(vertexshader, IVertexShader); + DECLARE_ASSET(techset, ITechset); + DECLARE_ASSET(image, IGfxImage); + DECLARE_ASSET(material, IMaterial) + DECLARE_ASSET(xmodelsurfs, IXSurface); + DECLARE_ASSET(xmodel, IXModel); + DECLARE_ASSET(map_ents, IMapEnts); + DECLARE_ASSET(col_map_mp, IClipMap); + DECLARE_ASSET(gfx_map, IGfxWorld); + DECLARE_ASSET(rawfile, IRawFile); + DECLARE_ASSET(com_map, IComWorld); + DECLARE_ASSET(font, IFontDef); + DECLARE_ASSET(leaderboarddef, ILeaderBoardDef); + DECLARE_ASSET(localize, ILocalizeEntry); + DECLARE_ASSET(attachment, IAttachmentDef); + DECLARE_ASSET(physpreset, IPhysPreset); + DECLARE_ASSET(phys_collmap, IPhysCollmap); + DECLARE_ASSET(fx, IFxEffectDef); + DECLARE_ASSET(stringtable, IStringTable); + DECLARE_ASSET(sound, ISound); + DECLARE_ASSET(loaded_sound, ILoadedSound); + DECLARE_ASSET(sndcurve, ISoundCurve); + DECLARE_ASSET(glass_map, IGlassWorld); + DECLARE_ASSET(fx_map, IFxWorld); + DECLARE_ASSET(weapon, IWeaponDef); + DECLARE_ASSET(structureddatadef, IStructuredDataDef); + DECLARE_ASSET(menu, IMenuDef); + DECLARE_ASSET(scriptfile, IScriptFile); + DECLARE_ASSET(lightdef, ILightDef); + DECLARE_ASSET(font, IFontDef); + } + + std::int32_t Zone::get_type_by_name(const std::string& type) + { + return m_linker->type_to_int(type); + } + + void Zone::add_asset_of_type(const std::string& type, const std::string& name) + { + std::int32_t itype = m_linker->type_to_int(type); + this->add_asset_of_type(itype, name); + } + + void Zone::build(ZoneBuffer* buf) + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("IW5::Zone::Build"); +#endif + + auto startTime = GetTickCount64(); + + // make a folder in main, for the map images + std::filesystem::create_directories("main\\" + this->name_ + "\\images"); + + ZONETOOL_INFO("Compiling fastfile \"%s\"...", this->name_.data()); + + constexpr std::size_t num_streams = 9; + XZoneMemory mem; + + std::size_t headersize = sizeof XZoneMemory; + memset(&mem, 0, headersize); + + auto zone = buf->at>(); + + // write zone header + buf->write(&mem); + + std::uintptr_t pad = 0xFFFFFFFF; + std::uintptr_t zero = 0; + + // write asset types to header + for (auto i = 0u; i < m_assets.size(); i++) + { + m_assets[i]->prepare(buf, this->m_zonemem.get()); + } + + // write scriptstring count + std::uint32_t stringcount = buf->scriptstring_count(); + buf->write(&stringcount); + buf->write(stringcount > 0 ? (&pad) : (&zero)); + + // write asset count + std::uint32_t asset_count = m_assets.size(); + buf->write(&asset_count); + buf->write(asset_count > 0 ? (&pad) : (&zero)); + + // push stream + buf->push_stream(3); + START_LOG_STREAM; + + if (stringcount) + { + // write pointer for every scriptstring + for (std::size_t idx = 0; idx < stringcount; idx++) + { + buf->write(&pad); + } + + // write scriptstrings + buf->align(3); + for (std::size_t idx = 0; idx < stringcount; idx++) + { + buf->write_str(buf->get_scriptstring(idx)); + } + } + + buf->pop_stream(); + buf->push_stream(3); + + // align buffer + buf->align(3); + + // set asset ptr base + this->m_assetbase = buf->stream_offset(3); + ZONETOOL_INFO("m_assetbase: %u", this->m_assetbase); + + // write asset types to header + for (auto i = 0u; i < asset_count; i++) + { + // write asset data to zone + auto type = m_assets[i]->type(); + buf->write(&type); + buf->write(&pad); + } + + // write assets + for (auto& asset : m_assets) + { + // push stream + buf->push_stream(0); + buf->align(3); + + // write asset + asset->write(this, buf); + + // pop stream + buf->pop_stream(); + } + + // pop stream + END_LOG_STREAM; + buf->pop_stream(); + + // update zone header + zone->size = buf->size() - headersize; + zone->externalsize = 0; + + // Update stream data + for (int i = 0; i < num_streams; i++) + { + zone->streams[i] = buf->stream_offset(i); + } + + // Dump zone to disk (for debugging) + buf->save("debug\\" + this->name_ + ".zone"); + + // Compress buffer + auto buf_compressed = buf->compress_zstd(); + + // Generate FF header + auto header = this->m_zonemem->Alloc(); + strcpy(header->header, "IWffu100"); + header->version = 2000; + header->allowOnlineUpdate = 0; + + // Save fastfile + ZoneBuffer fastfile(buf_compressed.size() + 21); + fastfile.init_streams(1); + fastfile.write_stream(header, 21); + + fastfile.write(buf_compressed.data(), buf_compressed.size()); + + std::string localappdata = getenv("LOCALAPPDATA"); + fastfile.save(localappdata + "\\Plutonium-staging\\storage\\iw5\\zone\\" + this->name_ + ".ff"); + + // oxygen output paths + // fastfile.save("C:\\Users\\RektInator\\AppData\\Local\\Plutonium\\storage\\iw5\\zone\\" + this->name_ + ".ff"); + fastfile.save("zone\\english\\" + this->name_ + ".ff"); + + ZONETOOL_INFO("Successfully compiled fastfile \"%s\"!", this->name_.data()); + ZONETOOL_INFO("Compiling took %u msec.", GetTickCount64() - startTime); + + // this->m_linker->UnloadZones(); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + Zone::Zone(std::string name, ILinker* linker) + { + currentzone = name; + + this->name_ = name; + this->m_linker = linker; + + this->m_zonemem = std::make_shared(MAX_ZONE_SIZE); + } + + Zone::~Zone() + { + // wipe all assets + m_assets.clear(); + } + } +} diff --git a/src/IW5/Zone.hpp b/src/IW5/Zone.hpp new file mode 100644 index 0000000..590b9ea --- /dev/null +++ b/src/IW5/Zone.hpp @@ -0,0 +1,77 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +extern std::string currentzone; + +#include "Assets/PhysCollmap.hpp" +#include "Assets/PhysPreset.hpp" +#include "Assets/PixelShader.hpp" +#include "Assets/VertexDecl.hpp" +#include "Assets/VertexShader.hpp" +#include "Assets/Techset.hpp" +#include "Assets/GfxImage.hpp" +#include "Assets/Material.hpp" +#include "Assets/XSurface.hpp" +#include "Assets/XModel.hpp" +#include "Assets/XAnimParts.hpp" +#include "Assets/MapEnts.hpp" +#include "Assets/ClipMap.hpp" +#include "Assets/GfxWorld.hpp" +#include "Assets/RawFile.hpp" +#include "Assets/SoundCurve.hpp" +#include "Assets/LoadedSound.hpp" +#include "Assets/Sound.hpp" +#include "Assets/TracerDef.hpp" +#include "Assets/LocalizeEntry.hpp" +#include "Assets/FontDef.hpp" +#include "Assets/LeaderBoardDef.hpp" +#include "Assets/ComWorld.hpp" +#include "Assets/FxEffectDef.hpp" +#include "Assets/AttachmentDef.hpp" +#include "Assets/WeaponDef.hpp" +#include "Assets/StringTable.hpp" +#include "Assets/FxWorld.hpp" +#include "Assets/GlassWorld.hpp" +#include "Assets/StructuredDataDef.hpp" +#include "Assets/MenuDef.hpp" +#include "Assets/ScriptFile.hpp" +#include "Assets/LightDef.hpp" + +namespace ZoneTool +{ + namespace IW5 + { + class Linker; + + class Zone : public IZone + { + private: + std::uintptr_t m_assetbase; + std::string name_; + ILinker* m_linker; + std::vector> m_assets; + std::shared_ptr m_zonemem; + + public: + Zone(std::string name, ILinker* linker); + ~Zone(); + + void* get_asset_pointer(std::int32_t type, const std::string& name) override; + + void add_asset_of_type_by_pointer(std::int32_t type, void* pointer) override; + + void add_asset_of_type(std::int32_t type, const std::string& name) override; + void add_asset_of_type(const std::string& type, const std::string& name) override; + std::int32_t get_type_by_name(const std::string& type) override; + + void build(ZoneBuffer* buf) override; + }; + } +} diff --git a/src/IW5/stdafx.cpp b/src/IW5/stdafx.cpp new file mode 100644 index 0000000..a55cad6 --- /dev/null +++ b/src/IW5/stdafx.cpp @@ -0,0 +1,9 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" diff --git a/src/IW5/stdafx.hpp b/src/IW5/stdafx.hpp new file mode 100644 index 0000000..9e56122 --- /dev/null +++ b/src/IW5/stdafx.hpp @@ -0,0 +1,28 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS + +#include + +#undef min +#undef max + +#include +#include +#include +#include + +// Namespaces +using namespace std::literals; +using namespace string_literals; + +#include "IW5.hpp" diff --git a/src/ImgPak.lua b/src/ImgPak.lua new file mode 100644 index 0000000..5247a59 --- /dev/null +++ b/src/ImgPak.lua @@ -0,0 +1,41 @@ +ImgPak = {} + +function ImgPak:include() + includedirs { + path.join(ProjectFolder(), "imgpak") + } +end + +function ImgPak:link() + self:include() + links { + "ImgPak" + } +end + +function ImgPak:project() + local folder = ProjectFolder(); + + project "ImgPak" + kind "ConsoleApp" + language "C++" + + pchheader "stdafx.hpp" + pchsource(path.join(folder, "imgpak/stdafx.cpp")) + + files { + path.join(folder, "imgpak/**.h"), + path.join(folder, "imgpak/**.hpp"), + path.join(folder, "imgpak/**.cpp") + } + + -- Linked projects + self:include() + ZoneUtils:link() + + -- ThirdParty + libtommath:link() + libtomcrypt:link() + zstd:link() + zlib:link() +end \ No newline at end of file diff --git a/src/ZoneTool.lua b/src/ZoneTool.lua new file mode 100644 index 0000000..288e75f --- /dev/null +++ b/src/ZoneTool.lua @@ -0,0 +1,64 @@ +ZoneTool = {} + +function ZoneTool:include() + includedirs { + path.join(ProjectFolder(), "ZoneTool") + } +end + +function ZoneTool:link() + self:include() + links { + "ZoneTool" + } +end + +function ZoneTool:project() + local folder = ProjectFolder(); + + project "ZoneTool" + kind "SharedLib" + language "C++" + + pchheader "stdafx.hpp" + pchsource(path.join(folder, "ZoneTool/stdafx.cpp")) + + files { + path.join(folder, "ZoneTool/**.h"), + path.join(folder, "ZoneTool/**.hpp"), + path.join(folder, "ZoneTool/**.cpp") + } + + -- Linked projects + self:include() + IW3:link() + IW4:link() + IW5:link() + CODO:link() + ZoneUtils:link() + SteamApi:link() + + -- ThirdParty + libtommath:link() + libtomcrypt:link() + SteamApi:link() + zstd:link() + zlib:link() + + if _OPTIONS["set-version"] then + defines { + "ZONETOOL_VERSION=\"" .. _OPTIONS["set-version"] .. "\"" + } + end + + filter "toolset:msc*" + postbuildcommands { + "if \"%COMPUTERNAME%\" == \"DESKTOP-QM2NUQP\" ( copy /y \"$(TargetPath)\" \"H:\\SteamLibrary\\steamapps\\common\\Call of Duty 4\\zoneiw3.dll\" )", + "if \"%COMPUTERNAME%\" == \"DESKTOP-QM2NUQP\" ( copy /y \"$(TargetPath)\" \"H:\\SteamLibrary\\steamapps\\common\\Call of Duty Modern Warfare 2\\zonetool.dll\" )", + "if \"%COMPUTERNAME%\" == \"DESKTOP-QM2NUQP\" ( copy /y \"$(TargetPath)\" \"H:\\SteamLibrary\\steamapps\\common\\Call of Duty Modern Warfare 3\\zonetool.dll\" )", + "if \"%COMPUTERNAME%\" == \"DESKTOP-G5EPE91\" ( copy /y \"$(TargetPath)\" \"D:\\Program Files (x86)\\Steam\\steamapps\\common\\Call of Duty 4\\zoneiw3.dll\" )", + "if \"%COMPUTERNAME%\" == \"DESKTOP-G5EPE91\" ( copy /y \"$(TargetPath)\" \"D:\\Program Files (x86)\\Steam\\steamapps\\common\\Call of Duty Modern Warfare 2\\zonetool.dll\" )", + "if \"%COMPUTERNAME%\" == \"DESKTOP-G5EPE91\" ( copy /y \"$(TargetPath)\" \"D:\\Program Files (x86)\\Steam\\steamapps\\common\\Call of Duty Modern Warfare 3\\zonetool.dll\" )", + } + filter {} +end \ No newline at end of file diff --git a/src/ZoneTool/DllProxy.cpp b/src/ZoneTool/DllProxy.cpp new file mode 100644 index 0000000..90b2472 --- /dev/null +++ b/src/ZoneTool/DllProxy.cpp @@ -0,0 +1,145 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +#undef LoadLibrary + +// Macro to declare an export +// --------------------------------------+ +#define EXPORT(_export) extern "C" __declspec(naked) __declspec(dllexport) void _export() \ +{ \ + SDLLP::GetExport(__FUNCTION__, LIBRARY); \ + __asm { jmp eax } \ +} + +// Static class +// --------------------------------------+ +class SDLLP +{ +private: + static std::map mLibraries; + + static void Log(const char* message, ...); + static void LoadLibrary(const char* library); + static bool IsLibraryLoaded(const char* library); + +public: + static FARPROC GetExport(const char* function, const char* library); +}; + +// Class variable declarations +// --------------------------------------+ +std::map SDLLP::mLibraries; + +// Load necessary library +// --------------------------------------+ +void SDLLP::LoadLibrary(const char* library) +{ + CHAR mPath[MAX_PATH]; + + GetSystemDirectoryA(mPath, MAX_PATH); + strcat_s(mPath, "\\"); + strcat_s(mPath, library); + + mLibraries[library] = LoadLibraryA(mPath); +} + +// Check if export already loaded +// --------------------------------------+ +bool SDLLP::IsLibraryLoaded(const char* library) +{ + return (mLibraries.find(library) != mLibraries.end() && mLibraries[library]); +} + +// Get export address +// --------------------------------------+ +FARPROC SDLLP::GetExport(const char* function, const char* library) +{ + if (!IsLibraryLoaded(library)) LoadLibrary(library); + FARPROC address = GetProcAddress(mLibraries[library], function); + return address; +} + +// Write debug string +// --------------------------------------+ +void SDLLP::Log(const char* message, ...) +{ + CHAR buffer[1024]; + va_list ap; + + va_start(ap, message); + vsprintf(buffer, message, ap); + va_end(ap); + + OutputDebugStringA(buffer); +} + +// --------------------------------------+ +// Adapt export functions and library +// --------------------------------------+ + +// steam +#define LIBRARY "steam_api.dll" +EXPORT(SteamAPI_Init) +EXPORT(SteamAPI_Shutdown) +EXPORT(SteamAPI_RestartAppIfNecessary) +EXPORT(SteamAPI_ReleaseCurrentThreadMemory) +EXPORT(SteamAPI_WriteMiniDump) +EXPORT(SteamAPI_SetMiniDumpComment) +EXPORT(SteamAPI_UnregisterCallback) +EXPORT(SteamAPI_UnregisterCallResult) +EXPORT(SteamAPI_RegisterCallback) +EXPORT(SteamAPI_RegisterCallResult) +EXPORT(SteamAPI_RunCallbacks) +EXPORT(SteamAPI_IsSteamRunning) +EXPORT(SteamClient) +EXPORT(SteamUser) +EXPORT(SteamFriends) +EXPORT(SteamUtils) +EXPORT(SteamMatchmaking) +EXPORT(SteamUserStats) +EXPORT(SteamApps) +EXPORT(SteamNetworking) +EXPORT(SteamMasterServerUpdater) +EXPORT(SteamMatchmakingServers) +EXPORT(SteamRemoteStorage) +EXPORT(SteamScreenshots) +EXPORT(SteamHTTP) +EXPORT(SteamUnifiedMessages) +EXPORT(SteamController) +EXPORT(SteamUGC) +EXPORT(SteamAppList) +EXPORT(SteamMusic) +EXPORT(SteamMusicRemote) +EXPORT(SteamHTMLSurface) +EXPORT(SteamInventory) +EXPORT(SteamVideo) +EXPORT(SteamGameServerNetworking) +EXPORT(SteamGameServerUtils) +EXPORT(SteamGameServer) +EXPORT(SteamGameServer_Init) +EXPORT(SteamGameServer_Shutdown) +EXPORT(SteamGameServer_RunCallbacks) + + // d3d9 +#define LIBRARY "d3d9.dll" +EXPORT(Direct3DShaderValidatorCreate9) +EXPORT(PSGPError) +EXPORT(PSGPSampleTexture) +EXPORT(D3DPERF_BeginEvent) +EXPORT(D3DPERF_EndEvent) +EXPORT(D3DPERF_GetStatus) +EXPORT(D3DPERF_QueryRepeatFrame) +EXPORT(D3DPERF_SetMarker) +EXPORT(D3DPERF_SetOptions) +EXPORT(D3DPERF_SetRegion) +EXPORT(DebugSetLevel) +EXPORT(DebugSetMute) +EXPORT(Direct3DCreate9) +EXPORT(Direct3DCreate9Ex) diff --git a/src/ZoneTool/ZoneTool.cpp b/src/ZoneTool/ZoneTool.cpp new file mode 100644 index 0000000..f28ef6f --- /dev/null +++ b/src/ZoneTool/ZoneTool.cpp @@ -0,0 +1,607 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" +#include + +// include zonetool linkers +#include +#include +#include +#include +#include "Utils/Swizzle.hpp" + +#pragma comment(lib, "Dbghelp") + +std::string currentzone; + +namespace ZoneTool +{ + std::vector> linkers; + std::map)>> commands; + + void register_command(const std::string& name, std::function)> cb) + { + commands[name] = cb; + } + + void execute_command(std::vector args) + { + const auto itr = commands.find(args[0]); + if (itr != commands.end()) + { + itr->second(args); + } + else + { + ZONETOOL_ERROR("Unknown command \"%s\".", args[0].data()); + } + } + + void command_thread() + { + while (true) + { + // Get console input + std::string input; + std::getline(std::cin, input); + + std::vector args; + + // Load arguments into vector + if (input.find(' ') != std::string::npos) + { + args = split(input, ' '); + } + else + { + args.push_back(input); + } + + // Execute command + execute_command(args); + } + } + + void add_assets_using_iterator(const std::string& fastfile, const std::string& type, const std::string& folder, + const std::string& extension, bool skip_reference, IZone* zone) + { + if (std::filesystem::is_directory("zonetool\\" + fastfile + "\\" + folder)) + { + for (auto& file : std::filesystem::recursive_directory_iterator( + "zonetool\\" + fastfile + "\\" + folder)) + { + if (is_regular_file(file)) + { + auto filename = file.path().filename().string(); + + if (skip_reference && filename[0] == ',') + { + // skip this file + continue; + } + + // check if the filename contains the correct extension + if (filename.length() > extension.length() && + filename.substr(filename.length() - extension.length()) == extension) + { + // remove the extension + filename = filename.substr(0, filename.length() - extension.length()); + + // add asset to disk + zone->add_asset_of_type(type, filename); + } + } + } + } + } + + void parse_csv_file(ILinker* linker, IZone* zone, const std::string& fastfile, const std::string& csv_file) + { + auto path = "zone_source\\" + csv_file + ".csv"; + auto* parser = CsvParser_new(path.data(), ",", false); + + if (!parser) + { + ZONETOOL_ERROR("Could not find csv file \"%s\" to build zone!", csv_file.data()); + return; + } + + auto is_referencing = false; + auto* row = CsvParser_getRow(parser); + while (row != nullptr) + { + // parse options + if ((strlen(row->fields_[0]) >= 1 && row->fields_[0][0] == '#') || (strlen(row->fields_[0]) >= 2 && row-> + fields_[0][0] == '/' && row->fields_[0][1] == '/')) + { + // comment line, go to next line. + goto nextRow; + } + if (!strlen(row->fields_[0])) + { + // empty line, go to next line. + goto nextRow; + } + if (row->fields_[0] == "require"s) + { + linker->load_zone(row->fields_[1]); + } + else if (row->fields_[0] == "include"s) + { + parse_csv_file(linker, zone, fastfile, row->fields_[1]); + } + // + else if (row->fields_[0] == "target"s) + { + if (row->fields_[1] == "xbox360"s) + { + zone->set_target(zone_target::xbox360); + } + else if (row->fields_[1] == "ps3"s) + { + zone->set_target(zone_target::ps3); + } + else if (row->fields_[1] == "pc"s) + { + zone->set_target(zone_target::pc); + } + else + { + ZONETOOL_ERROR("Invalid zone target \"%s\"!", row->fields_[1]); + } + } + // + else if (row->fields_[0] == "target_version"s) + { + auto found_version = false; + for (auto i = 0u; i < static_cast(zone_target_version::max); i++) + { + if (zone_target_version_str[i] == row->fields_[1]) + { + const auto target_version = static_cast(i); + if (!linker->supports_version(target_version)) + { + ZONETOOL_FATAL("Current linker (%s) does not support target version %s.", linker->version(), row->fields_[1]); + } + + zone->set_target_version(target_version); + found_version = true; + } + } + + if (!found_version) + { + ZONETOOL_FATAL("Invalid target version \"%s\".", row->fields_[1]); + } + } + // this allows us to reference assets instead of rewriting them + else if (row->fields_[0] == "reference"s) + { + if (row->numOfFields_ >= 2) + { + is_referencing = row->fields_[1] == "true"s; + } + } + // add assets that are required for maps + else if (row->fields_[0] == "map"s) + { + zone->add_asset_of_type("techset", "wc_l_hsm_r0c0n0s0"); + } + // this will use a directory iterator to automatically add assets + else if (row->fields_[0] == "iterate"s) + { + try + { + add_assets_using_iterator(fastfile, "fx", "fx", ".fxe", true, zone); + add_assets_using_iterator(fastfile, "xanimparts", "XAnim", ".xae2", true, zone); + add_assets_using_iterator(fastfile, "xmodel", "XModel", ".xme6", true, zone); + } + catch (std::exception& ex) + { + ZONETOOL_FATAL("A fatal exception occured while building zone \"%s\", exception was: %s\n", fastfile.data(), ex.what()); + } + } + // this will force external assets to be used + else if (row->fields_[0] == "forceExternalAssets"s) + { + ZONETOOL_WARNING("forceExternalAssets has been turned on!"); + FileSystem::ForceExternalAssets(true); + } + // if entry is not an option, it should be an asset. + else + { + if (row->fields_[0] == "localize"s && row->numOfFields_ >= 3) + { + ZONETOOL_INFO("Adding localized string to zone..."); + + struct LocalizeStruct + { + const char* value; + const char* name; + }; + + auto loc = new LocalizeStruct; + loc->name = _strdup(row->fields_[1]); + loc->value = _strdup(row->fields_[2]); + + auto type = zone->get_type_by_name(row->fields_[0]); + if (type == -1) + { + ZONETOOL_ERROR("Could not translate typename %s to an integer!", row->fields_[0]); + } + + try + { + zone->add_asset_of_type_by_pointer(type, loc); + } + catch (std::exception& ex) + { + ZONETOOL_FATAL("A fatal exception occured while building zone \"%s\", exception was: %s\n", fastfile.data(), ex.what()); + } + } + else + { + if (row->numOfFields_ >= 2) + { + if (linker->is_valid_asset_type(row->fields_[0])) + { + try + { + zone->add_asset_of_type( + row->fields_[0], + ((is_referencing) ? ","s : ""s) + row->fields_[1] + ); + } + catch (std::exception& ex) + { + ZONETOOL_FATAL("A fatal exception occured while building zone \"%s\", exception was: %s\n", fastfile.data(), ex.what()); + } + } + } + } + } + + nextRow: + // destroy row and alloc next one. + CsvParser_destroy_row(row); + row = CsvParser_getRow(parser); + } + + // free csv parser + CsvParser_destroy(parser); + } + + void build_zone(ILinker* linker, const std::string& fastfile) + { + // make sure FS is correct. + FileSystem::SetFastFile(fastfile); + + ZONETOOL_INFO("Building fastfile \"%s\" for game \"%s\"", fastfile.data(), linker->version()); + + auto zone = linker->alloc_zone(fastfile); + if (zone == nullptr) + { + ZONETOOL_ERROR("An error occured while building fastfile \"%s\": Are you out of memory?", fastfile.data()); + return; + } + + // set default zone target to PC + zone->set_target(zone_target::pc); + + if (linker->version() == "IW4"s) + { + zone->set_target_version(zone_target_version::iw4_release); + } + else if (linker->version() == "IW5"s) + { + zone->set_target_version(zone_target_version::iw5_release); + } + + parse_csv_file(linker, zone.get(), fastfile, fastfile); + + // allocate zone buffer + auto buffer = linker->alloc_buffer(); + + // add branding asset + zone->add_asset_of_type("rawfile", fastfile); + + // compile zone + zone->build(buffer.get()); + + // unload fastfiles + // linker->UnloadZones(); + } + + ILinker* current_linker; + + LONG NTAPI exception_handler(_EXCEPTION_POINTERS* info) + { + if (info->ExceptionRecord->ExceptionCode == STATUS_INTEGER_OVERFLOW || + info->ExceptionRecord->ExceptionCode == STATUS_FLOAT_OVERFLOW || + info->ExceptionRecord->ExceptionCode == 0x406D1388 || + info->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) + { + return EXCEPTION_CONTINUE_EXECUTION; + } + + if (ZONETOOL_VERSION == "0.0.0"s) + { + MessageBoxA(nullptr, va("An exception occured (0x%08X) and ZoneTool must be restarted to continue. However, ZoneTool has detected that you are using a custom DLL. If you want to submit an issue, try to reproduce the bug with the latest release of ZoneTool. The latest version can be found here: https://github.com/ZoneTool/zonetool/releases", info->ExceptionRecord->ExceptionCode).data(), "ZoneTool", MB_ICONERROR); + std::exit(0); + } + + std::filesystem::create_directories("zonetool/crashdumps"); + + const auto exception_time = std::time(nullptr); + const auto linker_name = (current_linker) ? current_linker->version() : "unknown"; + const auto file_name = va("zonetool/crashdumps/zonetool-exception-%s-%s-%llu.dmp", linker_name, ZONETOOL_VERSION, exception_time); + + DWORD dump_type = MiniDumpIgnoreInaccessibleMemory; + dump_type |= MiniDumpWithHandleData; + dump_type |= MiniDumpScanMemory; + dump_type |= MiniDumpWithProcessThreadData; + dump_type |= MiniDumpWithFullMemoryInfo; + dump_type |= MiniDumpWithThreadInfo; + dump_type |= MiniDumpWithCodeSegs; + dump_type |= MiniDumpWithDataSegs; + + const DWORD file_share = FILE_SHARE_READ | FILE_SHARE_WRITE; + const HANDLE file_handle = CreateFileA(file_name.data(), GENERIC_WRITE | GENERIC_READ, file_share, nullptr, (file_share & FILE_SHARE_WRITE) > 0 ? OPEN_ALWAYS : OPEN_EXISTING, NULL, nullptr); + MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), info, FALSE }; + if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file_handle, static_cast(dump_type), &ex, nullptr, nullptr)) + { + + } + + const auto message = va("An exception occured and ZoneTool must be restarted to continue. If this keeps happening, create an issue on https://github.com/ZoneTool/zonetool with the crashdump attached. The crashdump can be found at: \"%s\".", file_name.data()); + MessageBoxA(nullptr, message.data(), "ZoneTool", MB_ICONERROR); + std::exit(0); + } + + void create_console() + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("CreateConsole"); +#endif + + if (!IsDebuggerPresent()) + { + // Catch exceptions + AddVectoredExceptionHandler(TRUE, exception_handler); + } + + // Allocate console + AllocConsole(); + freopen("CONIN$", "r", stdin); + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + + // Set console name + SetConsoleTitleA("ZoneTool"); + + // Spawn command thread + CreateThread(nullptr, 0, reinterpret_cast(command_thread), nullptr, 0, nullptr); + + // Commands + register_command("quit"s, [](std::vector) + { + ExitProcess(0); + }); + register_command("buildzone"s, [](std::vector args) + { + // Check if enough arguments have been passed to the command + if (args.size() == 1) + { + ZONETOOL_ERROR("usage: buildzone "); + return; + } + + if (current_linker) + { + if(current_linker->supports_building()) + { + build_zone(current_linker, args[1]); + } + else + { + ZONETOOL_ERROR("Current linker does not support zone building."); + } + } + }); + register_command("loadzone"s, [](std::vector args) + { + // Check if enough arguments have been passed to the command + if (args.size() == 1) + { + ZONETOOL_ERROR("usage: loadzone "); + return; + } + + // Load zone + if (current_linker) + { + current_linker->load_zone(args[1]); + } + }); + register_command("verifyzone"s, [](std::vector args) + { + // Check if enough arguments have been passed to the command + if (args.size() == 1) + { + ZONETOOL_ERROR("usage: verifyzone "); + return; + } + + // Load zone + if (current_linker) + { + current_linker->verify_zone(args[1]); + } + }); + register_command("dumpzone"s, [](std::vector args) + { + // Check if enough arguments have been passed to the command + if (args.size() == 1) + { + ZONETOOL_ERROR("usage: dumpzone "); + return; + } + + // Load zone + if (current_linker) + { + current_linker->dump_zone(args[1]); + } + }); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } + + template + void register_linker() + { + linkers.push_back(std::make_shared()); + } + + void branding(ILinker* linker) + { + ZONETOOL_INFO("ZoneTool initialization complete!"); + ZONETOOL_INFO("Welcome to ZoneTool v" ZONETOOL_VERSION " written by RektInator."); + ZONETOOL_INFO(" \"No matter how hard or unlikely, if it's possible, it will be done.\""); + ZONETOOL_INFO("Special thanks to: Laupetin, NTAuthority, momo5502, TheApadayo, localhost, X3RX35 & homura."); + + if (linker) + { + ZONETOOL_INFO("Initializing linker for game \"%s\"...\n", linker->version()); + } + else + { + ZONETOOL_ERROR("No linker could be found for the current binary!\n"); + } + } + + std::vector get_command_line_arguments() + { + LPWSTR* szArglist; + int nArgs; + + szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); + + std::vector args; + args.resize(nArgs); + + // convert all args to std::string + for (int i = 0; i < nArgs; i++) + { + auto curArg = std::wstring(szArglist[i]); + args[i] = std::string(curArg.begin(), curArg.end()); + } + + // return arguments + return args; + } + + void handle_params() + { + // Wait until the game is loaded + Sleep(5000); + + // Execute command line commands? + auto args = get_command_line_arguments(); + if (args.size() > 1) + { + for (auto i = 0u; i < args.size(); i++) + { + if (i < args.size() - 1) + { + if (args[i] == "-buildzone") + { + build_zone(current_linker, args[i + 1]); + i++; + } + else if (args[i] == "-loadzone") + { + current_linker->load_zone(args[i + 1]); + i++; + } + else if (args[i] == "-dumpzone") + { + current_linker->dump_zone(args[i + 1]); + i++; + } + } + } + + std::exit(0); + } + } + + bool is_custom_linker_present() + { + return std::filesystem::exists("linker.dll") && std::filesystem::is_regular_file("linker.dll"); + } + + void startup() + { +#ifdef USE_VMPROTECT + VMProtectBeginUltra("Startup"); +#endif + + // Create stdout console + create_console(); + + // Register linkers + register_linker(); + register_linker(); + register_linker(); + register_linker(); + + // check if a custom linker is present in the current game directory + if (is_custom_linker_present()) + { + const auto linker_module = LoadLibraryA("linker.dll"); + + if (linker_module != nullptr && linker_module != INVALID_HANDLE_VALUE) + { + const auto get_linker_func = GetProcAddress(linker_module, "GetLinker"); + + if (get_linker_func != nullptr && get_linker_func != INVALID_HANDLE_VALUE) + { + current_linker = Function(get_linker_func)(); + } + } + } + + if (!current_linker) + { + // Startup compatible linkers + for (auto& linker : linkers) + { + linker->startup(); + + if (linker->is_used()) + { + current_linker = linker.get(); + } + } + } + + // Startup complete, show branding + branding(current_linker); + + // handle startup commands + CreateThread(nullptr, 0, reinterpret_cast(handle_params), nullptr, 0, nullptr); + +#ifdef USE_VMPROTECT + VMProtectEnd(); +#endif + } +} diff --git a/src/ZoneTool/ZoneTool.hpp b/src/ZoneTool/ZoneTool.hpp new file mode 100644 index 0000000..5d9e9e8 --- /dev/null +++ b/src/ZoneTool/ZoneTool.hpp @@ -0,0 +1,26 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +// include zonetool utilities +#include + +extern std::string currentzone; + +#ifdef ZONETOOL_COMPILING +#define ZONETOOL_LIB __declspec(dllexport) +#else +#define ZONETOOL_LIB __declspec(dllimport) +#endif + +namespace ZoneTool +{ + void startup(); + void register_command(const std::string& name, std::function)> cb); +} diff --git a/src/ZoneTool/dllmain.cpp b/src/ZoneTool/dllmain.cpp new file mode 100644 index 0000000..424af58 --- /dev/null +++ b/src/ZoneTool/dllmain.cpp @@ -0,0 +1,27 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + ZoneTool::startup(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/src/ZoneTool/stdafx.cpp b/src/ZoneTool/stdafx.cpp new file mode 100644 index 0000000..a55cad6 --- /dev/null +++ b/src/ZoneTool/stdafx.cpp @@ -0,0 +1,9 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" diff --git a/src/ZoneTool/stdafx.hpp b/src/ZoneTool/stdafx.hpp new file mode 100644 index 0000000..7943e28 --- /dev/null +++ b/src/ZoneTool/stdafx.hpp @@ -0,0 +1,47 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +// Generic Definitions +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS + +#define ZONETOOL_COMPILING + +// Windows Includes +#include +#include + +#undef min +#undef max + +// Std Includes +#include +#include +#include +#include +#include +#include + +// Namespaces +using namespace std::literals; +using namespace string_literals; + +// Dependency Definitions + +// Dependency Includes +// #include + +// Project Definitions +#ifndef ZONETOOL_VERSION +#define ZONETOOL_VERSION "0.0.0" +#endif + +// Project Includes +#include "ZoneTool.hpp" diff --git a/src/ZoneUtils.lua b/src/ZoneUtils.lua new file mode 100644 index 0000000..8a26f3f --- /dev/null +++ b/src/ZoneUtils.lua @@ -0,0 +1,38 @@ +ZoneUtils = {} + +function ZoneUtils:include() + includedirs { + path.join(ProjectFolder(), "ZoneUtils") + } +end + +function ZoneUtils:link() + self:include() + links { + "ZoneUtils" + } +end + +function ZoneUtils:project() + local folder = ProjectFolder(); + + project "ZoneUtils" + kind "StaticLib" + language "C++" + + pchheader "stdafx.hpp" + pchsource(path.join(folder, "ZoneUtils/stdafx.cpp")) + + files { + path.join(folder, "ZoneUtils/**.h"), + path.join(folder, "ZoneUtils/**.hpp"), + path.join(folder, "ZoneUtils/**.cpp") + } + + self:include() + + libtommath:include() + libtomcrypt:include() + zstd:include() + zlib:include() +end \ No newline at end of file diff --git a/src/ZoneUtils/CSV.cpp b/src/ZoneUtils/CSV.cpp new file mode 100644 index 0000000..537cbe0 --- /dev/null +++ b/src/ZoneUtils/CSV.cpp @@ -0,0 +1,305 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +CsvParser* CsvParser_new(const char* filePath, const char* delimiter, int firstLineIsHeader) +{ + CsvParser* csvParser = (CsvParser*)malloc(sizeof(CsvParser)); + if (filePath == nullptr) + { + csvParser->filePath_ = nullptr; + } + else + { + int filePathLen = strlen(filePath); + csvParser->filePath_ = (char*)malloc((filePathLen + 1)); + strcpy(csvParser->filePath_, filePath); + } + csvParser->firstLineIsHeader_ = firstLineIsHeader; + csvParser->errMsg_ = nullptr; + if (delimiter == nullptr) + { + csvParser->delimiter_ = ','; + } + else if (_CsvParser_delimiterIsAccepted(delimiter)) + { + csvParser->delimiter_ = *delimiter; + } + else + { + csvParser->delimiter_ = '\0'; + } + csvParser->header_ = nullptr; + csvParser->fileHandler_ = nullptr; + csvParser->fromString_ = 0; + csvParser->csvString_ = nullptr; + csvParser->csvStringIter_ = 0; + + return csvParser; +} + +CsvParser* CsvParser_new_from_string(const char* csvString, const char* delimiter, int firstLineIsHeader) +{ + CsvParser* csvParser = CsvParser_new(nullptr, delimiter, firstLineIsHeader); + csvParser->fromString_ = 1; + if (csvString != nullptr) + { + int csvStringLen = strlen(csvString); + csvParser->csvString_ = (char*)malloc(csvStringLen + 1); + strcpy(csvParser->csvString_, csvString); + } + return csvParser; +} + +void CsvParser_destroy(CsvParser* csvParser) +{ + if (csvParser == nullptr) + { + return; + } + if (csvParser->filePath_ != nullptr) + { + free(csvParser->filePath_); + } + if (csvParser->errMsg_ != nullptr) + { + free(csvParser->errMsg_); + } + if (csvParser->fileHandler_ != nullptr) + { + fclose(csvParser->fileHandler_); + } + if (csvParser->header_ != nullptr) + { + CsvParser_destroy_row(csvParser->header_); + } + if (csvParser->csvString_ != nullptr) + { + free(csvParser->csvString_); + } + free(csvParser); +} + +void CsvParser_destroy_row(CsvRow* csvRow) +{ + int i; + for (i = 0; i < csvRow->numOfFields_; i++) + { + free(csvRow->fields_[i]); + } + free(csvRow->fields_); + free(csvRow); +} + +const CsvRow* CsvParser_getHeader(CsvParser* csvParser) +{ + if (!csvParser->firstLineIsHeader_) + { + _CsvParser_setErrorMessage( + csvParser, "Cannot supply header, as current CsvParser object does not support header"); + return nullptr; + } + if (csvParser->header_ == nullptr) + { + csvParser->header_ = _CsvParser_getRow(csvParser); + } + return csvParser->header_; +} + +CsvRow* CsvParser_getRow(CsvParser* csvParser) +{ + if (csvParser->firstLineIsHeader_ && csvParser->header_ == nullptr) + { + csvParser->header_ = _CsvParser_getRow(csvParser); + } + return _CsvParser_getRow(csvParser); +} + +int CsvParser_getNumFields(const CsvRow* csvRow) +{ + return csvRow->numOfFields_; +} + +const char** CsvParser_getFields(const CsvRow* csvRow) +{ + return (const char**)csvRow->fields_; +} + +CsvRow* _CsvParser_getRow(CsvParser* csvParser) +{ + int numRowRealloc = 0; + int acceptedFields = 64; + int acceptedCharsInField = 64; + if (csvParser->filePath_ == nullptr && (!csvParser->fromString_)) + { + _CsvParser_setErrorMessage(csvParser, "Supplied CSV file path is NULL"); + return nullptr; + } + if (csvParser->csvString_ == nullptr && csvParser->fromString_) + { + _CsvParser_setErrorMessage(csvParser, "Supplied CSV string is NULL"); + return nullptr; + } + if (csvParser->delimiter_ == '\0') + { + _CsvParser_setErrorMessage(csvParser, "Supplied delimiter is not supported"); + return nullptr; + } + if (!csvParser->fromString_) + { + if (csvParser->fileHandler_ == nullptr) + { + csvParser->fileHandler_ = fopen(csvParser->filePath_, "r"); + if (csvParser->fileHandler_ == nullptr) + { + int errorNum = errno; + const char* errStr = strerror(errorNum); + char* errMsg = (char*)malloc(1024 + strlen(errStr)); + strcpy(errMsg, ""); + sprintf(errMsg, "Error opening CSV file for reading: %s : %s", csvParser->filePath_, errStr); + _CsvParser_setErrorMessage(csvParser, errMsg); + free(errMsg); + return nullptr; + } + } + } + CsvRow* csvRow = (CsvRow*)malloc(sizeof(CsvRow)); + csvRow->fields_ = (char**)malloc(acceptedFields * sizeof(char*)); + csvRow->numOfFields_ = 0; + int fieldIter = 0; + char* currField = (char*)malloc(acceptedCharsInField); + int inside_complex_field = 0; + int currFieldCharIter = 0; + int seriesOfQuotesLength = 0; + int lastCharIsQuote = 0; + int isEndOfFile = 0; + while (true) + { + char currChar = (csvParser->fromString_) + ? csvParser->csvString_[csvParser->csvStringIter_] + : fgetc(csvParser->fileHandler_); + csvParser->csvStringIter_++; + int endOfFileIndicator; + if (csvParser->fromString_) + { + endOfFileIndicator = (currChar == '\0'); + } + else + { + endOfFileIndicator = feof(csvParser->fileHandler_); + } + if (endOfFileIndicator) + { + if (currFieldCharIter == 0 && fieldIter == 0) + { + _CsvParser_setErrorMessage(csvParser, "Reached EOF"); + free(currField); + CsvParser_destroy_row(csvRow); + return nullptr; + } + currChar = '\n'; + isEndOfFile = 1; + } + if (currChar == '\r') + { + continue; + } + if (currFieldCharIter == 0 && !lastCharIsQuote) + { + if (currChar == '\"') + { + inside_complex_field = 1; + lastCharIsQuote = 1; + continue; + } + } + else if (currChar == '\"') + { + seriesOfQuotesLength++; + inside_complex_field = (seriesOfQuotesLength % 2 == 0); + if (inside_complex_field) + { + currFieldCharIter--; + } + } + else + { + seriesOfQuotesLength = 0; + } + if (isEndOfFile || ((currChar == csvParser->delimiter_ || currChar == '\n') && !inside_complex_field)) + { + currField[lastCharIsQuote ? currFieldCharIter - 1 : currFieldCharIter] = '\0'; + csvRow->fields_[fieldIter] = (char*)malloc(currFieldCharIter + 1); + strcpy(csvRow->fields_[fieldIter], currField); + free(currField); + csvRow->numOfFields_++; + if (currChar == '\n') + { + return csvRow; + } + if (csvRow->numOfFields_ != 0 && csvRow->numOfFields_ % acceptedFields == 0) + { + csvRow->fields_ = (char**)realloc(csvRow->fields_, + ((numRowRealloc + 2) * acceptedFields) * sizeof(char*)); + numRowRealloc++; + } + acceptedCharsInField = 64; + currField = (char*)malloc(acceptedCharsInField); + currFieldCharIter = 0; + fieldIter++; + inside_complex_field = 0; + } + else + { + currField[currFieldCharIter] = currChar; + currFieldCharIter++; + if (currFieldCharIter == acceptedCharsInField - 1) + { + acceptedCharsInField *= 2; + currField = (char*)realloc(currField, acceptedCharsInField); + } + } + lastCharIsQuote = (currChar == '\"') ? 1 : 0; + } +} + +int _CsvParser_delimiterIsAccepted(const char* delimiter) +{ + char actualDelimiter = *delimiter; + if (actualDelimiter == '\n' || actualDelimiter == '\r' || actualDelimiter == '\0' || + actualDelimiter == '\"') + { + return 0; + } + return 1; +} + +void _CsvParser_setErrorMessage(CsvParser* csvParser, const char* errorMessage) +{ + if (csvParser->errMsg_ != nullptr) + { + free(csvParser->errMsg_); + } + int errMsgLen = strlen(errorMessage); + csvParser->errMsg_ = (char*)malloc(errMsgLen + 1); + strcpy(csvParser->errMsg_, errorMessage); +} + +const char* CsvParser_getErrorMessage(CsvParser* csvParser) +{ + return csvParser->errMsg_; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/ZoneUtils/CSV.hpp b/src/ZoneUtils/CSV.hpp new file mode 100644 index 0000000..a377c88 --- /dev/null +++ b/src/ZoneUtils/CSV.hpp @@ -0,0 +1,58 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#ifndef CSVPARSER_H +#define CSVPARSER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CsvRow +{ + char** fields_; + int numOfFields_; +} CsvRow; + +typedef struct CsvParser +{ + char* filePath_; + char delimiter_; + int firstLineIsHeader_; + char* errMsg_; + CsvRow* header_; + FILE* fileHandler_; + int fromString_; + char* csvString_; + int csvStringIter_; +} CsvParser; + + +// Public +CsvParser* CsvParser_new(const char* filePath, const char* delimiter, int firstLineIsHeader); +CsvParser* CsvParser_new_from_string(const char* csvString, const char* delimiter, int firstLineIsHeader); +void CsvParser_destroy(CsvParser* csvParser); +void CsvParser_destroy_row(CsvRow* csvRow); +const CsvRow* CsvParser_getHeader(CsvParser* csvParser); +CsvRow* CsvParser_getRow(CsvParser* csvParser); +int CsvParser_getNumFields(const CsvRow* csvRow); +const char** CsvParser_getFields(const CsvRow* csvRow); +const char* CsvParser_getErrorMessage(CsvParser* csvParser); + +// Private +CsvRow* _CsvParser_getRow(CsvParser* csvParser); +int _CsvParser_delimiterIsAccepted(const char* delimiter); +void _CsvParser_setErrorMessage(CsvParser* csvParser, const char* errorMessage); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ZoneUtils/IAsset.hpp b/src/ZoneUtils/IAsset.hpp new file mode 100644 index 0000000..92fb7ad --- /dev/null +++ b/src/ZoneUtils/IAsset.hpp @@ -0,0 +1,40 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + class IAsset + { + public: + virtual void init(const std::string& name, ZoneMemory* mem) + { + } + + virtual void init(void* asset, ZoneMemory* mem) + { + } + + virtual void prepare(ZoneBuffer* buf, ZoneMemory* mem) + { + } + + virtual std::string name() { return ""; } + virtual std::int32_t type() { return -1; } + virtual void* pointer() { return nullptr; } + + virtual void write(IZone* zone, ZoneBuffer* buffer) + { + } + + virtual void load_depending(IZone* zone) + { + } + }; +} diff --git a/src/ZoneUtils/IPatch.hpp b/src/ZoneUtils/IPatch.hpp new file mode 100644 index 0000000..8399086 --- /dev/null +++ b/src/ZoneUtils/IPatch.hpp @@ -0,0 +1,17 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + class IPatch + { + public: + }; +} diff --git a/src/ZoneUtils/Json.hpp b/src/ZoneUtils/Json.hpp new file mode 100644 index 0000000..891c688 --- /dev/null +++ b/src/ZoneUtils/Json.hpp @@ -0,0 +1,10623 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +/* +__ _____ _____ _____ +__| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 2.0.5 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +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. +*/ + +#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING +#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS + +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// exclude unsupported compilers +#if defined(__clang__) +#define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +#if CLANG_VERSION < 30400 +#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" +#endif +#elif defined(__GNUC__) +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if GCC_VERSION < 40900 +#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" +#endif +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define JSON_DEPRECATED __declspec(deprecated) +#else +#define JSON_DEPRECATED +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + /*! + @brief unnamed namespace with internal helper functions + @since version 1.0.0 + */ + namespace + { + /*! + @brief Helper to determine whether there's a key_type for T. + + Thus helper is used to tell associative containers apart from other containers + such as sequence containers. For instance, `std::map` passes the test as it + contains a `mapped_type`, whereas `std::vector` fails the test. + + @sa http://stackoverflow.com/a/7728728/266378 + @since version 1.0.0 + */ + template + struct has_mapped_type + { + private: + template + static char test(typename C::mapped_type*); + template + static char (& test(...))[2]; + public: + static constexpr bool value = sizeof(test(nullptr)) == 1; + }; + + /*! + @brief helper class to create locales with decimal point + + This struct is used a default locale during the JSON serialization. JSON + requires the decimal point to be `.`, so this function overloads the + `do_decimal_point()` function to return `.`. This function is called by + float-to-string conversions to retrieve the decimal separator between integer + and fractional parts. + + @sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315 + @since version 2.0.0 + */ + struct DecimalSeparator : std::numpunct + { + char do_decimal_point() const override + { + return '.'; + } + }; + } + + /*! + @brief a class to store JSON values + + @tparam ObjectType type for JSON objects (`std::map` by default; will be used + in @ref object_t) + @tparam ArrayType type for JSON arrays (`std::vector` by default; will be used + in @ref array_t) + @tparam StringType type for JSON strings and object keys (`std::string` by + default; will be used in @ref string_t) + @tparam BooleanType type for JSON booleans (`bool` by default; will be used + in @ref boolean_t) + @tparam NumberIntegerType type for JSON integer numbers (`int64_t` by + default; will be used in @ref number_integer_t) + @tparam NumberUnsignedType type for JSON unsigned integer numbers (@c + `uint64_t` by default; will be used in @ref number_unsigned_t) + @tparam NumberFloatType type for JSON floating-point numbers (`double` by + default; will be used in @ref number_float_t) + @tparam AllocatorType type of the allocator to use (`std::allocator` by + default) + + @requirement The class satisfies the following concept requirements: + - Basic + - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null value. + - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): + JSON values can be destructed. + - Layout + - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): + JSON values have + [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the class + has no virtual functions or (virtual) base classes. + - Library-wide + - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. + - Container + - [Container](http://en.cppreference.com/w/cpp/concept/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. + + @invariant The member variables @a m_value and @a m_type have the following + relationship: + - If `m_type == value_t::object`, then `m_value.object != nullptr`. + - If `m_type == value_t::array`, then `m_value.array != nullptr`. + - If `m_type == value_t::string`, then `m_value.string != nullptr`. + The invariants are checked by member function assert_invariant(). + + @internal + @note ObjectType trick from http://stackoverflow.com/a/9860911 + @endinternal + + @see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange + Format](http://rfc7159.net/rfc7159) + + @since version 1.0.0 + + @nosubgrouping + */ + template < + template class ObjectType = std::map, + template class ArrayType = std::vector, + class StringType = std::string, + class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator + > + class basic_json + { + private: + /// workaround type for MSVC + using basic_json_t = basic_json; + + public: + // forward declarations + template + class json_reverse_iterator; + class json_pointer; + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + class iterator; + /// a const iterator for a basic_json container + class const_iterator; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /*! + @brief returns the allocator associated with the container + */ + static allocator_type get_allocator() + { + return allocator_type(); + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + + /*! + @brief a type for an object + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. + + To store objects in C++, a type is defined by the template parameters + described below. + + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) + + #### Default type + + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: + + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode + + #### Behavior + + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: + + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, later stored name/value + pairs overwrite previously stored name/value pairs, leaving the used + names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will + be treated as equal and both stored as `{"key": 1}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the object's limit of nesting is not constraint explicitly. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. + + #### Storage + + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. + + @sa @ref array_t -- type for an array value + + @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 7159](http://rfc7159.net/rfc7159), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType, + AllocatorType>>; + + /*! + @brief a type for an array + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + + To store objects in C++, a type is defined by the template parameters + explained below. + + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + + #### Default type + + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: + + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the array's limit of nesting is not constraint explicitly. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa @ref object_t -- type for an object value + + @since version 1.0.0 + */ + using array_t = ArrayType>; + + /*! + @brief a type for a string + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. + + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. + + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. + + #### Default type + + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: + + @code {.cpp} + std::string + @endcode + + #### String comparison + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. + + This implementation is interoperable as it does compare strings code unit + by code unit. + + #### Storage + + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. + + @since version 1.0.0 + */ + using string_t = StringType; + + /*! + @brief a type for a boolean + + [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. + + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. + + #### Default type + + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: + + @code {.cpp} + bool + @endcode + + #### Storage + + Boolean values are stored directly inside a @ref basic_json type. + + @since version 1.0.0 + */ + using boolean_t = BooleanType; + + /*! + @brief a type for a number (integer) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. + + #### Default type + + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: + + @code {.cpp} + int64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_integer_t = NumberIntegerType; + + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + + /*! + @brief a type for a number (floating-point) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. + + #### Default type + + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: + + @code {.cpp} + double + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. + + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. + + #### Storage + + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa @ref number_integer_t -- type for number values (integer) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; + + /// @} + + + /////////////////////////// + // JSON type enumeration // + /////////////////////////// + + /*! + @brief the JSON type enumeration + + This enumeration collects the different JSON types. It is internally used + to distinguish the stored values, and the functions @ref is_null(), @ref + is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref + is_number() (with @ref is_number_integer(), @ref is_number_unsigned(), and + @ref is_number_float()), @ref is_discarded(), @ref is_primitive(), and + @ref is_structured() rely on it. + + @note There are three enumeration entries (number_integer, + number_unsigned, and number_float), because the library distinguishes + these three types for numbers: @ref number_unsigned_t is used for unsigned + integers, @ref number_integer_t is used for signed integers, and @ref + number_float_t is used for floating-point numbers or to approximate + integers which do not fit in the limits of their respective type. + + @sa @ref basic_json(const value_t value_type) -- create a JSON value with + the default value for a given type + + @since version 1.0.0 + */ + enum class value_t : uint8_t + { + null, + ///< null value + object, + ///< object (unordered set of name/value pairs) + array, + ///< array (ordered collection of values) + string, + ///< string value + boolean, + ///< boolean value + number_integer, + ///< number value (signed integer) + number_unsigned, + ///< number value (unsigned integer) + number_float, + ///< number value (floating-point) + discarded ///< discarded by the the parser callback function + }; + + + private: + + /// helper for exception-safe object creation + template + static T* create(Args&& ... args) + { + AllocatorType alloc; + auto deleter = [&](T* object) + { + alloc.deallocate(object, 1); + }; + std::unique_ptr object(alloc.allocate(1), deleter); + alloc.construct(object.get(), std::forward(args)...); + assert(object.get() != nullptr); + return object.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) + { + } + + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) + { + } + + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) + { + } + + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) + { + } + + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } + + default: + { + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } + + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } + + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + }; + + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const + { + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /*! + @brief JSON callback events + + This enumeration lists the parser events that can trigger calling a + callback function of type @ref parser_callback_t during parsing. + + @image html callback_events.png "Example when certain parse events are triggered" + + @since version 1.0.0 + */ + enum class parse_event_t : uint8_t + { + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value + }; + + /*! + @brief per-element parser callback type + + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse(std::istream&, const + parser_callback_t) or @ref parse(const char*, const parser_callback_t), + it is called on certain events (passed as @ref parse_event_t via parameter + @a event) with a set recursion depth @a depth and context JSON value + @a parsed. The return value of the callback function is a boolean + indicating whether the element that emitted the callback shall be kept or + not. + + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. + + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: + + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. + + @param[in] depth the depth of the recursion during parsing + + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called + + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events + + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. + + @sa @ref parse(std::istream&, parser_callback_t) or + @ref parse(const char*, parser_callback_t) for examples + + @since version 1.0.0 + */ + using parser_callback_t = std::function; + + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /*! + @brief create an empty value with a given type + + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + + @param[in] value_type the type of the value to create + + @complexity Constant. + + @throw std::bad_alloc if allocation for object, array, or string value + fails + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} + + @sa @ref basic_json(std::nullptr_t) -- create a `null` value + @sa @ref basic_json(boolean_t value) -- create a boolean value + @sa @ref basic_json(const string_t&) -- create a string value + @sa @ref basic_json(const object_t&) -- create a object value + @sa @ref basic_json(const array_t&) -- create a array value + @sa @ref basic_json(const number_float_t) -- create a number + (floating-point) value + @sa @ref basic_json(const number_integer_t) -- create a number (integer) + value + @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned) + value + + @since version 1.0.0 + */ + basic_json(const value_t value_type) + : m_type(value_type), m_value(value_type) + { + assert_invariant(); + } + + /*! + @brief create a null object + + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} + + @since version 1.0.0 + */ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /*! + @brief create an object (explicit) + + Create an object JSON value with a given content. + + @param[in] val a value for the object + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for object value fails + + @liveexample{The following code shows the constructor with an @ref + object_t parameter.,basic_json__object_t} + + @sa @ref basic_json(const CompatibleObjectType&) -- create an object value + from a compatible STL container + + @since version 1.0.0 + */ + basic_json(const object_t& val) + : m_type(value_t::object), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an object (implicit) + + Create an object JSON value with a given content. This constructor allows + any type @a CompatibleObjectType that can be used to construct values of + type @ref object_t. + + @tparam CompatibleObjectType An object type whose `key_type` and + `value_type` is compatible to @ref object_t. Examples include `std::map`, + `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with + a `key_type` of `std::string`, and a `value_type` from which a @ref + basic_json value can be constructed. + + @param[in] val a value for the object + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for object value fails + + @liveexample{The following code shows the constructor with several + compatible object type parameters.,basic_json__CompatibleObjectType} + + @sa @ref basic_json(const object_t&) -- create an object value + + @since version 1.0.0 + */ + template ::value + and + std::is_constructible::value, int>::type = + 0> + basic_json(const CompatibleObjectType& val) + : m_type(value_t::object) + { + using std::begin; + using std::end; + m_value.object = create(begin(val), end(val)); + assert_invariant(); + } + + /*! + @brief create an array (explicit) + + Create an array JSON value with a given content. + + @param[in] val a value for the array + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for array value fails + + @liveexample{The following code shows the constructor with an @ref array_t + parameter.,basic_json__array_t} + + @sa @ref basic_json(const CompatibleArrayType&) -- create an array value + from a compatible STL containers + + @since version 1.0.0 + */ + basic_json(const array_t& val) + : m_type(value_t::array), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an array (implicit) + + Create an array JSON value with a given content. This constructor allows + any type @a CompatibleArrayType that can be used to construct values of + type @ref array_t. + + @tparam CompatibleArrayType An object type whose `value_type` is + compatible to @ref array_t. Examples include `std::vector`, `std::deque`, + `std::list`, `std::forward_list`, `std::array`, `std::set`, + `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a + `value_type` from which a @ref basic_json value can be constructed. + + @param[in] val a value for the array + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for array value fails + + @liveexample{The following code shows the constructor with several + compatible array type parameters.,basic_json__CompatibleArrayType} + + @sa @ref basic_json(const array_t&) -- create an array value + + @since version 1.0.0 + */ + template ::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type = 0 + > + basic_json(const CompatibleArrayType& val) + : m_type(value_t::array) + { + using std::begin; + using std::end; + m_value.array = create(begin(val), end(val)); + assert_invariant(); + } + + /*! + @brief create a string (explicit) + + Create an string JSON value with a given content. + + @param[in] val a value for the string + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for string value fails + + @liveexample{The following code shows the constructor with an @ref + string_t parameter.,basic_json__string_t} + + @sa @ref basic_json(const typename string_t::value_type*) -- create a + string value from a character pointer + @sa @ref basic_json(const CompatibleStringType&) -- create a string value + from a compatible string container + + @since version 1.0.0 + */ + basic_json(const string_t& val) + : m_type(value_t::string), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create a string (explicit) + + Create a string JSON value with a given content. + + @param[in] val a literal value for the string + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for string value fails + + @liveexample{The following code shows the constructor with string literal + parameter.,basic_json__string_t_value_type} + + @sa @ref basic_json(const string_t&) -- create a string value + @sa @ref basic_json(const CompatibleStringType&) -- create a string value + from a compatible string container + + @since version 1.0.0 + */ + basic_json(const typename string_t::value_type* val) + : basic_json(string_t(val)) + { + assert_invariant(); + } + + /*! + @brief create a string (implicit) + + Create a string JSON value with a given content. + + @param[in] val a value for the string + + @tparam CompatibleStringType an string type which is compatible to @ref + string_t, for instance `std::string`. + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for string value fails + + @liveexample{The following code shows the construction of a string value + from a compatible type.,basic_json__CompatibleStringType} + + @sa @ref basic_json(const string_t&) -- create a string value + @sa @ref basic_json(const typename string_t::value_type*) -- create a + string value from a character pointer + + @since version 1.0.0 + */ + template ::value, int>::type = 0> + basic_json(const CompatibleStringType& val) + : basic_json(string_t(val)) + { + assert_invariant(); + } + + /*! + @brief create a boolean (explicit) + + Creates a JSON boolean type from a given value. + + @param[in] val a boolean value to store + + @complexity Constant. + + @liveexample{The example below demonstrates boolean + values.,basic_json__boolean_t} + + @since version 1.0.0 + */ + basic_json(boolean_t val) noexcept + : m_type(value_t::boolean), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an integer number (explicit) + + Create an integer number JSON value with a given content. + + @tparam T A helper type to remove this function via SFINAE in case @ref + number_integer_t is the same as `int`. In this case, this constructor + would have the same signature as @ref basic_json(const int value). Note + the helper type @a T is not visible in this constructor's interface. + + @param[in] val an integer to create a JSON number from + + @complexity Constant. + + @liveexample{The example below shows the construction of an integer + number value.,basic_json__number_integer_t} + + @sa @ref basic_json(const int) -- create a number value (integer) + @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number + value (integer) from a compatible number type + + @since version 1.0.0 + */ + template ::value) and + std::is_same::value, int>::type = 0> + basic_json(const number_integer_t val) noexcept + : m_type(value_t::number_integer), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an integer number from an enum type (explicit) + + Create an integer number JSON value with a given content. + + @param[in] val an integer to create a JSON number from + + @note This constructor allows to pass enums directly to a constructor. As + C++ has no way of specifying the type of an anonymous enum explicitly, we + can only rely on the fact that such values implicitly convert to int. As + int may already be the same type of number_integer_t, we may need to + switch off the constructor @ref basic_json(const number_integer_t). + + @complexity Constant. + + @liveexample{The example below shows the construction of an integer + number value from an anonymous enum.,basic_json__const_int} + + @sa @ref basic_json(const number_integer_t) -- create a number value + (integer) + @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number + value (integer) from a compatible number type + + @since version 1.0.0 + */ + basic_json(const int val) noexcept + : m_type(value_t::number_integer), + m_value(static_cast(val)) + { + assert_invariant(); + } + + /*! + @brief create an integer number (implicit) + + Create an integer number JSON value with a given content. This constructor + allows any type @a CompatibleNumberIntegerType that can be used to + construct values of type @ref number_integer_t. + + @tparam CompatibleNumberIntegerType An integer type which is compatible to + @ref number_integer_t. Examples include the types `int`, `int32_t`, + `long`, and `short`. + + @param[in] val an integer to create a JSON number from + + @complexity Constant. + + @liveexample{The example below shows the construction of several integer + number values from compatible + types.,basic_json__CompatibleIntegerNumberType} + + @sa @ref basic_json(const number_integer_t) -- create a number value + (integer) + @sa @ref basic_json(const int) -- create a number value (integer) + + @since version 1.0.0 + */ + template ::value and + std::numeric_limits::is_integer and + std::numeric_limits::is_signed, + CompatibleNumberIntegerType>::type = 0> + basic_json(const CompatibleNumberIntegerType val) noexcept + : m_type(value_t::number_integer), + m_value(static_cast(val)) + { + assert_invariant(); + } + + /*! + @brief create an unsigned integer number (explicit) + + Create an unsigned integer number JSON value with a given content. + + @tparam T helper type to compare number_unsigned_t and unsigned int (not + visible in) the interface. + + @param[in] val an integer to create a JSON number from + + @complexity Constant. + + @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number + value (unsigned integer) from a compatible number type + + @since version 2.0.0 + */ + template ::value) and + std::is_same::value, int>::type = 0> + basic_json(const number_unsigned_t val) noexcept + : m_type(value_t::number_unsigned), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an unsigned number (implicit) + + Create an unsigned number JSON value with a given content. This + constructor allows any type @a CompatibleNumberUnsignedType that can be + used to construct values of type @ref number_unsigned_t. + + @tparam CompatibleNumberUnsignedType An integer type which is compatible + to @ref number_unsigned_t. Examples may include the types `unsigned int`, + `uint32_t`, or `unsigned short`. + + @param[in] val an unsigned integer to create a JSON number from + + @complexity Constant. + + @sa @ref basic_json(const number_unsigned_t) -- create a number value + (unsigned) + + @since version 2.0.0 + */ + template ::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, + CompatibleNumberUnsignedType>::type = 0> + basic_json(const CompatibleNumberUnsignedType val) noexcept + : m_type(value_t::number_unsigned), + m_value(static_cast(val)) + { + assert_invariant(); + } + + /*! + @brief create a floating-point number (explicit) + + Create a floating-point number JSON value with a given content. + + @param[in] val a floating-point value to create a JSON number from + + @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6 + disallows NaN values: + > Numeric values that cannot be represented in the grammar below (such as + > Infinity and NaN) are not permitted. + In case the parameter @a val is not a number, a JSON null value is created + instead. + + @complexity Constant. + + @liveexample{The following example creates several floating-point + values.,basic_json__number_float_t} + + @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number + value (floating-point) from a compatible number type + + @since version 1.0.0 + */ + basic_json(const number_float_t val) noexcept + : m_type(value_t::number_float), m_value(val) + { + // replace infinity and NAN by null + if (not std::isfinite(val)) + { + m_type = value_t::null; + m_value = json_value(); + } + + assert_invariant(); + } + + /*! + @brief create an floating-point number (implicit) + + Create an floating-point number JSON value with a given content. This + constructor allows any type @a CompatibleNumberFloatType that can be used + to construct values of type @ref number_float_t. + + @tparam CompatibleNumberFloatType A floating-point type which is + compatible to @ref number_float_t. Examples may include the types `float` + or `double`. + + @param[in] val a floating-point to create a JSON number from + + @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6 + disallows NaN values: + > Numeric values that cannot be represented in the grammar below (such as + > Infinity and NaN) are not permitted. + In case the parameter @a val is not a number, a JSON null value is + created instead. + + @complexity Constant. + + @liveexample{The example below shows the construction of several + floating-point number values from compatible + types.,basic_json__CompatibleNumberFloatType} + + @sa @ref basic_json(const number_float_t) -- create a number value + (floating-point) + + @since version 1.0.0 + */ + template ::value and + std::is_floating_point::value>::type> + basic_json(const CompatibleNumberFloatType val) noexcept + : basic_json(number_float_t(val)) + { + assert_invariant(); + } + + /*! + @brief create a container (array or object) from an initializer list + + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: + + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. + + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: + + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has now way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. + + With the rules described above, the following JSON values cannot be + expressed by an initializer list: + + - the empty array (`[]`): use @ref array(std::initializer_list) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(std::initializer_list) with the same initializer list + in this case + + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. + + @param[in] init initializer list with JSON values + + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(std::initializer_list) and + @ref object(std::initializer_list). + + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect + + @throw std::domain_error if @a type_deduction is `false`, @a manual_type + is `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string; example: `"cannot create object from + initializer list"` + + @complexity Linear in the size of the initializer list @a init. + + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa @ref array(std::initializer_list) -- create a JSON array + value from an initializer list + @sa @ref object(std::initializer_list) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + basic_json(std::initializer_list init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const basic_json& element) + { + return element.is_array() and element.size() == 2 and element[0]. + is_string(); + }); + + // adjust type if type deduction is not wanted + if (not type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (manual_type == value_t::object and not is_an_object) + { + throw std::domain_error("cannot create object from initializer list"); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + std::for_each(init.begin(), init.end(), [this](const basic_json& element) + { + m_value.object->emplace(*(element[0].m_value.string), element[1]); + }); + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init); + } + + assert_invariant(); + } + + /*! + @brief explicitly create an array from an initializer list + + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. + + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(std::initializer_list, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object + + @param[in] init initializer list with JSON values to create an array from + (optional) + + @return JSON array value + + @complexity Linear in the size of @a init. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa @ref basic_json(std::initializer_list, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref object(std::initializer_list) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + static basic_json array(std::initializer_list init = + std::initializer_list()) + { + return basic_json(init, false, value_t::array); + } + + /*! + @brief explicitly create an object from an initializer list + + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. + + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(std::initializer_list), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(std::initializer_list, bool, + value_t). + + @param[in] init initializer list to create an object from (optional) + + @return JSON object value + + @throw std::domain_error if @a init is not a pair whose first elements are + strings; thrown by + @ref basic_json(std::initializer_list, bool, value_t) + + @complexity Linear in the size of @a init. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa @ref basic_json(std::initializer_list, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref array(std::initializer_list) -- create a JSON array + value from an initializer list + + @since version 1.0.0 + */ + static basic_json object(std::initializer_list init = + std::initializer_list()) + { + return basic_json(init, false, value_t::object); + } + + /*! + @brief construct an array with count copies of given value + + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. As postcondition, + `std::distance(begin(),end()) == cnt` holds. + + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy + + @complexity Linear in @a cnt. + + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} + + @since version 1.0.0 + */ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + assert_invariant(); + } + + /*! + @brief construct a JSON container given an iterator range + + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of primitive types (number, boolean, or string), @a first must + be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, std::out_of_range is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector`. + - In case of a null type, std::domain_error is thrown. + + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) + + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) + + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** + + @throw std::domain_error if iterators are not compatible; that is, do not + belong to the same JSON value; example: `"iterators are not compatible"` + @throw std::out_of_range if iterators are for a primitive type (number, + boolean, or string) where an out of range error can be detected easily; + example: `"iterators out of range"` + @throw std::bad_alloc if allocation for object, array, or string fails + @throw std::domain_error if called with a null value; example: `"cannot + use construct with iterators from null"` + + @complexity Linear in distance between @a first and @a last. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 + */ + template ::value or + std::is_same::value, int>::type = 0> + basic_json(InputIT first, InputIT last) + { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + + // make sure iterator fits the current value + if (first.m_object != last.m_object) + { + throw std::domain_error("iterators are not compatible"); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + throw std::out_of_range("iterators out of range"); + } + break; + } + + default: + { + break; + } + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); + break; + } + + default: + { + throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()); + } + } + + assert_invariant(); + } + + /*! + @brief construct a JSON value given an input stream + + @param[in,out] i stream to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @deprecated This constructor is deprecated and will be removed in version + 3.0.0 to unify the interface of the library. Deserialization will be + done by stream operators or by calling one of the `parse` functions, + e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls + like `json j(i);` for an input stream @a i need to be replaced by + `json j = json::parse(i);`. See the example below. + + @liveexample{The example below demonstrates constructing a JSON value from + a `std::stringstream` with and without callback + function.,basic_json__istream} + + @since version 2.0.0, deprecated in version 2.0.3, to be removed in + version 3.0.0 + */ + JSON_DEPRECATED + explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) + { + *this = parser(i, cb).parse(); + assert_invariant(); + } + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + /*! + @brief copy constructor + + Creates a copy of a given JSON value. + + @param[in] other the JSON value to copy + + @complexity Linear in the size of @a other. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. + + @throw std::bad_alloc if allocation for object, array, or string fails. + + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} + + @since version 1.0.0 + */ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + default: + { + break; + } + } + + assert_invariant(); + } + + /*! + @brief move constructor + + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. + + @param[in,out] other value to move to this object + + @post @a other is a JSON null value + + @complexity Constant. + + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} + + @since version 1.0.0 + */ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + assert_invariant(); + } + + /*! + @brief copy assignment + + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the swap() member function. + + @param[in] other value to copy from + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} + + @since version 1.0.0 + */ + reference& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; + } + + /*! + @brief destructor + + Destroys the JSON value and frees all allocated memory_. + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory_ is freed. + + @since version 1.0.0 + */ + ~basic_json() + { + assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + AllocatorType alloc; + alloc.destroy(m_value.object); + alloc.deallocate(m_value.object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + alloc.destroy(m_value.array); + alloc.deallocate(m_value.array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + break; + } + + default: + { + // all other types need no specific destructor + break; + } + } + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /*! + @brief serialization + + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + parameter. + + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + + @return string containing the serialization of the JSON value + + @complexity Linear. + + @liveexample{The following example shows the effect of different @a indent + parameters to the result of the serialization.,dump} + + @see https://docs.python.org/2/library/json.html#json.dump + + @since version 1.0.0 + */ + string_t dump(const int indent = -1) const + { + std::stringstream ss; + // fix locale problems + const static std::locale loc(std::locale(), new DecimalSeparator); + ss.imbue(loc); + + // 6, 15 or 16 digits of precision allows round-trip IEEE 754 + // string->float->string, string->double->string or string->long + // double->string; to be safe, we read this value from + // std::numeric_limits::digits10 + ss.precision(std::numeric_limits::digits10); + + if (indent >= 0) + { + dump(ss, true, static_cast(indent)); + } + else + { + dump(ss, false, 0); + } + + return ss.str(); + } + + /*! + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @since version 1.0.0 + */ + constexpr value_t type() const noexcept + { + return m_type; + } + + /*! + @brief return whether type is primitive + + This function returns true iff the JSON type is primitive (string, number, + boolean, or null). + + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} + + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number + + @since version 1.0.0 + */ + constexpr bool is_primitive() const noexcept + { + return is_null() or is_string() or is_boolean() or is_number(); + } + + /*! + @brief return whether type is structured + + This function returns true iff the JSON type is structured (array or + object). + + @return `true` if type is structured (array or object), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} + + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object + + @since version 1.0.0 + */ + constexpr bool is_structured() const noexcept + { + return is_array() or is_object(); + } + + /*! + @brief return whether value is null + + This function returns true iff the JSON value is null. + + @return `true` if type is null, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} + + @since version 1.0.0 + */ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /*! + @brief return whether value is a boolean + + This function returns true iff the JSON value is a boolean. + + @return `true` if type is boolean, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} + + @since version 1.0.0 + */ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /*! + @brief return whether value is a number + + This function returns true iff the JSON value is a number. This includes + both integer and floating-point values. + + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} + + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number() const noexcept + { + return is_number_integer() or is_number_float(); + } + + /*! + @brief return whether value is an integer number + + This function returns true iff the JSON value is an integer or unsigned + integer number. This excludes floating-point values. + + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true iff the JSON value is an unsigned integer + number. This excludes floating-point and (signed) integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is a floating-point number + + This function returns true iff the JSON value is a floating-point number. + This excludes integer and unsigned integer values. + + @return `true` if type is a floating-point number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} + + @sa @ref is_number() -- check if value is number + @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /*! + @brief return whether value is an object + + This function returns true iff the JSON value is an object. + + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 + */ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /*! + @brief return whether value is an array + + This function returns true iff the JSON value is an array. + + @return `true` if type is array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} + + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /*! + @brief return whether value is a string + + This function returns true iff the JSON value is a string. + + @return `true` if type is string, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} + + @since version 1.0.0 + */ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /*! + @brief return whether value is discarded + + This function returns true iff the JSON value was discarded during parsing + with a callback function (see @ref parser_callback_t). + + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. + + @return `true` if type is discarded, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} + + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /*! + @brief return the type of the JSON value (implicit) + + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} + + @since version 1.0.0 + */ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get an object (explicit) + template ::value and + std::is_convertible::value, int>::type = 0> + T get_impl(T*) const + { + if (is_object()) + { + return T(m_value.object->begin(), m_value.object->end()); + } + throw std::domain_error("type must be object, but is " + type_name()); + } + + /// get an object (explicit) + object_t get_impl(object_t*) const + { + if (is_object()) + { + return *(m_value.object); + } + throw std::domain_error("type must be object, but is " + type_name()); + } + + /// get an array (explicit) + template ::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value, int>::type = 0> + T get_impl(T*) const + { + if (is_array()) + { + T to_vector; + std::transform(m_value.array->begin(), m_value.array->end(), + std::inserter(to_vector, to_vector.end()), [](basic_json i) + { + return i.get(); + }); + return to_vector; + } + throw std::domain_error("type must be array, but is " + type_name()); + } + + /// get an array (explicit) + template ::value and + not std::is_same::value, int>::type = 0> + std::vector get_impl(std::vector*) const + { + if (is_array()) + { + std::vector to_vector; + to_vector.reserve(m_value.array->size()); + std::transform(m_value.array->begin(), m_value.array->end(), + std::inserter(to_vector, to_vector.end()), [](basic_json i) + { + return i.get(); + }); + return to_vector; + } + throw std::domain_error("type must be array, but is " + type_name()); + } + + /// get an array (explicit) + template ::value and + not has_mapped_type::value, int>::type = 0> + T get_impl(T*) const + { + if (is_array()) + { + return T(m_value.array->begin(), m_value.array->end()); + } + throw std::domain_error("type must be array, but is " + type_name()); + } + + /// get an array (explicit) + array_t get_impl(array_t*) const + { + if (is_array()) + { + return *(m_value.array); + } + throw std::domain_error("type must be array, but is " + type_name()); + } + + /// get a string (explicit) + template ::value, int>::type = 0> + T get_impl(T*) const + { + if (is_string()) + { + return *m_value.string; + } + throw std::domain_error("type must be string, but is " + type_name()); + } + + /// get a number (explicit) + template ::value, int>::type = 0> + T get_impl(T*) const + { + switch (m_type) + { + case value_t::number_integer: + { + return static_cast(m_value.number_integer); + } + + case value_t::number_unsigned: + { + return static_cast(m_value.number_unsigned); + } + + case value_t::number_float: + { + return static_cast(m_value.number_float); + } + + default: + { + throw std::domain_error("type must be number, but is " + type_name()); + } + } + } + + /// get a boolean (explicit) + constexpr boolean_t get_impl(boolean_t*) const + { + return is_boolean() + ? m_value.boolean + : throw std::domain_error("type must be boolean, but is " + type_name()); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t*) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t*) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t*) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t*) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t*) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t*) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t*) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t*) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t*) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This funcion helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw std::domain_error if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // helper type + using PointerType = typename std::add_pointer::type; + + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr(); + + if (ptr != nullptr) + { + return *ptr; + } + throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + + obj.type_name()); + } + + public: + + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays + + @return copy of the JSON value, converted to type @a ValueType + + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON; example: `"type must be object, but is null"` + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @internal + The idea of using a casted null pointer to choose the correct + implementation is from . + @endinternal + + @sa @ref operator ValueType() const for implicit conversion + @sa @ref get() for pointer-member access + + @since version 1.0.0 + */ + template ::value, int>::type = 0> + ValueType get() const + { + return get_impl(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template ::value, int>::type = 0> + PointerType get() noexcept + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template ::value, int>::type = 0> + constexpr const PointerType get() const noexcept + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (implicit) + + Implicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template ::value, int>::type = 0> + PointerType get_ptr() noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template ::value and + std::is_const::type>::value, int>::type = 0> + constexpr const PointerType get_ptr() const noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a reference value (implicit) + + Implict reference access to the internally stored JSON value. No copies + are made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. + + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + std::domain_error otherwise + + @throw std::domain_error in case passed type @a ReferenceType is + incompatible with the stored JSON value + + @complexity Constant. + + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 + */ + template ::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template ::value and + std::is_const::type>::value, int>::type = 0> + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON, thrown by @ref get() const + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template ::value and + not std::is_same::value +#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 + and not std::is_same>::value +#endif + , int>::type = 0> + operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /*! + @brief access specified array element with bounds checking + + Returns a reference to the element at specified location @a idx, with + bounds checking. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw std::domain_error if the JSON value is not an array; example: + `"cannot use at() with string"` + @throw std::out_of_range if the index @a idx is out of range of the array; + that is, `idx >= size()`; example: `"array index 7 is out of range"` + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read and + written using `at()`.,at__size_type} + + @since version 1.0.0 + */ + reference at(size_type idx) + { + // at only works for arrays + if (is_array()) + { + try + { + return m_value.array->at(idx); + } + catch (std::out_of_range&) + { + // create better exception explanation + throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + } + } + throw std::domain_error("cannot use at() with " + type_name()); + } + + /*! + @brief access specified array element with bounds checking + + Returns a const reference to the element at specified location @a idx, + with bounds checking. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw std::domain_error if the JSON value is not an array; example: + `"cannot use at() with string"` + @throw std::out_of_range if the index @a idx is out of range of the array; + that is, `idx >= size()`; example: `"array index 7 is out of range"` + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + `at()`.,at__size_type_const} + + @since version 1.0.0 + */ + const_reference at(size_type idx) const + { + // at only works for arrays + if (is_array()) + { + try + { + return m_value.array->at(idx); + } + catch (std::out_of_range&) + { + // create better exception explanation + throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + } + } + throw std::domain_error("cannot use at() with " + type_name()); + } + + /*! + @brief access specified object element with bounds checking + + Returns a reference to the element at with specified key @a key, with + bounds checking. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw std::domain_error if the JSON value is not an object; example: + `"cannot use at() with boolean"` + @throw std::out_of_range if the key @a key is is not stored in the object; + that is, `find(key) == end()`; example: `"key "the fast" not found"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using `at()`.,at__object_t_key_type} + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (is_object()) + { + try + { + return m_value.object->at(key); + } + catch (std::out_of_range&) + { + // create better exception explanation + throw std::out_of_range("key '" + key + "' not found"); + } + } + throw std::domain_error("cannot use at() with " + type_name()); + } + + /*! + @brief access specified object element with bounds checking + + Returns a const reference to the element at with specified key @a key, + with bounds checking. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw std::domain_error if the JSON value is not an object; example: + `"cannot use at() with boolean"` + @throw std::out_of_range if the key @a key is is not stored in the object; + that is, `find(key) == end()`; example: `"key "the fast" not found"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + `at()`.,at__object_t_key_type_const} + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (is_object()) + { + try + { + return m_value.object->at(key); + } + catch (std::out_of_range&) + { + // create better exception explanation + throw std::out_of_range("key '" + key + "' not found"); + } + } + throw std::domain_error("cannot use at() with " + type_name()); + } + + /*! + @brief access specified array element + + Returns a reference to the element at specified location @a idx. + + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw std::domain_error if JSON is not an array or null; example: + `"cannot use operator[] with string"` + + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} + + @since version 1.0.0 + */ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (is_array()) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); + } + + return m_value.array->operator[](idx); + } + throw std::domain_error("cannot use operator[] with " + type_name()); + } + + /*! + @brief access specified array element + + Returns a const reference to the element at specified location @a idx. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw std::domain_error if JSON is not an array; example: `"cannot use + operator[] with null"` + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} + + @since version 1.0.0 + */ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (is_array()) + { + return m_value.array->operator[](idx); + } + throw std::domain_error("cannot use operator[] with " + type_name()); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (is_object()) + { + return m_value.object->operator[](key); + } + throw std::domain_error("cannot use operator[] with " + type_name()); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (is_object()) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + throw std::domain_error("cannot use operator[] with " + type_name()); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + template + reference operator[](T* (&key)[n]) + { + return operator[](static_cast(key)); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @note This function is required for compatibility reasons with Clang. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + template + const_reference operator[](T* (&key)[n]) const + { + return operator[](static_cast(key)); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (is_object()) + { + return m_value.object->operator[](key); + } + throw std::domain_error("cannot use operator[] with " + type_name()); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + const_reference operator[](T* key) const + { + // at only works for objects + if (is_object()) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + throw std::domain_error("cannot use operator[] with " + type_name()); + } + + /*! + @brief access specified object element with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(std::out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. + + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. + + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw std::domain_error if JSON is not an object; example: `"cannot use + value() with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + + @since version 1.0.0 + */ + template ::value, int>::type = 0> + ValueType value(const typename object_t::key_type& key, ValueType default_value) const + { + // at only works for objects + if (is_object()) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return *it; + } + return default_value; + } + throw std::domain_error("cannot use value() with " + type_name()); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(std::out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw std::domain_error if JSON is not an object; example: `"cannot use + value() with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template ::value, int>::type = 0> + ValueType value(const json_pointer& ptr, ValueType default_value) const + { + // at only works for objects + if (is_object()) + { + // if pointer resolves a value, return it or use default value + try + { + return ptr.get_checked(this); + } + catch (std::out_of_range&) + { + return default_value; + } + } + throw std::domain_error("cannot use value() with " + type_name()); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! + @brief access the first element + + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. + + @return In case of a structured type (array or object), a reference to the + first element is returned. In cast of number, string, or boolean values, a + reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw std::out_of_range when called on `null` value + + @liveexample{The following code shows an example for `front()`.,front} + + @sa @ref back() -- access the last element + + @since version 1.0.0 + */ + reference front() + { + return *begin(); + } + + /*! + @copydoc basic_json::front() + */ + const_reference front() const + { + return *cbegin(); + } + + /*! + @brief access the last element + + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode + + @return In case of a structured type (array or object), a reference to the + last element is returned. In cast of number, string, or boolean values, a + reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw std::out_of_range when called on `null` value. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element + + @since version 1.0.0 + */ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /*! + @copydoc basic_json::back() + */ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /*! + @brief remove element given an iterator + + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw std::domain_error if called on a `null` value; example: `"cannot + use erase() with null"` + @throw std::domain_error if called on an iterator which does not belong to + the current JSON value; example: `"iterator does not fit current value"` + @throw std::out_of_range if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` + + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between pos and the end of the container + - strings: linear in the length of the string + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template ::value or + std::is_same::value, int>::type = 0> + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (this != pos.m_object) + { + throw std::domain_error("iterator does not fit current value"); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not pos.m_it.primitive_iterator.is_begin()) + { + throw std::out_of_range("iterator out of range"); + } + + if (is_string()) + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + default: + { + throw std::domain_error("cannot use erase() with " + type_name()); + } + } + + return result; + } + + /*! + @brief remove elements given an iterator range + + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw std::domain_error if called on a `null` value; example: `"cannot + use erase() with null"` + @throw std::domain_error if called on iterators which does not belong to + the current JSON value; example: `"iterators do not fit current value"` + @throw std::out_of_range if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` + + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings: linear in the length of the string + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template ::value or + std::is_same::value, int>::type = 0> + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (this != first.m_object or this != last.m_object) + { + throw std::domain_error("iterators do not fit current value"); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + throw std::out_of_range("iterators out of range"); + } + + if (is_string()) + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + { + throw std::domain_error("cannot use erase() with " + type_name()); + } + } + + return result; + } + + /*! + @brief remove element from a JSON object given a key + + Removes elements from a JSON object with the key value @a key. + + @param[in] key value of the elements to remove + + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. + + @throw std::domain_error when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (is_object()) + { + return m_value.object->erase(key); + } + throw std::domain_error("cannot use erase() with " + type_name()); + } + + /*! + @brief remove element from a JSON array given an index + + Removes element from a JSON array at the index @a idx. + + @param[in] idx index of the element to remove + + @throw std::domain_error when called on a type other than JSON array; + example: `"cannot use erase() with null"` + @throw std::out_of_range when `idx >= size()`; example: `"array index 17 + is out of range"` + + @complexity Linear in distance between @a idx and the end of the container. + + @liveexample{The example shows the effect of `erase()`.,erase__size_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + + @since version 1.0.0 + */ + void erase(const size_type idx) + { + // this erase only works for arrays + if (is_array()) + { + if (idx >= size()) + { + throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + throw std::domain_error("cannot use erase() with " + type_name()); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /*! + @brief find an element in a JSON object + + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. + + @param[in] key key value of the element to search for + + @return Iterator to an element with key equivalent to @a key. If no such + element is found, past-the-end (see end()) iterator is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `find()` is used.,find__key_type} + + @since version 1.0.0 + */ + iterator find(typename object_t::key_type key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(typename object_t::key_type) + */ + const_iterator find(typename object_t::key_type key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + /*! + @brief returns the number of occurrences of a key in a JSON object + + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). + + @param[in] key key value of the element to count + + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} + + @since version 1.0.0 + */ + size_type count(typename object_t::key_type key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(key) : 0; + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /*! + @brief returns an iterator to the first element + + Returns an iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /*! + @brief returns a const iterator to the first element + + Returns a const iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /*! + @brief returns an iterator to one past the last element + + Returns an iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } + + /*! + @brief returns a const iterator to one past the last element + + Returns a const iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /*! + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /*! + @brief returns an iterator to the reverse-end + + Returns an iterator to the reverse-end; that is, one before the first + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /*! + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /*! + @brief returns a const reverse iterator to one before the first + + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + private: + // forward declaration + template + class iteration_proxy; + + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + @note The name of this function is not yet final and may change in the + future. + */ + static iteration_proxy iterator_wrapper(reference cont) + { + return iteration_proxy(cont); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + static iteration_proxy iterator_wrapper(const_reference cont) + { + return iteration_proxy(cont); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /*! + @brief checks whether the container is empty + + Checks if a JSON value has no elements. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + default: + { + // all other types are nonempty + return false; + } + } + } + + /*! + @brief returns the number of elements + + Returns the number of elements in a JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + object | result of function object_t::size() + array | result of function array_t::size() + + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements + + @since version 1.0.0 + */ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + default: + { + // all other types have size 1 + return 1; + } + } + } + + /*! + @brief returns the maximum possible number of elements + + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /*! + @brief clears the contents + + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + + @note Floating-point numbers are set to `0.0` which will be serialized to + `0`. The vale type remains @ref number_float_t. + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} + + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + default: + { + break; + } + } + } + + /*! + @brief add an object to an array + + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. + + @param[in] val the value to add to the JSON array + + @throw std::domain_error when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} + + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + throw std::domain_error("cannot use push_back() with " + type_name()); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + m_value.array->push_back(std::move(val)); + // invalidate object + val.m_type = value_t::null; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + throw std::domain_error("cannot use push_back() with " + type_name()); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + m_value.array->push_back(val); + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. + + @param[in] val the value to add to the JSON object + + @throw std::domain_error when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} + + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (not(is_null() or is_object())) + { + throw std::domain_error("cannot use push_back() with " + type_name()); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array + m_value.object->insert(val); + } + + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(std::initializer_list init) + { + if (is_object() and init.size() == 2 and init.begin()->is_string()) + { + const string_t key = *init.begin(); + push_back(typename object_t::value_type(key, *(init.begin() + 1))); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(std::initializer_list) + */ + reference operator+=(std::initializer_list init) + { + push_back(init); + return *this; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between pos and end of the + container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (is_array()) + { + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); + return result; + } + throw std::domain_error("cannot use insert() with " + type_name()); + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /*! + @brief inserts elements + + Inserts @a cnt copies of @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` + + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__count} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (is_array()) + { + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + return result; + } + throw std::domain_error("cannot use insert() with " + type_name()); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + @throw std::domain_error if @a first and @a last do not belong to the same + JSON value; example: `"iterators do not fit"` + @throw std::domain_error if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__range} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (not is_array()) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // check if range iterators belong to the same JSON object + if (first.m_object != last.m_object) + { + throw std::domain_error("iterators do not fit"); + } + + if (first.m_object == this or last.m_object == this) + { + throw std::domain_error("passed iterators may not belong to container"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert( + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); + return result; + } + + /*! + @brief inserts elements + + Inserts elements from initializer list @a ilist before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from + + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty + + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__ilist} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, std::initializer_list ilist) + { + // insert only works for arrays + if (not is_array()) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); + return result; + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other array to exchange the contents with + + @throw std::domain_error when JSON value is not an array; example: `"cannot + use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} + + @since version 1.0.0 + */ + void swap(array_t& other) + { + // swap only works for arrays + if (is_array()) + { + std::swap(*(m_value.array), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other object to exchange the contents with + + @throw std::domain_error when JSON value is not an object; example: + `"cannot use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} + + @since version 1.0.0 + */ + void swap(object_t& other) + { + // swap only works for objects + if (is_object()) + { + std::swap(*(m_value.object), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other string to exchange the contents with + + @throw std::domain_error when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} + + @since version 1.0.0 + */ + void swap(string_t& other) + { + // swap only works for strings + if (is_string()) + { + std::swap(*(m_value.string), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); + } + } + + /// @} + + + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + private: + /*! + @brief comparison operator for JSON types + + Returns an ordering that is similar to Python: + - order: null < boolean < number < object < array < string + - furthermore, each type is not smaller than itself + + @since version 1.0.0 + */ + friend bool operator<(const value_t lhs, const value_t rhs) noexcept + { + static constexpr std::array order = { + { + 0, // null + 3, // object + 4, // array + 5, // string + 1, // boolean + 2, // integer + 2, // unsigned + 2, // float + } + }; + + // discarded values are not comparable + if (lhs == value_t::discarded or rhs == value_t::discarded) + { + return false; + } + + return order[static_cast(lhs)] < order[static_cast(rhs)]; + } + + public: + /*! + @brief comparison: equal + + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same. + - Integer and floating-point numbers are automatically converted before + comparison. Floating-point numbers are compared indirectly: two + floating-point numbers `f1` and `f2` are considered equal if neither + `f1 > f2` nor `f2 > f1` holds. + - Two JSON null values are equal. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} + + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + { + return *lhs.m_value.array == *rhs.m_value.array; + } + case value_t::object: + { + return *lhs.m_value.object == *rhs.m_value.object; + } + case value_t::null: + { + return true; + } + case value_t::string: + { + return *lhs.m_value.string == *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean == rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer == rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float == rhs.m_value.number_float; + } + default: + { + return false; + } + } + } + if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + + return false; + } + + /*! + @brief comparison: equal + + The functions compares the given JSON value against a null pointer. As the + null pointer can be used to initialize a JSON value to null, a comparison + of JSON value @a v with a null pointer should be equivalent to call + `v.is_null()`. + + @param[in] v JSON value to consider + @return whether @a v is null + + @complexity Constant. + + @liveexample{The example compares several JSON types to the null pointer. + ,operator__equal__nullptr_t} + + @since version 1.0.0 + */ + friend bool operator==(const_reference v, std::nullptr_t) noexcept + { + return v.is_null(); + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, std::nullptr_t) + */ + friend bool operator==(std::nullptr_t, const_reference v) noexcept + { + return v.is_null(); + } + + /*! + @brief comparison: not equal + + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs == rhs); + } + + /*! + @brief comparison: not equal + + The functions compares the given JSON value against a null pointer. As the + null pointer can be used to initialize a JSON value to null, a comparison + of JSON value @a v with a null pointer should be equivalent to call + `not v.is_null()`. + + @param[in] v JSON value to consider + @return whether @a v is not null + + @complexity Constant. + + @liveexample{The example compares several JSON types to the null pointer. + ,operator__notequal__nullptr_t} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference v, std::nullptr_t) noexcept + { + return not v.is_null(); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, std::nullptr_t) + */ + friend bool operator!=(std::nullptr_t, const_reference v) noexcept + { + return not v.is_null(); + } + + /*! + @brief comparison: less than + + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + { + return *lhs.m_value.array < *rhs.m_value.array; + } + case value_t::object: + { + return *lhs.m_value.object < *rhs.m_value.object; + } + case value_t::null: + { + return false; + } + case value_t::string: + { + return *lhs.m_value.string < *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean < rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer < rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float < rhs.m_value.number_float; + } + default: + { + return false; + } + } + } + if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /*! + @brief comparison: less than or equal + + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} + + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return not (rhs < lhs); + } + + /*! + @brief comparison: greater than + + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} + + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs <= rhs); + } + + /*! + @brief comparison: greater than or equal + + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} + + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs < rhs); + } + + /// @} + + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ + + /*! + @brief serialize to stream + + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. The + indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. + + @note During serializaion, the locale and the precision of the output + stream @a o are changed. The original values are restored when the + function returns. + + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize + + @return the stream @a o + + @complexity Linear. + + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} + + @since version 1.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = (o.width() > 0); + const auto indentation = (pretty_print ? o.width() : 0); + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // fix locale problems + const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator)); + // set precision + + // 6, 15 or 16 digits of precision allows round-trip IEEE 754 + // string->float->string, string->double->string or string->long + // double->string; to be safe, we read this value from + // std::numeric_limits::digits10 + const auto old_precision = o.precision(std::numeric_limits::digits10); + + // do the actual serialization + j.dump(o, pretty_print, static_cast(indentation)); + + // reset locale and precision + o.imbue(old_locale); + o.precision(old_precision); + return o; + } + + /*! + @brief serialize to stream + @copydoc operator<<(std::ostream&, const basic_json&) + */ + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } + + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /*! + @brief deserialize from an array + + This function reads from an array of 1-byte values. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 + */ + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + + /*! + @brief deserialize from string literal + + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream + + @since version 1.0.0 (originally for @ref string_t) + */ + template ::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static basic_json parse(const CharPT s, + const parser_callback_t cb = nullptr) + { + return parser(reinterpret_cast(s), cb).parse(); + } + + /*! + @brief deserialize from stream + + @param[in,out] i stream to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} + + @sa @ref parse(const char*, const parser_callback_t) for a version + that reads from a string + + @since version 1.0.0 + */ + static basic_json parse(std::istream& i, + const parser_callback_t cb = nullptr) + { + return parser(i, cb).parse(); + } + + /*! + @copydoc parse(std::istream&, const parser_callback_t) + */ + static basic_json parse(std::istream&& i, + const parser_callback_t cb = nullptr) + { + return parser(i, cb).parse(); + } + + /*! + @brief deserialize from an iterator range with contiguous storage + + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} + + @since version 2.0.3 + */ + template ::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate(first, last, std::make_pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } + + return parser(first, last, cb).parse(); + } + + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam ContiguousContainer container type with contiguous storage + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ + template ::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))> + ::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + + /*! + @brief deserialize from stream + + Deserializes an input stream to a JSON value. + + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to + + @throw std::invalid_argument in case of parse errors + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} + + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing + + @since version 1.0.0 + */ + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + j = parser(i).parse(); + return i; + } + + /*! + @brief deserialize from stream + @copydoc operator<<(basic_json&, std::istream&) + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + j = parser(i).parse(); + return i; + } + + /// @} + + + private: + /////////////////////////// + // convenience functions // + /////////////////////////// + + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return basically a string representation of a the @a m_type member + + @complexity Constant. + + @since version 1.0.0 + */ + std::string type_name() const + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } + } + + /*! + @brief calculates the extra space to escape a JSON string + + @param[in] s the string to escape + @return the number of characters required to escape string @a s + + @complexity Linear in the length of string @a s. + */ + static std::size_t extra_space(const string_t& s) noexcept + { + return std::accumulate(s.begin(), s.end(), size_t{}, + [](size_t res, typename string_t::value_type c) + { + switch (c) + { + case '"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + { + // from c (1 byte) to \x (2 bytes) + return res + 1; + } + + default: + { + if (c >= 0x00 and c <= 0x1f) + { + // from c (1 byte) to \uxxxx (6 bytes) + return res + 5; + } + return res; + } + } + }); + } + + /*! + @brief escape a string + + Escape a string by replacing certain special characters by a sequence of + an escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. + + @param[in] s the string to escape + @return the escaped string + + @complexity Linear in the length of string @a s. + */ + static string_t escape_string(const string_t& s) + { + const auto space = extra_space(s); + if (space == 0) + { + return s; + } + + // create a result string of necessary size + string_t result(s.size() + space, '\\'); + std::size_t pos = 0; + + for (const auto& c : s) + { + switch (c) + { + // quotation mark (0x22) + case '"': + { + result[pos + 1] = '"'; + pos += 2; + break; + } + + // reverse solidus (0x5c) + case '\\': + { + // nothing to change + pos += 2; + break; + } + + // backspace (0x08) + case '\b': + { + result[pos + 1] = 'b'; + pos += 2; + break; + } + + // formfeed (0x0c) + case '\f': + { + result[pos + 1] = 'f'; + pos += 2; + break; + } + + // newline (0x0a) + case '\n': + { + result[pos + 1] = 'n'; + pos += 2; + break; + } + + // carriage return (0x0d) + case '\r': + { + result[pos + 1] = 'r'; + pos += 2; + break; + } + + // horizontal tab (0x09) + case '\t': + { + result[pos + 1] = 't'; + pos += 2; + break; + } + + default: + { + if (c >= 0x00 and c <= 0x1f) + { + // convert a number 0..15 to its hex representation + // (0..f) + static const char hexify[16] = + { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + // print character c as \uxxxx + for (const char m : + { + 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] + }) + { + result[++pos] = m; + } + + ++pos; + } + else + { + // all other characters are added as-is + result[pos++] = c; + } + break; + } + } + } + + return result; + } + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. Note that + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + + @param[out] o stream to write to + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(std::ostream& o, + const bool pretty_print, + const unsigned int indent_step, + const unsigned int current_indent = 0) const + { + // variable to hold indentation for recursive calls + unsigned int new_indent = current_indent; + + switch (m_type) + { + case value_t::object: + { + if (m_value.object->empty()) + { + o << "{}"; + return; + } + + o << "{"; + + // increase indentation + if (pretty_print) + { + new_indent += indent_step; + o << "\n"; + } + + for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) + { + if (i != m_value.object->cbegin()) + { + o << (pretty_print ? ",\n" : ","); + } + o << string_t(new_indent, ' ') << "\"" + << escape_string(i->first) << "\":" + << (pretty_print ? " " : ""); + i->second.dump(o, pretty_print, indent_step, new_indent); + } + + // decrease indentation + if (pretty_print) + { + new_indent -= indent_step; + o << "\n"; + } + + o << string_t(new_indent, ' ') + "}"; + return; + } + + case value_t::array: + { + if (m_value.array->empty()) + { + o << "[]"; + return; + } + + o << "["; + + // increase indentation + if (pretty_print) + { + new_indent += indent_step; + o << "\n"; + } + + for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) + { + if (i != m_value.array->cbegin()) + { + o << (pretty_print ? ",\n" : ","); + } + o << string_t(new_indent, ' '); + i->dump(o, pretty_print, indent_step, new_indent); + } + + // decrease indentation + if (pretty_print) + { + new_indent -= indent_step; + o << "\n"; + } + + o << string_t(new_indent, ' ') << "]"; + return; + } + + case value_t::string: + { + o << string_t("\"") << escape_string(*m_value.string) << "\""; + return; + } + + case value_t::boolean: + { + o << (m_value.boolean ? "true" : "false"); + return; + } + + case value_t::number_integer: + { + o << m_value.number_integer; + return; + } + + case value_t::number_unsigned: + { + o << m_value.number_unsigned; + return; + } + + case value_t::number_float: + { + if (m_value.number_float == 0) + { + // special case for zero to get "0.0"/"-0.0" + o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0"); + } + else + { + o << m_value.number_float; + } + return; + } + + case value_t::discarded: + { + o << ""; + return; + } + + case value_t::null: + { + o << "null"; + } + } + } + + private: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + + + private: + /////////////// + // iterators // + /////////////// + + /*! + @brief an iterator for primitive JSON types + + This class models an iterator for primitive JSON types (boolean, number, + string). It's only purpose is to allow the iterator/const_iterator classes + to "iterate" over primitive values. Internally, the iterator is modeled by + a `difference_type` variable. Value begin_value (`0`) models the begin, + end_value (`1`) models past the end. + */ + class primitive_iterator_t + { + public: + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return (m_it == begin_value); + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return (m_it == end_value); + } + + /// return reference to the value to change and compare + operator difference_type&() noexcept + { + return m_it; + } + + /// return value to compare + constexpr operator difference_type() const noexcept + { + return m_it; + } + + private: + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = std::numeric_limits::denorm_min(); + }; + + /*! + @brief an iterator value + + @note This structure could easily be a union, but MSVC currently does not + allow unions members with complex constructors, see + https://github.com/nlohmann/json/pull/105. + */ + struct internal_iterator + { + /// iterator for JSON objects + typename object_t::iterator object_iterator; + /// iterator for JSON arrays + typename array_t::iterator array_iterator; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator; + + /// create an uninitialized internal_iterator + internal_iterator() noexcept + : object_iterator(), array_iterator(), primitive_iterator() + { + } + }; + + /// proxy class for the iterator_wrapper functions + template + class iteration_proxy + { + private: + /// helper class for iteration + class iteration_proxy_internal + { + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + size_t array_index = 0; + + public: + explicit iteration_proxy_internal(IteratorType it) noexcept + : anchor(it) + { + } + + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_internal& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + string_t key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + return std::to_string(array_index); + } + + // use key from the object + case value_t::object: + { + return anchor.key(); + } + + // use an empty key for all primitive types + default: + { + return ""; + } + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } + }; + + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) + : container(cont) + { + } + + /// return iterator begin (needed for range-based for) + iteration_proxy_internal begin() noexcept + { + return iteration_proxy_internal(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_internal end() noexcept + { + return iteration_proxy_internal(container.end()); + } + }; + + public: + /*! + @brief a const random access iterator for the @ref basic_json class + + This class implements a const iterator for the @ref basic_json class. From + this class, the @ref iterator class is derived. + + @note An iterator is called *initialized* when a pointer to a JSON value + has been set (e.g., by a constructor or a copy assignment). If the + iterator is default-constructed, it is *uninitialized* and most + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** + + @requirement The class satisfies the following concept requirements: + - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + The iterator that can be moved to point (forward and backward) to any + element in constant time. + + @since version 1.0.0 + */ + class const_iterator : public std::iterator + { + /// allow basic_json to access private members + friend class basic_json; + + public: + /// the type of the values when the iterator is dereferenced + using value_type = value_type; + /// a type to represent differences between iterators + using difference_type = difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = const_pointer; + /// defines a reference to the type iterated over (value_type) + using reference = const_reference; + /// the category of the iterator + using iterator_category = std::bidirectional_iterator_tag; + + /// default constructor + const_iterator() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit const_iterator(pointer object) noexcept + : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @brief copy constructor given a non-const iterator + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + explicit const_iterator(const iterator& other) noexcept + : m_object(other.m_object) + { + if (m_object != nullptr) + { + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = other.m_it.object_iterator; + break; + } + + case value_t::array: + { + m_it.array_iterator = other.m_it.array_iterator; + break; + } + + default: + { + m_it.primitive_iterator = other.m_it.primitive_iterator; + break; + } + } + } + } + + /*! + @brief copy constructor + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + const_iterator(const const_iterator& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + { + } + + /*! + @brief copy assignment + @param[in,out] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + const_iterator& operator=(const_iterator other) noexcept( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_object, other.m_object); + std::swap(m_it, other.m_it); + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return *m_object; + } + throw std::out_of_range("cannot get value"); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return m_object; + } + throw std::out_of_range("cannot get value"); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator operator--(int) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const const_iterator& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + throw std::domain_error("cannot compare iterators of different containers"); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + return (m_it.object_iterator == other.m_it.object_iterator); + } + + case value_t::array: + { + return (m_it.array_iterator == other.m_it.array_iterator); + } + + default: + { + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const const_iterator& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const const_iterator& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + throw std::domain_error("cannot compare iterators of different containers"); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + throw std::domain_error("cannot compare order of object iterators"); + } + + case value_t::array: + { + return (m_it.array_iterator < other.m_it.array_iterator); + } + + default: + { + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const const_iterator& other) const + { + return not other.operator <(*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const const_iterator& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const const_iterator& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + throw std::domain_error("cannot use offsets with object iterators"); + } + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const const_iterator& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + throw std::domain_error("cannot use offsets with object iterators"); + } + + case value_t::array: + { + return m_it.array_iterator - other.m_it.array_iterator; + } + + default: + { + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + throw std::domain_error("cannot use operator[] for object iterators"); + } + + case value_t::array: + { + return *std::next(m_it.array_iterator, n); + } + + case value_t::null: + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.primitive_iterator == -n) + { + return *m_object; + } + throw std::out_of_range("cannot get value"); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + typename object_t::key_type key() const + { + assert(m_object != nullptr); + + if (m_object->is_object()) + { + return m_it.object_iterator->first; + } + throw std::domain_error("cannot use key() for non-object iterators"); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator m_it = internal_iterator(); + }; + + /*! + @brief a mutable random access iterator for the @ref basic_json class + + @requirement The class satisfies the following concept requirements: + - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + The iterator that can be moved to point (forward and backward) to any + element in constant time. + - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): + It is possible to write to the pointed-to element. + + @since version 1.0.0 + */ + class iterator : public const_iterator + { + public: + using base_iterator = const_iterator; + using pointer = pointer; + using reference = reference; + + /// default constructor + iterator() = default; + + /// constructor for a given JSON instance + explicit iterator(pointer object) noexcept + : base_iterator(object) + { + } + + /// copy constructor + iterator(const iterator& other) noexcept + : base_iterator(other) + { + } + + /// copy assignment + iterator& operator=(iterator other) noexcept( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + base_iterator::operator=(other); + return *this; + } + + /// return a reference to the value pointed to by the iterator + reference operator*() const + { + return const_cast(base_iterator::operator*()); + } + + /// dereference the iterator + pointer operator->() const + { + return const_cast(base_iterator::operator->()); + } + + /// post-increment (it++) + iterator operator++(int) + { + iterator result = *this; + base_iterator::operator++(); + return result; + } + + /// pre-increment (++it) + iterator& operator++() + { + base_iterator::operator++(); + return *this; + } + + /// post-decrement (it--) + iterator operator--(int) + { + iterator result = *this; + base_iterator::operator--(); + return result; + } + + /// pre-decrement (--it) + iterator& operator--() + { + base_iterator::operator--(); + return *this; + } + + /// add to iterator + iterator& operator+=(difference_type i) + { + base_iterator::operator+=(i); + return *this; + } + + /// subtract from iterator + iterator& operator-=(difference_type i) + { + base_iterator::operator-=(i); + return *this; + } + + /// add to iterator + iterator operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /// subtract from iterator + iterator operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /// return difference + difference_type operator-(const iterator& other) const + { + return base_iterator::operator-(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return const_cast(base_iterator::operator[](n)); + } + + /// return the value of an iterator + reference value() const + { + return const_cast(base_iterator::value()); + } + }; + + /*! + @brief a template for a reverse iterator class + + @tparam Base the base iterator type to reverse. Valid types are @ref + iterator (to create @ref reverse_iterator) and @ref const_iterator (to + create @ref const_reverse_iterator). + + @requirement The class satisfies the following concept requirements: + - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + The iterator that can be moved to point (forward and backward) to any + element in constant time. + - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + + @since version 1.0.0 + */ + template + class json_reverse_iterator : public std::reverse_iterator + { + public: + /// shortcut to the reverse iterator adaptor + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) + { + } + + /// create reverse iterator from base class + json_reverse_iterator(const base_iterator& it) noexcept + : base_iterator(it) + { + } + + /// post-increment (it++) + json_reverse_iterator operator++(int) + { + return base_iterator::operator++(1); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + base_iterator::operator++(); + return *this; + } + + /// post-decrement (it--) + json_reverse_iterator operator--(int) + { + return base_iterator::operator--(1); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + base_iterator::operator--(); + return *this; + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + base_iterator::operator+=(i); + return *this; + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return this->base() - other.base(); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + typename object_t::key_type key() const + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator *(); + } + }; + + + private: + ////////////////////// + // lexer and parser // + ////////////////////// + + /*! + @brief lexical analysis + + This class organizes the lexical analysis during JSON deserialization. The + core of it is a scanner generated by [re2c](http://re2c.org) that + processes a buffer and recognizes tokens according to RFC 7159. + */ + class lexer + { + public: + /// token types for the parser + enum class token_type + { + uninitialized, + ///< indicating the scanner is uninitialized + literal_true, + ///< the `true` literal + literal_false, + ///< the `false` literal + literal_null, + ///< the `null` literal + value_string, + ///< a string -- use get_string() for actual value + value_number, + ///< a number -- use get_number() for actual value + begin_array, + ///< the character for array begin `[` + begin_object, + ///< the character for object begin `{` + end_array, + ///< the character for array end `]` + end_object, + ///< the character for object end `}` + name_separator, + ///< the name separator `:` + value_separator, + ///< the value separator `,` + parse_error, + ///< indicating a parse error + end_of_input ///< indicating the end of the input buffer + }; + + /// the char type to use in the lexer + using lexer_char_t = unsigned char; + + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) + { + assert(m_content != nullptr); + m_start = m_cursor = m_content; + m_limit = m_content + len; + } + + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() + { + // fill buffer + fill_line_buffer(); + } + + // switch off unwanted functions (due to pointer members) + lexer() = delete; + lexer(const lexer&) = delete; + lexer operator=(const lexer&) = delete; + + /*! + @brief create a string from one or two Unicode code points + + There are two cases: (1) @a codepoint1 is in the Basic Multilingual + Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2) + @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to + represent a code point above U+FFFF. + + @param[in] codepoint1 the code point (can be high surrogate) + @param[in] codepoint2 the code point (can be low surrogate or 0) + + @return string representation of the code point; the length of the + result string is between 1 and 4 characters. + + @throw std::out_of_range if code point is > 0x10ffff; example: `"code + points above 0x10FFFF are invalid"` + @throw std::invalid_argument if the low surrogate is invalid; example: + `""missing or wrong low surrogate""` + + @complexity Constant. + + @see + */ + static string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) + { + // calculate the code point from the given code points + std::size_t codepoint = codepoint1; + + // check if codepoint1 is a high surrogate + if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) + { + // check if codepoint2 is a low surrogate + if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) + { + codepoint = + // high surrogate occupies the most significant 22 bits + (codepoint1 << 10) + // low surrogate occupies the least significant 15 bits + + codepoint2 + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00; + } + else + { + throw std::invalid_argument("missing or wrong low surrogate"); + } + } + + string_t result; + + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + result.append(1, static_cast(codepoint)); + } + else if (codepoint <= 0x7ff) + { + // 2-byte characters: 110xxxxx 10xxxxxx + result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0xffff) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); + result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0x10ffff) + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); + result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else + { + throw std::out_of_range("code points above 0x10FFFF are invalid"); + } + + return result; + } + + /// return name of values of type token_type (only used for errors) + static std::string token_type_name(const token_type t) + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_number: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + default: + { + // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE + } + } + } + + /*! + This function implements a scanner for JSON. It is specified using + regular expressions that try to follow RFC 7159 as close as possible. + These regular expressions are then translated into a minimized + deterministic finite automaton (DFA) by the tool + [re2c](http://re2c.org). As a result, the translated code for this + function consists of a large block of code with `goto` jumps. + + @return the class of the next token read from the buffer + + @complexity Linear in the length of the input.\n + + Proposition: The loop below will always terminate for finite input.\n + + Proof (by contradiction): Assume a finite input. To loop forever, the + loop must never hit code with a `break` statement. The only code + snippets without a `break` statement are the continue statements for + whitespace and byte-order-marks. To loop forever, the input must be an + infinite sequence of whitespace or byte-order-marks. This contradicts + the assumption of finite input, q.e.d. + */ + token_type scan() + { + while (true) + { + // pointer for backtracking information + m_marker = nullptr; + + // remember the begin of the token + m_start = m_cursor; + assert(m_start != nullptr); + + + { + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 32, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 160, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + }; + if ((m_limit - m_cursor) < 5) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + if (yych <= '\\') + { + if (yych <= '-') + { + if (yych <= '"') + { + if (yych <= 0x00) + { + goto basic_json_parser_2; + } + if (yych <= '!') + { + goto basic_json_parser_4; + } + goto basic_json_parser_9; + } + if (yych <= '+') + { + goto basic_json_parser_4; + } + if (yych <= ',') + { + goto basic_json_parser_10; + } + goto basic_json_parser_12; + } + if (yych <= '9') + { + if (yych <= '/') + { + goto basic_json_parser_4; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + goto basic_json_parser_15; + } + if (yych <= ':') + { + goto basic_json_parser_17; + } + if (yych == '[') + { + goto basic_json_parser_19; + } + goto basic_json_parser_4; + } + if (yych <= 't') + { + if (yych <= 'f') + { + if (yych <= ']') + { + goto basic_json_parser_21; + } + if (yych <= 'e') + { + goto basic_json_parser_4; + } + goto basic_json_parser_23; + } + if (yych == 'n') + { + goto basic_json_parser_24; + } + if (yych <= 's') + { + goto basic_json_parser_4; + } + goto basic_json_parser_25; + } + if (yych <= '|') + { + if (yych == '{') + { + goto basic_json_parser_26; + } + goto basic_json_parser_4; + } + if (yych <= '}') + { + goto basic_json_parser_28; + } + if (yych == 0xEF) + { + goto basic_json_parser_30; + } + goto basic_json_parser_4; + basic_json_parser_2: + ++m_cursor; + { + last_token_type = token_type::end_of_input; + break; + } + basic_json_parser_4: + ++m_cursor; + basic_json_parser_5: + { + last_token_type = token_type::parse_error; + break; + } + basic_json_parser_6: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + { + continue; + } + basic_json_parser_9: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych <= 0x1F) + { + goto basic_json_parser_5; + } + goto basic_json_parser_32; + basic_json_parser_10: + ++m_cursor; + { + last_token_type = token_type::value_separator; + break; + } + basic_json_parser_12: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_5; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + if (yych <= '9') + { + goto basic_json_parser_15; + } + goto basic_json_parser_5; + basic_json_parser_13: + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_37; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_38; + } + if (yych == 'e') + { + goto basic_json_parser_38; + } + } + basic_json_parser_14: + { + last_token_type = token_type::value_number; + break; + } + basic_json_parser_15: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yybm[0 + yych] & 64) + { + goto basic_json_parser_15; + } + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_37; + } + goto basic_json_parser_14; + } + if (yych <= 'E') + { + goto basic_json_parser_38; + } + if (yych == 'e') + { + goto basic_json_parser_38; + } + goto basic_json_parser_14; + basic_json_parser_17: + ++m_cursor; + { + last_token_type = token_type::name_separator; + break; + } + basic_json_parser_19: + ++m_cursor; + { + last_token_type = token_type::begin_array; + break; + } + basic_json_parser_21: + ++m_cursor; + { + last_token_type = token_type::end_array; + break; + } + basic_json_parser_23: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') + { + goto basic_json_parser_39; + } + goto basic_json_parser_5; + basic_json_parser_24: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'u') + { + goto basic_json_parser_40; + } + goto basic_json_parser_5; + basic_json_parser_25: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'r') + { + goto basic_json_parser_41; + } + goto basic_json_parser_5; + basic_json_parser_26: + ++m_cursor; + { + last_token_type = token_type::begin_object; + break; + } + basic_json_parser_28: + ++m_cursor; + { + last_token_type = token_type::end_object; + break; + } + basic_json_parser_30: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 0xBB) + { + goto basic_json_parser_42; + } + goto basic_json_parser_5; + basic_json_parser_31: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(); + } + yych = *m_cursor; + basic_json_parser_32: + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_31; + } + if (yych <= 0x1F) + { + goto basic_json_parser_33; + } + if (yych <= '"') + { + goto basic_json_parser_34; + } + goto basic_json_parser_36; + basic_json_parser_33: + m_cursor = m_marker; + if (yyaccept == 0) + { + goto basic_json_parser_5; + } + goto basic_json_parser_14; + basic_json_parser_34: + ++m_cursor; + { + last_token_type = token_type::value_string; + break; + } + basic_json_parser_36: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yych <= 'e') + { + if (yych <= '/') + { + if (yych == '"') + { + goto basic_json_parser_31; + } + if (yych <= '.') + { + goto basic_json_parser_33; + } + goto basic_json_parser_31; + } + if (yych <= '\\') + { + if (yych <= '[') + { + goto basic_json_parser_33; + } + goto basic_json_parser_31; + } + if (yych == 'b') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + if (yych <= 'q') + { + if (yych <= 'f') + { + goto basic_json_parser_31; + } + if (yych == 'n') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + if (yych <= 's') + { + if (yych <= 'r') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + if (yych <= 't') + { + goto basic_json_parser_31; + } + if (yych <= 'u') + { + goto basic_json_parser_43; + } + goto basic_json_parser_33; + basic_json_parser_37: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_44; + } + goto basic_json_parser_33; + basic_json_parser_38: + yych = *++m_cursor; + if (yych <= ',') + { + if (yych == '+') + { + goto basic_json_parser_46; + } + goto basic_json_parser_33; + } + if (yych <= '-') + { + goto basic_json_parser_46; + } + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_47; + } + goto basic_json_parser_33; + basic_json_parser_39: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_49; + } + goto basic_json_parser_33; + basic_json_parser_40: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_50; + } + goto basic_json_parser_33; + basic_json_parser_41: + yych = *++m_cursor; + if (yych == 'u') + { + goto basic_json_parser_51; + } + goto basic_json_parser_33; + basic_json_parser_42: + yych = *++m_cursor; + if (yych == 0xBF) + { + goto basic_json_parser_52; + } + goto basic_json_parser_33; + basic_json_parser_43: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_54; + } + goto basic_json_parser_33; + } + if (yych <= 'F') + { + goto basic_json_parser_54; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_54; + } + goto basic_json_parser_33; + basic_json_parser_44: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yych <= 'D') + { + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; + } + if (yych <= 'E') + { + goto basic_json_parser_38; + } + if (yych == 'e') + { + goto basic_json_parser_38; + } + goto basic_json_parser_14; + basic_json_parser_46: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych >= ':') + { + goto basic_json_parser_33; + } + basic_json_parser_47: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_47; + } + goto basic_json_parser_14; + basic_json_parser_49: + yych = *++m_cursor; + if (yych == 's') + { + goto basic_json_parser_55; + } + goto basic_json_parser_33; + basic_json_parser_50: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_56; + } + goto basic_json_parser_33; + basic_json_parser_51: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_58; + } + goto basic_json_parser_33; + basic_json_parser_52: + ++m_cursor; + { + continue; + } + basic_json_parser_54: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_60; + } + goto basic_json_parser_33; + } + if (yych <= 'F') + { + goto basic_json_parser_60; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_60; + } + goto basic_json_parser_33; + basic_json_parser_55: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_61; + } + goto basic_json_parser_33; + basic_json_parser_56: + ++m_cursor; + { + last_token_type = token_type::literal_null; + break; + } + basic_json_parser_58: + ++m_cursor; + { + last_token_type = token_type::literal_true; + break; + } + basic_json_parser_60: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_63; + } + goto basic_json_parser_33; + } + if (yych <= 'F') + { + goto basic_json_parser_63; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_63; + } + goto basic_json_parser_33; + basic_json_parser_61: + ++m_cursor; + { + last_token_type = token_type::literal_false; + break; + } + basic_json_parser_63: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(); + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + if (yych <= 'F') + { + goto basic_json_parser_31; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + } + + return last_token_type; + } + + /*! + @brief append data from the stream to the line buffer + + This function is called by the scan() function when the end of the + buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be + incremented without leaving the limits of the line buffer. Note re2c + decides when to call this function. + + If the lexer reads from contiguous storage, there is no trailing null + byte. Therefore, this function must make sure to add these padding + null bytes. + + If the lexer reads from an input stream, this function reads the next + line of the input. + + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor + + @post + u u u u u x x x x x x x . . . . . . + ^ ^ ^ + | m_cursor m_limit + m_start + m_content + */ + void fill_line_buffer() + { + // number of processed characters (p) + const auto offset_start = m_start - m_content; + // offset for m_marker wrt. to m_start + const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; + // number of unprocessed characters (u) + const auto offset_cursor = m_cursor - m_start; + + // no stream is used or end of file is reached + if (m_stream == nullptr or m_stream->eof()) + { + // copy unprocessed characters to line buffer + m_line_buffer.clear(); + for (m_cursor = m_start; m_cursor != m_limit; ++m_cursor) + { + m_line_buffer.append(1, static_cast(*m_cursor)); + } + + // append 5 characters (size of longest keyword "false") to + // make sure that there is sufficient space between m_cursor + // and m_limit + m_line_buffer.append(5, '\0'); + } + else + { + // delete processed characters from line buffer + m_line_buffer.erase(0, static_cast(offset_start)); + // read next line from input stream + std::string line; + std::getline(*m_stream, line); + // add line with newline symbol to the line buffer + m_line_buffer += line + "\n"; + } + + // set pointers + m_content = reinterpret_cast(m_line_buffer.c_str()); + assert(m_content != nullptr); + m_start = m_content; + m_marker = m_start + offset_marker; + m_cursor = m_start + offset_cursor; + m_limit = m_start + m_line_buffer.size(); + } + + /// return string representation of last read token + string_t get_token_string() const + { + assert(m_start != nullptr); + return string_t(reinterpret_cast(m_start), + static_cast(m_cursor - m_start)); + } + + /*! + @brief return string value for string tokens + + The function iterates the characters between the opening and closing + quotes of the string value. The complete string is the range + [m_start,m_cursor). Consequently, we iterate from m_start+1 to + m_cursor-1. + + We differentiate two cases: + + 1. Escaped characters. In this case, a new character is constructed + according to the nature of the escape. Some escapes create new + characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied + as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape + `"\\uxxxx"` need special care. In this case, to_unicode takes care + of the construction of the values. + 2. Unescaped characters are copied as is. + + @pre `m_cursor - m_start >= 2`, meaning the length of the last token + is at least 2 bytes which is trivially true for any string (which + consists of at least two quotes). + + " c1 c2 c3 ... " + ^ ^ + m_start m_cursor + + @complexity Linear in the length of the string.\n + + Lemma: The loop body will always terminate.\n + + Proof (by contradiction): Assume the loop body does not terminate. As + the loop body does not contain another loop, one of the called + functions must never return. The called functions are `std::strtoul` + and to_unicode. Neither function can loop forever, so the loop body + will never loop forever which contradicts the assumption that the loop + body does not terminate, q.e.d.\n + + Lemma: The loop condition for the for loop is eventually false.\n + + Proof (by contradiction): Assume the loop does not terminate. Due to + the above lemma, this can only be due to a tautological loop + condition; that is, the loop condition i < m_cursor - 1 must always be + true. Let x be the change of i for any loop iteration. Then + m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This + can be rephrased to m_cursor - m_start - 2 > x. With the + precondition, we x <= 0, meaning that the loop condition holds + indefinitly if i is always decreased. However, observe that the value + of i is strictly increasing with each iteration, as it is incremented + by 1 in the iteration expression and never decremented inside the loop + body. Hence, the loop condition will eventually be false which + contradicts the assumption that the loop condition is a tautology, + q.e.d. + + @return string value of current token without opening and closing + quotes + @throw std::out_of_range if to_unicode fails + */ + string_t get_string() const + { + assert(m_cursor - m_start >= 2); + + string_t result; + result.reserve(static_cast(m_cursor - m_start - 2)); + + // iterate the result between the quotes + for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) + { + // process escaped characters + if (*i == '\\') + { + // read next character + ++i; + + switch (*i) + { + // the default escapes + case 't': + { + result += "\t"; + break; + } + case 'b': + { + result += "\b"; + break; + } + case 'f': + { + result += "\f"; + break; + } + case 'n': + { + result += "\n"; + break; + } + case 'r': + { + result += "\r"; + break; + } + case '\\': + { + result += "\\"; + break; + } + case '/': + { + result += "/"; + break; + } + case '"': + { + result += "\""; + break; + } + + // unicode + case 'u': + { + // get code xxxx from uxxxx + auto codepoint = std::strtoul(std::string( + reinterpret_cast(i + + 1), + 4).c_str(), nullptr, 16); + + // check if codepoint is a high surrogate + if (codepoint >= 0xD800 and codepoint <= 0xDBFF) + { + // make sure there is a subsequent unicode + if ((i + 6 >= m_limit) or *(i + 5) != '\\' or *(i + 6) != 'u') + { + throw std::invalid_argument("missing low surrogate"); + } + + // get code yyyy from uxxxx\uyyyy + auto codepoint2 = std::strtoul( + std::string(reinterpret_cast + (i + 7), 4).c_str(), nullptr, 16); + result += to_unicode(codepoint, codepoint2); + // skip the next 10 characters (xxxx\uyyyy) + i += 10; + } + else + { + // add unicode character(s) + result += to_unicode(codepoint); + // skip the next four characters (xxxx) + i += 4; + } + break; + } + } + } + else + { + // all other characters are just copied to the end of the + // string + result.append(1, static_cast(*i)); + } + } + + return result; + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast(nullptr). + + @param[in] type the @ref number_float_t in use + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + long double str_to_float_t(long double* /* type */, char** endptr) const + { + return std::strtold(reinterpret_cast(m_start), endptr); + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast(nullptr). + + @param[in] type the @ref number_float_t in use + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + double str_to_float_t(double* /* type */, char** endptr) const + { + return std::strtod(reinterpret_cast(m_start), endptr); + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast(nullptr). + + @param[in] type the @ref number_float_t in use + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + float str_to_float_t(float* /* type */, char** endptr) const + { + return std::strtof(reinterpret_cast(m_start), endptr); + } + + /*! + @brief return number value for number tokens + + This function translates the last token into the most appropriate + number type (either integer, unsigned integer or floating point), + which is passed back to the caller via the result parameter. + + This function parses the integer component up to the radix point or + exponent while collecting information about the 'floating point + representation', which it stores in the result parameter. If there is + no radix point or exponent, and the number can fit into a @ref + number_integer_t or @ref number_unsigned_t then it sets the result + parameter accordingly. + + If the number is a floating point number the number is then parsed + using @a std:strtod (or @a std:strtof or @a std::strtold). + + @param[out] result @ref basic_json object to receive the number, or + NAN if the conversion read past the current token. The latter case + needs to be treated by the caller function. + */ + void get_number(basic_json& result) const + { + assert(m_start != nullptr); + + const lexer_char_t* curptr = m_start; + + // accumulate the integer conversion result (unsigned for now) + number_unsigned_t value = 0; + + // maximum absolute value of the relevant integer type + number_unsigned_t max; + + // temporarily store the type to avoid unecessary bitfield access + value_t type; + + // look for sign + if (*curptr == '-') + { + type = value_t::number_integer; + max = static_cast((std::numeric_limits::max)()) + 1; + curptr++; + } + else + { + type = value_t::number_unsigned; + max = static_cast((std::numeric_limits::max)()); + } + + // count the significant figures + for (; curptr < m_cursor; curptr++) + { + // quickly skip tests if a digit + if (*curptr < '0' || *curptr > '9') + { + if (*curptr == '.') + { + // don't count '.' but change to float + type = value_t::number_float; + continue; + } + // assume exponent (if not then will fail parse): change to + // float, stop counting and record exponent details + type = value_t::number_float; + break; + } + + // skip if definitely not an integer + if (type != value_t::number_float) + { + // multiply last value by ten and add the new digit + auto temp = value * 10 + *curptr - '0'; + + // test for overflow + if (temp < value || temp > max) + { + // overflow + type = value_t::number_float; + } + else + { + // no overflow - save it + value = temp; + } + } + } + + // save the value (if not a float) + if (type == value_t::number_unsigned) + { + result.m_value.number_unsigned = value; + } + else if (type == value_t::number_integer) + { + result.m_value.number_integer = -static_cast(value); + } + else + { + // parse with strtod + result.m_value.number_float = str_to_float_t(static_cast(nullptr), NULL); + } + + // save the type + result.m_type = type; + } + + private: + /// optional input stream + std::istream* m_stream = nullptr; + /// line buffer buffer for m_stream + string_t m_line_buffer{}; + /// the buffer pointer + const lexer_char_t* m_content = nullptr; + /// pointer to the beginning of the current symbol + const lexer_char_t* m_start = nullptr; + /// pointer for backtracking information + const lexer_char_t* m_marker = nullptr; + /// pointer to the current symbol + const lexer_char_t* m_cursor = nullptr; + /// pointer to the end of the buffer + const lexer_char_t* m_limit = nullptr; + /// the last token type + token_type last_token_type = token_type::end_of_input; + }; + + /*! + @brief syntax analysis + + This class implements a recursive decent parser. + */ + class parser + { + public: + /// a parser reading from a string literal + parser(const char* buff, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast(buff), strlen(buff)) + { + } + + /// a parser reading from an input stream + parser(std::istream& is, const parser_callback_t cb = nullptr) + : callback(cb), m_lexer(is) + { + } + + /// a parser reading from an iterator range with contiguous storage + template ::iterator_category, std:: + random_access_iterator_tag>::value + , int>::type = 0> + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast(&(*first)), + static_cast(std::distance(first, last))) + { + } + + /// public parser interface + basic_json parse() + { + // read first token + get_token(); + + basic_json result = parse_internal(true); + result.assert_invariant(); + + expect(lexer::token_type::end_of_input); + + // return parser result and replace it with null in case the + // top-level value was discarded by the callback function + return result.is_discarded() ? basic_json() : std::move(result); + } + + private: + /// the actual parser + basic_json parse_internal(bool keep) + { + auto result = basic_json(value_t::discarded); + + switch (last_token) + { + case lexer::token_type::begin_object: + { + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) + { + // explicitly set result to object to cope with {} + result.m_type = value_t::object; + result.m_value = value_t::object; + } + + // read next token + get_token(); + + // closing } -> we are done + if (last_token == lexer::token_type::end_object) + { + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result = basic_json(value_t::discarded); + } + return result; + } + + // no comma is expected here + unexpect(lexer::token_type::value_separator); + + // otherwise: parse key-value pairs + do + { + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) + { + get_token(); + } + + // store key + expect(lexer::token_type::value_string); + const auto key = m_lexer.get_string(); + + bool keep_tag = false; + if (keep) + { + if (callback) + { + basic_json k(key); + keep_tag = callback(depth, parse_event_t::key, k); + } + else + { + keep_tag = true; + } + } + + // parse separator (:) + get_token(); + expect(lexer::token_type::name_separator); + + // parse and add value + get_token(); + auto value = parse_internal(keep); + if (keep and keep_tag and not value.is_discarded()) + { + result[key] = std::move(value); + } + } + while (last_token == lexer::token_type::value_separator); + + // closing } + expect(lexer::token_type::end_object); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result = basic_json(value_t::discarded); + } + + return result; + } + + case lexer::token_type::begin_array: + { + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) + { + // explicitly set result to object to cope with [] + result.m_type = value_t::array; + result.m_value = value_t::array; + } + + // read next token + get_token(); + + // closing ] -> we are done + if (last_token == lexer::token_type::end_array) + { + get_token(); + if (callback and not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); + } + return result; + } + + // no comma is expected here + unexpect(lexer::token_type::value_separator); + + // otherwise: parse values + do + { + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) + { + get_token(); + } + + // parse value + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) + { + result.push_back(std::move(value)); + } + } + while (last_token == lexer::token_type::value_separator); + + // closing ] + expect(lexer::token_type::end_array); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); + } + + return result; + } + + case lexer::token_type::literal_null: + { + get_token(); + result.m_type = value_t::null; + break; + } + + case lexer::token_type::value_string: + { + const auto s = m_lexer.get_string(); + get_token(); + result = basic_json(s); + break; + } + + case lexer::token_type::literal_true: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = true; + break; + } + + case lexer::token_type::literal_false: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = false; + break; + } + + case lexer::token_type::value_number: + { + m_lexer.get_number(result); + get_token(); + break; + } + + default: + { + // the last token was unexpected + unexpect(last_token); + } + } + + if (keep and callback and not callback(depth, parse_event_t::value, result)) + { + result = basic_json(value_t::discarded); + } + return result; + } + + /// get next token from lexer + typename lexer::token_type get_token() + { + last_token = m_lexer.scan(); + return last_token; + } + + void expect(typename lexer::token_type t) const + { + if (t != last_token) + { + std::string error_msg = "parse error - unexpected "; + error_msg += (last_token == lexer::token_type::parse_error + ? ("'" + m_lexer.get_token_string() + + "'") + : lexer::token_type_name(last_token)); + error_msg += "; expected " + lexer::token_type_name(t); + throw std::invalid_argument(error_msg); + } + } + + void unexpect(typename lexer::token_type t) const + { + if (t == last_token) + { + std::string error_msg = "parse error - unexpected "; + error_msg += (last_token == lexer::token_type::parse_error + ? ("'" + m_lexer.get_token_string() + + "'") + : lexer::token_type_name(last_token)); + throw std::invalid_argument(error_msg); + } + } + + private: + /// current level of recursion + int depth = 0; + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + typename lexer::token_type last_token = lexer::token_type::uninitialized; + /// the lexer + lexer m_lexer; + }; + + public: + /*! + @brief JSON Pointer + + A JSON pointer defines a string syntax for identifying a specific value + within a JSON document. It can be used with functions `at` and + `operator[]`. Furthermore, JSON pointers are the base for JSON patches. + + @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + class json_pointer + { + /// allow basic_json to access private members + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the + empty string is assumed which references the whole JSON + value + + @throw std::domain_error if reference token is nonempty and does not + begin with a slash (`/`); example: `"JSON pointer must be empty or + begin with /"` + @throw std::domain_error if a tilde (`~`) is not followed by `0` + (representing `~`) or `1` (representing `/`); example: `"escape error: + ~ must be followed with 0 or 1"` + + @liveexample{The example shows the construction several valid JSON + pointers as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + { + } + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const noexcept + { + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string& a, const std::string& b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + private: + /// remove and return last reference pointer + std::string pop_back() + { + if (is_root()) + { + throw std::domain_error("JSON pointer has no parent"); + } + + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } + + /// return whether pointer points to the root document + bool is_root() const + { + return reference_tokens.empty(); + } + + json_pointer top() const + { + if (is_root()) + { + throw std::domain_error("JSON pointer has no parent"); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + */ + reference get_and_create(reference j) const + { + pointer result = &j; + + // in case no reference tokens exist, return a reference to the + // JSON value j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->m_type) + { + case value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case value_t::array: + { + // create an entry in the array + result = &result->operator[](static_cast(std::stoi(reference_token))); + break; + } + + /* + The following code is only reached if there exists a + reference token _and_ the current value is primitive. In + this case, we have an error situation, because primitive + values may only occur as single value; that is, with an + empty list of reference tokens. + */ + default: + { + throw std::domain_error("invalid value to unflatten"); + } + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + */ + reference get_unchecked(pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + if (reference_token == "-") + { + // explicityly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + } + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + reference get_checked(pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + // note: at performs range check + ptr = &ptr->at(static_cast(std::stoi(reference_token))); + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + */ + const_reference get_unchecked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" cannot be used for const access + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + // use unchecked array access + ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + const_reference get_checked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + // note: at performs range check + ptr = &ptr->at(static_cast(std::stoi(reference_token))); + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + /// split the string input to reference tokens + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (reference_string[0] != '/') + { + throw std::domain_error("JSON pointer must be empty or begin with '/'"); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + size_t slash = reference_string.find_first_of("/", 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == string::npos+1 = 0 + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = slash + 1, + // find next slash + slash = reference_string.find_first_of("/", start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (size_t pos = reference_token.find_first_of("~"); + pos != std::string::npos; + pos = reference_token.find_first_of("~", pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1')) + { + throw std::domain_error("escape error: '~' must be followed with '0' or '1'"); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @return The string @a s where all occurrences of @a f are replaced + with @a t. + + @pre The search string @a f must not be empty. + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, + const std::string& f, + const std::string& t) + { + assert(not f.empty()); + + for ( + size_t pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t + pos = s.find(f, pos + t.size()) // find next occurrence of f + ); + } + + /// escape tilde and slash + static std::string escape(std::string s) + { + // escape "~"" to "~0" and "/" to "~1" + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape tilde and slash + static void unescape(std::string& s) + { + // first transform any occurrence of the sequence '~1' to '/' + replace_substring(s, "~1", "/"); + // then transform any occurrence of the sequence '~0' to '~' + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const basic_json& value, + basic_json& result) + { + switch (value.m_type) + { + case value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), + element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + */ + static basic_json unflatten(const basic_json& value) + { + if (not value.is_object()) + { + throw std::domain_error("only objects can be unflattened"); + } + + basic_json result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (not element.second.is_primitive()) + { + throw std::domain_error("values in object must be primitive"); + } + + // assign value to reference pointed to by JSON pointer; Note + // that if the JSON pointer is "" (i.e., points to the whole + // value), function get_and_create returns a reference to + // result itself. An assignment will then create a primitive + // value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + private: + /// the reference tokens + std::vector reference_tokens{}; + }; + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to @ref operator[](const typename + object_t::key_type&), `null` values are created in arrays and objects if + necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,operatorjson_pointer} + + @since version 2.0.0 + */ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,at_json_pointer} + + @since version 2.0.0 + */ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer @a + ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,at_json_pointer_const} + + @since version 2.0.0 + */ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see [RFC + 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the @ref + unflatten() function. + + @return an object that maps JSON pointers to primitve values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this funcion, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] json_patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @throw std::out_of_range if a JSON pointer inside the patch could not + be resolved successfully in the current JSON value; example: `"key baz + not found"` + @throw invalid_argument if the JSON patch is malformed (e.g., mandatory + attributes are missing); example: `"operation add must have member path"` + + @complexity Linear in the size of the JSON value and the length of the + JSON patch. As usually only a fraction of the JSON value is affected by + the patch, the complexity can usually be neglected. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations { add, remove, replace, move, copy, test, invalid }; + + const auto get_op = [](const std::string op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer& ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.is_root()) + { + result = val; + } + else + { + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = std::stoi(last_path); + if (static_cast(idx) > parent.size()) + { + // avoid undefined behavior + throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + } + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + default: + { + // if there exists a parent it cannot be primitive + assert(false); // LCOV_EXCL_LINE + } + } + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [&result](json_pointer& ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (it != parent.end()) + { + parent.erase(it); + } + else + { + throw std::out_of_range("key '" + last_path + "' not found"); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(static_cast(std::stoi(last_path))); + } + }; + + // type check + if (not json_patch.is_array()) + { + // a JSON patch must be an array of objects + throw std::invalid_argument("JSON patch must be an array of objects"); + } + + // iterate and apply th eoperations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string& op, + const std::string& member, + bool string_type) -> basic_json& + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (it == val.m_value.object->end()) + { + throw std::invalid_argument(error_msg + " must have member '" + member + "'"); + } + + // check if result is of type string + if (string_type and not it->second.is_string()) + { + throw std::invalid_argument(error_msg + " must have string member '" + member + "'"); + } + + // no error: return value + return it->second; + }; + + // type check + if (not val.is_object()) + { + throw std::invalid_argument("JSON patch must be an array of objects"); + } + + // collect mandatory members + const std::string op = get_value("op", "op", true); + const std::string path = get_value(op, "path", true); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const std::string from_path = get_value("move", "from", true); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const std::string from_path = get_value("copy", "from", true); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + result[ptr] = result.at(from_ptr); + break; + } + + case patch_operations::test: + { + bool success = false; + try + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + catch (std::out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (not success) + { + throw std::domain_error("unsuccessful: " + val.dump()); + } + + break; + } + + case patch_operations::invalid: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + throw std::invalid_argument("operation value '" + op + "' is invalid"); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to copare from + @param[in] target JSON value to copare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa @ref patch -- apply a JSON patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + static basic_json diff(const basic_json& source, + const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + } + else + { + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + size_t i = 0; + while (i < source.size() and i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + std::to_string(i)}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.begin(); it != source.end(); ++it) + { + // escape the key name to be used in a JSON patch + const auto key = json_pointer::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, + {"path", path + "/" + key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.begin(); it != target.end(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto key = json_pointer::escape(it.key()); + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + key}, + {"value", it.value()} + }); + } + } + + break; + } + + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + break; + } + } + } + + return result; + } + + /// @} + }; + + + ///////////// + // presets // + ///////////// + + /*! + @brief default JSON class + + This type is the default specialization of the @ref basic_json class which + uses the standard template types. + + @since version 1.0.0 + */ + using json = basic_json<>; +} + + +/////////////////////// +// nonmember support // +/////////////////////// + +// specialization of std::swap, and std::hash +namespace std +{ + /*! + @brief exchanges the values of two JSON objects + + @since version 1.0.0 + */ + template <> + inline void swap(nlohmann::json& j1, + nlohmann::json& j2) noexcept( + is_nothrow_move_constructible::value and + is_nothrow_move_assignable::value + ) + { + j1.swap(j2); + } + + /// hash value for JSON objects + template <> + struct hash + { + /*! + @brief return a hash value for a JSON object + + @since version 1.0.0 + */ + std::size_t operator()(const nlohmann::json& j) const + { + // a naive hashing via the string representation + const auto& h = hash(); + return h(j.dump()); + } + }; +} + +/*! +@brief user-defined string literal for JSON values + +This operator implements a user-defined string literal for JSON objects. It +can be used by adding `"_json"` to a string literal and returns a JSON object +if no parse error occurred. + +@param[in] s a string representation of a JSON object +@return a JSON object + +@since version 1.0.0 +*/ +inline nlohmann::json operator "" _json(const char* s, std::size_t) +{ + return nlohmann::json::parse(s); +} + +/*! +@brief user-defined string literal for JSON pointer + +This operator implements a user-defined string literal for JSON Pointers. It +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer +object if no parse error occurred. + +@param[in] s a string representation of a JSON Pointer +@return a JSON pointer object + +@since version 2.0.0 +*/ +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t) +{ + return nlohmann::json::json_pointer(s); +} + +// restore GCC/clang diagnostic settings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif + +#endif + +// Dumping... +#define JSON_FIELD_ADV(__data__,__field__) \ + __data__[#__field__] = this->__field__ + +#define JSON_FIELD(__field__) \ + JSON_FIELD_ADV(data, __field__) + +#define JSON_FIELD_ARR_ADV(__data__,__field__,__count__) \ + for (int i##__data__ = 0; i##__data__ < __count__; i##__data__++) \ + { \ + __data__[#__field__][i##__data__] = this->__field__[i##__data__]; \ + } + +#define JSON_FIELD_ARR(__field__,__count__) \ + JSON_FIELD_ARR_ADV(data,__field__,__count__) + +#define JSON_STRUCT_ADV(__data__,__field__) \ + __data__[#__field__] = (this->__field__ != nullptr) ? this->__field__->ToJson() : nullptr + +#define JSON_STRUCT(__field__) \ + JSON_STRUCT_ADV(data, __field__) + +#define JSON_STRUCT_REF_ADV(__data__,__field__) \ + __data__[#__field__] = this->__field__.ToJson() + +#define JSON_STRUCT_REF(__field__) \ + JSON_STRUCT_REF_ADV(data,__field__) + +#define JSON_STRUCT_PTR_ARR_ADV(__data__,__field__,__count__) \ + for (int i##__data__ = 0; i##__data__ < __count__; i##__data__++) \ + { \ + __data__[#__field__][i##__data__] = (this->__field__ && this->__field__[i##__data__]) ? \ + this->__field__[i##__data__]->ToJson() : nullptr; \ + } + +#define JSON_STRUCT_PTR_ARR(__field__,__count__) \ + JSON_STRUCT_PTR_ARR_ADV(data, __field__, __count__) + +#define JSON_STRUCT_ARR_ADV(__data__,__field__,__count__) \ + for (int i##__data__ = 0; i##__data__ < __count__; i##__data__++) \ + { \ + __data__[#__field__][i##__data__] = this->__field__[i##__data__].ToJson(); \ + } + +#define JSON_STRUCT_ARR(__field__,__count__) \ + JSON_STRUCT_ARR_ADV(data,__field__,__count__) + +#define JSON_ASSET_ADV(__data__,__field__,__type__) \ + __data__[#__field__] = (this->__field__ != nullptr) ? reinterpret_cast<__type__*>(this->__field__)->name : "" + +#define JSON_ASSET(__field__,__type__) \ + JSON_ASSET_ADV(data,__field__,__type__) + +#define JSON_STRING_ADV(__data__,__field__) \ + __data__[#__field__] = (this->__field__ != nullptr) ? this->__field__ : "" + +#define JSON_STRING(__field__) \ + JSON_STRING_ADV(data, __field__) + +// Parsing... +#define JSON_PARSE_FIELD_INTERNAL_ADV(__data__,__field__,__type__) \ + __field__ = __data__[#__field__].get<__type__>() + +#define JSON_PARSE_STRING_ADV(__data__,__field__) \ + __field__ = mem->StrDup(__data__[#__field__].get()) + +#define JSON_PARSE_STRING(__field__) \ + // JSON_PARSE_STRING_ADV(data,__field__) + +#define JSON_PARSE_FLOAT_ADV(__data__,__field__) \ + JSON_PARSE_FIELD_INTERNAL_ADV(__data__,__field__,float) + +#define JSON_PARSE_FLOAT(__field__) \ + JSON_PARSE_FLOAT_ADV(data,__field__) + +#define JSON_PARSE_INT32_ADV(__data__,__field__) \ + JSON_PARSE_FIELD_INTERNAL_ADV(__data__,__field__,std::int32_t) + +#define JSON_PARSE_INT32(__field__) \ + JSON_PARSE_INT32_ADV(data,__field__) + +#define JSON_PARSE_FIELD(__field__,__type__) \ + JSON_PARSE_FIELD_INTERNAL_ADV(data,__field__,__type__) + +#define JSON_PARSE_FIELD_ARR_ADV(__data__,__field__,__count__,__type__) \ + for (int i##__data__ = 0; i##__data__ < __count__; i##__data__++) \ + { \ + __field__[i##__data__] = __data__[#__field__][i##__data__]; \ + } + +#define JSON_PARSE_FIELD_ARR_ALLOC_ADV(__data__,__field__,__count__,__type__) \ + __field__ = mem->Alloc<__type__>(__count__); \ + JSON_PARSE_FIELD_ARR_ADV(__field__,__count__,__type__) + +#define JSON_PARSE_FIELD_ARR(__field__,__count__,__type__) \ + JSON_PARSE_FIELD_ARR_ADV(data,__field__,__count__,__type__) + +#define JSON_PARSE_FIELD_ARR_ALLOC(__field__,__count__,__type__) \ + JSON_PARSE_FIELD_ARR_ALLOC_ADV(data,__field__,__count__,__type__) + +#define JSON_PARSE_STRUCT_ADV(__data__,__field__,__type__) \ + if (__data__[#__field__].is_object()) \ + { \ + __field__ = mem->Alloc<__type__>(); \ + __field__->Parse(data, mem); \ + } + +#define JSON_PARSE_STRUCT(__field__,__type__) \ + JSON_PARSE_STRUCT_ADV(data,__field__,__type__) + +#define JSON_PARSE_ASSET_ADV(__data__,__field__,__assettype__,__type__,__game__) \ + if (__data__[#__field__].is_string()) \ + __field__ = ZoneTool::__game__::DB_FindXAssetHeader(__assettype__,__data__[#__field__].get(),1).__type__; + +#define JSON_PARSE_ASSET(__field__,__assettype__,__type__,__game__) \ + // JSON_PARSE_ASSET_ADV(data,__field__,__assettype__,__type__,__game__) diff --git a/src/ZoneUtils/Linker.hpp b/src/ZoneUtils/Linker.hpp new file mode 100644 index 0000000..d9a21bc --- /dev/null +++ b/src/ZoneUtils/Linker.hpp @@ -0,0 +1,35 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + class ILinker + { + public: + virtual const char* version() = 0; + virtual bool is_used() = 0; + virtual void startup() = 0; + virtual std::shared_ptr alloc_buffer() = 0; + virtual std::shared_ptr alloc_zone(const std::string& zone) = 0; + virtual bool supports_building() = 0; + virtual bool supports_version(const zone_target_version version) = 0; + + virtual void dump_zone(const std::string& name) = 0; + virtual void verify_zone(const std::string& name) = 0; + virtual void load_zone(const std::string& name) = 0; + virtual void unload_zones() = 0; + + virtual bool is_valid_asset_type(const std::string& type) = 0; + virtual std::int32_t type_to_int(std::string type) = 0; + virtual std::string type_to_string(std::int32_t type) = 0; + + private: + }; +} diff --git a/src/ZoneUtils/Utils/BinaryDumper.cpp b/src/ZoneUtils/Utils/BinaryDumper.cpp new file mode 100644 index 0000000..60ae369 --- /dev/null +++ b/src/ZoneUtils/Utils/BinaryDumper.cpp @@ -0,0 +1,13 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ +} diff --git a/src/ZoneUtils/Utils/BinaryDumper.hpp b/src/ZoneUtils/Utils/BinaryDumper.hpp new file mode 100644 index 0000000..2afc1ac --- /dev/null +++ b/src/ZoneUtils/Utils/BinaryDumper.hpp @@ -0,0 +1,567 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + struct dumpListEntry + { + void* start; + int entryCount; + int entrySize; + }; + + enum DUMP_TYPE + { + DUMP_TYPE_INT = 0, + DUMP_TYPE_STRING = 1, + DUMP_TYPE_ASSET = 2, + DUMP_TYPE_ARRAY = 3, + DUMP_TYPE_OFFSET = 4, + DUMP_TYPE_FLOAT = 5, + DUMP_TYPE_RAW = 6, + }; + + const char DUMP_EXISTING = 1; + const char DUMP_NONEXISTING = 0; + + class AssetDumper + { + protected: + std::vector list; + FILE* fp = nullptr; + + template + void write_internal(const T& value) + { + if (fp) + { + fwrite(&value, sizeof T, 1, fp); + } + } + + template + void write_internal(const T& value, DUMP_TYPE type) + { + if (fp) + { + fwrite(&type, 1, 1, fp); + fwrite(&value, sizeof T, 1, fp); + } + } + + void write_string_internal(const char* str) + { + if (fp) + { + fwrite(str, strlen(str) + 1, 1, fp); + } + } + + public: + AssetDumper() + { + list.clear(); + } + + ~AssetDumper() + { + // close file pointer + close(); + } + + bool open(const std::string& filename) + { + close(); + fp = FileSystem::FileOpen(filename, "wb"); + return fp != nullptr; + } + + void close() + { + list.clear(); + FileSystem::FileClose(fp); + } + + void dump_float(float f) + { + write_internal(f, DUMP_TYPE_FLOAT); + } + + void dump_int(std::int32_t i) + { + write_internal(i, DUMP_TYPE_INT); + } + + void dump_uint(std::uint32_t i) + { + write_internal(i, DUMP_TYPE_INT); + } + + void dump_string(char* s) + { + if (fp) + { + if (s) + { + auto listIndex = 0; + for (auto oldEntry : list) + { + if (oldEntry.start == s && oldEntry.entrySize == 1 && oldEntry.entryCount == 1) + { + this->write_internal(listIndex, DUMP_TYPE_OFFSET); + this->write_internal(0); + return; + } + listIndex++; + } + + dumpListEntry entry; + entry.entryCount = 1; + entry.entrySize = 1; + entry.start = reinterpret_cast(s); + list.push_back(entry); + + this->write_internal(DUMP_EXISTING, DUMP_TYPE_STRING); + this->write_string_internal(s); + } + else + { + this->write_internal(DUMP_NONEXISTING, DUMP_TYPE_STRING); + } + } + } + + void dump_string(const char* s) + { + this->dump_string(const_cast(s)); + } + + template + void dump_asset(T* asset) + { + if (fp) + { + if (asset && asset->name) + { + auto listIndex = 0; + for (auto oldEntry : list) + { + if (reinterpret_cast(oldEntry.start) <= int(asset) && + reinterpret_cast(oldEntry.start) + (oldEntry.entryCount ? oldEntry.entryCount - 1 : 0) * oldEntry. + entrySize >= int(asset) && + (int(asset) - reinterpret_cast(oldEntry.start)) % oldEntry.entrySize == 0) + { + this->write_internal(listIndex, DUMP_TYPE_OFFSET); + this->write_internal((int(asset) - reinterpret_cast(oldEntry.start)) / oldEntry.entrySize); + return; + } + listIndex++; + } + + dumpListEntry entry; + entry.entryCount = 1; + entry.entrySize = sizeof(T); + entry.start = static_cast(asset); + list.push_back(entry); + + this->write_internal(DUMP_EXISTING, DUMP_TYPE_ASSET); + this->write_string_internal(asset->name); + } + else + { + this->write_internal(DUMP_NONEXISTING, DUMP_TYPE_ASSET); + } + } + } + + template void dump_array(T* data, int arraySize) + { + if (fp) + { + if (data && arraySize > 0) + { + int listIndex = 0; + for (auto oldEntry : list) + { + if (sizeof(T) == oldEntry.entrySize && + // Filter out the structs it definitely can't be without having to actually save what struct it is + reinterpret_cast(oldEntry.start) <= int(data) && // Check if it is in the range of the current array + reinterpret_cast(oldEntry.start) + (oldEntry.entryCount ? oldEntry.entryCount - 1 : 0) * oldEntry. + entrySize >= int(data) && // ^ + (int(data) - reinterpret_cast(oldEntry.start)) % oldEntry.entrySize == 0) + // Check if the data is actually at the start of an array entry + { + this->write_internal(listIndex, DUMP_TYPE_OFFSET); + this->write_internal((int(data) - reinterpret_cast(oldEntry.start)) / oldEntry.entrySize); + return; + } + listIndex++; + } + + dumpListEntry entry; + entry.entryCount = arraySize; + entry.entrySize = sizeof(T); + entry.start = static_cast(data); + list.push_back(entry); + + this->write_internal(arraySize, DUMP_TYPE_ARRAY); + fwrite(data, sizeof(T), arraySize, fp); + } + else + { + this->write_internal(0, DUMP_TYPE_ARRAY); + } + } + } + + template void dump_single(T* asset) + { + return this->dump_array(asset, 1); + } + + template void dump_raw(T* data, int size) + { + if (fp) + { + if (data && size > 0) + { + int listIndex = 0; + for (auto oldEntry : list) + { + if (sizeof(T) == oldEntry.entrySize && + // Filter out the structs it definitely can't be without having to actually save what struct it is + reinterpret_cast(oldEntry.start) <= int(data) && // Check if it is in the range of the current array + reinterpret_cast(oldEntry.start) + (oldEntry.entryCount ? oldEntry.entryCount - 1 : 0) * oldEntry. + entrySize >= int(data) && // ^ + (int(data) - reinterpret_cast(oldEntry.start)) % oldEntry.entrySize == 0) + // Check if the data is actually at the start of an array entry + { + this->write_internal(listIndex, DUMP_TYPE_OFFSET); + this->write_internal((int(data) - reinterpret_cast(oldEntry.start)) / oldEntry.entrySize); + return; + } + listIndex++; + } + + dumpListEntry entry; + entry.entryCount = 1; + entry.entrySize = size; + entry.start = static_cast(data); + list.push_back(entry); + + this->write_internal(size, DUMP_TYPE_RAW); + fwrite(data, size, 1, fp); + } + else + { + this->write_internal(0, DUMP_TYPE_RAW); + } + } + } + }; + + class AssetReader + { + protected: + std::vector list; + FILE* fp_ = nullptr; + ZoneMemory* memory_; + + template + T read_internal() + { + T value; + + if (fp_) + { + fread(&value, sizeof T, 1, fp_); + } + + return value; + } + + char* read_string_internal() + { + char tempBuf[1024]; + char ch = 0; + int i = 0; + + if (fp_) + { + do + { + fread(&ch, 1, 1, fp_); + + tempBuf[i++] = ch; + + if (i >= sizeof(tempBuf)) + { + throw std::exception("this is wrong"); + } + } + while (ch); + } + + char* retval = memory_->Alloc(i); // new char[i]; + strcpy(retval, tempBuf); + + return retval; + } + + public: + AssetReader(ZoneMemory* mem) + { + memory_ = mem; + + list.clear(); + } + + ~AssetReader() + { + // close file + close(); + } + + bool open(const std::string& filename, bool preferLocal = false) + { + close(); + + FileSystem::PreferLocalOverExternal(preferLocal); + fp_ = FileSystem::FileOpen(filename, "rb"); + FileSystem::PreferLocalOverExternal(false); + + return fp_ != nullptr; + } + + void close() + { + list.clear(); + FileSystem::FileClose(fp_); + } + + float read_float() + { + if (fp_) + { + char type = this->read_internal(); + if (type != DUMP_TYPE_FLOAT) + { + printf("Reader error: Type not DUMP_TYPE_FLOAT but %i", type); + throw; + return 0; + } + return this->read_internal(); + } + return 0; + } + + int read_int() + { + if (fp_) + { + const auto type = this->read_internal(); + if (type != DUMP_TYPE_INT) + { + printf("Reader error: Type not DUMP_TYPE_INT but %i", type); + throw; + return 0; + } + return this->read_internal(); + } + return 0; + } + + unsigned int read_uint() + { + if (fp_) + { + const auto type = this->read_internal(); + if (type != DUMP_TYPE_INT) + { + printf("Reader error: Type not DUMP_TYPE_INT but %i", type); + throw; + return 0; + } + return this->read_internal(); + } + return 0; + } + + char* read_string() + { + if (fp_) + { + const auto type = this->read_internal(); + + if (type == DUMP_TYPE_STRING) + { + const auto existing = this->read_internal(); + if (existing == DUMP_NONEXISTING) + { + return nullptr; + } + const auto output = this->read_string_internal(); // freadstr(fp_); + + dumpListEntry entry; + entry.entryCount = 1; + entry.entrySize = 1; + entry.start = static_cast(output); + list.push_back(entry); + + return output; + } + if (type == DUMP_TYPE_OFFSET) + { + const auto listIndex = this->read_internal(); // freadint(fp_); + const auto arrayIndex = this->read_internal(); + + return reinterpret_cast(reinterpret_cast(list[listIndex].start) + list[listIndex].entrySize * arrayIndex); + } + printf("Reader error: Type not DUMP_TYPE_STRING or DUMP_TYPE_OFFSET but %i", type); + throw; + return nullptr; + } + return nullptr; + } + + template + T* read_asset() + { + if (fp_) + { + const auto type = this->read_internal(); + + if (type == DUMP_TYPE_ASSET) + { + const auto existing = this->read_internal(); + if (existing == DUMP_NONEXISTING) + { + return nullptr; + } + const char* name = this->read_string_internal(); + // T* asset = new T; + + auto asset = memory_->Alloc(); + + memset(asset, 0, sizeof(T)); + asset->name = const_cast(name); + + dumpListEntry entry; + entry.entryCount = 1; + entry.entrySize = sizeof(T); + entry.start = static_cast(asset); + list.push_back(entry); + + return asset; + } + if (type == DUMP_TYPE_OFFSET) + { + const auto listIndex = this->read_internal(); + const auto arrayIndex = this->read_internal(); + + return reinterpret_cast(reinterpret_cast(list[listIndex].start) + list[listIndex].entrySize * arrayIndex); + } + + printf("Reader error: Type not DUMP_TYPE_ASSET or DUMP_TYPE_OFFSET but %i", type); + throw; + } + + return nullptr; + } + + template + T* read_array() + { + if (fp_) + { + const auto type = this->read_internal(); + + if (type == DUMP_TYPE_ARRAY) + { + const auto arraySize = this->read_internal(); + + if (arraySize <= 0) + { + return nullptr; + } + + auto nArray = memory_->Alloc(arraySize); + fread(static_cast(nArray), sizeof(T), arraySize, fp_); + + dumpListEntry entry; + entry.entryCount = arraySize; + entry.entrySize = sizeof(T); + entry.start = static_cast(nArray); + list.push_back(entry); + + return nArray; + } + if (type == DUMP_TYPE_OFFSET) + { + const auto listIndex = this->read_internal(); + const auto arrayIndex = this->read_internal(); + + return reinterpret_cast(reinterpret_cast(list[listIndex].start) + list[listIndex].entrySize * arrayIndex); + } + + printf("Reader error: Type not DUMP_TYPE_ARRAY or DUMP_TYPE_OFFSET but %i\n", type); + throw; + } + + return nullptr; + } + + template T* read_single() + { + return this->read_array(); + } + + template T* read_raw() + { + if (fp_) + { + const auto type = this->read_internal(); + + if (type == DUMP_TYPE_RAW) + { + const auto size = this->read_internal(); + + if (size <= 0) + { + return nullptr; + } + + auto nArray = memory_->ManualAlloc(size); + fread(static_cast(nArray), size, 1, fp_); + + dumpListEntry entry; + entry.entryCount = 1; + entry.entrySize = size; + entry.start = static_cast(nArray); + list.push_back(entry); + + return nArray; + } + if (type == DUMP_TYPE_OFFSET) + { + const auto listIndex = this->read_internal(); + const auto arrayIndex = this->read_internal(); + + return reinterpret_cast(reinterpret_cast(list[listIndex].start) + list[listIndex].entrySize * arrayIndex); + } + + printf("Reader error: Type not DUMP_TYPE_ARRAY or DUMP_TYPE_OFFSET but %i\n", type); + throw; + } + + return nullptr; + } + }; +} diff --git a/src/ZoneUtils/Utils/Expressions.hpp b/src/ZoneUtils/Utils/Expressions.hpp new file mode 100644 index 0000000..1d36a74 --- /dev/null +++ b/src/ZoneUtils/Utils/Expressions.hpp @@ -0,0 +1,172 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#define MAX_TOKEN_CHARS 1024 + +namespace ZoneTool +{ + class ExpressionParser + { + public: + ExpressionParser(const char* buffer) + { + dataPtr = buffer; + memset(com_token, 0, sizeof com_token); + memset(com_parseName, 0, sizeof com_parseName); + com_lines = 0; + } + + ExpressionParser(const ExpressionParser& other) + { + this->dataPtr = other.dataPtr; + memcpy(this->com_token, other.com_token, sizeof this->com_token); + memcpy(this->com_parseName, other.com_parseName, sizeof this->com_parseName); + this->com_lines = other.com_lines; + } + + std::string Parse(bool allowLineBreaks = false) + { + int c = 0, len; + bool hasNewLines = false; + char* data; + + data = (char*)dataPtr; + len = 0; + com_token[0] = 0; + + // make sure incoming data is valid + if (!data) + { + dataPtr = nullptr; + return com_token; + } + + while (true) + { + // skip whitespace + data = SkipWhitespace(data, &hasNewLines); + if (!data) + { + dataPtr = nullptr; + return com_token; + } + if (hasNewLines && !allowLineBreaks) + { + dataPtr = nullptr; + return com_token; + } + + c = *data; + + // skip double slash comments + if (c == '/' && data[1] == '/') + { + data += 2; + while (*data && *data != '\n') + { + data++; + } + } + // skip /* */ comments + else if (c == '/' && data[1] == '*') + { + data += 2; + while (*data && (*data != '*' || data[1] != '/')) + { + data++; + } + if (*data) + { + data += 2; + } + } + else + { + break; + } + } + + // handle quoted strings + if (c == '\"') + { + data++; + while (true) + { + c = *data++; + if (c == '\"' || !c) + { + com_token[len] = 0; + dataPtr = (char *)data; + return com_token; + } + if (len < MAX_TOKEN_CHARS) + { + com_token[len] = c; + len++; + } + } + } + + // parse a regular word + do + { + if (len < MAX_TOKEN_CHARS) + { + com_token[len] = c; + len++; + } + data++; + c = *data; + if (c == '\n') + com_lines++; + } + while (c > 32); + + if (len == MAX_TOKEN_CHARS) + { + // Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); + len = 0; + } + com_token[len] = 0; + + dataPtr = (char *)data; + return com_token; + } + + private: + const char* dataPtr; + char com_token[MAX_TOKEN_CHARS]; + char com_parseName[MAX_TOKEN_CHARS]; + int com_lines; + + char* SkipWhitespace(char* data, bool* hasNewLines) + { + int c; + + while ((c = *data) <= ' ') + { + if (!c) + { + return nullptr; + } + + if (c == '\n') + { + this->com_lines++; + *hasNewLines = true; + } + + data++; + } + + return data; + } + }; +} diff --git a/src/ZoneUtils/Utils/FileReader.cpp b/src/ZoneUtils/Utils/FileReader.cpp new file mode 100644 index 0000000..f248fbd --- /dev/null +++ b/src/ZoneUtils/Utils/FileReader.cpp @@ -0,0 +1,26 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + FileReader::FileReader(std::uint8_t* buffer, std::size_t size) + { + this->m_buf.resize(size); + this->m_pos = 0; + + memcpy(&this->m_buf[0], buffer, size); + } + + FileReader::FileReader(std::vector& buffer) + { + this->m_buf = buffer; + this->m_pos = 0; + } +} diff --git a/src/ZoneUtils/Utils/FileReader.hpp b/src/ZoneUtils/Utils/FileReader.hpp new file mode 100644 index 0000000..4b07ef7 --- /dev/null +++ b/src/ZoneUtils/Utils/FileReader.hpp @@ -0,0 +1,72 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + class FileReader + { + private: + std::vector m_buf; + std::size_t m_pos; + + public: + FileReader(std::vector& buffer); + FileReader(std::uint8_t* buffer, std::size_t size); + + template + T Read() + { + auto retval = *reinterpret_cast(&this->m_buf[this->m_pos]); + this->m_pos += sizeof T; + + return retval; + } + + template + T* ReadArray(std::size_t count = 1) + { + auto retval = reinterpret_cast(&this->m_buf[this->m_pos]); + this->m_pos += sizeof T * count; + + return retval; + } + + template + void ReadManual(T* ptr, std::size_t count = 1) + { + if (count) + { + memcpy(ptr, + &this->m_buf[this->m_pos], + count * sizeof T); + } + } + + template + void ReadManual(T* ptr, std::size_t count, std::size_t size) + { + if (count && size) + { + memcpy(ptr, + &this->m_buf[this->m_pos], + count * size); + } + } + + std::string ReadString() + { + auto retval = static_cast( + reinterpret_cast(&this->m_buf[this->m_pos])); + this->m_pos += retval.size() + 1; + + return retval; + } + }; +} diff --git a/src/ZoneUtils/Utils/FileSystem.cpp b/src/ZoneUtils/Utils/FileSystem.cpp new file mode 100644 index 0000000..5ed88cf --- /dev/null +++ b/src/ZoneUtils/Utils/FileSystem.cpp @@ -0,0 +1,309 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + std::string FileSystem::m_fastfile = ""; + bool FileSystem::preferLocal = false; + bool FileSystem::forceExternalAssets = false; + + void FileSystem::ForceExternalAssets(bool force) + { + forceExternalAssets = force; + } + + std::string FileSystem::GetDestFolderForAsset(const std::string& name) + { + std::string path = ""; + + if (forceExternalAssets || !preferLocal) + { + std::string path = "zonetool\\" + m_fastfile + "\\" + name; + if (std::filesystem::exists(path)) + { + return "zonetool\\" + m_fastfile + "\\"; + } + + path = "zonetool\\" + name; + if (std::filesystem::exists(path)) + { + return "zonetool\\"; + } + } + else + { + path = "zonetool\\" + name; + if (std::filesystem::exists(path)) + { + return "zonetool\\"; + } + + std::string path = "zonetool\\" + m_fastfile + "\\" + name; + if (std::filesystem::exists(path)) + { + return "zonetool\\" + m_fastfile + "\\"; + } + } + + + path = "zonetool\\images\\" + name; + if (std::filesystem::exists(path)) + { + return "zonetool\\images\\"; + } + + /*path = "dump\\" + m_fastfile + "\\" + name; + if (std::filesystem::exists(path)) + { + return "dump\\" + m_fastfile + "\\"; + }*/ + + return ""; + } + + std::string FileSystem::GetFilePath(const std::string& name) + { + auto destfolder = GetDestFolderForAsset(name); + if (!destfolder.empty()) + { + return destfolder + name; + } + + return ""; + } + + bool FileSystem::FileExists(const std::string& name) + { + return GetFilePath(name).size() > 1; + } + + void FileSystem::SetFastFile(const std::string& ff) + { + m_fastfile = ff; + } + + std::string FileSystem::GetFastFile() + { + return m_fastfile; + } + + void FileSystem::CreateDirectory(const std::string& name) + { + std::filesystem::create_directories(name); + } + + std::unordered_map filePaths; + std::unordered_map relativeFilePaths; + + FILE* FileSystem::FileOpen(const std::string& name, const std::string& mode) + { + if (mode[0] == 'r') + { + auto path = GetFilePath(name); + if (!path.empty()) + { + auto fp = fopen(path.data(), mode.data()); + + filePaths[fp] = path; + relativeFilePaths[fp] = GetDestFolderForAsset(name); + + return fp; + } + } + + if (mode[0] == 'w' || mode[0] == 'a') + { + auto path = "dump\\" + m_fastfile + "\\" + name; + + std::size_t pos1 = std::string::npos, pos2 = std::string::npos, pos = std::string::npos; + pos1 = path.find_last_of("/"); + pos2 = path.find_last_of("\\"); + + if (pos1 != std::string::npos && pos2 != std::string::npos) + { + if (pos1 > pos2) pos = pos1; + else pos = pos2; + } + else if (pos1 != std::string::npos && pos2 == std::string::npos) + { + pos = pos1; + } + else if (pos2 != std::string::npos && pos1 == std::string::npos) + { + pos = pos2; + } + else + { + pos = std::string::npos; + } + + if (pos != std::string::npos) + { + std::string dir = path.substr(0, pos); + CreateDirectory(dir); + } + + auto fp = fopen(path.data(), mode.data()); + filePaths[fp] = path; + relativeFilePaths[fp] = "dump\\" + m_fastfile + "\\"; + + return fp; + } + + return nullptr; + } + + std::size_t FileSystem::FileSize(FILE* fp) + { + if (fp) + { + auto i = ftell(fp); + fseek(fp, 0, SEEK_END); + + auto ret = ftell(fp); + fseek(fp, i, SEEK_SET); + + return ret; + } + + return 0; + } + + std::string FileSystem::GetRelativeFolderForFile(FILE* fp) + { + auto itr = relativeFilePaths.find(fp); + if (itr != relativeFilePaths.end()) + { + return itr->second; + } + + return ""; + } + + std::string FileSystem::GetFullPathForFile(FILE* fp) + { + auto itr = filePaths.find(fp); + if (itr != filePaths.end()) + { + return itr->second; + } + + return ""; + } + + bool FileSystem::IsExternalFile(FILE* fp) + { + auto itr = relativeFilePaths.find(fp); + if (itr != relativeFilePaths.end()) + { + return itr->second != "zonetool\\"; + } + + return false; + } + + void FileSystem::FileClose(FILE* fp) + { + if (fp) + { + auto itr = filePaths.find(fp); + if (itr != filePaths.end()) + { + itr->second.clear(); + } + + itr = relativeFilePaths.find(fp); + if (itr != relativeFilePaths.end()) + { + itr->second.clear(); + } + + fclose(fp); + } + } + + std::vector FileSystem::ReadBytes(FILE* fp, std::size_t size) + { + if (size) + { + // alloc vector + std::vector buffer; + buffer.resize(size); + + // read data + fread(&buffer[0], size, 1, fp); + + // return data + return buffer; + } + + return {}; + } + + std::shared_ptr FileSystem::ToReader(FILE* fp) + { + auto size = FileSize(fp); + auto buffer = ReadBytes(fp, size); + + auto reader = std::make_shared(buffer); + return reader; + } + + void FileSystem::PreferLocalOverExternal(bool state) + { + preferLocal = state; + } + + char* FileSystem::ReadString(FILE* fp, ZoneMemory* mem) + { + char tempBuf[1024]; + char ch = 0; + int i = 0; + + do + { + fread(&ch, 1, 1, fp); + + tempBuf[i++] = ch; + + if (i >= sizeof(tempBuf)) + { + throw std::exception("this is wrong"); + } + } while (ch); + + if (!mem) + { + char* retval = new char[i]; + strcpy(retval, tempBuf); + + return retval; + } + + auto retval = mem->Alloc(i); + strcpy(retval, tempBuf); + + return retval; + } + + int FileSystem::ReadInt(FILE* fp) + { + int data = 0; + + if (fp) + { + fread(&data, sizeof(int), 1, fp); + } + + return data; + } + + +} diff --git a/src/ZoneUtils/Utils/FileSystem.hpp b/src/ZoneUtils/Utils/FileSystem.hpp new file mode 100644 index 0000000..0b3b5b9 --- /dev/null +++ b/src/ZoneUtils/Utils/FileSystem.hpp @@ -0,0 +1,51 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#undef CreateDirectory + +namespace ZoneTool +{ + class FileSystem + { + private: + static std::string m_fastfile; + static bool preferLocal; + static bool forceExternalAssets; + + public: + FileSystem() + { + } + + ~FileSystem() + { + } + + static void ForceExternalAssets(bool force); + static bool IsExternalFile(FILE* fp); + static std::string GetDestFolderForAsset(const std::string& name); + static std::string GetRelativeFolderForFile(FILE* fp); + static std::string GetFullPathForFile(FILE* fp); + static void CreateDirectory(const std::string& name); + static std::string GetFilePath(const std::string& name); + static void PreferLocalOverExternal(bool state); + static bool FileExists(const std::string& name); + static void SetFastFile(const std::string& ff); + static std::string GetFastFile(); + static FILE* FileOpen(const std::string& name, const std::string& mode); + static std::size_t FileSize(FILE* fp); + static void FileClose(FILE* fp); + static std::vector ReadBytes(FILE* fp, std::size_t size); + static std::shared_ptr ToReader(FILE* fp); + static char* ReadString(FILE* fp, ZoneMemory* mem = nullptr); + static int ReadInt(FILE* fp); + + }; +} diff --git a/src/ZoneUtils/Utils/Function.hpp b/src/ZoneUtils/Utils/Function.hpp new file mode 100644 index 0000000..bcc2ef3 --- /dev/null +++ b/src/ZoneUtils/Utils/Function.hpp @@ -0,0 +1,54 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + template + class Function + { + using R = typename std::function::result_type; + + protected: + std::function m_func; + + public: + Function() + { + } + + Function(std::uintptr_t addr) + { + this->m_func = std::function(reinterpret_cast(addr)); + } + Function(FARPROC addr) : Function(std::uintptr_t(addr)) {} + + // Operators + void operator=(const std::uintptr_t addr) + { + this->m_func = std::function(reinterpret_cast(addr)); + } + + void operator=(const std::function func) + { + this->m_func = func; + } + + void operator=(const Function func) + { + this->m_func = func->m_func; + } + + template + R operator()(Args&&... args) + { + return this->m_func(std::forward(args)...); + } + }; +} diff --git a/src/ZoneUtils/Utils/Memory.cpp b/src/ZoneUtils/Utils/Memory.cpp new file mode 100644 index 0000000..1f161ef --- /dev/null +++ b/src/ZoneUtils/Utils/Memory.cpp @@ -0,0 +1,83 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + // Instruction opcodes + std::uint8_t Memory::Instructions::nop_ = 0x90; + std::uint8_t Memory::Instructions::call_ = 0xE8; + std::uint8_t Memory::Instructions::jump_ = 0xE9; + + void Memory::restore() + { + this->unprotect(this->original_data_.size()); + + for (std::size_t i = 0; i < original_data_.size(); i++) + { + *reinterpret_cast(this->address_ + i) = original_data_[i]; + } + + this->protect(this->original_data_.size()); + } + + void Memory::nop(std::size_t size) + { + this->set_original_data(size); + this->unprotect(size); + + for (std::size_t i = 0; i < size; i++) + { + *reinterpret_cast(this->address_ + i) = Instructions::nop_; + } + + this->protect(size); + } + + void Memory::write_string(const std::string& str) + { + this->set_original_data(str.size() + 1); + + this->unprotect(str.size() + 1); + strncpy(reinterpret_cast(this->address_), str.data(), str.size() + 1); + this->protect(str.size() + 1); + } + + void Memory::set_original_data(std::size_t size) + { + this->original_data_.clear(); + + for (std::size_t i = 0; i < size; i++) + { + original_data_.push_back(*reinterpret_cast(this->address_ + i)); + } + } + + void Memory::unprotect(std::size_t size) + { + VirtualProtect(reinterpret_cast(this->address_), size, PAGE_EXECUTE_READWRITE, + reinterpret_cast(&this->protect_)); + } + + void Memory::protect(std::size_t size) + { + VirtualProtect(reinterpret_cast(this->address_), size, this->protect_, + reinterpret_cast(&this->protect_)); + } + + Memory* Memory::initialize() + { + return new Memory(this); + } + + Memory* Memory::install() + { + return initialize(); + } +} diff --git a/src/ZoneUtils/Utils/Memory.hpp b/src/ZoneUtils/Utils/Memory.hpp new file mode 100644 index 0000000..f63aad2 --- /dev/null +++ b/src/ZoneUtils/Utils/Memory.hpp @@ -0,0 +1,161 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + class Memory + { + public: + struct Instructions + { + static std::uint8_t nop_; + static std::uint8_t call_; + static std::uint8_t jump_; + }; + + // Constructors + Memory(std::uintptr_t address) // used for stuff like nops + { + this->address_ = address; + this->function_ = 0; + } + + Memory(Memory* memoryptr) + { + this->address_ = memoryptr->address_; + this->function_ = memoryptr->function_; + } + + // Memory function + template + static std::function func(std::uintptr_t function) + { + return std::function(reinterpret_cast(function)); + } + + // Returns a pointer to the current Hook instance, incase you want to uninstall your hook later on. + Memory* initialize(); + Memory* install(); + + // Restores the original data of the address + void restore(); + + // Address only patches + template + void set(T value) + { + this->unprotect(sizeof(T)); + *reinterpret_cast(address_) = value; + this->protect(sizeof(T)); + } + + void nop(std::size_t size); + + // The following functions change the memory_ + template + void call(T func, std::size_t size = 5) + { + this->function_ = (std::uintptr_t)func; + this->set_original_data(size); + this->unprotect(size); + +#ifndef _WIN64 + this->original_address = *reinterpret_cast(this->address_ + 1); + this->original_address += this->address_; + this->original_address += 5; + + // 32bit .exe call + *reinterpret_cast(this->address_) = Instructions::call_; + *reinterpret_cast(this->address_ + 1) = (this->function_ - this->address_ - 5); +#else + this->original_address = *reinterpret_cast(this->address_ + 1); + this->original_address += this->address_; + this->original_address += 5; + + // 64bit .exe call + *reinterpret_cast(this->address_) = Memory::Instructions::Call; + *reinterpret_cast(this->address_ + 1) = (this->function_ - this->address_ - 5); +#endif + + this->protect(size); + } + + template + void jump(T func, std::size_t size = 5) + { + this->function_ = (std::uintptr_t)func; + this->set_original_data(size); + this->unprotect(size); + +#ifndef _WIN64 + this->original_address = *reinterpret_cast(this->address_ + 1); + this->original_address += this->address_; + this->original_address += 5; + + // 32bit .exe jump + *reinterpret_cast(this->address_) = Instructions::jump_; + *reinterpret_cast(this->address_ + 1) = (this->function_ - this->address_ - 5); +#else + this->original_address = *reinterpret_cast(this->address_ + 1); + this->original_address += this->address_; + this->original_address += 5; + + // 64bit .exe call + *reinterpret_cast(this->address_) = Memory::Instructions::Jump; + *reinterpret_cast(this->address_ + 1) = (this->function_ - this->address_ - 5); +#endif + + this->protect(size); + } + + void write_string(const std::string& str); + + template + void write(T* data) + { + this->write(data, 1, 0); + } + + template + void write(T* data, std::size_t count = 1, std::size_t size = 0) + { + if (!size) + { + size = sizeof(T); + } + size *= count; + + this->set_original_data(size); + + this->unprotect(size); + memcpy(reinterpret_cast(this->address_), data, size); + this->protect(size); + } + + // This holds the original jump/call location + std::uintptr_t original_address; + + private: + // Sets the original data for opcode restoration + void set_original_data(std::size_t size); + + // Protects / Unprotects the address. + void unprotect(std::size_t size); + void protect(std::size_t size); + + // Original data, for restoring purposes. + std::vector original_data_; + + // Other hooking information + std::uint32_t protect_; + std::uintptr_t address_; + std::uintptr_t function_; + }; +} diff --git a/src/ZoneUtils/Utils/PakFile.cpp b/src/ZoneUtils/Utils/PakFile.cpp new file mode 100644 index 0000000..1893b3f --- /dev/null +++ b/src/ZoneUtils/Utils/PakFile.cpp @@ -0,0 +1,63 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + PakFile::PakFile(const std::uint32_t version) + { + this->buffer_ = std::make_shared(1024 * 1024 * 200); + this->buffer_->init_streams(1); + + // write header + this->buffer_->write_stream("IWffu100", 8, 1); + auto dest_version = this->buffer_->write(&version); + + // endian convert version + endian_convert(dest_version); + } + + std::pair PakFile::add_entry(const std::uint8_t* pixels, const std::size_t size, const bool is_compressed) const + { + // get start position + auto start_location = this->buffer_->size(); + + // write image data + if (!is_compressed) + { + auto compressed = ZoneBuffer::compress_zlib(pixels, size); + this->buffer_->write_stream(compressed.data(), compressed.size(), 1); + } + else + { + this->buffer_->write_stream(pixels, size, 1); + } + + // get end position + auto end_location = this->buffer_->size(); + + return { start_location, end_location }; + } + + std::pair PakFile::add_entry(const std::vector& pixels) const + { + return add_entry(pixels.data(), pixels.size()); + } + + void PakFile::save(const std::string& filename) + { + this->buffer_->save(filename); + } + + std::size_t PakFile::size() + { + return this->buffer_->size(); + } + +} diff --git a/src/ZoneUtils/Utils/PakFile.hpp b/src/ZoneUtils/Utils/PakFile.hpp new file mode 100644 index 0000000..565c793 --- /dev/null +++ b/src/ZoneUtils/Utils/PakFile.hpp @@ -0,0 +1,27 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + class PakFile + { + public: + PakFile(const std::uint32_t version); + + [[nodiscard]] std::pair add_entry(const std::uint8_t* pixels, const std::size_t size, const bool is_compressed = false) const; + [[nodiscard]] std::pair add_entry(const std::vector& pixels) const; + void save(const std::string& filename); + std::size_t size(); + + private: + std::shared_ptr buffer_; + + }; +} diff --git a/src/ZoneUtils/Utils/Swizzle.cpp b/src/ZoneUtils/Utils/Swizzle.cpp new file mode 100644 index 0000000..0735303 --- /dev/null +++ b/src/ZoneUtils/Utils/Swizzle.cpp @@ -0,0 +1,10 @@ +#include "stdafx.hpp" + +namespace ZoneTool::Utils +{ + std::vector swizzle(const std::vector& pixels, const std::uint32_t width, const std::uint32_t height) + { + // todo! + return pixels; + } +} diff --git a/src/ZoneUtils/Utils/Swizzle.hpp b/src/ZoneUtils/Utils/Swizzle.hpp new file mode 100644 index 0000000..27cac98 --- /dev/null +++ b/src/ZoneUtils/Utils/Swizzle.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace ZoneTool::Utils +{ + std::vector swizzle(const std::vector& pixels, const std::uint32_t width, const std::uint32_t height); +} diff --git a/src/ZoneUtils/Zone/PrivateKey.hpp b/src/ZoneUtils/Zone/PrivateKey.hpp new file mode 100644 index 0000000..3f0f8a1 --- /dev/null +++ b/src/ZoneUtils/Zone/PrivateKey.hpp @@ -0,0 +1,307 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +static std::vector PrivateKey = +{ + 0x30, 0x82, 0x09, 0x28, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x02, 0x01, 0x00, 0xC6, 0x81, 0x48, 0xC1, + 0xBC, 0xE5, 0xB6, 0x82, 0x0B, 0xBB, 0x54, 0xCE, + 0xCC, 0xD5, 0xB9, 0x70, 0xC6, 0x6B, 0x1B, 0x23, + 0x23, 0xAB, 0x0A, 0xA3, 0xBA, 0x43, 0xA5, 0xFB, + 0x05, 0xA4, 0x40, 0x4A, 0xA7, 0xC5, 0x4B, 0x71, + 0x7F, 0x5D, 0xD3, 0xD2, 0x18, 0xF1, 0x24, 0x68, + 0x0C, 0x11, 0xA5, 0xE2, 0x63, 0x12, 0x17, 0x36, + 0x50, 0x03, 0x3B, 0x67, 0x9E, 0x9F, 0xF6, 0x0A, + 0x51, 0x10, 0x9D, 0x22, 0x53, 0xB0, 0x46, 0x7E, + 0x75, 0x2A, 0xC5, 0xB7, 0x3A, 0x0A, 0x2B, 0xC4, + 0xD4, 0x49, 0x1C, 0x60, 0x2E, 0x6A, 0x46, 0xF4, + 0x3C, 0xE7, 0x67, 0x5C, 0xD1, 0x7E, 0x88, 0xA8, + 0x69, 0x2C, 0xB3, 0x2B, 0x00, 0xF8, 0x66, 0x59, + 0xE0, 0xF6, 0x79, 0xB1, 0x1C, 0x21, 0xFE, 0xAA, + 0x7A, 0x15, 0x32, 0xF6, 0xD9, 0x91, 0xD5, 0x75, + 0xCC, 0xE0, 0x7E, 0x13, 0x42, 0xDD, 0x4E, 0x44, + 0x6F, 0x91, 0x5B, 0x55, 0x4B, 0x54, 0x2D, 0x38, + 0x40, 0x10, 0x25, 0x21, 0x69, 0x80, 0x11, 0xBF, + 0xB0, 0xA1, 0xB2, 0xC5, 0x3C, 0x6B, 0x93, 0x66, + 0x9F, 0xC4, 0xE8, 0x45, 0x2E, 0x3A, 0x4F, 0x42, + 0x80, 0xB1, 0xEB, 0x52, 0x7B, 0x5F, 0xC8, 0xF2, + 0xE9, 0xA1, 0xFB, 0x5A, 0x62, 0x9C, 0x1E, 0x2A, + 0xA4, 0x01, 0x9A, 0x1D, 0x30, 0x47, 0xD8, 0x99, + 0xBF, 0x52, 0x97, 0x0D, 0xBC, 0x97, 0x4C, 0xD7, + 0xE9, 0x91, 0x14, 0x47, 0x83, 0x5B, 0x80, 0x55, + 0xEE, 0x71, 0x9E, 0x8F, 0xE0, 0xCB, 0xE3, 0x69, + 0x10, 0x62, 0xA2, 0x2D, 0xEA, 0x64, 0x86, 0x3C, + 0x1D, 0x1A, 0x75, 0x9F, 0x1B, 0x4F, 0xFE, 0xCB, + 0x79, 0x54, 0x2B, 0x5C, 0x71, 0x9D, 0x95, 0xFA, + 0xB2, 0x34, 0xAB, 0x47, 0x8C, 0xEF, 0x2B, 0xD8, + 0x6A, 0xC2, 0xE4, 0x52, 0x17, 0x92, 0x67, 0xF0, + 0xB2, 0x3A, 0x71, 0x10, 0x0D, 0x0E, 0xE0, 0xB0, + 0x34, 0x3E, 0x25, 0x50, 0xEC, 0xAB, 0x63, 0x12, + 0x3E, 0x41, 0x36, 0x6F, 0xB8, 0xF1, 0x86, 0x88, + 0xED, 0xBE, 0xE6, 0x25, 0xB5, 0x8F, 0xA6, 0x25, + 0xF0, 0xE7, 0x15, 0x59, 0x72, 0x25, 0x33, 0x1B, + 0x91, 0xB0, 0xDE, 0x96, 0xAB, 0xBD, 0xAF, 0x6F, + 0xCE, 0x8A, 0xCB, 0x94, 0x74, 0x5D, 0x8E, 0x05, + 0xF1, 0xC3, 0x15, 0xD9, 0x84, 0xCC, 0x62, 0xCA, + 0x5B, 0x57, 0x36, 0xC6, 0x46, 0xED, 0x04, 0x96, + 0x59, 0x46, 0x99, 0xDB, 0xDF, 0x8C, 0xFD, 0x7E, + 0xE5, 0xBF, 0xB7, 0xCB, 0x93, 0xEB, 0x5C, 0xE3, + 0xBA, 0xDA, 0x93, 0xB7, 0x5F, 0x2A, 0x2A, 0x03, + 0xBC, 0xA5, 0x20, 0x2F, 0x46, 0xF7, 0xBD, 0x18, + 0xB1, 0xC8, 0x9E, 0xA6, 0x8E, 0x00, 0x78, 0x30, + 0x1A, 0xBA, 0xE0, 0x72, 0x06, 0x72, 0xDE, 0x3D, + 0xAF, 0xC5, 0x57, 0x1C, 0xF0, 0x4A, 0x41, 0x7B, + 0x89, 0x4C, 0x2C, 0xD7, 0x60, 0x67, 0x8D, 0x23, + 0x4B, 0x0F, 0x9B, 0x97, 0x1C, 0x6B, 0x1F, 0x13, + 0x5A, 0xB1, 0x18, 0x67, 0xE8, 0x4F, 0x99, 0x78, + 0xCC, 0x15, 0x79, 0x69, 0x07, 0x02, 0xF9, 0xE5, + 0x42, 0xA3, 0xF8, 0xBA, 0xA8, 0xAB, 0x6D, 0xC1, + 0x91, 0x15, 0x91, 0x1B, 0xA5, 0x28, 0xBA, 0xCA, + 0xF9, 0xF8, 0x79, 0xCF, 0xEF, 0x14, 0xFE, 0x80, + 0xB8, 0xB8, 0xEB, 0x24, 0xA7, 0xA8, 0xCF, 0x62, + 0xC1, 0xC9, 0x32, 0xDB, 0x87, 0xCD, 0x00, 0xEA, + 0x5F, 0xFA, 0xC4, 0x0E, 0x9A, 0xB1, 0xFB, 0x38, + 0x5C, 0xC3, 0xB4, 0x71, 0x4C, 0x18, 0x0C, 0xEA, + 0x97, 0x77, 0xB8, 0xC2, 0xB4, 0x48, 0x02, 0x4D, + 0x6A, 0xE9, 0xDE, 0x1F, 0x0D, 0x29, 0x61, 0x23, + 0x00, 0x65, 0x45, 0x42, 0x99, 0x20, 0x32, 0x93, + 0xD9, 0x01, 0x99, 0x22, 0xEE, 0x29, 0x05, 0x72, + 0x2F, 0xFE, 0x70, 0xD2, 0x73, 0x13, 0x31, 0xA5, + 0x6B, 0xCA, 0x25, 0xA4, 0x7A, 0x7E, 0xB4, 0xE4, + 0x83, 0xB8, 0x85, 0xBD, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x02, 0x00, 0x3A, 0x4C, 0x17, + 0xC1, 0x06, 0x27, 0xF4, 0x7C, 0xA6, 0xD3, 0x62, + 0x69, 0xC5, 0x90, 0x73, 0x00, 0xA2, 0xD5, 0x2B, + 0xC8, 0x1B, 0x1E, 0x8D, 0x66, 0x5E, 0x3A, 0xA5, + 0xD1, 0x5D, 0xEB, 0xCA, 0x11, 0xE2, 0xC9, 0xB7, + 0x89, 0x62, 0x9D, 0x7C, 0xAA, 0xF4, 0x36, 0x45, + 0xB9, 0xE4, 0xE9, 0x7D, 0xED, 0x49, 0xA9, 0x33, + 0x75, 0xF8, 0xF3, 0xAA, 0x0B, 0x99, 0x73, 0x62, + 0xFF, 0xCE, 0xA4, 0xF1, 0x70, 0xF6, 0x61, 0x0B, + 0xA2, 0xF1, 0xE8, 0x18, 0xDE, 0xE1, 0xC5, 0xC0, + 0xEA, 0xF2, 0x69, 0x72, 0x52, 0xF0, 0xEE, 0xB7, + 0x5D, 0xDD, 0x51, 0x1C, 0x73, 0x0E, 0xF7, 0x79, + 0x4C, 0xF4, 0x6E, 0x73, 0x44, 0x0A, 0xE9, 0xE8, + 0xAD, 0x36, 0xBB, 0x4C, 0x2B, 0xEE, 0x96, 0x31, + 0x43, 0x81, 0xCD, 0x1E, 0x05, 0x8F, 0x0A, 0x1C, + 0x45, 0x97, 0x60, 0xCA, 0xC5, 0xFF, 0x5A, 0x1D, + 0x35, 0x4E, 0x0A, 0xC3, 0x66, 0xFE, 0x53, 0x7F, + 0x60, 0x48, 0xB8, 0x35, 0x1F, 0x65, 0xEB, 0x7D, + 0xCA, 0xED, 0x25, 0xDF, 0xE9, 0xBA, 0xFF, 0xE0, + 0xEF, 0x3F, 0xB6, 0xC5, 0x7C, 0x23, 0xC2, 0x6E, + 0x2A, 0x9A, 0xBF, 0x25, 0xDC, 0x38, 0x6D, 0x7E, + 0x07, 0x91, 0x03, 0xC8, 0x5C, 0xD8, 0x1E, 0xFF, + 0x61, 0x08, 0x7F, 0x8B, 0xF2, 0x4C, 0x8E, 0x9D, + 0x17, 0x32, 0x30, 0x20, 0x3A, 0xA1, 0x8B, 0x51, + 0xBF, 0x0B, 0x52, 0x57, 0x41, 0x55, 0x5B, 0x97, + 0x0A, 0x25, 0x97, 0xEA, 0xB8, 0x58, 0x8D, 0xA8, + 0x80, 0xDF, 0x96, 0x08, 0x09, 0x32, 0x10, 0x49, + 0xB7, 0xF7, 0xD8, 0x20, 0x79, 0x14, 0xBD, 0x5E, + 0x98, 0x05, 0x4E, 0xE9, 0xD9, 0x21, 0x68, 0xE5, + 0x76, 0x15, 0xC6, 0x10, 0x13, 0x58, 0x59, 0x99, + 0x67, 0xDA, 0x9A, 0xD8, 0x11, 0x1F, 0xE9, 0x18, + 0x6B, 0x80, 0xFC, 0x56, 0xDE, 0x1E, 0xE7, 0xA2, + 0xA7, 0x3D, 0x00, 0x8F, 0x56, 0x7B, 0x70, 0x28, + 0x23, 0x76, 0x90, 0xB1, 0x17, 0xCB, 0xBA, 0x15, + 0x4D, 0x51, 0x07, 0xB9, 0xEA, 0x9F, 0x44, 0x64, + 0xF5, 0x76, 0x89, 0x47, 0x2C, 0xE6, 0xB1, 0x20, + 0xE4, 0x4C, 0x73, 0x74, 0xCB, 0x16, 0x93, 0x48, + 0x6C, 0xCC, 0x29, 0x6C, 0x91, 0xE1, 0x04, 0x4B, + 0xB2, 0x3E, 0x63, 0x21, 0x46, 0xCB, 0x1D, 0x18, + 0xFD, 0xC8, 0xA8, 0xBD, 0x7C, 0x0D, 0xCF, 0x78, + 0x9E, 0x27, 0x7A, 0x37, 0x12, 0xCF, 0x0A, 0x82, + 0x4E, 0x25, 0x2E, 0x13, 0xCA, 0x11, 0x81, 0xFB, + 0x8B, 0x80, 0x17, 0xAD, 0xBC, 0x7A, 0xB4, 0xBE, + 0x91, 0x54, 0xD8, 0x58, 0x73, 0xAC, 0x36, 0x49, + 0xDF, 0x56, 0x41, 0x5C, 0x56, 0xFA, 0x68, 0x5D, + 0xE5, 0x51, 0x31, 0x71, 0x15, 0x19, 0x1D, 0x1C, + 0x8E, 0xB7, 0x27, 0xED, 0x89, 0x9A, 0x0A, 0x7B, + 0x6B, 0xBC, 0x99, 0x43, 0x3C, 0x6D, 0xD3, 0x00, + 0x53, 0x77, 0xDC, 0x60, 0xAF, 0x99, 0xB1, 0xE6, + 0x4B, 0xF6, 0x84, 0x83, 0x67, 0x69, 0x9A, 0xCD, + 0x8C, 0x90, 0xD1, 0x6F, 0x69, 0xFE, 0x44, 0x86, + 0x96, 0xBA, 0x6E, 0xDA, 0x99, 0xE1, 0x90, 0x88, + 0x7D, 0xE6, 0x13, 0xA6, 0x75, 0xF6, 0x67, 0x4F, + 0x52, 0x1D, 0x5B, 0x6D, 0xC7, 0xCA, 0xE2, 0xF3, + 0x06, 0xC3, 0x4E, 0x1A, 0xFD, 0xF2, 0x56, 0x33, + 0xC5, 0xAC, 0x0C, 0xF1, 0xF5, 0x7B, 0x3B, 0x58, + 0x31, 0xBD, 0x37, 0x95, 0x09, 0xAB, 0x36, 0x79, + 0x7D, 0xFA, 0x91, 0x90, 0xDC, 0x42, 0xA0, 0x82, + 0xBC, 0xDA, 0xAB, 0xE1, 0x2A, 0xF7, 0xC5, 0x24, + 0x2C, 0xE7, 0xE5, 0x64, 0xFC, 0x04, 0x29, 0x62, + 0x25, 0x7C, 0x95, 0x03, 0xD1, 0xE5, 0xFE, 0x5B, + 0x9D, 0x36, 0x68, 0xD0, 0x37, 0x5C, 0x40, 0x25, + 0xE9, 0x7B, 0x97, 0x55, 0xFC, 0x12, 0x09, 0x24, + 0x52, 0x54, 0x0E, 0x24, 0x15, 0x9A, 0x85, 0x0C, + 0x13, 0x34, 0x31, 0x84, 0x81, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xE4, 0x04, 0xD3, 0x58, 0x32, 0x75, + 0x8F, 0xF0, 0x5F, 0xF8, 0x40, 0x16, 0x53, 0x82, + 0xB2, 0x41, 0x2A, 0x6B, 0xC3, 0x09, 0xD8, 0x49, + 0x35, 0x34, 0xA6, 0x33, 0x0E, 0xB2, 0x96, 0xE5, + 0x03, 0xEC, 0x45, 0x5D, 0xAF, 0xEE, 0x32, 0x12, + 0xEC, 0xDF, 0xDF, 0xE4, 0xCD, 0x85, 0x4D, 0x47, + 0xBC, 0x8D, 0x12, 0xAE, 0x0B, 0x8D, 0x6F, 0x59, + 0xE1, 0x58, 0xE1, 0xB4, 0xA3, 0x6F, 0x37, 0x04, + 0xBD, 0xF2, 0x4B, 0xDE, 0xE5, 0x93, 0xE2, 0x87, + 0xFA, 0x21, 0xDB, 0x8C, 0xFA, 0x4C, 0x09, 0x8B, + 0x54, 0xE7, 0xEC, 0x86, 0x33, 0x88, 0x28, 0xFA, + 0x54, 0x5B, 0xB4, 0xDF, 0xAB, 0xAB, 0xE8, 0xCE, + 0x26, 0x68, 0x11, 0xC6, 0xCD, 0x69, 0x32, 0xA0, + 0x5C, 0xE4, 0x96, 0x97, 0xB2, 0x44, 0x7F, 0x70, + 0x6C, 0xC8, 0xA4, 0x75, 0xE0, 0x85, 0xFA, 0x0D, + 0xF3, 0x2C, 0xDF, 0x5B, 0xAE, 0x00, 0x4C, 0xF5, + 0xE3, 0xF1, 0xB1, 0x06, 0x06, 0x23, 0x45, 0xF1, + 0x94, 0x43, 0xC5, 0x12, 0x1E, 0xAF, 0xC8, 0xAF, + 0xC8, 0x73, 0x84, 0xCA, 0x3A, 0x53, 0x76, 0xD2, + 0x4C, 0x9E, 0x7E, 0x8F, 0xE0, 0xAE, 0xF1, 0x0C, + 0x09, 0x37, 0xC2, 0x47, 0x66, 0x12, 0x42, 0x00, + 0x5C, 0x17, 0xC1, 0xBE, 0x3B, 0xD0, 0xBC, 0x23, + 0x00, 0x56, 0xF0, 0xE4, 0x6B, 0xD3, 0x0F, 0xED, + 0x22, 0x47, 0x14, 0x2D, 0x73, 0x92, 0xB7, 0x35, + 0xFB, 0x99, 0xD4, 0x71, 0x08, 0xA4, 0x4C, 0xE9, + 0x59, 0x6D, 0x5B, 0xE8, 0xDE, 0x96, 0x45, 0x7B, + 0x5D, 0x2A, 0xCD, 0xC8, 0xAB, 0x83, 0xC6, 0x05, + 0x1A, 0x72, 0xB0, 0xFD, 0x57, 0xC8, 0x76, 0xA7, + 0x9F, 0xD7, 0xED, 0x75, 0x00, 0x24, 0x06, 0x45, + 0xBD, 0x52, 0x2F, 0x3E, 0x15, 0x79, 0x7D, 0x71, + 0x2D, 0x00, 0xC5, 0xEF, 0xEA, 0xBE, 0x83, 0xBC, + 0x79, 0x12, 0xC4, 0x7A, 0x8E, 0xD1, 0x3B, 0x1F, + 0x7A, 0x1D, 0x02, 0x82, 0x01, 0x01, 0x00, 0xDE, + 0xDD, 0x49, 0x54, 0xAF, 0x50, 0x2B, 0xA8, 0x87, + 0x76, 0x27, 0xE0, 0x8E, 0x39, 0x57, 0xFF, 0xED, + 0x67, 0xF5, 0x1E, 0xF3, 0x01, 0x6C, 0x21, 0xCA, + 0xDE, 0x3C, 0x8B, 0x98, 0xD6, 0xE0, 0x93, 0xC5, + 0x37, 0xC6, 0xCA, 0x6C, 0xD5, 0xD1, 0x1F, 0x0B, + 0x7C, 0x14, 0xBA, 0xA8, 0xCC, 0xB3, 0x69, 0x91, + 0xBC, 0x64, 0x79, 0x4C, 0x77, 0x68, 0x02, 0xE0, + 0xFD, 0x81, 0x2C, 0x3D, 0x73, 0xD2, 0x9D, 0x66, + 0xDD, 0x92, 0x95, 0x15, 0x46, 0xF8, 0x7D, 0x87, + 0xF4, 0x04, 0xA4, 0x8B, 0xC5, 0xBB, 0x1E, 0x05, + 0xDD, 0x20, 0x3B, 0x9B, 0xA0, 0x15, 0xCA, 0xB4, + 0x1A, 0xC7, 0xC1, 0xC6, 0x7B, 0x5C, 0x0E, 0x23, + 0xEA, 0xB5, 0xAC, 0xAD, 0x24, 0xFA, 0x59, 0x0A, + 0xE6, 0x85, 0x8E, 0x70, 0xCA, 0xC0, 0x40, 0x7B, + 0xD1, 0x53, 0xA4, 0x69, 0x48, 0x5C, 0xD6, 0x5E, + 0x86, 0x6F, 0xAE, 0xEA, 0x0B, 0x81, 0x3F, 0x0D, + 0x3B, 0x69, 0xB3, 0xB2, 0xB0, 0x3F, 0x78, 0x62, + 0x78, 0x1B, 0x7E, 0x59, 0xE4, 0x64, 0x2E, 0x66, + 0x7B, 0x8F, 0x5D, 0x37, 0xC9, 0x77, 0x1C, 0x26, + 0x23, 0x73, 0x73, 0x20, 0xC4, 0xA3, 0x93, 0x01, + 0x8C, 0x54, 0x51, 0x1F, 0x88, 0xFC, 0x7D, 0xBC, + 0xE9, 0x30, 0x9B, 0xDF, 0x19, 0x7C, 0x08, 0xB6, + 0x38, 0x46, 0xF0, 0x91, 0x10, 0xA4, 0x81, 0x63, + 0x9C, 0x86, 0xF4, 0xF9, 0xAF, 0xB2, 0xAA, 0x88, + 0x67, 0x37, 0xC3, 0x51, 0x9A, 0xCF, 0xE0, 0xD8, + 0x23, 0xB9, 0x62, 0x55, 0x75, 0xF8, 0x0A, 0x02, + 0x30, 0x2C, 0x6C, 0x90, 0x79, 0x11, 0xCF, 0x53, + 0x6A, 0x2F, 0xDC, 0x73, 0x48, 0x4D, 0x27, 0x65, + 0xD2, 0xAD, 0xCE, 0xA4, 0xE2, 0xBD, 0xE6, 0x50, + 0x3C, 0x37, 0x5C, 0x94, 0x5F, 0xF1, 0x07, 0x95, + 0x86, 0xAE, 0xA4, 0x4A, 0x8A, 0x16, 0x72, 0x5D, + 0xC8, 0x94, 0x77, 0xE9, 0xE6, 0x68, 0x21, 0x02, + 0x82, 0x01, 0x00, 0x22, 0xEF, 0x5E, 0x56, 0x1F, + 0xFD, 0x05, 0x1D, 0xA6, 0x06, 0x8A, 0x03, 0x21, + 0xAE, 0x5F, 0x93, 0x3E, 0x65, 0x9E, 0x4F, 0x4E, + 0xD5, 0x00, 0x92, 0xDD, 0x77, 0xEB, 0x23, 0x8F, + 0x9C, 0xEC, 0xF9, 0xF1, 0x32, 0xC6, 0x76, 0xAF, + 0x33, 0x59, 0x19, 0x7F, 0xDB, 0x35, 0x65, 0xDC, + 0x0E, 0x46, 0xA4, 0x32, 0x9D, 0x79, 0xE5, 0xA4, + 0x8E, 0xD7, 0xC8, 0xDF, 0x63, 0xE6, 0xBA, 0x6E, + 0x6A, 0x8A, 0x93, 0xA9, 0xEE, 0x2F, 0xAC, 0x75, + 0xC3, 0xB1, 0x9E, 0x28, 0x5E, 0x34, 0x27, 0x1C, + 0x3B, 0x3D, 0xB9, 0xFE, 0xF2, 0x1C, 0xC9, 0x1A, + 0xC6, 0x27, 0xD0, 0x99, 0x28, 0xBF, 0xCB, 0xA9, + 0xF2, 0x93, 0xCD, 0xD4, 0x13, 0x6A, 0x7B, 0x58, + 0xA7, 0x5B, 0x34, 0x26, 0xA2, 0x7D, 0x4C, 0xA3, + 0x5D, 0xDD, 0x3C, 0xEC, 0x7D, 0xA6, 0xF5, 0xBF, + 0xF7, 0x0D, 0x2F, 0xED, 0xD4, 0xC5, 0x17, 0x04, + 0xB3, 0x93, 0xC5, 0xFF, 0x02, 0x56, 0x44, 0x67, + 0x3D, 0xF9, 0x22, 0x5C, 0xFA, 0x46, 0xBB, 0x55, + 0xFB, 0x20, 0xB2, 0x1A, 0x41, 0x45, 0xA8, 0x6E, + 0x07, 0x10, 0x78, 0x24, 0x19, 0xF5, 0x29, 0xB8, + 0x82, 0xC8, 0x92, 0xC3, 0x70, 0x5F, 0x02, 0x23, + 0x1C, 0x9C, 0xFF, 0x82, 0xCE, 0x9B, 0xA9, 0x82, + 0x79, 0xB6, 0x5E, 0x9B, 0xAF, 0xD3, 0x99, 0x55, + 0xD2, 0x56, 0x33, 0x1F, 0xB3, 0x4B, 0x06, 0xCA, + 0x8D, 0x39, 0xCC, 0x3A, 0xD1, 0x4A, 0xAA, 0xA9, + 0xA2, 0x02, 0xF2, 0x8F, 0x67, 0x61, 0x5B, 0x12, + 0xB0, 0x30, 0xF9, 0x12, 0xE8, 0x50, 0xE6, 0xF2, + 0x9A, 0xEB, 0x8F, 0x70, 0xF0, 0x8F, 0xD7, 0x85, + 0xDE, 0x6E, 0xCE, 0x0D, 0x72, 0xA4, 0x9F, 0xE0, + 0x5A, 0x25, 0x04, 0x87, 0x7A, 0x72, 0x9D, 0xD7, + 0x8B, 0x54, 0x82, 0xCE, 0xD0, 0xD0, 0x5D, 0x7A, + 0x1F, 0x91, 0x49, 0x3F, 0xD3, 0x2B, 0x38, 0x81, + 0x5F, 0x74, 0x3D, 0x02, 0x82, 0x01, 0x00, 0x26, + 0x3C, 0x79, 0x14, 0x5A, 0x6F, 0xBA, 0xCD, 0xD3, + 0x4F, 0xE6, 0x4F, 0x94, 0x97, 0x2A, 0x0D, 0xF2, + 0xC1, 0x5F, 0x40, 0xCC, 0x18, 0x76, 0x60, 0xE5, + 0xD9, 0x73, 0x31, 0xD0, 0x2B, 0x2D, 0xA4, 0xAC, + 0xB5, 0x81, 0x19, 0xC1, 0xCA, 0x7D, 0x72, 0x82, + 0x19, 0xEB, 0xC4, 0x8A, 0xA9, 0x74, 0x2E, 0xAC, + 0x9E, 0x51, 0xEE, 0xAD, 0xBB, 0xDE, 0xD0, 0x7F, + 0xA1, 0x7E, 0xC4, 0x04, 0x57, 0x16, 0xCF, 0x82, + 0x92, 0x89, 0x4F, 0xA2, 0xB2, 0xE6, 0x77, 0x43, + 0x18, 0x0E, 0xA3, 0xDC, 0x87, 0x34, 0x2F, 0x56, + 0x7A, 0x35, 0xC9, 0x84, 0x4D, 0xD6, 0xEF, 0x4C, + 0x2F, 0x3C, 0x0E, 0x76, 0xEB, 0x3B, 0x44, 0x01, + 0xCD, 0x87, 0x15, 0xF5, 0x2E, 0xDE, 0xF4, 0xDF, + 0xF5, 0xE4, 0xFC, 0x27, 0x45, 0xE1, 0xEE, 0x2E, + 0x50, 0x1F, 0xB5, 0x99, 0x8A, 0x9C, 0xC2, 0xFA, + 0xBC, 0xBD, 0xF4, 0x98, 0x9B, 0x18, 0xB5, 0xA4, + 0xA3, 0x88, 0xE3, 0x17, 0xC9, 0x3D, 0x5A, 0x7E, + 0x2A, 0x75, 0x2D, 0x1C, 0x67, 0x1E, 0xF9, 0x07, + 0x04, 0x33, 0xF8, 0x9E, 0x55, 0xA6, 0x65, 0xE1, + 0xF5, 0x3B, 0x1D, 0x89, 0x85, 0xB3, 0x3A, 0xC9, + 0x8D, 0x35, 0xFA, 0x25, 0x1A, 0xEF, 0x96, 0xF3, + 0x0E, 0x5F, 0x14, 0x9F, 0x16, 0x54, 0x3D, 0x7A, + 0x65, 0xF5, 0x65, 0xD8, 0xBD, 0xAE, 0x3B, 0xB4, + 0xC7, 0x6A, 0x30, 0x25, 0x96, 0x8B, 0x64, 0xCE, + 0xBA, 0x46, 0xCA, 0xEF, 0xF9, 0x4B, 0xA9, 0x1B, + 0xEC, 0x25, 0x26, 0x45, 0xDB, 0x53, 0x8F, 0x04, + 0x09, 0x17, 0xD6, 0x18, 0x83, 0x6D, 0x16, 0xD1, + 0x0C, 0xEA, 0xE1, 0x3B, 0xB6, 0x2F, 0xE6, 0x8E, + 0x85, 0xB2, 0xE4, 0x3D, 0x4D, 0xD9, 0x9E, 0xE6, + 0x2D, 0x2E, 0xA2, 0x67, 0xA2, 0x7E, 0x4B, 0xFA, + 0xCF, 0x7C, 0xAF, 0x1B, 0x4A, 0xCD, 0xC7, 0x19, + 0x68, 0x0C, 0x70, 0xF6, 0x64, 0x5D, 0x81, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xE1, 0xB3, 0xDC, 0x2D, + 0x13, 0x00, 0xE9, 0x3A, 0x99, 0xB6, 0x93, 0xDA, + 0xA0, 0xA0, 0xE4, 0x3F, 0x62, 0xAB, 0xB8, 0xDE, + 0xD4, 0x49, 0xFC, 0x54, 0xAE, 0xAB, 0x61, 0xCF, + 0x02, 0x9B, 0x24, 0xA4, 0x4F, 0xEB, 0xD6, 0x07, + 0xA6, 0x87, 0x3D, 0xB8, 0x64, 0xA6, 0x2F, 0xC5, + 0xB1, 0x31, 0x5B, 0xD3, 0x1C, 0x57, 0x7A, 0x96, + 0x02, 0x7A, 0xF2, 0x74, 0xFA, 0x58, 0x11, 0x05, + 0x54, 0x59, 0x2D, 0x83, 0xC9, 0x2B, 0xE8, 0x20, + 0x5B, 0x04, 0xC8, 0x98, 0x2D, 0xA2, 0x24, 0x36, + 0x77, 0x70, 0xBF, 0xEB, 0xE7, 0x19, 0xA0, 0xAA, + 0x50, 0x8B, 0x45, 0x02, 0x54, 0xDC, 0x88, 0x1A, + 0x6F, 0x49, 0x69, 0x02, 0xCA, 0xF1, 0x06, 0xBE, + 0xA3, 0xA2, 0x0A, 0xC4, 0xA2, 0x20, 0x85, 0x90, + 0xC1, 0x11, 0xBB, 0x49, 0x7A, 0xE3, 0x7C, 0xA1, + 0xD0, 0xA1, 0xEB, 0x43, 0xA8, 0x42, 0x60, 0xDA, + 0xBB, 0xB3, 0xC0, 0x4A, 0x60, 0x6B, 0xAF, 0x99, + 0x94, 0x46, 0x8E, 0xFF, 0x4E, 0x2C, 0xFA, 0x90, + 0x2B, 0xF6, 0xFC, 0x6A, 0x7B, 0x4D, 0x4C, 0x3D, + 0xD0, 0xA4, 0xAC, 0x20, 0x1D, 0xC4, 0x72, 0x3F, + 0x7E, 0xFC, 0x9C, 0x2B, 0x6A, 0x60, 0xBC, 0x28, + 0x1C, 0xE9, 0xCD, 0x57, 0xFD, 0x15, 0x28, 0x16, + 0x73, 0x0F, 0xA2, 0xEC, 0x23, 0x27, 0xEF, 0xB0, + 0x22, 0xB5, 0x47, 0x0E, 0xEC, 0xEC, 0xD0, 0x0E, + 0x32, 0x47, 0xD3, 0xE7, 0x83, 0xD8, 0x15, 0xAD, + 0x20, 0xD7, 0x8C, 0x50, 0xE5, 0x12, 0xE4, 0x93, + 0x1F, 0x04, 0xB6, 0xDC, 0x12, 0xB5, 0xDF, 0x3D, + 0x62, 0xF8, 0x1B, 0x29, 0xB5, 0x91, 0x67, 0x13, + 0x1C, 0x3E, 0xB8, 0x20, 0x9A, 0x81, 0xE8, 0xBF, + 0xD0, 0xD4, 0xF4, 0x99, 0x4F, 0xFF, 0xB3, 0xB9, + 0x9B, 0xD0, 0xBA, 0x07, 0x8C, 0xCB, 0x42, 0xC0, + 0xEE, 0xE3, 0x1C, 0xC0, 0x5E, 0x84, 0x83, 0xD8, + 0xF1, 0xEA, 0xFD, 0x3C +}; diff --git a/src/ZoneUtils/Zone/Zone.cpp b/src/ZoneUtils/Zone/Zone.cpp new file mode 100644 index 0000000..60ae369 --- /dev/null +++ b/src/ZoneUtils/Zone/Zone.cpp @@ -0,0 +1,13 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ +} diff --git a/src/ZoneUtils/Zone/Zone.hpp b/src/ZoneUtils/Zone/Zone.hpp new file mode 100644 index 0000000..8b0f454 --- /dev/null +++ b/src/ZoneUtils/Zone/Zone.hpp @@ -0,0 +1,55 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + class IAsset; + + class IZone + { + public: + virtual IAsset* find_asset(std::int32_t type, const std::string& name) + { + return nullptr; + } + + virtual void* get_asset_pointer(std::int32_t type, const std::string& name) = 0; + + virtual void add_asset_of_type_by_pointer(std::int32_t type, void* pointer) = 0; + + virtual void add_asset_of_type(const std::string& type, const std::string& name) = 0; + virtual void add_asset_of_type(std::int32_t type, const std::string& name) = 0; + virtual std::int32_t get_type_by_name(const std::string& type) = 0; + + virtual void build(ZoneBuffer* buf) = 0; + + virtual zone_target get_target() + { + return target_; + } + virtual zone_target_version get_target_version() + { + return target_version_; + } + virtual void set_target(const zone_target target) + { + target_ = target; + } + virtual void set_target_version(const zone_target_version version) + { + target_version_ = version; + } + + protected: + zone_target target_ = zone_target::pc; + zone_target_version target_version_ = zone_target_version::iw4_release; + + }; +} diff --git a/src/ZoneUtils/Zone/ZoneBuffer.cpp b/src/ZoneUtils/Zone/ZoneBuffer.cpp new file mode 100644 index 0000000..65eb07a --- /dev/null +++ b/src/ZoneUtils/Zone/ZoneBuffer.cpp @@ -0,0 +1,441 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +#include +#include +#include + +#include "PrivateKey.hpp" + +#define ZSTD_BEST_COMPRESSION 22 +#define ZLIB_BEST_COMPRESSION Z_BEST_COMPRESSION + +namespace ZoneTool +{ + ZoneBuffer::ZoneBuffer() + { + this->m_stream = 0; + this->m_shiftsize = 0; + this->m_numstreams = 0; + + this->m_pos = 0; + this->m_len = MAX_ZONE_SIZE; + + this->m_zonepointers.clear(); + + // 370mb, should be enough? + this->m_buf.resize(this->m_len); + } + + ZoneBuffer::~ZoneBuffer() + { + // clear zone pointers + this->m_zonepointers.clear(); + + // clear scriptstrings + for (std::size_t idx = 0; idx < this->m_scriptstrings.size(); idx++) + { + free(this->m_scriptstrings[idx]); + } + this->m_scriptstrings.clear(); + + // clear zone buffer + this->m_buf.clear(); + } + + ZoneBuffer::ZoneBuffer(std::vector data) + { + this->m_stream = 0; + this->m_shiftsize = 0; + this->m_numstreams = 0; + + this->m_buf = data; + this->m_pos = data.size(); + this->m_len = data.size(); + + this->m_zonepointers.clear(); + } + + ZoneBuffer::ZoneBuffer(std::size_t size) + { + this->m_stream = 0; + this->m_shiftsize = 0; + this->m_numstreams = 0; + + this->m_pos = 0; + this->m_len = size; + this->m_buf.resize(this->m_len); + + this->m_zonepointers.clear(); + } + + void ZoneBuffer::alloc_image_pak(const std::uint32_t version) + { + image_pak_ = std::make_shared(version); + } + PakFile* ZoneBuffer::image_pak() + { + return image_pak_.get(); + } + + void ZoneBuffer::add_image(const std::vector& pixels) + { + const auto stream_data = this->image_pak()->add_entry(pixels); + + XAssetStreamFile stream_file = {}; + stream_file.fileIndex = 5; + stream_file.offset = stream_data.first; + stream_file.offsetEnd = stream_data.second; + + this->stream_files_.push_back(stream_file); + } + + void ZoneBuffer::init_streams(std::size_t streams) + { + this->m_numstreams = streams; + this->m_zonestreams.resize(streams); + + this->m_shiftsize = 32; + + for (std::size_t i = 31; i > 0; i--) + { + if (std::bitset(this->m_numstreams).test(i)) + { + this->m_shiftsize -= (i + 1); + break; + } + } + } + + void ZoneBuffer::write_data(const void* _data, std::size_t size, std::size_t count) + { + // Check if we should realloc the buffer + if ((size * count) + m_pos > m_len) + { + ZONETOOL_ERROR("No more space left in zone buffer."); // this->realloc(((size * count) + m_pos) - m_len); + return; + } + + // Copy data to buffer + memcpy(&m_buf[m_pos], _data, size * count); + m_pos += size * count; + } + + void ZoneBuffer::write_data(const void* _data, std::size_t size) + { + write_data(_data, size, 1); + } + + void ZoneBuffer::write_stream(const void* _data, std::size_t size, std::size_t count) + { + // If we're writing to stream 2... + if (m_stream == 2) // ZONESTREAM_RUNTIME. Meaning that this data is generated when the game runs. + // Therefore we need to alloc space, but we don't write data. + { + // if (m_numstreams > 0) + { + m_zonestreams[m_stream] += size * count; + } + + return; + } + + write_data(_data, size, count); + + // Update streams + // if (m_numstreams > 0) + { + m_zonestreams[m_stream] += size * count; + } + } + + void ZoneBuffer::write_stream(const void* _data, size_t size) + { + return write_stream(_data, size, 1); + } + + char* ZoneBuffer::write_str(const std::string& _str) + { + write_stream(_str.data(), _str.size() + 1); + return reinterpret_cast(-1); + } + + void ZoneBuffer::write_str_raw(const std::string& _str) + { + return write_stream(_str.data(), _str.size() + 1); + } + + std::uint8_t* ZoneBuffer::buffer() + { + return m_buf.data(); + } + + std::size_t ZoneBuffer::size() + { + return m_pos; + } + + void ZoneBuffer::align(std::uint32_t alignment) + { + // if (m_numstreams > 0) + { + m_zonestreams[m_stream] = ~alignment & (alignment + m_zonestreams[m_stream]); + } + } + + void ZoneBuffer::inc_stream(const std::uint32_t stream, const std::size_t size) + { + m_zonestreams[stream] += size; + } + + void ZoneBuffer::push_stream(std::uint32_t stream) + { + m_streamstack.push(m_stream); + m_stream = stream; + } + + void ZoneBuffer::pop_stream() + { + m_stream = m_streamstack.top(); + m_streamstack.pop(); + } + + std::uint8_t ZoneBuffer::current_stream() + { + return m_stream; + } + + std::uint32_t ZoneBuffer::current_stream_offset() + { + return m_zonestreams[m_stream]; + } + + std::uint32_t ZoneBuffer::stream_offset(std::uint8_t stream) + { + return m_zonestreams[stream]; + } + + std::uint16_t ZoneBuffer::write_scriptstring(std::string str) + { + this->m_scriptstrings.push_back(_strdup(str.data())); + return static_cast(this->m_scriptstrings.size() - 1); + } + + const char* ZoneBuffer::get_scriptstring(std::size_t idx) + { + return this->m_scriptstrings[idx]; + } + + std::size_t ZoneBuffer::scriptstring_count() + { + return this->m_scriptstrings.size(); + } + + void ZoneBuffer::save(const std::string& filename) + { + // Alloc file + std::filebuf _fb; + _fb.open(filename, (std::ios::out | std::ios::binary)); + + // Write buffer to file + std::ostream _os(&_fb); + _os.write(reinterpret_cast(m_buf.data()), m_pos); + + // Close file + _fb.close(); + } + + void ZoneBuffer::save_image_pak(const std::string& filename) + { + this->image_pak()->save(filename); + } + + std::vector ZoneBuffer::compress_zlib(const std::uint8_t* data, const std::size_t data_size, bool compress_blocks) + { + auto compressBound = [](unsigned long sourceLen) + { + return static_cast((ceil(sourceLen * 1.001)) + 12); + }; + + if (compress_blocks == false) + { + // calculate buffer size needed for current zone + auto size = compressBound(data_size); + + // alloc array for compressed data + std::vector compressed; + compressed.resize(size); + + // compress buffer + auto status = compress2(compressed.data(), &size, data, data_size, ZLIB_BEST_COMPRESSION); + compressed.resize(size); + + // return compressed buffer + return compressed; + } + else + { + // data should be 0x10000 byte aligned + const auto block_size = 0x10000; + auto bound_size = compressBound(block_size); + auto num_blocks = data_size / block_size; + + std::vector> blocks; + blocks.resize(num_blocks); + + auto data_ptr = data; + for (auto& block : blocks) + { + // allocate for compressed data + block.resize(bound_size); + + // compress block buffer + unsigned long size; + auto status = compress2(block.data(), &size, data_ptr, block_size, ZLIB_BEST_COMPRESSION); + if (size >= block_size) + { + // discard compressed data and just store uncompressed data + block.resize(block_size + 2); + + // 0 block size is uncompressed + block[0] = 0; + block[1] = 0; + memcpy(block.data() + 2, data_ptr, block_size); + } + else + { + block.resize(size); + + // overwrite zlib header with block size + size -= 2; + block[0] = (size & 0xff00) >> 8; + block[1] = size & 0xff; + } + + // go to next block + data_ptr += block_size; + } + + std::vector compressed; + for (auto& block : blocks) + { + compressed.insert(compressed.end(), block.begin(), block.end()); + } + + + return compressed; + } + } + + std::vector ZoneBuffer::compress_zlib(const std::vector& data, bool compress_blocks) + { + return ZoneBuffer::compress_zlib(data.data(), data.size(), compress_blocks); + } + + std::vector ZoneBuffer::compress_zstd() + { + // calculate buffer size needed for current zone + auto size = ZSTD_compressBound(this->m_pos); + + // alloc array for compressed data + std::vector compressed; + compressed.resize(size); + + // compress buffer + auto destsize = ZSTD_compress(compressed.data(), size, this->m_buf.data(), this->m_pos, 11); + compressed.resize(destsize); + + if (ZSTD_isError(destsize)) + { + ZONETOOL_ERROR("An error occured while compressing the fastfile: %s", ZSTD_getErrorName(destsize)); + return {}; + } + + // return compressed buffer + return compressed; + } + + std::vector ZoneBuffer::compress_zlib(bool compress_blocks) + { + return ZoneBuffer::compress_zlib(this->m_buf.data(), this->m_pos, compress_blocks); + } + + void GenerateKeys(XZoneKey* key) + { + srand(time(nullptr)); + + auto buffer = reinterpret_cast(key); + + for (auto idx = 0u; idx < sizeof XZoneKey; idx++) + { + buffer[idx] = (rand() % 0xFF); + } + } + + void ZoneBuffer::encrypt() + { + // register ciphers + register_cipher(&aes_desc); + + // find hashes + auto h_sha512 = find_hash("sha512"); + auto h_aes = find_cipher("aes"); + + // generate random encryption key + XZoneKey zonekey; + GenerateKeys(&zonekey); + zonekey.version = 1; + + // realloc buffersize + { + auto destsize = +#ifdef USE_PRIVATEKEY + 512 + +#else + sizeof XZoneKey + +#endif + this->m_buf.size(); + std::vector temp = this->m_buf; + this->m_buf.resize(destsize + (16 - (destsize % 16))); + memcpy(&this->m_buf[0], &temp[0], temp.size()); + } + +#ifdef USE_PRIVATEKEY + unsigned long keylen = 512; + unsigned char enc_key[512]; + + // import rsa key... + rsa_key key; + rsa_import(&PrivateKey[0], PrivateKey.size(), &key); + rsa_encrypt_key(reinterpret_cast(&zonekey), sizeof XZoneKey, enc_key, &keylen, 0, 0, 0, 0, h_sha512, &key); + rsa_free(&key); + + // move ff data + memcpy(&this->m_buf[512], &this->m_buf[0], this->m_buf.size() - 512); + + // copy RSA key + memcpy(&this->m_buf[0], enc_key, 512); +#else + // move ff data + memcpy(&this->m_buf[sizeof XZoneKey], &this->m_buf[0], this->m_buf.size() - sizeof XZoneKey); +#endif + + // encrypt fastfile data + symmetric_CBC cbc; + cbc_start(h_aes, zonekey.iv, zonekey.key, 32, 0, &cbc); +#ifdef USE_PRIVATEKEY + cbc_encrypt(&this->m_buf[512], &this->m_buf[512], this->m_buf.size() - 512, &cbc); +#else + cbc_encrypt(&this->m_buf[sizeof XZoneKey], &this->m_buf[sizeof XZoneKey], this->m_buf.size() - sizeof XZoneKey, + &cbc); +#endif + cbc_done(&cbc); + } +} diff --git a/src/ZoneUtils/Zone/ZoneBuffer.hpp b/src/ZoneUtils/Zone/ZoneBuffer.hpp new file mode 100644 index 0000000..abfdb1a --- /dev/null +++ b/src/ZoneUtils/Zone/ZoneBuffer.hpp @@ -0,0 +1,356 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include +#include +#include +#include +#include + +namespace ZoneTool +{ + class FileSystem; + + template + static void encrypt_data(T* _data, std::size_t _size) + { + auto fastfile = FileSystem::GetFastFile(); + + auto encryptionKey = static_cast(fastfile + ": This fastfile is property of the Plutonium Project." + ); + auto dataptr = reinterpret_cast(_data); + + auto keyPos = 0; + for (auto rounds = 0u; rounds < 4; rounds++) + { + for (auto i = 0u; i < _size; i++) + { + if (keyPos >= encryptionKey.size()) + { + keyPos = 0; + } + + dataptr[i] = 0xFF - dataptr[i]; + dataptr[i] = dataptr[i] ^ encryptionKey[keyPos]; + dataptr[i] = dataptr[i] ^ rounds; + keyPos++; + } + } + } + + template + static void decrypt_data(T* _data, std::size_t _size) + { + auto fastfile = static_cast(*reinterpret_cast(0x1294A00) + 4); + + auto encryptionKey = static_cast(fastfile + ": This fastfile is property of the Plutonium Project." + ); + auto dataptr = reinterpret_cast(_data); + + auto keyPos = 0; + for (int rounds = 3; rounds >= 0; rounds--) + { + for (auto i = 0u; i < _size; i++) + { + if (keyPos >= encryptionKey.size()) + { + keyPos = 0; + } + + dataptr[i] = dataptr[i] ^ rounds; + dataptr[i] = dataptr[i] ^ encryptionKey[keyPos]; + dataptr[i] = 0xFF - dataptr[i]; + keyPos++; + } + } + } + + struct XZoneKey + { + std::uint32_t version; + std::uint8_t pad1[16]; + std::uint8_t key[32]; + std::uint8_t pad2[16]; + std::uint8_t iv[16]; + }; + + class PakFile; + class ZoneBuffer + { + protected: + std::vector m_buf; + std::size_t m_pos; + std::size_t m_len; + + std::uint8_t m_stream; + + std::int32_t m_shiftsize; + std::size_t m_numstreams; + + std::vector m_zonestreams; + std::stack m_streamstack; + + std::vector m_scriptstrings; + + private: + class Offset + { + public: + union + { + struct + { + std::uint32_t value : 28; + std::uint32_t stream : 4; + }; + + std::uint32_t packed; + }; + + Offset() : packed(0) + { + }; + + Offset(const Offset& offset) : value(offset.value), stream(offset.stream) + { + }; + + Offset(std::uint32_t _stream, std::uint32_t _value) : value(_value), stream(_stream) + { + }; + + // The game needs it to be incremented + std::uint32_t get_packed_value() { return this->packed + 1; }; + }; + + void write_data(const void* _data, std::size_t size, std::size_t count); + void write_data(const void* _data, std::size_t size); + + std::unordered_map m_zonepointers; + + std::shared_ptr image_pak_; + std::vector stream_files_; + + public: + ZoneBuffer(); + ~ZoneBuffer(); + + PakFile* image_pak(); + + ZoneBuffer(std::vector data); + ZoneBuffer(std::size_t size); + + void add_image(const std::vector& pixels); + void alloc_image_pak(const std::uint32_t version); + + std::vector stream_files() + { + return stream_files_; + } + + std::uintptr_t get_stream_pos() + { + return this->m_zonestreams[this->m_stream]; + } + + template + T* get_zone_pointer() + { + return reinterpret_cast(((this->m_stream & 0x0F) << this->m_shiftsize) | ((m_zonestreams[m_stream] + 1) + & 0x0FFFFFFF)); + // auto pointer = Offset(this->m_stream, this->m_zonestreams[this->m_stream]).get_packed_value(); + // return reinterpret_cast(pointer); + } + + Offset get_zone_offset() + { + return Offset(this->m_stream, this->m_zonestreams[this->m_stream]); + } + + template + T* at() + { + return reinterpret_cast(m_buf.data() + m_pos); + } + + template + bool has_pointer(T* pointer) + { + if (m_zonepointers.find(reinterpret_cast(pointer)) != m_zonepointers.end()) + { + return true; + } + + return false; + } + + template + T* get_pointer(T* pointer) + { + if (m_zonepointers.find(reinterpret_cast(pointer)) != m_zonepointers.end()) + { + return reinterpret_cast(m_zonepointers[reinterpret_cast(pointer)]); + } + + return nullptr; + } + + template + void create_pointer(T* pointer) + { + m_zonepointers[reinterpret_cast(pointer)] = reinterpret_cast(this-> + get_zone_pointer()); + } + + // T is return type, should be a pointer always. + template + T* write_s(uint32_t alignment, T* _data, std::size_t _count = 1, std::size_t _size = sizeof(T), + T** outPointer = nullptr) + { + if (m_stream != 2) + { + // if data is a nullptr, return null. + if (!_data) + { + return nullptr; + } + + // if a zonepointer is found, do not rewrite the data. + if (m_zonepointers.find((uintptr_t)_data) != m_zonepointers.end()) + { + return reinterpret_cast(m_zonepointers[(uintptr_t)_data]); + } + } + + if (alignment > 0) + { + if (alignof(T) - 1 > alignment) + { + printf( + "[WARNING]: You might have an alignment issue somewhere fella! AlignOf(T) is higher than the specified amount!\n"); + + if (IsDebuggerPresent()) + { + DebugBreak(); + } + } + + if (alignment % 2 == 0) + { + printf( + "[WARNING]: You might have an alignment issue somewhere fella! Alignment is an even number (%i)!\n", + alignment); + } + } + + // align the buffer writing. + this->align(alignment); + + if (outPointer) + { + *outPointer = this->at(); + } + + if (m_stream != 2) + { + // insert zonepointers + for (std::size_t i = 0; i < _count; i++) + { + auto ptr = (uintptr_t)(this->get_zone_pointer()); + m_zonepointers[(uintptr_t)(void*)&_data[i]] = ptr; + + // printf("Offset 0x%08X, index %03u, size %04u -> zonepointer 0x%08X\n", &_data[i], i, _size, ptr); + + this->write(&_data[i]); + } + } + + // return -1 (data is following) + return reinterpret_cast(-1); + } + + template + T* write_p(T* _data, std::size_t _count = 1) + { + if (_count > 1) + { + for (std::size_t i = 0; i < _count; i++) + { + m_zonepointers[reinterpret_cast(_data + sizeof(T) * i)] = reinterpret_cast(this->get_zone_pointer() + (sizeof(T) * i)); + } + } + else + { + m_zonepointers[reinterpret_cast(_data)] = reinterpret_cast(this-> + get_zone_pointer()); + } + + return write(_data, _count); + } + + void init_streams(std::size_t num_streams); + + void write_stream(const void* _data, std::size_t size, std::size_t count); + void write_stream(const void* _data, size_t size); + + char* write_str(const std::string& _str); + void write_str_raw(const std::string& _str); + + template + T* write(T* _data, std::size_t count = 1) + { + auto dest = this->at(); + write_stream(_data, sizeof(T), count); + return dest; + } + + template + T* write_encrypted(T* _data, std::size_t count = 1) + { + auto dest = this->at(); + write_stream(_data, sizeof(T), count); + encrypt_data(dest, sizeof(T) * count); + return dest; + } + + std::uint8_t* buffer(); + std::size_t size(); + + void align(std::uint32_t alignment); + void inc_stream(const std::uint32_t stream, const std::size_t size); + void push_stream(std::uint32_t stream); + void pop_stream(); + + std::uint8_t current_stream(); + std::uint32_t current_stream_offset(); + std::uint32_t stream_offset(std::uint8_t stream); + + std::uint16_t write_scriptstring(std::string str); + const char* get_scriptstring(std::size_t idx); + std::size_t scriptstring_count(); + + template + static void clear_pointer(T* ptr) + { + *reinterpret_cast(ptr) = -1; + } + + void save(const std::string& filename); + void save_image_pak(const std::string& filename); + + static std::vector compress_zlib(const std::uint8_t* data, const std::size_t size, bool compress_blocks = false); + static std::vector compress_zlib(const std::vector& data, bool compress_blocks = false); + + std::vector compress_zstd(); + std::vector compress_zlib(bool compress_blocks = false); + void encrypt(); + }; +} diff --git a/src/ZoneUtils/Zone/ZoneMemory.hpp b/src/ZoneUtils/Zone/ZoneMemory.hpp new file mode 100644 index 0000000..01c1109 --- /dev/null +++ b/src/ZoneUtils/Zone/ZoneMemory.hpp @@ -0,0 +1,110 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + class ZoneMemory + { + private: + LPVOID memory_pool_; + std::size_t memory_size_; + std::size_t mem_pos_; + std::recursive_mutex mutex_; + + public: + ZoneMemory(const ZoneMemory& mem) : memory_pool_(mem.memory_pool_), memory_size_(mem.memory_size_), + mem_pos_(mem.mem_pos_) + { + } + + ZoneMemory(const std::size_t& size) + { + mem_pos_ = 0; + memory_size_ = size; + memory_pool_ = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + + if (!memory_pool_) + { + char buffer[256]; + _snprintf(buffer, sizeof buffer, + "ZoneTool just went out of memory, and has to be closed. Error code is %u (0x%08X).", + GetLastError(), GetLastError()); + + MessageBoxA(nullptr, buffer, "ZoneTool: Out of Memory", NULL); + std::exit(0); + } + } + + void Free() + { + std::lock_guard g(this->mutex_); + VirtualFree(memory_pool_, memory_size_, MEM_DECOMMIT | MEM_RELEASE); + + printf("ZoneTool memory statistics: used %ub of ram (%fmb).\n", mem_pos_, + static_cast(mem_pos_) / 1024 / 1024); + + memory_pool_ = nullptr; + memory_size_ = 0; + mem_pos_ = 0; + } + + ~ZoneMemory() + { + this->Free(); + } + + char* StrDup(const char* name) + { + std::lock_guard g(this->mutex_); + + // get string length + auto len = strlen(name) + 1; + auto pointer = this->ManualAlloc(len); + memcpy(pointer, name, len); + + // return pointer + return pointer; + } + + char* StrDup(const std::string& name) + { + std::lock_guard g(this->mutex_); + return this->StrDup(name.data()); + } + + template + T* Alloc(std::size_t count) + { + std::lock_guard g(this->mutex_); + return this->ManualAlloc(sizeof T, count); + } + + template + T* Alloc() + { + std::lock_guard g(this->mutex_); + return this->Alloc(1); + } + + template + T* ManualAlloc(std::size_t size, std::size_t count = 1) + { + std::lock_guard g(this->mutex_); + + // alloc pointer and zero it out + auto pointer = reinterpret_cast(memory_pool_) + mem_pos_; + memset(pointer, 0, size * count); + mem_pos_ += size * count; + + // return pointer + return reinterpret_cast(pointer); + } + }; +} diff --git a/src/ZoneUtils/ZoneUtils.hpp b/src/ZoneUtils/ZoneUtils.hpp new file mode 100644 index 0000000..9e2ed0c --- /dev/null +++ b/src/ZoneUtils/ZoneUtils.hpp @@ -0,0 +1,314 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Json.hpp" +using Json = nlohmann::json; + +#undef xor +#undef and + +#define MAX_ZONE_SIZE 1024 * 1024 * 200 + +namespace ZoneTool +{ +#pragma push(pack, 1) + struct XFileHeader + { + char header[8]; + std::int32_t version; + std::uint8_t allowOnlineUpdate; + std::uint32_t dwHighDateTime; + std::uint32_t dwLowDateTime; + }; + + template + struct XZoneMemory + { + std::uint32_t size; + std::uint32_t externalsize; + std::uint32_t streams[num_streams]; + }; + + struct XAssetStreamFile + { + unsigned int fileIndex; + unsigned int offset; + unsigned int offsetEnd; + }; + + struct XZoneInfo + { + const char* zone; + std::int32_t loadFlags; + std::int32_t unloadFlags; + }; + + struct ScriptStringList + { + int count; + const char** strings; + }; + + struct XAssetList + { + ScriptStringList stringList; + int assetCount; + void* assets; + }; + + enum class zone_target + { + pc, + xbox360, + ps3, + }; + enum class zone_target_version + { + iw3_alpha_253, + iw3_alpha_290, + iw3_alpha_328, + iw4_alpha_482, + iw4_alpha_491, + iw4_release, + iw4_release_console, + iw5_release, + max, + }; + + static std::string zone_target_version_str[8] = + { + "iw3_alpha_253", + "iw3_alpha_290", + "iw3_alpha_328", + "iw4_alpha_482", + "iw4_alpha_491", + "iw4_release", + "iw4_release_console", + "iw5_release", + }; + + static void endian_convert(void* data, const std::size_t size) + { + if (size <= 0) + { + return; + } + + // clone data + const auto data_clone = new char[size]; + memcpy(data_clone, data, size); + + // prepare pointers for magic + const auto data_clone_ptr = reinterpret_cast(data_clone); + const auto data_ptr = reinterpret_cast(data); + + for (auto i = 0u; i < size; i++) + { + data_ptr[i] = data_clone_ptr[size - i - 1]; + } + + delete[] data_clone; + } + template static void endian_convert(T* data) + { + return endian_convert((void*)data, sizeof T); + } +#pragma push(pop) +} + +#include "IPatch.hpp" +#include "CSV.hpp" +#include "Utils/Swizzle.hpp" +#include "Zone/ZoneMemory.hpp" +#include "Zone/ZoneBuffer.hpp" +#include "Utils/PakFile.hpp" +#include "Zone/Zone.hpp" +#include "IAsset.hpp" +#include "Utils/FileReader.hpp" +#include "Utils/FileSystem.hpp" +#include "Utils/Function.hpp" +#include "Utils/Memory.hpp" +#include "Utils/BinaryDumper.hpp" +#include "Utils/Expressions.hpp" +#include "Linker.hpp" + +#define MAKE_STRING(__data__) #__data__ + +#define ASSET_SIZE(__size__) \ + void _assert_size() \ + { \ + static_assert(sizeof(*this) == __size__, __FUNCTION__": Invalid struct size.\n"); \ + } + +#define ZONETOOL_INFO(__FMT__,...) \ + printf("[ INFO ][ " __FUNCTION__ " ]: " __FMT__ "\n", __VA_ARGS__) + +#define ZONETOOL_ERROR(__FMT__,...) \ + printf("[ ERROR ][ " __FUNCTION__ " ]: " __FMT__ "\n", __VA_ARGS__) + +#define ZONETOOL_FATAL(__FMT__,...) \ + printf("[ FATAL ][ " __FUNCTION__ " ]: " __FMT__ "\n", __VA_ARGS__); \ + MessageBoxA(nullptr, &va("Oops! An unexpected error occured. Error was: " __FMT__ "\n\nZoneTool must be restarted to resolve the error. Last error code reported by windows: 0x%08X (%u)", __VA_ARGS__, GetLastError(), GetLastError())[0], nullptr, 0); \ + std::exit(0) + +#define ZONETOOL_WARNING(__FMT__,...) \ + printf("[ WARNING ][ " __FUNCTION__ " ]: " __FMT__ "\n", __VA_ARGS__) + +/* + * Debugging purposes + */ + +// #define FILEPOINTERS_DEBUG +#ifdef FILEPOINTERS_DEBUG +#define START_LOG_STREAM \ + auto streamStartPos = buf->get_stream_pos(); + +#define END_LOG_STREAM \ + streamStartPos = buf->get_stream_pos() - streamStartPos; \ + ZONETOOL_INFO("Streamsize consumed for asset is %u", streamStartPos); +#else +#define START_LOG_STREAM +#define END_LOG_STREAM +#endif + +template +std::size_t Difference(const T1& t1, const T2& t2) +{ + return std::uintptr_t(t1) - std::uintptr_t(t2); +} + +template +std::string va(const std::string& format, Args ... args) +{ + size_t size = _snprintf(nullptr, 0, format.c_str(), args ...) + 1; + std::vector buf; + buf.resize(size); + _snprintf(buf.data(), size, format.c_str(), args ...); + return std::string(buf.data(), buf.data() + size - 1); +} + +static std::vector split(const std::string& rawInput, const std::vector& delims) +{ + std::vector strings; + + auto findFirstDelim = [](const std::string& input, const std::vector& delims) -> std::pair + { + auto firstDelim = 0; + auto firstDelimIndex = static_cast(-1); + auto index = 0u; + + for (auto& delim : delims) + { + if ((index = input.find(delim)) != std::string::npos) + { + if (firstDelimIndex == -1 || index < firstDelimIndex) + { + firstDelim = delim; + firstDelimIndex = index; + } + } + } + + return {firstDelim, firstDelimIndex}; + }; + + std::string input = rawInput; + + while (!input.empty()) + { + auto splitDelim = findFirstDelim(input, delims); + if (splitDelim.first != 0) + { + strings.push_back(input.substr(0, splitDelim.second)); + input = input.substr(splitDelim.second + 1); + } + else + { + break; + } + } + + strings.push_back(input); + return strings; +} + +static std::vector split(const std::string& str, char delimiter) +{ + return split(str, std::vector({delimiter})); +} + +template +static std::shared_ptr RegisterPatch() +{ + return std::make_shared(); +} + +class CSV +{ +public: + CSV(const CSV& csv) : columns(csv.columns) + { + } + + CSV(const std::string& table) + { + std::ifstream iFile(table); + + if (iFile.is_open()) + { + while (!iFile.eof()) + { + std::string row; + iFile >> row; + + auto cols = split(row, ','); + columns.push_back(cols); + } + + iFile.close(); + } + } + + std::string Get(const std::uint32_t& row, const std::uint32_t& column) + { + // if (columns.empty()) return ""; + // if (columns.size() <= row) return ""; + // if (columns[column].size() <= column) return ""; + + return columns[row][column]; + } + + std::uint32_t Rows() + { + return columns.size(); + } + + std::uint32_t Columns(std::uint32_t row = 0) + { + if (columns.empty()) return 0; + return columns[row].size(); + } + +private: + std::vector> columns; +}; diff --git a/src/ZoneUtils/stdafx.cpp b/src/ZoneUtils/stdafx.cpp new file mode 100644 index 0000000..a55cad6 --- /dev/null +++ b/src/ZoneUtils/stdafx.cpp @@ -0,0 +1,9 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" diff --git a/src/ZoneUtils/stdafx.hpp b/src/ZoneUtils/stdafx.hpp new file mode 100644 index 0000000..34b78b5 --- /dev/null +++ b/src/ZoneUtils/stdafx.hpp @@ -0,0 +1,23 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS + +#include + +#undef min +#undef max + +// Namespaces +// using namespace std::literals; +// using namespace std::literals::string_literals; + +#include "ZoneUtils.hpp" diff --git a/src/imgpak/main.cpp b/src/imgpak/main.cpp new file mode 100644 index 0000000..14ef386 --- /dev/null +++ b/src/imgpak/main.cpp @@ -0,0 +1,288 @@ +#include "stdafx.hpp" +#include +#include +#include + +#include + +#include + +using namespace std::literals; + +class file +{ +public: + file() + { + + } + + file(const std::string& filename) : filename_(filename) + { + auto fp = fopen(filename_.data(), "rb"); + if (fp) + { + const auto filesize = std::filesystem::file_size(filename_); + buffer_.resize(filesize); + fread(&buffer_[0], 1, filesize, fp); + fclose(fp); + } + } + + template T* get(const std::size_t offset) + { + return reinterpret_cast(&buffer_[offset]); + } + + void save() + { + auto fp = fopen(filename_.data(), "wb"); + if (fp) + { + fwrite(&buffer_[0], 1, buffer_.size(), fp); + fclose(fp); + } + } + + void destroy() + { + buffer_.clear(); + } + + std::string name() + { + return filename_; + } + +protected: + std::string filename_; + std::vector buffer_; + +}; + +void endian_convert_entries(ZoneTool::XAssetStreamFile* entries, std::uint32_t count) +{ + for (auto i = 0u; i < count; i++) + { + ZoneTool::endian_convert(&entries[i].fileIndex); + ZoneTool::endian_convert(&entries[i].offset); + ZoneTool::endian_convert(&entries[i].offsetEnd); + } +} + +void handle_entries(file& ff, std::vector& paks, ZoneTool::PakFile& img5) +{ + auto count = *ff.get(0x19); // get count + ZoneTool::endian_convert(&count); // endian convert count + + auto entries = ff.get(0x1D); // entry array + + // convert to little endian so we can use the data + endian_convert_entries(entries, count); + + // generate new image pak + for (auto i = 0u; i < count; i++) + { + if (entries[i].fileIndex == 0) continue; + if (entries[i].fileIndex == 5) + { + printf("FF %s seems to be converted already.\n", ff.name().data()); + break; + } + + // get pointer to correct image pak + auto cur_pak = paks[entries[i].fileIndex - 1]; + + // grab image data + auto image_data = cur_pak->get(entries[i].offset); + + // store image in new imgpak + auto new_image_data = img5.add_entry(image_data, entries[i].offsetEnd - entries[i].offset, true); + + // replace existing image data + entries[i].offset = new_image_data.first; + entries[i].offsetEnd = new_image_data.second; + entries[i].fileIndex = 5; + } + + // convert back to big endian + endian_convert_entries(entries, count); +} + +std::pair read_sortkey_and_techset(const std::filesystem::path& path) +{ + auto json_data = ""s; + auto file_size = std::filesystem::file_size(path); + json_data.resize(file_size); + + auto path_str = path.string(); + + auto fp = fopen(path_str.data(), "rb"); + if (fp) + { + fread(&json_data[0], file_size, 1, fp); + fclose(fp); + } + + Json json = Json::parse(json_data); + return { json["techniqueSet->name"].get(), json["sortKey"].get() }; +} + +int main(int argc, char** argv) +{ + file img1("imagefile1.pakm"); + file img2("imagefile2.pakm"); + file img3("imagefile3.pakm"); + file img4("imagefile4.pakm"); + + std::vector paks = { &img1, &img2, &img3, &img4 }; + + file ff("mp_shipment.ff"); + file loadscreen("mp_shipment_load.ff"); + + // alloc new image pak + ZoneTool::PakFile img5(269); + + handle_entries(ff, paks, img5); + handle_entries(loadscreen, paks, img5); + + // destroy paks + for (auto& pak : paks) + { + pak->destroy(); + } + + // save data + ff.save(); + loadscreen.save(); + + // save image pak + img5.save("imagefile5.pak"); + + return 0; + + //std::vector iw3_materials; + //std::vector iw5_materials; + + //std::vector> iw3_keys; + //std::vector> iw5_keys; + + //std::unordered_map> mapped_keys; + //std::map final_map; + + //// find materials + //for (auto& itr : std::filesystem::recursive_directory_iterator("E:\\SteamLibrary\\steamapps\\common\\Call of Duty 4\\dump")) + //{ + // if (std::filesystem::is_regular_file(itr) && strstr(itr.path().string().data(), "\\materials\\")) + // { + // iw3_materials.push_back(itr.path()); + // } + //} + //for (auto& itr : std::filesystem::recursive_directory_iterator("E:\\SteamLibrary\\steamapps\\common\\Call of Duty Modern Warfare 2\\dump")) + //{ + // if (std::filesystem::is_regular_file(itr) && strstr(itr.path().string().data(), "\\materials\\")) + // { + // iw5_materials.push_back(itr.path()); + // } + //} + + //printf("found a total of %u materials to analyse...\n", iw3_materials.size() + iw5_materials.size()); + //printf("parsing...\n"); + + //// map techsets & sort keys + //for (auto& iw3_mat : iw3_materials) + //{ + // iw3_keys.push_back(read_sortkey_and_techset(iw3_mat)); + //} + //for (auto& iw5_mat : iw5_materials) + //{ + // iw5_keys.push_back(read_sortkey_and_techset(iw5_mat)); + //} + + //printf("mapping...\n"); + + //// + //for (auto& iw5_key : iw5_keys) + //{ + // const auto itr = std::find_if(iw3_keys.begin(), iw3_keys.end(), [iw5_key](auto& iw3_key) + // { + // if (iw5_key.first == iw3_key.first) + // { + // return true; + // } + + // if (iw5_key.first.find("_sat") != std::string::npos) + // { + // if (iw5_key.first == iw3_key.first + "_sat") + // { + // printf("mapped iw5 %s to iw3 %s because it looks similar.\n", iw5_key.first.data(), iw3_key.first.data()); + // return true; + // } + // } + + // return false; + // }); + + // if (itr != iw3_keys.end()) + // { + // auto map_vec_itr = mapped_keys.find(itr->second); + // if (map_vec_itr != mapped_keys.end()) + // { + // map_vec_itr->second.push_back(iw5_key.second); + // } + // else + // { + // mapped_keys[itr->second].push_back(iw5_key.second); + // } + // } + //} + + //// go through all maps and extract the maps that occur the most + //for (auto& key_pair : mapped_keys) + //{ + // auto iw3_key = key_pair.first; + // auto iw5_key = 0u; + + // std::unordered_map key_count; + + // // find best match + // for (auto& iw5_keys : key_pair.second) + // { + // if (key_count.find(iw5_keys) != key_count.end()) + // { + // key_count[iw5_keys] += 1; + // } + // else + // { + // key_count[iw5_keys] = 1; + // } + // } + + // // yeet + // auto cur_count = 0u; + // for (auto& iw5_keys : key_count) + // { + // if (cur_count < iw5_keys.second) + // { + // iw5_key = iw5_keys.first; + // cur_count = iw5_keys.second; + // } + // } + + // // finally + // final_map[iw3_key] = iw5_key; + //} + + //printf("done!\n\n"); + + //printf("std::map mapped_keys {\n"); + //for (auto& key : final_map) + //{ + // printf("\t{ %u, %u },\n", key.first, key.second); + //} + //printf("};\n"); + + //while (true) {} + + //return 0; +} diff --git a/src/imgpak/stdafx.cpp b/src/imgpak/stdafx.cpp new file mode 100644 index 0000000..7845e6a --- /dev/null +++ b/src/imgpak/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.hpp" diff --git a/src/imgpak/stdafx.hpp b/src/imgpak/stdafx.hpp new file mode 100644 index 0000000..4ba412f --- /dev/null +++ b/src/imgpak/stdafx.hpp @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include diff --git a/tools/premake5.exe b/tools/premake5.exe new file mode 100644 index 0000000000000000000000000000000000000000..9048d51e09da5bde02e6337dcfaa603a2cebf292 GIT binary patch literal 1362432 zcmeFad3;pW89#g{nIuCPxB~_V7$s^{Gze%=fdoxRCIKZdAuxjo1h+WGr3f>M5+FE< z&Ez_YE!L%4rE2YBt95ILTLLs;GhwlSVioJsLkBg0g#;q=exK*uJ986)*x&p4{q?^3 zA#?9ppYxpOJllEBxy5r9ctd~XjXocoQ*W92 z!8tR%w=GUzxaeoMF1q2)^qX$D>#m>q(r>&aeUbmJ^xN)AFPL7Me&^3_zGYOue#sen z)4me##xY|Oe{KAmKOla{`yk>wLV3^Gj6-dOMRVcWG<(oOtmB^xsQD? z*C?}3iL>;z^iNTy_ED4v`*QLtHzg{H1CI-kM%bd~I7LA!|7ZLvjzJQ+NR3x=S8_or zOBMJx`os0QZc0%eqNnFllzqVON&mf?qWET^+tL(e;YpdXgl)*rjlD9QzXyD$`frr) zmL)!9+OENP2re2c5ryf$bhI*R(aksbZcvoMfxrWeJq5om!a?l6Tp(nWET(veBE2dF zgo59j@oV}oS5X>AEn2+jCZzFLF=oP{vgo98xuX`{a>vh5kjAb62Z|fNzLUyLLhAqj z-~WJt`q@v;RMu~e{x?fIP*UplXq~}XU0ShCE3(FK)UM2Ky{8DA!J@1RjlF04x7qD# zVS9G#y@x6+ZgG{CsAziv8>nneqc7DJxz|=IzWiCDf(NnsCFB-a0~;K{GF$MV|deyG6 z2QBrqI9l>-L@$9!cE>z92!h{@4noc@kE;$s;xa`s5GL`3 z4mr!CwTeGBD$2w?Vus>NKay)x?->SA7?N`gNC3w?ZC+Q{nzc78!T_P#VpKZ`q$2=n zOrV)S8iQK>O10f6I$MHO5!bM7`xFHUMKL%Ti8`Rj`R39Xs0=dT#k6v#ZV`F^G<`PC#q5@@7xS+vR4noYiIG?y)u7pbaDjwGwjCJ@rNs>--XztH4}>fDF|7r9H&Ug&;{ zqTOr9qy!Vyw_Mi1=G>s$rg-=6 z3kH_hmE}n&^6e~(!h}?2i~#TlzU&AbuxITkB|6etJryG=lCZAGwkZaRRAb!Q*dfVik6?j{lzzFt*GV@w^FY*t>wu;qb0CInc)&&mGNkZ z%goGhs18LPU?Bto#0nbN)C|IBd*D8+vfM%Bw#2IVl7LHE?t$@&FFul=hEgd-fhIfk znGF~!A1s^Cv(JmBeAU${xym&BE5bhy{wbFDxOmflagx57K2ks(A8Q9afz6TN+>BK5 z#ydR_H4=#0H-r(j4L^jaB}w5_AZm#{JOGG#;06q-1LI8zCjv8-87U#_`@yAFM%Vqn z1-BP~rGUPX!8BpX05FsCy}aREVN)7Hlj_`-aNz|9de z^0ei4aqMkHsZc5`0BHpZfm5SFt(A)<5st+kl+amIr`0zy&>7E5c!ahM=wEsV8V32v zCDkf^Jp*k}17ZzTQv!_+o*m%HqP5Z_QKvPsQ*c_5%$?FGx^;Tfk9C^ZqtgqbosR6; zX`VIKt15eqN3W~rf%GG8(?57NQ__2UM{j}?{5PO5a4tTZODAw8Jq zz{mnE?ZM@CZ3^+U$@T46v2_C`IQ+w^clZX@l~^XYEdKt1rno?3T=fqBmtcemXEp{l zT0qsQHI4pZ8pt1$$g(Yt{-VjSm2v@ETBxW!KUioFG^K!q)3WDgq^J)a{~;17o1fLF z)36td@?QK~h<^{_UvD&Q$S(Rd$%9h2_;zh{reku^EQGZAwwk@m&-G+Q4EigyM|?>( z@$$1I3#_3P_CVO;-(MEF$1a|bX^;=s8|;<(!}`&jHn7;h5{E*%#v$z^wfb(1R?AOm zAPJ)_wQFrHKn0$aOj}^Tqp8!%oMMZ(79h%5)AH>gu3)~s=7>KbxX^~hwM*L~j`={4 z*LG)*`HleCvreO1#L?*};7Wu%k} z7bqD7mI^MaYg?MaEnQ%0X_%>{O<9dKo7B}%f0(+?#Cu?qrL1Ptd?a#`KA%t-P2A8f ze)l(B2U4qFruk$fOU`75>BSE*%-Kod6fn%$_HaL} zzLL4X%O1#V^^TD3dMs$?_7sM5Q8iz>}FmkN+Qtaz<;pL`WIy% zzh?vKbvz2G0?23C;8$l23A-Vf1l{wYSSEEWycf}adFy|5#rY#i~-a^vTJw~FIz^4*M>sk zn>R2n%CaI0)V{%5vlq0UwIge9=o$+~m=c8kfw@T4`CqojriMPkI5dwvYg7DAC_XbY zMzTB~fnpiildNj!G0L9<)g{{+|5?orwfaxA43^py-;h9MM!Mp6fWwc>+LVj63JH=D zh9V>`TliBjIr3VGNzxEd=@P&%G5H0?sqNH7Dki$LOHw~~dj_OQDq}x8->w#HM!%`V zGtoVGM$PZh7%zNn?aF75Y$#ajYuE!UV8FiDfI~8&pMQ^?kCvccZ5tFQPSZgJ#0o?NZjWZ6+^))xW?Y*7zEQFPW}e`#%R(^QJz{}p4I zZqM9Oq9ILOL|h{}+p2SmWjer8t5=~cR%OH^k|5I=R<-&8B*N^1IoIyeu80h(3eIHj z0@k754w;>GM4QzW925R!g)K73N15Abk?!zYvyOx>ZkfaeYH%1-0;_f?JV+~q`Uf2J zAtMy-3IEYxi4~d2rIA6EsExX$2vC#zCxRMvc3lC(G6K;5a&8avS(kS!4@^Zzfr%R* zp9yf+fma_S{w$ppq1h?Azamtdf|cJKufPh!e;!Z~ve-au$+c5bOD^j;{xtFWmUzY6 z_-#H+zN=Z4YDu8hsiN5ZK|GlXAjF!8KS_u2u(~|K0iG3_nlbyiR6I;6S9cX;%&slS zSWuT|2~Dw7H~NpPNkdN1vL>B=@~mqz@LPc=Tltz1^k{LHL)#TK5TN=D{1WiFyaO); zyyD^3cuKCYRE+hAmC=_>kMKobF7b$4EIdZAl;HuaR;V`Fr-xEmWnd@0)-v<;DMy(w(JA6de!Haqpybowa%tnPM@7z1tyqdc4PPKX=S^y}pI~VD@*}2Zeo!so=ivJ zKaQqjR`Ct^G$t)_rT0S2bE%lOmC*t4+xO>SF4j(^UR&xM69(uukjpZmgnDg^gBjG# z212hz)a!LduZjLE(VH|!gI9`@KVwMK>|WxS(r{xJfY@{iLm7mZk-GgmXkhJZ?p7VH z{M4RvsOMK?&)^rA5My$SyC6;phnT)!CD5`M#G||yYZb}dotwH_pm&%1zx8)@&;Cw8 zO6vVx!2P|;{Uw?D`x<)X+B^Qw`fGcYF%=u%!?FI}0z-3o-{bxs-y9p?J=EWl|Fu80 zhPb<2Jx@a02`sV2`$=>ukW&R2i0hXk`6=2@z`bxyCS3c^G=7 z1XHCivF6r3#3LzR^^?aEn~y#QfU)_Q%ww4hFmf0SV?I9pgFfu;^D)9jtncoPG3>ek z6Xs(CS~&K-9LRN0NqIg-$oW8xt4%e|rcpfozs*N%3{C^E*ce+JLsqffldg77xEA8wGa~hHRwK6GimEs$X;bxYK@iN8jox*i4 z*6S=7jykis&JdYY)?ApPXzQgejXe$0g&^FA4uz15my4|*J+rdbDlGhLr~rpkS#^Uw zT~ey8(>`*0g3d0jo)j{S`Eqr5HCn@-_m$ec)~GJnl0<2q=(~0Ty5c9Avn%*2SKRAM zf?sFP)mBAU!gPHw&QIKY(ABJ?|-ag!^rUN=OP*UdoZ&bK`u8T5d`(FSR9a_QFi^^50A- zYRU7;pM}_(Rid6Z*`V&IfmK_LbwM0jbiN3*_H;%?bQi$WZfHv;f5$sHP(Rp#vyQ64 z3U{ojg(3}V9_jLK#Xz9eJ@OE1urBc_*-y}p;zXs9)cOeLdAuLWvNstSuSLtcjHKVn zvLt6=xvak)7K`+c@D`sz597PRe=+%L#;~HbkG4Qeuq%MwsAQ!|qU4{3~`HK-@o zXkVbW>T|UUuB2R})j%}YCf8dy9tK)uY)#`AQxxx)0LLvxLyaZ$SzK8w@y!Id!7Zg8 zvHEx1Rd6CvnD;qQ6DIAI$7!zM@r8BO7`8((ZWCLchfB679ZbXymq#9KwWiOlncIWl z6l6JTZJC~Q*9_?XsSWKSS8<7t!?NW9xHe8i{Qb4*DX2KU#JV^hwiuQ|oG3y+6%jzu zA5ehJqz5*nkOgQgGSh;P^S|#*n?p%RNayg*Uv zEO}7PJ2N-?>{XR^HQApqrMAHCfTQbzD(J=&{ln4R0GHPbP~74Kysc=@?9YTRq`%w| zZ55wk|01lGK*&Lt7kPLi`?uGvMBV$`Dz=QXfZ1`2(&KBI3_QyDfJ*>gjf2p)4f1|r zjTymXXwN7SR@kQSct*36j4;#Eyal}&Rd1uJO4Smp)sH>i$w`PAm`t92n60284A<_h z!f0Jy$Lw6i4VyY${0gQ(v%5i&ncb$=T!BmmVO>dg5dL+gG1KBX zGXO3Ez-12{OHd!^B$nCOckKf7AHPn8#QMql*LzeIlbf{zsJ{YAH7R?VP;nqSXO?uyvBXebIli zTNbC4Gd-z}B-LRf76#TgKn;B7$ZlEmp9UzM1jBER;zv%4`tYS-$Vl4^jO{<4o?E2* zs2c&q<{z7(WFx|nIN!n1`*p#DZd{{`{)iMpmF=e+e<)`5J8jaf2ZYoQWmG#U7jQc{m7 zU|&%JiB0-X)Dv<$0ufm1?*TFBRrN5L43|Dut%G&2ybd5MqSn-+3}Jejowvr0Hjx`u zeV8}U;Owr&&^?m2ST>84Rp!E|W!NY?3Kt9pV3CKQ!cFxr4qc{42tRoV_`tF^6 z$S(khHdiRvwSy8vyLQCpKz@PG+cURACk1{p-RU!2EEUAxzhZpS8o<)k=WijPIpwsU z0iUs%V`PmrrhPnBlheM6r@iAPB=jTnV#>4*?8J^NF;tW91CmjTd}y@DhV=Q|NXq&- z=S5YMjYh_hXxTT7q$F7uWN-{LEN8D~5>2FO4Gbz@)eHSsSg-=b?RraITK?j8Xo`>; zZcc>M`2B;>_CS`HuIFhWMhH?=-%MeB)1j-JSA2|)ZBRO~Jy#;SR$?5RFuFA0cXE?i z{iOQy_8)^J(pRyh&y@!<5}>RTd3s28x-pGukWe(8>G)B^%@CMC7f%@I$J+>~U1Gyy z0CNwKSSyr-Sq&R#EcemQL+^}intl9iJMU2WF5^Nlt`e4PDpl&KGuZkTM5+)k!ZLE` z(*J$tO}oVmn9ow3vPmL=!n7%p)yO->fzAZgxekIY)rDOfdTEglP}iIt9^w2CPZ9g{+EJ`SR3mp4vHhi~-UcepI|~` z!U+vuq6kRpN>aLe#b8t{kG<5Qz&aZtvZ@l5a`i50n}OO6t6w*k=^FvGC%~8`bVki6 zLNz+s0qek!-zyKK8T0=(WqY*an0fY4Gwa#)Ke2z@qbyp zG#}f6DohqoHOHlEXPbm9|B9#-r;DLXk^+gN~y!1rB4(R1FrnoT(8=1ol>=ztc?7qv#A% zlrB~+*QbbIl3ucq}$${Vdwn7~>qEmO3m@T7?a|284I3CT>Z z21S6KIoy@*T7yxEqv`4)97Vys6HrYF8x+-!!;%TIzV5xUFjB}3&$yZFmF99`A z5~QnK0O!c%p)Y`6LJH}VlwqEv+o6r|B#m&1Hg@-)Zj!DWb*f1kDJN+%I*v|~X*um< zacL|kOwxDK)!)lzd2}5exI|w^;YxjyXz}*8j+oSYSx0p?^B$4przL@9HV$n;h?rUp zg@q)XSOtF~b1-ZM5EVro{~#2|w&+$GI*F-xVh(CWz5o9NqX2xp?VPIB0#i=PN-8#F z!IJ|WR{u4;nK1-Su!^L0h`xAU8R7WURLM>UxU}6_o5Vo2(`b+EY}yjrIazCUiA$hb zpg$xI1@7DT-%Rg#Fk2W>Ln_5aG39`G8iZtH3rWNNaW;16NU}c(=rgE3>JpkUbt&Hv zN{k?wR#AY0=1!v_Pu*6_)~#d?dS-YJ5Co9K%kA#WaZqxbB35w&g5szLx#Q|>M-lPE zYIH;U{V8iG(;6zX5XPVtWHE2X$kpn10g}`(Td8+zn&X4DzFyhKBMuRU^}} zBHr79Zau>P3&ZfcCgh3dFeF5Wau`yp7zctm4c;zC+Y;}4X>O%vT1wu<+aKq5#uesc zAEfAdk6X;-Vu;nHcN5MG{j{=FntU!}5n-%=A}TCYVVqw9V@;ETI@(hQUZc6(9))wO`%eYT7Z7kg5v~F z_U_8RLTL#VMW72nLZ>w(W3n=xDS?hbn$w|9r_hEJ0HC_q;p7&3p)CSmTA?foJZCm( z%TogT+Nr&X&!PH?%QL5?W6*)7gq59FOi~r~ktrZca8pOaf*tT_fL^M%QZqyzHJ2cd zhK?27^e=1ty75Ki+9Hss!aziVOO2Q1(K=VKixh zbw|+N{yVVXG4OPkcP5%B6=_h{fj%S^xV&?by(^3{Yece$O)gDlkXdf zlH-7e$u4g?mwrGmJ-T769P!Uz>Sj+-+049Su6YWwZ@^AWNz@b>Cusy znFX$@cpcxVo`!gAXy_CzQ~55gJ>NfJQ9b9$c90?|1Pp> znRA}1@%v70y!@1nM+I%*0`eoV3&}h8A2b^SK&Nca9JoXBoM|b%+SO`cQVBVq2o`s^ zyw5RmUwjKw>3j~8xAp}j{3Ndn?UywA2M4~h_y*a<7CNuiaT`neko?l=aC-jRR};rL}M{4OD*2nA9a`0Z3NN2860Ya$>aIuTM$)v2Aq07~_g)cx+DCcT76oivZ zl^aH4h#18?MW-4CIge-{6kqz*xhRGBmi12tljJx&Rz$8C2#!3+VCy#V$vGD6BYB*`Kv{@V@0B`esn*6(M~Mu}Vx#;YWqDp3LqlqHCqxNP-&Mf z@Gn){9N3@WOQrL3pf-p0&?+)Nut`L%g%ju}6(N+>0jgRkM~tNuM5o3SfZ|0ZHAu_n zmyZ#|`u0pYQgs)%wnxmy+sfl?vRoidmfXzYH^&plbo-l@6KDFB_r2O zDQqtQl|+w{f76ERx2)uo^&1aNAzhng$MHCE1uRL4b)9}WHWJiJ`{i@_n8Es5gKeks zF~5Noin~-yM2G^pj>*qdt~pZ>u1H&><(O*8Pg!z};?p2966)M;kx)aqm7|ZuT1gMr z^9d2O(hm(5YSvfrI4Jz*GDK$*byQdxo_K)l@s7nRE)_?a>)VNOmWrp~HjYscjYlh% zyd+pmYA}4aP{x<1G<8_Bn^ug1leRt=IxnKTGMfVH60*0g7#(ujql&NA#Pb9ulZtjQ zv)KNNZ!&b=GNL#r-pKB&i}{NIn9DnvIq2C@yc!TOJQC@5|J=Z+c}Fzc>F{@-oS~4` zMs=D`eaU5NGsufkOToaxuYW@a-OpZ_P7ar0&MWx=SZx}RxYX9wRJfOaMrID!Q ztt8^{h;9EhO{7i%J@GYCws(dfXAw1*Rb%gW1(_9AG(HW$gI(}ivS%QZl7UN|gvw4< zkYezfsi3*qpn^$^zZ@Rmt4IYqy3aD`2#w zMqpw={ZtQ69Crx0`rqwvH#Hx-(Vr~h%ZGQ;N061 ztClMM3ReH4P#}B8zR2ERVQm3?MDV*?K!; zLW?h(AHR+SiT6W-P2B%|bh0rcG#{8_7JS3dD9nxPfJ_ev$0Z`8$%~a@Sq@4@12lms zPsQz_>3aM^QW3{5bo%t#XGUu`nQBLs9J${33IZIGD6&x~i^=7G796KIW<10t0&x6d zHcw+<_e+IKp~x{l$#*@J6hoz$P0Obs!!Ax#0aHB}j`?&ENEYE?SC(SUq_Nu|nAB2a zB?1sPg4_ce(x7vg<5U1Y)&@+f5=)*RDm1TZFyY zR78)HqnVDgus?zH+2!_7;KD*xnMi*?Gw@)*Hv%VK{70;~rl1MhHNhFUC!wlid7}g4 zz%jHKLK`RoCyd1Y*gaSZ~B~ zL}O^V4K_ndnY@7l z`VwS_xc@$)T$1C7beM?~QIMmmDl8rmhaESqm<&4{ItMypWJ%+d=a;ZGBR*S-TS~@= zXI7y2GP-U=+f7lTt>UXnfGrsV7Ud{jlx&fOFXTm8gKZ=8qI~fTywlwrFH=SM9N}^5 za+P`oX)fYcsHWhPIO^mUnIJ?gtokG@njXA^mJID*Xdipt??Od6WE$^f_X9vObON0b z9}dbU6=iSqr)!C|Q^(jMiB(rC{*O>hmKO;qOhefi>>KDj&!0&R9FzzPbVTSJaCV7y^#u4gN$r@aaNW95MqIO}xSq<7HRRw=@sA|LB*pr9;KN%2X&dYEbFKZ*F= zKw1gn_8S>W^4UWJvo90EI~)0^Gvk{W-d%~wHnBzebqE? z>Mk;N3h$+c?9>nzq{q{chGg_J5RCy1I%So56(r++3(^la*u7S+iwHBk*<@X{W--{c zr@>zS^nQbb^!liFo;bGvV~OD`7fzZOl~jvjGMx_fs`; zRTE>D{`OTf5#1)XGIyYpjVUx{@$x+e+9(E)4tA%Btv*l(3ShLr-q|3sa!{eCAwv$N zk5mA3L@*Klj;K`b{Pv(h;_ps7G-p0i*9g_P*by9{pJ&s0Cx=lQ*q7=M4AQ3t}_}micp>8^M?M`D3sSYS?r@j_;1in`N{Q`~q zf_Q6Y<%zTlkLb6Q$x~}4FBqp2f75jK{CP4aje zj+|$Z$FkI+n`1<7CaVZCwP8K?2Fj&V$u_NtijalG#k?bV*?zTOS4z46#=wEGtQ?T0 zseN`57UtVCQxWAH{~3pMLo)8tbzo?czB$@Xkw_r_2nGi8E>{c#a&2$B2epj*9MtL@ zu*5(I;!P*xVgD%n!Ie=jql~`CPk|P(;y)*=B$83{^t@atDITEKFddAZsbVo+`7sHY z%C&~iXQd+t2zqjzj-Z&fL3+tJEZ`hhiFY9*$hwjWIrhJVUc`m6t$2ST8S-H=t?8`Q zf+jYt6QHw%DF^4UAPZrvTQj^qZsoly*~$%Qg~i!F{dkyWx<_F@T%j^+9c4|nA-Zlr zpwr?{2w?lnyyx4n1Wm53!XLGO_M~nkJ+@cLK!t-x~b$ zU!SWC!*3yay9WPC@K1>Y%~|l*^k4Tk6o|1}exmh5`VX0MuDx*ESuL!((c28un)h%vW&OHE6 zT5BXr6jBB98ZRRwY&~giLGH}|?9ts?8(fkIQ@E5EBFWVFjTDfK8ygBxE4pn%e9DMN z){zf`#!?uaiPe}1uk>4%e`VOjz#oL0?n0MB$0h!@h{0(StpJmF9}djQEiyC=5jtHr zP7n?ooNkvUN;1xA7zT>&gh+tQxMM=h4FS!>yxH-8y49h(BFuv72=>6}oe#Np-VMeC z*Ee=LSRly#h%b-|6t{_C5Ac}SO~d!|LEh7SFCZX}p^@it#TTR(ErV&qt-@>!$uG ztc9fz6$lSeDszp2;2LVpyC5BK4z&imhO7^R4oG_-yK*p*6?XP9QApLLY^xYgz~gR+ z2*mS!v_(TQ%}NS+uiOQqDO&YJ5=uqS?cnO3R+0HSLsu`a?lI2pe6i(7ZeDbfrzRvsz=m&dDBlU5H2T!R+ z^e%@9QVS<+hQ3RC9u*XC(w=z~zE|j1ZkJebAOg39OI&z16-%+eS*R^b(T;|uS?UrC z*^hHDT6l(D_}E#o!njloloWp)=ZVZc`Jd!A_`JkP?Ii2%ycTPR)je_W8&DKnB~q8z zB1(P+97k=kOUWiPUGuUHHtBc6F}@ZzNz$aVZZZ7}BSfsu5#yd4CTcgJ84y zv$$Uu&Z9;mc%O!Mmw0|QW0nnEgCHstuX0naB38dZuW%w@& zx#F;fmd^zyIXO8ZzY3gDU?%U^M(_H?(ex7eZhmk_xJ3Dj%}=&GHClz&T`qE zJtsr08DT*hE!KaW)qOJl8*fa;@jLX%P^*stHvmqpIfw@m6v~1pVxCyZh?td-8T&d~ zAV*272%?u$upZOFodA``!5!i#s8o$^iT^N9;A{4xd}*6_{4YGN9z3apAdS+$bQJOx zG5W!ngD^781UDc$#vxP_S5pl{$9=EIb4MuOQW1fh?-`IHv23W^IDqFCA$lk08fE|? z1K9e#34FapS-9+!i1=e1x`hX>6?*k@+E70&7hXa&x^v+dr83cD#AUaeN6e@6q-K}c zW2hIPMY$wLQWd_1E1hIotH{PxHthGO!TgHsfJhR~e9{Bi8FblD$904f%BRZ?IDZ)) zO1%p~cW&_qD2rx_?1kRztz*Dl8*3=&mo+Qh6bn^nIKX!y_g%2cyyPLJki5vC>IFEi z*yt-DzZfTj<)yhQBB(&O-r;bEcmEF%%Y$lBunydr&IIDtjx%yb`>Z*0{qfC~(YU~a z&IW!%E97a@3Rz2Pt}az3l9568u2>;&qU%Wf%2Z?+84EZSv|C<86uymW*!31co)vM3 zPB#gyIi|WNBl^3QQG3|0!A8^&DcTblM+GjX^}{O*t3XpHBK#Fk zMG}Tn&(T}Su|dsYhH4F!62tAM*OVN!m)Wf})l6qUfgTT^X$c4iVg$-E*U!gIzJ?Tq zys5XQgZS4!U{l6tyg0%2o;8pfI6hq9pTQoqW zk1a7>-je==h_Fvda#DB&_Jkt7B4DnP*ePEWHr(TV2QA@hBT}#2;t)4z=kqV*0dBF- zl(8=Zx+OhRIk^MRN8&`|o*u#sV30h;;c9Y^69}c^dIEv?W6 zmr@AsDoV74ioY*$i6`NoRR7lKatECqVq*np`&#IU6qB5<@12m#+#^bFLDm>C6+dpo z>G!z}DnR5)xRE3N4(~pUtW*r=A*Ja<;wk85RA^25|BWjXa6jd4s1@?|YP!DwZw7{G z{_G1dfAK5jiG^kGxseNfF^N+eCk^t~Ff?h99x<1au{ilC$uE$sPx<%nBgzBjtadrg zv@z|wpjah0IQC3Y_LBqIDElmzE$v=vB$fIW^Af6|#=KMU?h@CQ0W0lDLLHd|yo<|` z8o*A+p5VL=+|V!tYqU?P*ubgQhMCmT3up;}TxYn&pY`O0lpI1bMUlbo#=$AK7`Y4w z@9;ln=X-H<1zwO@D!#!}sZjOIQmtL=MT+M9KCoe2cC#-H(;{Y}-BK|fe|gsyQ?Bf9 z#UCPoSMaO(yVerN&u-H8xYeSMFbK$^Qk>=&H)4_jfA)H{=4&q5fy~dKd~QeHpvf9oXGOx|?*b>fe3$UuPPYY4{NOuRzuakV;KT{v zpt`)c36tXd({cRXXRFJLpD-!jKM7;WT#rGkH8UevLi0LkNDs`XAywip4~ZAa26Bt? z6WpT4P64TjRG|b#L7!rZQ3dcS;VqPP1(Mui4E{E}3e1Nw6X)rr&!ohMkVs+*9?&-Q zKovO@r*2OS<56Xao?0m7?FTx?_^hb(V?)*ArT7 zJWBsq6sNMEKZcqJIrZ8;f?8y1RQw!m0x4#`RDly(?RVuOp5SA+w22-rmag%~+ zi3>6b=|8Z_iQ7ogtIJCt*N6=2Kq*r8h)+ZYb>WSe5f?!XEKC$caap^&R;cHDytyxsA3ahu8b6+** zTInosRTxph-iJ8%7v|iA_Y){L4#5-IG`r7JQ&-Im6`B@6g(j3y+Ar-$3_^ zAU*S7sJOiZ-#dxJ47zZKkc}u{Svz0D?CcCKYadq}@%4}RzZWYC(DE_}S8F`_S_)^S zZx#S5A!wm95D5Srz)TGR?8qQH+5&b*1D;%$n2Y+P?Y4&iC+Q``gVCcbvfyG{GxTe@z$AZze#1r!+5v-GdJC^*s3$jS+W?J7X2)I^jC31Te{hN6 zar*^DH1%rhMQ!9qzNa&!kLEM905LMifpL`aGMAe`#$^PepiPn(t!L{3tmnl`Xg!B& zx*+qg2zY&v)s(dtYx1%6fNu&7+^gi$^5}XNrJz#F;>Pln))QxjLR91w78KNnt6;W; z9-}hQlK+;9p}FnBIOD9$Bn(}K@=MO!D&9D(>yi?a#s%yoFR)pTwM{A`YptbX#LXt2 zvlmneEom%6S^2v{d6v2XdAJo37gS_!3_06fB?#5&&?b&U@yKqT z?#Q45svD0+awv5dh{bt8GL%rw0bHvF*Hu?!P%dTqzo%y|((bCT@MQxu5xZqcFMUA7 zCGO=w0F0k%Yg?MD6X-z^uOQS2+Uopn2Wa76gNCWk@fPu?Ly#amquoUD7P069no;0` z;|DUZ>cKT&=BB_KnF=I;2g;|Iy$?@v;egm_A3Tj+m&l`+vGfOb&ne#2 zk5LY%-zCZ82_`j>7Twir@ee_xVr90IVtW^wb_;u+ zTu&VyEGE6oSd<~|KNTg=-bm^jXa7Gz16XM}dM@w!6=WcY5c|(`TtWTYVv8*Q+XpRIWjxiTm4uoayiZ z-c^v1LC4Z7et=;DPOEs}>a(y?c0NlNTf+;o;a~lvhF6&y9&w6>w@Ufb@P=%7;!kS$ zN>js*Q#8C-Z@5u5{AJ2dLB*%vF)Cm;Bm}QBPJjI2lvSfh{~hX8{NNEM;KzX81Tsuj z$RhdGRZLjCJao@~kAurtAH;kcGCbq5onXU)%*9A&(z-`nQ$Gl!CSeWivji>Si;#)4apDlJLP0vI-#D0z z9rM9WqQ5MX*g^wE;36A0a!~9LptfV0(4TNAn^(Fh95?Xny2TLe_Qdod8nPVm)$>_) zQoyS30&SZZjrK8{qycdIC*2Yr?MM`$8)_2xl>U^(cmEWB$_3B@;cPLZ0XR6i4MSuB ztKanPb%8eAV)_Do0B&qp$^lS+N;=v9)L(QY{&_qSjH6u6T8O$SfzPaeS56LvVXF63YTiA4y#~}XZYj9yWlXSXkLNeY<8jGo8;ac_YLcMy zU%6h$rNk@6yAbdqDi!`>9H*P+sEFgZI(R5?V*y?GEy70_2kb)q8PA_QBjsI!nquNi z06^Z=nWijpA*Dv`$IED^EE!@DIuwlakz9U*6$mmx{^ftr4PRUMpHft&-6=>c2fI15 zkE;&_P?h%Y*uU9AMWXFzQv2lRm7$RpJzB0q%UtVGszpv-cCFI)F(6-SNK1zRmx+Ae zzKkhYfpiF`t~_$6_#OH5wVUNDqlX0=s^mo5Ddeixi6+l0X=~*8O(YnAVDk1S0}TDE z1hLH*@xmWb!i0|x_eW<^>dw7y2bnurgDOTRFV28K=zV)J-EMMAzfu zJFy-qz<^kbM|^o5)uCP6n@_VKXLx5rWf9}Xf%B1xA=hVUnymy}Vm6p%Fp)dN@e5*r zq#s<0HH$jzf6>pZ=!$up_|tkU$7&$kBOKVR!*cY9A@FqSG!1x-vmm%Xy9Dh?x`=(O zquV)@MB>y*0Av9Gaf^GdWcg^?;pFjyP{raqBtK%;ET*lL+Wzq`$w_+vl|vx~x6uR| z$97{_w3PzjW}2R^rS|CB^e{$b!7>a>_h?RVgu zMECFO(GiBLNVMnOQMqXxiWWZB}kB5w1TTe@C zowRSuwK3({m-^2xhbl3sTw54do_%+mUkxL{hvm^VFld&w*DQ|E!UFEvu#vo z6VJVTaTeexGyda633xFkuRcG z9;_?Kt)k{jAOZ}TB}{E`R8X5Z@T5}%VG91qp@tA5RFFa5rUc**0YD@G<4;*V3WS2X zhU9+Z1+-)0_g{X2H7(`eROCd3n;z7$L*HW$VN=#z?a$F_uTwSJcm^z2;y{<2=5+0N zC~x1<&u}CJ+kIz7&g)QX?tnujD#vhS1bMwh8kj{(A-ci4ett<5p|4X)mpRabFd@B4 z7v@h=^N<>Ah-9+#lKLv4g}hH<0HE`=BDlIS(3xI&4a@7D*BU{R@*5cE;CoxRzXA0@ z-mLLv;ZU}yBrhjPaD2xGdiX%@PxX3Ye zFL?i&ar76u;~4tT13uN6FainmoQ`_-$RIDE3{&wOq_3HYKPWtc3)3fUrc!nKkPQz* z?hBDINATko7I_oLU{Y{#Xbb0i@I_TC4FEF?pcTH2!Zg;HUGcAms9jOSR13Re8l0Um zTB9e)KkKcNFhA`#b)TOFENqFaAW?%rJrIMfpDC0q*N|FWWuCTkPcdz`nG4a}A@(MU zP{SUcO&!Exc6s*kn@Y_^2Ho-hnOPn~>*0T#Vz(oSU$) zW`oMy;%XzU5NVPQF#q*RIwt?{4O0KqCHc+M(4a2K@0ROQTHYtSGJIuGlXxNKN(zE= zJYFA>x)Gf)le;#y%t*QHeY2*!I*#xE(TJowVkPK~M^t;G0ezHy$IuYQ&=6uK#2Sd* z=#Kv0C<)Rj0&Lu$M1N$ZW8v{}q#T!c9p4rr{h`t4qrG9%hV4_do1xIxopS@CukRcY zM1mWiiP(Svdqf3;X>3fhJ*nvB}7xj76Kq)9DBXH&r4&#a+@P!R1vc#3hhw2ggSg_a{lux+blAqM>wX$9I z&ZeEh*ZFhVuu1hb*j}3mzo{<)k8pv8F@-o!yC6z12EQYEQOdgWyM++>%-A5_aqg}a z1OW*@2VuG77~g_%=|}M1otUV2Z5UOlcny!VPH<5QJ{qtoyoll{JmMMb_=9nJ#5>os z#Rrye_uzE0#cM}3aoeSo>fmj7kLW@h&>>KJEQt({NamB`R+o5z&-&RsphCO&N3>W! zu}MC!1&G8lbYTGdkaS=n(NhwZdL8JVqc-H4CLjOF%-BB%Z8p%V!{s%!D;q(3 z@<&cDWRrN{1iC(@7OXxTAkgr=rs_U(nIunCJ^>GaJPE}l%MGJ z($8E7Fi&y4G|SHe3)uWEmB%B*lX=l2QFMO;o#0j<#9f&Rcn-NX`b+_0;Hj|FhD<6@ zQ#ZIiVGtrEo!@IGa8nMxjucvfP`QNoCiT6|bwlEtut654+>N!)jZOP)4F}(hQ)-Jh zX(#YfTf8|~yoVpQ)SVqH{v2xPJc*Zeu}P|4NA8uD-XWZg~9m>b+=dR=c*j zcGgzZ(oWPCfAIFOx8fAM1dI387Ps=lm$k)hb^WndJglxs+d^%8kr4lp`rgL6r1*~z zg&D@?s7Q=u?eHQCpNml za&%MzTg_;*xC1_*+Tstnp?~0Su(-Xpco)C!o*ym@IX6Nncn>YO`+&I5JM9YLq4bG8MYU)DKWgU)?*&MiUbCw0!vLFb-2=SKCdO}Nve zoqp7}ivNM<4w-a7{s=v1JF@Xv$Ko$#@;*KJ10-)o@+UHRkDg48HR1-i=e#Z^@MqaL zm>in*9N6lNaZ8D${rMy0_RS2LII{Sd!PaF<@T#bBK9)bkW>_?PR(>km;8k z#V3h3a(;zssGJ^Y_BlDspvKvP^SX%L!Q|3|!S-E8QBiRo87YPhRoqFn6={JDX>g6! zyn=#MVHJMsd869jwemPUXvfteM0Rz+b^x<;iQBKHO8DY}f4SD8?Fzj~IRQZdUCG(~ z5zqw>NwXCR2dcXtYDH>HiO&V;`j)1(M5EvoP$Mq6l9(tFcovz2b6j{Lh(D{+_HMG}K z9!%19=|M2EVCJFebww7s^`%1t?~FrOgLdffi&}A4;Pd#yFVuAp#~9j6hq4GFzjH?W zFApt^pgFzIs&{_?D5IlY`v-T+j;2clXC3hpi1?bq-BKyB1(zX?MXk;Tnd$g!sK>nj z^v_~0gd22RU1Cm#uRP~`-;MQD`zL*j9ppF`a>EqJ9?Cm``!>(4%1IsVvj>FbzW-)L z_LGu;)P&}OREvTwm3!9mUD`kO1Dd`<_-c;U#Z3bCJ2Q7lu!98$P+zGWGpPqGgkz#_6k8H&n=5ZMHjqejxRSu1e_FHQUh8owh@G{W# zOFM{BJU3J4Cs}(v_>?y8o{fIQgk4RJ<(J7Q^;^ex_cfgroK$(0WN?l> zILD@6AsL*L0=^NwK9YQZ;0w_Aj1O8o--d4Bn8YmcP6o4%N1#l83^Og1ce-BcrdY8&%sMU|*CG;+pB!1SD=^+Qjrmw1-&RLtl4{^Kl z<_LlhXLXDVQ8l%C9Ga-Bo=7DTd6uKT*XR;sv7prV9-2rcwDuB_K?`gaMqtZs_7AOk zhcia21-pyTkp8Op*W#=34`Y8(sjKNk#lW#Rzb$8iS`7t>AV$(?e>&J@THqMPKZU4E zFdKXyRIbncv;h9o$WBYAAi5@X03OuJC-b6XZU-#;<4IUsDlb0H4=msa%gZY8BeyzHVL z$eDdsR9maCc^f(N5hK(0MfTyvqwCHtRNuq-+qapv$qzroBhD~D^e0yxFY_f-9UtX? zGuz*>XuEl>La4RpobP`cpe_w9xA@Nv6~O6GUk6TD?&-g(sxk{pF>xB+?mbKfE%|!! z;_p+HJxZ;<9&aE%K8J|Qsr@#BJ`P_k3<$-Sj%oLufj?S{T0=+Anw84^xGhQ1oE`gd zOidwUuqC@mt)`<)62E~Haq0t~6M>=mnmzQe)Pg7QVWnbpiKzSlWJcTt{+I-9Sx4AX zw3bz#7JYtx+LyU~OaDhjPOw!e0Nw)<;biEd4`!C(fU-QDoc(EKKHtLm zFIbiGwqd#9H3El(aQ+EGoY$i7Itt94)dfEFJ_(cO*|w!46NTA_m%l4z5X{`$4`AMY z+|2d1^kh;Dz@@(h&4ONG2xRS;xBoKWg8jS3{{OuF!#OEBR(j~RUS7G<0QTaFN|$(b zE0E6%hE}s&GLM~d$-K5k@2_5|1^&LcCgx0zc*IOJO*fL+>5gacVMRNLMFe@HpYJ~e zA2?jqZXAHybWI{8FTFKaUrs*?0dpRJ3D7Y(|9M&);t2F_I^~2BqI3^LI29DXLEH}X zkMJ#s=)k+2v5?Y%rW7T&qB2xo)=yD0EHjy`;p)$~DLX;qm zL8r+lar#U_peB+y3aD9ZYM4XcG_xFn{zD_aQ$;#NnD^Q4!1WtZB@rc zgFPRc>mQs)R9-hAnvlQ=ElwM7jTN2Uss;|>sct~^4wXJeqVoePe;yj}on3Wo6h01G zV#(gMcwpTyxWMcX^}ZxDd3>%vu@3iBhIdda;=d+T5{HV8x`A-O zB}akC0^fwNI&*S%i?3guD-zB4_S!&Gd~lvEJh8k*88xaoH*R#GF&=5*kp!V{Ae9_S zaM@}ddAZrGi-c)xHjFhdH{!Gbo(C=lQKR`(oj;j6NkF&$Z?VEn5i!7by)75?nLk-K zE~GO1##knOT{0cQQs3k(eE)!|C6=ZBzIC=qxmBIZ{736j>$X%KKi^0>A1St~ z&J6!3lpEn6XxK|!{30Xsq()HXeBW=VUcb88ma5|;e2-QgU+Q~EgHvk9@~g7vb@)cg zA!N71E0iD6?dKtN(;`)!qkZ_u@LXSVZDOI(U;H(-iTQ>2oX&r><8?{U`7X@qQ)$iV zw|sa`y6;S=C_{2G0CSNgXN1q5vlRX$X}o`w{h4ogT|8?)7y1X)O-J+Rf;KxZ^xGyh z!O%-A%y+}u+#=-tayX5`_j>>UGaeIb`xh34_v6Z+d0oI{%{vg}=I~~jeE>gNF4k6n*zox7lUjWx`o;FxK4}d?UZPd~0yVP0VjoHSIlaYhN`iHr zipf>P1V|s_`w#%uu;%IW5dUv)Ofnsk*Z}hzD8qgpb^0FJ^|YzC$^iR~5uBgdO16EQ zxO_biUjI&;>TI=4hyE_V`vycyBAF5Ycxm^r%0)xe0{4e}W>%rA}_-&@Z zc8{qYp*fc9&lV3tYgLs4EEoIxHY=mFow?1*#o@~g~-aFytRyPc22Lor;L`=?yxulTnB|DqSl(Zm1rUo415=dWzDS;T{Owv9u@b{uNN z1yc{-1nOF7#g)4|lPM>)XL!8MuPAQ8I+ER1};K#1Z$so*AR= zSQ>>?sk8TwG7er|^6+LsYan05*N!md>VP_qqBNIeX~PbQ`k~uTtV}po0)N&(Q@l3VU_q{zaraohmVi z_J-#^quqD$iOk>kIpvQw<-aWR-;w!~xxQ-3r{fZ+|Fq1n<@(=Yv&Lxu4OzcZ<~MTv zHdDT`gX_!EWjNNR$S8zMC|-0=Cnt+ zLXYcmw$)P+zAk4EdZz^==5C-#mpYMh89iGY(j;nDp1#H9u@=8HT1+;z_!qgD8#?4* zN1wjMk+BxX?bBhbUmt^UuHNEQWMb6+{N%Kw{`havQCAu*mYZ5k)?3t1>}jWOu{PG? zLq>}!rWWg&LtDL1AX9`>;nCP8mC5tG&r#AIBoUrp#tW1P=e8btrtS0~B*Z{?)d1m9 zh%{p=FP9*=Mddo!!_f<)8jZ5In9BB%Wluiar28MBUk4711VbTR^PqTYIPlpsp4#|k z>cnlxguFA3RnL11FmV|6O;n9e3t@XV-G!slgXqXKLLHXihg|lwy#i^1qYmodDCi2W z0NhLKj9L4X5h#&n2mOnenqni(%%o>)!!`^J1hMsFn(giZy*=dbrZ=&N15QRqMm{(j z#rFi3+MK?`tj4Sz;XzQ{wZ71K#Uh|6YsYw(O?_+~TEsRFzW!rlE@}y@t`f<~N8Ul* z>+E&~S~I(t#>%04k*t4!z=p%aj&K@|cw?srKF?4ELk}|`3o&M?7_@^fBNvwsMmoNJ zU;mZ$^LB0hX zcup2|mPZd8nwpVO;tEw~j6_Mqn#XS71A#e0IMj`g5~M2oKZQ``0G9Te*t(ER0}Frr z5<8zc{Ka2U3TJ)jEID6OON!jQU7Xm>i6LuJHM&>wdQ|h;p`M7(2Qu3SuZvy0K^pJS zQ?_C1Jw^5&%G*Yh3qqnOdk0T=W??$MaBa+R_Kh@CWSrJ~fl`u1d_B})OvxOsSG^Hg z#@X5@|8c4@nEGG+YA>TNJ7rB{^z%^7IHEJ)ZZuKoFtR2gOF}mp(8X3mkK?D|P2FnQ zqqPQ7EfOfC@nQNXnjsyCaf{lwnLLg0t^E)Mdld<$@xAd17c$=zfcTwL){F|cN~*U- zJoW{;#HCaHjlpItgtk8lUhb`l^Wah~|1n^|E%r!v#X{Q9l>{Ap-=eEIrVWbs3Q@1#(+%@QcRJY zAKf3SA4aER=#Xn-A#`g)LHvJcI~(|@sx#qFGLy*w19y-CqDCDd+Sq6Xf-P}CXA&|A z-w8;9ia?d7EK-YM!iR(;IEiw(jCy9oBfB-kWCO9HltS`|NP zZyZ{qr6C|C@BcaX&SU~~_1(8`e!t26I-k#Z&U2pge4Ue5ONlBL%@SBUqW3 zN^;R-#8ZUeYm}+6R;4rm{z;50$ZKU@Mwp)jd{ zsVT%8WVGn@1AJ24zNQZTfZ-RmJ~sgqW;6Xyz;;@+sQGo7-+YC+&k-=j+A?B2j*3b- z^Y<`9Er}N}V$EuhU~u}kOw9+{d%qQXK9^EO)q1t!h0NRm6v2p$DU?~!K0-wzT>KmM zQlDNsa?N^jSDG&|}XW9gA4MR*pYmCQH(@|B}&u)bM z)XhdGM$w+S%do`1L~NqVGdK;dJrgY|x{$ZiebN1qxND=~Z;|{AVTT0c*vG4tahH0x zA{pON?N^D%p-ydwrPO7scZ?NyWV|HX#?vI({-*HS7v&97=>PTaLha9wEAqLI&UZKY zlJtAR8mdX7-x0nUB`(FEm*4`+O%4pYzuGz!smlBp)DN-eIYe~$?kC+I0Gal;SmPD1 z#~H?5=C>IbhS!Na4t_z7eA+)Gef;r3rv9i(ChcY138MV>y?*jvlS1lLOdImvxBVjW zcCqNQVq(aVKzVbnJTm8n=HrRd%O|dl?y!u z4S$;oewJXYB!K)lG;jpG6yffO4`k`)uunx^>&@#*s0G6ir8S1$*v|1u&CQuHNnZCV zb(SkfFst~t>~>R@?qZNhV8K_c@#!`@E|Le0$*q!y{wPltS^vq?kG{N){@t(Syh(e! zf&-ZU7iVBCk~G#`dNz2Vc7DMD=Rv}_xOKA@J{adj(@=gXXgYo{o{=`@n?A>%h{#Eu-TO3 zwbpIdb^zUHG7sEQ|FycldM8l`L!8x-6u%6bi8} z2~FE?ESZhZi&0WhWo#(N<7JPB(yNWF^Yx`C3G#>?Kq6bkuPGg9I_;}1jI|CBG9&Jf z8Knd9@-wm4EIC8yxMp~bLa)5!o}0pvhcS@Zxd4})lTD}n8#JS{uw!z2VQ1FYwmf)8 z;-;q4`5Q*dyTbOYZzo>5FEP34^vn(8j1J?_sP@SRCU=f%yCJXS?e&~lsuJDs$y>g8 zlh`vYg>(-LQq3D$TYHw%Z#b8ppc-d8U(U7Z+fEqgjJlI;hcanD9it0Prz^AvX9;bZ z7jm{43ry*9U7vI}9Kut`*{*LJpu@(U#2cM$l=eA0I>O8FI4!3)&Q3i%-zYud+_zE> zSENx1$li~dkkUO4F=%R7HY2J{LlI)O*pGGZtH+Hzs+%t)Z&q_-RNaP!|GQV1)k6>C z&ACGlqs>`E4_W3E9(GEe);FEzWcji%WE|f9Ccfqlqpi*Kjq2fY+_(J=UN}azwY}>x z7S3)v?veNCa*e~Jv65dbWqB&)?2kw}Kb5ll{FLu{(is9lOstV)G_TQ^^wi^ee zr2}nmNKNAaJv-P`4kBp>c5?&sjP;onaUeLuUSc;JHXr-#1( zWk>Ou3%-9i^!?n9;)`mv~|}ofs17| zV#yT_n(uyV_`Wr%B0Xq6{lG=(ql%xQR*!0C%1 zZW~%U4w;z2FMJR!UDyB%tuyZ3P( z*u9o^H5E;XUWPR9hkwjMG`sOn^1u26Wkbbk99Z*|VT@QM}~|CJra5?3gOx!mhIJ8s!yTWqwzILhPn<;|q=%J!k+9nd|P!lw^!> ztFle2%c)xPYXRIo;_LAt?NsB6=|=gK&Qg>+kbnih)R&_7ft^N}(8%dNTJzJ~At2@W z;ZoCJ!MMxRw;dl`A7HE16+3~<7hL((rc6a+0%D4sMFyHM=j)j=%$LeCbTmH4Yd*D+ zM2F0)x%9rl^k17}o=GT9A1B+ER+f*tD`TzR=xXtPhVx+s7b4*>->W0X54bnVXB!pe zL}v`P*WMm~U}W6&Gf7ZE*1`4j&B>Wb*xVp1D@BbSR*Rh4$amx+=iHDH7OVNmMg{}-Vr+}o5tZaub-H1m zQ^`ea?A=*Dn#L&Z;%%B*c%%x5zw#9sVD?Q>pHV)8cWjfgzpDnFxV*oPw{(FoYfOf` z!h8&6+fMZ&9+%g!RIq&)JL4t(Gx=`3&aq!RtrNejf(nShtz6vlz9|F7jw(Jk!)7Us z&Vs|~=cyL7hnxc{dA0fHZ?FUG5qv#T_bYVICeWPn_l;z=O^OC#tavZu_C4 z9UaZHLTXi%>^kyr#;#TtE%x*!^i_&+?+t2FE4mup6zPy<4M)FX{K^{$rw z4VM_zepZZcZs3EJ-KQ@-kr z%xFHErD#a6942XZE>HEg&i?Q%VJK%mcvg4qG@OIwvj;bL%?ZC(rFNEhK@WVm-PW^F z`db_be)}~X!;e8TgWGx<@2SrU-u253%b_d1d(I@Qs93W{X!9p%uQjU4=B(+H_nPX5 zf2beHUaDO0zkEJLhkkXLpPZYCoSbNOYN@gJTdZHo&#lV*Fq`4A8G&16w^e1Dys}6N zK8pw_)-X?4>{b#J#IoX-uplgWPWq@KHYXRop{4(K_opxYFY;vX_l&R~yW-JpZ@Qem zV5N7%^+x-??Prc*xY#g4URH7{^S-)lj*yGB=I`4pAK1J4Vh?yG)VcQ@W23&$!0fHq z9qeipZLafx(MB@fbG78l9Q>y2f$)}Gwf%IcSaUSqAc~ImEHqJ^DCF#kyGHDl_qcp4 zY74e&3wnZ_7~42*#a+GiMMy=^mC6EDF@t+giRLhCc)Mn~I$OQq##Ag5=@Rf1VYZwg zFJ`W}V&>XrtW*b^b~I!wwkRqzbj`v%gzMK&3vW z>O)>YiXv&&`#`2;|K?A}t9{O+MHSu+0a;cs=5LlM2&a&=p^XvTk?*yCh%fRv53SV8 zy-E<}2J+y@w5f(G%?4 zaAmSS&BrO%Bek)av38#}b_7-#!NaxhQl)V;cxc1KU79ab^Nr95bKb`Hkr;9yhIu#G zu_4#;KR|@aA&D|5t$ZIt{lSjfch$+{FrSMRC@Yh4IY;i>5ze|09R%Og?Cqrb^mBBFncAhb7l@jfB(^p#)GP5r@7f0;OCMXwpvK>8 zFaZ#w^3keK^0%|@OU8i-(GjtM8X^F5WR7CL)5{o2|0UAn?Y7x7jwRb@vlHrzTxM=E6*FhUh2G&1hbgKT~$0`l;rdNK5GsQhVmdyZ?I4lPZRpu`t6X()YWvi)U} zIP+qO?f!Gh;{u{49cEdO!5xrxKF`DUL7{Wei53)n$oC8Pn+F#uap_7qLWqA>eO0kD z-ss5KnbFZKD1BnA*qB6^?;$nal`7X8FF%ecMrDQU5*~U|QGAhjo;QScc~Ssp?A~HG z=@9rfP40W_HZ6dLOWR6~QhyBB!t2GgaDup%7A{t5i09{9&7^qi#s8jr-iu7TBcu?~ z`LOZAK8Ss`2s6CKK_x8YG-0##DTb&E4#zL{61toJem&yagZjsTL$8VgVqX4TJJGe4 zIfn|^$ExKnIH*TBz$nAO&(9JIsueP&7qG^h{tv^KnXH8hUq*=~iNcx0SPNtBpDn`4 zI*&3U;f!-=#;Y#0SU?lxnfjR2p3@pH!I&>e!52L*edx3)YrduIXIENT?9`Bn0BWH>XOX@8ma6dtOt)~@gLggTc2sfnGgf$y*iPoBn=gpa_pIN3fe zHWX}{%B5R5a#0vNRQ@HT2UE(eiEe*Rqh}vT&{naVpuedz-pgN6{hi4rtz5g0Q+fMI zemC*MV*X>D#V+k(IpatLJAxkPD5MiL>pQqy>Ln5LLd505*vWwlBvk!jFW^;2HpofY zLqX;^vmw1G1!2-xlFBm3CT?2N&+^wlWKMd)kP5SiTM$|d83**6wcY4fxIJ+!fm&(&t`qlFnL+O{r(3 zf0eSJ5#Zk^99*|KR|rP6=t~inN?dN#mF7*7sbhFklKmw+@yjf#)LmhYyjcD}8&>|b zo>*O?eyEy)S2ZfYu`H(mJ9Qj+Vx zh@u*zLrjq!bKQEe_qr9{U6B3woqd|ml|#hS%Y3okjJgxNQ-MWl7-pHnq8%24*iptI zILf8FU;0|x{+>WH?M-BgQ{NR)5dgL?@yQlW|Vynh-J|gp)>ys2kmG!8T z&VvZ)RXhYSnnT4_ZT{mAC=dHrR9Be59wA8I1Y;CuT{yo=<9iDC&3zc&0DDCY%h))AFDW9N!qz1Hu&0B04pkwWt;XTzyqcKve&;K~ z`k!acJI*rarD4gQa8NjMJuwkk97=C74OUAg@Ix)>6H*M#dQ|FT`uCVmvRkEz=~F3* zzpQUE1(cXs7WvyH(_Q{lI|b%BhPH%a&0it&7pT*78|v7OC84hD4AR=FASejw(U-X( zE3z&R0iHex;Eh|;yD9|n7G7y)WHHJ{99PqH*I&= zEu7hK=p~0$#>0x2LXbTOgt_&5^oR+Cblav6D-8{Vf9p#OM-OrXhl&eYCV1J^9~uZn zrx;i5oR@_dmFLnyS2SRR`uam`i6)}JN5Yr);f|6lBV3`EqI8J zLQsBmyhI|YCuxf_Nn1)3Z82Viw(vRDdR#HsEr<`HEx_Wqonr{UW&RD(m!vV=J%2w$ zU$Q@nzNF^vH~$DUEdnzcV`!+Hw4|?spzy|_3iI5|^k%@cum(eY0_HU-XhjbUK`P(4 zb>~X8YrHZEqw&C~>8%fUtW*#x%hk6eA+%C9z4ULGyS!Rohv?BcV!2xMX!^&eNBbqy zF){G5{UC%Otz-@xL8DuU(hRgoO1PXF?eI_^KKb!_AO~e@zO->xbZs;{q@h7pq|8|0 z=ZNeyYG%AJe5k)3Oowrb@gF|k>GZbu1pUJ*?pW--0F>QnbYX7eHNQl!)K0HXQ>;Fe zd8L}YVj3e&g;z729U+u+3%&heh1lbE-l2x;S|e5gGY%Q!l(|r;pNk*Wv#&z@VT9(J z--UZ(@zH!`s?q z_*iMC@nQw5smp3&8fgDEtpuLd;%cv8XWyl1tYF7t4Ff`%`U%6avCo({L}ITI5_>5Q z7W$7BiB)?{sG&eXq<&6)z0g}OIqdyzF-GmI%<6A#EII!EpO+%meWUQV|4; z%Ece395JlS?ls45UV>5oxJ!ow#}g-|WW=)G+4AUem*JDLMc{G`r86UvRj*5mJ5uw{ zpE|M&jhP&FC0py~Ops@wpdlcF&nj=&D9yDTnh8g}3mQ50D;C}^OX;Nzc^*{>vV3kGB zVkd566!uR4werF7KHGCy7@ZWy3WH#fE3s|#azzp3ml0}dtsdOS8NOY3iscFHX6G$u zMFDo=rgS|NWpxF*X~-KrcW|Xjbq(29+2_dn`S$fT`?}1&uClMw?JLVNWHN9gaNNkw z)5~f!1?wDfGV~g54v42cu0ia)bM?SFg+eG^wU;j|4AfI}e+Uw<(Y>MLTc zo?uVi1X-)LAdVhwT%w3bmU-y&!kZfVti6~J37Jm|JVNio-m2K(v$->pY|tM6`xHa1!%(*p|n(optPtcz2YJsvFNyweiwXV(wXYVi4=N=Sh2( zgKZ0sFtp~Mze95+2&uKHQzTN&b~T?pFnb=0BemwYWo1=3Xlx*+{0QygPoOCr%xlU0 zBS9G=w_0JsAl(ZItmRzj$qtg&q}53`Y3~dg0&HQ@;ckBUgjh z+2Cn=Z{)D&?Y|#HN@Ks;8M)P{628-Un7+|%sR0UiZTvy0Wp67&OVM?8;rj^U9Mr2k z<~=L`mZHM$)8dWPtAMEFHBYa%;=}hB-|Q zv$E?-Z&m3NY~rU^X@ZhlKF||W*sohd?GiLX1sTX1D zCBio&uL!yHOt~U%v@|$L$)W`n)qmu5K7=fY>iADz^o->8N#+l6(>)?dud0rH`1?(@ z#{Ir{NlwMu?7}1F3S22N8-C%lIxDTttXUu#{!~d9%p!ax5;xe38}3YOb2kNpMt~ue||0ESczO zca(!9udS;`mQ0Y*`l!LI3%i3ojcd`v?U5019%gJ;DuItWrd4Fi3w*KD8TDnzaW{K| zi@c4qjM+Q9gvcoi8MC{H8Yxu9yJuz*W`w>>wk*kvsN15jI55`fZ$%l90$mcnM02zh zYiNr}DtrZVmeRB4eEQ5JJZBOQ}h3I1rCUb64 zd7$B1BQ%p`9qcTsINO*L^li(EB{HH{8FgpF82kkn2H;TY-(u7-RS$0V*G=VZ;>NgZ zj+i03w#$5f(^$}l^}xz_WgiQh^XQ5@^|8XyBNA!zB|4c+hNGa31_wLpuOP(Zr6eT1 zN{i>|ZM0S&tJ*uvzD9sr-PGX8z)7TvDBb}l4EIPqvtVIBpPLJcFM*|B2%9AA3o~Fa zQ40%0f0sht;hh55X}oJJn;9?h)a;6FKI>@wEyhy~S+RFBqF&Xx?s}74Go>>#V|W-R zrW%V^#Y;I^lg;@dJ7%N#Sus$uOvM;LFscgbkYPz_eA2!x#lr##afas%_@# zzv~w#e_Jh}HRfAlHDT8-S`Cp{5JbwNxP7V^Vp*ht2lGJ%AZu5ZoFEFnaujNr3Iid7 z5>_yIW`R$wWWtSJ)dWIGh~!x7k7EPZX%A0j$FJ>f>KYK0TFZZMfh>NeX{+nn=u~5g z*C+XDp1^*pc%FpBdN6FMWl)jaN9$G^@<4)Tye zCbJ&N6^5$HR~cWJXFiR@23~+5trF7^!cQ|>?jD@xT@ha5DS~JI2v9}9Pl7pojQD0}K$>O=EiJtVH1Y}PI zw3c_dGVLW5v+5~ku&h?-qvwe2NCN5dACb$9xK~6Oj>L_7d*qTYPVqBr!{Z@8ZR znsI=3y?yr`zEXC>9U_d zwoAFWXFuq|jXA32+?0}U!jh*vq51(UrwcQV$P%UlwH5cP0`q7q{sO@eeqHyq-7JFIn4#2%EwBTp5jGk`m zo!Iu?7=JHdtl~!gm=V?8)h2QAA)s1rOpJ3PNP)nX#2g(x%GOZ%#)|hbk$9V>sf&M$fswpx|f8E>0R*s z2YxQJHqjxDrK40Y#SZ-lMB zHL$Lv<@&K*5*fC?A@DubrWUFKo&+w#^4>5!?~}AgS^+>Rkx`t3EL{{J(kYS-XV&&S zLd~>X3OM;;$T9wG$l>GX=64f6hwHEXWvCCCnOvMTyS_}?-~A=z_&&eg++Pk-#rQcg zGP1I;LdxRLl|DbcMw`t}$wp?GAI7=#WPgb;&T9MTWWM4Kz>#RRZidp2Evb=NG$Xn$ z%!ysWL-ilvcw)@+8K(-~Z##!K!RAJswJlDyZ`%f=Lv~Yb#I9b0(GE-*2J{tG1(k|- zHeV6G1A{VLdZOj*?L)F zFsiI`Lci8Hkg=OjGqs18ZmoYOI9uTZ{PklEM0me_5?jfKxCd%Rm+|7x%Wl3DZ|hg^wl2=NIc$d$Xv7Thm z&e^LUGVfLc2QHnKXwt}dk9LN<07Hn~HA{XXN*wF}jg3T5W1pXAehCAinv9i5=NNWq z=kM-Z$=+MwrQ&g`uv}}JSR_d8Ft++^SvV5_=-*KEa1%vq&0pt#ydneIQv7+wGdq<- ziPu%)=$7qeUMWbA1aPN~tjd?1N&EAKfzH_(irZqw%#vjF2_v#9zHmAKj?GJ#|KJDn z6+g&y>t@#tui-T_fnQoYYqOT=m|~&e+SWRDlxKB>Tv;OS^GLuCJre2+{Iz#S3 zECW@O5b(*n&X8{pqQB0NU!L@EerI?}DR+QB)BN*VZlZFE6VT1jQDwm9OeyEHxpTdY zdyd5zT_-FMpCm9A64j&J=5t3O+*xeV+WQoq;H+T9Y7y2*!g7N!KVVwX1%#Eu8&AWj z+|O3{<2HY7rtoD_yL7>P=ZjLO@4PlLc7LUf9TH?k6x_7&*(TL~X{Lg_$K1nITRZmI z1Ih??Q5#ZI%|wEidXA%LxuZww_dv%Zqc*Lcu*4P%_BuD zm3k1yIM(Lbep*hDs(s4;Wur)r&7ow|NimkbdZ{F_dn2r*Y6@(`Tt8LoW_V zH5vNc)!WaRWUN1k-;OW~B#y5ym%T1Lz+nu(ITX-vL#i&JonzSRc#w?u$eX%9GM{6O zs~k}cJUM87{!YNfW8M2G)gT;eIa02pY^JlL>=uXl^*hwq#j8Wc!L@^?k$haO9_x8D z?nH0pGPVYc4LJs=*7p0%>Z-vFO1%b_pbTSUq?LAFSK^H|1RT-H2+hQ}&2KK0#Wu1E zdR7%+qRS}|A7W_(Ns^eu?0n;qvlW0QK#*#~G1xNcD1=H|TAvhjM71S%JFrXG8t z^{$;IGdm(KPo8r}Bn##fblea^9HviU?w6CguvPX%`U+oXB)}sQ5k&-0y<@tyO5b?B z9+@I~cs;VZMqjuNjgTH$qeqr?M4}mbByUH=gXY&L8Kfl37Pad(x!zO7vKYFwuCu0w@sYWlqPhaor3-!UEdj+qVCF<>E-M7 z^3{6z8ofMEL9Zhe%^>%>4(MZNXt_!jsRwOU=p6mZ`0X$idV`BMLTU0ufg%*A!V~6n z2M_8K#AU8R!Vuh+JJIMSq>0h#Bm_mKvu9r6nb?OJ8-AxZ%y@Q{lQ=boVWEsNQr&mRV1? zS(i1|#UqH^8L`%e$U5ug`Yx`Wk*K;nWXenaIv+Y=-Eh~Jx;vS=8%W)qP2I^}K>j)( zk|T7jI~geXlZgjmWBrCd;2rRT9*p4WNoL-~loHG*?9*{TBg}NY*+8l^WgAFk&o{yp z#NKs9KNWkIAbpDQ3H6Z;qRuKVCR0MY)=qqO54@CltVz|J7$J6%P0i9Slx1n>hFAt- zN&T_Ke@LYH>R7`RM_mCX-aA%!jOzN>15+H)DZ;d4JXnmGB@CCaUKz%wdPy4njS-o` za=L6b*H|kDly6>*TR8VY?DYL;*t z4@*nanS+Fmf_OQ&>B}O}Y_Tw%rx0GT05_V7Kcd(5^o=V}cVOVJf+qX?L z4it1De~;gZ0hm+ame{}lLAL7)^J1+}J28fdU<~6e=+vuL#g6x>7v4KrV=ha|cQOh( z?GFe;*))+og2E&kwAb5mS3x(Yi8_&8W8mXSK8Wr{_e95lXvtS%trTLjUU$%z@K3fE|;_67uWoWv95EDHwD;Rx2cwBlNqu~3Tmke(PcM z89*#$m&rLEPt)$EhA2zXzj7z4wZmBaFh5Vs`9z9@2|*aL?lz z{S1meo}6-9$Sb|rkI|WR5Gx~96dNR(DZ?awR>3okvrrRFGCB$l886n;$8d zt9do4KeC<>#%od1P?Y%h0(VxF6U=oPffp)3*n%#xuN3_RwJ1zX%CjKz%Kx9(YbpeQ zYQ7rs^l|>8fa{PM#biqQfE0MaiKZ>t$c!u9(HbFM`=|xYS3wscbrzfz5&4nmwBWvN zpAwRzto{+JSqc!N3n~{PiM^?p<#oZ?V0(Q}qJ#zN)&dc**!yYOrF1Kq!ShVR+b5RyY zi@a?Klt3~koTad<*cUi@mGLpw6XgSNQkP)-ROgOu?sG)5ErY{M{Ks1Rbc~e%QgiDT z?@dh<9($kNQ)H4=95yGPk45~h#AoG5gne!1M115hf%3`>?*ZTihoRGIl7)_A9@Ze9 z8W>`gdCj>f(}3epvm&gB%3C5aX8T)?)<(zn4;>+gqt z4E6=c6Az9LJEqdvUak#XAK~{Ueh!C7^|_?~WjQjk(AQ>V4T=0M{IyU7cEHF{C