2 u
"""Defines a construct `pokemon_struct`, containing the structure of a single
3 Pokémon saved within a game -- often seen as a .pkm file. This is the same
4 format sent back and forth over the GTS.
9 from construct
import *
12 # - strings should be validated, going both in and out
13 # - strings need to pad themselves when being re-encoded
14 # - strings sometimes need specific padding christ
15 # - date_met is not optional
16 # - some way to be more lenient with junk data, or at least
17 # - higher-level validation; see XXXes below
18 # - personality indirectly influences IVs due to PRNG use
20 # The entire gen 4 character table:
468 # And the reverse dict, used with str.translate()
469 inverse_character_table
= dict()
470 for in_
, out
in character_table
.iteritems():
471 inverse_character_table
[ord(out
)] = in_
474 def LittleEndianBitStruct(*args
):
475 """Construct's bit structs read a byte at a time in the order they appear,
476 reading each bit from most to least significant. Alas, this doesn't work
477 at all for a 32-bit bit field, because the bytes are 'backwards' in
480 So this acts as a bit struct, but reverses the order of bytes before
481 reading/writing, so ALL the bits are read from most to least significant.
485 encoder
=lambda s
: s
[::-1],
486 decoder
=lambda s
: s
[::-1],
490 class PokemonStringAdapter(Adapter
):
491 u
"""Adapter that encodes/decodes Pokémon-formatted text stored in a regular
494 def _decode(self
, obj
, context
):
495 decoded_text
= obj
.decode('utf16')
497 # Real string ends at the \uffff character
498 if u
'\uffff' in decoded_text
:
499 decoded_text
= decoded_text
[0:decoded_text
.index(u
'\uffff')]
500 # XXX save "trash bytes" somewhere..?
502 return decoded_text
.translate(character_table
)
504 def _encode(self
, obj
, context
):
505 #padded_text = (obj + u'\xffff' + '\x00' * 12)
507 decoded_text
= padded_text
.translate(inverse_character_table
)
508 return decoded_text
.encode('utf16')
510 class DateAdapter(Adapter
):
511 """Converts between a three-byte string and a Python date.
513 Only dates in 2000 or later will work!
515 def _decode(self
, obj
, context
):
516 if obj
== '\x00\x00\x00':
519 y
, m
, d
= (ord(byte
) for byte
in obj
)
521 return datetime
.date(y
, m
, d
)
523 def _encode(self
, obj
, context
):
525 return '\x00\x00\x00'
527 y
, m
, d
= obj
.year
- 2000, obj
.month
, obj
.day
528 return ''.join(chr(n
) for n
in (y
, m
, d
))
531 # Docs: http://projectpokemon.org/wiki/Pokemon_NDS_Structure
532 pokemon_struct
= Struct('pokemon_struct',
534 ULInt32('personality'), # XXX aughgh http://bulbapedia.bulbagarden.net/wiki/Personality
536 ULInt16('checksum'), # XXX should be checked or calculated
539 ULInt16('national_id'),
540 ULInt16('held_item_id'),
541 ULInt16('original_trainer_id'),
542 ULInt16('original_trainer_secret_id'),
545 ULInt8('ability_id'), # XXX needs to match personality + species
546 BitStruct('markings',
556 ULInt8('original_country'),
566 # XXX sum cannot surpass 510
568 ULInt8('effort_attack'),
569 ULInt8('effort_defense'),
570 ULInt8('effort_speed'),
571 ULInt8('effort_special_attack'),
572 ULInt8('effort_special_defense'),
574 ULInt8('contest_cool'),
575 ULInt8('contest_beauty'),
576 ULInt8('contest_cute'),
577 ULInt8('contest_smart'),
578 ULInt8('contest_tough'),
579 ULInt8('contest_sheen'),
581 LittleEndianBitStruct('sinnoh_ribbons',
583 Flag('premier_ribbon'),
584 Flag('classic_ribbon'),
585 Flag('carnival_ribbon'),
586 Flag('festival_ribbon'),
588 Flag('green_ribbon'),
590 Flag('legend_ribbon'),
591 Flag('history_ribbon'),
592 Flag('record_ribbon'),
593 Flag('footprint_ribbon'),
594 Flag('gorgeous_royal_ribbon'),
595 Flag('royal_ribbon'),
596 Flag('gorgeous_ribbon'),
597 Flag('smile_ribbon'),
598 Flag('snooze_ribbon'),
599 Flag('relax_ribbon'),
600 Flag('careless_ribbon'),
601 Flag('downcast_ribbon'),
602 Flag('shock_ribbon'),
603 Flag('alert_ribbon'),
604 Flag('world_ability_ribbon'),
605 Flag('pair_ability_ribbon'),
606 Flag('multi_ability_ribbon'),
607 Flag('double_ability_ribbon'),
608 Flag('great_ability_ribbon'),
609 Flag('ability_ribbon'),
610 Flag('sinnoh_champ_ribbon'),
622 ULInt8('move1_pp_ups'),
623 ULInt8('move2_pp_ups'),
624 ULInt8('move3_pp_ups'),
625 ULInt8('move4_pp_ups'),
627 LittleEndianBitStruct('ivs',
628 Flag('is_nicknamed'),
630 BitField('iv_special_defense', 5),
631 BitField('iv_special_attack', 5),
632 BitField('iv_speed', 5),
633 BitField('iv_defense', 5),
634 BitField('iv_attack', 5),
635 BitField('iv_hp', 5),
637 LittleEndianBitStruct('hoenn_ribbons',
638 Flag('world_ribbon'),
639 Flag('earth_ribbon'),
640 Flag('national_ribbon'),
641 Flag('country_ribbon'),
644 Flag('marine_ribbon'),
645 Flag('effort_ribbon'),
646 Flag('artist_ribbon'),
647 Flag('victory_ribbon'),
648 Flag('winning_ribbon'),
649 Flag('champion_ribbon'),
650 Flag('tough_ribbon_master'),
651 Flag('tough_ribbon_hyper'),
652 Flag('tough_ribbon_super'),
653 Flag('tough_ribbon'),
654 Flag('smart_ribbon_master'),
655 Flag('smart_ribbon_hyper'),
656 Flag('smart_ribbon_super'),
657 Flag('smart_ribbon'),
658 Flag('cute_ribbon_master'),
659 Flag('cute_ribbon_hyper'),
660 Flag('cute_ribbon_super'),
662 Flag('beauty_ribbon_master'),
663 Flag('beauty_ribbon_hyper'),
664 Flag('beauty_ribbon_super'),
665 Flag('beauty_ribbon'),
666 Flag('cool_ribbon_master'),
667 Flag('cool_ribbon_hyper'),
668 Flag('cool_ribbon_super'),
672 BitField('alternate_form', 5),
673 Enum(BitField('gender', 2),
678 Flag('fateful_encounter'),
680 BitStruct('shining_leaves',
690 ULInt16('pt_egg_location_id'),
691 ULInt16('pt_met_location_id'),
694 PokemonStringAdapter(String('nickname', 22)),
696 Enum(ULInt8('original_version'),
709 LittleEndianBitStruct('sinnoh_contest_ribbons',
711 Flag('tough_ribbon_master'),
712 Flag('tough_ribbon_ultra'),
713 Flag('tough_ribbon_great'),
714 Flag('tough_ribbon'),
715 Flag('smart_ribbon_master'),
716 Flag('smart_ribbon_ultra'),
717 Flag('smart_ribbon_great'),
718 Flag('smart_ribbon'),
719 Flag('cute_ribbon_master'),
720 Flag('cute_ribbon_ultra'),
721 Flag('cute_ribbon_great'),
723 Flag('beauty_ribbon_master'),
724 Flag('beauty_ribbon_ultra'),
725 Flag('beauty_ribbon_great'),
726 Flag('beauty_ribbon'),
727 Flag('cool_ribbon_master'),
728 Flag('cool_ribbon_ultra'),
729 Flag('cool_ribbon_great'),
735 PokemonStringAdapter(String('original_trainer_name', 16)),
736 DateAdapter(String('date_egg_received', 3)),
737 DateAdapter(String('date_met', 3)),
738 ULInt16('dp_egg_location_id'),
739 ULInt16('dp_met_location_id'),
741 ULInt8('dppt_pokeball'),
743 Enum(Flag('original_trainer_gender'),
747 BitField('met_at_level', 7),
749 Enum(ULInt8('encounter_type'),
753 cave
= 5, # or hall of origin
759 ULInt8('hgss_pokeball'),