parse.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package gen7
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "strings"
  8. "time"
  9. "unicode/utf16"
  10. p "github.com/ajswis/go-pkparse-server/pokemon"
  11. )
  12. type consoleRegion map[uint8]string
  13. // Parse accepts a raw pokemon (byte slice) and either returns a Pokemon struct
  14. // with all applicable fields populated or an error depicting the reason for
  15. // failure.
  16. func Parse(rawPokemon p.RawPokemon) (*p.Pokemon, error) {
  17. if err := validateRawPokemon(rawPokemon); err != nil {
  18. return nil, err
  19. }
  20. consoleRegions := consoleRegion{0x00: "J", 0x01: "U", 0x02: "E", 0x03: "?", 0x04: "C", 0x05: "K", 0x06: "T"}
  21. var pkmn p.Pokemon
  22. pkmn.RawPokemon = &rawPokemon
  23. // Block A
  24. pkmn.EncryptionConstant = binary.LittleEndian.Uint32(rawPokemon[0x00:0x08])
  25. pkmn.PokedexNumber = toUint16(rawPokemon[0x08:0x0a])
  26. pkmn.HeldItemID = toUint16(rawPokemon[0x0a:0x0c])
  27. pkmn.TrainerID = toUint16(rawPokemon[0x0c:0x0e])
  28. pkmn.SecretID = toUint16(rawPokemon[0x0e:0x10])
  29. pkmn.FullTrainerID = binary.LittleEndian.Uint32(rawPokemon[0x0c:0x10])
  30. pkmn.Experience = binary.LittleEndian.Uint32(rawPokemon[0x10:0x15])
  31. pkmn.AbilityID = rawPokemon[0x14]
  32. pkmn.AbilityNum = rawPokemon[0x14]
  33. pkmn.PID = binary.LittleEndian.Uint32(rawPokemon[0x18:0x1c])
  34. pkmn.NatureID = rawPokemon[0x1c]
  35. pkmnGenderByte := rawPokemon[0x1d]
  36. pkmn.FatefulEncounter = (pkmnGenderByte & 0x01) == 1
  37. if (pkmnGenderByte & 0x02) == 0x02 {
  38. pkmn.Gender = "F"
  39. } else if (pkmnGenderByte & 0x04) == 0x04 {
  40. pkmn.Gender = ""
  41. } else {
  42. pkmn.Gender = "M"
  43. }
  44. pkmn.FormID = pkmnGenderByte >> 3
  45. pkmn.HPEV = rawPokemon[0x1e]
  46. pkmn.AtkEV = rawPokemon[0x1f]
  47. pkmn.DefEV = rawPokemon[0x20]
  48. pkmn.SpeEV = rawPokemon[0x21]
  49. pkmn.SpAtkEV = rawPokemon[0x22]
  50. pkmn.SpDefEV = rawPokemon[0x23]
  51. pokerusByte := rawPokemon[0x2b]
  52. pkmn.PokerusDuration = pokerusByte & 15
  53. pkmn.PokerusStrain = pokerusByte >> 4
  54. // Block B
  55. pkmn.RawNickname = rawPokemon[0x40:0x58]
  56. pkmn.Nickname = binaryToUTF16leString(pkmn.RawNickname)
  57. pkmn.Move1ID = toUint16(rawPokemon[0x5a:0x5c])
  58. pkmn.Move2ID = toUint16(rawPokemon[0x5c:0x5e])
  59. pkmn.Move3ID = toUint16(rawPokemon[0x5e:0x60])
  60. pkmn.Move4ID = toUint16(rawPokemon[0x60:0x62])
  61. pkmn.Move1PP = rawPokemon[0x62]
  62. pkmn.Move2PP = rawPokemon[0x63]
  63. pkmn.Move3PP = rawPokemon[0x64]
  64. pkmn.Move4PP = rawPokemon[0x65]
  65. pkmn.Move1PPUsed = rawPokemon[0x66]
  66. pkmn.Move2PPUsed = rawPokemon[0x67]
  67. pkmn.Move3PPUsed = rawPokemon[0x68]
  68. pkmn.Move4PPUsed = rawPokemon[0x69]
  69. pkmn.EggMove1ID = toUint16(rawPokemon[0x6a:0x6c])
  70. pkmn.EggMove2ID = toUint16(rawPokemon[0x6c:0x6e])
  71. pkmn.EggMove3ID = toUint16(rawPokemon[0x6e:0x70])
  72. pkmn.EggMove4ID = toUint16(rawPokemon[0x70:0x72])
  73. ivBytes := binary.LittleEndian.Uint32(rawPokemon[0x74:0x78])
  74. pkmn.HPIV = ivBytes & 0x1f
  75. pkmn.AtkIV = ivBytes >> 5 & 0x1f
  76. pkmn.DefIV = ivBytes >> 10 & 0x1f
  77. pkmn.SpeIV = ivBytes >> 15 & 0x1f
  78. pkmn.SpAtkIV = ivBytes >> 20 & 0x1f
  79. pkmn.SpDefIV = ivBytes >> 25 & 0x1f
  80. pkmn.IsEgg = ((ivBytes >> 30) % 2) == 1
  81. pkmn.IsNicknamed = ((ivBytes >> 31) % 2) == 1
  82. // Block C
  83. pkmn.RawNotOTName = rawPokemon[0x78:0x90]
  84. pkmn.NotOTName = binaryToUTF16leString(pkmn.RawNotOTName)
  85. if rawPokemon[0x92] == 1 {
  86. pkmn.NotOTGender = "F"
  87. } else {
  88. pkmn.NotOTGender = "M"
  89. }
  90. pkmn.CurrentHandlerIsOT = rawPokemon[0x93] == 1
  91. pkmn.GeoLocation1RegionID = rawPokemon[0x94]
  92. pkmn.GeoLocation1CountryID = rawPokemon[0x95]
  93. pkmn.GeoLocation2RegionID = rawPokemon[0x96]
  94. pkmn.GeoLocation2CountryID = rawPokemon[0x97]
  95. pkmn.GeoLocation3RegionID = rawPokemon[0x98]
  96. pkmn.GeoLocation3CountryID = rawPokemon[0x99]
  97. pkmn.GeoLocation4RegionID = rawPokemon[0x9a]
  98. pkmn.GeoLocation4CountryID = rawPokemon[0x9b]
  99. pkmn.GeoLocation5RegionID = rawPokemon[0x9c]
  100. pkmn.GeoLocation5CountryID = rawPokemon[0x9d]
  101. pkmn.NotOTFriendship = rawPokemon[0xa2]
  102. pkmn.NotOTAffection = rawPokemon[0xa3]
  103. pkmn.NotOTMemoryIntensity = rawPokemon[0xa4]
  104. pkmn.NotOTMemoryLine = rawPokemon[0xa5]
  105. pkmn.NotOTMemoryFeeling = rawPokemon[0xa6]
  106. pkmn.NotOTMemoryTextVar = toUint16(rawPokemon[0xa8:0xaa])
  107. pkmn.Fullness = rawPokemon[0xae]
  108. pkmn.Enjoyment = rawPokemon[0xaf]
  109. // Block D
  110. pkmn.RawOTName = rawPokemon[0xb0:0xc8]
  111. pkmn.OTName = binaryToUTF16leString(pkmn.RawOTName)
  112. pkmn.OTFriendship = rawPokemon[0xca]
  113. pkmn.OTAffection = rawPokemon[0xcb]
  114. pkmn.OTMemoryIntensity = rawPokemon[0xcc]
  115. pkmn.OTMemoryLine = rawPokemon[0xcd]
  116. pkmn.OTMemoryTextVar = toUint16(rawPokemon[0xce:0xd0])
  117. pkmn.OTMemoryFeeling = rawPokemon[0xd0]
  118. pkmn.DateEggReceived = uint16ToDatetime(binary.LittleEndian.Uint32(rawPokemon[0xd0:0xd4]))
  119. pkmn.DateMet = uint16ToDatetime(binary.LittleEndian.Uint32(rawPokemon[0xd4:0xd8]))
  120. pkmn.EggLocationID = toUint16(rawPokemon[0xd8:0xda])
  121. pkmn.MetLocationID = toUint16(rawPokemon[0xda:0xdc])
  122. pkmn.PokeballID = rawPokemon[0xdc]
  123. pkmn.LevelMet = rawPokemon[0xdd] & 0x7f
  124. if otGenderBit := rawPokemon[0xdd] >> 7; otGenderBit == 1 {
  125. pkmn.OTGender = "F"
  126. } else {
  127. pkmn.OTGender = "M"
  128. }
  129. pkmn.Gen4EncounterTypeID = rawPokemon[0xde]
  130. pkmn.OTGameID = rawPokemon[0xdf]
  131. pkmn.CountryID = rawPokemon[0xe0]
  132. pkmn.RegionID = rawPokemon[0xe1]
  133. pkmn.RawConsoleRegion = rawPokemon[0xe2]
  134. pkmn.ConsoleRegion = consoleRegions[pkmn.RawConsoleRegion]
  135. pkmn.OTLanguageID = rawPokemon[0xe3]
  136. return &pkmn, nil
  137. }
  138. func binaryToUTF16leString(b []byte) string {
  139. r := bytes.NewReader(b)
  140. u16 := make([]uint16, len(b)/2)
  141. binary.Read(r, binary.LittleEndian, &u16)
  142. runes := utf16.Decode(u16)
  143. s := strings.Split(string(runes), "\u0000")[0]
  144. return s
  145. }
  146. func calculateChecksum(b []byte) uint16 {
  147. var sum uint16
  148. for i := 8; i < 232; i += 2 {
  149. sum += toUint16(b[i : i+2])
  150. }
  151. return sum & 0xffff
  152. }
  153. func toUint16(b []byte) uint16 {
  154. return binary.LittleEndian.Uint16(b)
  155. }
  156. func validateRawPokemon(rawPokemon p.RawPokemon) error {
  157. if l := len(rawPokemon); l != 232 && l != 260 {
  158. return fmt.Errorf("Invalid length for generation 7 pokemon: %d bytes", l)
  159. }
  160. checksumValue := toUint16(rawPokemon[0x06:0x08])
  161. if calculatedChecksum := calculateChecksum(rawPokemon); calculatedChecksum != checksumValue {
  162. return errors.New("Invalid checksum for generation 7 pokemon")
  163. }
  164. if toUint16(rawPokemon[0x04:0x06]) == 1 || rawPokemon[0x58] == 1 || rawPokemon[0x90] == 1 || rawPokemon[0xc8] == 1 {
  165. return errors.New("Invalid bytes supplied for generation 7 pokemon")
  166. }
  167. return nil
  168. }
  169. func uint16ToDatetime(i uint32) int64 {
  170. if i == 0 {
  171. return 0
  172. }
  173. year := int((i & 0xff) + 2000)
  174. month := time.Month(int((i >> 8) & 0xff))
  175. day := int((i >> 16) & 0xff)
  176. date := time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
  177. // *1000 to return milliseconds
  178. return date.Unix() * 1000
  179. }