[Fortgeschritten][Research|Hex] Funktionen (Standardscripts, std's)

Wir sammeln alle Infos der Bonusepisode von Pokémon Karmesin und Purpur für euch!

Zu der Infoseite von „Die Mo-Mo-Manie“
  • 1. Vorwort
    Ich bin mir nicht ganz sicher, ob ich diese Informationen hier im Tutorial-Bereich oder im allgemeinen Bereich posten sollte, aber ich dachte mir, das passt im Tutorial-Bereich ganz gut.
    ACHTUNG: Ich setze Wissen im Umgang mit grundlegenden Script-Befehlen voraus!
    Wie der Titel bereits andeutet befasse ich mich in diesem Tutorial mit Standard-Scripts. Dass ich dieses Tut als fortgeschritten getaggt habe, könnte im Zusammenhang mit der Bezeichnung "Standard" vielleicht etwas paradox klingen, deswegen will ich kurz erklären, was Standardscripts, auch Funktionen oder kurz std's genannt, eigentlich sind:

    2. Einleitung - Standardscripts: Verwendung

    Okay, fangen wir mit einem Textscript an. Wie auch schon ein Anfänger wissen sollte, sieht ein Textscript - wohlbemerkt unter XSE - so aus:


    Code
    #dynamic 0x800000#org @mainmsgbox @text 0x2end#org @text= Hallo Welt!


    Fragen wir einmal die XSE Hilfe mit einem Druck auf F1, solange sich der Cursor in der Zeile msgbox @text 0x2 befindet. Wir erfahren, dass msgbox eigentlich gar kein Befehl ist, sondern ein Konstrukt. Würden wir statt des Konstrukts den eigentlichen Code schreiben, sähe das so aus:


    Code
    #dynamic 0x800000#org @mainloadpointer @textcallstd 0x2end#org @text= Hallo Welt!


    Wenn ihr sonst mit Pokescript arbeitet, sollte euch der Script jetzt auch etwas vertrauter vorkommen:



    Aber was bedeuten die Befehle loadpointer und callstd? Der loadpointer Befehl lädt einen Pointer, so dass andere Befehle ihn benutzen können. Der callstd Befehl ist für dieses Tutorial wesentlich interessanter. Er ruft eine Funktion auf, diese muss nicht zwangsläufig mit loadpointer zusammenhängen!


    Aber was ist denn jetzt eine Funktion? Funktionen sind Scripts, die darauf ausgelegt sind immer wieder verwendet zu werden. Die hier verwendete Funktion 2, beispielsweise sieht dekompiliert so aus:


    Code
    //---------------#org 0x1A7AF3lockfaceplayerpreparemsg 0x0  //bereitet eine Textbox mit dem Text am mit loadpointer geladenen Offset vorwaitmsg       //zeigt vorbereitete Textbox anwaitkeypress  //pausiert die Ausführung des Scripts, bis der Spieler eine Taste drücktreleasereturn        //springt zum letzten call zurück


    Die eventuell unklaren Zeilen habe ich auskommentiert. Wie ihr seht, beinhaltet die Funktion 2 sowohl die lock-/release-Kombination, als auch den faceplayer Befehl. Jetzt dürfte euch auch klar sein, warum ich zu Beginn einen so kurzen Script vorzeigen konnte, statt


    Code
    #dynamic 0x800000#org @mainlockfaceplayermsgbox @text 0x6releaseend#org @text= Hallo Welt!


    Wenn ich das schon so sage, dürftet ihr wohl auch eine Vermutung haben, wie die hier verwendete Funktion 6 aussieht - genau! Sie sieht exakt gleich aus, bis auf die erwähnten Befehle lock, faceplayer und release, diese fehlen bei Funktion 6. Wollten wir also einen Textscript ohne callstd schreiben, würde er so aussehen:


    Code
    #dynamic 0x800000#org @mainlockfaceplayerloadpointer @textpreparemsg 0x0waitmsgwaitkeypressreleaseend#org @text= Dieses Mal ohne callstd!


    Diese Schreibweise ist nicht nur umständlich lang und wenig einprägsam - sie verschwendet dazu auch noch Platz im Rom, den man doch lieber für andere Scripts, Grafiken oder Daten investieren würde, richtig?
    Und genau deswegen gibt es Standardscripts! Was euch allerdings vielleicht irritiert, ist der return Befehl in der Funktion, den ich im Script durch end ersetzt habe. Das Prinzip hierbei ist einfach: stünde im Standardscript end, würde euer Script nach dem Text enden, obwohl ihr eventuell noch weitere Befehle hinter dem Text ausgeführt sehen wolltet. Würde ich hingegen return im Script benutzen, würde der Rom hängen bleiben, da Script nicht von einem anderen Script aus gecallt wurde. Außerdem wollen wir ja nicht, dass der Script an irgendeine Stelle springt, wir wollen - zumindest in diesem Beispiel - das der Script endet.


    3. Unsere erste eigene Funktion
    In diesem Abschnitt werde ich weniger auf den callstd Befehl eingehen, als auf die Logik die hinter den Standardscripts steckt. Wie ich bereits erklärt habe, benutzen die Funktionen stets den return Befehl. Das hat - wie ebenfalls bereits erwähnt - den Grund, das der Script nach dem Aufruf der Funktion weiter laufen soll. Wenn wir eine eigene Funktion schreiben, dann gibt es zwei Möglichkeiten, diese Bedingung zu erfüllen.
    Dazu muss ich zunächst etwas weiter ausholen. Wenn ihr bereits den Unterschied zwischen goto und call kennt, dann braucht ihr den Inhalt des nächsten Spoilers nicht zu lesen.



    Als Beispiel werden wir eine Funktion schreiben, die in den Text-Puffer [buffer_1] den Namen des Rivalen aus Rubin/Saphir/Smaragd puffert. Zuerst die umständliche Methode mit call und danach die elegante Methode mit goto:




    Zugegeben, nicht das beste Beispiel um den Unterschied zu zeigen, aber stellt euch vor ihr habt das ganze auf, sagen wir ...100 Zeilen oder mehr gestreckt. Es wäre wirklich kein Spaß das ganze mit 10-20 return und call Befehlen zu lösen, nur um nach der Ausführung der Funktion am Ende des ersten Code-Blocks zu landen und dort das letzte return zu platzieren.
    Aber schauen wir uns unsere Funktion in Aktion an:


    Code
    #dynamic 0x800000#org @testcall @MeineFunktionmsgbox @string 0x2end#org @string= [buffer_1]: Hallo!


    Kompiliert die Beispiel Funktion, schreibt euch das Script-Offset auf und ersetzt das @MeineFunktion durch das Offset + 0x8000000. Wenn ihr die Funktion an das Offset 0x800000 kompiliert habt, dann ersetzt @MeineFunktion durch 0x8800000. Testet diesen Script mit beiden Geschlechtern. Ihr solltet zwei verschiedene Texte ausgegeben bekommen: ein Mal "Brix: Hallo!", wenn ihr als Mädchen spielt, ein Mal "Maike: Hallo!", wenn ihr als Junge spielt. Wenn das der Fall ist, habt ihr alles richtig gemacht!


    4. Funktionen mit callstd kompatibel machen
    Kommen wir zu dem Teil des Tutorials, in den ich am meisten Zeit investiert habe. Für dieses Kapitel braucht ihr neben XSE für die Scripts, AM zum platzieren der Events und einem Rom einen Hex-Editor. Ich selbst benutze NEXTSOFT Hex-Editor MX . Zudem kann ich noch Hex-Workshop empfehlen, ist allerdings keine Freeware.
    Wenn wir uns den callstd Befehl ansehen, stellen wir fest, dass er nur ein Byte als Parameter annimmt. Dieses Byte wird einem Header zugeordnet. Es gibt im Rom keine Limiter-Bytes, aber jeweils 4 Pointer auf den Anfang und das Ende des Headers. Der Header befindet sich in BPRD (Feuerrot Deutsch) am Offset 0x1603B0. Navigiert zu diesem Offset und mit geübtem Auge erkennen wir eine Vielzahl an Pointern, 9 Stück um genau zu sein. Es gibt also 9 Funktionen, die bereits von callstd genutzt werden können, nämlich die Funktionen 0x0 bis 0x8. Die Pointer die ihr seht, weisen auf die entsprechenden Funktionen.
    Wir wollen als kleines Beispiel die Funktion 5 bearbeiten, die JA-/NEIN-Boxen öffnet. Wir zählen also die Pointer ab, sie entsprechen jeweils 4 Bytes, Funktion 0, 1, 2, 3, 4, macht 5 Funktionen, also 20 Bytes. Gehen wir also 20 Bytes weiter.
    Dort steht 0x107B1A08. Wir drehen die Byte-Reihenfolge (aus 0xABCDEF wird 0xEFCDAB) und erhalten 0x081A7B10. Wir ziehen 0x8000000 ab und haben aus dem Pointer 0x107B1A08 das Offset 0x1A7B10 berechnet - wie man das bei Pointern eben so macht.
    Dekompilieren wir den Script... wir sehen dies:


    Code
    //---------------#org 0x1A7B10preparemsg 0x0waitmsgyesnobox 0x14 0x8return


    Ändern wir mal die Position der JA-/NEIN-Box mal auf (0|0):


    Code
    #dynamic 0x800000
    
    
    #org @JaNeinBox
    preparemsg 0x0
    waitmsg
    yesnobox 0x0 0x0
    return


    Kompilieren wir die Funktion und berechnen den Pointer auf das Script-Offset. Wir addieren 0x8000000 und drehen wieder die Byte-Reihenfolge. Aus 0x800000 wird 0x00008008. Überschreiben wir den Funktionspointer (von 0x107B1A08 zu 0x00008008). Wenn wir jetzt einen einfachen Entscheidungsscript testen, stellen wir fest, dass sich die JA-/NEIN-Box in die obere linke Ecke bewegt hat.


    Das ist schon einmal schön: wir haben eine bestehende, kompatible Funktion bearbeitet. Jetzt wollen wir eine zehnte Funktion, nummer 0x9 einfügen. Dazu müssen wir zunächst einmal den Header umpointen. Wir kopieren zunächst alle 9 Pointer und fügen sie an einer freien Stelle im Rom ein. Wir schreiben uns den Pointer auf das neue Offset auf (z.B. 0x00008008) auf. Jetzt durchsuchen wir die Rom nach Pointern auf das erste Byte im Header, also nach 0xB0031608 und überschreiben sie. Nun wollen wir hinten eine neue Funktion anhängen. Wir überschreiben also zunächst einmal 4 Bytes hinter unserem Header mit 00 Bytes. Jetzt haben wir Platz für eine weitere Funktion. Ich empfehle aber einige weitere Bytes zu überschreiben, damit man nicht bei jeder neuen Funktion den Header immer wieder umpointen muss.
    Jetzt müssen noch die Pointer auf das Ende des Headers geändert werden. Ich habe Platz für fünf Funktionen gelassen (20 Bytes), mit der Länge des ursprünglichen Headers (36 Bytes) macht das 56 Bytes, in Hex 0x38, die ich zum Grund-Offset, bei mir 0x800000 hinzuaddieren muss, um das Ende des Headers zu bestimmen. Bei mir liegt das Ende des Headers also bei 0x800038. Der Pointer auf dieses Offset ist 0x38008008. Jetzt suchen wir also nach den Pointern auf das Ende des Headers, sprich nach 0xD4031608 und ersetze sie durch meinen Pointer (zur Erinnerung: 0x38008008).
    Jetzt sind meine fünf neuen Funktionen freigeschaltet. Wenn ich eine neue Funktion kompiliert habe, kann ich einfach den Pointer auf die neue Funktion an einem der freien Plätze einfügen.

    5. Schlusswort

    Zum Ende möchte ich Haro100 danken, durch dessen Tutorial ich das Scripten mit Pokescript gelernt habe, das XSE-Scripting habe ich mir später selbst beigebracht, was ohne seine Hilfe nicht möglich gewesen wäre. In Folge dessen gäbe es dieses Tut also ohne Haro vermutlich nicht. Danke dir!
    Zudem möchte ich noch dir, dem Leser danken! Schließlich möchte ich, dass dieses Tutorial gelesen wird und nicht einfach nur Platz auf der Seite weg nimmt.
    Mein nächstes Tutorial folgt dann wohl in Video-Form, also am besten immer mal wieder auf meinem YT-Channelvorbei schauen, vielleicht werdet ihr demnächst dort ein Video-Tutorial finden. Würde mich auf alle Fälle sehr freuen ;D


    Ansonsten bleibt nicht viel zu sagen außer: "Danke fürs vorbeischauen, ich hoffe ihr konntet etwas lernen!"

    [tabmenu][tab=YT-Channel]DarkTekHacks
    In Bearbeitung: Scripting Tutorial #001 - Grundlagen zu Events
    Ich habe im Moment keine Zeit für Tutorials, sorry.
    [tab=dA-Account]TheDarkShark
    Ab sofort wird immer, wenn ich was sehenswertes mache, hochgeladen![/tabmenu]

    5 Mal editiert, zuletzt von Darkshark ()