mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Update struct
- Update for Generation 5. This means SaveFilePokemon becomes an abstract base, with SaveFilePokemonGen4 and SaveFilePokemonGen5 subclasses. The pokemon_struct is changed to a structure factory. Diff best viewed with the ignore whitespace setting. - Allow creating a SaveFilePokemon with a zeroed-out blob (to be filled in later). Several fields are zeroed out in gen 5 so this helps the above.
This commit is contained in:
parent
5ec4621cac
commit
4eceef18a7
2 changed files with 381 additions and 304 deletions
|
@ -13,7 +13,7 @@ import struct
|
||||||
from pokedex.db import tables
|
from pokedex.db import tables
|
||||||
from pokedex.formulae import calculated_hp, calculated_stat
|
from pokedex.formulae import calculated_hp, calculated_stat
|
||||||
from pokedex.compatibility import namedtuple, permutations
|
from pokedex.compatibility import namedtuple, permutations
|
||||||
from pokedex.struct._pokemon_struct import pokemon_struct
|
from pokedex.struct._pokemon_struct import make_pokemon_struct
|
||||||
|
|
||||||
def pokemon_prng(seed):
|
def pokemon_prng(seed):
|
||||||
u"""Creates a generator that simulates the main Pokémon PRNG."""
|
u"""Creates a generator that simulates the main Pokémon PRNG."""
|
||||||
|
@ -22,43 +22,54 @@ def pokemon_prng(seed):
|
||||||
seed &= 0xFFFFFFFF
|
seed &= 0xFFFFFFFF
|
||||||
yield seed >> 16
|
yield seed >> 16
|
||||||
|
|
||||||
|
|
||||||
class SaveFilePokemon(object):
|
class SaveFilePokemon(object):
|
||||||
u"""Represents an individual Pokémon, from the game's point of view.
|
u"""Base class for an individual Pokémon, from the game's point of view.
|
||||||
|
|
||||||
Handles translating between the on-disk encrypted form, the in-RAM blob
|
Handles translating between the on-disk encrypted form, the in-RAM blob
|
||||||
(also used by pokesav), and something vaguely intelligible.
|
(also used by pokesav), and something vaguely intelligible.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Stat = namedtuple('Stat', ['stat', 'base', 'gene', 'exp', 'calc'])
|
Stat = namedtuple('Stat', ['stat', 'base', 'gene', 'exp', 'calc'])
|
||||||
|
|
||||||
def __init__(self, blob, encrypted=False):
|
def __init__(self, blob=None, encrypted=False, session=None):
|
||||||
u"""Wraps a Pokémon save struct in a friendly object.
|
u"""Wraps a Pokémon save struct in a friendly object.
|
||||||
|
|
||||||
If `encrypted` is True, the blob will be decrypted as though it were an
|
If `encrypted` is True, the blob will be decrypted as though it were an
|
||||||
on-disk save. Otherwise, the blob is taken to be already decrypted and
|
on-disk save. Otherwise, the blob is taken to be already decrypted and
|
||||||
is left alone.
|
is left alone.
|
||||||
|
|
||||||
`session` is an optional database session.
|
`session` is an optional database session. Either give it or fill it
|
||||||
|
later with `use_database_session`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if encrypted:
|
try:
|
||||||
# Decrypt it.
|
self.generation_id
|
||||||
# Interpret as one word (pid), followed by a bunch of shorts
|
except AttributeError:
|
||||||
struct_def = "I" + "H" * ((len(blob) - 4) / 2)
|
raise NotImplementedError(
|
||||||
shuffled = list( struct.unpack(struct_def, blob) )
|
"Use generation-specific subclass of SaveFilePokemon")
|
||||||
|
|
||||||
# Apply standard Pokémon decryption, undo the block shuffling, and
|
if blob:
|
||||||
# done
|
if encrypted:
|
||||||
self.reciprocal_crypt(shuffled)
|
# Decrypt it.
|
||||||
words = self.shuffle_chunks(shuffled, reverse=True)
|
# Interpret as one word (pid), followed by a bunch of shorts
|
||||||
self.blob = struct.pack(struct_def, *words)
|
struct_def = "I" + "H" * ((len(blob) - 4) / 2)
|
||||||
|
shuffled = list( struct.unpack(struct_def, blob) )
|
||||||
|
|
||||||
|
# Apply standard Pokémon decryption, undo the block shuffling, and
|
||||||
|
# done
|
||||||
|
self.reciprocal_crypt(shuffled)
|
||||||
|
words = self.shuffle_chunks(shuffled, reverse=True)
|
||||||
|
self.blob = struct.pack(struct_def, *words)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Already decrypted
|
||||||
|
self.blob = blob
|
||||||
|
|
||||||
|
self.structure = self.pokemon_struct.parse(self.blob)
|
||||||
else:
|
else:
|
||||||
# Already decrypted
|
self.structure = self.pokemon_struct.parse('\0' * (32 * 4 + 8))
|
||||||
self.blob = blob
|
|
||||||
|
|
||||||
self.structure = pokemon_struct.parse(self.blob)
|
if session:
|
||||||
|
self.use_database_session(session)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def as_struct(self):
|
def as_struct(self):
|
||||||
|
@ -98,64 +109,72 @@ class SaveFilePokemon(object):
|
||||||
|
|
||||||
def use_database_session(self, session):
|
def use_database_session(self, session):
|
||||||
"""Remembers the given database session, and prefetches a bunch of
|
"""Remembers the given database session, and prefetches a bunch of
|
||||||
database stuff. Gotta call this before you use the database properties
|
database stuff. Gotta call this (or give it to `__init__`) before
|
||||||
like `species`, etc.
|
you use the database properties like `species`, etc.
|
||||||
"""
|
"""
|
||||||
self._session = session
|
self._session = session
|
||||||
|
|
||||||
st = self.structure
|
st = self.structure
|
||||||
self._pokemon = session.query(tables.Pokemon).get(st.national_id)
|
|
||||||
self._pokemon_form = session.query(tables.PokemonForm) \
|
if st.national_id:
|
||||||
.with_parent(self._pokemon) \
|
self._pokemon = session.query(tables.Pokemon).get(st.national_id)
|
||||||
.filter_by(name=st.alternate_form) \
|
self._pokemon_form = session.query(tables.PokemonForm) \
|
||||||
.one()
|
.with_parent(self._pokemon) \
|
||||||
|
.filter_by(form_identifier=st.alternate_form) \
|
||||||
|
.one()
|
||||||
|
else:
|
||||||
|
self._pokemon = self._pokemon_form = None
|
||||||
self._ability = self._session.query(tables.Ability).get(st.ability_id)
|
self._ability = self._session.query(tables.Ability).get(st.ability_id)
|
||||||
|
|
||||||
growth_rate = self._pokemon.evolution_chain.growth_rate
|
if self._pokemon:
|
||||||
self._experience_rung = session.query(tables.Experience) \
|
growth_rate = self._pokemon.species.growth_rate
|
||||||
.filter(tables.Experience.growth_rate == growth_rate) \
|
self._experience_rung = session.query(tables.Experience) \
|
||||||
.filter(tables.Experience.experience <= st.exp) \
|
|
||||||
.order_by(tables.Experience.level.desc()) \
|
|
||||||
[0]
|
|
||||||
level = self._experience_rung.level
|
|
||||||
|
|
||||||
self._next_experience_rung = None
|
|
||||||
if level < 100:
|
|
||||||
self._next_experience_rung = session.query(tables.Experience) \
|
|
||||||
.filter(tables.Experience.growth_rate == growth_rate) \
|
.filter(tables.Experience.growth_rate == growth_rate) \
|
||||||
.filter(tables.Experience.level == level + 1) \
|
.filter(tables.Experience.experience <= st.exp) \
|
||||||
.one()
|
.order_by(tables.Experience.level.desc()) \
|
||||||
|
[0]
|
||||||
|
level = self._experience_rung.level
|
||||||
|
|
||||||
|
self._next_experience_rung = None
|
||||||
|
if level < 100:
|
||||||
|
self._next_experience_rung = session.query(tables.Experience) \
|
||||||
|
.filter(tables.Experience.growth_rate == growth_rate) \
|
||||||
|
.filter(tables.Experience.level == level + 1) \
|
||||||
|
.one()
|
||||||
|
|
||||||
self._held_item = None
|
self._held_item = None
|
||||||
if st.held_item_id:
|
if st.held_item_id:
|
||||||
self._held_item = session.query(tables.ItemGameIndex) \
|
self._held_item = session.query(tables.ItemGameIndex) \
|
||||||
.filter_by(generation_id = 4, game_index = st.held_item_id).one().item
|
.filter_by(generation_id = 4, game_index = st.held_item_id).one().item
|
||||||
|
|
||||||
self._stats = []
|
if self._pokemon:
|
||||||
for pokemon_stat in self._pokemon.stats:
|
self._stats = []
|
||||||
structure_name = pokemon_stat.stat.name.lower().replace(' ', '_')
|
for pokemon_stat in self._pokemon.stats:
|
||||||
gene = st.ivs['iv_' + structure_name]
|
structure_name = pokemon_stat.stat.name.lower().replace(' ', '_')
|
||||||
exp = st['effort_' + structure_name]
|
gene = st.ivs['iv_' + structure_name]
|
||||||
|
exp = st['effort_' + structure_name]
|
||||||
|
|
||||||
if pokemon_stat.stat.name == u'HP':
|
if pokemon_stat.stat.name == u'HP':
|
||||||
calc = calculated_hp
|
calc = calculated_hp
|
||||||
else:
|
else:
|
||||||
calc = calculated_stat
|
calc = calculated_stat
|
||||||
|
|
||||||
stat_tup = self.Stat(
|
stat_tup = self.Stat(
|
||||||
stat = pokemon_stat.stat,
|
stat = pokemon_stat.stat,
|
||||||
base = pokemon_stat.base_stat,
|
base = pokemon_stat.base_stat,
|
||||||
gene = gene,
|
gene = gene,
|
||||||
exp = exp,
|
exp = exp,
|
||||||
calc = calc(
|
calc = calc(
|
||||||
pokemon_stat.base_stat,
|
pokemon_stat.base_stat,
|
||||||
level = level,
|
level = level,
|
||||||
iv = gene,
|
iv = gene,
|
||||||
effort = exp,
|
effort = exp,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
self._stats.append(stat_tup)
|
self._stats.append(stat_tup)
|
||||||
|
else:
|
||||||
|
self._stats = [0] * 6
|
||||||
|
|
||||||
|
|
||||||
move_ids = (
|
move_ids = (
|
||||||
|
@ -171,10 +190,13 @@ class SaveFilePokemon(object):
|
||||||
|
|
||||||
if st.hgss_pokeball >= 17:
|
if st.hgss_pokeball >= 17:
|
||||||
pokeball_id = st.hgss_pokeball - 17 + 492
|
pokeball_id = st.hgss_pokeball - 17 + 492
|
||||||
else:
|
elif st.dppt_pokeball:
|
||||||
pokeball_id = st.dppt_pokeball
|
pokeball_id = st.dppt_pokeball
|
||||||
self._pokeball = session.query(tables.ItemGameIndex) \
|
else:
|
||||||
.filter_by(generation_id = 4, game_index = pokeball_id).one().item
|
pokeball_id = None
|
||||||
|
if pokeball_id:
|
||||||
|
self._pokeball = session.query(tables.ItemGameIndex) \
|
||||||
|
.filter_by(generation_id = 4, game_index = pokeball_id).one().item
|
||||||
|
|
||||||
egg_loc_id = st.pt_egg_location_id or st.dp_egg_location_id
|
egg_loc_id = st.pt_egg_location_id or st.dp_egg_location_id
|
||||||
met_loc_id = st.pt_met_location_id or st.dp_met_location_id
|
met_loc_id = st.pt_met_location_id or st.dp_met_location_id
|
||||||
|
@ -182,15 +204,25 @@ class SaveFilePokemon(object):
|
||||||
self._egg_location = None
|
self._egg_location = None
|
||||||
if egg_loc_id:
|
if egg_loc_id:
|
||||||
self._egg_location = session.query(tables.LocationGameIndex) \
|
self._egg_location = session.query(tables.LocationGameIndex) \
|
||||||
.filter_by(generation_id = 4, game_index = egg_loc_id).one().location
|
.filter_by(generation_id = self.generation_id, game_index = egg_loc_id).one().location
|
||||||
|
|
||||||
self._met_location = session.query(tables.LocationGameIndex) \
|
if met_loc_id:
|
||||||
.filter_by(generation_id = 4, game_index = met_loc_id).one().location
|
self._met_location = session.query(tables.LocationGameIndex) \
|
||||||
|
.filter_by(generation_id = self.generation_id, game_index = met_loc_id).one().location
|
||||||
|
else:
|
||||||
|
self._met_location = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def species(self):
|
def species(self):
|
||||||
# XXX forme!
|
return self._pokemon_form.species
|
||||||
return self._pokemon
|
|
||||||
|
@property
|
||||||
|
def pokemon(self):
|
||||||
|
return self._pokemon_form.pokemon
|
||||||
|
|
||||||
|
@property
|
||||||
|
def form(self):
|
||||||
|
return self._pokemon_form
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def species_form(self):
|
def species_form(self):
|
||||||
|
@ -208,16 +240,6 @@ class SaveFilePokemon(object):
|
||||||
def met_location(self):
|
def met_location(self):
|
||||||
return self._met_location
|
return self._met_location
|
||||||
|
|
||||||
@property
|
|
||||||
def shiny_leaves(self):
|
|
||||||
return (
|
|
||||||
self.structure.shining_leaves.leaf1,
|
|
||||||
self.structure.shining_leaves.leaf2,
|
|
||||||
self.structure.shining_leaves.leaf3,
|
|
||||||
self.structure.shining_leaves.leaf4,
|
|
||||||
self.structure.shining_leaves.leaf5,
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def level(self):
|
def level(self):
|
||||||
return self._experience_rung.level
|
return self._experience_rung.level
|
||||||
|
@ -319,3 +341,42 @@ class SaveFilePokemon(object):
|
||||||
words[i] ^= next(prng)
|
words[i] ^= next(prng)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class SaveFilePokemonGen4(SaveFilePokemon):
|
||||||
|
generation_id = 4
|
||||||
|
pokemon_struct = make_pokemon_struct(generation=generation_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def shiny_leaves(self):
|
||||||
|
return (
|
||||||
|
self.structure.shining_leaves.leaf1,
|
||||||
|
self.structure.shining_leaves.leaf2,
|
||||||
|
self.structure.shining_leaves.leaf3,
|
||||||
|
self.structure.shining_leaves.leaf4,
|
||||||
|
self.structure.shining_leaves.leaf5,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SaveFilePokemonGen5(SaveFilePokemon):
|
||||||
|
generation_id = 5
|
||||||
|
pokemon_struct = make_pokemon_struct(generation=generation_id)
|
||||||
|
|
||||||
|
def use_database_session(self, session):
|
||||||
|
super(SaveFilePokemonGen5, self).use_database_session(session)
|
||||||
|
|
||||||
|
st = self.structure
|
||||||
|
|
||||||
|
if st.nature_id:
|
||||||
|
self._nature = session.query(tables.Nature) \
|
||||||
|
.filter_by(game_index = st.nature_id).one()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nature(self):
|
||||||
|
return self._nature
|
||||||
|
|
||||||
|
|
||||||
|
save_file_pokemon_classes = {
|
||||||
|
4: SaveFilePokemonGen4,
|
||||||
|
5: SaveFilePokemonGen5,
|
||||||
|
}
|
||||||
|
|
|
@ -578,238 +578,254 @@ class PokemonFormAdapter(Adapter):
|
||||||
return forms.index(obj) << 3
|
return forms.index(obj) << 3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# And here we go.
|
|
||||||
# Docs: http://projectpokemon.org/wiki/Pokemon_NDS_Structure
|
# Docs: http://projectpokemon.org/wiki/Pokemon_NDS_Structure
|
||||||
pokemon_struct = Struct('pokemon_struct',
|
# http://projectpokemon.org/wiki/Pokemon_Black/White_NDS_Structure
|
||||||
# Header
|
# http://projectpokemon.org/forums/showthread.php?11474-Hex-Values-and-Trashbytes-in-B-W#post93598
|
||||||
ULInt32('personality'), # XXX aughgh http://bulbapedia.bulbagarden.net/wiki/Personality
|
|
||||||
Padding(2),
|
|
||||||
ULInt16('checksum'), # XXX should be checked or calculated
|
|
||||||
|
|
||||||
# Block A
|
def make_pokemon_struct(generation):
|
||||||
ULInt16('national_id'),
|
"""Make a pokemon struct class for the given generation
|
||||||
ULInt16('held_item_id'),
|
"""
|
||||||
ULInt16('original_trainer_id'),
|
leaves_or_nature = {
|
||||||
ULInt16('original_trainer_secret_id'),
|
4: BitStruct('shining_leaves',
|
||||||
ULInt32('exp'),
|
Padding(2),
|
||||||
ULInt8('happiness'),
|
Flag('crown'),
|
||||||
ULInt8('ability_id'), # XXX needs to match personality + species
|
Flag('leaf5'),
|
||||||
BitStruct('markings',
|
Flag('leaf4'),
|
||||||
|
Flag('leaf3'),
|
||||||
|
Flag('leaf2'),
|
||||||
|
Flag('leaf1'),
|
||||||
|
),
|
||||||
|
5: ULInt8('nature_id'),
|
||||||
|
}[generation]
|
||||||
|
|
||||||
|
padding_or_hidden_ability = {
|
||||||
|
4: Padding(1),
|
||||||
|
5: Flag('hidden_ability'),
|
||||||
|
}[generation]
|
||||||
|
|
||||||
|
return Struct('pokemon_struct',
|
||||||
|
# Header
|
||||||
|
ULInt32('personality'), # XXX aughgh http://bulbapedia.bulbagarden.net/wiki/Personality
|
||||||
Padding(2),
|
Padding(2),
|
||||||
Flag('diamond'),
|
ULInt16('checksum'), # XXX should be checked or calculated
|
||||||
Flag('star'),
|
|
||||||
Flag('heart'),
|
|
||||||
Flag('square'),
|
|
||||||
Flag('triangle'),
|
|
||||||
Flag('circle'),
|
|
||||||
),
|
|
||||||
Enum(
|
|
||||||
ULInt8('original_country'),
|
|
||||||
jp=1,
|
|
||||||
us=2,
|
|
||||||
fr=3,
|
|
||||||
it=4,
|
|
||||||
de=5,
|
|
||||||
es=7,
|
|
||||||
kr=8,
|
|
||||||
),
|
|
||||||
|
|
||||||
# XXX sum cannot surpass 510
|
# Block A
|
||||||
ULInt8('effort_hp'),
|
ULInt16('national_id'),
|
||||||
ULInt8('effort_attack'),
|
ULInt16('held_item_id'),
|
||||||
ULInt8('effort_defense'),
|
ULInt16('original_trainer_id'),
|
||||||
ULInt8('effort_speed'),
|
ULInt16('original_trainer_secret_id'),
|
||||||
ULInt8('effort_special_attack'),
|
ULInt32('exp'),
|
||||||
ULInt8('effort_special_defense'),
|
ULInt8('happiness'),
|
||||||
|
ULInt8('ability_id'), # XXX needs to match personality + species
|
||||||
|
BitStruct('markings',
|
||||||
|
Padding(2),
|
||||||
|
Flag('diamond'),
|
||||||
|
Flag('star'),
|
||||||
|
Flag('heart'),
|
||||||
|
Flag('square'),
|
||||||
|
Flag('triangle'),
|
||||||
|
Flag('circle'),
|
||||||
|
),
|
||||||
|
Enum(ULInt8('original_country'),
|
||||||
|
_unset = 0,
|
||||||
|
jp=1,
|
||||||
|
us=2,
|
||||||
|
fr=3,
|
||||||
|
it=4,
|
||||||
|
de=5,
|
||||||
|
es=7,
|
||||||
|
kr=8,
|
||||||
|
),
|
||||||
|
|
||||||
ULInt8('contest_cool'),
|
# XXX sum cannot surpass 510
|
||||||
ULInt8('contest_beauty'),
|
ULInt8('effort_hp'),
|
||||||
ULInt8('contest_cute'),
|
ULInt8('effort_attack'),
|
||||||
ULInt8('contest_smart'),
|
ULInt8('effort_defense'),
|
||||||
ULInt8('contest_tough'),
|
ULInt8('effort_speed'),
|
||||||
ULInt8('contest_sheen'),
|
ULInt8('effort_special_attack'),
|
||||||
|
ULInt8('effort_special_defense'),
|
||||||
|
|
||||||
LittleEndianBitStruct('sinnoh_ribbons',
|
ULInt8('contest_cool'),
|
||||||
|
ULInt8('contest_beauty'),
|
||||||
|
ULInt8('contest_cute'),
|
||||||
|
ULInt8('contest_smart'),
|
||||||
|
ULInt8('contest_tough'),
|
||||||
|
ULInt8('contest_sheen'),
|
||||||
|
|
||||||
|
LittleEndianBitStruct('sinnoh_ribbons',
|
||||||
|
Padding(4),
|
||||||
|
Flag('premier_ribbon'),
|
||||||
|
Flag('classic_ribbon'),
|
||||||
|
Flag('carnival_ribbon'),
|
||||||
|
Flag('festival_ribbon'),
|
||||||
|
Flag('blue_ribbon'),
|
||||||
|
Flag('green_ribbon'),
|
||||||
|
Flag('red_ribbon'),
|
||||||
|
Flag('legend_ribbon'),
|
||||||
|
Flag('history_ribbon'),
|
||||||
|
Flag('record_ribbon'),
|
||||||
|
Flag('footprint_ribbon'),
|
||||||
|
Flag('gorgeous_royal_ribbon'),
|
||||||
|
Flag('royal_ribbon'),
|
||||||
|
Flag('gorgeous_ribbon'),
|
||||||
|
Flag('smile_ribbon'),
|
||||||
|
Flag('snooze_ribbon'),
|
||||||
|
Flag('relax_ribbon'),
|
||||||
|
Flag('careless_ribbon'),
|
||||||
|
Flag('downcast_ribbon'),
|
||||||
|
Flag('shock_ribbon'),
|
||||||
|
Flag('alert_ribbon'),
|
||||||
|
Flag('world_ability_ribbon'),
|
||||||
|
Flag('pair_ability_ribbon'),
|
||||||
|
Flag('multi_ability_ribbon'),
|
||||||
|
Flag('double_ability_ribbon'),
|
||||||
|
Flag('great_ability_ribbon'),
|
||||||
|
Flag('ability_ribbon'),
|
||||||
|
Flag('sinnoh_champ_ribbon'),
|
||||||
|
),
|
||||||
|
|
||||||
|
# Block B
|
||||||
|
ULInt16('move1_id'),
|
||||||
|
ULInt16('move2_id'),
|
||||||
|
ULInt16('move3_id'),
|
||||||
|
ULInt16('move4_id'),
|
||||||
|
ULInt8('move1_pp'),
|
||||||
|
ULInt8('move2_pp'),
|
||||||
|
ULInt8('move3_pp'),
|
||||||
|
ULInt8('move4_pp'),
|
||||||
|
ULInt8('move1_pp_ups'),
|
||||||
|
ULInt8('move2_pp_ups'),
|
||||||
|
ULInt8('move3_pp_ups'),
|
||||||
|
ULInt8('move4_pp_ups'),
|
||||||
|
|
||||||
|
LittleEndianBitStruct('ivs',
|
||||||
|
Flag('is_nicknamed'),
|
||||||
|
Flag('is_egg'),
|
||||||
|
BitField('iv_special_defense', 5),
|
||||||
|
BitField('iv_special_attack', 5),
|
||||||
|
BitField('iv_speed', 5),
|
||||||
|
BitField('iv_defense', 5),
|
||||||
|
BitField('iv_attack', 5),
|
||||||
|
BitField('iv_hp', 5),
|
||||||
|
),
|
||||||
|
LittleEndianBitStruct('hoenn_ribbons',
|
||||||
|
Flag('world_ribbon'),
|
||||||
|
Flag('earth_ribbon'),
|
||||||
|
Flag('national_ribbon'),
|
||||||
|
Flag('country_ribbon'),
|
||||||
|
Flag('sky_ribbon'),
|
||||||
|
Flag('land_ribbon'),
|
||||||
|
Flag('marine_ribbon'),
|
||||||
|
Flag('effort_ribbon'),
|
||||||
|
Flag('artist_ribbon'),
|
||||||
|
Flag('victory_ribbon'),
|
||||||
|
Flag('winning_ribbon'),
|
||||||
|
Flag('champion_ribbon'),
|
||||||
|
Flag('tough_ribbon_master'),
|
||||||
|
Flag('tough_ribbon_hyper'),
|
||||||
|
Flag('tough_ribbon_super'),
|
||||||
|
Flag('tough_ribbon'),
|
||||||
|
Flag('smart_ribbon_master'),
|
||||||
|
Flag('smart_ribbon_hyper'),
|
||||||
|
Flag('smart_ribbon_super'),
|
||||||
|
Flag('smart_ribbon'),
|
||||||
|
Flag('cute_ribbon_master'),
|
||||||
|
Flag('cute_ribbon_hyper'),
|
||||||
|
Flag('cute_ribbon_super'),
|
||||||
|
Flag('cute_ribbon'),
|
||||||
|
Flag('beauty_ribbon_master'),
|
||||||
|
Flag('beauty_ribbon_hyper'),
|
||||||
|
Flag('beauty_ribbon_super'),
|
||||||
|
Flag('beauty_ribbon'),
|
||||||
|
Flag('cool_ribbon_master'),
|
||||||
|
Flag('cool_ribbon_hyper'),
|
||||||
|
Flag('cool_ribbon_super'),
|
||||||
|
Flag('cool_ribbon'),
|
||||||
|
),
|
||||||
|
EmbeddedBitStruct(
|
||||||
|
PokemonFormAdapter(BitField('alternate_form', 5)),
|
||||||
|
Enum(BitField('gender', 2),
|
||||||
|
genderless = 2,
|
||||||
|
male = 0,
|
||||||
|
female = 1,
|
||||||
|
),
|
||||||
|
Flag('fateful_encounter'),
|
||||||
|
),
|
||||||
|
leaves_or_nature,
|
||||||
|
padding_or_hidden_ability,
|
||||||
|
Padding(1),
|
||||||
|
ULInt16('pt_egg_location_id'),
|
||||||
|
ULInt16('pt_met_location_id'),
|
||||||
|
|
||||||
|
# Block C
|
||||||
|
PokemonStringAdapter(String('nickname', 22)),
|
||||||
|
Padding(1),
|
||||||
|
Enum(ULInt8('original_version'),
|
||||||
|
_unset = 0,
|
||||||
|
sapphire = 1,
|
||||||
|
ruby = 2,
|
||||||
|
emerald = 3,
|
||||||
|
firered = 4,
|
||||||
|
leafgreen = 5,
|
||||||
|
heartgold = 7,
|
||||||
|
soulsilver = 8,
|
||||||
|
diamond = 10,
|
||||||
|
pearl = 11,
|
||||||
|
platinum = 12,
|
||||||
|
orre = 15,
|
||||||
|
),
|
||||||
|
LittleEndianBitStruct('sinnoh_contest_ribbons',
|
||||||
|
Padding(12),
|
||||||
|
Flag('tough_ribbon_master'),
|
||||||
|
Flag('tough_ribbon_ultra'),
|
||||||
|
Flag('tough_ribbon_great'),
|
||||||
|
Flag('tough_ribbon'),
|
||||||
|
Flag('smart_ribbon_master'),
|
||||||
|
Flag('smart_ribbon_ultra'),
|
||||||
|
Flag('smart_ribbon_great'),
|
||||||
|
Flag('smart_ribbon'),
|
||||||
|
Flag('cute_ribbon_master'),
|
||||||
|
Flag('cute_ribbon_ultra'),
|
||||||
|
Flag('cute_ribbon_great'),
|
||||||
|
Flag('cute_ribbon'),
|
||||||
|
Flag('beauty_ribbon_master'),
|
||||||
|
Flag('beauty_ribbon_ultra'),
|
||||||
|
Flag('beauty_ribbon_great'),
|
||||||
|
Flag('beauty_ribbon'),
|
||||||
|
Flag('cool_ribbon_master'),
|
||||||
|
Flag('cool_ribbon_ultra'),
|
||||||
|
Flag('cool_ribbon_great'),
|
||||||
|
Flag('cool_ribbon'),
|
||||||
|
),
|
||||||
Padding(4),
|
Padding(4),
|
||||||
Flag('premier_ribbon'),
|
|
||||||
Flag('classic_ribbon'),
|
|
||||||
Flag('carnival_ribbon'),
|
|
||||||
Flag('festival_ribbon'),
|
|
||||||
Flag('blue_ribbon'),
|
|
||||||
Flag('green_ribbon'),
|
|
||||||
Flag('red_ribbon'),
|
|
||||||
Flag('legend_ribbon'),
|
|
||||||
Flag('history_ribbon'),
|
|
||||||
Flag('record_ribbon'),
|
|
||||||
Flag('footprint_ribbon'),
|
|
||||||
Flag('gorgeous_royal_ribbon'),
|
|
||||||
Flag('royal_ribbon'),
|
|
||||||
Flag('gorgeous_ribbon'),
|
|
||||||
Flag('smile_ribbon'),
|
|
||||||
Flag('snooze_ribbon'),
|
|
||||||
Flag('relax_ribbon'),
|
|
||||||
Flag('careless_ribbon'),
|
|
||||||
Flag('downcast_ribbon'),
|
|
||||||
Flag('shock_ribbon'),
|
|
||||||
Flag('alert_ribbon'),
|
|
||||||
Flag('world_ability_ribbon'),
|
|
||||||
Flag('pair_ability_ribbon'),
|
|
||||||
Flag('multi_ability_ribbon'),
|
|
||||||
Flag('double_ability_ribbon'),
|
|
||||||
Flag('great_ability_ribbon'),
|
|
||||||
Flag('ability_ribbon'),
|
|
||||||
Flag('sinnoh_champ_ribbon'),
|
|
||||||
),
|
|
||||||
|
|
||||||
# Block B
|
# Block D
|
||||||
ULInt16('move1_id'),
|
PokemonStringAdapter(String('original_trainer_name', 16)),
|
||||||
ULInt16('move2_id'),
|
DateAdapter(String('date_egg_received', 3)),
|
||||||
ULInt16('move3_id'),
|
DateAdapter(String('date_met', 3)),
|
||||||
ULInt16('move4_id'),
|
ULInt16('dp_egg_location_id'),
|
||||||
ULInt8('move1_pp'),
|
ULInt16('dp_met_location_id'),
|
||||||
ULInt8('move2_pp'),
|
ULInt8('pokerus'), # Warning : Values changed in gen 5
|
||||||
ULInt8('move3_pp'),
|
ULInt8('dppt_pokeball'),
|
||||||
ULInt8('move4_pp'),
|
EmbeddedBitStruct(
|
||||||
ULInt8('move1_pp_ups'),
|
Enum(Flag('original_trainer_gender'),
|
||||||
ULInt8('move2_pp_ups'),
|
male = False,
|
||||||
ULInt8('move3_pp_ups'),
|
female = True,
|
||||||
ULInt8('move4_pp_ups'),
|
),
|
||||||
|
BitField('met_at_level', 7),
|
||||||
LittleEndianBitStruct('ivs',
|
|
||||||
Flag('is_nicknamed'),
|
|
||||||
Flag('is_egg'),
|
|
||||||
BitField('iv_special_defense', 5),
|
|
||||||
BitField('iv_special_attack', 5),
|
|
||||||
BitField('iv_speed', 5),
|
|
||||||
BitField('iv_defense', 5),
|
|
||||||
BitField('iv_attack', 5),
|
|
||||||
BitField('iv_hp', 5),
|
|
||||||
),
|
|
||||||
LittleEndianBitStruct('hoenn_ribbons',
|
|
||||||
Flag('world_ribbon'),
|
|
||||||
Flag('earth_ribbon'),
|
|
||||||
Flag('national_ribbon'),
|
|
||||||
Flag('country_ribbon'),
|
|
||||||
Flag('sky_ribbon'),
|
|
||||||
Flag('land_ribbon'),
|
|
||||||
Flag('marine_ribbon'),
|
|
||||||
Flag('effort_ribbon'),
|
|
||||||
Flag('artist_ribbon'),
|
|
||||||
Flag('victory_ribbon'),
|
|
||||||
Flag('winning_ribbon'),
|
|
||||||
Flag('champion_ribbon'),
|
|
||||||
Flag('tough_ribbon_master'),
|
|
||||||
Flag('tough_ribbon_hyper'),
|
|
||||||
Flag('tough_ribbon_super'),
|
|
||||||
Flag('tough_ribbon'),
|
|
||||||
Flag('smart_ribbon_master'),
|
|
||||||
Flag('smart_ribbon_hyper'),
|
|
||||||
Flag('smart_ribbon_super'),
|
|
||||||
Flag('smart_ribbon'),
|
|
||||||
Flag('cute_ribbon_master'),
|
|
||||||
Flag('cute_ribbon_hyper'),
|
|
||||||
Flag('cute_ribbon_super'),
|
|
||||||
Flag('cute_ribbon'),
|
|
||||||
Flag('beauty_ribbon_master'),
|
|
||||||
Flag('beauty_ribbon_hyper'),
|
|
||||||
Flag('beauty_ribbon_super'),
|
|
||||||
Flag('beauty_ribbon'),
|
|
||||||
Flag('cool_ribbon_master'),
|
|
||||||
Flag('cool_ribbon_hyper'),
|
|
||||||
Flag('cool_ribbon_super'),
|
|
||||||
Flag('cool_ribbon'),
|
|
||||||
),
|
|
||||||
EmbeddedBitStruct(
|
|
||||||
PokemonFormAdapter(BitField('alternate_form', 5)),
|
|
||||||
Enum(BitField('gender', 2),
|
|
||||||
genderless = 2,
|
|
||||||
male = 0,
|
|
||||||
female = 1,
|
|
||||||
),
|
),
|
||||||
Flag('fateful_encounter'),
|
Enum(ULInt8('encounter_type'),
|
||||||
),
|
special = 0, # egg; pal park; event; honey tree; shaymin
|
||||||
BitStruct('shining_leaves',
|
grass = 2, # or darkrai
|
||||||
Padding(2),
|
dialga_palkia = 4,
|
||||||
Flag('crown'),
|
cave = 5, # or giratina or hall of origin
|
||||||
Flag('leaf5'),
|
water = 7,
|
||||||
Flag('leaf4'),
|
building = 9,
|
||||||
Flag('leaf3'),
|
safari_zone = 10, # includes great marsh
|
||||||
Flag('leaf2'),
|
gift = 12, # starter; fossil; ingame trade?
|
||||||
Flag('leaf1'),
|
# distortion_world = ???,
|
||||||
),
|
hgss_gift = 24, # starter; fossil; bebe's eevee (pt only??)
|
||||||
Padding(2),
|
|
||||||
ULInt16('pt_egg_location_id'),
|
|
||||||
ULInt16('pt_met_location_id'),
|
|
||||||
|
|
||||||
# Block C
|
|
||||||
PokemonStringAdapter(String('nickname', 22)),
|
|
||||||
Padding(1),
|
|
||||||
Enum(ULInt8('original_version'),
|
|
||||||
sapphire = 1,
|
|
||||||
ruby = 2,
|
|
||||||
emerald = 3,
|
|
||||||
firered = 4,
|
|
||||||
leafgreen = 5,
|
|
||||||
heartgold = 7,
|
|
||||||
soulsilver = 8,
|
|
||||||
diamond = 10,
|
|
||||||
pearl = 11,
|
|
||||||
platinum = 12,
|
|
||||||
orre = 15,
|
|
||||||
),
|
|
||||||
LittleEndianBitStruct('sinnoh_contest_ribbons',
|
|
||||||
Padding(12),
|
|
||||||
Flag('tough_ribbon_master'),
|
|
||||||
Flag('tough_ribbon_ultra'),
|
|
||||||
Flag('tough_ribbon_great'),
|
|
||||||
Flag('tough_ribbon'),
|
|
||||||
Flag('smart_ribbon_master'),
|
|
||||||
Flag('smart_ribbon_ultra'),
|
|
||||||
Flag('smart_ribbon_great'),
|
|
||||||
Flag('smart_ribbon'),
|
|
||||||
Flag('cute_ribbon_master'),
|
|
||||||
Flag('cute_ribbon_ultra'),
|
|
||||||
Flag('cute_ribbon_great'),
|
|
||||||
Flag('cute_ribbon'),
|
|
||||||
Flag('beauty_ribbon_master'),
|
|
||||||
Flag('beauty_ribbon_ultra'),
|
|
||||||
Flag('beauty_ribbon_great'),
|
|
||||||
Flag('beauty_ribbon'),
|
|
||||||
Flag('cool_ribbon_master'),
|
|
||||||
Flag('cool_ribbon_ultra'),
|
|
||||||
Flag('cool_ribbon_great'),
|
|
||||||
Flag('cool_ribbon'),
|
|
||||||
),
|
|
||||||
Padding(4),
|
|
||||||
|
|
||||||
# Block D
|
|
||||||
PokemonStringAdapter(String('original_trainer_name', 16)),
|
|
||||||
DateAdapter(String('date_egg_received', 3)),
|
|
||||||
DateAdapter(String('date_met', 3)),
|
|
||||||
ULInt16('dp_egg_location_id'),
|
|
||||||
ULInt16('dp_met_location_id'),
|
|
||||||
ULInt8('pokerus'),
|
|
||||||
ULInt8('dppt_pokeball'),
|
|
||||||
EmbeddedBitStruct(
|
|
||||||
Enum(Flag('original_trainer_gender'),
|
|
||||||
male = False,
|
|
||||||
female = True,
|
|
||||||
),
|
),
|
||||||
BitField('met_at_level', 7),
|
ULInt8('hgss_pokeball'),
|
||||||
),
|
Padding(1),
|
||||||
Enum(ULInt8('encounter_type'),
|
)
|
||||||
special = 0, # egg; pal park; event; honey tree; shaymin
|
|
||||||
grass = 2, # or darkrai
|
|
||||||
dialga_palkia = 4,
|
|
||||||
cave = 5, # or giratina or hall of origin
|
|
||||||
water = 7,
|
|
||||||
building = 9,
|
|
||||||
safari_zone = 10, # includes great marsh
|
|
||||||
gift = 12, # starter; fossil; ingame trade?
|
|
||||||
# distortion_world = ???,
|
|
||||||
hgss_gift = 24, # starter; fossil; bebe's eevee (pt only??)
|
|
||||||
),
|
|
||||||
ULInt8('hgss_pokeball'),
|
|
||||||
Padding(1),
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in a new issue