diff --git a/.gitignore b/.gitignore index c08b9d3..8de8a3d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ *.app # Visual Studio Stuff +*.sln # Files built by Visual Studio *_i.c diff --git a/README.md b/README.md index 254653d..e3edcfe 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,7 @@ To build *VSTrack* use **Visual Studio 2019** or **Build Tools for Visual Studio ## Known Issues [known-issues]: #known-issues -- If the *VSTrack* is running before the game loaded, it will stop as soon as the game cinematic starts to play. - - **The workaround**: start *VSTrack* a few seconds after starting the "New Game" mode, or right after loading the game from a save file. +Everything's fine. Really. ## Acknowledgements diff --git a/includes/vst_equipment.h b/includes/vst_equipment.h index 4e9cdde..e171022 100644 --- a/includes/vst_equipment.h +++ b/includes/vst_equipment.h @@ -15,7 +15,7 @@ * 6 cuboid * 7 rhombohedron * Shape angle (bits 3-7): - * ncrement by 360°/32, + * increment by 360°/32, * center is casting position, * direction is upward, incrementing backward */ @@ -354,6 +354,14 @@ WriteBladeInfo(DWORD processID) BytesRead = ReadGameMemory( processID, OFFSET_EQUIPPED_WEAPON_BLADE, BytesToRead, &BladeInfo); + // Check for out-of-bound indexes + if (BladeInfo.NamesListPosition > 511 || // + BladeInfo.ListPosition > 90 || // + BladeInfo.Category > 10) + { + return; + } + FILE *fpBladeInfoExt = fopen("game_stats/blade-full.txt", "w"); // Check if weapon is eqipped @@ -443,6 +451,15 @@ WriteBladeInfoShort(DWORD processID) BytesRead = ReadGameMemory( processID, OFFSET_EQUIPPED_WEAPON_BLADE, BytesToRead, &BladeInfo); + // Check for out-of-bound indexes + if (BladeInfo.NamesListPosition > 511 || // + BladeInfo.ListPosition > 90 || // + BladeInfo.Material > 7 || // + BladeInfo.Category > 10) + { + return; + } + FILE *fpBladeInfoShort = fopen("game_stats/blade-short.txt", "w"); // Check if weapon is eqipped @@ -505,6 +522,19 @@ WriteWeaponInfo(DWORD processID) BytesRead = ReadGameMemory( processID, OFFSET_EQUIPPED_WEAPON_GEM_SLOT3, BytesToRead, &Gem3Info); + // Check for out-of-bound indexes + if (BladeInfo.NamesListPosition > 511 || // + BladeInfo.ListPosition > 90 || // + BladeInfo.Category > 10 || // + BladeInfo.Material > 7 || // + GripInfo.ListPosition > 31 || // + Gem1Info.ListPosition > 62 || // + Gem2Info.ListPosition > 62 || // + Gem3Info.ListPosition > 62) + { + return; + } + FILE *fpWeaponInfo = fopen("game_stats/weapon-full.txt", "w"); // Check if weapon is eqipped @@ -683,6 +713,16 @@ WriteShieldInfo(DWORD processID) BytesRead = ReadGameMemory( processID, OFFSET_EQUIPPED_SHIELD_GEM_SLOT3, BytesToRead, &Gem3Info); + // Check for out-of-bound indexes + if (ShieldInfo.ListPosition > 80 || // + ShieldInfo.Material > 7 || // + Gem1Info.ListPosition > 62 || // + Gem2Info.ListPosition > 62 || // + Gem3Info.ListPosition > 62) + { + return; + } + FILE *fpShieldInfoExt = fopen("game_stats/shield-full.txt", "w"); // Check if shield is eqipped. If not, write the warning and skip the rest. @@ -826,6 +866,13 @@ WriteShieldInfoShort(DWORD processID) BytesRead = ReadGameMemory( processID, OFFSET_EQUIPPED_SHIELD, BytesToRead, &ShieldInfo); + // Check for out-of-bound indexes + if (ShieldInfo.ListPosition > 80 || // + ShieldInfo.Material > 7) + { + return; + } + FILE *fpShieldInfoShort = fopen("game_stats/shield-short.txt", "w"); // Check if shield is eqipped. If not, write the warning and skip the rest. @@ -881,6 +928,13 @@ WriteGloveInfo(DWORD processID, int which_glove) BytesRead = ReadGameMemory(processID, offset, BytesToRead, &GloveInfo); + // Check for out-of-bound indexes + if (GloveInfo.ListPosition > 80 || // + GloveInfo.Material > 7) + { + return; + } + FILE *fpGloveInfoExt = fopen(FileName, "w"); // Check if a glove is eqipped. If not, write the warning and skip the rest. @@ -965,6 +1019,13 @@ WriteGloveInfoShort(DWORD processID, int which_glove) BytesRead = ReadGameMemory(processID, offset, BytesToRead, &GloveInfo); + // Check for out-of-bound indexes + if (GloveInfo.ListPosition > 80 || // + GloveInfo.Material > 7) + { + return; + } + FILE *fpGloveInfoShort = fopen(FileName, "w"); // Check if a glove is eqipped. If not, write the warning and skip the rest. @@ -1015,6 +1076,13 @@ WriteHeadArmorInfo(DWORD processID) BytesRead = ReadGameMemory(processID, OFFSET_EQUIPPED_HEAD, BytesToRead, &HeadInfo); + // Check for out-of-bound indexes + if (HeadInfo.ListPosition > 80 || // + HeadInfo.Material > 7) + { + return; + } + FILE *fpHeadInfoExt = fopen("game_stats/armor-head-full.txt", "w"); // Check if a head armor is eqipped. @@ -1092,6 +1160,13 @@ WriteHeadArmorInfoShort(DWORD processID) BytesRead = ReadGameMemory(processID, OFFSET_EQUIPPED_HEAD, BytesToRead, &HeadInfo); + // Check for out-of-bound indexes + if (HeadInfo.ListPosition > 80 || // + HeadInfo.Material > 7) + { + return; + } + FILE *fpHeadInfoShort = fopen("game_stats/armor-head-short.txt", "w"); // Check if a head armor is eqipped. @@ -1141,6 +1216,13 @@ WriteBodyArmorInfo(DWORD processID) BytesRead = ReadGameMemory(processID, OFFSET_EQUIPPED_BODY, BytesToRead, &BodyInfo); + // Check for out-of-bound indexes + if (BodyInfo.ListPosition > 80 || // + BodyInfo.Material > 7) + { + return; + } + FILE *fpBodyInfoExt = fopen("game_stats/armor-body-full.txt", "w"); // Check if a body armor is eqipped. If not, write the warning and skip the @@ -1219,6 +1301,13 @@ WriteBodyArmorInfoShort(DWORD processID) BytesRead = ReadGameMemory(processID, OFFSET_EQUIPPED_BODY, BytesToRead, &BodyInfo); + // Check for out-of-bound indexes + if (BodyInfo.ListPosition > 80 || // + BodyInfo.Material > 7) + { + return; + } + FILE *fpBodyInfoShort = fopen("game_stats/armor-body-short.txt", "w"); // Check if a body armor is eqipped. If not, write the warning and skip the @@ -1269,6 +1358,13 @@ WriteLegsArmorInfo(DWORD processID) BytesRead = ReadGameMemory(processID, OFFSET_EQUIPPED_LEGS, BytesToRead, &LegsInfo); + // Check for out-of-bound indexes + if (LegsInfo.ListPosition > 80 || // + LegsInfo.Material > 7) + { + return; + } + FILE *fpLegsInfoExt = fopen("game_stats/armor-legs-full.txt", "w"); // Check if a leg armor is eqipped. @@ -1346,6 +1442,13 @@ WriteLegsArmorInfoShort(DWORD processID) BytesRead = ReadGameMemory(processID, OFFSET_EQUIPPED_LEGS, BytesToRead, &LegsInfo); + // Check for out-of-bound indexes + if (LegsInfo.ListPosition > 80 || // + LegsInfo.Material > 7) + { + return; + } + FILE *fpLegsInfoShort = fopen("game_stats/armor-legs-short.txt", "w"); // Check if a leg armor is eqipped. @@ -1395,6 +1498,12 @@ WriteNecklaceInfo(DWORD processID) BytesRead = ReadGameMemory(processID, OFFSET_EQUIPPED_NECK, BytesToRead, &NeckInfo); + // Check for out-of-bound indexes + if (NeckInfo.NamesListPosition > 31) + { + return; + } + FILE *fpNeckInfoExt = fopen("game_stats/accessory.txt", "w"); // Check if a necklace is eqipped. diff --git a/includes/vst_player.h b/includes/vst_player.h index a28987c..ef6c3f6 100644 --- a/includes/vst_player.h +++ b/includes/vst_player.h @@ -241,12 +241,12 @@ PrintPlayerStats2(void) BytesRead = ReadGameMemory(processID, OFFSET_PLAYER_STATUS_EFFECTS, sizeof(status_effect), &StatusEffects); - UINT8 STR_Original = PlayerStats.STR_Original; - UINT8 STR_Equipped = PlayerStats.STR_Equipped; - UINT8 INT_Original = PlayerStats.INT_Original; - UINT8 INT_Equipped = PlayerStats.INT_Equipped; - UINT8 AGL_Original = PlayerStats.AGL_Original; - UINT8 AGL_Equipped = PlayerStats.AGL_Equipped; + UINT16 STR_Original = PlayerStats.STR_Original; + UINT16 STR_Equipped = PlayerStats.STR_Equipped; + UINT16 INT_Original = PlayerStats.INT_Original; + UINT16 INT_Equipped = PlayerStats.INT_Equipped; + UINT16 AGL_Original = PlayerStats.AGL_Original; + UINT16 AGL_Equipped = PlayerStats.AGL_Equipped; char STR_Buff = ' '; char INT_Buff = ' '; @@ -336,6 +336,37 @@ PrintPlayerStats2(void) } } +BOOL +CheckPlayerStats(void) +{ + player_stats PlayerStats; + + DWORD BytesRead; + + BytesRead = ReadGameMemory( + processID, OFFSET_PLAYER_HP_CURRENT, sizeof(player_stats), &PlayerStats); + + UINT16 HP_Current = PlayerStats.HP_Current; + UINT16 HP_Maximum = PlayerStats.HP_Maximum; + UINT16 MP_Current = PlayerStats.MP_Current; + UINT16 MP_Maximum = PlayerStats.MP_Maximum; + UINT16 Risk = PlayerStats.Risk; + UINT16 STR_Original = PlayerStats.STR_Original; + UINT16 INT_Original = PlayerStats.INT_Original; + UINT16 AGL_Original = PlayerStats.AGL_Original; + + // Check for invalid data + if (HP_Current == 0 || HP_Current > 999 || HP_Maximum > 999 || + MP_Current == 0 || MP_Current > 999 || MP_Maximum > 999 || + STR_Original > 999 || INT_Original > 999 || AGL_Original > 999 || + Risk > 100) + { + return FALSE; + } + + return TRUE; +} + DWORD ReadLastBossHealth(UINT16 *BossHP) { diff --git a/includes/vst_translate.h b/includes/vst_translate.h index f4bca21..2186da9 100644 --- a/includes/vst_translate.h +++ b/includes/vst_translate.h @@ -42,7 +42,7 @@ GetWeaponName(DWORD processID, char *WeaponName) processID, OFFSET_EQUIPPED_WEAPON_BLADE, BytesToRead, &BladeInfo); // Check if weapon is eqipped - if (BladeInfo.ListPosition == 0) + if (BladeInfo.ListPosition == 0 || BladeInfo.ListPosition > 511) { return; } diff --git a/main.c b/main.c index 04a71ce..28532b0 100644 --- a/main.c +++ b/main.c @@ -128,52 +128,61 @@ wmain(int argc, wchar_t *argv[]) SetCursorPosition(hStdout, 0, 0); SetCursorPosition(hBackBuffer, 0, 0); - sprintf(szBuffer, "============================\n" - "== VSTracker v0.1.4-alpha ==\n" - "============================\n"); - WriteToBackBuffer(); + // Write player stats into the file - ReadPlayTime(&PlayTimeCurrent); - PrintPlayTimeShort2(&PlayTimeCurrent); - WritePlayTimeToFile(&PlayTimeCurrent, _T("game_stats//play-time.txt")); + if (CheckPlayerStats()) + { + ReadPlayTime(&PlayTimeCurrent); + PrintPlayTimeShort2(&PlayTimeCurrent); + WritePlayTimeToFile(&PlayTimeCurrent, _T("game_stats//play-time.txt")); - // Check player's location - ReadLocation(&Location); - GetAreaAndRoomName(&Location, szAreaName, szRoomName); - PrintLocation2(&Location, szAreaName, szRoomName); - WriteLocationIntoFile(&Location, szAreaName, szRoomName); + // Check player's location + ReadLocation(&Location); + GetAreaAndRoomName(&Location, szAreaName, szRoomName); + PrintLocation2(&Location, szAreaName, szRoomName); + WriteLocationIntoFile(&Location, szAreaName, szRoomName); - // Write player stats into the file - WritePlayerStats(); - PrintPlayerStats2(); + PrintPlayerStats2(); + WritePlayerStats(); - GetWeaponName(processID, GlobalWeaponName); + GetWeaponName(processID, GlobalWeaponName); - // Write equipment stats into files - WriteWeaponInfo(processID); + // Write equipment stats into files + WriteWeaponInfo(processID); - WriteBladeInfo(processID); - WriteBladeInfoShort(processID); + WriteBladeInfo(processID); + WriteBladeInfoShort(processID); - WriteShieldInfo(processID); - WriteShieldInfoShort(processID); + WriteShieldInfo(processID); + WriteShieldInfoShort(processID); - WriteGloveInfo(processID, RIGHT_GLOVE); - WriteGloveInfoShort(processID, RIGHT_GLOVE); + WriteGloveInfo(processID, RIGHT_GLOVE); + WriteGloveInfoShort(processID, RIGHT_GLOVE); - WriteGloveInfo(processID, LEFT_GLOVE); - WriteGloveInfoShort(processID, LEFT_GLOVE); + WriteGloveInfo(processID, LEFT_GLOVE); + WriteGloveInfoShort(processID, LEFT_GLOVE); - WriteHeadArmorInfo(processID); - WriteHeadArmorInfoShort(processID); + WriteHeadArmorInfo(processID); + WriteHeadArmorInfoShort(processID); - WriteBodyArmorInfo(processID); - WriteBodyArmorInfoShort(processID); + WriteBodyArmorInfo(processID); + WriteBodyArmorInfoShort(processID); - WriteLegsArmorInfo(processID); - WriteLegsArmorInfoShort(processID); + WriteLegsArmorInfo(processID); + WriteLegsArmorInfoShort(processID); - WriteNecklaceInfo(processID); + WriteNecklaceInfo(processID); + } + else + { + sprintf(szBuffer, "============================\n" + "== VSTracker v0.1.6-alpha ==\n" + "============================\n"); + WriteToBackBuffer(); + + sprintf(szBuffer, "\nWaiting for the game to load ...\n"); + WriteToBackBuffer(); + } // Handle last boss if (IsThisTheLastBossRoom(&Location))