Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing value #29

Closed
Nordgaren opened this issue Jun 4, 2021 · 15 comments
Closed

Changing value #29

Nordgaren opened this issue Jun 4, 2021 · 15 comments

Comments

@Nordgaren
Copy link

I have been able to narrow down the values I want to change, but haven't been able to figure out how to cast it to the VToken.

trying to do something like this

foreach (var value in ((Gameloop.Vdf.Linq.VProperty)game).Value)
{
        Console.WriteLine(value);
        ((Gameloop.Vdf.Linq.VProperty)game).Value = "controller_ps4_gamepad_joystick.vdf";
}

Thank you in advance!

@shravan2x
Copy link
Owner

shravan2x commented Jun 4, 2021

Just create a wrapper VValue (which is a subtype of VToken) - new VValue("controller_ps4_gamepad_joystick.vdf").

@Nordgaren
Copy link
Author

Awesome, that works! I'm also trying to figure out how to add new objects or properties if they don't exist. Would you be able to point me in the right direction for that?

Thank you, again!

@shravan2x
Copy link
Owner

Vdf.NET is modeled after Json.NET. Most of the logic remains the same - https://stackoverflow.com/questions/15413825/how-do-you-add-a-jtoken-to-an-jobject. Create any new VProperty and .Add it to any VObject. A VProperty's value can be another VObject as it is a subtype of VToken.

@Nordgaren
Copy link
Author

Awesome. So I mguessing the final step here is to reserialize the the object (volvo) and then write that to a file, right?

@shravan2x
Copy link
Owner

Just depends on what you want to do with it. This library only deals with the data format and serialization/deserialization.

@Nordgaren
Copy link
Author

Nordgaren commented Jun 4, 2021

I just wanted to rewrite the file with the new entries I added.

When I do this, though, all of the properties in the file lose their proper spacing. I have tried setting the serialize settings, as well, but that didn't help.

example of how it looks originally

	"streaming_v2"
	{
		"EnableStreaming"		"1"
		"DefaultAudioDevice"		"{0.0.0.00000000}.{6506e0de-e7f3-472a-b94d-11e37193d798}"
		"DefaultAudioDeviceName"		"Realtek Digital Output (Realtek(R) Audio)"
		"DefaultMicrophone"		"{0.0.1.00000000}.{6ec97002-0153-490e-bc38-ef0c5fefb802}"
		"DefaultMicrophoneName"		"Analogue 1 + 2 (2- Focusrite Usb Audio)"
		"P2PScope"		"2"
		"ClientConfig"		"900102"
		"P2PScopeV2"		"0"
	}

how it looks after

	"streaming_v2"
	{
		"EnableStreaming" "1"
		"DefaultAudioDevice" "{0.0.0.00000000}.{6506e0de-e7f3-472a-b94d-11e37193d798}"
		"DefaultAudioDeviceName" "Realtek Digital Output (Realtek(R) Audio)"
		"DefaultMicrophone" "{0.0.1.00000000}.{6ec97002-0153-490e-bc38-ef0c5fefb802}"
		"DefaultMicrophoneName" "Analogue 1 + 2 (2- Focusrite Usb Audio)"
		"P2PScope" "2"
		"ClientConfig" "900102"
		"P2PScopeV2" "0"
	}

I haven't changed any of these properties.

@Nordgaren
Copy link
Author

Actually, I just found out something unfortunate. It seems that even if I were to be able to rewrite the file, Steam as some kind of feature that checks if the file has been modified and restores it.

Do you know if there is a way around this? I am editing the localconfig.vdf in F:\Steam\userdata{user}\config

@shravan2x
Copy link
Owner

Vdf.NET doesn't store whitespace information on deserialization. So it cannot be recreated when serializing.

https://github.com/shravan2x/Gameloop.Vdf/issues?q=localconfig

@Nordgaren
Copy link
Author

Nordgaren commented Jun 4, 2021

so I make my changes like so:

              foreach (var value in ((VProperty)game).Value)
             {
             ((VProperty)value).Key = "template";
             ((VProperty)value).Value = new VValue($"controller_{system}_gamepad_joystick.vdf");
             Console.WriteLine("changed");
             }

and write like this according to what you said in the other thread

            File.WriteAllText(file[0], VdfConvert.Serialize(volvo), Encoding.UTF8);

but steam does actually overwrite the changes I made. Is this new?

Also, does steam not care about these whitespaces, if it is possible to change these files?

I've tried changing them on two separate computers to no avail.

@Nordgaren
Copy link
Author

Nordgaren commented Jun 5, 2021

Vdf.NET doesn't store whitespace information on deserialization. So it cannot be recreated when serializing.

BTW, I think this behavior could possibly be patched in, as it seems that the whitespace is just two tabs, and it seems to only apply when it's in a line that's written with a key-value pair.

Possibly change the VDF text writer to ad two tabs to the value, and possibly check if it's a key value pair being written in one line? IDK.

Thought I would mention it, and I might mess around with the source code later to see if I can.

Anyways, thanks for your time!

Edit: I notice there is a whitespace between those pairs, and if you replace all of them with \t\t that could also work. I just don't know if that would work for all files, which is why I suggest it here.

in fact, this code is how I solved it temporarily.

            var replaceWhitespace = VdfConvert.Serialize(volvo);
            replaceWhitespace = replaceWhitespace.Replace("\" \"", "\t\t");
            File.WriteAllText(file[0], replaceWhitespace, Encoding.UTF8);

@Nordgaren
Copy link
Author

Nordgaren commented Jun 5, 2021

Okay, so I wrote some code to test this, and it seems like the PR I sent you works for most steam files.

Basically what I did was I created an original and modified folder on my desktop, and put all my steam.vdf files in the original one. I deserialized every files and then serialized and wrote it back.

I then ran each file through a checksum comparison and printed out the files it didn't work for.

Of-course, if I just serialize and write it, none of them match.

Maybe you know a more elegant way to put in the tabs?

:)

Here are the results

\CountryList.vdf
\empty.vdf
\gamepad+mouse.vdf
\gamepad_fps.vdf
\gamepad_generic.vdf
\gamepad_joystick.vdf
\gamepad_mouse_gyro.vdf
\gameserverconfig.vdf
\helpfile.vdf
\helpfile_adminmod.vdf
\helpfile_cstrike.vdf
\installscript.vdf
\launcher.vdf
\localconfig.vdf
\localization.vdf
\mainserverconfig.vdf
\registrykeys.vdf
\rules.vdf
\screenshots.vdf
\steamdesktop.vdf
\streaming_steamdesktop.vdf
\videoplayer.vdf
\wasd.vdf
Match: 111
No Match: 23

Here is the code

static void Main(string[] args)
        {
            var files = Directory.GetFiles(@"C:\Users\Nordgaren\Desktop\VDFExperiment\Original\");

            foreach (var file in files)
            {
                try
                {
                    dynamic vdf = VdfConvert.Deserialize(File.ReadAllText(file));
                    var rewritten = file.Replace("Original", "Modified");
                    File.WriteAllText(rewritten, VdfConvert.Serialize(vdf), Encoding.UTF8);

                }
                catch
                {

                }
                
            }

            int isTrue = 0;
            int isFalse = 0;
            foreach (var file in files)
            {
                var rewritten = file.Replace("Original", "Modified");
                var og = GetChecksum(file);
                var md = GetChecksum(rewritten);

                if (og == md)
                    isTrue++;

                if (og != md)
                {
                    isFalse++;
                    Console.WriteLine(file);
                }
            }
            Console.WriteLine($"Match: {isTrue}");
            Console.WriteLine($"No Match: {isFalse}");
            Console.ReadLine();
        }

        public static string GetChecksum(string filename)
        {
            using (var hasher = HashAlgorithm.Create("SHA256"))
            {
                using (var stream = File.OpenRead(filename))
                {
                    var hash = hasher.ComputeHash(stream);
                    return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
                }
            }
        }

@Nordgaren
Copy link
Author

It seems that you'd need to somehow add these values when creating the key value pairs on the single line. I don't know if that even makes sense.

I also found a bug that has more to do with steam being silly than anything. If you look at your Steam/cached/countrylist.vdf you'll see that there are no quotes on the top object or property, like most files. If you deserialize and reserialize, it shows up with the quotes.

Kinda funny. Interesting things valve does.

@shravan2x
Copy link
Owner

I don't see a PR from you. Did you forget to send one?

Also, does steam not care about these whitespaces, if it is possible to change these files?

In any case that I'm aware of, Valve's parser does not care about whitespace. I'm skeptical that the issue you're facing is caused by whitespace differences, unless there has been a recent regression.

Could you confirm that the behavior you were seeing before (Steam reverting changes) no longer occurs when you use tabs instead of spaces? If this is the case, could you include specific repro steps so I can confirm it on my end too?

Basically what I did was I created an original and modified folder on my desktop, and put all my steam.vdf files in the original one. I deserialized every files and then serialized and wrote it back.

I wouldn't expect the re-serialized files to have their hashes match - for this to be the case, every byte needs to be the exact same as before. In general, it's not the goal of Vdf.NET to produce exact re-serializations. I think whitespace modifications are ok as long as they don't trip up Valve's own parser (there's still some old copies of their code floating around).

I also found a bug that has more to do with steam being silly than anything. If you look at your Steam/cached/countrylist.vdf you'll see that there are no quotes on the top object or property, like most files. If you deserialize and reserialize, it shows up with the quotes.

Valve is not very consistent in their data formats, it's up to us to deal with it. This library supports reading-in unquoted tokens but does not store that as a quoted/unquoted flag. There just didn't seem to be a need for it. If for some reason this becomes an issue I'll be happy to add it in.

@Nordgaren
Copy link
Author

I don't see a PR from you. Did you forget to send one?

Also, does steam not care about these whitespaces, if it is possible to change these files?

In any case that I'm aware of, Valve's parser does not care about whitespace. I'm skeptical that the issue you're facing is caused by whitespace differences, unless there has been a recent regression.

Could you confirm that the behavior you were seeing before (Steam reverting changes) no longer occurs when you use tabs instead of spaces? If this is the case, could you include specific repro steps so I can confirm it on my end too?

Basically what I did was I created an original and modified folder on my desktop, and put all my steam.vdf files in the original one. I deserialized every files and then serialized and wrote it back.

I wouldn't expect the re-serialized files to have their hashes match - for this to be the case, every byte needs to be the exact same as before. In general, it's not the goal of Vdf.NET to produce exact re-serializations. I think whitespace modifications are ok as long as they don't trip up Valve's own parser (there's still some old copies of their code floating around).

I also found a bug that has more to do with steam being silly than anything. If you look at your Steam/cached/countrylist.vdf you'll see that there are no quotes on the top object or property, like most files. If you deserialize and reserialize, it shows up with the quotes.

Valve is not very consistent in their data formats, it's up to us to deal with it. This library supports reading-in unquoted tokens but does not store that as a quoted/unquoted flag. There just didn't seem to be a need for it. If for some reason this becomes an issue I'll be happy to add it in.

No, I think this is a thing steam has been doing if you use their cloud service, which means that changing the vdf in the case I want to use it, it wouldn't be guaranteed to work.

Here's one thread I found that described the behavior as well, and there were quite a few more I found the other night. I can go looking for them again, if you'd like.
https://steamcommunity.com/discussions/forum/0/1747893292776876733/

So I agree that it's probably not the whitespace that is the problem. It's steams own internal services. I used Notepad++ to confirm this. I changed the value in the file, booted up a game and Notepad++ immediately notified that the file had been updated, so I click yes to reload the file and see the change happen.

If, in the future, someone does need to add the tabs, the best solution without making changes to the library is the code that I posted (and updated) above.

I'm new to GitHub, and programming in general, so I'll just post the simple change I made here.

I changed the serialize return, basically, to do the replacement instead of having to serialize to a string and do the replacement that way. You can test it out if you'd like. I'm pretty sure the tabs would only be in a place where there's two quotation marks surrounding a whitespace, but I'm not 100% on this, as I only opened and looked at a few files.

        public static string Serialize(VToken value, VdfSerializerSettings settings)
        {
            if (value == null)
                throw new ArgumentNullException(nameof(value));

            StringBuilder stringBuilder = new StringBuilder(256);
            StringWriter stringWriter = new StringWriter(stringBuilder, CultureInfo.InvariantCulture);
            (new VdfSerializer(settings)).Serialize(stringWriter, value);

            return stringWriter.ToString().Replace("\" \"", "\t\t");
        }

Thanks again for the convo. I learned some stuff from this.

@shravan2x
Copy link
Owner

No, I think this is a thing steam has been doing if you use their cloud service, which means that changing the vdf in the case I want to use it, it wouldn't be guaranteed to work.

Thanks for confirming, I wasn't familiar with that particular behavior. You might want to combine updating the VDF with sending the new copy to Steam's servers. That would require working with protobufs using SteamKit.

If, in the future, someone does need to add the tabs, the best solution without making changes to the library is the code that I posted (and updated) above.

Simply using .Replace would likely affect performance too much as a whole new string will be reallocated. We would need to update this line in VdfTextWriter and add a new option in VdfSerializerSettings so the existing behavior remains available as well. I'd rather hold off on doing all this until there's a solid need for it.

Thanks again for the convo. I learned some stuff from this.

Happy to help! I'll go ahead and close this issue now but feel free to comment if there's anything else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants