SHARE
TWEET

c3mm.py

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