7163D

c3mm.py

Oct 4th, 2016
91
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

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×