Skip to content

Instantly share code, notes, and snippets.

@Bl4ckSh4rk
Last active January 8, 2022 16:50
Show Gist options
  • Save Bl4ckSh4rk/aef61826049a1e8ee25f38822a4a7811 to your computer and use it in GitHub Desktop.
Save Bl4ckSh4rk/aef61826049a1e8ee25f38822a4a7811 to your computer and use it in GitHub Desktop.
Tool to read out some data about the roaming Pokemon in the third generation games
#include <stdio.h>
#include <stdlib.h>
void exitTool()
{
printf("\n\nPress ENTER to exit...");
while(1)
{
if (getchar())
break;
}
}
char* species_list[] =
{
"Invalid",
"Bulbasaur",
"Ivysaur",
"Venusaur",
"Charmander",
"Charmeleon",
"Charizard",
"Squirtle",
"Wartortle",
"Blastoise",
"Caterpie",
"Metapod",
"Butterfree",
"Weedle",
"Kakuna",
"Beedrill",
"Pidgey",
"Pidgeotto",
"Pidgeot",
"Rattata",
"Raticate",
"Spearow",
"Fearow",
"Ekans",
"Arbok",
"Pikachu",
"Raichu",
"Sandshrew",
"Sandslash",
"Nidoran M",
"Nidorina",
"Nidoqueen",
"Nidoran F",
"Nidorino",
"Nidoking",
"Clefairy",
"Clefable",
"Vulpix",
"Ninetales",
"Jigglypuff",
"Wigglytuff",
"Zubat",
"Golbat",
"Oddish",
"Gloom",
"Vileplume",
"Paras",
"Parasect",
"Venonat",
"Venomoth",
"Diglett",
"Dugtrio",
"Meowth",
"Persian",
"Psyduck",
"Golduck",
"Mankey",
"Primeape",
"Growlithe",
"Arcanine",
"Poliwag",
"Poliwhirl",
"Poliwrath",
"Abra",
"Kadabra",
"Alakazam",
"Machop",
"Machoke",
"Machamp",
"Bellsprout",
"Weepinbell",
"Victreebel",
"Tentacool",
"Tentacruel",
"Geodude",
"Graveler",
"Golem",
"Ponyta",
"Rapidash",
"Slowpoke",
"Slowbro",
"Magnemite",
"Magneton",
"Farfetch'd",
"Doduo",
"Dodrio",
"Seel",
"Dewgong",
"Grimer",
"Muk",
"Shellder",
"Cloyster",
"Gastly",
"Haunter",
"Gengar",
"Onix",
"Drowzee",
"Hypno",
"Krabby",
"Kingler",
"Voltorb",
"Electrode",
"Exeggcute",
"Exeggutor",
"Cubone",
"Marowak",
"Hitmonlee",
"Hitmonchan",
"Lickitung",
"Koffing",
"Weezing",
"Rhyhorn",
"Rhydon",
"Chansey",
"Tangela",
"Kangaskhan",
"Horsea",
"Seadra",
"Goldeen",
"Seaking",
"Staryu",
"Starmie",
"Mr. Mime",
"Scyther",
"Jynx",
"Electabuzz",
"Magmar",
"Pinsir",
"Tauros",
"Magikarp",
"Gyarados",
"Lapras",
"Ditto",
"Eevee",
"Vaporeon",
"Jolteon",
"Flareon",
"Porygon",
"Omanyte",
"Omastar",
"Kabuto",
"Kabutops",
"Aerodactyl",
"Snorlax",
"Articuno",
"Zapdos",
"Moltres",
"Dratini",
"Dragonair",
"Dragonite",
"Mewtwo",
"Mew",
"Chikorita",
"Bayleef",
"Meganium",
"Cyndaquil",
"Quilava",
"Typhlosion",
"Totodile",
"Croconaw",
"Feraligatr",
"Sentret",
"Furret",
"Hoothoot",
"Noctowl",
"Ledyba",
"Ledian",
"Spinarak",
"Ariados",
"Crobat",
"Chinchou",
"Lanturn",
"Pichu",
"Cleffa",
"Igglybuff",
"Togepi",
"Togetic",
"Natu",
"Xatu",
"Mareep",
"Flaaffy",
"Ampharos",
"Bellossom",
"Marill",
"Azumarill",
"Sudowoodo",
"Politoed",
"Hoppip",
"Skiploom",
"Jumpluff",
"Aipom",
"Sunkern",
"Sunflora",
"Yanma",
"Wooper",
"Quagsire",
"Espeon",
"Umbreon",
"Murkrow",
"Slowking",
"Misdreavus",
"Unown",
"Wobbuffet",
"Girafarig",
"Pineco",
"Forretress",
"Dunsparce",
"Gligar",
"Steelix",
"Snubbull",
"Granbull",
"Qwilfish",
"Scizor",
"Shuckle",
"Heracross",
"Sneasel",
"Teddiursa",
"Ursaring",
"Slugma",
"Magcargo",
"Swinub",
"Piloswine",
"Corsola",
"Remoraid",
"Octillery",
"Delibird",
"Mantine",
"Skarmory",
"Houndour",
"Houndoom",
"Kingdra",
"Phanpy",
"Donphan",
"Porygon2",
"Stantler",
"Smeargle",
"Tyrogue",
"Hitmontop",
"Smoochum",
"Elekid",
"Magby",
"Miltank",
"Blissey",
"Raikou",
"Entei",
"Suicune",
"Larvitar",
"Pupitar",
"Tyranitar",
"Lugia",
"Ho-Oh",
"Celebi",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Invalid",
"Treecko",
"Grovyle",
"Sceptile",
"Torchic",
"Combusken",
"Blaziken",
"Mudkip",
"Marshtomp",
"Swampert",
"Poochyena",
"Mightyena",
"Zigzagoon",
"Linoone",
"Wurmple",
"Silcoon",
"Beautifly",
"Cascoon",
"Dustox",
"Lotad",
"Lombre",
"Ludicolo",
"Seedot",
"Nuzleaf",
"Shiftry",
"Nincada",
"Ninjask",
"Shedinja",
"Taillow",
"Swellow",
"Shroomish",
"Breloom",
"Spinda",
"Wingull",
"Pelipper",
"Surskit",
"Masquerain",
"Wailmer",
"Wailord",
"Skitty",
"Delcatty",
"Kecleon",
"Baltoy",
"Claydol",
"Nosepass",
"Torkoal",
"Sableye",
"Barboach",
"Whiscash",
"Luvdisc",
"Corphish",
"Crawdaunt",
"Feebas",
"Milotic",
"Carvanha",
"Sharpedo",
"Trapinch",
"Vibrava",
"Flygon",
"Makuhita",
"Hariyama",
"Electrike",
"Manectric",
"Numel",
"Camerupt",
"Spheal",
"Sealeo",
"Walrein",
"Cacnea",
"Cacturne",
"Snorunt",
"Glalie",
"Lunatone",
"Solrock",
"Azurill",
"Spoink",
"Grumpig",
"Plusle",
"Minun",
"Mawile",
"Meditite",
"Medicham",
"Swablu",
"Altaria",
"Wynaut",
"Duskull",
"Dusclops",
"Roselia",
"Slakoth",
"Vigoroth",
"Slaking",
"Gulpin",
"Swalot",
"Tropius",
"Whismur",
"Loudred",
"Exploud",
"Clamperl",
"Huntail",
"Gorebyss",
"Absol",
"Shuppet",
"Banette",
"Seviper",
"Zangoose",
"Relicanth",
"Aron",
"Lairon",
"Aggron",
"Castform",
"Volbeat",
"Illumise",
"Lileep",
"Cradily",
"Anorith",
"Armaldo",
"Ralts",
"Kirlia",
"Gardevoir",
"Bagon",
"Shelgon",
"Salamence",
"Beldum",
"Metang",
"Metagross",
"Regirock",
"Regice",
"Registeel",
"Kyogre",
"Groudon",
"Rayquaza",
"Latias",
"Latios",
"Jirachi",
"Deoxys",
"Chimecho"
};
int getCurrentSave(char *sav)
{
unsigned long savIndex1, savIndex2;
savIndex1 =
(sav[0xFFF] << 24 & 0xFFFFFFFF) +
(sav[0xFFE] << 16 & 0xFFFFFF) +
(sav[0xFFD] << 8 & 0xFFFF) +
(sav[0xFFC] & 0xFF);
savIndex2 =
(sav[0xEFFF] << 24 & 0xFFFFFFFF) +
(sav[0xEFFE] << 16 & 0xFFFFFF) +
(sav[0xEFFD] << 8 & 0xFFFF) +
(sav[0xEFFC] & 0xFF);
if (savIndex1 < savIndex2)
return 0xE000;
else
return 0;
}
int getSectionOffset(char* data, int section)
{
int currentOffset = getCurrentSave(data);
int x;
for(x = 0; x < 14 && currentOffset < 0x1C000; x++)
{
if(data[(currentOffset + 0xFF4)] == section)
{
return currentOffset;
}
currentOffset += 0x1000;
}
return -1; // section not found
}
int main(int argc, char** argv)
{
FILE *fp_in;
char *data;
unsigned int fSize = 0;
if(argc != 2)
{
printf("Error: Please drop your save file onto the exe\n");
exitTool();
return 0;
}
fp_in = fopen(argv[1], "rb");
if (!fp_in)
{
printf("Error: Can not read the save file\n");
exitTool();
return 0;
}
fseek (fp_in, 0, SEEK_END);
fSize = ftell(fp_in);
if(fSize != 0x20000)
{
printf("Error: Invalid save size\n");
exitTool();
return 0;
}
rewind (fp_in);
data = (char *)malloc(fSize);
if(data == NULL)
{
printf("Error: Memory allocation failed\n");
fclose(fp_in);
exitTool();
return 0;
}
fread(data, 1, fSize, fp_in);
fclose(fp_in);
int section0Offset = getSectionOffset(data, 0);
if(section0Offset == -1)
{
printf("Error: Save section 0 not found\n");
exitTool();
return 0;
}
int section4Offset = getSectionOffset(data, 4);
if(section4Offset == -1)
{
printf("Error: Save section 4 not found\n");
exitTool();
return 0;
}
unsigned int gameCode = (data[section0Offset + 0xAC] & 0xFF) + ((data[section0Offset + 0xAD] << 8) & 0xFFFF) + ((data[section0Offset + 0xAE] << 16) & 0xFFFFFF) + ((data[section0Offset + 0xAF] << 24) & 0xFFFFFFFF);
int roamerOffset;
if(gameCode == 0) // Ruby/Sapphire
roamerOffset = section4Offset + 0x2C4;
if(gameCode == 1) // FireRed/LeafGreen
roamerOffset = section4Offset + 0x250;
else // Emerald
roamerOffset = section4Offset + 0x35C;
unsigned int roamerSpeciesID = (data[roamerOffset + 8] & 0xFF) + ((data[roamerOffset + 9] << 8) & 0xFFFF);
if(roamerSpeciesID < 1 || roamerSpeciesID > 411 || (roamerSpeciesID > 251 && roamerSpeciesID < 277))
{
printf("Error: Invalid roamer species\n");
exitTool();
return 0;
}
unsigned int roamerIVs = (data[roamerOffset] & 0xFF) + ((data[roamerOffset + 1] << 8) & 0xFFFF) + ((data[roamerOffset + 2] << 16) & 0xFFFFFF) + ((data[roamerOffset + 3] << 24) & 0xFFFFFFFF);
// due to a bug only 8 bits are read, so max IVs are actually 31/7/0/0/0/0
int hpIVbug = roamerIVs & 0x1F;
int atkIVbug = (roamerIVs >> 5) & 0x7;
// without the bug the Pokemon would have these IVs
int hpIV = roamerIVs & 0x1F;
int atkIV = (roamerIVs >> 5) & 0x1F;
int defIV = (roamerIVs >> 10) & 0x1F;
int initIV = (roamerIVs >> 15) & 0x1F;
int satkIV = (roamerIVs >> 20) & 0x1F;
int sdefIV = (roamerIVs >> 25) & 0x1F;
unsigned int roamerPID = (data[roamerOffset + 4] & 0xFF) + ((data[roamerOffset + 5] << 8) & 0xFFFF) + ((data[roamerOffset + 6] << 16) & 0xFFFFFF) + ((data[roamerOffset + 7] << 24) & 0xFFFFFFFF);
int tidOffset = section0Offset + 0xA;
int id = (data[tidOffset] & 0xFF) + (data[tidOffset + 1] << 8 & 0xFF);
int sid = (data[tidOffset + 2] & 0xFF) + (data[tidOffset + 3] << 8 & 0xFF);
int shinyVal = (roamerPID >> 16 & 0xFFFF) ^ (roamerPID & 0xFFFF) ^ id ^ sid;
char* shiny;
if(shinyVal < 8)
shiny = "True";
else
shiny = "False";
printf("Pokemon: %s\n", species_list[roamerSpeciesID]);
printf("PID: 0x%X (%u)\n", roamerPID, roamerPID);
printf("IVs: %d/%d/0/0/0/0 (without bug: %d/%d/%d/%d/%d/%d)\n", hpIVbug, atkIVbug, hpIV, atkIV, defIV, satkIV, sdefIV, initIV);
printf("Shiny: %s\n", shiny);
free(data);
exitTool();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment