From 30e91e860dd25e5f67ca222e5d59aab82b13ad66 Mon Sep 17 00:00:00 2001 From: Elliot Nunn Date: Fri, 10 Jan 2020 20:20:17 +0800 Subject: [PATCH] Add wildcard arguments to rfx --- bin/rfx | 76 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/bin/rfx b/bin/rfx index 0d48e8a..c4a3864 100755 --- a/bin/rfx +++ b/bin/rfx @@ -3,6 +3,7 @@ import macresources import sys import tempfile +import os from os import path import re import subprocess @@ -11,16 +12,17 @@ import textwrap if len(sys.argv) < 2 or sys.argv[1].startswith('-'): sys.exit(textwrap.dedent(''' - usage: rfx command [arg[//type/id] ...] + usage: rfx command [arg | arg// | arg//type | arg//type/id ...] Shell command wrapper for accessing resources inside a Rez textfile Resources specified as filename.rdump//type/id are converted to tempfiles before - the command is run, and back to resources after the command returns. + the command is run, and back to resources after the command returns. Truncated + // arguments are wildcards. examples: rfx mv Doc.rdump//STR/0 Doc.rdump//STR/1 - rfx cp App.rdump//PICT/2000 2000.pict + rfx cp App.rdump//PICT allpictures/ rfx rm System.rdump//vers/2 ''').strip()) @@ -109,28 +111,66 @@ def rez_delete_resource(the_path, the_type, the_id): del the_file[start:stop] +def escape_ostype(ostype): + escaped = '' + for char in ostype: + if ord('A') <= char <= ord('Z') or ord('a') <= char <= ord('z'): + escaped += chr(char) + else: + escaped += '_%02X' % char + return escaped + + with tempfile.TemporaryDirectory() as backup_tmp_dir: new_argv = [sys.argv[1]] to_retrieve = [] for i, arg in enumerate(sys.argv[2:], 1): - m = re.match(r'(.*[^/])//([^/]{1,4})/(-?\d+)$'.replace('/', re.escape(path.sep)), arg) + m = re.match(r'(.*[^/])//(?:([^/]{1,4})(?:/(-?\d+)?)?)?$'.replace('/', re.escape(path.sep)), arg) - if m: - res_spec = (m.group(1), m.group(2).encode('mac_roman').ljust(4)[:4], int(m.group(3))) - tmp_file = path.join(backup_tmp_dir, str(i)) - - to_retrieve.append((tmp_file, res_spec)) - - res_data = rez_get_resource(*res_spec) - if res_data is not None: - with open(tmp_file, 'wb') as f: - f.write(res_data) - - new_argv.append(tmp_file) - - else: + if not m: + # Do not expand this argument new_argv.append(arg) + else: + # Expand arg into 1+ fake-resource tempfiles. This is a (filename, type, id) list. + res_specs = [] + + res_file = m.group(1) + res_type = m.group(2).encode('mac_roman').ljust(4)[:4] if m.group(2) else None + res_id = int(m.group(3)) if m.group(3) else None + + if res_type is None: + # File// = every resource + for found_res in macresources.parse_rez_code(get_cached_file(res_file)): + res_specs.append((res_file, found_res.type, found_res.id)) + elif res_id is None: + # File//Type/ = resources of type (can omit trailing slash) + for found_res in macresources.parse_rez_code(get_cached_file(res_file)): + if found_res.type == res_type: + res_specs.append((res_file, res_type, found_res.id)) + else: + # File//Type/ID = 1 resource + res_specs.append((res_file, res_type, res_id)) + + if not res_specs: + # Failed to expand so leave unchanged + new_argv.append(arg) + else: + # Expand! + tmp_subdir = path.join(backup_tmp_dir, str(i)) + os.mkdir(tmp_subdir) + for res_spec in res_specs: + res_file, res_type, res_id = res_spec + tmp_file = path.join(tmp_subdir, '%s.%d' % (escape_ostype(res_type), res_id)) + + to_retrieve.append((tmp_file, res_spec)) + + res_data = rez_get_resource(*res_spec) + if res_data is not None: + with open(tmp_file, 'wb') as f: + f.write(res_data) + + new_argv.append(tmp_file) result = subprocess.run(new_argv)