Staredit Network > Forums > SC1 UMS Theory and Ideas > Topic: [EUD] Self-modifying Trigger
[EUD] Self-modifying Trigger
Jan 22 2014, 2:00 pm
By: trgk  

Jan 22 2014, 2:00 pm trgk Post #1



Uses EUD Actions

I originally tried to overcome 1700 units limit, and came out with some useful ideas. (Eventually failed.)

Trigger self-modifying itself (which I'll call payload) is placed in MRGN section. (You may just initalize them with SetDeaths. I just put them directly in MRGN section.)

Triggers in TRIG (which I'll call vector)
- 1. Fill in offsets for payload. These offsets were needed for payload. (not important)
- (a): Pointer to STR section + real STR section size, rounded up to 86 modulo 336. This is where the newly created CUnit* memory layout will reside in.
- (b): Pointer to the first empty unit node.
- (c): Pointer to the first trigger. (return address)

- 2. Modifies 0051a280 + 12 + 8 (where the first executing trigger for player 2 is) so that trigger in MRGN is executed at next trigger cycle.
- 3. Forces fast recycle with Wait(0). (Other triggers are crafted to not be executed before payload executes)

and the payload is executed at next trigger cycle.

Payload consists of setter, advancer, and finalizer.
- Setter sets up CUnit memory layout
- Advancer ends infinite loop at certain condition, and proceeds to finalizer
- Finalizer cleans up stuff, and return to original trigger list.

- Payload knows it's address. (Fixed at where MRGN is loaded) So they can use SetDeaths to modify their internal contents.
Payload is initally constructed to have infinite loop. (Trigger engine traverses through doubly linked list, so play with it->next)

like this.

p2 trigger start ---> setter <--- loop ---> advancer ; finalizer ---> NULL

- 5. Payload creates CUnit memory layout at (a), (prev, next)
- Increases (a) by 336. (Increasing 84 in player section of SetDeaths action, in fact) See trggen.py for more details.

- 6. After 3300 layouts has been created, advancer changes it's next to finalizer trigger node.
p2 trigger start ---> setter ---> advancer ---> finalizer ---> NULL

- 7. finalizer uses (b) and (c) to link empty unitnode list and player 2 trigger list, and sets it's own next to (c).
setter ---> advancer ---> finalizer ---> (c)
(not referencable)
p2 trigger start -> (c)

Trigger list returns to normal


This example fails because SC uses binary search to locate units in certain rectangle (FindAllUnits in BW, .text:004308A0)
and they uses 1700-sized array in their internal implementation. (.data:006BD3D0)

But payload seems to be working properly as expected.


Some proposal :
1. Vanilla location table + Switches + BW Location table < 7200byte = Size of 3 trigger values, so you actually can't do much with MRGN sections only.
The one and only continuous memory region with adjustable size is STR section.
We can upload our own trigger at STR section and init their needed offsets with trigger at MRGN sections.
(TRIG -> MRGN -> TRIG -> MRGN -> ... -> TRIG -> MRGN -> (after complete load) TRIG -> MRGN (This time to traverse through TRIG and make goto statement)
-> TRIG -> STR )

(or One can upload memcpy(eud version) & custom trigger at STR section and let MRGN use memcpy to copy some needed addresses to STR)


2. Goto is possible, as shown in stage 6, 7. (Modifying ->next) Since trigger in STR should know their offsets after having been initalized by MRGN,
sections in STR should be able to jump around over themselves, leading us to write EUD 'program' at least as powerful as brainfuck. (Maybe much more powerful)

3. These tasks are very tedious to do by hand. Some automated tool (brainfuck->eud compiler?) should be made before this trick would have any
usability.



Check out testmap.scx and two python source files used for trigger generation. (Python 3)

Attachments:
trgformat.py
Hits: 5 Size: 4.63kb
trggen.py
Hits: 5 Size: 10.11kb
testmap.scx
Hits: 7 Size: 115.78kb

Post has been edited 2 time(s), last time on May 18 2014, 10:37 pm by trgk.



EUD

Jan 24 2014, 3:53 pm Sacrieur Post #2

Still Napping

Um pardon, but it's hard to understand what you're saying.



None.

Jan 24 2014, 4:47 pm trgk Post #3



Quote from Sacrieur
Um pardon, but it's hard to understand what you're saying.
Yeah I too thought the article was annoyingly too hard. :(

Idea is simple
Imagine you have an action like this:
SetDeaths(0x1234, SetTo, 0x5678, 0x9ABC)

It's equivilant to hex string

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 34 12 00 00 78 56 00 00 BC 9A 2D 07 14 00 00 00

If you know the address where the trigger is located, you can use simple SetDeaths to modify trigger in real-time.
If I want to modify the player (34 12 00 00) to 0x5678, I'll just have to do
SetMemory( offset to where the player field is , SetTo, 0x5678 );
( SetMemory is an imaginary action which will do what you think it will do. )


We actually don't know where our classic triggers are located. It's located somewhere. It's memory is dynamically allocated;
We need to dispatch our prefabricated trigger to some known location, and make SC recognize them as trigger an execute it.
In example map, I used Location table (5100byte from 0058DC60) to upload triggers.
(Easy to manipluate)


Trigger node is manages as doubly linked list. They have prev and next pointer at offset +0, +4
We don't care about prev field. Only next field (pointing to the next trigger) matters.
Manipluating next field of a trigger enables us to 'Jump' to any triggers we want.
For instance, looping is possible. Make next point to each other, and then break the cycle at certain condition.
Example map (testmap.scx in original article) uses loops to execute some trigger 3300 times and then escape.

The first trigger being executed for player p is determined by a fixed-offset pointer.
They are located in offset 51A280. More clearly,

51A280 + 12 * player + 4 : pointer to 'Last' trigger node.
51A280 + 12 * player + 8 : pointer to 'First' trigger node.

where 0 <= player <= 7.

At TRIG section, we modify address of pointer to 'First' trigger node.
and at next trigger cycle, (Forcefully triggered with Wait(0)) SC will execute the trigger at offset we specified.
and we can control the flow of triggers being executed from then.

The only problem (and the most major one) is that, Location table is too small to put on as many info as we want.
So I suggest that we should use String table, where we can control allocation size, to upload much trigger
and using MRGN section as a 'trigger' for initalizing STR triggers.

With this trick, programming with eud would be possible. (Real programming. I'm talking about something like calculating crc32 value of the text you type in)




Hard again; Sorry.

MRGN : where location datas are stored at scenario.chk. Fixed to 5200byte
STR : where strings are stored at scenario.chk. resizable.
TRIG : where triggers you modify with 'Classic Trigger Editor' or sorta resides in.

Post has been edited 1 time(s), last time on Jan 24 2014, 4:58 pm by trgk.



EUD

Jan 24 2014, 10:33 pm Cinolt Post #4



Quote from trgk
We actually don't know where our classic triggers are located. It's located somewhere. It's memory is dynamically allocated;
We need to dispatch our prefabricated trigger to some known location, and make SC recognize them as trigger an execute it.
In example map, I used Location table (5100byte from 0058DC60) to upload triggers.
(Easy to manipluate)

TBH I kinda TL;DR'd it because I don't think a lot of the UMS'ers in the West care about EUD Actions.

In any case how would you get SC to interpret the location table as triggers in the first place?



None.

Jan 25 2014, 1:10 am trgk Post #5



Quote from name:yoonkwun
TBH I kinda TL;DR'd it because I don't think a lot of the UMS'ers in the West care about EUD Actions.

In any case how would you get SC to interpret the location table as triggers in the first place?

Make data in triggernode structure, and place it in MRGN section.
Then change 51A280 + 12 * player + 8.

SC Code: (simplified)
for(it = (TriggeNode*)(51A280 + 12 * player + 8) ; it != NULL ; it = it->next) {
ExecuteTrigger(it);
}

EUDA is very common in here. Not in west, maybe;

Post has been edited 1 time(s), last time on Jan 25 2014, 1:16 am by trgk.



EUD

Jan 25 2014, 4:15 am Cinolt Post #6



So player is the owner of the trigger?

http://www.staredit.net/starcraft/CHK#TRIG

Quote
Following the 16 conditions and 64 actions, every trigger also has this structure

4 bytes: Used internally by starcraft (unknown usage. may be used to check if the trigger is "finished", ie no preserve trigger and the trigger has already run)
28 bytes: 1 byte for each player in the players/groups list (except for the last one) (see appendix).
00 - Trigger is not executed for player
01 - Trigger is executed for player

doesn't seem overflowable.



None.

Jan 25 2014, 4:31 am O)FaRTy1billion[MM] Post #7

👻 👾 👽 💪

He's saying there is a pointer that points to the next trigger to be run, and you overwrite that value to the location table so when SC reads the next trigger it reads your trigger in modifiable memory and then use wait(0) to make it immediately call that trigger.



TinyMap2 - Latest in map compression! ( 7/09/14 - New build! )
EUD Action Enabler - Lightweight EUD/EPD support! (ChaosLauncher/MPQDraft support!)
EUDDB - topic - Help out by adding your EUDs! Or Submit reference files in the References tab!
MapSketch - New image->map generator!
EUDTrig - topic - Quickly and easily convert offsets to EUDs! (extended players supported)
SC2 Map Texture Mask Importer/Exporter - Edit texture placement in an image editor!
\:farty\: This page has been viewed [img]http://farty1billion.dyndns.org/Clicky.php?img.gif[/img] times!

Jan 25 2014, 3:03 pm Sacrieur Post #8

Still Napping

To clarify what's being thrown around, using memory overflows to change Starcraft data using the Set Deaths action is known as EUD actions.

Shortly after the discovery of EUD actions by Heinermann, they were patched out. Farty (poster above) then created an add-on the re-enables EUD action functionality, but for it to work in a multiplayer setting, all players must be running the program.



None.

Feb 13 2014, 1:14 pm trgk Post #9



Stripped-down example
- 2 triggers in MRGN
- 1 trigger in TRIG
Trigger in TRIG activates MRGN triggers.

MRGN triggers builds up a loop. They fill Vanilla location table.
MRGN triggers loop 320 times. (320 = 64 * 20 / 4)

euddb entry for vanilla location table

Attachments:
trigexec2.scx
Hits: 4 Size: 74.25kb



EUD

Mar 4 2014, 9:55 am trgk Post #10



Simple linetracer example.
TRIG only initalizes STR trigger.
Triggers in STR sections does all the things.



It implements
- 1-cycle (under 100 trigger) multiplication/division of arbitary number. Since we can modify trigger to our likes, this is achieved very easily.
- Real-time terrain reading. Linetracer actually 'reads' terrain and follows some simple switch & if to calculate the next position.
- Terrain is an array of WORDs. So there also is trigger to 'split' dword to HIWORD and LOWORD, although this too basic.

in 177 triggers in STR (logic), thanks to loops and function calls enabled by direct trigger node pointer manipulation.

( About 500 triggers in TRIG section, but that can be automatically generated. )

( This can be implemented with non-EUDs. Mobile grid will suffice. This is just a demo. )

Attachments:
ltrace g.scx
Hits: 3 Size: 78.78kb

Post has been edited 1 time(s), last time on Mar 4 2014, 10:18 am by trgk.



EUD

Mar 4 2014, 1:28 pm TF- Post #11

🤙🏾

Powerful stuff, hope everyone can keep up, I'm more of an EUD user so I'll have to wait til there's some stable stuff to use.



🤙🏾

Mar 12 2014, 7:43 am trgk Post #12



Simple tool for trigger programming. (The one used for creating linetracer above)
See examples for more details.

Coded in python 3.

Attachments:
eudasm.7z
Hits: 0 Size: 2524.35kb



EUD

Apr 20 2014, 3:29 pm trgk Post #13



Trying to create a compiler which compiles from this:

Code
fdef bubblesort(arr, n) {
    vdef i, j;
    for(i = 0 ; i < n ; i++) {
        for(j = 1 ; j < n; j++) {
            vdef a, b;
            a = readdword(arr + j - 1);
            b = readdword(arr + j);
            if(a > b) {
                writedword(arr + j, a);
                writedword(arr + j - 1, b);
            }
        }
    }
}


to triggers.



EUD

Apr 20 2014, 4:07 pm Roy Post #14

An artist's depiction of an Extended Unit Death

Have you looked at http://www.staredit.net/topic/15156/? It's a trigger pre-processor capable of performing such logic.

Or are you referring to having it all self-contained as a self-modifying trigger?




Apr 20 2014, 10:58 pm trgk Post #15



Quote from Roy
Have you looked at http://www.staredit.net/topic/15156/? It's a trigger pre-processor capable of performing such logic.

Or are you referring to having it all self-contained as a self-modifying trigger?

Oreo triggers just duplicates triggers for every possible cases. (e.g. n = 1, 2, 3, 4, ...... , )

I'm trying to utilize self-modifying triggers to its maximum potential to create scripting language like JASS or something. (maybe slower)
This is possible. I've already made some basic looping, switch~case statement, nested if~else structure, and pointer dereferencing. (All in linetracer)

Code
$P1 = new Player("Player 1");
$spawntimer = new Deathcounter();

$P1->_if( $spawntimer->exactly(0) )->then(
    Display('Spawn timer set to 10 seconds'),
    $spawntimer->setTo(120),
'');

$P1->always(
    $spawntimer->subtract(1),
'');


Will be identical to the following code. (Grammer may change)

Code
vdef spawntimer; //global variable. Does not use any deaths counter.
// There is spawntimer somewhere in memory. You don't need to know about. There is.

fdef mainloop() {
    if(spawntimer == 0) {
        DisplayText("Spawn timer set to 10 seconds.");
        spawntimer = 0;
    }
}


Post has been edited 5 time(s), last time on Apr 21 2014, 6:19 am by trgk.



EUD

May 10 2014, 4:03 pm trgk Post #16



I'll leave some tutorial-style memo here.
This is mainly a translation of some article I've already written.

1. What is eudasm?

eudasm is a trigger programming tool, extensively using EUD actions. Map created by this tool won't run under plain vanilla game, and you will have to use some EUD enabler plugins to enable eud actions. Almost every concept of eudasm is based on EUD action.
With eudasm, you can create a general program structure such as functions, loops, variables, array, and pointers quite easily. I'll describe their basic examples below.

2. Internal of eudasm

Key concept of eudasm is modifying triggers with triggers. You can
- Modify trigger's execution order.
- Modify trigger's fields.

Since eudasm is designed for trigger engine 'bytecode', it has following limitations.
- Player-specific triggers. Every trigger is run for every player. This can be easily overcame by manipulating trigger's execution order
- Non-preserved triggers. Just like assembly languages (eudasm is short for EUD Assembly) , every triggers run when they are hit. Easily overcame



EUD

May 10 2014, 4:04 pm trgk Post #17



3. Basic loop.

So here we start with some basic loop. We'll create triggers that converts scout to zerglings.
Code
int i = 0;
while(P1 has protoss scout) {
   Move location #1 to P1's protoss scout
   Kill scout at #1
   Create zerging at #1
}


Here are the full code.
Code
Trigger: @A
    next B @Anext
    Conditions:
        Bring("Player 1", Exactly, 0, "Protoss Scout", 64)
    Actions:
        SetDeaths(&Anext, SetTo, C, 0)
   
Trigger: @B
    next A

    Actions:
        MoveLocation(1, "Protoss Scout", "Player 1", 64)
        GiveUnits(1, "Protoss Scout", "Player 1", 1, "Player 2")
        KillUnitAt(1, "Protoss Scout", 1, "Player 2")
        CreateUnit(1, "Zerg Zergling", 1, "Player 1")

Trigger: @C
    next triggerend
    Actions:
        SetDeaths(&Anext, SetTo, B, 0)



Here we defined three triggers A, B, C.

Key concept :
Each triggers has 'next' field. It points to the next executing triggers.
next of trigger A is trigger B. So B is executed right after A is executed.
Whether A's condition matches or not doesn't matter.
A's action can modify A's next before trigger engine reads it. So you can
create a conditional jump by mixing next pointer with actions.


Trigger A:

Code
Trigger: @A
    next B @Anext
    Conditions:
        Bring("Player 1", Exactly, 0, "Protoss Scout", 64)
    Actions:
        SetDeaths(&Anext, SetTo, C, 0)


- If Player 1 brings exactly 0 protoss scout to Location #64 (Anywhere), then set A's next to C.
if the action is taken, then starcraft's trigger engine will proceed to trigger C after executing trigger A.
if not, trigger engine will proceed to where A's next have been previously pointing. (In this case, trigger B)

( Anext is defined explicitly in "next B @Anext". )

Summary

Code
 No scout -> goto C
 Else -> goto B





Trigger B:

Code
Trigger: @B
    next A

    Actions:
        MoveLocation(1, "Protoss Scout", "Player 1", 64)
        GiveUnits(1, "Protoss Scout", "Player 1", 1, "Player 2")
        KillUnitAt(1, "Protoss Scout", 1, "Player 2")
        CreateUnit(1, "Zerg Zergling", 1, "Player 1")


- Move location to scout
- Give scout to P2 (Since scout that have been killed is recognized by Bring condition. I should have used command condition instead. :-( )
- Kill scout.
- Create zergling there
- Jump to A (B's next)


Summary

Code
 Convert scout to zergling





Trigger C:

Code
Trigger: @C
    next triggerend
    Actions:
        SetDeaths(&Anext, SetTo, B, 0)


- Since we went to C because jump from A to C has been taken, A's next is currently set to C. We reset it to B.
- And, we jump to triggerend. Jumping to triggerend (some predefined address) means ending trigger cycle for starcraft.

Summary

Code
 exit




Summary of overall code:

Code
A:
 Has scout -> goto C
 goto B

B:
 Convert scout to zergling
 goto A

C:
 exit


Quite simple, huh? eudasm code has one-to-one correspondence to assembly code.



-------------------


I forgot the most important part. How to compile a code?

1. Download eudasm Here
2. Extract to your favorite place.
3. Open examples\01. basic_loop\code.eud, and replace it by the code we've working on. (Code with trigger A, B, C)
4. Hit compile.bat. Some console screen will appear, and you'll have basemap g.scx and basemap.map
- basemap.scx : 'Base' map. Map with everything but triggers.
- basemap g.scx : Compiled map. bsaemap.scx + eudasm trigger.
- basemap.map : Debug purpose. Not yet complete.
5. Run basemap g.scx with starcraft. It should run well.


6. AND IT CRASHES??



-------

7. This won't crash. Some weird trick are used in this code.
Shortly, for eudasm trigger not to crash, you should ensure that proceeding next triggers will make it to triggerend.

After every trigger cycle, what we've made always ends like this.

start
|
A --> B C --> triggerend
^------<

So, going through next won't make it to triggerend.

Simple solution is to add two dummy triggers.

Code
Trigger: @start1
    next triggerend @start1next

Actions:
    SetDeaths(&start1next, SetTo, start2, 0)

Trigger: @start2
    next A

Actions:
    SetDeaths(&start1next, SetTo, triggerend, 0)

( .. rest .. )


Starting triggers is the trigger which appears the first. So in this case, starting trigger is start1.

Initial :

start
|
start1 -> triggerend

start2 -> A


After executing start1:

start
|
start1
|
start2 -> A

So start2 executes.


After executing start2:

start
|
start1 -> triggerend

start2 -> A

So A is exceuted, and rest of procedure is the same as previous. Scout will be converted to zergling.

Post has been edited 3 time(s), last time on Jun 1 2014, 7:12 am by trgk.



EUD

May 10 2014, 7:43 pm Wormer Post #18



Do you need EUDEnabler or other program to run these things? or is it working on plain SC BW?



Some.

May 11 2014, 4:26 am trgk Post #19



Quote from Wormer
Do you need EUDEnabler or other program to run these things? or is it working on plain SC BW?

Yes. eudasm workes only if eud actions can be used.
EUD action is the only thing needed to create various tricks.



EUD

May 11 2014, 10:56 am Wormer Post #20



Quote from trgk
Quote from Wormer
Do you need EUDEnabler or other program to run these things? or is it working on plain SC BW?

Yes. eudasm workes only if eud actions can be used.
EUD action is the only thing needed to create various tricks.
That's right. You should make it clear somewhere in the head post or header for people who read this for the first time.



Some.

Options
  Back to forum
Please log in to reply to this topic or to report it.
Members in this topic: None.
[05:00 pm]
lil-Inferno -- benis
[10:41 am]
v9bettel -- Nice
[01:39 am]
Ultraviolet -- no u elky skeleton guy, I'll use em better
[10:50 pm]
Vrael -- Ultraviolet
Ultraviolet shouted: How about you all send me your minerals instead of washing them into the gambling void? I'm saving up for a new name color and/or glow
hey cut it out I'm getting all the minerals
[10:11 pm]
Ultraviolet -- :P
[10:11 pm]
Ultraviolet -- How about you all send me your minerals instead of washing them into the gambling void? I'm saving up for a new name color and/or glow
[2024-4-17. : 11:50 pm]
O)FaRTy1billion[MM] -- nice, now i have more than enough
[2024-4-17. : 11:49 pm]
O)FaRTy1billion[MM] -- if i don't gamble them away first
[2024-4-17. : 11:49 pm]
O)FaRTy1billion[MM] -- o, due to a donation i now have enough minerals to send you minerals
[2024-4-17. : 3:26 am]
O)FaRTy1billion[MM] -- i have to ask for minerals first tho cuz i don't have enough to send
Please log in to shout.


Members Online: Ultraviolet