Dump flavor text

This commit is contained in:
Eevee (Lexy Munroe) 2016-09-10 21:23:57 -07:00
parent 50344e6794
commit dd3d3a3f25
2 changed files with 227 additions and 494 deletions

View file

@ -24,7 +24,7 @@ from construct import *
from pokedex.extract.lib.gbz80 import find_code from pokedex.extract.lib.gbz80 import find_code
import pokedex.schema as schema import pokedex.schema as schema
# TODO set this up to colorcode and use {} formatting # TODO set this up to colorcode, probably put that... elsewhere
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# Known official games, the languages they were released in, and hashes of # Known official games, the languages they were released in, and hashes of
@ -495,427 +495,74 @@ def bank(addr):
return bank, addr return bank, addr
EN_TEXT_MAP = { JA_CHARMAP = dict(enumerate([
# Sort of faux movement macros # 0x
0x00: "", # "Start text"? # 00 appears at the beginning of a lot of Pokédex entries?
0x4E: "\n", # Move to next line "", "イ゛", "", "エ゛", "オ゛", "", "", "",
0x49: "\f", # Start a new Pokédex page "", "", "", "", "", "", "", "",
0x5F: ".", # End of Pokédex entry, adds a period # 1x
"", "", "", "", "ナ゛", "ニ゛", "ヌ゛", "ネ゛",
"ノ゛", "", "", "", "", "マ゛", "ミ゛", "ム゛",
# 2x
"ィ゛", "あ゛", "い゛", "", "え゛", "お゛", "", "",
"", "", "", "", "", "", "", "",
# 3x
"", "", "", "", "", "な゛", "に゛", "ぬ゛",
"ね゛", "の゛", "", "", "", "", "", "ま゛",
# 4x
# 4F is the Pokédex newline
# 4E is the dialogue newline (puts the cursor on the bottom line)
"", "", "", "", "", "", "", "",
"", "\f", "が ", "も゜", "<EFBFBD>", "<EFBFBD>", "\n", "\n",
# 5x
# 50 is the string terminator, represented by @ in pokered
# 51 prompts for a button press, then clears the screen
# 52 is the player's name
# 53 is the rival's name
# 55 prompts for a button press, then scrolls up one line
# 57 ends dialogue, invisibly
# 58 ends dialogue, visibly
# 59 is the inactive Pokémon in battle
# 5A is the active Pokémon in battle
# 5F marks the end of a Pokédex entry, which automaticaly adds a period
"@", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "ポケモン", "<EFBFBD>", "……", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "パソコン", "わざマシン", "トレーナー", "ロケットだん", "",
# 6x
"A", "B", "C", "D", "E", "F", "G", "H",
"I", "V", "S", "L", "M", "", "", "",
# 7x
# 79 - 7E are the TL, T/B, TR, L/R, BL, and BR of the text box
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", " ",
# 8x
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
# 9x
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
# Ax
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
# Bx
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
# Cx
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
# Dx
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
# Ex
"", "", "", "", "", "", " ?", " !",
"", "", "", "", "", "", "", "",
# Fx
"", "×", ".", "/", "", "", "0", "1",
"2", "3", "4", "5", "6", "7", "8", "9",
]))
0x05: "", EN_CHARMAP = dict(enumerate([
0x06: "",
0x07: "",
0x08: "",
0x09: "",
0x0A: "",
0x0B: "",
0x0C: "",
0x0D: "",
0x0E: "",
0x0F: "",
0x10: "",
0x11: "",
0x12: "",
0x13: "",
0x19: "",
0x1A: "",
0x1B: "",
0x1C: "",
0x26: "",
0x27: "",
0x28: "",
0x29: "",
0x2A: "",
0x2B: "",
0x2C: "",
0x2D: "",
0x2E: "",
0x2F: "",
0x30: "",
0x31: "",
0x32: "",
0x33: "",
0x34: "",
0x3A: "",
0x3B: "",
0x3C: "",
0x3D: "",
0x3E: "",
0x40: "",
0x41: "",
0x42: "",
0x43: "",
0x44: "",
0x45: "",
0x46: "",
0x47: "",
0x48: "",
0x80: "",
0x81: "",
0x82: "",
0x83: "",
0x84: "",
0x85: "",
0x86: "",
0x87: "",
0x88: "",
0x89: "",
0x8A: "",
0x8B: "",
0x8C: "",
0x8D: "",
0x8E: "",
0x8F: "",
0x90: "",
0x91: "",
0x92: "",
0x93: "",
0x94: "",
0x95: "",
0x96: "",
0x97: "",
0x98: "",
0x99: "",
0x9A: "",
0x9B: "",
0x9C: "",
0x9D: "",
0x9E: "",
0x9F: "",
0xA0: "",
0xA1: "",
0xA2: "",
0xA3: "",
0xA4: "",
0xA5: "",
0xA6: "",
0xA7: "",
0xA8: "",
0xA9: "",
0xAA: "",
0xAB: "",
0xAC: "",
0xAD: "",
0xAE: "",
0xAF: "",
0xB0: "",
0xB1: "",
0xB2: "",
0xB3: "",
0xB4: "",
0xB5: "",
0xB6: "",
0xB7: "",
0xB8: "",
0xB9: "",
0xBA: "",
0xBB: "",
0xBC: "",
0xBD: "",
0xBE: "",
0xBF: "",
0xC0: "",
0xC1: "",
0xC2: "",
0xC3: "",
0xC4: "",
0xC5: "",
0xC6: "",
0xC7: "",
0xC8: "",
0xC9: "",
0xCA: "",
0xCB: "",
0xCC: "",
0xCD: "",
0xCE: "",
0xCF: "",
0xD0: "",
0xD1: "",
0xD2: "",
0xD3: "",
0xD4: "",
0xD5: "",
0xD6: "",
0xD7: "",
0xD8: "",
0xD9: "",
0xDA: "",
0xDB: "",
0xDC: "",
0xDD: "",
0xDE: "",
0xDF: "",
0xE0: "",
0xE1: "",
0xE2: "",
0xE3: "",
0x50: "@",
0x54: "#",
0x54: "POKé",
0x75: "",
0x79: "",
0x7A: "",
0x7B: "",
0x7C: "",
0x7D: "",
0x7E: "",
0x74: "",
0x7F: " ",
0x80: "A",
0x81: "B",
0x82: "C",
0x83: "D",
0x84: "E",
0x85: "F",
0x86: "G",
0x87: "H",
0x88: "I",
0x89: "J",
0x8A: "K",
0x8B: "L",
0x8C: "M",
0x8D: "N",
0x8E: "O",
0x8F: "P",
0x90: "Q",
0x91: "R",
0x92: "S",
0x93: "T",
0x94: "U",
0x95: "V",
0x96: "W",
0x97: "X",
0x98: "Y",
0x99: "Z",
0x9A: "(",
0x9B: ")",
0x9C: ":",
0x9D: ";",
0x9E: "[",
0x9F: "]",
0xA0: "a",
0xA1: "b",
0xA2: "c",
0xA3: "d",
0xA4: "e",
0xA5: "f",
0xA6: "g",
0xA7: "h",
0xA8: "i",
0xA9: "j",
0xAA: "k",
0xAB: "l",
0xAC: "m",
0xAD: "n",
0xAE: "o",
0xAF: "p",
0xB0: "q",
0xB1: "r",
0xB2: "s",
0xB3: "t",
0xB4: "u",
0xB5: "v",
0xB6: "w",
0xB7: "x",
0xB8: "y",
0xB9: "z",
0xBA: "é",
0xBB: "'d",
0xBC: "'l",
0xBD: "'s",
0xBE: "'t",
0xBF: "'v",
0xE0: "'",
0xE3: "-",
0xE4: "'r",
0xE5: "'m",
0xE6: "?",
0xE7: "!",
0xE8: ".",
0xED: "",
0xEF: "",
0xF0: "¥",
0xF1: "×",
0xF3: "/",
0xF4: ",",
0xF5: "",
0xF6: "0",
0xF7: "1",
0xF8: "2",
0xF9: "3",
0xFA: "4",
0xFB: "5",
0xFC: "6",
0xFD: "7",
0xFE: "8",
0xFF: "9",
}
JA_CHARMAP = {
**EN_TEXT_MAP,
0x05: "",
0x06: "",
0x07: "",
0x08: "",
0x09: "",
0x0A: "",
0x0B: "",
0x0C: "",
0x0D: "",
0x0E: "",
0x0F: "",
0x10: "",
0x11: "",
0x12: "",
0x13: "",
0x19: "",
0x1A: "",
0x1B: "",
0x1C: "",
0x26: "",
0x27: "",
0x28: "",
0x29: "",
0x2A: "",
0x2B: "",
0x2C: "",
0x2D: "",
0x2E: "",
0x2F: "",
0x30: "",
0x31: "",
0x32: "",
0x33: "",
0x34: "",
0x3A: "",
0x3B: "",
0x3C: "",
0x3D: "",
0x3E: "",
0x40: "",
0x41: "",
0x42: "",
0x43: "",
0x44: "",
0x45: "",
0x46: "",
0x47: "",
0x48: "",
0x80: "",
0x81: "",
0x82: "",
0x83: "",
0x84: "",
0x85: "",
0x86: "",
0x87: "",
0x88: "",
0x89: "",
0x8A: "",
0x8B: "",
0x8C: "",
0x8D: "",
0x8E: "",
0x8F: "",
0x90: "",
0x91: "",
0x92: "",
0x93: "",
0x94: "",
0x95: "",
0x96: "",
0x97: "",
0x98: "",
0x99: "",
0x9A: "",
0x9B: "",
0x9C: "",
0x9D: "",
0x9E: "",
0x9F: "",
0xA0: "",
0xA1: "",
0xA2: "",
0xA3: "",
0xA4: "",
0xA5: "",
0xA6: "",
0xA7: "",
0xA8: "",
0xA9: "",
0xAA: "",
0xAB: "",
0xAC: "",
0xAD: "",
0xAE: "",
0xAF: "",
0xB0: "",
0xB1: "",
0xB2: "",
0xB3: "",
0xB4: "",
0xB5: "",
0xB6: "",
0xB7: "",
0xB8: "",
0xB9: "",
0xBA: "",
0xBB: "",
0xBC: "",
0xBD: "",
0xBE: "",
0xBF: "",
0xC0: "",
0xC1: "",
0xC2: "",
0xC3: "",
0xC4: "",
0xC5: "",
0xC6: "",
0xC7: "",
0xC8: "",
0xC9: "",
0xCA: "",
0xCB: "",
0xCC: "",
0xCD: "",
0xCE: "",
0xCF: "",
0xD0: "",
0xD1: "",
0xD2: "",
0xD3: "",
0xD4: "",
0xD5: "",
0xD6: "",
0xD7: "",
0xD8: "",
0xD9: "",
0xDA: "",
0xDB: "",
0xDC: "",
0xDD: "",
0xDE: "",
0xDF: "",
0xE0: "",
0xE1: "",
0xE2: "",
0xE3: "",
0xE9: "",
}
for n in range(0x100):
if not n in JA_CHARMAP:
JA_CHARMAP[n] = '<EFBFBD>'
# ty, tachyon
DE_FR_TEXT_MAP = dict(enumerate([
# 0x0X # 0x0X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x1X # 0x1X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
@ -926,12 +573,65 @@ DE_FR_TEXT_MAP = dict(enumerate([
# 0x3X # 0x3X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x4X # 4x
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "\f", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "\n", "\n",
# 5x
"@", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "POKé", "<EFBFBD>", "……", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "PC", "TM", "TRAINER", "ROCKET", ".",
# 6x
"A", "B", "C", "D", "E", "F", "G", "H",
"I", "V", "S", "L", "M", ":", "", "",
# 7x
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", " ",
# 8x
"A", "B", "C", "D", "E", "F", "G", "H",
"I", "J", "K", "L", "M", "N", "O", "P",
# 9x
"Q", "R", "S", "T", "U", "V", "W", "X",
"Y", "Z", "(", ")", " :", " ;", "[", "]",
# Ax
"a", "b", "c", "d", "e", "f", "g", "h",
"i", "j", "k", "l", "m", "n", "o", "p",
# Bx
"q", "r", "s", "t", "u", "v", "w", "x",
"y", "z", "é", "ʼd", "ʼl", "ʼs", "ʼt", "ʼv",
# Cx
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x5X # Dx
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# Ex
"'", "ᴾₖ", "ᴹₙ", "-", "ʼr", "ʼm", " ?", " !",
".", "", "", "", "", "", "", "",
# Fx
# F0 is the currency symbol; this is the ruble sign, close enough
"", "×", ".", "/", ",", "", "0", "1",
"2", "3", "4", "5", "6", "7", "8", "9",
]))
# ty, tachyon
DE_FR_CHARMAP = dict(enumerate([
# 0x0X
"", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x1X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x2X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x3X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 4x
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "\f", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "\n", "\n",
# 5x
"@", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "POKé", "<EFBFBD>", "……", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x6X # 0x6X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
@ -957,23 +657,16 @@ DE_FR_TEXT_MAP = dict(enumerate([
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "cʼ", "dʼ", "jʼ", "lʼ", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "cʼ", "dʼ", "jʼ", "lʼ",
"mʼ", "nʼ", "pʼ", "sʼ", "ʼs", "tʼ", "uʼ", "yʼ", "mʼ", "nʼ", "pʼ", "sʼ", "ʼs", "tʼ", "uʼ", "yʼ",
# 0xEX # 0xEX
"'", "P\u200dk", "M\u200dn", "-", "¿", "¡", "?", "!", "'", "ᴾₖ", "ᴹₙ", "-", "¿", "¡", "?", "!",
".", "", "", "", "", "", "", "", ".", "", "", "", "", "", "", "",
# 0xFX # 0xFX
"$", "×", ".", "/", ",", "", "0", "1", "$", "×", ".", "/", ",", "", "0", "1",
"2", "3", "4", "5", "6", "7", "8", "9", "2", "3", "4", "5", "6", "7", "8", "9",
])) ]))
DE_FR_TEXT_MAP.update({
0x00: "", # "Start text"?
0x4E: "\n", # Move to next line
0x49: "\f", # Start a new Pokédex page
0x5F: ".", # End of Pokédex entry, adds a period
0x54: "POKé",
})
ES_IT_CHARMAP = dict(enumerate([ ES_IT_CHARMAP = dict(enumerate([
# 0x0X # 0x0X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x1X # 0x1X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
@ -984,12 +677,12 @@ ES_IT_CHARMAP = dict(enumerate([
# 0x3X # 0x3X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x4X # 4x
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
# 0x5X
"@", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "\f", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "\n", "\n",
# 5x
"@", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "POKé", "<EFBFBD>", "……", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", ".",
# 0x6X # 0x6X
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
@ -1015,19 +708,21 @@ ES_IT_CHARMAP = dict(enumerate([
"ì", "í", "ñ", "ò", "ó", "ú", "º", "&", "ì", "í", "ñ", "ò", "ó", "ú", "º", "&",
"ʼd", "ʼl", "ʼm", "ʼr", "ʼs", "ʼt", "ʼv", " ", "ʼd", "ʼl", "ʼm", "ʼr", "ʼs", "ʼt", "ʼv", " ",
# 0xEX # 0xEX
"'", "P\u200dk", "M\u200dn", "-", "¿", "¡", "?", "!", "'", "ᴾₖ", "ᴹₙ", "-", "¿", "¡", "?", "!",
".", "", "", "", "", "", "", "", ".", "", "", "", "", "", "", "",
# 0xFX # 0xFX
"$", "×", ".", "/", ",", "", "0", "1", "$", "×", ".", "/", ",", "", "0", "1",
"2", "3", "4", "5", "6", "7", "8", "9" "2", "3", "4", "5", "6", "7", "8", "9",
])) ]))
ES_IT_CHARMAP.update({
0x00: "", # "Start text"? CHARACTER_MAPS = dict(
0x4E: "\n", # Move to next line ja=JA_CHARMAP,
0x49: "\f", # Start a new Pokédex page en=EN_CHARMAP,
0x5F: ".", # End of Pokédex entry, adds a period es=ES_IT_CHARMAP,
0x54: "POKé", it=ES_IT_CHARMAP,
}) de=DE_FR_CHARMAP,
fr=DE_FR_CHARMAP,
)
class PokemonString: class PokemonString:
@ -1036,15 +731,9 @@ class PokemonString:
self.raw = raw self.raw = raw
def decrypt(self, language): def decrypt(self, language):
if language == 'ja': try:
charmap = JA_CHARMAP charmap = CHARACTER_MAPS[language]
elif language == 'en': except KeyError:
charmap = EN_TEXT_MAP
elif language in ('es', 'it'):
charmap = ES_IT_CHARMAP
elif language in ('de', 'fr'):
charmap = DE_FR_TEXT_MAP
else:
raise ValueError("Not a known language: {!r}".format(language)) raise ValueError("Not a known language: {!r}".format(language))
return ''.join( return ''.join(
@ -1069,6 +758,36 @@ class PokemonCString(Adapter):
return PokemonString(obj) return PokemonString(obj)
class MacroPokemonCString(Construct):
"""Similar to the above, but for strings that may contain the 0x17 "far
load" macro, whose parameters may in turn contain the NUL byte 0x50 without
marking the end of the string. Yikes."""
def _parse(self, stream, context):
buf = bytearray()
while True:
byte, = stream.read(1)
if byte == 0x17:
# "Far load"
addr_lo, addr_hi, bank = stream.read(3)
offset = unbank(bank, addr_lo + addr_hi * 256)
pos = stream.tell()
try:
stream.seek(offset)
buf.extend(self._parse(stream, context).raw)
finally:
stream.seek(pos)
elif byte == 0x50:
break
elif byte == 0x5F:
# 5F ends a Pokédex entry
buf.append(byte)
break
else:
buf.append(byte)
return PokemonString(bytes(buf))
class NullTerminatedArray(Subconstruct): class NullTerminatedArray(Subconstruct):
_peeker = Peek(ULInt8('___')) _peeker = Peek(ULInt8('___'))
__slots__ = () __slots__ = ()
@ -1217,34 +936,24 @@ evos_moves_pointer = Struct(
Pointer(lambda ctx: ctx.offset + (0xE - 1) * 0x4000, evos_moves_struct), Pointer(lambda ctx: ctx.offset + (0xE - 1) * 0x4000, evos_moves_struct),
) )
pokedex_flavor_struct = Struct( # There are actually two versions of this struct... an imperial one, used by
'pokedex_flavor', # the US, and a metric one, used by the ENTIRE REST OF THE WORLD. The imperial
PokemonCString('species'), # one has separate bytes for feet and inches, so it's a different size, making
# TODO HA HA FUCK ME, SOME GAMES USE METRIC SOME (OK JUST THE US) USE IMPERIAL # it completely incompatible.
#ULInt8('height_feet'), pokedex_flavor_struct_metric = Struct(
#ULInt8('height_inches'), 'pokedex_flavor_metric',
#ULInt16('weight_pounds'), PokemonCString('genus'),
ULInt8('height_decimeters'), ULInt8('height_decimeters'),
ULInt16('weight_hectograms'), ULInt16('weight_hectograms'),
# This appears to technically be a string containing a single macro, for MacroPokemonCString('flavor_text'),
# "load other string from this address", but it always takes this same form
# so there's no need to actually evaluate it.
Const(ULInt8('macro'), 0x17), # 0x17 is the "far" macro
ULInt16('address'),
ULInt8('bank'),
Const(ULInt8('nul'), 0x50), # faux nul marking the end of the string
Pointer(
lambda ctx: ctx.address + (ctx.bank - 1) * 0x4000,
PokemonCString('flavor_text'),
),
) )
# TODO this works very awkwardly as a struct pokedex_flavor_struct_imperial = Struct(
pokedex_flavor_pointer = Struct( 'pokedex_flavor_imperial',
'xxx', PokemonCString('genus'),
ULInt16('offset'), ULInt8('height_feet'),
# TODO hardcoded 0x10, same bank ULInt8('height_inches'),
# TODO this has to be on-demand because missingno's struct is actually bogus! ULInt16('weight_pounds'),
OnDemandPointer(lambda ctx: ctx.offset + (0x10 - 1) * 0x4000, pokedex_flavor_struct), MacroPokemonCString('flavor_text'),
) )
@ -1267,6 +976,7 @@ class RBYCart:
self.addrs = self.detect_addresses() self.addrs = self.detect_addresses()
self.game, self.language = self.detect_game() self.game, self.language = self.detect_game()
self.uses_metric = not self.language == 'en'
# And snag this before anything else happens; prevents some silly # And snag this before anything else happens; prevents some silly
# problems where a reified property seeks, then tries to read this, and # problems where a reified property seeks, then tries to read this, and
@ -1639,13 +1349,13 @@ class RBYCart:
# Warn about a potentially bad checksum # Warn about a potentially bad checksum
if not game_c or not language_c: if not game_c or not language_c:
log.warn( log.warn(
"Hmm. I don't recognize the checksum for {}, but I'll " "Hmm. I don't recognize the checksum for %s, but I'll "
"continue anyway.", "continue anyway.",
self.path.name) self.path.name)
elif game_c != game_h or language_c != language_h: elif game_c != game_h or language_c != language_h:
log.warn( log.warn(
"This is very surprising. The checksum indicates that this " "This is very surprising. The checksum indicates that this "
"game should be {}, {}, but I detected it as {}, {}. Probably " "game should be %s, %s, but I detected it as %s, %s. Probably "
"my fault, not yours. Continuing anyway.", "my fault, not yours. Continuing anyway.",
game_c, language_c, game_h, language_h) game_c, language_c, game_h, language_h)
@ -1854,25 +1564,24 @@ class RBYCart:
def pokedex_entries(self): def pokedex_entries(self):
"""List of pokedex_flavor_structs.""" """List of pokedex_flavor_structs."""
ret = [None] * self.NUM_POKEMON ret = [None] * self.NUM_POKEMON
dex_bank, _ = bank(self.addrs['PokedexEntryPointers'])
if self.uses_metric:
pokedex_flavor_struct = pokedex_flavor_struct_metric
else:
pokedex_flavor_struct = pokedex_flavor_struct_imperial
self.stream.seek(self.addrs['PokedexEntryPointers']) self.stream.seek(self.addrs['PokedexEntryPointers'])
for index, pointer in enumerate(Array(self.max_pokemon_index, pokedex_flavor_pointer).parse_stream(self.stream), start=1): # This address is just an array of pointers
for index, address in enumerate(Array(self.max_pokemon_index, ULInt16('pointer')).parse_stream(self.stream), start=1):
try: try:
id = self.pokedex_order[index] id = self.pokedex_order[index]
except KeyError: except KeyError:
continue continue
ret[id] = pointer.pokedex_flavor.value self.stream.seek(unbank(dex_bank, address))
ret[id] = pokedex_flavor_struct.parse_stream(self.stream)
record = pokemon_records_by_internal[index] return ret
pokedex_flavor = pointer.pokedex_flavor.value
# TODO FUCKKKK IMPERIALLLLL
#record.height = pokedex_flavor.height_feet * 12 + pokedex_flavor.height_inches
#record.weight = pokedex_flavor.weight_pounds
record.height = pokedex_flavor.height_decimeters
record.weight = pokedex_flavor.weight_hectograms
record.species = pokedex_flavor.species.decrypt(language)
record.flavor_text = pokedex_flavor.flavor_text.decrypt(language)
@reify @reify
def move_names(self): def move_names(self):
@ -2013,6 +1722,24 @@ def main(base_root):
evolutions.append(evo) evolutions.append(evo)
writer.evolutions = evolutions writer.evolutions = evolutions
# Pokédex flavor
flavor_struct = cart.pokedex_entries[id]
# TODO LOLLLL
if 'genus' not in writer.__dict__:
writer.genus = {}
writer.genus[cart.language] = flavor_struct.genus.decrypt(language)
if 'flavor_text' not in writer.__dict__:
writer.flavor_text = {}
writer.flavor_text[cart.language] = flavor_struct.flavor_text.decrypt(language)
if cart.uses_metric:
writer.height = 1000 * flavor_struct.height_decimeters
writer.weight = 100000000 * flavor_struct.weight_hectograms
else:
writer.height = 254 * (
12 * flavor_struct.height_feet
+ flavor_struct.height_inches)
writer.weight = 453592370 * flavor_struct.weight_pounds
fn = root / 'pokemon.yaml' fn = root / 'pokemon.yaml'
print('Writing', fn) print('Writing', fn)
with fn.open('w') as f: with fn.open('w') as f:

View file

@ -174,12 +174,18 @@ class Pokémon(VersionedLocus):
# TODO family? # TODO family?
evolutions = _List(Evolution) evolutions = _List(Evolution)
species = _Value(str) genus = _Value(str)
flavor_text = _Value(str) flavor_text = _Value(str)
# TODO maybe want little wrapper types that can display as either imperial # TODO maybe want little wrapper types that can display as either imperial
# or metric # or metric
# TODO maybe also dump as metric rather than plain numbers # TODO maybe also dump as metric rather than plain numbers
# Inches and pounds are both defined as exact numbers of centimeters and
# kilograms respectively, so this uses the largest units that can represent
# both metric and imperial values as integers with no loss of precision:
# myriameters (tenths of a millimeter) and micrograms.
# Divide by 100 for centimeters, or by 254 for inches
height = _Value(int) height = _Value(int)
# Divide by one billion for kilograms, or by 453592370 for pounds
weight = _Value(int) weight = _Value(int)
# TODO this belongs to a place, not to a pokemon # TODO this belongs to a place, not to a pokemon