Skip to content

Commit

Permalink
Updated for Gameshark code usage
Browse files Browse the repository at this point in the history
Added a few of user exposed functions; set_cheats_path(), toggle_cheats(), and run_cheats()  that allow user to save gameshark codes to a .txt file and run them in-game
  • Loading branch information
Fort-Bonnitar committed Nov 14, 2023
1 parent 9d93bc4 commit a8a74b7
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 1 deletion.
59 changes: 58 additions & 1 deletion pyboy/pyboy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
from pyboy.openai_gym import PyBoyGymEnv
from pyboy.openai_gym import enabled as gym_enabled
from pyboy.plugins.manager import PluginManager
from pyboy.utils import IntIOWrapper, WindowEvent
from pyboy.utils import IntIOWrapper, WindowEvent, GameShark


from . import botsupport
from .core.mb import Motherboard
Expand Down Expand Up @@ -109,6 +110,12 @@ def __init__(
self.stopped = False
self.window_title = "PyBoy"


self.cheats_enabled = False
self.gameshark = None
self.cheats_path = ''


###################
# Plugins

Expand Down Expand Up @@ -509,3 +516,53 @@ def _rendering(self, value):

def _is_cpu_stuck(self):
return self.mb.cpu.is_stuck



####################
# Cheat Codes #
def set_cheats_path(self, cheats_path: str):
'''
Sets the path to the .txt file containing the cheat codes.
The cheats_path should
point to a saved .txt file containing the codes with one code per line formatted with a name for the code and the code itself seperated by a space:
{code_name} {code}
Ex.
NoWildEncounters 01033CD1
InfiniteMoney 019947D3
'''
self.cheats_enabled = True
self.cheats_path = cheats_path
self.gameshark = GameShark()
if self.cheats_path != None:
self.gameshark.set_path(self.cheats_path)
else:
print('Error: Cheat code path not set!')

def run_cheats(self):
'''
Run this function on Tick to have the codes run in-game, as well as allowing
for the change of cheats at runtime by modifying the {cheat_path.txt} file.
Ensure to save the {cheat_path.txt} file after modifications.
'''
if self.gameshark != None:
if self.cheats_enabled:
self.gameshark._update_codes()
for key in self.gameshark.cheats.keys():
if key in self.gameshark.cheats:
cheat = self.gameshark.cheats[key]
address = cheat['address']
value = cheat['value']
# print(f'cheat_address= {address}, cheat_value= {value}')
self.set_memory_value(address, value)
else:
print(f"Cheat '{key}' not found in the cheats database.")
else:
print('Error: Need to run pyboy.set_cheats_path(cheat_path) to set the path of the cheat codes file.')

def toggle_cheats(self, active: bool):
'''
Allows for toggling the use of codes
'''
self.cheats_enabled = active
76 changes: 76 additions & 0 deletions pyboy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,79 @@ def __init__(
self.mouse_scroll_x = mouse_scroll_x
self.mouse_scroll_y = mouse_scroll_y
self.mouse_button = mouse_button



# Gameshark Cheat Codes
class GameShark:
def __init__(self):
'''
This class allows for the conversion and usage of Gameshark codes. The cheats_path should
point to a saved .txt file containing the codes with the following format a name for the code and the code itself seperated by a space:
{code_name} {code}
Ex.
NoWildEncounters 01033CD1
'''

self.cheats_path = None
self.cheats = {}
self._update_codes()

def set_path(self, path):
self.cheats_path = path
self._update_codes()


def _get_cheats(self):
with open(self.cheats_path, 'r') as f:
lines = f.readlines()
for line in lines:
parts = line.strip().split()
if len(parts) == 2:
cheat_name, gameshark_code = parts[0], parts[1]
self._convert_cheat(cheat_name, gameshark_code)
else:
print(f"Invalid line in cheats file: {line}")



def _convert_cheat(self, cheat_name: str, gameshark_code: str):
'''
A GameShark code for these consoles is written in the format ttvvaaaa. tt specifies the code type and VRAM bank, which is usually 01. vv specifies the hexadecimal value the code will write into the game's memory. aaaa specifies the memory address that will be modified, with the low byte first (e.g. address C056 is written as 56C0).
Example 011556C0 would output:
location = 01
value = 0x15
address = 0x56C0
'''
# Check if the input cheat code has the correct length (8 characters)
if len(gameshark_code) != 8:
raise ValueError("Invalid cheat code length. Cheat code must be 8 characters long.")

# Extract components from the cheat code
code_type = gameshark_code[:2]
value = int((f'0x{gameshark_code[2:4]}'), 16) # Convert hexadecimal value to an integer
print(f'converted value = 0x{gameshark_code[2:4]}')
unconverted_address = gameshark_code[4:] # Ex: 1ED1
lower = unconverted_address[:2] # Ex: 1E
upper = unconverted_address[2:] # Ex: D1
address_converted = '0x' + upper + lower # Ex: 0xD11E # Converting to Ram Readable address
print(f'converted address = {address_converted}')
address = int(address_converted, 16)

# Format the output
formatted_output = {
'location': code_type,
'value': value,
'address': address
}
self.cheats[cheat_name] = formatted_output



def _update_codes(self):
self.cheats = {}
self._get_cheats()



0 comments on commit a8a74b7

Please sign in to comment.