multitheftauto / mtasa-blue

Multi Theft Auto is a game engine that incorporates an extendable network play element into a proprietary commercial single-player game.
https://multitheftauto.com
GNU General Public License v3.0
1.42k stars 437 forks source link

File doesn't exist @ dxCreateShader; When using long-string as raw-data in certain ways #3825

Open Daniheee opened 1 month ago

Daniheee commented 1 month ago

Describe the bug

So this is the main script, it obviously runs:

local shader1 = dxCreateShader([[
    float4 asd():COLOR0 {
        return 0;
    }

    technique {
        pass {
            PixelShader = compile ps_2_0 asd();
        }
    }
]])
iprint("shader1",shader1)

And if you remove the unnecessary whitespaces, it will still run perfectly:

local shader2 = dxCreateShader([[float4 asd():COLOR0{return 0;}technique {pass {PixelShader=compile ps_2_0 asd();}}]])
iprint("shader2",shader2)

But if you remove the extra whitespaces after the word 'technique' or 'pass,' it will throw a 'file doesn't exist' error:

local shader3 = dxCreateShader([[float4 asd():COLOR0{return 0;}technique {pass{PixelShader=compile ps_2_0 asd();}}]])
iprint("shader3",shader3)

OR

local shader4 = dxCreateShader([[float4 asd():COLOR0{return 0;}technique{pass {PixelShader=compile ps_2_0 asd();}}]])
iprint("shader4",shader4)

However, if you remove every whitespace, even after 'technique' and 'pass', it can still run, if you use it in a single line like this:

local shader5 = dxCreateShader([[
    float4 asd():COLOR0{return 0;}technique{pass{PixelShader=compile ps_2_0 asd();}}
]])
iprint("shader5",shader5)

This way the shader will be created again without any problem.

Steps to reproduce

  1. Just run the provided code snippets in any client side script.

Version

Client: v1.6-release-22780 (Windows 64-bit) Server: v1.6-release-22780 (Windows 64-bit)

Additional context

My debug script results are shown here for better illustration: Image

Relevant log output

No response

Security Policy

TracerDS commented 1 month ago

I see the issue

if (!bValidFilePath || (strFile[0] != '@' && strFile[0] != ':'))
{
    bIsRawData = strFile.find("\n") != std::string::npos;

    if (!bIsRawData)
    {
        bIsRawData = (strFile.find("technique ") != std::string::npos) && (strFile.find("pass ") != std::string::npos) &&
                     (strFile.find('{') != std::string::npos) && (strFile.find('}') != std::string::npos);
    }
}

Maybe it should not check for an end line 😆

Lpsd commented 1 month ago

That's how you check for a match from find - std::string::npos is -1, find returns -1 when no match is found.

If you're referring to the \n check, that's because a filename wouldn't have a newline, therefore it has to be raw data.

I think the solution here is to remove the checks for technique and pass entirely. You can't have curly braces in a Windows filename so there's no clash possible when curly braces are being checked to determine if it's raw data or not.

TracerDS commented 1 month ago

That's how you check for a match from find - std::string::npos is -1, find returns -1 when no match is found.

If you're referring to the \n check, that's because a filename wouldn't have a newline, therefore it has to be raw data.

I think the solution here is to remove the checks for technique and pass entirely. You can't have curly braces in a Windows filename so there's no clash possible when curly braces are being checked to determine if it's raw data or not.

Raw data can also not have new line as shown above. Thats not an ideal solution. You also can have curly braces and semicolons in windows filenames

Lpsd commented 1 month ago

Raw data can have newlines. The author says the first example works, and I've also done it myself before.

You also can have curly braces and semicolons in windows filenames

Yeah true for some reason I thought they weren't allowed. Anyway, I think it's unlikely someone tries to load a shader with filename containing technique + pass + { + }

What is to stop them currently having a filename called technique pass { } anyway, makes sense to just remove the spaces from the find imo.

TracerDS commented 1 month ago

cant we just have it all under one if statement?

bIsRawData = strFile.find("\n") != std::string::npos || (strFile.find("technique ") != std::string::npos
         && strFile.find("pass ") != std::string::npos && strFile.find('{') != std::string::npos
         && strFile.find('}') != std::string::npos);
Lpsd commented 1 month ago

Sounds fine, but you'll want to remove the spaces from technique and pass to resolve the author's issue.

Fernando-A-Rocha commented 1 month ago

cant we just have it all under one if statement?

bIsRawData = strFile.find("\n") != std::string::npos || (strFile.find("technique ") != std::string::npos && strFile.find("pass ") != std::string::npos && strFile.find('{') != std::string::npos && strFile.find('}') != std::string::npos);

Wouldn't finding for { and } be enough to identify raw data instead of path?

TracerDS commented 1 month ago

Wouldn't finding for { and } be enough to identify raw data instead of path?

{}.txt is a valid file name. If {}.txt is a valid file name then {} is also a valid file name. You can see the issue here. The best way would be to introduce a boolean flag isRawData and then parse the shader according to that.

Fernando-A-Rocha commented 1 month ago

My bad I didn't realize file names can have {}