Compare commits

...
Author SHA1 Message Date
Your Name 0cf8f2b11f update amtl 2023-08-30 22:18:47 +02:00
BotoX 91cc6b300c Add "Restart Current Map" to sm_map menu. 2023-08-30 22:08:46 +02:00
BotoX ec3351a320 Fix GetClientCount(false) 2023-08-30 22:08:46 +02:00
Your Name aa2a71fc67 Add damageCustom argument to SDKHooks_TakeDamage native.
# Conflicts:
#	extensions/sdkhooks/natives.cpp
#	plugins/include/sdkhooks.inc
2023-08-30 22:08:46 +02:00
BotoX 142e76bd92 fix IsMapValid behavior change by a2246af121 2023-08-30 22:08:46 +02:00
BotoX 632429700b Changes on sm_*say 2023-08-30 22:08:46 +02:00
BotoX a8d95ca044 Add GetClientIClient native. 2023-08-30 22:08:46 +02:00
Your Name 28b4b248f7 Extend function calling API for natives and allow catching exceptions.
Change sourcepawn url.

# Conflicts:
#	.gitmodules
#	core/logic/AMBuilder
2023-08-30 22:08:46 +02:00
BotoX 964c9ae835 Fix @spec not targeting clients in unassigned team. 2023-08-30 22:08:46 +02:00
Your Name d099dbb659 Added hack to make plugins open a menu with all possible targets on ReplyToTargetError COMMAND_TARGET_AMBIGUOUS.
Explanation:
There are two clients in the server, one named gene, the other one "Ene ~special characters~".
An admin issues "sm_slay Ene" and gets following error message: More than one client matched the given pattern.
What this hack will do is: Use GetCmdArg(0, ...); to get the command name "sm_slay".
Use GetCmdArgString(...); to get the arguments supplied to the command.
Use GetLastProcessTargetString(...); (which was implemented in this commit) to retrieve the arguments that were passed to the last ProcessTargetString call.
It will then pass this data to the DynamicTargeting plugin through its AmbiguousMenu native.
The plugin will open up a menu on the client and list all targets which match the pattern that was supplied to ProcessTargetString.
If the client selects a menu entry, FakeClientCommand will be used to re-execute the command with the correct target.

# Conflicts:
#	tools/buildbot/PackageScript
2023-08-30 22:08:46 +02:00
BotoX 688e274c30 Add more macros to CDetour. 2023-08-30 22:08:46 +02:00
Mikusch a5ff53f00f TF2: Add TFCond_ImmuneToPushback condition (#2023)
(cherry picked from commit 531b053096)
2023-08-30 22:08:46 +02:00
Mikusch 38fc001a1d TF2: Update StunPlayer signature (#2024) (fixes #2022)
(cherry picked from commit 0dafe04899)
2023-08-30 22:08:46 +02:00
data-bomb 4b949e8228 Add LookupAttachment signature for ND (#1942)
Adds the LookupAttachment signature for Nuclear Dawn

(cherry picked from commit a9a1939f75)
2023-08-30 22:08:46 +02:00
Dysphie 080c4bb8e2 Fix LookupAttachment signature for NMRiH (#1940)
(cherry picked from commit 693e584dcd)
2023-08-30 22:08:46 +02:00
Nick Hastings 149b16cbf3 Enable ShowMenu support for Reactive Drop (see #1938)
Note: Currently only works on beta version of game
(cherry picked from commit 397b70add0)
2023-08-30 22:08:46 +02:00
Corey D b940b01be4 Fix LookupAttachment signature (#1933)
(cherry picked from commit a0eb6a9550)
2023-08-30 22:08:46 +02:00
Poggu 45258b8561 Fix EntityFactoryCaller signature (#1925)
(cherry picked from commit 6e839a95c6)
2023-08-30 22:08:46 +02:00
El Diablo ab7b9b231d Linux [SDKTOOLS] Sigscan for FireOutput FIX (#1923)
Addie said:
test the signatures:
\x55\x89\xE5\x57\x56\x53\x81\xEC\x8C\x01\x00\x00\x8B\x55\x08
or
digby's \x55\x89\xE5\x57\x56\x53\x81\xEC\x8C\x01\x00\x00\x8B\x55\x2A\x65\xA1

\x55\x89\xE5\x57\x56\x53\x81\xEC\x8C\x01\x00\x00\x8B\x55\x08 fixed the issue for linux server.

(cherry picked from commit 2dcee81f22)
2023-08-30 22:08:46 +02:00
GAMMACASE 76c2addeb9 Gamedata update after 2/2/2023 CSGO update (#1921) 2023-08-30 22:08:46 +02:00
nosoop 7cb7703cf9 Update TF2 gamedata for version 7757534 (2023-01-05) (#1901)
(cherry picked from commit 27b1817b10)
2023-08-30 22:08:46 +02:00
clague c2b204e810 Fix ReadMapList ignoring file's last modified time (#1891)
(cherry picked from commit 0d6179299c)
2023-08-30 22:08:46 +02:00
zer0.k 24c92c021c Add missing null pointer check to protobuf messages (#1883)
(cherry picked from commit ef7d3abefd)
2023-08-30 22:08:46 +02:00
nosoop dd370c3904 entitylump: Output separator as spaces instead of tabs
On NMRiH and possibly other games that use the Maphacks system,
entries that are modified using that system are rendered with tab
characters stripped out - see CNMRiHMapHackManager::GetEntDataString.

That results in there being no separators at all between keys and
values, as Maphacks receives the serialized string from Entity Lump
Manager.

This commit changes the key / value separator character to use
spaces instead.

This discovery upsets me greatly.

Fixes #1833.
2023-08-30 22:08:46 +02:00
Erin 384fb668d1 Trigger build for TF2 SDK update 2023-08-30 22:08:46 +02:00
Nick Hastings 1903c0bd2b Trigger build against SDK update 2023-08-30 22:08:46 +02:00
nosoop 13034eff05 Ensure gameconfig file uniqueness when reading master.games (#1859)
The extended gameconfig format reads the master gameconf file twice;
once each for the base engine and actual engine.  The file list
isn't checked for duplicates, so 'common.games.txt' is loaded in
twice, resulting in any 'common' file values potentially overriding
values listed under '#default' in other files due to
bShouldBeReadingDefault.  This happens in the case when matching
game versions by CRC (such as public game branches).

Required for #1857.

(cherry picked from commit 34c8220e5d)
2023-08-30 22:08:46 +02:00
Nick Hastings a4703722be Update TF2 gamedata.
(cherry picked from commit 89bd4d7329)
2023-08-30 22:08:46 +02:00
nosoop 2e13460823 entitylump: Fix behavior of append (#1836)
This change ensures that the iterator values used by `std::distance`
is correct.  Having the emplace within leads to the possibility of
`m_Entities.begin()` being invalidated due to reallocations.
2023-08-30 22:08:46 +02:00
nosoop d356d80537 Add functions for working with entity lumps (#1673) 2023-08-30 22:08:45 +02:00
Erin Baker 412759cbc4 Enable CI on release branches (#1854) 2023-08-30 22:08:45 +02:00
Maxim Telezhenko 08841227a4 Fix DHooks jit code stack memory alignment (#1849) 2023-08-30 22:08:45 +02:00
Nick Hastings 18b2ffa786 Trigger build for hl2sdk-csgo update 2023-08-30 22:08:45 +02:00
Vauff 3e9a48c778 Correct missed team offsets in CheckRestartRound (#1844) 2023-08-30 22:08:45 +02:00
Nick Hastings 339fd85c6f Trigger build for hl2sdk-csgo update 2023-08-30 22:08:45 +02:00
Vauff 59144cf868 Update gamedata for 2022/10/21 CS:GO update (#1842) 2023-08-30 22:08:45 +02:00
David Anderson ad3a44427a Update SourcePawn. 2023-08-30 22:08:45 +02:00
David Anderson 737d8c3240 Fix build. 2023-08-30 22:08:45 +02:00
David Anderson 4688783baf Revert "Introduce a pbproxy library to solve macOS linker issues."
This reverts commit e5ddbd9886.
2023-08-30 22:08:45 +02:00
David Anderson ba5c6ad07c Revert "Fix use-after-free when creating custom user messages"
This reverts commit 15450a6d0c.
2023-08-30 22:08:45 +02:00
David Anderson 69045ccd52 Update SourcePawn on 1.11-dev. 2023-08-30 22:08:45 +02:00
42 cf39d4b820 Fix support for SDKCall returning non-networked entity (#1797)
(cherry picked from commit 625c7a98f2)
2023-08-30 22:08:45 +02:00
peace-maker cb218a4911 Log a notice if the geoip database gets too old (#1791)
Since we ship an ancient version of the database, help server operators keep track of the database version.
2023-08-30 22:08:45 +02:00
peace-maker 344284196c Update SQLite library to 3.38.5 (#1792)
Bring in the changes of the last four years of SQLite development.

Fixes #1728
Fixes #1592
2023-08-30 22:08:45 +02:00
Nick Hastings e7c0726acd Trigger build against hl2sdk-csgo changes. 2023-08-30 22:08:45 +02:00
Nick Hastings d0b34e783d Trigger build against hl2sdk-csgo changes.
(cherry picked from commit 5714e7695a653dcade1832cb482855e77b5f13dd)
2023-08-30 22:08:45 +02:00
Deathreus d481e5fcb7 Add a PluginIterator methodmap (#1779)
* Add a PluginIterator methodmap

* Follow convention

* Update sourcemod.inc

* Turn method ReadPlugin into property Plugin

* Requested change

* Update sourcemod.inc

* Curse you VSC

* Follow behavior of other iterators instead of the natives

* Fix a stray space

* Implement a hacked CPluginIterator

* Oops

Copy paste go brr

* Revert a change made before the custom impl
2023-08-30 22:08:45 +02:00
dysphie 1459c10112 Add sdktools gamerules support for NMRiH (#1784) 2023-08-30 22:08:45 +02:00
David Anderson 8b9131f6e4 Update SourcePawn. 2023-08-30 22:08:45 +02:00
Margen67 ce1a21ba0b .gitmodules: Make submodules shallow (#1769) 2023-08-30 22:08:45 +02:00
Vauff e2237ce875 Expand ShowHudText message buffer for Protobuf games (#1777)
* Expand ShowHudText message buffer for CS:GO

* Expand buffer on Blade Symphony too
2023-08-30 22:08:45 +02:00
peace-maker 250bbf5955 DHooks: Error on argument passflags for detours (#1773)
The passflags are only supported by SourceHook for virtual hooks and are ignored for detours with DynamicDetours. This caused confusion, so throw an error when trying to set e.g. the DHookPass_ByRef flag on detour arguments.
2023-08-30 22:08:45 +02:00
peace-maker 0478912d73 DHooks: Fix changing of byref vector parameters (#1772)
We always created a new vector object instead of changing the passed in vector directly. This works for the function being called using our changed values - but the caller doesn't see the changed values if it's passing a vector by reference.

Only create a new vector if there isn't one being passed in and set the values directly in the passed in vector otherwise.
2023-08-30 22:08:45 +02:00
Nick Hastings d50e4b7a2f Update TF2 gamedata. 2023-08-30 22:08:45 +02:00
Nick Hastings abf59a4f4b Update Reactive Drop gamedata (closes #1771) 2023-08-30 22:08:45 +02:00
Headline 9b80e46097 Lift raw pointers out of DatabaseConfBuilder (#1637)
* Lift raw pointers out of DatabaseConfBuilder

* Maybe: not
2023-08-30 22:08:45 +02:00
David Anderson bbbaebd32a Update SourcePawn. 2023-08-30 22:08:45 +02:00
Vauff 1204d1fd0f Update WriteBaselines signature for CS:GO update (#1766) 2023-08-30 22:08:45 +02:00
Bone ef34cc67b6 fix WeaponPrice offset on windows (#1765) 2023-08-30 22:08:45 +02:00
Psykotikism 25184aa9c7 Add "DispatchKeyValueInt" stock (#1764) 2023-08-30 22:08:45 +02:00
Arron Vinyard b789d30658 Remove unnecessary timer typeset entry (#1735) 2023-08-30 22:08:45 +02:00
Arron Vinyard 305799b441 NPOTB: Use camel casing for variables in adminhelp.sp (#1750)
This is a stylistic change to ensure more adhered-to consistency throughout base plugins
2023-08-30 22:08:45 +02:00
Sikari 92fc27f67f Prevent workshop prefix from showing in nominations results menu (#1737)
Prevents workshop prefixes from being shown instead of the display names in results menus when items are disabled
2023-08-30 22:08:45 +02:00
domino_ 5292721f0b Add a OnPlayerRunCmdPre forward (#1760)
Adds an OnPlayerRunCmdPre forward in order for plugins to be able to hook OnPlayerRunCmd with the guarantee that none of the parameters have been modified by other plugins. As such, OnPlayerRunCmdPre's parameters cannot be modified and are read-only. 

Plugins that wish to use OnPlayerRunCmdPre can maintain backwards compatibility with SourceMod 1.10 by falling back to OnPlayerRunCmd if the Pre variant was never fired.
2023-08-30 22:08:45 +02:00
Mikusch 4e0c47a1d5 Fix TF2_OnIsHolidayActive forward not getting called after map change (#1752)
* Fix TF2_IsHolidayActive forward not getting called after map change

* Rename function to Unhook
2023-08-30 22:08:45 +02:00
Mikusch 65b6602a19 SDKTools: Clear gamerules pointer on level shutdown (#1755)
* Clear gamerules pointer on level shutdown

* Move LevelShutdown up
2023-08-30 22:08:45 +02:00
Alienmario 1723f97202 Add recent gamedata for BMS, HL2DM (#1756) 2023-08-30 22:08:45 +02:00
Kyle ec92653bb7 Honor AUTOLOAD_EXTENSIONS in clientprefs include (#1718)
* Optional autoload extensions

* revert autoload for cstrike/tf2
2023-08-30 22:08:45 +02:00
peace-maker b25d25890a DHooks: Allow setting CBaseEntity* param to NULL #1751 (#1754)
* DHooks: Allow setting CBaseEntity* param to NULL #1751

The param had to be a valid entity and wasn't allowed to be set to NULL. Behave similar to SetReturn which maps INVALID_ENT_REFERENCE (or -1) to NULL.

* Update include documentation
2023-08-30 22:08:45 +02:00
peace-maker b27243c3ac Add -m flag to checkout-deps to avoid downloading MySQL (#1753)
If you just want to build an extension or SourceMod core and not the MySQL extension, don't download the large MySQL library archive.
2023-08-30 22:08:45 +02:00
XeroX be648382d7 ZPS Update offsets for 3.2.4 (#1746)
* Update offsets for ZPS 3.2

Zombie Panic! Source Version 3.2 released and these are the updated offsets.

* Update offsets for 3.2

ZPS 3.2 has been release and these are the updated offsets / signatures.

* ZPS: Add missing offsets.

Adds support for OnTakeDamage_Alive, GetMaxHealth, Blocked, Reload and GroundEntChanged.
I wasn't aware these were missing entirely or I would have pushed them with the previous PR.

* ZPS Add missing offsets.

Adds support for GivePlayerAmmo.
Wasn't aware these were missing the first place.

* Fix ForcePlayerSuicide not properly working in ZPS

ZPS requires the second bool parameter to be true otherwise it won't do anything for players in the lobby or delayed for players on either team 2 or team 3.

* Fixed breaking code for other mods.

Added bForce which is set to true for zps.

* Update offsets for ZPS 3.2.4

Updates the offset
2023-08-30 22:08:45 +02:00
XeroX b5ed266941 ZPS: Add missing offsets (#1719)
* Update offsets for ZPS 3.2

Zombie Panic! Source Version 3.2 released and these are the updated offsets.

* Update offsets for 3.2

ZPS 3.2 has been release and these are the updated offsets / signatures.

* ZPS: Add missing offsets.

Adds support for OnTakeDamage_Alive, GetMaxHealth, Blocked, Reload and GroundEntChanged.
I wasn't aware these were missing entirely or I would have pushed them with the previous PR.

* ZPS Add missing offsets.

Adds support for GivePlayerAmmo.
Wasn't aware these were missing the first place.

* Fix ForcePlayerSuicide not properly working in ZPS

ZPS requires the second bool parameter to be true otherwise it won't do anything for players in the lobby or delayed for players on either team 2 or team 3.

* Fixed breaking code for other mods.

Added bForce which is set to true for zps.
2023-08-30 22:08:45 +02:00
MartLegion 2ffaa04130 Moved pvkii section for better readbility (#1744)
Moved pvkii section above for better readbility
(requested by psychonic)
2023-08-30 22:08:45 +02:00
MartLegion eddf9cf843 Added PVKII game with related team colors (beacon) (#1743)
Spectators = Yellow
Pirates = Red
Vikings = Green
Knights = Blue
2023-08-30 22:08:45 +02:00
Gaben 7c1095e4e7 Allow h-flag admins to bypass vote delay (#1733) 2023-08-30 22:08:45 +02:00
Arron Vinyard 686e8b4d7e Add GetCmdArgFloat(Ex) stocks (#1742) 2023-08-30 22:08:45 +02:00
David Anderson edc73649ff Update SourcePawn. 2023-08-30 22:08:45 +02:00
Mikusch 1ad8bb4560 Remove misleading note on DynamicHook.RemoveHook docs (#1725) 2023-08-30 22:08:45 +02:00
James Dickens 7eabeaa384 Fix Int64ToString producing incorrect output (#1723) 2023-08-30 22:08:45 +02:00
rtldg 4547270934 Correct some function docs that return char count (#1721) 2023-08-30 22:08:45 +02:00
V 3a2c5cb0f7 Inline one-expression single-use functions (#1700) 2023-08-30 22:08:45 +02:00
XeroX 7648c7fe03 ZPS: Update Offsets & Signatures for 3.2 (#1717)
* Update offsets for ZPS 3.2

Zombie Panic! Source Version 3.2 released and these are the updated offsets.

* Update offsets for 3.2

ZPS 3.2 has been release and these are the updated offsets / signatures.
2023-08-30 22:08:45 +02:00
Corey D ffb6d769e8 Add MAX_AUTHID_LENGTH (#1696) 2023-08-30 22:08:45 +02:00
Erik Minekus 77f999f810 Database.Format destination buffer should not be marked const (#1714) 2023-08-30 22:08:45 +02:00
Corey D c5049990a3 Add bitwise SetBitFlags for AdminId and GroupId (#1677)
* Add bitwise SetFlags for AdminId

* Inline SetFlags natives
2023-08-30 22:08:45 +02:00
XeroX 0e4894e8db ZPS: Add LookupEntityAttachment and GetEntityAttachment Gamedata (#1706)
* Add LookupAttachment and GetAttachment

Offset and Signature for Zombie Panic! Source.

* Fix Gamerules not being available

This fixes the issue that would throw an error when trying to use SDKCall with type SDKCall_GameRules.

* Add SetOwnerEntity offset

Add SetOwnerEntity offset
2023-08-30 22:08:45 +02:00
Asher Baker 19f1ffa401 Stop SQLite results being used before being fetched (#1709)
The DB API requires FetchRow to be called before accessing any values
from a row, but the SQLite driver did not enforce that requirement and
alowed accessing the first row immediately. A plugin developer hit this
when developing against SQLite locally but using MySQL in production,
where the API misuse threw an error as expected.

Resolves #1691
2023-08-30 22:08:45 +02:00
Asher Baker c206ac3099 Fix FindSendPropOffs with SendPropArray props (#1708)
FindSendPropOffs is deprecated and FindSendPropInfo behaves correctly
here, but there are still a lot of old plugins using FindSendPropOffs.
One of the SendPropArray props broken by the changes here is
m_hViewModel which there are known plugins in the wild accessing.
2023-08-30 22:08:45 +02:00
Alienmario e03320c80e Enable nextmap for Black Mesa (#1695) 2023-08-30 22:08:45 +02:00
Ҝℴţأķ 2ae4624344 Remove hardcoded question mark from sm_vote (#1699) 2023-08-30 22:08:45 +02:00
66c8005666 adminhelp.sp -> Switch to the new CommandIterator() (#1388)
* Switch to the new CommandIterator() methodmap

`ReadCommandIterator` 
->
```
CmdIter.GetName(Name, sizeof(Name));
Flags = CmdIter.Flags;
CmdIter.GetDescription(Desc, sizeof(Desc));
```

* PeaceMaker fixes

* Don't fetch unused properties

Co-authored-by: Michael Flaherty <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2023-08-30 22:08:45 +02:00
Arron Vinyard 210219030e Update dhooks.inc documentation for consistency (#1658)
* Update dhooks.inc documentation for consistency

- Modifies whitespace (change tabs to spaces for non initial indents, fix alignments, create consistency with rest of SM docs)
- Change `/*` to `/**` for consistency and to indicate comment doc
- Removes incorrect `@noreturn` doc

* Split long comments across multiple lines

* Remove `@noreturn` in IGameConfigs.h

* Remove `@noreturn` from IGameHelpers.h

* Remove `@noreturn` from asm.c

* Add `@noreturn` to ThrowError

* Add `@noreturn` and `@error` to ThrowNativeError
2023-08-30 22:08:45 +02:00
komashchenko 370fa8209b Fix GetGameSoundParams in CSGO (#1631)
* Fix GetGameSoundParams CSGO

* Added preprocessor comments

* Revert InternalPrecacheScriptSound
2023-08-30 22:08:45 +02:00
peace-maker 88a0a458a6 Fix silent error on large ArrayList startsize (#1705)
If there isn't enough memory to resize the ArrayList to the startsize on construction, throw an error instead of ignoring the OOM.

Fixes #1551
2023-08-30 22:08:45 +02:00
dysphie e4afe31fc0 Add NMRiH support for new collision and attachment natives (#1702)
- Add "GetAttachment" offset
- Add "SetOwnerEntity" offset
- Add "LookupAttachment" signature
2023-08-30 22:08:45 +02:00
Corey D fb0144900f Add ArrayStack.Clear native (#1676) 2023-08-30 22:08:45 +02:00
Mikusch 542b7673d6 Add LookupEntityAttachment & GetEntityAttachment natives (#1653)
Using the virtual `CBaseAnimating::GetAttachment(int, matrix3x4_t &)` was a deliberate choice because virtual offsets are generally easier to maintain than signatures. The `matrix3x4_t` is converted to world position and world angles internally.
Some of the other overloads are also inlined on a few games, making this the best choice.

Since this call can only be used on classes inheriting `CBaseAnimating`, we check if the `DT_BaseAnimating` SendTable exists on the entity, throwing a native error if it doesn't.
This safeguard could be greatly improved with a call to `CBaseEntity::GetBaseAnimating`, but would require more gamedata (maybe something to consider for the future?)
2023-08-30 22:08:45 +02:00
Corey D cc1c02e3de Fix ArrayList return types (#1679)
ArrayList.SetString and ArrayList.SetArray both have int return types but their methodmap natives use void.
2023-08-30 22:08:45 +02:00
naydef 4fc0a85581 Fix crash in non-bypass-hooks DropWeapon implementation (#1672)
Fixes #1670
2023-08-30 22:08:45 +02:00
Nick Hastings 6733c4182d Fix helpers module path lookup on newer Perl versions. 2023-08-30 22:08:45 +02:00
Nick Hastings 28ef59ada5 Missing ;s. 2023-08-30 22:08:45 +02:00
Nicholas Hastings 647e320b57 [Buildbot] Delete package after successful upload. (#1665)
* [Buildbot] Delete package after successful upload.

* Use autodie.

* ;
2023-08-30 22:08:45 +02:00
Nick Hastings cdfbdcc29d Remove dead code. 2023-08-30 22:08:45 +02:00
Nick Hastings 4f94b4f900 Use SteamWorks C++ API in CStrike RulesFix, for better interface compatibility. 2023-08-30 22:08:45 +02:00
Asher Baker 53be0e0812 Upload libsourcepawn Breakpad symbols as well 2023-08-30 22:08:45 +02:00
Asher Baker 88dd142f5c Upload libsourcepawn debug symbols 2023-08-30 22:08:45 +02:00
David Anderson 1d5ec7b982 Update SourcePawn/AMTL to fix build. 2023-08-30 22:08:45 +02:00
Fyren f1a71b0d5a Update SP. 2023-08-30 22:08:45 +02:00
Headline 85b8e09e8a Prevent enum shadowing & pin sourcemod for build fixes (#1661) 2023-08-30 22:08:45 +02:00
David Anderson df9b0ef1b3 Update AMTL. 2023-08-30 22:08:45 +02:00
David Anderson f81412b4fa Fix vote menu handler to work with direct arrays. 2023-08-30 22:08:45 +02:00
David Anderson bd478ebdfc Dedent some menu code for readability. 2023-08-30 22:08:45 +02:00
Benoist a57573864a Fix ShouldCollide originalResult behaviour (#1657) 2023-08-30 22:08:45 +02:00
David Anderson 6b6436db17 Update SourcePawn.
Fix fix an hl2sdk-mock build error.
2023-08-30 22:08:45 +02:00
Stanislav Polshyn c88b18e520 gamedata: Add SetEntityOwner L4D1 offset (#1649) 2023-08-30 22:08:45 +02:00
8c001872a8 sdktools: Add EntityCollisionRulesChanged & SetEntityOwner natives (#1620)
* Add EntityCollisionRulesChanged & SetEntityOwner natives

* fix win build, and unpushed changes

* Fixed bad world loop

* Requested changes + csgo offsets

* small copy paste mistake

* Strip the debug log lines

* Tiny clean up in comments

* line

* <dvander> try again

* sdktools: add default Param for owner.

Co-authored-by: Kenzzer <[email protected]>
Co-authored-by: Kyle Sanderson <[email protected]>
2023-08-30 22:08:45 +02:00
Adrián 5db571bbc6 menusys: Correct GetMenuExitBackButton returncode (#1646) 2023-08-30 22:08:45 +02:00
peace-maker 1f561b54f3 Cache copy of library on first symbol/signature lookup (#1642)
Always perform signature searches on an unaltered copy of the binary. This avoids signature mismatches if the same function is detoured twice and thus the first bytes of the function were replaced by the detour.
2023-08-30 22:08:45 +02:00
Peace-Maker 77a0fb4402 Ignore all build* folders in .gitignore
I frequently have one like `buildwin` and `buildlin` for the different OS in WSL.
2023-08-30 22:08:45 +02:00
Peace-Maker c72736dcfa Use SourceMod's version number 2023-08-30 22:08:45 +02:00
Peace-Maker d16f45c4b6 Print OS errors if mprotect fails
This helped catch a bug in DHooks before, so it's worth adding it in here as well.
2023-08-30 22:08:45 +02:00
Peace-Maker e280d33a28 Use JMP patching of CDetour 2023-08-30 22:08:45 +02:00
Peace-Maker fb1d501c24 Add copyright headers 2023-08-30 22:08:45 +02:00
Peace-Maker 3c0f700a6b Import DHooks + Dynamic Detouring
This is the latest DHooks version from https://github.com/peace-maker/DHooks2/tree/1314f2d1b4d870677a1b9f628c770f2799878f9b
2023-08-30 22:08:45 +02:00
Adrián 45e9da90fc Fix translations getting truncated prematurely (#1640) 2023-08-30 22:08:45 +02:00
Nick Hastings a60c443b29 Update Contagion SDKHooks gamedata. 2023-08-30 22:08:45 +02:00
Nick Hastings b064366169 Disable Reserved Slots plugin on Contagion.
(It doesn't have sv_visiblemaxplayers).
2023-08-30 22:08:45 +02:00
Nick Hastings a06a903989 Make separate copy of makesig script for IDA 7.x. 2023-08-30 22:08:45 +02:00
Mikusch e67fdf2331 Update Team Fortress 2 gamedata
Shift virtual offsets up by 3
2023-08-30 22:08:45 +02:00
Nick Hastings e2ca3a9493 Update Contagion SDKTools gamedata. 2023-08-30 22:08:45 +02:00
dysphie 9099634b95 Fix "ForcePlayerSuicide" linux offset in NMRiH (#1627) 2023-08-30 22:08:45 +02:00
David Anderson f182feed9d Add support for ASAN. 2023-08-30 22:08:45 +02:00
David Anderson 02dd6a9c00 Update SourcePawn.
This fixes a few more regressions found in the new compiler.
2023-08-30 22:08:45 +02:00
David Anderson b421a0c594 Update SourcePawn.
This fixes a recent stability regression.
2023-08-30 22:08:45 +02:00
Nick Hastings 9ec5cb819c Fix server binary name for CRC lookups. 2023-08-30 22:08:45 +02:00
Nick Hastings f8e664b797 Invert CRC to match most popular implementations. 2023-08-30 22:08:45 +02:00
dysphie 42575f7cc3 Update gamedata for NMRiH 1.12 (#1623)
* Fix NMRiH's sdktools gamedata

* Fix NMRiH's sdkhooks gamedata

* Fix GroundEntChanged linux offset
2023-08-30 22:08:45 +02:00
David Anderson aa98f60ca3 Update SourcePawn. 2023-08-30 22:08:45 +02:00
David Anderson d5f47a88e6 Switch to clang-8.
This is the new minimum supported Clang version going forward.
2023-08-30 22:08:45 +02:00
FortyTwoFortyTwo 62f08a31fc Fix compiling with tf2_stocks 2023-08-30 22:08:45 +02:00
David Anderson e565c83494 Fix build. 2023-08-30 22:08:45 +02:00
David Anderson a68ff2a587 Fix error return of FormatNativeString. (#1613) 2023-08-30 22:08:45 +02:00
David Anderson 377888276e Update to the new SourcePawn compiler.
This imports the brand new SourcePawn compiler. The new compiler is much
faster to compile and generates significantly improved code around
array generation and array access.

There are a number of compatibility changes in the new compiler. Most of
these are due to improved type checking and error detection. The full
list of notes can be found here:

https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md

Additionally, .smx files generated by the new compiler will NOT load on
earlier versions of SourceMod, including earlier versions of 1.11. Old
plugins will continue to load as normal.
2023-08-30 22:08:45 +02:00
Nick Hastings 52b8e95aae Throw error if unknown SDK call type specified. 2023-08-30 22:08:45 +02:00
Nick Hastings bb6e6fd3b6 Reorder SDKCallType enum for better compatibility with existing plugins. 2023-08-30 22:08:45 +02:00
dragokas 0449342bb0 Exposed SDKCall_Server call type to use CBaseServer methods 2023-08-30 22:08:45 +02:00
David Anderson 3171d4fb7b Bump handle limit to 1MB.
This bumps the handle bits to 20 and reduces the serial/cookie bits to
12. A warning is emitted if a single owner creates more than 100k
handles.

Tested on mock srcds with sm_dump_handles.
2023-08-30 22:08:45 +02:00
Kyle 066818fbec Update TerminateRound signature for CSGO 2023-08-30 22:08:45 +02:00
Vladimir 255f5ea745 Add OnClientLanguageChanged() forward (#1597) 2023-08-30 22:08:45 +02:00
Asher Baker 2e763c287c Fix build against released MM:S branches (#1607)
`SOURCE_ENGINE_MOCK` is part of the MM:S API and only available in 1.12
which is the dev branch of MM:S. We can build the mock SM build without
it anyway, but this is still a little gross as it is just missing loader
support which could be confusing.

This also changes AMBuildScript to first look for a checkout of MM:S
1.11 which has been the stable branch for a while, 1.10 is still allowed
as a fallback as that seems to still build without issue.
2023-08-30 22:08:45 +02:00
Nick Hastings 29c33bfcc9 Fix up OnClientSettingsChange logic order.
- Fixes bot name cache not getting updated (see #1579)
- Fixes IClientListener::OnClientSettingsChanged not being called for bots.
2023-08-30 22:08:45 +02:00
Nick Hastings 88bdbf4866 Add support for other Steam ID formats to admin-sql-threaded (#1520). 2023-08-30 22:08:45 +02:00
Nick Hastings 1559b75ae7 Don't cache replay_enable value when evaling whether to use FileExists hook.
Speculative fix for #1581
2023-08-30 22:08:45 +02:00
Nick Hastings ff00e3d036 Mark unloaded extensions as not fully loaded (fixes #1574). 2023-08-30 22:08:45 +02:00
Charles daa1a4df14 Removed incorrect parameter RemoveChangeHook desc 2023-08-30 22:08:45 +02:00
Charles 4e2ace9d2f Removed incorrect parameter in description 2023-08-30 22:08:45 +02:00
Corey D ee722d778f Use words instead of quotes for better clarity
As suggested by sneak-it
2023-08-30 22:08:45 +02:00
Corey D e82f0acdb8 Missed Database.Escape 2023-08-30 22:08:45 +02:00
Corey D 383cd81be6 Clarify SQL_EscapeString usage
It's hard to see the difference between 2 single-quotes and 1 double-quote unless you highlight it.
2023-08-30 22:08:45 +02:00
Nick Hastings 6250db7dba Add missing parameters for CSWeaponDrop. 2023-08-30 22:08:45 +02:00
GAMMACASE 8ae3be7185 Fix CS_OnCSWeaponDrop after latest CSGO update (21/10/21) (#1602) 2023-08-30 22:08:45 +02:00
Vauff 64b897822f Update HandleCommand_Buy_Internal sig and related offsets for CS:GO Win 2023-08-30 22:08:45 +02:00
Vauff 0ad0d839a0 Update CSWeaponDropBB signature for CS:GO Linux 2023-08-30 22:08:45 +02:00
Nick Hastings 55382e0a0f Add option to not bypass hooks with TakeDamage and DropWeapon natives. 2023-08-30 22:08:45 +02:00
Nick Hastings d8b00675d6 Remove obsolete core-legacy checks. 2023-08-30 22:08:45 +02:00
Nick Hastings 633298b6a9 Fix attempting to link win32 libprotobuf on win64. 2023-08-30 22:08:45 +02:00
Nick Hastings 656a9fa94f Use MM:S master for Windows builds (already doing for Linux/Mac). 2023-08-30 22:08:45 +02:00
Nicholas Hastings bb7929594d Typo fix. 2023-08-30 22:08:45 +02:00
Nick Hastings d4545a105e Fix incorrect param types in CS:GO GNI (otherwise broken for x64). 2023-08-30 22:08:45 +02:00
Nick Hastings 1c788b5c0a Remove manual ptr math in vnatives for params not mapped to cell params. 2023-08-30 22:08:45 +02:00
Nick Hastings 7ce8a80bf6 Fix Blade Symphony GiveNamedItem call on x64. 2023-08-30 22:08:45 +02:00
Nick Hastings d0c4657b75 Add ValveType_Object type.
This is for the few cases where we have function parameters to objects that
don't fit our other predefined types (CBaseEntity, CBasePlayer, string).

Most calls currently pass those as POD (which is incorrect, but works on
x86), or one of the other pointer types (which is also incorrect, and can
lead to confusion, but works on x86 and x86-64).

This type only works when manually buffering the parameter for calls, and
is not supported for return types.
2023-08-30 22:08:45 +02:00
Nick Hastings 167e6d8571 On Linux, link against server libs instead of client (fixes META_CONPRINT). 2023-08-30 22:08:45 +02:00
Nick Hastings b909ea07ec Update multiple SDKTools calls to be x64-safe [sizeof(void*) != sizeof(int)] 2023-08-30 22:08:45 +02:00
Nick Hastings 410eb01f91 Add missing WriteBaselines signatures for Blade Symphony. 2023-08-30 22:08:45 +02:00
Nick Hastings f18c6fb9af Fix crash with IBinTools calls on x64 for functions with void return. 2023-08-30 22:08:45 +02:00
Nick Hastings c66175d64e Use new GetIServer call and updated IServerTools calls on Blade Symphony. 2023-08-30 22:08:45 +02:00
Nick Hastings a3fbbe41f2 Enable more CS:GO engine logic for Blade Symphony. 2023-08-30 22:08:45 +02:00
Nick Hastings ed5713b3fd Blade Symphony gamedata updates, round 1. 2023-08-30 22:08:45 +02:00
Nick Hastings 4b01feea45 Fix CS:GO transposing of GiveNamedItem last param and return. 2023-08-30 22:08:45 +02:00
Nick Hastings 800f613f92 Enable 64-bit builds for Blade Symphony. 2023-08-30 22:08:44 +02:00
Nick Hastings 4c9e856197 Add .vs and .vscode dirs to .gitignore. 2023-08-30 22:08:44 +02:00
dysphie 0cb3fcaed8 Fix NMRiH's 'GivePlayerAmmo' offset on Windows (#1593) 2023-08-30 22:08:44 +02:00
naydef 02e2659ba5 Update float.inc (#1591) 2023-08-30 22:08:44 +02:00
A1m` 507b09c975 Fix 'GiveNamedItem' in the game left4dead2. (#1590)
* Fix 'GiveNamedItem' in the game left4dead2.

The method 'CCSPlayer::GiveNamedItem(char const*, int, CBaseEntity*)' does not work in game left4dead2, any given weapon and object immediately falls to the ground, besides, the code is missing a parameter to call this method (even with the added parameter does not give a weapon to hands). Another method 'CTerrorPlayer::GiveNamedItem(char const*, int, bool, CBaseEntity*)' works great, besides it makes it possible to give out all the items that exist in the game.

* Add x64 support

Add x64 support

* Changed argument type to bool.

Changed argument type to bool.
2023-08-30 22:08:44 +02:00
David Anderson 907fa77381 Update bootstrap.pl 2023-08-30 22:08:44 +02:00
David Anderson c5c302dccc Use master for Metamod:Source builds. 2023-08-30 22:08:44 +02:00
David Anderson c49d51206c Don't require hl2sdk-mock if sdks=all 2023-08-30 22:08:44 +02:00
pedrotski f48cbaab75 Update CS:GO Reload gamedata (#1585) 2023-08-30 22:08:44 +02:00
nosoop 61c634a870 TF2Tools: Prevent CalcIsAttackCriticalHelper* from being called twice (#1573)
* Ensure CalcIsAttackCriticalHelper only gets called once

* fixup: move origReturnValue up
2023-08-30 22:08:44 +02:00
David Anderson fec978afb4 Add support for hl2sdk-mock. 2023-08-30 22:08:44 +02:00
David Anderson 74ad70ba30 Add support for hl2sdk-mock. 2023-08-30 22:08:44 +02:00
GAMMACASE fe27c8810e Update gamedata after latest CSGO update (22/09/21) (#1583) 2023-08-30 22:08:44 +02:00
Nick Hastings b4c5726fc2 Trigger build for TF2 SDK INetChannel changes. 2023-08-30 22:08:44 +02:00
Nick Hastings 53c8d5533a Trigger build for TF2 SDK changes. 2023-08-30 22:08:44 +02:00
Bara 4e66452e58 Update sourcepawn 2023-08-30 22:08:44 +02:00
Vladimir 63545eb741 Fix crash in FindSendPropInfo() when the prop was DPT_DataTable (#1575) 2023-08-30 22:08:44 +02:00
Asher Baker a7af242453 Revert "Switch internal SM concept of frames to use Think (#1540)" (#1572)
This reverts commit b383302128.
2023-08-30 22:08:44 +02:00
232 changed files with 63406 additions and 29657 deletions
+4 -2
View File
@@ -3,9 +3,11 @@ on:
push:
branches:
- master
- '[0-9]+.[0-9]+-dev'
pull_request:
branches:
- master
- '[0-9]+.[0-9]+-dev'
jobs:
test:
strategy:
@@ -21,8 +23,8 @@ jobs:
compiler_cxx: clang++
- os: ubuntu-18.04
os_short: linux
compiler_cc: clang-3.9
compiler_cxx: clang++-3.9
compiler_cc: clang-8
compiler_cxx: clang++-8
fail-fast: false
runs-on: ${{ matrix.os }}
name: ${{ matrix.os_short }}-${{ matrix.compiler_cc }}
+1
View File
@@ -3,6 +3,7 @@ on:
push:
branches:
- master
- '[0-9]+.[0-9]+-dev'
paths:
- 'plugins/include/*'
- 'sourcepawn/**'
+4 -1
View File
@@ -28,7 +28,7 @@ LIB-Debug/
[Tt]humbs.db
# AMBuild build directories
build/
build*/
obj-*/
*~
*.rej
@@ -37,3 +37,6 @@ obj-*/
*.swp
*.gdb_history
objdir
.vs/*
.vscode/*
+2
View File
@@ -1,6 +1,8 @@
[submodule "public/amtl"]
path = public/amtl
url = https://github.com/alliedmodders/amtl
shallow = true
[submodule "sourcepawn"]
path = sourcepawn
url = https://github.com/alliedmodders/sourcepawn
shallow = true
+84 -7
View File
@@ -1,6 +1,7 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import collections
import os, sys
import subprocess
import traceback
class SDK(object):
@@ -31,11 +32,21 @@ class SDK(object):
WinOnly = ['windows']
WinLinux = ['windows', 'linux']
WinLinuxMac = ['windows', 'linux', 'mac']
Blade = {
'windows': ['x86', 'x86_64'],
'linux': ['x86_64'],
'mac': ['x86_64']
}
CSGO = {
'windows': ['x86'],
'linux': ['x86', 'x86_64'],
'mac': ['x86_64']
}
Mock = {
'windows': ['x86', 'x86_64'],
'linux': ['x86', 'x86_64'],
'mac': ['x86_64']
}
SDKMap = {
'episode1': SDK('HL2SDK', '2.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'),
@@ -54,11 +65,12 @@ SDKMap = {
'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'),
'csgo': SDK('HL2SDKCSGO', '2.csgo', '21', 'CSGO', CSGO, 'csgo'),
'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '17', 'PORTAL2', [], 'portal2'),
'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', WinOnly, 'blade'),
'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', Blade, 'blade'),
'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'),
'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '14', 'CONTAGION', WinOnly, 'contagion'),
'bms': SDK('HL2SDKBMS', '2.bms', '10', 'BMS', WinLinux, 'bms'),
'doi': SDK('HL2SDKDOI', '2.doi', '20', 'DOI', WinLinuxMac, 'doi'),
'mock': SDK('HL2SDK-MOCK', '2.mock', '999', 'MOCK', Mock, 'mock'),
}
# Stable sorting for command equivalence in AMBuild.
@@ -110,7 +122,8 @@ class SMConfig(object):
self.versionlib = None
self.all_targets = []
self.target_archs = set()
self.enable_asan = getattr(builder.options, 'enable_asan', False)
self.asan_libs = {}
if builder.options.targets:
target_archs = builder.options.targets.split(',')
@@ -173,7 +186,7 @@ class SMConfig(object):
else:
sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder)
if sdk_path is None or not os.path.isdir(sdk_path):
if use_all or sdk_name in sdk_list:
if (use_all and sdk_name != 'mock') or sdk_name in sdk_list:
raise Exception('Could not find a valid path for {0}'.format(sdk.envvar))
continue
if use_all or use_present or sdk_name in sdk_list:
@@ -186,7 +199,9 @@ class SMConfig(object):
if builder.options.mms_path:
self.mms_root = builder.options.mms_path
else:
self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10')
self.mms_root = ResolveEnvPath('MMSOURCE111', 'mmsource-1.11')
if not self.mms_root:
self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10')
if not self.mms_root:
self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'metamod-source')
if not self.mms_root:
@@ -336,6 +351,11 @@ class SMConfig(object):
cxx.cxxflags += ['-Wno-deprecated']
cxx.cflags += ['-Wno-sometimes-uninitialized']
if self.enable_asan:
if not have_clang:
raise Exception('--enable-asan only supported when using Clang')
self.configure_asan(cxx)
# Work around SDK warnings.
if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0':
cxx.cflags += [
@@ -348,12 +368,18 @@ class SMConfig(object):
cxx.cflags += ['-Wno-maybe-uninitialized']
if builder.options.opt == '1':
cxx.cflags += ['-O3']
if self.enable_asan:
cxx.cflags += ['-O1']
else:
cxx.cflags += ['-O3']
# Don't omit the frame pointer.
cxx.cflags += ['-fno-omit-frame-pointer']
def configure_msvc(self, cxx):
if self.enable_asan:
raise Exception('--enable-asan only supported when using Clang')
if builder.options.debug == '1':
cxx.cflags += ['/MTd']
cxx.linkflags += ['/NODEFAULTLIB:libcmt']
@@ -399,6 +425,29 @@ class SMConfig(object):
# Don't omit the frame pointer.
cxx.cflags += ['/Oy-']
def configure_asan(self, cxx):
if cxx.target.platform != 'linux':
raise Exception('--enable-asan only supported on Linux')
cxx.cflags += ['-fsanitize=address']
cxx.linkflags += ['-fsanitize=address']
if cxx.target.arch == 'x86':
libclang_rt = 'libclang_rt.asan-i386.so'
else:
libclang_rt = 'libclang_rt.asan-x86_64.so'
try:
argv = cxx.cxx_argv + ['--print-file-name', libclang_rt]
output = subprocess.check_output(argv)
output = output.decode('utf-8')
output = output.strip()
except:
raise Exception('Could not find {}'.format(libclang_rt))
print('ASAN library for {}: {}'.format(cxx.target.arch, output))
print('You will need to LD_PRELOAD this into srcds.')
self.asan_libs[cxx.target.arch] = os.path.dirname(output)
def configure_linux(self, cxx):
cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64']
cxx.linkflags += ['-lm']
@@ -447,7 +496,7 @@ class SMConfig(object):
'-current_version', self.productVersion
]
if self.use_auto_versioning():
binary.compiler.linkflags += [self.versionlib[binary.compiler.target.arch]]
binary.compiler.postlink += [self.versionlib[binary.compiler.target.arch]]
binary.compiler.sourcedeps += SM.generated_headers
return binary
@@ -456,6 +505,13 @@ class SMConfig(object):
self.AddVersioning(binary)
if binary.compiler.like('msvc'):
binary.compiler.linkflags += ['/SUBSYSTEM:WINDOWS']
# Dumb clang behavior means we have to manually find libclang_rt.
if self.enable_asan:
binary.compiler.linkflags += [
'-shared-libsan',
'-Wl,-rpath={}'.format(self.asan_libs[binary.compiler.target.arch]),
]
return binary
def ProgramBuilder(self, compiler, name):
@@ -463,6 +519,8 @@ class SMConfig(object):
self.AddVersioning(binary)
if '-static-libgcc' in binary.compiler.linkflags:
binary.compiler.linkflags.remove('-static-libgcc')
if self.enable_asan:
binary.compiler.linkflags.append('-static-libsan')
if '-lgcc_eh' in binary.compiler.linkflags:
binary.compiler.linkflags.remove('-lgcc_eh')
if binary.compiler.like('gcc'):
@@ -569,7 +627,25 @@ class SMConfig(object):
if compiler.target.platform == 'linux':
if sdk.name in ['csgo', 'blade']:
compiler.linkflags.remove('-static-libstdc++')
compiler.linkflags += ['-lstdc++']
compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0']
elif compiler.target.platform == 'mac':
if sdk.name in ['csgo']:
# Switch libc++ to libstdc++ for protobuf linkage.
compiler.cxxflags.remove('-stdlib=libc++')
compiler.linkflags.remove('-stdlib=libc++')
compiler.linkflags.remove('-lc++')
compiler.cxxflags += ['-stdlib=libstdc++']
compiler.linkflags += ['-stdlib=libstdc++']
compiler.linkflags += ['-lstdc++']
if 'c++1y' in compiler.cxxflags:
compiler.cxxflags.remove('-std=c++1y')
compiler.cxxflags += ['-std=c++11']
elif 'c++14' in compiler.cxxflags:
compiler.cxxflags.remove('-std=c++14')
compiler.cxxflags += ['-std=c++11']
for path in paths:
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
@@ -613,7 +689,7 @@ class SMConfig(object):
if compiler.target.platform == 'linux':
if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2', 'insurgency', 'doi']:
dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so']
elif compiler.target.arch == 'x86_64' and sdk.name in ['csgo', 'blade']:
elif compiler.target.arch == 'x86_64' and sdk.name in ['csgo', 'mock']:
dynamic_libs = ['libtier0_client.so', 'libvstdlib_client.so']
elif sdk.name in ['l4d', 'blade', 'insurgency', 'doi', 'csgo']:
dynamic_libs = ['libtier0.so', 'libvstdlib.so']
@@ -746,6 +822,7 @@ else:
'extensions/clientprefs/AMBuilder',
'extensions/curl/AMBuilder',
'extensions/cstrike/AMBuilder',
'extensions/dhooks/AMBuilder',
'extensions/geoip/AMBuilder',
'extensions/mysql/AMBuilder',
'extensions/pgsql/AMBuilder',
+2 -1
View File
@@ -3,6 +3,7 @@ image: Visual Studio 2015
clone_folder: c:/projects/sourcemod
clone_depth: 1
install:
- cmd: set PATH=C:\Python38;C:\Python38\Scripts;%PATH%
- cmd: git submodule update --init --recursive
- cmd: git pull --recurse-submodules
- cmd: cd ..
@@ -12,5 +13,5 @@ build_script:
- cmd: call "%VS140COMNTOOLS%/vsvars32.bat"
- cmd: mkdir build
- cmd: cd build
- cmd: c:\python27\python.exe ../configure.py --enable-optimize --no-mysql --sdks=episode1,css,tf2,l4d2,csgo
- cmd: python.exe ../configure.py --enable-optimize --no-mysql --sdks=episode1,css,tf2,l4d2,csgo
- cmd: ambuild
+2
View File
@@ -115,6 +115,8 @@ public:
virtual void ConsolePrint(const char *fmt, ...) = 0;
virtual void ConsolePrintVa(const char *fmt, va_list ap) = 0;
virtual void FormatSourceBinaryName(const char *basename, char *buffer, size_t maxlength) = 0;
// Game engine helper functions.
virtual bool IsClientConVarQueryingSupported() = 0;
virtual int QueryClientConVar(int client, const char *cvar) = 0;
+3
View File
@@ -73,6 +73,9 @@ struct sm_logic_t
void (*FreeCellArray)(ICellArray *arr);
void * (*FromPseudoAddress)(uint32_t pseudoAddr);
uint32_t (*ToPseudoAddress)(void *addr);
void (*SetEntityLumpWritable)(bool writable);
bool (*ParseEntityLumpString)(const char *entityString, int &status, size_t &position);
const char * (*GetEntityLumpString)();
IScriptManager *scripts;
IShareSys *sharesys;
IExtensionSys *extsys;
+19 -17
View File
@@ -1,30 +1,30 @@
# vim: set ts=2 sw=2 tw=99 noet:
import sys
try:
from ambuild2 import run, util
from ambuild2 import run, util
except:
try:
import ambuild
sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n')
sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n')
except:
sys.stderr.write('AMBuild must be installed to build this project.\n')
sys.stderr.write('http://www.alliedmods.net/ambuild\n')
sys.exit(1)
try:
import ambuild
sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n')
sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n')
except:
sys.stderr.write('AMBuild must be installed to build this project.\n')
sys.stderr.write('http://www.alliedmods.net/ambuild\n')
sys.exit(1)
# Hack to show a decent upgrade message, which wasn't done until 2.2.
ambuild_version = getattr(run, 'CURRENT_API', '2.1')
if ambuild_version.startswith('2.1'):
sys.stderr.write("AMBuild 2.2 or higher is required; please update\n")
sys.exit(1)
sys.stderr.write("AMBuild 2.2 or higher is required; please update\n")
sys.exit(1)
parser = run.BuildParser(sourcePath=sys.path[0], api='2.2')
parser.options.add_argument('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None,
help='Root search folder for HL2SDKs')
help='Root search folder for HL2SDKs')
parser.options.add_argument('--mysql-path', type=str, dest='mysql_path', default=None,
help='Path to MySQL 5')
help='Path to MySQL 5')
parser.options.add_argument('--mysql64-path', type=str, dest='mysql64_path', default=None,
help='Path to 64-bit MySQL 5')
help='Path to 64-bit MySQL 5')
parser.options.add_argument('--mms-path', type=str, dest='mms_path', default=None,
help='Path to Metamod:Source')
parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug',
@@ -37,11 +37,13 @@ parser.options.add_argument('-s', '--sdks', default='present', dest='sdks',
help='Build against specified SDKs; valid args are "none", "all", "present",'
' or comma-delimited list of engine names')
parser.options.add_argument('--breakpad-dump', action='store_true', dest='breakpad_dump',
default=False, help='Dump and upload breakpad symbols')
default=False, help='Dump and upload breakpad symbols')
parser.options.add_argument('--disable-auto-versioning', action='store_true', dest='disable_auto_versioning',
default=False, help='Disable the auto versioning script')
parser.options.add_argument('--targets', type=str, dest='targets', default=None,
help="Override the target architecture (use commas to separate multiple targets).")
help="Override the target architecture (use commas to separate multiple targets).")
parser.options.add_argument('--scripting-only', action='store_true', dest='scripting_only', default=False,
help="Only build and package the files required for scripting in SourcePawn.")
help="Only build and package the files required for scripting in SourcePawn.")
parser.options.add_argument('--enable-asan', action='store_true', dest='enable_asan',
default=False, help='Enable ASAN (clang only)')
parser.Configure()
+42 -74
View File
@@ -4,43 +4,42 @@ import os
project = builder.LibraryProject('sourcemod')
project.sources += [
'MenuStyle_Valve.cpp',
'logic_bridge.cpp',
'smn_entities.cpp',
'sm_stringutil.cpp',
'MenuVoting.cpp',
'smn_events.cpp',
'frame_hooks.cpp',
'smn_nextmap.cpp',
'sourcemm_api.cpp',
'ChatTriggers.cpp',
'smn_player.cpp',
'sourcemod.cpp',
'concmd_cleaner.cpp',
'HalfLife2.cpp',
'NextMap.cpp',
'ConCmdManager.cpp',
'ConVarManager.cpp',
'ConsoleDetours.cpp',
'CoreConfig.cpp',
'EventManager.cpp',
'GameHooks.cpp',
'HalfLife2.cpp',
'Logger.cpp',
'MenuManager.cpp',
'MenuStyle_Base.cpp',
'MenuStyle_Radio.cpp',
'MenuStyle_Valve.cpp',
'MenuVoting.cpp',
'NextMap.cpp',
'PlayerManager.cpp',
'TimerSys.cpp',
'UserMessages.cpp',
'concmd_cleaner.cpp',
'frame_hooks.cpp',
'logic_bridge.cpp',
'pb_handle.cpp',
'sm_autonatives.cpp',
'sm_stringutil.cpp',
'smn_commandline.cpp',
'smn_console.cpp',
'smn_entities.cpp',
'smn_events.cpp',
'CoreConfig.cpp',
'Logger.cpp',
'smn_halflife.cpp',
'smn_console.cpp',
'UserMessages.cpp',
'MenuManager.cpp',
'smn_hudtext.cpp',
'smn_keyvalues.cpp',
'smn_nextmap.cpp',
'smn_player.cpp',
'smn_usermsgs.cpp',
'MenuStyle_Base.cpp',
'smn_keyvalues.cpp',
'smn_vector.cpp',
'sourcemm_api.cpp',
'sourcemod.cpp',
'EventManager.cpp',
'MenuStyle_Radio.cpp',
'sm_autonatives.cpp',
'ConsoleDetours.cpp',
'smn_commandline.cpp',
'GameHooks.cpp',
]
for sdk_name in SM.sdks:
@@ -59,20 +58,18 @@ for sdk_name in SM.sdks:
builder.sourcePath
]
pb_includes = []
if sdk.name == 'csgo':
pb_includes = [
compiler.cxxincludes += [
os.path.join(sdk.path, 'common', 'protobuf-2.5.0', 'src'),
os.path.join(sdk.path, 'public', 'engine', 'protobuf'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf')
]
elif sdk.name == 'blade':
pb_includes = [
compiler.cxxincludes += [
os.path.join(sdk.path, 'common', 'protobuf-2.5.0', 'src'),
os.path.join(sdk.path, 'public', 'engine', 'protobuf'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'berimbau', 'protobuf')
]
compiler.cxxincludes += pb_includes
if compiler.like('msvc'):
compiler.defines += ['_ALLOW_KEYWORD_MACROS']
@@ -88,21 +85,27 @@ for sdk_name in SM.sdks:
compiler.linkflags += ['-Wl,--exclude-libs=libprotobuf.a']
elif compiler.target.platform == 'mac':
if compiler.target.arch == 'x86':
lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf-libcxx.a')
lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf.a')
elif compiler.target.arch == 'x86_64':
lib_path = os.path.join(sdk.path, 'lib', 'osx64', 'release', 'libprotobuf-libcxx.a')
lib_path = os.path.join(sdk.path, 'lib', 'osx64', 'release', 'libprotobuf.a')
elif compiler.target.platform == 'windows':
msvc_ver = compiler.version
vs_year = ''
platform = ''
if compiler.target.arch == 'x86':
platform = 'win32'
elif compiler.target.arch == 'x86_64':
platform = 'win64'
if 1900 <= msvc_ver < 2000:
vs_year = '2015'
else:
raise Exception('Cannot find libprotobuf for MSVC version "' + str(compiler.version) + '"')
if 'DEBUG' in compiler.defines:
lib_path = os.path.join(sdk.path, 'lib', 'win32', 'debug', 'vs' + vs_year, 'libprotobuf.lib')
lib_path = os.path.join(sdk.path, 'lib', platform, 'debug', 'vs' + vs_year, 'libprotobuf.lib')
else:
lib_path = os.path.join(sdk.path, 'lib', 'win32', 'release', 'vs' + vs_year, 'libprotobuf.lib')
lib_path = os.path.join(sdk.path, 'lib', platform, 'release', 'vs' + vs_year, 'libprotobuf.lib')
compiler.linkflags.insert(0, lib_path)
if sdk.name in ['csgo', 'blade']:
@@ -115,53 +118,18 @@ for sdk_name in SM.sdks:
'vprof_tool.cpp',
]
pb_sources = []
if sdk.name == 'csgo':
pb_sources = [
binary.sources += [
os.path.join(sdk.path, 'public', 'engine', 'protobuf', 'netmessages.pb.cc'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf', 'cstrike15_usermessages.pb.cc'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf', 'cstrike15_usermessage_helpers.cpp'),
]
elif sdk.name == 'blade':
pb_sources = [
binary.sources += [
os.path.join(sdk.path, 'public', 'engine', 'protobuf', 'netmessages.pb.cc'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'berimbau', 'protobuf', 'berimbau_usermessages.pb.cc'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'berimbau', 'protobuf', 'berimbau_usermessage_helpers.cpp'),
]
if len(pb_sources):
binary.sources += pb_sources
binary.compiler.cxxdefines += ['PROTOBUF_ENABLE']
if cxx.target.platform == 'mac' and sdk.name in ['csgo']:
# We need a proxy library since the game uses libstdc++.
pb_binary = SM.HL2Library(builder, cxx, 'pbproxy.' + sdk.ext, sdk)
pb_binary.sources += pb_sources
pb_binary.sources += ['pb_proxy.cpp']
pb_binary.compiler.cxxincludes += pb_includes
# Switch from libc++ to libstdc++.
pb_binary.compiler.cxxflags.remove('-stdlib=libc++')
pb_binary.compiler.linkflags.remove('-lc++')
pb_binary.compiler.linkflags.remove('-stdlib=libc++')
pb_binary.compiler.cxxflags.append('-stdlib=libstdc++')
pb_binary.compiler.linkflags.append('-lstdc++')
pb_binary.compiler.linkflags.append('-stdlib=libstdc++')
if '-std=c++1y' in pb_binary.compiler.cxxflags:
pb_binary.compiler.cxxflags.remove('-std=c++1y')
elif '-std=c++14' in pb_binary.compiler.cxxflags:
pb_binary.compiler.cxxflags.remove('-std=c++14')
if cxx.target.arch == 'x86':
pb_lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf.a')
elif cxx.target.arch == 'x86_64':
pb_lib_path = os.path.join(sdk.path, 'lib', 'osx64', 'release', 'libprotobuf.a')
pb_binary.compiler.linkflags.append(pb_lib_path)
SM.binaries += [builder.Add(pb_binary)]
binary.compiler.cxxdefines += [
'PROTOBUF_PROXY_ENABLE',
'PROTOBUF_PROXY_BINARY_NAME="pbproxy.{}"'.format(sdk.ext),
]
SM.binaries += builder.Add(project)
+1 -1
View File
@@ -115,7 +115,7 @@ void GameHooks::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPla
const char *cvarName, const char *cvarValue){
int client = IndexOfEdict(pPlayer);
# if SOURCE_ENGINE == SE_CSGO
# if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
if (g_Players.HandleConVarQuery(cookie, client, result, cvarName, cvarValue))
return;
# endif
+3 -2
View File
@@ -183,6 +183,7 @@ void CHalfLife2::InitLogicalEntData()
|| SOURCE_ENGINE == SE_CSS \
|| SOURCE_ENGINE == SE_SDK2013 \
|| SOURCE_ENGINE == SE_BMS \
|| SOURCE_ENGINE == SE_BLADE \
|| SOURCE_ENGINE == SE_NUCLEARDAWN
if (g_SMAPI->GetServerFactory(false)("VSERVERTOOLS003", nullptr))
@@ -1233,7 +1234,7 @@ bool IsWindowsReservedDeviceName(const char *pMapname)
}
#endif
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS && SOURCE_ENGINE != SE_MOCK
// This frees memory allocated by the game using the game's CRT on Windows,
// avoiding a crash due to heap corruption (issue #910).
template< class T, class I >
@@ -1384,7 +1385,7 @@ bool CHalfLife2::IsMapValid(const char *map)
if (!map || !map[0])
return false;
return FindMap(map) != SMFindMapResult::NotFound;
return FindMap(map) == SMFindMapResult::Found;
}
#if SOURCE_ENGINE < SE_ORANGEBOX
+1 -1
View File
@@ -235,7 +235,7 @@ public: //IGameHelpers
bool IsMapValid(const char *map);
SMFindMapResult FindMap(char *pMapName, size_t nMapNameMax);
SMFindMapResult FindMap(const char *pMapName, char *pFoundMap = NULL, size_t nMapNameMax = 0);
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS && SOURCE_ENGINE != SE_MOCK
void FreeUtlVectorUtlString(CUtlVector<CUtlString, CUtlMemoryGlobalMalloc<CUtlString>> &vec);
#endif
bool GetMapDisplayName(const char *pMapName, char *pDisplayname, size_t nMapNameMax);
+4
View File
@@ -503,6 +503,10 @@ void CRadioMenuPlayer::Radio_Refresh()
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
// TODO: find what happens past 240 on CS:GO
CCSUsrMsg_ShowMenu *msg = (CCSUsrMsg_ShowMenu *)g_UserMsgs.StartProtobufMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
if (!msg)
{
return;
}
msg->set_bits_valid_slots(display_keys);
msg->set_display_time(time);
msg->set_menu_string(ptr);
+102 -47
View File
@@ -196,6 +196,7 @@ void PlayerManager::OnSourceModAllInitialized()
m_clcommandkv_post = forwardsys->CreateForward("OnClientCommandKeyValues_Post", ET_Ignore, 2, NULL, Param_Cell, Param_Cell);
m_clinfochanged = forwardsys->CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2);
m_clauth = forwardsys->CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String);
m_cllang = forwardsys->CreateForward("OnClientLanguageChanged", ET_Ignore, 2, NULL, Param_Cell, Param_Cell);
m_onActivate = forwardsys->CreateForward("OnServerLoad", ET_Ignore, 0, NULL);
m_onActivate2 = forwardsys->CreateForward("OnMapStart", ET_Ignore, 0, NULL);
@@ -212,6 +213,9 @@ void PlayerManager::OnSourceModAllInitialized()
SH_ADD_HOOK(ConCommand, Dispatch, pCmd, SH_STATIC(CmdMaxplayersCallback), true);
maxplayersCmd = pCmd;
}
gameevents->AddListener(this, "player_connect", true);
gameevents->AddListener(this, "player_disconnect", true);
}
void PlayerManager::OnSourceModShutdown()
@@ -246,6 +250,7 @@ void PlayerManager::OnSourceModShutdown()
forwardsys->ReleaseForward(m_clcommandkv_post);
forwardsys->ReleaseForward(m_clinfochanged);
forwardsys->ReleaseForward(m_clauth);
forwardsys->ReleaseForward(m_cllang);
forwardsys->ReleaseForward(m_onActivate);
forwardsys->ReleaseForward(m_onActivate2);
@@ -259,6 +264,8 @@ void PlayerManager::OnSourceModShutdown()
{
SH_REMOVE_HOOK(ConCommand, Dispatch, maxplayersCmd, SH_STATIC(CmdMaxplayersCallback), true);
}
gameevents->RemoveListener(this);
}
ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key,
@@ -512,7 +519,7 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
/* Get the client's language */
if (m_QueryLang)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
pPlayer->m_LangId = translator->GetServerLanguage();
#else
const char *name;
@@ -520,6 +527,8 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
{
unsigned int langid;
pPlayer->m_LangId = (translator->GetLanguageByName(name, &langid)) ? langid : translator->GetServerLanguage();
OnClientLanguageChanged(client, pPlayer->m_LangId);
} else {
pPlayer->m_LangId = translator->GetServerLanguage();
}
@@ -666,6 +675,8 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
&& (m_SourceTVUserId == userId
#if SOURCE_ENGINE == SE_CSGO
|| strcmp(playername, "GOTV") == 0
#elif SOURCE_ENGINE == SE_BLADE
|| strcmp(playername, "BBTV") == 0
#elif (SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013 \
|| SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_NUCLEARDAWN || SOURCE_ENGINE == SE_LEFT4DEAD2)
|| (tv_name && strcmp(playername, tv_name->GetString()) == 0) || (tv_name && tv_name->GetString()[0] == 0 && strcmp(playername, "unnamed") == 0)
@@ -721,7 +732,7 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
}
pPlayer->Authorize_Post();
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
else if(m_QueryLang)
{
// Not a bot
@@ -781,7 +792,7 @@ void PlayerManager::OnServerHibernationUpdate(bool bHibernating)
CPlayer *pPlayer = &m_Players[i];
if (pPlayer->IsConnected() && pPlayer->IsFakeClient())
{
#if SOURCE_ENGINE < SE_LEFT4DEAD || SOURCE_ENGINE >= SE_CSGO || SOURCE_ENGINE == SE_NUCLEARDAWN
#if SOURCE_ENGINE < SE_LEFT4DEAD || SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_NUCLEARDAWN
// These games have the bug fixed where hltv/replay was getting kicked on hibernation
if (pPlayer->IsSourceTV() || pPlayer->IsReplay())
continue;
@@ -1337,65 +1348,69 @@ void PlayerManager::OnClientSettingsChanged(edict_t *pEntity)
m_clinfochanged->PushCell(client);
m_clinfochanged->Execute(&res, NULL);
if (pPlayer->IsFakeClient())
{
return;
}
IPlayerInfo *info = pPlayer->GetPlayerInfo();
const char *new_name = info ? info->GetName() : engine->GetClientConVarValue(client, "name");
const char *old_name = pPlayer->m_Name.c_str();
#if SOURCE_ENGINE >= SE_LEFT4DEAD
const char *networkid_force;
if ((networkid_force = engine->GetClientConVarValue(client, "networkid_force")) && networkid_force[0] != '\0')
{
unsigned int accountId = pPlayer->GetSteamAccountID();
logger->LogMessage("\"%s<%d><STEAM_1:%d:%d><>\" has bad networkid (id \"%s\") (ip \"%s\")",
new_name, pPlayer->GetUserId(), accountId & 1, accountId >> 1, networkid_force, pPlayer->GetIPAddress());
pPlayer->Kick("NetworkID spoofing detected.");
RETURN_META(MRES_IGNORED);
}
#endif
if (strcmp(old_name, new_name) != 0)
{
AdminId id = adminsys->FindAdminByIdentity("name", new_name);
if (id != INVALID_ADMIN_ID && pPlayer->GetAdminId() != id)
if (!pPlayer->IsFakeClient())
{
if (!CheckSetAdminName(client, pPlayer, id))
AdminId id = adminsys->FindAdminByIdentity("name", new_name);
if (id != INVALID_ADMIN_ID && pPlayer->GetAdminId() != id)
{
char kickMsg[128];
logicore.CoreTranslate(kickMsg, sizeof(kickMsg), "%T", 2, NULL, "Name Reserved", &client);
pPlayer->Kick(kickMsg);
RETURN_META(MRES_IGNORED);
if (!CheckSetAdminName(client, pPlayer, id))
{
char kickMsg[128];
logicore.CoreTranslate(kickMsg, sizeof(kickMsg), "%T", 2, NULL, "Name Reserved", &client);
pPlayer->Kick(kickMsg);
RETURN_META(MRES_IGNORED);
}
}
} else if ((id = adminsys->FindAdminByIdentity("name", old_name)) != INVALID_ADMIN_ID) {
if (id == pPlayer->GetAdminId())
{
/* This player is changing their name; force them to drop admin privileges! */
pPlayer->SetAdminId(INVALID_ADMIN_ID, false);
else if ((id = adminsys->FindAdminByIdentity("name", old_name)) != INVALID_ADMIN_ID) {
if (id == pPlayer->GetAdminId())
{
/* This player is changing their name; force them to drop admin privileges! */
pPlayer->SetAdminId(INVALID_ADMIN_ID, false);
}
}
}
pPlayer->SetName(new_name);
}
if (m_PassInfoVar.size() > 0)
if (!pPlayer->IsFakeClient())
{
/* Try for a password change */
const char *old_pass = pPlayer->m_LastPassword.c_str();
const char *new_pass = engine->GetClientConVarValue(client, m_PassInfoVar.c_str());
if (strcmp(old_pass, new_pass) != 0)
if (m_PassInfoVar.size() > 0)
{
pPlayer->m_LastPassword.assign(new_pass);
if (pPlayer->IsInGame() && pPlayer->IsAuthorized())
/* Try for a password change */
const char* old_pass = pPlayer->m_LastPassword.c_str();
const char* new_pass = engine->GetClientConVarValue(client, m_PassInfoVar.c_str());
if (strcmp(old_pass, new_pass) != 0)
{
/* If there is already an admin id assigned, this will just bail out. */
pPlayer->DoBasicAdminChecks();
pPlayer->m_LastPassword.assign(new_pass);
if (pPlayer->IsInGame() && pPlayer->IsAuthorized())
{
/* If there is already an admin id assigned, this will just bail out. */
pPlayer->DoBasicAdminChecks();
}
}
}
#if SOURCE_ENGINE >= SE_LEFT4DEAD
const char* networkid_force;
if ((networkid_force = engine->GetClientConVarValue(client, "networkid_force")) && networkid_force[0] != '\0')
{
unsigned int accountId = pPlayer->GetSteamAccountID();
logger->LogMessage("\"%s<%d><STEAM_1:%d:%d><>\" has bad networkid (id \"%s\") (ip \"%s\")",
new_name, pPlayer->GetUserId(), accountId & 1, accountId >> 1, networkid_force, pPlayer->GetIPAddress());
pPlayer->Kick("NetworkID spoofing detected.");
RETURN_META(MRES_IGNORED);
}
#endif
}
/* Notify Extensions */
List<IClientListener *>::iterator iter;
IClientListener *pListener = NULL;
@@ -1409,6 +1424,13 @@ void PlayerManager::OnClientSettingsChanged(edict_t *pEntity)
}
}
void PlayerManager::OnClientLanguageChanged(int client, unsigned int language)
{
m_cllang->PushCell(client);
m_cllang->PushCell(language);
m_cllang->Execute(NULL);
}
int PlayerManager::GetMaxClients()
{
return m_maxClients;
@@ -1428,6 +1450,11 @@ int PlayerManager::GetNumPlayers()
return m_PlayerCount;
}
int PlayerManager::GetNumClients()
{
return m_ClientCount;
}
int PlayerManager::GetClientOfUserId(int userid)
{
if (userid < 0 || userid > USHRT_MAX)
@@ -2008,7 +2035,7 @@ void CmdMaxplayersCallback()
g_Players.MaxPlayersChanged();
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
{
for (int i = 1; i <= m_maxClients; i++)
@@ -2016,7 +2043,9 @@ bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQue
if (m_Players[i].m_LanguageCookie == cookie)
{
unsigned int langid;
m_Players[i].m_LangId = (translator->GetLanguageByName(cvarValue, &langid)) ? langid : translator->GetServerLanguage();
unsigned int new_langid = (translator->GetLanguageByName(cvarValue, &langid)) ? langid : translator->GetServerLanguage();
m_Players[i].m_LangId = new_langid;
OnClientLanguageChanged(i, new_langid);
return true;
}
@@ -2026,6 +2055,27 @@ bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQue
}
#endif
/* IGameEventListener2::FireGameEvent */
void PlayerManager::FireGameEvent(IGameEvent *pEvent)
{
const char *name = pEvent->GetName();
if (strcmp(name, "player_connect") == 0)
{
const int client = pEvent->GetInt("index") + 1;
const int userid = pEvent->GetInt("userid");
m_ClientCount++;
}
else if (strcmp(name, "player_disconnect") == 0)
{
const int userid = pEvent->GetInt("userid");
const int client = m_UserIdLookUp[userid];
m_ClientCount--;
}
}
/*******************
*** PLAYER CODE ***
@@ -2064,7 +2114,8 @@ void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity)
|| SOURCE_ENGINE == SE_HL2DM \
|| SOURCE_ENGINE == SE_BMS \
|| SOURCE_ENGINE == SE_INSURGENCY \
|| SOURCE_ENGINE == SE_DOI
|| SOURCE_ENGINE == SE_DOI \
|| SOURCE_ENGINE == SE_BLADE
m_pIClient = engine->GetIServer()->GetClient(m_iIndex - 1);
#else
#if SOURCE_ENGINE == SE_SDK2013
@@ -2240,7 +2291,7 @@ void CPlayer::Disconnect()
m_bIsSourceTV = false;
m_bIsReplay = false;
m_Serial.value = -1;
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
m_LanguageCookie = InvalidQueryCvarCookie;
#endif
ClearNetchannelQueue();
@@ -2611,7 +2662,11 @@ unsigned int CPlayer::GetLanguageId()
void CPlayer::SetLanguageId(unsigned int id)
{
m_LangId = id;
if(m_LangId != id)
{
m_LangId = id;
g_Players.OnClientLanguageChanged(m_iIndex, id);
}
}
int CPlayer::GetUserId()
+13 -3
View File
@@ -151,7 +151,7 @@ private:
bool m_bIsReplay = false;
serial_t m_Serial;
CSteamID m_SteamId;
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
QueryCvarCookie_t m_LanguageCookie = InvalidQueryCvarCookie;
#endif
std::deque<std::string> m_PrintfBuffer;
@@ -159,7 +159,8 @@ private:
class PlayerManager :
public SMGlobalClass,
public IPlayerManager
public IPlayerManager,
public IGameEventListener2
{
friend class CPlayer;
public:
@@ -192,6 +193,7 @@ public:
#endif
void OnClientSettingsChanged(edict_t *pEntity);
//void OnClientSettingsChanged_Pre(edict_t *pEntity);
void OnClientLanguageChanged(int client, unsigned int language);
void OnServerHibernationUpdate(bool bHibernating);
void OnClientPrintf(edict_t *pEdict, const char *szMsg);
void OnPrintfFrameAction(unsigned int serial);
@@ -202,6 +204,7 @@ public: //IPlayerManager
IGamePlayer *GetGamePlayer(edict_t *pEdict);
int GetMaxClients();
int GetNumPlayers();
int GetNumClients();
int GetClientOfUserId(int userid);
bool IsServerActivated();
int FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags);
@@ -212,6 +215,11 @@ public: //IPlayerManager
int GetClientFromSerial(unsigned int serial);
void ClearAdminId(AdminId id);
void RecheckAnyAdmins();
public: // IGameEventListener2
void FireGameEvent(IGameEvent *pEvent);
#if SOURCE_ENGINE >= SE_ALIENSWARM
virtual int GetEventDebugID( void ) { return 42; }
#endif
public:
inline int MaxClients()
{
@@ -235,7 +243,7 @@ public:
{
return m_bInCCKVHook;
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
bool HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue);
#endif
private:
@@ -253,6 +261,7 @@ private:
IForward *m_clcommandkv_post;
IForward *m_clinfochanged;
IForward *m_clauth;
IForward *m_cllang;
IForward *m_onActivate;
IForward *m_onActivate2;
CPlayer *m_Players;
@@ -272,6 +281,7 @@ private:
int m_SourceTVUserId;
int m_ReplayUserId;
bool m_bInCCKVHook;
int m_ClientCount;
private:
static const int NETMSG_TYPE_BITS = 5; // SVC_Print overhead for netmsg type
static const int SVC_Print_BufferSize = 2048 - 1; // -1 for terminating \0
+19 -28
View File
@@ -35,7 +35,6 @@
#include "frame_hooks.h"
#include "ConVarManager.h"
#include "logic_bridge.h"
#include <bridge/include/IProviderCallbacks.h>
#define TIMER_MIN_ACCURACY 0.1
@@ -171,8 +170,9 @@ void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec,
TimerSystem::TimerSystem()
{
m_pMapTimer = NULL;
m_bHasMapTickedYet = false;
m_bHasMapSimulatedYet = false;
m_fLastTickedTime = 0.0f;
OnSourceModLevelEnd();
}
TimerSystem::~TimerSystem()
@@ -213,28 +213,29 @@ void TimerSystem::OnSourceModLevelEnd()
{
m_bHasMapTickedYet = false;
m_bHasMapSimulatedYet = false;
m_bWasSimulating = false;
m_uFramesAhead = 0;
}
/* Think is called before gpGlobals is updated every frame, even if the server is hibernating */
void TimerSystem::Think(bool unused)
void TimerSystem::GameFrame(bool simulating)
{
m_uFramesAhead++;
bool simulating = m_bWasSimulating && m_uFramesAhead == 1;
if (m_bHasMapTickedYet) {
g_fUniversalTime += gpGlobals->realtime - m_fLastTickedTime;
} else {
if (simulating && m_bHasMapTickedYet)
{
g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime;
if (!m_bHasMapSimulatedYet)
{
m_bHasMapSimulatedYet = true;
MapTimeLeftChanged();
}
}
else
{
g_fUniversalTime += gpGlobals->interval_per_tick;
}
m_fLastTickedTime = gpGlobals->realtime;
m_fLastTickedTime = gpGlobals->curtime;
m_bHasMapTickedYet = true;
logicore.callbacks->OnThink(simulating);
if (g_fUniversalTime >= g_fTimerThink) {
if (g_fUniversalTime >= g_fTimerThink)
{
RunFrame();
g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY);
@@ -242,19 +243,9 @@ void TimerSystem::Think(bool unused)
RunFrameHooks(simulating);
m_pOnGameFrame->Execute();
}
/* GameFrame is called after gpGlobals is updated, and may not be called when the server is hibernating */
void TimerSystem::GameFrame(bool simulating)
{
m_bWasSimulating = simulating;
m_uFramesAhead = 0;
if (simulating && !m_bHasMapSimulatedYet)
if (m_pOnGameFrame->GetFunctionCount())
{
m_bHasMapSimulatedYet = true;
MapTimeLeftChanged();
m_pOnGameFrame->Execute(NULL);
}
}
-3
View File
@@ -82,7 +82,6 @@ public: //ITimerSystem
public:
void RunFrame();
void RemoveMapChangeTimers();
void Think(bool unused);
void GameFrame(bool simulating);
private:
List<ITimer *> m_SingleTimers;
@@ -93,8 +92,6 @@ private:
/* This is stuff for our manual ticking escapades. */
bool m_bHasMapTickedYet; /** Has the map ticked yet? */
bool m_bHasMapSimulatedYet; /** Has the map simulated yet? */
bool m_bWasSimulating; /** Was the last GameFrame simulating */
unsigned m_uFramesAhead; /** Number of frames Think is ahead of GameFrame */
float m_fLastTickedTime; /** Last time that the game currently gave
us while ticking.
*/
+42 -49
View File
@@ -42,10 +42,6 @@
UserMessages g_UserMsgs;
#ifdef USE_PROTOBUF_USERMESSAGES
const protobuf::Message *GetMessagePrototype(int msg_type);
#endif
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
SH_DECL_HOOK3_void(IVEngineServer, SendUserMessage, SH_NOATTRIB, 0, IRecipientFilter &, int, const protobuf::Message &);
#else
@@ -62,7 +58,7 @@ UserMessages::UserMessages()
: m_InterceptBuffer(m_pBase, 2500)
{
#else
: m_InterceptBuffer(nullptr)
: m_InterceptBuffer(NULL)
{
#endif
m_HookCount = 0;
@@ -237,7 +233,7 @@ google::protobuf::Message *UserMessages::StartProtobufMessage(int msg_id, const
#ifndef USE_PROTOBUF_USERMESSAGES
return NULL;
#else
PbHandle buffer;
protobuf::Message *buffer;
if (m_InExec || m_InHook)
{
@@ -266,9 +262,8 @@ google::protobuf::Message *UserMessages::StartProtobufMessage(int msg_id, const
if (m_CurFlags & USERMSG_BLOCKHOOKS)
{
// direct message creation, return buffer "from engine". keep track
m_FakeEngineBuffer =
PbHandle(GetMessagePrototype(msg_id)->New(), PbHandle::Owned, PbHandle::Local);
buffer = m_FakeEngineBuffer.AsUnowned();
m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
buffer = m_FakeEngineBuffer;
} else {
char messageName[32];
if (!GetMessageName(msg_id, messageName, sizeof(messageName)))
@@ -277,30 +272,27 @@ google::protobuf::Message *UserMessages::StartProtobufMessage(int msg_id, const
return NULL;
}
// The message returned here should always local.
auto msg = OnStartMessage_Pre(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, messageName);
protobuf::Message *msg = OnStartMessage_Pre(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, messageName);
switch (m_FakeMetaRes)
{
case MRES_IGNORED:
case MRES_HANDLED:
m_FakeEngineBuffer =
PbHandle(GetMessagePrototype(msg_id)->New(), PbHandle::Owned, PbHandle::Local);
buffer = m_FakeEngineBuffer.AsUnowned();
m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
buffer = m_FakeEngineBuffer;
break;
case MRES_OVERRIDE:
m_FakeEngineBuffer =
PbHandle(GetMessagePrototype(msg_id)->New(), PbHandle::Owned, PbHandle::Local);
m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
// fallthrough
case MRES_SUPERCEDE:
buffer = std::move(msg);
buffer = msg;
break;
}
OnStartMessage_Post(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, messageName);
}
assert(!buffer.is_owned());
return buffer.GetLocalMessage();
return buffer;
#endif // USE_PROTOBUF_USERMESSAGES
}
@@ -312,12 +304,11 @@ bool UserMessages::EndMessage()
}
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
PbHandle localBuffer = std::move(m_FakeEngineBuffer);
if (m_CurFlags & USERMSG_BLOCKHOOKS)
{
PbHandle priv = localBuffer.ToPrivate(m_CurId);
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId,
*priv.GetPrivateMessage());
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
delete m_FakeEngineBuffer;
m_FakeEngineBuffer = NULL;
} else {
OnMessageEnd_Pre();
@@ -326,12 +317,10 @@ bool UserMessages::EndMessage()
case MRES_IGNORED:
case MRES_HANDLED:
case MRES_OVERRIDE:
{
PbHandle priv = localBuffer.ToPrivate(m_CurId);
engine->SendUserMessage(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId,
*priv.GetPrivateMessage());
engine->SendUserMessage(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
delete m_FakeEngineBuffer;
m_FakeEngineBuffer = NULL;
break;
}
//case MRES_SUPERCEDE:
}
@@ -451,7 +440,7 @@ bool UserMessages::InternalHook(int msg_id, IBitBufUserMessageListener *pListene
}
#ifdef USE_PROTOBUF_USERMESSAGES
const protobuf::Message *GetMessagePrototype(int msg_type)
const protobuf::Message *UserMessages::GetMessagePrototype(int msg_type)
{
#if SOURCE_ENGINE == SE_CSGO
return g_Cstrike15UsermessageHelpers.GetPrototype(msg_type);
@@ -538,8 +527,7 @@ void UserMessages::OnSendUserMessage_Pre(IRecipientFilter &filter, int msg_type,
}
else
{
m_FakeEngineBuffer =
PbHandle(&const_cast<protobuf::Message &>(msg), PbHandle::Unowned, PbHandle::Private);
m_FakeEngineBuffer = &const_cast<protobuf::Message &>(msg);
}
OnStartMessage_Post(&filter, msg_type, pszName);
@@ -575,7 +563,7 @@ void UserMessages::OnSendUserMessage_Post(IRecipientFilter &filter, int msg_type
#endif
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
PbHandle UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
protobuf::Message *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
#else
@@ -589,7 +577,7 @@ bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_typ
|| (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
{
m_InHook = false;
UM_RETURN_META_VALUE(MRES_IGNORED, nullptr);
UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
}
m_CurId = msg_type;
@@ -600,20 +588,22 @@ bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_typ
if (!is_intercept_empty)
{
#ifdef USE_PROTOBUF_USERMESSAGES
m_InterceptBuffer =
PbHandle(GetMessagePrototype(msg_type)->New(), PbHandle::Owned, PbHandle::Local);
UM_RETURN_META_VALUE(MRES_SUPERCEDE, m_InterceptBuffer.AsUnowned());
if (m_InterceptBuffer)
delete m_InterceptBuffer;
m_InterceptBuffer = GetMessagePrototype(msg_type)->New();
UM_RETURN_META_VALUE(MRES_SUPERCEDE, m_InterceptBuffer);
#else
m_InterceptBuffer.Reset();
UM_RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
#endif
}
UM_RETURN_META_VALUE(MRES_IGNORED, nullptr);
UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
}
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
void* UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
protobuf::Message *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
#else
@@ -627,9 +617,9 @@ bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_ty
#ifdef USE_PROTOBUF_USERMESSAGES
if (m_FakeMetaRes == MRES_SUPERCEDE)
m_OrigBuffer = m_InterceptBuffer.AsUnowned();
m_OrigBuffer = m_InterceptBuffer;
else
m_OrigBuffer = m_FakeEngineBuffer.AsUnowned();
m_OrigBuffer = m_FakeEngineBuffer;
#else
m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
#endif
@@ -725,11 +715,8 @@ void UserMessages::OnMessageEnd_Pre()
{
pInfo = (*iter);
pInfo->IsHooked = true;
#ifdef USE_PROTOBUF_USERMESSAGES
PbHandle local = m_InterceptBuffer.ToLocal(m_CurId);
res = pInfo->Callback->InterceptUserMessage(m_CurId, local.GetLocalMessage(), m_CurRecFilter);
m_InterceptBuffer.CopyFrom(local);
res = pInfo->Callback->InterceptUserMessage(m_CurId, m_InterceptBuffer, m_CurRecFilter);
#else
res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
#endif
@@ -781,8 +768,7 @@ void UserMessages::OnMessageEnd_Pre()
if (!handled && intercepted)
{
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
PbHandle priv = m_InterceptBuffer.ToPrivate(m_CurId);
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *priv.GetPrivateMessage());
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *m_InterceptBuffer);
#else
bf_write *engine_bfw;
#if SOURCE_ENGINE >= SE_LEFT4DEAD
@@ -798,9 +784,12 @@ void UserMessages::OnMessageEnd_Pre()
{
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
PbHandle tmp_msg(GetMessagePrototype(m_CurId)->New(), PbHandle::Owned, PbHandle::Local);
tmp_msg.CopyFrom(m_OrigBuffer);
auto pTempMsg = tmp_msg.GetLocalMessage();
int size = m_OrigBuffer->ByteSize();
uint8 *data = (uint8 *)stackalloc(size);
m_OrigBuffer->SerializePartialToArray(data, size);
protobuf::Message *pTempMsg = GetMessagePrototype(m_CurId)->New();
pTempMsg->ParsePartialFromArray(data, size);
#else
bf_write *pTempMsg = m_OrigBuffer;
#endif
@@ -823,6 +812,10 @@ void UserMessages::OnMessageEnd_Pre()
pInfo->IsHooked = false;
iter++;
}
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
delete pTempMsg;
#endif
}
UM_RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
+6 -6
View File
@@ -52,7 +52,6 @@ using namespace SourceMod;
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include <netmessages.pb.h>
#include "pb_handle.h"
using namespace google;
#else
@@ -109,8 +108,8 @@ public:
#endif
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
PbHandle OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name);
void* OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name);
protobuf::Message *OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name);
protobuf::Message *OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name);
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
bf_write *OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name);
bf_write *OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name);
@@ -122,6 +121,7 @@ public:
void OnMessageEnd_Post();
private:
#ifdef USE_PROTOBUF_USERMESSAGES
const protobuf::Message *GetMessagePrototype(int msg_type);
bool InternalHook(int msg_id, IProtobufUserMessageListener *pListener, bool intercept, bool isNew);
bool InternalUnhook(int msg_id, IProtobufUserMessageListener *pListener, bool intercept, bool isNew);
#else
@@ -141,11 +141,11 @@ private:
bf_read m_ReadBuffer;
#else
// The engine used to provide this. Now we track it.
PbHandle m_OrigBuffer;
PbHandle m_FakeEngineBuffer;
protobuf::Message *m_OrigBuffer;
protobuf::Message *m_FakeEngineBuffer;
META_RES m_FakeMetaRes;
PbHandle m_InterceptBuffer;
protobuf::Message *m_InterceptBuffer;
#endif
size_t m_HookCount;
bool m_InHook;
+2 -2
View File
@@ -36,7 +36,7 @@
#if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_HOOK1_void(ICvar, UnregisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
SH_DECL_HOOK2_void(ICvar, RegisterConCommand, SH_NOATTRIB, 0, ConCommandBase *, bool);
#else
SH_DECL_HOOK1_void(ICvar, RegisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
@@ -82,7 +82,7 @@ public:
#endif
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
void LinkConCommandBase(ConCommandBase *pBase, bool unknown)
#else
void LinkConCommandBase(ConCommandBase *pBase)
+3
View File
@@ -84,6 +84,9 @@ for cxx in builder.targets:
'smn_halflife.cpp',
'FrameIterator.cpp',
'DatabaseConfBuilder.cpp',
'LumpManager.cpp',
'smn_entitylump.cpp',
'NativeInvoker.cpp',
]
if binary.compiler.target.arch == 'x86_64':
+17 -21
View File
@@ -128,8 +128,8 @@ void DBManager::OnHandleDestroy(HandleType_t type, void *object)
bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
{
ConfDbInfoList *list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> pInfo = list->GetDatabaseConf(name);
ConfDbInfoList &list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> pInfo = list.GetDatabaseConf(name);
if (!pInfo)
{
@@ -148,7 +148,7 @@ bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool
/* Try to assign a real driver pointer */
if (pInfo->info.driver[0] == '\0')
{
std::string defaultDriver = list->GetDefaultDriver();
std::string defaultDriver = list.GetDefaultDriver();
if (!m_pDefault && defaultDriver.length() > 0)
{
m_pDefault = FindOrLoadDriver(defaultDriver.c_str());
@@ -209,17 +209,14 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
}
}
ConfDbInfoList *list = m_Builder.GetConfigList();
for (size_t i = 0; i < list->size(); i++)
{
ke::RefPtr<ConfDbInfo> current = list->at(i);
if (current->realDriver == pDriver)
ConfDbInfoList &list = m_Builder.GetConfigList();
for (auto conf : list) {
if (conf->realDriver == pDriver)
{
current->realDriver = NULL;
conf->realDriver = NULL;
}
}
/* Someone unloaded the default driver? Silly.. */
if (pDriver == m_pDefault)
{
@@ -255,8 +252,8 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
IDBDriver *DBManager::GetDefaultDriver()
{
ConfDbInfoList *list = m_Builder.GetConfigList();
std::string defaultDriver = list->GetDefaultDriver();
ConfDbInfoList &list = m_Builder.GetConfigList();
std::string defaultDriver = list.GetDefaultDriver();
if (!m_pDefault && defaultDriver.length() > 0)
{
m_pDefault = FindOrLoadDriver(defaultDriver.c_str());
@@ -322,12 +319,12 @@ IDBDriver *DBManager::GetDriver(unsigned int index)
const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
{
ConfDbInfoList *list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> info = list->GetDatabaseConf(name);
ConfDbInfoList &list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> info = list.GetDatabaseConf(name);
if (!info)
{
// couldn't find requested conf, return default if exists
info = list->GetDefaultConfiguration();
info = list.GetDefaultConfiguration();
if (!info)
{
return NULL;
@@ -337,11 +334,10 @@ const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
return &info->info;
}
ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
ke::RefPtr<ConfDbInfo> DBManager::GetDatabaseConf(const char *name)
{
ConfDbInfoList *list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> info(list->GetDatabaseConf(name));
return info;
ConfDbInfoList &list = m_Builder.GetConfigList();
return list.GetDatabaseConf(name);
}
IDBDriver *DBManager::FindOrLoadDriver(const char *name)
@@ -582,8 +578,8 @@ void DBManager::OnPluginWillUnload(IPlugin *plugin)
std::string DBManager::GetDefaultDriverName()
{
ConfDbInfoList *list = m_Builder.GetConfigList();
return list->GetDefaultDriver();
ConfDbInfoList &list = m_Builder.GetConfigList();
return list.GetDefaultDriver();
}
void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
+1 -1
View File
@@ -71,7 +71,7 @@ public: //IDBManager
void AddDriver(IDBDriver *pDrivera);
void RemoveDriver(IDBDriver *pDriver);
const DatabaseInfo *FindDatabaseConf(const char *name);
ConfDbInfo *GetDatabaseConf(const char *name);
ke::RefPtr<ConfDbInfo> GetDatabaseConf(const char *name);
bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength);
unsigned int GetDriverCount();
IDBDriver *GetDriver(unsigned int index);
+8 -10
View File
@@ -36,8 +36,8 @@
#define DBPARSE_LEVEL_DATABASE 2
DatabaseConfBuilder::DatabaseConfBuilder()
: m_ParseList(nullptr),
m_InfoList(new ConfDbInfoList())
: m_ParseList(),
m_InfoList()
{
}
@@ -50,7 +50,7 @@ DatabaseConfBuilder::~DatabaseConfBuilder()
{
}
ConfDbInfoList *DatabaseConfBuilder::GetConfigList()
ConfDbInfoList &DatabaseConfBuilder::GetConfigList()
{
return m_InfoList;
}
@@ -75,7 +75,7 @@ void DatabaseConfBuilder::ReadSMC_ParseStart()
m_ParseLevel = 0;
m_ParseState = DBPARSE_LEVEL_NONE;
m_ParseList = new ConfDbInfoList();
m_ParseList.clear();
}
SMCResult DatabaseConfBuilder::ReadSMC_NewSection(const SMCStates *states, const char *name)
@@ -116,7 +116,7 @@ SMCResult DatabaseConfBuilder::ReadSMC_KeyValue(const SMCStates *states, const c
{
if (strcmp(key, "driver_default") == 0)
{
m_ParseList->SetDefaultDriver(value);
m_ParseList.SetDefaultDriver(value);
}
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
if (strcmp(key, "driver") == 0)
@@ -161,7 +161,7 @@ SMCResult DatabaseConfBuilder::ReadSMC_LeavingSection(const SMCStates *states)
/* Save it.. */
m_ParseCurrent->AddRef();
m_ParseList->push_back(m_ParseCurrent);
m_ParseList.push_back(m_ParseCurrent);
m_ParseCurrent = nullptr;
/* Go up one level */
@@ -176,9 +176,7 @@ SMCResult DatabaseConfBuilder::ReadSMC_LeavingSection(const SMCStates *states)
void DatabaseConfBuilder::ReadSMC_ParseEnd(bool halted, bool failed)
{
m_InfoList->ReleaseMembers();
delete m_InfoList;
m_InfoList.clear();
m_InfoList = m_ParseList;
m_ParseList = nullptr;
m_ParseList.clear();
}
+10 -14
View File
@@ -38,6 +38,7 @@
#include <am-vector.h>
#include <am-string.h>
#include <am-refcounting.h>
#include <am-refcounting-threadsafe.h>
class ConfDbInfo : public ke::RefcountedThreadsafe<ConfDbInfo>
@@ -56,7 +57,7 @@ public:
DatabaseInfo info;
};
class ConfDbInfoList : public std::vector<ConfDbInfo *>
class ConfDbInfoList : public std::vector<ke::RefPtr<ConfDbInfo>>
{
/* Allow internal usage of ConfDbInfoList */
friend class DBManager;
@@ -66,10 +67,10 @@ private:
return m_DefDriver;
}
ConfDbInfo *GetDatabaseConf(const char *name) {
ke::RefPtr<ConfDbInfo> GetDatabaseConf(const char *name) {
for (size_t i = 0; i < this->size(); i++)
{
ConfDbInfo *current = this->at(i);
ke::RefPtr<ConfDbInfo> current = this->at(i);
/* If we run into the default configuration, then we'll save it
* for the next call to GetDefaultConfiguration */
if (strcmp(current->name.c_str(), "default") == 0)
@@ -83,20 +84,15 @@ private:
}
return nullptr;
}
ConfDbInfo *GetDefaultConfiguration() {
ke::RefPtr<ConfDbInfo> GetDefaultConfiguration() {
return m_DefaultConfig;
}
void SetDefaultDriver(const char *input) {
m_DefDriver = std::string(input);
}
void ReleaseMembers() {
for (size_t i = 0; i < this->size(); i++) {
ConfDbInfo *current = this->at(i);
current->Release();
}
}
private:
ConfDbInfo *m_DefaultConfig;
ke::RefPtr<ConfDbInfo> m_DefaultConfig;
std::string m_DefDriver;
};
@@ -108,7 +104,7 @@ public:
~DatabaseConfBuilder();
void StartParse();
void SetPath(char* path);
ConfDbInfoList *GetConfigList();
ConfDbInfoList &GetConfigList();
public: //ITextListener_SMC
void ReadSMC_ParseStart();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
@@ -120,10 +116,10 @@ private:
unsigned int m_ParseLevel;
unsigned int m_ParseState;
ConfDbInfo *m_ParseCurrent;
ConfDbInfoList *m_ParseList;
ConfDbInfoList m_ParseList;
private:
std::string m_Filename;
ConfDbInfoList *m_InfoList;
ConfDbInfoList m_InfoList;
};
#endif //_INCLUDE_DATABASE_CONF_BUILDER_H_
+6 -6
View File
@@ -69,13 +69,13 @@ void DebugReport::GenerateErrorVA(IPluginContext *ctx, cell_t func_idx, int err,
ke::SafeVsprintf(buffer, sizeof(buffer), message, ap);
const char *plname = pluginsys->FindPluginByContext(ctx->GetContext())->GetFilename();
const char *error = g_pSourcePawn2->GetErrorString(err);
if (error)
{
g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
} else {
g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
if (err >= 0) {
const char *error = g_pSourcePawn2->GetErrorString(err);
if (error)
g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
else
g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
}
g_Logger.LogError("[SM] %s", buffer);
+2
View File
@@ -47,6 +47,8 @@ public: // IDebugListener
void ReportError(const IErrorReport &report, IFrameIterator &iter);
void OnDebugSpew(const char *msg, ...);
public:
// If err is -1, caller assumes the automatic reporting by SourcePawn is
// good enough, and only wants the supplemental logging provided here.
void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...);
void GenerateErrorVA(IPluginContext *ctx, cell_t func_idx, int err, const char *message, va_list ap);
void GenerateCodeError(IPluginContext *ctx, uint32_t code_addr, int err, const char *message, ...);
+2
View File
@@ -240,6 +240,8 @@ void CLocalExtension::Unload()
m_pLib->CloseLibrary();
m_pLib = NULL;
}
m_bFullyLoaded = false;
}
bool CRemoteExtension::Reload(char *error, size_t maxlength)
+8 -5
View File
@@ -85,15 +85,12 @@ static const char *g_pParseEngine = NULL;
#if defined PLATFORM_WINDOWS
#define PLATFORM_NAME "windows" PLATFORM_ARCH_SUFFIX
#define PLATFORM_SERVER_BINARY "server.dll"
#elif defined PLATFORM_LINUX
#define PLATFORM_NAME "linux" PLATFORM_ARCH_SUFFIX
#define PLATFORM_COMPAT_ALT "mac" PLATFORM_ARCH_SUFFIX /* Alternate platform name if game data is missing for primary one */
#define PLATFORM_SERVER_BINARY "server_i486.so"
#elif defined PLATFORM_APPLE
#define PLATFORM_NAME "mac" PLATFORM_ARCH_SUFFIX
#define PLATFORM_COMPAT_ALT "linux" PLATFORM_ARCH_SUFFIX
#define PLATFORM_SERVER_BINARY "server.dylib"
#endif
struct TempSigInfo
@@ -315,7 +312,10 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
FILE *fp;
char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
char binName[64];
bridge->FormatSourceBinaryName(name, binName, sizeof(binName));
g_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/%s", binName);
if ((fp = fopen(path, "rb")) == NULL)
{
ke::SafeSprintf(error, sizeof(error), "Could not open binary: %s", path);
@@ -810,7 +810,10 @@ public:
(!had_game && matched_engine) ||
(matched_engine && matched_game))
{
fileList->push_back(cur_file);
if (fileList->find(cur_file) == fileList->end())
{
fileList->push_back(cur_file);
}
}
state = MSTATE_MAIN;
}
+32 -5
View File
@@ -47,6 +47,8 @@
#include "sm_invalidparamhandler.h"
#endif
using namespace std::string_literals;
HandleSystem g_HandleSys;
QHandle *ignore_handle;
@@ -299,6 +301,28 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
}
}
if (owner)
{
owner->num_handles++;
if (!owner->warned_handle_usage && owner->num_handles >= HANDLESYS_WARN_USAGE)
{
owner->warned_handle_usage = true;
std::string path = "<unknown>";
if (auto plugin = scripts->FindPluginByIdentity(owner))
{
path = "plugin "s + plugin->GetFilename();
}
else if (auto ext = g_Extensions.GetExtensionFromIdent(owner))
{
path = "extension "s + ext->GetFilename();
}
logger->LogError("[SM] Warning: %s is using more than %d handles!",
path.c_str(), HANDLESYS_WARN_USAGE);
}
}
QHandle *pHandle = &m_Handles[handle];
assert(pHandle->set == false);
@@ -320,7 +344,7 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
/* Create the hash value */
Handle_t hash = pHandle->serial;
hash <<= 16;
hash <<= HANDLESYS_HANDLE_BITS;
hash |= handle;
/* Add a reference count to the type */
@@ -484,7 +508,7 @@ HandleError HandleSystem::GetHandle(Handle_t handle,
unsigned int *in_index,
bool ignoreFree)
{
unsigned int serial = (handle >> 16);
unsigned int serial = (handle >> HANDLESYS_HANDLE_BITS);
unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
if (index == 0 || index > m_HandleTail || index > HANDLESYS_MAX_HANDLES)
@@ -640,7 +664,7 @@ Handle_t HandleSystem::FastCloneHandle(Handle_t hndl)
void HandleSystem::GetHandleUnchecked(Handle_t hndl, QHandle *& pHandle, unsigned int &index)
{
#ifndef NDEBUG
unsigned int serial = (hndl >> 16);
unsigned int serial = (hndl >> HANDLESYS_HANDLE_BITS);
#endif
index = (hndl & HANDLESYS_HANDLE_MASK);
@@ -664,6 +688,9 @@ HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
QHandleType *pType = &m_Types[pHandle->type];
if (pHandle->owner && pHandle->owner->num_handles > 0)
pHandle->owner->num_handles--;
if (pHandle->clone)
{
/* If we're a clone, decrease the parent reference count */
@@ -1138,12 +1165,12 @@ void HandleSystem::Dump(const HandleReporter &fn)
continue;
}
/* Get the index */
unsigned int index = (m_Handles[i].serial << 16) | i;
unsigned int index = (m_Handles[i].serial << HANDLESYS_HANDLE_BITS) | i;
/* Determine the owner */
const char *owner = "UNKNOWN";
if (m_Handles[i].owner)
{
IdentityToken_t *pOwner = m_Handles[i].owner;
IdentityToken_t *pOwner = m_Handles[i].owner;
if (pOwner == g_pCoreIdent)
{
owner = "CORE";
+7 -4
View File
@@ -42,14 +42,17 @@
#include <sm_namehashset.h>
#include "common_logic.h"
#define HANDLESYS_MAX_HANDLES (1<<15)
#define HANDLESYS_HANDLE_BITS 20
#define HANDLESYS_MAX_HANDLES ((1 << HANDLESYS_HANDLE_BITS) - 1)
#define HANDLESYS_MAX_TYPES (1<<9)
#define HANDLESYS_MAX_SUBTYPES 0xF
#define HANDLESYS_SUBTYPE_MASK 0xF
#define HANDLESYS_TYPEARRAY_SIZE (HANDLESYS_MAX_TYPES * (HANDLESYS_MAX_SUBTYPES + 1))
#define HANDLESYS_MAX_SERIALS 0xFFFF
#define HANDLESYS_SERIAL_MASK 0xFFFF0000
#define HANDLESYS_HANDLE_MASK 0x0000FFFF
#define HANDLESYS_SERIAL_BITS (32 - HANDLESYS_HANDLE_BITS)
#define HANDLESYS_MAX_SERIALS (1 << HANDLESYS_SERIAL_BITS)
#define HANDLESYS_SERIAL_MASK (((1 << HANDLESYS_SERIAL_BITS) - 1) << HANDLESYS_HANDLE_BITS)
#define HANDLESYS_HANDLE_MASK ((1 << HANDLESYS_HANDLE_BITS) - 1)
#define HANDLESYS_WARN_USAGE 100000
#define HANDLESYS_MEMUSAGE_MIN_VERSION 3
+131
View File
@@ -0,0 +1,131 @@
/**
* vim: set ts=4 :
* =============================================================================
* Entity Lump Manager
* Copyright (C) 2021-2022 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "LumpManager.h"
#include <iomanip>
#include <sstream>
EntityLumpParseResult::operator bool() const {
return m_Status == Status_OK;
}
EntityLumpParseResult EntityLumpManager::Parse(const char* pMapEntities) {
m_Entities.clear();
std::istringstream mapEntities(pMapEntities);
for (;;) {
std::string token;
mapEntities >> std::ws >> token >> std::ws;
// Assert that we're at the start of a new block, otherwise we're done parsing
if (token != "{") {
if (token == "\0") {
break;
} else {
return EntityLumpParseResult {
Status_UnexpectedChar, mapEntities.tellg()
};
}
}
/**
* Parse key / value pairs until we reach a closing brace. We currently assume there
* are only quoted keys / values up to the next closing brace.
*
* The SDK suggests that there are cases that could use non-quoted symbols and nested
* braces (`shared/mapentities_shared.cpp::MapEntity_ParseToken`), but I haven't seen
* those in practice.
*/
EntityLumpEntry entry;
while (mapEntities.peek() != '}') {
std::string key, value;
if (mapEntities.peek() != '"') {
return EntityLumpParseResult {
Status_UnexpectedChar, mapEntities.tellg()
};
}
mapEntities >> quoted(key) >> std::ws;
if (mapEntities.peek() != '"') {
return EntityLumpParseResult {
Status_UnexpectedChar, mapEntities.tellg()
};
}
mapEntities >> quoted(value) >> std::ws;
entry.emplace_back(key, value);
}
mapEntities.get();
m_Entities.push_back(std::make_shared<EntityLumpEntry>(entry));
}
return EntityLumpParseResult{};
}
std::string EntityLumpManager::Dump() {
std::ostringstream stream;
for (const auto& entry : m_Entities) {
// ignore empty entries
if (entry->empty()) {
continue;
}
stream << "{\n";
for (const auto& pair : *entry) {
stream << '"' << pair.first << "\" \"" << pair.second << '"' << '\n';
}
stream << "}\n";
}
return stream.str();
}
std::weak_ptr<EntityLumpEntry> EntityLumpManager::Get(size_t index) {
return m_Entities[index];
}
void EntityLumpManager::Erase(size_t index) {
m_Entities.erase(m_Entities.begin() + index);
}
void EntityLumpManager::Insert(size_t index) {
m_Entities.emplace(m_Entities.begin() + index, std::make_shared<EntityLumpEntry>());
}
size_t EntityLumpManager::Append() {
auto it = m_Entities.emplace(m_Entities.end(), std::make_shared<EntityLumpEntry>());
return std::distance(m_Entities.begin(), it);
}
size_t EntityLumpManager::Length() {
return m_Entities.size();
}
+116
View File
@@ -0,0 +1,116 @@
/**
* vim: set ts=4 :
* =============================================================================
* Entity Lump Manager
* Copyright (C) 2021-2022 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_LUMPMANAGER_H_
#define _INCLUDE_LUMPMANAGER_H_
#include <vector>
#include <memory>
#include <string>
/**
* Entity lump manager. Provides a list that stores a list of key / value pairs and the
* functionality to (de)serialize it from / to an entity string.
* This file and its corresponding .cpp should be compilable independently of SourceMod;
* the SourceMod interop is located within smn_entitylump.
*
* @file lumpmanager.h
* @brief Class definition for object that parses lumps.
*/
/**
* @brief A container of key / value pairs.
*/
using EntityLumpEntry = std::vector<std::pair<std::string, std::string>>;
enum EntityLumpParseStatus {
Status_OK,
Status_UnexpectedChar,
};
/**
* @brief Result of parsing an entity lump. On a parse error, m_Status is not Status_OK and
* m_Position indicates the offset within the string that caused the parse error.
*/
struct EntityLumpParseResult {
EntityLumpParseStatus m_Status;
std::streamoff m_Position;
operator bool() const;
const char* Description() const;
};
/**
* @brief Manages entity lump entries.
*/
class EntityLumpManager
{
public:
/**
* @brief Parses the map entities string into an internal representation.
*/
EntityLumpParseResult Parse(const char* pMapEntities);
/**
* @brief Dumps the current internal representation out to an std::string.
*/
std::string Dump();
/**
* @brief Returns a weak reference to an EntityLumpEntry. Used for handles on the scripting side.
*/
std::weak_ptr<EntityLumpEntry> Get(size_t index);
/**
* @brief Removes an EntityLumpEntry at the given index, shifting down all entries after it by one.
*/
void Erase(size_t index);
/**
* @brief Inserts a new EntityLumpEntry at the given index, shifting up the entries previously at the index and after it up by one.
*/
void Insert(size_t index);
/**
* @brief Adds a new EntityLumpEntry to the end. Returns the index of the entry.
*/
size_t Append();
/**
* @brief Returns the number of EntityLumpEntry items in the list.
*/
size_t Length();
private:
std::vector<std::shared_ptr<EntityLumpEntry>> m_Entities;
};
#endif // _INCLUDE_LUMPMANAGER_H_
+51 -28
View File
@@ -49,6 +49,7 @@ MemoryUtils g_MemUtils;
MemoryUtils::MemoryUtils()
{
m_InfoMap.init();
#ifdef PLATFORM_APPLE
task_dyld_info_data_t dyld_info;
@@ -77,20 +78,19 @@ void MemoryUtils::OnSourceModAllInitialized()
void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len)
{
DynLibInfo lib;
bool found;
char *ptr, *end;
const DynLibInfo* lib = nullptr;
memset(&lib, 0, sizeof(DynLibInfo));
if (!GetLibraryInfo(libPtr, lib))
if ((lib = GetLibraryInfo(libPtr)) == nullptr)
{
return NULL;
}
ptr = reinterpret_cast<char *>(lib.baseAddress);
end = ptr + lib.memorySize - len;
// Search in the original unaltered state of the binary.
char *start = lib->originalCopy.get();
char *ptr = start;
char *end = ptr + lib->memorySize - len;
bool found;
while (ptr < end)
{
found = true;
@@ -103,8 +103,9 @@ void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t l
}
}
// Translate the found offset into the actual live binary memory space.
if (found)
return ptr;
return reinterpret_cast<char *>(lib->baseAddress) + (ptr - start);
ptr++;
}
@@ -116,6 +117,8 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
{
#ifdef PLATFORM_WINDOWS
/* Add this this library into the cache */
GetLibraryInfo(handle);
return GetProcAddress((HMODULE)handle, symbol);
#elif defined PLATFORM_LINUX
@@ -162,6 +165,9 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
}
}
/* Add this this library into the cache */
GetLibraryInfo((void *)dlmap->l_addr);
/* If we don't have a symbol table for this library, then create one */
if (table == NULL)
{
@@ -325,6 +331,9 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
/* Uh oh, we couldn't find a matching handle */
return NULL;
}
/* Add this this library into the cache */
GetLibraryInfo((void *)dlbase);
/* See if we already have a symbol table for this library */
for (size_t i = 0; i < m_SymTables.size(); i++)
@@ -429,15 +438,17 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
#endif
}
bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
const DynLibInfo *MemoryUtils::GetLibraryInfo(const void *libPtr)
{
uintptr_t baseAddr;
if (libPtr == NULL)
{
return false;
return nullptr;
}
DynLibInfo lib;
#ifdef PLATFORM_WINDOWS
#ifdef PLATFORM_X86
@@ -456,7 +467,7 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
{
return false;
return nullptr;
}
baseAddr = reinterpret_cast<uintptr_t>(info.AllocationBase);
@@ -470,19 +481,19 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
/* Check PE magic and signature */
if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != PE_NT_OPTIONAL_HDR_MAGIC)
{
return false;
return nullptr;
}
/* Check architecture */
if (file->Machine != PE_FILE_MACHINE)
{
return false;
return nullptr;
}
/* For our purposes, this must be a dynamic library */
if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
{
return false;
return nullptr;
}
/* Finally, we can do this */
@@ -509,12 +520,12 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
if (!dladdr(libPtr, &info))
{
return false;
return nullptr;
}
if (!info.dli_fbase || !info.dli_fname)
{
return false;
return nullptr;
}
/* This is for our insane sanity checks :o */
@@ -524,31 +535,31 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
/* Check ELF magic */
if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
{
return false;
return nullptr;
}
/* Check ELF version */
if (file->e_ident[EI_VERSION] != EV_CURRENT)
{
return false;
return nullptr;
}
/* Check ELF endianness */
if (file->e_ident[EI_DATA] != ELFDATA2LSB)
{
return false;
return nullptr;
}
/* Check ELF architecture */
if (file->e_ident[EI_CLASS] != ELF_CLASS || file->e_machine != ELF_MACHINE)
{
return false;
return nullptr;
}
/* For our purposes, this must be a dynamic library/shared object */
if (file->e_type != ET_DYN)
{
return false;
return nullptr;
}
phdrCount = file->e_phnum;
@@ -598,12 +609,12 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
if (!dladdr(libPtr, &info))
{
return false;
return nullptr;
}
if (!info.dli_fbase || !info.dli_fname)
{
return false;
return nullptr;
}
/* This is for our insane sanity checks :o */
@@ -613,19 +624,19 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
/* Check Mach-O magic */
if (file->magic != MACH_MAGIC)
{
return false;
return nullptr;
}
/* Check architecture */
if (file->cputype != MACH_CPU_TYPE || file->cpusubtype != MACH_CPU_SUBTYPE)
{
return false;
return nullptr;
}
/* For our purposes, this must be a dynamic library */
if (file->filetype != MH_DYLIB)
{
return false;
return nullptr;
}
cmd_count = file->ncmds;
@@ -646,5 +657,17 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
lib.baseAddress = reinterpret_cast<void *>(baseAddr);
return true;
LibraryInfoMap::Insert i = m_InfoMap.findForAdd(lib.baseAddress);
if (i.found())
{
// We already loaded this binary before.
return &i->value;
}
// Keep a copy of the binary in its initial unpatched state for lookup.
lib.originalCopy = std::make_unique<char[]>(lib.memorySize);
memcpy(lib.originalCopy.get(), lib.baseAddress, lib.memorySize);
m_InfoMap.add(i, lib.baseAddress, std::move(lib));
return &i->value;
}
+6 -1
View File
@@ -32,6 +32,8 @@
#include "common_logic.h"
#include <IMemoryUtils.h>
#include <am-hashmap.h>
#include <memory>
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
#include <sh_vector.h>
#include "sm_symtable.h"
@@ -49,6 +51,7 @@ struct DynLibInfo
{
void *baseAddress;
size_t memorySize;
std::unique_ptr<char[]> originalCopy;
};
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
@@ -73,7 +76,7 @@ public: // IMemoryUtils
void *FindPattern(const void *libPtr, const char *pattern, size_t len);
void *ResolveSymbol(void *handle, const char *symbol);
public:
bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib);
const DynLibInfo *GetLibraryInfo(const void *libPtr);
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
private:
CVector<LibSymbolTable *> m_SymTables;
@@ -83,6 +86,8 @@ private:
SInt32 m_OSXMinor;
#endif
#endif
typedef ke::HashMap<void *, DynLibInfo, ke::PointerPolicy<void> > LibraryInfoMap;
LibraryInfoMap m_InfoMap;
};
extern MemoryUtils g_MemUtils;
+324
View File
@@ -0,0 +1,324 @@
// vim: set sts=2 ts=8 sw=2 tw=99 et:
//
// Copyright (C) 2006-2015 AlliedModders LLC
//
// This file is part of SourcePawn. SourcePawn is free software: you can
// redistribute it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// You should have received a copy of the GNU General Public License along with
// SourcePawn. If not, see http://www.gnu.org/licenses/.
//
#include <stdio.h>
#include <string.h>
#include "NativeInvoker.h"
/********************
* FUNCTION CALLING *
********************/
NativeInvoker::NativeInvoker(IPluginContext *pContext, const ke::RefPtr<Native> &native)
: context_(pContext),
m_curparam(0),
m_errorstate(SP_ERROR_NONE),
native_(native)
{
}
NativeInvoker::~NativeInvoker()
{
Cancel();
}
bool
NativeInvoker::IsRunnable()
{
return true;
}
IPluginContext *
NativeInvoker::GetParentContext()
{
return context_;
}
int NativeInvoker::PushCell(cell_t cell)
{
if (m_curparam >= SP_MAX_EXEC_PARAMS)
return SetError(SP_ERROR_PARAMS_MAX);
m_info[m_curparam].marked = false;
m_params[m_curparam] = cell;
m_curparam++;
return SP_ERROR_NONE;
}
int
NativeInvoker::PushCellByRef(cell_t *cell, int flags)
{
return PushArray(cell, 1, flags);
}
int
NativeInvoker::PushFloat(float number)
{
cell_t val = sp::FloatCellUnion(number).cell;
return PushCell(val);
}
int
NativeInvoker::PushFloatByRef(float *number, int flags)
{
return PushCellByRef((cell_t *)number, flags);
}
int
NativeInvoker::PushArray(cell_t *inarray, unsigned int cells, int copyback)
{
if (m_curparam >= SP_MAX_EXEC_PARAMS)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
ParamInfo *info = &m_info[m_curparam];
info->flags = inarray ? copyback : 0;
info->marked = true;
info->size = cells;
info->str.is_sz = false;
info->orig_addr = inarray;
m_curparam++;
return SP_ERROR_NONE;
}
int
NativeInvoker::PushString(const char *string)
{
return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
}
int
NativeInvoker::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
{
return _PushString(buffer, sz_flags, cp_flags, length);
}
int
NativeInvoker::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
{
if (m_curparam >= SP_MAX_EXEC_PARAMS)
return SetError(SP_ERROR_PARAMS_MAX);
ParamInfo *info = &m_info[m_curparam];
info->marked = true;
info->orig_addr = (cell_t *)string;
info->flags = cp_flags;
info->size = len;
info->str.sz_flags = sz_flags;
info->str.is_sz = true;
m_curparam++;
return SP_ERROR_NONE;
}
void
NativeInvoker::Cancel()
{
if (!m_curparam)
return;
m_errorstate = SP_ERROR_NONE;
m_curparam = 0;
}
int
NativeInvoker::Execute(cell_t *result, cell_t buffer, cell_t size)
{
context_->ClearLastNativeError();
// For backward compatibility, we have to clear the exception state.
// Otherwise code like this:
//
// static cell_t native(cx, params) {
// for (auto callback : callbacks) {
// callback->Execute();
// }
// }
//
// Could unintentionally leak a pending exception back to the caller,
// which wouldn't have happened before the Great Exception Refactoring.
SourcePawn::ExceptionHandler eh(context_);
eh.Debug(!size);
if (!Invoke(result)) {
if(size)
context_->StringToLocalUTF8(buffer, size, eh.Message(), NULL);
int Err = context_->GetLastNativeError();
context_->ClearLastNativeError();
return Err;
}
return SP_ERROR_NONE;
}
bool
NativeInvoker::Invoke(cell_t *result)
{
if (!IsRunnable()) {
Cancel();
context_->ReportErrorNumber(SP_ERROR_NOT_RUNNABLE);
return false;
}
if (int err = m_errorstate) {
Cancel();
context_->ReportErrorNumber(err);
return false;
}
//This is for re-entrancy!
cell_t _temp_params[SP_MAX_EXEC_PARAMS + 1];
cell_t *temp_params = &_temp_params[1];
ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
unsigned int numparams = m_curparam;
unsigned int i;
if (numparams)
{
//Save the info locally, then reset it for re-entrant calls.
memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
}
m_curparam = 0;
/* Initialize 0th parameter */
_temp_params[0] = numparams;
/* Browse the parameters and build arrays */
bool ok = true;
for (i=0; i<numparams; i++) {
/* Is this marked as an array? */
if (temp_info[i].marked) {
if (!temp_info[i].str.is_sz) {
/* Allocate a normal/generic array */
int err = context_->HeapAlloc(
temp_info[i].size,
&(temp_info[i].local_addr),
&(temp_info[i].phys_addr));
if (err != SP_ERROR_NONE) {
context_->ReportErrorNumber(err);
ok = false;
break;
}
if (temp_info[i].orig_addr)
{
memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
}
} else {
/* Calculate cells required for the string */
size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
/* Allocate the buffer */
int err = context_->HeapAlloc(
cells,
&(temp_info[i].local_addr),
&(temp_info[i].phys_addr));
if (err != SP_ERROR_NONE) {
context_->ReportErrorNumber(err);
ok = false;
break;
}
/* Copy original string if necessary */
if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
{
/* Cut off UTF-8 properly */
if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8) {
context_->StringToLocalUTF8(
temp_info[i].local_addr,
temp_info[i].size,
(const char *)temp_info[i].orig_addr,
NULL);
}
/* Copy a binary blob */
else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
{
memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
}
/* Copy ASCII characters */
else
{
context_->StringToLocal(
temp_info[i].local_addr,
temp_info[i].size,
(const char *)temp_info[i].orig_addr);
}
}
} /* End array/string calculation */
/* Update the pushed parameter with the byref local address */
temp_params[i] = temp_info[i].local_addr;
} else {
/* Just copy the value normally */
temp_params[i] = m_params[i];
}
}
/* Make the call if we can */
if (ok)
{
if(native_->native)
*result = native_->native->func(context_, _temp_params);
else
*result = native_->fake->wrapper->Invoke(context_, _temp_params);
}
/* i should be equal to the last valid parameter + 1 */
bool docopies = ok;
while (i--) {
if (!temp_info[i].marked)
continue;
if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK)) {
if (temp_info[i].orig_addr) {
if (temp_info[i].str.is_sz) {
memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
} else {
if (temp_info[i].size == 1) {
*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
} else {
memcpy(temp_info[i].orig_addr,
temp_info[i].phys_addr,
temp_info[i].size * sizeof(cell_t));
}
}
}
}
if (int err = context_->HeapPop(temp_info[i].local_addr))
context_->ReportErrorNumber(err);
}
return context_->GetLastNativeError() == SP_ERROR_NONE;
}
int
NativeInvoker::SetError(int err)
{
m_errorstate = err;
return err;
}
int NativeInvoker::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result) { return 0; }
funcid_t NativeInvoker::GetFunctionID() { return 0; }
int NativeInvoker::Execute2(IPluginContext *ctx, cell_t *result) { return 0; }
int NativeInvoker::CallFunction2(IPluginContext *ctx, const cell_t *params, unsigned int num_params, cell_t *result) { return 0; }
IPluginRuntime *NativeInvoker::GetParentRuntime() { return NULL; }
+78
View File
@@ -0,0 +1,78 @@
// vim: set sts=2 ts=8 sw=2 tw=99 et:
//
// Copyright (C) 2006-2015 AlliedModders LLC
//
// This file is part of SourcePawn. SourcePawn is free software: you can
// redistribute it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// You should have received a copy of the GNU General Public License along with
// SourcePawn. If not, see http://www.gnu.org/licenses/.
//
#ifndef _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
#define _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
#include <sp_vm_api.h>
#include <amtl/am-refcounting.h>
#include "Native.h"
struct ParamInfo
{
int flags; /* Copy-back flags */
bool marked; /* Whether this is marked as being used */
cell_t local_addr; /* Local address to free */
cell_t *phys_addr; /* Physical address of our copy */
cell_t *orig_addr; /* Original address to copy back to */
ucell_t size; /* Size of array in bytes */
struct {
bool is_sz; /* is a string */
int sz_flags; /* has sz flags */
} str;
};
class NativeInvoker : public IPluginFunction
{
public:
NativeInvoker(IPluginContext *pContext, const ke::RefPtr<Native> &native);
virtual ~NativeInvoker();
public:
int PushCell(cell_t cell);
int PushCellByRef(cell_t *cell, int flags);
int PushFloat(float number);
int PushFloatByRef(float *number, int flags);
int PushArray(cell_t *inarray, unsigned int cells, int copyback);
int PushString(const char *string);
int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
int Execute(cell_t *result, cell_t buffer=0, cell_t size=0);
void Cancel();
int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result);
IPluginContext *GetParentContext();
bool Invoke(cell_t *result);
bool IsRunnable();
funcid_t GetFunctionID();
int Execute2(IPluginContext *ctx, cell_t *result);
int CallFunction2(IPluginContext *ctx,
const cell_t *params,
unsigned int num_params,
cell_t *result);
IPluginRuntime *GetParentRuntime();
const char *DebugName() {
return native_->name();
}
private:
int _PushString(const char *string, int sz_flags, int cp_flags, size_t len);
int SetError(int err);
private:
IPluginContext *context_;
cell_t m_params[SP_MAX_EXEC_PARAMS];
ParamInfo m_info[SP_MAX_EXEC_PARAMS];
unsigned int m_curparam;
int m_errorstate;
ke::RefPtr<Native> native_;
};
#endif //_INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
+5 -3
View File
@@ -49,9 +49,11 @@ namespace SourceMod
{
struct IdentityToken_t
{
Handle_t ident;
void *ptr;
IdentityType_t type;
Handle_t ident = 0;
void *ptr = nullptr;
IdentityType_t type = 0;
size_t num_handles = 0;
bool warned_handle_usage = false;
};
};
+35
View File
@@ -56,6 +56,7 @@
#include "LibrarySys.h"
#include "RootConsoleMenu.h"
#include "CellArray.h"
#include "smn_entitylump.h"
#include <bridge/include/BridgeAPI.h>
#include <bridge/include/IProviderCallbacks.h>
@@ -89,6 +90,8 @@ CNativeOwner g_CoreNatives;
PseudoAddressManager pseudoAddr;
#endif
EntityLumpParseResult lastParseResult;
static void AddCorePhraseFile(const char *filename)
{
g_pCorePhrases->AddPhraseFile(filename);
@@ -135,6 +138,35 @@ static uint32_t ToPseudoAddress(void *addr)
#endif
}
static void SetEntityLumpWritable(bool writable)
{
g_bLumpAvailableForWriting = writable;
// write-lock causes the map entities to be serialized out to string
if (!writable)
{
g_strMapEntities = lumpmanager->Dump();
}
}
static bool ParseEntityLumpString(const char *pMapEntities, int &status, size_t &position)
{
lastParseResult = lumpmanager->Parse(pMapEntities);
status = static_cast<int>(lastParseResult.m_Status);
position = static_cast<size_t>(lastParseResult.m_Position);
return lastParseResult;
}
// returns nullptr if the original lump failed to parse
static const char* GetEntityLumpString()
{
if (!lastParseResult)
{
return nullptr;
}
return g_strMapEntities.c_str();
}
// Defined in smn_filesystem.cpp.
extern bool OnLogPrint(const char *msg);
@@ -170,6 +202,9 @@ static sm_logic_t logic =
CellArray::Free,
FromPseudoAddress,
ToPseudoAddress,
SetEntityLumpWritable,
ParseEntityLumpString,
GetEntityLumpString,
&g_PluginSys,
&g_ShareSys,
&g_Extensions,
+1 -1
View File
@@ -89,6 +89,6 @@ unsigned int UTIL_CRC32(const void *pdata, size_t data_length)
crc = CRCTable[c] ^ (crc >> 8);
}
return crc;
return ~crc;
}
+5 -1
View File
@@ -75,7 +75,11 @@ static cell_t CreateArray(IPluginContext *pContext, const cell_t *params)
if (params[2])
{
array->resize(params[2]);
if (!array->resize(params[2]))
{
delete array;
return pContext->ThrowNativeError("Failed to resize array to startsize \"%u\".", params[2]);
}
}
Handle_t hndl = handlesys->CreateHandle(htCellArray, array, pContext->GetIdentity(), g_pCoreIdent, NULL);
+17
View File
@@ -83,6 +83,22 @@ static cell_t CreateStack(IPluginContext *pContext, const cell_t *params)
return hndl;
}
static cell_t ClearStack(IPluginContext *pContext, const cell_t *params)
{
CellArray *array;
HandleError err;
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
if ((err = handlesys->ReadHandle(params[1], htCellStack, &sec, (void **)&array)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
}
array->clear();
return 1;
}
static cell_t CloneStack(IPluginContext *pContext, const cell_t *params)
{
CellArray *oldArray;
@@ -425,6 +441,7 @@ REGISTER_NATIVES(cellStackNatives)
// Transitional syntax support.
{"ArrayStack.ArrayStack", CreateStack},
{"ArrayStack.Clear", ClearStack},
{"ArrayStack.Clone", CloneStack},
{"ArrayStack.Pop", ArrayStack_Pop},
{"ArrayStack.PopString", ArrayStack_PopString},
+127 -2
View File
@@ -32,6 +32,7 @@
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <list>
#include "common_logic.h"
#include "Logger.h"
@@ -56,6 +57,7 @@
#include <bridge/include/CoreProvider.h>
#include <bridge/include/IScriptManager.h>
#include <bridge/include/IExtensionBridge.h>
#include <sh_vector.h>
using namespace SourceMod;
using namespace SourcePawn;
@@ -114,6 +116,71 @@ public:
}
} g_CoreNativeHelpers;
/**
* @brief Nearly identical to the standard core plugin iterator
* with one key difference. Next doesn't increment the counter
* the first time it is ran. This is a hack for the methodmap..
*/
class CMMPluginIterator
: public IPluginIterator,
public IPluginsListener
{
public:
CMMPluginIterator(const CVector<SMPlugin *> *list)
: m_hasStarted(false)
{
for(auto iter = list->begin(); iter != list->end(); ++iter) {
m_list.push_back(*iter);
}
scripts->FreePluginList(list);
m_current = m_list.begin();
scripts->AddPluginsListener(this);
}
virtual ~CMMPluginIterator()
{
scripts->RemovePluginsListener(this);
}
virtual bool MorePlugins() override
{
return (m_current != m_list.end());
}
virtual IPlugin *GetPlugin() override
{
return *m_current;
}
virtual void NextPlugin() override
{
if(!m_hasStarted)
{
m_hasStarted = true;
return;
}
m_current++;
}
virtual void Release() override
{
delete this;
}
public:
virtual void OnPluginDestroyed(IPlugin *plugin) override
{
if (*m_current == plugin)
m_current = m_list.erase(m_current);
else
m_list.remove(static_cast<SMPlugin *>(plugin));
}
private:
std::list<SMPlugin *> m_list;
std::list<SMPlugin *>::iterator m_current;
bool m_hasStarted;
};
void LogAction(Handle_t hndl, int type, int client, int target, const char *message)
{
if (g_OnLogAction->GetFunctionCount())
@@ -146,7 +213,7 @@ void LogAction(Handle_t hndl, int type, int client, int target, const char *mess
g_Logger.LogMessage("[%s] %s", logtag, message);
}
static cell_t ThrowError(IPluginContext *pContext, const cell_t *params)
static cell_t ThrowError(IPluginContext *pContext, const cell_t *params)
{
char buffer[512];
@@ -264,6 +331,60 @@ static cell_t ReadPlugin(IPluginContext *pContext, const cell_t *params)
return pPlugin->GetMyHandle();
}
static cell_t PluginIterator_Create(IPluginContext *pContext, const cell_t *params)
{
IPluginIterator *iter = new CMMPluginIterator(scripts->ListPlugins());
Handle_t hndl = handlesys->CreateHandle(g_PlIter, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
if (hndl == BAD_HANDLE)
{
iter->Release();
}
return hndl;
}
static cell_t PluginIterator_Next(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleSecurity sec{pContext->GetIdentity(), g_pCoreIdent};
HandleError err{};
IPluginIterator *pIter = nullptr;
if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
if(!pIter->MorePlugins())
return 0;
pIter->NextPlugin();
return 1;
}
static cell_t PluginIterator_Plugin_get(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleSecurity sec{pContext->GetIdentity(), g_pCoreIdent};
HandleError err{};
IPluginIterator *pIter = nullptr;
if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
IPlugin *pPlugin = pIter->GetPlugin();
if (!pPlugin)
{
return BAD_HANDLE;
}
return pPlugin->GetMyHandle();
}
IPlugin *GetPluginFromHandle(IPluginContext *pContext, Handle_t hndl)
{
if (hndl == BAD_HANDLE)
@@ -969,7 +1090,7 @@ REGISTER_NATIVES(coreNatives)
{"FormatTime", FormatTime},
{"GetPluginIterator", GetPluginIterator},
{"MorePlugins", MorePlugins},
{"ReadPlugin", ReadPlugin},
{"ReadPlugin", ReadPlugin},
{"GetPluginStatus", GetPluginStatus},
{"GetPluginFilename", GetPluginFilename},
{"IsPluginDebugging", IsPluginDebugging},
@@ -1000,5 +1121,9 @@ REGISTER_NATIVES(coreNatives)
{"FrameIterator.LineNumber.get", FrameIterator_LineNumber},
{"FrameIterator.GetFunctionName", FrameIterator_GetFunctionName},
{"FrameIterator.GetFilePath", FrameIterator_GetFilePath},
{"PluginIterator.PluginIterator", PluginIterator_Create},
{"PluginIterator.Next", PluginIterator_Next},
{"PluginIterator.Plugin.get", PluginIterator_Plugin_get},
{NULL, NULL},
};
+367
View File
@@ -0,0 +1,367 @@
/**
* vim: set ts=4 :
* =============================================================================
* Entity Lump Manager
* Copyright (C) 2021-2022 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "HandleSys.h"
#include "common_logic.h"
#include "LumpManager.h"
#include <algorithm>
HandleType_t g_EntityLumpEntryType;
std::string g_strMapEntities;
bool g_bLumpAvailableForWriting = false;
static EntityLumpManager s_LumpManager;
EntityLumpManager *lumpmanager = &s_LumpManager;
class LumpManagerNatives :
public IHandleTypeDispatch,
public SMGlobalClass
{
public: //SMGlobalClass
void OnSourceModAllInitialized()
{
g_EntityLumpEntryType = handlesys->CreateType("EntityLumpEntry", this, 0, NULL, NULL, g_pCoreIdent, NULL);
}
void OnSourceModShutdown()
{
handlesys->RemoveType(g_EntityLumpEntryType, g_pCoreIdent);
}
public: //IHandleTypeDispatch
void OnHandleDestroy(HandleType_t type, void* object)
{
if (type == g_EntityLumpEntryType)
{
delete reinterpret_cast<std::weak_ptr<EntityLumpEntry>*>(object);
}
}
};
static LumpManagerNatives s_LumpManagerNatives;
cell_t sm_LumpManagerGet(IPluginContext *pContext, const cell_t *params) {
int index = params[1];
if (index < 0 || index >= static_cast<int>(lumpmanager->Length())) {
return pContext->ThrowNativeError("Invalid index %d", index);
}
std::weak_ptr<EntityLumpEntry>* pReference = new std::weak_ptr<EntityLumpEntry>;
*pReference = lumpmanager->Get(index);
return handlesys->CreateHandle(g_EntityLumpEntryType, pReference,
pContext->GetIdentity(), g_pCoreIdent, NULL);
}
cell_t sm_LumpManagerErase(IPluginContext *pContext, const cell_t *params) {
if (!g_bLumpAvailableForWriting) {
return pContext->ThrowNativeError("Cannot use EntityLump.Erase() outside of OnMapInit");
}
int index = params[1];
if (index < 0 || index >= static_cast<int>(lumpmanager->Length())) {
return pContext->ThrowNativeError("Invalid index %d", index);
}
lumpmanager->Erase(index);
return 0;
}
cell_t sm_LumpManagerInsert(IPluginContext *pContext, const cell_t *params) {
if (!g_bLumpAvailableForWriting) {
return pContext->ThrowNativeError("Cannot use EntityLump.Insert() outside of OnMapInit");
}
int index = params[1];
if (index < 0 || index > static_cast<int>(lumpmanager->Length())) {
return pContext->ThrowNativeError("Invalid index %d", index);
}
lumpmanager->Insert(index);
return 0;
}
cell_t sm_LumpManagerAppend(IPluginContext *pContext, const cell_t *params) {
if (!g_bLumpAvailableForWriting) {
return pContext->ThrowNativeError("Cannot use EntityLump.Append() outside of OnMapInit");
}
return lumpmanager->Append();
}
cell_t sm_LumpManagerLength(IPluginContext *pContext, const cell_t *params) {
return lumpmanager->Length();
}
cell_t sm_LumpEntryGet(IPluginContext *pContext, const cell_t *params) {
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
HandleError err;
Handle_t hndl = static_cast<Handle_t>(params[1]);
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
}
if (entryref->expired()) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
}
auto entry = entryref->lock();
int index = params[2];
if (index < 0 || index >= static_cast<int>(entry->size())) {
return pContext->ThrowNativeError("Invalid index %d", index);
}
const auto& pair = (*entry)[index];
size_t nBytes;
pContext->StringToLocalUTF8(params[3], params[4], pair.first.c_str(), &nBytes);
pContext->StringToLocalUTF8(params[5], params[6], pair.second.c_str(), &nBytes);
return 0;
}
cell_t sm_LumpEntryUpdate(IPluginContext *pContext, const cell_t *params) {
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
HandleError err;
Handle_t hndl = static_cast<Handle_t>(params[1]);
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
}
if (entryref->expired()) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
}
if (!g_bLumpAvailableForWriting) {
return pContext->ThrowNativeError("Cannot use EntityLumpEntry.Update() outside of OnMapInit");
}
auto entry = entryref->lock();
int index = params[2];
if (index < 0 || index >= static_cast<int>(entry->size())) {
return pContext->ThrowNativeError("Invalid index %d", index);
}
char *key, *value;
pContext->LocalToStringNULL(params[3], &key);
pContext->LocalToStringNULL(params[4], &value);
auto& pair = (*entry)[index];
if (key != nullptr) {
pair.first = key;
}
if (value != nullptr) {
pair.second = value;
}
return 0;
}
cell_t sm_LumpEntryInsert(IPluginContext *pContext, const cell_t *params) {
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
HandleError err;
Handle_t hndl = static_cast<Handle_t>(params[1]);
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
}
if (entryref->expired()) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
}
if (!g_bLumpAvailableForWriting) {
return pContext->ThrowNativeError("Cannot use EntityLumpEntry.Insert() outside of OnMapInit");
}
auto entry = entryref->lock();
int index = params[2];
if (index < 0 || index > static_cast<int>(entry->size())) {
return pContext->ThrowNativeError("Invalid index %d", index);
}
char *key, *value;
pContext->LocalToString(params[3], &key);
pContext->LocalToString(params[4], &value);
entry->emplace(entry->begin() + index, key, value);
return 0;
}
cell_t sm_LumpEntryErase(IPluginContext *pContext, const cell_t *params) {
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
HandleError err;
Handle_t hndl = static_cast<Handle_t>(params[1]);
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
}
if (entryref->expired()) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
}
if (!g_bLumpAvailableForWriting) {
return pContext->ThrowNativeError("Cannot use EntityLumpEntry.Erase() outside of OnMapInit");
}
auto entry = entryref->lock();
int index = params[2];
if (index < 0 || index >= static_cast<int>(entry->size())) {
return pContext->ThrowNativeError("Invalid index %d", index);
}
entry->erase(entry->begin() + index);
return 0;
}
cell_t sm_LumpEntryAppend(IPluginContext *pContext, const cell_t *params) {
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
HandleError err;
Handle_t hndl = static_cast<Handle_t>(params[1]);
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
}
if (entryref->expired()) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
}
if (!g_bLumpAvailableForWriting) {
return pContext->ThrowNativeError("Cannot use EntityLumpEntry.Append() outside of OnMapInit");
}
auto entry = entryref->lock();
char *key, *value;
pContext->LocalToString(params[2], &key);
pContext->LocalToString(params[3], &value);
entry->emplace_back(key, value);
return 0;
}
cell_t sm_LumpEntryFindKey(IPluginContext *pContext, const cell_t *params) {
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
HandleError err;
Handle_t hndl = static_cast<Handle_t>(params[1]);
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
}
if (entryref->expired()) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
}
// start from the index after the current one
int start = params[3] + 1;
auto entry = entryref->lock();
if (start < 0 || start >= static_cast<int>(entry->size())) {
return -1;
}
char *key;
pContext->LocalToString(params[2], &key);
auto matches_key = [&key](std::pair<std::string,std::string> pair) {
return pair.first == key;
};
auto result = std::find_if(entry->begin() + start, entry->end(), matches_key);
if (result == entry->end()) {
return -1;
}
return std::distance(entry->begin(), result);
}
cell_t sm_LumpEntryLength(IPluginContext *pContext, const cell_t *params) {
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
HandleError err;
Handle_t hndl = static_cast<Handle_t>(params[1]);
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
}
if (entryref->expired()) {
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
}
auto entry = entryref->lock();
return entry->size();
}
REGISTER_NATIVES(entityLumpNatives)
{
{ "EntityLump.Get", sm_LumpManagerGet },
{ "EntityLump.Erase", sm_LumpManagerErase },
{ "EntityLump.Insert", sm_LumpManagerInsert },
{ "EntityLump.Append", sm_LumpManagerAppend },
{ "EntityLump.Length", sm_LumpManagerLength },
{ "EntityLumpEntry.Get", sm_LumpEntryGet },
{ "EntityLumpEntry.Update", sm_LumpEntryUpdate },
{ "EntityLumpEntry.Insert", sm_LumpEntryInsert },
{ "EntityLumpEntry.Erase", sm_LumpEntryErase },
{ "EntityLumpEntry.Append", sm_LumpEntryAppend },
{ "EntityLumpEntry.FindKey", sm_LumpEntryFindKey },
{ "EntityLumpEntry.Length.get", sm_LumpEntryLength },
{NULL, NULL}
};
+44
View File
@@ -0,0 +1,44 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2022-2022 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_ENTITYLUMP_H_
#define _INCLUDE_SOURCEMOD_ENTITYLUMP_H_
#include <IHandleSys.h>
#include "LumpManager.h"
using namespace SourceMod;
extern std::string g_strMapEntities;
extern bool g_bLumpAvailableForWriting;
extern EntityLumpManager *lumpmanager;
#endif // _INCLUDE_SOURCEMOD_ENTITYLUMP_H_
+1 -1
View File
@@ -414,7 +414,7 @@ static cell_t FormatNativeString(IPluginContext *pContext, const cell_t *params)
DetectExceptions eh(pContext);
written = atcprintf(output_buffer, maxlen, format_buffer, s_curcaller, s_curparams, &var_param);
if (eh.HasException())
return 0;
return pContext->GetLastNativeError();
}
cell_t *addr;
+62
View File
@@ -36,6 +36,8 @@
#include <IHandleSys.h>
#include <IForwardSys.h>
#include <ISourceMod.h>
#include "ShareSys.h"
#include "NativeInvoker.h"
HandleType_t g_GlobalFwdType = 0;
HandleType_t g_PrivateFwdType = 0;
@@ -44,6 +46,7 @@ static bool s_CallStarted = false;
static ICallable *s_pCallable = NULL;
static IPluginFunction *s_pFunction = NULL;
static IForward *s_pForward = NULL;
static NativeInvoker *s_pInvoker = NULL;
class ForwardNativeHelpers :
public SMGlobalClass,
@@ -103,6 +106,9 @@ inline void ResetCall()
s_pFunction = NULL;
s_pForward = NULL;
s_pCallable = NULL;
if(s_pInvoker)
delete s_pInvoker;
s_pInvoker = NULL;
}
static cell_t sm_GetFunctionByName(IPluginContext *pContext, const cell_t *params)
@@ -367,6 +373,27 @@ static cell_t sm_CallStartForward(IPluginContext *pContext, const cell_t *params
return 1;
}
static cell_t sm_CallStartNative(IPluginContext *pContext, const cell_t *params)
{
ResetCall();
char *name;
pContext->LocalToString(params[1], &name);
ke::RefPtr<Native> pNative = g_ShareSys.FindNative(name);
if (!pNative)
return 0;//pContext->ThrowNativeError("Invalid native \"%s\"", name);
s_pInvoker = new NativeInvoker(pContext, pNative);
s_pCallable = static_cast<ICallable *>(s_pInvoker);
s_CallStarted = true;
return 1;
}
static cell_t sm_CallPushCell(IPluginContext *pContext, const cell_t *params)
{
int err;
@@ -657,6 +684,39 @@ static cell_t sm_CallFinish(IPluginContext *pContext, const cell_t *params)
IForward *pForward = s_pForward;
ResetCall();
err = pForward->Execute(result, NULL);
} else if (s_pInvoker) {
err = s_pInvoker->Execute(result);
ResetCall();
}
return err;
}
static cell_t sm_CallFinishEx(IPluginContext *pContext, const cell_t *params)
{
int err = SP_ERROR_NOT_RUNNABLE;
cell_t *result;
if (!s_CallStarted)
{
return pContext->ThrowNativeError("Cannot finish call when there is no call in progress");
}
pContext->LocalToPhysAddr(params[1], &result);
// Note: Execute() swallows exceptions, so this is okay.
if (s_pFunction)
{
IPluginFunction *pFunction = s_pFunction;
ResetCall();
err = pFunction->Execute(result, params[2], params[3]);
} else if (s_pForward) {
IForward *pForward = s_pForward;
ResetCall();
err = pForward->Execute(result, NULL);
} else if (s_pInvoker) {
err = s_pInvoker->Execute(result, params[2], params[3]);
ResetCall();
}
return err;
@@ -743,6 +803,7 @@ REGISTER_NATIVES(functionNatives)
{"RemoveAllFromForward", sm_RemoveAllFromForward},
{"Call_StartFunction", sm_CallStartFunction},
{"Call_StartForward", sm_CallStartForward},
{"Call_StartNative", sm_CallStartNative},
{"Call_PushCell", sm_CallPushCell},
{"Call_PushCellRef", sm_CallPushCellRef},
{"Call_PushFloat", sm_CallPushFloat},
@@ -754,6 +815,7 @@ REGISTER_NATIVES(functionNatives)
{"Call_PushNullVector", sm_CallPushNullVector},
{"Call_PushNullString", sm_CallPushNullString},
{"Call_Finish", sm_CallFinish},
{"Call_FinishEx", sm_CallFinishEx},
{"Call_Cancel", sm_CallCancel},
{"RequestFrame", sm_AddFrameAction},
+4 -1
View File
@@ -592,7 +592,10 @@ private:
}
}
if (!libsys->FileTime(pMapList->path, FileTime_LastChange, &last_time)
char realpath[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", pMapList->path);
if (!libsys->FileTime(realpath, FileTime_LastChange, &last_time)
|| last_time > pMapList->last_modified_time)
{
/* Reparse */
+42 -94
View File
@@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@@ -141,10 +141,6 @@ public:
void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
bool OnSetHandlerOption(const char *option, const void *data);
#if 0
void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
#endif
private:
cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
private:
@@ -496,99 +492,51 @@ void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *
unsigned int winning_votes = results->item_list[0].count;
DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
} else {
IPluginContext *pContext = m_pVoteResults->GetParentContext();
bool no_call = false;
int err;
return;
}
/* First array */
cell_t client_array_address = -1;
cell_t *client_array_base = NULL;
cell_t client_array_size = results->num_clients + (results->num_clients * 2);
if (client_array_size)
{
if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
!= SP_ERROR_NONE)
{
g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
no_call = true;
} else {
cell_t target_offs = sizeof(cell_t) * results->num_clients;
cell_t *cur_index = client_array_base;
cell_t *cur_array;
for (unsigned int i=0; i<results->num_clients; i++)
{
/* Copy the array index */
*cur_index = target_offs;
/* Get the current array address */
cur_array = (cell_t *)((char *)cur_index + target_offs);
/* Store information */
cur_array[0] = results->client_list[i].client;
cur_array[1] = results->client_list[i].item;
/* Adjust for the new target by subtracting one indirection
* and adding one array.
*/
target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
cur_index++;
}
}
IPluginContext *pContext = m_pVoteResults->GetParentContext();
AutoEnterHeapScope heap_scope(pContext);
/* First array */
cell_t client_array_address = -1;
if (cell_t client_array_size = results->num_clients * 2) {
auto init = std::make_unique<cell_t[]>(client_array_size);
for (unsigned int i = 0; i < results->num_clients; i++) {
init[i * 2] = results->client_list[i].client;
init[i * 2 + 1] = results->client_list[i].item;
}
/* Second array */
cell_t item_array_address = -1;
cell_t *item_array_base = NULL;
cell_t item_array_size = results->num_items + (results->num_items * 2);
if (item_array_size)
{
if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
!= SP_ERROR_NONE)
{
g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
no_call = true;
} else {
cell_t target_offs = sizeof(cell_t) * results->num_items;
cell_t *cur_index = item_array_base;
cell_t *cur_array;
for (unsigned int i=0; i<results->num_items; i++)
{
/* Copy the array index */
*cur_index = target_offs;
/* Get the current array address */
cur_array = (cell_t *)((char *)cur_index + target_offs);
/* Store information */
cur_array[0] = results->item_list[i].item;
cur_array[1] = results->item_list[i].count;
/* Adjust for the new target by subtracting one indirection
* and adding one array.
*/
target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
cur_index++;
}
}
}
/* Finally, push everything */
if (!no_call)
{
m_pVoteResults->PushCell(menu->GetHandle());
m_pVoteResults->PushCell(results->num_votes);
m_pVoteResults->PushCell(results->num_clients);
m_pVoteResults->PushCell(client_array_address);
m_pVoteResults->PushCell(results->num_items);
m_pVoteResults->PushCell(item_array_address);
m_pVoteResults->Execute(NULL);
}
/* Free what we allocated, in reverse order as required */
if (item_array_address != -1)
{
pContext->HeapPop(item_array_address);
}
if (client_array_address != -1)
{
pContext->HeapPop(client_array_address);
if (!pContext->HeapAlloc2dArray(results->num_clients, 2, &client_array_address, init.get())) {
g_DbgReporter.GenerateError(pContext, m_fnVoteResult, -1,
"Menu callback could not allocate cells for client list.");
return;
}
}
/* Second array */
cell_t item_array_address = -1;
if (cell_t item_array_size = results->num_items * 2) {
auto init = std::make_unique<cell_t[]>(item_array_size);
for (unsigned int i = 0; i < results->num_items; i++) {
init[i * 2] = results->item_list[i].item;
init[i * 2 + 1] = results->item_list[i].count;
}
if (!pContext->HeapAlloc2dArray(results->num_items, 2, &item_array_address, init.get())) {
g_DbgReporter.GenerateError(pContext, m_fnVoteResult, -1,
"Menu callback could not allocate %d cells for item list.",
item_array_size);
return;
}
}
m_pVoteResults->PushCell(menu->GetHandle());
m_pVoteResults->PushCell(results->num_votes);
m_pVoteResults->PushCell(results->num_clients);
m_pVoteResults->PushCell(client_array_address);
m_pVoteResults->PushCell(results->num_items);
m_pVoteResults->PushCell(item_array_address);
m_pVoteResults->Execute(NULL);
}
bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
@@ -1014,7 +962,7 @@ static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *para
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK) ? 1 : 0;
}
static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
+29 -26
View File
@@ -270,18 +270,7 @@ static cell_t sm_GetClientCount(IPluginContext *pCtx, const cell_t *params)
return playerhelpers->GetNumPlayers();
}
int maxplayers = playerhelpers->GetMaxClients();
int count = 0;
for (int i = 1; i <= maxplayers; ++i)
{
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(i);
if ((pPlayer->IsConnected()) && !(pPlayer->IsInGame()))
{
count++;
}
}
return (playerhelpers->GetNumPlayers() + count);
return playerhelpers->GetNumClients();
}
static cell_t sm_GetMaxClients(IPluginContext *pCtx, const cell_t *params)
@@ -1471,24 +1460,23 @@ static cell_t IsClientInKickQueue(IPluginContext *pContext, const cell_t *params
return pPlayer->IsInKickQueue() ? 1 : 0;
}
cmd_target_info_t g_ProcessTargetString_info;
static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params)
{
cmd_target_info_t info;
pContext->LocalToString(params[1], (char **) &info.pattern);
info.admin = params[2];
pContext->LocalToPhysAddr(params[3], &info.targets);
info.max_targets = params[4];
info.flags = params[5];
pContext->LocalToString(params[6], &info.target_name);
info.target_name_maxlength = params[7];
pContext->LocalToString(params[1], (char **) &g_ProcessTargetString_info.pattern);
g_ProcessTargetString_info.admin = params[2];
pContext->LocalToPhysAddr(params[3], &g_ProcessTargetString_info.targets);
g_ProcessTargetString_info.max_targets = params[4];
g_ProcessTargetString_info.flags = params[5];
pContext->LocalToString(params[6], &g_ProcessTargetString_info.target_name);
g_ProcessTargetString_info.target_name_maxlength = params[7];
cell_t *tn_is_ml;
pContext->LocalToPhysAddr(params[8], &tn_is_ml);
playerhelpers->ProcessCommandTarget(&info);
playerhelpers->ProcessCommandTarget(&g_ProcessTargetString_info);
if (info.target_name_style == COMMAND_TARGETNAME_ML)
if (g_ProcessTargetString_info.target_name_style == COMMAND_TARGETNAME_ML)
{
*tn_is_ml = 1;
}
@@ -1497,16 +1485,30 @@ static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params
*tn_is_ml = 0;
}
if (info.num_targets == 0)
if (g_ProcessTargetString_info.num_targets == 0)
{
return info.reason;
return g_ProcessTargetString_info.reason;
}
else
{
return info.num_targets;
return g_ProcessTargetString_info.num_targets;
}
}
static cell_t GetLastProcessTargetString(IPluginContext *pContext, const cell_t *params)
{
cell_t *admin, *flags;
pContext->StringToLocalUTF8(params[1], params[2], g_ProcessTargetString_info.pattern, NULL);
pContext->LocalToPhysAddr(params[3], &admin);
pContext->LocalToPhysAddr(params[4], &flags);
*admin = g_ProcessTargetString_info.admin;
*flags = g_ProcessTargetString_info.flags;
return 0;
}
static cell_t FormatActivitySource(IPluginContext *pContext, const cell_t *params)
{
int value;
@@ -1658,6 +1660,7 @@ REGISTER_NATIVES(playernatives)
{ "NotifyPostAdminCheck", NotifyPostAdminCheck },
{ "IsClientInKickQueue", IsClientInKickQueue },
{ "ProcessTargetString", ProcessTargetString },
{ "GetLastProcessTargetString", GetLastProcessTargetString },
{ "FormatActivitySource", FormatActivitySource },
{ "GetClientSerial", sm_GetClientSerial },
{ "GetClientFromSerial", sm_GetClientFromSerial },
+128 -15
View File
@@ -35,6 +35,7 @@
#include "common_logic.h"
#include "CellArray.h"
#include <IHandleSys.h>
#include <amtl/am-raii.h>
/***********************************
* About the double array hack *
@@ -95,7 +96,7 @@ void sort_random(cell_t *array, cell_t size)
{
int n = rand() % (i + 1);
if (array[i] != array[n])
if (array[i] != array[n])
{
array[i] ^= array[n];
array[n] ^= array[i];
@@ -125,7 +126,7 @@ static cell_t sm_SortIntegers(IPluginContext *pContext, const cell_t *params)
if (type == Sort_Ascending)
{
qsort(array, array_size, sizeof(cell_t), sort_ints_asc);
}
}
else if (type == Sort_Descending)
{
qsort(array, array_size, sizeof(cell_t), sort_ints_desc);
@@ -195,7 +196,7 @@ static cell_t sm_SortFloats(IPluginContext *pContext, const cell_t *params)
static cell_t *g_CurStringArray = NULL;
static cell_t *g_CurRebaseMap = NULL;
int sort_strings_asc(const void *blk1, const void *blk2)
int sort_strings_asc_legacy(const void *blk1, const void *blk2)
{
cell_t reloc1 = *(cell_t *)blk1;
cell_t reloc2 = *(cell_t *)blk2;
@@ -206,7 +207,7 @@ int sort_strings_asc(const void *blk1, const void *blk2)
return strcmp(str1, str2);
}
int sort_strings_desc(const void *blk1, const void *blk2)
int sort_strings_desc_legacy(const void *blk1, const void *blk2)
{
cell_t reloc1 = *(cell_t *)blk1;
cell_t reloc2 = *(cell_t *)blk2;
@@ -217,7 +218,7 @@ int sort_strings_desc(const void *blk1, const void *blk2)
return strcmp(str2, str1);
}
static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
static cell_t sm_SortStrings_Legacy(IPluginContext *pContext, const cell_t *params)
{
cell_t *array;
cell_t array_size = params[2];
@@ -245,11 +246,11 @@ static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
if (type == Sort_Ascending)
{
qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
}
qsort(array, array_size, sizeof(cell_t), sort_strings_asc_legacy);
}
else if (type == Sort_Descending)
{
qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
qsort(array, array_size, sizeof(cell_t), sort_strings_desc_legacy);
}
else
{
@@ -275,6 +276,69 @@ static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
return 1;
}
static IPluginContext* sSortContext = nullptr;
int sort_strings_asc(const void *blk1, const void *blk2)
{
cell_t str_addr1 = *(cell_t *)blk1;
cell_t str_addr2 = *(cell_t *)blk2;
char *str1;
char *str2;
if (sSortContext->LocalToString(str_addr1, &str1) != SP_ERROR_NONE ||
sSortContext->LocalToString(str_addr2, &str2) != SP_ERROR_NONE)
{
return 0;
}
return strcmp(str1, str2);
}
int sort_strings_desc(const void *blk1, const void *blk2)
{
cell_t str_addr1 = *(cell_t *)blk1;
cell_t str_addr2 = *(cell_t *)blk2;
char *str1;
char *str2;
if (sSortContext->LocalToString(str_addr1, &str1) != SP_ERROR_NONE ||
sSortContext->LocalToString(str_addr2, &str2) != SP_ERROR_NONE)
{
return 0;
}
return strcmp(str2, str1);
}
static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
{
auto rt = pContext->GetRuntime();
if (!rt->UsesDirectArrays())
return sm_SortStrings_Legacy(pContext, params);
cell_t *array;
cell_t array_size = params[2];
cell_t type = params[3];
pContext->LocalToPhysAddr(params[1], &array);
ke::SaveAndSet<IPluginContext*> set_context(&sSortContext, pContext);
if (type == Sort_Ascending)
{
qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
}
else if (type == Sort_Descending)
{
qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
}
else
{
sort_random(array, array_size);
}
return 1;
}
struct sort_info
{
IPluginFunction *pFunc;
@@ -335,7 +399,7 @@ static cell_t sm_SortCustom1D(IPluginContext *pContext, const cell_t *params)
return 1;
}
int sort2d_amx_custom(const void *elem1, const void *elem2)
static int sort2d_amx_custom_legacy(const void *elem1, const void *elem2)
{
if (g_SortInfo.eh->HasException())
return 0;
@@ -361,7 +425,7 @@ int sort2d_amx_custom(const void *elem1, const void *elem2)
return result;
}
static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
static cell_t sm_SortCustom2D_Legacy(IPluginContext *pContext, const cell_t *params)
{
cell_t *array;
cell_t array_size = params[2];
@@ -401,7 +465,7 @@ static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
array[i] = i;
}
qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom_legacy);
/** Fixup process! */
for (int i=0; i<array_size; i++)
@@ -419,6 +483,55 @@ static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
return 1;
}
static int sort2d_amx_custom(const void *elem1, const void *elem2)
{
if (g_SortInfo.eh->HasException())
return 0;
cell_t iv1 = *(cell_t *)elem1;
cell_t iv2 = *(cell_t *)elem2;
cell_t result = 0;
g_SortInfo.pFunc->PushCell(iv1);
g_SortInfo.pFunc->PushCell(iv2);
g_SortInfo.pFunc->PushCell(g_SortInfo.array_addr);
g_SortInfo.pFunc->PushCell(g_SortInfo.hndl);
g_SortInfo.pFunc->Invoke(&result);
return result;
}
static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
{
auto rt = pContext->GetRuntime();
if (!rt->UsesDirectArrays())
return sm_SortCustom2D_Legacy(pContext, params);
cell_t *array;
cell_t array_size = params[2];
IPluginFunction *pFunction;
pContext->LocalToPhysAddr(params[1], &array);
if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
{
return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
}
sort_info oldinfo = g_SortInfo;
DetectExceptions eh(pContext);
g_SortInfo.pFunc = pFunction;
g_SortInfo.hndl = params[4];
g_SortInfo.array_addr = params[1];
g_SortInfo.eh = &eh;
qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
g_SortInfo = oldinfo;
return 1;
}
enum SortType
{
Sort_Integer = 0,
@@ -454,7 +567,7 @@ static cell_t sm_SortADTArray(IPluginContext *pContext, const cell_t *params)
HandleError err;
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
if ((err = handlesys->ReadHandle(params[1], htCellArray, &sec, (void **)&cArray))
if ((err = handlesys->ReadHandle(params[1], htCellArray, &sec, (void **)&cArray))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
@@ -491,7 +604,7 @@ static cell_t sm_SortADTArray(IPluginContext *pContext, const cell_t *params)
{
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_floats_asc);
}
else
else
{
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_floats_desc);
}
@@ -502,7 +615,7 @@ static cell_t sm_SortADTArray(IPluginContext *pContext, const cell_t *params)
{
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_strings_asc);
}
else
else
{
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_strings_desc);
}
@@ -545,7 +658,7 @@ static cell_t sm_SortADTArrayCustom(IPluginContext *pContext, const cell_t *para
HandleError err;
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
if ((err = handlesys->ReadHandle(params[1], htCellArray, &sec, (void **)&cArray))
if ((err = handlesys->ReadHandle(params[1], htCellArray, &sec, (void **)&cArray))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
+1 -1
View File
@@ -170,7 +170,7 @@ static cell_t Int64ToString(IPluginContext *pCtx, const cell_t *params)
char *str;
pCtx->LocalToPhysAddr(params[1], &num);
pCtx->LocalToString(params[2], &str);
size_t res = ke::SafeSprintf(str, params[3], "%" KE_FMT_I64, (int64_t)num[1] << 32ll | (int64_t)num[0]);
size_t res = ke::SafeSprintf(str, params[3], "%" KE_FMT_I64, (uint64_t)num[1] << 32ll | (uint32_t)num[0]);
return static_cast<cell_t>(res);
}
+2 -2
View File
@@ -1218,7 +1218,7 @@ reswitch:
cell_t *target;
pCtx->LocalToString(params[arg++], &key);
pCtx->LocalToPhysAddr(params[arg++], &target);
res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
res = Translate(buf_p, llen + 1, pCtx, key, *target, params, &arg, &error);
if (error)
{
return 0;
@@ -1235,7 +1235,7 @@ reswitch:
size_t res;
cell_t target = bridge->GetGlobalTarget();
pCtx->LocalToString(params[arg++], &key);
res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
res = Translate(buf_p, llen + 1, pCtx, key, target, params, &arg, &error);
if (error)
{
return 0;
+8 -38
View File
@@ -51,9 +51,6 @@
#include <bridge/include/IVEngineServerBridge.h>
#include <bridge/include/IPlayerInfoBridge.h>
#include <bridge/include/IFileSystemBridge.h>
#if PROTOBUF_PROXY_ENABLE
# include "pb_handle.h"
#endif
sm_logic_t logicore;
@@ -496,6 +493,8 @@ const char *CoreProviderImpl::GetSourceEngineName()
return "doi";
#elif SOURCE_ENGINE == SE_CSGO
return "csgo";
#elif SOURCE_ENGINE == SE_MOCK
return "mock";
#endif
}
@@ -655,41 +654,6 @@ void CoreProviderImpl::InitializeBridge()
rootmenu = logicore.rootmenu;
}
bool CoreProviderImpl::LoadProtobufProxy(char *error, size_t maxlength)
{
#if !defined(PROTOBUF_PROXY_ENABLE)
return false;
#else
char file[PLATFORM_MAX_PATH];
#if !defined(PROTOBUF_PROXY_BINARY_NAME)
# error "No engine suffix defined"
#endif
/* Now it's time to load the logic binary */
g_SMAPI->PathFormat(file,
sizeof(file),
"%s/bin/" PLATFORM_ARCH_FOLDER PROTOBUF_PROXY_BINARY_NAME PLATFORM_LIB_EXT,
g_SourceMod.GetSourceModPath());
char myerror[255];
pbproxy_ = ke::SharedLib::Open(file, myerror, sizeof(myerror));
if (!pbproxy_) {
ke::SafeSprintf(error, maxlength, "failed to load %s: %s", file, myerror);
return false;
}
auto fn = pbproxy_->get<GetProtobufProxyFn>("GetProtobufProxy");
if (!fn) {
ke::SafeStrcpy(error, maxlength, "could not find GetProtobufProxy function");
return false;
}
gProtobufProxy = fn();
return true;
#endif
}
bool CoreProviderImpl::LoadBridge(char *error, size_t maxlength)
{
char file[PLATFORM_MAX_PATH];
@@ -770,6 +734,12 @@ CoreProviderImpl::DefineCommand(const char *name, const char *help, const Comman
commands_.push_back(impl);
}
void CoreProviderImpl::FormatSourceBinaryName(const char *basename, char *buffer, size_t maxlength)
{
bool use_prefix = (!strcasecmp(basename, "tier0") || !strcasecmp(basename, "vstdlib"));
ke::SafeSprintf(buffer, maxlength, "%s%s%s%s", use_prefix ? SOURCE_BIN_PREFIX : "", basename, SOURCE_BIN_SUFFIX, SOURCE_BIN_EXT);
}
void CoreProviderImpl::InitializeHooks()
{
hooks_.Start();
-217
View File
@@ -1,217 +0,0 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2020 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#pragma once
#if defined(PROTOBUF_ENABLE)
#include <google/protobuf/message.h>
#if defined(PROTOBUF_PROXY_ENABLE)
# include "pb_proxy.h"
extern IProtobufProxy* gProtobufProxy;
#endif
const google::protobuf::Message *GetMessagePrototype(int msg_type);
// On SDKs which use protobufs, the engine has objects compiled against a specific
// version of protobuf. Normally this is fine, we take care on Linux to use the
// same C++ ABI. On macOS however, we use libc++ to enable C++11 functionality,
// whereas the protobuf library has been compiled with libstc++. These ABIs are
// not compatible.
//
// To address the problem, we introduce PbHandle. PbHandle is a wrapper around
// protobuf::Message with two added pieces of state: whether or not the handle
// "owns" the message (and can free it in its destructor), and whether or not
// the handle was created by the engine (private) or created by SourceMod
// (local).
//
// Whenever we transfer a protobuf::Message pointer to SourceMod, we must take
// care to convert it to a Local version first. Whenever we transfer a protobuf
// pointer to the engine, we must convert it to a Private handle.
//
// For platforms with no ABI differences (almost all of them), the handle is a
// no-op. The private and local localities are compatible and no translation
// takes place.
//
// On macOS, CS:GO does require translation. SourceMod loads a tiny shim
// library that contains a copy of the protobuf sources compiled against the
// game's ABI. It then provides serialization and deserialization methods.
// SourceMod must not interact with the game's protobuf objects without first
// going through this proxy library.
//
// Note that PbHandle is not quite like unique_ptr_. It can be converted into a
// PbHandle that does not destroy the underlying object. This is mainly because
// UserMessages.cpp has rather complex state, so it is useful to track locality
// without destroying an object. An unowned PbHandle must not outlive the
// owning PbHandle.
class PbHandle
{
public:
enum Ownership {
Owned,
Unowned,
};
enum Locality {
Local,
Private,
};
PbHandle() : msg_(nullptr) {}
PbHandle(decltype(nullptr)) : msg_(nullptr) {}
PbHandle(google::protobuf::Message* msg, Ownership ownership, Locality locality)
: msg_(msg),
ownership_(ownership),
locality_(locality)
{}
PbHandle(PbHandle&& other)
: msg_(other.msg_),
ownership_(other.ownership_),
locality_(other.locality_)
{
other.msg_ = nullptr;
}
PbHandle(const PbHandle&) = delete;
~PbHandle() {
maybe_free();
}
PbHandle& operator =(PbHandle&& other) {
if (other.msg_ != msg_)
maybe_free();
msg_ = other.msg_;
ownership_ = other.ownership_;
locality_ = other.locality_;
other.msg_ = nullptr;
return *this;
}
PbHandle& operator =(const PbHandle&) = delete;
google::protobuf::Message* operator ->() const { return msg_; }
google::protobuf::Message* GetLocalMessage() {
assert(locality_ == Local);
return msg_;
}
google::protobuf::Message* GetPrivateMessage() {
assert(locality_ == Private);
return msg_;
}
PbHandle ToLocal(int msg_type) {
if (locality_ == Local)
return AsUnowned();
#if defined(PROTOBUF_PROXY_ENABLE)
PbHandle local(GetMessagePrototype(msg_type)->New(), Owned, Local);
local.CopyFromPrivate(msg_);
return local;
#else
return PbHandle(msg_, Unowned, Local);
#endif
}
PbHandle ToPrivate(int msg_type) {
if (locality_ == Private)
return AsUnowned();
#if defined(PROTOBUF_PROXY_ENABLE)
PbHandle priv(gProtobufProxy->NewPrototype(msg_type), Owned, Private);
priv.CopyFromLocal(msg_);
return priv;
#else
return PbHandle(msg_, Unowned, Private);
#endif
}
void CopyFrom(const PbHandle& other) {
if (other.msg_ == msg_) {
assert(other.locality_ == locality_);
return;
}
#if defined(PROTOBUF_PROXY_ENABLE)
if (other.locality_ == Local)
CopyFromLocal(other.msg_);
else if (other.locality_ == Private)
CopyFromPrivate(other.msg_);
#else
msg_->CopyFrom(*other.msg_);
#endif
}
PbHandle AsUnowned() {
return PbHandle(msg_, Unowned, locality_);
}
bool is_owned() const { return ownership_ == Owned; }
bool is_local() const { return locality_ == Local; }
private:
#if defined(PROTOBUF_PROXY_ENABLE)
void CopyFromPrivate(google::protobuf::Message* message) {
void* out;
size_t len;
if (!gProtobufProxy->Serialize(message, &out, &len))
return;
if (locality_ == Local)
msg_->ParsePartialFromArray(out, len);
else
gProtobufProxy->Deserialize(out, len, msg_);
gProtobufProxy->FreeBuffer(out);
}
void CopyFromLocal(google::protobuf::Message* message) {
if (locality_ == Local) {
msg_->CopyFrom(*message);
} else {
auto data = message->SerializePartialAsString();
gProtobufProxy->Deserialize(data.data(), data.size(), msg_);
}
}
#endif
void maybe_free() {
if (ownership_ != Owned)
return;
#if defined(PROTOBUF_PROXY_ENABLE)
if (locality_ == Private) {
gProtobufProxy->FreeMessage(msg_);
return;
}
#endif
delete msg_;
}
private:
google::protobuf::Message* msg_ = nullptr;
Ownership ownership_ = Unowned;
Locality locality_ = Local;
};
#endif // PROTOBUF_ENABLE
-100
View File
@@ -1,100 +0,0 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2020 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "pb_proxy.h"
#include <stdlib.h>
#include <sm_platform.h>
#if SOURCE_ENGINE == SE_CSGO
# include <cstrike15_usermessage_helpers.h>
#else
# error "No source engine compatibility"
#endif
class ProtobufProxy : public IProtobufProxy
{
public:
bool Serialize(const google::protobuf::Message* message, void** out, size_t* len);
void FreeBuffer(void* data);
bool Deserialize(const void* buffer, size_t len, google::protobuf::Message* message);
google::protobuf::Message* NewPrototype(int msg_type);
void FreeMessage(google::protobuf::Message* message);
};
static ProtobufProxy sProtobufProxy;
PLATFORM_EXTERN_C IProtobufProxy*
GetProtobufProxy()
{
return &sProtobufProxy;
}
bool
ProtobufProxy::Serialize(const google::protobuf::Message* message, void** out, size_t* len)
{
*len = message->ByteSize();
*out = malloc(*len);
if (!*out)
return false;
if (!message->SerializePartialToArray(*out, *len)) {
free(*out);
return false;
}
return true;
}
void
ProtobufProxy::FreeBuffer(void* data)
{
free(data);
}
bool
ProtobufProxy::Deserialize(const void* buffer, size_t len, google::protobuf::Message* message)
{
return message->ParsePartialFromArray(buffer, len);
}
google::protobuf::Message*
ProtobufProxy::NewPrototype(int msg_type)
{
#if SOURCE_ENGINE == SE_CSGO
return g_Cstrike15UsermessageHelpers.GetPrototype(msg_type)->New();
#else
# error "No source engine compatibility."
#endif
}
void
ProtobufProxy::FreeMessage(google::protobuf::Message* message)
{
delete message;
}
+1 -2
View File
@@ -38,7 +38,6 @@ public:
// Local functions.
void InitializeBridge();
bool LoadProtobufProxy(char *error, size_t maxlength);
bool LoadBridge(char *error, size_t maxlength);
void ShutdownBridge();
@@ -67,6 +66,7 @@ public:
int QueryClientConVar(int client, const char *cvar) override;
bool IsClientConVarQueryingSupported() override;
void DefineCommand(const char *cmd, const char *help, const SourceMod::CommandFunc &callback) override;
void FormatSourceBinaryName(const char *basename, char *buffer, size_t maxlength) override;
ke::RefPtr<CommandHook> AddCommandHook(ConCommand *cmd, const CommandHook::Callback &callback);
ke::RefPtr<CommandHook> AddPostCommandHook(ConCommand *cmd, const CommandHook::Callback &callback);
@@ -76,7 +76,6 @@ public:
}
private:
ke::RefPtr<ke::SharedLib> pbproxy_;
ke::RefPtr<ke::SharedLib> logic_;
LogicInitFunction logic_init_;
GameHooks hooks_;
+11
View File
@@ -827,6 +827,16 @@ static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t sm_IsCommandCallback(IPluginContext *pContext, const cell_t *params)
{
const ICommandArgs *pCmd = g_HL2.PeekCommandStack();
if (!pCmd)
return 0;
return 1;
}
static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
{
const ICommandArgs *pCmd = g_HL2.PeekCommandStack();
@@ -1505,6 +1515,7 @@ REGISTER_NATIVES(consoleNatives)
{"GetConVarDefault", GetConVarDefault},
{"RegServerCmd", sm_RegServerCmd},
{"RegConsoleCmd", sm_RegConsoleCmd},
{"IsCommandCallback", sm_IsCommandCallback},
{"GetCmdArgString", sm_GetCmdArgString},
{"GetCmdArgs", sm_GetCmdArgs},
{"GetCmdArg", sm_GetCmdArg},
+7 -1
View File
@@ -818,6 +818,12 @@ static cell_t FindSendPropOffs(IPluginContext *pContext, const cell_t *params)
return -1;
}
// Before we added support for DPT_Array props, FindInSendTable would have given us the inner array prop itself.
// To maintain compatibility with older plugins still using us, pluck out the inner array prop ourselves.
if (pSend->GetType() == DPT_Array && pSend->GetArrayProp()) {
return pSend->GetArrayProp()->GetOffset();
}
return pSend->GetOffset();
}
@@ -825,7 +831,7 @@ static cell_t FindSendPropInfo(IPluginContext *pContext, const cell_t *params)
{
char *cls, *prop;
sm_sendprop_info_t info;
cell_t *pType, *pBits, *pLocal, *pArraySize;
cell_t *pType, *pBits, *pLocal, *pArraySize = nullptr;
pContext->LocalToString(params[1], &cls);
pContext->LocalToString(params[2], &prop);
+12
View File
@@ -320,6 +320,10 @@ void UTIL_SendHudText(int client, const hud_text_parms &textparms, const char *p
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
CCSUsrMsg_HudMsg *msg = (CCSUsrMsg_HudMsg *)g_UserMsgs.StartProtobufMessage(g_HudMsgNum, players, 1, 0);
if (!msg)
{
return;
}
msg->set_channel(textparms.channel & 0xFF);
CMsgVector2D *pos = msg->mutable_pos();
@@ -373,7 +377,11 @@ static cell_t ShowSyncHudText(IPluginContext *pContext, const cell_t *params)
Handle_t err;
CPlayer *pPlayer;
hud_syncobj_t *obj;
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
char message_buffer[512];
#else
char message_buffer[255-36];
#endif
if (!s_HudMsgHelpers.IsSupported())
{
@@ -453,7 +461,11 @@ static cell_t ShowHudText(IPluginContext *pContext, const cell_t *params)
{
int client;
CPlayer *pPlayer;
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
char message_buffer[512];
#else
char message_buffer[255-36];
#endif
if (!s_HudMsgHelpers.IsSupported())
{
+22
View File
@@ -356,6 +356,27 @@ static cell_t GetAvgPackets(IPluginContext *pContext, const cell_t *params)
return sp_ftoc(value);
}
static cell_t sm_GetClientIClient(IPluginContext *pContext, const cell_t *params)
{
int client = params[1];
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
if (!pPlayer)
{
return pContext->ThrowNativeError("Client index %d is invalid", client);
}
else if (!pPlayer->IsConnected())
{
return pContext->ThrowNativeError("Client %d is not connected", client);
}
else if (pPlayer->IsFakeClient())
{
return pContext->ThrowNativeError("Client %d is a bot", client);
}
return (cell_t)pPlayer->GetIClient();
}
static cell_t RunAdminCacheChecks(IPluginContext *pContext, const cell_t *params)
{
int client = params[1];
@@ -392,6 +413,7 @@ REGISTER_NATIVES(playernatives)
{"GetClientAvgChoke", GetAvgChoke},
{"GetClientAvgData", GetAvgData},
{"GetClientAvgPackets", GetAvgPackets},
{"GetClientIClient", sm_GetClientIClient },
{"RunAdminCacheChecks", RunAdminCacheChecks},
{NULL, NULL}
};
+30 -3
View File
@@ -46,6 +46,7 @@
#include <amtl/os/am-path.h>
#include <bridge/include/IExtensionBridge.h>
#include <bridge/include/IScriptManager.h>
#include <bridge/include/IProviderCallbacks.h>
#include <bridge/include/ILogger.h>
SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
@@ -53,6 +54,7 @@ SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false);
SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool);
SH_DECL_HOOK1_void(IServerGameDLL, Think, SH_NOATTRIB, false, bool);
SH_DECL_HOOK1_void(IVEngineServer, ServerCommand, SH_NOATTRIB, false, const char *);
SH_DECL_HOOK0(IVEngineServer, GetMapEntitiesString, SH_NOATTRIB, 0, const char *);
SourceModBase g_SourceMod;
@@ -277,6 +279,7 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late
/* Hook this now so we can detect startup without calling StartSourceMod() */
SH_ADD_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false);
SH_ADD_HOOK(IVEngineServer, GetMapEntitiesString, engine, SH_MEMBER(this, &SourceModBase::GetMapEntitiesString), false);
/* Only load if we're not late */
if (!late)
@@ -290,7 +293,6 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late
void SourceModBase::StartSourceMod(bool late)
{
SH_ADD_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false);
SH_ADD_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::Think), false);
SH_ADD_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false);
enginePatch = SH_GET_CALLCLASS(engine);
@@ -362,6 +364,8 @@ void SourceModBase::StartSourceMod(bool late)
{
g_pSourcePawn2->InstallWatchdogTimer(atoi(timeout) * 1000);
}
SH_ADD_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(logicore.callbacks, &IProviderCallbacks::OnThink), false);
}
static bool g_LevelEndBarrier = false;
@@ -414,10 +418,32 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch
g_LevelEndBarrier = true;
int parseError;
size_t position;
bool success = logicore.ParseEntityLumpString(pMapEntities, parseError, position);
logicore.SetEntityLumpWritable(true);
g_pOnMapInit->PushString(pMapName);
g_pOnMapInit->Execute();
logicore.SetEntityLumpWritable(false);
RETURN_META_VALUE(MRES_IGNORED, true);
if (!success)
{
logger->LogError("Map entity lump parsing for %s failed with error code %d on position %d", pMapName, parseError, position);
RETURN_META_VALUE(MRES_IGNORED, true);
}
RETURN_META_VALUE_NEWPARAMS(MRES_HANDLED, true, &IServerGameDLL::LevelInit, (pMapName, logicore.GetEntityLumpString(), pOldLevel, pLandmarkName, loadGame, background));
}
const char *SourceModBase::GetMapEntitiesString()
{
const char *pNewMapEntities = logicore.GetEntityLumpString();
if (pNewMapEntities != nullptr)
{
RETURN_META_VALUE(MRES_SUPERCEDE, pNewMapEntities);
}
RETURN_META_VALUE(MRES_IGNORED, NULL);
}
void SourceModBase::LevelShutdown()
@@ -532,6 +558,7 @@ void SourceModBase::CloseSourceMod()
return;
SH_REMOVE_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false);
SH_REMOVE_HOOK(IVEngineServer, GetMapEntitiesString, engine, SH_MEMBER(this, &SourceModBase::GetMapEntitiesString), false);
if (g_Loaded)
{
@@ -596,8 +623,8 @@ void SourceModBase::ShutdownServices()
}
SH_REMOVE_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false);
SH_REMOVE_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::Think), false);
SH_REMOVE_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false);
SH_REMOVE_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(logicore.callbacks, &IProviderCallbacks::OnThink), false);
}
void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...)
+1
View File
@@ -140,6 +140,7 @@ public: // ISourceMod
private:
void ShutdownServices();
private:
const char* GetMapEntitiesString();
char m_SMBaseDir[PLATFORM_MAX_PATH];
char m_SMRelDir[PLATFORM_MAX_PATH];
char m_ModDir[32];
+9 -2
View File
@@ -80,8 +80,15 @@ CallWrapper::CallWrapper(const SourceHook::ProtoInfo *protoInfo) : m_FnFlags(0)
CallWrapper::CallWrapper(const SourceHook::ProtoInfo *protoInfo, const PassInfo *retInfo,
const PassInfo paramInfo[], unsigned int fnFlags) : CallWrapper(protoInfo)
{
m_RetParam->fields = retInfo->fields;
m_RetParam->numFields = retInfo->numFields;
if (retInfo)
{
m_RetParam->fields = retInfo->fields;
m_RetParam->numFields = retInfo->numFields;
}
else
{
m_RetParam = nullptr;
}
unsigned int argnum = protoInfo->numOfParams;
for (unsigned int i = 0; i < argnum; i++)
+1 -1
View File
@@ -100,7 +100,7 @@ bool CStrike::SDK_OnLoad(char *error, size_t maxlength, bool late)
g_pHandleBuyForward = forwards->CreateForward("CS_OnBuyCommand", ET_Event, 2, NULL, Param_Cell, Param_String);
g_pPriceForward = forwards->CreateForward("CS_OnGetWeaponPrice", ET_Event, 3, NULL, Param_Cell, Param_String, Param_CellByRef);
g_pTerminateRoundForward = forwards->CreateForward("CS_OnTerminateRound", ET_Event, 2, NULL, Param_FloatByRef, Param_CellByRef);
g_pCSWeaponDropForward = forwards->CreateForward("CS_OnCSWeaponDrop", ET_Event, 2, NULL, Param_Cell, Param_Cell);
g_pCSWeaponDropForward = forwards->CreateForward("CS_OnCSWeaponDrop", ET_Event, 3, NULL, Param_Cell, Param_Cell, Param_Cell);
m_TerminateRoundDetourEnabled = false;
+20 -3
View File
@@ -216,12 +216,20 @@ DETOUR_DECL_MEMBER3(DetourTerminateRound, void, int, reason, int, unknown, int,
#endif
}
DETOUR_DECL_MEMBER2(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bThrowForward)
#if SOURCE_ENGINE == SE_CSGO
DETOUR_DECL_MEMBER3(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bThrowForward, bool, bDonated)
#else
DETOUR_DECL_MEMBER3(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bDropShield, bool, bThrowForward)
#endif
{
if (g_pIgnoreCSWeaponDropDetour)
{
g_pIgnoreCSWeaponDropDetour = false;
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bThrowForward);
#if SOURCE_ENGINE == SE_CSGO
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bThrowForward, bDonated);
#else
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bDropShield, bThrowForward);
#endif
return;
}
@@ -231,12 +239,21 @@ DETOUR_DECL_MEMBER2(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bThro
cell_t result = Pl_Continue;
g_pCSWeaponDropForward->PushCell(client);
g_pCSWeaponDropForward->PushCell(weaponIndex);
#if SOURCE_ENGINE == SE_CSGO
g_pCSWeaponDropForward->PushCell(bDonated);
#else
g_pCSWeaponDropForward->PushCell(false);
#endif
g_pCSWeaponDropForward->Execute(&result);
if (result == Pl_Continue)
{
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bThrowForward);
#if SOURCE_ENGINE == SE_CSGO
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bThrowForward, bDonated);
#else
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bDropShield, bThrowForward);
#endif
}
return;
+10 -3
View File
@@ -227,14 +227,17 @@ static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params)
if (!pWrapper)
{
REGISTER_NATIVE_ADDR(WEAPONDROP_GAMEDATA_NAME,
PassInfo pass[2]; \
PassInfo pass[3]; \
pass[0].flags = PASSFLAG_BYVAL; \
pass[0].type = PassType_Basic; \
pass[0].size = sizeof(CBaseEntity *); \
pass[1].flags = PASSFLAG_BYVAL; \
pass[1].type = PassType_Basic; \
pass[1].size = sizeof(bool); \
pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
pass[2].flags = PASSFLAG_BYVAL; \
pass[2].type = PassType_Basic; \
pass[2].size = sizeof(bool); \
pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 3))
}
CBaseEntity *pEntity;
@@ -267,7 +270,11 @@ static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params)
if (params[4] == 1 && g_pCSWeaponDropDetoured)
g_pIgnoreCSWeaponDropDetour = true;
ArgBuffer<CBaseEntity*, CBaseEntity*, bool> vstk(pEntity, pWeapon, (params[3]) ? true : false);
#if SOURCE_ENGINE == SE_CSGO
ArgBuffer<CBaseEntity*, CBaseEntity*, bool, bool> vstk(pEntity, pWeapon, !!params[3], false);
#else
ArgBuffer<CBaseEntity*, CBaseEntity*, bool, bool> vstk(pEntity, pWeapon, false, !!params[3]);
#endif
pWrapper->Execute(vstk, NULL);
return 1;
+4 -42
View File
@@ -39,9 +39,6 @@ bool bPatched = false;
RulesFix rulesfix;
ISteamGameServer *(*SteamAPI_SteamGameServer)();
void (*SteamAPI_ISteamGameServer_SetKeyValue)(ISteamGameServer *self, const char *pKey, const char *pValue);
SH_DECL_HOOK1_void(IServerGameDLL, GameServerSteamAPIActivated, SH_NOATTRIB, 0, bool);
RulesFix::RulesFix() :
@@ -80,39 +77,6 @@ bool SetMTUMax(int iValue)
void RulesFix::OnLoad()
{
ILibrary *pLibrary = libsys->OpenLibrary(
#if defined ( PLATFORM_WINDOWS )
"steam_api.dll"
#elif defined( PLATFORM_LINUX )
"libsteam_api.so"
#elif defined( PLATFORM_APPLE )
"libsteam_api.dylib"
#else
#error Unsupported platform
#endif
, nullptr, 0);
if (pLibrary != nullptr)
{
const char *pSteamGameServerFuncName = "SteamAPI_SteamGameServer_v013";
const char *pSetKeyValueFuncName = "SteamAPI_ISteamGameServer_SetKeyValue";
// When will hl2sdk-csgo be updated the SteamWorks SDK, change this to export.
SteamAPI_SteamGameServer = reinterpret_cast<ISteamGameServer *(*)()>(pLibrary->GetSymbolAddress(pSteamGameServerFuncName));
SteamAPI_ISteamGameServer_SetKeyValue = reinterpret_cast<void (*)(ISteamGameServer *self, const char *pKey, const char *pValue)>(pLibrary->GetSymbolAddress(pSetKeyValueFuncName));
if(SteamAPI_SteamGameServer == nullptr)
{
g_pSM->LogError(myself, "[CStrike] Failed to get %s function", pSteamGameServerFuncName);
}
if(SteamAPI_ISteamGameServer_SetKeyValue == nullptr)
{
g_pSM->LogError(myself, "[CStrike] Failed to get %s function", pSetKeyValueFuncName);
}
pLibrary->CloseLibrary();
}
host_rules_show = g_pCVar->FindVar("host_rules_show");
if (host_rules_show)
{
@@ -185,20 +149,18 @@ static void OnConVarChanged(IConVar *var, const char *pOldValue, float flOldValu
void RulesFix::OnNotifyConVarChanged(ConVar *pVar)
{
if (!bPatched || !SteamAPI_SteamGameServer || !SteamAPI_ISteamGameServer_SetKeyValue)
if (!bPatched)
return;
ISteamGameServer *pSteamClientGameServer = SteamAPI_SteamGameServer();
if (pSteamClientGameServer)
if (SteamGameServer())
{
if (pVar->IsFlagSet(FCVAR_PROTECTED))
{
SteamAPI_ISteamGameServer_SetKeyValue(pSteamClientGameServer, pVar->GetName(), !pVar->GetString()[0] ? "0" : "1");
SteamGameServer()->SetKeyValue(pVar->GetName(), !pVar->GetString()[0] ? "0" : "1");
}
else
{
SteamAPI_ISteamGameServer_SetKeyValue(pSteamClientGameServer, pVar->GetName(), pVar->GetString());
SteamGameServer()->SetKeyValue(pVar->GetName(), pVar->GetString());
}
}
}
+1 -3
View File
@@ -68,9 +68,7 @@
#define SMEXT_ENABLE_GAMEHELPERS
#define SMEXT_ENABLE_TIMERSYS
//#define SMEXT_ENABLE_THREADER
#if SOURCE_ENGINE == SE_CSGO
#define SMEXT_ENABLE_LIBSYS
#endif
//#define SMEXT_ENABLE_LIBSYS
#define SMEXT_ENABLE_USERMSGS
#define SMEXT_ENABLE_PLUGINSYS
+64
View File
@@ -0,0 +1,64 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os
for cxx in builder.targets:
binary = SM.ExtLibrary(builder, cxx, 'dhooks.ext')
# Only x86 on Linux and Windows is supported.
if binary.compiler.target.platform == 'mac' or binary.compiler.target.arch != 'x86':
continue
binary.compiler.defines += [
'META_NO_HL2SDK',
'HAVE_STRING_H',
]
if binary.compiler.like('gcc'):
binary.compiler.cflags += ['-Wno-invalid-offsetof']
binary.compiler.cxxincludes += [
os.path.join(SM.mms_root, 'core'),
os.path.join(SM.mms_root, 'core', 'sourcehook'),
os.path.join(builder.sourcePath, 'public', 'jit'),
os.path.join(builder.sourcePath, 'public', 'jit', 'x86'),
os.path.join(builder.sourcePath, 'sourcepawn', 'include'),
os.path.join(builder.sourcePath, 'sourcepawn', 'vm'),
os.path.join(builder.sourcePath, 'sourcepawn', 'vm', 'x86'),
os.path.join(builder.sourcePath, 'extensions', 'dhooks', 'DynamicHooks'),
]
binary.sources += [
'extension.cpp',
'listeners.cpp',
'natives.cpp',
'signatures.cpp',
'vhook.cpp',
'util.cpp',
'dynhooks_sourcepawn.cpp',
'../../public/smsdk_ext.cpp',
'../../public/asm/asm.c',
'../../public/libudis86/decode.c',
'../../public/libudis86/itab.c',
'../../public/libudis86/syn-att.c',
'../../public/libudis86/syn-intel.c',
'../../public/libudis86/syn.c',
'../../public/libudis86/udis86.c',
'../../sourcepawn/vm/x86/assembler-x86.cpp',
]
# DynamicHooks
binary.sources += [
os.path.join('DynamicHooks', 'hook.cpp'),
os.path.join('DynamicHooks', 'manager.cpp'),
os.path.join('DynamicHooks', 'registers.cpp'),
os.path.join('DynamicHooks', 'conventions', 'x86MsCdecl.cpp'),
os.path.join('DynamicHooks', 'conventions', 'x86MsStdcall.cpp'),
os.path.join('DynamicHooks', 'conventions', 'x86MsFastcall.cpp'),
]
if binary.compiler.target.platform == 'windows':
binary.sources += [os.path.join('DynamicHooks', 'conventions', 'x86MsThiscall.cpp')]
else:
binary.sources += [os.path.join('DynamicHooks', 'conventions', 'x86GccThiscall.cpp')]
SM.extensions += [builder.Add(binary)]
+290
View File
@@ -0,0 +1,290 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _CONVENTION_H
#define _CONVENTION_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "registers.h"
#include <cstdio>
#include <cstring>
#include <memory>
#include <vector>
#include <utility>
// ============================================================================
// >> DataType_t
// ============================================================================
enum DataType_t
{
DATA_TYPE_VOID,
DATA_TYPE_BOOL,
DATA_TYPE_CHAR,
DATA_TYPE_UCHAR,
DATA_TYPE_SHORT,
DATA_TYPE_USHORT,
DATA_TYPE_INT,
DATA_TYPE_UINT,
DATA_TYPE_LONG,
DATA_TYPE_ULONG,
DATA_TYPE_LONG_LONG,
DATA_TYPE_ULONG_LONG,
DATA_TYPE_FLOAT,
DATA_TYPE_DOUBLE,
DATA_TYPE_POINTER,
DATA_TYPE_STRING,
DATA_TYPE_OBJECT
};
typedef struct DataTypeSized_s {
DataTypeSized_s()
{
type = DATA_TYPE_POINTER;
size = 0;
custom_register = None;
}
DataType_t type;
size_t size;
Register_t custom_register;
} DataTypeSized_t;
// ============================================================================
// >> FUNCTIONS
// ============================================================================
/*
Returns the size after applying alignment.
@param <size>:
The size that should be aligned.
@param <alignment>:
The alignment that should be used.
*/
inline int Align(int size, int alignment)
{
int unaligned = size % alignment;
if (unaligned == 0)
return size;
return size + (alignment - unaligned);
}
/*
Returns the size of a data type after applying alignment.
@param <type>:
The data type you would like to get the size of.
@param <alignment>:
The alignment that should be used.
*/
inline int GetDataTypeSize(DataTypeSized_t type, int iAlignment=4)
{
switch(type.type)
{
case DATA_TYPE_VOID: return 0;
case DATA_TYPE_BOOL: return Align(sizeof(bool), iAlignment);
case DATA_TYPE_CHAR: return Align(sizeof(char), iAlignment);
case DATA_TYPE_UCHAR: return Align(sizeof(unsigned char), iAlignment);
case DATA_TYPE_SHORT: return Align(sizeof(short), iAlignment);
case DATA_TYPE_USHORT: return Align(sizeof(unsigned short), iAlignment);
case DATA_TYPE_INT: return Align(sizeof(int), iAlignment);
case DATA_TYPE_UINT: return Align(sizeof(unsigned int), iAlignment);
case DATA_TYPE_LONG: return Align(sizeof(long), iAlignment);
case DATA_TYPE_ULONG: return Align(sizeof(unsigned long), iAlignment);
case DATA_TYPE_LONG_LONG: return Align(sizeof(long long), iAlignment);
case DATA_TYPE_ULONG_LONG: return Align(sizeof(unsigned long long), iAlignment);
case DATA_TYPE_FLOAT: return Align(sizeof(float), iAlignment);
case DATA_TYPE_DOUBLE: return Align(sizeof(double), iAlignment);
case DATA_TYPE_POINTER: return Align(sizeof(void *), iAlignment);
case DATA_TYPE_STRING: return Align(sizeof(char *), iAlignment);
case DATA_TYPE_OBJECT: return type.size;
default: puts("Unknown data type.");
}
return 0;
}
// ============================================================================
// >> CLASSES
// ============================================================================
/*
This is the base class for every calling convention. Inherit from this class
to create your own calling convention.
*/
class ICallingConvention
{
public:
/*
Initializes the calling convention.
@param <vecArgTypes>:
A list of DataType_t objects, which define the arguments of the function.
@param <returnType>:
The return type of the function.
*/
ICallingConvention(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4)
{
m_vecArgTypes = std::move(vecArgTypes);
for (size_t i=0; i < m_vecArgTypes.size(); i++)
{
DataTypeSized_t &type = m_vecArgTypes[i];
if (!type.size)
type.size = GetDataTypeSize(type, iAlignment);
}
m_returnType = returnType;
if (!m_returnType.size)
m_returnType.size = GetDataTypeSize(m_returnType, iAlignment);
m_iAlignment = iAlignment;
}
virtual ~ICallingConvention()
{
}
/*
This should return a list of Register_t values. These registers will be
saved for later access.
*/
virtual std::vector<Register_t> GetRegisters() = 0;
/*
Returns the number of bytes that should be added to the stack to clean up.
*/
virtual int GetPopSize() = 0;
virtual int GetArgStackSize() = 0;
virtual void** GetStackArgumentPtr(CRegisters* pRegisters) = 0;
/*
Returns the number of bytes for the buffer to store all the arguments that are passed in a register in.
*/
virtual int GetArgRegisterSize() = 0;
/*
Returns a pointer to the argument at the given index.
@param <iIndex>:
The index of the argument.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters) = 0;
/*
*/
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr) = 0;
/*
Returns a pointer to the return value.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void* GetReturnPtr(CRegisters* pRegisters) = 0;
/*
*/
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr) = 0;
/*
Save the return value in a seperate buffer, so we can restore it after calling the original function.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void SaveReturnValue(CRegisters* pRegisters)
{
std::unique_ptr<uint8_t[]> pSavedReturnValue = std::make_unique<uint8_t[]>(m_returnType.size);
memcpy(pSavedReturnValue.get(), GetReturnPtr(pRegisters), m_returnType.size);
m_pSavedReturnBuffers.push_back(std::move(pSavedReturnValue));
}
virtual void RestoreReturnValue(CRegisters* pRegisters)
{
uint8_t* pSavedReturnValue = m_pSavedReturnBuffers.back().get();
memcpy(GetReturnPtr(pRegisters), pSavedReturnValue, m_returnType.size);
ReturnPtrChanged(pRegisters, pSavedReturnValue);
m_pSavedReturnBuffers.pop_back();
}
/*
Save the value of arguments in a seperate buffer for the post callback.
Compiler optimizations might cause the registers or stack space to be reused
and overwritten during function execution if the value isn't needed anymore
at some point. This leads to different values in the post hook.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void SaveCallArguments(CRegisters* pRegisters)
{
int size = GetArgStackSize() + GetArgRegisterSize();
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
size_t offset = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i, pRegisters), type.size);
offset += type.size;
}
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
}
virtual void RestoreCallArguments(CRegisters* pRegisters)
{
uint8_t *pSavedCallArguments = m_pSavedCallArguments.back().get();
size_t offset = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy(GetArgumentPtr(i, pRegisters), (void *)((unsigned long)pSavedCallArguments + offset), type.size);
offset += type.size;
}
m_pSavedCallArguments.pop_back();
}
public:
std::vector<DataTypeSized_t> m_vecArgTypes;
DataTypeSized_t m_returnType;
int m_iAlignment;
// Save the return in case we call the original function and want to override the return again.
std::vector<std::unique_ptr<uint8_t[]>> m_pSavedReturnBuffers;
// Save call arguments in case the function reuses the space and overwrites the values for the post hook.
std::vector<std::unique_ptr<uint8_t[]>> m_pSavedCallArguments;
};
#endif // _CONVENTION_H
@@ -0,0 +1,48 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _X86_GCC_CDECL_H
#define _X86_GCC_CDECL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86MsCdecl.h"
// ============================================================================
// >> CLASSES
// ============================================================================
typedef x86MsCdecl x86GccCdecl;
#endif // _X86_GCC_CDECL_H
@@ -0,0 +1,87 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86GccThiscall.h"
// ============================================================================
// >> CLASSES
// ============================================================================
x86GccThiscall::x86GccThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
x86GccCdecl(vecArgTypes, returnType, iAlignment)
{
// Always add the |this| pointer.
DataTypeSized_t type;
type.type = DATA_TYPE_POINTER;
type.size = GetDataTypeSize(type, iAlignment);
type.custom_register = None;
m_vecArgTypes.insert(m_vecArgTypes.begin(), type);
}
x86GccThiscall::~x86GccThiscall()
{
}
int x86GccThiscall::GetArgStackSize()
{
// Remove the this pointer from the arguments size.
DataTypeSized_t type;
type.type = DATA_TYPE_POINTER;
return x86GccCdecl::GetArgStackSize() - GetDataTypeSize(type, m_iAlignment);
}
void** x86GccThiscall::GetStackArgumentPtr(CRegisters* pRegisters)
{
// Skip return address and this pointer.
DataTypeSized_t type;
type.type = DATA_TYPE_POINTER;
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4 + GetDataTypeSize(type, m_iAlignment));
}
void x86GccThiscall::SaveCallArguments(CRegisters* pRegisters)
{
// Count the this pointer.
int size = x86GccCdecl::GetArgStackSize() + GetArgRegisterSize();
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
size_t offset = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i, pRegisters), type.size);
offset += type.size;
}
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
}
@@ -0,0 +1,59 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _X86_GCC_THISCALL_H
#define _X86_GCC_THISCALL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86GccCdecl.h"
// ============================================================================
// >> CLASSES
// ============================================================================
// |this| pointer is always passed as implicit first argument on the stack.
class x86GccThiscall: public x86GccCdecl
{
public:
x86GccThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment = 4);
virtual ~x86GccThiscall();
virtual int GetArgStackSize();
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
virtual void SaveCallArguments(CRegisters* pRegisters);
};
#endif // _X86_GCC_THISCALL_H
@@ -0,0 +1,187 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86MsCdecl.h"
#include <string.h>
// ============================================================================
// >> x86MsCdecl
// ============================================================================
x86MsCdecl::x86MsCdecl(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
ICallingConvention(vecArgTypes, returnType, iAlignment)
{
if (m_returnType.size > 4)
{
m_pReturnBuffer = malloc(m_returnType.size);
}
else
{
m_pReturnBuffer = NULL;
}
}
x86MsCdecl::~x86MsCdecl()
{
if (m_pReturnBuffer)
{
free(m_pReturnBuffer);
}
}
std::vector<Register_t> x86MsCdecl::GetRegisters()
{
std::vector<Register_t> registers;
registers.push_back(ESP);
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
{
registers.push_back(ST0);
}
else
{
registers.push_back(EAX);
if (m_pReturnBuffer)
{
registers.push_back(EDX);
}
}
// Save all the custom calling convention registers as well.
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
continue;
// TODO: Make sure the list is unique? Set?
registers.push_back(m_vecArgTypes[i].custom_register);
}
return registers;
}
int x86MsCdecl::GetPopSize()
{
return 0;
}
int x86MsCdecl::GetArgStackSize()
{
int iArgStackSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
iArgStackSize += m_vecArgTypes[i].size;
}
return iArgStackSize;
}
void** x86MsCdecl::GetStackArgumentPtr(CRegisters* pRegisters)
{
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
}
int x86MsCdecl::GetArgRegisterSize()
{
int iArgRegisterSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register != None)
iArgRegisterSize += m_vecArgTypes[i].size;
}
return iArgRegisterSize;
}
void* x86MsCdecl::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
{
if (iIndex >= m_vecArgTypes.size())
return NULL;
// Check if this argument was passed in a register.
if (m_vecArgTypes[iIndex].custom_register != None)
{
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
if (!pRegister)
return NULL;
return pRegister->m_pAddress;
}
// Skip return address.
size_t iOffset = 4;
for(unsigned int i=0; i < iIndex; i++)
{
if (m_vecArgTypes[i].custom_register == None)
iOffset += m_vecArgTypes[i].size;
}
return (void *) (pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
}
void x86MsCdecl::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
{
}
void* x86MsCdecl::GetReturnPtr(CRegisters* pRegisters)
{
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
return pRegisters->m_st0->m_pAddress;
if (m_pReturnBuffer)
{
// First half in eax, second half in edx
memcpy(m_pReturnBuffer, pRegisters->m_eax, 4);
memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4);
return m_pReturnBuffer;
}
return pRegisters->m_eax->m_pAddress;
}
void x86MsCdecl::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr)
{
if (m_pReturnBuffer)
{
// First half in eax, second half in edx
memcpy(pRegisters->m_eax, m_pReturnBuffer, 4);
memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4);
}
}
@@ -0,0 +1,88 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _X86_MS_CDECL_H
#define _X86_MS_CDECL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "../convention.h"
// ============================================================================
// >> CLASSES
// ============================================================================
/*
Source: DynCall manual and Windows docs
Registers:
- eax = return value
- edx = return value
- esp = stack pointer
- st0 = floating point return value
Parameter passing:
- stack parameter order: right-to-left
- caller cleans up the stack
- all arguments are pushed onto the stack
- alignment: 4 bytes
Return values:
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
- integers > 32 bits are returned via the eax and edx registers
- floating pointer types are returned via the st0 register
*/
class x86MsCdecl: public ICallingConvention
{
public:
x86MsCdecl(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
virtual ~x86MsCdecl();
virtual std::vector<Register_t> GetRegisters();
virtual int GetPopSize();
virtual int GetArgStackSize();
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
virtual int GetArgRegisterSize();
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters);
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr);
virtual void* GetReturnPtr(CRegisters* pRegisters);
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr);
private:
void* m_pReturnBuffer;
};
#endif // _X86_MS_CDECL_H
@@ -0,0 +1,61 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86MsFastcall.h"
// ============================================================================
// >> x86MsFastcall
// ============================================================================
x86MsFastcall::x86MsFastcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
x86MsStdcall(vecArgTypes, returnType, iAlignment)
{
// First argument is passed in ecx.
if (m_vecArgTypes.size() > 0) {
DataTypeSized_t &type = m_vecArgTypes[0];
// Don't force the register on the user.
// Why choose Fastcall if you set your own argument registers though..
if (type.custom_register == None)
type.custom_register = ECX;
}
// Second argument is passed in edx.
if (m_vecArgTypes.size() > 1) {
DataTypeSized_t &type = m_vecArgTypes[1];
if (type.custom_register == None)
type.custom_register = EDX;
}
}
@@ -0,0 +1,72 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _X86_MS_FASTCALL_H
#define _X86_MS_FASTCALL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86MsStdcall.h"
// ============================================================================
// >> CLASSES
// ============================================================================
/*
Source: DynCall manual and Windows docs
Registers:
- eax = return value
- edx = return value
- esp = stack pointer
- st0 = floating point return value
Parameter passing:
- first parameter in ecx, second parameter in edx, rest on the stack
- stack parameter order: right-to-left
- callee cleans up the stack
- alignment: 4 bytes
Return values:
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
- integers > 32 bits are returned via the eax and edx registers
- floating pointer types are returned via the st0 register
*/
class x86MsFastcall: public x86MsStdcall
{
public:
x86MsFastcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
};
#endif // _X86_MS_FASTCALL_H
@@ -0,0 +1,194 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86MsStdcall.h"
#include <string.h>
// ============================================================================
// >> x86MsStdcall
// ============================================================================
x86MsStdcall::x86MsStdcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
ICallingConvention(vecArgTypes, returnType, iAlignment)
{
if (m_returnType.size > 4)
{
m_pReturnBuffer = malloc(m_returnType.size);
}
else
{
m_pReturnBuffer = NULL;
}
}
x86MsStdcall::~x86MsStdcall()
{
if (m_pReturnBuffer)
{
free(m_pReturnBuffer);
}
}
std::vector<Register_t> x86MsStdcall::GetRegisters()
{
std::vector<Register_t> registers;
registers.push_back(ESP);
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
{
registers.push_back(ST0);
}
else
{
registers.push_back(EAX);
if (m_pReturnBuffer)
{
registers.push_back(EDX);
}
}
// Save all the custom calling convention registers as well.
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
continue;
// TODO: Make sure the list is unique? Set?
registers.push_back(m_vecArgTypes[i].custom_register);
}
return registers;
}
int x86MsStdcall::GetPopSize()
{
int iPopSize = 0;
for(size_t i=0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
iPopSize += m_vecArgTypes[i].size;
}
return iPopSize;
}
int x86MsStdcall::GetArgStackSize()
{
int iArgStackSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
iArgStackSize += m_vecArgTypes[i].size;
}
return iArgStackSize;
}
void** x86MsStdcall::GetStackArgumentPtr(CRegisters* pRegisters)
{
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
}
int x86MsStdcall::GetArgRegisterSize()
{
int iArgRegisterSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register != None)
iArgRegisterSize += m_vecArgTypes[i].size;
}
return iArgRegisterSize;
}
void* x86MsStdcall::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
{
if (iIndex >= m_vecArgTypes.size())
return NULL;
// Check if this argument was passed in a register.
if (m_vecArgTypes[iIndex].custom_register != None)
{
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
if (!pRegister)
return NULL;
return pRegister->m_pAddress;
}
size_t iOffset = 4;
for (unsigned int i = 0; i < iIndex; i++)
{
if (m_vecArgTypes[i].custom_register == None)
iOffset += m_vecArgTypes[i].size;
}
return (void *)(pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
}
void x86MsStdcall::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
{
}
void* x86MsStdcall::GetReturnPtr(CRegisters* pRegisters)
{
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
return pRegisters->m_st0->m_pAddress;
if (m_pReturnBuffer)
{
// First half in eax, second half in edx
memcpy(m_pReturnBuffer, pRegisters->m_eax, 4);
memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4);
return m_pReturnBuffer;
}
return pRegisters->m_eax->m_pAddress;
}
void x86MsStdcall::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr)
{
if (m_pReturnBuffer)
{
// First half in eax, second half in edx
memcpy(pRegisters->m_eax, m_pReturnBuffer, 4);
memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4);
}
}
@@ -0,0 +1,88 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _X86_MS_STDCALL_H
#define _X86_MS_STDCALL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "../convention.h"
// ============================================================================
// >> CLASSES
// ============================================================================
/*
Source: DynCall manual and Windows docs
Registers:
- eax = return value
- edx = return value
- esp = stack pointer
- st0 = floating point return value
Parameter passing:
- stack parameter order: right-to-left
- callee cleans up the stack
- all arguments are pushed onto the stack
- alignment: 4 bytes
Return values:
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
- integers > 32 bits are returned via the eax and edx registers
- floating pointer types are returned via the st0 register
*/
class x86MsStdcall: public ICallingConvention
{
public:
x86MsStdcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
virtual ~x86MsStdcall();
virtual std::vector<Register_t> GetRegisters();
virtual int GetPopSize();
virtual int GetArgStackSize();
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
virtual int GetArgRegisterSize();
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters);
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr);
virtual void* GetReturnPtr(CRegisters* pRegisters);
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr);
private:
void* m_pReturnBuffer;
};
#endif // _X86_MS_STDCALL_H
@@ -0,0 +1,239 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86MsThiscall.h"
#include <string.h>
// ============================================================================
// >> x86MsThiscall
// ============================================================================
x86MsThiscall::x86MsThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
ICallingConvention(vecArgTypes, returnType, iAlignment)
{
if (m_returnType.size > 4)
{
m_pReturnBuffer = malloc(m_returnType.size);
}
else
{
m_pReturnBuffer = NULL;
}
}
x86MsThiscall::~x86MsThiscall()
{
if (m_pReturnBuffer)
{
free(m_pReturnBuffer);
}
}
std::vector<Register_t> x86MsThiscall::GetRegisters()
{
std::vector<Register_t> registers;
registers.push_back(ESP);
// TODO: Allow custom this register.
registers.push_back(ECX);
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
{
registers.push_back(ST0);
}
else
{
registers.push_back(EAX);
if (m_pReturnBuffer)
{
registers.push_back(EDX);
}
}
// Save all the custom calling convention registers as well.
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
continue;
// TODO: Make sure the list is unique? Set?
registers.push_back(m_vecArgTypes[i].custom_register);
}
return registers;
}
int x86MsThiscall::GetPopSize()
{
// This pointer.
// FIXME LINUX
//int iPopSize = GetDataTypeSize(DATA_TYPE_POINTER, m_iAlignment);
int iPopSize = 0;
for(size_t i=0; i < m_vecArgTypes.size(); i++)
{
// Only pop arguments that are actually on the stack.
if (m_vecArgTypes[i].custom_register == None)
iPopSize += m_vecArgTypes[i].size;
}
return iPopSize;
}
int x86MsThiscall::GetArgStackSize()
{
int iArgStackSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
iArgStackSize += m_vecArgTypes[i].size;
}
return iArgStackSize;
}
void** x86MsThiscall::GetStackArgumentPtr(CRegisters* pRegisters)
{
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
}
int x86MsThiscall::GetArgRegisterSize()
{
int iArgRegisterSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register != None)
iArgRegisterSize += m_vecArgTypes[i].size;
}
return iArgRegisterSize;
}
void* x86MsThiscall::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
{
if (iIndex == 0)
{
// TODO: Allow custom this register.
return pRegisters->m_ecx->m_pAddress;
}
// The this pointer isn't explicitly defined as an argument.
iIndex--;
if (iIndex >= m_vecArgTypes.size())
return NULL;
// Check if this argument was passed in a register.
if (m_vecArgTypes[iIndex].custom_register != None)
{
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
if (!pRegister)
return NULL;
return pRegister->m_pAddress;
}
size_t iOffset = 4;
for(unsigned int i=0; i < iIndex; i++)
{
if (m_vecArgTypes[i].custom_register == None)
iOffset += m_vecArgTypes[i].size;
}
return (void *) (pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
}
void x86MsThiscall::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
{
}
void* x86MsThiscall::GetReturnPtr(CRegisters* pRegisters)
{
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
return pRegisters->m_st0->m_pAddress;
if (m_pReturnBuffer)
{
// First half in eax, second half in edx
memcpy(m_pReturnBuffer, pRegisters->m_eax, 4);
memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4);
return m_pReturnBuffer;
}
return pRegisters->m_eax->m_pAddress;
}
void x86MsThiscall::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr)
{
if (m_pReturnBuffer)
{
// First half in eax, second half in edx
memcpy(pRegisters->m_eax, m_pReturnBuffer, 4);
memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4);
}
}
void x86MsThiscall::SaveCallArguments(CRegisters* pRegisters)
{
// Account for implicit this-pointer in ecx.
int size = GetArgStackSize() + GetArgRegisterSize() + sizeof(void *);
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
memcpy(pSavedCallArguments.get(), GetArgumentPtr(0, pRegisters), sizeof(void *));
size_t offset = sizeof(void *);
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i + 1, pRegisters), type.size);
offset += type.size;
}
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
}
void x86MsThiscall::RestoreCallArguments(CRegisters* pRegisters)
{
uint8_t* pSavedCallArguments = m_pSavedCallArguments.back().get();
memcpy(GetArgumentPtr(0, pRegisters), pSavedCallArguments, sizeof(void *));
size_t offset = sizeof(void *);
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy(GetArgumentPtr(i + 1, pRegisters), (void *)((unsigned long)pSavedCallArguments + offset), type.size);
offset += type.size;
}
m_pSavedCallArguments.pop_back();
}
@@ -0,0 +1,93 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _X86_MS_THISCALL_H
#define _X86_MS_THISCALL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "../convention.h"
#include <am-vector.h>
// ============================================================================
// >> CLASSES
// ============================================================================
/*
Source: DynCall manual and Windows docs
Registers:
- eax = return value
- ecx = this pointer
- edx = return value
- esp = stack pointer
- st0 = floating point return value
Parameter passing:
- stack parameter order: right-to-left
- callee cleans up the stack
- all other arguments are pushed onto the stack
- alignment: 4 bytes
Return values:
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
- integers > 32 bits are returned via the eax and edx registers
- floating pointer types are returned via the st0 register
*/
class x86MsThiscall: public ICallingConvention
{
public:
x86MsThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
virtual ~x86MsThiscall();
virtual std::vector<Register_t> GetRegisters();
virtual int GetPopSize();
virtual int GetArgStackSize();
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
virtual int GetArgRegisterSize();
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters);
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr);
virtual void* GetReturnPtr(CRegisters* pRegisters);
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr);
virtual void SaveCallArguments(CRegisters* pRegisters);
virtual void RestoreCallArguments(CRegisters* pRegisters);
private:
void* m_pReturnBuffer;
};
#endif // _X86_MS_THISCALL_H
+504
View File
@@ -0,0 +1,504 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "hook.h"
#include <asm/asm.h>
#include <macro-assembler-x86.h>
#include "extension.h"
#include <jit/jit_helpers.h>
#include <CDetour/detourhelpers.h>
using namespace sp;
// ============================================================================
// >> DEFINITIONS
// ============================================================================
#define JMP_SIZE 6
// ============================================================================
// >> CHook
// ============================================================================
CHook::CHook(void* pFunc, ICallingConvention* pConvention)
{
m_pFunc = pFunc;
m_pRegisters = new CRegisters(pConvention->GetRegisters());
m_pCallingConvention = pConvention;
if (!m_hookHandler.init())
return;
if (!m_RetAddr.init())
return;
unsigned char* pTarget = (unsigned char *) pFunc;
// Determine the number of bytes we need to copy
int iBytesToCopy = copy_bytes(pTarget, NULL, JMP_SIZE);
// Create a buffer for the bytes to copy + a jump to the rest of the
// function.
unsigned char* pCopiedBytes = (unsigned char *) smutils->GetScriptingEngine()->AllocatePageMemory(iBytesToCopy + JMP_SIZE);
// Fill the array with NOP instructions
memset(pCopiedBytes, 0x90, iBytesToCopy + JMP_SIZE);
// Copy the required bytes to our array
copy_bytes(pTarget, pCopiedBytes, JMP_SIZE);
// Write a jump after the copied bytes to the function/bridge + number of bytes to copy
DoGatePatch(pCopiedBytes + iBytesToCopy, pTarget + iBytesToCopy);
// Save the trampoline
m_pTrampoline = (void *) pCopiedBytes;
// Create the bridge function
m_pBridge = CreateBridge();
// Write a jump to the bridge
DoGatePatch((unsigned char *) pFunc, m_pBridge);
}
CHook::~CHook()
{
// Copy back the previously copied bytes
copy_bytes((unsigned char *) m_pTrampoline, (unsigned char *) m_pFunc, JMP_SIZE);
// Free the trampoline buffer
smutils->GetScriptingEngine()->FreePageMemory(m_pTrampoline);
// Free the asm bridge and new return address
smutils->GetScriptingEngine()->FreePageMemory(m_pBridge);
smutils->GetScriptingEngine()->FreePageMemory(m_pNewRetAddr);
delete m_pRegisters;
delete m_pCallingConvention;
}
void CHook::AddCallback(HookType_t eHookType, HookHandlerFn* pCallback)
{
if (!pCallback)
return;
HookTypeMap::Insert i = m_hookHandler.findForAdd(eHookType);
if (!i.found()) {
HookHandlerSet set;
set.init();
m_hookHandler.add(i, eHookType, std::move(set));
}
i->value.add(pCallback);
}
void CHook::RemoveCallback(HookType_t eHookType, HookHandlerFn* pCallback)
{
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
return;
r->value.removeIfExists(pCallback);
}
bool CHook::IsCallbackRegistered(HookType_t eHookType, HookHandlerFn* pCallback)
{
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
return false;
return r->value.has(pCallback);
}
bool CHook::AreCallbacksRegistered()
{
HookTypeMap::Result r = m_hookHandler.find(HOOKTYPE_PRE);
if (r.found() && r->value.elements() > 0)
return true;
r = m_hookHandler.find(HOOKTYPE_POST);
if (r.found() && r->value.elements() > 0)
return true;
return false;
}
ReturnAction_t CHook::HookHandler(HookType_t eHookType)
{
if (eHookType == HOOKTYPE_POST)
{
ReturnAction_t lastPreReturnAction = m_LastPreReturnAction.back();
m_LastPreReturnAction.pop_back();
if (lastPreReturnAction == ReturnAction_Override)
m_pCallingConvention->RestoreReturnValue(m_pRegisters);
if (lastPreReturnAction < ReturnAction_Supercede)
m_pCallingConvention->RestoreCallArguments(m_pRegisters);
}
ReturnAction_t returnAction = ReturnAction_Ignored;
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
{
// Still save the arguments for the post hook even if there
// is no pre-handler registered.
if (eHookType == HOOKTYPE_PRE)
{
m_LastPreReturnAction.push_back(returnAction);
m_pCallingConvention->SaveCallArguments(m_pRegisters);
}
return returnAction;
}
HookHandlerSet &callbacks = r->value;
for(HookHandlerSet::iterator it=callbacks.iter(); !it.empty(); it.next())
{
ReturnAction_t result = ((HookHandlerFn) *it)(eHookType, this);
if (result > returnAction)
returnAction = result;
}
if (eHookType == HOOKTYPE_PRE)
{
m_LastPreReturnAction.push_back(returnAction);
if (returnAction == ReturnAction_Override)
m_pCallingConvention->SaveReturnValue(m_pRegisters);
if (returnAction < ReturnAction_Supercede)
m_pCallingConvention->SaveCallArguments(m_pRegisters);
}
return returnAction;
}
void* __cdecl CHook::GetReturnAddress(void* pESP)
{
ReturnAddressMap::Result r = m_RetAddr.find(pESP);
assert(r.found());
if (!r.found())
{
smutils->LogError(myself, "FATAL: Failed to find return address of original function. Check the arguments and return type of your detour setup.");
return NULL;
}
void *pRetAddr = r->value.back();
r->value.pop_back();
// Clear the stack address from the cache now that we ran the last post hook.
if (r->value.empty())
m_RetAddr.remove(r);
return pRetAddr;
}
void __cdecl CHook::SetReturnAddress(void* pRetAddr, void* pESP)
{
ReturnAddressMap::Insert i = m_RetAddr.findForAdd(pESP);
if (!i.found())
m_RetAddr.add(i, pESP, std::vector<void *>());
i->value.push_back(pRetAddr);
}
void* CHook::CreateBridge()
{
sp::MacroAssembler masm;
Label label_supercede;
// Write a redirect to the post-hook code
Write_ModifyReturnAddress(masm);
// Call the pre-hook handler and jump to label_supercede if ReturnAction_Supercede was returned
Write_CallHandler(masm, HOOKTYPE_PRE);
masm.cmpb(r8_al, ReturnAction_Supercede);
// Restore the previously saved registers, so any changes will be applied
Write_RestoreRegisters(masm, HOOKTYPE_PRE);
masm.j(equal, &label_supercede);
// Jump to the trampoline
masm.jmp(ExternalAddress(m_pTrampoline));
// This code will be executed if a pre-hook returns ReturnAction_Supercede
masm.bind(&label_supercede);
// Finally, return to the caller
// This will still call post hooks, but will skip the original function.
masm.ret(m_pCallingConvention->GetPopSize());
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
masm.emitToExecutableMemory(base);
return base;
}
void CHook::Write_ModifyReturnAddress(sp::MacroAssembler& masm)
{
// Save scratch registers that are used by SetReturnAddress
static void* pEAX = NULL;
static void* pECX = NULL;
static void* pEDX = NULL;
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
// Store the return address in eax
masm.movl(eax, Operand(esp, 0));
// Save the original return address by using the current esp as the key.
// This should be unique until we have returned to the original caller.
void (__cdecl CHook::*SetReturnAddress)(void*, void*) = &CHook::SetReturnAddress;
masm.push(esp);
masm.push(eax);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)SetReturnAddress));
masm.addl(esp, 12);
// Restore scratch registers
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
// Override the return address. This is a redirect to our post-hook code
m_pNewRetAddr = CreatePostCallback();
masm.movl(Operand(esp, 0), intptr_t(m_pNewRetAddr));
}
void* CHook::CreatePostCallback()
{
sp::MacroAssembler masm;
int iPopSize = m_pCallingConvention->GetPopSize();
// Subtract the previously added bytes (stack size + return address), so
// that we can access the arguments again
masm.subl(esp, iPopSize+4);
// Call the post-hook handler
Write_CallHandler(masm, HOOKTYPE_POST);
// Restore the previously saved registers, so any changes will be applied
Write_RestoreRegisters(masm, HOOKTYPE_POST);
// Save scratch registers that are used by GetReturnAddress
static void* pEAX = NULL;
static void* pECX = NULL;
static void* pEDX = NULL;
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
// Get the original return address
void* (__cdecl CHook::*GetReturnAddress)(void*) = &CHook::GetReturnAddress;
masm.push(esp);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)GetReturnAddress));
masm.addl(esp, 8);
// Save the original return address
static void* pRetAddr = NULL;
masm.movl(Operand(ExternalAddress(&pRetAddr)), eax);
// Restore scratch registers
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
// Add the bytes again to the stack (stack size + return address), so we
// don't corrupt the stack.
masm.addl(esp, iPopSize+4);
// Jump to the original return address
masm.jmp(Operand(ExternalAddress(&pRetAddr)));
// Generate the code
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
masm.emitToExecutableMemory(base);
return base;
}
void CHook::Write_CallHandler(sp::MacroAssembler& masm, HookType_t type)
{
ReturnAction_t (__cdecl CHook::*HookHandler)(HookType_t) = &CHook::HookHandler;
// Save the registers so that we can access them in our handlers
Write_SaveRegisters(masm, type);
// Align the stack to 16 bytes.
masm.subl(esp, 4);
// Call the global hook handler
masm.push(type);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)HookHandler));
masm.addl(esp, 12);
}
void CHook::Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type)
{
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
for(size_t i = 0; i < vecRegistersToSave.size(); i++)
{
switch(vecRegistersToSave[i])
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
case AL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress)), r8_al); break;
case CL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress)), r8_cl); break;
case DL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress)), r8_dl); break;
case BL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress)), r8_bl); break;
case AH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress)), r8_ah); break;
case CH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress)), r8_ch); break;
case DH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress)), r8_dh); break;
case BH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress)), r8_bh); break;
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
case EAX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress)), eax); break;
case ECX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress)), ecx); break;
case EDX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress)), edx); break;
case EBX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress)), ebx); break;
case ESP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress)), esp); break;
case EBP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress)), ebp); break;
case ESI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress)), esi); break;
case EDI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress)), edi); break;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
// TODO: Also provide movups?
case XMM0: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress)), xmm0); break;
case XMM1: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress)), xmm1); break;
case XMM2: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress)), xmm2); break;
case XMM3: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress)), xmm3); break;
case XMM4: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress)), xmm4); break;
case XMM5: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress)), xmm5); break;
case XMM6: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress)), xmm6); break;
case XMM7: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress)), xmm7); break;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
case ST0:
// Don't mess with the FPU stack in a pre-hook. The float return is returned in st0,
// so only load it in a post hook to avoid writing back NaN.
if (type == HOOKTYPE_POST)
masm.fst32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
break;
//case ST1: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress)), st1); break;
//case ST2: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress)), st2); break;
//case ST3: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress)), st3); break;
//case ST4: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress)), st4); break;
//case ST5: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress)), st5); break;
//case ST6: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress)), st6); break;
//case ST7: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress)), st7); break;
default: puts("Unsupported register.");
}
}
}
void CHook::Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type)
{
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
for (size_t i = 0; i < vecRegistersToSave.size(); i++)
{
switch (vecRegistersToSave[i])
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
case AL: masm.movl(r8_al, Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress))); break;
case CL: masm.movl(r8_cl, Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress))); break;
case DL: masm.movl(r8_dl, Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress))); break;
case BL: masm.movl(r8_bl, Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress))); break;
case AH: masm.movl(r8_ah, Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress))); break;
case CH: masm.movl(r8_ch, Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress))); break;
case DH: masm.movl(r8_dh, Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress))); break;
case BH: masm.movl(r8_bh, Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress))); break;
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
case EAX: masm.movl(eax, Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress))); break;
case ECX: masm.movl(ecx, Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress))); break;
case EDX: masm.movl(edx, Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress))); break;
case EBX: masm.movl(ebx, Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress))); break;
case ESP: masm.movl(esp, Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress))); break;
case EBP: masm.movl(ebp, Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress))); break;
case ESI: masm.movl(esi, Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress))); break;
case EDI: masm.movl(edi, Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress))); break;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
// TODO: Also provide movups?
case XMM0: masm.movaps(xmm0, Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress))); break;
case XMM1: masm.movaps(xmm1, Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress))); break;
case XMM2: masm.movaps(xmm2, Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress))); break;
case XMM3: masm.movaps(xmm3, Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress))); break;
case XMM4: masm.movaps(xmm4, Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress))); break;
case XMM5: masm.movaps(xmm5, Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress))); break;
case XMM6: masm.movaps(xmm6, Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress))); break;
case XMM7: masm.movaps(xmm7, Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress))); break;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
case ST0:
if (type == HOOKTYPE_POST) {
// Replace the top of the FPU stack.
// Copy st0 to st0 and pop -> just pop the FPU stack.
masm.fstp(st0);
// Push a value to the FPU stack.
// TODO: Only write back when changed? Save full 80bits for that case.
// Avoid truncation of the data if it's unchanged.
masm.fld32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
}
break;
//case ST1: masm.movl(st1, tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress))); break;
//case ST2: masm.movl(st2, tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress))); break;
//case ST3: masm.movl(st3, tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress))); break;
//case ST4: masm.movl(st4, tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress))); break;
//case ST5: masm.movl(st5, tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress))); break;
//case ST6: masm.movl(st6, tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress))); break;
//case ST7: masm.movl(st7, tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress))); break;
default: puts("Unsupported register.");
}
}
}
+217
View File
@@ -0,0 +1,217 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _HOOK_H
#define _HOOK_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "registers.h"
#include "convention.h"
#include <am-hashmap.h>
#include <am-hashset.h>
// ============================================================================
// >> HookType_t
// ============================================================================
enum HookType_t
{
// Callback will be executed before the original function.
HOOKTYPE_PRE,
// Callback will be executed after the original function.
HOOKTYPE_POST
};
enum ReturnAction_t
{
ReturnAction_Ignored, // handler didn't take any action
ReturnAction_Handled, // plugin did something, but real function should still be called
ReturnAction_Override, // call real function, but use my return value
ReturnAction_Supercede // skip real function; use my return value
};
// ============================================================================
// >> TYPEDEFS
// ============================================================================
class CHook;
typedef ReturnAction_t (*HookHandlerFn)(HookType_t, CHook*);
#ifdef __linux__
#define __cdecl
#endif
struct IntegerPolicy
{
static inline uint32_t hash(size_t i) {
return ke::HashInteger<sizeof(size_t)>(i);
}
static inline bool matches(size_t i1, size_t i2) {
return i1 == i2;
}
};
typedef ke::HashSet<HookHandlerFn*, ke::PointerPolicy<HookHandlerFn>> HookHandlerSet;
typedef ke::HashMap<HookType_t, HookHandlerSet, IntegerPolicy> HookTypeMap;
typedef ke::HashMap<void*, std::vector<void*>, ke::PointerPolicy<void>> ReturnAddressMap;
namespace sp
{
class MacroAssembler;
}
// ============================================================================
// >> CLASSES
// ============================================================================
class CHook
{
private:
friend class CHookManager;
/*
Creates a new function hook.
@param <pFunc>:
The address of the function to hook
@param <pConvention>:
The calling convention of <pFunc>.
*/
CHook(void* pFunc, ICallingConvention* pConvention);
~CHook();
public:
/*
Adds a hook handler to the hook.
@param type The hook type.
@param pFunc The hook handler that should be added.
*/
void AddCallback(HookType_t type, HookHandlerFn* pFunc);
/*
Removes a hook handler to the hook.
@param type The hook type.
@param pFunc The hook handler that should be removed.
*/
void RemoveCallback(HookType_t type, HookHandlerFn* pFunc);
/*
Checks if a hook handler is already added.
@param type The hook type.
@param pFunc The hook handler that should be checked.
*/
bool IsCallbackRegistered(HookType_t type, HookHandlerFn* pFunc);
/*
Checks if there are any hook handlers added to this hook.
*/
bool AreCallbacksRegistered();
template<class T>
T GetArgument(int iIndex)
{
return *(T *) m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
}
template<class T>
void SetArgument(int iIndex, T value)
{
void* pPtr = m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
*(T *) pPtr = value;
m_pCallingConvention->ArgumentPtrChanged(iIndex, m_pRegisters, pPtr);
}
template<class T>
T GetReturnValue()
{
return *(T *) m_pCallingConvention->GetReturnPtr(m_pRegisters);
}
template<class T>
void SetReturnValue(T value)
{
void* pPtr = m_pCallingConvention->GetReturnPtr(m_pRegisters);
*(T *) pPtr = value;
m_pCallingConvention->ReturnPtrChanged(m_pRegisters, pPtr);
}
private:
void* CreateBridge();
void Write_ModifyReturnAddress(sp::MacroAssembler& masm);
void Write_CallHandler(sp::MacroAssembler& masm, HookType_t type);
void Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type);
void Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type);
void* CreatePostCallback();
ReturnAction_t __cdecl HookHandler(HookType_t type);
void* __cdecl GetReturnAddress(void* pESP);
void __cdecl SetReturnAddress(void* pRetAddr, void* pESP);
public:
HookTypeMap m_hookHandler;
// Address of the original function
void* m_pFunc;
ICallingConvention* m_pCallingConvention;
// Address of the bridge
void* m_pBridge;
// Address of the trampoline
void* m_pTrampoline;
// Register storage
CRegisters* m_pRegisters;
// New return address
void* m_pNewRetAddr;
ReturnAddressMap m_RetAddr;
// Save the last return action of the pre HookHandler for use in the post handler.
std::vector<ReturnAction_t> m_LastPreReturnAction;
};
#endif // _HOOK_H
+107
View File
@@ -0,0 +1,107 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "manager.h"
// ============================================================================
// >> CHookManager
// ============================================================================
CHook* CHookManager::HookFunction(void* pFunc, ICallingConvention* pConvention)
{
if (!pFunc)
return NULL;
CHook* pHook = FindHook(pFunc);
if (pHook)
{
delete pConvention;
return pHook;
}
pHook = new CHook(pFunc, pConvention);
m_Hooks.push_back(pHook);
return pHook;
}
void CHookManager::UnhookFunction(void* pFunc)
{
if (!pFunc)
return;
for (size_t i = 0; i < m_Hooks.size(); i++)
{
CHook* pHook = m_Hooks[i];
if (pHook->m_pFunc == pFunc)
{
m_Hooks.erase(m_Hooks.begin() + i);
delete pHook;
return;
}
}
}
CHook* CHookManager::FindHook(void* pFunc)
{
if (!pFunc)
return NULL;
for(size_t i = 0; i < m_Hooks.size(); i++)
{
CHook* pHook = m_Hooks[i];
if (pHook->m_pFunc == pFunc)
return pHook;
}
return NULL;
}
void CHookManager::UnhookAllFunctions()
{
for(size_t i = 0; i < m_Hooks.size(); i++)
delete m_Hooks[i];
m_Hooks.clear();
}
// ============================================================================
// >> GetHookManager
// ============================================================================
CHookManager* GetHookManager()
{
static CHookManager* s_pManager = new CHookManager;
return s_pManager;
}
+86
View File
@@ -0,0 +1,86 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _MANAGER_H
#define _MANAGER_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "hook.h"
#include "convention.h"
#include <vector>
// ============================================================================
// >> CHookManager
// ============================================================================
class CHookManager
{
public:
/*
Hooks the given function and returns a new CHook instance. If the
function was already hooked, the existing CHook instance will be
returned.
*/
CHook* HookFunction(void* pFunc, ICallingConvention* pConvention);
/*
Removes all callbacks and restores the original function.
*/
void UnhookFunction(void* pFunc);
/*
Returns either NULL or the found CHook instance.
*/
CHook* FindHook(void* pFunc);
/*
Removes all callbacks and restores all functions.
*/
void UnhookAllFunctions();
public:
std::vector<CHook *> m_Hooks;
};
// ============================================================================
// >> GetHookManager
// ============================================================================
/*
Returns a pointer to a static CHookManager object.
*/
CHookManager* GetHookManager();
#endif // _MANAGER_H
@@ -0,0 +1,509 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#include "registers.h"
CRegisters::CRegisters(std::vector<Register_t> registers)
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
m_al = CreateRegister(registers, AL, 1);
m_cl = CreateRegister(registers, CL, 1);
m_dl = CreateRegister(registers, DL, 1);
m_bl = CreateRegister(registers, BL, 1);
// 64-bit mode only
/*
m_spl = CreateRegister(registers, SPL, 1);
m_bpl = CreateRegister(registers, BPL, 1);
m_sil = CreateRegister(registers, SIL, 1);
m_dil = CreateRegister(registers, DIL, 1);
m_r8b = CreateRegister(registers, R8B, 1);
m_r9b = CreateRegister(registers, R9B, 1);
m_r10b = CreateRegister(registers, R10B, 1);
m_r11b = CreateRegister(registers, R11B, 1);
m_r12b = CreateRegister(registers, R12B, 1);
m_r13b = CreateRegister(registers, R13B, 1);
m_r14b = CreateRegister(registers, R14B, 1);
m_r15b = CreateRegister(registers, R15B, 1);
*/
m_ah = CreateRegister(registers, AH, 1);
m_ch = CreateRegister(registers, CH, 1);
m_dh = CreateRegister(registers, DH, 1);
m_bh = CreateRegister(registers, BH, 1);
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
m_ax = CreateRegister(registers, AX, 2);
m_cx = CreateRegister(registers, CX, 2);
m_dx = CreateRegister(registers, DX, 2);
m_bx = CreateRegister(registers, BX, 2);
m_sp = CreateRegister(registers, SP, 2);
m_bp = CreateRegister(registers, BP, 2);
m_si = CreateRegister(registers, SI, 2);
m_di = CreateRegister(registers, DI, 2);
// 64-bit mode only
/*
m_r8w = CreateRegister(registers, R8W, 2);
m_r9w = CreateRegister(registers, R9W, 2);
m_r10w = CreateRegister(registers, R10W, 2);
m_r11w = CreateRegister(registers, R11W, 2);
m_r12w = CreateRegister(registers, R12W, 2);
m_r13w = CreateRegister(registers, R13W, 2);
m_r14w = CreateRegister(registers, R14W, 2);
m_r15w = CreateRegister(registers, R14W, 2);
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
m_eax = CreateRegister(registers, EAX, 4);
m_ecx = CreateRegister(registers, ECX, 4);
m_edx = CreateRegister(registers, EDX, 4);
m_ebx = CreateRegister(registers, EBX, 4);
m_esp = CreateRegister(registers, ESP, 4);
m_ebp = CreateRegister(registers, EBP, 4);
m_esi = CreateRegister(registers, ESI, 4);
m_edi = CreateRegister(registers, EDI, 4);
// 64-bit mode only
/*
m_r8d = CreateRegister(registers, R8D, 4);
m_r9d = CreateRegister(registers, R9D, 4);
m_r10d = CreateRegister(registers, R10D, 4);
m_r11d = CreateRegister(registers, R11D, 4);
m_r12d = CreateRegister(registers, R12D, 4);
m_r13d = CreateRegister(registers, R13D, 4);
m_r14d = CreateRegister(registers, R14D, 4);
m_r15d = CreateRegister(registers, R15D, 4);
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
m_rax = CreateRegister(registers, RAX, 8);
m_rcx = CreateRegister(registers, RCX, 8);
m_rdx = CreateRegister(registers, RDX, 8);
m_rbx = CreateRegister(registers, RBX, 8);
m_rsp = CreateRegister(registers, RSP, 8);
m_rbp = CreateRegister(registers, RBP, 8);
m_rsi = CreateRegister(registers, RSI, 8);
m_rdi = CreateRegister(registers, RDI, 8);
*/
// 64-bit mode only
/*
m_r8 = CreateRegister(registers, R8, 8);
m_r9 = CreateRegister(registers, R9, 8);
m_r10 = CreateRegister(registers, R10, 8);
m_r11 = CreateRegister(registers, R11, 8);
m_r12 = CreateRegister(registers, R12, 8);
m_r13 = CreateRegister(registers, R13, 8);
m_r14 = CreateRegister(registers, R14, 8);
m_r15 = CreateRegister(registers, R15, 8);
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
m_mm0 = CreateRegister(registers, MM0, 8);
m_mm1 = CreateRegister(registers, MM1, 8);
m_mm2 = CreateRegister(registers, MM2, 8);
m_mm3 = CreateRegister(registers, MM3, 8);
m_mm4 = CreateRegister(registers, MM4, 8);
m_mm5 = CreateRegister(registers, MM5, 8);
m_mm6 = CreateRegister(registers, MM6, 8);
m_mm7 = CreateRegister(registers, MM7, 8);
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
// Copying data from xmm0-7 requires the memory address to be 16-byte aligned.
m_xmm0 = CreateRegister(registers, XMM0, 16, 16);
m_xmm1 = CreateRegister(registers, XMM1, 16, 16);
m_xmm2 = CreateRegister(registers, XMM2, 16, 16);
m_xmm3 = CreateRegister(registers, XMM3, 16, 16);
m_xmm4 = CreateRegister(registers, XMM4, 16, 16);
m_xmm5 = CreateRegister(registers, XMM5, 16, 16);
m_xmm6 = CreateRegister(registers, XMM6, 16, 16);
m_xmm7 = CreateRegister(registers, XMM7, 16, 16);
// 64-bit mode only
/*
m_xmm8 = CreateRegister(registers, XMM8, 16);
m_xmm9 = CreateRegister(registers, XMM9, 16);
m_xmm10 = CreateRegister(registers, XMM10, 16);
m_xmm11 = CreateRegister(registers, XMM11, 16);
m_xmm12 = CreateRegister(registers, XMM12, 16);
m_xmm13 = CreateRegister(registers, XMM13, 16);
m_xmm14 = CreateRegister(registers, XMM14, 16);
m_xmm15 = CreateRegister(registers, XMM15, 16);
*/
// ========================================================================
// >> 16-bit Segment registers
// ========================================================================
m_cs = CreateRegister(registers, CS, 2);
m_ss = CreateRegister(registers, SS, 2);
m_ds = CreateRegister(registers, DS, 2);
m_es = CreateRegister(registers, ES, 2);
m_fs = CreateRegister(registers, FS, 2);
m_gs = CreateRegister(registers, GS, 2);
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
m_st0 = CreateRegister(registers, ST0, 10);
m_st1 = CreateRegister(registers, ST1, 10);
m_st2 = CreateRegister(registers, ST2, 10);
m_st3 = CreateRegister(registers, ST3, 10);
m_st4 = CreateRegister(registers, ST4, 10);
m_st5 = CreateRegister(registers, ST5, 10);
m_st6 = CreateRegister(registers, ST6, 10);
m_st7 = CreateRegister(registers, ST7, 10);
}
CRegisters::~CRegisters()
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
DeleteRegister(m_al);
DeleteRegister(m_cl);
DeleteRegister(m_dl);
DeleteRegister(m_bl);
// 64-bit mode only
/*
DeleteRegister(m_spl);
DeleteRegister(m_bpl);
DeleteRegister(m_sil);
DeleteRegister(m_dil);
DeleteRegister(m_r8b);
DeleteRegister(m_r9b);
DeleteRegister(m_r10b);
DeleteRegister(m_r11b);
DeleteRegister(m_r12b);
DeleteRegister(m_r13b);
DeleteRegister(m_r14b);
DeleteRegister(m_r15b);
*/
DeleteRegister(m_ah);
DeleteRegister(m_ch);
DeleteRegister(m_dh);
DeleteRegister(m_bh);
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
DeleteRegister(m_ax);
DeleteRegister(m_cx);
DeleteRegister(m_dx);
DeleteRegister(m_bx);
DeleteRegister(m_sp);
DeleteRegister(m_bp);
DeleteRegister(m_si);
DeleteRegister(m_di);
// 64-bit mode only
/*
DeleteRegister(m_r8w);
DeleteRegister(m_r9w);
DeleteRegister(m_r10w);
DeleteRegister(m_r11w);
DeleteRegister(m_r12w);
DeleteRegister(m_r13w);
DeleteRegister(m_r14w);
DeleteRegister(m_r15w);
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
DeleteRegister(m_eax);
DeleteRegister(m_ecx);
DeleteRegister(m_edx);
DeleteRegister(m_ebx);
DeleteRegister(m_esp);
DeleteRegister(m_ebp);
DeleteRegister(m_esi);
DeleteRegister(m_edi);
// 64-bit mode only
/*
DeleteRegister(m_r8d);
DeleteRegister(m_r9d);
DeleteRegister(m_r10d);
DeleteRegister(m_r11d);
DeleteRegister(m_r12d);
DeleteRegister(m_r13d);
DeleteRegister(m_r14d);
DeleteRegister(m_r15d);
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
DeleteRegister(m_rax);
DeleteRegister(m_rcx);
DeleteRegister(m_rdx);
DeleteRegister(m_rbx);
DeleteRegister(m_rsp);
DeleteRegister(m_rbp);
DeleteRegister(m_rsi);
DeleteRegister(m_rdi);
*/
// 64-bit mode only
/*
DeleteRegister(m_r8);
DeleteRegister(m_r9);
DeleteRegister(m_r10);
DeleteRegister(m_r11);
DeleteRegister(m_r12);
DeleteRegister(m_r13);
DeleteRegister(m_r14);
DeleteRegister(m_r15);
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
DeleteRegister(m_mm0);
DeleteRegister(m_mm1);
DeleteRegister(m_mm2);
DeleteRegister(m_mm3);
DeleteRegister(m_mm4);
DeleteRegister(m_mm5);
DeleteRegister(m_mm6);
DeleteRegister(m_mm7);
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
DeleteRegister(m_xmm0);
DeleteRegister(m_xmm1);
DeleteRegister(m_xmm2);
DeleteRegister(m_xmm3);
DeleteRegister(m_xmm4);
DeleteRegister(m_xmm5);
DeleteRegister(m_xmm6);
DeleteRegister(m_xmm7);
// 64-bit mode only
/*
DeleteRegister(m_xmm8);
DeleteRegister(m_xmm9);
DeleteRegister(m_xmm10);
DeleteRegister(m_xmm11);
DeleteRegister(m_xmm12);
DeleteRegister(m_xmm13);
DeleteRegister(m_xmm14);
DeleteRegister(m_xmm15);
*/
// ========================================================================
// >> 2-bit Segment registers
// ========================================================================
DeleteRegister(m_cs);
DeleteRegister(m_ss);
DeleteRegister(m_ds);
DeleteRegister(m_es);
DeleteRegister(m_fs);
DeleteRegister(m_gs);
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
DeleteRegister(m_st0);
DeleteRegister(m_st1);
DeleteRegister(m_st2);
DeleteRegister(m_st3);
DeleteRegister(m_st4);
DeleteRegister(m_st5);
DeleteRegister(m_st6);
DeleteRegister(m_st7);
}
CRegister* CRegisters::CreateRegister(std::vector<Register_t>& registers, Register_t reg, uint16_t iSize, uint16_t iAlignment)
{
for(size_t i = 0; i < registers.size(); i++)
{
if (registers[i] == reg)
{
return new CRegister(iSize, iAlignment);
}
}
return NULL;
}
void CRegisters::DeleteRegister(CRegister* pRegister)
{
if (pRegister)
{
delete pRegister;
}
}
CRegister* CRegisters::GetRegister(Register_t reg)
{
switch (reg)
{
case AL:
return m_al;
case CL:
return m_cl;
case DL:
return m_dl;
case BL:
return m_bl;
case AH:
return m_ah;
case CH:
return m_ch;
case DH:
return m_dh;
case BH:
return m_bh;
case AX:
return m_ax;
case CX:
return m_cx;
case DX:
return m_dx;
case BX:
return m_bx;
case SP:
return m_sp;
case BP:
return m_bp;
case SI:
return m_si;
case DI:
return m_di;
case EAX:
return m_eax;
case ECX:
return m_ecx;
case EDX:
return m_edx;
case EBX:
return m_ebx;
case ESP:
return m_esp;
case EBP:
return m_ebp;
case ESI:
return m_esi;
case EDI:
return m_edi;
case MM0:
return m_mm0;
case MM1:
return m_mm1;
case MM2:
return m_mm2;
case MM3:
return m_mm3;
case MM4:
return m_mm4;
case MM5:
return m_mm5;
case MM6:
return m_mm6;
case MM7:
return m_mm7;
case XMM0:
return m_xmm0;
case XMM1:
return m_xmm1;
case XMM2:
return m_xmm2;
case XMM3:
return m_xmm3;
case XMM4:
return m_xmm4;
case XMM5:
return m_xmm5;
case XMM6:
return m_xmm6;
case XMM7:
return m_xmm7;
case CS:
return m_cs;
case SS:
return m_ss;
case DS:
return m_ds;
case ES:
return m_es;
case FS:
return m_fs;
case GS:
return m_gs;
case ST0:
return m_st0;
case ST1:
return m_st1;
case ST2:
return m_st2;
case ST3:
return m_st3;
case ST4:
return m_st4;
case ST5:
return m_st5;
case ST6:
return m_st6;
case ST7:
return m_st7;
default:
return NULL;
}
}
+462
View File
@@ -0,0 +1,462 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _REGISTERS_H
#define _REGISTERS_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include <vector>
#include <am-platform.h>
#include <cinttypes>
#include <cstdlib>
// ============================================================================
// >> Register_t
// ============================================================================
enum Register_t
{
// No register at all.
None,
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
AL,
CL,
DL,
BL,
// 64-bit mode only
/*
SPL,
BPL,
SIL,
DIL,
R8B,
R9B,
R10B,
R11B,
R12B,
R13B,
R14B,
R15B,
*/
AH,
CH,
DH,
BH,
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
AX,
CX,
DX,
BX,
SP,
BP,
SI,
DI,
// 64-bit mode only
/*
R8W,
R9W,
R10W,
R11W,
R12W,
R13W,
R14W,
R15W,
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
EAX,
ECX,
EDX,
EBX,
ESP,
EBP,
ESI,
EDI,
// 64-bit mode only
/*
R8D,
R9D,
R10D,
R11D,
R12D,
R13D,
R14D,
R15D,
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
RAX,
RCX,
RDX,
RBX,
RSP,
RBP,
RSI,
RDI,
*/
// 64-bit mode only
/*
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
MM0,
MM1,
MM2,
MM3,
MM4,
MM5,
MM6,
MM7,
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
// 64-bit mode only
/*
XMM8,
XMM9,
XMM10,
XMM11,
XMM12,
XMM13,
XMM14,
XMM15,
*/
// ========================================================================
// >> 16-bit Segment registers
// ========================================================================
CS,
SS,
DS,
ES,
FS,
GS,
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
ST0,
ST1,
ST2,
ST3,
ST4,
ST5,
ST6,
ST7,
};
// ============================================================================
// >> CRegister
// ============================================================================
class CRegister
{
public:
CRegister(uint16_t iSize, uint16_t iAlignment = 0)
{
m_iSize = iSize;
m_iAlignment = iAlignment;
if (iAlignment > 0)
#ifdef KE_WINDOWS
m_pAddress = _aligned_malloc(iSize, iAlignment);
#else
m_pAddress = aligned_alloc(iAlignment, iSize);
#endif
else
m_pAddress = malloc(iSize);
}
~CRegister()
{
#ifdef KE_WINDOWS
if (m_iAlignment > 0)
_aligned_free(m_pAddress);
else
free(m_pAddress);
#else
free(m_pAddress);
#endif
}
template<class T>
T GetValue()
{
return *(T *) m_pAddress;
}
template<class T>
T GetPointerValue(int iOffset=0)
{
return *(T *) (GetValue<unsigned long>() + iOffset);
}
template<class T>
void SetValue(T value)
{
*(T *) m_pAddress = value;
}
template<class T>
void SetPointerValue(T value, int iOffset=0)
{
*(T *) (GetValue<unsigned long>() + iOffset) = value;
}
public:
uint16_t m_iSize;
uint16_t m_iAlignment;
void* m_pAddress;
};
// ============================================================================
// >> CRegisters
// ============================================================================
class CRegisters
{
public:
CRegisters(std::vector<Register_t> registers);
~CRegisters();
CRegister* GetRegister(Register_t reg);
private:
CRegister* CreateRegister(std::vector<Register_t>& registers, Register_t reg, uint16_t iSize, uint16_t iAlignment = 0);
void DeleteRegister(CRegister* pRegister);
public:
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
CRegister* m_al;
CRegister* m_cl;
CRegister* m_dl;
CRegister* m_bl;
// 64-bit mode only
/*
CRegister* m_spl;
CRegister* m_bpl;
CRegister* m_sil;
CRegister* m_dil;
CRegister* m_r8b;
CRegister* m_r9b;
CRegister* m_r10b;
CRegister* m_r11b;
CRegister* m_r12b;
CRegister* m_r13b;
CRegister* m_r14b;
CRegister* m_r15b;
*/
CRegister* m_ah;
CRegister* m_ch;
CRegister* m_dh;
CRegister* m_bh;
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
CRegister* m_ax;
CRegister* m_cx;
CRegister* m_dx;
CRegister* m_bx;
CRegister* m_sp;
CRegister* m_bp;
CRegister* m_si;
CRegister* m_di;
// 64-bit mode only
/*
CRegister* m_r8w;
CRegister* m_r9w;
CRegister* m_r10w;
CRegister* m_r11w;
CRegister* m_r12w;
CRegister* m_r13w;
CRegister* m_r14w;
CRegister* m_r15w;
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
CRegister* m_eax;
CRegister* m_ecx;
CRegister* m_edx;
CRegister* m_ebx;
CRegister* m_esp;
CRegister* m_ebp;
CRegister* m_esi;
CRegister* m_edi;
// 64-bit mode only
/*
CRegister* m_r8d;
CRegister* m_r9d;
CRegister* m_r10d;
CRegister* m_r11d;
CRegister* m_r12d;
CRegister* m_r13d;
CRegister* m_r14d;
CRegister* m_r15d;
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
CRegister* m_rax;
CRegister* m_rcx;
CRegister* m_rdx;
CRegister* m_rbx;
CRegister* m_rsp;
CRegister* m_rbp;
CRegister* m_rsi;
CRegister* m_rdi;
*/
// 64-bit mode only
/*
CRegister* m_r8;
CRegister* m_r9;
CRegister* m_r10;
CRegister* m_r11;
CRegister* m_r12;
CRegister* m_r13;
CRegister* m_r14;
CRegister* m_r15;
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
CRegister* m_mm0;
CRegister* m_mm1;
CRegister* m_mm2;
CRegister* m_mm3;
CRegister* m_mm4;
CRegister* m_mm5;
CRegister* m_mm6;
CRegister* m_mm7;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
CRegister* m_xmm0;
CRegister* m_xmm1;
CRegister* m_xmm2;
CRegister* m_xmm3;
CRegister* m_xmm4;
CRegister* m_xmm5;
CRegister* m_xmm6;
CRegister* m_xmm7;
// 64-bit mode only
/*
CRegister* m_xmm8;
CRegister* m_xmm9;
CRegister* m_xmm10;
CRegister* m_xmm11;
CRegister* m_xmm12;
CRegister* m_xmm13;
CRegister* m_xmm14;
CRegister* m_xmm15;
*/
// ========================================================================
// >> 16-bit Segment registers
// ========================================================================
CRegister* m_cs;
CRegister* m_ss;
CRegister* m_ds;
CRegister* m_es;
CRegister* m_fs;
CRegister* m_gs;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
CRegister* m_st0;
CRegister* m_st1;
CRegister* m_st2;
CRegister* m_st3;
CRegister* m_st4;
CRegister* m_st5;
CRegister* m_st6;
CRegister* m_st7;
};
#endif // _REGISTERS_H
+664
View File
@@ -0,0 +1,664 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "dynhooks_sourcepawn.h"
#include "util.h"
#include <memory>
#ifdef KE_WINDOWS
#include "conventions/x86MsCdecl.h"
#include "conventions/x86MsThiscall.h"
#include "conventions/x86MsStdcall.h"
#include "conventions/x86MsFastcall.h"
typedef x86MsCdecl x86DetourCdecl;
typedef x86MsThiscall x86DetourThisCall;
typedef x86MsStdcall x86DetourStdCall;
typedef x86MsFastcall x86DetourFastCall;
#elif defined KE_LINUX
#include "conventions/x86GccCdecl.h"
#include "conventions/x86GccThiscall.h"
#include "conventions/x86MsStdcall.h"
#include "conventions/x86MsFastcall.h"
typedef x86GccCdecl x86DetourCdecl;
typedef x86GccThiscall x86DetourThisCall;
// Uhm, stdcall on linux?
typedef x86MsStdcall x86DetourStdCall;
// Uhumm, fastcall on linux?
typedef x86MsFastcall x86DetourFastCall;
#else
#error "Unsupported platform."
#endif
// Keep a map of detours and their registered plugin callbacks.
DetourMap g_pPreDetours;
DetourMap g_pPostDetours;
void UnhookFunction(HookType_t hookType, CHook *pDetour)
{
CHookManager *pDetourManager = GetHookManager();
pDetour->RemoveCallback(hookType, (HookHandlerFn *)(void *)&HandleDetour);
// Only disable the detour if there are no more listeners.
if (!pDetour->AreCallbacksRegistered())
pDetourManager->UnhookFunction(pDetour->m_pFunc);
}
bool AddDetourPluginHook(HookType_t hookType, CHook *pDetour, HookSetup *setup, IPluginFunction *pCallback)
{
DetourMap *map;
if (hookType == HOOKTYPE_PRE)
map = &g_pPreDetours;
else
map = &g_pPostDetours;
// See if we already have this detour in our list.
PluginCallbackList *wrappers;
DetourMap::Insert f = map->findForAdd(pDetour);
if (f.found())
{
wrappers = f->value;
}
else
{
// Create a vector to store all the plugin callbacks in.
wrappers = new PluginCallbackList;
if (!map->add(f, pDetour, wrappers))
{
delete wrappers;
UnhookFunction(hookType, pDetour);
return false;
}
}
// Add the plugin callback to the detour list.
CDynamicHooksSourcePawn *pWrapper = new CDynamicHooksSourcePawn(setup, pDetour, pCallback, hookType == HOOKTYPE_POST);
wrappers->push_back(pWrapper);
return true;
}
bool RemoveDetourPluginHook(HookType_t hookType, CHook *pDetour, IPluginFunction *pCallback)
{
DetourMap *map;
if (hookType == HOOKTYPE_PRE)
map = &g_pPreDetours;
else
map = &g_pPostDetours;
DetourMap::Result res = map->find(pDetour);
if (!res.found())
return false;
// Remove the plugin's callback
bool bRemoved = false;
PluginCallbackList *wrappers = res->value;
for (int i = wrappers->size()-1; i >= 0 ; i--)
{
CDynamicHooksSourcePawn *pWrapper = wrappers->at(i);
if (pWrapper->plugin_callback == pCallback)
{
bRemoved = true;
delete pWrapper;
wrappers->erase(wrappers->begin() + i);
}
}
// No more plugin hooks on this callback. Free our structures.
if (wrappers->empty())
{
delete wrappers;
UnhookFunction(hookType, pDetour);
map->remove(res);
}
return bRemoved;
}
void RemoveAllCallbacksForContext(HookType_t hookType, DetourMap *map, IPluginContext *pContext)
{
PluginCallbackList *wrappers;
CDynamicHooksSourcePawn *pWrapper;
DetourMap::iterator it = map->iter();
// Run through all active detours we added.
for (; !it.empty(); it.next())
{
wrappers = it->value;
// See if there are callbacks of this plugin context registered
// and remove them.
for (int i = wrappers->size() - 1; i >= 0; i--)
{
pWrapper = wrappers->at(i);
if (pWrapper->plugin_callback->GetParentRuntime()->GetDefaultContext() != pContext)
continue;
delete pWrapper;
wrappers->erase(wrappers->begin() + i);
}
// No plugin interested in this hook anymore. unhook.
if (wrappers->empty())
{
delete wrappers;
UnhookFunction(hookType, it->key);
it.erase();
}
}
}
void RemoveAllCallbacksForContext(IPluginContext *pContext)
{
RemoveAllCallbacksForContext(HOOKTYPE_PRE, &g_pPreDetours, pContext);
RemoveAllCallbacksForContext(HOOKTYPE_POST, &g_pPostDetours, pContext);
}
void CleanupDetours(HookType_t hookType, DetourMap *map)
{
PluginCallbackList *wrappers;
CDynamicHooksSourcePawn *pWrapper;
DetourMap::iterator it = map->iter();
// Run through all active detours we added.
for (; !it.empty(); it.next())
{
wrappers = it->value;
// Remove all callbacks
for (int i = wrappers->size() - 1; i >= 0; i--)
{
pWrapper = wrappers->at(i);
delete pWrapper;
}
// Unhook the function
delete wrappers;
UnhookFunction(hookType, it->key);
}
map->clear();
}
void CleanupDetours()
{
CleanupDetours(HOOKTYPE_PRE, &g_pPreDetours);
CleanupDetours(HOOKTYPE_POST, &g_pPostDetours);
}
ICallingConvention *ConstructCallingConvention(HookSetup *setup)
{
// Convert function parameter types into DynamicHooks structures.
std::vector<DataTypeSized_t> vecArgTypes;
for (size_t i = 0; i < setup->params.size(); i++)
{
ParamInfo &info = setup->params[i];
DataTypeSized_t type;
type.type = DynamicHooks_ConvertParamTypeFrom(info.type);
type.size = info.size;
type.custom_register = info.custom_register;
vecArgTypes.push_back(type);
}
DataTypeSized_t returnType;
returnType.type = DynamicHooks_ConvertReturnTypeFrom(setup->returnType);
returnType.size = 0;
// TODO: Add support for a custom return register.
returnType.custom_register = None;
ICallingConvention *pCallConv = nullptr;
switch (setup->callConv)
{
case CallConv_CDECL:
pCallConv = new x86DetourCdecl(vecArgTypes, returnType);
break;
case CallConv_THISCALL:
pCallConv = new x86DetourThisCall(vecArgTypes, returnType);
break;
case CallConv_STDCALL:
pCallConv = new x86DetourStdCall(vecArgTypes, returnType);
break;
case CallConv_FASTCALL:
pCallConv = new x86DetourFastCall(vecArgTypes, returnType);
break;
default:
smutils->LogError(myself, "Unknown calling convention %d.", setup->callConv);
break;
}
return pCallConv;
}
// Some arguments might be optimized to be passed in registers instead of the stack.
bool UpdateRegisterArgumentSizes(CHook* pDetour, HookSetup *setup)
{
// The registers the arguments are passed in might not be the same size as the actual parameter type.
// Update the type info to the size of the register that's now holding that argument,
// so we can copy the whole value.
ICallingConvention* callingConvention = pDetour->m_pCallingConvention;
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
int numArgs = argTypes.size();
for (int i = 0; i < numArgs; i++)
{
// Ignore regular arguments on the stack.
if (argTypes[i].custom_register == None)
continue;
CRegister *reg = pDetour->m_pRegisters->GetRegister(argTypes[i].custom_register);
// That register can't be handled yet.
if (!reg)
return false;
argTypes[i].size = reg->m_iSize;
setup->params[i].size = reg->m_iSize;
}
return true;
}
// Central handler for all detours. Heart of the detour support.
ReturnAction_t HandleDetour(HookType_t hookType, CHook* pDetour)
{
// Can't call into SourcePawn offthread.
if (g_MainThreadId != std::this_thread::get_id())
return ReturnAction_Ignored;
DetourMap *map;
if (hookType == HOOKTYPE_PRE)
map = &g_pPreDetours;
else
map = &g_pPostDetours;
// Find the callback list for this detour.
DetourMap::Result r = map->find(pDetour);
if (!r.found())
return ReturnAction_Ignored;
// List of all callbacks.
PluginCallbackList *wrappers = r->value;
HookReturnStruct *returnStruct = NULL;
Handle_t rHndl = BAD_HANDLE;
HookParamsStruct *paramStruct = NULL;
Handle_t pHndl = BAD_HANDLE;
int argNum = pDetour->m_pCallingConvention->m_vecArgTypes.size();
// Keep a copy of the last return value if some plugin wants to override or supercede the function.
ReturnAction_t finalRet = ReturnAction_Ignored;
std::unique_ptr<uint8_t[]> finalRetBuf = std::make_unique<uint8_t[]>(pDetour->m_pCallingConvention->m_returnType.size);
// Call all the plugin functions..
for (size_t i = 0; i < wrappers->size(); i++)
{
CDynamicHooksSourcePawn *pWrapper = wrappers->at(i);
IPluginFunction *pCallback = pWrapper->plugin_callback;
// Create a seperate buffer for changed return values for this plugin.
// We update the finalRet above if the tempRet is higher than the previous ones in the callback list.
ReturnAction_t tempRet = ReturnAction_Ignored;
uint8_t *tempRetBuf = nullptr;
// Find the this pointer for thiscalls.
// Don't even try to load it if the plugin doesn't care and set it to be ignored.
if (pWrapper->callConv == CallConv_THISCALL && pWrapper->thisType != ThisPointer_Ignore)
{
// The this pointer is implicitly always the first argument.
void *thisPtr = pDetour->GetArgument<void *>(0);
cell_t thisAddr = GetThisPtr(thisPtr, pWrapper->thisType);
pCallback->PushCell(thisAddr);
}
// Create the structure for plugins to change/get the return value if the function returns something.
if (pWrapper->returnType != ReturnType_Void)
{
// Create a handle for the return value to pass to the plugin callback.
returnStruct = pWrapper->GetReturnStruct();
HandleError err;
rHndl = handlesys->CreateHandle(g_HookReturnHandle, returnStruct, pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity(), &err);
if (!rHndl)
{
pCallback->Cancel();
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Error creating ReturnHandle in preparation to call hook callback. (error %d)", err);
if (returnStruct)
delete returnStruct;
// Don't call more callbacks. They will probably fail too.
break;
}
pCallback->PushCell(rHndl);
}
// Create the structure for plugins to access the function arguments if it has some.
if (argNum > 0)
{
paramStruct = pWrapper->GetParamStruct();
HandleError err;
pHndl = handlesys->CreateHandle(g_HookParamsHandle, paramStruct, pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity(), &err);
if (!pHndl)
{
pCallback->Cancel();
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Error creating ThisHandle in preparation to call hook callback. (error %d)", err);
// Don't leak our own handles here! Free the return struct if we fail during the argument marshalling.
if (rHndl)
{
HandleSecurity sec(pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity());
handlesys->FreeHandle(rHndl, &sec);
rHndl = BAD_HANDLE;
}
if (paramStruct)
delete paramStruct;
// Don't call more callbacks. They will probably fail too.
break;
}
pCallback->PushCell(pHndl);
}
// Run the plugin callback.
cell_t result = (cell_t)MRES_Ignored;
pCallback->Execute(&result);
switch ((MRESReturn)result)
{
case MRES_Handled:
tempRet = ReturnAction_Handled;
break;
case MRES_ChangedHandled:
tempRet = ReturnAction_Handled;
// Copy the changed parameter values from the plugin's parameter structure back into the actual detour arguments.
pWrapper->UpdateParamsFromStruct(paramStruct);
break;
case MRES_ChangedOverride:
case MRES_Override:
case MRES_Supercede:
// See if this function returns something we should override.
if (pWrapper->returnType != ReturnType_Void)
{
// Make sure the plugin provided a new return value. Could be an oversight if MRES_ChangedOverride
// is called without the return value actually being changed.
if (!returnStruct->isChanged)
{
//Throw an error if no override was set
tempRet = ReturnAction_Ignored;
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Tried to override return value without return value being set");
break;
}
if (pWrapper->returnType == ReturnType_String || pWrapper->returnType == ReturnType_Int || pWrapper->returnType == ReturnType_Bool)
{
tempRetBuf = *(uint8_t **)returnStruct->newResult;
}
else if (pWrapper->returnType == ReturnType_Float)
{
*(float *)&tempRetBuf = *(float *)returnStruct->newResult;
}
else
{
tempRetBuf = (uint8_t *)returnStruct->newResult;
}
}
// Store if the plugin wants the original function to be called.
if (result == MRES_Supercede)
tempRet = ReturnAction_Supercede;
else
tempRet = ReturnAction_Override;
// Copy the changed parameter values from the plugin's parameter structure back into the actual detour arguments.
if (result == MRES_ChangedOverride)
pWrapper->UpdateParamsFromStruct(paramStruct);
break;
default:
tempRet = ReturnAction_Ignored;
break;
}
// Prioritize the actions.
if (finalRet <= tempRet)
{
// Copy the action and return value.
finalRet = tempRet;
memcpy(finalRetBuf.get(), &tempRetBuf, pDetour->m_pCallingConvention->m_returnType.size);
}
// Free the handles again.
HandleSecurity sec(pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity());
if (returnStruct)
{
handlesys->FreeHandle(rHndl, &sec);
}
if (paramStruct)
{
handlesys->FreeHandle(pHndl, &sec);
}
}
// If we want to use our own return value, write it back.
if (finalRet >= ReturnAction_Override)
{
void* pPtr = pDetour->m_pCallingConvention->GetReturnPtr(pDetour->m_pRegisters);
memcpy(pPtr, finalRetBuf.get(), pDetour->m_pCallingConvention->m_returnType.size);
pDetour->m_pCallingConvention->ReturnPtrChanged(pDetour->m_pRegisters, pPtr);
}
return finalRet;
}
CDynamicHooksSourcePawn::CDynamicHooksSourcePawn(HookSetup *setup, CHook *pDetour, IPluginFunction *pCallback, bool post)
{
this->params = setup->params;
this->offset = -1;
this->returnFlag = setup->returnFlag;
this->returnType = setup->returnType;
this->post = post;
this->plugin_callback = pCallback;
this->entity = -1;
this->thisType = setup->thisType;
this->hookType = setup->hookType;
this->m_pDetour = pDetour;
this->callConv = setup->callConv;
}
HookReturnStruct *CDynamicHooksSourcePawn::GetReturnStruct()
{
// Create buffers to store the return value of the function.
HookReturnStruct *res = new HookReturnStruct();
res->isChanged = false;
res->type = this->returnType;
res->orgResult = NULL;
res->newResult = NULL;
// Copy the actual function's return value too for post hooks.
if (this->post)
{
switch (this->returnType)
{
case ReturnType_String:
res->orgResult = malloc(sizeof(string_t));
res->newResult = malloc(sizeof(string_t));
*(string_t *)res->orgResult = m_pDetour->GetReturnValue<string_t>();
break;
case ReturnType_Int:
res->orgResult = malloc(sizeof(int));
res->newResult = malloc(sizeof(int));
*(int *)res->orgResult = m_pDetour->GetReturnValue<int>();
break;
case ReturnType_Bool:
res->orgResult = malloc(sizeof(bool));
res->newResult = malloc(sizeof(bool));
*(bool *)res->orgResult = m_pDetour->GetReturnValue<bool>();
break;
case ReturnType_Float:
res->orgResult = malloc(sizeof(float));
res->newResult = malloc(sizeof(float));
*(float *)res->orgResult = m_pDetour->GetReturnValue<float>();
break;
case ReturnType_Vector:
{
res->orgResult = malloc(sizeof(SDKVector));
res->newResult = malloc(sizeof(SDKVector));
SDKVector vec = m_pDetour->GetReturnValue<SDKVector>();
*(SDKVector *)res->orgResult = vec;
break;
}
default:
res->orgResult = m_pDetour->GetReturnValue<void *>();
break;
}
}
// Pre hooks don't have access to the return value yet - duh.
// Just create the buffers for overridden values.
// TODO: Strip orgResult malloc.
else
{
switch (this->returnType)
{
case ReturnType_String:
res->orgResult = malloc(sizeof(string_t));
res->newResult = malloc(sizeof(string_t));
*(string_t *)res->orgResult = NULL_STRING;
break;
case ReturnType_Vector:
res->orgResult = malloc(sizeof(SDKVector));
res->newResult = malloc(sizeof(SDKVector));
*(SDKVector *)res->orgResult = SDKVector();
break;
case ReturnType_Int:
res->orgResult = malloc(sizeof(int));
res->newResult = malloc(sizeof(int));
*(int *)res->orgResult = 0;
break;
case ReturnType_Bool:
res->orgResult = malloc(sizeof(bool));
res->newResult = malloc(sizeof(bool));
*(bool *)res->orgResult = false;
break;
case ReturnType_Float:
res->orgResult = malloc(sizeof(float));
res->newResult = malloc(sizeof(float));
*(float *)res->orgResult = 0.0;
break;
}
}
return res;
}
HookParamsStruct *CDynamicHooksSourcePawn::GetParamStruct()
{
// Save argument values of detoured function.
HookParamsStruct *params = new HookParamsStruct();
params->dg = this;
ICallingConvention* callingConvention = m_pDetour->m_pCallingConvention;
size_t stackSize = callingConvention->GetArgStackSize();
size_t paramsSize = stackSize + callingConvention->GetArgRegisterSize();
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
size_t numArgs = argTypes.size();
// Create space for original parameters and changes plugins might do.
params->orgParams = (void **)malloc(paramsSize);
params->newParams = (void **)malloc(paramsSize);
params->isChanged = (bool *)malloc(numArgs * sizeof(bool));
// Save old stack parameters.
if (stackSize > 0)
{
void *pArgPtr = m_pDetour->m_pCallingConvention->GetStackArgumentPtr(m_pDetour->m_pRegisters);
memcpy(params->orgParams, pArgPtr, stackSize);
}
memset(params->newParams, 0, paramsSize);
memset(params->isChanged, false, numArgs * sizeof(bool));
size_t firstArg = 0;
// TODO: Support custom register for this ptr.
if (callConv == CallConv_THISCALL)
firstArg = 1;
// Save the old parameters passed in a register.
size_t offset = stackSize;
for (size_t i = 0; i < numArgs; i++)
{
// We already saved the stack arguments.
if (argTypes[i].custom_register == None)
continue;
size_t size = argTypes[i].size;
// Register argument values are saved after all stack arguments in this buffer.
void *paramAddr = (void *)((intptr_t)params->orgParams + offset);
void *regAddr = callingConvention->GetArgumentPtr(i + firstArg, m_pDetour->m_pRegisters);
memcpy(paramAddr, regAddr, size);
offset += size;
}
return params;
}
void CDynamicHooksSourcePawn::UpdateParamsFromStruct(HookParamsStruct *params)
{
// Function had no params to update now.
if (!params)
return;
ICallingConvention* callingConvention = m_pDetour->m_pCallingConvention;
size_t stackSize = callingConvention->GetArgStackSize();
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
size_t numArgs = argTypes.size();
size_t firstArg = 0;
// TODO: Support custom register for this ptr.
if (callConv == CallConv_THISCALL)
firstArg = 1;
size_t stackOffset = 0;
// Values of arguments stored in registers are saved after the stack arguments.
size_t registerOffset = stackSize;
size_t offset;
for (size_t i = 0; i < numArgs; i++)
{
size_t size = argTypes[i].size;
// Only have to copy something if the plugin changed this parameter.
if (params->isChanged[i])
{
// Get the offset of this argument in the linear buffer. Register argument values are placed after all stack arguments.
offset = argTypes[i].custom_register == None ? stackOffset : registerOffset;
void *paramAddr = (void *)((intptr_t)params->newParams + offset);
void *stackAddr = callingConvention->GetArgumentPtr(i + firstArg, m_pDetour->m_pRegisters);
memcpy(stackAddr, paramAddr, size);
}
// Keep track of the seperate stack and register arguments.
if (argTypes[i].custom_register == None)
stackOffset += size;
else
registerOffset += size;
}
}
+73
View File
@@ -0,0 +1,73 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_DYNHOOKS_SP_H_
#define _INCLUDE_DYNHOOKS_SP_H_
#include "manager.h"
#include "vhook.h"
#include <am-hashmap.h>
class CDynamicHooksSourcePawn;
typedef ke::HashMap<IPluginFunction *, CDynamicHooksSourcePawn *, ke::PointerPolicy<IPluginFunction>> CallbackMap;
typedef std::vector<CDynamicHooksSourcePawn *> PluginCallbackList;
typedef ke::HashMap<CHook *, PluginCallbackList *, ke::PointerPolicy<CHook>> DetourMap;
//extern ke::Vector<CHook *> g_pDetours;
// Keep a list of plugin callback -> Hook wrapper for easily removing plugin hooks
//extern CallbackMap g_pPluginPreDetours;
//extern CallbackMap g_pPluginPostDetours;
// Keep a list of hook -> callbacks for calling in the detour handler
extern DetourMap g_pPreDetours;
extern DetourMap g_pPostDetours;
class CDynamicHooksSourcePawn : public DHooksInfo {
public:
CDynamicHooksSourcePawn(HookSetup *setup, CHook *pDetour, IPluginFunction *pCallback, bool post);
HookReturnStruct *GetReturnStruct();
HookParamsStruct *GetParamStruct();
void UpdateParamsFromStruct(HookParamsStruct *params);
public:
CHook *m_pDetour;
CallingConvention callConv;
};
ICallingConvention *ConstructCallingConvention(HookSetup *setup);
bool UpdateRegisterArgumentSizes(CHook* pDetour, HookSetup *setup);
ReturnAction_t HandleDetour(HookType_t hookType, CHook* pDetour);
bool AddDetourPluginHook(HookType_t hookType, CHook *pDetour, HookSetup *setup, IPluginFunction *pCallback);
bool RemoveDetourPluginHook(HookType_t hookType, CHook *pDetour, IPluginFunction *pCallback);
void RemoveAllCallbacksForContext(IPluginContext *pContext);
void CleanupDetours();
#endif
+196
View File
@@ -0,0 +1,196 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "extension.h"
#include "listeners.h"
#include "dynhooks_sourcepawn.h"
#include "signatures.h"
DHooks g_DHooksIface; /**< Global singleton for extension's main interface */
SMEXT_LINK(&g_DHooksIface);
IBinTools *g_pBinTools;
ISDKHooks *g_pSDKHooks;
ISDKTools *g_pSDKTools;
DHooksEntityListener *g_pEntityListener = NULL;
HandleType_t g_HookSetupHandle = 0;
HandleType_t g_HookParamsHandle = 0;
HandleType_t g_HookReturnHandle = 0;
std::thread::id g_MainThreadId;
bool DHooks::SDK_OnLoad(char *error, size_t maxlength, bool late)
{
HandleError err;
g_HookSetupHandle = handlesys->CreateType("HookSetup", this, 0, NULL, NULL, myself->GetIdentity(), &err);
if(g_HookSetupHandle == 0)
{
snprintf(error, maxlength, "Could not create hook setup handle type (err: %d)", err);
return false;
}
g_HookParamsHandle = handlesys->CreateType("HookParams", this, 0, NULL, NULL, myself->GetIdentity(), &err);
if(g_HookParamsHandle == 0)
{
snprintf(error, maxlength, "Could not create hook params handle type (err: %d)", err);
return false;
}
g_HookReturnHandle = handlesys->CreateType("HookReturn", this, 0, NULL, NULL, myself->GetIdentity(), &err);
if(g_HookReturnHandle == 0)
{
snprintf(error, maxlength, "Could not create hook return handle type (err: %d)", err);
return false;
}
if (!g_pPreDetours.init())
{
snprintf(error, maxlength, "Could not initialize pre hook detours hashmap.");
return false;
}
if (!g_pPostDetours.init())
{
snprintf(error, maxlength, "Could not initialize post hook detours hashmap.");
return false;
}
sharesys->AddDependency(myself, "bintools.ext", true, true);
sharesys->AddDependency(myself, "sdktools.ext", true, true);
sharesys->AddDependency(myself, "sdkhooks.ext", true, true);
sharesys->RegisterLibrary(myself, "dhooks");
plsys->AddPluginsListener(this);
sharesys->AddNatives(myself, g_Natives);
g_pEntityListener = new DHooksEntityListener();
g_pSignatures = new SignatureGameConfig();
g_MainThreadId = std::this_thread::get_id();
return true;
}
void DHooks::OnHandleDestroy(HandleType_t type, void *object)
{
if(type == g_HookSetupHandle)
{
delete (HookSetup *)object;
}
else if(type == g_HookParamsHandle)
{
delete (HookParamsStruct *)object;
}
else if(type == g_HookReturnHandle)
{
delete (HookReturnStruct *)object;
}
}
void DHooks::SDK_OnAllLoaded()
{
SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools);
SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
SM_GET_LATE_IFACE(SDKHOOKS, g_pSDKHooks);
g_pSDKHooks->AddEntityListener(g_pEntityListener);
gameconfs->AddUserConfigHook("Functions", g_pSignatures);
}
void DHooks::SDK_OnUnload()
{
CleanupHooks();
CleanupDetours();
if(g_pEntityListener)
{
g_pEntityListener->CleanupListeners();
g_pEntityListener->CleanupRemoveList();
g_pSDKHooks->RemoveEntityListener(g_pEntityListener);
delete g_pEntityListener;
}
plsys->RemovePluginsListener(this);
handlesys->RemoveType(g_HookSetupHandle, myself->GetIdentity());
handlesys->RemoveType(g_HookParamsHandle, myself->GetIdentity());
handlesys->RemoveType(g_HookReturnHandle, myself->GetIdentity());
gameconfs->RemoveUserConfigHook("Functions", g_pSignatures);
}
bool DHooks::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late)
{
if(!SetupHookManager(ismm))
{
snprintf(error, maxlength, "Failed to get IHookManagerAutoGen iface");
return false;
}
return true;
}
void DHooks::OnPluginUnloaded(IPlugin *plugin)
{
CleanupHooks(plugin->GetBaseContext());
RemoveAllCallbacksForContext(plugin->GetBaseContext());
if(g_pEntityListener)
{
g_pEntityListener->CleanupListeners(plugin->GetBaseContext());
}
}
// The next 3 functions handle cleanup if our interfaces are going to be unloaded
bool DHooks::QueryRunning(char *error, size_t maxlength)
{
SM_CHECK_IFACE(SDKTOOLS, g_pSDKTools);
SM_CHECK_IFACE(BINTOOLS, g_pBinTools);
SM_CHECK_IFACE(SDKHOOKS, g_pSDKHooks);
return true;
}
void DHooks::NotifyInterfaceDrop(SMInterface *pInterface)
{
if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_SDKHOOKS_NAME) == 0)
{
if(g_pEntityListener)
{
// If this fails, remove this line and just delete the ent listener instead
g_pSDKHooks->RemoveEntityListener(g_pEntityListener);
g_pEntityListener->CleanupListeners();
delete g_pEntityListener;
g_pEntityListener = NULL;
}
g_pSDKHooks = NULL;
}
else if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_BINTOOLS_NAME) == 0)
{
g_pBinTools = NULL;
}
else if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_SDKTOOLS_NAME) == 0)
{
g_pSDKTools = NULL;
}
}
+134
View File
@@ -0,0 +1,134 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
/**
* @file extension.h
* @brief Sample extension code header.
*/
#include "smsdk_ext.h"
#include <ISDKHooks.h>
#include <IBinTools.h>
#include <ISDKTools.h>
#include "sdk-hacks.h"
#include <thread>
/**
* @brief Sample implementation of the SDK Extension.
* Note: Uncomment one of the pre-defined virtual functions in order to use it.
*/
class DHooks : public SDKExtension, public ISMEntityListener, public IPluginsListener, public IHandleTypeDispatch
{
public:
virtual void OnHandleDestroy(HandleType_t type, void *object);
public: //IPluginsListener
virtual void OnPluginUnloaded(IPlugin *plugin);
public:
/**
* @brief This is called after the initial loading sequence has been processed.
*
* @param error Error message buffer.
* @param maxlength Size of error message buffer.
* @param late Whether or not the module was loaded after map load.
* @return True to succeed loading, false to fail.
*/
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
/**
* @brief This is called right before the extension is unloaded.
*/
virtual void SDK_OnUnload();
/**
* @brief This is called once all known extensions have been loaded.
* Note: It is is a good idea to add natives here, if any are provided.
*/
virtual void SDK_OnAllLoaded();
/**
* @brief Called when the pause state is changed.
*/
//virtual void SDK_OnPauseChange(bool paused);
/**
* @brief this is called when Core wants to know if your extension is working.
*
* @param error Error message buffer.
* @param maxlength Size of error message buffer.
* @return True if working, false otherwise.
*/
virtual bool QueryRunning(char *error, size_t maxlength);
//virtual bool QueryInterfaceDrop(SMInterface *pInterface);
virtual void NotifyInterfaceDrop(SMInterface *pInterface);
virtual void OnCoreMapEnd();
public:
#if defined SMEXT_CONF_METAMOD
/**
* @brief Called when Metamod is attached, before the extension version is called.
*
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @param late Whether or not Metamod considers this a late load.
* @return True to succeed, false to fail.
*/
virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late);
/**
* @brief Called when Metamod is detaching, after the extension version is called.
* NOTE: By default this is blocked unless sent from SourceMod.
*
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @return True to succeed, false to fail.
*/
//virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength);
/**
* @brief Called when Metamod's pause state is changing.
* NOTE: By default this is blocked unless sent from SourceMod.
*
* @param paused Pause state being set.
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @return True to succeed, false to fail.
*/
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
#endif
};
extern SourceHook::IHookManagerAutoGen *g_pHookManager;
extern sp_nativeinfo_t g_Natives[];
extern std::thread::id g_MainThreadId;
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
+169
View File
@@ -0,0 +1,169 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "listeners.h"
#include "vhook.h"
using namespace SourceHook;
std::vector<EntityListener> g_EntityListeners;
std::vector<DHooksManager *>g_pRemoveList;
void FrameCleanupHooks(void *data)
{
for (int i = g_pRemoveList.size() - 1; i >= 0; i--)
{
DHooksManager *manager = g_pRemoveList.at(i);
delete manager;
g_pRemoveList.erase(g_pRemoveList.begin() + i);
}
}
void DHooks::OnCoreMapEnd()
{
for(int i = g_pHooks.size() -1; i >= 0; i--)
{
DHooksManager *manager = g_pHooks.at(i);
if(manager->callback->hookType == HookType_GameRules)
{
delete manager;
g_pHooks.erase(g_pHooks.begin() + i);
}
}
}
void DHooksEntityListener::CleanupListeners(IPluginContext *pContext)
{
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
if(pContext == NULL || pContext == g_EntityListeners.at(i).callback->GetParentRuntime()->GetDefaultContext())
{
g_EntityListeners.erase(g_EntityListeners.begin() + i);
}
}
for (int i = g_pRemoveList.size() -1; i >= 0; i--)
{
DHooksManager *manager = g_pRemoveList.at(i);
IPluginFunction *cb = manager->callback->plugin_callback;
if (pContext == NULL || (cb && pContext == cb->GetParentRuntime()->GetDefaultContext()))
{
manager->callback->plugin_callback = nullptr;
manager->remove_callback = nullptr;
}
}
}
void DHooksEntityListener::CleanupRemoveList()
{
for (int i = g_pRemoveList.size() - 1; i >= 0; i--)
{
DHooksManager *manager = g_pRemoveList.at(i);
delete manager;
}
g_pRemoveList.clear();
}
void DHooksEntityListener::OnEntityCreated(CBaseEntity *pEntity, const char *classname)
{
int entity = gamehelpers->EntityToBCompatRef(pEntity);
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
EntityListener listerner = g_EntityListeners.at(i);
if(listerner.type == ListenType_Created)
{
IPluginFunction *callback = listerner.callback;
callback->PushCell(entity);
callback->PushString(classname);
callback->Execute(NULL);
}
}
}
void DHooksEntityListener::OnEntityDestroyed(CBaseEntity *pEntity)
{
int entity = gamehelpers->EntityToBCompatRef(pEntity);
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
EntityListener listerner = g_EntityListeners.at(i);
if(listerner.type == ListenType_Deleted)
{
IPluginFunction *callback = listerner.callback;
callback->PushCell(gamehelpers->EntityToBCompatRef(pEntity));
callback->Execute(NULL);
}
}
for(int i = g_pHooks.size() -1; i >= 0; i--)
{
DHooksManager *manager = g_pHooks.at(i);
if(manager->callback->hookType == HookType_Entity && manager->callback->entity == entity)
{
if(g_pRemoveList.size() == 0)
{
smutils->AddFrameAction(&FrameCleanupHooks, NULL);
}
g_pRemoveList.push_back(manager);
g_pHooks.erase(g_pHooks.begin() + i);
}
}
}
bool DHooksEntityListener::AddPluginEntityListener(ListenType type, IPluginFunction *callback)
{
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
EntityListener listerner = g_EntityListeners.at(i);
if(listerner.callback == callback && listerner.type == type)
{
return true;
}
}
EntityListener listener;
listener.callback = callback;
listener.type = type;
g_EntityListeners.push_back(listener);
return true;
}
bool DHooksEntityListener::RemovePluginEntityListener(ListenType type, IPluginFunction *callback)
{
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
EntityListener listerner = g_EntityListeners.at(i);
if(listerner.callback == callback && listerner.type == type)
{
g_EntityListeners.erase(g_EntityListeners.begin() + i);
return true;
}
}
return false;
}
@@ -1,14 +1,14 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2020 AlliedModders LLC. All rights reserved.
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@@ -28,31 +28,36 @@
*
* Version: $Id$
*/
#pragma once
#include <google/protobuf/message.h>
#ifndef _INCLUDE_LISTENERS_H_
#define _INCLUDE_LISTENERS_H_
class IProtobufProxy
#include "extension.h"
#include "vhook.h"
enum ListenType
{
public:
// Serialize the given message into a buffer. The buffer and its length are placed in |out|.
// The buffer must be freed with FreeBuffer.
virtual bool Serialize(const google::protobuf::Message* message, void** out, size_t* len) = 0;
virtual void FreeBuffer(void* data) = 0;
// Deserialize the given buffer into a message.
virtual bool Deserialize(const void* buffer, size_t len, google::protobuf::Message* message) = 0;
// Allocate/free a prototype.
virtual google::protobuf::Message* NewPrototype(int msg_type) = 0;
virtual void FreeMessage(google::protobuf::Message* message) = 0;
bool Serialize(const google::protobuf::Message& message, void** out, size_t* len) {
return Serialize(&message, out, len);
}
bool Deserialize(const void* buffer, size_t len, google::protobuf::Message& message) {
return Deserialize(buffer, len, &message);
}
ListenType_Created,
ListenType_Deleted
};
typedef IProtobufProxy*(*GetProtobufProxyFn)();
class DHooksEntityListener : public ISMEntityListener
{
public:
virtual void OnEntityCreated(CBaseEntity *pEntity, const char *classname);
virtual void OnEntityDestroyed(CBaseEntity *pEntity);
void CleanupListeners(IPluginContext *func = NULL);
void CleanupRemoveList();
bool AddPluginEntityListener(ListenType type, IPluginFunction *callback);
bool RemovePluginEntityListener(ListenType type, IPluginFunction *callback);
};
struct EntityListener
{
ListenType type;
IPluginFunction *callback;
};
extern std::vector<DHooksManager *> g_pHooks;
#endif
File diff suppressed because it is too large Load Diff
@@ -1,14 +1,14 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2020 AlliedModders LLC. All rights reserved.
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@@ -28,8 +28,18 @@
*
* Version: $Id$
*/
#include "pb_handle.h"
#if defined PROTOBUF_PROXY_ENABLE
IProtobufProxy* gProtobufProxy = nullptr;
#ifndef _INCLUDE_NATIVES_H_
#define _INCLUDE_NATIVES_H_
#include "extension.h"
#include "vhook.h"
#include "listeners.h"
extern DHooksEntityListener *g_pEntityListener;
extern ISDKTools *g_pSDKTools;
extern HandleType_t g_HookSetupHandle;
extern HandleType_t g_HookParamsHandle;
extern HandleType_t g_HookReturnHandle;
extern std::vector<DHooksManager *> g_pHooks;
#endif
+106
View File
@@ -0,0 +1,106 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SDK_HACKS_H_
#define _INCLUDE_SDK_HACKS_H_
class SDKVector
{
public:
SDKVector(float x1, float y1, float z1)
{
this->x = x1;
this->y = y1;
this->z = z1;
}
SDKVector(void)
{
this->x = 0.0;
this->y = 0.0;
this->z = 0.0;
}
float x;
float y;
float z;
};
struct string_t
{
public:
bool operator!() const { return ( pszValue == NULL ); }
bool operator==( const string_t &rhs ) const { return ( pszValue == rhs.pszValue ); }
bool operator!=( const string_t &rhs ) const { return ( pszValue != rhs.pszValue ); }
bool operator<( const string_t &rhs ) const { return ((void *)pszValue < (void *)rhs.pszValue ); }
const char *ToCStr() const { return ( pszValue ) ? pszValue : ""; }
protected:
const char *pszValue;
};
struct castable_string_t : public string_t // string_t is used in unions, hence, no constructor allowed
{
castable_string_t() { pszValue = NULL; }
castable_string_t( const char *pszFrom ) { pszValue = (pszFrom && *pszFrom) ? pszFrom : 0; }
};
#define NULL_STRING castable_string_t()
#define STRING( string_t_obj ) (string_t_obj).ToCStr()
#define MAKE_STRING( c_str ) castable_string_t( c_str )
#define FL_EDICT_FREE (1<<1)
struct edict_t
{
public:
bool IsFree()
{
return (m_fStateFlags & FL_EDICT_FREE) != 0;
}
private:
int m_fStateFlags;
};
class CBaseHandle
{
/*public:
bool IsValid() const {return m_Index != INVALID_EHANDLE_INDEX;}
int GetEntryIndex() const
{
if ( !IsValid() )
return NUM_ENT_ENTRIES-1;
return m_Index & ENT_ENTRY_MASK;
}*/
private:
unsigned long m_Index;
};
#endif
+619
View File
@@ -0,0 +1,619 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include <signatures.h>
SignatureGameConfig *g_pSignatures;
enum ParseState
{
PState_None,
PState_Root,
PState_Function,
PState_Arguments,
PState_Argument
};
ParseState g_ParseState;
unsigned int g_IgnoreLevel;
// The parent section type of a platform specific "windows" or "linux" section.
ParseState g_PlatformOnlyState;
SignatureWrapper *g_CurrentSignature;
std::string g_CurrentFunctionName;
ArgumentInfo g_CurrentArgumentInfo;
SignatureWrapper *SignatureGameConfig::GetFunctionSignature(const char *function)
{
auto sig = signatures_.find(function);
if (!sig.found())
return nullptr;
return sig->value;
}
/**
* Game config "Functions" section parsing.
*/
SMCResult SignatureGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
// We're ignoring the parent section. Ignore all child sections as well.
if (g_IgnoreLevel > 0)
{
g_IgnoreLevel++;
return SMCResult_Continue;
}
// Handle platform specific sections first.
#if defined WIN32
if (!strcmp(name, "windows"))
#elif defined _LINUX
if (!strcmp(name, "linux"))
#elif defined _OSX
if (!strcmp(name, "mac"))
#endif
{
// We're already in a section for a different OS that we're ignoring. Can't have a section for our OS in here.
if (g_IgnoreLevel > 0)
{
smutils->LogError(myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
// We don't support nested (useless) sections of the same OS like "windows" { "windows" { "foo" "bar" } }
if (g_PlatformOnlyState != PState_None)
{
smutils->LogError(myself, "Duplicate platform specific section for \"%s\". Already parsing only for that OS: line: %i col: %i", name, states->line, states->col);
return SMCResult_HaltFail;
}
// This is a specific block for us.
g_PlatformOnlyState = g_ParseState;
return SMCResult_Continue;
}
#if defined WIN32
else if (!strcmp(name, "linux") || !strcmp(name, "mac"))
#elif defined _LINUX
else if (!strcmp(name, "windows") || !strcmp(name, "mac"))
#elif defined _OSX
else if (!strcmp(name, "windows") || !strcmp(name, "linux"))
#endif
{
if (g_PlatformOnlyState != PState_None)
{
smutils->LogError(myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
// A specific block for a different platform.
g_IgnoreLevel++;
return SMCResult_Continue;
}
switch (g_ParseState)
{
case PState_Root:
{
auto sig = signatures_.find(name);
if (sig.found())
g_CurrentSignature = sig->value;
else
g_CurrentSignature = new SignatureWrapper();
g_CurrentFunctionName = name;
g_ParseState = PState_Function;
break;
}
case PState_Function:
{
if (!strcmp(name, "arguments"))
{
g_ParseState = PState_Arguments;
}
else
{
smutils->LogError(myself, "Unknown subsection \"%s\" (expected \"arguments\"): line: %i col: %i", name, states->line, states->col);
return SMCResult_HaltFail;
}
break;
}
case PState_Arguments:
{
g_ParseState = PState_Argument;
g_CurrentArgumentInfo.name = name;
// Reset the parameter info.
ParamInfo info;
memset(&info, 0, sizeof(info));
info.flags = PASSFLAG_BYVAL;
g_CurrentArgumentInfo.info = info;
// See if we already have info about this argument.
for (auto &arg : g_CurrentSignature->args) {
if (!arg.name.compare(name)) {
// Continue changing that argument now.
g_CurrentArgumentInfo.info = arg.info;
break;
}
}
break;
}
default:
smutils->LogError(myself, "Unknown subsection \"%s\": line: %i col: %i", name, states->line, states->col);
return SMCResult_HaltFail;
}
return SMCResult_Continue;
}
SMCResult SignatureGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
// We don't care for anything in this section or subsections.
if (g_IgnoreLevel > 0)
return SMCResult_Continue;
switch (g_ParseState)
{
case PState_Function:
if (!strcmp(key, "signature"))
{
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->offset.length() > 0)
{
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->signature = value;
}
else if (!strcmp(key, "address"))
{
if (g_CurrentSignature->signature.length() > 0 || g_CurrentSignature->offset.length() > 0)
{
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->address = value;
}
else if (!strcmp(key, "offset"))
{
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->signature.length() > 0)
{
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->offset = value;
}
else if (!strcmp(key, "callconv"))
{
CallingConvention callConv;
if (!strcmp(value, "cdecl"))
callConv = CallConv_CDECL;
else if (!strcmp(value, "thiscall"))
callConv = CallConv_THISCALL;
else if (!strcmp(value, "stdcall"))
callConv = CallConv_STDCALL;
else if (!strcmp(value, "fastcall"))
callConv = CallConv_FASTCALL;
else
{
smutils->LogError(myself, "Invalid calling convention \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->callConv = callConv;
}
else if (!strcmp(key, "hooktype"))
{
HookType hookType;
if (!strcmp(value, "entity"))
hookType = HookType_Entity;
else if (!strcmp(value, "gamerules"))
hookType = HookType_GameRules;
else if (!strcmp(value, "raw"))
hookType = HookType_Raw;
else
{
smutils->LogError(myself, "Invalid hook type \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->hookType = hookType;
}
else if (!strcmp(key, "return"))
{
g_CurrentSignature->retType = GetReturnTypeFromString(value);
if (g_CurrentSignature->retType == ReturnType_Unknown)
{
smutils->LogError(myself, "Invalid return type \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
}
else if (!strcmp(key, "this"))
{
if (!strcmp(value, "ignore"))
g_CurrentSignature->thisType = ThisPointer_Ignore;
else if (!strcmp(value, "entity"))
g_CurrentSignature->thisType = ThisPointer_CBaseEntity;
else if (!strcmp(value, "address"))
g_CurrentSignature->thisType = ThisPointer_Address;
else
{
smutils->LogError(myself, "Invalid this type \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
}
else
{
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SMCResult_HaltFail;
}
break;
case PState_Argument:
if (!strcmp(key, "type"))
{
g_CurrentArgumentInfo.info.type = GetHookParamTypeFromString(value);
if (g_CurrentArgumentInfo.info.type == HookParamType_Unknown)
{
smutils->LogError(myself, "Invalid argument type \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
}
else if (!strcmp(key, "size"))
{
g_CurrentArgumentInfo.info.size = static_cast<int>(strtol(value, NULL, 0));
if (g_CurrentArgumentInfo.info.size < 1)
{
smutils->LogError(myself, "Invalid argument size \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
}
else if (!strcmp(key, "flags"))
{
size_t flags = 0;
if (strstr(value, "byval"))
flags |= PASSFLAG_BYVAL;
if (strstr(value, "byref"))
flags |= PASSFLAG_BYREF;
if (strstr(value, "odtor"))
flags |= PASSFLAG_ODTOR;
if (strstr(value, "octor"))
flags |= PASSFLAG_OCTOR;
if (strstr(value, "oassignop"))
flags |= PASSFLAG_OASSIGNOP;
#ifdef PASSFLAG_OCOPYCTOR
if (strstr(value, "ocopyctor"))
flags |= PASSFLAG_OCOPYCTOR;
#endif
#ifdef PASSFLAG_OUNALIGN
if (strstr(value, "ounalign"))
flags |= PASSFLAG_OUNALIGN;
#endif
g_CurrentArgumentInfo.info.flags = flags;
}
else if (!strcmp(key, "register"))
{
g_CurrentArgumentInfo.info.custom_register = GetCustomRegisterFromString(value);
if (g_CurrentArgumentInfo.info.custom_register == Register_t::None)
{
smutils->LogError(myself, "Invalid register \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
}
else
{
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SMCResult_HaltFail;
}
break;
default:
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SMCResult_HaltFail;
}
return SMCResult_Continue;
}
SMCResult SignatureGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
{
// We were ignoring this section.
if (g_IgnoreLevel > 0)
{
g_IgnoreLevel--;
return SMCResult_Continue;
}
// We were in a section only for our OS.
if (g_PlatformOnlyState == g_ParseState)
{
g_PlatformOnlyState = PState_None;
return SMCResult_Continue;
}
switch (g_ParseState)
{
case PState_Function:
g_ParseState = PState_Root;
if (!g_CurrentSignature->address.length() && !g_CurrentSignature->signature.length() && !g_CurrentSignature->offset.length())
{
smutils->LogError(myself, "Function \"%s\" doesn't have a \"signature\", \"offset\" nor \"address\" set: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
if (!g_CurrentSignature->offset.length())
{
// DynamicDetours doesn't expose the passflags concept like SourceHook.
// See if we're trying to set some invalid flags on detour arguments.
for (auto &arg : g_CurrentSignature->args)
{
if ((arg.info.flags & ~PASSFLAG_BYVAL) > 0)
{
smutils->LogError(myself, "Function \"%s\" uses unsupported pass flags in argument \"%s\". Flags are only supported for virtual hooks: line: %i col: %i", g_CurrentFunctionName.c_str(), arg.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
}
}
// Save this function signature in our cache.
signatures_.insert(g_CurrentFunctionName.c_str(), g_CurrentSignature);
g_CurrentFunctionName = "";
g_CurrentSignature = nullptr;
break;
case PState_Arguments:
g_ParseState = PState_Function;
break;
case PState_Argument:
g_ParseState = PState_Arguments;
if (g_CurrentArgumentInfo.info.type == HookParamType_Unknown)
{
smutils->LogError(myself, "Missing argument type for argument \"%s\": line: %i col: %i", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
// The size wasn't set in the config. See if that's fine and we can guess it from the type.
if (!g_CurrentArgumentInfo.info.size)
{
if (g_CurrentArgumentInfo.info.type == HookParamType_Object)
{
smutils->LogError(myself, "Object param \"%s\" being set with no size: line: %i col: %i", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
else
{
g_CurrentArgumentInfo.info.size = GetParamTypeSize(g_CurrentArgumentInfo.info.type);
}
}
if (g_CurrentArgumentInfo.info.pass_type == SourceHook::PassInfo::PassType::PassType_Unknown)
g_CurrentArgumentInfo.info.pass_type = GetParamTypePassType(g_CurrentArgumentInfo.info.type);
// See if we were changing an existing argument.
bool changed = false;
for (auto &arg : g_CurrentSignature->args)
{
if (!arg.name.compare(g_CurrentArgumentInfo.name))
{
arg.info = g_CurrentArgumentInfo.info;
changed = true;
break;
}
}
// This was a new argument. Add it to the end of the list.
if (!changed)
g_CurrentSignature->args.push_back(g_CurrentArgumentInfo);
g_CurrentArgumentInfo.name = "";
break;
}
return SMCResult_Continue;
}
void SignatureGameConfig::ReadSMC_ParseStart()
{
g_ParseState = PState_Root;
g_IgnoreLevel = 0;
g_PlatformOnlyState = PState_None;
g_CurrentSignature = nullptr;
g_CurrentFunctionName = "";
g_CurrentArgumentInfo.name = "";
}
ReturnType SignatureGameConfig::GetReturnTypeFromString(const char *str)
{
if (!strcmp(str, "void"))
return ReturnType_Void;
else if (!strcmp(str, "int"))
return ReturnType_Int;
else if (!strcmp(str, "bool"))
return ReturnType_Bool;
else if (!strcmp(str, "float"))
return ReturnType_Float;
else if (!strcmp(str, "string"))
return ReturnType_String;
else if (!strcmp(str, "stringptr"))
return ReturnType_StringPtr;
else if (!strcmp(str, "charptr"))
return ReturnType_CharPtr;
else if (!strcmp(str, "vector"))
return ReturnType_Vector;
else if (!strcmp(str, "vectorptr"))
return ReturnType_VectorPtr;
else if (!strcmp(str, "cbaseentity"))
return ReturnType_CBaseEntity;
else if (!strcmp(str, "edict"))
return ReturnType_Edict;
return ReturnType_Unknown;
}
HookParamType SignatureGameConfig::GetHookParamTypeFromString(const char *str)
{
if (!strcmp(str, "int"))
return HookParamType_Int;
else if (!strcmp(str, "bool"))
return HookParamType_Bool;
else if (!strcmp(str, "float"))
return HookParamType_Float;
else if (!strcmp(str, "string"))
return HookParamType_String;
else if (!strcmp(str, "stringptr"))
return HookParamType_StringPtr;
else if (!strcmp(str, "charptr"))
return HookParamType_CharPtr;
else if (!strcmp(str, "vectorptr"))
return HookParamType_VectorPtr;
else if (!strcmp(str, "cbaseentity"))
return HookParamType_CBaseEntity;
else if (!strcmp(str, "objectptr"))
return HookParamType_ObjectPtr;
else if (!strcmp(str, "edict"))
return HookParamType_Edict;
else if (!strcmp(str, "object"))
return HookParamType_Object;
return HookParamType_Unknown;
}
Register_t SignatureGameConfig::GetCustomRegisterFromString(const char *str)
{
if (!strcmp(str, "al"))
return AL;
else if (!strcmp(str, "cl"))
return CL;
else if (!strcmp(str, "dl"))
return DL;
else if (!strcmp(str, "bl"))
return BL;
else if (!strcmp(str, "ah"))
return AH;
else if (!strcmp(str, "ch"))
return CH;
else if (!strcmp(str, "dh"))
return DH;
else if (!strcmp(str, "bh"))
return BH;
else if (!strcmp(str, "ax"))
return AX;
else if (!strcmp(str, "cx"))
return CX;
else if (!strcmp(str, "dx"))
return DX;
else if (!strcmp(str, "bx"))
return BX;
else if (!strcmp(str, "sp"))
return SP;
else if (!strcmp(str, "bp"))
return BP;
else if (!strcmp(str, "si"))
return SI;
else if (!strcmp(str, "di"))
return DI;
else if (!strcmp(str, "eax"))
return EAX;
else if (!strcmp(str, "ecx"))
return ECX;
else if (!strcmp(str, "edx"))
return EDX;
else if (!strcmp(str, "ebx"))
return EBX;
else if (!strcmp(str, "esp"))
return ESP;
else if (!strcmp(str, "ebp"))
return EBP;
else if (!strcmp(str, "esi"))
return ESI;
else if (!strcmp(str, "edi"))
return EDI;
else if (!strcmp(str, "mm0"))
return MM0;
else if (!strcmp(str, "mm1"))
return MM1;
else if (!strcmp(str, "mm2"))
return MM2;
else if (!strcmp(str, "mm3"))
return MM3;
else if (!strcmp(str, "mm4"))
return MM4;
else if (!strcmp(str, "mm5"))
return MM5;
else if (!strcmp(str, "mm6"))
return MM6;
else if (!strcmp(str, "mm7"))
return MM7;
else if (!strcmp(str, "xmm0"))
return XMM0;
else if (!strcmp(str, "xmm1"))
return XMM1;
else if (!strcmp(str, "xmm2"))
return XMM2;
else if (!strcmp(str, "xmm3"))
return XMM3;
else if (!strcmp(str, "xmm4"))
return XMM4;
else if (!strcmp(str, "xmm5"))
return XMM5;
else if (!strcmp(str, "xmm6"))
return XMM6;
else if (!strcmp(str, "xmm7"))
return XMM7;
else if (!strcmp(str, "cs"))
return CS;
else if (!strcmp(str, "ss"))
return SS;
else if (!strcmp(str, "ds"))
return DS;
else if (!strcmp(str, "es"))
return ES;
else if (!strcmp(str, "fs"))
return FS;
else if (!strcmp(str, "gs"))
return GS;
else if (!strcmp(str, "st0"))
return ST0;
return Register_t::None;
}
+84
View File
@@ -0,0 +1,84 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SIGNATURES_H_
#define _INCLUDE_SIGNATURES_H_
#include "extension.h"
#include "util.h"
#include <string>
#include <vector>
#include <sm_stringhashmap.h>
struct ArgumentInfo {
ArgumentInfo() : name()
{ }
ArgumentInfo(std::string name, ParamInfo info) : name(name), info(info)
{ }
std::string name;
ParamInfo info;
};
class SignatureWrapper {
public:
std::string signature;
std::string address;
std::string offset;
std::vector<ArgumentInfo> args;
CallingConvention callConv;
HookType hookType;
ReturnType retType;
ThisPointerType thisType;
};
class SignatureGameConfig : public ITextListener_SMC {
public:
SignatureWrapper *GetFunctionSignature(const char *function);
public:
//ITextListener_SMC
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
void ReadSMC_ParseStart();
private:
ReturnType GetReturnTypeFromString(const char *str);
HookParamType GetHookParamTypeFromString(const char *str);
Register_t GetCustomRegisterFromString(const char *str);
private:
StringHashMap<SignatureWrapper *> signatures_;
};
extern SignatureGameConfig *g_pSignatures;
#endif

Some files were not shown because too many files have changed in this diff Show More