SearchForCheats.de.vu » Tutorials » Advanced User Tutorial #1 - How To Create A Shiny Encounter


Gevorderde Gebruiker Tutorial #1 Hoe Maak Je Een Shiny-Battle

This tutorial was translated by the User D-Trogh. The original version can be found in the tutorials-section.
If you want to provide your support to my website by translating this tutorial to another language, please contact me. (Details can be found in the imprint)
Thanks!
Mastermind_X

Hallo! Fijn je hier te zien! Deze pagina kan enkel gevonden worden door goede codeurs. Grapje natuurlijk, maar je zult waarschijnlijk niet begrijpen welke procedures we hier gaan hacken als je nog maar net Hacking Lesson 4 hebt gevolgd.
Zullen we beginnen? Oké, wat we willen doen is het volgende: Een Shiny-Battle maken voor Pokmon FireRed [US] net zoals die goede, ouwe Red Gyarados in Pokmon Gold.

Wat je moet kunnen/weten vooraleer je deze tutorial tot een goed einde kan brengen:

  • Werken met een HEX-Editor
  • Repointen met AdvanceMap of EliteMap
  • GBA-Adressing (Van Pointer naar Offset en omgekeerd)
  • Scripten voor de Pokémon GBA games
  • Basis kennis ASM (Dit is niet noodzakelijk, maar dan begrijp je alles een beetje beter)

Wat we zullen nodig hebben:

  • Een HEX-Editor
  • Een Pokémon FireRed [US] ROM (Nee, ik vertel je niet waar je die vindt)
  • Een emulator om je veranderingen te testen
  • Eventueel de VBA-SDL-H debugger
  • Je HERSENEN !!!

Wat gaan we doen met deze dingen?

  • De Shiny-Routine vinden
  • Ze in twee delen splitsen - een gepatchte routine en een originele - afhankelijk van een flag in de RAM
  • Een ASM-Script schrijven dat deze flag in- of uitschakeld
  • Het (gewone) script schrijven met de twee ASM-Calls

De kans dat een Pokémon shiny is of niet is gebaseerd op de Pokémon ID (aka Rnd#). Die ID zorgt ook voor het geslacht, de nature, the unown shape en al dat andere. Wij zijn echter enkel geïnteresseerd in het shiny gedeelte. De Shiny-kans is eigenlijk een berekening tussen de Pokémon ID en de Trainer IDs, het resultaat hiervan noemen we de XKey. (XKey = Pokémon ID XOR Trainer IDs)
Ingewikkeld? Wel, dan zal ik het even duidelijker uitleggen met behulp van een voorbeeld.
Laat ons zeggen dat je Trainer ID gelijk is aan '56432'. Je 'Secret' Trainer ID is, eurm, gelijk aan '026025'. Zet deze om naar HEX-values en je krijgt '0xDC70' voor je TID en '0x65A9' voor je STID. Voeg deze samen op deze manier:

(STID << 0x10) OR (TID)

Je krijgt dan als tweede waarde (Trainer IDs) voor onze berekening '0x65A9DC70'. Onze eerste waarde, de PID, wordt willekeurig berekend. Laat ons zeggen dat de PID voor de Pokémon die je 'nu' tegenkomt gelijk is aan '0x57ADF001'. Met de kennis die we nu hebben berekenen we (of het spel) de XKey. Ik hoop dat je nog weet wat dat is? Inderdaad! XKey = Pokémon ID XOR Trainer IDs.
Met ons voorbeeld krijgen we dan:

    0x57ADF001
XOR 0x65A9DC70
--------------
    0x32042C71

De Shiny-data wordt opgeslagen in de verhouding van het eerste 'halfword' over het tweede. Als het resultaat van het XORen van deze twee waarden lager is dan acht, dan is de Pokémon shiny. Laten we even kijken hoe dit er uitziet (Zo snap je het misschien beter):

(Xkey >> 0x10) XOR (XKey AND 0xFFFF)

(Xkey >> 0x10)    ==     0x3204
(XKey AND 0xFFFF) == XOR 0x2C71
                     ----------
                         0x1E75

Het resultaat is '0x1E75', veel groter dan 8! De Pokémon is dus niet shiny. MAAR, we zijn hier om dat te veranderen!
Ik heb het volgende idee:
(Xkey >> 0x10) and (XKey AND 0xFFFF) mag niet groter zijn dan acht. Als we er nu in slagen iet te doen zodat (Xkey >> 0x10) altijd gelijk is aan (XKey AND 0xFFFF), dan zou het XORen als resultaat nul geven. Nul is kleiner dan acht, dus dan hebben we onze shiny!
Dus, elk halfword dat verdubbeld is tot een word zou een geldige integer zijn. Dat is het ene deel.
Maar, de XKey is gewoon een ander resultaat van een XOR berekening, hoe slagen we er in deze naar onze gewilde integer te veranderen? De volgende methode is de gemakkelijkste en diegene die ik (Mastermind_X) het beste vind.
Laten we '0x00000000' als onze XKey nemen. Het is een 'geldige' shiny-integer en is het gemakkelijkst om te berekenen uit een XOR operatie. Ik denk dat je al weet hoe we verdergaan? De PID gelijk stellen aan de TIDs zou in de XOR berekening '0x00000000' als resultaat opleveren, en dat is precies wat we willen op dit moment. Voor iedereen onder jullie die niets anders begrijpen dan een vergelijking:

((PID XOR TIDs) >> 0x10) XOR ((PID XOR TIDs) AND 0xFFFF) < 0x8

Dat is het. En als we de Trainer IDs gebruiken voor de PID, krijgen we het volgende:

((TIDs XOR TIDs) >> 0x10) XOR ((TIDs XOR TIDs) AND 0xFFFF) < 8
(0x0 >> 0x10) XOR (0x0 AND 0xFFFF) < 0x8
0 XOR 0 < 0x8
0 < 8

Jup. We hebben dat probleem opgelost. Cool hé? Laten we nu echt iets gaan doen! Open je ROM in je emulator (VBA), loop naar een grasveld, maar ga er nog niet in! Zoek (Ctrl+C) nu in de RAM naar de Wilde-Pokémon-data. Ik stel voor om te zoeken naar een waarde gelijk aan nul (Dus VOOR je in het gras stapt, na het laden van je save), daarna zoek je (32-Bit waarde) naar de Trainer IDs.
Als je niet weet wat je TIDs is en je weet niet hoe je hem moet zoeken, selecteer dan het stuk hieronder.

Je zal je TIDs vinden rond of op '0x02024030'. Op dit moment ben ik te lui om de DMA-Bescherming te controleren.
Ok, open je ROM in VBA-SDL-H (Of eventueel een andere debugger, maar de volgende stappen zijn enkel voor VBA-SDL-H).
We proberen te achterhalen wanneer of waar de TIDs' 32-Bit-value wordt geschreven naar de RAM. Dus, typ eerst:

mb 02024010

in de console en kijk naar de data. Het is nog niet veranderd, in mijn geval dan toch, het tweede 'word' van de data bevind zich nog steeds op '0x02024030'. Nu plaats ik een 32-Bit (4-Byte) breakpoint on write op de plaats van de data.

bpw 02024030 4

Typ 'c' in de console en 'speel' verder. Wanneer zou de TIDs opnieuw worden berekend? Misschien wanneer we een Pokémon tegenkomen! Laten we het proberen. Stap in het gras en ... Het spel stopt! Kijk:

Breakpoint (on write) address 02024030 old:00 new:00
R00=02024030 R04=0202402c R08=00000001 R12=00000001
R01=00000004 R05=02024220 R09=00000000 R13=03007d64
R02=0202402c R06=00000017 R10=00000000 R14=0803d99f
R03=00000000 R07=083c8d50 R11=00000000 R15=0803d98a
CPSR=0000003f (......T Mode: 1f)
0803d988 3101 add r1, #0x1

Damn! De huidige actie schreef alleen de byte '0x00' naar '0x02024030'. We zijn niet geïnteresseerd in het patchen van een stomme nop-actie, dus typ 'c' om verder te gaan. Het spel stopt weer!

Breakpoint (on write) address 02024031 old:00 new:00
R00=02024031 R04=0202402c R08=00000001 R12=00000001
R01=00000005 R05=02024220 R09=00000000 R13=03007d64
R02=0202402c R06=00000017 R10=00000000 R14=0803d99f
R03=00000000 R07=083c8d50 R11=00000000 R15=0803d98a
CPSR=0000003f (......T Mode: 1f)
0803d988 3101 add r1, #0x1

Er zijn slechts 2 registers veranderd. Een vier is een vijf geworden en het adres in R0 is met één toegenomen. We 'zitten' nu eigenlijk in een loop die de oude Pokémon-data wist. Dus, natuurlijk zullen we nog enkele breaks hebben vooraleer we vinden wat we zoeken.
Typ opnieuw 'c' om verder te gaan, het spel stopt weer, typ nog eens 'c', ... Je kan bestuderen wat er veranderd of alles gewoon negeren. Typ nog enkele keren 'c' tot:

Breakpoint (on write) address 02024030 old:00000000 new:65a9dc70
R00=bc000000 R04=03007cf8 R08=00000000 R12=00000008
R01=65a9dc70 R05=00000000 R09=00000000 R13=03007cc0
R02=03007cf8 R06=00000000 R10=00000001 R14=0803db9d
R03=0202402c R07=0202402c R11=00000000 R15=080406dc
CPSR=8000003f (N.....T Mode: 1f)
080406da e1fe b $08040ada

JOEPIE! Dat is de routine die we zoeken! Het 'word' @ '0x02024030' is net overschreven met '0x65a9dc70', mijn Trainer IDs.
Nu moeten we dit beter gaan bekijken. Dissamble het door dit te typen:

d 080406c2

Waarom? Het is gewoon een schatting van de lengte van de procedure. Ongeveer '0x080406DA' zei de debugger. Waarschijnlijk snap je er niets van... Daarom krijg je alles mooi uitgespeld. De subroutine is de operand tussen de twee codes, beginnend met 'b $'. Dit staat voor een sprong. (b $offset/locatie). Dit is de sub:

080406c4  7821 ldrb r1, [r4, #0x0]   // Laadt de byte @ R4 (=03007cf8) in R1
080406c6  7860 ldrb r0, [r4, #0x1]   // Laadt de byte @ R4+1 (=03007cf9) in R0
080406c8  0200 lsl r0, r0, #0x08   // De Byte in R0 wordt opgeschoven naar links met 1 Byte (=8-Bit)
080406ca  1809 add r1, r1, r0   // R1 en R0 worden samengevoegd. We krijgen een halfword R0+R1
080406cc  78a0 ldrb r0, [r4, #0x2]   // Laadt de byte @ R4+2 (=03007cfa) in R0
080406ce  0400 lsl r0, r0, #0x10   // R0 wordt opnieuw opgeschoven met 2 Bytes
080406d0  1809 add r1, r1, r0   // R0 wordt samengevoegd met R1
080406d2  78e0 ldrb r0, [r4, #0x3]   // Laadt de byte @ R4+3 (=03007cfb) in R0
080406d4  0600 lsl r0, r0, #0x18   // R0 wordt opgeschoven met 3 Bytes
080406d6  1809 add r1, r1, r0   // En samengevoegd met R1. (=Algoritme dat de TID & STID samenevoegd)
080406d8  6079 str r1, [r7, #0x4]   // En tot slot worden de TIDs opgeslagen in R7+4 (=0202402c)
------------------------------------- (De routine die ons spel deed stoppen)
080406da  e1fe b $08040ada   // Stopt de routine en gaat naar een andere locatie

Laten we onze theorie testen. Als we er in slagen dit:

080406d6  1809 add r1, r1, r0   // Voeg R1 samen met R0

Te vervangen in iets zoals dit:

080406d6  xxxx str r1, [r7, #0x0]   // Overschrijf de PID met de TIDs

Dan zou de PID altijd gelijk zijn aan de TIDs.
De thumb-operand voor str r1, [r7, #0x0] is '0x6039'. Vraag me niet hoe je dat berekent, het is genoeg als je weet wat het doet.
(Als je het echt wilt weten, zoek dan eens naar GBATEK van Martin Korth en kijk naar 'store word'.)
Laten we onze ROM eens 'on-the-fly' patchen. Typ het volgende in de debugger:

eh 080406d6 6039

Daarna delete je even dat breakpoint-on-write:

bpwc

Typ 'c' en laat het gevecht beginnen! Maar.. het is geen shiny?! Oooh neee.. ik dacht dat het gelukt was, toch?
Natuurlijk is het gelukt! Het is geen shiny omdat de CPU de key-locatie al heeft doorgestuurd als we patchten. Hoe lossen we dit op?
Simpel. Loop gewoon wat rond in het gras, de volgende Pokémon die je tegenkomt is een shiny!
Omdat we nu de offset en de waarde weten die we moeten patchen kunnen we snel een Action Replay code maken (AR V3):

00000000 18XXXXXX
0000YYYY 00000000

Eum... 'XXXXXX' staat voor '((address AND 0x3FFFFF) >> 1)' en 'YYYY' is de waarde die we gaan patchen. '0x18' verteld de AR het eerste ROM-patching-slot te gebruiken (z0C).

00000000 1802036B
00006039 00000000

Encrypt de code met ARCrypt en je eigen (werkende) Altijd-Shiny-code is klaar!

A74320F4 175B5B22
18452A7D DDE55BCC

Einde van deel 1!
Als je enkel geïnteresseerd bent in het maken van een Shiny-code moet je niet verder lezen, je hebt ze al!
Maar, als je geïnteresseerd bent om deze code in een ROM-Hack te stoppen, dan hebben nog een beetje werk!
Nu zullen we de goede, oude Shiny Gyarados toevoegen. Klaar? Start!


We beginnen (gaan verder?) met het aanpassen/verplaatsen van de.. eum.. 'Shiny-Routine (?)'. Waarom moeten we dat doen? Wel, we willen een soort flag-systeem gaan toevoegen, en daardoor wordt onze routine natuurlijk groter. Als het groter wordt zou andere data worden overschreven en dat willen we natuurlijk niet. Daarom gaan we het deels verplaatsen. Wat we willen is het volgende:

080406c4  b402 push {r1} // Sla de waarde in R1 op (door de push)
080406c6  4902 ldr r1, [$080406d0] (=$0871b701) // Laadt het adres van de nieuwe routine
080406c8  f000 bl $080406cc // Spring met bl om het terugkeer-adres op te slaan in lr
080406ca  f800 blh $0000 // Behoort tot bl
080406cc  470f bx r1 // Wisselt de waarden van pc en R1
080406ce  0000 // Niet uitgevoerd door CPU
080406d0  b701 // Data1 (Nodig @ '0x080406c6')
080406d2  0871 // Data2
080406d4  0000 lsl r0, r0, #0x0 // NOP, doet niets, onnodige bytes
080406d6  0000 lsl r0, r0, #0x0 // NOP, doet niets, onnodige bytes
080406d8  0000 lsl r0, r0, #0x0 // NOP, doet niets, onnodige bytes
080406da  e1fe b $08040ada // Zet normale program-flow voort

Open je ROM in een HEX-Editor en ga naar '0x406C0'. Daar vind je de 'Shiny-Routine' die we gaan patchen.

000406c0h: 39 60 0A E2 21 78 60 78 00 02 09 18 A0 78 00 04 ; 9`.!x`x.... x..
000406d0h: 09 18 E0 78 00 06 09 18 79 60 FE E1 00 22 3B 1C ; ..x....y`.";.

Overscrhijf deze met onze nieuwe routine:

000406c0h: 39 60 0A E2 02 B4 02 49 00 F0 00 F8 0F 47 00 00 ; 9`...I...G..
000406d0h: 01 B7 71 08 00 00 00 00 00 00 FE E1 00 22 3B 1C ; .q........";.

Op deze manier leest de CPU altijd eerst onze nieuwe Shiny-Routine. (Je kan dit testen door een gevect te starten, de ROM zal crashen omdat er nog geen data is om te lezen)

Laten we onze nieuwe/aangepaste 'Shiny-Routine' plaatsen @ '0x0871B700'. Waarom '0x00' in plaats van '0x01'? Wel, de laatste bit van een branch-operation (Waar je 'springt' enzo) verklaart of de routine in ARM of THUMB mode is geschreven. 0 is ARM en 1 staat voor een THUMB-Routine. Wij gebruiken een THUMB-Routine, dus het adres eindigt op 1, zelfs als het start @ '0x0871B700'.

0871b700  bc02 pop {r1} // Krijg de opgeslagen waarde
0871b702  b500 push {lr} // Sla het gekregen adres op
0871b704  bc02 pop {r1} // Herlaadt het in R1
0871b706  3108 add r1, #4 // Pas het gekregen adres aan met 8
-----------------------------(Om de data van het doel-adres over te slaan.)
0871b708  b402 push {r1} // Sla het gekregen adres terug op
0871b70a  490e ldr r1, [$0871b744] (=$02022000) // Aww.. Hier wordt het weer overschreven!
--------------------------------------------------(Beetje stom, ik weet het)
0871b70c  6809 ldr r1, [r1, #0x0] // Laadt de waarde @ '0x02022000' in R1
0871b70e  2901 cmp r1, #0x1
0871b710  d00b beq $0871b72a
0871b712  7821 ldrb r1, [r4, #0x0]   // Laadt de byte @ R4 (=03007cf8) in R1
0871b714  7860 ldrb r0, [r4, #0x1]   // Laadt de byte @ R4+1 (=03007cf9) in R0
0871b716  0200 lsl r0, r0, #0x08   // De byte in R0 wordt opgeschoven naar links met 1 byte
0871b718  1809 add r1, r1, r0   // R1 en R0 Worden samengevoegd. We krijgen een halfword R0+R1
0871b71a  78a0 ldrb r0, [r4, #0x2]   // Laadt de byte @ R4+2 (=03007cfa) in R0
0871b71c  0400 lsl r0, r0, #0x10   // R0 wordt opgeschoven naar links met 2 bytes
0871b71e  1809 add r1, r1, r0   // R0 wordt samengevoegd met R1
0871b720  78e0 ldrb r0, [r4, #0x3]   // Laadt de byte @ R4+3 (=03007cfb) in R0
0871b722  0600 lsl r0, r0, #0x18   // R0 wordt met 3 bytes naar links opgeschoven
0871b724  1809 add r1, r1, r0   // en bij R1 gevoegd
0871b726  6079 str r1, [r7, #0x4]   // De TIDs worden opgeslagen @ R7+4 (=0202402c)
0871b728  e00b b $0871b742 // Sla de volgende lijnen maar over, ze zijn enkel voor shinies
0871b72a  7821 ldrb r1, [r4, #0x0]   // Laadt de byte @ R4 (=03007cf8) in R1
0871b72c  7860 ldrb r0, [r4, #0x1]   // Laadt de byte @ R4+1 (=03007cf9) in R0
0871b72e  0200 lsl r0, r0, #0x08   // De byte in R0 wordt opgeschoven naar links met 1 byte
0871b730  1809 add r1, r1, r0   // R1 en R0 Worden samengevoegd. We krijgen een halfword R0+R1
0871b732  78a0 ldrb r0, [r4, #0x2]   // Laadt de byte @ R4+2 (=03007cfa) in R0
0871b734  0400 lsl r0, r0, #0x10   // R0 wordt opnieuw naar links opgeschoven met 2 bytes
0871b736  1809 add r1, r1, r0   // R1 en R0 Worden samengevoegd.
0871b738  78e0 ldrb r0, [r4, #0x3]   // Laadt de byte @ R4+3 (=03007cfb) in R0
0871b73a  0600 lsl r0, r0, #0x18   // R0 wordt met 3 bytes naar links opgeschoven
0871b73c  1809 add r1, r1, r0   // En samengevoegd met R1
0871b73e  6079 str r1, [r7, #0x4]   // Tot slot worden de TIDs opgeslagen @ R7+4 (=0202402c)
0871b740  6039 str r1, [r7, #0x0]   // En hier is onze patch. De PID wordt overschreven met de TIDs
0871b742  bd00 pop {pc} // Keer terug en sluit
0871b744  0020 // Data1
0871b746  0202 // Data2

Mooi zo. Misschien heb je gemerkt dat ik hier de commando's niet heb vervangen zoals bij de AR-Code? Waarom? Omdat het overschrijven van de operator @ '0x0871B73C' de eerste byte van de TIDs zou beïnvloeden. Bij de AR-Code zijn we enkel geïnteresseerd in het shiny-effect en schenken geen aandacht aan de TIDs. (Dus, cheaters, je gehackte shinies kunnen steeds achterhaald worden omdat de TIDs niet overeenkomen!) Rechtstreeks in de ROM scrhijven (wat we zodadelijk gaan doen) geeft ons de kans om dat 'foutje' op te lossen. We moeten geen operator verwijderen, we voegen gewoon een lijntje toe. Hoe krijg je al die berekende waarden in plaats van die operators? Wel, er zijn twee oplossingen: Ikzelf heb deze functie rechtstreeks in ASM geschreven, maar jij kan bvb. een compiler gebruiken (Bvb. DevKit Pro's ARM-Eabi). Dus, we gaan terug naar onze HEX-Editor en gaan naar onze gewenste locatie (In dit voorbeeld '0x71B700') Voeg daar onderstaande code in:

0071b700h: 02 BC 00 B5 02 BC 08 31 02 B4 0E 49 09 68 01 29 ; ....1..I.h.)
0071b710h: 0B D0 21 78 60 78 00 02 09 18 A0 78 00 04 09 18 ; .!x`x.... x....
0071b720h: E0 78 00 06 09 18 79 60 0B E0 21 78 60 78 00 02 ; x....y`.!x`x..
0071b730h: 09 18 A0 78 00 04 09 18 E0 78 00 06 09 18 79 60 ; .. x....x....y`
0071b740h: 39 60 00 BD 00 20 02 02 FF FF FF FF FF FF FF FF ; 9`.. ..

Dat kan al tellen! Laten we even ons resultaat testen. Open je ROM in VBA, stap in het gras en.. een normale Pokémon! Wat is er nu weer fout gegaan?! Helemaal niets! Je moet natuurlijk de flag nog activeren! Open de MemoryViewer (Die vind je onder Tools) en ga naar '0x02022000'. Je ziet een 0, verander deze naar een 1 en zoek opnieuw een Pokémon. JAAA! Een Shiny! Gelukt!
Goed. Nu moeten we een manier bedenken om deze flag te (de)activeren tijden eens (normaal) script. Daarvoor gebruiken we '0x23' wat zoveel betekend als 'callasm'. Maar, wat moeten we precies oproepen, wel.. dit:

0871b760  b507 push {r0, r1, r2, lr} // Sla het gekregen adres op
0871b762  4803 ldr r0, [$0871b770] (=$02022000) // Krijg de locatie van de flag
0871b764  6801 ldr r1, [r0, #0x0] // Krijg de waarde van de flag
0871b766  2201 mov r2, #0x1
0871b768  4051 eor r1, r2 // Wissel de waarde van de flag
0871b76a  6001 str r1, [r0, #0x0] // Sla de nieuwe waarde op
0871b76c  bd07 pop {r0, r1, r2, pc} // Krijg de waarde, ga terug
0871b76e  0000 // NOP
0871b770  0020 // Data1
0871b772  0202 // Data2

Dus, wanneer we deze routine oproepen activeert of deactiveerd het de 'Shiny-Flag'. Plaats dit in je ROM:

0071b770h: 07 B5 03 48 01 68 01 22 51 40 01 60 07 BD 00 00 ; ..H.h."Q@.`...
0071b780h: 00 20 02 02 FF FF FF FF FF FF FF FF FF FF FF FF ; . ..

Joepie! We zijn klaar met het ASM-stuk. Het laatste stukje is hoogstwaarschijnlijk het makkelijkste. We moeten een scriptje schrijven waar: de flag wordt geactiveerd, het gevecht start en de flag terug wordt gedeactiveerd. Het script ziet er zo uit:

[23]        //CallASM
[XXXXXXXX]  //Pointer naar THUMB-sub+1
[00]        //NOP
[B6]        //Wildbattle
[XXXX]      //Pokémon-NR (HEX)
[XX]        //Level (HEX)
[XXXX]      //Item (HEX)
[00]        //NOP
[25]        //Special
[3801]      //Start gevecht!
[28]        //Wait
[0101]      //Een seconde
[23]        //CallASM
[XXXXXXXX]  //Pointer naar THUMB-sub+1
[02]        //End

Als we nu een Shiny Gyarados van level 30, zonder item willen krijgen we:

0071b7a0h: 23 71 B7 71 08 00 B6 82 00 1E 00 00 00 25 38 01 ; #qq..‚.....%8.
0071b7b0h: 28 01 01 23 71 B7 71 08 02 FF FF FF FF FF FF FF ; (..#qq..

We zijn klaar. Plaats de offset van je script (van hierboven) ergens op een event in AdvanceMap en test je resultaat!



"Pokmon" ist ein eingetragenes Warenzeichen der Firma Nintendo
"Action Replay" ist ein eingetragenes Warenzeichen von Datel Interact.
© www.SearchForCheats.de.vu by Mastermind_X
© 2006 - 2008