From 96cc6286646fcee8ee77af1c8d7db6a81d61a366 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 1 Aug 2023 15:27:35 +0000 Subject: [PATCH] tools/parse: add parsers for pokemon dex data --- tools/parse/generic/label_data.py | 38 +++++++++++++++++++ tools/parse/pokemon/base_stats.py | 3 +- tools/parse/pokemon/dex_entries.py | 60 ++++++++++++++++++++++++++++++ tools/parse/pokemon/dex_order.py | 17 +++++++++ tools/parse/pokemon/dex_text.py | 0 tools/parse/pokemon/evos_moves.py | 56 +++++----------------------- 6 files changed, 125 insertions(+), 49 deletions(-) create mode 100644 tools/parse/generic/label_data.py create mode 100644 tools/parse/pokemon/dex_entries.py create mode 100644 tools/parse/pokemon/dex_order.py create mode 100644 tools/parse/pokemon/dex_text.py diff --git a/tools/parse/generic/label_data.py b/tools/parse/generic/label_data.py new file mode 100644 index 0000000..3950e3f --- /dev/null +++ b/tools/parse/generic/label_data.py @@ -0,0 +1,38 @@ +from itertools import chain + +def is_label(token_line): + return token_line[0].endswith(':') + +def is_data(token_line): + return token_line[0] == 'db' or token_line[0] == 'dw' \ + or token_line[0] == 'text_far' + +Label = object() +Data = object() + +def _event(token_line): + if is_label(token_line): + label0, = token_line + yield Label, label0.split(':')[0] + elif is_data(token_line): + _, args = token_line + yield Data, args + else: + return + +def event(tokens): + return list(chain.from_iterable(map(_event, tokens))) + +def pointer_table(type_args, ix): + pointer_table = [] + while ix < len(type_args): + type, args = type_args[ix] + if type is Label: + break + elif type is Data: + evos_moves_label, = args + pointer_table.append(evos_moves_label) + else: + assert False, type_args + ix += 1 + return ix, pointer_table diff --git a/tools/parse/pokemon/base_stats.py b/tools/parse/pokemon/base_stats.py index f37bbf7..9ee2423 100644 --- a/tools/parse/pokemon/base_stats.py +++ b/tools/parse/pokemon/base_stats.py @@ -57,7 +57,6 @@ def parse_base_stat(lines): tmhm, ) - def parse(path): with open(path) as f: token_lines = filter_lines(lines(f.read().split('\n'))) @@ -66,5 +65,5 @@ def parse(path): def parse_all(prefix): base_path = prefix / 'data/pokemon/base_stats' paths = [p for p in base_path.iterdir() if p.is_file()] - # order is pokedex order, not constant order + # in pokedex order return [parse(path) for path in paths] diff --git a/tools/parse/pokemon/dex_entries.py b/tools/parse/pokemon/dex_entries.py new file mode 100644 index 0000000..5ffba90 --- /dev/null +++ b/tools/parse/pokemon/dex_entries.py @@ -0,0 +1,60 @@ +import builtins +from dataclasses import dataclass + +from parse.generic import tokenize +from parse.generic import number +from parse.generic import string +from parse.generic import label_data + +@dataclass +class DexEntry: + label: str + species: str + height: tuple[int, int] + weight: int + text_ptr: str + +def dex_entry(type_args, ix): + label, species, height, weight, text_ptr = type_args[ix:ix+5] + + label_type, label_value = label + assert label_type == label_data.Label, label + species_type, (species_value,) = species + assert species_type == label_data.Data, species_type + height_type, height_value = height + assert height_type == label_data.Data, height_type + weight_type, (weight_value,) = weight + assert weight_type == label_data.Data, weight_type + text_ptr_type, (text_ptr_value,) = text_ptr + assert text_ptr_type == label_data.Data, text_ptr_type + + entry = DexEntry( + label_value, + string.parse(species_value).rstrip('@'), + tuple(map(number.parse, height_value)), + number.parse(weight_value), + text_ptr_value + ) + return ix+5, entry + +def build_tables(tokens): + type_args = label_data.event(tokens) + ix = 0 + while ix < len(type_args): + type, args = type_args[ix] + if type is label_data.Label: + label = args + assert builtins.type(label) is str + if label == 'PokedexEntryPointers': + ix, pointer_table = label_data.pointer_table(type_args, ix + 1) + yield pointer_table + else: + ix, entry = dex_entry(type_args, ix) + yield entry + else: + assert False, (type, args) + +def parse(prefix): + path = prefix / "data/pokemon/dex_entries.asm" + with open(path) as f: + return list(build_tables(tokenize.lines(f.read().split('\n')))) diff --git a/tools/parse/pokemon/dex_order.py b/tools/parse/pokemon/dex_order.py new file mode 100644 index 0000000..5a082e5 --- /dev/null +++ b/tools/parse/pokemon/dex_order.py @@ -0,0 +1,17 @@ +from functools import partial +from parse.generic import tokenize + +tokenize_lines = partial(tokenize.lines, prefix='db') + +def flatten(tokens): + for t in tokens: + if t[0] == 'db': + _, (name,) = t + yield name + else: + assert False, t + +def parse(prefix): + path = prefix / "data/pokemon/dex_order.asm" + with open(path) as f: + return list(flatten(tokenize_lines(f.read().split('\n')))) diff --git a/tools/parse/pokemon/dex_text.py b/tools/parse/pokemon/dex_text.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/parse/pokemon/evos_moves.py b/tools/parse/pokemon/evos_moves.py index a029c9b..4230c8a 100644 --- a/tools/parse/pokemon/evos_moves.py +++ b/tools/parse/pokemon/evos_moves.py @@ -1,30 +1,9 @@ import builtins from dataclasses import dataclass -from itertools import chain from parse.generic import tokenize from parse.generic import number - -lines = tokenize.lines - -def is_label(token_line): - return token_line[0].endswith(':') - -def is_data(token_line): - return token_line[0] == 'db' or token_line[0] == 'dw' - -Label = object() -Data = object() - -def event(token_line): - if is_label(token_line): - label0, = token_line - yield Label, label0.split(':')[0] - elif is_data(token_line): - _, args = token_line - yield Data, args - else: - return +from parse.generic import label_data @dataclass class EvosMoves: @@ -54,37 +33,24 @@ def parse_ev(tokens): else: assert False, ev_type -def parse_pointer_table(type_args, ix): - pointer_table = [] - while ix < len(type_args): - type, args = type_args[ix] - if type is Label: - break - elif type is Data: - evos_moves_label, = args - pointer_table.append(evos_moves_label) - else: - assert False, type_args - ix += 1 - return ix, pointer_table - def parse_learnset_entry(args): level_requirement, move_name = args return number.parse(level_requirement), move_name def build_tables(tokens): evos_moves = EvosMoves() - pointer_table = [] - type_args = list(chain.from_iterable(map(event, tokens))) + type_args = label_data.event(tokens) ix = 0 while ix < len(type_args): type, args = type_args[ix] - if type is Label: + if type is label_data.Label: label = args assert builtins.type(label) is str if label == 'EvosMovesPointerTable': - ix, pointer_table = parse_pointer_table(type_args, ix + 1) + ix, pointer_table = label_data.pointer_table(type_args, ix + 1) + yield pointer_table + continue if evos_moves.label is not None: yield evos_moves evos_moves = EvosMoves() @@ -92,7 +58,7 @@ def build_tables(tokens): assert evos_moves.evolutions == [], evos_moves assert evos_moves.learnset == [], evos_moves evos_moves.label = label - elif type is Data: + elif type is label_data.Data: if is_evolution(args): evos_moves.evolutions.append(parse_ev(args)) elif is_learnset_entry(args): @@ -101,17 +67,13 @@ def build_tables(tokens): pass # do nothing else: assert False, (type, args) + else: assert False, (type, args) ix += 1 - def parse(prefix): path = prefix / "data/pokemon/evos_moves.asm" with open(path) as f: - return list(build_tables(lines(f.read().split('\n')))) - -from pathlib import Path -from pprint import pprint -pprint(parse(Path("../pokered"))) + return list(build_tables(tokenize.lines(f.read().split('\n'))))