commit 3159020c3826fbd279027738e31e3ef0c5540355 Author: Hash Borgir Date: Tue Apr 16 21:45:38 2024 -0600 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ff134b --- /dev/null +++ b/.gitignore @@ -0,0 +1,70 @@ +# Visual Studio 2022 specific files and directories +.vs +_ReSharper +_AnalysisTools +*.user +*.suo +*.sln.docx + +# Compiler cache and object binaries +**/.dll.pdb +**/bin/ +**/obj/ +**/\.vs\* +**/BuildLogs/ +**/TemporaryItems/ +**/Release/ +**/AppData/Local/Microsoft/VisualStudio/** +**/ClientAppData/ + +# IDE and editor temporary files +**.vscode/** +**.vsct** +**.openproject** +**.idea/** +**.globe** +**.sublime-workspace** +**.atom-workspace.json** +**.editorconfig** +**.editorconfig.xml** + +# Misc build artifacts, logs, etc. +**/logs/** +**/_build/** +**/output/** +**/publish/** +**/testResults/** +**/coverage/** +**/_deployments/** +**/_migrations/** + +# Node.js and Yarn +**/node_modules/** +**/yarn.lock +**/package-lock.json + +# Gradle and IntelliJ caches +**/caches/** +**/build-cache/** +**/generated/** +**/local-repository/** +**/m2repository/** +**/settings.gradle +**/wrapper/** +**/groovy/** + +# NuGet and Package Manager Console +**.nuget/** +**/.packagemanager/** +**/.paket/** +**/.paket.dependency.json** + +# Other miscellaneous files +**.env** +**.sqlite_prf** +**.sqlite_stat** +**.sqlite_master** +**.bak** +**.old** +**.swp** +**.DS_Store** diff --git a/D2tweaks.sln b/D2tweaks.sln new file mode 100644 index 0000000..0f44108 --- /dev/null +++ b/D2tweaks.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34723.18 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D2tweaks", "D2tweaks.vcxproj", "{FD312A97-7B2C-4922-B2F5-7969DA0396F8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FD312A97-7B2C-4922-B2F5-7969DA0396F8}.Debug|x86.ActiveCfg = Debug|Win32 + {FD312A97-7B2C-4922-B2F5-7969DA0396F8}.Debug|x86.Build.0 = Debug|Win32 + {FD312A97-7B2C-4922-B2F5-7969DA0396F8}.Release|x86.ActiveCfg = Release|Win32 + {FD312A97-7B2C-4922-B2F5-7969DA0396F8}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8F0108AD-9A15-43A0-A115-910627CF0586} + EndGlobalSection +EndGlobal diff --git a/D2tweaks.vcxproj b/D2tweaks.vcxproj new file mode 100644 index 0000000..9c6b36d --- /dev/null +++ b/D2tweaks.vcxproj @@ -0,0 +1,303 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + 16.0 + {FD312A97-7B2C-4922-B2F5-7969DA0396F8} + Win32Proj + 10.0 + + + + DynamicLibrary + false + v143 + + + MultiByte + + + DynamicLibrary + false + v143 + + + MultiByte + + + + + + + + + + + + + + + true + ..\..\Diablo II\MODS\ironman-dev + $(SolutionDir)Build\$(Configuration)\ + + + true + ..\..\Diablo II\MODS\ironman-dev + $(SolutionDir)Build\$(Configuration)\ + + + + WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;SPDLOG_COMPILED_LIB;SPDLOG_FMT_EXTERNAL;FMT_LOCALE;JSON_USE_IMPLICIT_CONVERSIONS=1;%(PreprocessorDefinitions) + MultiThreaded + Level3 + ProgramDatabase + Full + .\include;.\vendor\minhook\include;.\vcpkg\spdlog_x86-windows-static\include;.\vcpkg\fmt_x86-windows-static\include;.\vcpkg\pugixml_x86-windows-static\include;.\vcpkg\nlohmann-json_x86-windows-static\include;.\vendor\D2Template;.\vendor\DllNotify;%(AdditionalIncludeDirectories) + stdcpp17 + true + 4068;4293;4996 + false + $(IntDir)/%(RelativeDir)/ + StreamingSIMDExtensions2 + true + + + MachineX86 + true + Console + wsock32.lib;ws2_32.lib;vendor\minhook\lib\minhook.x32.lib;vcpkg\spdlog_x86-windows-static\lib\spdlog.lib;vcpkg\fmt_x86-windows-static\lib\fmt.lib;vcpkg\pugixml_x86-windows-static\lib\pugixml.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;%(AdditionalDependencies) + + + false + + + + + + + + + WIN32;_WINDOWS;_DEBUG;WIN32_LEAN_AND_MEAN;SPDLOG_COMPILED_LIB;SPDLOG_FMT_EXTERNAL;FMT_LOCALE;JSON_USE_IMPLICIT_CONVERSIONS=1;%(PreprocessorDefinitions) + MultiThreadedDebug + Level3 + ProgramDatabase + Disabled + .\include;.\vendor\minhook\include;.\vcpkg\spdlog_x86-windows-static\include;.\vcpkg\fmt_x86-windows-static\include;.\vcpkg\pugixml_x86-windows-static\include;.\vcpkg\nlohmann-json_x86-windows-static\include;.\vendor\D2Template;.\vendor\DllNotify;%(AdditionalIncludeDirectories) + stdcpp17 + true + 4068;4293;4996 + false + $(IntDir)/%(RelativeDir)/ + true + + + MachineX86 + true + Console + wsock32.lib;ws2_32.lib;vendor\minhook\lib\minhook.x32d.lib;vcpkg\spdlog_x86-windows-static\debug\lib\spdlogd.lib;vcpkg\fmt_x86-windows-static\debug\lib\fmtd.lib;vcpkg\pugixml_x86-windows-static\debug\lib\pugixml_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;%(AdditionalDependencies) + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/D2tweaks.vcxproj.filters b/D2tweaks.vcxproj.filters new file mode 100644 index 0000000..a380632 --- /dev/null +++ b/D2tweaks.vcxproj.filters @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/!old/0000.bmp b/build/!old/0000.bmp new file mode 100644 index 0000000..653c2ba Binary files /dev/null and b/build/!old/0000.bmp differ diff --git a/build/!old/0001.bmp b/build/!old/0001.bmp new file mode 100644 index 0000000..a9ca1ba Binary files /dev/null and b/build/!old/0001.bmp differ diff --git a/build/!old/0002.bmp b/build/!old/0002.bmp new file mode 100644 index 0000000..75cc314 Binary files /dev/null and b/build/!old/0002.bmp differ diff --git a/build/!old/0003.bmp b/build/!old/0003.bmp new file mode 100644 index 0000000..e8e89c6 Binary files /dev/null and b/build/!old/0003.bmp differ diff --git a/build/!old/0004.bmp b/build/!old/0004.bmp new file mode 100644 index 0000000..edb289e Binary files /dev/null and b/build/!old/0004.bmp differ diff --git a/build/!old/0005.bmp b/build/!old/0005.bmp new file mode 100644 index 0000000..40ed870 Binary files /dev/null and b/build/!old/0005.bmp differ diff --git a/build/!old/0006.bmp b/build/!old/0006.bmp new file mode 100644 index 0000000..1ed667a Binary files /dev/null and b/build/!old/0006.bmp differ diff --git a/build/!old/0006_i.bmp b/build/!old/0006_i.bmp new file mode 100644 index 0000000..b5be37f Binary files /dev/null and b/build/!old/0006_i.bmp differ diff --git a/build/!old/0007.bmp b/build/!old/0007.bmp new file mode 100644 index 0000000..782f6cf Binary files /dev/null and b/build/!old/0007.bmp differ diff --git a/build/!old/0007_i.bmp b/build/!old/0007_i.bmp new file mode 100644 index 0000000..aa603c0 Binary files /dev/null and b/build/!old/0007_i.bmp differ diff --git a/build/!old/0008.bmp b/build/!old/0008.bmp new file mode 100644 index 0000000..6abe6d0 Binary files /dev/null and b/build/!old/0008.bmp differ diff --git a/build/!old/0008_i.bmp b/build/!old/0008_i.bmp new file mode 100644 index 0000000..7dcd7fa Binary files /dev/null and b/build/!old/0008_i.bmp differ diff --git a/build/!old/0009.bmp b/build/!old/0009.bmp new file mode 100644 index 0000000..ae67548 Binary files /dev/null and b/build/!old/0009.bmp differ diff --git a/build/!old/0009_.bmp b/build/!old/0009_.bmp new file mode 100644 index 0000000..a8cc7c6 Binary files /dev/null and b/build/!old/0009_.bmp differ diff --git a/build/!old/0010.bmp b/build/!old/0010.bmp new file mode 100644 index 0000000..db170e3 Binary files /dev/null and b/build/!old/0010.bmp differ diff --git a/build/!old/0010_.bmp b/build/!old/0010_.bmp new file mode 100644 index 0000000..e3d33bc Binary files /dev/null and b/build/!old/0010_.bmp differ diff --git a/build/!old/blank down.bmp b/build/!old/blank down.bmp new file mode 100644 index 0000000..5a1f8cc Binary files /dev/null and b/build/!old/blank down.bmp differ diff --git a/build/!old/blank up.bmp b/build/!old/blank up.bmp new file mode 100644 index 0000000..a0f6472 Binary files /dev/null and b/build/!old/blank up.bmp differ diff --git a/build/!old/d2tweaks.mpq b/build/!old/d2tweaks.mpq new file mode 100644 index 0000000..6b80306 Binary files /dev/null and b/build/!old/d2tweaks.mpq differ diff --git a/build/!resources/LOD item code/armor.txt b/build/!resources/LOD item code/armor.txt new file mode 100644 index 0000000..d3fceef --- /dev/null +++ b/build/!resources/LOD item code/armor.txt @@ -0,0 +1,202 @@ +Aegis uow +Aerin Shield pa4 +Akaran Rondache pa7 +Akaran Targe pa6 +Alpha Helm dr6 +Ancient Armor aar +Ancient Shield pad +Ancient Shield xts +Antlers dr3 +Archon Plate utp +Armet ulm +Assault Helmet ba4 +Avenger Guard ba5 +Balrog Skin upl +Barbed Shield xpk +Basinet xhl +Battle Belt ztb +Battle Boots xtb +Battle Gauntlets xtg +Belt(M) mbl +Blade Barrier upk +Blood Spirt drb +Bloodlord Skull nef +Bone Helm bhm +Bone Shield bsh +Bone Visage uh9 +Boneweave uhn +Boneweave Boots umb +Bracers(M) mgl +Bramble Mitts ulg +Breast Plate brs +Buckler buc +Cantor Trophy ne9 +Cap/hat cap +Carnage Helm bab +Casque xlm +Chain Boots mbt +Chain Mail chn +Chaos Armor xul +Circlet ci0 +Colossus Girdle uhc +Conquerer Crown bae +Corona urn +Coronet ci1 +Crown crn +Crown Shield pa5 +Crusader Gauntlets utg +Cuirass xrs +Death Mask xsk +Defender xuc +Demon Head ne5 +Demonhead usk +Demonhide Armor xla +Demonhide Boots xlb +Demonhide Gloves xlg +Demonhide Sash zlb +Destroyer Helm bad +Diadem ci3 +Diamond Mail ung +Dragon Shield xit +Dream Spirit drf +Dusk Shroud uui +Earth Spirit drd +Embossed Plate xth +Falcon Mask dr4 +Fanged Helm ba2 +Fetish Trophy ne7 +Field Plate fld +Full Helm fhl +Full Plate Mail ful +Fury Visor bac +Gargoyle Head ne4 +Gaunlets(H) hgl +Ghost Armor xui +Giant Conch uhl +Girdle(H) hbl +Gloves(L) lgl +Gothic Plate gth +Gothic Shield gts +Grand Crown xrn +Great Hauberk urs +Great Helm ghm +Griffon Headress dr7 +Grim Helm xh9 +Grim Shield xsh +Guardian Crown baf +Guilded Shield pa9 +Hard Leather Armor hla +Hawk Helm dr2 +Heater uuc +Heavy Belt tbl +Heavy Boots vbt +Heavy Bracers xmg +Heavy Gloves vgl +Heirophant Trophy nea +Hellforged Plate ult +Hellspawn Skull neg +Helm hlm +Heraldic Shield pa3 +Horned Helm ba3 +Hunter's Guise dr8 +Hydraskull ukp +Hyperion urg +Jawbone Cap ba1 +Jawbone Visor ba6 +Kite Shield kit +Kraken Shell uld +Lacquered Plate uth +Large Shield lrg +Leather Armor lea +Leather Boots lbt +Light Belt vbl +Light Gauntlets tgl +Light Plate ltp +Light Plate Boots tbt +Linked Mail xng +Lion Helm ba7 +Loricated Mail ucl +Luna uml +Mage Plate xtp +Mask msk +Mesh Armor xhn +Mesh Belt zmb +Mesh Boots xmb +Minion Skull neb +Mirrored Boots utb +Mithril Coil umc +Monarch uit +Mummified Trophy ne6 +Myrmidon Greaves uhb +Ogre Gauntlets uhg +Ornate Armor xar +Overseer Skull ned +Pavise xow +Plate Boots hbt +Plate Mail plt +Preserved Head ne1 +Protector Shield pa8 +Quilted Armor qui +Rage Mask ba8 +Ring Mail rng +Rondache pa2 +Round Shield xml +Royal Shield paa +Russet Armor xpl +Sacred Armor uar +Sacred Feathers dr9 +Sacred Rondache pac +Sacred Targe pab +Sallet xkp +Sash(L) lbl +Savage Helmet ba9 +Scale Mail scl +Scarab Husk ula +Scarabshell Boots uvb +Scutum xrg +Serpentskin Armor xea +Sexton Trophy ne8 +Shadow Plate uul +Shako uap +Sharkskin Belt zvb +Sharkskin Boots xvb +Sharkskin Gloves xvg +Sharktooth Armor xld +Skull Cap skp +Sky Spirit dre +Slayer Guard baa +Small Shield sml +Spiderweb Sash ulc +Spiked Shield spk +Spired Helm uhm +Spirit Mask dr5 +Splint Mail spl +Studded Leather stu +Succubae Skull nee +Sun Spirit drc +Targe pa1 +Templar Coat xlt +Tiara ci2 +Tigulated Mail xcl +Totemic Mask dra +Tower Shield tow +Trellised Armor xtu +Troll Belt utc +Troll Nest ush +Unraveller Head ne3 +Vambraces umg +Vampirebone Gloves uvg +Vampirefang Belt uvc +Vortex Shield paf +War Belt zhb +War Boots xhb +War Gauntlets xhg +War Hat xap +Ward uts +Winged Helm xhm +Wire Fleece utu +Wolf Head dr1 +Wyrmhide uea +Wyrmhide Boots ulb +Zakarum Shield pae +Zombie Head ne2 diff --git a/build/!resources/LOD item code/misc.txt b/build/!resources/LOD item code/misc.txt new file mode 100644 index 0000000..5078486 --- /dev/null +++ b/build/!resources/LOD item code/misc.txt @@ -0,0 +1,124 @@ +elixir elx +Healing Potion hpo +Mana Potion mpo +Full Healing Potion hpf +Full Mana Potion mpf +Stamina Potion vps +Antidote Potion yps +Rejuv Potion rvs +Full Rejuv Potion rvl +Thawing Potion wms +Town Portal Book tbk +Identify Book ibk +amulet amu +viper amulet vip +ring rin +gold gld +Bark Scroll bks +Arrows aqv +Torch tch +Bolts cqv +Town Portal Scroll tsc +Identify Scroll isc +Skeleton Key key +Mephisto Key luv +jade figurine j34 +gold bird g34 +lam esen's tome bbb +Horadric Cube box +Mephisto SoulStone mss +Book of Skill ass +KhalimEye qey +KhalimHeart qhr +KhalimBrain qbr +Player Ear ear +Chipped Amethyst gcv +Flawed Amethyst gfv +Amethyst gsv +Flawless Amethyst gzv +Perfect Amethyst gpv +Chipped Topaz gcy +Flawed Topaz gfy +Topaz gsy +Flawless Topaz gly +Perfect Topaz gpy +Chipped Saphire gcb +Flawed Saphire gfb +Saphire gsb +Flawless Saphire glb +Perfect Saphire gpb +Chipped Emerald gcg +Flawed Emerald gfg +Emerald gsg +Flawless Emerald glg +Perfect Emerald gpg +Chipped Ruby gcr +Flawed Ruby gfr +Ruby gsr +Flawless Ruby glr +Perfect Ruby gpr +Chipped Diamond gcw +Flawed Diamond gfw +Diamond gsw +Flawless Diamond glw +Perfect Diamond gpw +Lesser Healing Potion hp1 +Light Healing Potion hp2 +Healing Potion hp3 +Strong Healing Potion hp4 +Greater Healing Potion hp5 +Lesser Mana Potion mp1 +Light Mana Potion mp2 +Mana Potion mp3 +Strong Mana Potion mp4 +Greater Mana Potion mp5 +Chipped Skull skc +Flawed Skull skf +Skull sku +Flawless Skull skl +Perfect Skull skz +herb hrb +Charm Small cm1 +Charm Medium cm2 +Charm Large cm3 +Small Red Potion rps +Large Red Potion rpl +Small Blue Potion bps +Large Blue Potion bpl +El Rune r01 +Eld Rune r02 +Tir Rune r03 +Nef Rune r04 +Eth Rune r05 +Ith Rune r06 +Tal Rune r07 +Ral Rune r08 +Ort Rune r09 +Thul Rune r10 +Amn Rune r11 +Sol Rune r12 +Shael Rune r13 +Dol Rune r14 +Hel Rune r15 +Io Rune r16 +Lum Rune r17 +Ko Rune r18 +Fal Rune r19 +Lem Rune r20 +Pul Rune r21 +Um Rune r22 +Mal Rune r23 +Ist Rune r24 +Gul Rune r25 +Vex Rune r26 +Ohm Rune r27 +Lo Rune r28 +Sur Rune r29 +Ber Rune r30 +Jah Rune r31 +Cham Rune r32 +Zod Rune r33 +Jewel jew +Maguffin ice +Scroll 0sc +Scroll of Malah tr2 diff --git a/build/!resources/LOD item code/weapons.txt b/build/!resources/LOD item code/weapons.txt new file mode 100644 index 0000000..6167a19 --- /dev/null +++ b/build/!resources/LOD item code/weapons.txt @@ -0,0 +1,306 @@ +Ancient Axe 9gi +Ancient Sword 9wd +Arbalest 8lx +Archon Staff 6ws +Ashwood Bow am6 +Ataghan 7sm +Axe axe +Balanced Axe bal +Balanced Knife bkf +Balista 8hx +Balrog Blade 7gs +Balrog Spear 7s7 +Barbed Club 9sp +Bardiche bar +Bastard Sword bsw +Battle Axe btx +Battle Cestus 7cs +Battle Dart 9tk +Battle Hammer 9wh +Battle Scythe 9s8 +Battle Staff bst +Battle Sword 9bs +Bearded Axe 9ba +Bec-de-Corbin 9h9 +Berserker Axe 7wa +Bill 9vo +Blade bld +Blade Bow 6hb +Blade Talons btl +Bone Knife 7dg +Bone Wand bwn +Brandistock brn +Broad Axe bax +Broad Sword bsd +Burnt Wand 9wn +Caduceus 7ws +Cedar Bow 8lb +Cedar Staff 8cs +Ceremonial Bow am7 +Ceremonial Javelin ama +Ceremonial Pike am9 +Ceremonial Spear am8 +Cestus ces +Champion Axe 7ga +Champion Sword 7b7 +Choking Gas Potion gpm +Chu-Ko-Nu 8rx +Cinquedeas 9kr +Clasped Orb ob4 +Claws clw +Claymore clm +Cleaver 9ax +Cloudy Sphere ob8 +Club clb +Colossal Sword 7fb +Colossus Blade 7gd +Colossus Crossbow 6hx +Colossus Voulge 7vo +Composite Bow cbw +Conquest Sword 7bs +Crossbow mxb +Crowbill 9mp +Crusader Bow 6l7 +Cryptic Axe 7pa +Cryptic Sword 7ls +Crystal Sword crs +Crystalline Globe ob7 +Cudgel 9cl +Cutlass 9sm +Dacian Falx 9cm +Dagger dgr +Decapitator 7bt +Demon Crossbow 6rx +Demon Heart obd +Devil Star 7mt +Diamond Bow 6s7 +Dimensional Blade 9cr +Dimensional Shard obf +Dirk dir +Divine Scepter 9ws +Double Axe 2ax +Double Bow 8cb +Dragon Stone ob5 +Eagle Orb ob1 +Edge Bow 8sb +Elder Staff 6cs +Eldritch Orb obc +Elegant Blade 7sb +Espadon 92h +Ettin Axe 72a +Executioner Sword 9gd +Exploding Potion opm +Falcata 7ss +Falchion flc +Fanged Knife 7kr +Fascia 9xf +Feral Axe 7la +Feral Claws 7lw +Flail fla +Flamberge flb +Flanged Mace 9ma +Flying Axe 7ta +Flying Knife 7tk +Francisca 9ta +Fulminating Potion opl +Fuscina 9tr +Ghost Glaive 7gl +Ghost Spear 7st +Ghost Wand 7yw +Giant Axe gix +Giant Sword gis +Giant Thresher 7wc +Gidbinn g33 +Gladius 9ss +Glaive glv +Glorious Axe 7gi +Glowing Orb ob6 +Gnarled Staff cst +Gorgon Crossbow 6mx +Gothic Axe 9ga +Gothic Bow 8lw +Gothic Staff 8bs +Gothic Sword 9b9 +Grand Matron Bow amc +Grand Scepter gsc +Grave Wand 9gw +Great Axe gax +Great Bow 6cb +Great Maul gma +Great Pilum 9pi +Great Poleaxe 7h7 +Great Sword gsd +Greater Claws 9lw +Greater Talons 9tw +Grim Scythe 9wc +Grim Wand gwn +Halberd hal +Hand Axe hax +Hand Scythe 9cs +Harpoon 9ts +Hatchet 9ha +Hatchet Hands axf +Heavenly Stone obb +Heavy Crossbow hxb +Hellforge Hammer hfh +Highland Blade 7cm +Holy Water Sprinkler 9qs +Horadric Malus hdm +Horadric Staff hst +Hunter's Bow hbw +Hurlbat 9b8 +Hydra Bow 6lw +Hydra Edge 7fc +Hyperion Javelin 7ja +Hyperion Spear 7sr +Jagged Star 9mt +Javelin jav +Jo Staff 8ss +Katar ktr +KhalimFlail qf1 +Knout 9fl +Kriss kri +Lance 9p9 +Large Axe lax +Legend Spike 7bl +Legend Sword 72h +Legendary Mallet 7wh +Lich Wand 7bw +Light Crossbow lxb +Lochaber Axe 9b7 +Long Battle Bow lbb +Long Bow lbw +Long Siege Bow 8l8 +Long Staff lst +Long Sword lsd +Long War Bow lwb +Mace mac +Maiden Javelin am5 +Maiden Pike am4 +Maiden Spear am3 +Mancatcher 7br +Martel de Fer 9gm +Matriarchal Bow amb +Matriarchal Pike ame +Matriarchal Spear amd +MatriarchalJavelin amf +Maul mau +Mighty Scepter 7sc +Military Axe 9la +Military Pick mpi +Mithral Point 7di +Morning Star mst +Mythical Sword 7wd +Naga 9wa +Ogre Axe 7o7 +Ogre Maul 7m7 +Oil Potion ops +Partizan 9pa +Pellet Bow 6lx +Petrified Wand 9yw +Phase Blade 7cr +Pike pik +Pilum pil +Poignard 9dg +Poleaxe pax +Polished Wand 7wn +Quarterstaff 8ls +Quhab 9ar +Rancid Gas Potion gps +Razor Bow 8hb +Reflex Bow am2 +Reinforced Mace 7ma +Repeating Crossbow rxb +Rondel 9di +Rune Bow 8sw +Rune Scepter 9sc +Rune Staff 8ws +Rune Sword 9ls +Runic Talons 7tw +Saber sbr +Sacred Globe ob2 +Scepter scp +Scimitar scm +Scissors Katar skr +Scissors Quhab 9qr +Scissors Suwayyah 7qr +Scourge 7fl +Scythe scy +Seraph Rod 7qs +Shadow Bow 6lb +Shamshir 9sb +Shillelah 6bs +Short Battle Bow sbb +Short Bow sbw +Short Siege Bow 8s8 +Short Spear ssp +Short Staff sst +Short Sword ssd +Short War Bow swb +Siege Crossbow 8mx +Silver Edged Axe 7ba +Simbilan 9s9 +Small Crescent 7ax +Smoked Sphere ob3 +Sparkling Ball ob9 +Spear spr +Spetum spt +Spiculum 9gl +Spider Bow 6sb +Spiked Club spc +Staff of the Kings msf +Stag Bow am1 +Stalagmite 6ls +Stilleto 9bl +Strangling Gas Potion gpl +Stygian Pike 7tr +Stygian Pilum 7pi +SuperKhalimFlail qf2 +Suwayyah 7ar +Swirling Crystal oba +Tabar 9bt +Thresher 7s8 +Throwing Axe tax +Throwing Knife tkf +Throwing Spear tsp +Thunder Maul 7gm +Tomahawk 7ha +Tomb Wand 9bw +Trident tri +Truncheon 7cl +Tulwar 9fc +Tusk Sword 9gs +Twin Axe 92a +Two-Handed Sword 2hs +Tyrant Club 7sp +Unearthed Wand 7gw +Vortex Orb obe +Voulge vou +Walking Stick 6ss +Wand wnd +War Axe wax +War Club 9m9 +War Dart 9bk +War Fist 7xf +War Fork 9br +War Hammer whm +War Javelin 9ja +War Pike 7p7 +War Scepter wsp +War Scythe wsc +War Spear 9sr +War Spike 7mp +War Staff wst +War Sword wsd +Ward Bow 6sw +Winged Axe 7b8 +Winged Harpoon 7ts +Winged Knife 7bk +Wirt's Leg leg +Wrist Blade wrb +Wrist Spike 9wb +Wrist Sword 7wb +Yari 9st +Yew Wand ywn +Zweihander 9fb +decoy dagger d33 diff --git a/build/!resources/LOD item type/Item Types.txt b/build/!resources/LOD item type/Item Types.txt new file mode 100644 index 0000000..19be5ce --- /dev/null +++ b/build/!resources/LOD item type/Item Types.txt @@ -0,0 +1,101 @@ +ItemType Code Equiv1 Equiv2 + +Shield shie shld +Armor tors armo +Gold gold misc +Bow Quiver bowq misl +Crossbow Quiver xboq misl +Player Body Part play misc +Herb herb misc +Potion poti misc +Ring ring misc +Elixir elix misc +Amulet amul misc +Charm char misc +Boots boot armo +Gloves glov armo +Book book misc +Belt belt armo +Gem gem sock +Torch torc misc +Scroll scro misc +Not Used +Scepter scep rod +Wand wand rod +Staff staf rod +Bow bow miss +Axe axe mele +Club club blun +Sword swor mele +Hammer hamm blun +Knife knif mele +Spear spea mele +Polearm pole mele +Crossbow xbow miss +Mace mace blun +Helm helm armo +Missile Potion tpot thro +Quest ques +Body Part body misc +Key key misc +Throwing Knife tkni comb knif +Throwing Axe taxe comb axe +Javelin jave comb spea +Weapon weap +Melee Weapon mele weap +Missile Weapon miss weap +Thrown Weapon thro weap +Combo Weapon comb mele thro +Any Armor armo +Any Shield shld armo seco +Miscellaneous misc +Socket Filler sock misc +Second Hand seco +Staves And Rods rod blun +Missile misl misc +Blunt blun mele +Jewel jewl sock +Class Specific clas +Amazon Item amaz clas +Barbarian Item barb clas +Necromancer Item necr clas +Paladin Item pala clas +Sorceress Item sorc clas +Assassin Item assn clas +Druid Item drui clas +Hand to Hand h2h mele assn +Orb orb weap sorc +Voodoo Heads head shld necr +Auric Shields ashd shld pala +Primal Helm phlm helm barb +Pelt pelt helm drui +Cloak cloa tors assn +Rune rune sock +Circlet circ helm +Healing Potion hpot poti +Mana Potion mpot poti +Rejuv Potion rpot hpot mpot +Stamina Potion spot poti +Antidote Potion apot poti +Thawing Potion wpot poti +Small Charm scha char +Medium Charm mcha char +Large Charm lcha char +Amazon Bow abow bow amaz +Amazon Spear aspe spea amaz +Amazon Javelin ajav jave amaz +Hand to Hand 2 h2h2 h2h +Magic Bow Quiv mboq bowq +Magic Xbow Quiv mxbq xboq +Chipped Gem gem0 gem +Flawed Gem gem1 gem +Standard Gem gem2 gem +Flawless Gem gem3 gem +Perfect Gem gem4 gem +Amethyst gema gem +Diamond gemd gem +Emerald geme gem +Ruby gemr gem +Sapphire gems gem +Topaz gemt gem +Skull gemz gem diff --git a/build/!resources/d2tweaks.ini b/build/!resources/d2tweaks.ini new file mode 100644 index 0000000..72c471d --- /dev/null +++ b/build/!resources/d2tweaks.ini @@ -0,0 +1,302 @@ +[modules] +AutoGoldPickup=1 +AutoItemPickup=1 +ItemDropMessage=1 +Autosort=1 +DamageDisplay=1 +ItemMover=1 +LootFilter=1 +IdentifyOnPickup=1 +ReloadTradeGamble=1 +; Experimental function, do not use it +AutoTransmute=0 + +; Font numbers used in settings +; font_8 = 0 +; font_16 = 1 +; font_30 = 2 +; font_42 = 3 +; font_formal10 = 4 +; font_formal12 = 5 +; font_6 = 6 +; font_24 = 7 +; font_formal11 = 8 +; font_exocet10 = 9 +; font_ridiculous = 10 +; font_exocet8 = 11 +; font_lastsucker = 12 +; font_ingamechat = 13 + + +; Color numbers used in the settings +; WHITE = 0 +; RED = 1 +; LIGHT_GREEN = 2 +; BLUE = 3 +; DARK_GOLD = 4 +; GREY = 5 +; BLACK = 6 +; GOLD = 7 +; ORANGE = 8 +; YELLOW = 9 +; DARK_GREEN = 10 +; PURPLE = 11 +; GREEN = 12 +; WHITE2 = 13 +; BLACK2 = 14 +; DARK_WHITE = 15 +; LIGHT_GREY = 16 + + +[DamageDisplay] +; Font number +EnemyDamageFont=0 +PlayerDamageFont=1 +PlayerDamagePosx = 70 +PlayerDamagePosy = 660 +;Float text in milliseconds +DisplayTime=1000 + + + +[IdentifyOnPickup] +;Identify on pickup for target item quality, 1 - enabled, 0 - disabled +Normal=1 +Superior=1 +Magic=1 +Rare=1 +Set=1 +Unique=1 +Crafted=1 +Tempered=1 + + +[AutoGoldPickup] +; Maximum distance for gold pickup +; (recommended 4-8) +PickupDistance=6 +; Float text in milliseconds +DisplayTime=2500 + + +[AutoItemPickup] +; Maximum distance for item pickup +; (recommended 4-8) +PickupDistance=6 + +; Item type and his equivalent for autopickup, delimeter between each code "|" or "," +; Case sensitive, corresponds to the "Code", "Equiv1", "Equiv2" columns from itemtypes.txt +; Some codes have three characters each, (like the "key" "rod" "orb") so the last character must be a space +; If you want to add an item to autopickup, and its code or type is unknown - read description in the [ItemDropMessage] section below + +; Syntax: +; |ItemTypeCode| - all items with this itemtype will be picked up +; |ItemTypeCode:123456789| - item type with specified numbers of quality will be picked up, all items with a different quality will be ignored +; |ItemTypeCode-123456789| - item type with specified numbers of quality will be ignored, all items with a different quality will be picked up + +; For example +; |armo|weap:6| - all armors and rare weapons will be picked up +; |tors:5|orb | - only set torso armors and all orbs will be picked up +; |orb :7|rod :7| - only unique orbs and staves will be picked up +; |tors:46| - only magic and rare armors will be picked up, all other armors will be ignored +; |shie-46| - only magic and rare shields will be ignored, all other shields will be picked up + +; To disable any ItemType list, you can comment out entire line by putting a semicolon ";" at the beginning of line + +ItemTypeList1= +ItemTypeList2= +ItemTypeList3= +ItemTypeList4= +ItemTypeList5= +ItemTypeList6= +ItemTypeList7= +ItemTypeList8= +ItemTypeList9= +ItemTypeList10= + +; Item code for autopickup, delimeter between each code "|" or "," or "space" +; Case sensitive, corresponds to the "Code" column from weapons.txt, armor.txt, misc.txt +; You can additionally specify quality of item , according to the table qualities of item + +; Syntax: +; |ItemCode| - all items with this itemcode will be picked up +; |ItemCode:123456789| - item with specified numbers of quality will be picked up, all items with a different quality will be ignored +; |ItemCode-123456789| - item with specified numbers of quality will be ignored, all items with a different quality will be picked up + +; For example +; |rin|amu| - all rings and amulets will be picked up +; |rin:46| - only magic and rare rings will be picked up, all other rings will be ignored +; |amu-46| - only magic and rare amulets will be ignored, all other amulets will be picked up + +; Number of item quality +; 1 - Cracked item +; 2 - Normal item +; 3 - Superior item +; 4 - Magic item +; 5 - Set item +; 6 - Rare item +; 7 - Unique item +; 8 - Crafted item +; 9 - Tempered item + +; If you want to add an item to autopickup, and its code or type is unknown - read description in the [ItemDropMessage] section below +; To disable any Item list, you can comment out entire line by putting a semicolon ";" at the beginning of line +; Maximum string length for each ItemList - 65535 symbol + +; This Item list from vanilla LOD, you can edit it as you wish +; Weapons +ItemList1=9gi:5678|9wd:5678|8lx:5678|6ws:5678|am6:5678|7sm:5678|axe:5678|bal:5678|bkf:5678|8hx:5678|7gs:5678|7s7:5678|9sp:5678|bar:5678|bsw:5678|btx:5678|7cs:5678|9tk:5678|9wh:5678|9s8:5678|bst:5678|9bs:5678|9ba:5678|9h9:5678|7wa:5678|9vo:5678|bld:5678|6hb:5678|btl:5678|7dg:5678|bwn:5678|brn:5678|bax:5678|bsd:5678|9wn:5678|7ws:5678|8lb:5678|8cs:5678|am7:5678|ama:5678|am9:5678|am8:5678|ces:5678|7ga:5678|7b7:5678|gpm:5678|8rx:5678|9kr:5678|ob4:5678|clw:5678|clm:5678|9ax:5678|ob8:5678|clb:5678|7fb:5678|7gd:5678|6hx:5678|7vo:5678|cbw:5678|7bs:5678|mxb:5678|9mp:5678|6l7:5678|7pa:5678|7ls:5678|crs:5678|ob7:5678|9cl:5678|9sm:5678|9cm:5678|dgr:5678|7bt:5678|6rx:5678|obd:5678|7mt:5678|6s7:5678|9cr:5678|obf:5678|dir:5678|9ws:5678|2ax:5678|8cb:5678|ob5:5678|ob1:5678|8sb:5678|6cs:5678|obc:5678|7sb:5678|92h:5678|72a:5678|9gd:5678|opm:5678|7ss:5678|flc:5678|7kr:5678|9xf:5678|7la:5678|7lw:5678|fla:5678|flb:5678|9ma:5678|7ta:5678|7tk:5678|9ta:5678|opl:5678|9tr:5678|7gl:5678|7st:5678|7yw:5678|gix:5678|gis:5678|7wc:5678|g33:5678|9ss:5678|glv:5678|7gi:5678|ob6:5678|cst:5678|6mx:5678|9ga:5678|8lw:5678|8bs:5678|9b9:5678|amc:5678|gsc:5678|9gw:5678|gax:5678|6cb:5678|gma:5678|9pi:5678|7h7:5678|gsd:5678|9lw:5678|9tw:5678|9wc:5678|gwn:5678|hal:5678|hax:5678|9cs:5678|9ts:5678|9ha:5678|axf:5678|obb:5678|hxb:5678|hfh:5678|7cm:5678|9qs:5678|hdm:5678|hst:5678|hbw:5678|9b8:5678|6lw:5678|7fc:5678|7ja:5678|7sr:5678|9mt:5678|jav:5678|8ss:5678|ktr:5678|qf1:5678|9fl:5678|kri:5678|9p9:5678|lax:5678|7bl:5678|72h:5678|7wh:5678|7bw:5678|lxb:5678|9b7:5678|lbb:5678|lbw:5678|8l8:5678|lst:5678|lsd:5678|lwb:5678|mac:5678|am5:5678|am4:5678|am3:5678|7br:5678|9gm:5678|amb:5678|ame:5678|amd:5678|amf:5678|mau:5678|7sc:5678|9la:5678|mpi:5678|7di:5678|mst:5678|7wd:5678|9wa:5678|7o7:5678|7m7:5678|ops:5678|9pa:5678|6lx:5678|9yw:5678|7cr:5678|pik:5678|pil:5678|9dg:5678|pax:5678|7wn:5678|8ls:5678|9ar:5678|gps:5678|8hb:5678|am2:5678|7ma:5678|rxb:5678|9di:5678|8sw:5678|9sc:5678|8ws:5678|9ls:5678|7tw:5678|sbr:5678|ob2:5678|scp:5678|scm:5678|skr:5678|9qr:5678|7qr:5678|7fl:5678|scy:5678|7qs:5678|6lb:5678|9sb:5678|6bs:5678|sbb:5678|sbw:5678|8s8:5678|ssp:5678|sst:5678|ssd:5678|swb:5678|8mx:5678|7ba:5678|9s9:5678|7ax:5678|ob3:5678|ob9:5678|spr:5678|spt:5678|9gl:5678|6sb:5678|spc:5678|msf:5678|am1:5678|6ls:5678|9bl:5678|gpl:5678|7tr:5678|7pi:5678|qf2:5678|7ar:5678|oba:5678|9bt:5678|7s8:5678|tax:5678|tkf:5678|tsp:5678|7gm:5678|7ha:5678|9bw:5678|tri:5678|7cl:5678|9fc:5678|9gs:5678|92a:5678|2hs:5678|7sp:5678|7gw:5678|obe:5678|vou:5678|6ss:5678|wnd:5678|wax:5678|9m9:5678|9bk:5678|7xf:5678|9br:5678|whm:5678|9ja:5678|7p7:5678|wsp:5678|wsc:5678|9sr:5678|7mp:5678|wst:5678|wsd:5678|6sw:5678|7b8:5678|7ts:5678|7bk:5678|leg:5678|wrb:5678|9wb:5678|7wb:5678|9st:5678|ywn:5678|9fb:5678|d33:5678 +; Armor +ItemList2=uow:5678|pa4:5678|pa7:5678|pa6:5678|dr6:5678|aar:5678|pad:5678|xts:5678|dr3:5678|utp:5678|ulm:5678|ba4:5678|ba5:5678|upl:5678|xpk:5678|xhl:5678|ztb:5678|xtb:5678|xtg:5678|mbl:5678|upk:5678|drb:5678|nef:5678|bhm:5678|bsh:5678|uh9:5678|uhn:5678|umb:5678|mgl:5678|ulg:5678|brs:5678|buc:5678|ne9:5678|cap:5678|bab:5678|xlm:5678|mbt:5678|chn:5678|xul:5678|ci0:5678|uhc:5678|bae:5678|urn:5678|ci1:5678|crn:5678|pa5:5678|utg:5678|xrs:5678|xsk:5678|xuc:5678|ne5:5678|usk:5678|xla:5678|xlb:5678|xlg:5678|zlb:5678|bad:5678|ci3:5678|ung:5678|xit:5678|drf:5678|uui:5678|drd:5678|xth:5678|dr4:5678|ba2:5678|ne7:5678|fld:5678|fhl:5678|ful:5678|bac:5678|ne4:5678|hgl:5678|xui:5678|uhl:5678|hbl:5678|lgl:5678|gth:5678|gts:5678|xrn:5678|urs:5678|ghm:5678|dr7:5678|xh9:5678|xsh:5678|baf:5678|pa9:5678|hla:5678|dr2:5678|uuc:5678|tbl:5678|vbt:5678|xmg:5678|vgl:5678|nea:5678|ult:5678|neg:5678|hlm:5678|pa3:5678|ba3:5678|dr8:5678|ukp:5678|urg:5678|ba1:5678|ba6:5678|kit:5678|uld:5678|uth:5678|lrg:5678|lea:5678|lbt:5678|vbl:5678|tgl:5678|ltp:5678|tbt:5678|xng:5678|ba7:5678|ucl:5678|uml:5678|xtp:5678|msk:5678|xhn:5678|zmb:5678|xmb:5678|neb:5678|utb:5678|umc:5678|uit:5678|ne6:5678|uhb:5678|uhg:5678|xar:5678|ned:5678|xow:5678|hbt:5678|plt:5678|ne1:5678|pa8:5678|qui:5678|ba8:5678|rng:5678|pa2:5678|xml:5678|paa:5678|xpl:5678|uar:5678|dr9:5678|pac:5678|pab:5678|xkp:5678|lbl:5678|ba9:5678|scl:5678|ula:5678|uvb:5678|xrg:5678|xea:5678|ne8:5678|uul:5678|uap:5678|zvb:5678|xvb:5678|xvg:5678|xld:5678|skp:5678|dre:5678|baa:5678|sml:5678|ulc:5678|spk:5678|uhm:5678|dr5:5678|spl:5678|stu:5678|nee:5678|drc:5678|pa1:5678|xlt:5678|ci2:5678|xcl:5678|dra:5678|tow:5678|xtu:5678|utc:5678|ush:5678|ne3:5678|umg:5678|uvg:5678|uvc:5678|paf:5678|zhb:5678|xhb:5678|xhg:5678|xap:5678|uts:5678|xhm:5678|utu:5678|dr1:5678|uea:5678|ulb:5678|pae:5678|ne2:5678 +; Rune +ItemList3=r01|r02|r03|r04|r05|r06|r07|r08|r09|r10|r11|r12|r13|r14|r15|r16|r17|r18|r19|r20|r21|r22|r23|r24|r25|r26|r27|r28|r29|r30|r31|r32|r33 +; Charm +ItemList4=cm1:5678|cm2:5678|cm3:5678 +; Amulet, Ring, Jewel +ItemList5=rin:5678|amu:5678|jew:5678 +; Chipped gems +ItemList6=gcv|gcy|gcb|gcg|gcr|gcw|skc +; Flawed gems +ItemList7=gfv|gfy|gfb|gfg|gfr|gfw|skf +; Standart gems +ItemList8=gsv|gsy|gsb|gsg|gsr|gsw|sku +; Flawless gems +ItemList9=gzv|gly|glb|glg|glr|glw|skl +; Perfect gems +ItemList10=gpv|gpy|gpb|gpg|gpr|gpw|skz +; Rejuv Potion, Full Rejuv Potion +ItemList11=rvs|rvl +; Portal scroll, ID scroll, Key +ItemList12=tsc|isc|key +ItemList13= +ItemList14= +ItemList15= +ItemList16= +ItemList17= +ItemList18= +ItemList19= +ItemList20= +ItemList21= +ItemList22= +ItemList23= +ItemList24= +ItemList25= +ItemList26= +ItemList27= +ItemList28= +ItemList29= +ItemList30= + + + +[ItemDropMessage] +; A message will be displayed for these items +; Syntax same as [AutoItemPickup] + +; To find out item code or item type and its quality, +; you can press "ScrollLock" key during the game (LED is on) - +; all filters will be disabled and all dropped items will be visible. +; Extended information about each item will appear. +; Additionally all dropped items and filter patterns for each item will be saved in a file "d2tweaks.log" +; Press ScrollLock again to turn on filters (LED is off) + +; To disable any Item or ItemType list, you can comment out entire line by putting a semicolon ";" at the beginning of line +; Maximum string length for each list - 65535 symbol + +ItemTypeList1= +ItemTypeList2= +ItemTypeList3= +ItemTypeList4= +ItemTypeList5= +ItemTypeList6= +ItemTypeList7= +ItemTypeList8= +ItemTypeList9= +ItemTypeList10= + +; This Item list from vanilla LOD, you can edit it as you wish +; Weapons +ItemList1=9gi:5678|9wd:5678|8lx:5678|6ws:5678|am6:5678|7sm:5678|axe:5678|bal:5678|bkf:5678|8hx:5678|7gs:5678|7s7:5678|9sp:5678|bar:5678|bsw:5678|btx:5678|7cs:5678|9tk:5678|9wh:5678|9s8:5678|bst:5678|9bs:5678|9ba:5678|9h9:5678|7wa:5678|9vo:5678|bld:5678|6hb:5678|btl:5678|7dg:5678|bwn:5678|brn:5678|bax:5678|bsd:5678|9wn:5678|7ws:5678|8lb:5678|8cs:5678|am7:5678|ama:5678|am9:5678|am8:5678|ces:5678|7ga:5678|7b7:5678|gpm:5678|8rx:5678|9kr:5678|ob4:5678|clw:5678|clm:5678|9ax:5678|ob8:5678|clb:5678|7fb:5678|7gd:5678|6hx:5678|7vo:5678|cbw:5678|7bs:5678|mxb:5678|9mp:5678|6l7:5678|7pa:5678|7ls:5678|crs:5678|ob7:5678|9cl:5678|9sm:5678|9cm:5678|dgr:5678|7bt:5678|6rx:5678|obd:5678|7mt:5678|6s7:5678|9cr:5678|obf:5678|dir:5678|9ws:5678|2ax:5678|8cb:5678|ob5:5678|ob1:5678|8sb:5678|6cs:5678|obc:5678|7sb:5678|92h:5678|72a:5678|9gd:5678|opm:5678|7ss:5678|flc:5678|7kr:5678|9xf:5678|7la:5678|7lw:5678|fla:5678|flb:5678|9ma:5678|7ta:5678|7tk:5678|9ta:5678|opl:5678|9tr:5678|7gl:5678|7st:5678|7yw:5678|gix:5678|gis:5678|7wc:5678|g33:5678|9ss:5678|glv:5678|7gi:5678|ob6:5678|cst:5678|6mx:5678|9ga:5678|8lw:5678|8bs:5678|9b9:5678|amc:5678|gsc:5678|9gw:5678|gax:5678|6cb:5678|gma:5678|9pi:5678|7h7:5678|gsd:5678|9lw:5678|9tw:5678|9wc:5678|gwn:5678|hal:5678|hax:5678|9cs:5678|9ts:5678|9ha:5678|axf:5678|obb:5678|hxb:5678|hfh:5678|7cm:5678|9qs:5678|hdm:5678|hst:5678|hbw:5678|9b8:5678|6lw:5678|7fc:5678|7ja:5678|7sr:5678|9mt:5678|jav:5678|8ss:5678|ktr:5678|qf1:5678|9fl:5678|kri:5678|9p9:5678|lax:5678|7bl:5678|72h:5678|7wh:5678|7bw:5678|lxb:5678|9b7:5678|lbb:5678|lbw:5678|8l8:5678|lst:5678|lsd:5678|lwb:5678|mac:5678|am5:5678|am4:5678|am3:5678|7br:5678|9gm:5678|amb:5678|ame:5678|amd:5678|amf:5678|mau:5678|7sc:5678|9la:5678|mpi:5678|7di:5678|mst:5678|7wd:5678|9wa:5678|7o7:5678|7m7:5678|ops:5678|9pa:5678|6lx:5678|9yw:5678|7cr:5678|pik:5678|pil:5678|9dg:5678|pax:5678|7wn:5678|8ls:5678|9ar:5678|gps:5678|8hb:5678|am2:5678|7ma:5678|rxb:5678|9di:5678|8sw:5678|9sc:5678|8ws:5678|9ls:5678|7tw:5678|sbr:5678|ob2:5678|scp:5678|scm:5678|skr:5678|9qr:5678|7qr:5678|7fl:5678|scy:5678|7qs:5678|6lb:5678|9sb:5678|6bs:5678|sbb:5678|sbw:5678|8s8:5678|ssp:5678|sst:5678|ssd:5678|swb:5678|8mx:5678|7ba:5678|9s9:5678|7ax:5678|ob3:5678|ob9:5678|spr:5678|spt:5678|9gl:5678|6sb:5678|spc:5678|msf:5678|am1:5678|6ls:5678|9bl:5678|gpl:5678|7tr:5678|7pi:5678|qf2:5678|7ar:5678|oba:5678|9bt:5678|7s8:5678|tax:5678|tkf:5678|tsp:5678|7gm:5678|7ha:5678|9bw:5678|tri:5678|7cl:5678|9fc:5678|9gs:5678|92a:5678|2hs:5678|7sp:5678|7gw:5678|obe:5678|vou:5678|6ss:5678|wnd:5678|wax:5678|9m9:5678|9bk:5678|7xf:5678|9br:5678|whm:5678|9ja:5678|7p7:5678|wsp:5678|wsc:5678|9sr:5678|7mp:5678|wst:5678|wsd:5678|6sw:5678|7b8:5678|7ts:5678|7bk:5678|leg:5678|wrb:5678|9wb:5678|7wb:5678|9st:5678|ywn:5678|9fb:5678|d33:5678 +; Armor +ItemList2=uow:5678|pa4:5678|pa7:5678|pa6:5678|dr6:5678|aar:5678|pad:5678|xts:5678|dr3:5678|utp:5678|ulm:5678|ba4:5678|ba5:5678|upl:5678|xpk:5678|xhl:5678|ztb:5678|xtb:5678|xtg:5678|mbl:5678|upk:5678|drb:5678|nef:5678|bhm:5678|bsh:5678|uh9:5678|uhn:5678|umb:5678|mgl:5678|ulg:5678|brs:5678|buc:5678|ne9:5678|cap:5678|bab:5678|xlm:5678|mbt:5678|chn:5678|xul:5678|ci0:5678|uhc:5678|bae:5678|urn:5678|ci1:5678|crn:5678|pa5:5678|utg:5678|xrs:5678|xsk:5678|xuc:5678|ne5:5678|usk:5678|xla:5678|xlb:5678|xlg:5678|zlb:5678|bad:5678|ci3:5678|ung:5678|xit:5678|drf:5678|uui:5678|drd:5678|xth:5678|dr4:5678|ba2:5678|ne7:5678|fld:5678|fhl:5678|ful:5678|bac:5678|ne4:5678|hgl:5678|xui:5678|uhl:5678|hbl:5678|lgl:5678|gth:5678|gts:5678|xrn:5678|urs:5678|ghm:5678|dr7:5678|xh9:5678|xsh:5678|baf:5678|pa9:5678|hla:5678|dr2:5678|uuc:5678|tbl:5678|vbt:5678|xmg:5678|vgl:5678|nea:5678|ult:5678|neg:5678|hlm:5678|pa3:5678|ba3:5678|dr8:5678|ukp:5678|urg:5678|ba1:5678|ba6:5678|kit:5678|uld:5678|uth:5678|lrg:5678|lea:5678|lbt:5678|vbl:5678|tgl:5678|ltp:5678|tbt:5678|xng:5678|ba7:5678|ucl:5678|uml:5678|xtp:5678|msk:5678|xhn:5678|zmb:5678|xmb:5678|neb:5678|utb:5678|umc:5678|uit:5678|ne6:5678|uhb:5678|uhg:5678|xar:5678|ned:5678|xow:5678|hbt:5678|plt:5678|ne1:5678|pa8:5678|qui:5678|ba8:5678|rng:5678|pa2:5678|xml:5678|paa:5678|xpl:5678|uar:5678|dr9:5678|pac:5678|pab:5678|xkp:5678|lbl:5678|ba9:5678|scl:5678|ula:5678|uvb:5678|xrg:5678|xea:5678|ne8:5678|uul:5678|uap:5678|zvb:5678|xvb:5678|xvg:5678|xld:5678|skp:5678|dre:5678|baa:5678|sml:5678|ulc:5678|spk:5678|uhm:5678|dr5:5678|spl:5678|stu:5678|nee:5678|drc:5678|pa1:5678|xlt:5678|ci2:5678|xcl:5678|dra:5678|tow:5678|xtu:5678|utc:5678|ush:5678|ne3:5678|umg:5678|uvg:5678|uvc:5678|paf:5678|zhb:5678|xhb:5678|xhg:5678|xap:5678|uts:5678|xhm:5678|utu:5678|dr1:5678|uea:5678|ulb:5678|pae:5678|ne2:5678 +; Rune +ItemList3=r01|r02|r03|r04|r05|r06|r07|r08|r09|r10|r11|r12|r13|r14|r15|r16|r17|r18|r19|r20|r21|r22|r23|r24|r25|r26|r27|r28|r29|r30|r31|r32|r33 +; Charm +ItemList4=cm1:5678|cm2:5678|cm3:5678 +; Ring, Amulet, Jewel +ItemList5=rin:5678|amu:5678|jew:5678 +; Chipped gems +ItemList6=gcv|gcy|gcb|gcg|gcr|gcw|skc +; Flawed gems +ItemList7=gfv|gfy|gfb|gfg|gfr|gfw|skf +; Standart gems +ItemList8=gsv|gsy|gsb|gsg|gsr|gsw|sku +; Flawless gems +ItemList9=gzv|gly|glb|glg|glr|glw|skl +; Perfect gems +ItemList10=gpv|gpy|gpb|gpg|gpr|gpw|skz +ItemList11= +ItemList12= +ItemList13= +ItemList14= +ItemList15= +ItemList16= +ItemList17= +ItemList18= +ItemList19= +ItemList20= +ItemList21= +ItemList22= +ItemList23= +ItemList24= +ItemList25= +ItemList26= +ItemList27= +ItemList28= +ItemList29= +ItemList30= + +; If game crashes on start, try HookMethod=2 +HookMethod=1 +; Number of simultaneously displayed messages (maximum 32) +MaxNumMessages=8 +; String after item name +SecondString=" dropped!" +; Color number for every item quality +DefaultColor=0 +Cracked=0 +Normal=0 +Superior=0 +Magic=3 +Set=12 +Rare=9 +Unique=7 +Crafted=8 +Tempered=10 + + +[AutoTransmute] +EnableTransmuteSound=1 +DelayInFrames=5 + +ItemTypeList1= +ItemTypeList2= +ItemTypeList3= +ItemTypeList4= +ItemTypeList5= +ItemTypeList6= +ItemTypeList7= +ItemTypeList8= +ItemTypeList9= +ItemTypeList10= + +ItemList1= +ItemList2= +ItemList3= +ItemList4= +ItemList5= +ItemList6= +ItemList7= +ItemList8= +ItemList9= +ItemList10= diff --git a/build/!resources/d2tweaks/assets/buttons.dc6 b/build/!resources/d2tweaks/assets/buttons.dc6 new file mode 100644 index 0000000..7a9c269 Binary files /dev/null and b/build/!resources/d2tweaks/assets/buttons.dc6 differ diff --git a/build/!resources/d2tweaks/interface_d2expres/autogoldpickup.xml b/build/!resources/d2tweaks/interface_d2expres/autogoldpickup.xml new file mode 100644 index 0000000..243911b --- /dev/null +++ b/build/!resources/d2tweaks/interface_d2expres/autogoldpickup.xml @@ -0,0 +1,31 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_d2expres/autopickup.xml b/build/!resources/d2tweaks/interface_d2expres/autopickup.xml new file mode 100644 index 0000000..c48d148 --- /dev/null +++ b/build/!resources/d2tweaks/interface_d2expres/autopickup.xml @@ -0,0 +1,35 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_d2expres/autosort.xml b/build/!resources/d2tweaks/interface_d2expres/autosort.xml new file mode 100644 index 0000000..d0df14c --- /dev/null +++ b/build/!resources/d2tweaks/interface_d2expres/autosort.xml @@ -0,0 +1,95 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_d2expres/autotransmute.xml b/build/!resources/d2tweaks/interface_d2expres/autotransmute.xml new file mode 100644 index 0000000..8c27026 --- /dev/null +++ b/build/!resources/d2tweaks/interface_d2expres/autotransmute.xml @@ -0,0 +1,36 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_d2expres/loot_filter_settings_menu.xml b/build/!resources/d2tweaks/interface_d2expres/loot_filter_settings_menu.xml new file mode 100644 index 0000000..8f1cba7 --- /dev/null +++ b/build/!resources/d2tweaks/interface_d2expres/loot_filter_settings_menu.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_d2expres/loot_filter_settings_toggle_menu.xml b/build/!resources/d2tweaks/interface_d2expres/loot_filter_settings_toggle_menu.xml new file mode 100644 index 0000000..77021fb --- /dev/null +++ b/build/!resources/d2tweaks/interface_d2expres/loot_filter_settings_toggle_menu.xml @@ -0,0 +1,35 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_d2expres/reloaditems.xml b/build/!resources/d2tweaks/interface_d2expres/reloaditems.xml new file mode 100644 index 0000000..85c97b7 --- /dev/null +++ b/build/!resources/d2tweaks/interface_d2expres/reloaditems.xml @@ -0,0 +1,35 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_sgd2freeres/autogoldpickup.xml b/build/!resources/d2tweaks/interface_sgd2freeres/autogoldpickup.xml new file mode 100644 index 0000000..8c45a02 --- /dev/null +++ b/build/!resources/d2tweaks/interface_sgd2freeres/autogoldpickup.xml @@ -0,0 +1,58 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_sgd2freeres/autopickup.xml b/build/!resources/d2tweaks/interface_sgd2freeres/autopickup.xml new file mode 100644 index 0000000..b046fc9 --- /dev/null +++ b/build/!resources/d2tweaks/interface_sgd2freeres/autopickup.xml @@ -0,0 +1,62 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_sgd2freeres/autosort.xml b/build/!resources/d2tweaks/interface_sgd2freeres/autosort.xml new file mode 100644 index 0000000..498f448 --- /dev/null +++ b/build/!resources/d2tweaks/interface_sgd2freeres/autosort.xml @@ -0,0 +1,176 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_sgd2freeres/autotransmute.xml b/build/!resources/d2tweaks/interface_sgd2freeres/autotransmute.xml new file mode 100644 index 0000000..d533cce --- /dev/null +++ b/build/!resources/d2tweaks/interface_sgd2freeres/autotransmute.xml @@ -0,0 +1,62 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_sgd2freeres/loot_filter_settings_menu.xml b/build/!resources/d2tweaks/interface_sgd2freeres/loot_filter_settings_menu.xml new file mode 100644 index 0000000..3814f5c --- /dev/null +++ b/build/!resources/d2tweaks/interface_sgd2freeres/loot_filter_settings_menu.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_sgd2freeres/loot_filter_settings_toggle_menu.xml b/build/!resources/d2tweaks/interface_sgd2freeres/loot_filter_settings_toggle_menu.xml new file mode 100644 index 0000000..26e86ec --- /dev/null +++ b/build/!resources/d2tweaks/interface_sgd2freeres/loot_filter_settings_toggle_menu.xml @@ -0,0 +1,62 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_sgd2freeres/reloaditems.xml b/build/!resources/d2tweaks/interface_sgd2freeres/reloaditems.xml new file mode 100644 index 0000000..f0710bc --- /dev/null +++ b/build/!resources/d2tweaks/interface_sgd2freeres/reloaditems.xml @@ -0,0 +1,62 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_vanilla/autogoldpickup.xml b/build/!resources/d2tweaks/interface_vanilla/autogoldpickup.xml new file mode 100644 index 0000000..010d8cb --- /dev/null +++ b/build/!resources/d2tweaks/interface_vanilla/autogoldpickup.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_vanilla/autopickup.xml b/build/!resources/d2tweaks/interface_vanilla/autopickup.xml new file mode 100644 index 0000000..eab03a4 --- /dev/null +++ b/build/!resources/d2tweaks/interface_vanilla/autopickup.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_vanilla/autosort.xml b/build/!resources/d2tweaks/interface_vanilla/autosort.xml new file mode 100644 index 0000000..dad05f6 --- /dev/null +++ b/build/!resources/d2tweaks/interface_vanilla/autosort.xml @@ -0,0 +1,50 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_vanilla/autotransmute.xml b/build/!resources/d2tweaks/interface_vanilla/autotransmute.xml new file mode 100644 index 0000000..a12d2b1 --- /dev/null +++ b/build/!resources/d2tweaks/interface_vanilla/autotransmute.xml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_vanilla/loot_filter_settings_menu.xml b/build/!resources/d2tweaks/interface_vanilla/loot_filter_settings_menu.xml new file mode 100644 index 0000000..2c17f5f --- /dev/null +++ b/build/!resources/d2tweaks/interface_vanilla/loot_filter_settings_menu.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_vanilla/loot_filter_settings_toggle_menu.xml b/build/!resources/d2tweaks/interface_vanilla/loot_filter_settings_toggle_menu.xml new file mode 100644 index 0000000..c8e2102 --- /dev/null +++ b/build/!resources/d2tweaks/interface_vanilla/loot_filter_settings_toggle_menu.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/build/!resources/d2tweaks/interface_vanilla/reloaditems.xml b/build/!resources/d2tweaks/interface_vanilla/reloaditems.xml new file mode 100644 index 0000000..b44c9a4 --- /dev/null +++ b/build/!resources/d2tweaks/interface_vanilla/reloaditems.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/build/D2tweaks_17.04.2024.rar b/build/D2tweaks_17.04.2024.rar new file mode 100644 index 0000000..e6368a9 Binary files /dev/null and b/build/D2tweaks_17.04.2024.rar differ diff --git a/build/D2tweaks_18.04.2023.zip b/build/D2tweaks_18.04.2023.zip new file mode 100644 index 0000000..ee0b301 Binary files /dev/null and b/build/D2tweaks_18.04.2023.zip differ diff --git a/build/D2tweaks_27.04.2023.zip b/build/D2tweaks_27.04.2023.zip new file mode 100644 index 0000000..e22f4ee Binary files /dev/null and b/build/D2tweaks_27.04.2023.zip differ diff --git a/build/Debug/D2tweaks.dll b/build/Debug/D2tweaks.dll new file mode 100644 index 0000000..53fd4ce Binary files /dev/null and b/build/Debug/D2tweaks.dll differ diff --git a/build/Debug/D2tweaks.dll.recipe b/build/Debug/D2tweaks.dll.recipe new file mode 100644 index 0000000..3696fa9 --- /dev/null +++ b/build/Debug/D2tweaks.dll.recipe @@ -0,0 +1,11 @@ + + + + + D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\D2tweaks.dll + + + + + + \ No newline at end of file diff --git a/build/Debug/D2tweaks.ilk b/build/Debug/D2tweaks.ilk new file mode 100644 index 0000000..4f65deb Binary files /dev/null and b/build/Debug/D2tweaks.ilk differ diff --git a/build/Debug/D2tweaks.log b/build/Debug/D2tweaks.log new file mode 100644 index 0000000..17fe29e --- /dev/null +++ b/build/Debug/D2tweaks.log @@ -0,0 +1,28 @@ + client.cpp + autosort_client.cpp + auto_gold_pickup_client.cpp + auto_item_pickup_client.cpp + damage_display_client.cpp + item_drop_message_client.cpp + item_move_client.cpp +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\item_move\item_move_client.cpp(99,33): warning C4018: '<': signed/unsigned mismatch + loot_filter.cpp + loot_filter_settings_menu.cpp + loot_filter_settings_toggle_menu.cpp + trader_update_client.cpp + transmute_client.cpp + autosort_server.cpp + auto_gold_pickup_server.cpp +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\auto_gold_pickup\auto_gold_pickup_server.cpp(51,33): warning C4018: '>': signed/unsigned mismatch + auto_item_pickup_server.cpp + damage_display_server.cpp + identify_on_pickup_server.cpp + item_drop_message_server.cpp + item_move_server.cpp + trader_update_server.cpp + transmute_server.cpp + server.cpp + main.cpp + D2Template.cpp + DllNotify.cpp + D2tweaks.vcxproj -> D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\D2tweaks.dll diff --git a/build/Debug/D2tweaks.pdb b/build/Debug/D2tweaks.pdb new file mode 100644 index 0000000..30b1f7e Binary files /dev/null and b/build/Debug/D2tweaks.pdb differ diff --git a/build/Debug/D2tweaks.tlog/CL.command.1.tlog b/build/Debug/D2tweaks.tlog/CL.command.1.tlog new file mode 100644 index 0000000..de157f8 Binary files /dev/null and b/build/Debug/D2tweaks.tlog/CL.command.1.tlog differ diff --git a/build/Debug/D2tweaks.tlog/CL.read.1.tlog b/build/Debug/D2tweaks.tlog/CL.read.1.tlog new file mode 100644 index 0000000..3dc891a Binary files /dev/null and b/build/Debug/D2tweaks.tlog/CL.read.1.tlog differ diff --git a/build/Debug/D2tweaks.tlog/CL.write.1.tlog b/build/Debug/D2tweaks.tlog/CL.write.1.tlog new file mode 100644 index 0000000..0661c0c Binary files /dev/null and b/build/Debug/D2tweaks.tlog/CL.write.1.tlog differ diff --git a/build/Debug/D2tweaks.tlog/Cl.items.tlog b/build/Debug/D2tweaks.tlog/Cl.items.tlog new file mode 100644 index 0000000..92f04de --- /dev/null +++ b/build/Debug/D2tweaks.tlog/Cl.items.tlog @@ -0,0 +1,63 @@ +D:\VSCode\D2tweaks_src_17.04.2024\src\common\asm_code.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\common\asm_code.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\common\config.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\common\config.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\common\hooking.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\common\hooking.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\common\ini.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\common\ini.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\common\string_utils.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\common\string_utils.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\autosort\autosort_client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\autosort\autosort_client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\auto_gold_pickup\auto_gold_pickup_client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\auto_gold_pickup\auto_gold_pickup_client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\auto_item_pickup\auto_item_pickup_client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\auto_item_pickup\auto_item_pickup_client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\client_module.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\client_module.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\damage_display\damage_display_client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\damage_display\damage_display_client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\item_drop_message\item_drop_message_client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\item_drop_message\item_drop_message_client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\item_move\item_move_client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\item_move\item_move_client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\loot_filter\loot_filter.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\loot_filter\loot_filter.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\loot_filter\loot_filter_settings.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\loot_filter\loot_filter_settings.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\loot_filter\loot_filter_settings_menu.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\loot_filter\loot_filter_settings_menu.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\loot_filter\loot_filter_settings_toggle_menu.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\loot_filter\loot_filter_settings_toggle_menu.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\small_patches\small_patches.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\small_patches\small_patches.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\test\test.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\test\test.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\trader_update\trader_update_client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\trader_update\trader_update_client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\client\modules\transmute\transmute_client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\client\modules\transmute\transmute_client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\common\asset_manager.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\common\asset_manager.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\common\common.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\common\common.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\autosort\autosort_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\autosort\autosort_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\auto_gold_pickup\auto_gold_pickup_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\auto_gold_pickup\auto_gold_pickup_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\auto_item_pickup\auto_item_pickup_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\auto_item_pickup\auto_item_pickup_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\damage_display\damage_display_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\damage_display\damage_display_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\identify_on_pickup\identify_on_pickup_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\identify_on_pickup\identify_on_pickup_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\item_drop_message\item_drop_message_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\item_drop_message\item_drop_message_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\item_move\item_move_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\item_move\item_move_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\server_module.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\server_module.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\test\test.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\test\test.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\trader_update\trader_update_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\trader_update\trader_update_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\modules\transmute\transmute_server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\modules\transmute\transmute_server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\server\server.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\server\server.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\ui\controls\button.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\ui\controls\button.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\ui\controls\checkbox.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\ui\controls\checkbox.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\ui\controls\group.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\ui\controls\group.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\ui\controls\image.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\ui\controls\image.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\ui\controls\label.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\ui\controls\label.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\ui\menu.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\ui\menu.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\d2tweaks\ui\ui_manager.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\d2tweaks\ui\ui_manager.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2client.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2client.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2cmp.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2cmp.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2common.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2common.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2game.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2game.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2gfx.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2gfx.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2lang.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2lang.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2launch.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2launch.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2net.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2net.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\d2win.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\d2win.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\fog.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\fog.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\storm.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\storm.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\utils\mpq_ifstream.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\utils\mpq_ifstream.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\diablo2\utils\screen.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\diablo2\utils\screen.obj +D:\VSCode\D2tweaks_src_17.04.2024\src\main.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\src\main.obj +D:\VSCode\D2tweaks_src_17.04.2024\vendor\D2Template\D2Template.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\vendor\D2Template\D2Template.obj +D:\VSCode\D2tweaks_src_17.04.2024\vendor\DllNotify\DllNotify.cpp;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\vendor\DllNotify\DllNotify.obj +D:\VSCode\D2tweaks_src_17.04.2024\vendor\minhook\src\buffer.c;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\vendor\minhook\src\buffer.obj +D:\VSCode\D2tweaks_src_17.04.2024\vendor\minhook\src\hde\hde32.c;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\vendor\minhook\src\hde\hde32.obj +D:\VSCode\D2tweaks_src_17.04.2024\vendor\minhook\src\hde\hde64.c;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\vendor\minhook\src\hde\hde64.obj +D:\VSCode\D2tweaks_src_17.04.2024\vendor\minhook\src\hook.c;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\vendor\minhook\src\hook.obj +D:\VSCode\D2tweaks_src_17.04.2024\vendor\minhook\src\trampoline.c;D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\vendor\minhook\src\trampoline.obj diff --git a/build/Debug/D2tweaks.tlog/D2tweaks.lastbuildstate b/build/Debug/D2tweaks.tlog/D2tweaks.lastbuildstate new file mode 100644 index 0000000..d88a7bd --- /dev/null +++ b/build/Debug/D2tweaks.tlog/D2tweaks.lastbuildstate @@ -0,0 +1,2 @@ +PlatformToolSet=v143:VCToolArchitecture=Native32Bit:VCToolsVersion=14.39.33519:TargetPlatformVersion=10.0.19041.0: +Debug|Win32|D:\VSCode\D2tweaks_src_17.04.2024\| diff --git a/build/Debug/D2tweaks.tlog/link.command.1.tlog b/build/Debug/D2tweaks.tlog/link.command.1.tlog new file mode 100644 index 0000000..fe1052f Binary files /dev/null and b/build/Debug/D2tweaks.tlog/link.command.1.tlog differ diff --git a/build/Debug/D2tweaks.tlog/link.read.1.tlog b/build/Debug/D2tweaks.tlog/link.read.1.tlog new file mode 100644 index 0000000..2414a4b Binary files /dev/null and b/build/Debug/D2tweaks.tlog/link.read.1.tlog differ diff --git a/build/Debug/D2tweaks.tlog/link.secondary.1.tlog b/build/Debug/D2tweaks.tlog/link.secondary.1.tlog new file mode 100644 index 0000000..764c9a5 --- /dev/null +++ b/build/Debug/D2tweaks.tlog/link.secondary.1.tlog @@ -0,0 +1,2 @@ +^D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\COMMON\ASM_CODE.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\COMMON\CONFIG.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\COMMON\HOOKING.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\COMMON\INI.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\COMMON\STRING_UTILS.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\AUTOSORT\AUTOSORT_CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\AUTO_GOLD_PICKUP\AUTO_GOLD_PICKUP_CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\AUTO_ITEM_PICKUP\AUTO_ITEM_PICKUP_CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\CLIENT_MODULE.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\DAMAGE_DISPLAY\DAMAGE_DISPLAY_CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\ITEM_DROP_MESSAGE\ITEM_DROP_MESSAGE_CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\ITEM_MOVE\ITEM_MOVE_CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\LOOT_FILTER\LOOT_FILTER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\LOOT_FILTER\LOOT_FILTER_SETTINGS.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\LOOT_FILTER\LOOT_FILTER_SETTINGS_MENU.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\LOOT_FILTER\LOOT_FILTER_SETTINGS_TOGGLE_MENU.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\SMALL_PATCHES\SMALL_PATCHES.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\TEST\TEST.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\TRADER_UPDATE\TRADER_UPDATE_CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\CLIENT\MODULES\TRANSMUTE\TRANSMUTE_CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\COMMON\ASSET_MANAGER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\COMMON\COMMON.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\AUTOSORT\AUTOSORT_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\AUTO_GOLD_PICKUP\AUTO_GOLD_PICKUP_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\AUTO_ITEM_PICKUP\AUTO_ITEM_PICKUP_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\DAMAGE_DISPLAY\DAMAGE_DISPLAY_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\IDENTIFY_ON_PICKUP\IDENTIFY_ON_PICKUP_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\ITEM_DROP_MESSAGE\ITEM_DROP_MESSAGE_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\ITEM_MOVE\ITEM_MOVE_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\SERVER_MODULE.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\TEST\TEST.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\TRADER_UPDATE\TRADER_UPDATE_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\MODULES\TRANSMUTE\TRANSMUTE_SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\SERVER\SERVER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\UI\CONTROLS\BUTTON.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\UI\CONTROLS\CHECKBOX.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\UI\CONTROLS\GROUP.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\UI\CONTROLS\IMAGE.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\UI\CONTROLS\LABEL.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\UI\MENU.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\D2TWEAKS\UI\UI_MANAGER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2CLIENT.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2CMP.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2COMMON.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2GAME.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2GFX.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2LANG.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2LAUNCH.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2NET.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\D2WIN.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\FOG.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\STORM.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\UTILS\MPQ_IFSTREAM.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\DIABLO2\UTILS\SCREEN.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\SRC\MAIN.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\VENDOR\D2TEMPLATE\D2TEMPLATE.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\VENDOR\DLLNOTIFY\DLLNOTIFY.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\VENDOR\MINHOOK\SRC\BUFFER.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\VENDOR\MINHOOK\SRC\HDE\HDE32.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\VENDOR\MINHOOK\SRC\HDE\HDE64.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\VENDOR\MINHOOK\SRC\HOOK.OBJ|D:\VSCODE\D2TWEAKS_SRC_17.04.2024\BUILD\DEBUG\VENDOR\MINHOOK\SRC\TRAMPOLINE.OBJ +D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\D2tweaks.ilk diff --git a/build/Debug/D2tweaks.tlog/link.write.1.tlog b/build/Debug/D2tweaks.tlog/link.write.1.tlog new file mode 100644 index 0000000..1bb98ec Binary files /dev/null and b/build/Debug/D2tweaks.tlog/link.write.1.tlog differ diff --git a/build/Debug/D2tweaks.vcxproj.FileListAbsolute.txt b/build/Debug/D2tweaks.vcxproj.FileListAbsolute.txt new file mode 100644 index 0000000..b6614f7 --- /dev/null +++ b/build/Debug/D2tweaks.vcxproj.FileListAbsolute.txt @@ -0,0 +1 @@ +D:\VSCode\D2tweaks_src_17.04.2024\Build\Debug\D2tweaks.dll diff --git a/build/Debug/src/common/asm_code.obj b/build/Debug/src/common/asm_code.obj new file mode 100644 index 0000000..ee9067c Binary files /dev/null and b/build/Debug/src/common/asm_code.obj differ diff --git a/build/Debug/src/common/config.obj b/build/Debug/src/common/config.obj new file mode 100644 index 0000000..dd9786b Binary files /dev/null and b/build/Debug/src/common/config.obj differ diff --git a/build/Debug/src/common/hooking.obj b/build/Debug/src/common/hooking.obj new file mode 100644 index 0000000..5ab1ca1 Binary files /dev/null and b/build/Debug/src/common/hooking.obj differ diff --git a/build/Debug/src/common/ini.obj b/build/Debug/src/common/ini.obj new file mode 100644 index 0000000..30cee9c Binary files /dev/null and b/build/Debug/src/common/ini.obj differ diff --git a/build/Debug/src/common/string_utils.obj b/build/Debug/src/common/string_utils.obj new file mode 100644 index 0000000..ab85521 Binary files /dev/null and b/build/Debug/src/common/string_utils.obj differ diff --git a/build/Debug/src/d2tweaks/client/client.obj b/build/Debug/src/d2tweaks/client/client.obj new file mode 100644 index 0000000..eebe80d Binary files /dev/null and b/build/Debug/src/d2tweaks/client/client.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.obj b/build/Debug/src/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.obj new file mode 100644 index 0000000..4af3370 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.obj b/build/Debug/src/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.obj new file mode 100644 index 0000000..2f81ca7 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/autosort/autosort_client.obj b/build/Debug/src/d2tweaks/client/modules/autosort/autosort_client.obj new file mode 100644 index 0000000..9d20fca Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/autosort/autosort_client.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/client_module.obj b/build/Debug/src/d2tweaks/client/modules/client_module.obj new file mode 100644 index 0000000..f00a000 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/client_module.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/damage_display/damage_display_client.obj b/build/Debug/src/d2tweaks/client/modules/damage_display/damage_display_client.obj new file mode 100644 index 0000000..caf4e70 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/damage_display/damage_display_client.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/item_drop_message/item_drop_message_client.obj b/build/Debug/src/d2tweaks/client/modules/item_drop_message/item_drop_message_client.obj new file mode 100644 index 0000000..cd9e33f Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/item_drop_message/item_drop_message_client.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/item_move/item_move_client.obj b/build/Debug/src/d2tweaks/client/modules/item_move/item_move_client.obj new file mode 100644 index 0000000..cd5a0ef Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/item_move/item_move_client.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter.obj b/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter.obj new file mode 100644 index 0000000..653a112 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings.obj b/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings.obj new file mode 100644 index 0000000..13d94b6 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.obj b/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.obj new file mode 100644 index 0000000..0111d1a Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.obj b/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.obj new file mode 100644 index 0000000..0d6693f Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/small_patches/small_patches.obj b/build/Debug/src/d2tweaks/client/modules/small_patches/small_patches.obj new file mode 100644 index 0000000..261a5d3 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/small_patches/small_patches.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/test/test.obj b/build/Debug/src/d2tweaks/client/modules/test/test.obj new file mode 100644 index 0000000..f22aa55 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/test/test.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/trader_update/trader_update_client.obj b/build/Debug/src/d2tweaks/client/modules/trader_update/trader_update_client.obj new file mode 100644 index 0000000..3500f66 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/trader_update/trader_update_client.obj differ diff --git a/build/Debug/src/d2tweaks/client/modules/transmute/transmute_client.obj b/build/Debug/src/d2tweaks/client/modules/transmute/transmute_client.obj new file mode 100644 index 0000000..2d1a2c3 Binary files /dev/null and b/build/Debug/src/d2tweaks/client/modules/transmute/transmute_client.obj differ diff --git a/build/Debug/src/d2tweaks/common/asset_manager.obj b/build/Debug/src/d2tweaks/common/asset_manager.obj new file mode 100644 index 0000000..4733716 Binary files /dev/null and b/build/Debug/src/d2tweaks/common/asset_manager.obj differ diff --git a/build/Debug/src/d2tweaks/common/common.obj b/build/Debug/src/d2tweaks/common/common.obj new file mode 100644 index 0000000..af6b77b Binary files /dev/null and b/build/Debug/src/d2tweaks/common/common.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.obj b/build/Debug/src/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.obj new file mode 100644 index 0000000..b0456af Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.obj b/build/Debug/src/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.obj new file mode 100644 index 0000000..e17a30a Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/autosort/autosort_server.obj b/build/Debug/src/d2tweaks/server/modules/autosort/autosort_server.obj new file mode 100644 index 0000000..7773858 Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/autosort/autosort_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/damage_display/damage_display_server.obj b/build/Debug/src/d2tweaks/server/modules/damage_display/damage_display_server.obj new file mode 100644 index 0000000..7fac09c Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/damage_display/damage_display_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.obj b/build/Debug/src/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.obj new file mode 100644 index 0000000..e18270f Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/item_drop_message/item_drop_message_server.obj b/build/Debug/src/d2tweaks/server/modules/item_drop_message/item_drop_message_server.obj new file mode 100644 index 0000000..77a8991 Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/item_drop_message/item_drop_message_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/item_move/item_move_server.obj b/build/Debug/src/d2tweaks/server/modules/item_move/item_move_server.obj new file mode 100644 index 0000000..4ea394f Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/item_move/item_move_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/server_module.obj b/build/Debug/src/d2tweaks/server/modules/server_module.obj new file mode 100644 index 0000000..5fc3b33 Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/server_module.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/test/test.obj b/build/Debug/src/d2tweaks/server/modules/test/test.obj new file mode 100644 index 0000000..886635f Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/test/test.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/trader_update/trader_update_server.obj b/build/Debug/src/d2tweaks/server/modules/trader_update/trader_update_server.obj new file mode 100644 index 0000000..4a2f389 Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/trader_update/trader_update_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/modules/transmute/transmute_server.obj b/build/Debug/src/d2tweaks/server/modules/transmute/transmute_server.obj new file mode 100644 index 0000000..1c3e33c Binary files /dev/null and b/build/Debug/src/d2tweaks/server/modules/transmute/transmute_server.obj differ diff --git a/build/Debug/src/d2tweaks/server/server.obj b/build/Debug/src/d2tweaks/server/server.obj new file mode 100644 index 0000000..2354b12 Binary files /dev/null and b/build/Debug/src/d2tweaks/server/server.obj differ diff --git a/build/Debug/src/d2tweaks/ui/controls/button.obj b/build/Debug/src/d2tweaks/ui/controls/button.obj new file mode 100644 index 0000000..02b66d4 Binary files /dev/null and b/build/Debug/src/d2tweaks/ui/controls/button.obj differ diff --git a/build/Debug/src/d2tweaks/ui/controls/checkbox.obj b/build/Debug/src/d2tweaks/ui/controls/checkbox.obj new file mode 100644 index 0000000..0666979 Binary files /dev/null and b/build/Debug/src/d2tweaks/ui/controls/checkbox.obj differ diff --git a/build/Debug/src/d2tweaks/ui/controls/group.obj b/build/Debug/src/d2tweaks/ui/controls/group.obj new file mode 100644 index 0000000..86c74d9 Binary files /dev/null and b/build/Debug/src/d2tweaks/ui/controls/group.obj differ diff --git a/build/Debug/src/d2tweaks/ui/controls/image.obj b/build/Debug/src/d2tweaks/ui/controls/image.obj new file mode 100644 index 0000000..bac23d0 Binary files /dev/null and b/build/Debug/src/d2tweaks/ui/controls/image.obj differ diff --git a/build/Debug/src/d2tweaks/ui/controls/label.obj b/build/Debug/src/d2tweaks/ui/controls/label.obj new file mode 100644 index 0000000..5b6d77f Binary files /dev/null and b/build/Debug/src/d2tweaks/ui/controls/label.obj differ diff --git a/build/Debug/src/d2tweaks/ui/menu.obj b/build/Debug/src/d2tweaks/ui/menu.obj new file mode 100644 index 0000000..89991dd Binary files /dev/null and b/build/Debug/src/d2tweaks/ui/menu.obj differ diff --git a/build/Debug/src/d2tweaks/ui/ui_manager.obj b/build/Debug/src/d2tweaks/ui/ui_manager.obj new file mode 100644 index 0000000..a6913d0 Binary files /dev/null and b/build/Debug/src/d2tweaks/ui/ui_manager.obj differ diff --git a/build/Debug/src/diablo2/d2client.obj b/build/Debug/src/diablo2/d2client.obj new file mode 100644 index 0000000..1e69960 Binary files /dev/null and b/build/Debug/src/diablo2/d2client.obj differ diff --git a/build/Debug/src/diablo2/d2cmp.obj b/build/Debug/src/diablo2/d2cmp.obj new file mode 100644 index 0000000..a0f3592 Binary files /dev/null and b/build/Debug/src/diablo2/d2cmp.obj differ diff --git a/build/Debug/src/diablo2/d2common.obj b/build/Debug/src/diablo2/d2common.obj new file mode 100644 index 0000000..5939e19 Binary files /dev/null and b/build/Debug/src/diablo2/d2common.obj differ diff --git a/build/Debug/src/diablo2/d2game.obj b/build/Debug/src/diablo2/d2game.obj new file mode 100644 index 0000000..7959227 Binary files /dev/null and b/build/Debug/src/diablo2/d2game.obj differ diff --git a/build/Debug/src/diablo2/d2gfx.obj b/build/Debug/src/diablo2/d2gfx.obj new file mode 100644 index 0000000..7fc8a99 Binary files /dev/null and b/build/Debug/src/diablo2/d2gfx.obj differ diff --git a/build/Debug/src/diablo2/d2lang.obj b/build/Debug/src/diablo2/d2lang.obj new file mode 100644 index 0000000..8a3b248 Binary files /dev/null and b/build/Debug/src/diablo2/d2lang.obj differ diff --git a/build/Debug/src/diablo2/d2launch.obj b/build/Debug/src/diablo2/d2launch.obj new file mode 100644 index 0000000..ad30a48 Binary files /dev/null and b/build/Debug/src/diablo2/d2launch.obj differ diff --git a/build/Debug/src/diablo2/d2net.obj b/build/Debug/src/diablo2/d2net.obj new file mode 100644 index 0000000..6060389 Binary files /dev/null and b/build/Debug/src/diablo2/d2net.obj differ diff --git a/build/Debug/src/diablo2/d2win.obj b/build/Debug/src/diablo2/d2win.obj new file mode 100644 index 0000000..358adee Binary files /dev/null and b/build/Debug/src/diablo2/d2win.obj differ diff --git a/build/Debug/src/diablo2/fog.obj b/build/Debug/src/diablo2/fog.obj new file mode 100644 index 0000000..ec2641e Binary files /dev/null and b/build/Debug/src/diablo2/fog.obj differ diff --git a/build/Debug/src/diablo2/storm.obj b/build/Debug/src/diablo2/storm.obj new file mode 100644 index 0000000..a5fb1c2 Binary files /dev/null and b/build/Debug/src/diablo2/storm.obj differ diff --git a/build/Debug/src/diablo2/utils/mpq_ifstream.obj b/build/Debug/src/diablo2/utils/mpq_ifstream.obj new file mode 100644 index 0000000..831f0f0 Binary files /dev/null and b/build/Debug/src/diablo2/utils/mpq_ifstream.obj differ diff --git a/build/Debug/src/diablo2/utils/screen.obj b/build/Debug/src/diablo2/utils/screen.obj new file mode 100644 index 0000000..3ca3fd3 Binary files /dev/null and b/build/Debug/src/diablo2/utils/screen.obj differ diff --git a/build/Debug/src/main.obj b/build/Debug/src/main.obj new file mode 100644 index 0000000..4889dbf Binary files /dev/null and b/build/Debug/src/main.obj differ diff --git a/build/Debug/vc143.pdb b/build/Debug/vc143.pdb new file mode 100644 index 0000000..211a325 Binary files /dev/null and b/build/Debug/vc143.pdb differ diff --git a/build/Debug/vendor/D2Template/D2Template.obj b/build/Debug/vendor/D2Template/D2Template.obj new file mode 100644 index 0000000..e665d47 Binary files /dev/null and b/build/Debug/vendor/D2Template/D2Template.obj differ diff --git a/build/Debug/vendor/DllNotify/DllNotify.obj b/build/Debug/vendor/DllNotify/DllNotify.obj new file mode 100644 index 0000000..f8cbd63 Binary files /dev/null and b/build/Debug/vendor/DllNotify/DllNotify.obj differ diff --git a/build/Debug/vendor/minhook/src/buffer.obj b/build/Debug/vendor/minhook/src/buffer.obj new file mode 100644 index 0000000..4e194d9 Binary files /dev/null and b/build/Debug/vendor/minhook/src/buffer.obj differ diff --git a/build/Debug/vendor/minhook/src/hde/hde32.obj b/build/Debug/vendor/minhook/src/hde/hde32.obj new file mode 100644 index 0000000..3595617 Binary files /dev/null and b/build/Debug/vendor/minhook/src/hde/hde32.obj differ diff --git a/build/Debug/vendor/minhook/src/hde/hde64.obj b/build/Debug/vendor/minhook/src/hde/hde64.obj new file mode 100644 index 0000000..b42c94a Binary files /dev/null and b/build/Debug/vendor/minhook/src/hde/hde64.obj differ diff --git a/build/Debug/vendor/minhook/src/hook.obj b/build/Debug/vendor/minhook/src/hook.obj new file mode 100644 index 0000000..8908b76 Binary files /dev/null and b/build/Debug/vendor/minhook/src/hook.obj differ diff --git a/build/Debug/vendor/minhook/src/trampoline.obj b/build/Debug/vendor/minhook/src/trampoline.obj new file mode 100644 index 0000000..18df835 Binary files /dev/null and b/build/Debug/vendor/minhook/src/trampoline.obj differ diff --git a/include/common/Ini.h b/include/common/Ini.h new file mode 100644 index 0000000..7e4a0da --- /dev/null +++ b/include/common/Ini.h @@ -0,0 +1,205 @@ +/////////////////////////////////////////////////////////////////// +// Ini.h +// +// "CIni" is a simple API wrap class used for ini file access. +// The purpose of this class is to make ini file access more +// convenient than direct API calls. +// +// This file is distributed "as is" and without any expressed or implied +// warranties. The author holds no responsibilities for any possible damages +// or loss of data that are caused by use of this file. The user must assume +// the entire risk of using this file. +// +// 7/08/2002 Bin Liu +// +// Update history: +// +// 7/08/2002 -- Initial release. +// 7/14/2002 -- Added "IncreaseInt" and "AppendString" +// 9/02/2002 -- Added "removeProfileSection" and "RemoveProfileEntry" +// 2/09/2003 -- The class has been made unicode-compliant +// 11/04/2003 -- Integrated MFC support, added in new member functions +// for accessing arrays. +// 11/08/2003 -- Fixed "GetString" and "GetPathName" method, changed parameter +// from "LPSTR" to "LPTSTR" +// 11/10/2003 -- Renamed method "GetKeys" to "GetKeyLines", +// Added method "GetKeyNames" +// Added parameter "bTrimString" to method "GetArray" +// 11/14/2003 -- Use "__AFXWIN_H__" instead of "_AFXDLL" to determine MFC presence +// Removed length limit on "m_pszPathName" +// Removed "GetStruct" and "WriteStruct" +// Added "GetDataBlock" and "WriteDataBlock" +// Added "GetChar" and "WriteChar" +// 02/20/2004 -- Fixed a bug in "_TrimString". Thanks to yao_xuejun. +// +/////////////////////////////////////////////////////////////////// + +#ifndef __INI_H__ +#define __INI_H__ + +#include +#include + +// If MFC is linked, we will use CStringArray for great convenience +#ifdef __AFXWIN_H__ + #include +#endif + +// Number bases +#define BASE_BINARY 2 +#define BASE_OCTAL 8 +#define BASE_DECIMAL 10 +#define BASE_HEXADECIMAL 16 + +//--------------------------------------------------------------- +// Callback Function Type Definition +//--------------------------------------------------------------- +// The callback function used for parsing a "double-null terminated string". +// When called, the 1st parameter passed in will store the newly extracted sub +// string, the 2nd parameter is a 32-bit user defined data, this parameter can +// be NULL. The parsing will terminate if this function returns zero. To use +// the callback, function pointer needs to be passed to "CIni::ParseDNTString". +typedef BOOL (CALLBACK *SUBSTRPROC)(LPCTSTR, LPVOID); + +class CIni +{ +public: + + //----------------------------------------------------------- + // Constructors & Destructor + //----------------------------------------------------------- + CIni(); // Default constructor + CIni(LPCTSTR lpPathName); // Construct with a given file name + virtual ~CIni(); + + //----------------------------------------------------------- + // Ini File Path Name Access + //----------------------------------------------------------- + void SetPathName(LPCTSTR lpPathName); // Specify a new file name + DWORD GetPathName(LPTSTR lpBuffer, DWORD dwBufSize) const; // Retrieve current file name +#ifdef __AFXWIN_H__ + CString GetPathName() const; +#endif + + //------------------------------------------------------------ + // String Access + //------------------------------------------------------------ + DWORD GetString(LPCTSTR lpSection, LPCTSTR lpKey, LPTSTR lpBuffer, DWORD dwBufSize, LPCTSTR lpDefault = NULL) const; +#ifdef __AFXWIN_H__ + CString GetString(LPCTSTR lpSection, LPCTSTR lpKey, LPCTSTR lpDefault = NULL) const; +#endif + BOOL WriteString(LPCTSTR lpSection, LPCTSTR lpKey, LPCTSTR lpValue) const; + + // Read a string from the ini file, append it with another string then write it + // back to the ini file. + BOOL AppendString(LPCTSTR Section, LPCTSTR lpKey, LPCTSTR lpString) const; + + //------------------------------------------------------------ + // Ini File String Array Access + //------------------------------------------------------------ + // Parse the string retrieved from the ini file and split it into a set of sub strings. + DWORD GetArray(LPCTSTR lpSection, LPCTSTR lpKey, LPTSTR lpBuffer, DWORD dwBufSize, LPCTSTR lpDelimiter = NULL, BOOL bTrimString = TRUE) const; +#ifdef __AFXWIN_H__ + void GetArray(LPCTSTR lpSection, LPCTSTR lpKey, CStringArray* pArray, LPCTSTR lpDelimiter = NULL, BOOL bTrimString = TRUE) const; + BOOL WriteArray(LPCTSTR lpSection, LPCTSTR lpKey, const CStringArray* pArray, int nWriteCount = -1, LPCTSTR lpDelimiter = NULL) const; +#endif + + //------------------------------------------------------------ + // Primitive Data Type Access + //------------------------------------------------------------ + int GetInt(LPCTSTR lpSection, LPCTSTR lpKey, int nDefault, int nBase = BASE_DECIMAL) const; + BOOL WriteInt(LPCTSTR lpSection, LPCTSTR lpKey, int nValue, int nBase = BASE_DECIMAL) const; + BOOL IncreaseInt(LPCTSTR lpSection, LPCTSTR lpKey, int nIncrease = 1, int nBase = BASE_DECIMAL) const; + + UINT GetUInt(LPCTSTR lpSection, LPCTSTR lpKey, UINT nDefault, int nBase = BASE_DECIMAL) const; + BOOL WriteUInt(LPCTSTR lpSection, LPCTSTR lpKey, UINT nValue, int nBase = BASE_DECIMAL) const; + BOOL IncreaseUInt(LPCTSTR lpSection, LPCTSTR lpKey, UINT nIncrease = 1, int nBase = BASE_DECIMAL) const; + + BOOL GetBool(LPCTSTR lpSection, LPCTSTR lpKey, BOOL bDefault) const; + BOOL WriteBool(LPCTSTR lpSection, LPCTSTR lpKey, BOOL bValue) const; + BOOL InvertBool(LPCTSTR lpSection, LPCTSTR lpKey) const; + + double GetDouble(LPCTSTR lpSection, LPCTSTR lpKey, double fDefault) const; + BOOL WriteDouble(LPCTSTR lpSection, LPCTSTR lpKey, double fValue, int nPrecision = -1) const; + BOOL IncreaseDouble(LPCTSTR lpSection, LPCTSTR lpKey, double fIncrease, int nPrecision = -1) const; + + TCHAR GetChar(LPCTSTR lpSection, LPCTSTR lpKey, TCHAR cDefault) const; + BOOL WriteChar(LPCTSTR lpSection, LPCTSTR lpKey, TCHAR c) const; + + //------------------------------------------------------------ + // User-Defined Data Type & Data Block Access + //------------------------------------------------------------ + POINT GetPoint(LPCTSTR lpSection, LPCTSTR lpKey, POINT ptDefault) const; + BOOL WritePoint(LPCTSTR lpSection, LPCTSTR lpKey, POINT pt) const; + + RECT GetRect(LPCTSTR lpSection, LPCTSTR lpKey, RECT rcDefault) const; + BOOL WriteRect(LPCTSTR lpSection, LPCTSTR lpKey, RECT rc) const; + + DWORD GetDataBlock(LPCTSTR lpSection, LPCTSTR lpKey, LPVOID lpBuffer, DWORD dwBufSize, DWORD dwOffset = 0) const; + BOOL WriteDataBlock(LPCTSTR lpSection, LPCTSTR lpKey, LPCVOID lpData, DWORD dwDataSize) const; + BOOL AppendDataBlock(LPCTSTR lpSection, LPCTSTR lpKey, LPCVOID lpData, DWORD dwDataSize) const; + + //------------------------------------------------------------ + // Section Operations + //------------------------------------------------------------ + BOOL IsSectionExist(LPCTSTR lpSection) const; + DWORD GetSectionNames(LPTSTR lpBuffer, DWORD dwBufSize) const; +#ifdef __AFXWIN_H__ + void GetSectionNames(CStringArray* pArray) const; +#endif + BOOL CopySection(LPCTSTR lpSrcSection, LPCTSTR lpDestSection, BOOL bFailIfExist) const; + BOOL MoveSection(LPCTSTR lpSrcSection, LPCTSTR lpDestSection, BOOL bFailIfExist = TRUE) const; + BOOL DeleteSection(LPCTSTR lpSection) const; + + //------------------------------------------------------------ + // Key Operations + //------------------------------------------------------------ + BOOL IsKeyExist(LPCTSTR lpSection, LPCTSTR lpKey) const; + DWORD GetKeyLines(LPCTSTR lpSection, LPTSTR lpBuffer, DWORD dwBufSize) const; +#ifdef __AFXWIN_H__ + void GetKeyLines(LPCTSTR lpSection, CStringArray* pArray) const; +#endif + DWORD GetKeyNames(LPCTSTR lpSection, LPTSTR lpBuffer, DWORD dwBufSize) const; +#ifdef __AFXWIN_H__ + void GetKeyNames(LPCTSTR lpSection, CStringArray* pArray) const; +#endif + BOOL CopyKey(LPCTSTR lpSrcSection, LPCTSTR lpSrcKey, LPCTSTR lpDestSection, LPCTSTR lpDestKey, BOOL bFailIfExist) const; + BOOL MoveKey(LPCTSTR lpSrcSection, LPCTSTR lpSrcKey, LPCTSTR lpDestSection, LPCTSTR lpDestKey, BOOL bFailIfExist = TRUE) const; + BOOL DeleteKey(LPCTSTR lpSection, LPCTSTR lpKey) const; + + //------------------------------------------------------------ + // Parse a "Double-Null Terminated String" + //------------------------------------------------------------ + static BOOL ParseDNTString(LPCTSTR lpString, SUBSTRPROC lpFnStrProc, LPVOID lpParam = NULL); + + //------------------------------------------------------------ + // Check for Whether a String Representing TRUE or FALSE + //------------------------------------------------------------ + static BOOL StringToBool(LPCTSTR lpString, BOOL bDefault = FALSE); + +protected: + + //------------------------------------------------------------ + // Helper Functions + //------------------------------------------------------------ + static LPTSTR __StrDupEx(LPCTSTR lpStart, LPCTSTR lpEnd); + static BOOL __TrimString(LPTSTR lpBuffer); + LPTSTR __GetStringDynamic(LPCTSTR lpSection, LPCTSTR lpKey, LPCTSTR lpDefault = NULL) const; + static DWORD __StringSplit(LPCTSTR lpString, LPTSTR lpBuffer, DWORD dwBufSize, LPCTSTR lpDelimiter = NULL, BOOL bTrimString = TRUE); + static void __ToBinaryString(UINT nNumber, LPTSTR lpBuffer, DWORD dwBufSize); + static int __ValidateBase(int nBase); + static void __IntToString(int nNumber, LPTSTR lpBuffer, int nBase); + static void __UIntToString(UINT nNumber, LPTSTR lpBuffer, int nBase); + static BOOL CALLBACK __SubStrCompare(LPCTSTR lpString1, LPVOID lpParam); + static BOOL CALLBACK __KeyPairProc(LPCTSTR lpString, LPVOID lpParam); +#ifdef __AFXWIN_H__ + static BOOL CALLBACK __SubStrAdd(LPCTSTR lpString, LPVOID lpParam); +#endif + + //------------------------------------------------------------ + // Member Data + //------------------------------------------------------------ + LPTSTR m_pszPathName; // Stores path of the associated ini file +}; + +#endif // #ifndef __INI_H__ \ No newline at end of file diff --git a/include/common/asm_code.h b/include/common/asm_code.h new file mode 100644 index 0000000..a839909 --- /dev/null +++ b/include/common/asm_code.h @@ -0,0 +1,178 @@ +#pragma once + +#include +#include +#include + +namespace details { + class asm_address { + intptr_t m_full_offset = 0; + public: + virtual ~asm_address() = default; + + intptr_t get_full_offset() const { + return m_full_offset; + } + + void set_full_offset(const intptr_t full_offset) { + m_full_offset = full_offset; + } + + virtual void build(unsigned char* code) = 0; + + virtual intptr_t get_offset() const = 0; + virtual intptr_t get() const = 0; + }; +} + +class asm_address_static final : public details::asm_address { + intptr_t m_offset; + intptr_t m_address; +public: + /** + * \brief + * \param offset relative to current instruction + * \param address + */ + asm_address_static(intptr_t offset, void* address) { + m_offset = offset; + m_address = reinterpret_cast(address); + } + + void build(unsigned char* code) override { + *reinterpret_cast(code + get_full_offset() + m_offset) = m_address; + } + + intptr_t get_offset() const override { + return m_offset; + } + + intptr_t get() const override { + return m_address; + } +}; + +class asm_address_relative : public details::asm_address { + intptr_t m_offset; + intptr_t m_instruction_size; + intptr_t m_to_address; + + intptr_t m_address; +public: + /** + * \brief + * \param offset relative to current instruction + * \param instructionSize full size of instruction + * \param toAddress + */ + asm_address_relative(intptr_t offset, size_t instructionSize, void* toAddress) { + m_offset = offset; + m_instruction_size = instructionSize; + m_to_address = reinterpret_cast(toAddress); + + m_address = 0; + } + + void build(unsigned char* code) override { + const auto fromAddress = reinterpret_cast(code + get_full_offset()); + *reinterpret_cast(code + get_full_offset() + m_offset) = + m_to_address - fromAddress - m_instruction_size; + } + + intptr_t get_offset() const override { + return m_offset; + } + + intptr_t get() const override { + return m_address; + } +}; + +class asm_code { + const size_t m_growth_factor = 2; + + unsigned char* m_code; + unsigned char* m_buffer; + size_t m_buffer_size; + intptr_t m_offset; + + std::vector m_addresses; +public: + explicit asm_code(const size_t initSize = 0) { + m_code = nullptr; + m_buffer = nullptr; + m_buffer_size = initSize; + m_offset = 0; + + if (initSize != 0) + m_buffer = new unsigned char[initSize](); + } + + ~asm_code() { + delete[] m_buffer; + + for (auto addr : m_addresses) + delete addr; + } + + template + void add(int const(&code)[Len], details::asm_address* address = nullptr) { + if (m_code) + return; + + if (Len == 0) + return; + + ensure_buffer_size(Len); + + const auto startOffset = m_offset; + + for (size_t i = 0; i < Len; i++) + m_buffer[m_offset++] = static_cast(code[i]); + + if (!address) + return; + + //scratch some space in buffer for address + ensure_buffer_size(Len + sizeof(intptr_t)); + m_offset += sizeof(intptr_t); + + address->set_full_offset(startOffset); + m_addresses.push_back(address); + } + + size_t get_code_size() const { + return m_offset; + } + + unsigned char* get_code() { + if (!m_code) + build(); + + return m_code; + } + + void build(); +private: + void ensure_buffer_size(size_t size) { + if (m_buffer_size - m_offset > size) + return; + + auto newSize = m_buffer_size; + + if (newSize == 0) + newSize = 1; + + do { + newSize *= m_growth_factor; + } while (size > newSize); + + const auto newBuffer = new unsigned char[newSize](); + memcpy(newBuffer, m_buffer, m_buffer_size); + + delete[] m_buffer; + + m_buffer_size = newSize; + m_buffer = newBuffer; + } +}; \ No newline at end of file diff --git a/include/common/autopickup_lootfilter.h b/include/common/autopickup_lootfilter.h new file mode 100644 index 0000000..600122f --- /dev/null +++ b/include/common/autopickup_lootfilter.h @@ -0,0 +1,50 @@ +#pragma once +#include + +#define MAX_STRING_LENGHT 65536 + +enum D2ItemQuality +{ + ITEMQUALITY_CRACKED = 1, + ITEMQUALITY_NORMAL = 2, + ITEMQUALITY_SUPERIOR = 3, + ITEMQUALITY_MAGIC = 4, + ITEMQUALITY_SET = 5, + ITEMQUALITY_RARE = 6, + ITEMQUALITY_UNIQUE = 7, + ITEMQUALITY_CRAFTED = 8, + ITEMQUALITY_TEMPERED = 9 +}; + +struct item_code { + char code0; + char code1; + char code2; + char code3; + uint32_t qualityinclude[10] = { 0 }; +}; + +struct item_type { + uint32_t dwtype; + uint32_t qualityinclude[10] = { 0 }; +}; + +struct recipe { + item_code input1_code; + item_code input2_code; + item_code input3_code; + item_code input4_code; + item_code input5_code; + item_code input6_code; + item_code input7_code; + item_type input1_type; + item_type input2_type; + item_type input3_type; + item_type input4_type; + item_type input5_type; + item_type input6_type; + item_type input7_type; + uint32_t repeat_count; + bool back_to_inventory; + bool auto_transmute; +}; diff --git a/include/common/config.h b/include/common/config.h new file mode 100644 index 0000000..ba9e008 --- /dev/null +++ b/include/common/config.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +class config : public singleton { + nlohmann::json* m_json; + + bool m_unlock_fps; + bool m_prevent_minimize; + uint32_t m_gold_pickup_range; + +public: + explicit config(token); +}; \ No newline at end of file diff --git a/include/common/enum_helper.h b/include/common/enum_helper.h new file mode 100644 index 0000000..672f637 --- /dev/null +++ b/include/common/enum_helper.h @@ -0,0 +1,9 @@ +#pragma once + +template +class enum_helper { +public: + static constexpr const char* to_string(TEnum value) { + return __FUNCSIG__; + } +}; \ No newline at end of file diff --git a/include/common/hooking.h b/include/common/hooking.h new file mode 100644 index 0000000..5cf2b85 --- /dev/null +++ b/include/common/hooking.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +namespace hooking { + enum mh_status_t { + // Unknown error. Should not be returned. + MH_UNKNOWN = -1, + + // Successful. + MH_OK = 0, + + // MinHook is already initialized. + MH_ERROR_ALREADY_INITIALIZED, + + // MinHook is not initialized yet, or already uninitialized. + MH_ERROR_NOT_INITIALIZED, + + // The hook for the specified target function is already created. + MH_ERROR_ALREADY_CREATED, + + // The hook for the specified target function is not created yet. + MH_ERROR_NOT_CREATED, + + // The hook for the specified target function is already enabled. + MH_ERROR_ENABLED, + + // The hook for the specified target function is not enabled yet, or already + // disabled. + MH_ERROR_DISABLED, + + // The specified pointer is invalid. It points the address of non-allocated + // and/or non-executable region. + MH_ERROR_NOT_EXECUTABLE, + + // The specified target function cannot be hooked. + MH_ERROR_UNSUPPORTED_FUNCTION, + + // Failed to allocate memory. + MH_ERROR_MEMORY_ALLOC, + + // Failed to change the memory protection. + MH_ERROR_MEMORY_PROTECT, + + // The specified module is not loaded. + MH_ERROR_MODULE_NOT_FOUND, + + // The specified function is not found. + MH_ERROR_FUNCTION_NOT_FOUND + }; + + namespace details { + mh_status_t hook(void* target, void* detour, void** original); + } + + template + mh_status_t hook(void* target, void* detour, TOrig** original) { + return details::hook(target, detour, reinterpret_cast(original)); + } + + template + mh_status_t hook(void* base, void* detour, TOrig** original) { + auto fn = GetProcAddress(reinterpret_cast(base), + reinterpret_cast(TOrdinal)); + + return hook(fn, detour, original); + } + + intptr_t get_executable_memory(void* origin, size_t size); + void* set_call(void* address, void* function, size_t stubSize = 7); + void* set_jmp(void* address, void* function, size_t stubSize = 7); + void* get_call(void* address); +} diff --git a/include/common/ptr_wrapper.h b/include/common/ptr_wrapper.h new file mode 100644 index 0000000..bd5e965 --- /dev/null +++ b/include/common/ptr_wrapper.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include + +namespace details { + template + class wrap_func_cdecl_ { + void* m_function_ptr; + public: + explicit wrap_func_cdecl_(uintptr_t ptr, void* base) { + m_function_ptr = static_cast(base) + ptr; + } + + TRet operator()(Args... args) { + return reinterpret_cast(m_function_ptr)(args...); + } + }; + + template + class wrap_func_std_ { + void* m_function_ptr; + public: + explicit wrap_func_std_(uintptr_t ptr, void* base) { + m_function_ptr = static_cast(base) + ptr; + } + + TRet operator()(Args... args) { + return reinterpret_cast(m_function_ptr)(args...); + } + }; + + template + class wrap_func_cdecl_import_ { + void* m_base; + void* m_function_ptr; + uint32_t m_ordinal; + public: + explicit wrap_func_cdecl_import_(uint32_t ordinal, void* base) { + m_base = base; + m_function_ptr = nullptr; + m_ordinal = ordinal; + } + + TRet operator()(Args... args) { + if (!m_function_ptr) { + m_function_ptr = reinterpret_cast(GetProcAddress(reinterpret_cast(m_base), + reinterpret_cast(m_ordinal))); + } + + return reinterpret_cast(m_function_ptr)(args...); + } + }; + + template + class wrap_func_std_import_ { + void* m_base; + void* m_function_ptr; + uint32_t m_ordinal; + public: + explicit wrap_func_std_import_(uint32_t ordinal, void* base) { + m_base = base; + m_function_ptr = nullptr; + m_ordinal = ordinal; + } + + TRet operator()(Args... args) { + if (!m_function_ptr) { + m_function_ptr = reinterpret_cast(GetProcAddress(reinterpret_cast(m_base), + reinterpret_cast(m_ordinal))); + } + + return reinterpret_cast(m_function_ptr)(args...); + } + }; + + template + class wrap_func_fast_import_ { + void* m_base; + void* m_function_ptr; + uint32_t m_ordinal; + public: + explicit wrap_func_fast_import_(uint32_t ordinal, void* base) { + m_base = base; + m_function_ptr = nullptr; + m_ordinal = ordinal; + } + + TRet operator()(Args... args) { + if (!m_function_ptr) { + m_function_ptr = reinterpret_cast(GetProcAddress(reinterpret_cast(m_base), + reinterpret_cast(m_ordinal))); + } + + return reinterpret_cast(m_function_ptr)(args...); + } + }; + + template + class wrap_func_fast_ { + void* m_function_ptr; + public: + explicit wrap_func_fast_(uintptr_t ptr, void* base) { + m_function_ptr = static_cast(base) + ptr; + } + + TRet operator()(Args... args) { + return reinterpret_cast(m_function_ptr)(args...); + } + }; +} + +template +class wrap_func_cdecl {}; + +template +class wrap_func_cdecl : public details::wrap_func_cdecl_ { +public: + wrap_func_cdecl(uintptr_t ptr, void* base) : wrap_func_cdecl_(ptr, base) {}; +}; + +template +class wrap_func_cdecl_import {}; + +template +class wrap_func_cdecl_import : public details::wrap_func_cdecl_import_ { +public: + wrap_func_cdecl_import(uint32_t ordinal, void* base) : wrap_func_cdecl_import_(ordinal, base) {}; +}; + +template +class wrap_func_std {}; + +template +class wrap_func_std : public details::wrap_func_std_ { +public: + wrap_func_std(uintptr_t ptr, void* base) : wrap_func_std_(ptr, base) {}; +}; + +template +class wrap_func_std_import {}; + +template +class wrap_func_std_import : public details::wrap_func_std_import_ { +public: + wrap_func_std_import(uint32_t ordinal, void* base) : wrap_func_std_import_(ordinal, base) {}; +}; + +template +class wrap_func_fast_import {}; + +template +class wrap_func_fast_import : public details::wrap_func_fast_import_ { +public: + wrap_func_fast_import(uint32_t ordinal, void* base) : wrap_func_fast_import_(ordinal, base) {}; +}; + +template +class wrap_func_fast {}; + +template +class wrap_func_fast : public details::wrap_func_fast_ { +public: + wrap_func_fast(uintptr_t ptr, void* base) : wrap_func_fast_(ptr, base) {}; +}; + +template +class wrap_value { + TType* m_value; +public: + wrap_value(uintptr_t ptr, void* base) { + // Fuck off, I'm gonna cast it in C-style because of templates + // ReSharper disable once CppCStyleCast + m_value = (TType*)(static_cast(base) + ptr); + } + + operator void* () const { + return m_value; + } + + operator TType* () const { + return m_value; + } +}; \ No newline at end of file diff --git a/include/common/string_utils.h b/include/common/string_utils.h new file mode 100644 index 0000000..9790522 --- /dev/null +++ b/include/common/string_utils.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace string_utils { + std::wstring string_to_wstring(const std::string& str); +} diff --git a/include/d2tweaks/client/client.h b/include/d2tweaks/client/client.h new file mode 100644 index 0000000..7ec9961 --- /dev/null +++ b/include/d2tweaks/client/client.h @@ -0,0 +1,73 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include + +// Define the structure to hold stat information +struct StatEntry { + std::wstring stat_display_string; + diablo2::ui_color_t colorStat, colorStatValue; + int x1, y1, x2, y2, is_item_stat, item_type_id, stat = 0; // x1,y1 stat_display_string | x2,y2 statValue +}; + +extern std::vector globalStatsVector; // Declaration of the global variable + +extern diablo2::structures::gfxdata g_gfxdata; // global gfxdata + +extern int randStat; +extern int randStatRangeLow; +extern int randStatRangeHigh; +extern int randStatBool; + +namespace diablo2 { + namespace structures { + struct unit; + } +} + +namespace d2_tweaks { + namespace common { + struct packet_header; + enum packet_types_cs_t; + enum message_types_t; + } + + namespace client { + namespace modules { + class client_module; + } + + class client : public singleton { + uint8_t m_module_id_counter; + uint8_t m_tick_handler_id_counter; + modules::client_module* m_modules[0xFF]{ nullptr }; //max 255 modules atm. + modules::client_module* m_tick_handlers[0xFF]{ nullptr }; //max 255 handlers + modules::client_module* m_packet_handlers[0xFF]{ nullptr }; //max 255 handlers because of one-byte packet header + modules::client_module* m_packet_cs_handlers[0xFF]{ nullptr }; //max 255 handlers because of one-byte packet header + public: + explicit client(token); + + void init(); + void register_module(modules::client_module* module); + + void register_tick_handler(modules::client_module* module); + void register_packet_handler(common::message_types_t type, modules::client_module* module); + void register_packet_cs_handler(common::packet_types_cs_t packet, common::message_types_t type, modules::client_module* module); + static diablo2::structures::unit* get_client_unit(uint32_t type, uint32_t guid); + + private: + //static void __fastcall game_loop_start(); + static void __fastcall handle_standart_packet(common::packet_header* packet, size_t size); + static void __fastcall handle_cs_packet(common::packet_header* packet, size_t size); + static void __fastcall handle_packet(common::packet_header* packet, size_t size); + static void __fastcall game_tick(); + static int32_t __stdcall draw_game_ui(); + }; + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.h b/include/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.h new file mode 100644 index 0000000..0f28162 --- /dev/null +++ b/include/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace ui { + namespace controls { + class label; + } + } + + namespace client { + namespace modules { + class auto_gold_pickup final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + void tick() override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.h b/include/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.h new file mode 100644 index 0000000..a0a7a75 --- /dev/null +++ b/include/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace client { + namespace modules { + class auto_item_pickup final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + void tick() override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/autosort/autosort_client.h b/include/d2tweaks/client/modules/autosort/autosort_client.h new file mode 100644 index 0000000..687b7b9 --- /dev/null +++ b/include/d2tweaks/client/modules/autosort/autosort_client.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +//Inventory auto sort module client side + +namespace d2_tweaks { + namespace client { + namespace modules { + class autosort final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/client_module.h b/include/d2tweaks/client/modules/client_module.h new file mode 100644 index 0000000..28cc021 --- /dev/null +++ b/include/d2tweaks/client/modules/client_module.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#define MODULE_INIT(module_name) static d2_tweaks::client::modules::module_name g_instance; + +namespace d2_tweaks { + namespace common { + struct packet_header; + } + + namespace client { + namespace modules { + class client_module { + public: + virtual ~client_module() = default; + client_module(); + + virtual void init() = 0; + virtual void init_early() = 0; + virtual void draw_ui(); + virtual void tick(); + virtual void handle_packet(common::packet_header* packet); + virtual void handle_cs_packet(common::packet_header* packet); + }; + } + } +} diff --git a/include/d2tweaks/client/modules/damage_display/damage_display_client.h b/include/d2tweaks/client/modules/damage_display/damage_display_client.h new file mode 100644 index 0000000..a2d3f30 --- /dev/null +++ b/include/d2tweaks/client/modules/damage_display/damage_display_client.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +//Display damage client side + +namespace d2_tweaks { + namespace client { + namespace modules { + class damage_display final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + void tick() override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/item_drop_message/item_drop_message_client.h b/include/d2tweaks/client/modules/item_drop_message/item_drop_message_client.h new file mode 100644 index 0000000..3526524 --- /dev/null +++ b/include/d2tweaks/client/modules/item_drop_message/item_drop_message_client.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace client { + namespace modules { + class item_drop_message final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + static void GamePacketReceivedInterceptASM(); + static void __fastcall GamePacketReceivedIntercept(uint8_t* packet, size_t size); + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/item_move/item_move_client.h b/include/d2tweaks/client/modules/item_move/item_move_client.h new file mode 100644 index 0000000..3bdc100 --- /dev/null +++ b/include/d2tweaks/client/modules/item_move/item_move_client.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +//Item moving between inventory pages (cube, inventory and stash) by ctrl+click client side + +namespace d2_tweaks { + namespace client { + namespace modules { + class item_move final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/loot_filter/loot_filter.h b/include/d2tweaks/client/modules/loot_filter/loot_filter.h new file mode 100644 index 0000000..1b7ac99 --- /dev/null +++ b/include/d2tweaks/client/modules/loot_filter/loot_filter.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace client { + namespace modules { + class loot_filter final : public client_module { + public: + void init() override; + void init_early() override; + }; + } + } +} diff --git a/include/d2tweaks/client/modules/loot_filter/loot_filter_settings.h b/include/d2tweaks/client/modules/loot_filter/loot_filter_settings.h new file mode 100644 index 0000000..b8bc33a --- /dev/null +++ b/include/d2tweaks/client/modules/loot_filter/loot_filter_settings.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +namespace d2_tweaks { + namespace client { + namespace modules { + struct loot_filter_settings { + size_t size; //struct size + + bool alt_only; + bool show_gold; + bool show_runes; + bool show_gems; + + bool quality_settings[static_cast(diablo2::structures::item_quality_t::ITEM_QUALITY_COUNT)]; + + char reserved[1004]; + + static loot_filter_settings& get(); + + static void save(const char* name); + static void load(const char* name); + static void remove(const char* name); + + private: + loot_filter_settings() : size(sizeof(loot_filter_settings)), + alt_only(false), show_gold(true), show_runes(true), show_gems(true), reserved{} + { + memset(quality_settings, 0x1, sizeof quality_settings); + } + }; + } + } +} diff --git a/include/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.h b/include/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.h new file mode 100644 index 0000000..02e5bc9 --- /dev/null +++ b/include/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.h @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include + +namespace diablo2 { + namespace structures { + struct unit; + enum class item_quality_t : unsigned; + } +} + +namespace d2_tweaks { + namespace ui { + namespace controls { + class checkbox; + } + } + + namespace client { + namespace modules { + class loot_filter_settings_menu final : public ui::menu, singleton { + ui::controls::checkbox* m_altonly; + ui::controls::checkbox* m_show_gold; + ui::controls::checkbox* m_show_runes; + ui::controls::checkbox* m_show_gems; + + void(__fastcall* m_draw_dropped_items_names_original)(void*, void*); + void(__fastcall* m_handle_dropped_items_original)(void*, void*); + public: + explicit loot_filter_settings_menu(token); + + void reload_settings(); + + void draw() override; + private: + void register_misc_checkboxes(); + void register_quality_checkboxes(); + + void update_alt_only(bool value); + void update_show_gold(bool value); + void update_show_runes(bool value); + void update_show_gems(bool value); + + void update_quality_allowance(bool value, diablo2::structures::item_quality_t quality); + void register_quality_checkbox(const std::string& name, diablo2::structures::item_quality_t quality); + + void setup_hooks(); + void setup_alt_hook() const; + + static bool is_gold(diablo2::structures::unit* item); + static bool is_rune(diablo2::structures::unit* item); + static bool is_gem(diablo2::structures::unit* item); + + static bool __fastcall check_alt_item(diablo2::structures::unit* unit); + + //draw labels over dropped items + static void __fastcall draw_dropped_items_names(diablo2::structures::unit* unit, void* edx); + + //handle hovering over item and actual click + static void __fastcall handle_dropped_items(diablo2::structures::unit* unit, void* edx); + }; + + } + } +} diff --git a/include/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.h b/include/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.h new file mode 100644 index 0000000..cf5d80b --- /dev/null +++ b/include/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace d2_tweaks { + namespace ui { + namespace controls { + class button; + } + } +} + +namespace d2_tweaks { + namespace client { + namespace modules { + class loot_filter_settings_toggle_menu final : public ui::menu, singleton { + ui::controls::button* m_toggle_filter_settings_btn; + menu* m_filter_settings_menu; + bool m_show; + public: + explicit loot_filter_settings_toggle_menu(token); + + void toggle_filter_settings_click(); + + void draw() override; + + bool key_event(uint32_t key, bool up) override; + }; + } + } +} diff --git a/include/d2tweaks/client/modules/small_patches/small_patches.h b/include/d2tweaks/client/modules/small_patches/small_patches.h new file mode 100644 index 0000000..98597d7 --- /dev/null +++ b/include/d2tweaks/client/modules/small_patches/small_patches.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +//Client side patches that are too small to implement as separate modules + +namespace d2_tweaks { + namespace client { + namespace modules { + class small_patches final : public client_module { + public: + void init() override; + void init_early() override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/test/test.h b/include/d2tweaks/client/modules/test/test.h new file mode 100644 index 0000000..db19473 --- /dev/null +++ b/include/d2tweaks/client/modules/test/test.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +//Test client side module + +namespace d2_tweaks { + namespace client { + namespace modules { + class test final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/trader_update/trader_update_client.h b/include/d2tweaks/client/modules/trader_update/trader_update_client.h new file mode 100644 index 0000000..5d26989 --- /dev/null +++ b/include/d2tweaks/client/modules/trader_update/trader_update_client.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace d2_tweaks { + namespace client { + namespace modules { + class trader_update final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + void handle_cs_packet(common::packet_header* packet) override; + void tick() override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/client/modules/transmute/transmute_client.h b/include/d2tweaks/client/modules/transmute/transmute_client.h new file mode 100644 index 0000000..af4930d --- /dev/null +++ b/include/d2tweaks/client/modules/transmute/transmute_client.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace d2_tweaks { + namespace client { + namespace modules { + class transmute final : public client_module { + public: + void init() override; + void init_early() override; + void handle_packet(common::packet_header* packet) override; + void tick() override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/common/asset.h b/include/d2tweaks/common/asset.h new file mode 100644 index 0000000..e8190ea --- /dev/null +++ b/include/d2tweaks/common/asset.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace common { + enum mpq_file_type_t; + + class asset final { + std::string m_path; + void* m_asset; + mpq_file_type_t m_type; + public: + explicit asset(const std::string& path, void* asset, mpq_file_type_t type) : m_path(path), m_asset(asset), m_type(type) {} + + const std::string& get_path() const { + return m_path; + } + + void* get() const { + return m_asset; + } + + mpq_file_type_t get_type() const { + return m_type; + } + + void update(void* asset) { + m_asset = asset; + } + }; + } +} \ No newline at end of file diff --git a/include/d2tweaks/common/asset_manager.h b/include/d2tweaks/common/asset_manager.h new file mode 100644 index 0000000..adde480 --- /dev/null +++ b/include/d2tweaks/common/asset_manager.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include +#include +#include + +namespace d2_tweaks { + namespace common { + class asset; + + enum mpq_file_type_t { + MPQ_FILE_TYPE_UNKNOWN = -1, + + MPQ_FILE_TYPE_DC6 = 0, + MPQ_FILE_TYPE_DCC = 1, + + MPQ_FILE_TYPE_COUNT + }; + + class asset_manager : public singleton { + std::unordered_map m_assets; + public: + explicit asset_manager(token); + + void init(); + + asset* get_mpq_file(const std::string& path, mpq_file_type_t type); + private: + void* load_asset_data(const std::string& path, mpq_file_type_t type); + static int32_t __stdcall reload(); + }; + } +} diff --git a/include/d2tweaks/common/common.h b/include/d2tweaks/common/common.h new file mode 100644 index 0000000..95bb1e6 --- /dev/null +++ b/include/d2tweaks/common/common.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace common { + struct packet_header; + + class common : public singleton { + public: + explicit common(token); + + void init(); + + bool get_packet_size_cs(packet_header* packet, size_t& size); + bool get_packet_size_sc(packet_header* packet, size_t& size); + }; + } +} \ No newline at end of file diff --git a/include/d2tweaks/common/protocol.h b/include/d2tweaks/common/protocol.h new file mode 100644 index 0000000..b919d16 --- /dev/null +++ b/include/d2tweaks/common/protocol.h @@ -0,0 +1,391 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace common { +#pragma pack(push,1) +#define MAX_MSG_SIZE = 0x204 + + enum packet_types_cs_t { + PACKET_0x00, + PACKET_0x01, + PACKET_0x02, + PACKET_0x03, + PACKET_0x04, + PACKET_0x05, + PACKET_0x06, + PACKET_0x07, + PACKET_0x08, + PACKET_0x09, + PACKET_0x0A, + PACKET_0x0B, + PACKET_0x0C, + PACKET_0x0D, + PACKET_0x0E, + PACKET_0x0F, + PACKET_0x10, + PACKET_0x11, + PACKET_0x12, + PACKET_0x13, + PACKET_0x14, + PACKET_0x15, + PACKET_0x16, + PACKET_0x17, + PACKET_0x18, + PACKET_0x19, + PACKET_0x1A, + PACKET_0x1B, + PACKET_0x1C, + PACKET_0x1D, + PACKET_0x1E, + PACKET_0x1F, + PACKET_0x20, + PACKET_0x21, + PACKET_0x22, + PACKET_0x23, + PACKET_0x24, + PACKET_0x25, + PACKET_0x26, + PACKET_0x27, + PACKET_0x28, + PACKET_0x29, + PACKET_0x2A, + PACKET_0x2B, + PACKET_0x2C, + PACKET_0x2D, + PACKET_0x2E, + PACKET_0x2F, + PACKET_0x30, + PACKET_0x31, + PACKET_0x32, + PACKET_0x33, + PACKET_0x34, + PACKET_0x35, + PACKET_0x36, + PACKET_0x37, + PACKET_0x38, + PACKET_0x39, + PACKET_0x3A, + PACKET_0x3B, + PACKET_0x3C, + PACKET_0x3D, + PACKET_0x3E, + PACKET_0x3F, + PACKET_0x40, + PACKET_0x41, + PACKET_0x42, + PACKET_0x43, + PACKET_0x44, + PACKET_0x45, + PACKET_0x46, + PACKET_0x47, + PACKET_0x48, + PACKET_0x49, + PACKET_0x4A, + PACKET_0x4B, + PACKET_0x4C, + PACKET_0x4D, + PACKET_0x4E, + PACKET_0x4F, + PACKET_0x50, + PACKET_0x51, + PACKET_0x52, + PACKET_0x53, + PACKET_0x54, + PACKET_0x55, + PACKET_0x56, + PACKET_0x57, + PACKET_0x58, + PACKET_0x59, + PACKET_0x5A, + PACKET_0x5B, + PACKET_0x5C, + PACKET_0x5D, + PACKET_0x5E, + PACKET_0x5F, + PACKET_0x60, + PACKET_0x61, + PACKET_0x62, + PACKET_0x63, + PACKET_0x64, + PACKET_0x65, + PACKET_0x66, + PACKET_0x67, + PACKET_0x68, + PACKET_0x69, + PACKET_0x6A, + PACKET_0x6B, + PACKET_0x6C, + PACKET_0x6D, + PACKET_0x6E, + PACKET_0x6F, + PACKET_0x70, + PACKET_0x71, + PACKET_0x72, + PACKET_0x73, + PACKET_0x74, + PACKET_0x75, + PACKET_0x76, + PACKET_0x77, + PACKET_0x78, + PACKET_0x79, + PACKET_0x7A, + PACKET_0x7B, + PACKET_0x7C, + PACKET_0x7D, + PACKET_0x7E, + PACKET_0x7F, + PACKET_0x80, + PACKET_0x81, + PACKET_0x82, + PACKET_0x83, + PACKET_0x84, + PACKET_0x85, + PACKET_0x86, + PACKET_0x87, + PACKET_0x88, + PACKET_0x89, + PACKET_0x8A, + PACKET_0x8B, + PACKET_0x8C, + PACKET_0x8D, + PACKET_0x8E, + PACKET_0x8F, + PACKET_0x90, + PACKET_0x91, + PACKET_0x92, + PACKET_0x93, + PACKET_0x94, + PACKET_0x95, + PACKET_0x96, + PACKET_0x97, + PACKET_0x98, + PACKET_0x99, + PACKET_0x9A, + PACKET_0x9B, + PACKET_0x9C, + PACKET_0x9D, + PACKET_0x9E, + PACKET_0x9F, + PACKET_0xA0, + PACKET_0xA1, + PACKET_0xA2, + PACKET_0xA3, + PACKET_0xA4, + PACKET_0xA5, + PACKET_0xA6, + PACKET_0xA7, + PACKET_0xA8, + PACKET_0xA9, + PACKET_0xAA, + PACKET_0xAB, + PACKET_0xAC, + PACKET_0xAD, + PACKET_0xAE, + PACKET_0xAF, + PACKET_0xB0, + PACKET_0xB1, + PACKET_0xB2, + PACKET_0xB3, + PACKET_0xB4 + }; + + enum message_types_t { + MESSAGE_TYPE_ITEM_MOVE = 1, + MESSAGE_TYPE_INVENTORY_SORT, + MESSAGE_TYPE_DAMAGE_INFO, + MESSAGE_TYPE_GOLD_PICKUP_INFO, + MESSAGE_TYPE_ITEM_PICKUP_INFO, + MESSAGE_TYPE_ITEM_DROPPED_INFO, + MESSAGE_TYPE_TRANSMUTE, + MESSAGE_TYPE_TRADER_UPDATE, + + MESSAGE_TYPE_COUNT + }; + + enum damage_type_t : uint8_t { + DAMAGE_TYPE_PHYSICAL = 0, + + DAMAGE_TYPE_COLD = 1, + DAMAGE_TYPE_FIRE = 2, + DAMAGE_TYPE_LIGHTNING = 3, + DAMAGE_TYPE_POISON = 4, + DAMAGE_TYPE_MAGIC = 5, + + DAMAGE_TYPE_COUNT, + DAMAGE_TYPE_UNKNOWN = 0xFF + }; + + struct packet_header { + uint8_t d2_packet_type; + uint8_t message_type; + + packet_header() : d2_packet_type(0xBB), message_type(0) {} + }; + + struct d2_entity_action_cs : packet_header { + uint32_t action; + uint32_t entity_id; + uint32_t complement; + + d2_entity_action_cs() : action(0), entity_id(0), complement(0) {} + }; + + struct item_move_cs : packet_header { + uint32_t item_guid; + uint8_t target_page; + + item_move_cs() : item_guid(0), target_page(0) { + message_type = MESSAGE_TYPE_ITEM_MOVE; + } + }; + + struct item_move_sc : packet_header { + uint32_t item_guid; + uint32_t tx; + uint32_t ty; + uint8_t target_page; + + item_move_sc() : item_guid(0), tx(0), ty(0), target_page(0) { + message_type = MESSAGE_TYPE_ITEM_MOVE; + } + }; + + struct inventory_sort_cs : packet_header { + uint8_t page; + + inventory_sort_cs() : page(0) { + message_type = MESSAGE_TYPE_INVENTORY_SORT; + } + }; + + struct inventory_sort_sc : packet_header { + uint8_t page; + uint8_t tx; + uint8_t ty; + uint32_t guid; + + inventory_sort_sc() : page(0), tx(0), ty(0), guid(0) { + message_type = MESSAGE_TYPE_INVENTORY_SORT; + } + }; + + struct damage_info_cs : packet_header { + uint8_t state; //on or off + + damage_info_cs() : state(0) { + message_type = MESSAGE_TYPE_DAMAGE_INFO; + } + }; + + struct damage_info_sc : packet_header { + uint8_t unit_type; + uint32_t guid; + damage_type_t damage_type; + uint32_t damage; + + + uint32_t currentHp; // New field for current hit points + uint32_t maxHp; // New field for maximum hit points + + damage_info_sc() : unit_type(0), guid(0), damage_type(DAMAGE_TYPE_UNKNOWN), damage(0), currentHp(0), maxHp(0) { + message_type = MESSAGE_TYPE_DAMAGE_INFO; + } + }; + + struct gold_pickup_info_sc : packet_header { + uint32_t gold; + gold_pickup_info_sc() : gold(0) { + message_type = MESSAGE_TYPE_GOLD_PICKUP_INFO; + } + }; + + struct gold_pickup_info_cs : packet_header { + uint32_t item_guid; + gold_pickup_info_cs() : item_guid(0) { + message_type = MESSAGE_TYPE_GOLD_PICKUP_INFO; + } + }; + + struct item_pickup_info_cs : packet_header { + uint32_t item_guid; + item_pickup_info_cs() : item_guid(0) { + message_type = MESSAGE_TYPE_ITEM_PICKUP_INFO; + } + }; + + struct item_pickup_info_sc : packet_header { + bool inventory_full; + item_pickup_info_sc() : inventory_full(false) { + message_type = MESSAGE_TYPE_ITEM_PICKUP_INFO; + } + }; + + struct item_dropped_info_cs : packet_header { + uint16_t item_id; + uint8_t code[4]; + item_dropped_info_cs() : item_id(0), code{ 0 } { + message_type = MESSAGE_TYPE_ITEM_DROPPED_INFO; + } + }; + + struct item_dropped_info_sc : packet_header { + uint32_t item; + uint8_t code[4]; + uint8_t quality; + uint8_t showthis; + uint8_t namestr[130]; //130 + uint8_t index_arr_itemtype; + uint8_t arr_itemtype_codestr_equivstr[20][5]; + item_dropped_info_sc() : item(0), quality(0), showthis(0), index_arr_itemtype(0), code{ 0 }, arr_itemtype_codestr_equivstr{ 0 }, namestr{ 0 } { + message_type = MESSAGE_TYPE_ITEM_DROPPED_INFO; + } + }; + + struct transmute_info_sc : packet_header { + uint32_t item_guid; + uint32_t tx; + uint32_t ty; + uint8_t target_page; + uint8_t command; + + transmute_info_sc() : item_guid(0), tx(0), ty(0), target_page(0), command(0) { + message_type = MESSAGE_TYPE_TRANSMUTE; + } + }; + + struct transmute_info_cs : packet_header { + uint32_t item_guid; + uint8_t target_page; + uint8_t command; + bool transmute_start_flag; + + transmute_info_cs() : item_guid(0), target_page(0), command(0), transmute_start_flag(0) { + message_type = MESSAGE_TYPE_TRANSMUTE; + } + }; + + struct trader_update_cs : packet_header { + uint32_t npc_id; + uint32_t client_id; + uint8_t command; + bool is_gamble_menu_open; + trader_update_cs() : npc_id(0), client_id(0), command(0), is_gamble_menu_open(0) { + message_type = MESSAGE_TYPE_TRADER_UPDATE; + } + }; + + struct trader_update_sc : packet_header { + uint32_t npc_id; + uint32_t client_id; + uint8_t command; + bool is_gamble_menu_open; + trader_update_sc() : npc_id(0), client_id(0), command(0), is_gamble_menu_open(0) { + message_type = MESSAGE_TYPE_TRADER_UPDATE; + } + }; + +#pragma pack(pop) + } +} \ No newline at end of file diff --git a/include/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.h b/include/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.h new file mode 100644 index 0000000..70d40b8 --- /dev/null +++ b/include/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class auto_gold_pickup final : public server_module { + public: + void init() override; + void tick(diablo2::structures::game* game, diablo2::structures::unit* unit) override; + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) override; + bool au_pickup_gold(diablo2::structures::game* game, diablo2::structures::unit* unit, diablo2::structures::unit* item); + }; + } + } +} diff --git a/include/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.h b/include/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.h new file mode 100644 index 0000000..c210258 --- /dev/null +++ b/include/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class auto_item_pickup final : public server_module { + public: + void init() override; + void tick(diablo2::structures::game* game, diablo2::structures::unit* unit) override; + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) override; + bool au_pickup_item(diablo2::structures::game* game, diablo2::structures::unit* unit, uint32_t guid); + }; + } + } +} diff --git a/include/d2tweaks/server/modules/autosort/autosort_server.h b/include/d2tweaks/server/modules/autosort/autosort_server.h new file mode 100644 index 0000000..b09d0fa --- /dev/null +++ b/include/d2tweaks/server/modules/autosort/autosort_server.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include + +//Inventory auto sort module server side + +namespace diablo2 { + namespace structures { + struct game; + struct inventory; + struct unit; + } +} + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class autosort final : public server_module { + public: + void init() override; + + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, + common::packet_header* packet) override; + private: + bool sort(diablo2::structures::game* game, diablo2::structures::unit* player, uint8_t page); + bool find_free_space(diablo2::structures::inventory* inv, + diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y, bool isCharmZone); + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/server/modules/damage_display/damage_display_server.h b/include/d2tweaks/server/modules/damage_display/damage_display_server.h new file mode 100644 index 0000000..6bbff58 --- /dev/null +++ b/include/d2tweaks/server/modules/damage_display/damage_display_server.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +//Display damage server side + +namespace diablo2 { + namespace structures { + struct inventory; + struct game; + struct unit; + } +} + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class damage_display final : public server_module { + public: + void init() override; + + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, + common::packet_header* packet) override; + void tick(diablo2::structures::game* game, diablo2::structures::unit* unit) override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.h b/include/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.h new file mode 100644 index 0000000..f137b57 --- /dev/null +++ b/include/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class identify_on_pickup final : public server_module { + public: + void init() override; + }; + } + } +} diff --git a/include/d2tweaks/server/modules/item_drop_message/item_drop_message_server.h b/include/d2tweaks/server/modules/item_drop_message/item_drop_message_server.h new file mode 100644 index 0000000..7f6537f --- /dev/null +++ b/include/d2tweaks/server/modules/item_drop_message/item_drop_message_server.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class item_drop_message final : public server_module { + public: + void init() override; + void tick(diablo2::structures::game* game, diablo2::structures::unit* unit) override; + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) override; + }; + } + } +} diff --git a/include/d2tweaks/server/modules/item_move/item_move_server.h b/include/d2tweaks/server/modules/item_move/item_move_server.h new file mode 100644 index 0000000..0599b27 --- /dev/null +++ b/include/d2tweaks/server/modules/item_move/item_move_server.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +//Item moving between inventory pages (cube, inventory and stash) by ctrl+click server side + +namespace diablo2 { + namespace structures { + struct inventory; + struct game; + struct unit; + } +} + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class item_move final : public server_module { + public: + void init() override; + + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, + common::packet_header* packet) override; + private: + bool find_free_space(diablo2::structures::inventory* inv, + diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y); + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/server/modules/server_module.h b/include/d2tweaks/server/modules/server_module.h new file mode 100644 index 0000000..cab73c1 --- /dev/null +++ b/include/d2tweaks/server/modules/server_module.h @@ -0,0 +1,38 @@ +#pragma once +#include +#define MODULE_INIT(module_name) static d2_tweaks::server::modules::module_name g_instance; + +namespace diablo2 { + namespace structures { + struct game; + struct unit; + } +} + +namespace d2_tweaks { + namespace common { + struct packet_header; + } + + namespace server { + namespace modules { + class server_module { + public: + virtual ~server_module() = default; + server_module(); + + virtual void init() = 0; + + /** + * \brief + * \param game + * \param player + * \param packet + * \return true - block further packet processing, false - pass packet to game + */ + virtual bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet); + virtual void tick(diablo2::structures::game* game, diablo2::structures::unit* unit); + }; + } + } +} diff --git a/include/d2tweaks/server/modules/test/test.h b/include/d2tweaks/server/modules/test/test.h new file mode 100644 index 0000000..302bed3 --- /dev/null +++ b/include/d2tweaks/server/modules/test/test.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class test final : public server_module { + public: + void init() override; + }; + } + } +} diff --git a/include/d2tweaks/server/modules/trader_update/trader_update_server.h b/include/d2tweaks/server/modules/trader_update/trader_update_server.h new file mode 100644 index 0000000..25186fa --- /dev/null +++ b/include/d2tweaks/server/modules/trader_update/trader_update_server.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace diablo2 { + namespace structures { + struct inventory; + struct game; + struct unit; + } +} + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class trader_update final : public server_module { + public: + void init() override; + void tick(diablo2::structures::game* game, diablo2::structures::unit* unit) override; + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) override; + + //private: + // bool find_free_space(diablo2::structures::inventory* inv, diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y); + // bool send_to_cube(diablo2::structures::game* game, diablo2::structures::unit* player, diablo2::structures::unit* item); + // bool move_item_to(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet); + }; + } + } +} diff --git a/include/d2tweaks/server/modules/transmute/transmute_server.h b/include/d2tweaks/server/modules/transmute/transmute_server.h new file mode 100644 index 0000000..01fe1f1 --- /dev/null +++ b/include/d2tweaks/server/modules/transmute/transmute_server.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace diablo2 { + namespace structures { + struct inventory; + struct game; + struct unit; + } +} + +namespace d2_tweaks { + namespace server { + class server; + + namespace modules { + class transmute final : public server_module { + public: + void init() override; + void tick(diablo2::structures::game* game, diablo2::structures::unit* unit) override; + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) override; + + private: + bool find_free_space(diablo2::structures::inventory* inv, diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y); + bool send_to_cube(diablo2::structures::game* game, diablo2::structures::unit* player, diablo2::structures::unit* item); + bool move_item_to(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet); + }; + } + } +} diff --git a/include/d2tweaks/server/server.h b/include/d2tweaks/server/server.h new file mode 100644 index 0000000..b59ec3b --- /dev/null +++ b/include/d2tweaks/server/server.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include +#include +#include + +namespace diablo2 { + namespace structures { + enum class unit_type_t; + struct game; + struct inventory; + struct unit; + struct net_client; + } +} + +namespace d2_tweaks { + namespace common { + struct packet_header; + } + + namespace server { + namespace modules { + class server_module; + } + + class server : public singleton { + uint8_t m_module_id_counter; + uint8_t m_tick_handler_id_counter; + modules::server_module* m_modules[0xFF]{ nullptr }; //max 255 modules atm. + modules::server_module* m_tick_handlers[0xFF]{ nullptr }; //max 255 modules atm. + modules::server_module* m_packet_handlers[0xFF]{ nullptr }; //max 255 handlers because of one-byte packet header + public: + explicit server(token); + + void init(); + + void send_packet(diablo2::structures::net_client* client, common::packet_header* packet, size_t size); + bool handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet); + + void register_module(modules::server_module* module); + + void register_tick_handler(modules::server_module* module); + void register_packet_handler(common::message_types_t type, modules::server_module* module); + + diablo2::structures::unit* get_server_unit(diablo2::structures::game* game, uint32_t guid, diablo2::structures::unit_type_t type); + void iterate_server_units(diablo2::structures::game* game, diablo2::structures::unit_type_t type, + const std::function& cb); + private: + static int32_t __fastcall net_tick(diablo2::structures::game* game, diablo2::structures::unit* unit, int32_t a3, int32_t a4); + }; + } +} \ No newline at end of file diff --git a/include/d2tweaks/ui/controls/button.h b/include/d2tweaks/ui/controls/button.h new file mode 100644 index 0000000..71f3554 --- /dev/null +++ b/include/d2tweaks/ui/controls/button.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +namespace diablo2 { + namespace structures { + struct cell_file; + } +} + +namespace d2_tweaks { + namespace common { + class asset; + } + + namespace ui { + namespace controls { + class image; + + class button final : public control { + rect m_rect; + image* m_image; + int32_t m_frame_down; + int32_t m_frame_up; + int32_t m_click_sound; + + bool m_is_down; + int32_t m_current_frame; + uint32_t m_res_count; + + std::wstring m_popup; + std::function m_on_click; + + std::vector m_respos; + public: + button(menu* menu, const rect& rect, const std::function& onClick, + common::asset* image, int32_t frameDown, int32_t frameUp, int32_t clickSound = -1); + explicit button(menu* menu, const pugi::xml_node& node); + virtual ~button(); + + void set_x(int32_t value) override; + int32_t get_x() const override { + return m_rect.get_x(); + } + + void set_y(int32_t value) override; + int32_t get_y() const override { + return m_rect.get_y(); + } + + std::wstring popup() const { + return m_popup; + } + + void set_popup(const std::wstring& popup) { + m_popup = popup; + } + + void set_current_frame(int32_t value) { + m_current_frame = value; + } + + int32_t get_current_frame() { + return m_current_frame; + } + + std::function get_on_click() const { + return m_on_click; + } + + void set_on_click(const std::function& on_click) { + m_on_click = on_click; + } + + void draw() override; + void draw(int32_t offsetX, int32_t offsetY) override; + + void left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + void right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + + void key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) override; + }; + } + } +} \ No newline at end of file diff --git a/include/d2tweaks/ui/controls/checkbox.h b/include/d2tweaks/ui/controls/checkbox.h new file mode 100644 index 0000000..764880d --- /dev/null +++ b/include/d2tweaks/ui/controls/checkbox.h @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include +#include +#include + +namespace d2_tweaks { + namespace common { + class asset; + } +} + +namespace d2_tweaks { + namespace ui { + namespace controls { + class image; + class label; + + class checkbox : public control { + rect m_rect; + image* m_image; + label* m_label; + + std::wstring m_popup; + + int32_t m_frame_checked; + int32_t m_frame_unchecked; + int32_t m_click_sound; + + bool m_is_down; + bool m_state; + + std::function m_on_click; + public: + explicit checkbox(menu* menu, const std::wstring& text, const rect& rect, const std::function& onClick, + common::asset* image, int32_t frameChecked, int32_t frameUnchecked, int32_t clickSound = -1); + explicit checkbox(menu* menu, const pugi::xml_node& node); + + void set_x(int32_t value) override; + int32_t get_x() const override { + return m_rect.get_x(); + } + + void set_y(int32_t value) override; + int32_t get_y() const override { + return m_rect.get_y(); + } + + std::wstring popup() const { + return m_popup; + } + + void set_popup(const std::wstring& popup) { + m_popup = popup; + } + + std::function get_on_click() const { + return m_on_click; + } + + void set_on_click(const std::function& on_click) { + m_on_click = on_click; + } + + bool get_state() const { + return m_state; + } + + void set_state(bool value) { + m_state = value; + } + + void draw() override; + void draw(int32_t offsetX, int32_t offsetY) override; + + void left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + void right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + + void key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) override; + }; + } + } +} diff --git a/include/d2tweaks/ui/controls/control.h b/include/d2tweaks/ui/controls/control.h new file mode 100644 index 0000000..6f7372b --- /dev/null +++ b/include/d2tweaks/ui/controls/control.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include + +namespace d2_tweaks { + namespace ui { + class menu; + + namespace controls { + struct respos { + uint32_t res_x; + uint32_t res_y; + uint32_t pos_x; + uint32_t pos_y; + }; + + class control { + control* m_parent; + menu* m_menu; + + std::string m_name; + bool m_enabled = false; + bool m_visible = false; + int32_t m_x; + int32_t m_y; + int32_t m_width; + int32_t m_height; + + public: + control(menu* menu, int32_t x, int32_t y, int32_t w, int32_t h) : m_parent(nullptr), + m_menu(menu), + m_x(x), m_y(y), + m_width(w), m_height(h) {} + + virtual ~control() = default; + + control* get_parent() const { + return m_parent; + } + + void set_parent(control* const parent) { + m_parent = parent; + } + + menu* get_menu() const { + return m_menu; + } + + void set_menu(menu* const menu) { + m_menu = menu; + } + + std::string get_name() const { + return m_name; + } + + void set_name(const std::string& name) { + m_name = name; + } + + virtual bool get_enabled() const { + return m_enabled; + } + + virtual void set_enabled(bool value) { + m_enabled = value; + } + + virtual bool get_visible() const { + return m_visible; + } + + virtual void set_visible(bool value) { + m_visible = value; + } + + virtual int32_t get_x() const { + return m_x; + } + + virtual void set_x(int32_t value) { + m_x = value; + } + + virtual int32_t get_y() const { + return m_y; + } + + virtual void set_y(int32_t value) { + m_y = value; + } + + virtual int32_t get_width() const { + return m_width; + } + + virtual void set_width(const int32_t width) { + m_width = width; + } + + virtual int32_t get_height() const { + return m_height; + } + + virtual void set_height(const int32_t height) { + m_height = height; + } + + virtual void draw() = 0; + virtual void draw(int32_t offsetX, int32_t offsetY) = 0; + + virtual void left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) = 0; + virtual void right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) = 0; + + virtual void key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) = 0; + }; + } + } +} diff --git a/include/d2tweaks/ui/controls/group.h b/include/d2tweaks/ui/controls/group.h new file mode 100644 index 0000000..70ca7b8 --- /dev/null +++ b/include/d2tweaks/ui/controls/group.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace d2_tweaks { + namespace ui { + class menu; + + namespace controls { + class group : public control { + std::vector m_controls; + public: + explicit group(menu* menu, int32_t x, int32_t y); + explicit group(menu* menu, const pugi::xml_node& node); + + void draw() override; + void draw(int32_t offsetX, int32_t offsetY) override; + + void left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + void right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + + void key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) override; + + private: + void add_control(control* control); + }; + } + } +} diff --git a/include/d2tweaks/ui/controls/image.h b/include/d2tweaks/ui/controls/image.h new file mode 100644 index 0000000..bc168a0 --- /dev/null +++ b/include/d2tweaks/ui/controls/image.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include +#include + +namespace d2_tweaks { + namespace common { + class asset; + } + + namespace ui { + namespace controls { + class image : public control { + common::asset* m_image; + int32_t m_frame; + + rect m_rect; + bool m_block_click; + diablo2::structures::gfxdata m_draw_info; + public: + explicit image(menu* menu, common::asset* image, int32_t x = 0, int32_t y = 0, int32_t frame = 0); + explicit image(menu* menu, const pugi::xml_node& node); + + void set_frame(int32_t frame) { + m_frame = static_cast(frame); + } + + int32_t get_frame() const { + return static_cast(m_frame); + } + + void draw() override; + void draw(int32_t offsetX, int32_t offsetY) override; + + void left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + void right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + + void key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) override; + }; + } + } +} diff --git a/include/d2tweaks/ui/controls/label.h b/include/d2tweaks/ui/controls/label.h new file mode 100644 index 0000000..6381744 --- /dev/null +++ b/include/d2tweaks/ui/controls/label.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include + +namespace d2_tweaks { + namespace ui { + namespace controls { + + class label : public control { + std::wstring m_text; + bool m_text_owned; + diablo2::ui_color_t m_color; + diablo2::ui_font_t m_font; + uint32_t m_res_count; + std::vector m_respos; + public: + explicit label(menu* menu, const std::wstring& text, int32_t x = 0, int32_t y = 0, + diablo2::ui_color_t color = diablo2::UI_COLOR_WHITE, + diablo2::ui_font_t font = diablo2::UI_FONT_16); + explicit label(menu* menu, const pugi::xml_node& node); + + void set_text(const std::wstring& text) { + m_text = text; + } + + const std::wstring& get_text() const { + return m_text; + } + + + diablo2::ui_color_t get_color() const { + return m_color; + } + + void set_color(const diablo2::ui_color_t color) { + m_color = color; + } + + diablo2::ui_font_t get_font() const { + return m_font; + } + + void set_font(const diablo2::ui_font_t font) { + m_font = font; + } + + void draw() override; + void draw(int32_t offsetX, int32_t offsetY) override; + + void left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + void right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) override; + + void key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) override; + }; + } + } +} diff --git a/include/d2tweaks/ui/menu.h b/include/d2tweaks/ui/menu.h new file mode 100644 index 0000000..f8ca49c --- /dev/null +++ b/include/d2tweaks/ui/menu.h @@ -0,0 +1,88 @@ +#pragma once + +#include +#include +#include + +namespace d2_tweaks { + namespace ui { + namespace controls { + class control; + } + + struct respos { + uint32_t res_x; + uint32_t res_y; + uint32_t pos_x; + uint32_t pos_y; + }; + + class menu { + bool m_enabled = false; + bool m_visible = false; + + std::string m_name; + + int32_t m_x; + int32_t m_y; + int32_t m_width; + int32_t m_height; + uint32_t m_res_count; + + std::vector m_controls; + std::unordered_map m_named_controls; + std::vector m_respos; + public: + virtual ~menu() = default; + + menu(); + + const std::vector& get_controls() const { + return m_controls; + } + + const std::string& get_name() const { + return m_name; + } + + virtual bool get_enabled() const { + return m_enabled; + } + + virtual void set_enabled(bool value) { + m_enabled = value; + } + + virtual bool get_visible() const { + return m_visible; + } + + virtual void set_visible(bool value) { + m_visible = value; + } + + bool load_xml(const char* path); + + template + TControl* get_control(const std::string& name) { + const auto it = m_named_controls.find(name); + + if (it == m_named_controls.end()) + return nullptr; + + return static_cast(it->second); + } + + virtual void add_control(controls::control* control); + //virtual controls::control* get_control(const std::string& name); + virtual void remove_control(controls::control* control); + + virtual void draw(); + + virtual bool left_mouse(bool up); + virtual bool right_mouse(bool up); + + virtual bool key_event(uint32_t key, bool up); + }; + } +} diff --git a/include/d2tweaks/ui/rect.h b/include/d2tweaks/ui/rect.h new file mode 100644 index 0000000..d978846 --- /dev/null +++ b/include/d2tweaks/ui/rect.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +namespace d2_tweaks { + namespace ui { + //Special ui rectangle with starting point in the left bottom corner + class rect { + int32_t m_x, m_y, m_w, m_h; + public: + rect() : m_x(0), m_y(0), m_w(0), m_h(0) {} + rect(int32_t x, int32_t y, int32_t width, int32_t height) : m_x(x), m_y(y), m_w(width), m_h(height) {} + + int32_t get_x() const { + return m_x; + } + + void set_x(int32_t value) { + m_x = value; + } + + int32_t get_y() const { + return m_y; + } + + void set_y(int32_t value) { + m_y = value; + } + + void set_width(int32_t value) { + m_w = value; + } + + int32_t get_width() const { + return m_w; + } + + void set_height(int32_t value) { + m_h = value; + } + + int32_t get_height() const { + return m_h; + } + + bool contains(int32_t x, int32_t y) const { + return x >= m_x && x < m_x + m_w && + y > m_y - m_h && y <= m_y; + } + + bool contains(int32_t x, int32_t y, int32_t offsetX, int32_t offsetY) const { + return x >= m_x + offsetX && x < m_x + offsetX + m_w && + y > m_y + offsetY - m_h && y <= m_y + offsetY; + } + }; + } +} \ No newline at end of file diff --git a/include/d2tweaks/ui/ui_manager.h b/include/d2tweaks/ui/ui_manager.h new file mode 100644 index 0000000..e5e9a3a --- /dev/null +++ b/include/d2tweaks/ui/ui_manager.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace d2_tweaks { + namespace ui { + class menu; + + class ui_manager final : public singleton { + std::vector m_menus; + + bool m_was_down_before_left = false; + bool m_was_down_before_right = false; + bool m_mouse_state_left = false; + bool m_mouse_state_right = false; + public: + explicit ui_manager(token); + + void add_menu(menu* m); + menu* get_menu(const std::string& name); + void remove_menu(menu* m); + + void draw(); + private: + static LRESULT __stdcall wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void process_inputs(); + + bool process_left_mouse(bool up); + bool process_right_mouse(bool up); + + bool process_key_event(uint32_t key, bool up); + }; + } +} diff --git a/include/diablo2/d2client.h b/include/diablo2/d2client.h new file mode 100644 index 0000000..3eba2d0 --- /dev/null +++ b/include/diablo2/d2client.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include + +namespace diablo2 { + namespace structures { + struct gfxdata; + struct unit; + struct client_unit_list; + struct cellfile; + } + + enum ui_window_t { + UI_WINDOW_INTERFACE = 0x0, + UI_WINDOW_INVENTORY = 0x01, + UI_WINDOW_CHARACTER = 0x02, + UI_WINDOW_MINISKILL = 0x03, + UI_WINDOW_SKILL = 0x04, + UI_WINDOW_CHAT = 0x05, + UI_WINDOW_NPCMENU = 0x08, + UI_WINDOW_MAINMENU = 0x09, + UI_WINDOW_AUTOMAP = 0x0a, + UI_WINDOW_CONFIG = 0x0b, + UI_WINDOW_NPCSHOP = 0x0c, + UI_WINDOW_ALTDOWN = 0x0d, + UI_WINDOW_ANVIL = 0x0e, + UI_WINDOW_QUEST = 0x0f, + UI_WINDOW_QUESTLOG = 0x11, + UI_WINDOW_STATUSAREA = 0x12, + UI_WINDOW_WPMENU = 0x14, + UI_WINDOW_MINIPANEL = 0x15, + UI_WINDOW_PARTY = 0x16, + UI_WINDOW_TRADE = 0x17, + UI_WINDOW_MSGS = 0x18, + UI_WINDOW_STASH = 0x19, + UI_WINDOW_CUBE = 0x1a, + UI_WINDOW_BELT = 0x1f, + UI_WINDOW_HELP = 0x21, + UI_WINDOW_MERC = 0x24, + UI_WINDOW_SCROLL = 0x25 + }; + + class d2_client { + public: + static char* get_base(); + + static bool is_lod(); + + static structures::unit* get_local_player(); + static const char* get_local_player_name(); + static structures::client_unit_list* get_client_unit_list(); + + static int32_t get_view_offset_x(); + static int32_t get_view_offset_y(); + + static uint32_t get_mouse_x(); + static uint32_t get_mouse_y(); + static bool get_ui_window_state(ui_window_t window); + static void* get_buysellbtn(); + + static void play_sound(uint32_t soundId, structures::unit* u, uint32_t ticks, BOOL prePick, uint32_t cache); + + static structures::unit* get_unit_by_guid(int32_t type, int32_t guid); + + static void send_to_server(void* data, size_t size); + static void print_chat(wchar_t* string, uint32_t color); + + static bool cache_gfx_data(structures::gfxdata* gfxData, + structures::unit* unit, + structures::cellfile* cellfFile, + int32_t direction, + int32_t frame, + int32_t* outIndex, + int8_t flags, + int32_t colorTint); + + static structures::cellfile* load_gfx_resource(char* path); + static int32_t unload_gfx_resource(structures::cellfile* handle); + static int32_t send_to_server_7(BYTE type, DWORD num, DWORD unk1, DWORD unk2); + static uint32_t screen_height(); + static uint32_t screen_width(); + static uint32_t current_vendor_id(); + static uint32_t current_vendor_guid(); + static bool is_gamble_open(); + static uint8_t current_interact_menu(); + static void resync_vendor_inventory(structures::unit* ptNPC); + }; +} \ No newline at end of file diff --git a/include/diablo2/d2cmp.h b/include/diablo2/d2cmp.h new file mode 100644 index 0000000..285bb7e --- /dev/null +++ b/include/diablo2/d2cmp.h @@ -0,0 +1,14 @@ +#pragma once + +namespace diablo2 { + namespace structures { + struct gfxdata; + + class d2_cmp { + public: + static char* get_base(); + + static bool init_gfx_data(gfxdata* gfxdata); + }; + } +} \ No newline at end of file diff --git a/include/diablo2/d2common.h b/include/diablo2/d2common.h new file mode 100644 index 0000000..3f14b5c --- /dev/null +++ b/include/diablo2/d2common.h @@ -0,0 +1,456 @@ +#pragma once + +#include +#include + +//struct UniqueItemsBIN //size=0x14C (332) +//{ +// WORD uniqueId; //+00 +// BYTE uk1[0x20]; //+02 +// WORD uniqueNameId; //+22 +// BYTE uk2[0x08]; //+24 +// union { +// BYTE flag; //+2C +// struct { +// BYTE ukf : 2; +// BYTE carry1 : 1; +// BYTE ladder : 1; +// }; +// }; +// BYTE uk3[0x11F]; //+2D +//}; + +//struct DataTables//01EE6A20 * 01FDA2D0 //second comments=1.11 +//{ +// BYTE uk1[0xA78]; //+000 +// DWORD* monStats; //+A78 //1.11 and 1.10 +// BYTE uk2[0x4]; //+A7C +// DWORD nbMonStats; //+A80 //1.11 and 1.10 +// BYTE uk3[0x108]; //+A84 +// DWORD* skilldesc; //+B8C +// BYTE uk4[0x4]; //+B90 +// DWORD nbSkilldesc; //+B94 +// DWORD* skills; //+B98 +// BYTE uk5[0x4]; //+B9C +// DWORD nbSkills; //+BA0 +// int* nbSkillsPerPlayer; //+BA4 +// int maxSkillsPerPlayer; //+BA8 +// WORD* playerSkills; //+BAC +// BYTE uk6[0x14]; //+BB0 +// DWORD* charStats; //+BC4 +// DWORD nbCharStats; //+BC8 +// DWORD* itemStatCost; //+BCC +// BYTE uk7[4]; //+BD0 +// DWORD nbItemStatCosts; //+BD4 +// WORD* statsDescPriority; //+BD8 +// DWORD nbStatsDescPriority;//+BDC +// BYTE uk8[0x18]; //+BE0 +// DWORD* itemTypes; //+BF8 +// DWORD nbItemTypes; //+BFC +// BYTE uk9[0x0C]; //+C00 +// DWORD* sets; //+C0C //1.11 and 1.10 +// DWORD nbSets; //+C10 //1.11 and 1.10 +// BYTE uk9b[0x4]; //+C14 +// DWORD* setItems; //+C18 //1.11 and 1.10 +// DWORD nbSetItems; //+C1C //1.11 and 1.10 +// BYTE uk10[0x4]; //+C20 +// UniqueItemsBIN* uniqueItems; //+C24 //1.11 and 1.10 +// DWORD nbUniqueItems; //+C28 //1.11 and 1.10 +// BYTE uk11[0x2C]; //+C2C +// DWORD* levels; //+C58 +// DWORD nbLevels; //+C5C +// BYTE uk12[0x64]; //+C60 +// DWORD* cubemain; //+CC4 //14C0 by 148 for 1.11 +// DWORD nbCubemain; //+CC8 //14C4 for 1.11 +// DWORD nbInventory; //+CCC +// DWORD* inventory; //+CD0 +// BYTE uk13[0x04]; //+CD4 +// DWORD nbItems; //+CD8 +// DWORD* items; //+CDC +// DWORD* itemsBis; //+CE0 +// BYTE uk14[0x1F8]; //+CDC +// DWORD nbRunes; //+EDC +// DWORD* runes; //+EE0 +//}; +// +//extern DataTables* sgptDataTables; + +namespace diablo2 { + namespace structures { + struct unit; + struct path; + struct inventory; + struct room; + + struct items_line; + struct item_types_line; + } + + enum unit_stats_t { + UNIT_STAT_STRENGTH = 0x0, + UNIT_STAT_ENERGY = 0x1, + UNIT_STAT_DEXTERITY = 0x2, + UNIT_STAT_VITALITY = 0x3, + UNIT_STAT_STATPTS = 0x4, + UNIT_STAT_NEWSKILLS = 0x5, + UNIT_STAT_HITPOINTS = 0x6, + UNIT_STAT_MAXHP = 0x7, + UNIT_STAT_MANA = 0x8, + UNIT_STAT_MAXMANA = 0x9, + UNIT_STAT_STAMINA = 0xA, + UNIT_STAT_MAXSTAMINA = 0xB, + UNIT_STAT_LEVEL = 0xC, + UNIT_STAT_EXPERIENCE = 0xD, + UNIT_STAT_GOLD = 0xE, + UNIT_STAT_GOLDBANK = 0xF, + UNIT_STAT_ITEM_ARMOR_PERCENT = 0x10, + UNIT_STAT_ITEM_MAXDAMAGE_PERCENT = 0x11, + UNIT_STAT_ITEM_MINDAMAGE_PERCENT = 0x12, + UNIT_STAT_TOHIT = 0x13, + UNIT_STAT_TOBLOCK = 0x14, + UNIT_STAT_MINDAMAGE = 0x15, + UNIT_STAT_MAXDAMAGE = 0x16, + UNIT_STAT_SECONDARY_MINDAMAGE = 0x17, + UNIT_STAT_SECONDARY_MAXDAMAGE = 0x18, + UNIT_STAT_DAMAGEPERCENT = 0x19, + UNIT_STAT_MANARECOVERY = 0x1A, + UNIT_STAT_MANARECOVERYBONUS = 0x1B, + UNIT_STAT_STAMINARECOVERYBONUS = 0x1C, + UNIT_STAT_LASTEXP = 0x1D, + UNIT_STAT_NEXTEXP = 0x1E, + UNIT_STAT_ARMORCLASS = 0x1F, + UNIT_STAT_ARMORCLASS_VS_MISSILE = 0x20, + UNIT_STAT_ARMORCLASS_VS_HTH = 0x21, + UNIT_STAT_NORMAL_DAMAGE_REDUCTION = 0x22, + UNIT_STAT_MAGIC_DAMAGE_REDUCTION = 0x23, + UNIT_STAT_DAMAGERESIST = 0x24, + UNIT_STAT_MAGICRESIST = 0x25, + UNIT_STAT_MAXMAGICRESIST = 0x26, + UNIT_STAT_FIRERESIST = 0x27, + UNIT_STAT_MAXFIRERESIST = 0x28, + UNIT_STAT_LIGHTRESIST = 0x29, + UNIT_STAT_MAXLIGHTRESIST = 0x2A, + UNIT_STAT_COLDRESIST = 0x2B, + UNIT_STAT_MAXCOLDRESIST = 0x2C, + UNIT_STAT_POISONRESIST = 0x2D, + UNIT_STAT_MAXPOISONRESIST = 0x2E, + UNIT_STAT_DAMAGEAURA = 0x2F, + UNIT_STAT_FIREMINDAM = 0x30, + UNIT_STAT_FIREMAXDAM = 0x31, + UNIT_STAT_LIGHTMINDAM = 0x32, + UNIT_STAT_LIGHTMAXDAM = 0x33, + UNIT_STAT_MAGICMINDAM = 0x34, + UNIT_STAT_MAGICMAXDAM = 0x35, + UNIT_STAT_COLDMINDAM = 0x36, + UNIT_STAT_COLDMAXDAM = 0x37, + UNIT_STAT_COLDLENGTH = 0x38, + UNIT_STAT_POISONMINDAM = 0x39, + UNIT_STAT_POISONMAXDAM = 0x3A, + UNIT_STAT_POISONLENGTH = 0x3B, + UNIT_STAT_LIFEDRAINMINDAM = 0x3C, + UNIT_STAT_LIFEDRAINMAXDAM = 0x3D, + UNIT_STAT_MANADRAINMINDAM = 0x3E, + UNIT_STAT_MANADRAINMAXDAM = 0x3F, + UNIT_STAT_STAMDRAINMINDAM = 0x40, + UNIT_STAT_STAMDRAINMAXDAM = 0x41, + UNIT_STAT_STUNLENGTH = 0x42, + UNIT_STAT_VELOCITYPERCENT = 0x43, + UNIT_STAT_ATTACKRATE = 0x44, + UNIT_STAT_OTHER_ANIMRATE = 0x45, + UNIT_STAT_QUANTITY = 0x46, + UNIT_STAT_VALUE = 0x47, + UNIT_STAT_DURABILITY = 0x48, + UNIT_STAT_MAXDURABILITY = 0x49, + UNIT_STAT_HPREGEN = 0x4A, + UNIT_STAT_ITEM_MAXDURABILITY_PERCENT = 0x4B, + UNIT_STAT_ITEM_MAXHP_PERCENT = 0x4C, + UNIT_STAT_ITEM_MAXMANA_PERCENT = 0x4D, + UNIT_STAT_ITEM_ATTACKERTAKESDAMAGE = 0x4E, + UNIT_STAT_ITEM_GOLDBONUS = 0x4F, + UNIT_STAT_ITEM_MAGICBONUS = 0x50, + UNIT_STAT_ITEM_KNOCKBACK = 0x51, + UNIT_STAT_ITEM_TIMEDURATION = 0x52, + UNIT_STAT_ITEM_ADDCLASSSKILLS = 0x53, + UNIT_STAT_UNSENTPARAM1 = 0x54, + UNIT_STAT_ITEM_ADDEXPERIENCE = 0x55, + UNIT_STAT_ITEM_HEALAFTERKILL = 0x56, + UNIT_STAT_ITEM_REDUCEDPRICES = 0x57, + UNIT_STAT_ITEM_DOUBLEHERBDURATION = 0x58, + UNIT_STAT_ITEM_LIGHTRADIUS = 0x59, + UNIT_STAT_ITEM_LIGHTCOLOR = 0x5A, + UNIT_STAT_ITEM_REQ_PERCENT = 0x5B, + UNIT_STAT_ITEM_LEVELREQ = 0x5C, + UNIT_STAT_ITEM_FASTERATTACKRATE = 0x5D, + UNIT_STAT_ITEM_LEVELREQPCT = 0x5E, + UNIT_STAT_LASTBLOCKFRAME = 0x5F, + UNIT_STAT_ITEM_FASTERMOVEVELOCITY = 0x60, + UNIT_STAT_ITEM_NONCLASSSKILL = 0x61, + UNIT_STAT_STATE = 0x62, + UNIT_STAT_ITEM_FASTERGETHITRATE = 0x63, + UNIT_STAT_MONSTER_PLAYERCOUNT = 0x64, + UNIT_STAT_SKILL_POISON_OVERRIDE_LENGTH = 0x65, + UNIT_STAT_ITEM_FASTERBLOCKRATE = 0x66, + UNIT_STAT_SKILL_BYPASS_UNDEAD = 0x67, + UNIT_STAT_SKILL_BYPASS_DEMONS = 0x68, + UNIT_STAT_ITEM_FASTERCASTRATE = 0x69, + UNIT_STAT_SKILL_BYPASS_BEASTS = 0x6A, + UNIT_STAT_ITEM_SINGLESKILL = 0x6B, + UNIT_STAT_ITEM_RESTINPEACE = 0x6C, + UNIT_STAT_CURSE_RESISTANCE = 0x6D, + UNIT_STAT_ITEM_POISONLENGTHRESIST = 0x6E, + UNIT_STAT_ITEM_NORMALDAMAGE = 0x6F, + UNIT_STAT_ITEM_HOWL = 0x70, + UNIT_STAT_ITEM_STUPIDITY = 0x71, + UNIT_STAT_ITEM_DAMAGETOMANA = 0x72, + UNIT_STAT_ITEM_IGNORETARGETAC = 0x73, + UNIT_STAT_ITEM_FRACTIONALTARGETAC = 0x74, + UNIT_STAT_ITEM_PREVENTHEAL = 0x75, + UNIT_STAT_ITEM_HALFFREEZEDURATION = 0x76, + UNIT_STAT_ITEM_TOHIT_PERCENT = 0x77, + UNIT_STAT_ITEM_DAMAGETARGETAC = 0x78, + UNIT_STAT_ITEM_DEMONDAMAGE_PERCENT = 0x79, + UNIT_STAT_ITEM_UNDEADDAMAGE_PERCENT = 0x7A, + UNIT_STAT_ITEM_DEMON_TOHIT = 0x7B, + UNIT_STAT_ITEM_UNDEAD_TOHIT = 0x7C, + UNIT_STAT_ITEM_THROWABLE = 0x7D, + UNIT_STAT_ITEM_ELEMSKILL = 0x7E, + UNIT_STAT_ITEM_ALLSKILLS = 0x7F, + UNIT_STAT_ITEM_ATTACKERTAKESLIGHTDAMAGE = 0x80, + UNIT_STAT_IRONMAIDEN_LEVEL = 0x81, + UNIT_STAT_LIFETAP_LEVEL = 0x82, + UNIT_STAT_THORNS_PERCENT = 0x83, + UNIT_STAT_BONEARMOR = 0x84, + UNIT_STAT_BONEARMORMAX = 0x85, + UNIT_STAT_ITEM_FREEZE = 0x86, + UNIT_STAT_ITEM_OPENWOUNDS = 0x87, + UNIT_STAT_ITEM_CRUSHINGBLOW = 0x88, + UNIT_STAT_ITEM_KICKDAMAGE = 0x89, + UNIT_STAT_ITEM_MANAAFTERKILL = 0x8A, + UNIT_STAT_ITEM_HEALAFTERDEMONKILL = 0x8B, + UNIT_STAT_ITEM_EXTRABLOOD = 0x8C, + UNIT_STAT_ITEM_DEADLYSTRIKE = 0x8D, + UNIT_STAT_ITEM_ABSORBFIRE_PERCENT = 0x8E, + UNIT_STAT_ITEM_ABSORBFIRE = 0x8F, + UNIT_STAT_ITEM_ABSORBLIGHT_PERCENT = 0x90, + UNIT_STAT_ITEM_ABSORBLIGHT = 0x91, + UNIT_STAT_ITEM_ABSORBMAGIC_PERCENT = 0x92, + UNIT_STAT_ITEM_ABSORBMAGIC = 0x93, + UNIT_STAT_ITEM_ABSORBCOLD_PERCENT = 0x94, + UNIT_STAT_ITEM_ABSORBCOLD = 0x95, + UNIT_STAT_ITEM_SLOW = 0x96, + UNIT_STAT_ITEM_AURA = 0x97, + UNIT_STAT_ITEM_INDESCTRUCTIBLE = 0x98, + UNIT_STAT_ITEM_CANNOTBEFROZEN = 0x99, + UNIT_STAT_ITEM_STAMINADRAINPCT = 0x9A, + UNIT_STAT_ITEM_REANIMATE = 0x9B, + UNIT_STAT_ITEM_PIERCE = 0x9C, + UNIT_STAT_ITEM_MAGICARROW = 0x9D, + UNIT_STAT_ITEM_EXPLOSIVEARROW = 0x9E, + UNIT_STAT_ITEM_THROW_MINDAMAGE = 0x9F, + UNIT_STAT_ITEM_THROW_MAXDAMAGE = 0xA0, + UNIT_STAT_SKILL_HANDOFATHENA = 0xA1, + UNIT_STAT_SKILL_STAMINAPERCENT = 0xA2, + UNIT_STAT_SKILL_PASSIVE_STAMINAPERCENT = 0xA3, + UNIT_STAT_SKILL_CONCENTRATION = 0xA4, + UNIT_STAT_SKILL_ENCHANT = 0xA5, + UNIT_STAT_SKILL_PIERCE = 0xA6, + UNIT_STAT_SKILL_CONVICTION = 0xA7, + UNIT_STAT_SKILL_CHILLINGARMOR = 0xA8, + UNIT_STAT_SKILL_FRENZY = 0xA9, + UNIT_STAT_SKILL_DECREPIFY = 0xAA, + UNIT_STAT_SKILL_ARMOR_PERCENT = 0xAB, + UNIT_STAT_ALIGNMENT = 0xAC, + UNIT_STAT_TARGET0 = 0xAD, + UNIT_STAT_TARGET1 = 0xAE, + UNIT_STAT_GOLDLOST = 0xAF, + UNIT_STAT_CONVERSION_LEVEL = 0xB0, + UNIT_STAT_CONVERSION_MAXHP = 0xB1, + UNIT_STAT_UNIT_DOOVERLAY = 0xB2, + UNIT_STAT_ATTACK_VS_MONTYPE = 0xB3, + UNIT_STAT_DAMAGE_VS_MONTYPE = 0xB4, + UNIT_STAT_FADE = 0xB5, + UNIT_STAT_ARMOR_OVERRIDE_PERCENT = 0xB6, + UNIT_STAT_UNUSED183 = 0xB7, + UNIT_STAT_UNUSED184 = 0xB8, + UNIT_STAT_UNUSED185 = 0xB9, + UNIT_STAT_UNUSED186 = 0xBA, + UNIT_STAT_UNUSED187 = 0xBB, + UNIT_STAT_ITEM_ADDSKILL_TAB = 0xBC, + UNIT_STAT_UNUSED189 = 0xBD, + UNIT_STAT_UNUSED190 = 0xBE, + UNIT_STAT_UNUSED191 = 0xBF, + UNIT_STAT_UNUSED192 = 0xC0, + UNIT_STAT_UNUSED193 = 0xC1, + UNIT_STAT_ITEM_NUMSOCKETS = 0xC2, + UNIT_STAT_ITEM_SKILLONATTACK = 0xC3, + UNIT_STAT_ITEM_SKILLONKILL = 0xC4, + UNIT_STAT_ITEM_SKILLONDEATH = 0xC5, + UNIT_STAT_ITEM_SKILLONHIT = 0xC6, + UNIT_STAT_ITEM_SKILLONLEVELUP = 0xC7, + UNIT_STAT_UNUSED200 = 0xC8, + UNIT_STAT_ITEM_SKILLONGETHIT = 0xC9, + UNIT_STAT_UNUSED202 = 0xCA, + UNIT_STAT_UNUSED203 = 0xCB, + UNIT_STAT_ITEM_CHARGED_SKILL = 0xCC, + UNIT_STAT_UNUSED204 = 0xCD, + UNIT_STAT_UNUSED205 = 0xCE, + UNIT_STAT_UNUSED206 = 0xCF, + UNIT_STAT_UNUSED207 = 0xD0, + UNIT_STAT_UNUSED208 = 0xD1, + UNIT_STAT_UNUSED209 = 0xD2, + UNIT_STAT_UNUSED210 = 0xD3, + UNIT_STAT_UNUSED211 = 0xD4, + UNIT_STAT_UNUSED212 = 0xD5, + UNIT_STAT_ITEM_ARMOR_PERLEVEL = 0xD6, + UNIT_STAT_ITEM_ARMORPERCENT_PERLEVEL = 0xD7, + UNIT_STAT_ITEM_HP_PERLEVEL = 0xD8, + UNIT_STAT_ITEM_MANA_PERLEVEL = 0xD9, + UNIT_STAT_ITEM_MAXDAMAGE_PERLEVEL = 0xDA, + UNIT_STAT_ITEM_MAXDAMAGE_PERCENT_PERLEVEL = 0xDB, + UNIT_STAT_ITEM_STRENGTH_PERLEVEL = 0xDC, + UNIT_STAT_ITEM_DEXTERITY_PERLEVEL = 0xDD, + UNIT_STAT_ITEM_ENERGY_PERLEVEL = 0xDE, + UNIT_STAT_ITEM_VITALITY_PERLEVEL = 0xDF, + UNIT_STAT_ITEM_TOHIT_PERLEVEL = 0xE0, + UNIT_STAT_ITEM_TOHITPERCENT_PERLEVEL = 0xE1, + UNIT_STAT_ITEM_COLD_DAMAGEMAX_PERLEVEL = 0xE2, + UNIT_STAT_ITEM_FIRE_DAMAGEMAX_PERLEVEL = 0xE3, + UNIT_STAT_ITEM_LTNG_DAMAGEMAX_PERLEVEL = 0xE4, + UNIT_STAT_ITEM_POIS_DAMAGEMAX_PERLEVEL = 0xE5, + UNIT_STAT_ITEM_RESIST_COLD_PERLEVEL = 0xE6, + UNIT_STAT_ITEM_RESIST_FIRE_PERLEVEL = 0xE7, + UNIT_STAT_ITEM_RESIST_LTNG_PERLEVEL = 0xE8, + UNIT_STAT_ITEM_RESIST_POIS_PERLEVEL = 0xE9, + UNIT_STAT_ITEM_ABSORB_COLD_PERLEVEL = 0xEA, + UNIT_STAT_ITEM_ABSORB_FIRE_PERLEVEL = 0xEB, + UNIT_STAT_ITEM_ABSORB_LTNG_PERLEVEL = 0xEC, + UNIT_STAT_ITEM_ABSORB_POIS_PERLEVEL = 0xED, + UNIT_STAT_ITEM_THORNS_PERLEVEL = 0xEE, + UNIT_STAT_ITEM_FIND_GOLD_PERLEVEL = 0xEF, + UNIT_STAT_ITEM_FIND_MAGIC_PERLEVEL = 0xF0, + UNIT_STAT_ITEM_REGENSTAMINA_PERLEVEL = 0xF1, + UNIT_STAT_ITEM_STAMINA_PERLEVEL = 0xF2, + UNIT_STAT_ITEM_DAMAGE_DEMON_PERLEVEL = 0xF3, + UNIT_STAT_ITEM_DAMAGE_UNDEAD_PERLEVEL = 0xF4, + UNIT_STAT_ITEM_TOHIT_DEMON_PERLEVEL = 0xF5, + UNIT_STAT_ITEM_TOHIT_UNDEAD_PERLEVEL = 0xF6, + UNIT_STAT_ITEM_CRUSHINGBLOW_PERLEVEL = 0xF7, + UNIT_STAT_ITEM_OPENWOUNDS_PERLEVEL = 0xF8, + UNIT_STAT_ITEM_KICK_DAMAGE_PERLEVEL = 0xF9, + UNIT_STAT_ITEM_DEADLYSTRIKE_PERLEVEL = 0xFA, + UNIT_STAT_ITEM_FIND_GEMS_PERLEVEL = 0xFB, + UNIT_STAT_ITEM_REPLENISH_DURABILITY = 0xFC, + UNIT_STAT_ITEM_REPLENISH_QUANTITY = 0xFD, + UNIT_STAT_ITEM_EXTRA_STACK = 0xFE, + UNIT_STAT_ITEM_FIND_ITEM = 0xFF, + UNIT_STAT_ITEM_SLASH_DAMAGE = 0x100, + UNIT_STAT_ITEM_SLASH_DAMAGE_PERCENT = 0x101, + UNIT_STAT_ITEM_CRUSH_DAMAGE = 0x102, + UNIT_STAT_ITEM_CRUSH_DAMAGE_PERCENT = 0x103, + UNIT_STAT_ITEM_THRUST_DAMAGE = 0x104, + UNIT_STAT_ITEM_THRUST_DAMAGE_PERCENT = 0x105, + UNIT_STAT_ITEM_ABSORB_SLASH = 0x106, + UNIT_STAT_ITEM_ABSORB_CRUSH = 0x107, + UNIT_STAT_ITEM_ABSORB_THRUST = 0x108, + UNIT_STAT_ITEM_ABSORB_SLASH_PERCENT = 0x109, + UNIT_STAT_ITEM_ABSORB_CRUSH_PERCENT = 0x10A, + UNIT_STAT_ITEM_ABSORB_THRUST_PERCENT = 0x10B, + UNIT_STAT_ITEM_ARMOR_BYTIME = 0x10C, + UNIT_STAT_ITEM_ARMORPERCENT_BYTIME = 0x10D, + UNIT_STAT_ITEM_HP_BYTIME = 0x10E, + UNIT_STAT_ITEM_MANA_BYTIME = 0x10F, + UNIT_STAT_ITEM_MAXDAMAGE_BYTIME = 0x110, + UNIT_STAT_ITEM_MAXDAMAGE_PERCENT_BYTIME = 0x111, + UNIT_STAT_ITEM_STRENGTH_BYTIME = 0x112, + UNIT_STAT_ITEM_DEXTERITY_BYTIME = 0x113, + UNIT_STAT_ITEM_ENERGY_BYTIME = 0x114, + UNIT_STAT_ITEM_VITALITY_BYTIME = 0x115, + UNIT_STAT_ITEM_TOHIT_BYTIME = 0x116, + UNIT_STAT_ITEM_TOHITPERCENT_BYTIME = 0x117, + UNIT_STAT_ITEM_COLD_DAMAGEMAX_BYTIME = 0x118, + UNIT_STAT_ITEM_FIRE_DAMAGEMAX_BYTIME = 0x119, + UNIT_STAT_ITEM_LTNG_DAMAGEMAX_BYTIME = 0x11A, + UNIT_STAT_ITEM_POIS_DAMAGEMAX_BYTIME = 0x11B, + UNIT_STAT_ITEM_RESIST_COLD_BYTIME = 0x11C, + UNIT_STAT_ITEM_RESIST_FIRE_BYTIME = 0x11D, + UNIT_STAT_ITEM_RESIST_LTNG_BYTIME = 0x11E, + UNIT_STAT_ITEM_RESIST_POIS_BYTIME = 0x11F, + UNIT_STAT_ITEM_ABSORB_COLD_BYTIME = 0x120, + UNIT_STAT_ITEM_ABSORB_FIRE_BYTIME = 0x121, + UNIT_STAT_ITEM_ABSORB_LTNG_BYTIME = 0x122, + UNIT_STAT_ITEM_ABSORB_POIS_BYTIME = 0x123, + UNIT_STAT_ITEM_FIND_GOLD_BYTIME = 0x124, + UNIT_STAT_ITEM_FIND_MAGIC_BYTIME = 0x125, + UNIT_STAT_ITEM_REGENSTAMINA_BYTIME = 0x126, + UNIT_STAT_ITEM_STAMINA_BYTIME = 0x127, + UNIT_STAT_ITEM_DAMAGE_DEMON_BYTIME = 0x128, + UNIT_STAT_ITEM_DAMAGE_UNDEAD_BYTIME = 0x129, + UNIT_STAT_ITEM_TOHIT_DEMON_BYTIME = 0x12A, + UNIT_STAT_ITEM_TOHIT_UNDEAD_BYTIME = 0x12B, + UNIT_STAT_ITEM_CRUSHINGBLOW_BYTIME = 0x12C + }; + + class d2_common { + public: + static char* get_base(); + static int8_t diablo2::d2_common::get_item_page(structures::unit* item); + static void diablo2::d2_common::empty_inventory_1(structures::inventory* inv); + static void diablo2::d2_common::empty_inventory_2(structures::inventory* inv); + static void diablo2::d2_common::free_trade_inventory(structures::inventory* inv); + static int32_t get_item_type_from_unit(structures::unit* item); + static int32_t diablo2::d2_common::get_previous_interact_guid(structures::unit* player); + static int32_t get_inventory_index(structures::unit* item, char page, BOOL lod); + static void* get_inventory_data(int32_t index, int32_t zero, char* data); + static structures::unit* get_item_at_cell(structures::inventory* inv, uint32_t cellx, uint32_t celly, + uint32_t* pcellx, uint32_t* pcelly, int32_t invIndex, uint8_t page); + static uint32_t can_put_into_slot(structures::inventory* inv, structures::unit* item, uint32_t x, uint32_t y, + uint32_t invIndex, structures::unit** lastBlockingUnit, uint32_t* lastBlockingUnitIndex, uint8_t page); + + static uint32_t get_item_type(structures::unit* item); + static uint32_t get_item_type_class(structures::unit* item); + static uint32_t diablo2::d2_common::get_item_primary_weapon_class(structures::unit* item); + static uint32_t diablo2::d2_common::check_item_type_equiv(uint32_t itemtype, uint32_t itemtype_equiv); + + static structures::unit* inv_remove_item(structures::inventory* inventory, structures::unit* item); + static BOOL inv_add_item(structures::inventory* inv, structures::unit* item, uint32_t x, uint32_t y, + uint32_t invIndex, BOOL isClient, uint8_t page); + static BOOL inv_update_item(structures::inventory* inv, structures::unit* item, BOOL isClient); + + static structures::items_line* get_item_record(uint32_t guid); + static structures::item_types_line* get_item_type_record(uint32_t typeId); + + static uint32_t get_maximum_character_gold(structures::unit* player); + static uint32_t get_item_unique_index(structures::unit* item); + static int32_t set_stat(structures::unit* unit, unit_stats_t stat, uint32_t value, int16_t param); + static int32_t get_stat(structures::unit* unit, unit_stats_t stat, int16_t param); + static int32_t get_stat_signed(structures::unit* unit, unit_stats_t stat, int16_t param); + + static int32_t _10111(int32_t* x, int32_t* y); + static int32_t _10116(int32_t x1, int32_t y1, int32_t* x, int32_t* y); + + static structures::room* get_room_from_unit(structures::unit* unit); + + static int32_t get_unit_size_x(structures::unit* unit); + static int32_t get_unit_size_y(structures::unit* unit); + + static int32_t get_distance_between_units(structures::unit* unit1, structures::unit* unit2); + + static int32_t get_unit_x(structures::path* path); + static int32_t get_unit_y(structures::path* path); + static int32_t get_unit_precise_x(structures::unit* unit); + static int32_t get_unit_precise_y(structures::unit* unit); + + static int32_t get_item_quality(structures::unit* item); + static uint32_t diablo2::d2_common::set_unit_mode(structures::unit* item, uint32_t mode); + + static structures::unit* get_target_from_path(structures::path* path); + static structures::unit* diablo2::d2_common::get_first_inventory_item(structures::inventory* inv); + static structures::unit* diablo2::d2_common::get_last_inventory_item(structures::inventory* inv); + static structures::unit* diablo2::d2_common::get_next_inventory_item(structures::unit* prev_item); + static uint32_t diablo2::d2_common::get_max_cube_recipes(); + static void diablo2::d2_common::free_inventory(structures::inventory* inventory); + static void diablo2::d2_common::refresh_unit_inventory(structures::unit* unit, bool set_update_flags); + static void diablo2::d2_common::update_trade(structures::inventory* inventory, structures::unit* item); + //static void diablo2::d2_common::set_item_flags(structures::unit* item, structures::itemflags_t dwFlag, bool bSet); + }; +} diff --git a/include/diablo2/d2game.h b/include/diablo2/d2game.h new file mode 100644 index 0000000..6217b56 --- /dev/null +++ b/include/diablo2/d2game.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +namespace diablo2 { + namespace structures { + struct game_server; + struct net_client; + struct game; + struct unit; + } + + class d2_game { + public: + static char* get_base(); + + static void enqueue_packet(structures::net_client* client, void* packet, size_t size); + + static uint32_t* get_game_id_array_begin(); + static uint32_t* get_game_id_array_end(); + + static structures::game_server* get_game_server(); + static structures::game* get_game(structures::game_server* gs, uint32_t gameId); + + static structures::game* get_game_from_client_id(int32_t id); + static structures::net_client* get_net_client_from_id(structures::game* game, int32_t id); + static structures::net_client* diablo2::d2_game::get_net_client_from_id_2(structures::game* game, int32_t id); + + static structures::unit* get_player_pet(structures::game* game, structures::unit* unit, uint32_t type, uint32_t index); + + static structures::unit* get_server_unit(structures::game* game, diablo2::structures::unit_type_t type, uint32_t uniqueid); + static structures::npc_record* diablo2::d2_game::get_npc_record(structures::game* game, structures::unit* npc, structures::unit** ptnpc); + static void diablo2::d2_game::free_gamble(structures::game* game, structures::unit* player, structures::unit* npc, structures::npc_record* npcrecord); + static void diablo2::d2_game::fill_gamble(structures::game* game, structures::unit* player, structures::unit* npc, structures::npc_record* npcrecord); + static void diablo2::d2_game::create_vendor_cache1(structures::game* game, structures::unit* player, structures::unit* npc, uint32_t param, bool bGamble); + static void diablo2::d2_game::create_vendor_cache2(structures::game* game, structures::unit* player, structures::unit* npc, uint32_t param, bool bGamble); + + static int32_t identify_item(structures::game* game, structures::unit* player, structures::unit* item); + static int32_t pickup_gold_pile(structures::game* game, structures::unit* unit, structures::unit* item); + //static int32_t pickup_item(structures::game* game, structures::unit* unit, structures::unit* item); + static bool __fastcall pickup_item(structures::game* game, structures::unit* player, uint32_t guid, uint32_t* ptrItemCarried); + static structures::unit* get_unit_owner(structures::game* game, structures::unit* unit); + static void* iterate_unit_pets(structures::game* game, structures::unit* unit, + const std::function& cb); + + static void update_inventory_items(structures::game* game, structures::unit* player); + static uint32_t __fastcall diablo2::d2_game::transmogrify(diablo2::structures::game* game, diablo2::structures::unit* player); + }; +} diff --git a/include/diablo2/d2gfx.h b/include/diablo2/d2gfx.h new file mode 100644 index 0000000..c4df129 --- /dev/null +++ b/include/diablo2/d2gfx.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace diablo2 { + namespace structures { + struct gfxdata; + } + + class d2_gfx { + public: + static char* get_base(); + + static bool check_perspective_mode(); + static bool check_perspective_coords(int32_t x, int32_t y); + static int32_t adjust_perspective_coords(int32_t x, int32_t y, int32_t* adjustX, int32_t* adjustY); + static int32_t get_resolution_mode(); + static void draw_image(structures::gfxdata* data, uint32_t x, uint32_t y, int32_t gamma, int32_t drawType, void* palette); + }; +} \ No newline at end of file diff --git a/include/diablo2/d2lang.h b/include/diablo2/d2lang.h new file mode 100644 index 0000000..8cca1d6 --- /dev/null +++ b/include/diablo2/d2lang.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +namespace diablo2 { + namespace structures { + struct unit; + } + + class d2_lang { + public: + static char* get_base(); + static wchar_t* get_string_from_index(short); + }; +} \ No newline at end of file diff --git a/include/diablo2/d2launch.h b/include/diablo2/d2launch.h new file mode 100644 index 0000000..4e4e537 --- /dev/null +++ b/include/diablo2/d2launch.h @@ -0,0 +1,8 @@ +#pragma once + +namespace diablo2 { + class d2_launch { + public: + static char* get_base(); + }; +} diff --git a/include/diablo2/d2net.h b/include/diablo2/d2net.h new file mode 100644 index 0000000..63a0d91 --- /dev/null +++ b/include/diablo2/d2net.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace diablo2 { + namespace structures { + struct game_server; + } + + class d2_net { + public: + static char* get_base(); + + static int32_t send_to_server(int32_t queue, void* data, size_t size); + static int32_t send_to_client(int32_t queue, int32_t clientId, void* packet, size_t size); + }; +} \ No newline at end of file diff --git a/include/diablo2/d2win.h b/include/diablo2/d2win.h new file mode 100644 index 0000000..f9196b7 --- /dev/null +++ b/include/diablo2/d2win.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +namespace diablo2 { + enum ui_color_t { + UI_COLOR_WHITE = 0x0, + UI_COLOR_RED = 0x1, + UI_COLOR_LIGHT_GREEN = 0x2, + UI_COLOR_BLUE = 0x3, + UI_COLOR_DARK_GOLD = 0x4, + UI_COLOR_GREY = 0x5, + UI_COLOR_BLACK = 0x6, + UI_COLOR_GOLD = 0x7, + UI_COLOR_ORANGE = 0x8, + UI_COLOR_YELLOW = 0x9, + UI_COLOR_DARK_GREEN = 0xA, + UI_COLOR_PURPLE = 0xB, + UI_COLOR_GREEN = 0xC, + UI_COLOR_WHITE2 = 0xD, + UI_COLOR_BLACK2 = 0xE, + UI_COLOR_DARK_WHITE = 0xF, + UI_COLOR_LIGHT_GREY = 0x10, + }; + + enum ui_font_t { + UI_FONT_8 = 0, + UI_FONT_16 = 1, + UI_FONT_30 = 2, + UI_FONT_42 = 3, + UI_FONT_FORMAL10 = 4, + UI_FONT_FORMAL12 = 5, + UI_FONT_6 = 6, + UI_FONT_24 = 7, + UI_FONT_FORMAL11 = 8, + UI_FONT_EXOCET10 = 9, + UI_FONT_RIDICULOUS = 10, + UI_FONT_EXOCET8 = 11, + UI_FONT_REALLYTHELASTSUCKER = 12, + UI_FONT_INGAMECHAT = 13 + }; + + class d2_win { + public: + static char* get_base(); + + static int32_t get_text_pixel_width(wchar_t* str); + static void draw_text(wchar_t* str, uint32_t x, uint32_t y, ui_color_t color, int32_t transTbl); + static void draw_boxed_text(wchar_t* str, uint32_t x, uint32_t y, int32_t paletteIndex, int32_t transTbl, ui_color_t color); + static void set_popup_properties(wchar_t* str, uint32_t x, uint32_t y, ui_color_t color, int32_t align); + static void draw_popup(); + + static ui_font_t get_current_font(); + static int32_t get_current_font_height(); + static int32_t set_current_font(ui_font_t font); + + static void* load_mpq(char* dllName, char* mpqName, char* mpqTitle, int32_t overrideFlags); + static bool unload_mpq(void* mpq); + }; +} \ No newline at end of file diff --git a/include/diablo2/fog.h b/include/diablo2/fog.h new file mode 100644 index 0000000..a092d5b --- /dev/null +++ b/include/diablo2/fog.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace diablo2 { + namespace structures { + struct file_handle; + } + + class fog { + public: + static char* get_base(); + + static void get_save_path(char* buffer, size_t bufferSize); + + static bool mpq_open_file(char* path, structures::file_handle** outHandle); + static bool mpq_close_file(structures::file_handle* handle); + static bool mpq_read_file(structures::file_handle* handle, void* buffer, size_t size, size_t* bytesToRead); + static size_t mpq_get_file_size(structures::file_handle* handle, size_t* compressedSize); + }; +} \ No newline at end of file diff --git a/include/diablo2/storm.h b/include/diablo2/storm.h new file mode 100644 index 0000000..78b735b --- /dev/null +++ b/include/diablo2/storm.h @@ -0,0 +1,8 @@ +#pragma once + +namespace diablo2 { + class storm { + public: + static char* get_base(); + }; +} diff --git a/include/diablo2/structures/UniqueItems.h b/include/diablo2/structures/UniqueItems.h new file mode 100644 index 0000000..5b9c6e6 --- /dev/null +++ b/include/diablo2/structures/UniqueItems.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct D2UniqueItemsTxt + { + uint16_t unk0x00; //0x00 + char szName[34]; //0x02 + uint32_t dwVersion; //0x24 + union + { + uint32_t dwBaseItemCode; //0x28 + char szBaseItemCode[4]; //0x28 + }; + uint32_t dwUniqueItemFlags; //0x2C + uint32_t dwRarity; //0x30 + uint16_t wLvl; //0x34 + uint16_t wLvlReq; //0x36 + uint8_t nChrTransform; //0x38 + uint8_t nInvTransform; //0x39 + char szFlippyFile[32]; //0x3A + char szInvFile[34]; //0x5A + uint32_t dwCostMult; //0x7C + uint32_t dwCostAdd; //0x80 + uint16_t wDropSound; //0x84 + uint16_t wUseSound; //0x86 + uint32_t dwDropSfxFrame; //0x88 + uint32_t dwProp1; //0x8C + uint32_t dwPar1; //0x90 + uint32_t dwMin1; //0x94 + uint32_t dwMax1; //0x98 + uint32_t dwProp2; //0x9C + uint32_t dwPar2; //0xA0 + uint32_t dwMin2; //0xA4 + uint32_t dwMax2; //0xA8 + uint32_t dwProp3; //0xAC + uint32_t dwPar3; //0xB0 + uint32_t dwMin3; //0xB4 + uint32_t dwMax3; //0xB8 + uint32_t dwProp4; //0xBC + uint32_t dwPar4; //0xC0 + uint32_t dwMin4; //0xC4 + uint32_t dwMax4; //0xC8 + uint32_t dwProp5; //0xCC + uint32_t dwPar5; //0xD0 + uint32_t dwMin5; //0xD4 + uint32_t dwMax5; //0xD8 + uint32_t dwProp6; //0xDC + uint32_t dwPar6; //0xE0 + uint32_t dwMin6; //0xE4 + uint32_t dwMax6; //0xE8 + uint32_t dwProp7; //0xEC + uint32_t dwPar7; //0xF0 + uint32_t dwMin7; //0xF4 + uint32_t dwMax7; //0xF8 + uint32_t dwProp8; //0xFC + uint32_t dwPar8; //0x100 + uint32_t dwMin8; //0x104 + uint32_t dwMax8; //0x10 + uint32_t dwProp9; //0x10C + uint32_t dwPar9; //0x110 + uint32_t dwMin9; //0x114 + uint32_t dwMax9; //0x118 + uint32_t dwProp10; //0x11C + uint32_t dwPar10; //0x120 + uint32_t dwMin10; //0x124 + uint32_t dwMax10; //0x128 + uint32_t dwProp11; //0x12C + uint32_t dwPar11; //0x130 + uint32_t dwMin11; //0x134 + uint32_t dwMax11; //0x138 + uint32_t dwProp12; //0x13C + uint32_t dwPar12; //0x140 + uint32_t dwMin12; //0x144 + uint32_t dwMax12; //0x148 + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/act_map.h b/include/diablo2/structures/act_map.h new file mode 100644 index 0000000..5ecb530 --- /dev/null +++ b/include/diablo2/structures/act_map.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct room; + + struct act_map //ptGame+BC size=0x60 + { + uint32_t is_not_managed; + uint32_t uk4; + uint32_t uk8; //size = 0x488 + room* pt_first_room; + }; + } +} diff --git a/include/diablo2/structures/cellfile.h b/include/diablo2/structures/cellfile.h new file mode 100644 index 0000000..674c0bd --- /dev/null +++ b/include/diablo2/structures/cellfile.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct gfxcell; + + struct cellfile { + int32_t version; //0x00 + struct { + int16_t flags; + int8_t mylastcol; + int8_t mytabno : 1; + }; //0x04 + int32_t format; //0x08 + int32_t termination; //0x0C + int32_t numdirs; //0x10 + int32_t numcells; //0x14 + gfxcell* cells[255]; //0x18 + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/client_unit_list.h b/include/diablo2/structures/client_unit_list.h new file mode 100644 index 0000000..f9f8da6 --- /dev/null +++ b/include/diablo2/structures/client_unit_list.h @@ -0,0 +1,11 @@ +#pragma once + +namespace diablo2 { + namespace structures { + struct unit; + + struct client_unit_list { + unit* unit_list[5][128]; //0x1120 + }; + } +} diff --git a/include/diablo2/structures/damage.h b/include/diablo2/structures/damage.h new file mode 100644 index 0000000..d719ed3 --- /dev/null +++ b/include/diablo2/structures/damage.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct damage { + int32_t hit_flags; //0x00 + int16_t result_flags; //0x04 + int16_t extra; //0x06 + int32_t phys_damage; //0x08 + int32_t en_dmg_pct; //0x0C + int32_t fire_damage; //0x10 + int32_t burn_damage; //0x14 + int32_t burn_len; //0x18 + int32_t ltng_damage; //0x1C + int32_t mag_damage; //0x20 + int32_t cold_damage; //0x24 + int32_t pois_damage; //0x28 + int32_t pois_len; //0x2C + int32_t cold_len; //0x30 + int32_t frz_len; //0x34 + int32_t life_leech; //0x38 + int32_t mana_leech; //0x3C + int32_t stam_leech; //0x40 + int32_t stun_len; //0x44 + int32_t abs_life; //0x48 + int32_t dmg_total; //0x4C + int32_t unk0_x50; //0x50 + int32_t pierce_pct; //0x54 + int32_t damage_rate; //0x58 + int32_t unk0_x5_c; //0x5C + int32_t hit_class; //0x60 + int8_t hit_class_active_set; //0x64 + char conv_type; //0x65 + int8_t unk0_x66[2]; //0x66 + int32_t conv_pct; //0x68 + int8_t unk0_x6_c[4]; //0x6C + }; + } +} diff --git a/include/diablo2/structures/data/bodylocs_line.h b/include/diablo2/structures/data/bodylocs_line.h new file mode 100644 index 0000000..f7d7a82 --- /dev/null +++ b/include/diablo2/structures/data/bodylocs_line.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct bodylocs_line { + union { + uint32_t code; + char str[4]; + }; + }; + } +} diff --git a/include/diablo2/structures/data/elemtypes_line.h b/include/diablo2/structures/data/elemtypes_line.h new file mode 100644 index 0000000..ec5c4e4 --- /dev/null +++ b/include/diablo2/structures/data/elemtypes_line.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct elemtypes_line { + union { + uint32_t code; + char str[4]; + }; + }; + } +} diff --git a/include/diablo2/structures/data/hitclass_line.h b/include/diablo2/structures/data/hitclass_line.h new file mode 100644 index 0000000..ff03a0d --- /dev/null +++ b/include/diablo2/structures/data/hitclass_line.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct hitclass_line { + union { + uint32_t code; + char str[4]; + }; + }; + } +} diff --git a/include/diablo2/structures/data/item_types_line.h b/include/diablo2/structures/data/item_types_line.h new file mode 100644 index 0000000..abb29a6 --- /dev/null +++ b/include/diablo2/structures/data/item_types_line.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct item_types_line { + char code[4]; //0x00 + int16_t equiv1; //0x04 + int16_t equiv2; //0x06 + int8_t repair; //0x08 + int8_t body; //0x09 + int8_t body_loc1; //0x0A + int8_t body_loc2; //0x0B + int16_t shoots; //0x0C + int16_t quiver; //0x0E + int8_t throwable; //0x10 + int8_t reload; //0x11 + int8_t re_equip; //0x12 + int8_t auto_stack; //0x13 + int8_t magic; //0x14 + int8_t rare; //0x15 + int8_t normal; //0x16 + int8_t charm; //0x17 + int8_t gem; //0x18 + int8_t beltable; //0x19 + int8_t max_sock1; //0x1A + int8_t max_sock25; //0x1B + int8_t max_sock40; //0x1C + int8_t treasure_class; //0x1D + int8_t rarity; //0x1E + int8_t staff_mods; //0x1F + int8_t cost_formula; //0x20 + int8_t item_class; //0x21 + int8_t store_page; //0x22 + int8_t var_inv_gfx; //0x23 + char inv_gfx1[32]; //0x24 + char inv_gfx2[32]; //0x44 + char inv_gfx3[32]; //0x64 + char inv_gfx4[32]; //0x84 + char inv_gfx5[32]; //0xA4 + char inv_gfx6[32]; //0xC4 + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/data/items_line.h b/include/diablo2/structures/data/items_line.h new file mode 100644 index 0000000..2b68e2f --- /dev/null +++ b/include/diablo2/structures/data/items_line.h @@ -0,0 +1,211 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + //https://phrozen-library.fandom.com/wiki/Items_line + struct items_line { + char flippy_file[32]; //0x00 + char inv_file[32]; //0x20 + char unique_inv_file[32]; //0x40 + char setinv_file[32]; //0x60 + union { + uint32_t number_code; //0x80 + char string_code[4]; //0x80 + }; + + uint32_t norm_code; //0x84 + uint32_t uber_code; //0x88 + uint32_t ultra_code; //0x8C + uint32_t alternate_gfx; //0x90 + void* spell; //0x94 + uint16_t state; //0x98 + uint16_t cstate1; //0x9A + uint16_t cstate2; //0x9C + uint16_t stat1; //0x9E + uint16_t stat2; //0xA0 + uint16_t stat3; //0xA2 + uint32_t calc1; //0xA4 + uint32_t calc2; //0xA8 + uint32_t calc3; //0xAC + uint32_t len; //0xB0 + uint16_t spell_desc; //0xB4 + uint16_t spell_desc_str; //0xB6 + uint32_t spell_desc_calc; //0xB8 + uint32_t better_gem; //0xBC + uint32_t weapon_class; //0xC0 + uint32_t two_handed_weapon_class; //0xC4 + uint32_t transmog_type; //0xC8 + uint32_t min_ac; //0xCC + uint32_t max_ac; //0xD0 + uint32_t gamble_cost; //0xD4 + uint32_t speed; //0xD8 + uint32_t bit_field1; //0xDC + uint32_t cost; //0xE0 + uint32_t min_stack; //0xE4 + uint32_t max_stack; //0xE8 + uint32_t spawn_stack; //0xEC + uint32_t gem_offset; //0xF0 + uint16_t name_str; //0xF4 + uint16_t version; //0xF6 + uint16_t auto_prefix; //0xF8 + uint16_t missile_type; //0xFA + uint8_t rarity; //0xFC + uint8_t level; //0xFD + uint8_t min_damage; //0xFE + uint8_t max_damage; //0xFF + uint8_t min_misdamage; //0x100 + uint8_t max_misdamage; //0x101 + uint8_t two_hand_min_damage; //0x102 + uint8_t two_hand_max_damage; //0x103 + uint16_t range_adder; //0x104 + uint16_t str_bonus; //0x106 + uint16_t dex_bonus; //0x108 + uint16_t req_str; //0x10A + uint16_t req_dex; //0x10C + uint8_t absorbs; //0x10E + uint8_t inv_width; //0x10F + uint8_t inv_height; //0x110 + uint8_t block; //0x111 + uint8_t durability; //0x112 + uint8_t no_durability; //0x11 + uint8_t missile; //0x114 + uint8_t component; //0x115 + uint8_t right_arm; //0x116 + uint8_t left_arm; //0x117 + uint8_t torso; //0x118 + uint8_t legs; //0x119 + uint8_t right_spad; //0x11A + uint8_t left_spad; //0x11B + uint8_t two_handed; //0x11C + uint8_t useable; //0x11D + uint16_t type; //0x11E + uint16_t type2; //0x120 + uint16_t subtype; //0x122 + uint16_t drop_sound; //0x124 + uint16_t use_sound; //0x126 + uint8_t drop_sfx_frame; //0x128 + uint8_t unique; //0x129 + uint8_t quest; //0x12A + uint8_t quest_diff_check; //0x12B + uint8_t transparent; //0x12C + uint8_t trans_tbl; //0x12D + uint8_t pad0_x12_e; //0x12E + uint8_t light_radius; //0x12F + uint8_t belt; //0x130 + uint8_t auto_belt; //0x131 + uint8_t stackable; //0x132 + uint8_t spawnable; //0x133 + uint8_t spell_icon; //0x134 + uint8_t dur_warning; //0x135 + uint8_t quantity_warning; //0x136 + uint8_t has_inv; //0x137 + uint8_t gem_sockets; //0x138 + uint8_t transmogrify; //0x139 + uint8_t transmog_min; //0x13A + uint8_t transmog_max; //0x13B + uint8_t hit_class; //0x13C + uint8_t one_or_two_handed; //0x13D + uint8_t gem_apply_type; //0x13E + uint8_t level_req; //0x13F + uint8_t magic_lvl; //0x140 + uint8_t transform; //0x141 + uint8_t inv_trans; //0x142 + uint8_t compact_save; //0x143 + uint8_t skip_name; //0x144 + uint8_t nameable; //0x145 + uint8_t akara_min; //0x146 + uint8_t gheed_min; //0x147 + uint8_t charsi_min; //0x148 + uint8_t fara_min; //0x149 + uint8_t lysander_min; //0x14A + uint8_t drognan_min; //0x14B + uint8_t hralti_min; //0x14C + uint8_t alkor_min; //0x14D + uint8_t ormus_min; //0x14E + uint8_t elzix_min; //0x14F + uint8_t asheara_min; //0x150 + uint8_t cain_min; //0x151 + uint8_t halbu_min; //0x152 + uint8_t jamella_min; //0x153 + uint8_t malah_min; //0x154 + uint8_t larzuk_min; //0x155 + uint8_t drehya_min; //0x156 + uint8_t akara_max; //0x157 + uint8_t gheed_max; //0x158 + uint8_t charsi_max; //0x159 + uint8_t fara_max; //0x15A + uint8_t lysander_max; //0x15B + uint8_t drognan_max; //0x15C + uint8_t hralti_max; //0x15D + uint8_t alkor_max; //0x15E + uint8_t ormus_max; //0x15F + uint8_t elzix_max; //0x160 + uint8_t asheara_max; //0x161 + uint8_t cain_max; //0x162 + uint8_t halbu_max; //0x163 + uint8_t jamella_max; //0x164 + uint8_t malah_max; //0x165 + uint8_t larzuk_max; //0x166 + uint8_t drehya_max; //0x167 + uint8_t akara_magic_min; //0x168 + uint8_t gheed_magic_min; //0x169 + uint8_t charsi_magic_min; //0x16A + uint8_t fara_magic_min; //0x16B + uint8_t lysander_magic_min; //0x16C + uint8_t drognan_magic_min; //0x16D + uint8_t hralti_magic_min; //0x16E + uint8_t alkor_magic_min; //0x16F + uint8_t ormus_magic_min; //0x170 + uint8_t elzix_magic_min; //0x171 + uint8_t asheara_magic_min; //0x172 + uint8_t cain_magic_min; //0x173 + uint8_t halbu_magic_min; //0x174 + uint8_t jamella_magic_min; //0x175 + uint8_t malah_magic_min; //0x176 + uint8_t larzuk_magic_min; //0x177 + uint8_t drehya_magic_min; //0x178 + uint8_t akara_magic_max; //0x179 + uint8_t gheed_magic_max; //0x17A + uint8_t charsi_magic_max; //0x17B + uint8_t fara_magic_max; //0x17C + uint8_t lysander_magic_max; //0x17D + uint8_t drognan_magic_max; //0x17E + uint8_t hralti_magic_max; //0x17F + uint8_t alkor_magic_max; //0x180 + uint8_t ormus_magic_max; //0x181 + uint8_t elzix_magic_max; //0x182 + uint8_t asheara_magic_max; //0x183 + uint8_t cain_magic_max; //0x184 + uint8_t halbu_magic_max; //0x185 + uint8_t jamella_magic_max; //0x186 + uint8_t malah_magic_max; //0x187 + uint8_t larzuk_magic_max; //0x188 + uint8_t drehya_magic_max; //0x189 + uint8_t akara_magic_lvl; //0x18A + uint8_t gheed_magic_lvl; //0x18B + uint8_t charsi_magic_lvl; //0x18C + uint8_t fara_magic_lvl; //0x18D + uint8_t lysander_magic_lvl; //0x18E + uint8_t drognan_magic_lvl; //0x18F + uint8_t hralti_magic_lvl; //0x190 + uint8_t alkor_magic_lvl; //0x191 + uint8_t ormus_magic_lvl; //0x192 + uint8_t elzix_magic_lvl; //0x193 + uint8_t asheara_magic_lvl; //0x194 + uint8_t cain_magic_lvl; //0x195 + uint8_t halbu_magic_lvl; //0x196 + uint8_t jamella_magic_lvl; //0x197 + uint8_t malah_magic_lvl; //0x198 + uint8_t larzuk_magic_lvl; //0x199 + uint8_t drehya_magic_lvl; //0x19A + uint8_t pad0_x19_b; //0x19B + uint32_t nightmare_upgrade; //0x19C + uint32_t hell_upgrade; //0x1A0 + uint8_t perm_store_item; //0x1A4 + uint8_t multi_buy; //0x1A5 + uint16_t pad0_x1_a6; //0x1A6 + }; + } +} diff --git a/include/diablo2/structures/data/monmode_line.h b/include/diablo2/structures/data/monmode_line.h new file mode 100644 index 0000000..9754b9d --- /dev/null +++ b/include/diablo2/structures/data/monmode_line.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct monmode_line { + union { + uint32_t code; + char str[4]; + }; + }; + } +} diff --git a/include/diablo2/structures/data/monstats_line.h b/include/diablo2/structures/data/monstats_line.h new file mode 100644 index 0000000..7feeec5 --- /dev/null +++ b/include/diablo2/structures/data/monstats_line.h @@ -0,0 +1,234 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct monstats_line { + uint16_t id; //0x00 + uint16_t base_id; //0x02 + uint16_t next_in_class; //0x04 + uint16_t name_str; //0x06 + uint16_t desc_str; //0x08 + uint16_t unk0_x0_a; //0x0A + uint32_t mon_stats_flags; //0x0C + uint32_t code; //0x10 + uint16_t mon_sound; //0x14 + uint16_t u_mon_sound; //0x16 + uint16_t mon_stats_ex; //0x18 + uint16_t mon_prop; //0x1A + uint16_t mon_type; //0x1C + uint16_t ai; //0x1E + uint16_t spawn; //0x20 + uint8_t spawn_x; //0x22 + uint8_t spawn_y; //0x23 + uint16_t spawn_mode; //0x24 + uint16_t minion1; //0x26 + uint16_t minion2; //0x28 + uint16_t unk0_x2_a; //0x2A + uint8_t party_min; //0x2C + uint8_t party_max; //0x2D + uint8_t rarity; //0x2E + uint8_t min_grp; //0x2F + uint8_t max_grp; //0x30 + uint8_t sparse_populate; //0x31 + uint16_t velocity; //0x32 + uint16_t run; //0x34 + uint16_t unk0_x36; //0x36 + uint16_t unk0_x38; //0x38 + uint16_t miss_a1; //0x3A + uint16_t miss_a2; //0x3C + uint16_t miss_s1; //0x3E + uint16_t miss_s2; //0x40 + uint16_t miss_s3; //0x42 + uint16_t miss_s4; //0x44 + uint16_t miss_c; //0x46 + uint16_t miss_sq; //0x48 + uint16_t unk0_x4_a; //0x4A + uint8_t align; //0x4C + uint8_t trans_lvl; //0x4D + uint8_t threat; //0x4E + uint8_t ai_del; //0x4F + uint8_t ai_del_n; //0x50 + uint8_t ai_del_h; //0x51 + uint8_t ai_dist; //0x52 + uint8_t ai_dist_n; //0x53 + uint16_t ai_dist_h; //0x54 + uint16_t ai_p1; //0x56 + uint16_t ai_p1_n; //0x58 + uint16_t ai_p1_h; //0x5A + uint16_t ai_p2; //0x5C + uint16_t ai_p2_n; //0x5E + uint16_t ai_p2_h; //0x60 + uint16_t ai_p3; //0x62 + uint16_t ai_p3_n; //0x64 + uint16_t ai_p3_h; //0x66 + uint16_t ai_p4; //0x68 + uint16_t ai_p4_n; //0x6A + uint16_t ai_p4_h; //0x6C + uint16_t ai_p5; //0x6E + uint16_t ai_p5_n; //0x70 + uint16_t ai_p5_h; //0x72 + uint16_t ai_p6; //0x74 + uint16_t ai_p6_n; //0x76 + uint16_t ai_p6_h; //0x78 + uint16_t ai_p7; //0x7A + uint16_t ai_p7_n; //0x7C + uint16_t ai_p7_h; //0x7E + uint16_t ai_p8; //0x80 + uint16_t ai_p8_n; //0x82 + uint16_t ai_p8_h; //0x84 + uint16_t treasure_class1; //0x86 + uint16_t treasure_class2; //0x88 + uint16_t treasure_class3; //0x8A + uint16_t treasure_class4; //0x8C + uint16_t treasure_class1_n; //0x8E + uint16_t treasure_class2_n; //0x90 + uint16_t treasure_class3_n; //0x92 + uint16_t treasure_class4_n; //0x94 + uint16_t treasure_class1_h; //0x96 + uint16_t treasure_class2_h; //0x98 + uint16_t treasure_class3_h; //0x9A + uint16_t treasure_class4_h; //0x9C + uint8_t tc_quest_id; //0x9E + uint8_t tc_quest_cp; //0x9F + uint8_t drain; //0xA0 + uint8_t drain_n; //0xA1 + uint8_t drain_h; //0xA2 + uint8_t to_block; //0xA3 + uint8_t b_to_block_n; //0xA4 + uint8_t to_block_h; //0xA5 + uint16_t crit; //0xA6 + uint16_t skill_damage; //0xA8 + uint16_t level; //0xAA + uint16_t level_n; //0xAC + uint16_t level_h; //0xAE + uint16_t min_hp; //0xB0 + uint16_t min_hp_n; //0xB2 + uint16_t min_hp_h; //0xB4 + uint16_t max_hp; //0xB6 + uint16_t max_hp_n; //0xB8 + uint16_t max_hp_h; //0xBA + uint16_t ac; //0xBC + uint16_t ac_n; //0xBE + uint16_t ac_h; //0xC0 + uint16_t a1_th; //0xC2 + uint16_t a1_th_n; //0xC4 + uint16_t a1_th_h; //0xC6 + uint16_t a2_th; //0xC8 + uint16_t a2_th_n; //0xCA + uint16_t a2_th_h; //0xCC + uint16_t s1_th; //0xCE + uint16_t s1_th_n; //0xD0 + uint16_t s1_th_h; //0xD2 + uint16_t exp; //0xD4 + uint16_t exp_n; //0xD6 + uint16_t exp_h; //0xD8 + uint16_t a1_min_d; //0xDA + uint16_t a1_min_d_n; //0xDC + uint16_t a1_min_d_h; //0xDE + uint16_t a1_max_d; //0xE0 + uint16_t a1_max_d_n; //0xE2 + uint16_t a1_max_d_h; //0xE4 + uint16_t a2_min_d; //0xE6 + uint16_t a2_min_d_n; //0xE8 + uint16_t a2_min_d_h; //0xEA + uint16_t a2_max_d; //0xEC + uint16_t a2_max_d_n; //0xEE + uint16_t a2_max_d_h; //0xF0 + uint16_t s1_min_d; //0xF2 + uint16_t s1_min_d_n; //0xF4 + uint16_t s1_min_d_h; //0xF6 + uint16_t s1_max_d; //0xF8 + uint16_t s1_max_d_n; //0xFA + uint16_t s1_max_d_h; //0xFC + uint8_t el1_mode; //0xFE + uint8_t el2_mode; //0xFF + uint8_t el3_mode; //0x100 + uint8_t el1_type; //0x101 + uint8_t el2_type; //0x102 + uint8_t el3_type; //0x103 + uint8_t el1_pct; //0x104 + uint8_t el1_pct_n; //0x105 + uint8_t el1_pct_h; //0x106 + uint8_t el2_pct; //0x107 + uint8_t el2_pct_n; //0x108 + uint8_t el2_pct_h; //0x109 + uint8_t el3_pct; //0x10A + uint8_t el3_pct_n; //0x10B + uint8_t el3_pct_h; //0x10C + uint8_t unk0_x_10d; //0x10D + uint16_t el1_min_d; //0x10E + uint16_t el1_min_d_n; //0x110 + uint16_t el1_min_d_h; //0x112 + uint16_t el2_min_d; //0x114 + uint16_t el2_min_d_n; //0x116 + uint16_t el2_min_d_h; //0x118 + uint16_t el3_min_d; //0x11A + uint16_t el3_min_d_n; //0x11C + uint16_t el3_min_d_h; //0x11E + uint16_t el1_max_d; //0x120 + uint16_t el1_max_d_n; //0x122 + uint16_t el1_max_d_h; //0x124 + uint16_t el2_max_d; //0x126 + uint16_t el2_max_d_n; //0x128 + uint16_t el2_max_d_h; //0x12A + uint16_t el3_max_d; //0x12C + uint16_t el3_max_d_n; //0x12E + uint16_t el3_max_d_h; //0x130 + uint16_t el_1dur; //0x132 + uint16_t el_1dur_n; //0x134 + uint16_t el_1dur_h; //0x136 + uint16_t el_2dur; //0x138 + uint16_t el_2dur_n; //0x13A + uint16_t el_2dur_h; //0x13C + uint16_t el_3dur; //0x13E + uint16_t el_3dur_n; //0x140 + uint16_t el_3dur_h; //0x142 + uint16_t res_dmg; //0x144 + uint16_t res_dmg_n; //0x146 + uint16_t res_dmg_h; //0x148 + uint16_t res_magic; //0x14A + uint16_t res_magic_n; //0x14C + uint16_t res_magic_h; //0x14E + uint16_t res_fire; //0x150 + uint16_t res_fire_n; //0x152 + uint16_t res_fire_h; //0x154 + uint16_t res_light; //0x156 + uint16_t res_light_n; //0x158 + uint16_t res_light_h; //0x15A + uint16_t res_cold; //0x15C + uint16_t res_cold_n; //0x15E + uint16_t res_cold_h; //0x160 + uint16_t res_poison; //0x162 + uint16_t res_poison_n; //0x164 + uint16_t res_poiosn_h; //0x166 + uint8_t cold_effect; //0x168 + uint8_t cold_effect_n; //0x169 + uint16_t cold_effect_h; //0x16A + uint32_t send_skills; //0x16C + uint16_t skill1; //0x170 + uint16_t skill2; //0x172 + uint16_t skill3; //0x174 + uint16_t skill4; //0x176 + uint16_t skill5; //0x178 + uint16_t skill6; //0x17A + uint16_t skill7; //0x17C + uint16_t skill8; //0x17E + uint32_t unk0_x180[6]; //0x180 + uint8_t sk1_lvl; //0x198 + uint8_t sk2_lvl; //0x199 + uint8_t sk3_lvl; //0x19A + uint8_t sk4_lvl; //0x19B + uint8_t sk5_lvl; //0x19C + uint8_t sk6_lvl; //0x19D + uint8_t sk7_lvl; //0x19E + uint8_t sk8_lvl; //0x19F + uint32_t damage_regen; //0x1A0 + uint8_t spl_end_death; //0x1A4 + uint8_t spl_get_mode_chart; //0x1A5 + uint8_t spl_end_generic; //0x1A6 + uint8_t spl_client_end; //0x1A7 + }; + } +} diff --git a/include/diablo2/structures/data/playerclass_line.h b/include/diablo2/structures/data/playerclass_line.h new file mode 100644 index 0000000..5f1cfb3 --- /dev/null +++ b/include/diablo2/structures/data/playerclass_line.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct playerclass_line { + union { + uint32_t code; + char str[4]; + }; + }; + } +} diff --git a/include/diablo2/structures/data/properties_line.h b/include/diablo2/structures/data/properties_line.h new file mode 100644 index 0000000..30f6610 --- /dev/null +++ b/include/diablo2/structures/data/properties_line.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct properties_line { + uint16_t prop; + uint8_t set[8]; + uint16_t val[7]; + uint8_t func[8]; + uint16_t stat[7]; + }; + } +} diff --git a/include/diablo2/structures/data/storepage_line.h b/include/diablo2/structures/data/storepage_line.h new file mode 100644 index 0000000..1c9f989 --- /dev/null +++ b/include/diablo2/structures/data/storepage_line.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct storepage_line { + union { + uint32_t code; + char str[4]; + }; + }; + } +} diff --git a/include/diablo2/structures/data_tables.h b/include/diablo2/structures/data_tables.h new file mode 100644 index 0000000..0f56657 --- /dev/null +++ b/include/diablo2/structures/data_tables.h @@ -0,0 +1,305 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct linkage; + + struct playerclass_line; + struct bodylocs_line; + struct storepage_line; + struct elemtypes_line; + struct hitclass_line; + struct monmode_line; + struct properties_line; + + struct unique_items //size=0x14C (332) + { + WORD uniqueId; //+00 + BYTE uk1[0x20]; //+02 + WORD uniqueNameId; //+22 + BYTE uk2[0x08]; //+24 + union { + BYTE flag; //+2C + struct { + BYTE ukf : 2; + BYTE carry1 : 1; + BYTE ladder : 1; + }; + }; + BYTE uk3[0x11F]; //+2D + }; + + struct data_tables { + playerclass_line* player_class; //+00000000 01415B48 playerclass.txt + linkage* player_class_info; //+00000004 01410524 playerclass info + + bodylocs_line* body_locs; //+00000008 01415578 bodylocs.txt + linkage* body_locs_info; //+0000000C 01410504 bodylocs info + + storepage_line* store_page; //+00000010 01414CC8 storepage.txt + linkage* store_page_info; //+00000014 01415B24 storepage info + + elemtypes_line* elemtypes; //+00000018 01414958 elemtypes.txt + linkage* elemtypes_info; //+0000001C 01415B04 elemtypes info + + hitclass_line* hit_class; //+00000020 01414148 hitclass.txt + linkage* hit_class_info; //+00000024 01415AE4 hitclass info + + monmode_line* mon_mode; //+00000028 01416878 monmode.txt + linkage* mon_mode_info; //+0000002C 01415514 monmode info + + void* pPlayerModeStub; //+00000030 014162E8 plrmode.txt + linkage* iPlayerMode; //+00000034 01414934 plrmode info + void* pSkillCalc; //+00000038 05279860 skillcalc.txt + linkage* iSkillCalc; //+0000003C 01410014 skillcalc info + uint8_t* pSkillsCode; //+00000040 0537A514 skillscode formulae + uint32_t dwSkillsCodeSize; //+00000044 00001768 size of skillscode block + uint32_t dwSkillsCodeSizeEx; //+00000048 00001768 size of skillscode block + uint8_t* pSkillDescCode; //+0000004C 0535C994 skilldesccode formulae + uint32_t dwSkillDescCodeSize; //+00000050 0000107F size of skilldesccode block + uint32_t dwSkillDescCodeSizeEx; //+00000054 0000107F size of skilldesccode block + const char* pMissileCalc; //+00000058 01419C28 misscalc.txt + linkage* iMissileCalc; //+0000005C 01417024 misscalc info + uint8_t* pMissCode; //+00000060 014A4944 misscode formulae + uint32_t dwMissCodeSize; //+00000064 000000D4 size of misscode block + uint32_t dwMissCodeSizeEx; //+00000068 000000D4 size of misscode block + const char* pSkillCode; //+0000006C 052C445C skillcode.txt (Id from skills.txt) + linkage* iSkillCode; //+00000070 0141F084 skillcode info + void* pEvents; //+00000074 0141ACA8 events.txt + linkage* iEvents; //+00000078 0141F064 events info + uint32_t* pCompCodes; //+0000007C 06C4FAFC compcode.txt + linkage* iCompCodes; //+00000080 01410544 compcode info + int nCompCodes; //+00000084 00000073 # of compcode records + void* pMonAI; //+00000088 0564351C monai.txt + linkage* iMonAI; //+0000008C 01414914 monai info + int nMonAI; //+00000090 00000098 # of monai records + linkage* iItems; //+00000094 014BA014 items info + uint8_t* pItemsCode; //+00000098 013FDED8 itemscode formulae + uint32_t dwItemsCodeSize; //+0000009C 000010E0 size of itemscode block + uint32_t dwItemsCodeSizeEx; //+000000A0 000010E0 size of itemscode block + uint32_t* pProperties; //+000000A4 0579E218 properties.txt + linkage* iProperties; //+000000A8 01489464 properties info + int nProperties; //+000000AC 00000110 # of properties records + linkage* iRunes; //+000000B0 014C4774 runes info + uint32_t* pMercDesc; //+000000B4 01417208 hiredesc.txt + linkage* iMercDesc; //+000000B8 01415534 hiredesc info + uint32_t* pStates; //+000000BC 05767EA0 states.txt (limit = 255) + linkage* iStates; //+000000C0 014A16C4 states info + int nStates; //+000000C4 000000BD # of states records + void* pStateMasks; //+000000C8 014A2574 statemasks.txt + uint32_t* fStateMasks[40]; //+000000CC 014A2574 statemasks[40] (statemasks.txt segment array) + short* pProgressiveStates; //+0000016C 014A23E4 progressive-state list + int nProgressiveStates; //+00000170 00000006 # of progressive states + short* pCurseStates; //+00000174 014A2254 curse-state list + int nCurseStates; //+00000178 0000001A # of curse states + short* pTransformStates; //+0000017C 014A20C4 transform-state list + int nTransformStates; //+00000180 00000007 # of transform states + short* pActionStates; //+00000184 014A3E74 action-state list + int nActionStates; //+00000188 00000007 # of action states + short* pColourStates; //+0000018C 014A3CE4 color-state list + int nColourStates; //+00000190 00000002 # of color states + void* pSoundCodes; //+00000194 057656BC soundcode.txt (sound column from sounds.txt) + linkage* iSoundCodes; //+00000198 0141F0E4 soundcode info + int nSoundCodes; //+0000019C 00000000 # of soundcode records (blanked out later) + uint32_t* pHirelings; //+000001A0 055D8CD8 hirelings.txt (limit = 256) + int nHirelings; //+000001A4 00000078 # of hirelings records + int pMercFirst[256]; //+000001A8 00000000 array of 256 integers (namefirst column from hirelings.txt) + int pMercLast[256]; //+000005A8 0000000C array of 256 integers (namelast column from hirelings.txt) + void* pNPCs; //+000009A8 05724F74 npcs.txt + int nNPCs; //+000009AC 00000011 # of npcs records + void* pColours; //+000009B0 01417568 colors.txt + linkage* iColours; //+000009B4 01415554 colors info + linkage* iTreasureClassEx; //+000009B8 014C4714 treasureclassex info + uint32_t* pTreasureClassEx; //+000009BC 05718D98 treasureclassex.txt (limit = 65534 - autotcs#) + int nTreasureClassEx; //+000009C0 0000043C # of treasureclassex records + uint32_t* aTreasureClass[45]; //+000009C4 0571D074 chest treasureclassex list (an array of 45 pointers) + uint32_t* pMonstats; //+00000A78 04FE0014 monstats.txt (limit = 32766) + linkage* iMonstats; //+00000A7C 0143C024 monstats info + int nMonstats; //+00000A80 000002E1 # of monstats records + void* pMonSounds; //+00000A84 013EBC9C monsounds.txt + linkage* iMonSounds; //+00000A88 01438024 monsounds info + int nMonSounds; //+00000A8C 0000008D # of monsounds records + uint32_t* pMonstats2; //+00000A90 05287408 monstats2.txt (no sign of that 1023 record limit) + linkage* iMonstats2; //+00000A94 01502014 monstats2 info + int nMonstats2; //+00000A98 00000245 # of monstats2 records + void* pMonPlace; //+00000A9C 01412648 monplace.txt + linkage* iMonPlace; //+00000AA0 01417BA4 monplace info + int nMonPlace; //+00000AA4 00000025 # of monplace records + void* pMonPreset; //+00000AA8 057248B0 monpreset.txt + void* aMonPresetI[5]; //+00000AAC 057248B0 array of 5 pointers to the monpreset sections for each of the 5 acts + void* aMonPresetII[5]; //+00000AC0 0000002F array of 5 integers (# of records for monpreset in each of the 5 acts) + uint32_t* pSuperUniques; //+00000AD4 05364928 superuniques.txt (limit = 512) + linkage* iSuperUniques; //+00000AD8 0145A1F4 superuniques info + int nSuperUniques; //+00000ADC 00000042 # of superunique records + uint16_t aSuperUniques[66]; //+00000AE0 00010000 array of 66 uint16_ts (the IdNos of the default superuniques, new ones are not added here [thankfully]) + uint32_t* pMissiles; //+00000B64 05590014 missiles.txt (does some fixing for collidetype > 8) + linkage* iMissiles; //+00000B68 01492014 missiles info + int nMissiles; //+00000B6C 000002C4 # of missiles records + uint32_t* pMonLvl; //+00000B70 013B0064 monlvl.txt + int nMonLvl; //+00000B74 0000006F # of monlvl records + void* pMonSeq; //+00000B78 05375900 monseq.txt + linkage* iMonSeq; //+00000B7C 0143A024 monseq info + int nMonSeq; //+00000B80 000003F2 # of monseq records + uint32_t* pMonSeqTable; //+00000B84 0143E7E4 sequences table (created from monseq.txt) + int nMonSeqTable; //+00000B88 0000003C # of sequences + uint32_t* pSkillDesc; //+00000B8C 05741104 skilldesc.txt (limit = 32766) [JL and not JLE] + linkage* iSkillDesc; //+00000B90 014B8024 skilldesc info + int nSkillDesc; //+00000B94 000000DD # of skilldesc records + uint32_t* pSkills; //+00000B98 056E4D78 skills.txt (limit = 32766) [JL and not JLE] + linkage* iSkills; //+00000B9C 014B50E4 skills info + int nSkills; //+00000BA0 00000173 # of skills records + int* nClassSkillCount; //+00000BA4 014B9014 class skill count list + int nHighestClassSkillCount; //+00000BA8 0000001E # highest class skill count + short* nClassSkillList; //+00000BAC 014BCB54 class skill list + int nPassiveSkills; //+00000BB0 0000001C # of passive skills + uint16_t* pPassiveSkills; //+00000BB4 014BCB04 passiveskill list + linkage* iOverlay; //+00000BB8 01484024 overlay info + uint32_t* pOverlay; //+00000BBC 05352F54 overlay.txt + int nOverlay; //+00000BC0 00000125 # of overlay records + uint32_t* pCharStats; //+00000BC4 057AD178 charstats.txt + int nCharStats; //+00000BC8 00000007 # of charstats records + uint32_t* pItemStatCost; //+00000BCC 05219760 itemstatcost.txt (limit = 510) [511 used as END_OF_STATS in 'GF/JM' files] + linkage* iItemStatCost; //+00000BD0 0148C024 itemstatcost info + int nItemStatCost; //+00000BD4 0000016E # of itemstatcost records + void* pOPStats; //+00000BD8 014882A4 opstats nesting table + int nOPStats; //+00000BDC 000000D3 # of nested opstats + uint32_t* pMonEquip; //+00000BE0 013B3798 monequip.txt (limit = 32766) + int nMonEquip; //+00000BE4 0000002D # of monequip records + uint32_t* pPetType; //+00000BE8 05774138 pettype.txt (limit = 511) + linkage* iPetType; //+00000BEC 01486024 pettype info + int nPetType; //+00000BF0 00000014 # of pettype records + linkage* iItemTypes; //+00000BF4 0141E024 itemtypes info + uint32_t* pItemTypes; //+00000BF8 050D14AC itemtypes.txt + int nItemTypes; //+00000BFC 00000069 # of itemtypes records + int nItemTypesIndex; //+00000C00 00000004 (itemtypes#+31)/32 + uint32_t* pItemTypesNest; //+00000C04 0537C41C itemtypes nesting table + linkage* iSets; //+00000C08 014B3CE4 sets info + void* pSets; //+00000C0C 057A162C sets.txt (limit = 32766) + int nSets; //+00000C10 00000020 # of sets records + linkage* iSetItems; //+00000C14 014B1024 setitems info + uint32_t* pSetItems; //+00000C18 056BBAC0 setitems.txt (limit = 32766) + int nSetItems; //+00000C1C 0000007F # of setitems records + linkage* iUniqueItems; //+00000C20 014AA044 uniqueitems info + unique_items* pUniqueItems; //+00000C24 0510E8B4 uniqueitems.txt (limit = 32766) + int nUniqueItems; //+00000C28 00000191 # of uniqueitems records + //linkage* iMonProp; //+00000C2C 01439024 monprop info + //FileMonpropTable* pMonProp; //+00000C30 05132A2C monprop.txt + //int nMonProp; //+00000C34 0000000E # of monprop records + //linkage* iMonType; //+00000C38 0141C024 montype info + //void* pMonType; //+00000C3C 06C4F014 montype.txt + //int nMonType; //+00000C40 0000003B # of montype records + //uint32_t* pMonTypeNest; //+00000C44 0141AAB4 montype nesting table + //int nMonTypeIndex; //+00000C48 00000002 (montype#+31)/32 + //linkage* iMonUMod; //+00000C4C 0145A274 monumod info + //FileMonumodTable* pMonUMod; //+00000C50 0654F014 monumod.txt (limit = 512) + //int nMonUMod; //+00000C54 0000002B # of monumod records + //FileLevelsTable* pLevels; //+00000C58 0562D0EC levels.txt (limit = 1023) + //int nLevels; //+00000C5C 00000096 # of levels records + //FileLevelDefsTable* pLevelDefs; //+00000C60 055E134C leveldefs.txt + //FileLevelPresetTable* pLevelPreset; //+00000C64 0502C7E8 lvlprest.txt + //int nLevelPreset; //+00000C68 00000447 # of lvlprest records + //int nStuff; //+00000C6C 00000006 stuff value (ItemStatCost.txt + 0x140, record #1) + //int cStuff; //+00000C70 0000003F 2 ^ stuff - 1 (used as a controller field for opstats and other special stats) + //FileAnimDataTable* pAnimData; //+00000C74 052369C0 sgptAnimTables (see below) + //FileExperienceTable* pExperience; //+00000C78 05ECF014 experience.txt + //FileDifficultyLevelsTable* pDifficultyLevels; //+00000C7C 05750CD8 difficultylevels.txt (recordNo must equal 3) + //int nDifficultyLevels; //+00000C80 00000003 # of difficultylevels records + //BOOL bCompileTXT; //+00000C84 -txt switch + //void* ExpFieldI[6]; //+00000C88 + //uint32_t unk0[4]; //+00000CA0 + //void* pExpField; //+00000CB0 + //void* ExpFieldII[4]; //+00000CB4 + //void* pCubeMain; //+00000CC4 + //int nCubeMain; //+00000CC8 + //int nInventory; //+00000CCC + //D2InventoryRecordStrc* pInventory; //+00000CD0 + //uint32_t unk1; //+00000CD4 + //int nMasterItemList; //+00000CD8 + //FileItemTable* pMasterItemList; //+00000CDC + //void* pWeapons; //+00000CE0 + //int nWeapons; //+00000CE4 + //void* pArmor; //+00000CE8 + //int nArmor; //+00000CEC + //void* pMisc; //+00000CF0 + //int nMisc; //+00000CF4 + //uint32_t unk2; //+00000CF8 + //int nGems; //+00000CFC + //FileGemsTable* pGems; //+00000D00 + //int nLowQualityItems; //+00000D04 + //FileLowQualityItemsTable* pLowQualityItems; //+00000D08 + //int nBooks; //+00000D0C + //FileBooksTable* pBooks; //+00000D10 + //int nRareAffix; //+00000D14 + //FileRareAffixTable* pRareAffix; //+00000D18 + //FileRareAffixTable* pRareSuffix; //+00000D1C + //FileRareAffixTable* pRarePrefix; //+00000D20 + //int nItemRatio; //+00000D24 + //FileItemratioTable* pItemRatio; //+00000D28 + //uint32_t unk3; //+00000D2C + //int nGamble; //+00000D30 + //uint32_t* pGambleSelection; //+00000D34 + //int* pGambleChooseLimit[100]; //+00000D3C + //int nAffix; //+00000EC8 + //FileAffixTable* pAffix; //+00000ECC [suffixes][prefixes][automagic] + //FileAffixTable* pMagicSuffix; //+00000ED0 + //FileAffixTable* pMagicPrefix; //+00000ED4 + //FileAffixTable* pAutoMagic; //+00000ED8 + //int nRunes; //+00000EDC + //void* pRunes; //+00000EE0 + //int nQualityItems; //+00000EE4 + //FileQualityitemsTable* pQualityItems; //+00000EE8 + //uint32_t unk4; //+00000EEC + //uint32_t dwHiSeed; //+00000EF0 + //uint32_t dwLoSeed; //+00000EF4 + //uint32_t dwAutoMapRand[72]; //+00000EFC + //void* pLvlTypes; //+00001018 + //int* pPortalLevels; //+0000101C + //int nPortalLevels; //+00001020 + //int nLvlTypes; //+00001024 + //FileLevelWarpTable* pLevelWarp; //+00001028 + //int nLevelWarp; //+0000102C + //FileLevelMazeTable* pLevelMaze; //+00001030 + //int nLevelMaze; //+00001034 + //void* pLvlSub; //+00001038 + //int nLvlSub; //+0000103C + //void* sLvlSub; //+00001040 + //uint32_t unk5; //+00001044 + //void* ppLvlTypes; //+00001048 + //uint32_t unk6; //+0000104C + //D2AutoMapShortStrc* pAutoMap; //+00001050 + //int nAutoMap; //+00001054 + //void* pMonLink; //+00001058 + //int nMonItemPercent; //+0000105C + //void* pMonItemPercent; //+00001060 + //void* pUniqueTitle; //+00001064 + //void* pUniquePrefix; //+00001068 + //void* pUniqueSuffix; //+0000106C + //void* pUniqueAppelation; //+00001070 + //int nUniqueTitle; //+00001074 + //int nUniquePrefix; //+00001078 + //int nUniqueSuffix; //+0000107C + //int nUniqueAppelation; //+00001080 + //uint32_t unk7[4]; //+00001084 + //FileShrinesTable* pShrines; //+00001094 + //int nShrines; //+00001098 + //FileObjectsTable* pObjects; //+0000109C + //int nObjects; //+000010A0 + //void* pObjGroup; //+000010A4 + //int nObjectGroup; //+000010A8 + //void* pArmType; //+000010AC + //int nMonMode; //+000010B0 + //FileMonmodeTable* pMonMode[3]; //+000010B4 + //void* pMonLoc; //+000010B8 + //int nObjTypeAndMode; //+000010BC + //void* pObjTypeAndMode; //+000010C0 + //void* pObjType; //+000010C4 + //void* pObjMode; //+000010C8 + //int nPlayerTypeAndMode; //+000010CC + //void* pPlayerTypeAndMode; //+000010D0 + //void* pPlayerType; //+000010D4 + //void* pPlayerMode; //+000010D8 + }; + } +} diff --git a/include/diablo2/structures/file_handle.h b/include/diablo2/structures/file_handle.h new file mode 100644 index 0000000..3462760 --- /dev/null +++ b/include/diablo2/structures/file_handle.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct file_handle { + char pad_0000[0x134]; + void* file; + int32_t position; + int32_t size; + char pad_0001[0x18]; + }; + static_assert(sizeof(file_handle) == 0x158); + } +} diff --git a/include/diablo2/structures/game.h b/include/diablo2/structures/game.h new file mode 100644 index 0000000..a5fe489 --- /dev/null +++ b/include/diablo2/structures/game.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct unit; + + struct game { + char pad_0000[24]; //0x0000 + void* critical_section; //0x0018 + void* memory_pool; //0x001C + char pad_0020[74]; //0x0020 + int8_t game_type; //0x006A + char pad_006_b[2]; //0x006B + int8_t difficulty_level; //0x006D + char pad_006_e[2]; //0x006E + int32_t expansion; //0x0070 + int32_t game_type2; //0x0074 + int32_t item_format; //0x0078 + int32_t init_seed; //0x007C + int32_t object_seed; //0x0080 + int32_t init_seed2; //0x0084 + void* last_client; //0x0088 structure of last player that entered the game + int32_t clients_count; //0x008C + int32_t unit_counts[6]; //0x0090 + int32_t game_frame; //0x00A8 + char pad_00_ac[12]; //0x00AC + void* timer_queue; //0x00B8 + void* drlg_act[5]; //0x00BC + int32_t lo_seed; //0x00D0 + int32_t hi_seed; //0x00D4 + char pad_00d8[20]; //0x00D8 + int32_t monster_seed; //0x00EC + void* monster_region[1024]; //0x00F0 + void* object_controller; //0x10F0 + void* quest_controller; //0x10F4 + void* unit_nodes[10]; //0x10F8 + unit* unit_list[5][128]; //0x1120 + void* tile_list; //0x1B20 + int32_t unique_flags[128]; //0x1B24 + void* npc_control; //0x1D24 + void* arena_control; //0x1D28 + void* party_control; //0x1D2C + int8_t boss_flags[64]; //0x1D30 + int32_t monster_mode_data[17]; //0x1D70 + int32_t monster_mode_data_count; //0x1DB4 + char pad_1db8[12]; //0x1DB8 + int32_t sync_timer; //0x1DC4 + char pad_1dc8[32]; //0x1DC8 + int32_t uber_baal; //0x1DE8 + int32_t uber_diablo; //0x1DEC + int32_t uber_mephisto; //0x1DF0 + }; //Size: 0x1DF4 + static_assert(sizeof(game) == 0x1DF4); + } +} diff --git a/include/diablo2/structures/game_server.h b/include/diablo2/structures/game_server.h new file mode 100644 index 0000000..062ce9e --- /dev/null +++ b/include/diablo2/structures/game_server.h @@ -0,0 +1,7 @@ +#pragma once + +namespace diablo2 { + namespace structures { + struct game_server {}; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/gfxcell.h b/include/diablo2/structures/gfxcell.h new file mode 100644 index 0000000..6131025 --- /dev/null +++ b/include/diablo2/structures/gfxcell.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct gfxcell { + int32_t version; + int32_t width; + int32_t height; + int32_t offset_x; + int32_t offset_y; + void* fram_pixel_data; + }; + } +} diff --git a/include/diablo2/structures/gfxdata.h b/include/diablo2/structures/gfxdata.h new file mode 100644 index 0000000..2dd8aef --- /dev/null +++ b/include/diablo2/structures/gfxdata.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct cellfile; + struct gfxcell; + + struct gfxdata { + gfxcell* cell_init; //+00 + cellfile* cell_file; //+04 + int32_t frame; //+08 + int32_t direction; //+0C + int max_directions; //+10 + int max_frames; //+14 + int32_t flags; //+18 + int8_t state; //+1C + union { + int8_t component; //+1D + int8_t item_flags; //+1D + }; + int8_t unk1_e; //+1E - padding no doubt + int8_t unk1_f; //+1F + int unit_type; //+20 + int unit_index; //+24 + int mode; //+28 + int overlay; //+2C + union { + // [token][component][type][mode][hitclass] + struct { + int32_t token; //+30 + int32_t component; //+34 + int32_t armor_type; //+38 - lit, med , hvy + int32_t mode; //+3C + int32_t hit_class; //+40 + } details; + char unk_name[5][4]; //+30 + }; + const char* name; //+44 + }; + + static_assert(sizeof(gfxdata) == 0x48); + } +} diff --git a/include/diablo2/structures/inventory.h b/include/diablo2/structures/inventory.h new file mode 100644 index 0000000..7fd1b83 --- /dev/null +++ b/include/diablo2/structures/inventory.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct unit; + + struct inventory { + //Offset from Code. Size: 30 or 40 + int32_t tag; //0x0000 + void* memory_pool; //0x0004 + unit* owner; //0x0008 + unit* first_item; //0x000C + unit* last_item; //0x0010 + void* inventory_info; //0x0014 + int32_t inventory_info_count; //0x0018 + int32_t weapon_guid; //0x001C + unit* inventory_owner_item; //0x0020 + int32_t inventory_owner_guid; //0x0024 + int32_t filled_sockets_count; //0x0028 + char pad_002_c[8]; //0x002C + void* first_corpse; //0x0034 + char pad_0038[4]; //0x0038 + int32_t next_corpse_guid; //0x003C + }; + } +} diff --git a/include/diablo2/structures/item_data.h b/include/diablo2/structures/item_data.h new file mode 100644 index 0000000..fcd257c --- /dev/null +++ b/include/diablo2/structures/item_data.h @@ -0,0 +1,120 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct inventory; + struct unit; + + enum class item_quality_t : uint32_t { + ITEM_QUALITY_INFERIOR = 0x01, + ITEM_QUALITY_NORMAL = 0x02, + ITEM_QUALITY_SUPERIOR = 0x03, + ITEM_QUALITY_MAGIC = 0x04, + ITEM_QUALITY_SET = 0x05, + ITEM_QUALITY_RARE = 0x06, + ITEM_QUALITY_UNIQUE = 0x07, + ITEM_QUALITY_CRAFTED = 0x08, + ITEM_QUALITY_TEMPERED = 0x09, + + ITEM_QUALITY_COUNT + }; + + struct item_data { + //Offset from Code. + item_quality_t quality; //+00 + uint32_t seed_low; //+04 + uint32_t seed_hi; //+08 + uint32_t player_id; //+0C #10734 / #10735 (PCInventory->ptPlayer->0C) + uint32_t seed_starting; //+10 + uint32_t flags1; //+14 + union { + uint32_t flags2; //+18 + struct { + uint32_t fuk1 : 1; //0x00000001 + uint32_t is_indentified : 1; //0x00000002 + uint32_t fuk2 : 2; //0x0000000C + uint32_t is_unindentified : 1; //0x00000010 + uint32_t fuk3 : 3; //0x000000E0 + uint32_t is_broken : 1; //0x00000100 + uint32_t fuk4 : 2; //0x00000600 + uint32_t is_socketed : 1; //0x00000800 + uint32_t fuk5 : 10; //0x003FF000 + uint32_t is_etheral : 1; //0x00400000 + uint32_t fuk6 : 3; //0x03800000 + uint32_t is_runeword : 1; //0x04000000 + uint32_t fuk7 : 1; //0x08000000 + uint32_t is_personalized : 1; //0x10000000 + uint32_t fuk8 : 3; //0xE0000000 + }; + }; + + /* + ITEMFLAG_NEWITEM = 0x00000001, + ITEMFLAG_TAGETING = 0x00000004, + ITEMFLAG_UNIDENTIFIED = 0x00000010, + ITEMFLAG_QUANTITY = 0x00000020, + ITEMFLAG_Durability = 0x00000100, + ITEMFLAG_UNKNOWN2 = 0x00000400, + ITEMFLAG_SOCKETED = 0x00000800, + ITEMFLAG_NON_SELLABLE = 0x00001000, + ITEMFLAG_NEWITEM2 = 0x00002000, + ITEMFLAG_UNKNOWN3 = 0x00004000, + ITEMFLAG_CHECKSECPRICE = 0x00010000, + ITEMFLAG_CHECKGAMBLEPRICE = 0x00020000, + ITEMFLAG_UNKNOWN4 = 0x00080000, + ITEMFLAG_INDESTRUCTIBLE(ETHERAL) = 0x00400000, + ITEMFLAG_UNKNOWN5 = 0x00800000, + ITEMFLAG_FROMPLAYER = 0x01000000, + ITEMFLAG_RUNEuint16_t = 0x04000000 + */ + uint32_t guid1; //+1C Global Unique ID 1 + uint32_t guid2; //+20 Global Unique ID 2 + uint32_t guid3; //+24 Global Unique ID 3 + uint32_t unique_id; //+28 + uint8_t ilvl; //+2C + uint8_t uk1[0x03]; //+2D + uint16_t version; //+30 + uint16_t rare_prefix; //+32 + uint16_t rare_suffix; //+34 + uint16_t auto_pref; //+36 + uint16_t prefix[3]; //+38 + uint16_t suffix[3]; //+3E + uint8_t equip_loc; //+44 + /* emplacement si équipé + * 00 = noequip/inBelt + * 01 = head + * 02 = neck + * 03 = tors + * 04 = rarm + * 05 = larm + * 06 = lrin + * 07 = rrin + * 08 = belt + * 09 = feet + * 0A = glov + * 0B = ralt + * 0C = lalt + */ + uint8_t page; //+45 + /* page dans laquel se trouve l'item + * FF = mouse/equip/onEarth + * 00 = inventory + * 01 = cube + * 04 = stash + */ + uint8_t uk4[0x01]; //+46 + uint8_t item_data3; //+47 //D2Common10854 D2Common10853 + uint8_t p_ear_level; //+48 + uint8_t var_gfx; //+49 + char i_name[0x12]; //+4A //inscribed/ear get_name + inventory* inventory; //+5C + unit* pt_prev_item; //+60 + unit* pt_next_item; //+64 + uint8_t uk8[0x01]; //+68 + uint8_t item_data2; //+69 + uint8_t uk9[0x0A]; //+6A + }; + } +} diff --git a/include/diablo2/structures/linkage.h b/include/diablo2/structures/linkage.h new file mode 100644 index 0000000..7320d4d --- /dev/null +++ b/include/diablo2/structures/linkage.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct linkage { + void* link; + uint32_t unk[3]; + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/monster_data.h b/include/diablo2/structures/monster_data.h new file mode 100644 index 0000000..91371b2 --- /dev/null +++ b/include/diablo2/structures/monster_data.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct monstats_line; + + struct monster_data { // sizeof(MonsterData)=0x60 + monstats_line* monstats; + uint8_t components[16]; //+04 + union { + uint16_t flags; //+16 + struct { + uint16_t fuk1 : 1; + uint16_t is_super_unique : 1; + uint16_t is_champion : 1; + uint16_t is_unique : 1; + uint16_t fuk2 : 13; + }; + }; + uint8_t uk1[0x0E]; //+18 + uint16_t super_unique_id; //+26 + void* unknow1; //+28 + uint8_t uk2[0x38]; //+28 + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/net_client.h b/include/diablo2/structures/net_client.h new file mode 100644 index 0000000..33a553a --- /dev/null +++ b/include/diablo2/structures/net_client.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct game; + + struct net_client { + uint32_t client_id; //+00 + uint8_t uk1[0x06]; //+04 + union { //+0A + uint16_t flag; + struct { + uint16_t f1 : 1; + uint16_t f2 : 1; + uint16_t is_hard_core_game : 1; + }; + }; + uint8_t uk2; //+0C + char name[0x10]; //+0D + uint8_t uk3[0x15F]; //+1D + uint8_t* savefile; //+17C + uint32_t final_size; //+180 + uint32_t counter; //+184 + uint32_t current_size; //+188 + uint8_t uk4[0x1C]; //+18C + game* game; //+1A8 + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/npc_record.h b/include/diablo2/structures/npc_record.h new file mode 100644 index 0000000..f75b196 --- /dev/null +++ b/include/diablo2/structures/npc_record.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct inventory; + + struct npc_gamble //sizeof 0xC + { + inventory* pInventory; //+00 + uint32_t dwGUID; //+04 npc_gamble* pNext; //+08 + }; + + struct npc_record //sizeof 0x44 + { + int nNPC; //+00 + inventory* pInventory; //+04 + npc_gamble* pGamble; //+08 + bool bGambleInit; //+0C + uint32_t* pMercData; //+10 //D2MercDataStrc* + uint32_t* pEvent; //+14 //D2NPCEventStrc* + uint32_t* pVendorChain; //+18 //D2VendorChainStrc* + bool bTrading; //+1C + union + { + struct + { + union + { + bool bFlags[8]; //+20 + struct + { + bool bVendorInit; //+20 + bool bHireInit; //+21 + uint8_t nAct; //+22 + bool bTrader; //+23 + bool bLevelRefresh; //+24 + bool bInited; //+25 + bool bForceVendor; //+26 + bool bRefreshInventory; //+27 + }; + }; + + uint32_t dwTicks; //+28 + uint32_t pProxy[4]; //+2C //D2UnitProxyStrc + uint32_t dwUnk; //+3C + uint32_t dwNPCGUID; //+40 + }; + + uint32_t pTrade; //+20 //D2NPCTradeStrc + }; + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/object_data.h b/include/diablo2/structures/object_data.h new file mode 100644 index 0000000..e2d19c3 --- /dev/null +++ b/include/diablo2/structures/object_data.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct objects_bin; + + struct object_data { + objects_bin* pt_objects_bin; + uint8_t level_id; + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/path.h b/include/diablo2/structures/path.h new file mode 100644 index 0000000..dd03254 --- /dev/null +++ b/include/diablo2/structures/path.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct room; + + struct path //(8 dword) + { //Offset from Code. Size: 20 + uint16_t uk1; //+00 + uint16_t mapx; //+02 + uint16_t uk2; //+04 + uint16_t mapy; //+06 + uint32_t uk3; //+08 + uint32_t x; //+0C + uint32_t y; //+10 + uint32_t uk6; //+14 + uint32_t uk7; //+18 + room* pt_room; //+1C + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/player_data.h b/include/diablo2/structures/player_data.h new file mode 100644 index 0000000..1c33122 --- /dev/null +++ b/include/diablo2/structures/player_data.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct net_client; + + struct player_data { + char name[0x10]; //+00 Player Name + void* pt_quest[3]; //+10 Quest Pointers for each difficulty + uint8_t uk1[0x18]; //+1C //before : 0x14 + void* pt_arena_unit; //+34 ptArena for the Unit + uint8_t uk2[0x4]; //+38 //before : 0x7 + uint16_t mp_source_portal_unique_id; //+3C Source Portal Unique_ID + uint8_t uk3[0x2]; //+3E + uint16_t mp_dest_portal_unique_id; //+40 Destination Portal Unique_ID + uint8_t uk4[0x06]; //+42 + uint8_t pt_object_un_id; //+48 Object UniqueID for TownPortals + uint8_t uk5[0x53]; //+49 + net_client* net_client; //+9C ptClient + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/room.h b/include/diablo2/structures/room.h new file mode 100644 index 0000000..6b05fe2 --- /dev/null +++ b/include/diablo2/structures/room.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct unit; + + struct room//size=0x80 + { + //ptRoom +48 0 = spawn new units (monster, objects e.tc), 1 = don't spawn any new units + uint32_t seed1; //+00 + uint32_t seed2; //+04 + uint8_t uk8[0x1C]; //+08 + room* pt_near_rooms; //+24 + uint32_t nb_near_rooms; //+28 + unit* unit; //+2C + uint8_t uk5[0x44]; //+30 + room* pt_next_room; //+74 + union { + uint8_t flags; //+78 + struct { + uint8_t is_generated : 1; + uint8_t is_generated2 : 1;//??? + }; + }; + }; + } +} \ No newline at end of file diff --git a/include/diablo2/structures/unit.h b/include/diablo2/structures/unit.h new file mode 100644 index 0000000..965ce97 --- /dev/null +++ b/include/diablo2/structures/unit.h @@ -0,0 +1,223 @@ +#pragma once + +#include + +#pragma pack(push, 1) + +namespace diablo2 { + namespace structures { + struct skills; + struct game; + struct inventory; + struct stats; + struct path; + struct player_data; + struct item_data; + struct object_data; + struct monster_data; + struct drlg_act; + struct skill_sequence; + struct anim_data; + struct unit_gfxdata; + struct statslistex; + struct quest_record; + struct npc_record; + + enum class unit_type_t : int32_t { + UNIT_TYPE_PLAYER = 0, + UNIT_TYPE_MONSTER = 1, + UNIT_TYPE_OBJECT = 2, + UNIT_TYPE_MISSILE = 3, + UNIT_TYPE_ITEM = 4, + UNIT_TYPE_VIS_TILE = 5 //unused? + }; + + enum player_class_t : int32_t { + PLAYER_CLASS_AMAZON = 0, + PLAYER_CLASS_SORCERESS = 1, + PLAYER_CLASS_NECROMANCER = 2, + PLAYER_CLASS_PALADIN = 3, + PLAYER_CLASS_BARBARIAN = 4, + PLAYER_CLASS_DRUID = 5, + PLAYER_CLASS_ASSASSIN = 6 + }; + + enum itemflags_t : int32_t { + ITEMFLAG_NEWITEM = 0x00000001, + ITEMFLAG_TARGET = 0x00000002, + ITEMFLAG_TARGETING = 0x00000004, + ITEMFLAG_DELETED = 0x00000008, + ITEMFLAG_IDENTIFIED = 0x00000010, + ITEMFLAG_QUANTITY = 0x00000020, + ITEMFLAG_WEAPONSET_IN = 0x00000040, + ITEMFLAG_WEAPONSET_OUT = 0x00000080, + ITEMFLAG_BROKEN = 0x00000100, + ITEMFLAG_REPAIRED = 0x00000200, + ITEMFLAG_UNKNOWN2 = 0x00000400, + ITEMFLAG_SOCKETED = 0x00000800, + ITEMFLAG_NON_SELLABLE = 0x00001000, + ITEMFLAG_INSTORE = 0x00002000, + ITEMFLAG_NOEQUIP = 0x00004000, + ITEMFLAG_NAMED = 0x00008000, + ITEMFLAG_ORGAN = 0x00010000, + ITEMFLAG_SELLCHEAP = 0x00020000, + ITEMFLAG_UNK5 = 0x00040000, + ITEMFLAG_INIT = 0x00080000, + ITEMFLAG_UNK6 = 0x00100000, + ITEMFLAG_COMPACTSAVE = 0x00200000, + ITEMFLAG_ETHEREAL = 0x00400000, + ITEMFLAG_JUSTSAVED = 0x00800000, + ITEMFLAG_PERSONALIZED = 0x01000000, + ITEMFLAG_LOWQUALITY = 0x02000000, + ITEMFLAG_RUNEWORD = 0x04000000, + ITEMFLAG_SHOPITEM = 0x06000000, + ITEMFLAG_ITEM = 0x08000000 + }; + + struct unit { + unit_type_t type; + + union { + player_class_t player_class; + int32_t data_record_index; + }; + + void* memory_pool; + uint32_t guid; + uint32_t mode; + + union { + player_data* player_data; + monster_data* monster_data; + object_data* object_data; + //missile_data* missile_data; + item_data* item_data; + }; + + int8_t act; + int8_t act_padding[0x03]; + drlg_act* drlg_act; + + struct { + uint32_t low_seed; + uint32_t high_seed; + } seed; + uint32_t init_seed; + + path* path; + + skill_sequence* skill_sequence; + uint32_t skill_sequence_frame_count; + uint32_t skill_sequence_frame; + + uint32_t anim_speed; + uint32_t skill_sequence_mode; + + uint32_t current_frame; + uint32_t frame_count; + uint16_t anim_speed_w; + + uint8_t action_frame; + uint8_t pad1; + + anim_data* anim_data; + + unit_gfxdata* gfxdata; + unit_gfxdata* gfxdata_copy; + + statslistex* statslistex; + inventory* inventory; + + union { + struct { + uint32_t interact_guid; + uint32_t interact_type; + uint8_t interacting; + } interaction; + + struct { + void* light_map; + uint32_t start_light_radius; + uint16_t p12_shift_index; + } lightning; + }; + + uint16_t update_type; + unit* update_unit; + + quest_record* quest_record; + uint32_t sparky_chest; + void* timer_args; + + union { + game* game; + uint32_t sound_sync; + }; + + char pad2[0x0C]; + + void* event; + + unit_type_t owner_type; + uint32_t owner_guid; + + char pad3[8]; + + char* hover_text; + + void* skills; + + void* combat; + uint32_t hit_class; + + char pad4[4]; + + uint32_t drop_code; + + char pad5[8]; + + union { + struct { + uint32_t unit_flags; + uint32_t unit_flags_ex; + } flags; + uint64_t flags64; + }; + + char pad6[4]; + + uint32_t node_index; + uint32_t get_tick_count; + + union { + uint32_t get_tick_count2; + void* particle_stream; + }; + + void* timer; + + unit* change_next_unit; //? + unit* prev_unit; + unit* prev_unit_in_room; + + void* msg_first; + void* msg_last; + + bool is_hireling() const { + if (type != unit_type_t::UNIT_TYPE_MONSTER) + return false; + + return (flags.unit_flags & 0x00000200) == 0x00000200; + } + + bool is_pet() const { + if (type != unit_type_t::UNIT_TYPE_MONSTER) + return false; + + return (flags.unit_flags & 0x80000000) == 0x80000000; + } + }; + } +} + +#pragma pack(pop) \ No newline at end of file diff --git a/include/diablo2/utils/mpq_ifstream.h b/include/diablo2/utils/mpq_ifstream.h new file mode 100644 index 0000000..67be04a --- /dev/null +++ b/include/diablo2/utils/mpq_ifstream.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace diablo2 { + namespace structures { + struct file_handle; + } + + namespace utils { + class mpq_ifstream : public std::istream { + class mpq_streambuf : public std::streambuf { + structures::file_handle* m_handle; + char m_data; + public: + explicit mpq_streambuf(const std::string& path); + ~mpq_streambuf(); + + protected: + int_type underflow() override; + }; + + mpq_streambuf m_streambuf; + public: + explicit mpq_ifstream(const std::string& path); + + + }; + } +} diff --git a/include/diablo2/utils/screen.h b/include/diablo2/utils/screen.h new file mode 100644 index 0000000..d459f67 --- /dev/null +++ b/include/diablo2/utils/screen.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace diablo2 { + namespace utils { + class screen { + public: + static void screen_to_world(int32_t sx, int32_t sy, int32_t& wx, int32_t& wy); + static void world_to_screen(int32_t wx, int32_t wy, int32_t& sx, int32_t& sy); + }; + } +} \ No newline at end of file diff --git a/include/fw/pool.h b/include/fw/pool.h new file mode 100644 index 0000000..9bdd4a7 --- /dev/null +++ b/include/fw/pool.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +template +class growing_object_pool { + std::queue m_objects; + uint32_t m_objects_count; + T* (*m_factory)(); +public: + explicit growing_object_pool(T* (*factory)(), uint32_t initialSize = 0) { + m_factory = factory; + + m_objects_count = initialSize; + for (size_t i = 0; i < initialSize; i++) { + m_objects.push(m_factory()); + } + } + + uint32_t get_count() const { + return m_objects_count; + } + + T* get() { + if (m_objects.empty()) { + m_objects.push(m_factory()); + m_objects_count++; + } + + auto result = m_objects.front(); + + m_objects.pop(); + + return result; + } + + void put(T* obj) { + m_objects.push(obj); + } +}; \ No newline at end of file diff --git a/include/fw/singleton.h b/include/fw/singleton.h new file mode 100644 index 0000000..db5f8a5 --- /dev/null +++ b/include/fw/singleton.h @@ -0,0 +1,21 @@ +#pragma once + +template +class singleton { +public: + static T& instance(); + + singleton(const singleton&) = delete; + singleton& operator= (singleton) = delete; + +protected: + struct token {}; + singleton() = default; +}; + +#include +template +T& singleton::instance() { + static const std::unique_ptr instance{ new T{token{}} }; + return *instance; +} \ No newline at end of file diff --git a/src/common/asm_code.cpp b/src/common/asm_code.cpp new file mode 100644 index 0000000..84ed69a --- /dev/null +++ b/src/common/asm_code.cpp @@ -0,0 +1,13 @@ +#include +#include + +#include + +void asm_code::build() { + m_code = reinterpret_cast(hooking::get_executable_memory(GetModuleHandle(nullptr), m_offset)); + memcpy(m_code, m_buffer, m_offset); + + for (auto addr : m_addresses) { + addr->build(m_code); + } +} diff --git a/src/common/config.cpp b/src/common/config.cpp new file mode 100644 index 0000000..e2e0c2b --- /dev/null +++ b/src/common/config.cpp @@ -0,0 +1,18 @@ +#include + +#include +#include +#include +#include + +config::config(token) { + m_json = new nlohmann::json(); + + if (std::filesystem::exists("d2tweaks.json")) { + const std::ifstream cfgFile("d2tweaks.json"); + std::stringstream ss; + ss << cfgFile.rdbuf(); + + m_json->parse(ss.str()); + } +} diff --git a/src/common/hooking.cpp b/src/common/hooking.cpp new file mode 100644 index 0000000..826195f --- /dev/null +++ b/src/common/hooking.cpp @@ -0,0 +1,88 @@ +#include + +#include +#include +#include + +// Size of each memory block. (= page size of VirtualAlloc) +const size_t MEMORY_BLOCK_SIZE = 0x1000; + +static void* allocate_function_stub(void* origin, void* ptr, size_t size) { + static void* current_stub = nullptr; + + if (!current_stub) { + current_stub = + VirtualAlloc(nullptr, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + } + + if (!current_stub) + return nullptr; + + const auto code = static_cast(current_stub); + + DWORD old_protect; + VirtualProtect(code, size, PAGE_EXECUTE_READWRITE, &old_protect); + + *reinterpret_cast(code) = 0xB8; + *reinterpret_cast(code + 1) = reinterpret_cast(ptr); + + *reinterpret_cast(code + 5) = 0xE0FF; + + spdlog::debug("Created function stub at {0}", current_stub); + + current_stub = reinterpret_cast(reinterpret_cast(current_stub) + size); + + return code; +} + +hooking::mh_status_t hooking::details::hook(void* target, void* detour, void** original) { + mh_status_t result = static_cast(MH_CreateHook(target, detour, original)); + MH_EnableHook(nullptr); + return result; +} + +intptr_t hooking::get_executable_memory(void* origin, size_t size) { + const auto stub = reinterpret_cast(allocate_function_stub(origin, nullptr, size)); + memset(reinterpret_cast(stub), 0, size); + return stub; +} + +void* hooking::set_call(void* address, void* function, size_t stubSize) { + const auto stub = reinterpret_cast(allocate_function_stub(address, function, stubSize)); + + DWORD old_protect; + VirtualProtect(address, 5, PAGE_EXECUTE_READWRITE, &old_protect); + + *static_cast(address) = 0xE8; + *reinterpret_cast(static_cast(address) + 1) = stub - reinterpret_cast(address) - 5; + + VirtualProtect(address, 5, old_protect, &old_protect); + + assert(get_call(address) == reinterpret_cast(stub)); + + return reinterpret_cast(stub); +} + +void* hooking::set_jmp(void* address, void* function, size_t stubSize) { + const auto stub = reinterpret_cast(allocate_function_stub(address, function, stubSize)); + + DWORD old_protect; + VirtualProtect(address, 5, PAGE_EXECUTE_READWRITE, &old_protect); + + *static_cast(address) = 0xE9; + *reinterpret_cast(static_cast(address) + 1) = stub - reinterpret_cast(address) - 5; + + VirtualProtect(address, 5, old_protect, &old_protect); + + assert(get_call(address) == reinterpret_cast(stub)); + + return reinterpret_cast(stub); +} + +void* hooking::get_call(void* address) { + auto target = *reinterpret_cast(static_cast(address) + 1); + target += reinterpret_cast(address) + 5; + + return reinterpret_cast(target); +} diff --git a/src/common/ini.cpp b/src/common/ini.cpp new file mode 100644 index 0000000..cc3d868 --- /dev/null +++ b/src/common/ini.cpp @@ -0,0 +1,1095 @@ +/////////////////////////////////////////////////////////////////// +// Ini.cpp +// +// "CIni" is a simple API wrap class used for ini file access. +// The purpose of this class is to make ini file access more +// convenient than direct API calls. +// +// This file is distributed "as is" and without any expressed or implied +// warranties. The author holds no responsibilities for any possible damages +// or loss of data that are caused by use of this file. The user must assume +// the entire risk of using this file. +// +// 7/08/2002 Bin Liu +// +// Update history: +// +// 7/08/2002 -- Initial release. +// 7/14/2002 -- Added "IncreaseInt" and "AppendString" +// 9/02/2002 -- Added "removeProfileSection" and "RemoveProfileEntry" +// 2/09/2003 -- The class has been made unicode-compliant +// 11/04/2003 -- Integrated MFC support, added in new member functions +// for accessing arrays. +// 11/08/2003 -- Fixed "GetString" and "GetPathName" method, changed parameter +// from "LPSTR" to "LPTSTR" +// 11/10/2003 -- Renamed method "GetKeys" to "GetKeyLines", +// Added method "GetKeyNames" +// Added parameter "bTrimString" to method "GetArray" +// 11/14/2003 -- Use "__AFXWIN_H__" instead of "_AFXDLL" to determine MFC presence +// Removed length limit on "m_pszPathName" +// Removed "GetStruct" and "WriteStruct" +// Added "GetDataBlock" and "WriteDataBlock" +// Added "GetChar" and "WriteChar" +// 02/20/2004 -- Fixed a bug in "_TrimString". Thanks to yao_xuejun. +// +/////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////// +// Cini Class Implementation +///////////////////////////////////////////////////////////////////////////////// +//#include "stdafx.h" // include if you got "fatal error C1010: unexpected end of file..." +#include "common\ini.h" +#include +#include +#include +#include + +#define DEF_PROFILE_NUM_LEN 64 // numeric string length, could be quite long for binary format +#define DEF_PROFILE_THRESHOLD 512 // temporary string length +#define DEF_PROFILE_DELIMITER _T(",") // default string delimiter +#define DEF_PROFILE_TESTSTRING _T("{63788286-AE30-4D6B-95DF-3B451C1C79F9}") // Uuid for internal use + +// struct used to be passed to __KeyPairProc as a LPVOID parameter +struct STR_LIMIT +{ + LPTSTR lpTarget; + DWORD dwRemain; + DWORD dwTotalCopied; +}; + +///////////////////////////////////////////////////////////////////////////////// +// Constructors & Destructor +///////////////////////////////////////////////////////////////////////////////// +CIni::CIni() +{ + m_pszPathName = NULL; +} + +CIni::CIni(LPCTSTR lpPathName) +{ + m_pszPathName = NULL; + SetPathName(lpPathName); +} + +CIni::~CIni() +{ + if (m_pszPathName != NULL) + delete [] m_pszPathName; +} + +///////////////////////////////////////////////////////////////////////////////// +// Ini File Path Access +///////////////////////////////////////////////////////////////////////////////// + +// Assign ini file path name +void CIni::SetPathName(LPCTSTR lpPathName) +{ + if (lpPathName == NULL) + { + if (m_pszPathName != NULL) + *m_pszPathName = _T('\0'); + } + else + { + if (m_pszPathName != NULL) + delete [] m_pszPathName; + + m_pszPathName = _tcsdup(lpPathName); + } +} + +// Retrieve ini file path name +DWORD CIni::GetPathName(LPTSTR lpBuffer, DWORD dwBufSize) const +{ + *lpBuffer = _T('\0'); + DWORD dwLen = 0; + if (lpBuffer != NULL) + { + _tcsncpy(lpBuffer, m_pszPathName, dwBufSize); + dwLen = _tcslen(lpBuffer); + } + else + { + // just calculate the required buffer size + dwLen = _tcslen(m_pszPathName); + } + return dwLen; +} + +#ifdef __AFXWIN_H__ +CString CIni::GetPathName() const +{ + return CString(m_pszPathName); +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +// Raw String Access +///////////////////////////////////////////////////////////////////////////////// + +// Get a profile string value, if the buffer size is not large enough, the result +// may be truncated. +DWORD CIni::GetString(LPCTSTR lpSection, LPCTSTR lpKey, LPTSTR lpBuffer, DWORD dwBufSize, LPCTSTR lpDefault) const +{ + if (lpBuffer != NULL) + *lpBuffer = _T('\0'); + + LPTSTR psz = __GetStringDynamic(lpSection, lpKey, lpDefault); + DWORD dwLen = _tcslen(psz); + + if (lpBuffer != NULL) + { + _tcsncpy(lpBuffer, psz, dwBufSize); + dwLen = min(dwLen, dwBufSize); + } + + delete [] psz; + return dwLen; +} + +#ifdef __AFXWIN_H__ +CString CIni::GetString(LPCTSTR lpSection, LPCTSTR lpKey, LPCTSTR lpDefault) const +{ + LPTSTR psz = __GetStringDynamic(lpSection, lpKey, lpDefault); + CString str(psz); + delete [] psz; + return str; +} +#endif + +// Write a string value to the ini file +BOOL CIni::WriteString(LPCTSTR lpSection, LPCTSTR lpKey, LPCTSTR lpValue) const +{ + if (lpSection == NULL || lpKey == NULL) + return FALSE; + + return ::WritePrivateProfileString(lpSection, lpKey, lpValue == NULL ? _T("") : lpValue, m_pszPathName); +} + +// Read a string value from the ini file, append another string after it and then write it +// back to the ini file +BOOL CIni::AppendString(LPCTSTR lpSection, LPCTSTR lpKey, LPCTSTR lpString) const +{ + if (lpString == NULL) + return FALSE; + + TCHAR* psz = __GetStringDynamic(lpSection, lpKey); + TCHAR* pNewString = new TCHAR[_tcslen(psz) + _tcslen(lpString) + 1]; + _stprintf(pNewString, _T("%s%s"), psz, lpString); + const BOOL RES = WriteString(lpSection, lpKey, pNewString); + delete [] pNewString; + delete [] psz; + return RES; +} + +///////////////////////////////////////////////////////////////////////////////// +// Ini File String Array Access +///////////////////////////////////////////////////////////////////////////////// + +// Get an array of string +DWORD CIni::GetArray(LPCTSTR lpSection, LPCTSTR lpKey, LPTSTR lpBuffer, DWORD dwBufSize, LPCTSTR lpDelimiter, BOOL bTrimString) const +{ + if (lpBuffer != NULL) + *lpBuffer = _T('\0'); + + if (lpSection == NULL || lpKey == NULL) + return 0; + + LPTSTR psz = __GetStringDynamic(lpSection, lpKey); + + DWORD dwCopied = 0; + + if (*psz != _T('\0')) + { + if (lpBuffer == NULL) + { + // just calculate the required buffer size + const DWORD MAX_LEN = _tcslen(psz) + 2; + LPTSTR p = new TCHAR[MAX_LEN + 1]; + dwCopied = __StringSplit(psz, p, MAX_LEN, lpDelimiter, bTrimString); + delete [] p; + } + else + { + dwCopied = __StringSplit(psz, lpBuffer, dwBufSize, lpDelimiter, bTrimString); + } + } + + delete [] psz; + return dwCopied; +} + +#ifdef __AFXWIN_H__ +void CIni::GetArray(LPCTSTR lpSection, LPCTSTR lpKey, CStringArray *pArray, LPCTSTR lpDelimiter, BOOL bTrimString) const +{ + if (pArray != NULL) + pArray->RemoveAll(); + + const DWORD LEN = GetArray(lpSection, lpKey, NULL, 0, lpDelimiter); + if (LEN == 0) + return; + + LPTSTR psz = new TCHAR[LEN + 3]; + GetArray(lpSection, lpKey, psz, LEN + 2, lpDelimiter); + ParseDNTString(psz, __SubStrAdd, (LPVOID)pArray); + delete [] psz; +} +#endif + +#ifdef __AFXWIN_H__ +BOOL CIni::WriteArray(LPCTSTR lpSection, LPCTSTR lpKey, const CStringArray *pArray, int nWriteCount, LPCTSTR lpDelimiter) const +{ + if (pArray == NULL) + return FALSE; + + if (nWriteCount < 0) + nWriteCount = pArray->GetSize(); + else + nWriteCount = min(nWriteCount, pArray->GetSize()); + + const CString DELIMITER = (lpDelimiter == NULL || *lpDelimiter == _T('\0')) ? _T(",") : lpDelimiter; + CString sLine; + for (int i = 0; i < nWriteCount; i++) + { + sLine += pArray->GetAt(i); + if (i != nWriteCount - 1) + sLine += DELIMITER; + } + return WriteString(lpSection, lpKey, sLine); +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +// Primitive Data Type Access +///////////////////////////////////////////////////////////////////////////////// + +// Get a signed integral value +int CIni::GetInt(LPCTSTR lpSection, LPCTSTR lpKey, int nDefault, int nBase) const +{ + TCHAR sz[DEF_PROFILE_NUM_LEN + 1] = _T(""); + GetString(lpSection, lpKey, sz, DEF_PROFILE_NUM_LEN); + return *sz == _T('\0') ? nDefault : int(_tcstoul(sz, NULL, __ValidateBase(nBase))); +} + +// Get an unsigned integral value +UINT CIni::GetUInt(LPCTSTR lpSection, LPCTSTR lpKey, UINT nDefault, int nBase) const +{ + TCHAR sz[DEF_PROFILE_NUM_LEN + 1] = _T(""); + GetString(lpSection, lpKey, sz, DEF_PROFILE_NUM_LEN); + return *sz == _T('\0') ? nDefault : UINT(_tcstoul(sz, NULL, __ValidateBase(nBase))); +} + +// Get a boolean value +BOOL CIni::GetBool(LPCTSTR lpSection, LPCTSTR lpKey, BOOL bDefault) const +{ + TCHAR sz[DEF_PROFILE_NUM_LEN + 1] = _T(""); + GetString(lpSection, lpKey, sz, DEF_PROFILE_NUM_LEN); + return StringToBool(sz, bDefault); +} + +// Get a double floating value +double CIni::GetDouble(LPCTSTR lpSection, LPCTSTR lpKey, double fDefault) const +{ + TCHAR sz[DEF_PROFILE_NUM_LEN + 1] = _T(""); + GetString(lpSection, lpKey, sz, DEF_PROFILE_NUM_LEN); + return *sz == _T('\0') ? fDefault : _tcstod(sz, NULL); +} + +// Write a signed integral value to the ini file +BOOL CIni::WriteInt(LPCTSTR lpSection, LPCTSTR lpKey, int nValue, int nBase) const +{ + TCHAR szValue[DEF_PROFILE_NUM_LEN + 1] = _T(""); + __IntToString(nValue, szValue, nBase); + return WriteString(lpSection, lpKey, szValue); +} + +// Write an unsigned value to the ini file +BOOL CIni::WriteUInt(LPCTSTR lpSection, LPCTSTR lpKey, UINT nValue, int nBase) const +{ + TCHAR szValue[DEF_PROFILE_NUM_LEN + 1] = _T(""); + __UIntToString(nValue, szValue, nBase); + return WriteString(lpSection, lpKey, szValue); +} + +// Write a double floating value to the ini file +BOOL CIni::WriteDouble(LPCTSTR lpSection, LPCTSTR lpKey, double fValue, int nPrecision) const +{ + TCHAR szFmt[16] = _T("%f"); + + if (nPrecision > 0) + _stprintf(szFmt, _T("%%.%df"), nPrecision); + + TCHAR szValue[DEF_PROFILE_NUM_LEN + 1] = _T(""); + _stprintf(szValue, szFmt, fValue); + return WriteString(lpSection, lpKey, szValue); +} + +// Read a double value from the ini file, increase it then write it back +BOOL CIni::IncreaseDouble(LPCTSTR lpSection, LPCTSTR lpKey, double fIncrease, int nPrecision) const +{ + double f = GetDouble(lpSection, lpKey, 0.0); + f += fIncrease; + return WriteDouble(lpSection, lpKey, f, nPrecision); +} + +// Write a boolean value to the ini file +BOOL CIni::WriteBool(LPCTSTR lpSection, LPCTSTR lpKey, BOOL bValue) const +{ + return WriteInt(lpSection, lpKey, bValue ? 1 : 0, BASE_DECIMAL); +} + +// Read a boolean value from the ini file, invert it(true becomes false, false becomes true), +// then write it back +BOOL CIni::InvertBool(LPCTSTR lpSection, LPCTSTR lpKey) const +{ + return WriteBool(lpSection, lpKey, !GetBool(lpSection, lpKey, FALSE)); +} + +// Read a int from the ini file, increase it and then write it back to the ini file +BOOL CIni::IncreaseInt(LPCTSTR lpSection, LPCTSTR lpKey, int nIncrease, int nBase) const +{ + int nVal = GetInt(lpSection, lpKey, 0, nBase); + nVal += nIncrease; + return WriteInt(lpSection, lpKey, nVal, nBase); +} + +// Read an UINT from the ini file, increase it and then write it back to the ini file +BOOL CIni::IncreaseUInt(LPCTSTR lpSection, LPCTSTR lpKey, UINT nIncrease, int nBase) const +{ + UINT nVal = GetUInt(lpSection, lpKey, 0, nBase); + nVal += nIncrease; + return WriteUInt(lpSection, lpKey, nVal, nBase); +} + +TCHAR CIni::GetChar(LPCTSTR lpSection, LPCTSTR lpKey, TCHAR cDefault) const +{ + TCHAR sz[2] = _T(""); + GetString(lpSection, lpKey, sz, 1); + return *sz == _T('\0') ? cDefault : sz[0]; +} + +BOOL CIni::WriteChar(LPCTSTR lpSection, LPCTSTR lpKey, TCHAR c) const +{ + TCHAR sz[2] = { c, _T('\0') }; + return WriteString(lpSection, lpKey, sz); +} + +///////////////////////////////////////////////////////////////////////////////// +// User-Defined Data Type Access +///////////////////////////////////////////////////////////////////////////////// + +// Get a block of raw data from the ini file +DWORD CIni::GetDataBlock(LPCTSTR lpSection, LPCTSTR lpKey, LPVOID lpBuffer, DWORD dwBufSize, DWORD dwOffset) const +{ + LPTSTR psz = __GetStringDynamic(lpSection, lpKey); + DWORD dwLen = _tcslen(psz) / 2; + if (dwLen <= dwOffset) + { + delete [] psz; + return 0; + } + + // verify psz, must be all in hex format + for (int i = 0; psz[i] != _T('\0'); i++) + { + TCHAR c = psz[i]; + if ((c >= _T('0') && c <= _T('9')) + || (c >= _T('a') && c <= _T('f')) + || (c >= _T('A') && c <= _T('F'))) + { + // valid + } + else + { + delete [] psz; + return 0; + } + } + + DWORD dwProcLen = 0; + LPBYTE lpb = (LPBYTE)lpBuffer; + + if (lpb != NULL) + { + dwProcLen = min(dwLen - dwOffset, dwBufSize); + LPCTSTR p = &psz[dwOffset * 2]; + for (DWORD i = 0; i < dwProcLen; i++) + { + TCHAR sz[3] = _T(""); + _tcsncpy(sz, p, 2); + lpb[i] = BYTE(_tcstoul(sz, NULL, 16)); + p = &p[2]; + } + } + else + { + dwProcLen = dwLen - dwOffset; + } + delete [] psz; + return dwProcLen; +} + +// Write a block of raw data to the ini file +BOOL CIni::WriteDataBlock(LPCTSTR lpSection, LPCTSTR lpKey, LPCVOID lpData, DWORD dwDataSize) const +{ + const BYTE* lpb = (const BYTE*)lpData; + if (lpb == NULL) + return FALSE; + + LPTSTR psz = new TCHAR[dwDataSize * 2 + 1]; + psz[0] = _T('\0'); + for (DWORD i = 0, j = 0; i < dwDataSize; i++, j += 2) + _stprintf(&psz[j], _T("%02X"), lpb[i]); + const BOOL RES = WriteString(lpSection, lpKey, psz); + delete [] psz; + return RES; +} + +// Append a block of raw data to a specified key in the ini file +BOOL CIni::AppendDataBlock(LPCTSTR lpSection, LPCTSTR lpKey, LPCVOID lpData, DWORD dwDataSize) const +{ + const BYTE* lpb = (const BYTE*)lpData; + if (lpb == NULL) + return FALSE; + + LPTSTR psz = new TCHAR[dwDataSize * 2 + 1]; + psz[0] = _T('\0'); + for (DWORD i = 0, j = 0; i < dwDataSize; i++, j += 2) + _stprintf(&psz[j], _T("%02X"), lpb[i]); + const BOOL RES = AppendString(lpSection, lpKey, psz); + delete [] psz; + return RES; +} + +// Get a POINT value +POINT CIni::GetPoint(LPCTSTR lpSection, LPCTSTR lpKey, POINT ptDefault) const +{ + POINT pt; + if (GetDataBlock(lpSection, lpKey, &pt, sizeof(POINT)) != sizeof(POINT)) + pt = ptDefault; + return pt; +} + +// Get a RECT value +RECT CIni::GetRect(LPCTSTR lpSection, LPCTSTR lpKey, RECT rcDefault) const +{ + RECT rc; + if (GetDataBlock(lpSection, lpKey, &rc, sizeof(RECT)) != sizeof(RECT)) + rc = rcDefault; + return rc; +} + +// Write a POINT to the ini file +BOOL CIni::WritePoint(LPCTSTR lpSection, LPCTSTR lpKey, POINT pt) const +{ + return WriteDataBlock(lpSection, lpKey, &pt, sizeof(POINT)); +} + +// Write a RECT to the ini file +BOOL CIni::WriteRect(LPCTSTR lpSection, LPCTSTR lpKey, RECT rc) const +{ + return WriteDataBlock(lpSection, lpKey, &rc, sizeof(RECT)); +} + +///////////////////////////////////////////////////////////////////////////////// +// Sections & Keys Access +///////////////////////////////////////////////////////////////////////////////// + +// Retrieve a list of key-lines(key-pairs) of the specified section +DWORD CIni::GetKeyLines(LPCTSTR lpSection, LPTSTR lpBuffer, DWORD dwBufSize) const +{ + if (lpBuffer != NULL) + *lpBuffer = _T('\0'); + + if (lpSection == NULL) + return 0; + + if (lpBuffer == NULL) + { + // just calculate the required buffer size + DWORD dwLen = DEF_PROFILE_THRESHOLD; + LPTSTR psz = new TCHAR[dwLen + 1]; + DWORD dwCopied = ::GetPrivateProfileSection(lpSection, psz, dwLen, m_pszPathName); + + while (dwCopied + 2 >= dwLen) + { + dwLen += DEF_PROFILE_THRESHOLD; + delete [] psz; + psz = new TCHAR[dwLen + 1]; + dwCopied = ::GetPrivateProfileSection(lpSection, psz, dwLen, m_pszPathName); + } + + delete [] psz; + return dwCopied + 2; + } + else + { + return ::GetPrivateProfileSection(lpSection, lpBuffer, dwBufSize, m_pszPathName); + } +} + +// Retrieve a list of key names of the specified section +DWORD CIni::GetKeyNames(LPCTSTR lpSection, LPTSTR lpBuffer, DWORD dwBufSize) const +{ + if (lpBuffer != NULL) + *lpBuffer = _T('\0'); + + if (lpSection == NULL) + return 0; + + STR_LIMIT sl; + sl.lpTarget = lpBuffer; + sl.dwRemain = dwBufSize; + sl.dwTotalCopied = 0; + + const DWORD LEN = GetKeyLines(lpSection, NULL, 0); + if (LEN == 0) + return 0; + + LPTSTR psz = new TCHAR[LEN + 1]; + GetKeyLines(lpSection, psz, LEN); + ParseDNTString(psz, __KeyPairProc, (LPVOID)(&sl)); + delete [] psz; + if (lpBuffer != NULL) + lpBuffer[sl.dwTotalCopied] = _T('\0'); + return sl.dwTotalCopied; +} + +// Get all section names from an ini file +DWORD CIni::GetSectionNames(LPTSTR lpBuffer, DWORD dwBufSize) const +{ + if (lpBuffer == NULL) + { + // just calculate the required buffer size + DWORD dwLen = DEF_PROFILE_THRESHOLD; + LPTSTR psz = new TCHAR[dwLen + 1]; + DWORD dwCopied = ::GetPrivateProfileSectionNames(psz, dwLen, m_pszPathName); + while (dwCopied + 2 >= dwLen) + { + dwLen += DEF_PROFILE_THRESHOLD; + delete [] psz; + psz = new TCHAR[dwLen + 1]; + dwCopied = ::GetPrivateProfileSectionNames(psz, dwLen, m_pszPathName); + } + + delete [] psz; + return dwCopied + 2; + } + else + { + return ::GetPrivateProfileSectionNames(lpBuffer, dwBufSize, m_pszPathName); + } +} + +#ifdef __AFXWIN_H__ +void CIni::GetSectionNames(CStringArray *pArray) const +{ + if (pArray != NULL) + pArray->RemoveAll(); + + const DWORD LEN = GetSectionNames(NULL, 0); + if (LEN == 0) + return; + + LPTSTR psz = new TCHAR[LEN + 1]; + GetSectionNames(psz, LEN); + ParseDNTString(psz, __SubStrAdd, pArray); + delete [] psz; +} +#endif + +#ifdef __AFXWIN_H__ +// Retrieve a list of key-lines(key-pairs) of the specified section +void CIni::GetKeyLines(LPCTSTR lpSection, CStringArray *pArray) const +{ + if (pArray != NULL) + pArray->RemoveAll(); + + const DWORD LEN = GetKeyLines(lpSection, NULL, 0); + if (LEN == 0) + return; + + LPTSTR psz = new TCHAR[LEN + 1]; + GetKeyLines(lpSection, psz, LEN); + ParseDNTString(psz, __SubStrAdd, pArray); + delete [] psz; +} +#endif + +#ifdef __AFXWIN_H__ +// Retrieve a list of key names of the specified section +void CIni::GetKeyNames(LPCTSTR lpSection, CStringArray *pArray) const +{ + if (pArray == NULL) + return; + + pArray->RemoveAll(); + const LEN = GetKeyNames(lpSection, NULL, 0); + LPTSTR psz = new TCHAR[LEN + 1]; + GetKeyNames(lpSection, psz, LEN); + ParseDNTString(psz, __SubStrAdd, (LPVOID)pArray); + delete [] psz; +} +#endif + +// Remove whole section from the ini file +BOOL CIni::DeleteSection(LPCTSTR lpSection) const +{ + return ::WritePrivateProfileString(lpSection, NULL, _T(""), m_pszPathName); +} + +// Remove a key from a section +BOOL CIni::DeleteKey(LPCTSTR lpSection, LPCTSTR lpKey) const +{ + return ::WritePrivateProfileString(lpSection, lpKey, NULL, m_pszPathName); +} + +BOOL CIni::IsSectionExist(LPCTSTR lpSection) const +{ + if (lpSection == NULL) + return FALSE; + + // first get the section name list, then check if lpSection exists + // in the list. + const DWORD LEN = GetSectionNames(NULL, 0); + if (LEN == 0) + return FALSE; + + LPTSTR psz = new TCHAR[LEN + 1]; + GetSectionNames(psz, LEN); + BOOL RES = !ParseDNTString(psz, __SubStrCompare, (LPVOID)lpSection); + delete [] psz; + return RES; +} + +BOOL CIni::IsKeyExist(LPCTSTR lpSection, LPCTSTR lpKey) const +{ + if (lpSection == NULL || lpKey == NULL) + return FALSE; + + // Test it with the default unique string + LPTSTR psz = __GetStringDynamic(lpSection, lpKey, DEF_PROFILE_TESTSTRING); + const BOOL RES = (_tcscmp(psz, DEF_PROFILE_TESTSTRING) != 0); + delete [] psz; + return RES; +} + +BOOL CIni::CopySection(LPCTSTR lpSrcSection, LPCTSTR lpDestSection, BOOL bFailIfExist) const +{ + if (lpSrcSection == NULL || lpDestSection == NULL) + return FALSE; + + if (_tcsicmp(lpSrcSection, lpDestSection) == 0) + return FALSE; + + if (!IsSectionExist(lpSrcSection)) + return FALSE; + + if (bFailIfExist && IsSectionExist(lpDestSection)) + return FALSE; + + DeleteSection(lpDestSection); + + const DWORD SRC_LEN = GetKeyLines(lpSrcSection, NULL, 0); + LPTSTR psz = new TCHAR[SRC_LEN + 2]; + //memset(psz, 0, sizeof(TCHAR) * (SRC_LEN + 2)); + GetKeyLines(lpSrcSection, psz, SRC_LEN); + const BOOL RES = ::WritePrivateProfileSection(lpDestSection, psz, m_pszPathName); + delete [] psz; + + return RES; +} + +BOOL CIni::CopyKey(LPCTSTR lpSrcSection, LPCTSTR lpSrcKey, LPCTSTR lpDestSection, LPCTSTR lpDestKey, BOOL bFailIfExist) const +{ + if (lpSrcSection == NULL || lpSrcKey == NULL || lpDestKey == NULL) + return FALSE; + + if (_tcsicmp(lpSrcSection, lpDestSection) == 0 + && _tcsicmp(lpSrcKey, lpDestKey) == 0) + return FALSE; + + if (!IsKeyExist(lpSrcSection, lpSrcKey)) + return FALSE; + + if (bFailIfExist && IsKeyExist(lpDestSection, lpDestKey)) + return FALSE; + + LPTSTR psz = __GetStringDynamic(lpSrcSection, lpSrcKey); + const BOOL RES = WriteString(lpDestSection, lpDestKey, psz); + delete [] psz; + return RES; +} + +BOOL CIni::MoveSection(LPCTSTR lpSrcSection, LPCTSTR lpDestSection, BOOL bFailIfExist) const +{ + return CopySection(lpSrcSection, lpDestSection, bFailIfExist) + && DeleteSection(lpSrcSection); +} + +BOOL CIni::MoveKey(LPCTSTR lpSrcSection, LPCTSTR lpSrcKey, LPCTSTR lpDestSection, LPCTSTR lpDestKey, BOOL bFailIfExist) const +{ + return CopyKey(lpSrcSection, lpSrcKey, lpDestSection, lpDestKey, bFailIfExist) + && DeleteKey(lpSrcSection, lpSrcKey); +} + +///////////////////////////////////////////////////////////////////////////////// +// Helper Functions +///////////////////////////////////////////////////////////////////////////////// + +// Get a profile string value, return a heap pointer so we do not have to worry +// about the buffer size, however, this function requires the caller to manually +// free the memory. +// This function is the back-bone of all "Getxxx" functions of this class. +LPTSTR CIni::__GetStringDynamic(LPCTSTR lpSection, LPCTSTR lpKey, LPCTSTR lpDefault) const +{ + TCHAR* psz = NULL; + if (lpSection == NULL || lpKey == NULL) + { + // Invalid section or key name, just return the default string + if (lpDefault == NULL) + { + // Empty string + psz = new TCHAR[1]; + *psz = _T('\0'); + } + else + { + psz = new TCHAR[_tcslen(lpDefault) + 1]; + _tcscpy(psz, lpDefault); + } + + return psz; + } + + // Keep enlarging the buffer size until being certain on that the string we + // retrieved was original(not truncated). + DWORD dwLen = DEF_PROFILE_THRESHOLD; + psz = new TCHAR[dwLen + 1]; + DWORD dwCopied = ::GetPrivateProfileString(lpSection, lpKey, lpDefault == NULL ? _T("") : lpDefault, psz, dwLen, m_pszPathName); + while (dwCopied + 1 >= dwLen) + { + dwLen += DEF_PROFILE_THRESHOLD; + delete [] psz; + psz = new TCHAR[dwLen + 1]; + dwCopied = ::GetPrivateProfileString(lpSection, lpKey, lpDefault == NULL ? _T("") : lpDefault, psz, dwLen, m_pszPathName); + } + + return psz; // !!! Requires the caller to free this memory !!! +} + +// Split a string usinf a particular delimiter, split result are copied into lpBuffer +// in the "double null terminated string" format as the following figure shows: +// xxx\0xxxx\0xx\0xxx\0\0 +// +// For example, if the delimiter is ",", then string "ab,cd,e" will be +// splitted into "ab\0cd\0e\0\0", this string format can be parsed into an array +// of sub strings easily using user defined functions or CIni::ParseStringArray. +DWORD CIni::__StringSplit(LPCTSTR lpString, LPTSTR lpBuffer, DWORD dwBufSize, LPCTSTR lpDelimiter, BOOL bTrimString) +{ + if (lpString == NULL || lpBuffer == NULL || dwBufSize == 0) + return 0; + + DWORD dwCopied = 0; + *lpBuffer = _T('\0'); + if (*lpString == _T('\0')) + return 0; + + // If lpDelimiter is NULL, use the default delimiter ",", if delimiter length + // is 0, then return whole string + if (lpDelimiter != NULL && *lpDelimiter == _T('\0')) + { + _tcsncpy(lpBuffer, lpString, dwBufSize - 1); + return _tcslen(lpBuffer); + } + + LPTSTR pszDel = (lpDelimiter == NULL) ? _tcsdup(DEF_PROFILE_DELIMITER) : _tcsdup(lpDelimiter); + const DWORD DEL_LEN = _tcslen(pszDel); + LPTSTR lpTarget = lpBuffer; + + // Search through lpString for delimiter matches, and extract sub strings out + LPCTSTR lpPos = lpString; + LPCTSTR lpEnd = _tcsstr(lpPos, pszDel); + + while (lpEnd != NULL) + { + LPTSTR pszSeg = __StrDupEx(lpPos, lpEnd); + if (bTrimString) + __TrimString(pszSeg); + + const DWORD SEG_LEN = _tcslen(pszSeg); + const DWORD COPY_LEN = min(SEG_LEN, dwBufSize - dwCopied); + + // Need to avoid buffer overflow + if (COPY_LEN > 0) + { + dwCopied += COPY_LEN + 1; + _tcsncpy(lpTarget, pszSeg, COPY_LEN); + lpTarget[COPY_LEN] = _T('\0'); + lpTarget = &lpTarget[SEG_LEN + 1]; + } + delete [] pszSeg; + lpPos = &lpEnd[DEL_LEN]; // Advance the pointer for next search + lpEnd = _tcsstr(lpPos, pszDel); + } + + // The last part of string, there may not be the trailing delimiter, so we + // need to take care of this part, too + LPTSTR pszSeg = _tcsdup(lpPos); + if (bTrimString) + __TrimString(pszSeg); + + const DWORD SEG_LEN = _tcslen(pszSeg); + const DWORD COPY_LEN = min(SEG_LEN, dwBufSize - dwCopied); + + if (COPY_LEN > 0) + { + dwCopied += COPY_LEN + 1; + _tcsncpy(lpTarget, pszSeg, COPY_LEN); + lpTarget[COPY_LEN] = _T('\0'); + } + + delete [] pszSeg; + lpBuffer[dwCopied] = _T('\0'); + delete [] pszDel; + return dwCopied; +} + +// Parse a "double null terminated string", pass each sub string to a user-defined +// callback function +BOOL CIni::ParseDNTString(LPCTSTR lpString, SUBSTRPROC lpFnStrProc, LPVOID lpParam) +{ + if (lpString == NULL || lpFnStrProc == NULL) + return FALSE; + + LPCTSTR p = lpString; + DWORD dwLen = _tcslen(p); + + while (dwLen > 0) + { + if (!lpFnStrProc(p, lpParam)) + return FALSE; + + p = &p[dwLen + 1]; + dwLen = _tcslen(p); + } + return TRUE; +} + +// Callback function used to compare elements inside of a +// "double null terminated string" with a given string. Useful for +// searching in the section names list. +BOOL CALLBACK CIni::__SubStrCompare(LPCTSTR lpString1, LPVOID lpParam) +{ + assert(lpString1 != NULL); + LPCTSTR lpString2 = (LPCTSTR)lpParam; + assert(lpString2 != NULL); + // if two string matches, return zero to stop the parsing + return _tcsicmp(lpString1, lpString2) != 0; +} + +// Callback function used to process a key-pair, it extracts the +// key name from the key-pair string +BOOL CALLBACK CIni:: __KeyPairProc(LPCTSTR lpString, LPVOID lpParam) +{ + STR_LIMIT* psl = (STR_LIMIT*)lpParam; + if (lpString == NULL || psl== NULL) + return FALSE; + + LPCTSTR p = _tcschr(lpString, _T('=')); + if (p == NULL || p == lpString) + return TRUE; + + // extract the sub-string on left side of the '=' + LPTSTR psz = new TCHAR[_tcslen(lpString) + 1]; + int i = 0; + for (i = 0; &lpString[i] < p; i++) + psz[i] = lpString[i]; + psz[i] = _T('\0'); + + // trim + __TrimString(psz); + DWORD dwNameLen = _tcslen(psz); + DWORD dwCopyLen = 0; + + //copy to the buffer + if (psl->lpTarget != NULL) + { + dwCopyLen = (psl->dwRemain > 1) ? min(dwNameLen, psl->dwRemain - 1) : 0; + _tcsncpy(psl->lpTarget, psz, dwCopyLen); + psl->lpTarget[dwCopyLen] = _T('\0'); + psl->lpTarget = &(psl->lpTarget[dwCopyLen + 1]); + psl->dwRemain -= dwCopyLen + 1; + } + else + { + dwCopyLen = dwNameLen; + } + + delete [] psz; + psl->dwTotalCopied += dwCopyLen + 1; + return TRUE; +} + +#ifdef __AFXWIN_H__ +// Callback function used to add elements that are extracted from a +// "double null terminated string" to an MFC CStringArray. +BOOL CALLBACK CIni::__SubStrAdd(LPCTSTR lpString, LPVOID lpParam) +{ + CStringArray* pArray = (CStringArray*)lpParam; + if (pArray == NULL || lpString == NULL) + return FALSE; + + pArray->Add(lpString); + return TRUE; +} +#endif + +// Convert an integer into binary string format +void CIni::__ToBinaryString(UINT nNumber, LPTSTR lpBuffer, DWORD dwBufSize) +{ + if (dwBufSize == 0) + return; + + DWORD dwIndex = 0; + do + { + lpBuffer[dwIndex++] = (nNumber % 2) ? _T('1') : _T('0'); + nNumber /= 2; + } while (nNumber > 0 && dwIndex < dwBufSize); + + lpBuffer[dwIndex] = _T('\0'); + _tcsrev(lpBuffer); +} + +// Make sure the base will be expected value +int CIni::__ValidateBase(int nBase) +{ + switch (nBase) + { + case BASE_BINARY: + case BASE_OCTAL: + case BASE_HEXADECIMAL: + break; + + default: + nBase = BASE_DECIMAL; + } + + return nBase; +} + +// Convert a signed integer into string representation, based on its base +void CIni::__IntToString(int nNumber, LPTSTR lpBuffer, int nBase) +{ + switch (nBase) + { + case BASE_BINARY: + case BASE_OCTAL: + case BASE_HEXADECIMAL: + __UIntToString((UINT)nNumber, lpBuffer, nBase); + break; + + default: + _stprintf(lpBuffer, _T("%d"), nNumber); + break; + } +} + +// Convert an unsigned integer into string representation, based on its base +void CIni::__UIntToString(UINT nNumber, LPTSTR lpBuffer, int nBase) +{ + switch (nBase) + { + case BASE_BINARY: + __ToBinaryString(nNumber, lpBuffer, DEF_PROFILE_NUM_LEN); + break; + + case BASE_OCTAL: + _stprintf(lpBuffer, _T("%o"), nNumber); + break; + + case BASE_HEXADECIMAL: + _stprintf(lpBuffer, _T("%X"), nNumber); + break; + + default: + _stprintf(lpBuffer, _T("%u"), nNumber); + break; + } +} + +BOOL CIni::StringToBool(LPCTSTR lpString, BOOL bDefault) +{ + // Default: empty string + // TRUE: "true", "yes", non-zero decimal numner + // FALSE: all other cases + if (lpString == NULL || *lpString == _T('\0')) + return bDefault; + + return (_tcsicmp(lpString, _T("true")) == 0 + || _tcsicmp(lpString, _T("yes")) == 0 + || _tcstol(lpString, NULL, BASE_DECIMAL) != 0); +} + +BOOL CIni::__TrimString(LPTSTR lpString) +{ + if (lpString == NULL) + return FALSE; + + BOOL bTrimmed = FALSE; + int nLen = _tcslen(lpString); + + // '\n' and '\r' are actually not possible in this case, but anyway... + + // Trim right side + while (nLen > 0 + && (lpString[nLen - 1] == _T(' ') + || lpString[nLen - 1] == _T('\t') + || lpString[nLen - 1] == _T('\r') + || lpString[nLen - 1] == _T('\n'))) + { + lpString[--nLen] = _T('\0'); + bTrimmed = TRUE; + } + + // Trim left side + LPCTSTR p = lpString; + while (*p == _T(' ') + || *p == _T('\t') + || *p == _T('\r') + || *p == _T('\n')) + { + p = &p[1]; + bTrimmed = TRUE; + } + + if (p != lpString) + { + LPTSTR psz = _tcsdup(p); + _tcscpy(lpString, psz); + delete [] psz; + } + + return bTrimmed; +} + +LPTSTR CIni::__StrDupEx(LPCTSTR lpStart, LPCTSTR lpEnd) +{ + const DWORD LEN = ((DWORD)lpEnd - (DWORD)lpStart) / sizeof(TCHAR); + LPTSTR psz = new TCHAR[LEN + 1]; + _tcsncpy(psz, lpStart, LEN); + psz[LEN] = _T('\0'); + return psz; // !!! Requires the caller to free this memory !!! +} + +///////////////////////////////////////////////////////////////////////////////// +// End of Cini Class Implementation +///////////////////////////////////////////////////////////////////////////////// + +// If you are getting this error: +// ---------------------------------------------------------------------------- +// "fatal error C1010: unexpected end of file while looking for precompiled +// header directive" +//----------------------------------------------------------------------------- +// Please scroll all the way up and uncomment '#include "stdafx.h"' \ No newline at end of file diff --git a/src/common/string_utils.cpp b/src/common/string_utils.cpp new file mode 100644 index 0000000..a89ddf8 --- /dev/null +++ b/src/common/string_utils.cpp @@ -0,0 +1,14 @@ +#include + +#include + +std::wstring string_utils::string_to_wstring(const std::string& str) { + if (str.empty()) + return std::wstring(); + + const auto sizeNeeded = MultiByteToWideChar(CP_ACP, 0, &str[0], static_cast(str.size()), nullptr, 0); + std::wstring wstrTo(sizeNeeded, 0); + MultiByteToWideChar(CP_ACP, 0, &str[0], static_cast(str.size()), &wstrTo[0], sizeNeeded); + + return wstrTo; +} diff --git a/src/d2tweaks/client/client.cpp b/src/d2tweaks/client/client.cpp new file mode 100644 index 0000000..1d9dab3 --- /dev/null +++ b/src/d2tweaks/client/client.cpp @@ -0,0 +1,441 @@ +#include + +#include + +#include + +#include +#include +#include + + +#include +#include +#include +//debug junk +//#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION + +std::vector globalStatsVector; +diablo2::structures::gfxdata g_gfxdata; // global gfxdata + +int randStat; +int randStatRangeLow; +int randStatRangeHigh; +int randStatBool; + +std::wstring ConvertCharToWString(const std::string& charString) { + std::wstring_convert> converter; + return converter.from_bytes(charString); +} + +// Function to convert std::wstring to std::string +std::string WStringToString(const std::wstring& wstr) { + // Create a codecvt facet for UTF-8 conversion + std::wstring_convert, wchar_t> converter; + return converter.to_bytes(wstr); +} + +// Function to convert std::string to std::wstring +std::wstring StringToWString(const std::string& str) { + // Create a codecvt facet for UTF-8 conversion + std::wstring_convert, wchar_t> converter; + return converter.from_bytes(str); +} + +// Define the mapColorToEnum function +diablo2::ui_color_t mapColorToEnum(const std::string& colorName) { + static const std::unordered_map colorMap = { + {"RED", diablo2::ui_color_t::UI_COLOR_RED}, + {"LIGHT_GREEN", diablo2::ui_color_t::UI_COLOR_LIGHT_GREEN}, + {"BLUE", diablo2::ui_color_t::UI_COLOR_BLUE}, + {"DARK_GOLD", diablo2::ui_color_t::UI_COLOR_DARK_GOLD}, + {"GREY", diablo2::ui_color_t::UI_COLOR_GREY}, + {"BLACK", diablo2::ui_color_t::UI_COLOR_BLACK}, + {"GOLD", diablo2::ui_color_t::UI_COLOR_GOLD}, + {"ORANGE", diablo2::ui_color_t::UI_COLOR_ORANGE}, + {"YELLOW", diablo2::ui_color_t::UI_COLOR_YELLOW}, + {"DARK_GREEN", diablo2::ui_color_t::UI_COLOR_DARK_GREEN}, + {"PURPLE", diablo2::ui_color_t::UI_COLOR_PURPLE}, + {"GREEN", diablo2::ui_color_t::UI_COLOR_GREEN}, + {"WHITE", diablo2::ui_color_t::UI_COLOR_WHITE}, + {"BLACK2", diablo2::ui_color_t::UI_COLOR_BLACK2}, + {"DARK_WHITE", diablo2::ui_color_t::UI_COLOR_DARK_WHITE}, + {"LIGHT_GREY", diablo2::ui_color_t::UI_COLOR_LIGHT_GREY} + }; + + auto it = colorMap.find(colorName); + if (it != colorMap.end()) { + return it->second; + } + // Default color if not found + return diablo2::ui_color_t::UI_COLOR_WHITE; +} + + + +// Define a struct to hold key-value pairs within a section +struct Section { + std::map assignments; +}; + +// Function to parse an INI file and extract sections starting with "Stat" +void ParseIniFile(const std::string& iniFilePath) { + std::ifstream inFile(iniFilePath); + if (!inFile) { + std::cerr << "Error opening file: " << iniFilePath << std::endl; + return; + } + + std::string line; + std::string currentSection; + StatEntry entry; + + while (std::getline(inFile, line)) { + // Trim leading and trailing whitespace from the line + line.erase(line.find_last_not_of(" \t\r\n") + 1); + + // Check if the line contains a section header + if (line.size() > 2 && line.front() == '[' && line.back() == ']') { + currentSection = line.substr(1, line.size() - 2); // Extract section name + if (currentSection.find("Stat") == 0) { + entry = {}; // Reset entry for a new section + } + } + else if (!currentSection.empty() && line.find('=') != std::string::npos) { + // Parse key-value pairs within the section + std::istringstream iss(line); + std::string key, value; + if (std::getline(iss, key, '=') && std::getline(iss, value)) { + if (currentSection.find("Stat") == 0) { + // Process key-value pairs for "Stat" sections + if (key == "stat") { + entry.stat = std::stoi(value); + } + else if (key == "stat_display_string") { + entry.stat_display_string = ConvertCharToWString(value); // a conversion function + } + else if (key == "colorStat") { + entry.colorStat = mapColorToEnum(value); // a mapping function + } + else if (key == "colorStatValue") { + entry.colorStatValue = mapColorToEnum(value); // a mapping function + } + else if (key == "x1") { + entry.x1 = std::stoi(value); + } + else if (key == "y1") { + entry.y1 = std::stoi(value); + } + else if (key == "x2") { + entry.x2 = std::stoi(value); + } + else if (key == "y2") { + entry.y2 = std::stoi(value); + } + else if (key == "is_item_stat") { + entry.is_item_stat = std::stoi(value); + } + else if (key == "item_type_id") { + entry.item_type_id = std::stoi(value); + } + } + } + } + else if (!currentSection.empty() && currentSection.find("Stat") == 0) { + // End of section detected, add entry to global vector + globalStatsVector.push_back(entry); + } + } + + inFile.close(); +} + +#include + +static void(__fastcall* g_handle_packet)(d2_tweaks::common::packet_header* packet, size_t size); +static void(__fastcall* g_handle_packet_standart)(d2_tweaks::common::packet_header* packet, size_t size); +static void(__fastcall* g_handle_cs_packet)(d2_tweaks::common::packet_header* packet, size_t size); +static int32_t(__stdcall* g_draw_game_ui_original)(); +static int32_t(__fastcall* g_game_tick_original)(int32_t a1); +//void(__fastcall* g_game_loop_start)(); + +//static uint32_t g_ret; + +d2_tweaks::client::client::client(token) { + m_module_id_counter = 0; + m_tick_handler_id_counter = 0; +} + +__declspec(naked) void __stdcall game_tick_wrapper() +{ + // usercall, ecx = param1, esi = param2 + __asm + { + pushad + pushfd + call[d2_tweaks::client::client::game_tick] + popfd + popad + + push[g_game_tick_original] + ret + } +} + +__declspec (naked) void handle_cs_packet_wrapper() { + __asm { + pushad; + pushfd; + call [d2_tweaks::client::client::handle_cs_packet] + popfd; + popad; + // + sub esp, 0x200; + } + RET_TO_RVA(DLLBASE_D2CLIENT, 0xD856); +} + + +__declspec (naked) void handle_sc_standart_packet_wrapper() { + __asm { + pushad; + pushfd; + call[d2_tweaks::client::client::handle_standart_packet] + popfd; + popad; + // + sub esp, 0x54; + push ebx; + push ebp; + } + RET_TO_RVA(DLLBASE_D2CLIENT, 0x150B5); +} + + +static const DLLPatchStrc gpt_handle_cs_packet[] = +{ + {D2DLL_D2CLIENT, 0xD850 + 0, PATCH_JMP, FALSE, 0x1}, + {D2DLL_D2CLIENT, 0xD850 + 1, (DWORD)handle_cs_packet_wrapper, TRUE, 0x0}, + {D2DLL_D2CLIENT, 0xD850 + 5, (DWORD)PATCH_NOPBLOCK, FALSE, 0x1}, + {D2DLL_INVALID} +}; + + +static const DLLPatchStrc gpt_handle_sc_standart_packet[] = +{ + {D2DLL_D2CLIENT, 0x150B0 + 0, PATCH_JMP, FALSE, 0x1}, + {D2DLL_D2CLIENT, 0x150B0 + 1, (DWORD)handle_sc_standart_packet_wrapper, TRUE, 0x0}, + {D2DLL_INVALID} +}; + + +void d2_tweaks::client::client::init() { + // handle packet GamePacketReceivedIntercept + hooking::hook(diablo2::d2_client::get_base() + 0x11CB0, handle_packet, reinterpret_cast(&g_handle_packet)); + hooking::hook(diablo2::d2_client::get_base() + 0x9640, game_tick_wrapper, reinterpret_cast(&g_game_tick_original)); + hooking::hook(diablo2::d2_client::get_base() + 0x5E650, draw_game_ui, reinterpret_cast(&g_draw_game_ui_original)); + //hooking::hook(diablo2::d2_client::get_base() + 0x150B0, handle_standart_packet, reinterpret_cast(&g_handle_packet_standart)); + + // Get the path to the INI file + std::string iniFilePath = std::filesystem::current_path().generic_string() + "/d2tweaks.ini"; + LPCSTR lpIniFilePath = iniFilePath.c_str(); + + for (auto& m_module : m_modules) { + if (m_module == nullptr) + break; + + randStat = GetPrivateProfileIntA("RandStat", "stat", 0, lpIniFilePath); + randStatRangeLow = GetPrivateProfileIntA("RandStat", "statRangeLow", 0, lpIniFilePath); + randStatRangeHigh = GetPrivateProfileIntA("RandStat", "statRangeHigh", 0, lpIniFilePath); + randStatBool = GetPrivateProfileIntA("RandStat", "statBool", 0, lpIniFilePath); + + spdlog::info("randStat = {0}", randStat); + spdlog::info("randStatRangeLow = {0}", randStatRangeLow); + spdlog::info("randStatRangeHigh = {0}", randStatRangeHigh); + + + + // Load and parse the INI file + ParseIniFile(iniFilePath); + } + + + + D2TEMPLATE_ApplyPatch(gpt_handle_cs_packet); + //D2TEMPLATE_ApplyPatch(gpt_handle_sc_standart_packet); + + for (auto& m_module : m_modules) { + if (m_module == nullptr) + break; + + m_module->init_early(); + } +} + + +static int32_t g_ebp_send_to_client; +void d2_tweaks::client::client::handle_cs_packet(common::packet_header* packet, size_t size) { +#ifndef NDEBUG + __asm { + push [ebp + 0x2C]; + pop [g_ebp_send_to_client]; + } + spdlog::warn("[d2client SEND] Packet {} Message {} Size {} CallFrom {}", (void*)packet->d2_packet_type, (void*)packet->message_type, size, (void*)g_ebp_send_to_client); +#endif + + static common::packet_header dummy; + static auto& instance = singleton::instance(); + + if (size == -1) + return; + + auto handler = instance.m_packet_cs_handlers[packet->d2_packet_type]; + + if (!handler) + return; + + handler->handle_cs_packet(packet); +} + + +void d2_tweaks::client::client::handle_standart_packet(common::packet_header* packet, size_t size) { + if (size == -1) + return; + +#ifndef NDEBUG + spdlog::info("[d2client RECV] Packet {} Message {} Size {}", (void*)packet->d2_packet_type, (void*)packet->message_type, size); +#endif + + return; +} + + +void d2_tweaks::client::client::handle_packet(common::packet_header* packet, size_t size) { + static common::packet_header dummy; + static auto& instance = singleton::instance(); + + if (size == -1) + return; + +#ifndef NDEBUG + spdlog::info("[d2client RECV] Packet {} Message {} Size {}", (void*)packet->d2_packet_type, (void*)packet->message_type, size); +#endif + + if (packet->d2_packet_type != dummy.d2_packet_type) { + g_handle_packet(packet, size); + return; + } + + auto handler = instance.m_packet_handlers[packet->message_type]; + + if (!handler) + return; + + handler->handle_packet(packet); +} + + +static bool g_is_init = false; +void d2_tweaks::client::client::game_tick() { + + static auto& instance = singleton::instance(); /// d2 gl + + if (g_is_init == false) { + D2TEMPLATE_Init(); + + for (auto& m_module : instance.m_modules) { + if (m_module == nullptr) + break; + + m_module->init(); + } + g_is_init = true; + } + + for (auto & tick_handler : instance.m_tick_handlers) { + if (tick_handler == nullptr) + break; + + tick_handler->tick(); + } + + return; +} + + +int32_t d2_tweaks::client::client::draw_game_ui() { + static auto& ui = singleton::instance(); + + const auto result = g_draw_game_ui_original(); + + ui.draw(); + + return result; +} + + + +void d2_tweaks::client::client::register_module(modules::client_module* module) { + m_modules[m_module_id_counter++] = module; +} + +void d2_tweaks::client::client::register_tick_handler(modules::client_module* module) { + m_tick_handlers[m_tick_handler_id_counter++] = module; +} + +void d2_tweaks::client::client::register_packet_cs_handler(common::packet_types_cs_t packet, common::message_types_t type, modules::client_module* module) { + if (m_packet_cs_handlers[packet] != nullptr) { + spdlog::warn("Clientside packet cs handler for {0} is already registered!", type); + } + + m_packet_cs_handlers[packet] = module; +} + +void d2_tweaks::client::client::register_packet_handler(common::message_types_t type, modules::client_module* module) { + if (m_packet_handlers[type] != nullptr) { + spdlog::warn("Clientside packet handler for {0} is already registered!", type); + } + + m_packet_handlers[type] = module; +} + +diablo2::structures::unit* d2_tweaks::client::client::get_client_unit(uint32_t type, uint32_t guid) { + static auto units = diablo2::d2_client::get_client_unit_list(); + + const auto index = guid & 127; + + auto result = units->unit_list[type][index]; + + while (result != nullptr && result->guid != guid) { + result = result->prev_unit; + } + + return result; +} diff --git a/src/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.cpp b/src/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.cpp new file mode 100644 index 0000000..1a44439 --- /dev/null +++ b/src/d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.cpp @@ -0,0 +1,141 @@ +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_INIT(auto_gold_pickup) + +static uint32_t m_nDisplayTime = 2500; +static uint32_t m_nLastUpdate = 0; +static uint32_t m_nGoldValue = 0; +static int32_t m_iDistance = 4; + +class draw_gold_menu final : public d2_tweaks::ui::menu { +public: + d2_tweaks::ui::controls::label* m_label; + + draw_gold_menu() { + //load_xml("d2tweaks\\interfaces\\autogoldpickup.xml"); + if (DLLBASE_D2EXPRES != 0) + load_xml("d2tweaks\\interface_d2expres\\autogoldpickup.xml"); + if (DLLBASE_SGD2FREERES != 0) + load_xml("d2tweaks\\interface_sgd2freeres\\autogoldpickup.xml"); + if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0) + load_xml("d2tweaks\\interface_vanilla\\autogoldpickup.xml"); + + m_label = get_control("m_gold_label"); + + menu::set_visible(true); + menu::set_enabled(true); + } + + void draw() override { + static wchar_t buffer[1024]; + + if (m_nGoldValue == 0) { + return; + } + + if (GetTickCount() - m_nLastUpdate >= m_nDisplayTime) { + m_nGoldValue = 0; + return; + } + + swprintf_s(buffer, L"+%i", m_nGoldValue); + + m_label->set_text(buffer); + m_label->draw(); + } +}; + + +void d2_tweaks::client::modules::auto_gold_pickup::init_early() { + +} + + +void d2_tweaks::client::modules::auto_gold_pickup::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "AutoGoldPickup", 1, acPathToIni) != FALSE) { + m_iDistance = GetPrivateProfileInt("AutoGoldPickup", "PickupDistance", 4, acPathToIni); + m_nDisplayTime = GetPrivateProfileInt("AutoGoldPickup", "DisplayTime", 2500, acPathToIni); + singleton::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_GOLD_PICKUP_INFO, this); + singleton::instance().register_tick_handler(this); + singleton::instance().add_menu(new draw_gold_menu()); + } +} + + +void d2_tweaks::client::modules::auto_gold_pickup::tick() { + const auto unit = diablo2::d2_client::get_local_player(); + + if (!unit) + return; + + if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + return; + + const auto room = diablo2::d2_common::get_room_from_unit(unit); + + if (!room) + return; + + for (auto item = room->unit; item; item = item->prev_unit_in_room) { + if (!item) + continue; + + if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + continue; + + auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + if (!record) + continue; + + const auto distance = diablo2::d2_common::get_distance_between_units(unit, item); + if (distance > m_iDistance) + continue; + + if (record->string_code[0] == 'g' && + record->string_code[1] == 'l' && + record->string_code[2] == 'd') + { + static d2_tweaks::common::gold_pickup_info_cs request_packet_cs; + request_packet_cs.item_guid = item->guid; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + } + + continue; + } +} + + +void d2_tweaks::client::modules::auto_gold_pickup::handle_packet(common::packet_header* packet) { + const auto info = static_cast(packet); + m_nLastUpdate = GetTickCount(); + m_nGoldValue += info->gold; +} diff --git a/src/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.cpp b/src/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.cpp new file mode 100644 index 0000000..a066a5d --- /dev/null +++ b/src/d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.cpp @@ -0,0 +1,574 @@ +#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 +#include +#include + +#include +#include +#include + +MODULE_INIT(auto_item_pickup) + +static int32_t m_iDistance = 0; +static uint32_t m_bAutoItemPickupEnabled = 0; +static uint32_t m_nTick = 0; + +static bool m_bToggleAutoItemPickup = false; +static bool m_bInventoryFull = false; + +static item_code* m_stItemList; +static item_type* m_stItemTypes; + +static uint32_t m_nCountItemListAll = 0; +static uint32_t m_nCountItemTypesAll = 0; + +static const uint32_t m_nCountItemListKeys = 30; +static const uint32_t m_nCountItemTypesKeys = 10; + +static char* m_apcItemList[m_nCountItemListKeys] = { 0 }; +static char* m_apcItemTypes[m_nCountItemTypesKeys] = { 0 }; + +static char* m_pcItemListAll; +static char* m_pcItemListAllTemp; + +static char* m_pcItemTypesAll; +static char* m_pcItemTypesAllTemp; + +static char m_acBuffer[1024] = { 0 }; +static char m_acPathToIni[MAX_PATH] = { 0 }; +static const char* m_pcIniFile = "\\d2tweaks.ini"; + +void d2_tweaks::client::modules::auto_item_pickup::init_early() { + +} + +void ReloadFilters(char* szPathToIni) { + uint32_t dwLenght = 0; + + m_nCountItemListAll = 0; + m_nCountItemTypesAll = 0; + + free(m_stItemList); + free(m_stItemTypes); + + free(m_pcItemListAllTemp); + free(m_pcItemListAll); + free(m_pcItemTypesAll); + free(m_pcItemTypesAllTemp); + + m_pcItemListAll = (char*)malloc(MAX_STRING_LENGHT * m_nCountItemListKeys); + m_pcItemListAllTemp = (char*)malloc(MAX_STRING_LENGHT * m_nCountItemListKeys); + m_pcItemTypesAll = (char*)malloc(MAX_STRING_LENGHT * m_nCountItemTypesKeys); + m_pcItemTypesAllTemp = (char*)malloc(MAX_STRING_LENGHT * m_nCountItemTypesKeys); + + memset(m_pcItemListAll, 0, MAX_STRING_LENGHT * m_nCountItemListKeys); + memset(m_pcItemListAllTemp, 0, MAX_STRING_LENGHT * m_nCountItemListKeys); + memset(m_pcItemTypesAll, 0, MAX_STRING_LENGHT * m_nCountItemTypesKeys); + memset(m_pcItemTypesAllTemp, 0, MAX_STRING_LENGHT * m_nCountItemTypesKeys); + + for (uint32_t i = 0; i < m_nCountItemListKeys; i++) { + free(m_apcItemList[i]); + m_apcItemList[i] = (char*)malloc(MAX_STRING_LENGHT); + memset(m_apcItemList[i], 0, MAX_STRING_LENGHT); + + sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemList%d", i + 1); + dwLenght = GetPrivateProfileString("AutoItemPickup", m_acBuffer, 0, m_apcItemList[i], MAX_STRING_LENGHT - 1, szPathToIni); + if (dwLenght != 0) { + lstrcat(m_pcItemListAll, m_apcItemList[i]); + lstrcat(m_pcItemListAll, "|"); + } + } + + for (uint32_t i = 0; i < m_nCountItemTypesKeys; i++) { + free(m_apcItemTypes[i]); + m_apcItemTypes[i] = (char*)malloc(MAX_STRING_LENGHT); + memset(m_apcItemTypes[i], 0, MAX_STRING_LENGHT); + + sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemTypeList%d", i + 1); + dwLenght = GetPrivateProfileString("AutoItemPickup", m_acBuffer, 0, m_apcItemTypes[i], MAX_STRING_LENGHT - 1, szPathToIni); + if (dwLenght != 0) { + lstrcat(m_pcItemTypesAll, m_apcItemTypes[i]); + lstrcat(m_pcItemTypesAll, "|"); + } + } + + + /// Parse ItemCode + dwLenght = lstrlen(m_pcItemListAll); + memcpy(m_pcItemListAllTemp, m_pcItemListAll, dwLenght + 1); + // + char* token_string_itemcode = strtok(m_pcItemListAllTemp, " ,|"); + while (token_string_itemcode) + { + m_nCountItemListAll++; + token_string_itemcode = strtok(NULL, " ,|"); + } + + // , + m_stItemList = (item_code*)malloc(m_nCountItemListAll * sizeof(item_code)); + memset(m_stItemList, 0, m_nCountItemListAll * sizeof(item_code)); + + // + memcpy(m_pcItemListAllTemp, m_pcItemListAll, dwLenght + 1); + token_string_itemcode = strtok(m_pcItemListAllTemp, " ,|"); + + for (uint32_t i = 0; token_string_itemcode != 0; i++) + { + uint32_t cur_string_length = lstrlen(token_string_itemcode); + m_stItemList[i].code0 = token_string_itemcode[0]; + m_stItemList[i].code1 = token_string_itemcode[1]; + m_stItemList[i].code2 = token_string_itemcode[2]; + + if (token_string_itemcode[3] == ':') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = FALSE; + } + + if (cur_string_length <= 13) { //for example jew:123456789 + // p = 4 --> this is first digit after ':' + for (uint32_t p = 4; p <= cur_string_length; p++) { + if (token_string_itemcode[p] == 0) { + break; + } + if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') { + m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = TRUE; + } + } + } + } + + if (token_string_itemcode[3] == '-') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = TRUE; + } + + if (cur_string_length <= 13) { //for example jew:123456789 + // p = 4 --> this is first digit after '-' + for (uint32_t p = 4; p <= cur_string_length; p++) { + if (token_string_itemcode[p] == 0) { + break; + } + if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') { + m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = FALSE; + } + } + } + } + + if (token_string_itemcode[3] != '-' && token_string_itemcode[3] != ':') + { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = TRUE; + } + } + + token_string_itemcode = strtok(NULL, " ,|"); + } + + + /// Parse ItemType Code + dwLenght = lstrlen(m_pcItemTypesAll); + memcpy(m_pcItemTypesAllTemp, m_pcItemTypesAll, dwLenght + 1); + char* token_string_itemtype_code = strtok(m_pcItemTypesAllTemp, ",|"); + while (token_string_itemtype_code) + { + m_nCountItemTypesAll++; + token_string_itemtype_code = strtok(NULL, ",|"); + } + + m_stItemTypes = (item_type*)malloc(m_nCountItemTypesAll * sizeof(item_type)); + memset(m_stItemTypes, 0, m_nCountItemTypesAll * sizeof(item_type)); + + memcpy(m_pcItemTypesAllTemp, m_pcItemTypesAll, dwLenght + 1); + token_string_itemtype_code = strtok(m_pcItemTypesAllTemp, ",|"); + for (uint32_t i = 0; token_string_itemtype_code != 0; i++) + { + uint32_t cur_itemtypes_string_length = lstrlen(token_string_itemtype_code); + //m_stItemTypes[i].type0 = token_string_itemtype_code[0]; + //m_stItemTypes[i].type1 = token_string_itemtype_code[1]; + //m_stItemTypes[i].type2 = token_string_itemtype_code[2]; + //m_stItemTypes[i].type3 = token_string_itemtype_code[3]; + + m_stItemTypes[i].dwtype = *(DWORD*)token_string_itemtype_code; + + if (token_string_itemtype_code[4] == ':') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = FALSE; + } + + if (cur_itemtypes_string_length <= 14) { //for example tors:123456789 + // p = 5 --> this is first digit after ':' + for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) { + if (token_string_itemtype_code[p] == 0) { + break; + } + if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') { + m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = TRUE; + } + } + } + } + + if (token_string_itemtype_code[4] == '-') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = TRUE; + } + + if (cur_itemtypes_string_length <= 14) { //for example armo:123456789 + // p = 5 --> this is first digit after '-' + for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) { + if (token_string_itemtype_code[p] == 0) { + break; + } + if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') { + m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = FALSE; + } + } + } + } + + if (token_string_itemtype_code[4] != '-' && token_string_itemtype_code[4] != ':') + { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = TRUE; + } + } + + token_string_itemtype_code = strtok(NULL, ",|"); + } +} + + +class auto_item_pickup_menu : public d2_tweaks::ui::menu { + d2_tweaks::common::asset* m_buttons_img; + d2_tweaks::ui::controls::button* m_auto_pickup_btn; + +public: + auto_item_pickup_menu() { + menu::set_enabled(true); + menu::set_visible(true); + + //load_xml("d2tweaks\\interfaces\\autopickup.xml"); + if (DLLBASE_D2EXPRES != 0) + load_xml("d2tweaks\\interface_d2expres\\autopickup.xml"); + if (DLLBASE_SGD2FREERES != 0) + load_xml("d2tweaks\\interface_sgd2freeres\\autopickup.xml"); + if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0) + load_xml("d2tweaks\\interface_vanilla\\autopickup.xml"); + + m_buttons_img = singleton::instance().get_mpq_file("d2tweaks\\assets\\buttons", d2_tweaks::common::MPQ_FILE_TYPE_DC6); + m_auto_pickup_btn = get_button("m_auto_pickup_btn", std::bind(&auto_item_pickup_menu::auto_item_pickup_click, this)); + } + + void draw() override { + if (m_bToggleAutoItemPickup) { + //diablo2::d2_client::print_chat(L"AUTO TRANSMUTE ON", 1); + m_auto_pickup_btn->set_current_frame(8); // index of frame in buttons.dc6 + } + else if (!m_bToggleAutoItemPickup) + { + //diablo2::d2_client::print_chat(L"AUTO TRANSMUTE OFF", 2); + m_auto_pickup_btn->set_current_frame(6); + } + + if (m_bToggleAutoItemPickup && m_bInventoryFull) { + m_auto_pickup_btn->set_current_frame(7); + } + + //if (diablo2::d2_gfx::get_resolution_mode() == 0) { + // m_auto_pickup_btn->set_x(g_btn_hires_posx); + // m_auto_pickup_btn->set_y(g_btn_hires_posy); + //} + //else { + // m_auto_pickup_btn->set_x(g_btn_lowres_posx); + // m_auto_pickup_btn->set_y(g_btn_lowres_posy); + //} + + if (!should_draw()) { + m_auto_pickup_btn->set_enabled(false); + m_auto_pickup_btn->set_visible(false); + return; + } + + m_auto_pickup_btn->set_enabled(true); + m_auto_pickup_btn->set_visible(true); + + menu::draw(); + } +private: + static bool should_draw() { + return diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INTERFACE); + } + + d2_tweaks::ui::controls::button* get_button(const std::string& name, const std::function& onClick) { + auto result = static_cast(get_control(name)); + + result->set_on_click(onClick); + + result->set_enabled(false); + result->set_visible(false); + + return result; + } + + void auto_item_pickup_click() { + m_bToggleAutoItemPickup ^= true; + + if (m_bToggleAutoItemPickup) { + ReloadFilters(m_acPathToIni); + diablo2::d2_client::print_chat(L"AUTO PICKUP ON", 1); + } + else + { + diablo2::d2_client::print_chat(L"AUTO PICKUP OFF", 2); + } + } +}; + + +void d2_tweaks::client::modules::auto_item_pickup::init() { + GetCurrentDirectory(MAX_PATH, m_acPathToIni); + lstrcat(m_acPathToIni, m_pcIniFile); + + if (GetPrivateProfileInt("modules", "AutoItemPickup", 1, m_acPathToIni) != FALSE) { + m_iDistance = GetPrivateProfileInt("AutoItemPickup", "PickupDistance", 4, m_acPathToIni); + + //ReloadFilters(m_acPathToIni); + + singleton::instance().add_menu(new auto_item_pickup_menu()); + singleton::instance().register_tick_handler(this); + singleton::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_ITEM_PICKUP_INFO, this); + } +} + + +bool find_free_space(diablo2::structures::inventory* inv, diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y) { + char data[0x18]; + + diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data); + + const auto mx = static_cast(data[0]); + const auto my = static_cast(data[1]); + + for (x = 0; x < mx; x++) { + for (y = 0; y < my; y++) { + diablo2::structures::unit* blockingUnit = nullptr; + uint32_t blockingUnitIndex = 0; + + if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page)) + return true; + } + } + return false; +} + + +void d2_tweaks::client::modules::auto_item_pickup::tick() { + static common::item_pickup_info_sc packet; + const auto unit = diablo2::d2_client::get_local_player(); + + //spdlog::debug("g_tick_between_item_pickup {0}", g_tick_between_item_pickup); + m_nTick++; + + if (!m_bToggleAutoItemPickup) + return; + + if (!unit) + return; + + if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + return; + + const auto room = diablo2::d2_common::get_room_from_unit(unit); + + if (!room) + return; + + for (auto item = room->unit; item; item = item->prev_unit_in_room) { + if (!item) + continue; + + if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + continue; + + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + if (!record) + continue; + + const auto distance = diablo2::d2_common::get_distance_between_units(unit, item); + if (distance > m_iDistance) + continue; + + //// , + //if (record->string_code[0] == 'g' && + // record->string_code[1] == 'l' && + // record->string_code[2] == 'd') + //{ + // static d2_tweaks::common::gold_pickup_info_cs request_packet_cs; + // request_packet_cs.item_guid = item->guid; + // diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + // continue; + //} + + const auto itemtype_record = diablo2::d2_common::get_item_type_record(record->type); + auto itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv1); + auto itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv2); + + uint32_t quality = diablo2::d2_common::get_item_quality(item); + + // + char arr_itemtype_codestr_equivstr[20][5] = { 0 }; + + // itemtype code + *(DWORD*)arr_itemtype_codestr_equivstr[0] = *(DWORD*)itemtype_record->code; + // index second code in array + uint32_t index_arr_itemtype = 1; + + if (itemtype_record_equiv1) { + if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) { + // equiv1 + *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code; + index_arr_itemtype++; + // eqiv1 + for (index_arr_itemtype; itemtype_record_equiv1->equiv1 != 0; index_arr_itemtype++) { + // + itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv1->equiv1); + if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) { + *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code; + } + else break; + } + } + } + + + if (itemtype_record_equiv2) { + if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) { + // equiv1 + *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code; + index_arr_itemtype++; + // eqiv1 + for (index_arr_itemtype; itemtype_record_equiv2->equiv2 != 0; index_arr_itemtype++) { + // + itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv2->equiv2); + if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) { + *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code; + } + else break; + } + } + } + + static d2_tweaks::common::item_pickup_info_cs request_packet_cs; + request_packet_cs.item_guid = 0; + + for (uint32_t i = 0; i < m_nCountItemTypesAll; i++) + { + for (uint32_t count = 0; count < index_arr_itemtype; count++) + { + if (*(DWORD*)arr_itemtype_codestr_equivstr[count] == (DWORD)m_stItemTypes[i].dwtype) + { + if (m_stItemTypes[i].qualityinclude[quality] == TRUE) + { + const auto inventoryIndex = diablo2::d2_common::get_inventory_index(unit, 0, 0x65); + uint32_t tx = 0, ty = 0; + if (!find_free_space(unit->inventory, item, inventoryIndex, 0, tx, ty)) { + m_bInventoryFull = true; + if (m_nTick >= 500) { + diablo2::d2_client::play_sound(0xB76, unit, 0, 0, 0); + m_nTick = 0; + } + goto L1; + } + else { + m_bInventoryFull = false; + } + + request_packet_cs.item_guid = item->guid; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + goto L1; + } + } + } + } + + + for (uint32_t i = 0; i < m_nCountItemListAll; i++) + { + if (record->string_code[0] == m_stItemList[i].code0 && + record->string_code[1] == m_stItemList[i].code1 && + record->string_code[2] == m_stItemList[i].code2) + { + if (m_stItemList[i].qualityinclude[quality] == TRUE) + { + const auto inventoryIndex = diablo2::d2_common::get_inventory_index(unit, 0, 0x65); + uint32_t tx = 0, ty = 0; + if (!find_free_space(unit->inventory, item, inventoryIndex, 0, tx, ty)) { + m_bInventoryFull = true; + if (m_nTick >= 500) { + diablo2::d2_client::play_sound(0xB76, unit, 0, 0, 0); + m_nTick = 0; + } + goto L1; + } + else { + m_bInventoryFull = false; + } + + request_packet_cs.item_guid = item->guid; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + goto L1; + } + } + } + L1:; + } +} + + +void d2_tweaks::client::modules::auto_item_pickup::handle_packet(common::packet_header* packet) { + const auto info = static_cast(packet); + + //if (info->inventory_full == true) { + // m_nTick = 0; + //} + //else { + // g_tick_between_item_pickup = g_delay_between_item_pickup; + //} + //g_value += info->gold; +} diff --git a/src/d2tweaks/client/modules/autosort/autosort_client.cpp b/src/d2tweaks/client/modules/autosort/autosort_client.cpp new file mode 100644 index 0000000..4c40ec1 --- /dev/null +++ b/src/d2tweaks/client/modules/autosort/autosort_client.cpp @@ -0,0 +1,391 @@ +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include +#include + +MODULE_INIT(autosort) + +enum ColorEnum { + RED = diablo2::ui_color_t::UI_COLOR_RED, + LIGHT_GREEN = diablo2::ui_color_t::UI_COLOR_LIGHT_GREEN, + BLUE = diablo2::ui_color_t::UI_COLOR_BLUE, + DARK_GOLD = diablo2::ui_color_t::UI_COLOR_DARK_GOLD, + GREY = diablo2::ui_color_t::UI_COLOR_GREY, + BLACK = diablo2::ui_color_t::UI_COLOR_BLACK, + GOLD = diablo2::ui_color_t::UI_COLOR_GOLD, + ORANGE = diablo2::ui_color_t::UI_COLOR_ORANGE, + YELLOW = diablo2::ui_color_t::UI_COLOR_YELLOW, + DARK_GREEN = diablo2::ui_color_t::UI_COLOR_DARK_GREEN, + PURPLE = diablo2::ui_color_t::UI_COLOR_PURPLE, + GREEN = diablo2::ui_color_t::UI_COLOR_GREEN, + WHITE = diablo2::ui_color_t::UI_COLOR_WHITE, + BLACK2 = diablo2::ui_color_t::UI_COLOR_BLACK2, + DARK_WHITE = diablo2::ui_color_t::UI_COLOR_DARK_WHITE, + LIGHT_GREY = diablo2::ui_color_t::UI_COLOR_LIGHT_GREY +}; + +class inventory_sort_menu : public d2_tweaks::ui::menu { + d2_tweaks::common::asset* m_buttons_img; + + d2_tweaks::ui::controls::button* m_sort_inventory_btn; + d2_tweaks::ui::controls::button* m_sort_stash_btn; + d2_tweaks::ui::controls::button* m_sort_cube_btn; +public: + inventory_sort_menu() { + menu::set_enabled(true); + menu::set_visible(true); + + //load_xml("d2tweaks\\interfaces\\autosort.xml"); + if (DLLBASE_D2EXPRES != 0) + load_xml("d2tweaks\\interface_d2expres\\autosort.xml"); + if (DLLBASE_SGD2FREERES != 0) + load_xml("d2tweaks\\interface_sgd2freeres\\autosort.xml"); + if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0) + load_xml("d2tweaks\\interface_vanilla\\autosort.xml"); + + m_buttons_img = singleton::instance().get_mpq_file("d2tweaks\\assets\\buttons", d2_tweaks::common::MPQ_FILE_TYPE_DC6); + + m_sort_inventory_btn = get_button("m_sort_inventory_btn", std::bind(&inventory_sort_menu::sort_inventory_click, this)); + m_sort_stash_btn = get_button("m_sort_stash_btn", std::bind(&inventory_sort_menu::sort_stash_click, this)); + m_sort_cube_btn = get_button("m_sort_cube_btn", std::bind(&inventory_sort_menu::sort_cube_click, this)); + } + + void draw() override { + auto stats = globalStatsVector; + int textOffset = 40; // Initial offset for the first line + + const auto player = diablo2::d2_client::get_local_player(); + + // Add all items to vector + std::vector items; + for (auto item = player->inventory->first_item; item != nullptr; item = item->item_data->pt_next_item) { + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + if (record->type == 104 || record->type == 123) { + items.push_back(item); + } + } + + // Initialize statValue + int32_t statValue = 0; + + for (const auto& stat : stats) { + + double param = 6; + + int32_t spirits = diablo2::d2_common::get_stat(player, static_cast(185), NULL); + int32_t soulscaptured = statValue = diablo2::d2_common::get_stat(player, static_cast(184), NULL); + + switch (stat.stat) { + // 2. (statValue <- this is probably op stat1 ? * baseValue <- this is probably op base ) / 2 ^ param + + // (op stat1 value * base stat value) / (2 ^ param) + // let's try this fucking thing + + case 190: { + // str/spirits + statValue = static_cast((1 * spirits) / pow(2, param)); // what is the value of opStat_str + break; + } + case 191: { + // dex/spirits + statValue = static_cast((1 * spirits) / pow(2, param)); // what is the value of opStat_str + break; + } + case 192: { + // vit/spirits + statValue = static_cast((1 * spirits) / pow(2, param)); // what is the value of opStat_str + break; + } + case 193: { + // enr/spirits + statValue = static_cast((1 * spirits) / pow(2, param)); // what is the value of opStat_str + break; + } + case 200: { + // skills/souls + param = 8; + statValue = static_cast((1 * soulscaptured) / pow(2, param)); // what is the value of opStat_str + break; + } + + case 301: { + for (auto item : items) { + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + if (record->type == 104) { + statValue = diablo2::d2_common::get_stat(item, static_cast(stat.stat), NULL); + } + } + break; + } + + case 304: { + for (auto item : items) { + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + if (record->type == 104) { + statValue = diablo2::d2_common::get_stat(item, static_cast(stat.stat), NULL); + } + } + break; + } + + default: { + // By default, get player stats + statValue = diablo2::d2_common::get_stat(player, static_cast(stat.stat), NULL); + break; + + } + } + + /* + int32_t diablo2::d2_common::set_stat(structures::unit* unit, unit_stats_t stat, uint32_t value, int16_t param) { + static wrap_func_std_import set_stat(10517, get_base()); + return set_stat(unit, stat, value, param); + } + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(randStatRangeLow, randStatRangeHigh); + unsigned int randomNumber = dis(gen); + + std::random_device rdb; + std::mt19937 genb(rdb()); + std::uniform_int_distribution<> randBool(1, 2); + unsigned int randomBool = randBool(genb) - 1; + + if (stat.is_item_stat == 1) { + for (auto item : items) { + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + int RandStatValue = diablo2::d2_common::get_stat(item, static_cast(randStat), NULL); + + if (record->type == stat.item_type_id && RandStatValue != 0) { + // set randStat value to random number 1 and 2^(32) = 4294967296 + diablo2::d2_common::set_stat(item, static_cast(randStat), randomNumber, 0); + diablo2::d2_common::set_stat(item, static_cast(randStatBool), randomBool, 0); + } + } + } + else { + // set randStat value to random number 1 and 2^(32) = 4294967296 + //diablo2::d2_common::set_stat(player, static_cast(randStat), randomNumber, 0); + //diablo2::d2_common::set_stat(player, static_cast(randStatBool), randomBool, 0); + + int statValue1 = diablo2::d2_common::get_stat(player, static_cast(randStat), NULL); + int statValue2 = diablo2::d2_common::get_stat(player, static_cast(randStatBool), NULL); + + if (statValue1 > 0 ) { + diablo2::d2_common::set_stat(player, static_cast(randStat), 0, 0); + diablo2::d2_common::set_stat(player, static_cast(randStatBool), 0, 0); + } + + } + */ + auto statValueStr = std::to_wstring(statValue); + std::wstring statText = std::wstring(stat.stat_display_string);// .append(L" " + statValueStr); + + if (!diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CHARACTER) + // && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INVENTORY) + // && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_SKILL) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CHAT) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCMENU) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_MAINMENU) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CONFIG) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_ANVIL) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_QUEST) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_QUESTLOG) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STATUSAREA) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_WPMENU) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_PARTY) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_TRADE) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_MSGS) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_BELT) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_HELP) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_MERC) + && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_SCROLL)) { + + // Draw stats + diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); // Set font to FONT16 + diablo2::d2_win::draw_text(const_cast(statText.c_str()), stat.x1, stat.y1 + textOffset, stat.colorStat, 0); + + diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); // Set font to FONT16 + diablo2::d2_win::draw_text(const_cast(statValueStr.c_str()), stat.x2, stat.y2 + textOffset, stat.colorStatValue, 0); + + + //diablo2::d2_win::draw_boxed_text(const_cast(statText.c_str()), stat.x1, stat.y1 + textOffset, 1, 0, stat.colorStat); + //diablo2::d2_win::draw_boxed_text(const_cast(statValueStr.c_str()), stat.x2, stat.y2 + textOffset, 1, 1, statValueColor); + + diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); // Set font to FONT16 + + + //diablo2::structures::d2_cmp::init_gfx_data(&g_gfxdata); + + //diablo2::d2_gfx::draw_image(&g_gfxdata, 200, 200, 1, 5, 0); + + // instead try to load direct jpg with gdi and insetad ofloading jpg file, specify it bb64 encoded and decode it. + + diablo2::ui_color_t::UI_COLOR_WHITE; + + } + } + + if (!should_draw()) { + m_sort_inventory_btn->set_enabled(false); + m_sort_inventory_btn->set_visible(false); + + m_sort_stash_btn->set_enabled(false); + m_sort_stash_btn->set_visible(false); + + m_sort_cube_btn->set_enabled(false); + m_sort_cube_btn->set_visible(false); + + return; + } + + m_sort_inventory_btn->set_enabled(true); + m_sort_inventory_btn->set_visible(true); + + m_sort_stash_btn->set_enabled(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH)); + m_sort_stash_btn->set_visible(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH)); + + m_sort_cube_btn->set_enabled(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE)); + m_sort_cube_btn->set_visible(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE)); + + menu::draw(); + } +private: + static bool should_draw() { + return diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INVENTORY) || + diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH) || + diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE) || + diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP); + } + + d2_tweaks::ui::controls::button* get_button(const std::string& name, const std::function& onClick) { + auto result = static_cast(get_control(name)); + + result->set_on_click(onClick); + + result->set_enabled(false); + result->set_visible(false); + + return result; + } + + void sort_inventory_click() { + request_sort(0x00); + } + + void sort_stash_click() { + request_sort(0x04); + } + + void sort_cube_click() { + request_sort(0x03); + } + + void request_sort(const uint8_t page) { + static d2_tweaks::common::inventory_sort_cs packet; + + const auto player = diablo2::d2_client::get_local_player(); + + std::vector items; + + for (auto item = player->inventory->first_item; item != nullptr; item = item->item_data->pt_next_item) { + if (item->item_data->page == page) + items.push_back(item); + } + + for (auto item : items) + diablo2::d2_common::inv_remove_item(player->inventory, item); + + packet.page = page; + diablo2::d2_client::send_to_server(&packet, sizeof packet); + } +}; + + +void d2_tweaks::client::modules::autosort::init_early() { + +} + + +void d2_tweaks::client::modules::autosort::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "Autosort", 1, acPathToIni) != FALSE) { + singleton::instance().add_menu(new inventory_sort_menu()); + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_INVENTORY_SORT, this); + } +} + +void d2_tweaks::client::modules::autosort::handle_packet(common::packet_header* packet) { + static auto& instance = singleton::instance(); + const auto inventorySort = static_cast(packet); + + const auto item = instance.get_client_unit(0x04, inventorySort->guid); //0x03 -> 0x04 - item + + if (item == nullptr) + return;; + + const auto player = diablo2::d2_client::get_local_player(); + + //Last parameter is some boolean + const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, inventorySort->page, diablo2::d2_client::is_lod()); + + item->item_data->page = inventorySort->page; + + diablo2::d2_common::inv_add_item(player->inventory, item, inventorySort->tx, inventorySort->ty, inventoryIndex, true, item->item_data->page); + diablo2::d2_common::inv_update_item(player->inventory, item, true); +} + diff --git a/src/d2tweaks/client/modules/client_module.cpp b/src/d2tweaks/client/modules/client_module.cpp new file mode 100644 index 0000000..e8aabf3 --- /dev/null +++ b/src/d2tweaks/client/modules/client_module.cpp @@ -0,0 +1,15 @@ +#include + +#include + +d2_tweaks::client::modules::client_module::client_module() { + singleton::instance().register_module(this); +} + +void d2_tweaks::client::modules::client_module::draw_ui() {} + +void d2_tweaks::client::modules::client_module::tick() {} + +void d2_tweaks::client::modules::client_module::handle_packet(common::packet_header* packet) {} + +void d2_tweaks::client::modules::client_module::handle_cs_packet(common::packet_header* packet) {} diff --git a/src/d2tweaks/client/modules/damage_display/damage_display_client.cpp b/src/d2tweaks/client/modules/damage_display/damage_display_client.cpp new file mode 100644 index 0000000..0052ca3 --- /dev/null +++ b/src/d2tweaks/client/modules/damage_display/damage_display_client.cpp @@ -0,0 +1,410 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include // std::cout +#include // std::string, std::to_string + + +#include + +#include +#include +#include +#include + +MODULE_INIT(damage_display) + +struct damage_label { + bool screen_space; + diablo2::ui_color_t color; + uint32_t x; + uint32_t y; + uint32_t unit_width; + uint32_t unit_height; + uint64_t start; + int32_t damage; + int32_t currentHp; // New field for current hit points + int32_t maxHp; // New field for maximum hit points + int32_t text_width; + wchar_t text[64]; + + damage_label(uint32_t x, uint32_t y, uint32_t uw, uint32_t uh, int32_t damage, int32_t hp, int32_t maxHp) + : screen_space(false), color(diablo2::ui_color_t::UI_COLOR_WHITE), x(x), y(y), + unit_width(uw), unit_height(uh), damage(damage), currentHp(hp), maxHp(maxHp), text_width(42) { + start = GetTickCount64(); + memset(text, 0x00, sizeof text); + } + + damage_label() + : screen_space(false), color(diablo2::UI_COLOR_WHITE), x(0), y(0), + unit_width(0), unit_height(0), damage(0), currentHp(0), maxHp(0), text_width(0) { + start = GetTickCount64(); + memset(text, 0x00, sizeof text); + } +}; + +static growing_object_pool g_label_pool([]() { + return new damage_label(); +}); + +static const size_t DAMAGE_LABELS_SIZE = 1024; +static damage_label* g_labels[DAMAGE_LABELS_SIZE]{ nullptr }; +static size_t g_labels_count = 0; +static uint32_t DISPLAY_TIME = 500; + +static float lerp(const float v0, const float v1, const float t) { + return v0 + t * (v1 - v0); +} + +static bool add_label(damage_label* label) { + for (size_t i = 0; i < DAMAGE_LABELS_SIZE; i++) { + if (g_labels[i] != nullptr) + continue; + + g_labels[i] = label; + g_labels_count++; + return true; + } + + return false; +} + +static void remove_label(damage_label* label) { + for (size_t i = 0; i < DAMAGE_LABELS_SIZE; i++) { + if (g_labels[i] != label) + continue; + + g_labels[i] = nullptr; + g_labels_count--; + return; + } +} + +static void remove_label(size_t index) { + if (index < 0 || index >= DAMAGE_LABELS_SIZE) + return; + + g_labels[index] = nullptr; + g_labels_count--; +} + +static unsigned int g_font_enemy = 0; +static unsigned int g_font_player = 1; +static unsigned int g_player_label_posx = 70; +static unsigned int g_player_label_posy = 500; + +static void draw_damage_labels() { + const auto player = diablo2::d2_client::get_local_player(); + + if (!player) + return; + + if (g_labels_count == 0) + return; + + const auto currentTime = GetTickCount64(); + + size_t updatedLabels = 0; + auto mx = 0, my = 0; + + const auto font = diablo2::d2_win::get_current_font(); + + /*for (size_t i = 0; i < DAMAGE_LABELS_SIZE; i++) { + const auto label = g_labels[i]; + + if (label == nullptr) { + auto next = g_labels[i + 1]; + + if (updatedLabels == g_labels_count) + break; + + continue; + } + + const auto delta = currentTime - label->start; + + if (delta >= DISPLAY_TIME) { + remove_label(i); + g_label_pool.put(label); + continue; + } + + updatedLabels++; + + if (label->screen_space) { + mx = label->x; + my = label->y; + diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_player); + } else { + diablo2::utils::screen::world_to_screen(label->x, label->y, mx, my); + diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_enemy); + } + + const auto offset = static_cast(lerp(static_cast(label->unit_height) + 5.f, + static_cast(label->unit_height) + 30.f, + static_cast(delta) / static_cast(DISPLAY_TIME))); + + mx -= label->text_width / 2; + mx -= label->unit_width / 2; + my -= offset; + + diablo2::d2_win::draw_text(label->text, + mx, + my, + label->color, 0); + }*/ + + for (size_t i = 0; i < DAMAGE_LABELS_SIZE; i++) { + const auto label = g_labels[i]; + + if (label == nullptr) { + auto next = g_labels[i + 1]; + + if (updatedLabels == g_labels_count) + break; + + continue; + } + + const auto delta = currentTime - label->start; + + if (delta >= DISPLAY_TIME) { + remove_label(i); + g_label_pool.put(label); + continue; + } + + updatedLabels++; + + int32_t mx = label->x; + int32_t my = label->y; + + if (label->screen_space) { + diablo2::utils::screen::world_to_screen(label->x, label->y, mx, my); + } + else { + diablo2::utils::screen::world_to_screen(label->x, label->y, mx, my); + } + + mx -= label->text_width / 2; + mx -= label->unit_width / 2; + my -= label->unit_height; + + // Draw different labels based on screen_space flag + if (label->screen_space) { + const auto offset = static_cast(lerp(static_cast(label->unit_height) + 5.f, static_cast(label->unit_height) + 30.f, static_cast(delta) / static_cast(DISPLAY_TIME))); + mx -= 10; + my -= 50; + my -= offset; + + // Draw player label + std::wstring dmgText = std::to_wstring(label->damage); + const wchar_t* dmgTextPtr = dmgText.c_str(); + diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_player); + diablo2::d2_win::draw_text(const_cast(dmgTextPtr), mx, my, label->color, 0); + } + else { + // Draw monster healthbar + if (label->currentHp > 0) { + // Calculate the percentage of currentHp relative to maxHp + float healthPercentage = (label->maxHp != 0) ? static_cast(label->currentHp) / static_cast(label->maxHp) : 0.0f; + + // Determine the color based on healthPercentage + diablo2::ui_color_t textColor; + if (healthPercentage >= 0.67f) { + textColor = diablo2::UI_COLOR_GREEN; + } + else if (healthPercentage <= 0.33f) { + textColor = diablo2::UI_COLOR_RED; + } + else { + textColor = diablo2::UI_COLOR_YELLOW; + } + + int bMaxWidth = 100; + + // Calculate the width of the health bar based on the percentage (max width is 10 characters) + uint32_t barWidth = static_cast(healthPercentage * 10.0f); + barWidth = (barWidth > bMaxWidth) ? bMaxWidth : barWidth; // Ensure barWidth doesn't exceed 10 + barWidth = (label->currentHp > 0 && barWidth == 0) ? 1 : barWidth; // Ensure at least one '#' if currentHp is not 0 + + // Construct the health bar string representation + std::wstring barText; + for (uint32_t i = 0; i < barWidth; ++i) { + barText.append(L"!"); // Use '#' to represent filled portion of the bar + } + for (uint32_t i = barWidth; i < bMaxWidth; ++i) { + barText.append(L""); // Use '-' to represent empty portion of the bar + } + + // Construct the health fraction string + std::wstring fractionStr = std::to_wstring(label->currentHp) + L"/" + std::to_wstring(label->maxHp); + + // Combine the strings for health percentage and bar text + std::wstring combinedText = std::to_wstring(static_cast(healthPercentage * 100.0f)) + L"% " + barText; + + const WCHAR* combinedTextPtr = combinedText.c_str(); + + // Calculate text position for the combined text + uint32_t textX = mx; + uint32_t textY = my; + + + // Draw the combined text (health percentage and bar text) + //diablo2::d2_win::draw_text(const_cast(combinedText.c_str()), textX, textY, textColor, 0); + //diablo2::d2_win::draw_boxed_text(const_cast(fractionStr.c_str()), textX + label->unit_width/2, textY - 12, 0, 0, textColor); + + diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_enemy); // Set font to FONT16 + diablo2::d2_win::draw_text(const_cast(fractionStr.c_str()), textX + diablo2::d2_win::get_text_pixel_width(const_cast(combinedTextPtr)) / 2, textY - 12, textColor, 0); + + diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_enemy); // Set font to FONT6 + diablo2::d2_win::draw_text(const_cast(combinedText.c_str()), textX, textY, textColor, 0); + + //diablo2::d2_win::draw_boxed_text(const_cast(combinedText.c_str()), textX, textY, 0, 0, textColor); + + + const auto offset = static_cast(lerp(static_cast(label->unit_height) + 5.f, static_cast(label->unit_height) + 30.f, static_cast(delta) / static_cast(DISPLAY_TIME))); + my -= offset; + + // Draw damage label + std::wstring dmgText = L" " + std::to_wstring(label->damage) + L" "; + const wchar_t* dmgTextPtr = dmgText.c_str(); + diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_enemy); + diablo2::d2_win::draw_text(const_cast(dmgTextPtr), textX + diablo2::d2_win::get_text_pixel_width(const_cast(combinedTextPtr)) / 2, my + label->unit_height / 2, textColor, 0); + + } + + + } + } + + + + diablo2::d2_win::set_current_font(font); +} + +static void(__fastcall* g_draw_game_ui)(int32_t a1); +static void __fastcall draw_game_ui(int32_t a1) { + draw_damage_labels(); + g_draw_game_ui(a1); +} + +static diablo2::ui_color_t damage_type_to_color(d2_tweaks::common::damage_type_t type) { + switch (type) { + case d2_tweaks::common::DAMAGE_TYPE_PHYSICAL: return diablo2::UI_COLOR_GREY; + case d2_tweaks::common::DAMAGE_TYPE_COLD: return diablo2::UI_COLOR_BLUE; + case d2_tweaks::common::DAMAGE_TYPE_FIRE: return diablo2::UI_COLOR_RED; + case d2_tweaks::common::DAMAGE_TYPE_LIGHTNING: return diablo2::UI_COLOR_YELLOW; + case d2_tweaks::common::DAMAGE_TYPE_POISON: return diablo2::UI_COLOR_GREEN; + case d2_tweaks::common::DAMAGE_TYPE_MAGIC: return diablo2::UI_COLOR_PURPLE; + default: return diablo2::UI_COLOR_BLACK; + } +} + +void d2_tweaks::client::modules::damage_display::init_early() { + +} + +void d2_tweaks::client::modules::damage_display::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "DamageDisplay", 1, acPathToIni) != FALSE) { + g_font_enemy = GetPrivateProfileInt("DamageDisplay", "EnemyDamageFont", 0, acPathToIni); + g_font_player = GetPrivateProfileInt("DamageDisplay", "PlayerDamageFont", 1, acPathToIni); + g_player_label_posx = GetPrivateProfileInt("DamageDisplay", "PlayerDamagePosx", 70, acPathToIni); + g_player_label_posy = GetPrivateProfileInt("DamageDisplay", "PlayerDamagePosy", 500, acPathToIni); + DISPLAY_TIME = GetPrivateProfileInt("DamageDisplay", "DisplayTime", 1000, acPathToIni); + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_DAMAGE_INFO, this); + hooking::hook(diablo2::d2_client::get_base() + 0x80A30, draw_game_ui, reinterpret_cast(&g_draw_game_ui)); + } +} + +void d2_tweaks::client::modules::damage_display::handle_packet(common::packet_header* packet) { + static auto& instance = singleton::instance(); + static diablo2::structures::gfxdata gfxdata; + const auto player = diablo2::d2_client::get_local_player(); + const auto info = static_cast(packet); + + if (player == nullptr) + return; + + if (info->unit_type == 0x00 && info->guid == player->guid) { + const auto label = g_label_pool.get(); + label->screen_space = true; + label->color = damage_type_to_color(info->damage_type); + label->unit_width = 0; + label->unit_height = 0; + label->x = g_player_label_posx; + label->y = g_player_label_posy; + label->damage = info->damage; + label->start = GetTickCount64(); + swprintf_s(label->text, L"%u", label->damage); + label->text_width = diablo2::d2_win::get_text_pixel_width(label->text); + + if (add_label(label)) + return; + + g_label_pool.put(label); //prevent memory leak if there's no room for another label + return; + } + + if (info->unit_type != 0x01) + return; + + const auto monsterUnit = instance.get_client_unit(0x01, info->guid); + + if (monsterUnit == nullptr) + return; + + const auto mx = diablo2::d2_common::get_unit_x(monsterUnit->path); + const auto my = diablo2::d2_common::get_unit_y(monsterUnit->path); + + const auto label = g_label_pool.get(); + + label->color = damage_type_to_color(info->damage_type); + + memset(&gfxdata, 0x00, sizeof gfxdata); + + int32_t index; + if (diablo2::d2_client::cache_gfx_data(&gfxdata, monsterUnit, nullptr, 0, 0, &index, 1, -1) && + diablo2::structures::d2_cmp::init_gfx_data(&gfxdata) && + gfxdata.cell_init) { + label->unit_width = gfxdata.cell_init->width + gfxdata.cell_init->offset_x; + label->unit_height = gfxdata.cell_init->height - gfxdata.cell_init->offset_y; + } else { + label->unit_width = 0; + label->unit_height = 0; + } + + label->screen_space = false; + label->x = mx; + label->y = my; + label->damage = info->damage; + label->start = GetTickCount64(); + swprintf_s(label->text, L"%i", label->damage); + label->text_width = diablo2::d2_win::get_text_pixel_width(label->text); + + if (add_label(label)) + return; + + g_label_pool.put(label); //prevent memory leak if there's no room for another label +} + +void d2_tweaks::client::modules::damage_display::tick() { + +} diff --git a/src/d2tweaks/client/modules/item_drop_message/item_drop_message_client.cpp b/src/d2tweaks/client/modules/item_drop_message/item_drop_message_client.cpp new file mode 100644 index 0000000..f0d266d --- /dev/null +++ b/src/d2tweaks/client/modules/item_drop_message/item_drop_message_client.cpp @@ -0,0 +1,267 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_INIT(item_drop_message) + +struct message { + bool active; + uint64_t start; + int32_t text_width; + uint32_t quality; + wchar_t item_text[256]; + char item_code[4]; + // количество строк, длина каждой строки + char arr_itemtype_codestr_equivstr[20][5]; + + message(bool active, uint32_t hires_posx, uint32_t hires_posy, uint32_t lowres_posx, uint32_t lowres_posy, uint32_t quality) + : active(active), + text_width(0), quality(quality) { + start = 0;//GetTickCount(); + memset(item_text, 0x00, sizeof item_text); + memset(item_code, 0x00, sizeof item_code); + memset(arr_itemtype_codestr_equivstr, 0x00, sizeof arr_itemtype_codestr_equivstr); + } + + message() : active(0), text_width(0), quality(0) { + start = 0;//GetTickCount(); + memset(item_text, 0x00, sizeof item_text); + memset(item_code, 0x00, sizeof item_code); + memset(arr_itemtype_codestr_equivstr, 0x00, sizeof arr_itemtype_codestr_equivstr); + } +}; + +static uint32_t m_nMsgCount = 0; +static message m_stMsg[32]; + +static wchar_t* m_apwcColorStr[17] = {L"ÿc0", L"ÿc1", L"ÿc2", L"ÿc3", L"ÿc4", L"ÿc5", L"ÿc6", L"ÿc7", L"ÿc8", L"ÿc9", L"ÿc:", L"ÿc;", L"ÿc0", L"ÿc0", L"ÿc0", L"ÿc0", L"ÿc0" }; +static wchar_t* m_apwcQualityStr[10] = {L"", L"(Cracked)", L"(Normal)", L"(Superior)", L"(Magic)", L"(Set)", L"(Rare)", L"(Unique)", L"(Crafted)", L"(Tempered)"}; +static char* m_apcQualityStr[10] = {"", "(Cracked)", "(Normal)", "(Superior)", "(Magic)", "(Set)", "(Rare)", "(Unique)", "(Crafted)", "(Tempered)"}; + +static uint32_t m_nHookMethod = 1; +static uint32_t m_anQualityColor[10] = { 0 }; + +static char m_acSecondString[1024] = { 0 }; +static wchar_t m_awcSecondString[1024] = { 0 }; +static wchar_t m_awcCode[4] = { 0 }; + +static wchar_t m_awcItemtypeCode[8] = { 0 }; +static wchar_t m_aawcItemtypeEquiv[10][8] = { 0 }; + +static void(__fastcall* fn_GamePacketReceivedIntercept)(d2_tweaks::common::packet_header* packet, size_t size); + +class draw_item_menu final : public d2_tweaks::ui::menu { +public: + draw_item_menu() { + menu::set_visible(true); + menu::set_enabled(true); + } + + void draw() override { + for (uint32_t i = 0; i < m_nMsgCount; i++) { + m_stMsg[i].active = false; + } + } +}; + + +void d2_tweaks::client::modules::item_drop_message::GamePacketReceivedIntercept(uint8_t* packet, size_t size) { + if (packet == 0 || size == 0) + return; + + if ((packet[0] == 0x9C || (packet[0] == 0x9D)) && (packet[1] == 0x0 || packet[1] == 0x2 || packet[1] == 0x3)) { + static d2_tweaks::common::item_dropped_info_cs info; + info.item_id = *(WORD*)&packet[4]; + + spdlog::debug("[GamePacketReceived] ItemID {0} Pack {1} {2} {3} {4} Message {5} Size {6}", info.item_id, (void*)packet[4], (void*)packet[5], (void*)packet[6], (void*)packet[7], (void*)packet[1], size); + + diablo2::d2_client::send_to_server(&info, sizeof common::item_dropped_info_cs); + } + + return; +} + + +__declspec (naked) void d2_tweaks::client::modules::item_drop_message::GamePacketReceivedInterceptASM() { + __asm + { + // call our function (__fastcall) + pushad; + pushfd; + mov edx, ecx; + mov ecx, ebp; + call d2_tweaks::client::modules::item_drop_message::GamePacketReceivedIntercept; + popfd; + popad; + + jmp [fn_GamePacketReceivedIntercept] + } +} + + +void d2_tweaks::client::modules::item_drop_message::init_early() { + +} + + +void d2_tweaks::client::modules::item_drop_message::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "ItemDropMessage", 1, acPathToIni) != FALSE) { + m_nMsgCount = GetPrivateProfileInt("ItemDropMessage", "MaxNumMessages", 8, acPathToIni); + m_nHookMethod = GetPrivateProfileInt("ItemDropMessage", "HookMethod", 1, acPathToIni); + + m_anQualityColor[0] = GetPrivateProfileInt("ItemDropMessage", "DefaultColor", 0, acPathToIni); + m_anQualityColor[1] = GetPrivateProfileInt("ItemDropMessage", "Cracked", 0, acPathToIni); + m_anQualityColor[2] = GetPrivateProfileInt("ItemDropMessage", "Normal", 0, acPathToIni); + m_anQualityColor[3] = GetPrivateProfileInt("ItemDropMessage", "Superior", 0, acPathToIni); + m_anQualityColor[4] = GetPrivateProfileInt("ItemDropMessage", "Magic", 3, acPathToIni); + m_anQualityColor[5] = GetPrivateProfileInt("ItemDropMessage", "Set", 12, acPathToIni); + m_anQualityColor[6] = GetPrivateProfileInt("ItemDropMessage", "Rare", 9, acPathToIni); + m_anQualityColor[7] = GetPrivateProfileInt("ItemDropMessage", "Unique", 7, acPathToIni); + m_anQualityColor[8] = GetPrivateProfileInt("ItemDropMessage", "Crafted", 8, acPathToIni); + m_anQualityColor[9] = GetPrivateProfileInt("ItemDropMessage", "Tempered", 10, acPathToIni); + + GetPrivateProfileString("ItemDropMessage", "SecondString", "", m_acSecondString, 1023, acPathToIni); + mbstowcs(m_awcSecondString, m_acSecondString, 1023); + + // d2hackit hook d2client:$0x15123 + if (m_nHookMethod == 1) { + hooking::hook(diablo2::d2_client::get_base() + 0x15116, GamePacketReceivedInterceptASM, reinterpret_cast(&fn_GamePacketReceivedIntercept)); + } + + if (m_nHookMethod == 2) { + hooking::hook(diablo2::d2_client::get_base() + 0x1511A, GamePacketReceivedInterceptASM, reinterpret_cast(&fn_GamePacketReceivedIntercept)); + } + + singleton::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_ITEM_DROPPED_INFO, this); + singleton::instance().add_menu(new draw_item_menu()); + } +} + + +void d2_tweaks::client::modules::item_drop_message::handle_packet(common::packet_header* packet) { + const auto info = static_cast(packet); + const auto item_dropped_packet = static_cast(packet); + + spdlog::debug("[MyPacketReceived] Message {} Size {}", item_dropped_packet->message_type, sizeof item_dropped_packet); + + if (item_dropped_packet->message_type == common::message_types_t::MESSAGE_TYPE_ITEM_DROPPED_INFO) { + for (uint32_t i = 0; i < m_nMsgCount; i++) { + if (m_stMsg[i].active == false) { + static wchar_t buffer[512]; + static char buffermb[512]; + + m_stMsg[i].active = true; + m_stMsg[i].item_code[0] = item_dropped_packet->code[0]; + m_stMsg[i].item_code[1] = item_dropped_packet->code[1]; + m_stMsg[i].item_code[2] = item_dropped_packet->code[2]; + m_stMsg[i].quality = item_dropped_packet->quality; + m_stMsg[i].start = GetTickCount(); + + mbstowcs(buffer, (const char*)item_dropped_packet->namestr, 128); + memcpy(m_stMsg[i].item_text, buffer, 128); + memcpy(m_stMsg[i].arr_itemtype_codestr_equivstr, item_dropped_packet->arr_itemtype_codestr_equivstr, sizeof item_dropped_packet->arr_itemtype_codestr_equivstr); + + //сделать в названии предмета замену всех переносов на пробелы + for (uint32_t c = 0; c <= 128; c++) { + if (m_stMsg[i].item_text[c] == '\n') { + m_stMsg[i].item_text[c] = ' '; + } + } + + if (lstrlenW(m_awcSecondString) != 0) { + swprintf_s(buffer, L"%s%s%s", m_stMsg[i].item_text, m_apwcColorStr[m_anQualityColor[0]], m_awcSecondString); + } + else { + swprintf_s(buffer, L"%s", m_stMsg[i].item_text); + } + + // вывести сообщение о любом предмете + if (GetKeyState(VK_SCROLL) != 0) { + mbstowcs(m_awcCode, m_stMsg[i].item_code, 3); + mbstowcs(m_awcItemtypeCode, m_stMsg[i].arr_itemtype_codestr_equivstr[0], 4); + mbstowcs(m_aawcItemtypeEquiv[0], m_stMsg[i].arr_itemtype_codestr_equivstr[1], 4); + mbstowcs(m_aawcItemtypeEquiv[1], m_stMsg[i].arr_itemtype_codestr_equivstr[2], 4); + mbstowcs(m_aawcItemtypeEquiv[2], m_stMsg[i].arr_itemtype_codestr_equivstr[3], 4); + mbstowcs(m_aawcItemtypeEquiv[3], m_stMsg[i].arr_itemtype_codestr_equivstr[4], 4); + mbstowcs(m_aawcItemtypeEquiv[4], m_stMsg[i].arr_itemtype_codestr_equivstr[5], 4); + mbstowcs(m_aawcItemtypeEquiv[5], m_stMsg[i].arr_itemtype_codestr_equivstr[6], 4); + mbstowcs(m_aawcItemtypeEquiv[6], m_stMsg[i].arr_itemtype_codestr_equivstr[7], 4); + mbstowcs(m_aawcItemtypeEquiv[7], m_stMsg[i].arr_itemtype_codestr_equivstr[8], 4); + mbstowcs(m_aawcItemtypeEquiv[8], m_stMsg[i].arr_itemtype_codestr_equivstr[9], 4); + mbstowcs(m_aawcItemtypeEquiv[9], m_stMsg[i].arr_itemtype_codestr_equivstr[10], 4); + + swprintf_s(buffer, L"%s ÿc0Code: %s Quality: %i %s Type: %s Equiv: %s %s %s %s %s %s %s %s %s %s", + m_stMsg[i].item_text, + m_awcCode, + m_stMsg[i].quality, + m_apwcQualityStr[m_stMsg[i].quality], + m_awcItemtypeCode, + m_aawcItemtypeEquiv[0], + m_aawcItemtypeEquiv[1], + m_aawcItemtypeEquiv[2], + m_aawcItemtypeEquiv[3], + m_aawcItemtypeEquiv[4], + m_aawcItemtypeEquiv[5], + m_aawcItemtypeEquiv[6], + m_aawcItemtypeEquiv[7], + m_aawcItemtypeEquiv[8], + m_aawcItemtypeEquiv[9]); + } + + diablo2::d2_client::print_chat(buffer, (diablo2::ui_color_t)m_anQualityColor[m_stMsg[i].quality]); + + char* nul = ""; + wcstombs(buffermb, m_stMsg[i].item_text, 256); + spdlog::info("{} {} ItemList |{}:{}| ItemTypeCode |{}:{}| ItemTypeEquiv |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}|", + buffermb, + m_apcQualityStr[m_stMsg[i].quality], + + m_stMsg[i].item_code, m_stMsg[i].quality, + + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[0] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[0] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[0] != 0) ? m_stMsg[i].quality : 0, + + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[1] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[1] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[1] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[2] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[2] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[2] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[3] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[3] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[3] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[4] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[4] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[4] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[5] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[5] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[5] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[6] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[6] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[6] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[7] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[7] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[7] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[8] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[8] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[8] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[9] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[9] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[9] != 0) ? m_stMsg[i].quality : 0, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[10] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[10] : nul, + (*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[10] != 0) ? m_stMsg[i].quality : 0); + break; + } + } + } +} diff --git a/src/d2tweaks/client/modules/item_move/item_move_client.cpp b/src/d2tweaks/client/modules/item_move/item_move_client.cpp new file mode 100644 index 0000000..e390573 --- /dev/null +++ b/src/d2tweaks/client/modules/item_move/item_move_client.cpp @@ -0,0 +1,166 @@ +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +MODULE_INIT(item_move) + +int32_t(__fastcall* g_item_click_original)(diablo2::structures::unit* playerUnit, diablo2::structures::inventory* inventory, int mouse_x, int mouse_y, uint8_t flag, void* a6, unsigned int page); + +char get_target_page(char currentPage) { + if (currentPage == 0) { //item is in inventory + if (diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH)) + return 4; + + if (diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE)) + return 3; + } + + return 0; +} + +void request_item_move(diablo2::structures::unit* item, char targetPage) { + static d2_tweaks::common::item_move_cs packet; + + packet.item_guid = item->guid; + packet.target_page = targetPage; + + diablo2::d2_client::send_to_server(&packet, sizeof packet); +} + +int32_t __fastcall item_click(diablo2::structures::unit* owner, diablo2::structures::inventory* inventory, int mouse_x, int mouse_y, uint8_t flag, char* a6, unsigned int page) { + const auto player = diablo2::d2_client::get_local_player(); + + if (owner->guid != player->guid) + return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page); + + if ((static_cast(GetAsyncKeyState(VK_CONTROL)) >> 8 & 0x80u) == 0) + return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page); + + if (!diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE) && + !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH)) { + return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page); + } + + //code below taken from IDA directly, so that's why it is so ugly + const auto coefx1 = *(reinterpret_cast(a6) + 1); + const auto coefx2 = static_cast(a6[20]); + + const auto coefy1 = *(reinterpret_cast(a6) + 3); + const auto coefy2 = static_cast(a6[21]); + + const auto itemx = (mouse_x - coefx1) / coefx2; + const auto itemy = (mouse_y - coefy1) / coefy2; + + diablo2::structures::unit* cubeItem = nullptr; + + uint32_t px, py; + + const auto currentInventoryIndex = diablo2::d2_common::get_inventory_index(player, page, diablo2::d2_client::is_lod()); + const auto clickedItem = diablo2::d2_common::get_item_at_cell(player->inventory, itemx, itemy, &px, &py, currentInventoryIndex, page); + + for (auto item = player->inventory->first_item; item != nullptr; item = item->item_data->pt_next_item) { + auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + //auto flags1 = diablo2::d2_common::get_item_record(item->item_data->flags1); + //auto flags2 = diablo2::d2_common::get_item_record(item->item_data->flags2); + + if (record->string_code[0] == 'b' && + record->string_code[1] == 'o' && + record->string_code[2] == 'x') { //Cube + cubeItem = item; + break; + } + } + + static wrap_value dataTables(0x96A20, diablo2::d2_common::get_base()); + auto sgptdataTables = *dataTables; + + if (clickedItem != nullptr) { + const auto itemid = diablo2::d2_common::get_item_unique_index(clickedItem); + const auto itemRecord = diablo2::d2_common::get_item_record(clickedItem->data_record_index); + if (itemid != 0) { + if ((itemid >= 0) && (itemid < (int)sgptdataTables->nUniqueItems)) { + diablo2::structures::unique_items* uniqueItems = sgptdataTables->pUniqueItems + itemid; + if (uniqueItems && (uniqueItems->carry1 == 1)) { + //spdlog::warn("ItemID int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", itemid); + diablo2::d2_client::play_sound(itemRecord->drop_sound, nullptr, 0, 0, 0); + return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page); + } + } + } + } + + if (clickedItem == nullptr) { + return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page); + } + + const auto targetPage = get_target_page(page); + + if (targetPage == 0x03 && clickedItem == cubeItem) + return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page); + + request_item_move(clickedItem, targetPage); + return 0; +} + +void d2_tweaks::client::modules::item_move::init_early() { + +} + + +void d2_tweaks::client::modules::item_move::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "ItemMover", 1, acPathToIni) != FALSE) { + hooking::hook(diablo2::d2_client::get_base() + 0x475C0, item_click, reinterpret_cast(&g_item_click_original)); + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_ITEM_MOVE, this); + } +} + +void d2_tweaks::client::modules::item_move::handle_packet(common::packet_header* packet) { + static auto& instance = singleton::instance(); + + const auto itemMove = static_cast(packet); + const auto item = instance.get_client_unit(0x04, itemMove->item_guid); //0x03 -> 0x04 - item + + if (item == nullptr) + return; + + const auto player = diablo2::d2_client::get_local_player(); + + //Last parameter is some boolean + const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, itemMove->target_page, diablo2::d2_client::is_lod()); + + item->item_data->page = itemMove->target_page; + + diablo2::d2_common::inv_add_item(player->inventory, item, itemMove->tx, itemMove->ty, inventoryIndex, true, item->item_data->page); + diablo2::d2_common::inv_update_item(player->inventory, item, true); + + const auto itemRecord = diablo2::d2_common::get_item_record(item->data_record_index); + + if (itemRecord != nullptr) + diablo2::d2_client::play_sound(itemRecord->drop_sound, nullptr, 0, 0, 0); + else + diablo2::d2_client::play_sound(4, nullptr, 0, 0, 0); +} diff --git a/src/d2tweaks/client/modules/loot_filter/loot_filter.cpp b/src/d2tweaks/client/modules/loot_filter/loot_filter.cpp new file mode 100644 index 0000000..fe3462b --- /dev/null +++ b/src/d2tweaks/client/modules/loot_filter/loot_filter.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +#include + +#include + +#include +#include + +MODULE_INIT(loot_filter) + +static char* (__fastcall* g_set_player_name_original)(void*, void*); +static char* __fastcall set_player_name(void* player, void* edx) { + const auto result = g_set_player_name_original(player, edx); + + d2_tweaks::client::modules::loot_filter_settings::load(diablo2::d2_client::get_local_player_name()); + singleton::instance().reload_settings(); + + return result; +} + +static HANDLE(__fastcall* g_delete_save_file_original)(char*, char*); +static HANDLE __fastcall delete_save_file(char* name, char* a2) { + d2_tweaks::client::modules::loot_filter_settings::remove(name); + return g_delete_save_file_original(name, a2); +} + + +void d2_tweaks::client::modules::loot_filter::init_early() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "LootFilter", 1, acPathToIni) != FALSE) { + hooking::hook(diablo2::d2_client::get_base() + 0xBDE0, set_player_name, &g_set_player_name_original); + hooking::hook(diablo2::d2_launch::get_base() + 0x17C00, delete_save_file, &g_delete_save_file_original); + } +} + + +void d2_tweaks::client::modules::loot_filter::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "LootFilter", 1, acPathToIni) != FALSE) { + singleton::instance().add_menu(&singleton::instance()); + singleton::instance().add_menu(&singleton::instance()); + } +} diff --git a/src/d2tweaks/client/modules/loot_filter/loot_filter_settings.cpp b/src/d2tweaks/client/modules/loot_filter/loot_filter_settings.cpp new file mode 100644 index 0000000..81080c0 --- /dev/null +++ b/src/d2tweaks/client/modules/loot_filter/loot_filter_settings.cpp @@ -0,0 +1,86 @@ +#include + +#include + +#include + +#include + +using namespace d2_tweaks::client::modules; + +static char g_buffer[sizeof(loot_filter_settings)]{ 0 }; +static loot_filter_settings* g_settings = reinterpret_cast(g_buffer); + +loot_filter_settings& loot_filter_settings::get() { + return *g_settings; +} + +void loot_filter_settings::save(const char* name) { + static char savePath[1024]; + static char buffer[1024]; + diablo2::fog::get_save_path(savePath, sizeof savePath); + sprintf_s(buffer, "%s%s_lf.bin", savePath, name); + + spdlog::debug("Saving loot filter settings to {0}", buffer); + + FILE* file = nullptr; + const auto err = fopen_s(&file, buffer, "wb"); + + if (err != 0) { + spdlog::critical("Cannot save loot filter settings to {0}", buffer); + exit(0); + } + + fwrite(g_buffer, sizeof(loot_filter_settings), 1, file); + fclose(file); +} + +void loot_filter_settings::load(const char* name) { + static char savePath[1024]; + static char buffer[1024]; + diablo2::fog::get_save_path(savePath, sizeof savePath); + sprintf_s(buffer, "%s%s_lf.bin", savePath, name); + + spdlog::debug("Loading loot filter settings from {0}", buffer); + + if (!std::filesystem::exists(buffer)) { + FILE* file = nullptr; + const auto err = fopen_s(&file, buffer, "wb"); + + if (err != 0) { + spdlog::critical("Cannot create loot filter settings at {0}", buffer); + exit(0); + } + + loot_filter_settings dummy; + fwrite(&dummy, sizeof dummy, 1, file); + fclose(file); + } + + FILE* file = nullptr; + const auto err = fopen_s(&file, buffer, "rb"); + + if (err != 0) { + spdlog::critical("Cannot read loot filter settings from {0}", buffer); + exit(0); + } + + auto readed = fread(g_buffer, sizeof(loot_filter_settings), 1, file); + fclose(file); +} + +void loot_filter_settings::remove(const char* name) { + static char savePath[1024]; + static char buffer[1024]; + diablo2::fog::get_save_path(savePath, sizeof savePath); + sprintf_s(buffer, "%s%s_lf.bin", savePath, name); + + spdlog::debug("Deleting loot filter settings from {0}", buffer); + + if (!std::filesystem::exists(buffer)) + return; + + std::filesystem::remove(buffer); + memset(g_buffer, 0x00, sizeof g_buffer); +} + diff --git a/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.cpp b/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.cpp new file mode 100644 index 0000000..eadff2e --- /dev/null +++ b/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.cpp @@ -0,0 +1,347 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +d2_tweaks::client::modules::loot_filter_settings_menu::loot_filter_settings_menu(token) { + menu::set_enabled(false); + menu::set_visible(false); + + //load_xml("d2tweaks\\interfaces\\loot_filter_settings_menu.xml"); + if (DLLBASE_D2EXPRES != 0) + load_xml("d2tweaks\\interface_d2expres\\loot_filter_settings_menu.xml"); + if (DLLBASE_SGD2FREERES != 0) + load_xml("d2tweaks\\interface_sgd2freeres\\loot_filter_settings_menu.xml"); + if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0) + load_xml("d2tweaks\\interface_vanilla\\loot_filter_settings_menu.xml"); + + register_misc_checkboxes(); + register_quality_checkboxes(); + + setup_hooks(); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::reload_settings() { + register_misc_checkboxes(); + register_quality_checkboxes(); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::draw() { + if (diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_MAINMENU)) + return; + + menu::draw(); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::register_misc_checkboxes() { + m_altonly = get_control("m_altonly"); + m_show_gold = get_control("m_show_gold"); + m_show_runes = get_control("m_show_runes"); + m_show_gems = get_control("m_show_gems"); + + if (m_altonly) { + m_altonly->set_state(loot_filter_settings::get().alt_only); + m_altonly->set_on_click(std::bind(&loot_filter_settings_menu::update_alt_only, + this, std::placeholders::_1)); + } + + if (m_show_gold) { + m_show_gold->set_state(loot_filter_settings::get().show_gold); + m_show_gold->set_on_click(std::bind(&loot_filter_settings_menu::update_show_gold, + this, std::placeholders::_1)); + } + + if (m_show_runes) { + m_show_runes->set_state(loot_filter_settings::get().show_runes); + m_show_runes->set_on_click(std::bind(&loot_filter_settings_menu::update_show_runes, + this, std::placeholders::_1)); + } + + if (m_show_gems) { + m_show_gems->set_state(loot_filter_settings::get().show_gems); + m_show_gems->set_on_click(std::bind(&loot_filter_settings_menu::update_show_gems, + this, std::placeholders::_1)); + } +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::register_quality_checkboxes() { + using namespace diablo2::structures; + + register_quality_checkbox("m_inferior", item_quality_t::ITEM_QUALITY_INFERIOR); + register_quality_checkbox("m_normal", item_quality_t::ITEM_QUALITY_NORMAL); + register_quality_checkbox("m_superior", item_quality_t::ITEM_QUALITY_SUPERIOR); + register_quality_checkbox("m_magic", item_quality_t::ITEM_QUALITY_MAGIC); + register_quality_checkbox("m_set", item_quality_t::ITEM_QUALITY_SET); + register_quality_checkbox("m_rare", item_quality_t::ITEM_QUALITY_RARE); + register_quality_checkbox("m_unique", item_quality_t::ITEM_QUALITY_UNIQUE); + register_quality_checkbox("m_crafted", item_quality_t::ITEM_QUALITY_CRAFTED); + register_quality_checkbox("m_tempered", item_quality_t::ITEM_QUALITY_TEMPERED); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::update_alt_only(bool value) { + loot_filter_settings::get().alt_only = value; + loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name()); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::update_show_gold(bool value) { + loot_filter_settings::get().show_gold = value; + loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name()); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::update_show_runes(bool value) { + loot_filter_settings::get().show_runes = value; + loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name()); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::update_show_gems(bool value) { + loot_filter_settings::get().show_gems = value; + loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name()); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::update_quality_allowance(bool value, + diablo2::structures::item_quality_t quality) { + loot_filter_settings::get().quality_settings[static_cast(quality)] = value; + loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name()); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::register_quality_checkbox(const std::string& name, + diablo2::structures::item_quality_t quality) { + auto control = get_control(name); + + if (!control) + return; + + control->set_state(loot_filter_settings::get().quality_settings[static_cast(quality)]); + control->set_on_click(std::bind(&loot_filter_settings_menu::update_quality_allowance, + this, std::placeholders::_1, quality)); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::setup_hooks() { + m_handle_dropped_items_original = + static_cast(hooking::get_call( + diablo2::d2_client::get_base() + 0x1641B)); + m_draw_dropped_items_names_original = + static_cast(hooking::get_call( + diablo2::d2_client::get_base() + 0x81BF5)); + + hooking::set_call(diablo2::d2_client::get_base() + 0x81BF5, draw_dropped_items_names); + hooking::set_call(diablo2::d2_client::get_base() + 0x1641B, handle_dropped_items); + + setup_alt_hook(); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::setup_alt_hook() const { + asm_code asmCode; + asmCode.add({ 0x83, 0x7D, 0x00, 0x04 }); //cmp dword ptr [ebp+0], 4 + asmCode.add({ 0x0F, 0x85 }, + new asm_address_relative(2, 6, diablo2::d2_client::get_base() + 0x6A399)); //jnz D2Client.dll+6A399 + + asmCode.add({ 0x56 }); //push esi + asmCode.add({ 0xB8 }, new asm_address_static(1, check_alt_item)); //mov eax, check_alt_item + asmCode.add({ 0x8B, 0xCD }); //mov ecx, ebp + asmCode.add({ 0xFF, 0xD0 }); //call eax + asmCode.add({ 0x5E }); //pop esi + + asmCode.add({ 0x84, 0xC0 }); //test al, al + asmCode.add({ 0x0F, 0x84 }, + new asm_address_relative(2, 6, diablo2::d2_client::get_base() + 0x6A399)); //je D2Client.dll+6A399 + asmCode.add({ 0xE9 }, + new asm_address_relative(1, 5, diablo2::d2_client::get_base() + 0x6A027)); //jmp D2Client.dll+6A027 + + auto addr = diablo2::d2_client::get_base() + 0x6A022; + + DWORD old_protect; + VirtualProtect(addr, 5, PAGE_EXECUTE_READWRITE, &old_protect); + + memset(addr, 0x90, 10); + *reinterpret_cast(addr) = 0xE9; + *reinterpret_cast(static_cast(addr) + 1) = + reinterpret_cast(asmCode.get_code()) - reinterpret_cast(addr) - 5; + + VirtualProtect(addr, 5, old_protect, &old_protect); +} + +bool d2_tweaks::client::modules::loot_filter_settings_menu::is_gold(diablo2::structures::unit* item) { + static auto goldRecordIndex = -1; + + if (item == nullptr) + return false; + + if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + return false; + + if (item->item_data == nullptr) + return false; + + if (goldRecordIndex == -1) { + auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + if (record->string_code[0] == 'g' && + record->string_code[1] == 'l' && + record->string_code[2] == 'd') { //Gold pile + goldRecordIndex = item->data_record_index; + } + } + + return item->data_record_index == goldRecordIndex; +} + +bool d2_tweaks::client::modules::loot_filter_settings_menu::is_rune(diablo2::structures::unit* item) { + static auto runeTypeId = -1; + + if (item == nullptr) + return false; + + if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + return false; + + if (item->item_data == nullptr) + return false; + + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + if (runeTypeId == -1) { + const auto itemTypeRecord = diablo2::d2_common::get_item_type_record(record->type); + + const auto code = itemTypeRecord->code; + + if (code[0] == 'r' && + code[1] == 'u' && + code[2] == 'n' && + code[3] == 'e') { + runeTypeId = record->type; + } + } + + return record->type == runeTypeId; +} + +bool d2_tweaks::client::modules::loot_filter_settings_menu::is_gem(diablo2::structures::unit* item) { + static auto gemTypeId = -1; + + if (item == nullptr) + return false; + + if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + return false; + + if (item->item_data == nullptr) + return false; + + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + if (gemTypeId == -1) { + const auto itemTypeRecord = diablo2::d2_common::get_item_type_record(record->type); + + const auto code = itemTypeRecord->code; + + if (code[0] == 'g' && + code[1] == 'e' && + code[2] == 'm') { + gemTypeId = record->type; + } + } + + return record->type == gemTypeId; +} + +bool d2_tweaks::client::modules::loot_filter_settings_menu::check_alt_item(diablo2::structures::unit* unit) { + static auto& instance = singleton::instance(); + + if (!unit || unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) { + return true; + } + + if (unit->item_data == nullptr) { + return true; + } + + if (is_gold(unit)) + return instance.m_show_gold && instance.m_show_gold->get_state(); + + if (is_rune(unit)) + return instance.m_show_runes && instance.m_show_runes->get_state(); + + if (is_gem(unit)) + return instance.m_show_gems && instance.m_show_gems->get_state(); + + return loot_filter_settings::get().quality_settings[static_cast(unit->item_data->quality)]; +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::draw_dropped_items_names(diablo2::structures::unit* unit, + void* edx) { + static auto& instance = singleton::instance(); + + if (!unit || unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) { + instance.m_draw_dropped_items_names_original(unit, edx); + return; + } + + if (unit->item_data == nullptr) { + instance.m_draw_dropped_items_names_original(unit, edx); + return; + } + + if (instance.m_altonly && instance.m_altonly->get_state()) + return; + + if (is_gold(unit) && instance.m_show_gold && !instance.m_show_gold->get_state()) + return; + + if (is_rune(unit) && instance.m_show_runes && !instance.m_show_runes->get_state()) + return; + + if (is_gem(unit) && instance.m_show_gems && !instance.m_show_gems->get_state()) + return; + + if (!loot_filter_settings::get().quality_settings[static_cast(unit->item_data->quality)]) + return; + + instance.m_draw_dropped_items_names_original(unit, edx); +} + +void d2_tweaks::client::modules::loot_filter_settings_menu::handle_dropped_items(diablo2::structures::unit* unit, + void* edx) { + static auto& instance = singleton::instance(); + + if (!unit || unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) { + instance.m_handle_dropped_items_original(unit, edx); + return; + } + + if (unit->item_data == nullptr) { + instance.m_handle_dropped_items_original(unit, edx); + return; + } + + if (instance.m_altonly && instance.m_altonly->get_state()) + return; + + if (is_gold(unit) && instance.m_show_gold && !instance.m_show_gold->get_state()) + return; + + if (is_rune(unit) && instance.m_show_runes && !instance.m_show_runes->get_state()) + return; + + if (is_gem(unit) && instance.m_show_gems && !instance.m_show_gems->get_state()) + return; + + if (!loot_filter_settings::get().quality_settings[static_cast(unit->item_data->quality)]) + return; + + instance.m_handle_dropped_items_original(unit, edx); +} diff --git a/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.cpp b/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.cpp new file mode 100644 index 0000000..f6d1ee8 --- /dev/null +++ b/src/d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.cpp @@ -0,0 +1,60 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +d2_tweaks::client::modules::loot_filter_settings_toggle_menu::loot_filter_settings_toggle_menu(token) { + m_show = false; + + menu::set_enabled(true); + menu::set_visible(true); + + //load_xml("d2tweaks\\interfaces\\loot_filter_settings_toggle_menu.xml"); + if (DLLBASE_D2EXPRES != 0) + load_xml("d2tweaks\\interface_d2expres\\loot_filter_settings_toggle_menu.xml"); + if (DLLBASE_SGD2FREERES != 0) + load_xml("d2tweaks\\interface_sgd2freeres\\loot_filter_settings_toggle_menu.xml"); + if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0) + load_xml("d2tweaks\\interface_vanilla\\loot_filter_settings_toggle_menu.xml"); + + m_toggle_filter_settings_btn = static_cast( + get_control("m_toggle_filter_settings_btn")); + m_toggle_filter_settings_btn->set_enabled(false); + m_toggle_filter_settings_btn->set_visible(false); + m_toggle_filter_settings_btn->set_on_click(std::bind(&loot_filter_settings_toggle_menu::toggle_filter_settings_click, this)); + + m_filter_settings_menu = singleton::instance().get_menu("loot_filter_settings_menu"); +} + +void d2_tweaks::client::modules::loot_filter_settings_toggle_menu::toggle_filter_settings_click() { + m_show = !m_show; + + m_filter_settings_menu->set_enabled(m_show); + m_filter_settings_menu->set_visible(m_show); +} + +void d2_tweaks::client::modules::loot_filter_settings_toggle_menu::draw() { + m_toggle_filter_settings_btn->set_enabled(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INTERFACE)); + m_toggle_filter_settings_btn->set_visible(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INTERFACE)); + + menu::draw(); +} + +bool d2_tweaks::client::modules::loot_filter_settings_toggle_menu::key_event(uint32_t key, bool up) { + if (key == VK_ESCAPE && m_show) { + m_show = false; + + m_filter_settings_menu->set_enabled(m_show); + m_filter_settings_menu->set_visible(m_show); + + return true; //block escape key stroke + } + + return menu::key_event(key, up); +} diff --git a/src/d2tweaks/client/modules/small_patches/small_patches.cpp b/src/d2tweaks/client/modules/small_patches/small_patches.cpp new file mode 100644 index 0000000..da07726 --- /dev/null +++ b/src/d2tweaks/client/modules/small_patches/small_patches.cpp @@ -0,0 +1,42 @@ +#include + +#include + +#include +#include +#include +#include + +MODULE_INIT(small_patches) + +static int(__cdecl* g_is_iconic_original)(); +static int __cdecl is_iconic() { + return 0; +} + +void d2_tweaks::client::modules::small_patches::init_early() { +} + +void d2_tweaks::client::modules::small_patches::init() { + ////Ingame FPS unlock + //DWORD oldProtect; + //char PathToIni[MAX_PATH] = { 0 }; + //const char IniFile[] = "\\d2tweaks.ini"; + + //GetCurrentDirectory(MAX_PATH, PathToIni); + //lstrcat(PathToIni, IniFile); + + //if (GetPrivateProfileInt("modules", "SmallPatches", 1, PathToIni) != FALSE) { + // if (VirtualProtect(diablo2::d2_client::get_base() + 0xA2C9, 0x04, PAGE_EXECUTE_READWRITE, &oldProtect)) + // *reinterpret_cast(diablo2::d2_client::get_base() + 0xA2C9) = 0x90909090; + + // //Main menu FPS unlock + // if (VirtualProtect(diablo2::d2_win::get_base() + 0xD029, 0x03, PAGE_EXECUTE_READWRITE, &oldProtect)) + // memset(diablo2::d2_win::get_base() + 0xD029, 0x90, 0x03); + + // return; + + // //Window auto-hiding on focus loss + // hooking::hook<10026>(diablo2::d2_gfx::get_base(), is_iconic, &g_is_iconic_original); + //} +} diff --git a/src/d2tweaks/client/modules/test/test.cpp b/src/d2tweaks/client/modules/test/test.cpp new file mode 100644 index 0000000..0fb5640 --- /dev/null +++ b/src/d2tweaks/client/modules/test/test.cpp @@ -0,0 +1,74 @@ +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_INIT(test) + +class test_menu : public d2_tweaks::ui::menu { +public: + test_menu() { + menu::set_enabled(true); + menu::set_visible(true); + } + + void draw() override { + using namespace diablo2::structures; + + static wrap_value dataTables(0x96A20, diablo2::d2_common::get_base()); + const auto player = diablo2::d2_client::get_local_player(); + + if (!player) + return; + + auto v = *dataTables; + + draw_cursor_pos(); + + menu::draw(); + } +private: + static void draw_cursor_pos() { + static wchar_t buffer[512]{ 0 }; + + const auto mx = diablo2::d2_client::get_mouse_x(); + const auto my = diablo2::d2_client::get_mouse_y(); + + swprintf_s(buffer, L"%i, %i", mx, my); + diablo2::d2_win::draw_text(buffer, mx, my, diablo2::UI_COLOR_WHITE, 0); + } +}; + +static test_menu* g_test_menu; + +void d2_tweaks::client::modules::test::init_early() { + +} + +void d2_tweaks::client::modules::test::init() { +#ifndef NDEBUG + g_test_menu = new test_menu(); + singleton::instance().add_menu(g_test_menu); +#endif +} + +void d2_tweaks::client::modules::test::handle_packet(common::packet_header* packet) {} \ No newline at end of file diff --git a/src/d2tweaks/client/modules/trader_update/trader_update_client.cpp b/src/d2tweaks/client/modules/trader_update/trader_update_client.cpp new file mode 100644 index 0000000..944af22 --- /dev/null +++ b/src/d2tweaks/client/modules/trader_update/trader_update_client.cpp @@ -0,0 +1,204 @@ +#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 + +#include +#include +#include + +MODULE_INIT(trader_update) + +static uint8_t m_nMenuOpen; +static uint8_t m_nNpcId; + +enum trader_command { + COMMAND_NULL, + COMMAND_FREE_NPC_INVENTORY, + COMMAND_FREE_NPC_GAMBLE, + COMMAND_FILL_NPC_INVENTORY, + COMMAND_FILL_NPC_GAMBLE, + COMMAND_FINISHED, + COMMAND_ABORT +}; + + +class trader_update_menu : public d2_tweaks::ui::menu { + d2_tweaks::common::asset* m_buttons_img; + d2_tweaks::ui::controls::button* m_trader_update_btn; + d2_tweaks::ui::controls::button* m_gamble_update_btn; + +public: + trader_update_menu() { + menu::set_enabled(true); + menu::set_visible(true); + + //load_xml("d2tweaks\\interface_d2expres\\reloaditems.xml"); + if (DLLBASE_D2EXPRES != 0) + load_xml("d2tweaks\\interface_d2expres\\reloaditems.xml"); + if (DLLBASE_SGD2FREERES != 0) + load_xml("d2tweaks\\interface_sgd2freeres\\reloaditems.xml"); + if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0) + load_xml("d2tweaks\\interface_vanilla\\reloaditems.xml"); + + m_buttons_img = singleton::instance().get_mpq_file("d2tweaks\\assets\\buttons", d2_tweaks::common::MPQ_FILE_TYPE_DC6); + m_trader_update_btn = get_button("m_reload_items_btn", std::bind(&trader_update_menu::trader_update_click, this)); + } + + void draw() override { + if (!should_draw()) { + m_trader_update_btn->set_enabled(false); + m_trader_update_btn->set_visible(false); + return; + } + + m_trader_update_btn->set_enabled(true); + m_trader_update_btn->set_visible(true); + + menu::draw(); + } +private: + static bool should_draw() { + return diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP); + } + + d2_tweaks::ui::controls::button* get_button(const std::string& name, const std::function& onClick) { + auto result = static_cast(get_control(name)); + result->set_on_click(onClick); + result->set_enabled(false); + result->set_visible(false); + return result; + } + + void trader_update_click() { + const auto unit = diablo2::d2_client::get_local_player(); + + static d2_tweaks::common::trader_update_cs request_packet_cs; + request_packet_cs.client_id = unit->guid; + request_packet_cs.npc_id = diablo2::d2_client::current_vendor_guid(); + request_packet_cs.is_gamble_menu_open = diablo2::d2_client::is_gamble_open(); + + if (request_packet_cs.is_gamble_menu_open == false) { + request_packet_cs.command = COMMAND_FREE_NPC_INVENTORY; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + } + + if (request_packet_cs.is_gamble_menu_open == true) { + request_packet_cs.command = COMMAND_FREE_NPC_GAMBLE; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + } + } +}; + + +void d2_tweaks::client::modules::trader_update::init_early() { + +} + + +void d2_tweaks::client::modules::trader_update::init() { + char szDir[MAX_PATH]; + char szPath[MAX_PATH]; + const char szConfig[] = "d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, szDir); + snprintf(szPath, MAX_PATH, "%s\\%s", szDir, szConfig); + CIni config(szPath); + + if (config.GetInt("modules", "ReloadTradeGamble", 1) != FALSE) { + singleton::instance().add_menu(new trader_update_menu()); + singleton::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_TRADER_UPDATE, this); + //singleton::instance().register_tick_handler(this); + //singleton::instance().register_packet_cs_handler(common::packet_types_cs_t::PACKET_0x38, common::message_types_t::MESSAGE_TYPE_TRADER_UPDATE, this); + } +} + + +void d2_tweaks::client::modules::trader_update::tick() { +// const auto unit = diablo2::d2_client::get_local_player(); +} + + +void d2_tweaks::client::modules::trader_update::handle_cs_packet(common::packet_header* packet) { + static auto& instance = singleton::instance(); + const auto income_packet_cs = static_cast(packet); + +#ifndef NDEBUG + spdlog::debug("[D2CLIENT C > S] ENTITY ACTION! message {} action {} entity_id {} complement {}", (uint8_t) income_packet_cs->message_type, (void*)(income_packet_cs->action >> 24), (void*)(income_packet_cs->entity_id >> 24), (void*)income_packet_cs->complement); +#endif + m_nMenuOpen = (uint8_t)income_packet_cs->message_type; // 1 = trade, 2 = gamble + m_nNpcId = (income_packet_cs->action >> 24); +} + + +void d2_tweaks::client::modules::trader_update::handle_packet(common::packet_header* packet) { + static auto& instance = singleton::instance(); + const auto income_packet_sc = static_cast(packet); + + if (!diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP)) + return; + + m_nNpcId = diablo2::d2_client::current_vendor_guid(); + + diablo2::structures::unit* ptNPC = diablo2::d2_client::get_unit_by_guid(m_nNpcId, (int32_t)diablo2::structures::unit_type_t::UNIT_TYPE_MONSTER); + + if (ptNPC == 0) + return; + + if (income_packet_sc->command == COMMAND_FREE_NPC_INVENTORY) { + // + if (income_packet_sc->npc_id == m_nNpcId && income_packet_sc->is_gamble_menu_open == diablo2::d2_client::is_gamble_open()) { + diablo2::d2_client::resync_vendor_inventory(ptNPC); + //diablo2::d2_common::empty_inventory_2(ptNPC->inventory); + + static d2_tweaks::common::trader_update_cs request_packet_cs; + request_packet_cs.command = COMMAND_FILL_NPC_INVENTORY; + request_packet_cs.npc_id = m_nNpcId; + request_packet_cs.is_gamble_menu_open = diablo2::d2_client::is_gamble_open(); + request_packet_cs.client_id = income_packet_sc->client_id; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + } + } + + if (income_packet_sc->command == COMMAND_FREE_NPC_GAMBLE) { + // + if (income_packet_sc->npc_id == m_nNpcId && income_packet_sc->is_gamble_menu_open == diablo2::d2_client::is_gamble_open()) { + diablo2::d2_client::resync_vendor_inventory(ptNPC); + //diablo2::d2_common::empty_inventory_2(ptNPC->inventory); + + static d2_tweaks::common::trader_update_cs request_packet_cs; + request_packet_cs.command = COMMAND_FILL_NPC_GAMBLE; + request_packet_cs.npc_id = m_nNpcId; + request_packet_cs.is_gamble_menu_open = diablo2::d2_client::is_gamble_open(); + request_packet_cs.client_id = income_packet_sc->client_id; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + } + } +} + diff --git a/src/d2tweaks/client/modules/transmute/transmute_client.cpp b/src/d2tweaks/client/modules/transmute/transmute_client.cpp new file mode 100644 index 0000000..be20e1b --- /dev/null +++ b/src/d2tweaks/client/modules/transmute/transmute_client.cpp @@ -0,0 +1,565 @@ +#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 +#include +#include + +MODULE_INIT(transmute) + +static uint32_t m_nTransmuteSound = 0; +static bool m_bToggleTransmute = 0; + +static uint32_t m_nDelayFrames = 10; +static uint32_t m_nCountFrames = 0; + +static bool m_game_init_done = false; + +static const uint8_t PAGE_CUBE = 3; +static const uint8_t PAGE_STASH = 4; +static const uint8_t PAGE_INVENTORY = 0; + +static const uint32_t m_nCountKeys = 10; + +static uint32_t m_nCountItemListAll = 0; +static char m_aacItemList[m_nCountKeys][MAX_STRING_LENGHT] = { 0 }; +static char m_acItemListAll[MAX_STRING_LENGHT * m_nCountKeys] = { 0 }; +static char m_acItemListAllTemp[MAX_STRING_LENGHT * m_nCountKeys] = { 0 }; + +static uint32_t m_nCountItemTypesAll = 0; +static char m_aacItemTypes[m_nCountKeys][MAX_STRING_LENGHT] = { 0 }; +static char m_acItemTypesAll[MAX_STRING_LENGHT * m_nCountKeys] = { 0 }; +static char m_acItemTypesAllTemp[MAX_STRING_LENGHT * m_nCountKeys] = { 0 }; + +static char m_acBuffer[1024] = { 0 }; + +static item_code* m_stItemList; +static item_type* m_stItemTypes; + +static void(__fastcall* fn_hook_play_sound)(uint32_t soundId, uint32_t* unit, const uint32_t ticks, const BOOL prePick, const uint32_t cache); +static void(__fastcall* fn_hook_game_end)(); + +enum transmute_command { + COMMAND_NULL, + COMMAND_TRANSMUTE_START, + COMMAND_TRANSMUTE_END, + COMMAND_CALL_TRANSMUTE, + COMMAND_MOVE_ITEM, + COMMAND_ABORT +}; + +class auto_transmute_menu : public d2_tweaks::ui::menu { + d2_tweaks::common::asset* m_buttons_img; + d2_tweaks::ui::controls::button* m_auto_transmute_btn; + +public: + auto_transmute_menu() { + menu::set_enabled(true); + menu::set_visible(true); + + //load_xml("d2tweaks\\interfaces\\autotransmute.xml"); + if (DLLBASE_D2EXPRES != 0) + load_xml("d2tweaks\\interface_d2expres\\autotransmute.xml"); + if (DLLBASE_SGD2FREERES != 0) + load_xml("d2tweaks\\interface_sgd2freeres\\autotransmute.xml"); + if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0) + load_xml("d2tweaks\\interface_vanilla\\autotransmute.xml"); + + m_buttons_img = singleton::instance().get_mpq_file("d2tweaks\\assets\\buttons", d2_tweaks::common::MPQ_FILE_TYPE_DC6); + m_auto_transmute_btn = get_button("m_auto_transmute_btn", std::bind(&auto_transmute_menu::auto_transmute_click, this)); + } + + void draw() override { + if (m_bToggleTransmute) { + //diablo2::d2_client::print_chat(L"AUTO TRANSMUTE ON", 1); + m_auto_transmute_btn->set_current_frame(5); // index of frame in buttons.dc6 + } + else + { + //diablo2::d2_client::print_chat(L"AUTO TRANSMUTE OFF", 2); + m_auto_transmute_btn->set_current_frame(4); + } + + if (!should_draw()) { + m_auto_transmute_btn->set_enabled(false); + m_auto_transmute_btn->set_visible(false); + return; + } + + m_auto_transmute_btn->set_enabled(true); + m_auto_transmute_btn->set_visible(true); + + menu::draw(); + } +private: + static bool should_draw() { + return diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INVENTORY) || + diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH) || + diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE) || + diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP); + } + + d2_tweaks::ui::controls::button* get_button(const std::string& name, const std::function& onClick) { + auto result = static_cast(get_control(name)); + + result->set_on_click(onClick); + + result->set_enabled(false); + result->set_visible(false); + + return result; + } + + void auto_transmute_click() { + send_request(0x00); + } + + void send_request(const uint8_t page) { + static d2_tweaks::common::transmute_info_cs request_packet_cs; + + m_bToggleTransmute ^= true; + + //request_packet_cs.transmute_start_flag = m_bToggleTransmute; + //request_packet_cs.command = COMMAND_TRANSMUTE_START; + //diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + + if (m_bToggleTransmute) { + diablo2::d2_client::print_chat(L"AUTO TRANSMUTE ON", 1); + } + else + { + diablo2::d2_client::print_chat(L"AUTO TRANSMUTE OFF", 2); + } + } +}; + +void __fastcall hook_play_sound(uint32_t soundId, uint32_t* unit, const uint32_t ticks, const BOOL prePick, const uint32_t cache) { + if (m_bToggleTransmute) { + if (soundId == 0xB) + soundId = 0; + } + + fn_hook_play_sound(soundId, unit, ticks, prePick, cache); + return; +} + +void __stdcall hook_game_end () { + m_game_init_done = false; + m_bToggleTransmute = false; + return; +} + +__declspec (naked) void hook_game_end_asm() { + __asm { + pushad; + pushfd; + call hook_game_end; + popfd; + popad; + + jmp [fn_hook_game_end]; + } +} + +void d2_tweaks::client::modules::transmute::init_early() { + +} + +void d2_tweaks::client::modules::transmute::init() { + char szDir[MAX_PATH]; + char szPath[MAX_PATH]; + uint32_t dwLenght = 0; + const char szConfig[] = "d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, szDir); + sprintf_s(szPath, MAX_PATH, "%s\\%s", szDir, szConfig); + CIni config(szPath); + + //const char szRecipes[] = "d2tweaks.recipes.ini"; + //snprintf(szPath, MAX_PATH, "%s\\%s", szDir, szRecipes); + //CIni recipes(szPath); + // key enlarging the buffer unless we make sure all section names + // are loaded. + //DWORD dwBufSize = 1024; + //char* pszBuffer = new char[dwBufSize]; + //DWORD dwCopied = recipes.GetSectionNames(pszBuffer, dwBufSize - 1); + //while (dwCopied + 128 >= dwBufSize) + //{ + // dwBufSize += 1024; + // delete[] pszBuffer; + // pszBuffer = new char[dwBufSize]; + // dwCopied = recipes.GetSectionNames(pszBuffer, dwBufSize - 1); + //} + //for (char* pSection = pszBuffer; pSection[0]; pSection = &pSection[strlen(pSection) + 1]) + //{ + //} + + if (config.GetInt("modules", "AutoTransmute", 1) != FALSE) { + m_nDelayFrames = config.GetUInt("AutoTransmute", "DelayInFrames", 10); + m_nTransmuteSound = config.GetUInt("AutoTransmute", "EnableTransmuteSound", 0); + + for (uint32_t i = 0; i < m_nCountKeys; i++) { + sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemList%d", i + 1); + dwLenght = config.GetString("AutoTransmute", m_acBuffer, m_aacItemList[i], MAX_STRING_LENGHT - 1); + if (dwLenght != 0) { + lstrcat(m_acItemListAll, m_aacItemList[i]); + lstrcat(m_acItemListAll, "|"); + } + } + + for (uint32_t i = 0; i < m_nCountKeys; i++) { + sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemTypeList%d", i + 1); + dwLenght = config.GetString("AutoTransmute", m_acBuffer, m_aacItemTypes[i], MAX_STRING_LENGHT - 1); + if (dwLenght != 0) { + lstrcat(m_acItemTypesAll, m_aacItemTypes[i]); + lstrcat(m_acItemTypesAll, "|"); + } + } + +/////// Parse ItemCode + dwLenght = lstrlen(m_acItemListAll); + memcpy(m_acItemListAllTemp, m_acItemListAll, dwLenght + 1); + // + char* token_string_itemcode = strtok(m_acItemListAllTemp, " ,|"); + while (token_string_itemcode) + { + m_nCountItemListAll++; + token_string_itemcode = strtok(NULL, " ,|"); + } + + m_stItemList = (item_code*)malloc(m_nCountItemListAll * sizeof(item_code)); + memset(m_stItemList, 0, m_nCountItemListAll * sizeof(item_code)); + + memcpy(m_acItemListAllTemp, m_acItemListAll, dwLenght + 1); + token_string_itemcode = strtok(m_acItemListAllTemp, " ,|"); + + for (uint32_t i = 0; token_string_itemcode != 0; i++) + { + uint32_t cur_string_length = lstrlen(token_string_itemcode); + m_stItemList[i].code0 = token_string_itemcode[0]; + m_stItemList[i].code1 = token_string_itemcode[1]; + m_stItemList[i].code2 = token_string_itemcode[2]; + + if (token_string_itemcode[3] == ':') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = FALSE; + } + + if (cur_string_length <= 13) { //for example jew:123456789 + // p = 4 is first digit after ':' + for (uint32_t p = 4; p <= cur_string_length; p++) { + if (token_string_itemcode[p] == 0) { + break; + } + if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') { + m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = TRUE; + } + } + } + } + + if (token_string_itemcode[3] == '-') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = TRUE; + } + + if (cur_string_length <= 13) { //for example jew:123456789 + // p = 4 is first digit after '-' + for (uint32_t p = 4; p <= cur_string_length; p++) { + if (token_string_itemcode[p] == 0) { + break; + } + if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') { + m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = FALSE; + } + } + } + } + + if (token_string_itemcode[3] != '-' && token_string_itemcode[3] != ':') + { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = TRUE; + } + } + + token_string_itemcode = strtok(NULL, " ,|"); + } + + +/////// Parse ItemType + dwLenght = lstrlen(m_acItemTypesAll); + memcpy(m_acItemTypesAllTemp, m_acItemTypesAll, dwLenght + 1); + char* token_string_itemtype_code = strtok(m_acItemTypesAllTemp, ",|"); + while (token_string_itemtype_code) + { + m_nCountItemTypesAll++; + token_string_itemtype_code = strtok(NULL, ",|"); + } + + m_stItemTypes = (item_type*)malloc(m_nCountItemTypesAll * sizeof(item_type)); + memset(m_stItemTypes, 0, m_nCountItemTypesAll * sizeof(item_type)); + + memcpy(m_acItemTypesAllTemp, m_acItemTypesAll, dwLenght + 1); + token_string_itemtype_code = strtok(m_acItemTypesAllTemp, ",|"); + for (uint32_t i = 0; token_string_itemtype_code != 0; i++) + { + uint32_t cur_itemtypes_string_length = lstrlen(token_string_itemtype_code); + //m_stItemTypes[i].type0 = token_string_itemtype_code[0]; + //m_stItemTypes[i].type1 = token_string_itemtype_code[1]; + //m_stItemTypes[i].type2 = token_string_itemtype_code[2]; + //m_stItemTypes[i].type3 = token_string_itemtype_code[3]; + + m_stItemTypes[i].dwtype = *(DWORD*)token_string_itemtype_code; + + if (token_string_itemtype_code[4] == ':') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = FALSE; + } + + if (cur_itemtypes_string_length <= 14) { //for example tors:123456789 + // p = 5 is first digit after ':' + for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) { + if (token_string_itemtype_code[p] == 0) { + break; + } + if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') { + m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = TRUE; + } + } + } + } + + if (token_string_itemtype_code[4] == '-') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = TRUE; + } + + if (cur_itemtypes_string_length <= 14) { //for example armo:123456789 + // p = 5 is first digit after '-' + for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) { + if (token_string_itemtype_code[p] == 0) { + break; + } + if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') { + m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = FALSE; + } + } + } + } + + if (token_string_itemtype_code[4] != '-' && token_string_itemtype_code[4] != ':') + { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = TRUE; + } + } + + token_string_itemtype_code = strtok(NULL, ",|"); + } + + if (m_nTransmuteSound == false) { + hooking::hook(diablo2::d2_client::get_base() + 0xB5820, hook_play_sound, reinterpret_cast(&fn_hook_play_sound)); + } + + hooking::hook(diablo2::d2_client::get_base() + 0xB528, hook_game_end_asm, reinterpret_cast(&fn_hook_game_end)); + + singleton::instance().add_menu(new auto_transmute_menu()); + singleton::instance().register_tick_handler(this); + singleton::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_TRANSMUTE, this); + } +} + + +void d2_tweaks::client::modules::transmute::tick() { + const auto unit = diablo2::d2_client::get_local_player(); + + if (m_game_init_done == false) { + m_game_init_done = true; + m_bToggleTransmute = false; + } + + if (!unit) + return; + + if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + return; + + if (!m_bToggleTransmute) + return; + + if (m_nCountFrames > m_nDelayFrames) + { + m_nCountFrames = 0; + + auto item = diablo2::d2_common::get_first_inventory_item(unit->inventory); + + for (item; item != 0; item = diablo2::d2_common::get_next_inventory_item(item)) { + auto currentpage = diablo2::d2_common::get_item_page(item); + if (currentpage == PAGE_INVENTORY) { + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + if (*(DWORD*)record->string_code == *(DWORD*)"box ") { + continue; + } + + const auto itemtype_record = diablo2::d2_common::get_item_type_record(record->type); + auto itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv1); + auto itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv2); + + uint32_t quality = diablo2::d2_common::get_item_quality(item); + // + char arr_itemtype_codestr_equivstr[20][5] = { 0 }; + + // itemtype code + *(DWORD*)arr_itemtype_codestr_equivstr[0] = *(DWORD*)itemtype_record->code; + // index second code in array + uint32_t index_arr_itemtype = 1; + + // equiv1 + if (itemtype_record_equiv1) { + if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) { + // equiv1 + *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code; + index_arr_itemtype++; + // eqiv1 + for (index_arr_itemtype; itemtype_record_equiv1->equiv1 != 0; index_arr_itemtype++) { + // + itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv1->equiv1); + if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) { + *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code; + } + else break; + } + } + } + + // equiv2 + if (itemtype_record_equiv2) { + if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) { + // equiv2 + *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code; + index_arr_itemtype++; + // eqiv2 + for (index_arr_itemtype; itemtype_record_equiv2->equiv2 != 0; index_arr_itemtype++) { + // + itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv2->equiv2); + if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) { + *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code; + } + else break; + } + } + } + + for (uint32_t i = 0; i < m_nCountItemTypesAll; i++) + { + for (uint32_t count = 0; count < index_arr_itemtype; count++) + { + if (*(DWORD*)arr_itemtype_codestr_equivstr[count] == (DWORD)m_stItemTypes[i].dwtype) + { + if (m_stItemTypes[i].qualityinclude[quality] == TRUE) + { + static d2_tweaks::common::transmute_info_cs request_packet_cs; + request_packet_cs.command = COMMAND_MOVE_ITEM; + request_packet_cs.item_guid = item->guid; + request_packet_cs.target_page = PAGE_CUBE; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + goto L1; + } + } + } + } + + for (uint32_t i = 0; i < m_nCountItemListAll; i++) + { + if (record->string_code[0] == m_stItemList[i].code0 && + record->string_code[1] == m_stItemList[i].code1 && + record->string_code[2] == m_stItemList[i].code2) + { + if (m_stItemList[i].qualityinclude[quality] == TRUE) + { + static d2_tweaks::common::transmute_info_cs request_packet_cs; + request_packet_cs.command = COMMAND_MOVE_ITEM; + request_packet_cs.item_guid = item->guid; + request_packet_cs.target_page = PAGE_CUBE; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + goto L1; + } + } + } + } + } + } + +L1:; + m_nCountFrames++; +} + + +void d2_tweaks::client::modules::transmute::handle_packet(common::packet_header* packet) { + static auto& instance = singleton::instance(); + const auto income_packet_sc = static_cast(packet); + static d2_tweaks::common::transmute_info_cs request_packet_cs; + + if (income_packet_sc->command == COMMAND_MOVE_ITEM) { + const auto item = instance.get_client_unit(0x04, income_packet_sc->item_guid); //0x03 -> 0x04 - item + + if (item == nullptr) + return; + + const auto player = diablo2::d2_client::get_local_player(); + + //Last parameter is some boolean + const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, income_packet_sc->target_page, diablo2::d2_client::is_lod()); + + item->item_data->page = income_packet_sc->target_page; + + diablo2::d2_common::inv_add_item(player->inventory, item, income_packet_sc->tx, income_packet_sc->ty, inventoryIndex, true, item->item_data->page); + diablo2::d2_common::inv_update_item(player->inventory, item, true); + + // send 'transmute' command + request_packet_cs.command = COMMAND_CALL_TRANSMUTE; + diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs); + + //diablo2::d2_client::send_to_server_7(0x4F, 0x18, 0, 0); + } + + if (income_packet_sc->command == COMMAND_ABORT) { + m_bToggleTransmute = false; + diablo2::d2_client::print_chat(L"WRONG RECIPE, AUTO TRANSMUTE OFF", 2); + } +} diff --git a/src/d2tweaks/common/asset_manager.cpp b/src/d2tweaks/common/asset_manager.cpp new file mode 100644 index 0000000..bd18ec6 --- /dev/null +++ b/src/d2tweaks/common/asset_manager.cpp @@ -0,0 +1,79 @@ +#include + +#include + +#include + +#include +#include + +#include + +static void* g_d2_tweaks_mpq = nullptr; + +static int32_t(__stdcall* g_reload_original)(); + +d2_tweaks::common::asset_manager::asset_manager(token) {} + +void d2_tweaks::common::asset_manager::init() { + hooking::hook(diablo2::d2_client::get_base() + 0x5E370, reload, reinterpret_cast(&g_reload_original)); + + g_d2_tweaks_mpq = diablo2::d2_win::load_mpq("d2tweaks.dll", "d2tweaks.mpq", "D2TWEAKS", 6000); + + //if (g_d2_tweaks_mpq == nullptr) { + // spdlog::critical("Cannot find d2tweaks.mpq!"); + // exit(-1); + //} + + spdlog::debug("d2tweaks.mpq: {0}", g_d2_tweaks_mpq); +} + +int32_t d2_tweaks::common::asset_manager::reload() { + static auto& instance = singleton::instance(); + const auto result = g_reload_original(); + + spdlog::info("Reloading image assets!"); + + for (const auto& it : instance.m_assets) { + if (it.second->get_type() != MPQ_FILE_TYPE_DC6 && + it.second->get_type() != MPQ_FILE_TYPE_DCC) + continue; + + spdlog::info("Reloading {0}...", it.second->get_path()); + + if (it.second->get()) { + const auto ures = diablo2::d2_client::unload_gfx_resource(static_cast(it.second->get())); + spdlog::info("Unloaded: {0}", ures); + } + + const auto newData = instance.load_asset_data(it.second->get_path(), it.second->get_type()); + it.second->update(newData); + spdlog::info("Done! {0}", newData); + } + + return result; +} + +d2_tweaks::common::asset* d2_tweaks::common::asset_manager::get_mpq_file(const std::string& path, mpq_file_type_t type) { + if (path.empty()) + return nullptr; + + const auto it = m_assets.find(path); + + if (it != m_assets.end()) + return it->second; + + const auto asset = new common::asset(path, load_asset_data(path, type), type); + m_assets[path] = asset; + + return asset; +} + +void* d2_tweaks::common::asset_manager::load_asset_data(const std::string& path, mpq_file_type_t type) { + switch (type) { + case MPQ_FILE_TYPE_DC6: + return diablo2::d2_client::load_gfx_resource(const_cast(path.c_str())); + default: + return nullptr; + } +} diff --git a/src/d2tweaks/common/common.cpp b/src/d2tweaks/common/common.cpp new file mode 100644 index 0000000..4ee714d --- /dev/null +++ b/src/d2tweaks/common/common.cpp @@ -0,0 +1,181 @@ +#include + +#include + +#include +#include + +#include +#include + +static int32_t(__fastcall* g_get_packet_size_client_to_server)(d2_tweaks::common::packet_header* data, size_t size, size_t* sizeOut); +static char(__fastcall* g_get_packet_size_server_to_client)(d2_tweaks::common::packet_header* data, size_t size, size_t* outSize); + +static int32_t(__stdcall* g_net_send_to_server)(int32_t queue, d2_tweaks::common::packet_header* packet, size_t size); +static int32_t(__stdcall* g_net_send_to_client)(int32_t queue, int32_t clientId, d2_tweaks::common::packet_header* packet, size_t size); + +static int32_t g_ebp_send_to_server; +static int32_t g_ebp_send_to_client; +static int32_t __stdcall net_send_to_server(int32_t queue, d2_tweaks::common::packet_header* packet, size_t size) { + __asm { + push [ebp + 4] + pop [g_ebp_send_to_server] + } + + spdlog::debug("[d2net SEND C >>> S] Queue {} Packet {} Size {} CallFrom {}", queue, (void*)packet->d2_packet_type, size, (void*)g_ebp_send_to_server); + //do something + return g_net_send_to_server(queue, packet, size); +} + +static int32_t __stdcall net_send_to_client(int32_t queue, int32_t clientId, d2_tweaks::common::packet_header* packet, size_t size) { + __asm { + push [ebp+4] + pop [g_ebp_send_to_client] + } + + spdlog::error("[d2net SEND S >>> C] Queue {} Packet {} Size {} ClientID {} CallFrom {}", queue, (void*)packet->d2_packet_type, size, clientId, (void*)g_ebp_send_to_client); + //do something + return g_net_send_to_client(queue, clientId, packet, size); +} + +static int32_t __fastcall get_packet_size_client_to_server(d2_tweaks::common::packet_header* data, size_t size, size_t* sizeOut) { + static d2_tweaks::common::packet_header dummy; + static auto& instance = singleton::instance(); + + if (data->d2_packet_type == dummy.d2_packet_type) { + size_t packetSize; + + if (!instance.get_packet_size_cs(data, packetSize)) + return g_get_packet_size_client_to_server(data, size, sizeOut); + + *sizeOut = packetSize; + return packetSize; + } + + return g_get_packet_size_client_to_server(data, size, sizeOut); +} + +static char __fastcall get_packet_size_server_to_client(d2_tweaks::common::packet_header* data, size_t size, size_t* sizeOut) { + static d2_tweaks::common::packet_header dummy; + static auto& instance = singleton::instance(); + + if (data->d2_packet_type == dummy.d2_packet_type) { + size_t packetSize; + + if (!instance.get_packet_size_sc(data, packetSize)) + return g_get_packet_size_server_to_client(data, size, sizeOut); + + *sizeOut = packetSize; + return static_cast(packetSize); + } + + return g_get_packet_size_server_to_client(data, size, sizeOut); +} + +d2_tweaks::common::common::common(token) {} + +void d2_tweaks::common::common::init() { + singleton::instance().init(); + + hooking::hook(reinterpret_cast(diablo2::d2_net::get_base() + 0x1B60), get_packet_size_server_to_client, reinterpret_cast(&g_get_packet_size_server_to_client)); + hooking::hook(reinterpret_cast(diablo2::d2_net::get_base() + 0x1E60), get_packet_size_client_to_server, reinterpret_cast(&g_get_packet_size_client_to_server)); + +#ifndef NDEBUG + //hooking::hook(reinterpret_cast(diablo2::d2_net::get_base() + 0x1760), net_send_to_server, reinterpret_cast(&g_net_send_to_server)); + //hooking::hook(reinterpret_cast(diablo2::d2_net::get_base() + 0x22B0), net_send_to_client, reinterpret_cast(&g_net_send_to_client)); +#endif +} + +bool d2_tweaks::common::common::get_packet_size_cs(packet_header* packet, size_t& size) { + switch (packet->message_type) { + case MESSAGE_TYPE_ITEM_MOVE: + { + size = sizeof item_move_cs; + return true; + } + case MESSAGE_TYPE_INVENTORY_SORT: + { + size = sizeof inventory_sort_cs; + return true; + } + case MESSAGE_TYPE_DAMAGE_INFO: + { + size = sizeof damage_info_cs; + return true; + } + case MESSAGE_TYPE_GOLD_PICKUP_INFO: + { + size = sizeof gold_pickup_info_cs; + return true; + } + case MESSAGE_TYPE_ITEM_PICKUP_INFO: + { + size = sizeof item_pickup_info_cs; + return true; + } + case MESSAGE_TYPE_ITEM_DROPPED_INFO: + { + size = sizeof item_dropped_info_cs; + return true; + } + case MESSAGE_TYPE_TRANSMUTE: + { + size = sizeof transmute_info_cs; + return true; + } + case MESSAGE_TYPE_TRADER_UPDATE: + { + size = sizeof trader_update_cs; + return true; + } + default: + return false; + } +} + +bool d2_tweaks::common::common::get_packet_size_sc(packet_header* packet, size_t& size) { + switch (packet->message_type) { + case MESSAGE_TYPE_ITEM_MOVE: + { + size = sizeof item_move_sc; + return true; + } + case MESSAGE_TYPE_INVENTORY_SORT: + { + size = sizeof inventory_sort_sc; + return true; + } + case MESSAGE_TYPE_DAMAGE_INFO: + { + size = sizeof damage_info_sc; + return true; + } + case MESSAGE_TYPE_GOLD_PICKUP_INFO: + { + size = sizeof gold_pickup_info_sc; + return true; + } + case MESSAGE_TYPE_ITEM_PICKUP_INFO: + { + size = sizeof item_pickup_info_sc; + return true; + } + case MESSAGE_TYPE_ITEM_DROPPED_INFO: + { + size = sizeof item_dropped_info_sc; + return true; + } + case MESSAGE_TYPE_TRANSMUTE: + { + size = sizeof transmute_info_sc; + return true; + } + case MESSAGE_TYPE_TRADER_UPDATE: + { + size = sizeof trader_update_sc; + return true; + } + default: + return false; + } +} diff --git a/src/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.cpp b/src/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.cpp new file mode 100644 index 0000000..7868d09 --- /dev/null +++ b/src/d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.cpp @@ -0,0 +1,105 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_INIT(auto_gold_pickup) + +void d2_tweaks::server::modules::auto_gold_pickup::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "AutoGoldPickup", 1, acPathToIni) != FALSE) { + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_GOLD_PICKUP_INFO, this); + //singleton::instance().register_tick_handler(this); + } +} + + +bool d2_tweaks::server::modules::auto_gold_pickup::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) { + const auto income_packet_cs = static_cast(packet); + static auto& instance = singleton::instance(); + const auto item = instance.get_server_unit(game, income_packet_cs->item_guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); //0x4 = item + + if (item == nullptr) + return true; //block further packet processing + + d2_tweaks::server::modules::auto_gold_pickup::au_pickup_gold(game, player, item); + + return true; +} + + +bool d2_tweaks::server::modules::auto_gold_pickup::au_pickup_gold(diablo2::structures::game* game, diablo2::structures::unit* unit, diablo2::structures::unit* item) +{ + static common::gold_pickup_info_sc packet; + + const auto currentGold = diablo2::d2_common::get_stat(unit, diablo2::UNIT_STAT_GOLD, 0); + const auto goldToPickup = diablo2::d2_common::get_stat(item, diablo2::UNIT_STAT_GOLD, 0); + const auto maxGold = diablo2::d2_common::get_maximum_character_gold(unit); + + if (currentGold + goldToPickup > maxGold) + return FALSE; + + diablo2::d2_game::pickup_gold_pile(game, unit, item); + packet.gold = goldToPickup; + + singleton::instance().send_packet(unit->player_data->net_client, &packet, sizeof packet); + return TRUE; +} + + +void d2_tweaks::server::modules::auto_gold_pickup::tick(diablo2::structures::game* game, + diablo2::structures::unit* unit) { + //static common::gold_pickup_info_sc packet; + //static auto& instance = singleton::instance(); + //if (!game || !unit) + // return; + + //if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + // return; + + //const auto room = diablo2::d2_common::get_room_from_unit(unit); + + //if (!room) + // return; + + //for (auto item = room->unit; item; item = item->prev_unit_in_room) { + // if (!item) + // continue; + + // if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + // continue; + + // auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + // if (!record) + // continue; + + // const auto distance = diablo2::d2_common::get_distance_between_units(unit, item); + // if (distance > g_distance) + // continue; + + // // , + // if (record->string_code[0] == 'g' && + // record->string_code[1] == 'l' && + // record->string_code[2] == 'd') + // { + // d2_tweaks::server::modules::auto_gold_pickup::au_pickup_gold(game, unit, item); + // break; + // } + + // continue; + //} +} + diff --git a/src/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.cpp b/src/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.cpp new file mode 100644 index 0000000..658a600 --- /dev/null +++ b/src/d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.cpp @@ -0,0 +1,181 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_INIT(auto_item_pickup) + +void d2_tweaks::server::modules::auto_item_pickup::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "AutoItemPickup", 1, acPathToIni) != FALSE) { + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_ITEM_PICKUP_INFO, this); + singleton::instance().register_tick_handler(this); + } +} + +void d2_tweaks::server::modules::auto_item_pickup::tick(diablo2::structures::game* game, + diablo2::structures::unit* unit) { + //static common::item_pickup_info_sc packet; + //static auto& instance = singleton::instance(); + //if (!game || !unit) + // return; + + //if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + // return; + + //const auto room = diablo2::d2_common::get_room_from_unit(unit); + + //if (!room) + // return; + + + //for (auto item = room->unit; item; item = item->prev_unit_in_room) { + // if (!item) + // continue; + + // if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + // continue; + + // const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + // if (!record) + // continue; + + // const auto distance = diablo2::d2_common::get_distance_between_units(unit, item); + // if (distance > g_distance) + // continue; + + // const auto itemtype_record = diablo2::d2_common::get_item_type_record(record->type); + // auto itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv1); + // auto itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv2); + + // uint32_t quality = diablo2::d2_common::get_item_quality(item); + + // // + // char arr_itemtype_codestr_equivstr[20][5] = { 0 }; + + // // itemtype code + // *(DWORD*)arr_itemtype_codestr_equivstr[0] = *(DWORD*)itemtype_record->code; + // // index second code in array + // uint32_t index_arr_itemtype = 1; + + // if (itemtype_record_equiv1) { + // if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) { + // // equiv1 + // *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code; + // index_arr_itemtype++; + // // eqiv1 + // for (index_arr_itemtype; itemtype_record_equiv1->equiv1 != 0; index_arr_itemtype++) { + // // + // itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv1->equiv1); + // if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) { + // *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code; + // } + // else break; + // } + // } + // } + + + // if (itemtype_record_equiv2) { + // if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) { + // // equiv1 + // *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code; + // index_arr_itemtype++; + // // eqiv1 + // for (index_arr_itemtype; itemtype_record_equiv2->equiv2 != 0; index_arr_itemtype++) { + // // + // itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv2->equiv2); + // if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) { + // *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code; + // } + // else break; + // } + // } + // } + + + // for (uint32_t i = 0; i < g_pickup_count_itemtype_code; i++) + // { + // for (uint32_t count = 0; count < index_arr_itemtype; count++) + // { + // if (*(DWORD*)arr_itemtype_codestr_equivstr[count] == (DWORD)g_pickup_itemtype_code_st[i].dwtype) + // { + // if (g_pickup_itemtype_code_st[i].qualityinclude[quality] == TRUE) + // { + // d2_tweaks::server::modules::auto_item_pickup::au_pickup_item(game, unit, item); + // goto L1; + // } + // } + // } + // } + + + // for (uint32_t i = 0; i < g_pickup_count_all_items; i++) + // { + // if (record->string_code[0] == g_pickup_itemcode_st[i].code0 && + // record->string_code[1] == g_pickup_itemcode_st[i].code1 && + // record->string_code[2] == g_pickup_itemcode_st[i].code2) + // { + // if (g_pickup_itemcode_st[i].qualityinclude[quality] == TRUE) + // { + // d2_tweaks::server::modules::auto_item_pickup::au_pickup_item(game, unit, item); + // } + // } + // } + + // L1: + // continue; + //} + + //g_tick_between_item_pickup++; + ////spdlog::info("current {0}", g_tick_between_item_pickup); +} + + +bool d2_tweaks::server::modules::auto_item_pickup::au_pickup_item(diablo2::structures::game* game, diablo2::structures::unit* unit, uint32_t guid) +{ + static common::item_pickup_info_sc packet; + + uint32_t ptrNull = 0; + //if (g_tick_between_item_pickup >= 25) { + // true - if item picked up, false - if inventory full + if (diablo2::d2_game::pickup_item(game, unit, guid, &ptrNull) != true) + { + //packet.inventory_full = true; + //singleton::instance().send_packet(unit->player_data->net_client, &packet, sizeof packet); + } + //} + + return true; +} + + +bool d2_tweaks::server::modules::auto_item_pickup::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) { + const auto income_packet_cs = static_cast(packet); + static auto& instance = singleton::instance(); + + //const auto item = instance.get_server_unit(game, income_packet_cs->item_guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); //0x4 = item + //if (item == nullptr) + // return true; //block further packet processing + + d2_tweaks::server::modules::auto_item_pickup::au_pickup_item(game, player, income_packet_cs->item_guid); + + return true; +} \ No newline at end of file diff --git a/src/d2tweaks/server/modules/autosort/autosort_server.cpp b/src/d2tweaks/server/modules/autosort/autosort_server.cpp new file mode 100644 index 0000000..bb54685 --- /dev/null +++ b/src/d2tweaks/server/modules/autosort/autosort_server.cpp @@ -0,0 +1,360 @@ +#include + +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_INIT(autosort) + +struct backup_item { + diablo2::structures::unit* item; + uint8_t x; + uint8_t y; + + backup_item() : item(nullptr), x(0), y(0) {} +}; + +struct packed_area { + uint8_t x; + uint8_t y; + uint8_t w; + uint8_t h; +}; + +// Define the inventory zone +int iminValidX = 0; +int imaxValidX = 15; +int iminValidY = 0; +int imaxValidY = 12; + +// Define the charm zone +int cminValidX = 0; +int cmaxValidX = 15; +int cminValidY = 12; +int cmaxValidY = 15; + +void d2_tweaks::server::modules::autosort::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "Autosort", 1, acPathToIni) != FALSE) { + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_INVENTORY_SORT, this); + } +} + +bool d2_tweaks::server::modules::autosort::handle_packet(diablo2::structures::game* game, + diablo2::structures::unit* player, common::packet_header* packet) { + sort(game, player, static_cast(packet)->page); + return true; +} + +bool d2_tweaks::server::modules::autosort::sort(diablo2::structures::game* game, diablo2::structures::unit* player, uint8_t page) { + static common::inventory_sort_sc packet; + static auto& instance = singleton::instance(); + + if (player == nullptr) + return false; + + const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, page, game->item_format == 101); + char data[0x18]; + diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data); + + const auto inventoryWidth = static_cast(data[0]); + const auto inventoryHeight = static_cast(data[1]); + + std::vector areas{ {0,0, static_cast(inventoryWidth), static_cast(inventoryHeight)} }; + std::vector items; + std::vector charms; + std::vector backup_items; + std::unordered_map> items_typed; + std::unordered_map> charms_typed; + uint32_t occupied_cells = 0; + + for (auto item = player->inventory->first_item; item != nullptr; item = item->item_data->pt_next_item) { + if (item->item_data->page != page) + continue; + + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + const auto record_type = diablo2::d2_common::get_item_type_record(record->type); + bool is_charm = record_type->charm; + + backup_item backup; + backup.item = item; + backup.x = item->path->x; + backup.y = item->path->y; + + backup_items.push_back(backup); + + // only check charmzone grid for inventory page 0 + // Check if item's coordinates are within the valid range + if (is_charm) { + // Coordinates are within the valid range, add the item to items vector + charms_typed[item->data_record_index].push_back(item); + charms.push_back(item); + occupied_cells += record->inv_height * record->inv_width; + } + else { + // Coordinates are within the valid range, add the item to items vector + items_typed[item->data_record_index].push_back(item); + items.push_back(item); + occupied_cells += record->inv_height * record->inv_width; + } + + spdlog::info("--------------------------------"); + spdlog::info("Item Type: {0}", record->type); + spdlog::info("Item Type 2: {0}", record->type2); + spdlog::info("Occupied Cells: {0}", occupied_cells); + spdlog::info("x: {0}", backup.x); + spdlog::info("y: {0}", backup.y); + spdlog::info("is_charm: {0}\n\n", is_charm); + } + + if (occupied_cells > inventoryHeight* inventoryWidth) { + //should never be happen in normal cases + spdlog::warn("occupied_cells > inventoryHeight* inventoryWidth"); + return false; + } + + // Remove all items from inventory + for (auto item : items) + diablo2::d2_common::inv_remove_item(player->inventory, item); + for (auto item : charms) + diablo2::d2_common::inv_remove_item(player->inventory, item); + + const auto itemsCount = items.size(); + const auto charmsCount = charms.size(); + + + if (itemsCount == 0) + return true; //there's nothing to sort + + + auto success = NULL; + + if (itemsCount > 0) { + //sort items by height*width + for (size_t i = 0; i < itemsCount - 1; i++) { + auto swapped = false; + for (size_t j = 0; j < itemsCount - i - 1; j++) { + const auto record1 = diablo2::d2_common::get_item_record(items[j]->data_record_index); + const auto record2 = diablo2::d2_common::get_item_record(items[j + 1]->data_record_index); + + if (record1->inv_height * record1->inv_width > record2->inv_height * record2->inv_width) + continue; + + const auto temp = items[j]; + items[j] = items[j + 1]; + items[j + 1] = temp; + swapped = true; + } + + if (swapped == false) + break; + } + success = TRUE; + for (auto sorted_item : items) { + for (auto& item : items_typed[sorted_item->data_record_index]) { //iterate through all items of the same type for grouping + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + uint32_t tx, ty; + + if (!find_free_space(player->inventory, item, inventoryIndex, page, tx, ty, false)) { + success = false; + break; + } + + if (!diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, page)) { + success = false; + break; + } + + if (diablo2::d2_common::inv_update_item(player->inventory, item, false)) + continue; + + success = false; + break; + } + + if (!success) + break; + + items_typed.erase(sorted_item->data_record_index); + } + + } + + if (charmsCount > 0) { + //sort charms by height*width + for (size_t i = 0; i < charmsCount - 1; i++) { + auto swapped = false; + for (size_t j = 0; j < charmsCount - i - 1; j++) { + const auto record1 = diablo2::d2_common::get_item_record(charms[j]->data_record_index); + const auto record2 = diablo2::d2_common::get_item_record(charms[j + 1]->data_record_index); + + if (record1->inv_height * record1->inv_width > record2->inv_height * record2->inv_width) + continue; + + const auto temp = charms[j]; + charms[j] = charms[j + 1]; + charms[j + 1] = temp; + swapped = true; + } + + if (swapped == false) + break; + } + success = TRUE; + for (auto sorted_item : charms) { + for (auto& item : charms_typed[sorted_item->data_record_index]) { //iterate through all items of the same type for grouping + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + uint32_t tx, ty; + + if (!find_free_space(player->inventory, item, inventoryIndex, page, tx, ty, true)) { + success = false; + break; + } + + if (!diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, page)) { + success = false; + break; + } + + if (diablo2::d2_common::inv_update_item(player->inventory, item, false)) + continue; + + success = false; + break; + } + + if (!success) + break; + + charms_typed.erase(sorted_item->data_record_index); + } + } + + if (success) { + while (items.size() > 0) { + packet.page = page; + packet.tx = items.back()->path->x; + packet.ty = items.back()->path->y; + packet.guid = items.back()->guid; + + items.pop_back(); + + instance.send_packet(player->player_data->net_client, &packet, sizeof packet); + } + + while (charms.size() > 0) { + packet.page = page; + packet.tx = charms.back()->path->x; + packet.ty = charms.back()->path->y; + packet.guid = charms.back()->guid; + + charms.pop_back(); + + instance.send_packet(player->player_data->net_client, &packet, sizeof packet); + } + + diablo2::d2_game::update_inventory_items(game, player); + return true; + } + + //sorting failed, remove all items and re-insert them at old coords + + for (auto& backup_item : backup_items) { + const auto item = backup_item.item; + + diablo2::d2_common::inv_remove_item(player->inventory, item); + } + + while (backup_items.size() > 0) { + const auto backup = backup_items.back(); + const auto item = backup.item; + const auto tx = backup.x; + const auto ty = backup.y; + + packet.page = page; + packet.tx = tx; + packet.ty = ty; + packet.guid = item->guid; + + diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, page); + diablo2::d2_common::inv_update_item(player->inventory, item, false); + + backup_items.pop_back(); + + instance.send_packet(player->player_data->net_client, &packet, sizeof packet); + } + + diablo2::d2_game::update_inventory_items(game, player); + + return false; +} + +bool d2_tweaks::server::modules::autosort::find_free_space(diablo2::structures::inventory* inv, + diablo2::structures::unit* item, + int32_t inventoryIndex, char page, uint32_t& x, + uint32_t& y, bool isCharmZone) { + char data[0x18]; + diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data); + + // inventory coords + const auto mx = static_cast(data[0]); + const auto my = static_cast(data[1]); + + const int minValidX = isCharmZone ? cminValidX : iminValidX; + const int maxValidX = isCharmZone ? cmaxValidX : imaxValidX; + const int minValidY = isCharmZone ? cminValidY : iminValidY; + const int maxValidY = isCharmZone ? cmaxValidY : imaxValidY; + + // Get the item record to access the item size + const auto record = diablo2::d2_common::get_item_record(item->data_record_index); + + // Update the maximum valid Y coordinate for the inventory zone + const int inventoryMaxValidY = isCharmZone ? maxValidY : 11; + + if (page == 0x03 || page == 0x04) { + for (x = 0; x < mx; x++) { + for (y = 0; y < my; y++) { + diablo2::structures::unit* blockingUnit = nullptr; + uint32_t blockingUnitIndex = 0; + + if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page)) + return true; + } + } + return false; + } else { + for (x = minValidX; x <= maxValidX - record->inv_width + 1; x++) { + for (y = minValidY; y <= inventoryMaxValidY - record->inv_height + 1; y++) { + diablo2::structures::unit* blockingUnit = nullptr; + uint32_t blockingUnitIndex = 0; + + // Check if the item can be placed at the current position without overflowing + if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page)) + return true; + } + } + return false; + } +} diff --git a/src/d2tweaks/server/modules/damage_display/damage_display_server.cpp b/src/d2tweaks/server/modules/damage_display/damage_display_server.cpp new file mode 100644 index 0000000..5d46981 --- /dev/null +++ b/src/d2tweaks/server/modules/damage_display/damage_display_server.cpp @@ -0,0 +1,264 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +MODULE_INIT(damage_display) + +static char(__fastcall* g_apply_attack_results_origin)(diablo2::structures::game* game, + diablo2::structures::unit* attacker, + diablo2::structures::unit* defender, + BOOL recalculateDamage, + diablo2::structures::damage* dmg); + +#define PRINT_DMG_DELIMITER(name, delimiter) if(dmg->name > 0) spdlog::debug(#name ": {0}", dmg->name / delimiter) +#define PRINT_DMG(name) PRINT_DMG_DELIMITER(name, 256) + +d2_tweaks::common::damage_type_t get_damage_type(diablo2::structures::damage* dmg) { + uint32_t damage[d2_tweaks::common::DAMAGE_TYPE_COUNT]{ 0 }; + + if (dmg == nullptr || dmg->dmg_total == 0) + return d2_tweaks::common::DAMAGE_TYPE_UNKNOWN; + + damage[d2_tweaks::common::DAMAGE_TYPE_PHYSICAL] = dmg->phys_damage; + damage[d2_tweaks::common::DAMAGE_TYPE_COLD] = dmg->cold_damage; + damage[d2_tweaks::common::DAMAGE_TYPE_FIRE] = dmg->fire_damage; + damage[d2_tweaks::common::DAMAGE_TYPE_LIGHTNING] = dmg->ltng_damage; + damage[d2_tweaks::common::DAMAGE_TYPE_POISON] = dmg->pois_damage; + damage[d2_tweaks::common::DAMAGE_TYPE_MAGIC] = dmg->mag_damage; + + auto result = d2_tweaks::common::DAMAGE_TYPE_UNKNOWN; + uint32_t damage_temp = 0; + + for (size_t i = 0; i < d2_tweaks::common::DAMAGE_TYPE_COUNT; i++) { + if (damage[i] <= damage_temp) + continue; + + result = static_cast(i); + damage_temp = damage[i]; + } + + return result; +} + +static void send_damage_data(diablo2::structures::unit* defender, + diablo2::structures::net_client* client, + diablo2::structures::damage* dmg) { + static auto& instance = singleton::instance(); + static d2_tweaks::common::damage_info_sc packet; + + if (dmg->dmg_total <= 0) + return; + + auto currentHp = diablo2::d2_common::get_stat(defender, diablo2::UNIT_STAT_HITPOINTS, 0); + auto maxHp = diablo2::d2_common::get_stat(defender, diablo2::UNIT_STAT_MAXHP, 0); + + packet.unit_type = static_cast(defender->type); + packet.guid = defender->guid; + packet.damage_type = get_damage_type(dmg); + packet.damage = dmg->dmg_total / 256; + packet.currentHp = currentHp / 256; + packet.maxHp = maxHp / 256; + + spdlog::info("currentHp: {0}", packet.currentHp); + spdlog::info("maxHp: {0}", packet.maxHp); + spdlog::info("damage: {0}", packet.damage); + + + if (packet.damage_type == d2_tweaks::common::DAMAGE_TYPE_UNKNOWN) + return; + + if (packet.damage <= 0) + return; + + instance.send_packet(client, &packet, sizeof packet); +} + +static bool has_players(diablo2::structures::unit* attacker, + diablo2::structures::unit* defender) { + return attacker->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER || + defender->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER; +} + +static bool has_hirelings(diablo2::structures::unit* attacker, + diablo2::structures::unit* defender) { + return attacker && attacker->is_hireling() || defender && defender->is_hireling(); +} + +static bool has_pets(diablo2::structures::unit* attacker, + diablo2::structures::unit* defender) { + return attacker && attacker->is_pet() || defender && defender->is_pet(); +} + +static diablo2::structures::unit* get_hireling_owner(diablo2::structures::game* game, + diablo2::structures::unit* unit) { + static auto& instance = singleton::instance(); + + if (!unit) + return nullptr; + + auto guid = diablo2::d2_common::get_stat(unit, diablo2::UNIT_STAT_UNUSED212, 0); + + if (guid != 0) + return instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER); + + instance.iterate_server_units(game, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER, [&](diablo2::structures::unit* player) { + const auto pet = diablo2::d2_game::get_player_pet(game, player, 7, 0); + + if (pet == unit) { + guid = player->guid; + return false; //stop iterator + } + + return true; + }); + + if (guid == 0) + return nullptr; + + diablo2::d2_common::set_stat(unit, diablo2::UNIT_STAT_UNUSED212, guid, 0); + + return instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER); +} + +static diablo2::structures::unit* get_pet_owner(diablo2::structures::game* game, + diablo2::structures::unit* unit) { + static auto& instance = singleton::instance(); + + if (!unit) + return nullptr; + + auto guid = diablo2::d2_common::get_stat(unit, diablo2::UNIT_STAT_UNUSED212, 0); + + if (guid != 0) + return instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER); + + instance.iterate_server_units(game, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER, [&](diablo2::structures::unit* player) { + diablo2::d2_game::iterate_unit_pets( + game, player, [&](diablo2::structures::game*, diablo2::structures::unit*, diablo2::structures::unit* u) { + if (u != unit) + return; + + guid = player->guid; + }); + + return guid == 0; + }); + + if (guid == 0) + return nullptr; + + diablo2::d2_common::set_stat(unit, diablo2::UNIT_STAT_UNUSED212, guid, 0); + + return instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER); +} + +static void process_players_damage(diablo2::structures::unit* attacker, + diablo2::structures::unit* defender, + diablo2::structures::damage* dmg) { + diablo2::structures::net_client* client = nullptr; + + if (attacker->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + client = attacker->player_data->net_client; + else if (defender->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + client = defender->player_data->net_client; + + send_damage_data(defender, client, dmg); +} + +static void process_hireling_damage(diablo2::structures::game* game, + diablo2::structures::unit* attacker, + diablo2::structures::unit* defender, + diablo2::structures::damage* dmg) { + diablo2::structures::unit* owner = nullptr; + + if (attacker->is_hireling()) { + owner = get_hireling_owner(game, attacker); + } else if (defender->is_hireling()) { + owner = get_hireling_owner(game, defender); + } + + if (!owner) + return; + + send_damage_data(defender, owner->player_data->net_client, dmg); +} + +static void process_pet_damage(diablo2::structures::game* game, + diablo2::structures::unit* attacker, + diablo2::structures::unit* defender, + diablo2::structures::damage* dmg) { + diablo2::structures::unit* owner = nullptr; + + if (attacker->is_pet()) { + owner = get_pet_owner(game, attacker); + } else if (defender->is_pet()) { + owner = get_pet_owner(game, defender); + } + + if (!owner) + return; + + send_damage_data(defender, owner->player_data->net_client, dmg); +} + +static char __fastcall apply_attack_results(diablo2::structures::game* game, + diablo2::structures::unit* attacker, + diablo2::structures::unit* defender, + BOOL recalculateDamage, + diablo2::structures::damage* dmg) { + static auto& instance = singleton::instance(); + + static char result = 0; + + result = g_apply_attack_results_origin(game, attacker, defender, recalculateDamage, dmg); + + if (has_players(attacker, defender)) { + process_players_damage(attacker, defender, dmg); + return result; + } + + if (has_hirelings(attacker, defender)) { + process_hireling_damage(game, attacker, defender, dmg); + return result; + } + + if (has_pets(attacker, defender)) { + process_pet_damage(game, attacker, defender, dmg); + return result; + } + + return result; +} + +void d2_tweaks::server::modules::damage_display::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "DamageDisplay", 1, acPathToIni) != FALSE) { + hooking::hook(diablo2::d2_game::get_base() + 0x8FE90, apply_attack_results, reinterpret_cast(&g_apply_attack_results_origin)); + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_DAMAGE_INFO, this); + } +} + +bool d2_tweaks::server::modules::damage_display::handle_packet(diablo2::structures::game* game, + diablo2::structures::unit* player, common::packet_header* packet) { + return true; +} + +void d2_tweaks::server::modules::damage_display::tick(diablo2::structures::game* game, diablo2::structures::unit* unit) {} diff --git a/src/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.cpp b/src/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.cpp new file mode 100644 index 0000000..9b4fd0a --- /dev/null +++ b/src/d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.cpp @@ -0,0 +1,136 @@ +#include +#include + +#include + +#include +#include +#include + +MODULE_INIT(identify_on_pickup) + +static unsigned int g_item_Normal = 0; +static unsigned int g_item_Superior = 0; +static unsigned int g_item_Magic = 0; +static unsigned int g_item_Rare = 0; +static unsigned int g_item_Set = 0; +static unsigned int g_item_Unique = 0; +static unsigned int g_item_Crafted = 0; +static unsigned int g_item_Tempered = 0; + +static uint32_t(__fastcall* g_pickup_item_original)(diablo2::structures::game*, + diablo2::structures::unit*, + uint32_t, uint32_t); +static uint32_t __fastcall pickup_item(diablo2::structures::game* game, + diablo2::structures::unit* player, + uint32_t guid, + uint32_t a4) { + static auto& instance = singleton::instance(); + + if (!game || !player || player->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + return g_pickup_item_original(game, player, guid, a4); + + const auto item = instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); + + if (!item) + return g_pickup_item_original(game, player, guid, a4); + + auto quality = diablo2::d2_common::get_item_quality(item); + + if (quality == 2 && g_item_Normal != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 3 && g_item_Superior != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 4 && g_item_Magic != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 5 && g_item_Set != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 6 && g_item_Rare != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 7 && g_item_Unique != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 8 && g_item_Crafted != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 9 && g_item_Tempered != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + + return g_pickup_item_original(game, player, guid, a4); +} + +static uint32_t(__fastcall* g_pickup_item_cursor_original)(diablo2::structures::game*, + diablo2::structures::unit*, + uint32_t, uint32_t); +static uint32_t __fastcall pickup_item_cursor(diablo2::structures::game* game, + diablo2::structures::unit* player, + uint32_t guid, + uint32_t a4) { + static auto& instance = singleton::instance(); + + if (!game || !player || player->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + return g_pickup_item_cursor_original(game, player, guid, a4); + + const auto item = instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); + + if (!item) + return g_pickup_item_cursor_original(game, player, guid, a4); + + auto quality = diablo2::d2_common::get_item_quality(item); + + if (quality == 2 && g_item_Normal != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 3 && g_item_Superior != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 4 && g_item_Magic != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 5 && g_item_Set != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 6 && g_item_Rare != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 7 && g_item_Unique != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 8 && g_item_Crafted != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + if (quality == 9 && g_item_Tempered != 0) { + diablo2::d2_game::identify_item(game, player, item); + } + + return g_pickup_item_cursor_original(game, player, guid, a4); +} + +void d2_tweaks::server::modules::identify_on_pickup::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "IdentifyOnPickup", 1, acPathToIni) != FALSE) { + hooking::hook(diablo2::d2_game::get_base() + 0x13340, pickup_item, &g_pickup_item_original); + hooking::hook(diablo2::d2_game::get_base() + 0x12B80, pickup_item_cursor, &g_pickup_item_cursor_original); + + g_item_Normal = GetPrivateProfileInt("IdentifyOnPickup", "Normal", 1, acPathToIni); + g_item_Superior = GetPrivateProfileInt("IdentifyOnPickup", "Superior", 1, acPathToIni); + g_item_Magic = GetPrivateProfileInt("IdentifyOnPickup", "Magic", 1, acPathToIni); + g_item_Rare = GetPrivateProfileInt("IdentifyOnPickup", "Rare", 1, acPathToIni); + g_item_Set = GetPrivateProfileInt("IdentifyOnPickup", "Set", 1, acPathToIni); + g_item_Unique = GetPrivateProfileInt("IdentifyOnPickup", "Unique", 1, acPathToIni); + g_item_Crafted = GetPrivateProfileInt("IdentifyOnPickup", "Crafted", 1, acPathToIni); + g_item_Tempered = GetPrivateProfileInt("IdentifyOnPickup", "Tempered", 1, acPathToIni); + } +} + diff --git a/src/d2tweaks/server/modules/item_drop_message/item_drop_message_server.cpp b/src/d2tweaks/server/modules/item_drop_message/item_drop_message_server.cpp new file mode 100644 index 0000000..364bd41 --- /dev/null +++ b/src/d2tweaks/server/modules/item_drop_message/item_drop_message_server.cpp @@ -0,0 +1,381 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_INIT(item_drop_message) + +static const uint32_t m_nCountItemListKeys = 30; +static const uint32_t m_nCountItemTypesKeys = 10; + +static uint32_t m_nCountItemListAll = 0; +static char m_aacItemList[m_nCountItemListKeys][MAX_STRING_LENGHT] = { 0 }; +static char m_acItemListAll[MAX_STRING_LENGHT * m_nCountItemListKeys] = { 0 }; +static char m_acItemListAllTemp[MAX_STRING_LENGHT * m_nCountItemListKeys] = { 0 }; + +static uint32_t m_nCountItemTypesAll = 0; +static char m_aacItemTypes[m_nCountItemTypesKeys][MAX_STRING_LENGHT] = { 0 }; +static char m_acItemTypesAll[MAX_STRING_LENGHT * m_nCountItemTypesKeys] = { 0 }; +static char m_acItemTypesAllTemp[MAX_STRING_LENGHT * m_nCountItemTypesKeys] = { 0 }; + +static char m_acBuffer[1024] = { 0 }; + +static item_code* m_stItemList; +static item_type* m_stItemTypes; + + +void d2_tweaks::server::modules::item_drop_message::init() { + uint32_t dwLenght = 0; + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "ItemDropMessage", 1, acPathToIni) != FALSE) { + for (uint32_t i = 0; i < m_nCountItemListKeys; i++) { + sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemList%d", i + 1); + dwLenght = GetPrivateProfileString("ItemDropMessage", m_acBuffer, 0, m_aacItemList[i], MAX_STRING_LENGHT - 1, acPathToIni); + if (dwLenght != 0) { + lstrcat(m_acItemListAll, m_aacItemList[i]); + lstrcat(m_acItemListAll, "|"); + } + } + + for (uint32_t i = 0; i < m_nCountItemTypesKeys; i++) { + sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemTypeList%d", i + 1); + dwLenght = GetPrivateProfileString("ItemDropMessage", m_acBuffer, 0, m_aacItemList[i], MAX_STRING_LENGHT - 1, acPathToIni); + if (dwLenght != 0) { + lstrcat(m_acItemTypesAll, m_aacItemTypes[i]); + lstrcat(m_acItemTypesAll, "|"); + } + } + +/////// Parse ItemCode + dwLenght = lstrlen(m_acItemListAll); + memcpy(m_acItemListAllTemp, m_acItemListAll, dwLenght + 1); + // + char* token_string_itemcode = strtok(m_acItemListAllTemp, " ,|"); + while (token_string_itemcode) + { + m_nCountItemListAll++; + token_string_itemcode = strtok(NULL, " ,|"); + } + + // , + m_stItemList = (item_code*)malloc(m_nCountItemListAll * sizeof(item_code)); + memset(m_stItemList, 0, m_nCountItemListAll * sizeof(item_code)); + + // + memcpy(m_acItemListAllTemp, m_acItemListAll, dwLenght + 1); + token_string_itemcode = strtok(m_acItemListAllTemp, " ,|"); + + for (uint32_t i = 0; token_string_itemcode != 0; i++) + { + uint32_t cur_string_length = lstrlen(token_string_itemcode); + m_stItemList[i].code0 = token_string_itemcode[0]; + m_stItemList[i].code1 = token_string_itemcode[1]; + m_stItemList[i].code2 = token_string_itemcode[2]; + + if (token_string_itemcode[3] == ':') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = FALSE; + } + + if (cur_string_length <= 13) { //for example jew:123456789 + // p = 4 --> this is first digit after ':' + for (uint32_t p = 4; p <= cur_string_length; p++) { + if (token_string_itemcode[p] == 0) { + break; + } + if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') { + m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = TRUE; + } + } + } + } + + if (token_string_itemcode[3] == '-') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = TRUE; + } + + if (cur_string_length <= 13) { //for example jew:123456789 + // p = 4 --> this is first digit after '-' + for (uint32_t p = 4; p <= cur_string_length; p++) { + if (token_string_itemcode[p] == 0) { + break; + } + if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') { + m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = FALSE; + } + } + } + } + + if (token_string_itemcode[3] != '-' && token_string_itemcode[3] != ':') + { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemList[i].qualityinclude[q] = TRUE; + } + } + + token_string_itemcode = strtok(NULL, " ,|"); + } + + +/// Parse ItemType + dwLenght = lstrlen(m_acItemTypesAll); + memcpy(m_acItemTypesAllTemp, m_acItemTypesAll, dwLenght + 1); + char* token_string_itemtype_code = strtok(m_acItemTypesAllTemp, ",|"); + while (token_string_itemtype_code) + { + m_nCountItemTypesAll++; + token_string_itemtype_code = strtok(NULL, ",|"); + } + + m_stItemTypes = (item_type*)malloc(m_nCountItemTypesAll * sizeof(item_type)); + memset(m_stItemTypes, 0, m_nCountItemTypesAll * sizeof(item_type)); + + memcpy(m_acItemTypesAllTemp, m_acItemTypesAll, dwLenght + 1); + token_string_itemtype_code = strtok(m_acItemTypesAllTemp, ",|"); + for (uint32_t i = 0; token_string_itemtype_code != 0; i++) + { + uint32_t cur_itemtypes_string_length = lstrlen(token_string_itemtype_code); + //m_stItemTypes[i].type0 = token_string_itemtype_code[0]; + //m_stItemTypes[i].type1 = token_string_itemtype_code[1]; + //m_stItemTypes[i].type2 = token_string_itemtype_code[2]; + //m_stItemTypes[i].type3 = token_string_itemtype_code[3]; + + m_stItemTypes[i].dwtype = *(DWORD*)token_string_itemtype_code; + + if (token_string_itemtype_code[4] == ':') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = FALSE; + } + + if (cur_itemtypes_string_length <= 14) { //for example tors:123456789 + // p = 5 --> this is first digit after ':' + for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) { + if (token_string_itemtype_code[p] == 0) { + break; + } + if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') { + m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = TRUE; + } + } + } + } + + if (token_string_itemtype_code[4] == '-') { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = TRUE; + } + + if (cur_itemtypes_string_length <= 14) { //for example armo:123456789 + // p = 5 --> this is first digit after '-' + for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) { + if (token_string_itemtype_code[p] == 0) { + break; + } + if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') { + m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = FALSE; + } + } + } + } + + if (token_string_itemtype_code[4] != '-' && token_string_itemtype_code[4] != ':') + { + // first index quality = 1 + for (uint32_t q = 1; q <= 9; q++) { + m_stItemTypes[i].qualityinclude[q] = TRUE; + } + } + + token_string_itemtype_code = strtok(NULL, ",|"); + } + + //singleton::instance().register_tick_handler(this); + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_ITEM_DROPPED_INFO, this); + } +} + +void d2_tweaks::server::modules::item_drop_message::tick(diablo2::structures::game* game, + diablo2::structures::unit* unit) { + static common::item_pickup_info_sc packet; + static auto& instance = singleton::instance(); + if (!game || !unit) + return; + + if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER) + return; +} + + +bool d2_tweaks::server::modules::item_drop_message::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) { + const auto income_item_dropped_packet = static_cast(packet); + static common::item_dropped_info_sc response_item_dropped_packet; + + switch (income_item_dropped_packet->message_type) + { + case common::message_types_t::MESSAGE_TYPE_ITEM_DROPPED_INFO: + auto current_unit = diablo2::d2_game::get_server_unit(game, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM, income_item_dropped_packet->item_id); + if (current_unit != 0) { + if (current_unit->type == diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) { + if (current_unit->item_data != 0) { + auto record = diablo2::d2_common::get_item_record(current_unit->data_record_index); + uint32_t quality = diablo2::d2_common::get_item_quality(current_unit); + wchar_t* string_wc = diablo2::d2_lang::get_string_from_index(record->name_str); + static char string_mb[260] = { 0 }; + + const auto itemtype_record = diablo2::d2_common::get_item_type_record(record->type); + auto itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv1); + auto itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv2); + + wcstombs(string_mb, string_wc, 256); + + memset(response_item_dropped_packet.arr_itemtype_codestr_equivstr, 0, sizeof response_item_dropped_packet.arr_itemtype_codestr_equivstr); + // itemtype code + *(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[0] = *(DWORD*)itemtype_record->code; + // index second code in array + uint32_t index_arr_itemtype = 1; + + if (itemtype_record_equiv1) { + // + if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) { + // equiv1 + *(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code; + index_arr_itemtype++; + // eqiv1 + for (index_arr_itemtype; itemtype_record_equiv1->equiv1 != 0; index_arr_itemtype++) { + // + itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv1->equiv1); + if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) { + *(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code; + } + else break; + } + } + } + + + if (itemtype_record_equiv2) { + if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) { + // equiv1 + *(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code; + index_arr_itemtype++; + // eqiv1 + for (index_arr_itemtype; itemtype_record_equiv2->equiv2 != 0; index_arr_itemtype++) { + // + itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv2->equiv2); + if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) { + *(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code; + } + else break; + } + } + } + + + if (GetKeyState(VK_SCROLL) != 0) { + response_item_dropped_packet.item = income_item_dropped_packet->item_id; + response_item_dropped_packet.code[0] = record->string_code[0]; + response_item_dropped_packet.code[1] = record->string_code[1]; + response_item_dropped_packet.code[2] = record->string_code[2]; + + // itemtypes + response_item_dropped_packet.index_arr_itemtype = index_arr_itemtype; + + response_item_dropped_packet.quality = quality; + response_item_dropped_packet.showthis = TRUE; + + memcpy(response_item_dropped_packet.namestr, string_mb, 128); + //spdlog::debug("[item_drop_message_s] Code={} Type.Code={} Type.Equiv1={} Type.Equiv2={}", response_item_dropped_packet.code, response_item_dropped_packet.itemtype_code, response_item_dropped_packet.itemtype_equiv1, response_item_dropped_packet.itemtype_equiv2); + singleton::instance().send_packet(player->player_data->net_client, &response_item_dropped_packet, sizeof response_item_dropped_packet); + break; + } + + + for (uint32_t i = 0; i < m_nCountItemTypesAll; i++) + { + for (uint32_t count = 0; count < index_arr_itemtype; count++) + { + if (*(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[count] == (DWORD)m_stItemTypes[i].dwtype) + { + if (m_stItemTypes[i].qualityinclude[quality] == TRUE) + { + response_item_dropped_packet.item = income_item_dropped_packet->item_id; + response_item_dropped_packet.code[0] = record->string_code[0]; + response_item_dropped_packet.code[1] = record->string_code[1]; + response_item_dropped_packet.code[2] = record->string_code[2]; + + response_item_dropped_packet.index_arr_itemtype = index_arr_itemtype; + response_item_dropped_packet.quality = quality; + response_item_dropped_packet.showthis = TRUE; + + memcpy(response_item_dropped_packet.namestr, string_mb, 128); + //spdlog::debug("[item_drop_message_s_itemtypes] Code={} Type.Code={} Type.Equiv1={} Type.Equiv2={}", response_item_dropped_packet.code, response_item_dropped_packet.itemtype_code, response_item_dropped_packet.itemtype_equiv1, response_item_dropped_packet.itemtype_equiv2); + singleton::instance().send_packet(player->player_data->net_client, &response_item_dropped_packet, sizeof response_item_dropped_packet); + goto L1; + } + else { + response_item_dropped_packet.showthis = FALSE; + } + } + } + } + + + for (uint32_t i = 0; i < m_nCountItemListAll; i++) + { + if (record->string_code[0] == m_stItemList[i].code0 && + record->string_code[1] == m_stItemList[i].code1 && + record->string_code[2] == m_stItemList[i].code2) + { + if (m_stItemList[i].qualityinclude[quality] == TRUE) + { + response_item_dropped_packet.item = income_item_dropped_packet->item_id; + response_item_dropped_packet.code[0] = record->string_code[0]; + response_item_dropped_packet.code[1] = record->string_code[1]; + response_item_dropped_packet.code[2] = record->string_code[2]; + + response_item_dropped_packet.index_arr_itemtype = index_arr_itemtype; + response_item_dropped_packet.quality = quality; + response_item_dropped_packet.showthis = TRUE; + + memcpy(response_item_dropped_packet.namestr, string_mb, 128); + //spdlog::debug("[item_drop_message_s] Code={} Type.Code={} Type.Equiv1={} Type.Equiv2={}", response_item_dropped_packet.code, response_item_dropped_packet.itemtype_code, response_item_dropped_packet.itemtype_equiv1, response_item_dropped_packet.itemtype_equiv2); + singleton::instance().send_packet(player->player_data->net_client, &response_item_dropped_packet, sizeof response_item_dropped_packet); + } + } + else { + response_item_dropped_packet.showthis = FALSE; + } + } + + L1:; + } + } + } + break; + } + + return true; +} \ No newline at end of file diff --git a/src/d2tweaks/server/modules/item_move/item_move_server.cpp b/src/d2tweaks/server/modules/item_move/item_move_server.cpp new file mode 100644 index 0000000..bef6c8d --- /dev/null +++ b/src/d2tweaks/server/modules/item_move/item_move_server.cpp @@ -0,0 +1,88 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_INIT(item_move) + +void d2_tweaks::server::modules::item_move::init() { + char acPathToIni[MAX_PATH] = { 0 }; + const char* pcIniFile = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, acPathToIni); + lstrcat(acPathToIni, pcIniFile); + + if (GetPrivateProfileInt("modules", "ItemMover", 1, acPathToIni) != FALSE) { + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_ITEM_MOVE, this); + } +} + +bool d2_tweaks::server::modules::item_move::handle_packet(diablo2::structures::game* game, + diablo2::structures::unit* player, common::packet_header* packet) { + static common::item_move_sc resp; + static auto& instance = singleton::instance(); + + const auto itemMove = static_cast(packet); + const auto item = instance.get_server_unit(game, itemMove->item_guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); //0x4 = item + + if (item == nullptr) + return true; //block further packet processing + + const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, itemMove->target_page, game->item_format == 101); + + uint32_t tx, ty; + + if (!find_free_space(player->inventory, item, inventoryIndex, itemMove->target_page, tx, ty)) + return true; //block further packet processing + + //diablo2::d2_common::set_unit_mode(item, 0); // mode 4 - , mode 0 - + + item->item_data->page = itemMove->target_page; + + diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, item->item_data->page); + diablo2::d2_common::inv_update_item(player->inventory, item, false); + + diablo2::d2_game::update_inventory_items(game, player); + + //send update packet + resp.item_guid = itemMove->item_guid; + resp.target_page = itemMove->target_page; + resp.tx = tx; + resp.ty = ty; + + const auto client = player->player_data->net_client; + + diablo2::d2_net::send_to_client(1, client->client_id, &resp, sizeof resp); + + return true; +} + +bool d2_tweaks::server::modules::item_move::find_free_space(diablo2::structures::inventory* inv, + diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y) { + char data[0x18]; + + diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data); + + const auto mx = static_cast(data[0]); + const auto my = static_cast(data[1]); + + for (x = 0; x < mx; x++) { + for (y = 0; y < my; y++) { + diablo2::structures::unit* blockingUnit = nullptr; + uint32_t blockingUnitIndex = 0; + + if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page)) + return true; + } + } + + return false; +} diff --git a/src/d2tweaks/server/modules/server_module.cpp b/src/d2tweaks/server/modules/server_module.cpp new file mode 100644 index 0000000..bdbee27 --- /dev/null +++ b/src/d2tweaks/server/modules/server_module.cpp @@ -0,0 +1,15 @@ +#include + +#include + +d2_tweaks::server::modules::server_module::server_module() { + singleton::instance().register_module(this); +} + +bool d2_tweaks::server::modules::server_module::handle_packet(diablo2::structures::game* game, + diablo2::structures::unit* player, common::packet_header* packet) { + return false; +} + +void d2_tweaks::server::modules::server_module::tick(diablo2::structures::game* game, + diablo2::structures::unit* unit) {} \ No newline at end of file diff --git a/src/d2tweaks/server/modules/test/test.cpp b/src/d2tweaks/server/modules/test/test.cpp new file mode 100644 index 0000000..1947592 --- /dev/null +++ b/src/d2tweaks/server/modules/test/test.cpp @@ -0,0 +1,49 @@ +#include + +#include +#include +#include +#include +#include +#include + +MODULE_INIT(test) + +static int(__stdcall* g_set_stat_original)(diablo2::structures::unit* unit, + diablo2::unit_stats_t stat, + uint32_t value, int16_t param); +static int __stdcall set_stat(diablo2::structures::unit* unit, + diablo2::unit_stats_t stat, + uint32_t value, int16_t param) { + if (unit->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER && + stat == diablo2::UNIT_STAT_GOLD) { + + spdlog::debug("Setting UNIT_STAT_GOLD stat!"); + } + + return g_set_stat_original(unit, stat, value, param); +} + +static int32_t(__stdcall* g_set_stat_in_list_original)(void*, diablo2::unit_stats_t stat, + uint32_t value, uint32_t param); +static int32_t __stdcall set_stat_in_list(void* a1, diablo2::unit_stats_t stat, uint32_t value, uint32_t param) { + if (stat == diablo2::UNIT_STAT_GOLD) { + spdlog::debug("Setting UNIT_STAT_GOLD stat!"); + } + + return g_set_stat_in_list_original(a1, stat, value, param); +} + +static int(__fastcall* g_regen_tick_original)(diablo2::structures::game*, diablo2::structures::unit*, int32_t, int32_t); +static int __fastcall regen_tick(diablo2::structures::game* game, diablo2::structures::unit* unit, int32_t a3, int32_t a4) { + return g_regen_tick_original(game, unit, a3, a4); +} + +void d2_tweaks::server::modules::test::init() { + return; + + hooking::hook<10463>(diablo2::d2_common::get_base(), set_stat_in_list, &g_set_stat_in_list_original); + hooking::hook<10517>(diablo2::d2_common::get_base(), set_stat, &g_set_stat_original); + + hooking::hook(diablo2::d2_game::get_base() + 0x50F80, regen_tick, &g_regen_tick_original); +} diff --git a/src/d2tweaks/server/modules/trader_update/trader_update_server.cpp b/src/d2tweaks/server/modules/trader_update/trader_update_server.cpp new file mode 100644 index 0000000..64b4fc1 --- /dev/null +++ b/src/d2tweaks/server/modules/trader_update/trader_update_server.cpp @@ -0,0 +1,233 @@ +#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 +#include +#include + +MODULE_INIT(trader_update) + +enum trader_command { + COMMAND_NULL, + COMMAND_FREE_NPC_INVENTORY, + COMMAND_FREE_NPC_GAMBLE, + COMMAND_FILL_NPC_INVENTORY, + COMMAND_FILL_NPC_GAMBLE, + COMMAND_FINISHED, + COMMAND_ABORT +}; + +static uint32_t m_nNpcId; +static bool m_bGamble; +static uint32_t m_nParam4 = 1; + +//void* g_click_trade_menu; +//void* g_click_gamble_menu; +//void* g_ret; +//uint32_t gamble_or_trade; //gamble == 2, trade == 1 + +//__declspec (naked) void click_menu() { +//__asm { +// pushad; +// pushfd; +// mov eax, [esp + 0x2C];//+0x8 +// mov [m_nNpcId], eax; +// mov eax, DLLBASE_D2GAME; +// add eax, 0x99F4A; +// mov [g_ret], eax; +// popfd; +// popad; +// +// mov eax, dword ptr ss : [esp + 0x8] +// sub esp, 0x10C +// jmp[g_ret]; +// } +//} +// +//static const DLLPatchStrc gpt_click_menu[] = +//{ +// // d2game:$0x99F40 +// {D2DLL_D2GAME, 0x99F40 + 0, PATCH_JMP, FALSE, 0x1}, +// {D2DLL_D2GAME, 0x99F40 + 1, (DWORD)click_menu, TRUE, 0x0}, +// {D2DLL_D2GAME, 0x99F40 + 5, (DWORD)PATCH_NOPBLOCK, FALSE, 0x5}, +// {D2DLL_INVALID} +//}; +// +// +//__declspec (naked) void click_trade_menu() { +// __asm { +// pushad; //+0x24 esp +// pushfd; // +// //mov eax, [esp + 0x28];//+0x4 +// //mov [m_nNpcId], eax; +// mov eax, [esp + 0x34];//+0x10 +// mov [m_nParam4], eax; +// mov [m_bGamble], FALSE +// mov eax, DLLBASE_D2GAME; +// add eax, 0x9A086; +// mov [g_ret], eax; +// popfd; +// popad; +// // +// mov edx, dword ptr [esp + 0x10]; +// mov ecx, edi; +// jmp [g_ret]; +// } +//} +// +//__declspec (naked) void click_gamble_menu() { +// __asm { +// pushad; //+0x24 esp +// pushfd; // +// //mov eax, [esp + 0x28];//+0x4 +// //mov [m_nNpcId], eax; +// mov eax, [esp + 0x34];//+0x10 +// mov [m_nParam4], eax; +// mov [m_bGamble], TRUE +// mov eax, DLLBASE_D2GAME; +// add eax, 0x9A0F0; +// mov [g_ret], eax; +// popfd; +// popad; +// mov eax, dword ptr [esp + 0x10] ; +// mov edx, esi; +// jmp[g_ret]; +// } +//} +// +// +//static const DLLPatchStrc gpt_click_trade_menu[] = +//{ +// {D2DLL_D2GAME, 0x9A080 + 0, PATCH_JMP, FALSE, 0x1}, +// {D2DLL_D2GAME, 0x9A080 + 1, (DWORD)click_trade_menu, TRUE, 0x0}, +// {D2DLL_D2GAME, 0x9A080 + 5, (DWORD)PATCH_NOPBLOCK, FALSE, 0x1}, +// {D2DLL_INVALID} +//}; +// +//static const DLLPatchStrc gpt_click_gamble_menu[] = +//{ +// {D2DLL_D2GAME, 0x9A0EA + 0, PATCH_JMP, FALSE, 0x1}, +// {D2DLL_D2GAME, 0x9A0EA + 1, (DWORD)click_gamble_menu, TRUE, 0x0}, +// {D2DLL_D2GAME, 0x9A0EA + 5, (DWORD)PATCH_NOPBLOCK, FALSE, 0x1}, +// {D2DLL_INVALID} +//}; + + +void d2_tweaks::server::modules::trader_update::init() { + char szDir[MAX_PATH]; + char szPath[MAX_PATH]; + const char szConfig[] = "d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, szDir); + snprintf(szPath, MAX_PATH, "%s\\%s", szDir, szConfig); + CIni config(szPath); + + if (config.GetInt("modules", "ReloadTradeGamble", 1) != FALSE) { + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_TRADER_UPDATE, this); + //D2TEMPLATE_ApplyPatch(gpt_click_trade_menu); + //D2TEMPLATE_ApplyPatch(gpt_click_gamble_menu); + //D2TEMPLATE_ApplyPatch(gpt_click_menu); + } +} + + +void d2_tweaks::server::modules::trader_update::tick(diablo2::structures::game* game, diablo2::structures::unit* unit) { + return; +} + + +struct ClientFromNumber { + uint8_t padding[0x174]; + diablo2::structures::net_client* net_cleint; +}; + + +bool d2_tweaks::server::modules::trader_update::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) { + const auto income_packet_cs = static_cast(packet); + static common::trader_update_sc response_packet_sc; + + diablo2::structures::unit* temp_ptNPC = diablo2::d2_game::get_server_unit(game, diablo2::structures::unit_type_t::UNIT_TYPE_MONSTER, income_packet_cs->npc_id); + diablo2::structures::npc_record* npcrecord = diablo2::d2_game::get_npc_record(game, temp_ptNPC, &temp_ptNPC); + diablo2::structures::unit* ptNPC = diablo2::d2_game::get_server_unit(game, diablo2::structures::unit_type_t::UNIT_TYPE_MONSTER, income_packet_cs->npc_id); + + if (!ptNPC) + return true; + + // , + if (income_packet_cs->command == COMMAND_FREE_NPC_INVENTORY) { + // + // id net client - 1 , , 3, 5, 7, 9, 11, 13, 15 + for (uint32_t i = 0; i < 16; i++) { + diablo2::structures::net_client* netclient = diablo2::d2_game::get_net_client_from_id(game, i); + if (netclient != 0) { + response_packet_sc.command = COMMAND_FREE_NPC_INVENTORY; + response_packet_sc.npc_id = income_packet_cs->npc_id; + response_packet_sc.is_gamble_menu_open = income_packet_cs->is_gamble_menu_open; + singleton::instance().send_packet(netclient, &response_packet_sc, sizeof response_packet_sc); + } + } + } + + + if (income_packet_cs->command == COMMAND_FREE_NPC_GAMBLE) { + npcrecord->pGamble->pInventory = 0; + npcrecord->pGamble->dwGUID = 0; + npcrecord->pGamble = 0; + response_packet_sc.command = COMMAND_FREE_NPC_GAMBLE; + response_packet_sc.npc_id = income_packet_cs->npc_id; + response_packet_sc.is_gamble_menu_open = income_packet_cs->is_gamble_menu_open; + singleton::instance().send_packet(player->player_data->net_client, &response_packet_sc, sizeof response_packet_sc); + } + + + if (income_packet_cs->command == COMMAND_FILL_NPC_INVENTORY) { + // + npcrecord->bRefreshInventory = true; + diablo2::d2_game::create_vendor_cache1(game, player, ptNPC, 1, false); + npcrecord->bRefreshInventory = false; + + // + // id net client - 1 , , 3, 5, 7, 9, 11, 13, 15 + for (uint32_t i = 0; i < 16; i++) { + diablo2::structures::net_client* netclient = diablo2::d2_game::get_net_client_from_id(game, i); + if (netclient != 0) { + response_packet_sc.command = COMMAND_FILL_NPC_INVENTORY; + response_packet_sc.npc_id = income_packet_cs->npc_id; + response_packet_sc.is_gamble_menu_open = income_packet_cs->is_gamble_menu_open; + singleton::instance().send_packet(netclient, &response_packet_sc, sizeof response_packet_sc); + } + } + } + + + if (income_packet_cs->command == COMMAND_FILL_NPC_GAMBLE) { + diablo2::d2_game::create_vendor_cache1(game, player, ptNPC, 1, true); + response_packet_sc.command = COMMAND_FILL_NPC_GAMBLE; + response_packet_sc.npc_id = income_packet_cs->npc_id; + response_packet_sc.is_gamble_menu_open = income_packet_cs->is_gamble_menu_open; + singleton::instance().send_packet(player->player_data->net_client, &response_packet_sc, sizeof response_packet_sc); + } + + return true; +} diff --git a/src/d2tweaks/server/modules/transmute/transmute_server.cpp b/src/d2tweaks/server/modules/transmute/transmute_server.cpp new file mode 100644 index 0000000..89e6422 --- /dev/null +++ b/src/d2tweaks/server/modules/transmute/transmute_server.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_INIT(transmute) + +enum transmute_command { + COMMAND_NULL, + COMMAND_TRANSMUTE_START, + COMMAND_TRANSMUTE_END, + COMMAND_CALL_TRANSMUTE, + COMMAND_MOVE_ITEM, + COMMAND_ABORT +}; + +void d2_tweaks::server::modules::transmute::init() { + char PathToIni[MAX_PATH] = { 0 }; + const char IniFile[] = "\\d2tweaks.ini"; + + GetCurrentDirectory(MAX_PATH, PathToIni); + lstrcat(PathToIni, IniFile); + + if (GetPrivateProfileInt("modules", "AutoTransmute", 0, PathToIni) != FALSE) { + //singleton::instance().register_tick_handler(this); + singleton::instance().register_packet_handler(common::MESSAGE_TYPE_TRANSMUTE, this); + } +} + +// transmute function d2game +// topfunc d2game:$62130 int __fastcall CRAFT_Transmogrify(D2GameStrc* pGame, D2UnitStrc* pPlayer) +// subfunc d2game:$0x60010 int __fastcall CRAFT_Transmogrify(D2GameStrc* pGame, D2UnitStrc* pPlayer, D2CubemainTXT* pCubeTxt, void* pUnknown) + +void d2_tweaks::server::modules::transmute::tick(diablo2::structures::game* game, diablo2::structures::unit* unit) { + return; +} + + +inline uint64_t TimeStart() { + _asm { + cpuid + rdtsc + } +} + + +inline uint64_t TimeEnd() { + _asm { + rdtsc + } +} + + +bool d2_tweaks::server::modules::transmute::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) { + const auto income_packet_cs = static_cast(packet); + static common::transmute_info_sc response_packet_sc; + + // + if (income_packet_cs->command == COMMAND_CALL_TRANSMUTE) { + #ifndef NDEBUG + uint64_t time = TimeStart(); + #endif + // item, getmaxcuberecipes + if (diablo2::d2_game::transmogrify(game, player) == diablo2::d2_common::get_max_cube_recipes()) { + response_packet_sc.command = COMMAND_ABORT; + singleton::instance().send_packet(player->player_data->net_client, &response_packet_sc, sizeof response_packet_sc); + //diablo2::d2_net::send_to_client(1, client->client_id, &response_packet_sc, sizeof response_packet_sc); + } + #ifndef NDEBUG + time = (TimeEnd() - time); + spdlog::debug("Time {}", time); + #endif + } + + if (income_packet_cs->command == COMMAND_MOVE_ITEM) { + d2_tweaks::server::modules::transmute::move_item_to(game, player, packet); + } + + return true; +} + + + +bool d2_tweaks::server::modules::transmute::move_item_to(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) { + static common::transmute_info_sc resp; + static auto& instance = singleton::instance(); + const auto itemMove = static_cast(packet); + const auto item = instance.get_server_unit(game, itemMove->item_guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); //0x4 = item + + if (item == nullptr) + return true; //block further packet processing + + const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, itemMove->target_page, game->item_format == 101); + + uint32_t tx, ty; + + if (!find_free_space(player->inventory, item, inventoryIndex, itemMove->target_page, tx, ty)) + return true; //block further packet processing + + item->item_data->page = itemMove->target_page; + + diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, item->item_data->page); + diablo2::d2_common::inv_update_item(player->inventory, item, false); + diablo2::d2_game::update_inventory_items(game, player); + + //send update packet + resp.command = COMMAND_MOVE_ITEM; + resp.item_guid = itemMove->item_guid; + resp.target_page = itemMove->target_page; + resp.tx = tx; + resp.ty = ty; + + const auto client = player->player_data->net_client; + //diablo2::d2_net::send_to_client(1, client->client_id, &resp, sizeof resp); + singleton::instance().send_packet(player->player_data->net_client, &resp, sizeof resp); + return true; +} + + + +bool d2_tweaks::server::modules::transmute::find_free_space(diablo2::structures::inventory* inv, + diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y) { + char data[0x18]; + + diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data); + + const auto mx = static_cast(data[0]); + const auto my = static_cast(data[1]); + + for (x = 0; x < mx; x++) { + for (y = 0; y < my; y++) { + diablo2::structures::unit* blockingUnit = nullptr; + uint32_t blockingUnitIndex = 0; + + if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page)) + return true; + } + } + + return false; +} + diff --git a/src/d2tweaks/server/server.cpp b/src/d2tweaks/server/server.cpp new file mode 100644 index 0000000..b5c7c17 --- /dev/null +++ b/src/d2tweaks/server/server.cpp @@ -0,0 +1,228 @@ +#include + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +static int32_t(__fastcall* g_get_incoming_packet_info_original)(d2_tweaks::common::packet_header* data, unsigned int dataSize, size_t* packetSizeOut, size_t* someOffset, int* packetGroup, int32_t* a6, int a7, int a8); + +static int32_t(__fastcall* g_handle_packet_original)(diablo2::structures::game* game, diablo2::structures::unit* player, d2_tweaks::common::packet_header* data, size_t size); +static int32_t(__fastcall* g_net_tick_original)(diablo2::structures::game*, diablo2::structures::unit*, int32_t, int32_t); + + +//returns some kind of processing type (i.e. resultGroup == 0x04 means drop packet) +static int32_t __fastcall get_incoming_packet_info(d2_tweaks::common::packet_header* data, unsigned int dataSize, size_t* packetSizeOut, size_t* someOffset, int* packetGroup, int32_t* a6, int a7, int a8) { + static d2_tweaks::common::packet_header dummy; + static auto& instance = singleton::instance(); + + const auto resultGroup = g_get_incoming_packet_info_original(data, dataSize, packetSizeOut, someOffset, packetGroup, a6, a7, a8); + + if (data->d2_packet_type == dummy.d2_packet_type && resultGroup == 0x04) { //0x04 - drop packet + size_t size; + + if (!instance.get_packet_size_cs(data, size)) + return resultGroup; + + *packetSizeOut = size; + *packetGroup = 1; + *a6 = 100; + return 2; + } + + return resultGroup; +} + +static int32_t __fastcall handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, d2_tweaks::common::packet_header* data, size_t size) { + static d2_tweaks::common::packet_header dummy; + static auto& instance = singleton::instance(); + + if (data->d2_packet_type == dummy.d2_packet_type) { + if (!instance.handle_packet(game, player, data)) + return g_handle_packet_original(game, player, data, size); + + return 1; + } + + return g_handle_packet_original(game, player, data, size); +} + +d2_tweaks::server::server::server(token) { + m_module_id_counter = 0; + m_tick_handler_id_counter = 0; +} + +static int32_t g_ebp_send_to_client; +void __fastcall hook_sc_packet_before_sent(uint32_t* client_strc, d2_tweaks::common::packet_header* packet, size_t size) { +#ifndef NDEBUG + __asm { + push [ebp + 0x30]; + pop [g_ebp_send_to_client]; + } + + if (size == -1) + return; + + spdlog::error("[d2game SEND] Packet {} Message {} Size {} CallFrom {}", (void*)packet->d2_packet_type, (void*)packet->message_type, size, (void*)g_ebp_send_to_client); +#endif + + return; +} + + +__declspec (naked) void hook_sc_packet_before_sent_wrapper() { + __asm { + pushad; + pushfd; + push [esp+0x28] + call [hook_sc_packet_before_sent] + popfd; + popad; + // + push ecx + push ebp + push esi + mov esi, ecx + } + RET_TO_RVA(DLLBASE_D2GAME, 0xC715); +} + +// d2game:$0xC710 +static const DLLPatchStrc gpt_hook_sc_packet_before_sent[] = +{ + {D2DLL_D2GAME, 0xC710 + 0, PATCH_JMP, FALSE, 0x1}, + {D2DLL_D2GAME, 0xC710 + 1, (DWORD)hook_sc_packet_before_sent_wrapper, TRUE, 0x0}, + {D2DLL_INVALID} +}; + + +void d2_tweaks::server::server::init() { + hooking::hook(reinterpret_cast(diablo2::d2_game::get_base() + 0x59320), ::handle_packet, &g_handle_packet_original); + hooking::hook(reinterpret_cast(diablo2::d2_game::get_base() + 0x50F80), net_tick, &g_net_tick_original); + hooking::hook(reinterpret_cast(diablo2::d2_net::get_base() + 0x1FE0), get_incoming_packet_info, &g_get_incoming_packet_info_original); + + //D2TEMPLATE_ApplyPatch(gpt_hook_sc_packet_before_sent); + + //disable outgoing packet type checks + DWORD oldProtect; + if (VirtualProtect(diablo2::d2_net::get_base() + 0x18B2, 0x02, PAGE_EXECUTE_READWRITE, &oldProtect)) + *reinterpret_cast(diablo2::d2_net::get_base() + 0x18B2) = 0xBC3C; + + if (VirtualProtect(diablo2::d2_net::get_base() + 0x18D0, 0x02, PAGE_EXECUTE_READWRITE, &oldProtect)) + *reinterpret_cast(diablo2::d2_net::get_base() + 0x18D0) = 0xBC3C; + + for (size_t i = 0; i < sizeof m_modules / sizeof(void*); i++) { + if (m_modules[i] == nullptr) + break; + + m_modules[i]->init(); + } +} + +void d2_tweaks::server::server::send_packet(diablo2::structures::net_client* client, common::packet_header* packet, size_t size) { + diablo2::d2_game::enqueue_packet(client, packet, size); +} + +bool d2_tweaks::server::server::handle_packet(diablo2::structures::game* game, + diablo2::structures::unit* player, + common::packet_header* packet) { + auto handler = m_packet_handlers[packet->message_type]; + + if (!handler) + return false; + + return handler->handle_packet(game, player, packet); +} + +void d2_tweaks::server::server::register_module(modules::server_module* module) { + m_modules[m_module_id_counter++] = module; +} + +void d2_tweaks::server::server::register_tick_handler(modules::server_module* module) { + m_tick_handlers[m_tick_handler_id_counter++] = module; +} + +void d2_tweaks::server::server::register_packet_handler(common::message_types_t type, modules::server_module* module) { + if (m_packet_handlers[type] != nullptr) { + spdlog::warn("Serverside packet handler for {0} is already registered!", type); + } + + m_packet_handlers[type] = module; +} + +diablo2::structures::unit* d2_tweaks::server::server::get_server_unit(diablo2::structures::game* game, uint32_t guid, diablo2::structures::unit_type_t type) { + if (game == nullptr) + return nullptr; + + auto typeIndex = static_cast(type); + + if (type == diablo2::structures::unit_type_t::UNIT_TYPE_MISSILE) + typeIndex = 4; + + if (type == diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + typeIndex = 3; + + const auto index = guid & 127; + + auto result = game->unit_list[typeIndex][index]; + + while (result != nullptr && result->guid != guid) { + result = result->prev_unit; + } + + return result; +} + +void d2_tweaks::server::server::iterate_server_units(diablo2::structures::game* game, diablo2::structures::unit_type_t type, + const std::function& cb) { + if (!cb) + return; + + if (!game) + return; + + auto typeIndex = static_cast(type); + + if (type == diablo2::structures::unit_type_t::UNIT_TYPE_MISSILE) + typeIndex = 4; + + if (type == diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) + typeIndex = 3; + + for (size_t index = 0; index < 128; index++) { + auto unit = game->unit_list[typeIndex][index]; + + while (unit != nullptr) { + if (!cb(unit)) + return; + + unit = unit->prev_unit; + } + } +} + +int32_t d2_tweaks::server::server::net_tick(diablo2::structures::game* game, diablo2::structures::unit* unit, int32_t a3, int32_t a4) { + static auto& instance = singleton::instance(); + + for (size_t i = 0; i < sizeof instance.m_modules / sizeof(void*); i++) { + if (instance.m_tick_handlers[i] == nullptr) + break; + + instance.m_tick_handlers[i]->tick(game, unit); + } + + return g_net_tick_original(game, unit, a3, a4); +} diff --git a/src/d2tweaks/ui/controls/button.cpp b/src/d2tweaks/ui/controls/button.cpp new file mode 100644 index 0000000..4a50703 --- /dev/null +++ b/src/d2tweaks/ui/controls/button.cpp @@ -0,0 +1,188 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +d2_tweaks::ui::controls::button::button(menu* menu, + const rect& rect, const std::function& onClick, + common::asset* image, + int32_t frameDown, int32_t frameUp, int32_t clickSound) : control(menu, + rect.get_x(), + rect.get_y(), + rect.get_width(), + rect.get_height()) { + control::set_enabled(true); + control::set_visible(true); + + set_x(rect.get_x()); + set_y(rect.get_y()); + + m_rect = rect; + m_image = new controls::image(menu, image, m_rect.get_x(), m_rect.get_y(), frameUp); + m_frame_down = frameDown; + m_frame_up = frameUp; + m_click_sound = clickSound; + + m_is_down = false; + m_current_frame = m_frame_up; + + m_on_click = onClick; +} + +//struct respos { +// uint32_t res_x; +// uint32_t res_y; +// uint32_t pos_x; +// uint32_t pos_y; +//}; + + + +d2_tweaks::ui::controls::button::button(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) { + char buf[32] = { 0 }; + + auto cx = node.attribute("default_pos_x").as_int(0); + auto cy = node.attribute("default_pos_y").as_int(0); + + m_res_count = node.attribute("resolution_count").as_int(1); + + respos temp; + for (uint32_t i = 1; i <= m_res_count; i++) { + sprintf_s(buf, "res%d_x", i); + temp.res_x = node.attribute(buf).as_int(0); + sprintf_s(buf, "res%d_y", i); + temp.res_y = node.attribute(buf).as_int(0); + sprintf_s(buf, "pos%d_x", i); + temp.pos_x = node.attribute(buf).as_int(0); + sprintf_s(buf, "pos%d_y", i); + temp.pos_y = node.attribute(buf).as_int(0); + + if (temp.pos_x == diablo2::d2_client::screen_width() && temp.pos_y == diablo2::d2_client::screen_height()) { + cx = temp.pos_x; + cy = temp.pos_y; + } + + m_respos.push_back(temp); + } + + const auto cname = node.attribute("name").as_string(); + + const auto cw = node.attribute("width").as_int(0); + const auto ch = node.attribute("height").as_int(0); + + const auto cImgPath = node.attribute("image").as_string(nullptr); + const auto cimg = singleton::instance().get_mpq_file(const_cast(cImgPath), common::MPQ_FILE_TYPE_DC6); + + if (!cimg) { + spdlog::critical("Cannot load {0} image for {1} control!", cImgPath, cname); + exit(-1); + } + + const auto frameDown = node.attribute("frameDown").as_int(); + const auto frameUp = node.attribute("frameUp").as_int(); + const auto clickSound = node.attribute("clickSound").as_int(-1); + const auto popup = node.attribute("popup").as_string(); + + control::set_enabled(true); + control::set_visible(true); + + m_rect = rect(cx, cy, cw, ch); + m_image = new image(menu, cimg, m_rect.get_x(), m_rect.get_y(), frameUp); + m_frame_down = frameDown; + m_frame_up = frameUp; + m_click_sound = clickSound; + + m_is_down = false; + m_current_frame = m_frame_up; + + set_x(cx); + set_y(cy); + control::set_width(cw); + control::set_height(ch); + + set_name(cname); + set_popup(string_utils::string_to_wstring(popup)); +} + +d2_tweaks::ui::controls::button::~button() { + delete m_image; +} + +void d2_tweaks::ui::controls::button::set_x(int32_t value) { + m_rect.set_x(value); + m_image->set_x(value); + control::set_x(value); +} + +void d2_tweaks::ui::controls::button::set_y(int32_t value) { + m_rect.set_y(value); + m_image->set_y(value); + control::set_y(value); +} + +void d2_tweaks::ui::controls::button::draw() { + draw(0, 0); +} + +void d2_tweaks::ui::controls::button::draw(int32_t offsetX, int32_t offsetY) { + for (auto it = m_respos.begin(); it != m_respos.end(); it++) { + if (it->res_x == diablo2::d2_client::screen_width() && it->res_y == diablo2::d2_client::screen_height()) { + set_x(it->pos_x); + set_y(it->pos_y); + break; + } + } + + if (m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY) && + !m_popup.empty()) { + diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); + diablo2::d2_win::set_popup_properties(const_cast(m_popup.c_str()), + get_x() + offsetX + m_rect.get_width() / 2, + get_y() + offsetY - m_rect.get_height(), + diablo2::UI_COLOR_WHITE, TRUE); + diablo2::d2_win::draw_popup(); + } + + m_image->set_frame(m_current_frame); + m_image->draw(offsetX, offsetY); +} + +void d2_tweaks::ui::controls::button::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) { + if (up && m_is_down) { + block = true; + + m_is_down = false; + m_current_frame = m_frame_up; + + if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY)) + return; + + if (m_on_click) + m_on_click(); + + return; + } + + if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY)) + return; + + block = true; + + m_is_down = true; + m_current_frame = m_frame_down; + + if (m_click_sound < 0) + return; + + diablo2::d2_client::play_sound(m_click_sound, nullptr, 0, FALSE, 0); +} + +void d2_tweaks::ui::controls::button::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {} + +void d2_tweaks::ui::controls::button::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {} diff --git a/src/d2tweaks/ui/controls/button.cpp_new b/src/d2tweaks/ui/controls/button.cpp_new new file mode 100644 index 0000000..026c0a2 --- /dev/null +++ b/src/d2tweaks/ui/controls/button.cpp_new @@ -0,0 +1,161 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +d2_tweaks::ui::controls::button::button(menu* menu, + const rect& rect, const std::function& onClick, + common::asset* image, + int32_t frameDown, int32_t frameUp, int32_t clickSound) : control(menu, + rect.get_x(), + rect.get_y(), + rect.get_width(), + rect.get_height()) { + control::set_enabled(true); + control::set_visible(true); + + set_x(rect.get_x()); + set_y(rect.get_y()); + + m_rect = rect; + m_image = new controls::image(menu, image, m_rect.get_x(), m_rect.get_y(), frameUp); + m_frame_down = frameDown; + m_frame_up = frameUp; + m_click_sound = clickSound; + + m_is_down = false; + m_current_frame = m_frame_up; + + m_on_click = onClick; +} + +d2_tweaks::ui::controls::button::button(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) { + const auto cname = node.attribute("name").as_string(); + const auto cx_lo = node.attribute("x_lowres").as_int(0); + const auto cy_lo = node.attribute("y_lowres").as_int(0); + const auto cx_hi = node.attribute("x_hires").as_int(0); + const auto cy_hi = node.attribute("y_hires").as_int(0); + const auto cw = node.attribute("width").as_int(0); + const auto ch = node.attribute("height").as_int(0); + + const auto cImgPath = node.attribute("image").as_string(nullptr); + const auto cimg = singleton::instance().get_mpq_file( + const_cast(cImgPath), common::MPQ_FILE_TYPE_DC6); + + if (!cimg) { + spdlog::critical("Cannot load {0} image for {1} control!", cImgPath, cname); + exit(-1); + } + + const auto frameDown = node.attribute("frameDown").as_int(); + const auto frameUp = node.attribute("frameUp").as_int(); + const auto clickSound = node.attribute("clickSound").as_int(-1); + const auto popup = node.attribute("popup").as_string(); + + control::set_enabled(true); + control::set_visible(true); + + if (diablo2::d2_gfx::get_resolution_mode() == 0) { + m_rect = rect(cx_hi, cy_hi, cw, ch); + } + else { + m_rect = rect(cx_lo, cy_lo, cw, ch); + } + m_image = new image(menu, cimg, m_rect.get_x(), m_rect.get_y(), frameUp); + m_frame_down = frameDown; + m_frame_up = frameUp; + m_click_sound = clickSound; + + m_is_down = false; + m_current_frame = m_frame_up; + + if (diablo2::d2_gfx::get_resolution_mode() == 0) { + set_x(cx_hi); + set_y(cy_hi); + } + else if (diablo2::d2_gfx::get_resolution_mode() == 2) { + set_x(cx_lo); + set_y(cy_lo); + } + + control::set_width(cw); + control::set_height(ch); + + set_name(cname); + set_popup(string_utils::string_to_wstring(popup)); +} + +d2_tweaks::ui::controls::button::~button() { + delete m_image; +} + +void d2_tweaks::ui::controls::button::set_x(int32_t value) { + m_rect.set_x(value); + m_image->set_x(value); + control::set_x(value); +} + +void d2_tweaks::ui::controls::button::set_y(int32_t value) { + m_rect.set_y(value); + m_image->set_y(value); + control::set_y(value); +} + +void d2_tweaks::ui::controls::button::draw() { + draw(0, 0); +} + +void d2_tweaks::ui::controls::button::draw(int32_t offsetX, int32_t offsetY) { + if (m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY) && + !m_popup.empty()) { + diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); + diablo2::d2_win::set_popup_properties(const_cast(m_popup.c_str()), + get_x() + offsetX + m_rect.get_width() / 2, + get_y() + offsetY - m_rect.get_height(), + diablo2::UI_COLOR_WHITE, TRUE); + diablo2::d2_win::draw_popup(); + } + + m_image->set_frame(m_current_frame); + m_image->draw(offsetX, offsetY); +} + +void d2_tweaks::ui::controls::button::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) { + if (up && m_is_down) { + block = true; + + m_is_down = false; + m_current_frame = m_frame_up; + + if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY)) + return; + + if (m_on_click) + m_on_click(); + + return; + } + + if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY)) + return; + + block = true; + + m_is_down = true; + m_current_frame = m_frame_down; + + if (m_click_sound < 0) + return; + + diablo2::d2_client::play_sound(m_click_sound, nullptr, 0, FALSE, 0); +} + +void d2_tweaks::ui::controls::button::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {} + +void d2_tweaks::ui::controls::button::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {} diff --git a/src/d2tweaks/ui/controls/checkbox.cpp b/src/d2tweaks/ui/controls/checkbox.cpp new file mode 100644 index 0000000..777e287 --- /dev/null +++ b/src/d2tweaks/ui/controls/checkbox.cpp @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include +#include + +const int32_t PADDING = 3; + +d2_tweaks::ui::controls::checkbox::checkbox(menu* menu, const std::wstring& text, const rect& rect, + const std::function& onClick, + common::asset* image, int32_t frameChecked, int32_t frameUnchecked, int32_t clickSound) + : control(menu, + rect.get_x(), + rect.get_y(), + rect.get_width(), + rect.get_height()) { + control::set_enabled(true); + control::set_visible(true); + + checkbox::set_x(rect.get_x()); + checkbox::set_y(rect.get_y()); + + m_rect = rect; + + m_image = new controls::image(menu, image, rect.get_x(), rect.get_y(), frameUnchecked); + m_label = new label(menu, text, rect.get_x() + m_image->get_width() + PADDING, m_rect.get_y()); + + m_frame_checked = frameChecked; + m_frame_unchecked = frameUnchecked; + + m_click_sound = clickSound; + + m_is_down = false; + m_state = false; +} + +d2_tweaks::ui::controls::checkbox::checkbox(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) { + control::set_enabled(true); + control::set_visible(true); + + const auto cname = node.attribute("name").as_string(); + const auto cx = node.attribute("x").as_int(0); + const auto cy = node.attribute("y").as_int(0); + + const auto frameChecked = node.attribute("frameChecked").as_int(); + const auto frameUnchecked = node.attribute("frameUnchecked").as_int(); + const auto clickSound = node.attribute("clickSound").as_int(-1); + const auto popup = node.attribute("popup").as_string(); + + const auto ctext = node.attribute("text").as_string(); + const auto ccolor = node.attribute("color").as_int(); + const auto cfont = node.attribute("font").as_int(1); + + const auto cImgPath = node.attribute("image").as_string(nullptr); + const auto cimg = singleton::instance().get_mpq_file( + const_cast(cImgPath), common::MPQ_FILE_TYPE_DC6); + + if (!cimg) { + spdlog::critical("Cannot load {0} image for {1} control!", cImgPath, cname); + exit(-1); + } + + m_frame_checked = frameChecked; + m_frame_unchecked = frameUnchecked; + m_click_sound = clickSound; + m_popup = string_utils::string_to_wstring(popup); + + m_is_down = false; + m_state = false; + + m_image = new image(menu, cimg, cx, cy, m_frame_unchecked); + m_label = new label(menu, string_utils::string_to_wstring(ctext), cx + m_image->get_width() + PADDING, cy, + static_cast(ccolor), static_cast(cfont)); + + checkbox::set_x(cx); + checkbox::set_y(cy); + + set_name(cname); +} + +void d2_tweaks::ui::controls::checkbox::set_x(int32_t value) { + m_rect.set_x(value); + m_image->set_x(value); + m_label->set_x(value + m_image->get_width() + PADDING); + control::set_x(value); +} + +void d2_tweaks::ui::controls::checkbox::set_y(int32_t value) { + m_rect.set_y(value); + m_image->set_y(value); + m_label->set_y(value); + control::set_y(value); +} + +void d2_tweaks::ui::controls::checkbox::draw() { + draw(0, 0); +} + +void d2_tweaks::ui::controls::checkbox::draw(int32_t offsetX, int32_t offsetY) { + if (m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY) && + !m_popup.empty()) { + diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); + diablo2::d2_win::set_popup_properties(const_cast(m_popup.c_str()), + get_x() + offsetX + m_rect.get_width() / 2, + get_y() + offsetY - m_rect.get_height(), + diablo2::UI_COLOR_WHITE, TRUE); + diablo2::d2_win::draw_popup(); + } + + m_image->set_frame(m_state ? m_frame_checked : m_frame_unchecked); + m_image->draw(offsetX, offsetY); + m_label->draw(offsetX, offsetY); +} + +void d2_tweaks::ui::controls::checkbox::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) { + if (m_rect.get_width() == 0) + m_rect.set_width(m_image->get_width() + m_label->get_width() + PADDING); + + if (m_rect.get_height() == 0) + m_rect.set_height(m_image->get_height()); + + if (up && m_is_down) { + block = true; + + m_is_down = false; + + if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY)) + return; + + m_state = !m_state; + + if (m_on_click) + m_on_click(m_state); + + return; + } + + if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY)) + return; + + block = true; + + m_is_down = true; + + if (m_click_sound < 0) + return; + + diablo2::d2_client::play_sound(m_click_sound, nullptr, 0, FALSE, 0); +} + +void d2_tweaks::ui::controls::checkbox::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {} + +void d2_tweaks::ui::controls::checkbox::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {} diff --git a/src/d2tweaks/ui/controls/group.cpp b/src/d2tweaks/ui/controls/group.cpp new file mode 100644 index 0000000..c73f28d --- /dev/null +++ b/src/d2tweaks/ui/controls/group.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include + +d2_tweaks::ui::controls::group::group(menu* menu, int32_t x, int32_t y) : control(menu, x, y, 0, 0) { + control::set_enabled(true); + control::set_visible(true); +} + +d2_tweaks::ui::controls::group::group(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) { + control::set_enabled(true); + control::set_visible(true); + + const auto cname = node.attribute("name").as_string(); + const auto cx = node.attribute("x").as_int(0); + const auto cy = node.attribute("y").as_int(0); + const auto cw = node.attribute("width").as_int(0); + const auto ch = node.attribute("height").as_int(0); + + set_name(cname); + + control::set_x(cx); + control::set_y(cy); + control::set_width(cw); + control::set_height(ch); + + for (auto child : node.children()) { + const auto name = child.name(); + + if (strcmp(name, "button") == 0) { + add_control(new button(menu, child)); + continue; + } + + if (strcmp(name, "checkbox") == 0) { + add_control(new checkbox(menu, child)); + continue; + } + + if (strcmp(name, "group") == 0) { + add_control(new group(menu, child)); + continue; + } + + if (strcmp(name, "image") == 0) { + add_control(new image(menu, child)); + continue; + } + + if (strcmp(name, "label") == 0) { + add_control(new label(menu, child)); + } + } +} + +void d2_tweaks::ui::controls::group::draw() { + draw(0, 0); +} + +void d2_tweaks::ui::controls::group::draw(int32_t offsetX, int32_t offsetY) { + for (auto control : m_controls) { + if (!control->get_visible()) + continue; + + control->draw(get_x() + offsetX, get_y() + offsetY); + } +} + +void d2_tweaks::ui::controls::group::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) { + for (auto control : m_controls) { + if (!control->get_enabled()) + continue; + + control->left_mouse(get_x() + offsetX, get_y() + offsetY, up, block); + } +} + +void d2_tweaks::ui::controls::group::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) { + for (auto control : m_controls) { + if (!control->get_enabled()) + continue; + + control->right_mouse(get_x() + offsetX, get_y() + offsetY, up, block); + } +} + +void d2_tweaks::ui::controls::group::key_event(int32_t offsetX, int32_t offsetY, + uint32_t key, bool up, bool& block) { + for (auto control : m_controls) { + if (!control->get_enabled()) + continue; + + control->key_event(get_x() + offsetX, get_y() + offsetY, key, up, block); + } +} + +void d2_tweaks::ui::controls::group::add_control(control* control) { + control->set_parent(this); + m_controls.push_back(control); + + if (get_menu() == nullptr) + return; + + get_menu()->add_control(control); +} diff --git a/src/d2tweaks/ui/controls/image.cpp b/src/d2tweaks/ui/controls/image.cpp new file mode 100644 index 0000000..1b0f5da --- /dev/null +++ b/src/d2tweaks/ui/controls/image.cpp @@ -0,0 +1,95 @@ +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +d2_tweaks::ui::controls::image::image(menu* menu, common::asset* image, int32_t x, int32_t y, int32_t frame) + : control(menu, x, y, 0, 0) { + control::set_enabled(true); + control::set_visible(true); + + m_image = image; + m_frame = frame; + auto cellFile = static_cast(m_image->get()); + const auto cell = cellFile->cells[m_frame]; + m_rect = rect(x, y, cell->width + cell->offset_x, cell->height - cell->offset_y); + m_block_click = false; + + control::set_width(m_rect.get_width()); + control::set_height(m_rect.get_height()); +} + +d2_tweaks::ui::controls::image::image(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) { + const auto cname = node.attribute("name").as_string(); + const auto cx = node.attribute("x").as_int(0); + const auto cy = node.attribute("y").as_int(0); + + const auto cImgPath = node.attribute("image").as_string(nullptr); + const auto cimg = singleton::instance().get_mpq_file( + const_cast(cImgPath), common::MPQ_FILE_TYPE_DC6); + + if (!cimg) { + spdlog::critical("Cannot load {0} image for {1} control!", cImgPath, cname); + exit(-1); + } + + const auto frame = node.attribute("frame").as_int(); + const auto blockClick = node.attribute("blockClick").as_bool(false); + + control::set_enabled(true); + control::set_visible(true); + + m_image = cimg; + m_frame = frame; + auto cellFile = static_cast(m_image->get()); + const auto cell = cellFile->cells[m_frame]; + m_rect = rect(cx, cy, cell->width + cell->offset_x, cell->height - cell->offset_y); + m_block_click = blockClick; + + control::set_x(cx); + control::set_y(cy); + control::set_width(m_rect.get_width()); + control::set_height(m_rect.get_height()); + + set_name(cname); +} + +void d2_tweaks::ui::controls::image::draw() { + draw(0, 0); +} + +void d2_tweaks::ui::controls::image::draw(int32_t offsetX, int32_t offsetY) { + if (m_image == nullptr || !m_image->get()) + return; + + //I don't like to call memset for each draw_info every frame, but if we wouldn't clear it - we'll get access violation inside draw_image + memset(&m_draw_info, 0x00, sizeof m_draw_info); + m_draw_info.cell_file = static_cast(m_image->get()); + m_draw_info.frame = m_frame; + + diablo2::d2_gfx::draw_image(&m_draw_info, get_x() + offsetX, get_y() + offsetY, -1, 5, nullptr); +} + +void d2_tweaks::ui::controls::image::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) { + if (!m_block_click) + return; + + block = m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY); +} + +void d2_tweaks::ui::controls::image::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) { + if (!m_block_click) + return; + + block = m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY); +} + +void d2_tweaks::ui::controls::image::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {} diff --git a/src/d2tweaks/ui/controls/label.cpp b/src/d2tweaks/ui/controls/label.cpp new file mode 100644 index 0000000..6471928 --- /dev/null +++ b/src/d2tweaks/ui/controls/label.cpp @@ -0,0 +1,100 @@ +#include +#include +#include + +d2_tweaks::ui::controls::label::label(menu* menu, const std::wstring& text, int32_t x, int32_t y, diablo2::ui_color_t color, + diablo2::ui_font_t font) : control(menu, x, y, 0, 0) { + control::set_enabled(true); + control::set_visible(true); + + m_text = text; + m_text_owned = false; + m_color = color; + m_font = font; + + control::set_height(0); + control::set_width(0); +} + +d2_tweaks::ui::controls::label::label(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) { + const auto cname = node.attribute("name").as_string(); + + char buf[32] = { 0 }; + + auto cx = node.attribute("default_pos_x").as_int(0); + auto cy = node.attribute("default_pos_y").as_int(0); + m_res_count = node.attribute("resolution_count").as_int(1); + + respos temp; + for (uint32_t i = 1; i <= m_res_count; i++) { + sprintf_s(buf, "res%d_x", i); + temp.res_x = node.attribute(buf).as_int(0); + sprintf_s(buf, "res%d_y", i); + temp.res_y = node.attribute(buf).as_int(0); + sprintf_s(buf, "pos%d_x", i); + temp.pos_x = node.attribute(buf).as_int(0); + sprintf_s(buf, "pos%d_y", i); + temp.pos_y = node.attribute(buf).as_int(0); + + if (temp.pos_x == diablo2::d2_client::screen_width() && temp.pos_y == diablo2::d2_client::screen_height()) { + cx = temp.pos_x; + cy = temp.pos_y; + } + + m_respos.push_back(temp); + } + + const auto ctext = node.attribute("text").as_string(); + const auto ccolor = node.attribute("color").as_int(); + const auto cfont = node.attribute("font").as_int(1); + + control::set_enabled(true); + control::set_visible(true); + + m_text_owned = true; + m_text = string_utils::string_to_wstring(ctext); + m_color = static_cast(ccolor); + m_font = static_cast(cfont); + + control::set_x(cx); + control::set_y(cy); + + control::set_height(0); + control::set_width(0); + + set_name(cname); +} + +void d2_tweaks::ui::controls::label::draw() { + draw(0, 0); +} + +void d2_tweaks::ui::controls::label::draw(int32_t offsetX, int32_t offsetY) { + for (auto it = m_respos.begin(); it != m_respos.end(); it++) { + if (it->res_x == diablo2::d2_client::screen_width() && it->res_y == diablo2::d2_client::screen_height()) { + set_x(it->pos_x); + set_y(it->pos_y); + break; + } + } + + const auto font = diablo2::d2_win::get_current_font(); + diablo2::d2_win::set_current_font(m_font); + + if (get_width() == 0) { + set_width(diablo2::d2_win::get_text_pixel_width(const_cast(m_text.c_str()))); + } + + if (get_height() == 0) { + set_height(diablo2::d2_win::get_current_font_height()); + } + + diablo2::d2_win::draw_text(const_cast(m_text.c_str()), get_x() + offsetX, get_y() + offsetY, m_color, 0); + diablo2::d2_win::set_current_font(font); +} + +void d2_tweaks::ui::controls::label::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {} + +void d2_tweaks::ui::controls::label::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {} + +void d2_tweaks::ui::controls::label::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {} diff --git a/src/d2tweaks/ui/menu.cpp b/src/d2tweaks/ui/menu.cpp new file mode 100644 index 0000000..09f41aa --- /dev/null +++ b/src/d2tweaks/ui/menu.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +d2_tweaks::ui::menu::menu() : m_x(0), m_y(0), m_width(0), m_height(0) {} + +bool d2_tweaks::ui::menu::load_xml(const char* path) { + //diablo2::utils::mpq_ifstream file(path); + //auto file = fopen((char*)path, "r+"); + + //if (!file) + // return false; + + //pugi::xml_document doc; + //const auto result = doc.load_file(path); + + diablo2::utils::mpq_ifstream file(path); + + if (!file.good()) + return false; + + pugi::xml_document doc; + const auto result = doc.load(file); + + if (!result) + return false; + + const auto root = doc.select_nodes("/interface/menu"); + + if (root.size() == 0) + return false; + + const auto menuNode = root.first(); + + m_x = menuNode.node().attribute("default_pos_x").as_int(0); + m_y = menuNode.node().attribute("default_pos_y").as_int(0); + m_res_count = menuNode.node().attribute("resolution_count").as_int(1); + + char buf[32] = { 0 }; + + respos temp; + for (uint32_t i = 1; i <= m_res_count; i++) { + sprintf_s(buf, "res%d_x", i); + temp.res_x = menuNode.node().attribute(buf).as_int(0); + sprintf_s(buf, "res%d_y", i); + temp.res_y = menuNode.node().attribute(buf).as_int(0); + sprintf_s(buf, "pos%d_x", i); + temp.pos_x = menuNode.node().attribute(buf).as_int(0); + sprintf_s(buf, "pos%d_y", i); + temp.pos_y = menuNode.node().attribute(buf).as_int(0); + + if (temp.pos_x == diablo2::d2_client::screen_width() && temp.pos_y == diablo2::d2_client::screen_height()) { + m_x = temp.pos_x; + m_y = temp.pos_y; + } + + m_respos.push_back(temp); + } + + + m_name = menuNode.node().attribute("name").as_string(); + m_width = menuNode.node().attribute("width").as_int(-1); + m_height = menuNode.node().attribute("height").as_int(-1); + + for (auto child : menuNode.node().children()) { + const auto name = child.name(); + + if (strcmp(name, "button") == 0) { + add_control(new controls::button(this, child)); + continue; + } + + if (strcmp(name, "checkbox") == 0) { + add_control(new controls::checkbox(this, child)); + continue; + } + + if (strcmp(name, "group") == 0) { + add_control(new controls::group(this, child)); + continue; + } + + if (strcmp(name, "image") == 0) { + add_control(new controls::image(this, child)); + continue; + } + + if (strcmp(name, "label") == 0) { + add_control(new controls::label(this, child)); + } + } + + if (m_width == -1) { + for (auto control : m_controls) + if (control->get_width() > m_width) + m_width = control->get_width(); + } + + if (m_height == -1) { + for (auto control : m_controls) + if (control->get_height() > m_height) + m_height = control->get_height(); + } + + return true; +} + +void d2_tweaks::ui::menu::add_control(controls::control* control) { + if (control == nullptr) + return; + + if (control->get_name().empty()) { + spdlog::error("Cannot add control: empty name!"); + return; + } + + const auto it = m_named_controls.find(control->get_name()); + + if (it != m_named_controls.end()) + return; + + m_named_controls[control->get_name()] = control; + m_controls.push_back(control); +} + +void d2_tweaks::ui::menu::remove_control(controls::control* control) { + if (control == nullptr) + return; + + m_controls.erase(std::remove(m_controls.begin(), m_controls.end(), control), m_controls.end()); +} + +void d2_tweaks::ui::menu::draw() { + for (auto control : m_controls) { + if (!control->get_visible() || control->get_parent() != nullptr) + continue; + + for (auto it = m_respos.begin(); it != m_respos.end(); it++) { + if (it->res_x == diablo2::d2_client::screen_width() && it->res_y == diablo2::d2_client::screen_height()) { + m_x = it->pos_x; + m_y = it->pos_y; + break; + } + } + + control->draw(m_x, m_y); + } +} + +bool d2_tweaks::ui::menu::left_mouse(bool up) { + auto block = false; + + for (auto control : m_controls) { + if (!control->get_enabled() || control->get_parent() != nullptr) + continue; + + auto tblock = false; + control->left_mouse(m_x, m_y, up, tblock); + block |= tblock; + } + + return block; +} + +bool d2_tweaks::ui::menu::right_mouse(bool up) { + auto block = false; + + for (auto control : m_controls) { + if (!control->get_enabled() || control->get_parent() != nullptr) + continue; + + auto tblock = false; + control->right_mouse(m_x, m_y, up, tblock); + block |= tblock; + } + + return block; +} + +bool d2_tweaks::ui::menu::key_event(uint32_t key, bool up) { + auto block = false; + + for (auto control : m_controls) { + if (!control->get_enabled() || control->get_parent() != nullptr) + continue; + + auto tblock = false; + control->key_event(m_x, m_y, key, up, tblock); + block |= tblock; + } + + return block; +} diff --git a/src/d2tweaks/ui/ui_manager.cpp b/src/d2tweaks/ui/ui_manager.cpp new file mode 100644 index 0000000..f04b2b1 --- /dev/null +++ b/src/d2tweaks/ui/ui_manager.cpp @@ -0,0 +1,181 @@ +#include + +#include +#include + +#include + +#include +#include +#include + +static LRESULT(__stdcall* g_wnd_proc_original)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +d2_tweaks::ui::ui_manager::ui_manager(token) { + hooking::hook(diablo2::d2_win::get_base() + 0xD9B0, wnd_proc, reinterpret_cast(&g_wnd_proc_original)); +} + +void d2_tweaks::ui::ui_manager::add_menu(menu* m) { + if (m == nullptr) + return; + + const auto it = std::find(m_menus.begin(), m_menus.end(), m); + + if (it != m_menus.end()) + return; + + m_menus.push_back(m); +} + +d2_tweaks::ui::menu* d2_tweaks::ui::ui_manager::get_menu(const std::string& name) { + if (name.empty()) + return nullptr; + + //TODO: optimize somehow + for (auto menu : m_menus) { + if (menu->get_name() == name) + return menu; + } + + return nullptr; +} + +void d2_tweaks::ui::ui_manager::remove_menu(menu* m) { + if (m == nullptr) + return; + + m_menus.erase(std::remove(m_menus.begin(), m_menus.end(), m), m_menus.end()); +} + +void d2_tweaks::ui::ui_manager::draw() { + //process_inputs(); + + for (auto menu : m_menus) { + if (!menu->get_visible()) + continue; + + menu->draw(); + } +} + +LRESULT d2_tweaks::ui::ui_manager::wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + static auto& instance = singleton::instance(); + + bool block; + + switch (msg) { + case WM_LBUTTONDOWN: + { + block = instance.process_left_mouse(false); + break; + } + + case WM_LBUTTONUP: + { + block = instance.process_left_mouse(true); + break; + } + + case WM_RBUTTONDOWN: + { + block = instance.process_right_mouse(false); + break; + } + + case WM_RBUTTONUP: + { + block = instance.process_right_mouse(true); + break; + } + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + { + block = instance.process_key_event(wParam, false); + break; + } + + case WM_SYSKEYUP: + case WM_KEYUP: + { + block = instance.process_key_event(wParam, true); + break; + } + + default: return g_wnd_proc_original(hWnd, msg, wParam, lParam); + } + + if (block) + return 0; + + return g_wnd_proc_original(hWnd, msg, wParam, lParam); +} + +void d2_tweaks::ui::ui_manager::process_inputs() { + if (GetAsyncKeyState(VK_LBUTTON)) { + if (!m_was_down_before_left) { + m_was_down_before_left = true; + m_mouse_state_left = true; + + process_left_mouse(false); + } + } else if (m_was_down_before_left) { + m_was_down_before_left = false; + m_mouse_state_left = false; + + process_left_mouse(true); + } + + if (GetAsyncKeyState(VK_RBUTTON)) { + if (!m_was_down_before_right) { + m_was_down_before_right = true; + m_mouse_state_right = true; + + process_right_mouse(false); + } + } else if (m_was_down_before_right) { + m_was_down_before_right = false; + m_mouse_state_right = false; + + process_right_mouse(true); + } +} + +bool d2_tweaks::ui::ui_manager::process_left_mouse(bool up) { + auto block = false; + + for (auto menu : m_menus) { + if (!menu->get_enabled()) + continue; + + block |= menu->left_mouse(up); + } + + return block; +} + +bool d2_tweaks::ui::ui_manager::process_right_mouse(bool up) { + auto block = false; + + for (auto menu : m_menus) { + if (!menu->get_enabled()) + continue; + + block |= menu->right_mouse(up); + } + + return block; +} + +bool d2_tweaks::ui::ui_manager::process_key_event(uint32_t key, bool up) { + auto block = false; + + for (auto menu : m_menus) { + if (!menu->get_enabled()) + continue; + + block |= menu->key_event(key, up); + } + + return block; +} diff --git a/src/diablo2/d2client.cpp b/src/diablo2/d2client.cpp new file mode 100644 index 0000000..0889f74 --- /dev/null +++ b/src/diablo2/d2client.cpp @@ -0,0 +1,140 @@ +#include + +#include + +char* diablo2::d2_client::get_base() { + static auto base = reinterpret_cast(GetModuleHandle("d2client.dll")); + return base; +} + +void diablo2::d2_client::print_chat(wchar_t* string, uint32_t color) { + static wrap_func_fast print_chat(0x7C600, get_base()); + print_chat(string, color); +} + +bool diablo2::d2_client::is_lod() { + return *reinterpret_cast(get_base() + 0x1077C4) > 0; +} + + + +diablo2::structures::unit* diablo2::d2_client::get_local_player() { + static wrap_func_std get_local_player(0x883D0, get_base()); + return get_local_player(); +} + +const char* diablo2::d2_client::get_local_player_name() { + static wrap_value get_local_player_name(0x107810, get_base()); + return get_local_player_name; +} + +diablo2::structures::client_unit_list* diablo2::d2_client::get_client_unit_list() { + static auto unit_list = reinterpret_cast(get_base() + 0x11AA00); + return unit_list; +} + +int32_t diablo2::d2_client::get_view_offset_x() { + static wrap_func_std get_view_offset_x(0x15890, get_base()); + return get_view_offset_x(); +} + +int32_t diablo2::d2_client::get_view_offset_y() { + static wrap_func_std get_view_offset_y(0x158A0, get_base()); + return get_view_offset_y(); +} + +uint32_t diablo2::d2_client::get_mouse_x() { + static wrap_func_std get_mouse_x(0xB7BC0, get_base()); + return get_mouse_x(); +} + +uint32_t diablo2::d2_client::get_mouse_y() { + static wrap_func_std get_mouse_y(0xB7BD0, get_base()); + return get_mouse_y(); +} + +bool diablo2::d2_client::get_ui_window_state(const ui_window_t window) { + static auto ui_states = reinterpret_cast(get_base() + 0x11A6A8); + return ui_states[window]; +} + +void* diablo2::d2_client::get_buysellbtn() { + static wrap_func_cdecl get_buysellbtn(0x84110, get_base()); + return get_buysellbtn(); +} + + +void diablo2::d2_client::resync_vendor_inventory(structures::unit* ptNPC) { + static wrap_func_fastresync_vendor_inventory(0x578E0, get_base()); + resync_vendor_inventory(ptNPC); +} + +void diablo2::d2_client::play_sound(const uint32_t soundId, structures::unit* u, const uint32_t ticks, const BOOL prePick, const uint32_t cache) { + static wrap_func_fastplay_sound(0xB5820, get_base()); + play_sound(soundId, u, ticks, prePick, cache); +} + +diablo2::structures::unit* diablo2::d2_client::get_unit_by_guid(const int32_t guid, const int32_t type) { + static wrap_func_fast get_unit_by_guid(0x869F0, get_base()); + return get_unit_by_guid(guid, type); +} + +void diablo2::d2_client::send_to_server(void* data, const size_t size) { + static wrap_func_fast send_to_server(0xD850, get_base()); + send_to_server(data, size); +} + +bool diablo2::d2_client::cache_gfx_data(structures::gfxdata* gfxData, structures::unit* unit, + structures::cellfile* cellfFile, int32_t direction, int32_t frame, int32_t* outIndex, int8_t flags, + int32_t colorTint) { + static wrap_func_fast cache_gfx_data(0xBEC80, get_base()); + return cache_gfx_data(gfxData, unit, cellfFile, direction, frame, outIndex, flags, colorTint); +} + +diablo2::structures::cellfile* diablo2::d2_client::load_gfx_resource(char* path) { + static wrap_func_fast load_gfx_resource(0x1000, get_base()); + return load_gfx_resource(path, 0); +} + +int32_t diablo2::d2_client::unload_gfx_resource(structures::cellfile* handle) { + static wrap_func_fast unload_gfx_resource(0x1140, get_base()); + return unload_gfx_resource(handle); +} + + +int32_t diablo2::d2_client::send_to_server_7(BYTE type, DWORD num, DWORD unk1, DWORD unk2) { + static wrap_func_fast send_to_server_7(0xD9E0, get_base()); + return send_to_server_7(type, num, unk1, unk2); +} + +uint32_t diablo2::d2_client::screen_height() { + return *reinterpret_cast(get_base() + 0xD40F0); +} + +uint32_t diablo2::d2_client::screen_width() { + return *reinterpret_cast(get_base() + 0xD40F4); +} + +uint32_t diablo2::d2_client::current_vendor_id() { + return *reinterpret_cast(get_base() + 0x115CFD); +} + +uint32_t diablo2::d2_client::current_vendor_guid() { + return *reinterpret_cast(get_base() + 0x115CF5); +} + +bool diablo2::d2_client::is_gamble_open() { + return *reinterpret_cast(get_base() + 0x115D7C); +} + + +// 1 = talk +// 3 = trade or gamble +// 7 = imbue / add sockets / personalize +uint8_t diablo2::d2_client::current_interact_menu() { + return *reinterpret_cast(get_base() + 0x115C3B); +} + diff --git a/src/diablo2/d2cmp.cpp b/src/diablo2/d2cmp.cpp new file mode 100644 index 0000000..dfd1927 --- /dev/null +++ b/src/diablo2/d2cmp.cpp @@ -0,0 +1,15 @@ +#include + +#include + +#include + +char* diablo2::structures::d2_cmp::get_base() { + static const auto base = reinterpret_cast(GetModuleHandle("d2cmp.dll")); + return base; +} + +bool diablo2::structures::d2_cmp::init_gfx_data(gfxdata* gfxdata) { + static wrap_func_std_import init_gfx_data(10055, get_base()); + return init_gfx_data(gfxdata, 0, 1) > 0; +} diff --git a/src/diablo2/d2common.cpp b/src/diablo2/d2common.cpp new file mode 100644 index 0000000..a8eb3aa --- /dev/null +++ b/src/diablo2/d2common.cpp @@ -0,0 +1,252 @@ +#include +#include + +char* diablo2::d2_common::get_base() { + static auto base = reinterpret_cast(GetModuleHandle("d2common.dll")); + return base; +} + +int8_t diablo2::d2_common::get_item_page(structures::unit* item) { + static wrap_func_std_import get_item_page(10719, get_base()); + return get_item_page(item); +} + +uint32_t diablo2::d2_common::get_max_cube_recipes() { + static wrap_func_std_import get_max_cube_recipes(11233, get_base()); + return get_max_cube_recipes(); +} + +int32_t diablo2::d2_common::get_item_type_from_unit(structures::unit* item) { + static wrap_func_std_import get_item_type_from_unit(10751, get_base()); + return get_item_type_from_unit(item); +} + + +//__stdcall UNITS_GetPreviousInteractGUID(D2UnitStrc* pUnit) +int32_t diablo2::d2_common::get_previous_interact_guid(structures::unit* player) { + static wrap_func_std_import get_previous_interact_guid(10383, get_base()); + return get_previous_interact_guid(player); +} + + +//__stdcall INV_EmptyInventory(D2InventoryStrc* pInventory) +// deletes items one by one +void diablo2::d2_common::empty_inventory_1(structures::inventory* inv) { + static wrap_func_std_import empty_inventory_1(10279, get_base()); + return empty_inventory_1(inv); +} + +void diablo2::d2_common::empty_inventory_2(structures::inventory* inv) { + static wrap_func_std_import empty_inventory_2(10300, get_base()); + return empty_inventory_2(inv); +} + +//__stdcall INV_FreeTradeInventory(D2InventoryStrc* pInventory) +void diablo2::d2_common::free_trade_inventory(structures::inventory* inv) { + static wrap_func_std_import free_trade_inventory(10281, get_base()); + return free_trade_inventory(inv); +} + +diablo2::structures::unit* diablo2::d2_common::get_first_inventory_item(structures::inventory* inv) { + static wrap_func_std_import get_first_inventory_item(10277, get_base()); + return get_first_inventory_item(inv); +} + +diablo2::structures::unit* diablo2::d2_common::get_last_inventory_item(structures::inventory* inv) { + static wrap_func_std_import get_last_inventory_item(10278, get_base()); + return get_last_inventory_item(inv); +} + +diablo2::structures::unit* diablo2::d2_common::get_next_inventory_item(structures::unit* prev_item) { + static wrap_func_std_import get_next_inventory_item(10304, get_base()); + return get_next_inventory_item(prev_item); +} + + +int32_t diablo2::d2_common::get_inventory_index(structures::unit* item, char page, BOOL lod) { + static wrap_func_std_import get_inventory_index(10409, get_base()); + return get_inventory_index(item, page, lod); +} + +void* diablo2::d2_common::get_inventory_data(int32_t index, int32_t zero, char* data) { + static wrap_func_std_import get_inventory_data(10636, get_base()); + return get_inventory_data(index, zero, data); +} + +diablo2::structures::unit* diablo2::d2_common::get_item_at_cell(structures::inventory* inv, uint32_t cellx, + uint32_t celly, uint32_t* pcellx, uint32_t* pcelly, int32_t invIndex, uint8_t page) { + static wrap_func_std_import get_item_at_cell(10252, get_base()); + return get_item_at_cell(inv, cellx, celly, pcellx, pcelly, invIndex, page); +} + +uint32_t diablo2::d2_common::can_put_into_slot(structures::inventory* inv, structures::unit* item, uint32_t x, + uint32_t y, uint32_t invIndex, structures::unit** lastBlockingUnit, uint32_t* lastBlockingUnitIndex, uint8_t page) { + static wrap_func_std_import< uint32_t(structures::inventory*, structures::unit*, uint32_t, uint32_t, + uint32_t, structures::unit**, uint32_t*, uint8_t)> can_put_into_slot( + 10247, get_base()); + return can_put_into_slot(inv, item, x, y, invIndex, lastBlockingUnit, lastBlockingUnitIndex, page); +} + + +uint32_t diablo2::d2_common::get_item_type(structures::unit* item) { + static wrap_func_std_import< uint32_t(structures::unit*)>get_item_type(10751, get_base()); + return get_item_type(item); +} + + +uint32_t diablo2::d2_common::get_item_type_class(structures::unit* item) { + static wrap_func_std_import< uint32_t(structures::unit*)>get_item_type_class(10739, get_base()); + return get_item_type_class(item); +} + + +uint32_t diablo2::d2_common::get_item_primary_weapon_class(structures::unit* item) { + static wrap_func_std_import< uint32_t(structures::unit*)>get_item_type_class(10744, get_base()); + return get_item_type_class(item); +} + +uint32_t diablo2::d2_common::set_unit_mode(structures::unit* item, uint32_t mode) { + static wrap_func_std_import< uint32_t(structures::unit*, uint32_t mode)>set_unit_mode(10348, get_base()); + return set_unit_mode(item, mode); +} + + +diablo2::structures::unit* diablo2::d2_common::inv_remove_item(structures::inventory* inventory, structures::unit* item) { + static wrap_func_std_import inv_remove_item(10243, get_base()); + return inv_remove_item(inventory, item); +} + +BOOL diablo2::d2_common::inv_add_item(structures::inventory* inv, structures::unit* item, uint32_t x, uint32_t y, + uint32_t invIndex, BOOL isClient, uint8_t page) { + static wrap_func_std_import inv_add_item(10249, get_base()); + + return inv_add_item(inv, item, x, y, invIndex, isClient, page); +} + +BOOL diablo2::d2_common::inv_update_item(structures::inventory* inv, structures::unit* item, BOOL isClient) { + static wrap_func_std_import inv_update_item(10242, get_base()); + return inv_update_item(inv, item, isClient); +} + +diablo2::structures::items_line* diablo2::d2_common::get_item_record(uint32_t guid) { + static wrap_func_std_import get_item_record(10600, get_base()); + return get_item_record(guid); +} + +diablo2::structures::item_types_line* diablo2::d2_common::get_item_type_record(uint32_t typeId) { + static wrap_func_fastget_item_type_record(0x2B1A0, get_base()); + return get_item_type_record(typeId); +} + +uint32_t diablo2::d2_common::check_item_type_equiv(uint32_t itemtype, uint32_t itemtype_equiv) { + static wrap_func_std_importcheck_item_type_equiv(10730, get_base()); + return check_item_type_equiv(itemtype, itemtype_equiv); +} + +uint32_t diablo2::d2_common::get_item_unique_index(structures::unit* item) { + static wrap_func_std_import get_item_unique_index(10732, get_base()); + return get_item_unique_index(item); +} + +uint32_t diablo2::d2_common::get_maximum_character_gold(structures::unit* player) { + static wrap_func_std_import get_maximum_character_gold(10439, get_base()); + return get_maximum_character_gold(player); +} + +int32_t diablo2::d2_common::set_stat(structures::unit* unit, unit_stats_t stat, uint32_t value, int16_t param) { + static wrap_func_std_import set_stat(10517, get_base()); + return set_stat(unit, stat, value, param); +} + +int32_t diablo2::d2_common::get_stat(structures::unit* unit, unit_stats_t stat, int16_t param) { + static wrap_func_std_import get_stat(10519, get_base()); + return get_stat(unit, stat, param); +} + +int32_t diablo2::d2_common::get_stat_signed(structures::unit* unit, unit_stats_t stat, int16_t param) { + static wrap_func_std_import get_stat_signed(10520, get_base()); + return get_stat_signed(unit, stat, param); +} + +int32_t diablo2::d2_common::_10111(int32_t* x, int32_t* y) { + static wrap_func_std_import get_unk_coords(10111, get_base()); + return get_unk_coords(x, y); +} + +int32_t diablo2::d2_common::_10116(int32_t x1, int32_t y1, int32_t* x, int32_t* y) { + static wrap_func_std_import get_unk_coords2(10116, get_base()); + return get_unk_coords2(x1, y1, x, y); +} + +diablo2::structures::room* diablo2::d2_common::get_room_from_unit(structures::unit* unit) { + static wrap_func_std_import get_room_from_unit(10342, get_base()); + return get_room_from_unit(unit); +} + +int32_t diablo2::d2_common::get_unit_size_x(structures::unit* unit) { + static wrap_func_std_import get_unit_size_x(10336, get_base()); + return get_unit_size_x(unit); +} + +int32_t diablo2::d2_common::get_unit_size_y(structures::unit* unit) { + static wrap_func_std_import get_unit_size_y(10337, get_base()); + return get_unit_size_y(unit); +} + +int32_t diablo2::d2_common::get_distance_between_units(structures::unit* unit1, structures::unit* unit2) { + static wrap_func_std_import get_distance_between_units(10397, get_base()); + return get_distance_between_units(unit1, unit2); +} + +int32_t diablo2::d2_common::get_unit_x(structures::path* path) { + static wrap_func_std_import get_unit_x(10162, get_base()); + return get_unit_x(path); +} + +int32_t diablo2::d2_common::get_unit_y(structures::path* path) { + static wrap_func_std_import get_unit_y(10163, get_base()); + return get_unit_y(path); +} + +int32_t diablo2::d2_common::get_unit_precise_x(structures::unit* unit) { + static wrap_func_std_import get_unit_precise_x(10327, get_base()); + return get_unit_precise_x(unit); +} + +int32_t diablo2::d2_common::get_unit_precise_y(structures::unit* unit) { + static wrap_func_std_import get_unit_precise_y(10330, get_base()); + return get_unit_precise_y(unit); +} + +int32_t diablo2::d2_common::get_item_quality(structures::unit* item) { + static wrap_func_std_import get_item_quality(10695, get_base()); + return get_item_quality(item); +} + +diablo2::structures::unit* diablo2::d2_common::get_target_from_path(structures::path* path) { + static wrap_func_std_import get_target_from_path(10180, get_base()); + return get_target_from_path(path); +} + +void diablo2::d2_common::free_inventory(structures::inventory* inventory) { + static wrap_func_std_import free_inventory(10241, get_base()); + free_inventory(inventory); +} + +void diablo2::d2_common::refresh_unit_inventory(structures::unit* unit, bool set_update_flags) { + static wrap_func_std_import refresh_unit_inventory(10357, get_base()); + refresh_unit_inventory(unit, set_update_flags); +} + + +void diablo2::d2_common::update_trade(structures::inventory* inventory, structures::unit* item) { + static wrap_func_std_import update_trade(10283, get_base()); + update_trade(inventory, item); +} + +//void diablo2::d2_common::set_item_flags(structures::unit* item, diablo2::structures::itemflags_t dwFlag, bool bSet) { +// static wrap_func_std_import set_item_flags(10708, get_base()); +// set_item_flags(item, dwFlag, bSet); +//} \ No newline at end of file diff --git a/src/diablo2/d2game.cpp b/src/diablo2/d2game.cpp new file mode 100644 index 0000000..8e1f8f0 --- /dev/null +++ b/src/diablo2/d2game.cpp @@ -0,0 +1,141 @@ +#include +#include +#include +#include + + +char* diablo2::d2_game::get_base() { + static auto base = reinterpret_cast(GetModuleHandle("d2game.dll")); + return base; +} + +void diablo2::d2_game::enqueue_packet(structures::net_client* client, void* packet, size_t size) { + static wrap_func_fast enqueue_packet(0xC710, get_base()); + enqueue_packet(client, packet, size); +} + +diablo2::structures::npc_record* diablo2::d2_game::get_npc_record(structures::game* game, structures::unit* npc, structures::unit **ptnpc) { + static wrap_func_fastget_npc_record(0x9B910, get_base()); + return get_npc_record(game, npc, ptnpc); +} + +void diablo2::d2_game::free_gamble(structures::game* game, structures::unit* player, structures::unit* npc, structures::npc_record* npcrecord) { + static wrap_func_fastfree_gamble(0x9D190, get_base()); + free_gamble(game, player, npc, npcrecord); +} + +void diablo2::d2_game::fill_gamble(structures::game* game, structures::unit* player, structures::unit* npc, structures::npc_record* npcrecord) { + static wrap_func_fastfill_gamble(0x9A9F0, get_base()); + fill_gamble(game, player, npc, npcrecord); +} + +void diablo2::d2_game::create_vendor_cache1(structures::game* game, structures::unit* player, structures::unit* npc, uint32_t param, bool bGamble) { + static wrap_func_fastcreate_vendor_cache1(0x974F0, get_base()); + create_vendor_cache1(game, player, npc, param, bGamble); +} + +void diablo2::d2_game::create_vendor_cache2(structures::game* game, structures::unit* player, structures::unit* npc, uint32_t param, bool bGamble) { + static wrap_func_fastcreate_vendor_cache2(0x9AE20, get_base()); + create_vendor_cache2(game, player, npc, param, bGamble); +} + +diablo2::structures::unit* diablo2::d2_game::get_server_unit(structures::game* game, structures::unit_type_t type, uint32_t uniqueid) { + static wrap_func_fastget_server_unit(0x8BB00, get_base()); + return get_server_unit(game, type, uniqueid); +} + +diablo2::structures::game* diablo2::d2_game::get_game_from_client_id(int32_t id) { + static wrap_func_fast get_game_from_client_id(0x94E0, get_base()); + return get_game_from_client_id(id); +} + +// __fastcall D2CLIENTS_GetClientByClientNumber(pGame, int nNumber) +diablo2::structures::net_client* diablo2::d2_game::get_net_client_from_id(structures::game* game, int32_t id) { + static wrap_func_fastget_net_client_from_id(0x1DE0, get_base()); + return get_net_client_from_id(game, id); +} + +diablo2::structures::net_client* diablo2::d2_game::get_net_client_from_id_2(structures::game* game, int32_t id) { + static wrap_func_fastget_net_client_from_id_2(0x37B0, get_base()); + return get_net_client_from_id_2(game, id); +} + +diablo2::structures::unit* diablo2::d2_game::get_player_pet(structures::game* game, structures::unit* unit, + uint32_t type, uint32_t index) { + static wrap_func_fastget_player_pet(0x4E8B0, get_base()); + return get_player_pet(game, unit, type, index); +} + +int32_t diablo2::d2_game::identify_item(structures::game* game, structures::unit* player, structures::unit* item) { + static wrap_func_fast identify_item(0x19670, get_base()); + return identify_item(game, player, item); +} + +int32_t diablo2::d2_game::pickup_gold_pile(structures::game* game, structures::unit* unit, structures::unit* item) { + static wrap_func_fast pickup_gold_pile(0x12DD0, get_base()); + return pickup_gold_pile(game, unit, item); +} + +bool __fastcall diablo2::d2_game::pickup_item(diablo2::structures::game* game, diablo2::structures::unit* player, uint32_t guid, uint32_t* ptrNull) { + static wrap_func_fastpickup_item(0x13340, get_base()); + return pickup_item(game, player, guid, ptrNull); +} + +void diablo2::d2_game::update_inventory_items(structures::game* game, structures::unit* player) { + static wrap_func_fastupdate_inventory_items(0x14A90, get_base()); + update_inventory_items(game, player, 0); +} + +uint32_t* diablo2::d2_game::get_game_id_array_begin() { + static wrap_value game_id_array_begin(0x1147F8, get_base()); + return game_id_array_begin; +} + +uint32_t* diablo2::d2_game::get_game_id_array_end() { + static wrap_value game_id_array_end(0x1157F8, get_base()); + return game_id_array_end; +} + +diablo2::structures::game_server* diablo2::d2_game::get_game_server() { + static wrap_value game_server(0x115818, get_base()); + return *game_server; +} + +static diablo2::structures::game* (__thiscall* g_get_game)(diablo2::structures::game_server* gs, uint32_t gameId, void*) += decltype(g_get_game)(0xB6A0 + diablo2::d2_game::get_base()); + +diablo2::structures::game* diablo2::d2_game::get_game(structures::game_server* gs, uint32_t gameId) { + static wrap_func_std get_game(0xB6A0, get_base()); + return g_get_game(gs, gameId, reinterpret_cast(gs) + 0x44); +} + +diablo2::structures::unit* diablo2::d2_game::get_unit_owner(structures::game* game, structures::unit* unit) { + static wrap_func_fast get_unit_owner(0x8BB70, get_base()); + return get_unit_owner(game, unit); +} + +static void __fastcall unit_pet_iterator(diablo2::structures::game* game, diablo2::structures::unit* owner, + diablo2::structures::unit* unit, void* arg) { + const std::function* cb = + reinterpret_cast(arg); + cb->operator()(game, owner, unit); +} + +void* diablo2::d2_game::iterate_unit_pets(structures::game* game, structures::unit* unit, + const std::function& cb) { + static wrap_func_fast iterate_unit_pets(0x4E7C0, get_base()); + const auto cbref = &cb; + // ReSharper disable once CppCStyleCast + return iterate_unit_pets(game, unit, unit_pet_iterator, (void*)cbref); +} + + +uint32_t __fastcall diablo2::d2_game::transmogrify(diablo2::structures::game* game, diablo2::structures::unit* player) { + static wrap_func_fasttransmogrify(0x62130, get_base()); + return transmogrify(game, player); +} + + +// d2game:$0x60010 +// int __fastcall CRAFT_Transmogrify(D2GameStrc* pGame, D2UnitStrc* pPlayer, D2CubemainTXT* pCubeTxt, void* pUnknown) diff --git a/src/diablo2/d2gfx.cpp b/src/diablo2/d2gfx.cpp new file mode 100644 index 0000000..9d49299 --- /dev/null +++ b/src/diablo2/d2gfx.cpp @@ -0,0 +1,34 @@ +#include +#include + +char* diablo2::d2_gfx::get_base() { + static char* base = reinterpret_cast(GetModuleHandle("d2gfx.dll")); + return base; +} + +bool diablo2::d2_gfx::check_perspective_mode() { + static wrap_func_std_import check_perspective_mode(10010, get_base()); + return check_perspective_mode(); +} + +bool diablo2::d2_gfx::check_perspective_coords(int32_t x, int32_t y) { + static wrap_func_std_import check_perspective_coords(10065, get_base()); + return check_perspective_coords(x, y); +} + +int32_t diablo2::d2_gfx::adjust_perspective_coords(int32_t x, int32_t y, int32_t* adjustX, int32_t* adjustY) { + static wrap_func_std_import adjust_perspective_coords(10066, get_base()); + return adjust_perspective_coords(x, y, 0, adjustX, adjustY); +} + +int32_t diablo2::d2_gfx::get_resolution_mode() { + static wrap_func_std_import get_resolution_mode(10005, get_base()); + return get_resolution_mode(); +} + +void diablo2::d2_gfx::draw_image(structures::gfxdata* data, uint32_t x, uint32_t y, int32_t gamma, + int32_t drawType, void* palette) { + static wrap_func_std_import draw_image(10072, get_base()); + draw_image(data, x, y, gamma, drawType, palette); +} diff --git a/src/diablo2/d2lang.cpp b/src/diablo2/d2lang.cpp new file mode 100644 index 0000000..ee3827c --- /dev/null +++ b/src/diablo2/d2lang.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +char* diablo2::d2_lang::get_base() { + static auto base = reinterpret_cast(GetModuleHandle("d2lang.dll")); + return base; +} + +wchar_t* diablo2::d2_lang::get_string_from_index(short tbl_index_name_str) { + static wrap_func_fastget_string_from_index(0x3740, get_base()); + return get_string_from_index(tbl_index_name_str); +} + +//D2FUNC(D2LANG, GetStringFromIndex, wchar_t*, __fastcall, (short nTblIndex_name_str), -10004) + +//void diablo2::d2_game::enqueue_packet(structures::net_client* client, void* packet, size_t size) { +// static wrap_func_fast enqueue_packet(0xC710, get_base()); +// enqueue_packet(client, packet, size); +//} \ No newline at end of file diff --git a/src/diablo2/d2launch.cpp b/src/diablo2/d2launch.cpp new file mode 100644 index 0000000..e062fb2 --- /dev/null +++ b/src/diablo2/d2launch.cpp @@ -0,0 +1,8 @@ +#include + +#include + +char* diablo2::d2_launch::get_base() { + static char* base = reinterpret_cast(GetModuleHandle("d2launch.dll")); + return base; +} diff --git a/src/diablo2/d2net.cpp b/src/diablo2/d2net.cpp new file mode 100644 index 0000000..53c684e --- /dev/null +++ b/src/diablo2/d2net.cpp @@ -0,0 +1,17 @@ +#include +#include + +char* diablo2::d2_net::get_base() { + char* base = reinterpret_cast(GetModuleHandle("d2net.dll")); + return base; +} + +int32_t diablo2::d2_net::send_to_server(int32_t queue, void* data, size_t size) { + static wrap_func_std_import send_to_server(10005, get_base()); + return send_to_server(queue, data, size); +} + +int32_t diablo2::d2_net::send_to_client(int32_t queue, int32_t clientId, void* packet, size_t size) { + static wrap_func_std_import send_to_client(10006, get_base()); + return send_to_client(queue, clientId, packet, size); +} diff --git a/src/diablo2/d2win.cpp b/src/diablo2/d2win.cpp new file mode 100644 index 0000000..66e09f4 --- /dev/null +++ b/src/diablo2/d2win.cpp @@ -0,0 +1,57 @@ +#include +#include + +char* diablo2::d2_win::get_base() { + static const auto base = reinterpret_cast(GetModuleHandle("d2win.dll")); + return base; +} + +int32_t diablo2::d2_win::get_text_pixel_width(wchar_t* str) { + static wrap_func_fast_import get_text_pixel_width(10121, get_base()); + return get_text_pixel_width(str); +} + +void diablo2::d2_win::draw_text(wchar_t* str, uint32_t x, uint32_t y, ui_color_t color, int32_t transTbl) { + static wrap_func_fast_import draw_text(10117, get_base()); + draw_text(str, x, y, color, transTbl); +} + +void diablo2::d2_win::draw_boxed_text(wchar_t* str, uint32_t x, uint32_t y, int32_t paletteIndex, int32_t transTbl, + ui_color_t color) { + static wrap_func_fast_import draw_boxed_text(10132, get_base()); + draw_boxed_text(str, x, y, paletteIndex, transTbl, color); +} + +void diablo2::d2_win::set_popup_properties(wchar_t* str, uint32_t x, uint32_t y, ui_color_t color, int32_t align) { + static wrap_func_fast_import hover_text(10129, get_base()); + hover_text(str, x, y, color, align); +} + +void diablo2::d2_win::draw_popup() { + static wrap_func_cdecl_import draw_popup(10124, get_base()); + draw_popup(); +} + +diablo2::ui_font_t diablo2::d2_win::get_current_font() { + return static_cast(*reinterpret_cast(get_base() + 0x1DB24)); +} + +int32_t diablo2::d2_win::get_current_font_height() { + static wrap_func_cdecl_import get_current_font_height(10125, get_base()); + return get_current_font_height(); +} + +int32_t diablo2::d2_win::set_current_font(ui_font_t font) { + static wrap_func_fast_import set_current_font(10127, get_base()); + return set_current_font(font); +} + +void* diablo2::d2_win::load_mpq(char* dllName, char* mpqName, char* mpqTitle, int32_t overrideFlags) { + static wrap_func_fast load_mpq(0x12399, get_base()); + return load_mpq(dllName, mpqName, mpqTitle, 0, 0, 0, overrideFlags); +} + +bool diablo2::d2_win::unload_mpq(void* mpq) { + static wrap_func_fast unload_mpq(0x012548, get_base()); + return unload_mpq(mpq); +} diff --git a/src/diablo2/fog.cpp b/src/diablo2/fog.cpp new file mode 100644 index 0000000..f9c5bab --- /dev/null +++ b/src/diablo2/fog.cpp @@ -0,0 +1,32 @@ +#include +#include + +char* diablo2::fog::get_base() { + static auto base = reinterpret_cast(GetModuleHandle("fog.dll")); + return base; +} + +void diablo2::fog::get_save_path(char* buffer, size_t bufferSize) { + static wrap_func_fast_import get_save_path(10115, get_base()); + return get_save_path(buffer, bufferSize); +} + +bool diablo2::fog::mpq_open_file(char* path, structures::file_handle** outHandle) { + static wrap_func_fast_import mpq_open_file(10102, get_base()); + return mpq_open_file(path, outHandle); +} + +bool diablo2::fog::mpq_close_file(structures::file_handle* handle) { + static wrap_func_fast_import mpq_close_file(10103, get_base()); + return mpq_close_file(handle); +} + +bool diablo2::fog::mpq_read_file(structures::file_handle* handle, void* buffer, size_t size, size_t* bytesToRead) { + static wrap_func_fast_import mpq_read_fiile(10104, get_base()); + return mpq_read_fiile(handle, buffer, size, bytesToRead, 0, 0, 0); +} + +size_t diablo2::fog::mpq_get_file_size(structures::file_handle* handle, size_t* compressedSize) { + static wrap_func_fast_import mpq_get_file_size(10105, get_base()); + return mpq_get_file_size(handle, compressedSize); +} diff --git a/src/diablo2/storm.cpp b/src/diablo2/storm.cpp new file mode 100644 index 0000000..f9929c9 --- /dev/null +++ b/src/diablo2/storm.cpp @@ -0,0 +1,8 @@ +#include + +#include + +char* diablo2::storm::get_base() { + static auto base = reinterpret_cast(GetModuleHandle("storm.dll")); + return base; +} diff --git a/src/diablo2/utils/mpq_ifstream.cpp b/src/diablo2/utils/mpq_ifstream.cpp new file mode 100644 index 0000000..6539b1a --- /dev/null +++ b/src/diablo2/utils/mpq_ifstream.cpp @@ -0,0 +1,28 @@ +#include +#include + +#include + +diablo2::utils::mpq_ifstream::mpq_streambuf::mpq_streambuf(const std::string& path) { + fog::mpq_open_file(const_cast(path.c_str()), &m_handle); +} + +diablo2::utils::mpq_ifstream::mpq_streambuf::~mpq_streambuf() { + fog::mpq_close_file(m_handle); +} + +std::basic_streambuf::int_type diablo2::utils::mpq_ifstream::mpq_streambuf::underflow() { + if (!m_handle) + return EOF; + + if (!fog::mpq_read_file(m_handle, &m_data, 1, nullptr)) + return EOF; + + setg(&m_data, &m_data, &m_data + 1); + + return m_data; +} + +diablo2::utils::mpq_ifstream::mpq_ifstream(const std::string& path) : std::istream(&m_streambuf), m_streambuf(path) { + +} diff --git a/src/diablo2/utils/screen.cpp b/src/diablo2/utils/screen.cpp new file mode 100644 index 0000000..7ec8ebb --- /dev/null +++ b/src/diablo2/utils/screen.cpp @@ -0,0 +1,27 @@ +#include + +#include +#include + +void diablo2::utils::screen::screen_to_world(int32_t sx, int32_t sy, int32_t& wx, int32_t& wy) { + const auto x = sx + d2_client::get_view_offset_x(); + const auto y = sy + d2_client::get_view_offset_y(); + + auto tx = 0, ty = 0; + + d2_common::_10116(x, y, &tx, &ty); + d2_common::_10116(x, y, &tx, &ty); + + wx = tx; + wy = ty; +} + +void diablo2::utils::screen::world_to_screen(int32_t wx, int32_t wy, int32_t& sx, int32_t& sy) { + auto x = wx; + auto y = wy; + + d2_common::_10111(&x, &y); + + sx = x - d2_client::get_view_offset_x(); + sy = y - d2_client::get_view_offset_y(); +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..48b90a4 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,113 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#pragma comment(lib, "vcruntime.lib") +#pragma comment(lib, "mincore_downlevel.lib") + + +void init_log() { + const auto console_err = std::make_shared(); + const auto logPath = "d2tweaks.log"; + const auto file = std::make_shared(logPath); + + // ReSharper disable once CppSmartPointerVsMakeFunction + auto logger = std::shared_ptr(new spdlog::logger("d2tweaks", { file, console_err })); + + logger->flush_on(spdlog::level::trace); + + set_default_logger(logger); + +#ifndef NDEBUG + spdlog::set_level(spdlog::level::debug); +#else + spdlog::set_level(spdlog::level::info); +#endif + + spdlog::info("Log system initialized"); +} + + +void initialize(uint32_t param) { +#ifndef NDEBUG + AllocConsole(); + // ReSharper disable once CppDeprecatedEntity + freopen("CONOUT$", "w", stdout); + // ReSharper disable once CppDeprecatedEntity + freopen("CONOUT$", "w", stderr); +#endif + + init_log(); + D2TEMPLATE_Init(); + + if (MH_Initialize() != MH_OK) { + MessageBox(nullptr, "Cannot initialize hook system!", "Error", MB_OK | MB_ICONSTOP); + exit(0); + } + + singleton::instance().init(); + singleton::instance().init(); + singleton::instance().init(); + + MH_EnableHook(nullptr); +} + +//extern "C" { +//#pragma comment(linker, "/EXPORT:_Init@0=_init_plugy@0") +// __declspec(dllexport) void __stdcall init_plugy() { +// initialize(); +// } +// +//#pragma comment(linker, "/EXPORT:_Init@4=_init_d2_mod@4") +// __declspec(dllexport) void* __stdcall init_d2_mod(const char* a1) { +// initialize(); +// return nullptr; +// } +//} + +HANDLE g_hThread = 0; +// ReSharper disable once CppInconsistentNaming +BOOL APIENTRY DllMain(HMODULE moduleHandle, + DWORD reason, + LPVOID reserved) { + // ReSharper disable once CppDefaultCaseNotHandledInSwitchStatement + switch (reason) { + case DLL_PROCESS_ATTACH: + { + //dllnotify::DllNotify::Init_Dllnotify(); + DisableThreadLibraryCalls(moduleHandle); + initialize(0); + break; + } + + case DLL_PROCESS_DETACH: + { + if (g_hThread != NULL) { + #ifndef NDEBUG + FreeConsole(); + #endif + CloseHandle(g_hThread); + MH_Uninitialize(); + //dllnotify::DllNotify::Uninit_Dllnotify(); + } + break; + } + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + default: + break; + } + return true; +} diff --git a/vcpkg/fmt_x86-windows-static/BUILD_INFO b/vcpkg/fmt_x86-windows-static/BUILD_INFO new file mode 100644 index 0000000..eacd5b8 --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/BUILD_INFO @@ -0,0 +1,2 @@ +CRTLinkage: static +LibraryLinkage: static diff --git a/vcpkg/fmt_x86-windows-static/CONTROL b/vcpkg/fmt_x86-windows-static/CONTROL new file mode 100644 index 0000000..4e9451b --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/CONTROL @@ -0,0 +1,9 @@ +Package: fmt +Version: 7.1.3 +Port-Version: 4 +Depends: vcpkg-cmake:x64-windows, vcpkg-cmake-config:x64-windows +Architecture: x86-windows-static +Multi-Arch: same +Abi: a3dbf9569efb482109d3f51a949fe8e273232de4 +Description: Formatting library for C++. It can be used as a safe alternative to printf or as a fast alternative to IOStreams. +Type: Port diff --git a/vcpkg/fmt_x86-windows-static/debug/lib/fmtd.lib b/vcpkg/fmt_x86-windows-static/debug/lib/fmtd.lib new file mode 100644 index 0000000..684c211 Binary files /dev/null and b/vcpkg/fmt_x86-windows-static/debug/lib/fmtd.lib differ diff --git a/vcpkg/fmt_x86-windows-static/debug/lib/pkgconfig/fmt.pc b/vcpkg/fmt_x86-windows-static/debug/lib/pkgconfig/fmt.pc new file mode 100644 index 0000000..98237ad --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/debug/lib/pkgconfig/fmt.pc @@ -0,0 +1,12 @@ +prefix=${pcfiledir}/../.. + +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/../include + +Name: fmt +Description: A modern formatting library +Version: 7.1.3 +Libs: -L"${libdir}" -lfmtd +Cflags: -I"${includedir}" + diff --git a/vcpkg/fmt_x86-windows-static/include/fmt/chrono.h b/vcpkg/fmt_x86-windows-static/include/fmt/chrono.h new file mode 100644 index 0000000..1a3b8d5 --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/include/fmt/chrono.h @@ -0,0 +1,1118 @@ +// Formatting library for C++ - chrono support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CHRONO_H_ +#define FMT_CHRONO_H_ + +#include +#include +#include +#include + +#include "format.h" +#include "locale.h" + +FMT_BEGIN_NAMESPACE + +// Enable safe chrono durations, unless explicitly disabled. +#ifndef FMT_SAFE_DURATION_CAST +# define FMT_SAFE_DURATION_CAST 1 +#endif +#if FMT_SAFE_DURATION_CAST + +// For conversion between std::chrono::durations without undefined +// behaviour or erroneous results. +// This is a stripped down version of duration_cast, for inclusion in fmt. +// See https://github.com/pauldreik/safe_duration_cast +// +// Copyright Paul Dreik 2019 +namespace safe_duration_cast { + +template ::value && + std::numeric_limits::is_signed == + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + // A and B are both signed, or both unsigned. + if (F::digits <= T::digits) { + // From fits in To without any problem. + } else { + // From does not always fit in To, resort to a dynamic check. + if (from < (T::min)() || from > (T::max)()) { + // outside range. + ec = 1; + return {}; + } + } + return static_cast(from); +} + +/** + * converts From to To, without loss. If the dynamic value of from + * can't be converted to To without loss, ec is set. + */ +template ::value && + std::numeric_limits::is_signed != + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + if (detail::const_check(F::is_signed && !T::is_signed)) { + // From may be negative, not allowed! + if (fmt::detail::is_negative(from)) { + ec = 1; + return {}; + } + // From is positive. Can it always fit in To? + if (F::digits > T::digits && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + } + + if (!F::is_signed && T::is_signed && F::digits >= T::digits && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + return static_cast(from); // Lossless conversion. +} + +template ::value)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + return from; +} // function + +// clang-format off +/** + * converts From to To if possible, otherwise ec is set. + * + * input | output + * ---------------------------------|--------------- + * NaN | NaN + * Inf | Inf + * normal, fits in output | converted (possibly lossy) + * normal, does not fit in output | ec is set + * subnormal | best effort + * -Inf | -Inf + */ +// clang-format on +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + using T = std::numeric_limits; + static_assert(std::is_floating_point::value, "From must be floating"); + static_assert(std::is_floating_point::value, "To must be floating"); + + // catch the only happy case + if (std::isfinite(from)) { + if (from >= T::lowest() && from <= (T::max)()) { + return static_cast(from); + } + // not within range. + ec = 1; + return {}; + } + + // nan and inf will be preserved + return static_cast(from); +} // function + +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + static_assert(std::is_floating_point::value, "From must be floating"); + return from; +} + +/** + * safe duration cast between integral durations + */ +template ::value), + FMT_ENABLE_IF(std::is_integral::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // safe conversion to IntermediateRep + IntermediateRep count = + lossless_integral_conversion(from.count(), ec); + if (ec) return {}; + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + const auto max1 = detail::max_value() / Factor::num; + if (count > max1) { + ec = 1; + return {}; + } + const auto min1 = + (std::numeric_limits::min)() / Factor::num; + if (count < min1) { + ec = 1; + return {}; + } + count *= Factor::num; + } + + if (detail::const_check(Factor::den != 1)) count /= Factor::den; + auto tocount = lossless_integral_conversion(count, ec); + return ec ? To() : To(tocount); +} + +/** + * safe duration_cast between floating point durations + */ +template ::value), + FMT_ENABLE_IF(std::is_floating_point::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + if (std::isnan(from.count())) { + // nan in, gives nan out. easy. + return To{std::numeric_limits::quiet_NaN()}; + } + // maybe we should also check if from is denormal, and decide what to do about + // it. + + // +-inf should be preserved. + if (std::isinf(from.count())) { + return To{from.count()}; + } + + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // force conversion of From::rep -> IntermediateRep to be safe, + // even if it will never happen be narrowing in this context. + IntermediateRep count = + safe_float_conversion(from.count(), ec); + if (ec) { + return {}; + } + + // multiply with Factor::num without overflow or underflow + if (Factor::num != 1) { + constexpr auto max1 = detail::max_value() / + static_cast(Factor::num); + if (count > max1) { + ec = 1; + return {}; + } + constexpr auto min1 = std::numeric_limits::lowest() / + static_cast(Factor::num); + if (count < min1) { + ec = 1; + return {}; + } + count *= static_cast(Factor::num); + } + + // this can't go wrong, right? den>0 is checked earlier. + if (Factor::den != 1) { + using common_t = typename std::common_type::type; + count /= static_cast(Factor::den); + } + + // convert to the to type, safely + using ToRep = typename To::rep; + + const ToRep tocount = safe_float_conversion(count, ec); + if (ec) { + return {}; + } + return To{tocount}; +} +} // namespace safe_duration_cast +#endif + +// Prevents expansion of a preceding token as a function-style macro. +// Usage: f FMT_NOMACRO() +#define FMT_NOMACRO + +namespace detail { +inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } +inline null<> localtime_s(...) { return null<>(); } +inline null<> gmtime_r(...) { return null<>(); } +inline null<> gmtime_s(...) { return null<>(); } +} // namespace detail + +// Thread-safe replacement for std::localtime +inline std::tm localtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(localtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(localtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VER + bool fallback(detail::null<>) { + using namespace fmt::detail; + std::tm* tm = std::localtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher lt(time); + // Too big time values may be unsupported. + if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); + return lt.tm_; +} + +inline std::tm localtime( + std::chrono::time_point time_point) { + return localtime(std::chrono::system_clock::to_time_t(time_point)); +} + +// Thread-safe replacement for std::gmtime +inline std::tm gmtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(gmtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(gmtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VER + bool fallback(detail::null<>) { + std::tm* tm = std::gmtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher gt(time); + // Too big time values may be unsupported. + if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); + return gt.tm_; +} + +inline std::tm gmtime( + std::chrono::time_point time_point) { + return gmtime(std::chrono::system_clock::to_time_t(time_point)); +} + +namespace detail { +inline size_t strftime(char* str, size_t count, const char* format, + const std::tm* time) { + return std::strftime(str, count, format, time); +} + +inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format, + const std::tm* time) { + return std::wcsftime(str, count, format, time); +} +} // namespace detail + +template +struct formatter, Char> + : formatter { + template + auto format(std::chrono::time_point val, + FormatContext& ctx) -> decltype(ctx.out()) { + std::tm time = localtime(val); + return formatter::format(time, ctx); + } +}; + +template struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') ++it; + auto end = it; + while (end != ctx.end() && *end != '}') ++end; + tm_format.reserve(detail::to_unsigned(end - it + 1)); + tm_format.append(it, end); + tm_format.push_back('\0'); + return end; + } + + template + auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { + basic_memory_buffer buf; + size_t start = buf.size(); + for (;;) { + size_t size = buf.capacity() - start; + size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm); + if (count != 0) { + buf.resize(start + count); + break; + } + if (size >= tm_format.size() * 256) { + // If the buffer is 256 times larger than the format string, assume + // that `strftime` gives an empty result. There doesn't seem to be a + // better way to distinguish the two cases: + // https://github.com/fmtlib/fmt/issues/367 + break; + } + const size_t MIN_GROWTH = 10; + buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); + } + return std::copy(buf.begin(), buf.end(), ctx.out()); + } + + basic_memory_buffer tm_format; +}; + +namespace detail { +template FMT_CONSTEXPR const char* get_units() { + return nullptr; +} +template <> FMT_CONSTEXPR const char* get_units() { return "as"; } +template <> FMT_CONSTEXPR const char* get_units() { return "fs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ps"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ns"; } +template <> FMT_CONSTEXPR const char* get_units() { return "µs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ms"; } +template <> FMT_CONSTEXPR const char* get_units() { return "cs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ds"; } +template <> FMT_CONSTEXPR const char* get_units>() { return "s"; } +template <> FMT_CONSTEXPR const char* get_units() { return "das"; } +template <> FMT_CONSTEXPR const char* get_units() { return "hs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ks"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ms"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Gs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ts"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ps"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Es"; } +template <> FMT_CONSTEXPR const char* get_units>() { + return "m"; +} +template <> FMT_CONSTEXPR const char* get_units>() { + return "h"; +} + +enum class numeric_system { + standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. + alternative +}; + +// Parses a put_time-like format string and invokes handler actions. +template +FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, + const Char* end, + Handler&& handler) { + auto ptr = begin; + while (ptr != end) { + auto c = *ptr; + if (c == '}') break; + if (c != '%') { + ++ptr; + continue; + } + if (begin != ptr) handler.on_text(begin, ptr); + ++ptr; // consume '%' + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case '%': + handler.on_text(ptr - 1, ptr); + break; + case 'n': { + const Char newline[] = {'\n'}; + handler.on_text(newline, newline + 1); + break; + } + case 't': { + const Char tab[] = {'\t'}; + handler.on_text(tab, tab + 1); + break; + } + // Day of the week: + case 'a': + handler.on_abbr_weekday(); + break; + case 'A': + handler.on_full_weekday(); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::standard); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::standard); + break; + // Month: + case 'b': + handler.on_abbr_month(); + break; + case 'B': + handler.on_full_month(); + break; + // Hour, minute, second: + case 'H': + handler.on_24_hour(numeric_system::standard); + break; + case 'I': + handler.on_12_hour(numeric_system::standard); + break; + case 'M': + handler.on_minute(numeric_system::standard); + break; + case 'S': + handler.on_second(numeric_system::standard); + break; + // Other: + case 'c': + handler.on_datetime(numeric_system::standard); + break; + case 'x': + handler.on_loc_date(numeric_system::standard); + break; + case 'X': + handler.on_loc_time(numeric_system::standard); + break; + case 'D': + handler.on_us_date(); + break; + case 'F': + handler.on_iso_date(); + break; + case 'r': + handler.on_12_hour_time(); + break; + case 'R': + handler.on_24_hour_time(); + break; + case 'T': + handler.on_iso_time(); + break; + case 'p': + handler.on_am_pm(); + break; + case 'Q': + handler.on_duration_value(); + break; + case 'q': + handler.on_duration_unit(); + break; + case 'z': + handler.on_utc_offset(); + break; + case 'Z': + handler.on_tz_name(); + break; + // Alternative representation: + case 'E': { + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'c': + handler.on_datetime(numeric_system::alternative); + break; + case 'x': + handler.on_loc_date(numeric_system::alternative); + break; + case 'X': + handler.on_loc_time(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + } + case 'O': + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'w': + handler.on_dec0_weekday(numeric_system::alternative); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::alternative); + break; + case 'H': + handler.on_24_hour(numeric_system::alternative); + break; + case 'I': + handler.on_12_hour(numeric_system::alternative); + break; + case 'M': + handler.on_minute(numeric_system::alternative); + break; + case 'S': + handler.on_second(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + default: + FMT_THROW(format_error("invalid format")); + } + begin = ptr; + } + if (begin != ptr) handler.on_text(begin, ptr); + return ptr; +} + +struct chrono_format_checker { + FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } + + template void on_text(const Char*, const Char*) {} + FMT_NORETURN void on_abbr_weekday() { report_no_date(); } + FMT_NORETURN void on_full_weekday() { report_no_date(); } + FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } + FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } + FMT_NORETURN void on_abbr_month() { report_no_date(); } + FMT_NORETURN void on_full_month() { report_no_date(); } + void on_24_hour(numeric_system) {} + void on_12_hour(numeric_system) {} + void on_minute(numeric_system) {} + void on_second(numeric_system) {} + FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } + FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } + FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } + FMT_NORETURN void on_us_date() { report_no_date(); } + FMT_NORETURN void on_iso_date() { report_no_date(); } + void on_12_hour_time() {} + void on_24_hour_time() {} + void on_iso_time() {} + void on_am_pm() {} + void on_duration_value() {} + void on_duration_unit() {} + FMT_NORETURN void on_utc_offset() { report_no_date(); } + FMT_NORETURN void on_tz_name() { report_no_date(); } +}; + +template ::value)> +inline bool isnan(T) { + return false; +} +template ::value)> +inline bool isnan(T value) { + return std::isnan(value); +} + +template ::value)> +inline bool isfinite(T) { + return true; +} +template ::value)> +inline bool isfinite(T value) { + return std::isfinite(value); +} + +// Converts value to int and checks that it's in the range [0, upper). +template ::value)> +inline int to_nonnegative_int(T value, int upper) { + FMT_ASSERT(value >= 0 && value <= upper, "invalid value"); + (void)upper; + return static_cast(value); +} +template ::value)> +inline int to_nonnegative_int(T value, int upper) { + FMT_ASSERT( + std::isnan(value) || (value >= 0 && value <= static_cast(upper)), + "invalid value"); + (void)upper; + return static_cast(value); +} + +template ::value)> +inline T mod(T x, int y) { + return x % static_cast(y); +} +template ::value)> +inline T mod(T x, int y) { + return std::fmod(x, static_cast(y)); +} + +// If T is an integral type, maps T to its unsigned counterpart, otherwise +// leaves it unchanged (unlike std::make_unsigned). +template ::value> +struct make_unsigned_or_unchanged { + using type = T; +}; + +template struct make_unsigned_or_unchanged { + using type = typename std::make_unsigned::type; +}; + +#if FMT_SAFE_DURATION_CAST +// throwing version of safe_duration_cast +template +To fmt_safe_duration_cast(std::chrono::duration from) { + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +} +#endif + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + using CommonSecondsType = + typename std::common_type::type; + const auto d_as_common = fmt_safe_duration_cast(d); + const auto d_as_whole_seconds = + fmt_safe_duration_cast(d_as_common); + // this conversion should be nonproblematic + const auto diff = d_as_common - d_as_whole_seconds; + const auto ms = + fmt_safe_duration_cast>(diff); + return ms; +#else + auto s = std::chrono::duration_cast(d); + return std::chrono::duration_cast(d - s); +#endif +} + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + using common_type = typename std::common_type::type; + auto ms = mod(d.count() * static_cast(Period::num) / + static_cast(Period::den) * 1000, + 1000); + return std::chrono::duration(static_cast(ms)); +} + +template +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; + if (precision >= 0) return format_to(out, pr_f, val, precision); + const Char fp_f[] = {'{', ':', 'g', '}', 0}; + const Char format[] = {'{', '}', 0}; + return format_to(out, std::is_floating_point::value ? fp_f : format, + val); +} +template +OutputIt copy_unit(string_view unit, OutputIt out, Char) { + return std::copy(unit.begin(), unit.end(), out); +} + +template +OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { + // This works when wchar_t is UTF-32 because units only contain characters + // that have the same representation in UTF-16 and UTF-32. + utf8_to_utf16 u(unit); + return std::copy(u.c_str(), u.c_str() + u.size(), out); +} + +template +OutputIt format_duration_unit(OutputIt out) { + if (const char* unit = get_units()) + return copy_unit(string_view(unit), out, Char()); + const Char num_f[] = {'[', '{', '}', ']', 's', 0}; + if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num); + const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; + return format_to(out, num_def_f, Period::num, Period::den); +} + +template +struct chrono_formatter { + FormatContext& context; + OutputIt out; + int precision; + // rep is unsigned to avoid overflow. + using rep = + conditional_t::value && sizeof(Rep) < sizeof(int), + unsigned, typename make_unsigned_or_unchanged::type>; + rep val; + using seconds = std::chrono::duration; + seconds s; + using milliseconds = std::chrono::duration; + bool negative; + + using char_type = typename FormatContext::char_type; + + explicit chrono_formatter(FormatContext& ctx, OutputIt o, + std::chrono::duration d) + : context(ctx), + out(o), + val(static_cast(d.count())), + negative(false) { + if (d.count() < 0) { + val = 0 - val; + negative = true; + } + + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + // might need checked conversion (rep!=Rep) + auto tmpval = std::chrono::duration(val); + s = fmt_safe_duration_cast(tmpval); +#else + s = std::chrono::duration_cast( + std::chrono::duration(val)); +#endif + } + + // returns true if nan or inf, writes to out. + bool handle_nan_inf() { + if (isfinite(val)) { + return false; + } + if (isnan(val)) { + write_nan(); + return true; + } + // must be +-inf + if (val > 0) { + write_pinf(); + } else { + write_ninf(); + } + return true; + } + + Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + + Rep hour12() const { + Rep hour = static_cast(mod((s.count() / 3600), 12)); + return hour <= 0 ? 12 : hour; + } + + Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } + Rep second() const { return static_cast(mod(s.count(), 60)); } + + std::tm time() const { + auto time = std::tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + time.tm_min = to_nonnegative_int(minute(), 60); + time.tm_sec = to_nonnegative_int(second(), 60); + return time; + } + + void write_sign() { + if (negative) { + *out++ = '-'; + negative = false; + } + } + + void write(Rep value, int width) { + write_sign(); + if (isnan(value)) return write_nan(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(value, max_value())); + int num_digits = detail::count_digits(n); + if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); + out = format_decimal(out, n, num_digits).end; + } + + void write_nan() { std::copy_n("nan", 3, out); } + void write_pinf() { std::copy_n("inf", 3, out); } + void write_ninf() { std::copy_n("-inf", 4, out); } + + void format_localized(const tm& time, char format, char modifier = 0) { + if (isnan(val)) return write_nan(); + auto locale = context.locale().template get(); + auto& facet = std::use_facet>(locale); + std::basic_ostringstream os; + os.imbue(locale); + facet.put(os, os, ' ', &time, format, modifier); + auto str = os.str(); + std::copy(str.begin(), str.end(), out); + } + + void on_text(const char_type* begin, const char_type* end) { + std::copy(begin, end, out); + } + + // These are not implemented because durations don't have date information. + void on_abbr_weekday() {} + void on_full_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} + void on_abbr_month() {} + void on_full_month() {} + void on_datetime(numeric_system) {} + void on_loc_date(numeric_system) {} + void on_loc_time(numeric_system) {} + void on_us_date() {} + void on_iso_date() {} + void on_utc_offset() {} + void on_tz_name() {} + + void on_24_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_localized(time, 'H', 'O'); + } + + void on_12_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_localized(time, 'I', 'O'); + } + + void on_minute(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_localized(time, 'M', 'O'); + } + + void on_second(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + write(second(), 2); +#if FMT_SAFE_DURATION_CAST + // convert rep->Rep + using duration_rep = std::chrono::duration; + using duration_Rep = std::chrono::duration; + auto tmpval = fmt_safe_duration_cast(duration_rep{val}); +#else + auto tmpval = std::chrono::duration(val); +#endif + auto ms = get_milliseconds(tmpval); + if (ms != std::chrono::milliseconds(0)) { + *out++ = '.'; + write(ms.count(), 3); + } + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_localized(time, 'S', 'O'); + } + + void on_12_hour_time() { + if (handle_nan_inf()) return; + format_localized(time(), 'r'); + } + + void on_24_hour_time() { + if (handle_nan_inf()) { + *out++ = ':'; + handle_nan_inf(); + return; + } + + write(hour(), 2); + *out++ = ':'; + write(minute(), 2); + } + + void on_iso_time() { + on_24_hour_time(); + *out++ = ':'; + if (handle_nan_inf()) return; + write(second(), 2); + } + + void on_am_pm() { + if (handle_nan_inf()) return; + format_localized(time(), 'p'); + } + + void on_duration_value() { + if (handle_nan_inf()) return; + write_sign(); + out = format_duration_value(out, val, precision); + } + + void on_duration_unit() { + out = format_duration_unit(out); + } +}; +} // namespace detail + +template +struct formatter, Char> { + private: + basic_format_specs specs; + int precision; + using arg_ref_type = detail::arg_ref; + arg_ref_type width_ref; + arg_ref_type precision_ref; + mutable basic_string_view format_str; + using duration = std::chrono::duration; + + struct spec_handler { + formatter& f; + basic_format_parse_context& context; + basic_string_view format_str; + + template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { + return arg_ref_type(context.next_arg_id()); + } + + void on_error(const char* msg) { FMT_THROW(format_error(msg)); } + void on_fill(basic_string_view fill) { f.specs.fill = fill; } + void on_align(align_t align) { f.specs.align = align; } + void on_width(int width) { f.specs.width = width; } + void on_precision(int _precision) { f.precision = _precision; } + void end_precision() {} + + template void on_dynamic_width(Id arg_id) { + f.width_ref = make_arg_ref(arg_id); + } + + template void on_dynamic_precision(Id arg_id) { + f.precision_ref = make_arg_ref(arg_id); + } + }; + + using iterator = typename basic_format_parse_context::iterator; + struct parse_range { + iterator begin; + iterator end; + }; + + FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return {begin, begin}; + spec_handler handler{*this, ctx, format_str}; + begin = detail::parse_align(begin, end, handler); + if (begin == end) return {begin, begin}; + begin = detail::parse_width(begin, end, handler); + if (begin == end) return {begin, begin}; + if (*begin == '.') { + if (std::is_floating_point::value) + begin = detail::parse_precision(begin, end, handler); + else + handler.on_error("precision not allowed for this argument type"); + } + end = parse_chrono_format(begin, end, detail::chrono_format_checker()); + return {begin, end}; + } + + public: + formatter() : precision(-1) {} + + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto range = do_parse(ctx); + format_str = basic_string_view( + &*range.begin, detail::to_unsigned(range.end - range.begin)); + return range.end; + } + + template + auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) { + auto begin = format_str.begin(), end = format_str.end(); + // As a possible future optimization, we could avoid extra copying if width + // is not specified. + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs.width, width_ref, + ctx); + detail::handle_dynamic_spec(precision, + precision_ref, ctx); + if (begin == end || *begin == '}') { + out = detail::format_duration_value(out, d.count(), precision); + detail::format_duration_unit(out); + } else { + detail::chrono_formatter f( + ctx, out, d); + f.precision = precision; + parse_chrono_format(begin, end, f); + } + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + } +}; + +FMT_END_NAMESPACE + +#endif // FMT_CHRONO_H_ diff --git a/vcpkg/fmt_x86-windows-static/include/fmt/color.h b/vcpkg/fmt_x86-windows-static/include/fmt/color.h new file mode 100644 index 0000000..94e3419 --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/include/fmt/color.h @@ -0,0 +1,603 @@ +// Formatting library for C++ - color support +// +// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COLOR_H_ +#define FMT_COLOR_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +enum class color : uint32_t { + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aquamarine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32 // rgb(154,205,50) +}; // enum class color + +enum class terminal_color : uint8_t { + black = 30, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black = 90, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white +}; + +enum class emphasis : uint8_t { + bold = 1, + italic = 1 << 1, + underline = 1 << 2, + strikethrough = 1 << 3 +}; + +// rgb is a struct for red, green and blue colors. +// Using the name "rgb" makes some editors show the color in a tooltip. +struct rgb { + FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} + FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + FMT_CONSTEXPR rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} + FMT_CONSTEXPR rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF), + g((uint32_t(hex) >> 8) & 0xFF), + b(uint32_t(hex) & 0xFF) {} + uint8_t r; + uint8_t g; + uint8_t b; +}; + +namespace detail { + +// color is a struct of either a rgb color or a terminal color. +struct color_type { + FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} + FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), + value{} { + value.rgb_color = static_cast(rgb_color); + } + FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { + value.rgb_color = (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b; + } + FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), + value{} { + value.term_color = static_cast(term_color); + } + bool is_rgb; + union color_union { + uint8_t term_color; + uint32_t rgb_color; + } value; +}; +} // namespace detail + +// Experimental text formatting support. +class text_style { + public: + FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems(em) {} + + FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + background_color.value.rgb_color |= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) | + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator|(text_style lhs, + const text_style& rhs) { + return lhs |= rhs; + } + + FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't AND a terminal color")); + foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't AND a terminal color")); + background_color.value.rgb_color &= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) & + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator&(text_style lhs, + const text_style& rhs) { + return lhs &= rhs; + } + + FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { + return set_foreground_color; + } + FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { + return set_background_color; + } + FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { + return static_cast(ems) != 0; + } + FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { + FMT_ASSERT(has_foreground(), "no foreground specified for this style"); + return foreground_color; + } + FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { + FMT_ASSERT(has_background(), "no background specified for this style"); + return background_color; + } + FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { + FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); + return ems; + } + + private: + FMT_CONSTEXPR text_style(bool is_foreground, + detail::color_type text_color) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems() { + if (is_foreground) { + foreground_color = text_color; + set_foreground_color = true; + } else { + background_color = text_color; + set_background_color = true; + } + } + + friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) + FMT_NOEXCEPT; + friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) + FMT_NOEXCEPT; + + detail::color_type foreground_color; + detail::color_type background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { + return text_style(/*is_foreground=*/true, foreground); +} + +FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { + return text_style(/*is_foreground=*/false, background); +} + +FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { + return text_style(lhs) | rhs; +} + +namespace detail { + +template struct ansi_color_escape { + FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, + const char* esc) FMT_NOEXCEPT { + // If we have a terminal color, we need to output another escape code + // sequence. + if (!text_color.is_rgb) { + bool is_background = esc == detail::data::background_color; + uint32_t value = text_color.value.term_color; + // Background ASCII codes are the same as the foreground ones but with + // 10 more. + if (is_background) value += 10u; + + size_t index = 0; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + + if (value >= 100u) { + buffer[index++] = static_cast('1'); + value %= 100u; + } + buffer[index++] = static_cast('0' + value / 10u); + buffer[index++] = static_cast('0' + value % 10u); + + buffer[index++] = static_cast('m'); + buffer[index++] = static_cast('\0'); + return; + } + + for (int i = 0; i < 7; i++) { + buffer[i] = static_cast(esc[i]); + } + rgb color(text_color.value.rgb_color); + to_esc(color.r, buffer + 7, ';'); + to_esc(color.g, buffer + 11, ';'); + to_esc(color.b, buffer + 15, 'm'); + buffer[19] = static_cast(0); + } + FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { + uint8_t em_codes[4] = {}; + uint8_t em_bits = static_cast(em); + if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; + if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; + if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; + if (em_bits & static_cast(emphasis::strikethrough)) + em_codes[3] = 9; + + size_t index = 0; + for (int i = 0; i < 4; ++i) { + if (!em_codes[i]) continue; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + buffer[index++] = static_cast('0' + em_codes[i]); + buffer[index++] = static_cast('m'); + } + buffer[index++] = static_cast(0); + } + FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } + + FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } + FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { + return buffer + std::char_traits::length(buffer); + } + + private: + Char buffer[7u + 3u * 4u + 1u]; + + static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, + char delimiter) FMT_NOEXCEPT { + out[0] = static_cast('0' + c / 100); + out[1] = static_cast('0' + c / 10 % 10); + out[2] = static_cast('0' + c % 10); + out[3] = static_cast(delimiter); + } +}; + +template +FMT_CONSTEXPR ansi_color_escape make_foreground_color( + detail::color_type foreground) FMT_NOEXCEPT { + return ansi_color_escape(foreground, detail::data::foreground_color); +} + +template +FMT_CONSTEXPR ansi_color_escape make_background_color( + detail::color_type background) FMT_NOEXCEPT { + return ansi_color_escape(background, detail::data::background_color); +} + +template +FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { + return ansi_color_escape(em); +} + +template +inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { + std::fputs(chars, stream); +} + +template <> +inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { + std::fputws(chars, stream); +} + +template inline void reset_color(FILE* stream) FMT_NOEXCEPT { + fputs(detail::data::reset_color, stream); +} + +template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { + fputs(detail::data::wreset_color, stream); +} + +template +inline void reset_color(buffer& buffer) FMT_NOEXCEPT { + const char* begin = data::reset_color; + const char* end = begin + sizeof(data::reset_color) - 1; + buffer.append(begin, end); +} + +template +void vformat_to(buffer& buf, const text_style& ts, + basic_string_view format_str, + basic_format_args>> args) { + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = detail::make_foreground_color(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + has_style = true; + auto background = detail::make_background_color(ts.get_background()); + buf.append(background.begin(), background.end()); + } + detail::vformat_to(buf, format_str, args); + if (has_style) detail::reset_color(buf); +} +} // namespace detail + +template > +void vprint(std::FILE* f, const text_style& ts, const S& format, + basic_format_args>> args) { + basic_memory_buffer buf; + detail::vformat_to(buf, ts, to_string_view(format), args); + buf.push_back(Char(0)); + detail::fputs(buf.data(), f); +} + +/** + \rst + Formats a string and prints it to the specified file stream using ANSI + escape sequences to specify text formatting. + + **Example**:: + + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template ::value)> +void print(std::FILE* f, const text_style& ts, const S& format_str, + const Args&... args) { + vprint(f, ts, format_str, + fmt::make_args_checked(format_str, args...)); +} + +/** + Formats a string and prints it to stdout using ANSI escape sequences to + specify text formatting. + Example: + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + */ +template ::value)> +void print(const text_style& ts, const S& format_str, const Args&... args) { + return print(stdout, ts, format_str, args...); +} + +template > +inline std::basic_string vformat( + const text_style& ts, const S& format_str, + basic_format_args>> args) { + basic_memory_buffer buf; + detail::vformat_to(buf, ts, to_string_view(format_str), args); + return fmt::to_string(buf); +} + +/** + \rst + Formats arguments and returns the result as a string using ANSI + escape sequences to specify text formatting. + + **Example**:: + + #include + std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}", 42); + \endrst +*/ +template > +inline std::basic_string format(const text_style& ts, const S& format_str, + const Args&... args) { + return vformat(ts, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); +} + +/** + Formats a string with the given text_style and writes the output to ``out``. + */ +template ::value)> +OutputIt vformat_to( + OutputIt out, const text_style& ts, basic_string_view format_str, + basic_format_args>> args) { + decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); + detail::vformat_to(buf, ts, format_str, args); + return detail::get_iterator(buf); +} + +/** + \rst + Formats arguments with the given text_style, writes the result to the output + iterator ``out`` and returns the iterator past the end of the output range. + + **Example**:: + + std::vector out; + fmt::format_to(std::back_inserter(out), + fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + \endrst +*/ +template >::value&& + detail::is_string::value> +inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, + Args&&... args) -> + typename std::enable_if::type { + return vformat_to(out, ts, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); +} + +FMT_END_NAMESPACE + +#endif // FMT_COLOR_H_ diff --git a/vcpkg/fmt_x86-windows-static/include/fmt/compile.h b/vcpkg/fmt_x86-windows-static/include/fmt/compile.h new file mode 100644 index 0000000..3a33b02 --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/include/fmt/compile.h @@ -0,0 +1,701 @@ +// Formatting library for C++ - experimental format string compilation +// +// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COMPILE_H_ +#define FMT_COMPILE_H_ + +#include + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { + +// A compile-time string which is compiled into fast formatting code. +class compiled_string {}; + +template +struct is_compiled_string : std::is_base_of {}; + +/** + \rst + Converts a string literal *s* into a format string that will be parsed at + compile time and converted into efficient formatting code. Requires C++17 + ``constexpr if`` compiler support. + + **Example**:: + + // Converts 42 into std::string using the most efficient method and no + // runtime format string processing. + std::string s = fmt::format(FMT_COMPILE("{}"), 42); + \endrst + */ +#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) + +template +const T& first(const T& value, const Tail&...) { + return value; +} + +// Part of a compiled format string. It can be either literal text or a +// replacement field. +template struct format_part { + enum class kind { arg_index, arg_name, text, replacement }; + + struct replacement { + arg_ref arg_id; + dynamic_format_specs specs; + }; + + kind part_kind; + union value { + int arg_index; + basic_string_view str; + replacement repl; + + FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} + FMT_CONSTEXPR value(basic_string_view s) : str(s) {} + FMT_CONSTEXPR value(replacement r) : repl(r) {} + } val; + // Position past the end of the argument id. + const Char* arg_id_end = nullptr; + + FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) + : part_kind(k), val(v) {} + + static FMT_CONSTEXPR format_part make_arg_index(int index) { + return format_part(kind::arg_index, index); + } + static FMT_CONSTEXPR format_part make_arg_name(basic_string_view name) { + return format_part(kind::arg_name, name); + } + static FMT_CONSTEXPR format_part make_text(basic_string_view text) { + return format_part(kind::text, text); + } + static FMT_CONSTEXPR format_part make_replacement(replacement repl) { + return format_part(kind::replacement, repl); + } +}; + +template struct part_counter { + unsigned num_parts = 0; + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + if (begin != end) ++num_parts; + } + + FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; } + FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; } + FMT_CONSTEXPR int on_arg_id(basic_string_view) { + return ++num_parts, 0; + } + + FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} + + FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, + const Char* end) { + // Find the matching brace. + unsigned brace_counter = 0; + for (; begin != end; ++begin) { + if (*begin == '{') { + ++brace_counter; + } else if (*begin == '}') { + if (brace_counter == 0u) break; + --brace_counter; + } + } + return begin; + } + + FMT_CONSTEXPR void on_error(const char*) {} +}; + +// Counts the number of parts in a format string. +template +FMT_CONSTEXPR unsigned count_parts(basic_string_view format_str) { + part_counter counter; + parse_format_string(format_str, counter); + return counter.num_parts; +} + +template +class format_string_compiler : public error_handler { + private: + using part = format_part; + + PartHandler handler_; + part part_; + basic_string_view format_str_; + basic_format_parse_context parse_context_; + + public: + FMT_CONSTEXPR format_string_compiler(basic_string_view format_str, + PartHandler handler) + : handler_(handler), + format_str_(format_str), + parse_context_(format_str) {} + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + if (begin != end) + handler_(part::make_text({begin, to_unsigned(end - begin)})); + } + + FMT_CONSTEXPR int on_arg_id() { + part_ = part::make_arg_index(parse_context_.next_arg_id()); + return 0; + } + + FMT_CONSTEXPR int on_arg_id(int id) { + parse_context_.check_arg_id(id); + part_ = part::make_arg_index(id); + return 0; + } + + FMT_CONSTEXPR int on_arg_id(basic_string_view id) { + part_ = part::make_arg_name(id); + return 0; + } + + FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) { + part_.arg_id_end = ptr; + handler_(part_); + } + + FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, + const Char* end) { + auto repl = typename part::replacement(); + dynamic_specs_handler> handler( + repl.specs, parse_context_); + auto it = parse_format_specs(begin, end, handler); + if (*it != '}') on_error("missing '}' in format string"); + repl.arg_id = part_.part_kind == part::kind::arg_index + ? arg_ref(part_.val.arg_index) + : arg_ref(part_.val.str); + auto part = part::make_replacement(repl); + part.arg_id_end = begin; + handler_(part); + return it; + } +}; + +// Compiles a format string and invokes handler(part) for each parsed part. +template +FMT_CONSTEXPR void compile_format_string(basic_string_view format_str, + PartHandler handler) { + parse_format_string( + format_str, + format_string_compiler(format_str, handler)); +} + +template +void format_arg( + basic_format_parse_context& parse_ctx, + Context& ctx, Id arg_id) { + ctx.advance_to(visit_format_arg( + arg_formatter(ctx, &parse_ctx), + ctx.arg(arg_id))); +} + +// vformat_to is defined in a subnamespace to prevent ADL. +namespace cf { +template +auto vformat_to(OutputIt out, CompiledFormat& cf, + basic_format_args args) -> typename Context::iterator { + using char_type = typename Context::char_type; + basic_format_parse_context parse_ctx( + to_string_view(cf.format_str_)); + Context ctx(out, args); + + const auto& parts = cf.parts(); + for (auto part_it = std::begin(parts); part_it != std::end(parts); + ++part_it) { + const auto& part = *part_it; + const auto& value = part.val; + + using format_part_t = format_part; + switch (part.part_kind) { + case format_part_t::kind::text: { + const auto text = value.str; + auto output = ctx.out(); + auto&& it = reserve(output, text.size()); + it = std::copy_n(text.begin(), text.size(), it); + ctx.advance_to(output); + break; + } + + case format_part_t::kind::arg_index: + advance_to(parse_ctx, part.arg_id_end); + detail::format_arg(parse_ctx, ctx, value.arg_index); + break; + + case format_part_t::kind::arg_name: + advance_to(parse_ctx, part.arg_id_end); + detail::format_arg(parse_ctx, ctx, value.str); + break; + + case format_part_t::kind::replacement: { + const auto& arg_id_value = value.repl.arg_id.val; + const auto arg = value.repl.arg_id.kind == arg_id_kind::index + ? ctx.arg(arg_id_value.index) + : ctx.arg(arg_id_value.name); + + auto specs = value.repl.specs; + + handle_dynamic_spec(specs.width, specs.width_ref, ctx); + handle_dynamic_spec(specs.precision, + specs.precision_ref, ctx); + + error_handler h; + numeric_specs_checker checker(h, arg.type()); + if (specs.align == align::numeric) checker.require_numeric_argument(); + if (specs.sign != sign::none) checker.check_sign(); + if (specs.alt) checker.require_numeric_argument(); + if (specs.precision >= 0) checker.check_precision(); + + advance_to(parse_ctx, part.arg_id_end); + ctx.advance_to( + visit_format_arg(arg_formatter( + ctx, nullptr, &specs), + arg)); + break; + } + } + } + return ctx.out(); +} +} // namespace cf + +struct basic_compiled_format {}; + +template +struct compiled_format_base : basic_compiled_format { + using char_type = char_t; + using parts_container = std::vector>; + + parts_container compiled_parts; + + explicit compiled_format_base(basic_string_view format_str) { + compile_format_string(format_str, + [this](const format_part& part) { + compiled_parts.push_back(part); + }); + } + + const parts_container& parts() const { return compiled_parts; } +}; + +template struct format_part_array { + format_part data[N] = {}; + FMT_CONSTEXPR format_part_array() = default; +}; + +template +FMT_CONSTEXPR format_part_array compile_to_parts( + basic_string_view format_str) { + format_part_array parts; + unsigned counter = 0; + // This is not a lambda for compatibility with older compilers. + struct { + format_part* parts; + unsigned* counter; + FMT_CONSTEXPR void operator()(const format_part& part) { + parts[(*counter)++] = part; + } + } collector{parts.data, &counter}; + compile_format_string(format_str, collector); + if (counter < N) { + parts.data[counter] = + format_part::make_text(basic_string_view()); + } + return parts; +} + +template constexpr const T& constexpr_max(const T& a, const T& b) { + return (a < b) ? b : a; +} + +template +struct compiled_format_base::value>> + : basic_compiled_format { + using char_type = char_t; + + FMT_CONSTEXPR explicit compiled_format_base(basic_string_view) {} + +// Workaround for old compilers. Format string compilation will not be +// performed there anyway. +#if FMT_USE_CONSTEXPR + static FMT_CONSTEXPR_DECL const unsigned num_format_parts = + constexpr_max(count_parts(to_string_view(S())), 1u); +#else + static const unsigned num_format_parts = 1; +#endif + + using parts_container = format_part[num_format_parts]; + + const parts_container& parts() const { + static FMT_CONSTEXPR_DECL const auto compiled_parts = + compile_to_parts( + detail::to_string_view(S())); + return compiled_parts.data; + } +}; + +template +class compiled_format : private compiled_format_base { + public: + using typename compiled_format_base::char_type; + + private: + basic_string_view format_str_; + + template + friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf, + basic_format_args args) -> + typename Context::iterator; + + public: + compiled_format() = delete; + explicit constexpr compiled_format(basic_string_view format_str) + : compiled_format_base(format_str), format_str_(format_str) {} +}; + +#ifdef __cpp_if_constexpr +template struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template +constexpr const auto& get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) { + static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); + if constexpr (N == 0) + return first; + else + return get(rest...); +} + +template struct get_type_impl; + +template struct get_type_impl> { + using type = remove_cvref_t(std::declval()...))>; +}; + +template +using get_type = typename get_type_impl::type; + +template struct is_compiled_format : std::false_type {}; + +template struct text { + basic_string_view data; + using char_type = Char; + + template + OutputIt format(OutputIt out, const Args&...) const { + return write(out, data); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr text make_text(basic_string_view s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template struct code_unit { + Char value; + using char_type = Char; + + template + OutputIt format(OutputIt out, const Args&...) const { + return write(out, value); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N. +template struct field { + using char_type = Char; + + template + OutputIt format(OutputIt out, const Args&... args) const { + // This ensures that the argument type is convertile to `const T&`. + const T& arg = get(args...); + return write(out, arg); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N and has format specifiers. +template struct spec_field { + using char_type = Char; + mutable formatter fmt; + + template + OutputIt format(OutputIt out, const Args&... args) const { + // This ensures that the argument type is convertile to `const T&`. + const T& arg = get(args...); + const auto& vargs = + make_format_args>(args...); + basic_format_context ctx(out, vargs); + return fmt.format(arg, ctx); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template struct concat { + L lhs; + R rhs; + using char_type = typename L::char_type; + + template + OutputIt format(OutputIt out, const Args&... args) const { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr concat make_concat(L lhs, R rhs) { + return {lhs, rhs}; +} + +struct unknown_format {}; + +template +constexpr size_t parse_text(basic_string_view str, size_t pos) { + for (size_t size = str.size(); pos != size; ++pos) { + if (str[pos] == '{' || str[pos] == '}') break; + } + return pos; +} + +template +constexpr auto compile_format_string(S format_str); + +template +constexpr auto parse_tail(T head, S format_str) { + if constexpr (POS != + basic_string_view(format_str).size()) { + constexpr auto tail = compile_format_string(format_str); + if constexpr (std::is_same, + unknown_format>()) + return tail; + else + return make_concat(head, tail); + } else { + return head; + } +} + +template struct parse_specs_result { + formatter fmt; + size_t end; + int next_arg_id; +}; + +template +constexpr parse_specs_result parse_specs(basic_string_view str, + size_t pos, int arg_id) { + str.remove_prefix(pos); + auto ctx = basic_format_parse_context(str, {}, arg_id + 1); + auto f = formatter(); + auto end = f.parse(ctx); + return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; +} + +// Compiles a non-empty format string and returns the compiled representation +// or unknown_format() on unrecognized input. +template +constexpr auto compile_format_string(S format_str) { + using char_type = typename S::char_type; + constexpr basic_string_view str = format_str; + if constexpr (str[POS] == '{') { + if (POS + 1 == str.size()) + throw format_error("unmatched '{' in format string"); + if constexpr (str[POS + 1] == '{') { + return parse_tail(make_text(str, POS, 1), format_str); + } else if constexpr (str[POS + 1] == '}') { + using type = get_type; + return parse_tail(field(), + format_str); + } else if constexpr (str[POS + 1] == ':') { + using type = get_type; + constexpr auto result = parse_specs(str, POS + 2, ID); + return parse_tail( + spec_field{result.fmt}, format_str); + } else { + return unknown_format(); + } + } else if constexpr (str[POS] == '}') { + if (POS + 1 == str.size()) + throw format_error("unmatched '}' in format string"); + return parse_tail(make_text(str, POS, 1), format_str); + } else { + constexpr auto end = parse_text(str, POS + 1); + if constexpr (end - POS > 1) { + return parse_tail(make_text(str, POS, end - POS), + format_str); + } else { + return parse_tail(code_unit{str[POS]}, + format_str); + } + } +} + +template ::value || + detail::is_compiled_string::value)> +constexpr auto compile(S format_str) { + constexpr basic_string_view str = format_str; + if constexpr (str.size() == 0) { + return detail::make_text(str, 0, 0); + } else { + constexpr auto result = + detail::compile_format_string, 0, 0>( + format_str); + if constexpr (std::is_same, + detail::unknown_format>()) { + return detail::compiled_format(to_string_view(format_str)); + } else { + return result; + } + } +} +#else +template ::value)> +constexpr auto compile(S format_str) -> detail::compiled_format { + return detail::compiled_format(to_string_view(format_str)); +} +#endif // __cpp_if_constexpr + +// Compiles the format string which must be a string literal. +template +auto compile(const Char (&format_str)[N]) + -> detail::compiled_format { + return detail::compiled_format( + basic_string_view(format_str, N - 1)); +} +} // namespace detail + +// DEPRECATED! use FMT_COMPILE instead. +template +FMT_DEPRECATED auto compile(const Args&... args) + -> decltype(detail::compile(args...)) { + return detail::compile(args...); +} + +#if FMT_USE_CONSTEXPR +# ifdef __cpp_if_constexpr + +template ::value)> +FMT_INLINE std::basic_string format(const CompiledFormat& cf, + const Args&... args) { + basic_memory_buffer buffer; + cf.format(detail::buffer_appender(buffer), args...); + return to_string(buffer); +} + +template ::value)> +OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} +# endif // __cpp_if_constexpr +#endif // FMT_USE_CONSTEXPR + +template ::value)> +std::basic_string format(const CompiledFormat& cf, const Args&... args) { + basic_memory_buffer buffer; + using context = buffer_context; + detail::cf::vformat_to(detail::buffer_appender(buffer), cf, + make_format_args(args...)); + return to_string(buffer); +} + +template ::value)> +FMT_INLINE std::basic_string format(const S&, + Args&&... args) { +#ifdef __cpp_if_constexpr + if constexpr (std::is_same::value) { + constexpr basic_string_view str = S(); + if (str.size() == 2 && str[0] == '{' && str[1] == '}') + return fmt::to_string(detail::first(args...)); + } +#endif + constexpr auto compiled = detail::compile(S()); + return format(compiled, std::forward(args)...); +} + +template ::value)> +OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + using char_type = typename CompiledFormat::char_type; + using context = format_context_t; + return detail::cf::vformat_to(out, cf, + make_format_args(args...)); +} + +template ::value)> +OutputIt format_to(OutputIt out, const S&, const Args&... args) { + constexpr auto compiled = detail::compile(S()); + return format_to(out, compiled, args...); +} + +template +auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf, + const Args&... args) -> + typename std::enable_if< + detail::is_output_iterator::value && + std::is_base_of::value, + format_to_n_result>::type { + auto it = + format_to(detail::truncating_iterator(out, n), cf, args...); + return {it.base(), it.count()}; +} + +template ::value)> +format_to_n_result format_to_n(OutputIt out, size_t n, const S&, + const Args&... args) { + constexpr auto compiled = detail::compile(S()); + auto it = format_to(detail::truncating_iterator(out, n), compiled, + args...); + return {it.base(), it.count()}; +} + +template +size_t formatted_size(const CompiledFormat& cf, const Args&... args) { + return format_to(detail::counting_iterator(), cf, args...).count(); +} + +FMT_END_NAMESPACE + +#endif // FMT_COMPILE_H_ diff --git a/vcpkg/fmt_x86-windows-static/include/fmt/core.h b/vcpkg/fmt_x86-windows-static/include/fmt/core.h new file mode 100644 index 0000000..0a81e0c --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/include/fmt/core.h @@ -0,0 +1,2122 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include // std::FILE +#include +#include +#include +#include +#include +#include +#include + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 70103 + +#ifdef __clang__ +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif + +#if defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION +#else +# define FMT_HAS_GXX_CXX11 0 +#endif + +#ifdef __NVCC__ +# define FMT_NVCC __NVCC__ +#else +# define FMT_NVCC 0 +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VER _MSC_VER +# define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n)) +#else +# define FMT_MSC_VER 0 +# define FMT_SUPPRESS_MSC_WARNING(n) +#endif + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#if defined(__has_include) && !defined(__INTELLISENSE__) && \ + (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Check if relaxed C++14 constexpr is supported. +// GCC doesn't allow throw in constexpr until version 6 (bug 67371). +#ifndef FMT_USE_CONSTEXPR +# define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ + (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ + !FMT_NVCC && !FMT_ICC_VERSION +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +# define FMT_CONSTEXPR_DECL constexpr +#else +# define FMT_CONSTEXPR inline +# define FMT_CONSTEXPR_DECL +#endif + +#ifndef FMT_OVERRIDE +# if FMT_HAS_FEATURE(cxx_override_control) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +# define FMT_OVERRIDE override +# else +# define FMT_OVERRIDE +# endif +#endif + +// Check if exceptions are disabled. +#ifndef FMT_EXCEPTIONS +# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ + FMT_MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +# else +# define FMT_EXCEPTIONS 1 +# endif +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +# define FMT_DETECTED_NOEXCEPT noexcept +# define FMT_HAS_CXX11_NOEXCEPT 1 +#else +# define FMT_DETECTED_NOEXCEPT throw() +# define FMT_HAS_CXX11_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT +# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +# else +# define FMT_NOEXCEPT +# endif +#endif + +// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code +// warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ + !FMT_NVCC +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VER +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + +// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. +#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC +# define FMT_DEPRECATED_ALIAS +#else +# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED +#endif + +#ifndef FMT_INLINE +# if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_INLINE inline __attribute__((always_inline)) +# else +# define FMT_INLINE inline +# endif +#endif + +#ifndef FMT_USE_INLINE_NAMESPACES +# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ + (FMT_MSC_VER >= 1900 && !_MANAGED) +# define FMT_USE_INLINE_NAMESPACES 1 +# else +# define FMT_USE_INLINE_NAMESPACES 0 +# endif +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# if FMT_USE_INLINE_NAMESPACES +# define FMT_INLINE_NAMESPACE inline namespace +# define FMT_END_NAMESPACE \ + } \ + } +# else +# define FMT_INLINE_NAMESPACE namespace +# define FMT_END_NAMESPACE \ + } \ + using namespace v7; \ + } +# endif +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + FMT_INLINE_NAMESPACE v7 { +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275) +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# define FMT_EXPORTED +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# endif +#else +# define FMT_CLASS_API +#endif +#ifndef FMT_API +# define FMT_API +#endif +#ifndef FMT_EXTERN_TEMPLATE_API +# define FMT_EXTERN_TEMPLATE_API +#endif +#ifndef FMT_INSTANTIATION_DEF_API +# define FMT_INSTANTIATION_DEF_API FMT_API +#endif + +#ifndef FMT_HEADER_ONLY +# define FMT_EXTERN extern +#else +# define FMT_EXTERN +#endif + +// libc++ supports string_view in pre-c++17. +#if (FMT_HAS_INCLUDE() && \ + (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +# include +# define FMT_USE_STRING_VIEW +#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +# include +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +#ifndef FMT_UNICODE +# define FMT_UNICODE !FMT_MSC_VER +#endif +#if FMT_UNICODE && FMT_MSC_VER +# pragma execution_character_set("utf-8") +#endif + +FMT_BEGIN_NAMESPACE + +// Implementations of enable_if_t and other metafunctions for older systems. +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; +template +using remove_const_t = typename std::remove_const::type; +template +using remove_cvref_t = typename std::remove_cv>::type; +template struct type_identity { using type = T; }; +template using type_identity_t = typename type_identity::type; + +struct monostate {}; + +// An enable_if helper to be used in template parameters which results in much +// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed +// to workaround a bug in MSVC 2019 (see #1140 and #1186). +#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 + +namespace detail { + +// A helper function to suppress "conditional expression is constant" warnings. +template constexpr T const_check(T value) { return value; } + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#ifndef FMT_ASSERT +# ifdef NDEBUG +// FMT_ASSERT is not empty to avoid -Werror=empty-body. +# define FMT_ASSERT(condition, message) ((void)0) +# else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +# endif +#endif + +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) +template +using std_string_view = std::experimental::basic_string_view; +#else +template struct std_string_view {}; +#endif + +#ifdef FMT_USE_INT128 +// Do nothing. +#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ + !(FMT_CLANG_VERSION && FMT_MSC_VER) +# define FMT_USE_INT128 1 +using int128_t = __int128_t; +using uint128_t = __uint128_t; +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +struct int128_t {}; +struct uint128_t {}; +#endif + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::type>(value); +} + +FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5"; + +template constexpr bool is_unicode() { + return FMT_UNICODE || sizeof(Char) != 1 || + (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); +} + +#ifdef __cpp_char8_t +using char8_type = char8_t; +#else +enum char8_type : unsigned char {}; +#endif +} // namespace detail + +#ifdef FMT_USE_INTERNAL +namespace internal = detail; // DEPRECATED +#endif + +/** + An implementation of ``std::basic_string_view`` for pre-C++17. It provides a + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). + */ +template class basic_string_view { + private: + const Char* data_; + size_t size_; + + public: + using value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} + + /** Constructs a string reference object from a C string and a size. */ + constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT + : data_(s), + size_(count) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ +#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. + FMT_CONSTEXPR +#endif + basic_string_view(const Char* s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** Constructs a string reference from a ``std::basic_string`` object. */ + template + FMT_CONSTEXPR basic_string_view( + const std::basic_string& s) FMT_NOEXCEPT + : data_(s.data()), + size_(s.size()) {} + + template >::value)> + FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), + size_(s.size()) {} + + /** Returns a pointer to the string data. */ + constexpr const Char* data() const { return data_; } + + /** Returns the string size. */ + constexpr size_t size() const { return size_; } + + constexpr iterator begin() const { return data_; } + constexpr iterator end() const { return data_ + size_; } + + constexpr const Char& operator[](size_t pos) const { return data_[pos]; } + + FMT_CONSTEXPR void remove_prefix(size_t n) { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + int compare(basic_string_view other) const { + size_t str_size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, str_size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + friend bool operator==(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) != 0; + } + friend bool operator<(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) < 0; + } + friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) <= 0; + } + friend bool operator>(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; +using wstring_view = basic_string_view; + +/** Specifies if ``T`` is a character type. Can be specialized by users. */ +template struct is_char : std::false_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; + +/** + \rst + Returns a string view of `s`. In order to add custom string type support to + {fmt} provide an overload of `to_string_view` for it in the same namespace as + the type for the argument-dependent lookup to work. + + **Example**:: + + namespace my_ns { + inline string_view to_string_view(const my_string& s) { + return {s.data(), s.length()}; + } + } + std::string message = fmt::format(my_string("The answer is {}"), 42); + \endrst + */ +template ::value)> +inline basic_string_view to_string_view(const Char* s) { + return s; +} + +template +inline basic_string_view to_string_view( + const std::basic_string& s) { + return s; +} + +template +inline basic_string_view to_string_view(basic_string_view s) { + return s; +} + +template >::value)> +inline basic_string_view to_string_view(detail::std_string_view s) { + return s; +} + +// A base class for compile-time strings. It is defined in the fmt namespace to +// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + +template ::value)> +constexpr basic_string_view to_string_view(const S& s) { + return s; +} + +namespace detail { +void to_string_view(...); +using fmt::v7::to_string_view; + +// Specifies whether S is a string type convertible to fmt::basic_string_view. +// It should be a constexpr function but MSVC 2017 fails to compile it in +// enable_if and MSVC 2015 fails to compile it as an alias template. +template +struct is_string : std::is_class()))> { +}; + +template struct char_t_impl {}; +template struct char_t_impl::value>> { + using result = decltype(to_string_view(std::declval())); + using type = typename result::value_type; +}; + +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} +template ::value)> +void check_format_string(S); + +struct error_handler { + constexpr error_handler() = default; + constexpr error_handler(const error_handler&) = default; + + // This function is intentionally not constexpr to give a compile-time error. + FMT_NORETURN FMT_API void on_error(const char* message); +}; +} // namespace detail + +/** String's character type. */ +template using char_t = typename detail::char_t_impl::type; + +/** + \rst + Parsing context consisting of a format string range being parsed and an + argument counter for automatic indexing. + + You can use one of the following type aliases for common character types: + + +-----------------------+-------------------------------------+ + | Type | Definition | + +=======================+=====================================+ + | format_parse_context | basic_format_parse_context | + +-----------------------+-------------------------------------+ + | wformat_parse_context | basic_format_parse_context | + +-----------------------+-------------------------------------+ + \endrst + */ +template +class basic_format_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + public: + using char_type = Char; + using iterator = typename basic_string_view::iterator; + + explicit constexpr basic_format_parse_context( + basic_string_view format_str, ErrorHandler eh = {}, + int next_arg_id = 0) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} + + /** + Returns an iterator to the beginning of the format string range being + parsed. + */ + constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); } + + /** + Returns an iterator past the end of the format string range being parsed. + */ + constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + + /** Advances the begin iterator to ``it``. */ + FMT_CONSTEXPR void advance_to(iterator it) { + format_str_.remove_prefix(detail::to_unsigned(it - begin())); + } + + /** + Reports an error if using the manual argument indexing; otherwise returns + the next argument index and switches to the automatic indexing. + */ + FMT_CONSTEXPR int next_arg_id() { + // Don't check if the argument id is valid to avoid overhead and because it + // will be checked during formatting anyway. + if (next_arg_id_ >= 0) return next_arg_id_++; + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + + /** + Reports an error if using the automatic argument indexing; otherwise + switches to the manual indexing. + */ + FMT_CONSTEXPR void check_arg_id(int) { + if (next_arg_id_ > 0) + on_error("cannot switch from automatic to manual argument indexing"); + else + next_arg_id_ = -1; + } + + FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char* message) { + ErrorHandler::on_error(message); + } + + constexpr ErrorHandler error_handler() const { return *this; } +}; + +using format_parse_context = basic_format_parse_context; +using wformat_parse_context = basic_format_parse_context; + +template class basic_format_arg; +template class basic_format_args; +template class dynamic_format_arg_store; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +// Specifies if T has an enabled formatter specialization. A type can be +// formattable even if it doesn't have a formatter e.g. via a conversion. +template +using has_formatter = + std::is_constructible>; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; +template +struct is_contiguous> : std::true_type {}; + +namespace detail { + +// Extracts a reference to the container from back_insert_iterator. +template +inline Container& get_container(std::back_insert_iterator it) { + using bi_iterator = std::back_insert_iterator; + struct accessor : bi_iterator { + accessor(bi_iterator iter) : bi_iterator(iter) {} + using bi_iterator::container; + }; + return *accessor(it).container; +} + +/** + \rst + A contiguous memory buffer with an optional growing ability. It is an internal + class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. + \endrst + */ +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_SUPPRESS_MSC_WARNING(26495) + buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} + + buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT + : ptr_(p), + size_(sz), + capacity_(cap) {} + + ~buffer() = default; + + /** Sets the buffer data and capacity. */ + void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual void grow(size_t capacity) = 0; + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + + T* begin() FMT_NOEXCEPT { return ptr_; } + T* end() FMT_NOEXCEPT { return ptr_ + size_; } + + const T* begin() const FMT_NOEXCEPT { return ptr_; } + const T* end() const FMT_NOEXCEPT { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + size_t size() const FMT_NOEXCEPT { return size_; } + + /** Returns the capacity of this buffer. */ + size_t capacity() const FMT_NOEXCEPT { return capacity_; } + + /** Returns a pointer to the buffer data. */ + T* data() FMT_NOEXCEPT { return ptr_; } + + /** Returns a pointer to the buffer data. */ + const T* data() const FMT_NOEXCEPT { return ptr_; } + + /** Clears this buffer. */ + void clear() { size_ = 0; } + + // Tries resizing the buffer to contain *count* elements. If T is a POD type + // the new elements may not be initialized. + void try_resize(size_t count) { + try_reserve(count); + size_ = count <= capacity_ ? count : capacity_; + } + + // Tries increasing the buffer capacity to *new_capacity*. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow(new_capacity); + } + + void push_back(const T& value) { + try_reserve(size_ + 1); + ptr_[size_++] = value; + } + + /** Appends data to the end of the buffer. */ + template void append(const U* begin, const U* end); + + template T& operator[](I index) { return ptr_[index]; } + template const T& operator[](I index) const { + return ptr_[index]; + } +}; + +struct buffer_traits { + explicit buffer_traits(size_t) {} + size_t count() const { return 0; } + size_t limit(size_t size) { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + size_t count() const { return count_; } + size_t limit(size_t size) { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return size < n ? size : n; + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer final : public Traits, public buffer { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + void grow(size_t) final FMT_OVERRIDE { + if (this->size() == buffer_size) flush(); + } + void flush(); + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), + buffer(data_, 0, buffer_size), + out_(out) {} + ~iterator_buffer() { flush(); } + + OutputIt out() { + flush(); + return out_; + } + size_t count() const { return Traits::count() + this->size(); } +}; + +template class iterator_buffer final : public buffer { + protected: + void grow(size_t) final FMT_OVERRIDE {} + + public: + explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} + + T* out() { return &*this->end(); } +}; + +// A buffer that writes to a container with the contiguous storage. +template +class iterator_buffer, + enable_if_t::value, + typename Container::value_type>> + final : public buffer { + private: + Container& container_; + + protected: + void grow(size_t capacity) final FMT_OVERRIDE { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit iterator_buffer(Container& c) + : buffer(c.size()), container_(c) {} + explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) + : iterator_buffer(get_container(out)) {} + std::back_insert_iterator out() { + return std::back_inserter(container_); + } +}; + +// A buffer that counts the number of code units written discarding the output. +template class counting_buffer final : public buffer { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + protected: + void grow(size_t) final FMT_OVERRIDE { + if (this->size() != buffer_size) return; + count_ += this->size(); + this->clear(); + } + + public: + counting_buffer() : buffer(data_, 0, buffer_size) {} + + size_t count() { return count_ + this->size(); } +}; + +// An output iterator that appends to the buffer. +// It is used to reduce symbol sizes for the common case. +template +class buffer_appender : public std::back_insert_iterator> { + using base = std::back_insert_iterator>; + + public: + explicit buffer_appender(buffer& buf) : base(buf) {} + buffer_appender(base it) : base(it) {} + + buffer_appender& operator++() { + base::operator++(); + return *this; + } + + buffer_appender operator++(int) { + buffer_appender tmp = *this; + ++*this; + return tmp; + } +}; + +// Maps an output iterator into a buffer. +template +iterator_buffer get_buffer(OutputIt); +template buffer& get_buffer(buffer_appender); + +template OutputIt get_buffer_init(OutputIt out) { + return out; +} +template buffer& get_buffer_init(buffer_appender out) { + return get_container(out); +} + +template +auto get_iterator(Buffer& buf) -> decltype(buf.out()) { + return buf.out(); +} +template buffer_appender get_iterator(buffer& buf) { + return buffer_appender(buf); +} + +template +struct fallback_formatter { + fallback_formatter() = delete; +}; + +// Specifies if T has an enabled fallback_formatter specialization. +template +using has_fallback_formatter = + std::is_constructible>; + +struct view {}; + +template struct named_arg : view { + const Char* name; + const T& value; + named_arg(const Char* n, const T& v) : name(n), value(v) {} +}; + +template struct named_arg_info { + const Char* name; + int id; +}; + +template +struct arg_data { + // args_[0].named_args points to named_args_ to avoid bloating format_args. + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; + named_arg_info named_args_[NUM_NAMED_ARGS]; + + template + arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} + arg_data(const arg_data& other) = delete; + const T* args() const { return args_ + 1; } + named_arg_info* named_args() { return named_args_; } +}; + +template +struct arg_data { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; + + template + FMT_INLINE arg_data(const U&... init) : args_{init...} {} + FMT_INLINE const T* args() const { return args_; } + FMT_INLINE std::nullptr_t named_args() { return nullptr; } +}; + +template +inline void init_named_args(named_arg_info*, int, int) {} + +template +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const T&, const Tail&... args) { + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const named_arg& arg, + const Tail&... args) { + named_args[named_arg_count++] = {arg.name, arg_count}; + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template +FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} + +template struct is_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template constexpr size_t count() { return B ? 1 : 0; } +template constexpr size_t count() { + return (B1 ? 1 : 0) + count(); +} + +template constexpr size_t count_named_args() { + return count::value...>(); +} + +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_t, int128_type); +FMT_TYPE_CONSTANT(uint128_t, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} + +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; +} + +template struct string_value { + const Char* data; + size_t size; +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +template struct custom_value { + using parse_context = typename Context::parse_context_type; + const void* value; + void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); +}; + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_t int128_value; + uint128_t uint128_value; + bool bool_value; + char_type char_value; + float float_value; + double double_value; + long double long_double_value; + const void* pointer; + string_value string; + custom_value custom; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value(int val = 0) : int_value(val) {} + constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} + FMT_INLINE value(long long val) : long_long_value(val) {} + FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} + FMT_INLINE value(int128_t val) : int128_value(val) {} + FMT_INLINE value(uint128_t val) : uint128_value(val) {} + FMT_INLINE value(float val) : float_value(val) {} + FMT_INLINE value(double val) : double_value(val) {} + FMT_INLINE value(long double val) : long_double_value(val) {} + FMT_INLINE value(bool val) : bool_value(val) {} + FMT_INLINE value(char_type val) : char_value(val) {} + FMT_INLINE value(const char_type* val) { string.data = val; } + FMT_INLINE value(basic_string_view val) { + string.data = val.data(); + string.size = val.size(); + } + FMT_INLINE value(const void* val) : pointer(val) {} + FMT_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + template FMT_INLINE value(const T& val) { + custom.value = &val; + // Get the formatter type through the context to allow different contexts + // have different extension points, e.g. `formatter` for `format` and + // `printf_formatter` for `printf`. + custom.format = format_custom_arg< + T, conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>>; + } + + private: + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg(const void* arg, + typename Context::parse_context_type& parse_ctx, + Context& ctx) { + Formatter f; + parse_ctx.advance_to(f.parse(parse_ctx)); + ctx.advance_to(f.format(*static_cast(arg), ctx)); + } +}; + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T& value); + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +enum { long_short = sizeof(long) == sizeof(int) }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +struct unformattable {}; + +// Maps formatting arguments to core types. +template struct arg_mapper { + using char_type = typename Context::char_type; + + FMT_CONSTEXPR int map(signed char val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned char val) { return val; } + FMT_CONSTEXPR int map(short val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned short val) { return val; } + FMT_CONSTEXPR int map(int val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned val) { return val; } + FMT_CONSTEXPR long_type map(long val) { return val; } + FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; } + FMT_CONSTEXPR long long map(long long val) { return val; } + FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; } + FMT_CONSTEXPR int128_t map(int128_t val) { return val; } + FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; } + FMT_CONSTEXPR bool map(bool val) { return val; } + + template ::value)> + FMT_CONSTEXPR char_type map(T val) { + static_assert( + std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); + return val; + } + + FMT_CONSTEXPR float map(float val) { return val; } + FMT_CONSTEXPR double map(double val) { return val; } + FMT_CONSTEXPR long double map(long double val) { return val; } + + FMT_CONSTEXPR const char_type* map(char_type* val) { return val; } + FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; } + template ::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + static_assert(std::is_same>::value, + "mixing character types is disallowed"); + return to_string_view(val); + } + template , T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + return basic_string_view(val); + } + template < + typename T, + FMT_ENABLE_IF( + std::is_constructible, T>::value && + !std::is_constructible, T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + return std_string_view(val); + } + FMT_CONSTEXPR const char* map(const signed char* val) { + static_assert(std::is_same::value, "invalid string type"); + return reinterpret_cast(val); + } + FMT_CONSTEXPR const char* map(const unsigned char* val) { + static_assert(std::is_same::value, "invalid string type"); + return reinterpret_cast(val); + } + FMT_CONSTEXPR const char* map(signed char* val) { + const auto* const_val = val; + return map(const_val); + } + FMT_CONSTEXPR const char* map(unsigned char* val) { + const auto* const_val = val; + return map(const_val); + } + + FMT_CONSTEXPR const void* map(void* val) { return val; } + FMT_CONSTEXPR const void* map(const void* val) { return val; } + FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; } + template FMT_CONSTEXPR int map(const T*) { + // Formatting of arbitrary pointers is disallowed. If you want to output + // a pointer cast it to "void *" or "const void *". In particular, this + // forbids formatting of "[const] volatile char *" which is printed as bool + // by iostreams. + static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); + return 0; + } + + template ::value && + !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR auto map(const T& val) + -> decltype(std::declval().map( + static_cast::type>(val))) { + return map(static_cast::type>(val)); + } + template ::value && !is_char::value && + (has_formatter::value || + has_fallback_formatter::value))> + FMT_CONSTEXPR const T& map(const T& val) { + return val; + } + + template + FMT_CONSTEXPR auto map(const named_arg& val) + -> decltype(std::declval().map(val.value)) { + return map(val.value); + } + + unformattable map(...) { return {}; } +}; + +// A type constant after applying arg_mapper. +template +using mapped_type_constant = + type_constant().map(std::declval())), + typename Context::char_type>; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; +} // namespace detail + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + template + friend FMT_CONSTEXPR basic_format_arg detail::make_arg( + const T& value); + + template + friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)); + + friend class basic_format_args; + friend class dynamic_format_arg_store; + + using char_type = typename Context::char_type; + + template + friend struct detail::arg_data; + + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + + public: + class handle { + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(typename Context::parse_context_type& parse_ctx, + Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + + private: + detail::custom_value custom_; + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + + constexpr explicit operator bool() const FMT_NOEXCEPT { + return type_ != detail::type::none_type; + } + + detail::type type() const { return type_; } + + bool is_integral() const { return detail::is_integral_type(type_); } + bool is_arithmetic() const { return detail::is_arithmetic_type(type_); } +}; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( + Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { + using char_type = typename Context::char_type; + switch (arg.type_) { + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(arg.value_.int_value); + case detail::type::uint_type: + return vis(arg.value_.uint_value); + case detail::type::long_long_type: + return vis(arg.value_.long_long_value); + case detail::type::ulong_long_type: + return vis(arg.value_.ulong_long_value); +#if FMT_USE_INT128 + case detail::type::int128_type: + return vis(arg.value_.int128_value); + case detail::type::uint128_type: + return vis(arg.value_.uint128_value); +#else + case detail::type::int128_type: + case detail::type::uint128_type: + break; +#endif + case detail::type::bool_type: + return vis(arg.value_.bool_value); + case detail::type::char_type: + return vis(arg.value_.char_value); + case detail::type::float_type: + return vis(arg.value_.float_value); + case detail::type::double_type: + return vis(arg.value_.double_value); + case detail::type::long_double_type: + return vis(arg.value_.long_double_value); + case detail::type::cstring_type: + return vis(arg.value_.string.data); + case detail::type::string_type: + return vis(basic_string_view(arg.value_.string.data, + arg.value_.string.size)); + case detail::type::pointer_type: + return vis(arg.value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} + +template struct formattable : std::false_type {}; + +namespace detail { + +// A workaround for gcc 4.8 to make void_t work in a SFINAE context. +template struct void_t_impl { using type = void; }; +template +using void_t = typename detail::void_t_impl::type; + +template +struct is_output_iterator : std::false_type {}; + +template +struct is_output_iterator< + It, T, + void_t::iterator_category, + decltype(*std::declval() = std::declval())>> + : std::true_type {}; + +template +struct is_back_insert_iterator : std::false_type {}; +template +struct is_back_insert_iterator> + : std::true_type {}; + +template +struct is_contiguous_back_insert_iterator : std::false_type {}; +template +struct is_contiguous_back_insert_iterator> + : is_contiguous {}; +template +struct is_contiguous_back_insert_iterator> + : std::true_type {}; + +// A type-erased reference to an std::locale to avoid heavy include. +class locale_ref { + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + locale_ref() : locale_(nullptr) {} + template explicit locale_ref(const Locale& loc); + + explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } + + template Locale get() const; +}; + +template constexpr unsigned long long encode_types() { return 0; } + +template +constexpr unsigned long long encode_types() { + return static_cast(mapped_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = arg_mapper().map(value); + return arg; +} + +template int check(unformattable) { + static_assert( + formattable(), + "Cannot format an argument. To make type T formattable provide a " + "formatter specialization: https://fmt.dev/latest/api.html#udt"); + return 0; +} +template inline const U& check(const U& val) { + return val; +} + +// The type template parameter is there to avoid an ODR violation when using +// a fallback formatter in one translation unit and an implicit conversion in +// another (not recommended). +template +inline value make_arg(const T& val) { + return check(arg_mapper().map(val)); +} + +template +inline basic_format_arg make_arg(const T& value) { + return make_arg(value); +} + +template struct is_reference_wrapper : std::false_type {}; +template +struct is_reference_wrapper> : std::true_type {}; + +template const T& unwrap(const T& v) { return v; } +template const T& unwrap(const std::reference_wrapper& v) { + return static_cast(v); +} + +class dynamic_arg_list { + // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for + // templates it doesn't complain about inability to deduce single translation + // unit for placing vtable. So storage_node_base is made a fake template. + template struct node { + virtual ~node() = default; + std::unique_ptr> next; + }; + + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template const T& push(const Arg& arg) { + auto new_node = std::unique_ptr>(new typed_node(arg)); + auto& value = new_node->value; + new_node->next = std::move(head_); + head_ = std::move(new_node); + return value; + } +}; +} // namespace detail + +// Formatting context. +template class basic_format_context { + public: + /** The character type for the output. */ + using char_type = Char; + + private: + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + using iterator = OutputIt; + using format_arg = basic_format_arg; + using parse_context_type = basic_format_parse_context; + template using formatter_type = formatter; + + basic_format_context(const basic_format_context&) = delete; + void operator=(const basic_format_context&) = delete; + /** + Constructs a ``basic_format_context`` object. References to the arguments are + stored in the object so make sure they have appropriate lifetimes. + */ + basic_format_context(OutputIt out, + basic_format_args ctx_args, + detail::locale_ref loc = detail::locale_ref()) + : out_(out), args_(ctx_args), loc_(loc) {} + + format_arg arg(int id) const { return args_.get(id); } + format_arg arg(basic_string_view name) { return args_.get(name); } + int arg_id(basic_string_view name) { return args_.get_id(name); } + const basic_format_args& args() const { return args_; } + + detail::error_handler error_handler() { return {}; } + void on_error(const char* message) { error_handler().on_error(message); } + + // Returns an iterator to the beginning of the output range. + iterator out() { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; + } + + detail::locale_ref locale() { return loc_; } +}; + +template +using buffer_context = + basic_format_context, Char>; +using format_context = buffer_context; +using wformat_context = buffer_context; + +// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. +#define FMT_BUFFER_CONTEXT(Char) \ + basic_format_context, Char> + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + static const size_t num_args = sizeof...(Args); + static const size_t num_named_args = detail::count_named_args(); + static const bool is_packed = num_args <= detail::max_packed_args; + + using value_type = conditional_t, + basic_format_arg>; + + detail::arg_data + data_; + + friend class basic_format_args; + + static constexpr unsigned long long desc = + (is_packed ? detail::encode_types() + : detail::is_unpacked_bit | num_args) | + (num_named_args != 0 + ? static_cast(detail::has_named_args_bit) + : 0); + + public: + format_arg_store(const Args&... args) + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{detail::make_arg< + is_packed, Context, + detail::mapped_type_constant::value>(args)...} { + detail::init_named_args(data_.named_args(), 0, 0, args...); + } +}; + +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` + can be omitted in which case it defaults to `~fmt::context`. + See `~fmt::arg` for lifetime considerations. + \endrst + */ +template +inline format_arg_store make_format_args( + const Args&... args) { + return {args...}; +} + +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references + to arguments and can be implicitly converted to `~fmt::format_args`. + If ``format_str`` is a compile-time string then `make_args_checked` checks + its validity at compile time. + \endrst + */ +template > +inline auto make_args_checked(const S& format_str, + const remove_reference_t&... args) + -> format_arg_store, remove_reference_t...> { + static_assert( + detail::count<( + std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + detail::check_format_string(format_str); + return {args...}; +} + +/** + \rst + Returns a named argument to be used in a formatting function. It should only + be used in a call to a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template +inline detail::named_arg arg(const Char* name, const T& arg) { + static_assert(!detail::is_named_arg(), "nested named arguments"); + return {name, arg}; +} + +/** + \rst + A dynamic version of `fmt::format_arg_store`. + It's equipped with a storage to potentially temporary objects which lifetimes + could be shorter than the format arguments object. + + It can be implicitly converted into `~fmt::basic_format_args` for passing + into type-erased formatting functions such as `~fmt::vformat`. + \endrst + */ +template +class dynamic_format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr detail::type mapped_type = + detail::mapped_type_constant::value; + + enum { + value = !(detail::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != detail::type::cstring_type && + mapped_type != detail::type::string_type && + mapped_type != detail::type::custom_type)) + }; + }; + + template + using stored_type = conditional_t::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + std::vector> named_info_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + detail::dynamic_arg_list dynamic_args_; + + friend class basic_format_args; + + unsigned long long get_types() const { + return detail::is_unpacked_bit | data_.size() | + (named_info_.empty() + ? 0ULL + : static_cast(detail::has_named_args_bit)); + } + + const basic_format_arg* data() const { + return named_info_.empty() ? data_.data() : data_.data() + 1; + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(detail::make_arg(arg)); + } + + template + void emplace_arg(const detail::named_arg& arg) { + if (named_info_.empty()) { + constexpr const detail::named_arg_info* zero_ptr{nullptr}; + data_.insert(data_.begin(), {zero_ptr, 0}); + } + data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); + auto pop_one = [](std::vector>* data) { + data->pop_back(); + }; + std::unique_ptr>, decltype(pop_one)> + guard{&data_, pop_one}; + named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); + data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; + guard.release(); + } + + public: + /** + \rst + Adds an argument into the dynamic store for later passing to a formatting + function. + + Note that custom types and string types (but not string views) are copied + into the store dynamically allocating memory if necessary. + + **Example**:: + + fmt::dynamic_format_arg_store store; + store.push_back(42); + store.push_back("abc"); + store.push_back(1.5f); + std::string result = fmt::vformat("{} and {} and {}", store); + \endrst + */ + template void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(detail::unwrap(arg)); + } + + /** + \rst + Adds a reference to the argument into the dynamic store for later passing to + a formatting function. Supports named arguments wrapped in + ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``. + + **Example**:: + + fmt::dynamic_format_arg_store store; + char str[] = "1234567890"; + store.push_back(std::cref(str)); + int a1_val{42}; + auto a1 = fmt::arg("a1_", a1_val); + store.push_back(std::cref(a1)); + + // Changing str affects the output but only for string and custom types. + str[0] = 'X'; + + std::string result = fmt::vformat("{} and {a1_}"); + assert(result == "X234567890 and 42"); + \endrst + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + detail::is_named_arg::type>::value || + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } + + /** + Adds named argument into the dynamic store for later passing to a formatting + function. ``std::reference_wrapper`` is supported to avoid copying of the + argument. + */ + template + void push_back(const detail::named_arg& arg) { + const char_type* arg_name = + dynamic_args_.push>(arg.name).c_str(); + if (detail::const_check(need_copy::value)) { + emplace_arg( + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + } else { + emplace_arg(fmt::arg(arg_name, arg.value)); + } + } + + /** Erase all elements from the store */ + void clear() { + data_.clear(); + named_info_.clear(); + dynamic_args_ = detail::dynamic_arg_list(); + } + + /** + \rst + Reserves space to store at least *new_cap* arguments including + *new_cap_named* named arguments. + \endrst + */ + void reserve(size_t new_cap, size_t new_cap_named) { + FMT_ASSERT(new_cap >= new_cap_named, + "Set of arguments includes set of named arguments"); + data_.reserve(new_cap); + named_info_.reserve(new_cap_named); + } +}; + +/** + \rst + A view of a collection of formatting arguments. To avoid lifetime issues it + should only be used as a parameter type in type-erased functions such as + ``vformat``:: + + void vlog(string_view format_str, format_args args); // OK + format_args args = make_format_args(42); // Error: dangling reference + \endrst + */ +template class basic_format_args { + public: + using size_type = int; + using format_arg = basic_format_arg; + + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const detail::value* values_; + const format_arg* args_; + }; + + bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; } + bool has_named_args() const { + return (desc_ & detail::has_named_args_bit) != 0; + } + + detail::type type(int index) const { + int shift = index * detail::packed_arg_bits; + unsigned int mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + basic_format_args(unsigned long long desc, + const detail::value* values) + : desc_(desc), values_(values) {} + basic_format_args(unsigned long long desc, const format_arg* args) + : desc_(desc), args_(args) {} + + public: + basic_format_args() : desc_(0) {} + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template + FMT_INLINE basic_format_args(const format_arg_store& store) + : basic_format_args(store.desc, store.data_.args()) {} + + /** + \rst + Constructs a `basic_format_args` object from + `~fmt::dynamic_format_arg_store`. + \endrst + */ + FMT_INLINE basic_format_args(const dynamic_format_arg_store& store) + : basic_format_args(store.get_types(), store.data()) {} + + /** + \rst + Constructs a `basic_format_args` object from a dynamic set of arguments. + \endrst + */ + basic_format_args(const format_arg* args, int count) + : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), + args) {} + + /** Returns the argument with the specified id. */ + format_arg get(int id) const { + format_arg arg; + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (id >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ == detail::type::none_type) return arg; + arg.value_ = values_[id]; + return arg; + } + + template format_arg get(basic_string_view name) const { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template int get_id(basic_string_view name) const { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + int max_size() const { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +#ifdef FMT_ARM_ABI_COMPATIBILITY +/** An alias to ``basic_format_args``. */ +// Separate types would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; +using wformat_args = basic_format_args; +#else +// DEPRECATED! These are kept for ABI compatibility. +// It is a separate type rather than an alias to make symbols readable. +struct format_args : basic_format_args { + template + FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {} +}; +struct wformat_args : basic_format_args { + using basic_format_args::basic_format_args; +}; +#endif + +namespace detail { + +template ::value)> +std::basic_string vformat( + basic_string_view format_str, + basic_format_args>> args); + +FMT_API std::string vformat(string_view format_str, format_args args); + +template +void vformat_to( + buffer& buf, basic_string_view format_str, + basic_format_args)> args, + detail::locale_ref loc = {}); + +template ::value)> +inline void vprint_mojibake(std::FILE*, basic_string_view, const Args&) {} + +FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); +#ifndef _WIN32 +inline void vprint_mojibake(std::FILE*, string_view, format_args) {} +#endif +} // namespace detail + +/** Formats a string and writes the output to ``out``. */ +// GCC 8 and earlier cannot handle std::back_insert_iterator with +// vformat_to(...) overload, so SFINAE on iterator type instead. +template , + bool enable = detail::is_output_iterator::value> +auto vformat_to(OutputIt out, const S& format_str, + basic_format_args>> args) + -> typename std::enable_if::type { + decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); + detail::vformat_to(buf, to_string_view(format_str), args); + return detail::get_iterator(buf); +} + +/** + \rst + Formats arguments, writes the result to the output iterator ``out`` and returns + the iterator past the end of the output range. + + **Example**:: + + std::vector out; + fmt::format_to(std::back_inserter(out), "{}", 42); + \endrst + */ +// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3. +template >::value> +inline auto format_to(OutputIt out, const S& format_str, Args&&... args) -> + typename std::enable_if::type { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return vformat_to(out, to_string_view(format_str), vargs); +} + +template struct format_to_n_result { + /** Iterator past the end of the output range. */ + OutputIt out; + /** Total (not truncated) output size. */ + size_t size; +}; + +template ::value)> +inline format_to_n_result vformat_to_n( + OutputIt out, size_t n, basic_string_view format_str, + basic_format_args>> args) { + detail::iterator_buffer buf(out, + n); + detail::vformat_to(buf, format_str, args); + return {buf.out(), buf.count()}; +} + +/** + \rst + Formats arguments, writes up to ``n`` characters of the result to the output + iterator ``out`` and returns the total output size and the iterator past the + end of the output range. + \endrst + */ +template >::value> +inline auto format_to_n(OutputIt out, size_t n, const S& format_str, + const Args&... args) -> + typename std::enable_if>::type { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return vformat_to_n(out, n, to_string_view(format_str), vargs); +} + +/** + Returns the number of characters in the output of + ``format(format_str, args...)``. + */ +template +inline size_t formatted_size(string_view format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + detail::counting_buffer<> buf; + detail::vformat_to(buf, format_str, vargs); + return buf.count(); +} + +template > +FMT_INLINE std::basic_string vformat( + const S& format_str, + basic_format_args>> args) { + return detail::vformat(to_string_view(format_str), args); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}", 42); + \endrst +*/ +// Pass char_t as a default template parameter instead of using +// std::basic_string> to reduce the symbol size. +template > +FMT_INLINE std::basic_string format(const S& format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return detail::vformat(to_string_view(format_str), vargs); +} + +FMT_API void vprint(string_view, format_args); +FMT_API void vprint(std::FILE*, string_view, format_args); + +/** + \rst + Formats ``args`` according to specifications in ``format_str`` and writes the + output to the file ``f``. Strings are assumed to be Unicode-encoded unless the + ``FMT_UNICODE`` macro is set to 0. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template > +inline void print(std::FILE* f, const S& format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return detail::is_unicode() + ? vprint(f, to_string_view(format_str), vargs) + : detail::vprint_mojibake(f, to_string_view(format_str), vargs); +} + +/** + \rst + Formats ``args`` according to specifications in ``format_str`` and writes + the output to ``stdout``. Strings are assumed to be Unicode-encoded unless + the ``FMT_UNICODE`` macro is set to 0. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template > +inline void print(const S& format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return detail::is_unicode() + ? vprint(to_string_view(format_str), vargs) + : detail::vprint_mojibake(stdout, to_string_view(format_str), + vargs); +} +FMT_END_NAMESPACE + +#endif // FMT_CORE_H_ diff --git a/vcpkg/fmt_x86-windows-static/include/fmt/format-inl.h b/vcpkg/fmt_x86-windows-static/include/fmt/format-inl.h new file mode 100644 index 0000000..8f2fe73 --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/include/fmt/format-inl.h @@ -0,0 +1,2801 @@ +// Formatting library for C++ - implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_FORMAT_INL_H_ +#define FMT_FORMAT_INL_H_ + +#include +#include +#include +#include +#include +#include // std::memmove +#include +#include + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +# include +#endif + +#ifdef _WIN32 +# include // _isatty +#endif + +#include "format.h" + +// Dummy implementations of strerror_r and strerror_s called if corresponding +// system functions are not available. +inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; } +inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; } + +FMT_BEGIN_NAMESPACE +namespace detail { + +FMT_FUNC void assert_fail(const char* file, int line, const char* message) { + // Use unchecked std::fprintf to avoid triggering another assertion when + // writing to stderr fails + std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + // Chosen instead of std::abort to satisfy Clang in CUDA mode during device + // code pass. + std::terminate(); +} + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// A portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +inline int safe_strerror(int error_code, char*& buffer, + size_t buffer_size) FMT_NOEXCEPT { + FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer"); + + class dispatcher { + private: + int error_code_; + char*& buffer_; + size_t buffer_size_; + + // A noop assignment operator to avoid bogus warnings. + void operator=(const dispatcher&) {} + + // Handle the result of XSI-compliant version of strerror_r. + int handle(int result) { + // glibc versions before 2.13 return result in errno. + return result == -1 ? errno : result; + } + + // Handle the result of GNU-specific version of strerror_r. + FMT_MAYBE_UNUSED + int handle(char* message) { + // If the buffer is full then the message is probably truncated. + if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) + return ERANGE; + buffer_ = message; + return 0; + } + + // Handle the case when strerror_r is not available. + FMT_MAYBE_UNUSED + int handle(detail::null<>) { + return fallback(strerror_s(buffer_, buffer_size_, error_code_)); + } + + // Fallback to strerror_s when strerror_r is not available. + FMT_MAYBE_UNUSED + int fallback(int result) { + // If the buffer is full then the message is probably truncated. + return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE + : result; + } + +#if !FMT_MSC_VER + // Fallback to strerror if strerror_r and strerror_s are not available. + int fallback(detail::null<>) { + errno = 0; + buffer_ = strerror(error_code_); + return errno; + } +#endif + + public: + dispatcher(int err_code, char*& buf, size_t buf_size) + : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} + + int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); } + }; + return dispatcher(error_code, buffer, buffer_size).run(); +} + +FMT_FUNC void format_error_code(detail::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.try_resize(0); + static const char SEP[] = ": "; + static const char ERROR_STR[] = "error "; + // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. + size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + auto abs_value = static_cast>(error_code); + if (detail::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); + auto it = buffer_appender(out); + if (message.size() <= inline_buffer_size - error_code_size) + format_to(it, "{}{}", message, SEP); + format_to(it, "{}{}", ERROR_STR, error_code); + assert(out.size() <= inline_buffer_size); +} + +FMT_FUNC void report_error(format_func func, int error_code, + string_view message) FMT_NOEXCEPT { + memory_buffer full_message; + func(full_message, error_code, message); + // Don't use fwrite_fully because the latter may throw. + (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} + +// A wrapper around fwrite that throws on error. +inline void fwrite_fully(const void* ptr, size_t size, size_t count, + FILE* stream) { + size_t written = std::fwrite(ptr, size, count, stream); + if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); +} +} // namespace detail + +#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +namespace detail { + +template +locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { + static_assert(std::is_same::value, ""); +} + +template Locale locale_ref::get() const { + static_assert(std::is_same::value, ""); + return locale_ ? *static_cast(locale_) : std::locale(); +} + +template FMT_FUNC std::string grouping_impl(locale_ref loc) { + return std::use_facet>(loc.get()).grouping(); +} +template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .thousands_sep(); +} +template FMT_FUNC Char decimal_point_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .decimal_point(); +} +} // namespace detail +#else +template +FMT_FUNC std::string detail::grouping_impl(locale_ref) { + return "\03"; +} +template FMT_FUNC Char detail::thousands_sep_impl(locale_ref) { + return FMT_STATIC_THOUSANDS_SEPARATOR; +} +template FMT_FUNC Char detail::decimal_point_impl(locale_ref) { + return '.'; +} +#endif + +FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; +FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default; + +FMT_FUNC void system_error::init(int err_code, string_view format_str, + format_args args) { + error_code_ = err_code; + memory_buffer buffer; + format_system_error(buffer, err_code, vformat(format_str, args)); + std::runtime_error& base = *this; + base = std::runtime_error(to_string(buffer)); +} + +namespace detail { + +template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { + // fallback_uintptr is always stored in little endian. + int i = static_cast(sizeof(void*)) - 1; + while (i > 0 && n.value[i] == 0) --i; + auto char_digits = std::numeric_limits::digits / 4; + return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +} + +template +const typename basic_data::digit_pair basic_data::digits[] = { + {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, + {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, + {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, + {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, + {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, + {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, + {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, + {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, + {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, + {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, + {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, + {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, + {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, + {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, + {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, + {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, + {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; + +template +const char basic_data::hex_digits[] = "0123456789abcdef"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 + +template +const uint64_t basic_data::powers_of_10_64[] = { + 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +template +const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, + FMT_POWERS_OF_10(1)}; +template +const uint64_t basic_data::zero_or_powers_of_10_64[] = { + 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +template +const uint32_t basic_data::zero_or_powers_of_10_32_new[] = { + 0, 0, FMT_POWERS_OF_10(1)}; + +template +const uint64_t basic_data::zero_or_powers_of_10_64_new[] = { + 0, 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. +// These are generated by support/compute-powers.py. +template +const uint64_t basic_data::grisu_pow10_significands[] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, +}; + +// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding +// to significands above. +template +const int16_t basic_data::grisu_pow10_exponents[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; + +template +const divtest_table_entry basic_data::divtest_table_for_pow5_32[] = + {{0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, + {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba}, + {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, + {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, + {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897}, + {0x3ed61f49, 0x000001b7}}; + +template +const divtest_table_entry basic_data::divtest_table_for_pow5_64[] = + {{0x0000000000000001, 0xffffffffffffffff}, + {0xcccccccccccccccd, 0x3333333333333333}, + {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, + {0x1cac083126e978d5, 0x020c49ba5e353f7c}, + {0xd288ce703afb7e91, 0x0068db8bac710cb2}, + {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, + {0x790fb65668c26139, 0x000431bde82d7b63}, + {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, + {0xc767074b22e90e21, 0x00002af31dc46118}, + {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, + {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, + {0x0fee64690c913975, 0x00000057f5ff85e5}, + {0x3662e0e1cf503eb1, 0x000000119799812d}, + {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, + {0x54186f653140a659, 0x00000000b424dc35}, + {0x7738164770402145, 0x0000000024075f3d}, + {0xe4a4d1417cd9a041, 0x000000000734aca5}, + {0xc75429d9e5c5200d, 0x000000000170ef54}, + {0xc1773b91fac10669, 0x000000000049c977}, + {0x26b172506559ce15, 0x00000000000ec1e4}, + {0xd489e3a9addec2d1, 0x000000000002f394}, + {0x90e860bb892c8d5d, 0x000000000000971d}, + {0x502e79bf1b6f4f79, 0x0000000000001e39}, + {0xdcd618596be30fe5, 0x000000000000060b}}; + +template +const uint64_t basic_data::dragonbox_pow10_significands_64[] = { + 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, + 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, + 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, + 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, + 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, + 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, + 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, + 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, + 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, + 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, + 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, + 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, + 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, + 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, + 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, + 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, + 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, + 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, + 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6, + 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, + 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, + 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, + 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; + +template +const uint128_wrapper basic_data::dragonbox_pow10_significands_128[] = { +#if FMT_USE_FULL_CACHE_DRAGONBOX + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0x9faacf3df73609b1, 0x77b191618c54e9ad}, + {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, + {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, + {0x9becce62836ac577, 0x4ee367f9430aec33}, + {0xc2e801fb244576d5, 0x229c41f793cda740}, + {0xf3a20279ed56d48a, 0x6b43527578c11110}, + {0x9845418c345644d6, 0x830a13896b78aaaa}, + {0xbe5691ef416bd60c, 0x23cc986bc656d554}, + {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, + {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, + {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, + {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, + {0x91376c36d99995be, 0x23100809b9c21fa2}, + {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, + {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, + {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, + {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, + {0xdd95317f31c7fa1d, 0x40405643d711d584}, + {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, + {0xad1c8eab5ee43b66, 0xda3243650005eed0}, + {0xd863b256369d4a40, 0x90bed43e40076a83}, + {0x873e4f75e2224e68, 0x5a7744a6e804a292}, + {0xa90de3535aaae202, 0x711515d0a205cb37}, + {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, + {0x8412d9991ed58091, 0xe858790afe9486c3}, + {0xa5178fff668ae0b6, 0x626e974dbe39a873}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, + {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, + {0xc987434744ac874e, 0xa327ffb266b56221}, + {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, + {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, + {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, + {0xf6019da07f549b2b, 0x7e2a53a146606a49}, + {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, + {0xc0314325637a1939, 0xfa911155fefb5309}, + {0xf03d93eebc589f88, 0x793555ab7eba27cb}, + {0x96267c7535b763b5, 0x4bc1558b2f3458df}, + {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, + {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, + {0x92a1958a7675175f, 0x0bfacd89ec191eca}, + {0xb749faed14125d36, 0xcef980ec671f667c}, + {0xe51c79a85916f484, 0x82b7e12780e7401b}, + {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, + {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, + {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, + {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, + {0xaecc49914078536d, 0x58fae9f773886e19}, + {0xda7f5bf590966848, 0xaf39a475506a899f}, + {0x888f99797a5e012d, 0x6d8406c952429604}, + {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, + {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, + {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0xd0601d8efc57b08b, 0xf13b94daf124da27}, + {0x823c12795db6ce57, 0x76c53d08d6b70859}, + {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, + {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, + {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, + {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, + {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, + {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, + {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, + {0xc21094364dfb5636, 0x985915fc12f542e5}, + {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, + {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, + {0xbd8430bd08277231, 0x50c6ff782a838354}, + {0xece53cec4a314ebd, 0xa4f8bf5635246429}, + {0x940f4613ae5ed136, 0x871b7795e136be9a}, + {0xb913179899f68584, 0x28e2557b59846e40}, + {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, + {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, + {0xb4bca50b065abe63, 0x0fed077a756b53aa}, + {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, + {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, + {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, + {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, + {0x89e42caaf9491b60, 0xf41686c49db57245}, + {0xac5d37d5b79b6239, 0x311c2875c522ced6}, + {0xd77485cb25823ac7, 0x7d633293366b828c}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, + {0xd267caa862a12d66, 0xd072df63c324fd7c}, + {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, + {0xa46116538d0deb78, 0x52d9be85f074e609}, + {0xcd795be870516656, 0x67902e276c921f8c}, + {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, + {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, + {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, + {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, + {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, + {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, + {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, + {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, + {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, + {0xef340a98172aace4, 0x86fb897116c87c35}, + {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, + {0xbae0a846d2195712, 0x8974836059cca10a}, + {0xe998d258869facd7, 0x2bd1a438703fc94c}, + {0x91ff83775423cc06, 0x7b6306a34627ddd0}, + {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, + {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, + {0x8e938662882af53e, 0x547eb47b7282ee9d}, + {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, + {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, + {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, + {0xae0b158b4738705e, 0x9624ab50b148d446}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, + {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, + {0xd47487cc8470652b, 0x7647c32000696720}, + {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, + {0xa5fb0a17c777cf09, 0xf468107100525891}, + {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, + {0x81ac1fe293d599bf, 0xc6f14cd848405531}, + {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, + {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, + {0xfd442e4688bd304a, 0x908f4a166d1da664}, + {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, + {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, + {0xf7549530e188c128, 0xd12bee59e68ef47d}, + {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, + {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, + {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, + {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, + {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, + {0xebdf661791d60f56, 0x111b495b3464ad22}, + {0x936b9fcebb25c995, 0xcab10dd900beec35}, + {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, + {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, + {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, + {0xb3f4e093db73a093, 0x59ed216765690f57}, + {0xe0f218b8d25088b8, 0x306869c13ec3532d}, + {0x8c974f7383725573, 0x1e414218c73a13fc}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, + {0x894bc396ce5da772, 0x6b8bba8c328eb784}, + {0xab9eb47c81f5114f, 0x066ea92f3f326565}, + {0xd686619ba27255a2, 0xc80a537b0efefebe}, + {0x8613fd0145877585, 0xbd06742ce95f5f37}, + {0xa798fc4196e952e7, 0x2c48113823b73705}, + {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, + {0x82ef85133de648c4, 0x9a984d73dbe722fc}, + {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, + {0xcc963fee10b7d1b3, 0x318df905079926a9}, + {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, + {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, + {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, + {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, + {0x9c1661a651213e2d, 0x06bea10ca65c084f}, + {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, + {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, + {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, + {0xbe89523386091465, 0xf6bbb397f1135824}, + {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, + {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, + {0xba121a4650e4ddeb, 0x92f34d62616ce414}, + {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, + {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, + {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, + {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, + {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, + {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, + {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, + {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, + {0x87625f056c7c4a8b, 0x11471cd764ad4973}, + {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, + {0xd389b47879823479, 0x4aff1d108d4ec2c4}, + {0x843610cb4bf160cb, 0xcedf722a585139bb}, + {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, + {0xce947a3da6a9273e, 0x733d226229feea33}, + {0x811ccc668829b887, 0x0806357d5a3f5260}, + {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, + {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, + {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, + {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, + {0xc5029163f384a931, 0x0a9e795e65d4df12}, + {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, + {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, + {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, + {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, + {0x964e858c91ba2655, 0x3a6a07f8d510f870}, + {0xbbe226efb628afea, 0x890489f70a55368c}, + {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, + {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, + {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, + {0xb32df8e9f3546564, 0x47939822dc96abfa}, + {0xdff9772470297ebd, 0x59787e2b93bc56f8}, + {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, + {0xaefae51477a06b03, 0xede622920b6b23f2}, + {0xdab99e59958885c4, 0xe95fab368e45ecee}, + {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, + {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, + {0xd59944a37c0752a2, 0x4be76d3346f04960}, + {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, + {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, + {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, + {0x825ecc24c873782f, 0x8ed400668c0c28c9}, + {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, + {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, + {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, + {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, + {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, + {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, + {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, + {0xc24452da229b021b, 0xfbe85badce996169}, + {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, + {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, + {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, + {0xed246723473e3813, 0x290123e9aab23b69}, + {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, + {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, + {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, + {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, + {0x8d590723948a535f, 0x579c487e5a38ad0f}, + {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, + {0xdcdb1b2798182244, 0xf8e431456cf88e66}, + {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, + {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, + {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, + {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, + {0xa87fea27a539e9a5, 0x3f2398d747b36225}, + {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, + {0x83a3eeeef9153e89, 0x1953cf68300424ad}, + {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, + {0xcdb02555653131b6, 0x3792f412cb06794e}, + {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, + {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, + {0xc8de047564d20a8b, 0xf245825a5a445276}, + {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, + {0x9ced737bb6c4183d, 0x55464dd69685606c}, + {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, + {0xf53304714d9265df, 0xd53dd99f4b3066a9}, + {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, + {0xbf8fdb78849a5f96, 0xde98520472bdd034}, + {0xef73d256a5c0f77c, 0x963e66858f6d4441}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xbb127c53b17ec159, 0x5560c018580d5d53}, + {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, + {0x9226712162ab070d, 0xcab3961304ca70e9}, + {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, + {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, + {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, + {0xb267ed1940f1c61c, 0x55f038b237591ed4}, + {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, + {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, + {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, + {0xd9c7dced53c72255, 0x96e7bd358c904a22}, + {0x881cea14545c7575, 0x7e50d64177da2e55}, + {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, + {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, + {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, + {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, + {0xcfb11ead453994ba, 0x67de18eda5814af3}, + {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, + {0xa2425ff75e14fc31, 0xa1258379a94d028e}, + {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, + {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, + {0x9e74d1b791e07e48, 0x775ea264cf55347e}, + {0xc612062576589dda, 0x95364afe032a819e}, + {0xf79687aed3eec551, 0x3a83ddbd83f52205}, + {0x9abe14cd44753b52, 0xc4926a9672793543}, + {0xc16d9a0095928a27, 0x75b7053c0f178294}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, + {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, + {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, + {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, + {0xb877aa3236a4b449, 0x09befeb9fad487c3}, + {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, + {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, + {0xb424dc35095cd80f, 0x538484c19ef38c95}, + {0xe12e13424bb40e13, 0x2865a5f206b06fba}, + {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, + {0xafebff0bcb24aafe, 0xf78f69a51539d749}, + {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, + {0x89705f4136b4a597, 0x31680a88f8953031}, + {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, + {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, + {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, + {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, + {0xd1b71758e219652b, 0xd3c36113404ea4a9}, + {0x83126e978d4fdf3b, 0x645a1cac083126ea}, + {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, + {0xcccccccccccccccc, 0xcccccccccccccccd}, + {0x8000000000000000, 0x0000000000000000}, + {0xa000000000000000, 0x0000000000000000}, + {0xc800000000000000, 0x0000000000000000}, + {0xfa00000000000000, 0x0000000000000000}, + {0x9c40000000000000, 0x0000000000000000}, + {0xc350000000000000, 0x0000000000000000}, + {0xf424000000000000, 0x0000000000000000}, + {0x9896800000000000, 0x0000000000000000}, + {0xbebc200000000000, 0x0000000000000000}, + {0xee6b280000000000, 0x0000000000000000}, + {0x9502f90000000000, 0x0000000000000000}, + {0xba43b74000000000, 0x0000000000000000}, + {0xe8d4a51000000000, 0x0000000000000000}, + {0x9184e72a00000000, 0x0000000000000000}, + {0xb5e620f480000000, 0x0000000000000000}, + {0xe35fa931a0000000, 0x0000000000000000}, + {0x8e1bc9bf04000000, 0x0000000000000000}, + {0xb1a2bc2ec5000000, 0x0000000000000000}, + {0xde0b6b3a76400000, 0x0000000000000000}, + {0x8ac7230489e80000, 0x0000000000000000}, + {0xad78ebc5ac620000, 0x0000000000000000}, + {0xd8d726b7177a8000, 0x0000000000000000}, + {0x878678326eac9000, 0x0000000000000000}, + {0xa968163f0a57b400, 0x0000000000000000}, + {0xd3c21bcecceda100, 0x0000000000000000}, + {0x84595161401484a0, 0x0000000000000000}, + {0xa56fa5b99019a5c8, 0x0000000000000000}, + {0xcecb8f27f4200f3a, 0x0000000000000000}, + {0x813f3978f8940984, 0x4000000000000000}, + {0xa18f07d736b90be5, 0x5000000000000000}, + {0xc9f2c9cd04674ede, 0xa400000000000000}, + {0xfc6f7c4045812296, 0x4d00000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xc5371912364ce305, 0x6c28000000000000}, + {0xf684df56c3e01bc6, 0xc732000000000000}, + {0x9a130b963a6c115c, 0x3c7f400000000000}, + {0xc097ce7bc90715b3, 0x4b9f100000000000}, + {0xf0bdc21abb48db20, 0x1e86d40000000000}, + {0x96769950b50d88f4, 0x1314448000000000}, + {0xbc143fa4e250eb31, 0x17d955a000000000}, + {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, + {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, + {0xb7abc627050305ad, 0xf14a3d9e40000000}, + {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, + {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, + {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, + {0xe0352f62a19e306e, 0xd50b2037ad200000}, + {0x8c213d9da502de45, 0x4526f422cc340000}, + {0xaf298d050e4395d6, 0x9670b12b7f410000}, + {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, + {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, + {0xab0e93b6efee0053, 0x8eea0d047a457a00}, + {0xd5d238a4abe98068, 0x72a4904598d6d880}, + {0x85a36366eb71f041, 0x47a6da2b7f864750}, + {0xa70c3c40a64e6c51, 0x999090b65f67d924}, + {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, + {0x82818f1281ed449f, 0xbff8f10e7a8921a4}, + {0xa321f2d7226895c7, 0xaff72d52192b6a0d}, + {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490}, + {0xfee50b7025c36a08, 0x02f236d04753d5b4}, + {0x9f4f2726179a2245, 0x01d762422c946590}, + {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5}, + {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2}, + {0x9b934c3b330c8577, 0x63cc55f49f88eb2f}, + {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb}, + {0xf316271c7fc3908a, 0x8bef464e3945ef7a}, + {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac}, + {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317}, + {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd}, + {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a}, + {0xb975d6b6ee39e436, 0xb3e2fd538e122b44}, + {0xe7d34c64a9c85d44, 0x60dbbca87196b616}, + {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd}, + {0xb51d13aea4a488dd, 0x6babab6398bdbe41}, + {0xe264589a4dcdab14, 0xc696963c7eed2dd1}, + {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2}, + {0xb0de65388cc8ada8, 0x3b25a55f43294bcb}, + {0xdd15fe86affad912, 0x49ef0eb713f39ebe}, + {0x8a2dbf142dfcc7ab, 0x6e3569326c784337}, + {0xacb92ed9397bf996, 0x49c2c37f07965404}, + {0xd7e77a8f87daf7fb, 0xdc33745ec97be906}, + {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3}, + {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c}, + {0xd2d80db02aabd62b, 0xf50a3fa490c30190}, + {0x83c7088e1aab65db, 0x792667c6da79e0fa}, + {0xa4b8cab1a1563f52, 0x577001b891185938}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, + {0x80b05e5ac60b6178, 0x544f8158315b05b4}, + {0xa0dc75f1778e39d6, 0x696361ae3db1c721}, + {0xc913936dd571c84c, 0x03bc3a19cd1e38e9}, + {0xfb5878494ace3a5f, 0x04ab48a04065c723}, + {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76}, + {0xc45d1df942711d9a, 0x3ba5d0bd324f8394}, + {0xf5746577930d6500, 0xca8f44ec7ee36479}, + {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb}, + {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e}, + {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e}, + {0x95d04aee3b80ece5, 0xbba1f1d158724a12}, + {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97}, + {0xea1575143cf97226, 0xf52d09d71a3293bd}, + {0x924d692ca61be758, 0x593c2626705f9c56}, + {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c}, + {0xe498f455c38b997a, 0x0b6dfb9c0f956447}, + {0x8edf98b59a373fec, 0x4724bd4189bd5eac}, + {0xb2977ee300c50fe7, 0x58edec91ec2cb657}, + {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed}, + {0x8b865b215899f46c, 0xbd79e0d20082ee74}, + {0xae67f1e9aec07187, 0xecd8590680a3aa11}, + {0xda01ee641a708de9, 0xe80e6f4820cc9495}, + {0x884134fe908658b2, 0x3109058d147fdcdd}, + {0xaa51823e34a7eede, 0xbd4b46f0599fd415}, + {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a}, + {0x850fadc09923329e, 0x03e2cf6bc604ddb0}, + {0xa6539930bf6bff45, 0x84db8346b786151c}, + {0xcfe87f7cef46ff16, 0xe612641865679a63}, + {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e}, + {0xa26da3999aef7749, 0xe3be5e330f38f09d}, + {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5}, + {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6}, + {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa}, + {0xc646d63501a1511d, 0xb281e1fd541501b8}, + {0xf7d88bc24209a565, 0x1f225a7ca91a4226}, + {0x9ae757596946075f, 0x3375788de9b06958}, + {0xc1a12d2fc3978937, 0x0052d6b1641c83ae}, + {0xf209787bb47d6b84, 0xc0678c5dbd23a49a}, + {0x9745eb4d50ce6332, 0xf840b7ba963646e0}, + {0xbd176620a501fbff, 0xb650e5a93bc3d898}, + {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe}, + {0x93ba47c980e98cdf, 0xc66f336c36b10137}, + {0xb8a8d9bbe123f017, 0xb80b0047445d4184}, + {0xe6d3102ad96cec1d, 0xa60dc059157491e5}, + {0x9043ea1ac7e41392, 0x87c89837ad68db2f}, + {0xb454e4a179dd1877, 0x29babe4598c311fb}, + {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a}, + {0x8ce2529e2734bb1d, 0x1899e4a65f58660c}, + {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f}, + {0xdc21a1171d42645d, 0x76707543f4fa1f73}, + {0x899504ae72497eba, 0x6a06494a791c53a8}, + {0xabfa45da0edbde69, 0x0487db9d17636892}, + {0xd6f8d7509292d603, 0x45a9d2845d3c42b6}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, + {0xa7f26836f282b732, 0x8e6cac7768d7141e}, + {0xd1ef0244af2364ff, 0x3207d795430cd926}, + {0x8335616aed761f1f, 0x7f44e6bd49e807b8}, + {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6}, + {0xcd036837130890a1, 0x36dba887c37a8c0f}, + {0x802221226be55a64, 0xc2494954da2c9789}, + {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c}, + {0xc83553c5c8965d3d, 0x6f92829494e5acc7}, + {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9}, + {0x9c69a97284b578d7, 0xff2a760414536efb}, + {0xc38413cf25e2d70d, 0xfef5138519684aba}, + {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69}, + {0x98bf2f79d5993802, 0xef2f773ffbd97a61}, + {0xbeeefb584aff8603, 0xaafb550ffacfd8fa}, + {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38}, + {0x952ab45cfa97a0b2, 0xdd945a747bf26183}, + {0xba756174393d88df, 0x94f971119aeef9e4}, + {0xe912b9d1478ceb17, 0x7a37cd5601aab85d}, + {0x91abb422ccb812ee, 0xac62e055c10ab33a}, + {0xb616a12b7fe617aa, 0x577b986b314d6009}, + {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b}, + {0x8e41ade9fbebc27d, 0x14588f13be847307}, + {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8}, + {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb}, + {0x8aec23d680043bee, 0x25de7bb9480d5854}, + {0xada72ccc20054ae9, 0xaf561aa79a10ae6a}, + {0xd910f7ff28069da4, 0x1b2ba1518094da04}, + {0x87aa9aff79042286, 0x90fb44d2f05d0842}, + {0xa99541bf57452b28, 0x353a1607ac744a53}, + {0xd3fa922f2d1675f2, 0x42889b8997915ce8}, + {0x847c9b5d7c2e09b7, 0x69956135febada11}, + {0xa59bc234db398c25, 0x43fab9837e699095}, + {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb}, + {0x8161afb94b44f57d, 0x1d1be0eebac278f5}, + {0xa1ba1ba79e1632dc, 0x6462d92a69731732}, + {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe}, + {0xfcb2cb35e702af78, 0x5cda735244c3d43e}, + {0x9defbf01b061adab, 0x3a0888136afa64a7}, + {0xc56baec21c7a1916, 0x088aaa1845b8fdd0}, + {0xf6c69a72a3989f5b, 0x8aad549e57273d45}, + {0x9a3c2087a63f6399, 0x36ac54e2f678864b}, + {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd}, + {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5}, + {0x969eb7c47859e743, 0x9f644ae5a4b1b325}, + {0xbc4665b596706114, 0x873d5d9f0dde1fee}, + {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea}, + {0x9316ff75dd87cbd8, 0x09a7f12442d588f2}, + {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f}, + {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa}, + {0x8fa475791a569d10, 0xf96e017d694487bc}, + {0xb38d92d760ec4455, 0x37c981dcc395a9ac}, + {0xe070f78d3927556a, 0x85bbe253f47b1417}, + {0x8c469ab843b89562, 0x93956d7478ccec8e}, + {0xaf58416654a6babb, 0x387ac8d1970027b2}, + {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e}, + {0x88fcf317f22241e2, 0x441fece3bdf81f03}, + {0xab3c2fddeeaad25a, 0xd527e81cad7626c3}, + {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074}, + {0x85c7056562757456, 0xf6872d5667844e49}, + {0xa738c6bebb12d16c, 0xb428f8ac016561db}, + {0xd106f86e69d785c7, 0xe13336d701beba52}, + {0x82a45b450226b39c, 0xecc0024661173473}, + {0xa34d721642b06084, 0x27f002d7f95d0190}, + {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4}, + {0xff290242c83396ce, 0x7e67047175a15271}, + {0x9f79a169bd203e41, 0x0f0062c6e984d386}, + {0xc75809c42c684dd1, 0x52c07b78a3e60868}, + {0xf92e0c3537826145, 0xa7709a56ccdf8a82}, + {0x9bbcc7a142b17ccb, 0x88a66076400bb691}, + {0xc2abf989935ddbfe, 0x6acff893d00ea435}, + {0xf356f7ebf83552fe, 0x0583f6b8c4124d43}, + {0x98165af37b2153de, 0xc3727a337a8b704a}, + {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c}, + {0xeda2ee1c7064130c, 0x1162def06f79df73}, + {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8}, + {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692}, + {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437}, + {0x910ab1d4db9914a0, 0x1d9c9892400a22a2}, + {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b}, + {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, + {0xb10d8e1456105dad, 0x7425a83e872c5f47}, + {0xdd50f1996b947518, 0xd12f124e28f77719}, + {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f}, + {0xace73cbfdc0bfb7b, 0x636cc64d1001550b}, + {0xd8210befd30efa5a, 0x3c47f7e05401aa4e}, + {0x8714a775e3e95c78, 0x65acfaec34810a71}, + {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d}, + {0xd31045a8341ca07c, 0x1ede48111209a050}, + {0x83ea2b892091e44d, 0x934aed0aab460432}, + {0xa4e4b66b68b65d60, 0xf81da84d5617853f}, + {0xce1de40642e3f4b9, 0x36251260ab9d668e}, + {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019}, + {0xa1075a24e4421730, 0xb24cf65b8612f81f}, + {0xc94930ae1d529cfc, 0xdee033f26797b627}, + {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1}, + {0x9d412e0806e88aa5, 0x8e1f289560ee864e}, + {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2}, + {0xf5b5d7ec8acb58a2, 0xae10af696774b1db}, + {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29}, + {0xbff610b0cc6edd3f, 0x17fd090a58d32af3}, + {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0}, + {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e}, + {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1}, + {0xea53df5fd18d5513, 0x84c86189216dc5ed}, + {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4}, + {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, + {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e}, + {0xb2c71d5bca9023f8, 0x743e20e9ef511012}, + {0xdf78e4b2bd342cf6, 0x914da9246b255416}, + {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e}, + {0xae9672aba3d0c320, 0xa184ac2473b529b1}, + {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e}, + {0x8865899617fb1871, 0x7e2fa67c7a658892}, + {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7}, + {0xd51ea6fa85785631, 0x552a74227f3ea565}, + {0x8533285c936b35de, 0xd53a88958f87275f}, + {0xa67ff273b8460356, 0x8a892abaf368f137}, + {0xd01fef10a657842c, 0x2d2b7569b0432d85}, + {0x8213f56a67f6b29b, 0x9c3b29620e29fc73}, + {0xa298f2c501f45f42, 0x8349f3ba91b47b8f}, + {0xcb3f2f7642717713, 0x241c70a936219a73}, + {0xfe0efb53d30dd4d7, 0xed238cd383aa0110}, + {0x9ec95d1463e8a506, 0xf4363804324a40aa}, + {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5}, + {0xf81aa16fdc1b81da, 0xdd94b7868e94050a}, + {0x9b10a4e5e9913128, 0xca7cf2b4191c8326}, + {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0}, + {0xf24a01a73cf2dccf, 0xbc633b39673c8cec}, + {0x976e41088617ca01, 0xd5be0503e085d813}, + {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18}, + {0xec9c459d51852ba2, 0xddf8e7d60ed1219e}, + {0x93e1ab8252f33b45, 0xcabb90e5c942b503}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, + {0xe7109bfba19c0c9d, 0x0cc512670a783ad4}, + {0x906a617d450187e2, 0x27fb2b80668b24c5}, + {0xb484f9dc9641e9da, 0xb1f9f660802dedf6}, + {0xe1a63853bbd26451, 0x5e7873f8a0396973}, + {0x8d07e33455637eb2, 0xdb0b487b6423e1e8}, + {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62}, + {0xdc5c5301c56b75f7, 0x7641a140cc7810fb}, + {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d}, + {0xac2820d9623bf429, 0x546345fa9fbdcd44}, + {0xd732290fbacaf133, 0xa97c177947ad4095}, + {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d}, + {0xa81f301449ee8c70, 0x5c68f256bfff5a74}, + {0xd226fc195c6a2f8c, 0x73832eec6fff3111}, + {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab}, + {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55}, + {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb}, + {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3}, + {0xa0555e361951c366, 0xd7e105bcc332621f}, + {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7}, + {0xfa856334878fc150, 0xb14f98f6f0feb951}, + {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3}, + {0xc3b8358109e84f07, 0x0a862f80ec4700c8}, + {0xf4a642e14c6262c8, 0xcd27bb612758c0fa}, + {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c}, + {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3}, + {0xeeea5d5004981478, 0x1858ccfce06cac74}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc8}, + {0xbaa718e68396cffd, 0xd30560258f54e6ba}, + {0xe950df20247c83fd, 0x47c6b82ef32a2069}, + {0x91d28b7416cdd27e, 0x4cdc331d57fa5441}, + {0xb6472e511c81471d, 0xe0133fe4adf8e952}, + {0xe3d8f9e563a198e5, 0x58180fddd97723a6}, + {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648}, + {0xb201833b35d63f73, 0x2cd2cc6551e513da}, + {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1}, + {0x8b112e86420f6191, 0xfb04afaf27faf782}, + {0xadd57a27d29339f6, 0x79c5db9af1f9b563}, + {0xd94ad8b1c7380874, 0x18375281ae7822bc}, + {0x87cec76f1c830548, 0x8f2293910d0b15b5}, + {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22}, + {0xd433179d9c8cb841, 0x5fa60692a46151eb}, + {0x849feec281d7f328, 0xdbc7c41ba6bcd333}, + {0xa5c7ea73224deff3, 0x12b9b522906c0800}, + {0xcf39e50feae16bef, 0xd768226b34870a00}, + {0x81842f29f2cce375, 0xe6a1158300d46640}, + {0xa1e53af46f801c53, 0x60495ae3c1097fd0}, + {0xca5e89b18b602368, 0x385bb19cb14bdfc4}, + {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5}, + {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1}, + {0xc5a05277621be293, 0xc7098b7305241885}, + {0xf70867153aa2db38, 0xb8cbee4fc66d1ea7} +#else + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0xc350000000000000, 0x0000000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xfee50b7025c36a08, 0x02f236d04753d5b4}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, + {0xa6539930bf6bff45, 0x84db8346b786151c}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, + {0xd910f7ff28069da4, 0x1b2ba1518094da04}, + {0xaf58416654a6babb, 0x387ac8d1970027b2}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc8} +#endif +}; + +#if !FMT_USE_FULL_CACHE_DRAGONBOX +template +const uint64_t basic_data::powers_of_5_64[] = { + 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, + 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, + 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, + 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, + 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, + 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, + 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, + 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, + 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; + +template +const uint32_t basic_data::dragonbox_pow10_recovery_errors[] = { + 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001, + 0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555, + 0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110, + 0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454, + 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014, + 0x69514555, 0x05151109, 0x00155555}; +#endif + +template +const char basic_data::foreground_color[] = "\x1b[38;2;"; +template +const char basic_data::background_color[] = "\x1b[48;2;"; +template const char basic_data::reset_color[] = "\x1b[0m"; +template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; +template const char basic_data::signs[] = {0, '-', '+', ' '}; +template +const char basic_data::left_padding_shifts[] = {31, 31, 0, 1, 0}; +template +const char basic_data::right_padding_shifts[] = {0, 31, 0, 1, 0}; + +template struct bits { + static FMT_CONSTEXPR_DECL const int value = + static_cast(sizeof(T) * std::numeric_limits::digits); +}; + +class fp; +template fp normalize(fp value); + +// Lower (upper) boundary is a value half way between a floating-point value +// and its predecessor (successor). Boundaries have the same exponent as the +// value so only significands are stored. +struct boundaries { + uint64_t lower; + uint64_t upper; +}; + +// A handmade floating-point number f * pow(2, e). +class fp { + private: + using significand_type = uint64_t; + + template + using is_supported_float = bool_constant; + + public: + significand_type f; + int e; + + // All sizes are in bits. + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + static FMT_CONSTEXPR_DECL const int double_significand_size = + std::numeric_limits::digits - 1; + static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = + 1ULL << double_significand_size; + static FMT_CONSTEXPR_DECL const int significand_size = + bits::value; + + fp() : f(0), e(0) {} + fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 double. It is a template to prevent compile + // errors on platforms where double is not IEEE754. + template explicit fp(Double d) { assign(d); } + + // Assigns d to this and return true iff predecessor is closer than successor. + template ::value)> + bool assign(Float d) { + // Assume float is in the format [sign][exponent][significand]. + using limits = std::numeric_limits; + const int float_significand_size = limits::digits - 1; + const int exponent_size = + bits::value - float_significand_size - 1; // -1 for sign + const uint64_t float_implicit_bit = 1ULL << float_significand_size; + const uint64_t significand_mask = float_implicit_bit - 1; + const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; + const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; + constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); + auto u = bit_cast>(d); + f = u & significand_mask; + int biased_e = + static_cast((u & exponent_mask) >> float_significand_size); + // Predecessor is closer if d is a normalized power of 2 (f == 0) other than + // the smallest normalized number (biased_e > 1). + bool is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e != 0) + f += float_implicit_bit; + else + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + e = biased_e - exponent_bias - float_significand_size; + return is_predecessor_closer; + } + + template ::value)> + bool assign(Float) { + *this = fp(); + return false; + } +}; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template fp normalize(fp value) { + // Handle subnormals. + const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = + fp::significand_size - fp::double_significand_size - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; } + +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +inline fp get_cached_power(int min_exponent, int& pow10_exponent) { + const int shift = 32; + const auto significand = static_cast(data::log10_2_significand); + int index = static_cast( + ((min_exponent + fp::significand_size - 1) * (significand >> shift) + + ((int64_t(1) << shift) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return {data::grisu_pow10_significands[index], + data::grisu_pow10_exponents[index]}; +} + +// A simple accumulator to hold the sums of terms in bigint::square if uint128_t +// is not available. +struct accumulator { + uint64_t lower; + uint64_t upper; + + accumulator() : lower(0), upper(0) {} + explicit operator uint32_t() const { return static_cast(lower); } + + void operator+=(uint64_t n) { + lower += n; + if (lower < n) ++upper; + } + void operator>>=(int shift) { + assert(shift == 32); + (void)shift; + lower = (upper << 32) | (lower >> 32); + upper >>= 32; + } +}; + +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + bigit operator[](int index) const { return bigits_[to_unsigned(index)]; } + bigit& operator[](int index) { return bigits_[to_unsigned(index)]; } + + static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; + + friend struct formatter; + + void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + void subtract_aligned(const bigint& other) { + FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); + FMT_ASSERT(compare(*this, other) >= 0, ""); + bigit borrow = 0; + int i = other.exp_ - exp_; + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) + subtract_bigits(i, other.bigits_[j], borrow); + while (borrow > 0) subtract_bigits(i, 0, borrow); + remove_leading_zeros(); + } + + void multiply(uint32_t value) { + const double_bigit wide_value = value; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + void multiply(uint64_t value) { + const bigit mask = ~bigit(0); + const double_bigit lower = value & mask; + const double_bigit upper = value >> bigit_bits; + double_bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * lower + (carry & mask); + carry = + bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(carry & mask); + carry >>= bigit_bits; + } + } + + public: + bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + ~bigint() { assert(bigits_.capacity() <= bigits_capacity); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + void assign(const bigint& other) { + auto size = other.bigits_.size(); + bigits_.resize(size); + auto data = other.bigits_.data(); + std::copy(data, data + size, make_checked(bigits_.data(), size)); + exp_ = other.exp_; + } + + void assign(uint64_t n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = n & ~bigit(0); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + int num_bigits() const { return static_cast(bigits_.size()) + exp_; } + + FMT_NOINLINE bigint& operator<<=(int shift) { + assert(shift >= 0); + exp_ += shift / bigit_bits; + shift %= bigit_bits; + if (shift == 0) return *this; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + bigit c = bigits_[i] >> (bigit_bits - shift); + bigits_[i] = (bigits_[i] << shift) + carry; + carry = c; + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } + + template bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend int compare(const bigint& lhs, const bigint& rhs) { + int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); + if (num_lhs_bigits != num_rhs_bigits) + return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + int i = static_cast(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); + int num_rhs_bigits = rhs.num_bigits(); + if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; + if (max_lhs_bigits > num_rhs_bigits) return 1; + auto get_bigit = [](const bigint& n, int i) -> bigit { + return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; + }; + double_bigit borrow = 0; + int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = + static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); + bigit rhs_bigit = get_bigit(rhs, i); + if (sum > rhs_bigit + borrow) return 1; + borrow = rhs_bigit + borrow - sum; + if (borrow > 1) return -1; + borrow <<= bigit_bits; + } + return borrow != 0 ? -1 : 0; + } + + // Assigns pow(10, exp) to this bigint. + void assign_pow10(int exp) { + assert(exp >= 0); + if (exp == 0) return assign(1); + // Find the top bit. + int bitmask = 1; + while (exp >= bitmask) bitmask <<= 1; + bitmask >>= 1; + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + assign(5); + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + void square() { + basic_memory_buffer n(std::move(bigits_)); + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + bigits_.resize(to_unsigned(num_result_bigits)); + using accumulator_t = conditional_t; + auto sum = accumulator_t(); + for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { + // Compute bigit at position bigit_index of the result by adding + // cross-product terms n[i] * n[j] such that i + j == bigit_index. + for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { + // Most terms are multiplied twice which can be optimized in the future. + sum += static_cast(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; + } + --num_result_bigits; + remove_leading_zeros(); + exp_ *= 2; + } + + // If this bigint has a bigger exponent than other, adds trailing zero to make + // exponents equal. This simplifies some operations such as subtraction. + void align(const bigint& other) { + int exp_difference = exp_ - other.exp_; + if (exp_difference <= 0) return; + int num_bigits = static_cast(bigits_.size()); + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + exp_ -= exp_difference; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + align(divisor); + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +template +FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, + int& exp, Handler& handler) { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = count_digits(integral); // kappa in Grisu. + // Divide by 10 to prevent overflow. + auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e, + value.f / 10, error * 10, exp); + if (result != digits::more) return result; + // Generate digits for the integral part. This can produce up to 10 digits. + do { + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); + } + --exp; + auto remainder = (static_cast(integral) << -one.e) + fractional; + result = handler.on_digit(static_cast('0' + digit), + data::powers_of_10_64[exp] << -one.e, remainder, + error, exp, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = static_cast('0' + (fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + result = handler.on_digit(digit, one.f, fractional, error, exp, false); + if (result != digits::more) return result; + } +} + +// The fixed precision digit handler. +struct fixed_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error, + int& exp) { + // Non-fixed formats require at least one digit and no precision adjustment. + if (!fixed) return digits::more; + // Adjust fixed precision by exponent because it is relative to decimal + // point. + precision += exp + exp10; + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (precision > 0) return digits::more; + if (precision < 0) return digits::done; + auto dir = get_round_direction(divisor, remainder, error); + if (dir == round_direction::unknown) return digits::error; + buf[size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + + digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, + uint64_t error, int, bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; + } +}; + +// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. +namespace dragonbox { +// Computes 128-bit result of multiplication of two 64-bit unsigned integers. +FMT_SAFEBUFFERS inline uint128_wrapper umul128(uint64_t x, + uint64_t y) FMT_NOEXCEPT { +#if FMT_USE_INT128 + return static_cast(x) * static_cast(y); +#elif defined(_MSC_VER) && defined(_M_X64) + uint128_wrapper result; + result.low_ = _umul128(x, y, &result.high_); + return result; +#else + const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1); + + uint64_t a = x >> 32; + uint64_t b = x & mask; + uint64_t c = y >> 32; + uint64_t d = y & mask; + + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + + uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); + + return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + (bd & mask)}; +#endif +} + +// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. +FMT_SAFEBUFFERS inline uint64_t umul128_upper64(uint64_t x, + uint64_t y) FMT_NOEXCEPT { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return static_cast(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + return umul128(x, y).high(); +#endif +} + +// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +FMT_SAFEBUFFERS inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) + FMT_NOEXCEPT { + uint128_wrapper g0 = umul128(x, y.high()); + g0 += umul128_upper64(x, y.low()); + return g0.high(); +} + +// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT { + return static_cast(umul128_upper64(x, y)); +} + +// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +FMT_SAFEBUFFERS inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) + FMT_NOEXCEPT { + uint64_t g01 = x * y.high(); + uint64_t g10 = umul128_upper64(x, y.low()); + return g01 + g10; +} + +// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { + return x * y; +} + +// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from +// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4. +inline int floor_log10_pow2(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); + const int shift = 22; + return (e * static_cast(data::log10_2_significand >> (64 - shift))) >> + shift; +} + +// Various fast log computations. +inline int floor_log2_pow10(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + const uint64_t log2_10_integer_part = 3; + const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9; + const int shift_amount = 19; + return (e * static_cast( + (log2_10_integer_part << shift_amount) | + (log2_10_fractional_digits >> (64 - shift_amount)))) >> + shift_amount; +} +inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); + const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; + const int shift_amount = 22; + return (e * static_cast(data::log10_2_significand >> + (64 - shift_amount)) - + static_cast(log10_4_over_3_fractional_digits >> + (64 - shift_amount))) >> + shift_amount; +} + +// Returns true iff x is divisible by pow(2, exp). +inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp >= 1, ""); + FMT_ASSERT(x != 0, ""); +#ifdef FMT_BUILTIN_CTZ + return FMT_BUILTIN_CTZ(x) >= exp; +#else + return exp < num_bits() && x == ((x >> exp) << exp); +#endif +} +inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp >= 1, ""); + FMT_ASSERT(x != 0, ""); +#ifdef FMT_BUILTIN_CTZLL + return FMT_BUILTIN_CTZLL(x) >= exp; +#else + return exp < num_bits() && x == ((x >> exp) << exp); +#endif +} + +// Returns true iff x is divisible by pow(5, exp). +inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp <= 10, "too large exponent"); + return x * data::divtest_table_for_pow5_32[exp].mod_inv <= + data::divtest_table_for_pow5_32[exp].max_quotient; +} +inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp <= 23, "too large exponent"); + return x * data::divtest_table_for_pow5_64[exp].mod_inv <= + data::divtest_table_for_pow5_64[exp].max_quotient; +} + +// Replaces n by floor(n / pow(5, N)) returning true if and only if n is +// divisible by pow(5, N). +// Precondition: n <= 2 * pow(5, N + 1). +template +bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT { + static constexpr struct { + uint32_t magic_number; + int bits_for_comparison; + uint32_t threshold; + int shift_amount; + } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}}; + constexpr auto info = infos[N - 1]; + n *= info.magic_number; + const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1; + bool result = (n & comparison_mask) <= info.threshold; + n >>= info.shift_amount; + return result; +} + +// Computes floor(n / pow(10, N)) for small n and N. +// Precondition: n <= pow(10, N + 1). +template uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { + static constexpr struct { + uint32_t magic_number; + int shift_amount; + uint32_t divisor_times_10; + } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}}; + constexpr auto info = infos[N - 1]; + FMT_ASSERT(n <= info.divisor_times_10, "n is too large"); + return n * info.magic_number >> info.shift_amount; +} + +// Computes floor(n / 10^(kappa + 1)) (float) +inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT { + return n / float_info::big_divisor; +} +// Computes floor(n / 10^(kappa + 1)) (double) +inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT { + return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9; +} + +// Various subroutines using pow10 cache +template struct cache_accessor; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint64_t; + + static uint64_t get_cached_power(int k) FMT_NOEXCEPT { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + return data::dragonbox_pow10_significands_64[k - float_info::min_k]; + } + + static carrier_uint compute_mul(carrier_uint u, + const cache_entry_type& cache) FMT_NOEXCEPT { + return umul96_upper32(u, cache); + } + + static uint32_t compute_delta(const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + return static_cast(cache >> (64 - 1 - beta_minus_1)); + } + + static bool compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + FMT_ASSERT(beta_minus_1 >= 1, ""); + FMT_ASSERT(beta_minus_1 < 64, ""); + + return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return static_cast( + (cache - (cache >> (float_info::significand_bits + 2))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1)); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return static_cast( + (cache + (cache >> (float_info::significand_bits + 1))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1)); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (static_cast( + cache >> + (64 - float_info::significand_bits - 2 - beta_minus_1)) + + 1) / + 2; + } +}; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint128_wrapper; + + static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + +#if FMT_USE_FULL_CACHE_DRAGONBOX + return data::dragonbox_pow10_significands_128[k - + float_info::min_k]; +#else + static const int compression_ratio = 27; + + // Compute base index. + int cache_index = (k - float_info::min_k) / compression_ratio; + int kb = cache_index * compression_ratio + float_info::min_k; + int offset = k - kb; + + // Get base cache. + uint128_wrapper base_cache = + data::dragonbox_pow10_significands_128[cache_index]; + if (offset == 0) return base_cache; + + // Compute the required amount of bit-shift. + int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset; + FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); + + // Try to recover the real cache. + uint64_t pow5 = data::powers_of_5_64[offset]; + uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); + uint128_wrapper middle_low = + umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); + + recovered_cache += middle_low.high(); + + uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); + uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); + + recovered_cache = + uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; + + if (kb < 0) recovered_cache += 1; + + // Get error. + int error_idx = (k - float_info::min_k) / 16; + uint32_t error = (data::dragonbox_pow10_recovery_errors[error_idx] >> + ((k - float_info::min_k) % 16) * 2) & + 0x3; + + // Add the error back. + FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), ""); + return {recovered_cache.high(), recovered_cache.low() + error}; +#endif + } + + static carrier_uint compute_mul(carrier_uint u, + const cache_entry_type& cache) FMT_NOEXCEPT { + return umul192_upper64(u, cache); + } + + static uint32_t compute_delta(cache_entry_type const& cache, + int beta_minus_1) FMT_NOEXCEPT { + return static_cast(cache.high() >> (64 - 1 - beta_minus_1)); + } + + static bool compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + FMT_ASSERT(beta_minus_1 >= 1, ""); + FMT_ASSERT(beta_minus_1 < 64, ""); + + return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (cache.high() - + (cache.high() >> (float_info::significand_bits + 2))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (cache.high() + + (cache.high() >> (float_info::significand_bits + 1))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return ((cache.high() >> + (64 - float_info::significand_bits - 2 - beta_minus_1)) + + 1) / + 2; + } +}; + +// Various integer checks +template +bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT { + return exponent >= + float_info< + T>::case_shorter_interval_left_endpoint_lower_threshold && + exponent <= + float_info::case_shorter_interval_left_endpoint_upper_threshold; +} +template +bool is_endpoint_integer(typename float_info::carrier_uint two_f, + int exponent, int minus_k) FMT_NOEXCEPT { + if (exponent < float_info::case_fc_pm_half_lower_threshold) return false; + // For k >= 0. + if (exponent <= float_info::case_fc_pm_half_upper_threshold) return true; + // For k < 0. + if (exponent > float_info::divisibility_check_by_5_threshold) return false; + return divisible_by_power_of_5(two_f, minus_k); +} + +template +bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, + int minus_k) FMT_NOEXCEPT { + // Exponent for 5 is negative. + if (exponent > float_info::divisibility_check_by_5_threshold) return false; + if (exponent > float_info::case_fc_upper_threshold) + return divisible_by_power_of_5(two_f, minus_k); + // Both exponents are nonnegative. + if (exponent >= float_info::case_fc_lower_threshold) return true; + // Exponent for 2 is negative. + return divisible_by_power_of_2(two_f, minus_k - exponent + 1); +} + +// Remove trailing zeros from n and return the number of zeros removed (float) +FMT_ALWAYS_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { +#ifdef FMT_BUILTIN_CTZ + int t = FMT_BUILTIN_CTZ(n); +#else + int t = ctz(n); +#endif + if (t > float_info::max_trailing_zeros) + t = float_info::max_trailing_zeros; + + const uint32_t mod_inv1 = 0xcccccccd; + const uint32_t max_quotient1 = 0x33333333; + const uint32_t mod_inv2 = 0xc28f5c29; + const uint32_t max_quotient2 = 0x0a3d70a3; + + int s = 0; + for (; s < t - 1; s += 2) { + if (n * mod_inv2 > max_quotient2) break; + n *= mod_inv2; + } + if (s < t && n * mod_inv1 <= max_quotient1) { + n *= mod_inv1; + ++s; + } + n >>= s; + return s; +} + +// Removes trailing zeros and returns the number of zeros removed (double) +FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { +#ifdef FMT_BUILTIN_CTZLL + int t = FMT_BUILTIN_CTZLL(n); +#else + int t = ctzll(n); +#endif + if (t > float_info::max_trailing_zeros) + t = float_info::max_trailing_zeros; + // Divide by 10^8 and reduce to 32-bits + // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17, + // both of the quotient and the r should fit in 32-bits + + const uint32_t mod_inv1 = 0xcccccccd; + const uint32_t max_quotient1 = 0x33333333; + const uint64_t mod_inv8 = 0xc767074b22e90e21; + const uint64_t max_quotient8 = 0x00002af31dc46118; + + // If the number is divisible by 1'0000'0000, work with the quotient + if (t >= 8) { + auto quotient_candidate = n * mod_inv8; + + if (quotient_candidate <= max_quotient8) { + auto quotient = static_cast(quotient_candidate >> 8); + + int s = 8; + for (; s < t; ++s) { + if (quotient * mod_inv1 > max_quotient1) break; + quotient *= mod_inv1; + } + quotient >>= (s - 8); + n = quotient; + return s; + } + } + + // Otherwise, work with the remainder + auto quotient = static_cast(n / 100000000); + auto remainder = static_cast(n - 100000000 * quotient); + + if (t == 0 || remainder * mod_inv1 > max_quotient1) { + return 0; + } + remainder *= mod_inv1; + + if (t == 1 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 1) + quotient * 10000000ull; + return 1; + } + remainder *= mod_inv1; + + if (t == 2 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 2) + quotient * 1000000ull; + return 2; + } + remainder *= mod_inv1; + + if (t == 3 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 3) + quotient * 100000ull; + return 3; + } + remainder *= mod_inv1; + + if (t == 4 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 4) + quotient * 10000ull; + return 4; + } + remainder *= mod_inv1; + + if (t == 5 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 5) + quotient * 1000ull; + return 5; + } + remainder *= mod_inv1; + + if (t == 6 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 6) + quotient * 100ull; + return 6; + } + remainder *= mod_inv1; + + n = (remainder >> 7) + quotient * 10ull; + return 7; +} + +// The main algorithm for shorter interval case +template +FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decimal_fp shorter_interval_case( + int exponent) FMT_NOEXCEPT { + decimal_fp ret_value; + // Compute k and beta + const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); + const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + + // Compute xi and zi + using cache_entry_type = typename cache_accessor::cache_entry_type; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + + auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( + cache, beta_minus_1); + auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( + cache, beta_minus_1); + + // If the left endpoint is not an integer, increase it + if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; + + // Try bigger divisor + ret_value.significand = zi / 10; + + // If succeed, remove trailing zeros if necessary and return + if (ret_value.significand * 10 >= xi) { + ret_value.exponent = minus_k + 1; + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + } + + // Otherwise, compute the round-up of y + ret_value.significand = + cache_accessor::compute_round_up_for_shorter_interval_case( + cache, beta_minus_1); + ret_value.exponent = minus_k; + + // When tie occurs, choose one of them according to the rule + if (exponent >= float_info::shorter_interval_tie_lower_threshold && + exponent <= float_info::shorter_interval_tie_upper_threshold) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } else if (ret_value.significand < xi) { + ++ret_value.significand; + } + return ret_value; +} + +template +FMT_SAFEBUFFERS decimal_fp to_decimal(T x) FMT_NOEXCEPT { + // Step 1: integer promotion & Schubfach multiplier calculation. + + using carrier_uint = typename float_info::carrier_uint; + using cache_entry_type = typename cache_accessor::cache_entry_type; + auto br = bit_cast(x); + + // Extract significand bits and exponent bits. + const carrier_uint significand_mask = + (static_cast(1) << float_info::significand_bits) - 1; + carrier_uint significand = (br & significand_mask); + int exponent = static_cast((br & exponent_mask()) >> + float_info::significand_bits); + + if (exponent != 0) { // Check if normal. + exponent += float_info::exponent_bias - float_info::significand_bits; + + // Shorter interval case; proceed like Schubfach. + if (significand == 0) return shorter_interval_case(exponent); + + significand |= + (static_cast(1) << float_info::significand_bits); + } else { + // Subnormal case; the interval is always regular. + if (significand == 0) return {0, 0}; + exponent = float_info::min_exponent - float_info::significand_bits; + } + + const bool include_left_endpoint = (significand % 2 == 0); + const bool include_right_endpoint = include_left_endpoint; + + // Compute k and beta. + const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + + // Compute zi and deltai + // 10^kappa <= deltai < 10^(kappa + 1) + const uint32_t deltai = cache_accessor::compute_delta(cache, beta_minus_1); + const carrier_uint two_fc = significand << 1; + const carrier_uint two_fr = two_fc | 1; + const carrier_uint zi = + cache_accessor::compute_mul(two_fr << beta_minus_1, cache); + + // Step 2: Try larger divisor; remove trailing zeros if necessary + + // Using an upper bound on zi, we might be able to optimize the division + // better than the compiler; we are computing zi / big_divisor here + decimal_fp ret_value; + ret_value.significand = divide_by_10_to_kappa_plus_1(zi); + uint32_t r = static_cast(zi - float_info::big_divisor * + ret_value.significand); + + if (r > deltai) { + goto small_divisor_case_label; + } else if (r < deltai) { + // Exclude the right endpoint if necessary + if (r == 0 && !include_right_endpoint && + is_endpoint_integer(two_fr, exponent, minus_k)) { + --ret_value.significand; + r = float_info::big_divisor; + goto small_divisor_case_label; + } + } else { + // r == deltai; compare fractional parts + // Check conditions in the order different from the paper + // to take advantage of short-circuiting + const carrier_uint two_fl = two_fc - 1; + if ((!include_left_endpoint || + !is_endpoint_integer(two_fl, exponent, minus_k)) && + !cache_accessor::compute_mul_parity(two_fl, cache, beta_minus_1)) { + goto small_divisor_case_label; + } + } + ret_value.exponent = minus_k + float_info::kappa + 1; + + // We may need to remove trailing zeros + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + + // Step 3: Find the significand with the smaller divisor + +small_divisor_case_label: + ret_value.significand *= 10; + ret_value.exponent = minus_k + float_info::kappa; + + const uint32_t mask = (1u << float_info::kappa) - 1; + auto dist = r - (deltai / 2) + (float_info::small_divisor / 2); + + // Is dist divisible by 2^kappa? + if ((dist & mask) == 0) { + const bool approx_y_parity = + ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; + dist >>= float_info::kappa; + + // Is dist divisible by 5^kappa? + if (check_divisibility_and_divide_by_pow5::kappa>(dist)) { + ret_value.significand += dist; + + // Check z^(f) >= epsilon^(f) + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number + if (cache_accessor::compute_mul_parity(two_fc, cache, beta_minus_1) != + approx_y_parity) { + --ret_value.significand; + } else { + // If z^(f) >= epsilon^(f), we might have a tie + // when z^(f) == epsilon^(f), or equivalently, when y is an integer + if (is_center_integer(two_fc, exponent, minus_k)) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } + } + } + // Is dist not divisible by 5^kappa? + else { + ret_value.significand += dist; + } + } + // Is dist not divisible by 2^kappa? + else { + // Since we know dist is small, we might be able to optimize the division + // better than the compiler; we are computing dist / small_divisor here + ret_value.significand += + small_division_by_pow10::kappa>(dist); + } + return ret_value; +} +} // namespace dragonbox + +// Formats value using a variation of the Fixed-Precision Positive +// Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/p372-steele.pdf. +template +void fallback_format(Double d, int num_digits, bool binary32, buffer& buf, + int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + fp value; + // Shift numerator and denominator by an extra bit or two (if lower boundary + // is closer) to make lower and upper integers. This eliminates multiplication + // by 2 during later computations. + const bool is_predecessor_closer = + binary32 ? value.assign(static_cast(d)) : value.assign(d); + int shift = is_predecessor_closer ? 2 : 1; + uint64_t significand = value.f << shift; + if (value.e >= 0) { + numerator.assign(significand); + numerator <<= value.e; + lower.assign(1); + lower <<= value.e; + if (shift != 1) { + upper_store.assign(1); + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= shift; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (shift != 1) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= significand; + denominator.assign(1); + denominator <<= shift - value.e; + } else { + numerator.assign(significand); + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower.assign(1); + if (shift != 1) { + upper_store.assign(1ULL << 1); + upper = &upper_store; + } + } + // Invariant: value == (numerator / denominator) * pow(10, exp10). + if (num_digits < 0) { + // Generate the shortest representation. + if (!upper) upper = &lower; + bool even = (value.f & 1) == 0; + num_digits = 0; + char* data = buf.data(); + for (;;) { + int digit = numerator.divmod_assign(denominator); + bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. + // numerator + upper >[=] pow10: + bool high = add_compare(numerator, *upper, denominator) + even > 0; + data[num_digits++] = static_cast('0' + digit); + if (low || high) { + if (!low) { + ++data[num_digits - 1]; + } else if (high) { + int result = add_compare(numerator, numerator, denominator); + // Round half to even. + if (result > 0 || (result == 0 && (digit % 2) != 0)) + ++data[num_digits - 1]; + } + buf.try_resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + // Generate the given number of digits. + exp10 -= num_digits - 1; + if (num_digits == 0) { + buf.try_resize(1); + denominator *= 10; + buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + return; + } + buf.try_resize(to_unsigned(num_digits)); + for (int i = 0; i < num_digits - 1; ++i) { + int digit = numerator.divmod_assign(denominator); + buf[i] = static_cast('0' + digit); + numerator *= 10; + } + int digit = numerator.divmod_assign(denominator); + auto result = add_compare(numerator, numerator, denominator); + if (result > 0 || (result == 0 && (digit % 2) != 0)) { + if (digit == 9) { + const auto overflow = '0' + 10; + buf[num_digits - 1] = overflow; + // Propagate the carry. + for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] == overflow) { + buf[0] = '1'; + ++exp10; + } + return; + } + ++digit; + } + buf[num_digits - 1] = static_cast('0' + digit); +} + +template +int format_float(T value, int precision, float_specs specs, buffer& buf) { + static_assert(!std::is_same::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + + const bool fixed = specs.format == float_format::fixed; + if (value <= 0) { // <= instead of == to silence a warning. + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.try_resize(to_unsigned(precision)); + std::uninitialized_fill_n(buf.data(), precision, '0'); + return -precision; + } + + if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf); + + if (precision < 0) { + // Use Dragonbox for the shortest format. + if (specs.binary32) { + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + + // Use Grisu + Dragon4 for the given precision: + // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. + int exp = 0; + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + fp normalized = normalize(fp(value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::significand_size), cached_exp10); + normalized = normalized * cached_pow; + // Limit precision to the maximum possible number of significant digits in an + // IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) { + exp += handler.size - cached_exp10 - 1; + fallback_format(value, handler.precision, specs.binary32, buf, exp); + } else { + exp += handler.exp10; + buf.try_resize(to_unsigned(handler.size)); + } + if (!fixed && !specs.showpoint) { + // Remove trailing zeros. + auto num_digits = buf.size(); + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + buf.try_resize(num_digits); + } + return exp; +} // namespace detail + +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + static_assert(!std::is_same::value, ""); + + // Subtract 1 to account for the difference in precision since we use %e for + // both general and exponent format. + if (specs.format == float_format::general || + specs.format == float_format::exp) + precision = (precision >= 0 ? precision : 6) - 1; + + // Build the format string. + enum { max_format_size = 7 }; // The longest format is "%#.*Le". + char format[max_format_size]; + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same()) *format_ptr++ = 'L'; + *format_ptr++ = specs.format != float_format::hex + ? (specs.format == float_format::fixed ? 'f' : 'e') + : (specs.upper ? 'A' : 'a'); + *format_ptr = '\0'; + + // Format using snprintf. + auto offset = buf.size(); + for (;;) { + auto begin = buf.data() + offset; + auto capacity = buf.capacity() - offset; +#ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about a nonliteral format string. + // Cannot use auto because of a bug in MinGW (#1532). + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + int result = precision >= 0 + ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); + if (result < 0) { + // The buffer will grow exponentially. + buf.try_reserve(buf.capacity() + 1); + continue; + } + auto size = to_unsigned(result); + // Size equal to capacity means that the last character was truncated. + if (size >= capacity) { + buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. + continue; + } + auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; + if (specs.format == float_format::fixed) { + if (precision == 0) { + buf.try_resize(size); + return 0; + } + // Find and remove the decimal point. + auto end = begin + size, p = end; + do { + --p; + } while (is_digit(*p)); + int fraction_size = static_cast(end - p - 1); + std::memmove(p, p + 1, to_unsigned(fraction_size)); + buf.try_resize(size - 1); + return -fraction_size; + } + if (specs.format == float_format::hex) { + buf.try_resize(size + offset); + return 0; + } + // Find and parse the exponent. + auto end = begin + size, exp_pos = end; + do { + --exp_pos; + } while (*exp_pos != 'e'); + char sign = exp_pos[1]; + assert(sign == '+' || sign == '-'); + int exp = 0; + auto p = exp_pos + 2; // Skip 'e' and sign. + do { + assert(is_digit(*p)); + exp = exp * 10 + (*p++ - '0'); + } while (p != end); + if (sign == '-') exp = -exp; + int fraction_size = 0; + if (exp_pos != begin + 1) { + // Remove trailing zeros. + auto fraction_end = exp_pos - 1; + while (*fraction_end == '0') --fraction_end; + // Move the fractional part left to get rid of the decimal point. + fraction_size = static_cast(fraction_end - begin - 1); + std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); + } + buf.try_resize(to_unsigned(fraction_size) + offset + 1); + return exp - fraction_size; + } +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from buf, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +inline const char* utf8_decode(const char* buf, uint32_t* c, int* e) { + static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + static const int shiftc[] = {0, 18, 12, 6, 0}; + static const int shifte[] = {0, 6, 4, 2, 0}; + + int len = code_point_length(buf); + const char* next = buf + len; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + auto s = reinterpret_cast(buf); + *c = uint32_t(s[0] & masks[len]) << 18; + *c |= uint32_t(s[1] & 0x3f) << 12; + *c |= uint32_t(s[2] & 0x3f) << 6; + *c |= uint32_t(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (s[1] & 0xc0) >> 2; + *e |= (s[2] & 0xc0) >> 4; + *e |= (s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +struct stringifier { + template FMT_INLINE std::string operator()(T value) const { + return to_string(value); + } + std::string operator()(basic_format_arg::handle h) const { + memory_buffer buf; + format_parse_context parse_ctx({}); + format_context format_ctx(buffer_appender(buf), {}, {}); + h.format(parse_ctx, format_ctx); + return to_string(buf); + } +}; +} // namespace detail + +template <> struct formatter { + format_parse_context::iterator parse(format_parse_context& ctx) { + return ctx.begin(); + } + + format_context::iterator format(const detail::bigint& n, + format_context& ctx) { + auto out = ctx.out(); + bool first = true; + for (auto i = n.bigits_.size(); i > 0; --i) { + auto value = n.bigits_[i - 1u]; + if (first) { + out = format_to(out, "{:x}", value); + first = false; + continue; + } + out = format_to(out, "{:08x}", value); + } + if (n.exp_ > 0) + out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits); + return out; + } +}; + +FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { + auto transcode = [this](const char* p) { + auto cp = uint32_t(); + auto error = 0; + p = utf8_decode(p, &cp, &error); + if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); + if (cp <= 0xFFFF) { + buffer_.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + buffer_.push_back(static_cast(0xD800 + (cp >> 10))); + buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); + } + return p; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p); + } + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + memcpy(buf, p, to_unsigned(num_chars_left)); + p = buf; + do { + p = transcode(p); + } while (p - buf < num_chars_left); + } + buffer_.push_back(0); +} + +FMT_FUNC void format_system_error(detail::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + FMT_TRY { + memory_buffer buf; + buf.resize(inline_buffer_size); + for (;;) { + char* system_message = &buf[0]; + int result = + detail::safe_strerror(error_code, system_message, buf.size()); + if (result == 0) { + format_to(detail::buffer_appender(out), "{}: {}", message, + system_message); + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buf.resize(buf.size() * 2); + } + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +FMT_FUNC void detail::error_handler::on_error(const char* message) { + FMT_THROW(format_error(message)); +} + +FMT_FUNC void report_system_error(int error_code, + fmt::string_view message) FMT_NOEXCEPT { + report_error(format_system_error, error_code, message); +} + +FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) { + if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { + auto arg = args.get(0); + if (!arg) error_handler().on_error("argument not found"); + return visit_format_arg(stringifier(), arg); + } + memory_buffer buffer; + detail::vformat_to(buffer, format_str, args); + return to_string(buffer); +} + +#ifdef _WIN32 +namespace detail { +using dword = conditional_t; +extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // + void*, const void*, dword, dword*, void*); +} // namespace detail +#endif + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, + basic_format_args>(args)); +#ifdef _WIN32 + auto fd = _fileno(f); + if (_isatty(fd)) { + detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); + auto written = detail::dword(); + if (!detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), + u16.c_str(), static_cast(u16.size()), + &written, nullptr)) { + FMT_THROW(format_error("failed to write to console")); + } + return; + } +#endif + detail::fwrite_fully(buffer.data(), 1, buffer.size(), f); +} + +#ifdef _WIN32 +// Print assuming legacy (non-Unicode) encoding. +FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, + format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, + basic_format_args>(args)); + fwrite_fully(buffer.data(), 1, buffer.size(), f); +} +#endif + +FMT_FUNC void vprint(string_view format_str, format_args args) { + vprint(stdout, format_str, args); +} + +FMT_END_NAMESPACE + +#endif // FMT_FORMAT_INL_H_ diff --git a/vcpkg/fmt_x86-windows-static/include/fmt/format.h b/vcpkg/fmt_x86-windows-static/include/fmt/format.h new file mode 100644 index 0000000..58c7876 --- /dev/null +++ b/vcpkg/fmt_x86-windows-static/include/fmt/format.h @@ -0,0 +1,3961 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - present, Victor Zverovich + + 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. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#pragma warning(disable:4189) +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#ifdef __INTEL_COMPILER +# define FMT_ICC_VERSION __INTEL_COMPILER +#elif defined(__ICL) +# define FMT_ICC_VERSION __ICL +#else +# define FMT_ICC_VERSION 0 +#endif + +#ifdef __NVCC__ +# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) +#else +# define FMT_CUDA_VERSION 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_NOINLINE __attribute__((noinline)) +#else +# define FMT_NOINLINE +#endif + +#if __cplusplus == 201103L || __cplusplus == 201402L +# if defined(__INTEL_COMPILER) || defined(__PGI) +# define FMT_FALLTHROUGH +# elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +# elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +# else +# define FMT_FALLTHROUGH +# endif +#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define FMT_FALLTHROUGH [[fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +#ifndef FMT_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] +# else +# define FMT_MAYBE_UNUSED +# endif +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# if FMT_MSC_VER || FMT_NVCC +FMT_BEGIN_NAMESPACE +namespace detail { +template inline void do_throw(const Exception& x) { + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (b) throw x; +} +} // namespace detail +FMT_END_NAMESPACE +# define FMT_THROW(x) detail::do_throw(x) +# else +# define FMT_THROW(x) throw x +# endif +# else +# define FMT_THROW(x) \ + do { \ + static_cast(sizeof(x)); \ + FMT_ASSERT(false, ""); \ + } while (false) +# endif +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifndef FMT_USE_USER_DEFINED_LITERALS +// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. +# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ + FMT_MSC_VER >= 1900) && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif +#endif + +#ifndef FMT_USE_UDL_TEMPLATE +// EDG frontend based compilers (icc, nvcc, PGI, etc) and GCC < 6.4 do not +// properly support UDL templates and GCC >= 9 warns about them. +# if FMT_USE_USER_DEFINED_LITERALS && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 501) && \ + ((FMT_GCC_VERSION >= 604 && __cplusplus >= 201402L) || \ + FMT_CLANG_VERSION >= 304) && \ + !defined(__PGI) && !defined(__NVCC__) +# define FMT_USE_UDL_TEMPLATE 1 +# else +# define FMT_USE_UDL_TEMPLATE 0 +# endif +#endif + +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 +#endif + +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 +#endif + +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 +#endif + +// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of +// int_writer template instances to just one by only using the largest integer +// type. This results in a reduction in binary size but will cause a decrease in +// integer formatting performance. +#if !defined(FMT_REDUCE_INT_INSTANTIATIONS) +# define FMT_REDUCE_INT_INSTANTIATIONS 0 +#endif + +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519 +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz)) +# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +#endif +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll)) +# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) +#endif + +#if FMT_MSC_VER +# include // _BitScanReverse[64], _BitScanForward[64], _umul128 +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the +// MSVC intrinsics if the clz and clzll builtins are not available. +#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && \ + !defined(FMT_BUILTIN_CTZLL) && !defined(_MANAGED) +FMT_BEGIN_NAMESPACE +namespace detail { +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# ifndef __clang__ +# pragma intrinsic(_BitScanForward) +# pragma intrinsic(_BitScanReverse) +# endif +# if defined(_WIN64) && !defined(__clang__) +# pragma intrinsic(_BitScanForward64) +# pragma intrinsic(_BitScanReverse64) +# endif + +inline int clz(uint32_t x) { + unsigned long r = 0; + _BitScanReverse(&r, x); + FMT_ASSERT(x != 0, ""); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. + FMT_SUPPRESS_MSC_WARNING(6102) + return 31 ^ static_cast(r); +} +# define FMT_BUILTIN_CLZ(n) detail::clz(n) + +inline int clzll(uint64_t x) { + unsigned long r = 0; +# ifdef _WIN64 + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ (r + 32); + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + FMT_ASSERT(x != 0, ""); + FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + return 63 ^ static_cast(r); +} +# define FMT_BUILTIN_CLZLL(n) detail::clzll(n) + +inline int ctz(uint32_t x) { + unsigned long r = 0; + _BitScanForward(&r, x); + FMT_ASSERT(x != 0, ""); + FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + return static_cast(r); +} +# define FMT_BUILTIN_CTZ(n) detail::ctz(n) + +inline int ctzll(uint64_t x) { + unsigned long r = 0; + FMT_ASSERT(x != 0, ""); + FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. +# ifdef _WIN64 + _BitScanForward64(&r, x); +# else + // Scan the low 32 bits. + if (_BitScanForward(&r, static_cast(x))) return static_cast(r); + // Scan the high 32 bits. + _BitScanForward(&r, static_cast(x >> 32)); + r += 32; +# endif + return static_cast(r); +} +# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) +} // namespace detail +FMT_END_NAMESPACE +#endif + +// Enable the deprecated numeric alignment. +#ifndef FMT_DEPRECATED_NUMERIC_ALIGN +# define FMT_DEPRECATED_NUMERIC_ALIGN 0 +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +// An equivalent of `*reinterpret_cast(&source)` that doesn't have +// undefined behavior (e.g. due to type aliasing). +// Example: uint64_t d = bit_cast(2.718); +template +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + Dest dest; + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +inline bool is_big_endian() { + const auto u = 1u; + struct bytes { + char data[sizeof(u)]; + }; + return bit_cast(u).data[0] == 0; +} + +// A fallback implementation of uintptr_t for systems that lack it. +struct fallback_uintptr { + unsigned char value[sizeof(void*)]; + + fallback_uintptr() = default; + explicit fallback_uintptr(const void* p) { + *this = bit_cast(p); + if (is_big_endian()) { + for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) + std::swap(value[i], value[j]); + } + } +}; +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } +#else +using uintptr_t = fallback_uintptr; +inline fallback_uintptr to_uintptr(const void* p) { + return fallback_uintptr(p); +} +#endif + +// Returns the largest possible value for type T. Same as +// std::numeric_limits::max() but shorter and not affected by the max macro. +template constexpr T max_value() { + return (std::numeric_limits::max)(); +} +template constexpr int num_bits() { + return std::numeric_limits::digits; +} +// std::numeric_limits::digits may return 0 for 128-bit ints. +template <> constexpr int num_bits() { return 128; } +template <> constexpr int num_bits() { return 128; } +template <> constexpr int num_bits() { + return static_cast(sizeof(void*) * + std::numeric_limits::digits); +} + +FMT_INLINE void assume(bool condition) { + (void)condition; +#if FMT_HAS_BUILTIN(__builtin_assume) + __builtin_assume(condition); +#endif +} + +// An approximation of iterator_t for pre-C++20 systems. +template +using iterator_t = decltype(std::begin(std::declval())); +template using sentinel_t = decltype(std::end(std::declval())); + +// A workaround for std::string not having mutable data() until C++17. +template inline Char* get_data(std::basic_string& s) { + return &s[0]; +} +template +inline typename Container::value_type* get_data(Container& c) { + return c.data(); +} + +#if defined(_SECURE_SCL) && _SECURE_SCL +// Make a checked iterator to avoid MSVC warnings. +template using checked_ptr = stdext::checked_array_iterator; +template checked_ptr make_checked(T* p, size_t size) { + return {p, size}; +} +#else +template using checked_ptr = T*; +template inline T* make_checked(T* p, size_t) { return p; } +#endif + +template ::value)> +#if FMT_CLANG_VERSION +__attribute__((no_sanitize("undefined"))) +#endif +inline checked_ptr +reserve(std::back_insert_iterator it, size_t n) { + Container& c = get_container(it); + size_t size = c.size(); + c.resize(size + n); + return make_checked(get_data(c) + size, n); +} + +template +inline buffer_appender reserve(buffer_appender it, size_t n) { + buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); + return it; +} + +template inline Iterator& reserve(Iterator& it, size_t) { + return it; +} + +template +constexpr T* to_pointer(OutputIt, size_t) { + return nullptr; +} +template T* to_pointer(buffer_appender it, size_t n) { + buffer& buf = get_container(it); + auto size = buf.size(); + if (buf.capacity() < size + n) return nullptr; + buf.try_resize(size + n); + return buf.data() + size; +} + +template ::value)> +inline std::back_insert_iterator base_iterator( + std::back_insert_iterator& it, + checked_ptr) { + return it; +} + +template +inline Iterator base_iterator(Iterator, Iterator it) { + return it; +} + +// An output iterator that counts the number of objects written to it and +// discards them. +class counting_iterator { + private: + size_t count_; + + public: + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + using _Unchecked_type = counting_iterator; // Mark iterator as checked. + + struct value_type { + template void operator=(const T&) {} + }; + + counting_iterator() : count_(0) {} + + size_t count() const { return count_; } + + counting_iterator& operator++() { + ++count_; + return *this; + } + counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + friend counting_iterator operator+(counting_iterator it, difference_type n) { + it.count_ += static_cast(n); + return it; + } + + value_type operator*() const { return {}; } +}; + +template class truncating_iterator_base { + protected: + OutputIt out_; + size_t limit_; + size_t count_; + + truncating_iterator_base(OutputIt out, size_t limit) + : out_(out), limit_(limit), count_(0) {} + + public: + using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = void; + using pointer = void; + using reference = void; + using _Unchecked_type = + truncating_iterator_base; // Mark iterator as checked. + + OutputIt base() const { return out_; } + size_t count() const { return count_; } +}; + +// An output iterator that truncates the output and counts the number of objects +// written to it. +template ::value_type>::type> +class truncating_iterator; + +template +class truncating_iterator + : public truncating_iterator_base { + mutable typename truncating_iterator_base::value_type blackhole_; + + public: + using value_type = typename truncating_iterator_base::value_type; + + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + truncating_iterator& operator++() { + if (this->count_++ < this->limit_) ++this->out_; + return *this; + } + + truncating_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + value_type& operator*() const { + return this->count_ < this->limit_ ? *this->out_ : blackhole_; + } +}; + +template +class truncating_iterator + : public truncating_iterator_base { + public: + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + template truncating_iterator& operator=(T val) { + if (this->count_++ < this->limit_) *this->out_++ = val; + return *this; + } + + truncating_iterator& operator++() { return *this; } + truncating_iterator& operator++(int) { return *this; } + truncating_iterator& operator*() { return *this; } +}; + +template +inline size_t count_code_points(basic_string_view s) { + return s.size(); +} + +// Counts the number of code points in a UTF-8 string. +inline size_t count_code_points(basic_string_view s) { + const char* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80) ++num_code_points; + } + return num_code_points; +} + +inline size_t count_code_points(basic_string_view s) { + return count_code_points(basic_string_view( + reinterpret_cast(s.data()), s.size())); +} + +template +inline size_t code_point_index(basic_string_view s, size_t n) { + size_t size = s.size(); + return n < size ? n : size; +} + +// Calculates the index of the nth code point in a UTF-8 string. +inline size_t code_point_index(basic_string_view s, size_t n) { + const char8_type* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) { + return i; + } + } + return s.size(); +} + +template +using needs_conversion = bool_constant< + std::is_same::value_type, + char>::value && + std::is_same::value>; + +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::copy(begin, end, it); +} + +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::transform(begin, end, it, + [](char c) { return static_cast(c); }); +} + +template +inline counting_iterator copy_str(InputIt begin, InputIt end, + counting_iterator it) { + return it + (end - begin); +} + +template +using is_fast_float = bool_constant::is_iec559 && + sizeof(T) <= sizeof(double)>; + +#ifndef FMT_USE_FULL_CACHE_DRAGONBOX +# define FMT_USE_FULL_CACHE_DRAGONBOX 0 +#endif + +template +template +void buffer::append(const U* begin, const U* end) { + do { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); + size_ += count; + begin += count; + } while (begin != end); +} + +template +void iterator_buffer::flush() { + out_ = std::copy_n(data_, this->limit(this->size()), out_); + this->clear(); +} +} // namespace detail + +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; + +/** + \rst + A dynamically growing memory buffer for trivially copyable/constructible types + with the first ``SIZE`` elements stored in the object itself. + + You can use one of the following type aliases for common character types: + + +----------------+------------------------------+ + | Type | Definition | + +================+==============================+ + | memory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + | wmemory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + + **Example**:: + + fmt::memory_buffer out; + format_to(out, "The answer is {}.", 42); + + This will append the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42. + + The output can be converted to an ``std::string`` with ``to_string(out)``. + \endrst + */ +template > +class basic_memory_buffer final : public detail::buffer { + private: + T store_[SIZE]; + + // Don't inherit from Allocator avoid generating type_info for it. + Allocator alloc_; + + // Deallocate memory allocated by the buffer. + void deallocate() { + T* data = this->data(); + if (data != store_) alloc_.deallocate(data, this->capacity()); + } + + protected: + void grow(size_t size) final FMT_OVERRIDE; + + public: + using value_type = T; + using const_reference = const T&; + + explicit basic_memory_buffer(const Allocator& alloc = Allocator()) + : alloc_(alloc) { + this->set(store_, SIZE); + } + ~basic_memory_buffer() { deallocate(); } + + private: + // Move data from other to this buffer. + void move(basic_memory_buffer& other) { + alloc_ = std::move(other.alloc_); + T* data = other.data(); + size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { + this->set(store_, capacity); + std::uninitialized_copy(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } else { + this->set(data, capacity); + // Set pointer to the inline array so that delete is not called + // when deallocating. + other.set(other.store_, 0); + } + this->resize(size); + } + + public: + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); } + + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { + FMT_ASSERT(this != &other, ""); + deallocate(); + move(other); + return *this; + } + + // Returns a copy of the allocator associated with this buffer. + Allocator get_allocator() const { return alloc_; } + + /** + Resizes the buffer to contain *count* elements. If T is a POD type new + elements may not be initialized. + */ + void resize(size_t count) { this->try_resize(count); } + + /** Increases the buffer capacity to *new_capacity*. */ + void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } + + // Directly append data into the buffer + using detail::buffer::append; + template + void append(const ContiguousRange& range) { + append(range.data(), range.data() + range.size()); + } +}; + +template +void basic_memory_buffer::grow(size_t size) { +#ifdef FMT_FUZZ + if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); +#endif + size_t old_capacity = this->capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) new_capacity = size; + T* old_data = this->data(); + T* new_data = + std::allocator_traits::allocate(alloc_, new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::uninitialized_copy(old_data, old_data + this->size(), + detail::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) alloc_.deallocate(old_data, old_capacity); +} + +using memory_buffer = basic_memory_buffer; +using wmemory_buffer = basic_memory_buffer; + +template +struct is_contiguous> : std::true_type { +}; + +/** A formatting error such as invalid format string. */ +FMT_CLASS_API +class FMT_API format_error : public std::runtime_error { + public: + explicit format_error(const char* message) : std::runtime_error(message) {} + explicit format_error(const std::string& message) + : std::runtime_error(message) {} + format_error(const format_error&) = default; + format_error& operator=(const format_error&) = default; + format_error(format_error&&) = default; + format_error& operator=(format_error&&) = default; + ~format_error() FMT_NOEXCEPT FMT_OVERRIDE; +}; + +namespace detail { + +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; + +// Returns true if value is negative, false otherwise. +// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. +template ::value)> +FMT_CONSTEXPR bool is_negative(T value) { + return value < 0; +} +template ::value)> +FMT_CONSTEXPR bool is_negative(T) { + return false; +} + +template ::value)> +FMT_CONSTEXPR bool is_supported_floating_point(T) { + return (std::is_same::value && FMT_USE_FLOAT) || + (std::is_same::value && FMT_USE_DOUBLE) || + (std::is_same::value && FMT_USE_LONG_DOUBLE); +} + +// Smallest of uint32_t, uint64_t, uint128_t that is large enough to +// represent all values of an integral type T. +template +using uint32_or_64_or_128_t = + conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, + uint32_t, + conditional_t() <= 64, uint64_t, uint128_t>>; + +// 128-bit integer type used internally +struct FMT_EXTERN_TEMPLATE_API uint128_wrapper { + uint128_wrapper() = default; + +#if FMT_USE_INT128 + uint128_t internal_; + + uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT + : internal_{static_cast(low) | + (static_cast(high) << 64)} {} + + uint128_wrapper(uint128_t u) : internal_{u} {} + + uint64_t high() const FMT_NOEXCEPT { return uint64_t(internal_ >> 64); } + uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); } + + uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { + internal_ += n; + return *this; + } +#else + uint64_t high_; + uint64_t low_; + + uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT : high_{high}, + low_{low} {} + + uint64_t high() const FMT_NOEXCEPT { return high_; } + uint64_t low() const FMT_NOEXCEPT { return low_; } + + uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { +# if defined(_MSC_VER) && defined(_M_X64) + unsigned char carry = _addcarry_u64(0, low_, n, &low_); + _addcarry_u64(carry, high_, 0, &high_); + return *this; +# else + uint64_t sum = low_ + n; + high_ += (sum < low_ ? 1 : 0); + low_ = sum; + return *this; +# endif + } +#endif +}; + +// Table entry type for divisibility test used internally +template struct FMT_EXTERN_TEMPLATE_API divtest_table_entry { + T mod_inv; + T max_quotient; +}; + +// Static data is placed in this class template for the header-only config. +template struct FMT_EXTERN_TEMPLATE_API basic_data { + static const uint64_t powers_of_10_64[]; + static const uint32_t zero_or_powers_of_10_32_new[]; + static const uint64_t zero_or_powers_of_10_64_new[]; + static const uint64_t grisu_pow10_significands[]; + static const int16_t grisu_pow10_exponents[]; + static const divtest_table_entry divtest_table_for_pow5_32[]; + static const divtest_table_entry divtest_table_for_pow5_64[]; + static const uint64_t dragonbox_pow10_significands_64[]; + static const uint128_wrapper dragonbox_pow10_significands_128[]; + // log10(2) = 0x0.4d104d427de7fbcc... + static const uint64_t log10_2_significand = 0x4d104d427de7fbcc; +#if !FMT_USE_FULL_CACHE_DRAGONBOX + static const uint64_t powers_of_5_64[]; + static const uint32_t dragonbox_pow10_recovery_errors[]; +#endif + // GCC generates slightly better code for pairs than chars. + using digit_pair = char[2]; + static const digit_pair digits[]; + static const char hex_digits[]; + static const char foreground_color[]; + static const char background_color[]; + static const char reset_color[5]; + static const wchar_t wreset_color[5]; + static const char signs[]; + static const char left_padding_shifts[5]; + static const char right_padding_shifts[5]; + + // DEPRECATED! These are for ABI compatibility. + static const uint32_t zero_or_powers_of_10_32[]; + static const uint64_t zero_or_powers_of_10_64[]; +}; + +// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). +// This is a function instead of an array to workaround a bug in GCC10 (#1810). +FMT_INLINE uint16_t bsr2log10(int bsr) { + static constexpr uint16_t data[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + return data[bsr]; +} + +#ifndef FMT_EXPORTED +FMT_EXTERN template struct basic_data; +#endif + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; + +#ifdef FMT_BUILTIN_CLZLL +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +inline int count_digits(uint64_t n) { + // https://github.com/fmtlib/format-benchmark/blob/master/digits10 + auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63); + return t - (n < data::zero_or_powers_of_10_64_new[t]); +} +#else +// Fallback version of count_digits used when __builtin_clz is not available. +inline int count_digits(uint64_t n) { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#endif + +#if FMT_USE_INT128 +inline int count_digits(uint128_t n) { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000U; + count += 4; + } +} +#endif + +// Counts the number of digits in n. BITS = log2(radix). +template inline int count_digits(UInt n) { + int num_digits = 0; + do { + ++num_digits; + } while ((n >>= BITS) != 0); + return num_digits; +} + +template <> int count_digits<4>(detail::fallback_uintptr n); + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#elif FMT_MSC_VER +# define FMT_ALWAYS_INLINE __forceinline +#else +# define FMT_ALWAYS_INLINE inline +#endif + +// To suppress unnecessary security cookie checks +#if FMT_MSC_VER && !FMT_CLANG_VERSION +# define FMT_SAFEBUFFERS __declspec(safebuffers) +#else +# define FMT_SAFEBUFFERS +#endif + +#ifdef FMT_BUILTIN_CLZ +// Optional version of count_digits for better performance on 32-bit platforms. +inline int count_digits(uint32_t n) { + auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31); + return t - (n < data::zero_or_powers_of_10_32_new[t]); +} +#endif + +template constexpr int digits10() FMT_NOEXCEPT { + return std::numeric_limits::digits10; +} +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } + +template FMT_API std::string grouping_impl(locale_ref loc); +template inline std::string grouping(locale_ref loc) { + return grouping_impl(loc); +} +template <> inline std::string grouping(locale_ref loc) { + return grouping_impl(loc); +} + +template FMT_API Char thousands_sep_impl(locale_ref loc); +template inline Char thousands_sep(locale_ref loc) { + return Char(thousands_sep_impl(loc)); +} +template <> inline wchar_t thousands_sep(locale_ref loc) { + return thousands_sep_impl(loc); +} + +template FMT_API Char decimal_point_impl(locale_ref loc); +template inline Char decimal_point(locale_ref loc) { + return Char(decimal_point_impl(loc)); +} +template <> inline wchar_t decimal_point(locale_ref loc) { + return decimal_point_impl(loc); +} + +// Compares two characters for equality. +template bool equal2(const Char* lhs, const char* rhs) { + return lhs[0] == rhs[0] && lhs[1] == rhs[1]; +} +inline bool equal2(const char* lhs, const char* rhs) { + return memcmp(lhs, rhs, 2) == 0; +} + +// Copies two characters from src to dst. +template void copy2(Char* dst, const char* src) { + *dst++ = static_cast(*src++); + *dst = static_cast(*src); +} +FMT_INLINE void copy2(char* dst, const char* src) { memcpy(dst, src, 2); } + +template struct format_decimal_result { + Iterator begin; + Iterator end; +}; + +// Formats a decimal unsigned integer value writing into out pointing to a +// buffer of specified size. The caller must ensure that the buffer is large +// enough. +template +inline format_decimal_result format_decimal(Char* out, UInt value, + int size) { + FMT_ASSERT(size >= count_digits(value), "invalid digit count"); + out += size; + Char* end = out; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + out -= 2; + copy2(out, data::digits[value % 100]); + value /= 100; + } + if (value < 10) { + *--out = static_cast('0' + value); + return {out, end}; + } + out -= 2; + copy2(out, data::digits[value]); + return {out, end}; +} + +template >::value)> +inline format_decimal_result format_decimal(Iterator out, UInt value, + int size) { + // Buffer is large enough to hold all digits (digits10 + 1). + Char buffer[digits10() + 1]; + auto end = format_decimal(buffer, value, size).end; + return {out, detail::copy_str(buffer, end, out)}; +} + +template +inline Char* format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) { + buffer += num_digits; + Char* end = buffer; + do { + const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= BASE_BITS) != 0); + return end; +} + +template +Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, + bool = false) { + auto char_digits = std::numeric_limits::digits / 4; + int start = (num_digits + char_digits - 1) / char_digits - 1; + if (int start_digits = num_digits % char_digits) { + unsigned value = n.value[start--]; + buffer = format_uint(buffer, value, start_digits); + } + for (; start >= 0; --start) { + unsigned value = n.value[start]; + buffer += char_digits; + auto p = buffer; + for (int i = 0; i < char_digits; ++i) { + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--p = static_cast(data::hex_digits[digit]); + value >>= BASE_BITS; + } + } + return buffer; +} + +template +inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + format_uint(ptr, value, num_digits, upper); + return out; + } + // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). + char buffer[num_bits() / BASE_BITS + 1]; + format_uint(buffer, value, num_digits, upper); + return detail::copy_str(buffer, buffer + num_digits, out); +} + +// A converter from UTF-8 to UTF-16. +class utf8_to_utf16 { + private: + wmemory_buffer buffer_; + + public: + FMT_API explicit utf8_to_utf16(string_view s); + operator wstring_view() const { return {&buffer_[0], size()}; } + size_t size() const { return buffer_.size() - 1; } + const wchar_t* c_str() const { return &buffer_[0]; } + std::wstring str() const { return {&buffer_[0], size()}; } +}; + +template struct null {}; + +// Workaround an array initialization issue in gcc 4.8. +template struct fill_t { + private: + enum { max_size = 4 }; + Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; + unsigned char size_ = 1; + + public: + FMT_CONSTEXPR void operator=(basic_string_view s) { + auto size = s.size(); + if (size > max_size) { + FMT_THROW(format_error("invalid fill")); + return; + } + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast(size); + } + + size_t size() const { return size_; } + const Char* data() const { return data_; } + + FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } + FMT_CONSTEXPR const Char& operator[](size_t index) const { + return data_[index]; + } +}; +} // namespace detail + +// We cannot use enum classes as bit fields because of a gcc bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +namespace align { +enum type { none, left, right, center, numeric }; +} +using align_t = align::type; + +namespace sign { +enum type { none, minus, plus, space }; +} +using sign_t = sign::type; + +// Format specifiers for built-in and string types. +template struct basic_format_specs { + int width; + int precision; + char type; + align_t align : 4; + sign_t sign : 3; + bool alt : 1; // Alternate form ('#'). + detail::fill_t fill; + + constexpr basic_format_specs() + : width(0), + precision(-1), + type(0), + align(align::none), + sign(sign::none), + alt(false) {} +}; + +using format_specs = basic_format_specs; + +namespace detail { +namespace dragonbox { + +// Type-specific information that Dragonbox uses. +template struct float_info; + +template <> struct float_info { + using carrier_uint = uint32_t; + static const int significand_bits = 23; + static const int exponent_bits = 8; + static const int min_exponent = -126; + static const int max_exponent = 127; + static const int exponent_bias = -127; + static const int decimal_digits = 9; + static const int kappa = 1; + static const int big_divisor = 100; + static const int small_divisor = 10; + static const int min_k = -31; + static const int max_k = 46; + static const int cache_bits = 64; + static const int divisibility_check_by_5_threshold = 39; + static const int case_fc_pm_half_lower_threshold = -1; + static const int case_fc_pm_half_upper_threshold = 6; + static const int case_fc_lower_threshold = -2; + static const int case_fc_upper_threshold = 6; + static const int case_shorter_interval_left_endpoint_lower_threshold = 2; + static const int case_shorter_interval_left_endpoint_upper_threshold = 3; + static const int shorter_interval_tie_lower_threshold = -35; + static const int shorter_interval_tie_upper_threshold = -35; + static const int max_trailing_zeros = 7; +}; + +template <> struct float_info { + using carrier_uint = uint64_t; + static const int significand_bits = 52; + static const int exponent_bits = 11; + static const int min_exponent = -1022; + static const int max_exponent = 1023; + static const int exponent_bias = -1023; + static const int decimal_digits = 17; + static const int kappa = 2; + static const int big_divisor = 1000; + static const int small_divisor = 100; + static const int min_k = -292; + static const int max_k = 326; + static const int cache_bits = 128; + static const int divisibility_check_by_5_threshold = 86; + static const int case_fc_pm_half_lower_threshold = -2; + static const int case_fc_pm_half_upper_threshold = 9; + static const int case_fc_lower_threshold = -4; + static const int case_fc_upper_threshold = 9; + static const int case_shorter_interval_left_endpoint_lower_threshold = 2; + static const int case_shorter_interval_left_endpoint_upper_threshold = 3; + static const int shorter_interval_tie_lower_threshold = -77; + static const int shorter_interval_tie_upper_threshold = -77; + static const int max_trailing_zeros = 16; +}; + +template struct decimal_fp { + using significand_type = typename float_info::carrier_uint; + significand_type significand; + int exponent; +}; + +template FMT_API decimal_fp to_decimal(T x) FMT_NOEXCEPT; +} // namespace dragonbox + +template +constexpr typename dragonbox::float_info::carrier_uint exponent_mask() { + using uint = typename dragonbox::float_info::carrier_uint; + return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) + << dragonbox::float_info::significand_bits; +} + +// A floating-point presentation format. +enum class float_format : unsigned char { + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + hex +}; + +struct float_specs { + int precision; + float_format format : 8; + sign_t sign : 8; + bool upper : 1; + bool locale : 1; + bool binary32 : 1; + bool use_grisu : 1; + bool showpoint : 1; +}; + +// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. +template It write_exponent(int exp, It it) { + FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); + if (exp < 0) { + *it++ = static_cast('-'); + exp = -exp; + } else { + *it++ = static_cast('+'); + } + if (exp >= 100) { + const char* top = data::digits[exp / 100]; + if (exp >= 1000) *it++ = static_cast(top[0]); + *it++ = static_cast(top[1]); + exp %= 100; + } + const char* d = data::digits[exp]; + *it++ = static_cast(d[0]); + *it++ = static_cast(d[1]); + return it; +} + +template +int format_float(T value, int precision, float_specs specs, buffer& buf); + +// Formats a floating-point number with snprintf. +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf); + +template T promote_float(T value) { return value; } +inline double promote_float(float value) { return static_cast(value); } + +template +FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { + switch (spec) { + case 0: + case 'd': + handler.on_dec(); + break; + case 'x': + case 'X': + handler.on_hex(); + break; + case 'b': + case 'B': + handler.on_bin(); + break; + case 'o': + handler.on_oct(); + break; +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': +#endif + case 'L': + handler.on_num(); + break; + case 'c': + handler.on_chr(); + break; + default: + handler.on_error(); + } +} + +template +FMT_CONSTEXPR float_specs parse_float_type_spec( + const basic_format_specs& specs, ErrorHandler&& eh = {}) { + auto result = float_specs(); + result.showpoint = specs.alt; + switch (specs.type) { + case 0: + result.format = float_format::general; + result.showpoint |= specs.precision > 0; + break; + case 'G': + result.upper = true; + FMT_FALLTHROUGH; + case 'g': + result.format = float_format::general; + break; + case 'E': + result.upper = true; + FMT_FALLTHROUGH; + case 'e': + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case 'F': + result.upper = true; + FMT_FALLTHROUGH; + case 'f': + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case 'A': + result.upper = true; + FMT_FALLTHROUGH; + case 'a': + result.format = float_format::hex; + break; +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': +#endif + case 'L': + result.locale = true; + break; + default: + eh.on_error("invalid type specifier"); + break; + } + return result; +} + +template +FMT_CONSTEXPR void handle_char_specs(const basic_format_specs* specs, + Handler&& handler) { + if (!specs) return handler.on_char(); + if (specs->type && specs->type != 'c') return handler.on_int(); + if (specs->align == align::numeric || specs->sign != sign::none || specs->alt) + handler.on_error("invalid format specifier for char"); + handler.on_char(); +} + +template +FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) { + if (spec == 0 || spec == 's') + handler.on_string(); + else if (spec == 'p') + handler.on_pointer(); + else + handler.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); +} + +template class int_type_checker : private ErrorHandler { + public: + FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_dec() {} + FMT_CONSTEXPR void on_hex() {} + FMT_CONSTEXPR void on_bin() {} + FMT_CONSTEXPR void on_oct() {} + FMT_CONSTEXPR void on_num() {} + FMT_CONSTEXPR void on_chr() {} + + FMT_CONSTEXPR void on_error() { + ErrorHandler::on_error("invalid type specifier"); + } +}; + +template +class char_specs_checker : public ErrorHandler { + private: + char type_; + + public: + FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh) + : ErrorHandler(eh), type_(type) {} + + FMT_CONSTEXPR void on_int() { + handle_int_type_spec(type_, int_type_checker(*this)); + } + FMT_CONSTEXPR void on_char() {} +}; + +template +class cstring_type_checker : public ErrorHandler { + public: + FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) + : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_string() {} + FMT_CONSTEXPR void on_pointer() {} +}; + +template +FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { + auto fill_size = fill.size(); + if (fill_size == 1) return std::fill_n(it, n, fill[0]); + for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it); + return it; +} + +// Writes the output of f, padded according to format specifications in specs. +// size: output size in code units. +// width: output display width in (terminal) column positions. +template +inline OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, size_t size, + size_t width, F&& f) { + static_assert(align == align::left || align == align::right, ""); + unsigned spec_width = to_unsigned(specs.width); + size_t padding = spec_width > width ? spec_width - width : 0; + auto* shifts = align == align::left ? data::left_padding_shifts + : data::right_padding_shifts; + size_t left_padding = padding >> shifts[specs.align]; + auto it = reserve(out, size + padding * specs.fill.size()); + it = fill(it, left_padding, specs.fill); + it = f(it); + it = fill(it, padding - left_padding, specs.fill); + return base_iterator(out, it); +} + +template +inline OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, size_t size, + F&& f) { + return write_padded(out, specs, size, size, f); +} + +template +OutputIt write_bytes(OutputIt out, string_view bytes, + const basic_format_specs& specs) { + using iterator = remove_reference_t; + return write_padded(out, specs, bytes.size(), [bytes](iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); +} + +// Data for write_int that doesn't depend on output iterator type. It is used to +// avoid template code bloat. +template struct write_int_data { + size_t size; + size_t padding; + + write_int_data(int num_digits, string_view prefix, + const basic_format_specs& specs) + : size(prefix.size() + to_unsigned(num_digits)), padding(0) { + if (specs.align == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = prefix.size() + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; + +// Writes an integer in the format +// +// where are written by f(it). +template +OutputIt write_int(OutputIt out, int num_digits, string_view prefix, + const basic_format_specs& specs, F f) { + auto data = write_int_data(num_digits, prefix, specs); + using iterator = remove_reference_t; + return write_padded(out, specs, data.size, [=](iterator it) { + if (prefix.size() != 0) + it = copy_str(prefix.begin(), prefix.end(), it); + it = std::fill_n(it, data.padding, static_cast('0')); + return f(it); + }); +} + +template +OutputIt write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) { + auto data = s.data(); + auto size = s.size(); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); + auto width = specs.width != 0 + ? count_code_points(basic_string_view(data, size)) + : 0; + using iterator = remove_reference_t; + return write_padded(out, specs, size, width, [=](iterator it) { + return copy_str(data, data + size, it); + }); +} + +// The handle_int_type_spec handler that writes an integer. +template struct int_writer { + OutputIt out; + locale_ref locale; + const basic_format_specs& specs; + UInt abs_value; + char prefix[4]; + unsigned prefix_size; + + using iterator = + remove_reference_t(), 0))>; + + string_view get_prefix() const { return string_view(prefix, prefix_size); } + + template + int_writer(OutputIt output, locale_ref loc, Int value, + const basic_format_specs& s) + : out(output), + locale(loc), + specs(s), + abs_value(static_cast(value)), + prefix_size(0) { + static_assert(std::is_same, UInt>::value, ""); + if (is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (specs.sign != sign::none && specs.sign != sign::minus) { + prefix[0] = specs.sign == sign::plus ? '+' : ' '; + ++prefix_size; + } + } + + void on_dec() { + auto num_digits = count_digits(abs_value); + out = write_int( + out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) { + return format_decimal(it, abs_value, num_digits).end; + }); + } + + void on_hex() { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = specs.type; + } + int num_digits = count_digits<4>(abs_value); + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<4, Char>(it, abs_value, num_digits, + specs.type != 'x'); + }); + } + + void on_bin() { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(specs.type); + } + int num_digits = count_digits<1>(abs_value); + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } + + void on_oct() { + int num_digits = count_digits<3>(abs_value); + if (specs.alt && specs.precision <= num_digits && abs_value != 0) { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + prefix[prefix_size++] = '0'; + } + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } + + enum { sep_size = 1 }; + + void on_num() { + std::string groups = grouping(locale); + if (groups.empty()) return on_dec(); + auto sep = thousands_sep(locale); + if (!sep) return on_dec(); + int num_digits = count_digits(abs_value); + int size = num_digits, n = num_digits; + std::string::const_iterator group = groups.cbegin(); + while (group != groups.cend() && n > *group && *group > 0 && + *group != max_value()) { + size += sep_size; + n -= *group; + ++group; + } + if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); + char digits[40]; + format_decimal(digits, abs_value, num_digits); + basic_memory_buffer buffer; + size += static_cast(prefix_size); + const auto usize = to_unsigned(size); + buffer.resize(usize); + basic_string_view s(&sep, sep_size); + // Index of a decimal digit with the least significant digit having index 0. + int digit_index = 0; + group = groups.cbegin(); + auto p = buffer.data() + size - 1; + for (int i = num_digits - 1; i > 0; --i) { + *p-- = static_cast(digits[i]); + if (*group <= 0 || ++digit_index % *group != 0 || + *group == max_value()) + continue; + if (group + 1 != groups.cend()) { + digit_index = 0; + ++group; + } + std::uninitialized_copy(s.data(), s.data() + s.size(), + make_checked(p, s.size())); + p -= s.size(); + } + *p-- = static_cast(*digits); + if (prefix_size != 0) *p = static_cast('-'); + auto data = buffer.data(); + out = write_padded( + out, specs, usize, usize, + [=](iterator it) { return copy_str(data, data + size, it); }); + } + + void on_chr() { *out++ = static_cast(abs_value); } + + FMT_NORETURN void on_error() { + FMT_THROW(format_error("invalid type specifier")); + } +}; + +template +OutputIt write_nonfinite(OutputIt out, bool isinf, + const basic_format_specs& specs, + const float_specs& fspecs) { + auto str = + isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); + constexpr size_t str_size = 3; + auto sign = fspecs.sign; + auto size = str_size + (sign ? 1 : 0); + using iterator = remove_reference_t; + return write_padded(out, specs, size, [=](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + return copy_str(str, str + str_size, it); + }); +} + +// A decimal floating-point number significand * pow(10, exp). +struct big_decimal_fp { + const char* significand; + int significand_size; + int exponent; +}; + +inline int get_significand_size(const big_decimal_fp& fp) { + return fp.significand_size; +} +template +inline int get_significand_size(const dragonbox::decimal_fp& fp) { + return count_digits(fp.significand); +} + +template +inline OutputIt write_significand(OutputIt out, const char* significand, + int& significand_size) { + return copy_str(significand, significand + significand_size, out); +} +template +inline OutputIt write_significand(OutputIt out, UInt significand, + int significand_size) { + return format_decimal(out, significand, significand_size).end; +} + +template ::value)> +inline Char* write_significand(Char* out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) { + if (!decimal_point) + return format_decimal(out, significand, significand_size).end; + auto end = format_decimal(out + 1, significand, significand_size).end; + if (integral_size == 1) + out[0] = out[1]; + else + std::copy_n(out + 1, integral_size, out); + out[integral_size] = decimal_point; + return end; +} + +template >::value)> +inline OutputIt write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) { + // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. + Char buffer[digits10() + 2]; + auto end = write_significand(buffer, significand, significand_size, + integral_size, decimal_point); + return detail::copy_str(buffer, end, out); +} + +template +inline OutputIt write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) { + out = detail::copy_str(significand, significand + integral_size, out); + if (!decimal_point) return out; + *out++ = decimal_point; + return detail::copy_str(significand + integral_size, + significand + significand_size, out); +} + +template +OutputIt write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs& specs, float_specs fspecs, + Char decimal_point) { + auto significand = fp.significand; + int significand_size = get_significand_size(fp); + static const Char zero = static_cast('0'); + auto sign = fspecs.sign; + size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); + using iterator = remove_reference_t; + + int output_exp = fp.exponent + significand_size - 1; + auto use_exp_format = [=]() { + if (fspecs.format == float_format::exp) return true; + if (fspecs.format != float_format::general) return false; + // Use the fixed notation if the exponent is in [exp_lower, exp_upper), + // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. + const int exp_lower = -4, exp_upper = 16; + return output_exp < exp_lower || + output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); + }; + if (use_exp_format()) { + int num_zeros = 0; + if (fspecs.showpoint) { + num_zeros = (std::max)(fspecs.precision - significand_size, 0); + size += to_unsigned(num_zeros); + } else if (significand_size == 1) { + decimal_point = Char(); + } + auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; + int exp_digits = 2; + if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; + + size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); + char exp_char = fspecs.upper ? 'E' : 'e'; + auto write = [=](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + decimal_point); + if (num_zeros > 0) it = std::fill_n(it, num_zeros, zero); + *it++ = static_cast(exp_char); + return write_exponent(output_exp, it); + }; + return specs.width > 0 ? write_padded(out, specs, size, write) + : base_iterator(out, write(reserve(out, size))); + } + + int exp = fp.exponent + significand_size; + if (fp.exponent >= 0) { + // 1234e5 -> 123400000[.0+] + size += to_unsigned(fp.exponent); + int num_zeros = fspecs.precision - exp; +#ifdef FMT_FUZZ + if (num_zeros > 5000) + throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); +#endif + if (fspecs.showpoint) { + if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; + if (num_zeros > 0) size += to_unsigned(num_zeros); + } + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + it = write_significand(it, significand, significand_size); + it = std::fill_n(it, fp.exponent, zero); + if (!fspecs.showpoint) return it; + *it++ = decimal_point; + return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + }); + } else if (exp > 0) { + // 1234e-2 -> 12.34[0+] + int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; + size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + it = write_significand(it, significand, significand_size, exp, + decimal_point); + return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + }); + } + // 1234e-6 -> 0.001234 + int num_zeros = -exp; + if (significand_size == 0 && fspecs.precision >= 0 && + fspecs.precision < num_zeros) { + num_zeros = fspecs.precision; + } + size += 2 + to_unsigned(num_zeros); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + *it++ = zero; + if (num_zeros == 0 && significand_size == 0 && !fspecs.showpoint) return it; + *it++ = decimal_point; + it = std::fill_n(it, num_zeros, zero); + return write_significand(it, significand, significand_size); + }); +} + +template ::value)> +OutputIt write(OutputIt out, T value, basic_format_specs specs, + locale_ref loc = {}) { + if (const_check(!is_supported_floating_point(value))) return out; + float_specs fspecs = parse_float_type_spec(specs); + fspecs.sign = specs.sign; + if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. + fspecs.sign = sign::minus; + value = -value; + } else if (fspecs.sign == sign::minus) { + fspecs.sign = sign::none; + } + + if (!std::isfinite(value)) + return write_nonfinite(out, std::isinf(value), specs, fspecs); + + if (specs.align == align::numeric && fspecs.sign) { + auto it = reserve(out, 1); + *it++ = static_cast(data::signs[fspecs.sign]); + out = base_iterator(out, it); + fspecs.sign = sign::none; + if (specs.width != 0) --specs.width; + } + + memory_buffer buffer; + if (fspecs.format == float_format::hex) { + if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); + snprintf_float(promote_float(value), specs.precision, fspecs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, specs); + } + int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; + if (fspecs.format == float_format::exp) { + if (precision == max_value()) + FMT_THROW(format_error("number is too big")); + else + ++precision; + } + if (const_check(std::is_same())) fspecs.binary32 = true; + fspecs.use_grisu = is_fast_float(); + int exp = format_float(promote_float(value), precision, fspecs, buffer); + fspecs.precision = precision; + Char point = + fspecs.locale ? decimal_point(loc) : static_cast('.'); + auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, fp, specs, fspecs, point); +} + +template ::value)> +OutputIt write(OutputIt out, T value) { + if (const_check(!is_supported_floating_point(value))) return out; + + using floaty = conditional_t::value, double, T>; + using uint = typename dragonbox::float_info::carrier_uint; + auto bits = bit_cast(value); + + auto fspecs = float_specs(); + auto sign_bit = bits & (uint(1) << (num_bits() - 1)); + if (sign_bit != 0) { + fspecs.sign = sign::minus; + value = -value; + } + + static const auto specs = basic_format_specs(); + uint mask = exponent_mask(); + if ((bits & mask) == mask) + return write_nonfinite(out, std::isinf(value), specs, fspecs); + + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, fspecs, static_cast('.')); +} + +template ::value && + !is_fast_float::value)> +inline OutputIt write(OutputIt out, T value) { + return write(out, value, basic_format_specs()); +} + +template +OutputIt write_char(OutputIt out, Char value, + const basic_format_specs& specs) { + using iterator = remove_reference_t; + return write_padded(out, specs, 1, [=](iterator it) { + *it++ = value; + return it; + }); +} + +template +OutputIt write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs* specs) { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + using iterator = remove_reference_t; + auto write = [=](iterator it) { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + return format_uint<4, Char>(it, value, num_digits); + }; + return specs ? write_padded(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); +} + +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; + +template +OutputIt write(OutputIt out, monostate) { + FMT_ASSERT(false, ""); + return out; +} + +template ::value)> +OutputIt write(OutputIt out, string_view value) { + auto it = reserve(out, value.size()); + it = copy_str(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, basic_string_view value) { + auto it = reserve(out, value.size()); + it = std::copy(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template +buffer_appender write(buffer_appender out, + basic_string_view value) { + get_container(out).append(value.begin(), value.end()); + return out; +} + +template ::value && + !std::is_same::value && + !std::is_same::value)> +OutputIt write(OutputIt out, T value) { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto size = (negative ? 1 : 0) + static_cast(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer(it, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, bool value) { + return write(out, string_view(value ? "true" : "false")); +} + +template +OutputIt write(OutputIt out, Char value) { + auto it = reserve(out, 1); + *it++ = value; + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, const Char* value) { + if (!value) { + FMT_THROW(format_error("string pointer is null")); + } else { + auto length = std::char_traits::length(value); + out = write(out, basic_string_view(value, length)); + } + return out; +} + +template +OutputIt write(OutputIt out, const void* value) { + return write_ptr(out, to_uintptr(value), nullptr); +} + +template +auto write(OutputIt out, const T& value) -> typename std::enable_if< + mapped_type_constant>::value == + type::custom_type, + OutputIt>::type { + using context_type = basic_format_context; + using formatter_type = + conditional_t::value, + typename context_type::template formatter_type, + fallback_formatter>; + context_type ctx(out, {}, {}); + return formatter_type().format(value, ctx); +} + +// An argument visitor that formats the argument and writes it via the output +// iterator. It's a class and not a generic lambda for compatibility with C++11. +template struct default_arg_formatter { + using context = basic_format_context; + + OutputIt out; + basic_format_args args; + locale_ref loc; + + template OutputIt operator()(T value) { + return write(out, value); + } + + OutputIt operator()(typename basic_format_arg::handle handle) { + basic_format_parse_context parse_ctx({}); + basic_format_context format_ctx(out, args, loc); + handle.format(parse_ctx, format_ctx); + return format_ctx.out(); + } +}; + +template +class arg_formatter_base { + public: + using iterator = OutputIt; + using char_type = Char; + using format_specs = basic_format_specs; + + private: + iterator out_; + locale_ref locale_; + format_specs* specs_; + + // Attempts to reserve space for n extra characters in the output range. + // Returns a pointer to the reserved range or a reference to out_. + auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) { + return detail::reserve(out_, n); + } + + using reserve_iterator = remove_reference_t(), 0))>; + + template void write_int(T value, const format_specs& spec) { + using uint_type = uint32_or_64_or_128_t; + int_writer w(out_, locale_, value, spec); + handle_int_type_spec(spec.type, w); + out_ = w.out; + } + + void write(char value) { + auto&& it = reserve(1); + *it++ = value; + } + + template ::value)> + void write(Ch value) { + out_ = detail::write(out_, value); + } + + void write(string_view value) { + auto&& it = reserve(value.size()); + it = copy_str(value.begin(), value.end(), it); + } + void write(wstring_view value) { + static_assert(std::is_same::value, ""); + auto&& it = reserve(value.size()); + it = std::copy(value.begin(), value.end(), it); + } + + template + void write(const Ch* s, size_t size, const format_specs& specs) { + auto width = specs.width != 0 + ? count_code_points(basic_string_view(s, size)) + : 0; + out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) { + return copy_str(s, s + size, it); + }); + } + + template + void write(basic_string_view s, const format_specs& specs = {}) { + out_ = detail::write(out_, s, specs); + } + + void write_pointer(const void* p) { + out_ = write_ptr(out_, to_uintptr(p), specs_); + } + + struct char_spec_handler : ErrorHandler { + arg_formatter_base& formatter; + Char value; + + char_spec_handler(arg_formatter_base& f, Char val) + : formatter(f), value(val) {} + + void on_int() { + // char is only formatted as int if there are specs. + formatter.write_int(static_cast(value), *formatter.specs_); + } + void on_char() { + if (formatter.specs_) + formatter.out_ = write_char(formatter.out_, value, *formatter.specs_); + else + formatter.write(value); + } + }; + + struct cstring_spec_handler : error_handler { + arg_formatter_base& formatter; + const Char* value; + + cstring_spec_handler(arg_formatter_base& f, const Char* val) + : formatter(f), value(val) {} + + void on_string() { formatter.write(value); } + void on_pointer() { formatter.write_pointer(value); } + }; + + protected: + iterator out() { return out_; } + format_specs* specs() { return specs_; } + + void write(bool value) { + if (specs_) + write(string_view(value ? "true" : "false"), *specs_); + else + out_ = detail::write(out_, value); + } + + void write(const Char* value) { + if (!value) { + FMT_THROW(format_error("string pointer is null")); + } else { + auto length = std::char_traits::length(value); + basic_string_view sv(value, length); + specs_ ? write(sv, *specs_) : write(sv); + } + } + + public: + arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) + : out_(out), locale_(loc), specs_(s) {} + + iterator operator()(monostate) { + FMT_ASSERT(false, "invalid argument type"); + return out_; + } + + template ::value)> + FMT_INLINE iterator operator()(T value) { + if (specs_) + write_int(value, *specs_); + else + out_ = detail::write(out_, value); + return out_; + } + + iterator operator()(Char value) { + handle_char_specs(specs_, + char_spec_handler(*this, static_cast(value))); + return out_; + } + + iterator operator()(bool value) { + if (specs_ && specs_->type) return (*this)(value ? 1 : 0); + write(value != 0); + return out_; + } + + template ::value)> + iterator operator()(T value) { + auto specs = specs_ ? *specs_ : format_specs(); + if (const_check(is_supported_floating_point(value))) + out_ = detail::write(out_, value, specs, locale_); + else + FMT_ASSERT(false, "unsupported float argument type"); + return out_; + } + + iterator operator()(const Char* value) { + if (!specs_) return write(value), out_; + handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value)); + return out_; + } + + iterator operator()(basic_string_view value) { + if (specs_) { + check_string_type_spec(specs_->type, error_handler()); + write(value, *specs_); + } else { + write(value); + } + return out_; + } + + iterator operator()(const void* value) { + if (specs_) check_pointer_type_spec(specs_->type, error_handler()); + write_pointer(value); + return out_; + } +}; + +/** The default argument formatter. */ +template +class arg_formatter : public arg_formatter_base { + private: + using char_type = Char; + using base = arg_formatter_base; + using context_type = basic_format_context; + + context_type& ctx_; + basic_format_parse_context* parse_ctx_; + const Char* ptr_; + + public: + using iterator = typename base::iterator; + using format_specs = typename base::format_specs; + + /** + \rst + Constructs an argument formatter object. + *ctx* is a reference to the formatting context, + *specs* contains format specifier information for standard argument types. + \endrst + */ + explicit arg_formatter( + context_type& ctx, + basic_format_parse_context* parse_ctx = nullptr, + format_specs* specs = nullptr, const Char* ptr = nullptr) + : base(ctx.out(), specs, ctx.locale()), + ctx_(ctx), + parse_ctx_(parse_ctx), + ptr_(ptr) {} + + using base::operator(); + + /** Formats an argument of a user-defined type. */ + iterator operator()(typename basic_format_arg::handle handle) { + if (ptr_) advance_to(*parse_ctx_, ptr_); + handle.format(*parse_ctx_, ctx_); + return ctx_.out(); + } +}; + +template FMT_CONSTEXPR bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +template +FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, + ErrorHandler&& eh) { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0; + // Convert to unsigned to prevent a warning. + constexpr unsigned max_int = max_value(); + unsigned big = max_int / 10; + do { + // Check for overflow. + if (value > big) { + value = max_int + 1; + break; + } + value = value * 10 + unsigned(*begin - '0'); + ++begin; + } while (begin != end && '0' <= *begin && *begin <= '9'); + if (value > max_int) eh.on_error("number is too big"); + return static_cast(value); +} + +template class custom_formatter { + private: + using char_type = typename Context::char_type; + + basic_format_parse_context& parse_ctx_; + Context& ctx_; + + public: + explicit custom_formatter(basic_format_parse_context& parse_ctx, + Context& ctx) + : parse_ctx_(parse_ctx), ctx_(ctx) {} + + void operator()(typename basic_format_arg::handle h) const { + h.format(parse_ctx_, ctx_); + } + + template void operator()(T) const {} +}; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +template class width_checker { + public: + explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T value) { + if (is_negative(value)) handler_.on_error("negative width"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T) { + handler_.on_error("width is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +template class precision_checker { + public: + explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T value) { + if (is_negative(value)) handler_.on_error("negative precision"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T) { + handler_.on_error("precision is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +// A format specifier handler that sets fields in basic_format_specs. +template class specs_setter { + public: + explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) + : specs_(specs) {} + + FMT_CONSTEXPR specs_setter(const specs_setter& other) + : specs_(other.specs_) {} + + FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + specs_.fill = fill; + } + FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } + FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } + FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } + FMT_CONSTEXPR void on_hash() { specs_.alt = true; } + + FMT_CONSTEXPR void on_zero() { + specs_.align = align::numeric; + specs_.fill[0] = Char('0'); + } + + FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } + FMT_CONSTEXPR void on_precision(int precision) { + specs_.precision = precision; + } + FMT_CONSTEXPR void end_precision() {} + + FMT_CONSTEXPR void on_type(Char type) { + specs_.type = static_cast(type); + } + + protected: + basic_format_specs& specs_; +}; + +template class numeric_specs_checker { + public: + FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type) + : error_handler_(eh), arg_type_(arg_type) {} + + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic_type(arg_type_)) + error_handler_.on_error("format specifier requires numeric argument"); + } + + FMT_CONSTEXPR void check_sign() { + require_numeric_argument(); + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::char_type) { + error_handler_.on_error("format specifier requires signed argument"); + } + } + + FMT_CONSTEXPR void check_precision() { + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) + error_handler_.on_error("precision not allowed for this argument type"); + } + + private: + ErrorHandler& error_handler_; + detail::type arg_type_; +}; + +// A format specifier handler that checks if specifiers are consistent with the +// argument type. +template class specs_checker : public Handler { + private: + numeric_specs_checker checker_; + + // Suppress an MSVC warning about using this in initializer list. + FMT_CONSTEXPR Handler& error_handler() { return *this; } + + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) + : Handler(handler), checker_(error_handler(), arg_type) {} + + FMT_CONSTEXPR specs_checker(const specs_checker& other) + : Handler(other), checker_(error_handler(), other.arg_type_) {} + + FMT_CONSTEXPR void on_align(align_t align) { + if (align == align::numeric) checker_.require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_plus() { + checker_.check_sign(); + Handler::on_plus(); + } + + FMT_CONSTEXPR void on_minus() { + checker_.check_sign(); + Handler::on_minus(); + } + + FMT_CONSTEXPR void on_space() { + checker_.check_sign(); + Handler::on_space(); + } + + FMT_CONSTEXPR void on_hash() { + checker_.require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_zero() { + checker_.require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } +}; + +template