diff --git a/src/Etterna/MinaCalc/MinaCalc.cpp b/src/Etterna/MinaCalc/MinaCalc.cpp index da72679dd8..c824a82550 100644 --- a/src/Etterna/MinaCalc/MinaCalc.cpp +++ b/src/Etterna/MinaCalc/MinaCalc.cpp @@ -50,7 +50,8 @@ TotalMaxPoints(const Calc& calc) -> float auto Calc::CalcMain(const std::vector& NoteInfo, const float music_rate, - const float score_goal) -> std::vector + const float score_goal, + const std::string& filename) -> std::vector { InitializeKeycountLogic(); @@ -65,7 +66,7 @@ Calc::CalcMain(const std::vector& NoteInfo, ++cur_iteration) { const auto skip = InitializeHands( - NoteInfo, music_rate, 0.1F * static_cast(cur_iteration)); + NoteInfo, music_rate, 0.1F * static_cast(cur_iteration), filename); // if we exceed max_rows_for_single_interval during processing if (skip) { @@ -529,10 +530,11 @@ Calc::InitializeKeycountLogic() -> void auto Calc::InitializeHands(const std::vector& NoteInfo, const float music_rate, - const float offset) -> bool + const float offset, + const std::string& filename) -> bool { // do we skip this file? - if (fast_walk_and_check_for_skip(NoteInfo, music_rate, *this, offset)) + if (fast_walk_and_check_for_skip(NoteInfo, music_rate, *this, offset, filename)) return true; // if debug, force params to load and reset pmods and base diffs @@ -873,7 +875,8 @@ MinaSDCalc(const std::vector& NoteInfo, const float musicrate, const float goal, const unsigned keycount, - Calc* calc) -> std::vector + Calc* calc, + const std::string& filename) -> std::vector { if (NoteInfo.size() <= 1) { return dimples_the_all_zero_output; @@ -882,14 +885,15 @@ MinaSDCalc(const std::vector& NoteInfo, calc->debugmode = false; calc->keycount = keycount; - return calc->CalcMain(NoteInfo, musicrate, min(goal, ssr_goal_cap)); + return calc->CalcMain(NoteInfo, musicrate, min(goal, ssr_goal_cap), filename); } // Wrap difficulty calculation for all standard rates auto MinaSDCalc(const std::vector& NoteInfo, const unsigned keycount, - Calc* calc) -> MinaSD + Calc* calc, + const std::string& filename) -> MinaSD { MinaSD allrates; const auto lower_rate = 7; // 0.7x @@ -901,7 +905,7 @@ MinaSDCalc(const std::vector& NoteInfo, calc->keycount = keycount; for (auto i = lower_rate; i < upper_rate; i++) { allrates.emplace_back(calc->CalcMain( - NoteInfo, static_cast(i) / 10.F, default_score_goal)); + NoteInfo, static_cast(i) / 10.F, default_score_goal, filename)); } } else { for (auto i = lower_rate; i < upper_rate; i++) { @@ -920,7 +924,8 @@ MinaSDCalcDebug( const unsigned keycount, std::vector>>>& handInfo, std::vector& debugstrings, - Calc& calc) + Calc& calc, + const std::string& filename) { if (NoteInfo.size() <= 1) { return; @@ -930,7 +935,7 @@ MinaSDCalcDebug( calc.debugmode = true; calc.ssr = true; calc.keycount = keycount; - calc.CalcMain(NoteInfo, musicrate, min(goal, ssr_goal_cap)); + calc.CalcMain(NoteInfo, musicrate, min(goal, ssr_goal_cap), filename); make_debug_strings(calc, debugstrings); handInfo.emplace_back(calc.debugValues.at(left_hand)); diff --git a/src/Etterna/MinaCalc/MinaCalc.h b/src/Etterna/MinaCalc/MinaCalc.h index 51ff4b0bdd..199a79e9dc 100644 --- a/src/Etterna/MinaCalc/MinaCalc.h +++ b/src/Etterna/MinaCalc/MinaCalc.h @@ -89,7 +89,8 @@ class Calc */ auto CalcMain(const std::vector& NoteInfo, float music_rate, - float score_goal) -> std::vector; + float score_goal, + const std::string& filename) -> std::vector; /// For debug output. Should only ever be true at music select. bool debugmode = false; @@ -125,7 +126,8 @@ class Calc auto InitializeHands( const std::vector& NoteInfo, float music_rate, - float offset) -> bool; + float offset, + const std::string& filename) -> bool; /** Returns estimate of player skill needed to achieve score goal on chart. * The player_skill parameter gives an initial guess and floor for player @@ -328,25 +330,31 @@ class Calc /// Given score value percentage - 1.0 is 100% /// Pointer to the Calc instance to use /// if using threads or a centralized Calc. +/// The filename of the chart, used for displaying +// error messages if the file is invalid /// A list of the resulting skillset values. MINACALC_API auto MinaSDCalc(const std::vector& NoteInfo, float musicrate, float goal, const unsigned keycount, - Calc* calc) -> std::vector; + Calc* calc, + const std::string& filename) -> std::vector; /// /// Calc driving function used for generating skillset values for caching. /// /// Output from NoteData::SerializeNoteData2 /// Pointer to the Calc instance to use /// if using threads or a centralized Calc. +/// The filename of the chart, used for displaying +// error messages if the file is invalid /// MinaSD, a list of the resulting skillset values, /// for every rate. MINACALC_API auto MinaSDCalc(const std::vector& NoteInfo, const unsigned keycount, - Calc* calc) -> MinaSD; + Calc* calc, + const std::string& filename) -> MinaSD; /// /// Calc driving function used for generating skillset values for debugging. /// Works the same as the score-based MinaSDCalc, but runs debug mode. @@ -359,6 +367,8 @@ MinaSDCalc(const std::vector& NoteInfo, /// representation of the notes in every interval /// Pointer to the calc instance to use /// if using threads or a centralized Calc. +/// The filename of the chart, used for displaying +// error messages if the file is invalid MINACALC_API void MinaSDCalcDebug( const std::vector& NoteInfo, @@ -367,7 +377,8 @@ MinaSDCalcDebug( const unsigned keycount, std::vector>>>& handInfo, std::vector& debugstrings, - Calc& calc); + Calc& calc, + const std::string& filename); /// External access to the internally defined calculator version number. MINACALC_API auto GetCalcVersion() -> int; diff --git a/src/Etterna/MinaCalc/SequencingHelpers.h b/src/Etterna/MinaCalc/SequencingHelpers.h index 66a3c011b4..920d58814d 100644 --- a/src/Etterna/MinaCalc/SequencingHelpers.h +++ b/src/Etterna/MinaCalc/SequencingHelpers.h @@ -41,7 +41,8 @@ inline auto left_mask(const unsigned& keycount) -> unsigned { const auto m = right_mask(keycount); - return ~m & static_cast(std::exp2(std::ceil(std::log2(m))) - 1); + // Use keycount_to_bin to get the proper mask size instead of exp2/log2 + return ~m & keycount_to_bin(keycount); } // outputs 0b1111 for 4, 0b101 for 3, etc diff --git a/src/Etterna/MinaCalc/UlbuAcolytes.h b/src/Etterna/MinaCalc/UlbuAcolytes.h index 7fb34e4114..a88d01d7da 100644 --- a/src/Etterna/MinaCalc/UlbuAcolytes.h +++ b/src/Etterna/MinaCalc/UlbuAcolytes.h @@ -2,6 +2,8 @@ #include #include #include +#include +#include /* PRAISE ULBU FOR IT IS ITS GLORY THAT GIVES OUR LIVES MEANING */ @@ -132,7 +134,8 @@ inline auto fast_walk_and_check_for_skip(const std::vector& ni, const float& rate, Calc& calc, - const float& offset = 0.F) -> bool + const float& offset = 0.F, + const std::string& filename = "") -> bool { // an inf rowtime means 0 bpm or some other odd gimmick that may break things // skip this file @@ -243,8 +246,25 @@ fast_walk_and_check_for_skip(const std::vector& ni, // make sure row_count adds up... // this validates that the mask is correct - assert(nri.hand_counts[left_hand] + nri.hand_counts[right_hand] == - nri.row_count); + if (nri.hand_counts[left_hand] + nri.hand_counts[right_hand] != nri.row_count) { + // Print detailed debugging information + std::cerr << "[FATAL]: Hand count mismatch detected!\n"; + if (!filename.empty()) { + std::cerr << " File: " << filename << "\n"; + } + std::cerr << " Keycount: " << calc.keycount << "\n"; + std::cerr << " Row notes (binary): " << std::bitset<32>(ri.notes) << "\n"; + std::cerr << " Row count: " << nri.row_count << "\n"; + std::cerr << " Left hand count: " << nri.hand_counts[left_hand] << "\n"; + std::cerr << " Right hand count: " << nri.hand_counts[right_hand] << "\n"; + std::cerr << " Left hand mask: " << std::bitset<32>(left_hand_mask) << "\n"; + std::cerr << " Right hand mask: " << std::bitset<32>(right_hand_mask) << "\n"; + std::cerr << " All columns without middle: " << std::bitset<32>(all_columns_without_middle) << "\n"; + std::cerr << " Row time: " << scaled_time << "\n"; + std::cerr << " Interval: " << itv << ", Row: " << row_counter << "\n"; + std::cerr << " Expected: " << nri.hand_counts[left_hand] + nri.hand_counts[right_hand] << " == " << nri.row_count << "\n"; + assert(false); + } ++row_counter; } diff --git a/src/Etterna/Models/Misc/PlayerStageStats.cpp b/src/Etterna/Models/Misc/PlayerStageStats.cpp index 2c5676a04e..9bc6f528e9 100644 --- a/src/Etterna/Models/Misc/PlayerStageStats.cpp +++ b/src/Etterna/Models/Misc/PlayerStageStats.cpp @@ -346,7 +346,7 @@ PlayerStageStats::CalcSSR(float ssrpercent) const const unsigned columnCount = GAMEMAN->GetStepsTypeInfo(steps->m_StepsType).iNumTracks; return MinaSDCalc( - serializednd, musicrate, ssrpercent, columnCount, SONGMAN->calc.get()); + serializednd, musicrate, ssrpercent, columnCount, SONGMAN->calc.get(), steps->GetFilename()); } float diff --git a/src/Etterna/Models/StepsAndStyles/Steps.cpp b/src/Etterna/Models/StepsAndStyles/Steps.cpp index abd51e2125..fa6c319f07 100644 --- a/src/Etterna/Models/StepsAndStyles/Steps.cpp +++ b/src/Etterna/Models/StepsAndStyles/Steps.cpp @@ -431,9 +431,9 @@ Steps::CalcEtternaMetadata(Calc* calc) GAMEMAN->GetStepsTypeInfo(m_StepsType).iNumTracks; if (calc == nullptr) { // reloading at music select - diffByRate = MinaSDCalc(cereal, columnCount, SONGMAN->calc.get()); + diffByRate = MinaSDCalc(cereal, columnCount, SONGMAN->calc.get(), GetFilename()); } else { - diffByRate = MinaSDCalc(cereal, columnCount, calc); + diffByRate = MinaSDCalc(cereal, columnCount, calc, GetFilename()); } ChartKey = GenerateChartKey(*m_pNoteData, GetTimingData()); @@ -465,7 +465,7 @@ Steps::DoATestThing(float ev, Skillset ss, float rate, Calc* calc) -> float const auto& etaner = GetTimingData()->BuildAndGetEtaner(nerv); const auto& cereal = m_pNoteData->SerializeNoteData(etaner); - auto newcalc = MinaSDCalc(cereal, rate, 0.93F, 4, calc); + auto newcalc = MinaSDCalc(cereal, rate, 0.93F, 4, calc, GetFilename()); auto last_msd = newcalc[ss]; const auto prev_vers = GetCalcVersion() - 1; if (vh.count(prev_vers) != 0U) { @@ -509,7 +509,8 @@ Steps::GetCalcDebugOutput() columnCount, calcdebugoutput, debugstrings, - *SONGMAN->calc); + *SONGMAN->calc, + GetFilename()); m_pNoteData->UnsetNerv(); m_pNoteData->UnsetSerializedNoteData(); @@ -971,7 +972,7 @@ class LunaSteps : public Luna const unsigned columnCount = GAMEMAN->GetStepsTypeInfo(p->m_StepsType).iNumTracks; - d = MinaSDCalc(ni, rate, goal, columnCount, SONGMAN->calc.get()); + d = MinaSDCalc(ni, rate, goal, columnCount, SONGMAN->calc.get(), p->GetFilename()); const auto ssrs = d; LuaHelpers::CreateTableFromArray(ssrs, L); diff --git a/src/Etterna/Singletons/ScoreManager.cpp b/src/Etterna/Singletons/ScoreManager.cpp index 1a8b31f4a6..4292519f64 100644 --- a/src/Etterna/Singletons/ScoreManager.cpp +++ b/src/Etterna/Singletons/ScoreManager.cpp @@ -676,7 +676,8 @@ ScoreManager::RecalculateSSRs(LoadingWindow* ld) musicrate, ssrpercent, columnCount, - per_thread_calc.get()); + per_thread_calc.get(), + steps->GetFilename()); auto ssrVals = dakine; FOREACH_ENUM(Skillset, ss) @@ -841,7 +842,8 @@ ScoreManager::RecalculateSSRs(const std::string& profileID) musicrate, ssrpercent, columnCount, - per_thread_calc.get()); + per_thread_calc.get(), + steps->GetFilename()); auto ssrVals = dakine; FOREACH_ENUM(Skillset, ss)