Skip to content

Commit

Permalink
Fixed a bug in IsValidForNewGamePlus that caused exceptions on old saves
Browse files Browse the repository at this point in the history
  • Loading branch information
not_alphanine committed Jun 1, 2024
1 parent f167bd9 commit e843ef0
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 32 deletions.
Binary file modified NewGamePlus.rc
Binary file not shown.
80 changes: 62 additions & 18 deletions src/filesystem/fs_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ bool HasValidPointOfNoReturnSave()
});
}

bool IsValidForNewGamePlus(std::string_view aSaveName, uint64_t& aPlaythroughHash)
bool IsValidForNewGamePlus(std::string_view aSaveName, uint64_t& aPlaythroughHash) noexcept
{
static const auto basePath = GetCpSaveFolder();

Expand All @@ -74,49 +74,82 @@ bool IsValidForNewGamePlus(std::string_view aSaveName, uint64_t& aPlaythroughHas

const auto metadataPath = basePath / aSaveName / "metadata.9.json";

if (!std::filesystem::is_regular_file(metadataPath))
std::error_code ec{};

if (!std::filesystem::is_regular_file(metadataPath, ec))
{
return false;
}

auto padded = simdjson::padded_string::load(metadataPath.string());

if (padded.error() != simdjson::error_code::SUCCESS)
if (padded.error() != simdjson::SUCCESS)
{
return false;
}

auto json2 = simdjson::dom::parser{};
simdjson::dom::parser parser{};
simdjson::dom::element document{};

const auto document = json2.parse(padded.value());
if (parser.parse(padded.value()).get(document) != simdjson::SUCCESS)
{
return false;
}

if (!document.is_object())
{
return false;
}

if (!document["RootType"].is_string())
if (!document.at_key("RootType").is_string())
{
return false;
}

simdjson::dom::element saveMetadata{};

if (document.at_key("Data").at_key("metadata").get(saveMetadata) != simdjson::SUCCESS)
{
return false;
}

const auto saveMetadata = document["Data"]["metadata"];
// ISSUE: old saves (might not be from PC?) don't have save metadata correct
// Switch to noexcept versions

int64_t gameVersion{};

if (saveMetadata.at_key("gameVersion").get_int64().get(gameVersion) != simdjson::SUCCESS)
{
return false;
}

if (minSupportedGameVersion > int64_t(saveMetadata["gameVersion"]))
if (minSupportedGameVersion > gameVersion)
{
return false;
}

const auto isPointOfNoReturn = aSaveName.starts_with("PointOfNoReturn");

aPlaythroughHash = Red::FNV1a64(saveMetadata["playthroughID"].get_c_str().value());
std::string_view playthroughId{};

if (saveMetadata.at_key("playthroughID").get_string().get(playthroughId) != simdjson::SUCCESS)
{
return false;
}

aPlaythroughHash = Red::FNV1a64(playthroughId.data());

if (isPointOfNoReturn)
{
return true;
}

const auto questsDone = saveMetadata["finishedQuests"].get_string().value();
std::string_view questsDone{};

if (saveMetadata.at_key("finishedQuests").get_string().get(questsDone) != simdjson::SUCCESS)
{
return false;
}

using std::operator""sv;
auto questsSplitRange = std::views::split(questsDone, " "sv);
Expand All @@ -128,23 +161,34 @@ bool IsValidForNewGamePlus(std::string_view aSaveName, uint64_t& aPlaythroughHas
return true;
}

constexpr auto q307_active_fact = "q307_blueprint_acquired=1";
constexpr auto q307ActiveFact = "q307_blueprint_acquired=1";

simdjson::dom::array importantFacts{};

for (auto fact : saveMetadata["facts"])
if (!saveMetadata.at_key("facts").get_array().get(importantFacts) != simdjson::SUCCESS)
{
if (fact.is_string())
return false;
}

for (auto fact : importantFacts)
{
std::string_view factValueString{};

if (fact.get_string().get(factValueString) != simdjson::SUCCESS)
{
continue;
}

if (factValueString == q307ActiveFact)
{
if (fact.get_string().value() == q307_active_fact)
{
return true;
}
return true;
}
}

return false;
}

bool IsValidForNewGamePlus(std::string_view aSaveName)
bool IsValidForNewGamePlus(std::string_view aSaveName) noexcept
{
uint64_t dummy{};
return IsValidForNewGamePlus(aSaveName, dummy);
Expand Down
6 changes: 3 additions & 3 deletions src/filesystem/fs_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
namespace files {
std::filesystem::path GetCpSaveFolder();
bool HasValidPointOfNoReturnSave();
bool IsValidForNewGamePlus(std::string_view aSaveName);
bool IsValidForNewGamePlus(std::string_view aSaveName, uint64_t& aPlaythroughHash);
}
bool IsValidForNewGamePlus(std::string_view aSaveName) noexcept;
bool IsValidForNewGamePlus(std::string_view aSaveName, uint64_t& aPlaythroughHash) noexcept;
}
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ RED4EXT_C_EXPORT void RED4EXT_CALL Query(RED4ext::PluginInfo* aInfo)
{
aInfo->name = L"New Game+";
aInfo->author = L"not_alphanine";
aInfo->version = RED4EXT_SEMVER_EX(1, 0, 0, 0, 0); // Set your version here.
aInfo->version = RED4EXT_SEMVER_EX(1, 0, 0, 1, 0); // Set your version here.
aInfo->runtime = RED4EXT_RUNTIME_INDEPENDENT;
aInfo->sdk = RED4EXT_SDK_LATEST;
}
Expand Down
22 changes: 13 additions & 9 deletions src/redscript_api/redscriptBindings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,8 @@ inline constexpr auto Q003Chip = Red::TweakDBID("Items.q003_chip");
inline constexpr auto Q003ChipCracked = Red::TweakDBID("Items.q003_chip_cracked");
inline constexpr auto Q003ChipCrackedFunds = Red::TweakDBID("Items.q003_chip_cracked_funds");

inline constexpr auto Q001Lexington = Red::TweakDBID("Items.Preset_Q001_Lexington");
inline constexpr auto CyberdeckSplinter = Red::TweakDBID("Items.CyberdeckSplinter");
inline constexpr auto PresetLexingtonWilson = Red::TweakDBID("Items.Preset_Lexington_Wilson");
inline constexpr auto PresetLexingtonWilsonRare = Red::TweakDBID("Items.Preset_Lexington_Wilson_Rare");
inline constexpr auto PresetLexingtonWilsonEpic = Red::TweakDBID("Items.Preset_Lexington_Wilson_Epic");

inline constexpr auto PresetLexingtonWilsonLegendary = Red::TweakDBID("Items.Preset_Lexington_Wilson_Legendary");
inline constexpr auto MQ011WilsonGun = Red::TweakDBID("Items.mq011_wilson_gun");

inline bool IsForbidden(Red::TweakDBID aId)
{
Expand All @@ -223,9 +217,8 @@ inline bool IsForbidden(Red::TweakDBID aId)
aId == PersonalLink2 || aId == MaTppHead || aId == WaTppHead || aId == FppHead || aId == HolsteredFists ||
aId == MQ024DataCarrier || aId == Skippy || aId == SkippyPostQuest || aId == PresetSkippy ||
aId == PresetSkippyPostQuest || aId == SaburoDataCarrier || aId == SaburoDataCarrierCracked ||
aId == Q003Chip || aId == Q003ChipCracked || aId == Q003ChipCrackedFunds || aId == Q001Lexington ||
aId == CyberdeckSplinter || aId == PresetLexingtonWilson || aId == PresetLexingtonWilsonRare ||
aId == PresetLexingtonWilsonEpic || aId == PresetLexingtonWilsonLegendary || aId == MQ011WilsonGun;
aId == Q003Chip || aId == Q003ChipCracked || aId == Q003ChipCrackedFunds ||
aId == CyberdeckSplinter;
}
};

Expand Down Expand Up @@ -750,6 +743,11 @@ class NewGamePlusSystem : public Red::IGameSystem
return HasTag("WeaponMod") || m_itemType == Red::gamedataItemType::Prt_Program;
}

bool IsDyingNight() const
{
return HasTag("Lexington_Wilson");
}

bool IsAllowedType() const
{
using Red::gamedataItemType;
Expand Down Expand Up @@ -908,6 +906,12 @@ class NewGamePlusSystem : public Red::IGameSystem
return;
}

if (aExtendedData.IsDyingNight())
{
// Just fuck off...
return;
}

if (aExtendedData.m_tdbId == "Items.money")
{
m_saveData.m_playerMoney += aItem.itemQuantity;
Expand Down
2 changes: 1 addition & 1 deletion wolvenkit/fileTreeState.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"archive":true,"archive\\base":true,"archive\\mod":true,"archive\\base\\quest":true,"archive\\base\\quest\\main_quests":true,"archive\\base\\quest\\main_quests\\part1":true}
{"archive":true,"archive\\base":true,"archive\\mod":true,"archive\\base\\quest":true,"archive\\base\\quest\\main_quests":true,"archive\\base\\quest\\main_quests\\part1":true,"archive\\base\\gameplay":true,"archive\\base\\gameplay\\gui":true,"archive\\base\\gameplay\\gui\\fullscreen":true,"archive\\base\\gameplay\\gui\\fullscreen\\main_menu":true,"archive\\base\\gameplay\\gui\\fullscreen\\main_menu\\new_game_plus_selection.inkatlas":true,"archive\\base\\gameplay\\gui\\fullscreen\\main_menu\\new_game_plus_selection.xbm":true,"archive\\base\\gameplay\\gui\\fullscreen\\main_menu\\new_game_plus_selection_1080p.xbm":true,"archive\\base\\gameplay\\gui\\fullscreen\\new_game_plus":true,"archive\\base\\gameplay\\gui\\fullscreen\\new_game_plus\\newgameplus_select_savegame.inkwidget":true,"archive\\base\\gameplay\\gui\\fullscreen\\new_game_plus\\newgameplus_select_starting_point.inkwidget":true,"archive\\base\\gameplay\\gui\\fullscreen\\new_game_plus\\newgameplus_stats_adjustment.inkwidget":true,"archive\\base\\localization":true,"archive\\base\\localization\\en-us":true,"archive\\base\\localization\\en-us\\new_game_plus":true,"archive\\base\\localization\\en-us\\new_game_plus\\subtitles":true,"archive\\base\\localization\\en-us\\new_game_plus\\subtitles\\q001":true,"archive\\base\\localization\\en-us\\new_game_plus\\subtitles\\q001\\q001_restored_subtitles.json":true,"archive\\base\\localization\\en-us\\new_game_plus\\subtitles\\q101":true,"archive\\base\\localization\\en-us\\new_game_plus\\subtitles\\q101\\q101_restored_subtitles.json":true,"archive\\base\\localization\\en-us\\new_game_plus\\subtitles\\q116":true,"archive\\base\\localization\\en-us\\new_game_plus\\subtitles\\q116\\q116_restored_subtitles.json":true,"archive\\base\\localization\\en-us\\new_game_plus\\subtitles\\subtitles.json":true,"archive\\base\\localization\\en-us\\new_game_plus\\new_game_plus_onscreens.json":true,"archive\\base\\quest\\main_quests\\part1\\q101":true,"archive\\base\\quest\\main_quests\\part1\\q101\\scenes":true,"archive\\base\\quest\\main_quests\\part1\\q101\\scenes\\q101_08_takemura_v_room.scene":true,"archive\\base\\quest\\main_quests\\part1\\q116":true,"archive\\base\\quest\\main_quests\\part1\\q116\\scenes":true,"archive\\base\\quest\\main_quests\\part1\\q116\\scenes\\q116_05_mikoshi.scene":true,"archive\\base\\quest\\main_quests\\prologue":true,"archive\\base\\quest\\main_quests\\prologue\\q001":true,"archive\\base\\quest\\main_quests\\prologue\\q001\\scenes":true,"archive\\base\\quest\\main_quests\\prologue\\q001\\scenes\\q001_00a_before_mission.scene":true,"archive\\base\\quest\\main_quests\\prologue\\q001\\scenes\\q001_00d_leaving.scene":true,"archive\\ep1":true,"archive\\ep1\\quest":true,"archive\\ep1\\quest\\main_quests":true,"archive\\ep1\\quest\\main_quests\\q306":true,"archive\\ep1\\quest\\main_quests\\q306\\scenes":true,"archive\\ep1\\quest\\main_quests\\q306\\scenes\\q306_10_finale.scene":true,"archive\\mod\\quest":true,"archive\\mod\\quest\\changedQuests":true,"archive\\mod\\quest\\changedQuests\\open_world":true,"archive\\mod\\quest\\changedQuests\\open_world\\NewGamePlus_open_world_content.questphase":true,"archive\\mod\\quest\\changedQuests\\NewGamePlus_act_1.questphase":true,"archive\\mod\\quest\\changedQuests\\NewGamePlus_additional_game_elements.questphase":true,"archive\\mod\\quest\\changedQuests\\NewGamePlus_cyberpunk2077.questphase":true,"archive\\mod\\quest\\changedQuests\\NewGamePlus_persistent_content.questphase":true,"archive\\mod\\quest\\newgameplus":true,"archive\\mod\\quest\\newgameplus\\bossEncounterTest":true,"archive\\mod\\quest\\newgameplus\\bossEncounterTest\\bossEncounterTest.gamedef":true,"archive\\mod\\quest\\newgameplus\\bossEncounterTest\\bossEncounterTest.quest":true,"archive\\mod\\quest\\newgameplus\\journal":true,"archive\\mod\\quest\\newgameplus\\journal\\NGPlusJournal.journal":true,"archive\\mod\\quest\\newgameplus\\new_q101":true,"archive\\mod\\quest\\newgameplus\\new_q101\\Q101P2VRoom.questphase":true,"archive\\mod\\quest\\newgameplus\\new_q101\\Q101SetJohnnyStuff.questphase":true,"archive\\mod\\quest\\newgameplus\\new_q101\\new_q101.questphase":true,"archive\\mod\\quest\\newgameplus\\new_q101\\q101_01_covered_in_trash.scene":true,"archive\\mod\\quest\\newgameplus\\new_q101\\q101_p1_a_landfill.questphase":true,"archive\\mod\\quest\\newgameplus\\vrTutorialHack":true,"archive\\mod\\quest\\newgameplus\\vrTutorialHack\\VRTutorialForQ001Start.questphase":true,"archive\\mod\\quest\\newgameplus\\FastTrackToQ001.questphase":true,"archive\\mod\\quest\\newgameplus\\FastTrackToQ101New.questphase":true,"archive\\mod\\quest\\newgameplus\\FixHauntedWeaponsPreEP1.questphase":true,"archive\\mod\\quest\\newgameplus\\InitStreetStoriesAndFastTravel.questphase":true,"archive\\mod\\quest\\newgameplus\\MarkQ000AsCompleted.questphase":true,"archive\\mod\\quest\\newgameplus\\NGPlusAdditionalContent.questphase":true,"archive\\mod\\quest\\newgameplus\\NGPlusBossEncounterLoop.questphase":true,"archive\\mod\\quest\\newgameplus\\NGPlusBossEncounterRetrofix.questphase":true,"archive\\mod\\quest\\newgameplus\\RestoreHUD.questphase":true,"archive\\mod\\quest\\newgameplus\\SetBandageOutfit.questphase":true,"archive\\mod\\quest\\newgameplus\\SetPlayerProgression.questphase":true,"archive\\mod\\quest\\NewGamePlus.gamedef":true,"archive\\mod\\quest\\NewGamePlus.quest":true,"archive\\mod\\quest\\NewGamePlus_NoEP1.gamedef":true,"archive\\mod\\quest\\NewGamePlus_Q001.gamedef":true,"archive\\mod\\quest\\NewGamePlus_Q001.quest":true,"archive\\mod\\quest\\NewGamePlus_Q001_NoEP1.gamedef":true}

0 comments on commit e843ef0

Please sign in to comment.