Beliebiges Shiny erzwingen
Weil öfter gefragt wurde, wie man einen Encounter à la "Shiny Garados" machen kann, habe ich mich einmal mit dem "scripten" in den Advance-Generationen befasst. Was ich unter dem Strich sagen kann: Das Vorhaben ist gelungen, wenn auch nur mit viel Arbeit.
Wer also ein beliebiges Pokémon in der GBA-Generation zwingend möchte, ein Shiny zu sein, der wird dies hier lernen!
Wir arbeiten mit einer Pokémon Saphir German ROM. Das Ganze sollte auf Rubin genau so funktionieren, allerdings gebe ich dafür keine Garantie.
Als erstes müssen wir die Routine, welche die PID in den RAM schreibt, also auch dafür verantwortlich ist, ob ein Pokémon ein Shiny ist oder nicht, aus der Umgebung ausgliedern. Ausgliedern deswegen, weil die Veränderungen, welche wir machen werden so groß sind, dass die Routine insgesamt länger sein wird, als Platz vorhanden ist. Würden wir die unteren Bytes weiter nach unten schieben, würden die Jumpadressen nicht mehr stimmen, die ganze ROM wäre also unbrauchar. Deswegen => Ausgliedern!
Das funktioniert folgendermaßen: Öffnet die Saphir ROM im Hexeditor und springt an die Adresse 0x3d6a0. Dort müsst ihr folgendes Script finden:
0003d6a0h: A0 78 00 04 09 18 E0 78 00 06 09 18 39 60 02 E2 ;
0003d6b0h: 21 78 60 78 00 02 09 18 A0 78 00 04 09 18 E0 78 ;
0003d6c0h: 00 06 09 18 79 60 F6 E1 00 22 3B 1C 08 33 98 18 ;
0003d6d0h: A1 18 09 78 01 70 01 32 09 2A F8 DD EB E1 20 78 ;
Das wichtige, das wir extrahieren werden, ist diese Funktion:
0803d6b0 7821 ldrb r1, [r4, #0x0]
0803d6b2 7860 ldrb r0, [r4, #0x1]
0803d6b4 0200 lsl r0, r0, #0x08
0803d6b6 1809 add r1, r1, r0
0803d6b8 78a0 ldrb r0, [r4, #0x2]
0803d6ba 0400 lsl r0, r0, #0x10
0803d6bc 1809 add r1, r1, r0
0803d6be 78e0 ldrb r0, [r4, #0x3]
0803d6c0 0600 lsl r0, r0, #0x18
0803d6c2 1809 add r1, r1, r0
0803d6c4 6079 str r1, [r7, #0x4]
0803d6c6 e1f6 b $0803dab6
Wer ein bisschen ASM kann, der wird verstehen, dass hier die PID byteweise aus dem RAM zu einem Word umgeschrieben wird. Anschließend springt die CPU nach $0803dab6 . Wir können also frei schalten und walten, solange wir uns oberhalb des b $0803dab6 austoben. Wir ersetzen diese Funktion also mit meinem kleinen Patch:
0003d6a0h: A0 78 00 04 09 18 E0 78 00 06 09 18 39 60 02 E2 ;
0003d6b0h: 40 68 07 47 33 EB 6B 08 33 EB 6B 08 FF FF FF FF ;
0003d6c0h: FF FF 00 00 00 00 F6 E1 00 22 3B 1C 08 33 98 18 ;
0003d6d0h: A1 18 09 78 01 70 01 32 09 2A F8 DD EB E1 20 78 ;
Gewissenhaft übernehmen. Der kleinste Fehler wird alle Pokémonencounter zerstören!!! Das Event wurde jetzt folgendermaßen umgeschrieben.
0803d6b0 6840 ldr r0, [r0, #0x4]
0803d6b2 4707 bx r0
0803d6b4 eb33 [ ??? ]
0803d6b6 086b lsr r3, r5, #0x01
0803d6b8 eb33 [ ??? ]
0803d6ba 086b lsr r3, r5, #0x01
0803d6bc ffff blh $0ffe
0803d6be ffff blh $0ffe
0803d6c0 ffff blh $0ffe
0803d6c2 0000 lsl r0, r0, #0x00
0803d6c4 0000 lsl r0, r0, #0x00
0803d6c6 e1f6 b $0803dab6
0xFFFF wird übersprungen, 0x0000 sind NOPs, lsl 0 ist immer äquivalent. Das wichtige sind die ersten beiden Befehle. Es wird ein Pointer geladen und anschließend zu genau dieser ROM-Adresse gesprungen. Alles klar? Natürlich springen wir nicht zufällig irgendwohin, sondern die CPU wird hübsch im "Unaligned Memory" landen, wo wir schreiben können, was wir wollen.
33 EB 6B 08 steht direkt im Code. Meine Sprungadresse ist also 0x086BEB33. Die 33 steht (odd) für eine folgende THUMB-Routine, das Ganze ist noch ein Pointer. Umgewandelt kommen wir auf 0x6BEB32.
Hexviewer her, ab nach 0x6BEB32. (jedem sollte klar sein, wie das geht) Jetzt kommt hier unser erweitertes Script. Die Idee ist die: Wir führen eine neue Flag
ein, im RAM. Wenn diese 01 lautet, dann werden alle Pokémon zu Shinys, in jedem anderen Fall begegnen uns normale
Pokés. Diese Flag habe ich willkürlich auf 0x02021330 gelegt. Da können wir künftig "Shinyness" erzwingen.
Der erste Teil ist die veränderte Routine, die nur im "If_01_Equal"-Fall ausgeführt wird. Hier wird UPPER_XKEY = LOWER_XKEY gewährleistet, was so zur Folge hat, dass UPPER XOR LOWER immer 0 ist, welches ja kleiner als 8 ist. Damit wäre die "Shinyness" erzwungen.
Nur, wie coded man sowas? Hier ist jetzt ein bisschen mehr Scripting gefordert, hier füge ich meine ausgegliederte ASM-Routine ein. Sorgt dafür, dass der ROM an der Stelle so aussieht:
006beb10h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ;
006beb20h: 50 49 44 5F 70 61 74 63 68 65 64 2E 63 FF FF FF ;
006beb30h: FF FF 04 B4 02 21 08 06 02 21 0A 04 10 43 13 21 ;
006beb40h: 0A 02 10 43 30 22 10 43 00 68 04 BC 01 28 0B D1 ;
006beb50h: 21 78 60 78 00 02 09 18 A0 78 00 04 09 18 E0 78 ;
006beb60h: 00 06 39 60 79 60 0A D1 21 78 60 78 00 02 09 18 ;
006beb70h: A0 78 00 04 09 18 E0 78 00 06 09 18 79 60 08 21 ;
006beb80h: 08 06 03 21 09 04 08 43 D6 21 09 02 08 43 C7 21 ;
006beb90h: 08 43 07 47 FF FF FF FF FF FF FF FF FF FF FF FF ;
006beba0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ;
Dies ist hochsensibles ASM, jeder Fehler hat drastische Nachwirkungen!!!
So... was steht da jetzt? Der Disassembler meldet:
086beb32 b404 push
086beb34 2102 mov r1, #0x2
086beb36 0608 lsl r0, r1, #0x18
086beb38 2102 mov r1, #0x2
086beb3a 040a lsl r2, r1, #0x10
086beb3c 4310 orr r0, r2
086beb3e 2113 mov r1, #0x13
086beb40 020a lsl r2, r1, #0x08
086beb42 4310 orr r0, r2
086beb44 2230 mov r2, #0x30
086beb46 4310 orr r0, r2
086beb48 6800 ldr r0, [r0, #0x0]
086beb4a bc04 pop
086beb4c 2801 cmp r0, #0x1
086beb4e d10b bne $086beb68
086beb50 7821 ldrb r1, [r4, #0x0]
086beb52 7860 ldrb r0, [r4, #0x1]
086beb54 0200 lsl r0, r0, #0x08
086beb56 1809 add r1, r1, r0
086beb58 78a0 ldrb r0, [r4, #0x2]
086beb58 78a0 ldrb r0, [r4, #0x2]
086beb5a 0400 lsl r0, r0, #0x10
086beb5c 1809 add r1, r1, r0
086beb5e 78e0 ldrb r0, [r4, #0x3]
086beb60 0600 lsl r0, r0, #0x18
086beb62 6039 str r1, [r7, #0x0]
086beb64 6079 str r1, [r7, #0x4]
086beb66 d10a bne $086beb7e
086beb68 7821 ldrb r1, [r4, #0x0]
086beb6a 7860 ldrb r0, [r4, #0x1]
086beb6c 0200 lsl r0, r0, #0x08
086beb6e 1809 add r1, r1, r0
086beb70 78a0 ldrb r0, [r4, #0x2]
086beb72 0400 lsl r0, r0, #0x10
086beb74 1809 add r1, r1, r0
086beb76 78e0 ldrb r0, [r4, #0x3]
086beb78 0600 lsl r0, r0, #0x18
086beb7a 1809 add r1, r1, r0
086beb7c 6079 str r1, [r7, #0x4]
086beb7e 2108 mov r1, #0x8
086beb7e 2108 mov r1, #0x8
086beb80 0608 lsl r0, r1, #0x18
086beb82 2103 mov r1, #0x3
086beb84 0409 lsl r1, r1, #0x10
086beb86 4308 orr r0, r1
086beb88 21d6 mov r1, #0xd6
086beb8a 0209 lsl r1, r1, #0x08
086beb8c 4308 orr r0, r1
086beb8e 21c7 mov r1, #0xc7
086beb90 4308 orr r0, r1
086beb92 4707 bx r0
Schön. Damit wäre der erste Teil geschafft. Wer mag, kann jetzt mal testweise das Ganze speichern und im Memory Viewer vom Emulator auf 02021330 gehen. Gebt ihr dort 01 ein, so erscheinen nurnoch Shinys. Bei 00 sind alle Pokémon wieder normal. Sehr schön. Damit wäre mal die Shinyness ordentlich ansprechbar. Jetzt schreiben wir ein nettes kleines Script (Pokéscript + ASM) um das Ganze aufzurufen.
[23] //Damit ruft man eine ASM-Routine auf (die brauchen wir zum Aktivieren der Flag!)
[XXXXXXXX] //Pointer zur ASM-Routine
[00] //Füller
[B6] //Wildes Pokémon
[XXXX] //Pokémon-ID (INGAME)
[XX] //Level
[XXXX] //Gehaltenes Item
[00]
[25] //Special-Event
[3801] //Wildes Pokémon erscheint!
[28] //Warte
[0101] //Eine Sekunde
[23] //ASM-Routine
[XXXXXXXX] //Pointer zur ASM-Routine (Flag wieder auf 00!!!)
[02] //Ende
Setzen wir dies alles zu einem Script zusammen, so kommen wir (die Pointer setze ich willkürlich - übernehmt sie 1:1 oder ändert sie, aber nur, wenn ihr wirklich wisst, was ihr tut!) zu Folgendem:
006bea50h: 23 71 EA 6B 08 00 B6 82 00 1E 00 00 00 25 38 01 ;
006bea60h: 28 01 01 23 B1 EA 6B 08 00 02 FF FF FF FF FF FF ;
Das war der Pokéscript-Part. Noch zwei kleine ASM-Scripte und wir haben´s geschafft. Die beiden Scripte sind an die selbe Stelle in den ROM zu legen, es sei denn, man hat oben schon die Pointer umgeschrieben. Mit 1:1 übernehmen seid ihr auf der sicheren Seite.
006bea70h: 00 B5 02 21 08 06 02 21 0A 04 10 43 13 21 0A 02 ;
006bea80h: 10 43 30 22 10 43 01 21 01 70 01 BC 07 47 FF FF ;
006bea90h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ;
006beaa0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ;
006beab0h: 00 B5 02 21 08 06 02 21 0A 04 10 43 13 21 0A 02 ;
006beac0h: 10 43 30 22 10 43 00 21 01 70 01 BC 07 47 FF FF ;
Zwei mal fast das selbe, nämlich:
086bea70 b500 push
086bea72 2102 mov r1, #0x2
086bea74 0608 lsl r0, r1, #0x18
086bea76 2102 mov r1, #0x2
086bea78 040a lsl r2, r1, #0x10
086bea7a 4310 orr r0, r2
086bea7c 2113 mov r1, #0x13
086bea7e 020a lsl r2, r1, #0x08
086bea80 4310 orr r0, r2
086bea82 2230 mov r2, #0x30
086bea84 4310 orr r0, r2
086bea86 2101 mov r1, #0x1 // oder "2100 mov r1, #0x0", je nachdem, ob wir die Flag an oder ausmachen wollen.
086bea88 7001 strb r1, [r0, #0x0]
086bea8a bc01 pop
086bea8c 4707 bx r0
Lädt jeweils die Flags und setzt, bzw. löst sie. Pointet zuletzt noch ein beliebiges Event in AM auf $6bea50 um und betrachtet das Ergebnis. Ihr werdet vom roten Garados auf Level 30 angegriffen.


Zur