Advertisement
7163D

c3mm.py

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