Skip to content

Instantly share code, notes, and snippets.

@Grayfox96
Last active October 30, 2021 13:03
Show Gist options
  • Save Grayfox96/d00747b4da9aacd7a8346d61ec57bf6c to your computer and use it in GitHub Desktop.
Save Grayfox96/d00747b4da9aacd7a8346d61ec57bf6c to your computer and use it in GitHub Desktop.
"""This script is used to find a seed by inputting the first 3 damage values
from Tidus and the first 5 damage values from Auron. It will look for files
in the ffx_ps2_seeds directory and try to find the seed that corresponds
to those damage values.
"""
import os
import time
import datetime
import array
from typing import List
DIRECTORY = 'ffx_ps2_seeds'
AURON_DAMAGE_VALUES = (
(
260, 261, 262, 263, 264, 266, 267, 268, 269, 270, 271,
272, 273, 274, 275, 276, 278, 279, 280, 281, 282, 283,
284, 285, 286, 287, 288, 289, 291, 292, 293, 294
),
(
520, 522, 524, 526, 528, 532, 534, 536, 538, 540, 542,
544, 546, 548, 550, 552, 556, 558, 560, 562, 564, 566,
568, 570, 572, 574, 576, 578, 582, 584, 586, 588
)
)
TIDUS_DAMAGE_VALUES = (
(
125, 126, 127, 128, 129, 130, 131, 132, 133,
134, 135, 136, 137, 138, 139, 140, 141
),
(
250, 252, 254, 256, 258, 260, 262, 264, 266,
268, 270, 272, 274, 276, 278, 280, 282
)
)
def get_input() -> List[int]:
while True:
input_string = input('Damage values (ATATATAA):')
for symbol in (',', '-', '/', '\\'):
input_string = input_string.replace(symbol, ' ')
damage_values = input_string.split()
try:
damage_values = [int(i) for i in damage_values]
except ValueError as error:
error = str(error).split(':', 1)[1]
print(f'{error} is not a valid damage value.')
continue
damage_values = damage_values[:8]
if len(damage_values) < 8:
print('Need at least 8 damage values.')
continue
damage_values_check = (
('Auron', AURON_DAMAGE_VALUES, damage_values[0]),
('Tidus', TIDUS_DAMAGE_VALUES, damage_values[1]),
('Auron', AURON_DAMAGE_VALUES, damage_values[2]),
('Tidus', TIDUS_DAMAGE_VALUES, damage_values[3]),
('Auron', AURON_DAMAGE_VALUES, damage_values[4]),
('Tidus', TIDUS_DAMAGE_VALUES, damage_values[5]),
('Auron', AURON_DAMAGE_VALUES, damage_values[6]),
('Auron', AURON_DAMAGE_VALUES, damage_values[7]),
)
damage_values_indexes = []
for character, damage_values_list, damage_value in damage_values_check:
try:
damage_values_indexes.append(
damage_values_list[0].index(damage_value))
except ValueError:
try:
damage_values_indexes.append(
damage_values_list[1].index(damage_value))
except ValueError:
print(
f'Invalid damage value for {character}: {damage_value}')
if len(damage_values_indexes) != 8:
continue
print('Damage values:', ', '.join([str(d) for d in damage_values]))
print(damage_values_indexes)
return damage_values_indexes
def convert_to_integer(damage_values_indexes: List[int]) -> int:
damage_values_as_int = 0
for offset, damage_value in enumerate(damage_values_indexes):
damage_values_as_int += damage_value << offset * 8
return damage_values_as_int
def find_seed(damage_values_as_int) -> None:
files_list = sorted(os.listdir(DIRECTORY))
if input('Write anything to search in reverse: '):
files_list = reversed(files_list)
start_time = time.time()
for filename in files_list:
filename = os.path.join(DIRECTORY, filename)
print(datetime.datetime.now(), filename)
seeds_array = array.array('Q')
with open(filename, 'rb') as file:
seeds_array.fromfile(file, 0x100000)
try:
index = seeds_array.index(damage_values_as_int)
except ValueError:
continue
seed_number = int((filename[33:])) * 0x100000 + index
print(f'Seed found: {seed_number}')
break
else:
print('Seed not found')
print('Time elapsed: '
f'{datetime.timedelta(seconds=time.time() - start_time)}')
def main() -> None:
damage_values_indexes = get_input()
damage_values_as_int = convert_to_integer(damage_values_indexes)
find_seed(damage_values_as_int)
if __name__ == '__main__':
main()
input('Press enter to quit...')
"""This script will produce files in the "./ffx_ps2_seeds" directory each
containing the first 3 damage values from Tidus and the first 5 damage values
from Auron corrisponding to a given seed.
"""
import os
import datetime
import array
DIRECTORY = 'ffx_ps2_seeds'
SLICE_LENGTH = 0x100000
TIDUS_DAMAGE_ROLLS = (
125, 126, 126, 127, 127, 128, 128, 129, 129, 130, 130,
131, 131, 132, 132, 133, 134, 134, 135, 135, 136, 136,
137, 137, 138, 138, 139, 139, 140, 140, 141, 141
)
TIDUS_DAMAGE_TO_INDEX = (
125, 126, 127, 128, 129, 130, 131, 132, 133,
134, 135, 136, 137, 138, 139, 140, 141
)
RNG_CONSTANTS_1 = (
2100005341, 1700015771, 247163863, 891644838, 1352476256,
1563244181, 1528068162, 511705468, 1739927914, 398147329,
1278224951, 20980264, 1178761637, 802909981, 1130639188,
1599606659, 952700148, -898770777, -1097979074, -2013480859,
-338768120, -625456464, -2049746478, -550389733, -5384772,
-128808769, -1756029551, 1379661854, 904938180, -1209494558,
-1676357703, -1287910319, 1653802906, 393811311, -824919740,
1837641861, 946029195, 1248183957, -1684075875, -2108396259,
-681826312, 1003979812, 1607786269, -585334321, 1285195346,
1997056081, -106688232, 1881479866, 476193932, 307456100,
1290745818, 162507240, -213809065, -1135977230, -1272305475,
1484222417, -1559875058, 1407627502, 1206176750, -1537348094,
638891383, 581678511, 1164589165, -1436620514, 1412081670,
-1538191350, -284976976, 706005400
)
RNG_CONSTANTS_2 = (
10259, 24563, 11177, 56952, 46197, 49826, 27077, 1257, 44164,
56565, 31009, 46618, 64397, 46089, 58119, 13090, 19496, 47700,
21163, 16247, 574, 18658, 60495, 42058, 40532, 13649, 8049,
25369, 9373, 48949, 23157, 32735, 29605, 44013, 16623, 15090,
43767, 51346, 28485, 39192, 40085, 32893, 41400, 1267, 15436,
33645, 37189, 58137, 16264, 59665, 53663, 11528, 37584, 18427,
59827, 49457, 22922, 24212, 62787, 56241, 55318, 9625, 57622,
7580, 56469, 49208, 41671, 36458
)
def s32(integer):
return ((integer & 0xffffffff) ^ 0x80000000) - 0x80000000
def rng_array_generator(rng_value):
rng_value = s32(rng_value)
starting_values = []
for rng_index in range(23):
rng_value = s32(s32(rng_value * 0x5d588b65) + 0x3c35)
rng_value = s32((rng_value >> 0x10) + (rng_value << 0x10))
starting_values.append(rng_value & 0x7fffffff)
return starting_values
def get_rng_array(starting_value, rng_index, amount):
rng_value = s32(starting_value)
rng_constant_1 = RNG_CONSTANTS_1[rng_index]
rng_constant_2 = RNG_CONSTANTS_2[rng_index]
values = []
for _ in range(amount):
rng_value = s32(rng_value * rng_constant_1 ^ rng_constant_2)
rng_value = s32((rng_value >> 0x10) + (rng_value << 0x10))
values.append(rng_value & 0x7fffffff)
return values
def seed_to_file(slice_):
seeds = array.array('Q')
start = SLICE_LENGTH * slice_
stop = SLICE_LENGTH * (slice_ + 1)
filename = f'{DIRECTORY}/ffx_ps2_seeds_part_{slice_:04}'
print(f'{datetime.datetime.now()}: '
f'Creating {filename} with seeds '
f'0x{start:08x}-0x{stop - 1:08x}')
for seed in range(start, stop):
starting_values = rng_array_generator(seed)
auron_rolls = get_rng_array(starting_values[22], 22, 37)
tidus_rolls = get_rng_array(starting_values[20], 20, 7)
numbers = []
# first encounter
# get 3 damage rolls from auron
# and 3 damage rolls from tidus
for i in range(1, 6, 2):
numbers.append(auron_rolls[i] & 31)
tidus_damage_roll = TIDUS_DAMAGE_ROLLS[tidus_rolls[i] & 31]
numbers.append(TIDUS_DAMAGE_TO_INDEX.index(tidus_damage_roll))
# second encounter after dragon fang
# get 2 damage rolls from auron
for i in range(32, 35, 2):
numbers.append(auron_rolls[i] & 31)
item = 0
for offset, number in enumerate(numbers):
item += number << offset * 8
seeds.append(item)
with open(filename, 'wb') as file:
seeds.tofile(file)
print(f'{datetime.datetime.now()}: Done creating {filename}')
def main():
if not os.path.exists(DIRECTORY):
os.mkdir(DIRECTORY)
print('Created directory:', DIRECTORY)
start = int(input('Start:'))
stop = int(input('Stop:'))
print(f'{datetime.datetime.now()}: Creating {stop - start} files '
f'(from #{start} to #{stop - 1})')
if start > stop:
quit()
for i in range(start, stop):
if i * SLICE_LENGTH > 0xffffffff + 1:
return
seed_to_file(i)
if __name__ == '__main__':
main()
input('Press enter to quit...')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment