-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathueexport.py
260 lines (201 loc) · 8.61 KB
/
ueexport.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
import argparse
import sys
from contextlib import contextmanager
from pathlib import Path
from pprint import pprint
from typing import Any, Dict, Optional, Tuple, cast
from ark.discovery import initialise_hierarchy
from ark.types import PrimalItem
from automate.ark import ArkSteamManager
from automate.jsonutils import _format_json
from config import ConfigFile, get_global_config, switch_config
from export.wiki.stage_items import get_item_name
from ue.asset import ExportTableItem, UAsset
from ue.gathering import gather_properties
from ue.hierarchy import inherits_from
from ue.loader import AssetNotFound
from ue.paths import find_asset_from_external_path
from ue.proxy import UEProxyStructure
from ue.utils import sanitise_output
EPILOG = '''example: python ueexport.py /Game/PrimalEarth/Dinos/Dodo/Dodo_Character_BP'''
DESCRIPTION = '''Convert assets as JSON.'''
args: argparse.Namespace
def create_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser("uegrep", description=DESCRIPTION, epilog=EPILOG)
parser.add_argument('--config', type=str, default='config/config.ini', help='config file to use')
parser.add_argument('--dir', nargs=1, help='output to a directory')
parser.add_argument('--output', '-o', nargs=1, help='output to a specific file')
parser.add_argument('--all', '-a', action='store_true', help='include all exports')
parser.add_argument('--default', '-d', action='store_true', help='include only the default export')
parser.add_argument('--ovi', action='store_true', help='create a 2D item icon render template for Oviraptor')
parser.add_argument('assetname', metavar='ASSETNAME', type=str, help="asset to convert")
parser.add_argument('export',
metavar='EXPORT',
type=str,
nargs='?',
help="select single export by name or index (all if not specified)")
return parser
def find_asset(assetname, loader):
try:
assetname = find_asset_from_external_path(assetname, loader)
except AssetNotFound:
print(f'Not found: {assetname}', file=sys.stderr)
sys.exit(404)
return assetname
def collect_asset(assetname: str, config: ConfigFile) -> UAsset:
config.settings.SkipInstall = True
arkman = ArkSteamManager(config=config)
if args.ovi:
# Hierarchy is needed for proxies in the Oviraptor target.
arkman.ensureSteamCmd()
arkman.ensureGameUpdated()
arkman.ensureModsUpdated(config.mods)
initialise_hierarchy(arkman)
loader = arkman.getLoader()
# config = get_global_config()
assetname = find_asset(args.assetname, loader)
if not assetname:
print("Asset not found")
sys.exit(1)
return loader[assetname]
def create_filename(name: str) -> str:
name = name.strip('/')
if not name.endswith('.json'):
name = name + '.json'
name = name.replace('/', '+')
return name
def find_export(asset: UAsset) -> Optional[ExportTableItem]:
if args.default:
# Produce default export only
if not hasattr(asset, 'default_export'):
print("Asset does not have a default export", file=sys.stderr)
sys.exit(1)
assert asset.default_export and asset.default_export.fullname
return asset.default_export
elif args.export:
# Single export
as_int: Optional[int] = None
try:
as_int = int(args.export)
except ValueError:
pass
if as_int is not None:
# Integer-based export lookup
if as_int < 0 or as_int >= len(asset.exports.values):
print(f"Maximum export index for this asset is {len(asset.exports.values)-1}", file=sys.stderr)
sys.exit(1)
return asset.exports[as_int]
else:
# Name-based export lookup
found_indexes = []
search_name = args.export.lower()
for i in range(len(asset.exports.values)):
export = asset.exports.values[i]
if str(export.name).lower() == search_name:
found_indexes.append(i)
if found_indexes:
print("Export with this name not found", file=sys.stderr)
sys.exit(1)
elif len(found_indexes) > 1:
print("This name was found at multiple indexes:", file=sys.stderr)
pprint(found_indexes, stream=sys.stderr)
sys.exit(1)
return asset.exports.values[found_indexes[0]]
return None
def collect_data(asset: UAsset) -> Tuple[str, Any]:
if args.default and args.export is not None:
print("Cannot specify an export with --default", file=sys.stderr)
sys.exit(1)
if args.ovi:
if not args.export and not args.default:
# Grab the default export since we need a starting point for the proxy.
args.default = True
export = find_export(asset)
assert export
assert export.fullname
if not inherits_from(export, PrimalItem.get_ue_type()):
print(f"Export {export.name} is not a subclass of PrimalItem.", file=sys.stderr)
sys.exit(1)
proxy: UEProxyStructure = gather_properties(export)
item = cast(PrimalItem, proxy)
if 'ItemIconMaterialParent' not in item:
print("Item does not use an icon shader", file=sys.stderr)
sys.exit(1)
name = get_item_name(item) or export.name
data: Dict[str, Any] = dict(
Format='2.0',
Name=name,
BlueprintPath=export.fullname,
)
assert asset.loader
mat_instance = asset.loader.load_related(item.ItemIconMaterialParent[0]).default_export
assert mat_instance
mat_properties = mat_instance.properties.as_dict()
# Convert all parameters from the material instance.
parameters = dict()
if 'ScalarParameterValues' in mat_properties:
for param in mat_properties['ScalarParameterValues'][0].values:
param_info = param.as_dict()
param_name = sanitise_output(param_info['ParameterName'])
parameters[param_name] = param_info['ParameterValue']
if 'VectorParameterValues' in mat_properties:
for param in mat_properties['VectorParameterValues'][0].values:
param_info = param.as_dict()
param_name = sanitise_output(param_info['ParameterName'])
parameters[param_name] = param_info['ParameterValue'].values[0]
if 'TextureParameterValues' in mat_properties:
for param in mat_properties['TextureParameterValues'][0].values:
param_info = param.as_dict()
param_name = sanitise_output(param_info['ParameterName'])
parameters[param_name] = param_info['ParameterValue']
if parameters:
data['2DMaterial'] = dict(Parent=mat_properties['Parent'][0], **parameters)
else:
# Export only the parent material as the instance has no parameters.
data['2DMaterial'] = mat_properties['Parent'][0]
data = sanitise_output(data)
filename = create_filename(export.fullname)
elif args.default or args.export:
export = find_export(asset)
assert export
assert export.fullname
data = sanitise_output(export.properties)
filename = create_filename(export.fullname)
else:
# Full asset extraction
data = sanitise_output(asset)
assert asset.assetname
filename = create_filename(asset.assetname)
return (filename, data)
@contextmanager
def manage_output_file(filename: str):
# Work out where to save it
if args.output:
path = Path(args.output[0])
elif args.dir:
path = Path(args.dir[0], filename)
else:
path = Path('-')
if path == Path('-'):
yield sys.stdout
return
with open(path, 'wt', newline='\n', encoding='utf8') as handle:
yield handle
print("Output saved as: " + str(path))
return
def main():
global args # pylint: disable=global-statement, invalid-name
args = create_parser().parse_args()
if args.dir and args.output:
print("Cannot specify both --dir and --output:", file=sys.stderr)
sys.exit(1)
config = get_global_config()
if args.config:
config = switch_config(args.config)
asset = collect_asset(args.assetname, config)
filename, data = collect_data(asset)
json = _format_json(data, pretty=True)
with manage_output_file(filename) as handle:
handle.write(json)
if __name__ == '__main__':
main()