VasVadum

Save File Fixer

Jul 26th, 2015
361
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.44 KB | None | 0 0
  1. import os
  2. import sys
  3. import glob
  4. import fileinput
  5. import shutil
  6.  
  7. # Don't touch these imports, or else, I'll export you into a black hole! :|
  8.  
  9. # Script collaboration between Vas, Artex, Anoyomouse, Harag (In no particular order)
  10. # Will try to comment as much of it as we can to explain it for those who want to learn from it.
  11. # Short Description: This script asks user for save location, then asks what saves to fix,
  12. #                    and then fixes them according to the words listed below, currently
  13. #                    only handles up to 10 words, but I can expand it later to do more easily.
  14. #
  15. # Last update: 26-Jul-2015 @ 4:17 AM GMT-6
  16. #
  17. # I will edit this script to make it easier for mod authors who don't know python or coding really,
  18. # to be able to make it work easily for them.  Simply edit the values below, if you need help,
  19. # contact me on the forums via https://ludeon.com/forums/index.php?action=profile;u=2872
  20.  
  21. replacements = [
  22.     ('HydroponicsBasinModularPotent', 'HydroponicsBasin4S'),   # Enter the words you want replaced
  23.     ('HydroponicsBasinModularFertile', 'HydroponicsBasin3S'),  # in these slots.
  24.     ('HydroponicsBasinModularRich', 'HydroponicsBasin2S'),     # "ReplaceWord", "ReplaceWith"
  25.     ('HydroponicsBasinModular', 'HydroponicsBasin1S'),         # and you can copy the entire
  26.     ('', ''),  # line below the next to add more, supports any amount of replacements.
  27.     ('', ''),  # Copy the line without the comment, and place it below the last line before the ]
  28.     ('', ''),
  29. ]
  30.  
  31. finalMsgSuccess = "Your save file(s) should now be functional with the new modular hydroponics mod update. Press enter to exit."
  32.  
  33. # Please note, the script searches for the text, nothing past it. What I mean is that if you were
  34. # to search "me" and replace with "of", every instance of "me" will become "of" even if it's in a
  35. # word like "mean" making iit "ofan". Look at how mine works, I left "HydroponicsBasinModular" as
  36. # the last one to replace, because the first 3 also have that same word in them, and therefor if
  37. # I had done it first, it would have broken all those words, like "HydroponicsBasin1SPotent".
  38. # This is why you should always try to make your defNames very unique in your items, to avoid issues.
  39.  
  40.  
  41.  
  42. # Beyond this line is the actual script, do not edit unless you are knowledgable in Python!
  43.  
  44. # Ask if the script is in the saves directory
  45. print 'Is the script in the Rimworld save directory?'
  46. print 'It\'s not a requirement, so don\'t worry!'
  47. scriptDir = raw_input('Yes|No > ')
  48. scriptDir = scriptDir.strip(' \t\r\n').lower()
  49. if scriptDir == 'yes':
  50.     directory = os.getcwd()
  51.     answered = True
  52. else:
  53.     print 'Oh, alright, that\'s fine.'
  54.  
  55. if scriptDir == 'no':
  56.     # Ask if the rimworld save directory is the default
  57.     answered = False
  58.     while not answered:
  59.         print 'Are you using the default Rimworld directory?'
  60.         dirAns = raw_input('Yes|No|Quit > ')
  61.         # clean up the string we get from the user, it might have an enter on the end
  62.         dirAns = dirAns.strip(' \t\r\n').lower()
  63.         if dirAns == 'yes':
  64.             # Grab the user's App Data directory, slice off the "Roaming" part
  65.             the_directory = os.path.split(os.getenv("APPDATA"))[0]
  66.             # Append on the directory where Rimworld saves it's data, should be fine
  67.             directory = os.path.join(the_directory, 'LocalLow', "Ludeon Studios", "RimWorld", "Saves")
  68.             # Then check it exists, in case there was an error getting the default directory
  69.             if os.path.exists(directory):
  70.                 print 'Ok, I will use the default directory.  For validation, I will display it for you:'
  71.                 print directory
  72.                 answered = True
  73.             else:
  74.                 print 'Path does not seem to exist.  For debugging, I will now display the path for you.'
  75.                 print directory
  76.         elif dirAns == 'no':
  77.             # Because \ is a special character, we have to put a \ before it (and other specials) to null them out,
  78.             # So when it shows "\\Rimworld\\Saves\\" it actually translates to "\Rimworld\Saves\".
  79.             print 'Please give me the directory you wish me to read files from.  Here are two examples;'
  80.             print 'C:\\Users\\Username\\Rimworld\\Saves\\ - To give an exact location.'
  81.             print '\\Rimworld\\Saves\\ - If the files are two directories down from the script\'s current location.'
  82.             directory = raw_input('Dir: ')
  83.             if directory.startswith('\\'):
  84.                 directory = os.getcwd() + directory
  85.             # We need to check this path exists as well, in case the user put it in wrong.
  86.             if os.path.exists(directory):
  87.                 print 'Ok, I will use the directory you gave.  For validation, I will display it for you:'
  88.                 print directory
  89.                 answered = True
  90.             else:
  91.                 print 'Path does not seem to exist.  For ease of use, I will now display the path for you.'
  92.                 print directory
  93.         elif dirAns.startswith('q'):
  94.             # Just in case someone forgot to not run the script, or they remembered something else!
  95.             print 'So you asked to quit, okay!'
  96.             sys.exit(0)
  97.         else:
  98.             print 'Something happened, looping back to the beginning of this block.'
  99.     else:
  100.         print 'You\'ve chosen to edit files in the same directory as the script.'
  101.         print 'Chosen directory = ' + directory
  102.  
  103. # At this point in the script we have a single variable that should be correct: directory
  104.  
  105. # Get the input file
  106. print 'Here are a list of files in the directory you have listed:'
  107. fileList = filter(os.path.isfile, glob.glob(os.path.join(directory, '*.rws')))
  108. # Glob is nice, but the list it returns is unsorted, so we sort it here
  109. fileList.sort()
  110.  
  111. cntr = 1
  112. for i in fileList:
  113.     print str(cntr) + ') ' + os.path.split(i)[1]
  114.     cntr += 1
  115.  
  116. print 'Enter a number, or a list of numbers seperated by a space or type "all" if you want to process all RimWorld Save files'
  117. inputFileNumbers = raw_input('Files > ')
  118.  
  119. # Clean up the string we get from the user, it might have an enter on the end
  120. inputFileNumbers = inputFileNumbers.strip(' \t\r\n')
  121.  
  122. # Define a list to keep all the files we want to process, even if it's a single file
  123. inputFileNames = []
  124.  
  125. # Did the user want all files done?  If not, we move on.
  126. if inputFileNumbers.lower() == 'all':
  127.     inputFileNames = fileList
  128. else:
  129.     # Split the string into a list of "words" and filter out:
  130.     isNonEmptyString = bool
  131.     isNonNegativeNumber = str.isdigit
  132.     isInRange = lambda val: 1 <= val <= len(fileList)
  133.         # same as: def isInRange(val): return 1 <= val <= len(fileList)
  134.     # What we have left are valid indices for fileList (offset by 1)
  135.     lst = filter(isInRange,
  136.             map(int,
  137.                 filter(isNonNegativeNumber,
  138.                     filter(isNonEmptyString,
  139.                         inputFileNumbers.split(' ')))))
  140.     # Make a list of only the relevant filenames based on the extracted indices
  141.     inputFileNames = [fileList[index-1] for index in lst]
  142.     # Redundant, but if we want to notify them of illegal values:
  143.     illegal = filter(isNonEmptyString, inputFileNumbers.split(' '))
  144.     illegal = filter(lambda value: value not in map(str, lst), illegal)
  145.     for v in illegal:
  146.         print "  * Ignored illegal value:", v
  147.        
  148.     # Alternative, for those wanting to know another way of doing the list.
  149.     # Same as lines 81 to 93, only in one line.
  150.     # lst = [x for x in inputFileNumbers.split(' ') if x.isdigit and int(x) > 0 and int(z) <= len(fileList)]
  151.     # inputFileNames = [fileList[y] for y in [int(x) for x in inputFileNumbers.split(' ') if x and x.isdigit and int(x) > 0 and int(x) <= len(fileList)]]
  152.  
  153. if len(inputFileNames) > 0:
  154.     if len(inputFileNames) == 1:
  155.         print 'Ok, I will edit ' + os.path.split(inputFileNames[0])[1] + ' for you.'
  156.     else:
  157.         print 'Ok, I will edit the following files for you:'
  158.         for i in inputFileNames:
  159.             print '    ' + os.path.split(i)[1]
  160. else:
  161.     print 'You did not select any valid file to exit, so we cannot edit anything, killing the script!'
  162.     sys.exit(-1)
  163.  
  164.  
  165. # Ask if I should edit the auto saves
  166. answered = False
  167. while not answered:
  168.     print 'Would you also like me to edit  your auto saves? (Yes|No)'
  169.     autoSaves = raw_input('Yes|No > ')
  170.     # Clean up the string we get from the user, it might have an enter on the end
  171.     autoSaves = autoSaves.strip(' \t\r\n').lower()
  172.     if autoSaves == 'yes':
  173.         print 'Ok, I will fix the auto saves as well.'
  174.         answered = True
  175.     elif autoSaves == 'no':
  176.         print 'Ok, I will ignore the auto saves.'
  177.         answered = True
  178.     else:
  179.         print 'I\'m sorry, I didn\'t understand that.  You need to type Yes, or No.'
  180.  
  181.  
  182. def processFile(srcFile):
  183.     autoData = None
  184.     # Do magic here!
  185.     bakFile = srcFile + '.bak'
  186.     os.rename(srcFile, bakFile)
  187.     inputFileObj = open(bakFile, 'r')
  188.     print 'Writing file: ', os.path.split(srcFile)[1]
  189.     outputFileObj = open(srcFile, 'w')
  190.     while 1:
  191.         autoData = inputFileObj.readline()
  192.         if not autoData:
  193.             break
  194.         for replaced, replacement in replacements:              # Harag's Replacement
  195.             autoData = autoData.replace(replaced, replacement)  # Harag's Replacement
  196.         outputFileObj.write(autoData)
  197.         # outputFileObj.flush() - Not really needed
  198.     inputFileObj.close()
  199.     outputFileObj.close()
  200.  
  201.  
  202. print "Last chance to back out here, the files will be modified past this point"
  203. res = raw_input('Press enter to continue.')
  204. if res != '':
  205.     print 'This ' + res + ' was not valid, so I\'m stopping'
  206.     sys.exit(2)
  207.  
  208. # Read the files into memory, tehcnically only a single line at a time, so this can modify huge files
  209. # You checked for a case insensitive 'yes' in the variable autoSaves on line 116
  210. autoSaveProcessed = 0
  211. if autoSaves.lower() == 'yes':
  212.     # We got asked to modify the saved games
  213.     # NOTE: range(1,6) => [1, 2, 3, 4, 5]
  214.     for autoGroup in range(1,6):
  215.         autoData = None
  216.         # Process 5 autosave files
  217.         saveFileName = os.path.join(directory, 'Autosave-' + str(autoGroup) + '.rws')
  218.         # The following check makes sure we don't double up on the work and
  219.         # if there's a backup we overwrite it with the changed data, so things break
  220.         if saveFileName in inputFileNames:
  221.             inputFileNames.remove(saveFileName)
  222.         if os.path.exists(saveFileName):
  223.             processFile(saveFileName)
  224.             autoSaveProcessed += 1
  225.         else:
  226.             print 'Can\'t find any autosave files:', saveFileName
  227. else:
  228.     print 'Skipping autosaves'
  229.  
  230. print 'Doing the main file(s) now'
  231.  
  232. # So loop over the input file names and edit the files, by this time the files should still exist
  233. for i in inputFileNames:
  234.     processFile(i)
  235.  
  236. print str((len(inputFileNames) + autoSaveProcessed)) + ' files processed.'
  237.  
  238. # Move the backups now that we finished.
  239. os.chdir(directory)
  240. if not os.path.isdir('backups'):
  241.     os.mkdir('backups')
  242.  
  243. for fName in glob.glob('*.bak'):
  244.     shutil.move(fName, os.path.join('backups', fName))
  245.     fd = os.path.join(directory, 'backups')
  246.     for f in os.listdir('backups'):
  247.         if not f.startswith('.'):
  248.             os.rename(os.path.join(fd, f), os.path.join(fd, f.replace('.bak', '')))
  249.  
  250. print 'Backups have been moved into their own folder now.'
  251.  
  252. print '\n\n' + finalMsgSuccess
  253. raw_input('> ')
Add Comment
Please, Sign In to add comment