Advertisement
DennisSkov

TCAdmin Workshop install script

Jan 21st, 2021
1,208
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.83 KB | None | 0 0
  1. import clr
  2.  
  3. from System.IO import Directory, File, Path, SearchOption
  4. from System import Environment, PlatformID, String, Exception
  5. from System.Text.RegularExpressions import Regex, RegexOptions, Match
  6.  
  7. #Support for parallel extraction
  8. clr.AddReference('System.Core')
  9. from System.Collections.Generic import List
  10. from System import Action
  11. from System.Threading.Tasks import Parallel, ParallelOptions
  12.  
  13. extractedcount=0
  14. totalfilecount=0
  15. lastfileprogress=0
  16.    
  17. ########################################
  18. # https://github.com/TheCherry/ark-server-manager #
  19. ########################################
  20. import struct
  21. import zlib
  22. import sys
  23.  
  24. def str_to_l(st):
  25.     return struct.unpack('q', st)[0]
  26.  
  27. def z_unpack(src, dst):
  28.     global extractedcount, totalfilecount, lastfileprogress
  29.     with open(src, 'rb') as f_src:
  30.         with open(dst, 'wb') as f_dst:
  31.             f_src.read(8)
  32.             size1 = str_to_l(f_src.read(8))
  33.             f_src.read(8)
  34.             size2 = str_to_l(f_src.read(8))
  35.             if(size1 == -1641380927):
  36.                 size1 = 131072L
  37.             runs = (size2 + size1 - 1L) / size1
  38.             array = []
  39.             for i in range(runs):
  40.                 array.append(f_src.read(8))
  41.                 f_src.read(8)
  42.             for i in range(runs):
  43.                 to_read = array[i]
  44.                 compressed = f_src.read(str_to_l(to_read))
  45.                 decompressed = zlib.decompress(compressed)
  46.                 f_dst.write(decompressed)
  47.     Script.WriteToConsole("Extracted " + dst.Replace(ThisService.RootDirectory, ""))
  48.     File.Delete(src)
  49.     File.Delete(src + ".uncompressed_size")
  50.     extractedcount=extractedcount+1
  51.     progress=round((float(extractedcount)/totalfilecount)*100,0)
  52.     ThisTaskStep.WriteLog(String.Format("Extracted {0}/{1} files...", extractedcount, totalfilecount))
  53.     if progress > lastfileprogress + 4:
  54.       lastfileprogress=progress
  55.       ThisTaskStep.UpdateProgress(progress)
  56.      
  57. #######################################################################
  58. # https://github.com/barrycarey/Ark_Mod_Downloader/blob/master/Ark_Mod_Downloader.py #
  59. #######################################################################
  60. import os
  61. import struct
  62. from collections import OrderedDict
  63. map_names = []
  64. map_count=0
  65. temp_mod_path = os.path.join(ThisService.RootDirectory, "ShooterGame/Content/Mods")
  66. meta_data = OrderedDict([])
  67.  
  68. def parse_base_info(modid):
  69.    
  70.         Script.WriteToConsole("[+] Collecting Mod Details From mod.info")
  71.  
  72.         mod_info = os.path.join(temp_mod_path, modid, "mod.info")
  73.  
  74.         if not os.path.isfile(mod_info):
  75.             raise Exception("[x] Failed to locate mod.info. Cannot Continue. Please try again.")
  76.             return False
  77.  
  78.         with open(mod_info, "rb") as f:
  79.             read_ue4_string(f)
  80.             map_count = struct.unpack('i', f.read(4))[0]
  81.  
  82.             for i in range(map_count):
  83.                 cur_map = read_ue4_string(f)
  84.                 if cur_map:
  85.                     map_names.append(cur_map)
  86.  
  87.         return True
  88.  
  89. def parse_meta_data(modid):
  90.         """
  91.        Parse the modmeta.info files and extract the key value pairs need to for the .mod file.
  92.        How To Parse modmeta.info:
  93.            1. Read 4 bytes to tell how many key value pairs are in the file
  94.            2. Read next 4 bytes tell us how many bytes to read ahead to get the key
  95.            3. Read ahead by the number of bytes retrieved from step 2
  96.            4. Read next 4 bytes to tell how many bytes to read ahead to get value
  97.            5. Read ahead by the number of bytes retrieved from step 4
  98.            6. Start at step 2 again
  99.        :return: Dict
  100.        """
  101.  
  102.         ThisTaskStep.WriteLog("[+] Collecting Mod Meta Data From modmeta.info")
  103.         ThisTaskStep.WriteLog("[+] Located The Following Meta Data:")
  104.  
  105.         mod_meta = os.path.join(temp_mod_path, modid, "modmeta.info")
  106.         if not os.path.isfile(mod_meta):
  107.             raise Exception("[x] Failed To Locate modmeta.info. Cannot continue without it. Please try again.")
  108.             return False
  109.  
  110.         with open(mod_meta, "rb") as f:
  111.  
  112.             total_pairs = struct.unpack('i', f.read(4))[0]
  113.  
  114.             for i in range(total_pairs):
  115.  
  116.                 key, value = "", ""
  117.  
  118.                 key_bytes = struct.unpack('i', f.read(4))[0]
  119.                 key_flag = False
  120.                 if key_bytes < 0:
  121.                     key_flag = True
  122.                     key_bytes -= 1
  123.  
  124.                 if not key_flag and key_bytes > 0:
  125.  
  126.                     raw = f.read(key_bytes)
  127.                     key = raw[:-1].decode()
  128.  
  129.                 value_bytes = struct.unpack('i', f.read(4))[0]
  130.                 value_flag = False
  131.                 if value_bytes < 0:
  132.                     value_flag = True
  133.                     value_bytes -= 1
  134.  
  135.                 if not value_flag and value_bytes > 0:
  136.                     raw = f.read(value_bytes)
  137.                     value = raw[:-1].decode()
  138.  
  139.                 # TODO This is a potential issue if there is a key but no value
  140.                 if key and value:
  141.                     Script.WriteToConsole("[!] " + key + ":" + value)
  142.                     meta_data[key] = value
  143.  
  144.         return True
  145.  
  146. def create_mod_file(modid):
  147.         """
  148.        Create the .mod file.
  149.        This code is an adaptation of the code from Ark Server Launcher.  All credit goes to Face Wound on Steam
  150.        :return:
  151.        """
  152.         if not parse_base_info(modid) or not parse_meta_data(modid):
  153.             return False
  154.  
  155.         ThisTaskStep.WriteLog("[+] Writing .mod File")
  156.         with open(os.path.join(temp_mod_path, modid + ".mod"), "w+b") as f:
  157.  
  158.             modid = int(modid)
  159.             if modid > 2147483647:
  160.               diff = modid-2147483647
  161.               modid = -2147483647 + diff - 2                                          
  162.             f.write(struct.pack('ixxxx', modid))  # Needs 4 pad bits
  163.             write_ue4_string("ModName", f)
  164.             write_ue4_string("", f)
  165.  
  166.             map_count = len(map_names)
  167.             f.write(struct.pack("i", map_count))
  168.  
  169.             for m in map_names:
  170.                 write_ue4_string(m, f)
  171.  
  172.             # Not sure of the reason for this
  173.             num2 = 4280483635
  174.             f.write(struct.pack('I', num2))
  175.             num3 = 2
  176.             f.write(struct.pack('i', num3))
  177.  
  178.             if "ModType" in meta_data:
  179.                 mod_type = b'1'
  180.             else:
  181.                 mod_type = b'0'
  182.  
  183.             # TODO The packing on this char might need to be changed
  184.             f.write(struct.pack('p', mod_type))
  185.             meta_length = len(meta_data)
  186.             f.write(struct.pack('i', meta_length))
  187.  
  188.             for k, v in meta_data.items():
  189.                 write_ue4_string(k, f)
  190.                 write_ue4_string(v, f)
  191.  
  192.         return True
  193.  
  194. def read_ue4_string(file):
  195.         count = struct.unpack('i', file.read(4))[0]
  196.         flag = False
  197.         if count < 0:
  198.             flag = True
  199.             count -= 1
  200.  
  201.         if flag or count <= 0:
  202.             return ""
  203.  
  204.         return file.read(count)[:-1].decode()
  205.  
  206. def write_ue4_string(string_to_write, file):
  207.         string_length = len(string_to_write) + 1
  208.         file.write(struct.pack('i', string_length))
  209.         barray = bytearray(string_to_write, "utf-8")
  210.         file.write(barray)
  211.         file.write(struct.pack('p', b'0'))
  212.  
  213. ###########################################
  214. ###########################################
  215. ###########################################
  216. # If the content folder doesn't exist use downloads                                                
  217. if not Directory.Exists(InstallPath) :
  218.   InstallPath=InstallPath.Replace("/content/", "/downloads/")                                                            
  219.  
  220. # Always use Windows files. Linux files cause the game server to crash at startup.
  221. oseditor = "WindowsNoEditor"
  222. noeditor=Path.Combine(InstallPath, oseditor)
  223.  
  224. # Use other OS folder if it doesn't exist.
  225. if not Directory.Exists(noeditor) :
  226.   oseditor = "LinuxNoEditor"
  227.   noeditor = Path.Combine(InstallPath, oseditor)  
  228.  
  229. if Environment.OSVersion.Platform == PlatformID.Win32NT:
  230. # Extract and delete all .z files (Windows)
  231.   actions = List[Action]()
  232.   currentNumber = 0
  233.   zfiles=Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories)
  234.   totalfilecount=zfiles.Count
  235.   for zfile in zfiles:
  236.    file=Path.Combine(Path.GetDirectoryName(zfile), Path.GetFileNameWithoutExtension(zfile))
  237.    action=Action(lambda a=zfile, b=file: z_unpack(a, b))
  238.    actions.Add(action)
  239.    
  240.   options=ParallelOptions()
  241.   #Extract 2 files at a time.
  242.   options.MaxDegreeOfParallelism = 2
  243.   totalfilecount=actions.Count
  244.   Parallel.Invoke(options, actions.ToArray())
  245. else:
  246.   # Extract and delete all .z files (Linux)
  247.   zfiles=Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories)
  248.   totalfilecount=zfiles.Count
  249.   currentNumber = 0
  250.   for zfile in zfiles:
  251.     currentNumber += 1
  252.     progress = (float(currentNumber) / float(totalfilecount)) * 100
  253.     file=Path.Combine(Path.GetDirectoryName(zfile), Path.GetFileNameWithoutExtension(zfile))
  254.     z_unpack(zfile, file)
  255.     File.Delete(zfile)
  256.     File.Delete(zfile + ".uncompressed_size")
  257.  
  258. # Move folder to correct location. Delete if it already exists.
  259. # Define modid before FileId is altered so we write the correct id to inifile
  260. modid = FileId
  261. if FileId > 2147483647:
  262.   diff = FileId-2147483647
  263.   FileId = -2147483647 + diff - 2
  264.  
  265. modfolder=Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Content/Mods/{0}", modid))
  266. if Directory.Exists(modfolder) :
  267.   Directory.Delete(modfolder, True)
  268. Directory.Move(Path.Combine(InstallPath, oseditor), modfolder)
  269.  
  270. # Update ini file
  271. serveros = "WindowsServer" if Environment.OSVersion.Platform == PlatformID.Win32NT else "LinuxServer"
  272. inifile = Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Saved/Config/{0}/GameUserSettings.ini", serveros))
  273. pattern="ActiveMods[ \t]*=[ \t]*(?<ActiveMods>[0-9, \t]*)"
  274. filecontents = File.ReadAllText(inifile)
  275. match = Regex.Match(filecontents, pattern, RegexOptions.IgnoreCase)
  276. if match.Success :
  277.   activemods = match.Groups["ActiveMods"].Value
  278.   if String.IsNullOrEmpty(activemods) or activemods.IndexOf(modid.ToString()) == -1 :
  279.     if activemods.Length > 0 :
  280.       activemods = activemods + ","
  281.       activemods = activemods + modid.ToString()
  282.       filecontents=filecontents.Replace(match.Groups["ActiveMods"].Value, activemods)
  283.     else :
  284.       activemods = modid.ToString()
  285.       filecontents = filecontents.Substring(0, match.Groups["ActiveMods"].Index) + activemods + filecontents.Substring(match.Groups["ActiveMods"].Index)
  286.     File.WriteAllText(inifile, filecontents)
  287.  
  288. #Create .mod
  289. parse_base_info(modid.ToString())
  290. parse_meta_data(modid.ToString())
  291. create_mod_file(modid.ToString())
  292.  
  293. # Delete folder
  294. if Directory.Exists(InstallPath) :
  295.   Directory.Delete(InstallPath, True)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement