Skip to content

Commit

Permalink
Robust CGB detection with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Baekalfen committed Dec 17, 2024
1 parent c845acf commit 66d4d99
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 62 deletions.
1 change: 1 addition & 0 deletions pyboy/core/bootrom.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ cdef Logger logger

cdef class BootROM:
cdef uint8_t[:] bootrom
cdef bint cgb
cdef uint8_t getitem(self, uint16_t) noexcept nogil
6 changes: 3 additions & 3 deletions pyboy/core/bootrom.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@


class BootROM:
def __init__(self, bootrom_file, cgb):

def __init__(self, bootrom_file, cartridge_cgb):
if bootrom_file is None:
# Default to PyBoy boot ROM
rom = "/bootrom_cgb.bin" if cgb else "/bootrom_dmg.bin"
rom = "/bootrom_cgb.bin" if cartridge_cgb else "/bootrom_dmg.bin"
bootrom_file = os.path.dirname(os.path.realpath(__file__)) + rom

with open(bootrom_file, "rb") as f:
rom = f.read()

self.bootrom = array.array("B", struct.unpack("%iB" % len(rom), rom))
self.cgb = len(rom) > 0x100

def getitem(self, addr):
return self.bootrom[addr]
15 changes: 10 additions & 5 deletions pyboy/core/mb.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,33 @@ def __init__(

self.cartridge = cartridge.load_cartridge(gamerom)
logger.debug("Cartridge started:\n%s", str(self.cartridge))

self.bootrom = bootrom.BootROM(bootrom_file, self.cartridge.cgb)
if self.bootrom.cgb:
logger.debug("Boot ROM type auto-detected to %s", ("CGB" if self.bootrom.cgb else "DMG"))
cgb = cgb or True

if cgb is None:
cgb = self.cartridge.cgb
logger.debug("Cartridge type auto-detected to %s", ("CGB" if cgb else "DMG"))
logger.debug("Cartridge type auto-detected to %s", ("CGB" if self.cartridge.cgb else "DMG"))

self.timer = timer.Timer()
self.interaction = interaction.Interaction()
self.bootrom = bootrom.BootROM(bootrom_file, cgb)
self.ram = ram.RAM(cgb, randomize=randomize)
self.cpu = cpu.CPU(self)

if cgb:
self.lcd = lcd.CGBLCD(
cgb,
self.cartridge.cgb,
self.cartridge.cgb or self.bootrom.cgb,
color_palette,
cgb_color_palette,
randomize=randomize,
)
else:
self.lcd = lcd.LCD(
cgb,
self.cartridge.cgb,
self.cartridge.cgb or self.bootrom.cgb,
color_palette,
cgb_color_palette,
randomize=randomize,
Expand Down Expand Up @@ -345,7 +350,7 @@ def tick(self):
#
def getitem(self, i):
if 0x0000 <= i < 0x4000: # 16kB ROM bank #0
if self.bootrom_enabled and (i <= 0xFF or (self.cgb and 0x200 <= i < 0x900)):
if self.bootrom_enabled and (i <= 0xFF or (self.bootrom.cgb and 0x200 <= i < 0x900)):
return self.bootrom.getitem(i)
else:
return self.cartridge.rombanks[self.cartridge.rombank_selected_low, i]
Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ def kirby_rom(secrets):


@pytest.fixture(scope="session")
def any_rom(default_rom):
return default_rom
def any_rom(secrets, tetris_rom):
return tetris_rom


@pytest.fixture(scope="session")
Expand Down
55 changes: 3 additions & 52 deletions tests/test_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,9 @@
import hashlib
import os
import os.path
import sys
from io import BytesIO
from pathlib import Path
from unittest import mock

import PIL
import pytest
from pytest_lazy_fixtures import lf

from pyboy import PyBoy
from pyboy import __main__ as main
Expand Down Expand Up @@ -302,10 +297,10 @@ def test_randomize_ram(default_rom):
pyboy.stop(save=False)


def test_not_cgb(pokemon_crystal_rom):
pyboy = PyBoy(pokemon_crystal_rom, window="null", cgb=False)
def test_not_cgb(pokemon_crystal_rom, boot_rom):
pyboy = PyBoy(pokemon_crystal_rom, window="null", cgb=False, bootrom=boot_rom)
pyboy.set_emulation_speed(0)
pyboy.tick(60 * 7, False)
pyboy.tick(60 * 10, True)

assert pyboy.tilemap_background[1:16, 16] == [
134,
Expand All @@ -326,47 +321,3 @@ def test_not_cgb(pokemon_crystal_rom):
] # Assert that the screen says "Game Boy Color." at the bottom.

pyboy.stop(save=False)


OVERWRITE_PNGS = False


@pytest.mark.parametrize("cgb", [False, True, None])
@pytest.mark.parametrize("_bootrom, frames", [(lf("boot_cgb_rom"), 120), (lf("boot_rom"), 120), (None, 30)])
@pytest.mark.parametrize("rom", [lf("tetris_rom"), lf("any_rom_cgb")])
def test_all_modes(cgb, _bootrom, frames, rom, any_rom_cgb, boot_cgb_rom):
if cgb == False and _bootrom == boot_cgb_rom:
pytest.skip("Invalid combination")

if cgb == None and _bootrom == boot_cgb_rom and rom != any_rom_cgb:
pytest.skip("Invalid combination")

pyboy = PyBoy(rom, window="null", bootrom=_bootrom, cgb=cgb)
pyboy.set_emulation_speed(0)
pyboy.tick(frames, True)

rom_name = "cgbrom" if rom == any_rom_cgb else "dmgrom"
png_path = Path(f"tests/test_results/all_modes/{rom_name}_{cgb}_{os.path.basename(str(_bootrom))}.png")
image = pyboy.screen.image
if OVERWRITE_PNGS:
png_path.parents[0].mkdir(parents=True, exist_ok=True)
png_buf = BytesIO()
image.save(png_buf, "png")
with open(png_path, "wb") as f:
f.write(b"".join([(x ^ 0b10011101).to_bytes(1, sys.byteorder) for x in png_buf.getvalue()]))
else:
png_buf = BytesIO()
with open(png_path, "rb") as f:
data = f.read()
png_buf.write(b"".join([(x ^ 0b10011101).to_bytes(1, sys.byteorder) for x in data]))
png_buf.seek(0)

old_image = PIL.Image.open(png_buf).convert("RGB")
diff = PIL.ImageChops.difference(image.convert("RGB"), old_image)
if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"):
image.show()
old_image.show()
diff.show()
assert not diff.getbbox(), f"Images are different! {(cgb, _bootrom, frames, rom)}"

pyboy.stop(save=False)
52 changes: 52 additions & 0 deletions tests/test_bootroms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#
# License: See LICENSE.md file
# GitHub: https://github.com/Baekalfen/PyBoy
#

import os.path
import sys
from io import BytesIO
from pathlib import Path

import PIL
import pytest
from pytest_lazy_fixtures import lf

from pyboy import PyBoy

OVERWRITE_PNGS = False


@pytest.mark.parametrize("cgb", [False, True, None])
@pytest.mark.parametrize("_bootrom, frames", [(lf("boot_cgb_rom"), 120), (lf("boot_rom"), 120), (None, 30)])
@pytest.mark.parametrize("rom", [lf("any_rom"), lf("any_rom_cgb")])
def test_all_modes(cgb, _bootrom, frames, rom, any_rom_cgb):
pyboy = PyBoy(rom, window="null", bootrom=_bootrom, cgb=cgb)
pyboy.set_emulation_speed(0)
pyboy.tick(frames, True)

rom_name = "cgbrom" if rom == any_rom_cgb else "dmgrom"
png_path = Path(f"tests/test_results/all_modes/{rom_name}_{cgb}_{os.path.basename(str(_bootrom))}.png")
image = pyboy.screen.image
if OVERWRITE_PNGS:
png_path.parents[0].mkdir(parents=True, exist_ok=True)
png_buf = BytesIO()
image.save(png_buf, "png")
with open(png_path, "wb") as f:
f.write(b"".join([(x ^ 0b10011101).to_bytes(1, sys.byteorder) for x in png_buf.getvalue()]))
else:
png_buf = BytesIO()
with open(png_path, "rb") as f:
data = f.read()
png_buf.write(b"".join([(x ^ 0b10011101).to_bytes(1, sys.byteorder) for x in data]))
png_buf.seek(0)

old_image = PIL.Image.open(png_buf).convert("RGB")
diff = PIL.ImageChops.difference(image.convert("RGB"), old_image)
if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"):
image.show()
old_image.show()
diff.show()
assert not diff.getbbox(), f"Images are different! {(cgb, _bootrom, frames, rom)}"

pyboy.stop(save=False)
Binary file modified tests/test_results/all_modes/dmgrom_True_CGB_ROM.bin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/test_results/dmg_which.gb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 66d4d99

Please sign in to comment.