├── .gitattributes ├── .gitignore ├── pawn.json ├── main.pwn ├── api.md ├── README.md ├── LICENSE.md └── indirection.inc /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pwn linguist-language=Pawn 2 | *.inc linguist-language=Pawn 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Package only files 3 | # 4 | 5 | # Compiled Bytecode, precompiled output and assembly 6 | *.amx 7 | *.lst 8 | *.asm 9 | 10 | # Vendor directory for dependencies 11 | dependencies/ 12 | 13 | # Dependency versions lockfile 14 | pawn.lock 15 | 16 | 17 | # 18 | # Server/gamemode related files 19 | # 20 | 21 | # compiled settings file 22 | # keep `samp.json` file on version control 23 | # but make sure the `rcon_password` field is set externally 24 | # you can use the environment variable `SAMP_RCON_PASSWORD` to do this. 25 | server.cfg 26 | 27 | # Plugins directory 28 | plugins/ 29 | 30 | # binaries 31 | *.exe 32 | *.dll 33 | *.so 34 | announce 35 | samp03svr 36 | samp-npc 37 | 38 | # logs 39 | logs/ 40 | server_log.txt 41 | 42 | # 43 | # Common files 44 | # 45 | 46 | *.sublime-workspace 47 | *.sublime-project 48 | -------------------------------------------------------------------------------- /pawn.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": "Y-Less", 3 | "repo": "indirection", 4 | "entry": "main.pwn", 5 | "output": "indirection.amx", 6 | "dependencies": [ 7 | "sampctl/samp-stdlib", 8 | "Zeex/amx_assembly" 9 | ], 10 | "dev_dependencies": [ 11 | "pawn-lang/YSI-Includes", 12 | "Zeex/samp-plugin-crashdetect" 13 | ], 14 | "builds": [ 15 | { 16 | "name": "default", 17 | "version": "", 18 | "workingDir": "", 19 | "args": null, 20 | "input": "", 21 | "output": "", 22 | "includes": null, 23 | "constants": null 24 | }, 25 | { 26 | "name": "y_testing", 27 | "version": "", 28 | "workingDir": "", 29 | "args": [ 30 | "-;+", 31 | "-(+", 32 | "-Z+", 33 | "-\\+", 34 | "RUN_TESTS=1", 35 | "LIGHT_TEST_REPORT=1", 36 | "TEST_AUTO_EXIT=1", 37 | "_YSI_NO_VERSION_CHECK=1" 38 | ], 39 | "input": "", 40 | "output": "", 41 | "includes": null, 42 | "constants": null 43 | }, 44 | { 45 | "name": "full_testing", 46 | "version": "", 47 | "workingDir": "", 48 | "args": [ 49 | "-;+", 50 | "-d3", 51 | "-O0", 52 | "-(+", 53 | "-Z+", 54 | "-\\+", 55 | "YSI_TESTS=1", 56 | "TEST_AUTO_EXIT=1", 57 | "_YSI_NO_VERSION_CHECK=1", 58 | "_DEBUG=1" 59 | ], 60 | "input": "", 61 | "output": "", 62 | "includes": null, 63 | "constants": null 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /main.pwn: -------------------------------------------------------------------------------- 1 | //#pragma option -a 2 | 3 | #define RUN_TESTS 4 | 5 | #include 6 | #include 7 | #include "indirection" 8 | 9 | new gCall = 0; 10 | 11 | forward TestCallback3(a, arr[], size); 12 | 13 | stock TestCallbackX(n, ...) 14 | { 15 | ASSERT_EQ(numargs(), n + 1); 16 | gCall = 100; 17 | } 18 | 19 | stock TestCallbackV() 20 | { 21 | ASSERT_EQ(numargs(), 0); 22 | gCall = 101; 23 | } 24 | #define CALL@TestCallbackV%8() TestCallbackV%8() 25 | 26 | static TestCallback0(a, b, c) 27 | { 28 | ASSERT_EQ(numargs(), 3); 29 | gCall = a * b * c; 30 | } 31 | 32 | static TestCallback1(a, b, c) 33 | { 34 | ASSERT_EQ(numargs(), 3); 35 | gCall = a + b + c; 36 | } 37 | 38 | static stock TestCallback2(a, const string:s[], d) 39 | { 40 | ASSERT_EQ(numargs(), 3); 41 | ASSERT_SAME(s, "Hello World"); 42 | ASSERT_NE(s[0], '\0'); 43 | gCall = a * d; 44 | } 45 | #define CALL@TestCallback2%8() TestCallback2%8(0,"",0) 46 | 47 | static TestCallback4(a, &b, c) 48 | { 49 | ASSERT_EQ(numargs(), 3); 50 | gCall = a + b + c; 51 | --b; 52 | } 53 | 54 | public TestCallback3(a, arr[], size) 55 | { 56 | ASSERT_EQ(numargs(), 3); 57 | ASSERT_EQ(a, size); 58 | gCall = arr[0] + arr[1]; 59 | } 60 | 61 | TestCall1(Func:func, a, b, c) 62 | { 63 | @.func(a, b, c); 64 | } 65 | #define TestCall1(&%0,%1) TestCall1(addressof(%0),%1) 66 | 67 | TestCallArray1(Func:func, a, b, c) 68 | { 69 | new 70 | arr[3]; 71 | arr[0] = ref(a); 72 | arr[1] = ref(b); 73 | arr[2] = ref(c); 74 | Indirect_Array(_:func, tagof (func), arr); 75 | } 76 | #define TestCallArray1(&%0,%1) TestCallArray1(addressof(%0),%1) 77 | 78 | TestCallArray2(Func:func, a, &b, c) 79 | { 80 | new 81 | arr[3]; 82 | arr[0] = ref(a); 83 | arr[1] = ref(b); 84 | arr[2] = ref(c); 85 | Indirect_Array(_:func, tagof (func), arr); 86 | } 87 | #define TestCallArray2(&%0,%1) TestCallArray2(addressof(%0),%1) 88 | 89 | TestCall2(Func:func, a, d) 90 | { 91 | @.func(a, "Hello World", d); 92 | } 93 | 94 | TestCall3(Func:func, a, arr[], size) 95 | { 96 | @.func(a, arr, size); 97 | } 98 | 99 | @test() Indirect1() 100 | { 101 | gCall = 0; 102 | TestCall1(&TestCallback0, 4, 5, 6); 103 | ASSERT_EQ(gCall, 120); 104 | gCall = 0; 105 | TestCall1(&TestCallback1, 4, 5, 6); 106 | ASSERT_EQ(gCall, 15); 107 | } 108 | 109 | @test() Indirect2() 110 | { 111 | gCall = 0; 112 | TestCall2(Func:addressof(TestCallback2), 99, 100); 113 | ASSERT_EQ(gCall, 9900); 114 | } 115 | 116 | @test() Indirect3() 117 | { 118 | new arr[4] = { 5, 55, 555, 5555 }; 119 | gCall = 0; 120 | TestCall3(Func:Indirect_Ref("TestCallback3"), 4, arr, sizeof (arr)); 121 | ASSERT_EQ(gCall, 60); 122 | } 123 | 124 | @test() IndirectX() 125 | { 126 | @.&TestCallbackX(10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); 127 | } 128 | 129 | @test() IndirectV() 130 | { 131 | @.&TestCallbackV(); 132 | ASSERT(TRUE); 133 | } 134 | 135 | @test() IndirectArray() 136 | { 137 | TestCallArray1(&TestCallback1, 9, 9, 9); 138 | ASSERT_EQ(gCall, 27); 139 | new 140 | a = 8; 141 | TestCallArray2(&TestCallback4, 9, a, 7); 142 | ASSERT_EQ(gCall, 24); 143 | ASSERT_EQ(a, 7); 144 | TestCallArray2(&TestCallback4, 9, a, 7); 145 | ASSERT_EQ(gCall, 23); 146 | ASSERT_EQ(a, 6); 147 | TestCallArray2(&TestCallback4, 9, a, 7); 148 | ASSERT_EQ(gCall, 22); 149 | ASSERT_EQ(a, 5); 150 | } 151 | 152 | @test(.run = false) Indirection() 153 | { 154 | new str[32]; 155 | Indirect_Call(0, 0); 156 | Indirect_Array(0, 0, "", 0); 157 | Indirect_Callstring(0, 0); 158 | Indirect_Callvoid(0, 0); 159 | Indirect_Claim(0); 160 | Indirect_Release(0); 161 | Indirect_Tag(0, str); 162 | new 163 | Func:a = addressof (TestCallback1); 164 | @.a(0, 0, 0); 165 | @.&TestCallback1(0, 0, 0); 166 | } 167 | 168 | static 169 | gTestVar1, 170 | gTestVar2, 171 | gTestVar3, 172 | gTestVar4; 173 | 174 | forward OnPublicIndirectionTest(a, Float:b, const c[], d); 175 | public OnPublicIndirectionTest(a, Float:b, const c[], d) 176 | { 177 | //printf("meta: %d", INDIRECTION_COUNT); 178 | new meta; 179 | if (Indirect_GetMetaInt(0, meta)) 180 | gTestVar1 = a + meta; 181 | else 182 | gTestVar1 = a; 183 | if (Indirect_GetMetaInt(1, meta)) 184 | gTestVar2 = (_:b) + meta; 185 | else 186 | gTestVar2 = _:b; 187 | gTestVar3 = strval(c); 188 | gTestVar4 = d; 189 | } 190 | 191 | #define ALS_DO_PublicIndirectionTest<%1> %1() 192 | #define CALL@OnPublicIndirectionTest%8() OnPublicIndirectionTest%8(0, 0.0, "", 0) 193 | 194 | @test() OnPublicIndirectionTest() 195 | { 196 | gTestVar1 = 100; 197 | gTestVar2 = 100; 198 | gTestVar3 = 100; 199 | gTestVar4 = 100; 200 | @.&OnPublicIndirectionTest[2](5, 6.0, "7", 8); 201 | ASSERT_EQ(gTestVar1, 5 + 2); 202 | ASSERT_EQ(gTestVar2, _:(6.0)); 203 | ASSERT_EQ(gTestVar3, 7); 204 | ASSERT_EQ(gTestVar4, 8); 205 | gTestVar1 = 200; 206 | gTestVar2 = 200; 207 | gTestVar3 = 200; 208 | gTestVar4 = 200; 209 | @.&OnPublicIndirectionTest[5, 6](5, 6.0, "7", 8); 210 | ASSERT_EQ(gTestVar1, 10); 211 | ASSERT_EQ(gTestVar2, _:(6.0) + 6); 212 | ASSERT_EQ(gTestVar3, 7); 213 | ASSERT_EQ(gTestVar4, 8); 214 | gTestVar1 = 300; 215 | gTestVar2 = 300; 216 | gTestVar3 = 300; 217 | gTestVar4 = 300; 218 | @.&OnPublicIndirectionTest(5, 6.0, "7", 8); 219 | ASSERT_EQ(gTestVar1, 5); 220 | ASSERT_EQ(gTestVar2, _:(6.0)); 221 | ASSERT_EQ(gTestVar3, 7); 222 | ASSERT_EQ(gTestVar4, 8); 223 | gTestVar1 = 400; 224 | gTestVar2 = 400; 225 | gTestVar3 = 400; 226 | gTestVar4 = 400; 227 | @.&OnPublicIndirectionTest[1](5, 6.0, "7", 8); 228 | ASSERT_EQ(gTestVar1, 5 + 1); 229 | ASSERT_EQ(gTestVar2, _:(6.0)); 230 | ASSERT_EQ(gTestVar3, 7); 231 | ASSERT_EQ(gTestVar4, 8); 232 | gTestVar1 = 500; 233 | gTestVar2 = 500; 234 | gTestVar3 = 500; 235 | gTestVar4 = 500; 236 | @.&OnPublicIndirectionTest(5, 6.0, "7", 8); 237 | ASSERT_EQ(gTestVar1, 5); 238 | ASSERT_EQ(gTestVar2, _:(6.0)); 239 | ASSERT_EQ(gTestVar3, 7); 240 | ASSERT_EQ(gTestVar4, 8); 241 | } 242 | 243 | 244 | -------------------------------------------------------------------------------- /api.md: -------------------------------------------------------------------------------- 1 | indirection 2 | ========================================== 3 | Function pointers and code redirection. 4 | ------------------------------------------ 5 | Copyright (c) 2022 Alex "Y_Less" Cole. Licensed under MPL 1.1 6 | 7 | Indirection is a system for calling function pointers in a generic and type-safe way. Instead of `CallLocalFunction`, `Call`, `defer`, `Callback_Call`, or any other method, this gives one common interface which can be extended by library authors; utilising tags for compile-time parameters. 8 | 9 | 10 | 11 | 12 | ## Functions 13 | 14 | 15 | ### `Indirect_Call`: 16 | 17 | 18 | #### Syntax 19 | 20 | 21 | ```pawn 22 | Indirect_Call(func, tag, ...) 23 | ``` 24 | 25 | 26 | #### Parameters 27 | 28 | 29 | | **Name** | **Info** | 30 | | --- | --- | 31 | | `func` | | 32 | | `tag` | | 33 | | `...` | ` {_,Bit,Text,Group,File,Float,Text3D} ` | 34 | 35 | #### Estimated stack usage 36 | 1 cells 37 | 38 | 39 | 40 | ### `Indirect_Claim_`: 41 | 42 | 43 | #### Syntax 44 | 45 | 46 | ```pawn 47 | Indirect_Claim_(func) 48 | ``` 49 | 50 | 51 | #### Parameters 52 | 53 | 54 | | **Name** | **Info** | 55 | | --- | --- | 56 | | `func` | The function pointer you want to use later. | 57 | 58 | #### Remarks 59 | If a function pointer is used within one function, that is not a problem. However, if you want to store the function pointer for use later, you must first "claim" it, so that any associated data is not cleared when the parent function ends (i.e. the function that called your function). After use it must be released, and the number of claims must match the number of releases. 60 | 61 | 62 | #### Depends on 63 | * [`gsCodSize`](#gsCodSize) 64 | #### Estimated stack usage 65 | 1 cells 66 | 67 | 68 | 69 | ### `Indirect_DePtr_`: 70 | 71 | 72 | #### Syntax 73 | 74 | 75 | ```pawn 76 | Indirect_DePtr_(ptr) 77 | ``` 78 | 79 | 80 | #### Parameters 81 | 82 | 83 | | **Name** | **Info** | 84 | | --- | --- | 85 | | `ptr` | | 86 | | `` | The array to convert to an offset pointer. | 87 | 88 | #### Remarks 89 | Strings and arrays are passed relative to `COD` not `DAT` so they can be distinguished from normal function pointers. This function does the offset. 90 | 91 | 92 | #### Depends on 93 | * [`gsCodSize`](#gsCodSize) 94 | #### Estimated stack usage 95 | 1 cells 96 | 97 | 98 | 99 | ### `Indirect_FromCallback`: 100 | 101 | 102 | #### Syntax 103 | 104 | 105 | ```pawn 106 | Indirect_FromCallback(cb, release) 107 | ``` 108 | 109 | 110 | #### Parameters 111 | 112 | 113 | | **Name** | **Info** | 114 | | --- | --- | 115 | | `cb` | `F@_@ ` | 116 | | `release` | `bool ` | 117 | 118 | #### Remarks 119 | A generic public wrapper for calling inline functions. 120 | 121 | 122 | #### Depends on 123 | * [`INDIRECTION_DATA`](#INDIRECTION_DATA) 124 | * [`INDIRECTION_NAUGHT`](#INDIRECTION_NAUGHT) 125 | * [`INDIRECTION_TAG`](#INDIRECTION_TAG) 126 | * [`Indirect_Call__`](#Indirect_Call__) 127 | * [`Indirect_Release_`](#Indirect_Release_) 128 | #### Attributes 129 | * `public` 130 | #### Estimated stack usage 131 | 5 cells 132 | 133 | 134 | 135 | ### `Indirect_GetMetaBool`: 136 | 137 | 138 | #### Syntax 139 | 140 | 141 | ```pawn 142 | Indirect_GetMetaBool(index, &ret) 143 | ``` 144 | 145 | 146 | #### Parameters 147 | 148 | 149 | | **Name** | **Info** | 150 | | --- | --- | 151 | | `index` | | 152 | | `ret` | `bool & ` | 153 | 154 | #### Tag 155 | `bool:` 156 | 157 | 158 | #### Remarks 159 | Get boolean metadata. 160 | 161 | 162 | #### Depends on 163 | * [`INDIRECTION_COUNT`](#INDIRECTION_COUNT) 164 | * [`INDIRECTION_META`](#INDIRECTION_META) 165 | * [`false`](#false) 166 | * [`true`](#true) 167 | #### Estimated stack usage 168 | 1 cells 169 | 170 | 171 | 172 | ### `Indirect_GetMetaFloat`: 173 | 174 | 175 | #### Syntax 176 | 177 | 178 | ```pawn 179 | Indirect_GetMetaFloat(index, &ret) 180 | ``` 181 | 182 | 183 | #### Parameters 184 | 185 | 186 | | **Name** | **Info** | 187 | | --- | --- | 188 | | `index` | | 189 | | `ret` | `Float & ` | 190 | 191 | #### Tag 192 | `bool:` 193 | 194 | 195 | #### Remarks 196 | Get float metadata. 197 | 198 | 199 | #### Depends on 200 | * [`INDIRECTION_COUNT`](#INDIRECTION_COUNT) 201 | * [`INDIRECTION_META`](#INDIRECTION_META) 202 | * [`false`](#false) 203 | * [`true`](#true) 204 | #### Estimated stack usage 205 | 1 cells 206 | 207 | 208 | 209 | ### `Indirect_GetMetaInt`: 210 | 211 | 212 | #### Syntax 213 | 214 | 215 | ```pawn 216 | Indirect_GetMetaInt(index, &ret) 217 | ``` 218 | 219 | 220 | #### Parameters 221 | 222 | 223 | | **Name** | **Info** | 224 | | --- | --- | 225 | | `index` | | 226 | | `ret` | ` & ` | 227 | 228 | #### Tag 229 | `bool:` 230 | 231 | 232 | #### Remarks 233 | Get integer metadata. 234 | 235 | 236 | #### Depends on 237 | * [`INDIRECTION_COUNT`](#INDIRECTION_COUNT) 238 | * [`INDIRECTION_META`](#INDIRECTION_META) 239 | * [`false`](#false) 240 | * [`true`](#true) 241 | #### Estimated stack usage 242 | 1 cells 243 | 244 | 245 | 246 | ### `Indirect_GetMetaRef`: 247 | 248 | 249 | #### Syntax 250 | 251 | 252 | ```pawn 253 | Indirect_GetMetaRef(index, &ret) 254 | ``` 255 | 256 | 257 | #### Parameters 258 | 259 | 260 | | **Name** | **Info** | 261 | | --- | --- | 262 | | `index` | | 263 | | `ret` | ` & ` | 264 | 265 | #### Tag 266 | `bool:` 267 | 268 | 269 | #### Remarks 270 | Get ref metadata. 271 | 272 | 273 | #### Depends on 274 | * [`INDIRECTION_COUNT`](#INDIRECTION_COUNT) 275 | * [`INDIRECTION_META`](#INDIRECTION_META) 276 | * [`false`](#false) 277 | * [`true`](#true) 278 | #### Estimated stack usage 279 | 1 cells 280 | 281 | 282 | 283 | ### `Indirect_GetMetaString`: 284 | 285 | 286 | #### Syntax 287 | 288 | 289 | ```pawn 290 | Indirect_GetMetaString(index, dest[], size) 291 | ``` 292 | 293 | 294 | #### Parameters 295 | 296 | 297 | | **Name** | **Info** | 298 | | --- | --- | 299 | | `index` | | 300 | | `dest` | ` [] ` | 301 | | `size` | | 302 | 303 | #### Tag 304 | `bool:` 305 | 306 | 307 | #### Remarks 308 | Get ref metadata. 309 | 310 | 311 | #### Depends on 312 | * [`INDIRECTION_COUNT`](#INDIRECTION_COUNT) 313 | * [`INDIRECTION_META`](#INDIRECTION_META) 314 | * [`Indirect_Strcat`](#Indirect_Strcat) 315 | * [`false`](#false) 316 | * [`true`](#true) 317 | #### Estimated stack usage 318 | 5 cells 319 | 320 | 321 | 322 | ### `Indirect_Init`: 323 | 324 | 325 | #### Syntax 326 | 327 | 328 | ```pawn 329 | Indirect_Init() 330 | ``` 331 | 332 | #### Remarks 333 | Get the size of the COD AMX segment. 334 | 335 | 336 | #### Depends on 337 | * [`AMX_HDR`](#AMX_HDR) 338 | * [`AMX_HDR_COD`](#AMX_HDR_COD) 339 | * [`AMX_HDR_DAT`](#AMX_HDR_DAT) 340 | * [`AddressofResolve`](#AddressofResolve) 341 | * [`GetAmxHeader`](#GetAmxHeader) 342 | * [`gsCodSize`](#gsCodSize) 343 | #### Estimated stack usage 344 | 21 cells 345 | 346 | 347 | 348 | ### `Indirect_Memcpy`: 349 | 350 | 351 | #### Syntax 352 | 353 | 354 | ```pawn 355 | Indirect_Memcpy(dest[], source, index, numbytes, maxlength) 356 | ``` 357 | 358 | 359 | #### Parameters 360 | 361 | 362 | | **Name** | **Info** | 363 | | --- | --- | 364 | | `dest` | ` [] ` | 365 | | `source` | | 366 | | `index` | | 367 | | `numbytes` | | 368 | | `maxlength` | | 369 | 370 | #### Attributes 371 | * `native` 372 | 373 | ### `Indirect_SetMetaRef`: 374 | 375 | 376 | #### Syntax 377 | 378 | 379 | ```pawn 380 | Indirect_SetMetaRef(index, val) 381 | ``` 382 | 383 | 384 | #### Parameters 385 | 386 | 387 | | **Name** | **Info** | 388 | | --- | --- | 389 | | `index` | | 390 | | `val` | | 391 | 392 | #### Tag 393 | `bool:` 394 | 395 | 396 | #### Remarks 397 | Set ref metadata. 398 | 399 | 400 | #### Depends on 401 | * [`INDIRECTION_COUNT`](#INDIRECTION_COUNT) 402 | * [`INDIRECTION_META`](#INDIRECTION_META) 403 | * [`false`](#false) 404 | * [`true`](#true) 405 | #### Estimated stack usage 406 | 1 cells 407 | 408 | 409 | 410 | ### `Indirect_Strcat`: 411 | 412 | 413 | #### Syntax 414 | 415 | 416 | ```pawn 417 | Indirect_Strcat(dest[], source, maxlength) 418 | ``` 419 | 420 | 421 | #### Parameters 422 | 423 | 424 | | **Name** | **Info** | 425 | | --- | --- | 426 | | `dest` | ` [] ` | 427 | | `source` | | 428 | | `maxlength` | | 429 | 430 | #### Attributes 431 | * `native` 432 | 433 | ### `Indirect_Tag`: 434 | 435 | 436 | #### Syntax 437 | 438 | 439 | ```pawn 440 | Indirect_Tag(id, dest[]) 441 | ``` 442 | 443 | 444 | #### Parameters 445 | 446 | 447 | | **Name** | **Info** | 448 | | --- | --- | 449 | | `id` | The ID of the tag to get the specifiers from the name of. | 450 | | `dest` | ` [32] ` Where to store the name. | 451 | 452 | #### Remarks 453 | Functions are tagged with a special tag containing their specifiers. Get the string value of that tag from the AMX header. 454 | 455 | 456 | #### Depends on 457 | * [`GetTagNameFromID`](#GetTagNameFromID) 458 | * [`strcat`](#strcat) 459 | #### Estimated stack usage 460 | 6 cells 461 | 462 | 463 | 464 | ### `OnScriptInit@C`: 465 | 466 | 467 | #### Syntax 468 | 469 | 470 | ```pawn 471 | OnScriptInit@C() 472 | ``` 473 | 474 | #### Depends on 475 | * [`Indirect_Init`](#Indirect_Init) 476 | #### Attributes 477 | * `public` 478 | #### Estimated stack usage 479 | 3 cells 480 | 481 | 482 | 483 | ### `OnScriptInit@E`: 484 | 485 | 486 | #### Syntax 487 | 488 | 489 | ```pawn 490 | OnScriptInit@E() 491 | ``` 492 | 493 | #### Depends on 494 | * [`Indirect_Init`](#Indirect_Init) 495 | #### Attributes 496 | * `public` 497 | #### Estimated stack usage 498 | 3 cells 499 | 500 | 501 | 502 | ### `ScriptInit_OnJITCompile`: 503 | 504 | 505 | #### Syntax 506 | 507 | 508 | ```pawn 509 | ScriptInit_OnJITCompile() 510 | ``` 511 | 512 | #### Depends on 513 | * [`Indirect_Init`](#Indirect_Init) 514 | #### Attributes 515 | * `public` 516 | #### Automaton 517 | _ALS 518 | 519 | 520 | #### Estimated stack usage 521 | 3 cells 522 | 523 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `indirection.inc` 2 | 3 | [![sampctl](https://shields.southcla.ws/badge/sampctl-indirection-2f2f2f.svg?style=for-the-badge)](https://github.com/Y-Less/indirection) 4 | 5 | Indirection is a system for calling function pointers in a generic and type-safe way. Instead of `CallLocalFunction`, `Call`, `defer`, `Callback_Call`, or any other method, this gives one common interface which can be extended by library authors; utilising tags for compile-time parameters: 6 | 7 | ```pawn 8 | Caller(Func:func, playerid) 9 | { 10 | @.func(playerid, _:func, "Hello World", random(5)); 11 | } 12 | 13 | Callee(playerid, func, string[], rand) 14 | { 15 | } 16 | 17 | main() 18 | { 19 | Caller(&Callee, 42); 20 | } 21 | ``` 22 | 23 | ## Installation 24 | 25 | Simply install to your project: 26 | 27 | ```bash 28 | sampctl package install Y-Less/indirection 29 | ``` 30 | 31 | Include in your code and begin using the library: 32 | 33 | ```pawn 34 | #include 35 | ``` 36 | 37 | ## Usage 38 | 39 | ### Basics 40 | 41 | If the library supports it, you can call a function that takes another function using `&`: 42 | 43 | ```pawn 44 | FuncThatWantsACallback(&MyCallback); 45 | ``` 46 | 47 | You can also use y_inline: 48 | 49 | ```pawn 50 | FuncThatWantsACallback(using inline MyInline); 51 | ``` 52 | 53 | Or you can use `addressof` from amx_assembly: 54 | 55 | ```pawn 56 | FuncThatWantsACallback(addressof(MyCallback)); 57 | ``` 58 | 59 | Functions that take other functions are type-safe, so a function that expects a callback that expects two integers will have a type of: 60 | 61 | ```pawn 62 | FuncThatWantsACallback(Func:callback) 63 | { 64 | } 65 | ``` 66 | 67 | Here `ii` is the standard notation for two integer parameters. If you try and pass a function that does not take two integer parameters, the compiler will give a `warning 202: number of arguments does not match definition`: 68 | 69 | ```pawn 70 | Caller(Func:func, playerid) 71 | { 72 | @.func(playerid, _:func, "Hello World", random(5)); 73 | } 74 | 75 | Callee(playerid, func, string[]) 76 | { 77 | } 78 | 79 | main() 80 | { 81 | Caller(&Callee, 42); // `Callee` is not `iisi`, but just `iis`. 82 | } 83 | ``` 84 | 85 | Function pointers are just normal variables, and so can be passed around between functions as normal: 86 | 87 | ```pawn 88 | Caller(Func:func, playerid) 89 | { 90 | @.func(playerid, _:func, "Hello World", random(5)); 91 | } 92 | 93 | Intermediate(Func:func) 94 | { 95 | Caller(func, 42); 96 | } 97 | 98 | Callee(playerid, func, string[], rand) 99 | { 100 | } 101 | 102 | main() 103 | { 104 | Intermediate(&Callee); 105 | } 106 | ``` 107 | 108 | The available "types" are: `i` - any integer, `f` - floats, `a` - arrays, `s` - strings, `v` - references (`&var`), `x` - varargs (`...`, must be last), `tTag:` - tagged with `Tag:`. 109 | 110 | ### Metadata 111 | 112 | You can attach a single piece of metadata to a function. This is arbitrary data that you can access via the function handle: 113 | 114 | ```pawn 115 | Indirect_SetMeta(func, 5); 116 | printf("%d", Indirect_GetMeta(func)); // Prints 5 117 | ``` 118 | 119 | This allows you to associate things like handles, for example a timer handle: 120 | 121 | ``` 122 | stock SetTimerCallback(Func:func<>, time, bool:repeat) 123 | { 124 | Indirect_Claim(func); 125 | new timer = SetTimerEx("@y_inlineTimerInvoke", time, repeat, "ii", _:func, !repeat); 126 | Indirect_SetMeta(func, timer); 127 | return _:func; 128 | } 129 | ``` 130 | 131 | There is also a second type of metadata, which you can pass at call time instead to `@` to configure how the call is made: 132 | 133 | ```pawn 134 | @.timer[1000, true](playerid); 135 | ``` 136 | 137 | Assuming `timer` is a handle to some timer function, the `1000` and `true` would configure how the timer is called, while the `playerid` is a parameter to the timer function itself. This separates information about how the function is called from information passed to the function. `INDIRECTION_META_DATA_SIZE` defines how much metadata can be stored and defaults to `8`; it is stored in the variable `INDIRECTION_META` and the amount is in `INDIRECTION_COUNT`. 138 | 139 | ### Callbacks 140 | 141 | There is special handling in-built for callbacks that start with `On`. This is almost identical to `call` from y_als (indeed it is designed to replace it): 142 | 143 | ```pawn 144 | @.&OnPlayerConnect(playerid); 145 | ``` 146 | 147 | As with most calls this tries to use `CALL@` for both the address and parameter types, but with some special handling to not use the lastest hook if there are any. These macros are pre-defined for all standard SA:MP callbacks in y_als, but can be custom defined for any other callback too: 148 | 149 | ```pawn 150 | #define CALL@OnCallbackExample%8() OnCallbackExample%8(0, 0.0, "", 0) 151 | ``` 152 | 153 | ## Library Writers 154 | 155 | A parameter that takes a function pointer has the tag `Func:`, and is followed by the type specifier for the function. For example `OnPlayerCommandText` would be `Func:func` and `OnUnoccupiedVehicleUpdate` would be `Func:func`. In a function signature this would look like: 156 | 157 | ```pawn 158 | MyFunction(playerid, Func:func) 159 | { 160 | } 161 | ``` 162 | 163 | Pointers are regular variables, so can be stored and copied as normal, with one small exception. This is fine: 164 | 165 | ```pawn 166 | MyFunction(playerid, Func:func) 167 | { 168 | @.func(playerid); 169 | } 170 | ``` 171 | 172 | This is not: 173 | 174 | ```pawn 175 | public TheFuture(playerid, Func:func) 176 | { 177 | @.func(playerid); 178 | } 179 | 180 | MyFunction(playerid, Func:func) 181 | { 182 | SetTimerEx("TheFuture", 1000, 0, "ii", playerid, _:func); 183 | } 184 | ``` 185 | 186 | In this case, the function `func` is used after the original function ends. If you pass the pointer in a timer, save it in a global variable, or in any other way store it to be used at some unknown point in the future you must first "claim" it: 187 | 188 | ```pawn 189 | MyFunction(playerid, Func:func) 190 | { 191 | Indirect_Claim(func); 192 | SetTimerEx("TheFuture", 1000, 0, "ii", playerid, _:func); 193 | } 194 | ``` 195 | 196 | And later "release" it: 197 | 198 | ```pawn 199 | public TheFuture(playerid, Func:func) 200 | { 201 | @.func(playerid); 202 | Indirect_Release(func); 203 | } 204 | ``` 205 | 206 | This is similar to the old y_inline `Callback_Get` and `Callback_Free` functions, but are not always needed, and are not restricted in when they can be called (those y_inline functions are now synonyms for the more generic indirection.inc functions). 207 | 208 | As a library writer, the `&` operator must be explicitly supported for your function-taking functions. This is done with a simple define: 209 | 210 | ```pawn 211 | MyFunction(playerid, Func:func) 212 | { 213 | @.func(playerid); 214 | } 215 | #define MyFunction(%1,&%2) MyFunction(%1, addressof(%2)) 216 | ``` 217 | 218 | The second parameter of this define is `&%2`, which means it will only match when users call your function with something like `MyFunction(playerid, &Callback)`. In that case the `&` is replaced with a typed call to `addressof`. This does mean you have to specify the type twice - once in the function prototype and once in the define. Note, however, that the define can be more strict as the specifier in the function prototype must NOT use the specifier `t` - all tagged vars are just types of integer: 219 | 220 | Good: 221 | 222 | ```pawn 223 | MyFunction(playerid, Func:func) 224 | { 225 | @.func(playerid); 226 | } 227 | #define MyFunction(%1,&%2) MyFunction(%1, addressof(%2)) 228 | ``` 229 | 230 | Bad: 231 | 232 | ```pawn 233 | MyFunction(playerid, Func:func) 234 | { 235 | @.func(playerid); 236 | } 237 | #define MyFunction(%1,&%2) MyFunction(%1, addressof(%2)) 238 | ``` 239 | 240 | ### MySQL Example 241 | 242 | 243 | ### Low-Level Providers 244 | 245 | The function variable is one of three things. 246 | 247 | 1) A function pointer, in which case it will point somewhere in the `COD` section. 248 | 249 | 2) A string pointer, in which case it will point to a string in the `DAT` section, RELATIVE TO `DAT`. This relativity is how indirection.inc can tell the difference. Since `DAT` follows `COD`, any data pointers will be larger than any possible function pointer, and the difference can then just be subtracted. 250 | 251 | 3) A function structure pointer. This points to `DAT` in the same way as strings, but the target is of the form: 252 | 253 | ```pawn 254 | enum E_INDIRECTION 255 | { 256 | E_INDIRECTION_ALWAYS_NULL, // So we can tell this is not a string. 257 | E_INDIRECTION_HANDER, // Called by `@` to handle this pointer. 258 | E_INDIRECTION_CLAIM, // Called by `Indirect_Claim` to handle any required allocations. 259 | E_INDIRECTION_RELEASE, // Called by `Indirect_Release` to handle any required frees. 260 | ... // Any custom data required by your custom handlers. 261 | } 262 | ``` 263 | 264 | `ALWAYS_NULL` is (unsurprisingly) always NULL. This is so that the data at the pointer is a zero-length string, which is treated as not a string at all. 265 | 266 | The `CLAIM` and `RELEASE` functions both have the same signature: 267 | 268 | ```pawn 269 | MyCustomClaim(func[E_INDIRECTION]) 270 | { 271 | } 272 | 273 | MyCustomRelease(func[E_INDIRECTION]) 274 | { 275 | } 276 | ``` 277 | 278 | You can replace the array size there with any custom one to account for additional data after the standard four items. 279 | 280 | The `HANDLER` function has the prototype: 281 | 282 | ```pawn 283 | MyCustomHandler(...) 284 | { 285 | } 286 | ``` 287 | 288 | All the parameters are the parameters the user gave to `@`, i.e. the parameters for the final function to be called. For this reason there are two magic globals: `INDIRECTION_DATA` contains a pointer to the `E_INDIRECTION` data. `INDIRECTION_TAG` contains the index of the function's tag, the string of the tag (which includes all the specifier characters) can be retrieved with `Indirect_Tag(id, dest[64])`. 289 | 290 | ## Testing 291 | 292 | To test, simply run the package: 293 | 294 | ```bash 295 | sampctl package run 296 | ``` 297 | 298 | And connect to `localhost:7777` to test. 299 | 300 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MOZILLA PUBLIC LICENSE 2 | Version 1.1 3 | 4 | --------------- 5 | 6 | 1. Definitions. 7 | 8 | 1.0.1. "Commercial Use" means distribution or otherwise making the 9 | Covered Code available to a third party. 10 | 11 | 1.1. "Contributor" means each entity that creates or contributes to 12 | the creation of Modifications. 13 | 14 | 1.2. "Contributor Version" means the combination of the Original 15 | Code, prior Modifications used by a Contributor, and the Modifications 16 | made by that particular Contributor. 17 | 18 | 1.3. "Covered Code" means the Original Code or Modifications or the 19 | combination of the Original Code and Modifications, in each case 20 | including portions thereof. 21 | 22 | 1.4. "Electronic Distribution Mechanism" means a mechanism generally 23 | accepted in the software development community for the electronic 24 | transfer of data. 25 | 26 | 1.5. "Executable" means Covered Code in any form other than Source 27 | Code. 28 | 29 | 1.6. "Initial Developer" means the individual or entity identified 30 | as the Initial Developer in the Source Code notice required by Exhibit 31 | A. 32 | 33 | 1.7. "Larger Work" means a work which combines Covered Code or 34 | portions thereof with code not governed by the terms of this License. 35 | 36 | 1.8. "License" means this document. 37 | 38 | 1.8.1. "Licensable" means having the right to grant, to the maximum 39 | extent possible, whether at the time of the initial grant or 40 | subsequently acquired, any and all of the rights conveyed herein. 41 | 42 | 1.9. "Modifications" means any addition to or deletion from the 43 | substance or structure of either the Original Code or any previous 44 | Modifications. When Covered Code is released as a series of files, a 45 | Modification is: 46 | A. Any addition to or deletion from the contents of a file 47 | containing Original Code or previous Modifications. 48 | 49 | B. Any new file that contains any part of the Original Code or 50 | previous Modifications. 51 | 52 | 1.10. "Original Code" means Source Code of computer software code 53 | which is described in the Source Code notice required by Exhibit A as 54 | Original Code, and which, at the time of its release under this 55 | License is not already Covered Code governed by this License. 56 | 57 | 1.10.1. "Patent Claims" means any patent claim(s), now owned or 58 | hereafter acquired, including without limitation, method, process, 59 | and apparatus claims, in any patent Licensable by grantor. 60 | 61 | 1.11. "Source Code" means the preferred form of the Covered Code for 62 | making modifications to it, including all modules it contains, plus 63 | any associated interface definition files, scripts used to control 64 | compilation and installation of an Executable, or source code 65 | differential comparisons against either the Original Code or another 66 | well known, available Covered Code of the Contributor's choice. The 67 | Source Code can be in a compressed or archival form, provided the 68 | appropriate decompression or de-archiving software is widely available 69 | for no charge. 70 | 71 | 1.12. "You" (or "Your") means an individual or a legal entity 72 | exercising rights under, and complying with all of the terms of, this 73 | License or a future version of this License issued under Section 6.1. 74 | For legal entities, "You" includes any entity which controls, is 75 | controlled by, or is under common control with You. For purposes of 76 | this definition, "control" means (a) the power, direct or indirect, 77 | to cause the direction or management of such entity, whether by 78 | contract or otherwise, or (b) ownership of more than fifty percent 79 | (50%) of the outstanding shares or beneficial ownership of such 80 | entity. 81 | 82 | 2. Source Code License. 83 | 84 | 2.1. The Initial Developer Grant. 85 | The Initial Developer hereby grants You a world-wide, royalty-free, 86 | non-exclusive license, subject to third party intellectual property 87 | claims: 88 | (a) under intellectual property rights (other than patent or 89 | trademark) Licensable by Initial Developer to use, reproduce, 90 | modify, display, perform, sublicense and distribute the Original 91 | Code (or portions thereof) with or without Modifications, and/or 92 | as part of a Larger Work; and 93 | 94 | (b) under Patents Claims infringed by the making, using or 95 | selling of Original Code, to make, have made, use, practice, 96 | sell, and offer for sale, and/or otherwise dispose of the 97 | Original Code (or portions thereof). 98 | 99 | (c) the licenses granted in this Section 2.1(a) and (b) are 100 | effective on the date Initial Developer first distributes 101 | Original Code under the terms of this License. 102 | 103 | (d) Notwithstanding Section 2.1(b) above, no patent license is 104 | granted: 1) for code that You delete from the Original Code; 2) 105 | separate from the Original Code; or 3) for infringements caused 106 | by: i) the modification of the Original Code or ii) the 107 | combination of the Original Code with other software or devices. 108 | 109 | 2.2. Contributor Grant. 110 | Subject to third party intellectual property claims, each Contributor 111 | hereby grants You a world-wide, royalty-free, non-exclusive license 112 | 113 | (a) under intellectual property rights (other than patent or 114 | trademark) Licensable by Contributor, to use, reproduce, modify, 115 | display, perform, sublicense and distribute the Modifications 116 | created by such Contributor (or portions thereof) either on an 117 | unmodified basis, with other Modifications, as Covered Code 118 | and/or as part of a Larger Work; and 119 | 120 | (b) under Patent Claims infringed by the making, using, or 121 | selling of Modifications made by that Contributor either alone 122 | and/or in combination with its Contributor Version (or portions 123 | of such combination), to make, use, sell, offer for sale, have 124 | made, and/or otherwise dispose of: 1) Modifications made by that 125 | Contributor (or portions thereof); and 2) the combination of 126 | Modifications made by that Contributor with its Contributor 127 | Version (or portions of such combination). 128 | 129 | (c) the licenses granted in Sections 2.2(a) and 2.2(b) are 130 | effective on the date Contributor first makes Commercial Use of 131 | the Covered Code. 132 | 133 | (d) Notwithstanding Section 2.2(b) above, no patent license is 134 | granted: 1) for any code that Contributor has deleted from the 135 | Contributor Version; 2) separate from the Contributor Version; 136 | 3) for infringements caused by: i) third party modifications of 137 | Contributor Version or ii) the combination of Modifications made 138 | by that Contributor with other software (except as part of the 139 | Contributor Version) or other devices; or 4) under Patent Claims 140 | infringed by Covered Code in the absence of Modifications made by 141 | that Contributor. 142 | 143 | 3. Distribution Obligations. 144 | 145 | 3.1. Application of License. 146 | The Modifications which You create or to which You contribute are 147 | governed by the terms of this License, including without limitation 148 | Section 2.2. The Source Code version of Covered Code may be 149 | distributed only under the terms of this License or a future version 150 | of this License released under Section 6.1, and You must include a 151 | copy of this License with every copy of the Source Code You 152 | distribute. You may not offer or impose any terms on any Source Code 153 | version that alters or restricts the applicable version of this 154 | License or the recipients' rights hereunder. However, You may include 155 | an additional document offering the additional rights described in 156 | Section 3.5. 157 | 158 | 3.2. Availability of Source Code. 159 | Any Modification which You create or to which You contribute must be 160 | made available in Source Code form under the terms of this License 161 | either on the same media as an Executable version or via an accepted 162 | Electronic Distribution Mechanism to anyone to whom you made an 163 | Executable version available; and if made available via Electronic 164 | Distribution Mechanism, must remain available for at least twelve (12) 165 | months after the date it initially became available, or at least six 166 | (6) months after a subsequent version of that particular Modification 167 | has been made available to such recipients. You are responsible for 168 | ensuring that the Source Code version remains available even if the 169 | Electronic Distribution Mechanism is maintained by a third party. 170 | 171 | 3.3. Description of Modifications. 172 | You must cause all Covered Code to which You contribute to contain a 173 | file documenting the changes You made to create that Covered Code and 174 | the date of any change. You must include a prominent statement that 175 | the Modification is derived, directly or indirectly, from Original 176 | Code provided by the Initial Developer and including the name of the 177 | Initial Developer in (a) the Source Code, and (b) in any notice in an 178 | Executable version or related documentation in which You describe the 179 | origin or ownership of the Covered Code. 180 | 181 | 3.4. Intellectual Property Matters 182 | (a) Third Party Claims. 183 | If Contributor has knowledge that a license under a third party's 184 | intellectual property rights is required to exercise the rights 185 | granted by such Contributor under Sections 2.1 or 2.2, 186 | Contributor must include a text file with the Source Code 187 | distribution titled "LEGAL" which describes the claim and the 188 | party making the claim in sufficient detail that a recipient will 189 | know whom to contact. If Contributor obtains such knowledge after 190 | the Modification is made available as described in Section 3.2, 191 | Contributor shall promptly modify the LEGAL file in all copies 192 | Contributor makes available thereafter and shall take other steps 193 | (such as notifying appropriate mailing lists or newsgroups) 194 | reasonably calculated to inform those who received the Covered 195 | Code that new knowledge has been obtained. 196 | 197 | (b) Contributor APIs. 198 | If Contributor's Modifications include an application programming 199 | interface and Contributor has knowledge of patent licenses which 200 | are reasonably necessary to implement that API, Contributor must 201 | also include this information in the LEGAL file. 202 | 203 | (c) Representations. 204 | Contributor represents that, except as disclosed pursuant to 205 | Section 3.4(a) above, Contributor believes that Contributor's 206 | Modifications are Contributor's original creation(s) and/or 207 | Contributor has sufficient rights to grant the rights conveyed by 208 | this License. 209 | 210 | 3.5. Required Notices. 211 | You must duplicate the notice in Exhibit A in each file of the Source 212 | Code. If it is not possible to put such notice in a particular Source 213 | Code file due to its structure, then You must include such notice in a 214 | location (such as a relevant directory) where a user would be likely 215 | to look for such a notice. If You created one or more Modification(s) 216 | You may add your name as a Contributor to the notice described in 217 | Exhibit A. You must also duplicate this License in any documentation 218 | for the Source Code where You describe recipients' rights or ownership 219 | rights relating to Covered Code. You may choose to offer, and to 220 | charge a fee for, warranty, support, indemnity or liability 221 | obligations to one or more recipients of Covered Code. However, You 222 | may do so only on Your own behalf, and not on behalf of the Initial 223 | Developer or any Contributor. You must make it absolutely clear than 224 | any such warranty, support, indemnity or liability obligation is 225 | offered by You alone, and You hereby agree to indemnify the Initial 226 | Developer and every Contributor for any liability incurred by the 227 | Initial Developer or such Contributor as a result of warranty, 228 | support, indemnity or liability terms You offer. 229 | 230 | 3.6. Distribution of Executable Versions. 231 | You may distribute Covered Code in Executable form only if the 232 | requirements of Section 3.1-3.5 have been met for that Covered Code, 233 | and if You include a notice stating that the Source Code version of 234 | the Covered Code is available under the terms of this License, 235 | including a description of how and where You have fulfilled the 236 | obligations of Section 3.2. The notice must be conspicuously included 237 | in any notice in an Executable version, related documentation or 238 | collateral in which You describe recipients' rights relating to the 239 | Covered Code. You may distribute the Executable version of Covered 240 | Code or ownership rights under a license of Your choice, which may 241 | contain terms different from this License, provided that You are in 242 | compliance with the terms of this License and that the license for the 243 | Executable version does not attempt to limit or alter the recipient's 244 | rights in the Source Code version from the rights set forth in this 245 | License. If You distribute the Executable version under a different 246 | license You must make it absolutely clear that any terms which differ 247 | from this License are offered by You alone, not by the Initial 248 | Developer or any Contributor. You hereby agree to indemnify the 249 | Initial Developer and every Contributor for any liability incurred by 250 | the Initial Developer or such Contributor as a result of any such 251 | terms You offer. 252 | 253 | 3.7. Larger Works. 254 | You may create a Larger Work by combining Covered Code with other code 255 | not governed by the terms of this License and distribute the Larger 256 | Work as a single product. In such a case, You must make sure the 257 | requirements of this License are fulfilled for the Covered Code. 258 | 259 | 4. Inability to Comply Due to Statute or Regulation. 260 | 261 | If it is impossible for You to comply with any of the terms of this 262 | License with respect to some or all of the Covered Code due to 263 | statute, judicial order, or regulation then You must: (a) comply with 264 | the terms of this License to the maximum extent possible; and (b) 265 | describe the limitations and the code they affect. Such description 266 | must be included in the LEGAL file described in Section 3.4 and must 267 | be included with all distributions of the Source Code. Except to the 268 | extent prohibited by statute or regulation, such description must be 269 | sufficiently detailed for a recipient of ordinary skill to be able to 270 | understand it. 271 | 272 | 5. Application of this License. 273 | 274 | This License applies to code to which the Initial Developer has 275 | attached the notice in Exhibit A and to related Covered Code. 276 | 277 | 6. Versions of the License. 278 | 279 | 6.1. New Versions. 280 | Netscape Communications Corporation ("Netscape") may publish revised 281 | and/or new versions of the License from time to time. Each version 282 | will be given a distinguishing version number. 283 | 284 | 6.2. Effect of New Versions. 285 | Once Covered Code has been published under a particular version of the 286 | License, You may always continue to use it under the terms of that 287 | version. You may also choose to use such Covered Code under the terms 288 | of any subsequent version of the License published by Netscape. No one 289 | other than Netscape has the right to modify the terms applicable to 290 | Covered Code created under this License. 291 | 292 | 6.3. Derivative Works. 293 | If You create or use a modified version of this License (which you may 294 | only do in order to apply it to code which is not already Covered Code 295 | governed by this License), You must (a) rename Your license so that 296 | the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", 297 | "MPL", "NPL" or any confusingly similar phrase do not appear in your 298 | license (except to note that your license differs from this License) 299 | and (b) otherwise make it clear that Your version of the license 300 | contains terms which differ from the Mozilla Public License and 301 | Netscape Public License. (Filling in the name of the Initial 302 | Developer, Original Code or Contributor in the notice described in 303 | Exhibit A shall not of themselves be deemed to be modifications of 304 | this License.) 305 | 306 | 7. DISCLAIMER OF WARRANTY. 307 | 308 | COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 309 | WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 310 | WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF 311 | DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. 312 | THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE 313 | IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, 314 | YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE 315 | COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER 316 | OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF 317 | ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 318 | 319 | 8. TERMINATION. 320 | 321 | 8.1. This License and the rights granted hereunder will terminate 322 | automatically if You fail to comply with terms herein and fail to cure 323 | such breach within 30 days of becoming aware of the breach. All 324 | sublicenses to the Covered Code which are properly granted shall 325 | survive any termination of this License. Provisions which, by their 326 | nature, must remain in effect beyond the termination of this License 327 | shall survive. 328 | 329 | 8.2. If You initiate litigation by asserting a patent infringement 330 | claim (excluding declatory judgment actions) against Initial Developer 331 | or a Contributor (the Initial Developer or Contributor against whom 332 | You file such action is referred to as "Participant") alleging that: 333 | 334 | (a) such Participant's Contributor Version directly or indirectly 335 | infringes any patent, then any and all rights granted by such 336 | Participant to You under Sections 2.1 and/or 2.2 of this License 337 | shall, upon 60 days notice from Participant terminate prospectively, 338 | unless if within 60 days after receipt of notice You either: (i) 339 | agree in writing to pay Participant a mutually agreeable reasonable 340 | royalty for Your past and future use of Modifications made by such 341 | Participant, or (ii) withdraw Your litigation claim with respect to 342 | the Contributor Version against such Participant. If within 60 days 343 | of notice, a reasonable royalty and payment arrangement are not 344 | mutually agreed upon in writing by the parties or the litigation claim 345 | is not withdrawn, the rights granted by Participant to You under 346 | Sections 2.1 and/or 2.2 automatically terminate at the expiration of 347 | the 60 day notice period specified above. 348 | 349 | (b) any software, hardware, or device, other than such Participant's 350 | Contributor Version, directly or indirectly infringes any patent, then 351 | any rights granted to You by such Participant under Sections 2.1(b) 352 | and 2.2(b) are revoked effective as of the date You first made, used, 353 | sold, distributed, or had made, Modifications made by that 354 | Participant. 355 | 356 | 8.3. If You assert a patent infringement claim against Participant 357 | alleging that such Participant's Contributor Version directly or 358 | indirectly infringes any patent where such claim is resolved (such as 359 | by license or settlement) prior to the initiation of patent 360 | infringement litigation, then the reasonable value of the licenses 361 | granted by such Participant under Sections 2.1 or 2.2 shall be taken 362 | into account in determining the amount or value of any payment or 363 | license. 364 | 365 | 8.4. In the event of termination under Sections 8.1 or 8.2 above, 366 | all end user license agreements (excluding distributors and resellers) 367 | which have been validly granted by You or any distributor hereunder 368 | prior to termination shall survive termination. 369 | 370 | 9. LIMITATION OF LIABILITY. 371 | 372 | UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT 373 | (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL 374 | DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, 375 | OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR 376 | ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY 377 | CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, 378 | WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER 379 | COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN 380 | INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF 381 | LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY 382 | RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW 383 | PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE 384 | EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO 385 | THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 386 | 387 | 10. U.S. GOVERNMENT END USERS. 388 | 389 | The Covered Code is a "commercial item," as that term is defined in 390 | 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer 391 | software" and "commercial computer software documentation," as such 392 | terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 393 | C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), 394 | all U.S. Government End Users acquire Covered Code with only those 395 | rights set forth herein. 396 | 397 | 11. MISCELLANEOUS. 398 | 399 | This License represents the complete agreement concerning subject 400 | matter hereof. If any provision of this License is held to be 401 | unenforceable, such provision shall be reformed only to the extent 402 | necessary to make it enforceable. This License shall be governed by 403 | California law provisions (except to the extent applicable law, if 404 | any, provides otherwise), excluding its conflict-of-law provisions. 405 | With respect to disputes in which at least one party is a citizen of, 406 | or an entity chartered or registered to do business in the United 407 | States of America, any litigation relating to this License shall be 408 | subject to the jurisdiction of the Federal Courts of the Northern 409 | District of California, with venue lying in Santa Clara County, 410 | California, with the losing party responsible for costs, including 411 | without limitation, court costs and reasonable attorneys' fees and 412 | expenses. The application of the United Nations Convention on 413 | Contracts for the International Sale of Goods is expressly excluded. 414 | Any law or regulation which provides that the language of a contract 415 | shall be construed against the drafter shall not apply to this 416 | License. 417 | 418 | 12. RESPONSIBILITY FOR CLAIMS. 419 | 420 | As between Initial Developer and the Contributors, each party is 421 | responsible for claims and damages arising, directly or indirectly, 422 | out of its utilization of rights under this License and You agree to 423 | work with Initial Developer and Contributors to distribute such 424 | responsibility on an equitable basis. Nothing herein is intended or 425 | shall be deemed to constitute any admission of liability. 426 | 427 | 13. MULTIPLE-LICENSED CODE. 428 | 429 | Initial Developer may designate portions of the Covered Code as 430 | "Multiple-Licensed". "Multiple-Licensed" means that the Initial 431 | Developer permits you to utilize portions of the Covered Code under 432 | Your choice of the MPL or the alternative licenses, if any, specified 433 | by the Initial Developer in the file described in Exhibit A. 434 | 435 | EXHIBIT A -Mozilla Public License. 436 | 437 | ``The contents of this file are subject to the Mozilla Public License 438 | Version 1.1 (the "License"); you may not use this file except in 439 | compliance with the License. You may obtain a copy of the License at 440 | https://www.mozilla.org/MPL/ 441 | 442 | Software distributed under the License is distributed on an "AS IS" 443 | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 444 | License for the specific language governing rights and limitations 445 | under the License. 446 | 447 | The Original Code is the indirection.inc function pointers library. 448 | 449 | The Initial Developer of the Original Code is Alex "Y_Less" Cole. 450 | Portions created by the Initial Developer are Copyright (c) 2022 451 | the Initial Developer. All Rights Reserved. 452 | 453 | 454 | -------------------------------------------------------------------------------- /indirection.inc: -------------------------------------------------------------------------------- 1 | /* 2 | Version: MPL 1.1 3 | 4 | The contents of this file are subject to the Mozilla Public License Version 5 | 1.1 (the "License"); you may not use this file except in compliance with 6 | the License. You may obtain a copy of the License at 7 | http://www.mozilla.org/MPL/ 8 | 9 | Software distributed under the License is distributed on an "AS IS" basis, 10 | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 | for the specific language governing rights and limitations under the 12 | License. 13 | 14 | The Original Code is the indirection.inc function pointers library. 15 | 16 | The Initial Developer of the Original Code is Alex "Y_Less" Cole. 17 | Portions created by the Initial Developer are Copyright (c) 2022 18 | the Initial Developer. All Rights Reserved. 19 | */ 20 | 21 | #if defined _INC_indirection 22 | #endinput 23 | #endif 24 | #define _INC_indirection 25 | 26 | /** 27 | * 32 | * 33 | * This library uses the enhanced pawndoc.xsl from 34 | * pawn-lang/pawndoc. 35 | * This XSL has features such as library and markdown support, and will not 36 | * render this message when used. 37 | * 38 | * 39 | * Indirection is a system for calling function pointers in a generic and 40 | * type-safe way. Instead of CallLocalFunction, Call, 41 | * defer, Callback_Call, or any other method, this gives one 42 | * common interface which can be extended by library authors; utilising 43 | * tags for compile-time parameters. 44 | * 45 | * 46 | */ 47 | 48 | ///

49 | 50 | #if !defined AMX_HEADER_INC 51 | #tryinclude "..\amx\amx_header" 52 | #endif 53 | #if !defined AMX_HEADER_INC 54 | #tryinclude 55 | #endif 56 | #if !defined AMX_HEADER_INC 57 | #tryinclude "..\amx_assembly\amx_header" 58 | #endif 59 | #if !defined AMX_HEADER_INC 60 | #tryinclude "..\..\amx_assembly\amx_header" 61 | #endif 62 | #if !defined AMX_HEADER_INC 63 | #tryinclude "amx_header" 64 | #endif 65 | #if !defined AMX_HEADER_INC 66 | #tryinclude 67 | #endif 68 | #if !defined AMX_HEADER_INC 69 | #define AMX_INCLUDING_FAILED 70 | #endif 71 | 72 | #if defined AMX_INCLUDING_FAILED 73 | #error Could not include "https://github.com/Zeex/amx_assembly" - ensure its files are in "include\amx_assembly\" 74 | //#elseif !defined AddressofResolve 75 | // #error Please update "https://github.com/Zeex/amx_assembly" to the latest version. 76 | #endif 77 | 78 | #if defined CUSTOM_TAG_TYPES 79 | #define GLOBAL_TAG_TYPES {_,Bit,Text,Group,File,Float,Text3D,CUSTOM_TAG_TYPES} 80 | #else 81 | #define GLOBAL_TAG_TYPES {_,Bit,Text,Group,File,Float,Text3D} 82 | #endif 83 | 84 | #if !defined INDIRECTION_META_DATA_SIZE 85 | #define INDIRECTION_META_DATA_SIZE (8) 86 | #endif 87 | 88 | enum E_INDIRECTION 89 | { 90 | E_INDIRECTION_ALWAYS_NULL, // So we can tell this is not a string. 91 | E_INDIRECTION_HANDER, // Called by `@` to handle this pointer. 92 | E_INDIRECTION_CLAIM, // Called by `Indirect_Claim`. 93 | E_INDIRECTION_RELEASE, // Called by `Indirect_Release`. 94 | E_INDIRECTION_METADATA, // Only used by end-users. 95 | E_INDIRECTION_TAG, // Save the encoded parameters tag of this callback. 96 | E_INDIRECTION_OWNER, // Attach the function to players etc. 97 | E_INDIRECTION_NEXT // Next pointer in a generic list of callbacks. 98 | } 99 | 100 | stock 101 | INDIRECTION_META[INDIRECTION_META_DATA_SIZE], 102 | INDIRECTION_COUNT = 0, 103 | INDIRECTION_DATA = 0, 104 | INDIRECTION_TAG = 0; 105 | 106 | // Constant offsets for assembly. 107 | const 108 | E_INDIRECTION_ALWAYS_NULL__ = _:E_INDIRECTION_ALWAYS_NULL * cellbytes, 109 | E_INDIRECTION_HANDER__ = _:E_INDIRECTION_HANDER * cellbytes, 110 | E_INDIRECTION_CLAIM__ = _:E_INDIRECTION_CLAIM * cellbytes, 111 | E_INDIRECTION_RELEASE__ = _:E_INDIRECTION_RELEASE * cellbytes, 112 | E_INDIRECTION_METADATA__ = _:E_INDIRECTION_METADATA * cellbytes, 113 | E_INDIRECTION_TAG__ = _:E_INDIRECTION_TAG * cellbytes, 114 | E_INDIRECTION_OWNER__ = _:E_INDIRECTION_OWNER * cellbytes, 115 | E_INDIRECTION_NEXT__ = _:E_INDIRECTION_NEXT * cellbytes, 116 | E_INDIRECTION__ = _:E_INDIRECTION * cellbytes; 117 | 118 | stock const 119 | INDIRECTION_NAUGHT = 0; 120 | 121 | static stock 122 | INDR_gsAddr = 0, 123 | INDR_gsTmp = 0, 124 | INDR_gsTag[64], 125 | INDR_gsCodSize = 0, // The size of `COD`. 126 | INDR_gsKnownOwnedCallbacks = 0; 127 | 128 | #if !defined YSI_MAX_STRING 129 | #define YSI_MAX_STRING (144) 130 | #endif 131 | 132 | #define string: 133 | #define void: 134 | 135 | #define PP_LEFT_BRACKET<> ( 136 | 137 | #if !defined TAGOF 138 | #if ((__Pawn & 0x0F) >= 0x0A) || ((__Pawn & 0xF0) >= 0xA0) 139 | // Defer `tagof` on things with colons slightly to remove anything after 140 | // it. This lets us do `tagof (Float:i)` with no problem, since the `i` 141 | // is removed. 142 | #define TAGOF(%0); 143 | #define __TAGOF__[%0] %0: 144 | #define tagof(%0:%1) __NO_TAGOF:__IS_TAGOF:tagof PP_LEFT_BRACKET<>%0:$%1) 145 | #define __NO_TAGOF:__IS_TAGOF:tagof%9<>%0)%2:$%1) tagof%9<>%0)%2:%1) 146 | #define __IS_TAGOF:tagof%9<>%0:$%1) tagof%9<>__TAGOF__[%0]) 147 | #elseif defined __COMPILER_TAG_DATA 148 | #define TAGOF(%0); 149 | #else 150 | #define TAGOF(%0); stock const %0:__TAGOF__%0; 151 | #define __TAGOF__%0\32; __TAGOF__ 152 | #define tagof(%0:%1) __NO_TAGOF:__IS_TAGOF:tagof PP_LEFT_BRACKET<>%0:$%1) 153 | #define __NO_TAGOF:__IS_TAGOF:tagof%9<>%0)%2:$%1) tagof%9<>%0)%2:%1) 154 | #define __IS_TAGOF:tagof%9<>%0:$%1) tagof%9<>__TAGOF__%0) 155 | #endif 156 | 157 | #if !defined __COMPILER_TAG_DATA 158 | stock bool:operator==(_:a, __NO_TAGOF:b) 159 | { 160 | return a == _:b; 161 | } 162 | 163 | stock bool:operator!=(_:a, __NO_TAGOF:b) 164 | { 165 | return a != _:b; 166 | } 167 | #endif 168 | 169 | #define __TAGOF__F@_@%0\32; __TAGOF__F@_@ 170 | #if defined __COMPILER_TAG_DATA 171 | #define SPECIFIER(%0) __COMPILER_TAG_DATA(F@_@%0,0) 172 | #else 173 | #define SPECIFIER(%0) TAGOF(F@_@%0) 174 | #endif 175 | #endif 176 | 177 | #if defined __PawnBuild 178 | // Because I somewhat scuppered myself! 179 | #if __PawnBuild == 0 180 | #define INDIRECTION_FUNC_INC__ 181 | #undef __PawnBuild 182 | #endif 183 | #else 184 | #define INDIRECTION_FUNC_INC__ 185 | #endif 186 | #if defined INDIRECTION_FUNC_INC__ 187 | forward __Indirect_FuncInc__(); 188 | 189 | public __Indirect_FuncInc__() 190 | { 191 | memcpy 192 | ("", "", 0, 0, 0); 193 | funcidx 194 | (""); 195 | } 196 | 197 | #undef INDIRECTION_FUNC_INC__ 198 | #endif 199 | 200 | TAGOF(Float); 201 | TAGOF(File); 202 | 203 | // Technically the function tags are not always `F@_@`. The third character can 204 | // be anything, and is reserved for representing the return type. However, it 205 | // is currently not used. DO NOT consume spaces after `F@_@` since this will 206 | // break functions with no parameters since their tag is just `F@_@:` (or 207 | // `F@_@_`). Function tags could be weak, which means if you pass a pointer to 208 | // a function that wants a different type, or pass an untyped pointer to a 209 | // function that wants any type, you will get a mismatch warning. However, you 210 | // can pass a typed pointer to a function that doesn't expect any type for 211 | // backwards-compatability reasons. I decided against this though - make them 212 | // strong and give warnings on all old code! 213 | #define Func:%0<%1> F@_@%1:%0 214 | 215 | // Examples of predeclarations of specifier types. These fix a bug in the old 216 | // compiler where you can't (always) do `tagof (Float:)`. This is relevant here 217 | // since the specifiers are passed as `F@_@i:Func`, so we need `tagof (F@_@i:)` 218 | // to get at the specifier. However, since that doesn't work, we instead need 219 | // `tagof (__TAGOF__F@_@i)` with `stock const F@_@i:__TAGOF__F@_@i`. I.e. we need a 220 | // variable using the tag to get the tag from, instead of getting it directly. 221 | SPECIFIER(i); 222 | SPECIFIER(s); 223 | 224 | // This is for getting excess parameters on non-strings. 225 | #define _:%0,) _:%0) 226 | 227 | // I did consider an alternate method of making `@` do this: 228 | // 229 | // #define @.%0(%1) Indirect_Data(%0),Indirect_Call(%1) 230 | // 231 | // But that would fail when an `@` call contained another `@` call: 232 | // 233 | // @.Func1(@.Func2(42, 43)); 234 | // 235 | // It would save the stack manipulation though (not convinced it would be less 236 | // code though with the generation of multiple function calls). 237 | #define @ str_new_static 238 | 239 | // V2 design, with meta-data on `@`. Can now do: 240 | // 241 | // @.var(params) 242 | // @.&func(params) 243 | // @.&func(params) 244 | // @.func[meta](params) 245 | // 246 | #define str_new_static.%0(%1) (Indirect_Call__(_:(INDIRECTION_DATA=_:%0,INDIRECTION_TAG=tagof(%0),Indirect_Meta()),%1)) 247 | 248 | /// indirection 249 | native Indirect_Memcpy(dest[], source, index = 0, numbytes, maxlength = sizeof (dest)) = memcpy; 250 | 251 | /// indirection 252 | stock Indirect_Meta(GLOBAL_TAG_TYPES:...) 253 | { 254 | // Offset to the first parameter. 255 | const cells0 = 3 * cellbytes; 256 | const cells1 = 2 * cellbytes; 257 | new meta; 258 | #emit ADDR.pri cells0 259 | #emit STOR.S.pri meta 260 | #emit LOAD.S.pri cells1 261 | #emit STOR.pri INDIRECTION_COUNT 262 | Indirect_Memcpy(INDIRECTION_META, meta, 0, INDIRECTION_COUNT, INDIRECTION_META_DATA_SIZE); 263 | return (INDIRECTION_COUNT /= cellbytes); 264 | } 265 | 266 | // Look for `&` for variable dereferencing. 267 | #define INDIRECTION_DATA=_:%9&%0,INDIRECTION_TAG=tagof(%9), INDIRECTION_DATA=_:INDIR2_:addressof(%0),INDIRECTION_TAG=prototypeof(%0), 268 | 269 | // Now we know this is an indirection call, are there any meta-data params? 270 | #define Indirect_Call__(_:(INDIRECTION_DATA=_:%0[%2]%6,INDIRECTION_TAG=tagof(%4),Indirect_Meta()),%3) Indirect_Call__(_:(INDIRECTION_DATA=_:%0%6,INDIRECTION_TAG=tagof(%4),Indirect_Meta(%2)),%3) 271 | 272 | // Look for `<>` generic tag types. 273 | #define INDIR2_:addressof(%0<%4>),INDIRECTION_TAG=prototypeof(%9), addressof(%0<%4>),F@_@%4:INDIRECTION_TAG=F@_@%4:tagof(F@_@%4:), 274 | 275 | // The `ALS_DO` callback syntax is `CALLBACK(params)` 276 | #define Indirect_DoCallback<%0,%4>(%9) O@A@("On"#%0),F@_@%4:INDIRECTION_TAG=F@_@%4:tagof(F@_@%4:), 277 | 278 | // Simplify the complex ALS syntax. 279 | //#define Indirect_ALS<%0><%1> %1<,%0>() 280 | // Example: 281 | // 282 | // #define ALS_DO_PlayerConnect Indirect_ALSTag 283 | // 284 | 285 | // Can now do: 286 | // 287 | // @.var(params) 288 | // @.&func(params) 289 | // @.func[meta](params) 290 | // @.OnPlayerConnect(params) 291 | // 292 | 293 | /// indirection 294 | forward Indirect_Call(func, tag, GLOBAL_TAG_TYPES:...); 295 | 296 | // Old API. 297 | #define Indirect_Call(%0,%1) (Indirect_Call__(_:INDIR1_:(INDIRECTION_DATA=(%0),INDIRECTION_TAG=(%1),Indirect_Meta()))) 298 | #define INDIR1_:(INDIRECTION_DATA=(%0),INDIRECTION_TAG=(%1,%2),Indirect_Meta())) (INDIRECTION_DATA=(%0),INDIRECTION_TAG=(%1),Indirect_Meta()),%2) 299 | 300 | #define Indirect_Meta() INDIRECTION_NAUGHT 301 | 302 | /// indirection 303 | stock Indirect_Call__(meta, GLOBAL_TAG_TYPES:...) 304 | { 305 | const cells0 = 4 * cellbytes; 306 | const cells1 = -1 * cellbytes; 307 | const cells3 = 1 * cellbytes; 308 | const cells4 = 2 * cellbytes; 309 | #pragma unused meta 310 | if (!INDIRECTION_DATA) 311 | { 312 | return 0; 313 | } 314 | {} 315 | // The COD and DAT segments are different, so `func` pointing in to DAT 316 | // relative to COD will not be a valid pointer, and THAT you can detect! 317 | // 318 | // Get the previous frame. This undoes the effects of our `PROC` so the 319 | // called function must have a proper `PROC`. 320 | #emit ADDR.pri cells0 321 | #emit STOR.pri INDR_gsAddr 322 | #emit POP.pri 323 | #emit SCTRL 5 324 | // Get the return address. 325 | #emit POP.alt 326 | // Get the parameter count. 327 | #emit POP.pri 328 | // Reduce the parameter count. 329 | #emit ADD.C cells1 330 | // Store the new parameter count. 331 | #emit SWAP.pri 332 | #emit STOR.pri INDIRECTION_COUNT 333 | // Store the return address. 334 | #emit PUSH.alt 335 | // Check the pointer type. If it is in the `COD` area, jump straight to it 336 | // with the tag for parameter types (if it isn't 0). Otherwise, use the 337 | // `func` from `DAT` as a pointer to a handler. 338 | if (INDIRECTION_DATA >= INDR_gsCodSize) 339 | { 340 | // Get the data at `func - COD`. 341 | #emit LOAD.pri INDIRECTION_DATA 342 | #emit LOAD.alt INDR_gsCodSize 343 | #emit SUB 344 | #emit MOVE.alt 345 | #emit LOAD.I 346 | #emit STOR.pri INDIRECTION_DATA 347 | if (!INDIRECTION_DATA) 348 | { 349 | // Get the function at `func - COD + 4`. 350 | #emit CONST.pri E_INDIRECTION_HANDER__ 351 | #emit ADD 352 | #emit LOAD.I 353 | // Call it, passing `func` as a proper pointer, NOT skipping `PROC`. 354 | #emit STOR.alt INDIRECTION_DATA 355 | #emit SCTRL 6 356 | // NEVER RETURNS PAST HERE. 357 | } 358 | {} 359 | // `INDIRECTION_DATA` is now a pointer to a string of a function name. 360 | // Resolve it via index lookup. 361 | #emit PUSH.alt 362 | #emit PUSH.C cells3 363 | #emit SYSREQ.C funcidx 364 | #emit STACK cells4 365 | #emit STOR.pri INDIRECTION_DATA 366 | if (INDIRECTION_DATA == -1) 367 | { 368 | #emit PROC 369 | #emit RETN 370 | } 371 | {} 372 | // Get the address from the index. 373 | #if cellbits == 64 374 | const shifter = 3; 375 | #else 376 | const shifter = 2; 377 | #endif 378 | #emit LCTRL __dat 379 | #emit NEG 380 | #emit ADD.C 32 // Always this, regardless of cell size. 381 | #emit STOR.pri INDR_gsTmp 382 | #emit LREF.alt INDR_gsTmp 383 | #emit LCTRL __dat 384 | #emit NEG 385 | #emit ADD 386 | // This replicates `IDXADDR`, but in no more instructions since the data 387 | // are in the wrong registers. 388 | #emit LOAD.alt INDIRECTION_DATA 389 | #emit SHL.C.alt shifter 390 | #emit ADD 391 | // Add more data, to account for `__defsize` offsets. 392 | #emit LOAD.alt INDIRECTION_DATA 393 | #emit SHL.C.alt 2 394 | #emit ADD 395 | #emit STOR.pri INDR_gsTmp 396 | #emit LREF.pri INDR_gsTmp 397 | #emit STOR.pri INDIRECTION_DATA 398 | } 399 | if (INDIRECTION_TAG) 400 | { 401 | // Skip the `F@_@` prefix. 402 | GetTagNameFromID(INDIRECTION_TAG, INDR_gsTag); 403 | if (INDR_gsTag[0]) for (INDR_gsTmp = 4; ; ) 404 | { 405 | switch (INDR_gsTag[INDR_gsTmp++]) 406 | { 407 | case 'i', 'd', 't', 'f', 'c': 408 | { 409 | // Resolve non-reference parameters. 410 | #emit LREF.pri INDR_gsAddr 411 | #emit LOAD.I 412 | #emit SREF.pri INDR_gsAddr 413 | } 414 | case '\0', 'x': 415 | break; 416 | } 417 | INDR_gsAddr += cellbytes; 418 | } 419 | } 420 | {} 421 | // No handler, and no tag data. Just jump to it and hope (hope it has a `PROC` too). 422 | #emit LOAD.pri INDIRECTION_DATA 423 | #emit SCTRL 6 424 | return 0; 425 | } 426 | 427 | /// indirection 428 | /// 429 | /// Not `Indirect_CallString` to make use of the `string:` macro. 430 | /// 431 | stock string:Indirect_Callstring(func, tag, GLOBAL_TAG_TYPES:...) 432 | { 433 | const cells0 = 2 * cellbytes; 434 | const cells1 = 3 * cellbytes; 435 | const cells2 = 5 * cellbytes; 436 | const cells3 = -2 * cellbytes; 437 | const cells4 = 1 * cellbytes; 438 | const cells5 = 1 * cellbytes; 439 | const cells6 = 2 * cellbytes; 440 | #pragma unused tag 441 | if (!func) 442 | { 443 | // Get the offset to the secret return parameter. 444 | #emit LOAD.S.pri cells0 445 | #emit ADDR.alt cells1 446 | #emit ADD 447 | #emit LOAD.I 448 | #emit MOVE.alt 449 | #emit ZERO.pri 450 | #emit STOR.I 451 | #emit RETN 452 | new ret[YSI_MAX_STRING]; 453 | return ret; 454 | } 455 | {} 456 | #emit ADDR.pri cells2 457 | #emit STOR.pri INDR_gsAddr 458 | #emit POP.pri 459 | #emit SCTRL 5 460 | // Get the return address. 461 | #emit POP.alt 462 | // Get the parameter count. 463 | #emit POP.pri 464 | // Reduce the parameter count. 465 | #emit ADD.C cells3 466 | // Store the return address. 467 | #emit SWAP.alt 468 | #emit STOR.alt INDIRECTION_DATA 469 | #emit POP.alt 470 | // Store the new parameter count. 471 | #emit SWAP.pri 472 | #emit STOR.pri INDIRECTION_TAG 473 | #emit PUSH.alt 474 | // Check the pointer type. If it is in the `COD` area, jump straight to it 475 | // with the tag for parameter types (if it isn't 0). Otherwise, use the 476 | // `func` from `DAT` as a pointer to a handler. 477 | if (INDIRECTION_DATA >= INDR_gsCodSize) 478 | { 479 | // Get the data at `func - COD`. 480 | #emit LOAD.pri INDIRECTION_DATA 481 | #emit LOAD.alt INDR_gsCodSize 482 | #emit SUB 483 | #emit MOVE.alt 484 | #emit LOAD.I 485 | #emit STOR.pri INDIRECTION_DATA 486 | if (!INDIRECTION_DATA) 487 | { 488 | // Get the function at `func - COD + 4`. 489 | #emit CONST.pri cells4 490 | #emit ADD 491 | #emit LOAD.I 492 | // Call it, passing `func` as a proper pointer, NOT skipping `PROC`. 493 | #emit STOR.alt INDIRECTION_DATA 494 | #emit SCTRL 6 495 | // NEVER RETURNS PAST HERE. 496 | } 497 | {} 498 | // `INDIRECTION_DATA` is now a pointer to a string of a function name. 499 | // Resolve it via index lookup. 500 | #emit PUSH.alt 501 | #emit PUSH.C cells5 502 | #emit SYSREQ.C funcidx 503 | #emit STACK cells6 504 | #emit STOR.pri INDIRECTION_DATA 505 | if (INDIRECTION_DATA == -1) 506 | { 507 | #emit PROC 508 | #emit RETN 509 | } 510 | {} 511 | // Get the address from the index. 512 | #if cellbits == 64 513 | const shifter = 3; 514 | #else 515 | const shifter = 2; 516 | #endif 517 | #emit LCTRL __dat 518 | #emit NEG 519 | #emit ADD.C 32 // Always this, regardless of cell size. 520 | #emit STOR.pri INDR_gsTmp 521 | #emit LREF.alt INDR_gsTmp 522 | #emit LCTRL __dat 523 | #emit NEG 524 | #emit ADD 525 | // This replicates `IDXADDR`, but in no more instructions since the data 526 | // are in the wrong registers. 527 | #emit LOAD.alt INDIRECTION_DATA 528 | #emit SHL.C.alt shifter 529 | #emit ADD 530 | // Add more data, to account for `__defsize` offsets. 531 | #emit LOAD.alt INDIRECTION_DATA 532 | #emit SHL.C.alt 2 533 | #emit ADD 534 | #emit STOR.pri INDR_gsTmp 535 | #emit LREF.pri INDR_gsTmp 536 | #emit STOR.pri INDIRECTION_DATA 537 | } 538 | if (INDIRECTION_TAG) 539 | { 540 | // Skip the `F@_@` prefix. 541 | GetTagNameFromID(INDIRECTION_TAG, INDR_gsTag); 542 | if (INDR_gsTag[0]) for (INDR_gsTmp = 4; ; ) 543 | { 544 | switch (INDR_gsTag[INDR_gsTmp++]) 545 | { 546 | case 'i', 'd', 't', 'f', 'c': 547 | { 548 | // Resolve non-reference parameters. 549 | #emit LREF.pri INDR_gsAddr 550 | #emit LOAD.I 551 | #emit SREF.pri INDR_gsAddr 552 | } 553 | case '\0', 'x': 554 | break; 555 | } 556 | INDR_gsAddr += cellbytes; 557 | } 558 | } 559 | {} 560 | // No handler, and no tag data. Just jump to it and hope. 561 | #emit LOAD.pri INDIRECTION_DATA 562 | #emit SCTRL 6 563 | // Never called. Don't use "static" because it would allocate real memory 564 | // in advance. Instead, this will want to allocate on the stack but never 565 | // get hit to do so. 566 | new ret[YSI_MAX_STRING]; 567 | return ret; 568 | } 569 | 570 | /// indirection 571 | /// 572 | /// Not `Indirect_Callvoid` to make use of the `void:` macro. 573 | /// 574 | stock void:Indirect_Callvoid(func, tag, GLOBAL_TAG_TYPES:...) 575 | { 576 | const cells0 = 5 * cellbytes; 577 | const cells1 = -2 * cellbytes; 578 | const cells2 = 1 * cellbytes; 579 | const cells3 = 1 * cellbytes; 580 | const cells4 = 2 * cellbytes; 581 | #pragma unused tag 582 | if (!func) 583 | { 584 | return; 585 | } 586 | {} 587 | #emit ADDR.pri cells0 588 | #emit STOR.pri INDR_gsAddr 589 | #emit POP.pri 590 | #emit SCTRL 5 591 | // Get the return address. 592 | #emit POP.alt 593 | // Get the parameter count. 594 | #emit POP.pri 595 | // Reduce the parameter count. 596 | #emit ADD.C cells1 597 | // Store the return address. 598 | #emit SWAP.alt 599 | #emit STOR.alt INDIRECTION_DATA 600 | #emit POP.alt 601 | // Store the new parameter count. 602 | #emit SWAP.pri 603 | #emit STOR.pri INDIRECTION_TAG 604 | #emit PUSH.alt 605 | // Check the pointer type. If it is in the `COD` area, jump straight to it 606 | // with the tag for parameter types (if it isn't 0). Otherwise, use the 607 | // `func` from `DAT` as a pointer to a handler. 608 | if (INDIRECTION_DATA >= INDR_gsCodSize) 609 | { 610 | // Get the data at `func - COD`. 611 | #emit LOAD.pri INDIRECTION_DATA 612 | #emit LOAD.alt INDR_gsCodSize 613 | #emit SUB 614 | #emit MOVE.alt 615 | #emit LOAD.I 616 | #emit STOR.pri INDIRECTION_DATA 617 | if (!INDIRECTION_DATA) 618 | { 619 | // Get the function at `func - COD + 4`. 620 | #emit CONST.pri cells2 621 | #emit ADD 622 | #emit LOAD.I 623 | // Call it, passing `func` as a proper pointer, NOT skipping `PROC`. 624 | #emit STOR.alt INDIRECTION_DATA 625 | #emit SCTRL 6 626 | // NEVER RETURNS PAST HERE. 627 | } 628 | {} 629 | // `INDIRECTION_DATA` is now a pointer to a string of a function name. 630 | // Resolve it via index lookup. 631 | #emit PUSH.alt 632 | #emit PUSH.C cells3 633 | #emit SYSREQ.C funcidx 634 | #emit STACK cells4 635 | #emit STOR.pri INDIRECTION_DATA 636 | if (INDIRECTION_DATA == -1) 637 | { 638 | #emit PROC 639 | #emit RETN 640 | } 641 | {} 642 | // Get the address from the index. 643 | #if cellbits == 64 644 | const shifter = 3; 645 | #else 646 | const shifter = 2; 647 | #endif 648 | #emit LCTRL __dat 649 | #emit NEG 650 | #emit ADD.C 32 // Always this, regardless of cell size. 651 | #emit STOR.pri INDR_gsTmp 652 | #emit LREF.alt INDR_gsTmp 653 | #emit LCTRL __dat 654 | #emit NEG 655 | #emit ADD 656 | // This replicates `IDXADDR`, but in no more instructions since the data 657 | // are in the wrong registers. 658 | #emit LOAD.alt INDIRECTION_DATA 659 | #emit SHL.C.alt shifter 660 | #emit ADD 661 | // Add more data, to account for `__defsize` offsets. 662 | #emit LOAD.alt INDIRECTION_DATA 663 | #emit SHL.C.alt 2 664 | #emit ADD 665 | #emit STOR.pri INDR_gsTmp 666 | #emit LREF.pri INDR_gsTmp 667 | #emit STOR.pri INDIRECTION_DATA 668 | } 669 | if (INDIRECTION_TAG) 670 | { 671 | // Skip the `F@_@` prefix. 672 | GetTagNameFromID(INDIRECTION_TAG, INDR_gsTag); 673 | if (INDR_gsTag[0]) for (INDR_gsTmp = 4; ; ) 674 | { 675 | switch (INDR_gsTag[INDR_gsTmp++]) 676 | { 677 | case 'i', 'd', 't', 'f', 'c': 678 | { 679 | // Resolve non-reference parameters. 680 | #emit LREF.pri INDR_gsAddr 681 | #emit LOAD.I 682 | #emit SREF.pri INDR_gsAddr 683 | } 684 | case '\0', 'x': 685 | break; 686 | } 687 | INDR_gsAddr += cellbytes; 688 | } 689 | } 690 | {} 691 | // No handler, and no tag data. Just jump to it and hope. 692 | #emit LOAD.pri INDIRECTION_DATA 693 | #emit SCTRL 6 694 | // Don't return anything. 695 | } 696 | 697 | /// indirection 698 | stock Indirect_Array(func, tag, const params[], size = sizeof (params)) 699 | { 700 | #if cellbits == 32 701 | const cells0 = 2; 702 | #elseif cellbits == 64 703 | const cells0 = 3; 704 | #else 705 | #error Unsupported `cellbits`. 706 | #endif 707 | const cells1 = 5 * cellbytes; 708 | const cells2 = 6 * cellbytes; 709 | const cells4 = 1 * cellbytes; 710 | const cells5 = 2 * cellbytes; 711 | static 712 | ret = 0, 713 | src = 0; 714 | #pragma unused func, tag, params, size 715 | // Get the previous frame. This undoes the effects of our `PROC` so the 716 | // called function must have a proper `PROC`. 717 | #emit POP.pri 718 | #emit SCTRL 5 719 | // Get the return address. 720 | #emit POP.pri 721 | #emit STOR.pri ret 722 | // Remove the parameter count. 723 | #emit POP.pri 724 | // Get the target tag. 725 | #emit POP.pri 726 | #emit STOR.pri INDIRECTION_DATA 727 | // Get the target function. 728 | #emit POP.pri 729 | #emit STOR.pri INDIRECTION_TAG 730 | // Get the source data. 731 | #emit POP.alt 732 | #emit STOR.alt src 733 | // Get the size. 734 | #emit POP.alt 735 | #emit SHL.C.alt cells0 736 | // #emit STOR.alt cnt 737 | // We cannot just adjust the stack size while the other parameters are still 738 | // on it, since the new stack might be smaller than the old one, and dealing 739 | // with that in a simple way is not possible. Well, it is possible - it's 740 | // what we are doing! Copy the parameters. 741 | #emit LCTRL 4 742 | #emit SUB 743 | #emit SCTRL 4 744 | #emit PUSH.alt 745 | #emit PUSH.alt 746 | #emit PUSH.C 0 747 | #emit PUSH src 748 | #emit STOR.pri src 749 | #emit PUSH.pri 750 | #emit PUSH.C cells1 751 | #emit SYSREQ.C memcpy 752 | #emit MOVE.pri 753 | #emit STACK cells2 754 | #emit PUSH.pri 755 | #emit PUSH ret 756 | // 757 | // Check the pointer type. If it is in the `COD` area, jump straight to it 758 | // with the tag for parameter types (if it isn't 0). Otherwise, use the 759 | // `func` from `DAT` as a pointer to a handler. 760 | if (INDIRECTION_DATA >= INDR_gsCodSize) 761 | { 762 | // Get the data at `func - COD`. 763 | #emit LOAD.pri INDIRECTION_DATA 764 | #emit LOAD.alt INDR_gsCodSize 765 | #emit SUB 766 | #emit MOVE.alt 767 | #emit LOAD.I 768 | #emit STOR.pri INDIRECTION_DATA 769 | if (!INDIRECTION_DATA) 770 | { 771 | #emit STOR.alt INDIRECTION_DATA 772 | // Get the function at `func - COD + 4`. 773 | #emit LOAD.pri INDIRECTION_DATA 774 | #emit ADD.C E_INDIRECTION_HANDER__ 775 | #emit LOAD.I 776 | // Call it, passing `func` as a proper pointer, NOT skipping `PROC`. 777 | #emit SCTRL 6 778 | // NEVER RETURNS PAST HERE. 779 | } 780 | {} 781 | // `INDIRECTION_DATA` is now a pointer to a string of a function name. 782 | // Resolve it via index lookup. 783 | #emit PUSH.alt 784 | #emit PUSH.C cells4 785 | #emit SYSREQ.C funcidx 786 | #emit STACK cells5 787 | #emit STOR.pri INDIRECTION_DATA 788 | if (INDIRECTION_DATA == -1) 789 | { 790 | // Failure. 791 | #emit PROC 792 | #emit RETN 793 | } 794 | {} 795 | // Get the address from the index. 796 | #if cellbits == 64 797 | const shifter = 3; 798 | #else 799 | const shifter = 2; 800 | #endif 801 | #emit LCTRL __dat 802 | #emit NEG 803 | #emit ADD.C 32 // Always this, regardless of cell size. 804 | #emit STOR.pri INDR_gsTmp 805 | #emit LREF.alt INDR_gsTmp 806 | #emit LCTRL __dat 807 | #emit NEG 808 | #emit ADD 809 | // This replicates `IDXADDR`, but in no more instructions since the data 810 | // are in the wrong registers. 811 | #emit LOAD.alt INDIRECTION_DATA 812 | #emit SHL.C.alt shifter 813 | #emit ADD 814 | // Add more data, to account for `__defsize` offsets. 815 | #emit LOAD.alt INDIRECTION_DATA 816 | #emit SHL.C.alt 2 817 | #emit ADD 818 | #emit STOR.pri INDR_gsTmp 819 | #emit LREF.pri INDR_gsTmp 820 | #emit STOR.pri INDIRECTION_DATA 821 | } 822 | if (INDIRECTION_TAG) 823 | { 824 | // Skip the `F@_@` prefix. 825 | GetTagNameFromID(INDIRECTION_TAG, INDR_gsTag); 826 | if (INDR_gsTag[0]) for (INDR_gsTmp = 4; ; ) 827 | { 828 | switch (INDR_gsTag[INDR_gsTmp++]) 829 | { 830 | case 'i', 'd', 't', 'f', 'c': 831 | { 832 | // Resolve non-reference parameters. 833 | #emit LREF.pri src 834 | #emit LOAD.I 835 | #emit SREF.pri src 836 | } 837 | case '\0', 'x': 838 | break; 839 | } 840 | src += cellbytes; 841 | } 842 | } 843 | {} 844 | // No handler, and no tag data. Just jump to it and hope (hope it has a `PROC` too). 845 | #emit LOAD.pri INDIRECTION_DATA 846 | #emit SCTRL 6 847 | return 0; 848 | } 849 | 850 | /*-------------------------------------------------------------------------*//** 851 | * indirection 852 | * The function pointer with attached Nextdata. 853 | * 854 | * Gets extra data from the pointer. 855 | * 856 | *//*------------------------------------------------------------------------**/ 857 | 858 | stock Indirect_GetNext_(func) 859 | { 860 | if (func >= INDR_gsCodSize) 861 | { 862 | // Get the data at `func - COD`. 863 | #emit LOAD.S.pri func 864 | #emit LOAD.alt INDR_gsCodSize 865 | #emit SUB 866 | #emit MOVE.alt 867 | #emit LOAD.I 868 | #emit STOR.S.pri func 869 | if (func) 870 | { 871 | // Probably a string. 872 | return 0; 873 | } 874 | {} 875 | // I'm relying on `alt` not changing here... 876 | // Get the function at `func - COD + 16`. 877 | #emit CONST.pri E_INDIRECTION_NEXT__ 878 | #emit ADD 879 | #emit LOAD.I 880 | #emit RETN 881 | } 882 | return 0; 883 | } 884 | #define Indirect_GetNext(%0) Indirect_GetNext_(_:%0) 885 | 886 | /*-------------------------------------------------------------------------*//** 887 | * indirection 888 | * The function pointer to attach Nextdata to. 889 | * The Nextdata. 890 | *//*------------------------------------------------------------------------**/ 891 | 892 | stock Indirect_SetNext_(func, data) 893 | { 894 | if (func >= INDR_gsCodSize) 895 | { 896 | // Get the data at `func - COD`. 897 | #emit LOAD.S.pri func 898 | #emit LOAD.alt INDR_gsCodSize 899 | #emit SUB 900 | #emit MOVE.alt 901 | #emit LOAD.I 902 | #emit STOR.S.pri func 903 | if (func) 904 | { 905 | // Probably a string. 906 | return false; 907 | } 908 | {} 909 | // I'm relying on `alt` not changing here... 910 | // Get the function at `func - COD + 16`. 911 | #emit CONST.pri E_INDIRECTION_NEXT__ 912 | #emit ADD 913 | #emit LOAD.S.alt data 914 | #emit XCHG 915 | #emit STOR.I 916 | return true; 917 | } 918 | return false; 919 | } 920 | #define Indirect_SetNext(%0,%1) Indirect_SetNext_(_:(%0),%1) 921 | 922 | /*-------------------------------------------------------------------------*//** 923 | * indirection 924 | * The function pointer to attach Nextdata to. 925 | * 1 - an indirection function. 2 - a string name. 926 | * 3 - a code pointer. 927 | *//*------------------------------------------------------------------------**/ 928 | 929 | stock Indirect_GetType_(func) 930 | { 931 | if (func >= INDR_gsCodSize) 932 | { 933 | // Get the data at `func - COD`. 934 | #emit LOAD.S.pri func 935 | #emit LOAD.alt INDR_gsCodSize 936 | #emit SUB 937 | #emit MOVE.alt 938 | #emit LOAD.I 939 | #emit STOR.S.pri func 940 | if (func) 941 | { 942 | // String. 943 | return 2; 944 | } 945 | // Indirect. 946 | return 1; 947 | } 948 | // Pointer. 949 | return 3; 950 | } 951 | #define Indirect_GetType(%0) Indirect_GetType_(_:(%0)) 952 | 953 | /*-------------------------------------------------------------------------*//** 954 | * indirection 955 | * The function pointer with a tag. 956 | * 957 | * Gets the tag of the pointer. 958 | * 959 | *//*------------------------------------------------------------------------**/ 960 | 961 | stock Indirect_GetTag_(func) 962 | { 963 | if (func >= INDR_gsCodSize) 964 | { 965 | // Get the data at `func - COD`. 966 | #emit LOAD.S.pri func 967 | #emit LOAD.alt INDR_gsCodSize 968 | #emit SUB 969 | #emit MOVE.alt 970 | #emit LOAD.I 971 | #emit STOR.S.pri func 972 | if (func) 973 | { 974 | // Probably a string. 975 | return 0; 976 | } 977 | {} 978 | // I'm relying on `alt` not changing here... 979 | // Get the function at `func - COD + 16`. 980 | #emit CONST.pri E_INDIRECTION_TAG__ 981 | #emit ADD 982 | #emit LOAD.I 983 | #emit RETN 984 | } 985 | return 0; 986 | } 987 | #define Indirect_GetTag(%0) Indirect_GetTag_(_:%0) 988 | 989 | /*-------------------------------------------------------------------------*//** 990 | * indirection 991 | * The function pointer to attach a tag to. 992 | * The tag. 993 | *//*------------------------------------------------------------------------**/ 994 | 995 | stock bool:Indirect_SetTag_(func, tag) 996 | { 997 | if (func >= INDR_gsCodSize) 998 | { 999 | // Get the data at `func - COD`. 1000 | #emit LOAD.S.pri func 1001 | #emit LOAD.alt INDR_gsCodSize 1002 | #emit SUB 1003 | #emit MOVE.alt 1004 | #emit LOAD.I 1005 | #emit STOR.S.pri func 1006 | if (func) 1007 | { 1008 | // Probably a string. 1009 | return false; 1010 | } 1011 | {} 1012 | // I'm relying on `alt` not changing here... 1013 | // Get the function at `func - COD + 16`. 1014 | #emit CONST.pri E_INDIRECTION_TAG__ 1015 | #emit ADD 1016 | #emit LOAD.S.alt tag 1017 | #emit XCHG 1018 | #emit STOR.I 1019 | return true; 1020 | } 1021 | return false; 1022 | } 1023 | #define Indirect_SetTag(%0,%1) Indirect_SetTag_(_:(%0),%1) 1024 | 1025 | /*-------------------------------------------------------------------------*//** 1026 | * indirection 1027 | * The function pointer with an owner. 1028 | * 1029 | * Gets the owner of the pointer. 1030 | * 1031 | *//*------------------------------------------------------------------------**/ 1032 | 1033 | stock Indirect_GetOwner_(func) 1034 | { 1035 | if (func >= INDR_gsCodSize) 1036 | { 1037 | // Get the data at `func - COD`. 1038 | #emit LOAD.S.pri func 1039 | #emit LOAD.alt INDR_gsCodSize 1040 | #emit SUB 1041 | #emit MOVE.alt 1042 | #emit LOAD.I 1043 | #emit STOR.S.pri func 1044 | if (func) 1045 | { 1046 | // Probably a string. 1047 | return 0; 1048 | } 1049 | {} 1050 | // I'm relying on `alt` not changing here... 1051 | // Get the function at `func - COD + 16`. 1052 | #emit CONST.pri E_INDIRECTION_OWNER__ 1053 | #emit ADD 1054 | #emit LOAD.I 1055 | #emit CONST.alt cellmax 1056 | #emit AND 1057 | #emit RETN 1058 | } 1059 | return 0; 1060 | } 1061 | #define Indirect_GetOwner(%0) Indirect_GetOwner_(_:%0) 1062 | 1063 | /*-------------------------------------------------------------------------*//** 1064 | * indirection 1065 | * The function pointer to attach an owner to. 1066 | * The owner. 1067 | *//*------------------------------------------------------------------------**/ 1068 | 1069 | stock Indirect_SetOwner_(func, owner) 1070 | { 1071 | if (func >= INDR_gsCodSize) 1072 | { 1073 | // Get the data at `func - COD`. 1074 | #emit LOAD.S.pri func 1075 | #emit LOAD.alt INDR_gsCodSize 1076 | #emit SUB 1077 | #emit MOVE.alt 1078 | #emit LOAD.I 1079 | #emit STOR.S.pri func 1080 | if (func) 1081 | { 1082 | // Probably a string. 1083 | return false; 1084 | } 1085 | {} 1086 | #emit STOR.S.alt func 1087 | // Store the owner. 1088 | WriteAmxMemory(func + E_INDIRECTION_OWNER__, owner | cellmin); 1089 | return true; 1090 | } 1091 | return false; 1092 | } 1093 | #define Indirect_SetOwner(%0,%1) Indirect_SetOwner_(_:(%0),%1) 1094 | 1095 | /*-------------------------------------------------------------------------*//** 1096 | * indirection 1097 | * The function pointer with attached metadata. 1098 | * 1099 | * Gets extra data from the pointer. 1100 | * 1101 | *//*------------------------------------------------------------------------**/ 1102 | 1103 | stock Indirect_GetMeta_(func) 1104 | { 1105 | if (func >= INDR_gsCodSize) 1106 | { 1107 | // Get the data at `func - COD`. 1108 | #emit LOAD.S.pri func 1109 | #emit LOAD.alt INDR_gsCodSize 1110 | #emit SUB 1111 | #emit MOVE.alt 1112 | #emit LOAD.I 1113 | #emit STOR.S.pri func 1114 | if (func) 1115 | { 1116 | // Probably a string. 1117 | return 0; 1118 | } 1119 | {} 1120 | // I'm relying on `alt` not changing here... 1121 | // Get the function at `func - COD + 16`. 1122 | #emit CONST.pri E_INDIRECTION_METADATA__ 1123 | #emit ADD 1124 | #emit LOAD.I 1125 | #emit RETN 1126 | } 1127 | return 0; 1128 | } 1129 | #define Indirect_GetMeta(%0) Indirect_GetMeta_(_:%0) 1130 | 1131 | /*-------------------------------------------------------------------------*//** 1132 | * indirection 1133 | * The function pointer to attach metadata to. 1134 | * The metadata. 1135 | *//*------------------------------------------------------------------------**/ 1136 | 1137 | stock Indirect_SetMeta_(func, data) 1138 | { 1139 | if (func >= INDR_gsCodSize) 1140 | { 1141 | // Get the data at `func - COD`. 1142 | #emit LOAD.S.pri func 1143 | #emit LOAD.alt INDR_gsCodSize 1144 | #emit SUB 1145 | #emit MOVE.alt 1146 | #emit LOAD.I 1147 | #emit STOR.S.pri func 1148 | if (func) 1149 | { 1150 | // Probably a string. 1151 | return false; 1152 | } 1153 | {} 1154 | // I'm relying on `alt` not changing here... 1155 | // Get the function at `func - COD + 16`. 1156 | #emit CONST.pri E_INDIRECTION_METADATA__ 1157 | #emit ADD 1158 | #emit LOAD.S.alt data 1159 | #emit XCHG 1160 | #emit STOR.I 1161 | return true; 1162 | } 1163 | return false; 1164 | } 1165 | #define Indirect_SetMeta(%0,%1) Indirect_SetMeta_(_:(%0),%1) 1166 | 1167 | /*-------------------------------------------------------------------------*//** 1168 | * indirection 1169 | * The function pointer you want to use later. 1170 | * 1171 | * If a function pointer is used within one function, that is not a problem. 1172 | * However, if you want to store the function pointer for use later, you must 1173 | * first "claim" it, so that any associated data is not cleared when the 1174 | * parent function ends (i.e. the function that called your function). After 1175 | * use it must be released, and the number of claims must match the number of 1176 | * releases. 1177 | * 1178 | *//*------------------------------------------------------------------------**/ 1179 | 1180 | stock Indirect_Claim_(func) 1181 | { 1182 | if (func >= INDR_gsCodSize) 1183 | { 1184 | // Get the data at `func - COD`. 1185 | #emit LOAD.S.pri func 1186 | #emit LOAD.alt INDR_gsCodSize 1187 | #emit SUB 1188 | #emit MOVE.alt 1189 | #emit LOAD.I 1190 | #emit STOR.S.pri func 1191 | if (func) 1192 | { 1193 | // Probably a string. 1194 | return; 1195 | } 1196 | {} 1197 | // I'm relying on `alt` not changing here... A LOT! 1198 | #emit CONST.pri E_INDIRECTION_CLAIM__ 1199 | #emit ADD 1200 | #emit LOAD.I 1201 | #emit STOR.S.pri func 1202 | if (!func) 1203 | { 1204 | // Already claimed. 1205 | return; 1206 | } 1207 | {} 1208 | // Save the pointer to pass on. 1209 | #emit STOR.S.alt func 1210 | // Clean up owner lists. 1211 | new next = ReadAmxMemory(func + E_INDIRECTION_OWNER__); 1212 | if (next) 1213 | { 1214 | WriteAmxMemory(func + E_INDIRECTION_NEXT__, INDR_gsKnownOwnedCallbacks); 1215 | INDR_gsKnownOwnedCallbacks = func + E_INDIRECTION_NEXT__; 1216 | } 1217 | next = ReadAmxMemory(func + E_INDIRECTION_CLAIM__); 1218 | #pragma unused next // It is used, secretly in assembly... 1219 | WriteAmxMemory(func + E_INDIRECTION_CLAIM__, 0); 1220 | // `next` is at the top of the stack with the code pointer. 1221 | #emit POP.pri 1222 | #emit SWAP.pri 1223 | #emit SCTRL 5 1224 | #emit POP.pri 1225 | #emit SCTRL 6 1226 | } 1227 | } 1228 | #define Indirect_Claim(%0) Indirect_Claim_(_:%0) 1229 | 1230 | /*-------------------------------------------------------------------------*//** 1231 | * indirection 1232 | * 1233 | * Null function that does nothing for when an owner is gone. 1234 | * 1235 | *//*------------------------------------------------------------------------**/ 1236 | 1237 | static stock Indirect_Stub(...) 1238 | { 1239 | return 0; 1240 | } 1241 | 1242 | /*-------------------------------------------------------------------------*//** 1243 | * indirection 1244 | * 1245 | * Hack to allow external access to this address, so that we can do some tests 1246 | * more quickly in places. 1247 | * 1248 | *//*------------------------------------------------------------------------**/ 1249 | 1250 | stock Indirect_GetStubPointer() 1251 | { 1252 | static stub = 0; 1253 | if (!stub) 1254 | { 1255 | if (stub) 1256 | { 1257 | // Include in the output. Can't be called. 1258 | Indirect_Stub(); 1259 | } 1260 | {} 1261 | #emit CONST.pri Indirect_Stub 1262 | #emit STOR.pri stub 1263 | } 1264 | return stub; 1265 | } 1266 | 1267 | /*-------------------------------------------------------------------------*//** 1268 | * indirection 1269 | * A person or other entity that no longer exists. 1270 | * 1271 | * Disables all callbacks belonging to this entity. 1272 | * 1273 | *//*------------------------------------------------------------------------**/ 1274 | 1275 | stock Indirect_Disconnect(owner) 1276 | { 1277 | // Special "owned" flag. 1278 | owner |= cellmin; 1279 | new 1280 | prev = ref(INDR_gsKnownOwnedCallbacks), 1281 | cur = ReadAmxMemory(prev), 1282 | stub = Indirect_GetStubPointer(); 1283 | while (cur) 1284 | { 1285 | if (ReadAmxMemory(cur - (E_INDIRECTION_NEXT__ - E_INDIRECTION_OWNER__)) == owner) 1286 | { 1287 | // Disable the handler. 1288 | WriteAmxMemory(cur - (E_INDIRECTION_NEXT__ - E_INDIRECTION_HANDER__), stub), 1289 | WriteAmxMemory(cur - (E_INDIRECTION_NEXT__ - E_INDIRECTION_OWNER__), 0), 1290 | // Remove from the list. 1291 | INDR_gsTmp = ReadAmxMemory(cur), 1292 | WriteAmxMemory(prev, INDR_gsTmp), 1293 | WriteAmxMemory(cur, 0), 1294 | cur = INDR_gsTmp; 1295 | } 1296 | else 1297 | { 1298 | prev = cur; 1299 | cur = ReadAmxMemory(prev); 1300 | } 1301 | } 1302 | } 1303 | 1304 | ///*-------------------------------------------------------------------------*//** 1305 | // * indirection 1306 | // * A person or other entity that no longer exists. 1307 | // * Meta-data that needs matching. 1308 | // * 1309 | // * This is a cheap way of adding two owners, To only disable all callbacks 1310 | // * belonging to this entity with matching meta-data. 1311 | // * 1312 | // *//*------------------------------------------------------------------------**/ 1313 | // 1314 | //stock Indirect_Disconnect2(owner, meta) 1315 | //{ 1316 | // // Special "owned" flag. 1317 | // owner |= cellmin; 1318 | // new 1319 | // prev = ref(INDR_gsKnownOwnedCallbacks), 1320 | // cur = ReadAmxMemory(prev), 1321 | // stub = Indirect_GetStubPointer(); 1322 | // while (cur) 1323 | // { 1324 | // if ( 1325 | // ReadAmxMemory(cur - (E_INDIRECTION_NEXT__ - E_INDIRECTION_METADATA__)) == meta && 1326 | // ReadAmxMemory(cur - (E_INDIRECTION_NEXT__ - E_INDIRECTION_OWNER__)) == owner 1327 | // ) 1328 | // { 1329 | // // Disable the handler. 1330 | // WriteAmxMemory(cur - (E_INDIRECTION_NEXT__ - E_INDIRECTION_HANDER__), stub), 1331 | // WriteAmxMemory(cur - (E_INDIRECTION_NEXT__ - E_INDIRECTION_OWNER__), 0), 1332 | // // Remove from the list. 1333 | // INDR_gsTmp = ReadAmxMemory(cur), 1334 | // WriteAmxMemory(prev, INDR_gsTmp), 1335 | // WriteAmxMemory(cur, 0), 1336 | // cur = INDR_gsTmp; 1337 | // } 1338 | // else 1339 | // { 1340 | // prev = cur; 1341 | // cur = ReadAmxMemory(prev); 1342 | // } 1343 | // } 1344 | //} 1345 | // 1346 | //#define Indirect_Disconnect(%0,%1) Indirect_Disconnect2(%0,%1) 1347 | 1348 | /*-------------------------------------------------------------------------*//** 1349 | * indirection 1350 | *//*------------------------------------------------------------------------**/ 1351 | 1352 | native Indirect_FuncIDX_(ptr) = funcidx; 1353 | 1354 | /*-------------------------------------------------------------------------*//** 1355 | * indirection 1356 | * The function pointer you had previously stored. 1357 | * 1358 | * Is it still valid (exists and not disabled). 1359 | * 1360 | *//*------------------------------------------------------------------------**/ 1361 | 1362 | stock bool:Indirect_IsValid_(func) 1363 | { 1364 | if (func >= INDR_gsCodSize) 1365 | { 1366 | // Get the data at `func - COD`. 1367 | #emit LOAD.S.pri func 1368 | #emit LOAD.alt INDR_gsCodSize 1369 | #emit SUB 1370 | #emit MOVE.alt 1371 | #emit LOAD.I 1372 | #emit STOR.S.pri func 1373 | if (func) 1374 | { 1375 | // Strings must be valid. I guess we could use `funcidx`... 1376 | // Extract the pointer back out of `alt`. 1377 | #emit STOR.S.alt func 1378 | return Indirect_FuncIDX_(func) != -1; 1379 | } 1380 | {} 1381 | // I'm relying on `alt` not changing here... 1382 | #emit CONST.pri E_INDIRECTION_HANDER__ 1383 | #emit ADD 1384 | #emit LOAD.I 1385 | #emit STOR.S.pri func 1386 | return func != Indirect_GetStubPointer(); 1387 | } 1388 | // Direct pointers are always valid (because we can't tell otherwise). 1389 | return func != 0; 1390 | } 1391 | #define Indirect_IsValid(%0) Indirect_IsValid_(_:%0) 1392 | 1393 | /*-------------------------------------------------------------------------*//** 1394 | * indirection 1395 | * The function pointer you had previously stored. 1396 | * 1397 | * If a function pointer is used within one function, that is not a problem. 1398 | * However, if you want to store the function pointer for use later, you must 1399 | * first "claim" it, so that any associated data is not cleared when the 1400 | * parent function ends (i.e. the function that called your function). After 1401 | * use it must be released, and the number of claims must match the number of 1402 | * releases. 1403 | * 1404 | *//*------------------------------------------------------------------------**/ 1405 | 1406 | stock Indirect_Release_(func) 1407 | { 1408 | if (func >= INDR_gsCodSize) 1409 | { 1410 | // Get the data at `func - COD`. 1411 | #emit LOAD.S.pri func 1412 | #emit LOAD.alt INDR_gsCodSize 1413 | #emit SUB 1414 | #emit MOVE.alt 1415 | #emit LOAD.I 1416 | #emit STOR.S.pri func 1417 | if (func) 1418 | { 1419 | // Probably a string. 1420 | return; 1421 | } 1422 | {} 1423 | // I'm relying on `alt` not changing here... A LOT! 1424 | #emit CONST.pri E_INDIRECTION_RELEASE__ 1425 | #emit ADD 1426 | #emit LOAD.I 1427 | #emit STOR.S.pri func 1428 | if (!func) 1429 | { 1430 | // Already claimed. 1431 | return; 1432 | } 1433 | {} 1434 | // Save the pointer to pass on. 1435 | #emit STOR.S.alt func 1436 | // Clean up owner lists. 1437 | new next = ReadAmxMemory(func + E_INDIRECTION_OWNER__); 1438 | if (next) 1439 | { 1440 | next = ref(INDR_gsKnownOwnedCallbacks); 1441 | new cur = ReadAmxMemory(next); 1442 | while (cur) 1443 | { 1444 | if (cur == func + E_INDIRECTION_NEXT__) 1445 | { 1446 | cur = ReadAmxMemory(cur); 1447 | WriteAmxMemory(next, cur); 1448 | WriteAmxMemory(func + E_INDIRECTION_NEXT__, 0); 1449 | WriteAmxMemory(func + E_INDIRECTION_OWNER__, 0); 1450 | break; 1451 | } 1452 | next = cur; 1453 | cur = ReadAmxMemory(next); 1454 | } 1455 | } 1456 | next = ReadAmxMemory(func + E_INDIRECTION_RELEASE__); 1457 | #pragma unused next // It is used, secretly in assembly... 1458 | WriteAmxMemory(func + E_INDIRECTION_RELEASE__, 0); 1459 | // `next` is at the top of the stack with the code pointer. 1460 | #emit POP.pri 1461 | #emit SWAP.pri 1462 | #emit SCTRL 5 1463 | #emit POP.pri 1464 | #emit SCTRL 6 1465 | } 1466 | } 1467 | #define Indirect_Release(%0) Indirect_Release_(_:%0) 1468 | 1469 | /*-------------------------------------------------------------------------*//** 1470 | * indirection 1471 | * The ID of the tag to get the specifiers from the name of. 1472 | * 1473 | * Where to store the name. 1474 | * 1475 | * Functions are tagged with a special tag containing their specifiers. Get 1476 | * the string value of that tag from the AMX header. 1477 | * 1478 | *//*------------------------------------------------------------------------**/ 1479 | 1480 | stock Indirect_Tag(id, dest[32]) 1481 | { 1482 | GetTagNameFromID(id, INDR_gsTag), 1483 | dest[0] = '\0'; 1484 | if (INDR_gsTag[0]) 1485 | strcat(dest, INDR_gsTag[4]); 1486 | } 1487 | 1488 | /*-------------------------------------------------------------------------*//** 1489 | * indirection 1490 | * The array to convert to an offset pointer. 1491 | * 1492 | * Strings and arrays are passed relative to `COD` not `DAT` so they can be 1493 | * distinguished from normal function pointers. This function does the 1494 | * offset. 1495 | * 1496 | *//*------------------------------------------------------------------------**/ 1497 | 1498 | stock Indirect_Ref_(...) 1499 | { 1500 | const cells0 = 3 * cellbytes; 1501 | assert(numargs() == 1); 1502 | #emit LOAD.S.pri cells0 1503 | #emit LOAD.alt INDR_gsCodSize 1504 | #emit ADD 1505 | #emit RETN 1506 | return 0; 1507 | } 1508 | #define Indirect_Ref(%0) Indirect_Ref_(_:(%0)) 1509 | 1510 | /*-------------------------------------------------------------------------*//** 1511 | * indirection 1512 | * The array to convert to an offset pointer. 1513 | * 1514 | * Strings and arrays are passed relative to `COD` not `DAT` so they can be 1515 | * distinguished from normal function pointers. This function does the 1516 | * offset. 1517 | * 1518 | *//*------------------------------------------------------------------------**/ 1519 | 1520 | stock Indirect_Ptr_(ptr) 1521 | { 1522 | const cells0 = 3 * cellbytes; 1523 | #pragma unused ptr 1524 | assert(numargs() == 1); 1525 | #emit LOAD.S.pri cells0 1526 | #emit LOAD.alt INDR_gsCodSize 1527 | #emit ADD 1528 | #emit RETN 1529 | return 0; 1530 | } 1531 | #define Indirect_Ptr(%0) Indirect_Ptr_(_:(%0)) 1532 | 1533 | /*-------------------------------------------------------------------------*//** 1534 | * indirection 1535 | * The array to convert to an offset pointer. 1536 | * 1537 | * Strings and arrays are passed relative to `COD` not `DAT` so they can be 1538 | * distinguished from normal function pointers. This function does the 1539 | * offset. 1540 | * 1541 | *//*------------------------------------------------------------------------**/ 1542 | 1543 | stock Indirect_DeRef_(...) 1544 | { 1545 | const cells0 = 3 * cellbytes; 1546 | assert(numargs() == 1); 1547 | #emit LOAD.S.pri cells0 1548 | #emit LOAD.alt INDR_gsCodSize 1549 | #emit SUB 1550 | #emit RETN 1551 | return 0; 1552 | } 1553 | #define Indirect_DeRef(%0) Indirect_DeRef_(_:(%0), sizeof (%0[])) 1554 | 1555 | /*-------------------------------------------------------------------------*//** 1556 | * indirection 1557 | * The array to convert to an offset pointer. 1558 | * 1559 | * Strings and arrays are passed relative to `COD` not `DAT` so they can be 1560 | * distinguished from normal function pointers. This function does the 1561 | * offset. 1562 | * 1563 | *//*------------------------------------------------------------------------**/ 1564 | 1565 | stock Indirect_DePtr_(ptr) 1566 | { 1567 | #emit LOAD.S.pri ptr 1568 | #emit LOAD.alt INDR_gsCodSize 1569 | #emit SUB 1570 | #emit RETN 1571 | return 0; 1572 | } 1573 | #define Indirect_DePtr(%0) Indirect_DePtr_(_:(%0)) 1574 | 1575 | #if !defined _ALS_OnJITCompile 1576 | forward OnJITCompile(); 1577 | #endif 1578 | 1579 | /*-------------------------------------------------------------------------*//** 1580 | * indirection 1581 | * 1582 | * A generic public wrapper for calling inline functions. 1583 | * 1584 | *//*------------------------------------------------------------------------**/ 1585 | 1586 | forward Indirect_FromCallback(Func:cb<>, bool:release); 1587 | 1588 | public Indirect_FromCallback(Func:cb<>, bool:release) 1589 | { 1590 | new ret = @.cb(); 1591 | if (release) 1592 | Indirect_Release(cb); 1593 | return ret; 1594 | } 1595 | 1596 | /*-------------------------------------------------------------------------*//** 1597 | * indirection 1598 | * 1599 | * Get integer metadata. 1600 | * 1601 | *//*------------------------------------------------------------------------**/ 1602 | 1603 | stock bool:Indirect_GetMetaInt(index, &ret) 1604 | { 1605 | if (INDIRECTION_COUNT > index) 1606 | { 1607 | index = INDIRECTION_META[index]; 1608 | #emit LREF.S.pri index 1609 | #emit SREF.S.pri ret 1610 | return true; 1611 | } 1612 | return false; 1613 | } 1614 | 1615 | /*-------------------------------------------------------------------------*//** 1616 | * indirection 1617 | * 1618 | * Get float metadata. 1619 | * 1620 | *//*------------------------------------------------------------------------**/ 1621 | 1622 | stock bool:Indirect_GetMetaFloat(index, &Float:ret) 1623 | { 1624 | if (INDIRECTION_COUNT > index) 1625 | { 1626 | index = INDIRECTION_META[index]; 1627 | #emit LREF.S.pri index 1628 | #emit SREF.S.pri ret 1629 | return true; 1630 | } 1631 | return false; 1632 | } 1633 | 1634 | /*-------------------------------------------------------------------------*//** 1635 | * indirection 1636 | * 1637 | * Get boolean metadata. 1638 | * 1639 | *//*------------------------------------------------------------------------**/ 1640 | 1641 | stock bool:Indirect_GetMetaBool(index, &bool:ret) 1642 | { 1643 | if (INDIRECTION_COUNT > index) 1644 | { 1645 | index = INDIRECTION_META[index]; 1646 | #emit LREF.S.pri index 1647 | #emit SREF.S.pri ret 1648 | return true; 1649 | } 1650 | return false; 1651 | } 1652 | 1653 | /*-------------------------------------------------------------------------*//** 1654 | * indirection 1655 | * 1656 | * Get ref metadata. 1657 | * 1658 | *//*------------------------------------------------------------------------**/ 1659 | 1660 | stock bool:Indirect_GetMetaRef(index, &ret) 1661 | { 1662 | if (INDIRECTION_COUNT > index) 1663 | { 1664 | index = INDIRECTION_META[index]; 1665 | #emit LOAD.S.pri index 1666 | #emit SREF.S.pri ret 1667 | return true; 1668 | } 1669 | return false; 1670 | } 1671 | 1672 | /*-------------------------------------------------------------------------*//** 1673 | * indirection 1674 | * 1675 | * Set ref metadata. 1676 | * 1677 | *//*------------------------------------------------------------------------**/ 1678 | 1679 | stock bool:Indirect_SetMetaRef(index, val) 1680 | { 1681 | if (INDIRECTION_COUNT > index) 1682 | { 1683 | index = INDIRECTION_META[index]; 1684 | #emit LOAD.S.pri val 1685 | #emit SREF.S.pri index 1686 | return true; 1687 | } 1688 | return false; 1689 | } 1690 | 1691 | /// indirection 1692 | native Indirect_Strcat(dest[], const source, maxlength = sizeof (dest)) = dest; 1693 | 1694 | /*-------------------------------------------------------------------------*//** 1695 | * indirection 1696 | * 1697 | * Get ref metadata. 1698 | * 1699 | *//*------------------------------------------------------------------------**/ 1700 | 1701 | stock bool:Indirect_GetMetaString(index, dest[], size = sizeof (dest)) 1702 | { 1703 | if (INDIRECTION_COUNT > index) 1704 | { 1705 | dest[0] = '\0', 1706 | Indirect_Strcat(dest, INDIRECTION_META[index], size); 1707 | return true; 1708 | } 1709 | return false; 1710 | } 1711 | 1712 | /*-------------------------------------------------------------------------*//** 1713 | * indirection 1714 | * 1715 | * Get the size of the COD AMX segment. 1716 | * 1717 | *//*------------------------------------------------------------------------**/ 1718 | 1719 | Indirect_Init() 1720 | { 1721 | if (!INDR_gsCodSize) 1722 | { 1723 | #emit LCTRL __cod 1724 | #emit MOVE.alt 1725 | #emit LCTRL __dat 1726 | #emit SUB 1727 | #emit STOR.pri INDR_gsCodSize 1728 | } 1729 | } 1730 | 1731 | /// indirection 1732 | public OnJITCompile() 1733 | { 1734 | Indirect_Init(); 1735 | #if defined Indirection_OnJITCompile 1736 | return Indirection_OnJITCompile(); 1737 | #else 1738 | return 1; 1739 | #endif 1740 | } 1741 | 1742 | /// indirection 1743 | public OnFilterScriptInit() 1744 | { 1745 | Indirect_Init(); 1746 | #if defined Indirection_OnFilterScriptInit 1747 | Indirection_OnFilterScriptInit(); 1748 | #endif 1749 | return 1; 1750 | } 1751 | 1752 | /// indirection 1753 | public OnGameModeInit() 1754 | { 1755 | Indirect_Init(); 1756 | #if defined Indirection_OnGameModeInit 1757 | Indirection_OnGameModeInit(); 1758 | #endif 1759 | return 1; 1760 | } 1761 | 1762 | #if defined Indirection_OnJITCompile 1763 | forward Indirection_OnJITCompile(); 1764 | #endif 1765 | #if defined _ALS_OnJITCompile 1766 | #undef OnJITCompile 1767 | #else 1768 | #define _ALS_OnJITCompile 1769 | #endif 1770 | #define OnJITCompile(%0) Indirection_OnJITCompile(%0) 1771 | 1772 | #if defined Indirection_OnFilterScriptInit 1773 | forward Indirection_OnFilterScriptInit(); 1774 | #endif 1775 | #if defined _ALS_OnFilterScriptInit 1776 | #undef OnFilterScriptInit 1777 | #else 1778 | #define _ALS_OnFilterScriptInit 1779 | #endif 1780 | #define OnFilterScriptInit(%0) Indirection_OnFilterScriptInit(%0) 1781 | 1782 | #if defined Indirection_OnGameModeInit 1783 | forward Indirection_OnGameModeInit(); 1784 | #endif 1785 | #if defined _ALS_OnGameModeInit 1786 | #undef OnGameModeInit 1787 | #else 1788 | #define _ALS_OnGameModeInit 1789 | #endif 1790 | #define OnGameModeInit(%0) Indirection_OnGameModeInit(%0) 1791 | 1792 | --------------------------------------------------------------------------------