Advertisement
7163D

c3mm.py

Oct 6th, 2016
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.22 KB | None | 0 0
  1. # Cossacks 3 Mod Manager version 1.3.9
  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.9 === #
  7.     >Now program add signature only on .script file
  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.9
  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.         if not ".script" in file:
  274.             continue
  275.         for old in mod['file'][file]:
  276.             mod['file'][file][old] = mod['file'][file][old] + "//" + str(randrange(100000, 999999))
  277.  
  278.     #Add mod to list of installed mod
  279.     data['mod'][mod['name']] = mod
  280.  
  281.     #Adding mod in list of file modification
  282.     for file in mod['file']:
  283.         if not file in data['file']:
  284.             data['file'][file] = [mod['name']]
  285.             #backup file
  286.             copyFile(file, BCKUP_PATH + file)
  287.         else:
  288.             data['file'][file].append(mod['name'])
  289.  
  290.     for file in mod["otherfile"]:
  291.         if not file in data['otherfile']:
  292.             data['otherfile'][file] = [mod['name']]
  293.             #backup file
  294.             copyBFile(file, BCKUP_PATH + file)
  295.         else:
  296.             data['otherfile'][file].append(mod['name'])
  297.  
  298.     for file in mod["addfile"]:
  299.         if not file in data['otherfile']:
  300.             data['addfile'][file] = [mod['name']]
  301.         else:
  302.             data['addfile'][file].append(mod['name'])
  303.  
  304.     for file in mod["remove_file"]:
  305.         copyBFile(file, BCKUP_PATH + file)
  306.         data["remove_file"][file] = [mod['name']]
  307.  
  308.     apply(mod)
  309.  
  310.     for file in mod['file']:
  311.         #make a save of the file
  312.         copyFile(file, COPY_PATH + file)
  313.  
  314. def uninstall(mod: Mod, data: Data) -> None:
  315.     """
  316.     uninstall function, use to delete a mod.
  317.     Mod mod   -> mod to uninstall
  318.     Data data -> c3mm main data
  319.     None return
  320.     """
  321.     uninstallmod = mkuninstallmod(mod)
  322.  
  323.     if not checkMod(uninstallmod, data):
  324.         Alert.info("'" + mod['name'] + "' mod uninstaller isn't valid.")
  325.         Alert.info("Do you want restore all files concerned by this mod?")
  326.         Alert.info("List of mod will uninstalled in this case:")
  327.         modlist = []
  328.         for file in mod['file']:
  329.             for mod in data['file'][file]:
  330.                 if not mod in modlist:
  331.                     modlist.append(mod)
  332.                     Alert.info(mod)
  333.         choice = Alert.input("Restore all file? yes/no:")
  334.         if choice == 'no':
  335.             quit(data)
  336.  
  337.         filelist = [file for file in mod['file']]
  338.         for file in filelist:
  339.             Alert("Restore '" + file + "'")
  340.             copyFile(BCKUP_PATH + file, file)
  341.             for mod in modlist:
  342.                 if file in mod["file"]:
  343.                     del mod["file"][file]
  344.                 if file in mod["otherfile"]:
  345.                     del mod["otherfile"][file]
  346.                 if file in mod["addfile"]:
  347.                     del mod["addfile"][file]
  348.                 if file in mod["remove_file"]:
  349.                     mod["remove_file"].remove("file")
  350.  
  351.         for mod in modlist:
  352.             uninstall(mod, data)
  353.     #Delete mod to list of installed mod
  354.     del data['mod'][mod['name']]
  355.  
  356.     #Delete mod of list of file modification
  357.     for file in data['file']:
  358.         if mod['name'] in data['file'][file]:
  359.             data['file'][file].remove(mod['name'])
  360.  
  361.     Alert.info("Uninstall '" + mod['name'] + "' version " + mod['version'])
  362.  
  363.     for file in mod["otherfile"]:
  364.         Alert.info("Restore " + file)
  365.         copyBFile(BCKUP_PATH + file, file)
  366.         del data["otherfile"][file]
  367.  
  368.     for file in mod["addfile"]:
  369.         Alert.info("Delete " + file)
  370.         os.remove(file)
  371.         del data["addfile"][file]
  372.  
  373.     for file in mod["remove_file"]:
  374.         Alert.info("Restore " + file)
  375.         copyBFile(BCKUP_PATH + file, file)
  376.         del data["remove_file"][file]
  377.  
  378.     apply(uninstallmod)
  379.  
  380. def mkuninstallmod(mod: Mod) -> None:
  381.     """
  382.     mkuninstallmod function, use to make an uninstalator from a mod
  383.     Mod mod    -> mod to uninstall
  384.     Mod return -> uninstalator mod
  385.     """
  386.     uninstallmod = {}
  387.     uninstallmod['file'] = {}
  388.  
  389.     for file in mod['file']:
  390.         uninstallmod['file'][file] = {}
  391.         for old in mod['file'][file]:
  392.             new = mod['file'][file][old]
  393.             #For each modification of mod, inverse new and old
  394.             uninstallmod['file'][file][new] = old
  395.  
  396.     uninstallmod["otherfile"] = {}
  397.     uninstallmod["addfile"] = {}
  398.     uninstallmod["remove_file"] = {}
  399.  
  400.     return uninstallmod
  401.  
  402. def checkStatu(data:  Data) -> bool:
  403.     """
  404.     checkStatu funtion, check if a modded file has been modified since last launch of this program
  405.     Data data   -> main data
  406.     Bool return ->
  407.     """
  408.     badfile = []
  409.     #Check all modded file
  410.     for file in data['file']:
  411.         if open(file, 'r').read() != open(COPY_PATH + file, 'r').read():
  412.             Alert.info("File '" + file + "' has been modified since last launch of c3mm.")
  413.             badfile.append(file)
  414.  
  415.     badotherfile = []
  416.     for file in data["otherfile"]:
  417.         if open(file, 'rb').read() != open(BCKUP_PATH + file, 'rb').read():
  418.             Alert.info("File '" + file + "' has been modified since last launch of c3mm.")
  419.             badotherfile.append(file)
  420.  
  421.     badmod = []
  422.     #Uninstall all mod concerned by these files
  423.     for file in badfile:
  424.         for mod in data['file'][file]:
  425.             if not mod in badmod and not checkMod(mkuninstallmod(data['mod'][mod]), data):
  426.                 badmod.append(mod)
  427.     for file in badotherfile:
  428.         if not data["otherfile"][file][0] in badmod:
  429.             badmod.append(data["otherfile"][file][0])
  430.  
  431.     for mod in badmod:
  432.         _mod = data['mod'][mod]
  433.         for file in badfile:
  434.             if file in _mod['file']:
  435.                 del _mod['file'][file]
  436.         uninstall(_mod, data)
  437.  
  438.     #make a new copy of all modified files
  439.     for file in badfile:
  440.         copyFile(file, COPY_PATH + file)
  441.  
  442.     return len(badmod) == 0
  443.  
  444. def quit(data: Data) -> None:
  445.     """
  446.     Save all data before exit the program
  447.     """
  448.     dfile = []
  449.     for file in data['file']:
  450.         if data['file'][file] == []:
  451.             dfile.append(file)
  452.  
  453.     for file in dfile:
  454.         del data['file'][file]
  455.  
  456.     savejson(INFO_PATH, data)
  457.  
  458.     logs = time.strftime('%D %H:%M\n',time.localtime())
  459.     for info in Alert.linfo:
  460.         logs += info + '\n'
  461.     for error in Alert.lerror:
  462.         logs += error + '\n'
  463.  
  464.     open(LOG_PATH, "a").write(logs)
  465.     exit()
  466.  
  467. def main(argc: int, argv: List[str]) -> None:
  468.  
  469.     try:
  470.         data = loadjson(INFO_PATH)
  471.     except:
  472.         open(INFO_PATH, "w").write('{"mod":{}, "file":{}}')
  473.         data = loadjson(INFO_PATH)
  474.  
  475.     if not "otherfile" in data:
  476.         data["otherfile"] = {}
  477.  
  478.     if not "addfile" in data:
  479.         data["addfile"] = {}
  480.  
  481.     if not "remove_file" in data:
  482.         data["remove_file"] = {}
  483.  
  484.     if not checkStatu(data):
  485.         quit(data)
  486.  
  487.     elif argc == 1 or argv[1] == "help":
  488.         print(HELP)
  489.  
  490.     elif argv[1] == 'install':
  491.  
  492.         if argc < 3:
  493.             Alert.error("You must give a mod name", data)
  494.  
  495.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  496.         if not mod:
  497.             quit(data)
  498.         modname = mod['name']
  499.  
  500.         if modname in data['mod']:
  501.             if mod["version"] == data["mod"][modname]["version"]:
  502.                 Alert.error("Mod '" + modname + "' version " + mod["version"] + " is already install.", data)
  503.             else:
  504.                 Alert.info("Mod '" + modname + "' version " + data["mod"][modname]["version"] + " is already install. Do you want change it for version " + mod["version"] + "?")
  505.                 choice = Alert.input("yes/no:")
  506.                 if choice == "no":
  507.                     quit()
  508.  
  509.                 uninstall(data['mod'][modname], data)
  510.  
  511.         install(mod, data)
  512.  
  513.     elif argv[1] == 'uninstall':
  514.  
  515.         if argc < 3:
  516.             Alert.error("You must give a mod name", data)
  517.  
  518.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  519.         if not mod:
  520.             quit(data)
  521.        
  522.         if not mod["name"] in data['mod'].keys():
  523.             Alert.error(mod + " isn't installed!")
  524.         modname = mod['name']
  525.         uninstall(data['mod'][modname], data)
  526.  
  527.     elif argv[1] == 'uninstall_all':
  528.  
  529.         Alert.info("Restore all files")
  530.         for file in data['file']:
  531.             Alert.info("Restore '" + file + "'")
  532.             copyFile(BCKUP_PATH + file, file)
  533.         for file in data['otherfile']:
  534.             Alert.info("Restore '" + file + "'")
  535.             copyFile(BCKUP_PATH + file, file)
  536.         for file in data['remove_file']:
  537.             Alert.info("Restore '" + file + "'")
  538.             copyBFile(BCKUP_PATH + file, file)
  539.         for file in data['addfile']:
  540.             Alert.info("Remove '" + file + "'")
  541.             os.remove(file)
  542.  
  543.         data['file'] = {}
  544.         data["otherfile"] = {}
  545.         data["addfile"] = {}
  546.         data["remove_file"] = {}
  547.         data['mod'] = {}
  548.  
  549.     elif argv[1] == 'giveall':
  550.         Alert.list([mod + " version " + data['mod'][mod]['version'] + " by " + data['mod'][mod]["author"] for mod in data['mod']])
  551.  
  552.     elif argv[1] == 'valid_mod':
  553.         if argc < 3:
  554.             Alert.error("You must give a mod name", data)
  555.  
  556.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  557.         if mod and checkMod(mod, data):
  558.             Alert.info("Mod is valid")
  559.         else:
  560.             Alert.info("Mod isn't valid!")
  561.  
  562.     elif argv[1] == "info":
  563.         if argc < 3:
  564.             Alert.error("You must give a mod name", data)
  565.  
  566.         Alert.show = False
  567.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  568.         Alert.show = True
  569.  
  570.         if not mod:
  571.             Alert.info("Impossible to load mod.")
  572.         else:
  573.             Alert.info(mod["name"] + " version " + mod["version"] + " by " + mod["author"])
  574.             Alert.list(["\t" + line for line in mod["info"].split("\n")])
  575.         Alert.info()
  576.  
  577.     quit(data)
  578.  
  579. if __name__ == '__main__':
  580.     main(len(sys.argv), sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement