<filename>AFT Shader Patcher/aft_shader_patcher.py
import sys
from os import chdir, makedirs, get_terminal_size
from os.path import join as joinpath, splitext, isfile, exists, dirname, abspath
from pydiva import pyfarc
import importlib, importlib.util

if getattr(sys, 'frozen', False):
    datadir = dirname(sys.executable)
else:
    datadir = dirname(__file__)

arbpatcher_dir = joinpath(datadir, 'ARB Patcher')
arbpatcher_gamesettings_dir = joinpath(arbpatcher_dir, 'gamesettings')

sys.path = [arbpatcher_dir] + sys.path
import main as ArbPatcher
sys.path = sys.path[1:]

game_settings_spec = importlib.util.spec_from_file_location('divaaft', joinpath(arbpatcher_gamesettings_dir, 'divaaft.py'))
game_settings_module = importlib.util.module_from_spec(game_settings_spec)
game_settings_spec.loader.exec_module(game_settings_module)


# not bothering to check for main here because it's just a glue script anyway
def get_args():    
    import argparse
    parser = argparse.ArgumentParser(description='AFT Shader Patcher: Patches Nvidia-only ARB shaders from PDAFT to work on AMD. Patching techniques from Nezarn; implementation by somewhatlurker.')
    parser.add_argument('-i', '--in_farc', default='shader.farc', help='input shader farc file (default: "shader.farc")')
    parser.add_argument('-o', '--out_farc', default='shader_patched.farc', help='output shader farc file (default: "shader_patched.farc")')
    parser.add_argument('-c', '--compress', action='store_true', help='force use of farc compression (this mey produce larger vcdiff files)')
    parser.add_argument('-x', '--xdelta', action='store_true', help='generate a vcdiff patch file')
    
    return parser.parse_args()

args = get_args()

print("AFT Shader Patcher")
print("==================")
print("Patching techniques from Nezarn; implementation by somewhatlurker")
print("=================================================================")
print("Input file: '{}'".format(args.in_farc))
print("Output file: '{}'".format(args.out_farc))

if not exists(args.in_farc):
    print ("'{}' does not exist. Aborting".format(args.in_farc))
    sys.exit()
elif not isfile(args.in_farc):
    print ("'{}' is not a file. Aborting".format(args.in_farc))
    sys.exit()

if exists(args.out_farc):
    if not isfile(args.out_farc):
        print ("'{}' already exists but is a directory. Aborting".format(args.out_farc))
        sys.exit()


with open(args.in_farc, 'rb') as f:
    farcdata = pyfarc.from_stream(f)
    if args.xdelta:
        f.seek(0)
        from binascii import crc32
        in_farc_crc_str = '{:08x}'.format(crc32(f.read()))

proc_count = 0
    
last_status_len = 0

for fname in farcdata['files']:
    proc_count += 1
    progress_val = proc_count / len(farcdata['files'])
    progress_cnt_X = int(progress_val * 20)
    
    status_str = '\r[{e:{s1}<{n1}}{e:{s2}<{n2}}]'.format(e='', s1='X', s2='-', n1=progress_cnt_X, n2=20-progress_cnt_X)
    status_str += ' {:.2%}'.format(progress_val)
    status_str += '   ' + fname
    
    try:
        terminal_width = get_terminal_size()[0]
    except:
        terminal_width = 120
    
    if len(status_str) > terminal_width:
        status_str = status_str[:terminal_width-3] + '...'
    
    # fix characters left on screen from a previous longer line
    # (without cursor staying off to the side)
    this_status_len = len(status_str)
    if this_status_len < last_status_len:
        status_str = '{: <{l}}'.format(status_str, l=last_status_len)
    last_status_len = this_status_len
    
    print (status_str, end='')
    
    if game_settings_module and game_settings_module.filename_filter:
        openname = game_settings_module.filename_filter(fname)
    else:
        openname = fname
    
    f_lines = farcdata['files'][openname]['data'].decode('utf-8').splitlines(keepends=True)
    
    f_full = ArbPatcher.patch_shader(fname, f_lines, game_settings_module, True)
    
    farcdata['files'][fname]['data'] = f_full.encode('utf-8')

print('')

if args.compress:
    farcdata['farc_type'] = 'FArC'

with open(args.out_farc, 'wb') as f:
    pyfarc.to_stream(farcdata, f, no_copy=True)

if args.xdelta:
    import subprocess
    
    # make paths absolute before running this (actually not needed)
    #args.in_farc = abspath(args.in_farc)
    #args.out_farc = abspath(args.out_farc)
    
    subprocess.run([joinpath(datadir, 'xdelta3.exe'), '-e', '-f', '-S', 'none', '-s', args.in_farc, args.out_farc, joinpath(dirname(args.out_farc), in_farc_crc_str + '.vcdiff')], )
    print ("Generated '{}.vcdiff'".format(in_farc_crc_str))

<filename>AFT Shader Patcher/aft_shader_patcher.py
import sys
from os import chdir, makedirs, get_terminal_size
from os.path import join as joinpath, splitext, isfile, exists, dirname, abspath
from pydiva import pyfarc
import importlib, importlib.util

if getattr(sys, 'frozen', False):
    datadir = dirname(sys.executable)
else:
    datadir = dirname(__file__)

arbpatcher_dir = joinpath(datadir, 'ARB Patcher')
arbpatcher_gamesettings_dir = joinpath(arbpatcher_dir, 'gamesettings')

sys.path = [arbpatcher_dir] + sys.path
import main as ArbPatcher
sys.path = sys.path[1:]

game_settings_spec = importlib.util.spec_from_file_location('divaaft', joinpath(arbpatcher_gamesettings_dir, 'divaaft.py'))
game_settings_module = importlib.util.module_from_spec(game_settings_spec)
game_settings_spec.loader.exec_module(game_settings_module)


# not bothering to check for main here because it's just a glue script anyway
def get_args():    
    import argparse
    parser = argparse.ArgumentParser(description='AFT Shader Patcher: Patches Nvidia-only ARB shaders from PDAFT to work on AMD. Patching techniques from Nezarn; implementation by somewhatlurker.')
    parser.add_argument('-i', '--in_farc', default='shader.farc', help='input shader farc file (default: "shader.farc")')
    parser.add_argument('-o', '--out_farc', default='shader_patched.farc', help='output shader farc file (default: "shader_patched.farc")')
    parser.add_argument('-c', '--compress', action='store_true', help='force use of farc compression (this mey produce larger vcdiff files)')
    parser.add_argument('-x', '--xdelta', action='store_true', help='generate a vcdiff patch file')
    
    return parser.parse_args()

args = get_args()

print("AFT Shader Patcher")
print("==================")
print("Patching techniques from Nezarn; implementation by somewhatlurker")
print("=================================================================")
print("Input file: '{}'".format(args.in_farc))
print("Output file: '{}'".format(args.out_farc))

if not exists(args.in_farc):
    print ("'{}' does not exist. Aborting".format(args.in_farc))
    sys.exit()
elif not isfile(args.in_farc):
    print ("'{}' is not a file. Aborting".format(args.in_farc))
    sys.exit()

if exists(args.out_farc):
    if not isfile(args.out_farc):
        print ("'{}' already exists but is a directory. Aborting".format(args.out_farc))
        sys.exit()


with open(args.in_farc, 'rb') as f:
    farcdata = pyfarc.from_stream(f)
    if args.xdelta:
        f.seek(0)
        from binascii import crc32
        in_farc_crc_str = '{:08x}'.format(crc32(f.read()))

proc_count = 0
    
last_status_len = 0

for fname in farcdata['files']:
    proc_count += 1
    progress_val = proc_count / len(farcdata['files'])
    progress_cnt_X = int(progress_val * 20)
    
    status_str = '\r[{e:{s1}<{n1}}{e:{s2}<{n2}}]'.format(e='', s1='X', s2='-', n1=progress_cnt_X, n2=20-progress_cnt_X)
    status_str += ' {:.2%}'.format(progress_val)
    status_str += '   ' + fname
    
    try:
        terminal_width = get_terminal_size()[0]
    except:
        terminal_width = 120
    
    if len(status_str) > terminal_width:
        status_str = status_str[:terminal_width-3] + '...'
    
    # fix characters left on screen from a previous longer line
    # (without cursor staying off to the side)
    this_status_len = len(status_str)
    if this_status_len < last_status_len:
        status_str = '{: <{l}}'.format(status_str, l=last_status_len)
    last_status_len = this_status_len
    
    print (status_str, end='')
    
    if game_settings_module and game_settings_module.filename_filter:
        openname = game_settings_module.filename_filter(fname)
    else:
        openname = fname
    
    f_lines = farcdata['files'][openname]['data'].decode('utf-8').splitlines(keepends=True)
    
    f_full = ArbPatcher.patch_shader(fname, f_lines, game_settings_module, True)
    
    farcdata['files'][fname]['data'] = f_full.encode('utf-8')

print('')

if args.compress:
    farcdata['farc_type'] = 'FArC'

with open(args.out_farc, 'wb') as f:
    pyfarc.to_stream(farcdata, f, no_copy=True)

if args.xdelta:
    import subprocess
    
    # make paths absolute before running this (actually not needed)
    #args.in_farc = abspath(args.in_farc)
    #args.out_farc = abspath(args.out_farc)
    
    subprocess.run([joinpath(datadir, 'xdelta3.exe'), '-e', '-f', '-S', 'none', '-s', args.in_farc, args.out_farc, joinpath(dirname(args.out_farc), in_farc_crc_str + '.vcdiff')], )
    print ("Generated '{}.vcdiff'".format(in_farc_crc_str))

# not bothering to check for main here because it's just a glue script anyway # fix characters left on screen from a previous longer line # (without cursor staying off to the side) # make paths absolute before running this (actually not needed) #args.in_farc = abspath(args.in_farc) #args.out_farc = abspath(args.out_farc)