generate/text: added
Of the "trivial" texts that the current parser can handle, these are now being inserted in obj_events.
This commit is contained in:
parent
a56def6074
commit
e4ff1b4c5a
2
Makefile
2
Makefile
@ -11,6 +11,8 @@ GEN_SRC += gen/map_objects.cpp
|
||||
GEN_SRC += gen/sprites.cpp
|
||||
GEN_SRC += gen/tilesets.cpp
|
||||
GEN_SRC += gen/collision_tile_ids.cpp
|
||||
GEN_SRC += gen/text.cpp
|
||||
GEN_SRC += gen/text_pointers.cpp
|
||||
|
||||
SRC =
|
||||
SRC += $(GEN_SRC)
|
||||
|
16
control.hpp
16
control.hpp
@ -3,13 +3,14 @@
|
||||
#include <stdint.h>
|
||||
|
||||
struct control_t {
|
||||
static constexpr uint8_t next = 0x80;
|
||||
static constexpr uint8_t line = 0x81;
|
||||
static constexpr uint8_t para = 0x82;
|
||||
static constexpr uint8_t cont = 0x83;
|
||||
static constexpr uint8_t done = 0x84;
|
||||
static constexpr uint8_t prompt = 0x85;
|
||||
static constexpr uint8_t page = 0x86;
|
||||
static constexpr uint8_t text = 0x80;
|
||||
static constexpr uint8_t next = 0x81;
|
||||
static constexpr uint8_t line = 0x82;
|
||||
static constexpr uint8_t para = 0x83;
|
||||
static constexpr uint8_t cont = 0x84;
|
||||
static constexpr uint8_t done = 0x85;
|
||||
static constexpr uint8_t prompt = 0x86;
|
||||
static constexpr uint8_t page = 0x87;
|
||||
};
|
||||
|
||||
struct ligatures_t {
|
||||
@ -24,6 +25,7 @@ struct ligatures_t {
|
||||
|
||||
struct extended_t {
|
||||
static constexpr uint8_t jpy = 0xa5; // ¥
|
||||
static constexpr uint8_t colon_sm = 0xa6; // (small ':' symbol)
|
||||
static constexpr uint8_t e = 0xe9; // é
|
||||
static constexpr uint8_t ellipsis = 0xa8; // …
|
||||
static constexpr uint8_t pk = 0xb2; // ᴾₖ
|
||||
|
@ -3,6 +3,8 @@ from generate import map_objects
|
||||
from generate import sprites
|
||||
from generate import tilesets
|
||||
from generate import collision_tile_ids
|
||||
from generate import text
|
||||
from generate import text_pointers
|
||||
|
||||
files = [
|
||||
(maps.generate_header, "maps.hpp"),
|
||||
@ -15,4 +17,8 @@ files = [
|
||||
(sprites.generate_source, "sprites.cpp"),
|
||||
(collision_tile_ids.generate_header, "collision_tile_ids.hpp"),
|
||||
(collision_tile_ids.generate_source, "collision_tile_ids.cpp"),
|
||||
(text.generate_header, "text.hpp"),
|
||||
(text.generate_source, "text.cpp"),
|
||||
(text_pointers.generate_header, "text_pointers.hpp"),
|
||||
(text_pointers.generate_source, "text_pointers.cpp"),
|
||||
]
|
||||
|
@ -4,13 +4,13 @@ def _render(out, lines):
|
||||
indent = " "
|
||||
level = 0
|
||||
for l in lines:
|
||||
if l and l[0] == "}":
|
||||
if l and (l[0] == "}" or l[0] == ")"):
|
||||
level -= 2
|
||||
assert level >= 0, out.getvalue()
|
||||
|
||||
out.write(indent * level + l + "\n")
|
||||
|
||||
if l and l[-1] == "{":
|
||||
if l and (l[-1] == "{" or l[-1] == "("):
|
||||
level += 2
|
||||
|
||||
if level == 0 and l and l[-1] == ";":
|
||||
|
@ -3,6 +3,7 @@ from parse import parse
|
||||
from generate.generate import renderer
|
||||
from generate.maps import sorted_map_headers
|
||||
from generate.sprites import sprite_name
|
||||
from generate.text_pointers import sorted_text_pointers
|
||||
|
||||
def warp_event(ev):
|
||||
x, y = ev.position
|
||||
@ -31,8 +32,9 @@ def range_or_direction(ev):
|
||||
direction = 'any_dir' # hack?
|
||||
yield f".range_or_direction = object_event_t::range_or_direction::{direction},"
|
||||
|
||||
def object_event(ev):
|
||||
def object_event(ev, sorted_text_constants):
|
||||
x, y = ev.position
|
||||
text_id = '0xff' if ev.text_id not in sorted_text_constants else ev.text_id
|
||||
return [
|
||||
"{",
|
||||
f".type = object_event_t::type::{ev.type},",
|
||||
@ -40,7 +42,7 @@ def object_event(ev):
|
||||
f".sprite_id = spritesheet_t::{sprite_name(ev.sprite_id)},",
|
||||
f".movement = object_event_t::movement::{ev.movement.lower()},",
|
||||
*(range_or_direction(ev)),
|
||||
".text_id = 0,", # fixme
|
||||
f".text_id = {text_id},", # fixme
|
||||
".trainer = { 0 },", # fixme
|
||||
"},",
|
||||
]
|
||||
@ -57,10 +59,10 @@ def bg_events(map_name, obj):
|
||||
yield from bg_event(ev)
|
||||
yield "};"
|
||||
|
||||
def object_events(map_name, obj):
|
||||
def object_events(map_name, obj, sorted_text_constants):
|
||||
yield f"const object_event_t {map_name}_object_events[] = {{"
|
||||
for ev in obj.object_events:
|
||||
yield from object_event(ev)
|
||||
yield from object_event(ev, sorted_text_constants)
|
||||
yield "};"
|
||||
|
||||
def object(map_name, obj):
|
||||
@ -80,17 +82,22 @@ def includes_source():
|
||||
yield '#include <cstdint>'
|
||||
yield ""
|
||||
yield '#include "../map_objects.hpp"'
|
||||
yield '#include "text_pointers.hpp"'
|
||||
yield '#include "maps.hpp"'
|
||||
yield ""
|
||||
|
||||
def map_objects():
|
||||
map_headers = list(sorted_map_headers())
|
||||
scripts_list = parse.scripts_list()
|
||||
for map_header in map_headers:
|
||||
map_name = map_header.name2.lower()
|
||||
map_objects = parse.map_objects_list()[map_header.object()]
|
||||
# fixme: hack due to viridanmart
|
||||
text_pointers = scripts_list.get(map_header.text_pointers(), ({}, {}))
|
||||
sorted_text_constants = {k for k, _ in sorted_text_pointers(*text_pointers)}
|
||||
yield from warp_events(map_name, map_objects)
|
||||
yield from bg_events(map_name, map_objects)
|
||||
yield from object_events(map_name, map_objects)
|
||||
yield from object_events(map_name, map_objects, sorted_text_constants)
|
||||
|
||||
yield "const object_t map_objects[] = {"
|
||||
for map_header in map_headers:
|
||||
|
@ -64,6 +64,7 @@ def struct_map_t():
|
||||
*struct_connection_t(),
|
||||
"",
|
||||
"start_size_t blocks;",
|
||||
"start_size_t text_pointers;",
|
||||
"uint32_t width;",
|
||||
"uint32_t height;",
|
||||
"connection_t connections[4];",
|
||||
@ -104,14 +105,19 @@ def connections(map_header):
|
||||
yield f".map = map_t::{connection.map_name2.lower()},"
|
||||
yield f".offset = {connection.offset},"
|
||||
yield "},"
|
||||
|
||||
|
||||
def map(map_header):
|
||||
|
||||
|
||||
block_path = parse.maps_blocks_list()[map_header.blocks()]
|
||||
map_constant = parse.map_constants_list()[map_header.name2]
|
||||
return [
|
||||
f"[map_t::{map_header.name2.lower()}] = {{",
|
||||
".blocks = {",
|
||||
*start_size_value(block_path),
|
||||
"},",
|
||||
".text_pointers = {",
|
||||
|
||||
"},",
|
||||
f".width = {map_constant.width},",
|
||||
f".height = {map_constant.height},",
|
||||
|
145
tools/generate/text.py
Normal file
145
tools/generate/text.py
Normal file
@ -0,0 +1,145 @@
|
||||
"""
|
||||
const uint8_t string[] = "foo" "\x0a" "abc";
|
||||
"""
|
||||
|
||||
from itertools import chain
|
||||
from parse import parse
|
||||
from generate.generate import renderer
|
||||
|
||||
_control_encode = {
|
||||
'text_end': None,
|
||||
'text_ram': None,
|
||||
'text_start': None,
|
||||
'text_decimal': None,
|
||||
'text_bcd': None,
|
||||
'text' : 0x80,
|
||||
'next' : 0x81,
|
||||
'line' : 0x82,
|
||||
'para' : 0x83,
|
||||
'cont' : 0x84,
|
||||
'done' : 0x85,
|
||||
'prompt' : 0x86,
|
||||
'page' : 0x87,
|
||||
}
|
||||
|
||||
_bracketed_encode = {
|
||||
'PKMN' : '\\x96',
|
||||
'COLON' : '\\xa6',
|
||||
# fixme hacks
|
||||
'PLAYER': 'PLAYER',
|
||||
'RIVAL' : 'RIVAL',
|
||||
'TARGET': 'TARGET',
|
||||
'USER' : 'USER',
|
||||
'SCROLL': 'SCROLL', # _ContTextNoPause
|
||||
'_CONT' : '_CONT', # _ContText
|
||||
}
|
||||
|
||||
def encode_bracketed(bracketed):
|
||||
# see control.hpp
|
||||
assert bracketed in _bracketed_encode, bracketed
|
||||
return _bracketed_encode[bracketed]
|
||||
|
||||
def parse_bracketed(string):
|
||||
chars = []
|
||||
in_bracket = False
|
||||
for c in string:
|
||||
if c == '<':
|
||||
assert in_bracket is False
|
||||
in_bracket = True
|
||||
if chars:
|
||||
yield ''.join(chars)
|
||||
chars = []
|
||||
elif c == '>':
|
||||
assert in_bracket is True
|
||||
in_bracket = False
|
||||
yield encode_bracketed(''.join(chars))
|
||||
chars = []
|
||||
elif ord(c) == 165:
|
||||
if chars:
|
||||
yield ''.join(chars)
|
||||
chars = []
|
||||
yield '\\xa5'
|
||||
else:
|
||||
chars.append(c)
|
||||
if chars:
|
||||
yield ''.join(chars)
|
||||
|
||||
def encode_control(control):
|
||||
assert control in _control_encode, control
|
||||
encode = _control_encode[control]
|
||||
if encode is not None:
|
||||
return f'\\x{encode:x}'
|
||||
else:
|
||||
return None
|
||||
|
||||
def quote(s):
|
||||
return f'"{s}"'
|
||||
|
||||
def generate_literals(text):
|
||||
for control__value in text:
|
||||
if len(control__value) == 1:
|
||||
control, = control__value
|
||||
ec = encode_control(control)
|
||||
if ec is not None:
|
||||
yield ec
|
||||
elif len(control__value) == 2:
|
||||
control, value = control__value
|
||||
ec = encode_control(control)
|
||||
if ec is None:
|
||||
continue
|
||||
yield ec
|
||||
yield from parse_bracketed(value)
|
||||
else:
|
||||
assert False, control__value
|
||||
|
||||
def generate_text(name, text):
|
||||
yield f"const uint8_t {name}[] = ("
|
||||
for literal in generate_literals(text):
|
||||
yield quote(literal)
|
||||
yield ");"
|
||||
|
||||
def calculate_length(text):
|
||||
return sum(
|
||||
1 if literal[0:2] == '\\x' else len(literal)
|
||||
for literal in generate_literals(text)
|
||||
)
|
||||
|
||||
def texts_header():
|
||||
text_list = chain.from_iterable(
|
||||
d.items() for d in parse.text_list())
|
||||
|
||||
for name, text in text_list:
|
||||
length = calculate_length(text) + 1
|
||||
yield f"extern const uint8_t {name}[{length}];"
|
||||
|
||||
def includes_header():
|
||||
yield "#pragma once"
|
||||
yield ""
|
||||
yield "#include <cstdint>"
|
||||
yield ""
|
||||
|
||||
def texts_source():
|
||||
text_list = chain.from_iterable(
|
||||
d.items() for d in parse.text_list())
|
||||
|
||||
for name, text in text_list:
|
||||
yield from generate_text(name, text)
|
||||
yield ""
|
||||
|
||||
def generate_header():
|
||||
render, out = renderer()
|
||||
render(includes_header());
|
||||
render(texts_header())
|
||||
return out
|
||||
|
||||
def includes_source():
|
||||
yield "#include <cstdint>"
|
||||
yield ""
|
||||
yield '#include "text.hpp"'
|
||||
yield ""
|
||||
|
||||
def generate_source():
|
||||
render, out = renderer()
|
||||
render(includes_source());
|
||||
render(texts_source())
|
||||
return out
|
90
tools/generate/text_pointers.py
Normal file
90
tools/generate/text_pointers.py
Normal file
@ -0,0 +1,90 @@
|
||||
from operator import itemgetter
|
||||
from parse import parse
|
||||
from generate.generate import renderer
|
||||
|
||||
"""
|
||||
parse.scripts_list() == {
|
||||
'PalletTown_TextPointers':
|
||||
({'TEXT_PALLETTOWN_FISHER': 'PalletTownFisherText',
|
||||
'TEXT_PALLETTOWN_GIRL': 'PalletTownGirlText',
|
||||
'TEXT_PALLETTOWN_OAK': 'PalletTownOakText',
|
||||
'TEXT_PALLETTOWN_OAKSLAB_SIGN': 'PalletTownOaksLabSignText',
|
||||
'TEXT_PALLETTOWN_PLAYERSHOUSE_SIGN': 'PalletTownPlayersHouseSignText',
|
||||
'TEXT_PALLETTOWN_RIVALSHOUSE_SIGN': 'PalletTownRivalsHouseSignText',
|
||||
'TEXT_PALLETTOWN_SIGN': 'PalletTownSignText'},
|
||||
{'PalletTownFisherText': '_PalletTownFisherText',
|
||||
'PalletTownGirlText': '_PalletTownGirlText',
|
||||
'PalletTownOaksLabSignText': '_PalletTownOaksLabSignText',
|
||||
'PalletTownPlayersHouseSignText': '_PalletTownPlayersHouseSignText',
|
||||
'PalletTownRivalsHouseSignText': '_PalletTownRivalsHouseSignText',
|
||||
'PalletTownSignText': '_PalletTownSignText'}),
|
||||
}
|
||||
"""
|
||||
|
||||
def labels_by_constant(constants, labels):
|
||||
# fixme: removes constants with no (parsed) label
|
||||
for text_name, label_name in constants.items():
|
||||
if label_name in labels:
|
||||
yield text_name, labels[label_name]
|
||||
|
||||
def sorted_text_pointers(constants, labels):
|
||||
return sorted(labels_by_constant(constants, labels), key=itemgetter(0))
|
||||
|
||||
def text_constant_enum(name, constants, labels):
|
||||
yield f"enum {name} {{"
|
||||
for i, (k, _) in enumerate(sorted_text_pointers(constants, labels)):
|
||||
yield f"{k} = {i},"
|
||||
yield "};"
|
||||
|
||||
def header_includes():
|
||||
yield "#pragma once"
|
||||
yield ""
|
||||
|
||||
def text_constant_enums():
|
||||
map_text_pointers = parse.scripts_list()
|
||||
for name, (constants, labels) in map_text_pointers.items():
|
||||
yield from text_constant_enum(name, constants, labels)
|
||||
|
||||
def extern_text_pointer(name, constants, labels):
|
||||
pointers = list(sorted_text_pointers(constants, labels))
|
||||
yield f"extern const start_size_t {name}[{len(pointers)}];"
|
||||
|
||||
def extern_text_pointers():
|
||||
map_text_pointers = parse.scripts_list()
|
||||
for name, (constants, labels) in map_text_pointers.items():
|
||||
yield from extern_text_pointer(name, constants, labels)
|
||||
|
||||
def generate_header():
|
||||
render, out = renderer()
|
||||
render(header_includes())
|
||||
render(text_constant_enums())
|
||||
render(extern_text_pointers())
|
||||
return out
|
||||
|
||||
def text_pointer(name, constants, labels):
|
||||
yield f"const start_size_t {name}[] = {{"
|
||||
pointers = list(sorted_text_pointers(constants, labels))
|
||||
for constant, label in pointers:
|
||||
yield f"[{constant}] = {{"
|
||||
yield f".start = &{label}[0],"
|
||||
yield f".size = (sizeof ({label})),"
|
||||
yield "},"
|
||||
yield "};"
|
||||
|
||||
def text_pointers():
|
||||
map_text_pointers = parse.scripts_list()
|
||||
for name, (constants, labels) in map_text_pointers.items():
|
||||
yield from text_pointer(name, constants, labels)
|
||||
|
||||
def source_includes():
|
||||
yield '#include "../start_size.hpp"'
|
||||
yield ""
|
||||
yield '#include "text_pointers.hpp"'
|
||||
yield '#include "text.hpp"'
|
||||
yield ""
|
||||
|
||||
def generate_source():
|
||||
render, out = renderer()
|
||||
render(source_includes())
|
||||
render(text_pointers())
|
||||
return out
|
@ -1,6 +1,6 @@
|
||||
def skip_whitespace(lines):
|
||||
i = 0
|
||||
while lines[i:] and not lines[i].strip():
|
||||
while lines[i:] and (not lines[i].strip() or lines[i][0] == ';'):
|
||||
i += 1
|
||||
return lines[i:]
|
||||
|
||||
|
@ -16,6 +16,9 @@ from parse import gfx_sprites
|
||||
from parse import spritesheets
|
||||
from parse import sprite_constants
|
||||
|
||||
from parse import text
|
||||
from parse import scripts
|
||||
|
||||
prefix = Path(sys.argv[1])
|
||||
|
||||
def memoize(f):
|
||||
@ -51,3 +54,5 @@ spritesheets_list = memoize(lambda: spritesheets.parse(prefix))
|
||||
sprite_constants_list = memoize(lambda: sprite_constants.parse(prefix))
|
||||
|
||||
# text
|
||||
scripts_list = memoize(lambda: scripts.parse_all(prefix))
|
||||
text_list = memoize(lambda: text.parse_all(prefix))
|
||||
|
@ -1,3 +1,5 @@
|
||||
from itertools import chain
|
||||
|
||||
from parse.line import next_line, skip_whitespace
|
||||
|
||||
def parse_label(lines):
|
||||
@ -6,7 +8,7 @@ def parse_label(lines):
|
||||
name = line.removesuffix('::')
|
||||
return lines, name
|
||||
|
||||
string_tokens = {"text", "cont", "para", "line"}
|
||||
string_tokens = {"text", "cont", "para", "line", "next"}
|
||||
|
||||
def parse_string(line):
|
||||
line = line.strip()
|
||||
@ -42,18 +44,29 @@ def parse_body(lines):
|
||||
value, = rest
|
||||
body.append((type, parse_args(value)))
|
||||
else:
|
||||
# hack hack; some texts don't have a control word at the end
|
||||
# _MoveNameText
|
||||
if line.endswith('::'):
|
||||
return [line] + lines, body
|
||||
assert False, line
|
||||
|
||||
return lines, body
|
||||
|
||||
def tokenize_text(lines):
|
||||
lines, name = parse_label(lines)
|
||||
# fixme: hack
|
||||
if name == '_CableClubNPCLinkClosedBecauseOfInactivityText':
|
||||
return None
|
||||
lines, body = parse_body(lines)
|
||||
return lines, (name, body)
|
||||
|
||||
def tokenize(lines):
|
||||
while lines:
|
||||
lines, tokens = tokenize_text(lines)
|
||||
lines__tokens = tokenize_text(lines)
|
||||
if lines__tokens is None:
|
||||
# fixme: hack9000
|
||||
return
|
||||
lines, tokens = lines__tokens
|
||||
lines = skip_whitespace(lines)
|
||||
yield tokens
|
||||
|
||||
@ -65,11 +78,9 @@ def parse(path):
|
||||
return d
|
||||
|
||||
def parse_all(prefix):
|
||||
base_path = prefix / 'text'
|
||||
paths = [p for p in base_path.iterdir() if p.is_file()]
|
||||
return [parse(path) for path in paths]
|
||||
|
||||
import sys
|
||||
from pprint import pprint
|
||||
from pathlib import Path
|
||||
pprint(parse_all(Path(sys.argv[1])))
|
||||
base_path0 = prefix / 'text'
|
||||
paths0 = [p for p in base_path0.iterdir() if p.is_file()]
|
||||
base_path1 = prefix / 'data/text'
|
||||
paths1 = [p for p in base_path1.iterdir()
|
||||
if p.is_file() and p.stem.startswith('text_')]
|
||||
return [parse(path) for path in chain(paths0, paths1)]
|
||||
|
Loading…
x
Reference in New Issue
Block a user