@@ -4,54 +4,81 @@ A SourceMod extension that provides:
44- An easy way for plugins to validate and patch platform-specific address locations with a game
55configuration file.
66- A new handle type for plugins to allocate / free their own memory blocks.
7+ - Additional memory-related utilities.
78
8- ## Origin
9+ The extension comes bundled with ` sourcescramble_manager ` , a helper plugin that reads gamedata
10+ file names and patch names from a config file, then loads them all in. For those simple patches
11+ that don't need further configuration other than being toggled on.
12+
13+ ## Installation
14+
15+ This is the installation process for end-users.
16+
17+ 1 . Download and unpack the latest release (` package.zip ` for Windows, ` package.tar.gz ` for
18+ Linux) into your game directory.
19+ 2 . Install ` sourcescramble_manager.smx ` into your SourceMod installation's ` plugins/ `
20+ directory.
921
10- I just got really tired of writing plugin boilerplate for good, clean, memory patching
11- shenanigans. I've written a number of them for public / private purposes, and they've been
12- generally a pain.
22+ There are two ways patches can be applied:
1323
14- [ This is the most recent one I've written.] [ bot-collide ] The problems that stand out to me are:
24+ - managed patches, where ` sourcescramble_manager.smx ` handles installation / uninstallation in
25+ a set-and-forget fashion; these are installed by adding a configuration entry in
26+ ` configs/sourcescramble/ `
27+ - unmanaged patches, where another plugin (usually by the patch author) performs the patching
1528
16- 1 . Hardcoded, platform-specific patch payload.
17- 2 . No verification on bytes to be patched (what if a game update modifies the function?).
18- 3 . Reverting the patch would have to be manually implemented.
29+ It's up to the developer of each patch to provide instructions on how to install them.
30+ Regardless, all patches do require a game configuration file installed in ` gamedata/ ` .
1931
20- [ bot-collide ] : https://gist.github.com/nosoop/08774339937179d0022a541b05b51c8c
32+ ## Origin
33+
34+ This was originally just dedicated to memory patching. I had a number of gripes with existing
35+ solutions like [ Memory Patcher] [ ] , [ No Thriller Taunt] [ ] , and one-off plugins for this purpose:
2136
22- SourceMod's built-in memory access functions are very powerful, but also very primitive.
23- Additionally, there is no method to access arbitrary sections of the game config file, so a
24- pure-SourcePawn implementation would force developers to either:
37+ 1 . Plugins either hardcode the solution in the plugin, or use some custom conventions in their
38+ game config file (solutions I've seen / written myself use either pollute the ` Keys ` section or
39+ do their own config parsing with ` SMCParser ` ).
40+ 2 . There is no verification on bytes to be patched (what if a game update modifies the
41+ function?).
42+ 3 . Reverting the patch would have to be manually implemented. Some plugins neglect to do this.
2543
26- * Load and parse the config separate from ` LoadGameConfigFile ` with an ` SMCParser ` or similar.
27- I've written this myself as a standalone include file, and there's still a decent amount of
28- manual management that needed doing for every plugin I wrote using it.
29- * Have them place the required information in separate "Keys" section entries with particular
30- naming conventions. This is what [ Memory Patcher] [ ] and [ No Thriller Taunt] [ ] do.
44+ Writing it as an extension allows it to:
45+
46+ 1 . Leverage SourceMod's game configuration parsing logic. No need to reparse the file, and
47+ this provides a uniform convention for patches.
48+ 2 . Use the SourceMod handle system; this allows for managed cleanup and provides a relatively
49+ nice API for developers to work with.
3150
3251[ Memory Patcher ] : https://forums.alliedmods.net/showthread.php?p=2617543
3352[ No Thriller Taunt ] : https://forums.alliedmods.net/showthread.php?t=171343
3453
35- Both solutions (which are the ones that I can think of) are fairly cumbersome.
54+ ## Developer usage
3655
37- The extension comes bundled with ` sourcescramble_manager ` , a helper plugin that reads gamedata
38- file names and patch names from a config file, then loads them all in. For those simple patches
39- that don't need further configuration other than being toggled on.
56+ There are a number of things provided with this extension:
57+
58+ - Memory patches, which are a game config-dedicated section that allows a plugin to safely
59+ overwrite and restore the contents of a memory location.
60+ - Memory blocks, which provide a Handle type to allocate memory.
61+ - Address-of natives.
4062
41- ## Memory patching usage
63+ Developing with this extension is intended for power users that are already getting their hands
64+ dirty with server internals. Most developers do not need this kind of flexibility.
4265
43- ### Writing patches
66+ ### Memory patches
4467
45- This assumes you have a decent enough understanding of x86 assembly. I mean, why else would you
46- be writing one?
68+ Memory patches allow developers to declare a byte payload to be written to memory.
69+
70+ #### Creating new patches
71+
72+ Since patches generally operate on machine code, you'll need to be familiar with that to write
73+ patches.
4774
4875A new ` MemPatches ` section is added at the same level of ` Addresses ` , ` Offsets ` , and
4976` Signatures ` in the game configuration file. An example of a section is below:
5077
5178```
5279"MemPatches"
5380{
54- // this patch makes buildings solid to TFBots
81+ // this patch makes buildings solid to TFBots by forcing certain code paths
5582 "CTraceFilterObject::ShouldHitEntity()::TFBotCollideWithBuildings"
5683 {
5784 "signature" "CTraceFilterObject::ShouldHitEntity()"
@@ -73,33 +100,37 @@ A new `MemPatches` section is added at the same level of `Addresses`, `Offsets`,
73100
74101A few things are present:
75102
76- * A subsection. The name of the section will be the name used when getting a ` MemoryPatch `
103+ - A subsection. The name of the section will be the name used when getting a ` MemoryPatch `
77104handle with ` MemoryPatch.CreateFromConf() ` .
78- * A function ` signature ` name referencing the name of a signature in a ` Signatures ` section
105+ - A function ` signature ` name referencing the name of a signature in a ` Signatures ` section
79106somewhere else in the game config file.
80- * The ` offset ` to patch. Hexadecimal notation is supported with the ` h ` suffix, for easy
107+ - The ` offset ` to patch. Hexadecimal notation is supported with the ` h ` suffix, for easy
81108referencing in IDA or similar.
82- * ` patch ` (required) and ` verify ` (optional) Hex strings (` \x01\x02\x03 ` or ` 01 02 03 ` formats
109+ - ` patch ` (required) and ` verify ` (optional) Hex strings (` \x01\x02\x03 ` or ` 01 02 03 ` formats
83110supported) indicating the byte payload and a signature to match against at the previously
84111mentioned offset.
112+ - ` verify ` signatures can use ` \x2A ` to indicate wildcards, same as SourceMod.
85113
86- ### Automatically applying patches
114+ Any values written on top of an applied patch will be reverted back when the patch is removed.
87115
88- Add a key / value pair to ` configs/sourcescramble_manager.cfg ` , where the key and value
89- correspond to a gamedata file and patch name, respectively.
116+ #### Automatically applying patches
90117
91- Alternatively, you can add your own config files to ` configs/sourcescramble/ ` , making it easy
92- for end-users to install without having to edit the base config file.
118+ Once you've created a game configuration file that gets stored in ` gamedata/ ` , you can apply it
119+ in "managed" mode.
120+
121+ Create a new config file in ` configs/sourcescramble/ ` , following the same format as
122+ ` configs/sourcescramble_manager.cfg ` . Key / value pairs should correspond to a gamedata file
123+ and patch name, respectively.
93124
94125Reload the plugin using ` sm plugins reload sourcescramble_manager ` to reload the patches.
95126No reload command is built-in, because the plugin intentionally leaks and doesn't keep track of
96127handles. (The extension automatically disables patches when the handle is deleted, which
97128happens when the owning plugin is unloaded or reloaded.)
98129
99- ### Manually applying patches
130+ #### Manually applying patches
100131
101132For more complex cases (e.g., scoped hook memory patches or potentially dynamic patch
102- modifications), you'll have to write your own plugin.
133+ modifications), you'll have to write your own plugin to patch / unpatch the memory as desired .
103134
104135This should be fairly self-explanatory:
105136
@@ -117,10 +148,12 @@ if (!patch.Validate()) {
117148
118149// ...
119150
151+ // restore the bytes that were in place when the patch was enabled
152+ // any writes on top of the patched area are also wiped
120153patch.Disable();
121154```
122155
123- ## Memory blocks
156+ ### Memory blocks
124157
125158A ` MemoryBlock ` is a ` calloc ` -allocated chunk of memory that can be accessed with
126159` StoreToAddress ` and ` LoadFromAddress ` (indirectly via wrapped helper methods).
@@ -129,6 +162,8 @@ Some patches I've dealt with operate on fixed locations in memory (e.g., floatin
129162operations that don't take immediate values), so with this I can point to the ` MemoryBlock `
130163address space and put in whatever I need.
131164
165+ It's also a useful way of allocating structures for things like ` SDKCall ` s.
166+
132167Basic use of the API:
133168
134169``` sourcepawn
@@ -142,3 +177,23 @@ Address pFloatBlock = block.Address;
142177// frees the 4 bytes that was allocated
143178delete block;
144179```
180+
181+ ### Address-of natives
182+
183+ New to Source Scramble 0.6.x, this allows a plugin to get the address of one of its own
184+ variables (` cell_t ` or ` char[] ` ).
185+
186+ This replaces certain use cases of memory blocks; you can now point to an existing variable in
187+ the plugin's memory space in cases when you need to read a float value or more granular control
188+ in things like DHooks to send a fixed buffer.
189+
190+ ``` sourcepawn
191+ float g_flValue;
192+
193+ Address pFloatLocation = GetAddressOfCell(g_flValue);
194+ // patch an indirect load or whatever with the address of that float value
195+
196+ g_flValue = 0.75; // changes are reflected instantly wherever this memory location is referenced
197+ ```
198+
199+ Of course, this is all use-at-your-own-risk.
0 commit comments