Difference between revisions of "Ark Workshop Scripts for Linux"

Line 1: Line 1:
 +
* Updated 2020/7/7. Fix for file ids > 2147483647
 
* Updated 2020/6/9. Some Linux mods are placed in /downloads/ instead of /content/
 
* Updated 2020/6/9. Some Linux mods are placed in /downloads/ instead of /content/
  
Line 4: Line 5:
  
 
<source lang="python">import clr
 
<source lang="python">import clr
 
+
 
from System.IO import Directory, File, Path, SearchOption
 
from System.IO import Directory, File, Path, SearchOption
 
from System import Environment, PlatformID, String, Exception
 
from System import Environment, PlatformID, String, Exception
 
from System.Text.RegularExpressions import Regex, RegexOptions, Match
 
from System.Text.RegularExpressions import Regex, RegexOptions, Match
 
+
 
extractedcount=0
 
extractedcount=0
 
totalfilecount=0
 
totalfilecount=0
 
lastfileprogress=0
 
lastfileprogress=0
 
+
 
########################################
 
########################################
 
# https://github.com/TheCherry/ark-server-manager #
 
# https://github.com/TheCherry/ark-server-manager #
Line 19: Line 20:
 
import zlib
 
import zlib
 
import sys
 
import sys
 
+
 
def str_to_l(st):
 
def str_to_l(st):
 
     return struct.unpack('q', st)[0]
 
     return struct.unpack('q', st)[0]
 
+
 
def z_unpack(src, dst):
 
def z_unpack(src, dst):
 
     global extractedcount, totalfilecount, lastfileprogress
 
     global extractedcount, totalfilecount, lastfileprogress
Line 51: Line 52:
 
       lastfileprogress=progress
 
       lastfileprogress=progress
 
       ThisTaskStep.UpdateProgress(progress)                 
 
       ThisTaskStep.UpdateProgress(progress)                 
 
+
 
#######################################################################
 
#######################################################################
 
# https://github.com/barrycarey/Ark_Mod_Downloader/blob/master/Ark_Mod_Downloader.py #
 
# https://github.com/barrycarey/Ark_Mod_Downloader/blob/master/Ark_Mod_Downloader.py #
Line 62: Line 63:
 
temp_mod_path = os.path.join(ThisService.RootDirectory, "ShooterGame/Content/Mods")
 
temp_mod_path = os.path.join(ThisService.RootDirectory, "ShooterGame/Content/Mods")
 
meta_data = OrderedDict([])
 
meta_data = OrderedDict([])
 
+
 
def parse_base_info(modid):
 
def parse_base_info(modid):
 
+
 
         Script.WriteToConsole("[+] Collecting Mod Details From mod.info")
 
         Script.WriteToConsole("[+] Collecting Mod Details From mod.info")
 
+
 
         mod_info = os.path.join(temp_mod_path, modid, "mod.info")
 
         mod_info = os.path.join(temp_mod_path, modid, "mod.info")
 
+
 
         if not os.path.isfile(mod_info):
 
         if not os.path.isfile(mod_info):
 
             Script.WriteToConsole("[x] Failed to locate mod.info. Cannot Continue.  Aborting")
 
             Script.WriteToConsole("[x] Failed to locate mod.info. Cannot Continue.  Aborting")
 
             return False
 
             return False
 
+
 
         with open(mod_info, "rb") as f:
 
         with open(mod_info, "rb") as f:
 
             read_ue4_string(f)
 
             read_ue4_string(f)
 
             map_count = struct.unpack('i', f.read(4))[0]
 
             map_count = struct.unpack('i', f.read(4))[0]
 
+
 
             for i in range(map_count):
 
             for i in range(map_count):
 
                 cur_map = read_ue4_string(f)
 
                 cur_map = read_ue4_string(f)
 
                 if cur_map:
 
                 if cur_map:
 
                     map_names.append(cur_map)
 
                     map_names.append(cur_map)
 
+
 
         return True
 
         return True
 
+
 
def parse_meta_data(modid):
 
def parse_meta_data(modid):
 
         """
 
         """
        Parse the modmeta.info files and extract the key value pairs need to for the .mod file.
+
      Parse the modmeta.info files and extract the key value pairs need to for the .mod file.
        How To Parse modmeta.info:
+
      How To Parse modmeta.info:
            1. Read 4 bytes to tell how many key value pairs are in the file
+
          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
+
          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
+
          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
+
          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
+
          5. Read ahead by the number of bytes retrieved from step 4
            6. Start at step 2 again
+
          6. Start at step 2 again
        :return: Dict
+
      :return: Dict
        """
+
      """
 
+
 
         print("[+] Collecting Mod Meta Data From modmeta.info")
 
         print("[+] Collecting Mod Meta Data From modmeta.info")
 
         print("[+] Located The Following Meta Data:")
 
         print("[+] Located The Following Meta Data:")
 
+
 
         mod_meta = os.path.join(temp_mod_path, modid, "modmeta.info")
 
         mod_meta = os.path.join(temp_mod_path, modid, "modmeta.info")
 
         if not os.path.isfile(mod_meta):
 
         if not os.path.isfile(mod_meta):
 
             Script.WriteToConsole("[x] Failed To Locate modmeta.info. Cannot continue without it.  Aborting")
 
             Script.WriteToConsole("[x] Failed To Locate modmeta.info. Cannot continue without it.  Aborting")
 
             return False
 
             return False
 
+
 
         with open(mod_meta, "rb") as f:
 
         with open(mod_meta, "rb") as f:
 
+
 
             total_pairs = struct.unpack('i', f.read(4))[0]
 
             total_pairs = struct.unpack('i', f.read(4))[0]
 
+
 
             for i in range(total_pairs):
 
             for i in range(total_pairs):
 
+
 
                 key, value = "", ""
 
                 key, value = "", ""
 
+
 
                 key_bytes = struct.unpack('i', f.read(4))[0]
 
                 key_bytes = struct.unpack('i', f.read(4))[0]
 
                 key_flag = False
 
                 key_flag = False
Line 118: Line 119:
 
                     key_flag = True
 
                     key_flag = True
 
                     key_bytes -= 1
 
                     key_bytes -= 1
 
+
 
                 if not key_flag and key_bytes > 0:
 
                 if not key_flag and key_bytes > 0:
 
+
 
                     raw = f.read(key_bytes)
 
                     raw = f.read(key_bytes)
 
                     key = raw[:-1].decode()
 
                     key = raw[:-1].decode()
 
+
 
                 value_bytes = struct.unpack('i', f.read(4))[0]
 
                 value_bytes = struct.unpack('i', f.read(4))[0]
 
                 value_flag = False
 
                 value_flag = False
Line 129: Line 130:
 
                     value_flag = True
 
                     value_flag = True
 
                     value_bytes -= 1
 
                     value_bytes -= 1
 
+
 
                 if not value_flag and value_bytes > 0:
 
                 if not value_flag and value_bytes > 0:
 
                     raw = f.read(value_bytes)
 
                     raw = f.read(value_bytes)
 
                     value = raw[:-1].decode()
 
                     value = raw[:-1].decode()
 
+
 
                 # TODO This is a potential issue if there is a key but no value
 
                 # TODO This is a potential issue if there is a key but no value
 
                 if key and value:
 
                 if key and value:
 
                     Script.WriteToConsole("[!] " + key + ":" + value)
 
                     Script.WriteToConsole("[!] " + key + ":" + value)
 
                     meta_data[key] = value
 
                     meta_data[key] = value
 
+
 
         return True
 
         return True
 
+
 
def create_mod_file(modid):
 
def create_mod_file(modid):
 
         """
 
         """
        Create the .mod file.
+
      Create the .mod file.
        This code is an adaptation of the code from Ark Server Launcher.  All credit goes to Face Wound on Steam
+
      This code is an adaptation of the code from Ark Server Launcher.  All credit goes to Face Wound on Steam
        :return:
+
      :return:
        """
+
      """
 
         if not parse_base_info(modid) or not parse_meta_data(modid):
 
         if not parse_base_info(modid) or not parse_meta_data(modid):
 
             return False
 
             return False
 
+
 
         print("[+] Writing .mod File")
 
         print("[+] Writing .mod File")
 
         with open(os.path.join(temp_mod_path, modid + ".mod"), "w+b") as f:
 
         with open(os.path.join(temp_mod_path, modid + ".mod"), "w+b") as f:
 
+
 
             modid = int(modid)
 
             modid = int(modid)
 +
            if modid > 2147483647:
 +
              diff = modid-2147483647
 +
              modid = -2147483647 + diff - 2
 
             f.write(struct.pack('ixxxx', modid))  # Needs 4 pad bits
 
             f.write(struct.pack('ixxxx', modid))  # Needs 4 pad bits
 
             write_ue4_string("ModName", f)
 
             write_ue4_string("ModName", f)
 
             write_ue4_string("", f)
 
             write_ue4_string("", f)
 
+
 
             map_count = len(map_names)
 
             map_count = len(map_names)
 
             f.write(struct.pack("i", map_count))
 
             f.write(struct.pack("i", map_count))
 
+
 
             for m in map_names:
 
             for m in map_names:
 
                 write_ue4_string(m, f)
 
                 write_ue4_string(m, f)
 
+
 
             # Not sure of the reason for this
 
             # Not sure of the reason for this
 
             num2 = 4280483635
 
             num2 = 4280483635
Line 169: Line 173:
 
             num3 = 2
 
             num3 = 2
 
             f.write(struct.pack('i', num3))
 
             f.write(struct.pack('i', num3))
 
+
 
             if "ModType" in meta_data:
 
             if "ModType" in meta_data:
 
                 mod_type = b'1'
 
                 mod_type = b'1'
 
             else:
 
             else:
 
                 mod_type = b'0'
 
                 mod_type = b'0'
 
+
 
             # TODO The packing on this char might need to be changed
 
             # TODO The packing on this char might need to be changed
 
             f.write(struct.pack('p', mod_type))
 
             f.write(struct.pack('p', mod_type))
 
             meta_length = len(meta_data)
 
             meta_length = len(meta_data)
 
             f.write(struct.pack('i', meta_length))
 
             f.write(struct.pack('i', meta_length))
 
+
 
             for k, v in meta_data.items():
 
             for k, v in meta_data.items():
 
                 write_ue4_string(k, f)
 
                 write_ue4_string(k, f)
 
                 write_ue4_string(v, f)
 
                 write_ue4_string(v, f)
 
+
 
         return True
 
         return True
 
+
 
def read_ue4_string(file):
 
def read_ue4_string(file):
 
         count = struct.unpack('i', file.read(4))[0]
 
         count = struct.unpack('i', file.read(4))[0]
Line 192: Line 196:
 
             flag = True
 
             flag = True
 
             count -= 1
 
             count -= 1
 
+
 
         if flag or count <= 0:
 
         if flag or count <= 0:
 
             return ""
 
             return ""
 
+
 
         return file.read(count)[:-1].decode()
 
         return file.read(count)[:-1].decode()
 
+
 
def write_ue4_string(string_to_write, file):
 
def write_ue4_string(string_to_write, file):
 
         string_length = len(string_to_write) + 1
 
         string_length = len(string_to_write) + 1
Line 204: Line 208:
 
         file.write(barray)
 
         file.write(barray)
 
         file.write(struct.pack('p', b'0'))
 
         file.write(struct.pack('p', b'0'))
 
+
 
###########################################
 
###########################################
 
###########################################
 
###########################################
Line 211: Line 215:
 
if not Directory.Exists(InstallPath) :
 
if not Directory.Exists(InstallPath) :
 
   InstallPath=InstallPath.Replace("/content/", "/downloads/")
 
   InstallPath=InstallPath.Replace("/content/", "/downloads/")
 
+
 
# Always use Windows files. Linux files cause the game server to crash at startup.
 
# Always use Windows files. Linux files cause the game server to crash at startup.
 
oseditor="WindowsNoEditor"
 
oseditor="WindowsNoEditor"
 
noeditor=Path.Combine(InstallPath, oseditor )
 
noeditor=Path.Combine(InstallPath, oseditor )
 
+
 
# Extract and delete all .z files
 
# Extract and delete all .z files
 
zfiles=Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories);
 
zfiles=Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories);
Line 225: Line 229:
 
   File.Delete(zfile)
 
   File.Delete(zfile)
 
   File.Delete(zfile + ".uncompressed_size")
 
   File.Delete(zfile + ".uncompressed_size")
 
+
 
# Move folder to correct location. Delete if it already exists.
 
# Move folder to correct location. Delete if it already exists.
 +
# Define modid before FileId is altered so we write the correct id to inifile
 +
modid = FileId
 +
if FileId > 2147483647:
 +
  diff = FileId-2147483647
 +
  FileId = -2147483647 + diff - 2
 +
 
modfolder=Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Content/Mods/{0}", FileId))
 
modfolder=Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Content/Mods/{0}", FileId))
 
if Directory.Exists(modfolder) :
 
if Directory.Exists(modfolder) :
 
   Directory.Delete(modfolder, True)
 
   Directory.Delete(modfolder, True)
 
Directory.Move(Path.Combine(InstallPath, oseditor), modfolder)
 
Directory.Move(Path.Combine(InstallPath, oseditor), modfolder)
 
+
 
# Update ini file
 
# Update ini file
 
serveros = "WindowsServer" if Environment.OSVersion.Platform == PlatformID.Win32NT else "LinuxServer"
 
serveros = "WindowsServer" if Environment.OSVersion.Platform == PlatformID.Win32NT else "LinuxServer"
Line 240: Line 250:
 
if match.Success :
 
if match.Success :
 
   activemods = match.Groups["ActiveMods"].Value
 
   activemods = match.Groups["ActiveMods"].Value
   if String.IsNullOrEmpty(activemods) or activemods.IndexOf(FileId.ToString()) == -1 :
+
   if String.IsNullOrEmpty(activemods) or activemods.IndexOf(modid.ToString()) == -1 :
 
     if activemods.Length > 0 :
 
     if activemods.Length > 0 :
 
       activemods = activemods + ","
 
       activemods = activemods + ","
       activemods = activemods + FileId.ToString()
+
       activemods = activemods + modid.ToString()
 
       filecontents=filecontents.Replace(match.Groups["ActiveMods"].Value, activemods)
 
       filecontents=filecontents.Replace(match.Groups["ActiveMods"].Value, activemods)
 
     else :
 
     else :
       activemods = FileId.ToString()
+
       activemods = modid.ToString()
 
       filecontents = filecontents.Substring(0, match.Groups["ActiveMods"].Index) + activemods + filecontents.Substring(match.Groups["ActiveMods"].Index)
 
       filecontents = filecontents.Substring(0, match.Groups["ActiveMods"].Index) + activemods + filecontents.Substring(match.Groups["ActiveMods"].Index)
 
     File.WriteAllText(inifile, filecontents)
 
     File.WriteAllText(inifile, filecontents)
 
+
 
#Create .mod
 
#Create .mod
 
parse_base_info(FileId.ToString())
 
parse_base_info(FileId.ToString())
 
parse_meta_data(FileId.ToString())
 
parse_meta_data(FileId.ToString())
 
create_mod_file(FileId.ToString())
 
create_mod_file(FileId.ToString())
 
+
 
# Delete folder
 
# Delete folder
 
if Directory.Exists(InstallPath) :
 
if Directory.Exists(InstallPath) :
 
   Directory.Delete(InstallPath, True)</source>
 
   Directory.Delete(InstallPath, True)</source>

Revision as of 16:31, 7 July 2020

  • Updated 2020/7/7. Fix for file ids > 2147483647
  • Updated 2020/6/9. Some Linux mods are placed in /downloads/ instead of /content/


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
 
extractedcount=0
totalfilecount=0
lastfileprogress=0
 
########################################
# 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):
    global extractedcount, totalfilecount, lastfileprogress
    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)
    Script.WriteToConsole("Extracted " + dst.Replace(ThisService.RootDirectory, ""))
    File.Delete(src)
    File.Delete(src + ".uncompressed_size")
    extractedcount=extractedcount+1
    progress=round((float(extractedcount)/totalfilecount)*100,0)
    if progress > lastfileprogress + 4:
      lastfileprogress=progress
      ThisTaskStep.UpdateProgress(progress)                
 
#######################################################################
# 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)
            if modid > 2147483647:
              diff = modid-2147483647
              modid = -2147483647 + diff - 2
            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'))
 
###########################################
###########################################
###########################################
# If the content folder doesn't exist use downloads
if not Directory.Exists(InstallPath) :
  InstallPath=InstallPath.Replace("/content/", "/downloads/")
 
# Always use Windows files. Linux files cause the game server to crash at startup.
oseditor="WindowsNoEditor"
noeditor=Path.Combine(InstallPath, oseditor )
 
# Extract and delete all .z files
zfiles=Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories);
totalfilecount=zfiles.Count
for zfile in zfiles:
  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.
# Define modid before FileId is altered so we write the correct id to inifile
modid = FileId
if FileId > 2147483647:
  diff = FileId-2147483647
  FileId = -2147483647 + diff - 2
 
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(modid.ToString()) == -1 :
    if activemods.Length > 0 :
      activemods = activemods + ","
      activemods = activemods + modid.ToString()
      filecontents=filecontents.Replace(match.Groups["ActiveMods"].Value, activemods)
    else :
      activemods = modid.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)
Retrieved from "https://help.tcadmin.com/index.php?title=Ark_Workshop_Scripts_for_Linux&oldid=2227"