diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2017-08-18 13:49:00 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2017-08-18 13:49:00 -0400 |
commit | 08dfb0aa80668b80c4a31bd064f5f2d729e5b7f6 (patch) | |
tree | b46baa9353c0919efaed4349d2207958fa0bfaf5 /gba/source | |
parent | a19507262602fa31f9f14a9e4f4e03e375bca111 (diff) | |
download | gen3uploader-08dfb0aa80668b80c4a31bd064f5f2d729e5b7f6.tar.gz gen3uploader-08dfb0aa80668b80c4a31bd064f5f2d729e5b7f6.tar.bz2 gen3uploader-08dfb0aa80668b80c4a31bd064f5f2d729e5b7f6.zip |
Started working on serializing pokemon data
The GBA program now sends serialized data about the first pokemon in the player's party over to the Wii. This data doesn't yet include all of the information that we will eventually want. It does, however, not transfer any private data, specifically IVs, EVs, and the personality value. It does this by deriving the public information (stats, nature, gender, shiny) before sending the pokemon over. Because of this, lookup tables for things such as base stats were needed, and given that these are large tables, it was easier to use the tables already existent in the game's ROM. Thus, the addresses of the three lookup tables that are now used are necessary for each ROM that this tool supports. I derived the addresses for version 1 of English Pokemon LeafGreen by dumping my own copy and searching through it with a text editor. Thus, at the current time, that cartridge is the only one that is supported. I will supplement this soon with addresses for the other four gen 3 carts that I have, but that will still not provide a very large amount of coverage. I have not yet decided how to address this issue. There is one current bug with the serialized data: the Wii doesn't seem to see the original trainer ID. Will fix.
Diffstat (limited to 'gba/source')
-rw-r--r-- | gba/source/gamedata.c | 167 | ||||
-rw-r--r-- | gba/source/gamedata.h | 191 | ||||
-rw-r--r-- | gba/source/main.c | 86 | ||||
-rw-r--r-- | gba/source/savestructs.h | 7 | ||||
-rw-r--r-- | gba/source/serialize.c | 227 | ||||
-rw-r--r-- | gba/source/serialize.h | 19 |
6 files changed, 655 insertions, 42 deletions
diff --git a/gba/source/gamedata.c b/gba/source/gamedata.c index bbcf4dd..ae593b1 100644 --- a/gba/source/gamedata.c +++ b/gba/source/gamedata.c | |||
@@ -7,7 +7,7 @@ | |||
7 | */ | 7 | */ |
8 | #include "gamedata.h" | 8 | #include "gamedata.h" |
9 | 9 | ||
10 | void decryptSaveStructures( | 10 | static void decryptSaveStructures( |
11 | pSaveBlock1 SaveBlock1, | 11 | pSaveBlock1 SaveBlock1, |
12 | pSaveBlock2 SaveBlock2, | 12 | pSaveBlock2 SaveBlock2, |
13 | pSaveBlock3 SaveBlock3) | 13 | pSaveBlock3 SaveBlock3) |
@@ -119,10 +119,15 @@ void decryptSaveStructures( | |||
119 | *xor_key_ptr = 0; | 119 | *xor_key_ptr = 0; |
120 | } | 120 | } |
121 | 121 | ||
122 | bool initSaveData( | 122 | static void CryptBoxPokemon(struct BoxPokemon* pkm) |
123 | pSaveBlock1* SaveBlock1, | 123 | { |
124 | pSaveBlock2* SaveBlock2, | 124 | for (u32 i = 0; i < 12; i++) |
125 | pSaveBlock3* SaveBlock3) | 125 | { |
126 | pkm->secure.raw[i] ^= (pkm->otId ^ pkm->personality); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | bool initSaveData(struct GameData* gameData) | ||
126 | { | 131 | { |
127 | // check the ROM code, make sure this game is supported. | 132 | // check the ROM code, make sure this game is supported. |
128 | u8* ROM = (u8*) 0x8000000; | 133 | u8* ROM = (u8*) 0x8000000; |
@@ -135,6 +140,9 @@ bool initSaveData( | |||
135 | pSaveBlock1 gSaveBlock1; | 140 | pSaveBlock1 gSaveBlock1; |
136 | pSaveBlock2 gSaveBlock2; | 141 | pSaveBlock2 gSaveBlock2; |
137 | pSaveBlock3 gSaveBlock3; | 142 | pSaveBlock3 gSaveBlock3; |
143 | struct BaseStats* gBaseStats; | ||
144 | const u32 (*gExpTables)[101]; | ||
145 | const u16* gNatOrder; | ||
138 | //u32 titlemid = 0; | 146 | //u32 titlemid = 0; |
139 | 147 | ||
140 | // get the address of the save loading function. | 148 | // get the address of the save loading function. |
@@ -337,6 +345,9 @@ bool initSaveData( | |||
337 | //mainloop = (void(*)()) 0x8000429; | 345 | //mainloop = (void(*)()) 0x8000429; |
338 | //titlemid = 0x807928f; | 346 | //titlemid = 0x807928f; |
339 | //load_pokemon = (void(*)()) 0x804c245; | 347 | //load_pokemon = (void(*)()) 0x804c245; |
348 | gBaseStats = (struct BaseStats*) ( GAME_FR ? 0 : 0x82547d0 ); | ||
349 | gExpTables = (ExperienceTables) ( GAME_FR ? 0 : 0x8253b30 ); | ||
350 | gNatOrder = (const u16*) ( GAME_FR ? 0 : 0x825203a ); | ||
340 | 351 | ||
341 | break; | 352 | break; |
342 | } | 353 | } |
@@ -504,9 +515,149 @@ bool initSaveData( | |||
504 | // applicable. | 515 | // applicable. |
505 | decryptSaveStructures(gSaveBlock1,gSaveBlock2,gSaveBlock3); | 516 | decryptSaveStructures(gSaveBlock1,gSaveBlock2,gSaveBlock3); |
506 | 517 | ||
507 | *SaveBlock1 = gSaveBlock1; | 518 | gameData->SaveBlock1 = gSaveBlock1; |
508 | *SaveBlock2 = gSaveBlock2; | 519 | gameData->SaveBlock2 = gSaveBlock2; |
509 | *SaveBlock3 = gSaveBlock3; | 520 | gameData->SaveBlock3 = gSaveBlock3; |
521 | gameData->baseStats = gBaseStats; | ||
522 | gameData->expTables = gExpTables; | ||
523 | gameData->natOrder = gNatOrder; | ||
510 | 524 | ||
511 | return true; | 525 | return true; |
512 | } | 526 | } |
527 | |||
528 | void DecryptPokemon(struct Pokemon* pkm) | ||
529 | { | ||
530 | struct BoxPokemon* boxMon = &(pkm->box); | ||
531 | |||
532 | CryptBoxPokemon(boxMon); | ||
533 | } | ||
534 | |||
535 | void DecryptBoxPokemon(struct BoxPokemon* pkm) | ||
536 | { | ||
537 | CryptBoxPokemon(pkm); | ||
538 | } | ||
539 | |||
540 | void EncryptPokemon(struct Pokemon* pkm) | ||
541 | { | ||
542 | struct BoxPokemon* boxMon = &(pkm->box); | ||
543 | |||
544 | EncryptBoxPokemon(boxMon); | ||
545 | } | ||
546 | |||
547 | void EncryptBoxPokemon(struct BoxPokemon* pkm) | ||
548 | { | ||
549 | FixBoxPokemonChecksum(pkm); | ||
550 | CryptBoxPokemon(pkm); | ||
551 | } | ||
552 | |||
553 | union PokemonSubstruct* GetPokemonSubstruct(struct Pokemon* pkm,u8 substructId) | ||
554 | { | ||
555 | struct BoxPokemon* boxMon = &(pkm->box); | ||
556 | |||
557 | return GetBoxPokemonSubstruct(boxMon,substructId); | ||
558 | } | ||
559 | |||
560 | union PokemonSubstruct* GetBoxPokemonSubstruct( | ||
561 | struct BoxPokemon* pkm, | ||
562 | u8 substructId) | ||
563 | { | ||
564 | if (substructId > 3) | ||
565 | { | ||
566 | return NULL; | ||
567 | } | ||
568 | |||
569 | u32 personality = pkm->personality; | ||
570 | u32 modulo = (personality % 24); | ||
571 | |||
572 | // Staring at the substruct indexes, I noticed the last two columns are the | ||
573 | // reverse of the first two! that is, substructId==2 column is the reverse of | ||
574 | // substructId==1, substructId==3 is the reverse of substructId==0. | ||
575 | // At least that means there's no need to hardcode all four. | ||
576 | u8 substruct_idxes[2][24] = { | ||
577 | { 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3 }, | ||
578 | { 1, 1, 2, 3, 2, 3, 0, 0, 0, 0, 0, 0, 2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2 } | ||
579 | }; | ||
580 | |||
581 | if (substructId < 2) | ||
582 | { | ||
583 | return &(pkm->secure.substructs[substruct_idxes[substructId][modulo]]); | ||
584 | } | ||
585 | |||
586 | return &(pkm->secure.substructs[ | ||
587 | substruct_idxes[3 - substructId][23 - modulo]]); | ||
588 | } | ||
589 | |||
590 | u16 CalculateBoxPokemonChecksum(struct BoxPokemon* pkm) | ||
591 | { | ||
592 | u16 checksum = 0; | ||
593 | |||
594 | union PokemonSubstruct* substructs[4] = { | ||
595 | GetBoxPokemonSubstruct(pkm,0), | ||
596 | GetBoxPokemonSubstruct(pkm,1), | ||
597 | GetBoxPokemonSubstruct(pkm,2), | ||
598 | GetBoxPokemonSubstruct(pkm,3) | ||
599 | }; | ||
600 | |||
601 | for (int substruct = 0; substruct < 4; substruct++) | ||
602 | { | ||
603 | for (int i = 0; i < 6; i++) | ||
604 | { | ||
605 | checksum += substructs[substruct]->raw[i]; | ||
606 | } | ||
607 | } | ||
608 | |||
609 | return checksum; | ||
610 | } | ||
611 | |||
612 | void FixBoxPokemonChecksum(struct BoxPokemon* pkm) | ||
613 | { | ||
614 | pkm->checksum = CalculateBoxPokemonChecksum(pkm); | ||
615 | } | ||
616 | |||
617 | struct PokemonSubstruct0* GetPokemonSubstruct0(struct Pokemon* pkm) | ||
618 | { | ||
619 | struct BoxPokemon* boxMon = &(pkm->box); | ||
620 | |||
621 | return GetBoxPokemonSubstruct0(boxMon); | ||
622 | } | ||
623 | |||
624 | struct PokemonSubstruct0* GetBoxPokemonSubstruct0(struct BoxPokemon* pkm) | ||
625 | { | ||
626 | return &(GetBoxPokemonSubstruct(pkm,0)->type0); | ||
627 | } | ||
628 | |||
629 | struct PokemonSubstruct1* GetPokemonSubstruct1(struct Pokemon* pkm) | ||
630 | { | ||
631 | struct BoxPokemon* boxMon = &(pkm->box); | ||
632 | |||
633 | return GetBoxPokemonSubstruct1(boxMon); | ||
634 | } | ||
635 | |||
636 | struct PokemonSubstruct1* GetBoxPokemonSubstruct1(struct BoxPokemon* pkm) | ||
637 | { | ||
638 | return &(GetBoxPokemonSubstruct(pkm,1)->type1); | ||
639 | } | ||
640 | |||
641 | struct PokemonSubstruct2* GetPokemonSubstruct2(struct Pokemon* pkm) | ||
642 | { | ||
643 | struct BoxPokemon* boxMon = &(pkm->box); | ||
644 | |||
645 | return GetBoxPokemonSubstruct2(boxMon); | ||
646 | } | ||
647 | |||
648 | struct PokemonSubstruct2* GetBoxPokemonSubstruct2(struct BoxPokemon* pkm) | ||
649 | { | ||
650 | return &(GetBoxPokemonSubstruct(pkm,2)->type2); | ||
651 | } | ||
652 | |||
653 | struct PokemonSubstruct3* GetPokemonSubstruct3(struct Pokemon* pkm) | ||
654 | { | ||
655 | struct BoxPokemon* boxMon = &(pkm->box); | ||
656 | |||
657 | return GetBoxPokemonSubstruct3(boxMon); | ||
658 | } | ||
659 | |||
660 | struct PokemonSubstruct3* GetBoxPokemonSubstruct3(struct BoxPokemon* pkm) | ||
661 | { | ||
662 | return &(GetBoxPokemonSubstruct(pkm,3)->type3); | ||
663 | } | ||
diff --git a/gba/source/gamedata.h b/gba/source/gamedata.h index 4d0a2a9..247626a 100644 --- a/gba/source/gamedata.h +++ b/gba/source/gamedata.h | |||
@@ -21,9 +21,192 @@ | |||
21 | 21 | ||
22 | #define LANG_JAPAN ((*(u8*)(0x80000AF)) == 'J') | 22 | #define LANG_JAPAN ((*(u8*)(0x80000AF)) == 'J') |
23 | 23 | ||
24 | bool initSaveData( | 24 | typedef const u32 (*ExperienceTables)[101]; |
25 | pSaveBlock1* SaveBlock1, | 25 | |
26 | pSaveBlock2* SaveBlock2, | 26 | struct GameData { |
27 | pSaveBlock3* SaveBlock3); | 27 | pSaveBlock1 SaveBlock1; |
28 | pSaveBlock2 SaveBlock2; | ||
29 | pSaveBlock3 SaveBlock3; | ||
30 | struct BaseStats* baseStats; | ||
31 | ExperienceTables expTables; | ||
32 | const u16* natOrder; | ||
33 | }; | ||
34 | |||
35 | bool initSaveData(struct GameData* gameData); | ||
36 | |||
37 | /** | ||
38 | * Decrypts the substructures of a Pokémon structure, so they can be viewed or | ||
39 | * modified easily. | ||
40 | * | ||
41 | * Remember to call EncryptPokemon() afterwards. | ||
42 | * | ||
43 | * @param pkm The Pokémon to decrypt the substructures of. | ||
44 | */ | ||
45 | void DecryptPokemon(struct Pokemon* pkm); | ||
46 | |||
47 | /** | ||
48 | * Decrypts the substructures of a core Pokémon structure, so they can be viewed | ||
49 | * or modified easily. | ||
50 | * | ||
51 | * Used by DecryptPokemon(). | ||
52 | * | ||
53 | * Remember to call EncryptPokemon() afterwards. | ||
54 | * | ||
55 | * @param pkm The BoxPokemon to decrypt the substructures of. | ||
56 | */ | ||
57 | void DecryptBoxPokemon(struct BoxPokemon* pkm); | ||
58 | |||
59 | /** | ||
60 | * Encrypts the substructures of a Pokémon structure, and fixes the checksum. | ||
61 | * | ||
62 | * Must be used after DecryptPokemon() has been called, otherwise the Pokémon | ||
63 | * you decrypted and forgot to re-encrypt will become a Bad Egg. | ||
64 | * | ||
65 | * @param pkm The Pokémon to encrypt the substructures and fix | ||
66 | * the checksum of. | ||
67 | */ | ||
68 | void EncryptPokemon(struct Pokemon* pkm); | ||
69 | |||
70 | /** | ||
71 | * Encrypts the substructures of a core Pokémon structure, and fixes the | ||
72 | * checksum. | ||
73 | * | ||
74 | * Must be used after DecryptBoxPokemon() has been called, otherwise the Pokémon | ||
75 | * you decrypted and forgot to re-encrypt will become a Bad Egg. | ||
76 | * | ||
77 | * @param pkm The BoxPokemon to encrypt the substructures and fix the checksum | ||
78 | * of. | ||
79 | */ | ||
80 | void EncryptBoxPokemon(struct BoxPokemon* pkm); | ||
81 | |||
82 | /** | ||
83 | * Gets a substructure of a Pokémon structure. | ||
84 | * | ||
85 | * Call DecryptPokemon() first or the substructure data will be encrypted. | ||
86 | * | ||
87 | * @param pkm The Pokemon to get a substructure of. | ||
88 | * @param substructId The substructure to get. | ||
89 | * | ||
90 | * @return The substructure. | ||
91 | */ | ||
92 | union PokemonSubstruct* GetPokemonSubstruct(struct Pokemon* pkm,u8 substructId); | ||
93 | |||
94 | /** | ||
95 | * Gets a substructure of a core Pokémon structure. | ||
96 | * | ||
97 | * Call DecryptBoxPokemon() first or the substructure data will be encrypted. | ||
98 | * | ||
99 | * @param pkm The Pokemon to get a substructure of. | ||
100 | * @param substructId The substructure to get. | ||
101 | * | ||
102 | * @return The substructure. | ||
103 | */ | ||
104 | union PokemonSubstruct* GetBoxPokemonSubstruct( | ||
105 | struct BoxPokemon* pkm, | ||
106 | u8 substructId); | ||
107 | |||
108 | /** | ||
109 | * Gets the checksum of a core Pokémon structure. | ||
110 | * | ||
111 | * @param pkm The BoxPokemon to calculate the checksum of. | ||
112 | * | ||
113 | * @return The checksum. | ||
114 | */ | ||
115 | u16 CalculateBoxPokemonChecksum(struct BoxPokemon* pkm); | ||
116 | |||
117 | /** | ||
118 | * Fixes the checksum of a core Pokémon structure. | ||
119 | * | ||
120 | * @param pkm The BoxPokemon to fix the checksum of. | ||
121 | */ | ||
122 | void FixBoxPokemonChecksum(struct BoxPokemon* pkm); | ||
123 | |||
124 | /** | ||
125 | * Gets the zeroth substructure ("Growth") of a Pokémon structure. | ||
126 | * | ||
127 | * Call DecryptPokemon() first or the substructure data will be encrypted. | ||
128 | * | ||
129 | * @param pkm The Pokémon to get a substructure of. | ||
130 | * | ||
131 | * @return The substructure. | ||
132 | */ | ||
133 | struct PokemonSubstruct0* GetPokemonSubstruct0(struct Pokemon* pkm); | ||
134 | |||
135 | /** | ||
136 | * Gets the zeroth substructure ("Growth") of a core Pokémon structure. | ||
137 | * | ||
138 | * Call DecryptBoxPokemon() first or the substructure data will be encrypted. | ||
139 | * | ||
140 | * @param pkm The BoxPokemon to get the substructure of. | ||
141 | * | ||
142 | * @return The substructure. | ||
143 | */ | ||
144 | struct PokemonSubstruct0* GetBoxPokemonSubstruct0(struct BoxPokemon* pkm); | ||
145 | |||
146 | /** | ||
147 | * Gets the first substructure ("Attacks") of a Pokémon structure. | ||
148 | * | ||
149 | * Call DecryptPokemon() first or the substructure data will be encrypted. | ||
150 | * | ||
151 | * @param pkm The Pokémon to get a substructure of. | ||
152 | * | ||
153 | * @return The substructure. | ||
154 | */ | ||
155 | struct PokemonSubstruct1* GetPokemonSubstruct1(struct Pokemon* pkm); | ||
156 | |||
157 | /** | ||
158 | * Gets the first substructure ("Attacks") of a core Pokémon structure. | ||
159 | * | ||
160 | * Call DecryptBoxPokemon() first or the substructure data will be encrypted. | ||
161 | * | ||
162 | * @param pkm The BoxPokemon to get the substructure of. | ||
163 | * | ||
164 | * @return The substructure. | ||
165 | */ | ||
166 | struct PokemonSubstruct1* GetBoxPokemonSubstruct1(struct BoxPokemon* pkm); | ||
167 | |||
168 | /** | ||
169 | * Gets the second substructure ("EVs & Condition") of a Pokémon structure. | ||
170 | * | ||
171 | * Call DecryptPokemon() first or the substructure data will be encrypted. | ||
172 | * | ||
173 | * @param pkm The Pokémon to get a substructure of. | ||
174 | * | ||
175 | * @return The substructure. | ||
176 | */ | ||
177 | struct PokemonSubstruct2* GetPokemonSubstruct2(struct Pokemon* pkm); | ||
178 | |||
179 | /** | ||
180 | * Gets the second substructure ("EVs & Condition") of a core Pokémon structure. | ||
181 | * | ||
182 | * Call DecryptBoxPokemon() first or the substructure data will be encrypted. | ||
183 | * | ||
184 | * @param pkm The BoxPokemon to get the substructure of. | ||
185 | * | ||
186 | * @return The substructure. | ||
187 | */ | ||
188 | struct PokemonSubstruct2* GetBoxPokemonSubstruct2(struct BoxPokemon* pkm); | ||
189 | |||
190 | /** | ||
191 | * Gets the third substructure ("Miscellaneous") of a Pokémon structure. | ||
192 | * | ||
193 | * Call DecryptPokemon() first or the substructure data will be encrypted. | ||
194 | * | ||
195 | * @param pkm The Pokémon to get a substructure of. | ||
196 | * | ||
197 | * @return The substructure. | ||
198 | */ | ||
199 | struct PokemonSubstruct3* GetPokemonSubstruct3(struct Pokemon* pkm); | ||
200 | |||
201 | /** | ||
202 | * Gets the third substructure ("Miscellaneous") of a core Pokémon structure. | ||
203 | * | ||
204 | * Call DecryptBoxPokemon() first or the substructure data will be encrypted. | ||
205 | * | ||
206 | * @param pkm The BoxPokemon to get the substructure of. | ||
207 | * | ||
208 | * @return The substructure. | ||
209 | */ | ||
210 | struct PokemonSubstruct3* GetBoxPokemonSubstruct3(struct BoxPokemon* pkm); | ||
28 | 211 | ||
29 | #endif | 212 | #endif |
diff --git a/gba/source/main.c b/gba/source/main.c index 14d2f1d..aeb05af 100644 --- a/gba/source/main.c +++ b/gba/source/main.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <gba.h> | 7 | #include <gba.h> |
8 | #include "gamedata.h" | 8 | #include "gamedata.h" |
9 | #include "link.h" | 9 | #include "link.h" |
10 | #include "serialize.h" | ||
10 | 11 | ||
11 | int main(void) | 12 | int main(void) |
12 | { | 13 | { |
@@ -42,11 +43,9 @@ int main(void) | |||
42 | waitForAck(); | 43 | waitForAck(); |
43 | 44 | ||
44 | // Get access to save data. | 45 | // Get access to save data. |
45 | pSaveBlock1 SaveBlock1; | 46 | struct GameData gameData; |
46 | pSaveBlock2 SaveBlock2; | ||
47 | pSaveBlock3 SaveBlock3; | ||
48 | 47 | ||
49 | if (!initSaveData(&SaveBlock1, &SaveBlock2, &SaveBlock3)) | 48 | if (!initSaveData(&gameData)) |
50 | { | 49 | { |
51 | // Unsupported game version. | 50 | // Unsupported game version. |
52 | sendS32(-1); | 51 | sendS32(-1); |
@@ -63,13 +62,13 @@ int main(void) | |||
63 | 62 | ||
64 | if (GAME_RS) | 63 | if (GAME_RS) |
65 | { | 64 | { |
66 | trainerName = SaveBlock2->rs.playerName; | 65 | trainerName = gameData.SaveBlock2->rs.playerName; |
67 | } else if (GAME_FRLG) | 66 | } else if (GAME_FRLG) |
68 | { | 67 | { |
69 | trainerName = SaveBlock2->frlg.playerName; | 68 | trainerName = gameData.SaveBlock2->frlg.playerName; |
70 | } else if (GAME_EM) | 69 | } else if (GAME_EM) |
71 | { | 70 | { |
72 | trainerName = SaveBlock2->e.playerName; | 71 | trainerName = gameData.SaveBlock2->e.playerName; |
73 | } | 72 | } |
74 | 73 | ||
75 | u32 tn1 = | 74 | u32 tn1 = |
@@ -94,20 +93,24 @@ int main(void) | |||
94 | u8* trainerId = 0; | 93 | u8* trainerId = 0; |
95 | if (GAME_RS) | 94 | if (GAME_RS) |
96 | { | 95 | { |
97 | trainerId = SaveBlock2->rs.playerTrainerId; | 96 | trainerId = gameData.SaveBlock2->rs.playerTrainerId; |
98 | } else if (GAME_FRLG) | 97 | } else if (GAME_FRLG) |
99 | { | 98 | { |
100 | trainerId = SaveBlock2->frlg.playerTrainerId; | 99 | trainerId = gameData.SaveBlock2->frlg.playerTrainerId; |
101 | } else if (GAME_EM) | 100 | } else if (GAME_EM) |
102 | { | 101 | { |
103 | trainerId = SaveBlock2->e.playerTrainerId; | 102 | trainerId = gameData.SaveBlock2->e.playerTrainerId; |
104 | } | 103 | } |
105 | 104 | ||
106 | u32 tti = | 105 | u16 trainerIdNum = |
107 | (trainerId[1] << 8) | 106 | (trainerId[1] << 8) |
108 | | (trainerId[0]); | 107 | | (trainerId[0]); |
109 | 108 | ||
110 | sendU32(tti); | 109 | u16 secretIdNum = |
110 | (trainerId[3] << 8) | ||
111 | | (trainerId[2]); | ||
112 | |||
113 | sendU32(trainerIdNum); | ||
111 | waitForAck(); | 114 | waitForAck(); |
112 | 115 | ||
113 | // Does the player want to import this game? | 116 | // Does the player want to import this game? |
@@ -120,22 +123,22 @@ int main(void) | |||
120 | u8* pokedexSeen = 0; | 123 | u8* pokedexSeen = 0; |
121 | if (GAME_RS) | 124 | if (GAME_RS) |
122 | { | 125 | { |
123 | pokedexSeen = SaveBlock2->rs.pokedex.seen; | 126 | pokedexSeen = gameData.SaveBlock2->rs.pokedex.seen; |
124 | } else if (GAME_FRLG) | 127 | } else if (GAME_FRLG) |
125 | { | 128 | { |
126 | pokedexSeen = SaveBlock2->frlg.pokedex.seen; | 129 | pokedexSeen = gameData.SaveBlock2->frlg.pokedex.seen; |
127 | } else if (GAME_EM) | 130 | } else if (GAME_EM) |
128 | { | 131 | { |
129 | pokedexSeen = SaveBlock2->e.pokedex.seen; | 132 | pokedexSeen = gameData.SaveBlock2->e.pokedex.seen; |
130 | } | 133 | } |
131 | 134 | ||
132 | for (int i=0; i<13; i++) | 135 | for (int i=0; i<13; i++) |
133 | { | 136 | { |
134 | u32 psi = | 137 | u32 psi = |
135 | (pokedexSeen[i*4]) | 138 | (pokedexSeen[i*4] << 24) |
136 | | (pokedexSeen[i*4+1] << 8) | 139 | | (pokedexSeen[i*4+1] << 16) |
137 | | (pokedexSeen[i*4+2] << 16) | 140 | | (pokedexSeen[i*4+2] << 8) |
138 | | (pokedexSeen[i*4+3] << 24); | 141 | | (pokedexSeen[i*4+3]); |
139 | 142 | ||
140 | directSendU32(psi); | 143 | directSendU32(psi); |
141 | } | 144 | } |
@@ -143,23 +146,54 @@ int main(void) | |||
143 | u8* pokedexCaught = 0; | 146 | u8* pokedexCaught = 0; |
144 | if (GAME_RS) | 147 | if (GAME_RS) |
145 | { | 148 | { |
146 | pokedexCaught = SaveBlock2->rs.pokedex.owned; | 149 | pokedexCaught = gameData.SaveBlock2->rs.pokedex.owned; |
147 | } else if (GAME_FRLG) | 150 | } else if (GAME_FRLG) |
148 | { | 151 | { |
149 | pokedexCaught = SaveBlock2->frlg.pokedex.owned; | 152 | pokedexCaught = gameData.SaveBlock2->frlg.pokedex.owned; |
150 | } else if (GAME_EM) | 153 | } else if (GAME_EM) |
151 | { | 154 | { |
152 | pokedexCaught = SaveBlock2->e.pokedex.owned; | 155 | pokedexCaught = gameData.SaveBlock2->e.pokedex.owned; |
153 | } | 156 | } |
154 | 157 | ||
155 | for (int i=0; i<13; i++) | 158 | for (int i=0; i<13; i++) |
156 | { | 159 | { |
157 | u32 psi = | 160 | u32 psi = |
158 | (pokedexCaught[i*4]) | 161 | (pokedexCaught[i*4] << 24) |
159 | | (pokedexCaught[i*4+1] << 8) | 162 | | (pokedexCaught[i*4+1] << 16) |
160 | | (pokedexCaught[i*4+2] << 16) | 163 | | (pokedexCaught[i*4+2] << 8) |
161 | | (pokedexCaught[i*4+3] << 24); | 164 | | (pokedexCaught[i*4+3]); |
162 | 165 | ||
163 | directSendU32(psi); | 166 | directSendU32(psi); |
164 | } | 167 | } |
168 | |||
169 | // Start sending over party pokémon. | ||
170 | struct Pokemon* playerParty = 0; | ||
171 | if (GAME_RS) | ||
172 | { | ||
173 | playerParty = gameData.SaveBlock1->rs.playerParty; | ||
174 | } else if (GAME_FRLG) | ||
175 | { | ||
176 | playerParty = gameData.SaveBlock1->frlg.playerParty; | ||
177 | } else if (GAME_EM) | ||
178 | { | ||
179 | playerParty = gameData.SaveBlock1->e.playerParty; | ||
180 | } | ||
181 | |||
182 | waitForResponse(); | ||
183 | |||
184 | u32 partyCount = 1; | ||
185 | |||
186 | sendU32(partyCount); | ||
187 | waitForAck(); | ||
188 | |||
189 | for (int pki=0; pki<partyCount; pki++) | ||
190 | { | ||
191 | struct Pokemon* pkm = (playerParty + pki); | ||
192 | struct BoxPokemon* bpkm = &(pkm->box); | ||
193 | |||
194 | struct PokemonIntermediate pki; | ||
195 | |||
196 | PokemonIntermediateInit(&pki, bpkm, trainerIdNum, secretIdNum, &gameData); | ||
197 | PokemonIntermediateStream(&pki); | ||
198 | } | ||
165 | } | 199 | } |
diff --git a/gba/source/savestructs.h b/gba/source/savestructs.h index 2bf4d4d..fb8ef36 100644 --- a/gba/source/savestructs.h +++ b/gba/source/savestructs.h | |||
@@ -9,9 +9,7 @@ | |||
9 | 9 | ||
10 | // Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity | 10 | // Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity |
11 | 11 | ||
12 | #define POKEMON_NAME_LENGTH 10 | 12 | #include "pokemon.h" |
13 | #define OT_NAME_LENGTH 7 | ||
14 | #define TILE_SIZE_4BPP 32 | ||
15 | 13 | ||
16 | struct Coords16 | 14 | struct Coords16 |
17 | { | 15 | { |
@@ -495,6 +493,7 @@ struct BaseStats | |||
495 | /* 0x17 */ u8 ability2; | 493 | /* 0x17 */ u8 ability2; |
496 | /* 0x18 */ u8 safariZoneFleeRate; | 494 | /* 0x18 */ u8 safariZoneFleeRate; |
497 | /* 0x19 */ u8 bodyColor; | 495 | /* 0x19 */ u8 bodyColor; |
496 | u16 filler; | ||
498 | }; | 497 | }; |
499 | 498 | ||
500 | struct BattleMove | 499 | struct BattleMove |
@@ -790,4 +789,4 @@ struct Pokedex | |||
790 | /*0x0C*/ u32 unknown3; | 789 | /*0x0C*/ u32 unknown3; |
791 | /*0x10*/ u8 owned[52]; | 790 | /*0x10*/ u8 owned[52]; |
792 | /*0x44*/ u8 seen[52]; | 791 | /*0x44*/ u8 seen[52]; |
793 | }; \ No newline at end of file | 792 | }; |
diff --git a/gba/source/serialize.c b/gba/source/serialize.c new file mode 100644 index 0000000..6303772 --- /dev/null +++ b/gba/source/serialize.c | |||
@@ -0,0 +1,227 @@ | |||
1 | #include "serialize.h" | ||
2 | #include "gamedata.h" | ||
3 | #include "link.h" | ||
4 | |||
5 | #define UNOWN_SPECIES_INDEX 201 | ||
6 | #define SHEDINJA_SPECIES_INDEX 303 | ||
7 | |||
8 | enum Stat { | ||
9 | StatAttack, | ||
10 | StatDefense, | ||
11 | StatSpeed, | ||
12 | StatSpAttack, | ||
13 | StatSpDefense | ||
14 | }; | ||
15 | |||
16 | u32 CalculateStat( | ||
17 | u8 base, | ||
18 | u32 iv, | ||
19 | u8 ev, | ||
20 | u8 level, | ||
21 | u8 statIndex, | ||
22 | u8 nature) | ||
23 | { | ||
24 | u32 n = (((2 * base + iv + ev / 4) * level) / 100) + 5; | ||
25 | |||
26 | u8 naturePlus = (nature / 5); | ||
27 | u8 natureMinus = (nature % 5); | ||
28 | |||
29 | if (naturePlus != natureMinus) | ||
30 | { | ||
31 | if (statIndex == naturePlus) | ||
32 | { | ||
33 | return (u32)(n * 110) / 100; | ||
34 | } else if (statIndex == natureMinus) | ||
35 | { | ||
36 | return (u32)(n * 90) / 100; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | return n; | ||
41 | } | ||
42 | |||
43 | void PokemonIntermediateInit( | ||
44 | struct PokemonIntermediate* pki, | ||
45 | struct BoxPokemon* bpkm, | ||
46 | u16 trainerId, | ||
47 | u16 secretId, | ||
48 | const struct GameData* gameData) | ||
49 | { | ||
50 | DecryptBoxPokemon(bpkm); | ||
51 | |||
52 | struct PokemonSubstruct0* sub0 = GetBoxPokemonSubstruct0(bpkm); | ||
53 | struct PokemonSubstruct1* sub1 = GetBoxPokemonSubstruct1(bpkm); | ||
54 | struct PokemonSubstruct2* sub2 = GetBoxPokemonSubstruct2(bpkm); | ||
55 | struct PokemonSubstruct3* sub3 = GetBoxPokemonSubstruct3(bpkm); | ||
56 | |||
57 | struct BaseStats* baseStats = &gameData->baseStats[sub0->species]; | ||
58 | |||
59 | for (int i=0; i<POKEMON_NAME_LENGTH; i++) | ||
60 | { | ||
61 | pki->nickname[i] = bpkm->nickname[i]; | ||
62 | } | ||
63 | |||
64 | for (int i=0; i<OT_NAME_LENGTH; i++) | ||
65 | { | ||
66 | pki->otName[i] = bpkm->otName[i]; | ||
67 | } | ||
68 | |||
69 | pki->otId = bpkm->otId; | ||
70 | pki->otGender = sub3->otGender; | ||
71 | pki->species = gameData->natOrder[sub0->species - 1]; | ||
72 | pki->heldItem = sub0->heldItem; | ||
73 | pki->experience = sub0->experience; | ||
74 | |||
75 | pki->moves[0] = sub1->moves[0]; | ||
76 | pki->moves[1] = sub1->moves[1]; | ||
77 | pki->moves[2] = sub1->moves[2]; | ||
78 | pki->moves[3] = sub1->moves[3]; | ||
79 | |||
80 | pki->ppBonuses = sub0->ppBonuses; | ||
81 | pki->metLevel = sub3->metLevel; | ||
82 | pki->metLocation = sub3->metLocation; | ||
83 | pki->pokeball = sub3->pokeball; | ||
84 | pki->altAbility = sub3->altAbility; | ||
85 | |||
86 | // Derive nature from the personality value. | ||
87 | pki->nature = (bpkm->personality % 25); | ||
88 | |||
89 | // Derive gender from the personality value. | ||
90 | int genderThreshold = baseStats->genderRatio; | ||
91 | |||
92 | if ((genderThreshold == 0) || (genderThreshold == 255)) | ||
93 | { | ||
94 | pki->gender = 0; | ||
95 | } else if (genderThreshold == 254) | ||
96 | { | ||
97 | pki->gender = 1; | ||
98 | } else { | ||
99 | u8 genderDeterminer = bpkm->personality & 0x000000FF; | ||
100 | |||
101 | if (genderDeterminer >= genderThreshold) | ||
102 | { | ||
103 | pki->gender = 0; | ||
104 | } else { | ||
105 | pki->gender = 1; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | // Determine shininess from the personality value. | ||
110 | u16 shinyDeterminer = | ||
111 | (trainerId) | ||
112 | ^ (secretId) | ||
113 | ^ ((bpkm->personality >> 16) & 0x0000FFFF) | ||
114 | ^ (bpkm->personality & 0x0000FFFF); | ||
115 | |||
116 | if (shinyDeterminer < 8) | ||
117 | { | ||
118 | pki->shiny = 1; | ||
119 | } else { | ||
120 | pki->shiny = 0; | ||
121 | } | ||
122 | |||
123 | // Determine Unown letter from the personality value. | ||
124 | if (sub0->species == UNOWN_SPECIES_INDEX) | ||
125 | { | ||
126 | u8 unownDeterminer = | ||
127 | ((bpkm->personality & 0x07000000) >> 18) | ||
128 | | ((bpkm->personality & 0x00070000) >> 12) | ||
129 | | ((bpkm->personality & 0x00000700) >> 6) | ||
130 | | (bpkm->personality & 0x00000007); | ||
131 | |||
132 | pki->unownLetter = (unownDeterminer % 28); | ||
133 | } | ||
134 | |||
135 | // Calculate level from experience. | ||
136 | pki->level = 1; | ||
137 | |||
138 | const u32* expTable = gameData->expTables[baseStats->growthRate]; | ||
139 | while ((pki->level <= 100) && (expTable[pki->level] <= sub0->experience)) | ||
140 | { | ||
141 | pki->level++; | ||
142 | } | ||
143 | |||
144 | pki->level--; | ||
145 | |||
146 | // Calculate stats. | ||
147 | if (sub0->species == SHEDINJA_SPECIES_INDEX) | ||
148 | { | ||
149 | pki->hp = 1; | ||
150 | } else { | ||
151 | u32 n = 2 * baseStats->baseHP + sub3->hpIV; | ||
152 | pki->hp = (((n + sub2->hpEV / 4) * pki->level) / 100) + pki->level + 10; | ||
153 | } | ||
154 | |||
155 | pki->attack = CalculateStat( | ||
156 | baseStats->baseAttack, | ||
157 | sub3->attackIV, | ||
158 | sub2->attackEV, | ||
159 | pki->level, | ||
160 | 0, | ||
161 | pki->nature); | ||
162 | |||
163 | pki->defense = CalculateStat( | ||
164 | baseStats->baseDefense, | ||
165 | sub3->defenseIV, | ||
166 | sub2->defenseEV, | ||
167 | pki->level, | ||
168 | 1, | ||
169 | pki->nature); | ||
170 | |||
171 | pki->speed = CalculateStat( | ||
172 | baseStats->baseSpeed, | ||
173 | sub3->speedIV, | ||
174 | sub2->speedEV, | ||
175 | pki->level, | ||
176 | 2, | ||
177 | pki->nature); | ||
178 | |||
179 | pki->spAttack = CalculateStat( | ||
180 | baseStats->baseSpAttack, | ||
181 | sub3->spAttackIV, | ||
182 | sub2->spAttackEV, | ||
183 | pki->level, | ||
184 | 3, | ||
185 | pki->nature); | ||
186 | |||
187 | pki->spDefense = CalculateStat( | ||
188 | baseStats->baseSpDefense, | ||
189 | sub3->spDefenseIV, | ||
190 | sub2->spDefenseEV, | ||
191 | pki->level, | ||
192 | 4, | ||
193 | pki->nature); | ||
194 | |||
195 | // Approximate the values of the contest conditions. | ||
196 | pki->cool = ((u16)(sub2->cool) * 10) / 255; | ||
197 | pki->beauty = ((u16)(sub2->beauty) * 10) / 255; | ||
198 | pki->cute = ((u16)(sub2->cute) * 10) / 255; | ||
199 | pki->smart = ((u16)(sub2->smart) * 10) / 255; | ||
200 | pki->tough = ((u16)(sub2->tough) * 10) / 255; | ||
201 | pki->sheen = ((u16)(sub2->sheen) * 10) / 255; | ||
202 | |||
203 | // Determine Pokerus status. | ||
204 | if (sub3->pokerus & 0xF0) | ||
205 | { | ||
206 | if (sub3->pokerus & 0x0F) | ||
207 | { | ||
208 | pki->pokerus = 1; | ||
209 | } else { | ||
210 | pki->pokerus = 2; | ||
211 | } | ||
212 | } else { | ||
213 | pki->pokerus = 0; | ||
214 | } | ||
215 | |||
216 | EncryptBoxPokemon(bpkm); | ||
217 | } | ||
218 | |||
219 | void PokemonIntermediateStream(struct PokemonIntermediate* pki) | ||
220 | { | ||
221 | u32* raw = (u32*)pki; | ||
222 | |||
223 | for (int i=0; i<(sizeof(struct PokemonIntermediate)/4); i++) | ||
224 | { | ||
225 | directSendU32(raw[i]); | ||
226 | } | ||
227 | } | ||
diff --git a/gba/source/serialize.h b/gba/source/serialize.h new file mode 100644 index 0000000..21049c5 --- /dev/null +++ b/gba/source/serialize.h | |||
@@ -0,0 +1,19 @@ | |||
1 | #ifndef POKEMON_H_67C60AC1 | ||
2 | #define POKEMON_H_67C60AC1 | ||
3 | |||
4 | #include <gba.h> | ||
5 | |||
6 | struct BoxPokemon; | ||
7 | struct PokemonIntermediate; | ||
8 | struct GameData; | ||
9 | |||
10 | void PokemonIntermediateInit( | ||
11 | struct PokemonIntermediate* pki, | ||
12 | struct BoxPokemon* bpkm, | ||
13 | u16 trainerId, | ||
14 | u16 secretId, | ||
15 | const struct GameData* gameData); | ||
16 | |||
17 | void PokemonIntermediateStream(struct PokemonIntermediate* pki); | ||
18 | |||
19 | #endif /* end of include guard: POKEMON_H_67C60AC1 */ | ||