From e4c24d09531f89a8effe9aab5ad774caf47012fa Mon Sep 17 00:00:00 2001 From: Darrel Malkin Date: Sat, 16 Nov 2024 17:27:56 +1100 Subject: [PATCH] Rewrote All Uses of 'from_str_radix' to Use Bit Manipulation in saveLoader - Rewrote function getPokemonOTIDFromSave() - Rewrote function getPokemonHPFromSave() - Rewrote function getPokemonStatsFromSave() - Rewrote function getPokemonEVsFromSave() - Rewrote function getPokemonMovesFromSave() - Rewrote function getPokemonIVsFromSave() - Added test getTrainerIDFromSave_BitManipTest() - Added test getPokemonOTIDFromSave_BitManipTest() - Added test getPokemonHPFromSave_BitManipTest() - Added test getPokemonStatsFromSave_BitManipTest() - Added test getPokemonEVsFromSave_BitManipTest() - Added test getPokemonMovesFromSave_BitManipTest() - Fixed faulty order of EVs in saveLoader - Updated pokemonStats::IVs::to_string() to include HP IV - Updated test pokemon::basicPkmnTests::getDetails() to include new HP IV printout --- src/PKRust/creatureData/pokemon.rs | 2 +- src/PKRust/creatureData/pokemonStats.rs | 5 +- src/PKRust/saveLoader.rs | 274 +++++++++++++++++++----- 3 files changed, 225 insertions(+), 56 deletions(-) diff --git a/src/PKRust/creatureData/pokemon.rs b/src/PKRust/creatureData/pokemon.rs index 451323f..31368ee 100644 --- a/src/PKRust/creatureData/pokemon.rs +++ b/src/PKRust/creatureData/pokemon.rs @@ -270,7 +270,7 @@ mod basicPkmnTests { let testPkmn:Pokemon = Pokemon::new(); let stringPokemon:String = testPkmn.getDetails(); - let actualString = "Bulbasaur Bobsaur LVL:10 Current HP: 100\n\tNull PP: 0 PP Up: 0\n\tNull PP: 0 PP Up: 0\n\tNull PP: 0 PP Up: 0\n\tNull PP: 0 PP Up: 0\n\n\n\tHP: 0\n\tATK: 0\n\tDEF:0\n\tSPD: 0\n\tSPCL: 0\n\n\tHP EV: 0\n\tATK EV: 0\n\tDEF EV:0\n\tSPD EV: 0\n\tSPCL EV: 0\n\n\tATK IV: 0\n\tDEF IV:0\n\tSPD IV: 0\n\tSPCL IV: 0\n"; + let actualString = "Bulbasaur Bobsaur LVL:10 Current HP: 100\n\tNull PP: 0 PP Up: 0\n\tNull PP: 0 PP Up: 0\n\tNull PP: 0 PP Up: 0\n\tNull PP: 0 PP Up: 0\n\n\n\tHP: 0\n\tATK: 0\n\tDEF:0\n\tSPD: 0\n\tSPCL: 0\n\n\tHP EV: 0\n\tATK EV: 0\n\tDEF EV:0\n\tSPD EV: 0\n\tSPCL EV: 0\n\n\tHP IV: 0\n\tATK IV: 0\n\tDEF IV:0\n\tSPD IV: 0\n\tSPCL IV: 0\n"; assert_eq!(stringPokemon, actualString); } diff --git a/src/PKRust/creatureData/pokemonStats.rs b/src/PKRust/creatureData/pokemonStats.rs index 5ac271a..855e2df 100644 --- a/src/PKRust/creatureData/pokemonStats.rs +++ b/src/PKRust/creatureData/pokemonStats.rs @@ -52,11 +52,12 @@ impl IVs { /// Returns a string to display all stats pub fn to_string(&self) -> String { - return format!("\tATK IV: {}\n\tDEF IV:{}\n\tSPD IV: {}\n\tSPCL IV: {}\n", + return format!("\tHP IV: {}\n\tATK IV: {}\n\tDEF IV:{}\n\tSPD IV: {}\n\tSPCL IV: {}\n", + self.hp, self.atk, self.def, self.spd, - self.spc + self.spc, ); } diff --git a/src/PKRust/saveLoader.rs b/src/PKRust/saveLoader.rs index 04ad763..051ac20 100644 --- a/src/PKRust/saveLoader.rs +++ b/src/PKRust/saveLoader.rs @@ -549,10 +549,12 @@ impl Save { /// Function for retrieving a Pokemons Original Trainers ID fn getPokemonOTIDFromSave(save: &Vec, currAddr: &usize) -> u16{ - return u16::from_str_radix( - &format!("{:02X}{:02X}", save[currAddr+OT_OFF],save[currAddr+OT_OFF+1]), - 16 - ).unwrap(); + let mut pokemonOTID:u16 = 0b0; + + pokemonOTID |= (save[currAddr+OT_OFF] as u16) << 8; + pokemonOTID |= save[currAddr+OT_OFF+1] as u16; + + return pokemonOTID; } /// Function for retrieving a Pokemons Original Trainers Name @@ -571,10 +573,13 @@ impl Save { /// Function for retrieving the Pokemons current Health Points fn getPokemonHPFromSave(save: &Vec, currAddr: &usize) -> i16{ - return i16::from_str_radix( - &format!("{:02X}{:02X}",save[currAddr+HP_OFF],save[currAddr+HP_OFF+1]), - 16 - ).unwrap(); + + let mut pokemonHP:i16 = 0x0; + + pokemonHP |= (save[currAddr+HP_OFF] as i16) << 8; + pokemonHP |= save[currAddr+HP_OFF + 1] as i16; + + return pokemonHP; } /// Function for retrieving a Pokemons Nickname. @@ -590,28 +595,26 @@ impl Save { /// Function for retrieving a Pokemons base stats fn getPokemonStatsFromSave(save: &Vec,currAddr: &usize) -> [u16;5] { - let mut stats: [u16;5] = [0; 5]; - for stat in 0..5 { - let currAddr = currAddr+STAT_OFF+(stat*2); - stats[stat] = u16::from_str_radix( - &format!("{:02X}{:02X}",save[currAddr],save[currAddr+1]), - 16 - ).unwrap(); - } + let mut stats: [u16;5] = [0x0; 5]; + + for stat in 0..5 { + let currAddr = currAddr+STAT_OFF+(stat*2); + stats[stat] |= (save[currAddr] as u16) << 8; + stats[stat] |= save[currAddr + 1] as u16; + } return stats; } /// Function for retrieving a Pokemons Effort Values fn getPokemonEVsFromSave(save: &Vec,currAddr: &usize) -> [u16;5] { - let mut evs: [u16;5] = [0; 5]; - for stat in 0..5 { - let currAddr = currAddr+EV_OFF+(stat*2); - evs[stat] = u16::from_str_radix( - &format!("{:02X}{:02X}",save[currAddr],save[currAddr+1]), - 16 - ).unwrap(); - } + let mut evs: [u16;5] = [0x0; 5]; + + for stat in 0..5 { + let currAddr = currAddr+EV_OFF+(stat*2); + evs[stat] |= (save[currAddr] as u16) << 8; + evs[stat] |= save[currAddr + 1] as u16; + } return evs; } @@ -623,14 +626,19 @@ impl Save { for moves in 0..4 { let moveIndex = save[moveAddr+moves] as u16; - let ppStr = format!("{:08b}",save[currAddr+PP_OFF+moves]); - let (ppUp,pp) = ppStr.split_at(2); - let currPP = u16::from_str_radix(pp, 2).unwrap(); - let currPPUp = u8::from_str_radix(ppUp, 2).unwrap(); + + // As per the Wikiw (https://m.bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_data_structure_(Generation_I)#PP), PP values are determined by: + // The current PP-Up being the first two bits of the move + // The current PP value being the remaining 6 bits + let ppValues = save[currAddr+PP_OFF+moves]; + + let PP: u16 = ((ppValues << 2) >> 2) as u16 ; + let PPUp: u8 = ppValues >> 6; + if moveIndex == 0 { returnVec.push(Move::empty()); } else { - returnVec.push(Move::get(moveIndex, currPP, currPPUp).unwrap()); + returnVec.push(Move::get(moveIndex, PP, PPUp).unwrap()); } } @@ -638,26 +646,28 @@ impl Save { } /// Function for retrieving a Pokemons Individual Values - /// (Also known as Determinant Values) + /// Also known as [Determinant Values](https://m.bulbapedia.bulbagarden.net/wiki/Individual_values) /// - /// **TODO**: Figure out a better way to split into fours + /// **NOTE**: This function does not have a bit manip test as the original implementation + /// was faulty. fn getPokemonIVsFromSave(save: &Vec,currAddr: &usize) -> [u16;5]{ - let allIVs = format!("{:08b}{:08b}",save[currAddr+IV_OFF],save[currAddr+IV_OFF+1]); - let (half, half2) = allIVs.split_at(8); - let ((atk, def), (spd, spc)) = (half.split_at(4), half2.split_at(4)); - let hp = format!("{}{}{}{}", - atk.chars().last().unwrap(), - def.chars().last().unwrap(), - spd.chars().last().unwrap(), - spc.chars().last().unwrap() - ); - // println!("At address: {:#0X} IV: {}",pkmnAddress+IV_OFF,allIVs); + + let atk = (save[currAddr+IV_OFF] >> 4) as u16; + let def = ((save[currAddr+IV_OFF] << 4) >> 4) as u16; + let spd = (save[currAddr+IV_OFF+1] >> 4) as u16; + let spc = ((save[currAddr+IV_OFF+1] << 4) >> 4) as u16; + + + // For the HPIVs we have to get the last bits of all four stats + let mut hp = 0; + hp |= (atk & 1) << 3 | (def & 1) << 2 | (spd & 1) << 1 | (spc & 1); + let ivs: [u16;5] = [ - u16::from_str_radix(atk,2).unwrap(), - u16::from_str_radix(def,2).unwrap(), - u16::from_str_radix(spd,2).unwrap(), - u16::from_str_radix(spc,2).unwrap(), - u16::from_str_radix(&hp,2).unwrap(), + hp, + atk, + def, + spd, + spc ]; return ivs; @@ -1279,14 +1289,172 @@ mod fileLoadingBitManipulationTests { #[test] fn getTrainerIDFromSave_BitManipTest() { - // Read a test save file - let testSave = fs::read("./test/POKEMON BLUE.sav").unwrap(); - // Get the ID with bit manipulation - let readID = Save::getTrainerIDFromSave(&testSave); - // Get the expected ID with the simpled String-radix method - let expectedlID = u16::from_str_radix(&format!("{:02X}{:02X}",testSave[ID_ADDR],testSave[ID_ADDR+1]), 16).unwrap(); + let testSaveList = ["./test/POKEMON YELLOW.sav", "./test/POKEMON YELLOW 2.sav", "./test/POKEMON BLUE.sav", "./test/Pokeblue.sav", "./test/POKEpi.sav"]; + + for save in testSaveList { + // Read a test save file + let testSave = fs::read(save).unwrap(); + + // Get the ID with bit manipulation + let readID = Save::getTrainerIDFromSave(&testSave); + // Get the expected ID with the simpled String-radix method + let expectedlID = u16::from_str_radix(&format!("{:02X}{:02X}",testSave[ID_ADDR],testSave[ID_ADDR+1]), 16).unwrap(); + + assert_eq!(readID, expectedlID); + } + } + + #[test] + fn getPokemonOTIDFromSave_BitManipTest() { + + let testSaveList = ["./test/POKEMON YELLOW.sav", "./test/POKEMON YELLOW 2.sav", "./test/POKEMON BLUE.sav", "./test/Pokeblue.sav", "./test/POKEpi.sav"]; + + for save in testSaveList { + // Read a test save file + let testSave = fs::read(save).unwrap(); + + let testPkmnAddress: usize = PARTY_ADDR + 0x8 + 0x2C; + + // Get the ID with bit manipulation + let readID = Save::getPokemonOTIDFromSave(&testSave, &testPkmnAddress); + // Get the expected ID with the simpled String-radix method + let expectedlID = u16::from_str_radix(&format!("{:02X}{:02X}", testSave[testPkmnAddress+OT_OFF],testSave[testPkmnAddress+OT_OFF+1]),16).unwrap(); + + assert_eq!(readID, expectedlID); + } + } + + #[test] + fn getPokemonHPFromSave_BitManipTest() { + + let testSaveList = ["./test/POKEMON YELLOW.sav", "./test/POKEMON YELLOW 2.sav", "./test/POKEMON BLUE.sav", "./test/Pokeblue.sav", "./test/POKEpi.sav"]; + + for save in testSaveList { + // Read a test save file + let testSave = fs::read(save).unwrap(); + + let testPkmnAddress: usize = PARTY_ADDR + 0x8 + 0x2C; + + // Get the HP with bit manipulation + let readHP = Save::getPokemonHPFromSave(&testSave, &testPkmnAddress); + // Get the expected HP with the simpled String-radix method + let expectedHP = i16::from_str_radix(&format!("{:02X}{:02X}",testSave[testPkmnAddress+HP_OFF],testSave[testPkmnAddress+HP_OFF+1]), 16).unwrap(); + + assert_eq!(readHP, expectedHP); + } + } + + #[test] + fn getPokemonStatsFromSave_BitManipTest() { + + let testSaveList = ["./test/POKEMON YELLOW.sav", "./test/POKEMON YELLOW 2.sav", "./test/POKEMON BLUE.sav", "./test/Pokeblue.sav", "./test/POKEpi.sav"]; + + for save in testSaveList { + // Read a test save file + let testSave = fs::read(save).unwrap(); + + let testPkmnAddress: usize = PARTY_ADDR + 0x8 + 0x2C; + + // Get the stats with bit manipulation + let readStats = Save::getPokemonStatsFromSave(&testSave, &testPkmnAddress); + + // Get the expected stats with the simpled String-radix method + let mut expectedStats: [u16;5] = [12345; 5]; + for stat in 0..5 { + let currAddr = testPkmnAddress+STAT_OFF+(stat*2); + expectedStats[stat] = u16::from_str_radix( + &format!("{:02X}{:02X}",testSave[currAddr],testSave[currAddr+1]), + 16 + ).unwrap(); + } + + + assert_eq!(readStats, expectedStats); + } + } + + #[test] + fn getPokemonEVsFromSave_BitManipTest() { + + let testSaveList = ["./test/POKEMON YELLOW.sav", "./test/POKEMON YELLOW 2.sav", "./test/POKEMON BLUE.sav", "./test/Pokeblue.sav", "./test/POKEpi.sav"]; + + for save in testSaveList { + // Read a test save file + let testSave = fs::read(save).unwrap(); + + let testPkmnAddress: usize = PARTY_ADDR + 0x8 + 0x2C; + + // Get the stats with bit manipulation + let readEVs = Save::getPokemonEVsFromSave(&testSave, &testPkmnAddress); + + // Get the expected stats with the simpled String-radix method + let mut expectedEVs: [u16;5] = [12345; 5]; + for ev in 0..5 { + let currAddr = testPkmnAddress+EV_OFF+(ev*2); + expectedEVs[ev] = u16::from_str_radix( + &format!("{:02X}{:02X}",testSave[currAddr],testSave[currAddr+1]), + 16 + ).unwrap(); + } + + assert_eq!(readEVs, expectedEVs); + } + } + + #[test] + fn getPokemonMovesFromSave_BitManipTest() { + + let testSaveList = ["./test/POKEMON YELLOW.sav", "./test/POKEMON YELLOW 2.sav", "./test/POKEMON BLUE.sav", "./test/Pokeblue.sav", "./test/POKEpi.sav"]; + + for save in testSaveList { + // Read a test save file + let testSave = fs::read(save).unwrap(); + + let testPkmnAddress: usize = PARTY_ADDR + 0x8 + 0x2C; + + // First, let's get the moves the old string way + let mut expectedMoves: Vec = Vec::new(); + let moveAddr = testPkmnAddress + MOVE_OFF; + + for moves in 0..4 { + let moveIndex = testSave[moveAddr+moves] as u16; + let ppStr = format!("{:08b}",testSave[testPkmnAddress+PP_OFF+moves]); + let (ppUp,pp) = ppStr.split_at(2); + let currPP = u16::from_str_radix(pp, 2).unwrap(); + let currPPUp = u8::from_str_radix(ppUp, 2).unwrap(); + if moveIndex == 0 { + expectedMoves.push(Move::empty()); + } else { + expectedMoves.push(Move::get(moveIndex, currPP, currPPUp).unwrap()); + } + } + + // Now we do it the new way + let actualMoves = Save::getPokemonMovesFromSave(&testSave, &testPkmnAddress); + + // Now from these moves, let's create an Array for the PP, and PPUp + let mut expectedPPArr: [&u16; 4] = [&0; 4]; + let mut expectedPPUpArr: [&u8; 4] = [&0; 4]; + + let mut actualPPArr: [&u16; 4] = [&0; 4]; + let mut actualPPUpArr: [&u8; 4] = [&0; 4]; + + for index in 0..4 { + expectedPPArr[index] = expectedMoves[index].getPP(); + expectedPPUpArr[index] = expectedMoves[index].getPPUp(); + actualPPArr[index] = actualMoves[index].getPP(); + actualPPUpArr[index] = actualMoves[index].getPPUp(); + } + + // And finally, we assert that all these values are the same + assert_eq!(actualPPArr,expectedPPArr); + assert_eq!(actualPPUpArr,expectedPPUpArr); + + } + + - assert_eq!(readID, expectedlID); } + } \ No newline at end of file