Advertisement
7163D

c3mm.py

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