Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand cartridge information #336

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,42 @@ <h2 id="kwargs">Kwargs</h2>
Game title
&#34;&#34;&#34;

self.cartridge_title = self.mb.cartridge.gametype
&#34;&#34;&#34;
The game type stored on the currently loaded cartridge ROM. Values are:
Game Boy, Game Boy Color, Super Game Boy

Example:
```python
&gt;&gt;&gt; pyboy.cartridge_type # Game type of PyBoy&#39;s default ROM
&#39;Game Boy Color&#39;

```

Returns
-------
str :
Game type
&#34;&#34;&#34;

self.cartridge_region = self.mb.cartridge.gameregion
&#34;&#34;&#34;
The game region stored on the currently loaded cartridge ROM. Example values are:
Europe, USA, Japan, Spain, Germany, World...

Example:
```python
&gt;&gt;&gt; pyboy.cartridge_region # Game region of PyBoy&#39;s default ROM
&#39;Europe&#39;

```

Returns
-------
str :
Europe
&#34;&#34;&#34;

self._hooks = {}

self._plugin_manager = PluginManager(self, self.mb, kwargs)
Expand Down
4 changes: 4 additions & 0 deletions pyboy/core/cartridge/base_mbc.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ cdef Logger logger
cdef class BaseMBC:
cdef str filename
cdef str gamename
cdef srt gametype
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misspelled str

cdef str gameregion
cdef uint8_t[:, :] rombanks
cdef uint8_t[:,:] rambanks
cdef uint8_t carttype
Expand All @@ -37,6 +39,8 @@ cdef class BaseMBC:
cdef void load_ram(self, IntIOInterface) noexcept
cdef void init_rambanks(self, uint8_t) noexcept
cdef str getgamename(self, uint8_t[:,:]) noexcept
cdef str getgametype(self, uint8_t[:,:]) noexcept
cdef str getgameregion(self, uint8_t[:,:]) noexcept

cdef uint8_t getitem(self, uint16_t) noexcept nogil
cdef void setitem(self, uint16_t, uint8_t) noexcept nogil
Expand Down
54 changes: 53 additions & 1 deletion pyboy/core/cartridge/base_mbc.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def __init__(self, filename, rombanks, external_ram_count, carttype, sram, batte
self.external_ram_count = external_ram_count
self.init_rambanks(external_ram_count)
self.gamename = self.getgamename(rombanks)
self.gametype = self.getgametype(rombanks)
self.gameregion = self.getgameregion(rombanks)

self.memorymodel = 0
self.rambank_enabled = False
Expand Down Expand Up @@ -103,7 +105,55 @@ def init_rambanks(self, n):
self.rambanks = memoryview(array.array("B", [0] * (8*1024*16))).cast("B", shape=(16, 8 * 1024))

def getgamename(self, rombanks):
return "".join([chr(rombanks[0, x]) for x in range(0x0134, 0x0142)]).split("\0")[0]
return "".join([chr(rombanks[0, x]) for x in range(0x0134, 0x0143)]).split("\0")[0]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to conditionally extract a shorter or longer string if it's a CGB or DMG ROM? I know it's a little outside of your PR


def getgametype(self, rombanks):
if rombanks[0, 0x143] == 0x80 or rombanks[0, 0x143] == 0xc0:
return "Game Boy Color"
elif rombanks[0, 0x146] == 0x03:
return "Super Game Boy"
else:
return "Game Boy"
Comment on lines +110 to +116
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably better to split this out into separate "cgb" and "sgb" checks, as they are seemingly not mutually exclusive https://gbdev.io/pandocs/The_Cartridge_Header.html


def getgameregion(self, rombanks):
game_type = self.getgametype(rombanks)
# The region character is only present in GBC/SGB games only; for regular GB games can't be fetched
if game_type == 'Game Boy':
return "Unknown"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to return None, when we don't have an answer. It's more intuitive that it returns something that evaluates to False: bool(pyboy.cartridge_region) == False.

I.e. this won't work if it's "Unknown":

if python.cartridge_region:
    do_something(python.cartridge_region)

else:
# Get the last letter of the sales code that determines the region
region_string = "".join([chr(rombanks[0, x]) for x in range(0x13f, 0x0143)]).split("\0")[0]
if region_string:
region_code = region_string[-1]
else:
return "Unknown"
# Return game region according to the last letter of the serial code
if region_code == 'J':
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did you find these codes?

return "Japan"
elif region_code == 'E':
return "USA"
elif region_code == 'P':
return "Europe"
elif region_code == 'S':
return "Spain"
elif region_code == 'I':
return "Italy"
elif region_code == 'F':
return "France"
elif region_code == 'D':
return "Germany"
elif region_code == 'A':
return "World"
elif region_code == 'X':
return "Europe"
elif region_code == 'Y':
return "Europe"
elif region_code == 'K':
return "Korea"
elif region_code == 'U':
return "Australia"
else:
return "Unknown"

def setitem(self, address, value):
raise Exception("Cannot set item in MBC")
Expand Down Expand Up @@ -138,6 +188,8 @@ def __repr__(self):
"MBC class: %s" % self.__class__.__name__,
"Filename: %s" % self.filename,
"Game name: %s" % self.gamename,
"Game type: %s" % self.gametype,
"Game region: %s" % self.gameregion,
"GB Color: %s" % str(self.rombanks[0, 0x143] == 0x80),
"Cartridge type: %s" % hex(self.carttype),
"Number of ROM banks: %s" % self.external_rom_count,
Expand Down
2 changes: 2 additions & 0 deletions pyboy/pyboy.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ cdef class PyBoy:
cdef readonly object game_wrapper
cdef readonly MemoryScanner memory_scanner
cdef readonly str cartridge_title
cdef readonly str cartridge_type
cdef readonly str cartridge_region

cdef bint limit_emulationspeed
cdef int emulationspeed, target_emulationspeed, save_target_emulationspeed
Expand Down
36 changes: 36 additions & 0 deletions pyboy/pyboy.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,42 @@ def __init__(
Game title
"""

self.cartridge_type = self.mb.cartridge.gametype
"""
The game type stored on the currently loaded cartridge ROM. Values are:
Game Boy, Game Boy Color, Super Game Boy

Example:
```python
>>> pyboy.cartridge_type # Game type of PyBoy's default ROM
'Game Boy Color'

```

Returns
-------
str :
Game type
"""

self.cartridge_region = self.mb.cartridge.gameregion
"""
The game region stored on the currently loaded cartridge ROM. Example values are:
Europe, USA, Japan, Spain, Germany, World...

Example:
```python
>>> pyboy.cartridge_region # Game region of PyBoy's default ROM
'Europe'

```

Returns
-------
str :
Europe
"""

Comment on lines +326 to +361
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might make sense to move this to pyboy.cartridge.region and create a cartridge API, just like pyboy.screen etc. I can do it, if you think it's too much.

self._hooks = {}

self._plugin_manager = PluginManager(self, self.mb, kwargs)
Expand Down
Loading