Skip to content

Commit d5a87cc

Browse files
committed
Fix: MilkdropShader - ignore sampler keywords in shader comments (#958)
1 parent 6b1dc9c commit d5a87cc

5 files changed

Lines changed: 1018 additions & 19 deletions

File tree

src/libprojectM/MilkdropPreset/MilkdropShader.cpp

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -341,32 +341,36 @@ void MilkdropShader::PreprocessPresetShader(std::string& program)
341341

342342
// Find "sampler_state" overrides and remove them first, as they're not supported by GLSL.
343343
// The logic isn't totally fool-proof, but should work in general.
344-
found = program.find("sampler_state");
344+
// Use a comment-stripped copy for searching so commented-out sampler_state blocks are skipped.
345+
// StripComments preserves string length, so positions map 1:1 to the original.
346+
std::string stripped = Utils::StripComments(program);
347+
found = stripped.find("sampler_state");
345348
while (found != std::string::npos)
346349
{
347350
// Now go backwards and find the assignment
348-
found = program.rfind('=', found);
351+
found = stripped.rfind('=', found);
349352
auto startPos = found;
350353

351354
// Find closing brace and semicolon
352-
found = program.find('}', found);
353-
found = program.find(';', found);
355+
found = stripped.find('}', found);
356+
found = stripped.find(';', found);
354357

355358
if (found != std::string::npos)
356359
{
357-
program.replace(startPos, found - startPos, "");
360+
stripped.replace(startPos, found - startPos, "");
358361
}
359362
else
360363
{
361364
// No closing brace and semicolon.
362365
break;
363366
}
364367

365-
found = program.find("sampler_state");
368+
found = stripped.find("sampler_state");
366369
}
367370

368371
// replace shader_body with entry point function
369-
found = program.find("shader_body");
372+
// Use the stripped copy so a commented-out shader_body is not matched.
373+
found = stripped.find("shader_body");
370374
if (found != std::string::npos)
371375
{
372376
if (m_type == ShaderType::WarpShader)
@@ -430,7 +434,7 @@ void PS(float4 _vDiffuse : COLOR,
430434
switch (program.at(pos))
431435
{
432436
case '/':
433-
// Skip comments until EoL to prevent false counting
437+
// Skip line comments until EoL to prevent false counting
434438
if (pos < program.length() - 1 && program.at(pos + 1) == '/')
435439
{
436440
for (; pos < program.length(); ++pos)
@@ -441,6 +445,19 @@ void PS(float4 _vDiffuse : COLOR,
441445
}
442446
}
443447
}
448+
// Skip block comments to prevent false counting
449+
else if (pos < program.length() - 1 && program.at(pos + 1) == '*')
450+
{
451+
pos += 2;
452+
for (; pos < program.length() - 1; ++pos)
453+
{
454+
if (program.at(pos) == '*' && program.at(pos + 1) == '/')
455+
{
456+
++pos; // skip past '/'
457+
break;
458+
}
459+
}
460+
}
444461
continue;
445462

446463
case '{':
@@ -492,40 +509,43 @@ void MilkdropShader::GetReferencedSamplers(const std::string& program)
492509
// "main" should always be present.
493510
m_samplerNames.insert("main");
494511

512+
// Strip comments so that commented-out sampler/texsize declarations are not matched.
513+
std::string const stripped = Utils::StripComments(program);
514+
495515
// Search for sampler usage
496-
auto found = program.find("sampler_", 0);
516+
auto found = stripped.find("sampler_", 0);
497517
while (found != std::string::npos)
498518
{
499519
found += 8;
500-
size_t const end = program.find_first_of(" ;,\n\r)", found);
520+
size_t const end = stripped.find_first_of(" ;,\n\r)", found);
501521

502522
if (end != std::string::npos)
503523
{
504-
std::string const sampler = program.substr(static_cast<int>(found), static_cast<int>(end - found));
524+
std::string const sampler = stripped.substr(static_cast<int>(found), static_cast<int>(end - found));
505525
// Skip "sampler_state", as it's a reserved word and not a sampler.
506526
if (sampler != "state")
507527
{
508528
m_samplerNames.insert(sampler);
509529
}
510530
}
511531

512-
found = program.find("sampler_", found);
532+
found = stripped.find("sampler_", found);
513533
}
514534

515535
// Also search for texsize usage, some presets don't reference the sampler.
516-
found = program.find("texsize_", 0);
536+
found = stripped.find("texsize_", 0);
517537
while (found != std::string::npos)
518538
{
519539
found += 8;
520-
size_t const end = program.find_first_of(" ;,.\n\r)", found);
540+
size_t const end = stripped.find_first_of(" ;,.\n\r)", found);
521541

522542
if (end != std::string::npos)
523543
{
524-
std::string const sampler = program.substr(static_cast<int>(found), static_cast<int>(end - found));
544+
std::string const sampler = stripped.substr(static_cast<int>(found), static_cast<int>(end - found));
525545
m_samplerNames.insert(sampler);
526546
}
527547

528-
found = program.find("texsize_", found);
548+
found = stripped.find("texsize_", found);
529549
}
530550

531551
{
@@ -555,15 +575,15 @@ void MilkdropShader::GetReferencedSamplers(const std::string& program)
555575
}
556576
}
557577

558-
if (program.find("GetBlur3") != std::string::npos)
578+
if (stripped.find("GetBlur3") != std::string::npos)
559579
{
560580
UpdateMaxBlurLevel(BlurTexture::BlurLevel::Blur3);
561581
}
562-
else if (program.find("GetBlur2") != std::string::npos)
582+
else if (stripped.find("GetBlur2") != std::string::npos)
563583
{
564584
UpdateMaxBlurLevel(BlurTexture::BlurLevel::Blur2);
565585
}
566-
else if (program.find("GetBlur1") != std::string::npos)
586+
else if (stripped.find("GetBlur1") != std::string::npos)
567587
{
568588
UpdateMaxBlurLevel(BlurTexture::BlurLevel::Blur1);
569589
}

src/libprojectM/Utils.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,53 @@ void ToUpperInPlace(std::string& str)
2929
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
3030
}
3131

32+
auto StripComments(const std::string& source) -> std::string
33+
{
34+
std::string result = source;
35+
size_t i = 0;
36+
37+
while (i < result.size())
38+
{
39+
if (i + 1 < result.size() && result.at(i) == '/' && result.at(i + 1) == '/')
40+
{
41+
// Line comment: replace until end of line
42+
while (i < result.size() && result.at(i) != '\n' && result.at(i) != '\r')
43+
{
44+
result.at(i) = ' ';
45+
i++;
46+
}
47+
}
48+
else if (i + 1 < result.size() && result.at(i) == '/' && result.at(i + 1) == '*')
49+
{
50+
// Block comment: replace until closing */
51+
result.at(i) = ' ';
52+
result.at(i + 1) = ' ';
53+
i += 2;
54+
while (i < result.size())
55+
{
56+
if (i + 1 < result.size() && result.at(i) == '*' && result.at(i + 1) == '/')
57+
{
58+
result.at(i) = ' ';
59+
result.at(i + 1) = ' ';
60+
i += 2;
61+
break;
62+
}
63+
// Preserve newlines to keep line structure intact
64+
if (result.at(i) != '\n' && result.at(i) != '\r')
65+
{
66+
result.at(i) = ' ';
67+
}
68+
i++;
69+
}
70+
}
71+
else
72+
{
73+
i++;
74+
}
75+
}
76+
77+
return result;
78+
}
79+
3280
} // namespace Utils
3381
} // namespace libprojectM

src/libprojectM/Utils.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,16 @@ auto ToUpper(const std::string& str) -> std::string;
1111
void ToLowerInPlace(std::string& str);
1212
void ToUpperInPlace(std::string& str);
1313

14+
/**
15+
* @brief Strips C and C++ style comments from source code.
16+
*
17+
* Replaces // line comments and block comments with spaces, preserving
18+
* string length and newline positions so that character offsets remain valid.
19+
*
20+
* @param source The source code string to strip comments from.
21+
* @return A copy of the source with all comment content replaced by spaces.
22+
*/
23+
auto StripComments(const std::string& source) -> std::string;
24+
1425
} // namespace Utils
1526
} // namespace libprojectM

tests/libprojectM/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ find_package(GTest 1.10 REQUIRED NO_MODULE)
33
add_executable(projectM-unittest
44
WaveformAlignerTest.cpp
55
PresetFileParserTest.cpp
6+
MilkdropShaderCommentParsingTest.cpp
67

78
$<TARGET_OBJECTS:Audio>
89
$<TARGET_OBJECTS:MilkdropPreset>

0 commit comments

Comments
 (0)