SHARE
TWEET

c3mm.py

7163D Oct 4th, 2016 (edited) 66 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # Cossacks 3 Mod Manager version 1.3.5
  2. # Require python 3.5.x to work : https://python.org/downloads
  3. # Get all update at http://www.cossacks3.com/forum/index.php?threads/cossacks-3-text-mod-manager.17753
  4.  
  5. """
  6. # === News in 1.3.5 === #
  7.     >Changes fields name. They are now:
  8.         -"modify_file" in place of "file"
  9.         -"replace_file" in place of "otherfile"
  10.         -"add_file" in place of "addfile"
  11.     >Add a new field "remove_file", which contain a list.
  12.     >Improve error messages
  13.  
  14.     1.3.5 is compatible with mods created for 1.3.2 or higher
  15. """
  16.  
  17.  
  18. """
  19. Structur uses
  20. Mod  := {'name':name, 'version':version, 'info':info, 'author':author, file':{name => list of (str old, str new)}, 'otherfile':{name => new}}
  21. Data := {'mod':{name => mod mod}, 'file':{name => list of str mod}}
  22. """
  23.  
  24. import json, sys, os, time
  25. from random import randrange
  26.  
  27. CONFIG_PATH = "c3mm/"
  28. MOD_PATH    = "mod/"
  29. COPY_PATH   = "c3mm/copy/"
  30. BCKUP_PATH  = "c3mm/bckup/"
  31. INFO_PATH   = "c3mm/c3mm.json"
  32. LOG_PATH    = "c3mm/log.txt"
  33. HELP = """
  34. Cossacks3 (text) Mod Manager Version 1.3.5.
  35. Type python c3mm.py action [parameter]
  36.     action:
  37.         install modname   -> intall a mod find by name
  38.         uninstall modname -> unintall a mod find by name
  39.         help              -> show this message
  40.         uninstall_all     -> uninstall all mod
  41.         giveall           -> print all installed mods
  42.         valid_mod modname -> check if mod modname is valid
  43. """
  44.  
  45. PATH = [CONFIG_PATH, MOD_PATH, COPY_PATH, BCKUP_PATH]
  46. FILE = [INFO_PATH, LOG_PATH]
  47. for path in PATH:
  48.     try:
  49.         os.mkdir(path)
  50.     except:
  51.         pass
  52. for file in FILE:
  53.     try:
  54.         open(file, 'r')
  55.     except:
  56.         open(file, 'w')
  57.  
  58. class Alert:
  59.     linfo = []
  60.     lerror = []
  61.  
  62.     def info(i):
  63.         Alert.linfo.append(i)
  64.         print(i)
  65.  
  66.     def error(e, data):
  67.         Alert.lerror.append(e)
  68.         print(e)
  69.         quit(data)
  70.  
  71.     def input(m):
  72.         return input(m)
  73.  
  74.     def list(l):
  75.         for i in l:
  76.             print(i)
  77.  
  78. def loadjson(path):
  79.     return json.loads(open(path, 'r').read())
  80.  
  81. def savejson(path, data):
  82.     open(path, "w").write(json.dumps(data))
  83.  
  84. def loadjsonmod(path):
  85.     mod = loadjson(path)
  86.     if not "modify_file" in mod:
  87.         mod["modify_file"] = {}
  88.     if not "replace_file" in mod:
  89.         mod["replace_file"] = {}
  90.     if not "otherfile" in mod:
  91.         mod["otherfile"] = {}
  92.     if not "addfile" in mod:
  93.         mod["addfile"] = {}
  94.     if not "file" in mod:
  95.         mod["file"] = mod["modify_file"]
  96.     if not "otherfile" in mod:
  97.         mod["otherfile"] = mod["replace_file"]
  98.     if not "addfile" in mod:
  99.         mod["addfile"] = mod["add_file"]
  100.     if not "remove_file" in mod:
  101.         mod["remove_file"] = []
  102.     if not validMod(mod):
  103.         return False
  104.     return mod
  105.  
  106. def mkPath(path):
  107.     rpath = ""
  108.     for dir in path.split('/')[:-1]:
  109.         rpath += dir
  110.         try:
  111.             os.mkdir(rpath)
  112.         except:
  113.             pass
  114.         rpath += '/'
  115.  
  116. def copyFile(file, path):
  117.     mkPath(path)
  118.     open(path, "w").write(open(file, 'r').read())
  119.  
  120. def copyBFile(file, path):
  121.     mkPath(path)
  122.     open(path, "wb").write(open(file, 'rb').read())
  123.  
  124. def checkMod(mod, data):
  125.     """
  126.     checkMod function, use to check if a mod is valid
  127.     Mod mod     -> mod to check
  128.     Bool return -> is the mod valid?
  129.     """
  130.     for file in mod['file']:
  131.         Alert.info("Check existence of '" + file + "'")
  132.         try:
  133.             f = open(file, 'r').read()
  134.             Alert.info("'" + file + "' exist.")
  135.         except:
  136.             Alert.info("'" + file + "' don't exist.")
  137.             return False
  138.  
  139.         for old in mod['file'][file]:
  140.             Alert.info("Check exsitence of '" + old + "' in '" + file + "'")
  141.             if not old in f:
  142.                 Alert.info("Not found.")
  143.                 return False
  144.             else:
  145.                 Alert.info("Found.")
  146.  
  147.     for file in mod["otherfile"]:
  148.         Alert.info("Check existence of '" + file + "'")
  149.         try:
  150.             open(file, 'r')
  151.             Alert.info("'" + file + "' exist.")
  152.         except:
  153.             Alert.info("'" + file + "' don't exist.")
  154.             return False
  155.  
  156.         Alert.info("Check existence of '" + MOD_PATH + mod["otherfile"][file] + "'")
  157.         try:
  158.             open(MOD_PATH + mod["otherfile"][file], 'r')
  159.             Alert.info("'" + MOD_PATH + mod["otherfile"][file] + "' exist.")
  160.         except:
  161.             Alert.info("'" + MOD_PATH + mod["otherfile"][file] + "' don't exist.")
  162.             return False
  163.  
  164.         if file in data["otherfile"]:
  165.             Alert.info(file + " is already modified by " + data["otherfile"][file][0])
  166.             Alert.info("You have to uninstall " + data["otherfile"][file][0] + " to install " + mod['name'])
  167.             return False
  168.  
  169.     for file in mod["addfile"]:
  170.         Alert.info("Check existence of '" + MOD_PATH + mod["addfile"][file] + "'")
  171.         try:
  172.             open(MOD_PATH + mod["addfile"][file], 'r')
  173.             Alert.info("'" + MOD_PATH + mod["addfile"][file] + "' exist.")
  174.         except:
  175.             Alert.info("'" + MOD_PATH + mod["addfile"][file] + "' don't exist.")
  176.             return False
  177.  
  178.         if file in data["addfile"]:
  179.             Alert.info(file + " is already added by " + data["addfile"][file][0])
  180.             Alert.info("You have to uninstall " + data["addfile"][file][0] + " to install " + mod['name'])
  181.             return False
  182.  
  183.     for file in mod["remove_file"]:
  184.         Alert.info("Check existence of '" + file + "'")
  185.         try:
  186.             open(file, 'r').read()
  187.             Alert.info("'" + file + "' exist.")
  188.         except:
  189.             Alert.info("'" + file + "' don't exist.")
  190.             return False
  191.  
  192.     return True
  193.  
  194. def validMod(mod):
  195.     """
  196.     validMod function, assert not error occured when install mod
  197.     Mod mod -> mod to check
  198.     Bool return
  199.     """
  200.     for field in ['name', 'info', 'version', 'author']:
  201.         if not field in mod:
  202.             Alert.info("Missing field '" + field + "'")
  203.             return False
  204.  
  205.     if not type(mod['file']) == type({}):
  206.         Alert.info("Wrong data mod")
  207.         return False
  208.  
  209.     for file in mod['file']:
  210.         if not type(mod['file'][file]) == type({}):
  211.             Alert.info("Wrong data mod")
  212.             return False
  213.         try:
  214.             for old in mod['file'][file]:
  215.                 new = mod['file'][file][old]
  216.                 if not type(old) == type("") or not type(new) == type(""):
  217.                     Alert.info("Wrong data mod")
  218.                     return False
  219.         except:
  220.             Alert.info("Wrong data mod. field modify_field must contain only string:string")
  221.             return False
  222.  
  223.     if not type(mod['otherfile']) == type({}):
  224.         Alert.info("Wrong data mod, field replace_file must be a dictionary.")
  225.         return False
  226.  
  227.     if not type(mod['addfile']) == type({}):
  228.         Alert.info("Wrong data mod, field add_file must be a dictionary.")
  229.         return False
  230.  
  231.     if not type(mod['remove_file']) == type([]):
  232.         Alert.info("Wrong data mod, field remove_file must be a list.")
  233.         return False
  234.  
  235.     return True
  236.  
  237.  
  238. def apply(mod):
  239.     """
  240.     apply function, use to modify files
  241.     Mod mod -> mod that contain all modifications
  242.     None return
  243.     """
  244.     mfile = {}
  245.     for file in mod['file']:
  246.         Alert.info("Calculate'" + file + '"')
  247.         f = open(file, 'r').read()
  248.  
  249.         #Calculate modifications
  250.         for old in mod['file'][file]:
  251.             new = mod['file'][file][old]
  252.             f = f.replace(old, new)
  253.  
  254.         mfile[file] = f
  255.    
  256.     #Apply mod
  257.     for file in mfile:
  258.         Alert.info("Patching '" + file + '"')
  259.         open(file, 'w').write(mfile[file])
  260.  
  261.     for file in mod["otherfile"]:
  262.         Alert.info("Replace '" + file+ "'")
  263.         copyBFile(MOD_PATH + mod["otherfile"][file], file)
  264.  
  265.     for file in mod["addfile"]:
  266.         Alert.info("Add '" + file + "'")
  267.         copyBFile(MOD_PATH + mod["addfile"][file], file)
  268.  
  269.     for file in mod["remove_file"]:
  270.         Alert.info("Remove'" + file + "'")
  271.         os.remove(file)
  272.  
  273. def install(mod, data):
  274.     """
  275.     install function, use to add a mod.
  276.     Mod mod   -> mod to install
  277.     Data data -> c3mm main data
  278.     None return
  279.     """
  280.     if not checkMod(mod, data):
  281.         Alert.error("'" + mod['name'] + "' mod version " + mod['version'] + " isn't valid", data)
  282.  
  283.     Alert.info("Install '" + mod['name'] + "' version " + mod['version'])
  284.  
  285.     #Add some "signature"
  286.     for file in mod['file']:
  287.         for old in mod['file'][file]:
  288.             mod['file'][file][old] = mod['file'][file][old] + "//" + str(randrange(100000, 999999))
  289.  
  290.     #Add mod to list of installed mod
  291.     data['mod'][mod['name']] = mod
  292.  
  293.     #Adding mod in list of file modification
  294.     for file in mod['file']:
  295.         if not file in data['file']:
  296.             data['file'][file] = [mod['name']]
  297.             #backup file
  298.             copyFile(file, BCKUP_PATH + file)
  299.         else:
  300.             data['file'][file].append(mod['name'])
  301.  
  302.     for file in mod["otherfile"]:
  303.         if not file in data['otherfile']:
  304.             data['otherfile'][file] = [mod['name']]
  305.             #backup file
  306.             copyBFile(file, BCKUP_PATH + file)
  307.         else:
  308.             data['otherfile'][file].append(mod['name'])
  309.  
  310.     for file in mod["addfile"]:
  311.         if not file in data['otherfile']:
  312.             data['addfile'][file] = [mod['name']]
  313.         else:
  314.             data['addfile'][file].append(mod['name'])
  315.  
  316.     for file in mod["remove_file"]:
  317.         copyBFile(file, BCKUP_PATH + file)
  318.         data["remove_file"][file] = [mod['name']]
  319.  
  320.     apply(mod)
  321.  
  322.     for file in mod['file']:
  323.         #make a save of the file
  324.         copyFile(file, COPY_PATH + file)
  325.  
  326. def uninstall(mod, data):
  327.     """
  328.     uninstall function, use to delete a mod.
  329.     Mod mod   -> mod to uninstall
  330.     Data data -> c3mm main data
  331.     None return
  332.     """
  333.     uninstallmod = mkuninstallmod(mod)
  334.  
  335.     if not checkMod(uninstallmod, data):
  336.         Alert.info("'" + mod['name'] + "' mod uninstaller isn't valid.")
  337.         Alert.info("Do you want restore all files concerned by this mod?")
  338.         Alert.info("List of mod will uninstalled in this case:")
  339.         modlist = []
  340.         for file in mod['file']:
  341.             for mod in data['file'][file]:
  342.                 if not mod in modlist:
  343.                     modlist.append(mod)
  344.                     Alert.info(mod)
  345.         choice = Alert.chocie("Restore all file? yes/no:")
  346.         if choice == 'no':
  347.             quit(data)
  348.  
  349.         filelist = [file for file in mod['file']]
  350.         for file in filelist:
  351.             Alert("Restore '" + file + "'")
  352.             copyFile(BCKUP_PATH + file, file)
  353.             data['mod'][mod]['file'].remove(file)
  354.  
  355.         for mod in modlist():
  356.             uninstall(mod, data)
  357.     #Delete mod to list of installed mod
  358.     del data['mod'][mod['name']]
  359.  
  360.     #Delete mod of list of file modification
  361.     for file in data['file']:
  362.         if mod['name'] in data['file'][file]:
  363.             data['file'][file].remove(mod['name'])
  364.  
  365.     Alert.info("Uninstall '" + mod['name'] + "' version " + mod['version'])
  366.  
  367.     for file in mod["otherfile"]:
  368.         Alert.info("Restore " + file)
  369.         copyBFile(BCKUP_PATH + file, file)
  370.         del data["otherfile"][file]
  371.  
  372.     for file in mod["addfile"]:
  373.         Alert.info("Delete " + file)
  374.         os.remove(file)
  375.         del data["addfile"][file]
  376.  
  377.     for file in mod["remove_file"]:
  378.         Alert.info("Restore " + file)
  379.         copyBFile(BCKUP_PATH + file, file)
  380.         del data["remove_file"][file]
  381.  
  382.     apply(uninstallmod)
  383.  
  384. def mkuninstallmod(mod):
  385.     """
  386.     mkuninstallmod function, use to make an uninstalator from a mod
  387.     Mod mod    -> mod to uninstall
  388.     Mod return -> uninstalator mod
  389.     """
  390.     uninstallmod = {}
  391.     uninstallmod['file'] = {}
  392.  
  393.     for file in mod['file']:
  394.         uninstallmod['file'][file] = {}
  395.         for old in mod['file'][file]:
  396.             new = mod['file'][file][old]
  397.             #For each modification of mod, inverse new and old
  398.             uninstallmod['file'][file][new] = old
  399.  
  400.     uninstallmod["otherfile"] = {}
  401.     uninstallmod["addfile"] = {}
  402.     uninstallmod["remove_file"] = {}
  403.  
  404.     return uninstallmod
  405.  
  406. def checkStatu(data):
  407.     """
  408.     checkStatu funtion, check if a modded file has been modified since last launch of this program
  409.     Data data   -> main data
  410.     Bool return ->
  411.     """
  412.     badfile = []
  413.     #Check all modded file
  414.     for file in data['file']:
  415.         if open(file, 'r').read() != open(COPY_PATH + file, 'r').read():
  416.             Alert.info("File '" + file + "' has been modified since last launch of c3mm.")
  417.             badfile.append(file)
  418.  
  419.     badotherfile = []
  420.     for file in data["otherfile"]:
  421.         if open(file, 'rb').read() != open(BCKUP_PATH + file, 'rb').read():
  422.             Alert.info("File '" + file + "' has been modified since last launch of c3mm.")
  423.             badotherfile.append(file)
  424.  
  425.     badmod = []
  426.     #Uninstall all mod concerned by these files
  427.     for file in badfile:
  428.         for mod in data['file'][file]:
  429.             if not mod in badmod and not checkMod(mkuninstallmod(data['mod'][mod]), data):
  430.                 badmod.append(mod)
  431.     for file in badotherfile:
  432.         if not data["otherfile"][file][0] in badmod:
  433.             badmod.append(data["otherfile"][file][0])
  434.  
  435.     for mod in badmod:
  436.         _mod = data['mod'][mod]
  437.         for file in badfile:
  438.             if file in _mod['file']:
  439.                 del _mod['file'][file]
  440.         uninstall(_mod, data)
  441.  
  442.     #make a new copy of all modified files
  443.     for file in badfile:
  444.         copyFile(file, COPY_PATH + file)
  445.  
  446.     return len(badmod) == 0
  447.  
  448. def quit(data):
  449.     """
  450.     Save all data before exit the program
  451.     """
  452.     dfile = []
  453.     for file in data['file']:
  454.         if data['file'][file] == []:
  455.             dfile.append(file)
  456.  
  457.     for file in dfile:
  458.         del data['file'][file]
  459.  
  460.     savejson(INFO_PATH, data)
  461.  
  462.     logs = time.strftime('%D %H:%M\n',time.localtime())
  463.     for info in Alert.linfo:
  464.         logs += info + '\n'
  465.     for error in Alert.lerror:
  466.         logs += error + '\n'
  467.  
  468.     open(LOG_PATH, "a").write(logs)
  469.     exit()
  470.  
  471. def main(argc, argv):
  472.  
  473.     try:
  474.         data = loadjson(INFO_PATH)
  475.     except:
  476.         open(INFO_PATH, "w").write('{"mod":{}, "file":{}}')
  477.         data = loadjson(INFO_PATH)
  478.  
  479.     if not "otherfile" in data:
  480.         data["otherfile"] = {}
  481.  
  482.     if not "addfile" in data:
  483.         data["addfile"] = {}
  484.  
  485.     if not "remove_file" in data:
  486.         data["remove_file"] = {}
  487.  
  488.     if not checkStatu(data):
  489.         quit(data)
  490.  
  491.     elif argc == 1 or argv[1] == "help":
  492.         print(HELP)
  493.  
  494.     elif argv[1] == 'install':
  495.  
  496.         if argc < 3:
  497.             Alert.error("You must give a mod name", data)
  498.  
  499.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  500.         if not mod:
  501.             quit(data)
  502.         modname = mod['name']
  503.  
  504.         if modname in data['mod']:
  505.             if mod["version"] == data["mod"][modname]["version"]:
  506.                 Alert.error("Mod '" + modname + "' version " + mod["version"] + " is already install.", data)
  507.             else:
  508.                 Alert.info("Mod '" + modname + "' version " + data["mod"][modname]["version"] + " is already install. Do you want change it for version " + mod["version"] + "?")
  509.                 choice = Alert.input("yes/no:")
  510.                 if choice == "no":
  511.                     quit()
  512.  
  513.                 uninstall(data['mod'][modname], data)
  514.  
  515.         install(mod, data)
  516.  
  517.     elif argv[1] == 'uninstall':
  518.  
  519.         if argc < 3:
  520.             Alert.error("You must give a mod name", data)
  521.  
  522.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  523.         if not mod:
  524.             quit(data)
  525.         modname = mod['name']
  526.         uninstall(data['mod'][modname], data)
  527.  
  528.     elif argv[1] == 'uninstall_all':
  529.  
  530.         Alert.info("Restore all files")
  531.         for file in data['file']:
  532.             Alert.info("Restore '" + file + "'")
  533.             copyFile(BCKUP_PATH + file, file)
  534.  
  535.         data['file'] = {}
  536.         data['mod'] = {}
  537.  
  538.     elif argv[1] == 'giveall':
  539.         Alert.list([mod + " version " + data['mod'][mod]['version'] for mod in data['mod']])
  540.  
  541.     elif argv[1] == 'valid_mod':
  542.         if argc < 3:
  543.             Alert.error("You must give a mod name", data)
  544.  
  545.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  546.         if mod and checkMod(mod, data):
  547.             Alert.info("Mod is valid")
  548.         else:
  549.             Alert.info("Mod isn't valid!")
  550.  
  551.     quit(data)
  552.  
  553. if __name__ == '__main__':
  554.     main(len(sys.argv), sys.argv)
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top