├── .gitignore ├── README.md ├── converter.py ├── files.py ├── messages.py ├── run.py └── shell.py /.gitignore: -------------------------------------------------------------------------------- 1 | .temp 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reshade2Basalt 2 | This python3 scripts will convert Reshade .ini presets to vkBasalt .conf files, changing shader settings directly in .fx files. 3 | 4 | # Dependencies 5 | - Python >= 3.4 6 | - vkBasalt 7 | 8 | # Usage 9 | After downloading it, you are ready to start using, with ```python3 run.py [--args] ``` command. 10 | 11 | # Options 12 | 13 | Because method of handle arguments, all arguments must to be passed with python types (True/False, "str", etc). These are the types that script will use: 14 | 15 | 16 | 17 | | Type | Description | Example | 18 | |------|-----------------------------------------------------------|------------------------| 19 | | bool | A True or False argument | --arg=True | 20 | | str | A phrase/word wrapped in quotes | --arg="Str1,Str2..." 21 | | path | A complete path to file or folder, looking at system root | --arg="/home/user/dir" | 22 | 23 | Arguments syntax are the following ```--arg=value```, and can contain various arguments. Bellow, you will find the complete arguments list. 24 | 25 | | Option | Description | Required | Default | Type | 26 | |-----------------------|-----------------------------------------------------------|----------|----------------------------------------------|------| 27 | | --help | Show help menu | No | False | bool | 28 | | --verbose | Show some extra debug messages. Use when reporting issues | No | False | bool | 29 | | --shaders-repo, --fxr | Set a different one reshade-shaders git repository | No | "https://github.com/crosire/reshade-shaders" | str | 30 | | --shaders-branch, --fxb | Shaders repository branch | No | master | str | 31 | | --preset, --p | Path to reshade .ini file. | No | $PWD/preset.ini | path | 32 | | --ignore-shaders, --fxi | Shaders that are preset in preset, but that you want to ignore. Util when vkBasalt can't handle correctly the shader. Pass without spaces, divided by a comma. | No | "" | str | 33 | | --output-dir | Path to save shaders after setting them up. Default is current directory (note that if you want to change shaders folder path, you must to change it on generate vkBasalt.conf too) | No | $PWD/output/ | path | 34 | 35 | # Contributions 36 | All contributions to source are welcome, just create a pull request in this repo and i will verify as soon possible. 37 | 38 | If you can try different presets (for now, only part of reshade default shaders are noticable 100% working in vkBasalt) in looking for issues, you would already help me a lot. Please report any issue with default shaders in Issues section of this repo. 39 | 40 | And if you're a Linux gamer entusiast and want to helpe-me and encourage to do more linux games utilities, buy me a coffe! You can enter in contact to me to receive a payment link to donate how much you want and can to give-me. 41 | 42 | # Project status 43 | 44 | This is a Beta 1.0.0 version, with obviously various bugs to map and fix, and improvements to do. Updates by author will only occur in weekends, so you can enter here each sunday night to check for status 45 | 46 | Below, a list of updates that you can do, or wait for me: 47 | 48 | * Test various presets to map bugs, and register then in issues. 49 | * A better documentation 50 | * A argument that allow user to include custom shaders in .temp dir 51 | * A better argument handler 52 | * Special treatment to shaders that contains a .fxh file included (e.g. SMAA/FXAA, that is current not supported) 53 | * A install script to make it available globally 54 | * A argument that allow user to automatically creates a .sh/.desktop for some game/application already with vkBasalt configuration variables in command. 55 | * A argument that allow user to live test presets (with vkcube) before write shaders/settings in output_dir 56 | * .sh to run script with arguments directly as a shell script 57 | 58 | # Authors 59 | * Emmy Gomes | | +55 (11) 9537-8163. 60 | -------------------------------------------------------------------------------- /converter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | import re 5 | 6 | class Converter: 7 | def __init__(self): 8 | self.pwd = os.getcwd() + "/" 9 | 10 | def get_config_update(self, shader, config, new_val): 11 | # Add a identifier to isolate config from rest of file 12 | config_regex = r"(.+) {}?(.+)".format(config) 13 | config_start = re.sub(config_regex, r'|||||||\1 {}\2'.format(config), shader, count=1) 14 | config_end = "|||||||" + re.sub(r"> =(.+)", r"> =\1|||||||", config_start.split("|||||||")[1], count=1) 15 | 16 | # Get old config and new config string 17 | old_config = config_end.split('|||||||')[1] 18 | new_config = re.sub(r"\> \= (.+)", " > = {};".format(new_val) , old_config, count=1) 19 | 20 | return { 21 | "old": old_config, 22 | "new": new_config 23 | } 24 | 25 | def update_file(self, filename, shader, configs, verbose = False): 26 | if verbose: 27 | print("\nOperation: Converting file {}".format(filename)) 28 | 29 | for config in configs: 30 | if config == '': 31 | continue 32 | 33 | # Get param name and new value 34 | param = config.split("=")[0] 35 | value = config.split("=")[1] 36 | 37 | if verbose: 38 | print("\n--------------------------------------------------------------------------------------------------------------------------------") 39 | print("Param: " + param) 40 | print("Value: {}\n".format(value)) 41 | 42 | # Get and treat setting replaces 43 | replaces = self.get_config_update(shader, param, value) 44 | replaces["new"] = self.treat_update_values_conversion(replaces["old"], replaces["new"]) 45 | 46 | if verbose: 47 | print("Replace: " + replaces["old"] + "\n") 48 | print("With: " + replaces["new"]) 49 | print("--------------------------------------------------------------------------------------------------------------------------------") 50 | 51 | # Replace config in shader 52 | shader = shader.replace(replaces["old"], replaces["new"]) 53 | 54 | return shader 55 | 56 | def treat_update_values_conversion(self, old, new): 57 | # Treatments conditions 58 | bool_cond = old.find("> = false") != -1 or old.find("> = true") 59 | float_cond = old.find("float3(") != -1 or old.find("float2(") != -1 or old.find("float4(") != -1 60 | 61 | # Treats booleans 62 | if bool_cond: 63 | new = new.replace("> = 0;", "> = false;") 64 | new = new.replace("> = 1;", "> = true;") 65 | 66 | # Treats float 67 | if float_cond: 68 | value = new.split("> = ")[1].replace(";", "") 69 | funcType = "" 70 | 71 | # Prevent errors with float type (2, 3 or 4 values) 72 | if old.find("float2(") != -1: 73 | funcType = "2" 74 | elif old.find("float3(") != -1: 75 | funcType = "3" 76 | elif old.find("float4(") != -1: 77 | funcType = "4" 78 | 79 | new = new.replace(value, "float{}({});".format(funcType, value)) 80 | 81 | return new 82 | 83 | converter = Converter() 84 | -------------------------------------------------------------------------------- /files.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from shell import shell 3 | import shutil 4 | import sys 5 | import os 6 | 7 | class Files: 8 | def __init__(self): 9 | self.pwd = os.getcwd() + "/" 10 | self.ini_path = self.pwd + "preset.ini" 11 | self.shaders_path = self.pwd + ".temp/shaders/Shaders/" 12 | 13 | self.replace_shaders_rules = { 14 | "BloomAndLensFlares.fx": "Bloom.fx" 15 | } 16 | 17 | self.exclude_shaders_rules = [ 18 | "Tint.fx", 19 | "" 20 | ] 21 | 22 | 23 | def add_excludes(self, extra_excludes = False): 24 | if extra_excludes != False: 25 | extra_excludes = extra_excludes.split(",") 26 | 27 | for extra in extra_excludes: 28 | if extra.find(".fx") == -1: 29 | extra = extra + ".fx" 30 | 31 | self.exclude_shaders_rules.append(extra) 32 | pass 33 | pass 34 | pass 35 | 36 | def init_temp_dirs(self, use_cache = False): 37 | # Verifies if temp dir exists, and remove it case exists 38 | if os.path.exists("{}.temp".format(self.pwd)): 39 | try: 40 | if use_cache: 41 | shutil.rmtree("{}.temp/output".format(self.pwd)) 42 | else: 43 | shutil.rmtree("{}.temp".format(self.pwd)) 44 | except: 45 | print("Error when trying to delete old temporary dir") 46 | sys.exit() 47 | 48 | # Create temporary directories 49 | try: 50 | if use_cache == False: 51 | os.mkdir(".temp") 52 | os.mkdir(".temp/shaders/") 53 | 54 | os.mkdir(".temp/output/") 55 | os.mkdir(".temp/output/shaders") 56 | except: 57 | print("Error when trying to create temporary directories.") 58 | sys.exit() 59 | 60 | def get_preset_ini(self, path = False): 61 | # Verifies if preset file is a custom one or default 62 | if path == False: 63 | path = self.ini_path 64 | 65 | # Verifies if preset exists 66 | if not os.path.exists(path): 67 | print("Cannot found preset file. Informed path: {}".format(path)) 68 | sys.exit() 69 | 70 | file_contents = open(path, "r").read() 71 | settings = { 72 | "effects": [], 73 | "configurations": {} 74 | } 75 | 76 | # Get used effects 77 | effects = file_contents.split("Techniques=")[1].split("TechniqueSorting=")[0].replace(" ", "").replace("\n", "").split(",") 78 | 79 | # Get effects names 80 | for effect in effects: 81 | if effect.find(".fx") == -1: 82 | effect = effect + ".fx" 83 | 84 | # Treat replace rule for shader name, if was 85 | if effect in self.replace_shaders_rules: 86 | effect = self.replace_shaders_rules[effect] 87 | 88 | # Exclude shader if it is not supported 89 | if effect in self.exclude_shaders_rules: 90 | continue 91 | 92 | settings["effects"].append(effect) 93 | 94 | # Get effects settings 95 | for effect in settings['effects']: 96 | try: 97 | effect_key = "[{}]\n".format(effect) 98 | effect_settings = file_contents.split(effect_key)[1].split("\n\n")[0].split("\n") 99 | 100 | settings["configurations"][effect] = effect_settings 101 | except: 102 | print("Shader {} not found, add it to extra shaders or ignore this file".format(effect)) 103 | sys.exit() 104 | 105 | return settings 106 | 107 | def get_shader_content(self, shader): 108 | # Verifies if shader exists 109 | if not os.path.exists(self.shaders_path + shader): 110 | print("Shader file {} not exists".format(self.shader)) 111 | sys.exit() 112 | 113 | content = open(self.shaders_path + shader, "r").read() 114 | 115 | return content 116 | 117 | def write_new_temp_shader(self, file_content, filename): 118 | try: 119 | new_file = open("{}.temp/output/shaders/{}".format(self.pwd, filename), "w") 120 | new_file.write(file_content) 121 | new_file.close() 122 | except: 123 | print("Error when trying to write new shader file in temp folder. Filename: " + filename) 124 | sys.exit() 125 | 126 | def copy_reshade_core_to_temp(self): 127 | try: 128 | current_path = [ 129 | self.pwd + ".temp/shaders/Shaders/ReShade.fxh", 130 | self.pwd + ".temp/shaders/Shaders/ReShadeUI.fxh" 131 | ] 132 | new_path = [ 133 | self.pwd + ".temp/output/shaders/ReShade.fxh", 134 | self.pwd + ".temp/output/shaders/ReShadeUI.fxh" 135 | ] 136 | 137 | shutil.copyfile(current_path[0], new_path[0]) 138 | shutil.copyfile(current_path[1], new_path[1]) 139 | except: 140 | print("Error when trying to copy Reshade Core file to temporary output folder.") 141 | sys.exit() 142 | 143 | def copy_reshade_textures_to_temp(self): 144 | try: 145 | current_path = self.pwd + ".temp/shaders/Textures/" 146 | new_path = self.pwd + ".temp/output/textures/" 147 | 148 | shutil.copytree(current_path, new_path) 149 | except: 150 | print("Error when trying to copy Reshade Textures to temporary output folder.") 151 | sys.exit() 152 | 153 | def create_conf(self, effects, output_dir = False): 154 | # Verifies if user want to use default output dir 155 | if output_dir == False: 156 | output_dir = self.pwd + "output" 157 | 158 | # Verifies if output dir exists, and han 159 | if os.path.exists(output_dir): 160 | create_dir = input("Output directory already exists. Want to overwrite it? [S/n]: ") 161 | 162 | if create_dir == "n" or create_dir == "N": 163 | print("Exiting the program") 164 | sys.exit() 165 | else: 166 | os.system("cd {} && rm -rf * && rm -rf .*".format(output_dir)) 167 | else: 168 | os.system("mkdir {}".format(output_dir)) 169 | 170 | if output_dir[-1] != "/": 171 | output_dir = output_dir + "/" 172 | 173 | # Variables 174 | textures_path = output_dir + "textures/" 175 | shaders_path = output_dir + "shaders/" 176 | used_effects_str = "effects = " 177 | effects_path_str = "" 178 | 179 | # Loop effects to build file 180 | for effect in effects: 181 | name = effect.lower().replace(".fx", "") 182 | 183 | used_effects_str += "{}:".format(name) 184 | effects_path_str += "{} = {}{}\n ".format(name, shaders_path, effect) 185 | 186 | file_content = """ 187 | reshadeTexturePath = {} 188 | reshadeIncludePath = {} 189 | 190 | {} 191 | 192 | {} 193 | """.format(textures_path, shaders_path, effects_path_str, used_effects_str[:-1]).replace(" ", "") 194 | 195 | # Write config file 196 | conf_file = open(self.pwd + ".temp/output/vkBasalt.conf", "w") 197 | conf_file.write(file_content) 198 | conf_file.close() 199 | 200 | return output_dir 201 | 202 | 203 | files = Files() 204 | -------------------------------------------------------------------------------- /messages.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | version = "v1.0.0" 4 | 5 | help_str = """ 6 | Reshade2Basalt. 7 | 8 | {} 9 | Author: Emmy Gomes 10 | Email: aou-emmy@outlook.com 11 | Issues: https://github.com/eemmy/Reshade2Basalt/issues 12 | 13 | To donate, please contact +55 11 95837-8163 on Whatsapp, or send-me a email. 14 | 15 | 16 | Syntax: python3 run.py [--argument=value] 17 | 18 | Types: 19 | bool True/False (uppercase). 20 | int 0,1,2... 21 | float 0.4,0.5,0.6... 22 | path Absolute path to files or folders 23 | str Phrase or word wrapped in quotes (ex: \"Shaders, Textures, etc...\") 24 | 25 | 26 | Options: 27 | 28 | --help=bool Show this menu instead running script. 29 | --verbose=bool Add a high debug output. Recommended to report issues. 30 | [--shaders-repo, --fxr]=str Git clone string to shaders repository (default is Reshade Shaders in packages section). 31 | [--shaders-branch, --fxb]=str Shaders repository branch (default is master). 32 | [--preset, --p]=path Path to reshade .ini file. If not specified, the script will assume that the file is in the current directory, as preset.ini 33 | [--ignore-shaders, --fxi]=str Inform which preset using shaders you want to ignore. This is util with complex shaders that vkBasalt can't handle correctly. Pass without spaces, divided by a comma. 34 | --use-cached-shaders=bool Inform to script does not re-download shaders from remote git repository if it already exists. 35 | --include-shaders-dir=path Inform a path to folder with custom shaders that you want to include in script job. This shaders will replace remote shaders. 36 | --output-dir=path Path to save shaders after setting them up. Default is current directory (note that if you want to change shaders folder path, you must to change it on generate vkBasalt.conf too) 37 | 38 | 39 | Packages: 40 | 41 | List of packages that work with this script, that the conversion result might be useful, or that inspired or were used to build this script. 42 | 43 | Reshade by Crosire: https://reshade.me, https://github.com/crosire/reshade 44 | reshade-shaders by Crosire: https://github.com/crosire/reshade-shaders 45 | vkBasalt by DadSchoorse: https://github.com/DadSchoorse/vkBasalt 46 | 47 | """.format(version) 48 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from files import files 3 | from converter import converter 4 | from shell import shell 5 | from messages import * 6 | import sys 7 | 8 | # Arguments variations 9 | repo = ["shaders-repo", "fxr"] 10 | branch = ["shaders-branch", "fxb"] 11 | preset = ["preset", "p"] 12 | exclude_shaders = ["ignore-shaders", "fxi"] 13 | verbose = ["verbose"] 14 | include_shaders = ["include-shaders-dir"] 15 | cache = ["use-cached-shaders"] 16 | output_dir = ["output-dir"] 17 | 18 | # Get script arguments 19 | shell.get_args(sys.argv) 20 | 21 | # Verifies if user want's to see help menu 22 | if (shell.get_arg("help")): 23 | print(help_str) 24 | sys.exit() 25 | 26 | # Add exclude rules 27 | files.add_excludes(shell.get_arg(exclude_shaders)) 28 | 29 | # Init temp dirs and download shaders 30 | files.init_temp_dirs(shell.get_arg(cache)) 31 | shell.download_shaders(shell.get_arg(repo), shell.get_arg(branch), shell.get_arg(cache)) 32 | 33 | # Get ini configurations 34 | settings = files.get_preset_ini(shell.get_arg(preset)) 35 | 36 | # Loop settings to convert files 37 | debug = shell.get_arg(verbose) 38 | for shader in settings["configurations"]: 39 | old_shader_file = files.get_shader_content(shader) 40 | new_shader_file = converter.update_file(shader, old_shader_file, settings["configurations"][shader], debug) 41 | 42 | files.write_new_temp_shader(new_shader_file, shader) 43 | 44 | # Get reshade core to shaders path, and copy textures folder to prevent errors 45 | files.copy_reshade_core_to_temp() 46 | files.copy_reshade_textures_to_temp() 47 | 48 | # Create vkBasalt.conf file 49 | output_dir_path = files.create_conf(settings["effects"], shell.get_arg(output_dir)) 50 | 51 | # Move converted files to output dir 52 | shell.move_temp_to_output(output_dir_path) 53 | -------------------------------------------------------------------------------- /shell.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | # Handle shell operations, like shade repositories management. 6 | class Shell: 7 | def __init__(self): 8 | self.shaders_repo = "https://github.com/crosire/reshade-shaders.git" 9 | self.shaders_branch = "master" 10 | self.shaders_package = "crosire/reshade-shaders" 11 | 12 | self.pwd = os.getcwd() + "/" 13 | 14 | self.args = {} 15 | 16 | def download_shaders(self, repo = False, branch = False, use_cached = False): 17 | # Verifies if user want's to use cached shaders 18 | if use_cached: 19 | if os.path.exists(self.pwd + ".temp/shaders/Shaders"): 20 | return 21 | else: 22 | print("Can't find cached shaders. Downloading from remote.\n") 23 | 24 | # Verifies if branch/repo are a custom one or the defaults 25 | if repo == False: 26 | repo = self.shaders_repo 27 | if branch == False: 28 | branch = self.shaders_branch 29 | 30 | # Download shaders repo 31 | try: 32 | os.system("git clone {} {}.temp/shaders/".format(repo, self.pwd)) 33 | os.system("cd {}.temp/shaders && git checkout {}".format(self.pwd, branch)) 34 | except: 35 | print("Error when trying to download git-repository {} from {}:{}".format(self.shaders_package, repo, branch)) 36 | sys.exit() 37 | 38 | def get_args(self, args): 39 | args_dict = {} 40 | args.pop(0) 41 | 42 | # Add arguments to dict 43 | for arg in args: 44 | # Verifies if argument is invalid 45 | if arg.find("--") == -1 or arg.find("=") == -1: 46 | if "invalid" not in args_dict: 47 | args_dict["invalid"] = [ arg ] 48 | else: 49 | args_dict["invalid"].append(arg) 50 | else: 51 | key_val = arg.split("=") 52 | args_dict[key_val[0].replace("--", "")] = key_val[1] 53 | 54 | # Validates arguments 55 | self.validate_args(args_dict) 56 | 57 | self.args = args_dict 58 | 59 | def validate_args(self, args): 60 | if not "invalid" in args: 61 | return True 62 | 63 | for invalid_arg in args["invalid"]: 64 | print("Invalid argument {}".format(invalid_arg)) 65 | 66 | print("\nPlease consult supported arguments and arguments syntax.") 67 | sys.exit() 68 | 69 | def get_arg(self, name): 70 | if isinstance(name, list): 71 | for n in name: 72 | if n in self.args: 73 | return self.args[n] 74 | 75 | return False 76 | else: 77 | if not name in self.args: 78 | return False 79 | else: 80 | return self.args[name] 81 | 82 | def move_temp_to_output(self, path): 83 | try: 84 | os.system("mv {}.temp/output/* {}".format(self.pwd, path)) 85 | except: 86 | print("Error when trying to move temporary output folder to final destination.") 87 | sys.exit() 88 | 89 | shell = Shell() 90 | --------------------------------------------------------------------------------