Guest User

Untitled

a guest
Oct 5th, 2020
52
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.07 KB | None | 0 0
  1. import clr
  2.  
  3.  
  4. from System.IO import Directory, File, Path, SearchOption
  5. from System import Environment, PlatformID, String, Exception
  6. from System.Text.RegularExpressions import Regex, RegexOptions, Match
  7.  
  8.            
  9.  
  10. #Support for parallel extraction
  11. clr.AddReference('System.Core')
  12. from System.Collections.Generic import List
  13. from System import Action
  14. from System.Threading.Tasks import Parallel, ParallelOptions
  15.  
  16. extractedcount=0
  17. totalfilecount=0
  18. lastfileprogress=0
  19. ########################################
  20. # https://github.com/TheCherry/ark-server-manager #
  21. ########################################
  22. import struct
  23. import zlib
  24. import sys
  25.  
  26. def str_to_l(st):
  27.     return struct.unpack('q', st)[0]
  28.  
  29. def z_unpack(src, dst):
  30.     global extractedcount, totalfilecount, lastfileprogress
  31.     with open(src, 'rb') as f_src:
  32.         with open(dst, 'wb') as f_dst:
  33.             f_src.read(8)
  34.             size1 = str_to_l(f_src.read(8))
  35.             f_src.read(8)
  36.             size2 = str_to_l(f_src.read(8))
  37.             if(size1 == -1641380927):
  38.                 size1 = 131072L
  39.             runs = (size2 + size1 - 1L) / size1
  40.             array = []
  41.             for i in range(runs):
  42.                 array.append(f_src.read(8))
  43.                 f_src.read(8)
  44.             for i in range(runs):
  45.                 to_read = array[i]
  46.                 compressed = f_src.read(str_to_l(to_read))
  47.                 decompressed = zlib.decompress(compressed)
  48.                 f_dst.write(decompressed)
  49.     Script.WriteToConsole("Extracted " + dst.Replace(ThisService.RootDirectory, ""))
  50.     File.Delete(src)
  51.     File.Delete(src + ".uncompressed_size")
  52.     extractedcount=extractedcount+1
  53.     progress=round((float(extractedcount)/totalfilecount)*100,0)
  54.     if progress > lastfileprogress + 4:
  55.       lastfileprogress=progress
  56.       ThisTaskStep.UpdateProgress(progress)
  57.      
  58. #######################################################################
  59. # https://github.com/barrycarey/Ark_Mod_Downloader/blob/master/Ark_Mod_Downloader.py #
  60. #######################################################################
  61. import os
  62. import struct
  63. from collections import OrderedDict
  64. map_names = []
  65. map_count=0
  66. temp_mod_path = os.path.join(ThisService.RootDirectory, "ShooterGame/Content/Mods")
  67. meta_data = OrderedDict([])
  68.  
  69. def parse_base_info(modid):
  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.         print("[+] Collecting Mod Meta Data From modmeta.info")
  103.         print("[+] 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.         print("[+] 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.  
  217. # Only extract files the correct folder depending on operating system
  218. oseditor="WindowsNoEditor" if Environment.OSVersion.Platform == PlatformID.Win32NT else "LinuxNoEditor"
  219. noeditor=Path.Combine(InstallPath, oseditor )
  220.  
  221. # Use other OS folder if it doesn't exist.
  222. if not Directory.Exists(noeditor) :
  223.   oseditor = "LinuxNoEditor" if Environment.OSVersion.Platform == PlatformID.Win32NT else "WindowsNoEditor"
  224.   noeditor = Path.Combine(InstallPath, oseditor)  
  225.  
  226. # Extract and delete all .z files
  227. actions = List[Action]()
  228. for zfile in Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories):
  229.  file=Path.Combine(Path.GetDirectoryName(zfile), Path.GetFileNameWithoutExtension(zfile))
  230.  action=Action(lambda a=zfile, b=file: z_unpack(a, b))
  231.  actions.Add(action)
  232.  
  233. options=ParallelOptions()
  234. #Extract 2 files at a time.
  235. options.MaxDegreeOfParallelism = 6
  236. totalfilecount=actions.Count
  237. ThisTaskStep.WriteLog(String.Format("Extracting {0} files...", totalfilecount))
  238. ThisTaskStep.UpdateProgress(0)
  239. Parallel.Invoke(options, actions.ToArray())
  240.  
  241. # Move folder to correct location. Delete if it already exists.
  242. # Define modid before FileId is altered so we write the correct id to inifile
  243. modid = FileId
  244. if FileId > 2147483647:
  245.   diff = FileId-2147483647
  246.   FileId = -2147483647 + diff - 2                                                                        
  247.  
  248. modfolder=Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Content/Mods/{0}", FileId))
  249. if Directory.Exists(modfolder) :
  250.   Directory.Delete(modfolder, True)
  251. Directory.Move(Path.Combine(InstallPath, oseditor), modfolder)
  252.  
  253. # Update ini file
  254. serveros = "WindowsServer" if Environment.OSVersion.Platform == PlatformID.Win32NT else "LinuxServer"
  255. inifile = Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Saved/Config/{0}/GameUserSettings.ini", serveros))
  256. pattern="ActiveMods[ \t]*=[ \t]*(?<ActiveMods>[0-9, \t]*)"
  257. filecontents = File.ReadAllText(inifile)
  258. match = Regex.Match(filecontents, pattern, RegexOptions.IgnoreCase)
  259. if match.Success :
  260.   activemods = match.Groups["ActiveMods"].Value
  261.   if String.IsNullOrEmpty(activemods) or activemods.IndexOf(modid.ToString()) == -1 :
  262.     if activemods.Length > 0 :
  263.       activemods = activemods + ","
  264.       activemods = activemods + modid.ToString()
  265.       filecontents=filecontents.Replace(match.Groups["ActiveMods"].Value, activemods)
  266.     else :
  267.       activemods = modid.ToString()
  268.       filecontents = filecontents.Substring(0, match.Groups["ActiveMods"].Index) + activemods + filecontents.Substring(match.Groups["ActiveMods"].Index)
  269.     File.WriteAllText(inifile, filecontents)
  270.  
  271. #Create .mod
  272. parse_base_info(FileId.ToString())
  273. parse_meta_data(FileId.ToString())
  274. create_mod_file(FileId.ToString())
  275.  
  276. # Delete folder
  277. if Directory.Exists(InstallPath) :
  278.   Directory.Delete(InstallPath, True)
  279.  
Add Comment
Please, Sign In to add comment