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
))
530 class PokemonFormAdapter(Adapter
):
531 """Converts form ids to form names, and vice versa."""
534 201: 'abcdefghijklmnopqrstuvwxyz!?',
537 386: ['normal', 'attack', 'defense', 'speed'],
540 412: ['plant', 'sandy', 'trash'],
541 413: ['plant', 'sandy', 'trash'],
543 # Shellos and Gastrodon
544 422: ['west', 'east'],
545 423: ['west', 'east'],
548 479: ['normal', 'heat', 'wash', 'frost', 'fan', 'cut'],
551 487: ['altered', 'origin'],
554 492: ['land', 'sky'],
558 'normal', 'fighting', 'flying', 'poison', 'ground', 'rock',
559 'bug', 'ghost', 'steel', 'fire', 'water', 'grass',
560 'thunder', 'psychic', 'ice', 'dragon', 'dark', '???',
564 def _decode(self
, obj
, context
):
566 forms
= self
.pokemon_forms
[ context
['national_id'] ]
570 return forms
[obj
>> 3]
572 def _encode(self
, obj
, context
):
574 forms
= self
.pokemon_forms
[ context
['national_id'] ]
578 return forms
.index(obj
) << 3
583 # Docs: http://projectpokemon.org/wiki/Pokemon_NDS_Structure
584 pokemon_struct
= Struct('pokemon_struct',
586 ULInt32('personality'), # XXX aughgh http://bulbapedia.bulbagarden.net/wiki/Personality
588 ULInt16('checksum'), # XXX should be checked or calculated
591 ULInt16('national_id'),
592 ULInt16('held_item_id'),
593 ULInt16('original_trainer_id'),
594 ULInt16('original_trainer_secret_id'),
597 ULInt8('ability_id'), # XXX needs to match personality + species
598 BitStruct('markings',
608 ULInt8('original_country'),
618 # XXX sum cannot surpass 510
620 ULInt8('effort_attack'),
621 ULInt8('effort_defense'),
622 ULInt8('effort_speed'),
623 ULInt8('effort_special_attack'),
624 ULInt8('effort_special_defense'),
626 ULInt8('contest_cool'),
627 ULInt8('contest_beauty'),
628 ULInt8('contest_cute'),
629 ULInt8('contest_smart'),
630 ULInt8('contest_tough'),
631 ULInt8('contest_sheen'),
633 LittleEndianBitStruct('sinnoh_ribbons',
635 Flag('premier_ribbon'),
636 Flag('classic_ribbon'),
637 Flag('carnival_ribbon'),
638 Flag('festival_ribbon'),
640 Flag('green_ribbon'),
642 Flag('legend_ribbon'),
643 Flag('history_ribbon'),
644 Flag('record_ribbon'),
645 Flag('footprint_ribbon'),
646 Flag('gorgeous_royal_ribbon'),
647 Flag('royal_ribbon'),
648 Flag('gorgeous_ribbon'),
649 Flag('smile_ribbon'),
650 Flag('snooze_ribbon'),
651 Flag('relax_ribbon'),
652 Flag('careless_ribbon'),
653 Flag('downcast_ribbon'),
654 Flag('shock_ribbon'),
655 Flag('alert_ribbon'),
656 Flag('world_ability_ribbon'),
657 Flag('pair_ability_ribbon'),
658 Flag('multi_ability_ribbon'),
659 Flag('double_ability_ribbon'),
660 Flag('great_ability_ribbon'),
661 Flag('ability_ribbon'),
662 Flag('sinnoh_champ_ribbon'),
674 ULInt8('move1_pp_ups'),
675 ULInt8('move2_pp_ups'),
676 ULInt8('move3_pp_ups'),
677 ULInt8('move4_pp_ups'),
679 LittleEndianBitStruct('ivs',
680 Flag('is_nicknamed'),
682 BitField('iv_special_defense', 5),
683 BitField('iv_special_attack', 5),
684 BitField('iv_speed', 5),
685 BitField('iv_defense', 5),
686 BitField('iv_attack', 5),
687 BitField('iv_hp', 5),
689 LittleEndianBitStruct('hoenn_ribbons',
690 Flag('world_ribbon'),
691 Flag('earth_ribbon'),
692 Flag('national_ribbon'),
693 Flag('country_ribbon'),
696 Flag('marine_ribbon'),
697 Flag('effort_ribbon'),
698 Flag('artist_ribbon'),
699 Flag('victory_ribbon'),
700 Flag('winning_ribbon'),
701 Flag('champion_ribbon'),
702 Flag('tough_ribbon_master'),
703 Flag('tough_ribbon_hyper'),
704 Flag('tough_ribbon_super'),
705 Flag('tough_ribbon'),
706 Flag('smart_ribbon_master'),
707 Flag('smart_ribbon_hyper'),
708 Flag('smart_ribbon_super'),
709 Flag('smart_ribbon'),
710 Flag('cute_ribbon_master'),
711 Flag('cute_ribbon_hyper'),
712 Flag('cute_ribbon_super'),
714 Flag('beauty_ribbon_master'),
715 Flag('beauty_ribbon_hyper'),
716 Flag('beauty_ribbon_super'),
717 Flag('beauty_ribbon'),
718 Flag('cool_ribbon_master'),
719 Flag('cool_ribbon_hyper'),
720 Flag('cool_ribbon_super'),
724 PokemonFormAdapter(BitField('alternate_form', 5)),
725 Enum(BitField('gender', 2),
730 Flag('fateful_encounter'),
732 BitStruct('shining_leaves',
742 ULInt16('pt_egg_location_id'),
743 ULInt16('pt_met_location_id'),
746 PokemonStringAdapter(String('nickname', 22)),
748 Enum(ULInt8('original_version'),
761 LittleEndianBitStruct('sinnoh_contest_ribbons',
763 Flag('tough_ribbon_master'),
764 Flag('tough_ribbon_ultra'),
765 Flag('tough_ribbon_great'),
766 Flag('tough_ribbon'),
767 Flag('smart_ribbon_master'),
768 Flag('smart_ribbon_ultra'),
769 Flag('smart_ribbon_great'),
770 Flag('smart_ribbon'),
771 Flag('cute_ribbon_master'),
772 Flag('cute_ribbon_ultra'),
773 Flag('cute_ribbon_great'),
775 Flag('beauty_ribbon_master'),
776 Flag('beauty_ribbon_ultra'),
777 Flag('beauty_ribbon_great'),
778 Flag('beauty_ribbon'),
779 Flag('cool_ribbon_master'),
780 Flag('cool_ribbon_ultra'),
781 Flag('cool_ribbon_great'),
787 PokemonStringAdapter(String('original_trainer_name', 16)),
788 DateAdapter(String('date_egg_received', 3)),
789 DateAdapter(String('date_met', 3)),
790 ULInt16('dp_egg_location_id'),
791 ULInt16('dp_met_location_id'),
793 ULInt8('dppt_pokeball'),
795 Enum(Flag('original_trainer_gender'),
799 BitField('met_at_level', 7),
801 Enum(ULInt8('encounter_type'),
802 special
= 0, # egg; pal park; event; honey tree; shaymin
803 grass
= 2, # or darkrai
805 cave
= 5, # or giratina or hall of origin
808 safari_zone
= 10, # includes great marsh
809 gift
= 12, # starter; fossil; ingame trade?
810 # distortion_world = ???,
811 hgss_gift
= 24, # starter; fossil; bebe's eevee (pt only??)
813 ULInt8('hgss_pokeball'),