#include #include #include #include "main.h" #include #include #include #include int main() { nio_console csl; lcd_incolor(); clrscr(); // 54, 30 would be the normal max values, but this way a graphic bug is fixed and i do \n manually anyway nio_InitConsole(&csl, 55, 31, 0, 0, NIO_COLOR_BLACK, NIO_COLOR_WHITE); nio_DrawConsole(&csl); nio_drawing_enabled(&csl,TRUE); nio_cursor_type(&csl, NIO_CURSOR_UNDERSCORE); drawLogo(&csl, 3000); struct TradeOption *options; options = (struct TradeOption *)malloc(sizeof(struct TradeOption)); for(int c = 0; c < 6; ++c) { options->trades[0][c] = -1; options->trades[1][c] = -1; } options->saved = 0; short stop = 0; while(stop == 0) { int choice = displayMenu(&csl); switch(choice){ case 48: // ASCII Code for 0 -> Exit stop = 1; break; case 49: // ASCII Code for 1 -> Start trading if(options->saved == 0) { nio_printf(&csl, "\n\n[!]Please choose the Pokemons you\nwant to trade first!"); wait_key_pressed(); break; } TradePokemon(&csl, options); break; case 50: // ASCII Code for 2 -> Choose Trading Options ChoosePokemon(options); options->saved = 1; break; case 51: // ASCII Code for 3 -> Fix Savestates FixSaves(&csl); break; case 52: // ASCII Code for 4 -> Display Help & Disclaimer nio_clear(&csl); nio_printf(&csl, HELPSTRING); wait_key_pressed(); break; default: // exit if invalid choice break; } } drawBye(&csl, 3000); free(options); clrscr(); lcd_incolor(); return 0; } // Displays the Menu string and returns the choice int displayMenu(nio_console *csl) { clrscr(); nio_clear(csl); char choice[1]; nio_printf(csl, HEADLINE); // Print the Headline nio_printf(csl, MENUSTRING); // Print the options nio_printf(csl, "> "); // wait for input nio_fgets(choice,3,csl); // store input in choice[] return (int)choice[0]; // return choice } // Fill struct TradeOption options with values int ChoosePokemon(struct TradeOption *options) { clrscr(); char * title = ""; // Displays Number of the trade #%d char * subtitle = "Cancel to save your choice."; char * msg1 = "Trade your Pokemon #"; char * msg2 = "with Partner's Pokemon #"; int valid = 0; int value1 = 1; int value2 = 1; char popup_result[256]; for(int c = 0; c < 6; ++c) { valid = 0; while(valid == 0) { valid = 1; sprintf (title, "Choose Trade %d/6", c+1); // return 0; if pressed cancel to save the struct if (show_2numeric_input(title, subtitle, msg1, &value1, 1, 6, msg2, &value2, 1, 6) != 1) return 0; // check for double trades for(int i = 0; i < 6; ++i) { if(options->trades[0][i] == value1) { show_msgbox("Notice", "You can't trade a Pokemon more than once. Pick different ones!"); valid = 0; continue; } if(options->trades[1][i] == value2) { show_msgbox("Notice", "You can't trade a Pokemon more than once. Pick different ones!"); valid = 0; continue; } } } options->trades[0][c] = value2-1; options->trades[1][c] = value1-1; sprintf(popup_result, "Your Pokemon #%d will be traded \nwith your partner's Pokemon #%d", value1, value2); show_msgbox("Information", popup_result); } show_msgbox("Saved", "Successfully saved your choices!"); clrscr(); return 0; } // Trade Pokemon using a TradeOption int TradePokemon(nio_console *csl, struct TradeOption *options) { nio_clear(csl); //~ nio_printf(csl, "Please be patient, \nthis may take some minutes!\n"); nio_printf(csl, "Press any key start ...\n"); wait_key_pressed(); //~ nio_printf(csl, ">> Setting up Trade Machine 1.0\n"); unsigned char *buffer; // buffer of the first savefile buffer=malloc(FILESIZE*sizeof(unsigned char)); memset( buffer, 0x0, FILESIZE); unsigned char *buffer2; // buffer of the second savefile buffer2=malloc(FILESIZE*sizeof(unsigned char)); memset( buffer2, 0x0, FILESIZE); struct Pokemon *TeamData; // import/export Pokemon for trading TeamData=malloc(12*sizeof(struct Pokemon)); // Create an array of 12 Pokemons for both teams memset( TeamData, 0x0, 12); // loading both savefiles into buffer & buffer2 nio_printf(csl, ">> Loading first savefile\n"); if(loadSaveFile(buffer, FILE1) != FILESIZE) { nio_printf(csl, "\n[!] Your savefile is corrupted or can't be opened\nBe sure to use 128kb .sav files with this program"); wait_key_pressed(); return -1; } nio_printf(csl, ">> Loading second savefile\n"); if(loadSaveFile(buffer2, FILE2) != FILESIZE) { nio_printf(csl, "Your partner's savefile is corrupted or can't be opened\nBe sure to use 128kb .sav files with this program"); wait_key_pressed(); return -1; } // load both teams into TeamData[] nio_printf(csl, ">> Loading teams\n"); for(int c = 0; c<6; c++) { TeamData[c] = exportPokemon(buffer, c); TeamData[c+6] = exportPokemon(buffer2, c); /* TeamData[0] = Pokemon 1 of team 1; ... TeamData[5] = Pokemon 2 of team 1; TeamData[6] = Pokemon 1 of team 2; ... TeamData[11] = Pokemon 6 of team 2; */ } // TRADING POKEMON nio_printf(csl, ">> Trading Pokemon\n"); for(int i = 0; i < 6; i++) { if( (options->trades[0][i] >= 0) && (options->trades[1][i] >= 0) ) { importPokemon(buffer2, TeamData[options->trades[1][i]], options->trades[0][i]); importPokemon(buffer, TeamData[(options->trades[0][i])+6], options->trades[1][i]); } } // write both saves back to FILE3 and FILE4 and fix their checksums before // so they aren't corrupted fixChksum(buffer); fixChksum(buffer2); nio_printf(csl, ">> Writing back the first savefile\n"); if(writeSaveFile(buffer, FILE3) != FILESIZE) { nio_printf(csl, "[!] Error, can't write first savefile\n"); free(buffer); free(buffer2); free(TeamData); wait_key_pressed(); return 0; } nio_printf(csl, ">> Writing back the second savefile\n"); if(writeSaveFile(buffer2, FILE4) != FILESIZE) { nio_printf(csl, "[!] Error, can't write second savefile\n"); free(buffer); free(buffer2); free(TeamData); wait_key_pressed(); return 0; } nio_printf(csl, ">> Finished, returning to menu\n"); free(buffer); free(buffer2); free(TeamData); wait_key_pressed(); return 0; } // load savefile into buffer and return filesize for checking after unsigned long loadSaveFile(unsigned char buffer[], char filename[]) { FILE * fptr = fopen(filename, "rb"); if(fptr == NULL) { return 0; } unsigned long filesize = fread(buffer,1,FILESIZE,fptr); fclose(fptr); return filesize; } // write new savefile from buffer unsigned long writeSaveFile(unsigned char buffer[], char filename[]) { FILE * fptr = fopen(filename, "wb"); if(fptr == NULL) { return 0; } unsigned long filesize = fwrite (buffer , 1, FILESIZE, fptr); fclose(fptr); return filesize; } // write Pokemon into struct Pokemon and return it struct Pokemon exportPokemon(unsigned char *buffer, int index) { int VersionOffset = RSE_TEAM; // Emerald/Ruby/Sapphire Version as default version if(getGameCode(buffer) == 0x01) // FireRed/LeafGreen { VersionOffset = FR_TEAM; } else { // Ruby/Sapphire/Emerald VersionOffset = RSE_TEAM; } int64_t StartOffset = getOffset(buffer, 0x01) + VersionOffset + (100*index); printf("[EXPORT #%d] VersionOffset: -> %X", index, (unsigned int) VersionOffset); printf("Startoffset: -> %X\n", (unsigned int) StartOffset); struct Pokemon TempPoke; for(int c=0; c %X", index, (unsigned int) VersionOffset); printf("Startoffset: -> %X\n", (unsigned int) StartOffset); for(int c=0; c>8; return x; } int64_t calculateChkSum(unsigned char *buffer, int64_t offset) { uint64_t bytesum = 0; // Sum of all Bytes in the data part of a section uint64_t ChkSum = 0; // final checksum for(int c=0; c split it up into 4 Byte parts and change the order (little endian) > use bitshifting to build one final value > add final value to bytesum */ uint64_t tmp = (uint64_t) buffer[(uint64_t)offset+c]; uint64_t tmp2 = (uint64_t) buffer[(uint64_t)offset+c+1]; uint64_t tmp3 = (uint64_t) buffer[(uint64_t)offset+c+2]; uint64_t tmp4 = (uint64_t) buffer[(uint64_t)offset+c+3]; tmp2 = tmp2 << 8; tmp3 = tmp3 << 16; tmp4 = tmp4 << 24; bytesum += (tmp+tmp2+tmp3+tmp4); } // add 16 lower to 16 upper bits ChkSum = bytesum & 0xFFFFFFFF; //truncate to 32 bits ChkSum = bytesum>>16; //get 16 upper bits ChkSum += bytesum; //add to bytesum ChkSum = ChkSum & 0xFFFF; //truncate to 16 Bits to get the lower 16 bits ChkSum = swapBytes(ChkSum); //Swap Bytes because idk, sry dude but it only works this way return ChkSum; } //fixes all checksums of a buffer, so the savefiles isn't corrupted after the trade void fixChksum(unsigned char *buffer) { int64_t ChkSum = 0; int64_t Offset = 0; for(int c = 0; c < 14; c++) { Offset = getOffset(buffer, c); ChkSum = calculateChkSum(buffer, Offset); buffer[Offset+CHKSUM_OFFSET] = ChkSum >> 8; buffer[Offset+CHKSUM_OFFSET+1] = ChkSum & 0xFF; } } // returns the offset of a section uint64_t getOffset(unsigned char *buffer, int SectionId) { // saveindex to determine which savestate is the latest // There are 2 saves per 128 kb .sav file if you haven't noticed int64_t SaveIndex = -1; //-1 to check if something went wrong int64_t Offset = -1; for( int c = 0; c= SaveIndex) { Offset = c; } } } if(Offset<0) { //~ printf("\n\n[!] Offset not found for SectionID: %X\nSaveIndex: %X\n\n", (unsigned int) SectionId, (unsigned int) SaveIndex); return -1; } return Offset; } // Fix corrupt savefiles (only possible for wrong checksums) int FixSaves(nio_console *csl) { nio_clear(csl); nio_printf(csl, "Press any key start ...\n"); wait_key_pressed(); nio_clear(csl); unsigned char *buffer; // buffer of the first savefile buffer=malloc(FILESIZE*sizeof(unsigned char)); memset( buffer, 0x0, FILESIZE); unsigned char *buffer2; // buffer of the second savefile buffer2=malloc(FILESIZE*sizeof(unsigned char)); memset( buffer2, 0x0, FILESIZE); unsigned long result; //result of loadSaveFile to check if its a) there and b) exactly 128kb big nio_printf(csl, ">> Fixing first savefile\n"); result = loadSaveFile(buffer, FILE1); if(result == FILESIZE) { fixChksum(buffer); nio_printf(csl, ">> Writing back the first savefile\n"); writeSaveFile(buffer, FILE5); } else { if(result == 0){ nio_printf(csl, "\n[!] '1.sav' not found, continue with second\n"); } else { nio_printf(csl, "\n[!] Sorry but i can't repair '1.sav'\nBe sure to use 128kb .sav files with this program"); } wait_key_pressed(); } nio_printf(csl, ">> Fixing second savefile\n"); result = loadSaveFile(buffer2, FILE2); if(result == FILESIZE) { fixChksum(buffer2); nio_printf(csl, ">> Writing back the second savefile\n"); writeSaveFile(buffer2, FILE6); } else { if(result == 0){ nio_printf(csl, "\n[!] '2.sav' not found, exit\n"); } else { nio_printf(csl, "\n[!] Sorry but i can't repair '2.sav'\nBe sure to use 128kb .sav files with this program"); } wait_key_pressed(); } nio_printf(csl, ">> Finished, returning to menu\n"); free(buffer); free(buffer2); wait_key_pressed(); return 0; } int getGameCode(unsigned char *buffer) { uint64_t GameCodeOffset = getOffset(buffer, 0x00); GameCodeOffset += 172; return buffer[GameCodeOffset]; }