diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 0000000..0cf5ea5 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "No Configurations" +} \ No newline at end of file diff --git a/.vs/ShinyShadow/v16/.suo b/.vs/ShinyShadow/v16/.suo new file mode 100644 index 0000000..52c0c88 Binary files /dev/null and b/.vs/ShinyShadow/v16/.suo differ diff --git a/.vs/ShinyShadow/v16/Browse.VC.db b/.vs/ShinyShadow/v16/Browse.VC.db new file mode 100644 index 0000000..c2103d3 Binary files /dev/null and b/.vs/ShinyShadow/v16/Browse.VC.db differ diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..8b0d1b7 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,7 @@ +{ + "ExpandedNodes": [ + "" + ], + "SelectedNode": "\\test.cpp", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..7822bc1 Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/execution.py b/execution.py index 3eefb1a..99bd2e5 100644 --- a/execution.py +++ b/execution.py @@ -16,6 +16,8 @@ def __init__(self, start_address, starting_ram={}, starting_registers={}): self.cr0 = None self.cr1 = None self.ctr = None + self.debug = False + self.breakpoints = [] def print_line(self): print(hex(self.current_address), SOURCE[self.current_address]) @@ -74,13 +76,10 @@ def print_registers(self): print("") def execute(self): - DEBUG = False - breakpoints = [] - while not self.end_reached: - if self.current_address in breakpoints: - DEBUG = True + if self.current_address in self.breakpoints: + self.debug = True try: args = SOURCE[self.current_address].replace(",", " ").split() @@ -88,7 +87,7 @@ def execute(self): raise Exception("no source code for address", hex( self.current_address)) - if DEBUG: + if self.debug: self.print_line() input("press Enter to run line") @@ -102,6 +101,6 @@ def execute(self): self.print_ram() raise - if DEBUG: + if self.debug: self.print_registers() self.print_ram() diff --git a/npc.cpp b/npc.cpp new file mode 100644 index 0000000..efd5da4 --- /dev/null +++ b/npc.cpp @@ -0,0 +1,261 @@ +// class for simulating NPC interactions with pRNG + +# include PPC_executor +# include LCG + +class NPC { + LCG lcg; + float destX; + float destY; + float currentX; + float currentY; + float nextX; + float nextY; + float walk_speed; + float wait_time; + bool walking; + bool first_step; + bool debug; + + NPC(LCG lcg, float startX, float startY) { + wait_time = 0.0; + walk_speed = 0.0; + currentX = startX + currentY = startY + self.nextX = None + self.nextY = None + self.state = WAITING + self.first_step = True + self.debug = False + + } + +void step () { + // simulates one frame of NPC action + if (walking) { + walk(); + if (nextX == destX and nextY == destY) { + set_wait_time(); + walking = false; + } + + + } +} + + + def step(self): + """ + simulates one frame of NPC action + """ + if self.state == WALKING: + self.walk() + + + elif self.state == WAITING: + if self.nextX: + self.currentX = self.destX + self.nextX = None + if self.nextY: + self.currentY = self.destY + self.nextX = None + + if self.wait_time <= float32(0.0): + self.set_walk_params() + self.walk() + self.state = WALKING + else: + self.wait() + + def set_wait_time(self): + """ + simulates logic for selecting a pseudo random wait time + """ + prn1 = self.lcg.generate() + prn2 = self.lcg.generate() + rand = (prn1 + prn2) - 1.0 # constant from rtoc + self.wait_time = float32((3.0 * rand) + 5.0) # constants from r31 + + def wait(self): + """ + simulates waiting one frame + """ + val = single_to_double(self.wait_time) + val -= 0.03333333507180214 # 0x3fa1111120000000 + if val >= 0: + self.wait_time = double_to_single(val) + else: + self.wait_time = float32(0.0) + + def set_walk_params(self): + """ + executes logic for selecting a pseudo random walk destination + and walk speed + """ + prn = self.lcg.generate() + + starting_ram = { + 0x809e5458: "41700000", + 0x80270184: "400921fb", + 0x80270188: "4012d97c", + 0x8027018c: "401921fb", + 0x809e544c: "4080000000000000", + 0x809e5454: "41c0000041700000", + 0x8048e610: "00000000", + 0x8048e618: "ffffffff", + + 0x809e53e0: "8050f0c0", + 0x8050f0dc: "00000000", + 0x809e5404: "00000000", + 0x809e5400: "00000020", + 0x8047b1f8: "00000030", + 0x8047b200: "809e5220", + 0x809e5220: "01", + 0x809e5248: "00000000", + 0x809e52fc: "01", + 0x809e5324: "00000000", + 0x809e53d8: "01", + 0x809e53dc: "809e53d8", + 0x809e5224: "809e5220", + 0x809e5300: "809e52fc", + + 0x8050f0e4: "000000000000000000000000", + + 0x80270348: "3f90ad3ae322da11", + 0x80270338: "3fa97b4b24760deb", + 0x80270328: "3fb10d66a0d03d51", + 0x80270340: "bfa2b4442c6a6c2f", + 0x80270330: "bfadde2d52defd9a", + 0x80270318: "3fb745cdc54c206e", + 0x80270320: "bfb3b0f2af749a6d", + 0x80270308: "3fc24924920083ff", + 0x80270310: "bfbc71c6fe231671", + 0x802702f8: "3fd555555555550d", + 0x80270300: "bfc999999998ebc4", + 0x802702f0: "3c9aa62633145c07", + 0x802702d0: "3ff921fb54442d18", + 0x802702d8: "3c7a2b7f222f65e2", + 0x802702b8: "3fddac670561bb4f", + 0x802702e0: "3c81a62633145c07", + 0x802702c0: "3fe921fb54442d18", + 0x802702e8: "3c7007887af0cbbd", + 0x802702c8: "3fef730bd281f69b", + + 0x8050f0d8: single_to_hex_str(self.currentX), + 0x8050f0e0: single_to_hex_str(self.currentY), + } + starting_registers = { + "sp": "8048e5e0", + "r13": "80480820", + "r30": "809e53d8", + "r31": "809e53d8", + "f1": single_to_hex_str(prn), + } + exc = PPC_executor(0x80184e64, starting_ram, starting_registers) + exc.execute() + self.destX = hex_str_to_single( + exc.read_hex_str_from_ram(0x809e5434, 4)) + self.destY = hex_str_to_single( + exc.read_hex_str_from_ram(0x809e543c, 4)) + self.walk_speed = hex_str_to_single( + exc.read_hex_str_from_ram(0x809e5418, 4)) + + def walk(self): + """ + executes logic for walking one frame + """ + starting_registers = { + "lr": "80185afc", + "sp": "8048e5b0", + "r3": "00000020", + "r4": "00000000", + "r13": "80480820", + "r28": "00000001", # 0x809e53d8, # unused? + "r29": "807f1048", # 0x00000002, # unused? + "r30": "809e53d8", + "r31": "809e53d8", + "f1": "3ff0000000000000", + "f30": "ffffffffffffffff", + "p30": "4070000000000000", + "f31": "402d6b5aa0000000", # unused? + "p31": "0000000000000000", + } + starting_ram = { + # constants + 0x8047b1f8: "00000030", + 0x8047b200: "809e5220", + 0x8047aa94: "0000003c", + 0x809e5220: "01", + 0x809e5248: "00000000", + 0x809e52fc: "01", + 0x809e5324: "00000000", + 0x809e53d8: "01", + 0x809e5400: "00000020", + 0x809e5404: "00000000", + 0x809e540c: "3e94a529", + 0x809e53dc: "809e53d8", + 0x809e5224: "809e5220", + 0x809e5300: "809e52fc", + 0x809e53e0: "8050f0c0", + 0x8050f0dc: "00000000", + 0x8050f160: "41f00000", + 0x8031554c: "000000003f800000", + 0x80315554: "0000000000000000", + 0x8047aa80: "e0000000", + + 0x809e5418: single_to_hex_str(self.walk_speed), + + # changes between some steps, but doesn't seem to be used + 0x8050f0c0: "10102027", + } + + # fist step on load changes this out of memory value, very strange + if self.first_step: + starting_ram[0xe0000054] = "00000001" + self.first_step = False + else: + starting_ram[0xe0000054] = "00000002" + + # if this is the first step in the walk, use current vals for calc + if self.nextX is None: + starting_ram[0x8050f0d8] = single_to_hex_str(self.currentX) + else: + starting_ram[0x8050f0d8] = single_to_hex_str(self.nextX) + + if self.nextY is None: + starting_ram[0x8050f0e0] = single_to_hex_str(self.currentY) + else: + starting_ram[0x8050f0e0] = single_to_hex_str(self.nextY) + + exc = PPC_executor(0x80188214, starting_ram, starting_registers) + + if self.debug: + exc.breakpoints = [0x800cc2f0] + + exc.execute() + + calcedX = hex_str_to_single( + exc.read_hex_str_from_ram(0x8048e540, 4)) + calcedY = hex_str_to_single( + exc.read_hex_str_from_ram(0x8048e548, 4)) + + # check if we would pass the destination and if so stop at it + if (self.nextX and ((self.nextX <= self.destX and self.destX <= calcedX) or + (self.nextX >= self.destX and self.destX >= calcedX))): + print("stopping instead of going to ", single_to_hex_str(calcedX), single_to_hex_str(calcedY)) + calcedX = self.destX + + if (self.nextY and ((self.nextY <= self.destY and self.destY <= calcedY) or + (self.nextY >= self.destY and self.destY >= calcedY))): + calcedY = self.destY + + # move to next vals + if self.nextX: + self.currentX = self.nextX + self.nextX = calcedX + if self.nextY: + self.currentY = self.nextY + self.nextY = calcedY + + +}; \ No newline at end of file diff --git a/npc.py b/npc.py index 6e71cfe..654f0dd 100644 --- a/npc.py +++ b/npc.py @@ -21,6 +21,7 @@ def __init__(self, lcg, startingX, startingY): self.nextY = None self.state = WAITING self.first_step = True + self.debug = False def step(self): """ @@ -126,11 +127,11 @@ def set_walk_params(self): 0x8050f0e0: single_to_hex_str(self.currentY), } starting_registers = { - "sp": 0x8048e5e0, - "r13": 0x80480820, - "r30": 0x809e53d8, - "r31": 0x809e53d8, - "f1": prn, + "sp": "8048e5e0", + "r13": "80480820", + "r30": "809e53d8", + "r31": "809e53d8", + "f1": single_to_hex_str(prn), } exc = PPC_executor(0x80184e64, starting_ram, starting_registers) exc.execute() @@ -146,20 +147,20 @@ def walk(self): executes logic for walking one frame """ starting_registers = { - "lr": 0x80185afc, - "sp": 0x8048e5b0, - "r3": 0x00000020, - "r4": 0x00000000, - "r13": 0x80480820, - "r28": 0x00000001, # 0x809e53d8, # unused? - "r29": 0x807f1048, # 0x00000002, # unused? - "r30": 0x809e53d8, - "r31": 0x809e53d8, - "f1": hex_str_to_double("3ff0000000000000"), - "f30": hex_str_to_double("ffffffffffffffff"), - "p30": hex_str_to_double("4070000000000000"), - "f31": hex_str_to_double("402d6b5aa0000000"), # unused? - "p31": hex_str_to_double("0000000000000000"), + "lr": "80185afc", + "sp": "8048e5b0", + "r3": "00000020", + "r4": "00000000", + "r13": "80480820", + "r28": "00000001", # 0x809e53d8, # unused? + "r29": "807f1048", # 0x00000002, # unused? + "r30": "809e53d8", + "r31": "809e53d8", + "f1": "3ff0000000000000", + "f30": "ffffffffffffffff", + "p30": "4070000000000000", + "f31": "402d6b5aa0000000", # unused? + "p31": "0000000000000000", } starting_ram = { # constants @@ -209,6 +210,10 @@ def walk(self): starting_ram[0x8050f0e0] = single_to_hex_str(self.nextY) exc = PPC_executor(0x80188214, starting_ram, starting_registers) + + if self.debug: + exc.breakpoints = [0x800cc2f0] + exc.execute() calcedX = hex_str_to_single( @@ -219,6 +224,7 @@ def walk(self): # check if we would pass the destination and if so stop at it if (self.nextX and ((self.nextX <= self.destX and self.destX <= calcedX) or (self.nextX >= self.destX and self.destX >= calcedX))): + print("stopping instead of going to ", single_to_hex_str(calcedX), single_to_hex_str(calcedY)) calcedX = self.destX if (self.nextY and ((self.nextY <= self.destY and self.destY <= calcedY) or diff --git a/prediction_vid.py b/prediction_vid.py index a28242a..58e6292 100644 --- a/prediction_vid.py +++ b/prediction_vid.py @@ -3,42 +3,27 @@ import numpy import os -from conversion import * -from walk_analysis import paths - -def create_frames(cors): +def create_frame(frame, x, y): """ - For each cordinate pair, output a graph to be used as a frame + For given frame and position output a graph to be used as a frame """ - - for i in range(len(cors)): - fig = plt.figure() - plt.ylim(-50, 50) - plt.xlim(-50, 50) - - plt.plot(cors[i][0], cors[i][1], "ro") - fig.savefig("frames/{}.png".format(i)) - plt.close() + fig = plt.figure() + plt.ylim(-50, 50) + plt.xlim(-50, 50) + plt.plot(x, -y, "ro") + fig.savefig("frames/{}.png".format(frame)) + plt.close() -def create_video(): +def create_video(fps=60): """ - Merges images in /frames into a 60 fps prediction video + Merges images in /frames into a prediction video """ fnames = os.listdir("frames/") fnames.sort(key= lambda a : int(a[:-4])) - videowriter = imageio.get_writer('prediction_video.mp4', fps=60) + videowriter = imageio.get_writer('prediction_video.mp4', fps=fps) for fname in fnames: videowriter.append_data(plt.imread("frames/{}".format(fname))) videowriter.close() - -positions = [] -for path in paths: - for c in path: - positions.append((hex_str_to_single(c[0]), - hex_str_to_single(c[1]))) - -create_frames(positions) -create_video() \ No newline at end of file diff --git a/prng_predictor.py b/prng_predictor.py index a9dbcda..27c6413 100644 --- a/prng_predictor.py +++ b/prng_predictor.py @@ -3,7 +3,7 @@ from npc import NPC import random -from walk_analysis import paths +from prediction_vid import create_frame, create_video def prng_range(prng_state, n): @@ -62,32 +62,29 @@ def pyrite_noise(prng_state, noise_state): lcg = LCG(seed) npc = NPC(lcg, float32(4.0), float32(24.0)) - for i in range(1, 52000): + for i in range(1, 60*60*2): npc.step() - print("step: ", i) - print("prng: ", int_to_hex_str(lcg.state)) - print("destX: ", single_to_hex_str(npc.destX)) - print("destY: ", single_to_hex_str(npc.destY)) - print("speed: ", single_to_hex_str(npc.walk_speed)) - print("currentX: ", single_to_hex_str(npc.currentX)) - print("currentY: ", single_to_hex_str(npc.currentY)) - print("wait_time: ", single_to_hex_str(npc.wait_time)) - if i >= 165: - input("press ENTER to continue") - print() - - ''' - npc.step() - assert single_to_hex_str(npc.destX) == "40327ef7" - assert single_to_hex_str(npc.destY) == "421bcddc" - if single_to_hex_str(npc.walk_speed) != "bda585a9": - raise ValueError(single_to_hex_str(npc.walk_speed)) - - for step in paths[0]: - npc.step() - if single_to_hex_str(npc.currentX).lower() != step[0].lower(): - raise ValueError("unexpected currentX", single_to_hex_str(npc.currentX), step[0]) - if single_to_hex_str(npc.currentY).lower() != step[1].lower(): - raise ValueError("unexpected currentY", single_to_hex_str(npc.currentY), step[1]) - print("good step!") - ''' + # create_frame(i, npc.currentX, npc.currentY) + + if i >= 368: + print("step: ", i) + print("prng: ", int_to_hex_str(lcg.state)) + print("destX: ", single_to_hex_str(npc.destX)) + print("destY: ", single_to_hex_str(npc.destY)) + print("speed: ", single_to_hex_str(npc.walk_speed)) + print("currentX: ", single_to_hex_str(npc.currentX)) + print("currentY: ", single_to_hex_str(npc.currentY)) + try: + print("nextX: ", single_to_hex_str(npc.nextX)) + print("nextY: ", single_to_hex_str(npc.nextY)) + except: + print("nextX: None") + print("nextY: None") + print("wait_time: ", single_to_hex_str(npc.wait_time)) + + npc.debug = True + + # input("press ENTER to continue") + print() + + # create_video(fps=18) \ No newline at end of file