Quakespasm icon indicating copy to clipboard operation
Quakespasm copied to clipboard

Fixed: backslash characters in savegames skips string chars

Open mankrip opened this issue 1 year ago • 11 comments

ED_NewString had a bug in its newline parser, which always skipped the first character after a backslash even when it's not the 'n' char. This resulted in string corruption across subsequent saves & loads in the same gameplay session.

mankrip avatar Feb 21 '24 11:02 mankrip

Patch is utterly broken: looks like copy+paste'd from an incompatible work.

sezero avatar Feb 21 '24 12:02 sezero

Patch is utterly broken: looks like copy+paste'd from an incompatible work.

Utterly broken how?

This function in QuakeSpasm is identical to its vanilla Quake code, and its only purpose is to convert escape sequenced newlines into newline characters. It doesn't try to convert any other escaped characters, in which case the characters should be parsed raw. This is all this patch does, it doesn't break anything.

mankrip avatar Feb 21 '24 12:02 mankrip

Utterly broken how?

There is no local new variable here, the procedure had been changed long ago to call PR_AllocString and return the string num instead of the string itself and your patch reverts all of that and leaves num uninitialized.

This function in QuakeSpasm is identical to its vanilla Quake code,

No it is not: see above

and its only purpose is to convert escape sequenced newlines into newline characters. It doesn't try to convert any other escaped characters, in which case the characters should be parsed raw. This is all this patch does, it doesn't break anything.

Did you even attempt to compile?

In any case, the actual intended change should be something like the following I think:

diff --git a/Quake/pr_edict.c b/Quake/pr_edict.c
index cc12171..0aed418 100644
--- a/Quake/pr_edict.c
+++ b/Quake/pr_edict.c
@@ -769,13 +769,11 @@ static string_t ED_NewString (const char *string)
 
 	for (i = 0; i < l; i++)
 	{
-		if (string[i] == '\\' && i < l-1)
+		if (i < l-1 && string[i] == '\\' && string[i + 1] == 'n')
 		{
+			// convert both characters into a single newline character
 			i++;
-			if (string[i] == 'n')
-				*new_p++ = '\n';
-			else
-				*new_p++ = '\\';
+			*new_p++ = '\n';
 		}
 		else
 			*new_p++ = string[i];

sezero avatar Feb 21 '24 12:02 sezero

There is no local new variable here, the procedure had been changed long ago to call PR_AllocString and return the string num instead of the string itself and your patch reverts all of that and leaves num uninitialized.

I see now, you're right. I copied the entire function from my code without the variable declarations (which I didn't modify), when I should have copied just the for loop. Sorry for my mistake, and thanks for pointing it out. :)

mankrip avatar Feb 21 '24 12:02 mankrip

OK.

In any case, @ericwa: Should we apply the patch (the corrected one I inlined above) to mainstream?

sezero avatar Feb 21 '24 15:02 sezero

From what I can see, the old code converts:

  1. \n to linefeed
  2. \\ to \
  3. \ followed by any other char to \

Case 3 does seem sketchy, but the patch in https://github.com/ericwa/Quakespasm/pull/17#issuecomment-1956565862 drops cases 2. and 3. I'm not sure this is obviously the correct thing to do, why are we dropping case 2? Could there be side effects from dropping case 3?

I'm lacking the context for where ED_NewString is run in the save/load process at the moment.

ericwa avatar Feb 22 '24 01:02 ericwa

@mankrip: I'm not applying the patch for now, per @ericwa's comments. If there is a reproducible case to demonstrate the issue, it'd be great.

sezero avatar Feb 23 '24 07:02 sezero

Start the game, then load a map using this command: map ..\..\mod_dir\maps\mapname

Create a savegame, then load it:

save path_test.sav
load path_test.sav

Now save again: save path_test_2.sav

And compare both saves in a text editor. The "mapname" global in the second save is corrupted: "mapname" "..\.\od_dir\aps\apname"

mankrip avatar Feb 23 '24 13:02 mankrip

Ugghhhh -- is such a command ever allowed? And it looks exaggerated to me. @ericwa what do you think?

sezero avatar Feb 23 '24 13:02 sezero

That command does work, even in WinQuake.

This engine bug affects all mods that uses this hack from Preach, including the Copper mod: https://tomeofpreach.wordpress.com/2015/09/30/fixing-runes-and-restart-with-qc/

Starting Copper, entering a map with relative pathnames and "restarting" the map upon death after multiple saves & loads will crash the game to the console because the "mapname" global var is corrupted. I fixed this bug specifically to get such mods to stop crashing.

mankrip avatar Feb 24 '24 14:02 mankrip

By the way, the \n conversion alone will still make some relative pathnames crash the game. In my latest engine release I've also added some code to skip ED_NewString's conversion entirely on filename-related strings.

mankrip avatar Feb 24 '24 14:02 mankrip