Repository navigation
Expand file tree
/
Copy pathfunction_replace.py
More file actions
633 lines (491 loc) · 24.2 KB
/
Copy pathfunction_replace.py
File metadata and controls
633 lines (491 loc) · 24.2 KB
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
"""
This replaces text for all files in the given folder.
Run the find_replace() function, with the globpath parameter as well as the function parameter.
e.g. find_replace(globpath, replace_ssf_press_left_joystick)
To set the folder, update `globpath`. You can either use a path relative to this file, or an absolute path.
The function parameter should take a given file's contents, make the replacement, then return the new contents.
This function will be applied recursively on all child files.
The examples given below use re.sub to do the replacement.
re.sub() has 3 parameters:
1: the regex pattern that finds the selected strings.
2: the function that takes in matches (as per the regex pattern) and returns the desired replacement string
3: the old content
"""
import glob
import fileinput
import re
import tempfile
from pathlib import Path
import os
# look through each file, line by line (for very large files that you can't load into RAM)
# write the changes to a temp file, then replace the original file
def find_replace_line_by_line(top_dir, replace_content):
for filepath in glob.iglob(top_dir, recursive=True):
path = Path(filepath)
if not path.is_file():
continue
# Use delete=False so the file persists after close()
# Use 'dir=path.parent' to force the temp file onto the SAME DRIVE as the original. This prevents the "different disk drive" error.
with tempfile.NamedTemporaryFile('w', encoding='utf-8', dir=path.parent, delete=False, newline='') as tf:
temp_name = tf.name
with open(path, 'r', encoding='utf-8', newline='') as f:
for line in f:
new_line = replace_content(line) # if no replacement, new_line is the same as line
tf.write(new_line)
# if new_line != line:
# print(line)
# print(new_line)
# 2. FILE IS NOW CLOSED (Exit 'with' block)
# On Windows, you MUST close the file before os.replace can access it
# Replace the original file with the new one atomically
try:
# if os.path.exists(temp_name):
# os.remove(temp_name)
os.replace(temp_name, path)
except Exception as e:
# 3. Clean up if the replacement fails
if os.path.exists(temp_name):
os.remove(temp_name)
print(f"Error updating {path}: {e}")
raise
def replace_pbf_left_joystick(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_move_left_joystick_old("
function_name = r"pbf_move_left_joystick_old\("
# 2. Group 1: Captures first 3 arguments: (.*?, \d+, \d+)
group_1 = r"(.*?,\s*\d+,\s*\d+)"
# 3. Group 2, 3: Captures last 2 numeric durations: (\d+), (\d+)
group_2 = r"(\d+)"
group_3 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r"\);"
def replace_joystick_ticks_with_milliseconds(match):
# e.g. pbf_move_left_joystick_old(context, 128, 0, 10, 10);
# match.group(1) is "context, 128, 0"
# match.group(2) is "10" (first duration)
# match.group(3) is "10" (second duration)
prefix = match.group(1)
dur1 = int(match.group(2)) * 8
dur2 = int(match.group(3)) * 8
return f"pbf_move_left_joystick_old({prefix}, {dur1}ms, {dur2}ms);"
new_content: str = re.sub(pattern, replace_joystick_ticks_with_milliseconds, old_content)
return new_content
def replace_pbf_left_joystick2(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_move_left_joystick_old("
function_name = r"pbf_move_left_joystick_old1\("
# 2. Group 1: Captures first 3 arguments: (.*?, .*?, .*?)
group_1 = r"(.*?,\s*.*?,\s*.*?)"
# 3. Group 2, 3: Captures last 2 numeric durations: (\d+), (\d+)
group_2 = r"(\d+)"
group_3 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r"\);"
def replace_joystick_ticks_with_milliseconds(match):
# e.g. pbf_move_left_joystick_old(context, 128, 0, 10, 10);
# match.group(1) is "context, 128, 0"
# match.group(2) is "10" (first duration)
# match.group(3) is "10" (second duration)
prefix = match.group(1)
dur1 = int(match.group(2)) * 8
dur2 = int(match.group(3)) * 8
return f"pbf_move_left_joystick_old({prefix}, {dur1}ms, {dur2}ms);"
new_content: str = re.sub(pattern, replace_joystick_ticks_with_milliseconds, old_content)
return new_content
def replace_pbf_left_joystick3(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_move_left_joystick_old("
function_name = r"pbf_move_left_joystick_old1\("
# 2. Group 1: Captures first 4 arguments: (.*?, .*?, .*?, .*?)
group_1 = r"(.*?,\s*.*?,\s*.*?,\s*.*?)"
# 3. Group 2, 3: Captures last numeric duration: (\d+)
group_2 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r"\);"
def replace_joystick_ticks_with_milliseconds(match):
# e.g. pbf_move_left_joystick_old(context, 128, 0, 10, 10);
# match.group(1) is "context, 128, 0"
# match.group(2) is "10" (first duration)
# match.group(3) is "10" (second duration)
prefix = match.group(1)
dur1 = int(match.group(2)) * 8
return f"pbf_move_left_joystick_old1({prefix}, {dur1}ms);"
new_content: str = re.sub(pattern, replace_joystick_ticks_with_milliseconds, old_content)
return new_content
def replace_mash_button(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_mash_button_old("
function_name = r"pbf_mash_button_old\("
# 2. Group 1: Captures first 2 arguments: (.*?, .*?)
group_1 = r"(.*?,\s*.*?)"
# 3. Group 2: Captures last numeric duration: (\d+)
group_2 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r"\);"
def replace_mash_button_ticks_with_milliseconds(match):
# e.g. pbf_mash_button_old(context, BUTTON_A, 10);
# match.group(1) is "context, BUTTON_A"
# match.group(2) is "10" (first duration)
prefix = match.group(1)
dur1 = int(match.group(2)) * 8
return f"pbf_mash_button({prefix}, {dur1}ms);"
new_content: str = re.sub(pattern, replace_mash_button_ticks_with_milliseconds, old_content)
return new_content
def replace_pbf_wait(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_wait_old("
function_name = r"pbf_wait_old\("
# 2. Group 1: Captures first argument: (.*?)
group_1 = r"(.*?)"
# 3. Group 2: Captures last numeric duration: (\d+)
group_2 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r"\);"
def replace_pbf_wait_ticks_with_milliseconds(match):
# e.g. pbf_wait_old(context, BUTTON_A, 10);
# groups are defined as per the regex pattern above
# match.group(1) is "context, BUTTON_A"
# match.group(2) is "10" (first duration)
prefix = match.group(1)
dur1 = int(match.group(2)) * 8
return f"pbf_wait({prefix}, {dur1}ms);"
new_content: str = re.sub(pattern, replace_pbf_wait_ticks_with_milliseconds, old_content)
return new_content
def replace_pbf_button(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_press_button_old("
function_name = r"pbf_press_button_old\("
# 2. Group 1: Captures first 2 arguments: (.*?, .*?)
group_1 = r"(.*?,\s*.*?)"
# 3. Group 2, 3: Captures last 2 numeric durations: (\d+), (\d+)
group_2 = r"(\d+)"
group_3 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r"\);"
def replace_button_ticks_with_milliseconds(match):
# e.g. pbf_press_button_old(context, BUTTON_A, 10, 20);
# match.group(1) is "context, BUTTON_A,"
# match.group(2) is "10" (first duration)
# match.group(3) is "20" (second duration)
prefix = match.group(1)
dur1 = int(match.group(2)) * 8
dur2 = int(match.group(3)) * 8
return f"pbf_press_button({prefix}, {dur1}ms, {dur2}ms);"
new_content: str = re.sub(pattern, replace_button_ticks_with_milliseconds, old_content)
return new_content
def replace_pbf_dpad(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_press_button_old("
function_name = r"pbf_press_dpad_old\("
# 2. Group 1: Captures first 2 arguments: (.*?, .*?)
group_1 = r"(.*?,\s*.*?)"
# 3. Group 2, 3: Captures last 2 numeric durations: (\d+), (\d+)
group_2 = r"(\d+)"
group_3 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r"\);"
def replace_dpad_ticks_with_milliseconds(match):
# e.g. pbf_press_dpad_old(context, DPAD_UP, 10, 20);
# match.group(1) is "context, BUTTON_A,"
# match.group(2) is "10" (first duration)
# match.group(3) is "20" (second duration)
prefix = match.group(1)
dur1 = int(match.group(2)) * 8
dur2 = int(match.group(3)) * 8
return f"pbf_press_dpad({prefix}, {dur1}ms, {dur2}ms);"
new_content: str = re.sub(pattern, replace_dpad_ticks_with_milliseconds, old_content)
return new_content
def replace_pbf_left_joystick4(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_move_left_joystick_old("
function_name = r"pbf_move_left_joystick_old\("
# 2. Group 1: Captures first argument: (.*?)
group_1 = r"(.*?)"
# 3. Group 2, 3: Captures next 2 numeric x, y: (\d+), (\d+)
group_2 = r"(\d+)"
group_3 = r"(\d+)"
# 4. Group 4 captures hold, delay duration: (\d+ms, \d+ms)
group_4 = r"(.*?,\s*.*?)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r",\s*" + group_4 + r"\);"
def replace_joystick_u8_to_float(match):
# e.g. pbf_move_left_joystick_old(context, 128, 0, 160ms, 160ms);
# match.group(1) is "context"
# match.group(2) is 128 (x)
# match.group(3) is 0 (y)
# match.group(4) is 160ms, 160ms
# convert to: pbf_move_left_joystick(context, {0, +1}, 160ms, 160ms);
prefix = match.group(1)
x_float = linear_u8_to_float(int(match.group(2)))
y_float = -linear_u8_to_float(int(match.group(3)))
x_string = linear_float_to_string(x_float, 3)
y_string = linear_float_to_string(y_float, 3)
group_4 = match.group(4)
return f"pbf_move_left_joystick({prefix}, {{{x_string}, {y_string}}}, {group_4});"
new_content: str = re.sub(pattern, replace_joystick_u8_to_float, old_content)
return new_content
def replace_pbf_left_joystick_flying_trial(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_move_left_joystick_old("
function_name = r"pbf_move_left_joystick_old\("
# 2. Group 1: Captures first argument: (.*?)
group_1 = r"(.*?)"
# 3. Group 2, 3: Captures next 2 numeric x, y: (\d+), (\d+)
group_2 = r"(\d+)"
group_3 = r"(-?\d+)"
# 4. Group 4 captures hold, delay duration: (\d+ms, \d+ms)
group_4 = r"(.*?,\s*.*?)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*get_final_y_axis\(\s*" + group_3 + r"\),\s*" + group_4 + r"\);"
def replace_joystick_u8_to_float(match):
# e.g. pbf_move_left_joystick_old(context, 128, get_final_y_axis( 0), 160ms, 160ms);
# match.group(1) is "context"
# match.group(2) is 128 (x)
# match.group(3) is 0 (y)
# match.group(4) is 160ms, 160ms
# convert to: pbf_move_left_joystick(context, {0, +1}, 160ms, 160ms);
prefix = match.group(1)
x_float = linear_u8_to_float(int(match.group(2)))
y_magnitude = -float(match.group(3))/128.0
x_string = linear_float_to_string(x_float, 3)
y_string = linear_float_to_string(y_magnitude, 3)
group_4 = match.group(4)
return f"pbf_move_left_joystick({prefix}, {{{x_string}, get_final_y_axis({y_string})}}, {group_4});"
new_content: str = re.sub(pattern, replace_joystick_u8_to_float, old_content)
return new_content
def replace_move_cursor_struct(old_content: str) -> str:
# Regex pattern:
# 1. Matches "pbf_move_left_joystick_old("
group_0 = r"\{ZoomChange::"
# 2. Group 1: Captures ZoomChange type: (.*?). e.g. KEEP_ZOOM
group_1 = r"(.*?)"
# 3. Group 2, 3: Captures next 2 numeric x, y: e.g. 0, 10
group_2 = r"(\d+)"
group_3 = r"(\d+)"
# 4. Group 4 captures move duration: e.g. 100ms
group_4 = r"(\d+ms)"
pattern = group_0 + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r",\s*" + group_4 + r"\}"
def replace_joystick_u8_to_float(match):
# e.g. {ZoomChange::KEEP_ZOOM, 0, 10, 100ms}
# match.group(1) is "KEEP_ZOOM"
# match.group(2) is 0 (x)
# match.group(3) is 10 (y)
# match.group(4) is 100ms
zoom_change = match.group(1)
x_float = linear_u8_to_float(int(match.group(2)))
y_float = -linear_u8_to_float(int(match.group(3)))
x_string = linear_float_to_string(x_float, 3)
y_string = linear_float_to_string(y_float, 3)
group_4 = match.group(4)
return f"{{ZoomChange::{zoom_change}, {x_string}, {y_string}, {group_4}}}"
new_content: str = re.sub(pattern, replace_joystick_u8_to_float, old_content)
return new_content
def replace_walk_forward_until_dialog(old_content: str) -> str:
# Regex pattern:
# 1. Matches "walk_forward_until_dialog("
function_name = r"walk_forward_until_dialog\("
# 2. Group 1: Captures first 5 arguments: e.g. env.program_info(), env.console, context, NavigationMovementMode::DIRECTIONAL_ONLY, 60000ms,
group_1 = r"(.*?,\s*.*?,\s*.*?,\s*.*?,\s*.*?)"
# 3. Group 2, 3: Captures last 2 numeric durations: e.g. 0, 128
group_2 = r"(\d+)"
group_3 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r"\);"
def replace_joystick_u8_to_float(match):
# e.g. walk_forward_until_dialog(env.program_info(), env.console, context, NavigationMovementMode::DIRECTIONAL_ONLY, 60000ms, 0, 128);
# groups are defined as per the regex pattern above
# match.group(1) is "context, BUTTON_A"
# match.group(2) is "10" (first duration)
prefix = match.group(1)
x_float = linear_u8_to_float(int(match.group(2)))
y_float = -linear_u8_to_float(int(match.group(3)))
x_string = linear_float_to_string(x_float, 3)
y_string = linear_float_to_string(y_float, 3)
return f"walk_forward_until_dialog({prefix}, {x_string}, {y_string});"
new_content: str = re.sub(pattern, replace_joystick_u8_to_float, old_content)
return new_content
def replace_realign_player(old_content: str) -> str:
# Regex pattern:
# 1. Matches "realign_player("
function_name = r"realign_player\("
# 2. Group 1: Captures first 4 arguments: e.g. env.program_info(), env.console, context, PlayerRealignMode::REALIGN_NEW_MARKER,
group_1 = r"(.*?,\s*.*?,\s*.*?,\s*.*?)"
# 3. Group 2, 3: Captures x, y: e.g. 0, 128
group_2 = r"(\d+)"
group_3 = r"(\d+)"
# 4. Group 4: Captures duration: e.g. 80 ticks
group_4 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r",\s*" + group_4 + r"\);"
def replace_joystick_u8_to_float(match):
# e.g. realign_player(env.program_info(), env.console, context, PlayerRealignMode::REALIGN_NEW_MARKER, 128, 0, 80);
# groups are defined as per the regex pattern above
# match.group(1) is "env.program_info(), env.console, context, PlayerRealignMode::REALIGN_NEW_MARKER"
# match.group(2) is "128" (first duration)
# match.group(3) is "0" (first duration)
# match.group(4) is "80" (first duration)
prefix = match.group(1)
x_float = linear_u8_to_float(int(match.group(2)))
y_float = -linear_u8_to_float(int(match.group(3)))
duration = int(match.group(4)) * 8
x_string = linear_float_to_string(x_float, 3)
y_string = linear_float_to_string(y_float, 3)
return f"realign_player({prefix}, {x_string}, {y_string}, {duration}ms);"
new_content: str = re.sub(pattern, replace_joystick_u8_to_float, old_content)
return new_content
def replace_map_move_cursor_fly(old_content: str) -> str:
# Regex pattern:
# 1. Matches "map_move_cursor_fly("
function_name = r"map_move_cursor_fly\("
# 2. Group 1: Captures first 3 arguments: e.g. info, stream, context
group_1 = r"(.*?,\s*.*?,\s*.*?)"
# 3. Group 2, 3: Captures x, y: e.g. 0, 128
group_2 = r"(\d+)"
group_3 = r"(\d+)"
# 4. Group 4: Captures rest of arguments: e.g. 1840ms, 160ms, "Polar Rest Area"
group_4 = r"(.*?)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r",\s*" + group_4 + r"\);"
def replace_joystick_u8_to_float(match):
# e.g. map_move_cursor_fly(info, stream, context, 75, 0, 1840ms, 160ms, "Polar Rest Area");
# groups are defined as per the regex pattern above
prefix = match.group(1)
x_float = linear_u8_to_float(int(match.group(2)))
y_float = -linear_u8_to_float(int(match.group(3)))
suffix = match.group(4)
x_string = linear_float_to_string(x_float, 3)
y_string = linear_float_to_string(y_float, 3)
return f"map_move_cursor_fly({prefix}, {x_string}, {y_string}, {suffix});"
new_content: str = re.sub(pattern, replace_joystick_u8_to_float, old_content)
return new_content
def replace_overworld_navigation(old_content: str) -> str:
# Regex pattern:
# 1. Matches "overworld_navigation("
function_name = r"overworld_navigation\("
# 2. Group 1: Captures first 5 arguments: e.g. env.program_info(), env.console, context, NavigationStopCondition::STOP_DIALOG, NavigationMovementMode::DIRECTIONAL_ONLY
group_1 = r"(.*?,\s*.*?,\s*.*?,\s*.*?,\s*.*?,\s*)"
# 3. Group 2, 3: Captures x, y: e.g. 0, 128
group_2 = r"(\d+)"
group_3 = r"(\d+)"
# 4. Group 4: Captures rest of arguments: e.g. 20, 20, true, true
group_4 = r"(.*?)"
pattern = function_name + group_1 + group_2 + r",\s*" + group_3 + r",\s*" + group_4 + r"\);"
def replace_joystick_u8_to_float(match):
# e.g. overworld_navigation(env.program_info(), env.console, context, NavigationStopCondition::STOP_DIALOG, NavigationMovementMode::DIRECTIONAL_ONLY, 128, 0, 20, 20, true, true);
# groups are defined as per the regex pattern above
prefix = match.group(1)
x_float = linear_u8_to_float(int(match.group(2)))
y_float = -linear_u8_to_float(int(match.group(3)))
suffix = match.group(4)
x_string = linear_float_to_string(x_float, 3)
y_string = linear_float_to_string(y_float, 3)
return f"overworld_navigation({prefix}{x_string}, {y_string}, {suffix});"
new_content: str = re.sub(pattern, replace_joystick_u8_to_float, old_content)
return new_content
def replace_ssf_press_left_joystick(old_content: str) -> str:
# Regex pattern:
# 1. Matches "overworld_navigation("
function_name = r"ssf_press_left_joystick_old\("
# 2. Group 1: Captures first argument: e.g. context, STICK_MAX, STICK_MAX
group_1 = r"(.*?,\s*.*?,\s*.*?)"
# 3. Group 2, 3: Captures last 2 numeric durations: e.g. 0, 128
group_2 = r"(\d+)"
group_3 = r"(\d+)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r"\);"
def replace_ticks_to_milliseconds(match):
# e.g. ssf_press_left_joystick_old(context, STICK_MAX, STICK_MAX, 400ms, 400ms);
# groups are defined as per the regex pattern above
prefix = match.group(1)
dur1 = int(match.group(2)) * 8
dur2 = int(match.group(3)) * 8
return f"ssf_press_left_joystick_old({prefix}, {dur1}ms, {dur2}ms);"
new_content: str = re.sub(pattern, replace_ticks_to_milliseconds, old_content)
return new_content
def replace_ssf_press_left_joystick2(old_content: str) -> str:
# Regex pattern:
# 1. Matches "overworld_navigation("
function_name = r"ssf_press_left_joystick_old\("
# 2. Group 1: Captures first argument: e.g. context
group_1 = r"(.*?)"
# 3. Group 2, 3: Captures x, y: e.g. STICK_MAX, STICK_MAX
group_2 = r"(.*?)"
group_3 = r"(.*?)"
# 4. Group 4: Captures rest of arguments: e.g. 400ms, 400ms
group_4 = r"(.*?)"
pattern = function_name + group_1 + r",\s*" + group_2 + r",\s*" + group_3 + r",\s*" + group_4 + r"\);"
def replace_joystick_u8_to_float(match):
# e.g. ssf_press_left_joystick_old(context, STICK_MAX, STICK_MAX, 400ms, 400ms);
# groups are defined as per the regex pattern above
prefix = match.group(1)
x = match.group(2)
y = match.group(3)
# print(match)
x_string = ""
y_string = ""
try:
x_float = -10
if x == "STICK_MAX":
x_float = 1
elif x == "STICK_CENTER":
x_float = 0
elif x == "STICK_MIN":
x_float = -1
else:
x_float = linear_u8_to_float(int(x))
x_string = linear_float_to_string(x_float, 3)
except ValueError:
x_string = x
try:
y_float = -10
if y == "STICK_MIN":
y_float = 1
elif y == "STICK_CENTER":
y_float = 0
elif y == "STICK_MAX":
y_float = -1
else:
y_float = -linear_u8_to_float(int(y))
y_string = linear_float_to_string(y_float, 3)
except ValueError:
y_string = y
suffix = match.group(4)
return f"ssf_press_left_joystick({prefix}, {{{x_string}, {y_string}}}, {suffix});"
new_content: str = re.sub(pattern, replace_joystick_u8_to_float, old_content)
return new_content
def linear_float_to_string(x: float, decimal_places: int) -> str:
if (x == 0):
return "0"
if (x == 1):
return "+1"
if (x == -1):
return "-1"
elif (x > 0):
return "+" + str(round(x, decimal_places))
else: #(x < 0):
return str(round(x, decimal_places))
def linear_u8_to_float(x: int) -> float:
if (x == 128):
return 0
elif (x == 64):
return -0.5
elif (x == 192):
return 0.5
elif (x < 128):
return (x - 128) * (1. / 128)
else: # x > 128
return (x - 128) * (1. / 127)
# top_dir: top level directory, within which it will be recursively searched for .cpp files
# replace_content: the function that returns the new file content that will replace the old
def find_replace(top_dir, replace_content):
for filepath in glob.iglob(top_dir, recursive=True):
path = Path(filepath)
if not path.is_file():
continue
# 1. Read the file
# we need newline='' so that line endings are preserved
with open(filepath, 'r', encoding='utf-8', newline='') as f:
content = f.read()
# 2. Perform the global substitution
updated_content = replace_content(content)
if (updated_content == content): # don't overwrite the file if the file content is the same
continue
# 3. Overwrite the file
with open(filepath, 'w', encoding='utf-8', newline='') as f:
f.write(updated_content)
# globpath = '../Pokemon-Arduino-Source/SerialPrograms/Source/**/*.cpp'
globpath = '../Pokemon-Arduino-Source/SerialPrograms/Source/**/*.*'
# find_replace(globpath, replace_pbf_left_joystick)
# find_replace_line_by_line(globpath, replace_pbf_left_joystick)
# find_replace(globpath, replace_mash_button)
# find_replace(globpath, replace_pbf_wait)
# find_replace(globpath, replace_pbf_button)
# find_replace(globpath, replace_pbf_dpad)
find_replace(globpath, replace_ssf_press_left_joystick2)