Repository: lutris/lutris

Issue:
Lutris takes extremely long time to parse very long string keys in registry
If user.reg contains very long (we a talking megabytes long here) string key it takes a very long time to start anything in this wineprefix with lutris. With ~15MB key lutris was taking ~30 minutes. It happens before starting wine, in "preparing to start" phase.

P.S. If you ask how does this happens - Battletech ny HBS writes some analytics into string key in registry. And it only adds to it, never cleans.


Discussion:
Can you attach the reg file?
[user.reg.zip](https://github.com/lutris/lutris/files/2684074/user.reg.zip)

awesome, thanks!
It gets stuck here https://github.com/lutris/lutris/blob/next/lutris/util/wine/registry.py#L111, because of the len of the reg, not sure how do u want to handle this, anyhow here is a regex to extract that part just in case `rg --multiline '"analytics\.sav.*"=hex:[\w+|\W+]*?"'`, the last `"` is not part of the match but I'm not sure how to extract it, still sharpening my regex skills :p

Resolving patch:
diff --git a/lutris/util/wine/registry.py b/lutris/util/wine/registry.py
--- a/lutris/util/wine/registry.py
+++ b/lutris/util/wine/registry.py
@@ -109,32 +109,29 @@ def parse_reg_file(self, reg_filename):
         registry_lines = self.get_raw_registry(reg_filename)
         current_key = None
         add_next_to_value = False
+        additional_values = []
         for line in registry_lines:
-            line = line.rstrip("\n")  # Remove trailing newlines
-
-            if line.startswith(self.version_header):
-                self.version = int(line[len(self.version_header):])
-                continue
-
-            if line.startswith(self.relative_to_header):
-                self.relative_to = line[len(self.relative_to_header):]
-                continue
-
-            if line.startswith("#arch"):
-                self.arch = line.split("=")[1]
-                continue
-
-            if line.startswith("["):
-                current_key = WineRegistryKey(key_def=line)
-                self.keys[current_key.name] = current_key
-                continue
+            line = line.rstrip("\n")
 
             if current_key:
                 if add_next_to_value:
-                    current_key.add_to_last(line)
-                else:
+                    additional_values.append(line)
+                elif not add_next_to_value:
+                    if additional_values:
+                        additional_values = '\n'.join(additional_values)
+                        current_key.add_to_last(additional_values)
+                        additional_values = []
                     current_key.parse(line)
                 add_next_to_value = line.endswith("\\")
+            elif line.startswith("["):
+                current_key = WineRegistryKey(key_def=line)
+                self.keys[current_key.name] = current_key
+            elif line.startswith(self.version_header):
+                self.version = int(line[len(self.version_header):])
+            elif line.startswith(self.relative_to_header):
+                self.relative_to = line[len(self.relative_to_header):]
+            elif line.startswith("#arch"):
+                self.arch = line.split("=")[1]
 
     def render(self):
         content = "{}{}\n".format(self.version_header, self.version)
@@ -258,7 +255,7 @@ def parse(self, line):
             self.subkeys["default"] = value
 
     def add_to_last(self, line):
-        last_subkey = list(self.subkeys.keys())[-1]
+        last_subkey = next(reversed(self.subkeys))
         self.subkeys[last_subkey] += "\n{}".format(line)
 
     def render(self):


Test patch: