Advertisement
7163D

c3mm.py

Oct 4th, 2016
212
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.15 KB | None | 0 0
  1. # Cossacks 3 Mod Manager version 1.3.3
  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.3 === #
  7.     >Now, mod file has a field 'otherfile'. Use this to replace a file by an other. Two mods can't replace the same file
  8. """
  9.  
  10.  
  11. """
  12. Structur uses
  13. Mod  := {'name':name, 'version':version, 'info':info, 'author':author, file':{name => list of (str old, str new)}, 'otherfile':{name => new}}
  14. Data := {'mod':{name => mod mod}, 'file':{name => list of str mod}}
  15. """
  16.  
  17. import json, sys, os, time
  18. from random import randrange
  19.  
  20. CONFIG_PATH = "c3mm/"
  21. MOD_PATH    = "mod/"
  22. COPY_PATH   = "c3mm/copy/"
  23. BCKUP_PATH  = "c3mm/bckup/"
  24. INFO_PATH   = "c3mm/c3mm.json"
  25. LOG_PATH    = "c3mm/log.txt"
  26. HELP = """
  27. Cossacks3 (text) Mod Manager Version 1.3.3.
  28. Type python c3mm.py action [parameter]
  29.     action:
  30.         install modname   -> intall a mod find by name
  31.         uninstall modname -> unintall a mod find by name
  32.         help              -> show this message
  33.         uninstall_all     -> uninstall all mod
  34.         giveall           -> print all installed mods
  35.         valid_mod modname -> check if mod modname is valid
  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.  
  55.     def info(i):
  56.         Alert.linfo.append(i)
  57.         print(i)
  58.  
  59.     def error(e, data):
  60.         Alert.lerror.append(e)
  61.         print(e)
  62.         quit(data)
  63.  
  64.     def input(m):
  65.         return input(m)
  66.  
  67.     def list(l):
  68.         for i in l:
  69.             print(i)
  70.  
  71. def loadjson(path):
  72.     return json.loads(open(path, 'r').read())
  73.  
  74. def savejson(path, data):
  75.     open(path, "w").write(json.dumps(data))
  76.  
  77. def loadjsonmod(path):
  78.     mod = loadjson(path)
  79.     if not "otherfile" in mod:
  80.         mod["otherfile"] = {}
  81.     if not validMod(mod):
  82.         return False
  83.     return mod
  84.  
  85. def mkPath(path):
  86.     rpath = ""
  87.     for dir in path.split('/')[:-1]:
  88.         rpath += dir
  89.         try:
  90.             os.mkdir(rpath)
  91.         except:
  92.             pass
  93.         rpath += '/'
  94.  
  95. def copyFile(file, path):
  96.     mkPath(path)
  97.     open(path, "w").write(open(file, 'r').read())
  98.  
  99. def copyBFile(file, path):
  100.     mkPath(path)
  101.     open(path, "wb").write(open(file, 'rb').read())
  102.  
  103. def checkMod(mod, data):
  104.     """
  105.     checkMod function, use to check if a mod is valid
  106.     Mod mod     -> mod to check
  107.     Bool return -> is the mod valid?
  108.     """
  109.     for file in mod['file']:
  110.         Alert.info("Check existence of '" + file + "'")
  111.         try:
  112.             f = open(file, 'r').read()
  113.             Alert.info("'" + file + "' exist.")
  114.         except:
  115.             Alert.info("'" + file + "' don't exist.")
  116.             return False
  117.  
  118.         for old in mod['file'][file]:
  119.             Alert.info("Check exsitence of '" + old + "' in '" + file + "'")
  120.             if not old in f:
  121.                 Alert.info("Not found.")
  122.                 return False
  123.             else:
  124.                 Alert.info("Found.")
  125.  
  126.     for file in mod["otherfile"]:
  127.         if file in data["otherfile"]:
  128.             Alert.info(file + " is already modified by " + data["otherfile"][file][0])
  129.             Alert.info("You have to uninstall " + data["otherfile"][file][0] + " to install " + mod['name'])
  130.             return False
  131.  
  132.     return True
  133.  
  134. def validMod(mod):
  135.     """
  136.     validMod function, assert not error occured when install mod
  137.     Mod mod -> mod to check
  138.     Bool return
  139.     """
  140.     for field in ['name', 'info', 'file', 'version', 'author']:
  141.         if not field in mod:
  142.             Alert.info("Missing field '" + field + "'")
  143.             return False
  144.  
  145.     if not type(mod['file']) == type({}):
  146.         Alert.info("Wrong data mod")
  147.         return False
  148.  
  149.     for file in mod['file']:
  150.         if not type(mod['file'][file]) == type({}):
  151.             Alert.info("Wrong data mod")
  152.             return False
  153.         try:
  154.             for old in mod['file'][file]:
  155.                 new = mod['file'][file][old]
  156.                 if not type(old) == type("") or not type(new) == type(""):
  157.                     Alert.info("Wrong data mod")
  158.                     return False
  159.         except:
  160.             Alert.info("Wrong data mod")
  161.             return False
  162.  
  163.     return True
  164.  
  165.  
  166. def apply(mod):
  167.     """
  168.     apply function, use to modify files
  169.     Mod mod -> mod that contain all modifications
  170.     None return
  171.     """
  172.     mfile = {}
  173.     for file in mod['file']:
  174.         Alert.info("Calculate'" + file + '"')
  175.         f = open(file, 'r').read()
  176.  
  177.         #Calculate modifications
  178.         for old in mod['file'][file]:
  179.             new = mod['file'][file][old]
  180.             f = f.replace(old, new)
  181.  
  182.         mfile[file] = f
  183.    
  184.     #Apply mod
  185.     for file in mfile:
  186.         Alert.info("Patching '" + file + '"')
  187.         open(file, 'w').write(mfile[file])
  188.  
  189.     for file in mod["otherfile"]:
  190.         Alert.info("Replace '" + file+ "'")
  191.         copyBFile(MOD_PATH + mod["otherfile"][file], file)
  192.  
  193. def install(mod, data):
  194.     """
  195.     install function, use to add a mod.
  196.     Mod mod   -> mod to install
  197.     Data data -> c3mm main data
  198.     None return
  199.     """
  200.     if not checkMod(mod, data):
  201.         Alert.error("'" + mod['name'] + "' mod version " + mod['version'] + " isn't valid", data)
  202.  
  203.     Alert.info("Install '" + mod['name'] + "' version " + mod['version'])
  204.  
  205.     #Add some "signature"
  206.     for file in mod['file']:
  207.         for old in mod['file'][file]:
  208.             mod['file'][file][old] = mod['file'][file][old] + "//" + str(randrange(100000, 999999))
  209.  
  210.     #Add mod to list of installed mod
  211.     data['mod'][mod['name']] = mod
  212.  
  213.     #Adding mod in list of file modification
  214.     for file in mod['file']:
  215.         if not file in data['file']:
  216.             data['file'][file] = [mod['name']]
  217.             #backup file
  218.             copyFile(file, BCKUP_PATH + file)
  219.         else:
  220.             data['file'][file].append(mod['name'])
  221.  
  222.     for file in mod["otherfile"]:
  223.         if not file in data['otherfile']:
  224.             data['otherfile'][file] = [mod['name']]
  225.             #backup file
  226.             copyFile(file, BCKUP_PATH + file)
  227.         else:
  228.             data['otherfile'][file].append(mod['name'])
  229.  
  230.     apply(mod)
  231.  
  232.     for file in mod['file']:
  233.         #make a save of the file
  234.         copyFile(file, COPY_PATH + file)
  235.  
  236. def uninstall(mod, data):
  237.     """
  238.     uninstall function, use to delete a mod.
  239.     Mod mod   -> mod to uninstall
  240.     Data data -> c3mm main data
  241.     None return
  242.     """
  243.     uninstallmod = mkuninstallmod(mod)
  244.  
  245.     if not checkMod(uninstallmod, data):
  246.         Alert.info("'" + mod['name'] + "' mod uninstaller isn't valid.")
  247.         Alert.info("Do you want restore all files concerned by this mod?")
  248.         Alert.info("List of mod will uninstalled in this case:")
  249.         modlist = []
  250.         for file in mod['file']:
  251.             for mod in data['file'][file]:
  252.                 if not mod in modlist:
  253.                     modlist.append(mod)
  254.                     Alert.info(mod)
  255.         choice = Alert.chocie("Restore all file? yes/no:")
  256.         if choice == 'no':
  257.             quit(data)
  258.  
  259.         filelist = [file for file in mod['file']]
  260.         for file in filelist:
  261.             Alert("Restore '" + file + "'")
  262.             copyFile(BCKUP_PATH + file, file)
  263.             data['mod'][mod]['file'].remove(file)
  264.  
  265.         for mod in modlist():
  266.             uninstall(mod, data)
  267.  
  268.        
  269.  
  270.     #Delete mod to list of installed mod
  271.     del data['mod'][mod['name']]
  272.  
  273.     #Delete mod of list of file modification
  274.     for file in data['file']:
  275.         if mod['name'] in data['file'][file]:
  276.             data['file'][file].remove(mod['name'])
  277.  
  278.     Alert.info("Uninstall '" + mod['name'] + "' version " + mod['version'])
  279.  
  280.     for file in mod["otherfile"]:
  281.         Alert.info("Restore " + file)
  282.         copyBFile(BCKUP_PATH + file, file)
  283.         del data["otherfile"][file]
  284.  
  285.     apply(uninstallmod)
  286.  
  287. def mkuninstallmod(mod):
  288.     """
  289.     mkuninstallmod function, use to make an uninstalator from a mod
  290.     Mod mod    -> mod to uninstall
  291.     Mod return -> uninstalator mod
  292.     """
  293.     uninstallmod = {}
  294.     uninstallmod['file'] = {}
  295.  
  296.     for file in mod['file']:
  297.         uninstallmod['file'][file] = {}
  298.         for old in mod['file'][file]:
  299.             new = mod['file'][file][old]
  300.             #For each modification of mod, inverse new and old
  301.             uninstallmod['file'][file][new] = old
  302.  
  303.     uninstallmod["otherfile"] = {}
  304.  
  305.     return uninstallmod
  306.  
  307. def checkStatu(data):
  308.     """
  309.     checkStatu funtion, check if a modded file has been modified since last launch of this program
  310.     Data data   -> main data
  311.     Bool return ->
  312.     """
  313.     badfile = []
  314.     #Check all modded file
  315.     for file in data['file']:
  316.         if open(file, 'r').read() != open(COPY_PATH + file, 'r').read():
  317.             Alert.info("File '" + file + "' has been modified since last launch of c3mm.")
  318.             badfile.append(file)
  319.  
  320.     badotherfile = []
  321.     for file in data["otherfile"]:
  322.         if open(file, 'rb').read() != open(BCKUP_PATH + file, 'rb').read():
  323.             Alert.info("File '" + file + "' has been modified since last launch of c3mm.")
  324.             badotherfile.append(file)
  325.  
  326.     badmod = []
  327.     #Uninstall all mod concerned by these files
  328.     for file in badfile:
  329.         for mod in data['file'][file]:
  330.             if not mod in badmod and not checkMod(mkuninstallmod(data['mod'][mod]), data):
  331.                 badmod.append(mod)
  332.     for file in badotherfile:
  333.         if not data["otherfile"][file][0] in badmod:
  334.             badmod.append(data["otherfile"][file][0])
  335.  
  336.     for mod in badmod:
  337.         _mod = data['mod'][mod]
  338.         for file in badfile:
  339.             if file in _mod['file']:
  340.                 del _mod['file'][file]
  341.         uninstall(_mod, data)
  342.  
  343.     #make a new copy of all modified files
  344.     for file in badfile:
  345.         copyFile(file, COPY_PATH + file)
  346.  
  347.     return len(badmod) == 0
  348.  
  349. def quit(data):
  350.     """
  351.     Save all data before exit the program
  352.     """
  353.     dfile = []
  354.     for file in data['file']:
  355.         if data['file'][file] == []:
  356.             dfile.append(file)
  357.  
  358.     for file in dfile:
  359.         del data['file'][file]
  360.  
  361.     savejson(INFO_PATH, data)
  362.  
  363.     logs = time.strftime('%D %H:%M\n',time.localtime())
  364.     for info in Alert.linfo:
  365.         logs += info + '\n'
  366.     for error in Alert.lerror:
  367.         logs += error + '\n'
  368.  
  369.     open(LOG_PATH, "a").write(logs)
  370.     exit()
  371.  
  372. def main(argc, argv):
  373.  
  374.     try:
  375.         data = loadjson(INFO_PATH)
  376.     except:
  377.         open(INFO_PATH, "w").write('{"mod":{}, "file":{}}')
  378.         data = loadjson(INFO_PATH)
  379.  
  380.     if not "otherfile" in data:
  381.         data["otherfile"] = {}
  382.  
  383.     if not checkStatu(data):
  384.         quit(data)
  385.  
  386.     elif argc == 1 or argv[1] == "help":
  387.         print(HELP)
  388.  
  389.     elif argv[1] == 'install':
  390.  
  391.         if argc < 3:
  392.             Alert.error("You must give a mod name", data)
  393.  
  394.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  395.         if not mod:
  396.             quit(data)
  397.         modname = mod['name']
  398.  
  399.         if modname in data['mod']:
  400.             if mod["version"] == data["mod"][modname]["version"]:
  401.                 Alert.error("Mod '" + modname + "' version " + mod["version"] + " is already install.", data)
  402.             else:
  403.                 Alert.info("Mod '" + modname + "' version " + data["mod"][modname]["version"] + " is already install. Do you want change it for version " + mod["version"] + "?")
  404.                 choice = Alert.input("yes/no:")
  405.                 if choice == "no":
  406.                     quit()
  407.  
  408.                 uninstall(data['mod'][modname], data)
  409.  
  410.         install(mod, data)
  411.  
  412.     elif argv[1] == 'uninstall':
  413.  
  414.         if argc < 3:
  415.             Alert.error("You must give a mod name", data)
  416.  
  417.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  418.         if not mod:
  419.             quit(data)
  420.         modname = mod['name']
  421.         uninstall(data['mod'][modname], data)
  422.  
  423.     elif argv[1] == 'uninstall_all':
  424.  
  425.         Alert.info("Restore all files")
  426.         for file in data['file']:
  427.             Alert.info("Restore '" + file + "'")
  428.             copyFile(BCKUP_PATH + file, file)
  429.  
  430.         data['file'] = {}
  431.         data['mod'] = {}
  432.  
  433.     elif argv[1] == 'giveall':
  434.         Alert.list([mod + " version " + data['mod'][mod]['version'] for mod in data['mod']])
  435.  
  436.     elif argv[1] == 'valid_mod':
  437.         if argc < 3:
  438.             Alert.error("You must give a mod name", data)
  439.  
  440.         mod = loadjsonmod(MOD_PATH + argv[2] + '.json')
  441.         if mod and checkMod(mod, data):
  442.             Alert.info("Mod is valid")
  443.         else:
  444.             Alert.info("Mod isn't valid!")
  445.  
  446.     quit(data)
  447.  
  448. if __name__ == '__main__':
  449.     main(len(sys.argv), sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement