mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00

Bizhawk's "hard reset" option fills RAM with 0x55s. This causes game completion to be erroneously flagged, and likely many erroneous location checks with it. This fix checks for 0x55 and will not proceed to process anything if present.
120 lines
4.2 KiB
Python
120 lines
4.2 KiB
Python
|
|
from NetUtils import ClientStatus, color
|
|
from worlds.AutoSNIClient import SNIClient
|
|
from .Regions import offset
|
|
import logging
|
|
|
|
snes_logger = logging.getLogger("SNES")
|
|
|
|
ROM_NAME = (0x7FC0, 0x7FD4 + 1 - 0x7FC0)
|
|
|
|
READ_DATA_START = 0xF50EA8
|
|
READ_DATA_END = 0xF50FE7 + 1
|
|
|
|
GAME_FLAGS = (0xF50EA8, 64)
|
|
COMPLETED_GAME = (0xF50F22, 1)
|
|
BATTLEFIELD_DATA = (0xF50FD4, 20)
|
|
|
|
RECEIVED_DATA = (0xE01FF0, 3)
|
|
|
|
ITEM_CODE_START = 0x420000
|
|
|
|
IN_GAME_FLAG = (4 * 8) + 2
|
|
|
|
NPC_CHECKS = {
|
|
4325676: ((6 * 8) + 4, False), # Old Man Level Forest
|
|
4325677: ((3 * 8) + 6, True), # Kaeli Level Forest
|
|
4325678: ((25 * 8) + 1, True), # Tristam
|
|
4325680: ((26 * 8) + 0, True), # Aquaria Vendor Girl
|
|
4325681: ((29 * 8) + 2, True), # Phoebe Wintry Cave
|
|
4325682: ((25 * 8) + 6, False), # Mysterious Man (Life Temple)
|
|
4325683: ((29 * 8) + 3, True), # Reuben Mine
|
|
4325684: ((29 * 8) + 7, True), # Spencer
|
|
4325685: ((29 * 8) + 6, False), # Venus Chest
|
|
4325686: ((29 * 8) + 1, True), # Fireburg Tristam
|
|
4325687: ((26 * 8) + 1, True), # Fireburg Vendor Girl
|
|
4325688: ((14 * 8) + 4, True), # MegaGrenade Dude
|
|
4325689: ((29 * 8) + 5, False), # Tristam's Chest
|
|
4325690: ((29 * 8) + 4, True), # Arion
|
|
4325691: ((29 * 8) + 0, True), # Windia Kaeli
|
|
4325692: ((26 * 8) + 2, True), # Windia Vendor Girl
|
|
|
|
}
|
|
|
|
|
|
def get_flag(data, flag):
|
|
byte = int(flag / 8)
|
|
bit = int(0x80 / (2 ** (flag % 8)))
|
|
return (data[byte] & bit) > 0
|
|
|
|
|
|
class FFMQClient(SNIClient):
|
|
game = "Final Fantasy Mystic Quest"
|
|
|
|
async def validate_rom(self, ctx):
|
|
from SNIClient import snes_read
|
|
rom_name = await snes_read(ctx, *ROM_NAME)
|
|
if rom_name is None:
|
|
return False
|
|
if rom_name[:2] != b"MQ":
|
|
return False
|
|
|
|
ctx.rom = rom_name
|
|
ctx.game = self.game
|
|
ctx.items_handling = 0b001
|
|
return True
|
|
|
|
async def game_watcher(self, ctx):
|
|
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
|
|
|
|
check_1 = await snes_read(ctx, 0xF53749, 1)
|
|
received = await snes_read(ctx, RECEIVED_DATA[0], RECEIVED_DATA[1])
|
|
data = await snes_read(ctx, READ_DATA_START, READ_DATA_END - READ_DATA_START)
|
|
check_2 = await snes_read(ctx, 0xF53749, 1)
|
|
if check_1 in (b'\x00', b'\x55') or check_2 in (b'\x00', b'\x55'):
|
|
return
|
|
|
|
def get_range(data_range):
|
|
return data[data_range[0] - READ_DATA_START:data_range[0] + data_range[1] - READ_DATA_START]
|
|
completed_game = get_range(COMPLETED_GAME)
|
|
battlefield_data = get_range(BATTLEFIELD_DATA)
|
|
game_flags = get_range(GAME_FLAGS)
|
|
|
|
if game_flags is None:
|
|
return
|
|
if not get_flag(game_flags, IN_GAME_FLAG):
|
|
return
|
|
|
|
if not ctx.finished_game:
|
|
if completed_game[0] & 0x80 and game_flags[30] & 0x18:
|
|
await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
|
|
ctx.finished_game = True
|
|
|
|
old_locations_checked = ctx.locations_checked.copy()
|
|
|
|
for container in range(256):
|
|
if get_flag(game_flags, (0x20 * 8) + container):
|
|
ctx.locations_checked.add(offset["Chest"] + container)
|
|
|
|
for location, data in NPC_CHECKS.items():
|
|
if get_flag(game_flags, data[0]) is data[1]:
|
|
ctx.locations_checked.add(location)
|
|
|
|
for battlefield in range(20):
|
|
if battlefield_data[battlefield] == 0:
|
|
ctx.locations_checked.add(offset["BattlefieldItem"] + battlefield + 1)
|
|
|
|
if old_locations_checked != ctx.locations_checked:
|
|
await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": ctx.locations_checked}])
|
|
|
|
if received[0] == 0:
|
|
received_index = int.from_bytes(received[1:], "big")
|
|
if received_index < len(ctx.items_received):
|
|
item = ctx.items_received[received_index]
|
|
received_index += 1
|
|
code = (item.item - ITEM_CODE_START) + 1
|
|
if code > 256:
|
|
code -= 256
|
|
snes_buffered_write(ctx, RECEIVED_DATA[0], bytes([code, *received_index.to_bytes(2, "big")]))
|
|
await snes_flush_writes(ctx)
|