import clr
from System.IO import Directory, File, Path, SearchOption
from System import Environment, PlatformID, String, Exception
from System.Text.RegularExpressions import Regex, RegexOptions, Match
import sys
if Environment.OSVersion.Platform == PlatformID.Win32NT :
sys.path.append("C:\\Python27\\Lib")
else :
sys.path.append("/usr/lib/python2.7")
########################################
# https://github.com/TheCherry/ark-server-manager #
########################################
import struct
import zlib
import sys
def str_to_l(st):
return struct.unpack('q', st)[0]
def z_unpack(src, dst):
with open(src, 'rb') as f_src:
with open(dst, 'wb') as f_dst:
f_src.read(8)
size1 = str_to_l(f_src.read(8))
f_src.read(8)
size2 = str_to_l(f_src.read(8))
if(size1 == -1641380927):
size1 = 131072L
runs = (size2 + size1 - 1L) / size1
array = []
for i in range(runs):
array.append(f_src.read(8))
f_src.read(8)
for i in range(runs):
to_read = array[i]
compressed = f_src.read(str_to_l(to_read))
decompressed = zlib.decompress(compressed)
f_dst.write(decompressed)
#######################################################################
# https://github.com/barrycarey/Ark_Mod_Downloader/blob/master/Ark_Mod_Downloader.py #
#######################################################################
import os
import struct
from collections import OrderedDict
map_names = []
map_count=0
temp_mod_path = os.path.join(ThisService.RootDirectory, "ShooterGame/Content/Mods")
meta_data = OrderedDict([])
def parse_base_info(modid):
Script.WriteToConsole("[+] Collecting Mod Details From mod.info")
mod_info = os.path.join(temp_mod_path, modid, "mod.info")
if not os.path.isfile(mod_info):
Script.WriteToConsole("[x] Failed to locate mod.info. Cannot Continue. Aborting")
return False
with open(mod_info, "rb") as f:
read_ue4_string(f)
map_count = struct.unpack('i', f.read(4))[0]
for i in range(map_count):
cur_map = read_ue4_string(f)
if cur_map:
map_names.append(cur_map)
return True
def parse_meta_data(modid):
"""
Parse the modmeta.info files and extract the key value pairs need to for the .mod file.
How To Parse modmeta.info:
1. Read 4 bytes to tell how many key value pairs are in the file
2. Read next 4 bytes tell us how many bytes to read ahead to get the key
3. Read ahead by the number of bytes retrieved from step 2
4. Read next 4 bytes to tell how many bytes to read ahead to get value
5. Read ahead by the number of bytes retrieved from step 4
6. Start at step 2 again
:return: Dict
"""
print("[+] Collecting Mod Meta Data From modmeta.info")
print("[+] Located The Following Meta Data:")
mod_meta = os.path.join(temp_mod_path, modid, "modmeta.info")
if not os.path.isfile(mod_meta):
Script.WriteToConsole("[x] Failed To Locate modmeta.info. Cannot continue without it. Aborting")
return False
with open(mod_meta, "rb") as f:
total_pairs = struct.unpack('i', f.read(4))[0]
for i in range(total_pairs):
key, value = "", ""
key_bytes = struct.unpack('i', f.read(4))[0]
key_flag = False
if key_bytes < 0:
key_flag = True
key_bytes -= 1
if not key_flag and key_bytes > 0:
raw = f.read(key_bytes)
key = raw[:-1].decode()
value_bytes = struct.unpack('i', f.read(4))[0]
value_flag = False
if value_bytes < 0:
value_flag = True
value_bytes -= 1
if not value_flag and value_bytes > 0:
raw = f.read(value_bytes)
value = raw[:-1].decode()
# TODO This is a potential issue if there is a key but no value
if key and value:
Script.WriteToConsole("[!] " + key + ":" + value)
meta_data[key] = value
return True
def create_mod_file(modid):
"""
Create the .mod file.
This code is an adaptation of the code from Ark Server Launcher. All credit goes to Face Wound on Steam
:return:
"""
if not parse_base_info(modid) or not parse_meta_data(modid):
return False
print("[+] Writing .mod File")
with open(os.path.join(temp_mod_path, modid + ".mod"), "w+b") as f:
modid = int(modid)
f.write(struct.pack('ixxxx', modid)) # Needs 4 pad bits
write_ue4_string("ModName", f)
write_ue4_string("", f)
map_count = len(map_names)
f.write(struct.pack("i", map_count))
for m in map_names:
write_ue4_string(m, f)
# Not sure of the reason for this
num2 = 4280483635
f.write(struct.pack('I', num2))
num3 = 2
f.write(struct.pack('i', num3))
if "ModType" in meta_data:
mod_type = b'1'
else:
mod_type = b'0'
# TODO The packing on this char might need to be changed
f.write(struct.pack('p', mod_type))
meta_length = len(meta_data)
f.write(struct.pack('i', meta_length))
for k, v in meta_data.items():
write_ue4_string(k, f)
write_ue4_string(v, f)
return True
def read_ue4_string(file):
count = struct.unpack('i', file.read(4))[0]
flag = False
if count < 0:
flag = True
count -= 1
if flag or count <= 0:
return ""
return file.read(count)[:-1].decode()
def write_ue4_string(string_to_write, file):
string_length = len(string_to_write) + 1
file.write(struct.pack('i', string_length))
barray = bytearray(string_to_write, "utf-8")
file.write(barray)
file.write(struct.pack('p', b'0'))
###########################################
###########################################
###########################################
# Only extract files the correct folder depending on operating system
oseditor="WindowsNoEditor" if Environment.OSVersion.Platform == PlatformID.Win32NT else "LinuxNoEditor"
noeditor=Path.Combine(InstallPath, oseditor )
# Use other OS folder if it doesn't exist.
if not Directory.Exists(noeditor) :
oseditor = "LinuxNoEditor" if Environment.OSVersion.Platform == PlatformID.Win32NT else "WindowsNoEditor"
noeditor = Path.Combine(InstallPath, oseditor)
# Extract and delete all .z files
for zfile in Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories):
file=Path.Combine(Path.GetDirectoryName(zfile), Path.GetFileNameWithoutExtension(zfile))
z_unpack(zfile, file)
Script.WriteToConsole("Extracted " + file)
File.Delete(zfile)
File.Delete(zfile + ".uncompressed_size")
# Move folder to correct location. Delete if it already exists.
modfolder=Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Content/Mods/{0}", FileId))
if Directory.Exists(modfolder) :
Directory.Delete(modfolder, True)
Directory.Move(Path.Combine(InstallPath, oseditor), modfolder)
# Update ini file
serveros = "WindowsServer" if Environment.OSVersion.Platform == PlatformID.Win32NT else "LinuxServer"
inifile = Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Saved/Config/{0}/GameUserSettings.ini", serveros))
pattern="ActiveMods[ \t]*=[ \t]*(?<ActiveMods>[0-9, \t]*)"
filecontents = File.ReadAllText(inifile)
match = Regex.Match(filecontents, pattern, RegexOptions.IgnoreCase)
if match.Success :
activemods = match.Groups["ActiveMods"].Value
if String.IsNullOrEmpty(activemods) or activemods.IndexOf(FileId.ToString()) == -1 :
if activemods.Length > 0 :
activemods = activemods + ","
activemods = activemods + FileId.ToString()
filecontents=filecontents.Replace(match.Groups["ActiveMods"].Value, activemods)
else :
activemods = FileId.ToString()
filecontents = filecontents.Substring(0, match.Groups["ActiveMods"].Index) + activemods + filecontents.Substring(match.Groups["ActiveMods"].Index)
File.WriteAllText(inifile, filecontents)
#Create .mod
parse_base_info(FileId.ToString())
parse_meta_data(FileId.ToString())
create_mod_file(FileId.ToString())
# Delete folder
if Directory.Exists(InstallPath) :
Directory.Delete(InstallPath, True)