yonilerner
7/10/2016 - 2:25 PM

apply_mods.py

import argparse
import shutil
import os

always_on = set()
mods = {
    'positive_tk': [{ # We change the TK points to +2, and change tank enemy tank kill to -2
        'file': 'ingame.lvl',
        'offset': 0x000000b9,
        'orig': [0x00, 0xc0],
        'mod': [0x40, 0x40]
    }, {
        'file': 'ingame.lvl',
        'offset': 0x000000be,
        'orig': [0x40, 0x40],
        'mod': [0x00, 0xc0]
    }, {
        'file': 'ingame.lvl',
        'offset': 0x0000013b,
        'orig': [0x89],
        'mod': [0x09]
    }, {
        'file': 'ingame.lvl',
        'offset': 0x00000143,
        'orig': [0xc9],
        'mod': [0x89]
    }, {
        'file': 'ingame.lvl',
        'offset': 0x0000014b,
        'orig': [0x09],
        'mod': [0xc9]
    }],
    'droid_invincible': [{
        'file': 'ingame.lvl',
        'offset': 0x002e7614, # Health droid
        'orig': [
            0x50, 0x52, 0x4F, 0x50, 0x08, 0x00, 0x00, 0x00, # These bytes correspond to the MaxHealth=200 part of the ODF
            0x1B, 0x1F, 0x97, 0x19, 0x32, 0x30, 0x30, 0x00
        ],
        'mod': [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # 0ing them gives it no health, and therefore makes it invincible
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ]
    }, {
        'file': 'ingame.lvl',
        'offset': 0x002a9644, # Ammo droid built
        'orig': [
            0x50, 0x52, 0x4F, 0x50, 0x07, 0x00, 0x00, 0x00, # These bytes correspond to the MaxHealth=0 part of the ODF
            0x1B, 0x1F, 0x97, 0x19, 0x38, 0x30, 0x00, 0x00
        ],
        'mod': [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # 0ing them gives it no health, and therefore makes it invincible
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ]
    }],
    'droid_fast_heal': [{
        'file': 'ingame.lvl',
        'offset': 0x002e7630, # speed
        'orig': '1.0',
        'mod':  '0.1'
    }, {
        'file': 'ingame.lvl',
        'offset': 0x002e7640, # heal power
        'orig': '25.000',
        'mod':  '1000.0'
    }],
    'no_chewie': [{
        'file': 'SIDE\\all.lvl',
        'offset': 0x0011CABC8,
        'orig': '24',
        'mod':  '00'
    }],
    'no_gunners': [{
        'file': 'SIDE\\all.lvl',
        'offset': 0x0017D3DD8, # han 1
        'orig': '15',
        'mod': '00'
    }, {
        'file': 'SIDE\\all.lvl',
        'offset': 0x0029784C8, # han 2
        'orig': '15',
        'mod': '00'
    }, {
        'file': 'SIDE\\all.lvl',
        'offset': 0x0019D9EA0, # lei
        'orig': '12',
        'mod': '00'
    }, {
        'file': 'SIDE\\imp.lvl',
        'offset': 0x002B3FAC, # boba
        'orig': '12',
        'mod': '00'
    }, {
        'file': 'SIDE\\cis.lvl',
        'offset': 0x007994A8, # jango
        'orig': '12',
        'mod': '00'
    }],
    'no_force': [{
        'file': 'SIDE\\cis.lvl',
        'offset': 0x0052471C, # choke
        'orig': '1',
        'mod': '0'
    }, {
        'file': 'SIDE\\imp.lvl',
        'offset': 0x003012E0, # choke 1
        'orig': '1',
        'mod': '0'
    }, {
        'file': 'SIDE\\imp.lvl',
        'offset': 0x003A07E4, # choke 2
        'orig': '1',
        'mod': '0'
    }, {
        'file': 'SIDE\\imp.lvl',
        'offset': 0x003A00E4, # lightning ord 1
        'orig': '1',
        'mod': '0'
    }, {
        'file': 'SIDE\\cis.lvl',
        'offset': 0x0052401C, # lightning ord 2
        'orig': '1',
        'mod': '0'
    }, {
        'file': 'SIDE\\rep.lvl',
        'offset': 0x00581CF4, # pull ord 1
        'orig': '1',
        'mod': '0'
    }, {
        'file': 'SIDE\\rep.lvl',
        'offset': 0x0072A1D8, # pull ord 2
        'orig': '1',
        'mod': '0'
    }, {
        'file': 'SIDE\\rep.lvl',
        'offset': 0x00958FF4, # pull ord 3
        'orig': '1',
        'mod': '0'
    }, {
        'file': 'SIDE\\rep.lvl',
        'offset': 0x007B75B0, # push ord 1
        'orig': '4',
        'mod': '0'
    }, {
        'file': 'SIDE\\rep.lvl',
        'offset': 0x00843518, # push ord 2
        'orig': '4',
        'mod': '0'
    }, {
        'file': 'SIDE\\rep.lvl',
        'offset': 0x008CAC88, # push ord 3
        'orig': '4',
        'mod': '0'
    }, {
        'file': 'SIDE\\rep.lvl',
        'offset': 0x00959548, # push ord 4
        'orig': '4',
        'mod': '0'
    }, {
        'file': 'SIDE\\cis.lvl',
        'offset': 0x005B8128, # push ord 5
        'orig': '4',
        'mod': '0'
    }, {
        'file': 'SIDE\\all.lvl',
        'offset': 0x001BF2D88, # push ord 6
        'orig': '4',
        'mod': '0'
    }, {
        'file': 'SIDE\\all.lvl',
        'offset': 0x002020570, # push ord 7
        'orig': '4',
        'mod': '0'
    }],
    'no_border': [{
        'file': 'TAT\\tat2.lvl',
        'orig': 'TAT\\tat2.lvl.orig',
        'mod': 'TAT\\tat2.lvl.mod'
    }],
    'enemy_tk': [{
        'file': 'mission.lvl',
        'offset': 0x0000C2BB,
        'orig': [0x00],
        'mod': [0x80]
    }]
}


def write_bytes_to_file_at_offset(val, file, offset, encoding='ascii'):
    with open(file, 'r+b') as f:
        f.seek(offset)
        if type(val) == str:
            f.write(bytes(val, encoding))
        else:
            f.write(bytes(val))


def apply_mods(revert=False, mods_to_apply=set(mods.keys())):
    if type(mods_to_apply) != set:
        mods_to_apply = set(mods_to_apply)
    for mod_name in mods:
        if mod_name not in mods_to_apply or ((mod_name in always_on) and revert):
            continue

        mod_arr = mods[mod_name]
        for mod in mod_arr:
            if revert and mod_name:
                val = mod['orig']
            else:
                val = mod['mod']
            if mod.get('offset'):
                write_bytes_to_file_at_offset(
                    val, mod['file'], mod['offset']
                )
            else:
                os.remove(mod['file'])
                shutil.copy(val, mod['file'])
    return mods_to_apply

parser = argparse.ArgumentParser(description='Apply SWBF2 server mods.')
parser.add_argument(
    'mods_to_apply', metavar='mod_name', type=str, nargs='*',
    help='The mods you want to enable/disable. Defaults to all.', default=set(mods.keys())
)
parser.add_argument('--revert', help='Whether or not we are reverting changes. If not used, we are applying the mods.', action='store_true')
parser.add_argument('--list', help='To just list available mods.', action='store_true')

args = parser.parse_args()

if args.list:
	print(', '.join(list(mods.keys())))
else:
	print(', '.join(apply_mods(revert=args.revert, mods_to_apply=args.mods_to_apply)))