diff --git a/.gitignore b/.gitignore index d81ec85..7450615 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,16 @@ applications/PoP/PoP1_DOS_music/ # R1 — рабочая копия roomtest для экспериментов пользователя, в репозиторий # не идёт (сама roomtest и есть версируемая ветка разработки). applications/PoP/R1/ + +# SprPoP — автономное приложение. В репозиторий идут исходники, генерируемые +# заголовки (gen/) и упакованные ресурсы (assets/packed/), потому что без них +# из чистого клона ничего не собрать. НЕ идут: +# assets/orig/ оригинальные данные игры — копирайт (SDLPoP, MSDOS, +# записи музыки DOS-версии); откуда их взять, +# написано в assets/orig/README.md +# assets/packed/LEVELS/ уровни res20xx.bin ложатся на диск прямо из +# оригинала, конвертации нет — то есть это тот же +# копирайтный материал, только в раскладке диска +# build/ уже покрыт общим правилом build/ выше +applications/SprPoP/assets/orig/ +applications/SprPoP/assets/packed/LEVELS/ diff --git a/Makefile b/Makefile index f08ac14..71d1fe6 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,9 @@ floppy: tests examples tests/seek/big.txt # Модульные тесты под ucsim_z80. Обвязка — testkit/, сами наборы лежат # рядом с кодом, который проверяют. MAME не нужна, идут за секунды; # ucsim идёт в комплекте нашего SDCC. -HOST_TEST_DIRS := testkit applications/PoP/roomtest/tests-host +# applications/PoP/roomtest заморожена (её ветка развития — SprPoP), поэтому +# её набор здесь больше не гоняется. +HOST_TEST_DIRS := testkit applications/SprPoP/tests/host host-tests: @for d in $(HOST_TEST_DIRS); do $(MAKE) -C $$d || exit 1; done diff --git a/app.mk b/app.mk index 6a52c9f..bd142ad 100644 --- a/app.mk +++ b/app.mk @@ -36,17 +36,37 @@ LIB := $(PROJ_ROOT)/lib/sprinter.lib MAME_DIR := $(PROJ_ROOT)/mame/v306 FLOPPY_IMG := $(MAME_DIR)/IMG/mc.img -HDD_IMG := $(MAME_DIR)/IMG/test_hdd.chd +# ?= — приложение со своим каталогом выхода (applications/SprPoP) держит +# образ у себя и связывает его с MAME символьной ссылкой. +HDD_IMG ?= $(MAME_DIR)/IMG/test_hdd.chd MAKE_DISK := $(MAME_DIR)/make_disk.py MAKE_HDD := $(PROJ_ROOT)/toolchain/make_hdd.sh RUN_MAME := $(MAME_DIR)/run_mame.sh # Optional knobs — see top of file. MEMORY ?= tiny -SOURCES := $(EXAMPLE).c $(EXTRA_SRCS) + +# SRC_DIR / BUILD_DIR — раскладка приложения, которое НЕ держит исходники и +# выхлоп в одной папке с Makefile (applications/SprPoP: src/ и build/). По +# умолчанию обе пусты, то есть всё как было: ./$(EXAMPLE).c → ./$(EXAMPLE).exe. +# Пустое значение обрабатывается отдельной веткой намеренно: "./prog.exe" и +# "prog.exe" — разные имена целей, и склеивать префикс безусловно нельзя. +SRC_DIR ?= +BUILD_DIR ?= +ifeq ($(strip $(SRC_DIR)),) +MAIN_SRC := $(EXAMPLE).c +else +MAIN_SRC := $(SRC_DIR)/$(EXAMPLE).c +endif +ifeq ($(strip $(BUILD_DIR)),) +EXE := $(EXAMPLE).exe +else +EXE := $(BUILD_DIR)/$(EXAMPLE).exe +endif +SOURCES := $(MAIN_SRC) $(EXTRA_SRCS) # Аргументы упаковщика HDD. Обычно это exe и EXTRA_DATA; приложение со # своей раскладкой каталогов может переопределить переменную до include. -HDD_PACK_ARGS ?= $(EXAMPLE).exe $(EXTRA_DATA) +HDD_PACK_ARGS ?= $(EXE) $(EXTRA_DATA) CC_FLAGS := --memory $(MEMORY) ifneq ($(STACK_SIZE),) @@ -54,14 +74,15 @@ CC_FLAGS += --stack-size $(STACK_SIZE) endif CC_FLAGS += $(EXTRA_FLAGS) -all: $(EXAMPLE).exe +all: $(EXE) # runtime/*.s (crt0-семейство, bank.s, heap.s) собираются per-build # внутри sprinter-cc — без этой зависимости их правка не перелинкует # уже собранный exe (кусало: фикс bank.s не подхватился). RUNTIME_DEPS := $(wildcard $(PROJ_ROOT)/runtime/*.s) -$(EXAMPLE).exe: $(SOURCES) $(MKEXE) $(LIB) $(RUNTIME_DEPS) +$(EXE): $(SOURCES) $(MKEXE) $(LIB) $(RUNTIME_DEPS) + $(if $(strip $(BUILD_DIR)),@mkdir -p $(dir $@)) $(SPRINTER_CC) $(CC_FLAGS) -o $@ $(SOURCES) $(MKEXE): @@ -76,13 +97,13 @@ $(LIB): $(MAKE) -C $(PROJ_ROOT)/libbgi clean: - rm -rf .sprinter-cc-* $(EXAMPLE).exe + rm -rf $(if $(strip $(BUILD_DIR)),$(BUILD_DIR),.sprinter-cc-* $(EXE)) # `make floppy` packs ONLY this program (+ optional EXTRA_DATA files) into # the MAME floppy image, replacing whatever was there. Handy for trying a # single program without rebuilding everything. -floppy: $(EXAMPLE).exe - python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(EXTRA_DATA) +floppy: $(EXE) + python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXE) $(EXTRA_DATA) @echo @echo "Floppy ready: $(FLOPPY_IMG) (with $(EXAMPLE).exe$(if $(EXTRA_DATA), + $(EXTRA_DATA))) " @echo "Run: cd $(MAME_DIR) && ./run_mame.sh" @@ -94,7 +115,7 @@ run: floppy # HDD image mounted as disk D: (-hard2 test_hdd.chd). Гораздо быстрее FDD — # используется MCP-мостом к MAME (run_bridge.sh). После пересборки образа # MAME ОБЯЗАН полный рестарт (chdman -f = новый inode; см. memory). -hdd: $(EXAMPLE).exe +hdd: $(EXE) $(MAKE_HDD) $(HDD_IMG) $(HDD_PACK_ARGS) @echo @echo "HDD (D:) ready: $(HDD_IMG) (with $(EXAMPLE).exe$(if $(EXTRA_DATA), + $(EXTRA_DATA)))" diff --git a/applications/SprPoP/CLAUDE.md b/applications/SprPoP/CLAUDE.md new file mode 100644 index 0000000..bf6e083 --- /dev/null +++ b/applications/SprPoP/CLAUDE.md @@ -0,0 +1,179 @@ +# SprPoP — правила приложения + +Порт Prince of Persia (DOS/Apple II) на Sprinter Sp2000 поверх нашего +sprinter-cc / libc / libbgi. Действуют правила корневого `CLAUDE.md` +(сборка, libc, ABI, MAME-автотест); ниже — только специфика порта. +Общение и комментарии — на русском. + +Приложение **автономно**: код, оригинальные данные, конверторы и сборка +лежат внутри этой папки. Наружу оно знает ровно один путь — корень +тулчейна, `SPRINTER_ROOT` (по умолчанию `../..`). + +История: SprPoP выделен из `applications/PoP/roomtest` 2026-08-26. Та +папка ЗАМОРОЖЕНА и остаётся архивом (закрытые задачи и баги с разбором +корней — `../PoP/roomtest/TASKS_CLOSED.md` и `BUGS_CLOSED.md`; там же +исполненные планы в `../PoP/docs/`). Разработка идёт только здесь. + +## Главное правило: SDLPoP — источник истины. Сначала читай, потом кодь + +**`assets/orig/SDLPoP/src/` (github.com/NagyD/SDLPoP, GPLv3) — ЕДИНСТВЕННЫЙ +авторитетный источник того, как оригинальный движок это делает.** Правило +без исключений: + +1. **Перед реализацией ЛЮБОЙ функции** (движение, коллизия, окклюзия, + падение, loose-полы, стражники, отрисовка, тайминги, любые числовые + константы) — СНАЧАЛА найди и прочитай соответствующий код в SDLPoP и + портируй по нему. Не пиши по памяти, не выводи логику «из общих + соображений», не угадывай значения — это источник багов, которые потом + ловятся в MAME часами. +2. **По любому вопросу «как в оригинале должно быть»** (что окклюдит что, + в каком порядке слои, когда меняется тайл, какая скорость/задержка, что + делает такой-то кадр анимации) — ответ ищи в SDLPoP, а не строй гипотезу. + Если не нашёл — это повод копать дальше в исходнике, а не додумывать. +3. Расхождение нашей реализации с SDLPoP — по умолчанию **баг у нас**, пока + не доказано обратное (наша платформа/ABI требует отличия — тогда явно + зафиксировать почему в комментарии **и записью в `docs/impl_diff.md`**: + что делает оригинал, что делаем мы, чем платим, что проверять при + регрессе). + +Карта сегментов: `seg005` control-диспетчер, `seg006` play_kid/коллизия/ +seqtbl, `seg007` mob/loose/падающие объекты, `seg008` отрисовка тайлов/ +слои/окклюзия, `seg009` чтение ресурсов. Слои окклюзии у нас = слои SDLPoP. +См. memory `pop_check_sdlpop_first`. + +Вторичные референсы (когда в SDLPoP непонятно или нужен другой ракурс) +лежат вне приложения, в `../PoP/`: `Prince-of-Persia-Apple-II/` — +оригинальный 6502-исходник 1989 (Мехнер), `PR/` — Princed Resources, +`mininim/` — независимая реализация. Все они — **справочник логики, +структур и констант**, но НЕ код для копирования (лицензии несовместимы, +наш ABI другой): читаем и переписываем под наш движок. + +## Доски + +**Что в работе сейчас — `docs/TASKS_OPEN.md`** (приоритеты, критерии +готовности). Баги — `docs/BUGS_OPEN.md` (только ОТКРЫТЫЕ). + +Правило разнесения: как только задача или баг закрыт — запись целиком +переезжает в архив roomtest (`../PoP/roomtest/TASKS_CLOSED.md` / +`BUGS_CLOSED.md`), а в открытом файле остаётся ссылка. Перед заведением +нового бага грепни архив по симптому: корень мог уже разбираться. +Открытые файлы обязаны читаться целиком за раз. + +Индекс документации с отметками актуальности — **`docs/README.md`**, +начинать чтение оттуда. + +## Сборка и запуск + +``` +make собрать build/sprpop.exe и разложить build/ +make hdd + образ build/hdd/sprpop.chd +make mame-link однократно: подставить образ в MAME (см. ниже) +make -C tests/host модульные тесты движка под ucsim_z80 (секунды, без MAME) +make resources перегенерировать ресурсы из assets/orig/ +make music-mp3 музыка из другого набора (flac|mp3|ogg|mt32) +make clean снести build/ (ассеты не трогает) +make distclean clean + снести assets/packed/ (вернуть — make resources) +``` + +`MEMORY=huge`, `--gfx 256`, 11 банков кода в W3. Отладочные ключи сборки: +`make LEVEL=9` (стартовый уровень), `make ROOM=15 POS=2` (стартовая комната +и тайл `row*10+col`), `make PROF=1` (профиль фаз кадра полосами бордюра), +`make ALLOCS=100000` (плотная упаковка регистров — для замеров размера). +Сравнивать занятость банков можно **только при одном `ALLOCS`**. + +**Ловушка `make mame-link`:** он подменяет `mame/v306/IMG/test_hdd.chd` +символьной ссылкой на наш образ. Пока ссылка стоит, `make hdd` любого +другого приложения (в т.ч. замороженной roomtest) писал бы через неё в +наш `build/hdd/` — если понадобится собрать чужой образ, ссылку сначала +убрать. + +## Раскладка папки + +| Папка | Что | +|---|---| +| `src/` | рукописный C: главный цикл `sprpop.c`, холодная половина `sprpop_cold.c`, движок `pop_*.c` | +| `gen/` | генерируемые заголовки (индексы архивов, таблицы кадров, шрифт, палитры). **Руками не править** — их печатают упаковщики; лежат в репозитории, потому что без них `src/` не собрать | +| `assets/orig/` | оригинальные данные (SDLPoP, MSDOS, записи музыки). Вне репозитория — копирайт; что и откуда взять, написано в `assets/orig/README.md` | +| `assets/packed/` | то, что ложится на диск игры, уже в раскладке диска (`BG/`, `KID/`, …). В репозитории — иначе из чистого клона не собрать | +| `tools/` | конверторы ресурсов. Раскладку путей знает ОДИН файл — `tools/paths.py`; менять пути нужно там, а не в отдельных упаковщиках | +| `docs/` | планы, доски, справочники; `docs/PoP/` — форматы ресурсов оригинала | +| `tests/host/` | модульные тесты движка под ucsim_z80 | +| `build/` | выход: `sprpop.exe`, каталоги ресурсов, `hdd/`, промежуточные `atl/` | + +Музыка собирается из одного из четырёх наборов записей +(`assets/orig/PoP1_DOS_music/`): `flac` по умолчанию, плюс `mp3`, `ogg` и +`mt32` (исполнение Roland MT-32 — звучит иначе, длина треков другая). Набор +входит в имя stamp'а, поэтому `make music-mt32` пересобирает музыку сам, без +`-B`; разовая сборка без смены умолчания — `make MUSIC_FMT=ogg resources-music`. + +Ресурсы: упаковщик пишет промежуточные атласы в `build/atl/<набор>/`, из них +Makefile склеивает архивы прямо в `assets/packed/<КАТАЛОГ>/`, а `make` +копирует их в `build/`. Порядок файлов в списках `*_ATL` Makefile — это +порядок индексов внутри архива, и он же напечатан в `gen/*_arc.h`: менять +порядок нельзя, не перегенерировав заголовок. + +**Каноническая спецификация форматов `.DAT`** — +`docs/PoP/POP-DAT-FormatSpecifications.pdf` (грепаемая копия — `.txt`): +первоисточник Princed для DAT v1.0. Наши разборы +(`docs/PoP/MSDOS_RESOURCE_FORMAT.md`, `docs/PoP/APPLEII_RESOURCE_FORMAT.md`) — +практические заметки; при расхождении источник истины — спецификация. + +## Проверка в MAME + +Только через `toolchain/mame_interactive.py` или MCP-мост `mame-z80` +(memory `mame_autotest`, `mame_mcp_bridge`, `mame_hdd_test_disk`). +Пересобрал образ → MAME ОБЯЗАН полный рестарт (`mame_hdd_rebuild_restart`). + +**Тайминги моста** (не ждать дольше, см. `docs/mame-autotest.md` §10): +старт `run_bridge.sh` → 6 с → `go` → 8 с → `keyseq d:{ENTER}` + +`keyseq sprpop{ENTER}` → 5 с → программа работает. + +Отладочные тумблеры в живой сессии (`src/sprpop.c`): **1** — заморозить +кадр, **2** — продолжить (разбор позы/окклюзии); **ESC** — выход. Читы +(`src/pop_cheat.h`): **K** — убить стража, **I** — бессмертие, +**Shift+L** — следующий уровень, **+/−** — обход комнат (`ROOMNAV`). +Не чит, а штатная клавиша оригинала: **Ctrl+S** — вкл/выкл звук (пурпурная +палочка в борте = звука нет). Целевая раскладка управления, к которой +подгоняем SprPoP, — `docs/keys.txt`. + +Логику, которую можно проверить без железа, покрывать в `tests/host/` +(обвязка — `testkit/`). MAME остаётся для отрисовки, банков, таймингов и +клавиатуры. + +## Модули + +| Файл | Роль (порт SDLPoP) | +|------|--------------------| +| `sprpop.c` | Главный цикл: дабл-буфер (2 страницы + флип на vsync), порядок tick→draw. | +| `sprpop_cold.c` | Холодная половина главного цикла (банк 8): старт уровня, отладочная навигация. | +| `pop_tile.c/.h` | Общие ЛИСТЬЯ слоя фона — РЕЗИДЕНТ W1: блит куска атласа, чтение тайла комнаты, `tile_table`, метка «фон трогали вот здесь» (`pop_cd_touch`). Резидент потому, что их зовут обе половины слоя фона из разных банков, а `const`-таблицы банка из чужого банка не видны. | +| `pop_bg.c/.h` | ГОРЯЧАЯ половина слоя фона (банк 2, каждый кадр): fore-окклюзия поверх ЛЮБОГО персонажа (`seg003` redraw_at_char/char2 — один проход на всех Char), оверлеи кромки, кладка стены (`wall_pattern`), клип. `pop_bg.h` — публичный API ВСЕГО слоя. | +| `pop_room.c` | ХОЛОДНАЯ половина слоя фона (банк 7, раз на комнату/событие): полная отрисовка комнаты (`seg008` draw_tile), точечные перерисовки тайлов (пики, ворота, кнопки, дверь уровня, дрожащие плиты), падающие куски loose (`seg007` mob), загрузка атласов. | +| `_pop_bg.h` | Внутренний контракт между половинами: общее состояние + три тонкие `__banked`-обёртки; там же почему рез прошёл именно здесь и сколько стоит. | +| `pop_kid.c/.h` | Анимация/движение: интерпретатор seqtbl `play_seq` + frame_table (`seg006`), окна `Char` (loadkid/loadshad), атласы Кида. | +| `pop_cdraw.c/.h` | ОТРИСОВКА персонажей — одна на всех Char (порт `add_kid_to_objtable`/`add_guard_to_objtable`): спрайт, clip_char, брызги, клинок, heal; полоса HP и палитры соперника. Там же ПРОПУСК неизменившегося кадра (`pop_char_skip_mask`). | +| `pop_ctrl.c/.h` | Управление: диспетчер `control()` (`seg005`) + ввод `read_user_control` (`seg006`) поверх ``. | +| `pop_map.c/.h` | Коллизия с картой + физика падения/приземления/стены + loose-полы (`seg005/006/007`). | + +## Порядок слоёв в кадре (важно для окклюзии) + +`sprpop.c` каждый тик рисует в СКРЫТУЮ страницу: `pop_char_heal` (стереть +прошлый кадр — по слоту на персонажа) → `kid_tick` → `pop_phys_tick` → +`pop_loose_tick` (loose СЛОЙ ФОНА — ДО персонажей, чтобы они были поверх +плиты) → `pop_char_draw`/`pop_char_fore` для соперника и Кида (кто позже — +тот поверх, порядок задаёт обход тайлов) → `pop_room_clip_borders` (чистка +бортов, гейт по флагу — только в кадрах падения) → флип на vsync. + +**Дабл-буфер:** у каждой страницы своя видео-ОЗУ и теневая ОЗУ-копия; heal +берёт чистый фон из копии ТОЙ страницы, в которую рисуем. Любой +динамический элемент (Kid, loose-плита, падающий кусок) обязан чистить свой +прошлый кадр на КАЖДОЙ из двух страниц — иначе остаток виден через кадр как +**мерцание**. Типовой источник багов «остаётся кусочек» (memory +`pop_fall_debug_baseline`). + +## Ключевые memory + +`pop_port_project`, `pop_check_sdlpop_first`, `pop_banking_architecture`, +`pop_background_strategy`, `pop_kid_plan`, `pop_hang_state`, `pop_fore_layer`, +`pop_fall_debug_baseline`, `accel_vertical_copy`, `kbd_raw_fifo_drain`, +`pop_perf_registry`. diff --git a/applications/SprPoP/Makefile b/applications/SprPoP/Makefile new file mode 100644 index 0000000..063ba96 --- /dev/null +++ b/applications/SprPoP/Makefile @@ -0,0 +1,491 @@ +# SprPoP — порт Prince of Persia на Sprinter Sp2000. +# +# Приложение АВТОНОМНО: весь код, все исходные ресурсы, все конверторы лежат +# здесь. Наружу оно знает ровно один путь — корень тулчейна sprinter-cc: +# +# export SPRINTER_ROOT=/путь/к/C-Compiler +# make +# +# Без переменной берётся ../.. (раскладка applications/SprPoP внутри дерева +# компилятора), так что в родном дереве ничего настраивать не надо. +# +# Раскладка: +# src/ рукописный C +# gen/ генерируемые заголовки (в репозитории: без них src/ не +# собрать, а генератору нужны оригиналы из assets/orig/) +# assets/orig/ оригинальные данные — SDLPoP, MSDOS, записи музыки. +# НЕ в репозитории (копирайт), см. assets/orig/README.md +# assets/packed/ то, что ложится на диск игры, в раскладке диска +# tools/ конверторы ресурсов +# build/ ВЫХОД: sprpop.exe + BG/ KID/ ... + hdd/sprpop.chd +# +# Основные цели: +# make собрать sprpop.exe и разложить build/ +# make hdd + образ build/hdd/sprpop.chd +# make mame-link однократно связать образ с MAME (см. ниже) +# make resources перегенерировать ресурсы из assets/orig/ +# make music-mp3 музыка из другого набора записей (flac|mp3|ogg|mt32) +# make clean снести build/ (ассеты не трогает) +# make distclean clean + снести упакованные ассеты assets/packed/ + +SPRINTER_ROOT ?= $(abspath $(CURDIR)/../..) +PROJ_ROOT := $(SPRINTER_ROOT) +EXAMPLE := sprpop +SRC_DIR := src +BUILD_DIR := build + +# huge: small-раскладка (CODE в W1, DATA в W2) + банки кода в W3. +MEMORY ?= huge +# --max-allocs-per-node: во сколько вариантов размещения регистров упирается +# SDCC на узел. Дефолт sprinter-cc (100000) на модулях такого размера — это +# МИНУТЫ на банк. 6000 — компромисс: сборка дольше секунд на двадцать, зато +# резидент W1 худеет примерно на 150 байт против дефолтных 3000 SDCC, а это +# ровно те байты, которых не хватает куче. +# +# make — обычная сборка (ALLOCS=6000) +# make ALLOCS=100000 — минимальный код, для замеров размера и релиза +# +# ВАЖНО: сравнивать занятость банков можно только при ОДНОМ ALLOCS — иначе +# сравниваются не правки, а уровни оптимизации. +ALLOCS ?= 6000 +# Build info не хранится в C: ID и дата извлекаются из текущего commit. +BUILD_ID ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo dev) +BUILD_DATE ?= $(shell git log -1 --format=%cs 2>/dev/null || echo unknown) +BUILD_FLAGS := -DPOP_BUILD_ID=\"$(BUILD_ID)\" -DPOP_BUILD_DATE=\"$(BUILD_DATE)\" + +# --------------------------------------------------------------------------- +# Банковая раскладка +# --------------------------------------------------------------------------- +# Единственный список банковых исходников: из него строятся И ключи +# sprinter-cc, И зависимости sprpop.exe. Держать два списка нельзя — так уже +# ловили stale-бинарник (модуль компилировался, но make не замечал правок). +# +# Банк 2 = отрисовка (pop_bg + pop_cdraw в соседних банках): вызовы банк→банк +# работают, трамплин сохраняет страницу окна на СТЕКЕ. Резидента --w3 нет. +# Банк 5 = pop_ctrl.c: кандидат выбран не по размеру, а по частоте вызова — +# диспетчер управления дёргается раз в кадр на персонажа и горячих банк→банк +# переходов не создаёт (в отличие от pop_level, чей pop_level_tile зовётся из +# банка 2 на КАЖДЫЙ тайл). +# Банк 9 = оболочка (menu/settings/config/font, автомат, title, attract-demo, +# таблица рекордов) — мелкие редкие точки входа, но не рендеры заставок. +# Банк 10 = единственный владелец экранных палитр/fade; отделён от UI, потому +# что его зовут и игровой lifecycle, и все ветки катсцен. +# Банк 11 = тяжёлая кинематика заставок, чтобы анимации не вытесняли +# menu/config из банка оболочки. +BANK1_SRCS := guards.c +BANK2_SRCS := pop_bg.c +BANK3_SRCS := pop_map.c +BANK4_SRCS := pop_cdraw.c pop_kdraw.c +BANK5_SRCS := pop_ctrl.c pop_tile_cold.c +BANK6_SRCS := pop_trob.c +BANK7_SRCS := pop_room.c pop_redraw.c +BANK8_SRCS := sprpop_cold.c pop_arc.c pop_level_cold.c pop_kboot.c pop_guard_cold.c \ + pop_shadow.c pop_sfx_cold.c pop_qsave.c pop_qsave_coldio.c \ + pop_level_qsave.c +BANK9_SRCS := pop_menu.c pop_status.c pop_music.c pop_config.c pop_app.c pop_timer.c \ + pop_ui.c pop_title.c pop_demo.c pop_flow.c pop_hof.c +BANK10_SRCS := pop_pal.c +BANK11_SRCS := pop_cutscene.c pop_intro.c +BANK_NUMS := 1 2 3 4 5 6 7 8 9 10 11 +BANKED_SRCS := $(foreach n,$(BANK_NUMS),$(BANK$(n)_SRCS)) +BANK_FLAGS := $(foreach n,$(BANK_NUMS),\ + $(foreach f,$(BANK$(n)_SRCS),--bank $(n)=$(SRC_DIR)/$(f))) + +# Модули резидента (кроме sprpop.c — его подставляет app.mk). +EXTRA_SRCS := $(addprefix $(SRC_DIR)/,pop_vflip.c pop_pace.c pop_sfx.c pop_state.c \ + pop_draw.c pop_tile.c pop_kid.c pop_level.c pop_geom.c pop_guard.c \ + pop_qsave_io.c) + +# --------------------------------------------------------------------------- +# Отладочные ключи +# --------------------------------------------------------------------------- +# Профилирование полосами бордюра (PROF() в sprpop.c): каждая фаза кадра красит +# бордюр в свой цвет, высота полосы на скриншоте MAME = время фазы в строках. +PROF ?= 0 +PROF_FLAGS := -DPROF_BORDER=$(PROF) +# Стартовый уровень: make LEVEL=9 — начать сразу с девятого. +LEVEL ?= 1 +PROF_FLAGS += -DFIRST_LEVEL=$(LEVEL) +# Стартовая КОМНАТА и позиция Кида: make ROOM=15 POS=2 — начать прямо в +# целевой комнате, минуя проход уровня. POS — тайл 0..29, то есть row*10+col. +# make ROOM= (пусто) возвращает штатный старт из данных уровня. +ROOM ?= +POS ?= 2 +ifneq ($(strip $(ROOM)),) +PROF_FLAGS += -DDBG_START_ROOM=$(ROOM) -DDBG_START_POS=$(POS) +endif + +EXTRA_FLAGS ?= --gfx 256 -I $(CURDIR)/gen -I $(CURDIR)/$(SRC_DIR) \ + --max-allocs $(ALLOCS) $(BANK_FLAGS) $(PROF_FLAGS) $(BUILD_FLAGS) + +# --------------------------------------------------------------------------- +# Ресурсы: что уходит на диск +# --------------------------------------------------------------------------- +# assets/packed/<КАТАЛОГ>/ повторяет раскладку диска (8.3, как в DSS), поэтому +# отдельного списка «файл → каталог образа» больше нет: каталог виден из пути. +PACKED := assets/packed +ATL := $(BUILD_DIR)/atl + +# Промежуточные атласы (в build/atl, в репозиторий не идут) — из них +# упаковщик архивов склеивает то, что реально ложится на диск. Порядок в +# списках = порядок индексов внутри архива, а их упаковщик печатает в gen/*.h; +# менять порядок нельзя, не перегенерировав заголовок. +BG_SETS := pop pal +BG_PAGES := 0 1 2 3 4 5 6 7 8 9 +BG_ATL := $(foreach s,$(BG_SETS),\ + $(foreach n,$(BG_PAGES),$(ATL)/bg/$(s)_env$(n).atl) \ + $(ATL)/bg/$(s)_wall.atl $(ATL)/bg/$(s)_fore.atl) \ + $(ATL)/bg/pop_pot.atl +# ВСЕ 28 страниц Kid — иначе кадры торможения/разворота/приседа (image>23) не +# рисуются. Плюс зеркальный ПО ВЕРТИКАЛИ набор (зелье инверсии, уровень 9): +# разворот готовит упаковщик, а не рантайм — страница разворачивается 0,59 с +# против 0,045 с на её чтение с диска. +KID_N := 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 +KID_ATL := $(foreach n,$(KID_N),$(ATL)/kid/kid$(n).atl) \ + $(foreach n,$(KID_N),$(ATL)/kid/kid$(n)_v.atl) \ + $(ATL)/kid/sword.atl $(ATL)/kid/sword_v.atl +GUARD_ATL := $(foreach n,0 1 2 3 4,$(ATL)/guard/g$(n).atl) \ + $(foreach n,0 1 2 3 4,$(ATL)/guard/g$(n)_v.atl) +# Скелет — ОТДЕЛЬНЫЙ набор (SKEL.DAT), его грузит уровень 3; кадров меньше. +# Зеркального набора у него нет: зелье инверсии — уровень 9, там обычный страж. +SKEL_ATL := $(foreach n,0 1 2 3,$(ATL)/skel/g$(n).atl) +# Джафар (VIZIER.DAT) — уровень 13; кадров столько же, сколько у стража, но +# палитра своя, из файла набора. +VIZIER_ATL := $(foreach n,0 1 2 3 4,$(ATL)/vizier/g$(n).atl) +# ТЕНЬ — набор, запечённый упаковщиком (наложение спрайта на себя со сдвигом; +# наш блит так не умеет). sk* — кадры вне боя, sf* — кадры 150..189. +SHADOW_ATL := $(foreach n,$(KID_N),$(ATL)/shadow/sk$(n).atl) \ + $(foreach n,0 1 2 3,$(ATL)/shadow/sf$(n).atl) +# Звук: оригинальная оцифровка на частоте CBL (10 937,5 Гц, 8 бит моно). +# Число файлов задаёт упаковщик (POP_SND_PAGES в gen/pop_sound_tbl.h) — +# список ОБЯЗАН совпадать: недостающий файл это молча пропавшие эффекты. +SND_ATL := $(foreach n,0 1 2 3 4 5 6 7 8 9,$(ATL)/sound/s$(n).bin) +TITLE_ATL := $(foreach s,0 1 2 3,$(foreach p,0 1 2 3 4,$(ATL)/title/t$(s)_$(p).atl)) +# PV/INTRO: story и стадии режутся на полосы; у настоящего PV intro есть ещё +# чистый фон b0_* и шесть <=16K actor-атласов — они остаются в EMM на время +# сцены, а не перечитывают полный 320x200 кадр с диска. +PV_ATL := $(foreach s,0 1 2 3 4 5,$(foreach p,0 1 2 3 4,$(ATL)/pv/s$(s)_$(p).atl)) \ + $(foreach s,0 1 2 3 4 5 6 7,$(foreach p,0 1 2 3 4,$(ATL)/pv/p$(s)_$(p).atl)) \ + $(foreach p,0 1 2 3 4,$(ATL)/pv/b0_$(p).atl) \ + $(ATL)/pv/a0.atl $(ATL)/pv/a1.atl $(ATL)/pv/a2.atl \ + $(ATL)/pv/j0.atl $(ATL)/pv/j1.atl $(ATL)/pv/j2.atl \ + $(ATL)/pv/h0.atl $(ATL)/pv/t0.atl + +# МУЗЫКА: записи DOS-версии в формате CBL. Номера — оригинала, чтобы игровой +# код просил трек тем же числом, каким SDLPoP зовёт play_sound. +# заставка 54 55 50 53 52 | катсцены 26 27 35 36 40 +# игра 24 25 28 29 30 32 33 37 39 41 43 | 56 = won (стриминг с диска) +MUS_IDS := 24 25 26 27 28 29 30 32 33 35 36 37 39 40 41 43 50 52 53 54 55 56 +# Набор записей, из которого делается музыка: flac (по умолчанию — лучший +# исходник), mp3, ogg, mt32 (то же в исполнении Roland MT-32). Все четыре +# лежат в assets/orig/PoP1_DOS_music/, имена треков совпадают. +# Формат входит в имя stamp'а, поэтому смена MUSIC_FMT сама по себе делает +# музыку устаревшей — принудительная пересборка не нужна. +MUSIC_FMT ?= flac +# res2000 — attract-demo; res2001..res2014 — штатная игра. res2015 (copy +# protection) намеренно не пакуется и не существует в маршруте загрузчика. +LVL_NUMS := 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 + +# ЕДИНСТВЕННЫЙ список того, что ложится на диск. Из него выводятся и +# staging в build/, и аргументы упаковщика образа. +# Архивы вместо россыпи файлов не ради места: замер 2026-08-25 — `open` 51,4 мс +# против 32,6 мс на чтение 16 КБ, то есть на одних открытиях набор Кида терял +# три секунды. Четыре архива актёров, а не один: имена внутри наборов +# совпадают (g0.atl есть и у стража, и у скелета, и у Джафара), а константы +# индексов упаковщик делает из имени файла. +DISK := BG/bg.arc BG/pop_tile.pal BG/pal_tile.pal \ + KID/kid.arc KID/kid.pal KID/kid.ani \ + GUARD/guard.arc SKEL/skel.arc VIZIER/vizier.arc SHADOW/shadow.arc \ + SND/snd.arc \ + $(foreach i,$(MUS_IDS),MUS/m$(i).bin) \ + FONT/font.atl \ + TITLE/title.arc TITLE/title.pal \ + PV/pv.arc PV/story.pal PV/pv.pal \ + $(foreach n,$(LVL_NUMS),LEVELS/res20$(n).bin) + +# Все шестнадцать генерируемых заголовков перечислены явно, а не wildcard'ом: +# список — это и зависимость exe, и то, что умеет чинить себя само. +GEN_ARC_H := bg_arc.h kid_arc.h guard_arc.h skel_arc.h vizier_arc.h \ + shadow_arc.h title_arc.h pv_arc.h +GEN_H := $(addprefix gen/,$(GEN_ARC_H) pop_bg_atlas.h kid_atlas.h kid_data.h \ + pop_guard_pal.h pop_shadow_atlas.h pop_sound_tbl.h \ + pop_music_tbl.h pop_music_ticks.h pop_font.h) + +PACKED_FILES := $(addprefix $(PACKED)/,$(DISK)) +STAGED := $(addprefix $(BUILD_DIR)/,$(DISK)) + +EXTRA_DATA := $(STAGED) +HDD_PACK_ARGS := $(BUILD_DIR)/$(EXAMPLE).exe \ + $(foreach f,$(DISK),$(word 1,$(subst /, ,$(f))):$(BUILD_DIR)/$(f)) +# Образ живёт в build/, а не в mame/v306/IMG — приложение автономно. Связь с +# MAME — одна символьная ссылка, ставится однократно: make mame-link. +HDD_IMG := $(CURDIR)/$(BUILD_DIR)/hdd/$(EXAMPLE).chd + +include $(PROJ_ROOT)/app.mk + +# --------------------------------------------------------------------------- +# Сборка приложения +# --------------------------------------------------------------------------- +# sprinter-cc собирает все модули одним вызовом и depfile не выдаёт, поэтому +# зависимости перечисляем сами. wildcard по заголовкам намеренно широкий: +# цена лишней полной сборки ниже риска получить stale-бинарник. +HEADERS := $(wildcard $(SRC_DIR)/*.h) +# ОТПЕЧАТОК КЛЮЧЕЙ СБОРКИ. LEVEL/ROOM/POS/PROF/ALLOCS — это -D на входе +# компилятора, а не файлы, и make их «не видит»: после `make LEVEL=14` +# обычный `make` считал бы отладочный бинарь свежим и молча оставил его. +# Поэтому ключи пишутся в файл, и exe зависит от него; меняется строка — +# перелинковка. Файл переписывается ТОЛЬКО при изменении, иначе каждый +# make дёргал бы полную сборку заново. +BUILD_SIG := MEMORY=$(MEMORY) ALLOCS=$(ALLOCS) $(PROF_FLAGS) $(BANK_FLAGS) +BUILD_STAMP := $(BUILD_DIR)/.buildflags +# Отпечаток обновляется НА РАЗБОРЕ Makefile, а не рецептом. Так время файла +# уже верное к моменту, когда make сверяет его с exe — иначе не работает ни +# один из простых вариантов: +# * рецепт с cmp — под `make -n` не выполняется, и make врёт про работу; +# * подпись в имени файла — при ВОЗВРАТЕ к прежним ключам файл уже лежит +# и оказывается старше exe, то есть перелинковки не будет. +# Запись идёт только при отличии, поэтому обычный make ничего не трогает. +# На целях очистки пропускаем, чтобы `make clean` не создавал build/ заново. +ifeq ($(filter clean distclean,$(MAKECMDGOALS)),) +$(shell mkdir -p $(BUILD_DIR); printf '%s\n' '$(BUILD_SIG)' | cmp -s - $(BUILD_STAMP) \ + || printf '%s\n' '$(BUILD_SIG)' > $(BUILD_STAMP)) +endif + +# Цепочка: exe -> gen/*.h -> ресурсы -> упаковщик -> assets/orig (см. ниже). +$(EXE): Makefile $(PROJ_ROOT)/app.mk $(addprefix $(SRC_DIR)/,$(BANKED_SRCS)) \ + $(HEADERS) $(GEN_H) $(BUILD_STAMP) + +# Полная сборка = exe + разложенный build/. +all: stage + +# --------------------------------------------------------------------------- +# staging: assets/packed/ -> build/ +# --------------------------------------------------------------------------- +# build/ обязан быть самодостаточным (его можно снести целиком), поэтому +# ресурсы именно КОПИРУЮТСЯ, а не берутся с диска из assets/. +stage: $(STAGED) + +$(BUILD_DIR)/%: $(PACKED)/% + @mkdir -p $(dir $@) + @cp $< $@ +# --------------------------------------------------------------------------- +# Ресурсы: цепочка зависимостей +# --------------------------------------------------------------------------- +# Направление одно и сквозное: +# +# build/sprpop.exe -> gen/*.h -> архивы и атласы -> упаковщик -> assets/orig/ +# +# Код зависит от генерируемых заголовков, заголовки — от ресурсов, которые они +# описывают (индексы внутри .arc, число страниц звука, таблицы кадров), а те — +# от упаковщика и оригиналов. Поэтому собрать код против устаревших констант +# нельзя: gen/*.h делаются ТЕМ ЖЕ запуском упаковщика, что и сам ресурс, и +# объявлены его целями наравне с ним. +# +# Правила генерации живут в ветке GENERATE=1 и включаются в двух случаях: +# * явная цель `make resources*` — перегенерация с учётом таймстемпов +# (поправил упаковщик — ресурс пересоберётся); +# * какого-то ресурса или заголовка НЕТ на месте (после `make distclean`, +# либо файл снесли руками) — тогда обычный `make` сам уходит в +# рекурсивный вызов и доделывает недостающее вместе с его gen/*.h. +# +# Почему не одни таймстемпы на все случаи: assets/packed/ и gen/ лежат в +# репозитории, а git не сохраняет времена файлов — в свежем клоне упаковщик +# и его выхлоп получают одинаковую метку, и любое сравнение по времени +# превращается в лотерею «перепаковать 6 МБ ресурсов на ровном месте, причём +# на машине, где assets/orig/ может вообще не быть». Отсутствие файла — +# признак однозначный, и именно на нём построена автоматика. +TOOLS := $(CURDIR)/tools +PY := python3 + +ifeq ($(GENERATE),1) +# =========================================================================== +# Ветка генерации +# =========================================================================== +STAMP := $(BUILD_DIR)/.stamps +$(STAMP): + @mkdir -p $@ + +# Упаковщики создают сразу ГРУППУ файлов. Обычное multi-output правило +# заставляет make при `-B` запускать один и тот же упаковщик отдельно для +# каждого выходного файла. Stamp представляет ОДИН запуск генератора; все +# созданные им файлы зависят от stamp без собственных рецептов. +# +# check-orig — order-only: без оригиналов запускать упаковщик бессмысленно, и +# понятное сообщение лучше трейсбека Python. На «устарел ли stamp» он не +# влияет, только выполняется раньше. +define stamp_rule +$(STAMP)/$(1): $(2) | $(STAMP) check-orig + cd $(TOOLS) && $(PY) $(3) + @touch $$@ +endef + +$(eval $(call stamp_rule,bg,$(TOOLS)/pop_pack_bg.py $(TOOLS)/render_room.py $(TOOLS)/paths.py,pop_pack_bg.py dungeon && $(PY) pop_pack_bg.py palace)) +$(eval $(call stamp_rule,kid,$(TOOLS)/pop_pack_kid.py $(TOOLS)/pop_pack_bg.py $(TOOLS)/paths.py,pop_pack_kid.py)) +$(eval $(call stamp_rule,kid-bin,$(TOOLS)/pop_extract_kid_data.py $(TOOLS)/paths.py,pop_extract_kid_data.py)) +$(eval $(call stamp_rule,guard,$(TOOLS)/pop_pack_guard.py $(TOOLS)/paths.py,pop_pack_guard.py)) +$(eval $(call stamp_rule,skel,$(TOOLS)/pop_pack_guard.py $(TOOLS)/paths.py,pop_pack_guard.py SKEL)) +$(eval $(call stamp_rule,vizier,$(TOOLS)/pop_pack_guard.py $(TOOLS)/paths.py,pop_pack_guard.py VIZIER)) +$(eval $(call stamp_rule,shadow,$(TOOLS)/pop_pack_shadow.py $(TOOLS)/pop_pack_bg.py $(TOOLS)/pop_pack_kid.py $(TOOLS)/paths.py,pop_pack_shadow.py)) +$(eval $(call stamp_rule,sound,$(TOOLS)/pop_pack_sound.py $(TOOLS)/paths.py,pop_pack_sound.py)) +$(eval $(call stamp_rule,music-$(MUSIC_FMT),$(TOOLS)/pop_pack_music.py $(TOOLS)/paths.py,pop_pack_music.py --format $(MUSIC_FMT) $(MUS_IDS))) +$(eval $(call stamp_rule,font,$(TOOLS)/pop_extract_font.py $(TOOLS)/paths.py,pop_extract_font.py)) +$(eval $(call stamp_rule,title,$(TOOLS)/pop_pack_title.py $(TOOLS)/paths.py,pop_pack_title.py)) +$(eval $(call stamp_rule,pv,$(TOOLS)/pop_pack_intro.py $(TOOLS)/paths.py,pop_pack_intro.py)) + +# Файл, сделанный упаковщиком, зависит только от stamp. Если stamp свежий, а +# файла нет (снесли руками, либо build/ вычистили) — перезапускаем генератор +# принудительно. Заголовки gen/, которые упаковщик печатает попутно, стоят в +# ТЕХ ЖЕ списках: это и есть «gen зависит от ресурсов, для которых сделан». +define from_stamp +$(1): $(STAMP)/$(2) + @test -f $$@ || { $(MAKE) GENERATE=1 -B $(STAMP)/$(2); test -f $$@; } +endef +$(eval $(call from_stamp,$(BG_ATL) $(ATL)/bg/pop_tile.pal $(ATL)/bg/pal_tile.pal gen/pop_bg_atlas.h,bg)) +$(eval $(call from_stamp,$(KID_ATL) $(ATL)/kid/kid.pal gen/kid_atlas.h,kid)) +$(eval $(call from_stamp,$(ATL)/kid/kid.ani gen/kid_data.h,kid-bin)) +$(eval $(call from_stamp,$(GUARD_ATL) gen/pop_guard_pal.h,guard)) +$(eval $(call from_stamp,$(SKEL_ATL),skel)) +$(eval $(call from_stamp,$(VIZIER_ATL),vizier)) +$(eval $(call from_stamp,$(SHADOW_ATL) gen/pop_shadow_atlas.h,shadow)) +$(eval $(call from_stamp,$(SND_ATL) gen/pop_sound_tbl.h,sound)) +$(eval $(call from_stamp,$(foreach i,$(MUS_IDS),$(ATL)/music/m$(i).bin) gen/pop_music_tbl.h gen/pop_music_ticks.h,music-$(MUSIC_FMT))) +$(eval $(call from_stamp,$(ATL)/font/font.atl gen/pop_font.h,font)) +$(eval $(call from_stamp,$(TITLE_ATL) $(ATL)/title/title.pal,title)) +$(eval $(call from_stamp,$(PV_ATL) $(ATL)/pv/story.pal $(ATL)/pv/pv.pal,pv)) + +# Архивы: собираются ПРЯМО в assets/packed — это уже дисковый артефакт. +# Заголовок с индексами объявлен целью НАРАВНЕ с архивом: индексы описывают +# именно это содержимое и разъехаться с ним не должны. +ARC := $(PY) $(TOOLS)/pop_pack_arc.py +define arc_rule +$(PACKED)/$(1)/$(2).arc gen/$(3)_arc.h: $(4) $(TOOLS)/pop_pack_arc.py + @mkdir -p $(PACKED)/$(1) + $(ARC) --header gen/$(3)_arc.h --prefix $(5) $(PACKED)/$(1)/$(2).arc $(4) +endef +$(eval $(call arc_rule,BG,bg,bg,$(BG_ATL),BG)) +$(eval $(call arc_rule,KID,kid,kid,$(KID_ATL),KID)) +$(eval $(call arc_rule,GUARD,guard,guard,$(GUARD_ATL),GUARD)) +$(eval $(call arc_rule,SKEL,skel,skel,$(SKEL_ATL),SKEL)) +$(eval $(call arc_rule,VIZIER,vizier,vizier,$(VIZIER_ATL),VIZIER)) +$(eval $(call arc_rule,SHADOW,shadow,shadow,$(SHADOW_ATL),SHADOW)) +$(eval $(call arc_rule,TITLE,title,title,$(TITLE_ATL),TITLE)) +$(eval $(call arc_rule,PV,pv,pv,$(PV_ATL),PV)) +# У звука своего *_arc.h нет: содержимое адресуется таблицей pop_sound_tbl.h, +# которую печатает сам упаковщик звука. +$(PACKED)/SND/snd.arc: $(SND_ATL) $(TOOLS)/pop_pack_arc.py + @mkdir -p $(dir $@) + $(ARC) $@ $(SND_ATL) + +# Файлы, которые ложатся на диск как есть, — простое копирование из staging. +define copy_rule +$(PACKED)/$(1): $(2) + @mkdir -p $$(dir $$@) + @cp $$< $$@ +endef +$(eval $(call copy_rule,BG/pop_tile.pal,$(ATL)/bg/pop_tile.pal)) +$(eval $(call copy_rule,BG/pal_tile.pal,$(ATL)/bg/pal_tile.pal)) +$(eval $(call copy_rule,KID/kid.pal,$(ATL)/kid/kid.pal)) +$(eval $(call copy_rule,KID/kid.ani,$(ATL)/kid/kid.ani)) +$(eval $(call copy_rule,FONT/font.atl,$(ATL)/font/font.atl)) +$(eval $(call copy_rule,TITLE/title.pal,$(ATL)/title/title.pal)) +$(eval $(call copy_rule,PV/story.pal,$(ATL)/pv/story.pal)) +$(eval $(call copy_rule,PV/pv.pal,$(ATL)/pv/pv.pal)) +$(foreach i,$(MUS_IDS),$(eval $(call copy_rule,MUS/m$(i).bin,$(ATL)/music/m$(i).bin))) +# Уровни идут на диск ПРЯМО из оригинала: формат res20xx.bin наш загрузчик +# читает как есть, упаковщика для них нет. check-orig здесь тоже order-only — +# без оригиналов взять уровни неоткуда, и сказать об этом надо внятно. +LVL_ORIG := assets/orig/SDLPoP/data/LEVELS +define lvl_rule +$(PACKED)/LEVELS/res20$(1).bin: $(LVL_ORIG)/res20$(1).bin | check-orig + @mkdir -p $$(dir $$@) + @cp $$< $$@ +endef +$(foreach n,$(LVL_NUMS),$(eval $(call lvl_rule,$(n)))) + +resources-bg: $(PACKED)/BG/bg.arc $(PACKED)/BG/pop_tile.pal $(PACKED)/BG/pal_tile.pal +resources-kid: $(PACKED)/KID/kid.arc $(PACKED)/KID/kid.pal $(PACKED)/KID/kid.ani +resources-actors: $(PACKED)/GUARD/guard.arc $(PACKED)/SKEL/skel.arc \ + $(PACKED)/VIZIER/vizier.arc $(PACKED)/SHADOW/shadow.arc +resources-sound: $(PACKED)/SND/snd.arc +resources-music: $(foreach i,$(MUS_IDS),$(PACKED)/MUS/m$(i).bin) +resources-font: $(PACKED)/FONT/font.atl +resources-title: $(PACKED)/TITLE/title.arc $(PACKED)/TITLE/title.pal +resources-pv: $(PACKED)/PV/pv.arc $(PACKED)/PV/story.pal $(PACKED)/PV/pv.pal +resources-levels: $(foreach n,$(LVL_NUMS),$(PACKED)/LEVELS/res20$(n).bin) +resources: resources-bg resources-kid resources-actors resources-sound \ + resources-music resources-font resources-title resources-pv \ + resources-levels $(GEN_H) + +check-orig: + @test -d $(LVL_ORIG) || { \ + echo "SprPoP: нет assets/orig/ — генерировать ресурсы не из чего." >&2; \ + echo " Что и откуда положить, написано в assets/orig/README.md" >&2; false; } + +else +# =========================================================================== +# Обычная сборка: assets/packed/ и gen/ — готовые входы +# =========================================================================== +# Ни одного правила с таймстемпами: пока файлы на месте, make их даже не +# рассматривает. Пропал файл — доделываем ровно его, рекурсивным вызовом в +# ветку генерации (она подтянет и заголовок, и оригиналы). +$(PACKED_FILES) $(GEN_H): + @echo "SprPoP: нет $@ — генерирую ресурсы" + @$(MAKE) GENERATE=1 $@ + +RES_TARGETS := resources resources-bg resources-kid resources-actors \ + resources-sound resources-music resources-font resources-title \ + resources-pv resources-levels +$(RES_TARGETS): + @$(MAKE) GENERATE=1 $@ +# Перегенерация ВСЕГО, не глядя на таймстемпы. +resources-rebuild: + @$(MAKE) GENERATE=1 -B resources +.PHONY: $(RES_TARGETS) resources-rebuild +endif +# --------------------------------------------------------------------------- +# Прочее +# --------------------------------------------------------------------------- +# Образ HDD собирается после staging — иначе на диск уедет старое содержимое. +# Рецепта здесь нет намеренно: он в app.mk, а это только зависимости. +hdd: stage | $(BUILD_DIR)/hdd +$(BUILD_DIR)/hdd: + @mkdir -p $@ + +# Однократная связка с MAME: run_bridge.sh жёстко открывает IMG/test_hdd.chd, +# поэтому подсовываем ему символьную ссылку на наш образ. chdman переписывает +# сам файл, ссылка остаётся живой — но MAME после пересборки образа ОБЯЗАН +# полный рестарт (новый inode). +mame-link: hdd + ln -sf $(HDD_IMG) $(PROJ_ROOT)/mame/v306/IMG/test_hdd.chd + @echo "MAME: IMG/test_hdd.chd -> $(HDD_IMG)" + +# Музыка из конкретного набора записей. Каждая цель пересобирает музыку и +# сразу раскладывает её в build/, чтобы `make hdd` взял уже новую. +# make music-mp3 собрать музыку из mp3 +# make music-mt32 вариант Roland MT-32 +# Разовая сборка без смены умолчания: make MUSIC_FMT=ogg resources-music +MUSIC_TARGETS := music-flac music-mp3 music-ogg music-mt32 +$(MUSIC_TARGETS): + @$(MAKE) MUSIC_FMT=$(patsubst music-%,%,$@) resources-music + @$(MAKE) MUSIC_FMT=$(patsubst music-%,%,$@) stage + +# Перелинковка приложения без пересборки библиотек и ресурсов. +relink: + $(MAKE) -W Makefile $(EXE) + +# clean (из app.mk) сносит только build/ — ассеты остаются на месте. +# distclean дополнительно убирает УПАКОВАННЫЕ ассеты; вернуть их можно +# командой `make resources`, но для неё нужны оригиналы в assets/orig/. +# Сам assets/orig/ не трогается НИКОГДА: он не производный, а исходный, и +# восстановить его командой нельзя — только положить руками. +# gen/ тоже остаётся: без него не скомпилировать src/, а вес его — байты. +distclean: clean + rm -rf $(PACKED) + @echo "assets/packed снесён; вернуть — make resources (нужен assets/orig)" + +# Цели генерации ресурсов объявлены .PHONY в своей ветке (см. выше): в +# GENERATE=1 они настоящие агрегаты, в обычной сборке — переходники. +.PHONY: stage check-orig resources resources-rebuild resources-bg resources-kid \ + resources-actors resources-sound resources-music resources-font \ + resources-title resources-pv resources-levels $(MUSIC_TARGETS) \ + relink distclean mame-link diff --git a/applications/SprPoP/README.md b/applications/SprPoP/README.md new file mode 100644 index 0000000..dd08920 --- /dev/null +++ b/applications/SprPoP/README.md @@ -0,0 +1,64 @@ +# SprPoP — Prince of Persia на Sprinter Sp2000 + +Порт оригинального Prince of Persia (1989) на компьютер Sprinter Sp2000: +режим 320×256×256, дабл-буфер, звук через CBL, код разложен по одиннадцати +16-килобайтным банкам. + +Приложение **автономно** — код, оригинальные данные, конверторы ресурсов и +сборка лежат внутри этой папки. Наружу нужен только компилятор: + +```sh +export SPRINTER_ROOT=/путь/к/C-Compiler # не нужен, если папка лежит внутри его дерева +make # build/sprpop.exe + ресурсы в build/ +make hdd # + образ build/hdd/sprpop.chd +``` + +## Что где + +``` +src/ рукописный C: главный цикл sprpop.c + движок pop_*.c +gen/ генерируемые заголовки — руками не править +assets/orig/ оригинальные данные игры (вне репозитория, см. там README) +assets/packed/ то, что ложится на диск игры, в раскладке диска +tools/ конверторы ресурсов; все пути — в tools/paths.py +docs/ планы, доски задач и багов, справочники (начинать с docs/README.md) +tests/host/ модульные тесты движка под ucsim_z80 +build/ выход: sprpop.exe, каталоги ресурсов, hdd/, промежуточные atl/ +``` + +## Цели сборки + +| Команда | Что делает | +|---|---| +| `make` | собрать `build/sprpop.exe` и разложить ресурсы в `build/` | +| `make hdd` | + образ жёсткого диска `build/hdd/sprpop.chd` | +| `make mame-link` | однократно подставить образ в MAME (символьная ссылка на `IMG/test_hdd.chd`) | +| `make resources` | перегенерировать ресурсы из `assets/orig/` | +| `make music-mp3` | музыка из другого набора записей: `music-flac` (умолчание), `music-mp3`, `music-ogg`, `music-mt32` | +| `make -C tests/host` | модульные тесты движка (секунды, без эмулятора) | +| `make clean` | снести `build/` (ассеты не трогает) | +| `make distclean` | `clean` + снести упакованные ассеты `assets/packed/` | + +Отладочные ключи: `make LEVEL=9` — стартовый уровень, `make ROOM=15 POS=2` — +стартовая комната и тайл (`row*10+col`), `make PROF=1` — профиль фаз кадра +полосами бордюра, `make ALLOCS=100000` — плотная упаковка регистров для +замеров размера. + +## Сборка из чистого клона + +Работает без `assets/orig/`: упакованные ресурсы (`assets/packed/`) и +генерируемые заголовки (`gen/`) лежат в репозитории. Исключение — +`assets/packed/LEVELS/`: файлы уровней ложатся на диск прямо из оригинала, +без конвертации, поэтому они, как и `assets/orig/`, вне репозитория. Что и +откуда положить — `assets/orig/README.md`. + +`assets/orig/` нужен только чтобы ресурсы **перегенерировать** (`make +resources`) и как **эталон механики**: любая правка поведения сверяется с +`assets/orig/SDLPoP/src/` до кодинга, см. `CLAUDE.md`. + +## Происхождение + +Выделен из `applications/PoP/roomtest` 2026-08-26; та папка заморожена и +служит архивом закрытых задач, багов и исполненных планов. Референсные +реализации (Apple II 6502, Princed Resources, mininim) остались в +`applications/PoP/`. diff --git a/applications/SprPoP/assets/packed/BG/bg.arc b/applications/SprPoP/assets/packed/BG/bg.arc new file mode 100644 index 0000000..cc36258 Binary files /dev/null and b/applications/SprPoP/assets/packed/BG/bg.arc differ diff --git a/applications/SprPoP/assets/packed/BG/pal_tile.pal b/applications/SprPoP/assets/packed/BG/pal_tile.pal new file mode 100644 index 0000000..df429b2 Binary files /dev/null and b/applications/SprPoP/assets/packed/BG/pal_tile.pal differ diff --git a/applications/SprPoP/assets/packed/BG/pop_tile.pal b/applications/SprPoP/assets/packed/BG/pop_tile.pal new file mode 100644 index 0000000..e62c573 Binary files /dev/null and b/applications/SprPoP/assets/packed/BG/pop_tile.pal differ diff --git a/applications/SprPoP/assets/packed/FONT/font.atl b/applications/SprPoP/assets/packed/FONT/font.atl new file mode 100644 index 0000000..a8a98e1 Binary files /dev/null and b/applications/SprPoP/assets/packed/FONT/font.atl differ diff --git a/applications/SprPoP/assets/packed/GUARD/guard.arc b/applications/SprPoP/assets/packed/GUARD/guard.arc new file mode 100644 index 0000000..ad3aed9 Binary files /dev/null and b/applications/SprPoP/assets/packed/GUARD/guard.arc differ diff --git a/applications/SprPoP/assets/packed/KID/kid.ani b/applications/SprPoP/assets/packed/KID/kid.ani new file mode 100644 index 0000000..ec368af Binary files /dev/null and b/applications/SprPoP/assets/packed/KID/kid.ani differ diff --git a/applications/SprPoP/assets/packed/KID/kid.arc b/applications/SprPoP/assets/packed/KID/kid.arc new file mode 100644 index 0000000..112e465 Binary files /dev/null and b/applications/SprPoP/assets/packed/KID/kid.arc differ diff --git a/applications/SprPoP/assets/packed/KID/kid.pal b/applications/SprPoP/assets/packed/KID/kid.pal new file mode 100644 index 0000000..bfbd2cd Binary files /dev/null and b/applications/SprPoP/assets/packed/KID/kid.pal differ diff --git a/applications/SprPoP/assets/packed/MUS/m24.bin b/applications/SprPoP/assets/packed/MUS/m24.bin new file mode 100644 index 0000000..d861766 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m24.bin @@ -0,0 +1 @@ +{}|{~zwynvymo{|wvy|{svusnkoyzrx}w~uvsspwnvuswxywtlr|v~wxumwvlq|{vvz|wtvvrmkqzxq{}|y}uvsrrvlwvtwyyuqjwww~w~ur|tks{yvw|xxxwspovyqpy~|{vwtky~{z}zroypy}~~|ysyytu|{}~~}}{}xlr~zwqw}|}}s}z|z~wo}w~ywx~ysz|uvzx{|ym{yos{~~{|xtpx{ps|xrv~sr|ussywuyu{unxryw}ur}~ypmvzz~rfsxmjt|~{~vp}vkox}}|}{w|}w}~~z|~|xxqpvy~|||~u~qswt{np}vouy~wv|z{ury{w}omru{r{s|x}okyypz|ppzy}}qusjkpmqxv~x}qzyuvup|{z}q}uwpevkutytt{|kozwpsy}sujsx{ybj}w~}|}|xsz|nzx{~~y|wyuv|}lqx{zwytw|~x}sz{ik{}psls|t{}~|{tz{vwktvqt}~rz}{yy{oozqhtz~~zv~rynjnm{sr|pjk~ooknpqoxnw~p_vqu~yqrymszpwmyyzxsnx}yx~~txuolq{nnrwjg}wtwynZT`dqqvyzqyjvy_W^\lzofpwmdieWpmptzmfu|opzicfqyx}q][kok}owwqp~|pni^exzyx{h|wvmpgikz{ry|x|so}qmrquw~rfv{kt~ywnmzw~|zxuleksvyw||swzpnlshxplbvpoz|mwqj{{jptlkjmxq~zsyqnpmnruwqkqu|tqtuxl}{xhnvyo{h~~}wstyvni}wjrzqynehv~z|wyumlvtml_gv}gjzxsirzwnor{vm}ry|uvnhtzy{zqpyqtwp|unrguvvuiylktnlq{uvwtsqvqutwynz|zcm|qnwmw|r~z}{lusgcnpvs{}z~v~y{znxsmgos{sawj~ynqovd|zlgx|tluvTntiiu}~~wgv~kiton{pUSWnyo~r|vxtwtdzsl||~{~|pqnbqpmzu|~wmfzv|zi|}zzcZdmu|i^jtlo`jy|j\nc`eZXdru}}w{pj~torz}zkl}ROYWqupw{g\Yjhir|}xpo{rwqzz|wtxxwprjM_aPo|zwnj|ulqe`eXYmm|~vntswagld`CC^ks~stzpW[cRRl~yyv~zmpuvw~}s}tfzhmfWTZaU]glxs~}q{{ttuv{W\eOO^aprrsltoXT[aNGPdbjwvebg|evod`Zkegvypowhqdefagp|}jaslivur|~~{{x~phwwmp]kkdclk_jyxz{~qqtyz}{~_c|fhjDG7Khoqub^l]WM@WXhz||zjdpt_ekUJOZckjn}qrkroXWd`k{x}r|o]mjvwwx~ztrc\Y]b_QLbcxz}{t|{{{yytmxxzy}jywyiWF=@Ti|lbTaZUXYKKg__|{tx{lqXmzui_migdecckufry~p{iiophpqtxzsepugnufbywh}tp}yeszyuo\Ramckxqp~~}}ufihr||uwecy}yznLFEFPZbrgixcHOMFSRdb}wpi^ord[Y[b\WWdsnvvuz{zxgovc_pzx|}{o~u{|~{mbqbkblgewlmpng`aq\`dnfuv{r}vu|mx|~rs_`|{{w|tnrr}sz`RONGFKotgxgal[KY\LSkz~rnYplhX[ntqpl_k{senxkdpyu~~uyptwzwjy}xytckogpl`epqmwmt{|yuqu|oksthittr}fuyuzuyypcdkknfkzz~~lp{rlu`geZKDDWuºw]VcRYNSVWuuupj_TXXZ]Zc|s|xuwl{k]Zx|qp|~ztrymo~vbajYbZonw{wxyvuvrtnmxdWb~ywvv|t~wj}vbn^hwz}ypid\VUgkhz~{{pacj`]m|{utz|{uoffV]blponwtwwg`W[uvpqxtw~ntqigltfkvhbcdXRbkem~|s{xka[p{ke_rzsv}rpklb\]dp||vssqjc]colaprzihfbqcnutu{yxxrf[XZ^clvz}xqxkT^j`bq|}zp{uknrx}yxxk]fcXe\njh{xz}`Yhd^frur|ou|vqvdbfmu}|{vvvtqw~gkgjvt{s{{psxy|~{mrwpxh_bfijks{{th\]pgSLPil{z{swyrifhmv}ypgheYT_e^]iu{y|vkvxaTPcfewr{uyx|sf\[^iyy~zz{wqsobiplipv~w}}py}}}}xpnnu~rhdbdjwtqy}ws`jsc^[XWe|~vyurrroospdjkZg^fj]hptx~~~~}plzmdc]Yfwyxwtbrzlihgdchyt~yxzy}|z{{~{uuoamk_`usv_yt|rmmlfaamyyt|xqdhxfOQYKWYc~}~vmhgipz}xuh[[ee\Zcmrsu{~zuz|}vpu|{b[cV_d_vv{~|qv~{umhdcekmeyzz~um}}~|{~x||pd]VPf~|wstlhz}{{yvtpnptimxo}wwqdxocWPV][]lozvutsst|}ssoakcemcgnqqqokjquozxvsru{}teY[deck{gjtrnijlmlnswuryyzpq||~}zyz~yyjTNfmkrxub{zqqkkpsssu{|vrzzxsgn}pae]RZNS_`q{}zvux}{wkhkg^]fossqqngfm^o}|xnklpw~zxggf[gikz}rwvrpmkkmt~uy|s}~}jpussuy|}rdT[fZUdns~upzumnnpsx}zx}qu{sjzvnf_^cb\[cp|z}~z~{zzz|ztzyrwkoqjntuqlgb^ZVepmtz{xuv{zrpsohiq|njkjdbdgny|{~|nmllmrzthssb[XW]oyxzhlieflt}vmbeywkjombiealhflt|{{{{}~|r}}}vppsvvspkb[VPcm^eswy|}~y|}srw}~~rpokjknt~{ysuqpnhgks~vfixn^Y\]Xft|ursrooryuncn{snmjgiorolnqsstx~|}xvx}ogx~uu}ztssrokd[R\dVRZclu~~}}}qqngefiovwgq~yrliltyp}vle_Y[bkswuvqmmos{zfaqqggmpjptowxrpponmnsw~|~vzwuwz~{wsnhc[S`dSNV[`hs~~{yy{{y|{xsnmotyzxvqx{umikqz{kn|tigh`[`\`py}|zvuvz~}ou{ngddelsutuwyywtpljmktyxzxq~}omppt~zvqmh`YadWPPRU[er~zx{vt}|volklpvyyy}xmu{olsyyrppqs|zuvmgccc`[[an}zvuvz~tr|rhgedlmluyxyzyvrmjgacy||~|~pihnw~{tomgV[m`TSPNOT^jw{wrn|}zvtsrprzzywy|lden{~{{vsrtvx~{pxxqmfgh``b__fp}~}}|}re``ejllpv|ztqnbVgxsv~}zz|~xqjhnos~xsmbYcg[USQOOSZajwyhm}}yvttvwvuw{|tqzpgbhpv}|urvwxzyumiikkifeegils|xndcgfjsvy}~{wob]ilfir|}zrmlmnpu~xlZ`peXVTRPOPU]fox}st}ouxuy}}zy}{x{{xvsnuyqkgginwy~}yy|yq{~toihkiiorqpnllowumihhilry~}sbgvi]^dlu~|usrlmuzqekpcZWUTSTUWY]gqvx}xrlpty}}}}ztjcq|xyvrrplr}~z{}~{zurmn}tolkmoqruxzzxtqoqrqvy~xvpmqwz}}slwzka^^ahr}|||zxy{xmxvcYWWVVWZ]acdejr}{}wolmqwzqdcuzqnorvz}|zz}~yyz{~ynbi}~wusonpqpuz{{||{yumdf}|s{xvwz}|qqpfba`bhs}|~|}zwf^ZXXY[^_`adediqtus}|xzwrpos{|pcjxrkjlnt}~|{{}wf^nysonnoqsuwxy{|}}}{wnaex|zzx~|z{|~vq~yojgecdgnw~t~~}}|zyjc^[ZZ]`dfedcdgjkgh~}xv{zustyyjgvxledejqx}|{|zhhurkjllosssw{{{{|||{uidqsjlu||z{~{z{}}ynstnljgedfkr{yv}|}|xvwnhb_]]^_befdced`\Yg|yvuxyvvzujs|siecejns|}~}}}tlvviccglqtuvwxz{{{{{wlaluh`clx{y|}{{{xqqyromkigedgnwv~~}ztmx~wqke`^^_abcdeeda[TWlzxwwwvtrw~yvx~ttzlecdfimry|{}~{w{tgaafjlouxz{{{{{zxqhitob\]dmy~{{~}{wojw}tqpomkjihikov}|xy}|vml|}zwqkfb_`ceeefd`[TNZkkilrvxwuv{~~zymgefggkry{wrpqe`_bfkotx{}}}|{zukgt{nb]\]bly||}~}tkl{wrqqqponmnoonqx~xtux}shn~wwzzxtpligfeeffeb[RQ_f`^bjrxyz{}zqkihhikotzxki{{ofbaadksy|~}{xqikyyohda__ckwvjp}ypnoqqrsuvvutrrruwyxxuvy|wnu|tnorvy{zxusqnjhge`WOUb`WVY_hr{ztponmosw{~ts}{uw~~{tniffhlrw{~}ypgo|xokjihghjpyw|~tljknpsvxz|}}zwusoigv~{z|{wtkikou{{upljgaWR\e^WTTW^gp{}ux|ywvuuvwyz|y~spsz{~{wrmlmpsvy|}|vmkvzsmklmnnoopsyxnjjlptx|~|zvndcs}unjijou|ysoh^STabZTRRUZajt~{r{}{{|}||}~~y|upory~z~{xtpooqsvyxrjo{ypkikloqstuuux|snllnrvz}~|vkaftvtv|wqmkkmryzsi]U^f`XTRRTX\cmwyxu~}|{||{us~vqnot||~{wsqprturkly|sliijknpsuvvuuyvqmmorv{~{shdptnknt}~wrnlklpv}}sfZ[fdZTRQRSVZ`gpyvv|}{}~}|ztowzuqmlou~}xtqpqqojgsxojhhijloqrsrqqtx|ztpnnquz}}vkelunfeipx}xtqnmnqu{}re_hk`WRQQRRTY^elt|{t|}xy|~ztmo~zxvsomnrz~}~~ytqolhdkz{rlihhhiknrttsrqpoopxzurqsvy}|tklxvjb`cipx}zwtqooquy~~rgjqi\USRQRSUX\agmu|wx|y}|vvy}~xqksywwwvspopv}}}}zuoh``ozumjiiiijmortvvurokfes}ywwxz|~}wokt|tic`adjr{{w~}{ywuuuvx{~}tqyzl_XUTSSTWZ^aeinsxywwytuzzwwy}ynmzztsuwyyyxwwy~~~~}sh^`oslhhhiijloruxyyywrja_o}~~}}}~~|vnp}ulgdccejr|wt~~}}}||{zz|}|xsyqg_[XWWY[]`bdfhkmmkr~xvvz~{z{{qsvqpqtx|}ocgpmebcfiknqtvxyz{|{wofgsvqqv~}{wpjr~}tnkihhikou}zx}|}}{yxtnn|xpjea_^^`bcdeefgfb`m}{{}w}vqpqsw|}|vouvlc__aeinruxz{{{{zvmhoxtmikr{{tlkw{tpnmmnnoqsw|~zz{~~{vngk{}yuqmjgeddddddddc_YZjxzz}wsqqsv{~xrsy{{nd_^_chmrvxyzzzzwrjlx{tlhgkr||rio{|vrpoopqrrstuw{|xwy}|ukfp{{wvutrpmjiggfeddc`YU\kpoquzxusstwz}tmt~|pga^^aejotwyyzyxslhq{woifeglt|qnx}wronnopprssttuw{{wvwz~{qilwytrstuutqoljhfedb_ZSTbjhfiou{~yvtstvy}xnmy{pic`_aeinrvxyxwsmhmyyqjfcbcgnxzrw~zsonnoppqrssssstvwwyzvuvx|yomvzupoqtvvutroljgec`[UR[ed`_ciov{{xvuuvy|vot|{xy~|tmgcaacglptvwwtoiisztlhecbbdipz~|wpnmnooprstttsrqpoou}yvuwz~wqu|xplloruwyxwuqnkgd_XSVad^Z[^cjqx~~|zxxy{~~vu}}upqw}ytnifddgjnqtuurlhnxxpkhgfdddgls|wqnmnoqrtvwwwusplhgq~{yxz}xwxpkjknrvz|}|yupkf_WT[daZWWY]biqz}~z|~}}~}zvomot{|xsokiijknprqnhhszuokjjjjjjjlosz}uqonopsvxzzzxvrledo{|{~~x}zrmjjknsx|~ztlcYXae_YVVWZ^dkt|xw{}}yz}vqoosz|yvrnlkllkhdhuysnkkkmnpqssssux}zusrsuwz{||{yunfgqvsqsx~|z}wrommmpty}|ti_ahg_YWVWY\afmt|zy~yu{~zvttv{}xsnkie_^htuqmllmnqsvwxxwvuvvvx{xwxyz||}|zumhmvuollpv~|vz~yvsrqqrtvz}wlflpja[XWXZ]aejou{}||~}ut~~|zxxz}ytnhaZ]krpljjklnqsvxyyxwuroko}|{{{{{{xqjjtysmiikpw}vu~zxwutttuvxz}}unpwtja\YXXZ\`cgkptz}|}|{|ysx~~~~}|zz|xqg^Ybmnjhhijlnqsvxyyywtohdl~~|||{ytmjr{xpjgghlrzxsx~|zyxwwvvvwxy{}|xrrz}tib]ZXXZ\_behlptwxwy{xy|}{|~ww~|||}}}|zyz|ype]`kniedeghjlortvwwusngacq|~}|zxuojoz{slhfefjov{tt~{yyyxxwwwwwwxyywsrz}sic^[YYZ\^acfhknoopz}wuux}}}~|xzyz{|}}}|zyy{~{pebinidabdfhjloqsuvvtqkc`grtsuz}zwrlmv}xpkhfefhlryzuz}zxyyzzzzzzyxwwvtrpv}slfb^\[\]^`bdeggggl{|xutux~~}~{xxxz|~|zyyz{{tklrof`^_acfiloqsuuusoicdmrmjkqx{uokq{|uolihgghkot{~|zwvwxz{|}~~|{ywtplmw|vqmieca``abbcccb_`iy~}|zxwx{|yxyz||xtqq{yuyzrg`]]^aehlortuvusoifktskfehnvwomu|ztomlkkklmoruzzwvvxz}}ytnhkv~|yvusqnljhfeeddca^Z[fswvwz}|zz{}wolu~{pgb_^`bfknrtuvusoiiryvojggimt}~tsz}xronnnopqrrstvx{~~|yxy{}|vniluxusstuvvutrpmkhfda\WZemnmosy~~~|sqx~~~xqkfcbcehknqrssplhkuyvqmkjjlpv}|}xspoopqrtuuuuuuuutt}}||~~wolrxvqooqtwyzzyvsolie`YV\fjhgimry|~}wy~~zwx|}xrmifeegiknpqpmhhpxxspnmlllnrw~yspooprtuvvvvusrolo{~}vqsyztommnruy{}|zwsoje^XXagfcbdhmsz}z|{}wssw|{wrnkhgghikmmkhejtwtpmlkkklmosx~ztqpoprsuvvvutrnjho}zttz~ysnllmptx{}~}zvqkd\Y^ffb_^`dhnu|~zy{|}|{tppsx~}yuqmjhhhhijigdgpvtoljjiiiijlosx{urppqrsuvvvtrnifhs}|wu{xqmlkmpsw{~~{wqjb\^egb][[]`djpw~~yuw}~|yronosy|yurnkihhggcectwzx|yz{w~v}puvwyopwzzvohibXjlt}u}}zlwq}ux{zphtd_gqwqgfmsjdzqqxwbT[jsleZKL`eTN`s~{~x}vmro|ncs~qorzwpwp~uzuw|yz}yvxx^[^^swfdrz~zzb_q|[ZSi~wzwsurvuoZjupr[]Zcysur}wintrpppdx~z}hq{garffzcZwmhkttqtmv{pkqmpx{xw|zs}~xudcqp{ystuxl_`ysql|~iz{sxfqumluy}}or|~}{yjy|y{|xxkhf~n]eth_trnx}gd}|s{|wxz~wjsjmowxxvyzxxsqƴujor\Yjaazyvxxvvtai}ppkoy{~|s~wy}tr|mmwz}_RkdXf^V`ktxx~zlvy|swmq|jqwfchYam`afU`_dhuxmyuv~pjqwzywl|vvoupfwwl}zzz}xtwkevyhXxoXgcWZfouxrprtnro~o[pznp~qw~~wrlhisRSmnai`bqnswzsrrwwopeuzmpwl~{}vwn|c[`i]Sdi_n}vwy~f|tmtgy}z{sx|sxljyxqp~wtr|ey~{|fjqb[ZYrvlpmarmtu~|lSltSTYKCHQWZYXqluy^_tiVVblcgz{telr`WYJZeojqnblcX\fosvp}{~zj_yrp{~tzzrllnsd~xpqtrqk}r{yms|ufqznoxqmmp}tX]iiPXIJW[`ddajztgsxidaXfiVSEDWVkszkrx}rovon~nt}nuvrxmgehvzho{~qx~phkbkupgevlwr~ywmx|~|wz}xtj|vh]Zplqmrj`^NCCGIJIUh^kemfmqZLSfWT][MRb]\p{wuu|{gi`}}t}}yn~ug`sgqmuxvue_]ZURYnevzxz~omzthp}|tomov|hl\q}oviwyy}~qrjp|~ulht|pjvufcf_YnkVFSQLSM_Wk|vwppwtoc]]_]]_tuvom}rmpf\el`_oyxusw||q~t~{wuuup`ir^JN_Ymnzvqiqrog`bhkmn|{ztyyqe^XPMPbZfquy{xg]eeVWfqrrtzuwmbnoc^gkmjil|}yysfss[HPVN^Xmkr{prqq|||tkhigghyw~}mf{ztzukp}vnvypkjlfW`Pm|vyzxys}vs|}z{yz}wtmfws^NU`Vjgzxwod__`afus|}}ievohjeXWdcY^lqmgdehfU\Sk}mvv||wruwsuomfrrd]mjhuo~tnqxkeh_UPQRW`tvvvxrxx{ylhyp{}t}zry|upqw}rmy~vm{wh\`gXe_fpeqxxwwum`Wbsb[ef_[[\^cnsfurvx{kwzlkni^_nsmrnnq}ydrkdjpux{{k\\ok\UUknu}ux~{tsw{{rz~|wz~|ukggip}|wr{}row}|wtvwoYYdjiVcc\bioqsxrjrvj[MUhaQIGXVYlhzhivadjf`_chp|v{xswyqlwy~|{}{kbsyvvox|o~yyyzyvwymYcrcZMXsp~tccvnZfnopmz}ztyllgaWScgaetqadjpfcokkhdjrcUfkr~{x{ssz{zswuukjekolrm~rzyzdc]Y]OS]Wgwnz{pu}y|onznezqjjmqxynwzsly}~z|v^[Zpyu~ozy{}mmmemdPMZ[K`ggzxtxo~v]anbXX`[QTWcsxfhnwqstxtqmrxpoux{tgjlwlxotslgf~yz~mcfcvgbtrYg|s~hq~ysmnt~osdzureo{neYY\[cit|~w}~xvzz{oq||tqmRikq{jfomaWxefiorhbbT]skeicjhWx[fzvui}}sdljZTYe]oqw~gu^J\VVOP_t~xzffmedhtt{vpz~hWipec{su}lb}{lsw~xp{p^{wxjeWcxcXbTSXEjwkTWqzguyflr^xjwe_xn\`{qXaXQUY[oskxvhh\L_ybWX^\anu~{vxijsX`aSckksvuqvzzp[`oeYQPUVWY]bwrr|{rt~nv||e`gnqapwoy]cka}ww|x}{|k`]mthfkgp_Nrywpro~k]Zd_JPh`furt|{qf`hYI>JFKVeshpyx|ruvtbfe`cj~{|||aqUN|matspvnlwz{}wfimbUKNaoigobawivxtrsty|xvvdtznnlrs{w{tllpr}zmunsxrg^nze_rw~sm|||ymmdc[[NMbr~|TGfo_=@E>[Zlnx{\kr\gnp}vs~kafeo||ws`ngsn_]^ihjyxtacul|wcwuuz{xx[goaT^eaaxxx|uwr~y{sv~|polK\rc]ers{{xwbowvkkrrmgz~|glkxj[crw}mbe[`|usytnhX^|ivkml}¹}||tesvvufexxqthddixzzy|wnjw}mtjep}}y~tvo[vyw{}pysgmiWeXw|||c\]N`ujgu\aq|}vlshjIWRWld^awrgR\morlrqzmpmxusjmVbQ`qtkz~|snywg}|l|yw~v|r_]paomcvkfu^j`\U_|}rshwdpssgjyxxzyniggsh|m{u{oj[frp||lrdcXhvsz{~}~||ousue]qjk|r}mmqg_JT^YSXZFZRel~t{}ug^}izu{ymxyzknù~so}qnlkwnso~egfXlb`kdQhbJ_Y]WYhukvYgkk_mqu}l{lyuc_gR]hcjo{~peQ_]aykzw{trs{|xuajrfy|uz|us^sv[yrnv}tp\YR^mqo[hXV{ci||y{|tufvrgytklq{qpvh~{beist~pyqheixsx~z~mxsdno__pRTXVTP]_xu]gxMNVWMWaev|jvru}h`n_Yicgqyadi\uuox{vunei_p{}u~vstrww|qirly|~{honqi_`S`oiTSZ?cnVky}z{qqprzz|y{pmwz{ux~}xkptmod`\ckdpm{wwrbpcYaaN[_LW\da_lm^ywTX]__rv~pjot[WbPPbcjsymqunWbg`{}wy|{unvr}v|h`k\]nklq~x|krqpzz{ynpdj}x_[]Cjr]m{}ufowgbtkk~{tsgvxw}h~~gmv~pvh^khefnxtrxxglpbxmZd\`d^gmvtt^qr[XXZVirvpUhti[if[oyz{ytuw~~nxqqspw}}}}}yls^ecYo~uut{srsnkkdtt`UWPeokluf|m{}t}vf~x~z~o~|~t|ss|pun`qwpns|twu}rs{jjgbx]S^WZchlx|zwlb`bbw~^y|oX`_Tdpruzopwqt~rxx~{|~XtpbNYWSl|vvjkrzy}{}~tqf`bllljpx~_}o`okh}ysorrxspoxqwisf_quqn{yxxqqdpwU^^\]ilzqppbWNQQ_rup]zybV`TWlszvmw|~w|ywzxvyu}~}{nazcagZdy{}yyxzs}tvuyuxnwse^P_eadfoywqjqsix~n}yx~m~squ~x{l\gkhdnmmvqv|ygf`YmXZ_``hkx}j|k]VXYiwzko{i\^NM]goyxr`Ypzvywxz~{{wzt}{vfp|qb]YGP`jr}zursy|rtyxkluvTbyghkovxfw~tptmaowzztmsej}xdt~fein}}zv`NUXYZd`nzytqn`hmfcifjerz_uqUPOO\`ds{qi{oinhSN[bit~wim{{nx~vswsulz|wuqmkvpwpm{fdmeQSalv~~uzzz{||}xwjtvsfXczcVxodikp~w|v{lpx{z}skrjh{z{rjx~t{~sdpekr}y~u[Y\]\a]bssx|vmm`XddW^`cdd{~}qjZXV]d_rxzalvaKNV]eqp|}}xnflz}ytwmj{}q{x{wr|w||s|kvoP^l\KT`ks|ty{nk~mw}~}~~wsepgmnjmnv{vv~apr`gpuuwijmdksv|rslilr~htoWVX\\_Xdrpwradnh_fgknmwwx^TOOX__sywuWcqaKOZenyz}vpu}q{}vywuvn~z|tpsvn|}un}kQZldSXes{tz~xsabqentjjjpx~gnnp|ztzgyykz}yzzz}ymlls}rr|e_beefcbumriZihad`igus{kd]^drqx_]mlYS]gnvz|xsqs~wl~xyu|v{}zzztufOL^`RR_lv~s~{ngmr|ususx|tbav}ppzyqta{|q~rkjipz~icsud]`ccedhupogyommiqo~{tjb[\con~yp]Xjm^[bkqy|xt{|xw|y}|urvzu}~lzy_LI]bY\iv~}xjcbu}sttuy}vgg~z{wypj~lo~syu~zp~nlinvzhfywifhifgdrn{vdktgiaigqz|~vlb`blmyp^]li]]bhmsx}z|{}m{zys{v}ywys||qm|kTFJ[\Y`ktz|~pz~|}vif{ywy}~}{i`gvwtw{{wool^wzo|xpi|nklqy{|kcflha^]\Z[iimqiyuidliry|vu{h^YZ]]kv|{oaYY]^[^dkq{~~yvrqsntqs{wxplf`jjrvsyqeusXQNPX`djrxxpr~~~}zxnXmzwvwss}}zonmnv~}|zvprkajmtvmww}v\ovonpus{urlegjc]YWZfgj}xfp|g`gdkpsk~vjcab_jsz~kh_V[b``eju|zzyxz~~~poknhnmmv~zvog^egltrwvjkydTTMHR`ehnpvymgo~wx~lc|wryw}~u{tlokfp|{vqjjg\[m{oouz{m`v{vropop}womcW\c^WTT_eenzp|fkjiqs{{p{si`]Z\imyxpifZNUbcbgm{{|~ympminlpw}{{nf]ZbbjnmqlanqYTTMFSgoswy}om|xzfj~}xqqxw~}tsrtohrxrqk_c{vswkruwednltrooptwtqgZ_kkd_]ejftvtqcefeou}~|~uwnea`drx~toj_QUekklq{w||~{nnojpsv}wmgdlmpvqqjcumYVTOGOftwyz~}kqww}ox|{|zytommkek~wro\fy{xrrz{pskesurqtx{spmh^^lrleadibxvmjonwzmtpgcdht|~~rmibVUeqqqu}y~vqupv{}{ulgdjllrnkadufXUURLQgzqz~wp}rptyzqpqqmq{satz}soowy|qvb]lsssw{|vtsoecpytlhkkfqkinnytjstnklq}}~tpmg\Yguwvyyyqprow{~zypjiompsni_ns`XVWTORg{you|w~uw~xproosrnqysc_zxx~spvzvyv_^lvuuw~|vtppoi`]hrnhgjcpsnqpumgnrmijsy}|zuqnkcYWetxy~x~wssnquw{urxxndaeegkhe]^pdVUVVSQWlnxuwzvty|{}{}}xulsworuvtqt~vi^q~yy|rmqruumyj[`oyvty}|r|xrssog_]gpollhl}qmmjmtzxedmrmkrv|xysqme\VXgx{zwvuwzvsoiiijow~rotshab`dgec^WfiXVXXVTU^qzlhvrmvy|}zwzyurli{{nptutrpq{ug]cwyzyssuuw}zlvxfaft|xxxx}|yvon|opqng`XU^imjghw{utppuyodbkqnpqu|~}{tu|onkf_YUXh{y{sstw|xtlihfiov~xmgjjc^[\`_][SWg]SUWWVX\cttipypr|yz}||wvwuqmel}snsvwwwuu{tf`p~zuryutsv{~okxpcbdpyzywy|zwtluvqssoid]Z`msplo|y|vropuzogbjsuvw{{t}xqqmhb_\^k{vrqsv|~xqjhghkqyxnfgkieabdc`[S_hYUWXXZ]bhuskvulr{z~~|{xsmet|nnruwxzywzudauyvss|xwvzps|nfcbky~}|~{wpjx~qoppnkhc]_ltrkq~~{wsu{sibeqy{|}}wruqomifdbbh{ywuw{}wpmopsv{zodbikjfcdc^WRbdWUUWZ^dimvzuypv}x|~}ztkgxznoqvzji}yxtv}xwvy}owxiea_eu~yom}rrrtuutpifnxxnx}{xtv}vlddr}wwxurqonmlikz{xwx}}wqoqtvy|}sgckrqmhhf`UWibXWX[^djpswz|upu}|~vjn~vopsx|}|hq}twv}|{yr~uigdbeu|uiqvppqstttpidkvvo}z}wnedqzpzzsponmmnnlmzzwwywqprtvy~}yod^eopied_WN\eYUUX[`govx{zrrxy~|~wmet{onquz}~tm~|v}|{{{{|mtzkffebet}wmhy|poprsstspielwv|zx}yrkdes}sqrpnmkkloqqtvrpqu||}yojjkosx}~}{si^YcnmifaZPQc]RSVY]clu{}~{}rlqy{}}{~wodjzqknsw{{trrmxvx{r{|~zlkzrhiihehw|umclznhjlkjjjigccju{x|}ytoidgu{sjvoihfdcehnruxvolmot{t{~vlhgimrx||yyskaWU^ije_XOIW]OMQUX^fpx}}zyzzzmov~~{wywxpgcsvjkpux{~vmjmvy~yr}~~}vlcovifiihfht~wofduthhijihhhhgfekw|u~~~}uqnifiu~wnm}wigfecdglsx{{~qmkkns{xpvysjfeimsxzxy|vpi`XU\hlf^VLN^WMORW[bku}xssslqx}x{}y~yoeiyqimquy}~ujdi}|z~uq{|thfurgiklkjktwndixnfgiihhhijihehtzv}{vroljkuvkr}pgfecceiov{~|zrmlmpwwpsyvokkmqvyzv~zuoh`ZV[flh_TKS^RMORW]eoyzqsvru||v|~{oeoxnjmrw||nch~~vqv}tgkvmfgiklmmr~znepxmijlmnpqrrqojfr}vy~xusqppuvmx~qkjiijmqv|x{trrv|yoovxurqqsvyxt}xsnid__fnlcWO[`TRSW\clvrtyuv|}wrjwzonpuzrbf~~ysuujrxlggjnqssu}|ohvwljknpsuvvuurkcqxyzwvvvuv~up}{okjjkmpsx}v}wtuz{rouzzxvuvxyvv|wsokgb^ckkbTQ_[RQSX^foyqx{x{zy~onzumnqv}qamztuzmitqgegkosuutyyknyslkmortvwwvuslg|yy|xvvwxww}swvnkjjknqv{z{vtv{yplqxyvtsuvvr{}ytpmjgb^bji^SZaWRRUZajt}zp}{z}}vzox|qnpsx~xicu{ww{pelsjefimquwusx~skuvkhhijlnoqqrsqkq{z|xvuvxyww}xr|xkfdcceiov}}}vrqtz|umimuwtstvvst|vrnjfda]Z^dbWT_YONPUZblv}pr|{{}~|vy|~rr|tlmosw|xmdn}{xxwmbanjbcfjmqssrqu{xnpxmeeeeefgiloqsqr{{yusssuwwww{~uy|oeca``diqy~zspqty~wphejrussttspz}splhdb`^[Y\`^V]_SNORW]fpzunwwu}|xu~zz~{t||plnqtw{~yqhiw{|yyzqf]fmdaeilpsuusqrwtpxvhdccbbbegjnqrrz}}~vtsstwyzywy}{w~xic`^^_djs{y|ysqrv{{vogdisxwvvuqqwpmjfdba_\XX\ZYb[OMOSX_is}}stzrs~|vwzxzxyznmoqtx}~ypiq|x}~zyynb^kj``cgkosvvtqorqt}sgdccbcehknrttu~~{usrsux{|{wvxx|uhca_`chpx|x||wuuy~|wpidgrzzxvsotvqnkhggfd`[WXY`g\RPRV\doyw}}pp|}u{~xvvzqoqtx~|rlw|tx}|~rddoiaadimsx||ztonoztjgggiknprtuvtvzwvwz~{urtvkfdceint{}wz~|z{~~zuojjr|~|ytnxxtqomllkie^VSUdi\SRTX_hs}}qoz}uwpsyrprv|sq}svqdipfaaejou{~~|umglsliijmpsuvwxxt|~ywwy|{rlrtkgefhmry|uy|yupkjq{~yro~|vsqonnnnkg_UNVki\VUW]dnxqoyzwulvztsvyzpw{tvzjdnmb`bfkqw||tjcp~qkihiknprtvvuuzwvwz}ynhwqiecdglrywqu|~zwspkjpz~wpvvromkkkkjhd[PJ\lcYUUX^fpz}wop{x{nj}vssuy}zqqzwz~obfmc]^afkqvz||xodcxvmheddegjmpsuuvrqqsw{|tikvkea`adjrzzpmrz~~~{{xuqnjghoz|ustmjgdccdeec^UKPeh]XUWZ`ir{x}{qos||ulr}xuuuwz|}|zvpmzzz~~~|~tfbkg]\^bfkptwywriaj}{qjeb__`beinqtyxrpopsvz}~}wngqzphb_^`elt|unkqy~~}y||wuromjijp{{uzzojfcaaabcb`ZQKWeb\YXY]dlt{~xwvrrv}{rlx|xvuuwxyzxuqlq~}}}~{{~{ocgka[\_cglptwxvpgcpyuojea_^_bfjnruupoopsw{~|vmiuzuoic``chow~~unlpz|xyvtqonlkkoy~zwumhebaabccc`ZQO[a_^\[]ahpx~}w{vttx|rp|~}{yxy{|}|zvqmv~}}z{yoglk`\]adioswyzwqhhrtrqmieccdfimprvwsqrtw{~~wnnvusqlgdeglsyyrnpy|z{xvtsrqonou{zzwpligfgghhfc\ST]]]___afmu|{zxxz~tu}{{~~}|~zspz|zwolqka^^afkqvy{{xrilsporpmkjjkmoqsswzvtuwz}~worwqoqokijmqw}~xrrz{||ywvvvusporww}wqnljjjkkjhd\UZ^YZ`bcfkry}{|}~v{~wx~{sr{vsmptia^_chntx{}|xqkrtlmqrpnmnnoqrss{zvuvy|}uqxvmlppmlmosy~~xssyz~{yxwwwwuropsx~vqnkjjkkjigaYW_]VX_dfiou|~}}~~~zy|tw}|uowwpkltqe_^`diotxz{ytmowqhjpqomlllmoqru|vsstw{~~xquzqghmnlkmpu{zvrryzy}xvtsttuvuronr~xplhfefgggfc\W]cZSXagimrx~|z{{~~y|}yxrw~{wrmoxnfepvj`]^`chmruwwtnlvvidgmmjgffgjlor}|tpooqux{}|xrrzwhbejljjlqw||wtqoqx{w~}urpoopqsttronv}qjeb`_`abb`\WYc_TRYcilosx|~|xtzyy|yw~y|tqw~{yvsojjvzqf_ftre_]^adhlpstrokqypdadjkgdbcehknuwqnmnpswyzxtpv|qd`cilkknsy}}xusqprxyywqonmmoqsttrmm{xngb_]\]___]YW_f\SSZelprvz||xss~zxz~~ww{y|sqw~{xuqlhm}xma\hupe_^_adhlprrplmwynd`bikieddfiko{yspooqtvxwtqs{{pe`binooruy|}~zwusrrv~z~wrpoooprttsolo~{rjeb`___^]ZX\ef_XV[gpuwy{{zvty}|~{vy~|xux}xsnjjs~vlb_hqqkeccehknoppnnszzskeejonkhffhksyusstuvvutsw}|ulfejqsttvy|~~|ywuuz~yvutssssrpnlpzzsmifdba_][[_egc^\_gqx|~~||{wz~~~{|ysppw}ulfdhmonmkklnoqqqpoquyyuplknqrqnlklpx}zxxwwvutux{zvpkjlptvxz||yvuuw{~}zwutrqponmot{ytokheb`]\^befebbdkry~~zy}~zusuyyrlhgikmnoooppqqqpppsvxxuronnppponnpu}~{yxwutttuxxwtpmlmpsvy|}zwutuvy|}yvtrqoonopsx|}ytplheb`__adfgfefintz~{z||xuuw{zuokiijkmnpqrrrrrrqqsuwxwtrpnnnnnnnqu|~|zywvuuuuwxwurommnqtx|{xvuttuwz~}zvtqpoooqsvy}}yuqmifdbaabdfggghjnrx|~||~~{xvuw{~~zuqmkjjkmnprssttsssstuvwvuspnmlllmnqv|~|zyxwvuuvvwvurpnnnptx|~{xvutuvwy|}zwtrqppqsux{}~|yvrokifddcdfghijklosw{~}}~~}}~{ywvwz}{wspmlklmnoqsttutttuuvwwwvtrpnlkklnqv{~}{zyxxwwwwwwvtrqooprvz~|ywvuuvwxz}~{yvtrqqrsvx{}~}{xuroljhgffghijklmorvy}~}}}~~}~~|zxwwy{~{wspnmllmnprsuvvvvvvwxxxxwusqomllmosw}}|{zyyxxxxxxxwusrqpqsvz|zxvvvvwyz|~}{ywusrrstvy{}~~|zxurpmkjihiijklmnoqtwz}~~~~}~}{yxxy{~|xuronmlmnoqstuvvwwxxyyyyxwusqonnnpsw|}|{zyyyxxyyyxwvusrqrsvy}|ywvvvvwyz|}~}}{ywutssstvx{}~~}{ywtronlkkkklmnopqsux{~~~}{yxxy{}}zvsqommmnoqstuvwxxyyzz{zzxwusqpooqsw|~|{zyyyyyyyyyyxwutsrstvz}|zxwvvwxyz{|}||zywvttstuwy{}~~~}{yvtrpnmmlmmnoopqstwy|~~}~}{yxxy{~}yvsqonnnoprtuvwxyyz{{{{{zywusqpppruy}{zzyyyyzzzzzyxwutsstvx|}zywwwwxxyz{||{zyxvuttuvwy|}~|zxusqponnnnoopqrsuwy{~~~~~~}{yxxy{}}zwtrpooopqrtuvwxyzz{{{{{zywusqpppruy~}{zzyyyyzzzzyyxwvuttuwy|}zyxwwwxyyz{{{{zyxwvuvvwy{}~~|zxvtrqpoooppqrrsuvxz|~~}}~~}zyxxy{}~zxusqpppqrsuvwxyzz{||||{zywvtrqpqruy~}{zzzzzzz{{{zzyxwvuuvwz}|zyxwwxxyyz{{{zzyxwvvvwxz|~}{ywusrqqppqqrrstuwxz|~~}||}~}{yxxxz||ywtsqqqqrsuvwxyz{|||}}|{zywusrqqrtx|~|{zzzzzz{{{{zyxwvvuvwy|~|zyxxxxyyzzzzzzyxxwwwwxz{}~}{ywutsrrrrrsstuvwxz|~~}||}~}{yxxxz||ywusrqqrrtuvwxyz{||}}}|{zywvtsrrsux|~|{zzzzzzz{{{zzyxwwvwxz|~|zyyxxxyyzzzzzyyxwwvwwxz{}}{yxvussrrssstuvwxy{}~~}||}~~|zyxxy{~}{xvtsrrrstuvwxyz{||}}}}}|{yxvtsstux{~|{zzyyzzzz{{zzyxxwwwxy{~}{zyyyyyyzzzzzyyxwwwwwxy{}~~|zxwuttsssstuuvwxz{}}}}}~~~}{yxxy{}}{yvusrrrstuvxyyz{||}}}}}|{yxwuttuvy|~|{zzyyzzzzzzzzyyxxxxy{}~}{zyyyyyyyyyyyyxxwwwwxyz|}~|{ywvuttttuuvwwyz{}~~}||}~~~~|zyxyz|~~|zxvutssstuvwxyz{||}}}}}||zyxvuuuvx{~}|{zzzzzzz{{zzzyyxxxyz|}|zzyyyyzzzzzyyxxwwwwxyz|}}{zxwvuuuuuuvwxyz{}~~}|||}~~~|zyyyz|~|zxwuttttuvwxyz{||}}~~~}}|{yxwvuuvx{~}|{zzzzz{{{{{zzyyyyyz{}~|{zzyyyzzzzzzyyyxxwwxyz{}~}{zxwvvuuuvvwwxyz|}}|{{|}~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m25.bin b/applications/SprPoP/assets/packed/MUS/m25.bin new file mode 100644 index 0000000..6559022 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m25.bin @@ -0,0 +1 @@ +hzlpztpytncybxjehgl^{}d~tzmzm{}ztv~~]zsi{vrrtefzea}~yosthwx}wqzjcwgps`utxxe|Zyvfwyzlvts}zwvxtnleo{qvt{s]}y]s{zzi}ez~ykrtwlva{gpcuw|kefp~sfztr}k|~nxyakg~~vsruclubh|~y}q|un|{uq|rfyfnveizvsg|\y{pdtryusz~~xtvp{ick|r}ytxx_{y||atzz|nly{m}xu~hz`wni`|}csb}nsy`xr{m{yz|r|ttty_q{oswzphoxph|xx{zlkw}rusd|mvrkqszmeo|h}lhux|m{|x|nttifd~~uvsxevytzfuz||zvuw}pv|yl|drxm^~}vgve}oq|`{xxnywu~q{ptx^q{otwztl}jrrp~ywyr}lys|qhs}fxppvoivve|fovz~zoyvx|ed{qs]U|Vuzn`bVi||jj`qroywjggv{||w`z|thbulX{yF{~^ewrksrtoqzqtt\Jsevhp}]RWf}ihwxrj}w|swzpubxmiznuta|rr{yqh}soredxwrvtv|kuclqVsp{cLtrcqiee|{|lz~{|wpo~rimt`pPyynybgsxi~X~imt~qnfgkW`pz[v\v~`urx~|{sVwuwz{wixrY_xqx{wl~twrmn}vzbd{vqmg|kXsvvpv}rcz]wy^~Vbfw^}ht|~ty`tYly}|wx{tzmo|awz{wz}wb}ihbT~ygnysltrfofW~yjwvpyuvlZt{zy}woYxcr}inkizww`vjuizql_}lNxs}^sddjWww|nrzwmii{y_sexXqz]sxe\{lzhyx}p|k~|uqmpxwRr}yup~X\}wuhwZax}]myw|xibkrwp]pqvvsmqywzzvv{{w||erjo}lqcgRm}sqqaefxXnsgrxZkYtM`~t~hjzSndo~zeg\|tw~u~i}|LtxxrwWL|~dkmrnpujrekgl^rkkveS~j~{{cwaoyqc|Tsqy{l~sgr}nb|ekSIu}|d|op\{gW}ved>j|Q~n_tuzS^tX}puzVn}|{twycy~[b|n~ktbyrbdmugS`xdYj^mejsq{jjVWYjewjk{sztdutvS`HqjYaxyyuL`m~{ug}|gq`ces^rqnq_p{nFhhdtum]rgm}oolzxw|phfu{yw}l{`xzz|ngg|ixwxfnivxvmljv\ymp\|]|`cxoZt}l~nlaqw^qco}~_]y[ukvqw|q}ow{]wgsgw}ixsWnw|smshshds~se}}Qjhp~lfwwx\rsy{~onywtur~vzpnljvi|d^^ngtkgjl}z~pe]s~wvukuvpuQd]i{aryswttf}rknlwfzwwtpl線q~ql~βϴӾĪxɘиijt^baP]U642eB4UF~ogkjyu̬ȵvshΣ]agF^6HqspTQM[L?x|TsǾġ}gRn{~fLazcUlj^]I@Grimx[JMUSJbzvrmtau\vTxLZc&Wdyqz}}rȼqZ{VyXxlqfq}SI=OBNho}ncujmMui=YKgu֯rucacu\{whWHE9YSksɯȵǢlyerITwYEc`avtmO:.$RfeRaL]K=Y1>^?\_[EBV<|MBaRR4@QML6-YN.ccRnoou]cmxbpT^Vab8eZdMTZ2XYLechhysgfTblY;AWi\Xzx|m_a1IqLXWinny_^yk^nky˰ެܻȵlf[hx]jDGq\`iѼhsaPWL\.:Z]ORj7M=O`B\ZBgGXHfkIvV]M^qKnu_~q_Vbpv{zod~exslefSf_{_B[Yem~vk{om}zo{YpnY_7hNJgYga}|³ܱϵuĠѧsvZ]=/C-U\DZrSRZc/7]_RJhkOlZ>_RJ;;[>ZDKkDlkg`XgK^sdIJCR>)JJA.'1X`m{r}{rF\pwYPygdhkqefSfZCiUdj=i_mixga_k[K*D`UJS{q^jbszl~|UkRQ8@dPlǾ̸Ǧ¿༴x{a`bR[C(C@WQt|d|{^il]qNQhWV<^d@Y5R]RgS\U[h\~|quwnim}k`c|~mƺrl{vudM\GGZWiX[nWztx}rirpjfOeqa\ivxpvwmYzyst{r}nȳs}cuvq}{gzqT[bqbLaU{~o`dnxj[z}sukx[ZCfrOiFeqc~{ejt`|sdnmUr}nWCVxo|VYcnllei{}\i{uzg}bObrj]wqys|eL`lb\\gR_sW^nWF:ruc~atv|qk{ygYdLIi~ijǵþwkq\CZiLKSW=DR9GQ:&L-2H><=WVI3JWPhooga{}lpm|hawopfrry|tp`qqΫʾ~xpns{xhzvXlyY6ItPf^Oqejztmhͱҭs}}\g{ejnzkQ05+6KB5`WҲÛ}yxo}idu_@+VQ2K3M^R_fdPTc[omV>UtcEhz[Y_zv}ȭ}|ym~ynVIS:2L\cJZe]zurr{u|t~{jvnqm[uihiP\]ewqfZ~vuqm~{isytlW?KA*FbcTvuhyuzvf]^dga[XV^nkSalVXg\OV_gptof[TZk|ti`f|{spt||{ur{{~}}xh\fu{xd_elnjfd_VM@>SiYh{slktx`Wj[OcjZU^hryzskc_etsfZP]jlh`XQSZcloc[lxemli{~{~q`cormWJLSXXXXVOH>:J^NJme]v~vqoptwzpotre_agjgdabn}scwxfpujoxytrxwrxeciquwyytnf`hp}z}}}qfny}xeZ[`fiklhaWLL_n\asnvrtue[Y\\XUUZgo_Sik[etlcfpz|tmjprdftzyria^cjswnck|qh{fitwrkb]\\VJ<8EQXRDBIQUVWUOG=5>W^Pmwqoty}~xpeapxn~{xxxuqps~kvniyyjcflqspjc`co|zoeamy~{unikqx|vfeutbl}k]epvwsonmi_RIOZ^[K>=CILOPOKCF\nij|{oklpsuvupjjvx}{xttwopzhn{xjbbfgfd`_clvwoe^ajsvtqoptvsi\]ln\ZlkZS[cggec`[RICGPUTK?9:>CGIJGEEPdla_ysw}{yx{woup~{olnprqonqy{rq{{|}|vwwmkoty||zurw~{ru}|w{sorutplgbZQKMWcjiaXUVXZZWRLGJYhf]oy|~nuuopty|{z{vmmty{yvsrssnd\dspagxuhfox|}|zxrjb`fnrodXQOQTUUSNKMYhgY^uvhl|{{}xtrqsx}mn~~nm}zpllnonmnt}zsszwx|u|qs|~xphcfntuncZXZ^bddcabl}zuĻ}}x}{skdcipsodZTSUXYYVRPVdpk^f{ylpxw|}tnjiikqwypcgxxiixxokklljiimsxvmaWSW^cdcaabcaZTXhn`ZioaSPTVTPLF?81/5AKQPHBACFIJIE@@HXaZXo|~zrooprrpmkozyyvp~vkt~{pgdccbaadjqtqh_[^elpqqpookcYWbkbSYgeWPRX\]\YTNHCDJRVTLC<;=?CDDCEM[geYav{qp~}xyvuuuvx|zw{|}|z~}wst{{qkjlnpqpoowy{qkkpvy{|}~~|vpt~qw~nb`ab`]YTNIGKU`ghc[USSSSQNJJR`kjcnv~}}|uux}~|zxupg_amvmagwvjcflqssqnic__empng]VRRSTUUUW_jrlah{~spz~~||~{prvnwzsonmmoqv}zrorx~}unr~wkt|phinqrqnje_\^ekomf_ZYZ]_abchs{yzrruwwuqke`^aglnjaXRPPRSSSTZeprhbp|qr}ytuz|xuuvz~zoju|oo|xqmjgeefimni`UMJMSX[]^__^XSU`kfVT_bUE?=??><95214>IRVTMHDDDDDCCFO]jlgn||~zxzxqnnnoppqv~zryplv~{skgdcccfkpspja[Y\afijjheaZRT^faRP\c[OIJMOOMJFB@AFMRSOHB??@BEGIOYemiafy|v|{}~|yxwy|}|zvrnnrx~}wojhhijklnu|y~tkdcfkoqsttsokjq}ujmwvi\USRQOLHEDFLV_efb\WTRQPONOT^hjdct~~~{vogcfpvm`cpvnd`behiifc`^afmppkd^ZXWXXY[`istldl|xrwyszwpwyurqrsv{{tnkmquy{{zxsnijt|xkfpzvldbceedb_\Z[`fkmke^ZYY[\^afp{vy{~vqoppomiebacglnlg_YUSRRSTW]fmlc^gvypjoy{xx||spywpwztpnlkkmoniaXPJJLPRTVVUSOMR\ebVPYa]PD?<;9640//2:DLRSQMJHHGGGGJR[b`[_rƹ}z{zy{}~~|zxx{yurqqrsv{}x{}qqz~xrnkiijmqsrng_ZYZ]abbb`\WRQXbbXNP[`YPIGHIHGECBBEJPSTQLGDCDEGJOVahic^hxztw~|}y~{vttuuspnlmpuz}}xrmjhhhikov{t|}vnf`^`cfijkkigeirzzndenofZPKHGFDCBCFMU^dfea]YVTSRSU[cjkfeq|xrlggnuuj`cntofa_`aba`_^_bhmqqnida_^_`bgmuzxpltzzyv||rs||yvvwy{~~ytnjikmpqqpnjfdgowtiaeptne^\\\\[YXWY]bgkkhd`^^^`dgnv}|}v{}slihgfda^]^aeijid_ZWUUVWZ_fmpkccnz{smpx~yxy}{uwwrwysnkhggggd_XPIFFILORTSSPOPW`cZMJQVRF;521000026854444569>EMTY[YWUSRRRRUY_ehfbgvƾyw~xqnmmnmmllmpuz~~{xuttuwy}{y|wtrrrstusnhb^[[]^__][WSQRW^]TLMVZVOIGGGGGFEEGJOTXYWURPPQRUX]dkppkhp|zz}z|{tw~|uolkkkihfefimqssplifedefhlrx|ysouzz~ytqooooolhb]ZYZ]`cdeda_]^dif[RRXZSJC@??@ABCFIPW^ceea^[YXWXY\binokkt~yx|~sjjquphb_____^]]_bgmpqpmkihijlosy|xz{}zxvvwxzzxtniecdegggfc`\Z\bhf\VYad`YTRRSSSRQRTW]befeb`__`beinu|zy}z|wqu||vnhfedcb`^]_aeijjgc_]\\]^aekqsohhq{{uqsy{uw|}ysmhebaabba^YTPNOQTWYZYWTQQU[[SHDIMJB:6568:;=?CIPW]``]ZWUTTTVZ_gnqonv|st{~yrljijjjiiiknsx{|{xutttvwz~~x{}yusrqrstspke`]\\^__^\YURQU\^XNMTZYRLIHIIIIHHILQVZ\\ZXVVVX[^bhpvwrot|z{xru}~xqliiihgedcehlpqpmjgeddefimsyzvppz|y|~zupmkkkkkjfa\YXZ\_bccb_\Z[`dbWNNTUOGA>>>@ABDFJPW^cdc`][YXXXZ^cjnmjlw{qouyumfcbaaa`__`cglpqpmkiiijlosx|x~}~~{yxwxzzzwsmhdcdefffda^ZY\bfaXTZaa[UQPPQQQPOPSW\acba^\\\^`cflsy|xsv}}{su|~yrlihgfeca`acfjlkieb_^]^_adiorpignx{vqqw}z{~ysnjhffffeb^XTQQSVXZ[[ZWTSV\_YNHLQOH@:889:;;=@ELSY]_^[YWVVVWY\chkifjw»yw}}unjiiihgffgjnsvxwtrpopqrux}~~|~zxvuuvwwuqkfa__`aaa_]ZVSTY_^VOPW[XQLJJKKKJJJKOSX\]\ZXVWWY[^cipuupnt~~}{|zsu|~yrljiihgedcdfjmpomjgeccdegjouxvpmt~~xy~}||vqmkiiijjhe`\YYZ]_abb`^ZXX]a^SKKPRME?==>@ACDGKPW^bdca^[ZXXYZ^cjopmluzqqwyumgdccbba``adimprqnlkkklnquz~{}}~zxvvvwxxuqlgcaabcdcb_\XVX]b`XRU\_[TPOOOPPOOPQUZ_bcb`_^^`bdhlsz~}xw{y{vpsyztmhfeedcb``adgkllifcaaabceiotvsmmu~yux}ztvzzwqlhecbcddda]YUTUWZ\]]\ZVSRTYYQGCHLIB<99;=?ACEINT[`cb`]ZXWWXZ]bipsqpwþ}uu{}xpkhgggffeegjnsvwusqppqrtw{{~{wutstuutqlgb_^^_``_]ZVSSV\^XPNTZYSNKKKLLLKLMPTY]__]\[Z[]_bglsyyvsw|y{wqtz{vojhhggfdccdgjnoolifeddefimrwyupqz}y||x{~~ztokihghiihea]ZZ[]_aba`]YVVZ][QIINOKD?==?@BDEHLRX^bdb`][ZYYZ\_ekoolnxxw|~zrlhggfedcbceinpqpnljjjkmosx~{x~}~}zxvvvwxwuqkfcbbcddca^[XWY_b^URW]^XSOOOPPPOOPRV[_ab`^]]]^`cglrxzwrv~}vw}{uomllljihghjmprrpmjhggghjmqwywqou~|wx|~wx|~}ytpnlkklllifa]ZZ[\^__^\YVUW\^YPLOUUOIEDDEFFGHKNSY^aba_]\\\]_afkqrplpz}|{uqpoonmlklnquxyxusqpppqsv{}y}{z~}ywuttuvvtqlgca`aaba`^[XWX\a`YSTZ^\VRPPPPQPPQRUZ^acb`_^^^`beintxxsqw~xzytqppponmmnptwyywtrponopruy}}wuz}|xw{~~|xusqpqqrrpmhda``abcba_]ZYZ_b`XRSY]ZUPNNOPPQRSVY^cfggecbbbcegkpvyxtsyzzztqpoonmmmnpswyyxvtrqqqrtw{zx|}~|xw|~{xvtttuuutplgdbbbccba_][Z]bec\WY`ca\XVVVVVWWXZ^bfijihgffghknrw|~|xw~}x{zusrqqppooqswy{{yvtrqqrsuy}|ww}|{~z{xsuy|{yvsqqqqrrqnkfca``abba`][YZ^cc]VUZ_`\WTSSTUUVWY\`ehjjiggffgikosy||xuz||~yvtssrrrrsvy|}~}zxwvuvwy|{z~{|{vw{~}zxvuuuuuurnjfdbbcccba_]\]afga[Y^dea]YXXXYYYZ\^bfikkjihghikmquz}|xuyz}}xvuttssssuwz}~~}zxvuuvwy|}xy}|}{~xux}~|ywvuuvvvurnjgeeefffeca__afig`[\bfea\ZYZZ[[[]_bfjlmmkjijjkmptx{|yuv|}|{xvuttssstvy|~~}{ywvuvvx{~~yw{}{|~{|}ww{}zyxxxxxwurnkhgfgggfecbabfjlga^bhjgc_]]]]]^^`bfilnonlkkkkmorvz}|xux|}|ywvvuuuuvx{}~|zxwvvwy|~xw||z|}z{{vvy}~|zxwwwxwwuqnjhfffggfecbabfjkga^agigc`^]]]]^_`cfilnonmlkklmorvz|{wtv~~~~~{yxwwvvvwy|~~|zyxxy{}{x{||{z~xvy}~|zyxxyyxwtqmjhghhhhgedccfjmlfaagkkhda`````abcfimopponmlmnoruy|}zvuz}|~}zxxwwwwwx{}}{zyzz|zy}~||}z{}wvy}~|zyyyyyywtqnkiiiiiihgedehmomgbcimmjfcbbbbbcdfilorssrppopqrtx{~~|xw{|}}zxwwvvvwxz|~}{zyyz|~zy|~{{~~}~|yz~}wux|}{zyyyyyywuqnkjiiiiihgffgjnpnhdfkoolhfddddeegilortuttsrrrsuwz~|yy~|~}zyxxwwwxz|~}|{z{|~}yz}{|~}}~}zx{yuuy}~~|{zyyyyyxvsoljiiiiiihgffimppkedhnpnjgeddeefghknqsuutssrrstvy|~zx{}~|zyxxxxyz|}|||}~zz~~||~}}}}zy{zvvy}~~}|{zzzzzywtqnlkjjjjjihhhkorqlgfjoqpligfffgghjlortvvuttsstuwz}}yw{~|~|zyyyxyz{}}|||~}z{}{|~~}}~}zz}yvw{~~||{{{{{ywtqomlllllkjiiknrsqlhinrrpljhhhhhijlortvwwvuttuuwy|~~{xx~~~}}{zyyyyy{|~}||}~zy}{z|~~|||~~zy{|wvy}~}|||||{ywtqonmmmmllkjkmqttpkilqtspmkjiiijklnqtvwxwvuuuuvxz}}ywz~}}~~|{zzzzz|}~}}}~~zy}~{{|~}|||~}zy{|wvy|~}}||||{ywtqonmmmmmlkklnruuqkilqttqnlkjjjkkmoqtvwxxwvuuvwx{}}ywy}~~}{{zzz{|~~~~~{y||{|}~|||}~{yz~~yvx{~~}}}}}|zxvsqonnnnnmmllnquvtokkptutqnllkkllmoqtvxyyxwvvvwxz||xx|}}}~}{{{z{|}~~{z~|{|}}|||}}zy{~ywx{~~~}}}}|{xvsqpoooonnmmnosvwtolmquvurpnmmmmnoqsuwyzzzyxxxyz|~|yy}~}}}|{{zzz{|~~~~{z}|{{}~}|{|}~|zyz~}xvx{~~}}}}}|{yvtrppoooonnnoqtwxupmnrvwvsqonnnooqrtwyz{{{zyyyz{}|yz~~}~~}|{{{{|}}z{~{{|}}|{{|}~{yy|{wvy|~~~}}}}}{zwusqppoooonnnpsvxwrnmpuxwurpoooooprtvxz{|{{zzzz{}~zy|~~}~~||{{||~~{{|{{}~~}|{{|}~}{yy|{wwy|~~~~~}}|zxvtrqpppppoopqtwyxtonqvxxvtrppppqrsuwy{|||{{zz{|}}zy|}~}~~|||||}~}z{~{{|}~}|{{|}}zyz~zwxz}~~~~}|zxvtrrqqqqqppqsvyzwroptxyxvtrqqqqrstvxz||||{{{{|}~~{yz~~}~~}}||||}~~{z}|{{|}~~}|{{{|~~{yy|}ywy|~~~}|zxvtsrrrrqqqqsuxzzvrprvyzyvtsrrrrstvxz{|}}||{{{|}}zy{}}~~~}}||}}~}{{~|{{|}~~~}|{{{|~}{yz|}ywy|~~}|zxvtssrrrrqqrsvy{zvrprvyzywussrrssuvxz{|}}||{{|}~}zy{}}}~~~}}}}}~{z}}{{|}~~~~}|{{{|}~|zy{zxx{}~}{ywutssssrrrrsux{{yurruy{{ywutssstuvxz{}}}}}||||}|yz}~}}~}~~}}}}~~{{~}{{|}~~~~~}|{{{|}}{yz|~zxx{}~}{ywvutssssssstwy{|yursvz|{zwvutttuvwyz|}~~~}}}}~~|zz}}}~}}~}}}}~~{{~}{{|}}~~~}}|{{{|}~}{yy|~zxx{}~}{yxvutttsssstuxz||yusswz||zxwvuuuvwxz{}~~~~~~~|z{~}}~}~~}}}~~|{||{{|}}~~}}|{{{{|}~|zyz}|yxy|~~~|zywvuttttsstuwy|}{wtsuy||{yxvvuuvwxy{}~~~~~~}{z}~}~}~~~~~~}{{}{{{|}}~}}|{{{{|}~~|zyz}}yxy{~~}{yxvuutttttuvwz|}{xttvy|}|zywvvvwwxz{}~~~~}{z}}}}~}~~~~~|{|}{{||}~~}}|{{{{|~}{zz{|yxz}~|{yxvvuuuuuuuwy{}}zwtux{}}|zxwwwwwxy{|~~~{z{~}}}~~}~~~}{{~~|{{|}}}}}|{{{{|}~~|zzz}~zxy{~}|zyxwvvuuuuvwx{}~}yvtvy|~}|zyxwwxxyz| \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m26.bin b/applications/SprPoP/assets/packed/MUS/m26.bin new file mode 100644 index 0000000..ac75614 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m26.bin @@ -0,0 +1,3 @@ +vmfa__bgmt||xvtuw{}~~|yutv~wqlmoszwh_m}trstvx{{jm|tmhb_`ciq{|gbrpuwrg`cqzijywxwxyyyxw{|t{omosvvussuy~fcobemuz~ur~jfowrfbo~~padzxx{xqtljnrttronprvy|h`yskpxvv{ooxzqegv~yx{~~{th^aw|{{vt||qkjmpqokihiknqsv{}xkbnyv|z{~x|}ogn}yttuurlc\_o~unoprywokjkmkgdbabdfhkosskch|~~ylkx~vsrrqkd\\i}ykiknt~wnhffggeb_^^_adgjnpleev~slt|usrple^\ex{||kdeipzyohedeeda^\\]_adhlnlfcozorxtrplf_\bs{ymbaelv{qiecddda^\[\^`cfjmmgbjsq||vspmga\_n{x~pc`cis}vmgeeeeb`]\\^`cgknnhbfztqz}wspmhb]^i}{w|sd`bgozzqjgffec`^\]^`cgkooiabuvpxxspmic]]fxzw{uf`afmw}umigfec`^]]^`cglooia`p~vqxxtqnjd_]duyv{ufabejs}xrmjigda_]]_afjorqi_]l}~vpxxtqnje`]bq~wv|ugbceipz|upljgda_^^`cfkpsri^[i}|vqwxtqnkfa^am}vu{ugbbdgmwysnkheb`__`cglqtsk_Ze}{~yrvzurolgb_`j||vtzwibbdfkt~|upmifca__`cglqtsl`Ybz}z{{ru{urolhc_`hw}wtyzkcaceir|xsokheb``acgkpttncZ^s~zztt|~xtpmid``es~wtw~od`adhoy{uqmjfdbabcgkostoe[[lzy~wsyzuqmjea_cnytu}sf`_bfmw~xsokhecbbcfinrsph]Ye~{y|}tv~xsokfb_ajz{utyyj`]_dku{uqnjgeccdfimprqk`Y_t{xzwtz{uqlhc``gt}xtv~pc]]ahr}}wsoligeddegjnppld[[j|yz}tv~ysnid`_co{vuyyi^Z]do{zuqnligedegiloomf^Zbw}yyyty~wqkfa_ajy~xtu|qbYY_kw}xspnkigfffhjmnmib\^m~zy}uu|ztmgb_`gt{vtxxh\W[eszuromjigfghjlmmjd^]g|{y|ytx~wpjd__do|wtv}}n`WXan||wsqnljhgghiklmkf`]cs|y{~vv}zrlea_blz}xtuzsdYW]ixyurpmkihghijllkfa]`m|yzxuz}zwuux|~}{yz|taRLQ\ky|{|}zvpiiilpsvtpeVHAOtrj}|~~r}j]QGGUnmtffknle^]ft}wykecaabfmu}w_GQ~rmvw|{y{vqjxrhc``ah{}xnnswvoe^\aotf|o``dhknrttttsodSI\~xuq{zvw{nfaZVYbpmrwot}tkhkrwvpr}udVXejfbbdec[OGRr~~|vwy|p^`}zzyzz}z~yihwzj\UTUXZ^i|vnfdnzwibdbXQXk|ztsv|zp|vnrrabjjedjt|}wu}}yz~wdVX^aahwxmd[TOMNSYbih\NNaxtrwjbfy{pfeq||wy|uxwld_^_a`^\^_ZOOb}|woe_eu{y~|pdbovkaXROOS]jw~yqtyslfdk~rjptoga__^]ZWXbt~{uwxogbbgp{rekss|}xqjbZTPPYnvkeirth\X[ZURXcjhfiouwwvw|tu~wux|yphddfilotzqq|p_WUQMQas|yri`YUSSW\beaVOVk}~vszwoowzqc^cjlnuxne_]^cjprl_NIVp~zungenslw~{xw~~y}}uor{{umfa]\^aeiic]czvi`bp~rtzztleb^[WTU`s|xx{{vwvuy|tmihjow{qt|xuvz}{urpmf\UUavumlsytfXTSSS[hsslc\Z\dmv}~rkv~y~{rkfb^\^j}vu}qe_[VV]jstqlhd_\[[^adcZNJXt}{zwuv|vw~yhZUW]hy{tnifeehlpneXR`{~wohekzyigr~ysos~yonpvyqljhgggijje[RXszspu{qrz{tle_XSQXh|{unjmyzokmtyurppruy{xpkuxqnt~|tmhc_`gv|wqpu{xiYOMQ[hsvrke`\ZZ]dnw~~wlhww|{skd]Z^lz{~m^XZakuzwqkgecbbabced_VMOd~{vqopyyw|ygZUW`n|~yuqnlklmnle\Wazzslimysjksyywvvuspr|}nffnzxqljjkmonjaWUfztu}~pmt}|ume^XVZgx}wpkimv}zqhfjq}}zwvuvxyxsjem~vrs~{slfaaiy|upnpw}{n]ROS[gsxupid`]\\^bgmolggvxqjd`bl}}oaZZ_jw{rjda``behigaVOSh~wrmmt{{vg]Z\alw}yvsrqqqstsndZYg{unknzqmqxzwqmihhjo{wkeeirytpoprsqj_Y^r{uu||qqw{{wrmgb][_kz|unlovzui^Z]clw~{yy{}~}wmddryuv~||vpjfflz|smmryzqdZUUW\fqxxtnidbabdghf`ZYd{~xsnifhqzz~xjb__ahs}{slfcbcfhid\ST_ufkrwvznnx|ozfVBGTFVbfxzwqcbUcxz~r}iqccq}mlwv|hqnjnghcrqw}r``\kt~vjrtWTA9Vuz~ll[byuhLT\Xclz~ysew||dbkWWUQlk`pnfhbi^cpoýwhywvh}ukpp|v}rk~zpiX_U?:4@GWtrt}sxtV38c~~åre}lbHPWNU`t|tofJNxujXLZWLNKUPSYKXbga^N]{~|r}yih`ssZL?EJVsw{Q?\~vf}~tso{xyTZw^\GNTFCHYs|[No~wl_liZZVbcjs`\sžwohgUZnqx}zzr~}lwoYTGZf\TFBCHbgniUjuSds|ţugmo[_eTKHNdsuK3H}id^Q^]MJEOT_qe^kgx°uxuccsqr{l{rhS_ibd`^ZSeeg`J^qA<=Ny|pvtxotgmvh[UTbkgCIzmjk`npa\V\`h}vq~uuz̷}lsm[Vd^X`ix~gSa{t^dkbcgnslvoioVg[K=:^ygg[cqgYRKSSm~}mD>fwYT\Vioc`]bgl}z}λx}kdpk`cg}nOR~fgk]XZbnmyn]kvbESWF6';O_y̭}tch{wmh`d_p{|]StZNUQfrieaehj{wsskg¶k{vn`Vb_SSTepzTO~zz~odcgvzrw|rS]iWJ5>DHaxrn|lURdd]]X\VaicvfceOSMbtpporxx|yjuĞ{pdnk^^[gm|QCi}kuknueVQQ^d{~nprnz~vW_o^VCGF>Lcqiùj`mmgjgmfnsgp}abdDB7GZZ]`djjzukzuaaxxrhtsed`ilyúaRtzcpnwyia]fj}x~`fl[WEHE42?^|^Ryobljchirnu{mpimwRL>HXWZbituytmxyxsitrc_[ack}|\LkiISUbxuibZ`bwnvwbbSYXD98JigWw}puodefqpw~nlzzuWZuND27FCBKVgm{yvvƗ}tpidilrſo_|vNNMZtyrnefcs}xv}clpTSGOSA2(+?YmZw|plkv{}]_eZD@KGCGOblvghWX˜ufqwj`XZ^cusrpeZQHMhttxuxqz{}gtf_PXaUF;49DliVopinomd^div|~\T~icQJQICGJZexnN@fȪvzvqifgj}}zqgeUG@Q\`imuru{rz|ox~vzhT_meQR^YQJBA>TiǾu^rznmkyooqyq_}mj\VZN@?BOUmzoI(5tz{|pklmml{|zŶyim[MVXWdpzoTWxud`cZUVRRIQUVx®vT]p`_Xhwwxsqps{qomfcmfUNIS[og=5]}x|sos||yx]nqaNQNCFQhw|{mTQy|ppwk\[^fcjg[eǶrtziTN=?_]fstž~zmX`^WHY{{prd{pw~oxpo_6?m{zttrrgq{ȗc_^JIC@Ws|~dSEcyYWiWRBOtox{q]c|nS]mzdbVX|ŒoT_h`kd\fk}xĴxeUFVh_[`Y|ump}pH=]|ySamu`sckUQTgyuautƔrxtp\HQblyŷxaLW=Di}{fIZzwww|``sr|osvIJunsrtpzjlejVDDO[»a]g7Oo}bxtgZpiV^hpgof`]naYPJ@S[`sǭfkUA-;:DkskDRxsxzss~wUZfdKQ[domnv{x}i[QSP@'#);XòyZ\_NYnffox{k\tslu|sdc]cw|qyrZGdbmgkdT_e|f_KDDAM\h}itZNNQHZecyrxKhu\gnww{}{wbwufRapߴr}tiXT`ie|Zmr|wzmWXhnZdpSD8TJEZvx\90+4HsýL]rbbawqmu|}eG[ssp|w_Vcs{~xe|ffRNU\YVM]i˴syjLLzʬzhkP@^VNczulVqcjohlsbopjyobXjutV`zq~fjpm]bcI;>]Sk}R{__RSSiej}~{a}joRev~otv[WWcpqieviiYoy__kWsged]t~|jTtik^NQZaPk»uTC]g_uy}rv|x{nmjfspZ^~k\~`hpoo^pe[m}[_dR1.;TnrdaGkipwjTfmum}iqbWSHdmory}{ykkmfTJ7:P{~T_qg`LRwtw~|oJeqjf~q`_ZZpmbmP\di`hXT`zΰry}[GAPMJ\i\]ssjnv}}|wxNXfr{msl^iqb`m_NEIlhppgpeaLR_t}~tdo[\p[\qpS9?|tmjmujfw{yqp\Y}omroz[EtpXhgMPdid˸uOmsM@Mmui|pp~v\mldYhy||^ShyogcxjlsNkiMH73;coP>fyvvinoog]y|{vmux[dQKSqus|̱{WirkOLB5J_ɨ~bqsKZPPfz}j{}r\YqoZjtcccgo~yVZwRVpnp]fZ_pýqz{lZKC@UEXzypiQ^`ka_oqykL|qXe~xhylWfoƏppitcaQJNikq~kl}r]YROZh}oobNV[SPn~rg@-\poopvtphiqwje}vlei|nbthxzjMn}kbjiMRW^fvɰbhzc44Bt}ztv~x^bpniidkbbl|qojYbmzsjfon_ov|oizcHI=-;Z_?Mx]lkmaegjey~zjVfuyt`]SSbj~ǚrWcxfUCB;@Usp^LKYR^nqim|dVl^b``o{ubljvumVPw{aVl}xggh]j}zyd[TA?LPTuomfZZXYXdYgog{w\UWvqxxoeUVaxrlvpob_TX[tvuY|krkSXTZjszthqlRTUNNYy}YCBhlujqr}z}pqwwdmwcllhb|wkxwYfqssoifhS[glnűjeuVD(4Fm|opcGTvajoW[|tw|yx~UhrVSrcaomczuebo\RZR84>JɫoBl|]ZTUmx\gsjnzr`Cnug}}y_Shqr|wrnШ~YW^Qeh_S_Vʺ`xgKD8QQJ^y}xSOVU\py~t}e^lowufpk}p\c`q}pVJ7Kq_oiuzb^NBXRdw^ixfZgT_nH,Ku^k}}m[azgwtZcvrf]Zko|XV{rUbk_[gcclXwnKHLe|ykvok\iyfqg^`z~}~u~dVaheZ\xv{|~Wns]I4<=R`Odut|poc^Oblasyq}ybLdq|ucl~kgcL\y~bXB:Nbbhss_zv]P\XPWtmzpr]TKNav}qcE:Gpyjy}qh^Lauv||omxseszmO^iahom|lcrXPQGY}kXO`^TMiolp{syrgjsbff\o{qrv{x~wfy~hepg_i\kk\y`=>BZlrtr{unkrxrn}|qkigroohp|yaqhek[cmvu{rbUx{UC6Nuwl`gZPsynihpmon|ry~}ypgaonlltyxihfaS@:@ijEFp~d^h}ttqrxiWTevrjcmuyybX\jgg\QUoſyo~lK@UONZsobyulacqtz~{ei|ran}}ieiz{|fGAMY\hxyb`w]RMQZsxedseVns]WEUuwvy{{b\`lsujuntxq^XVXjxb[uxohd_\feNRoaWl|uvlttpo|t|pkcghj||nulx}optiiovlqpr}rbriTE=7Sut[NKRwyy}vnhhp~vtqzjx}nc[_slupgjWNM<:R{{e[hkhUH_lsw~y}zx[Jatotok`o}}iZ}u_glb_fRP^{xzmF@IJDXd}vnzphc^nfew\Yys|kv{|xiW^fwufHBLNZcs|o^nPR^X\gnqribNPatz|bJ:3c}zyvsaWcanwv~ygyslmlzn[Ufgjzwi`uwng^\NKatzm\SjcCAh|urhjq^dulneaelg{ts{~}|vlszrqejjb\soj|U?A-/NtygMARptniclh`ryz~{veZpngb]r~yysvzu\jiVDF63KlqayrbRJNfrqvjoz~w|YZzysh_x}uked|}k`PUvZcod_ZUKUgr{eJCE@N^r}~yorfcTX^b]n}}}qafr^cn{tn|xgYRPa÷~~xyjTGJLZr|wkfs~pZZVVdjoz}~cZSGT_s^E46_{{w~pecagwkxkdhdq{kggfz~qhvrtjb]ZNYev~k_iyn]:.HxvdbjiSj|jgm^afep{zz~wguonwvqwqmvpknvz~tdKF9(:[pJ=bwipp|tkirln{zpk`Wr}ztvohu{}yxlooYbaWPNAG]ywVOPVS]f|mdnchgcrw}taV[ygdqmpnc]fivnSHMQ\cm}gdqs`aVYakj{~ho|eZ_s}ncXMDizpuwmab`mwwjd~|q{~reh`bpywbj{qdb[Ycj~tnRI[ka`s|tnvspz}kdfliqu}}w~{snhkikky~qYf}eZGBAUrs^PMY{uqkblu}|wyspeuyidkyyzz}}f]]VWQHITpô|sgqnfZZft||{x~lSYytw~snxq_uv^]^bhlb__m{x_NGMTamws{|b[_jgozad~v|wqqvzga^pyv|}iVNSfmy{rh|ocXR]ht}sizxqig_k|u\B6Uxvrhbs{y}m~shg{}vjhcixxeowlkjkijjnylfbevhPHZswgksc^c|ypoljg|~xu~}pjspomjw{{zy|jzlZN:=IcsSHVnxyy{sr{vs|y|xsjaby~rciow{y{}_ikdZSDGQ`}vlZSSZbvr~yX[m{vq}{vox}zjV]xbiqnpc\Z[^tZTLGUYd{~msk]Scjmox~wmxslhn{xvebUM[zqc[SP_jmse|~yi^UTanq~zvsraUV[_xvYL9=dxtfbktn||v|trkiw|pl_cq~rlp}znpbYZ_d|zlkjuqX99Nmscf_WUl|trkhpim{wr~~mpsirov}tyuiokoq^QC41@XeIPgurglxpqrnqxx{qsm^f|{uhelqzzqejrvjhh_\J@@EVt]RHTS]t}|~nbttieknrxqqv}kZJbujkopeVWSTkr]SKGOX_{|snl|~nfa`b^lov}pvfTY^qz|d[R=@XǴ~gaYSZfk~qopsuzpngadkjvunwvqfVNRVe|tYCHPZ^m~uehhkq{zejjdkuy}~qw|{uq{nmp_\`al_goW@08Dmk\RGTx{rvoegnju{xurjcqflywz{|swrq{vp`XOA66=Zt][ogcefps|{xv~thZi}rut|simx|q`YZzmec]`WOMMV~gQFDW]fzsr|}fhhhhns}r^bq~tn~ztrvsgcVQ^zxk\NHYkol`e{rh]_jkv{rwtbZ[Tj}{l^J18b~}{j`ev{~}rzlcl|}{rmox}si|qog`]aak{kZQZm]OLZurkmpfbhsegddj{{vsxouzztqvrruywvr`SF:?IcpUHVu~yvnmut}rg_nxmlo~|zl`XVSOILTcȵswq^ZY\f~hjxxpntu}~wgY^{teachdeacfv|k\RNX\j}whmo^`bhmu~yiesxkn|~xuyvplaYR]tǭz{uvlc^dnuk_v{ysja_iq~xss}sd_bddu{xp_SBIbs}rouuvwspknx|ytqx}sswphldcirr|z`W\s{kODDX{vj]XVh{tkirqswyy|ymosrnsuspwxt~}i`\TG?@Nx|\VdwzmlxuwurxsaTayrqqqywrjpm_Z\cYTOKVwylUO[W\p~tjuxqefrty|}qbYoveamtnecVYkraOPX\bx{ojq][ZY_rw~~rfyrc[`w~r~|mcPBFUyo^_dio~zvty}zztn]]`ak|tor|xvh^YT\n}|n\JIYc`pvpohmqqrux~sqlqrv}|pkroy|w{xoprndd]evpcpqQC:Mpüv]U_rwruzxy|~}kcm}{vvvw~xrk`gpc[ZTSMJTl¾oYR\^fuz|yw~tqqpt{~m_Zly}ojihdaVW`uyjXSZekzzreassnijksv}znwvjb_mzvqibSEBJp}{lfhpv~{qr~xu{}|srqqt|rlz{sia_]`k|raUHJWbag|xsnrqpurninqw{}~z}xqnmhjgnwfboxnYNEEKg{jYR^r{tqlow~|tpgem{sry||{z{wsu{{we^XLJHDLcsquysnejw}~|z~x}sa_qtx}{vxzoie]fk]_[XXXQVc|w_YVZcpzzw{xjbchgq{pdYj|~x}xilkijd[UYc~sb]Z`mt}urq|yhc`[bfnx}{syylcZ^fu~xvqo`M@;Pumiiuy|sotzuyxmghflqw~tqzvieb_Y\gtvg`[\ahXO[t~pjjg_iz~xxrrssx}zuuuut}|ux{{zurlinkfgoxwptnVH?9H_|y`SYm~|zrnqwtv}}zqj_[h{tooyy}~{y}tpkmt}}pchaVKD>GYt{n_[`ipwws|yi^p~|ysryy{vrunaTTmndijd^SPPXhsbZTR\erwtqne^dimpx~yrnq|}mhip|vuzvqdWQJJ[zwj`[Ybnwvpvxmc`_eot|zut~m`VT\cu{qaH?>Okxmghppx~zw~xuqmtztqllr}|ko|zple][bgvqceoshV@>Qlxf\WT\|~ztmsqqvy|~}wvmpzotuwz~zyxonopsp^PG:5BYznX[nvqps{~~y}~vkgfr{tpknw|wsi_^lxif^YSLDGVnhYQTZes{wsoq}|utrtz{}|zy|g^_mzuqokkcYQSZq{eWRR]gvrdgwxqpkmrw|wt|i`^guxvl\K<7Es{lc_fovyulsusqttv}rhg~zqkc^_duseYJMVYVatvjdigjt}sliflx}vtw~zvvtqlnovplxr\L>;D[xzbNJVo~ussst}xpqw}vnow{vtow~rg^RJC>FVrƽwrrrlfbiw}ymjz{z{wwtvxk`VZx}njc^XUNPWjzbWQVaq{sjemystwtuz{ocjwzrpwzsmhbZQPRdzƶxi^X\iv{kel{uplsx}~~|w~rg^]]i}}qi\L=4Bg}rjfqyzqkly~{{x}}uqxxpgb^`dkp]RS[bVMQe~qgghcj|zolkkn}}}wonx~xrrmjknp}~xiWLB;DUq}dTWk~}zsswv}wkbg{zw|~zrojimxibYRLEBJWn~|rgehnyunz|uopsvxi^RTk{jd`]ZQQSZgvib\]gl{|xuts|mdechmr{xmdjttlllv{pmkf[USLP`{vqjeemtwqjszpgdfhlvy~~|ufYVZak~qncTFCEYs|rpqtx{yz}|zxy~zoopqsz{}~w|yohdZZ^dmzc\^fldTIN]zwme^bn|wqpsxw~}wwomjhhwz|yspmhfjip{~rbYOEADQl~memw~|wy}{turu{~og_`o{wz}~zuqmfbei{yja]ZWLJIPeob`eeny}~~~~uwwogkruz|}qi`Z`u}rgehf^VTQZnuh^^bgn~zwu}sgdaafrx~vpp~yjaagwzqtsi[RE@Mi~vigimr}}vv~}vrhfhhmy~sr}vqh^[[apxpm`RLSZafu|roiikor}{ztux{}}wrusu|}y}|upqpgfdgrpfsyiUGBGUspbWVh}zyyz}xslllp|{ttvw}{z|~ztrmotzypd\PFCDMd|w{znigjt~uhdg{~~zttx|zqd\\g~slhc`VSQQ[t{hZU[`l}zmku{qstux{ulirz}vsz|vsph`[PMSew~qc[_fn~qr|ysmjnuy|xustxkgdal{|tk\P@8Hgwkfjmuxomrsvttx~{qjq{wpjhihl|rdZ\egXNN\sug_\Zbv|ztont{~yyyvz~}|}wvvtqx~pcVI?@KblWSas{zzz~yplsxrorw|tlfgm~ymb^WOGGM^zo_\_fu~zvwsmm||}{zz{wl^Ydw~slif^WQQWjtcWU]dt|ne`o|uuuz~vovwneht|vrg\RFAFZȻ{od_dlwvuyxwyywuvz}squyka]]_oxl\I>=K^mskhkpz|sljmt||uos|vtjdcdfqtebgkcTFDKbyi\SXfzrqnqs{~|xqjq|~~}vonjkow}i[OD=Mlyogjmt}wrvyqqnknsw{y|th[UQS`qvk_PFBN]emwtqossu{|spqw{~{xstsssqpt~smf`[XXes~lemtnZJEEOeynb\ey~ysqpov}xphb^gvzxrmicaemtocXMCBCI]w|tzzurx|||zxxzyxiem{~xrx}~{}~~uodZVW`xuf^\TOLMNZnvhdchpy~{y}wledgir|{mb[\lvwx~~sihc^ZUPPZh~ujfejs{~vqs||nhebdiowzyype_\akxyqlc\OC?Idzrpnuz|{uy|||snnkmrv}zu{vld_^\_ju~n`XW^baYVax~sjggeo}zxxzz|yvssprsty~~xtpkkjihntzy{|uiXLFCMc|zi_ew~|~zqgbbm}zwz~}}yxxsmkjir|zic`YPLIK[qsjgfnx}snmw|{}|~~zxnb\`n|oghfa[WRXctwhb`ciwtnqyqlloy|}tkmxyqlt{xusme\RJIOeumiinv{}ywpnnnu~|sop}yojefkv{ti]OGIVco|tlklpu}}xtruxywyx|~xy|ywvqommqwqgfmqi]QLO`wtcWW`w}z{wy}y{zxv}yutz{|~~{yuvuw}qf[RLGIUmzpnorqqx|~~|vngl|yvtw~yrkd^g}ule`YVPNTe|{g^^alz}rljkw}~~ujdjvyz~wqkfaZQOUcuwg^]clzvmoy|vvvx{~~||zx{yogcjt|sjaUE;=Pttkfhnwvqruvux{|wols{qkfbbgsyk\TNQTVXeztjcefkx{spmpx|~~wtw}xtplghjruuxugUIACPhp^SWh{zwvuv~}wrnu|yv{}zrkgffnv~~peYLE@@Ma~zywtolmw~{z|~}{sryzyyxwwx{naXRUhwlf\TLHFN\urd^\dp~{tolqxsrqpswz~~wlb`illmr}vof^VLGFM^xļ}pf``huzqmszvqnporvz~pbXTYarvk`TC97Ba{skhlw}{vu{}ywuvtuyyz{xsw{mc[TRW`pq`WSTWVPUf}~sihikp~wqqqt|~|wqmmhiryohc\Z[bn|ylZJ@=CUl|l_ao{xvsuwz|tkbcp~zsnf_[Y_jwug_SJB?CQd}voqu{~}}~~|vx}vpnorv}yocWON\vuic\UNHJQ]owmhfiqx}wtsx{pgfeimry{phfkokfinyvnhaZOJIJTjzsollsz}trt{tmihjlsw}}nbXUZcm~xkbVI?DObw}vrsuw|}|~}{|}vsrrquvw{|z|zpgb[Y]em{tc]afd^UNTcyylebhx|wv{|~yuojfgjz~zuolhdfims~}qbVOIFIVj~upw~~qhdgu~}xtlhe`_fwqe_XURONUawznginu~zuvy}vrrtzwnd\_iw{pjeb`[WTV_rwnghou{ut|}uokmpv~}xqms}}vmkkr~xpie\QGAG`{sosvz{|{wtrqsw|}vrxzsnkhhkp}}naXRQY_`do~~snlgir}yvy}}}|zy{{|||xtqrrqpst{umpvvl^TLNWi~{h]_l{~ztojkuywx{|}{|}zwtrpsuy}qh`XRMMUbvyqmpx~{uphhq~|{}xnf_`mxpkfa\XTW]j~vjbbhr}tkirxwy}|tolmw}}ywy}xrlf^WQNO]qzphfkr~yz}yusqry~ztqrwxnihhnywmcTH@BRk|qklou~}tprsvvwyupq||vpjgiimy{k`]_caWQUdysf^[^m~ytrvy}~}~}|wu{~}zwqnmlls|yk]QGBHVn{h_bny{zz}~yurnq|~yvx|~wpjb^_erwlaYOGDHSh|oefjs~zvssppx}{yyzxy}{pdZT[k{xogaXNJIO^t~oc^`hq{smkrzusruvwz~~yqpuunfccnzxqi^PGA>H\|¿~tidekt|z~}}yvuttwzy|}{y{wg[UUYdtrhXH>>GZluljinv~{urtw}||zxyxutspqvwneZVUYcrykgfe`SIFOa}zlc]_n|wtssw{zsoigkw|ulc^[Zaiup^QE==DUqtllsz~|zxz|~vpms|}yxyx|ukaYRQWd}ug]TJBBDQg}nhkqw|{yww{vnmmnrwzte[ST_lv{ne^VMHGJYmtiehnv}uopuwrliinouz|wvxsg^Z]hx{oh^RG?8>Oj}unlnu|}yx{|zwvutuuutts{|nc[USXan~o`XVWVTRWg}~sjefltxutuy|wrljilv|sjc]Z[ahuxj[MEBFUj|pggr}zzz|}unkmvvng_[YZ`onbXNGDEN`u~vstx|yxyxxz}yvuxz}{oeYRT_oujbYRMKMVdvwnlms{zsmoy~wsrtuz~~tllppokiluwmcZQJDDK\x~vrqtz~~}{xuvwz}|}vkd``enypbTJCDN\jy{vrsuy~~{zz~{yyyyyxx{wpjedchnwzldbdb\VSV_q|qgdkx~}~}{tpllq}{vrnkijmpu|se\SNMOWfzzz|~}{unlqz}wrlfb`afv{nd\WSSRV`pwqqu}{tqrx{xyz~|sic^alv|vmf`[XVSV]j{wqpt{}zzyurruz|wsruzzurnmov~vmd]UMGFQgytux|}||yvuttw{}z|xqkihilr{vh]UTU[^`fs|smlkoz~z{}|{ywuvvxz{|~ytpnlkknqw{urssndZSPTarwkglw~|xsnjio{}zvtrnkihiou}wnf]UPMPZj~ytsx~zvrnkpxzuph`\\fu~tnhaZTPPV`rulhlqzzurvzuuwz||uokjmqqqqt{|vpiaXOKJO]svnilqx~ytqprux}~|||~sia`bgq|~ukaTHCEQg~~tmlnrx}}xxxwvttvxy|~}}{wvz}skc^\^cmy}qhc`_\WSXdvvkebfq~ywxz{{yxxvtsrrw~}vohb_]`gpy}o_RHDHUhsjkqz}z{{}{xsqoov}|~wof_YVX`m~sh\RHDEN^uzqlot|zxusttx{yxwxz{~zncZTV`o{vmdZPHFISdx}qifipxysru}zvtsuvvx{}xsrrnfaaeozvmbVJB?BPhvnijov~}|{xwvuwyxy{{yz{rf[VW\fs|ocUHBEN\mwnkjnt|yuux{~}}{yxwurqqt|yog^YX[bn{{plje\SKJScy{md`dq|yvvy}~ztomns}~wpg`\[]dmvtcUKCBGUj{tsvy|{yyy|}{vtuz|||~yod[URVbu}qeYQIEGO]sumkqy}vqmnu|xwxy{~wla[]ckqw{pe[SKHHN[ntmjmv~vqt{}zyxyz}~||~}xogbcfp}zm`UI@MbþyZn{gqpyuww~|geztqs~tpllhrjjuw~qz~VRFJG<:CTlmYyqjqs~yvxv|}dnttow}tplkhpflsv~nqUREKD<=J[vc\morx~zwxw|~zdwquoy|tnliipemrw}nhVOEKA<@Pb~\`jrs}zwwwyudpsn|{snmhlncnpv}xp`UKEH>=CVktUhkvu~yvtwvogzopqztomiojcmnv}ruZTFEF=>I]s¾jTrzoxx~xvrx|tkksootytnmiofdlnx}o}yVQBEC;?Oc|aV{vsz}|vtqywuernpmvwsominbdkow}opSK>D>:CVkwY\ux}{tspxtxydzlqm{wsqlkm`fjoxqfPFKcy»~jSky}vrnpwqogxlnp{xrpjng`jjt{vwVL<;>9@Qj{dVu~|uqlrvrljsons{wrpjoecjlv}s{uRH8<;9CWox_X}|spltstiopplv~yuqokpdglpyrlOD5<9:F]u¼u[\zqolurxguopkx}wsqmmqeknt{qdL?5<7Kc|ye`womkuuykoxvqx}wrnkilchoultdH=2<=CTmxdf}smmmwv}hvxxr|}uolggiajqyhv|ZD94>@K^vwcnypmlozyxhyws}zrlhcgfams}udwuPA67AERftcxtnmls|{skzwv~wogc_dabovicxkI?5;FL[qqf~{rmmnx}~op{wx~tjc_\c_dq{`dwaD=6BJRdymj|xpmoq{~~ly|uz{ne_[[a]husZhvYB<9GO[m|iq{{~toops~~{m~|v{via[WZ_]mxfWmqPA=>NVdvxhz~w{|sqpqv}xq}{uzqe\XTZ]_p{}\YqkJA=DT]m~th}{v}{uqqrx|tu|xtxlaYUS[]bssW^teFA>KYdsnj|zw{urrrz}zr|{uvvi_XUT]\fvkTdv\CA@Q^jx{jo|yzzurqr{yyq|ztvsf]WUV]\jxcUltSB@CWco}wgv|y}xspprzvz|pyxrvoc[WTW]]m{z]XqoL@?G[es}sg~~{wqooqxtz~wryvrvlb[WSY\_q}sZ]tiG@?K]ix~zoi{}tonlrws{~ruytrtjaZVS[[bsoWau`B??Oan|}yln{yqmkjsut}oy}wrssh`YUT[[gviUesW@>?Sdq|xkt}vojijtsv}l}zwqtqg^YUV]]jycUioP>:BWm~utjb`civ}ms}urod\VTW`dr}`FPbR@@Hau~{sqgbafn{~}l{|sqk_WSRWbgytUCS`LAESmuqnfbdjs~yozpoeYRPPYckjKAW]IDL^w}qpledinv~wtvmj^TNMP\frv`CC[[IKTjxopkgimqz~}v|~rjdYPKMR`jz~lT>H_XLS_w~tnqmklpt{{{wzke_SKJLUeovdKWeWWctzxqtvqprrtwruy|}zmb^UMILRanzyhWAD_eX]j|y}wswwsrrqssotx|~uh^[SKKOVfr~tdQ?KedZcp}y|xuywsqqopomsx~|pc]ZQLMR[ku}o`M@Ric]ivz|}xvzvrqolommty{yl`\XPNOT_nw{l^JBYka`mzx}xwytqolknlnv|{ui^[WQOQWcpzwhYFF`kadp~x}wwwroljjmkqx}{rg]]WQPSYfs|~ufVCJdjags{|vwuoljikmmt|y|pe^^VQQU\ju~sdRBNfhajv{tuqljihlnoyv|nc_]VRRV_mv~paM@Rieamzxssmihhhnos~tymc`]URSXbox}n_IAVicco|uqpiffgipry{rwlca\VSV[er{{l\EBZhaeq~sokedeglsuwqvlcb\VUX]it~yjXBD]g`gs{olgcbdgovysrujcbZVUY_lvvfR?F`fajvxlidaaeiry~~ouuidaYUVYaoxqaLb|}y{}{nfhkkmopsxy~~m\QNJIOZh{uk^OA7Iqyx|}vmhmonnnnqtt{ycTLKHKS`pzneWK?Ei}zw|y|tpptroljhklqz|dTIIJLT`m|zmd\PHBPw}{yxu~vqsuqmifeikr{{v]OHJLPZfsuibYOIF[|{yz|u}vsutojfcdhjt}}zmXMHKOU_kw{qf`XPJJg||y|zw|vsurlgcadhmxxyfTKINRYcozwne`WPJPr}|y}x{|urtoida`eip{t{|bSKLQU]gr||tjc^VOKW|}{y~xztrrlfb`aglt|r}v_RMPTX`jtzric^UOL]}zzyxqqohca`cjozwq~r]SORV[cmvyqid]UONd}yz|uonkebaafntsqn\TQUX]eox~xphd\UOPk}xz|rmlidbbdkrzprl]UTWZ_hqy}xphc[TNRq|x{xojifcbdgpw{mtk^VVY[ajr{~xohcZSMUvzx|ukggdbcflu}wlv~k_XY[]ckt}~wnhbYRMX|~yx|qhffcceipzslz|k_Y[\^emv}ulf_VPL^|ww{|needbdhmv~pm~zi_Z[\_fnx|ske]UNMd~zuw|vjcdccfjqz|opwh^Z\\`hqzzphcZSMPl{wsw}qgbddeinu~znuse\Z[\ajs}wme`WQLUw|xsqw~|meceeglrxxoyobZZZ\clv}sib\TPM]xtpqywjcdfgkot{wq}|j^XXY^fpzyoe^XRPQg{sqnr|}ridghjmqu|vrudYTWY`it~}ti_ZTQQXt~uonnux~ohfikmorv|stm^TSV[cnyxnc[WSRUaxplloy{w{pjjmnnpru{~~rvyfXQRX_it~zqg\VTRU[o{rlkmstwyqmnponopsxzyov}p\RNS[do{|si_VSSU[eumilox{qyyspqqnllmptx~sluveUNNW`kv}tkbZSSV[dqypjins~up~zuqrpliiimrw~}lirp^QMQ\fr|}wme]VSVZal|unjkqx~rr{vrrnigfhmrwwggpjXONVbmwxph`YTTY`itzrmjnu|ypw{urpkfddgmrzoafndTOQ\is|yrkc]WTW^fp{wpllrxwp|ztpmgcbcgnt}{g^fl`SPUbmw}tng`[WV[dlv~upmot{urxqnjdaaciqxsa\gj]ST[gqzxpjc]YWX_gpz{tpoqw}}tvvokfa`aelt{~l\\ii\VW`ku|{slga]YX\dlt}ytporx~|uz|slhc``bgoxygY^ki]X[dow}}xqjf`]Z[_gowysposy|u}ypjfb`aekt|tbXami^[^hqx}~zvnie`]\]bjrzxsppuz|vvmgea`choyp^Xdoi_]aksy}~|ytmida_^_emt{xrpqv{{u~rifdabflt~}m\Zhrja`dmty|}{xsmieb`_agnv|~wroqw}yuzogfcbejqzzj[]mtkcbgouy||zxrmjfca`cipw}}vqorx~wuvkfecdhnvwg[`rvledhpux{{ywqmjgebaekry}{tpnsxtvsjfeeglrzsd[evvmgekqux{zyvqnkgdcbflsy~ysonsyrv{oheffjpw~qb]jyvmgglquxzyyuqnkgecbgntzwqmntz{owwmfeghmt{|oa_o{ukfgmqtxyyxtqnkgdbcipv||tnkmt|vnytjfghkpw}zl`bs{sjfhmquxyyxtqnifcadjqy~ypljmu~rmz|qighkntzxk`evzphehnrvyzyxtplhdaaelt|umhhnw}ml|yoihjmqv{vh_hwwmfdioswzzywrnjea_`foxzoifhpzvhm~wnijmosw|sd^jwshcdkquy{zyuokfa_]`it}÷tjedis~ofqvnkloptw{}n`]lvnebfmtx||{xrlga][[alx³znfcelwxjgvvpmopqsvy}wh[^mqicdjrx|~|ytmfa[XX\fsþ~rhcbgq}rgk}wqnoooqsvz~q`V]lmfcfow|~{uoga[VUY`m|øwmebdkvzngqvpmmlmnpty}yhXR]kjdekt|}xqiaZURU[fu{ricdhp|vljxuomlkklotzraRQ_kieipz{sld[UQQV_m}wngdflv}sln}smkjhiknt{{jYMQblihmv~wnf]UQORZfu|rkffjq{{smtzpjigfgjow~tbSJSemkls|{riaWQNOU_m~xpjginvzspzwnigefhlrz}m\NIWiomqx~wnd\SONQYetuojhlryysstkgedfinu}vfVJK\nrrv|{sh_WPMNS]k{}snjkou}ytv}qheddgkqy~paRINbrtuyxnd\TONPWbq{rnlmqxytwynfddeint}zl]PIRhvwx||tjaYRNOR[hvyrnmnszxsyvkedegkqxviZNKWnyz{~ypg^WQOPV`m|xronpv|xszsiffgjou}~sfYON]s||}~vmd]VRPSYdqwronqw~}vrz}qighjmsy|qeYPQdx~}~{tkb[VRRV]gtwroorx|spzzpihjmqw}zpdYQVj|yqia[VTUY`kxwroosyzqo{wojjmpu{xncYS[p}vof_[VUW[cn{~vqopt{wno|vnkmpty}ulbYVauzsld_ZWWZ^fr~}vqopu|~tlo|}tnmosw}{tkbZYfz}wqic^[XY\aju{tonqv}{qjo}{rnnrv{zsjbZ[l~~}ztnga][YZ]cmyyrnmqwxmhp~yroquy}~xrja[_q}|xrlfa^[Y[^ep|vplmqxtjgrwrqtx{}xri`[bu}|zvpje`]ZY\`hs|smklrz{pfgu~wrrvz}|wpg^[ey~|{ytoid_\YY[ajwwojils|wldix}vsux{}{vmd\[i|}{zxsmhb]ZWX[bm{~rkhhmv}rhdl|}xvwy{}~zri`Y\m~}|{wrlf`[WTV[dqxmhfipyxnffr}xuwxyz|}}vndZV]q~}{wqjc\WRQU\hxqifglt}}tlfjy|vtuvwxy{yqh^VT`v~|vog_WRNOU_nxlgfipyzrkhoztrsttux{~ulbXQSe|{tkbYRMKNWdurhfhlt}~xrljuxsqrrsux}zpf\SOVkyqg]TMIIP[k~{mggjpy|xrmnz~upoppruz~tjaWPO[s~wmbXOIFISasujginu~|xrnqzrnmnorv|zoe\SNPa{|sh]RJFFKWg{qihlry|xsotwollmosyujaXQNThyncXMFDFN]o|ojkpv}|xspwunklnqv|zoe\TOOYp~ui]RIDDHScwynjmsy}xrqz}rljlnsx~wlbZSOQ^x{pdXNFCDKXj~vmlpv|}wrq|zpkkmpu{}sh`YSQUewl_TKDCFO^qunnsx}vqr}wolmosx~yof^YTSYl~th[QIDDITdwtopu{|uor~vpmorv{wne_ZVV]r{pdXOGDFMYj}~tprw}zsnsvqoqtx~~vme`\YYbwxm`VMGFIP^o}tqsx~yqmswrrtw{|tlfb^\]g}ui]SLGHLUct|truz~wolu~wstw{yrjeb_^`l}qeZQKHJPZiz|utw|{tllv~xvxz~}wpjfdaadrxmbWPKIMT_n{uux~yqjkw~yx{}{tojgedchwti^UOKKPXdtzuvz~wnhky~zz}~xrnjigfek|{pe[SNLNS\iyyuv{{tkfm{~|}{vqmkjighowlbYRNMPV`n~wuw{xphfo~~}xtpmlkjhjs~rh_WQONRYdr|utw{|umffr~zvsomlkjikwzoe]VQOOT\hwyttw|~yqjdhw|yuromlkiimzvlc[UPNPU_l|wrtx~{ungdl|}zwspmkkihiosjbZSONPWbp{sqty|xrkffq~|yurnkihggir|qh`WQMLPYeuvqqv|~zvpjfiw~~~}zwsokhffefjwyne\TNJKR\j||tqsy|yuojhn}}}}}}zvqlhedddfm|wlcYQKIKS`pxrrv|}{xtokjr}{{|}|xsniecbbdhqti_UMHGLVewvrty~|zxtolmv{yz|~{vpjeb`abekw}qf[QIEGN[k~|utw}}|zyupmoz}yyz|ztmgc__acgn|zmaVLFDGQ`r}|zvv{}}|{yuqnq}zxy{~}wpjd`]^adjswi]RHCDIVfyz{{xz}}}}|zupns~yxz}{tmfa]\_bgmwrdXMEBDL[m}w{{z}~}}~}}zupnu}yxz~yrkd_]]`ejq|}m`SIBAEPasyu{||~}}~~~ztonv|yy|~wphb_]_chmtyi[OFAAHUfzvt|~}}}~ztoox|yz~|umfa^^afkpxtdWLC@CKZl}st}}}~~ysnpz}{|ztlfb``dint{p`SIBAEO_ryqt~~}~~ysnq|}|~yrlfcacglqv}}l]PGBBHSdxvpu~~~xrnr~~~wqjfdcfkpuyxgXMEBDKYkspw~|vpms{tniedeinsw{sbUKDCGP_r|qpz{tnmuxrlheehlrvz}n^RIDEJUexypr}~yrlmx{uojgfgkpvy|~yhYNGDGO[lvpu|vojm{}wqlhfginty|~sbULGFJTbsuqy~yslip~~ysnjggilrx|~~~m]RKGHNYhy~tr|{vojiszuokhghkpu{~~~xgYPJHKS^n|su|xrlhjw{vqlhggimrx}~~}rbVNJJNWdtzsx}yuojhm|}xsnigfhkotz}~|{}n_TNKLR\izwsz}yvqlhir~ytojgffhlqvz}}zzxi[RMKNU`o~ut}}zvsokhkx{vplheefimqv{|{yzteYQMLPXet{su}zwtqmjjo~}xsnjfddeimqvz{yx{pbVOLLR\iyvqv~|zxuspmklt{wqmieddeimrwzzyx}|m^TNKMT_n~}spw}{zywuspnmox~zuplhdccdhmrwzzyyxi[QKJNWcsxooy|zzyxvusqonq{}yupkfcabdhmrxzzz{tdWNJJOZhyslo{|zyxxxwutspos~}ytojeb`adhnty{{{}p`SKHJQ^m~zojp~}zxxwxxwvusppu}ytnhc`_`ciouz||||l\OIGKUbsukjs~zxwwxxyxxvsqqw}ysmgb_^`djqw|~}~xgWLGGMXgy}qijv{xvvwxyzzywtqrx}xrkf`^^`elszsbSIEGO\mymgky}yvuvwy{||zxtqrz}wqjd_^^agov}~n]OGEIRasukgm}|xuuvxz|}}|yurs{|vpic_^_cjqyzjYLFEJVfx~rigozwttvy|~~}zust}|voic`_afmu}vfVJEFMZk~{oggr~yvttwz}~zvsu{unhcaadipx~~raSIEGP^pxmgiv}xutux{~zvsvysmgbabelt|~}~zm]OGFJUdwtkgl{{wutvz}}ytsx~wqjeaachow}||uhYMGGNZk~}rjhozvuux{~|xssz|unhcaaeks{{yxpbTJGJR`rzqjjt}xvuwz}~zvrs|xqjea`bgnw}xwtk]PIHMXgzxpknz{xwwy|~|xtru|umgb_`djr|zuspeYNIKR^ovpmr{xxx{}}zvsrwxpic`_afnwvqpkaUMJNXfw}vqpv~zyyz|~~|zwtrs{{skd`]^cjr|zqmlg\RLMS_m~}vrs{}{z{|}~~}{ywusrv~vnf`]]_fnwumkjcZQNQYet|vsu~}||||}}|zxvusrsyyphb]\]bir{yoiihaXRQU_l{{vtw~}||||zywutsssv~}tkc][[^dlu~tjghf_WSTZeszutz~}|{zywutrrssuzwnf_[Z[`gpyyogfhe^WTW_kx~xtu{~|{zxvtsrrrtuw}{rjb]ZZ]bjr{~tkfghe]WVZco}|vsu}}{ywutrqqrtvwzxog`\Z[^dlt|zqifhid\WW]gsytru}{ywusrqqrtvwy|}tle_[Z\`fnv}xohfiic[WX`kw}vrqv~{ywtsrqqrtvxyz}zrjc^[[]agow~~umggjhaZVZbm{ztpqx|yvtsrqrstvxzz{~xpic^[[]bipx|tkghjg_YW\eq}vqor|~zvtsqqrrtvxzzz{~wohb^[[^cjqy{rjghie]WW]huysons{wtrqqrstvyz{{z{}vngb][\_dks{zqifhhbZVW_ky~vqnov}xtrqpqstwy{|}|z{|unga][\_emu}yphegf_XUXao}ztonpy{urppqrtwy|}~~|z{{umfa][]`gowxnfdec]VUZdrxrons}xspooqsvy|~|zz{tmfa^]^biqzvldcdaZUU[gv}uqnou|uqnnoruy||yz{tmga^^`dlt|ujbab_XTV^k{ztpoqxzsomnpsx||yzzslfa__agnw~rg`_`]WTWaoxspps|wqmlnquz~zxzyrke`^_biqz{oc^^^ZVUZfu|uqpqv|tommpsx|~yw{xpic_^`dkt}xk`[\\XUW^k{ztrrtz{tlkpssy~{zxz~smjb[]cfkwqf_YWZYUVduuqtxx}vmkpttw}~{vs}vpng^\^_dqxof[UXWSR^p|wrw|z|xnkrvuwz{z~|vqwytumb]ZZ]k~~wqaUTTRQ[kvzty~|||qmtxuvwww{}wruyzqd]WVYeyziXQOQQ[hpzx}}||~wrxzuuuuuy|yuwtf]VTUas}q^PKMQ\gks}~|{||v{|vtstsw}zywyxi^VSR]nv}yfTJIN\ginv|y{||xtqsrv|ywxv{}m_VSP[ipv|mYNHJ[fgjo}{x{{{tqsrv~}yssvv}q`XSPYdjotr_TJIXcgfjw{x|{}trtrw|zunptu~ub[UPX_ehlzwf\PKT^ecfs}{x|{uturw{xrknrt}}|haYSX\`aet}|meXPRX``cow|y|{wvwtxzxqhlps||pg^WY[][_mwsm`WUTYZalr~{}{}yxxuyywpgknqz|xpd]\Z[VYgp|yti`ZUTS\hnz}~{y}{zwzyxpgjmpy~{ylc`][TTaiu}{qibYSMTcjt|x|~|xzzxqgjlnw|~zvkf`]TQ\cn|wqj`WKL\ep{}wzz|zyrhjlmuz}ytnf`VPX^gu}wsh]MHS]iv~~xy|}|ytijmlty}}y~}wnf[QVZ`nx}{qfTHMS`pxxx~~zukjmlry|}y}wlaTUXZhrz~}zo^MKLUgrz}~xw~{vmjnkqy{}y{ui[WYWbmtyzwiWOKK\jrx{~zv|~wpjmknx{|{{re\[W]intw}tcVOGPaiqu~{vy~zskkkkv{{}|~{oc_ZZfjntx|o`VJIW_ipw|xw|}~vnjkis{z}}~}ykc]Zchips{yj^QIPV_jp{}yuxzzrkkhpzx{|}|}tic\bfekou}sfYMOPTait~{uvxwmjhmxwy|{}|{~ph`bfcgkox}|ocUROLWalw~wuw|}qlhkvvw{z~}yxnddgadfjsyvk]XRINWcp{xuuyuojkutuyw{}{ujghaacemt|sf`XKIMXht{utw{slltsswuy{}}plkb```ho}zmh`OIFM]l~xuuxpnusrtsvw~wqnc_\\ciytpgVLDFSc{zvutrwsrrqtt{}xre`ZZ_dtzwn]RECJXs}wuzvztsqosryxiaYX[_o{~tdWHDDOi{xw~~}}wtpoqow}|~|}ncWWX\ku}xk\MHCJ_qzz~|~~zvoppnvyxyv|teXWUYgox}}raSMDHVfy}~}{|yxprpouuusqy{|i[YSXbirv~xfZSHIP[n{~|zv}}{srpxwq^WHP|z~g}w}vlLztswzyoxd}y\XzuugzcK}zMLOLZk~x~tyRHuryxl]w|mT_f^ivtqjixVbzywfsMXldSUM\rtv~|^=[|t~Vqz|zargh^l{xjc{}{]Jx{~oaeEve}x[XRgursrsiE=t}wonX{|{qboieuujniphJS{e]ZUugrc[`mmj|{eU8Lmjgm}y{uij|smprkeytwdWDe~[\\l|mqlgehjhjpZG8cemm}xvxmgxvxvrngguj\LG~{Vccy|pn~gojghfp~dT>Aaup~vuz|dmtsmkcnzwaWDWsWle{{~iukteefdy~YM9Xygxpwtwpbwz~llgbyp}}XPFr{m_kjx{sj~~robgbevzRGAs|vktytv}gg}vllbemrRLRxm`iwwno}qide_lumMGQ{tiv|u}vdmyrmg`pk}hONbzh_qy{}ivxmhd_`zq~eLKa~ljy{}r_w}wribdfxaPTq~_dzxvdpog^^gk|^NPr~csy{h]vyjbblvcy[SY~|Yo|zhgnp]\asuk{XPW}b{|t[iwtacezgjvWTcs[t~t`wofX`ekrvUQfvc}|f[{ti`ejcqqTVu~lZy|hdh_[anixpPVwl`~nacjeafs}cxjP`}]\phlpb_]fwh~dNaZj}shbnojdbj{xg|^ThyOn|uoh{ef^aiyn{YSeuT}{uohdklcejrnvXVm}gR}}vtmn~fg`aluqtSQo}g[ytqnhnkmcaroprNW}|v^\vuwrotxgi]awntkFZzz_k}nqpnltyol]exhvbGfvtWrvpvutm}rmcZezkuVGizt^onprmiwqb]b}zkoROv~wm]oquxlori^Yb}iiLP|zqdzlmtoeqrk^UdwdgK]~wkazmtxmivihWSlqccGf}ikvktnffwlhSUnid_Otwaqusvmgf{i`PXxfc[Suuc|~trnhae{j^PY}cc^Z}}ma~ytmi_ktd[O`}^d\ZyoexmiaZqqdYNhx\j]aqkcwmi^]whbURts\kUerkhoiaX^}hcRYymblVo~neloj_Zbd^R`kcfTr}nguzkdZWd~d\Vc~lddZzvhgyylc\Xmy_\YgjaaY}tjkte]XVww^_Zi~fab^likte_WZ}o^aYp``^`kmn|m`[R`kb_Xt[c^i|hlp}ka[Uj|gc\[|zXb[ozjoxxe^VWpieX^tWc_xtgove_W]v}hbVdnV``|tiv|qbZVaz~i_VhjUcfogzzob[[e|e[XmgSchnlul^Y\fbZZp}cSgo}josk^]aiz|]Z\w~}^Rir}jwpg[^`lzy[[]{{|YUnzyi|of]bbsxsZ[`{ySXqxl{mc]aazzn[[c{uP]wunwncabezi[Yh|oMb{ustl`a_h|h[Xo}yiMh}ttslbb`pzeXYw}vdNm}vxqia_`uzcU\}zqaPu{uxpjc_eyx}^Sbvn_R|}u~mi_]g{z|YUhpm]V|smj^`k~|zvRXmlnZ\}smg[aiy~pQ\qxknWeyt|oe\cjv|fQ_xtlmVlwyym`\_lvzbTcpmkXtv{yk`]]sst_Uh}qmjZy}x}uh_Y\zpr^Wnxplj[{w}qg]U`jrZ[uypni[|vlfVRd~jsXayxoqe_zrleQUiskoXg|xqr`fvrj]NWlnom[jwsp_n~pv~hXOYtkok\l~ysm_ryo|zaSNZ}jqk\q}|pl^wrps]SN`guh\w{nkZ}}zpsiZONeiybazwogY~|unudZMRjzlw^fzun`]}}xsn{aWMUlyps]h|~uj]d}yuom|^VPUrwnp\i~qe\izvtlsw[WOUztnnYn}kd\rwupjyq[YLYmojYuyfd[~uunn}k]VJ_jqd]ysec_{vrlq~i`QKchp`b}{odaeyvpori]MMfgm_f~wncdmytooqhYLNlzcl_k{ukagrysqnrdVJOtubl_q}wthckwvtqku_THS|nal_zzwsegm{uvpjy|[REYgckbwvpeln~vwkj{uYNEaceii{vvmgmn|yuil~oVIHgafipwwsmjnq|{qhmkSGLmz_fmvuvqnjnu~znhmfOFPtw]hpy}uuqpiqywkgoaLGT}tZls|zusrpht{~tkgr}[KGYpZqtxtsuniw~{rifw}|VJH`k\vtxrtukkyxqhg|}vRJIjg_xuvrvtio{upeh}pPJNtecxwurwqhr}updl||kNJS}}dfx{~stwoiu|tmdpzzgMKZzchz}svvmkwztketxwcLMayci||swsllzxrigxutaMQixak{twqmm}wphk|qq^MVow`mzuuommvmhmmp\O[uv^rzvsnlm~sjipioXQ`zt]wzuqojp}oijs~hnVUcp]|{tpnht{kikxvhlUXfl`~zqpkg{whjm}qijV[k~hcxpphhqih}swsdnvg\~pkxt{wdLDuguwluqh|_[^[eqax?dkb`hrucn}tQ|tcbaqjnb@q_[hqwatnTO{qddtsX~|Ocf[XdxshqrhGswmir_YfMaZ\orbfoNQqrlpOtz_ZoeZgvfjnlZ?p}{fhcZ}p]~fa`und~msb?G~bgNwrh]ugYksfpo~dT;dqca^yn]uqifmcehX=Geb_{~zfa|kiokcg~~]M8Zx[di~~}qdxnnmifc{}lUD?gYis|||_inuhd_d}ZS;YYmh}}zlcsu}ob`YuyiRG?f]snxwz}akx~hcX`|[R9[[og}wri`vna\\ovmVIG|ocqiztvZg~gYW^s|_MF]immupxeWvv^ZYfzuuPMLsycsiwu~sUlfY]XocNOUkelov}}{]Yy|rZ]UixsyTWNlbmd|}}|lUj|{bZVWq~eRUWobiq{|}t_^uzpb[Qgvz[WOebah|xtf^hri^U[yxpXWUvuXds{xga]tvpgVUfpzeVU^eXozxkfbapt^VZw~owWWQpxShwymcf\vswlYYbo|kSQUcTs}|rde_aov`\[tm|[TKmyUfwxhecZsywl[YaqypQKUeWr|lgg^btue]Ymp}`JHms[`zrfg_`ssoaZ`||uwPEX|jVk}ikb^dxqj\YfrzgCMlseX{njh^dopsdYZqpyMB\~yrWeyil`ag|pr^X]xvk?MgvkQzmlf]dnougZVlowQB[x~x[^yll``e~pu_UXokALd|qRxomeaeltwlYQjrpWAUvy_[|ohabezrvaRVkkIG`rVpulddbi|tlVMiwh`CPvzfZqggectvtaMTjhS@_r^hyifjcgrmVLfdgFIvzm[}qfkgap}obLT|ne^=^ui`|ginbdrkVKcahKCx}tbqpeoh`mkdMStvacZoyj\q~vhMW|ifm`X?Hmoz^cwo^Gqtfirm^L?WrwqYl}yiSR}mfkcXBGhn|b]tpbLhvjgqu]NAT{ytuYg}{iZP}qhigWGHbrxhYqrdS`wmfr~]PFPuqv]`{|j]Qx{siinUMH\yrmXmtcXZ~woet_SKLopsb\z|k_UoytijuVRHWlqYhvc]X|vpgvcVNIjrnhYw|k`Yiwukm{ZVGShp\d}vc_Z|upkvkYPGevijXs~{k`]ivsmo}aWGPxim`a|td_`|upntu\OF^{ej[o}xl_alwrppy~jUHLplfd_zse_g}~srqp]NFWbh_l|un_foxstnv}rSJGkq_h_wrganwsvpm~_NDQ`eclytoajoyvtjtvSIBfs\ic}trhdoswxlj}bMAN|bahpusnfko~zqgoySEAaw[hkxsqlhms~vhfxhI@Kxg]pr~trolgo||nci}TCB]|Zis{wrppgjrsf`tkFAHxk[vrztprmdnu{k]d|}TBA]]kvxvqrregnr_[q|lG@Kxp_vs{sssjait|fWb~yxYADafixvvsumc`moZZluoIARuxbruz|uvpg\et}`W_|vu^@Kamdxqxxrj_[n|kYWipsJEUpbouv}zvlcXdt{`V\|utcCN_rauq}zpfZZn|l[UjosQGUmdgtw~uj]Xcu|cV]zyqhHN_wv]ouzo`XZlq[WipqZFVhh^wwueYY_xeW^ulnLL^qzZjz|{l[XYlu\ZgqpbFUc~iXxytaXY_{hZ^qkrQJYm|Yf~|{hZXYlx`[dvohGO_}jVv}r_YW_zk^\oԧif^V^fzTf\N_gugw|B[ǘif^jhtd`ziYh{RSZhfts2oΐgZYfmzarub}pHr|egdl~z^e;ƃaW[gw|cuh}ZB~o`h~uX^=`Y^n}nc{ejIGsmm{iUxXI|c\\kykvydrbER|ylu[Sr]Vv_U^xxfwfmW6\djOcpgekd\gxmfzuu`L8nz_kOo~sghng[lsdjyxkaD<{}^aL|tkhzģjf^omewzg_{zhdiw}{`o|pplqlhnsJ:Kx|edj||s]|ypkhsklmkG>St{`ej|p`spjotapq_C=_}uz^hqxkesshpl\{tSFCnuxz`ksx~fgqpgre_uyRHD{wv]k}{vdlpnjp^htpOEHw|s_p~ufjponiZn~lkNBR{t^wraiqqh^^xtgjLF]zq]}}q[pmqc^c}hklHFeyi^~}nV}so[^e`oeELpweg~gT{ueX`f[vaHP|}_i}yZ\yr[Zan{_y\IS~}^jqXfvjY__tsdxWEX~|ZjfXnsdY\a~pevRFe}Snxa[uh`[]fljxKIk~sJz{ma]bcZ_iipqDPo{gLyykc`}cdZ`hfvgDTrWT}}qj`gre`[_ljw`GSw|N]|}toibqmeb^Zsmu[BSrKi|wpohgvlgdWY|isU>\}nIxyppmigygkaR^~gtJ?dzfKrnmkkehpZSawjo>Cky_UklkpidnnUT`pkd:Jvw]a{kkmqbipfSTcnj]:Qs^hviiqm^smbSPhmgV7[{q_nrflse`|i`OOofdQ:kyu\uogro`czf]JQv`eJAyztWkksg]eqfXHV~}\cDM}pW}ipoc]hlfSHXwZ_CX~i\ylria[mjcNH^pX\H`{dbwqoe_Yth_LIhlW\Kesbh~yrjc\Z~e\JKufT_Mkocn}|ofbX]aYFO`XaLt~mbt|je_Tc{]UCZZ]_M}xmb|xfe\Uiƹt[REd|XbZQtmbrfdYVo˰nYNIm~xXdUZsmdmfaVWv˨hWLPt|u[cScqjgjf]UYȡcULWy|t[`TloilzidYT^ĝ`TO[}|tX^WrljquhaWUdš\TQ^}}rT_\yilv~tg^VWn}YVRbz~oQb`{gn{zse[VZx{YWRhzgOfdufsxsaYX_xXWRpyaQigohwxq\YYdĺuXVS{x}[Ujjil|ymYZ[kƲzrWUWy{WYjo}frxeW[^rƨwnXS]xvU[hxvfwv_Y\`xŸxkWRfwrS\jqh|p[Z[d~xhVTn~upQ[n|oj~iZ[\ixeTXs|tnM]vwomxe[[^mxaT\vxskHb~upood[[`qx\U]yuueDj}vntjdZ\cvz~YW]}sv[Ds{wmzgdX^d}~yWW\rsRJ{|wmhbX_f}tVT`rnLR~}uozi_Y^j{qTQg~phJY|urwg]Z^oymOQq|jdJ`{vuuf^Y_svgJVyxfbHj|wvqf\Xats]H\pebHu~u{mgYXatu~SKaig^L~sleUY_wwvMOf{gjZR{slaTW\ºumKSnuijYXxvj^TR]Ķ}rgLXwrijX^wvf]PLbƱundK_~~sjlTe~wszcZHKhkp_Mg{smlPp}rtrcRCMpȼfq[Sk{tqgN{zmynaJBOzǹfqZYmzwqaRrkjZEASƶ|fqZZs}zm]T|kmeTB@\ůwgsX\y~wi[Wviq^Q@Cf¨qktTa~rhW[}oiu|YN=InümqoQhngQb~xli|sWJ>PtõlviSl}lcNl~{sijlUGBS{mvcVnyi]Nv}wpfpfRGEUnt_WrweZQ{umdxaQIFZkr]Yw~t`YSwsjg\RJEahqY]|}o^XY|vqgmYTGEi~fpUb{i]WayunhrzXUCIqwflSi~wd\WmvtlkvuZS@NwpghTn}sb\Zzurlnwr[N?S~lfcWt~zo_\auqooyqYJAWhebZz}vl]^h|sqqn||oUGC\dd`]|sj[cnyrsrm{kQFDc`c_bypg[irwrupn{fNEFk[c_iuoe^outtvnq}_MCHt{We_r{rmcbsyswuluZMBM|¹tUeb{xpkbfusytmz}WLATĵmTffunicjx~tyrp}yVJA\ıfTilqlhdmz|uxqstTHCe®aUlrolgeo{vwru~qREHnÿ]Wow|njgfr{vutv}|nODNwż|ZZs{xnigfvzuuuvz|jJDUźxX^wvmhhf}ztutuv{dFG\ĸuVb{tlhhgysvrvt{]DJcõsTh~skigjwsupzsxUDNk±pTnrjifottsorsOGSt~lUspjhftstoqqmLIW~¿~iWw}nkggw~rrltnfJK]Ĺ|f[|znlgiy{qnkyzjbJMdızd]wojgj{yojm}sf`IQlybavoihhwjhpke^HVrz_eumighsfircf[J\xy\k~tlidimdku_hXN`uZq~qlgao|fhhܬ_VOLX]i^~hmY/\>8YrK]xqwi K:0G_miRqWQ^äy&S:7WbwhR}nkoo(W@5G_j~b@a[b}p*qGFROgwSu`fh3iJ?Jdku[8mfhzg2tڏYVIC_UqYbye>l|WMQijqU2|qfxu_9iۢke=?Z|Qn^btcDSh]VlbnpW0r|fy~}k[@`֬n8DWvP~qjcrcBCwm_q_ng\0gg}qwcXFZͱw=JUsNwxtfrzd@=|isZog_2^g~np^XMWzGOUrNoyhqvf>=orZo}pc5Th}tpk_]TT{QOUrRezkswi>;ukYmvua;Ml}qsfaaYQ~WJVrZ[xpuyjA:}zc\hqx_CGrzosabe^O^IXqycQxvx{}hE:m|\`aoz^LC{yups^dhcMndG[qsmHy}|sdJ:^~Ye[nx]TB{rtq\ekfJ^gG]rqtBzrdP8QXgVmu]\D~nvm]gnkHQlH`tqyBw~~tcT7G[gUkradImwjajroEIpKbvtyFkwx{eV5?aeVgmdiPt}nvgdltoBDuPdyxxuL_q|vhW6:g`YaihmYfzqtfiowmCA{Ve{~s|pVSnqlU89ém[\\goqcYwsnemt|jG?y`f~zrl_ImppT;9ɥrW_VcvumNvshgt{fM>fjfushfB|p~prSA<{͢wUaSb}yuGyrbjzaT>Uxiqugi@xwuzqtSGBoˢzT_RbzDz}q\m_Z?Em{lx|fgBqqzuqsUNId£}U\SdyCroYp\^@:rtj{wieFmo|prs[VQ[XVVhxHjoWr`_B3{njrkaNko{mrsd][V~^R]pvOaqZrh_D/sgjnl_WksxlqspfbT~eRcxtXYucpq]D2`dj~mn_`kzpkrs|ofVo}iThqaR~ypo{\C6PckxmnajkiissyjZZnZonjKu~~n}]B;GįemqondsldguwlYJqatkpEkq}]C>Bʳhoiqoiyk`du{oYBuizktC`v_EBDηmpbrqo~n`avsW?yq|lsEW{zbHEHϼup^rtvrc_uwTAywooJNv{dMHNͿ|m\qy}wyh^vzPFnsrhPJrqzgQLUi]o}|pm^uyOJZruaVFnkvkVPZsƽd^nf|r`vwONJsv[[FifqmZU_jaam_zvbwtSN>xvtV_Ifepo`ZcdaclZ~|ygxrYK7r{pUaNzefzppf_eaddmXw|myp]F5m}jWaTtggqrpkce`hdn|Yq}tzq`A5vl~eZ`Zolehsopfeamcpt^h~|{u_=7iwnzb^_aktabtoujfbpsdsle_}|y\;8^qqta_^gg|\^snymf``weufjV~w~X<9Umslb`_leX_qn{mf[T{gvcnN}tS>9O²msfe`andVamo}ngTMjwyemJwu|P@9Jôrpbf_cpeXbjo|oeNKnwrjiJowxvQ@:Gijyjaf^fpg^chp{qaIKtwlocNgqznT?;EŮede^ipjxgcgqzsYHKz{wjr^S_}p|iX=FŢag`_nqo^}t`isyuLOG_{wqoZ\Prxue`:?Gzcf^`proUy`lt{tHSBWsvj\]Lq}nf_:@Jqjf^bqtlR{bnu{oHU?Q~sxf_\L|ukj_=ALjqd_dqvhS|dpw}lMT?Kxvzcb[Mwzil_BAOcwa`gsxcV~|grz~hTP@FrzybcXOr|jm]GBQ]|`bjtw_[szks}}d[KABxn~vccVRmwll]NCSW`dmuu]`gzqtzcbFD>kmsebUVisoi]TFUQuafpxr^e\zvtwdiCF;^ori_V[fnpg_]KVLkeitznagT{|uthmCG:Tt~qm^WaelpfafSVHbklx|iehO}wqmmFF:M{yqp\Zffmoeem[TD]sp{~eifM|zorkKCHpns`aoltffpwoP@W{{dn`Qdok|eXBAKnmshfrrrybhx{xQBTuxin\VXhjc]EEPmmrqmrxf}bj~SEPftolW[Rdi}d`JKVynnr{vr{^}emVJKYpujR_QchxfbPR[rnprr|Z}kqYRGMoziO_SchoidWZ`moqttyYqu[XBEo}fO]Xgifkf]`bissxxqZ}~xz~_\?@r}dR[`}mi]lidfegwt}}i`t|e]>^{od`Zwu]Qdxrq_YZpTu~v{RFAY~lfc^xu\Ra{trZV`lQnuy{QIDVjhfcxw]T_~wrVU{gfPgm}wRKHUûjjijwz_U^{oSWyo^RahqULKUĶ{kjlnt}{dV_~iRXsztUU\}gkYLMVůunjosrp|jW`bTXd}vOXXwj}f\LOWçqoirvqezoYb\XTXtLZUsoud^MQVzqqiuyq}]zr\e}X\LOzoKYSqtmd_OSVtrphwzswY|r^g{zW^EKwiNXTsvde^QSUownhx{tnZrcizqZ\?HydQVV{u^f\TRUi|mix{te_rijyg`V>i{qgUR^nhaaX[PTNvrnt}e]nZrzqznblD@:amkURajgd_Z^RSErvpt|_blTs~txghhCB7ZkmTSchjg][_UO?o{sux\ggQuyubmbFB6T}lmUTegpg\]_XIg{wm`k\Wx}lfrWQ>8LxskZXgiy~f_dd_>A_ygfiYYmgkqUU=:Iyui^[hjkfagfa:EU|ycldWZccplVV<;G|vhb]hhafcija9IKux`q_W[[dtgXW>>G|whg`if[hfll^zdyriRQVz]bgltfu\rf`^gAR{iu_BBXɢ`^bpyxYztPdvTX-Xnm|aPWk}bcjvztpI{`{sb^6g{eveQFMj¦x[at|Vzrik[F/uc~cZTZrdUcowviMytrdRH{haRFOv͛nZbmpTtll}_XGD{}jo\XcǸl^iun^R~dcTK|nol_LS^Ժf[kyj_w}`ZGAʺz}gfUcfѤRVmvq`aohRK~hcM[]䮝]csy^ezo`^IMƮ~xq[kkѓ^k~pwJkmlgLTqjYPjmӜx^lLzikY>Zȳ}~gZ_vs`SgtqFz]Hk~|aU]nqΨhfwnKjmMKw{|mipzydmtZHrpRUw|pj[Yiy}ʾir}]VysbKP|ıd`dx~|Ƨ~\hxhUajXTª~]a`vqذk~wmYh}usSSQжziwuqˎvlkeOmwvZXP¨sg`dh}n̖yw|oVyqoPINigkvqijsui`ZXL\§hejpvlx}usec~~thCKjͲ|oy~sqzqkY`zcGRo»pnldllo{tw}ydgt|M?Hx˾wuluq~{rdnug]hTNPήonnojxfrvocixk=ITջysfrt~~{_Ve{iFT]ǰljokoiprbz}{cdk|qZ;J[ìz~uotwtgibfuSefeKZdæoylhlec^zpyt_h`krHHUfİwxx{qmljgv]SRdruQ[Tozg{bie`edsnl}faVkqogHJAz˿hoglpniVVvzQXMr|yYTN}rdc[^[d~zmkWcM|{ygscIIKsttrmoxpbkhBRC~liSTQst^a[\]npzyj~kU]Lpk|S@>JŹffcgp~sv]Xs}RPNSw]RJXzZ`X[dhcuwZ`Rdywe~lID>[ygvltyvlxqry~nCLFqsmVNJgxv]X\Viji`{{QbRuvr}ZA5;l̯he`h{utb|tgfy~z`D_IyjOIKv~]\Wei^`Z}{eYnNz{rvtO?@DǼhouyqk]qu{|xLNWE{xWNMPǭtfRZbmgg\\x]k`NmzeE:7NѸx_iqyusago`my}jOeMN|tYKJciXfik`\Pn{tdsQXxh{UG=GhħmlqnXrxsx~zfVZ=[x]UJ[v|rQ_kjobfN{~~khH`usqNB9Su̟httx}orQ~fgw}}f\\@c}fTRh{vbmipl[\F˜pkdEk~eueKBQe}}t~}ngQu}~]ZL9uykWSena`hnvgc]WqnWCwox[JHYa̵~zw~rqcc{jtyyZcLD|rfblntonxuaaRh}hnPKresTQVhpá{vr[ti_\LNq|}smii`~kgnv|`EXzy`[UX`_RTUW^mҷ|]zwV]7FʨWghuøP|Y1*3X{qun@Ndvj~ʝHB۬L# ?oyyvloG5g^sk~“^,~P,)Drr|tyiHQxsnvּPQq=%Ouz[o[<-Xlnul{RIqˢiG8Usnqvwe[IT|qlbRVQ%2exXqF;'QĤxdpkN]ceGaqn{n~stPMGMμ|}q}Z^aeDNswbr9:&Tş[|o}|\D_c֑{av{{oszqPE?>èzt~ybbd}ڭu\\tw{nqi7?+Rǩ]xvytbPDan؛nkzY:48~y}zi|wwf]kνser}zycnF;'NŲgr|h`UDSt˟oom=-8zos}xuwiZpǖ~irw_kR+ HxszxhgYFI}}vsvB(=|Ⱦ|mzslutiToȢtq~cj^ $Iryoon\GBėz~xrpCcwsnmedcUC?Xvذ~ddlQ51>`}xur|tc>Yeusn_PH>9Ir๳nvrxwilR?b\pd{NE5.=`έ|xo~xq>OnlicOL::Lqtypzwlm]8d`hjyQK4+?`ڦ{o{xGGreilQM5=Ppɦ~zp||yqke:`cc|snYM2*B`ަ|r~}zTBmdp}vUI1?QoΥys|z}tjhCUdd}|hfL0,B_٭~w}wxaA`izs~YC3AQnӬxvzyzviiPGej~g}sF0.A^ѻ|utvmBQql\<7@Rlٵ{{us~ykiZ8er~jv}B41?]Ȧ}lvvtAEzh^:<ZӧwexytC>}lz_<>:Pgʬyfpph[3]ziuJ:2UalV958T{ۻkhtbS:Cb|ᬵijm}z\XIMYg_983QpĨkjr{\\?pzTqM@7@Xo粰imk|vWZQLYcg>61Jb̨nlru^`HgzUoX>6;Ij漤pnpw~n[Z\Lc^pC21;XԦnrp}u`bS]~Xoc87,:fvsqn|wja]fJym]vB2+,Sڠrvj|wchWX^oe933`澐xnlsmkfefJs~uarH0"'Lՠ|rkw|~vlfYVjgh<*,_޿voighimhcQdaiQ(!#JЩpnnwzyr``Qu\m<%)`ŒrrcaejuecVW^cV$""K|бqmitxq]cNzzWl>#+\ɛtp^^aptcbWP`\W&!'Kqκpkhp{m^aQo|VdD /Rʪzl`Zcsnd^ZN}}iRY+!/DiǤnlgp{l^]WeWZM '/IϺibYhqjfZ^N{|rJY1$4=dөmkeswm][]`ZQT&-+DxŔhc\kkheW_S|y~wGT;*48_ٮqjhx~tk[Xad_JV3/)@oɗieckghbW]]wyzKMH/17Yٷygnyzuj[UekhEWB,)K@^Ȼk]LIYh|tqqsuiO-A;Dv²cQDBRbptbaiqym^:2NBaļg^KMWbzş}onpywfS':AExµdPEDS\mt^_gt|l_=*NHcþh^NPS_uƤ{mlq|vfV)3GKu»~kOHIQXizq]]fxzl^B'JSb~¿l_UQN\ozmku|tfU/*MQo»~sPNLLUcun_[iywm[G'E__{³~sa]NLWh{mmxxseT7%RVhɺ}|SSMIR^sm`ZkwunYL+@g]xĴv{ebMKSd{npyvseTA%Q[c̺u\WMHPZpob[lstmYO3;j`tŴuleOKN`}{rsxtrdTH+K`a̻nhXMGLVmtd_koslZQ?9egpƶx~seRJK^{|wvutqcVO6Bca|ͿlvYMGHTl~gcgmsj\SK9\omȹs{eWKI\y{wuvpcXUD:cetïpy[PHFSklgdkrh^UU>Qvm˻ng]LJ\x~yuwob[XR7^lmƮ{qbRIDSkskbjof`W[HGxp~ͽpjbML\v|vxmc]Y[`wñtXCDQkxiZ[bebcXVevWIZdyڸqD>wdUP'-r~xskgB7d}ğ`&V­one_M&On[L4OvrŤh*yմaIHGQ@5e{uzkRKjlnǢv;#v}YQ]pqQR||iqdQDSuwue[&. rN=BNEGc|w{UMSapxU:6àcS`^KXtmR?Q\yyrUve8=EȊmF?NHTrwzGJWYs{pg|jc`SqRbi^jvq|t=WU\wRY_owLJMg˲lT]PVjowVfQUc{t[invc_UtƩxpogsvw{]LUFRvnafJT`ydPVpԢ|gTDRl{ub`GEnfpgur{flwʓqjqlvz\vc]_CWRigBev~wkNr|ysVY`ispwkaL.PvTn]xuf{|ldZ]gkUU:?hpS|W]}spbO_bYS][dbSPzViiJa^v}w_bnCov[TNSzQNL_ze~kNY[xq{wZrmkZ^æ`OIc{Tr_O_jtmhdZLvncMKjeWLOarfMW`i|oookurWXjͧw_GNhtYqWNfli}UT]]qW=Zy…maPHWomXM_Yh^r|uwoZTnyɋvTGTxsXuT^n`o~Bcdc~eAHimucNO\{kQXVS||Oru|dTfrñ~hMI^l^tL^ZbeBr\luN@ShlsPJRgsq}QXNhd\p~lT_mt[TVqZfqO[ZwWPpWuv`KRZuqdKSZqp{PPRr~bhss\[gku^XWq`sjJWh}tSPhb~|mWUOZjYTWautiCQcw}ybf~zaWbaywnbV_wct]DaowuwPUvjym\XRKr{ddZYeyq]D^c{t{Ro}k]b`k}w]T`[qLIegvpggqn[drt}c]jYPPcniYQh`@sgpzu_]hmllZ\aD:>donURaxXFujx[_kvzxiwaiWQRr¡j^O^owPO{jy{cYbiolj`ta\C9Hpi_U\itOQ|pd]bpxywfrcfVO^sWTXg|}jN]j~iX^bnlpi\o^V<=UnX^[cu|oMasrb`duumaledRWasż|L[]qqmKlkr_^agpluYag_NJf{uswzoux|}sxq]QRT\rzk}QV`qĭror|rsr|}vftgjZRMIYn{h}`4GWstpzpruy{qvymWPHOgv~jeL`giv~wrqx}~conoeXJ?KbuzivE;Rfsszpusy{}}zhydUBDWm~vn{VWfvlz{xqvv~{q`~nnbO;BSmscd>E^uwmsuuvzyutp{s^E;K^yogtU_p|mt}wuty|pqejnX@?Ph{}QaZB_o|esz|zvxrlyzr[A@F\qsTun\pxo|nvssnpzpyweRB?DZnlOgUJdwxgx|~wyqmteLA@Ncv~hZ{lbt~kyttsjow~smXKC@M_vzfSjTPlzql}}xshrysXI@DWh~~wg_jgy{a|y}tjhw{t\RJAETf{yycVoQXouf}p|simyaVF@K]p}w{cegnuwz[|mfq{tQSDBKZmxx~]^qO^ix|gyw~yllv|\T@DPdty{~^pgppla~vgmxtqQNAHPanw}YioP]hrpx|rkty[M@JUis{z_|fmsfjnkt{siPHELTdm|z]qkN\pov}wmrxxVHELYjtyd}bm{dqwkqunbKIJLWcr{`tbKbxl~yonuylRLGL[h{{fuau^~qor|soXNNIMWezy^uXOj~h~qnqv{bWKEMZn~wgjhy|_wrowhw}SUJGLXm|o^qPYoo|xmpqvaWDDK`tlkgp}tntoqe|uXRDDH_pb^iQa|~vqnmyx`O??Ngwcmhvw{|qn}}dtTK>>OdyU_gRo|sqltyWG8@UkXv|jtxpxthiOC7BSmtJga]zrulr{kN96EZymXwzntxw}nzYF7:D]}_Llcrussrxw}}X?4:Ik}c`}mzusx{q|I=7;MmxUQvl{orzwsrs}~C;5?Yt}z]m~uounkpne>?7E[q{qId~orw}~|rkfr_>:;Oat|{|r]}h{m`dt}lzS@>BO_qw|wbNv{qlvg\guz~P=@HRbkt|zpiuynq]ZhwmftJCGHQ]gxyr]Urpo~i\]ivutEGIMTZgx{mruz~zz{a[^m[olHNHKMXlyxrT`}i~{{}q`]`tjzgGLJMK[m{izs}}k_\dwo[veLMHEI`q}xiOeyq}qia^j{i|aHMHDMaszczxph`amy{i_}_KM@AQfy~q`Fn}z|roicfnwmVJJ@FUh|p[sqgego|{{dk~TLC=HYp}xmPI}~|lqhhgsquxNH?BL]r{acqqjigrw{~ydtuMDDP^ltnicZbs{xnc]osba?=GT_lqsxztzwj__o|c]bA>HUboqqmf_lv{vh]cwh]]JP[fnwxque~ȮiT\`gw}v~mo_grfqgcgmsɹ}8$v-7K/.C`ogoxĸZuQ[vc]WeǼ͓>-d50D=37j|mpvR7MkBt{jVRuۭg/ڹV@2,1Gf^\UcrhsekљjmzsYѳƯ~),aJKP@)PfGKa`tTUpdgldbsʝ[*8Dⴔ~b0KSOMC`u_ZZNGZӣZX^yrɪU#'Cܱ]PBg?Qpʨg~l;.*rƲ|YuFDBbzs}lEa`bpuǢ|u{pRHaybeHcUfblDEEGMLpj~geg+$ 6_ͲzpBONcppt\B:Hmwl}zzpe?EѲh{]JJQxanBSadbiXng~vr|WN&@ʏU_d_iwhycgLCUd`Wzc_bnf&BӍzSPJ]xrr[EDCRxwf\ifz~kW_4``McqiWjZZgl~okNaxwks~l,9kYiei}{p]~eZEKYfKMBhsq|d~hj:"8}ܲȒz]Th{i|iuy`\IHhjEkzriqse<(sұgoZWwPi\LN]iuk\?-F``sml33 :zuyqjbFqzfMJnqhN[^dDzny`[QO$-dԮtV[~{uxOLCLRyo8;4UmйmrZ^xjJ3-S갞spIJ]fldnr}scLQ^aذtuyq|~|yvD-?wվ}twcdR_CWl{b:O&%DSҦiwmoxrMJ3)FYkϯ{T]NRvmrglsZDoKLAOڴunczougZG=-6PĜydrfS>S`liTS/'9{ٞ]cjm|ejM2 344:Ŭhj:OPU~GT^4GGbЛnnsq^]HIGB:}nf{Oic^elnu>PM2+.qɹÈo\ao}raWXʺgrp^\fzvbl]6,)=rĠzh[k`\a_M>-@[̴bUAL\qrL1()WuȔrkkcq}m\(2"JČzVST~zOa9<7):O^}ʝjbYa}{b8(5k؜qoYkx}~uN;/7Oe{ɡzk^gw?$>iյuebauwco<7&3S`\{q}r~ivyxcW3;5`ޮ{jXdt}sAFAFZhp}ikdz~FKZ򸑌\opzTrhC?BHNFVjsmkpywR"*.^z}x|ou[D?Jghqn¿}wxtubY[Siac|gnA;DYUZON6^ƶthvxIA3Xʙ^cpgXaYUQ`aɚgieWZ3%O۽ʖ|tv{iQJDR\c]C:fZuxrk]~gMPпogbaf~{PEG%=Eh֧xlzeeG8,31:N]fRTn}ug5D0+8Zƫi`orJ;)''@dĔ|q}i^OFJE9-@wvqlS]nmveMV#!#I˱ylvs|\aK:.495QtϺeab[uSFJ&29MmȶzjfKJB?@5HT|˿irnjnrlnETM85!GuٮwliqYU:BHI? GAs͛uv__zbkA++-:emklr_XNF>HL›lqc`vgIX80$*6Ti{q|yoR?EECI%17.b{͜qmmvsf`CF41:a~ö_mZUdl]@!!.g̲yif|{u^o_>1 $-Np}kdr`XTLG65->YmûzhgmvyR@0(>TgwrqsrYZid]D(1"Fr௪{qiXhDM@207H[RxuheTSgf[-)$@s׻Ƨwrx|`Q-*8H_zpxifios|m`3,!);i̼̝xt}noc>D+1HQSMBolx}kbak]M2C$.\Ο~~lwX\GBILYVWlg^fpg=@)nƻ\~iRH9[޾î}|eo@4/AKHIYNҽ~{iijptirKBD ';gҵsiwkac?>-8?=BQPկf]`vGC.4_j}kFD7?BAPJGđbnw~qZ1L7)-Ijəos{kU6-.FMDO9GxsÏolruVV("(KvԷʵ}`tMRMLSN<:My|lfkzRKD+SpĮm{QJ77==DK-[ag彠rjqX[G/,4Uu٭soFF>RdYL0EAi͛}u{{nVf;'&=bžb^PSGIL?>'XMkàznjvie.">]{~qcYLGQTKM!071_Ǡxx}SVQ78(2@Yr{ȳw~SS?@SXM96*2^߿|w}y`9'1KpnnTbYZ_UL)4$>[}ٻųvyz[g=4&+?Q^j\zbxi\EENFI,/91Gkߺʩ|{|ON617Kbni_\SLZogU$ ++Q°czmF>%*3D^``gr]ib\\WI83$(7DbګkQB+8KXdQh|onWO\`[D3? ;hڻƳsbyNQJ<9:?I5dupbcWTdf[0(+ $Gmս{shB04?O^Q|ƠnjfgjqeV1;)7_൫zrHN88HQR@-a]s²xwoyla\d\I2K*4\ҭpmqYLHBHAfșkjbs}tS2H$&>XytswzhzP6%!+9K[Ie{Ɣp}UAT24A]}ƸϽyenB82AMKDA'Tsɭzvx{rtSQN(&.JrǩcciFH:=@<;;1nx}Ʊtnz_[>'Diĭf|rLJ>BGDK?-^[pܮz}}c>S@44AXlϭe~cA<9ITJK4/VNoԫrvD1(8UxdwTLTV[[G?.POrշã~cZV*(,Jcscd`WOJGF@F,GNHaԾėlteC@9K^kɴRJGRbeYD!5(3_廴x[tU:61@Rd~_ndNX_`_RD(@:Lfɤ|Z?39Reovǽsva[\[\SQ78=$3Mѫj]e?DBFRVYcZpaVQV\e]L&3+6\ƺѲtbUA=FOhttǻ[Y_lmk^B!5$%5Uۭo_=86G\]dT\yƭdtrddcfaYO61D$'0Ovֿ˨umb_URSNVIphZY_lqi?;4 !R̸Թuv\QD?EKNXLsvsνw{WW\ourdL"3'-7Vt¹ibPMX[ZNHrkzǹ|xjiggbM=T,>jĻӪrePUQQULJ4Bie}¼za\[cksnG8;0WwnYOOHNBcmbvӹtkowzsa:D9"&>`Ǻ^WJFQ^[K1OHNkɼlrjlkmjV9T: &Baƨút}wogXI53ZUk{~ijmu{dLX( &Ge~siUKQPN;JY@M[xà{eccusG@H'#$0@Uij|vninfT3EE>WlxrpiH_\4, 1TnünmfeccU?+I0KRg}Ѳgoi82#"2;arYr\LC2%&)+$Lvrȩ~r{uLAQ*2:DYrrt{tqT=+$%4BD0OYUuټ\^_672;VvʴwooEA8?CC:&@Jaˮȟx{{xWeQ))(7Xz{\pmQR?:557)']Q[nď}goH*,8MgzpuSI90Ifٲkjb51/EYepladgb\TONNHC)F?.?ZҧqxWSDIQVgdq\QKSai`F%75dۼ̬deG@7ANXncrȵhseMVbij_N*=-9IhĶ״pVKGTaakUr˼medcaZU7?F!#,Q~ã|jvLJMRY[UUBrý~cVQ[elk]0=3&>lйvwocZTQVYUͣkddtytkP'?, -<]}ŻzPFEO_ddOKm_oʹ~x_ceqqg_B2Q.&,<[{ӯŸzxjqwtm]Q5KqoŸ|phmqqtMDN6]~ɴȺzrc[[XWSS@bi^oٷgTTe|yl7:9)6MgxļyqlqsrjT:\RUqϥxk[8XI*)'2IbǶ^\alle\;3WM_tŸrh]eptzdFZ0(Fgx}оvfX8KcN^lȫ¤|zxODQ+'.5@MXgmýo_^`ehZ:QN:LdɰvtrxzpdBS[7-*-:K^j|Ýzw}qJ-KDIbwͩ|wkUhT/%!(:ITUvèupf[P8CbLHO_{ķtim|x^O^?*.49BEFHvº}~mJPP51%$%(IVeo{ƶwt~\B5.),)#1RbqŶm_QNe`I@:@Qbyʶ|~{rnvlRC@>6/#7Wct̰~xuR517Jctrt~udUH6'=Y`n̸o\[^J@DHNSXgzrkowZD91-0.4HMM\rǸy~{^I<7=OZȩxơzfszlt`?jx__UHtiomf~gRSSX¿ἃTzʼ}y{{zV=SZL[cXSRpv_aL@P[trhyfPPVwyty~}YFA9D1;^Zh}îzm`WL'-0%4Z|{mru~ǰktt{yunx}hq^EACWVKYSPWYoz~~na_D;Nesjjp_QTarsxu|wYEHG;2:LWgȹor[C2#%'FVpzipžo{vxz{p}k][FJNEUMEPNdf^|~}uYSI>_hit`daSbX^rdp||uw\QV;:4-M]z~ԾqRH(%'>CNsxpirȴ{stmx}}v]VVRO>@J@@N`iZ^syzhRMPS]bhk^akeZOVc\hstmTJ703.SxuѻylK?*4:5BEiwvu|tfϿȸnxpvrwZQiRD:1I;>`[_WRtutlMVcLW]coZky]UFMaUputNB2221_yz{ǼľsaN>*#7819Gbuysv}ÿv}yqwtzxmc\aR864;GIVWPWUc{ea[_NE_bgpjr`IOHWlmxP8=*6FPsu{̲ǰsdY:?0(7,9C?b{|uzź´±y]aH5(*5Bgnzs~ȺurwwqzwtgW=@>@LAIPBEIWg\ju^M=BFO`aebSV^bd]grkq~i_WE835=C^zthN?*!%O\i}|m|΅qtzjlixxroYN6:QCDHFRFH^U[_`y{qNB4DVES^X]P_p^ccbtn}ygl]64-+?Lw}ĻĹ{WH."AVbxliѴxlrjelxtl[NHAEGED?HVVSTWYUgulYDABGKHLRWbbcke__kʻ~}p_Q0#'3SdzǿqC0$!0,Dddwwjxdzt]jkqw`_VTP8EG7AEZdMTRIX`r\`OKC:ND=QYjfYj`Wbj~̿raX2)7R\o~þpI1-,,+9P^qzx{ǭp]cwyuz|yh_d[L?7::@QZ`\KGHTkzs``dMA>;B?Nfde\RYV`ruvƮ}dM>%1@F`gw|t˼ïwkU9;+'/)EauwǬypaiuxxnzpjlTM;&6=JVP_W@FK`oqwufidKC33EJ[daeQIW^jknwsļx[LD87;HX^rxs̾ujbJ6+$%'Akw̧|tvq{zrurpeT@4$7MINLOR@K]^kpxubaG966W]Xe[XND`d^farursZPV:5>B[azyu|ŵtyj@2 &(Srx~yl~ynytvx}k^H>0,CGBEAMNKXXZdjp]VE;:I_\W[XTMN[XQUZiuti_U<-:GZq~u{ʸzj@&%);Mmr}yorvʡzdnzrdYN9?67D8AFG_VOTL_j{qRVI=MKX[M]YW_MPQFUVh~̽Ĺx\Q:+B[l{vpqzӷtfhwoZPHE8+4;>FP`\IFMXjo[LOMJJHTWT`d_Q?GNL]l~xí{cFB41Obtxt{sƿx^U3$1.8:Cjvptխpqbs~z~aYYB:&%<;KYT\J@MPl|pfLSVCC?NOcnp~x~ʺkNC>7JZdqpz|yzҹ}hXI61-,5>Tv}~švklo|{{zyzkYI4+$)ESWWQTKBUgwxifVNI79>JfgcaOI==Zbelk{|Dzn]G>LCHZ_qs{t|~ʫ~t`VV6)*&:Ebֹ~qqxhqypwvcO?**%7SQVTMUORbew{xrpfHA:-3>Xlb`YLJFRddjkmſfUJJIAER_rxtʽvnhZK3!"+B^t~Ū|}rnfepp{z^?4(09]g~tǷrmdUct}~{jF735;7BMJQXah\X\e}}m[@(,6=GQ_`UVZZVPU\_k|ƻzzwcRD=<>Tqx|ºztthQ<./CPgqxw}ӽzf\[]y~rgV=@94:1>NQcabeNRdxzqRB1+=;@MM^`\eYVQDUcoʹӽeU@3@EYqy}s{whK:6,4>K^cvxzsb^bhxznhaOA72.*8P^a_]ZNQiygTE=57=6EY[mxwyrèt]NWkluuӿv^KACW\]jmtwx{yvĸ}{m]QJE5,3?Rh̲rvvtlnzz|ykK0)#+9EWVQRNXabq{snhTD-,8;Shqt[LF;EKRdbht~IJtm`LROV_Wgpu|{|mswzɸr{oeaJB1#4@Yu{}}ĭtrfi}ugE.*'7==LLMRRde^iq{jQ='*9G^hllYLFDKGGT[ftvlf]WOOW[evxnonq}{}û~uw}qaJ61(0J]ou}ozɲsgjh{t^P3-65:49MLU`_eYZr}|mO:0%Siyí~rkVEELasyzpjam|ȿ{|{dL?2ICEOYk{жtijpuqt|xrleZB.('/=K[_ZUR[jvfQJ>4*):HWmvueLA*-2;OQX^TY]nzznRB>.-+3P`r{neO:;GQjyynjflhfsxȺtfSLIGKJNUU_oȱxvqmlksxrdP@6,.-#)4QoxxhWK9:GNVVawнŹs[K>N_ixy~}nqofbXdw|{¯|jdWMVNHFCSZb{ê}tlennxsaJ8916EEICBQU`otqP:' *5UqurcVPCDJHMLXrȺ¥s_QGR^dmt}|yqbYS]t~ytle`YVPB>CPamxʾmghkw}w\K>9@>@B9CLeusyx}ʼqegl{~wsaPF?HG<;8>DJ]fceizyzcC,%)8COaghea`TC==ERdŽƴvj\LBKYh}p`VRZh}ynsriXB<:=Pbqwqu|xkcpvxuhdYLMIGC58?DQhpʵ{cP@6H\sg^X]ik{ywxk`K79>L]esvqzþxtnmpnoh^^[VOEC=5;FRXUWZ[h}vxs^PA2+(-7F`txreXLAFUbltԿʼoW@45Nm~ygcfdikuvpaN@5@MR`gnxzǶw|vikdbb]e_PI<:>=KRSVLO[e}|ywj[U>*$*>Ts{tkWQOMY_emn˭w^@55Catypmeccez}n`OG@@INU\fxϺ{nc^_ehfaTD:8BJLPPLHIXkx|ukZH/$=Vhxte[TY\X]_`jzƷľz[G9>FTnw}}lfZYcsj^WNNBAJIUfxҼ{o\]bkqe]Q=;HMQLAABKV[got~wy{t[A/#4AS[`hcef_]PIPXoªg]NCCDXp~cWU\q||rlkZK:-9H^u}~µ}srnuyslXKIFKLII?;@HTUT^esuv~~iO>4-28BMT`knk`WOEFSduʺȷs_K>=Gao]W^gqwuhS>2.@Xfsvz~{¼~x~yxkbWLRQJG>A@=KPONGTfyyw|tdQEC615:KVevpfXJKGL]dowIJu[D8@LkxqaadcilwucK<54HYajmw|}ŵwpid]WWOD>;AFJNMFBBQi{~ug\RH7*.:L_ltl[PIKQTZ^amʹľwUD;BWkzvqea\\htt]P@=>BPV^lu}|yǶsmnhj_RL=:=APPIF<>BMl{}yoiXG7&+9MeklhYSOR]XUVXi|Ʒİ{YIAHXbs~k]VWetwcVLFB;@LYjz~|~ʳ}vopspdOA=:?JRTI?=@GUXdv}vz{~vbL6/-4FR[^Y]`be`\WOTbvžȸzhWMFGP\rzjZVaq|yuiYG6.0Cawðxsv~|vkWG99FKMMHGABMQTW^s{uty}wiP?21:;DNRY[bnjf`XZYbvûŻzmSE>=M_{rjaequ~zbO:+.8Smz}οwvyvlZKB=DJIIDEKMQRPPQ`}uw}~sgUD<8;;:@GR^gnng_X[dkt~ƽxbG76B[swqqonrux{pT=0.;J^ry|ó|yy{h\RJIBCE?BGPZUQNFKWr~|~q_WMFC::63BQbmjkdY[^jurx}Ͼŷ}hJ46AZo|~y|wmljp|~rV@76?GSdn{~͹~z~wi`\VNB;::ALW^YNFBIZqxl^YXPE;313@Velia]Z]houtptȻŻcM;;JXitzzmbfo|k\HB?Pl˱zvy|wkZE5,3DOUTOJBBMVam|ztonnf^M:1.;KT^`[WS\gghdbdgxÿwe_TLOR^lyogcct~z|ytoYE6,4AZy{۾|~weM;41@LMNHGIFMRS[cxxxy|rbUC86;KRTXWWXZdd_\X^fp}rgVIHQatvkhkpwzyvt{qV;=@GDBEFORMMJPaw|{nXKDPZirx~pjqµ~r_RHD@:;<@GNWVKDBL`yxgUKHGHGFHKQ[dgbWSRT]itxv|ʿ̿|cQGGXl|{wxxxsiims|kWC6:AJRXiwwplq̸~yi\VH>3.9?KWXVJ>AI_zud[NNPHC=?LS`kf`QKRVcnquqtŻɼeTMKYiryvx~~vqgfnvn_QDA@@EK\r~trvܿ|xm`O<0)/?MWWQKB@K]qsg]XSNG<7:FXdjg]SLO]hnpnqv¼úkZQUX^gjpu{znkjn}}v~ui_YJ?;8CQg|а}~~lWC/*+7LRURJKIM[drumea\MC9/3=Qdgf_URR\jlnnnxǺrd[Y[WZ_eq{}rlpv~|sq~~wrngZG838I_vé}wwx~{cI6+-7ALPMLLRXY^en|}rolaS@2..:LZed\YVZ`cjjhnvzmgaYWQR_l~|rpryqqwxvyyzp`P906E]n}yϼ{nox~mP=429;@GEIPX`]Z\ar~yseT@-*0;IS\`[\_bebbcdn{¿þ|xpeYQLM[n~ysu{{ups|}urzucN=38HXgqz{v~̽tmmz|jXE><8869BJW_`^UR]ovgP<0,6>DMQX_aihc_WZdpʾufXJKR_s|~tux|uttywuwu`LB<@KS^emwwƬrqtvk`SI?50,2BQ]_\WOP_uvaL>76:=?CKXfmmg]TOVgw¹̺uaQJO^jtzxtqvzxuw~~~~z|}k\NJIFIMR]ky}ǩx{{xqm_J<-'*4JWZZROPUi{n\OEC;787APbsrj`QNP\ox~ij~kYSR^jlqqptv{tsx{zvs~»}rg^WTLB@BN_sռ~r[B0&(2@MRPMKPYcoz|k`YPD7/2:J`oul^TMPYbloqxrg]^cglijos|}topuymjqyvrlj_SF8:CSiw~˺|w~oT:/*1<@FGFKNYbdjq{slbO?.)3AVhnodYUSX[[`cjx|qlhifdeekt}wqlmt~zogiu|uvzxo_L?7;J[jrv|tv̿}wwiS>457:99@EMX^b__kztcL7-*6JXdhgf^\]ZWQS^i{ttqic]_gn|{njmq|tkikzzxy}o]I@=@OZbgit|orİzxz}iZJ@=73//;FS]]\ZZh{yaJ7/.9HRZ_djihdZQKM[mï}ysg\X\iv~ulhnx|vnmryz|~o\MDEJOVX[cn{trͺ~x}|qc[OA7,(,4GV[[VU\e{fO?457;EKUakusg\NIMWm~ȱ}pdWTZgz|qmko{}xyxvzxy~½~xn^SLLRPOQS^l}{{}{vpi^K8+%)3CRWVSSZgxqZI@:622;GXkx{q`SKMXgwžɻq_UPTcq|~xsopuy||xx{{uqxviaYUVTRNIMWg{ͳ}xswxywiYD.'(2@GOQOSYhyviXMD6.'(9Mew{xjYST]gnxõƼt^POS_krxuqtw|{wwtu|tkm|}sf___]XQLGJWgxһwokoz}q]H5+-5=BDGKQ]kwzqi\K;*,F_pxumb[^bfilt·¾s_ROW]djmqux|umnt~riky{leacgaYOFGMYjt|´shio{~r^I=459:<:2++5Kdw|pbVRW`hmt±s^PKUft}wvwupjiovtkp|rd[UXZWTNMQVdouvrtx|ͽwtuy|o\G;207:<:9CO^r|skbO<,#*8Pjwztic\[__`ai}ǻn[SR\jrxz}|ri`bn{{niq~nd`]^\SLHLU_inmkjoy}uuz|kXH?<;;8314?Qbpz~wjS:)#);Pcpqolihe`[VYdx¿ǹqaYZ\chlv~}oa^anvpmt{qjhf_WMEHN[gijfdio|ſ|xxxiYOHHE<4-,3=P`ipvnX>,&*9HUahlqrtnaXQUbvʻxkb^YVZam}pc_cn|xuvzvspk_RHDFQ]gidabgr}ƶ|~|qg^VTQJ@2+*1?LX`emz}t\G6,/5INTZbrxeQC94214;H\p~|reZZdqȾ{kYIADUm~snprsstw{|ueSFAGRZ`bbceoz{}Ͼ|rhc`de]SE9318@DHHMZkwhZOH>2,(.=Rl}~rhcbjrxɳr\F=@Nex~zyuqmjp{~vyweSIFIQVYZ[bkt}}yuzͽznedfhh_RD:9;>AA@?DUk|sib\SF5'!'9Pgw|xqlmoqstv~ǷuZG>CP_py~vleem}xvzvcVNNNNQOQYds{|{uqszǹvkhjnle[LB>@FD@<69CUm~spnlgYH5# (;Qanspqrvysolmyļt[MGKRYenwrd`eqzuzte]YVOHFGN]ly|wqmnvºvprtsj^RHDGJIC922:IZkyvqtxuiWB0%&3BQ[biou{|xndcj|¿¿taWSOPRZjzyiablxzxzwmiaUI>=FTepuskhjq{ļ~ypbTOMNPKE:006CPYeo{ywytcN<305=DLS]kvwnb^erĺzmbVMIM]t~offlruuvvx~ym\J=9@P_ikigdhpvwusxĶte[TUWTNC9317AHMQ[l|zjYKB@=;<=ERbu~~wkc`bnyǷ½ubQEGRhzqmonljipv~|iTC<=IW^babfjptrmgiuɼ|md_\^ZQF;78:@BBBFUm|qdZSNIA748FWjy|vmecfkquy;v]LEK[o|wqib`ep}~zcQEBFLSVW\blturjbafv´wohge_WJ@<89CToskfa^WL@208G\ksuojikqrpopzȺw]NIO^kv~}oc[]er{xzdVMKJHHJOZfrxtkb]`ixпztrpi\PE?@DIIA836AUlzpjjjf\L<2/8IZeijjkouxvnikvĽzeUPTZagp{qb\^ht|~zuw~}oaZTKD=@JXiuyvkcadnw{{vj[KCADKMIB626AScswpmmpj]L;43:HS[^`fmu|{vogjuobZWUUX`pwgacktwwurvyqh]OA87BTgtxvpihlsxy}Ⱦ{m\MBBHMNJB;69CNYcn}{rprql`N?66>BIMNNMSby|thXMEBDCB?;?JYku{{usu}ŵziTC?FXl{zridfmxt]H=:AJQZ`fp{wmipųpbZVSNFA;97>M^kqrqos{}gSECLZhsy~zlcbgrzp]MEDDFHLVcs{nhis®~sje`VK>89?JQRND?DPf|}k]USURJA75;FVbhifipy¼wbUNOW]djp{rebfovwwvw|maXQJB>=DUhz|qiioyҼ|wobQB76>HPPKD>?JZkztg`^]YPD95;FR[__^ajw~{}¾qd^\YYY\erukfiorqlkpzzqlaRD74:I_qyztokjpsv{ɼ~mYF;8>ILLF@?AIU^hr~|rlkg^SF<;?JTVXVW^fs||zttzƿvpkcYST]l}xqnoqojebgt}s`K:14ARclnlkmpqpljn~|fSE@BFIFA??DLQUW]j}zxqfWIB?DLRTQPSYdouwspqvzoaSOUbu}xvxuog_]`k|r\E85;HS[`chmswrjb_i~ôxcVMJHDA=;?ENRPNNVh~q`PFDGMPONKMU`jooljlt~ù~m]RPYhw~xka[\cnzq[I>=BGLOU^ht{{se]]h~ïyj_YQI?76:CMSSMFHSgn\OHINOMIFJPZfkkfcfmxý~k\UU^jrwwx}xj`]`iptx|r`SJGECBDL[jy|sg_`j|Ű~vmdXI;12:FPTPKEGSe{p^SNNOMGBAGR^giea_dp}¼|k_Z\afjkmrzxlebglnnlq|th_YQH@:;DTix~|tlffoy͸{p^L:008FOQOIGJTdrue[USNG@:;CQ_eeb\]co}ý|mc_`dcaabkv}snmprojeepvpkf^PB75>Marz{wsrswz}Ŷ|iS?317AILKHIOXbks{of`YOC935?N[bb^[\doyļxngefea[X\et}wuxzwqg``i|zwuvqfWD75;9NX[YVX_oqZJ@>??@@CLXgopkc_cmþxl`YY`jopolnt}|qljknmkjn|~sg_ZXSKC=>HXjy}{tnnr|ï|vgR?/*/:HPRQPT^lz~iZQLG@:8=HXgpoh_[_iv|ne`bioqniilt~wqoqspkc^bp}tnjhe\PA88@P`kqomlnuz}¶vaI8019@EGFJP\jsy~zmd\QD816BSclmg_]_gpvz}tifhnqplgflt}{uqrvzwnbYYdvvrssqhYI;7=HU^bdfjpvzyvx·q[G<9:<<;=DLQUUYblv{ztnqλo^QGD?9416AO^fiijrwbL:23=LW]_`cgknlhdep|tqstpidchqz}volqx|rf\[`kvzy{~|qbSIFGJLKJLTapz|wplpֿrf]UL@5,+2AR]cccgq~eN=57>ELQU\entrlc_bmÿzxvqjb^ajv}}yqlmr|{qidchkosx~~yn`VPOQOKFCGRaqz{vonsŭ~vpjbTC4))1?OX\]^frmVF=;<=>BIVeryvlc]al}¼~xmc[X]it||volnu|zsonopnjhkvvjaZWXXUMEADP_nwyvssyлzxwvn`M9-*0FTevwpfTB/$$/BWfmnllmrwxyz}hYSU]dhiikqx~{tmlr|sf^al|{lcbege]RHFJQY]^^_gtų{w{sdUKFC?:537DVhu}|mX@-$'4DS]cgkrxzxtpr}þyg]Z]_```eny}tkhlwxmedis{wmikkf_TLILSXXVRT[hxDZxkaZVRI=3,.9J\iqtxkRFLQWaqo_TKD<637EXl{~yofbgq|Ķq`TQXgv~xrqsuupkijpvyvmecjztdYTV[]ZRIEDKU]`__ckx|ww{}vhVD947;>@AGTez|rhaUF8..8Kaqywpkijpux{Ǽk[TVaox|{xyy{{vnfbfnw|yqe_bnna[Z^_[SJFGLSXXVSWao}Ÿ|{scSGA??<868BTi|zujZE4+-:M_kponnrturopx¹~i\X\dkprtx~~sg``gs{|vkcagt}ndaba]ULHINUXVQMNUcr}̻{o`VOLJC:2.3@Thwp\F4-0?J\t}smjf_QA0'*7J[ipruyƻq[MHMW`hnrywj`_fq{~zslms}tcXSSROLIJPZdhe^XW^k|ȿztsrneZPKKNNJA96=Lavxtttm`N;.+3?MX_elv~y}m]UTVX[^fq~nc_cmtxuplnu~shb^XOFACKWbgd\USXcpz˼|sh[SPRUTOD947DUer~~{}~ykYG;68?EJOVap~{uvĹ}pgb]WSS[j{rgceknmkhiow~ysk_QD=?IU_a]VPPU^fkouózk^WVZ\YPD958BMX`ivq`QGDCCBABJXk{wrt}ʾ|th[PMTcwxoklljea`enx~|xy{kXG>>FRY[XRPQV\__^bnʼse^]`a\SG>:67AQcpvvtuyʿwbSOS`mv|}~}reYUXbnvxuqrxydTKILMLJILS[a`ZQLO\o·upnkdZOHFHLMHA<>J^wvmjihcWH;59DS`gjmqx}ŽweXUY`gkou|scYV[enrqnmr|{j]VRNIC@DLWbebXOLP]n~Ź~zumaTKHJOSPI?:=H\puomomdWG<8IWfs~zsrroeWI?=@FKNOS[iy~rkfb\VRT_o}qhegiifbcis{vpfXH;6:FVafd^ZY]ejnszķtbRJHNTVRJC@CLV_goz|ywodVJCBFIHGEHScuȺxtph]RJLWi{wsqqnha\^gt{o]J;6:FS\_^]^bgkkiiowcTMMQTSMGCEKRWZ\bnzn_SJGIKJF@>COar~ʻ~xm]NGIUfv~}}}ype\Y]hu~s_K>:?GOTVW\ckpnicaj|˻weYUUTQLFCELSWVTSZjzjZPMNQOIA;;DScovyzŸ|l[MHNZgpvy{|n`XYaluzzz{r^NECEHHIKR]iqrlc\^j~Ƹwjc_[SJC@CKTYWRMNYmvdYTUWUND<;@LYchjnvƾxgYRRX_dhjqzue[Z_gmpppu|k^UOLGB=>GTcnpkbZZapº|uofZLA=AJSXVPIIQatoc^]]YQF>=BKUZ\]akyqe_^_`^]_gtykb_bfgfdfmzvlf`WL?65=M]gjf^YY_is}þsbPB>BKSUQKFHO\juzokid\RGBCIQUVTSXcs·xqnmjd]XYbqyoighfb\Y\fv~zwqdSA529FU^`]YX[`ehkq~ømYJDFLQQLHFIQZbgmuzumaTJFHOUVSNMQ]lyƻ{{zul`VU\jy{urqpjbWQS]m}taL<57AKRUTTW]cec__h{̾~hXOMNNLGCDJRZ]^^cpxhYMILRWWRLIMWdovyy{ƿvh\VYcpy}}zxy{{ui[QNUaozs^L@@IUbjjd[UXeyƷtle\QE<9?IT[[WRS]nydWQQUVRMHHMV`eedelx}pe_`eikjijq|wi]WX^cfgjq}qe\VQKC:7:EUckjc\X\hwʼypcSC97>JUZYTRU^n~k^XVURLECFOY`b_]_gvymfceiifb`cmz|ofbcdda^_gv}rkfc]SF:36AQ`hjea`dlvƿ|lYF:7=CNY__[XZdrĻ~sjefikic\Z^iw|tonomg_XW`pwqnnmgZJ;35@O[bdccgmtxz}ùs]J>;@FKMMMQZfpvz|vndXJ>8:COY][VUZesúymffjnmh_XW]iv}}xuvxysh]TT]m~xporutl]K=7:CMTX[^dmuzyvu|̼t`OFDEFEDDIS`krsru}zkYG:6IXemomlqlUC::AJOQOOS[enrrsw½}qmossof^Z]clqqnlov{n`XX^fmrv|xsuz~{qcVMJIIFB@DN^mvwrlip˵~ric\RF:46@P^fhfejv~eQEADGIIHJQ\flmjgju|wwxvpg`]ahnpmhfir}}qf`^adeego|z{|sg^XVTOF=79CScmokfflxŶ~zul^L<33LY`b`_bju~ǿ{jTB76?CFFFJR_my}thYI=8=JXcfd^\^fpx|~xlgjrwxtlfejqwxuollpuunbVOQ\m~vomqusjZK?:GUerxzx{~lXE:9ANY__]\`fmrsst|skkpvwtmhgkrwwrkgipwzvl^TPUamw~sprvwqeWKEDEFC@>BM\iqrnlp~Ծuh_XRJ@749FWgpsstzoXF<=DLSUVX]emqqmjmxuopstqlgfjrxxtlfejt{}wk`YW\chmqxvtwxvnbWPNNMHA:9?L\ionkkq¯wpke[M?307EUbilmr|u^LCBDGHILS^jrtoieiv|vusoic`cluzyridelu||vmeabdeecelz|z{yri_XVWWTLA97>KZfkkjlt̺yvupeUC505AOY`cgo|kYOJFB><@JYhrupieitztlaYX^hsyxqhdfmuyytmjjlnke^\bpyod[XY]_ZQD;9@KX`degmxƻ{yz|xl[H:59AJQUYam}tg]TJ?516CUfprnhglwĸwi\ST\hrvsmhgkrvwsnkmqvuodZW]l~zl`XX]bb\PE>>ENUYZ]cn|ʿyz{n]LA>@DFHIP\m}tk^N=0+2ASbjkihkr|ĸxfXRU^iopmijnswvqkgjqy|xnaXX`n|xh]Z]bd`WLECGLPQPPVbqdz~xj[PJHGD@=@K\pucM9-,5CQ\`beksz~vcXV[bghgfipwzxqhcdlv|{sh^[^gqx}tf`adeaYPJILPPMHEIUeuȰ|pe]XVQI@86>Nbsw`I925=FMRW^hs|~{vtzÿrfaaccb`agq{{rhabisyysjc`chlnoszrljjgaXPLNRUSLC>@JYhswy{­~skgfc]QC627EVeouzoYH?=?ABDJUds|}xplq}ûvqmic]Z]gt~}shbcjqtrlgdeilkgegr{xtlaVOOSXYSI?9;ER]dgio{ϾuqprpgXF837ANW_dmyzhZQLF@;:AOaqzztlimxƼ|rf[W[ft~tlhimpojecdjnokc]_izrcWQRW\\UJ@:;BKRUVZcqǿzvx{zqbPB;;AGLOS]mynf^RF:48DWhsvrmknu~n_WXboz~|vqoprqld^^bjqrlc[Yap}l]UUZ^^WMD??DIJIHKUdwɸ|}yl\OGEEFDBDL\qyreSA42:IZflmklpv|~~~Ļ~j]Y]gqvvtstxzwpf]Y^gptqh^Z]gt{j^Z\_^YQIEFILJFA@FUgxι~tf[TQOLE>9=I\ryhR?54=JU]adipx~{vv~~la^bgkllnszzobZZ`jqrme_^clty||mdba`ZRKHINRQKB<=FUft|ӽznd^\\YQF;6:GYm{lWE;:?FLPU\gt~yss|qheedb`agq}|ob\]ckomhcbekprrqt|tmjf_VLGGMTXUMC<>GUblsxðujcabd`WI<69ETcowr^NDAAAABFQ`q{uu}ö{smibZTT]jyticchlliecekqtrmijs~ytmbTIDFNV[XQHBCKU_eioyɹsgabfhe[MA;=EPZbisudWPKF@:6:FYl{|xy˻~xnbULLUeu}unlmnmic`ahpwwrjegp~scSGCGPXZVOIGKRX\]_esºqfbeijdYMDADJPTW\h{uia\VL@5/3ATgu{{y{;sbRIJTcqxzwtsuvslc\[akuywogdisubQGFKRVUPLKNTXYWTV`qƽqihjkh_UMIKNONLLTd{}tolg]N=0-4CUclpsxɽs_QKOYckoprv|}th]X[dovvqifhoys`SMNQRPKHJOV[ZUNLR`svrqoiaWQPRUTOGCFSg}{xxul\J913DIMQYfuž{neba_\Y[cp~qd\\`fhgdbemu{{wst{zqkf_UI@>CMW[XPGCEO[gowƵrcYVZ_a]RG??GTbmv~o^QJHHGDBEN]p||ĵ}xsmd[TU_n~siccdda\Z\cmvzxsnpy}vj[K?=BLUXTLECFMTY]bm¸zi^\_dd_TIBBHPX^cl{ug]XVSLC<>DMTX[ajw{mc`addcdhq|scWSV]cfecelv{{zlc]XRI@;78BOY]ZSMKPYdmuǸxfVLKPY^\VNIKS_kt|~}xobULIJLLIEEL[n˾|urpme[ROUcszpjggfb\WW^kxxtv}|yxul^L=68AMWZWSQSY_dgjqķ|hWNNSY[XRMLRZbgkoy»xk^TOPQQLD??GXjzzyzxrfXNLSaoz~|xtstrlbXRT]kw~~zuuz||}raM?9IYhsz~ʽ|}~sdVNOXcmrttuy}}wk]SPVamuwvtv|}yzq_NC@CGIHHJPZcge_Z[ezƼ}lb^\YSMHIPYaca\[bqqb[Z]^YPE==DP[dinu»~}n`WTY_eghjoxtdWQS\ekmmnt|{vx{k\QLJIE@=@IUafe]VT[jþtnh_TJDEMX`b^XUZgy|lcbcc]SIBAFNUY[^ft¿vjb`acc`^`htyj^WW[`babfo{{wysha\WOE;57AO[a`YRPVbqĸwiXJCDLW^^YTRWbqxpmlh_TKGINSTRPR[k~ƽzrnnnke]X[esxlc^^_]ZVV]jz|zxsoleXH:23=JUYWQNOU^gnuȼt`OGGNVZXTPQWaksy~zulaVNMQVXUOJJSct´|xxzztj^WXanz~wojhgd^UNLSar~~{z{zseR@64:DLONLLPV]`acjyo\PNQTUSOMPXahklowtfYRRV\\WOHGMZiu|Ĺ~|~re[Y_isxxtqpqrneXMGKWft|~}}}z}tbN@:<@DEDDGNW^_\YYdxǻo`YWVSNIHMV`ghfdhtp`VSW]_[SKGJS_hnpt|ý|{nc^_fmppnmpvzzrcTJHO[fnrtx}y{q`QHDDC@<;>GS]a^XSVcxĻuke_WNFBEOZdge`_fto_XX\^\VNIJPX_cdflx~wlecfjkifehpz~teWONT[adgktyw{}pcYSPKD<54;HV_b]WSXdvƿxpfYK@>DP\dea^_gvte^]^\WPJINU[^][]fu|skhilmjd^^do{wj_YX[]\[\bn}yx|yofa_[SH;219FT^`]YX]hu|o^L@318EQY\[Z]dnv}ŻwcPB>CLTXXX[cny»|slbVJBAGPY\ZTQS^m~Ǿxngfkqsog]WXalvzxsonopmdZQOVeu|skhimok_O@66BFGGIOYgqwvrrzʶxfYRONLHEGO]lw|{yzycPC@ENTVSONR[fosv{»|nhiotuog_]`fkmjggks{~xl^SPT^gnruz~qjjouvqeXMHFGEB==BO^krrnko|ųuib]XOE>=EScpuust{t]NGIMQQNKMS\eijiluxppsvuohcbglnlfa`fpz{qdZVX]acdhp}xposwxrh^WSROI@748DTbjjgeitytneWH<9?M\gllloxm]USRQMHHLU_ffc_`jz}{|zunhfjptslc]]dox{wmd^]^_]ZX\gw~yy{|xqha__^YN@4/3?MX]]\^eq~¼~q_L>:?JU\`adlw|of`ZRIDELXbfd^YZcrvmhjpw{xod^^entuqjecdec\TOQ]nzphdehibUE702:DLOPQWamw~ʾlXIABHNQSU[eszqeWIAAIV`ec]XX_kwynhjrz~{sibbgmqpkgegkmi`TKKSctrifinpk_O@87:@CCCFNZhrwwy̺|iXNKKKJGHN[kzxeRD@EOZ``\XX^gouxztjiow||vmhgjopmgbafmrqi\PKMXeqy}ymhinrqh[MD@AA@<9:BO_kqqps~̷yj`ZVQJC?BM^o{~{dQECIQWYXVX]eloonq{slmrxxuokknrtph`]airvrh\SPT\cimqyxnkmqrmcXPLKJF?625@P_imllrϻzpjf`UI>9>K\kuyz{iUKHKOPONQXbkonigkwxrrttpkhhmtxvoe]\bksvqh_YY\`aaafq{spqroh_WTTUSK@504?O\ehhksözvtqi[K>8;GVdlpsysaUOMKGCDIUblpnhdhsyvtoha_cktyxoe^]bkrsoicacfeb\Y\gwywvslbYUVZ][RE936@LW^adjuúzy{yqbPB;=GR[aflv}nc[SJ@::BQ`knkfditźwlaYX_juyvne`agmpnjeehmnkbYTWcs}ui]UTX^a]SF<8IU\`adjrz|{oc`dkqsqopsxytj^TRWajnjc\Y\dlruwzxh^[\^\UNHFHKKG>76=M`rɱ~sha^^]YPF?AM_txcOB>BHNQTX`kw}xv}rifhjjhfhnw~~wj]TTZbijf`]^djmmklr}zmfca^WOHGJPSQI>65>L]ku|͹znebcfe^RF?AK\mz}hWKFFGFEGMZjx}wv}¸yqmkhb][_iv{nbZY^dgfb__djookeciu~uplg_UJDEKSXVNC;:@LXbjq{µykbaejibVJBCKWdnu}o`VQMGA;;BQdt~|xy̿{wqh]TRWds}zpgbbefe`\\`iptpib`er|zvobSGADLUYWOE@AGOV[^erwibchljbVLGIPX^bhqqhb\TI=43=M`pxzxy}³wiZOMTboxyuokjllhaYVYbmtuof``gr}sbQEBGOUVRKGFJOSSRT]nĶwjfhllf]TOOSWXXX]k~|uqmfYI:13>N^hnptzygWOPYdmqpooquupeZSS[fptqicbfnvz{zzr`PIHMQRNJGINSURLIMZn̹zqoomg^WSUX[YTPPYj}|{uiXE849DOX^bitžudYVZ`egghmt{}xl^TQWajnlgddipuvtqsz~m^URRQNIDDHPVWRICCM^rʴ}xqg]WUY^`]TLIN]qudRD=>CHLOS]k{qgbbcb_^`ht~}qbWSW]cecaaemsvtokmvwkd`\VMD>?GPWWQF?>FTcq{xj]XY^de_TJGKXgv}l\QKJJHECGSdvȽ}vspkc\X\ft~re\Y[^^\XX\eovvqjgkv~xtpi]NB<>GPVTLC==DMW^eo~̿oa[]chg_ULIMV`hovsg_[XSJA<>J[mz||´vi]VXbp|}ulecca\UPOVamuupifiswiWG>>ENRQKC?@EKNPRYh¸|kcbfig`WPNQW[^_cmypkif^RE;:BQanuvwyǺsdYX^jtzyupnnnjaULINZgprnifiq{xeSGCFKNMHCBDIMMIFHSg˽|ojjjg`XSRV[][XV\jwttsm`PB;=GS^fims}qc\^dlpqooquxul]PHIQ]gkjgfiqy}~{yzwdUMKLLID@@ELQQLD@DQf~пytpjbYSSX^a_XRQYi~}xyzxn`OC?BJQVY]eq¾rgcdgigffks{{o_RLMU]cdccfnw|}xsqvwh^XUQKC<;?IRVTLC?DRdw}th\SPT\bd_VPPXhy|~~yn_QHFHKMMMR]n¹umjjhd_[]er}seYTUY]^\[^fqz|uomr|xnhc_WL@87>ITYVOGEITany²{jZPMR[cd`XRSZfs~þym`UOMNNKFCGSfy²~wsrpkbYST^ly~vkb___^ZUTYdq|{tmkpz}vrpni^O@66=ISWUPKKQYbhnum[PNS[``\VTW_iqvzvi^VUUUQI@=AObtŵ{xyzwnaUOR]jvzxsmjjjf_VOOWdr{|xqmnt{~zutuxwpbP@88?IOQOMMRY`ccdjyȾo]SSW[\YUSW^flnnpw½rd\Z\]ZQF=:AP`nx}ö|z}zm^SPV`jpqpnpsuri\QKOYgrwuqoqv|~|vqpu{|q`OB=?DHIGGJQZac`[\ewþpb\[[YTPOS\eklhfit|lb^`caYMB<>GS^flr|Ǿ~{~wi]WX^dggghnu{{reVMLS^hmmmnrz|unmr{{l\PIGGFB?=BLW`b^WTYg{ĻumgbZQKIOZekje`aj{vjfgig_SHBCIPUX[am~¾}qgbbdec`^ait}xk]RPTZ_bbchq|unlr{~sg]WTPI@85:ER\^ZSOR]nôyn`SIGMXbgfa]^gtvqqpkbWOLMQTSQQVbtĿvpopokd]Z^ht|~wlaZXZ[ZXWZbo|}upqw~}tmjgc\PC95:DOVWSONR[fpyö}l\QOS\ceb^]`hr{~{vnd\WWZ\[VPNR^n}¾{z{|zsi`\_hryzungdcb_YSPS]jv~{vtv{zussspgZK@;=DLQQONOU\chkq{Ĺ|j^YZ^bb`]^cjrwz{~tia^_cdb[TPRZfr{}~zpfabhoturnlllkf^VPPWbnvxwuux}|xttwyyrfWKDCFJLLKKOV]accdkyĺ|ngddda^\^cjqtutu||pfbcfhgb[UUYbjquy}~xnhfimpqommostph^VRU\dkoqrty~}xtsw{}yqeYQMMMLJHIMU]bcb`bkyǽwrnjd_[\ahpttrqt|{oigikkga\Z\afjmosz~}volmnonljjmswwsja[Y[`ehiknt{ztrsw{|wof^YWURMIGIPX`ddbadlyú{vnf_\^dkqsrqqu}}rmkllie`^_bgijjkpyzurqrrpmigintxytmfa`acdddfksz}xtrtwzyunhda_\VOJHKRZaddccgnx}tjb_`ekprqqsw~yspolhda`bfijighmu|wtrsttqlhfhmswxuqljiihfcachpx}~{wtsuwwuqmjhgfb\TNKMSZ`cddfkqxyogccgknoopsxyurlgb`beilkihimu~~xtrsuvuqlhgjnsvvtqpoonkgb`bgnuyzxvvwxxvsolkklje^WRPRV[^`bdhnty}|sliijlmmmpu{|unhcbdhlmliijov}|wttuwwuqlijlprsrqqrttrmhcbdiosuvuvxyzxupmlmnnlg`ZVVWZ\]^`dkqvy{|~wronmlkjmqw}~vngeehkmlkjknsx|{vuvxxwtpnmnprrqppruwvrmheegknpqrtwz|{wsomnppokf`][[\[[Z\`fmruvwz{xurnkjkntz}~unjijlmlkklosvxz|{yyzzxurqprsrqooprvxxuplihijkllnrvz}|zvrqqrrqnieba`_]ZYY]binqrsvz}xsnkkmrwz{|}{upnnnmlklnqtvwwxz}}|{yvtttuvtrpnoruwwuqnlkkkjiikoty||zwuttttrnkigfec_[YY\afilmorw}}wqnmoruwxy{{wusqomlmpsuvvuvx}}zxvwxyxvsqpqsuwvtrpoonljhgimrwz{zxxxxwvspmlkkifb^[Z\`cfgikoty~}wsqqsttuvy}}zwtpnmnpsvwvuuw{{yxyz{zxvsrstuvutsrrrqolighkotvxxxyzzzxtqonnnmjfb_]^`acdehlquy|}yvuutsstw{}ytpnopsuvvuvwz}~{yyz{{zwuttuvvutsstttroliijmprtuwxz||zwtrppppmjfcaaabbbceimrvx{}|zxvtsstx|~ytqpqrtuuuvwy{}~{zz{{zxwvvwwwutsstuvuspmkklmopqsux{|{ywtrrrrpmjhfeedcbbceinqtvx{~{xussux{~{vsrrstttuvx{}~~~|{{{zxwvvwxxwutstuwwvtqonmnnnnoqtxz|{ywuttsrpnkjihhgedcdgjnqtux{}yvttvx{}~|xvttsssstvy{}~~~}|{zywvvvwxxwvttuvwwwusrqqponnnprvxzzywvuutsqnmkkkjigfefhjnprtvz~|yvvvxz{}~{ywvtsrrtvy{}~~~|zxvuuvwxxwvuuvwwwvuttssrpomnortwxxxxwwwusqonmmmlkigggikmoqsvy}}zyxxyyz{}}{ywusrrtvy{|}}~}{xvuvvwxwwvvvwwwwvuuuuusqonnoqtuvwwxxxxvtqpoonnmkjiiiklmnpruy}|{zyyyyz|}{yvtssuwy{|}~}zxwvwwwwwvvwxxxwwvvvwvutqpooprstuvwyyyxvtrqpppomlkjkklmmnprvy|~}|{zyyz}|ywutuvxyz{|~}zyxxxxwwwwwxyxxwvvwwwwusrqpqqrsstvxyzyxvtsrrqpomlllllllmnpsvy{}~}{yyy{~~|ywvvwxxyz{}}{zzyxxwwxxyzyxwwwwxxwvtsrrrrqrrsuwyzzyxwuutsqpnnmmmmlllmoqsvxz|~|{zz{}~~{zyxxxxxy{}}||zyxxxxyzzzyxxxxxxwvutsssrqqqsuwyzzyyxxwvtsqpooonnmllmnpqsuwy|~|{{{|}~~|{zyyxxyz|~~}|{yxxyzzzzyyyyyyxwvvuuttsrqqrtvwxyyyyyyxvtsqqppoonmmmnopqstwy|}||||}~~}|{zyyyz{}~}|zyyyzzzzzyyzzzyxwwvvvutsrqrstvwxxyzzzyxvussrqqponnnnoppqrtvy|~~}}||}~}{zyyy{|}~~|{zzzzzzzzzz{zzyxxwwwvutsrrrstuvwxyz{zzxwvutsrqppoooppppqrtvy{}~}||}~|{zzz{|}}~~|{zzzzyyyzz{{{zyyxxxxwvutssstttuvwxzzzzywwvutsrqppppppqqqstvxz|~~}}}}~~}{{z{{{|}~~}|{zzyyyyyz{{{zzyyyyxxwvutttttttuvxyzzzyxxwvutsrqqqqqqqqrstvxy{}~}}}~}|{{{{{||}~~}|{{zyyxxyyz{{{zzzzzyxwwvuuuttttuvwxyzyyyyxwvutsrrrrrrrrstuvwxz|~~~~~~}|||{{{|}~~}}|{zzyxxyzzz{{zzzzzzyxwwvvuutttuvwxxyyyyyyxwvutssssssssstuvwxy{}~~~~}}|||||}~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m27.bin b/applications/SprPoP/assets/packed/MUS/m27.bin new file mode 100644 index 0000000..7472064 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m27.bin @@ -0,0 +1 @@ +yl`Y\clty|||uhdeeedgkoqq{}{ytplnrigmnnmkkmplgpqnrvyz}{mmkgfddgltt{xrmlkjhbbib`gfghgghllgoqknpqnkjijmdahda``dffdahibgjjllnpszwz}wusstrniklccfddefhmuvy}~|wtqmlooimtporsqnljhjg`fgedgkpsrqtzsqvsrsrqtzzy~{xtpolbcda__bdhfeglgchhggijmuwv~}~tswvtswz~|}wyzxwvuty{w|{~}~z}zwtronqnjqwspttpligdg_Y^_]Z]afhfglpgflkmlmnrxvyz}~{z~x||{ywwvzyv~v|}{xy}~|~wrurqmkjikednrmlpnmhfeeh\[a`_]aejkikqrhkoopnpqvyu}}}}z~|}xxxx{vxvw|zxvy{~|zy~{orqpojjhihcirpmoonlghfje[bdcabgjmllovojppsqqstywx~y}}}{xyxzxvyt{zxvxy||zx{~rprpqkkjijegqrnoonojjikj_bgfdehjmnnovtkpqssqtuxxwz}}~}xzxzxu}{sz{yvxy{|zyztorqrmlljkegquppqopkkkmnadihgfjloooqwvlprtuquuxwv~~y}|~}xzxzwt~ysz{zvwz|{xz|spsqtmmlkleitwrrsqrlnmonafjkhfknqopszvlrruurutxuu}~y~{{xzx{tuvuz{{uy{~{y|~qssstnplnkenwxqstrqlpmqjbijmghmprnsw{qmsqvrstuyrx}~}{}{~yzxzyr{~ty{~yw|}y|zqvrtqpompghs{xqwusooqnpeeimnglnspovzznqrrurtrwvq}~~}{}|{|yzv{xr~|uy{zz}~x~ysvrtqqnnqgku}xryusprrooefionhmotooy{yorrqsrsqxtr~}}}|}{y{yyu|ws~{vx{{{}x{uwstssooshlv~yryvsrstpogghoqjootpoz|zrrrpsstpxvq}}||}|~}|wyzyt{ys}|wxz~|~~x}~wysrtupotklu}}ryxsstvpnighotmposrmy|{ustoqtupvxr{|{z~~wvyztv{sxzt|~|~z{~~z{{tqwnmu~v~}zy{zsvxoryzt{wuxzyrponoz}y}|~{}zrxsmt|yz}~vquwpnunlt|}rxysvz{somkjpwqqoppisxuronfflmfjpioy|~}~}x{}ywpjnpkhplkt~z}~xyw}yuy}zqomkjswqqnqmlwyxuupjmsqlttpx~~{vtrpv}vurrpkuxwtroghnngmqkq|~ywvtx}zxxyrz~|{rqwyrt{uw~}~y}y~~vz{y~ww{xpnmkmwtponphnwvurrjgnqlktopz~~|{yz~|~w{wry}wt}xw~~}{zvxv{|su|zt{usx}xnmljkvvpompjlxxwuumhntokurpz~}{{}y{ztz~yu~zx~}}zyuw~~uz|rt{ys{trx}xnmmkkwwqporjnzxxwwnipuomwrq{~~}}|}~x}yt{xvyy}}|zy~tx~u|{ru}vszrry~vmmnlnzvrpprjr|yyxwljrvnoxps~~~}}~{~wuv~~uzw|}}{|z|yt|yv~vryrxxpu|}rmnnkt|tsprojy{zzztjnvuluvpx~~}|}}{~{y}ty|u|w}|}zwvv~wy~tt{|qzvqw~|pnonmy{tsptnm|{zzzrjpxsmxuqz}}~|}~~}|~}{~zz{szzuzw|||yvuww|su|{q{tqx{pnpon{|tsquno}|{{{qjqxsmxtpz|}~{}}}}{}}z}zy|ry|t|v||}z~wvvyzut{~qzwpw~qnpqnz~utqupm|}{||tjoxumwvpx}|~|}~~}|z|}y}}vsv~s|~u{}z|zzyy{t}~xzsysuzpt}vnpsouxustulx|}~zllvynrzpt{}}z||}~}}z{~{}~}w}wr{wuwvz{|xuuxx~tv}{q|upy~porrp~vuswpo~}~~tjqzvmyvpy{{~{~|}}|zy{~y}~ust~tyuy~z{{x|v~t{w~txws}qr|zoptrs}vttwnt~~pks{ro{sq|z{}yz}~zy{y}~z~|zw|pv|r}{s|{z|y|y}|y}yv}~t{ytwzu}zx{yq|oq|}px{qw|y|{y|zx}x~yzuy|tvzv{zxy{q|qpz~ru}suy{|xwx|x||wvwtuywyzyy|rysnxts}tty{|xvx~|y{}vwvuuzyy{yx|txtnwur}tr~yz|wvy}}y{uxuuuzyx{yx}uwunvwq}ur}yy|wvy|}z{uyu~vuzzx{yx}uvunvwr}ur}yy|wvy}}y{uxuuuzyx{yx}twtnwvr}tr~yy|wvy~}y{}vwuuuzyy{yy}txsoxus}tsxy{vuy}y||vvvuv|y|zyy}s|qoysu}rt~xzzw|vy}x}xytx|tw|xzxz{s}oq|~qx{pv{x{yxyy}y~xv|~szytx|w}zx|zszns~|q{ypyzx{x|w|{{{yu~{s|xuy|x|zx|xtxnuzq|wpzyx{w}v}z|~{yuzs}wuz{x|zx}xvwnuzq}vp{yx{v~u}z|~zztzs}wv{|y|zx}wwvnvyr}uq|xxzvuy}}zztyt}wu{|z|zx}wwvnvyr}vp|xw{v~u}z|~{ytzs}wuz|y|zx}xvwnu{q}wpzyw{v|v|z|{yu~|s|xuz}y}zx|yuynt|q|xpyzwzw{wz{{}yu}}s{zuy~y~zx|{t{os~~rzzpv{wyxwyv~z~x~xysx}ux~z{y{}tpq{sw|qt~~wxyu}ty|x|{vvvvw}{}{yy}v{soywt~sq}wwzuty~}zzuys}wv{}z|zy}yvxov|r}wpyywzwzwy|{~xv{sy{uy~y{y{|t~pr|sx|pt~}vxyu|sy|y||vvvvw}|~|yy~w{toxxt~sp}}xvzu~t|~z}~{zu{s}xv{~z}zy}zvxou}r}woyyvyvyvx|{}yv|~rz{vzz{y||u|os~szzpu~{vxxvyu~z}x}yytx~vx~{{y{~uqq{uw}qs}~wwyu}sy|x||vvuvw~|~{yz~w|spzwv~rq|wvyt~r~y|y{}vwuww}}~|zy~x{tpyyu~sp~|wvyts}~y~|yzuyt~ww}}}|zy~yyuoxzt~tp|}wvyt~s|~z~}zzuzt~xw|}||zy~yyuoxzt~tp}}wvyts}~y~|yzvyu~ww}}}|zy~xztpyyu~sp~|wvyts~y|y{~vxuwx}}~|zzx|spzxv~rq|vvxt}sy|y||wwvwx~||yzxrq{vx}qr|~vvwtzsz|x}yzux~vy{{y{~w}ps~tz{ou~{uwvvwv|{~~xw}tz{vz{{y}}wzous|xoxyuxuyuy~{|~{yv}t|yw{~|~zy~{xxov}t}voy~xuxt{ty~z}}{zw}u}yw|~|~zy~{zwow}t}uoz}wuws{s{~z~|zzw|u}yw|~}}zy~{{vpx|u}to||wuws|s|~z~|zzw{u}xw}~}}zy~{{vpx|t}uo{}wuws{tz~z~}{zw|u}yw|~|~zy~|zwow}t}voz}xuwtzty~{}~{yw~u|zw{~|~{y}|yxov~t|wox~ytvtxux~{|~~|yxu{{w{|{y}~y{ouu{you~{uvuuxu}{|x~z|vy~wz||y{y~qr}vy|pr|~uuvs{sz{y|}yxwxy~}}zzzsqzyv}rp~|vtvs~~s}z|zzx|v}yx|~}~zy~|{wow~u|voy~ytvtxvw||}}yy~vz}wz||y|y}ps~vz{os|}uuuszszzy|}zyxxy~}}zz{tpz{v}sp}|wtvs{s{~z~|{zx~v}zx|~}{y~}{xovu|vnx~ytvswuw~||}}yyv{|x{|{y}~z{ptvzyou~|uutuxu~{{y~{|xyxz||y{zqr|xx{pr|uuus{szzy|~zzxxy~}}zz{rq{zw|qp|vtur~|s~zzy|z{xyy~~}zz{spz{w}rp~|vtur}}s|~z{y{z|wyy}~~zz|~upy|v}so||wtur{~s{~z~{z{z}w~zy}~~~zy|~upy|v|so||wtur|}s|~z{y{z|xyy}~~zz|tpz{w|rp~|vtur}|s}~zzy|z|xyy~}~zz{sqzzw|rp|vtur~|s~zzy|{{yyz~}}y{{rr|yxsh^fqtjjou}vnjh`hmilkf_hyvrmkvurrplaaliilnhgoumnyss{}sqldnnkljb^k|rgd]`kgegebW\gehkkehqtikrjiouqgnkafllb^abalmb^]bccsuuwulgq~zzzvvtpyvib_caapqqsqgdp~~{}pu{vwwtmouuilpggntngojdjqphgklmzyollrqs}qlv}x~ws|xw}ypwpimroeceeeqoe``fegvwxyvkhs}y{y|y~zy|}|~yx}||vow~v|uoz~xssqv~zos{wxzxpqwymnrihntoeliafllcadfeqtjeejllz}}~{pjsx{u~{qvwzvms~{yumlkkxyvywtigrpprrkioskfmhchppfhlcdkpidfjipzrkilqnywlp{y|~tyur{zwzom{~v|ymiifowsuuslclpmopmgjqpfjlefnsmgojejqphgkmmzzqmlrrt~smvx~ys~rt~wxwlo|zu}sjhgfrurutricnomoplgmrnfmkeiqtljrjhntqjkoorzropvty}qoy}zvtyqw~uyrjt{vw}nhheiutsusoegqnnppjjpsjioignutkpqimsvolnqpzxrptwuxnty}{szrs|xwwlm}|~zt|ujggepurutrjdnpnprnintqhoniltxpnvnlrxtnosrv}ursyv}}qq{|{~tuuqzzvxnly~}|tzxlhhenwsuvtmenroqspknusjnpjltysovqmrxwoossuwssyw{spy~z~vs~vqy{vyokx}}uzzmiigowtvwunfnsprtqloutkoqkmuztpwrnsywppttvwssyx|~rpz}z}us~~}tr{{wynm{~}v|ymjihqxuwxungqtqstqmqwtkqqlpw{tryqpuzwqqutywsuzx|pq|{{|ru}}}}ru~xyvlq}zxtljikwxvyxtjkussuupouxpmtomszzrvxpsx{tqsuu{uswyyvnuy|wqz||{xqxwzslw~x{rlkioywxzxsiowstvuoqwwnptnov|ysyvquzzsruvvzusyy{tow~y|uq}{|zx}|rpv{xt}|y~z|xqnjowtuwvqku}}z}~wuvuvwqpuux|pmv{yv|sqlqyuvxvrkt~~}{tvzyqoqqr}~upotuvzoipywpslep~xyru~{mhhflxwy||xpw~{|u}{{qjhgkvvwzzwou~~~~|xuxzx|qhmvxprnfm{}xwvonmp|~}}uu~}|~}|pqz~vutil{z{ws{ukiiitzy}}|ur}us|}y{qp~}wssrz{t}{~y}z|~{{z{{|}wuzz{{ohoxvoqjeoy{twrlkjo{|}|swwz}txxutuyv}zx}|w{~|zx|}wwxw{xsswv{~vjhpwrnphft}{|u{~rmmlt~}{s{~~x~{u~~wvuxvy{{zx}xw|{~~wy}yuvwv~{tqtwv{qgluwpqnfm~|~yxxpnnp|~~vv~~yzvyzwvuzv}y}~w{zvy}zyv{{uuwvzwrrwu{~vjirxspqiix~}x~soooxzu~}}~{xxv|wvu}|u}~y|wz{vx~zzvz}vuwvxxsrwvzxliqxuprkivy~uppov{u|}}~{x~xv|wvu||v}~y|wz{vx~zzvz}vuwwyxsswv{xljryuqrkjxzuqpox{u~|}}zx~ww|wvu~|v}y}w{zvy}{yw{|vvww{wstxw~~vkltysrrjm|~{}tqpp{xv~}|}wz~~|tzywuwxy{{{x|xw}{~~wy~zvwxx|vsvxy|qjoxxrsois|}xrqot}uz|~~~}~|v|}~xt~xvuzw|z|zz}wx~{|wzywxxy{utxx{zpkrywrsmjv|wrqpv|u||}~|~zv}|~wu}xvu{~w}~z}y{|wy{|x{ywxxz{utxx|zokrywrsmkw|wrqpv|u||}~|~~{u||}wt~xvuzw|{|{z}xy|}x{zwxyy|vtxy{|rkqyxrtojt}ysqpt~vz|~~}}}~}uy||yszyvuwyy||z|z|}||}}}y{~~{qs{~ywvosxtss}zw~{z~}z~~|||~}~zz~~~sry~zwxqryutsz|w~{~{}{}|}|~~{z~~vqv}|wxspz|vuswxz}}}{}|}~|}~|y|{rt{}xwvquxvuu}{x|{~~|~}|}~~zz~urx}zvxss~zvvty~x|~|~||}}}}|~{z}xrv|{wxurz|wvuwyz~}}}{~}}~~|~|y|zru{|wxvrx~xwuv~zy~~~}~{}~}|~}z{}ssz|xwwsvywvv||x}~~}{~}}|}~zz~~usy|ywxtuzwwv{}x}~~}{~~}|}~zz~vsx|ywxtu~{xwvz}y|~}}|~~}|}{z~vsx|zwxuu}{xwvz~y|~}~|}}|}{z}wsx|zwxuu}{xxvz}y|}}~|~}|~{z}vsx|ywxuv~{yxw{}y|}}~|~}|~~zz~vtx{ywxuv{yxw{}y|}}~|}|~z{~~uty{xwxuwzyxx||y||}~|~~}}}z{}{tv{|xxwtxzxwx~zz~|~}vvy{}u~{wvw}y~y}{tnotx}}pvu|yzsw{}{wxyzzz}y}ys}w}~|ztzyszsqr|z~}}xussvx~wx~wz{wxxyxz~zvsuuyzulyrz}ty|~~}xuv{|~~{y|yz{~||trr|yzw{~{t~}kvyzzywvvvytv}}{~v|tvwy}u{x}wkz}x~}y|yw|xtttvvnzzq|{tvsu|}uloux~xvtgq||{zzxnqyz|wivso|upspvuryxttjn~rgorfhlvxhkonvtm~qr}qoroxnlttw{quvfnpilgiu}~}tz|stmdyylo|srsowqmrprvksyq}|qnehy|}wuy{w~{r~klwnnomyonusvxipngwytwru{|wmdr}|zrznvvqkhtopwstwjsxq|xlmjqwsgkmhnnpxikxrrsr{}sqwsrxnqskz}uxqoz|iinkqmbswwsnkgr}omvut{povityqrnpxsv{tsmarwu}{zpr}tjpqnvonvju}tqkky{jnwtyzo{}vks|yqx~zuq{zruiivmx|sslm||tvxnpqi{yggtnott}pirsr{stxnx{uvnm|y~ot{v{ylwzfgwqmibj}}wzsnsimrkz~xxml}z}lpvpvumqvsnhcoy~{w~z{pqmgwwstlp}znuyquoj~tiuzzsmu~{u}}qx||}q|tsxqnfmyqwwpwonznwyqsmnrzikpjt{uxhn~z|wms~}xqyvt{nptp}|miy~tgovlqmfxqv{utnn}u~qs|vz{myjfsqw}||yywppq{ttwvy{mjz|{qv~nr}myugr|tpirt{tjvssugo~}wsz~wzvo|{gyqg{~wyprmr|uqrkvrhkmkqu|to}|kot}x{{}mpvxqm|~~fnrolpmnpfh}jzsixuq~y{tm|~|~~}qt|zxq~x|~}qsmZlyzpv~qoumpvc\wsr|y~w|ynnuvzt{wxmatvqo~~s~zqo{lntbccftrkdjrrryqfjus{zpdlnz}xw~{nffnyvstmg`lruokzmhyyvn^`nol~||or||mtqt~rz}qz}rvwjgs|xoo`]skkwflyyz{xunqjr|~~}zs}~{|u}xyx~rqs_`q{|{~}skszxudeeitqlmqrx{zzytzq{oh|{{vxzpo{ztj^ox~usmnkptnymi~zs}|x~uoutnl{{~x~~xqyzwst~~{xtbmomvpjtwu|lZjumvxw~}yuzqy|xw~nu~{wz}~wytil|kyvihegsxnmz|~z~pimsv{squvpn~uywxpkqw{~ru|wukl}yi_rwvwccqv{qjz~w{z}vw}|t~wrqzu~onrnlz|vuq{hjzpwtzgcjv}y|ycq~v~{}nyo|sv}yru~nouzwoimqrty}}xcgqmuw{gkrsp}vw}uu{}{~nvxws{|rkslktswqxtm|bhedux}xzqmmux{clsort|yuzx{rs}q}shuwmdo~jwkago}ykvvkedpyiuxuwksvfdpsnme|r{x{y~zhyh~|phdlt~{wnX`u~`atjojjx~zez{yt~uw{gxrmwp~xybkqtwjyw}yjh^_}~zgv{pqjq~nWwwzxsxutzt~nyppk}gfy~lu{v|q^`fv}zmziicglwqqt{mWzzvukx~vmoqvtcsxuxhgu|v}v~zqcixib~ujoij}zeuzw{}q~or{zfwr}ptfktvxsp}wok_cxwu{plhm}y]Yxxu}|}~~n}u}xrn}p~}owgr|vispcepu{u_spfc]l}s|ytmw~r_nvv{ttv~pzu{oj}s{oumf\mtqm}l`o}~uYoysxuh}wik~xpwzkw}^nsiwjztioi`w»}w}oMV_XWXLayzwnsQ^vigmVNdfgkjjwp|r|tmVH^b`_z|j{oQ^cZ\`U[mlbdrUk|cb{woridwupfmoVmxhHQgb`cXTok_a{hazlzz{~thormqcaupljbXtr^Vjpsxrg{xoppfgh_dupzu{pooUbjebbZ^noqulxo_pqoqlh~w~vqsxqu}zk|~trmZengjmtZn|s_^lkh^WXkorrrstvvmpt}}wt}xxqllwyzxomw~}virxvt~xe[noipvbjyxsx~`pzmTZh^^d_[oqsp{s^y~t_izxxylo~|~tho~ww|tq{y~qyly}|jv{ss}phrommfT_nmmp|k|uhgqjff_Xefmijqdywdgwqfihbwz~}sr~{|zwnpzxsslm}z}w}hswnmm_e{{tqjgunb^jgfro]lxvtqydqxl[_icenkbu|}|whzmv~yxvpx~up{xz}uuz{|vsthv|xytiu{|xlpzrjnmjg_Udnnln|qz|wjhqjgkdXhqtlwxlzoy{kirkl~~~{w|p~}y~ugwynqthq|~s}qovnjlgcxztpxubqrk^cjiruedyzys}rk{rkgljipsisvzp{uq}yswzt}wtv~yx~ynzz~|pxpyxttqs~{|t{qmuelohfeYbqpqsuv|rntmjng[mwupv|ruxvimwpm~|{{v|vr}~qr}rltrivr}ppskjpkl|uvu{pevskdlpstpgp~zys}oowtognmmuul{yutvx|sr}zq}z}y~}x|}pr|v~tz}wwypw{pz|wx{mykotifiaaqruvzpxnrpkmk_kytuuv~|wrjoxtn}|}|}w|}rr}}vvxpmuvjvx~urtlltspxv{uuqgvunhoyupsmr~{{u}olzyqjrpowxq~~stvwuxquxm~~|zz|~xz|qn~uyx~}xq{ry|yu}txoqujfkhbpuz~|pzrsskjkflxuxu{zqnoqwypx}z~{{}vpx|zvsnrxqr|zqsqms|soy{{rwmnytpp}ynsvpy}|yuxiuwnotrs{yv}ut~w|vwvt|}pr|~twyw}xws}x|{vxynu{x|}}ztt}w~|}{z|~}}}rv{uqnznlurqqzxzukuvwsx{zvsz|tvzqoww~{}rrwuogjjmuxx{~zo|~{qrqjwvst~zos{vu~}vxpsxrpuspvztxslqryouzsljmnpplhw}}|otxrprlhljfuvrsxw|tuzst~zvwpgltmoxxzxz}mjtspuvnuzxx}rx|yz|{w|ury{y}ww|yx~xigmjpwwvsxns}zqntst{~{}{~|z}zzxz~{|wuzztttq|{z}yy{|wtxywxv}|x{qnquolqtnt}xrju|vssuomqqqywxy}{|zvy|u{zy|u|~~}s~|z|yz{zzwnoxx{y{|}{vswsfkuyvtyqz}zz}|kfqsnx{{wwxruquyuxx{~{x}~ssxuxyuxzw~~uxysqswv}uq}{}}suqpstrnqwrttqrw~zwvwrosxutwz}}~{}{z|z{}{}u||zu|{~{yxz}wutlovy{|y~}~{zvtunfqzyvvyu|}~shnyur{||vyxxystzzv~yu}z}vpw{v{~}vu||~}yrxvrty{}|xsy~{~upttvturpxxt}uoyx|}ywwvqq{yovz|~|{~}}~|{~}vyw{x~~~z~|}{}x}urtonw{~}wz{ytrojt{yyv{x|zplszwsz|{wxz{~vtw|zyuw|}||sry}v~}}uv~~|~{xrwvux|}wyx|}|sowxvuxssxzx{rs}y{~zwvvrt}vmv}||~||~|~}|{{|xwvz||~{z~{{urtrnv}}x{}|soppsyz{w{{zvpquxzuw{zzw|}xwvy||}}~xv{}}{vsv}z{}~zw|x}{yvsxwy|vx|}}zvps{xvxxvvz~|}st{}x||xvwwtzyqpz}~~~}{}}~w|zy{yxzw{~{~~~{{~~uqttpx~|z}|~}rmqutx|{y|||~rqwwwzwuyzzx}}swx{{}~}~}}zxz{z~xvuw{|~~}{z|w||xwsvy{~ww~{~ztrr{zxyxzwyztw|}{y}zvwxwywsqv}}}{{}zw}wx|x|yxx~||~xvyyy}tsz}y~yszz{}zwz}||xsy|}{~}}xy}|}~~uv|~}{yqw|{{zyx}ztvwyz~{~{{x{|~~{wx{~~|~{}wy~~|~zxy{}xsuu{}~}~~}yyz~~{}xv{|z{}~|xtz~}}}xtxyz|yxy|~||vwy{}~vrx~z{ysz|z||ww|}~xsw}}||~zw{|~ut|}{wpy{}{xzx}wtw{zy}~~zzx{~yvz}}~~|}wt|~~{|yyy|urvy{}}|~||yx|}}ywy~~}{z~}{tx~~}|{wvy|}vvy|}|}}~}zvw|~yuv}}{|~{tx{z|zuy}xut{~|}~}|xx{}wsy~~}~ypw}{~|xzz{xtv||y}~{zy{zv{~{~~|vr}}}{yyzy}trv|z|||~{{zy}~zvx|~}{z~vu}~~~|{zxwx~|svz|~}}}|zvw~zwwz|}}{~uv}~|zzxwy}|tuw}|~}|{xw|~}xvv||~~}tt}}{|}zyz{~ztuy~{}~}~}{y{{xy~}||~|ru}|zwzz{zrsy}z{~}yzy{wuy~}}{|~ux}}~|zxzxyxru|||}{{wyxvz|{}~z~ztx~~}yyxxx|{tuy||~~{zzwz~}wvx|}|~~|uw|~}|z|zzz{~ytuy}}~~~}}~|yz~yyz~~|~}~|tu}~|zwx{{{usv}|~}~{yyz~yuw}~}~|}~ww~|}|{xx{x}|tsw~~}~~|zzy|{vw}}{~}{ut|}~}ywxzy}wuv|{}~~zzyx{{uvz}|}xtz~|}|zyz{{|~vtv{}}~~~|||z|}wx{}}}|~zsw~~|zwxz{yuuw||}~{yxz}zvv|~}~}~~}ww}}||{xw{{||utu}~~|zyz|~|wvz||~|~~xry~}}|wvyz{zvvz}{}yyyzwtx}}}~|sw}|}|yw{|{zsux}{~~}{{{{yuy}~|}~su}~}~{xwyz~|tuw{{~~|zxy~~{vvz~}~}}}vv|}|{{xwy{~|utv{}}{yy}|vvz~|}|~xsx}}||yvyz|~xvw{}{~~yyy|~wty~}}~~xsx}||{ywz|}wsuz}|~}zz{}}wuz~~~{~yrx~}~}zwyy{xtv{|{~~{zxzxvy}}}~|ysz~}|zywyz}wtuz||~}{zy||wuy~~~}{~ysw~}}|{zwxz|wuw{}|~~{xx|}xvx~}~}}xsx|{{ywy|~xtuz}|~~}{yz~|wvz~~{~~xry}}}}yxzy}~vtv|||~~{zy}}ww{}}~}|ut|~}|ywwzz~~ttw||~~~|zyz~xuw}~~{}tv|~}}|zxxy{{tuz}|~~|{xzxwx|~~~|~{sv}~~|zywxz}zutx}{~|zy{~xvx}|}~{tv}~}|{{xyz{~ytuy~|~}zy{~ywy~}}~|~zsw~}}{xwxz|ztty}|~}{zy{~vux~~~|}|tw}}||{ywzy{ytu{~|~~{{yz~xuy}}~~|~zsv}~}}zywxz|{uux|{~|zyz~~xvx}~|~|tv|~}}{zyyz{~zttx|}~~}|y{ywx}~}~|}~tu|}|zvwy{|vtv}|~|zyy~zuv{~~~~}~wu{~||{zxwzz}~vuv}~}~}{zz|{vv{~}}|wsx~}}}zxwy{}yuvz||~}|zy|~vvy~~|~{sw}}}|{yxzz|yttx}|~}|zzyvy}~~||ut{~~~|zwvyz~}vuv{}}~~{yy||wuz~~}}~xux~}||zxwy{|~xtuz~~~|zy|~wvx~}|~|sv|~}||ywxz|{vux|{~|{yzyux|~~}~ut|~|}{zwx{{|utv||~}{{z~zvw}~||~vty~}~}zxwyz}~wtw{}}~~|yy||wvy~}~|wuz~~|{zwwy{~~xtu{}~}{zy}|vvz~~~|~ysw}}}|{yvyz}xvvz}|~{zy|}wuy~~~}ytw~~|}zywy|}ytuy}}~|zz|}wvy~{~~ysw}~}}|xwxz|~ytuz|||zx{~wvy}~~~|~ysx~}}|zxvyz}xuuz}}xla`fjkqw{|xth_\]^^clpqt}zsporuxtlilpljkkfcbgmnstu|}ylc_`_`empou~yqkebefilfbbgjhjjidadimlmlkmloqrl`]cedc`_[[]]a`ZZ]egjsyzx{}ztnjgfegbZY\acgpvuuy~~zxwqifhmmllijkkoqrh_bhjiiiffhjmpmiinstx~}~|~~{{{y{{xmdejihfc`]_`cf`\]aghov{yx|{tv|}||zxvwyz}wstw|||yxy}}z|}zzysmjlqpomjllmoonc\_efddb_^`cfieabhlot||{~}zy}|y}{yusqqvy}~vtswvrrqmfcfjkkighijlnnd\^egfgedcdhjokfgjpqu}~|~}{}}~|wz}{wtrnnrtxzuppstqppnfcdgkjihgjjmppka_fjjjihefikppjikqsv|~~}}~}wwz{vtqnmnsvzyrpqtsppojdbfkkkjhjkmprqgacjlklkihjmpspkkotuy~~|}}}||vx|zvsqomptvzvqpqurqqojddilmlkikmortqgcgnnonlkjloruolnrww}}|~~~|~xvy{xtromnquyztpqttrqqmgdflnnnlloosuvoeflopponlmprvupnqvwz~{{~||~|vw{zvtqonpuw{xrqsvtrsqkgfkoopmlnoruwtifjpqqqommoruwqnpuxz~{{~~}ww|{wtrpnpux|ztrtxuttrmggkoqponoqsvxulgjpqrqpompruxspptyy}{z}~|xx||yvsqpquy|{trtwvuutniilqrqqnpqtwxwmhlqrrrqnnprvwsopuxz~{z~~~yy}}xvsqprwz~{utvywuvtniinrrsqprsvxzvlimrrsrpooqtwxrpqvyz}z{}x{~|yvtrrvy}yutxyvwvsmilqtssqqstwzyrjjqsstrqnpsuxupprxy}~|}~yvy~~|~{tv{}||zxvwz|{vux|}}{z{}x{zwutvxxqmsz~zvuwy}{usu{{||~|tuyywvtqnpruwplkotty}~}{~}yxz{~|usuz||}~woossrqnljknqsojknstx~~{y{|yyx{}}utwyvtronpty~zxz~zz}}|xtqoptw}}xvx}zz~}}}zwxy{}xrpswwy~~{xy~}zzz|ywz}{zwuuu{}~~ywzyy~||zyz~~}{}xuwzyvtqopty~}yz~{y{~z{~|zy}{vw{}|~wsw{||zxutvx{{urquxx}{y{~{xxy{~{uuz{xvtrqtz}}|}}yx|~~~|}~xuw|~~~{{}}{qpuyxwvtqrsvywqnostvz~|ww{~yyyy}zux||zxvttx}~||xy}{~~}~|wux|~~|yz|}vopuxwvtrppsuxtnmotuw|~{wx}|zzy|yx{~|zyvuvz~~zxz||}}}~xuvz}}}zy{|~{qnrvwvtspprtwwqmnstvz}}xx{|{{{}xy~~|{xwvy~~|xy}~z}~}}~zuuy}}}{yz{}}soqvwwvsqpqtvwrnnruvz~~zyz|{|{yz~~{ywvy}~}xx|y|~}}|}{utx|}}{yz{}}topuvwvsqprtvxsonquvz~~{y{}||}zz~~|zxwy~~|xx|}y{}}||~zuuy}~}{yz{}}sorvwwvtqpstwxsoosvw{~{z|}|}}z{~|zxwz~~|wx}|x|~||||~}xuv{~~}{z{|zqpswwwvsqqtvywroquwy}}zz~}}}}z|}{ywx|~ywy~xy~}{}}{}~~}wux}~|zz|}wpqvxxwusqruwyupprvw{}z|}}~|{~}{xxy}~}xwz~}xz~|{|{{~~}|vux}~}z{|~vprvxxwusqsvyzuqptwx||z}}~~|{}zywz~~|ww{~|wy~|{}{z}~}~|wux}~}z{|~wqrwyxxvsrsvyzvqpswx|}{}}~~}{~~|yxy~~}xvz~vprx}~zz}~|zxvtsu{x~}yxvux|~|w{~wz}}|}}|ywvy||}||z}z{|{zwvwz}}yx{vz}~|}~}~~}|}zxy|~}{{xvvtsrrv}|st}{vrstv}}|~}~|{yvtru||x{xwuw{~{x~|wz}||}}{xvv{{}}|{{~~y||{yxwy{}|xx{vy|~|{}~}~~~|~{z{}~|{}{xwvusrsw}|ss||wsstv}~}~}{yvtsu|}w~|ywvw{~~~~zw~}wy|||}~{xvw|{}}|{||y}}{zyy{|}{wy{vw{}{{~~~~~}|~||}~}z{~}~ywwvtsruz~zru}|xttuy~|}|{yustyzy{ywvy|~}}}|xy{wy{|}~~zwwzz}|{~~}zy~~}{zz{|}|ww}wux{{z|~~}{~}}~}zz|~~zwwwussty~|trz~zutux~~{}}|zvttx|x}yxwy{}}||{xx|wxz{|~{ww{z~}|~~}|yy~~}{z{|}}{vw}xuwzzy|~~}|~~~}yz{}~|xwwwvssvz|ss{~{vuuy|{~|}|zvtv{zx}zxwz|}|{{zwzzwxy{}~~zxy~}z~}}}|zy|~~|{{|}~}yuy}vuxyxy~~~~}{~}zx}}{}}ywwxwusuy}~wqw~}yvuw}}z~|}}{xuvz}w||ywy{||zzzxx~~xvwy|}}yy|z}~|~}{zyy}~~|{|~~}{vv|zuuxxw{~}|||xx|{|{wvwxwutw{~}uqx~}ywvy|z~}|}}{xvx{|w}|zxz||{yyxwy~}wvvy{}}zz}z}}|zyyz}}~|{}~}zuu|{uuwwwz~~}||}xw}{|{xvwxwvtw{~~vqv~~zxwx|y~~}{|}|yww{~xz~{xz|||yxxxx|yvuwz||z||z~~}zyyy}}||~~{|~|wsywtvwwx}~~|||~~zvz{{{yvvwxwvvy|{srz}zxw{zy~|{|}{ywy||w|~{x{||{wwxxy}~xvuwz||{~{z~|yyyz~}|{~~~{}~{vsy~wtvwvx}~~|||~~zvz|{|yvuwwwvvy||tqy}zywz{y~|{||{zwy|}w{{x{||{wwxxy}}wvuvy||{}|{~|yyzz~}||~~~{~}{usz}vtwwvx}~}|}}}yv{{|{xuvwwxvwz}{ss{|zyw|~yz~~{z|{{yxz}{x~zy|||yvwxxz~zvuuwz~~{}z}~zxyz|}|{}~|}~|xsv}ytvwvvz||}~~{vx|{|yvuvwxwwy|}ury~~zzwzzy~|z|{{zxz|}w}{x{||yvwxxz}{vutwy}|}z~}~zxyz|}|{|}~|}~|xrv}ytvwvvz~||}~~~{uy{{|yuuvvxwwy||trz}zzw{yy~~{z|{|zyz}{x~yy|||xvwxy{~yvuuwz~~|}~z~|xxz{~}|{|}|}{ury}wtwwvw|~||}}~}xu}~z|zwtvvwxwy{~~xqv}~{zxx{x~~|z{{{{yz|~w{|x{||zvvxyz||vvtvx|~}}~{|}zxyz|}|{{|}}||yrt|{uuwvvy~||}~~~|vw|{|yvtvvxwwy{~vqx~~{{xzzx~~|z{{{{yz|}w|{x|||yvvxyz}{vutvx|~}}z}}~zxyz|}|{{|}}||yrt|{uuwvvx~~||}~~~|vv~}z|zvtvvwxwy{~~xqw~~{{xy|w~~|z{{{|yz|xz}x{|}zwuxyz|~}wvtuwz~}~}z~~{xxz{~|{z{||}{urx~xtwwvv{~}{}~~}zty{{{xutvvxwxz|}trz}{{w{yx~~|y{{|{y{|~w||x|}|yvvxyz||vvtuw{~~}~|{~{xyz|~|{z{|||{try~wuwwvw{~}{}~~}zty{{{xutvvxxxy|}uqy~~{{x{zw~|y{{{|z{|~w{|x|}}yvuxy{||vvtuw{~~}~|{}{xy{|~|{z{|||zsrz}wuwwvw|~|{}~}}yt{{|{xttvvyxxz}|rs{}{{w}ofb^\^elw|y|~zqf_^]\]djrz|xwtuttpjjhggkqusuyxuuqlt~~qottpljflsy~{tsoha\RS\dea]]```bhlssrsuxvkgotsmjefklkgdccbcce`__`bdiprrz}{vnglqsokikkklrw}~~yyzzz{xvxwspiafnttolnlhcbdeijlmoqmehrxxwuqw}~zzy{yvuuvtkirxzxvpprqkfcegfghieefjosx|zy~zz}|}}|w{||zzsr|}~xs{~yuvxyywumihhjmqusptwvuunjpvvnfceeb]_`eikotyyphntvuwutxxvrqu{z}~||}y~|tpppollllnnpsxyrimvyxzzx||ztnjjlpsuqkkjklqwxqopnkjhemuyvpmprsqqrswwy}}xxy{|xsqqrx|upsttsspuzvtponpmjhfginpjeijhed`cjnkfbbehlnohddglq|xw~~|vrpsvtllprqrrow~~ywxwusttnjeehmuzxy|woid[]gnng`^]]\]cdghjntz|xz}|}yz~~zxx|xvqmuzzxsmlotxvtuy|zrw}wqklnnlheedfimpmlgeeiptsw}|zsu||{{{{|{~}ut~z|~}}zvv{}xz~zrjnuzyvrqnkfehhkhdachibcowxvvrswvtstwutwxyuuwyzz}}|rov|zxxx}{~~}zzyqpy~}tquvrnnrsrqmkfdefffjqsqvy{yvmkrwvplmpmheilrx{xpt{yzzyvuwzz{~}z{{|uvz}wqorrohggjprvwvtninv{}y{}yphgmruttusutux{~{rrutstuqu|~{y|~{uswy|{}|ytruy|utssuwxtnqvvuwxw||tstwurooqooqvzwkglommnmsvvqmnrvzzvrmnnqv|x||xssuz|shkrvuvvx{wutw{}ujiilqxzwrmgcjtxtmgiklkihhnsvy~~|{vvz~~|wtuz~|xrr}|yrmlrvvuvy{}x}{xyrotwvoifgkqu{wqmlnosz~~}upz~{~~|wy{|zv}}||~yz~~yy||zux|zz{}vrsx{{wz}{~||wy~ysytkihfelt|xy{{{xssu{|~~|zxwxrqnihkt|||}xuy}~~}rv|~{tnquvvwuvzzz|~|ullu~}~yrprqrttustrlinwztojjsvwvx{||~{qijnttv~tz{z}{{{z|wpsw}|yxwqnmrvw{~wrpsy~{z~zpdfpyxvvz{vstuttuzyss|}|xrqu|~~~}uouwvvxwvyxtrruzyvrqokjmt}~}z{|xyzxzy}{|}~zyxy|~vmq{}}|{|vtsw{ytqmqw{xqoqrsmfehqtuyww|}}|wvuy~|~~z{ztqw||xx~yttuywty|}|{}~}yw{{tmhlpu{{wqpqpnqy|y~{upmv|zz|~xwwx|}|~|zx||z}~}}zy||www{|uvsnuxssuvy{tpmnpquwuux|~{vnpywrvz~{uorx}}wst~{{}{{~}{~|~z{z|vx{y{wwtty~{zyy}~zrjhltz{pjmv~zqqqporv||y}{tpw|z~|tprx~~}~}}|x||qptz~{vpmqt{~yww||upw~{vsqssontw|}}{upvxrty}}zvpu}wuwzz}wt|}{~|}}ux|y}|}zvuy||}{}}vpqx{vy}|z{}{|~xssuy{|{{|{vsnmrwz|yuuw|||~}zz|}vuw|~rr|{uyz}|y~~|{~~{xwvsuz}{zuw~||ysssw}{|zyvoqvy{{z}yqjjqsx||y||||}yyy}{z|}|||zz}vwy|w|{vxz{snqv{|nrz||~~~|ummpuz{vvwyxttx~ztuw|~{}ywyyz{|y}~~~ut~}wttz{{yxy{~yyxtsr{|vxy{|xuruvssxwu~}wsrzzxy|sptuy~|wz~~~}w{}}{{~}~}|zz}~yyy}}zzztv~~{wzxnjntw}xlis}wxxxxtx{z}|{wv}|sosyz~}{}~~}~}}|~~zsw}zuos}~{uutvzzyxz~|xv|{wxyzunptx}~tpuxxv{{xrs|wvz{y|ztx|}|~~|xy~wv{}|~|zz{z{z{~vqu|}~~yy{{{|wszzx|{~|}||yqry|uuuty}wu{}~~|{}}|~|}wv}~}u}~yy|wx|}vw||~{z~~utxzusuww~|uw}~|~}{z~}|{z|}~{zzst|yy~{yv{}}{}{yxz~~~{v}||rsvz{rw{zwwx~~{y|~|}zxz}z~{~yx{{tx{x{~}{sqw}{~~|}zxww~}upx~}vqt{~~}|{~z}yyz}{wy}{z~}|y{~~~zvvtw}{vv{~{z~}qu}}uvuuy{xyz~xx~{z}zwz~{}{||y}|zz}y~wtvvxwqt~}~{|yz~~wsuwwzztu|}{|{|}{}}zyz{wz{tv|~||uruy{rqz||}}|{vuz{{{wrtw|xwww{zwyz|{~{z}~|z{z~~{|}}~||}xwv||st~|uqv}}~|z}~~yxvwytvxy}{vyzzz|~yy~zv{~yy|w{}y}{ysw{|xsuy~zz~~z||}xsuzwwz~~ywzwuz{y~~~|}~z~xou~yy{xux~{pq{}}~|z~~zzy|~|utw}}{z}zy}}z{x{~}|xuw}z|xwuv{|ysu||}}{|zvuz}{|z{~~uu~|w}}|}ytz}wv~vrt|vs}~yttz}zyvu{vrt{}{~~}|~}|{}|~wtx~~{{z|}y~}wsu|~rnv}}}||~zsrx{{wsu{}|}|}|zyy|~}~~~|y{yz}~}tz}|~{~}zuu|{zyyy}{uz{vvz|yyxvv|wsu}y|~~~~~|yz~~zz}xqv~|{|vuv{zuu~ztrvx}yuuv{{zwtw~zxz{~|{|yz}wy~{|yssz~xv||wvy}yy~}}}~yuty~}yxyvw}~xsx|||~~}y|}}}|}{||yx}}|~~~~~{ww~~|xuw}}xwx|}|}|{}vuv{yxxv|{wz|{vx}~zzy||wx~uqv||~|xw|xrtzw|~|xtx~xuxxy}xy~{x{{|z|~xrry~ww{}{zxz{~~xqt|}}~zusv}}}zzxuv{~xy{|}}{yyzxy}zwy|zyuqyzsux~||}yrwxuw|}y{~{z}|}~{yz|vux|~{vx~~w{{vuyxopz{xx}}zwsy}~{uux~~~|vtx}~}~~~}zvy~|wxz|~ww|xqvxtwz}xps{{z}|ww{{vvz~}{|~||~}}}{}~}yvx|~~y{}yxz~~}zx{zrqv{~{|}}{zxx{~~~xuv||{||}}yy~~~|xzvu{~{{}trw|~}{~|}|vtzvt{|usv{}{}~yvyyuv|}|zz~|{|{}}|}}}zww{~y|}{{{|{}}unr{}zxy~|{xx{}{zwuy~yz{}~{z{|{}~~yz|{|vsxz~}zwu{}}~~{zyyx{ytxz{~{trw}}zxyyxx~xvzzz|~}~z{~||upry}tpw~~{}~zvu{~uqsy|~{zz|{wxx|zzyw{|z|~|}|{z}}{|utz{vst{{xwx{~}}}yw|{{|}xuy}}uv|}{}{wtz|wwzz{}uv~|{{~|zzz{}~yustzxtv~~~|xx}{xx{||}|}wqry~}~{wyz}}yux~}z}~|~|}~{rryyvvvyzsqw}}}zz}|{|}xw{~~~y~~|yy}~zz{zwy|{}z{zuw{{vsvwzywx}}}|{{|{xx{}~zz~~~}wty}z|yvx~{{xvvz~z|zsuytsy|{utvz}{~~{z}~yx{~~}|y{{vw{{{~~|~wsv~}}}}~{yxx||snt{~yuv|~~}{{{}}|xww|~}}~{yy|~||~{|}}~|uuux}}zxxz}~}zsu|}uuwx{yuux~}yy{|yy{{xz}||~|~~|z|{y~}trtw{|srz~}{xy|~{squxz~xvy~zzy{|}{xx{~yx|}wvx|~}|{stx~~ztqw{zzy{zyz~~|~~|utx|}vvxz|~{vtw~z{{z{~zx{|}~~~}xwwy}zytt|{tuz|y}|||zxyx{}urtx{{|{z{|{m_Z^cehkpyxnd^YV[bfjqy~wwwxuuwuqjbagknooolifdirw{yvzyqmnljebejqw~|qie`]^cjmmllh`Y[cnsrolmnnllmomkkligedb^]]]^`cd_[Y\fnty}~~~tlgdeilpqoia]\`is}~~|vquz}}zxwtpiiljihjmmkf_aglonifiorpqv{~xpou}~}{rmouwtpppnlhghjhcbcbaadfhnruwxz}{yy}zsry}}~~xrtz{xvtw{}zywxyxwuwzxwxzz{}tpoqrmklmllloqnf^`dddegjllhefkqwyvsponpx}}{z|~}ywxz}|tmmswvwy|~|xtrsy}{qhdcfkqwywsomic^\bjnnkhhhigcacjqtuxzyst|||~|vsv|~}|ytpt|}vollopopsuuspmkmmmppmkllic`_afkoqlgcccdiquvvwvsploy{{{}|||~|z{|{zx{}}vnklotuvyxskhgglswwsmhdegknmhdehjiijorsuuroorvwtqty~}zxvx~yx|}{|~yvwxvsty}}xrprrrqrsrssrsqlhc`bhorruz{vqnihjnswxsmkjjnryzvsmms||}}}|{y{|{}~}smrwyzzzxrmgfintwtpljgcgow||zyvqjefjnmnpsuutsrrqpqqqqrstrruz|wvw|{{~{wrqty||{xvusrplklnpqqrttqsz~|vqprrmjmoqrstwwsjfilnqtutttrrsw{}}{{yurt{|}~}zyyyz{xrllpsux||{|~|wrpt{{uokhjntz~|vuurmkijmrtssttspjhjqwz~{x{~{yz|~~ystx~~~zwvx|ztrrrpmoux{{zytolmrtuuuvtoieejotwuroonlmsz}||~|wsqt}xvy}|zywy}{urpptw{||yromkkpy~}xrnmnprronmopqsutsuy{zvsuz|vux}}vsvz~~zwy~|~~}}}}~z|zxyz{zwx{|xuussvyyvtssvz{zsjeejoty}~ztminw~zvutssuy}|wsrw}yuuy~~}wusolloqu}}~~{uonty|~{z|}}}|z{{yurnmtzztporsuz~|x||unkkmosw|}~ztpqw|}xtrolnu|yxvrooqtuvuvxxxtniimqqrvxxuttw{||}~zvxz}}{tsstx|~{vphfjqx|{yx{|{{}~}}zwtssuvwwsqnmlnrx}}}}xvy~}yww{|xy|{wx{~{z{~{wrpqstspnopppnkmt{~}~}~{y{~|vux~zux||vstw|~~~|z|{xy{~}yxwttuwupkklpsuusrqrrsu{ytprxz~~{yy~~~~}{z{}||{}~|{~}zz|vonnqw|~}zuqollortuwvutuvvw{|zzxtru{{vwy||y{{tsvyxru}~}{zyy|~~~~{xurruwxyzyyyunjimqu{~{xtrrux{zvsstuz}{yxz~|zz||{yyz}}|z}|{}}xvx||usuvwxz{xtpnpv~{vttttx}}}|zupmqvwuuww{~~||~|xvtstw{|xvuw|}|y{~}zxsqty||zwuwz{zxvvz~~{yxvw{|~zrmmry~wronptz{vrpsy}~~~~}}}|||ztprx}~xsppqsyysqu|zzywuux{}|{|{xvtttsttsuwxwtppv~}{xy~|{}~~{||xy|{wuuwyz{|~}|||}~}zwwxxvw}{wwxwvvxyvrljnrwyxutvyyy|yssy}~~zyy|{y}~}}|}~{y{}~~|zzzz{~~|{{z|~xrnmou{~|zyxvtpmosy|||}~}zz|~ysu{|xvvx}|zy{~~|~}}}}{wvvz|wvx|~~}|{{yy|~~}|zvqqsv{~}zuqnoqt{zusv{~|yyyyyz}~{{}}zz{}~zusv{|z|{x{~}|{zy|}wvx|{vtsux{{{xtqpqu|}|{zy{~}zwvvuttvy|}~|z}ztqptz~|zww~}z||zz|{zz|}~|{yvstwyyxwwy|}{wuw{}xxy{}~|wtrsy~yvsprv{}{xvuwy{~~z{~}|}}~~~}}}{z}ysosz~~|z}zvvy}||}~~}xux~~|{|~~}~|wtv{~{urtz~{z|vutuv|{}{xvtvz|~|~zzyxuv{}|{vuttv{|wtrtx|yzyuty|~yx|~{zz{|zy{}}}}|{zyz~~~~~z{~~~~|~yx{|}{vrsv||}}yxxy~zz{|{}~}|wuu{|z}~~~{vsux|}}~}||yx{}yuu|~yvtuvx|z~~|y}}zwvz}|yxwwvtx}~~ywvwz~}}xvtqsz||zvuy||uty~~{y{||}||}{z|{zy~{yz}|}~|{~}xx~~wtvvy~~~~ytuwz|z~}{{~~wvvwvw|}}{xw{~}|}||{xrprwzyz|||}}xz}|wtx~wtsvuy~{|zz~~zzvvz~}zzyzxvy|{zxxy~}|||~}~{yvtv|}xy|yw{~zsrv{~||~~~}zz}}~~|{|}}{|zyzz{~|zz~}yy~ytuvy~zuuuv{~z}~{z}~ywwwvx}zwx~{zzxrnquyy{~~~xvx~}xv|ytrsssz||wz||~}{wz~|yzzzuux{~{yuuw}~}~|}~{zvtu{|y|{xx{|}~zsrw~~}~~~~|z{}}~~~|zyxy{z|}}}zz~zvxwz~}xttsv|~|}yx{zyyyxz~|wux~}}~~~z{zxrpsx|{~~~}ww{{uu{}}wtsttx~}zx|}}|xx{}|xxyyvuxy}~zyutv{~~}{yurryzz{wvyz|~}vtx~}|}||||}~~~~|zx|{z{}|~}}~xx|}zvwtx~~~xuutx~~|~}xx|~zyyxvx}|xvz~~|~|}|xqptz{{~}|}{xy}|wsx|}|wuuvu{z}z|~~}{yux{}zxyzzwyz|{{yuvz~~}}}||~{xsswzw|}xvy{}~xsvz~}~}||}|~}~}|~}{xz}{{}|~}||}xw||xuutx~zwwtx~{z}zz}|yxxvvy~}yx|~}~}|}zunptyy{~||}zwx}{vu{}zwuvtu|z}z}|}}|xvz~~zz{|zwzz|||wuw|||}}}|}~|xtty~xx}}yxz{}~}urv|~~}~{|}|~}~~|x}|z{|z}}|}~yy}}~xuvuyywusv}{{zy|}zyxww{}yx||~~}{|zvoosxz|~}~zvw{~ytx|~{wuuttz~|~~y{||}{wy|~|z{{{vxyy~}|yttw}}}}~~~}}~{urt{|x{{xyyz{~{tty~~|}}{}}~}zx~|{||{~}~xy||~ywvtyxwusv}||~yx|~|zyxwz}ywz~}~~~{|{xqpsy{{~}~|xx{~ysvy|}xvvvty~~}z{}}~}xwz}|yz{|xwyx|||vtuz}~~~|~}xsrw~yx|~yxyyz{ytv{~~}{~}~}~~{xx~}}~|}~~|wyy|xvtty~~yxutw{{}yz}~|zxvvz|yx||~}}|wppsyy{||}|xw{}}xsvy|}ywxutz}}}~{|}}~{vx{}{y{}}xyyx}~}|vtw|~~~~~}|{~|vstz}wx||yzyzz}~vtw}~}}}}||~~|zw|}~}{~~}}~yxyzywusx}zyvsu|{{}{y|~}zyvvz}zy}~{~~}}{wporxy{||~{wvz{}xtwy}|ywwtty{~~~~z|||~{xz|~{{|~|xzxx}}~zutx}~}}~}{|{usv}zvy|zzyyzztuz~|}||{~{xz~}~z|~}}~|yzy}|xwtv||xwrsx}|||yz~|zwvz~zy{{}~~~}}|wqosxy{}|~{wvy{}xuwy}|ywwtty{~~z||}{y{}}{{|~{xywx}}}xttx}~~}~}{~xsrw}ywz{yyxy{xuw|}|}|||}zx{~~~}{~~}}zyyy~~{yvtx|zxvrt{||}{y||zww{}yy}~{~~~~|wqptxy||{~{wvy{|wtwy~|zxxtuy{~~}~{}|}{yz|}{{}~{xyww|}}zutx|~~}{|zuru|{vx}|qf]_bbejov~ytpmmllmmliho~{vssusg`egfb`bcehkmrv{vja[Y]emqs|{unhdejlgclttld^XUTW[_bdgiiggjkntzuhbdec`][Z[_bdegkoty|}ysr{{xwxywrkfa^`elpqt~|}vmg`[Y[^acgihgfgggnwypgeilmlmnpquwxx{~~y{|}~zz|~wmhlqplmrvuqmlllossokgdejotvu{}wttw{}{zyxz}tu{xsuxwqprw~}xsssvy|{z}}sptxtonrsstuxz{~zsi^\dihfipttrrsuwy|{wsmjlqv{||yxx|vuyzuqrtx~~{vrljjlortsw|~tkkpqkgilnqstx{}~ztk`Y]fhfgov{{z||}}vpnprvxwz}}}x|{usstz|xrmhddgjnssv{~~wmjnrohghhknopstvy{yupf`clqonsw{{xyxx{~|wqorux{z{{tuxuomnpv{zwqmihjkknnotvwrieimmhgjknpqruwy|~yshabkqonsw|~|~}{tpsvz}~~~}}{|}|vwzwqpqrwzxtnjgfikkopsy{{vlhkooiilnrtvwz}~ysgackqooty}vqttwyy{}z{|~~}}}z}{usssxywrkfceijmrtx|}|vmknqpkkkmqrttvwz}~{xrhgluwtuy|~}|{~}zsrvwyzz~~z{z|~~wvzxsoppsyzxsnjhjllnpqvxxunhjmpnlooruvuwy{~{xnfgnvvtvy}~yrtwxz{}|yyx{|z{||~}xy|ztrrrvzyvqlhgkmmqsv|~~yqlmprnoqquyyy{}{wnefovusvyzstvvwwy}xxxy}|z||~z}yvutwzyuokfeilnsuz{soostqprpsvvvwxz~~zwpjkt{zwxz~ysuwwxxzzxxwz}{}~~~|}{vx|{vrqquz{ytpkhjmnqsu{}|xplmpsqqttuxyxyz||ysjiqyzxwy}|uuwxyy{|yxvx}xxz{|}{{~|~~xx|~{vtstyzyuqlhiloruv|~wqnpssqtvvy||}}{woimvyywx|zuvvvvv{|wwww|{xz{}~rhhmuy~{z||yzzutvutwy{}~yrpqqpnnv}{wsooopx|tkcadhloty}~zy}|uuz~~}{xvvw|{slmrvwrommswwurlgfgknqru{}ztnlmqssv{{{~|}~{zywx|wqjcehlopsxzxtu{~{~upsvxvrolntusroighlptxzxusuxxy}~{~~|{zzxrqpot|{xtroqvwtqtx~xskhjoqqsvyxtqt{|~|~}yx}}zsoorw{|yy|}}}z||xxvutsw~{tmkjiltz|}zwusrsy{yuvzzuoklquuvy{zvrsyzz|zzutvz~}zz}{xy~~xttrqqqv}}vnkkiksy{|zwvutuz}zvx|}wpkkptssuwvroqxyx}~|{wwy}|{~{yz}}xssqooptzyrkijjmu{}}zwutsuxzwuw|~xsnmrtssuvuqnqw~yy~zvwz~}}~zz|~{vttrpqsw|wpkjjkow}~~zwwvwy|}ywz{uqnqvxwwyyvqpt{yvy|}}zwz||{}|xz||xtstrpsw{xqlklmrz~~}xwwwy{}|yx|zsonqtuttvurnot}}wvx}|~|yw{~|||}yz}}xuttqoswz}unkkmot|}xuuuwyyyww{{tqpruvtuvtqnot|}wwz~~{xwz}||}}yz~}yvuusqswz~vokklosz~yvwwz||{zx{|tqpsvyxxzwtppu{|vuy}}~|}{ywz|{z{~yy}zuuutrsy}}tnlmosx~zvvxz}}|zzz~wqprtvwuwvronry~xtvz~~}~}|{yxy|~{{}}xz~xuvusquz~~vollnsw}}wtuvy{zxxw{|tpqtuwvvwtqnou}{vuz}}~~~ywwz||z{{y||wvwvtsx||tmllotx|xvwz}}zzz~zsqtvwzyzzvrorxyuvz~~{||~}xvwz}yx{~~yx}{vvvutu{|tnmnqv{zvvx{{zz{xqptuvwwxvroot||vsvz}|{}~|yvvy|}yy|~z|}z{zyx{{sqqsy~{xx{~~z{{~vrsvuwwxxtomov~vrswz~{vx|}z{~~}xrpswz~}xvy}~|{{|{zy{}uppqw}}zy|}|}xstxwxyzzwropv~vrrvz}{vvz}}|zz~~}ytqrwy|}xuw|~|{~{{{yy{wqqrv}}zy{~|}xrrvvuvxxupnpvwqquxz~|vvz}~|{{~~|xtpsxz~~yvx|~}|~{{{yx{|upprw~|ywy|y{ysswvuuxxtpmpvwqrvz}|xw|~}~{wsosx{}yvy}~}|~||{xy}ysoorx|yx|}{}~wsuxxwx{yupotz|sptx|~yvv|~}{|}yvrpuy{}zxv{~~~}}~}{{zx{xspptz{yy}|{~|uruwvtvyvropu|{rpswz|}~~yww|~}|~|xurpuz}}zyw{~~~~~}|{ywz~}vqoot{zxw{{y}}vsuvvtuxurnpv}|squy|~zyy~~}xtrpty}}zyx{~}|~~~}|{zwy~}vqonry}yx|~z|xuuwxvvyxtqqv|}upsx|~~zxw{~~}|~ytspsw|}yyxz}~{|}~~||zxx~ztqoqw~~zyz|{zuuuvvsvwtposzxqquy|||}|zxy|}}}|usrrv{{zzz{~}{|~}|{ywy{tqont{|xx{}z|xuuuvttvurpqw~}vqtx|~}}|{z|~ytrqsw|~zzz{}~{z|~}|zxw{xspnou|}yz}~{~xvwwxuvwvrqsy{tqty|~}}|zzy{~~}}~xsrrsw||yyz|}|zy|}}|zxy~~xsqopw|y{~}|{wvuuvtuuspqu|yrquy||{{~{{zz|}}}|urrsuz{z{||~|zz}}}{xwzytqpos{}yx{~~{|zwvuvttttqpsy|vrtx|~}|~||||}~~xsqrtw}|zz}}}{yx{}}|yxy}ztqpoqx}z|}}|yxvvvvvusqty}wssw{~~{|z{{||}}}ztqrtvz~{yy|~}}yxy~}}|zxz~}urqpqw}{|~~~{yxuuuutsqpsy}wtsvz}}zz~yz||||~|}ztqsuw{{zz}~}zxy~}}|yxz}{sqpprw~}{{}~~~|zyvututrpptz|wuuw|~~{||x{}~~}~ztqsvw|}zzz~{xvy}||{yx{~xqppqsy~}~~{{zvuvwtrqrw~zvvvx|~~z|yw{}~~~}|~~xsqtvx|zxy{zwvz{||zyz}wqpqru{~~~|{{yusturpory~xvvvx|}|y{xvz~~~~||~~xsquxz~zxy{{xvz{{{zxy}vpppsv{~~~}}}|~|{|yvsturoosy~xwxxy}~{|xvz~}|~ysquxz}zxy||xvy{{{zyy}xpopsvz}~}{}{xtuwtposyyvxyy|~~|{~xux}}|{}zuqswy{{vx{}xvw}{z{zyy}|soprvy~}|~{||yurutpnpv}{vvxxy|}|z{{uuz~|{||xsrvy{~xwy}|wvyzzzyx{~wpoqux{~~}z{|{|{xssusonry}yvyzz{~~}|}xuw}~||~{wrswz}}ww{~zvw{~}zzzyy|~uooqvy|~{|||}|ytuvsoot||wvy{z|~~}|~~vtw}}{{~~zvrsw{|zuw{}yvx|~|yyyxz~|topswz~}{}~{||zvrttqmpw~yvwzzz{}||{{uuy|{||yusuy}~xvy~}xwy~zyxxx{~wqoquy}~~zz}|{|{ytstsons{{xwy||{}~~}}wuw}~|{~}zwttw|~{vw|~~zwx|~{yxxxz~zsooty}~|z|}|}}{wtutqnry{wvx|}||}~~}xtuz~{z|}{xutv{~{wvz~~{xx{}ywwwz~{upoty}~|z{}{||{wsssqnqy{vvx{}{{|}~}yuvz{z|}{yvuv{{wvz~~|yy||ywvwz~zupoty~{yz}||{{wtrrqnqyzvvy|~}||~~yvv{{{}|zxwuv|zwv{}}{yz|~|xvuw|}xtppu{{y{}|}||{xusrqpt}xod^]`diryvpic`efffiotzztoifdeilprpokdba]_eltvx}yrke`[VTVX^hx~yuwwtqmg`ZXZ`flkebdhlnkhgghffkoqrqnid`^\afilkhfjkjhjpssqrvxz~~||unhdbb_^^]\[]`enzzwvx|{smmoppmhfffddinqqrpmifeejquxywuwyxvuz}|yxy{{}}{xvx}}{{ywwxxpifejquqkgdabdddefebcfjpy~}~}{~|xwwy}~{{|}|~zywsoqw{{wsmjhefkppnlmmllmolfbaemttokheehjiijkhfhkpw}~~||}}}}|~}uqqmhgjpuxyuplgcb``bhllihkkjlnojecektzvrnkjknnmmonjilpu}{}||~}}|{|~}~xpnmiddiosvvrlhdaa``cjmmjjmmmoqqmhgjpy}ytpnmorqqprqmknsy}z~}{|~}}{{|~}}~|unlkgcdjpuwvqkgdbbabflnnklooprtrnjjnu}zuqoorsrrrtqllpv|zy|{}~}|zz|}}~zqlkiecgmsvwtmheccccejopnknqqrttrnkmrzzuqpqttsrttplntz}y{|{|~~}{yz}}}~{tmkifcekqvwvpifdddefjorqnostuwwurnosy}xsqruvutuvsnntz~yy~}{{~~{zy|~}woljhefkrwyyskgeefghkprroosvwxxwtpptyytqqtvustusnmsy}yx|}{||zy|}xqlkhfglsxzyskfeffijmqssppsvwxxvtqquzztqruvustusonsz|yx|}{||zy}}wqljhginuyzyrieffhkmorutqrvyzzywurtw}xsqsuvutuusoov}zxx}|{~|zz~|wqlkiilqx{|yqifghkmoqtvtrrwz{{ywutuy~~wsqstuttuuropw}yxx|}|~|zz}xrljijmrx||yqiegiloqrtvussw{|{xwvuvzxsqstuututspqw}yxx|{wokkmrux~}{zz||sjdeglqvz~xtsssrppnmkinu|}ytpqqsy~}zwvusqpoptwz~||}~}~~~|wtsuspoqrqtw{{xuux}|xtmhghjnty|{voiilpuy}~|utuwwvvttvwvtssuwz||xwxxwy|~{wuvy~|ytnllnrv|xqmnrvz~}}}~~}}{zyx|{wuutw{|{{zyz}~}}}ztqruxxyxwxz{ywuuwy{}~~ztrrvy}zroquy}ywwvwwvutsrrv}|wroonnruvwxvwy|}|||ytstx{{|{yz}}zxvvwy|}}{~}wtvz}zspsw{zussrssrqpoopu}}wsnmmmnruvwxwx{~~}}|yuvx|~~}{{|~zxvvx{}~z{{vvy|~vqqty}~~}}vrrqqqrqonnoryytpnnnnqtwxyyy{~~}|xwx|}||}zwvwy|~zx{~xvx|yspsx|~}~~xsrqqqrqpoopsxzuqnnooptwxz{{{~}|ywx|~|{~~{wvvx{}~zwy~yvx|ztqrw|}~ytrpppqqpoortx{upnoppqsvx{|||}|zyz}}|~{wvvx{}}ywy~yvx|ytqrw|~}}yurpppqqpopsvzztpopqrrtwz|}}}}{zz{~}|~{wuux{~|xvx~~~~xvy}}xsqrx}~~~}yurppqrrpprvy}xrooqsssux{}~~{zyz|~||~~zvuux|}yvvy}~}xwz|wsqtz~~}|xurpoprrppsx{~vqnpqsttux|~~zyz{}||~~zvuux||xvvy}}|xvz~zvsqty~}|yvrpoprrqqty}~wqoprtuuux|~~zyz|~||~zvuux{|xvvx~~}|}yvy}{vtqsx~}|zxtqppssrqty~xroprtvvuw{~{xy|}}|}|wutwz~|xuuw|~}|}}zwx||xusrv|~|{yvrpoqsrqsx}{tooqtvvuvy}}yx{}||~~zvtux|zvuvy}|{~{xwy~}xusqsx~~|xuttvwvwz}vsuwz|{z|~{|~~}yvtvy}zvssty~|yxww{|zwuux}}zvtrrty~~|xutuwwwx||usuxz||z|}z|~~}xutvz~ytrsuy~~{ywwx||ywvvy~|yutrru{~~~{wutuvwwy~yssux{|{z}{z}~~~~{wtuw|}wsstw|}zxwwz}zxvvx|}zvtsstw~~~}yvttuvwx||ustwz||{|}z{~~~}yvtvy~ysrsvz~zxwwx|~{xwwwz}zvttstv|~~~{wttuvwx{}vssvy|}|{~~zz~~}~}zvtvx}ztrsuy}{xwwx{~{xwxxz~~zvttttv{}}~{xustuvx{}vrruy{}|{~~zz}~}}}zwuvx}{trsuy}{xwwx{~{xwxy{}yutttuv{}|}{xustuvx||vrsuy|}||~}zz~~}}|zwuvy~ysrsvz~~zwwwy|}zwwyz|{wtstuvx||}}{xtttuwzytrsvz}}|}|y{}||{yvuwz~wrrtx|}ywwxz}~|ywxz|~zusstvwy}~|||zwtttuw{}xsrswz}}}}{y{}|{{yvvw{~wrruy|}ywwxz}~{ywx{|~~yusstvxz}~~|||{wtssuw|}wsrsvz}~}~{z{}|zzzwvwz~xsrty|}ywwxz|}{ywx{}~zurrtvxz}~|||{yutstw{~xtrsuy|~~~~|zz~~|zzzxwwy}ysrtx|~zwwxz{~}|ywwz}{vsrsuxz|~}{{{zvtssuy~zurrtw{}~~~}zz|}{yzyxwx||vssvz}|ywwy{}~|zxwy|}xtrrtwy{}~}~|{z{yvtstw|}wsrsuy|~~{z{}~|zyyxxxz}zustx|{xwxz{}}{zxwz}|wrqruxz|~}}~|{z{xvtstx}|vsrsvy|~~{z{~~{yyyxxxz~~ytstx|~zxwxz|~~|{ywx{~~zurqsvy{}~}~}{zzzwutsuyytrrtwz}~|zz|}zxyyyxy|{wtsv{~|ywwy{}~|{yxwy||wsqrtwz}~~||zzzywustw}}vsrsux|~~{z{~{yxxyyy{~}xutuy}}zxwy{|~~}{yxwx{}xtqqsvy}~~|~}{yzzxuttv{wsrsuw{}{z{~|ywxxyy{}}yvtux|~{yxxz|}}}{yxwx{}ytqqrvy}~|}}{yyyxvttv{xsrsuwz}{z{~|ywwxzz{~}yvuuy}}{yxy{|}}~}zxwwx{|xtqprvy}~|}}{yyyxvttv|wsrsuwz}~{{||xwwxz{||xvvvz~~~~}{yxy{}}|~|ywwwy}{wsppsw{}|~}zxyyxvtux~}urrtvx{~}z{}~zwvwy{{}~zwwvx{~~}|zyyz|}}|~~{xwwxz~~zurpqtx|}||yxxxxvtuy{urrtvx{~|{{~~zwvwy{|~}ywwwy|}}}|zyyz|}||}~zxvvx{~~yurpqtx}|||yxxxxvuvz|urstvx{~}{|~~zwvvy{}~}ywwxz|}||{zzzz|}|{|~{xvvwz~zurppsw|}|~}zxxxxwvvy}vsstvxz}~{{}{xvvx{}~~zwwxz|~||{{zzz|}}{{}|xvuwz}|vsqprvz}|}{xwwxxvvw|ytrsuwy||{|}ywuvz|~|xwxy{~}|{{{zz{|}|{{~~zwuux{~ytqpqtx}||}zxwwxwvwz}wsstvxz}~||~|xvuw{}~zwwy{}~|{{zzz{|}}{z{}yvuvy|}wsqpquy~~|}}yxwwxwwx{|vssuwx{~}||{xvux{~}yvwy|}}{zzzz{{|}|{z||xuuvz~|vrqqrvz}|~{ywvwxwwy}zussuwy{}|}}zwuvy}~{wwy{}|zzzz{{|}}{zz}~zvtux|ytqpqtx}~|}~zxwwxxxx|}xtsuwyz}~|}{xvux{~|xwxz}}zyyz{{|}}|zz{{wttvz{urpqsv{||~{ywvwxxy{~yuttvxz}}}|yvuwz~|xvwz}~{yyz{||}}|{z}}rkgfgilptywohc_\[\airz}vropsrrpopoljgfecbeiqwz~xog`]\]`dioty{ria]]_bfggd_ZWWY[_fkoqqrsvxwtplihhjmopnkgdb^\ZZ[]`chow~~~~{xuqnlheb^]^`dhlptvy|}uojgggjnprsrrstuurnkhilosuvusqpmkjhijlorv||zyy{}~{{{}{vrmihikmmmllkjiihgfdbbdgkoruvwy{~~{ywutuwy{~zxvvwyz|~||}~}{yvrpliijmprrpmiea_`behjkklllklllkjhgilpsvxxxyz~}{zz{}~zwvuuvxz{}~|ywvuwy{{zxvutqnkifdcegknomkgc`^`bfjmnoopppppponlklorvxyyyyz}}}~zxvuuvwxz{}~|ywtsstvxxxusrqomjgecbdfjmonlieb`adhloqrrssrrssrqonnpswy{zyyz|~}~|xvutuvwxy{}~|yvtrrruwxxvtrqpnkhgdcdfimopolhebbdhlpsttuuuuuuttrpoqsvy{|{zz{}~}~~ywuuuuvwyz|~}{xusrrtvxxwusrqomjhfedfhkoqrplhecdgkptvwwwwwwwvvusrqrux{}|{zz|~}}~|xvuutuvwyz|~~~}zvtrqrtwxyxusrqomjhgffhjnqsspmifegjnsvxyyyyxxxwwvtsrtwz}~}{{{}}|}~zwuuuuvwxy{}~~|yusrrtwyzyxusrqomkigghjmpstsplhfgimrvyzzzzzyyxxwvtstvy|~~|{{|~~||}|xvuuuvwxyz|~~{xusstwy{{ywusrqoljihijmpsuurnjggimqvy{|{{{zzyyxwuttux{~~}|{{}~|{|~}xvuuvvwxz{}~|yvtsuwz{|{yvtsrpmkjijkmptvvtplihimqvy|||||{{zyxwvutux{}~|{{}~~|{|~}xvvvvwxyz{}}zvttvy|}}|zwutrpnmkjklnqtwwuqlihjnrwz|}}}|{{zyxwvuuvx{~}|{{}~~}|{|~|xvvvwxyz{|~}zwuuwz}~~}zxvtsqomlklnpsvwwupliikotx|~~~}||{zyxwvuuvy|~~}{{{}~}}|ywwx{~}|zxwvvx{{xwxz{|}~~zupmkkmqv||xtqommnqu{~{xvwy{}}|{zxwutssssux}|{{{|{zywwz~~{xvvuvwxxvqmhghkpuy|}~}}{zyxvusrqqrtuvtrpooqsvy{}~~}|{xvtrqqsv{~~}xussssstuvxz{}~}|zz{~{urrux|~|zywvtrqpqrtvwvusrsux{~~{xvtstvz}zyyy{~~|zxvuuux}zupnmnoqrsuwy|~}{|~~{{~~|{zz{|~}{zz|~~{xvtuvy}{xvuvwy|}}~|zwutqponorv{zuqnmnoqsuwy{}}|~~}~~~~zwuuuw{~{xutttvy{}|zz{}}zxvtrqonmnpsx~~ytqooqsuvxz|~}}~|}}zwuuvx|}zvtsstvy{}}zxyz|~~{ywusrponnoqu{~ytqpqrtvxz|~~~~}}~}{||yvtuvy}~|xusrstvy{}|yxy{}}{ywusrponoptx~}xtqqrtvxz|}~~}}}{{}~zwutux{~}zvtrrsuxz|~}zxxz|~|zxvtsqpoopsw|{vsrrtvxz|~~~~}||}{z{{wutuwz~}zwtsrsuwz|~~{yxz|}{ywutrqppqsw||wtsstvy{|~~}||{|~{zz}{xutuwz}~{wtsrsuxz|~|yyz}~|ywutsqqqqtw||xtsstwy{}~~~}|{{|~~{yz}{wutuw{~}zwtsstvx{}|yy{}~|ywutsqqqrux}|wtssuwy{}}|{{{|~}yyz~}zvttux||yvtstuwz|~~{zz|~{ywutsrqrtw{zvtstvx{}~~|{zz{|~{xy{~~|xvttvz}~|xvtstvx{}~{z{~~{ywutsrrsux|yussuwy{}~~|zzz{}}zxy{~~~|xuttwz~~{xuttuwy|~~{z|~~{ywutsrrsuy}yvttuwy|~}{zzyz|}yxx{~~~|xuttvz~}zwuuvx{~~|{yxwwxz}~zxwxz|~~|yxwwwy{}|yustvy~}{zyyz{}~~|{z{|~{xussuy}}zxvuvx{}}{zxwwxy|{xwxy{}}zxwwwxz|~zwtsux|~|{yyy{|~}|{z{}}zvtstw{|ywvvwy|~|zyxwwx{~~zwwxz|~~|yxwvwy{}|yustvy}}|zyyz{}~}{z{|~|yvtsux|~{xvuvxz}}{zyxwwy{}ywwxz}}{ywwvwy{~{xustwz~}{zyyz|~~|{z{|~~{wussvy}}zwvvwx{~}{zxwwxz}{xwxy{~|zxwvwxz}}zvtsux|~|zyyy{}~}{zz{}|yvtstw{~{xvuvwz}~|zyxwwy{}ywwy{}}{ywvvxy|~~{wtstw{~|{yyyz|~}|zz{}}zvtstvz~|ywvvwy|~|zyxwwy{~zwwxz|~{ywvvwy{~{xtstwz~}{zyyz|~~|{z{}}zwtstvz~|ywvvwy|~|zyxwwy{~zxwxz|}{ywvvwy|~~{wtstw{~|{yyyz|~~|zz{}}zvtstvz~|ywvvwy|~|zyxwxy|}ywwy{}}{ywvvxz|~}zvssux|~|zyyy{}~}{zz{}|xutsux|~{xvuvxz}}{zxwwxz}{xwxy{~|zxwvwx{}|xustvz}}{zyyz|}~|{zz|~~zwussvy}}zwvuwy{~}{yxwwx{~~zxwxz|~~|yxwvwy{}{xtstvz~}{yyyz|~~|zzz|~}zwtstvy}}zwvvwy|~}{yxwwy{~zxwxz|~~{yxwvwy{}~{xtstvz~}{yyyz|~~|zzz|~}zwtssvy}}zwvvwy{~}{yxwwx{~{xwxz|~|zxwvwx{}|xussvy}}{zyyz{}~}{zz{}~{xussux|~{xvuvxz}~|zxwwxz}|yxxy{}}{ywvvxz|~}zvsstx|~|zyyy{|~}{zzz||yvtstvz~|ywvvwy|}{yxwxy{~{xxxz|~~|zxwvwx{}|xussvy}}{zyyz{}}{zz{}~{xusstw{~~{yvvvxz}|zyxwxy|}zxxyz}~{yxwvwy{~~{wtstvz~}{yyyz|~~|{zz{~}zwtssuy|}zxvvvx{~~|zxwwxz~|yxxy{~}zxwvwxz|}yussux|~|zyyy{}}{zz{||xutstw{~~|ywvvwz|}{yxwxy|}zxxy{}}{yxwvwy{~}zvtstw{~|zyyyz|~~|zyz|~|yvtstvz}|zwvvwy|}{yxwxy|~zxxyz|~{yxwvwy{}~{wtstvz~~|{yyyz|~~|zyz{~}yvtstvy}}zwvvwy|~}{yxwxy|~zxxyz|~{yxwwwy{~~zwtstwz~~|zyyyz|~~|zzz|~|yvtstvz}|zwvvwy|}{yxwxy|}zxxy{}}{ywwwxz|~}yvsstx{}{zyyy{}}{zyz|~~{xusstw{~~{ywvvxz}~|zyxxxz~|yxxz|~|zxwwwxz}~{xtssvy}~|{yyyz|~~|{yz{}}zwtssuy|}zxvvwx{~~|zxxxy{{yxyz|~~|zxwwwy{}~{wtssvz~~|{yyyz|~~|zyz{}}yvtssvy}}zxvvwy{~~|zxxxy{{yxyz|~|zxwwwy{}~{wtssvz}~|{yyyz|~~|zyz{}}zvtssuy|}zxvvwx{~~|zyxxy{~{yxyz|~|zxwwwy{}~{xtssuy}~|{yyyz|~~|{zyz|}zwtssux{~~{ywvvxz}}{yxxxz}}zxxz{~}{yxwwxz|~}yvsstw{}{zyyy{}~}{zyz{~|xvsstvz}|zxvvwy|~~|zxxxy|{yxyz|~|zxwwwy{}~{xussuy}~|{yyyz|}~}{zyz|~~{wusstw{~~|ywvvxz|}{zxxxz|~zxxy{}~{yxwwxy{}}zwtssvz}~|zyyyz|~~|zyyz|}z|~vme_[Z\bju|wohb___aeiov|}xrnjhiiiiijieb^\^adinsy|~|tnjgffgikosx}{tojfddefgikmooonmjhjlostvvutqmkhfdbabdgkmppnjea^]^`dhlpuy~}ywvtrojeb_^_adhlpswz|~|wsqqrtuuutqoljhfeddfimqvy|{xtpmllnqtw{~{yy{~}zwsplifeddeefedb``acfjnrvy{}~~~}{zyxy{}zwwxz|~}zwvuttuwxyyxuqmihgilorsssrpnkifdba`abdfggfeddfhlptwz}~}|{z{~}}}~~|yvspnmmmnpqrrpmiebabdhkmopoomkigedbbbdfhjkkjihijmptwz}}|zxxy|~}~}yuromkkjklnopomieb``beilnppponljigeedegikmnmlkkkmpswz}~|zxwvx{}}~{wtqnlkjjkmnoonlieb`acfjmoqqqpomkjhgffgiknoponmlmorux{~~|ywvvwz}}~{xtqnlkjjkmnopomifcabdgknqrsrqpomkjhhhikmpqrqonnnpsvy|~~|ywuuvy}~}~|xuqomkjjkmnpppnjgdbbdgkorsttsrqomlkjjjlnqrssqpopqsvy|}zxvuvx|~}}~zvspnlkklmopqqpmjfdcdgkortuuutsqonlkkklnqsttsqppqsuy{~~{ywuuwz~}}~|yurpnlllmnpqrqolifddfjmqtuvvutsqpnmllmnqsuuusrqqruwz}}{xvuvx{~}}|xuronmlmnpqrsrplifefhkosuwxwwutrponmmnprtvvutrrrtvy|~}zxvuvy|~}}~~zwtqonmmnpqstsrolhffhkoruwxxxwutrponnnprtvwwutsrsux{~~|ywuuwz~~~}}|yusponnnpqsttsqmjhghjnruwyyyxvusqponoprtvwwvusssuwz}|ywuuvy}~~}{zz|~~xrmkklptx}~~ytpliggimsy~}zyxy{}~|zxvtsrrtvy~|xvttvz~}|{{{yvsokgfgjmquxz{zzyxvtrqqqrsuwwwurponoqsuwxyyyxvtqomllnrv{~~}~~zxwxyzz{||}|{zywwwz~}yvuuwz|}{yvtrqppqsuwwvtrqpqruwz|~~}{yvsqppruy}}{zyy{}~|zyz{~zvrppqrtvxz|}}~}|zyyz}~||~~|{z{|~~|zyxy{}|yvsrsux{~}{ywwwy{}~~~{xvtsssux|{vrpooqsuxz|~~|{{|~~~~}}~}}}~zwustvy|}zxvvvwz||{{}}zxusrqqrtw{{vsppprtwy|~}||}~}|}~|{|~{xvttvy|}{xvuuwy{}|zz{}}zxusrqqrtw{|xtrqqsuxz}}}}}|{|}~|z{}}yvutvx|~~{ywuuvxz||zz{|~{yvtrqqrsvz~zvsrrsuxz}~}}}|{{|}|zz{~~{xutuwz}~|zwvuuwy{~}{zz|~}zxutrqrsuy}}yvsssuwz|~~~~|{{{|~}zyy|~}yvutux{~~{ywuuuwy|~|zzz|~~|zwutrrrtwz}yvtttvy{~~~}|{z{|~|yxy|~~~|xvttvy|~}zxvuuvxz||zz{}~|zwutsstvy}|xuttvxz}~}{zz{}}zxxz}~~}~}yvttuwz}~|ywuuuwy{~}{z{}~{ywutstux{~zwuuvwz|~}|{z{|~}zxxy|~~~}}~~zwuttvy|~~|zwvuuwy{~~|{{}~|zwvtttux{{xuuvwz|~}|zzz{}~{xwx{~~}~~}}}~zwuttvy|~~}zxvuvwy{~|{{}}zxvuttvx|{xvuvxz}}{zzz{}}zxwx{~}}}~~}|}}zwtttvy|~|zxvuvwy|~|{|~}zxvutuvy}{xvvwx{}~|{zzz|}|ywwx{~}}}~}||~|xutsuwz}~|zxwwxz|~|{zyz|~{zz{}|zxxxxz{}}zwtssux|~|zyxxyz|~~|zyxy{}|yvtssux{~~{yxwwxz}~|zzz{}}zyz{}~|zxxxyz|~~|yvtssvy}}{zxxxy{}~~|zxxy{}|yvsssux|~}{yxwwy{}~|zzz{~}zzz|~~|zxxxyz|~~|yvtssvy}}|zxxxy{}~~|zxxyz}|yvtssux{~~|yxwwxz}~|{zz{}}{zz{}|zxxxxz|}}zwtssux|~|zyxxyz|~~|zyxxz|}zvtsstw{}~|zxwwxz|}{zzz||zz{|}{yxxxy{}~~{xusstwz~}{yxxxy{}}{yxxy{}~{xusstvy|~}{yxwxy{~~|zzz{~}{zz|~~|zxxxyz|}}zwtssux|~|zxxxxz|~~|zyxxz|~}zwtsstwz}~}zxwwxz|}|zzz||zzz|~~{yxxxyz|~~|yvtssvy}}{yxxxy{}~~|zxxyz||yvtssux{~~|zxwwxz}}{zz{}~{zz{}|zyxxxz{}}{xusstw{~~|zyxxxz|~}{yxxy{~~zwusstvz}}{ywwxy|~~|zzz||zz{|~}{yxxxy{}~~|yvsstvz}}{yxwxy{}}{yxxyz}~{xusssvy|~}{yxwxy{~~|{zz|~}{zz|~~{yxxxyz|~~|yvtssvy}}{yxwxy{}~|yxxyz}|xusssuy|~}{yxwwy{}~|{zz|~}{zz|~~{yxxxy{|~~|yvsstvy}}{yxwxy{}}{yxxyz}~{xusstvy|~}{yxwxy{~~|zzz||zz{|~}{yxxxy{}~~{xusstwz~~|zxwwxz|~}{yxxy{}}zwtsstwz}~}zywwxz|}{zz{}~|zz{}|zxxxyz|~}zwtssux|}{yxwxyz|~~|zxxxz|~|yvtssux{~~|zxwwxz}}{zz{~~{zz|~~|zxxxyz|~~|yvtssuy|}{yxwxy{}~~|zxxxz||xvsssux|~~|zxwwy{}~|{zz|~}{zz|~~{zxxxyz|~~|yvtssuy|}{yxwxy{}~~|zxxxz|~|yvtssux{~~|zxwwxz}}{zz{~~{zz|~|zxxxyz|~}zwtssux|}{yxwwxz|~~|zxxxy{~}yvtssuwz}~}zyxwxz|}|{z{}|zz{}}{yxxxz{}~{xusstwz~~|zxwwxy{}}{yxxxz}~{xusstvy|~}{yxwxy{~~|{z{|~}{z{|~~|zxxxyz|~}zwtssux{}{yxwwxz|~~|zxxxy{}}zwtsstwz}}{yxwxy|~~|{z{|}{z{|~~{yxxxy{}~~|yvtssvy|}zyxwwy{}~~|zxwxy|~|yvtssux{~~|zxxwxz}}|{z{}|zz|}|zyxxyz|}}{wusstw{~~{zxwwxz|~}{yxxyz}}zwusstvz}}{yxwxy{~~|{z{|}{z{}}{yxxxy{}~~{xusstvy}~|zxwwxy{}}{yxwxz|~{xusstvy|~~|zxwxy{}~|{z{|}{z{|~~{yxxxy{}~~|yvtssvy|~|zywwxy{}~{yxwxz|~~{xusssvy|~~|zxwxy{}}{{{|}{z{|~~{yxxxy{}~~|yvtstvy}~|zxwwxy{}}{yxwxz|~~{xusstvy|~~|zxwxy{~~|{{{}}{z{}}{yxxyz{}}{xusstwz~}{yxwwxz|~~}zywwxz}}zwtsstwz}}{yxwxz|}|{{{}|{{|~|zyxxyz|~~}zwtssux|}{ywwwyz}~~|zxwxy{~|yvtssux{~~|zyxxyz}}{{{|~~|z{|~~|zxxxy{}~~|yvtssuy|~|zxwwwy{}~{yxwxy{~~{xvsssux|~~|zxxxy{}}{{{|~|{{|~~{zxxxy{}~~|yvtssuy|~|zxwwwy{}~~|yxwxy{~|yvtssux{~~|zyxxyz}}|{{|~~|{{|~|zxxxy{|~~|zwtssux{}zywwwxz|~~|zxwwy{}|yvtssuwz}}{yxxxz|~|{{|}}{{|}}{yxxyz{}~}{xutstwz}~|zxxwxy{}~~}{yxx~{qha^\]agou~wqlgeegimqtwy|~{yxy{yxuqolifdcdefhjnsuwxx|~~umga^]^`cgkpv{xpid`^^^_```__^_`acgjnrsvxyzwtrpnmklmmnmkjgecba`abeinrw{~}{xuplhea_]]^`bfjnrux{~}ywtqonmnoprsuvwvutrpnmmnpqssttsrqppppqsuy}~}||{zxurnkifffghhiihgfedcbbcdfiloruwy{|~}||}~~~~~|yvsqonmllkkkjhfdb`__`bdfghiiiiihhhijlnpsuwyz{|}~}||}}~~}|{zyyyz{|{{ywuromkihgggggggfdca```bdfhjklmmmmmmmmnoqrtvxyz{|}~}||{|||}}}}||{yxwwvwwxxxwvtrpnljhgggggggggfedccdegiklnoopppppppqqrtvwxyz{{|~~||{{{|||||{{zxwwvvvwxxxwvtrpnlkihhhhhhhhhgfeeefgikmnppqqqqqqqrrstuvxyyz{{|~~}|||||||||{zyxxwwwwxxxwvusrpnmkjiiiiiiiiihgggghjkmopqrrsssssssstuvwxyzz{{}~~}|||||||||{zyyxwwwwxxxwvutrpomlkkjjkkkkkjjiiijklnoqrsstttttttttuvwwxyzz{|}~~}}||||||||{{zyxxxxxxyyxwvusrponmlllllllllkkjjklmopqrsttuuuuuuuuuvwxxyzz{|}~~}}}}}}||||{{zyyyyyyyyyxwvtsrponmmmmmmmmmlllllmnoqrstuuuuvvuuuvvvwxxyzz{|}~~}}}}}}}}|||{{zzyyyyzzyyxwvtsrqponnnnnnnnnmmmmnopqrstuvvvvvvvvvvwwxxyzz{||}~}}}}}}}}|||{{zzzzzzzzzyxwvutrqppooooooooonnnoopqrstuvvwwwwwwwwwxxyyzz{||}~~~}}}}}}}}||||{{{zzz{{zzyyxvutsrqqppppppppoooopqqstuvvwwxxxxxxxxxxyyzz{||}~~}|||||||{{{{z{{{|}}~zvrpoopsvz|~{yxwvwxy{~~|{yxwwwwwwxxyzz{{}~}zwtrponnoqtwz~}{ywusrqqqqqqrssttttututtssstsstttssrrrssstuvvwxxyyzz{{|}~|zxvtsrqpppppqrstvy{}~~}}|{{zyxwvuttssssssssssttuuvwxz{||}~~}|{yxwutsrrqqqqrrsstuvwxz{}~}}}}}}||||||}}~~~~}}}}}}}}}}||||{{{{{zzyxwvvutsssssstttuuvwwxy{|}~~}}|{{zzzzzzzz{{zzzzzzzzzzzzyyxxwwvuuutuuuuvvvwwxxyz{|}~~~~~~}|{{zzyyyyyzzzzzzzzzzzz{{{{zzzyyxwwvvvvvvvwwwwxxyyz{||}~~~~~~~}}|{zzzyyyyyzzzzzzzz{{{{{{{{{{zzyyxxwwwwwwwwxxxxyyzz{|}}~~~~~~~~}}|{{zzzyyyzzzzz{{{{{{{{|||||{{{zzyyxxxwwxxxxxyyyyzz{|}}~~~~~~~~~}|{{zzzzzzzzzz{{{{{||||||||||||{{zzyyxxxxxxyyyyyzz{{||}~~~~~~~~~~}}||{{zzzzzz{{{{{|||||||}}}}}}||{{zzyyyyyyyyyyzzzz{{||}~~~~~~~~~}}||{{{zzz{{{{{||||||}}}}}}}}}}||{{zzzyyyyyyzzzzz{{||}}~~~~~~~~~}}||{{{{{{{{{||||}}}}}}}}~~~}}}||{{{zzzzzzzzzzz{{{||}}~~~~~~~~}}|||{{{{{|||||}}}}}}}}~~~~~}}}||{{{zzzzzzzzz{{{{||}}~~~~~}}||||{||||||}}}}}}~~~~~~~~~~}}||{{{zzzzzzz{{{{{||}}~~~~}}}}}}}||||}}}}}~~~~~~~~}}}}||}}}}~~~~~~~~}}}}}|||}}}}}}}}~~~~}}}}}}}}}|}}}}}}~~~~~~~~}}}}|}}}}}~~~~~~~}}}}}}}}}}}}}}}~~~~~}}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}}~~~~}}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}~~~~~}}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}}~~~~~~~~~}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}~~~~~~~~~~}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}~~~~~~~~~}}}}}}}~~~~~~~~~~}}}}}}}}}}}}~~~~~~~}}}}}}}}}}}~~~~~~~~~~}}}}}}~~~~~~~~~~~}}}}}}}}}}}~~~~~~~}}}}}}}}}}}~~~~~~~~~~}}}}}}~~~~~~~~~~~}}}}}}}}}}~~~~~~~~}}}}}}}}}}~~~~~~~~~~~}}}}}}~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~}}}}}}~~~~~~~~~~~}}}}}}}~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sjc`_aflu{ytnhecbbdgkqw~}yvsrqomkkkkjjijlllmmqtux{xrlgdaabdhnu{{vqlhfdba``aabceffhikmmmnoopnnnnmljjihgfeddccccdefgiknpsvy|}zwtqmjhfdcccdegjlptw||zywutrqqpoonnnmmmlmmmmlmmnnnnopppqrsttuvxyz{|~~|{ywutrpnmkjigffeeeeeffgijlnprtwz|~~~~~}}||{zyxxwvutsqponmlkjiihggfffffffgghiiklmnpqstvwy{|~~~~}||{{{zzyyxxwwvvuttssrqpponnmlkkjiihhhhgggghhiiijkklmnopqrstuvxyz{}~~~}|{{zyyxxwvvuuttssrrqqppoonmmlllkkjjjjiiiiijjjkkllmmnopqqrstuvwxyz{|}~~}||{zyyxwwvvuuttssrrqqpppoonnnmmlllkkkkkkkkklllmmnnoopqrrstuvvwxyz{{|}~~}||{zzyxxwvvuuttsssrrqqpppooonnnmmmmllllllmmmmnnoopqqrrsttuvwwxyyz{||}~~}}|{zzyxxwvvuutttssrrrqqqpppoooooonnnnnnnnooooppqqrrssttuvvwxxyyz{{||}~~}}|{{zyyxwwvvvuutttsssrrrrqqqppppoooooooooppppqqqrrsstuuvvwwxyyzz{{|}}~~~~}||{zzyyxxwwvvvuuutttsssrrrrqqqqppppppppppqqqrrsssttuuvvwwxyyzz{{||}}~~~}}|{{zzyyxxxwwvvvuuuttttssssrrrrrrqqqqqqqrrrrssstttuuvvwwxxyyzz{{||}}~~~~}||{{zzyyyxxwwwvvvvuuuuttttsssssrrrrrrrrrsssstttuuuvvwwxxyyzz{{{||}}~~~~}}||{{zzzyyyxxxwwwvvvvuuuuuttttsssssssssssttttvwxyz{{|}}}~~~~~~~~~~~~~~~zvtsstwz}~|yvtsrqrstvy{~~|{yxxwwvuvvvuuuvwwwwxzz{|~|zwvuttuvx{}~|zxwvuuttttttuuuvvwxxxxyyyyxyxxxwwwvvuutttsssssttuuvwxyz|}~~|{zywvvuuttuuvwxy{}~~~}|{{zzyyyxxxxxxxxxxxxxxxxxyyyyyyyzzzz{{|||}}~~~~}|{{zyxxwvvuuuttttuuuvvwxyz{|}~~~~~~~~~~~~~~~~~}}}}}||||{{{{zzyyyxxwwwvvuuuuuttttuuuuvvwwxyyz{{|}~~~~~~~~~~}}}}}||||{{{zzzzyyyyyxxxxwwwwvvvvvuuuuuuuuuuvvvvwwxxyyzz{{||}~~~~~~~}}}}||||{{{zzzyyyyxxxxxxwwwwwwwwvvvvvvvvvvvvvvwwwwxxxyyzz{{||}}~~~~~~}}}}||||{{{zzzyyyyyxxxxxxxwwwwwwwwwwwwwwwwwwwwwwxxxxyyzzz{{||}}~~~~~~}}}}}|||{{{zzzzyyyyxxxxxxxxxxxwwwwwwwwwwwwwwxxxxxyyyzzz{{|||}}}~~~~~~~}}}}|||{{{{zzzyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxyyyyzzz{{{|||}}}~~~~~~~}}}}||||{{{zzzzyyyyyyyyyyyyyyxxxxxxxxxxxxyyyyyyyzzz{{{|||}}}~~~~~~~~~}}}}||||{{{{zzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzz{{{{|||}}}~~~~~~~~}}}}||||{{{{{zzzzzzzzzyyyyyyyyyyyyyyyyyyzzzzzzz{{{{|||}}}}~~~~~~~~}}}}}|||||{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{||||}}}~~~~~~~~~~}}}}}}||||{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{||||}}}}~~~~~sjc``bgmv|~yslhdbaacfkpv}|xurqpmljjkjiiijllmmosuwz|xrlgdbbcfjpv}{vqmigecbaabbcdffghjklllmnnnmmmlljiihgfeddcccccddegikmorux{~~{wtqmjhfedcddfhknquy}}{zxvtsrqponmmmllkkllllkllmmmmnoopqqsstuvwxyz{}~}|yxvusqonlkihgffeeeefghikmoqsvx{}~~~~~~~~~~~~}}}|{{zzyxxwvutssrqponmlkjjihhgggggggghhijklmoprsuwxz|~~~}}}||{{zzyyxxwvvuutssrrqpponnmmllkkjjiiiihhhhhiijjkklmmnopqrtuvwyz{}~~}}||{zzyxxwwvuuttsrrqqppoonnmmllllkkkkkjjjjjkkkklllmmnoopqrstuvwxyz{|}~~}}|{zzyxxwvvuutssrrqqpppoonnnmmmmllllllllllllmmmnnnoopqqrsttuvwxyzz{|}~~}}|{{zyyxwwvuuttssrrqqpppooonnnnmmmmmmmmmmnnnnnooppqqrrsttuvvwxyyz{||}~~}}|{{zyyxwwvuuttssrrqqqpppoooooonnoooooooooppppqqqrrssttuuvwwxyyz{{|}}~~}}||{zzyxxwwvuuttssssrrqqqqqpppppppppppppppqqqqrrrsstttuvvwwxxyzz{||}~~~~}||{{zzyxxwwvvuuuttsssrrrrrqqqqqqqqqqqqqqqqrrrrsstttuuvvwwxxyyz{{||}~~~}}||{{zzyxxwwwvvuuutttssssrrrrrrrrrrrrrrrrssssstttuuuvvwwxxyyzz{{||}~~~~}}||{zzyyyxxwwvvvuuuutttttsssssssssssssssstttttuuuvvvwwxxyyzzz{||}}~~~~}}||{{{zzyyxxxwwwvvvuuuuutttttttttttttttttttuuuuvvvwwwxxyyyzz{{||}}~~|xtsrstwz}}{xvtssrstvx{}}{zyyxwwvwvvvvvwwwxxz{|}~}{xvussstvx{~|zxwvutsssssttuuuvvwxwwxxxxxxxwwwvvvuuttttsssstttuvvwxy{|}~~}{zxwvuuttttuuvxy{|~~}}|{zzzyyxxxxwwwwwwwwwwwwwxxxxyyyyzzz{{||}}~~~~}||{{zyxxwwvuuuuutuuuvvwwxyz{|}~~~~~~}}}||||{{zzzyyxxxwwwvvvvvuuuuuuuvvvvwwxxyzz{||}~~~~~~}}}}}||||{{{{zzzzyyyyxxxxwwwwwvvvvvvvvvvvvvwwwwxxxyyyzz{{||}~~~~~~}}}}|||{{{{zzzzyyyyyxxxxxwwwwwwwwwwwwwwwwwwwwxxxxxyyyzzz{{||}}}~~~~~}}}}|||{{{{zzzzzyyyyyxxxxxxxwwwwwwwwwwwwwxxxxxxyyyyzzzz{{{||}}}~~~~~~}}}}|||{{{{{zzzzyyyyyyxxxxxxxxxxxxxxxxxxxxxyyyyyzzzz{{{{|||}}}~~~~~~}}}}||||{{{{zzzzzyyyyyyyxxxxxxxxxxxxyyyyyyyyyzzzzz{{{{||||}}}}~~~~~~~}}}||||{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyyyyyzzzzzz{{{{{|||||}}}~~~~~~~~}}}}}||||{{{{{{zzzzzzzzyyyyyyyyyyyyyyzzzzzzzz{{{{{{|||||}}}}~~~~~~~~}}}}}|||||{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{|||||}}}}~~~~~~~~~~}}}}}|||||{{{{{{{{{zzzzzzzzzzzzzzzzzzz{{{{{{{{||||||}}}}}~~~~~~~~~~}}}}}|||||||{{{{{{{{{{{{zzzzzzz{{{{{{{{{{{{|||||||}}}}}~~~~~zogb``diqz|vqjfca`adhlrx~zwsqpolkiijiiihjklmnosvx{}}uojfcbbdhlry~ztplhfdcbbbbcdegghijlmmmnnonmmmlljihggfddccbbccdefhjknpsvy|}zvspljhfedddegilosw{}zyxvtrrqponmmmllkklkkkklllmmmnoopqqsstuvwyz{|}~~}{ywvtsqomlkihgffeeefggijlnprtwz|~~~~~~~~~~~~~~~~}}}|{{{zyxxwvvutsrqponmmlkjjihhhgggggghhijklmnoqstvxy{}~~}}}||{{zzyyxxwvvuuttsrrqppoonmmllkkjjjiiiiihiiijjjkkllmnopqrstuwxy{|}~~}||{zzyyxwwvvuttssrqqppoonnmmlllkkkkkkjjjjkkkkklllmmnoopqrrstuvwxyz{|~~}}|{{zyyxwwvuutssrrrqpppoonnnmmmlllllllllllllmmmnnnoopqqrsstuvvwxyz{|}~~}}|{{zyyxwwvvuttssrrqqpppooonnnnmmmmmmmmmmmnnnnooppqqrrsstuuvwxxyz{{|}~~}}|{{zyyxwwvvuttssrrqqqpppooooonnnnooooooooopppqqqrrssttuuvwwxxyzz{|}}~~~}||{zzyxxwwvvuuttsssrrqqqqppppppppppppppppqqqqrrrsstttuuvwwxxyyz{{|}}~~}}|{{zzyyxwwvvuuuttsssrrrrqqqqqqqqqqqqqqqqqrrrrsstttuuvvwwxxyyzz{||}}~~~}||{{zzyyxxwwvvuuutttssssrrrrrrrrrrrrrrrrssssstttuuuvvwwxxyyzz{{||}}~~~}}||{{zzyyxxwwvvvuuuuttttssssssssssssssssstttttuuuvvvwwxxxyyzz{{||}}~~~}}|||{{zzyyxxxwwwvvvuuuutttttttttsstttttttttuuuuvvvwwwxxyyyzz{{||}}~~zvsrrsuw{~}zxutsrssuvy{~~|{zyyxwvvwvvvvvwwwxxz{|}~}zwutsssuvy{~~|zxvuutsssssttuuuvvwwwwxxxxxxxwwwvvuuutttssssstttuvwxyz{|}~|{zxwvutttttuvwxy{}~~}}|{zzzyyxxxxwwwwwwwwwwwwxxxxxyyyyzz{{{||}}~~~}||{zzyxxwvvuuuuuuuuuvvwxxyz{|~~~~~~}}}|||{{{zzzyyxxxwwwvvvvvuuuuuuuvvvvwwxxyzz{|}}~~~~~~~}}}}}||||{{{{zzzzyyyxxxxwwwwwvvvvvvvvvvvvvvwwwwxxxyyzzz{||}}~~~~~~}}}||||{{{{zzzzyyyyyxxxxxwwwwwwwwwwwwwwwwwwwwxxxxyyyyzz{{{||}}~~~~~~~}}}||||{{{{zzzzzyyyyyxxxxxxxwwwwwwwwwwwwxxxxxxxyyyyzzz{{{|||}}}~~~~~~}}}}|||{{{{zzzzzyyyyyyxxxxxxxxxxxxxxxxxxxxxyyyyyzzzz{{{{|||}}}~~~~~~~}}}}|||{{{{{zzzzzyyyyyyyxxxxxxxxxxxxyyyyyyyyyzzzzz{{{{||||}}}~~~~~~~}}}}||||{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyyyyyzzzzzz{{{{{||||}}}}~~~~~~~~}}}}|||||{{{{{{zzzzzzzzyyyyyyyyyyyyyyzzzzzzzz{{{{{{||||}}}}}~~~~~~~~}}}}|||||{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{||||||}}}}~~~~~~~~~~}}}}||||||{{{{{{{{{zzzzzzzzzzzzzzzzzzz{{{{{{{{||||||}}}}}~~~~~~~~~}}}}}|||||||{{{{{{{{{{{{zzzzzzz{{{{{{{{{{{|||||||}}}}}}~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m28.bin b/applications/SprPoP/assets/packed/MUS/m28.bin new file mode 100644 index 0000000..fbdd262 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m28.bin @@ -0,0 +1 @@ +{wvtsttvxy}~||}}upnllnpqrsrqpnljgfffgknry}uy|ywwvxy|}ytqnlkklmoprttqi^Ydttjefkry~{yxxy{~|suwwz}}{wtqnmmnpsuvvuusrqppoonmlllmou||ywutrqppopt{{pgdj{wg\WUUVWXZ[[Z[_iw}yuqopu}~wu}wmb\euzvsqqokje`dptpmmnpsvy||zvqlo}yvtstw}}zvt}uhZ`hejvyupje^Wbz|vqmjiknrw{zu|wuttvwxxxxz}{yh_ryqpt|{ywyzxox{yz|~{tlcozslgdb`_^_bhpz|~{zz{|~yy{twwlaTQozpf_ZXWX[^acefgjmqqip}vssstttuvwxyz}|yxy|o_l}ofbeku}xqkgefd\Yig``abdfinrw{~{oh|urpqrtvxxxwx{{pw||}}uhnoa[[_grwj[fzme`_`cglquy{|~~{xss|tnkjkmoqrrsuy||uzsnrd[Z^huu~|yz{|}{qg]n|rkfb```aa`^]_cjrsv{t{xvvyn^^}|qf^WSQRTWZ]_`behjgamxonoqrrsuwy|}}}|{yvpk|}}~rixh\RNOV`my~yrffrkcccdddfimrvz}~zqeevpnmnpsvy{{{zyy{~~tjx}xutuwz}~~vozykeb_\\`hszkzricaabehlptwyz|z}wmbY_}vqnmnoqrrstvwunw~{~ux}od]YX[bmz|tj_Yoypjd`__`bccba``a`[_uzxvq~~}zxx|tv}pg^XUTTWY[]^_adeb\jtkjlnopqtwz~}xodZitmvh\SLIKPXcnx}{}rpnmlkkmptxz|~~|wnbb~~unlklorvy|~~}}}|yr~~rhvzurpqsvy{|}~xruqmjfcaadjr|zqmkjknqtvxyz|~y{{obUSt~{ywvuuvwvvuuuwxvnu~z{}wy{pida`achnv}uj_Vhyohc`__acdeeddded\Yinilszyp}|}ynfb`__`````bege]gunklmopruy}wk]bysqtywmvkaZURQRTW\cmx}{z{}zrf`yxqmlmptx|w{xlq|vsrrtwxyzz{}}u~yvtqomllmoqty~||}~{~ti]Vm{snkknsw{~}||~}uw}|}~{xztpmlllmmnorwxnbXe|rkfcbcdfgggggijjc^kpgegjou}u~}}}zxvtqmu|vrpnmljhhhjkiahvqllmopsvy}zobeyoihjpwum{rjd`]ZXWUTUY_gnowuiczyqmlmosw{~z~}qv|vrqqrtvwwxy{}~zt{yxwvututttsssssqli}}~uj^[szoga^^afmsx|x|~}}~wx{vtrrrrsrqpooquz{|~sg^mzqjebabcefghhijkjcanpgeefgimszz~|{}}wohaYU`~}{ywusrrpkdnwollmnoqsvz~}vlblwmgdcehnu|{qn|tmhdb`_][YXWXYYV_sxyxllxqmllnptw{}~x~~~~}v~wspopqsuvvwxyysq~{xxyxxwwxxyyxvsoiaXUk{rh_a{{pha\YXZ^chnsx|z}sz{wutttuvwvusrqpokjz{ohy~umhdbbbdfghiijkiccqqigghghikouz~~~~{si_TLUt~|||}}ztnw~uqqrrrsuwz}}vldoypifeefimqv{ypn|uokhfeedb`_]\ZWQUdedipxxvwsqpqsvy|~zyt|xusstuvwwxyyxtq}{yyzzzz{|}~~~|ytmdYSbwwqnosy|sjac{vnga][[\^`cfkouyzw}tx}ywvwxz|||{zxwvtnis|xx||twqljiijjkkklmnmgfsvolllllmoqtwz}yxypfZPRk}ysmkjlpu{|~|{zz{|~{qgmwpljjkmpsuwy|{ql~ysomlkkjigedcb_WVcd__bfls{~{zz{~}ytnis|{{{zzyyyz||wr|{z{{|}|wnbW`vxogddhnvxoeaw|slgca``aaaacfjopmtvv{yyz|~~~~}wov{vvx{~{vsqpppoonopqqkhtztqpppqrtvxz{|}}|zunk~vlaUUl~{rib^]_chou|~tjo|uqonoqtvwxyz{~{ql}{uqonnnmlkiihhd\Zfg`]]_aekrz}xqjb]g~~~~~~}|||}|wr|{zz{|}zqe\ez{pg`^^aelt|zofd{~voifccbbbbbbcefeahy{x{~uwzxxy{}ys{|vuuvwz}~zwvvuutssssqkiwzspppqrsuwz|}~}{xslcav}~wlaW\v}rh`YUSTV[`flrx~}sju{tpnnoqsuwyyzz{|zvwzooxrnkjjjjjjiihgb[^jh`]\]]^aejpw~~vndZVe}yvuuvwy{|}}}}}ztr~|yxyyz{|yoc^m}pf_[ZZ\`ekqw}{pgk~voiecbbbcccccca]Ycpmjmquz~~u|~ywvwy{~xu}xvvuuuvxz}|xtw~}||{zyyxwsmn{|trrrsstvx{}~~|wqh_^sysqrtx}vlbZb|vlc[VRPPRUY]aflquuwvp{~wsqqqsuxz{|||}|ztu|tt{upnllmmmmllljf_bnkda````acfjnrvy{|{ws{xpf]Wd|uqnlmorux{}~yw}|||}}{rgbpvle`^]^`cfjnquzxoq}vpljihhhhhhggfb]comhhknquzz~{{{}}y~|{zyyyyzzzyxurnjfg{}wu}zyyyyz|~}wnd`qyrnmnqv|{rh_cz}sjc]YWVVVWXZ]aeiijvy|yyy{}{xyv|xutsssrqqqqqnggrtmiggffgikmoprssrplgj}vmd\c{~wqmjjknqtwz}|yogp~unifefgijlmnpsvz|{~zwzwtsrrqpoononkdfrrmkklnquz~~}}}}||zwtpkfa[Zh|tjeq}upmmnrv{~uldezyqjea^]\[ZZZ[\_a`^hwzz}{~xv~{zyxwvuutttqkjuwrnlkkklmnopqqqomic]_ryqhag~wqkhffgilnqtw{~|{zqjsxqljiijklmmnnoqrror{ywvvutssrrqmgkutolllmoqtx|z~}}|zwsnhb[TRa{~||}}umjx|uolkkmoswz~~vmglzslgc`^]]\[[Z[[ZWV_jjilqv{~~|xsor|zyyyxwwvusojmwwqnmmllmmnpqqqomhc[UZm{ytqqsv{zqiepxqkgdcbcegikmprsqsxpmzxqmkiijklmnnnnnmkglx{xy||zyxxwvvutqmioxuommmmnoqsvy{}~}{xrpz~~|zwrmf_VPP`w~ytpmllnquz~vnn~~vqmkklnpsuxz}vniq|unidb`_^]]\[[ZXSR[db_adhmrx~}yuoiej~|{{{{zzyyxvrmpzzsppooopprstttrojd[TXkwtmhffhmrxvnjt|uojgedddefghjkkikx~}~|sq}}vronmnoqqrrrrqpmhkvyustwz}~}|{zxsns}{usrrrrstvxz{||zwrlhq|wqjbYRO]t|xrlhedegkosw{~usytrqrsuvxy{|~||smq|vplihfedcbaa`^YU[ed`_acfjou{~ytngaas~ztt|xvvvvwxyz{{{yvqjbYXhxwpieddgkpv|~unuxsomlkjjiiijkkgfp{{z{}{t}~yvuuvwxyyyxxxxuonx}yvuvwy|{~}vx{yyyyz|~}ytlei}}wph_VQ[r~{tmgca`acehlosx||x|yxyy{|}}~~{~wps|wspnlkihgfeeb^X]ggca`acfinrw|}ztme_^m~||}~~xy~|{{{{}~}yune\[jyyrkfcbceimrv{zt{~yusqponmlklkjecmwvttvy|}x~zyyyz{||||||zwqqzzvutuuwxz|~~~}{yuqot|~}}}~}xrjch{{xxy{~yqjaYUaw{tmfa^\[\]_bdhkorsrw~|{{|}~~{w|wsy~yuronmlkjihfc^Zaiga___`bdgjnqtvxxwtpp}zsld^`r{vsrrrtvxz|}~~{w{~}}||}}~}ytmd^aq|xphc`^^`behlosvz}~{{wtrqponmlljgcclspmmnqsvz~zz~{zzz{|}}}}}|yuqt}~yussrrrsstttsqnjfcbl}wpidl}ytqppruy}~yrkc\\j||tmga]ZYXYZ[]_aceeeky~~zw|{vt}}yvtrqponmkifa_flidaaaabcegikmnnmkgdes}vogbeu~xspnmmnpsuwy{||zw|}wqicfu{slfb```bdfhkmoqtttu|zxwvutsqplhgouqnmmopsvy}zz~zvw|xvuttsssrrpnkhd_[Zbw}vnio~|vrpoqsvy}xqibal~zrlfb_]\[[[\]^_`_^bnvvwy}}~ytry~|zyxwutsqojfkrqlihggghiklnnnnlid_^gz~woiht|wsponopqstvxz{zx{zrjjv{tnjhgghijklmnpqrpov}|{yvqntzyusrrsuwy|}yw}|}{zyxxwvusplhc_ZVYi}wpr}xustuwy|~xpifmztnjgdca``___``_\]foppruy}|ytpms}|{zyvqmpwxspnmmmmnoppppnlhc]Y`q}~{xwxz~}unlw{wsqoooopqrsuvusv~vonzztpmlkllmnnoopppmkp{~}~{vsx}yvuuuvwy{}~{wrq{~}|{zywuqmhb\VRUex~zwuttuwz}ztwzvtttvwy{}{sljt~wqlifdba`__^^][WXbihfgjmquz}yvqmhgp~}|zwrpu|zurpoonoopqqqomie_YW_pxuqmkkmpty~~wqs}wspnmllllmmnmkjp{}|}~vqt|vqnmllmmnnoonnlhgnwwtttvx{~}|ww~zwvvvvwxyz{{{ywsnjky~}|zywtplf`YSPWix{wsnkihhjlorux|~|x{wuttuvwyz{|}}{z|zsno}ztokhfdcba`^][XTW`eb``adgjnsw|~~{wrmgcbm}|||}}}}}|{xtsy}xussrrrrssssqnje^XV`pvrlhecdfjnrw|{vy{vspnmlllkkkigekvywvwy{~ytxzvsqppqqrrrrqpnjhowwsqqrstvx{}~~|yvtv||}|{{{{|}}~~}zwrlghu~|{|~~{xtoic\URXhx{xrmifdcdegjmoruwyxy~|zzz{|}}~~~}|yy}wrr~|wspnljigfeca]YZbgeb`abdgjnquy{}~}{xvzzuoicah{~|||}~~zx}}|{zzz{{|{zxuqle]Z`pxvpkgeefhknrvy}}}}{xwutsrqpolhktyxwwxz}~yz}{zzzzzzzyxwuqmqy{wtssstuvwxyyxwtrolmx|wqjhq}||~}wrkd]WXfw}{vplhedddeghjlorsssy}{zut}|ywusqonlkie`_ekjfdddegiloqtvwwwtqnq~ztmgcgw|{zz{|}~}{|xrkd_cqzytnjgffgiknpsvy|~~|{yxvusplmtyxvvvxz|z|~}|yuqt{}yvtsrrrssssrpnlifddnzsmkr}zxxz|~{uoha\^kz|wrmhecbbbbcdfgijiip{}|{wtu}{yxvtsqomiddjnlhfedefgiklnoonmjgehv|vojgm|~zwuttuvwxy{||{yy}ytmfdjw}ztnifdcdefhjlnprssrv}{zxvsomovxvsrrstvx{}|xw|}zvtx~}yusrqpoonnmkifc`][^j|ztoox~yvtssuwz|}wqkebgt~xrlhda_^^^^^__`_^`gqttuwz~~~~~~}zxurpt}|zyxvurokhioqnjgffeffghiiiigeb_]bp}~||}ysnmt}yvsrqqrrstuvvvtv}|wqkjq||vplhfedefghijkllkknw~~}~}{xtqtz{xtssssuvxz{}~}|zwtsy~zy}|yvusrqpomljgd`\XVXdu}|||}ytt||xutttvxz|~|vpkhly}wrmifdba``___^][\bjnmnoruy}~}zxuqmlo{~}|zxvrnotxuqnmlkkkllmmlkhea]Z]iv|{xvuvx{~ztqv~zwussrrssttutrry~xrotytpmlkkkllmmnnmljksz{{{|~}yy|zyyyyz|}~~}zwrps}|zyxvtrolhc^YUT\lz~~|zxwxyz||y|zzzz{|~}wqmny{vromjigfedcba^[_gllklnptw{~{xtpliis~zvuz}zwutssssssrqolid_ZZdqxxvsrrsux|zvy}{ywvuuttttsqpu~~wtx|xusrrqqqqqqqpmjjpwyxwwxz|~}{~~~~~|ytplo{~}{yvsoje_YTSZjw|{xusqqqrtvxz|~~}}}}~~~|~ysps~|wtqnljigfdc`]Z^eihgghjlosvz}~}~{wrmhefq~{wx~}zywwvvvvvutqnid^YZerwuqmkjjlnruy}~{}zxwuttsrqollrz|{zz{}}xv}~zwutttsssrrqoliipvurpppqrsuvwxxxwusqrx~}{xtokin{~}~}{yvsojd_YUV_oyzwsolihhhijlnprtuvvzvmrywzztokjkloswz||xob_c]UQOLIHIMQW]chmqstslk|{tmquq}|y}{|~wrnmha\XXY\`fjnruuo`LUvzstx{|~xs}vz~}ptfWIN]WW]__\YZ]afjoruwz||us{ov{mie]VZeoqmihijlnqqnfk}{n^QJg|wvvy|{po|vrvxxwuuvxz{zxtqnjg_Zltpw|}ztoiebackv|otww|x~yf_[Z_l|wme`[VO]srtyywspnopsvwxxyz{xnk{xqtuusrqqqrqnibZSMJJQt~{|~unhfgkt~||||{yuojbYPICSrwutqlfa^\\]^__^_acc\Wfg_bgnvwvh[RMWwnhd`]YVUSTTQGN[TSVY[\^aeinqstutrmdW^~}yxxyz{}|pfz}qismbaciq}}rdRH]ne\WUVXZ^bfikmnorvyyrywlcn~}|{yxyz{wny|{~|{wtrqrv}}zwutstuvvurnic[RGB^}~xsomlmoruxz||}~vv~vvx}}nj{{}}pcWMFYxohb^ZWTQOOORSOJYa\]afimrw|}rynm~|xqbah\TSU[do{}vl_OOkshb_`cgmsx~}rg]Wn}xutttttsstvyzus~tvojfb``adjqzzri^d}ywvwxyzzxwvvvupfmsiefims{wku}si^TJCEizvsqonljjjklldhxuqsuwxz}ykjzspnnopruy}}qhv{xvvwwwvtromkg\T^^SRTZbmyshz|urrsuy|wne\T\{yxxxxwvvvwwsluywxz{|}vjrvnjgedeglrzyqf[Voxqlhedeghjkkkkkkh``njcdgkpv~{nk}xuuwy||sjaXUi|yvromkjha\infegijklnqtx{}~zsias|tponnoqtw|}ukac~zwuuwy{|||{yxuoehrhcdhnu~{km{qjhgijmptwy{|}~|x~ulaWTlytomlkkkjjjjkjddtvoqtwy{~~yzvspppsw}}woeZQZ|{snifeefhjllllllic\fmc_`cfjpw~ylam}urrsvz~}sh]TVx}yvsqnkigfdaY]licdgijlnqty}zqeeumigffgilpty~um|}zwrh`jl`]^bgnwshvypkiiknquy|~|}yqh_TLPszvrppqrssttvxywqyy{unjgefgkpw~yqf[Tfyqjeb``acdddccccaYUbd[Z\_djqzxqypf]TMV}~|zxurpnnmh_gslgghijlnqtxz|}}{xrg\b~wmgdbcehlqu{wp~{yyyywusqnmkg\Xed[Z]bhq{xo}snkkmpsvyz{{|}~v}~wof]Ua}vxkutkfdcdgkpu||tk_SOh|rjd`__`bdffgghjkiacrpjilpu||yxog^UOa~|yvromlkjc\iphedeghkoswz}~wlbtwrppqtx|vkj{vuuuuutsqomljeZ]ic]]`dks}pqxokijmptwz|}~~|tkb]s~}|zyyz{ztr}||~rk~{pidbacfjnsy|ulbX_ztomllnpqrrrrrssmenwpklnqv|s}~{{}}une]TMNl~|{ywusrrqnfixuoopqsux|xmnvroooqtx{{piw}wsqpoonmkigec_VQ]`XVW[ahpy}u~xuuvy|zri_X_~{zyyzyyxxxyyuox~zz{|}{odkwkb]ZYZ\`ejqw{pebyysnkiijkllllkkjf_`mkddfilqw~wv{zz|~~zuoh`XPM_~}|yrnzwttuuvwx{~~vkdt{rliggikosw|zpgixrmigffgffecba^XQWb]XZ^cipy}zyy{}~yskc[Zo}{zyyyyxwvvvsmmz|ustvvwxz}{rgdv~sjd`__`dhmtz}tibizsnjgffghiiiiiifa]fnhdegknsyzr|yvuvwz}}xrkd\V[v}zvoq}{trrsstuwz}ypgi~wpkihhjlptx|}tkerytpmlllmmlkihgc]Ydib`bejpw{yyy{}}wog_X\x~{ywwwwwvvuuuuqlr~{vvxyz|~~tjntlgdccehmrw}|sidrzsnkihijjjjjiihf_^ilecdgjnsyyt|zz|~|vpib[V^|~yrx}xvvwwxz|xofl}tmhffgimptw{zqm~}xtrqppomljhgfc[Yega_aejpw~~~xrld\Wa~~~~~zu~{pgo{pidaabeinrw}|rhfyysnkjiijjiihhihe^`lmgfhkotz~~|wqke_XUp}vrsqr}wldo|pjjkheddfjnqtvtsqpyyidw{ph`YST`kkigghijje]_e\NLTXRNNQW_hr~o{xp{{mndSLHGINT[bhmmh[a}n_Xhyvmd_]^^_uy^DNujefhkmopmajpcb`[Vanmkkklopqxztlmnpsx|~|wsnmk~|mg_WXqzwsr}jYPJftaVSRTVZ]]Udg_ghfdq}{w|~{xtokz{wurqpqqng\RIFFfpeaaejpu{}wuxxhVHFo|l`WQLIHHA=NHFMMJGTehhiid^Rbysonopstrmgr|qvxz|zpin|yphhwewyvwz~qbQUrf_[ZXYXZQVdZ^bb]ZevxxyztiZtxqnnllkjgcaqznbTa|ofddgjmqswpyz}{tkh{vj}kZHXvg\TMGD@=2EMTY^bgcuŴhliJ4-@hsfIES@64459AKWcmrqf]qeYimgmsurmhp|~{n|xninzsjkknppponmoinu]IAi}hWOMORLP_XTXZSdkhsz{l\NHLexz{~wu}x{urpi_mwszw|xungdmt_Mgy\VRA:KZGCFGIKRZdmtxvqg}}uyw{|jUOVC::Fbdxr\SONQV]elsx|{v}x}zma_aqyk`YRJNa`]eltr~sfesv}spnlllmmkg_WL[z|skmp|sonnnkiytpj_Vszwy~rmfeokogXQPRW]elu||vffvha___Zarrtƻzp\I<:Jdx|unhbTYVGURJJMSY`eknlb^}{y|{kws}q]UNGBFY{|wugaadfikpw}z~zkWC\|k`\\][RYjheav}wqou}yvtsqlf^TI@_zrnorqxq{|vw]\}~qmow~ytqr~{ggg[WX[]biruunl}}uqoonial}q{ɽvg[RJEIZsykunb\PQ`^Z\_`ba]Q_tpry~}w}~z|wg`[XUTYkvzvsrsvyszk[Xyldistnhdbbc_Z^itnov~~z}ulbVJ:?ish`ZY]fpvv|j[}wkegluukfl{~se^kj`]_bfam}qncfv|urrrf\{yȾ©wmg_VNKThu}tmjfaZdne`\UHCcshdchnvoy~uqquywvwnie_XRRXs}|st{nnapp`SMTesuoiggd[bcjnint|{}{ydQ@5Qh\RKHIMSZbkrvyqu}vviehmsytcP?Cy|wsmcgqhbZYjhfmsz}q|rke`coxqfpsbgy{zöwoiaXJKet{vsrogckbM?PfZQNQW_iryw||~{mjknqsx|}}vulcUC4Khszz|}~q^b}mbZSMJM[ktrj^dnc_Zbpmowu~m_[GLhaRE<77;AIPW_fkit~ykhuvprvx{}~}kYIVmeeq|{yumgmhjjacehkrz|zwqppnkhjvxt{pllhdp}{}ºzochaSXh}|uiVKWjbRKHILS[dlsy~~wjivtmmopqon~x`KLaVFCQkymistpa\[ZWRPVcknyqgggfcbq|yz}|xq{x~~lVIk~fSF=7569>DJPVV\rtsu|ytz}}}|w~k`y~jYLITj~{ohsxqjhikou{wxs~zz{ytnjlqxsqpokkw{qlhdqyog`^ey|l\enhcTMLOTY^djpuwvsiy~ywsns}xsqkp~yzvifqucTICCMcwnxvidoohfec_ZQ^vz{wrpqsurq~tje}jn|e`ZJA>==>@EJNLXgejqx|~}gv}l^SKHO`w}rq|zuvy}~wu|~~rkvy~{zxshaf`Shzuqr|tucV]b]\^`bfjpnt}p}xrysz}vwz~xp}l]WTRQT_t|sl{xtlmxvpni`hg`hw|qihm}z}~|kYLRRHB@?ADBBU[Z_dlt~|sf_YSNMVj~w~}yqgt|wpoyvoeXEHvzuru{yw|xpjb`mnfdegfbq|y~xyyzqnyvvy}zzjmpgb`]YW[i|viq}}}}{unu~ooxohdaesvi\kvmns|vh_aymcYMKSNFB?7@LHKQW_gpyzzputib\VQNMSs~qb`{{vux}{vpmr~qaRH]sjij`\_foyn]c{xuogktme[`nihlrx~wgjxoiku|wps}wx{~t~xrikwtjea\VM[vqkzvtux{}}~|xkiqnmkgefpscm{h_hnjmt|~lXSqzphaZOGLN=8D>:?BHOW^ekopt|{zxt}|h_nteZXbsxy|{||}|umgvzpqlaYTPLGFJQWdi_ZXXWVZhrpoomjq|j]htdZTPPQTX]adddqxrooy~xtt|xury{xvqu~~tf_\T^nfYMGIUhy{u|}ytrs~yxsiab{~pic__hy{l\Zfb\WJCACFJOTZ_cco~}~~v|{ywpre[VV^o~|xtnhj{peckjb\XTMDDWbehfcbcddbdnnc\j²|urnbZWVWX[^`_csvsuxyxzuju~y~viZVryhZPHDEN^r~x}qklfg}wrnlnws{|iXOTXSQPQTW[[]mxwy}x~tlhedfn}wohan{vrlfgong`UV\TRYfpttrqpmfZT\t}xx|¿oglle_\\]\Ydrqrtx|yt}xnervrodYRMIFITfv}qbWl}xwy}zsmklp}~qh_WW_`ZWVVQWgjkoty~~{}xytpljluti_]v}zxxwuqkfjlejh^WSRWdr|}xqi^SWjuwqklrzľuo{~tihnkd^VVdgehlrw}ytwxuy{unfkse_dd\TNJGEDSsxrfj~}}unmnrv{whYRiysoib[^d_SNY]YZ]chnswyxund]r~}~yslcbs{utk_j~vpnmnprssrnf[eupf`\YVUW_jrqgZWksgZW`ggjpw~xo}~ukehg]ac\[\_ciov{~~zsjl{vtttw~{wqkc\^uzslf^Y\a\SKB=GLO^q|}~ywx{{usrgelmigiknppvqbftpghq{~zuog]VXbbWQQRSVY]acc`\X_y{xwwwz|wmqrigo}wh]iwoieddgilnnmhkslcgnkea_\XUSTW[lzrf]WRRZhmmptxz{obfvqe_^^`cglprrnii{xu|{zz{zz|~zoe\Wc}~xsnkigea[X\\QPTKEEJVh|ws{pfad|ypic`fnmihfeq}|{la[^hw|thaeddaYUSSSTUTRPMNb}}uopy}zyzzzyzvpkikuuo{vlgeefgiklkju{uqnjhpvsmhaXLBJfwume_\\\\^hsspnv}y{~potrlijkmmmkfbk|wtrx~x}yyrd_pzsolkllmlkhbXUfj]UQNMPXfwtgcuzurnifmsmityvy~vnheelwwkbbhe^ZWTPLGCIcxyvrpomkr~~zz{vqopv}uxytpnmnoolo|{{{{{yvu{~vi\QVeeachlmlkkkiggmr{~xz{wtpjdao}{|}~wjdp}uqnnopqrrqohhngbhhc_]\[\aiszy{~}zwrkep}wwy~|wsppv|ulfgjdZQIB>Iconkiijlnooow}{skdbdm{tszzvsqnjr|{z|~~vqpjdoul`XV[eotusqpmfdp{}||ukabu}xvwy|vnxxqmlklmopqnksxrmhcgonie`ZTON`{tsw{sotsqx{zz}~xrmjwype^]XK??Rcc_\Z[]`dgjlljit~reYOUqztosxtkjttqrtw{~tg^mrf]WTTXaltvtnipslglvyyy{~y|{ypgp|xqkijnsx|}z{wywpkhffffdbjtronnmjfgmog\PECTdjr{yrmknv~}w{~vpkjrz{z{||zwt{|qntsqy}thYKFIVcaYQLIIKNRW[_a``ipr{~~n_Zgvtqsz}xrkgmywnkjlnquy|}|wpin}|srog`\YWUV\djlrxrkhebdmsqmjheem~tiuwlebbdhmrvz{{z~|wropu{qjlrpieb_[^jlijkmopqokedcZOTa^TOQ\l{~xsnjikp{~xuspnq{{uoiiyxoklr|wi[OOaol_QIDAACFIMQSS\ijgfju{{vqqwylb]_hu~wnnssvupmnoruvwupkk|ynefmniea]YUSZnywtqmjhdabih`XWg|rkghilnqtuuy{sja_rytnkotof`fmjghknqtwxwsj^TYovi[QKIMWgyysjeo|}yuuxtjk~|upmnt|yngp~xi_]YPIFEEFHJJLZeefilnpuyrw{srh`\\bn|vqv~~|zzywtniq|xtpnrzztng^]ffiqx{yvsnh`WTW[m{~}ywvwwwu|tjhy~yrmnryyrnnprvz}~{ulcfsuspg^YVUV[et~z~xx~uifw}~yrkhilowugZUXYSNKJIFJY_`bfkpuy{z|}wwohc`bir{~|{tliv}yvz~ypstlecemu{|xqi_ULPl|wuvy~|wuwstsy}{|rqy~zvuvxz|}|ztljxvjdhkhc_][\_ft|x{|tpz}{}ti]UVl{ri_WVZXRJELUUUX]bhnsvxwrkjwzrke`[]nvln|{|~}upywojebadjooj`WUcsrmoqollnrv|{|}}|}}wogh~wsru|~xsu|}zxwwurnheo}vpkebellgb]WS\js~~{}{~~{urw{{z{~re[]o{}{tnf^VQQPPSPKJLOSY^dgiifaao~||}z{}xoe]_ddfp|yolryspprux{~}xw{zxzxqje`[WUVXXZevyqh`Z[bedcehkmp}~yvuuuvvvtqmhiw~wromlmry~{wstz|vqkd_du~ysolkjigddhjd[Z_\XZbo|~umo}yvvxztons{}{zzyy~|upy}slimsy{yung^TOW^YQMKKMPSVYZYWWbuzuopw|||}{xyvqrne``ckwvpt}upoprtwy{|zzytruxskd\TLEEQk|{skc]XTTZbca__erxy~{xvtrpmifen|vv{|wtqnkiis}zxy|zpgcmzzsnlkkmnoomidaenmc\YWX\eq{}xt{xon~}zxuv|zywstkebeltz{xqigh`WW[ZUSSSTSRPNRcrspmkjks~}v{yqkhfglu~y{|ywvwyz{{z}~zutvvnbWMHPcmoqplhc`]ZWW]a`fnoptz}xsnhddr}yvtw~~{vplqxy~~vqqs~xroopsuwxxwtmkoqqoic_\ZZ]ckqux~|}z}voieeipwzzypia^`ed`[WRMIPcnnlkkmprsv~zuqnmnqw}||~|{~xpkjg_\enniefinpomifb]WXgrrpqtyypieix{y}~|}zz~wpt~ywvwxyzzyvtx{wpiiljd]WQLJMVi{{{tmigkv{tme`a`ZRLLU_fhhijlnqsuussyvngdgpzytqpqty~~tjfhe]TKFITgzvlc[SNQ[bfkqw}~wrmgb`evzpklmjfeehmt}}wqppt{vsqonmlllnops|wnf`\]binppu|upnnqu}xy}wrpwwv}l_TMJKQYbipuwtlaXVZ[YXWWRIDOj{thddfhijox||{}}}}|ywvz}|zslgegkqy~}z|sopw}}zwvtru}iQId|nWLJLNPONLIE@>Kbf_\^bhpzp[Xshb^\]]]^ckj``oi_aemwt`qpebcdhlptvwvqkx|lilrrnlklkhd^Wbtjb^blstssrmrvnns{|qvx~qmib[XVSRSWWcyommgaZWYcmmg\N;QiVNMPTY`gmpru}{xuvz~y}zywttxyxxyxunf^}zwuv|~wp~zviZd{dTE<7;DNUVURNAFOGT\][^eo{~u^kvka\XY\adedbb^otehgfdgp|msqqpkjknrtvvwpnvtxuqnsz|wodUF8T{tmgddegnuwsguzs}zwsty}|ylvkddknjd^YRHZ`apvwsqooommleOGe_MIGJMSZbimqqriq~~z~{}~oZY|zsx~|{ygVuq_PD=:Nqod\UQNOPSUX_eg_hpgjpw~xqjedzogceknmkhd^kqimprsuwz{zwsnb_vt`UOORZbinnnkg^`porvwxy}~ws|{zyz|~{lnxroopswz}}}~~|me~wkb[YYZ\]]\WTb_VWUTV[]\\ZWRLFX}zxuuuuw|unkmsy||ytkgrliquussv{xo}zurty}}zwtqnk{zupmeWGFeobYPKGFHKORTUTR\pkgjkmpv|n`T\{uplihhknmdfrjhlnpruy}{rd[pwh]VRQSX_gmpojdondglsvvvxzywqkerywz|yrzvwponpsw|~~yv|r|xolieba```_\V^h_^`_][Z]cge^RE?Xg``elrvz~vpnorv{~}v}tqppsx|~~ynurYWydRIC><;@Nevxn`_srccfjqx{e\rn{ymZ@>djPDACEHIHLYb[QBD[VLMQW^iuO^vFUfv|skb[]tw\U_OB>@ENXdpyv[?ppegoyvuwafxnkmqy~{ôoA$jØkN@>CLT\djoicyzspjeu}|fpwtuwzyrguzchvljjns{}od~pms\jwomqwo\HE§r`UPOPQUX\TTlkijhbs~m~ywvuqi`ot|~rnjlosvvsmgb^TT|vstvx|}u{¹sdVELŲtfZPICBAB59LFGHGANa^`cfgcYKjxpkmrx}|vips~{vkwunxj~sh`Pdtlgc_\\\\PZf_bbb\gzxx{~~zme|{zzytk_g}}urpquz}{woh_ZPkwpoprtwzyɸ½tdWIhtg\SKFDEB7GKEIIJDMb`_acb]NPy|rmlpu{~wik|}}~unkyqovwnf]Y~tmgc_]]_YTfc_bcc^f{{yz|xrbq|{||zuk_dzvqpptxzxrjaXPU|qmmnprsx}yxð¸}n`QWsg\RJFDE.KG4BD[QHG`iC\e_[G?[d]pts^gj[uoU{_juaH_iOMN<^P+`\Q_owx{vjPBSL5(8R`(8fvolba~}{oafI13]UW[wxƿƴìܶͨchm<2V?@F`tq|ryd~ql}uzgpn>TFTXVTcZHfcu{tw{}ka`z{wtrZVenz}wngY?WoMOaGLFL`ST[GJYf]ZCLg]FYghdW\Wawpjct|xasuosu}|~wyzoz|ƞֶ~{jca_enrZsnctbkump{szxniwwtjizpRO^^inp}yfWu}Wfy_cQJYZ`WPTT^W\[k|kTcjhg^]SMQSPCT[P>cTNf[hg[Sbqchguz~lobfxwjd[GCATNO=RhKcicunfqzvvgNOMU[SNGS\H>GHZp^acfOUt^X}qyrzüütyv|}nohzwwp|_tqtzx~~xv{v{uz{w}r~utq{}da}hUixyohRPLQV\TSuYYui|r~uoovrYWdnvpgaSEiiJcaRbTReqvcfryugknxn~zp}|v}u{VV`_VUC;BAJGI?_aOnfhvw|}}vvzwy~ywzotpztlaqjv~|rov{ug_wm`{mexjgt{}qxvzuill{i]kw}{qjcrpnmss}~yxzup{x[]ltoiUQTRR@?>PE7][Yoniqtvriu{yqqk\_gsrkrdZJZjLOcKS[JN]if`e`dPINZmgVVdmoha[^kaiorz~z}wq~}qpttrgY^[fcXSdmQdr`r|pikp~y}zpcdkwptojjnxfubsiZ`hj^a_\[MQWgpjafouuk`SXTNRUmjQYqTRdT[llcfcepo|{wh[f|z`W`lom`VVMUJ;=W\F`j\oyojimtnregtyzl_\hrgUPWdofi`YU[qa[yof{tju}}x~yufe|kx~xuu{zv|{z~}}lbl~{bYbnsrh`d`g]MMfq]ss}qkt~we[]dkcd[SU[jVIhbRd_QYfmiqqpqegrvz{}~rxhkno{yxhajthK@@EGGJBCYj]]wret{zº}zy}sv{~uqv}|xmcn{nyttroplmz~inqdv{mehlpuqnkcfq}~tjjrz~|vrrpu}{ml~isthp|z{zvuj\TXdkdSHGMQSRSQKG=?S]MMijaq|z~~||yxwsz{oqq~qmquy{usnkr}vnr|}||ytmpw}}xphsy~z{zzvts~xv{{xstwylhmrm[I@?AEHNROMHGWkdYmyhfv{vwz~}zx{}{vttupkidit{z|lgwybcvn_dqyzwy|z{ukceowuj_Z\_bcfe_[TTdpd^vqsyzvpnkikjo{xgotfuujhlqusrrot~yw}~|}zphirxtfYTW\`acc^ZUVgvmg~|zmfhqywj]WX]adfgb_\^n|rjzrlhdedgsxn^dvl]jvnb`djoomnns~}y~{pbapnX[ok^_ktwwxxtneVKJPTM>/*,3:@GJJJJPdqe`v}ll}{rxrtvsxzojkoruwvrnkr{|{wsqqrzujynhw}rgegjkigfgmw}zqhgnv{{zzzz}}uhco{o`mxk_bkpqopmgbXOMS\_WKB@BFIKMKHHJYif[i{p{}~{~ywx|{xyx|}y}~~|vps|xlefjorvvrpouzxz|{q}}q~|wx|}~zmfp|rcn}uikt|~wnbVQSXXPB735:AFLLMOTcql_h{uhnzyzwtttz|pq}hfvvgahptuwwtqkdbfpwvmb\[]`cec_^_ix{oo}|xttwyrm|qkz|qjjlmkkmpxwpou}zpqwm|ujmty{{zvpi`Z\ckkbWOLNQTWWTSU^nujg{||}z}vnhiowyrg^[[^`bb^\]et}tl{|yustvz}odm{rcjxyphfhkkloszzuw~}zvl_YaldSXhfYU\ejmnlg`XOKLRSL@3+*-28=?@DM\ki^dxykjw|t{nntz}}yuomqy}tlijlnoolkmw{w~xp|yls~tkggggfhjpw{yqhegmty{zzyxumb`lvl]cqobZ]begfc^XRNMSZ^[RICABDFGFEGO\fcX`vyoo|~~}~{ysory|sjfegijjhhkt|u{s{st|wwxyz}~zpd_epm]Zjri`_ekoppke]UPPTWTK?62259=@BFO]lndaq{rgky~{po|}pedhmopplhd`afovvpha^^`bddcekv}rs~ws}os}tomlklnrw~xpkkpvz}~}yphhr|vfguxmb`cgiihc^YTSW^cc]UNKJLMOOORZfqqgi{z~}zvx{}}zvqlhglswwpha\[\]___bit~|rr~}~{njvwjm{~wrpqqrv{xrprvxyxuqle[RQ[e^OQ`f^TSX]`a_YRKD@BEHE>5/-/49?DHOZhrodev~tlq~suxmjlprsroligjpx}}xqkihjkmmns|~tvwnt{smjiijlpuzzvnfbdhmqstsqmf_[bnobX_ji]TRTVWWTPLHGJPWZWQJECCEGIKOYgtztsͽwvria_^_bdgbUP\gbUQ[de_XVVXZ`gpzrkgd_YQH@:79>AA;4,'%%&(+/8H_uzgN}TmھҠ{yvlc\WTSTW[_`^XSQSYaiotwyyxuv}uj_TKECB>7,"#',3?M[a__qydW[|´ʹ»xohc``befe`ZVW]dlsx|}~}yx}~}}~̴l_[ct~tj_RJP^bXPVbf_WQMKIIJLNOMF=40037<>@A@?<:>KY[RQ_orlilqx~źƴ}|lfklf^WSQPSVY]``\UMHIKNRTUUSPLHIS_aWQZinhbbglquxyz|Ǿƻxd\cljel{{ocZRMKKLMMHA93/013330+&$( *31+*.4:>AAAABFMTZ\ZWVW[aiqyŴxmbZVVX\_bb`\WPKKRXP@:BJG=41358:;<>AGQ[elnnnnquz}zpuwmdaagpy~~wlaYRMHE@;60)"!'13( +0+"$+39<;988:>CIOWcq{{z|xyznaXTX\UG?ENNF=87888753359@FIIFA?>?BEINVamttnq¾żſ~}~wojgeeghhfb^_b_Q>5:@<2)$###" !*5BMSPJO]jib`ht}{~}ygVJB<730,))+06;=;85211258=DMUVQLQalkddlwſ{qjgedegikkklrxvj^^jvxtokgd]WPG?9778984.($! !"%)08?@:5:HSRLKR]gnty~ýż~uoov}~}tpqty~~{xusqmf^SI@:632249?EE>68EOLD@EMU[^aceedb`_`emu|~shaac`THIUbfdaaaa_\YVUWZagjid[RJC?<::416?HPW]chlnnmlmqw}Ŀwh]XX[^adfikpu|}ug\]fh`UQTZ_bcccba]YURRV\bgjkklnpstsojjt~x|}qhb[SNIGGKR[dkmlhecceiov~|tw}xj^Y\_abaa`^[XROMNSY_cdeffhjlmjeadp{{spxwplkllljgeefikoqu|}wrlf^WOIFGJNRSSRQQRSTSOIFMZa\TWbmstttvx||wsqojcZQIDBCFLS]iu{yqnv~y}{vtsrokgc`_`bba\Y]jusjektywsoljknrw}}xsonorvtmhoztnjgc]TKFLUUJACLSTQNLLLOSX\_`^[XWY^cinqsutsv}vielrmc_eoyzmeflmbVTZab_[XVVWZ^addb^ZYY]bgknpqqpqw}plmnnnmnopppooqv}}wg`bghc]XUSRSUWXWSMFA>>?ABCCA?=:878>EMU[_cgnu|}xutuxyyxusrrtvwwtrwwnhd_XRMIFFIMQTTTTUY^dks}~}{yxxwtngdktumfhqxzyvuuvx|~}~|zyuplgdbbcca]VPKGEFGKPXblrqpv{tlgktvoffnuwusqpqsvz~}ywy|~|{|}~}xria[UQMJIKNSTOHJUbebbityrrz~wmjovywtqonnprtvurmgc`aceghhhfeehq{zuy~zwvuwyyxsnifcbaaabeggaULMSTKCBHR[dmv{xvuvvvtqliikouz~~}~~~ynlqunc\[_ceghjlmnoprw{yxxyywrlgb`acegiihfdcemuwpjmw~|wsrtvxyyy{}~wyvkffhihfc`^[WSPORYajrx~{tpnnopppomjgfjrxumiox|wrooprttuuvy~||trtvwurnje_XQKGGIMQUWZ\`elrwzz|~wromligc_[XX]ejf^^hqsniffgggfdbbcgkorqponoruy}xx{}xsonnmliea\YXY[\\Z\fwyw|}tmhfeeddccdglrwyywuttuwy|~}~~}yuplifd_YQMPZ_\VYcow{~xv{}unjhhhiihghkouxzywtrqqrsux}~}zwtqlcZRRY[TJHNV[\\]`ekrz}xusqqomkjlosvwurnkihhikmqw||vpq{xuw}|{|~{tleekmeZUX]^[WTRQSVZ_dghhhilqx|wqlihikmmliffegilosx~wppx}wolpw~|yvy|zskc]XUSSSTUTSRTY`is||xvtttux|}x|}wvz}{vpjeb_^]\ZWRLFCBDHMQUY\_afo|~~xtz{tty}}{z{z}{wsqpponlg`XQLJIJJKJJIGGJR]eebfsyw~~toptxz{{{{zxuqoorw|}}|zuof]VQNLJIGD@<97:AIKFCJW`a_`chmqvy|zwz~}wrooprssrolgb`bff^RNSXVOIGHJMQTX]cku~|}~|xwwxyxwuqlgdeikeYSV[ZRJDBBBCCCEHMT]fmruwz~xuy~vv}~~|yxy{|}}|yvropuyukcejjcZSOLJHEA?=>AEJMNNMMORW\bjs}}~|ztpmmnopqpomjikqyztmnv{xpida^[VOJEA@@BBA><99:=AFLT]gopnmvzwvwz~{tqolie`]ZYZ\\[XSMHDBAAADHNTVSOTalnlnw{xvwy|}~~}{xwx~xy}wuuutsqommnpssqmgb]XURPOOQTUPHCGPTPKLR[cjpv{zwvwyz{|{zwttw~xty~xtsstsspoooruwwuqlgb_\ZXWWYZXQGCGMJA;;?DHKNQTWY[]`elvyurrrssrqpmlmr{|uu}{wvuuspmjiiknqrqnkihhijklortpg\Y\^WKCBDGIKLNPRTTTUX]elsy}½{yxxvtsqqsvy|}{xtqonnnopswzxqjjqtofaadffeca_\XTOLLNRX]`dgjnsx}}zyzzzzyxy{{yxwwxy|yrqx|wnhhkmmljgea\VQKIIJMOPQPPQSVY[ZYZanxyw{}zxxwwvuttvy}~{xtrqppprtx|ztllsxtlggjmonmkifb]WSPPQTUVUSRQQRRROKJOZbb^`kv~}yurqqswz{zywvvwy{|~ut{}wnjlptxz|}~}{xtpmlmmlie`\XVTSPLGEJTYVQS\dijjklosyż~{yyxyz}|}~zvuvxz{zxuspnlie^UQTYXPHHMRSRPOPRV[agmqtuwz}{{~}yusstxzzzxvtrqomg`YW[_ZPJKPROKGECCDGJNPQQPPSW^emsy~zyytvz}}zvtsux|~|wplntumdadgfa\VRNKJIIHEA=8558>@CFJLNPQSW`krros~}z~}uuz~}zuqomlkkkjgb[UQNMNNMLJHEBBELRQLKS^ccbdhnuzyvy{srw||zvrponnopomhc]YVUUTTROLHDAAFJG?:>EIGCABFJNRVZ_fnw}zyxxxwtpjc^ZYXXXWVTQMKKPUVOHHNROHC@?>>=<<=?DKSZ`dhlqw~}xrmjjklnooomkhhjormc^aec[SMIGEC@=;;<@DIKMMMNPTY^elu|ywwxyyyxvrollptskcchkf_YUSRPNKIGFGJMOOMJHGGHJMRW^gnpnox}yvuuvwwwvspmkmruridgmmg`[XWVTQNKIIJKMMKGB>;9889;?DJLJGIS^a__enx}wtsstuvvusrpqu|{trx}|vojgeb_[VSPOOQRRPMJGFEEEFHJNQOIBAHNKFDHPX`gnv|{vtrqnjgdbbcdec`[VQNKIHGGILNLE?AHMJDBFLRVZ^behkmorv~ü|yxxwvusqqrtvwwtpkfc`^\[[\^_\TLLQTOGCCGJLMNNOPOOOORW_gntz~~zvuttsqpnnnqsutrnjeb`^\[[\_`^VNMRUQHCCEHIIHHGFDB@?@DJPV\`dinsy~~{yvspopruwxxusrppqqqrsuuqh_]aa[QLLORTUUUVUSPMJIJLNPPOOOPRUY[]]ajy{wtqpooopsvxunikqsnfcdgijigfc`]XSPNOQSTUTSSSSUVUSPQXbfdbhr{~~wtw|{tmkmoppomkhd`[VRQRSUTSQOMLKJIF@<=DJJDBHQX[\^aejpw~}}}~zssx|xqlmorsstsqolhd`^_acdedb`^]\[XSMIKPRLEDINPOMLLNPTY^bfghilpv}{trux{}~~|yusqrsrqnjgc`^\YVPKLRWUOKOTWVSPNLLLMPRSSRQQTX_fmu{¿}zyy{~~}{yxvuspic_bfe]VUY[ZWSOMLLLNOPPNKIHJMRV[_begjnuÿ}{{}}{yxwurlfachhaYWZ^]YUQNLKJJKKIEA=:9:<>@BCDDEGLU]`^^fqxzz|{vw|~xplnqqnid`\ZXVUTQMGA<999:;<<<<;=>?BDC>99@FD@>BGNSW[_bfikmpt{¼xspmjgd`^\\^`bba^[YWVUTSSTUUQHA?CE@968AEIKNOQSUY\_``_dnwzxy~zvrpnlkkkmnle^]ad`XTSUWWWWVTROLHEDEHKMOOPPQRTVVUSSXafebemuz|}»|rlmpmf^\^`bccccb`]ZVSRRSSRPLJGEDDCA>B>9972/,'""##$$#%(*-/0/3>IQST]ju}}y}}zyzzzyxvuuvwxxvsqpsyժdoƐNmwncYVUOB6/-+'  + +  (1<<>>>?@?>>=@FMPMJMU\]\[]`eimrw|¾wcI1'@\s~}q^J8+(267ACEIQY``^ahpsrqqtvy|ƹvomqz²wmcZSMIECA?<71+&"!   "#&(,18?FLPTW[`flt{ug[SMKMS]jzpbYSJ=0*++' $).5:753234441.*'$"! !"%('#&+*&%).49=AFJMPRTX\cks{ĿviZOGCCGNWcr{qhaZRG;1//+# !"""!  "&(*,,-.0369;<>DOZ_`eoz|ri_WPG=0&$)/26?O`r}tle_YSLFA=;9740-,079636=CGHIJLPSX]aehiiiknrw|vmd]WPI@7.&!#(/;Ncu|xtojd_[YZ[\]^__^]]`eiidaekmkgeeefggghijmqtwxwvuuvwy{~~wpic]WQLF@:40/18CRbsµ{vqnmmnnmlifdcba```acc_XRSWWRLJMPSVX[]_aaaabdhlorssssstuvusqsyzsnjfdcejr~|qhdca_\ZWTQNJFBAABDEFEDCCCCCB?<:GRZ_hvzoe[TNJGDB@=<:779>@>98=CCA>=>@ACEGJNRX_flpsvy|~wqibZQF;0& %,5?KWevytpnmmlkheb_]\\\]_bfhiebdkonkjlquxz{}~~~~wmhfd^TJC=84/,*+-2;ER^jwǿ}xtqnnoopppnmlkkjhea_`ehe`_cgijihhhjknprssrqppqtvxz{{{zz{~|zxwvtog_[\]YSPRX_hq{Ľwponlhb^YVTSRRRPNKHFEFGIKMNOONNPTY[XTTY]]ZWVWXY[\^`chntz~pxuywwy|~~·sijYGC>4-.+'&'%#""#(./-+/5?BEJOTY]chow~xrnkiecb`_]YWUSRTW_iw~vEQs>Lbglqwwrmlnqqopqsxǝ\:~[-?Xm|½zrmifd`YPJIF@5+%##%')-161)(+2;EPZagnu}ziZPMUiѿ}rkif^RF?70,+09ENSQJB:3,$  %-22039@CBAABDGILORUZ_fkpsvx|ƽn_SHBCKXlƳwi\PB3(" + + $WУkJ.&$(03DWYbt|{zy{}~sjdbcfkptvtofZL>1& &2@Rfx}lFNwQ5FUcnyŹÑrsofYSPSZes{hTB6219CNYfnrsrqsxiVrÞrnpsuvvusqnlhfggghjmqttqh]RKKP[k{omyljoyymZC-!*Ffuvqprsoe[UXezzyzvurpoopqsuwz~μskfb^[WSPOUe}maWOJGEEDDC@<70(!%,0110+''/=KUY[`gq~|~lXF83:Moukd`_`accc`\VPJGGJLNOMLKMQX`iqy}vngehp{ƿvgYLC>?HXn|phb__adghf^P<#  =R\bege_YSOLJJKMQV\bjqx|}{yyxxwwxy{}uicb^SE?EUj{vrpqv|s\F4& !@`}pcVMOURG>::=BHPW]``^\ZY[_bgkpuy~{ttz~vhZON`Ļwlc^]ajt~yqha\WTRQRSTUUUTRPPRV\chlllklmqstqnsÿrbQC835?Pf{m]QKIHJLNQTVUSOLLR]b]PC:6568:;<=>?@BCEGHKMPRVZ_dkrz|w{ytnhedgnzijwkdcfkpsuwwxz}|wz}|zwsj^PGEHE8%0Rm}zyz}{wsqonnmljigdb_^^_^\TJDQs|tlcZQG>5-'%&+5@MXbhjie`[VSQQSW^dije`blpiceo{|zz{{zui][m~fQC9437AQe|yohb^\[[\]^\WN@2+/63+$""$&).5>HS]cedjxwk`UKDABGQ]juz{xwywgZc}{z{|~|zvssv~Ǹ|vqlf_XPHA:546;BFD70)"!.AWjz~}{zxvtromjigghjmqstpg^]nzsmheb_]YURXfw~}~|~zqopl`WSRTSM@0%)8;FSZ_fs|yvrolihggjnruuphci|}zy{||zvpi`WMD;4/16ARew{xurpnljheb`]\]^aba\WW]`[TPRW^ekrx~~ysniea_]]]]\WPJK^}yyyttv{|xsnhb]XVX]hw}zxnfi{|wrnjgdb_]\ZYXWVVUUUUTTTTTTTSSSRRRRRSUX[^`_[WW^c]ODAIWaeflu{vpkgda_][XURPPQUZahlmican~|wlc_]]]]\ZYWUROLIFEEGMYi|Ľzy~tlgfghjmpuz~w{|{{}~{ywuuuuuuspmlr~ump{|xtoje_YSMG@:3-(##(,/2:Ou̿~xtqnkgc_[WSQONMMMMNOOPRTVY]afknnjffnvvqmmquy|~|skhkqpjeejr{~}~vt{tsvzxoga_`bdgilorrpjb`djh`Z[bo~ytoic]YWVX[_bcccgkjb[cyztoje`[VQLHC?<9777:=BIPX_gntz~{y~zx{|zvsokgb]YTPMKIIJMPTVUOE:7D[mndZUUX\`bcba`adly}}}uj_WUVTLA;;?GR_kw½~|zyxwvvuuttstuwz~}xvwxzwofbdgloqrqpnljgda^[WSNIFDEKUdt~zwutstuwy|~}yqe[\m}{ywurpnkihfdb`_][YWVTRQPPPQRRQMFABLUWVX_iu|wvwxxxuspmjihhjlqv|wno~{upkgc_[WSPMJHHJMRYco}zvsokfb_[YWWX[`fnuxvl\KGXtqb[\clu~}yuqmjfdbaabbbccdfikmlhbahquqnotzzx|{rlhghhgfda^[WSOKGC>:50,&!#2E[o{}}vpopruwyz{{|||}}|~~|yvsqommmoquy}xrsx{yusuy~~xrvzvrnjfb^ZWSPMKIGFEEFILPTWXTLDBLavztmhb]YURONNOQUY^cggc[ROYo~z{ukea^\[YXWWXZ\`djpuz~zz~wstwz~~}|{{{||~}vokmppi`VPMKIHGFDDEFIMQUWVQIABQgwyqgbbfmx{wsnid_YTNIEA=;::<@ELU`lx~{xvtrpnljhgeca_][YXWVVWXYXVQJDDIMKE@>AEJOTXZ[ZVPJJQ\gpw}zwutsux}zqoyyqmnqsnf_YWWWXXYZ[^bgmsy}~|xtplieb`^]]]^__^[YZalvyumc[Xatzy{~|yvspmjgdbbdiq}~{xtqmjfb^ZURNJGDBAABDFJMPQMG>7;J_ornklr|~}{zxvsqomkjjklnqtuurmiinsrlfccdeffedb_\YVSPMKIGFFFGGHIHD?821477548CRdvþ~~|ywz~sjhmstpkiijmoprtvy||wqlgb^\Z[\`dhmqsrmgbet|ztoje`\XTPMJHFEEDCBBA@?>?@CHQ\jx~|zxvtqokheb_^\\]^bejotutogabmzxy}}zwtrpoprux~}{yvtqnjgc_[WRMID?951.+**+.26:?ACDGJJE>=>@CGKORRNIEHVl}wpkjmqrpkfefjnqsrmifinokd]ZZ[\]]]\[YWVUTSRQPOMKIFC?<:8:=DMXblty|~øÿ~{xvsqpnnmmmlic\WW[aa^\]bhotwyz}{vpkfa]ZWTRQQQSVY]befd^VONXix}ypheehlptwyzzzyyxwvtrnib[WWZ^^ZTNKJJJJKLOT[fr|yvsqponmkie`ZVV\djlllnpqojd`eryuvx|vpryyqkhhknqsttsqolifb^[WSOKGB?;730-*'%$#"#$%(,/49>DHLNOPS^r½¾}xy}}wpkiijjihfca^[YWUTTTVWXXVTQORY`ffda`acgjnrvz~xqq|~zuqmiea^[XVUTTSQOLGA==AHORRQQQQPOQX`ca^^cly|ywvvwy|~{xusqppqrsvx{~~wrqsurkc\WTSRQOMKHDA=951.,*(()+.27<@BB>70-3CWdgb\WWY^dinrw|»|xuqmiea\XSOJFA=:64322468:;CIMNLJJLNRTWXYXWUSRRTX_gqzľ}zvrnkhfdccdefgeb_]`gpttrrv{~|zwtpkd\UPPQRPJC><<:850*# "!  %+29?CC@;9;:989:<@DIOW_gpx~|}|xtpmieb_\YWUTSRSSRPMHCABFJJGCAACFHJLMMMNNPSX^elrw|}|}|vohddfiihdbbdinswyz{~|xtplhd`\WSOJFB?<97544469=BHMRUTPG<42:J[dcZQJHKPW_gntz~}}}~{yvrnkgc_\XURPNMMMNQSWZ]``]XRNOXhx~~|yxvutsrqpmhb\XYZ[YSMHEDCCCBA@><:8653210/....-----../1258:742/.,,,-/148;>@@=81+).;JUYXSPPSYajrz½}{xvtrolhb[TMHGGIHEA<99;?ELSY_dffd`]_ix}{ywusplgb^]^bec]TJDCFIIEA?>>@ACDEEEEEEDDCBA@><97421126;BKT\dkptx{ľ|{yxwvtqlfa^_cffc_]^`ejpu{~~|vpligedca`_]\[[[[]^acfghfb\SKHKUblnjbZURRUX\_cfhjlmmnnopqrtuwyyxvrlgddfhihffioy}yurolihgfghjmpsvwwvtrqolf`^cmw|zupmmptzztqpswwrj`XQOPTWYVRKEA=;:876531/-*(&%#""!!"#$&)+/26:?CGKOQRRSWbsxqmjihgfdb_]ZXUSQOMLLLMNPRUWZ[\[YWWZ_gnrtuvx|yvy}|}~zwsokhd`]YVSQOMLLLNPRUX[]^^\YWUUVXYZXURRW_kwzxvuutssrpomkhd`YQJC@?@@=70)$  "%'+/37<@CEDA<759DTcmokfb__aejpv~¿~zvqmhd_[VRNJFB?;86433347:?ELT]fov{}zuoko{{wsojfc_\XVSRPOONLJE@;89>CFECA@@AAA@BDIORROJE@>>@CIPX`iqx}ü}|{zyyxyyz{{{yvrpqu{~{zz{~~{xtqmifb_\ZWVTSQPNKGB><=BHOSUUSPMIC>:;CO\egc\VRPRTY^bglptwy|~~|zxvutux||xtplieb_][YYXYZ[^`cfhhgc]VPOT`nz~|{}}{xtnf`[Z[[YTMFA><;;:986543211110/-*&#!!$(-/.,)''(*/4FLMIC<8:BP^innkijnv|yvtrqomkheb_[XUQNKHFDBA@@@ACFILQV\biov{~~{wuv~~}zxuspmifc`][YXWVVVUTQMIEEGKOOMIFDDDEFFFFFFGILQW^fnv|}yvvx}~|zxuuw{{yz~|xtplhc_[WSOKGC@=:754323357:=?BCCA>9559BMUWTOJIKPVZ^adhlqv{{tmgb_][ZYXXWVVWWYZ]_beghhea\XVXanz~xtrqqqrqokf`ZWWXXVPJD@=;;:::987654322100/..-,*)('&'(+/5>HT`my¾~zuqnoruupia[XX[agjkkigffgikmopqrrrrqpomljigfeddddfgikmoppnke]VQPU^hnnjc]XVVX\`ejnqstspnkjlorttrolihhijlmnooonnmmnqty{xxz~{usu}}{{}~}yskc\XVVVVVUTROLIEA>:62.+'$!#(-39?EJMOOMIFDHSdw¿xrnkjiihhggfdc`]YSNIFFHKMNLIFEEGJNRX]ciov|~|zxwutrokf`ZUQOQSVWVTRQQRTUVUTTVZ_cb^YUSSTW\ahov}~}|}}~|xvuvxywsmf_YTPMKIFDB?=:7520.-,+,,-/258;=??>:50-.5@NX^^\XVVX[`flrx}|xtokgc_\YVTRQPONMLIGDBCFMU]dhjkjhfcaaeo~~zvrnkigffffffea[RI@;ACEFFFFECA?=<:::CMZfnpnid`^_aels{Ļ}xtokfb^ZURNKHECA?>>>?@CFJNSY_ekptuutssv~{wrnjgca^\ZYXVTQMHB><;;=BGLORUX\`ejnsvy|~ſ}{zz|~}zxy{|zxyz{|}~~}|{ywtqnkhda]ZWTPNKIGFEEEGHKMOQRRQPMKIGEB><=BJQUVSPMKLNRW]cintx}}zwtpnkigeddddefhjlnoomie_YVX^ht|~{yy{ztmf_[YYXVQJC=853210.-,*)'&%$$$%&')+-/000/-+*+.4;AHMSYajt~ÿ¿}{{zyvpic][ZZ\^`bcdeeeeedddcccbbbaaaaaaabccdfgilnqtvxyywsoidbelvypga]^aflpqqpnmlmmnopqqrqqqppoonnmmmllllllllllllkihgjov~}vpkgdb`]ZWTPMID@<840,(%!#)/6>EKORRPKFBBFP]jswvsonqw~ytrqqrrqmhb\WROMLKJJJJIJJKLMNQSVZ^bglrw|}ywy~ztnhc`^\[ZZZZZ[\\]\ZXTQNMOSVWUQMJHFFFFFGHIKNQUZ_ejnruxy{|}~}yz~}yvvvxyz{{{yxuspliea]YURNJGCA>;:8776789;=?@@@>:50,+.6AMW]^][XVVVWZ^dlt|yslgdccdca]YTQOOQSW[`dhloppmjea`ckv|zz{||zvoh_WPLJKMONLGB>;:99::::::9987665321/.,+)(((*,06=ENYdozÿÿ~xrnkloqrpmjhfffffecbbcfimnnlifc`___`abcddeefghijlnpqstutqlf_WRPSZbilje^WRONNPSVZ^bfjnqtwyzzywtrqrtw{||yuqmjhhikotzumijq{~}~{xusplhda]XSNIEB@><;97531/-*(%#"  !#&)-26;@DILNONKGB?>CN\m}}vplifeca`^\[YXWVUUUTSQOLIFDDFJOUY]_`adglrx~||~|yvtqoljhgfeddcba`^[YXXZ^bfgea[VSSTVVTQOMMPTY`gnu|}|{{{|}~|ywwwxxwvsnib]XUTSSQMHA:4/+)''''()+-/258<::999999::<=@DHNU\cjqx~»zurqruwvtokgeeefgijjkkkjjjihhhgggfeda^ZVRONORVZ]_`_^\[ZYWSNIDCEKT^fihc]WRPOPSVZ^bgknruwz|}~~}|{yxwwwy|}zxvtrqpppqrsuwxzzywsniecfmxzusuz{slfa__^\WPIC>:8878888753/+&!!$&&%$"!"#&*/49?DIOTY^behjkmot|~wqlhfdcba`_^\[YWVTSQPPONNNNOPQSUX[_chmsy~~vojimu|tnjijlosvy|~~}{ywusqomkigfdcbbaabbcdefggec`\XWX\bhmoppqrtw{{upligec`]ZWSOKGC?<8530.-+**)(&$"  $*19?CFFDA>:768>HUakqqolheefjovû~|zxurnkgd`\YVSPNLJIIIIJLNPTX\`dhlnponkighnw|xuqnkigedb`^[WRMHEDEGIIHEA>;;;<=>@ABCCDDEFGGHIKNSYaipvyzzyxyz}¾ytqqtx||zxwxxyzz{zzxwurpmjgda^ZWTQNLIGFDCCCDEGJLORTVVTPKD<645;EPX]_]XSNLKMQV\ciouz}yvtrqponmmlllmmnpqstvvvtqmhb^\_enw~{yxy|}wqjea_^][VPJC=9643210/.,+*)('&&%%&&&')*,.02479DIMQRRQONMOT[dkpsuw{}{yxwtrnid_[XVVWWWUQMIFDCCEGJMQVZ_ejpuz{yy}ytokiiiiigd`\YWVWXYZZYWUTSUX[]][WSNKHGGGHJLORVZ_cglortvwwwwwxxy{~xtrrtvxxwtplhda_][ZXVTROMJHECA?=<;;;;<>?ACDEDC@<71-,/6@KT[^]\ZZZ]`ejoswy{{|}}{xvtromjfb]YVSSTX\aegiijjjjihgebachp{~zupjd_ZVSQPPOLHC<61-,-.1368:;<===<<;:865321001259>EMU^hq{½¾ytpnnopolida^^^`cehjlmlkhfb`^^_bdffeb_\ZYYY[^adhloruvwvsoib[VSTY`glnlg`ZTPNMNORUX[_bfilorvy|}yurpopsvy|~~voklqz{wttvz~zyz{{ytlcZQJEAAABCCA>93.)%" !#$&(*-047;?CGKNQRRQNJFCACJUdtýztnjgeca_][YVTRPOMLLLLMNOQRTUVVVVWY]cjrzztokknv~|zwtroljhecbaaaabdgilnpqpmhb[UQQTY]`_ZUNJHHLQY`iqx~~~|wrnkjiiiiihhfeb_[UOIB=854455530-*(''),059>BEGGEC?<;@AA?<8520025:AHPYajrz¾~{zz|~~}zvqmjihhhiiiiihgfdca_^\[ZYXXXXYZ[[\\\[ZYXXY[^aba]VNE?<=BKT[_`^[XUSTUW[_chlptx{}~|{yyyy{~~|zyxxxxyz{|}}}|yupjd`_agpzwpidb`_\XRKC<730.,+++*****+++++*)'%#""#'+16:=?@ABDGKPU[`dhlosy~}{yuqlhebaaaaabaa`_^][ZYXWVUUUUUUVWYZ\_beilptx|~zvrqrv{}zwuromjihhikmoqsuvwxxxwwvutsrqponnmmmmmnnnnmkhd_[WUVZ_dimqsvz~}vpkgda_\YVSOLHD@<730,)&$!  "$'*.269?CKVamv}»~ytqnmllkkjigeb_[VQMJHHILOQSTUTUUWY]bgmrw||zxy}}{xtplhd`^^`bfhjjie`ZTPMLNQTWWURNKIGGHILORVZ^cgkoruwyyzzzyyyyz{}º}}~~yuqnjhfeefghhfc_ZTOIEB?=<<;;<<=>?ABDFHIJKKIGD?:6337=FPX^bb`][YXY[^bglqv{{wtpmjhecba```aabbcbbbbcehlptvvtojeabgoy}|{zyxwtqlf^WPJFDCDDDB@>:7420..//1346788888877778:<@DJPX`hqz¿~yussstsplgb^[ZYZ[]^`bdeghiiihgda^[XVVXZ^beghhgfffghiihfc_ZURQTYahnrqnic]XTSRSUWZ]adgjmortvxyz|}~~~|{zyz|zwuux}ysolkkkigb\WQMJGDA=951-*)()*++*(%" $(,17@ABBA@?=;9767:AKVajoqpmieb`_`bejou{~zwsokgd`]ZXUSRRQQRTUX[^adgjlmmljihhkqy}zwuspmjfa[UOJGEEGHHGEB?=;;<=?@AA@><841/./26:?DHKOSW\ahov}¾¾zwvvwz}~}zwspmllkllllkkihgeca_][YWVUTSSSSTVXZ\_bdfgfda\UNGA>=@FNW^cec_YTPMLMPSX]chmrvz}~}}~{vqkfbachow~~}~wqmjhgd`[TME?:620.-+*)(''&&&&'()*,-/0122211236;@GNUZ^accddegls~xoha\XWVVWYZ[\]]]]]]]\\\[[\\]]^`acfhknpsvxyzzxvrnkhgjov~zrlgdcdfjnqtuutsqponnoopqrsttuuuuuvvvvvvutsqnjfa^[[\`dimoqstvx{~}ytojfc`^\ZWTRNKGC@<840-*'%#! !"$'+/38=BGKNOPOLHDA?AFNYcmsvvuronnqv|zuqnmllllkifb]YUQNLKKLMOQTWZ]aeilptwz{||{xurqprv}}ytpkhecbbbbbba_\YVSRRTUVVTQMIDA><;;::;;<=?ADGKPUZ`fkqv{¿~zxwxz|}}|zwtpmjhfeeeeeeeddcb`^][YWUSQPNMMLLLLMMNNNMKIEA<84348?HQZaeffdb_]\[[\^aekpv|{uqmjiijlnpqqpponnoppppomkhffhmt|{wpib[VSQOLIEA<9654445667776654321/.-,++**+,.15:@GOW`ir{ÿ|ywwxyxwtplifdba_^\ZYXXZ[^`bbb`^\ZXWXYZ]`cgjnrvy{}~~}zwqlfa^^`djptvuqlfa\YWVVXZ]`dhlosvxz{{zywusrrsuwyz{zxvsqpoopqrstvx{}xrlfa]ZXVTQNJEA<9530/-,++***)**+,-/1369FP\gpx}ľ~wrmjgedba`_^]\[[ZYXWVTRPNLKLNRW]dioruwxxwvtromllns{}{yxvutssrrrrrqpomkifca_]\[[[[[ZXTNIC>;9:;86668:=@CGILMMLJGD?<;<@FNW_ehhfb^[XVVVX[^afjou{þ|xtpliec`^\[ZZ[]_behloruvwvsokea]]_dmw}{ywurokgb]YUSRSTTROJC<50.-.158;<<;9630-,+*+,.037<==>?@ACFHKORVZ^cgkosw{}ysnjhgghjlmmljgda^[XWUTRRQQPPPPQQQRRSSSTTSRPMJFA=:88;?EMTY]```_^^^`bdgilnpqrstvx{~}{zywutqomkihghjlosvxyxvsojfb`adiqyxpib\XUUUVVTQLF?94/-+*+,-.0123444444433445679<@DINT[bhov|ÿ¿}ywutsrolieb_]]]]^`abccba`^][ZZ[\^aceghiiiijjlmoqstttsqnkgecdfinrvxxwsojfb_][[[\^_adfhkmprtvxyz{{{zxvtrqppqruwz}~~}||}|yxz~~|xtngaZSNIFCAA@@?>=:730,)'%$$%&(*,/259<<<=@DJQW]bdedca_^]]]_`cfimqv{}zvspmjgdba_^]]]^_`acegiklmmlkjihhilpu{{xtoje`\XTRQPPONLIFC@=:8643100001234554444579<:987666789:<=>?@AA@?>=<<<>AEJPV[`cefffggiknrw|¿~|zxuqnkgda_][ZYXXXYYZ[\]_`bdfhjlmnnnnnnnoqtw||zwutrpnligda]ZVTQPNMLKIGDA?<:987777899;<>@CEILPTX\aejnrvz}{zz{|||zxvsqnljiiiiiijjjjjiihgffedcba`__^^]]]\\\[ZZXWUSQPNMMNPSVZ]_``_^][[[[\^acfhklnopqstuwy{}~|{zyxxxxxwwwvutsrqqqsux||wrnjgeb`^[WTPMJGDB@><;98765433221111234568:=@CFJNRVZ^behkmpsw{|xtpmjgecbbbccccccba``___``acdfgikmoprtuvwwvvusrponmnoqtvxyzyxvtrqponnnnoopppppoonnnnoopqqqqqpponmmmlllllllllllmnpsuxz|~~}|zxvsolhea^[XVTRPNMKIHFDCB@?>==<<<<==>?@ABCDEEFGGFFGHILOTY^chlortvxz|~|zywvtsqomkigedcbbbcdefhijkllmnoppqqrrrsstvy|~|{ywusqomljihgfecb`^\ZXWVUTTSQPONMMLMMNOQRTVX[]`behknqsvy{~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m30.bin b/applications/SprPoP/assets/packed/MUS/m30.bin new file mode 100644 index 0000000..744fa8d --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m30.bin @@ -0,0 +1 @@ +~}~ywyz{|z}~z{|{zw}x|x|y~{zzw|yzy~~}z|x~y~v~x{z|}|z~}z{xy|zy~}y|x~zw{y{{~yy{}}~ywyz{{z}}y{{{yx|x~|w|y{zzx}z{z~~~|~zz~xy|{~}||}~zzxy{yx|{xzu{w}~uywzy~{x~~~y{|~~zy{|~}}}}~{z{{xv|vxw{y|ww~zy{wtws|wryzs||v}u{yz{{{}{}~~~{||}{wu~zuvxy|}t}|sw}}tyzy|ptqszqqy{ts|uwzs}~zxz}~|~{yxuv}uvtz}v~vs}vqv{wryuxvnrluvnqxwquyqyvt}}z{}y}}}}|{~y}wrxzttr{yy{ov{rpuzssvsyqnmlxqlryupwtszqv|w}zz|{}{~w|{tq{xtqs}xztn{xnpwzqtsvymnjrxmluzsqwrxxoz{vw}~~}{vy}szplxyqqqxxwvyknox~nxouqrshudlw|rwmt|hswxx{z~}|~s|~}qjo~spnyuxuythppupvpwnulpjgp}xsul{tg{zvyt||{}~zu}vvuit}opo|twtzmjotntqstnth{|djutvrq~mnxx~~x}}}}sx~spn|yqqvzzx{{lqr~~qyrzstqmsfmz|quluxfr|susy{x}y}z|u{~ypzim{omkv{rtouoell|qppltlrgpigoyvvown~~{}|~{}~yvyx{zryvvu}yutvzw}sxzvwk}llstullxjlxsrvvu|vv{yu|yz{~~yoz{mnszsokoxrxnkogkitrpueooqhfzmjjt}yzpz|t}|~x}~}}}{z{vx}x||u{yzzzww}ow{~lyptpwxyohsjnxv}xsr~utsl{~szzv|~y~~vp{vqwqospttixvnlkjcvtkfcl{o]ygiynuvowk|~z}v|uu{tw}y}vyy~s~x~w{~xv}{vv{~qi}pw~ku{xq~rbzngxuwynt{njp||{}y|v{~~|y}rxnsx|oksl|qp|hlmnjfwg}z]frsdrr\qto}npwx}{{{}qyrx{~wvwu}x}|{v~|vvrvuvlpsnlmxpx}y`kudzynxzvw|qxrqwx|ww}}y}u~tkyx{uonu{vpborlnsft~hern]rvbjhnxsh|ry~{x~||zx{z|xyxzyz{{yu~y~yu~r{}~p~n~pnqslsyehjfovwsrmtqwtnx{|s~t}~}}v~suyurhn{|rhxtuolrlnqns~ezwpefmokmzuu}m}nyz}{x~}}yy~||vvkx~wwtvyp}w}}~ox~v|yprtq{rlxfhoiun|pslvruvt{~uyozykw~sdlyvphlqqrlkhcdnusklmsohcgyzfhy{{|}{~}|~~~xy}}px{{xl{tpwz~y|{rnq{|xpt}|xrqy{gu}zsnqxiz~zsqty{xwsz~{z~zt}lt|ych{trikprrjpidclutohotojdgtifz~zw}~{~z|wz|ztryl~{wnux|zy~tpoy|xsq{}yuqwkp}~|tpowmr}t|wq|y{uzuz|zy{}oo|xlduxrnhrpsqklffjtwnolprlfcq{qfp}{|z~~}{}yzwywxuyro{w}uyrst}zp{rz}wrqxpt~zmpsovwwwt{{x||wy}tw~u|yxkz|jqksyqlkxmzsakflmzqk}eoribg~kpkpu~|~}~uwx~q}zyzwzq}zsw|uqq{s{kzuytothz|un|qrw|ugwktmyy{{psryy|}p~wp~r}hu{zxpsv{xrzshlnjzrkynkikj`xibrnwy{~xwv~~||{}y~wwy|to}~z|tou{zyyyu}zoyyrzuw}x}qlvmmqszvvmxo{vex~p}|{ztz~|~xyiyvmzup}vo{zrqo}umv|dnxo`jqljjmzrq}t~x}yrz~x{|w{~s|}~~{{yw}|tyszqqrsvpwsyfsts|w~z{mopkyr}{{|yxnkz|vu~~|~}{wxupuhjrvoitpvzozlopwz{w|vltwivshwpw}}z}wt~zzqtov{{z~x}~||}uw}y{yxzxz~zvt~{r~ruvwqmzr{s{nnnokit{{wo}u~~r~yp|z}xhxwloiwyijqv{tlkrqllu|wevwllh~wom{}z}}uo{||}}zw~}}}~x}~|{iyx~yy{|tvy|x~}{w|yqy{wuqw|xumfmhmfu|quxv~xxrv~~}z|{xurx}io~}xqykrzoqqyufyk`hbwqck|v{~yov~m||~vxx~~{m}wpnyseqt{s|nr}}zvu{}y~utznshjpx}jvytxpvtsw~~uw}}v}|wry|skl~mzz}uxq{jlz}uvrlj_njgyln{}wyx~zvx~}wxu}y}}|ztw||y}wnqnrwsw~xzu|{|y~swtw}tyklxhjq}zitw||lrzz~~yy{v}}w}ptuqrtvx}uypoyrw|y{jiojfemxxfozz|ywpqw|~zzyz~~~xs{vyx~xn~tpiyy}ytyy}zs|u{}|yzourqlfrwrlmzr}uwvu{~}~{{yqquzsot}rr{surtpxuzzlpihgouitly|}y}}szzy}zusyz}z~~yywups~{roqruq{~w~uu|{}tpnlsljmqsj|w{km}}{y~tu}|pztmv{xovvw~vrnl~tv~vwenrkaqqowl~}|{~ox{~|zww{|~~}~stxwzpszvqopz~~{{v}|zvozz~xkvngolrnlpys|iv|zvt}|z|wk|puxputxq{}uqsrvt|qntecrsmt{w{zyxsqv~||}zxt{{z~}{|zsr{}uu}|wxlr{t~}|zwyz}yvzzvmkhquhmqrywu}~llv{xs||}}~xp|vixutupy~|||~pirv}zwhnnbqpqv|}x||zqtxu~x~~~}yn}|~}w}v}}l{{|zrvsww||}}}o|wy~znnnqlipsvmst}xn}yw{}wuuz{}uvunursvtvtyz}soqx|wgpnhjl}~|x~~x{ko{vz}~~wxs}}|qv~tuzxxzwruu}yt{yww|vtrpvghmvzjq{|zw|yu|yxo~}{~}vnsxwnk~wpvt}yvr}uw~xopmesxy}zz{sosyvu}|~}vw}x}y}}}zwv~{twv~vrvrr{z{{z~{{p|~{zuznnpjpqtrq|x|y}~~strvu~v}~vnwrytjryxov{z}zyvty|~|pqjoyw~x{{}x~}nspvzy}}~vy{}}|}~~}y~}w{vwx}|vttozzz~u|zw}y~ztruuldoxtqv}v}xsuyxyxvvz|y{}ttsnxqlvwussy}zr{~rpynvw|}y~{t}qivttwz}~|wy|~~~{z~}p~yvt~oopw}{}wy{uv}w|xyshlksrqz}~q|~xpw~y}|rtvx}{pnxpropyooszy}z{v~wtvpww{~zzz}{zskuos|vy~~soy{~|yw~~}~uy{}z{~xquwt|}ss{zq|~qtoloktvwz}uq}qtyx{v|}utvoz~nnriwunqqxvw|{tw|uwyjo|{y{~ysmmxkqzwxvqx{}{us|~{wx~{{~|}ruwx{~|vuxvpxzwrqlqvqtw{~x~ww~{pqu}~y~|ursry||~nhsqlvsuwptz~wt{rqmlw}{xqtopno{t~ywyp}~~}}rxwz{yq{ywtxux{osyu|~}utptuuus}yy~xuyupr{vywp|tx}}~nnrlnwzupsw|}|ys|yoprrz|}vzzxxnjrruxxy~|~{wsr{{~y~}wpt~{~x~~xws}ztuqu|~xqx~vu}wuzsuux{uu{u|}wqvuyx}{xwwwy}|}yoiqrwypxwy|u|}w~trwpu}{uux}{rnoyqn|}|}vurv{|z||vtvxy|z~tvw~xrmuz~zyw}qyz~z{urvuvwx||z|}~ynm}~}~{xxux}v}~|}pkqsyvpy}rsv}wwmr{zywszx{wnspsy~}|svutuzz}xyvvwxx~}z}z~~}vxx~qprv~z|~zuwy|ztqlt{}~~{urntsw|zusttwxxqnrrs|w{~srvw~uqqr{~vxuv||zmmwx{yw~wvvmu}~z|w}|vxsv|y}xy}zvyrtrvz|wv|~z|{|rkou}x}zx~||zutxutv|}ywlmux{~~voloy~tvxswu|~svpt~zvtx|~wsszzpsy|}trrpz}||xy{yrvx{~zyvx|vposv}|{~{sy{ttntwv{{~~~}x}|movwuv~roouyx~}}~rlpz{vwuyws{|z}qqrw|ywx{y}{{uurnxxz}~|xssozzv}{{wz{xyt{~xx{v~xonqw~wuxux}xvxtvvu{|}|v{}{rlquww}}|swspxz~}~~|~wqrusw{xxyy}~z~yqsry}tt}xtqtpq{{||~~wtsmvu}{yx~trt{}xtyywwpnrsu||qvxv~}}xwuwus}pu|~spnt{y{}{tvntyw~{~~nmrqv|~|z{|~tsuw~xuv}ytqusuz|}|{}smkszw~y{}}|smryzxuyx{xxsrmow{tt}z~}zuvutw{}|rvvpsu|{zy}tspoyz}~|{|nhssw~{~{z~uvpq}zyz|~{vwqyxu||~tnmmvtv|w|wrpq{vx|}xz|}{rlot}~|t{}~zzuqvwz{}{tu~~~wssz}}zyvvsqntx}~|xy{}sknt{yvw}{qmqxxry|~ysx|yw~~~~{trnonw{}yuxywqsz~x|}|~}zx{|rposxw~~|z~|xrtu|~}|xr}yzxru}~sux}uonrwz~wv|{~zxpls}xvx|rqpsurt{}~y{~yyyx}|~|ropltz|{}~vtv|xsxx|}yw}ywwzwpnov~~}~xytuwx~zwx{{|zrtzvtw}rqnuwt|}zvz}~ynrx|{{{z~zwpot}xutvy~{|}yru{}|{toppsxvzyxvvz|{xyzvvw|~xvy{ypkq|{y{|zxssv}~{vz~}|z~{sksyx{yzvrqossyzvx{~|}~yqv{|z~~yy}|qoq}wwr{~{{usu~}{~}~vsrrnpy}zxu|}{xxx{wyv}~}{yvy|~qrryzz~uxy}yrqtz~ur||qkoz~wxy|wqokrw{}ww{~yyxrwzy}}zz|~xqrv{~xyw{uyxxz|}}z}}{yolnx~y|{{||{qpx}vxv|}xty|zwqqtzvu}~|sptwxy}}xx~xqrtz{uzw{ulmlvvx||~zx}|y|zmouz}{{z}~{uqpz{yzzxz~yyxz|~y}xqortxuu}}}|yqow~|vxwyuywpkqx~y{}xusutswwx~xttszxv{xvoonnuxzux}{~{{zkkx|z|}|}{qoq{~z|}|{zvxx}zxw{{zwusorqu|~y|xvtv~~wyzz~~~y}{ztnnpy|z{|}zxtotw|tq}}}{rpsx{xzyyrnjox~yyz}~~}|wnpx}}~}||~~rswz|uy{{zvx|}yuyz}~}~zqojquu~{wu{}ytsz}yvpxyu{ytonu|wy}}{uqsy|z~}xpwzw}tqpu}yy}|vlmovzx||}~z}}~~~xqrx|~~~}}}}wtrx~yttzz~xxz}{yyzywnllptv{wtv}ytqwvqtv}vw}xppt{|y|~}wxtvvw|yws{}yx|{uorz}|{yy|qoorrv~wvy~pms||xvzomt~zwv||}}zx}{wyx~wpnpnqyz}xww}~yvuuxysusv}{{{~xuqv~~y~~{xqpv~ztw||~|tpr}www}uoknw|usx|~|yoov{}xxzrnov}~||y~}}~|{zzzwz{}{yspopuy{}||zw{{ywtw~urst}~~~|x{vst||z}zuptz{{w{y~vrsy{vwzy~vlkqy|{zxuxyxx~vorz}{{w{{ppp}yv||xvw~}zz|xz}wqstrx{{y|}xvy{xut|yrsty~uv{|wrqw~}|~zvwuywx~zx~zx{}vqru{xyyx|rprtuvy|yuz}{{y~vos}~~{yw~~~zosv~{xx~ztxy}}vvx{zwuttyvrxzxyy}xvv{}xuvut{zux|}rmpzzz|}|ysstx~{v}{zy|~slpw|wxy{xsomswz|{z{}{}|vov}{z{|zsuw{x{{|xxyv~}vw||~|wwtpsrw~yzz{|zurw~|vqpy}~zv|uqqvuw|}wqotv{{quz{{rnpuxyy}|~|qljqwy~}z~~y}srvy~}}}}yuux{{y~z}|wvx}~{z|xx|~{tpmsww~}{{{vpqw~{rrsz}x|zusuyxx|{{rpptx|yrw}{|~uqoqy|}}v{ymmlqy|~}z|}zoktz{wz|}xvsv{{|}yvy|xxtx|toopvxvz|||}wsqsw|xuxvx{{{}zuot{xw{|vuqqw{|zv{}~~~tmow~|vyztomov{~{wx}|}slpx{~xy{~}}qor}|{}}z}{y{x~vuvx}ztqssrutw~}{xxzvsqs|zxrqx}}~{sosz{v}{vssvyxt}~{yxrptzyv|~~roptz}wvy}zpmrx}|}}|~rprz{ww{~~zvv}~xuyx{}zuusoqvxzxuv|xtrrx|or|yx}tsyrrso~y~w|~{~xqv{sr}zznnyrngp}||qyzzyyzzqxzu~{w{uvx||||xu|w{{nvy{qmtxur~~tlsxrt{zyzns}zrsqjvxq|z~svnow}rsyu}tr{xqfqz|tzw|x{{xrrljv{{toqoxx}|{|yvxu~w~vy~~{nltxrp|zzvpqrw}}~yynw|wz{qrjkxzz}|}r|tx~}sv}wwmmzvsmy~}nvtvuwwxpulr|z|}xqjsvpz~w~xuvr}u}vy~|sruyqlvsywqppw}}}spbsvysuir|y}{~~}}nqjt}w{wwhkuwsx}xxuoxw}x{y{mq~s|xpeytsyuw|{~xunvw~{uy|ytquyrw{yxyxsvuzyxsdsqnz}lrxm{{}zwx|~trnw~~}}ppv}wvxyv~zsqxsu|r}vfk{r~xofzqrwrz{{qih}|wv~~vpuznsrsytuxz{v{perop}pz~q|suuy~{sov|~yz}lq{vzzyux{rswv}v|koukv}qk{ovyv}{|rij|{u{{}vps|vqyyxz}~ys}txzmhvqw{n{wozsxwv~{ulky|ysz~~quosrtv~suxy~uufpsnz~~|pts}vtw~||yogl|{}mqzuwzzv{wyvw{py|nnwkvrkzwz}yuw}yopd~yhpg}wwuok~zxpxxvkq|rf|y{}~osvrszv}|ujeyuu}~||vs|yqkr~|y~yvz{zrkvvw|wknzjtxjx|~}}{tx{|or{v~{}ntqlyp||{xu}~uzoigs{uzshmx}wx{r{yvt|rs}py~owz|mimzm~{vt{wwu|wtywg_lxuzz~}zu|y|ywylovs||ugq{oispgmzsr|||{mksk|mjngzyy|{|rpij{~|~uffy~{ttqonz|sw{zvp{|zptljumnv~ztrjp~o}uxxmtmmgh|wzsqvu}{zswtzzw~v}yzsktzyv}yuxtzxn{}{uu}tfium|~|w}qzpgtoizpuwtr}vxvzz{}yxt}|uxx{~{wzwyw|upyspvx|kpemy{~ntwt|}u~~tv{wuuxx{szxxy}uxos~~z{n|xnuyztvqu~}wrtwykht~z}wvsbnwt~nu~rgtztqz|ys{turwy}xlvsp|zsdhr{v~|{qjogmtzuv|ikq}vtvq}{wxyrapx|~vmpooz}vx{~xtvvwvvyz}xs{xqefuwz{opx{xy{zetx{wpx~~~qwv}urp{||}oil{mnlpvl{~z{}zirlmt||ny~{sx}|vwhk{}|{zyuuz~rhsqw|ywkg{lw~opwr{vtny}~|sm}z~q{u|v}zntzl|ou|vszvywuvp{~}}tzvrs{vfrjs~u}w~u{sfx{qrowpzxbi{w~}zruwm~u{uwvspqen{~{|y~wxux}gkznixzw~x~{xgkuhzxv~q{ysndxnhtxrjfj|wpx~tkk~tp}~}rkn}~}vrnjmtzws}y}lrut|r|ouw{uvh{|{y~|oh}shsuxpif{~~z{vtrrtyy{ux|jooh}zsue^vykrpxzst}}xp{x{~ymz~pxx{swq~rgzv|uxy{w{zyr|pytrzpyzr|wzrpl}wlptp~y||vto`szx}}}ihotzz~~~syxbpypp{}~koo~ut~z}ufro||wxofj}~ln||~xv~yxrsr~w~~~}~~njq|k^lxy~~{rurvywsr{~vsw}markozuwmrZh|z{t|ovo{y}{{zuxgx|r{}x~}wk|kpvy{|x~~|ww~|touo}}x~ywy{w`Yulx{}zsxzmhtt{z~osmz|s}hwtwpmznrmsysw}xms|}~stzuympo{}omjvvv}xzy~zu~yxysrt~|w||uqldv{uot{tyrkjrwx|jgmx|wvv~puslyr~ppwfjzs}lw~umszu|k{szmnvxzlrqw{}xv~yu~lyuw{zrquyswl`wmu|{}u{omons{}~ohaz|{q~yqexoxlp|seu}smx||xnsws}~k~tzlpwz{lrnz{~ww~vy~ozru|wott{p{lavk~s}{x{woopooy}uhdyx}s~~{od}pynqzsesy}zrxyy|ussn}{k|u~lpv~~nql}x|y}|p~|z{qpz}{~qvu}{mnhpkz~r}vuwrqq~qs}|lixp~yx~~odzqxpuxztgqvzw{txzuonvpyypox{qpl}~}w}||xmx|oqt}txvwnnnljy{tzu~t~yuot{rq}~mowmu}~~lgvswqxs{~skovy~y|q{|wmo}ssv{ro{zsnny|w~{vlw}{prs}uyvsrmpil{yuzu{u~yuowyrs~luuns}}jmrtuswr}~omnyy{{yp}zult}qsv}}rp~z|rltxyxytow~wprv{tyvqzlnio}xtz}wvxvroyxpvykxrtu}zgupuutuv~inn~ywws|utm|nsywrs{uqk{zxx|}ztwxuov{wytwxsxglmttuz||t{nsp}ts}}rkwuzy}vjx}puvpyxvfnrsqyv{trvst~{qrq{xws|z}}v~poz~r|zzu~}wx~sfjsswsktrinrs}lmvuz{wwqovvtsk}|rus|}yrr}hlovz~yj||z|z~xsmyrs}|~~q|pzt}xxuyym`puwxl|tlutys{toot}pz~y}yzdv}y|mrnpx{~zwh|vmj{}vnty}y{u~txx}}t~|zoy}v|yyut{~zvs}wgp~nvx|ytv~rlrs{}|}|pmiy||khzxrrwzzdom|~yvssw}sjwz}um}kw{}wt~{{{~vtuv{w{~}tt{x}w|yqqfwtwwp|~||chosywxonyq}}{trlr~zlp~}viiy}yyzyw~ktp}rkuvuvmw|yz{xmwow|vvuws{{nfkuvwzr|wz~zzxuvfqwwxxtz}|v~wsogz}yuwokrz~vvuxurhv{|t}vy~t}rmz~y{|xv~zy~t}ysz}uzu~{onu{jyrprxvu{opw{}rvwywu~{siwxsx{yo|rmhr{suszl~~yrz|zt~z~~{|x|kn{~y}vwq|w}~{sw}}pw{|prpzokrzwvonvyxtvmr~~}}qvs{vxrp~|ilo{utsv~{{srxu{|r{y|xwvu~}zyrrr{}yz|x{x{~yq{vzslnkhytz|wx}{kuvt~xns|q{~{|znns~ntwglv{pvvzi~}xyx~|}v|zz{v|{wvv|yxo|rqs}|xz}z{x|}us}~vvsjohnuvz|u{|xkwty~unxwsy}|{}ykpv~lx|qho{srvwwlw}x{yt~w|}u~~}rwwxsuwosvyv}~{|}}y}yvv~yqqhqhwux}~{wzskvw}zpqoy|x}{}tfwwxn}qpku|lvtxtt}zs~}}|}w~u|~u{sq{z}|t}|opw{p{|y|zzv{zz|}unljrozt}}uzunj{tvm~o{wx}jm{ysu}vntm|uoxtwxxzuq}~{y|r~~tz}kxz~{ww|kww}p~}y{}xsy{omkkwq}uxyusjmztsr{rwz{~yxgszz~szzntss~rqxrzy|~znw}|{yzr{wzvm{{}}wxywmyyxt~x|}|z{ywx|~}mmjnxp|x~x~}srhr~wuqvytyw~|{uhvz{|s}zmvqv{qsxr|w|yl|~~|yzxty{|{so{|~|}xv{sny{uvz}~z~yvyy{{moiowrzy|{}~srhuwwpwww|v~{}tgwy|zs}|nvnwzouwszw}zn~~~xzwwx|{}tpz{~}zzz}rnxztw||z{wx}x|zppinstxx|~}vqku~yypwtyw}|ugvy}xr{rrmu~{nvvuuw{t|~~xyw{y|}yqwy}u}untyux||~{zvvx||vrjmky~xvz}}rptw|sus{~{z}visw~vrx{nls{}owswrv|{|x~~wyv}y|tsy}svqqzvy~z}|{~|vqy}|}ytklhz|wvx~stvt}uw|s~}~}y}vmrw}uswlkty|qwrwqv~||t~xxw{z|}vqz}svrp{}vz}z~{}||uoz~{}|{skkh|zwvyrvtu~{ux|~s{||xsnsx|stw}jmtzyrwrxpz~{zuxvyz|{|uq||tuqr}{v{~~{~y}~|rp{{y|plil}wxt}}qwrzzu{~{uz|{ypnuz{rvwwjov}stvsvq{~xz{yt|~~~}~xss{zxzspuzu}{x|yqu}{xymlht|yyuvqut~vu~wz|~zy||kqv|wtw||qmrz|owtvsw}xz|}zvu|~~|~zttxx~uptzvx~~}}{|wvwxxwrljow|{wx{ppu~~{rz~z|}}x|}rlt{zvxz~tsnx|sqvwswzzr}ytv{{|{tuv|~ypsv~~vz~}}z|xtzx}wsqjmswyyxtno|}wt}}{|{}yz}~|pmv{xyxwusq{~zrqwwtzztt|}wuuz{wtvy}|upux|wz}|}zzuu|x|urpipsz~yyxrmr~}vv~|~z}x{}yoow{xyyuvrt{wrryvuy|rw||vtv{{utuz||squz{x{}|{{zsw{yzuroipt{}zxzqmt~~uw||{{w||xnpw{vyyvvpt{vqtxvty}sy}|usx|~|vvty}~~sqtyxy{|z|ztwwxxwtojmuz|zw|rpt|uyz~~~zw{{ynqv}txy~zuntz~xovuwryxz|~|vr{~~~~xwsu{urrxwy|{~}y}wvruyyxolhvwyzu}vsvuwyw}yxyz~ort{tuyxtmrx}}oxtusw}}~y||xq}{}~~{yts|xtqwwx~yy|yvot}}{z{olhuwwzuyvwsyz~w~}yyy{~pruyttxwtmrw}}pwtssw{x{}ws}z|}yss}ztqx~ww~~y{|~zunu|~{{{plivvwyuyxvtz|}~wxyx||psvysuxyrnsx}zqxstrz|~x|}vt~z|~|ysu}ysry}vy}z~|||ysnv}y|yokkwtyxwwyuwy~~|x}wyy}xpsvxqxw|}qouytrxrus~}~|z|sv}|}}yxqz~}~uss||u|~y~z~}yvrqz~v}tohpxu{vxtxt}|x~x}~{uyyrrryus{w~yprxz}nvuutv}|}||}zqy}}xxvsvssvw{|z~y}~|rutu~wyxqmjuxx{u{wrw{w}~}z~~wv|{xsqtzszz}|yxpwxsqvvxuzw~}~|{ur{~}|tys|qus||y}{}zy~upyu|~}~wxtplnwyyztrrz~}~y}~{~|}ty{~ttpwxv{|wzusxzzqruxxv{{x~zyts{}wuxvyquu}z|{}~}yz~}przw~~{wwspmoyyzxv}qt}}|{}{||t{{|tsqxww{}v{tuw|xqsuxxv}y{~zystz}uvwwvruv}y}{}~}x{~|otyw~~~ywwsompzx{wx|pv}}{|}{|{s{z{ttqxvw||w|suw|wptuwwv~y|~{yruz~vwwvwsuv~x}|~|~x{~~}otwu~}xwxsolqyw{ww}rw{}|}~y~zr{y}tuqxtv|zx{rtxzyouuvvw~|}~}}zrv~{~xwxr{sut~xz}~zz{|~trus|~zw{tpkqvvzxvwwx~|~~w{twz~uuswtszx~~ztrxy}ptuusw~}~~|{swz~}{xxs}vuu{zw}y}|y}wrqry~{w{vpkrtuyxx}{yw}|x~zuuz~xtuvssyx}yurwz~}rtutrx~{~}~{txy~}|ywt{wtvzzv|~z|x}yspswzx{wpksruxxy|}yw~{x~yuu{~xtvvrtxy}xurwz~{rtutrz~{~}ztxy~||xwt}vuwzyw}}z{y{xqpsxyyzvnmtrwxyy}}wzzywtv{~vuuuqvyz~}xusxzwqutts||~~}wuw|}|yxvvzuux{wz~||~~x|~ztpst||vzxulostxyxy}yw}}~x{}usy|zvuutryz}~zxswx~}rrtvtv|~~}|uty}|{vyt}tvu{yz}~{|y{}}uousz}}xwyvqmouwyzvxw}~|z}zqxz}wwsutv{|~x|tvw{~uqsvvxw}}~|xsux|vwwywuuw{x~}~~}wz}{zntuw}~}ywwxtqnpzw{v{~t{}||||usz{yxuruvx}}{yztxxysqtvxyw}|xwqz}yzuxw~tvuzy{}}zt}{{unvu{}~{xwwwrrltzx{vzu~~|}}z{suz}~xxtrvwy~}~x{yuxyvrquvyyx~}~wwq}{yxuxxtvvzy|}}yu~z{sovu|}ywvwwrrlvyxzvyv~|}~z{rvz}}xxtruwy~|x{xvxyvrquvyyy~xwr}{zyvxxuvvzx{~}}{v|zzsoutz|yvwwwrqntyxzw|w~{{~}{ruz}}yxustvx}|zzyuxywsrtvvyz~zwty~zzvww}wvwyxz}~}}~yy|zvortw|{uwwxtoqrwwyx{y{}x~|urx}~zxvurtxz}|~yzwvy||strvtw}|wwu}{xwvz|vxyxy{~~{~v{zwqotu{|uwxwvnsqtxwzy}y~w}|wrw|}{yvvqswy}{yzxvy{~~turusw~{wxu~~{ywvy}wxyxyz~~zv{zwrotuz|uwxwvotqswwzx~y~w~|wrw}}{yvvqswy|{yzxvz{}tururx~{wxu~{yww{|wyyxyz}{~v|yvqotu{{txwwuouquwwzy|{|xzurx}|{xvupuwz||}x{wwz|yttrus{|}|xvw|zxvx|ywyxwz|{}yx{wtoruw}~wtyvxrrsrwvyy}yx{xst{}{{wvrrxx}z~zzzwx|}turuuv||xwu~}zxww||wxzwz|~}|{wzxupqtwzzrwvwvqrqwwwy|{}{zztsx}{{yvtrxx|||{zzxw{}utrsvw{{ywu{}yxxv{~yvywx|~~|{wzyvrqsvy}}ttwvwsspuxxx{}{~y~{wsu{|z{xvqvxz}|~~}{{yxx||tsquvzz}}wvw{wxwx}}wwyw{||~~wxzwtqrtwz~~yrvuwvtrqwxxx~|~|{zutv|{{zxuqxx{}|}|{zyxz|ytrrvw{z{wuyzwxvy~|wxxw{}|}vxyvsqrtx{}wrvuxutqrxxwy|{{yttv|z{zxtqxx{}|}~{|zzxz|xtsrvw{z|wux{wyvy}wxyv{||~~xxywsqqtxy~}xqvuwvtrrwxwy~}~}zzutu|z{{xvqwxz}{}|{z{xy|}zstqvvy{}~xwv}wxww~~ywzuz|~~{|wyxtqprww~~{qtwuxstrtxwx|}x}~wssz|y}ywrsxw}z}}{z{yw|{tvrsvw|{{yu{{xxv|~{x{wx||zxxyuqpquv{~{srwuxttsswwyz~}z{xssx|y|zwsqww|{|}zz{zw{{vvssuv{{|yuz|xxv{~|yzxw||~{~yxxvqprtvz~{tqwuwuusrvwyz}~z{ystx|y|zwsqww|{}}z{{zx{{vvssuw{{}yu{|xxv||y{wx||~{yxxuppruv{~~zrrwuwuursvxyz~z}wsty{y}ywrswx|z}~{z{{zx|{~uvrtuy{|{xv~yxww~~{yzuz|}}||xxvrpqsvw~~}wpuuvwutruwxy}~z{tsu|y{|xuqwwz|{}}z{{|x{|~xutruwz|}ywz|wyu|}x{ww}|~{}ywwsqrqvv{~~~zqqvuwvustxxw}z~}uuszzy}ywruyy|{z~~z}z|yy||{turuwy|~~xwy}vyvyyyyv||~}~}ywwupsqtwy}zuptuvwuttvyv{||wttu{x|{xusxyz|z{z||z|x|{wtssvxz~{vx|yvyw}~~xzvy|}~}zxvvqpspvw|~|xrqtuwwttuwxv{~~uusxzy}ywtuyy{|y|~y~{{{y}|~vtstwy{zuz}xwxx~}xzvz|}~~zxvvpqsqww}}{vqruuwwtuuxxw|~}tusxzy}ywtuyy{{y}~y~z{{z}|}vtsswy{zvz}xxyx~~y{vy|}~}zyvvpqspww|}{wqquuwwtuuwxw|~~uuswzx}zxtuyyz{y|~y}{{|y}{~wutswy{~|wy|zwyw}~z{ww||}}}zwvrospuwz~}~xrptuvwvtuvxv}}}yutu{x{|xuswxy{zz~{z|{}z{|~yvustxz|{wz~xywz|{zv{}||{yvtoqqsww|}zspsuuvxtuuwxy}}uttyxz}yusvxyz{y}}y|{|{z|}{wvtsxz{}wz~zxxy~}{zvz}||~{zvtporrvw{}{spstuvytuuwxx~~}utuyyz}zutuxyz{y}}x|{|{z|}{xutsxz{}w{~yxxz~}{zw{}{|{zutoprrvw|}zrprtuwxtvuwxy~~|ttuyxz}yttvxyz{x~~{y}{}z{|~~zwuttzy|{w|}xxx|~~|{xx|}{}}{xtrnrqtwy|~~vqqsuuxwtvvxx}~~wttwyy||wtuwyy{yz~}y{|||z{||xuuswzz}x{~yxxz}{zw{}||zzusoqrrvw{}yrpqtuwxuvuxw{~xutuxx{|yuuvyxzyy~~~y||{}{{|}yuutvzz~w{}zxxy~~{zxz}}}{zutopsquwz|{tqpsuvyuwvwxx~~|uutvxz|{wvuxyyzw|}~{y~{||{|~~{wtutyy}zx}~xwx{~}{yx{}|}{xtrnsrrvx{}wrqptuxwvxuxw|~~ytuuvx{|yvwvyxzxw}|~y{}{|{{|}{vttuzyxy~|xxy}}}zxy|}||{wtqosqsvy{~~uqppuuyvvxvxw~~xttuvy{|yvwvzxzxx}|}y|}{||{||{uutvzyxz~|xwy}~}zxy|}||{wtqosrsvy{~~vqqpuuxwvxvxx}~yuuuvx{|zvwvyxyyw|}}z{}{||{||{wuuuzy~{y~}yxy|~~~|yyz}|}{ztroqrruxy}yqqpsuvyvxvwxz~|vuuuwz||vvvwyxzwz~|{y|||}|z~{zuvtwzzy{{yx{}}~~zyz|}{{{wrporrtwx{|sqqqutzwvxvxy~~yuuuvy{}wuwvyxzxx~||y{}|}|z}{{vvtv{yz{|ywz}~~~{yz|}z}|xspnrrsvxz~}upqpttyxuxvxy}~yuuuvy{}xuwvyxzxx~|{yz}|}|z}{{wvuv{yz{|yx{}}~zyz|}z||wrporrtwx{~~|sqqqtuzwvxvxy~}xuuuvy{|vvwvywzwy}{{y{}|}|z~~z{vvtx{zz}~|yx|~|~~yzz}{{{{uqoprruxx}}yqqprtvzvxwwy{~~{vuvtxz|zuxvxxxyv{|{zz|||~{|{{yuvuzz}|{}zw{}~~~{zz{}{|{xrpprrswxz}{sqqpuuyxwxvx{~{wtvtvz{|wxwwywxvy|{zz|}|}|{|{zuvuyz}|z~}{wz}~}~~|y{z}{~{xsppqrrvyy~~|upqosuxxxywwz~}yuuutyz|yxyvyxxww{{zz{|||~{~~z|wvvwz{~z||yx}}~~yzz||}|zuqpqrssxx{~}yqqpquvxxyywx|{wtvtvzz{xywwywwvx{zy{{}|}}{|{{vvvxz}}{}~|xy}}~~~~y{z||~|ytppqrstxx|~|xqqoquwxxyyvy}~zwtvtwzz{xywwywwvxzzz{{}|}}|~{{zvvvxz}}{}~|yz}}~~~~z{z{|~|yuqqqrstxy|}{xrqqruwxyyxwy}{xuvuvyz{yyxxyxwwwyzz{{}}}~|~~}|{{xwwxz}}|}}{z|}~~~~|{zz|}}zwsqqrstwy{}|ytrrruvxyxxxy|~|ywvvvyyzzyyxyxxwwyz{{{|}}~}}~}}||zxwxz|}}}~~|{||}}~~~|{z||~|yusrrstwyz||zvtrrtvxzyxxy{}}zxwvwxyzzyyyyyxxwyz{{{|}~~}}~}|||{yxxz{}~~}~|{||}}~~~~}{{{|~}zvtrssuwxz||zwusstvxzyxxy{}~{yxwwxyzzyyyyyyxwyz{{{|}~~~~~}|||{zxyz{|~~~~}|||}}~~~}{{{|~|zvtsstuwy{||zwussuwyzyxxz{}}{yxwwyz{zzyyzyyxxyz{{{|}~~~~~}|||{zyy{|}~~~~|||}}}~~~}{{|}~~|yvtstuvxz|||ywuttvxzzyyy{|~|zyxwxyz{{zyzzyyxxz{{{|}~~~~}}|||{zyz|}~~~~~|}}}~~~~}|{|}~}zwuttuvxz{||{xvutvwyzzzyz|~}{yxxxyz{{{zzzyxxxyz{||}~~~~~}|||{zzz{}~~~~}|}}~~~~}|||}~}zxvuuuvwy{||{yvuuuwyz{zzz|~~{yxxxyz{||{zzzyxxyz{|||~~~~}}|||{zz{|~~~~~}}~~~~~~}||}~~|ywvvuvwx{|||{xvuvvxz{{{{{}}{yxxxy{{||{zzzyxyyz{|}}~~~~}}|||{z{{}~~~~~}}~~~~~}||}~~|ywvvvwxy{|}|zxvvvwyz{{{{{~|{yxyyz{|||{zzzyyyy{|}}}~~~~}}|||{{{|}~~~~}}~~~~~}||}~~|zxwvvwxy{|}|{xvvvwyz{{{{|~}{zyyyz{|||{zzzyyyyz|}}}~~~~}}|}}{{{|}~~~}~~~~~~}}}}~}{yxwwxxy{|}|{ywwvwyz||{{|~}|zzyyz{||||{{zzzyyz|||}~~~}}|}}|{{|}~}~~~~~~~~}}}}~~|zywwxxyz||}|zxxwwxz{|{{|}~~}{{zyz{||||{{{zzyzz{||}~~~~}|}}||||}}~~~~~~~~~~~}}}~~~}{ywwxxyz||}|zyxwwxz|||{|}~~}{{zyz{||||{{{{zzzz{||}~~~~}|}}}|||}}~~~~~~~~~}}~~~}{yxxxyz{||}|zyxwxy{|||{|}~~}|{zzz{||||{{{{zzzz||}~~~~}}}}}||}}~~~~~~~~~~~~}~~~|{yxxyyz{|}}{zyxxxz{||||}~~}|{{zz{||}||{{{{zzz{||}~~~}|}}}}|}}}~~~~~~~~~~~~~~~}{zyxyyz{|}}|{yyxxy{|||||~~~}{{{zz{|}}}|{{{zzz{|}}~~~~}|}~}||}~~~~~~~~~~~~~}|zzyyzz{|}}}|zyyyyz|}}}|}~}|{{{z{|}}}}|{{{zz{{|}}~~}}}}}}|}}~~~~~~~~~~}{zzyyz{|}}}|{zyyyz{|}}}}~~}|{{{{|}}}}||{{{zz{|}~~~~}}}}}}}}~~~~~~~~~~~~}{zzyz{{|}}}|{zyyzz{}}}}}~}}|{{{{|}}}}||{{{z{{|}~~~}}}~~}}}~~~~~~~~~~}{zzzz{{|}}}|{zyzzz|}}}}}~~}|{{{{|}}}}}||{{{{{|}~~~}}}~~}}~~~~~~~~~~~}|{zzz{{|}}}}{zzzzz|}}}}}~~}|||{{|}}}}}|||{{{{|}}~~~}}~~}}~~~~~~~~~||{z{{{|}}}}|{{zzz|}}}}}~~}}||{{|}}}}}||||{{{|}}~~~~}}~~}~~~~~~~~~~}|{{{{{|}}}}|{{zzz|}}}}}~~~~}}||{|}}}}}||||||{|}}~~~~}}~~~~~~~~~~~~~~~}|{{{{||}}}}||{{z{|}}}}~~~~}}||{|}}}}}}|||||{|}}~~~}}~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m32.bin b/applications/SprPoP/assets/packed/MUS/m32.bin new file mode 100644 index 0000000..44fa7c7 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m32.bin @@ -0,0 +1 @@ +û~~zpkU7&02LQfq{|s}PVMdzƳuzujMUF*&XmiU>L@zwjbλnǿxSjK!\fKOJH'/5Ypu|bxrzns|p|p}ioipjolxw}hrwzs{qe{~cyyhebjqqlxvvpo|xUWSDU99[zݸţuehbRigavYqlfunukx}sk|^TH_CVF:M8IP[elr|zz|cGZZ_5'2DRv}xestmvpkibyoxw{o}mmz]zq}}ezzj\loa{]lkcshmcj|nVlW]aAEVP_r۹ɼ{wl^w^kkVm^jokpi|os|÷{avtcejKaEAE7H>FIRs{i}dT_dsO8$6(,KCofmwzswpmarxatyz~z^tdercv~pcqo]w\bbcsgmdbulZtbcqUXbQSV]ҿwlhjrUcZhohpeponr|\grrq_n\GN@PGDE>VgZuynXcf|mL9@:!;&2Gapit~ttyvyhfvda~ntrz]pujUfv~lj{`pnWb[pnkn`jt\jvazrcodY]Q\mvѲu|e}iZ[[ofkmcrvd~th[jvslzh`WOQ[LOH@SROxw`\itkK>L5-<&+Kiyclyzdkuzz~ojt`jmuconVF]t~kqy_uo[\\pjmrgls]qxf{owf_bU\fixpͯzjrb]andineotexºc\o~oh{xgk_W[`RUSEPQInqw^^qzqNCM<+@% 6MYxųǴ}ak~bcvjo}~|oyljs|mijR@Wqnrgix`_\nkirqkylolxzrdc\Uffl`uþp|igfukiojjzn}wfbxrgx|e|wd_em^[^SQYMiqi~|aj}}sTIUG-A.!0>CZt÷ſzsav~card`~rtys}wtx|qfgOE_qw|xiukmzc]bumkpskyr}q{tli`TdgeSe|qxpjssqtniwpz~es~hdyvfsfaqtihfXZ^Xskgx}lb|zoRPZC:H/"#849H]cҿzybfvalqXercewxvwrmbJYnvqrc_wyg~wd]mtnpulkurxptpd]jeYNi_VimxĦss~|{}jnoxnot_mtar}jco{qsuo\b\rybw{zq]rcS^P>RE/%5427BBOg~ÿf`xijr]^p`Xc{z||qRZlzwsfUfj^yofstmrwrdnm{sxupgtkZMdXDMIJs˶|vonw~xyhmlXjwkcqwpuy{hkcx{hzwo]n{vdfPDXN>8C344=12@VɿƸojtvwqYgraVdkft~~{dkj~o^dUPqvnosroypgin|z}zyxpp~qZWhRDF28JcxztuzzxeTjoedvkkowxkssylur{~tinowpRL\REIR<;3?/+/@\VmĻí~v~v[fqf]id]cixp}s{pokSH`edmtysyqgotxs|um~vfdnXGI558@^tzz}}{uYdmd_rnhklventrv~w~tzq{m~fS_\NJXGI>>9+29OFFg˿Ǵigtlbkr]d\lbfv}o{y}zjNVb[Xpur{wikqr~owykdurTG?4?3HTkxwrqjpqdpladscojsncmz~|ppsyz{b`l`OSVCNCM9-4DTAD_pżҷydn|seomcj_m]^kyrhzu~|mXaf\Wiclxyrru|wyxjcssYM:/>:TT`faopx~rk_bu|ofxwgdo_njw{ho{~p~}wtu{|zwd`qo_ZZDNCPL::BUJGZephȽĬ~ggzzqso]netj_hz~y{vetzp[bpj^fcZoo|||pdls]LC2?=R]T`U]^Zw||l^Vptzeoytlq\cknuy~|~~ux~rv}vaZmskhhIPHKSCHMXLEXdj]tĬlhwysyyblatugkx~yuof|z`_oqimaXja~}{oru]G<6NKVUKZRSMI`|uj^^tu{nvyrsy^fbl~r{|xzpokz`[krmpjV]IVTFN[cNETabYqnԿtr|zs|omucvonuu|ticivipsoov^edfzq|{}tVG8DUViLKRPM=?Xkt¬i^[ssvqw}rwiin]soq~uh`mqflqnpt]i`Y`NOWe]PUW[SghfyoƲŧy|peykysgrtp}|g^`yyukw{qwfkl`ulo~lTCHQPn^WSHG97GSZgrs¸zhZmms}xtoju~|efretiftzk`fu{mjy|rpo^ocgmWV]eXS\`XKe`el[iq{˸nkthxmn|srw|urabli{lgvw`umlwglwxiUXLRk_jaO=/7FJHRLTruumm]um]Wl~gojg}lgn{{t}wgmhhpkxzj]rdmsgcdgXQZc^WdVdjW[\eyʽrrwg|yvz}xnt{{o{{|pjfZ|fcp|jukprot~vstjkSQmakj_L20@JII;=VOpzx|cqp\QYjw|~lutcvsstwskt~wsl^|ty~|coegzootraVY\^\odXgZ[ZXcw{v»}mx~t{tku~zww}~usx^j}scdrzz~ry~gz}z|{ps}~tqnYllhn`VF;<@EJG4DDGdmwzum~nWP\fhrqg{ppzpsrho{~zmkvxtq|clwsv{}l^_b\^tejhQWZ^gpmffpx|~~xuxyxvzsnqf~wykemruxvgys{{tu}xptnb|kk_Q@@FKNJ8>E@Yccl{ȴúz~|s`U_loj^Tieeww|{|ymefq~}uw{yzyvy}eho][]_aa]WQR]`}{|wx{uuz~suwo]V_pyzvmzuozw{wutkbPCGMJHA?P==W`fkprvżǷ~zuoebbinkZ^h[jy}yz~yv|}zjbjuyy|vv{|{~lui]X\flcOHLWV`xu{|~|php}xxrni`]dlsrgxkzy|uxuj\WVRQM?:QHBNOZdjc`juɻŶ{nhmrohh_glWerr|~z{|wzxy}sllrvvpsxwz|ma^\bliZIBMIZrdhpqv}xzymhhil}occkljifdwsp|s}umb^deZPBCM@LWOMW^XUW`i}ujoz|ueYnm^ikmqw}||yw|wxpqvprxytsih|~y}ncdijf_WOF8Qj``aa\]q~thibY|zg_fsul_Yqoi}}}zzy|mcfkmkbIHT?FVWVSOOQUWXj}t}tsw|~wdgjaqsklrpkt}}y~neritxjcvno{xolqm^WUUBJTXOOdakŰut}zxzzqaV[chptit{ksy|wy{z}x}{~zwtougaswvvwjYWXUZnfai_bhlpwztq}~ztzwz~wkmut|{rooptunj}s|}vmklaj}g][PE@BGFDJFMYL\ouɺ}xt|xui_\afebcvsm}{w{xsw~ltfc{nimdeggylWUUeuztolxlz{tnbTWntRW{tq}fdtxmaamylwkkYSuTQjrhnsrptfU^v}ydRA39IX`_QL?)Frm]r{qjn{v_OB7NWVhmke`[aq|mtlyyskipsy_V_mty|yvdSQQQYjypO\Ƚ{r~wldp{wv{sgbcZSsmYTb}tqYLdt{}vaKLiilshYOKLPOG0 +*=\lytaaqƹ|jWN^u~rx{nmw}nfe`Sowj\STbmqtwre[WTUTYRJULEDBAGS]flrketpuĴztszikpXOU`iv|zxvn`_glilvksnq}qlcWD/*././)3<5Pjtz|ynegrzƻzn_RMVcji`ntmwesxx{qzu|wk[W_gq~xtsmjsdR\SIJR^febgmsjhyqwyurz|tuvoumptw{ytz|{rhWKOYdllxz~teXPJJJF?J\RYfq{}qmmntyk_XXYVVZT[aTaln{}yuojt~mptpywu{{ugechsyqc]gqorsooqpw}yv}zrudciptnaYV]gnupe[TVYZ^dim|pknkb]TRb^dqlnyypnqszypdWX`c]SJUaRV_adjsqmopknvm{~oov}rnpstnf__dkponkely|z{qzpgagpl]OLVW`}vlni]QQ]geb^f}y{vwyzviX]dWdqqpnrrrrpq~se]\afe\KTZTa`]aecdknoq{uj|~wrr{utvmb^`f_g~uqohd`dszttplije\RQUThpjolh_VV^gleZipqxu|~zmag\[fioroddlqqtzygdggedZ]\M]fdbbb\\eop|szztzyx}|yzreZXa`esikplaY\fo{~zttwyrc[XYXPdlahmja]_abdfaigcx{|~qpu_^gdflng^]ekt{ny}|tppkc[hfV\aehg_XY[al}{vzppw{trt{yyi`\[Viuhfih_VV^ehmp|x{p_X^ZVhgcfcc_\age`Y[ne`ns|vw}mkjffgheca]_pymuxrtzzsk_iiake`fkg\XZ\_swt~vqpxukjrpux{}{jb_R_rnpi_ZWXZ\^_Zl}w}re]XVlofhe\TW_dgg^Tbdcnmnv~vvut{qjiha^cfbcokr{olotyikoakrnifheb`]]gnh{wwvux|{vkhhfwrvv~si[Wllmsl]NOX^]ZTUfgrvpkgWZsrqncYPOZege^^eZbssqsxz|~wyu}}xqjc^`egooapxpmkpy~uusfostusibehgclmdrxvvw{}yqmnnfdvot{~}~}vuhasojliaULOY^^URb^dzxmimehuorsj[ONRYaf^`m`alqy|ytv~s{zkccccmxjkolnoosxups~ntxtw{yoeflmtufoskqwzuhbhqn}~xtv{|zw}{vvvszjic\VRSSTY[V`a]nx~pijko|lkjdYQRWY[]_mihrqs{wx~~z}rigehslpvhdipuuohfk}||||xtojjv~prtkiku}~wmddlq}}{w{~|zyr}xqw}{tjcZPMSYYTQRcd]ksyxnrqsvod_[YZZYYSWlhjxywy}||~}{wogmqdouke`fpska^hs{|vvxunvxr|uihgktyqihnnp~yvsu{}tq{~~v{z|ufYPMRY[XLN_\dnnu}}{uz{vj\W\bb]WP[absxz}}yu{}~}xsujhpihfcchic]]gj{|wwzz~{mz}rlgilmljglro|vlkt}|qzyu|yiWQSUVWQPVNYikmqvvy~|vj^Z`ed\QY[Ufrvz~{srw~zz~omrfcdgie^[Z]_\o}}|{|vzwsrnlmld`dkjnz~znhmv{|wv~qr~tf\YYVRLUXJR\bgjhfjq}ypjgccd_X_WT`dmvywtrrt|}xvzy}}{vidadklbUQW[Vgrqz{pqstsmd]Zbei~~zuronprvssxtz{~~rnjb[VKPXOVWU]ef``ekruoqrme_\faV_abgqtqsutt}ytwrv}vjecehf^UORP]ojottru~~uosyxnb[[[Zr|xmipvtpnky{u{}{uttnbUNWOP\\XX^``abfn~wqsvtn_\h`bfbcfikosvx|y~|~xuo{{x~vkghf_ZVUPI^nkonkffpyuvxqg_][Yny|zohksxwnhuwv~{~vsvwtn`TYPNX[^^\XZaefkyv}}wwwvukfi_eoljjkggpxz}}~t|yv}wz{}xqkf^SNQVQ[gelnia_dn{~wnieec^nwr{|upopsvwptvp}}sqtvskadZNWYY_c`YX`hlvwt~}{vpmvmcmrtusmijowzxz|zz{~||~~zwvo`RKJRT`l`_egc]_govteagnjtzqqtvtprwwtrp{}qz|{|wtsrnelgZ\[Z[_`]\[^fvwr~~}|soysquqsy{tmmoqyupu~xtw}{zw{|{zyuhVLMRQbka_[[[[_dinpsxldgnr|qroljnty{xqm{{y~wx||}vpijrfgh`\]\YY]^\aomu{uvzuyzr|~yvxzvsqpq{vpv}yrot{}tp~}{}|tg\TTPXe_``YQNU^cdecm}}uponk~zwrlebjuyyvovuszv{}}~{niskkqnga^XTV[]`f_hupnns{v}|vsuvurz{u{xwxvzsoquxzvqxtyynfb_TZe]^`\SJJS[^]Wakj}zuxup}~zoeadksvrpzsp|}{yzuvttjqurqmcXTTUXbeYafeghjqusv~~}srvxx~wt~{}ytu{vx{rqsssos{or}|smjh_eg`a_ZSMKKNTUQY][iu|{xx~xkddghlmnysryy}{}xtw{t{y}~oqvvvuqf[WWU]c\caY]bfjkifhs~yuvzv}ytv}wrtty|zvsqkkxssyx}|}yqnoglvlge]SLLONJJIQZRZeku~w|umihgfdbqupz}{|~~~~xprtv|w~x{wwyyslg`ZZb\\f`YV[cig`^`hl~y{~vsru}|wuto}~}xqimsnw~{z~~~sonjw}vtj^RJJNQOHBNUR\aemv~{pmoolhc_knp|}zwtsou{||{}{zzwrpmgae\[b^[YVY`b]YZacr{}vssuy{xvvzu~~y{|ontls|~}{vsmy{zuhXLJKMONGJOM[ceipuxwlkprpiagjfv}vppvyvzvy{zuttropg[b^XYZZZXVVX_dj}|yuturmns{}zvz{x{pqz{{~xzy||zxpfXOMNLLHMSJQ^ejorrw}upnpqpgjlenw~unlrz||vw{~yuvvtysfg_WUUY[WNKQ^ej|~~}yzzxvoheky~~zz}{~xw|{}~z{|zxria\WRNJDMRMTX]fmomqw~{xxwsomksmhqswwmmtv~{yz|}}{zwvy~srp`XTSUWTLHLY_nysx}xvy{wmedkrz{|~}{z~~|~~~}wpiedaYPGHOIPXWX_fghlqv|{}}xogqslrrruz~yrssvzuw{}|zvv{{yx{pe[VVUQLJKNO_pmprsrt|xz{skfhkm{w{xu~~~{x{umllkf]NMOFMTUWWWY^dgju}}wnrnmvurstst{}{zrv|sqx}|yqt{wzvpia]YQKILMHWadlliefmyztplkfjz|}yy|yv}x}xvwz}spqqmg[YSINPQUWTOQY_blon||x|qluwwutpmpv}~}yz~xtsvyyssxqx{xspkbWNIIKHUZXbgf`]ahoxwpnqlivwy~{|}{{yzxwyvwx|zx{yvtrofeeVRTQQTUROOSYckemutzzuwvvyzuomorw{z~}|}wuwwtssrzurz|{ywvtj[PJKGL\[[]^^ZZ]bfknz{qmpmt}x{|{yxz}~|ysyz}}zy~||~wsv{~~xsnhlj``[UTTSQRSRS_fdnnlpv~z|xxzytrqqt~zw{{{tquxxtmnzvv||~zyyxqeYPNIN\\^_YTSV[_acck|~wrplw~|wqnsz~~|uw}y}}|xssww~{silofiib\YVQQTWW\`^joklou}{|yxxxvu{}z}z{|~vqqtwxuntuu{xwsja\ZTQ]]Z]\WOMT\acabpt|}wvyvvzrljow|~zzz|~}{yvtuzy~sorimpmjf^VSSUW^bZ`ihjkmsxy}xvy{z}~w|wyurtuutrxvq|~yrjdadb^d_YZXUQNOTZ__`klqxw}}~|tljlptyx{|~||}}tnpv~}~xzwlqsrqoi^WUUV^a\ba_dgknomnv~zxz}~y{y}{uv|yvtsoqzuu{}sidfjhmpb]ZUOMNPQRVW`gdoy}~|ysnlmnoor~~}}{z{}{slmw{z{yvwuuuslf_ZWY^Y_b[Y[agifcfo~{z|}wvw|}xvz}ztomusu|{{tkimlrzqjcZRKKORQNKT^\dlqxzsqqqomjhtxy}zzzxspnqr}~|{xxwrmje]]^V[^YWTV\`_\\ckr~}}wtsuz|zwx{y{potnsz{{{{|~~wspmx}xwpdWNLNOOLFLPQ]bdjqv}vqsusngfnkr~|{wqpqsnwz{|}ytsqojicY][VVUUUVVUVZ]bu}{wttutrtvzx}~yuvmpvwz|{vv{~}}zrz}|tfYRPOMKGLJFRY]aehjq}yutusmglhhrx~zsnnrpu~zxz}zvuustseb`XUTVWVPMNSXWdrv|{yxwtnkltwx~{xpuuvy|{wuw|}}}~vkb[UQMGHOIIORY_bbdjr{|xtrmopgkprx|rmnpo|{yz{{|}{xww{xnneZVSTVVOIGNSWfmpy|yyzytmhgmqx}}~{|xwyzzyyyx}}xojf`XQIINJMRQSZ_abekq|zslptmoqqsx}zspqp{~zyz|{zyv~|y|}tvrf]VTTSOKIJNRcmmrvwy~~xwyxqjgjmp~}z||zxz}}|yqmkje^SKOLJSUVWZ]`eimr~|tnspqwutvxx|~{yyuzztuz~}{vy~wyypiaYURLHHLQP\hinqqpqyzwtpljloo|~~|}yy{}}tnmmkg_VVOLRTWY[XY_fkpyzyvxoqxxxxxuuz|{~}{vtw{}|wz}yz|zrnhb\TKFELRS^aailljkqy{tljlqqs~|}{zz{~{wyztrqnjca_RRTSUXZWVY_entr{z~{svwy{{wttv{|y|~{wwwwwywx~y{xtpmicWLGHMNYa\^addcflrx~~umjost~}y|}~~~}}|z|{{}{vtw{wsoihi_\ZUTUVUUUVYbkjqutx~~z}zwy{yustuxyyzuuwywtpt|wy~zvtrndYPLMKV][^\ZYZ^cfilo~|uppot~|}zwux}zz}~||yy}{urtxzzslonfhd]XVSQQSTT[^_jjikpw}zywvutsu|z}{|||utuwwtmpusy||}|yxume]XRMWZZ_^YRQV\^``amsz|yxrs~}wqqv{~~zz~z~{~}|{yyyxussut}strlonjd^XRPQSTYXU_baceiouy~xuvwvvzvv}|}{|vuvuuqqtnsz{|}~|~~ztmhdaXZ]Z]^\WPNQVYZX_efs~~|x}uooquyyw~|y}zzz{{vrqrusv~{{rqtrpmg^VSSRV\VW[Y\_aeijkq{}xvxx|}uy{x{~zx{~|xvtrntunswz}~{|~unjjgche_`^ZTQPOPRTU]_`ks}~~vqoppqru}{{zyxz{xqlntu{~yvwutspia[WUW[WZ\VVY]bdcbemy}zx}y{{vuv{~zwy}{vsnruqvxwz~}~{qkkljrsjf`ZSOOQQOOOY^]fms{|vsrrpommw{{{xwwvrnmptz~{xwvrmid]Y\XV]ZWUUY^`^_dnw~|||yussvz{yxzzrrvrv|{{|~xrpqovzspi_VONPRRQLRZ[dknt{vrsutqnjqtv|xvtonotux|yuqolhcb[W[XVUTTVXY[`jp{z|zwsqrrrsv{ywwrw{|~}{}}yxyxu||wupg[SPPPPOMSST`gkpuy~ztrtusnlqor|}xrmlpww}}|~wtsqnmj^][USSSSRPPV_gkz~{xwtrqoklr|~wuyy{~|zz~~z|~|{xsjaYURPNKOSNTZ`flopu||yvtsqnromsw|zqmmsu{}{|~zwutttjfbXSQQRRNJKS\amvxxvuutpjhjrx~{{yyz|{zyyzzskfaZTOJNPLQSUZadeint|}{kgfggg`\Zd~lbvqd^]]^ehgskeziSW^ehikf\K1*Ko^FhpZ|Ĺx|zmgjq{sSV{olfq}~wfbhyfXhhefipuyzvkgtm}{blqf`gjihYB4>Yt|oYO`yòϴohc_WSVS[l}l^d{{qjgfhjltq\ft]autnvmcchy~|vpkbS@0-;99;@HU]_jnpu{~Ļwohc``dhijjkqnlonnqtutuuvw}|z{slfa[UK@649>I\dkrvxxy{}ž}||rkgcabfkpqqnqvsturqrrpmliginknqlheded`\]cnx|||uvtpnnmkkllkptt|~wwsmolgdcba``acjnpyzvtqoonjhipx}umgbZVXQMKHDCGOUZ^agsw|uohcacilxxtsuurkdeb\]YSMHC@ACFHNOPZ\\]_bglqxľyphfhjklhilhmrqrrsru{{tjikjlwwhaTSSTU_ml|ŨտrjiywWWn~dNL]swc`zY~t^tuaVQOU^k}kcxvst~zUSx|QGqiPKUfsz~xokpǴ~me\]ojJ\cO_{kf{srr{~sqpmg_\anlI56GZc`SE<97=BCPaxzrf\]qqbbm|{~xvpoonq|xigs{pmjbgr{nO41G`j\B738EQZ__\VSYao˹yw~jlvslls~{{|y{fWXjuieeghdYOGFHM`ny{kZNE?=?CMOPW[ds¹}mb\]cmv~}mb`lk]URRQMIFIS]s{smonikjks~oZMDDR\dkqyzj_]bm}sg^YY[ZXX\dlzoedebinsz~xuwtj]SH?::?GMPQS\_`eeca_^aivü{skjopwwvzzz|~~{xqrtnlifdeku}|si`XPNPTYcihljb[TNMMLLOWam{ofigfgjotxz{{|xvzxwwsnjjnqsrmjkfdfc_[WUW^gq}}|}{zsniea[[^^gpwywsld][]__]Y\][`ddddcchp{yuuy|yneWJHC??@BCDEGJOQT`elv{~~yvw{}zxz~{srtsvz|~~{vtssmlmgea[RHB>=;:8:;6:>AEIMR[hx{{zwwwvuqkfdefekkhjljgefilprsxwqsqonnkghkosxxrrlea]\[XTSXaiq{srlhghhhfcacjnqyxwxwvtsssttrprlffcbcefhlry|muuxuoefUGKACKLRY_q´}y_ijPTfr}~uk`e}pn~ebp{qehd~d~}s]eeJFXkrl^TMJSbsr[i{vvhrv]QPSRSUWX[opmx|i`iyhiyudqmLPmyjN93./447?Nav}lhqɾ}|xb`k|skkwpmtskcdxf[l}wz~l]W[ny|uh[OF>>EO]no`dvf^]agjjgba\]gr}iiy~oc_`pohq{cM=::9@CBA@AEP_qxihr~xpnkq}phgo}qjiflsuvwww{p`RF>82//4;@Papyjceq{uz}||tooqx}tqtrrrrqt{{tpmkjnt|xfZTSUV[^\`ccdc_YUTRPRUVcnzvnnllmoru}{wwy|nd^]`cktw}wrokbcb`___\WQLIJIFHFCHNV_k{ſ{sqsxzvojihnssy~~woiebbb^`aadhloponpuxx}|xyz|~vkfa]]^^^aelt~ɿzqigiefggijiea___digkmklnqturnmnnloojklnpu|~vld`abdnpqsroid`][[]cot{zvsqojc][[Y]a`chlqx|qg_XWSNMIECC@>>=<=CEGNPRX_hrx{~ztnga[]a`eiknruwyzyxxxsutmheca]WOHC?;@CCHJLOU^gov{ý|zxtpkimmouvvwxwutqmjjfehfdccdggeddggny|{tniea`abcfjiputvx|û|ysttromjgfecbdecknnǿ~j`UE?0/Vjj~}aM^t[XowowyueNZX_hoy}jZYUMjؿmVbg]Zjdjmwhjoi^qx|qcg]TGMtbSRG.-9?Uo^ZZPPlu[Re_N`u{v|~w{ypsuidaz}snlrs|slr}uYWn\84>IKfv{u~WFJMAGWX\clt~ww|hnvrnv}}|sanv`_c`qoX2>ZH3473.EP[fy|`YysVTWWHQUSR^h{vnuriaZ|vàmb[TAFDA;IMbmv}`MIF9>HKLX`gn^ZcmecJ[~kXYZV[vxqkjmjǼn|ja]QXbikrwtsicihgrxno__NU}gKltz]c|`JB?43770:>@:707=FVzxxm[su}˽|pXL>2) $1ZiŮobZOMHA9MJ_WRQZSSJ32TYIOdrjhgjoywvmkgghfgoifWUOSXdf}trjecSV{}zor`]q~{|wymez{ZGC:1'"1;at|iife`bUfnz~~i]cŸzk[_aemy{tuhIGC@:?3FOZjcjlpkoVJId]=8>FN]ag{vxwqshn_U[~æ~eSMA0*#&FQv|{`]VMCB4JIY[XZ\YYU>>Sx|spj^dfdmptȯgihc^aTkk|lei{bhxxuhe[cNIU|}}tzmdWZnfF9<92=DQqu|decZTXMdaoqprpouwfsƻ~ijyrSBLQP\js¨|pTSVMCG9JNU_Z^XWWX80AXYOWovqvpox{}utpzfUf{}rȫƹzcmtdI3:=:CONhdvzldeYLQCMOKRHLEIRdS]vgxtfNS\Y\t|̾ulwsgmlp|{xu|_Mrx}ovvqjiaaheFdxmy~x{}jC]eQO?G\`_mfpvryrammcfhdujsntwUhtXUC?U`lulwkbKRSHITQe\_\\\VU`R@t}rqyqitnwypuotkgijGd~u|xPnv_f\]pm`faeql{}s[dcSPUO_TXZfr{n~qt{ywKdrVXOJgyù|{{v]dkbeqo}tssvyvyyzz~|nnfeYQVY;bt~|t~~ku~roywzyvsicjVD>Hoοzxk]GTUJNTQ[PQRY\_jqq}}ho{pnyyzxsod_k`Gp{gqgfzyzü{w_\{}fhjXU`bomo|i]eXOWXbjjyew|wody|v~vrvRJj`V]Xtyyubbqf`hkvxpu}swkrw{mfa`YQWbFGptrxmĺlzcblabot~yoswxogwu_{seqf^dgmsoyzyzkWfcX]hvy~vmwbAMd]a]k½żs}kZLZLBHOW\U^oyw`mkafmptfaeig\[gTCSpmj_tļrn}sqx|{|xdpcTQRYb_gu}|zclgWZevulzehp]ZahsuosxyshtnXOhxvsrjS]YPTY`hitrwpmlig^TZ_]RCQNA7Mijevø{gUeaVVX^d^]hotsjpp]\`homgpx~~~ny{^WcY]mq}tlpw~sq{}ygepcZWyv_kpkwʼzyVO[KEDKU_g|uoaslb^]bd_ammgWYgXH=]uvtzqn{jehqxulttY_TKMRYYPOco}~{ioyvlvz[RbXZ_gjhckwvsgu{{}gvuqt{ooV[_PSZmvku`WSX]]TSYUTGNUI?Ct}ewt[V\NSU^ab`r~jWf^ZZ]_[UXlvz}yltdvzyd^offchoqjktsvozv}iZkhbk{nlUQWLVcn|~lY`ROPYedYR_`bTTb[Ud}lu_Wf\_^jv~tcR]VLHDMONPfpvsv~vgqzbYN^UNOYhjd`ryxhiniwrc}{aVLa`rnVYXNTVaZPEPRMJE\abw{o_VJWTNVaz{|vkb_fTRP^giektp{wquo}ok___T_drtrkykmr_UNdfv}zhe]o|wn\RP[T]Zafe\]c`ie{~|ufe[_ectlvo]UGH8=@N_aZXjró}ylgsq}~{ttz~os}y}\bvwt~é~{uK8Dds<1YlIC\kiSWVhkf±¶{\YqvP;XwÔswrWR\ciF56]hWYUgnw{sR79Mb}|puvXC9_|dd}{agztypsUJX_VipfG'ElnMAZ|zibsu|QDEnptbat|c^TM?('&@Wms~~yǾƬ~ypnpX973IVpyiqpPJMetus_hy}ytsrwxhD6%2G`wq]Pbdonqsaazffl}muiZ[gTQ_mbPQ<3'2M]eoմkkqmfclYCG_MF>M`l{vwfdRCC_VKILaajemplm}}wt~ymR:<42>Quqwpe`yqpuxsjukXKkwwzzhny\N=;" /16Dfz̺~|zeWMhdRMWkzzvbsh\Zieokic\]fyyî~shdu|~~~t^k]H81,"1;]zyoXptzsk~tdRkkZB60+1)24;@_}vozs|}gavogeq[kgZMW^]]R`fgaowpqm{}ururxmy}nMD`WC.42=Mkxyswy{caaVAA[K/%'-:Pq|~ivsfxkaj^UU\je][pvwkienjuuiYcigogn`OC\KJQeilup|xmwyʻƯkrliiehO9C_M9,.::Tbs~uv~pakfmx}mo_T]o[ABDQJR]qzwgffqrtnrgemgjTL^tntvx|~lsy~yvȬ|}ylabbbeRH84BM8''%;FV[jslhp|t{ʭ{|uhmodZ[QVUbn|||sh`VQXWjqlYYbgiYYMMdyomjl`lvxt||qg]l}{oʲž~~}uirwoeS]SJS[OEIUhdoorxz~~y{}ifbrronx{|vxprkoZEQf]LDBUXo{cgcaksuhgyxsnjpcutov{~tyjd}jluѾ|nffojidiZSWTR84ISDLNPXSURdtsdjwn}z}sĪ|p[auz\US[k||^WOTR_]^_fggjblfnwrxmb[lg_^h|~yvzijfag`dsynww~ֹ´ntnwtjmqxicZOTsrmgl]^fv{qlr~tecuyr__hqxvxzxvurgpzvoX`]OJ][NLXrr~qfsolbf|xnz|~zvkgt|vvwvssqZXtfgvx̼}dg\\^jpdU[lfbRVXMOg|laMMISXQX\ejx{qnypivvn~}}˺}p{}{}yxwxbObvous}}{nktj`cUJT\ha[\lqwe~{_UZ[d^jiYPY\]jcfXRPfyidgxxwosxtrkrs{qpqm{}rxceof^Wgrsnzxlpjruteoytpij^ilzxxb[\Zdjkbb`mia]SJ@e{thonfmwylijtooqutxlztrsnifd`pv|obmodThtog{ük_jdcRUdf]W_`fezwjfulb[ZGDJU`_`X[^fdl|l[ws{|ĽwtlljxzilnpulguxuowripW\c[WP`ekyso{tkabZWFHOJJUbYTQ^eYFHkt{ʿʾ~igm{{qhs|ww}bb]gghrjZXgcntwwe`eoelp||cZ[Q\^a[T[cj]_[_fnhjm]gqrkm~|xjrtxswaZZ\cjlhhappmi^Y\|m{|n]VR`^`^``e}n^kkUGRSPRSQJLJYdqpemtsq˺wgpptles|{swv~{yv~vjqzzftwk[^]eknevshUHB@@CWZSBO]a^Q[ZUgɲ{hvyxtqyljjiceTZa][iuojfxylg}sp|iZV\]OLTilcXbij}{rozvqp_bat}|~xs]Y\S_foj`dovlljpowouf^aedX`fsv|wfakf^VMTC=@HOV]_c\pw{vk}̻}}zvnfbvy}}}y{lcrx|~~z}sp}qqlszulwy\JD:BENIEP[YVXYeao}y¾xx{|q~ql^TLROZklbXjt~upsqsdakxk_KIYZ_WbkklvfdzjZ]cmitqn|{~}m_X_g]XYpxsejvuihsfZegkqj\dprbQ95:2=HWVLQeqors|wqjnxslqz~}xxomsfbert|{zorTSa[KNGE5XUbs{f{wxDZtiN#"-Vǵɰ}Zui^ahjkzywSfP_J>:JXWflY4F4(;&ACFV_ajf~~svӺ̸ֽwF03.[dͿ^QhCOAHTDW@MJTE[LLh\tzqeeXchpQ_`blRWaxpuxwWbuXr^phbpd|ǹwr}~iwlmlXEKNcxܷġ~pi\fhVR?02FCDPhlclfgVq]oYbemhown}|zqox{x{ixiz^dQMZLgz{TbBXTYTJ]]j^s`~|vVlScXBF]\ZZa\EhVSjf­ƭb_{rscgxoqqtu~|}W[KO?Q^ӵnw{bfubUXPSTXzuh[YT^bUe_xfl~u{s}~gZs\LY_UD\kƹԻum7JF-E.L\yu}iz{bxntpbCTSTkW]z{|e{{]kk{ow{ujnv|t`rvoZ|imTdVa:J2D,49Dy}ֽ}~wvl|teSW*Pji}Ul`i_Ui}yqonnQShN\HVNCmw}ȱvhzbDGB2!0!#MbƝyvduh`e?Tb\RSP^Uinr~w_m}yadfyp{g}}gfwpbJNJW6XG9?Dn⵿|lUP\fxtoO@IIhkqvo~}gXdvJ"'3>gntpomM.47;>,B6I>K`i^ǪyaxyqZatlbl}knbcXahutvQqxpY^|t}ivx}tvipa_`^qZGZcnXjjnyrv[X~rvŷ{xZ/9DGNYkw|quyoSHZecZjlnrdicw|s||{x}~^hy[pry];C1;>dz~}ryu~|wzkeT=QUYp|fD[h]bYks~~zRjqfmmfU7\QP]joy|ijvi`MHQJzxgbpzrsw|}e}z{w`Y}xiuwlqnsugUilub]m~l{tyvĻ|kiI4 -.(:F9?c_yǼ~waWubr~rfLM]V]IOfwwru~}zizqszn|mZrxwcWmwrpjYcsbokMQ2@CDjejġ|}zYmZmdWokaVRnp}vҰp|g\NO_Zjlf^Ki|os`nz}uf~{xjaO:M?<%.-@/EHfkǶ}jkitr_inssnrzv`kaYifzxZg{{u}~ifqhO]Khz{u_UfskmopniHas|x|uOkzi{nkpbqmk|rt\na];`mdqdhmQXyym[9bzv{xn{rhczZN[`@N6Ya[gYcc_wdfniY?;GQmvhVR_}i{teIR_yYEEOOho|jrz}pb`WqmfIGJZktwtnkxq~x_VZ~ut|}rlz~|xvlZfzvsmT[byvz\q}obeU63)WYX7EGGPTlw}ľɷks_Taja^=L[_Xc{m|nxsot~qplo~}oplf]m_Vgs~uQpy|kZ^WbfQ4PhpoNiglferqnɪwfc~~}xkup_z|nagOPL=Cd[ghjyvs~||qinwjx~U]B@A=(+3TekNTbjyoqqtcj_Xbllwyt[ss{ah|ryxXvnkskgZYifdwyoybnthXcVptsueffgjoqxuxrienaVZoo~Pab[Q]jpes{xllsv|xdttf|}yaaaqdcg\Nek{fWUVcOQehwƩsiubO]^knmzkzgfqwjlfzxob[TUJOLV[tw]kknvlqr{urseRUgntGQX^K@LSNggy}impVp~{{|~x|crmurqwZYzo|b}z|eprp~|x]BbG03K^b|}sm}^vuXe`tqz{rkmoopy~oypimymtxvezT^]XfW[dgUymvnynkjvwm[cjks~nt}nufjmxxixvyvuwsy}sylsunXiq~tfhd[[hqodkdkg[TSNJQ[mtýʵ~w{~|jqueleyyyfk]e_hoy{xloi||wvvpzregjp_sluZR_HWghiwm}xx{zt}}~vvdtj_ohsxyy~{zhJ^ONRRZQOYUPcMF_ejhvxow}ztq{y|srpu{xyglnxixuutjc[bXXSg_jrhrf}py|u|}{rnol{uhioro~wyzu|omtnzvri]Ta@Ubam_ihmjskek[mmsq{vwwzy{|tx{rvlrutz]mkWWXXfgy~~smukq{vumnrqixlz{ik]\`dqwz|}oomp}v~yuyrfp~~ygslxjuxylynlarn_iXfYuZ]\P[Ikiheoents~}wucuw{{~obx^af]fplw~qtyemry}wsfgnnrXS]Y^]_iX]cWUige}y}}njhdiqzhrmpr~|xx}~fzcxpcr`ZWeI^LPKHKKXY{`nmysxtvnpruzx~ypvs{p{kfxovv~{osphYfcYud]ZXh_cvrnymfljhaloyxz|zx}suup|{{o}idfepuuuq|xmk\^Z_OkWieegkq^fR\TYhmq|ffvekz~}kvuu||{}~r{|r}ruqxxeecbikkylpxshzzvt~r]q\]mklvoni^e\Q]``kwox~~byhlymp|x|u~ytryturrwxpomwcpgol}l{iiw]aNXLea`rfwbac^Lmkww~z{|uwhwfp~gfmrunypwuu{vqqu~z{ct^OYVkhbd`SdZT_bnq{}wx|rzvwu}zlxy}ppegZswu{jhecbTEHEWgcohpwpluux}x}{vxzrz~yy{}~k{~qziciukn|h_maZYcxm~|ifudZcurpoq||yqs}phqrz{xykt]MUbc|aufvihiU^gn^y{ortjhs~f}u{}z}z}{sqzio~~zox}k~z`gg||z|yahmmiklnofkX_fbzq`__p~u~yyw~v~uw_gu}ypmkaizyysovhd\j]hW]se{kc__jvomvgy{{xs{wzyu|xqtbdly}lhpuumk_livh_YSWlUNbduovpnmqyu~~yqm~~sqqwyqmlkgtlgrmqfVS\nWTd]gnt]jyy|sftofx}bsghv|Zdnx{htmnnslmuocchkvnz]`^Veab]syvjsxgg\dfxy~utzxvwqzzpot}zyynbXplVfgnnlj\hksgzo_]e^Wgg|tlxg`[hj}||pzm`oxufv\mn{y~~wlyjswwkwrjxjrjsrct`RVLVUiilpxoSkVHQft{{{xxz~nvubpspitxor{ew|kw{kjbjlRhkqn{kopTZMWYWexnp}\q^^cmq}~qriuu|z~xrpg{hvv|v~p|~x~twejfg`txm|ndUMLAAYb`lnvmra`_aid}vx}wmiys~z{|~uoszfr{}mzsia_X^_[eotomcjRIURlls{ppecm]tt~{zy~tkuusz{st~p{{~mvs~wlScXX`rr{ptpTlQLU_\cmpqiwmX_i_kyv{miyxy}xxolnmjovvqtgudaQTahrw{}o_`XPVhozotmlmc_uvghnzrx}~~~vyw|wz}nimnpm}qrkWaj{rzzgilW_M]Z_jjjktVY_emhsu{y}}}}~zqfshfn}}v~gS\`fxyyqth^_^_ic_kqheiiop}|kwmu}}|}{{wy|v|ytskurtzgddssukhqjQQdeXXdffol]gmnx|ml|wp{}z_ds}ytufk]_jslptlsdhbkpi`bncvvljm}}uz{isprx|v|y|{totzw}|lfhkogoztlke]fesrfcV`qk_iszumzunrptxrtxgjjy|ut}pdnsoiigwt{xqqgkuwdlefkdlev{zs|tbjedl~zxv|qfq{z{{xkmz}xtuigzp]jsuojm]Z_`^liduurnwueir}mvxxj{tsvwvwzuhmvfpsqzqggswrp|mS_^Z`v|~yxsfyn`jw~ggrnv~w||pv}|{neecjhjhkrrjdq`aRT\kvs~orvgdnw~~~|v}zi}~sry|{snluyn{k^hjccp}wxwikh]Wajooy}usynhfairy}zms}p|px}u{xxuxs|r|usxssobh[`chvsw}x^k]UT\rrwwmzp`qmrjyz}~{}}~~tw|s|~xwfoweix}{q{qfj_hrko~yvhudW[^gpnt}omj^ijitwxv|}wzwzz}{}{z|s{ruslov{{}qce]^durzvwofe_YZf[irurprlbvmsyyz}xwuuvvzx~~pjrjouxwtth^`cmrx{vs__h[`jqqpilrhohu~nozwu}{w{w{t{|hskqsyzaweY`jokrv{ivsZeb_`mnyp~ygrwrx||qt}r}}u{~}{qx}v{}vxggmwwzpjcafj{zyrtngbttpijrnpotruxn|uqks|vq}w{zz{z{qeip|ztsnobhklv{uuneenzhquqnzmitu}qmvsn{r~u~wz_inz~~xgrqqx|w{}y|ovvr|xikegtuqlw~t}qumlt}||ut{{wqt}zphyzvnvv~vklpqt||pamvpry|{|xwot{rlz~}wvos{}tvzpqtn|tzt|yz{rjnohov{w{|not|~w}~~x|}~wqpr~rzkj|ypn{q{ufomlwqnos{{{rsxry|xx|{|tzuqv{tvr{v}kmhgnx{lxzpmxyv}urzquv|~}qfnsnwz}yrxffktutxsu~vyzzuzyzux}|kpqmv{ufvp_cm{zy|ouuqwz~v~u{~{ztor|xw|}zsvgfjt{{trtdfiuw|ylr~|y~y}w||yw|{pv{vxyuvrldgq~wvtvniafkqz|{qtl|{{|{}}}}yzv}}|{z|wtznopt~}sxlafhv~~qyperjups|xyyxrt|xtxtys{yu{{~{}}z~uzv|~}xnuknx~z~lreghnwx}~y}oeosjqytzltvmrvru|rz}z}~zwyy~{~wx|~qqimsxxnrojfrru|}~x|niwlmqrvpvqw|{uvntqqt~tyy}wpytvw~xwjnowy}povmqp~}zstwloiq{ulsnrwozvy|szvpzywv}{qw|}~xztv}lqhnq~~{|ntrmlst}s|osuq||xxlqvjqv~~|pynrwzzz|v||xtry~~}yrpwz}}vlm|vxxqys{wuzwxr{sojqtvruyxkltxzotz|zpwwv~}}}snxxv|zttpvwysntsqxxr}xkty}zx}~|quzpyx{|}y~zttop~yy{|~{}uuploz~upuw{{z~}}{w}}}yvyw~ovz~z}yxtsomxvvss|}z|wy~x}z|wz}q{ytxy}yz|ypmv{}y|vz}~~~}{wz|~v}~z{mutuwqopsx}s}yy|}~~w~{}yvspwlx{qqr|v||~{~w~|nv{y}~}sowy~}}v|sx}z|ss}|~{}yzyy{w}~n}yjpr~~vxxutwz}z{zy|}|}{~~}{~}tx|utpx|x}rnu}||zjr}y}y{~y~~y}||wvx{tw|~~|~}{z{~|zvmrty|}rnqqx||{vsnzv~{wx}wvx}}r|}z||xxz~}w{{|}txv{y{|vonu~z|vlypszyxzzox|vtsssxv}~~{p{mqu||uyy{|~|{|s{qwyumqst}}yxkxvut||{tzzqrwq~vzowr||w{vxsuwv~prv}z}~xyysrzr{wporz}uovrvtyt~wy}wzwjwrlpv~x~|wpp{~~~x{~t|r}x{{sry{}||v}qwx~z~trm{}}xxutvz}z~uyqnwpvr{~xxjmrz}}wz}}~~~}y|}tylsv}}|t~wxtx{~z|yzvz~quotw}~|yrvopv}{m|nuu|v|~|{v|{{w|xuwx{qr}y|{{~y|xtkvytzx{zvnkp{}x}xuy{uz}tt}}xy~}s~~{w{|~|p{wv|~us}xnmw~t~uu{|z}{~zxt}~~zuty|v~tuw|~wsrz|~q}~~{|y}}|yy|xytvxwwuwy|xx||}}{v~z}yxuyxzx{|yx~~}x}z}~r|z{wzt{}t~||z~{~|~z|~~}{||tzuvy}|yz~z~~yz}{|u{}}w}y{y~}vq~z~~t|w~}zxru{~yz||z|{|zwy}z}}|ux}}y{}w~uxxw|qu}|~}~|z|wqz~}}~ywry}{z|||}rw{~{szy{~uv~}{y{yx{xzux}y|}yx|qxx~|uz{}|}zzz}vwwyuzyxz||~t{zxvty~{{|~}}p{upvw|x{vv{|v|y|}vuz~{wt{|~|r}y}~wwwvy~}zvxxz~vxv{pvx}vrotv~t~z~|{wzzy~}tvyyxxyyzyz~zy{|qy|qtx~~||wrtx{zw}~wwx{}{u{~}u}{y~zv{~~zy~{xwp|~{zz~|||~vp}uvy{y{xrnrx|||tyz||x}}u~}~xz~~~{|st|~~w|{}{~z~~z}~xxyw{}z|w}rwy~xp~qqxz~~q~|xx||}yzy~z|{{zz}~sx}~|}}xqxw{{sts|}xvvy}{zwv}|~{w~}vvz~|}y}v~xzuztsvy|zwy|~w~{}|w}~{|{{}~xy~y|x}vzvz~r||~z}}z|{~z}y~~|zxz~y~w}}w{{{|~|~|~||z|}|}{}~}}z}}{{x}~{~y~|v{|z~x{}}v|y{}{~{w|xz~|{y~|~w}}||~~{~}xu~}s||}}z{{x~zyw|}z}y|}x||{szw~x~{~~~w{|}tz~z{rz||}zwxwy{|~zy}wwv~zxzyxy{|{}|v}||}xz~ssy~~}}uxyyx~z}z}||wzz~|y}y~{|x|{zuz{~z}v{zvvz{w~swxz{y}{|||}yyv~y~xw~~xzwx~wz|xzzvy|w}|~{{utsz|{v}wqtw}~r|}~|~w}~z{{x}{{}xz|~|{wu|{w}~||zy~~~}||vzxv{~z~~~rvy~xw|ytyy}ux}z{~yz|~|w||w|~yyy}||{~}ws~}~z~}{xr}|vz~{{zutsz{|}z|{z{~}y~y{t{|w}~~{v|{~zz~utz{z~~y|z~y}~||}xwv}~xs~wsw}r}xy{~x~y|zx|zz{{{}}{|y~~u{~~zuz}z}uxz}{y{u|}}|sy~~|z}}{|x|~vwy}~zz|}y|~~z~vzx~~~z}{~~|z|y|}~}y}}|wy|}~z{}zu}|{~v||z~w}{{}y}}}z~}{w{}{|{~{~}~~~{|~~~|}|~~|}y{}{}~~|~x~}~{z}~x~~w}~}|~v|~||}|u~~||}}}~{x~~w{}|~~~~~}|y|{~}~}z{|t~z|z|}~{}z}x}~}wz}~~{|||x{z~}x}~~~y|zy|y}{z{~}}|~x|z|zvy|~~{y{}|{v|}|}~~w~}{zz~||z}~y{xz}}~y~{|z|yzvw{y~{yz||~}~yy||~xz}|yx{~xzzy~}y{|zxy}~x{~|x~{wy~|wwxvy}~|~w{~}~|~zz}zw|u}|}z}yy}}zxyvz~y}~||x|~}|~x~~|{|||wv|~yw~}vx{~tx~{z|{x~{|~yw|xy~zyz}{{~{xv}}{}~zzv}~{y~{~uxx{}y}~~y||~~}zxw|~~v|~|z|y~{z}zxvvw}||{~z~{y~y~}~~~~{|x|{uyxw}~w|}z||z~~yy{u~z{}~{|z}~ywzz|y|~~}~}~u|~~~yx}{wxz}ty~~y{|zw~}~~zwy}}}~~~yx~~|}x~|~xz}|~{~|z{y~}zywzy}}~}|}{}~w~~~~y}{x{|~~w~~|~{}}u~{}~|z~~}yzy~z|z{|z}{~{||z|{|}zx~~~}}u}~}~~u~}{zzz~x}}}~z|{{}z}~|~{~v~}}}|z~||w}~x{~|{v{~w~{|~~{}|z|{~~~x|~}|~}y~~~|{z}|~|}|~{y}z}~~}|w~~~y~{{}}|||}z~y{z}|~xz{x{|}}z~{}zzw}|z{}z{y{{{|{{}{x~~}|xz|y~~~}~~z{{z~zzxy{{~{~y~~}|||z{y~v}}}}~{yz|{wzxy}}y}~{z~|}|~y{~~~}|x{|x}{y||wx||{|{~~|y{vzz}}||z|}y||}~~~zw{{}}~}}w~~}|}~yyx|}~z}{{~}||xx~~yz~|~|{z{{|zzywx}}}{~{~~zzx~~~{}~~|}{~|{{zy{{|{z}}}}}}|z}w}}|}}|~||}ywzz|~z~}}|}~}y}~}z}}x|{~~wz}||z{w|~~}zz~|{~~~~{y~}}}x{{z~}{}}~|{}{{~zy~z}}~}~z~}{|{}|}~z{~}{x}{~}|~x~{}|z}~}{~}~y~|}}z|~{~{{~}~~~{}x~~|~w|~~x~~}{~w}~{~}}~y}z|{~{{~{|z}~~{|~{v}{|}}v}y}}||~y~|}|z~z{~~|~~y}~|}~z{|{{z|~~~|z~}y|{{}|{||~~z{z{~~~~~{|~zz~{z}~~}}|{{w~~{y}}}~yz{}y{}{|~z{~|}x{|x~}~}{||}}wz{y|}~|~z}|z}zy|}y|~~~{~zy|yx}yy{~~~w}~|y{z{|y|}~~|~}{z~~zz}|{{}}xz}{z|~}}y}|w|{}~{|{{|}~~xz~{|~~~}zy|y|~}~|}~y~|~~~y~{x{~{|}{}~z}xw~}z|~|{}{y}z|y||xy|~{|}}}z{w}{y~}}{~z}|}~x{{z|}{|~~}|{~|z}}x|}}~~|~||~ww}y{~~~y~}|z|{}}z}~~~{|{y}}}~}x{~~}}z|w~~}}}||~zz}~~{{{}~}}y~{~}{}}{}}{{}|~~{~{z}|~~}~z|~~{zz~~}{|~{v~{}|~}~~}y~|~}{~|y~}}~}}y~}~|}~{|~~|~|}{~}~}~}w}~}x}~~~~}x~~~}}~x~}~}z~~}{~{~|~}|~~|{z~}}}|~{w}~}}}}y}{~~|~z~{y}}}~~}z~}~}{y~~{}}{~}~|~z}~{}||~}~}z{||}}~}|~}}{|}|{~}|z~|y~~{{~}~}y|}}}{~~~~}~|{}~y}~~~~~|~yy~{|~~|~~}z~{y|}z~}~}~~}z|zz{{|}}y~~}~}{}zz{z~|~}{~||}~x{~|}}~~}z}~|z|~}}{~}y~~|}z||{}~~~yz{|}~}~y|~~z{|||~}{|~}{}}{{~{}}|{yy~}{~}{||z||~~|z}}|{|~}z~~~~~|~|y}|x~~~}{|}~}}w{|z|~|~~~|{~|z~~z}~~~~}~~{~xx{{~~~z~~~z}z||z~~~}}}~~y{~~~}z}~}}}|}y~~|~}}~yz}~~z}||~}}~~{~|}~|}|{}z{}}~}}~{~||~{}~{{}}|yy}}~~}||{w~|~|}|~|x}~}{~~|w}|}}}~y~}}|~}}{}}}|z~|~|}w}~}~x~}}~}w}~~~~w~}~}}~~z~~|~~|}|}{}~}}{y}~}~}~{w~~~|~}~zy}}~~}z~}yx~|}}~}{~~}~{y~}{}}|}|~{~{}}}{|~~|{z}|}~|}~{~|z~~~z}}}{}z}~||~~}x|~|~{}~~}|{z~~z}~~}~~|}~yz||~}|~|~~{|y||z}|~~~||~}y{}||~||{~~|||~}{z|y}~~|{~}}}}xz|}~~~~{~}{{~|||~{~~~~{|}{}~yz}|}}|y|~}{{}{{~|{}}~~||~||}|z}~}~~|zz~|{~}{|}{~|~{z~}||}~}y}~~~~}|~{{~}y}~}}}|}~~}x{~{|~|~~~|{{z~~{~~}}~~|}}yz}|~}||~~~~|~{{|z~~|}~~~}xz~~~}|~|}~}|~z~|}}yy~~}z}}|~||}|~}~}}{|}~|{~}~~}}~{}{~{|~}}||~~zz}}}}|}}~{z}~}}|~{y}~}~}x}}~}~~~z~}~~|}~}z~}~|{{~}}~}z~~~~|y~~~~~}w~~x~~~~~~}}z~}~}|}}z~}~|z~}~}~~|y}}~|~~zy}~~~|~~zx~|~|}~|~~~{z}~|}~}}|~|}}~~~z|}~}|~{~~|}{|z~~z|~}||~z|~~}}}}y|}}~{}~{{z}{}~}}~~}}}y|~}~}{}~~|}z||z~}|}~}~|x{}}}~}|}}{|~|{|}z~~}~~|{~~}~~~}xz}|~~~}~~{~{{|{|}{~~}}|}||}y{}}~{~z|~|{|~{z~|{~}~~}{{}}}|y|~~~~~|{{|z}~~}||~|}}{y~~|}~~}z|~~~||~z{}z}}|||~~~~|x|||~~}{}~}{{y~|{~}|{~~~}{x{~}}}{}~}||}{|}z~~~~||~~|xz~~~}}~||}|}~{~}~}~~yy~|~{}}||{}{~|}~~}z{}~||~|}~|}{|z~z{}}|||~~}~z{|}}~{}~~~~zz|~~}~|~~zx}~}~~~~|x~}~}}~|y~~~}~|}}y~}~|z~{~}~~|z}}~~~~{y~~~}x~~~}y~~}~~|y~~||}z~}}~|{}}}~~~{z|}~~}~~zy~~~~}~~{y~~}}}}|~~zz~}}~~~~|||}~z{~~}~|}~}}}|z}~~{|~}|}|}}}~~}z|~~}|~~||{}|~~||~~~|z}~~}{~~}}{}}{~|}~~|x|~~}|~~|||{}~{~~}}|~}~}y{~}~}|}|~~||}{|}|~~}||~~}~~|z|~}~~|}|}~}|}~|{~}|~~}{|~~~~|y|~}}|}}{}}}|}~}~~~|z~}}~}{}~~}|{{}|~~||}~~~~~}|z}~}~~|{~~~}||{~|{~||~~~~~{y}~~~}{~||}|}~{~~~~}}}y{~~~}}}||}|}~|~}}~~}z{~}}|~~}}}|~|}|}}z{~~}~~|}~}~}|{~{{}}}}}~}~~~{|~|}}~|}~~}~~z{|}~~}~~zy~}}~~{y}~~}}}~{y~~~~|}}z~~}~}|}{~~~~~~|{}|~{y~}~~~|y~~~~|y~~~~~|x~}}|}z~~}~~||||}~~~~zz||~~~~zy}~}}~~zy~}~~}}|~}~yz~}~}}~}|||~zz}~}~|}}}~}|{}~z|~|||~|}}|}~~~|z}~|z}~|}{}{~~}|~~{y}}{~~}|}|~~{~~~}|}|x{~~~||~~|||{}}|~||}~~~|y|}~}{|}~}||}{|}|~~~}|{~~~~~{y|~~~~|}}~}{}}||~~|~~~~}{|~~~~~|y|~~~}||}}{}}||~~}~~}{{~~}~~|z}~~}{{|}|~~|{|~~~~}{z~}~~~{z~~}}||{~}{~~||~~~~{x}~~~~}{~~||||~~|~~}}}}~|y{~~|}~~}|}{}}|}|}~|y|}}}~}}~}|~|}{}}y{~}~}|}~}}}~|{~~{|}~}}}~~}~~|{}~{}~~}|~~}~}z{|}}}}~~{z}~}~~~~{z}~~}}~{y~}~}}|z~~~~~~}}|{~~~{{}|~~|z~~~|z}~~~|y~~~~~}y~~}}|{~~~~~~|}|{~~~~{{}|~~~~{z}~~}~~~~~z{}~~~}~}~zz~}~}}~~}}}~~{{}~}}}}}~~}|~}{|}~}}~}~~|}~~{{~}z}~}}}~|~~}}~|z}}{}~}|}|~|~~}}~~|z|~~||~}|}{}}|~}|}~~{z}~~~~||~~}|}}||}|~~~}||~~~~~{y|~~}|}}}|}}||~}~~}}{|~~~~|z|~~~}|~|~}|}}||~~~~~}|{|~~~{z}~~~}|~||}|~~}|}~~~~~}zz~~~~~|z}~}|}}|}{~~}||~~~~}{y}~~~||~~||||~~|}||~}{z|~~~||~}}~|}}|}|}~{y|~}}~~|}~}}}~}|}}z{~}~}}}~}}~~}||~}z|}~}}~}~~}~~~|{~~z|}~~}~~~}z{|}}~}~~~~z{~|~~~~}~~~zz}~~~~~~{y~~~~~}~|z~~~~~|}|z~{{}{~~~|z}}~{z}~~{y~~~~~|y~~~~~~}~|z~~~~|||{~{{||~~~~~~{{|}~~~~~~~zz}~~~~}~}~~zz~}~}}~~~}}~}z{}}~~~}}~}||~}z|}~~~~|~~}~~~{{~}{|}}}}~|~~}}~|z}~}|}}|}|~}}}}}{z}~||~}}}|~}|~}|}~~{y}~~~||}|}}|}~|~~}||~~~~|z|~}|}~~|}}|}~~~}||}~~}|z}~~}|~}~}|~~||~~~}|{|~~~{z}~~}|~}}}|~~}|}~~~~}{{~~~|{}~||}}~|~~}|}~~}{z~~~}||~~}|}|}}}||~}{z}~||~}}~}~~}~}|}~|z|~}}~}}~}~}~~}}}~|z|~|~~~}}}}~~~}|}|z|}~~~~}~~}~~~|{~~{|~}~~}~~~}{|~|}}}~~~}{|~|}~~~~~~{{}~~~~~~{z}~~~~~~{{~~~|}|{~~||}|~~|{}}{z}}~|z~~~|z~~~}~|z~~~|}|{~~~~||||~~~~~{{|}~~~~~~zz}~~~~~~~~z{~}~}~~~~}~}z{}}~~~}~}||}z|~}~~}~~}~~~|{~}{|}}}~~}~}}~~}|{}~|{}}|}}~}~|}}{z}~|{~~}}~}~~|~}|}}{y}~}||~|}}|~~}~}}||~~~}{z}~}|}~~|}}|}~~~}||}~~~}{z}~~~}|}~}|~~||~~~~~}|{|~~~}{z}~~}|~}~}|~}}|}~~}|{{~~}|{}~||}}}|~~||}~}|{z~~~~}{{~~~}|}|}|~}||~~~}{z}~~||~~|}}}~~}~~}|}~~{z|}|}~}}~|}~}~}|}~~|z|~|}~~}~}}~~}|}~|z|~}~~~}~~}~~~|{~}{|~}~~~}~~}~~|{}}{}}~~~}|z|~|}~~~~~~z{~}~~~~~~{{~}~~~}~{z~~~|}|{~~~~||||~~~~||}|~{{}}~|z}~~|z~~~}~|z~~~~}}|{~~~~||||~~~~{|||~~~~~{{~}~~~~~}{|}}~~~~}}~|{|}}~~~~}|}~|{|~}~~~~~~}||~}||}}}~~~}~~}||~}||~~~}~~~~~}}~~}{|~~}||~~}~~~}}~~~}||~~}|}~~}~~~~}}}~~}|||~~}|}~~~~~~}|}~~~|{|~~~}}~~~~~~~}}|}~}}||~~}}~~~~~~~}}}}}||}~~}}~~}~~}||}~~}|}~~~~~}~~}}|}~}}}~~~~~}~~~~}}|}~~}}}~~~}}~~~}|}~~}}~~}}~~}|}~~}~~~~}}}~}}}~~}~~~~}}}~}}~~~~~~~~}}~~}}~~~~~~~}}~~}}~~~~~}}~~}~~}~~}}}~~~~~}~~}}}~~~~~}}~}}}~~~}}~~}}~~~~~}}~}~~~~~~~}}~~}~~~~~}}~~~~~~~~~}}~~~~~~~~~}}~~~~~~~~~}~~~~~~}~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m33.bin b/applications/SprPoP/assets/packed/MUS/m33.bin new file mode 100644 index 0000000..5c9c638 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m33.bin @@ -0,0 +1 @@ +{yw}yzy~}}{}~y~y}v~w{y}|{{}~z{xy{zy}}y{w~yw{y|{}y~y{|~}ywyz||z~}y|{|yy|y~~x}y{{yw~yzz~||{}|y}y|v~w{y~|z|}z{xz{{z~~z}yzx}z~|}{~z|{|xw}xy|}zx}{vzxzvwyx{|~v|xz|zy{}|~~}{||~|~}zyvywwvz{zzu}~w|zrxszyqw|tzv|wwy}{x~{|~{|}~||{|vt||tvv|yv{~su}vw}y}qrspyroy}vr|yuzrz~~}v~w{{~|}{}{~wsu~vtuyvz{s|wov}yryyzxlsnqulpzxrt|uvur{{}wx~|wy~|}}y|srxzrut}|s}us{qpy{us{xzpmqjspkuyvrx{rurtzyzs||yy|z~~~z~yvrtzvsuvuw|ovwnuzytwzxvmplltlpwxus|wqvquyzwv~}x}x{}}}v{|rtuxvut{{t}toyrqvxzszyvsnnipqnrv{tv}qtwnxxzxy}}y}y}~|t|p||qwour|zwxulrzuxl~wk~z{pmghxnvyzsqqt{prrs|vtz}zw~z~}|vxquvqrvtvzurjxxyqqnt~x{lmdqvpxz{ox|m{vnsrv{xt~z~u{xxz}svuvoxsz~s|romzwxk{}j{x{vijdvrrv|vn}sp}norq{vxx|z}z}~z}tyxnzytqovp|vtvrkt{zvpvqypnim{ty}uwq{|ptsu~vu{z}w|~}||~{zvyyrxwwv}uqlywxmtktyvwfi`orosxwjwvjypkons}vw|t|zz|w~}ptwmsxoqismvyr|ztmq|w{pqwpom~x}|v{xwuwv|yyz}yxyz}~~v{{x}{yt{v}}}wkwvstl}po{vtgg`hpjurukozmptmklmrwq~rz|{{}tnynnwjqnjmout{uyrk}uxx{vyrrk|x{y}}~|uwy{~|}y~}z~uzv||vszzyu~ztw~zwrr|pqqxsn~{yfibcqeqzplmurtnrrjmposx|rx}|}|}z{~owvjymjsohoto}yp|o}zovs~myqpwt{y{tr{uzu|~z{pixqpz{}~{~yuwwhz||xvw~uuiuvrzgcp_pei|wtjostwpziewnmyrmu}xz{~}~}t~t~rvhts`vxhhqntqqqr|yux{{ktpvzt~|xmyzx}}o}q{xw}}vjuxq~}{qs~pwowmzvstnumws\gckpgsyptrruprqilojykr|~r~zu{qt{pvyfjxmiwim~t{oqwt}y{xniniw}~|x~vt|q{yzuvz|~omzvy{|vxozwy~wwupuwwrryw`cYimnynrmp{uohxpgqfhp|~o}zz{|}wy||yn|isltnoqs|ny{}|srx~|llns~~y~}p{vrysuoxz{tzwz{ux||y}|yt|ws{~zzxuuxptwztjikbspwwue|xkuhjefmceu{wuxo|{vz{}y{xxpol^trprhifwzrzrz}vxuz{u~{xymnvuwrtmp~{~ww|lt|z}uxy|ks{z}qyetwuqttwyv}ywuctlo{|yot|w|xiebjvgy{o{{||zt||tv}|mrqp~tqnpurwlrvx}ynkn}}|sz~zukizzy~w}zx}ulq}|~~{wyvss~{z~x{v~wtwxwoy{rmceiwqrv|zrtpkvtph_cqks}qt|yyzxx|wt~~ossq{xnsmutuop{{u}|ysjnxy~}wu|znjtyvw}~u}~ypnw|}}yzyquz}z}yuzx~z{x}||}wwvnhajuvn{z{|suppxso`djjnutmzzw}|{vzz}sxu~zstxo|zontnvzkrwx|w{~tngv~{y}yw{viwst}}|xywyyrrqszyx{mpy{z}~{|st}ut{~t~y{qidatunqw{}qvsqxtjcpcjqmos}u~|t{tr{u|{xq}sxqmpum~sj{tx~wz~xpil~yw|yx~yor{l{|xzu{z|}ypumxz~u~ohuyw}|srxzxx|~~{wz{|jfanzqr~wxtzrvypeojbrklqyxwxwv||myvvtv{ytunnvnw{hwys~wy}qmiz|v}~v}{ro|ou~vz}y|y{{}rtpr}zq{i|~wu}wrlxtzzlysrr|~~{}rm`~~{zytnqp_mtrohn|x~t}~pr~x}~}}~uwziotpyprtzujvw{|suvke|vu~yuouytzyxz|xwmmvm{~{{~sroqvx{zvtvyx}|~z{{zw|z||o|~wblhozvzvuuslivsywm{yy}mv}yys}|irspufmxppjhrrnwqy|jpzstxsyrr}xn{{xt|y{}vpisqwvhtry~~}z{{~wx~~|xw|yq}{uockz~{z{vzyomipqpv{slxyrtouwtw~}{|w{xustvwjrokpgp|vtk}xurusxys}~{psz~w|}{~rngp|u}zkz~vwu{}trpy~s{uzzx~|xnuyw~}y~o{ukh^jxossluqw}mz|~z~rurmyu~ms|v{t{~vvjq|r{yty~}ueq{{y{z{t{w{{~z{~{lh^r|}~|xuuyvzu|rtuqyty}|xoxv|y~zz|wgu~yqilhlrq|qiwx~xot~{q{vo~yu|wzwnpwz}~|}thnt{uw|||nptomzruprvvvzz{lrdisxywpopsho|xymsrxszw~s}x{y|}wzqo}~}onqiqk{yfnq~~vktw|yytxn|xs{}{{~urry|vqqwzmwu|s}jfm~|swow|z{zz}}}w~ieevyszzzorprtu}~{onzsqyv~r{x}{uzkliz~}{qjjiwqplo{su~{q{{t}{uyvz}nmt||x}{|r{tzz~wtrw}pxyyllpzv}yqvwz}~}|~{qkpklwzxosnos|}{xwwpjo}u|||zyxpixryoirsgorxukswtvxz}|ts|qqo}|qx{{~z~uvq~vywuutuwv}szhm|~z}xzux{~~~yz{{{|wchn{y{zu|tnzvz~{hpmz}xz}~vx~ztpswxpwniksulpu~{xwsywt|}w~xutwznxvwt{}pqx|w}pq|rzuzijn{y}wxxu|}zxvigkzvy|}|}vy}unpu~|~}vkv{ny}|z|z|}{ntr~w}whmmxxqwqyyr~ysypw|}~yysxwqmw}}}zu~rvp|x}{pwtzxy}w}qpr|{}zyowz|u|t{z|xropqos{|v}rinr~~x|{zzsuqn{v{x{z{zwsw}ztmopwqp}{mmvw|}ooux~}w{{xyop{z{{uusvy{wvuwzqyzu||wqm|}squ~y{x}}|{v{xuefttzxiqqwwz~|vyxtip}v~{z~s}}}z}s{yvrspk{|~knoy{souuzx{t{~qo{wyr}}|{{{sy{}|wwpqvyyyhitzzpvt}y}{w|}}z{vxdgjuuyvpmt{pm}wysvkuv{z}w||||su}{zwkmvtwtnrp~z|rvtozv{{twwxlww~xxswz|ssunuwzz|spiru~{sz}tx~~vvz}~xvyzsmhinw{zuwxoqpzzzuyu|{xwos{{zyt{}prt{ytvzzpmx}}|}{nvpovvxuzxsopxv}{x~yv{sqrsxvsyrohuzwws~~~ut{{wvztleox{}{~vwvnuoxypvzmqnsy~z}~ss{~}y~{zsrrtzxxxty~}}}|{oonrxw{x~}}{{urom|~tw}z{x}xxtvyopw|}nuo}vywq~|zu|}w{zxy}rqktx~{~uzpjvu||{sxvzujqmz~wx~yxrt~tsppsx}zwwzsomvrlv{~tzxwsruw{}ty}wz~yxqpur~vxysruzvtwz|~~syzyw~}{zqmp~yz~xvrprwwy}|}xsvzxjpwx{z{z{lprumrovz}{u||}wsrumnpz|{|qt{u{wty~|~z|w~{}ust~wsvz{ss}|txxt||uu}|z|wyz{pqutvwrzsptu}zzsqv}upvw|y~~}~unuy~}|xsppx|tor|}wynlpr{vyzvuvwy}{vv}~y{}||{w}zyxy|qq|~yxsr|zxwxx{zv{z~uuz{vujkvy{vtwrtqz~{wux|~z~wwry|~y{|tvs{||ypsyzqop}|{pmsstpwytwv{z|u~{x}z}~xvu|~~}yvpu~|x|knwzyzsx|~zwt|zssvztgip~}zxsqsnr}|{ynwzswwzw}zwtx~}uzwy~~|vtyyy~|wvnsmnz}|qu}z{urz|{~~{}vvxz~qv{ywv|qmpwtsv~znx{zwuu|{nkmxzxuoqqsn|~ztlow~tuwt{}~|}}{uyw|yyx|y}xpvw}}|ypiqu}{wzzy}|wmq{{zzwwz{~y{|woxx|wsuu{vtrvyz{y}}yttvrqkvz~~xorrrrr~~{rnptx~pkry|x~{|yvr~y{tt|wst~}xwpovxtqsz}z{~xttsz{~y{~|xt{xtsqzu}zqpu{ttuuwtz{zz}z|zpkm{y~{ttvonqww~}soqx{wjpyy~{{~}|ntzsrrz~zts~|}u{wxxwsqnpwy|ztzy}ytuz|w}yz{{}~zrvtr}zspqx|wuns~~xr{{{}}{~{~smlp|}~{zytmnv{z|{~ystvw{~xpuxz~|zuu{{}rryx~zwvwz~xxz}~zsqmoou||voy||tqwz~yz|~xw~|xvuzuz|~xpmxsrrv|wtx{{sv{ylkrx~}~}|rurtwrv~~~~~utuw{rsu{|uz|~svw|{ywx{zyuw||{|ywomqotw{~vtv{~xvpv}utz{vz}|{wz{w{}vrqv}uvztx~~wu~zsqzuplq{~~~{uyynqt|~zzzvtyxv|ymuv{yvy~wpry}ywwx}xt{{}ywzurpoptvy}yw{x~|zzqvz}{xy~{}x}||{}{zy}zxqt{}xxnuxxz|ttstnqv~{|~}}yrotwyzxz~~vwotzyrrt|~{xwwsty~zuuz|}srw~~xx{wwtktuuyzwuz}xvsx|~|x{z~{x}|}|x~yw~wtu{~zrrw~~v||ywwz}~|tqnvz|~{qrxzvoy{z~z~rnpyypqw}{ty|}wvvx~zwyw|xpq|{~||zvtstuuyxyzsu}xxwu{}~~{~y|uu{}}}suzzxpryuvyy{{~~yz~vs~smms}y}~yzwyumnz~y}xqqs|}yrrw}}}vv{sx}xuxzztqu}}xx|{tuvusqw~wvy~~x{tt{|}|}}|vvv{}~|wrt}}ypnx{y{sox~|wx|yt|xlmpx|y{zqopnt{}vwvtzxstvz{{w||xz~|wvz|xrqx}zv{}}ytvonqw}vxyzyos|~}y{|x{u{wuvyy}z|uppw|troqz~vszz}vyvmmov{}}wupoqpx~xutuz~}tqqwxt|}u{~}wy{w|ups~{xy||yonnsvz}}zwxvspu|zyw}|{}y|zxz|zw{z~uolxzosos~zyy~{~wyvkjr||}ztvpqut|~{tutu}ukpt{}{yu}z~{vvy}xtsy}zvwy|{z}zvpptruvy~}zz||xvuou|~|{||}~~z|ytyy~tonx|tsts|~}z}~zxumlt{zy~}|wxurvuyz{|xsww{wmrqt}{||z|xpszxqsuz|~~{rs~{~~~~}wuustsqvy~|sw|{xppw{~{}xxztz{|wrtv{~}uvuu}}v|~{}}wqqovz~}{}yzvtvx{}yyvvvx}xnnrywx}srz~{usvtx{qqy~zz{srpquu}wtv{|utrv~|xx|{x|}xyyvx||xrmv|wxwy|wy|v{vmis~|~~~zywwvox{}~vxy}zonty{stxxzz}ztsrw{wux~}uorsruyzwuy}vwvsvz{zx}}~wv{}}zxyu||}rnu~zyywz{~|wy{tw{nmqz~xz}~~|wwsnq}~{yuzvprsv{ywsx}vwz~xssvy{|{wsw~~|zvqspsw{}vuy~zwtnv{|}|}~wwy}wyyw{{y}yvtw|z~yxrt||vy~{yv{urstvv{|vpqqs~|wury|rpnu{yvtzuw{zvuuuy~|pnuz~}{{rqsrxy{ytz|trsw~}~|~z{||}~~zy|vtxx||wpry~wsrs}zy~{wz}tmktxz||trrvsu}}ztvv|wlpu|~~{xx|~xww{}|vrqwzsnqy~~{yrttsuu{}{y{{wwyvuyz}yz~{yxrsyx{vomt|~{suwv||z|}|zx{wmjnvx|~xxxtuqu~}~yvxuyzruuu{|~}||xtw{yqsvz}|ywtpt}~|zuvsnqv|~vv}~{vppx{~|y{~~yrustxu}}poox{wxvu|yuz}~|{~qmlrz|~}{vrtrx~yuvx{qqot|}|~~vuz}|vvwuwyzywtsz}}zsomovw||xwytoov|~~}y{}{wsuvoux}upqvyxusx|xuz~ywwoorz}~~~{urrsv~ywz{}{olos|yxxv{z}{tsqsyz{zvuy~upppprx~~~{||usqpx~}|}~{{zxwutsqx|{wssyvvus{}{xwy}~|y|xrqt{~}{zururx~{{{w{umopw~zuzyttzupprvz}}{wv|zwrqqmqw~{wyxutqv}}~}|~yyurttvzv{xuv|zvwuw~~~}zuv~}~~}wtsx~y{~~{vuutu}~{~{wv}yqnqw|zx{wqtx}vpprs|}~yvtyz{vpqoqvy}xux|vtvu{~~}{{{}yxtvvrtx{zvx|}yvwv{~}|xvz|x~wtsvxy~~zvuwtrz{}}xz|xpsuz|}{|zttux{{vrqrx~~}wps|}zvprrrtv~}xw{|xxxw|~xwx|}yyuusnv}}zusx}ywyxy{|~}}{wx{wwxrnsz||xwtpnx}|}}|||vtvy~}yx}wsuw}}zstvw~}zurt|}{xtsrnqx|xy}yzvwz{{wvyz}{{vqstu|yxzrpv~yzvsv{}~}}xsy|{x|zpoqz}{{uqpos{|}}xw|}vuuv{~}|y}{urvz}}{vvwy}yxxvvv|~||ztpnqw|}|}~{xqt{}yywxy}zvstxvsvuyustzxvrqx|}~~ywx~~}~y~xrrsyz{~xrpqqrz|}xwx}vqps}}}z|{vtww}}wxuswx{{wtu}}wqrstvx{~}wtrx~}|yux{|wxxuuqovv|~xvuy~{rtrtxz}}}}|z||~|ytrsyz|~xvrqqov~~yyz{yppsy}~z|}uqty~{usqu|||xttxzyvuuqpsy~~}wwvu{~zzwy|{~{{ysqqotx{{vrv||tutpv|~~|~{wwz}tqrw{|}yysoqrw}{||{}}{sttw|~~}~|~~{rpv|xrqsw|{ywttx{}ztrmouz{{}zzwty~{|{yxy|xuoqrpwy}ytsv}|ussry~|}}|wty|zsqry{}~zwsrussz|~~|utsv~~|}~~xtuxz|usvuxyyzwttz|tqoorx|{yxtv|~}{uwz~vtqqrru{|xttzxsvuvyy}~{wuw||wsrv|~|xvtspozvrru|}yw}yvtu{}~zvustyyyyvtx{vprpqx}~}ywvuy~~}xvy|}~~xuuqsvtz~}}wuw}~wvvrtz~|||{xuz{}~ussy|z{}}ztqqqu~~}|tssv}{yvz|uqsv~~|tsruzxxywuu{{zwsrruzyxwuzzzz{zx{}yvvtvwuy|y~}yvx{}xttqw}|z|~|yy|~xutuxw{~yrquttyz||~wtsv|zzxw~ysqsv||usttxzy{wrqw}|zuvvuz}}~}wxvx}|xvvy~|wywvxutwx||xtvy~{rtvvz{z|{|}~{|~wrptxy|yxvvuory|~~}vtty~}{}zzwz}|wsruz~zustuzzxvqosz}}}xywvx{|yzwx|z{xuvxz~z{xvurrx{~ztpuzzuxwsvy{}|~}|}~|sopvxx{}|wrqnrz}wuuvyzz~zzy{~ytsw|ztttuyxtrqpszzzvsv|~{zutyz|xxvxy}{{ursru{{~}tqqwz~|vvrqwz|zy|~upprvxz}xqpqpw~~wtpsyz|~|{}y{~yuvzxuuutusssppv}wuru||wrqw{{}zzxy{}{xuqtvtxy{~vsrsy~~ussqv{|}yy}ysppuy{~{wsqssvuporyyy}{wy}|}xwyzzvtppstsrpqw{vvuw|wusswz|{|z{}~xvvtvvrwyzwrqt{{sttsx}~|{{~yqprxzx|}yxtsuuzyqppsxz|}yyy~|}xuvywqoortrssrsw~}xxxvx|}zvvssy|}}|}}}~{wyvttrtyy~{ussx{~~wtvvw}}}~{{~}tsrtww||ywvwww|uqpqx|{~|z|||~~~~wsuy~wppopsrtuspr{}yxuuz||~wutsx}|}|{y||zwssurvz{zvssv{~vvyx{~}{y{|urorx{||yxzww}~{sqptyzy}~}~yxyuuy{|zvqpoptttsppv~~xwuw{~|~}wuvux{z~{xxz~{wwsturw|}{vqqx~zy{yz|{}~{z||~upou{{|}~{xxus{}trrrvyy|zww{xwxx}{|yrpopttqrssvz~yxxuy~}}~zwwtsy|~}zxz{}}wxusutu{yz}ussx|~z|zvx{}~~{z}}wsstxxx|~yuutx|uroqx|}{xyxz|wuvy{{vqqqprqsutrsy~yxww|}}{wurv}~{{{|{|~|wwusvutwuy|wvtvy|zyvuz~~~}{{~zvsquvx}|wtwwy~tpptz{y|zyzz||vtvyz{xrqnnrttsqorzzyyx|yvvvx{z}}{zz{~{wwutvqpuv{}wtqty~|vxwx|~}~~~}}~}yspqvx{}{xxxvzysssuxwx|{{}|~yttw{~~||uooorusqooqw}{zxvy}~}|yywtwy{~zzzz}}yxwsqpnsvw~wrqty~~wyzxz||~~~{z{zrqqvz|~zxxvx}wtqquwx}}|~zutx{~|ztpqqqrpopprx|ytty~}zxurvz|~}zyzz~~ywtooposuw}usru{zzzvy|}}ywy|xsrsy|~~yvwuy}uqorwx{~~yvvy{~~}yyurqooppqrru||wusv{|vusuz|}|zzz|{trpopopvw{zvtuz}xyww{}~~{xwx{}}}vttx~~~zxxvw~~uqpqvz||{{xvvvz~~~{|ysonnqqqttuy|wvutx}}wuuty~|{{{{|{urroppotww{~ywwy}zxzwy}~|{{zxvy||xww{~}}zxxwy{trqtz}~}|{}zursv{~}|vonnorrtvvvy~|wvuuz~~}xwww|}|{zxxyusropqoruv||zyy|~{yzwy}~~|yz|zxvy}zyxy|{~~zxyxx|~}wtsv{~}|}}~xsqsuy}~|zvpoootuuwvtu{}xwvvx|~~{xzy{}ywuvywrrqpqontw{}zwy{~xzzy{{z{zz{{yxx{|~~zwuy|~}zzyuv|~}xvvx||{~}~}~zurqsuz}~yyvqqqrvvuussw|yywuw|}||z|~}}zvvvwyvstqoonqwxy{wux}~y{zwxxy{zy{|{ywy|}~wuuz~~{xvsu|~{xwwzzz~}}}|zvrprv{}yzwssssvtrrsuy~{yuuy}}xy|~~zxvvuvywsronqqruux}xvv{~~zzvsvxy{zz}}ywx|zwwx|}~}vtutx}~zvtuz{}~}~||}}ytqruw{}~~}{|wtusrsrsuuw{|wwvy}~{xw|~zywuuvyyrqqprrortv~zxxz}yvusvxxz{{|{yxz|}~}yxxz|}{vtvtv|yusv{||~}~}~~|}}}|yvsrstz~|}}~zvtqpsttuuuz~{yzxx{~|zzz}~{yvvwwxwrrsppomrux|ywx|}uvvsuxy||yz{{{yz{}~zwvz}~|zvuutx~~xvvx|{|~}~}~|}~|||zvqpsw}~|xrqpsuttttv{|{yvw|{}{{~~|yxwwvuwvttpnonpvx|zwwz}~yuwttxyzzxz}}zwwz}zxwy|}~{xuvvv{}{yvvy{{~~}~~}~}|}|}}zuqqtwz|}}~ysrrsusrtuuxzxvvz~}{y|{zxvuuxzvrpnnpoty{|ywx{}wvvuvxxy{{||ywvy|zxwz}~{xwvuv{|yvwy{|~~||~~}}~}|zwtstuvy|~~{xuutttssstvz~zxwx{|{z|~|ywvwxywtrpopqswz}}zyz}}ywvvvxy||{zyxwxz~|zy{}~~}ywutw|}ywwy{}}|zz}~~~}}{zywutsux|~|zxvuttttttux}{yyz}}{z{~}yxyyxwvusrqrtuwy{~|zz{~{xvuvx{||zyyyxy||z{|||}~~{xvvwz}|yxy{|}~|{yz{}~}|||{yvsrsvz~}{xuttuuuutux}~|||}}||}}{{zxwvvutuuvvvwxz}|zz{|~{wvvxz{{zzyyz{}~|zzzz{|~}{zxwwy|~}zyz{||{{zyz{}}|}~}zxusstx}}zwuuvvvvtssw|~~}|}~~~|zxwwwwxxwwvvvvwz|zxy{}}zxxxxyzzzz{|}~~{xxyz|}~}zxwwz}}{zzzyzzzzz{}~~~~}~~}{ywutuy|~{yxwwwwvtrrty~~~~}|{|}~|zyxxyzzzyxvvvvxz~|xwxz}~|zyxwxyzz|}~~~~~~zxxyz|~~{xwwz}~{yxxxyzzz{||}~~}}|{yxwwxz|}|zyxwvutsrrty~}}~{zz{}~~{zyyz{|{zywvuvxy{}~zwwxz}}zxwwxz|}~~~}}~|yxxz{}~~|zxwy|}ywwwyz{{{{{|}~}||{zzzyy{}~|zywvutssssv{~}~{zy{|~|{{{|}}|{ywvvwxyz{~~{ywxz|~{yxxy{}~}}}~~{yyz{|}~}{zyz}}ywwxyz{|{{{|~~}||||}|{zzz{}}{ywuttttttvx}~}{zz{|~~}|}}}}|{zxxxxxxyz}}{yxy{|}zyxz|~~~}}}~}{yyz{|}~}{{|}{yxxyz{{{{{|~~}}}}~~|{zzz{}|zxvuutuuuuvy}}{{{{}~~~~}||{zyyxxxxy{}}|zyyz|}{z{|}~~~}~~~~~|zyz{}~~~~}}~|zyyyzz{{||}}~~~~~~}{zyz{~~|zxvuuuuuuuux}~}|{{|}~~~}||{{{zyxxxyz|~~}{yyy|~}}|}}~~~~~~~~~}{zz{}~~}}~~|zyyyz{|}}||}}~~}{zzz{|~|{ywwvvvvuuvwz~~}|{{|~~}||}||{yxxxyz{|~}zyz{}~}}}}}~~~~~~}{z{|}}~~|}~}{zyz{||||||}~~}|{{{{|}~}|{yxwwvvvvvwxz~~|{|}~~~}}}}|{zyyyyyzz{}~|{z{{}~~}||}~~~~}~~}|{{|}}~~}}~|zzz{{|||||}~~~}}|{z{|~~}}|zyxxwvwwwwwy|~}||}}~~~~}|{{zzyyyyyz{~}|{z{|}~}||}~~~~~~~~|{{|}}~~}~}{{{{{{|||}~~~}|{z{|~~~~~~}|{zywwwwxwwwy{~~}}}}~~}}||{zzyyyyyz{~~|{z{|~~}}}~~~~~~~~~~~|{{|}~~~}|{z{{||}~~}|{{{{}~~~}~~~}|{yyxxxxxwwxz}~}}}~~~~}}}||{zyyxxyz{|}{z{|}~~~}~~~~~~~~~}}~~}{{|~~~~~}{{{{|}~}||{{{|}~~~~~~~~~~}{zzyyxxxwxxz}~~~}}}~~~~}}}||zyyxxyzz|}~}{{|}~}}}~~~~~~~}}}}~~}||~~~~~|{{{|}~~}||{{{|}}~~~~~~~~}||{zzyxxxxyz{~~~~}}}~~~~~}}|{zyyyyyz{|}~}||}~~}}~~~~~~}}|}}~~}~~~~~||{|}~~~}||{{{|}~~~~~~~~~}}|{yyyyyyz{{}~~~~}~~~~}}|{zzyyyyzz{|~~~~}}~~~}}~~~~~}}}}}}~~~~~~~}|}}~~~~~}}|{{{{|}~~~~~~~~}|{zyyyzz{{{|}~~~~}}||{zzyyyzz{|}~}}~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~}}|{z{{|}~~~~~~~~}|{zzzz{{{{{|}~~}|||{zzzzzzz{|}~}}~~~}}~~}}}}}}}~~~~~~~~~~~~}~~~~~}|{{z{|}~~~~~~~~~}}||{{{{{{{{{}~}}}||{{{zzzz{{|}~~}}~~~}}}}}}}}}}}~~~~~~~~~}}~~~}}||{{{|}}~~~~~~~~~}}}||{{{{{{|}~~}}}|||{{zzz{{{|}~~}}~~~}}}}}}}}}}}~~~~~}~~~}~~~~~~}|||{{{|}~~~~~~~~~~~}|||{{{{||}~~}}}}||{{{{{{{||} \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m35.bin b/applications/SprPoP/assets/packed/MUS/m35.bin new file mode 100644 index 0000000..40340d0 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m35.bin @@ -0,0 +1 @@ +}zyupnlhefcabcbbcdfiknrtv{~{|wsnjgd`_^^_abcfimquyz~|xuqmiea]ZVTQQONMLKKKKLLMNOSUX\agnxs`gkv~}}}~~{z||yvspmkgeca`^^\\]]_`bcehjmpsuwz|~}|{yxwwwwwwwxyz|~wka^ev|snllmoqruwy{}~~}}||{{zzz{{|}~{qiddjuwpjfcb```_____````_____^^^^^^^__``acdfhilnpsux{}wnfa`clx~vojfcba`````````````_____^__^__`aabcefhjlnpruwz|zrjaYSOPU_ky{rjb[VRPNLKKJJJJJIIIIIIIIHHHHHIIIJKLMNPQSUWY\^acfilnqtvy{~xpic_^bju{uqmlkjiiiiijjkkklllmmmnnoopqrstuvxz{}}xuv{~{ywussrqqqqqpppppppoooopppqqrstuwxy{}{slfbacjsztnifda`^]\\[[[ZZZZYYYYYYYYYYZZ[\\]_`aceghjmoqsuwz|~zuohaZSNLMRZdp{~wpib]YVTRPONMMMMMMMMMMMMMNNNOOPQRSTUWXZ\^`bdgilnqsvx{}ztmgb__bhq}zuqpmkjihhgggghhhhiiijjkklmnopqrtuwy{}~xtrsx~zxvutsrrqqqqqpppppqqqqrrstuvwxy{|~}voidaachq{ztolifdb`_^]\\[[[ZZZZZZZZZ[[\\]^_`abdegiklnprtvxz|~{vqke_YTPOPTZcnxxrlgca][XWUTSSRRRRRRRRSSSTTUVVWXY[\]_abdfhjloqsuxz|~{upjea__bgox{xuromljjihhhhhhhhiijjkklmnopqrtuwyz|~}xspoptz~{ywutsrrqqqpppppqqqrrsstuvwxyz|}}wrlgcaachnvzvsoligecba`_^^^]]]]]]]^^^__`abcdefhiklnpqsuwxz|~{wsnic^YURQRV\clu~zuolifc`^\[ZYXXWWWWWWXXXYYZ[\\]^`abdegiklnprtvxz|~zvqlgc`__afmu~~zwtrpnmlkkjjjjjjkkkllmnnopqstuwxz{}|wspmmnqw}}|zywvtsqpomlkjihhgfffefgghiijlnoqsuwy{~zuqmkiilpu}~|zyxwwvvvuttttttsrrrrrrsrrrsstuuvwyz|}~}zwtqmiea]ZWVVX\ahpz~|{zyxwvvvvvuuuuvvvvvvwwxxyyz{|}~{wsqonprv|}{zxwvuttssrqqqppooonnoooooopqqrstuvwxz{|}~|yvrolheca`acfkpw~~{xwutssrrrrrrrrrssssttuvvwxyyz{}~|xuronmmnptx}}yvsqommlkjjiiiiiiiiijjjkllmnopqrstvwxz{}~}yvspnljjkmpsx|}zwtrponmlkkjjjjjjjjkkklmmnnopqrstuvxyz|}~|yvsoljgfffgikosw|}yuroljigfeddccbbbbbbbcccddeefghijklmnpqrtuvxy{|}|zwuromkjiiiknquy~}{ywvtssrrrqqqqrrrssstuuvwxyz{|}~~|yxvvvwx{~}{zyxwwvvuuuuuuuvvvvwwxyyz{||}~}{xvsrpoooprtw{~~{xvusrqponnmmmmlllmmmnnnooppqrsstuvwxyz{|}~}{ywurpmkihgffghjmpswz}}ywtrpomlkjiiihhhhhhhhiijjkkllmnopqrstuvwxyz{|~}|zxvtrpnmmlmnprux{~|{zxwvvuuutttttuuuvvvwxxyyz{|}~}{zxwwwwyz|~}|{{zzyyyyyyyyyzzz{{||}}~~|zxwusrqqqrsuwy||{ywvutsrrqqqppppppqqqqrrrssttuvvwxyyz{|}}~~|{yxvtrpomljiiijklnprux{~~{ywvtsrqpoonnnmmmmmnnnnooppqqrsstuvvwxyz{||}~~|{yxvtsrppooopqsuwy|~}|{zyyxxwwwwwwwxxxxyyzz{{|}}~~|{zyxwwxxz{}~}}||||{{{{{{||||}}}~~}|zywvutsssstuwxz|~|{zyxwvuuuttttssttttttuuuvvwwxxyyzz{||}}~~~}|{zywvtsrponmllllmnoqrtwy{~}{zxwvuttssrrrqqqqqrrrrsssttuuvvwxxyyz{{|}}~~}|{zyxvutsrqqqqqrsuvxz}~}|{{{zzzzzzzzzzz{{{||}}~~~}|{zyxxxxyyz|}~~~}}}}}}}}}}~~~~~|{zyxwvuttttuuvxy{}~}|{zyyxxwwwvvvvvvvwwwwwxxxyyyzz{{|||}}~~~~}|{zyxwvutsrqpoonnnoopqstvxz|~}|{zyxwwvvuuuuuuuuuuuuvvvvwwxxyyyzz{{||}}~~~}|{{zyxwvutssrrrsstuvxy{}~}}||{{zzzzzzzzzzzz{{{{|||}}~~~~~}|{zyxwvuutttttuuvwyz|~~}}||{{{zzzzzzzzzz{{{{|||}}}~~~}|{zyxxwvuutttttuvwxy{}~~}|||{{{{zzzz{{{{{{{|||}}}~~~~}|{{zyxwvvuutttuuvvwyz|}~}}|||{{{{{{{{{{{{||||}}}}~~~~}}|{zyxxwvvuutttuuvwxy{|~~~}}||||{{{{{{{{|||||}}}}~~~~~}|{{zyxwwvvuuuuuuvvwxz{}~~~}}}|||||||||||||||}}}}~~~~~~}||{zyyxwwvvuuuuuvvwxyz|}~~}}}||||||||||||}}}}}~~~~~~}||{zzyxxwvvuuuuuvvwwxy{|~~~~}}}}|||||||||}}}}}}~~~~~~}}|{{zyyxwwvvvuuuuvvwxyz{}~~~~}}}}}}}}}}}}}}}}}~~~~~~}}||{zzyxxwwvvvuuvvvwwxyz|}~~~~}}}}}}}}}}}}}}}~~~~~~~}}}|{{zzyxxwwvvvvvvvvwxyz{|~~~~~}}}}}}}}}}}}~~~~~~~~~~}}||{zzyyxwwwvvvvvvvwwxyz{}~~~~~~}}}}}}}}}~~~~~~~~~~}}||{{zzyxxwwwvvvvvvwwxyz{|}~~~~~~~~}~~~~~~~~~~~~~~}}||{{zzyyxxwwwvvvvvwwwxyz{|~~~~~~~~~~~~~~~~~~~~~~~}}}||{{zzyyxxwwwvvvvwwwxyyz{}~~~~~~~~~~~~~~~~~~~~~}}}||{{zzyyxxxwwwvvvwwwxxyz{|}~~~~~~~~~~~~~~~~~~~~}}|||{{zzyyxxwwwwwwwwwwxyyz{|~~~~~~~~~~~~~~~~~~}}}||{{zzzyyxxxwwwwwwwwxxyz{|}~~~~~~~~~~~~~~~}}}|||{{zzyyyxxxwwwwwwwxxyyz{|}~~~~~~~~~~~}}}|||{{zzzyyxxxwwwwwwwxxxyz{{}~~~~~}}}}||{{{zzyyyxxxxwwwwwxxxyyz{|}~~~~~~}}}}|||{{{zzyyyxxxxxwwxxxxyyz{|}~~~~~~}}}}|||{{{zzzyyyxxxxxxxxxxyyzz{|}~~~~~~~}}}}||||{{{zzzyyyxxxxxxxxxyyzz{|}}~~~~~~~~}}}}|||{{{zzzzyyyyxxxxxxyyyzz{{|}~~~~~~~~}}}}||||{{{zzzzyyyyxxxxxyyyyz{{|}~~~~~~~~~}}}}}|||{{{{zzzzyyyyyyyyyyyzzz{||}~~~~~~~~}}}}}||||{{{{zzzzyyyyyyyyyyzzz{{|}}~~~~~~~~~}}}}}||||{{{{zzzzyyyyyyyyyzzz{{||}~~~~~~~~~}}}}}||||{{{{{zzzzzyyyyyyyzzz{{||}~~~~~~~~~~}}}}}}||||{{{{{zzzzzzyyyyzzzz{{{|}}~~~~~~~~~~}}}}}|||||{{{{zzzzzzzzzzzzzz{{{||}~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzzz{{{||}}~~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzz{{{|||}~~~~~~~~~~}}}}}}||||||{{{{{{zzzzzzzzz{{{{||}}~~~~~~~~~~~}}}}}}||||||{{{{{{zzzzzzz{{{{{||}}~~~~~~~~~~~}}}}}}||||||{{{{{{{{{z{{{{{{{|||}}~~~~~~~~~~~}}}}}}}||||||{{{{{{{{{{{{{{{{|||}}~~~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{|||}}}~~~~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{||||}}}~~~}|zyxuttspppmlonmnnnpsuwxy||z~ysnid]YURPONLMNPSVZ]aeimquwz|~~}|zxuspmjgeb`^]]\[[ZZ[[\\]^`adgimrxotv}{zzzz{}|}vuw{}zvrolieb_\YWURPONNNNOOQSUX[^adgjmqsvy{}yokq~xuuuwyz|~~}|{zyyxwwwwwxyy{}~{qiccitvnieba`^^]^^^^^^^^^^]]]\\\\\\\\\]^_`acdfhkmoruxz}yqicbemyvojfcba___^^^^^^^^^]]]\\\[[[[[\\\]^_`bcegikmprtvy|~zriaXRNOT]jwzqh`ZUPNLKJIHHHHHHHHGGGGGGGGGGGGHHIJKLNPQSUXZ]_cehknqtwy|xqjfehp{zvrqonmmmmmmmnnnnoooooopppqqrstuvwy{}~~yuvz}ywusrqpooonnnnmmmmllllllllmmnnoprsuvxz|~{slea`bir~xrlgdb_][ZYYXXWWWVVVVUUUUUUUUUVVWXXZ[\^`acegjlnpruwy{}|wqjc[UPMNS[eq|xqic^YWURPONNMMMMMMMMMMMMMNNNOPPQRSUVXY[]_adfhknpsvx{}ysmgddfmv}xtspnmkkjjiiijjjjjjkkkllmmnopqrsuvxz|~zvtux}ywusrqppooonnnnnnnnnnnnoopqrstuvxy{}}voida`bgoy~xrmjgda_]\[ZYXXXWWWVVVVVVVWWWXYYZ[\^_abdfhjlnprtvxz|~}xsmga[VRPQU\eoyysmhda^[YWVUTSSSRRRRRRSSSTTUUVWXYZ[]^`bcegjlnpsuwz|~ztnieccejr|}zwtqonlkkjjiiiiiijjjkkllmnopqrsuvxz{}zurqru{}zxvtsrqppoonnnnnnnnooopqqrstuwxy{|~}wrlgc``bfmu~~xtqmjgdba_^]\\[[ZZZZZZZZZ[[\\]^_`abdeghjlnoqsuwy{|~}yupke`[WTSTW]emw{upmjfca_][ZYYXXWWWWWWXXXYZZ[\]^_`bcefhjlmortvxz|~zupkgdbceipx|yvtrponmllkkkkkkkllmmnoopqrsuvxy{|~{uplhghjou}}{ywutrqonlkjihgfeddddcccdeffhijlnoqsvxz|~~yvsrrtw}}{xvuuttuuusokprjlprsssttuuvvwxyz{|}}{wsokfc_]]^`ekry}zxvutsstuvvuqmovsnnprtvxy{|}~~zwutsux|}|{xurnmpsuspmkkklmnoppqqrstuvwwxyzz{{|}}~~}zwsplifdccdfjpv}~|zxusqppruxyyxvutssstuvwxz{|}~zwtqonnorv{|yvtqnljiijlnpqqponmlkkkkllmnopqrstvwxyz|}~~{xtromllmorw||yvsqnlkkkmnpqrrrqpnmmmmmmnnopqrstuvwxyz{|}~|zwtqnkihggikosx~}zvspnkhfddddfgijjjihgfeeeeeeffghijklmnopqrstvwxyz{|}}~}zxuspnlkjjlnquz~|ywusrrrtuwyzzzyyxwvvvvvwwxyzz|}}}{yxwxy{~|zxwwxyz|}}}}|{zyxxwwwxxyyzz{||}~~{ywusrqqqsuy}}zxusqpoopqrtuuutsrqppooooooppqqrrstuuvwxxyz{{|}}~~|{ywurpnljihhijlnrvz~{xvspnlkjjjkmnopppponmllkkkklllmnnopqqrstuvwwxyz{{|}~~~}{zxvtrqponopqtwz~}zywvvvwxy{||}}|{{zyyxxxxyyzz{{|}~~~|{zyyy{|~}|||}~~~}||{{{{{||}}}~~|{ywvutsttuwz}~|ywvttttuvwxyyyxwvuutssrrrrrrsssttuuvvwwxxyzz{{|||}}~~~~~}}|{yxwutrqonmlkklmoqsvz}}zxvtrqpppqrstuuvvuttsrqqqqpqqqqqrrsstuuvvwxxyzz{{|}}~~~}|{zywvutsrqqrrtvx{~~|{zyyzz|}~~}}|{{{{{{{{{|||}~~~}|{zzzz{}~~~~~}}}}}~~~~~}|zyxwvvuvvwy{}~|{yxxwxxyz{{|||{{zyxxwvvvuuuuuuvvvvwwwxxxyyzzz{{{|||}}}}}}}}}}}}||{{zyxwvutsrqpoonnopqsuwz}}{yxvutttuuvwxyyyyyxxwvvuuuttttttuuuuvvwwxxxyyzz{{|||}}~~~~~~~}||{zyxwvvuttssttvwy{}~}||||}~~}}}}}}}}}}}~~~~}||{{{{{|}~~~~~}|{zyxxwwwwxyz{}}|{zzz{{|}~~~~~}}|{{zyyyxxxxwwwxxxxxxyyyyyzzz{{{{{|||||||||||{{{zzyyxwvvutsrrqqqqrssuvxz|}|zyxxwwxxyz{{|||||{{zzyxxxwwwwwwwwwwwxxxxyyyzzz{{{||||}}}}}}}}}}}}|||{{zzyxxwvvuuuuvvwxz{}~~}}}}~~~}}||||{{{{{{{|||||}}}}~~~~~~~}}||{zzyxxwwwwwwxyz{}~}}}}~~~~}}|||||{{{|||||||}}}}}~~~~~~~~}}||{{zyyxxwwwwwxyz{|~~~}}~~~~~}}|||||||||||||}}}}}}~~~~~~~~~~}}||{{zzyxxxwwwwxyyz|}~~~~~~~}}}||||||||||||}}}}}}~~~~~~~~~~~~}}}||{{zzyyxxxxxxxxyz{|~~~~~~~}}}|||||||||}}}}}}}}~~~~~~~~~~~~~~~~~}}}|||{{zzyyxxxxxxxyz{|}~~~~~~~}}}}}|||||}}}}}}}}}~~~~~~~~~~~~~~~~}}}|||{{zzzyyyxxxxyyzz{|~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}|||{{{zzyyyxxxxyyyz{|}~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}|||{{zzzyyyyyyyyyz{{|~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}|||{{{zzzyyyyyyyyzz{|}~~~~~}}}}}}}}}}}}}}}}}}~~~~~~}}}}}}}}||||{{{zzzyyyyyyyzz{|}~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||{{{zzzzyyyyyyzz{{|}~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}||||{{{{zzzzzyyyzzz{{|}}~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}|||||{{{{zzzzzzzzzz{{||}~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}||||||{{{{zzzzzzzzz{{{|}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}|||||{{{{zzzzzzzzzz{{||}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}||||||{{{{{zzzzzzzz{{{|}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}||||||{{{{{zzzzzz{{{{||}~~~~~~~~~~~~~}}}}}}}}}}}}}}||||||{{{{{{{zzz{{{{{||}}~~~~~~~~~~~~}}}}}}}}}}}}|||||||{{{{{{{zzz{{{{{||}~~~~~~~~~~~~~~}}}}}}}}}}}|||||||{{{{{{{{{{{{{{||}}~~~~~~~~~~~~~~}}}}}}}}}}|||||||{{{{{{{{{{{{{|||}}~~~~~~~~~~~~~}}}}}}}}}}|||||||{{{{{{{{{{{{|||}}~~~~~~~~~~~~~}}}}}}}}}}||||||{{{{{{{{{{{{{|||}}~~~~~~~~~~~~~}}}}}}}}}|||||||{{{{{{{{{{{|||}}}~~~~~~~~~~~~~}}}}}}}}}|||||||{{{{{{{{{{||||}}~~~~~~~~~~~~~}}}}}}}}|||||||||{{{{{{{|||||}}}~~~~~~~~~~~~}}}}}}}}|||||||||{{{{{{{||||}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||{{|||||||}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||}}}}~~~~~~~~~~~}}}}}}}}||||||||||||||||||}}}}~~~~~~~~~~~~}}}}}}}}}|||||||||||||||||}}}}~~~~~~~~~~~~}}}}}}}}}||||||||||||||||}}}}~~~~~~~~~~~~~~}}}}}}}}}||||||||||||||}}}}}~~~~~~~~~~~~}}}}}}}}}}|||||||||||||}}}}}~~~~~~~~~~~~~~}}}}}}}}}||||||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}||||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}||||||}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}~~~~~}||ywwwtssroqrqqqqruwy{{~z~xtnjc^ZVTRQPOQRUW[^bfjnquxz|~~|{yvspmjgd`^[YWWWUUTUUVVWXY[\_ceimszsx}s|}}|}}yzvwz{vtpnjfc`][YVTRQQQQQRSUWY\_behknqtvy{}~~}}||~~~~~rkly|xwwx{|~~}|{zzzzzz{|}~tkdaen{{smheccaa``aaaaaaaaa``_____^^^^_``abcefhjloqsvy{~|skdabhs{tnjfedbbbaaaaaaaa````___^^^^^^^__`abdeghjlnqsuxz}zskc[SNMPXdq}vme^YTQONLLKKKKKKKJJJJJJJIIIIJJJKKLMOPQSUWY\^adfiloqtwy|~zrkfdeku~yusrqpoooopppqqqqqrrrrrrssttuvwxy{}~yutw~|zxvussrrqqqqppppoooonnnnooopqqrtuvxz{}}umga_`dmx}vpkgeb`^]\[[ZZZYYYXXXXXWWWWWXXXYZ[\]^_acefhkmoqsuxy{}|wrkd]VPMLOV`kw|ungb]YXUSRQPPPOOOOOOOOOPPPPQQRRSTUVXY[]^`cegiloqtvx{}{tmhdbdip{|xusqonmmlllllllllmmmnnnoopqrstuwxz{}{vssu{}zxvutsrrrqqqpppppppppqqqrsttuwxy{|~}wpjda_`dkt~|vpmjgdb`_]\\[[ZZYYYYYXXXYYYZZ[[\]^_abdegikmoqrtvxz|}|xsnhb\VRPORX`jt}wqlgda^\ZXWVVUUUUTTUUUUUVVWWXYZ[\]^`acefhkmoqsuxz|~zuojfbbcgnv}zwtrponmllllkklllllmmnnopqrstuvxy{}{vrpprw~}zxwutssrqqqpppppppqqqrrstuvwxy{|}}xrmgc`_`ciqz|wtqmjgecba`_^]]]\\\\\\\\]]^^_`abcdefhikmnprtuwy{|}|yupkf`[WTRRTY`ir{~ytpmifda_^]\[ZZZYYYYYZZZ[[\]^^_`bcdfgiklnprtvxz|~zuplgdbacfls||yvtrqponnmmmmmmmnnnoopqrstuvwyz|}{vspnmmnqu||wsolifdcb`_^^]]]]]^^^_`abcefhikmpruvx{~}yurqpqqrrw}{ywusqpomlkihgfeedcbbbbcccdfghjlmoqsuwy{|~|yvspnkjjjkllo{~|{yxwwvuutttssstttuvwxyz|}~{xurommr{~{yvtrpnlkjihffeddddccccddeefgijkmnpqsuvxz{}~|yvrokhedfkr{~|{zxwvvuutttsssstttuuvwxyz{|~~zuqlhdbabekry~{yvtrponmlkjjiiiiiiiijjkklmnopqrtuvxz{}~}yuqmifcbbdhnu}|ywusrponmllkkjjjjjjjkkklmmnopqrsuvwxz{}~}zwsokgc`][Z\^cipw~}zxuspnljihffeddcccbbccccddefgghijlmnpqrtuwxz{}~~{xuqnkhfeehkqw~~}{zyxwwvvuuuuuuuvvwwxxyz{|}~~{wtqnmlmptz~|{zyxwvvuuttttttuuuvvwxxyz{|}~}zwtpmjgedegjntz}zxvusrqponmmllllklllllmmmnoopqrstuvwxyz{|}~~|zxuspmjgda_^^_aejou{~|zwvtrqonmllkkjjjjjjjjkkklmmnoppqrtuvwxyz{|~}{yvsqnkjhhhjmqv|~}|{zyyxxxwwwwwwxxxyyz{{|}~|ywtrpoopruy~~}|{zzyyyyyxyyyyyzz{{||}~~}{xvspnkihhhilotx~}{yxvutssrqqpppoooooppppqqrrsstuvvwxyzz{|}}~~}{zxvtromjhfdcbbcehlpuz}{zxwutssrqqppooooooppppqqrrsttuvwxxyz{|}~}{ywusqomkjjklnrvz~}}|{{zzzzzzzzzz{{{||}}~}zxvtrqqqqsvy}~~}|||{{{{{{{{|||}}}~~}{ywusqomkkjklnptx|}|{zyxwvuutttssssssssstttuuvvwwxxyzz{{|}}~~~}|{zxwusrpnljhgfeffhjmquy}~|{zyxwvvutttsssssssstttuuuvvwxxyzz{||}~~~|{yxvtsqpnmmlmnpsvz}~}}}||||||||||}}}~~~}{ywvtsrrrstvy|~~~}}}}}}}}}}~~~~}{zxvusqpnmmmmnprux{~}|{zzyxxwwwvvvvvvvvvvwwwxxxyyzzz{{||}}}~~~~}}|{zyxvusrpomljiihiikmoruy|~}|{{zyyxxwwwvvvvvvvwwwwxxxyyyz{{{||}}~~~}|{zxwvtsqpponnoprtvy|~~}}||{{{{{zzz{{{{{{|||}}}~~~}|{yxwutrqpoonooprtwz}~~}}|||{{{{{{{{{{{||||}}}~~~~}|{zywvusrqpooooprtvx{~~~}}||||{{{{{{{|||||}}}~~~~}|{zyxwutsrqppoppqsuwz}~~}}}||||||||||||||}}}~~~~~}||zyxwvusrqqppppqrtvy{~~~}}}|||||||||||}}}}~~~~~~}|{zyxvutsrqqpppqrsuwz|~~~}}}}||||||}}}}}}~~~~~~}|{zyxwvutsrqqqqqrsuvy{}~~~~}}}}}}}}}}}}}}~~~~~~~}|{zzyxwutssrqqqqrstvxz|~~~~}}}}}}}}}}}}~~~~~~~}||{zyxwvutsrrrqqrstuwy{}~~~~}}}}}}}}}~~~~~~~~}}|{zyxwvuutsrrrrrrstvxz|~~~~~~~~~}~~~~~~~~~~~}}|{zzyxwvuttsrrrrrstuwy{}~~~~~~~~~~~~~~~~~~}}|{{zyxwwvutsssrrsstuvxz|~~~~~~~~~~~~~~~~~}}||{zyyxwvuutsssssstuvwy{}~~~~~~~~~~~~~~}}||{zzyxwwvuttsssssttuwxz|~~~~~~~~~~~~}}|{{zyyxwvvuttssssttuvxy{}~~~~~~~}}||{zzyxxwvuutttstttuvwxz|}~~~}}||{zzyyxwvvuuttttttuvwxy{|~~~~}}||{{zyyxxwvvuutttttuuvwyz|}~~~}}|||{zzyyxwwvvuuttttuuvwxy{|~~~~}}}||{{zzyxxwwvvuuuuuuuvwxyz{}~~~~}}}||{{zzyyxxwvvvuuuuuvvwwxz{|~~~~~}}}|||{{zzyyxwwwvvvuuuvvwwxyz|}~~~~~}}}|||{{zzyyxxwwwvvvvvvvwwxyz{|~~~~~}}}}||{{{zzyyxxwwwvvvvvvwwxyz{|}~~~~~~}}}}|||{{zzzyyxxwwwwvvvwwwxyyz{|}~~~~~~}}}|||{{{zzyyyxxxwwwwwwwwxxyz{|}~~~~~~~}}}}|||{{{zzyyyxxxwwwwwwxxxyz{{|}~~~~~~~}}}}|||{{{zzzyyyxxxxwwwxxxxyzz{|}~~~~~~~}}}}}|||{{{zzzyyyxxxxxxxxxyyyz{||}~~~~~~~~}}}}||||{{{zzzyyyyxxxxxxxyyyz{{|}~~~~~~~~}}}}}|||{{{{zzzyyyyyxxxxxyyyzz{|}}~~~~~~~~}}}}}||||{{{{zzzyyyyyyyyyyyyzz{||}~~~~~~~~}}}}}||||{{{zzzzyyyyyyyyyyyzz{{|}}~~~~~~~~}}}}}|||||{{{zzzzzyyyyyyyyzzz{{||}~~~~~~~~~~}}}}}||||{{{{zzzzzzyyyyyyzzz{{||}}~~~~~~~~}}}}}}||||{{{{{zzzzzzzzzzzzz{{{|}}~~~~~~~~~~}}}}}|||||{{{{{zzzzzzzzzzzz{{{||}}~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzz{{{||}}~~~~~~~~~~}}}}}}|||||{{{{{{zzzzzzzz{{{{|||}~~~~~~~~~~}}}}}}|||||{{{{{{{zzzzzz{{{{|||}}~~|{wqoniffeabcbbcdfhknrsuz~}~~ytplhea____abbfimqtxz}~{wsokgc_[WTQNMKJHGGFFFGGHIKNQTX\cjtwa}kmx~~~~~}{yuspmifdba`_]]]^_`bcehjmprtwy{}~|{yxvutstssssstuwy{~wka]dtwrppqsuwy{}~}}|||||}~zpidcit|tokgfeddcccccccccbbabaa```_````aabdefhjlnpsuxz}}umf`_bkwztnkhfeddccccccccccbbbaaa`````aabbcdeghjlnpruwy|~}wph`XRNOT^kxwnf_ZVTRPONNNMMMMMLLLLKKKJJJJJJKKLMNOPRSUWY\^acfhknpsvxz}}vohb^]aityuqpommlllmmmnnnoooooppppqqrstuvwxz|~|wtuz~}zywvuuttttssssrrrrqqqqqrrrstuvwxz{}zrkea`bir~~wrmjhecb``_^^^]]]\\[[[[[ZZZ[[[\\]^_`bceghjlnpsuwy{}|xsmg`YRNKLQYco{{tlfa\ZXUTRQQPPPOOOOOOOOOOPPPQQRSTUVWY[\^`bdgiknpsuxz|~~yrlfa^^agp|~yusqomlkkjjjjjjjkkkklllmmnopqrstvwy{}}wsqrw~~{yxwvuuttsssssrrrrrsssttuvwwyz{}~{unhda`bgpz}wrpmigecba`_^^]]]\\\\\\\\\\]]^_``bcdfgiklnprtuwy{}~|ytoid^XSONOSYbmx|upjfd`^[ZXWVVUUTTTTTTTUUUVVWXXYZ[]^_acdfhjlnqsuwy{}~ytnid`^^afnw~{xurpomllkkjjjjjjkkkllmmnopqrstvwy{|~|wsonosy~{zxwvuttsssrrrrrrsssttuvvwxz{|}|vqkfb``bgmv}yvroljhfdcbaa``__________``abccdfghiklnoqstvxz{}~}yuqlgb]XTQQRU[bku~}wrolhfca_^\[[ZZYYYYYYYZZ[[\]]^_`bcdfgiklnprtvxy{}}ytpkfb_^^aelt}}zwusqponmmlllllllmmnnoppqrstvwxz|}{vromlmpu{~|zywvtrqonmlkjhhggffffffghijklnpqsuwz|}zwsqppru{}{yxwvvwwxvqmqtmnrtvuvvwwxyxyz{|}~~~{xtplhd`][[\^cipx}zxwvvvwwxywtpqxwqpruwxz{}~|xurqqruz}{xtqpruwvronmmnopqrsstuuvwxxyzz{||}}~~~|yvspmjgdb``bdinu|}{xvsrrtwz||zywvuuvvwxyz|}~~{wtqomllmpuz|yvsqnmkklnprsssrqponmmmnoopqrstuvwxy{|}~}zxuromkjjkmpuz~{xvsqonmnoqrttttsrqpoooooppqrstuvwxyyz||}~~|ywtqnkifedefimqw||yvspmkhgfffhiklmllkjihggggghhijkklmnopqrstuvwxyz{{|}~~}{zwuspnljihhjlosx~~{ywutttvwy{||||{zyxxxxxyyz{{|}~|zxwvuvwz}~|zyyy{|}~}|{zzyyyzzz{{|}}~}{xvtrqooopqtw{|zwusrqqrstvwwwvutsrrqqqqqqqrrsstuuvwwxxyzz{||}}~~~~}|{yxvtrpnljhgfffhjmpty~}zxurpnmlllmopqrrrrqponnmmmmmnnoopqqrsttuvwwxyzz{{|}}~~~}{zywusrpommlmnpruy}|{yxxxxz{|~~~}}|{{zzzzz{{||}}~}{zyxwwxy{}~}~~~}}}}}}}~~~}{zxwutsrrrstvy|}{ywvuuvwxyzz{zzyxwvuutttttttttuuuvvwwxxyyyzz{{{|||}}}}}}~~~}}}}}|{{zyxwvtsqpnmlkjiijkmoruy|~|zwutrrqrrstvwwwwwvuutssrrrrrrsssttuuvvwwxxyyzz{{||}}}~~~~~}}|{zyxvutsrqppopqrtwz}~|{{{{|}~~~}}||||||}}}~~~}|{zyxxxyz{}~}|zyxwvuttttuvwy|~~|{zyyyzz{|}}~}}|{{zyxxwwwwvvvwwwwwwxxxyyyyzzzz{{{{||||||||||{{{zzyyxwvutsrqponmmmmmnpqtvy|}{yxwvvvvwxyzz{{{{zyyxwwvvvuuuuuvvvvwwwxxxyyzzz{{{{|||}}}}}}}}}}}}||{{zzyxwvuttsrrrrrstvxz}~}}~~~~~~~~~~~~}|{{zyyyyz{|}~}}|{zyxwwvvvvvvwxz|~}|||||}}~~}}|{{zzyyyyxxxxxyyyyyyyzzzzzzz{{{{{{{{{{{{zzzyyyxwwvuutsrqqpppoppqrsuwy{~}|{zyyyyyz{|}}~~~}}||{zzyyxxxxxxxxxxxxyyyyyzzzz{{{{{||||||||||||{{{zzzyxxwwvuuttttttuvwyz|~~~~~~}}}||||||||||}}}}}}}~~~~~~~~~~~~~~~~~~}}}||{{zyyxwwvvuuuvvwxyz|~~~~~~}}}}||||||||}}}}}}}}~~~~~~~~~~~~~~~~}}}|||{{zyyxxwwvvvvvvwxyz{}~~}}}}}||||}}}}}}}}}}}~~~~~~~~~~~~~}}}}|||{{zzyyxwwwvvvvvwwxy{|~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}|||{{zzyyxxwwwvvvvwwxyz{}~~~}}}}}}}}}}}}}}}}}}}}~~~~~~}}}}}}}|||{{{zzyyxxwwwwwwwwxyz{|~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||{{{zzyyyxxwwwwwwwxyyz|}~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||{{zzzyyxxxwwwwwwxxyz{|}~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}|||||{{{zzzyyxxxxwwwxxxyz{|}~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}|||||{{{zzzyyyxxxxxxxxxyzz{|}~~~~~}}}}}}}}}}}}}}}}}}}}}}|||||{{{{zzzyyyxxxxxxxyyyz{|}~~~~~~~~}}}}}}}}}}}}}}}}}}}||||||{{{zzzzyyyxxxxxxxyyz{{|}~~~~~~~~}}}}}}}}}}}}}}}}}||||||{{{{zzzzyyyyxxxxyyyzz{|}~~~~~~~~~}}}}}}}}}}}}}}}|||||||{{{{zzzzyyyyyyyyyyzz{||}~~~~~~~~}}}}}}}}}}}}}}|||||||{{{{zzzzyyyyyyyyyyzz{{|}~~~~~~~~~}}}}}}}}}}}}}||||||{{{{{zzzzyyyyyyyyyzzz{||}~~~~~~~~}}}}}}}}}}}}|||||||{{{{{zzzzzyyyyyyyzzz{{|}}~~~~~~~~~}}}}}}}}}}}|||||||{{{{{zzzzzzyyyyzzzz{{||}~~~~~~~~}}}}}}}}}}|||||||{{{{{zzzzzzzyyyzzzz{{||}}~~~~~~~~~}}}}}}}}}|||||||{{{{{{zzzzzzzzzzzzz{{||}~~~~~~~~~}}}}}}}}}|||||||{{{{{{zzzzzzzzzzzz{{||}}~~~~~~~~~~}}}}}}}}|||||||{{{{{{zzzzzzzzzzz{{{||}}~~~~~~~~~}}}}}}}}|||||||{{{{{{zzzzzzzzz{{{{||}}~~~~~~~~~}}}}}}}}|||||||{{{{{{{zzzzzzzz{{{{||}}~~~~~~~~~~}}}}}}}|||||||{{{{{{{zzzzzz{{{{{||}}}~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{{{{|||}}~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{{{|||}}}~~~~~~~~~}}}}}}}}||||||{{{{{{{{{{{{{{{{|||}}~~~~~~~~~~}}}}}}}}|||||||{{{{{{{{{{{{{{|||}}}~~~~~~~~~}}}}}}}}|||||||{{{{{{{{{{{{|||||}}}~~~~~~~~~}}}}}}}}|||||||{{{{{{{{{{{|||||}}}~~~~~~~~~~}}}}}}}}||||||||{{{{{{{{{|||||}}}}~~~~~~~~~~~}}}}}}}||||||||||{{{{{|||||||}}}~~~~~~~~~~~}}}}}}}}||||||||||||||||||||}}}}~~~~~~~~~~~}}}}}}}}|||||||||||||||||||}}}}~~~~~~~~~~~~}}}}}}}}||||||||||||||||||}}}}}~~~~~~~~~~~}}}}}}}}|||||||||||||||||}}}}}~~~~~~~~~~~~}}}}}}}}}|||||||||||||||}}}}}}~~~~~~~~~~~~}}}}}}}}}||||||||||||||}}}}}}~~~~~~~~~~~~}}}}}}}}}}||||||||||||}}}}}}}~~~~}|ytpokggfcbccbcdegjlprsx||{wrnjgb`___`bbehkoswy{~|wtplhd`\XURONMKJIHHHHIJKLMPSVZ^djs~doohr~~~|~{||zwtqnkhfdb`_^]^^_`bcdgiloqsvxz|~~}{zxwvttuttttuvwy{}|pe^anwqmlmorsvxz}~}}}||}}~ulfdgp~{tmiecbbbaaaabbbbbbbbbbaa``````aabbdefhikmprtwz|~zqic`ahszsmifdcbbaabbbbbbbbbbbbaaaaaaaaabccdeghjlnprtvy{}|tme\UPOR[gtwnf_YUQPOMMLLLLLLLLLLLKKKKKKKKKKLLMNOPRSUWY[]`begjmoruwy|~zsle`^`fq}~xtpnmlkkkkkllmmnnnooopppqqrrstuvwxz|}yuux}{ywvuttsssssssrrrrrrrrrrrrsstuvwxz{}}vnhcabgo{~wqlhfdb`_^^]]]]\\\\\[[[[[[[[[\\]]^_`bcefhjlnprtvxz|~{vqjc\UPMLOV`lw{tlf`[XWTRQPOOOOOOOOOOOOOPPPPQRRSTUVWYZ\^`bdfhkmortwy|~|void`^`emx}xtrpmlkjiiiiijjjjkkklllmnnopqrstvwy{|~zusrv|}zxwvuttssssssrrrrsssssttuvwwxz{|~~xqkfbabfmw}wrnlhfdba`_^^]]]\\\\\\\\\\\]]^_``acdeghjlmoqsuwyz|~{wrmga[UQOORW_it~|uojec`][YWVUUTTTTTTTTTUUUVVWXXYZ[]^_abdfhjlnprtvy{}|wqkfb_^`dkt~zwtqonlkkjjjjjjjjkkkllmmnopqrstvwyz|~yuqoorw}{ywvuttssrrrrrrrrsssttuvvwxy{|}~ysnhda`beks|}xvrnligedcba``__________```abbcdeghiklmoqrtvwy{|~{xtoje_ZVSQRTY`hr{}wrnlheb`^]\[ZZYYYYYYYYZZ[[\\]^_`acdeghjlnoqsuwy{}~{wrmhda_^`djqz}ywtrponmmllllllllmmnnoopqrstuwxz{}~~ytqnlmoty~{xuromkihfdcb``__^^^^^_`abdeghjmortwy{~|xurpoprrsy~|zywvusrqponmlkjjjiiihijjkmmnprsuwxz|}~|zxusolifdccdeefiv~}{zyxwvvuuutttttttuuvwxyz{}~~zwuromns}|zxvtsqponmlkkjiiiiiiiijjklmnopqstvwxz{}~~|yvsolhda_^`emw~|zyxwvvuuttttttttuuvvwxyz{|}~zvqlhebbcflsz~{xvtrqonmlkkjjjiiijjjjkklmnopqrstvwyz|}|xtplhebaadhnu}~|ywusqponmmllkkkkkkklllmnnopqrstuvwyz{}~}zwsplhd`][[\_djqx}{xuspnlkihgfeddcccccccccddefgghijlmnoqrsuvxyz|}~{xuqnjgdbbbeinu|~|{yxwvvuutttttttuuvvwwxyz{|}~~zwspnmlnpuz~}{zyxxwwvvvvvvvvvvwwxxyz{|}}~}zwtpmjhfeegjou{}{ywutsrqponnnmmmmmmmnnnooppqrsstuvwxyz{|}~~|zwurolifca_]]^aeiou{}{ywusrponmlkkjjjjjijjjjkkllmnnopqrstuvwyz{|}~~{yvtqnkigfefhkouz}|{zyxxwwwwvwwwwwxxyyzz{|}~~~{yvsqpoopruz~}||{{zzzzzzzzzz{{{||}~~}{yvsqnljihijmpuy}|zywvuttsrrqqqqqqqqqqqrrssttuvvwxxyz{{|}~~|{yxvtqoljhecbaabdgkpuz~~|{yxvutsrqqppoooooooopppqqrrsttuvwwxyz{||}~}{zxusqomkiihijmpty~~}||{{zzyyyyyyyzzz{{{|}}~~~|zwusrqppqsvy}~~}}}|||||||}}}}~~}|zwusqomlkkklnquy}~}{zyxxwvvuuuttttttttuuuvvvwwxxyyz{{||}}~~}|{yxvusqomkihfeeefhjmquy}}|{yxwwvuuttsssssssssstttuuvvwwxyyz{{|}}~~~}{zxvusqonlkkkkmoquy}~}}|||||{{{|||||}}~~~~|zywutsrqrrtvy|~~~~~~~~~~~}|zxwusrponmmmnprux|~}|{zzyyxxxwwwwwwwwwwxxxyyyzzz{{||}}}~~~~}||{zxwvtsrpnmkjihhhijlorux|~}|{zyyxxwwwvvvvvvvvvwwwwxxyyyzz{{||}}~~~}|{zywvusrponmmmnopsux|~~}}|||{{{{{{{{{{{||||}}}~~~}|zyxvusrqponnnoprtwz}~~}}||||{{{{{{{{{||||}}}~~~~}|{yxwutsrqpoooopqsvx{~~~}}||||||||||||||}}}}~~~~}|{zywvutrqppooopqsuwz}~~}}}}|||||||||||}}}}~~~~}|{zyxwutsrqppoopqrtvx{~~~}}}}}|||||||}}}}}~~~~~}||{yxwvutsrqppppqrsuwz|~~~}}}}}}}}}}}}}}}~~~~~}}|{zyxwvtsrrqpppqrstvy{}~~~}}}}}}}}}}}}~~~~~~}}|{zyxwvutsrrqqqqrstvwz|~~~~~}}}}}}}}}~~~~~~~}|{zzyxwvutsrqqqqqrsuwy{}~~~~~~~}}~~~~~~~~~~}||{zyxwvutssrrqqrrstvxz|~~~~~~~~~~~~~~~~~~}||{zyyxwvutssrrrrrstuwy{}~~~~~~~~~~~~~~~}}|{zzyxwvuutssrrrrstuvxz|~~~~~~~~~~~~~~}}|{{zyxxwvuttssrrrsstvwy{}~~~~~~~~~}}||{zzyxwvvuttsssssstuwxz|~~~}}||{{zyxxwvuuttssssttuvwy{|~~~}}||{{zyyxwvvuuttsssttuvwxz{}~~~}}||{zzyxxwvvutttssttuuvxy{|~~~~}}||{{zyyxwwvuuttttttuuvwxz{}~~~}}||{{zzyxxwvvuutttttuuvwxy{|~~~~}}||{{zzyyxwwvvuuuttuuuvwxyz{}~~~~}}}||{{zzyyxwwvvuuuuuuuvvwxy{|~~~~~}}}||{{zzyyxxwwvvvuuuuvvvwxyz|}~~~~~}}}|||{{zzyyxxwwvvvvvvvvwwxyz{|}~~~~}}}|||{{zzzyyxxwwwvvvvvvwwxyy{|}~~~~~}}}}|||{{zzyyyxxwwwvvvvvwwxxyz{|}~~~~~}}}|||{{{zzyyxxxwwwwwwwwwxxyz{|}~~~~~~}}}}|||{{zzzyyyxxxwwwwwwwxxyzz{|}~~~~~~}}}}|||{{{zzzyyxxxxxwwwwxxxyyz{|}~~~~~~}}}}||||{{{zzzyyyxxxxxxxxxxyyz{{|}~~~~~~~}}}}|||{{{zzzyyyyxxxxxxxxxyyzz{|}~~~~~~~}}}}}|||{{{zzzzyyyyxxxxxxyyyzz{||}~~~~~~~}}}}}||||{{{zzzzyyyyyxxxyyyyzz{{|}~~~~~~~~}}}}}||||{{{zzzzyyyyyyyyyyyzz{{|}}~~~~~~~}}}}}||||{{{{zzzzyyyyyyyyyyzz{{||}~~~~~~~~~}}}}}||||{{{{zzzzzyyyyyyyzzz{{{|}}~~~~~~~~}}}}}}||||{{{{zzzzzzyyyyzzzz{{{||}~~~~~~~~~}}}}}|||||{{{{zzzzzzzzzzzzzz{{||}}~~~~~~~~~}}}}}|||||{{{{{zzzzzzzzzzz{{{||}}~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzz{{{|||}}~~~~~~~~~}}}}}~}|ytrqljjiffhgghijmorvwy~zw{vsolhdcccdefgjmquy|~~zvrnjfb^ZWTRQONMKKJJJKLLMORTW[_elur]|dfr{|xvw{{ywtromjhfeccb`aabcdfgilnqtvx{}~}{zywwxwwwwwxy{}vi_[brxpjgghklnpsuwyz|}}~~~~~}}}}}}}~~zphbbgr|tlfb_]\\[[[[\\]]]]^^^^_____``abbcdeghjlmprtwy|~~ume_^aiu{rkfb_^]\\\\\\]]]^^^^____````aabcdefgijlnprtvy{}yqi`XQMMR\huxof^XRNLJHGGFFFFGGGGGHHHHIIIJJKKLMNOPRSUWY[]_bdgiloqtwy{~~woha]\_gr~wrmjhgfeeeffgghiijkllmnnopqrstuwxz{}|vssx{wusqpoonnnnnnooooopppqqqrsstuvwyz{}zrkd`_agq|wpkfca^\[ZYYXXXXXXXYYYYYZZZ[[\]]^_abcefhjlnprtvxz|~~ztng`XRMJKOWamx|tmf_ZUSQOMLKJJJJJKKKLLMMNNOOPQRSTUWXZ[]_acehjloqtvx{}yslfa]]`foz}wrnmjhgfeeeeeeffgghiijklmnopqrsuvxz|~}wspqu|}{wusrqpppooooppppqqqrrsttuvwxz{|~|unhc`_afnx}wqlifca_]\[[ZZZYYYZZZZZ[[\\]^^_`acdeghjlmoqsuwxz|~~zupjd^XSOMNQXaku|vojd`^ZXVTSRQQQQQQQQRRSSTUUVWXYZ\]^`bcegikmoqtvxz|~ztoid`]]`elv}xuromkihgggffggghhiijkllmnpqrsuvxy{}|wronorx~{xvusrqqppppppppqqrrsstuvwxyz|}~|wqkfb`_aflt}}xtqmjgeca`_^^]]]]]]]]^^__``abcdefhijlmoprtuwy{|~~{wrmhb]XTQPQTYajs|}wrmjgca^\ZYXWWWVVVWWWXXYZZ[\]^_`bcefhikmoqrtvxz|~~zupkfb_]^`dkr|~|xurpnmkkjjiiiijjkkllmnopqrstvwyz|~|wrolklotz|xuromkigfeccaa`````aabbcdeghjkmoqsuxz{~zvqmjhhjot{|yxvuutsrrrqqqqpppppppoooooopoppqrstuvwy{|~~{xuqmiea^\ZZ\`elt}~}{zyyxxwwwwwwwxxxxxxyzz{{||}~zvspnmnptz}zywutsrqpoonnmmmlllkkkkkkklllmmnopqrstuwxz{}~|yvsoligeddfjntz}zywvutttttstttttuuuvvwwxyyz{|}~~zvspmkjjknqu{{wtqomlkjiiihhhhhiiijjkklmnoopqrtuvwyz|}~|yvspmkjijlnrw{|xusqonllkjjjjjjjjjkkklmmnoopqrstuvwyz{|~}zwtqnkhedccdfimquy}{wspmkhgfddcbbbbbbbbbccddeefghijklmnoprstvwyz|}~~|yvtqomkjjklorvz~{yxwuttssssssstttuuvvwxyyz{|}~}{ywuuuuwy|}{zyxwvvuuuuuuuuuvvvwwxyyz{{|}~~|ywtrpnmmmnpruy||yvtsqponmmlllllllllmmnnnoppqqrstuvwxxyz{|~~~|zxvtqoljhfeeefgiknrux||yvsqonmlkjiiiiiiiiiijjkkllmnnopqrstuvwxyz{|~}{yvtrqonmmmnprux{}{zyxwwvvvvvvvvvwwwxxyzz{||}~}{yxwvvwxy{~}|{{zzyyyyyyyyyzzz{{{|}}~~}{ywusrqpoopqsuwz}}{ywvussrqqppppppppppqqqrrssttuuvwxxyz{{|}~~~}|zywusrpnlkjihhijkmoqtwy|}{ywutsrqpooonnnnnnooooppqqrrsttuvwwxyz{||}~~}{zxwusrqpooopqsuwz|}|{{zyyyxxxxxyyyyzz{{||}}~~|zyxwwwwxyz|~~~}||||{{{{{|||||}}~~~~|{ywvusrrqqrrsuwx{}~}{zywwvuutttssssssttttuuuvvvwwxxyzz{{|}}~~~}|{zxwutsqpnmllkkllmnpqsvxz}~|{zxwvuttsssrrrrrrrssstttuuvvwwxxyzz{||}~~~}|{yxwvtsrrqqqrrsuvxz}~}}||{{{{{{{{{{|||}}}~~~}|zyyxxwxxyz{}~~~}}}}}}}}}}~~~~~}{zyxwuutssssstuvxy{}~}|{zyxxwwwwvvvvvvvwwwwwxxxyyyzz{{{||}}~~~~}}|{zyxwvutrqpoonnnnnopqrsuwy{}~}|{zyxwwvvvvuuuuvvvvvvwwwxxyyyzz{{||}}~~~}|{zyxwvuttsssssstuvxy{}~}||{{{{zzzzzzzz{{{{|||}}}~~~~~}|{zyxwvuttsssttuvwxz{}~}}||{{{{{{{{{{{{{{||||}}}~~~}|{zyxwvvuttttttuuvwyz|~~~}}|||{{{{{{{{{{|||||}}}~~~~}|{zzyxwvuuttttttuvwxy{|~~}}}||||{{{{{{|||||}}}}~~~~}}|{zyxwwvuutttttuuvwxz{}~~}}}|||||||||||||}}}}~~~~~~}|{zzyxwvvuuttttuuvwxyz|~~~}}}||||||||||}}}}}~~~~~~}||{zyyxwvvuuuuuuuvvwxz{|~~~~}}}}}||||}}}}}}}~~~~~~~}||{zzyxwwvvuuuuuuvvwxyz{}~~~}}}}}}}}}}}}}}~~~~~~~}}|{{zyyxwwvvuuuuuuvvwxy{|}~~~~}}}}}}}}}}}}~~~~~~~~}}||{zzyxxwwvvuuuuuvvwxyz{|~~~~~~}}}}}}}}~~~~~~~~~~}}|{{zyyxxwwvvvuuuvvwwxyz|}~~~~~~~~~~~~~~~~~~~~~}}|{{zzyxxwwvvvuuvvvvwxyz{|}~~~~~~~~~~~~~~~~~~}}||{{zyyxxwwvvvvvvvvwwxyz{|~~~~~~~~~~~~~~~~~}}||{{zzyyxxwwvvvvvvvwwxxyz|}~~~~~~~~~~~~~~~}}}||{{zzyyxxwwvvvvvvvwwxyz{|}~~~~~~~~~~~~~}}||{{zzyyxxwwwvvvvvvwwxxyz{|~~~~~~~~~}}|||{{zzyyxxwwwvvvvvwwwxyz{|}~~~~}}}||{{zzyyyxxwwwwvvwwwwxxyz{|}~~~}}}|||{{zzyyxxxwwwwwwwwwxxyzz{}~~~~}}}|||{{zzzyyxxxwwwwwwwwxxyyz{|}~~~~~~}}}||{{{zzyyyxxxwwwwwwwxxxyz{{}~~~~~~}}}|||{{zzzyyyxxxxwwwwwxxxyzz{|}~~~~~~}}}|||{{{zzzyyyxxxxxxxxxxxyyz{|}}~~~~~}}}}|||{{{zzyyyyxxxxxxxxxxyyz{{|}~~~~~~}}}}|||{{{zzzyyyyxxxxxxxxyyyzz{|}}~~~~~~~}}}}|||{{{zzzyyyyyxxxxxxyyyzz{{|}~~~~~~~}}}}|||{{{{zzzyyyyyyxxxyyyyzz{{|}}~~~~~~~}}}}||||{{{zzzzyyyyyyyyyyyyzzz{||}~~~~~~~}}}}}||||{{{zzzzzyyyyyyyyyyzzz{{|}}~~~~~~~~}}}}}||||{{{zzzzzyyyyyyyyyzzz{{||}~~~~~~~~}}}}}||||{{{{zzzzzzyyyyyyzzzz{{||}}~~~~~~~~~}}}}}||||{{{{zzzzzzyyyyyzzzz{{{|}}~~~~~~~~~}}}}}|||||{{{{zzzzzzzzzzzzzz{{{||}}~~~~~~~~~}}}}}|||||{{{{{zzzzzzzzzzzz{{{||}}~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzz{{{|||}}~~~~~~~~~}}}}}||||||{{{{{zzzzzzzzz{{{{|||}}~~~~~~~~~}}}}}}||||||{{{{{{{zzzzz{{{{{|||}}~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{{||}}}~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{|||}}~~~~~~~~~~}}}}}}}||||||{{{{{{{{{{{{{{||||}}}~~|{ysonkffebabbbbcefilorsw|{vqmifb_^^^_aadgjnrvx{~~|xtqlhd`\XTQNMLJIHGFFFGHIJKNQTX\biq|jjxjs~}}}~~~~|yvspnjgeca__]\]]^_abcfhknqsuxz|~~|{yxvutssssssstuwy{~~sh`am|uqpprtuwz|~~}}||{{{{|}~wnhehp}xqlifeddccbbbbcbbbaaaa``______```abdeghjloqsvy{~{skeabhrwqligfdccccccbbbbbbaaa`````__``aabcdfgikmoqsuxz}|vnf^WQPSZfs{rjc]XTSQONMMMLLLLLKKKKJJJJJIJJJJKLMNOPRTVXZ\_bdgiloqtvy{~|tmfa_`fp}|wsponmllllllmmmnnnnnooopppqrstuvwy{|~{wvy}{ywvuuttssssrrrqqqqqqqqqqrrstuvwyz|~wpidbbgoz{uokifdb`__^]]]\\\[[[ZZZZZZZZZ[\\]^_abdegikmoqsuwz{}{wrke^WQNMPV_kwwpic^[YVTRQPPOOOOONNNNNOOOOOPPQRSTUVXY[]_acegjloqtvx{}}wpjea_`emw{vtromlkjjjjjjjjjjjkkkllmmnopqrsuvxz|}{vssv{|zxwvuttsssrrrrrrrrrrrsstuvwxyz{}yslgcabfmvzuqnjhecba`_^]]\\\\[[[[[[[\\\]^_`abcdfhikmnprtvxz{}{wsmhb\VRPORW_it~xrmheb_\ZYWVUUTTTTTTTTTTUUUVWWXYZ\]^`acegikmoqsvxz|~}wrmgc`_`dkt~|yvsqomlkkjjjjjjjjjkkklmmnopqrsuvxy{}zvrpprw~|zxwvutssrrrrrrrrrrssttuvvwyz{|~ztojebabekr|{wtpmjhfecbaa`___^^^^^^___``abcdefghjkmnprsuwxz|}|xtpkf`[WTRRTY_hqzzupnjgda_^][[ZYYYYXXYYYYZZ[\\]^_abcefhikmoqstvxz|~|wsnieb__`diqy{xusqponmllllkkllllmmnoppqrsuvwyz|~~zuqnmmotz}{zxwutrqpnmljiihgggffggghijklnoqsuwy{~|xsoljjjnsy}|zyxxwwwvuuuuuttsssssssrrsstuuuvwyz|}~~|zxuqnjfb_[XVVWZ^dlu~}|{zyxxwwwwwvvvwwvwwwxxxyyz{{|}~}yurpooqty~|{yxwvvuttsrrqqqppooooooooppqrrstuvwxz{|}~|yvspmifdb``adhmt{~{yxvuttsssssssssttttuuvvwxxyz{|}~}zvsqnmmmosw{~zwtrponmlkkjjjjjjjjjjkkllmmnopqrstvwyz{}~}zwtqomkjklnrv{~zwusqponmllkkkkkkkkkllmmnnopqqrstuwxyz{}~|zwtqnkhgfffhjnrv{}yvrpmkjhgfedddccccccccdddeefgghijklmopqrtuvxyz|}~}zxuspnljiiijmpsx|~{yxwuttssrrrrrrsssttuuvwwxyz{|}~}zxwvvvxz}~|{zyxwwwvvvvvvvvvwwwxxyzz{|}~~~{ywtrqooopqsvz}~{ywutrqpponnnmmmmmmmnnnooppqqrsttuvwxyz{|}~~}{ywuspnljhgffghjlorvy}}zwusqonmlkjjiiiiiiiiiijjjkklmmnopqrstuvwxyz{|}~}|zxvtrponmlmmoqtwz~}|zyxwwvvuuuuuuuuvvvwwxyyzz{|}~}|zyxwwwxz|~~}|{{zzzzzyyyzzzz{{{||}}~}{ywutsrqqrstvy{~}{yxwvutsrrqqqqqqqqqqqrrrssttuuvvwxxyz{{|}~~~}|{yxvtsqomlkjiijjkmortwz}~|zxvusrqppoonnnnnnnnnooooppqqrsstuvvwxyzz{|}~~}|{yxvusrqpooopqrtvy{~~|{{zyyxxxxxxxxxxyyyzz{{||}~~~|{zyxxwxxy{|~}}}|||||||||||}}}~~~}|zyxvutsssstuvxz|~}|zyxwwvuuuttttttttttuuuuvvwwxxyyzz{{||}}~~~~}|{zywvusrqonmllllmmoprtvx{}}|zyxwvuttssrrrrrrrrrrssssttuuvvwwxyyzz{||}}~~}|{zyxvutsrrqqqqrstvxz|~~}}|{{{zzzzzzzz{{{{|||}}~~~}|{zyxxxxxyz{}~~~~}}}}}}}~~~~~~|{zyxwvuutttuuvwy{|~~}|{zzyxxxwwwwwwwwwwwwwxxxxyyyzz{{{||}}}~~~~~}||{zyxwvutsrqpoonnnoopqrtvwy|~}|{zyxxwvvvuuuuuuuuuuuvvvwwwxxxyyzz{{||}}}~~~}}|{zzyxwvutsssrrrstuvwy{}~~}||{{{zzzzzzzzzzz{{{{|||}}}~~~}}|{zyxwvuuttttttuvwyz|~~~}|||{{{zzzzzzzz{{{{{|||}}}~~~~~}|{zyyxwvuutttttuvwxy{|~~}}|||{{{{{{{{{{{{{||||}}}}~~~~~}|{{zyxwvvuutttuuuvwxz{}~~}}|||{{{{{{{{{{{|||||}}}~~~~~}||{zyyxwvvuuuttuuvwxyz|}~}}}|||||{{{{{||||||}}}}~~~~~~}}|{zzyxxwvvuuuuuuvvwxy{|~~~}}}||||||||||||||}}}}}~~~~~~}}|{{zyyxwwvvuuuuuuvwxyz{}~~~~}}}||||||||||||}}}}}~~~~~~}}||{zzyxxwvvvuuuuvvvwxyz|}~~~}}}}|||||||}}}}}}}~~~~~~~~}||{{zyyxwwvvvuuuuvvwxxz{|~~~~}}}}}}}}}}}}}}}}~~~~~~~~~}}||{zzyyxwwvvvvuvvvwwxyz{}~~~~~}}}}}}}}}}}}}}~~~~~~~~~}}||{{zzyxxwwvvvvvvvvwxxyz|}~~~~}}}}}}}}}}}}~~~~~~~~~~}}||{{zzyyxxwwvvvvvvvwwxyz{|~~~~~~~}}}}}}}}~~~~~~~~~~~~}}}||{{zyyxxwwwvvvvvvwwxxyz{}~~~~~~~~~~}~~~~~~~~~~~~~~}}}||{{zzyyxxwwwvvvvvvwwxyz{|}~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzyyxxwwwwvvvwwwxxyz{|}~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzyyxxxwwwvvvwwwwxyyz{}~~~~~~~~~~~~~~~~~~~~~~~}}}||{{{zzyyxxxwwwwwwwwwxxyz{|}~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzzyyxxxwwwwwwwwxxyyz{|}~~~~~~~~~~~~~~~~~~~}}}|||{{{zzyyyxxxwwwwwwwwxxyz{|}~~~~~~~~~~~~~~~~~}}}}||{{{zzzyyxxxwwwwwwwwxxyyz{|}~~~~~~~~}}}}|||{{{zzyyyxxxxwwwwwxxxyyz{|}~~~~~~~~}}}}|||{{{zzzyyyxxxxxwwxxxxyyzz{|}~~~~~~~~~}}}}||||{{{zzzyyyxxxxxxxxxxyyzz{|}~~~~~~~~~}}}}}|||{{{zzzzyyyxxxxxxxxxyyyz{{|}~~~~~~~~~~}}}}}||||{{{zzzzyyyyxxxxxxxyyyzz{|}~~~~~~~~~~~}}}}}|||{{{{zzzzyyyyxxxxxyyyyzz{||}~~~~~~~~~~~}}}}}||||{{{{zzzzyyyyyyyyyyyyzz{{|}}~~~~~~~~~~~}}}}}|||||{{{zzzzzyyyyyyyyyyyzz{{||}~~~~~~~~~~~}}}}}}||||{{{{zzzzzyyyyyyyyyyzz{{||}~~~~~~~~~~~~}}}}}}||||{{{{zzzzzyyyyyyyyzzzz{{|}}~~~~~~~~~~~}}}}}}|||||{{{{zzzzzzzyyyyzzzzz{{||}~~~~~~~~~~~}}}}}}}||||{{{{{zzzzzzzzzzzzzzz{{||}}~~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzzzzz{{{||}~~~~~~~~~~~~}}}}}}|||||{{{{{{zzzzzzzzzzzz{{{||}}~~~~~~~~~~~}}}}}}}|||||{{{{{{zzzzzzzzzz{{{{||}}~~~~~~~~~~~~}}}}}}||||||{{{{{{{zzzzzzzz{{{{|||}}~~~~~~~~~~~}}}}}}}||||||{{{{{{{{zzzzz{{{{{|||}}~~~~~~~~~~~~}}}}}}}||||||{{{{{{{{{{{{{{{{{|||}}}~~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{{||||}}~~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{||||}}}~~}zyuomlgdec``a`abcdgilpqsx||wrnjfc_^]]^_`adgkosvx{~~|xtqmiea]YVSPNMLKJIIIJJLMNOQUX[_djr{yelnz}|||}~}|yvspnkgeba_^][[\\]_`acfhknpruwz{~~}|{yxwuusttttuuvwy{~|pfbiywrooprtuwy{}~~}||{{zzzzz{|}~unihny{smifdcbbaaaaaaaa```____^^^]^^^__`abcefhjloqtvy{~{rkedgo{yrmifecbbaaaaaaaaa```____^^^^^__`abcdegikmnqsuxz}}ume]VSSXbo|vme^YURQOMLLKKKKJJJJJIIIIIHHHHIIJJKLMOPRTVXZ]_bdgiloqtvy{}{tmgcbemy~xtpnmlkjjjkkkllllmmmmnnnooppqrstvwyz|~{xy~}{ywuttsrrrrqqqqpppppooopppqqrstvwxz|~~voiedgmv}vqlhfca`_^]\\\[[[ZZZYYYYYYYYYZZ[\]^_`bdegikmoqsuwy{}|wrkd]VROPU]gs~{sle`[YVTRQPONNNNMMMMMMMNNNNOOPPQRSTVWY[]_acegjloqtvx{}}vpjebbekt}xtromkjjiihhhhiiiiijjjkklmmnoprstvxz{~{wuvz|zxvutssrrrqqqqqqqqqqqrrsstuvwxz{}xrlgddfks}|vqnkheca`_^]]\\[[[ZZZZZZZZ[[\\]^_`acdfgikmnprtvxy{}|xsmga[VSQRV]fp{{uoiec_\ZXWVUTSSSSSSSSSSSTTUUVWXYZ[]^`acegikmoqtvxz|~}wrlgdaadiq{}zwtqomlkjiiiiiiiiiijjkklmmnoqrsuvxy{}zvsrsv|}zxwutssrrqqqqqqqqqqrrsttuvwxy{|~ytnifccejpy|xuqnkhfdcba`__^^^]]]]]]^^__``abcdeghjkmnprsuwxz|}|xtoje`[WUTUX^enw|wrnkgdb_^\[ZYYXXXXXXXXXYYZZ[\]^_`bcefhikmoqrtvxz|~|wsniebaadhow|yvsqpnmllkkkkkkkkkllmmnopqrstvwyz|~~yuroopsx~}{zxvusrqonlkjihggffeefffghhiklnprtvwz|~|wromkkmrw}|zyxwwvvvttttttsrrrrrrrqqrrssttuvwyz{|~}zxuqnjfb_[YXXZ]cjr{~|{zyxwwvvvvvvvvvvvvvwwwxxyyz{|}~|xurqpqtx~~|{yxwvuttsrrqqppooonnnnnonooppqrstuvwxz{|}~|yvspmjgdbbbdhlrx|zxvuttsssrrrrrsssssttuuvvwxyz{|}~}yvsqonnorvz{wtrpnmlkkjjiiiiiiiiijjkkllmnopqrsuvwyz{}}zwtqomlllnquy~{xusqpnmmlkkkjjjjjjkkkllmmnoppqrstuwxyz|}~|yvspmkihgghjmquy~zvspmkjhgfedcccbbbbbbbcccddeffghijklmopqrtuvxyz|}~}zxuspnlkjjklorw{~|zxwutssrrrrrrrrrsssttuvvwxyz{|}~}zyxwwxz|~|{zyxwvvvuuuuuuuvvvwwxxyyz{|}}~~{ywtsqpppqsvy||ywutrqpoonnmmmmllmmmmnnnooppqrsstuvwxyz{|}~}{ywuspnljihgghjlnqtx{~{xusqonmlkjiiihhhhhhhiiijjkklmmnopqrstuvwxyz{|}~~|zxvtrqonnmnoqsvy|}|zyxwvvuuuttttuuuuvvwwxxyzz{|}~~|zyxxxxz{~~}|{{zzyyyyyyyyyzzz{{||}}~~}{ywvtsrrrstvxz}}{zxwutssrrqqppppppppqqqrrrssttuvvwxxyz{{|}~~~}|{yxvtsqonlkjjjklmoqsvy||zxvusrqpponnnnmmmmnnnnnooppqqrsstuvvwxyzz{|}~~~|{yxvusrqppppqrtvx{}~|{zzyxxxwwwwwwwxxxyyyzz{||}}~~}{zyyxxyy{|~~}}|||{{{{{{{||||}}}~~}|{yxvutttttuvxy{~}|zyxwvvuuttttsssstttttuuuvvvwwxxyzz{{||}}~~~~}|{zywvusrqpnnmmmmnopqsuxz|~|zyxwvutssrrrqqqqqqrrrrsssttuuvvwwxyyzz{||}~~~}|{zyxwutssrrqrrstvwy{~~}||{{zzzzzzzzzzzz{{{||}}~~~}|{zyyyyyyz{}~~~}}}}}}}}}}}}~~~~~}{zyxwvvuuuuuvwyz|~~}|{zyyxxwwwvvvvvvvvvwwwwxxxxyyzzz{{|||}}~~~~~}||{zyxwvutsrqppooooopqrsuwy{}}|{zyxwwvvuuuuututuuuuuuvvvwwwxxyyzz{{||}}}~~~~}|{{zyxwvuttssssstuvwxz|~~~}||{{zzzzzzzzzzzzzz{{{|||}}}~~~}}|{zyxwvvuutttuuvwxz{}~~}||{{{zzzzzzzzzzz{{{{{|||}}}~~~~}|{zyyxwvvuuuuuuvwxyz|~~}}||{{{{zzzzzzz{{{{{||||}}}~~~~~}|{{zyxwwvuuuuuuvvwxy{|~~~}|||{{{{{{{{{{{{{{||||}}}}~~~~~}||{zyyxwvvuuuuuuvwwyz{}~~}}||||{{{{{{{{{{|||||}}}}~~~~~}}|{{zyxxwvvvuuuuvvwxyz|}~~}}||||||{{{{|||||||}}}}~~~~~~}}||{zzyxwwvvvuuuvvwxxz{|~~~}}}|||||||||||||||}}}}}~~~~~~}}||{zzyxxwwvvvuuvvwwxyz{}~~~}}}}||||||||||||}}}}}~~~~~~~~}}|{{zyyxxwwvvvvvvvwwxyz|}~~~}}}}}|||||||}}}}}}}~~~~~~~~}}||{zzyyxxwwvvvvvvwwxyz{|~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~}}||{{zzyxxwwwvvvvvwwxxyz{}~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~}}||{{zzyyxxwwvvvvvwwwxyz{|}~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}||{{zzyyxxwwwvvvvwwxxyz{|~~~~~~}}}}}}}}}}}~~~~~~~~~~~}}}||{{zzyyxxxwwwwvwwwwxyyz|}~~~~~~~}}}}}}}~~~~~~~~~~~~~}}}|||{{zzyyxxxwwwwwwwwxxyz{|}~~~~~~~~~~~~~~~~~~~~~~~~~}}|||{{zzyyxxxwwwwwwwwxxyyz{|~~~~~~~~~~~~~~~~~~~~~~~~~~}}}||{{{zzyyxxxwwwwwwwwxxyz{|}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzzyyxxxwwwwwwwxxyyz{|}~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{zzyyyxxxwwwwwwxxxyzz{|~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{zzzyyxxxxwwwwwxxxyyz{|}~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{zzyyyxxxxxwwxxxxyyz{{|}~~~~~~~~~~~~~~~}}}}|||{{{zzzyyyxxxxxxxxxxyyzz{|}~~~~~~~~~~}}}}||||{{{zzzyyyyxxxxxxxxyyzz{||}~~~~~~~~~~}}}}}|||{{{zzzzyyyyxxxxxxxyyyz{{|}~~~~~~~~~~~}}}}}||||{{{zzzzyyyyxxxxxyyyyzz{|}}~~~~~~~~~~~~}}}}}}|||{{{{zzzzyyyyyyyyyyyyzz{{|}~~~~~~~~~~~~}}}}}}||||{{{{zzzzyyyyyyyyyyyzz{{|}}~~~~~~~~~~~~}}}}}|||||{{{zzzzzyyyyyyyyyzzz{{||}~~~~~~~~~~~~}}}}}}||||{{{{zzzzzyyyyyyyyzzzz{{|}}~~~~~~~~~~~~}}}}}}|||||{{{{zzzzzzyyyyyyzzzz{{||}~~~~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzzzzz{{||}}~~~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzzzzz{{||}}~~~~~~~~~~~~~}}}}}}|||||{{{{{{zzzzzzzzzzzz{{{||}}~~~~~~~~~~~}}}}}}}|||||{{{{{{zzzzzzzzzz{{{{||}}~~~~~~~~~~~~~}}}}}}||||||{{{{{{{zzzzzzzz{{{{|||}}~~~~~~~~~~~}}}}}}}||||||{{{{{{{{zzzzz{{{{{|||}}~~~~~~~~~~~~}}}}}}}||||||{{{{{{{{{{{{{{{{{|||}}~~~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{{|||}}}~~~~~~~~~~~~}}}}}}}}|||||||{{{{{{{{{{{{{{||||}}~~~~~~~~~~~~}}}}}}}}|||||||{{{{{{{{{{{{{||||}}}~~zyvpmlhdec``a`aabdfikoqrx{}xsokgd_^]]]_``dgjnrvwz~~|yurnjfb^ZWSQNNLKJIIIIJKLMOPTWZ^cipyfzomw~||||}}}ywtqnkgeca_^][[\\]^`acfhkmprtwy{}~|{zxwvutttttuuvwy{}~rgbfuxsonoqstwy{}~~~}||{{zzzzz{|}~woihlv|unjfdcbbaaaaaaaa```____^^^]^^^^_``bcefhjlnqsvx{}|tlfdfmy{tnjfedbbaaaaaaaaa```____^^^^^__`aabdeghjlnpsuwz|~~wog_XSSW`lyxog`ZURQOMLLKKKKJJJJJIIIIHHHHHIIIJKLMNPRSUWZ\_adfilnqsvx{}}unhcbdkvytpnmlkjjjkkkklllmmmmmnnooppqrstuwxz|~|yy}}|ywvttssrrrqqqqpppppooopppqqrstuwxz{}xqjfdfktxrligdb`_^]\\\[[[ZZZYYYYYYYYYZZ[\\^_`bceghkmoqsuwy{}}xsle^XSPPT[ep||tmfa\YWTRQPONNNMMMMMMMMMNNNNOPPQRSTVWYZ\^`cegjlnqsvxz|~xqkfbbdir}~ytrpmljjiihhhhhiiiiijjkkllmnopqstvwy{}|xuvy}zxwutssrrrqqqqqqqqqqqrrsstuvwxz{}~zsmhedejq{~xrolhfcb`_^]]\\[[[ZZZZZZZZ[[\\]^_`abdegijlnprtvxy{}~}ytnhb\WSRRU[dnx|vpjfc`]ZXWVUTTSSSSSSSSSSTTUUVWXYZ[\^_acegikmoqsuxz|~~xsmhdbachoy~zxtqomlkjjiiiiiiiiijjkkllmnoprstvwy{}{wsrru{}zywvtssrrqqqqqqqqqqrrsstuvwxyz|}zuojfdceiow}yvrnkigecba`__^^^]]]]]]^^^_``abcdeghiklnpqsuvxz{}~}yupkfa\XUTTW\clu}xrolheb`^\[ZYYXXXXXXXXXYYZZ[\]^_`acdfgikmnprtvxz|}}xtojfcaacgmu~}yvtrpommlkkkkjkkkkllmmnopqrstuwxz|}zvrpoprw}~zwtqnljhgecb`_^^]]]]]]^__abdfgilnqtvx{~}zvsrrstuu}~{zxwutrpponmlkiihhhhgghhijklmoqrtvwy{|~}{yvtqmjhfeefgghm{~|{zxwvuuttssssrssssttuvxyz|}|yvsqoqv|ywutrqonmlkjjihhhgghhhhijklmnoprsuvwyz|}}zwtqmieca`chqz}|zyxwvuttssssssssttuuvwxyz{|~{vrmifdceiov~~{xvtrpomlkjjiiihhhiiiijjklmnopqrsuvxy{|~}yuqmifdccfkqx~|ywtsqpnmmlkkkjjjjjjkkkllmnopqrstuwxy{|}~{xtplhea_]]^bgmt{}{xuspnljigfeddccbbbbbbbbccdeffghjklmnpqrtuwxz{|~~|yvrokhfdcdgkqx~|{yxwvuuttssssstttuuvvwxyz{|}~{xtronnpsw}~|{zyxwvvuuuuuuuuuuvvwwxyz{||~~{xuqnkigfgimqw}}{ywutrqponnmmmllllllmmmnnoopqrrstuvwxyz{|}~~|zxvspmjgdb`__`cglqw}~{ywusqpnmlkkjjiiiiiiiiijjkklmnnopqrstvwxyz{|}~~|zwtroljhgghjmqw}~}|zyyxwwvvvvvvvvvwwxxyyz{|}~|ywtrqppqtw|~}|{{zzyyyyyyyyyzz{{||}}~~|ywtqomkjijlnrw|}{zxwvutsrrqqpppppppppqqqrrsstuuvwxxyz{||}~~}{zxvtromkhfdcccdfimrw|~|zywvusrrqppooonnnnnnoooppqqrsttuvwwxyz{|}}~}|zxvtromljjjjlorv{~}|{{zzyyyyxxyyyyyzz{{||}~~|zxvtsrqrsuw{~~}}|||{{{{{||||}}}~~~|zxvtrpnmlllnpsvz~|{zyxwvvuuttttssssttttuuuvvwwxxyyzz{||}}~~~}|{zxwusrpnljigfffgilorvz~}|zyxwvuuttsssrrrrrrssssttuuvvwwxyyz{{|}}~~~}{zywusrpomlllmnpsvz~~~}|||{{{{{{{{{{|||}}~~~|{ywvtssrstuxz~~~~}}}}}}}}}~~~}|zywutrqoonnopqtwz}}|{{zyyxxwwwwvvvvvvwwwwxxxyyyzz{{{||}}~~~~~}||{zywvusrpomlkjiiijlnpsvz}~}|{zyxxwwvvvvvuuuuvvvvvwwwxxyyzz{{||}}~~~}|{zyxvusrqponnnoprtwz}~}}||{{{{zzzzzzz{{{{{|||}}~~~~}|zyxwutrqpoooopqsvx{~~~}|||{{{{{{{{{{{{{||||}}}~~~}|{zxwvusrqppoopqsuwz}~~}}|||{{{{{{{{{{{||||}}}~~~~}|{zyxvutsrqppppqrtvx{~~~}}|||||{{{{{|||||}}}}~~~~}|{zyxwvusrqqpppqrsuwz|~~}}}|||||||||||||}}}}~~~~~}}|{zywvutsrqqqpqrstvx{}~~~}}}}|||||||||}}}}}~~~~~}}|{zyxwvutsrqqqqrrtuwz|~~~}}}}}||||}}}}}}}~~~~~}}|{zyxwvuttsrrqqrrsuwy{}~~~~}}}}}}}}}}}}}}~~~~~~~}|{{zyxwvutsrrrqrrstvwz|~~~~~}}}}}}}}}}}~~~~~~~~}||{zyxwvuutsrrrrrstuwy{}~~~~~}}}}}}}}~~~~~~~~~}}|{zyyxwvuttssrrrstuvxz|~~~~~~~~~~~~~~~~~~~~~}}|{{zyxwvvuttssssstuvwy{}~~~~~~~~~~~~~~~~~~}}|{{zyyxwvuutsssssstuvxz|~~~~~~~~~~~~~~~~~}}||{zzyxwwvuttssssttuvwy{|~~~~~~~~~~~~~~~}}||{{zyxxwvvutttssttuvwxz{}~~~~~~~~~~~~}}||{{zyyxwwvuuttttttuuvxy{|~~~~~~~~~}}}||{zzyxxwvvuutttttuuvwxz{}~~~}}}||{{zyyxwwvvuuttttuuvwxy{|~~~~~}}||{{zzyxxwwvvuuuttuuvvwyz{}~~~~}}||{{zzyyxxwvvvuuuuuuvvwxy{|~~~~~}}|||{{zzyyxwwvvvuuuuuvvwxyz{}~~~~~~}}}||{{zzyyxxwwwvvvuvvvvwxyz{|}~~~~~}}}|||{{zzyyxxwwwvvvvvvvwxxyz|}~~~~~~}}}|||{{zzzyyxxwwwwvvvvwwxxyz{|}~~~~~}}}}|||{{zzyyyxxwwwwwwwwwxxyz{|}~~~~~~~}}}}|||{{{zzyyyxxxwwwwwwwxxyyz{|}~~~~~~~}}}}||||{{zzzyyyxxxwwwwwwxxyyz{|}~~~~~~~}}}}}|||{{{zzzyyyxxxxxxxxxxyyz{{|}~~~~~~~~}}}}||||{{{zzzyyyxxxxxxxxxyyzz{|}~~~~~~~~}}}}}|||{{{{zzzyyyxxxxxxxxyyzz{||}~~~~~~~~}}}}}||||{{{zzzzyyyyyxxxxyyyzz{{|}~~~~~~~~~}}}}}||||{{{zzzzyyyyyyyyyyyzz{{|}}~~~~~~~~}}}}}||||{{{{zzzzyyyyyyyyyyzzz{||}~~~~~~~~~~}}}}}||||{{{{zzzzzyyyyyyyyzzz{{|}}~~~~~~~~~}}}}}}||||{{{{zzzzzzyyyyyzzzz{{||}~~~~~~~~~~~}}}}}|||||{{{{zzzzzzzzzzzzzz{{||}}~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzzz{{||}}~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzz{{{{||}}~~~~~~~~~}}}}}}}|||||{{{{{{zzzzzzzz{{{{||}}~~~~~~~~~~}}}}}}||||||{{{{{{zzzzzz{{{{{||}}~~}zytonkgeeb`bbabcdehknqrvz~{wrnifb_^]^_`abfimquwy}~|xtplhd`[XTQNLLJIHGGGGHIJLMOSVZ^cjr}qgkq~~}}}~~|yvspnjgeca_^]\\\]^`abegjmortvy{}~}|zywvusrsssssstvwz|xlcbl~~vqopqsuvy{}~~~}||{{{{{{|}~{rkgip|yrmifedcbbbbabbbaaa````__^^^____`aacdfhiknpruxz}wohdchr~xrmifedcbbbbbbbbaaaa```_______``abbdefhjlnprtwy|~yrjbZTRTZer}tld]YURQONMLLLLKKKKKJJJJIIIIIIIJJKLMNPQSUWY[^`cfhkmpsuxz}xqjdabfo|}wspomllkkkllllmmmmnnnnoooppqrstuwxz|}~ywy}{ywvuttsssrrrqqqqpppppppqqrrstuwxy{}{slgddgoy|upkifdb`_^^]]\\[[[ZZZZZYYYYZZZ[\]^_`acefhjlnpruvy{|~~ztnhaZTPNPV_jvyrjd_[YVTRQPOOONNNNNNNNNNNOOOPPQRSTUWXZ\^`bdfiknpsuwz|~zsmhcaaemw|wtromlkjiiiiiiiijjjjkkklmmnopqrtuwy{}~yutv{|zxwuutssrrrrrqqqqqqrrrsstuvwxy{|~|vojeccflu{uqnkhecb`_^^]]\\[[[[[[[[[[[\\]^_`abdeghjlnpqsuwy{|~}yupjd^YTQQRW^hs}zsnheb_\ZXWVUTTTSSSSSSTTTTUUVWXYZ[\^_abdfhjlnpsuwy{}zuojeb`aejs}|zwsqomlkjjjiiiiijjjjkklmmnopqstuwyz|~}xtqqsw~|zxwvutssrrrqqqqqqrrssttuvwxyz|}|wqlgdbcfkr{{xuqmkhfecba``__^^^^^^^^^__``abcdefhiklnoqrtvxy{|~}zvrmhc^YVSSUY_gpy{vqnkgdb_^\[ZZYYXXXXXXYYYZZ[\]^_`abdegijlnprsuwy{}~yupkgca`adipy|xvsqpommllkkkkkklllmmnopqrstuwxz{}~|wspnnptz}{zxwutrqpnmkjiihggffffgggiijlmoqsuwxz}~yuqmkkknrx}|zyxxwwwvuuuutttssssrssrrrsstuuvwxy{|}}{yvsolhd`]ZXWWZ^dkt}}|{zyxwwwwvvvvvvvvvvwwxxxyzz{|}~~zwsqpoqty~|{yxwvuuttsrrqqpppoooooooooppqrsstuwxyz{}~}zwtqnkhecaabdhmsz|yxvuttssssssssssttttuuvvwxyyz{|~{xurpnmnosw{~zwtrpnnmlkjjjjiiiiijjjkkllmnopqrstuwxy{|~|yvspnlkklnrvz~{xusqponmllkkkkkkkkkkllmmnoopqrstuvwxz{|~}{xuroljhgfghjmqvz~zvspmkjhgfeddcccccbccccdddeffghijklmnoqrstvwxz{|}}{yvtqomkjijkmpsx|~|zxwuttssrrrrrrrsssttuuvwxyyz{|~~{yxwvwxz}~|{zyxwwvvvvvvvvvvvwwwxyyz{{|}~|zxusrppoprtvz}~|ywutrqpoonnmmmmmmmmmnnnooppqrrstuvwxxyz{|}~~|zxvtqomkihggghjloruy|}zwusqonmlkjjiiihhhhiiiijjkkllmnoppqrstuvwxy{|}~~|{ywusqpnmmmnoqtwz}}|zyxwvvuuuuuuuuuuvvwwxxyyz{||}~~}{yxxwxxz|~~}|{{zzzyyyyyyzzzz{{{||}~~~|zxvussrrrstvy{~}{zxwvttsrrqqqqppppqqqqrrrssttuuvwwxyzz{||}~~|{zxwusrpnmkjjjjklmortwz}~|zxvusrqppoonnnnnnnnnnoooppqqrrsttuvwxxyz{||}~~}{zxwutsqqpoppqrtvy{~~|{{zyyxxxxwwxxxxxyyyzz{{||}~}|{yyxxxyy{}~}}|||||{{||||||}}}~~~}{zxwvutssttuvxz|~}|zyxwwvuuutttttttttttuuuvvvwwxxyyzz{{||}~~~}|{zyxwutsqponmllmmnoprtvx{}}|zyxwvutsssrrrrrrrrrrrssstttuuvwwxxyzz{{|}}~~~}|{zyxwvutsrrqqrrstvxz|~~}}|{{{zzzzzzzzzz{{{|||}}~~~|{zzyxxxyyz{}~~~}}}}}}}}}}~~~~~}|{zxwvvuutuuuvxyz|~~}|{zzyxxwwwwwvvvvvwwwwwxxxxyyyzz{{{||}}}~~~~~}}|{zzyxwvusrqqpoonooopqrtvwy{~}|{zyxxwvvvuuuuuuuuuuuuvvvwwwxxyyyzz{{||}}~~~~~}||{zyxwvuutsssssstuvwy{|~~}||{{{zzzzzzzzzzzz{{{||||}}~~~~~}|{zyxxwvuuttttuuvwyz|}~~}||{{{{zzzzzzzzz{{{{{|||}}}~~~~~}||{zyxwwvuuuttuuvwxy{|~~}}||{{{{{{{{{{{{{{{||||}}}~~~~}||{zyxxwvvuuttuuvvwxz{}~~~}}|||{{{{{{{{{{{{||||}}}}~~~~}}|{zzyxwwvvuuuuuuvwxyz|}~}}}||||{{{{{{{{|||||}}}}~~~~~~}||{zyyxwwvvuuuuuvvwxy{|~~~}}}|||||||||||||||}}}}}~~~~~~}||{zzyxxwwvvuuuuvvwxyz{}~~~~}}}|||||||||||||}}}}}~~~~~~}}|{{zyyxwwvvvuuuvvwwxyz|}~~}}}}}|||||||||}}}}}}~~~~~~~}}||{zzyxxwwvvvvvvvvwxxz{|~~~~}}}}}}}|||}}}}}}}}~~~~~~~~}}||{{zyyxxwwvvvvvvvwwxyz{}~~~~}}}}}}}}}}}}}}}}}~~~~~~~~}}||{{zzyyxxwwvvvvvvwwxxyz|}~~~~}}}}}}}}}}}}}}~~~~~~~~~~}}}||{{zyyxxwwwvvvvvwwwxyz{|}~~~~~}}}}}}}}}}}~~~~~~~~~~~}}}||{{zzyyxxwwwvvvvvwwxxyz{}~~~~~~~}}}}}}}~~~~~~~~~~~~~}}|||{{zzyyxxwwwvvvvwwwxyz{|}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}|||{{zzyyxxxwwwwwwwwwxxyz{|}~~~~~~~~~~~~~~~~~~~~~~~~}}}||{{zzzyyxxwwwwwwwwwxxyzz{}~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzyyyxxwwwwwwwwwxxyz{|}~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{zzyyxxxwwwwwwwwxxyyz{|}~~~~~~~~~~~~~~~~~~~~~}}}}|||{{zzzyyxxxxwwwwwwxxxyz{{}~~~~~~~~~~~~~~~~~~~~}}}}|||{{{zzyyyxxxwwwwwwxxxyyz{|}~~~~~~~~~~~~~~~~}}}}|||{{{zzzyyyxxxxwwwwxxxyyz{|}~~~~~~~~}}}}}|||{{{zzzyyyxxxxxxxxxxyyzz{|}~~~~~~~~~}}}}}|||{{{zzzzyyyxxxxxxxxxyyzz{|}}~~~~~~~~~~}}}}}||||{{{zzzyyyyxxxxxxxxyyyz{{|}~~~~~~~~~~~~}}}}||||{{{{zzzyyyyyxxxxxyyyyzz{|}}~~~~~~~~~~~~}}}}}||||{{{zzzzyyyyyyxxyyyyyzz{||}~~~~~~~~~~~~}}}}}||||{{{{zzzzyyyyyyyyyyyyzz{{|}}~~~~~~~~~~~}}}}}}||||{{{{zzzzyyyyyyyyyyzzz{{||}~~~~~~~~~~~}}}}}}|||||{{{{zzzzzyyyyyyyyzzz{{||}}~~~~~~~~~~~~}}}}}}||||{{{{{zzzzzyyyyyyyzzzz{{||}~~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzyyzzzzzz{{||}}~~~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzzzzz{{||}}~~~~~~~~~~~~}}}}}}}|||||{{{{{zzzzzzzzzzzzz{{{||}~~~~~~~~~~~~~}}}}}}||||||{{{{{zzzzzzzzzzzz{{{||}}~~~~~~~~~~~~}}}}}}}||||||{{{{{{zzzzzzzzz{{{{||}}~~~~~~~~~~~~}}}}}}}||||||{{{{{{{zzzzzzz{{{{|||}}~~~~~~~~~~~~}}}}}}}||||||{{{{{{{{{{{{{{{{{|||}}~~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{{{|||}}}~~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{{||||}}~~~~~~~~~~~~}}}}~~~|{{{ywxvtvwvvvwwz|~t{|wrmfb^[YWVUTVWZ]acgkosvz|~~|yvsplifb`\[YXXVUUUUUVVWXZ[]acgkqwlytnx{uspquy~}yvtqokhfca^\ZXWVVUVVWXZ\^bdgjmpruxz}~~}~xmfiw{urqqsuvxz}~~~~~~~yof`]bky~tlfb_^]\\[\]]]^^__```````aaaabbcdefghikmoqsvxz}wof`]_ep~}umhc`_^]]]]]]^^____````````aaabbcddfghjkmoqsvwz|~}wog_VOJJMUao{ypg_XRNKJIGGGGGGHHHIIIJJJJKKKLLMNNOPQSTVXZ\^`behkmpsux{}~vngb`bis~xtpnmlkkkkllmnooppqrrsstuuvwxyz{|~|vrqt{{wusqpoonnnooooooopppppqqqrrsttuvxyz|~yqjc^\]bkvwpjeb`][ZYXXXXXXXXXXXXXYYYYZZZ[\\]^_abdegijmoqsuwy{}~ytnhaYRMIIMT^iu~~wphb\XUSQONMMMMMMMNNOOOPPQRRSTTUVWYZ[]_`bdgiknpsuwz|~wpjea_afny|wsqomkjjiiijjjkkllmnnoopqrstuvwyz|}~wsppsy}{xvtsrqppppppppppqqqrrrsttuvwxyz|}~{tmga]\]ahr|}wqlhfc`^][[ZYYYYYYYYYYYZZZ[[\]^^_`bcdfgiklnprtvxz{}~zvqke_YSOMMPV^hs|yrlgb`]ZXVUTTSSSSSTTTUUVVWXXYZ[\]_`acefhjlnpruwy{}~xrlgc`_aelu|yvsqomlkkjjjjkkkllmmnoopqrstuwxy{}~}xsommpu||ywutsrqqppppppqqqqrrsstuvwxyz{|~{upje`]\^agox}xspmigdba`_^]]\\\\\\]]]^^__`aabcdfghjkmnprsuwxz|}~{wsnic^YTQPPSX^gpyzuplifc`^][ZZYYYYYYYYZZ[\\]^_`abcdfgijlnpqsuwy{}}xsniea__adjrz|yvsrponmmlllmmmmnnoppqrstuvwyz|}}xsolkknrx{xvspnkihgedba```_^___``abdegijloqtvy{}{wsponopqqw|zxwvutrqponnmlkkjjjjjjjklmnoprsuwyz|}}zxurokhecbbcdddhu~|{zyxvvuuuttttttttuuvwxyz{}~}yvspnlmr{}zxvtsqponmllkkjiiiiijjjkklmnoprstvwyz{}~~|yvrokgc`^]_dlu}|zywwvuttttttttttuuvvwxyz{|}~yupkgca`bekqy|yvtrpomlkkjjiiiiiijjjkklmnopqrstvwyz|}|xtokgda``cgmt||zwusqponmllkkkkkkkklllmnnopqrstuvxyz|}~}zwsokgc_\ZZ[^cipw~~{xvsqnlkihfeedcccccccccdddefgghiklmnoqrsuvxy{|}~{xtqmifcaaadhmt{~|{yxwvuuttttttttuuvvwwxyz{|}~}zvspmlkmoty~}{zyxwwvvvvuuvvvvwwwxyyz{|}~}zwspligeddfinsz~{ywutsqpponnmmmmmmmmnnnoopqqrstuvwxyz{|}~~|zwurolieb`^]\^`dintz}|ywusrponmlkjjjiiiiijjjkkllmnoopqrstuwxyz{|}~~{yvsqnkhfedegjnty}|{zyxxwwvvvvvwwwxxyyzz{|}~~{xusqonnoquy~~}||{{zzzzzzzzzz{{||}}~}{xvspmkihghilotx~~|zywvutssrrqqqqqqqqqqrrrsstuuvwwxyzz{|}}~~}{yxvsqoligecaaabdgjoty}}{yxvutsrqqppoooooooooppqqrrsttuvwxxyz{|}~~}{zwusqnljihhhjlptx}~}||{zzzyyyyyyyzzz{{||}}~~|ywusqpopqrux|~~}}}|||||||}}}~~~}{ywusqomkjjklnqtx|~}|zyyxwvvuuutttttttuuuuvvwwxxyyzz{||}}~~~}|{zxwusqomkigfedeegilptx|}|{yxwwvuuttssssssssstttuuuvwwxxyzz{||}~~~}{zxvusqomlkjjklnqtx|~~}}||||{{{{||||}}}~~~|zxvusrqqqrsvx|~~~~~~~~~~}|zxwusqpnmmmmnprux{~}|{zzyyxxxwwwwwwwwxxxxyyyzzz{{||}}}~~~~}|{zywvtsqpnmkjihhhijlnqtx{~~}|{zyyxxwwwvvvvvvvvwwwwxxxyyzz{{||}}~~~}|{zywvtsqponmmmmnprux{~~}}|||{{{{{{{{{{||||}}}~~~}|zyxvusrponnnnopqtvy|~}}}|||{{{{{{{|||||}}}~~~~}|{zxwutsqpoonnopqsux{~~~}}}||||||||||||}}}}~~~~}|{zywvusrqpoooopqrtwy|~~~}}}||||||||||}}}}~~~~}|{zyxwutsrqpooooprsux{}~~}}}}}}|||}}}}}}}~~~~~~}|{zxwvutsqqppoppqsuwy|~~~~}}}}}}}}}}}}}~~~~~~~}|{zyxwvtsrqqppppqrtvxz}~~~~}}}}}}}}}}~~~~~~~}|{zyxwvutsrqqppqqrsuwy|~~~~~~}}}}}~~~~~~~~}||{zyxwvutsrqqqqqrstvxz}~~~~~~~~~~~~~~~~~}}|{zyxwvutssrqqqqrstuwy{~~~~~~~~~~~~~~~}}|{zyyxwvutsrrrqrrstuwxz|~~~~~~~~~~~}}|{{zyxwvuutssrrrrssuvxy{}~~~~~~~}||{zyxxwvuttsrrrrsstuwxz|~~~}||{zzyxwvvutsssrrsstuvxy{}~~}}|{{zyxxwvuutsssssstuvwyz|~~~}}||{zyyxwvvuuttsssstuuwxz{}~~}}||{zzyxxwvvutttsssttuvwyz|~~~}}||{{zyyxwwvuutttttttuvwxz{}~~~~}}||{zzyxxwvvuuttttttuvwxyz|~~~~}}||{{zyyxwwvvuuutttuuvvwyz{}~~~~}}||{{zzyyxwwvvuuuuuuuvvwxy{|}~~~}}}||{{zzyxxwwvvvuuuuuvvwxyz{}~~~~}}}||{{zzyyxxwwvvvvuuvvvwxyz{|}~~~~}}}|||{{zzyyxxwwwvvvvvvwwxxyz{}~~~~}}}}||{{zzzyyxxwwwvvvvvwwxxyz{|}~~~~~}}}|||{{zzyyyxxwwwwwwwwwxxyz{|}~~~~~~}}}|||{{{zzyyyxxxwwwwwwwxxyyz{|}~~~~~}}}}|||{{zzzyyyxxxwwwwwxxxyyz{|}~~~~~~}}}|||{{{zzzyyyxxxxxwxxxxyyz{{|}~~~~~~}}}}|||{{{zzzyyyxxxxxxxxxyyzz{|}~~~~~~~}}}}||||{{{zzzyyyyxxxxxxxyyzz{||}~~~~~~~}}}}|||{{{zzzzyyyyyxxxxyyyzz{{|}~~~~~~~~}}}}||||{{{zzzzyyyyyyyyyyyzz{{||}~~~~~~~}}}}||||{{{zzzzyyyyyyyyyyzzz{||}~~~~~~~~}}}}}||||{{{zzzzzyyyyyyyyzzz{{|}}~~~~~~~~}}}}||||{{{{zzzzzzyyyyyzzzz{{||}~~~~~~~~}}}}}||||{{{{{zzzzzzzzzzzzz{{||}}~~~~~~~~}}}}}||||{{{{{zzzzzzzzzzzz{{|||}~~~~~~~~~}}}}}}||||{{{{{zzzzzzzzzz{{{{||}}~~~~~~~~~}}}}}|||||{{{{{{zzzzzzzz{{{{||}}~~|xuspnllkllllmnoprsuwy{}~lur{xtojea\XSOKHECBBBCEHKOTY_djouz~}zxvvvttsrrrssttuvx{|{}|{z{|}qivqd_`cfjlpoopqqoonljhfca]YWUROMKIGFDCCCCDEGILORVZ^bgkptx}yz{yxyzz{}}~~}|yxwutsrqpoooooppqsuwy{}ypg`^ajwvngc`^][[Z[[[[[[[[[[ZZZZZZZZ[[\]^_abdfhjmorux{~zqib_`fp}vnhc_^\[ZYYYYYXXXXWWWWWVVVVVVWWXXZ[\^`acfhjmortwy||vog^VOJHLT_myzqh`YSOKJIGFFFFFFFFGGGGGHHHIIJJKLNOPRTVX[]`beiloruy|{tnlms}|xutsqqppppppppqqqqqqqqrrsttuwxy{}{uqpsz~ytrpnlkjiihhhgggggffffffggghijklnoqsuwy{~woha\YZ_gr~}umgb^\YWUSRRQQPPPOOOOOOOOOOPPQRRTUVXY[]_acehjmortvy{}|wrkd]VPMLOV`kw{tle`[XVSQPONNNNMNNNNNOOPPQQRSTUVXY[\_acehjmoruxz}ysmiginv~ywtrpnmmlllllllllmmmmnnopqrstvwy{}xspprx{yvspomlkkjiiiihhhhhhhiijjklmnpqrtvxz|~~xqkd_[YZ^enxzsnhdb^[YWVTTSRRQQQQQQQQQRRSSTUVWXZ[]_`bdgikmoqtvxz|~~ytoic]WSPPSX`ju|vpjec`]ZXWVUTTTSSSTTTUUVVWXYZ[\]_abdfhjloqsvx{}ytnjfegkqz}{wtrponmllkkkkkkkllmmnopqrstvwyz|~ytpnnpu|~zwtrpomllkjjjjjiijjjkkllmnoprstvwy{}~ysnhb^[Z[^dkt~{upmieb`^\ZYXWWVVVUUVVVVWWXYYZ[\^_abdegikmoqsuwxz|~~zvqlga\XTSSUZ`ir{}xrnlheb`^\[ZZYYXXXYYYZZ[\\]^_abcefhjlnprtvxz|~~ytokgedfiov|yvtrqoonmmmmmmmmmnnopqrstuvxy{}~|wspmlkklnsz}ytpkgda_]\[ZYYYYYYZ[\]^_abdfhklnpsvx{|~}xsomllnsyyrpuoiklmmllkjiiiijiiijjklllmnnopqqrsttuvwxyz{|~}yuplhecbceiov}|x|}vrrstuuvvwwwxxyyzz{|}}~zvspnmnpty{vtvwvqlhfeeeeeeeefffffggghhiijjklmnopqrstuvwyz{}~{xtqnljjjlpu{}zwusrqqqqqrrrsttuvwwxyz{|}}{wtpmkjijmpu{{ywvvvvutrpnljhhgggggghiijkklmnoqrstuwxy{|}{xuromkjklosx~|zyxxxxwusqomkjiihhhhiiijkklmmnopqrstuwxyz{|}~~{xurolifeccdfimrx}|xvsqpppponmkigedbbaaaaabbccdefghijklmnoqrstvwxz{|}}zxusqonnnpsv{~|{ywvuttttttuuuvwxxyz{|}~|zxvvuvx{~}{ywvutsssssssttuuvvwxyyz{|}~}zxusqonnnoqtx|}|{{{zzywvtrpomlkkkjjjjkkkllmmnoopqrsstuvwxyzz{|}~~}{ywuspnljhggghilorw{}{yxwwwwwvutrqomlkjiiiiiijjjkklmmnopqrstuvwwxyz{|}~}{yxvtsqqqqrtwz~~|{yxxwwwwwwwwxxyyz{{|}~~~|{yxxxyz|~}{zyyxxxwwxxxxyyyzz{||}}~}{yxvtsqqpqrsux{~~~~}|{zxwutrqppooonnnnoooppqqrrsstuuvwwxyyz{{|}}~~~}|{zxvusqpnmlkkklmoqtwz~~}||||||{zyxwutsrqppooooooopppqqrsstuuvwxxyz{{|}~~~|{yxvutssstuvxz}~}|{{zzyyyyzzzz{{||}}~~|{zzyyz{}~}||{{{zzzz{{{{|||}}~~~|{yxwuttssstuwy{}}|{yxwvuttssrrrrrrssssttuuuvvwwxxyyzz{{||}}~~~~}}|{zyxvutsqpoonnnopqsuwz|~}|{zxwvuuttsssssssstttuuvvwwxxyyz{{||}}~~}|{zyxwvvuuuuvwy{}~~}}|||||||||}}}~~~}|{{zzz{|}~~~}}}||||||}}}}}~~~~}|{yxwwvuuuuvwxy{}~}{zyyxwwvvvvuuuuvvvvvwwwxxxyyyzzz{{|||}}}}~~~~~~~~~~~~}}||{zyyxwvutsrrqqqqqrstvxy{}~}|{zyyxwwwvvvvvvvvwwwxxxyyzzz{{||}}}~~~}||{zyxxwwwvwwwxy{|~~~}||{{{zzzzzzzz{{{{|||}}}~~~~}||{zyxxwwvvvwwxy{|~~}}||{{{{{z{{{{{{{||||}}}~~~~}}|{zzyxxwwwwwwxyz{}~~~}}||{{{{{{{{{{{||||}}}}~~~~~}|{{zyyxxwwwwxxyz{|}~~}}||||{{{{{||||||}}}}~~~~~~}||{zzyxxxxwwxxyyz{}~~~}}}|||||||||||||}}}}~~~~~~~}}|{{zyyxxxxxxxxyz{|}~~~}}}|||||||||||}}}}}~~~~~~~}}||{zzyyxxxxxxxyzz{|}~~~}}}}||||||}}}}}}}~~~~~~~}}||{{zzyyyxxxxxyyz{|}~~~~}}}}}}}}}}}}}}}~~~~~~~~}}}|{{zzzyyyxxxyyyzz{|}~~~~}}}}}}}}}}}}}~~~~~~~~~~}}||{{zzyyyyxxyyyzz{||}~~~~~}}}}}}}}}}}~~~~~~~~~~~}}|||{{zzyyyyyyyyyzz{|}~~~~~~~}}}}}}}~~~~~~~~~~~~}}}||{{{zzzyyyyyyyzz{{|}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzzyyyyyyyzz{{||}~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{zzzyyyyyyzzz{{|}}~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{zzzzyyyyzzz{{||}}~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{zzzzzzzzzzz{{||}}~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{zzzzzzzzzzz{{{||}~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{{zzzzzzzzzzz{{||}}~~~~~~~~~~~~~~~~~~~~}}}}}|||{{{{zzzzzzzzzz{{{||}}}~~~~~~~~~~~~}}}}||||{{{{zzzzzzzzz{{{|||}}~~~~~~~~~~~~}}}}||||{{{{zzzzzzzzz{{{{|||}}~~~~~~~~~~~~}}}}}||||{{{{{zzzzzzzz{{{{||}}}~~~~~~~~~~~~}}}}}|||||{{{{{zzzzzzz{{{{|||}}}~~~~~~~~~~~~}}}}}}||||{{{{{{zzzzz{{{{{|||}}}~~~~~~~~~~~~~}}}}}|||||{{{{{{zz{{{{{{{{|||}}}~~~~~~~~~~~~}}}}}}|||||{{{{{{{{{{{{{{{||||}}}~~~~~~~~~~~~~}}}}}||||||{{{{{{{{{{{{{{||||}}}~~~~~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{||||}}}~~~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{||||}}}~~~~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{|||||}}}~~~~~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{|||||}}}}~~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{|||||}}}}~~~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{|||||||}}}}~~~~~~~~~~~~~}}}}}}}||||||||{{{{{{|||||||}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||||||||||||}}}}}~~~~~~~~~~~~~}}}}}}}}|||||||||||||||||||}}}}}~~~~~~~~~~~~~}}}}}}}|||||||||||||||||||}}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||||||||||}}}}}~~~~~~~~~~~~~~~}}}}}}}}|||||||||||||||||}}}}}}~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m36.bin b/applications/SprPoP/assets/packed/MUS/m36.bin new file mode 100644 index 0000000..66f4253 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m36.bin @@ -0,0 +1 @@ +zvqnlhda_[YYVTTSRRRRRSUVXXZ]_beihkoquy|{}~{vusrppmjjihhhgeeeeeffdddedddbbba``_]\\ZZYXWWVUUTTTTTTUVVWXYZZ[]_`bcegjmpsvz}~yw~~}{zwutsqpomkjiihgedddcccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccddeefghijklmopqstuwxxyyyxwutssuy}~~~~~~}}}|}}||||||}}}}}}}~~}{yxwxy|}{yxwvvuuuuutttttttsssssrrrrrrqqqqqqqqqqqrrrrssstttuuuvvvwwwxxxxxxxyxyxxxxxwwwvvvvuuuuttttsssrrrqqpoonlkigec`^]\\]_adgikkkihfca_]\[ZYXXWWWWWWWWWWWWWWWWWWWWWWWXXXXXYYZZ[[\\]^^_`abccdefghiijklmmnoopqrrssttuuvvwwwxxyyzz{{||}}}~~~~~}||{yxwuutuvy{}|zyxxy{}}{zxwutssrqqpppooonnnnmmmlllllllkkkkkkkkkkkllllmmmmnnnooopppqqqqqqqrrrrrrrrqqqqqqqppqqpppppooonnmmlkjihfeca_^]\]]_adfiklmmmlkigfdca``_^^^]]]]^^^^^^^^^^^_____```aaabbccddeffghhijkklmnnopqrrsttuvvwwxyyyzz{{{|||}}}~~~~~}||zyxwvuuuvxz}}{zyxxyz{}}|zywvutssrrqqppoooonnnmmmmmmmmlllllmmmmmmmmnnnnoooppppqqqrrrrrssssssssssssssssssssssssrrrrqqpponnmkjigfecbaaabcdfikmoqrrrqponmkjihgfeeddcccccccccddddddeeeeefffgghhhiijjkllmmnoopqqrsstuuvvwxxyyzz{{|||}}}~~~~~}|{zyxwvuuuvwy{~~|{zyxxyy{|~}|{yxwvvuttssrrrqqqppppppoooooooooopppppppqqqqrrrrssssttttuuuuuvvvvvvvvvvvvvvvvvvuuvxy{}~|zxvutrqpoonnnnmmmmnnnnooppqrrstuuvwxyz{|}~}~}|{zyxwvuttsrqqpooonnmmmmmmmmnnnooppqqrssttuuvwwwxxyyyzzz{{{|||}}}~~~}|}~}~~}|{yxwvvutssrqqppooonnnnmmmmnnnnoooppqqqrrsttuuvwwxxyyzz{{||}}~~~}||||}~|{zzzz{|}~~}}||{{{zzzzzzzyyzzzzzzzz{{{|||}}}~~~}|zyxwvvwwxz{|}~~}||{zzyyxxwwwvvvvvvvvvvvvvvvvvvwwwwxxxyyyzzz{{{{|||}}}}}~~~~~~~~~~~~~~~~~}}}|||{{zyxwvutrqponmmmnopqrsuvvwxyyzzzzzzyyxwwvuutssrrqqqpppppppppppppppqqqqrrsssttuuvvwwxxyzz{{||}}~~~}|{zzyyzz{|~~}{zyxwwvvvwxyz{|}~~}}|{{zyyxxwwwvvvuuuuuuuuuuuuuuuuuuvvvvwwwwxxxxyyyzzzz{{{{{||||||||||}}}}||||||||{{{{zzyyxwvutsrqqpooooppqrtuvwxyzz{||||||||{{zzyyxxwwvvuuutttttttttttttttuuuuvvvwwwxxyyyzz{{{||}}}~~~~}|{zzyyyyzz{}~~}|{zyxxwwwwwxyy{|}}~~~}||{{zzyyyxxxwwwwwwwvvvvvvwwwwwwwwxxxxxyyyyyzzzzz{{{{{|||||||}}}}}}}}}}}||||||{{{zzyyxxwvuutsrrqqqqrrstuvwyyz{||}}}~~~~}}}}||{{{zzyyyxxxxwwwwwwwwwwwwwwwwxxxxyyyyzzz{{{|||}}}~~~~~~}|{{zzyyyyzz{|}~~~}||{zyyxxxwxxxyz{|}~~~~}}||{{{zzzzyyyyyyxxxxxxxxxxyyyyyyyyzzzzzz{{{{{{||||||||}}}}}}}}}}}}}}}|||||{{{zzyyxxwwvuuttssssssttuvwxyz{||}~~~~~~~}}|||{{{zzzzyyyyyyyyyyyyyyyyyyyzzzzz{{{{||||}}}}~~~~~~}}||{{zzzyyzzz{|}~~~~~~~~}}||{zzzyyyyyyzz{|}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}||{{zzyyyyyyzz{|}~~~~~~~~~~~~~~~~~~~~~~~~~}}||{{zzzyyyyyzz{||}~~~~~~~~~~~~~~~~~~~~~~~}}||{{zzzyyyyyzz{{|}~~~~~~~~~~~~~~~~~~~~~}}||{{{zzzyyyzzz{{|}~~~~~~~~~~~~~~~~~~~~}}||{{{zzzyyyzzz{{|}}~~~~~~~~~~~~~~~}}|||{{zzzzzzzzz{{||}~~~~~~}}|||{{{zzzzzzzzz{||}~~~~~}}}||{{{zzzzzzzzz{{|}}~~~~~}}}||{{{zzzzzzzzz{{||}~~~~~}}}|||{{{zzzzzzzz{{||}~~~~~~}}}|||{{{zzzzzzzz{{||}}~~~~~}}}|||{{{{zzzzzzz{{||}}~~~~~~}}}|||{{{{zzzzzz{{{|||}~~~~~~}}}}||||{{{{zzzzz{{{{||}}~~~~~}}}}||||{{{{zzzzz{{{{||}}~~~~~~~}}}||||{{{{{{zzz{{{{||}}~~~~~~~}}}}||||{{{{{{{{{{{{|||}}~~~~~~~}}}}|||||{{{{{{{{{{{|||}}~~~~~~~~}}}}}||||{{{{{{{{{{{|||}}~~~~~~~~}}}}}|||||{{{{{{{{{{|||}}~~~~~~~~}}}}}||||||{{{{{{{{||||}}}~~~~~~~~}}}}}}||||||{{{{{{{||||}}}~~~~~~~~~}}}}}}|||||||{{{{{|||||}}}~~~~~~~~~~~}}}}}}||||||||||||||||}}}~~~~~~~~~~~}}}}}}||||||||||||||||}}}}~~~~~~~~~}}}}}}}|||||||||||||||}}}}~~~~~~~~~~~}}}}}}}||||||||||||||}}}}~~~~~~~~~~~}}}}}}}}|||||||||||||}}}}~~~~~~~~~~~~}}}}}}}}||||||||||||}}}}~~~~~~~~~~~~~}}}}}}}}|||||||||||}}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||}}}}}}~~~~~~~~~~~~~}}}}}}}}}}|||||||}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}||||}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~}zvsqprwy|}}}~~~~}}|{zyxwwwvtssrrsrpoonpqpnnopqstrsuvxy{y{}~{~u{|zyxwvusppnmljgeca`^\YVTRQOMJHGFFEEDDEFGIJLNPSVX[^`cfhkmortuwxyz{||||}~xrqu~zrlgdbbabbba``___^][[ZYXWUSRQPONLKJIIHHGFFGGGHIIJLMOPSTVX[]_acfhjmoqsuvxz|}~{z{}}|{{{zzzzzzzzzzzyyyyyxxxxxxwwwwwwwxxxyyzz{||}~~{xtqnljjlosx}~{xtromkjihhggfffeeedddccbbaa``___^^^^]]]]]]]]^^^__``aaabbccddeeffgggggggggggffffeedddccccbbbbaaaa``__^]\[YWTQNKIFEEFIMRW\_aba_]ZWURPONMLLLLMMMMNOOOPPPQQQRRSSSTUUVVWXYZ[\]^_`bcdfhiklnoqrtuwxz{}~~|zxvsoligedcdgjmruy{||zxvspmkhfdcb`__^]]\\\[[ZZZYYYXXXWWWWWWWWWWWWWXXXYYZZ[[\]]^^_``abbccddeefffffgggggggggggffffgfffffeeeddcb`_][YWURPONNOQUY]bfikmmljhfdb`^]\[ZZYYYYYYZZZ[[[[\\\]]^^__``aabccdefghijklnopqstuwxyz{}~}|zxvtqoljgfedefikosvy{|}|{ywtromkigfdcbaa`__^^^]]]\\\\[[[[[[[[[[[\\\\]]]^^__``aabccddeffgghhiijjjkkkkllllllllllllllllllkkkjiihgedba_][YWVUUVXZ]aeimprstsrqomljhgedcbbaaaaaaaaaaabbccccddeeffgghhijkklmnopqrstuvwxyz{|}~~}||}~~}{zxvtrpmkihggghjmpsvy{}}|zxvtrpnmkjihgfeeddccbbbbaaaaaaaaaaaaaabbbbcccddeeffgghhiijkklllmmnnooooppppppppppqpppppppooonmlkjihfecb`^][[ZZ[\^`cgjnqtvwxxxwvusqpnmlkjihhgggffffffggggghhhiiijjkklmmnoopqrrstuvwxyzz{|}~~|{zzz{|~}}||}~~}||{{{zzzzzzzz{{{{||}}~~~|yvtqnlkiggfghjlorux{}~~~}|{zyxxxwxwvwwwxxyxyyzz{|{||}~~~|zywussrssuvwz|~~}|{zzyxwuttsrponmlkjihhgggffffffgghiijklmnpqrtuvxy{|~~~~}}|{{zzyxwvutsqpomlkihhgffghjlnqtw{~~|{yxvutsrqqqppppppqqqrrrssttuuvvwxy{}~{|}{{|}~~~~}}|{{zyyxwvutsqponmlkkjjjkklnprtwz~~}|{zyxwvvvvvwxz{}~~}}|||{{{{{{{{{{{{{zzyxwvutstuvx{}~~}|zzyyxxxxxxxxxxxxxxxxxxxwwwvvuuttssrqpoonmlkjiihhhijklnpruwz|~}|{{zzyyyxxxxxxxxxxxxxxxxxxwwvuttssstvxz}~~}}||{{{{||||||}}}}}}}}}}||||{{zzyyxwwvuttsrrqqqqqrstvwy|~~~~~~~~~}}||{zyxxxyz|~~~~}}|{{zyyxwwvvvuvvwwyz|~~}||{zzyyxxxyyz{}~~~}|{zyyxxxyz|}~}}|||{{{{{{{{{zzzzzyyyxxxwvvuutssrqqpoonnnmnnnoprsuwy{}~~}}|||{{{{{{zzzzzyyxxwvvuttssstuvxz|}~~~}|{{zzzyyyyyyyyyyyyyyxxxxxwwwvvuuttssrrqqppooooppqrsuwy{}~~}||{zzyyyz{|~~~~}}||{{zyyxxxwwwxxxyz{}~~~}}||{zzyyxxxxxxxyzz|}~~}}|{zzyyyz{|}~~}}|||{{{{{zzzzzyyyxxxwwvvuuttssrrqqqppppqqrstuvxy{|~~~~}}}}|||{{zzyyxwwvuutttuuvxy{|~~~}||{{zzyyyyxxxxxxxxwwwwwvvvvuutttssrrrqqqqqqqqrrstuwxz{}~}}||{zzzzz{|}~~~~~~~}}}}|||{{zzzyyyxxxxxxxyyz{|}~~~}}}||{{zzzyyyxxxxxyyzz{|~~}|{{zzzzz{|}~~~}}||{{{{zzzzyyyxxxwwwvvuuuttsssrrrrrrrsstuvwxy{|}~~}}}||{zzyxxwwvvvvvvwxz{|}~~~}||{{zzzyyyyxxxxxxwwwwvvvuuutttssssrrrrrrssttuvwxz{|~~}}||{{zzzz{|}~~~~~~}}}}}||||{{{zzzzyyyyxxxxxyyzz{|}~~~}}}}}}~~~~}}}||||{{{{zzzzz{{{|}~~~~~~}}}||||{{{zzzzyyyxxxwwwwvvvvvvvwwwxyyz{|}~~}||{{zzzz{{|}~~~}}}||||{{{{zzzzyyyxxxxwwwwvvvvvvwwwxyyz{|}~~~}}|{{{zzz{{|}~~~~}}}|||{{{{zzzyyyyxxxxwwwwwwwwwwwxxyyz{|}~~}}||{{{z{{{|}~~~~}}||||{{{{zzzyyyyxxxxwwwwwwwwwwxxyyz{{|}~~~}||{{{{{{{||}~~~}}}|||{{{{zzzzyyyxxxxxwwwwwwwwwxxyyz{{|}~~~}|||{{{{{{||}~~~}}}|||{{{{zzzzyyyyxxxxxwwwwwwxxxyyzz{|}~~~~}}||{{{{{{{|}~~~~~}}}|||{{{{zzzyyyyxxxxxxwwwxxxxxyyzz{||}~~~}}||{{{{{{{||}~~~~}}||||{{{zzzzyyyyxxxxxxxxxxxxxyyzz{||}~~}}|||{{{{{{||}~~~}}}|||{{{{zzzzyyyyxxxxxxxxxxxyyyzz{{|}~~~~}}||{{{{{{||}}~~~}}}|||{{{{zzzzyyyyyxxxxxxxxxyyyzz{{|}}~~~}}|||{{{{{{|}}~~~}}}|||{{{{zzzzzyyyyyyxxxxyyyyyzz{{||}~~~~}}|||{{{{{{||}~~~~}}}|||{{{{zzzzzyyyyyyyyyyyyyzzz{{||}~~~~}}|||{{{{{||}}~~~~}}}|||{{{{zzzzzyyyyyyyyyyyyzzz{{||}}~~~~}}|||{{{{{||}}~~~~}}}|||{{{{zzzzzzyyyyyyyyyzzzz{{||}}~~~~}}|||{{{{{|||}~~~~~}}}|||{{{{zzzzzzyyyyyyyyzzzz{{||}}~~~~}}||||{{{{|||}}~~~~}}}|||{{{{{zzzzzzzzzzzzzzzz{{|||}}~~~}}}|||{{{{|||}}~~~~}}}}|||{{{{{zzzzzzzzzzzzzz{{{|||}}~~~~}}}||||{{{||||}~~~~}}}}||||{{{{{zzzzzzzzzzz{{{{|||}}~~~~~}}||||{{{{|||}}~~~~}}}||||{{{{{{zzzzzzzzz{{{{|||}}~~~~~}}}||||{{||||}}~~~~~}}}||||{{{{{{{zzzzzz{{{{{|||}}}~~~~}}}|||||{||||}}~~~~~}}}|||||{{{{{{{{{{{{{{{{|||}}}~~~~}}}|||||||||||}}~~~~~}}}|||||{{{{{{{{{{{{{{{||||}}~~~~~}}}|||||||||||}}~~~~~}}}}|||||{{{{{{{{{{{{||||}}}~~~~~~}}}||||||||||}}}~~~~~}}}}||||||{{{{{{{{{|||||}}}}~~~~}}}}|||||||||}}}~~~~~~}}}}|||||||{{{{{|||||||}}}}~~~~~~}}}}||||||||}}}~~~~~~}}}}}|||||||||||||||||}}}}~~~~~~}}}}}||||||}}}}~~~~~~}}}}}}|||||||||||||||}}}}~~~~~~}}}}}|||||}}}}~~~~~~~}}}}}}|||||||||||||}}}}}~~~~~~~~}}}}}}||}}}}}~~~~~~~}}}}}}}||||||||||}}}}}}~~~~~~~}}}}}}}}}}}}}~~~~~~~}}}}}}}}|||||||}}}}}}}~~~~~~~~}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}yvsqnmkhfdcaaaaeiotwz|~}{{zyxxtrsrrqronoopqrqprstuxz{}}zwtqnjfeca`_\ZZYYXXUSSRQPPNLMNRV^emodIRlKHX_fhllllmmnoooppqqrssstttttsrqqponmlkjjihhhhijkmpruw{~Ƽñ}zwvtsqponnnmnnnnopqrttsqmg_UMGGN[gnld\SLGEEEFHJLNQQRSUVWYZ[[\\]]]]^^__``abbcdeghjlnprtwy|{vrpprx|xurpnnmmmmmmmmmmmnnnnmmmmmmmmmmmmmmnnnoppqrstuvwxy{|}|xtojd_[YXZ^dkrx|~|yuoje`\XVSRPOONNNMMMMLLLLKKKJJJJIIIIIIIIIIIJJKLLMNOPQRSTUWXYZ\]^_`abcddeffgghhhiiiiiiijjjjkkkklllmmmmmmmlkifd`\XTPLJJKOU]dlquvvsplhd`][YXWVVVWWXXYZ[\]]^_``abcddefghijklmopqstvxz{}~|zyxwvvuutttssssrrrqqqqppppppppppqqqrssttuvwxyyz{|}~~~}{zywtrnkgc^ZVSQPQUY_ekqtvvurnjea]YVTQONMLKJJJIIIIIIIIIIIIIIIIIJJJKKKLMMNOPQRSTVWXYZ\]^`abcefghijkklmnnoppqqqrrsssstttuuuuvvvuuttsrpnlifb_[XUSSTW\bhouz~~|yurnkhfdba``___```aabbccddeffghhijkklmnopqstuwxy{}~~|{yxwwvuutttsssrrrrrrrqqqqqqrrrrsstttuvvwxxyz{{|}~~}|{ywuspmjfc_[XUTSTVY^cinswyzywtqnjfc_]ZXVUSSRRQPPPPOOOOOOOOOPPPQQQRRSSTTUVWXYZ[\]^_`acdefghjklmnoppqrsstuuvvwwxxxyyyyyzzzzzzyyxxwutrpnkheb_\ZYXY[_ciouz~{xvspnljihgfffeeffffggghhiijjkllmnoopqrstuwxyz|}~}||}~|{yxwwvuutttsssssrrrrrrrsssssttuuuvwwxxyzz{||}~~~}{zxvtromjgd`^[YXWXZ\`dinswz}~~}|ywtqnkheca_]\[ZYXXWWWVVVVVVVVWWWWXXYYYZ[[\]]^_`abcdefghijklmnopqrsstuuvvwxxyyyzzz{{{{{{{{{{zzyxwvtsqomjhec`^]\[\^`dhmrx}|zwusqonmlkkjjjjjjjjjkkkllmmnnopqqrstuvwxyz{|}~~}|}~~{ywvutuwy}}{zxwuttsrrqqqqppppqqqrrrrssttuuvvwxxyz{{|}~~{ywtromlkkkmorvz~|{ywvvuttssrrrqqqqqrrrrrsssttuuvvwxxyzz{|}~~}xsnjgda_]\\^__`bb`bb_^]\XWVTSRRPOOPQSUUTWY]_cddhkpsxzz~~~~}vsnjfa\VSOLIGD@?=<;::999::;<=>@ACDFHIKMNPRTVWXZ[]_`bcdfgikmnpsuwy{}ztpmkhfefghjlmpsuwxxwvvtrolhda]ZWSPMKHFEDBA@????>???@ABCDFGHJLNOQSUVXZ\^_acegikmnprtwxz|~}yvsqpppqruw{~~|{zyxwwvvvvuvvwwwxxyz{{|}~~}zxwvvxy{||{zwsplifdcbabcegjmpsuwz{||||{zxvtsqomkihfedcba```_______``aaabbccdddeefffggghhhiiiiijjjjjjjjjjjjiiiiiihhhgfedb`^\YWTRPNMMNOQRSSRQNKHEB@>>=>?@CEHKNQTVXZ[\\\\[ZYXWVUTSRQPPPQQQQQRRSTUVVXYZ[]^_abceghjkmnoqrtuvxyz{|~~{xvtrpoopqrsttsrpnkhfca`_^__`bcegikmnopqqqponmljigfecba__^]\\[[ZZZZZZZ[[[\\]]]^^__``aabbbccdddeeffffggggghhhhhhhhhhggffedcb`^\ZXVTSQQQRSTVXYYYXWUSQONLLLLMOQSUWZ\^`bdeffggfffeddcbaa``______``aabbcdeffghijkmnopqrstvwxyz{|}~~~}~~}zxvtrponmmnopqrrrrpomkigfeddddefghiklmnopqqqpponmlkjihgfeddcbbaa``````````abbbcccdddeeffggghhhiiijjjjkkkkkklllllkkkkjjiihgfdcb`^\[YXWVVWXY[\^_```_^]\ZYYXXYYZ[]^`bdeghjkkllmmlllkkjjiiihhhhhhhhiiijjkklmmnopqqrstuvwxyyz{|}}~~}|{zzz{|}~|{ywusqponnnnopqrstttsrqponmlkkjkkklmnopqrrsssssssrqqponnmlkkjiihhhggggggggggghhhiiijjjjkkkllllmmmmnnnnooooooooooonnnmmllkjihgedca`^]\[ZZ[\\^_abcdeeeddcbbaaaabbcdefhijklmnooppppppppooonnnnnmmmnnnnnooppqqrrsttuuvwxxyzz{||}~~~}|zyyxxxxyz|~~|{zxwvutsssstuvwyz{|}}}}}||{{zzzz{{||}~~~~~~~~~~~~~|{zxwvutssssstuwxyz{|}}}}}}||{{{{{{||}~~~}|{zywvutssssstuvwyz{|}}~~~}}}|||||||}}~~}|{yxwvutssssstuvwxy{|}}~~~~~}}}}||}}}~~~}|{yxwvutsssssstuvxyz{|}~~~~~~~~}}}}}~~~~}|zyxwvutsssssstuvwxz{|}~~~~~~~~~~~}}{zyxwvutsssrrsstuvxyz{|}~~~~~~~}|{zyxwvuttssrrsstuvwyz{|}~~}|{zyxwvuutssrrsstuvwxy{|}~~~}|{zyxwvuutssrrssttuvxyz{|}~~~}|{zyxwvvutsssrssstuvwxz{|}~~}||{zyxwvvutssssssstuvwxyz|}~~~}|{{zyxwvvuttssssssttuwxyz{|}~~~}|{{zyxwwvuttssssssttuvwxz{|}~~}}|{{zyxwwvuttssssssstuvwxyz|}~~~}}|{zzyxwwvuttssssssstuvwxyz{|}~~~}||{{zyxwwvuuttssssssttuvwxy{|}~~~~}||{zzyxxwvuuttssssssttuvwxyz{}~~~}}||{zzyxxwvvuttssssssttuvwxyz{|}~~~}}||{{zyxxwvvutttsssssttuvvwyz{|}~~~}}||{{zzyxxwvvuuttsssssttuuvwxyz{}~~~}}||{{zzyxxwvvuutttssssttuuvwxyz{|}~~~~}}||{{zzyxxwwvuutttsssssttuvwxyz{|}~~~~}}||{{zzyxxwwvuutttsssstttuvvwxyz{}~~~}}}||{{zzyxxwwvvuutttssstttuuvwxyz{|}~~~~}}}||{{zzyyxwwvvuutttttttttuuvwxyz{|}~~~~}}}||{{zzyyxxwwvvuuttttttttuuvwxyy{{|}~~~~}}}||{{{zzyyxxwwvvuuuttttuuuvvwxxyz{|}~~~~}}}||{{zzzyyxxwwvvvuuuuuuuuuvvwxxyz{|}~~~~~~}}}||{{{zzyyxxxwwvvvuuuuuuuvvvwwxyz{{|}~~~~~}}}|||{{zzyyyxxwwwvvvuuuuuuvvvwwxyzz{|}~~~~~}}}|||{{zzzyyxxxwwvvvvvuuuvvvvwwxyyz{|}}~~~~~}}}|||{{{zzyyyxxxwwwvvvvvvvvvwwwxyyz{{|}~~~~~~}}}|||{{{zzzyyxxxwwwwvvvvvvvvwwxxyyz{{|}}~~~~~}}}}|||{{{zzyyyxxxwwwwwvvvvvwwwxxyyzz{||}~~~~~}}}}|||{{{zzzyyyxxxwwwwwwwwwwwwxxyyzz{||}~~~~~~~}}}||||{{zzzyyyxxxxwwwwwwwwwwwxxyyzz{{|}}~~~~~~}}}||||{{{zzzyyyxxxxxwwwwwwwwxxxyyzz{{||}~~~~~~~}}}}|||{{{{zzzyyyxxxxxwwwwwwxxxxyyzz{{||}}~~~~~~}}}}||||{{{zzzzyyyxxxxxxxxxxxxxxyyzzz{||}}~~~~~~}}}}||||{{{{zzzyyyyxxxxxxxxxxxxyyyzzz{{|}}~~~~~~~}}}}||||{{{zzzzyyyyyxxxxxxxxxxyyyzzz{{||}}~~~~~~}}}}||||{{{{zzzzyyyyyxxxxxxxxyyyyzzz{{||}}~~~~~~~}}}}}||||{{{{zzzzyyyyyyyxxxyyyyyyzzz{{||}}~~~~~~~}}}}||||{{{{zzzzzyyyyyyyyyyyyyyzzzz{{|||}}~~~~~~~}}}}}||||{{{{zzzzzyyyyyyyyyyyyyzzzz{{|||}}~~~~~~~}}}}}||||{{{{{zzzzzyyyyyyyyyyyyzzzz{{|||}}~~~~~~~~~}}}}}||||{{{{{zzzzzzyyyyyyyyyzzzzz{{{||}}}~~~~~~~}}}}}|||||{{{{{zzzzzzzzyyyyzzzzzz{{{{||}}}~~~~~~~~}}}}}|||||{{{{{zzzzzzzzzzzzzzzzz{{{{|||}}~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzzzzzzz{{{{|||}}~~~~~~~~~}}}}}|||||{{{{{{zzzzzzzzzzzzzz{{{{{|||}}}~~~~{xurpoljhfedcbaa`````aabcddeghjkmlnoqsuvuwxz|}}~{wtqlgd`\ZXTPMKIGFDBA@@@???@@ABBCEGHKMLOQTVY[[^adgilloruxz}|}tpqkfgfbaba_``_`aaacdegikmpsvz}|yvsrqonlllllllkkkjjihggfghoxgsjj|zxvutsrrstvx{~z~vorsx~|zwurolhec`^[ZXVUTSSSSTVWY[^adhlosw{yz}zxyz|~|{zxxwvvuuuuvvxyz|~vkb]\akxxqib]YWUTSSSSTTSSSSSSSSSRRRQQQPPPPPQQQRSTUVXY[\^`bdgiknpruwy{}~zung_XPJGHLT`mx|uld]WRNKIHGGFGGGGHHHIIIIIJJJJJJKKLLMNOPQRTUWXZ\_acfhknpsvx{}zrjc^ZZ]dnzwpkfb`^\\[[[[\\\\]]^^^___``aabbcdefghjkmnpruwy{~|xvx~}zxwvvuuuuuuuuuuuuuuvvvvvwwxxyz{|}~xqkfdeioyztnjeb`]\[ZYXXXWWWWWWWWVVVVVVWWWXXYZZ[]^_`bdegikmoqsuwy{}}zuoic\VPLJJMS[eoy~wqjc^YUQOMKJIHHGGGGGGHHHHHIIIJJKKLMNOPQRSUWXZ\^`begilnpsuxz|~}xrlfa\ZZ\ais}}wrnjgdba`__^^^^___```aabbccdefghijkmnpqsuwy{}{vsrtx~|ywvtssrrqqqqqqqqqqqrrrssttuvwxyz{|~ztnifddgls{~xsnjgdb`_^]\[[ZZZZZZZZZZZZZ[[\\]^^_`acdegijlmoqstvxz|}|xsnic]XSOMMOSZbkt|{uoje`\YVTRPONNMMLLLLLLMMMNNNOPQRRSTUVXYZ\]_acefhkmoqsuwy{}~ytojea][[\_emu~{vroligfdcbbcbbbbbbccddeeffghijklnoprsuwxz|~{vrpoorw~~|ywvtsrqqqppppppppqqqrrsttuvwxyz|}~|vqlhecceiov}}yuqmjhfdcba`_^^^]]]]]^^^^__``abccdfghijlmoprsuwxz{}~ysokfa]XURQPRV[ahpx|wrmifb`][YXWVUTTSSSSSSTTTUUVVWXYZ[\]^`abdegiklnprtuwy{}~|xtpkgc`]\[]_djqy|xurpmljihgffeeeeefffgghiijklmnopqstvwyz|~}yurolkklptz}zxutrqponmmmmlmmmmnnnoppqrstuvwyz{}~|xtpmkjjlosy~zwtqomljihggffffffffgghhijjklmnoqrsuwxy{|~~zvrnifc`__`cglrz|yuspnmkjihgfffeeeeffffghiijklmnopqrsuvxyz|}}yurnjfca__`bejpv}|yvsqomljjjihhhggggghhhiijkklmnopqrstvwxz{}~}yvrnjgda`__adhmsz~{xusqpnmlkjiiihhhhhiiijjkllmnopqrstvwxy{|}}yvrokhdb`__`cfkqw~~{xvtrponmlkjjjiiiiijjjkklmmnopqrstuwxyz{}~}zvsnjgdb`__`cfjou{~{yvtrqonmllkkkjjjjkkkllmmnoopqrstuvxyz{|~~{xuqnkgdb`__`behmsy~{ywusrponmmlllkkkklllmmnnoppqrstuvwxz{|}~~{wtqnkheca`_`acgkpv|~|ywutrqponnmmlllllmmmnnoopqqrstuvwxyz|}~}zxtqnkhfca`__`ceinsy~|zxvtsrqpoonnmmmmmnnnooppqrrstuvwxyz{|~}zwuqnlifdb`__`bdhlqw}}zxwuspnljihfeddddefghjklnorsuusuvuuwunopmmnmgggffffa`bbegjjmsyzwz~|zvtrpnlieb_[WTPKFB>951,)&#"!"#&).39@GNU[bgmqux{|~ƻ{vqqx}uojfdba`__^]\[YXWUTROMJGDA>;852/,)'%$#$%'*-27=<<<<=>@CFJNRW\aglqvz~~|zxvtsqonlkkjjklmorux|~}|{zxvtromjhfeegimquy|~~}{xurolifdb`^][YXVUSRPNLKIGECB@?>=<<<=>?ACFIMPTX\`cfilnprstttttssrqponmlkjihhgffeeddddcccccddddddddddccba`_]\ZXVTSSSUW[_chloqrrqomjheca_^\[ZYXWVUTTSRQPONMLKJIIIIIJKLNPSWZ^bfjnsw{~~}|{{{{||}}|zxwutrqponnnnnoprtvx{~~~|{zxwusqnljhfeefhjmqtwz|}}|{xvspmkhfcb`^\[YWVTSQPNMKJHGFEDDCCDDEFHJLNQTWZ^adgjloqrtuvwwxxwwwvuutssrqqppoonnmmmlllllkklkkkkkkjjiihgedba_]\ZYYYZ\_bfjmqtuvwvutromkigecb`_^][ZYXWVVUTSRRQPPPPQQRSUWY[^aehkosvz}~}}|{zyyyyyyz{|~}|zywvusrqppooooopqrsuwy|~~}{zxvtsqomkihgghikmpsvy|}~~}|zxuspnkigeca_]\ZYWVTSRQPONMLLKKLLMNOQRTWY[^adfiknprtuwxyzz{{{{{{zzyyxxwwvvuuttsssrrrrqqqppppoonmlkjihgecb`_^]]]^_adgjmqtvxyzyywvtrpnljhgedba`_^]\[ZYXXWWVVVVVWXYZ[]_adfilorux{}~~~~~}|{zzyxxxwwxxxyz{}}{ywusqonmkjihhggffgghhjklnprtwy{~~}{yxvutssssuvx{~~|zxwvtrqpnmlkjihggfffffffghijlmoqsuxz|~}{zxwutsssstuwz|~|zywutrqonmlkjihhggfffggghiklmoqsuwy|~~}{zxwvtsssstuwy{~~|zxwusrqonmlkjjihhhggghhijklmoqstvy{}~|{yxwutssssstvxz}}{zxvusrqonmlkjjiihhhhhiijklmoprtvxz|~~|{zxwutssssstuwy{~~|{ywvtsrqonmmlkjjiiiiijjkklnoprtuwy{}~|{zxwvutsssstuvxz}~|zywvtsrqonnmlkkjjjjjjkklmnoprsuwyz|~~|{zxwvutssrsstvwy|~}|zxwvtsrqoonmllkkkjkkkllmnoprsuwxz|~~}{zywvutssrsstuvx{}}{zxwutsrqponmmlllllllmmnopqrsuvxz{}~}{zyxvutsssrstuvxz|~~}{yxwvusrqpoonnmmmmmmmnnopqrsuvxy{|~~}{zyxvuttssrsstuwy{}~}{zxwvutsrqpoonnnnnnnnoopqrstvwyz|}~}{zyxwvutssrsstuvxz|~~}{zxwvutsrqppoonnnnnooppqrstvwxz{}~~}{zyxwuutssrrsttvwy{}~|{zxwvutsrqqppooooooppqrrsuvwxy{|~~}|{zyxwvutssrrsstuwxz|~~|{zxwvutsrrqqpppppppqqrstuvwxyz|}~~}|{zyxwvutsssrsstuvxy{}}|{yxwvutssrrqqqppqqqrrstuuwxyz{}~}|{zyxwvutsssrssttvwyz|~~}|{yxwvuutssrrrqqqrrrsstuvwxyz{|~~|{zyxwvuttssrssstuvxz{}~}|zyxwvvuttssrrrrrsssttuvwxyz{|}~~}|{zyxwvuutsssrsstuvwyz|~~}|{zyxwvuuttsssssstttuuvwxyz{|}~~}}|zyxwvvutsssrssttuwxz|}~}|{zyxwwvuuttttttttuuvvwxyz{|}~~}}|{zyxwvuttsssssstuvxy{}~~}|{zyxxwvvuuuuttuuuuvwwxyzz{|}~~}|{zyxwvuttsssssstuvwyz|~~}|{zyyxwwvvvuuuuuvvvwwxyzz{|}~~}|{zyxxwvuttsssssstuuwxz{}~~}|{zzyxxwwvvvvvvvvwwxxyzz{|}~~}|{zyyxwvuttssssssstuvwyz|~~}|{zzyxxwwwwwvwwwwwxyyzz{|}}~~}|{{zyxwvuutssssssstuvwxz{}~}|{{zyyxxxwwwwwwxxxyyzz{|}}~~}|{{zyxwvuuttsssssstuvwxy{|~~}||{zzyyxxxxxxxxxyyzz{{|}}~~}|{{zyxwvvuttssssssttuvwyz|}~~}||{zzzyyyyyyyyyyzz{{||}~~}||{zyxwvvuttssssssstuvwxy{|~~}||{{zzzyyyyyyyzzz{{||}~~~}||{zyxwwvuutssssssstuuvxyz|}~}}|{{{zzzzzzzzzz{{||}}~~~}||{zyxxwvuuttssssssttuvwyz{}~~}}||{{{zzzzzzz{{{||}}~~~}|{zzyxwwvuuttssssssttuvwxy{|~~~}}||{{{{{{{{{{{||}}~~~~}|{{zyxwwvuuttssssssttuvvxyz{}~~~}}||||{{{{{{||||}}~~~~}|{{zyxxwvuuttsssssssttuvwxy{|~~~}}|||||||||||}}}~~~~}}|{{zyxxwvvutttssssssttuvwxyz|}~~}}}}|||||}}}}}~~~~}}|{{zyxxwvvuuttssssssttuvvwyz{|~~~~}}}}}}}}}}}~~~~}}|{{zyyxwvvuuttssssssstuuvwxy{|}~~~}}}}}}}}~~~~~}}|{{zyyxwwvuuttsssssssttuvwxyz{}~~~~~~~~~~~~~~~}}|{{zyyxwwvuutttssssssttuvvwxz{|}~~~~~~~~~~~~}}|{{zyyxwwvuutttssssssttuuvwxyz|}~~~~~~~~}||{{zyyxwwvvutttssssssttuuvwxyz{|~~}}||{zzyyxwwvvuuttsssssssttuvvwxy{|}~~~}}||{{zyyxwwvvuutttssssssttuuvwxyz{|~~~}}||{{zyyxwwvvuutttssssssttuuvwxyz{|}~~~}}||{zzyyxxwvvuutttsssssstttuvvwxyz|}~~~}}||{{zyyxxwvvuutttttsssstttuvvwxyz{|}~~}}||{{zzyyxxwvvvuutttttttttuuvvwxyz{|}~~~~}}||{{zyyxxwwvvuuuttttttttuuvvwxyyz{|~~~~}}||{{zzyyxxwwvvvuuuuutuuuuuvvwxyyz{|}~~~~}}||{{zzyyxxxwwvvvuuuuuuuuuvvvwxxyz{|}~~~~}}|||{{zzyyxxwwwvvvvuuuuuuvvvwwxyyz{|}}~~~}}}||{{zzzyyxxwwwvvvvvvuvvvvvwwxxyz{{|}~~~~}}|||{{zzyyyxxwwwvvvvvvvvvvwwwxxyzz{|}~~~~~}}}||{{{zzyyyxxwwwwvvvvvvvwwwwxxyzz{||}~~~~}}}|||{{zzzyyxxxwwwwwvvvvwwwwxxxyzz{||}~~~~}}}|||{{{zzyyyxxxwwwwwwwwwwwxxxyyzz{{|}~~~~~}}}||{{{zzzyyyxxxxwwwwwwwwwxxxyyzz{{|}}~~~~}}}|||{{{zzzyyyxxxxxwwwwwwxxxxyyyz{{||}~~~~~~}}}|||{{{zzzyyyyxxxxxxxxxxxxxyyzzz{||}}~~~~~}}}|||{{{zzzzyyyyxxxxxxxxxxxyyyzzz{{|}}~~~~~~}}}}|||{{{zzzyyyyyyxxxxxxxxyyyyzz{{{|}}~~~~~~}}}||||{{{zzzzyyyyyxxxxxxyyyyyzzz{{||}}~~~~~}}}}|||{{{{zzzzyyyyyyyyyyyyyyyzzz{{||}}~~~~~~}}}}|||{{{{zzzzyyyyyyyyyyyyyzzz{{{||}}~~~~~~}}}}|||{{{{zzzzzyyyyyyyyyyyyzzz{{{||}}}~~~~~~}}}}||||{{{{zzzzzzzyyyyyyyzzzzz{{{|||}}~~~~~~~}}}}||||{{{{{zzzzzzzyyyzzzzzzz{{{|||}}~~~~~~}}}}}||||{{{{zzzzzzzzzzzzzzzzz{{{|||}}}~~~~~~~}}}}}||||{{{{{zzzzzzzzzzzzzz{{{{|||}}}~~~~~~~}}}}}|||||{{{{{zzzzzzzzzzzzz{{{{|||}}}~~~~~~~~}}}}}||||{{{{{{{{zzzzzzzz{{{{{{|||}}}~~~~~~~~~}}}}||||||{{{{{{{zzzzzz{{{{{{{||||}}}~~~~~~~}}}}}|||||{{{{{{{{{{{{{{{{{{{{||||}}}~~~~~~~~~}}}}}||||||{{{{{{{{{{{{{{{{{|||||}}}~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{|||||}}}~~~~~~~~~}}}}}||||||||{{{{{{{{{{{{{||||||}}}}~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{|||||}~xpjegp|tjd``acfggiihijijknoqsuvwy{}~}{wsqokjjhhkpyt}zwoiccormlklmquy}z{{}xtoja_kb`^]]]^^^^^]]\[YYXX[ajnpsw{~{yqyywz{}~|{zzzz{{zvmpfYOJSestj_VSSVZ_dinsw{~qeadluzysle``Z]_[\SY\Z_ehjmortvy{|~|y|~upje`Y^\]_acfhknqsvqv}pedit~}wttw|{vpkfc^[SNWTX\cjr{}qeccipw{zupke`]ZXWVUVWY`ku|}ykiipy{snjjlrx}|wttxpfdiopiWCDJRX^cgknoomkhfddegimptr{|xzp_XRRW`n{}xvprsoonpsw{~x~yvuvz}plbcelqwxzywsqnmmmnnonljgfgmu|vv}qc]RQTZclu|yusvy~|qd\[`c]O:7-*-3=FPW\^_^\ZYXYZ\_chmsy~ve[RONS[fovxyyz|}|zvrmjhikmnnh^ha\]]aflrvyzyxxy|~vqszohicioy~znd_cotd_PLKOT[afjhkpnqsw{yx}~xgSMCEELT]flqssrpnmkklmpsvz}~sj\LD=<=CM[hrxywspmjhgffhkpu|~{{zy{~zysw{}ywx|~yx|~~~~yqh^XX_kol\PNEFJQZdnv|znfejnmcVWNKLNSW\_``^[VSWSSSUWZ^cglpvy}yvi^UWTW`m{}zyxy{soiijpx}qheqxxscc^Z^cjqw|~}zywwxy{}}zz{zphipz~zib]SUW^emt{}wqmlnrvuogbbghcQEB78:AIR[bgkmnnnnnoprux|{znhd`[XX[cnx~zqlkc`]]^aejoqskmngfegjoty}{}~yw{zl^TSX_^_LF?79;BIRZbglpsvz}~z|yf\WKKLRX`flprrpmjfb^]\^cir|yxuo^TLGDDHP\l|}{{|~zzwhicemx~{yvu{wwusomjmnzzqg_Z^hrvra_XRTV]dlsx{}uqqrmbK@;1/16=@EMU_hotxxwtplifddcdcdmloln{y~|y}|se`ZVTTV]gu|{|{xwy{~}{~xpncefklw}~}{zyyyzzzzywtronl}zqg`]agqtf\[QOPU[ckry~rjg^\[[\_elol^NK=98:@ELRX\_accdddefhknruy}vh`WPKGFJRaryusttwuuuqwnmkklptx{}}{ywvvvxyxupjfhnw||qsqlrv~{tmd\UQU^kstg_[MIFHKOTWcilruy|}wsqqrw}siiWTRTX^cinrtvwwwwwwwxxz{|~~~||u{unjmu~|onaXRMJGFFJQ\jy|zz{~yy~~}x{}ypkv}{yxvsniaXOIHMV\_TTSJLMSYaiqxukb\YZakx{sqb]XWVXZ]^_]aebcabbcdegiknpstvq{smldb_^^agq}zwstw}{ulcd`elpoeifcgjpv{~~|tjaZY^fllbe_WXX\`fkpux{}zrjb[WUW]fqvvhd_RPNQUZ`ejoruvxxyyzzz{||{|snov~~{uqmiea^\[]dlv}}wtrsux|}~v}}vustvy}{ust{{zxz{~{qfZNFAGSTSHKE??@EJQX_flrw|}tlgehozzuna^[]_cgknoppnligfegjou{~~woiimv}vqpb^YWVUVX[ajv~wpkkosuniplqx~{vsinuv}{xupjbZTQSZcgf\`YTTVY^bivxsh_[\bluvrhaXKHEFIMQUY]_abccdddefgiknpstvuu{|rnhfdbbbdhoy}z{~}|||{|{wtqponmkfb^]ainrkfldfiov|y~wof\TOPV[]TKNBABHPZenw}~vlc[UTZetxwka^[]_cfjkowuwxxxxxxyz{}~zxzqmd^VPNLLKMOT[eq||ywy{~}~~{uwrqw~~~wz|}vsnje^VMD=;>GNSNHLA@@CGMT\cjqw}~rh`\`ixvraUNJKOU]emsxzzxvspljgebdjgjijektqvwz|~}vpmov|~wmjfedeefhkpv{wttuwz{zwuvyvushgepsw|~xpyxz{tkbXQLU^bibacYXWY]ciou|zqeZNFBEP`oywno`XROOPSW[_behjkmnoprtvxz|~~}yzwkd]YVUTWZaju|zywtttwz}|ywutsrqnjebciry}w~|wz|{tmg`ZTOLOU_gkbc_ROJIIKWZ^ejoty}~rf\VXar~yrkhfgiknqtvxyzz{{zzzyyxwutrqkv|}zsjc_`gpwuptha\YWVVWXY[`fp{{vrsx|||~vxyqru|~yupke^VMD>=AKTZUZZRSRVY`fmsz}o`RHDIWm|shb]ZXXV^_^`abbcdeefgijkmnnnjvyx|}}wuw}~urnmlmmoprv{||{~{uqjhg^\\bioojqlhikpu{xqh_WQQU^dhbgd\[YZ\_behlptx~}ti]RHDGQbq}{~~plecbcehknqtvxyzzz{zzx||sjcadnx}ysoligedba`hos}{||yz{~}{zxwspmlov}~|x|vvuzvlaUJ=ACFJNSY^dglso|~~|tvrooqwpkiiihga\\UU^l{}yu}{xicdnqxxe\VUVZ^bgkmoonlfi`ejjpqwtrwponsvuvppqtx|zsmo}unmd]WW_mtsobfbbdfiknqux{}|xrq~{gWNUXY\[fq}}{zzxzuvsqvrposqrsuwy|{xpgclifffqptpllmqsxz~}solpu~uplffefgijmpsuvrmiq|||{~rmu}mbYQIC@BIXmpcc`VSNX_bfbaabddebhkjnikuv||wpg]UU_s|ywvuuurmd\Wflkh^_\_fglosx|~||{zzyyxwvvuspnpysqsicXW]\]XPMKJJIPUX\aaanpv{zolmrzqjssuqd`]Z`bdhkosx~yx}{z{~ysmknvzoruvwqnjhaba\[XWUOY]bjpxz~zx|xtpmigabky{tqtpqqqstvx|}|{zqrv~{qlv{yz||piqsvxx{nlkffefde]`fcfgjloqtwyw}ysop}}rwvy}znnurrh`agn}|trnlt|qkq{|vqppqqrstvy}ze[ZYSQMKWix~z{}xmgmomjfegmrw{~~{vqos{|~}|yvrupnlkmrz{vv}wf][ede^WVVY[^adgkotxz~|~vmi_]YXVWY^bh}yswzuzkecbcdfgggfda`ckwyz|||}}}}||~}nfkiidXVSUVY[^aeimps}vjebdfjosttkokda\\_gt{yz~xogaagmhusortwz}~||{wpiektuyrpruxzwonr|||wpjgedcbbbcaahjknihwvy}|z}wtlde{ztnnpxuy}qnjigheekjkif`^boqwtnqrwv}ykhjfg_\bjt|zuxvuuurjtxw{}zpkq{wztmlknsx~y{xg]VUX`l|}vnbYV_cgicfinrt}}~rnhfb_[YWXdkv}wx}{yiXW\djmqjhghlotx|}{~|{yxurmidceq~}xpgagloqeb``b`djjnqtwz~~{xutssromrppqqrqpiku}z||n^ZU]fjppkkknquy{}w|zyy{}|xvvsxumbYXcl{~xy{~~|xwrvwsutwx|qmklmoppy{}phfkpqtmgdbccffidgpnrtxz~zwtrpnlhd_]cvz|wtv|wqww~womlmllsuw{~ustt~pkkpzyromlllheomortwz~~}|}~yurrtwz{ztmfijkgWRPU^lxysgjksqhhghfmnnoprsvx|~|xka^afgmjkpw~{~upmlvz{urrsux{~}vy~xtrqpmkpkkgltyvrrrqb[UPUVVX[^bfkpvz}zpmox{~upjgdc`a\fqwurpw|vusuwz}|xq~|{{~~ysnljjhotszxosonqxwpjnpopnmkigggilpprvmkmv{{||ywtosx{}{{{{{{{{yvrkf^m{{}zxxy|~|wrr}}uomovy~|vqomlkkklmoqstuq{{y{zsmns~~|~xnbbhp|~ytldbfvz~{{|}|unkqv|}okhhijmjr~vsnc_QEEFR_o}}xqr|zuonpw{wsrrrrnx{z}ytrpqtzx{xwvvtpjc^cjnukeddegggntwz{{mb]ZXVUTSSUZakw}ytkb][`hotxronmpqsov}{|~|ytohida^p~{vphabjmuogfdffiqns{{maXTSTVZ^bfhjjjgd^Xbbfq{xxz|rmhhhkikxu}~wr~qs|~{yx|}vqkhflijhopquvzz}w~wsoonpprrsuvxz{|{w}{~wrsy~~ytronielkqoqsuwy|~|xq{yv}zvsnh`XU]gt||xw{xvursxvy{~~}{vrq{|xhb]]^aeinrw{y}zyw~zogihpogils{{yq|zsnm~ypz|{tswzvjcecdjpy~ztjkmp{zphcaacegjlpswz~yph`\Z_ejuxzyry}yyxyyyw~x}~wsxy~runnmmmmjiomnpsx~tpokoz|pkghhjkmoqrtuwwxvr|{||{~tlfb_`eou|~vnf[VTN[^iv~~~{{yqloqy}zvsuvy{}~~vnjhggghijknprstp{~{~y|vrqu{~xsvojsqopoooooquyysyuzztoqsy|zyz{}|wsf_\am}|wspnmllmnpqtuwsy~~x{|}yspmklpy~~uodhiehilptx}|tj`YYbswy~ysmhglx~{tqw}}rieeiov~}x{zuqmjkov{{{tx}}wrtvzuoiilhknt|zrjcmnwynmijkmortvw~uqt}ypieb`^]\[Z[^bhotu}~|xsollpruwrmid`kjmptx|xooms}oleiootvxxwurnkfd^_lqyprw||{xwutqu||~smmx~yqpnooqrstuvvwtshuytyz~}wqlgefktzzurnkeopptuxz|~{wy}|}}~~}wouompux}}|~zwvvwwxxywvpoz{wtsssstuuuvvtrqw|z|||y~|}{zxwuuywpidcnrv{xtrkryvz{~~~zuniekixypuptv{~~xshljfkr}woa\RHHPZnvx}umks}yxvutsrrqqnrtp~}xwxxyxxv~y{{{|||}}}|zwqlimos|usstvxz|~}zytjd`efpqe`[ZY[_dlt~{kw}{y}wrnkjiklnprtvtyw~~xqmgaWWSQV^esx{||utzvutuuuuuutuvvwwxvump}~ytme\Yaqrmlddccb`[Y^[^dmxxib`^_^ekmrw}zzsjknu{tpjfcceipy~zsrqrrpnkgfipxy|}zuz}ymmjkhgmjjkmrxvkovqfeehjmorqw|se^[ZYXWVVUWZ_fowzlcdgkovqjfb``adhlpsox~znhw~xrqpolhb\VQPT_qxvsuw{~zrs}ymjiiggnkkkjs|ypuwg_][\]_acehlpuz|na\Y]bfonifdehpv{ywwt{|z{yxuuw|xow|uhfkiiiihhijns{|xon|yujgfgghiijjjjjstu~rf\_pric^XTVOMLMQXcqrkdcdkr|~~}|}||{zywvuuwxwpr{vjhcfjqssvx{~timtypl`\ZZ[[\]^`cgmt~sp{xtomkkklmoqsvwzyng`XUWSXZbjilpu{w~wvtuttssrqqponmmmqrz|zwupi`[`ptpogfeeadd___afmwphdbaijmqu{}srvy|uxxrpkhedcdgkrz|sqnmifddgmr}{z{zrvzy|}wqvxnljgknjkjkmpt{ttuzvkkjmnopx{|{u}~tna]ZZYYXXWWX[^div{{wolotopic`]\[\]`cgkmjuz}{pkls||uonlje`YSOOU_k|zxz{}|toq~xtjhfekmjjjrrv{}wqd`^_`acdfgiloruy|~|v{w}voc]acjpkhc`^^`dix{yuw|xwtrooqw~yvst}uslhgghhhgghjmqx~wrt{xmjhiiiiggdfkiinwv|zzztokhdn{~wrf_\W[YTROOPU\fq|okior{}zyxwxwwuspmjikq{wx~xjokmtrtuwxz|~~}|~vqkgrspma^]]^_`abcefq{~y{}uqnnnnopqrsttz~~qd\TYY[^ghddcfjpw||zxuussqpnljgedfkqw|}}|zxvuroib]`nsutjhfmjhfdbbcgluxqjlpnqsw{~xnhs~~vqmjihgggikpv}upjgdegmr}|zvz{yzyrx{z}}{zx|qljpnmmmmmnoqux~yyxvtru}|uouyuthb_]\\\\[\\_`hsx|tx}rsoheb`^^_`cfjnqtq{~yxojp}~{sojf_YTQTWaot|~vony|~|pkiolkptrtvzx{~{{mheddeghjlorux{~~vmotqtmiqrnkfc`^]^_frwy{{vtpnmpu{xqpz|qnkkjjiihijmqv{~|ut}rnjihgfcijhjisvy|~zvtuuvxojqvtsg_\`[YVTRRSW^hs~vuv}{yyz|{~zxvusqnifbbdpyxtostruuwwy{|~xw{w{~{{nhfosrqea__`aaaabknrzyrnllmnnooppr{|~}y|zufXX]\cjfc_\[\`eltz}{{wsromjgc_\[\bir~w{|wz}|{zxwusplg_\`nrwsmpjkhgdb`_`amxxqrpnoprtxy{zpnoulfdcbbbbcehlryvjdabgktzvtnlqooqqqluww{|xux{lnnkkihfecaacip}{vwvquywz{~ulirvtre_\[ZYYXWWWW_kszzrr}|pnfa^\ZYXXYZ]`dhlnly{~}}|rkhp{{~tic]YVVXYbecfhov~~~zqklwzzwhjifnnnoprtw|vqw{yylgdcdefhjlnpqst}|thciuw~tnhd`^[YXXXahlu}||spljjnsyx}spw~~rokkjihhggghjmotv{y{us}roihdejhfgejpptwz}{~~yvuwyypfagmnqcea][XVTSSUY`hr}|yvvvxz~y|zvqnifa_]`duyv{~vwvwwxyz{|}}}|ywor{{|~vohemquwkhddccaekjmpu{xsqqqqrrsrx~}{rqsorfgfjojhc`]\\^bhov|ztqlid`\[[`eq{||{swxz{}{{yywvusqlfaalqwuqoljgeb`blovzyvwwxx~~|~z|unifeeeffgilqv}xnkknyzxvrnqsqssuuupz{{~|vu|}}ytonljheb`^`eox~|woiorvymhdba`_^]Z\lmqw~|}wlhda`_^^^`behlorsq}}~~ytlhjrt{rhb^^_bjhecbchnv~|y~~}|tnkrswyspronopqqsuy}ytx{~wrnmmmnopqrsu~~}z|umgfl{|rkeb`^\[Z]eekqxwrooru}y|tu|}urmljjihgeedegkpw~~yw~vqilmjigfemnnqswx{wy}~|ywvvx{}xpg_]bhiqmfb`^\ZXXXZ]bht}ywuvvxz}|wx}smgd`__bcq|y|}~~{xwwwwwwwvvtsqpopmv}{xy~zwrkfelotvjgcbahihjkmqtz~yusssssrrzzz|}}ynggkkwwvqkhc`][Z[\_chouz}|zslhc_\[\_corqqpnmenxtwwxwwvusrpnkga]art}~trnmjhdb^bfejot{}zxvwxrtuuznhdcbbcdefhkosy~yx{zzuw}zwsokjomlmnopqpkvwwz{~~~}vrqxvroigca^[Z[_fo}}}ru}~yqkhnquxlgca_^\ZZghhmqx~xngb`^]\\]^_aegkmpnp|~{xsmfbdjktlfedkojgb^\[^biqy}xsrwtw|{sminnzxsonnnnopqsvy~~wrvyytpnnnoooouzy||~x||yvspnty|~wngb`]\ZX_bbfkqx~{vrtwy|wu{}zvpnljhec`^^_dluzv{}yzsomjigiomopsuxy{vw~xxvwxz~ysme^Y^bp{olfda_]\[Z[]irx~zxwwxy|~xv{|}pia_^_cmvutuvy|}~|zzyxwvtrpooqty~}{y}}yuniekox}rmjolkkllnorv{}{xvuuty~|~~}siaci||tmgc`^]\]_afkrx~{ric^\]_fpqrqqponlmyzy{{{zzyxwvtrojjigpu}zsplidafcacdhmt{wnnpvtnifddddefhknswypot{}xrojmpmnnoprtttp|}|xqns|wphc^ZXX[`jvx}}xvv~{smhlpxsnhda^aeiiilotyzv{yohc`^^]^_`bdhjnov{w~}{wrjb\\bguspwuplfa]ZXY\ahow}{vpssrx||slfn}zuponmmnnopruy||uqvz|vrpnnnnuwvxxzz{zz}}}{xwvxx|~wrrtv~rjc_\ZXZ`^_bejpv{}y~}~~|{zz{{||{wyutz|ytmjfc_\YXY]cm{}wtyy}wpmigekljkkmnopooiltptw|}xsoid\XT]ioyojeb_][YWYachov|yxvvvwyz}yrpsrzrhcaagmnrmlkkmqu{}zzxutrpmkihimpxz~{{yxwuv}zzywtpjfdjluysskkiiijjkmorvy}~z~yvtsszzyy}~ytmjgu~skea_]\\\]_bglqw|~|~~qib``bjpmmjiggffgohqvtwvwvvutsqomqmhfgosyuokgbcd^\Z[]cju{ts|xplgglrvx}}{}}zx|yf`~zux}{rgkp~|xxoporrrqpoooonmjlh}|xvwy|~vw~}hhzmjpw}yvxndairih_XSVj}yvw{ĽqhjYKIHJJLNPRTWZcjjnhntv}{tod^ds}~mZ]wwrw|wmy{w}}|yxy|xtwxjej}znsrsuwwuroq|wjfowlfYOKGP^ityz{~r__`zu`QQUYVQLKPZfry|srtpst}~~tnhhm{~pednkecXXX\fhjmoruxz||zune`t~xt|y{}ui`g|qadlcb[WWWY]eq|vmozse\VSQRSUX\]a\`hkrw~}skkwuovvzrkc]hykehoy~zvuuwz|ty{~}~|{}vmmko{{wsu{~tyypj`XURQOQU_jsz~v`QOfseceeaYWY[cmnv|yyvwx|~zwspqwxqxvlgXVYY]`dgknqtw{}yoqhhm~y{}z~ytuy|wledk}n`RIHLTSVX^gr~scafw~oe^ZXWXWXRU\\bhuy~{vlis|sko}tqrxzvmdcn~ui\ciny}zxxz|~}y~~zxwstvy{}~}|yunlptvjbh|xhZWTTSQONOS[dn|sZIFSd|qlnpoib^X^lr{~{zz|{{|yvw{xo`[XWU\]_begiktz{}{v}xvsqqv~}ugbft|xplmrvukeadqzmkwk[NIIKNQSVZajuxpifeitsnd_[ZRT[[aflqv{wl`arrsurv}xtzwrrwywnebi~}~}wpgckpz{xwspz{~|v~zqmmnqsuvwwwvuzyuqmrn~qijqntujff\XVTRPONQV^hrw|x~zrqi^SOUe{yzzykcb]bpz}|zwqns~~yurpnpynccb_^^[^cchkoswz}|x~}{zy|tidcfksz~~zrkhjputiY`fwxor}wtrpsibXOLJLMOS_hp|~}vqmkjlr~um_]a^bejmruxz||{}ylecnmfh_cku}zt||uty{xnbXYjyx|~umdinx|tzy||ofjlkmnprtuwy{|}}|xoqx~|}~uf[^homlb[WTRPPQTZaiju~njjnokaXU[gx{tmlov~xnghv}|ynw{xqc[[k|~ytpmjhgggfemorwz||w~ysmhedeioux}}smikolsqflu|sfgehonhcXSRSW[ahox{}yvsrrsuxyujnonpruwy{}}|wnfcmqnl_WX_dnvywwuj[PVgt||~vppzxqmlmnoryqnllkjhfcdiit}ytqsu}g_RFB?@CHPZeox}wv|ndbfr}|vnhfimur}~}{|~wrmkjlqzxxrre_befikmoprtuxy||~zv{omy~ttnlic[TRU[bee`cqwpuwfSJHHKOT[bn}|wqgihls}}solmnpqskmroqpqqqqqszxvrkkm|}{yz|~opqtrmhirujb]djouw~{{{||tuxxmjjkllmlkjiihklwvges{pfVNHECCEIPYdoz|mkwreXW^jvzrohnz~~||}~zrs|unjgflw|{tigfgfekkmoprtvwyyyut~{~~{md\\aehe_VQUY`hnnljv|tryx{lb]SPNPRVZ`is|wlccdgimpv~zuskrtoqpqpqqrrsrqlf^\bx{v{z~}trtutokkr}~~{vmhbfpu~~xzvmijkmnnnmlkjihiiklp{{vyzic`a`^YNIDELNS\eq|}srwsrnhf`]^fr~{stpuz}ytnggo}}}vplkklnoor}tpnmlkjijqrqtvxz|}zt|{|~}qaYWY^chigb\XY_iqvuqdq{yvlfjlhf_WTSUX^fnwvjfbbcegmvx|y|vvttttsstssqmf]X]imgcfks}~{xs{{wqicbj}xtx}zsmkjv~qjiijjjjpqoonnnnooojuyxgXTT\``aXSPPRW^fpyqf]aejjgcelx|{}}thbdrtnnejty{tnic]Zas{yxwwvvuuttsszz}~yyh`YWVY^dhjiea_`fmwvxuquzpmhffjjnma]Z\_fnu~toigdddgkqw~}xvtxzwwuqka\]ikecZ\\gry}zqcWUex|vqpty}yxy|vponnnooppqrstuttlrvv}qjaZTSW_afe_]\^bfqt{}sx~|i`Z\`fjpmiltthcntjg\\]bfrz~|paWUalmrnpswz{||{{z{{{{y~}qf]VW[\bhnqrolhgdpxyzuuvwvuwuqljlonqjecdgljuxtomigfefhlsz}{zxtmc\\gjdaURRUY]ex~}}ylc[ch^a`isxxtrsyzvsqpppppqqqswmrtrvw|{upkf_^ZVZbjnuomjjdjtv}qhl~xrke`^agmppnmpy|vs{znfVQOQTY`dn{}wj`dha`YX^dkpwx~yz~ria\[]cjqwyvpbbdemqtrqvzzxwyyxtommrrqqjl`cloxxqmjhfjkijknsz~ype[\ksg_UNMNQTY^dhu|wphkp{m[NCHSbnvwusv}xw}rtzzvtssuvxxytpwsttuwy}}zxvsqmid`^bkv{tjmkmqv{tq{smflnpoke`_agmrsrqyyrt|m_RMHHIQW[bjr~vnijxraVLNRYaipvz}xt|~sopt}xogccejs}tukfehntwwvuz}~|wttx}}}opmmrw~ulkolmmllmnorvzthdlwlaYQPOQRTVY]bhnutbZduwocSIB@FXgr{~~~xmkvxvutw|~|{||}yryvutssx{z}}{zxwutpmjmw{|vsrtw|zyoe^``isvsmhccgov|}{vspxypea[WSQPQTYajt|nbeuwpn_XTSUZ_fmtyzrs~{srrqrvx{~zuvw{zy}xqjfeiouxwurqow~zv}~wsry~}tronjponpprsuw}|qr{tlcXURRVZYZ[^bgnu}vlle^^Yb]YRLJP[kyynlx~yystx{~~|{}{~}|{{yyyy{|{wxxow|}{xuvx}uiecehknxwvrnklqy|~~~~wriigfb_[XVWY]dlx{uzthb[[\b``]_bgnv}}yu~{yonoswz||{{~|t||||xwskghlsxyukbbt~~}{{~~tqmptrqnpvuy{~}}reb^^]]]]]^`cfkpx}n]JFKR^c]YSRWcr|u{}uqfdfkv}~}|zzyxwuqmlkwxqqt|~||~|{~snigffhkoovyututrus}xfa_dkkjifdbcdgkrw~|}saYUX]`hhdddhmu|xu}yvjffhkpuy}|z{|~}{vpjffhigb_dmjpru~}{zyyzyxusrrrrrsstt|y|{~rlgecba`__^^_cjnotrw{|yeWHDFKT^ifb^^epykdcdhlrw}~|zvph`^gqorkjlq}{wuw||wrlmw}tqosolihhjnrtw~zuqflsw{ngcdfjlnonnmlmmnjnxypia\WUV\_ejfehpuy{yw}yugb_chhlouz}|}}|xrja[YUWcnheZX_ku|sjho~}~yqqpqqqpqqqrtv}~}xyrnkhgfeeeffhikhjuwupjbXMEDIV`nsnkjnw~xxwojgffgjmrv{}yri`\ajdd\WYbjmq}~wttx{}zpimz{|ztuuvvurpmkjknsv|y~vqrknu|rjggilpt{|zxvnlsqvzztqtlk^||wtxwuskc```ccq}xps{snjlrspqklnsrw~{yx{}udcht}}~{~ywxyupbRUvynefls{uRHphPD>>DO[flkfa^`jzrhw{x|xzzmqfdfiloprttwz~}}tsxxurpi_QJUq}tm^YWY\_cgkpt{mRU}hQ@8:FUad_UMOYj||{tqxxpjp}vz|uqnljjjjlnruz{}tqphhn{}yqnkhc\TS_x{vifdegjmqty|eQY~}dOCCLXaa[RLNXhwxzut{}umirxszztpljhhhjmquz|~uuskls{rnkhc]VXf|tgdbdfilnpsw~eU`|eSHHOY``ZRNR]mz|vwqu|~umju{|xv|vromlllmoqtwyyruqjip}{tplhb\VYj}shecdfikorvzyaVf}gVLMT]b`YQNS^mw~vrrmryxpijx|~{vvytpmlllmoqtvxunqlhjsyqnjgaZU[n}skhfggiknqvzq\Vj{fUMOW_c`XPNS_mx}tqplrzvoim~}}wsv~zuronlmmpruwytmpigjt~wpmie_XU]r{ynheddegilpuykYYsyeWPS[beaYRPU`mwzqqolqxwohm~{uqv|xtqonmnpqtuvomoffis~|snjgb]VU_u~zxkheddegilpty~|fWZwweXTX_egaYQPVbnwxprlkox}tlip~}xsqw~yurpnnnpqttummnffju~|snjgc]WXdy}{wljggfghjmquz~yeX]{wfYWZbhicZSRYepywprklpy{sljs~{wrr{zwsqpoprsvvvmomffju}{qlifb]X[gz||wmkhhghiknruzwcYaxh\Z_glkd[UU\hs{wqskmqzyrkku{wss}|yvsrqrsuwxvmqlggku}{qlhd`ZVZgyyzskigggijmpuyr`Xcwg]]aimle\VW^jt{uuunorz|tllu~{xtu~}yvusstuwxyuotmigkt~~}zqmhea[X[ixy|smjhhhiknqvzsaYfwh``elpmf]XZbnx~wyuops{{tmmw}{yvx|yvvuvwzz{urvnjhlt}~|yplhea\Y\lxy|rmkiiijlosw|ra[ixjbciorog_[]fqz~wztoqu|{smny~|zxvy|zxwwxy{{|utvnkhlt~}}yqmhea[Y^ox{}rnkjjjkmptx}p`\mykdflrtog`\`it}x{tprv~zrmo}~}{zxvz~|zyyyz|{{svumkhnu~}}yqmie`[Y`pw}{qnjjijkmpsx||j\Zpwjfhnstog`]`is||y}trqv|{smo~~}|zyvuz}{zz{|}|{swtmjhmu}|}wokgc^YX`qu{wnliiijknquz~yh\]tvjgjpuuog`^bkt}zz|srqw}zrmq~}|{ywutz~|{{{|}}zrxrmiinw~|}vnjea\WXcsv{tljhhhikmpty}vdZ`ztihkrvuof_^blu}xyyqppv|xpnt~~}{zywurt|~}|||}}}xrwplhioy||tmhc_YWZgtv{rljhhhjkmpty}qaZd}siimtwvof`^cmu{vzvppqw}}unnw}~}{yxwusps|~}}|}||tsvnkhjpz~}}smhc^XW[jtwzolihhhijlosw|}m^Yg~rjkouxune__dnvzu{uooqw~|snoy~{}|zywvtros~~}}~}}tuwnkhjqz~||rlfa\WU[kswxmjhhhijlorvzzi\Zk}qkmqwyvofa`fnwyw}vrprw}~uop|~{~|{yxwvtrpt~uxwolhkq{~~|rlgb\VU\mrxvljghgijlorv{yh\[n|qlnsyzvnfabiqzzy~urqsx~uop~}z~}|{yxwwvurqv}v|wqmilr{}}|rmgb\WV^mrzumkiiijkmptx|wg\\p~rnqv{|wogdels|z}vtruz}toq~}~~~}|{yxxwwvusrw~w~wrmjlqz|}toid]XW`ms|unljjjkmoruz~wg\^qtqty}}xpifhpw{vusv{}tpr~}}}{zyxxwwxwwtty~zxsnjlqy{~|sohd]XWams|solkklmoruy~~uf]atusv|~yqkhjqy|ywtwzvqt~}}{zyxwwwxxwuu{~|xtnklqyz~{soid]YXdmu|rolkklnprvy~}~se]dw~vux~zrkils{~}yvtw{}uqu~}~||zyxwwwwxwvtu|{~wsmklqxy~zrnhc]YZfmw{rplmlnoqsvz~|{|~pd]gx~ww{zrljnt|~~}yutw{zsqx|~||zzyxwvvvvvvtsu~z~wsmklsyz~yqlfa[Y\hnyzqolmmnoqswz~~zz{zla^k{~wx}zrlkou}}~{wtsvzxsqz{|{zyywwvuuuuuussvx~ztqlkmtyz~xqke`ZY_kozwpnlmmoprux|}{yz}uh_`o|wy~zrmkou}{{xtsuy~xts}~zzzyxxwvuuttttsrrvyytpllnuyy}void^YYakp{tomlmmoprux{~{yxy|}rf^br{x{yqlkpu|yzvrrty~wst}|{xywxvvutttttsrqrw|ywtolknuxz|tnhc]ZZclr{romlmnoqsux{~}ywvx{|pe_ds|y}yrnnrx}zyvrsuy~~vsu~z{}wxvvuutsssssrqprx{{wtollouxz|sngb]Z\fmt{rpmmmnoqsux{~|xvvx{zod_gv|z~yrnosy}{zvrsvz|usw}z}|wxvvttsssssssrqszz}wtollovx{}toic][\gmvzqpmmmoprtvy|zxuvx|ymc_iw}|ysopt{|}|wttvz~xty~|~{{xxvvutssssttsrqs{|yvpmlovx}~upid]Z\glyzrqnooprtvy{~yxvx{xlc`jw~~yspru||}xuuw{~xtz~|}|{yxvuttssstuuusrs|~{wqmlovx~~wrkd^Z]gm{yrqnooqruwz}}ywvx{xlcalyysqsw}~xvvy}~xt{~|}~zywvutsssstuvvtst~|wrmlpvx~~wrkd^[_hn|ytrppprsux{~|xwvx|wkccnzytruz~}xvwz~}wu|}|}zyvvttsssstuuussu|vqmmpvx~wrkd^[aip}wtqppprsvx{~~{xvvx|tiadn}ytsv{~}xwwz|vu}{z|yyvutsrrrstuutrqt}vqmmqwz~xrjc][bit~vtqppqsuwy||ywvx{|qg`eq~xssuz}~}ywwy~}wx~zy{}~xwutsrrrrstttsppu|upmnrx{~wpha\[djv}troooprtvx{~|{xvvx{zoe`gs}vrrtz|{xvwy~{vz|xx{|{vutsrrqqqrsssqopw}ztpmnsy{|unf_[\fly{rqnnnpqsuxz}{zyuuuw{vkcaku|vrru{{~yvtvx}yv|{xx|zwusrrqqppqqqqpnnoy~|xrnmotx{ztld^[^gm{yrqnnopqsuwz|~y~xwtttwz}shaalw{uqruzy~xutux}}xv}~zwx|~{wusrqqppppqqqpnnp{|xrnmoux|zsld^\`io}xsqopqrtvwz|~~}v~zvussux|zpgacoz{vrsuzy~yvtuw|~~yx|xvv{||}utrrppppppqqqpnnq||wrnmoux|yskd^\biq~wsqppqsuvxz|~~|v~xutrsux|ypgbfq}|vstw{z~yvtvx|~~yy|yvw{{~}vurrpppppqqrqpnor~}wsnnoux}ysld_]djs~vtqqqstvxy{}zw~wusrstx|yogchq|wuvy{}~ywuwy}}yy~}ywx{{|vurrpppppqrrrqopt~xtonotw~ytle`^eiwxvssstuwy{}yzwusrsuy}xngcir}xwx|}zwvx{~}xz~~zxx|}}xvsrqqpqqrsssrqqv~xtonotv~zunfa`fjzxwtttuvxz|~vz|utrrtw{~wngelt~yxy}||xwx{}z||yvw{~{xusrqpppqrsttrqrwytooptwzvngbbhm}yxvvvxy{}~u|{ttqrsw{}vmhfnu~zyz~}{xwx{}~z}|ywx{~zxusqqpppqrsttrqqx~ytpopuwzumgacgo}zxvvvxy{}~~~t}xtsqrsw{|ulggow~zyz~}~zwww{}}z}~|xww{~ywtrqppppqrsssqqrz}xsonptw~xskfbeiq|zxvvwxy{}~|{s|vsqpqsw{zrkfhqz}zy{~}}zvwx{}|z~~zwvw{~}xwsrppoppqqrrrppr{{wrooqux~xrjebfjt{yvuuvwxz|}z~xs{srppqsw{~woheir}{xxz{||yuuvyz||~zwttuz}z}vtqponnnoopqponnr|zvqoorvy~wqidbhlxzyuuuwxy{|}~}|w{sqxppopruy}{tlgeltzwvyy~|xuuvy{{}|yvssuy{zzusponnnnnopponmnr|yuqoosvz|vogcbim|yxuuuvwxz{|}}}|zuypswopnprvy~zsjedmt~ywvyx{wuuvy{~z~~|xurrty{zxurponnmnnoooomlnt|~yupoosv{|vngcdjn~yxuuuvwyz{|}}}|yuxntunonprvy~~xqjffov~ywwzx~zvuuwz{}z~|xurrtyz{wuqponnnnnooonmlnv}~ytpoptw{|vnhdejp~ywuuuvxyz{|}~}|xuxnvtoonprwz~xpiefow~yxx{y~zwuvx{|}z~{xurstzz~wvrponnnnooponllnv}zuqppux}}woidfjt|ywwwxy{|}~~~}|wtunwrpppsvz}}wpiehpz~zxxzz|xwwx{}~|}zvspqrwy~ywsqoonnoopqpommnw~{vrpptw~ypjegjv}yxxxyz{|}~~}|wwvqzsrqrtw{~xpjfjq}~{yz{}}yxxz|~}~zwsppswz~zxsrpoooopqrqpnmow~|vrppuw~ypjehky}zyxyz{|}~~}wwuszrrqstx{~xpkglr|z||}yxy{}}}~{xtqqtwz~zwsrpoooopqqqpnnpx|vrpquw~ypjfjl}~zzyzz{|~~}vwstyqrqstx||vnigms|z}||yxy{}|}}zvsqqswz{xtrppoopqqqqonnqy{vrpqux}xpkhko|{z{|}~~~|zsuptvprqtvz}zsmihou~|z|z|yxxz{}~{xtqooruz~~}yusqonnnoppppnmmryzurprvy}wojhlq}{zzz{}}~~~}{xrsoutoppsuy}}xrkhipx}{y{yzxwwyz}~}zwspoosty}zxtqonnnnnopoomlmtz}ytqprvy{tmiimt~|zyyz{|}}}}}{yuppmtqnoorty|{vojgjqz|zyzy}ywvvxy~}~|yvspoosty{xvqpnmlmmmnnnmklnv{|wsqprvy~xrlhjmx~{zyyz{|||}|{zwsnmmsnmmnqtx{~}xslhfkr}|yyyz|xuuuxx}}~|zxtqonorsxzxvqpmmllmmmnmlklow}~zvrppruy}xqliln{~{zyzz{||}||{yvqmlmsmllnptx|~~{vqkggmt|yyx{|xvuuww~}~}{yvspmmnqrwy}vtpomllllmmmmlkkow~~{vsqqsvz~xrmjnp{{z{{|}}}|{zwuokjmqklmoruy|~}zupjhhou|yzx~|xuuuwx}~}{yvspnmnqrvz|wtpomlllmmnnmlklpw{vsqqtv{}xrmkoq||{||}}}}}|zwuokjoqlmmpsvz}~zupkhipv}zzx{xvuvxy}~|zwtqonnpru|{xtqonmmmmnnnnmmmrw{wsqqsu{}xrnmos~}}}}~~~~~|{xuollrqmnnqswz}}zupkikqx~{|z|xvvwyz}~}{xuqonoprv~|yusponnnnoooommmsx{wsqqtu|}ysonov~~~~}|yuplmtqnoortx{~~zupkikrz~||{|ywwxy{~~|zwtpnmmoqu{yurponnnnopoommnsx|xtrruv~~zuppqz~}{xtnjnupopqtwz}}yuoljms|~|||}zxxxz|~~|zwspnlmopv|yusponnnoopoommnty|xtrruw~ztopq}~|zwsmipuopprtw{~}ytokjns}|{~}zxxyz}~|yvsomlmnpx|yurponnnnooonmlnt{{wsrruw~ysopq~~~~~}{yvqliqtooprtx{}~{wrmkjot}|z{xwwxy}~}{yvrolllnoy{wtqonmmmnnnnmkknu}zvsrruw|xroqs~~~~~~}}{zwtojgrrnoortx{}}zupkiipu{{y~zwvvxx|}}{yvspmkklnoz}zvspommmmmmmlkjjou~yvsrsvx|wrqrv}}}}}|{zyvtplfeqomnorux{}~}{wsnjijrx~zzw}zwvuvv{~}}}|zwtqmkiijmn{{ytromlkklllkkiijou}xurrsvyzvqqry}|||||{zywurnjdfrnmnoruy{}}|zvqlihkrz|yxw|yvutvv{~}|}}{yvsplihhiln~{ytqnmkkkkkkkjhhipv|xtrrsvyzuqqs|~}||||{{zxwtqmibgrmmnprvy{}}|yuqlihlt}|yxx{xuutuu{}}|}}{yvsplihhiknzxspnlkkkkkkjihikrw{xtrqsuyyuqrs}}||||{zywtqmibislmmprvy{}}{xtpkhhmt|zxz{yvuuvv}~}|~}{yvsplihhjko{xtromllllllkjiikry{xtrrtvzyvsuu~~}}}|{zxvsqlhaksloortxz|}|{xsokijou|{x}}zwvvvw}}}|}{yvsolihgiiq~{xtronllllllkjiilr{}yusrtv{{wstv~~}|{ywtqlhbptnpqtvy{}~}{xtpljkpv}|y}zxwwxx~}~|zwtpmjhgihs}|xurpnmmmmnmmkjjms}}yusstv|{wtux~}{zwuqmhcssnqqtvy|}~}{xtpmklrx~}z}zxwwxy}}{xuqnjigihu||xurpnnmmmnmmkkkos}yusrtv|~{wuuz~|zxuqmgduroqqtvy{}}}zwsollmsz}{}zxwwxy}|zwtqnjihigx||xvrqonnnnnnmlllpt|yusstv|}{vvu}~|zwtplefvqopqtwy{}}|yvrnlkns|~|{}zxwwwy~}}~|yvsolihfggzzzvtqonmmmmmmlkkkpu|xutsuw~}{www}|zxurmjcgvoppruwz{||zxtqmkkou~{||ywvvvx}}}}{xurokigfgg|zyuspnmlllmmlkjklqw{wussuw~|zvwx~|{ywspkh`humoortwyz{zyvroljkpv|y|~zwvtuuw}||~~|zwtqnjhfffh}}ywsqnmlkkkkkkjijmry}zvtssuv}{yvxy~}|zxvrojf^isknnqsuxyyywtpmjjlrw~|x|yvtsttv}{{{~}{yvspmihffek{xvsqnmkkkkkjjiijns||xusrstv~{ywx{~|{ywtpmhd^lrknnqsuwyyxvsoliikrx}{x|xutsssw}{{{}zxurnkhfdeclzxurpmlkjjjjjihhjns~|yvtssuw{zxz}~}{zxurolgb^nqlnortvxyxwtqnkijmtz}{x|yvtssrw|zz{|zwuqnkgfdecoyxurpmlkjjjjiihhins|yvtstux{yyz~}{zwurokgb_ppmopsuwyyywurnkjjnu|}{y|yvuttsx}z{|}zxurnkgfdecqyyuspnlkkkkkjjiijot}yvtstux{yyz~}{zxurokgbbspnpqtvxyzywuroljkou~{{|ywututz}{{|~{yvsokhfddduzzvtqomlllkkkjiijpu}yvtstux~|zz{~|{xvsplhbfuqpqsuwyz{zxurolklpu|~}zwvuvv{~{||~|yvsokhfdddwzzwurponmmmmlkjjkpw~zwuttvz}z{|~|zxurokhbhvrrsuwyz{{zxuromlmrw|~{xwvvv|~{{}|yvrokgecbey}zzwurpnnmmmlljiikpx~zwuttvz|{{}}|zwurojgbkvrssvwy{{{zxuromlmsx{}zxwvvv|}{{}~|yvrnjgdcbgz}zzwurponmmmlkjiilp{}zvtstv{~|{{~}{yvtqnjfbnvrstvxy{{{ywtqnllnsy~{|ywvuuu{|z{~~{xuqmifcbai{|zyvtqonmlllkjihilq~|yvtstu{}|{|~|zxvspmhdantqrsuwyzzyxuromkknt{}{{xvuttt{{z{~|yvsolhecbal|{zxvsqomllkkjihhins~{wusstu{}{||~|zxvspmjfbaosprsuvxyxwuspmkjkou~~{z~zwutsrsyyxz~~{xurnjfdaa`mzyxvtqomkkjjihhggins~zwtsstu|}|}~~}{ywtrolhd`boqpqrtvwxwvtroljjkpv}z{|yvtrrqrxxx{}zwtpmieca`apzyxuspnlkjiihhgfgiou}yvtsstu|}|}~|{yvtqnkgc_cppoprtvwwwvsqnkjjlrw}y}|xvtrqqsxxx{}zwtpmifca`bqyxwurpnlkjiihhgghkpw|yvtrrtu|}|~~|{ywtqnkgc_eppoprtuvvvurpmkjjmsx~z{xusrqpsyxx||zwtpmifca`dsyxwurpnlkjjiihhhilqz|yvtsstu|~|}~}{ywtqnkgc_hpppqstvwwvurpmkjkmsy~z|yvtrrpsyxy|}zwtqmifba_esxxvurpnlkkjjiihhilq}}zwutstv}}}~}{yvtqnkgcalrrrsuvwwwvtrpnlklot{|}zwussruzxy}~{xuqnjfca_gtxxwusqonmlkkjjiiimr~zwutttw~}~~~|zwurokhdcnssstuwxxxwuspnmlmpu}}}zwvttsw{yz}|yvrnjgca_itxxvusqonmlllkkjjjns~{xvtttw}~}~~}{xurokhdeoststuwwwwvtrpnmmnqv|zwutssw{y{}|yvrokgca`ktwwvusqonmmllkkjjkot}zwutttw~~}~}zxurokhcgpststuwwwwvtronllnrw|ywutsswzy{~~{xuqmieb_`mtwwvtrqonmllkkjiikov}zwutttx~~}}{xvspmifbiqsutuvwwwvuspnmlmnsx~|yvusrrvyx{}~{wtplieb_answvutrpnmllkkjiiikox|yvusttx~~~~|ywtrolhebjprtsuuvvvutrpmlklntxztpllorv|yk^USRTW`deijiaYcsxw~zwuuvwz}~xoktqrvx|{{|{xvrqt|lb]`ifZVICA@BDFJOTZ^`_^^h~o`TIGFJMP_jq{|tfhiluqep°xl`YTT\k~rjc^\U^ksypnu{{w|{yxyz{zwpja]durhgaeipv||ĽlXVlhSG=843468;?DJPV]_bdotz|utqmhb\WV[eu~ysrqrpry|wmhevt`QLTnp^TPT[dlpqnhc\XTSNTl{wqorpolhht~vqqw|upms~|j_\ima_Y[_ekqx|~þx{rhi_OD81/./0259=BFJFPX]fnv~{oijptsyykgb`__`cfjosuurvžm[MGLaq]NGGLU^fgfg`[WWX_cj~~yt{}~{{~{unf`^dt}vw~tf`hx~{uxvmd_ZWSOJB;7ADHMPTR^ejrx|rifinjhmsvrngfdddginsy~~}~pbWRUarl_XY^fmouuojc_]_ckow|yv}|rq~}{||}|zwsnifgpzw|vvu|}{xqkga\TLEBGQZXXUOQKS[^eiosww~ysonr}tqlt{yvrllnrw{}|xx}rnjkkopy|hYXd{}dVOOT[bghf`ZSMLOU^ekyxog`]^es{{xtvuwx{|~~~xx}wrnnr{}mldda`^^]]]^_bdhikddhdir~xux}qonnnnljfb\\gq~zjc__`deozzvnmsw`WYetyzwkcYRKHHLRY[ku{|~z~xplo~~ttstsuuvwxyyvsmlr{zvnlow}~ve^YWUTSSRSSUSW]_dhhhtv{}}skgfeda^ZVRRXdq~~qwtsrs{|ukdbhr|vkpvz}qfabk{uontz}rfZQMT`pp|~}wwwrsrsrpmnu~{y||wy}wpidgsyxzqqsvzzzg\UQNLKJIIIKMOSVZ\[hlqw{}~vohddijdc_dnxxyvhkjghiknrv|}zvwy{{yw}sg]WXarlgfkpvztmheejoy|||xqkmy|zxxxvtqnkkoyvnr}zzuy|unh`XPOS\`_`YWWXZV^egmottx}}zxuqmkkq{~oq||||uqqrvz}|qmjlnr|~l_\fwye][_flqsqlg`]\^cjqwysmjedgp}|yusrv~ytomjiggfffhilnpnlbbko~~~wqpz}rokkiifc\_afs~xvga]^_fsy|xs~ups}mdjsx|wqiaYTQQTZbil}zqjiu~xyxyxyz{|~|vtx|topw{ke_]\\\\]\[bcfjnqutu}nea^\YVSOMNVbpz}zszv|qc^cmwzsnjhdqyypieiqyy{reYSWbouw}|zywwwxwuqpv~{{~{~}{wslhit|}vutvvn`ZVTRRQPPPQSUX\`dferuxzqh_^agea\UX_jprvpmchjimotx}vsst|xqqwyu~}|tj`XV[i|{mmly{tnjjmtz{upkhkxyurrqomjikr|}qjm~vqnmuvx{}ukaZVX^edf`XUTUWY[Xchinpqz}}~zuspmiecelt{zvvqutyzwuvx{vomuttqedefo~|~|ykb`hpxsdaciotwvrmga^^`bnx|wnju{snjfcbcit{}~z{~w|~z|yxvsqoqwzuqponnmlllljifc_`_l}{}vzyy|~}xqmmwyzylgca_ZW[Y]gs~}rimnjj^ZZZfnt|{vqolmtvwqqw|{x{}zywtng`ZVUW[ckrt{tlfgrv|}z|{||}~~}yvzz|xqmmr{y~}~|nieddcd`dhfjkoqtvzwz{qstsug_ZWTPMKKOXcou{zzrq~}ywppfelm|~|~}~zvpjea`aefv}zrjedj}qe_`hltywzw~|zyyzzzxtsv}|~}~}zwqmmv||zuz}|~pga]\ZZZZZ[]_cfjmqqoy}rebcfcd[TSW`imtuqpmgossy|~{wvu{rllpqvwow}||wqh`ZZap{|wtsv{zxusmjjs{}zvusrpqsz}z~zrtwuxy{|}shbbgknphd`^^_adegcnqqrv~}~{yzuolgdaabhmrpvx|z|}tlgmnvxoko|xuvx~~zogehkx{xx|stv|ztolhkuzxokmz|toljhhjqy~{xv|}||zxqyzwxvvtstw~~ywvutttsrpmjfhkus~|xw{xz{}~}xrlipsxznic]_^[]bku~sh`bdgmeacmpw{~~|wrmiejqu{~zspr{||xrkfb`bflt{~{unijrz}zzzv~ysonpu~}x}}wsomjhnnnpqstvx{|~{}}wmgjllqe]XSPNMNT]hot}zzv|{xqfbehvtoxwuwwwuqke_[Y[_ejl|~|yske`hu{snnoourrux{y~|{yyyzxwrrq||}y~~~~{xsoow|zvzzz}|~zvuxzyyy~qjeca```aabdfhkmopns~}}zspqlslifdaVQNRYbgkronmmlhtxzxrqww}sy}yphegginjkmjswqnf`ZY\hx~{}xvux{yvtrolhefotzurnmlmouz~~wpspqqstuvvwxyz{|{yr~~wnnqqurjfa`__`bdffgcmolwx{~yvv}}rleb]\[^bginx|~zqu{y~xpga_gjqtpwz~{upmmnsw|yztpkkikqt}sstx|~|vpimpry{vrrtuwzqkgedfks|wwry~~zvussssrqkturrqqpsw{ywvusplhdbektwvwwpxuwxz{||}|{wslhfmovxhcb\ZZ]dlw|wncZUZ^fpgmnquwxurmhc_fhnv~}vqpt}}|xsmgcbcglt{}vlggoxx{wtvz}}zspmmpxyuy||xpqtqsstuwxz}~}{|xnf`beipd\UPLLOV_jpy{}zl^W[iq}xturqlhb\XVW[biqvu~wridjo{{wxvyysrrv{~{txx~|}|wspw{}}z|uqrz|sruxwpjfeddefhjloqtty||znb^cj{wti\SMMQYbgqusuuvwxv{smltzzsjebddkljlnqtovvlg_\[am}w}zz{~~{xwusojgelr}}yspoosuuw|{tsqrrssuvwyz|}}{}}wvv~~wpkgdcccdfhjlnmjry|~{usxzumd^YXY]cix}{t|}xsle_^cl~ztoljkosxz{srvoqmqonvuknsxtwsuy~~ywx}}}ttuxyqkfefls}}~|{}~{vsqpooppqpojsuqrqsu|~{xvrnjfegnu{|xnmwuuwxyz{{||{zwrlgeikpxme_\\`hqy~}}yrkd\VTYahxxttttroje^^bbhow~~wrnou|vqkhffint{{xy~~}pkipu~}w~{vrnnqx|uvvnmjklrz|wty|{}xvuuvwwxyz{}}~~}vs~xuvy~ysme`\_ael_XQNNRZckq{v|}ukc]]_gvtvtonie`ZVSRTZ`hossnuoddfgq|snklosy|z~}{zvuxrqs{~yy{}ytrpvw{x}{|}~yxvrmknw{toruyyrmjihiijklmnnvwwyvsypf`^elsrcXPNPW_elussstuxy{y{vpkjktv}tmgddccgb`adhmnnfih][X\er}y}ywuuuv{~{xvtsqolheelpxwrnlq}zsnpqu|vrooooppqrsstru{xvt}|}~zrrx~vpkigggghijklmmkfyzx||~~{upnrr{xkc[XWZahu}|z{v~zwtplga]dmozvpkgedfimprppimsnqiilv{rnszyyx{}ywy}woijlq}uokikpx~uqos}xtqonmmmnnooonirtqstw}{vqliilrv}{uqivyuxwyyzzz{zzxurlgbelsyniddgltyz}zuplgc^[Z_dv~zvqke^baafksz~y}tomnu~|z~~xspnoqu{~xspqp{{vww}|xtpmmpuz}yoqumnmqu|}x|{}}}}~~}xquyux~|xuqlf`\\^cne_[Z]clq{}unjhiinpmq{}vnha[WTSUZ`houyytkooijnx~}vrpqty}{}|wusx{}{vssy~}urxnllr|~wrrt{{vsrqqrrsrv}{~~|}|wqlhgempmvlaZWY^eiuyyz{}~}{xtpont~~ulgecgga``cgloqolacb[]aly{xuttw}~{xwutsqokigknxytx{~~wpkiqxxuqpoppqrrsqw{z{zxu}}|vuy|tolkjjjklmmoopmrqz~{|zsolnluse^XX]gr}|}|}x~|ywtqnmhgjhit~xqkfcabdfikmooqjougfenx~vqqy|zz{|{~|tmgcdhmzsnlmry~wmhgw~xsnljjiiijjkkkjidnpnqu{wpkhimqw}yuqnhmiowrvuwwxxyyxwuqmeefgirxnkilrx}|zyzz{uqmifda_\[Zeqyypja^`\]`elsy}~zqjohgjr|vrv~|vqnllnqv{}wpga_ch{}~{}~~~}}x|y{wqlhhinryzvqlh`djfkov~|xt~}||||||{zyuqjmij{{{xvsqokf`[XY[`kc__bhmr|unihimpwrlfktt|rjaZTQPQTY`flqrqjfbgffoz|ww~~uokjknrw{}~}~|ytrqvx~}}|xsnlvv~|{{{{|{{{zxvqmdajhpy}wroqt{|wsqpponvwwyzzzy~||xsqpqqqtkgdblf_^^dgpyxzyz{|~{}{zxwuqnjpvtupkilf`][]`eillke_TY\Zcn}{uollov~|yxvutrpnjgehjr}~}wqignovzvrqopopopxwxyzzyvs~}~}zspmlkkllmnopppwuq}~yrkffclla``ip~~~~~y}{ywtsqpmsurqnv}xpida`abdgimpsswjfjcho|~ywx~{vqkeaachxvtuw|yslnv}uqmkjjjjkllmnnmlgptry~uqnrt}}yvtpotrls{wyyzz{{{{{zxsvpjfggq}wxx}~{uqnlkjjklkigmqs{~vkif`achmsy~ysidlilr|~xvy|xtstvy~~xqjb^^el~~xqkijnq|~{yurokibhomsx}|xuonrppv~{ywvusqnje`[[[bqnnoru{unkilow|yrnqnorzpg^YUUW[agmrttqsnf`jnsyvxztqpqswz~}zvrorr{{ssuzz}|{{zzyywuroie`csu|wstw}zxuv}{||}}}{~|~zwsrruw}}tkb^\]klikmpz~}~|zyyyyxwroqlmly}ttqga\Z[^cgkllie_ZQYcfsvngdcdjorzwwtnaWV[Z^dkqv{}}ulgdejtx|||yvp|}uuokpw~|vumjjige`bkzy{psvw~nbveLGAABFIMORUWZ]_bcc]chjorvww~}z|wuvvwxxxxxxyyzxvlee^`jxtk[]^bjs}ïy\E@Zöw^F8-()/9EQ]fkmlf]ORPPV]gpw|{ukhqrxz||~{pdXPWnxjcZWUUUWY[^adhkooofba[]dpxupnijlonmhhqyvowzûo_]qn_QJB=86432357:=AB>JMRX]bfgqv||y}wtqnmlkklmnppoecc^cn~}qeiiowt`OFNnuaQE>;9888:<;;<>@DEHBKQSY]bfhlvy~wyytplihgghjloprmimggkywgiinw{k[NJYsbSJEDHOW`glnlh_RTOORW^hnz{~}yjfjjpruuuw}wog_X[l|wmidb_^]^_bdhkoprjjja`akx~|}vuuvwtokq~u{|ýwg^focZRMHEB@??@ADFHGEPQVZ`chhpw{zuzyupmjihhijlopphlkfhn}|nehgowueUIMcq`TKHINU]dknolg[SUOQTZbkq~{{ufihkoqsstx}wph`XU^v||slifca```acfiknpneid__do{}|vuttrnijuvtyz}n`_p~lcYTNKGECBBCDFGIEHRRX\bejjsx|~yrr}~yuqnkihhhiklmidkfegr~}xecdfnv|k[NHRq|l]SMKMSZbinqpleWVTORV]emt|}zndiglorspms~xqiaZ[g{}rokigedddefhknopkgkdaait}xvvurmjn|swy{xj`czzkd\XSPMKIHHHIJKKFOSUZ^cfijtw{{{zsnq}wtpmjhgffhilmojjpklp{zyvm`dcjr|vgZOM[~{l`WRQSYaiotwvrj]_YVWZ_fnu|lihgjkmmnov{tnf_[_n~|rqlkhhffghikmppqijkdccku~ywvtqkjq{tyw}tgafyof`[WTQONMMMOOQMMWW[_cgkloyz|}ysptyvroljiiiklnppiqqnos~xxywjcedls}reYPQ`~mbYVUZ`govz|zvjde]]\aems||lnikmopppqxzuoia^ct{usonkjiijklnqssrkqlheelv{zyvrlkrywzx~vjdkyqgb]ZWUTSSTTVVXRW^]cejmppu}{usxzwspnlkklmpqsqmvrsx{zrtxssdefirz{{pd[TWi~nd]ZZ_emu||onkccaehos~{qpjllnnnmou~ztngej~}|wurqoonoprsuvwqqvnlhjp{}{yromx~x}y|}rheq{tlgb_][ZYYYZ[[ZU_`bfiloqqx~~}~ytnovywspnlkkkmorsvpuzvxx~uousrmbfdkqzzyuj`WU[s|ne_]_dkrzyorjfeehlrwvsnkllnmmmpw{vpidfpzzutrqooopqrsuvvnsslkhkr|~{xtnlm{||tyuz|ynghzxoida^][ZZZZ[[\XXa`dgjnqss{}~~zuolo|}|wuqpmlkklmoqstnvwuwxwpoupod^aaioy|sx|qe\TUb~vkb^]`fmu|tpqgfdfiotzsrkllmmmllpy|wqkdbfv~xwtsqqppopqrstuqltokiglt~}zvtnihozy}stsswy~|rhel~wnidb_^\\[[[\[\V\b`fglmqrs|~{z}~|}}zupjedk|}xvrpnlkjjkmnqsvss}yz{~vmgjmji^`_dks|xto{~vj`WRWitjb^^agnu}uwskjgikptzxummkljighlu~yrlfgn}zxwvttsttuvxwxrqvpnjkpx}xsmiiuz}}rvqtvz}zpgfp}umhdb_^]]]]^^^]Xcdeilpquux}{~|}}yuojden|yuspnmlkllnpsuwsy{}|ridjikf]`^ekt{{wpr~uj`WTZorha_`djqyx|somlmpuy|uppnnmkiinw~yslggp~{zxwuuuuvwxzyyrxxrpklpy~ysmhitwyuuquv{}|qhfruojfca``_``bac^`jhlnqtwzy~}|}ytoiefq{wurponnnprtwyzv~~}pgchglda``gmv}~{vpwtj`XV^ttidachnv~}utqqrv{utqqpomkgkt|wplmz}|{{{{|}~}x{wtppt}|unjnwzzyvtvx{~ynghvwqkhecbbbccede_glkoqtwy{z}||z|}{wtojc`bp}zwtrponnopsuy{~|~th_`cej`caekry~xzuqn|{qg^XXdxsjdcejqyzwtttwz~xvrromjhhmvzunlo~}||{{{}}~yzywsppu~~yqkhpu{~wwrstwy|ulglx~wpligeeeeefggealmnqsvxz|{}{z}~|zwtpkfaagv}xusqpooopqtvyz}yzmc]aaed_a^ejqx~~wrsoily{~tjaYV[l{pieegmt{}yvssux{|wsqomkhghox{upklu~}|{{z{{||}||t{{utpoqwxskgiqs|xtsoqruwz|xoihs{{uokhgfeeeefffadnkoqtvx{{|}||yy|~~|zxuqmhb_`kx|ytsqoonnopqsvxyzwznc[Z_^f```ahmty~~yumllhdox{yof^XX`tzoiefinu||xvuuxz~|yusomifeflv~|wqoq}~|x|yvssu{yrlimtu~uuqorsuwz|~}vnilw~{upmjjihhiijiicjomqsuwy{{}}{||zvwz~~{|{{zzywuspmhc^]_lu~zxvqqopopprsvx{}~wkaZ\]_d_b_diouz~|wrkjjecqv|~ulc\Y[gxzqkhimsz~yxwx{}}yusomifehmw~{vqptz}|{wutv|yqliorx|vwqqrtuxz|~|unjnv~wspmlkkklllljgrrsuwy|}~|}zwx{~~||{zywusqmid_^bnu||xutrrrrstvy{~~wk`Y\[cebdagkrv{~~{wqklkdjvx~ume^\^kxyqlklpw~}{{|{wurolihjoy|wsqv{~}xvuw|zrmjpq{{xvqrrtvx{}}uokquxtpommmmnonojlvsvwz{~{{xuuy~~|zyyxwutrpmie_[Zalvy|vutsstuwy{~}pdZVY[efgffkouy|~~|zvrlhkidqy|zrjc]\`nz{snmnsx~~~{wsokhfhnv}|vsr{{xwzyqklptz{trssuvxz|yqkkqwxtpommmmnonnirvtxy{}}{}zwtuy}|zyxwvtsqnjfa\YZem{~y{uussstuwy{~xj_VVX]fcfbfjotxz|{yvrnhejeeuxxog`[[esvokjmrx}}~|yuqmifehox~|xrqs~{yvwz{slhmow|vvoooprsvwy{}{tnjnu}ztqmlkkkkllljgsstvx{|~}{}~{wtruz}{zxxvvtrpnjfa\XW[hpxwwrrqqqrtuwz|}pcXSVV`cab_dhmquwxwtqmhcaf_gtv|sjb[Y\iw{rliimry}||}zvrnjfddfmuz||xspqx~{ywy}ypjjoq{{xsnonpqstvxz|}~}}vojipuztpmlkkkklklgjuquvxz|}~|yx}~~|yvrooqw}{ywutsrqomkhd`[VST]hr~twurrrssuwy|~ug\SQVXcdecdgkoruuusolgd_`e_mvzxog_ZZ_nz{sljjmsy~}~yupkgddhnw||xspq{|zxy~woijnr|yxqonnpqstvxz|}~|unijqwzvronmmmmnmmhswtxy||}{z}{wsqprw}zzwvtsrpnlifa]WUU`hw~uzvtttuvwy{~tg\SST[ffgdgjnrtvvusplhd_cdbtw~woga\\co}|tomnrx~{vrmiffipw~}ytst~~{zzxqkmnv~|zssprrtuvxz|~~|uokmqy~zvsrqqqrrrpmzyy|}||~|yusrty~}|ywutsqomjgb]YWXbi{~x}xwwwwyz|~{qd[TVVbklmjnpsuvwvsqmjfc_fciyz|umga^_hrwrqrv{}ytojgfiou|}}yutw~}}yrmop|~ytsrrstuwy{|~~{uoknq~~zwusssstssos}y|}~~y|~~|yvsopqvy|}zwutrqonligc_ZWUZbl~yy}xyxyz|~xmaXTUXfjlkjmpsuvutqnjgc`]eanwzyrke`_alv}vrqsw}|wrmifgips~~{wut{|}~}vonoq}~|usqqqrstvwyz{||{||vqmmqt}zvutssstrsmv|x||~}zy~~~}|yvspnoqwx}}yxtsqpnmkifd`\WVW^er~uxzvwwxy{}{rf]UUV[gikhiloqssspnjfb_[Z`^osyzsle`]_fq|ztqqtx~|xsnjfdfios}{~{xttv~}{|}xrlnnu}{xqpnoopqstuvxxywz}|}ysolorz{xusrrrrrqomzyy{{}~~zwy}}|{yxvromklotu}x~}utqpmmkjhfc_\XUTW`gx{uyzwyy{|~~}~}ul`YSVWbjllilmpqqpolida]\W[^cst}~wpic^^`jsysqqtx}zuokgefinrzx|}|}~}{xursw}}xqmooy~|vqpooopqssuvxxyv{{{vqmlps}|yvuststrsoq|y{{}~|zwz|~}{zxurolklptv|z{wtqpnmkigeb^[WTTZbk{{w{{yzz|~}|}|~sj_WTVXglokkmnpqqpnkhda][X^_kvx}vnhb^]akuzussv{zupkgffknu{}~{xtrrx~~xqnoq~~wsqppqqstuwxyz{x}}}wrmlos~zwvuututtoy~{~~~{x|}~|zxtqnmmqty~~{wusqomligd`\WUT[ap|z}{|}~||||~tk`XUV]koqmnoprrrqolieb_^[adsx}}voic`_dmx{vuvz~{vqlhggknuz~~}zwsrryyrpov~wurrrrstuwxz{|{z~|vqlmov{ywwwvvvtr~}}yxz~~}{xvrolkkoqy|}{vtrpnljgea^ZVTU\dw{xxxyyqg^WWXerttqqqrrrpnkheb`^\]cky{zslfa^^fn~|wvwz~}wqlhgglnv|~~|zwtqpsz~vpoo{{usqqpqrstuvxyzyy|~xrnknp|~zxvvuuutqt|~~{xy{~}{ywtpmkjkorx}|yuspnljheb_[VSRW^i|||~~}yvwvx}ulbZVWZiqspnooppomkhea_\[YZ`nwz}vohb^]`js}xttuy~~ytnifegknu|~|||}}}|{ywtqppv~~~yrooq~}vrpnnnoopqrstuutuy~ytoklot{xvutttsrnx~z}}{xvy{~|zywtqnkiiknqv|yurpnljhec`\XTST[ao}y||}}zwsrqtwnf_YXZaottppooonlifc`\ZXWVYaquz}wpjd_]^cmx}xutvy~ysnigfgjmr~|{yxxxxxxwvurpnnpxxrqpu{uronnnoopqrstttrrz~zvqnkmpw}yxvvuutsq}}|~~}zvwy|~|zxwurolihhjlotywsqnljhfdb_[XUUW_ev~{{vsqrquume^YZ[erusppoonmkiec_]ZYXWZgtv}|void_^`hp~}xvvx|ysnjgfhjmu}|zzyyyyyyxvtqooryxsqpz{vsqpoppqqrstuuust~{wromop||zyxxwwsw~~{wxy~|zywtqnkiijmox|yvspnljhfd`]YWVY_h|~|wsqqpvumf_[[]kvxutssrqomjgda^\[ZY^owy{unid``bjq|{{~zuplihikmy~}|||{{{zywurqptyxtrr|xusrrrrsttuvwwvtw{wrnmoq}}|{zv{xxy~|zxvtqnkiijlm{}|xvsqomkigeb_[YY]bn~ytpnnntslg`_^cs{{wvutrpnkhda_\\[[[dwy}ysmhdaaelu}||}wrmjiikm~}|zzyyyxwvtronntywurw{wtsrrrrsstuuvuurz}yuqnnov~||{zxv}ywxz}{zxuspmjhhjko}|xurpnljhfc`]ZXY^dv|vrommntpid^^^hx{yvtsrpnlheb_][[YZYhxx~|wqkfb`afn{|{|ytokhhijn~}{zxxwwvvusqommpv|{utqz}xtrpoooppqqsssrrn{~|yuqnlopz}{zzxxuw|yvwx}|zywuroljgggjjq|yvspmkigdb_\YWVZ`i}}xsollkmrlf`]^`nyyvsrpomkieb_\YXWVWXlvw}}xrlfa^^aiq|yyz}~{vplhgfiis~}zxvttsrqpomkihinuxtrsyuqonmmmnnooppppnm~|yvrollos~|zyywws{~~zwuwx}{xwuspnjhfffihv}|xuqomjhfdb_\YXY^dq~ytnkiiilqjea_`eu}zvsqomkheb^\YWWVWX\swz{uoje`^_clu|yyz}~}xrnjhgihx}}zyvutsrqpnmkiijpw~xusvzvrponnnoopqqrqqnq~{yuqmklou}{zyxvuywuwz~|zxvtqnkhfffii}~~zwsqnligda^[XWX_ew~ysnkiiinqjfbabl{|xvsqnlifb_][YYYZYdzy{uojea_`emy}}~~ysokihik~|zywvutrpnkiijpx~xus|{wtrpppppqrsstssoy}zvrnkmoz~~|{xz|xwx}~|zxvspmjhggil|yvspnkifc`\ZXZ`h|ztokihioqliefft~{xtqnkhea_\\Z[[^]n~|ztojebabho|wqnkkjo}{ywutsrpnlihgjoz}xuu|xusqqqqqqrsstrro~|xtpmlmp~}z{wwx}{ywurolihfggq~{yuspnkigda^\[_dpzuplhgghoojgefiz}zwspmjgc`][ZZZ[\^t}||wqlgc``cjrytoliigs}{ywutsqpomkhhhlr{wtw}yurqpoooppqqqqpnn~{yurolmot~}{zx{xvwx|zxvtroljgfegfu~~{yvspnkigda_\\]bgw|wrmjgffgnlhfefm}}yvspmjgd`^[YWWXXY_uxz|wrmhda`agny~~~}}}~{vqljghgx}~zyvtsqponljiggiou~}}~}xvt{yurpnnmmmnnnnnmmjo~}}}~~~~}{zwuromlnpy}{y{|~ywvw{|zxvtrpmjhfedegz|}ywtqomjhfdb_^^agn|wsnigeeehnkihhiu}yurnkgd`]ZXWVVVYXexx||xtnjeb_`bhp~~|~{{}{wrnkihj||{xvtrpomlkigfefjow~}}}|xvu~yurpnnmmmmnnnnllit|}}}}}}}|{ywtrommor~}|y}~ywww}zywuspnkigeeek~}}zwtromkigeb`__chr|wsnjgeeeinkihil{}yurnkgd`]ZYWWXXZ[mzz~|xtnjeb`adjr~|||wrokigo~}{xvtrqonljhgffjo|}~}{xuwzvtrpooooopppponm{~}{yvspmmot~|~ywwx|{ywtromjhffep~|zwtromjhec`__chw}xsnjgeeejnkjijq{wsplieb_\[ZZ[\]at|}}xsojfcabekv}~}xsokiiv}zwurpomkhfdcdhn~}}|~{xv|{wtrqppppppqqppmq~|zxtqnlmow~zxw|}{ywuspmjhffhw|yvtqoljgdbabfmzvqmifeefmpmllmy{vrnjfb_][ZZ[[]^hz{wrmiebabgn|~{|~~ytoljkz~|yvtrpnligeccejr}||||ywv}yusqpoonooooonnkt~}{yvspmlnq~|{xwx~|zxvtqnkigeejz}zwtqoljgdb``chr{wrnjfdcdelnllmp~|wsnkgc`]ZYXXYZ[]kx|~~{wrmieb_`ciq||~zy|~ztpljhn|~|zwtqoljhfdbabflx|{zzzzxwyzvrpnmllllllllkjjv}}}|}}||{zywurnlkknt~|z{{|xvwx}{ywuspmkhfdddn|~{xtqoligda__`eky|xtokgdbbcfmomnov~xsnjfb^[YWVVVWYZ`ox|~~{xsojfb`_`dlvz{|xx{~zuqmkis~~|xuroljgeca__afl|}{yyxy~}yxv||wspnmlkkkkkkkjjgkw{|{{{{{{zywuspmlkmpx||~{||xvw{}{ywusqnkigedes}~{xuspmkifdbbdip}yuqmifdccdgmnmopz~xsnjfb^[YWWVWXZ[dry}~}zvrmieb_`bgn{z{|xy|~ytpmjkw~}{xuromjhfca``cho}{yxxy~|yxw~yurpnnmlmllllkkiqy|}|||||{zywvspnlknp}|~}xxx}{ywurpmjhfeiw~}zwuromjhedcejs{wsokheddehnnnorzupkgd`][ZYYZ[]_lv|~{wsojfcaacip}~~z{}zvqnjnz~}zwtromjhecbbdis|zyxz~{xwy~zvtrppoooooonmllv{~}~~~}}|{ywtrommnr~~yyy~|{yvtrolihfnz~|zwuspmkiggjnz}zvsokigffgkqpprv~xsnjfb_][ZZ[\^`dry|yuqmifcbbeks}}y{~|wrolt||zvtqnligdbaadiw}{ywwy|yxv|}yvsqponnnnnnmljmwy~}||||{zzxwurpnlmov}|yy{}{ywuspmkhggrz~{yvtqoljhghkq{xtqmjgfeeflqoqr{|vqlhd`^[ZYYZ[]^fsw~}zvsokgdbbcgmy}{}{y{~yupmlw{~}zwtqnligecaabfl~~{ywvvy{xxw~yurponmllklkkjignvv{yzyyyyxwvtrpnmmoq{|{}zxy~~|zywuspnkigfhuy}{xvsqnljhggimu{xuqnkhedcdflpoqt}xrmiea^[YWWWXY[]irv}}~}|zwsokhdb`adiq~zy{yy{zvrnkmx{~|xurnkhfca_^_bfp~{xvutsxzwwx|wspnmlkjjjjiihgfptvywxwwwvvusrpnlklos}zz|yyz~|zxvtromkhgelwz~|yvtqomkihilq|}~~{xuroligeddegnrrtx{vpkfb^[YXWWXYZ\_mrw}|}|zxtqmifca`bekt~yxzxy{{wsols{{|yvrolifca_^_chu}zxvtssxyww{{wspnmlkkjjjjihgistwzxyxxxwwutrpnlkmov~{{|yy|}{ywuspnkhgfqy{}zxurpmkihimr~}zwtpmjhfeeehoqrt}|vplgc_\ZYYYZ[]_esu|~|yvrnjfdbabfmxzz{yz|xspnw{}~{wtpmjgdb__`cj{~{xvsrsxwww~yvsqonnmmmllkjinwv{|{{zzyyxvuspnlkmpz||}zz}{ywuromjhiuy}~|yvtqnkiijmt~{xurnkigeeejqrtw|vpkfc_]\[[\]^abmwx~|yuqnjfdbbchn~{{{yz~yuqr|||xuqmjgc`^]_ck}zwtrqrvvvx}xurponmmllllkjjrxw|||{{{zyxvurpmkkmq|}|z{}{ywurpmkimxz}zwurolkjlq{|yvspnkigffgkqsuzztniea^\[ZZ[]^`cpvx~}zwsplhecabdjr}yzzy{|wspt|{}zwsolheb`^^`eq~{xurporvvv{zvsqomllkkjjihgisttzyyxxwwvusqomlklou|y|{z|~}{ywurpmjigoxy}zwtromkjkot|~{xvspmkhgeeeflrsv}|vpkfb_\ZXXXYZ\^dprv|||zyvsokhdb``bfmw~zwyyy}|xtpnv{z}zwspliec`^^_cix~{xuspoorvvw|wspnlkjiiihhgfeirqswwvvvuttrqomlkkmqz}zy|{{}|zxvtromjijtyz}zwurpnllnrx~|y}{xvtromkiggfgintuxyrlgc_\ZXXXXY[]_hrrw|{zyvspmjfcb`adip}~ywyyy|xtqqz|{}zvsolheb`^^`dk}}zwtrpnnruuw~yuromlkjiihhgfeelrpswvvvuutsrqonlklor}zy|||}|zxvtromjilwx{}zwurpnmnpt||{~{yvtromkihgfgiotuzyrmhd`][YYYZ[\^bmtsy}|zyvsplifcbabekr~zxzz{|xuqt|{|}{wtpmjfda``aep~{xuronnruuy{wtqonmlkkjjihghqtrvyxwwwvvtsrpnmlmot{|}|}~|zxvtqnlkqyx|~|ywtrponpu}}{xvtromkihhhjptu|ysniea_]\[\\^_afruv|}|zwtqnjgeccdgluzz{z|~yurx}z~|yvrnkgdb``bfs~{wtqommqst|{wtqpnmllkkjiihkttswzxxxwwvutrpomlmpx|}~~}{ywurommvzz}{xusqppsx~||zxvtrpnlkiiikquw~wqlgda^]\\]^_ackuvw|~|{xurolifdccdhn{~yy{z}yut|}{~{xtqmifca``ciz|yuromkmqsv~zvspomlkkjjihggmtrrwxwwvvutsrpnmllnq}}z{~}}|zxuspnlnwyz~{yvsqoopt||{~|zxvtrpnljihghlrvyztnida^\ZZZ[\^_bltsv{|zxvsplifdbabejrzwxz{}yvru|{{|yuqnjgca__aen|yvrpmkjmruyzvspmljiihggfeeemqoquvuutssqpomlkklou}zx{}~~|{ywuspnljpxxz~|yvsqonoqv|y{|zwvsrpnljhgffhmtw}}vpjea^[ZYYYZ\]_cnrruyywurolifca``bflv}yvxz||xurx}{||xtplieb`__bfs~~|yvspmjiilru|}yuqnljihggfeddcfnompttssrrqpomlkjkmpx|yx{~}{ywusqnllsyy||yvsqoopsy|zx|~{yxvtrpomkihgginux|uoiea^[ZYZZ\]^`gqrsvzywurolifca`acgo{}xvyz~{xutz}{}|yuqmieb`_`ciy}zvspljhilru~yuromkjihhggfeejqpprvvuttsrqpnmlkknr}}zz}}{ywuronowzz}~{xurppqt{~|{}{zxvtsqomjihhinty}vqkgc`^]\\]^_admutvx|zxvspmjgdcbbdip}yxz||yuv}}{}}yvrnjgda`adl~~zwtpmjhhlqv{wtqonlkjjiigggosqqtwwvvutsrqonlklns||}{ywtqps{{{|zwtrrsv}{|~|zyxvtrpomkiijpuz{uojfb`^^]^_`bdgrvvwz|zxusomigecbdfkt~zx{}}zwz}}}zvrokgdbabfq|yvrolighmrx}yurpnmkjjihggfhqrpqtwvuutsrqpomllmpw~{|~|zxvsqpv{z|~{xvsrrtx~|z~~}{yxwusqonljiikqv~~xrlhda_]]]]^_achsttuxzxvspmkhfdccehny|xx|}zwv{~||~~{wtplifcbbdiw|yvspljhghns|~zvspnlkihhgfeddhoonnrtsrrqqponllklnr|~|z|}{ywuspoowzy{~~|ywtsrsv|~{yx}}|zxwutrqomljihimsy{tniea^\[[[\]^_bjrrrsvwuspmjhecbbcejq}ywx|~{xuv||{{}}zvsokhecccfm|~}~}}~~|ywtpmjhffiou{wspmkjhgffedcbcinllmqrrqqpoonmlllmpt~{{}~}{ywusqorzzz|~|ywutsux~|ywx~|{yxwvtsrpnmkjjjnt{~xqlgc`]\[[\\]_`dmrqrsvvtqoligecbbdglt}ywz}}{xvy~}|||yvrokhedcdhq~~}~{yvspmjgffjpw{wtqnlkihgfeedcelnmmnqsrqqpoonmlklmpw}{{~}|zxusqpu{z{}~|ywussuy}zy|~|{yxwutrqnlkiijnt}~xrmhda_^]]^^`achqtstuwwtroljgedccehnx}zx{|yw|}|}}zwsolhedceiu}zwtpmifeeipz}xurpnlkjihgffeippooqtutssrqpomlllnqz~||}{ywtrrx|{{~}zwuttv{~|{}|{zxwvtromkiiinu}wrmheb`__``acdfmuuuuvywuromjhfdcdfjp|~zy||zz}}~~{xtplifddflz~|yvrokheddiq}|xurpnlkjihgfefkqpooqttssrqponmlklns~|}~|zwusu{|{||ywuttw~|{{}|{zywvtqomjihjow|vqlgdb`___`abdgoutttuwvspnkhfdccdfkr|yy}}zxz~||~}zvrnjgeddgp~|zwsplifccekt~zvspnljihgfeddelonmmpssrqpoomlkkklpv~|{}}{ywtrqu{zz|}zwussuy~|zxz~|{zyxvusqomkihhkq{xsmhda_^]]]^_`bforqqqsuspmkhfdbbbdhnw}zwy}zxvz~|{{}~{wtplifddfkv~~~~}}~|zwtqmjgdccfmy{wspmkihgfedcbbellkkknppoonmlkjjjkmrz~|{~|zxvtrrw{z{|~|ywuttv{~|yxw{~}{zyxwvusqoljihils~|vpkfc`^]]]]^_`chpqppprsqnkifdcbaceip|}zxz}{yww|~|{|~}{wtpligffhn{}}~~|||~|zxusolhfcbcgo{~zvromkigfedcbabgllkkknppoonmlkkjklos~}||~|{yvtrsy{z{|~{ywutuw~~|zxy~}|{zyxwusqomjihimuzuojfca_^^^_`abelrrqqqssqnljgedccdgls}zy||zxz}||~{xtqmjhfgiq~~~~|ywtpmifdbcgp~~zwsqnlkihgfeddekonmmnqrrqqponmlllmpu}~~|zwutw||{|}}zxvuvy~|z|~}|{zywuspnkihimvzuplhecaaaaabcehptssrsttromjhfeddfhmv~{z~}{y|~}|||yvrolihilu~}{yvspliecbcgr}zvsqomkjihgfedgmonmmoqrrrqponmmllnqx~}{ywvz~}|}~}zxvuvz}{{~~}|{zxvtqnkihinx}xsnkgecbaabccegksttsrsttqnljhfedegjpz~{{~|zz~~}|}~{xuqnkiijny~~}zxurokhebbdjv{xurpmkjihgfeddhnnmllnqrqqpoonmllmos|~}{ywvv{}|||~~{ywvvx}}{zz~}|{zyxvtromjihjq}{vqlheca```aabcekrrqppqrqnljgfdddegls|z|}{yxy~~|{{|~|yvroljiikr~~}~~}|}~|zxurolhecbbem{}zvspmkihgedcbbchlkjjjlooonmmlkkkkmpv}}~}{ywutv{{z{{~~|zwvvwz}{yxy}||{zywvtromkihilt~xsnjfca`___`aacfmqponnopnljgedcbcehmv~{{~~}{yxwz~}|{{}~|yvrolkjknv~}||~}|{{}}|zxvspmjfdbabfp~{xtqnljhfedcbaacikjiijlnonmmlkkkklnry~}{ywutx|{{{|~{ywuuw{~|zyx{~}}|{yxvtromjihilv}wrnifdb````aabdhoqponnoonkigedccdfipy~{{~}{yxx|~}|{{}~{xuroljjkpz~}~~}||~|zxusplifca`bgr|xuromkihfedcbcfklkjjkmooonmmlkkklns|~~|zxvv{}|{{|~{ywvuw|~|{{~~}{zxuspmjhghmx}xtokhedcbbbcdeglrsrqppqqomjhfedcegkq}~||}{yz~}|{{}~|yvrolkjlr}~~|zwtqmifc``bhu}yvspnlkihgfedeinnmllmoqqpoonmlllmot~{yxy~~}||}~{ywuuw~}|}~}{ywspmjgfhnz{wrnkhfeddddeegiotsrqppqqoljhfeddfhmt~}~~|zy{}|{|~~|yvspnllov~~|zxurokhda`_bjx~{xuromljhgfeddejnmlklloppoonmllklmqw~|zxwz~}|{{}}zxvvvz~||~~}{zwurokiggiq~}yuqmigedccccdefipsrqponoomkhgeddefiox}|~}|zyx|~|{{|~}zxtqomlmqz~~~}|~|zxvspmifca``dn||yvrpmkihfedcbbejljjijkmnnmmllkkklos{~|zxvvz}|{z{|~{ywvvx|}{zz~~}|{ywuspmjhghlu{wrnjgdcbaaabbceiopponmmmmkigedddehlr}}}~}|{zxwx|~|{zz{}~|zwtrpnnpu}}|}~}|{{}{zxuspmjgdb``bgr~}yvspmkigfecbaaaejjihhhjlmmllkkkklmpu}{ywvw{}{{z{}}zxwvvy~~}~~|zz{~}|{ywuromjhghmw}ytplifdcbbbbbcdfkpponllllljhfeddefinu~~}}|zyxwy}~|{zz|~~|ywtrpooqw~}}~~}|{}}|zxvsqmjgdb``biu|yvspmkihfedcbacgkjiiiikmnmmllkkklnrw~|zxxz}}|{{{}}{ywwwz~}|{~~}|zxvspmjhginy|xtpmjgeddccddefiorqponmmnmkigfeefgjpx~~}|{yy|}|{{|~~|zwtrpopsz~~}{ywtqnkheb``cjw~|ywtqomkihgedddfkmlkkjkmooonnmmllmosy}{yy|~|{{|}}{ywwx{}~~}{yvspliggip{~{xtqnkigffffffghlqsrqonmmnmkigffefhkqz~}|zyz}~|{{{}~|zxusqpqu~~~|{yvspmjfca_`cmx~{yvsqomkihgedddglmlkkkkmooonnmlllmpt}}{zz}}|{{|~~|zxvvx}~~~}{xurnkhffir|}zwspmjhgfeeeffgimrrqpommmmljhgfefgimt~~}|zyxz~}|{z{|~}{yvtrpprx~~}~}{ywtrokheb`_afp{}{xurpmkihfedcbchkkjjiijlmmmmlkkklnqw~|zyxy}}{zzz{}~|zxwvwy~~~||~~|zxuroligfglv~|yuqnkhfeddcdddehmppomlkjjjigfeddegjow~}|{zyxwwy||{zyyz|~~}|zwusqqqt{~}}}~~}||~~|zywtrolifc`_`bit}~|yvspnkigfdcbaacgjihhgghjllkkjjjklosz}{ywwy|{zyyyz|~}{ywvvwz~}}~~}||~}{ywtqnjhffhnx~~{wspljgfedccdddehmonmljiiiihfedddehkqz~}|{zyxvvvy|{zyyyz|~~}|zxusrrsw~}|}~~}||}{yxuspnkgda___ckv}}{xuromjhgedcaaadhiihhggikllkkjjjkmpt|}{yxx{}{zyyyz|~|{ywvvx}~}~~}}}}{ywtpmjgffipy~~}zvspmjhgfeeeeeegkoponlkjijihgeedefimt~~}|{zywwx{}|zyyyz|~~}|zxutrrty~~~~~}{yvtqnkheb`_`dmw}~}{xvspnljhgedcccgkkjjiiijlmmmllkklnqv~|zyz}~|{zyz{}~}{ywvwy~}{xuqnjgffiqy}}|yvspnkihggfggghjnrrqonlkkkkihgfffhjov~}|{zyy{~~}{zzz{}~~~}{yvuttv|}{ywtrokheb`_`eox|}}{ywurpnljigfedceillkjjjjlmnnnmmllmorx~}{z|~|{zyz{}~}|zxwvwz}zxtqmigefjsy||zxuromkjihhggghikprqpomlkkkjihgffgilqy~}|{zyxx{}}|zzyz{}~~}|zxvutux~|zxvspmjgda`_bhry|}|{xvtqomkihfedccfjkkjiiijkmmmmllllnpt{}{zz|~}|zyyy{}~~~}{yxwwx|~~}{yvsoligfhmuz|{yvspnkjhgffffffhkoppnmkjiiiihgfffgjms|}|{zyxwvw{}|zyyyyz|~}|{ywvuuvz~}}~~}}}}|zxvtqolifca`adltz||{yvtqomkigfdcbabeiiihhghijlllllllmorw}{yyy|}|zyyyy{}~~}|zyxwwy~~}}~~~}}{ywtqnkhggjpw{{zwtqoljigffeeeefgknonlkihghhgffeefhkpv~}{zyxwvuuwz{zyyxxyz|}}|{yxvvvy~~}}}}}|}~|zxvtroljgdb``afnvz|{zxuspnljhfecbaabfhihgggghikkkkkklmosy~|zyxz||{yxxxxz|}}|{yxwvwz~}}~~~}{yvspligffjqwzzywtqoljiggffffffimoonlkihhhhgffefgilqy}|{zxwvvvx{{zyxxxyz|}}|zywvvwz~~~~~~~~|{ywurolifda``bgpvz{{zxusqnljigedcbbdhjiihhhhiklllllllnpt{}|zz|~}{zyxxyz|}}|{yxwvx{|zwsplifefkrwyzywtromljiihhhhhikoqpomljiiiihgfffhjms{}|{zyxwx{}|{yyxxyz|}|{zywvvw{~|zxuspmjgda``biqvz{{zxvtrpnljhgfdddgjkkjiiiijlmmmlllmnqu}~|{{}~}{yxwwxz{||{yxwvvx||yvrokhedflrvxyxvtrpnmkjjiiiiijmqqpomljiiiihggfghkou~~}|zyxwwx{|{zyxxxy{|}|{zywwwy~}{ywtrolheb`_`cjrwy{zywusqomkihfeddehjkjjiiiijlmmllllmorw}{z{}}|zywwwxy{{{zyxvvwy~}{xtqmigeehntwyywusqomlkjihhhhhjnppomljihhhhggffgilqx~|{zywvvvxz{zyxwwwxz|||{yxwwx{~~~}{ywtrolifda__afmtxzzzxvtromkigfdcbbdgiihhgggghjkkkkklmptz~|{yyz||zxwvvvwyz{zyxwwwx{~~~~}{xurokhfefjquwxxvtromkjihggfffgimonmkjhgfffffeefhjns{~|{yxwvutuwzzyxwvvwxz{|{{zyxyz~~}}}}~}{ywuspnkheca``bhouxyyxvtrpmkigfdcbaacghhggfffghjkkkklmorv}}{zyy{|{yxwvvvwyzzzyxwvwx|~}~~~|zwtqnjhfeglruwwvuspnmkjihgggfghjnnmlkihgffggfffgikov~}|zyxwuttuxzyxwwvwwyz||{{zyyy{~}~~~}{ywuspmkheca`adjquwxxwvtromkihfecbbcehihhgggghijkklllnpsx~|zzz|}{zxwvvvwyzzyxwwwwy~}zwtqnjhfehmruvwvtsqomlkjiihhhhjmoonmkjihgghhggghjmqw}|zyxwvvxz{zyxwwwxy{||{zzyyy|~|zxvtqolifca`aekquwxxwvtrpnljigfeddehjjjiiihiiklmmmmmoqtz}|{|~~|zyxwvwxyzzyxwwvwz~{xuqnjhffhnrtuuusrqonmlkjjjiijloqponlkihhhiihhhiknsz~}{zyxwvwy{{zyxxwwxy{|||{zyyz}~|zxvsqnkhecaabfmrtvwwvusqonljigfeddfikkjiiiiijklmmmmnorv|~|{{}~}{yxwvvvwxyyxwwvvx{}zwspligefjortuutsrponmlkjjiiijloponmkjihghhhhhhjlpu}}|zyxwvvwy{zyxwwwwxyz{{{zzyz|}{ywtroligdbaachosuvwwvtrpomkihfedcdfijiihhhhhijklllmnpsw~|{zz|}{zxwvuuuwxyxxwwvwy}}zxtqnkhfeglqstuutrqonlkjiihhhhiknnmlkihgfffgggghjmqw}|zywvuttvxyyxwvvvvwyz{{{zzz{}~}}~~~}{ywurpmkhecaabekptuwwvusqomkigfdcbbcehhhggfffghijkllmnqtz~|{yxy{{yxvuttttvwwwwvvvxz~{xvrolifeehmqstutsrpomlkjiihhghilnmlkihfeeeefffgiknsz~|zywvutssuxxxwvuuuvwxz{{{zz{|~~~~~|zxvtqolifdba`bflqtuvwvtrqomkigfdcbbcfhhggffffghijkklmoqu|~|zyyz{{ywvutsstuwwwwvvwx{}{xurnkhfefinqstttsrqonmlkjiihhikmnnlkihgfeeffgghilou|}{zxwvutuwyyxwvuuuvwxzz{zzz{}}{ywuroljgdbaacgmqsuvvvtsqomljhgedddfhjiihhhghhijkllmnorw~}|z{|}{zxvutsttuvwwvvvwy}~|yurnkhfefjnqrsttsrqponmllkjjjkmoponljihgffggghijmqv~~|{yxwvvwyzzxwvvuuvwxzzzzzz{~~|ywuromjgdbaadhnqstuvutrqomljigfedegjjjiihhhhijkllmmnpsx}|{{}}{ywvutsstuvvvuuuwy~~{xuqmjgeegkoqrssssrqponmmlkkjjlnppomljihgfgghhhiknrx}{zxwvuuwyzyxwvvuuvwyz{{zz{|}{yvtqolifdbabejoqstuutsqpnlkihfeddehjjiihhhhhijklmmnoru{~|{z{||zywutssstuvvvvvvx{|yvspligefhlpqrsssrqponmlkjjiiikmonmljigfeefgghijlpu{}{yxvutttvxxwwvuuuuvwyz{{{{|~~~~~|zxvtromjhecbbdgmprstttsqpnlkigfdcccdghhhgggfgghijllmnpsw}~|zyyz{zywvtsssstuvvvvvwy}}zwtqnkhgfgjnqrrrrrqponmlkjiihhijmmmkjigfeeeffghiknrw~~|zxwutsstvxwwvuutuuvwyz{{{|}~~~~|zxvsqnligecbceinqrsstsrpomkjhfedcbceghhgggffgghiklmnoquy}{zyy{{zxwutssrstuvvvvvwz~|zwtqmjhffgkopqqrrqppnnmlkjjiiijlmnmkjigfeeefghijlosy~|zxwvtttuwxxwvuuuuuvwyz{{{|~~|zxvsqnligecbcfjnqrssssrpomljigfddcdfhiihhgggghijklmnpru{~|{z{||zywutsrrsttuuuuvxz}zwtpmjhffhlopqqrrrqpoonmllkjjjlnoonlkjhgfffghiikmpt{}{yxwvuuwyyxwvvuuuuvwyzzz{|}}{yvtroligecbcfkopqrsssrqonlkihgfeefhjjjiihhhhijklmnopsw|~|{{|||zxvusrrrrstttuuvw{|zvspligefhlnppqqrrqpponnmllkkkmooonlkjhggfgghijlnqv}}{yxvuuvwyyxwvutttuvwxyzz{|~|zxvsqnkhfdcbdhlopqrssrqpomljihfeeegijjiihhhhhijklmnoqtx~}{zz{|{ywutsrqqrssttuuvy}~{xuqnkhfefjmoppqrrrqpponmllkkjkmnonlkjhgfeefghijlnsx}{yxvuttuwxwvuutssttuwxyz{|}}{xvtqoligecbceimpqrrssrqonlkigfedcdfhihhgggggghijlmnoruz}{yyyzzywutrqqpqqrttuuvx{ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m37.bin b/applications/SprPoP/assets/packed/MUS/m37.bin new file mode 100644 index 0000000..dcce0f0 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m37.bin @@ -0,0 +1 @@ +ysgx{lw}twz]{~v|mxzzp|x{spror[uqt{}|uwvo}tlm}fzmm|}lxwjxs\rrcXvkt]ZhyqUrzl|yp}gzz~g_~{t~||rtruqzu~yTe{nwrnywmp||pautjtzh{dp{zsc_icr{|}o~}uv{f^||z{i_NOroo~s}wokSRry|||{yv~}|ju~ur`bbngiv}nwdgz~}i[Zh~wski`kflvwxz|qu_mhluux~uew{|jnhkmwvtb`a|zi|wek}vmo}tvm{z~{rebtfspn\Q_dhqqcgl`owyu{~}|ol|{vw|znrvZ_]mxjvz{uvhZ`lnjwWealpXVi}uvw~}x|uactxwyeduOYkutwmquilgbgrarzgmqz{hxw\YVX]cx}ecv{v|~zxxvid]oyz~goxkqkxux~{qhzskvufTV{dOagiiwvsjryy|rzvbo|vqb|~xyxyshfjfh|z}jyyt~sffop`jtjhkr`sbZhx~z|w~~t}uyyyyl`it}opay`Yirqmiaonejrs]|zvVerr\pxpb_WXXexoht|r{sp}qqji|}uyulw~z{zmt~yu|r|tv|hqo_ul_bi[jstqzzvqxzysy}{{eblmf}s{ospomgkfupv|u|vup~}tanfvredyqYbowy|rz|uytsz|{}}sfq{zmcp|otvurljtiovqlnmzoRk{{nrs^svn`^_r{{}~{zy|aisyotlmvqepd~tu}v~xsrxntzvtqdf\]seTdsx|}|phu{{z~xsso^es~txkiemlvw}wnltr|~}d`rhsrmazzihjno~{zyrm|rx|w_lgwylywow^vve{{tr{wteql~silggloor~q{{}st}qujjageetxtg~sqsrp~~~rhdmzp[ht~`plbpoomrks{prxz|xo|{wlr{~tqvl~}yxx|r|dzvxu{}ttx}wkvw]tZttbgsxxv~rxp~rwpinpwvx|p{xwuqfsx}r}exxkikw|giwpypudmskhssqyowz~~y{uwdoopvrz{ajm{y|f~z|wxxrzu~|ksp}qgmc]iw|xx{lvzz||n}vzqxiegu|mszspvmn}~yyylpb|~|n`cjek|ltwsptxxv~wsmqn~vty~wuqwv~x^nrgvvssvw}wwovmtebpkdpxz~zv}wrzwt{jdmilrtlttt|vtu}~wkmmq|z|xbqvwuh{gcw|xrp~trhuywx{z}vvpxrvlrkv}yxczpv}m}xqsw{vho{ptfvrcdu~|~z}pvwzxyjmkmpx~ymzumx||pmmkv}`cvprd~|[tpiu{yypzvpp{~|}t|~{jyrimtu|sq{zzn|wpzuzx~gr{yqhk|ifjdiwz}|y|{wnu{zrjglw{otlh|zupwmqmts{z|pblp~y^elz}~svyywvi{}ow~uttxysxs|ztltpvtwlh|~xz|w|unuyj}}_dqkgt|}zto{{}hkkllkio~}v}~rykydoup}rs}mtfqw_gwxvzxpiz{|||yzxv}vsyzkzlx{u|zstvy~|njtv~{~|obosv|tstlhvxu}pvu{pmlmt|yxxj|~~xvx}pkwixpp|~uokjvQr{`iz|t~xws~z{syu~~r}tktuvy{z{y|~yzr}|o~khx{kdty}|zuovuozhjnefs~}zqz~xqoqqnhrvfuvwoylw{n~r~~}{nkxutmnp_t}{vtvz}olu~ty~yyvyszuxvv~}psx}sn|kn}}~uw|{~zysjpvrtmflsniov~~iwz{|{xvqplfiu|{z|vurwoypo|~tl|~y}~pvu]}tYev}|}lssxvz~z}}{||y}{su{~s{zvz|urpv{imxv~ytoin~zmxyuvsq~~z}jrz}}utvyytxmq}||zr|kokd}rvzuotste\w[enz~y~nw{~s|qu~rvwyuqwtu~yz}xo}ngy~umhq{y|}z}}{z|wonomhu~}v{~~{y~z}qukhdj^Kow|||mp{y~|vfnt`oxtgcwgystiomf_lflO^_ekrz^xn{tw{qlv`iti\lztjV]efSjabkstv~}rfqlz}hpu^x}mgm^bu^Rmvj}spcp`zhtub{skszgl]dyg~yqZetjy~tmpk|uY~zfnffNap`dxliz|}zy}gij{ifodkegppeuu[nYjzrprhmahplev{id}yivwcs~v|}ao~xYuwrrmd[jxvppulXye~w{|}z|{\s}XtaPgnujcrqmh_nvs{{xv|{kt}hbks[~g`nlfdijh}xoysz{wx~wrwqiquvysnsru~u~qssk}lzk_ktzbYucThg\fuVmzotzzjk]S\]UYswv||vw}cctxxrz~wl~qtw~oixtoshamugszwrxweppZetolhp{[o~jk~vajwx{swvZ^emkk]gwbcubtS_~}uu{jhqacknkqxflnd{|o}~xjvdu{qstb\{U`reYiwdr|ztsyqz~b_ja`o~{sryr\jsjuv{uvulsmplu~jqgfx}bgx[iyyo~{yahngba^Tb{spyeau}lcy{x{ywbnvyy}ktqlylrdUu~pz}nq}q~yutojxzrxz{}~spm{p^vrpyrpzj_`RTw{pgYYQroXh{f}}|zmvtuycnqrfjtqmtpnkcaUkyyxkwx|z{|o~~tzz]}apXTbdmwqfs{zQsx~|xjvy|_uuovitzypo`uuj{g_ekZnu{xxwl~vmzzmSaxpm|Xpws|piq[u|xgmhjvovdhOJevxto\pwuefztw{{xm{u||nesrh{~t~dflXf{{}~||\Yokyrp~tmumbrwu{e[|p~~~|}t{r}vvz}pn~e}vweWdk}{ndTZpwxh|mnytw^Hlwrw|rmwSRdkbbwvienzv}|{n}}lsxk}kkwsifhdnkpiR}swtrurcmb_rlhlzrw|ukluz}zw{xwmytawzvZq}YMTW[[[[wdYeyugkxvkw}zh`_[MXtw{ttssn|ykvy~xm^ftjhlikhg|qhs|tuoxjmxh~{g}{tyt^kkmmwuumo|ytzwn}}zqVtbtsm`ROMLUjraasz|ehyyz}fphe`}[YddYa~|zwuxv{{|z~Wdn^bki_ed`qfy[n}}qsshuswrq}~uoeedr{upx~q{|rwmXnw{m]skXSSTP^tpy~}}le~yznu}zmp]Zada\kx{qs~{xo{ss~tnfoqmcWUijvjmt{{sh~mdrus|p|gs}{ypjkoqryx{ze}x~odwcXwud\VSTVQp{khh}usyrsmznwq]Z^ajlzuzyumvz|uum^swnnlcZkuxsqzqyzmzzz|yz~g}hxpqqt|vy}vz|zzxkxyo]syml{ygVOO`dy|wsv{y}egxn|nuoukmokz|xuzs|slvw~xxkogjrupibf{ulfox{xyzzsvsp|y}tqv|{}||~z|u~ovxjpok{fgsiaZZs|lr|z{|hn|t~gjzzsyuiqr}zy~w~rv~lyx|z{{ghvxtpimqypqpw~yostyq~uud}n}|||wz{}y||r~evxw}}v}urnuzvmi_bw|uykiz~|zs}~yuwvxq~|stvot~|}~muxqz|ysuq~uwpwtuyzrquxsrytsu{~zy}y{z{{y~|zy~}x~qure~yys~}dq}vnnplyzzvp~zy~tns~zy|yvruxyvyy{rvxwtp|ouzt}ysgo{zly}yo~rq||}~u|}zuovlyw{||}~z}{|~xzlm{sz|}vwvwzytmz|xyyy{~zzxpu~}zw|zuytzw~stxyyzpozpsqwprvw|wt{xtj~{|q{ws}vxx~sz{vxw}}u~xwy|}w~zzuyr}m{uwjs~ukww{wsky|~zw{xz}rwt{q{~{}yz_|{smz|sw~hkidpSm~eYq|vsdzsvmnn_}}c_u}{wif{n}pkq}f]eyxnbzym|zxbkfmxfptl{jshb}oj~{zvpyzyor}_zlescolt|hsw}uh|z||~rkttsp\lohwxxzo~|lw|hbdcbz}vxms}uovnVaw{||\Vi|wpgUk|{xz|~{ruxv}qotwsdshzmllm~sb||[oxzpsytkovysxvznouvrrqvif}tulmq~yo|w{fqtvqhpg{svkmra\sswuhzc`mn}ob`d`ar}~{qsmspdvs\e{o~rwxux|nvcgavuhmcpwb|ftknpVtvbvkptpaUZnx{qipxatlrxj^n|}~xwttvtt\o{ikyf_{tsz~|}s{cdbdmlaj}zl}x|o|ih]Ufa]ontu\jx{z~rzhe}dqlYYkqhwyxt}rv~U^zotcskfq~v{s}etipmduau{trm~yp}|mewekko~wro^Rou|wz{spUtqgz\^unj}{phuggmPYufzuv~~r~~gg{sizplf{qnf~vmsjguaiskuq^ct}{sx{xrlmyblqlmdv{tz{soxceygnsky~jizjsu`luixtcow~~|lxm{sZgs]Wmwh}|esy|fws|ruimjfvmdt}y|swuxyswYcsrlgqww}{onlrpdokm~jw~{s~ytfvpgl{}}zpeizxtynp~iuuxqyfr|bylpxy~z{jb_Vek~xtqtr{vqg{wigwmr{uosviw}s}oqycfwx{x}emz|l|q~nriovfsrslvwuvwjplw}ql{x{msl~v|lgy{lp\ve~~~f|x{on`~uT[ph}{uk|o~b{my}|{fwu|ztwqmzoxwyz||qsrfks}w^n~y|~|{zzrktmviq_}ro|}|rzx{rv}zfh~u~wx~vwmmnzuuziywu}iewuptwt|u~vu_tq^h`wtmxpy~{nwsn|xzpl{zwzaz{ko}rnyu_nz~xt~}d{qhyxxxwytnipydu}~|v{|zyx}s}qqxih{||wrt}nfzx~naqhrz|p}x|uvujze[pmzwy||l|kl||}ury~yf}}w{{}l~ywx|}z{|ju{nrsrv~z\{yxy}{t~~qptrp~tug|}pzwsq{zym}{kk~qx}u|tyg~{zqmulwyd]zulx}|yyvyfs~rhqi~~t{|n{w|yzlryvtytlwxdxwq|{}rtrfp{qnxzg}yotxt{njwklsZuz{|}}}~s}~q~}vsvyem|qyqw~gqruzekjorr}y|xqwvmx}kku{s~w~wjy{l~hs{tl{w~^wx|pp{~z~u}ufmnrvma{tc~m~y~m~knxmsmzwwpvwt||t|nxxyrpyr||y{mnxzxcqteuup[qdw}|yuvxtvsnswvxrz|htzww~iu~ypnr}uyrh~|~sopswuctw{k}tn~r}ci{fvu|_~|z|{zx}uz|mlny|ly}zwuq|w}|fvspgoqpjvnq{||qi{zsyxxp|{|ytk~rz{hvy}skutbvxiu}}xd`o|ze^xokunlt~wxslzsybvuvmt}pq~}yjvywqw~uzw~sqqywfgzmrtyvu^yn]xx|~lyuhs~~b~rs~w~juyyxj|}zxr{nd{{iw}x~yspm{wdmu~huv~w{so|usj{rttrvqpu{zsw}txtobpt|wwxsrss}~lpyt|vnn|Ydxnpx|zzx|pv}g~ywyx{p||nzxixsv}}|rrn||yslzzwh]l}y`dmlqm}~nyi~~|pr}izuoyvt}mlsvy~co{mqu|q}~x{wzwutdoqvospyplm_jw~wyg~pi}ghqwyp~osp~{nt|xwx~yyzqqlqo~mzzrimppcqnrgux{yyq~}x~uw}{zys}}iqq~{oty~~|o|vbh{ysr~|nqs}~}|omrx{{ukbTtzspzw}sprww}|huzx~}w|zqqumxhsuqr}xqvpsc~xvszjne`n{l_igzmusy}{j}woz}wyzgy}xijowr`ozqxru}ty}~~o{zwtvienxmknxuuabnu|yy{gtyn|cm}y{qlwwm}r{||qyzytzw~|yqtvot~m|vqagp|qgqmpiw}x|yrv{xzmmywt{mot}y|xn~{igwmvznp|}y{|}|omlzzzwpcZpzq|}uxuxxiwz|gzzx{yvy|knrtiiuroqrrzsu}c|zum_jihpscjevmy||yxq}wzrz|vom}wjehr|rzfhszvsu~uxz{xpt{wv}xpfkzrhnvw^^uu}~}wxzooy}avuhfq}}mqvzryr~ywr}zx~sjkxx}}vw}qj_hu}osmx{fq{|~ye}xz}pv~up{{rkou|v{vqw|yynq~tstul}typmju}v~vhcexuv|sp|xko~~mu{svzyx~pjzy{_fuqmmtr~~x~mu}skWetomiileyno|yszw}~}swy}}spsjfdjq}srgjq|qq|t}w|ftxy}z~}uoktxpisoyUgwuv~~xz{{xvshtvpsafsnjoxzi{~wrv~v~rudqvu{qbgenwzu{qt{dmu}w}|vqwsssmrq}uqnsv|wwyyuvypq{zyqpz}ytwzvvumkpz|zyofngr~|{vptyopu}w}ot||y{rpjyZhqojpywr~yr~x||lZ^vxnbnkjvao}zuzzv{qyz}w{ubceimuz}znlmkt~}yqz{skw|s|~|xurrttvnqkhalztyzz}yw||vkq|mx|~i`gujnyq|jn|~vv{{{rhuzzy|mZekovttp}rary{wpw|opot~zqvnqqstvryw}q{{}ukzywnv~vx{sou{|qlipw{~}yugtpkw~xrtt~qwwv}ytov}{zuyhoidlroq|}wzqttsh[`v{kdphuk`v~xyvrvq|~}}|r^_eilqsjqrosw{w}yw{ryttyxqqquwstssjsljzz|~|yv}y|euzmvvxjbdr}qlrx{ho{}|}~xns}}}}rX_nosqs~vpfs|||u}zmnpy{|v{}pinrsttozwp}|mwr}lwuyslvzzxkiiqvx{~|rnyyouzxty{wv}t~zzxxrpv}|xw{fuznmntsytsowq{vkf_dw{hkjst`l}~v}qs~{sv}~}dZaiknkyppxur{uu}|yy|vmzyw~uijpw|wsvwkzwymt~}z~w}zdywpttqiffp}yoiwfqx~~yuzww~r|nZ`strlvx~gmv}|sslsxz~uyqeeowvvmtx|ouxotxqt{wm|z}wzzjdhqvxv|vow}xpxtv{}|z|vp~zxqprv|ww{ptkrtnqu|}}upxypphjifsypli{i`lz}vqyzvz}}{yte]emnkjy~~ntxwtqrxz|ytzmz{yiagu~xr{qyvo~}or|x{}|otysvqyxhiklt~hr~hkpw~{|oo~|l|~vm_fwxlop|kkqvzoyzpqxzyu|zf`et{zposxqnwzwtyt}~|~q~}w|occnwzvv}|puy~zu~tqzyv{|j|y}vjnv{uyv}q^syrpx~z~u~{xuzpv}~nglrlmrxnlzdclu~~zz}y{zplkcdlqkhry~tvw{x|}o|t~z}svqw|k`_m}uyv}whxpwwxw|~zx|qxru|pgkqqxy}nwnmorv|w}mm~vi{trphgwxlpl~lpsuz|pywqsyvx{|~xf_bo{|pqpynqv~~vxqv~z{pdakvzws|}}qwy~{}sxtxulu~|{|~yjhq|~xxv}adw}uvwy}{vuyy||n}}uigpvqjo{oxpcjpvs}{}uicihhmrilo{yz{{~t{}w|z~s|qpyi^^k|y{wwixxvvuw|}|q~qwruzqhluuvuyvirttu~|w}qkzrjxoospirwpplypuvvz|wytrtx~v|zxwtg_bly}qtpzotx~t{~tsy}|~ncbku{uuy~yt{|~x||wwvm{u{{}qghs~|wzu{chxz||vzzps}~}o{}y{ymhkvzrdtxzxfkotxp|||}~wyr]alnmrllot{{~{x}|{uwmyvi_^iw}~xyjwzwt}y}}uiuvswz}rloxzvsrcuxvt}zw~}unyiv}|rinwxnorwntynuwxv}~yrux|w}{mjfchoyuusuprx{~~wyyy}vp{{pgekrzuvxz{t{}}|w~|y|x{trw{~}phhq}zxzvxkip}zuxro~rwy}|tkjox~sfnvpfoqswt~}uztm[_pusnkpm}yv|vy{vvr{zkc`eoz|znqy~}vw|xfv}yrxz~urrv||px|cgv{vw}xz~zu{ou}u{jis~}qguvtxfnxyvx|vu{~{sbgjklqqvvs}mmvyz~~yu{|}uvo~~unkhkottsyu}ty{}}xq{v|~~yp}tvv|skimt}w{v~qrmmxwtywp}w{vzx~rkmsyumhsnhossw~}ru}w}sb[iwxnlrn{rq~~twwv~}zundbfmtzzruv}{~|z~xunz|uv{zusuy{wtxxgix|y{|zzzyn~ww|vjitsetu}rbnx{x|}yw}~v_alqoojuvw|imw{|~|v}xu~ovsgfimqsqrzvzsz|~~qsxxh|wunx|qjilsxyzx|ruwqp|{wxxsvszxuvwnkqv{|urg{|lnswv|}yptt}{~{pe^hw{nprt}lqyv~vu}w~~~~y{ncciotvysz{xz}w~}uuvvvv{|tprv}~xywzolv}}|~xuovxv|uljtxhpwrclw}|~~~{y|z}xa]kutogsv}|hmw~~}}|v{}yttpbahortmryzts}ur|zzf{rlvwifipvz{x}x{pz|vrzz~zvyi}xwpxvpmnuy|wzpqzprtxz|tpwrx|~zyqleeo{spsumlzyyz|wtyx~{|{{|pddlqurut|yu~}zx}{vtrzxz}ynkqy~x}xywptyy}}~{{oz~wxs}zpmoyunl~jhmx}~wy}}v}p``qyvhjqzkiq{vvw|~wzvc\dpuvlnvzsoz|u|~zlz|uns~ufbhrx{uw}zuny}uz~y|{{rh}{tpx{olnsx{vv}uylkuwy|wtwwmzx}qmnmkptrurtgp{u|uv}w|x{tpjejrvrmy~uxutr{{~wrrt}~|ofiuw}|w{zttyrwx|}uyz{swrorw~}ynqqllov~}x{{qxtxxmcgu}pikqphls{|tzw{{}}~m`]htwpjqtylr{|~|{y|wzutu~}ncajv{ypyzmqyzy|z}}no{y|rsxwkjpvz{rv|}}dftzz}}}yx|tmzw{ojousnlpwtuiov}ww{s}w}thhilswultxyz}uxt}}|w~}uluuhelz|z~xwwuvqw|z|xs}|xsy{qpw||~rwwoqppt~zvz|pup|~vqkir{rokp}mjnsyu}|y{|~|{qe_epwqjpp{nrx~~~}s{xxvy}}reaeqz{qtyrov~{~}~~|mxtywquz}phkt{|wqz|xbiw}}~|y|~|z|mt~|wx~vkjqyxpfpwyqmry|~~zwxz~|rzv|udbiouyunuwz~{zw{||sxjp}shelz}~xy~xzswz}~{~}or{|wsxzrrz}|y{qovvtt}{vzqr}n|ztsqnpwstnsylmquxz{{z|~xuohcgovomprwquz|}zsuw{zyyvlcbhs{xqwxpsy{z}{sxvqvqsv|tjhny}zqvymfn{|~zu|{op{w~xw|xlhny~xhiuzsosx}}yzvwy{}{s{v{ze^fqvyupvv{~|~x~|~~rzkoz|qhgnz}w{|~|wyw}|ygx{|uw{vsvyzhpyzvv~~xw}~}st~sw~ssvwrqpwuswjlqtv{~~|{}~|okkijmqnnpq}spvz~v}xpt~}wpiccjqyuswwzpty}txyvvrpqvy}|ypjjqz~vqwyokmx{yt}xn{wxyw{qijr}ugkqymswz~y}tty|~swzq`^jvzvputw}u{{{~r~vps~~sjhlt}{zy~}}{~x{~lo}|~vx}~wuw~z|lfr}|yy|}xvy~xxxw}vqu}}ujoyx|idmsuw|}|vfflqqokjqozrksx|~|r{yluthccgnsupvu~rsu{~|qu~}o{ytkpw|}yrljlsz}tsuzztpos|~|~tx~ro}vwtu|}qijr{znll~xmrx|}}xqu|~t~xz~yi`cqzwovu}sy|zw}|w{wx~yuw~wlikrx}z}z}z{{{znww|zuz~wtu{zlir~~{zyww|}z~t||{vrvyjnw}jemtwx|~~|}jcjtwtkhrq{tjqy}~}w|rpz{mcdiostovx{utw{}ztysp{smt{~|umkmrw{yuyvwxvtv~~wzylv|vxsx~yqlnu|yvqq~tqu{~zsu{yu~~}z|xnhiqxvrzx~syz}{}zvyv}}~}{z|}vnlpvyzw~|~zy~}{~~}u{tzyw|{utv}~tps|}zz{~~s{~z~wtw~{psvplpvz||z{~{}nglvzwnotutnt{|}}}}wv|zmgiosurr{yuxz}}y}~~o{}}ssy~zqmnsxzzw|y~yz{zwz|z~}ou}vzvw~ztpquz}z}yt}vsvz~|ww{vv}~~|ysooqvuv|y}tx~y~~ywz~x~}|ztootxyxx|}{wz}|y{u{y{wsty~||wuw|}z|~}~u|{{}ywx|~wxvyyrqty|{xz~yvnmtyystvuwqsy}}{||~|}|rklquurv{y}vx{}}~~|v{~yy|yqnqvzzwzyxz||z|ys~yz{x{~wstvxzy|x}zruw{~~|{|yu~~zvuutttv|xwvyz~{xz~y|{vrqtxyxw~}~{}}x{|}~{x{z|ztsw|~~|zyxy}|{}|}w{{~{xy|}~{}{v}vuuwz~xy}z{{urswyxxyt}|ttvz~y}~|~}zsoptvuu{x}zxy|~|{y~}~~yrpswzzx~}{|xz}~}~yyx{}{}xuuxyzy}z|tuwz}|}~zv~}{wwxxvuv}x}zwx|~}|yz}z|ytrtwyyx}}|||~{{~~}}}~{}|~yuux|}}}}|{{{~~}z~}{v{zxz|}}||y|yxxx{~zy}z|~|xvuvxy|}u}~wvwy}{|}|}||wtsuvww|zz~zy{~}~~}~z{|wssvyzz}z{z{~|~y}w}~}}xvwyzz{||wwy{~}{|~xy|}zxxyyxvy~x{yz|~|~}zz{~z|zutuxyzz||}}~~~}{~~}}{~}}~}~yvvy{}}}~}|}}z~}|~zw~z{yz|}|~|||zzz{}~~~zz}y~|{yxxxxz}x~yxy{~|}~||~~~~zwvvvwx{~x}{{}{}|z~zvtuxz{}|~~{{}}~~yx|~zxxyz{{|yxy|~}z{~~x{{}{xxz{zy|~x|{{|{~|zz|||}zvuwxyz}z}}|{|~~||~}{||ywxz{}~}}}~{~~~}}}xz|zz||}~|~z{{|~~~|{}~z{|zyyyyz~yzyz{~}}}{|}~}zxwxxxy~}x}||}}||~~}~}}|}ywvwxz|z}{|~}~z}|{|yxyz{|{}zy{}|{}~z{|{}yyz{{{}y}}{{|~~}|~~{z|~}||}yvwxyz}{|~}~|}{}~||~~{|}zxxyz|~}~}}~|~}~}~z~z}}{z{|}~||z{}~~}}~{~|}~{zz{z{z|zz{}}~~~~}||}}~}|yxyyyz}z|||}~{~|~}}}|}yxwxy{}~{~||~}}~~z~|||zyz{|}{~|{{}~}|}~z{}{~{yz{||~z|~|{|~~}~~|{|~}{|{yxxyz}~z~}~}}||~~|{|~|~~}|zyyz{}|~}~{}}~}}~}{z~~|{{{|}~}}{|}~}~}}~~{|}~|{{{{|z~{{|}}~~}||}}}}~{yyzz{}|z}|}~}~|}~~}|}}~}|yxyyz|{~}~|~}~~|{{~~{zz{{|~|}|}~~}|}}z}|{~{zz{|~z}}|}~~~}~~|{|~||||zyyz{~}|~~~}|}~~}|{|~~|}~|zzz{|~|~~|}|~~}}~|}~z~~|{{||~}~~|}~}}~~}}~|{|}~|{{||~~{~||}~~~~}|{|}~|~}}{zz{{}z~}}~|}|}~~}||~}~|}{zzz{|}}~~|}~~}~}|{}~|{{{|}|~}~~~~~}}~}{~|{~|{{{}{}}~~~}~~~}||}~}{~|~|{zz{}{~~~|~|}~~}||}}~|~{zz{{}}~|~|}~~}}~}}{}}{{||~}~}~}}~~}}~|{|}~||||}~{}}~~~~~~}|||~}|~~}{z{{|~{~}~~~}|}~~}|}~~}~}}{zz{|~{~~~}}}~~}~~|}|}{{{|}|~~}~~~}}~~|~~{~}|{|}|}~}~}}~~~}||}~~|}|}|{{{}|}~~|}}}}}||}~}}|~~|{{{|}~}||}}~~}}}}}|}}|{||~|~~}~~}~~}}}}|}|~}|||}{~~}~}~}~~~}||}~|~}}|{{|~||~~}|}}}}||}~}~}~}|{{|}~|~|~}}~~~}}~~}}|}|{||}|~~}}~~~}}~~|~~|~}|||}}}~~}}~~~}||}~~|}}~~}|||}~{~~~}}}}}|||}}}|~|{{|}|~||}}~}}}~~}~|}}|||}~}~}~}~~~}}}~~}|}|}||}~|~~~}}~~~}||}~~|~}~~||||~}|~~}}|}}}}|}~}}~}~}|||}|~~|}}}~}}}~~}~|}}|||}~~|}~~}~~~}}}~~}|~|}||}~|~~~}}}~~}||}~}|~|~~}||}~}|}}}}}}||}~}}~}~||||~|~}~}}}}}}}~~}|~}||}~~}|}}~~}}}}~~|}}}~}}}}|}~~}}~~}|}}~~}}~}}}|}}|~}}}}}}|}}~~}}~}|||}}}|}}}}}}}~~}~}}~}||}|}~~}~~~}}}~~}}~|}}}}~|~~}}~}}}}~~~}||~~}}}~|~}}}}}}|}}~~}}~}}|}}}}}}}}}}}}~~~}~}}~}}}}~}}~}}~}}}}~~}}}}~}}~|}~}}~}}}}~~~}}~}~}}}~|}~~}}}}}}}~~}~}~}}}}~|}~~}}}}}}}~}}}~~}}}}~}~}}}~~}}}~~}}|~~}}}~}~~}~}}}}}}}~~}}|~~}}}~}}}}}}}}}}}~~}}}~~}}}~~}}~}}}}}}}~~}~~}~}}}~}}~~}}~}}}}~~}}~|~~}}~}~~~}}}}}}~~~}}}~~~}}~}}}~}}}}}}}~~~}}}~}}}~~|}~}}}}}}}~~}~~}~~}}}}}~}}~~}}}~~}}}~~~}~~}~}~}}~}}}}~~}}~}}~}~~~|}}}}}}}}~~~~}~}~~}}~~}~~}}}}}}}}~~~}}}~~~}}~~}}~}}}}}}}~~}~~}~~~~~}~~~~}}}}}}~~~}}}}~~~~~}}}}}}}}}~~~~}~}~~}~}~~}~}}}}}}~~~~}}}~~}~~~}}~}}}}}}~~~}~}~~~~~}~~~~}}}}}}~~~~}}}}~~~~}}~}}}}}}}~~~~}~~}~~~~}~~~~}}}}}}~~~}~}~~~~~~}~}~}}}}}}~~~~}~}~~~}}~~}}}}}}~~~~~}~}~~~~~}~~~~}}}}}}~~~~~}}}~~~~~}}~}}}}}}~~~~~}~}~~~~}}}}}}}}~~~~~}~}~~~~~}~~~}}}}}}~~~~~}~}~~~~}~~~~}}}}}}~~~~~}}}~~~~~}~}~}}}}}}~~~~}}~}~~~~}}}}}}}}~~~~~~}~}~~~~}}~}}}}}~~~~~}}~~}~~~}}~}}}}}}~~~~~}~}~~~~}~}~~}}}}}}~~~~}~}~~~~~}~}~~}}}}}~~~~~}~}~~~}~~~~~}}}}}~~~~~}~}}~~~}~~~}}}}}~~~~~}}~}~~~~}}~}}}}}~~~~~~}~}~~~}}~}}}}}~~~~~~}}~~~}~~~~~}}}}~~~~~~}~}~~~~~~~}}}}}~~~~~}~~}~~~}}~}}}}}~~~~~~~}~~~}}~~}}}}}~~~~~~}~}~~~~~~~~~~~}~~~~~~}~~}~}~~~}}}}~~~~~~}~}~}~~~~~}}}}}~~~~~}~~~~~}~}~}}}}~~~~~~~~}~~}~~~~~~~~}~~~~~~}~~}~}~~~~}}}~~~~~~~}~}~~~~}}}}~~~~~~~~~~~~}}~~}}}~~~~~~~~}~~~~~~~~~~~~~~~~}~~}~}~~~~~}}~~~~~~~}~}~~~~~}}}}~~~~~~~~~~~~~~~~}}~~~~~~~~~}~}~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m39.bin b/applications/SprPoP/assets/packed/MUS/m39.bin new file mode 100644 index 0000000..cdd7951 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m39.bin @@ -0,0 +1 @@ +~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}||||||{{{{{{{zzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzz{{{{{{|||||}}}}}~~~~~~~~~}}}}||||{{{{zzzzzyyyyyxxxxxwwwwwwwwwwwwvvvvvvvvwwwwwwwwwwwwwxxxxxyyyyyzzzzz{{{{|||}}}}~~~~~~~}}}|||{{{{zzzzyyyyxxxxwwwwwvvvvvvvvvuuuuuuuuuuuuuuuuvvvvvvvvwwwwwwxxxxyyyyzzzz{{{|||}}}}~~~~~~}}}|||{{{{zzzyyyyxxxxxwwwwwwvvvvvvvvuuuuuuuuuuuuuuuuvvvvvvvvwwwwwwxxxxyyyyzzzz{{{||||}}}~~~~~~}}}|||{{{{zzzyyyyxxxxxwwwwwvvvvvvvvvuuuuuuuuuuuuuuuuvvvvvvvvwwwwwwxxxxyyyyzzzz{{{||||}}}~~~~~~~}}}|||{{{{zzzzyyyyxxxxwwwwwwvvvvvvvvvvvuuuuuuuuuvvvvvvvvvvwwwwwwxxxxxyyyyzzzz{{{{|||}}}~~~~~~}}}||||{{{zzzzyyyyxxxxxwwwwwwvvvvvvvvvvuuuuuuuuuvvvvvvvvvvvwwwwwwxxxxyyyyzzzz{{{{|||}}}~~~~~~~}}}|||{{{{zzzzyyyyxxxxxwwwwwwvvvvvvvvvvvuuuuuuuuvvvvvvvvvvwwwwwwwxxxxxyyyyzzz{{{{|||}}}~~~~~~~}}}||||{{{zzzzyyyyyxxxxxwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwxxxxxyyyyzzzz{{{{|||}}}}~~~~~~}}}}|||{{{{zzzzyyyyxxxxxwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwxxxxxyyyyyzzzz{{{{|||}}}~~~~~~}}}||||{{{{zzzzyyyyyxxxxxwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwxxxxxyyyyyzzzz{{{{||||}}}~~~~~~}}}||||{{{zzzzzyyyyyxxxxxwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwxxxxxyyyyyzzzz{{{{||||}}}~~~~~~}}}|||{{{{zzzzzyyyyyxxxxxxwwwwwwwvvvvvvvvvvvvvvvvvvvvvvwwwwwwwxxxxxxyyyyyyzzzz{{{{|||}}}~~~~~~}}}||||{{{{zzzzyyyyyyxxxxxxwwwwwwwwvvvvvvvvvvvvvvvvvvwwwwwwwwxxxxxxyyyyyyzzzz{{{{||||}}}~~~~~~~}}}|||{{{{zzzzzyyyyyxxxxxxxwwwwwwwwvvvvvvvvvvvvvvvvvvwwwwwwwwxxxxxxyyyyyzzzzz{{{{||||}}}~~~~~}}}||||{{{{zzzzzyyyyyyyxxxxxxxwwwwwwwwwvvvvvvvvvvvvwwwwwwwwwxxxxxxxyyyyyyzzzzzz{{{{|||}}}~~~~~~}}}|||{{{{zzzzzzyyyyyyxxxxxxxwwwwwwwwwvvvvvvvvvvvvwwwwwwwwwxxxxxxxyyyyyyzzzzzz{{{{|||}}}~~~~~~}}|||{{{{zzzzzyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvwwwwwwwwxxxxxxxxyyyyyyyyzzzzzz{{{{|||}}}~~~~~}}}|||{{{{zzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvwwwwwwwwwxxxxxxxxyyyyyyyyyzzzzzz{{{{||||}}}~~~~~}}}|||{{{{zzzzzzyyyyyyyyyxxxxxxxxwwwwwwwwwvvvvvvwwwwwwwwwxxxxxxxxyyyyyyyyzzzzzzz{{{{|||}}}~~~~~}}|||{{{{zzzzzzyyyyyyyyyyyyyyxxxxxxxwwwwwwwwvvwwwwwwwwwxxxxxxxyyyyyyyyyyyyyzzzzzzzz{{{{|||}}~~~~}}}|||{{{zzzzzzzzyyyyyyyyyyyyyxxxxxxxwwwwwwwwwwwwwwwwwwxxxxxxxyyyyyyyyyyyyyzzzzzzzz{{{{|||}}~~~~}}}|||{{{zzzzzzzzyyyyyyyyyyyyyyyyyxxxxxwwwwwwwwwwwwwwwxxxxxyyyyyyyyyyyyyyyyyyyyyyzzzzz{{{||}}~~~~}}|||{{{zzzzzyyyyyyyyyyyyyyyyyyyyyyxxxxxwwwwwwwwwwwwwxxxxxyyyyyyyyyyyyyyyyyyyyyyzzzzzz{{{||}}~~~~}}||{{{zzzzzyyyyyyyyyyyyyyyyyyyyyyyxxxxxwwwwwwwwwwwwxxxxxxyyyyyyyyyyyyyyyyyyyyyzzzzzz{{{|||}}~~}}|{{{zzzyyyyyyyyyyyyyyyzzzzzzzzzzyyyyxxxxwwwwwwwwwwxxxxyyyyzzzzzzzzzzzyyyyyyyyyyyyyyzzz{{{||}~~~}}||{{zzzyyyyyyyyyyyyyyzzzzzzzzzzzzyyyyxxxxwwwwwwwwwxxxyyyyzzzzzzzzzzzzzzyyyyyyyyyyyyzzzz{{||}~~~}}||{{zzzzyyyyyyyyyyyyyzzzzzzzzzzzzyyyyxxxxwwwwwwwwwxxxxyyyyz{{{{{{{zzzzzyyyyyyxxxxxyyyyyzz{{|}~~~}}|{{zzyyyyyyxxxyyyyyyyzzzz{{{{{{{{{zzzyyyxxxxwwwwwxxxyyyzzz{{{{{{{{{{zzzzyyyyyyyyyyyyyyyzz{{||}~~}}|{{zzyyyyyyxxxyyyyyyyzzzz{{{{{{{{{zzzyyyxxxwwwwwwxxxyyyzzz{{{{{{{{{{zzzzyyyyyyyyyyyyyyyzzz{||}~~}|{{zyyyxxxxxxxxxxxxyyyzzz{{{||||||||{{{zzyyxxwwwwwxxyyzz{{{|||||||||{{zzzyyyyxxxxxxxxxxxyyzz{{|}~~}||{zzyyyxxxxxxxxxxyyyyzzz{{{||||||||{{{zzyyxxxwwwxxxyyzz{{||||||||||{{{zzyyyyxxxxxxxxxxyyyzz{|}}~~}|{{zyyyxxxxxxxxxxxyyyzzz{{{|||||||||{{zzyyyxxxwwwxxxyyzz{{|||||||||{{{zzzyyyyxxxxxxxxxxxxyyz{{|~~}|{zyyxxxxxxxxxxxxxxyyyzz{{||}}}~~~~}}||{{zyyxxxxxxxyzz{||}}}~~~~}}}||{{zzyyyxxxxxxxxxxxxxyyzz{|}~~}|{zzyyxxxxxxxxxxxxxyyyzz{{||}}}~~~~}}||{{zyyxxxxxxxyzz{||}}}~~~~}}}||{{zzyyyxxxxxxxxxxxxxyyz{{|}~}{zyyxxxxxxxxxxxxxxxxyyz{{|}}~~}}|{zyyxxxxxyyz{|}~~~}}|{{zyyxxxxxxxxxxxxxxxxyz{|}~~}|{zyyxxxxxxxxxxxxxxxyyzz{|}}~~~~}|{zzyxxxxxyyz{|}}~~~}||{zzyyxxxxxxxxxxxxxxxyyz{|~~|{zzyxxxxxxxxxxxxxxxyyyz{{|}}~~~}|{zyyxxxxxyyz{|}~~~~}|{{zzyyxxxxxxxxxxxxxxxyzz{}~~~~~~~~~~~~~}|{zyxxxxxxxxxxxxxxxxxyyz{|}~~}|{zyxxxxyyz{|~~}|{zzyxxxxxxxxxxxxxxxxxyz{|}~~~~~~~~~~~~~|{zyxxxxxxxxxxxxxxxxxyyz{|}~~~}{zyyxxxxyz{|}~~}|{zzyyxxxxxxxxxxxxxxxxyz{|}~~~~~~~~~~~~~|{zyxxxxxxxxxxxxxxxxxyyz{|}~|{zyxxxyz{|~~}{zyyxxxxxxyyyyyyxxxxxxyz|}~}||||||}~~~}}|||||}}~}{zyxxxxxyyyyyyyxxxxxxyz{|}~}|{zyxxxyz{}~~}|{zyyxxxxxyyyyyyyxxxxxyz{|~~}}|||||}}~~}||||||}~~~|{yyxxxxxyyyyyyyxxxxxyyz{|~~|{zyxxxyz{}~~|{zyyxxxxxyyyyyyyxxxxxyy{|~~~}|||||}}~~~~}|{{zzzz{{|}~}|zyxxxxyyzzzzzzyyxxxxyyz{}~}|zyxxxy{|~~}{zyyxxxyyyzzzzzzyyyxxxyz|~~}|{{{zz{{{|}~~}|{{{zz{{{|}~}|zyxxxyyyzzzzzzyyyxxxyyz{}~~|{yxxxyz|~~}|zyyxxxyyyzzzzzzyyyxxxyz|}~}||{{zz{{{|}~}|{{{zz{{{|}~~|{yyxxxyyzzzzzzzyyxxxyyz{|~~}{zyxxyz{}~~}{zyxxxyyz{{|||{{zzyxxxyz|~~}|{zzyyyyzz{|}~|{zzyyyyyzz{|}}{zyxxyyz{{||||{zzyyxxyyz|}}{zyxxy{}~|{yyxxxyzz{{|||{{zzyxxxy{}~|{{zzyyyyzz{|~~|{zzyyyyyzz{|}}{yyxxyyz{{|||{{zzyyxxyyz|}}{zyxxy{}~|{zyxxxyzz{{|||{{zzyxxxy{|~}|{zzyyyyzz{|}~}{zzyyyyyzz{{|~}{yxxyyz{|}}~}}}|{zyyxxyz{}~|zyxxy{}}{zyxxyyz{|}}}}}}|{zyxxyz|~}|{zyyyyyyyyz{|~}{zyyyyxyyyyz{|~}{yxxyyz{|}}~}}}|{zyyxxyz|}~|zyxxy{}~}{zyxxyzz{|}}}}}||{zyxxyz|~}{zzyyyyyyyyz{|~~|{zyyyxxyyyyz{}~|zyxxyz{||}}~}}||{zyxxyy{|~}{zxxyz|~~|zyxxxyz|}~~~}{zyxxy{}}{zyyxxxxxxxyy{|~~|{yyxxxxxxxyyz{}}{yxxyz{}~~~}|{zyxxyz|~|zyxyz|~~|{yxxyyz|}~~~~}|zyxxyz|}|zyyxxxxxxxyyz|~~|{zyxxxxxxxyyz{}}{yxxyz{}~~~}|{zyxxyz|~|zyxyz|~~|zyxxyyz|}~~~~}{zyxxyz}}{zyyxxxxxxxyy{|~~|{zyxxxxxxxyyz{}}zyxyz{}~~}|zyxxyz|~}{yxyz|}{yxxyz{}~~}|zyxyz|~~~}{zyxxxxxxxxxyy{|~|zyxxxxxxxxxxyz|}~~~{yxxyz|~~|{yyxyz{}~|zxxy{~~|zyxxy{|}~|{yxxy{~~~~~|zyyxxxxxxxxxyz{~}{yyxxxxxxxxxyy{|~~~~}zyxyz{}~~}|zyxxyz|~}{yxyz|}{yxxyz{|~}|zyxxy{~~}}}}~}{zyxxxyyyyxxxyy{}~|zyxxxyyyyxxxxyz|~~}}}}~~|zxxyz|~~}{yxxyz|~{yxyz}}{yxxyz|}}{zyxy{~~}}}}~}{zyxxxyyyyxxxxy{}|zyxxxyyyyxxxxyz|~~~}}}}~}zyxyz|~}{zyxyz|~|zxxz|~{zyxyz{}}|zyxy{}~}}}}~}{zyxxxxyyyxxxxyz||zyxxxxyyyxxxxyz|~~}}}}~}zyxyz|~~}{zyxyz|~|zxxz}~{yxxyz|~~|zyxy{~~||{{||}|zyxxxyyyyyyyxxyz|}{yxxxyyyyyyyxxyz{~~}||{||}~|yxxz{~}{zyxyz}~{yxy{~}zyxyz{}}{zxxz|~}||{||}~}{zyxxyyyyyyyxxxy{~~|zyxxyyyyyyyxxxy{}}|||||}~}zyxy{}~|zyxyz||yxyz}}{yxxy{}~|zyxy{}||{||}~~|zyxxyyyzzzzyxxyz|~{yxxyyzzzzzyyxxyz|}|{{zz{{|~}zxxz|~}{yxxz||yxy{~}{yxyz|~}zyxy|}|{zzz{|}~}{yxxyyzzzzzzyxxyz}~{yxxyyzzzzzyyxxyz|}|{{zz{{|~}zxxz|~}{yxxz||zxy{~~{yxxz{~}{yxy{~}|{zzz{{|~~{yxxxyzzzzzzyxxyz||zxxyyzzzzzzyxxyz|~~|{{zzz{|}~{yxy{}~{zxxy{~}{yxz}|zxxy{}}zyxz|}{zzzyzz{|~|zyxxyz{{{{{zyxxyz}}zyxyyz{{{{{zyxxyz|~|{zzzzz{|}|zxyz|}{yxxz|}zxy{~~|yxxz|~~|zxxz}}{zzzzzz{}~~|zyxyyz{{{{{zyxxy{}|zyxyyz{{{{{zyxxyz|~|{zzzzz{|}|zxyz}}{yxxz|}zxyz}|yxxz{~|zxxz}}{zzzyzz{|~|zyxxyz{{{{{zyyxyz}}zyxxyz{{|||{zyxxy{~}|zzyyyyz{|~}zxyz}|zyxy{~{yxz}|zxxy|~}{yxy|~|{zyyyyyz{}|zyxyzz{||||{zyxxz|}zxxyz{|||||{zyxxy{~}|zzyyyyz{|~}zxyz}~|zxxy|~zyxz}|zxxz|~}zyxz}~|zzyyyyzz|}~{yxxyz{|||||{zyxyz}|yxxyz{||||{zyyxyz|}{zyyyyyz{||yxy{~}{yxyz}|yxy|~{yxy{~|zxy{~|zyyyxyyyz|~|zyxyz{|}}}}|{zyxy{~|zxxyz|}}~}}|{yxxy{~}{zyyxxyyz{}}zxy{~|zxxz|~zxy{~{yxy{}}zxxz~|zyyyxxyyz{}}zyxyz{|}}}}|{zyxyz~}zxxyz|}}~}}|{zyxy{}}{zyxxxxyy{}}zyy{~|zxxy|~{yy|~{yxy{~}zxxz}|zyxwwwxxy{||{zz{||}}||{yxvuvx{|zyyz|~~}{{{||zxvuuuuuvwxz~}|}}zwutuw{|{}}|{{|}~~|zz|~~zwuvy}~{yvuuuvwxyz{~|{{{||||{zywusrrtw{}{z{}~|{|}}zwtsrrrsttvxz}~|xusrrux|}{|~~|zzyz{}~||}~zvttvy}|zwvvvwxyz{|~}{zzz{{{zzxwutrrsuy~}||}~|{z{}~zxussrsstuvxz}}zwtrrsvz~{z{}~|{zzz{|~}}~|xussvz~|zxwwwxyz{|}~{yyyyzzzzyxwvtsrtv{~}}~}zyyy|~}zwutsstuvvxy{~~}zwtsstx|}yxy|}|zyyz{}~{wtrsuz~}{yxxyz{||}~}zxwwxyyzzyxwvutstw{~~~|yxwxz|}{xvtttuvwxyz|~~~}{xutsux||xwwz}~|{zz{|~~~{wtrruy~}{zzz{|}}~|xvvvwxxyyyyxwvutux|~}zxvvvx{~}{xvuuuvwxyz|}}}~}{yvuuvz~zwuvz}}|{zz{}~~zwsrsv{}|{{{|}~~~}yvuuuwxyyzzzyxwvvwz~{xvuuvx{~}zxwvvwxyz{|}}{|~}{ywvvy}|wuuwz~}|{{|}~|xussuy~~}||}}~~~~~yvtttvwxyz{{zzxxwxz~~}{xvttuwz}|zxxxxyz{||}}zz|~}zxwwy}|wtsuy}~}}|}~~~|yvtsuy~~}}}~~~~~~}yvtstvwyz{|||{zyyy{~~}zxutstvz}}{yyyyz{|}}~{yy{}~|zxy{~{vssuy}~}}~~}}}zvttv{~~~~~~~~}}~{wtsstvxy{|}}}||{z{}~}}~~|ywusstvz~~|{zz{||}}~~zwwy|~}{zz|{vssuy}~~||~~{xvuw{~~~~~~}}||}|xtsrtvxz|}~~~~}}||}~||}~}{ywusrsvz~~}||||}}~~~~zvvwz}~|{||xtrtx}~|{|~~{xvwz~~~~}}||{{|~}yvsrsuxz|~~}}~|{|}~~}|zxvtssux|}||}}~~~~~~{wuux{~}|}~}~{vssvz}{z{}~{xwy}~}}}||{{zzz||xussuwy{}~}}~{{{||}~~}}|zxvtstvy}~}}}~~~~}}}~zvtux{~}}~}|||~~{wttv{|yxy{}~|zyz~~}}|||{zyxxy{}}yvttuwy{~}}~{yyz{|}~~}|{ywvttvx|~}}}~~~}}||}~zvttwz~~}~|{zz{|~|xutvz{xvwz}|{{~~||||{zywvvwy}}zwutuwz}~~~}ywwxz|}~~}|{zxvuuvy~~}~~}}|||||}~zvttw{~}~|zyyz{|~}yvuw{}xuuwz}~||~||||{zxvuuvwz}}zwuuvy|~}~~yvuvx{|}}}}|{zxvuux|~}}||||||{z{}}xtsuy}~}~|zyyyz{}|wuvz~yutvy|~~|}~}||{zywuttuvx{~|xuuvz~~||~~yutvx{|}}~~~}|zwvvx}~}|||}}|zyxxz~~ytsuy}}}}zyxxyyy{~}xvw{}xutux{}~|}}|{yxvuutuuvwz}|xvvx|}||~}xutvxz{}~~|zxwx{~}|||}}|zxvuwz~}yutvy}}{|~|zyyxxxy|}yxz~|xvuuwz~}|~}zxvuuuuuuuuw{{xwy|~}|}{wuuvxy{~}{zzz{}}|{|}}|zwutuwz~~zwuvy|}|}~|{zyxwx{}~|{|}yvttvz~}~{xuuuuuvuttuw{}{{{}~~}|}}yvuuvy|~}}}}}}}~}|||}|zwuttuwz}}xuuw{~|}~~|zxwxy{}~~}~|xtsvz~~|}zvttuvvvutsuxz}~}}}~~|zz||xutuy|~}||~~}|{zwvuttuvxz}{vtuy~}||}~{xxyz{{|~~|}~}}zutvy|~{z}{vttuvwvuttvwy{}}||}|xwx||vttwz|}}~~~|{|~}{xvuttuvvvwz~yutw}}zxx{~}zz{{{z{|}{|}{{}}ywwwxz|}z{~~yvuuvwvutuvwwwy||z{}~xutw{{wuvxyz{}~~{z|{wuttuuvwvuvzzwvz|wuvy~}}||zyyz||z|~{yy{}~{ywvvx|}||}zwvwvuttvwwvuvy}~zz|yuttw}{yxwwwy{}|z{~{vtttuvwwutuy}~{z{}}wttv{}{yyz|~{|~~zyyz}}yutvy}~}}}{yxvttuwwvutuw||{|~}~}yvttw{~zvuuwz~}|}}|}{vtttvwxvttwz|}~}~|wttuz}zyyz~~}|||yyy{}yuttx~}||~~|xutuxxwuttux~~~}{z|{ywuuxz}zuttv{}zy|~yvutvwwvttvxyyz}}}~{xuuwz}~{zyz~|yz}zyyz}}xutv|{wx|{vtvxxvutttw|~zxz}~{zxwwxxxz~xutuy}zxz~}zwvwxwutuxxwvwz~{z{}zxwwxyz~}|{|~yy}~|{{|~~}}{xvw|zuux|}xxyywutttuy}zxz}}|{zzzywuw}|xuvz~~zyz}}zyyywttwyxvtuwz}yy}~{zzxvv{~}|z{~}|}~~|{~|zz|}|vttw}|{{zwttttvz}~}zz|~~|||||}{xtty}yx{}~|z{|}~}|{wttxyxvttuw|{yz~{vtw}~}}~|z}~~}~zvttw}}yutttuy}}}~~~}}}~~{z{{|}~{vtwz}~~~~}}~~~~~|}~}}~~}~zvuwyyvtsttx~|}|{}{utx|~~}~~~~z{}|{||yvuw{yvtttx|}}}}{{~~}}zwxz{|{wwyzyy|~}|{{~~~}|yx{{wxyzxuttsuy{xz|wwyzz{}~~|yx|~{}~~|xx|}zxyz{{~|xvtuy}}}}{yx{~|wuvxy||y{|zwuw{{xx{~~~|wuwz~{{|{xtstsuz~|ww{{{{zwvx|~}vtw{}~|}}xwz}||zxvz|yvw{}}}}zww{|utuvx|{{}~{vtuw{{ww{~}{utuw{{wttttw|}}~yx{~zvtvy|}vtux}|{|}~yx{~~}{vty~{y|~}}}{xwx|}}zuttux}|{~~zuttuz{xx|~|{}xtttu{zvtttv{}|}~}|||}}yttuw}~{wutw}}{{|}~{|}zy{ytw{~~}}{xvx|~{z|yvttv{}}zz~|wutsv~|{}|yxzyvuttx}~yvutw|}|}}{y{}}~|~|wuttz~~|xtuz~|||}yvx}|xy{{{|~zwvx||xwz}zwuwz|{x{|xwvtw}{wvyyxwuux{}}}{ywuy}}|}|xwz}|yx|}zwuu{~~{xxz|}}~}|{yvw}}}~|yvw|~yvwz}yvvz}yxz{zww|{yywvz}}{vvz}zzxvwz{zx{~|yy}~|}}zwvz{uuy{~~}zwx|}~|||zxx~}{xx|~yttx{zwx|}xvvz~}|}|yuu{}zzzxx}}|}~ywz}~{zzyxy{{xux||}}}yvvy~~{|~utuwz~{|~}|~|wty|{}}}~~wttvy}yy|}yvvy}~|vty}}|yyzyz~}|}|{}{{}}}zyzzy{}}ztuz~~zvvx|}yx{|wutu{}}}{{~}uu{}|~|z|}xvttz|}{wuw{}}~~{~|vwz|{xwyyy}~~|xw{}|}zwxzyz~}xtw{}}|~}yvvz}zww{~{wtuz~{xx{}~xy{|||~}|xvy|}{yvtz{wvx|}|}~zx|~~yz|{yvvxxy|wux}|wuwxx{~yvy{{zxz~yvw{}xvvz|wwz|}{}|wux}~}~}{xwz|utwy{~|yx|}}{xwz}}|}|vvz{}~~{wtuwwz{wux~~{ttvwx|}xy||zvux{}yxz}{vuw{|||{yw|{wvy~}~~~{utx{~ztuuvz~{}|||z{~~|}}wuwxy~zutuuw~~yww{}zz~xtuuuy~~yy}~|wtuwx}~{{~}wuvz~~~~~ytw~zwy}{yz~~ytuvxxvutv}~|}|{}}}}}}wtvvvz~wuutu|zxy||wvy|wvttx}}yz~}xttuuy~~~xvw{}}}~z}|uv|~~}{|~zvw{~|xvtu}|zwtv{~~~~zwx~~~|vtvuuzzxvtv|~}}yx{~{vux~|yuuy|}zw{}xuuutz~xwy}}|}~zx{~}wy||{z|~xuv{~{ytv}}yvy{|{y~xuv}ytuvtv|}}}{xuz}|}~zyz~yuux~|wx{|{ww}|wvvtu{~}yy{~}|~|vvy{}}~}{wvz}~yvv{|xy}}|z||{wv}zvv{yuvutw{}}z{{y}}{|~|yy{zwvy}}||}|yuv|{xxwuw|}||{~~}~{uuwxz|vtvy{|yx{}}|}||~|wty~yx{}zz}}xwvuw{}|xx~~}|}{wwy{xwz}||~{utz}}zwxxvw|}{}~}~~|vuvvw|{utuv{|{}~|||~}}}||{uv{}~||~||~zvw||zyvvz||yuy~~|xvw{}yxz}~||}{y}zuw{|{wwxxw{}|}|yz}yuuvuw||wvtt{}|}yx|~{xx||wy{{zxz}~zuuz}{xx{|{wuz~~|~|wuw|{yy|}}|wuy}}xz|{yuvwww}}}}{wwzyuvvuw||}|yuuz~|vux{}|wuy~{}}{wuwz{{wvz~}z|}}zuv{}}{z|}xvx}|xwy}~~zutwy|}~}zvtuvvz~~}yvvzxwwuuy|}{y}|xy|}~zttvx{|wvz~zutvwz{yy|}~~}wuy|}zwx{||xx||xvx{}wtuuv|~yttuuw~{wvx}~|}|yxvvx||zwy~}~|}~~~vtutw}|yz}{z|}wttuv}||~}|~~~ywz}|zvwyz}}{~}xuvz~}wtuttx|vuttv}~|xvx||yy}|{xwz||zvx~}}}zz|yvutuz~~~yvx~zvutu|}}|yzzy|}|xuvwx|~xuw{~~|wuuttx|~~ywutx}~~{xx{}zww|}zz|}|wuy}~|}~{ww{zyvtvz}}|~xuw}{yvtw}}xux{~{}~~zvuvvw}}xwy|}}~yvvvtvy{{y||yvvz}}~}zxz}}xvw{}}~{vw{}}{z|~{vvz~|{xwxz{yx}zwx|~|ywz}}~~wsuwz{wvvvw~{y{}}}~|wvvvuwzzyvy~zy}}|}~{yx{~}xvw{~|wx{}|ywy{}ywy~~|}~}{z{|zwuy{z|}~}{}~}|~~|}{uttuz~ywvvw|}}~~|}|wuvvvx{{yuu{}}}}{xxz~~zwwz}}yx|}|yvwxy~{z}|yy}~|}~}ytu{~~}}}}}|zy{yvutuz}zxwvz}}ytuuuvz{{wsv{~~~~|xwx||xwy|}}~}{~z|~~{wvwwyzww{~~xtwz||{}~}|yx|~{xwz{ywuuy|~}|zxx|}}}wttuuw{|zvtwz{zz||xvx|~{xxz}}}}|yx{}}~{wvvvx~{wvzzwyzzywy|{vux{~|wvy~~~}|ywxzzzy|~|z{}{|}vtttux{|yuvyzywvy||xwy}|yxy|~~}}zuvxz~zwvvvz|xx{~~zz|{yvuwz|zttvx||xx{~|{}~|zz{{yvv|~}~{z|{vuttvz{zvuy{zwtuwz|yy||ywx{~~~}{vtuvy~}yxwwy~}{|}}~|}~}|~~{vttvx~wtttw}{z{}zxz~~}}~}zvtx}~{y{~ywutvyzyvvy{zwttuv{}|~|xvx{~~~~{usttv{{zxwz}}~|y{}~~|wtttv|{wutuz}~~ywx|~|vux{}}~~zz|~~|zwvwyywtvz{zvtttuz}xvx|~~~~yutttvz~}|zz}~~}{wvxz||xutsv|}zwuwz|||~xwx|{wwyzyxz~}{{~~||~|yyzzxutx{{xuttsu{}xxz|}~~|wttttvz||{~~|}~|}zutvx{}ywvtw|~}zxyzyxx~zxy|~}~}z{{zwuvz}~|||z{}|}{xttxzzxutttv{~{z{}}}}{wuuttwyzyw{~}{{|vstux}|zxwy|}~~|||yvuy~{{|}}~|{|}~~|}~|xutux|}{{~{vtwzzwututvz}}~~~~}}}{vtuttvyyxuw|~{{}{vttux}}~|{yz}~}}~}}}xtuz~~~~}}}{xy{|~~~|xtttv||{|{vwyzxutttuy|}}}}}}~}|xttutvxzyutw|~}{|~|xvtux{|{{~}||~}}|zz~yuvy{{{}~}|xvwyz}~~yvttu{|{}~}yz{zwutttvz}}}}|z{~|wttttvyzxutwz||{}|}~|}}ywvxzyxy~~~}~~{xx||yyzyxwy|{vtuvx|~~zxvtv{~}||~~}}}zwtsttw|~}}|zxy}|vtttuwyywtuxzyxx{}~}|}|zzzyvux~~~~~~zxx|}}}{xuuwz~ytttux}~}}{ywwz}}|z{|~~zvtttv{}~}}{xxz~zutttvyyxuuwyywvwy|}{|~}zwtuz}~}}}~~}yxz}}|~{vttvy}wtttvz}}||~|{zy|~}}~{yyz|}xvutvz}}}}{xxy}~}}}xvutvxywtuxyxvtuvy~~|}~xtuy{{zz|}~~{z{}{z{~~xuttv}ywutuy{{yz~}|{|~}}|{~zxyy{~zxvuw{}}}}zxx{}|z{~{yvvwyxvtvyyxuttuw}~}~~zvwyyxxyz|}}~}yxy~zwutw}{yvvwyyxwz~}}}~|yy|}yxxy{~|zwwz}}}}|yyz}}zyz}~{xxyxvttwyywtttux~|{}}{{{ywvwy{~}~~|xvx||yvtvz|~|zxxxvtswz||zz{{}~{{}|wvuuw{|}|~~~}zyz|{xuvx|~~|}}|zwx{}}zxwvvx|~~~|xsqqrsx~~|}}~|zyx{~}{ywvvvtrrv{~}yyz|{z}}yyyz|~~|zxusrtwxvtvy}}wuwwvspnnnpuxy|~~{zz{}|{|{xtqqruz}{{{{{{zwwyz|~~}z{~}|||||xutvxxx{}{|~||{{}~}}|zy{~}ytpqstsrrstv{|xtstux|}{z{|xvvwxxzzyy|~~|ywutuz~xttttsqqstw}}zxwxxwttw{~~~|ywxyyxxwutwz}~{}~~}{zzy{~~}{yy|~{|}~yurruxy{||{|~|}}||}}|}~}~~}yusuvvuuwy|zvvx|}|{{}~~|~|wuuwvtrtvyz||{z{~~{vsqpprvwwwwvvx}}||}~~|z|}zwutvz|~|wrqqsvz}~~}|{}~~~~{yyz|}{{|{xttvz}}}{z{{~|z{}~|{||yyz|~{{zwspopqtz}~|xvutvxyyy{||}}|{yuqprtx}zvtuxxx{|xspooqvz}|wuvxxxwxz||{|}|{zvrooprv{|zzz}}zyz||zz}~|||}}zxwuuw{~~{zyz{~}}|~~}{||{wtrrsw}~}|{zyxvwy{}|}~{vttuwxyyvtsuwz~|yvuvyyxxvtstwzzz||z{~~}||||~}|{z{{vsqqrtvvux}{yz|~}}}~|zz}~~~zyyzzzzzxx{}{zy{~{{}|zyxxz|}}|}~zwtstwz{|}|{|~~|{{{ywuvy|}~~}}~~zurqqrtwyy{~yusux|zwtsuwxxyzz{}|z{}}}}{xvuuvz}ytqpooqsttux~}|}~|ywwz|~~~~~{wuvxxwx{~}|{|}}|zz|~~~~~~{yxvwy|}~zuqpqsv{~~|{zyxxurppqsvy{{{}~{ywutuwwvtvy|~{yyy{~}{|~~~~~|zxwvurpqtwyzz|~~}||zyyz{}~|}}||~~~}{wsqrtw}~}|yxyyz{|{ywy|}~}~~}}~~}{zxxz~}}{xxzzyxxy{||}{xvuuuvvurruz}~}{zyyyxwwy}}{ywutvz}}|~|zyyz|}zxwx{~{wtrrrssrporvxz{}~{zz{{{{zxvxz|}xutux{}~}{z|}~~{y{|||{||}|{}~~}||~}|{|~|wtrqrsttux~}xspopqstuuuwz}~~yxxyxuttwyyzzzy{~}{yy|}{|~|xtsttsqqsuyysrrtw{~~}}}}~{z|~}yvtsssuvxy{~~}zyxxz||{xwy{{yxwx{|zz{|{{|~|z{}~{xwwx{~{wvvwwxyxvttuvvuvxz}|wrppru{{xwxxwvwy|~}|||}|zxx|~~~}{yxwvurpprssrrsux}}}~}zvsqrsux|~~|{zxxxvttuwz~~|}~~|ywwx{|yxy{}~|}~}}~}|zxwusppsuxz|}~|xusrsttrprvyzyxyz~{xvvwxxyxurqsv{~{yz|~~|{zwtrpppsx}|xtrrtvwwy}~|||||}}{xvuvwxxvsruy|}~~}}}}}|zxyz{zyxwwvw{|yy{}zyyyy{~}{{}}xusstwyywuvxyxusrtw{zwutssttuy{wuvxz{{{yy{}~~{yyz{{{{{|}ytqrttsqoorvz}|wrppqtvvuvz~}wrpqrsuxz{}}|{yx{~|||{yx{~~}||}zussuusqqtx|~}||{xutssttstvxx{~}|||{yusrqpqrvy{{}~}zxwwz}~}{}|wtssuxyxz||}~~zvrpquxyy{}~~{wsstvwwxyxvuuvvwwxz|~~~{z{||ywvwy|~||}~~}ywvwy{{yxz}}}~~~{yxvutttuwyyxxwvtuw|~|z{zxspqty}~~}{yyyvrrsvz}|}~{xvwx{}}{wtqpqrsux{||{{~|yxwtssssrstvy{||}~|ytrsuusqrw}}{|~~}}~~~zyz{yyz}}ywxzz{}~|{ywussrqsy~{xxyxurpqty~~zvrpqsttsqquz~~~}~~}|{xwxz{|}~~}{yy{|||yvvy|~~~zvtsssttuz}xspqsvyxuux{zwtrsvz}~{{~|yyyxx{|xx{}yyyyyyyyyxwxz|}}|{{~|vqpsuwxvuvy|}}||}~~~~~{zzyvsqrvz~}wssrqrsux{|~}}|zxx|~~{urqsx{{yyzzyz|~{yyz{{wropsvvtsuy{~~~~}yvusrsstvz|~}}~zy|~~}{wuv{|zwuw{{yxz~|wtttwzzxz|}|~~}zyxvtux{}||yxwutuvtqqrtwzz{|}~~~|xwy{zwwy}}|{{zyxz{zy{~|vsqqtwy|}{yvsrrrtvwwxyxvtux}}~}{||z{~~}z|~~}{zyz}}zyz}~}zvuy{zwvxz|~|zurqqsuvx|}{y|~||~{vsqsz}zutvwurppu|}{z{~{z}}zz~{zzz{|zwv{}|yxy~~}~~{yvssrrstsrw~}~{xyzwrppry~|zz{}~{|~}||{z|}~~|ywwxwwz|}}|xwy{}~}yvy|}}}~~~}~{|~|{z|~~|}~}|}~|wrtwwtrpquz}}{~}xwxxvx~}{z|~{vw{|{z{z|}xvwy||zy|~~{tppruwwttwz{|||}~~|zz}~||~|{~{wwy|~~~}||{xwxyvtw}}~|tpprw|}|}}||}}~}~~z{~}~~~{zzz}~|}~~{xxwtrrrswz{|}~~~{xy||{{}}{yz||yy{{yyz{}}~~}xrppqvz{z|}}~{{zvrruy~|}zvttw{|{~}~|}}||{wvy{wtuy|}}||}}zy||zxz}~~~|zy||{~~}~~}}{wwxvrpqt{~~~~{z~}~~yx|}|~~}|{|~}zz||wrrsw~|||yw{~~|xsrpquvuw~}{z{|z{~~~}{}}}~~~}yyyyzzxx}}|zvuy|{{~||~{|}xxxy|}{x{~{zz~yuwyxx{~|z}~}~~}xx{zvqppsz~~~{xz{~}}{}}}~~|wx}~|xwtv|~~|wstwy}~{xz~~~~~}|~|~{yxy~~}wppqsy}{x|~~|}|{}xwywtv||zz|}wy|}zyzz~~~|~~|{||}zvvz~}}wwzyxxyy}}|~~|~{y|zz{|}wvwuqprtz}}zw{}~}~{trqrx||z}|{|}|{~|y|~|~}{~~|~~wtrquyyx|~{{{|~~z{|~}}~}zxy||y{~~|{|||yusvzyvw{|}|~~}}}|}|{|zuqrtx}{~}~yxvv{~~~~~~}}~}|~ytstv}~}xxwwy{yw{|{~||{|{wuv|}~|vuutvyzxz|yz{~~~~{vsvzwuw{||}zy|{}~~ywwz~~~~{}~z||z|}~}|~~yvz}{vttsy~|}}|}yxx{~}~}yz}~yz}}yutrv|~~zx{~}{|zsqsrtwwssy}}{z{}~|{}}~~{vx~}|wwwy}~yv{}{zy~{y{|xw{~}{z~|~|yyy|~|x{}~|yy{xtvz|||zvxz}~~~zuxzyutsswzz}~||zvwwz~}}{z~}{~}zwxwx}}~}{}|}}vuuv|~|x|zvwy}||{y~~|~~vrqqtxwrsxzzz}~zzz|~~{}~~}zy{}ytuz}}y|}~{wvwx}z|}~~}z{}wuy}~|wwxz~~~xvxx|~y|}{xz}~{~|{y|~zy|zuqqqrwxxyxy~}yxz|{{~|wwtuxzxx}~|~}yy}~yx{}{{y|}}|wwutwywtx{zwx{~|{}{}}{y|~|}|yyy}|vsvytpquwy}|{~~~yy~~~}{y|~}z}{wvtx~}}{vwuvyzyx~{zz{zz~~ztsrt{{zzwuy{|{{~}~{{{{{~~~{{{|}|xrqonqsqov~}}~~yvxx|||{zyz}|}yz}}~y{~}xutrv|}{z~}ywzyutwyxyxuy|||z}~|~zx~~}wxxy}{tvyxrnmkpuw|}||~z}}zvwwx||{}zyyy}|x|~|zxz{usv{}}~yx{}|tsrruwtorxzz{~}xyx{}~}|{z}|ywxxz~||{vrsuqmov{}}z|}}yx|}xw{zxyz~{|{|{{|tsqrvxurw{|{}}z|{wrrqqvvvxwx|~~~~~zwyy~|}~~|xx~ywx|{{ypmmkpuwtw}|{~|uussy|{w{~}yy|~|}|{yz~~zy}|wqqqqwxxzyy~~xuwzupqtwy|{y|}zy~|{}~{|z}~~|vvvuwzxstwuolmnsz{}~{zz|~ztqrsy}}~}zz}~~}~yxwyzuuywrpswx{}{~~}xsroortrqx}}{{}ywxxy||yy~zzz|xw{|xttru{{zxspqvvsty|}~}z}zuvyvsuy||~|{~~~|z}zy|}{uqqnorsprz~xx{ytoompwz|~|{~}xz}|ywwuy{z~|xvyzutvyxwupoqquyxtu{~yrqpptvurw~~zuvvx|}xtxyvqnmlrvx|}}~yssvuqsx}}}yz|~{y{~yvx|~~{x{|~|uqrqtwupptvtrssu{||~}}{z{xsoonrxy}~~z||yuvvw|}||ytsutoosy||}~|uoooqvyxuz}}z|~~yuusvyyttz}|{~~xxzxsqqpswwxwuuy|yvwz}~~|{vtvupotz}~~~ywy~{wwz|{ztnnmnsvutz}~xvxwspqrv|~~~|z~{x{}zusssw{z||z}~xttwupptxx{ytuuuwywuz}uomlnsvuw~~zuuuuxywrsvtomnou{|~~|}~~|xtuz{wx|~}z|~|~~ywy{vstx{|||~|xqonmqtsquzzwuutx}~~{xzxsnmmpx|~}wxzzvttswzyxvrquxutv{}~{z~~~}}zttuvy|zvx}~}|~~~xsrpqtvstz~xwxvqmllqwz||zy{~zwx}~ztruvrsw}}|~}zxy{yttwxwwsopqswywux|}}~~}xw{|zvvwy~~~}}~~xy{xspoosy{~~~xrqtuqrv{}~~xvutx{{xz~|vpppqvyxw|~~~~wsrqrttoosvurssu{}|}||~}{}}{}~zz|~|~~{vsvwsrv|~}zunmmotwwux}|xuuvzzvwywsrrsy~}~}yx{{wrpootvvxwuw||yxz||~~{}}}~zzyyz{yw|}zsooorwywx~~~~wtutqnopt{~{xz~}z||{{||~|}~|yxz|ywy}~~|}{}xuwvrorvyz{vtvvxzxuu{~}{{{~}}~{ywy{uuvtpoqsx~~{wttxzvvz~|wvwx||||yyz|}{}|wuuvyzxtx~~}vomlmqtsrwzyxwvuy|}~}{{|{{~~{xtsw{zwy~{}~~~}|zusssw{{wvy{ywy{~}z|~z{~|zy{}{x{~}ywxwy||~~}|~{wxwrnllovz{|{z|~xwz}}|yxy|~}||}}}{zxtstuw{~{{~~}}~~zvx{zvstvz~}}}zz~zvvuw}}zxxy~}~zwy|~|}~{uqrutsuy|}}xvtuyzxy~||~~zyy}~}|wsuxyvuuw~~|{|~zz~{yz|}~|z|}~|vx{zustw~|~}||{wvxxvw~|yunmnquxvty~}xtuuy{z}{yz{}~}}}z|~}}}{z|~~}~}}~~}~xxxwxxvtw}~}}}~}yvx|||~zwwx|~}|wuv{~~zssuspprsw{~{y{~{z~~zyz~~~~~|}~}zyywz||~{vwy|~|xw|}{{{~~}}zwtv{}~}{}~{uqsvy|~{wy{zxx{~{y{~}~}~~}{yutw|zvw}~yuuvx~~|~|xxz{}~~|~}y{|{{|}}{|~|z{{z{~~~{z{~xwy}}|yyzzvtuuuuvvz~}}{y}{yxx{~~{~|z{~|z|{zzwutsuz|yvxyxvwz~}z{{{}{}~}|}xutw}~|{z{~}y{~~yvuwyzz}~zwvy~}ywvx{~|zyy{||zz}~|yx{~|~{yzytty|zusuz}|{y{~{wwutx~|{z}{x{}ytsvz~~}zwx{{y|{{|xvvx~~}{zyxx{zvtuuuutru{~~}~zvyzy{}~zwx{}~}z||{{|}|yz}}xuxzxsqsyzwy~~~zz|}}xwx{{z|~~}~yuuwxvsqtyywtsoqwyz~}}yxyzywuu|}z{zwzzzywutw|}|}~~}}}xuw{|~}{z{~|xuwyy|~zux|||~~|}{{~}|~zy{}{{zwvvssw{}~zxyxursv{}|~}{{{z{z|}xxwx|~||}~}zwuy}}~{z|~}wpptx{~|z}~|{}~|~{ttxzzxxy|||~}|}zyx||y{}~~}}~|yy}|wtv|~}|wpmmoqtvy~~}|~|tpqv|}~}|zwwy|xy{~}}zxxxxussstvwx~~}|~||}}~zwy|}~|z|~}|~{}}{|{zwropuzxtvxsnmmmt}}yuvxurqty|}||yuvxxyz}~yuty}xvux|~|{{{}}~~{|~~ywwttvuswzzzww{}{{|~xsuzxqopuz}}}yrqtusuz~}{}{usv~|}~|xx|~zwwvz~}zz{ywwyxutuwvutuy~{yxx{}{yvqquy{}xy|}yuty}}|}~xsuz|zy|}yuqmlmsy{wv~|||~{wutuvvz|xtvyz{}~~~{~vstwxtpotyxutsptz{}yy~~}{yxvux~|z|}~{{z|zy}~ytoptuvxwpmpqnmquy|}|{wqrwzxvwyy|~~}zxx}||{xtrttqsvy|yvwytpqtw||~}}trstv{}xtvz}|}}zz}{vuvvokmoqqppuvssspou||xw~|~~~~y|{|}wtw{wpnrxywtpjfhklmqv||z}zz}}~|xwuux~|zyz}}~~~|{}~xsquxtpoprtuusonrrqpqstru{~}|}~||ww{|z||yvtrlikpspmppjeeegnv{|yy~~|{~|{|~~}||~||{~}|{yupopstolmljjkhhotusprz~|vvx{|tpswslijpvwvvrkjmmjntyzy{~~}{{~{~|wvz~||~}yxxz}yuuvrnkjmponmifjoprvxwvx|~~~}}}}}|}{~|{z|yrorvvttwvplhedhnsrnq{~zxzywyzy{~}yy|}zx}|~|zyxsnoswwtqrpnmllovzztosxzzwuwz}wvvuz}yz}|uoknqqqrohhkifglptuw~}~|{~zy~~|{{}~~~~~|vrswxuuridfghloqssrvxxvv{~~~~{zyvtxzxyysoooqrpotuppolkqvvrpw~~xuwy{|z}}}}}wuxyxz~{sppuvrnooiggegowzyuw|yvy|}~|{~~yx}zvttwyxvtooqpmkmoomotvvvuvxwy}}{{}zw{|~~~wvww||y{|urrvxyxxrllmnnquxwvz{xuqrvvty~~~xw{~~ywvxxropplkkhhnqqoknw|}ywxz}|{~|yy{~zz}{~|{yy|}yxytonieflqtqmsyzvqpsuy{zzz{~~|}{}}xsprwvsrpjjpqpruvvttwyzxvxxtv|||}}wv{~~zxvv{|~zwyzwwxrjiljhjnprsx~~{vvzyvz{xx}{zzvx{~~ywxuqonnqvywplptvtomqtuy}}~}~yzzz}{z|zvx|~{|xux{}{xwyvssqmnswwrqx}~ztqqqry}zz}~{xw|~|{}|y||zulijkkmoqsqorttrruwww|zvvz~|{|zy|~|{{~~}ususqqqpolouwuqqssqrx{~{y}}|||~|}}}zwx|}xuwunkkijrxzytuz}{uqtvvz~{z|~|{zz~~zxx|~~}|}~|}}{zurrpllqtsnlpw{{wvvuv}}}}zxxzvux}{|}zy{}uqrsqruxyvvywtpmoqoouzz{|zwy|||y|~|z{~|~}|yyy|~~}wrrtuvyzxusx{yxvvxwstxwx{ztqsxzvrty}~}~yuqljlqutonuywrnnpsw}~}}}{{}}}~}|~xvxyz}{xy~{{xuw{}{uoqtusqooprwywwyzyxy|{xx{~}|{{{~~~yqpromnqsuux}}zvuwvsw}~}~zurrw|zuuy}|}|{~~~}~~~wttstwxwussvuspqvwwy}}~~yvvy|yrprx|~|{}}{|~~{zwsty{yttz~}yroooqwxvvxxsqrvyz{}}z}~~}ywy~~|}|xwutu{}yz|}|vstvw|~{wwvrqqrsqppswy{~|}}~}{|zwuutsprvwurqrqoqw{}~~zvux}{ww{~{vwyxvz}|||yz}|{|}~~|~~ztpoqqqtwyyyvqoqwzvsru{~~}~}~{yxuruxxuppv{}zvuuuy}}{}~|wsqsspqtxz|~}wvwy}}|z|~~}|wvy}}xw{|ztpppsy~}|zxuuwvsstvuuvyyvvwy}|~}}~}yz}}}~zy{|{zwwwuqtwwxywqopstpmou~~|~{}~~}~~}~zuvyyz}~~}~xxzxwz~zrnnqrokinsutuvtv|~~~}}{{~}xtvwwuqoopswxwwyzywvxwttuw{~|wwvwz~|~~{}}y{}~}|}~~}zusqrxzywwvspoqpnorvxwwz{wvx{~|z{z{}~}|yyzyvttwxwy|}~}yvvxzvnkmqvxxxwvwxww{{|}z{}~~|}xwwusvy{~zvtwzuonrvxwurnlosux|zvw|||{{~}wuvvx||xwwurqqrrommortw|~{{||~}}}zvwxvv{}|z~~~~{xvusnjjmppnprnklnqx|z}{wwy~}wsrrqptwxxwtqquzzvsrvzzyyyxy{{y{~}}~~~|zzzz~~~yuux{~{{{zz{ztpquuokjmsvvvupnqttw~~{}|{~zussty~~}||zxvvyxutttsqrsrooqtx}}}~zx|}~}}{{}{z|~{{{zyyz{wsrtuuuuvsmjiilsy|{{}zyzz{|}yvx{|}~zx{~~}|wqnortplmquusrqoquxy}}~~||xuuuw{~}|}{xy|}xqmmpqppnigiihkpw|}{{~}xvwz|{z{|||~{xvvzzwvvuqnoqrqsuxzyxzxsprtw|}~~xwwxy|~}|ywvuv{}zwuuvvspppqqprtqnmmlpvyzz}zz}}~yxz|}~zy{|}~~{vstxxsprw{{xurmkmmnqw}~~|yxwx~~~}||~yutx}|xvvxzzxuqnnnmklnpqsy~{|}zyurstsu|~}yvsqpmkloqrsuuplklntz~}}~|xwz}~}|~}|||zxxyyvsqokhgffjnoonpy}|{xuvwy|}|yspqux|}{wqnorqmijouyz{yusuustx|~}~~zyz}{{{zyyz|}}|~xvwwtqqswwuspkjmnoqstutvz~|vux~~zyxvv{}{xvwxuqqrtuvyzxtponoruussy~~{z{{{}}|~{{~{yxtsw{~}|{wrommpsusnlotwxwx|}{{}xsqppsvwxy|{xwywqljlopqqomnoooquxzy|~}~yy{~~|z}}|~yvx|~~}yrlkjjjkmoonprtuw{~wtuwxwvwvttrprv{}}~{wtrpmllnoopsussttvz~~{yz}~~~}}~~}z{}}~~{}ysrtwwwxxtpmllosvvssvz{xuw{zxwxy{}|yxxyz{{{{{}~zz|ytqqtwwutqoqssrtvwvuvxyz{|~~}{|~zxy|~|yz}|vsuz}}xvwvttuvustutrpoqrt{}{{xvvvttvwwtsuz}~}~}xsrsrpoonjhhimrvxxvx~~|~~|{{{}~~~}|}ywy~~~}~~|{}~{wtqpprsqmlpturoopty}{yxupmmpsvyyxwy}|uolnqqpomjjmoruy}~~~~|{z|~{{}zz|}zyy{}~}|ywwvrqssssuvvuuwyyxwwwuv{~~{ywtsttux}}z|~~{zz||yxwwvuvvrnlmoswywvw{~~zvuvx}~wux{{yz{||{zyz{zz}|yxxwwxxy|~zwwyzxtpppptwxz~{usstuxzzywyzyvtuwy{}{zyvtplhhhhikosuvy}~||{y{~~~zvuux~~||{|~~}~}|}}vtvxyyyyxvuvurqppqqrv{}{uponlmqvxxy{}~}zzz{~~|{{ytqpoliiiknsvwwy~||~|{{~~{z|}}}~}zsonnqvz{yww{~|yvuuw{}}~~~~}{|~~wrqrtvxyyz}zyzyz}}{||xsooqrqrtuttwz||{{|{z{~~{wvy~~|zwsqrvz~|ussuy}}yvussvyzzxursx~~{{|yuuxyyvvy}~|yusruz}~yqljijmppnkmsxzzy{~~~|xwxyxyz}|{}|xvwxz|~}yuvz~}z{}~|xspoquyzz{|zxy}|yvsstsqpquy|||{{{{~{{~~xojiijmpstttwz|}{{~}}~|z{}~{xwxz~|yusstwy{}}}}{yxz}~zxxyzyyz|~~~}|{{{zwwxzzxxz|}}}{{{~~}}~~{uqnotxywuvxyxvuwy|zxz{|~}zzzussrsvz}~}zz~}{{|||}~|{{yxyyxvsrqqruz}~~|yxz{yvuwz|{zyyz{yww{}yupmnpqqrssrqqsvx}}zvvvx||{|zwwy}}wuux|~|{}~{{~|{{zyxyxxwwvvx{zvtu|}||||}~}zvttuvustw{}|}~}|zywvspnooppsy|{xuux}|xxy|}yxz~~zvvx}}{|~}}~}~xsu{}||~~|||{{||yursvz}~|xsrtw{{z}~~{xwz}|wttvxyz||yvw{}}{xvuvx{}}{xusrv|~~}zwux~|~~}~{xxzywux}~~~}|~ztqruwuqmmpsw{}|zzz{}}zwtrty~yvtsuuuv{{zz|~wpnqvxxurppqv{~{{~{zz|}~{wvxz{{{z}}|}~~~~||~~~}}}}~}ytqqtwwxxxwvx||{zz|~|wsssstw|~zxz|zxy}ytstux{{yspqtvxz{zyxz~|urtz}zyxyz}~~~}}|~|wwy|~|xwz{yxyzzwtqrsvvuttwz|}|{zy{~|yvspoqux{zussu{~zy{|{{||||{yx{}~~}}~~|yvuw}~~~|z{~}z{||{zz|}xvvx}}}~xtqooqtutsqomnswz|~~|{zzzyy|~~{wutqooquz~|{{}~xtqrtvvwyyxwxy{~}}}{~{xwvtsrtz}zz~|zz{|||zxupmnsy}}{wrorx|~~}|}~yuuy{zyxy|}wtuxyyz|}yvuwxyy{zsprvz~||}~|yvuwy{~~||~|xusuxz}~{{~}~~yy}xttvy{}}yslhjpw{|xvw{~}{}zsopqsstsrvz{zyyyz}~yxx{ztqty}|yxz}~}yutw}}}|xwxz|~{zzz|}}|{{z{~wpmnqtuuroljhlt||xtsty|zvrppqrsuuvy}}}wqptz~~~|ytqqv{~~wtx~~}}uqqrsuwz}|~|zz{|}~~~}{xtqoorwyyvtrprv{}{|}|{yussw|}zxwxxvsrtx|~{xxy{|~}xssy}zyzz{}}ztonqv}{z{|}zurru{}{z}~}~~~}{xvvvvvvuqnmnrw{}}|zyy{~}|{zyx{~|vqoomlklou}|z|~{wspoquyz{||{xurqu|}~~|yz|~~|~}zz{~}{~}ysommlmnoqqpnnt}~~}xvutrpptyzwrolkmoruz{xy{|zyyyvqmou}~}~|xvy~{xuuwwwx{~{{}yttx||y|~zxy{{xsnklptwyzzxuu{~{y{zwvvurprw}}wrqpprtw{}~{xwx|}yz}~zvtuy}zwuvvustw|~{xuvy||{yyz||{~~|||||}{zy|yrnnquy{yuqoqv|~}zyy{~}}|zwuux}wmhhjlnoqsx~~}~~{|~~xrnnptwxwutuvwyzyz~z{}{{|}|~}{|~|~~}zsmhgimquvutsrtz~ytqppppruxuojhjnswxx{ytsuxyxvtrppsy||{zz}}y{|yy{}|xxy{|||}|ywvy|~}{z{}}wvz}}|zzzxsokjmty|||}~|xy~{vrqrstuwy{zwsqrtvwwxz~|zy|{vw{~~}|}|yvtvxyzxwwy|{xuu{yurruy~~|~~|zxz~~zvtuz{uu{}{zwspquyz{{zxtqrv|~~~zxzzywuttvwsnlmopopsw{~{yz{||{xuqnkknqtvwyz|}~}}~{zz|~~~zxy|zz{~~||~~yvusqnmmnqvz{zvsrwzusssrokhkqtspnoruy{||ztppsvyyxtpllqvz}~}~||~~~~~zxy{~~~ytrsvz}}yvw{}|wtttuutsqru{~zwy}zyz}~|xutsssqoorw|}zvsux{|zxy|~}|}~|vtw{~~ztnmpv{}|yxz~~xuxzy}~zxwwxz~|zyyyz}|xusuy}xw{~{zz}~ztsv{~}{ywutuz|yy{}{zxtqoopuyyuonpstsstvzzuvy||zvqmjhhmtxzyy|~}}~|wvz~|{{{{{}~}}~}~~{|}}|zyxyyvrooquwyyyxy{~}wrolkjhimtxxtrswyzzyz}}}~}ytporvwuqomlnrx|~}~}|~{}}{}~}}|yxxyy||~{ursuvvwxyyyxwwwxz}~~{ywvwz~{uolmoqrsuy~{xxxyyxx{}zyz{{|}}zwwyzyxxwtrqswz|}}}}~~~{y{{vv{}z{}~~}|{ywtsvz}~|zyz~zsqv{}ywvxyz|~~|yvw{{wvy}ytrrrstttuy{yurppqswz}~|wuutrqqppnllou{~~}}}}~}{xvx~~~}|{zxwwz~}vuy~~{y{|{vqnpty}~|{~||}}uollmnnmmpv|}yutvy|~|xwy}~zsmjloqrsttsruz}~~~{xxy}{{|~}wroqvz{{z{}~~|yvwz|y{|xvwwxy{{zvqnortuutw|{xwz}}|{}}ywvwz}zspptwz|{xutvz}}}||~}~}zz~~}}}{xutsrsw}~|~}yutx~~zxx{}~}yursw|}{yxy{}~}zxvvvuuvvwy{zvssuxzzzy{}}xroopstutrpopswz|}|xxz|}|y{~~|{||wuuvxz}|||y{~~}|{xtsuy|}}}||{yy|}|{{||{xurqpopqrsuy||{|}~}zxxyzyuojilqtuuvxyxwy}z{~ywz}}}|{{|}|{{|}~}|}|xtsv|}}~~}~~{wvx|~}zxurqrvz{zwtsuxyyxx{~~}zwvwz~~}{zyxwwxz{xtrtwz||}|zwuvx{{yz~|}~}~{vuvwwvvx}~}~}zz~|zyyyzzxtonrx}~|ywvwy||yxzzyvssw}|xvvvuuvx|~}{{|}|xtqqrtwyxtpopu{~yvwz}~|yyz{|}}xwxz|}}~}}~{|~}~{wutuwy{{ywuvy~yvvwz|~~|xutsrqqqru{~{z{|}||~zurrtwxvspoptx{||{yxz{wy~|{~|zz{}~~~}|xutvz|}|}zwvy|}{ywusrsvwvrpnnoswz{{yxwwwwvvy}{xwvusrtx|}zvsrtx~}xwz~|{}{y|~|zyyyz{}~zvsrtw{}~~~}yxy|{upnoppoomkjjloqrqqrtvy{~|xvuuttuw{|vrqrrqqrv|~{{{|~ywwy{||{zz{}~~}{yy|}}|wvwz}~}zurquz{vtvxz{|||{xtolkjklnpssqnlnruvtsstuuvy{zwrommortuw{|yxvvwxy{|xutvy|~}|zxwx{}~~}|}~zz}{wttx}}}~~{vplnsx|||}}|{wtpnnoqrsrpkgdgknoonmmotxzzxvuuvxzz{|~}yuqooprtvxyyyxvwx{~}|z{|~|}~}zvrpnoquy{}~~|xuv{~|{xrkffhklljgcaaekqtuttux{~{xxyyxussux{zvqnnopqsux|~}xwz}~}~{yxxyz}}xuuvwvutsrsvz}}ywxz||{yxvuspkgegjmpqokhgiouyywvvwwwxyyxvtstttrqrv|{wuuwwwvuw{~~|zyyz}~zwy}~zz~|yyz}~~}{zzz{||{yxxy|~}{xutuwz{zz{}|zuqnnnpppoljfddinrsqqqstuxz|}~~}{zyxxy{~~{vspoonmmosx|}}{z|~|}|y{~}}{xvvvuuutrqqu{}{|~~}{yupkgfghgfeddcdhmrwz{zyy{~}zwtqpqrtvwvsqonmlmpty~||~~|{{|{zz{|{|}~|uonpsuwwwwwx|zvuvxyxvtrqomigffhjlllllmpuy|}{ywvvy}}xtrqpppqsx}~zwusrqqsv{~}}~~~~{yy|}}~}||}~~yuuwz}|zz{|~}xtrsuxzzzzzywtpmjijkmnmjgfiouwvtssuw|{xwwwvuvy}}xqliijkmpty}|xxz~~|}~yvtuwwwwwvutuw{~ywy|~}{zzzxtnhcaaceghihfgkry|}|{{~~zvtqnmmosvwvrmjijmqux|}|}~~|ywx|}|}~}||{zvropsuwxz{||{|{urrstuutssroidbbfknqqpopruy|~}|{{{}~}yusqommmpuz}}yurqqrtuwy}~~~|~|{~~|{zz{~~|zxyz}|{{|~{xtqopsvy{{zxtpmjiiloppnlkklptwwxyz{|}~{wuttttvxzzxsnkklnnoqw|}zxxyz{|}~~}~{wuuwy{zxuttx}{vuvy{|{zxtokfdcdfgiklmlklqx~}xsommmmmnprrpnmmoqtw{~{zzzz{{{yxwy|~~zwwxz||xtrsvy{|}}}}~|tnmorttsqonljhggjmprtutsrsw|}{zz|}|xtqonnnnoqswyzywvutuwz|~}}~}{{}|zz|}ywvwz}{|~~~}zvqnmptwyxvuttsqnllnqssqnlklqw|~~|zyy{~{xvvutrpprvxxvronnpqsux|}~~{wsrsvy}~|{z{~~~~{xwwxyzzyyz{}}yvuuwxwvutrqoligfhjmoonmmou||xtpmkihhimrvvtqpruxz{}zwvwy{{yvssuz~{yyz{}}}|zwvvx{~~ysommnnnnopqpolklnqstuuvvvx{}zyxxxxxwvspmkijlosx|~}{zyxyyyz|||}}{zyz}~}{zzzzzz{{{zz{|~}|}|{{}|{zyvrnlloruwxxyxwtrqqqrrrqqqqrtwz||{zxxx{~}yvrpnnnpswyyxvtsrqqruy~~}~}{xvtstuwz}~{z{~}|{{zz{~yvwy|~~|zywxz}}ytpoqsvwxwurqomkjjknprsttux|~}}~~ztnjhhhikmquxyxvuvwy{~{wuttuvvvvxz~~||~~{{|~}zwwy}~}|voighknqrssstsrqpqrtwz||zxwy|~zwuuvwxwvrnkijknqsvz|yyy{}}{yz{|~~}|}~~{xutuvy|~}}|yxwxy{||{zxtokijotxzz{|}|yvsrrtuvvusqppswz{ywwwy|~~|yvtrqqqqrtx{|{yvtstvxz}{xxxxxxxwvwy}~}}|||}~|xvvwz}|{{||zyxwvuuwz~}zwsppruxyyyxwvspmlmpsuuutssvy}~}|||{yvrnlkkkklotx{{zyyz}}wrqruwyyyz|~~~|{|}~|{|}}{z{}~{wspmkkmoruwxxwvuuuuwy{||zxvvwyz{{zxwvtsssssqonnopsvx{~~~~}|{}|zz{~~~yvvvvwy{~~{wtrsvxyyxvvutrpoorw{~}{yxxwwvusrqpnnoswyyyxwwxyz{{zxvtstuvwxy{||{yxwwwxy|~{zzzzzz{{zyz}|z{|~~~|yxxy|~|xwwwwvusrrsuy|}}}}}|{xusswz||zyxwwvtrqqrrrsssssux|}zxxxzzyvspnnnnopsv{~~}}}~~}zwuuwxz|~||}}ywx{||{zyyyyz{{|||{ywtqnnorvxyyzz|}|{ywwxyyywtssuwyzyvsrqqsuwwusrstuvvx{~|zyxxxz|~~~~~~}~{zyyyz}|{{zzywtrqpqqstvxyyxvtsuwz}~{wtsrrqpnmklnrvwwuuvwz|~~~|zxwvvvvx{}yvtttuuwz~}||}~~~|{{|~}}}~}||~|xusrrrssrqooquy|~~{ywxy{||}~~}zwspooppppqrrtvy{}}|{z{|}||zywtrpoopsw{~}|zzzz{|}~~{zz|}|{}~zwuuwxyyywutsux{~~|{zyvsqrtx{~~|ywvvvvvuutsssstuvuuuuvwy{{zxvvvwx{}}{xvsrsuy{}}}|||}}{z{~~~~}}~}ywwwxvtpmkkmpsvxz{|||{yxy{}zvspnmnnnmkjkmqtwxz{|}|zyyzzzz{|}}{wtrppqsuy}}~~|{z{}~zuqooooonmllmqvz~~{{|~|yvspmllmoqssrqqtx}~~~{yvtsstuvwy{~}ywvvwyz|}}}|ywwy{}|{{}~|yvutuuutsssstvy{~~|zwuvy||yussstutsqonnptwyzyxyz|}~}||{||}|||}{wtrqpqqstwy|~|yvuw{|xusrqonmlmnoqtx|~~~{wrnlllmljhhhkotx{}~|zxvvwyzyvsqoooooqtx}~~{yz|~{{{|}~}{yuqmjijklmnoqsw|~}{xtqomlllmnoppqsv{}zxutsstvx{}}|ywvutttux|~xtqrtx|}||}~|{{{|{yvspppqsuuuuuwz~~{zz{}~~|zxusrqqppppppqruy|~~}}~~|zzz{|}~~}{xspmkklorvz~}}}{xussuy|wrpooooonnnoqtx{~}zvqmjhhhijkmnpsw}|ywusrrtvwvtqnkiiknsy~~}||}}{zxy{~~~~~~}{zxtokiijlnnoprux{~}~~}{zxvsqnkhgilortuvw{|xusrrqqsvz|}{wsqqqrtwz~~|yvsrrtw{~~}{zzyyxwusrqqrtuvwxxxz|~}{yz{|}}~~|zvronnoprttsstw{~~|zxwwwy{||zvqmkjkloqty~|{{||{zxxwwutssuz|zxwy|~}|zxusqppppqqqqqtw|}ysnjgghjlmoprux|{wsqppqsttsqnlkklnrvz~}|{{z{|}~~}zxy{~}|}{xvuutrpmllmoqstuwy{|zz{||zxurnkihiloruwy{}{wtqooopruwyyxvtssstux|}{xvsssux|~}}~}|{{{}|zxvuuvxyxwuttvxyyyxyz}~{zz|~~{xusqppqsuwyyyyz~|ywvvvvvwxyxwspnllmorvz}zxwvwwxxyyxwvuvx{~}zwussuwyzz{|~}{{|~~~|yvtssssrrrsux||yvrnljjjlortvwx{|xtqonmmnprssrqonprvz}~{zz{{{zxwwx{~~|zz{}~|{{zzzz|~}zwvvwxwvtrqqqrstvx{}~{yzzzywvtsqonlmorvy{}~}yvtqpnmmnqux{|{yxvvvwy{~~}||}~|{ywussuy~~{ywuuuvwyyzz{}|zwvuuvxz|~~}|zzz{{{||}}}~}{zz{|~~~}{zwusstvxz{{{|~}zxutsrsux{|{xusqqqqruy~{yvuuvwxyyz{{zyxwxy}~|zxvspnnorvy{}~}~~~{xuttuvwvvvwy|~|yuqommnpqtvy{~|wrnkjkloruwxwvuttuwy}|yxwwwwvvwxz}~|z{}~}|{zywuttvy}~|zxxxy{{zxvutuvxz||||}|xvvwxyyyxwtsqqrtvy}~|xuqnlklorw{~~|zyyz}}|||||}}~~~|zxvtttvz}|yurqqrtvxyz{|~}|{{zyyxxxy{~~~|{yyz{}|ywwy}}{ywvvxz}}yvtsssuwy|~}zwtsrsvz~|zyyyxxxxxxy{|}}||{{{{|}~{xurnlklnqtx{~~}~|zzyyxvusstvxz}~}~}zwtqooqtx|~|xsomlmnoquy|~}{yxyz|~|wtrrsuwxyz|~}|||}~~|zxvuttstux{~~{z{|}~}||||||{zzz{}{xwxz|}~~~~{yvuuvy}|yurpnnnpsvz~~~}~~{ywwxz|~}|{ywuttvy|~|ywusrqqrtxz||}}~|{||}}}||{{|}~|{yvttux{}~}{yyz||yxy{}|zxwvvvwz}~|zyxwwx{~~zxwxxyyyz{{||}}~~~}{z{}}yvtrpnmmmorvz~}|{{|~}{ywvusqpprtx|}yvtstuxz|}~{xtqpopqtx{~~~~}yurppqsvxz|}zxwvwwxz{|}~~|{ywvvvuuttvy|}}}}|||{zxwvwz|}{zz{}}|{{{|~}{zxwtrqqtwz~~{zz{}~}{yyxyz||yvtrrrsuxz}}{yxvtrrrtwz|}~~|{|}~~~~~|zwtrqqrtvxz|}~~}|}|{{{{|}}}}|}~|zyyz{|~|xvuvwy||zxxxyz|}}}}~~|zyxxy{}}|zxwusqoopqruy}~~~~}|{{{|}~|zwurponnoqtx}|xustvx{}~{wutstuvy}~}||~~|zxurqqtwy|~|yvsqrstuvvwxyz{{zzyxxwwwxy|~}}}}{{zyxvutuuwz~}}}~}{zz|}{zxwvwx{~|yvuvy|~}{zz{|}{ywtqonoqsuwy{}}{yxvtssuy|~||}~~|zxurnllmptx{~}zxxxxyzz{z{{}~|||~~{wusrtw{~{yxwxxz|~~}}|{ywvuvxxxxyz{}}}}{zxvtrqqqsvz}zxwwxyz{{{{}~{xvtrpnljjmqw}|wtrrsuwz|~{xvuvx{~}zyz{~}{xvuttvxz~}zxurpoooopqsux{|}}~~}|{yyy{}~{yyy|~~||{zywtrqqsvy~~~{xxx{}~~~|zyz}~zvtssuxz}}||}~|zwtrqonmmmortwz}}zxvuvwz|~~~~~~|yvroljjlosx|~yussuwxyxwvvx{{xvsrrsvz~{xwwy|~}|{zzyxvussstuvxy{|}~~{xvtttuwy}|ywvvwxz{}}}~}zwurpnlkkloty{vqonpsvy{|||}~|zzz{}}{ywwx|}{yvuvwz}~|zxvrolkklnoqsvx|~}}}}~~}|}~~|{zz{}~|zywvutsssvy~~yvuvxz|}}|||}~{vronorv{~~~{ywusrpnlkkloruwy{~}{yxwwwy{}~~~|{{}~}~~}}}zxvsqnllmptz~|wsppqstuuuvwy{~~{wspopsx~~{zyyz{|}~~|{zyyyxvtsrssuvwxyz|}}{yxwvvwy}}zywwxz}~{xusrrqpnnorv|zuqnmmoruy{}~|zyy{}}zwvuwz}|{zyxy{}|zxvspnlllnoprtvy|~}||}~}{zyyz{}~~}|}~}{{}~||{{zyxwuuvy|}xtssuxz|~~}||}~yuqoopsx}~~~}{xvsqonnnmnnprux{}~zwutuuwxz{||{zyz{~~|zxusqpopsw|zsnkknqtvwwxy{~~~~{xvsrrsv{{xvvvxyz{|||{zywvuuuuuvuuvwyz{{{|~~{xuttuwy|~}}~~|{zz{}~|zxwvvutsssux|zupkijmrw{~~{xwwxz}|zxwx{~|ywvwy{}}zwvttsqpoooqstuwy{~}{zyyz|~~|ywuuvx|~{yy{}~|zxxy|}{{|~|wsqrtx{~~~}}{xurqruz}|{zz{}~~~|zwtqonoppqqqsvx{|~zvsrqqrtvyzzyxwxz}~~~|zxvsqqsw|ysnkknquxy{{|}|{z{|}~~}{xwvwy~|xusrqrsvxzzzywvvvvwxxyyyyz{||||}~~zwtrqrtwz}~}|}~}{|~~}}~}{{{{zyxwwx{}|{}~~|xtpmklpuz|xutuxz|}||}~|zwusstvx{}}zxwvvuutttttuvwyz|}}zxvvwxz{|{zyxwvvwy|}xvuwz}~|{|}}ywwxz|{wtsux|~|{|}}{xvuuy~|ywvvwxyyyyxwusrqqqstuvwyz|~{uqpoopqsuwyzzzz{}~|{{}}|}}{ywuuuw{~~}xsooptx{}}}}}}{{{|}}zyxy|}yvutttuwyz||||{yxwvvwxxwwwxy{||||}~~zvtssuvxy{~~}}~}~~~~}~~}{zxvustvy~~~{uqoqtxz|{{zz|~}}~}zwvwz}zxvuuuvwy{}}{yxwxxxxwwwwyzzzyyyz}~{xvuvwx{}~~~|zyz{}~|{zzyxvtttw{}}}~ytpopsw{}~~}}~|zz{~|ywwz~}|zywvvwxz|~|ywwvuutsstuvwwwwxy||zyxyz{}~|{zz{}~}~~|}~~|yvuux|}{z{}{vsrsvy|~~~}|{}~}}}zwuuvy~~|zxwvwy{}~~|ywtsrrstuuwxy{|||||~|wustuwy{}~}||}~}}~~~~|zwusrtw|~||~|vqnnptx{||{{{|~|{|~}{zyy{|yvtsrsuwz|~~}{zxvuuvwyyzzz{{{{zzz|{wtstvx{}}{|}|{{}~}~~~~}{zxwwx{|zyz{~{uqnnqw|~{xxz}|zy{~~{yvttuvxz|~}zwutttuvwwwwxyzz{{{~}yvuuvxz|}~~}{yxy|~{z{~}zxwwz~~{yxxy{~}wsqruy}}||}}||}}zxwy}}zxutttuwyz||{yvsrrsuwxyz{|}~~~zurqrsuwy{}~|||}}||~~}~~}{yvuux}~|zzz|~{uqnorw{}~~~}~}zyy{}~|{|}zwtrqpqrtwz|}|{ywvvwxz{||||}~~}|{{}zvsrstvxz}~}|}~~}~~~}{{}~|zyy{~~{xvvwy|~|xtqqsw||xwwy|~|{}}{xvtssssuwz|~~|zxvuvvwxyzz{||}~~}}}{wtsstvxz|}~~~}|{{|~}||}}yxz}}zxxz}}zxvuuwz}{wttuy}~~~zyz|~|{{}~|zwurqpqrtvyz{zxvuuvwyz{|~|wspopqsux{}~|{zz||{|~|ywwx|}ywvwxz|zvrqsw{|xvvxz}~~|ywtrpnnnpsvy{|}|{zyyy{|}~~}|{{}}xtqppruwz|~}||}~{yz}}zyz|~zwtsrtvy|}yutuz~yvuvx{~~~}{zxusqppprtw{}~}{ywwxy{|}~~~|{|~~xtqpprtvy{}~~}{zyz{~|yy|~{xvwy|{wusrsux|zvuw{~~{xwxz~~~~|zwtqonmnpsvy|~}|yxwxz|~}{z{}~xrnmmnpsvz~}|}}~}|zxwxz}~}}~{wuuw{{wtssuwz~yustx}~zvuuwz~~|zwtromllmpsw{~~{zz{}~|zxwxz}~zupnnpswz}}}}}~}{yxy|~|}}zwuwz|xtrppruy}}xvw{~~}yvuwy}}|zxurpooopruy}~|zyyz|~}{xxy|~ytpnmorvy|~||}~|zxvuvy}~}~~zvsstx|}ywusrsux}~ywx|~|{{|}{xwx{~~~{ywurpnnnoqtw{~|zz|~~|zywwz}{vpmlmprvz~~{yyz|}}{xuuvx{}ytrsuy}}yvtttux{}yxy{}{{|{xwwy}~|xusrqponnorvy}~}}~~|zxwvvwy{}}yupnorvy}}{{{|}}}|{xvvw{|yvttvz~~|zwsqqswz~{z|}zxxz}~{yy{~|z||zxvutsssrrsux|~}||}~~}}}}{yxwwy{|}{xuqoorv{~}{{{|{zyxxwvuvy~{ywtrqrvz~~|zxvuuwz}zxy}{xxyz{}~|zz}~{{~zwvwvusqqrsttvy}~}}~}||||yvsrtx||zwspprux{}zxwwxyyxxxxyz|~}zvtrrruz~{xwwwwwz}~zxx{}zyz|}~}}~}{zz}}yy}}zwuuvvutsstvvwy}}{{}}}||||}{zywtrrsw||vrrux{|}}zwvwy|}|{xxz~}ysompuy}|ywvuvx{}zxz}~}{yxxyzz{{}}|~|yx{}|{zywvwxxvtstw|~ywxz|||{{}~|xtrrtwz~~|xtrsux{|~}xuuuvwz||{xwz~~}ztokjnu|~zwuvzzuux{}}||{zzywvw{~{{}~}||~~{zzywvwwvtsrsx~}{zyyy|~~}|{ywtpopty}}|zxvuttx}~wrrsw{}}|{|}~~~xsponot{}zxvtw}}zxuuy~{yxxxxy{zwy~|yy|}~zwxz||xttuvw{~zxxwuux{}}|xtrrsstux}zvuvwy|{z}}yvtux}~}||}~~ysolmqvy}{wvvy}~{wuvy|ztrsux{}~~{}}wtuy}{|}}|yvtrtz~zurrv{}||}|xussswyyz{wrqsw||}~xuuwxxz|~}zy{|ywusolmqv|~zxy~zsqrw{|}|zwvvuw|~|~wsty}~~zwwvvvvwy|{ursw|~~{vrrttssuvwz}zwuuz|y{yuuwz}~~|z|}zwuvutrrt|~|||{|~|wssw}{wuwxy{}}}~wrstw~~}||yvx|}|{{}{usuuuwyzwurqrtxzyuv|~zyz{}}}~{{|}{yyz~zz||~{~{{}|zwtqqqty|}~{zywvwxyyxusuy|~}{wqprx|z|}|||yxzzxx}|vstuwzzusvwvuwxxyy{}}~{xy{|||zy{~~~}}|xw{|yy|ywxz{|ysnnt{~{xwvuvursvxz}}|}||zusv|~zwx|}xutuy|yvuvwvuusruyzusvz}}|{}{{}}{z}}}~{wyzx{~~zy~|{|}~~~|wtuvy~zyy{}~}vsttuwxy|}||}~zvuxyy}~||}wssw~~|yyxtnmpsuvvvwwx|}z{~~zurt{|y|}zwvz}}~}{|xy~|{~|vtwxtrw~~|z|~xqllpv{|{||||y||wwxy}~~}wrruvuvy~~xvspqsrrx}}yvvz|ywwz~yvtruxxzwuvz}z{~zx{~||~~}{wurtz~~z{~}xw{~{vonnmry~~zxz}~{ww~xqoruux{}zrmnrtwyyvux{zzyxxyxywtwzyutw~~wuwuu{~~}|}{{~~~~vqqu||vwzzxxy{voortw|~|{||~~|{}{ww}~{zxvtpot}~yuqmjlsy|{uty}|xw{yvtpnqtuz{tttv~|~}x{~~}~{utsopz}|uw}|xsnlpw}{y~||~}wy|zy||wtsrplls{|sqpptxxy{||}}{y{|zyz||{yyrkkpw~~zww{}yy}||}}}wtrrw~{xwyyz~xwxursrsvxtqu}~}{z{wollqy}}~|xtpqx|{xvwz~|usvyz|~~zvrpquw{~{yvv{|z{~|z{{vty~~xtuzzstwz}{y||{|vx{yut{~zzyx|~yvz{~yqmorw|~{vuvvsrtvuw||yxvx|}{~~xojlsusuwxz||wy|~}z|{zyy}}y{{{{xwzxuw}}}}{{{xz|}vomv~~{y|{x}|}wnkospnsyxx{xuwy|}yuyxwww}|yy{{z~wtrooquzzxx|~z|~yz~~}}{ux{|{y}|||{{z~|yurvyww}|sntz~~ywy}}{yz}|z}|}yvppsrtyxrs{~}|}zvvx|}ztsxxtwxvx~{y|{uqrpouxxyz{|}wv|yz}vpot{~}~|~{{|}|{wnostzzxyywxx{}usz}{{}xw{{}}xsqppopsrquz|slpx{zxttww{ypmoldekpt{~zvuy{uv}}zywvxz{yvx|}~yvttqqu{}ywsrtttx~}}uqsqpv~}{xsrsmddjnpwyyyuu}{vy}}y{|vwzx|~xpjeekprtz|y~|yz~~|ytx}}|z|}}|yrnjjpwxz{|~}topu~zrnpqstw~~~~urqjinolp|ywz||wwusu}~~}{z{xxylefeirx{{tt|~xw}|{}~yvx|{wvxyw|zx}}yvx|{ytrsnksx}zssux}yvqow~~~vlhgjmpvyvsx}yvvwyyw|~{wuv|vidgeafpwxxz|}zvz}xx}{wzyx{wz}uuyxuwz|ystuvwxzx{vprusz}vxvqtvvx{~}wojkgbdmtwytw~zx|}~upsuvy~{qifdfpwxsv}{zy~~~wt{~~|}~}|xqu~z{yurprvysuytsv~vsqsvzztu}sjkiiouutw{{~{{}|{{uw{ypotx~{{xphilpurt||{{}|yy~}|}xvxusywy|{xttxwuy|ssvvuyxttrzzz~}{x~~~~}|zy~|y}}wrljjpvursvz{xy{|~|}{~~|{xvwww{zvnhlojhipyzy}~}{{~|tnrvuw{}~|qorvwxvwyuz~xzzsw}yz}x{}|{||wps|~}toklnlknoow~}||}~zwxzwrpswtqsswyzuoqqonqwy}~~~z|{vx{ws{~|{{{y{|xuy}zstww|ypp{~{z}}}}~}{~|z||ux{{~|wz|yxplpruvsrx|{wxwuxxwuvyusstvvxxy}|upimxzzyuvzytrvxv{}{y{}}yw|}wuslipvrrz}xvy}|~}y|{z~|~}x{{}{||{}|ywvuoovy{ytt{~ztvy~{vu}{utokswwz|yurmkmrvuw|yz~yttsons{}{w|}ywvuusoijpqx|}|yzspu|~}{ut|}{|~yttvwnipwywxwy}}y|{tvxy{wppqlfhlszzrqswzwru~~rrssw~}~||~}wpnqqnqx}}{yyyxy~}wx~~}~{{~{xtz}~~zy{~~}{wspv}}wtz~ytw|vnntzyxvpjhkosw{zyxtsttwxw}zvtsu~~zy{}{}vmkgeitxxyzwwy}|xw~~z|~|vtz}}~{~~|y}|}xtwwzzuvwtw~|xtru|yv{zpjkijt{~ywz~ynjotu{|ursuuvy{y|xuqnlmjgnuy}}|||~~zvrsw}}~z||{zwv}|wz}|tv|xsqsvxz{vssv|zxwofgkjfjsyz{~~ztvywt}{z~~wruy}~z}~}}xsnmorssut{zy|~}|yxw{|vv|}|y}~wppxyx}{y|{zyzyv|wrtvtrrvupmihhksyytx}xuvxy~||~z}~~}z{|yz~yx}~ysvtpmkknrprz||~vvy{}{yyyxustz~~umptxzux}{{~|~xsvwwyzumospkloquuv}}{||vru|~~}|{|zx{~zsu}|{}sooppqsokkp{}tquxxz{usvxz~|ywssw~~zutvz~~{xssvrnnkfhpqmjmv|z|~|~|~}z||}zz}|xy}|~|~xuxywstxwpmrvz}|~ztw{ywwwv{}{upmpwz}{z{~}~~{yxtokilnnmknpoouyx{~xtv}|y}z|{zz{}~}}{xwtwzyxqiiqurqw|{xvsmnrvwwz~~}|zysnrx~~|}yz}~~vtspoqnlpuurnknvzyxy{~~}~{xtw}|~ywz~}z~zxz~~}}wz{uruyytqonopr{|tssnnqpqu{}|y~|{{zxxzyzvyysljoqoppommnmnpstz~}xz|~|yy|{vswywy|~~~}~|yyw{zsvyvpmou~}z}|tkmtvux|~~}|{}xuuxxty}}~~~xuqmihjnqtskjnquust{~ywvvvxxy}~}uquwz|z~~{{|~zxwsmknqrv~zwroomkpy}yvz|}~~|wssyzvx}}zzypmnoqrqswtqpqpnrwy{}|zy|yssv|||{zxvwz{z}}|}~{z~|wvyzytkkpsy|rnopnopsxx{zx}xuwy~|}|yxyysqnmrvvsmknqpijqwy{}}zwx{z{~yusstxy{}~}xvuy~~{}~~~zyxusrrv~~{xyxrnrwywux}}{tuywtv|zz}ztsurllptqpqnmllrxxx{trv{wstwvvyvrtx~}{zy{|}{||vrqrtvxy}|xwzytrrporwyyz|}{{~|wwvuxzvx~~yy~|{yusqsxyrotwuolorw{~|}~{yvvtsxzvtw|{yy|||zsw}}}~{z{{yxzvruy{~|zz{xtstuvrlouy}}||~~z{|vsv~~yy|{|zy}wvvvvtsspkioroqx~}~~~}ysrqqtxywtty}yvzzsrux{}|~~yz}|zwx~wvz{|}{xvusstuuy}zz}xvvvw|~ywy{z|~|wwz{tnnrsrnjnqryz{}zrpsoilrvwyyyxwxyxz{{z}}}|z{{yuuxz|}}}|z{{tnmosrsy}|~{wyzz|~vux{~{xxvqpw|~}zz{xuvwtpqtvtsvz}}|}{ww~yqnkjpvwuw~}xwy|~|xy|||}wzyvwy{zx||wwy||z{|zvtsnkmt}yvsuy}|y}~}{||uutrv{|{~zutwwrpqqpuzwsuy|{xvz}{tlkmpuwy|zuuyzwuz~ztw}}~}}}{~{ywuvz|}|y{}yxz~}wvwtooux~}zwwx{~}~~|wqot{}|~|{xtqppsvtrvxwy||xwz~|tkjonkmsz}{vtwwx{~{~z{}|~z{|vopw|zy~|~}yskkrww|yuz|{yx{~~yuqqqrtw{}yyyywttvx{}{vtw|{wvwxz~{rlmnnpvzyy{|{xuu~~|}|yx}|yurv{|z}}}}xx~yy|{wropqu|uuwxxz~}||wopsvxy{}|xtw{zxxywvvttssvzzwx}{xxupmmt{zwvuuuuwy|zxz|}zw|vsuy|{}|~|vx}}|{}~|}|vwzxtw|~{|~zv{~}{}}xvutsw~|z{}zttxyz~}vstwywuuy}}|~}yvsqonoqrsvxwrrx}{y~~{z{~{vxxutuy{y|zy~|}|{||{zusx|xw{}}|~{vx}|{||uuwwsqv|||}|zxw||~~zwwuqty{zwuy}y{|{|~|tnosvusruyxvy{{~}~yx{}zvxyyz{|{{}~zwy~}z|}{yx|~zxywv{zw{}zwvz{|~}}zvssy~}yyz|~~}wtvxsnouxxyzzwvz~}|~|vvuqpsvvtptz|}zz}|yx|}~~{xw}zwwtsw||{{~xxz|}~wuv}zy{}{xzyy~~}wvyz{z{|{wx}~}{zwsopvzzvw~~xuwuv}ywyxrllpsrtxwx}~|{}~~z}~~~|xxxuuwtorx|~~}{x{|wwwwuv|{yxvuz}{wz~ztsw{{yy}~~{}zrqtvyzz}yrrvtpt{~|wroqtrnqz}|{x{~|z{}{vtrqrwyzyz|z{{z{yuvwuw||{{{vrw}~{{{xvwyxux|squxvsty|xtrolow~|~}uqppqqvywz|}}~~}~~|vwxsnosvyy{~yuw~}ztpt}|zywy}|~}xuv{zx|}{ustuuvz{usvunmquz}ywwsmkrwx{~}zz~~}||}ztspmptsqqw}|{z~~}ztrty~}wwz{|zwvtt{zy{{yutx|yuuqjlsvtsy~wvtoouvsy|z|}{~~~xwwrmmqttrw||~~yzwrsv{{{{||z}}y{~}||ywwxvvz{~}|zyyyyvrtupkknpqs{}vpqvxy~{x{{y~~|~{|yxspruurnpy}{xz~}~}||wwwxz~~~|}|z~~~~wu{|{wtzyuwyvttokmqrpnox~}zxywtx~{xw{|y{z{}~wopsuspqtttuz~}x|}{}~zux~}}wv|~{zxw{}wx{|}|{ztqutonppppqvxy{}zuyzx{~zwx{}{vw|~~{}~{upsvvplqwvsqsx}}~}zwx}ztx}zwz~}z{zuw{{wy~~~}|vsqmmqsqkjpuvwvw|~|~~zz}zyz{yy{}~}{z}~uruvxuswvrqsvwvx}yz|yxzxtv}}||}xy{zxw}|~~}}trrplmnopnorrqrv|~||~zz{~zrsw||~|wz|yuqtywsmkorrw|ttwusvxzzy~{}~wvz|zwz~z}zvtmjpuurmotusopuxy}~|yxzxvvx{|}}}}}~xwxwuswvrpooonmt~yuuslltzzxx~~yy~|}|wv~}||~{zwqnpqrppsqnkkosrry}~xuw{}}wx~|~~{y|}{wsu{{wroooosxz}xuurpswwwx~xy~{}|xxzz{}|~wnlorrmjptrmjknps{zxyyxxx}~zz{yy||}}~~}{z{ywtrroijquy}~~yqmquwuru|}}~~}|{}xv{}zww{~}~~|tpqqqprvvrmmpqmnv|ytw{|yx|}|~}xy}|}}|zz~yuwzzwqnoolmqru|zwqoqpoorxzzyz|{}||~}zy{}~{||vwyxrnqvurmjjjinuvw~~|z}}xz{yx{{zxz~zzywwy|~{y{{xvvwupnruwz~~}}wqrvwtqsxz|{z}~~xssvx{~ywvsoossollmmihmtx}~~zyyuuxwrrx|{xy}{vuz{|}wuvvrrtqpuz{||}zxwusqtwtrqty{z}}z|~}~|zwuutu~~xy||wroswvsponlmsttx~{{}|usttux|}{x{~xw{~~|}|xuuvtontutuxww|zuvwwtnnqsrpqtvx|{zyz~}|{xxxx{|}zxzxurqqqmhjoptz|yy~zwywqrw{|zwy|||}}vtxzyuv}}xuuvvvz{ww{}zxy}~~zyzywtuyxtqptxwx||~{|}{wqqx}}{~~wuwvspmlkkloposz}}~ysqnmpuvtsw||zxzzxxyzz}}{{}yuv{|{{zvrsw||xz{|}wvyxuqonnns{}|zy|{y|{xyyuuxy|{zzyx{}||z{{xtqrtsoossvz{yxz|}~ywtnkntxyx{}|||}~~~yw|}xvvuswzxw{}xtsuyz|~}~}zwuxyuokmommry}{zyusvy|{z~|x{}{xuw~~~zusrqptuqquvtuvyzz|~zuqsuuttw|~zutvyz}}yvw{ywwxywtuz~zx{}zvy~zvx}~{wxzzyvsromrz}~}zuvz~~|z~}}|zz~~|{|~|vsrspmnrsuwvsrv}}|~zrmnpqpqtvwvuvvvx~xw|~{zzzy|~}yvwurtx{|~}~}}}{zvutrnmqsw~|wx{|{z|}|{{}~{x{~}|utwz{yx|zvtsqsxzxxz{yxwz{zz~{wsrrqomry|zxwxz{|}{zz{yuvz|}|yz{zuqrx~|{~~zz|zxvtqllpssu{}wvxywst{~|zy{~{wvxyy{}|}{uuyzz|{vrqtwvssw}}ywwwurtwxxwwurrw}}~~{xtstuvz||}yyyz|~~{{ywy|{yx{~}yvvyxutvvw}~yxyzzy|~|}~}~||{}{y~}zwusvywvyzwtsuvuvz~~||xsnmqtspoqtssx|~|yz{{xy~}ustwxxy{{{{zwvy~}~{zxvxzxuvxxy|~yx|~zvvxyz}|~|zxuv{}{yy{{wux{}{xy||wtw{~||~~zxyzwroqrrtuvwuv||{zyz~}|~}~}ywvwwx{|{|{{~~}zwvwwuuvvvyyvuw~~~|yvxxvvvvwww|~{}~|xux|~~}||ywvrqsvwwx|~|yz{{~zwyzwsqsuuuwyy|{{zvsv{}}|~~{|}zyzxsswyzyxz||zxwwux~~~|z{|}xx}}zxyz{~|||~}|}|z|}}~ywyzwrqsw|}~~{|}{}|ywtqnnonmpwz{}}~~|{z}}zwwyxsqqqrtwxxx}}{|~||}vsuyzy{}~|zyywtv{||{yzzxwwwwx{{xy{{zz|}xutssx|}zwz{xsonprsw{|~~~}~|z}~}zyxvuvvtsvz||{}}{yvuy}~~~}{xwwwvw}|}~|xussttx|}}}~}zwwz}}~{utuvvwxvsstsqrw||ywvsqrx|}~~}}|}}~~~xttrrtutsrv{|zvvxxwx|~}}~}zy}~{z{{yxz~}{{{|}ywwxwvvy|zwvtsuz|||~ztqpnortuy~}|~~}ysqpmjkorrqsxz|}~~}{}|vrsw{}}}~|{zy{|{|zwvwwwy}}~~~}|zwvvtpnpsuvx||{zxuropv{}~~|~~ztqpnorttssy~}{z|}}{|}}~|~~yttvz~}{zyutw{|~~{~}yxww|{xwwuromklnqqqsz}|zyyzz||{}~~}|}{yuporssqprttuvwvux}}{}~}||yx{}~||||~yxyyz}|~~|}~}zywtrtsqprtuvwz~~{vtvxxyzzyz|~~|}|vsolmnmjjnrsrrtx{|{xxwwy{xvxzxwz}~~}~{{|yx{{vsqpprrolmrwxwwxzyxzzz}|}~{yvqmmprrprtuuvwxxy}}|}|zxx{|{{{zz~~||~~~~~~}{z{{{{}}{|}vrpnkijlmllortvz}~~{y{~}yvvvurppstrnmoqsuy|~|xwxxwvxyyy|}}~{zvrrvyyy|~xspnmorsqopuxwvwz}~~~|yy{|zy{}~}ztrrqpnnnnnnnopqw~{yvrpommquvvux~}~}{}}zyyyz||{|}{xtstusqqqpooqsttw}}|}~{{~~|xwz}~}~}{{|~|zxxxxvsswzzxvuwyz~~ztpopopsuvvx|}}}}~~|y{}~|z{||xwz}xwy{{zy{~}wsqssqnmqsroopsvz}~~{xwwvvwyyurssstwyz|}|}|{yxwtsv{{vqmlnqrompsuvuuuvwz}}~}|}~{xusrttsrsx|}|xuuuvvwyzxvvwwvw|}{{|zurtvwwvwxxwxvtuy|~|}~{ywy}~zvwyzwtrstsuy}wqnjggghiknqssuy||||wvxz{|||zxwurqsvxxx{~~}~}zwwyzxuqppprwz{~~}~~}|||||}~|wuutssssssw{}{zz{zyz~}}{xxz{zxxzzzz{{yx{{upkjlnnmmquwxwxz{}~}}|zyyyz{}|zxwxyxvuwz{yxy|~zxvtstusrrrqppt{}}~~|ywvtqqokknqqqrv|}{{~|}{z{{yyzz{~}ytpnmlkmonmmorttv{~~|zxtppqrqpprtuvwz|~||{xutw{{yxwwwy|~}{zyxxxywsppqqrtuttw|~~~}~~}~|zzxwvvvtsuy{|}}upmljfceikkkmpsx~}{z{{xtrqppqqpoqvyyxxz}}|||{yyxwssvy{~~~|~~|zxwtqrttspoqtvwxyzz}|yyzzwuwzzxvuwy{}{vsqnlklnnmnptuvz|{|~}|{zyxxxvuuwxvtuwwwvvuuw}{wwxwusrrstvy{}~xrpommnnmlmoqrsv{}z{|}}}~~}|{{zy{~}}~}{vpmnnmkiijkpv{}}ywvtttrommpqommortw}}zyyxwwxzzyz~~~~|zxtrpnlnqttrrtuwxyz|~|}|wutttwz{}~}|xsnlkiedeghint{~zvtrpmmopomnpqrstuuvz~{yyzz|~}}zwuttuuutsturonorux|~|yxyxwvutsrsw~~|vrppomkkkknty|~{wusstuvurruwvsrrsstwz}~yvtuuttw{~~zwrnmnoonmnoonmnpsz|z{~~|{zzz|~~|}}{vqmlkjhjnrv{~|yvvvvsqoonmllljkpvz~}}~|xutuwz{urpnmnooprwxwtrstvy~}|zwttwy|~~||~{yuqlhecdhlosx}|xurqrrpmlnpomlmoqsvy|~~~}|z{~~}{xustuuttvxwuspmmpv|~}}|zwuuuuvxz|~zuqonllnqty}~~zxxvsstvwuuuussssrqsvyz{}~yvttwz|}}{xvtqommprrpooonnqv|}|}}}}}~}{{}}}}}}}~yuqnnpppqtvxz~~{zz{{ywuvvtplkklmoqrtx{|~}}~~}{yvvy}|zxvrpppolloruvwwvtuy|~|{z|~~~}}}{z{||{{}~~{xupljkmorstuy{wttuusrrqpomlkkmrxz|}~~{z{{{{}~~|yxyyzzxussvxwvwxxwuuuvy}~~}{zxwx{}}}}||}|{yy{~~~}zxvvwvvwxyz{}{wtssttstwzywtrrrsuxz|}~{yy}{yz{}}{z{{yupooopqrrrtwyxxz}~zxwxzz{~~||}~|zurrstvy}~|{yxxusrtwyyz}|yyz{{{||zxvsollpsuwxwvwx{}|xwz}~}~}}{wttuvusppqpnnptwz|~|{zxwxz|~|z{|{yz}|zxwvvx|~}}}{wtssrrtvwx|~{yvsrsvwutqqpnnpsv{~}}~~{xwwxy{~}yustx{{zzxwuuuvwy}~{z{~~}|~|}~||}}~{wussttuvy}~}~~~~zxy{}|{|{xusrrtwxyzzyxvwy{|}~{xwz~~~~|{{{zz{|zvpmmnptwz}}{||zyxwvwxz|~{vrnkknqqsw}|ywvutuwyz|zwx{~}||{{zyxvuvxxxyyzxvvy~}yvvwz}|wutstuvvusqnnnszzwwyyxwx{~}zxxxxz|~|ywtrrsssuy~zvuvuttvz}~yvtstuvvutvyyxxxx{~}|~~|{ywuvw{}{zzzzxvssv{~~{ywvvw{}{{~~~~~}|z|~~yurrqonoruy|~~}~}zz|~~~{xvtsrtwz|~~}~{ywx|~}|||}}|~~~~~{xy{}~}zvrpquy|}~{xwyzzxvvx}~|xvwvtroliiknpruz~yuuvwxxxuuwy{}|yxy{|}~zwwyzyyyzzxwy|}yvuvz}|{|~~{vsstuvvwwuttvy}ywvwy{{|}}~~}{ywvtsrstuvvtqopqsuwz}}xuuvwy{|{zyz|ztqqtw{}~~{yz|~}|~~~~|zxvvy~|yvssuxyywvuvx{~~~~}|~}|~~~~~~|yvuvx{|zvrppomlnquz}|}~~{ywvuvx{}~|yurqtz~}~{wsrsvwxz{{{|~~~}}~|zxxy{|}~}yxz}~|z{|}}zyyyz{zy{~}yvtqnnqttrmkjjjjlnrz{wwwwurpmmotx{}~{y{|xwwy{}}|xspquz~|||~}zyyyyz{{xusrtvz~}}|zyxwxz|}xsomlklnpsuvvtqooqsvz}ywvvuuvuttux|}wuv{|y{~}ytrty}}zyz|~~~}{zywvurporwzzxvuw{yuvz~~}~|xvtrppqsuwzyvromkklqv|~|{ywvutssrrty~~~}|zyz~~|wrnkknruwxyyz||ywvwwxyyxwwy{}|y{~}~|z{zwwy}{vsqpnljjnsurmihijmrv}}vqooonnmllnrx|~|yxxyyxvsollpuy|~|zz|}ywwxwwwyywvwz~|xwz}|yww{}yuokijjihjnswxvrporw{}{somnpsuusrtx}~~~}}}}|{zyvsqprw|}{xwxz~|yutuwwvtrqpqsvy{|}~ytopuz~}{yvsrqponprtvxyvrooprux{~}wtsstuusrppqtx|~|zyxvsqomjhkqvxwvvx|}yusqqstvwyzywvwy}}}|yww{}|~{wtromkjjjlprronoqsux{vnjkmnmllmnnptz|vqopqrrqomlosy|}}}~~}{yxwxz|~}{xvtstw}~}zvtuy~~ywwvurmigijjiilrxzzyxy|~{tmjloqsuvussvz}yvttvxyyvrpprvyzxvwz|{yvrpquxxuqooqty|{zywtpnqw}}ywuuwxwtqoppppppsw|~|xvvvwy{}|wsqqrtvvspmmpv}zurqrsrqpomjhkqvxxy|~yutsrpoqtxz{zxwwz~}~~|z{}}yuuz|z{|}{wsqomkihhjouyzxwvvwz}}vojijmnnnnmmnt|~{vqkiknppqqqpoosx}~||{ywvwy{|}}{yxxy{~|yustx|{vsstutqmjiiiihjnv~}|}|zyz|{vniimrtuutttw}~}{ywurpprtwyyvspprw|}{zxvsrtwyxvuvwy{~}|{{yxvtqopuz~yurppprturnllmoqsw|~{{||||}}zzz{}~ztqqsuwyywvvx|~~{yvtssuvwusokimt{}}}~yussromlnswz{{{}xttuvvuttvyyvrrw~}yvuvwz}~|xurqqrtuwz~~yutssux{~yspooppppppqsx}{zvojgikmmmmmkknu}}zxy{~~{wuuuuttvx|}xutw{~~|ww{~yvvxyyuokhghjlnqv|~|}~|yxwwxwslffimpruxz||xtrrsrnkjlptwxyxxxz~~|zz}}zwvvz}{yy{}|}~~}||{zxvrnmrz~xrnllnqssqmloswz{}}|}}}|{|}|yvuuvvx|~|wrqtx|~||~~}}}}}{yvtssuvwwvtrqu{~|wtsqommpw}{uqpqrstuvwyyxvuy~ywy~~yvvwxy|~~{xwwxyzyxy}|vrsuwyzz{~~~|uomnquwxxwuuz~|zywtpkhgikmptvvuvz~{xwuttw|~zwvvvwy{}zwtsuz|}|xutuwwusqoopqrsux~}~|xuttvwvsmiinty|}vuvwvtrqqstrnklpuy~{yyyyyy{~}|||}|zyz~|xx{}~}{yyyzyvux~}~~ytolkmquxyxwwz|}~~{{{yvsqrtxzyvttuvwz~|wvy|vtvz}}|{|~}ytqqtx|}zvtv|}{|{vttuvvwz~ztonpruuvwy|~{wuv{|xvvwy~}{{{{{zxy}~ytrrssrrrv|~~}~{wtux{||{zyxy}}{~|zyyxvsnigintx|~~|yy~{ywvtrrtx~{yz}xvwz|~}zwttw{{wutvz}~}xtrrtvwvwz}{zzzzxxy|}yvsrrtwyxusuz~{tqqttsrrrtuuroosz~zxxxxyz{}}zyz||z}{xxz|}}~{y|{z|}wronptz~{{}ytrqpnmlmpuyzxvuuvx|~~}ytrtx|}}|}~~{xvx{}xtuz}{{~}||}}|{}}{}{uomoswz}~}}~~}|ywvw||zz|~|{{{zxwx{}ysmkkmoppqtz~}}}yy{}}}|{zz{ywz}~{yxwwwuroorw{~~{z{wvvusrru|~{uqrvz}{wtvz~{{~}||~{wtstvxxxz}|wttvwvutuy}|yvttvz|xy}xqmorttssuw{}{yx{}~{xwwy}}zxyzzzz|}zxwx{~|xy~|xwz}||yz}|zxyz|~||~yrnmmmkiikpuz{zyz{}|||}~}{{|}}|xtrty|~~{z|~zusu{~{zzz|~}|{z~~~~~xw{|wrprx~~zyxyz|}}{xw{~zzz{zwspqssplijkmnopsy|yxyz{|}|yxy|zvwz{{{|~~|ytrty}|z}~}~}xusssux}{xwwxz{xspqv{~}{z}|zz|~{wuuvwwvtstxzywtsrsssssuz}{{}}{|~|}~yqklpuxz|~}{}{yz~}yurrsv{}{zzz}}xusrsw||yy~~~~~|{~~~~zvttwxwsnkkkihhimt{}zvsqruxyz{|~~ztru{~zz}{vroquyzzyxxx{~}|||}~}xvy~}zy{~yuuwy{}~}}~{xwurojgimqrnjilnonorwyvvxyzyvsqrtx||wvy}}zwvwz}{vtw}|yyz{~}xvwz}}xuuuuuvxxursw{~}|{{}}yvtqoppnmoswzyvtrqqqrru|~~||}~~~|yvvy}~~{uonrz~}|uoosy{ytpoqty~~zyyxwutrqty}|z|{{}}|zxwuqooprstsoljhghjnv}|}|ywvuspnlmrvz}}}}yx}|yz|}wrnjilqvyzywwy|~~}}}~~~}}{{{||ww|~~xssvzxspomlhddflstplmoopru{}tpoprssqpqqru{~|~~|zxxxz}}yuqoqw||zzzyz~~|}{xvututtvxxwxz~ywxy{||}|z}}}|yurnklnnkjlqy}|wsqqswwx|{tstvz|{ytqqw{||~~zwv{~{ywqkjmrwyvqppsy}zvrqtvxwustwy{}{uuz~}~}{yvtspmlmmnrvuqnkjlosx}xtsqprsrpmiilqw|~zxwvvspmljjjkow}|xwx{~{yzyxx{|~}|yy|}zz}yvw|~xspnljgccdgmsussttuvy|ulhhkoqpoqrppsy}~}|yuqsvwusrppqty}}{zzxy~|xwz{zyzywusrty~~}zvsqrwz}~wuttutqkjmmljkms{|xvx{zyz}snnpuxyxvroqtvy~|{|xwvttvrliilpuvtrrtz|{zxvqmnqvz{xuwzz|}{|~}yvvvx|{yzz|{vspmklmjkpx{zwrprsw{}vrnjknqrqmhgiow|wtqnnmkkjjkkjks{}{{|~{wwyyvtvz~|z}{vuz~~~zzzxusqnkjfccdgnuy|~{yz{~|slgehmooqtsporx}~|zywqlnrsqqqprsruxz|~~{vtwxwx{|zyxvx|{z||xtpmpuz}vpppruuqmnonkjlnv{{}zy|}}}tnkmswxxuqpruw|~}}yuspopsqmkkkmruvyz|{zyvupmnotz~|z~~}zxxxz||yxywuw|}xvwwz}zwsnklnllqzztttw{~~~yuoihkpqqpkilox~xtsqmkifhjkmnlinx}}xuxzxuuw{}{~~~yst{~~|wwxtrtusnlgdeglsy}|||}~zz{{|{tojfhlnnqvwutw{~zxuuvvqllooopqqtutux|~zvuwvux~|}|{~|xy|}zvolou}yqnnotxwqprqnnqsx{wy}}{||yx}~ysolquwyxvvyzx{}}{z{}zurmknrpllnoptwz~~|{ywvpmnou~~zxusuuvz}|z|}yuv}~z|zuuvy|xrnotsqsy{wuuy}}||{zvmginqsurqrsx}~zussrnkhdfhjorqou|~|wvz}zx{{xuvx~yy||z{|}yuw|~ywxvrsvxusnjkmouy{}}||~yxzyyyvtrmklnnqz~}z{{~~|z{zutssuwtooqommpqv{{|{z}~|}|wvyywz~xux}{xy|}||upru}yw||tqposxyvwzyvtvwz{vwz{zzxvyyz||yv{|yvqrvx|}{{}}yx}xv|}zzyy}|zwpkkopnpuxwwwv{}z|~zxsprsw}~~{xtoosuy}~|}~{}~{xvy}{www|yvxyusv{|zvwz{wv{~}}}{{}yplnsuyyuuuuz}{yxz|wrruuqnhdfhmuyywz~~~|{ztrtw~|~}{|~{yyz~zy{yww{|~}{{{ztru{}}|uqqqty|{}}{~||yvx~zwxzyyxtuussttsx}~}}{wstrprqrvxvrstqmosv}{{|y}|y{|{|}}~ytuz{xx|{yyz{tuz|utqrx}~~{yzzysrtsrspptuy|}yx}}}}{}}}}}yx{|trwyxwxxz~~|ysnnstsuz|{zxuy~zwz~|}zwzz||}}zy{}{xvonqsy~yxwuwy{|{zz|{}xuvz|}}|wtuuqotxxwy|~~{{|wvxxz{xwxwy}|ytrtzytsuwutpjkmpx}}z}~}}|yyuqsuzxsstx}~zz}|xvy{~|}|y|zx{|vvtvz|zz{vxxvwtqrx{xxz|zzzvx||~|wz~}|zzwrpmlpqswzxuy{ytuxz{y~~}zy}|y{~}yzzytvzyy~|~~}}vsv{}{yyx}~}{}xomoonoljnrw}|y~}}ywy~}||z}}uruvuuxx{{vvywtw|}|wsvyyw{}z|~~}~z{xtu{}{zuoprv~~|{|wuvtwzzx||uux{z|{wxurrrolpttruy|~~{z{wvxwy}}z|}ysorxwstx|{|xrqqry|{|~~~}zysrtu{}~vspqx}}z~}yx}}{z~|}||}{wvy|zw{}|{vvwttrnnswvvz}|~{}~~}}}zxzzurniloqx}}|}yyz}yz~||~ywz{~{}z|~~~yxw|}yz~||~zy~~xuwz{|}}yvy}xpmnnlmjhkns{{z~zvvxvvxuuwwz}~yvxwsrvy|~|}zuv{~}}zsrsu|}|y{~}||yssw|}}|vuwx~}yyvsuvvz|zyzuuxvttoorqrsqlkqsrpsw|~|ytuuuxywrtz}||~}|toqwwvz~xvtuy}|{ywxy}{~~}~|vuxz|y{~~~xvtquz|{~~~y{~~{|}}~|xxyytqvwvuwxvxwttqmnsutx~}{{zz}zyxvvy}{wx{|yvojlou}~|z|xwwrqv|}|{vtvx}~}z{}}z|}{xz{xz~|xy}{|}yxuqpuxtpopnmmihknuzx{zsppqppqnpsty}~zy{yuty}|uuy|||xpmkkrx{y}}|~}vsw}~|}|}~~{zwrtuw}|tqqqnnjhlnqttplpssqsx}~zsppnpsrnnsvvvz|~{uuz{{}}zwux{{y{|vqppv~|z|~}|~|xx{{}~zxwuvz~~|{||~~{||}{|~{|~~|xuusnlpsqpruw{zwvropvyx|}xttrtxvutqptzzwx|~|{vqqty}{~}wrrpkjpwyzzutvw}~~|xyyx{|{x}~|}xw~~zz~~~yxyvusnlntsqrsroolknqwxstsmkmommlilpsx}~|~}z|}vuwxvupjhfglrtv|||||~~zz~~}~}}{utwz|sooollidgjntwupsvwvw{~yoiihkookkprsswz}}}{~}|ywxxuruwupnmlqx||{}}~}{|~yxz||}|y{||{yxvvxvvyz~{yvssrnjmpomnru{}{yvuy|z{|uonnotusspnpvxvx}zxvx~||}zywrooojfiouxzwtvx}}||zywstvw{~}y|}|}{vsw|}}~|yxyvtrmkmqqpswxwwsprtx~wqoojhilllkhhlov}xrqqpomgfedgloqx~}~zw{}|z|~|zxw|~z{}~~~wqoomkkfdfiovzyy}~|{|}}vkgeehlljlpqpquz|~}{xvusolorqnlkkoux|~}}{xx}~zz|~~~~}zxxvssrqux{xttsstrmkopomnqw|{~~{|~zsmkjnsttsompvwx}}zz~}yxxutrnlmnifhmquyxxz}{zzxwuqqtvz~}~{zyy{{xsswyz||zywyyvurmloqqrx~}xuvw{~vpolgfimmmlhimrz{sonmlkidddehklnv~~|xux{zyz}~}zwux~{{|wrqqpnlhegjox~~~}}~|xohecfjlklqsrsw||{~zwtrrqniknomlllnsuz~~|xw{}{{~~ywxxutqorv{}vrrqrutpmprqoquz|{~|}~{|~{tnkjmstuurqty{{|{}~{xvwwsrolknnjginrw||~{zyxwtpqsw}~~}||xvvvwz{wuwxxy}}~{xxxz}{xuqorvuuz~yxwy}ytrnhgimnppmmqu|}||wqnnljjgddegjmnpy~~~~~|ww{|zy{~|x{~}|||{vv{{z{yutttrqnjlnry~~zz||{yrlifgjllouxxwz}}z{|||ytrqpqqmiknnllmnswy}{xy}~}~{{}~{xy{zxuqpu{}~~xsrrsvxurtwwuvx{}z{~|zyxy{{yy}~ztpnnsvxyxwx{|{}}||yvy}}zxwuwxusplkmomkmqux||~~}}{ywrruy|zzyxwtrstw{|zx{|{{~|xww{|yyy|~|ywx{yxz|yxx{}{z~~|ywtmjkpsuurqrty~~{yyzwrooomkhdefinrssx~}zxz||z|~}|~~{zz{|zvvz{zz{|~{}||}|||yvwzzyxsqrsw|~~~~zxyz{{xspnkmopquz|{z{~{wvuturooooqrolmoomnqsy}~|{}~~|yxz}|yz}~~}zvwz~}yxz~~xtssvz|{{~}zz{~zwwxxxwtuvvxzzxz~|xvtuz|}~|{|~}{}}zywspsxxvuuuxzxvsonqtsrtwxy{{}}~~|yxz}~zxxxwvtpqsvz}~}zusrtx{}}}||}}~~{z||ywvwvttx||{||}}|}{ursvwxxttuvy~}}{yxvtuxvrqrrpnkhilotwwvz}}|{zwux{yutuwyxuuxzyy{}}}zyy}}{}zvuvx{~{y{|{{zxy|}zxy{{{{wvutuvvuw|~|||yusrppolmnortspqstssvx|~~{y{~~~{zz|}zxy}}|}|{|||{wuw|{xxx|~~}vrrsssrpprux{{y{}~~}}|yz~{ywvvtpmpsutuvwz}|{xutwywvxz{{{yz}}~~}||yvvxxwvrpsvz}}||ytrqqtxz{}zz}zwsrrroorvwwy{}~zyzzzyvttux{||}|{|zwusrtwvssuwvurnopswzzy|~|}|{zvux{{trqrvxwvxz{z{~}yy|}{yxxz{ywxz||||yvwyxwvtuy|zyz}~}|}}}}{xy}}|{{}|wtsqonljlnptwwvy{{xxy{~~~|||}~}zz}~~{xxy}~{z}~~}zz{yuux|~~}vqopppommpsw{}}~||~|xvxzzzyutttuurnnqrstvx}}z{|yxy{|{zvuwz~~}{{xuvyzzyvtvy~}{{zxvrqqrtwyzzyz{{{xusrqqpnlosuuwz}~}{yussstvvtuz||zzyz||yvsqswxvx{}~}ytsstxzzyz}}||~~~}|ywy|~wrqqtwyxy}~}}|yvx{{zyyz}|zxxyywvz||zywvxxwvtsuyzz|{{}}{zyxy|||{zyz|yuttsqokjloty}}}}{{||z|}|{}~zxy{}}}~~}zyy|~}{z{|ywx|~}~}{yz{ytpopoonlmptz}{{{wsqqrssrppqruvsqqsssuy}~{xxz{zxsooquz|||~}zwwy|~}zz|}zzyxwtrrsux{}|wuuutsqopppqpnnqtuux|~{wrponopomotwwvwxz~~{yusuz|{}}xvuuwyxwxzyvtvx~~}~~~~yutsux{{~~~|{{{{{ywx{{zz{|~zxvvurqtwxxxwwyzxwvtux||~~|||zwutuxxxxwvwzzwuvvtrolmptz~}{{~{wwxwwy|~~}zxxz|~~|zy{~zyz{yxy|~}{yz|{wtrrpomkknsy~|{{xsrssssqopqruusqrssrtw{{yz||{xsppquz||}~}xuvy{||ywxz}~}{ywtpoprvyzz~|yxxwvspqrstsomorssux}ztqqprsrpqtvvvwxz}}|yvstwxwz}}yxwxz{zy{|zxvwy~|z{~|xussuxxy}~|{||}|xwxyxxxz}|zzyvtuxxxxxy{{zxvttwyz{~~{xvvwyzxwutuxwttuvvtpllosx|~~zxxwutwz|}{xuvx{~}}}~}}|{~~{|~{yz|{zz|~~|zwvxywtttsrpnmorw}|yyvrppqpomkkloruvuvxxvvx{{yzzxvqmllosvwy}~}~~zy{~}{{|~|{{zxvrqsux|}}|xwvtsommnqtusrsvvvxz~{tommnoommpqrrsuy~}zyzzy{~|zyyyxvvxwusrsw}~~~}~~~}zyyyz{||~}|{zzywwwxz{zxz||{{|~|zyxvsrtutstvy}~|zxy{||~|xtsrtwwvtqpruuuvyzzyvrqruz~}{wuutqoqtwyywuwy}}zyzz|}|{}~}{xwy|}}}}~}|~~}~~~|zvtsuvuvwyxwurqsv{zwuronnnmkigilpuxyy|~~{z{}zxwvtrnkjiknpqty}}}~~~|{|~}{{|}}{xuuwz}~zwvtrokiknrwyxxz||{|~yqmkjlmljjmopqtw|~~|{}~{yxwwtrrttsqpprwz}}|}~}}}~{zywvsqqsvy||{}|zyxxvspqrrrtvz~~~~~|wtqpqstsrqpruwwx||wuuw{}}}|zxvtsrnkloswzyxy{~}{wuuvy|~}~{zz{zxutvwwy{~~}zxustvxxy{~~{wuvx|zwupljkklljikmrw{}~~|wttsrplhffgjmpt{~||}|{{|~zwvw{}}}~{yyz}zvtrqomkmosx|~~~~~zsnjhhiiilorrsuz{ywuutrprsqomlnrw{~~{|~~~~|zwurpnoruy}~|zywvtrpqtuuvw{~ytomnrtuutstwyz|~|{{{}~}~~|{|}|zvroonllnquxzz|~}zwtrruy}~}zyyzzyvrqsuwx{~}}}||}|{ywwxyy{~{yz|}wsokjlnpomkmptx|}}~{wvusqmhddfimopsy|xwyzz|~~||}~~{z}~~~|zywy{}}~}{{~|xvvvvuroorv{{smighkmmnqtuwy{~{{}~~{wuuuvvsonnnmmoquxz}~|{}~yvvvuuspqsx~}wtstvwvwxxxy{~|||~~}zvrppswxxwwxz{zyz~~{zyzzz|}}}|||}|yurooqrqoprvz|||}|zyxwuuvy}}zz{||zxutstuuuwyyxy~}ywwz{z{}}{|}|~~~~~}ywvtqppqrtsqpqsw{}~|xtqnmkihjklnqsw|~|{zxy|~~}~|zyxxyyy{~~|yxy|{y{~~|}~}~||}}{yxwvvvx{~~{yvrpoooooptxzywxz}{ywvwwvuuuspnlmprsrrsx}~}~}|~{yzzxy{|{xusuwz}~}~~|xuuvy||zy}~||~~}|z{|}~~{xxz||zywvx|~~|yyzz{}zyzyvsrv{}}}zwwxxwwvuutttvxxxyyz~~~|wwz~~|{{{|zwrooqtx{{yy|~~~~~zustvy}~|{|~~~~|}~{z|}{yz~~xttwxwurpprx~}||}~~~~}ytqqrrsrnkkosvvtvz~}wuwy|ytstwyzxvtuvwy|~}~~||~~|zz||}}~zvuuy~yyz{|}zwtruwwuuwxxz~~xuuwwwurnmnpqrsuvttvy{}~~}}~}|}}||}~{xwwz~~}||~zutux}~~~{wwy{||||{{}~}{xxz||{yxvvxyy|~}~~|{z}~~|yustuuuvywtstvz~{wx{|yvuvwy|}|~~|{|}zxtrrqpquxyz|~||~{yyzxvttsrsux}~{xwwy{~}z{~|xwvuuvxyz||zz}|z{~~{vsuvvvxzyvtsssrrsttw~zxvtuy~}zxvvvurppsvz|}}}}~|ywvy}~}{wuw||z|~}|{|{z|~{|~}{yvuuy~~~}|zxwupljkmqvyxuv{}|ywx{~~~~xvx{|~|zwyz{{|}~|}{wvy~~zvsstx}~{}|||||{vpprty~~ywy}~{vstwxxxuonprsvz~}{ywvw||zy{~}{{~~wrrrtvxwurt{~}zz||{ytnklnrv{~~{wwy{{zyxvvwx{~{z|{ywtstwzyxy{}~xutsty}}zxz~|vqpqrw|}}}|zzzz{{{z{ztnmoqruy|{y|}xvwz{zyzyusstw}~|z{}{yyy|~|{{{}{xx|}|||vqponmorttuy~~}{zxy}}~{xyyxy~}}}vuuw{}{}}}{{|}~|vvvvwx{||}|wssrqsw{}}~{zzzyustwywvwvrponosxzzx{}|}~|wttxyxy|~}zywtwz{zvvyz{z{}~~}|xspolmrvxxw|~~}zyz|||||xsrsuy}|uqpsusppsx|}~}yvyzyz{{{yz}}{yvtx{{||zwwz}~{{~|{y}{urppsxyxuy~|y|{xwy{{{{wqqtuvy|}}~~yx{~{vv{|}}||~~~}zxx{{wvx|~zvussuwwsnou{~}zz|}~}}~zyy}yvwxz~~{{~|}}zy|~~~yvvz|ywy}|{zxy|}xw|~|xwwvxzzwux||~|uqquxxwwrmnpqsvxyxux}}yvvxysmmqx}}~~}~~{wx~~yuuwy}}~~}vuutstuusrv|zwx|~}{{wtuwvvz~}{zxwuokkotutwyvsuvw||y|{{}}~}{{xuttxzwttw}}{}}}~|yy{zxux|~|}}~{utw{|zyzvsttstz}}zx}zvuwz{}}z|}}{z}}xsruz}}~}wtvxwx{}|wuwxwutwzz|{{~{vuy||{~~{z{ywz}||~~{z||||yy{xsrsw{~}yz}~~}z}|{}~}~|~~{}}{{{wy|~~}}wuvxwuvvqmmmmpv{{wu{}yz}~ywyzvqooqrsvyvuy|~~}|yy}~{zyvv||yz~|zzxwz}}zsrw{|zxy{~~~}|}ztqrsu{~z}vpmoqqrtqlnrsty~~{|{z}ytruywqoqwyxx{{z}~|{}~}~yz~{xz}}~ywyzyyzvpprsuz|}{y|{wwz|{{{xuwyz|~~~xvvyywxzyuvvuv{|x|~xrrrsz}{|}zyy||zz{vqswx{}{{|~yuwz{}~|}ysrtxxwwyvuwxy|}z{{|{|{{~}xxz|~}~|zyvomoonpsuvtv}~{{{y{vqqtwurqojkprtz{xy}~~||}}}{}~|}}{yy|}z{~}wwyz{~~z|~zvtwxx}zuuvssx|~}||{{{xrqpljlmlqx{|yy}}}ywyxsooruuvxwrpsvy~}{||z|~}}}{zvqqqopu{}|y{~|}~~~|||xstuuy~}~{z{~~|{yrpsvvy}~}zz||{wuy{{}}zz}zvw{}}ysrsrqv}}}}wvvuw}{y|}xvvxywxysnpstw}~|yxz||}|}|yz{tqpnotwxtory}~{z{}}{xwyywvvpiijjmuz||}}{|~~xw{||~ytsv|{vux~~zyzxw{{}~ywvuy}~{yywx|}|xu{~~zqjkkknsvwvu{~yxy~~vqprvwuutpoqqqu|||~~~~{z}~}~{tstrruwwutz~~}~}~xtvwvw|~||xx|yutsrv|{y~{{}}~}{{xttv{}zxx{~~}~xtussvyzywy}zyzxwy|~~|~}}vuvz|zxxupqsqqv{}{y~}ywy{xuw{|}~~~~|}{zxuuwxxuswyyvtuyz{{z||{}|zysoonklpstssz|}zwxxx|{vuxxsqrvz|}~xwy{{~z|~|{~}~|xwz{z||~zy}}}zwupnptvtpsz~{y{~|yy|zvttwwvuwuomooquyzyw||}zyz{{}}xwz~yyyvuy|{wrsy|}|{}~~{{|zxwvw|}z~~}zz|~~}xxzyy{||{y~}{}}{ytruyvqqu{~}}~zvxywwz||yux}}|}}}}{|{w|~ywwz{z{ztopqqty{zwv||yy}~xrpsx{}|{~~~{yz|z|~wty||ytrssv}||~zyz~~wssrqsvurnouz{zy|~}~xwyyz}~~zroptwwwxwtuwwy~~}~~~}yvvy|yvvx|~{xz{}z{|zz|{xsqvz{ywxzz{ysruyyxwupmppprvyyvu{|xx|~}zwwz}~yxyyz~|xz}}yutvy{}}}{{~|y{|{z}}|zz|}}}xvyyy||z~}}|}}zyzysopsvwx{{vtvwy}~yy~~zwy{{~~}}{{~|zzuqrrppuyzxvz~{z|~~yutvwvtvwutwy{~}~ywx{}}~zwwwtvz{|~|z{ywx}{vwxvuy{zvrsvwvttvwy{wwwwz~}~yvvvutvupnqsuy~~}}~}yvx{ytrsx|||}zx{|z~~~~}|}}xz}}zwuvwy{zyzywxwqmmnnrwyxuu{|y{~}{}~}wwz|~}zz}~}||zwxzyy}{}{wutty||~~~zuuvx{}z}}~wstvw{{y{xsqsvvuutppstuy~~~{zyxz}~|{{|zsqrrruwwusw}|{||{{wwy{yxvplmprv{ywy|~}}~{y{|}}{yy}}{zyvuw{}{yy{}~}~~zvvvvy}~|yw{~|xsqssv{|yyyxz|~~}~~zwtnlnoosx||y{~}}~{vssvxxxzytrtux~~~|}~}yyyxz~~{{}}}{vpoonnqtwvtw{~~|zzzz|wvwwx~}|}{utuvy~~{~}}|z{}}|}~|z{|z{~|vuvvvy}~}|wvvtuy}~}|{|}{wvvxxvvvrnmmnrw{|{z~}}|{xx{~||~}~~}xwustwzyvsvy{{zz{{|~}~}{{ytonllotyzyz~}}~~}}|wtuxy}zurswxussx|~~~|wvwxwy|}}{z~}zxvtuz}}~~|z{~~}{{}~yz|tpnmnrvwvty{z}~yuuwxxxxtpqssuy|~{{||~~~||~{zz}~{z{~ytsqqruvtpqv{}|yy{{}~~yvvwx{~~{{~~yxxvvy}}~~~}}|xuvy|{yy|}yxxww{}|yw|~~yxxwy{|{xy~~zutwzzyxuomnooquwxvw}}yxy{yusv{~}~~}{{|}{wux||yvuvxz|}}vrrqpqrtsrsy~||~|wuvvvy~~{xxxtpoquwxy{yuuvvy}{y{}{|{{}}||{xvvz}{yy|}}~}zzwttvvusty|~}{|}~~|xvwz{yxxuqppprwz{yx{}yxz}}}~}zz}~zvvy}zwwwvvxyxtrtwwwvwyy|~~~z{~|zyxwz~|}~~~zyyz{~|z~~~~yvwywssuy}~{wxzz{}~{y{~~}}~~~}{|~zx{}yxyzyxywrnnnnqvxvsty}~}{}}zz|{wsrsvxz||yx{}~}}|}{z}~}zxvuuy~{z}}xvuttwywsprwzzyy{~zvuvwy}~~||yspprtuuuqprtuw{~~}}||{}{wuvyzvstx{}~|||}|||~~zuux{{ywxz{~~xvwzzzyvqnopqtwxxwx|}yy{}~~~}zz|}~~~ywwy{{|}|xwwwx{~}zwx{|zurrtvz~|~~||}~~|wwyz{}~}{~~~{wwxy{~~~}~~ztqruwwxyxuuvwy~|}}zz{|~~}|z|~}~~}{ytpoppprtutsvz|{zyyz||wuuwwvttqprtvz{z|~}}|zzyz|~}}~~|{|{z{}{xxxyy{|{xvxyyvttvwzzwwwvx{~~|||yyxvssqnlmnpty{{z{}}|~|yxyxtrrtwxzzyuux{~}}zxxxx}}}~}{zxuqoonptwxvux|~|{|~~}}|yuuwy||zz|}|{yustvwz|~}{{}}zxvtty|~}}~|yz}~{wuuuvz~||~yvuvw{~~}~~zwuvwvvvrnortvz}~~~}||~}~~~~|{||wrqppruvtqqvz{zxxyz|{yyyyxwsmkkmpuz}~~}|}~{yz{}~}~{xvx{}{xy{~~~}zwwwwy}~zy|~~|wuttw|~yxxxz||{y{|xrljklnqtvwvy~~||~~ytrsuwvvvsqqrtw}}{{{}~}~yspppprtutsv{}}{{{||~yuuuvw{|{}~~|xtsstw|~~{z}|{{{|~~|{{zxxz}}{z{}~yuuuuwz{{z|~zxxwwy{}}|}yxyzzywvronnnquy{zz~~{{}~}{|~}|}~xwuttvwvtsvwxwvwxy{~~}~~|zwronllnrtuux~|xvvwz~}ywwyxutvy|}~~zvvwxy{}}{{}}{zyxz}~~~{{|~||~~|z{~ztqnmnqssrrv{~}||~|{}~{xvvvwvvuqnnpqtwzz{}~~}|~}{}zwusstvvtqquy{zyyz|zwuuwy|}|{~}|}|xvvvvxz{{z|}}}}~yvuwyyvvx{~~~}{xwwwwy{{zwx|~~~}{zz|~}zx{}zyz{{{yupnnopsuuuux}|{}~{wuvz}~||}}~~~|||}{wuvy{yvtttw{}}~~~~ytqqpqssrposx{|}~{wwwxy|~}}|wsqsvxxyywuuvvy||{}~|||~}{yxy{|{zz}~}||}~{zywwvusqquxxxxyz{~zwwxzzywtpnnnoquwwuw{~||~~~|z{|~ywwwxyyxustvvuttuwz~~{yyxxyzzxxz~~zwvvvwzzyvux|~|{||}}|yvvwy{|}{wsstuwyywuuxzzyxy{}~}}}}zxxz{{{{}}{wrpnmlnppomosvwwwy||||{{{{xuuuwy|}}||||}}~~{wvwxwvttttw{~~xutrpqrrolknpqqrtvzzwutuwyxvuw|}{zzyxvrnmnpqtuuuuy{|{yyz|~~~{{|~{{|}~}|~~|{{{|zyyzzytporssrqrtx}~~}|ztolkklnoommqtvvvx{~zwxyz|~~~}{wuuuuvxxvssvwwtrqqty}xvvwxz{yvtwz}|{{|~zutttvxxxvvxzzzyz{}{yyzzyxurpppqsvxwuuwzzyxwwy}~}}}~{{~~{yyz}yspoooppommosutssuvy~{wrooprtwz{zz}}zyyz|~}~}ywwxz~}xutttvwvsqqsusqopqtx~|yxwwwxyxwx{}}}}}{xsnlkkmorsrqtx|}|{z{||z{||||{wsrsuy~~~~|||}~~|zyz{zwtsstv{~ztpnmlmoponmpsuuttuw|~yuttux{}|{|~zuqqrtvxxwuvy{{ywvvvx|~xuuuuwy{zxxz||{{{|~{wuttvxywvw{~~|zyz|~~}|zxsnkklnqstssvz}|zxxy{~}{{|}~~}}~xusqqstsqnortutsstv{}xrnllnqtuttx}{vuuvx{|}}}~~~|}}|}|xtssstvwwvtuxyxvtrrtx~{zyz||zwvx|~~|{{}wplkkmoponorvxxxy{}||}}}{wronopruxyz|}|||~|||yxx{~xrpooqrqnlmqtusrrtvzzvttuwxxxxz~{urqpqsvwvuwz||{yxy{}|~~|wtstuxyxvvy~~{zz{{yxxyzywuvy|}|{{|}~xrmkkkmoppoptxz{zzz{~}zyyz}}~~ywvvwwusqsuvurqqsv{ytqpoopppooswz{||}{vtstvy{zxy}|yy{}~ztqopruwwusuy|{ywvwx{~|}~}~~~{yz}~}{z{|{wtrqqqpmmosuutstw|}xsnllmpssrqsy~}zz{}~|~~~~~~|~}xutttuuspnprtsrqqrv}{xwvxzzxuvy~~}}yusstvvusruy||zxxy|}|~~{z{~ytssuvxyxvuwz||{{z{}}|{|~~{xvx{}|zyy|}vpnmmoqpnkmquvutuw{~}zwwxz|~~}|~}|||}}{wtuxzxurqqt{~}wsrstvurnmptvuttvz~xvvvxzzxvw{~~zyz|~~|wroooqsvwvttxzzyxwwy~~{|~|{|~}~|}}zxy||{z{{zvqopsusqpqrv|~}vpnmnorsroorvyyyy{~~zxyz}}}~~~{{}zvttvx{zwsruwvspnoqv}~~~{zz{}}|zww{}}{yyz|{wvvwxzzwsswzzyxwwy{xx{}~}ztqpqtwzzwuvz||ywwx{~~}{|~}zxwy|{tqqqqstspmortsqppqu}~~zustux|~~{z|{yy{}z||xuttw{~|}|z|}xuvvwyzxtrsuvtqppqu}}zyyz|}|xvx|~}{yy{}ztnllmptvurrvz|zwuuvz~~}yxz}~yuux{}|{~~~~zwyzxurpopu|~wrpooruurnnsvwurrsw~yuuvy}~|~~}|~ytqrsvy{{yvwz|zwsqpqv|~|}}|~zxwx{}yz}~{xwx{{wvwy|}|xvy|}zwuuvz~{{}~~~zsnmnpswyxuuy}~{xwvwz|||~|yxx}ytrrsvxxtoptwvsonnpv}{soopsw{{yxz~~~ywwx{~~zxwy|z|~{z}yustux|~{wux{{xspoot{|zz{~|xy}|xwxyzqmllmqttrorwzzxvvvw}{xy|~~|vqopqu{~~~}}~}}wtrru|||wrqqrtvvsoptwvsqpprxyuuuvz~~{y}wqonpty{zvuy~|xvutw}}zy|~zz}zutuw{~{|}zyyy~{zyz|~{xz~~{xwvx~~|uolklotwvrrv|~}ywvwz~yxy{~}}|wuuuwzzwssvxwsqpopu|~|}vrpoptwxusv|}{{|~xuuvy~|{~}~z{|yx{~vqqrsw{}{ww|~{wutru{~{{~}}{z~~zyxy~ztpnorutqmouxxusstw~||~}wpmmnpuxyww|}||}}zxy{y{|}}wussuxywqoswxurpppt|zwvwz}~{wx}~~xsqqqtwywtu{~~{xwww{|xwz~zwy}}wtttuy}~{wy{zyz~}|}|xy~|yxyz~{sonmnrttpnqwzywvwwy}zyxxz}}{yxy}}wsw{|ytqppryzxy~~~vtsrtwxvqoty{zwwxy|zxwvx|~zx}~xwy~~wrpppsx{zvty~}ywwvy~zxz~~{|~~|{yyz|}{ywwyyupnrvvtqppqu}~}}}uonmmptwupqw|~|}~{zz{~~|z|{xy}{wwvwz}|xssxzxtpppqu}~z{~}{z}{wx}|zz{|zwuttx{zuqsy}|yvvvx~|{}|wuw{~zsqqruz}}xuy~~{yzz|~{~~zyyx{~{utrprvwuomqvwuqqrsx}}{wwwx|{|~{{|}~xy}~}xutsrv}|xx|~ywzzwwuvz||vqrvxwtrrsv~|zyz~yvz|{|~}{|}uooopsxzxtsy}}zwvwwz{xzzww|}{}}}{zy~}}zstxywrnnnpw}~|sqpopuwupnrwywttwy|~yzzz}~~|xx|~xvvvv{|vv{}{vrpppu}}yy}}|~|||~}x{~ywxxzzxxwy}~|vtx{{xuuvvy~}wvy}~}~zspqpqu{}zuv|{xwwx~~}yyxy}vuutvyzxroswwtonoor{~}xtuuuy~zy|yyyz|}|wvuuz|xx|~ywx}~xvvvx|~ytw{{xrpppqy~}|~{x|~zxyy|}uponnqwywrqw|}zvuuuy}~zww{~}vtuvy~~}|||uqqpou||z|~}vtsqrvxxsnqvxvrpqqt}ywwwz}{~|~wrrstx}}xw|{vttrsz~}ywz|xy~|yzzz}|}yxxw{}{yx|}ww|~yttttz||~~~ypmnmotyzwru|~zvwww|}|}}~}{zywvuvz}}wqtxywroonov}~}usrqtz}}yv{|{}~~xwxwz}~|{yyzwx~|wvy~xstttw}zvz~{vsrpqx}|~}}z~zxyxzzsqonquwvqouyzxttvuw~{y{}upqqqu{}|~~~~{wvsu|}ywz}}{utssvz{wqpuyxtpopps|~yywwz~yz~urrppu{}{vw}|xwwvz{wwzywy}ywwvw}z}~zzzy~~~||}xzzwwvv{~~}yqonlmrvwsosy|{xvwww~~{zzy|zzxwz~}vty}|wrqqpry}}|xx{~~zvvtsuy{ystz~|yz{z|yxvv{~y{}|{}~yvw|{sqqprx}}yuyyvvvtyzwx~~}|{zzyyzxurswxvpmswxuqqrrv~}}}}tppooty{ytw~~~~||xx}zy|zyxuv{}|vqtxywqopoow}~{z}|{xvx}~|vsy~}yyz{~wsrposwxupry||xuvwvz}wvy}xvw}}vvwwx~z~~~~}}|~{vtqpswwtnnuxxursuty{z~|yy~}vtusty}~{vz}{||{}yy~~yx{}xxwuv{}{uquyzwqoonov|||}{vtv}|z{{zyx{}vu{}z{}}}}}vttssx|}ytx}~ywwvu{|vssx|xssv}~yyyxzzvz~~{urrrsz~ywy}}~xwusuwwsliorrnjjllox}|{~||wttrrvz|ytv}}~}|{|{ux||ysopoou{{|}{vstz}yx{~xxwuvyzxroswwtpoqqr{}}|wqqomosusljotvsppqqs|zxy~~xttx}~xtuuv{|||{xwz||wqqvxwroppqv{z~zwx|~}~yrppopuyyuqtz}}xvwww}|z}}wuw{~~zsrssuz}|wqtyzwrooonsyzz|{vssx}zutw~{{{z{|x{}yxyz{|vuttv{}|wty~|wvwwxzvvz|wrqsxz{||vrstux~zw||wutssz~ywx~{xx|~yyxvx|}zsoqtsojhhhjrxy{|{xxz~{{~|vvtsvz|zusy}|yy|}~z{{{}{{vtsqrz~}~}yutv{|wtuzyvvuvy~~{trwyytpooos|~~~wsrpptwwsmnsutolmnot|~zy}zvuy|||}yrpqqsx}~|y{~~}{|ztw{{xsppppw~~||{yz~}vonnmpuwwspuy{yuuvvxzwy}~}~|uqqqpty|{vuy||xtrqpry}|||xtrtz{vssw{}}~xvwwx|{z}xwww{zwwuvz}}zux}~zxxxx}|{~|urswyxxxrllmmqw|}zw{{xxwx~{xy~}xwz~|wwwwz~xuy|zuolkjkrvwxywssv}|yy}xtuttw{{ytty||yvvwx|zxxx{|}zyxw|}xuvz}yusvz}}~wrrrsv{}|wuy||xsrrqrz|{zvutruxyvqptwvrnmmmqz~|yy}{xy|~|{{volllntyzwtw~~|}}~}{z}~zutsru|}{{~{|zromklpturnpvyxusstv|~~~wponnqvyyusx}}xvvuu{{vssxzuqpsxyyzxrnoprv|~{z|{{y{{yww{~}yuw}~zxyyz~{wwzzyywpihhhkptusrw}~{zzz{}{|~yx{xtuuvy~zx||wtrpqwzzyyurqtyzvuv{~vsrqquzzxrsx{{xuuuv{~{yxwx}~{~~~}}|xx|{vtvz{{{{upoooqvyyurw|}{wttst{|z|~xvuuwz{ysquyyvrpqprx}~}yxz|yx{~~~yqlkjjmqsropv{|{yz|||}~zyxx|~{z~~||zronlmoqqmjlrutrprstz{sponosvwtqtz}}zyyyy|xwz|xrpruwwwwqkjjjmswxutx~}|||}~||~}xuy}~|yxxy{~|~~|xqjhgfhlonkjouxxwwxy||{}xsrqrv{}|yx|{yyxy~~zurswyurqty|}~|vqppprvwvrquyywtttuw~||zyxz~}yx~~|xwy{|{{xplllmptusopuyywutuux||}}}yxwwy||yttxzzvssssw~}yy}{xy|~yrnljjmpolhjptvtsuwz~}|}~~}|{{~~}{}zsqpnorsrlikoqollmnqy~wtrpruvtpotxzyvwxz}~~|vtuwxwwtlhgghkopomntxzyxyz{~xy|~}ywwwy~xpljhikmlheinppnnqsv~ytrrqswyyvuy~|{|{~|yy||wsrtwxyywrnnnnquvtonsvwtqqqrw||~|}~}{z{}~zwy~}|}~|}~}ztnljjkorqnlptutqpqru|~{zyy{~~{vvz}|yvuuvz~}~zz}~yrnmllnonjfglnnlkmorz~}|}}{~}{{|~~~~~xtrqqsutpllpqokiiikpx}~{ywvxxwroquvvtstvy~||}|zyunigfehlnlhglqsrpqruy}}~zxwwx}~vromlnomiegkmlihiknw|vtssuxywsrv{}{yyz{~ywxzzyxunjjklpstrnnrutqonnosz~|xwwx~~{yy|~}yuw|~}zyz{~yqnlklnpokjmrsronooqw~~|~}|||}zxz}}{xvvvy~}vqonmorrojhkmmjgfghmv|~}|}{{~||}~{zyy|~|}yvuttvxwsnorsrnkihinuxy{{yy{~}}}xssvwurpprt{}umihfgjlkhehlnmkklmpw~|{{|~}|yxxz|xvttutpkhjlmjgffgjqx{~~~}{zyz|}{vtvyzxvvwx|}yqkihhkoqqnmptusommmouz|}~}|}|zxxz}}~~{vux{{ywvwx||vtqpqrrojimpqonmnosz~~{{~}|||}}}|ywww{xsqpprtspllopokhgfgkquwz{{{~~}}|}~{zz{~{yy|}|yvy~~}}~zxwwxz{ytqsuvspmmlmrvxxxwtsu{~}|}~{|}|xtrqpsy|uqmkkmmjfehlmkihijnu{}~~}yxwx|~{|}zyyy}~}|}~{uonoomigffhnuxyzzxx{~}~{y{~~{xuuvyyrolklnomjkostsppppsx{||{xuvz||}~}|{wwyzyusssv|}yxwwyxupmnppnlklmpw||z|~{z|~{z{|}|{|{xvuvwvrnnqsroljjjnsvvvusrty~}~|z|~|||}|wux{|zyyz{|yxwx{~~{wwyzxuqonosy{{{yvttwzyvuw|~zxvvz~~{xvusoigiklkihijntz|}}{xxzzwvuwz{zwy}}}}zyyy~~}~}yyzxtokihintwxyywvx}||~~~~}}{zz|}ywttusokilpqpoopqv|~{wvwyywvx}~zyyz}}~~~~}}}zvsrrsx~|zyy{|{wtuvvtpmkkmsz}}|~|{{}~~}yuuvw{~}~~ztrtutronmmpuyzzyvrqsvwutw|~zy|~~}|{|}{y{|{xuuuw}xtsstwz{zxz~{wussvz}~~}{xx{}|xuvxz{||yvvx{~|}~wqopomigghkqx{}~}{z{}}zy{~{yxxyzwtrtx{{{{~}zz|}{zz|~{zzz|}|~~zvroosvwwwvuux|~}{{}|xxxz~~~~~~~ztqrrrollmnry}}~zustwy{}}ywwxxz}~|yy|}|~}~{wutv{}}|{z{||yuuxzzxusrqswz{|||{{~~zsoooquy{{z|}|}}zvrpopsy|}}|zwx{|zvsrtwy{~}{|}~}yy{|{xvvwz}{{|~zvttuy{vtsstvusruy|}}||{|}{z{~}zyz|}|{ytonnptz~~~{upljikqvy{|{z{~|{|}~~~|xwxyz|}|ytsuvvtssux~|vsrsuy}}}}|yxxy{}|zxz~~||{|~}{xtrtw{{yy{~|vtttvy|}{{}{|}{wspnmpv|~~|{{ywvrnlmosw{|zxx{}}{yyz}|wtuvy~~|}}{|~~~}~~|wuuwxwvuuux}~{xxz~{uqonoqrrqsx}}zwvx{}~~|{{~|yyyzyxvqmmosx}~~|yvttuz{yxxz|~}{{|z{}zxwwxvsooruuutvx{~|}|z{}}xrommnprsstx~|xtsvxxxwwvwz|upnoquz|{xwyzyurppqu|ytrqqsuuttw}}}~}zxxxy{||yvwz|{zyyz}}ywxywttw{~|xvuuvxywutw|~~}zxwx{}~~~}|}}woigfgkpsttvz}}{yxxy}zurqrsvyzyxz|}}||~~}~{wwxxurpopsy~}~}zz}{xwuuutqmlnruvwy|{yyz|~{z|~||~}yrmjiimqtvwz|zxwy}~zyxy{||yvwy|}}}}zzz{|}{wsrsssqoopsy}zz|}~~zurqpqrsromoswyz|~|wwy|{wtux{|{{~~xtrpopqrqrvz|{yvtssw{~{wtssuutpoqtwxy{~|wsqrtw{|{ywy{|zxvvwz}wttvxz{{wutuwy{{yvtvxxxwwy|}{|~~}{|~xsomkkjhfhntxzyyxy{{urpqsuvvttx{|||}}|~}xuuvy|~~}yvsqpsw{}}{{}~|zyyz{|{wsqqrqonoqu|~xtsux|~~}~|z{}|vsqoooonkjmsx|~~}{{~|yxy{||zxxz|{xwvx{{xvvxz||zxxzywsponpuz~~|zvrqqqsuutrpqttsrrsv|zvssvz~}|zwuvx|~{yxxxxvrnlnrtuuvxy{~|z{}}zyxxvsqqsutsrrsv|}vronoqsuuux{}}{xwvwz~{ywtqonpruy||zyz}}|xvuux~~||}||~|zyxwvtojghijkmquy~}yvttutrqrvyzzyyz|~~{vsrrtvyyyz}|ywwz|}||{yy{{vsstwy|}|zxyzyvsponqv}~{xwvutw|~||}}}}zvtsssuvtqnmoppoqtx~~}~~}zy{~~}}}}{xwz|}|zyxxy}|zyxxyxwuux{|{xvttuy{|}||{}zsnmmortusqruwwusrsty~|{zxvtuz~ywwxz{{xwwyz}}zwwvtqnmnqw~{z{|}{z{~}xtstvvutsstx}|xutsssqnloswzzz{{|~}umihilqtvvwz~~|{{{~~zy{~{yyz{|}|yussrplhfgjqz}}}}}|ytqqsuvvvxy|~zvtsstvwvtstx{}~}zxxz{zyy|~wrpoqtwyxx{}~}{xwvwy|}}}}~~wssuxy{}|xspoprtvwussuvvsqonos{~zxyz{{{zxwy{~{yz||{zzz||zzz{}}|yutvwwvvwxz}|zxy{~~}}ytpnnooooruwxwvvwy|}ywwxz|{wrnmnquy|}|{}~zvsqpsw{~zxxz|}~~{zz|}{zyvtsstw|{yxxxyxvroopqpprtx}~}}}xtqmkkkkkmrx{}}}}|{|~ytrsux{||zyz{zxurommpsvy}}yvuuwz~~{yxxvtsrsv{ytqqrtvwvttvwxwvvy|~}}|yvuvxz|}|zyyxwvtrpoqvz|}|{{|~~{z|{vssuyxqmlmorssrtvyyxwvvvy{}}~}|zywsnllmqvz}~|zyz|{wvwy|~~}{|||zwtssv{~|}}{yyz|~~||}}|zwrlikortttuvy~~}~~yvvyzrmjjlortux{~}}~|zxy|~||{zyvsppqtx|zvtux}{yxyzzxuuwwurpnnqv}~~~{wtrstvwuqlggjnqsvy|}}~}{}~yusstuuuwz}~}zyxxz|}|zwutw||wsqqqsuuvvy}|xutux}~xtrrssssuxzzxwvvx}|yxxxxwtqnnorvz|{vrqqstvxz|~{{~{xx|}xtqomllnsvwwwwxz~~{|zsnkiijlmnorw}~~}zyyyyyz}~{yxy|~{yyz{|{xurrsvz}~|xuspmllnquz}zy{~|yuspnkjkpuz|}}~~{yz{|~}xrnllnprsssv{}zwuvxzzywutuwz|}|wrommnpsttssrpljilpw~|||ytqpsvyzz{{|~|zyyxxz{vsqqqrqomnqtwy{|}}{vqoptwyyzz{}|zxuplihhjloqsvz||xtqprw~~~~~~}wplkmopqsuw{~|~{snkjjkjighkoruy}|yy{{wvx{}~~}~}{yurpoopqsttux|~}xsnklqx}~~|{{||{{}}|}}~}ytommmoqsw{zvux|}wrommnoonnpsuwx{~||~|zyyyxxxy|~||~ytqonnnmljknsvwurolmrx~}wssux|}||}~~|z{}{ywvtstvy~{wvwy~|yvutsqonoppooprv|}zy{~zwussstvy~zrmjhhijjihimrwz||zxxz}{urqsx~~{xsnkklpv}~~~|xuroliggijkkkmpv~{vsrtw|~|yyz|~ysommnpqponpsuwy{{{zyxwxy{}|xwwz~~{zyz}~|{{|}|{zzzwsollou}~zwutstuvvtspmkknpqqqrtw}|zyy{}}ywwy}~~~xtrqqqomjjkllmorux{|{yyz}}wsppruwy{~~}|~}yxxy{|}}|wttw|}zvsqrvz}~}zvrpqrqponmosx~~|{zzy{~|vsrtz}vqmlkjhgfgiklmosx~~}~yvsqqrtvy~|wrpqtx|ysqsvz}|xsmkkkjiiijnsx~|zxwvtsruz}{}}|}~~zwsqpponmnpqrqqqsw}~yvvz~}{zywvx|~~~}{ywwx{}}|{xvuvy||slhhkpuwvspoooooopsv{~}||}~~|zxy~~~{yvsommnnmkiijnt{}yy|~|zxtpmmqv{~~|{zywvwz~}vpljlpv{}|{zxusqppqsvyz{|}|xwwy|~~~~~}|zwsojgghjjjijkou|{yxxxxwtpoqv{~}~~{yxy||wspoqtvxyywsoliijnrvxz{|}|||||{xusstvx{~{wtsuwyzxuqnmnpqqqqqsvz~|yww{}}~|xvvxy||yxxyyyyz|}|}~ytpljhhjnswxvronorw{~|xvuwy{}~~}|}}}||~|xuux{~yusrqpomllnpsvxz}}zz}|xusqppqtx}~yy{~}ywwxyzz|~}ywutromklov||yutuxz||{zyyz}|zz{~~{xvw{~yvvwz}}{vspnljihiknrvy{}}}{yxxxyyxwvvutstv{{vtw{}zy{|~~~}|zuplmqwz{yuqppsuwxxxxz|~zxyz|}}|zz{{zwutuyxromnquwxwvutrpopqtx|~~~}}}yvvx{}|{zyz{}~~~~|yvvy~~{|}zxwvx{}|{zxtojffimrvwwvwx{|~~}vrpqtwyyz|~}|~|wtrrtwz|~~{vsppqsvxxxvuux|~|{|~~}||}|yvrposy{wuwz}}~~}|zxwxz~}ywvvwxwtqnmmosw|}|{{||{zyz|}ywwx{|}~|{|}zxvuuuwz{{xtolklptwyyyyz||zxxyyyzyxwuuwy{{ywvux}|xuuvy}~{xwz~}|}}}{xvspoquy}~{ywvwy{||{{}|yxxwwxz|~}|{zz{}}yvsqnlloty{{yvttvy}~|vqnnqtxz{{zz|~~}}|z{~~{zz}~~~}|zxvusokhhlrz~}~|xurpopsw{~}{zyxwtrqty~|zz{||{zyxxyyxxz}~wrqsx|~}}~~}|{zzz{{||uqrv{}{{|}{yxxxwwvvvxxwtqopt|~~~|zyyyyyyzz|~}{xvuw{{wsrsvxz{{yxwwwxxxxy{}~~}~xropsvyywvwwxyyy{|~~|{|~ztqsx~~zxxwwxyz|}~~}|}~zurqsy~{zzzzyyz|}}}||}zvtuwxyz{|~~}|ytpnmnoruy|~}|}zsmiilqtvxy|~}{z{~{z{~}{z{}~|zz{{zxuronotz}ywvwz}~{uqopqsvx{~|zyyyz{||zxvuuuuvy}~|{{||{xwvwxz}}zvrqqtwz|~~~|{~{wtsux|}wsrsvxyyxvuvxyzzzyxxy{~~|{{{zxuqnnosw|}{yxxxy{~|zyz|}|zxutsuy~|yyz{||{||}~~zuqoopsvyzzyxy{~{xwwyz|}}}}{wutx}yrmlnrvy{||}~~||~}yxxyzzywtsstw{|xvtuvz~}zz{}~}{yxwuspnnpu|~|zxwwxxvrnihjnty{}~~zwvvxz|~}zz}ytstw{}~~~~~}|{{{|}}{xwx||yusrrsuwyz{|zwsqpquz{uqqrtvwwwxyzyxvuvx~~{yyxxwvtrqrtx{|zvqopuz}}}~~|wssw}ytqpqrstvwyyyyyz|~{yuqnlkklnpsvz~~{yxzyusuwz|}~~{xwwy}|zyz{|}|{xvuw{~|vqnnpsuwy{}}zyxwvuwz}|ywz|vqnlmnpsw{~||}{xvsqoopqrtuvy}{xxz}~zxxyyyy{{{zxvtstvy}}yurppqtwxvsolklnrw{~{yxxwvvwz~~|yvtssvz}~|zy{}yvtssssrppquy|}}{xwwy||wsqooprvxzzzzz{}~|zyxurnlklnquxz{zxussux}}|}~||}~}}ytolmosvxyyz|{upnllllkjjlotx|}||}~}zwtstwz~~|{{zyvtrrrsuwy{}~~{vqnnqw}}zwuuvwxz|~}}|{ytoljlptx{~}{}|vqnmmnppppqsvz}~}~yutvxyzyyyz{}~}|||~{wtrqppoopqtwywsolmpw~|yxyz|}~}{xvvxz{}~|{|~~yussuwy|~~zyz~zwutttrqppqrstvy}~~~{z|zupnoprtvxz~}xsomkkkkklloty}~{wvw||xutuy}~~~}xtqoooqtx|{vrpnlkiijklnorv|}xsqqty}~{wuuvwy{~}ytqppqqrrrrsvz~~{wuvx||yxz}}{zyxwwx{}{zz{||zxvtqonnptz}{z|}~}{xurponmmnprstux{~zwvvy}~{xvvvx{~~}~{wussrqpnlmortwyyywwwy{~xspqsvyz{{{{{zzz{~}ywwwy{}~~~|yvuvy~|zyy{}|ywtrqrssrqqrty~zxwwy{|~~}zvrooquz{upnllkjiijloruy}}wspopsw{~~~{vrppsx}|{|~~zuqmkklllllnqv|}xurqppqsw|~{xvw{}~{vsqppppppqsuvxz}|z{}}zwvwxy{~~|z{|~|{zyxxy{|{yvsqruyztqprvyzyvronopqrstvy}}}}|zxwvvvuvw{~zyy|}|}}zxvtqpooooooqtx}~{{~|yurpnoqty|}~}}}}zxwwwwwx{~||}ytporw||ywvutssstvx{~~}{ywtstx|~{wuvx{~{wspmkiijllmnpty~{ywurpnnpu{~}}~}}}}{zzz{}~yutux|~~|yvsqomlmoruy|}zywvusqnlmpty}}|||{{{{{ywurpopqstttuw{~}}}}zwutux{~~{ywxyz{||}}}~}}}~xsnlmoruwxxwusrrsux|~|zxvuuuvwwvutsuwz}~}|{{|}}yvtssrqpppquy}|xsommoqux{~~|{|}~~}{yvuvwy{}{wsqonpsw{|ywwxz|}~}}~|{yyz{|}}{ywvuuuvx|}}}}{{}~~zuqonmlllmorvz}|{zyxvspooqsvz~yvtvy}~~~{{|}{xusstvz~}zwtrstwxzz{|}ywvvwwvtrqppooprv|~yurqrtwz{zxvutttstuwy}~zz{}~}~~~|zyxwvvx{|yy|~}|{zyz{}|yuqmjjlqvy{{yxxy|~{uqoopqsssttvvwvvx{~{xtrrtw{~|ywuttuvyz{|~~}~}{ywtronosx{vstvy{{{zyyyyyz|}{yxwurommpuz~~|wutuvwxyyyzzzxvuuw{}ywvvxz{|||{ywtqoopsvy{}~}}||{{zyxwwvvtsrrtyztqoptw{}~|{||yusrsvy|~~}|{z{|}{xvuttsrsstsrqpprw|~ytqonnpqsvy{|{yxwwy{~{wuttvwy{|}}}~~|zxxz~{xwwy|~~}|}}zvromllnrw{|wspnmmmoqsuwwxxy{~~~~}{xusqqqqrtw{}{|}}~}|||}|ywxz|~{yvtstw{}xtqpqswz|}|||}}|{zzyxwusrqqqruz}}}~~{ywutssuwy{{{zzz{|~~~~|zyxwvtttwz}~~}{yxxyz{{{|}|wuuvwyyyxxxyzzyxxwxz|}zwtpnnpu{~{zzz{||||}~~}|{yvttvz~~~~}}}~{wtrqrssttttuwyz|}}zxvtrpnlkmptx{}~~zsommoruwxz|~}yutvz~~}{zxvspnmot{~|yuqmllnpqsuwz}~|zwusrrrrqpooprvy}~~~~}xussuwz{}~~~~~}|{{|~}wspoqsvy|~}zxwwxyyyxwwwxxxvustvz~}||{zxxxz|}}|zwuttuvwyz|~|zyxwxyyyywutttuvxz}~}}~{vtrrrsuvxzzzz{|~~}}}}|yvsqopruz{vsqqruwz{|}~}{zyz}~}|||||{{{{}{xvussrsuwz|}~}}}~}zxwwwvurpnmmmnorv{|xsolkkmptx{}}||}~|zxwxz~}yvvwy{}~~~~~~~}{ywvuvwy|~|zzz{}~}|ywurponoqty}{uqpoopppooopqrsux|}|||}}~~~~}|zwtrqruy}}}}~~|xtqpquzyusrstuuuvwxyzzzz{|}~~{xtrqqrtwyz{||{zxwwwz}yusrsuvvvwwwwwvuuwz~~~~|wrpoqsuwyz|~}||{{zz{{ywtrrtz|vqnmnoqsux{~~|zwuttuvx{}}zwvvwz|~~zxvvuutsssrqpoopsw}}|||}}|ytplkloqux{}||{|||}|zz|}ywuttuvxz}}{z{~}{xwwwwxxyz|}}|zwuttvy|~|wsqonmmnprsssstvy~}|{ywuuvxz|~}{xutttvy|}{{}~{xvwy|~zwtqppqsuxyz{|}~yurqpppqrsvy}~}}~}~}zwusrrstvxyyyxxyz|~}}{zyyy{}~{xurqrtw{~|{z{|}~~~|{{{|~~{xurommnqux{}}yusssstuvwy}~}}}~~}}}~zwtssttuttsstvwy{}|zxwwxyz{{{zxurpopsx}~~~~}~~~~~}|{{|~}{xusstvx{|~~}|}~{yxwvvuuuwy{}~~}}}~{wspnnpqrstuwy{}|yusrrrtuwy{}~}zwvvx|~|{zyyz{}}|}{wsqqqrtvxz}}yvtsqpooqsuxz}~}{yxwy{}~zwtsstuvwyz|~|ywuuuvwxz{~~zwuuw{~~|{{|~~~~~~~|yvsqppqrux{}}}zwvttsrstvxz}~~~~~}}}}|{yxxy{~|ywvuuuuvxz{||{{|}{yxwvvvvwxz}~~}zxvuwy|~{wuuvx{|}}}}~~~|zxwvuuwy|}{z{}~|zxvuuvvwxy{}~|zxxy{~}{xusrqrsux{}~{xutsrrrrsvy|~}}~{wtrrtwz}~~}{xusrsux|~{xusrrrsstvy|~|yvtsstvy{}~~~|zxvvvx{~~zxwvvuuuvxz}}|||~}zwvvwy{}~~}|{yvtsrtw{}{zzz|}~|zwutuuvwwy{~~}|||||zxvutttvx{~}zxwwy{|}~}}}}}}~~~|zxwvuuuvxz{}}||}{xussstvxz}~~}{yxxz}|zxwwy|}{zyxxwwxy{~~}~~~~}{yxwvvvvxz||ywvvxz|}~}zxvtsrrstvxz}|yvtrrrstw{~~||}}zxwxy{}~{xwxz}~{yxwvuuuwz}~}{ywutsrrrrtw{}||}~~}~~|zywvuuvwyz|~}}}}{ywvutuvy|}{zzyz{}{wtstvy|~~~}|zyxxwxz|}|zywvutttsssstwz~~}}}}~~~}|{|}~~|yxwwwwwxy{~~~~~|ywurqqsux{~~~~~~~~~}yusrsux|~}|zzz{}~}{zxwwwwwvutttvx{~~}||}~~~~}zxvvuuuvwx{~}{zxvtrqqsux{~|zzz{|~~}||}~|ywuuuwy|~}{zyyz|}|zxvuuuttrqqqsuwz}~~}|{{zz{}~|zyyxxxxy{}~|{|}~~}|zxvuttvwy|}{zz{~}|}~zwusrrsuy|~}||~{xutssssssrrstuwy|~~~~~}}||{{zyxxz|~}|{zyxxy{}~||}~|zwutsssuwz}{xvwx{}~|ywutrrrtw{}ywuuuuuuuttuuuvwy|~~~~~~~~~}}{zyxy{}~|{yxxxy{~~}}~~~~}}}}||zxwvutssuwz~~yvttvx{~~}}}~~|{ywutstwz~{xvuuttssssssstuw{~~~~}{ywuuvwy{}~~}{{{{}~|zzzzz{{|}}}}|{zyxwvvvx{{xvuwy{~~~~~}{yvsrqrtx{~|xvtssrrrssttuuvwz~~}||||{ywvtttvwz|~~}}~|zyyyz{|~~~~}|{zywvutuw{~~zwutuvx{}~}~~}|{zywusrrtvy}~{ywvutsttuvvvvvwy|~}||||{zxwvuuvwxz|~~}}~|ywwwyz{||{{{{zyxwvvwy{~zwusstvx|~|{{|}~~~~~~~~}|zxvuutuwz}|zxvtssstttttuvxz}~~}|{zxvutttttuwz}|xuttuwyz{{|}}}}|{zyyz{}|ywutuwz}~}||}}~~~~~~~}|{ywvusssux|~{yvtsrrsttuuvwy{}}|{yxvuttsssttvx{~|xvtuvwyz{|}~~~}|{zyyz{|}~{wusstwz}~}|{{zzz{||||{zyxwvuttvy}}{xvutuuuvvwxyz|}~~|zywvvuuuutttvxz}zwutuuvxyz{|}}||{{|||||}~{xusrsux{~~|zyxwxyz{||||||{zyxvuvx|~{xwvuutuuvwxz{{|}~{ywvvuuttsrrsuwy|~|xutsstuvxz|}}~~~~~~}}~|ywuuuwy||zxwwxyz{||||||{zxwuuvx|}zxwuuttuvwy{|}}~|xvutttsssrrstvxz}|yvutttvxz|}~~~}||}~|ywuttvx{~{xvuvwxyyzz{{{{{zxxwxy{~~}}~}{zxwvvvxy{|}}}}}|ywuuuuutttttuvwy{~|yvtsstvxz{|}~~}}}}{ywutuwz}yvtttuvwyz{|}}}}|{{zz{|}~}}}~~|zxwvwxy{{||||}~~zxvuuttssssttuvwz}|yvsrrsuwy{|~~~~}{ywvvx{}ywuuuuvwxz{|}}}||{{{{{{|~}||}}~|zxwwxy{|||}}~~|ywutssrrssttuuwy|}zwuttuvxz|~~~}}}~~|ywvvx||ywutttuvwy{|||}}}}}}}||}~|{zz{|}~}{zz{{|}}}}}~~~~~~{ywuttsttuuuuvwx{~}ywuttuvxz}~}}~}{ywwy{~}zwutssstvxz{|}~~~}}}~~|{{{{|}~~}||||||||}}}}}}~|zwvttstttttuvwy||ywutttvx{~~}|}~}{zz{}}{xvutstuwyz|}~~}||}~}|{{{||}~~~}}|||}}~~~}}}~|zwussstttuuvwy{}~}|}~~|zwvuvwz}}|{{|}~}|{{|}|zwusssuvxyz|}~~}~~~|{zzzzz|}~}}}~~~}}||}}~~{xvuuuuuuvvwyz|}~}|}~|zwvvwy{~~}|||||}~~|||~}{xvtsstuvxz|~~~}~}|{zzz{|}~}}}}}|||||||}~~{ywvvuuuuvwxz{|}~~}||}}~|ywvwxz}~}|{zyyz|~~}}~~{ywuuuuvwy{}~}||~~}|{zz{|~~~~}}}||||||{{|}~{ywvuuuuvwyz{|}~~|{{{||}~}{zyy{|~}{zxwwxy|}}|}~}{ywvutuuwy|~~||}~}|{zz{{}~~}}|||{{zyyyz|~|zxwvvvwyz{||}}~~}|||||}~~|{{{{|}~|zxwvwxz|~~}~~|zxwvuuuwy|~}|||~~|{{{{|}~~}|||{{zzxxwxy{~~|zxwwwxz{{||||}}||{{{|}}|{{|}~|ywvuvwxz|}~~~}|zywvvwy{}}|{{{|}~}|||||}~~}||||{zyxwwxy{}~|yxwwxyz{{||}~~|{zyyz{}~~~~|zwutsstuwy|~}{zz{}~{ywuuvwyz|}~~~~~~|{zyxxxz{~~}{ywusrrstuwz|~~~}~~~~~~}}|||}~|yxxyz{|}~~~}{zzzz{||~~}|{zxwuttuvy{~}ywutuwyz||||{{{|~}{ywutssrqqrsvx{}~|zyxwwwwwwxy{}~}}||}~|zz{}}zxusqpprsuxz|~~~~~~}}}}|}}}~~~|{yxvusrstw{~}}}~~}||{{{{{{{|}~|zyxxxxxyz|~{yxvuuvwyz|~}{ywusqppoopruz~}zwuuvxy{|}zxwwwwwxz|~~}|||}}|zyxxy{~}{yvtrqppopqtwz}~}{{{{||||}~~}|||}|zxwwwxy{{{{{{|}||zzyzz{|~~~~}|{zzzzyyz}}yurqqrtvwy|}~~~~~{yvsqpppppqrtvy||zyyyxwvvwxxyz|~}~}{{}~}|yvsqppqrtux{~~|ywwwyz{{{|}~~}}}|zxusrqppqtx}~|{{{{zzyzzzyyy{|~~~}|{zzz{|~}|{zzxxxy{}|zxwvvusqqpppprtx}|yusrssuwz~|zyxxxxyz||}}~~|{zzzzz|~~~~~|{zyvspooppqqsvz}~{xvuvwyyz|}~~}}~~{yxvuuvwwwwwy{||{zyzzzz{|}{z{}}{z{~~|xtqooprtvy||ywtrpppppoprux{}}{yxxwvuvvuttvy}|{{}~~~}}~}}|{yvrqpqstvy}|zwtstuxyz{|~~|{zzywusssrpoorw||zyz{{zxxxyzyyz{}~~~|zyz|~~||}}}}~~~}|{{}~}|{yvuuuvusrrssrqqsw}}ytrqrrrsuy~|{zywvwyzzyyy{~~~|zyyyxxz}}|{zxuqppprrrruy}~}zvsrrtvwwy|~~{ywsqqstttuvy|}}|{z{{{{}}{yxy{|}~~||}~}~~}zuqooprtuw{~{zywtqpqrqppruy|~~{yyyxusstsrrruz~~{{{{zzyy{}}{{|}}|xtrrsvwxy|}{wsppruxzz||xvuvwwusstsqppquz~~|zz|||zyxxz{yyz|{yyz}}|{yxy{{yz}~{zzyxurrsuvvutvvutstx}~~|xtssrppqtz}{{ywuvwwutuwz}~}{yyzzxwy}}||{zwsqqrsuuux|~~||{{yurpqrtvvw{~|zxtqprssqqsv{~~|{|~}}~}~{wvwzzz}~||~~|zz|}|z{|}|zurpprtuux~~|{zyvsrttsqqsw||yxwvspqssqpprw}~{yz{zyxvwz|{z|~}yvvwxzyxz~~{vqoprvyz{~~~~}{wsrstvwustvvsqqrw|~~|zz}~~|zxx{|zz}~~|xvwz{zywvwyyxy|}zzzywspqruwxvvx{zxwwy}~}~}{vtutrpprw~}||zvrrttsrrsw{}}|zyz{zxz~~~~}zusstwzzxy}zxyzzyuqopruwww{}{zywroqsrqqsv{~~}~}}~|ywxzzy||{}}|zxvwz{yxy|}}{vsrsuxyxz}||||yvvwwutuw{}ywvvurprsrppqtz~~|zz|{zxuux{{yz~~zyxy{|zy|~ztqqrvz|}|{{zyuqpqruwvtvyxuttv{}|~}}~||}|z|}}}}~yvuwzzyxuuxzywy}|{{zyvqpqswzyx{~~|yyy}~}|}}ywyxurqsw~}{{zvqrttqpprw|}~}zz}}{|~|{}}xwwx{|{y{~}}yxzzyxspqqtwxwy~}||{vrrttrqrtz}~~|}~~~}|}}yy{|z{~|y{}|zxutvyxwx|~~~zvwwx{{yy~~~~~zz}}zxxx|}zxwxwspsutrqrtz~}{|~|zwuw{{y{|{z{}}zwz~~|{|}~xttux|}|~}{{|zyupopruxwuw{{yxxx{}|{||{~~~}|{|}{yww{{zywuw{zwy~~}~}|ztqrtw|}{{|{z{~}~~}}}{{}|xvvv{}{z{zursusppqsx}~~}}~|}|{{||zyz|~}y{~~|{|}zxz{{zvrrruy|{z~}}~}wuwwustvy~|~}xxxy}|~}}}~|~}{||zy|~{zwtswywvy}|zzy{}}zy~}~{zz|~}~|zxyyvstwwtstu{~~~}~}}}~{ww|~|{~~|{z{}|xwz{yvwx{{yxxz}}{|z||zyuppqswzyw{|zzy}}|{|||}{yyz}~|zxy}}|{xvx}}z{}|}xuvvy}~|{|{zz~~{xx|~|{}~~~~zyxz~|{z|{xstwvrqqsx~~}}}{}}{z{||{}~yy}~{xxx{|z|}||yuuvx|~}{~}~~|y|}zwwwy}{|~}|ztsuvz~~{}~~~{|~{zy|~}|yutxzxw{}{{{}~yx|~}{}~~~||z}|z}|z}}}{|}}xuy{zwwxy~}||}~}{|~{|{{zz||xsvxvssuv}|{z|~~z{|{}~||wrrsuz}{y}}{{z~~{|||{}}}}||vuuv{~}{}}{~~zz~}{}~}|}zxxx|~{}|zzx{~|{yutx|yx{~}|zz}|{{}~{vwzxtstu{~|}zwxy|||{}~|{~|wy|zvtuuz~~{xyx{||~~~{xxx}~}}yy{{zytqrrvz|z{}|~}}~~~~{{z}}xvy}{y{{|{|~~zwz|zvwxz}|z|}yvx|zwy~~~}z}~zyyz~}|~|}~~{|zzxxzzvqtwurqrsz~|}~}yx~~{||~~}|vvvw{~}y}}{{z}}xvy~|{~}~}~~|}~}{tsssw{{y{~|~|}}~}|{{zy{~~zy~}yxvvz{yyvrsxyvw|}{||{~~|}|}|x|~|xwww}~~~{|~}{ustuy}~~~}}{}~{uvyxurssw~z{z{}z~}}~~~{zyy~{{~}wvxzyyvppqrw||y}|{~~{wxy{|~|}||{~{z|}z{y{~}wtxxvrsux~}z{}|{wtvzyuw|}z{z~~}}~|~}|z|yvvtvyxsquvspqru}~|}~|vw{{xvxy}~yyxy}}z}|zzx||{ytsw{xw{}yz}{{ysrrrvz{x{~||zzz{~}}|}|yz}}yw{}zuttsyzyyvrrwyvvz~{{~||xvx~~{|}~~~}|yyx|}{~}yy|{zytprru{~}|}z{|zutxxuqrru}}|{{~}y|~zxzz}{yx}}~{wv{{utwyxxwqprrw|}z~|z||z{wsuvx}~{~~}}z}{yywy{ztrvwtpqrv~|{xz}{{zusvzwux}}{zz~zy~||~~yy|}||xststwxtptwuqqrt{|}~{tswxvsuvz|{zz~|x|~ywwuy|zzwsrvyvvzyxz|zzwqrrsx{{x}~{|yvwwz}}~|y{|ysuyxtrsruzyyxtsvzwvz~{z}|zzvsu{{xy~~{{y|{y|~yvwzzyxropptz}{|}{y{|xsuxwsrssx~}|z|~~yw{|yuvwy|{xz}{zyutwzvsuyzyytqrrv|~|}|yz{yywqrstz~~{z{zvvtuxytquvtppqs{|zxy}|{zvtv{yvx}~{zy~~~{ww{}zy}ywy{yzvqrqrvxuruxwsrtu|~{{|ztrvvtqrtw}|z}~{wy|{vstsv{yyxsquyvux~|yz|{{ztsssw{|z}{z|~||yttuv{~}{}zyzxsquwtqqqsy{z{xuv{|yz~}{yz}{zxtruzywz~|zy}~~{wvy{vtvyyyxrpqquz|z}}{y{|ytuyyustty~}zz||wtwzxttuu|~{xwzzyyvstxxttx{{{yttuv{~~{zy{{yxtoqru{~}|~z{~}|{ursrtwwrquwtqqrt||{yz~}}|xvx|{xz{zwz}||zvux|yx{{wwyyxwrpqqtxxttyzxvvwy|{y{|xrruurqrty~}{z||xsuxwsqrqvzyyxtswzwvz}z{~~}{vtuvz~|~yxz|{{xsssuz}}{~|yxyxspsvtqqrsy||}{yy~{z~|zyy}|zyustzzwy|{xz}{{yutwzvsuxzzzuqrruz}|}||z|}|wvz{xvwwz~{ywyzwsuxwtstu{}zxvzzyxussxyvuy}~}xwwx{~~{}zy{|zzuqqru{~~}yy{zzxspqpsvvsrwxurstw|{z}~zy|~|~|ywvz|{{xuvz|yy}ywyyxxtpqqsxzxvz}|ywxy~~{{yz|zttwwtqsty~|zyzzuqtvuqppqvzz{yvuy|zy|~}}xxyz~||xx{{zzurttw|~|}}{yzzvqrvurqrsx~|{~~{}}~~~{zyy}~}|xuvz{y{{ywy{zyxtsvyvtvz{{{vsuux|~|}~}}|}y{~~{yzz~|zxxzytrvxvstux|zxz|zzxutw{xwz~{xyxz~~{z~}}}~|{|~}|ysstuz~}}xxzzyxsopprvwtswzxvvvx~~~~}}}}|yxwz|{{yww{}zz~{y{|zzvqrsuy|zx{}zyyy}~~~~{zz{}|xvyywtuvy~{ywyyvqrutqppqu{{||yy|}z|~}~}{{{~}~zxz|||xttuw{~}}|z{|zusvwussuw~}~|{~zzzz~{}}zzz}~zx{~|z}|ywvy{zyvsswyvuw{}~}ywwwz}~{|}~{{{}{yxzzwtwyxvuvx}{zz~~}|yvx|}zz~{yyy{}{wy}}zxyy}~}}{vvvx|}}{xxzzywrppqtwxvvz|{xxxy~}}~}}~~zywz~}}|yx|~||}~~}{vtuvy}}zz~}zyxy~|z{|||{{{~}yz}}zwww{{yxwyxuqsuurqqrx}~||~~|}~}|}}}|~|||}}|z|~~~{vwxz~~}}|}~}yvy{yvvwx~~}|z|~~~|wuvwz}}z{~{{{}|}||~|xwvx{{zyutwzxwy}|xxxz}~{y~~}|~}|zz||zvx{{yxy{~~~}|}}z{|{~{yxxy{yuuxyvuvvzzyxy|~}{~{xy{{zytqrsvz{xx|~|yyyz~}}}~}zzz|~}{zz~~|}~~|xwwy|~{y|~}yxww{}||zww{}{z}~||}~~}yyx{zyxwyyvsuwvsstu{~}~|}}{|}}}}~}}~}z{~}zxyz~~|zzz}{|~}zxxx}~|~|yy{{{zusstwz{yz~}}}}|~}}~|}{xwwz}}|{xwz}{z|}yyyz||xx{}|zz{~~}||{~~}~}}~{z}}{{{}~~~}}~|}{xwvwxwsruvusstw}}{zz{}{y{}|||~~{{}~}|xtuux{|zy|~}zyxy}}{|~~~{wvwy}~}|~}~~}}{yyz|}{xz}|xvutxzzyxuuy{yy|}|zyz~~{yyy{|yvwzzwvvw{}~{{~~}yvwx{~}~~}|}~|xwzzxvvw{|||}}~{yyz~{{}|xvwyyyvrqrswzzy{||~|yyy{~|z}~{yyy}}{{~}{}|yxxy{zvuxywvwx{~|{}}zy{~}{}~}||}~}}}|~|wutsuvtpqttrqrty~~|zz{{xvx{zxxxz~}~zxwxz||zy}~|yxwx|}||ywy}~|}~}~~~}zuuuwz}||~}~}}|zyz|{wwyzwtrrswyxxvtvzzyy}|zxz~~~}zz||}{zz{}~|y{}|zxxy}~}}zy{{{zvstux|~}}~|{{|zvuxywuuvz}}}~|}~||}|zyy}~{xxzzwuvxxxvrqrswz{z||{{}||yuuvx{|{y|}{{{~}}|xwvwxwtqtvuttux~}{yz}~}|zxz}}{|}||~~~}{|}}|ytrqqstsopstsrsty~{zyyyurtwwuuvx}}zyy{||yx{|zwvuvyzzywvw{|z||{}}}|wttux{}|}{zz{~}~}{yyzyvsuvurqqquxxxwuvy{zz}|yxx{{zzxwy}|{|}|}~}~~|zyz}~|{}}zwwyyywsqrtw{}|~|zyzzwtuxwuuuw|~}|}~~{y|}{yyz|~{yxz||{ywvxywtuwxxxtrrtwz||}{yyzzywsrsuy{{z|~}~}}~yutstutqpstsrrtx~~{yx{}}}{yy{~||~~|{}~|}~~zyz{zxtqppqssqpsutssux{ywwwtqqttsrsuz|zzz{{xvxyxusrswyyxvuvy{z{|{|}}|xuuvx{}}~~}ywwx{~~|}{yxxwtqsttqppquxyyxww{}||{xvwyzyxvuwz{z{~}{yy|~}zxz{xvvwxxwsqqsvz||}|zyzzxuuxxwvvw{}|{||yvxzzxwwy~|ywwyzzywuuxxuuwyzzxttuwz}}|~}yxyyxwtqqsuy{zz~||~~}zurqqstspprtsrrtx~~{yy{~~~}{z|~}|{z}~}{{~~}{xxyyxuqoppstsqsvwvuvx|{ywvwvrpqssrrsv||zyyzyutvvtrqqrvxxxvuwz{z{}|}~~~{wvwy}}|}~~}yvvwy|~}}|ywvvuqprsrppqsxz{{zy{~}~|xvuwyyxwuuw{{z|}zyxz}|{ywvxywuuwxxwtrrtw{}}|zyz{ywwyzyxxz~|zyzzwuvxxwvwy}{xvvxzyxvuuxywvxz||{xvwxz}}|}~zxxyxxurqsuy{||{z{{zytqppqssqprttssux~~|zy{}}~~{yx{}~}|zz}~~{xxyxxurppqtvutuyzywxy}~zwvvvurprttsstw}{yxxxvrqstrppprvyyyxwy|}|~~~|yz{}}{|}}|zwuvx{}~}|ywvvurpqssrqrtx|}~}|}~~~~||}}{xvuwzzzywvx{||}|ywvxzzyxuuwxvuvxyzxustvy}~~~|{{|}{yz|}|{{}|zyyyxutwxxwwx{}ywvxzzyxvvxzyxz|~~{xxyz|}{{~~}}~~~{yyzzywsstvz}}}}yxyyywspopqssrqtvvuvw{~~|||{yxy|}}}{z|~}zyzzyxtrrsuwxwwz{zyxy{zxwvwwussuuttuw|{xvvvurprsrppprwzz{zy{~~~}|}~~{|}}}{xvwx{~}zxwxwtrsuusstvz~~~~}{{{|}}zz||{xwwx{}|{zyz}~}~~{xvuwyyywuuwywvwy{|{yvvwy|~}~~}}}}}}~}{yyyywuwyyxxy{~{yxx{||{yxy|}{{}~zyyyz{zyz||{zyz}~~|{|}}{xuuvx{}}}~{xxyyxvrppqsuuttwyxwwx|~~~|zyy|~~~}|}}||||{yutuvxzyxy{|zyxy{~~~~}yxxxyywuwxxwwwz~|xvuuutqprsrqqrty|}}}|}~~~~~}}~~{yyz}|zzzzxuuwxwvvwy~~~}~|ywwxz{zxy{{zyxy{~~}|~~||}{yvuuwyyyxvvxzyxy|~~~{xxyz|}||~~{zz{{ywx{{{z{~~}{z{}~}{|~~}~{xxxyyxvvxyxvvwz~~~~}ywxy{}}|~}~~{yxyyyxurrrtwxwvy{zyyy{~~~~~~}{z{}}ywwxz{zyy{{zxwwy|}}|{z|~}||~~zzz{||zyz|{yxxz~{wvuuutrrsutsstw|~~~~~~}~}}}}||}~}||}|zxz{zyxxz~~~}{{|||zvttuwyyxx{||{z{~~}}}~~|zz{zxvuuxz{{yxy{}|{|{yyyz{{zz}~~}}~~~||}}}{z}~~}}~~~}}~~{xvvvwvsstvuttux|{yyy{|{z{~~}|||~}~~|zz{{{zwttuwyyxxz|{yyy{~~~}{z{}~~~}}~~}zyyz{{yxyzywuuvyzzzyxy{|{{}|{|}~||~}{zyy|{xvvvwwutuwwvvwy}~}~~{zz|~}~~~~|z{||{z{}~~}}~}{zyz}~}|}}zxxyyyxusstvxyxy|~~}}~~}~~{yyz{{zxyzzywvwy|}}|{{}~}~}yxxxyywvxzzyyz}~}~~~~~~~~|wutsttrqqstsssuy~|zyyzyxwxzzyxxz}~~~||}~~}ywwxyzzyy{{zyxwy|~~}||~|yxy{}~}}~~~{zzzzywwxxvtrruwyyxwwy{{{|~~~~~{yxy{~~~}|}~~}~~{xxxyyywwyzzxxx{~~~}{{|}|zxwwy{}}|~~}|||ywxzzyxy{~~{zyz}~}{zzzzwvwxxxvsrsuwyyy{~}||}}|ywvvwyyxwy{{zyyz|~{xwvvwvtsuvwvwx{~|{|}}~~~}}}}|yurqqrrqpprssstuy~|zxxxwuttvwvuvwz~}zyyz{{yxyzywvuvxz{{zz{}~~~~{xwxz|~~~}}}~~}{zyyywttuusqqqsvwxxwwz|||}~}{xwwy{{{zyz|}||}|zzz|}|{z||{zyz{~~}}}|zxyyzywutuwz|}}~|{zzywvwyyxxy|~~||~~}||}|zyy{||{yxwyywuvwxxwtstuxz{{}~{yyzzywtttvxyxxz|||{{}~~}yvtsttsqqstttuw{~{zz|~~~}|}~|{{{zxtqppqqqpprtttuvz|ywvuusqrsttstvz~~{zzzzywwwxwtsstwxyyxxz|~~~}~~~}zxxy{~|zz{}~}}~|zxwwusqrsrqppruwxyxxz}~~~~~~}{xvuvxyyxwwz{|{|}}}~}|}~}{zyz}~|zz{zxwwxxxvtstvy|}}}{zyzywvwyyyyz}~}}}|zy{{{zz{~|ywwxzzyxvvwxwuvxyyxvuuwy{||}}zxxxxwusrsuwxyxz}~~~~~}~~}{wtrqrrrppqsttuvz~{zz{}~~~~~}}|zyyyxurpopqrrqrtvvvwy}~yvuttsqoprsrstw{{zyyyxutuutrqqsuwxxwxz|~~~}~~|yyz|~{yyy{}}}~~zxvuusppqrqppqtwyzzz{}~|||zxvttvxxwwvwz|||~~~~}{yyy{}|{yxxyxwvvwxxvttuwz|~}{zyzyxwxzzzz|~}|{{zxwxzzyyz|}ywvvxyyxvvwxxwwxz{{zxwxy{}}}~~}}{xwwwwwusrtuxzz{}|{{{zxurpppqqpprttuvx{~}{zz{~|{{}~~~~{yxxxwurqpqrttstvxxxyz}}xvtssrqoprsssuw{{yxwwusrrsrqppqtwxyxyz}~||}~~~~}zyxz{}~~}ywuutrppqrrqqsvy|}}}}~~~~~|zzzywuttvwxxwwx{}}~~}~}|yxwwyzzywvwxwvvwxyywvuwy|~|{z{{{zy{}}}~}{zzyxvwxyyyz|~|zwvvwxyxwwwyzyyz|}~}zyyz{}|||~~}}~}}}{ywxxxxvttuwy{||~|zyyyxvspppqrrqqsuvwxz~~~~~}|{|~|zz{}~~~~|zyyyxwtrrstvvvvxzzyzz}|xutttsrqqsttuvy}}ywvutsqpqrqppqsvyzzz{}~~~~~~|zyz{~~zxvuutrqrsssstw{}}|{{{{yxxxxwuttvyzzyyz|~~|||zxvuuwyyxwvwxxwwxyz{zxwxz|~~}}}~~}|~|zzyyxwwyzzz{}~~~|zxwwxzzzyyy{|||}~~|zyz{{{zz{||{zz|~}|}}|zyyzzzxvvvxz|||~~~{yxxxwvsqpqrssssuwxxyz}}~~~~}{zz|~|{{{{zxvtuvwxxwxz{zzzz}~}zwvuuvutstvvwwx{{wuttsrppqrrqqrtx{|}}}~|{{}|zxwwwuttuvvvvx{~|yxxxyxwvwxxwvvwy{||||}~{zzzxvutuwxxxwwxyzyyz|}}|zyy{|~~~}{zzzzyxz{||}~~}}}|zyxy{}}|||}~~~{yyyyyxwwxyxwwxz}~~~}}~~|||}}}{xxxz{|||~~~~}}|zxxxxxwtrrstuvuvxyzzz{}~~~~~}{{{}~}~~}|yxwxyzyyyzzzyxyz}~~~}|}~~}|}~~}{ywwwxxwvwxyyyy{~}zwutsssqqrsssstvz}~~~~~|zzzzxwwyyyxyz|}|}}}{ywuvvwwvvwyyxxxz|~}||||{yxxxwuttuwyyyyyz||||}~}{zz{||||}}|||||{|}~}}~~}|{|~~{xwwwwuttuvvuuvy|~~}zyyz{{{z{}}}|{|~~}|||{yyyzzxvuuuwxxwxyz{zz{}~~~~~}}}zyyzzzyxyyxwvvwy{{{zz{|}||}~}{zzz{{zyyz{zyy{}~|yvuttutsstuvvvwy|~}}~~}~}}~~~~~~~}}~}{z{{{zzz{~~}}~}{zzzzzxuttuvwwwxz{{{{|}{yyyzyxwwxwvuuwy{||{{|~~~~~{yyyyzyyy{||||~~~~~~|yvutttsrrstttuvy|~~{yxxyyxwxyzyyyy|~~}}~~}|{|||{ywwwxyyxxyzzyxxz|}}}~|zyyyyxvvwwvuttvxyzzyz{}}}}~~}||}}}{z{{{yxxy{}~~~~~~~~|zxwwwwwvvwxxxxxz}~~}}~~}{z{|}~~}~~~}}{z{|}||}}|}|{zyy{}~~}|{{{zyxyyyywutuvxxxyz|}}}}~~}}}}}|yxwwxyxwwxyyxxxy|~~~~~~~~~|zxwwwwvvvxyyz{|~~~}}}}{yvtssssrqrstuuvxz~}zxwwwvuuuvwwwwy{~~~}{yxxxyxwwwxxwvvxz|}}}~}||~}zyxxwvutuutsssuwyyzzz|}~~~}}}~}|{zzzywwwy{|||||}~~~~~~|zyyyzyxxxyyyxxz|~~~}}~}|{{|||{zyyz|}~~~}|||zzz{|||}~~~}}|{yxxy{||{zzzzzyxyyzywvuvwyzzz|}~~~~~|{{{{{zxvvwxxxxxyzzzzz|~~~~~~}zxvuuuuttuwxxyz|~}}}~||{{zxusrrssrrrtuvvwx{|ywvuutsstuvvvwy{~~{yxxxxwvvvwvuuuvy{|||}~}}}~~~{ywwvutssttsssuwyzz{{|~~}}~}|zzzyxvuvwyzzzz{}~~~~}{{{{{{yyyzzyxxy{}}}|{|||zzz{{{zxxy{|~~}|||{zz{|}}}~~~~}||~~~~~~}}{zxxxz{{zzyzzzyyyzzzywvwxyzzz{}~~}}~~~}|zyzzzywvuvwxyxyz{|{{|}~}}}|{ywutttttstuwxxz|~}}}~~|{{{zxusssssssstvvwwy{~|ywutttsrstuuvvx{~|zxwwwvutuuutttuxz{{||~~}~~}~}zxwvutssstssstvyz{{||~~}}}}|zyyyxwvuvwyzzzz{}~~~~|||||{zyzzyxxxy{|||{z{{{zyzz{{zxxyz|~~~}|||{{{}}~~~~}}|{{|}}}~~}|{yxwxz{{zzzz{{zzz{{{yxwxyzzzz{}}}|}~~~}}{zyyzyxwvvvwyyyz{||||}~}|||{zxvtssttsstuwxxz|~}~~~}|{{zywutsstttttvwwwxz|~{xvutttsrstuuvwy|~|ywwvvutsttttstuxz{||}~~~~}~}zxwvvusstttttuwy{|||}~}|{{{{zyxxxwvuvwyzzzz{}~~~}||||{zyyzyxwwxy{{{zzz{{zzz{{{zyxyz|}~~~}}}|||}~~~}}}|{{|}}~~~}}|{yxxyz{{{zz{||{{{||{zxwxxyyyyz{||{|}~~~~}|zzzzzywvvwxyyyz{|}||}~~|{{{{zxutsttttttvwxyz|~~~~~~||{{zyvuttuuutuvwwwxy{~~{xvuuutssstuvvwy|~{xwvvuussttttttvy{|||}~~|zxxwvtttuuuuuwz|}}}}~~|{zz{zyxxxxwvvvxz{{{{|}~~~~}||||{zyyyxwwwxy{{{zz{{{{z{{|{zyxyz|}}~~}}~~}}}|{|}~~~~~}|{zyz{|||{{||}|{||||zywwxxxxxyz{{zz{}~~~}|{{{zyxvvvwxyyyz|||||}~}{{{{zxvttttuttuvwxxy{~~~~}}||zxvuuvvvuuvwwwwxz}~~|zxvvvutstuuvvwy{~~{ywvvvutsttuttuvy{||}}~~~|zyyxvuuvvvvvwy{|}}}}~~~~~~|zzyzzyyxxyyxwwxy{||{{|}~~~~~|{{{{zyyyyxwwwxz{{{{{{||{{{|||zyxxy{{||~~~~~~~}||}~~~~~}|{{|}}}}||}}}|||}|{zxwwwwwwwxyyyyz{}~}|||{zxvvvwxxxyz{{{{|}~}|{{{zyvutuuuutuvwxxyz}~~}|zxvvvvvvuuvwwwwxz|~~~|zxwwwvutuuvvvwx{}~{ywvvvutttuuuuvwy{|}}}~~|{zyxwvwwwvvwy{|}}}}}~~}}}~~}|zyyyyyyxxyyyxxyz||||||}~~}~~}{{z{{zyxxyyxwwxz{||{{||}||||||{yxxxyz{{|~~~}}}~~}||}~~}}~~}}||||{ywvvvvvvvwxxyyy{}~}}}|zxvvvwwwwxyzzz{{}~}||||{ywvuuvvuuuvwwxyz}~|ywvvvvvuuvvvvvwy{}}~~~}{yxxxwvuuvvvvwxz}~}{yxwwvvuuuvvvvvxz|}}}}~~}|{zxxxxwwwwxz|||||}}}}||}}|{zyxyyyyxyzzzyyz{|}}}||}}}}}}~~}|zzzzzyyxyyyxxxy{|||||}}}||||||zxwwxyyyz{|~~~~~~~~~~~}||||{ywvuvvvuuvwxxxyz|~~~}|{ywvvvvvvvwyyyzz|~~}}}||zxvvvvvuuuvwwwxy{~}zxwvvvuuuuvvvvvxz|}}}~~|zyyyxwvvvvvvvwy{}~~~~|zxxxwwvuvvvvvvxz{|}}}}~~~~~~~~~~}|{yyyxxwwxy{|||||}}}||||||zyxxyyyyyyzzzzz{|}~~}}}}}}||}}}|{yyyyzyyxyyyyyyz|}}}}}}~}}||||{zxwwwwxxxy{|}~~~}|||{zxwuuuuuuuvwwxxyz}~~~}{ywvuuvuuuwxxyyz{~~~}}|zxwvvvvuuuvvvwwxz|~}zxwvvvuttuuuuuvxz||}}~~|zzyyxwvvvvvvvwy{|}}~~~|zyxxxwvvvwwvvwxz{||||}~~~}~~~}~~~~~~~}~~}|zyyyxxwxyz{|||||}}||||||zyxxxyyyyyzzzzz{|}~~}}}}}||{|||{zyxxyyyxxyyzyyyz|}~~}}}~~}||||{zxwvvwwwwxy{|}~~}|||{zxvuuuuuuuuvwxxyz}~~}{ywuuuuutuvwxxxz{~~~}|zxwvvvvutuuvvvwxz|~~{yxwvvuttuuuuuvxz{|}}}~~|{zzyywwvvvvuvwxz||}}~~|zyyyxxwvwwwwvwxz{||||}}}}}}~~~~}|}~~~~}~~~~~}|zzzyxxxyz{|||||}}|||||{zyxxxyyyxyzzzzz{|~~~~}}}}|{{{{{{yxxxxyyxxyzzzzz{}~~~~}~~~}||||{ywvvvvvvwxyz{|~~}||{{ywvuuuuuuuvwxxyz|~~}|zwvuuuuttuvwxxy{}~~}{yxwwvvvuuvvvvwxy{}~}zyxxwvuuvvvvvwxz{||}}~~|{{zzyyxwwwwvvwxz{|||}~~}{{zzzyxxxxxxwxyz{|||||}}}|}}}~}|||}~~~~~~~}||{zzyyz{||||||}||||||{zyyyyyyyyz{{{{{|}~~~~}}}}|{{{{{zyyxyyyyyzz{{{{{|}~~~~~~~~}||||{zxwwwwwwwxyz{|}~}|||{yxwwwwwvvwxxyyz{|~~}|zxwwwvvvvvwxxyz{}~~}|zyxxwwwvvwwwwwxy{|}~}{zyyxwwwwwwwwxy{||}}}~~~~~}||{{{zyyyyyxxxxyz{|||}~~~~~~}|{{{{zyyyyyyyyyz{|||||}}}||}}}}|||}~~~}}||{zz{|||||||}}||||||{zyyzzzzzz{{{{||}~~~~}}}}||{{{{{zyyyzzzzz{{|||||}~~~~~~~~}||||{zyxxxxxxxyyz{|}~~}}||{zyxxxxxxxxyyyzz{|~~}|{yyxxxwwwwxyyyz|}~~~}|{zyyxxxwwxxxxxyz|}}~~~}{{zzyxxxxyxyyz{||}}}~~~~~~~~~}|||{{{zzzzzyyyyz{{|||}}~~~~~~}|||{{{zzzzzzzzz{||||||}}}|}}}}}|||}~~~~~}|||||}}}}|}}}}|||||{{zzzzzzz{{|||||}~~~~~~}}}||||{{zzyzzzz{{{|||||}~~~~~~~~}}||||{{zyyyyyyyyz{{|}~~}}}|{zyyyyyyyyyzzzz{|}~~~~}|{zyyyxxxxyyyzz{}~~~~}|{zzzyyyyyyyyyz{|}}}~~~}||{zzyzzzzzz{{|}}}}}~~~~~~~~}}|||||{{{{{zzzz{{|||||}}}~~~~~~}}|||||{{{{{{zz{{||||||}}}|}}}}}|||}}~~~~}}}}}}}}}}}}}}|||||{{zzz{{{{{{||||}~~~~~~~~~}}||||{{zzzz{{{{||||}}}~~~~~~~~~}}}|||{zzzzzzzzz{{||}~~~~}||{zzzzzzyzzzz{{||}~~~~~~}|{{zzzyyyyyzzz{|}}~~~~}|{{{zzzyyzzzzzz{|}}}~~~~}||{{{{{zzz{{||}}}}}~~~}~~~~}}|||||||{|{{{{{{|||||}}}}}~~~~~}}}||||||||{{{{{||}}}}}}}}}}}}}}|||}}}~~~~~~~~~~}}}}}}}}}}||{{{{{{{{{||||}}}~~~~~~~~}}}||||{{zz{{{{{||||}}}~~~~~~~~~~}}}}||{{zzzzzz{{{||}}~~~~}||{{{{zzzz{{{{{||}~~~~~~}}||{{{zzzzzz{{{|}}~~~~~}||{{{{{zzz{{{{{||}}}~~~~}}|||{{{{{{||}}}}}}}}}}}~~}}}}|}}||||||||||||}}}}}}}}}}}~~~}}}}}}||||||||||}}}}}}}}}}}}}}}}|||}}}~~~~~~~~~~~~~}}}}}}||{{{{{{{||||}}}~~~~~~~~~~}}}}|||{{{{{{{||||}}}}~~~~~~~~~~}}}}}||{{{{{{{{{|||}}~~~}|||{{{{{{{{{{{||}}~~~~~~~~}}||||{{{{{{{{{||}}~~~~~~}}||||{{{{{{{{||}}}}}~~~~~}}}|||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||}}}}}}}}}}}}}}}}}}||}}}}~~~~~~~~}}}}}}||||||||||||}}}}~~~~~~~}}}}|||{{{{|||||}}}}~~~~~~~~~~~~}}}}||||||{{|||||}}~~~}}|||||{{{||||||}}}~~~~~~~~}}}||||{{{{{{||}}}~~~~~~~}}}}||||||||||||}}}}}~~~~~~~~}}}}|||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~}}}}}}}}}}}}}}~~~~~~~~}}}}}}||||||||||}}}}}~~~~~~~}}}|||||||||||}}}}~~~~~~~~~~}}}|||||||||||}}}~~~~}}}|||||||||||}}}}~~~~~~~~~}}}}||||||||||}}}~~~~~~~~~~~~~}}}}}}|||||||}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}~}}}~~}}}}}}}}}}}}}}}}}}~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~}}}}}|||||||}}}}}}~~~~~~~~}}}|||||||||}}}}}~~~~~~~~~}}}}|||||||}}}}}~~~~~}}}}|||||||}}}}}~~~~~~~~~~~~~~}}}}}}|||||||}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~}}}}||||||}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m40.bin b/applications/SprPoP/assets/packed/MUS/m40.bin new file mode 100644 index 0000000..018cc3b --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m40.bin @@ -0,0 +1 @@ +xmd`bis|{qjd``cipy~wsojgdccbaaaaaabbcfhkosy}skeabfnwzrjfcaacfhkptx{|}{yvtpligecbbbdgjosy~|upjfcbcejov}yuqmkigeddccccccccccccccccddeffhjkmoruwz}}yvrnkhfeddegjmqv{|zywutrrqqppoopopppqqrsstvvwyz{|}|zxvtronljhgffeffgikmpsvz}~~}}}}}}}}}}}~~~~~~}|{zyxwvtsrqonmljihhggggghijklnpruwz|~~}}}||{{zyyxwvvutsrrqponmlkkjiiihhhhhiijklmoprtuwz|~~~}||{zyyxwwvutssrqpoonmmlkkjjjiiiiiijjklmmnoqrsuvxz{}~~}|{zyyxwvuutsrqqpoonnmmllkkkkjjjkkkllmmnopqrstuwxz{|~~}|{zzyxwvuutsrqqpoonnmmllllkkkkklllmmnoopqrstuvwxz{|}~}|{zyxwvvutsrrqqppoonnmmmmmmmmmmmnnnoppqrrstuvwxyz|}~~}|{{zyxwvuutssrqqppoonnnnnmmmmnnnnooppqrrstuvvwxyz{|}~~}|{zyxxwvuttsrrqqpppoooooooooooopppqqrrsttuvwxyyz{|}~~~}|{zyxxwvuutssrrqqppppoooooooppppqqrrsttuvvwxyyz{|}~~~}||{zyxxwvuuttssrrrqqqqppppppqqqqrrrssttuvvwxxyz{{|}~~}||{zyyxwvvuuttssrrrrqqqqqqqqqqrrrssstuuvvwxxyz{{|}~~~~}|{{zyyxwwvvutttssssrrrrrrrrrrrssstttuuvvwxxyzz{||}~~~}|{{zyyxxwvvuuutttsssssrrrrssssstttuuvvwwxxyzz{||}~~~}}|{{zyyxxwwvvuuutttttssssssstttttuuuvvwwxxyyz{{|}}~~}}|{{zzyyxwwwvvuuuutttttttttttttuuuvvvwwxxyyz{{||}~~~xtrsuz}~zwtsrsuy||zywutsssssrsssssstvwxz|{wtsstw{~zwutsssuvwyz|}~~}|{yxvuttssstuvxz|~|ywutsstvx{~}{yxwvuutttttstttttttssstttttuuuvwxyz{|}~}|zxwvuttttuvxy{}~}}|{{zzzyyyyyyyyyyyzzzz{{||}}~~~}}|{zyxwwvuuutuuuvwxyz{}~~~~~~~~~~~~}}}|{{zzyyxxwwvvvuuuuuuvvwwxyz{|}~~~~~}}}}||||{{zzzyyyxxwwwvvvvvvvvvvvvwwxxyzz{|}~~~~~}}}|||{{{zzzyyyxxxxwwwwvvvvvvvvvvwwwxxyyzz{||}~~~~~}}}|||{{{zzzyyyyxxxxwwwwwwwwwwwwwwwxxxyyzz{{||}}~~~}}}|||{{{zzzyyyxxxxxwwwwwwwwwwwwxxxxyyyzz{{{||}}~~~~~}}|||{{{zzzyyyyyxxxxxxxxxxxxxxxxxyyyyzzz{{|||}}~~~~}}}|||{{{zzzzyyyyyxxxxxxxxxxxxxxyyyyzzz{{{|||}}~~~~~}}}||{{{{zzzyyyyyyyyxxxxxxxyyyyyyzzzz{{{{||}}}~~~~~~}}}|||{{{zzzzzyyyyyyyyyyyyyyyyyyzzzz{{{|||}}}~~~~~~}}}|||{{{{zzzzzzyyyyyyyyyyyyyzzzzz{{{{||||}}}~~~~~~}}}|||{{{{{zzzzzzzyyyyyyyyzzzzzzz{{{{||||}}}~~~~~~~}}}||||{{{{{zzzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~~~~~~}}}|||||{{{{{zzzzzzzzzzzzzzzz{{{{{|||||}}}}~~~~~~~}}}|||||{{{{{{{{zzzzzzzzz{{{{{{{{|||||}}}}~~~~~~~}}}}|||||{{{{{{{{{{{{{{{{{{{{{{{|||||}}}}~~~~ymd`bhs|{qjda`cipywsojgdccbaaaaaabbcfhkosy~tkeabfmwzrkfcaacfhkptx{|~~{yvtpligecbbbdgjosx~|vpjfcbceiou|zuqmkigedddcccccccccccccccddeefhjkmoruwz}}yvrnkhfeddegjmqvz|zywutrrqqppoopopppqqrsstvvwyz{|}|zxvtrpnljigffeffgikmpsvz}~~}}}}}}}}}}}~~~~~~}|{zyxwvtsrqonmljihhggggghijklnprtwz|~~}}}||{{zyyxwwvutsrrqponmlkkjiiihhhhhiijklmoprtuwy|~~~}||{zyyxwwvutssrqpoonmmlkkjjjiiiiiijjklmmnoqrsuvxz{}~~}|{zzyxwvuutsrqqpoonnmmllkkkkjjjkkkllmmnopqrstuwxy{|~~}|{{zyxwvuutsrqqpoonnmmllllkkkkklllmmnoopqrstuvwxy{|}~}|{zyxwvvutsrrqqppoonnmmmmmmmmmmmnnnoppqrrstuvwxyz|}~~}||{zyxwvvutssrqqppoonnnnnmmmmnnnnooppqrrstuvvwxyz{|}~~}|{zyxxwvuttsrrqqpppoooooooooooopppqqrrsttuvwxyyz{|}~~~}|{zyxxwvuutssrrqqppppoooooooppppqqrrsttuvvwxxyz{|}~~~}||{zyxxwvuuttssrrrqqqqppppppqqqqrrrssttuvvwxxyz{{|}~~}||{zyyxwwvuuttssrrrrqqqqqqqqqqrrrssstuuvvwxxyzz{|}~~~}|{{zyyxwwvvuuttssssrrrrrrrrrrrssstttuuvvwxxyzz{||}~~~}|{{zyyxxwvvuuutttsssssrrrrssssstttuuvvwwxxyzz{||}~~~}}|{{zyyxxwwvvuuutttttssssssstttttuuuvvwwxxyyz{{|}}~~}}|{{zzyyxxwwvvuuuutttttttttttttuuuvvvwwxxyyz{{||}~~{wsrsw{}yvsrstvz~~|zxvutsssssrssssstuvwyz}~zvtssuy}|yvtssstuvwy{}~~~}}{zywvutsssstuwy{}}{xvttstuwy|}{yxwvuttttttststttttssttttttuuvvwxyz{|}~|{yxwvutttuuwxz|~~}||{zzzzyyyyyyyyyyyzzzz{{||}}~~~}|{{zyxwvvuuutuuuvwxy{|}~~~~~~~~~~}}||{{zzyxxwwwvvuuuuuuvvvwxxyz{|}~~~~~~}}}}|||{{{zzzyyxxxwwwvvvvvvvvvvvwwwxyyz{{|}~~~~~}}}|||{{{zzzyyyxxxwwwwwvvvvvvvvvwwwxxxyyz{{||}~~~~}}}|||{{zzzzyyyxxxxwwwwwwwwwwwwwwwxxxyyyzz{{||}~~~~~}}}||{{{zzzzyyyxxxxxwwwwwwwwwwwwxxxxyyyzz{{||}}~~~~}}}|||{{{zzzyyyyyxxxxxxxxxxxxxxxxxyyyyzzz{{||}}}~~~~~}}}|||{{{zzzyyyyyxxxxxxxxxxxxxxxyyyyzzz{{{||}}}~~~~~}}|||{{{zzzzyyyyyyyxxxxxxxxyyyyyyzzzz{{{|||}}}~~~~}}}|||{{{{zzzzyyyyyyyyyyyyyyyyyyzzzz{{{{|||}}}~~~~~}}}||||{{{zzzzzzyyyyyyyyyyyyyzzzzzz{{{{|||}}}~~~~~~}}}}|||{{{{zzzzzzzzyyyyyyyzzzzzzzz{{{{|||}}}}~~~~~~}}}}|||{{{{{{zzzzzzzzzzzzzzzzzz{{{{{{||||}}}~~~~~~}}}}||||{{{{{{zzzzzzzzzzzzzzzz{{{{{||||}}}}~~~~~~~}}}}|||||{{{{{{{{zzzzzzzzz{{{{{{{{||||}}}}~~~~~~~}}}}}|||||{{{{{{{{{{{{{{{{{{{{{{|||||}}}}}~~~ymd`bhs|{qjda`cipywsojgdccbaaaaaabbcfhkosy~tkeabfmwzrkfcaacfhkptx{|~~{yvtpligecbbbdgjosx~|vpjfcbceiou|zuqmkigedddcccccccccccccccddeefhjkmoruwz}}yvrnkhfeddegjmqvz|zywutrrqqppoopopppqqrsstvvwyz{|}|zxvtrpnljigffeffgikmpsvz}~~}}}}}}}}}}}~~~~~~}|{zyxwvtsrqonmljihhggggghijklnprtwz|~~}}}||{{zyyxwwvutsrrqponmlkkjiiihhhhhiijklmoprtuwy|~~~}||{zyyxwwvutssrqpoonmmlkkjjjiiiiiijjklmmnoqrsuvxz{}~~}|{zzyxwvuutsrqqpoonnmmllkkkkjjjkkkllmmnopqrstuwxy{|~~}|{{zyxwvuutsrqqpoonnmmllllkkkkklllmmnoopqrstuvwxy{|}~}|{zyxwvvutsrrqqppoonnmmmmmmmmmmmnnnoppqrrstuvwxyz|}~~}||{zyxwvvutssrqqppoonnnnnmmmmnnnnooppqrrstuvvwxyz{|}~~}|{zyxxwvuttsrrqqpppoooooooooooopppqqrrsttuvwxyyz{|}~~~}|{zyxxwvuutssrrqqppppoooooooppppqqrrsttuvvwxxyz{|}~~~}||{zyxxwvuuttssrrrqqqqppppppqqqqrrrssttuvvwxxyz{{|}~~}||{zyyxwwvuuttssrrrrqqqqqqqqqqrrrssstuuvvwxxyzz{|}~~~}|{{zyyxwwvvuuttssssrrrrrrrrrrrssstttuuvvwxxyzz{||}~~~}|{{zyyxxwvvuuutttsssssrrrrssssstttuuvvwwxxyzz{||}~~~}}|{{zyyxxwwvvuuutttttssssssstttttuuuvvwwxxyyz{{|}}~~}}|{{zzyyxxwwvvuuuutttttttttttttuuuvvvwwxxyyz{{||}~~}xtrsvz~~zwtsrsvy}|zywutsssssrsssssstvwxz|{wtssux|}zwutsssuvwyz|}~~}|{ywvuttssstuvxz|~{ywutsstvx{~}{yxwvuutttttstttttttssstttttuuuvwxyz{|}~~}{zxwvuttttuvxy{}~}}|{{zzzyyyyyyyyyyyzzzz{{||}}~~~}}|{zyxwvvuuutuuuvwxyz|}~~~~~~~~~~~~}}||{{zzyyxxwwvvuuuuuuvvvwwxyz{|}~~~~~~}}}}||||{{zzzyyxxxwwwvvvvvvvvvvvvwwxxyzz{|}~~~~~}}}|||{{{zzzyyyxxxxwwwwvvvvvvvvvvwwwxxyyzz{||}~~~~~}}}|||{{{zzzyyyyxxxxwwwwwwwwwwwwwwxxxxyyzz{{||}~~~~}}}|||{{{zzzyyyxxxxxwwwwwwwwwwwwxxxxyyyzz{{{||}}~~~~}}|||{{{zzzyyyyyxxxxxxxxxxxxxxxxxyyyyzzz{{|||}}~~~~}}}|||{{{zzzzyyyyyxxxxxxxxxxxxxxyyyyzzz{{{|||}}~~~~~}}}||{{{{zzzyyyyyyyyxxxxxxxyyyyyyzzzz{{{|||}}}~~~~~~}}}|||{{{zzzzzyyyyyyyyyyyyyyyyyyzzzz{{{|||}}}~~~~~~}}}|||{{{{zzzzzzyyyyyyyyyyyyyzzzzz{{{{||||}}}~~~~~~}}}|||{{{{{zzzzzzzyyyyyyyyzzzzzzz{{{{||||}}}~~~~~~~}}}||||{{{{{zzzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~~~~~~}}}||||{{{{{{zzzzzzzzzzzzzzzz{{{{{|||||}}}~~~~~~~~}}}|||||{{{{{{{{zzzzzzzzz{{{{{{{{|||||}}}}~~~~~~~}}}}|||||{{{{{{{{{{{{{{{{{{{{{{{|||||}}}}~~~~~skzjg~qot~wvekizsyynmltxcwqx~|uow|}~}}qygjqN\i}xtOptQklwt~n~l`~}uYPwjP}`_oao|ztnyb|Zym`]sfzzaObfTvvx}{an}ubNiyOltR{dfpwnw~vnsvt~^OVXmsS{azizv{n}f^yuiPY|^ZYilbjvm~zwucLSklUd{T^w_qxktinrRfogTQunPzlY}ddmmrxOS_yzY|gNbx{~dYsehjbr\Tmj_Mc}YcYomcgpho~]NYnnwYXo{ztQek_jaglNcmgUSxqT|p[icjit}}jmvUYds]fyzy\Xp`fg`o`Ohe]Pi\f^nueelg~{ubvhS]no_p}{tRfl`idbqSYicWTvxUxyZ~ldgkm{xnbzbZexhbsyxbUrcehcgoN]b\Rah[kakdjhywejwY_kzcizyvVapahh`neNc`YRn\i`myhdkjzuu`nv^gx~{ahuskPmjchh`raO_ZUXyxWt]uxigkt{xro\tp`lsamvteSsefggbuYS`ZT^qX}x]~ugik|y|uok\zjdpzncqvv^[wajghbwWT[WUhj]s_wjln}vvsljZ{plywm`mpoX^uajggcwTV[WVlg`qawjmo~xxfClnvZofm`P]Ia{nMMMoDM|rsuduy`pwxyuś˱qp{}PKKLY|OgmKyZ_ZnljPwWss\y|eOhOqxedmdz|}yArb=^]A5bucwmu_er~jGXBRRri7iaYdh_WswȪo{hqdjLP_PBZr~thgt[}]qv[gHdsml\rxqaCoqjOfvpp[`]^^i^֮԰yZsrbzlimg;k|ePqhykdj{wjd_oXsczwfxto^rn˹||~vz{zig=V:UmquNbjikZlmqtkY\YeWvv}~{n{bzsz¹{pX^F[Lb|lovnqX`kFXec}w}gDd`yn]p{vt}u}g¦İv^SmRObKZmyeycbs^PR]n_t~dR|odne[xrpw|qjhv`?`GSmp|mziTYUX|ear[aWKhgA[x|hkmfl_n~t^k_IQ\>\{oqwmjVVb]K^h8]{un{keoZgqr}whkkBej@sk|m{~z|iRk`Iwg9jkppkgdpgp}qv|wwlqLl`Mrwkos~cr}q]}zggJf{lwaVu^e}o~{u}wyrpuP{tb~~~s`uEWpwrYVzxpk^{cru^vpf|yo|{wtsgQyve}x||Mfka`SLx|V]~Ozpuo~kyty~]w~omzqip~jujP̿~\WcZoQestqkruxVxooqrXsv|ijm[s{l|pRhHReg{pmt5Ba0<6FsCam|MSn_f~joʴph_Ma\J][hmi]Qfbbq\Wo[X^^`=H~aSYn}jbI=ZUX]mƳwnpU^zVPWUhxafjypRerPmgW{vsjirgenRTVm|dkyxjZY[XXBdipxUqiscb~|wtwȁyrfljfjYKcrrqKxqqPv{jryjjy}}VSjtdLrlrD&Y]F+3h>P~X{gwyTy}lTltJwϦ}}r|~}YvOeYInq~`K%QH80?aHMYW|Lbkxıä̻m`].DFpXornKeaa7EnBFE@lL=R@<]hjX}vdm}qvt{zybuݳמv{vquxjed4YfV8TY[wYijadiNf`sqçɾœ{r}xu}O[PSO'9AE848* H68pr[|yirkkff{mWozh\ln\x_lbkMh}t]vZEcTJRBWe`kutqilyZb{jpbQFAA.8UGRYS[EJoVUtiuYdRrglyjdzv~wǧhrqWx{jyf}tqWRiv`ssjyb[fkktoy|cwhenkq|iUlb`tradvyzb{{]sdlbTY}\qymrrhjcPegsupomypEW``DS8F?.LZ^MaiOnu]Yuqtuu`jansʢ{bgeuQc`KXOSlfTy\piblztyvsnzdbhmWFRFpcg{dj|KfkcWk]WXWl~kYt~Uplkgosunnro^][XHVTR;YQE;=WkWAju\xwm~xvorxs]vvjLngbXO^tkapnr{~ymwwjw[ce|kbfjgl{{yv{uyo}_UsylQl^gjinjhs~wop_ji`wsbxzsfjfxRPZcWU`N^GWjdHXee{p{s}zabz~zvi~r~x~|k}sgbkg\eqmXhlstraozRkz}xdvyrNgYtpciqe[zv]w[eqdllznXx}s{|fxzaoa~p^fskd}zus{uPgdmOckWTQanbMvuMbx|wszvx}ndsiwuyt}x}f||SfsjlekhWSwr_czq}c|}dp]}}vojmkzYzw[diV\`]haHv{dtt}{~|x{{j~{cy^uoluhe|`zmoRY`]eWcohZn}ojz|zj{|fp`pjh`dxvsWuevYLfajiqxxryt}}v}x|vnygrsoiXLh~zjwyr[zqw{uZr`\jov{kluxluwpspwlwyrrib[Re{ymuswoolym}csi[jxp~b{xryvspancxoeXq~|x[luqvp{|s|olhhilxktptqqj[zvjgI`JXabZNNXqw{sxzv~yzmgo{gqvyrlmdysuRgd\}|rjfg¤j~y[tntz~gr{g_lx~isqs{t{}yrcawk]UbPpzwoidz}v{|iip`h}}syxw~~|txi}jrrk^[zfiMj]l|nsx~ltaxu|}rpbqoykju\Zbk{Wk|bpvhfav}_vx{~svsdsziy{l|hcdhv`noq{ong{{]\fZquup`\xyztts|dz`swrwynommnuw~}{e\sgw|whg}w~s|\j}x~rsryhjpupclSpve`]bpss~}vpx|eVtcq}vs|ov{|mkvzvox^}ujjq}}p{unkuiXGcXgtrj`]qsw|otje{wvs{mfrxvmzvvwkVl_l{}{tpw~uyjapsz~je`qcYgmqkyepknyvtk~zurzun_Plft~vslj{yqopnyyxsqswptkwngoml[Md[hommgcw|}~{mz|^^qy}wqoqxwxxxyp~|xy`Vkgswywos{|~j~~X_rx~rmsn}{zpzwiqhglwzh|p}{wnuUYkhx}{vq|ygts}qurwkfjt~wsrntxuyhiLZ^^nrlb]ks{y|x~fxuts|rsyrt{z}tpVhgeprohfuwp}uhyhygwqhsbrsjiotzr|}oy||xkcK_^amomkn|{xzq}qrrp|msjmsv{sjuwtwiaW@TTZed`_evpnxzrpwmn}vvpyrt{|nw}|picM]^eqrokpiryrfdrfksyvhdshxvkinuzr|udhxtw}jcaL]bdpvww~s|vrpqt}~qkyo~|ojkptoy|ogdupsxl_]GYacqxyvx|jqz}qlltr}~vq}r~|plntywwuj}oq\hspz~~noxtkg}b|virrmephh{vicgmomts{aw}rs^^onw~~z||t{uop~owuqfnmgoegotttvgyml]Scahsvrqzz}ot{vmutqjorqgkrj~squ{y|wmgVfjktyzy~zmpxsgbx^xgaikfaibo}rory~}zl|ifRXhirxzz}yryzmdtew{elopjrpmyx{~gx~}igYN`ekuz{|jnv}|pilsjmnssnryqzvy||xymj|{}ld^M\iluzzz{iir{sjijtoxywv~uyqrxzwqvdyy{|sfdPVeir{~}~pkry}rkor{|yu}vy|spsuuzq~av|}wecRRfjow|~vlt}yrxry{ztxspyqorrsuu{ds{zz{hdTNbkry}~~ouv}{quvvswtnzpmprtuwwbo|||}xheYO`kpwyz{}uzvz{xpsusossm|sprrsrxvdlz{}|idXK[fksvxy{yz}ru~umossoqqk~|qmoqssxqfm{{|}yf`UHWdjsx{{xx}rrzvknqrosrm}tnnnpswsdmyxyztc^SIZgmsw{uxzptvxjpsspsokyqnppqssr^kvvxzteaRI[iqy|~tu~yovu{joqrotplzqnommplr}\oxzzzqdbUOamu|sv~yo}o~irrqnrmqumjjjjsh{y]sx|~sgcSUgs{|sxvmovhqrrprkt|piijklzfrd{|skeU\lu|{tzwnqqkqrppnguzpkkijm~kmktmcU^muz{|zxt|~uprninommlgvxolmmnp~onpslaR[ku{|{{yyt{up~rihmnmomhw|rnpoqttnt~oj_S\jrwyyzzyv}|rozsijnolmlj{}uqsrsvtlr~og[OZiqwyyzxzu{|rm~sihorqqol|xstrsunmo|ng\OWgrxzz{xysx~ulrpkqtttso}ysrpprmmj|ng]OVfpvxy{uzsywosumuwxwvq}vqqpooiqe}~}pg^PScpxzyz~xsv|qtnvxyxwpwyqnmlmyi|zay~~|rhaTQ`muz{|~{wxvznuwxutosxplljksns`s}vibTN\kuz{{}{{y{znquxvuon}zqlljjnviel~~~|wldYNVgryzz{w{svymsuuspkv~smkihj{e~q_x|~|zof]OP`nv{{}z}~uvjqsurqlounjkhipko~]p{|{zrg_SLYju|}~zytzomrutrmkyzpihfgjxdddyyzxtibXNUfs|~z~{psvjrttqnkvzpjhgfgx}_zj\vxzywldZOSeu~yz~qr~hpsvsoip}rjgfceqbrtZsyzywphaUSctzyszvjortqojm}~rjfebdlhkyYnz||{tkdYUbs{x~uxyjnrtrojl{~rjffcdjji}]n|}~|wng[Vas|x}vt|}lnqtroijxtkfecflqham~}xoh]V_p}~y~xszllpsqnjjxtmihfgmticm~}xng[T^p}}x}ws|{klqtroijywojjgioskbo~~}{vnf[T^o{}y~utz~knqtrpkn}wpkkhjqpm_o}~~{uldXS]o{|y~uwykptwurmpwpljhiqko}^r|}|yskbVS_p|{zs}vltwyvsnuunjigjvfvu]v{|zxpi_TTbr|{{|twowyzwrox|smkigjzb{lay{|ywnf\RVet}x{~v{zrx{zvpozzqjiffj~yb~eez{{yumf[RVft|w~x~xrxzyupp{xojhefjvc~cfz{|yuldXQWgu|~~wyusx{ytoozvnihefjteahzzzwtlcXQWgt|}}~xzutwzwsno{umhgefkse}_hyzzwskcWPWft{}}~wyurwywsnnyvniheei~tb|beyyyvskcXQVes{}}~xw|wqwyxsnmxwmhfddh|w`{dbxxyvsldYQUcr{~ywzzpvxxsomwwnhfdcgxz_xg^vxyvtle[RUcs}zwxovxytokryohecbdr`rpZswxvuoi_VUaq|y{otvxtpkp}{piedbdmfivZnxzywslcYV_o|}zqrvxuqkmy}rjedach}pb{_gyzzxuog\W\ky|uxzouwvrlksulfdcbfv}`wm^wz{ywrkaXYet~xxmsvwsoko{zpifeceniky\oz{zxtmdZW`p}}v}squxupklwtlgfdei}ub{cexyywuoh^W\jy}txzouwwrnmvwnigedgw|_vi^vxzwvpi_WZgv~vwovxyupmszpjfeceqbpr[qwywuqjaXXcsy}|qvx{vrnr}|qjfdbemghvYlwxvurkcYW`p}||svy{wrnq||rjfdacj~ievZivwvurldZW`o||uvy{wrnp{}rjedaci}kbw[guvvtrld[W^mz{vuy{xsnpy}qjecabg{maw[euvvtqldZV^lz|uvy{wrmoz|qidc`bh|kbvZfuuusqkcZV^mz}tvyzvqmoz{phdc`bi}hcuXhuuuspjbYV^n{svxzvqlpzzohdb`bj}dfsWktutspjaXW`o{||rwyzupmr}wmgcb`cn{_mmXotutsoh_WXcr}~z{rxzytomtulfcaadtx]sf\rtvsrmf]WZgu|z}|tyzxsnnv~sjecaaexs^v``tuvtsmf\W\iw{zytxzxrnnw}rjecabfyq^v_buvwusnf\X]jx{{xuyzwrmnw}qjecabg{oax^evwwutnf\X^kx||wvy{wrnox|qiedbch|obx^fvwwutnf\X]kx{{xuy{xsnow}skfebdh{raxadvwwutnf]X]jwy{~{uy{xsnowtlgecdhzu`xcbvwwutog]X\hu~x{~}ty{ytppwvmhfddgwx_vg_tvwutoh^X[fs}y{|tz{{vrouxoifdcfr}apn[quvutpj`YYcq{}~uz{|xsot}zpifebem~fhsZkuutsqkcZY`nyxy{}ytprz}rkfebdj{mav^brtusqle\X^jv~z}~vy{yuqpwvmgdbbftw]qhZnsusrnh_YZer||vyz{vrps|yoidcadl{dfrYgrtsrojb[Yany{wyzwsopx|rjecabgwn]t_^prsrqle]Z^jw}}~uxyxsoou~tkfcaaest[peZmqsrqmg_Z]hu~}}twwwspns|vmgca`dpw]jmWhpsrqnha[\er}~vvwwtpnqzxngca`clxadqXdprrqojb\\dq|xvwxtqnqyxohca`ckxcbrYborrqojd^]dp|yuwwtpnpxyohcb`bjxf`r[`orsrple_]cp{{uvwuqnpwypidb`biwg_s\`orsrqlf_]co{{uvvtpnpxzpidbadlxeas\`orsrpke_]dp{zuvvtpnqyypiecbenydds[borsrojd^^dp{zvvwtqorzypjfdcfoybfrZdorsrojc^^eq{~xwwwtqpt|xoifddhsv^lmYhpsrqnha\^gr||~wxxxtrqwwoifdejvr^qh[kqsrplf_\_hs|{xxxwtrsy~vnifegmym_td\kprqojd^[_it||yyywtrtz~unigehnyk`tb]kpqpnid^[`jt||zyzxust{~unigehnyiat`^lpqpnic][`jt{}zzzxusu{}unifehoyhat`^lpqpnic][_it{}zzzxusuz}unigegmxj`sb]koqomic][_hrz~|{yyxustz~vojgfhnxk^rd[hnpomic][^grz~{{zyxusty~wpjgfgmvn]qgZfmoomid^[^fpy~z|yyxussw~xpkgefktr]klYcloonjf`\]dox~{~yxxvsrv|yrkgeeiqu`fp[`jnonkga]]cmv}~zxxvtrtz{slheeipve`ra[gmonlhc^]bkt|{xwvtrsw~}vojfeflun\niYckoonjfa^`hrz}~xwvtrru{xqkgeeiqs_ep\_inonlhc_`eoxywvurqsx~{smhedgnug^qc[gmoonjea_clv~zwvusqrw}|unifefltl]nhZdloonkgb`ckt}~|xvusqru{}wpkgfglsp]jn[`inonkgc`bhr{xvusrqtz~xqlhffjrr_ep]^hmonlhd`agpyywvtrrtzyrlhffjqsacq^]gmnnlhdaagoxzwvusrtyzsmhffiptbbq`\glnnlidaafoxzxwusrty~zsmiffiptcaq`\flnmlhdaafnw~{xwussuzzsniggkqsabp`\ekmmkgc`afnw~{xwussuzysmiggkqr`cp_\fkmljgc`afnw~{xwutsv{~yrmiggkrq_eo]]fklljfb_afow~~{xwuttw|~xqlighmsn]jl[_gklkiea_ahpx~}{yxutux}}wpligintj]mhZahllkhd`_biqy~zywuuvz|vplihkqtf_oeZbhkkjgc__bjry~~zywvuvz|vpliikqsd`pd[bhkkifc__cjry~~{yxvuw{|upliilqsdapc[cikkifb__cksy~}{yxvuw{{upliilrscapc[cikkjfc__cksy~}{yxvuw{|vpljilqse`pe[cikkjgc``cjrx}}{zxvvw{|wqmkjmrtf`pg[agkkigc`_ciqx}}|zxwvwz~}wrnkjlqth_ni\`gjkjgda`bipw|~}zywvvy}~ysnkjkptl^km]_fjkjhea`bgov{|~zywvvx|ztoljkoso`fo`]dikjifb`afmtz}{yxvvx{{upljjnrqbbpe\agjjifcaaekry~~|zxvvwz}|wrnkjlpsh^lk\^eijigdbacipw}~zxwvvx{}ysokjknrn_eo`\chjjhfcacgnu{~{ywuuvz}~zupljilppc`nf[`fijigdbbfltz{ywvuvx|~~zupljikoqf^li\_eijjhecbekry~}ywvuux{~~{wrmkjkoqi]hm^]cgiihecbdjqx}~zxvuuwz}}|wrnkijnqk^en`\bgiihfdcdipw}zxvuuvy|}|xsnkijmpl^cnb\afiihfdcdiov|zxvuuvy|}|xsnkiimpm_bnc[afiiigdcdhov|zxvuuvy|}|xsnkiilpn`anc\afiiigecdhov|{xwuuvy|}|xsoljkmpl_bnc\`ehihfdcdiov|{xwuuwz|}|xsoljknql_cnb\afhihfdcdiov{{ywvvwz}}{xsolkknqk^ena\afhihfdceiov|{ywvvx{}~{wsolkloqh^il_]cghhgeccejpw|{yxwwy|~~{wrnlkmpqf_lj^^dghhfdbbekqw|~|zxxy{}~{wrommorocamh]_dghgecbbekqw|}|zyxy{~~{wrommorobbng]_dgggecbbfkqw{}|zyyz|~~{vrommprnbbnf]_dgggecabfkqw{||{yyz|~{vrommprnbcnf]`dggfecabekqv{~|}{zyz|~~{wsommorobbng]_dfgfecabejpvz~|}{zyz|~~{wspnnprocami^^beffecabejpuz}|~|zyz|~~|xspnnoqod`lj_^beffecbbdiouy}|~|zyy{}~|xtqnmnqpg_il`]aeffedbbdhnty|~|zyyz|~~|yuqnmnpqj_fmc]`dfffdcbdgmsx|~|zyyz{}~|yuqnmmoplabmf]^beffecccglqw{~}{yxyz|}}zvsommnpnd_jk`]adffedccfjpvz~~{yxxy{||{wspmlmooh_emc]_cfgfeddeinty~{yxwxz{|{xtqnllnolaalg^^beggfedehmsx}|yxwwyz{{xuqnllmomc_jj_^begggeeehmrx|}zxwwxz{{yvromlmone^glb]`dfgffeehlqw{}zxwwxy{{yvsomlmong^eld^`cfggfeegkpv{~{ywwxy{{ywspmlmnoh_dle^_cfggfeegkpuz~{ywwxy{{zwspmlmnoi_clf^_cfggfeegjouz~{yxwxyz{zwspnllnoj`blf^_cfggfeegjouz~|zxxxz{{zwtqnmmooh_clf^_befffeegjoty}|zxxyz{{zwtqnmmonh_dle^_beffeeegjoty}|zyxyz{{zwtpnmnong_eld^_ceffeeegjoty}|zyyz{||zwspnmnome_hkb^`cefeedegkpuy}|{zz{|||yvspnnoolc`jja^adeeeddegkpuy}~}{z{|}}|yvspooppkbbki`^acdeddcdgkpuy|~~}|{{|}}|yvspoopojabkh`^acdedcceglpuy|~~}|{{|}}|yvspoopojaclh_^acdedccdglpuy|~}~}|{{|}}|yvspoopojaclg_^acdddccdglpuy|~}~~|{{|}}|yvsqoopojbckh`^acdedccdgkptx{~}}~||||}}|zwtqpopokbbkia^`cdddccdgkotx{~}}~}|||}}|zwtqpoppkcajja^`cddddddgkosw{}~}}|{||}|zwtrpoopme`hkc_`bdeddddfjnswz}~}}{{{|||zxurpoopng`fle__bdeedddfinrvz}~}|{{{||zxurpooonhackh`_acdeddefimquy|~~|{z{{|{yvsqooookcaijc_`bdeeeefhlptx|~~|{zz{{zyvtqonnomf`ekf`_bdeeeefhkosx{~|zzyzzzywtqonnnmhabjha_aceefefgjnrw{~|{yyyzzywtronmnnjbaijb_`ceffffgjmrvz~}{zyyzzywurpnnnnkc`gkd_`bdefffgimquy}}{zyyyzyxuspnmnnke_ekf`_bdefffgilpuy|~{zyyyyyxuspnmmnle`dkf`_bdefffgilpty|~{zyyyyyxuspnmmnlf`cjg`_adefffgilptx|~|zyyyyyxuspnmmmlf`cjga_adefffgilptx||zyyyzyxvsqonnnlf`djga_aceeffgilptx{~|{zyzzyxvsqonnnke`djga_aceeffgilptx{~|{zzzzyxvsqonnnke`ejf``aceeefgilptx{~}{zzzzzxvsqonnnjc`gjd``bceeeegilptx{~}|{{{{zxusqoonnibaijc_`bddeeegilptx{}~}||||{zxusqpoomhbcjib_`bcddeegilptxz}~~}~}|||{zxusqpoomhbcjib_`bcdddegilptwz|}}}~}|||{zxvsqpoomgadjhb_`bcdddefilptwz|}}}~}}|||zxvsqppomgadjhb_`bcdddefilptwz|}}}~}}|||zxvsqpoomhbcjhb``bcdddefilpsvy{|}|~}}||zxvtrppomhbciic``bcdddefhlosvy{|}|~}}||zyvtrppomicbiid``acdddefhkosvy{|}}~~}|||zyvtrpponjdbgje``acdddefhknrux{}}}~~}||{zywtrpoonkeafjfa`abddeefhjnqux{}}}~~}|{{zywurpoonkfadjhb``bcddefgjmqtwz|}~~~||{{zywusqoonlhbbhje``acdeefgjlptwz|~~~~|{{zzywusqonnmjdaejgb`acdeefgilosvz|~~|{zzyywusqonnmkfacihc`abdeffgilorvy|~|{zzyxwvsqonmmkgbbhidaabdeffgiknruy|~}{zyyxwvtrpnnmlhbafjfaabceffgiknqux{~}{zyyxwvtrpnnmlicaejgbabceffgiknqux{}}{zyyxwvtrpnmmlidadigbabceffgikmqtx{}}|zyyxwvtrponmlidadihcaaceffgijmptw{}~|zyyxwvtrponmljeadihcabceffgijmptwz}~|{zyyxvtrponmlidadihcaacdefghjmptwz|~~|{zyyxvurqonmlidaeigcaacdefghjmpswz|~}{zzyxvurqonmlidaeigbabcdefghjmpswz|~}|{zyxwurqonmlhcbfifbabcdefghjmpswy|}~~}|{{zxwurqonmkgbbhieaabcdefghjmptwy{}}~~}|{zywusqpomkfbchidaabcdeefhjmpsvy{|}~~}|{zywusqpomkfbdihdaabcdeefhjmpsvy{||}~}|{zywusqpomkfbdihdaabcddefhjmpsvxz{|}~}||zywusqpomkfbdihcaabcddefhjmpsvxz{|}~}||zywusqponkfbdihdaabcddefhjmpsuxz{|}~}|{ywusqponkfbdhieaabccdefhjloruxz{|}~}|zywusqponkgcchiebaaccdefgjloruwz{|}~||zywusrponlhcbgifbaabcdefgiloruwy{|}~|{zyxvtrponlidbeigcaabcdefgilnqtwy{|}~|{zyxvtrponliebdhhdbabcdefgiknqtwy{|}~|{zyxvtrponmjfbcgifbabcdefgikmpsvy{|}~|{zyxvtrponmkhcbeigcbbcdefgikmpsvy{}~~|{zyxvtrqonmliebchhebbcdefgijmorux{}~~|{zywvtsqonmljfbbgifcbcdefgijlorux{}~~|{zywvusqonmljfbbfigcbbdefghjloruwz|~|{zywvusqonmljgcbehgdbbcefghjlnqtwz|~}{zywvusqonmljgcaehgdbbcefghjlnqtwz|~}{zyxvusqpnmlkhdadhhdbbcefghjlnqtwy|~}{zyxvusqpnmlkhdadhhdbbcefghjlnqtwy|~}|zyxwusrpnmljgdbdhhdbbcdfghjknqsvy{}~~|zyxwusrpnmljgcbehgdbbcdfghiknpsvy{}~~|{yxwusrpnmljgcbehgdbbcdeghiknpsvy{}~~|{zxwutrpomljfcbfhfcbbcdefhiknpsvy{|}~}{zywutrpomljfccghfcbbcdefhiknpsvxz|}}|zywvtrponliebdghecbbcdefgiknpsvxz{}~}|zywvtrponliecdhhebbbcdefgiknpsvxz{|~}|{yxvtrponliecdhhebbbcdefgiknpsuxy{|~~|{yxvtrqonliecehhebbbcdefgikmpsuwy{|~~|{zxvtrqonliecdhhebbbcdefgikmpruwyz|~~|{zxvtrqonlifcdghecbbcdefgikmpruwyz|~~}{zxvtsqonljfcdghfcbbcdefgikmortwyz|}~}{zxvusqpnmjgccfhgdbbcdefghjmortwyz|}~}{zxwusqpnmkgdcehgdbbcdefghjloqtvxz|}~}{zxwusqpnmkhdcdhhecbcdefghjlnqtvxz|}~}{zxwusqpnmkieccfhfdbccdfghjlnqsvxz|}~}{zxwusqpnmljgcbehgecccdfghjknpsuxz|}}{zxwusrpnmljgdbdghfcccdeghjkmpsuxz|~}{zxwusrpomljhebcfhfdccdeghikmpruwz|~}{zxwusrpomlkheccfhgeccdeghikmoruwz|}}{zxwutrpomlkifcbehgeccdefhikmortwy|}}{zxwutrpomlkifcbeggedcdefhikmortwy{}~|zxwutrponlkifcbdggedcdefhikmoqtwy{}~|zxwutrqonlkigdbdggedcdefhikmoqtvy{}~~|zywvtrqonlkifcbdggedcdefgijloqtvy{}~~|zywvtrqonlkifcbeggedcdefgijlnqsvxz|~~|{yxvtsqonmkifcceggeccdefgijlnqsvxz|~}{yxvusqonmkifccfhgdccdefgijlnqsvxz|}}{zxvusqpnmkhecdfhfdccdefghjlnqsvxz{}}|zxwusqpnmkhecdghfdccdefghjlnqsuwy{}}|zxwusqpnmkhecdggfdccdefghjlnpsuwy{}~|zywusqpnmkheceggedccdefghjlnpsuwy{|~|zywusrpnmkheceggecccddfghjlnpsuwy{|~|{ywusrpnmkheceggfdccddfghjlnpruwxz|~|{ywutrpomkhecdghfdcccdeghikmprtvxz|~~|{ywutrpomkheddghfdcccdefhikmprtvxz|~~|{ywvtrpomkifddfggecccdefhikmortvxz|~~}{ywvtrpomligdceggecccdefhikmoqtvxz|~}{ywvtrqomljgdceggfdccdefhikmoqsvxz|~}{yxvtrqonljhecdfgfedcdefgikloqsvxz|}}{yxvtrqonlkhfcceggedddefgijlnqsuxz|}}{yxvtrqonlkigdcdggfdddefgijlnpsuwz|}}{yxvtsqonlkigdcdfgfeddefgijlnpsuwz|}}{zxvtsqonlkigecceggeddefgijlnpruwy{}~{zxvusqpnmkjhecceggfddefghjlnpruwy{}~|zxvusqpnmkjhecceggfedefghjlnprtwy{}~|zxvusqpnmkjhfccdggfedefghjkmortvy{}~|zxvusqpnmkjhfdcdggfedefghjkmortvy{}~|zxwusrpnmkjhfdceggfedefghjkmoqtvx{|~|zywusrpomljhfdceggfedefghikmoqtvxz|~|{ywutrpomljhfdceggfddeeghikmoqtvxz|~}{ywutrpomljhecdfggedddeghikmoqsvxz|~}{ywvtrpomljgeddfgfedddefhikmoqsuxz{}}{yxvtrpomljgedefgfedddefhikmoqsuwy{}}{zxvtrqomljgedefgfddddefhikmnqsuwy{}~|zxvtrqomljgedeggfddddefgiklnqsuwy{}~|zxvtsqonljgedeggfddddefgijlnpsuwy{}~|zxvusqonljgedeggfddddefgijlnprtwyz}~|zxwusqonljgedefgfedddefgijlnprtvxz|~|zywusqonljhedefgfedddefgijlnprtvxz|~|zywusqpnljhfddfgfedddefghjlnprtvxz|}{ywusrpnlkifddeggedddefghjlmprtvxz|~}{ywusrpnmkigddefgfeddefghjkmoqtvxz|~}{ywusrpnmkigeddfgfeddefghjkmoqsvxz|~}{ywusrpnmkjheddeggfedefghikmoqsuxz|~}{ywutrpomljhfdcdfgfeeefghikmoqsuwz|~}{ywvtrpomljhfdcdfgfeeefghikmoqsuwz{~~{ywvtrpomljigecdeggfeefghiklnpsuwy{}~{yxvtrqomljigeccefgfeeefhiklnpruwy{}~|zxvtrqomlkigedcefgfeeffhijlnprtwy{}~|zxvtrqonlkigedcefgfeeefgijlnprtwy{}~|zxvtsqonlkigedcefgfeeefgijlnprtvy{}|zxvtsqonlkigedcefgfeeefgijlnprtvxz||zxwusqonlkigeddefgfeeefgijlnprtvxz|~}zxwusqpnlkigeddefgfeeefghjlmortvxz|~}{ywusqpnmkigeddegffeeefghjkmoqsvxz|~}{ywusqpnmkigeddfgfeeeefghjkmoqsvxz|~}{ywusrpnmkigedefgfeddefghjkmoqsuwy{~}{ywutrpnmkigedefgfeddefghjkmoqsuwy{~~{ywvtrpnmkigedefgfeddefghikmoqsuwy{}~|zxvtrpomkigedefgfeddefghikmoqsuwy{}~|zxvtrpomkigedefgfeddefghikmnpruwy{}~|zxvtrpomkigedefgfeddefghiklnprtwy{}~|zxvtrqomkigedefgfeddeefhiklnprtvxz}~|zxvtsqomljhfddfffeedeefhijlnprtvxz||zxvusqomljhfedefffeeeefgijlnprtvxz|}zxwusqonljhfedefffeeeefgijlnprtvxz|}{ywusqonlkigeddffffeeefgijlnoqtvxz|~}{ywusqpnlkigeddefffeeefgijlmoqsvxz|~}{ywusqpnlkihfdddffffeefghjkmoqsuxz|~}{ywusqpnmkjhfdddefffeffghjkmoqsuwz|~~{ywusrpnmkjhfeddeffffffghjkmoqsuwy{~~{ywutrpnmkjhgeddefgffffghjkmoqsuwy{}~|ywutrpnmkjhgeddeffffffghikmnpsuwy{}~|zxvtrpomljigeddeffffffghikmnpruwy{}~|zxvtrpomljigeddeffffffghiklnprtwy{}|zxvtrqomljigeddeffffffghiklnprtvx{}|zxvtrqomljigeddeffffefghiklnprtvxz}}zxvtsqomljigeddefffeefghijlnprtvxz|}{xvusqonljigeddefffeeffhijlnprtvxz|~}{ywusqonljigedefffeeeffgijlnoqtvxz|~}{ywusqonljhgedefffeeeefgijlmoqsuxz|~}{ywusqpnljhgeeefffeeeefgijlmoqsuwy|~}{ywusqpnljhgeeefffeeeefghjkmoqsuwy{~~{ywusrpnljigeeefffeeeefghjkmoqsuwy{~~|zwutrpnlkigeeefffeeeefghjkmoqsuwy{~~|zxvtrpnmkigeeefffeeeefghjkmoqsuwy{}~|zxvtrpnmkigfeefffeeeefghikmnprtwy{}~|zxvtrpomkigfeeefffeeefghikmnprtvx{}|zxvtrpomkjhfeeefffeeefghiklnprtvxz}|zxvtrqomkjhfedefffeeefghiklnprtvxz|}{xvtrqomljhgedeefffeefghijlnprtvxz|}{yvusqomljigeddeffffffghijlnprtvxz|~}{ywusqonljigfddeffffffghijlnoqtvxz|~}{ywusqonlkigfeddefffffghijlmoqsuxz|~}{ywusqpnlkihfeddefffffghijlmoqsuwz|~~{ywusqpnlkihfeddefffffggijkmoqsuwy{~~|ywusqpnlkihfeddefffffgghjkmoqsuwy{~~|zwusrpnmkjhgeddefffffgghjkmoqsuwy{}~|zxvtrpnmkjhgeddeffffffghjkmoqsuwy{}~|zxvtrpnmkjhgeddeffffffghjkmnprtwy{}|zxvtrpomkjhgeddeffffffghiklnprtvx{}|zxvtrpomkjhgeddeffffffghiklnprtvxz}}zxvtrpomljhgeddeffffffghiklnprtvxz|}{xvtrqomljhgeeeefffeefghiklnprtvxz|}{ywusqomljhgeeeffffeefghijlnprtvxz|}{ywusqomljhgeeefffeeefghijlnpqsuxz|~}{ywusqomljhgeeefffeeefghijlnoqsuwz|~~{ywusqonljhgeeefffeeeffgijlmoqsuwy|~~{ywusqonljigfeefffeeeffgijlmoqsuwy{~~|zwusqpnljigfeefffeeeffgijkmoqsuwy{~~|zxvsrpnlkigfeefffeeeffghjkmoqsuwy{}~|zxvtrpnlkigfeeefffeeffghjkmoprtwy{}~|zxvtrpnmkihfeeefffeeffghjkmoprtvx{}|zxvtrpnmkihfeeeffffeffghikmnprtvxz}}zxvtrpomkjhgeeeeffffffghiklnprtvxz}}{xvtrpomkjhgeedeffffffghiklnprtvxz|}{yvtrqomljigfedeefffffghiklnprtvxz|}{ywusqomljigfedeefffffghiklnprtvxz|~}{ywusqomljigfeddefffffghijlnoqsuxz|~~{ywusqonljihfeddefffffghijlmoqsuwz|~~{ywusqonlkihfeddeeffffghijlmoqsuwy{~~|ywusqpnlkihfeddeeffffghijlmoqsuwy{~~|zwusqpnlkihgeddeeffffghijkmoqsuwy{}~|zxvtrpnlkihgeddeeffffghijkmoqsuwy{}~|zxvtrpnmkihgeedeeffffggijkmoprtvy{}|zxvtrpnmkjhgeedeeffffgghjkmnprtvx{}}zxvtrpnmkjhgeeeeffffffghjkmnprtvxz}}zxvtrpomkjhgeeeeffffffghiklnprtvxz}}{xvtrpomkjhgeeeefffeffghiklnprtvxz|}{ywtsqomkjhgfeeefffeffghiklnprtvxz|}{ywusqomkjhgfeeeffeeffghiklnpqsuxz|}{ywusqomkjhgfeeeffeeffghijlnpqsuxz|}{ywusqomljhgfeeeffeeffghijlnoqsuwz|~~{ywusqomljhgfeeeffeeffghijlmoqsuwy|~~|ywusqonljigfeeeffeeffghijlmoqsuwy{~~|zwusqpnljigfeeefffeffghijlmoqsuwy{~~|zxusrpnlkigfeeeefffffghijkmoqsuwy{}~|zxvtrpnlkihfeeeefffffggijkmoqrtwy{}|zxvtrpnmkihfeeeefffffgghjkmoprtvx{}|zxvtrpnmkihgeeeeeffffgghjkmnprtvx{}}zxvtrpomkjhgfedeeffffgghjkmnprtvxz}}{xvtrpomkjhgfedeeffffgghiklnprtvxz|}{yvtrqomljigfeddeefffgghiklnprtvxz|~}{ywusqomljigfeddeefffgghiklnprsvxz|~}{ywusqomljigfeddeefffgghiklnoqsuwz|~~{ywusqonljihfeedeefffgghijlnoqsuwy|~~|ywusqonljihfeedeefffgghijlmoqsuwy{~~|ywusqonlkihfeedeeffffghijlmoqsuwy{~~|zxusqpnlkihfeeeeeffffghijlmoqsuwy{}~|zxvtrpnlkihfeeeeeffffghijkmoqsuwy{}|zxvtrpnlkihfeeeeeffffghijkmoprtvy{}|zxvtrpnmkihgeeeeeffffgghjkmnprtvx{}}zxvtrpnmkihgfeeeeffffgghjkmnprtvxz}}{xvtrpnmkihgfeeeefffffghjklnprtvxz}}{yvtrpomkjhgfeeeefefffghiklnprtvxz|}{ywusqomkjhgfeeeeeeeffghiklnprtvxz|}{ywusqomkjhgfeeeeeeeffghiklnpqsuxz|}{ywusqomljhgfeeeeeeeffghijlnoqsuwz|~~{ywusqomljhgfeeeeeeeffghijlmoqsuwy|~~|ywusqonljigfeeeeeefffghijlmoqsuwy{~~|zwusqpnljigfeeeeeefffghijlmoqsuwy{~~|zxusrpnlkihfeeeeeffffghijkmoqsuwy{}~|zxvtrpnlkihffeeeeffffghijkmoqrtwy{}|zxvtrpnlkihgfeeeeffffghijkmoprtvy{}}zxvtrpnmkjhgfeeeeefffghijkmnprtvx{}}{xvtrpnmkjhgfeeeeefffghhjkmnprtvxz}}{yvtrpomkjhgfeeeeefffghhjklnprtvxz|}{ywtrqomkjigfeeeeefffgghjklnprtvxz|~}{ywusqomljigfeeeeefffgghiklnpqsvxz|~}{ywusqomljigfeeeeefffgghiklnoqsuwz|~~{ywusqomljihfeeeeefffgghijlnoqsuwy|~~|ywusqonljihfeeeeefffgghijlmoqsuwy{~~|zwusqonlkihfeeeeeefffghijlmoqsuwy{~~|zxusqpnlkihgfeeeeefffghijlmoqsuwy{}~|zxvtrpnlkihgfeeeeefffghijkmoqrtwy{}|zxvtrpnlkihgfeeeeefffghijkmoprtvy{}|zxvtrpnlkihgfeeeeefffggijkmnprtvx{}}zxvtrpnmkihgfeeeeeeffgghjkmnprtvxz}}{xvtrpnmkihgfeeeeeeffgghjklnprtvxz}}{yvtrpomkjhgfeeeeeefffghiklnprtvxz|}{ywusqomkjhgfeeeeeefffghiklnprtvxz|}{ywusqomkjhgfeeeeeefffghiklnpqsuwz|~~{ywusqomljhgffeeeeeeffghijlnoqsuwz|~~|ywusqomljigffeeeeefffghijlmoqsuwy|~~|zwusrpnlkihgffffffffgghjkmnprsuwy|~~|zxvtrpnmkjiggggggggghhijkmnprsuwy{~~|zxvusqonlkjihgggghhhhijklmoprtuwy{}}{ywusqpnmljihhhhhhhiiijklmoprtuwy{}}{ywutrpomlkjihhhhiiiijkklnoqrtvwy{}}{ywvtrqonmkjiihhiiijjjklmnoqrtvwy{}}{zxvtsqpnmlkjiiiiijjjkklnopqstvxy{}~|zxvusrponmkkjjjjjkkkllmnopqstvxy{}~|zywutrqpomlkkjjjkkkllmmnoprstvxy{}~|{ywvtsqponmlkkkkkllllmnnoqrsuvxy{}~~|{yxvusrqonmllkkkllllmmnopqrsuvxy{}~~}{yxvutrqponmllllllmmmnnopqrsuvxy{}~~}{zxwutsqponmmllllmmmmnoppqstuvxy{|~}{zxwvtsrqponmmmmmnnnnoopqrstuwxy{|~}|zywvutrqponnmmnnnnnnoopqrstuwxy{|~}|{yxvutsrqponnnnnnnoooppqrstuwxy{|~~|{yxwvtsrqpoonnnoooooopqqrstvwxy{|~~|{zxwvusrqppoooooooooppqrrstvwxy{|~~}{zywvutsrqpoooooooppppqrstuvwxz{|~~}{zyxvutsrqppppppppppqqrrstuvwxz{|~~}|zyxwvutsrqqpppppppqqqrsstuvwxz{|~~}|{yxwvutsrqqqqqqqqqqqrrsstuvwxz{|}}|{zywvutssrqqqqqqqqqqrrsttuvwxz{|}}|{zyxwvutsrrqqqqqqqrrrsstuvvwyz{|}~|{zyxwvutsrrrqqrrrrrrrsttuvwxyz{|}~}{zyxwvuttsrrrrrrrrrsssttuvwxyz{|}~~}|{zyxwvutssrrrrrsssssttuuvwxyz{|}~~}|{zyxwvuutssrrrssssssttuvvwxyz{|}~~}|{zyxwvvuttsssssssstttuuvvwxyz{|}~~}|{zyxxwvuttsssssstttttuuvwwxyz{|}~~}|{zzyxwvuuttsssstttttuuvvwxxyz{|}~~}|{zyxwwvutttttttttuuuuvvwxxyz{|}~~}|{zyxxwvvutttttttuuuuvvvwxyyz{|}~~}|{zyyxwwvuutttttuuuuuvvwwxyyz{|}~~}|{zzyxwwvvuutttuuuuuvvvwwxyyz{|}~~}|{{zyxxwvvuuuuuuuuuvvvvwwxyzz{|}~~}||{zyyxwwvuuuuuuuvvvvvwwxxyzz{|}~~}}|{zyyxwwvvuuuuvvvvvvwwwxxyzz{|}~~~}|{zzyxxwwvvvvvvvvvvvwwwxyyzz{|}~~}|{{zyyxwwvvvvvvvvvvwwwxxyyz{{|}~~}||{zyyxxwwvvvvvvvwwwwwxxyyz{{|}~~}||{zzyxxwwvvvvwwwwwwwwxxyyz{{|}~~~}}|{zzyyxwwwwwwwwwwwwwxxyyzz{{|}~~~}}|{{zyyxxwwwwwwwwwwwxxxyyzz{{|}~~~}}||{zzyxxxwwwwwwwwwxxxxyyzz{{|}~~~~}||{zzyyxxwwwwxxxxxxxxxyyzz{{|}~~~~}||{{zyyxxxwwxxxxxxxxxyyyzz{||}~~~}}|{{zzyyxxxxxxxxxxxxxyyyzz{||}}~~}}|{{zzyyxxxxxxxxxxxxyyyzz{{||}}~~}}||{zzyyyxxxxxxxxxyyyyyzz{{||}}~~~}||{{zzyyxxxxxxyyyyyyyzzz{{||}}~~~}}|{{zzyyyxxxxyyyyyyyyzzz{{||}}~~~}}||{{zzyyyxxxyyyyyyyyzzz{{||}}~~~}}||{{zzyyyyyyyyyyyyyzzz{{{||}}~~~}}||{{zzzyyyyyyyyyyyzzzz{{|||}}~~}}|||{{zzyyyyyyyyzzzzzzz{{|||}}~~~}}||{{zzzyyyyyyzzzzzzz{{{|||}}~~~}}||{{{zzzyyyyzzzzzzzz{{{|||}}~~~~}}|||{{zzzyyyyzzzzzzzz{{{||}}}~~~~}}|||{{zzzzyyzzzzzzzz{{{{||}}}~~~~}}}||{{{zzzzzzzzzzzzz{{{|||}}}~~~~~}}||{{{zzzzzzzzzzz{{{{{|||}}}~~~~}}|||{{{zzzzzzz{{{{{{{{|||}}}~~~~}}}||{{{zzzzzz{{{{{{{{{|||}}}~~~~}}}||{{{zzzzz{{{{{{{{{{|||}}}~~~~}}}|||{{{zzz{{{{{{{{{{{|||}}~~~~~~}}|||{{{{{{{{{{{{{{{{||||}}~~~~~~}}|||{{{{{{{{{{{{{{{{|||}}}~~~~~~}}}|||{{{{{{{{{{{{{{||||}}}~~~~~}}}|||{{{{{{{{{{{{{{||||}}}~~~~~~}}|||{{{{{{{{{{{{{|||||}}}~~~~~~}}}|||{{{{{{{{{{{||||||}}}~~~~~~}}}|||{{{{{{|||||||||||}}}~~~~~~}}}||||{{{{|||||||||||}}}}~~~~~~}}}||||{{{|||||||||||}}}}~~~~~~}}}|||||{||||||||||||}}}}~~~~~~}}}}|||||||||||||||||}}}}~~~~~~}}}}||||||||||||||||}}}}}~~~~~~~}}}|||||||||||||||}}}}}}~~~~~~~}}}}||||||||||||||}}}}}}~~~~~~}}}}|||||||||||||}}}}}}~~~~~~~~}}}}|||||||||}}}}}}}}}~~~~~~~~}}}}||||||||}}}}}}}}}}~~~~~~~~}}}}|||||||}}}}}}}}}}}~~~~~~~~}}}}}|||||}}}}}}}}}}}}~~~~~~~~}}}}||||}}}}}}}}}}}}}~~~~~~~~}}}}}|||}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}~~~}}~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m41.bin b/applications/SprPoP/assets/packed/MUS/m41.bin new file mode 100644 index 0000000..fae6352 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m41.bin @@ -0,0 +1,2 @@ +ĺ}yfK2:HG``v|qrDDhyjK^P~veZvyc{uVhM+`z`Nba\cfpzd{{}pXf`ziv_m~vc~Zpr{bfHWQn|rW_\|YaYsx}rryg;Hx>FKmiwpg_Qoy]jx|rfYedvuqs^hVHHJTL^v{`pQb~~wűJ=2trNKPKJssswlaqi[XXQg~q|gTXflkhYc]_n}uuw{xo{m^uc~uz{uvj;16Y{ɺ~Qe`JRYZWgcfDQBPJCTo_meMWHqf{{~{rjxSlalon|~|vut~~m~q{ƯȮR91*MS~i_mJVALSDUBKJUJg_o~wlja\TjyouyrVWdpoxo|o{}zs~hqwtN?156Szֵ|qf_SWcgNRXQeJL_uuxr~alqmsqm|xpz~l~o}{fl}}i}yrx}{eNO0@,/XӺuhwdlU`]i[cafmqzuyoxwxsnu{tydsaztovWulh}pqygr{{ZSFB1/:GѲckkcw\Zqzwyvqy}zy|txvvY^mcdaieSvgquvzw~xpupw}^[U@M+#7,65JSYnȲxos}u|w}ozt{syjzv}|^elYYa`fyzywsr~ytzmyawusr^\VIGUQH8X]a~osur|trvywyzsz|o||~~y|~zq]cW[lTc{|u~muu{xpul_nnR^W?6AJAHW`kòq}{o~~~uvviyu~|yun~}~|ohreaid`iohuy~~{lwpoz~vt^`\WHUVN;LQAVmuȽ{nqk~qx|ztw~zpxz{wzjhbhz_btq{q{ztt}py{vdplScbK?JPGGJGDYexxq~{{yrwkoqtx}v~~x|hmrnhmm`ryl|lqkpvdifbXd[H9NE3BHMo˸°wsxzztvxzottv~~hxyrvtiyzbquunbrzvtzxn~}~s|k\piXOZJGEJ<4=JqvǸ|z}ntvjtwpyw}r}oy|uyxsywiun_{~rtqtvehbaXlgTEXM9?89PWm~ųº~~wv{}z~vxwfny~kwzru}~x~|o{q\fbomx{}ry|oZkk^Zk\SMOJ677QQPzº|{rut~lrp~zxrtosty{tx|zyzrYattvwwvnysyln\cj]MVU<;?6IEOTmĸx{wxmskziv}yj|{sw|z}syqnnkkhf{tzu|~xqkvl]bhVZKQ@;>HTCJg|u|zutxq~ltz}kuvnoowtybnulkux|r~~t|onYffZUg`B<:?Wfsrxw~rzlzjhry}okytp|pro}hnvnizz~zxx{~txjegXN[bMC>6NFMHNei~óxxuz{ppkddjsztv~tsyyuttzajlej}|}zrtjlSZQXYB@CULAQZcYpºwtpvvejwpvkes|swpnu|ovqh|{{~zj}r}pkp_U_eQGG?N@ILLa^kkiȻ~w}riynheh~sy}isvzy|z{yxo}gmlej{~|znk~z|x[dV\YHIKP>;MVXMio|pxlukm|t|~wtriwwqtw{ltzkqvsuzzx~soqqd[d`LFDPYCK?P_^bY^{q~~y}ykmgvuoto{m~u|ww~wmwkbmzokw~}nv~mgbScPLT`W>AMTFLYVrn¬{xx~oqu|mfuryvqs{qgmoxxjsf|wy{r|pmxomwtZNIL_NXKCXZ\MDXnyy{xrzkfh{|kxi{sitxpzxxmxvszrghqxzwxzfoZRS^[HHILDOYM`Var~szmtrfnuspz}vtxfekyxz~~nsnx~untoq}}fWQ[aOaRSZUXKGU_bmwu·|ywni~vvzfy}~{w}|xskssw|~v}yrxpffszpts|c_di[PVVM:OLJUAMV_x~{wszoilnhj{~yt~~ogk~q~~xxuwury~vtqztj_eeTcT\g^QABRYW^`Yy~{yxwqum{{zshvzrrt~tt{qtu~|uls~lzszxoik_NPXYKTOJ]IDDGVm}¹}{uptsi_thqw~wuq{mqr{nw|zw}|nsur}}rotw[e_RdgeRBJZ]ZWDW_bxyp~rw|scan}~{znhqxyv{}x}}{yurqup{}~u`WVVQQ`GJQEJGIXiik{mgpvloxfxsmyx}}rrkusntzw~qqy{zvs|}go^Zea_RJLQW]YI[W\uz|z{nqzheqy{x}|tglssqzytxz|z|vp}v~~}hWWXPQfU[V?DFJS\\\dyunmjq|cnpchq~}twu}xfwzt|}plrvu}y{qr~mzqZde`SOW^YRTGVWKfmttu{yyogjrwywq~||nggkn}svw~{{xsw||~ylf\SM\PN`NC@GWdbX[f|ytwujktvzeddi|pvpqwzzv{x||xsspy}twp{zing`SGLY`]SBU_Rcfitwxwlmv|xupbv|y{rtphdy{u{wxxz~ty||ujhh^WfVS`RMGAJ[^XW_smy~uwyrrvfx{hddlwrz|q~{tkjwv{zzzuqzylxlvtfUQUZ]^RCOINddenzy{yzzges}}wgj|l{silspq}n~{z{y~|yu{y}sqnjekaM]VJMPSVVVWZc`]yzyutv|xnumgdkvvs|~pruhkz{}tw|~x|qp{|pzytfVT\\YVO^UCXaabcbagv}{vtx}|mdreizxpmlwil|vwxx|os~ojnqnzyagaQJJS\[MKTa]Pfffz}wy|zmmpruwuomyzzwzzxojpywx|vy~vwx{n{{okjfd_PH]TKUT\ce[SY`gz}x|yzqx}eoyuzywwrn{w}ylosyy}{}||}tposrkwaUTRXccYNLYRRbRXhowzljr{|rjkrwqy|sr{}zwuo~v{u|~uvx~y||{rlqvneZSaVVd[RU[TLJOWc}}~{hslqutwz~|xxzty|mnily{u|}~uzsruxfXTY]\ZYXXKJaVOX`dcr~xrzvlmufmy|ylmzwjvxxu}}tsx}utz}}yhR^[J\cb_XTQPPLMcfctxv{rssjhrvv~{u}vliq~~uxy{|rvu|yozrmiabg_UV]_IKYPX\XUW`s~}{{|ywvqcrsmxv{|hrtoyww{u~y}~rlt{rfk^V`]^a^NAFNQSb`bxz|on{vogfouy{ysqxxx|}|~}zy|~~}y{|uzwpjkmg_XSW_QWXDQ]_XR\kv|}|sw~|fnugn|yu{}{zv|kzvpuww}~zvy}ueqkS^a\\b_SHCJP`^Qiokwzz~{~}obcegq|qlo{{ss|zt}zu{zyrmqqsyr_VXZMObSKOWZUU\juz}}z}rustxflstxz{yjsqs|~wtu~{x|}|z~{xvoxwgme]\XRMLLFE\f[ihdjoyz{}~qgdffuzw{vqv}rr~~zwv{}{mimuxslaZ_PM[QTYRNRZdhjoju}vk{lmqrjhwrz}uyzyw|ztzx{}yz|nfyqgurgaaVJKOMJROTia[aly~|uqnlhdn{lz~yzyy|{}{yzn~{zx|z|}xwor{vmlk\M\SJW[VGCRagfad|{wm}ttxrmijtx{mt}zzzz{s|vpyyp|ytpeWMHFKP^WH]`ZYXcs{wmpqljsgkxvzz~vxnryss~xyu{~~xxzwurup[_YMRWYSMLT_b[Sefhnt~{obens{yz|ymksyyyzw{vx}uk}}{zkVQNHJ\TNVMPVZ_gmmq~qjor|~iswjlu{yv}w}xn|~~}~zv}xxww~xuy~pjt\U[WQMOTTQXUYfXasuyvzyplpuspn~vuc\`dnwnehuthmnO66^sjP6JgbJAJ_yŸzppx{jSUoibjWwxppjdgf|zhdtun~omxyyhL;A`ub@:MgxW?CK[l}~kgga_`bc]WSMcvdqdPTuvalvwzvu{{eJ4APZwmJ2/JpxdWXQLjppʺsgjbbm_V[g|pj`r}fY[j|~pkkoysmpu|v{{aYSHEO`nrdK/3LXt}{{vqyyzsopt{yrfZjjfkjltumjmriZqzxoxlfx|tlf]UQMPTbci{n_P>36DR`m{z~Ȱxqjkbj~tmpqnlb`h\bkmsvrio~}~zogjplV[cZ`holektsj]GA9'=S^is{~|}hlpmjlomq|xpmnlkifhu|uv{{|zmbchkm}|kvqaWROKA6:ETWTnvwzz}~|ztomi^X^kge|zyytm}~xsu{wrqmcRNWB:?CKRTPT_jxx{|z|}xtthggSY`^hrtqtwy|ww|we\ROSQF96CIBRSSdp}zrpmwso|qb`ejjcYUV_cfywy}oeTOUGJMCDMQKEHPVe}|}zwmejrskaZhjX]_]^dkikrxx|j_\ZZWPJDC=BTJIT\_br{ps{knymdYWahcYTZ`Vewox~{yfYbWMZXPHEECCDFOdenvnjlrsn\UbZ_f`aeebhmps|vzrd^`\SPQTI;LLIQRSTXh|}}pmuljifcfhd`]_V[igr{~|vu}wee\RZZ[\WIAEHGFSRUkry~uostrskkm[dojea_XV_ko{z{ytmf\TT[QJPDISTMGLWcq}|~~qkknqmcaeifV_dZgvzvv{~~zhag_[\ZNA=CHOYLTa\coyzrhrucfknqoe[XX[duyqvxsc]\]SS]KELSQKIPY^cgt~yuqsz}ugbim_dcY`eknms|~{y~zppifc`ZRLC>EULJUQRXfyst~swwljnl`VUWXewr{zvsx}ncd\P]YQRLJHIMPTXX[v|}xvwzzvqkkdepa\bdbcmw}~ss~qhc[PMNLFJRLXXMMQXgx~s{wm{~xromgb\WVbcbyzvyy{ztn^W_SRYWMCGQWVSMQcds|uuvuccqgffc_Z]ly|~y|y~zoeYPMMLPN?OWQOOVajpyu{}|zth^_ba\dh_mutuv|~~{|p\bZPTWULCDNWYTO`aawxrszsinddmmf]]cjszvt|x{q^QNKIP\PMRNPQRW`dfpx{{{~{ma^aafhZdjemu{}}~~zmobTWVSONONNRSNYXViq}xvxrnzmbhmkb]`fimqv}}~qbVPKLTKRUHGNV]a__do{xumhb^`nqdkjbbgt~~}zs{m`]YRIIPTQLJRaYZfjnw}~ps{polihfghhhf`n}u~zniaWR[TN\VMFEP[^YY_kksupoldbkejoc`bfp}}~xxzppg\SNOUWTIAQRS``dkqu{yw~suyth`dkmjf`ero}z{|upj``WNXSPPMLRYXWW_]`tyxpnppstenvkd_aglqvzy}|z|xkZSTUVWQNTJR_^\]`^`lywy}}ujgmrph\cf_p}{wz~yyxtuwc^bUPOQSQMMRX[QYeao|xtrz{knokkhgjlhhq|}wx|~{pe`_[UMPXJITY^_]WW[bm}~ytqonpphmkblpt||wutuz}|{ztkj]VSSX\XMIPYRR]W\gr{}|y}z~|lhjlmlgddlws|z}yrpkc^WO[XSYSRWZUNNTYd{yy}|umgiredmmov~{wxxtw|{y~xeZUTVYWRMNQNZ[RZbfkt|~utmioutkedjii}}y~xz}zzzreVQYOQ\ZUTXWSQQRZikyzw{~xjkwnprmlllmptwy|~|x}ri^[\[USSUPES[UZ]__an}zvplnsqlhkibovv~y}{~yvy{yti[_XQYZYWRKJOSUXfhj}vnrikwvrqqlhntvy}z~|{|xqke^[VPNSYNMTPXab__ep}{yyzyupooqockpjv~{|~{xy{yx{|xoef[PXXW[\VLINTWa`_qt{yxvnvwwuphcchqz}~~|z~vtqooi\SPQTMTXLQ[`^Z^hqx{xz}tlnutipmhov}}|~}}{xxx~|zyyrnuh__YVTTPLIHMXf`dolnvwyvyyuy|xmgefgty}}|{urrrsocXTVOKYTPSTYZ]agkopz{sptnswjlppptzz~yw}|{zsnwpine^\YSNNMIHVXZgccip}~xuupjgefmwu}||y|uxvy|yqia]TMWRQWUOLQZaceek}~}ymt{qqqpljr~y~|z~|v~zvxvztj`YPJIMNPVOXd^\\bkvxrqokflmhw~}~}yuy}|~|{}|vrpm[W[PQWXTLLT\_]W_jhzvyxsxwqjfiqz||}z{}||vx|yuj\RNLKPWLMWVYZ]ckmr}smoqpusktusy{~|}}{}~}zz}x}|zxwxmeeWTVVSOMMPV[X[eaiv~tv{{tlkoqswv~{|}~yyz~z}|}{~{vl]SPNNXWSZQNSW\`bcgq}uposznntmmr{}~~~|{v~{wwz}yx{zosn][ZVQOSUSPPP\^Yems}}|yxtpooppoq{z|ywwz|z}~tkbXPOUNQZTNMS\cb_ahru~|xru}vyzolknyzzrx}x}yxw{zyqxujh`YQKMSVUPKX`\fikov|zssvwtplgrxy|z}y{~~~~zw}~|ztmhe_X[UPYVRPNPY__`cklo~}zwyqr}vpmnrw|~}}|xy{}yy{{z}~rszpnng[QOQSTTMNTR^ijmruv|ypouywrjquq~{x|}||yx{w~~~|xqnjfbbXNVROQSUWXY]bhggwz~||~{u|xuqnorsrx~~vx|vx{{}zywyprsneYRQRQQNVZPYcfikjiltzwvwxtlrokv}|z{|ywx~|{{y|y}xnklkkpd]]SNLOTXUQT^fbfqnv|{}{rsuvwuroqz}}~~~ytsv{~yrvvrje`ZURKJURQXZ`hkgehms}}~{vurwzotxvz~||{zyw{zuz|~~vpmmsmfj^TPORWXTPQY[^jfiry}uqtxytnnqvu~~zyxtr||}}|{yuokjibYQNVRT\YVX]][]`foxntzrvxvwz|||||{}}|ys}}{zwy~||ztt{uswlaWRSTSRRSTNWc^bhlor~zx{}ysprplz~}z~~|vrsvwx~z~|z{zttutofWSWMRZZZXVUVYZZcml}y|{w}xuspnrx{}~|u}xx~wu{xw}|}tv|snia][VQPTVMPXV]bcabiu}{xwvwrkttu~~||usstwxu~~}yuvxwsjddWVZXXXUMINTW]fclx{uz}zuolntw|}|vz}}zwvx}~z}{{vrnke]UQQVSPWPPZ``]_fnvwvy{qqvnry|}|sqrr{{y{|~}|}|{ytkni[]\YYZXQLJMR\_[glmv~}}{skijls~y{|zxy||zzxy{{zvuttrj]UTUNQZRQTXZY[`flrw}wx{uxxoqtvy{yxsqvzuywy~{|}yvqvtjjc[XUQMMLKLY_]hhgjp|}{tokjjtxz}ytsw|}{vvzusttqibZXQNVSRVUTTZ`egjjo|{st}vstsqot|}xw}wz}{xvz~|}|yopumnpia\WPLMNMOUS_gdfjq{{vqpomlrurxtvy|}ysyzz}xvwvpjfcXPVQOUWUOOW`eggjx|yszvvyvrnou}~~~~tw}yy{||}~~~zxwyquupjcXNJIKNUXQZaaceiqx}|spqpnrplwy||vuwyzyyzz|y{~{xtpnmlaZZPPTUTQQSY_b`eony|~{swzxqlmpuz~}}|~||wvw{~|xx|z~}vrxwusnbUOKIKTQRXUY_dinqsy{sooqwypuwruz|zxur{|w}{yy{vrqslgi]UUTQNOQSTXZ_jhmx|{xyzwsqrrrss}}}xwvy~|wv|~{|~{ywsjaYRNNURT[UQRX`efgks~ztouyrwvppryzvs||{|}}ywwx|ywxrptic^YTOOSUTPLT_]dlpv~yustusronz}xvwz{zyyzvz~}~{tmg`WTVNRWSRPRX_`_agmn~~z|zwzsonqw|~}xszuw}{zzxvx}~uw{rpmdYQOQSTSMQXW`ffhlos{{uwzyuojrru~~||{wwz}wt~|~}yz|vqnlgdbWWZSRQQQSVX[_dbkvzu{}xvsrtuvx}~~~|w~~wrswxy~|y~yuwtlaXVUTRONTNQ]adgihjpz~ywyzxqrvpv~~zz}}xuvz|w{{v}|y{ytrolnh[]YSRRUVUQRW^_]hln{{xwuttspqw}y~}~|vrsuyz{yxzwqib\XUSNRYRTXZ^bb_`elv{xwruvnswz~}{{xvxyu}~wy|~|yz|~}vrqqusjjaVRPQTVROPX\[efgpxxvwyzwqopvx{|{{xutsru~}~{{yvplie]VNLSPRYXY]bb`beiq~xsu|vvyvvx{|{|~}|yztw~z{}{zz|~}z{upqtlnmcZTSTUTRQRUT_fdjptw}{utxyuqorsq}~~zvuvusxyy}}~|vpmlkhaWQVRQYYXWWXZ]adkx|~vrwsv{ywxxvx{}}~uw|wz~}xtv|~z~~}xxxquskd]VTSPOQUUOX_`glnps|}zyxvtstpnwy|~}vqqrux~xy}|~|tqqqmh^XXORVWYZXTTY]`fnnz|~{tz}zxuqmnsy}z}yvz}}yvvz~|x~~~~wswsnkgb]VPNPTQRXU[cgggks||vstwvosuqyytrqrzzv|{{}~~xutsqkgh]WYVUVUQMMQW_ihmwyyz{|}|wqmmot}xzzywvwxxz{{}ytqnliaWRQSOQXRRX]`aciot{xtwxrwuptwz|}~xtrs{zz{wwz~|{~}xuonpfca[YXVRPNLMU^\djkpw|{{vplkloz~~|yvvy{yvsw{}zwvvtmd\XVOSVRTTTTV[`dhlo~|{wu{wstuttx~~||vtyuv~|yww{|z{~|uwzrrpf_ZUOLMMLPWW`ebdgny~{vspmlrru{usuyzzut}{}zyyuoje`UUWRTWUQOSZ^`a`ityvz{wxwtpnry~}{w}|yyxwx{{{{~|z~x|wuyupiaXQNMMNRNQ\\^`djqx~~vrrrprupv}}ywxxyxtxxt~}}|wspng\]XSUVUPMNSY\]^ilp}yx{zvqoqty||}|y}ywxz||ywx|z{z{}yupg[SOMMQWQTWVY\_dilqy~vsrruunsvuy~{yxwvu|}w|~}xwz~yustogg_VWVTRPQRSVXYbdgrzyzzxsppqruw{~}xvvy|{wtw}|{~~|~{ywsk`XQMMRPRWSSW\bgijnu{wsqtzuvxsrsxzwrtzw{~}~~zyyzzuttlnla]YUPMORSSST^femsw}~{xutttsqppz}ywx{{ywvxxy~zwtmfa[TQURQXVSRTZadehmvz}ytsvsuytrrsx|uw{w|~|{{yyyyy{yvorqigb[TOOQSSQNV\^inqv|zsqsutrooxy~zxyxvwyzuw}~}xrlhc]YWORURSSTW[^adinoz~}{|ztzzvsqqtwy|xzwu|~}yvxyyy~}~xtwqoniaVQPQQRPRXU\fiknqrw~wtuvvrmqsq{~{wuvzyv{yz{z}vomkhfg]XYSQQRTUTUZ`fflvxxyyvvutuussxy}}|||xsqswz}}~wtusme^XURPMOUPSZ_dhjikpw~{xvvurvuqwz}}}~yuvyww}xw{~|{|~ytqnnofc`WTRRUVSPQW]]djjs{xvvvvtporx{}~}}}zvtrrv}z}~~|{xsmhd^XTOSVRVWVY]^]^bho}Ŀurrgiknzxjorc_\ZZ\^h|dfpnw}qWSjg7MsT3LkjXDEIHRau{dzsdp]~zYT_qykbhtexk]]bejlga_[mbjm|{cOKZkqcD7>Ocqwuttw}t|ɾ|ztZ^rpbZXZ]^akxmi|fhx}nc_evyuwm]ZavnL:C[llaMEGEKS[fu~}ŹmdslYV^euvkehszqllpssutpjf]Wdw~jhw}hQ@BHTgqtqjb]]dltst|uu|wod^\\[\`babfgrzmmwuj__js~p\TWcqvp`PE>5599@EHIMXi}ma^er|tnhntzqkb_gtwytorttqpu|}pb\SLGD>98<<.1CVb_RD:49>@KXhu~rpsj_bozv}v{nf`[X_n~~mcdpytoginviI/,A^eR?1.;L]jswxxxyyò}x{xherujdfo{~|ycTUgphiovzxrmid^cilu|{rdWLD??BMST[_bjxûobXVYalv}}l_]h|xh^\^bdcbchkt|uuxstspqw~zgUH>FQX`gmsuttv{~}~zj^Z^i{rhcdhmnpsx|zpjmklrux|tuwoj\PG>758?GLOQWabgkjhfc`bht{uorvx|twxxxxy{|ywutoikhba_^^clv}wmcYPMMPU_cekhb]XWY\^agpvwjeheeegjnqrstupnpllmjgfhmqsrmmokklid_YUV\dmy~uoliec^`deox}~{vnlje_ZZ\__^[`a`gjihgdbfmvth`RGFA>===>>?BEJKNW[bmty{~ytsv{|xwporruxyzzwrpppidd]ZYUOGA>=<;:<@<>DFILOQXcrxwxuutsrnhcabc`bc^aefedehkostz|wwvsqokeceilrtnolfdcceeddhpvx¿}voojeeeeec`_bgjjpompqrqqqqrsrqtpjjgddeddfkr{{f_OFC;99;KVew}zn[MM^sĹIJyup_QQix[Xxz`\j}jl_zzYtypppurxpqxpgijnsmS6:`h@4\yrVKWiv|}ʶżqikpomlbY\pyXds`tsmqra`nqhyjnj]VPKGEDGQ]kmZF89MfwxnaWTZcjwů¢}iZ[evnbbm{vij}iYbz~rhcgmu~vpxxnkeinptvseN:8NjugVMITbpyŴxlb]btjWS_||rhgmxzoownkheb`\VPHABLUervrgZMFDFKS]aktw~ÿyjc\Y]fp{ynlu}piea]\^acegm|unhdc][]ZZ[`fmmeXH;17ESds|th^\bp{vpmnpsxz~|rkgegceknu}}qnrt}}ujaWIB=99;=@GQ[dnux¼zxwx|z|}mdb^cjptvwy{|{vutmmooqtuuvy}{skaWNF@@GR^dnpnoomiginu}ƿ{sf]\WYaiosvz|}}{z|ursponlgdceglqopqkigeddeis~z|{uvvtsrnhb`_agklv|{vvpjhc`_^ZXXZ^enprvronnoqqrwzmaXOEB?89:>@DINTY_eqx{{vsrpnnpw}{y|wvvusrrrplidbbZUQID@=98889?GHNSSUY`jt|þzyunlhecbdfgfdciihllklnpqtuvw{zx{vpjfcb`]ZZ\\^fghjjhghloruxyojjdaaa`aekoqrprvtwzxvutqpomjjkhjlifdceilosz}wrlea_]\^abjprw{zzwsnjebaa`adggoqs}zlhbXRWkZ`M82+CV|rpsYfto\Zpz}Yhv|mw|w`PX}\pjrtk`tv~Sczvoen}jqlc}t?)Lrvii]kqstpmvqwb_bmwOXrkX\jykjWV`ht|mfruw~mYSuż`H!$LnnQQFOOFPK\lxq{y}{ztmyrPNHF_mmubZMg{~}|ohZfn|qñ{cPjaE9>>SSe~ytmcgd]Vc]k^WZkfigkxο{pngmyymd]`[PIVSf[XV_QRJKSUXZVVKONQPow{uzsjhcutuivŴ|lcVRG7 (";H[vzuhd^ZHQNQSBCJOHK@KSYabp}|w|~z|xwesr|vwzypqcnootjf`]_hixvnfk]_S]sypna]XWqz~nd^WNE1*8;]at~vwrpli\oftkdemkkf[jnz{uwwxvcb^]YWFXP_^Z[`[ZWK[X[_RSV]duq}igbe\aQbr~yvkd[LH7HG_imvu{njffdmg`x|x|xs|yyltghdKMVXZfYgcgqupsk[[QJRAG9=:@G__pp}spxdbmkh|yxuqetgqjke^W_UPgks{rif[^YZ`gYqy~rztxnZf]]gW]rwsyiihbop~|}}oywihd[gW\T[_jw~}fne]i[Vipuȹzt}xahj^[]P\ORPRQIDRRSqxu{otode^c\Z_fXn{}w~tcngm}qxvucdc_kq~|zjskXVRMUFKJZdqumtm\e\\i[_yȺ~iuuiljbhX]]da\_wyu{md`[XMHTXQjq|{{zvpojke]\hXXgh|~{znukkr|}{zpkrbV\W\\Vam}ofkVSUIQWNiȾ~ebnc[]QQJAFIOLNexxx{yz~nkdcZRWfZblj{r~ywwogppeu||mngdcXcpovn[ZXU]U^l}{yv{}nblcY_VYidt}xmxmnigfY[alorzz}{}j^VUQIK\SS`ctv~|vtpxy}vfeikd[hqmxwpig\alwvlr{lwudbfjwwx}zl`jdVZUXmpx~v{uboj\]]ZXJJWhuw|vvzyxgWUXXRLZTNZ]dsmuwzrmpqphryqkff\W`hidd~yv|icisy~tvwxxkhkmi[\dW\bi}{zkhqeefdc]Xj}vmYQTTP@ALDJNQ_oo³y~op}okjloiZX\_dfvvvvqc^dhkdn|{wz|hnndn~{jiqofRY]Zfovxzahm`]Z[]]cz}}}|vqrrk\[_ZTFRXOQRZl~~~zusw~sbgp{rzmkkki_MKZbkgurx~mkyqvxzuk_dlhbTdrz{{rtelocfjw~sjfjmgVMNGI>EMGPYm{vtrymkpbfhnng]g}}l|usqoi\NL^fnffvw~{yu|u~}{tmyzvttoc^fdh`o|~rv}}uprg^iajspuiabeojYFLMPHCUWcr~{zxxvrcrljjnz||zzkosgeYYSLETbee]t~rpkocnpddcmh^S`kuxxyrnpjh[mr~mjm_cag_N=AE@B;Qat|y|}ureg]hf\bg|{~wrzhd[``[SXb_icpyz~~~uxtyonndmkqkc[j|vttqldwuxwnrj|}u~qmfdlbf^a`[MKPNYWpwu~omirhnnkwwzvsj^]MRLUYTHJ\crjwz}|njo|sqwifdm}jmr~sfMJT|I:_sE9Mc[JNNgo{̾vomb]`y]N`|sqqi[bgxvVB<^qkSNHGSXwxnwaFNUrzdfv|pTSjef{jkhuni^httxuage\OLS_|{K9Lt`91QknxlZkwlZLW~kqrsloplpiiQE32?O]gtjngsýrmahmplcQMBHUeu{|z|plcZV\rvulhnv~~zo^F:ANmthQXW^bbolqpxllmzxxrlWchiZSNP\szy}{mp^P<4ADGNitɾt{~j`[Uc^WHPOOHL`l|rta_XYTUTSURfkxwzvdU_î}{uw}{nZVN>AFex}hnntihhp{rj[bc^VV`i|iswwxlj]ZL75081**E[vùtrg`]^^UXWnzwnvxxpuigcqtxxvd\T`ahtysy{woyuwlux~wl_TUHG49@Ttz{zpkz{}w{vjiss}u{soifhd`LI?FA;;43;^qvzz|nlgjmyz{ohafaqoqieombV`aqy~}whheee\lmhTSTRI>D?O^x}osqmkU[YXQWUH:1>55+.44;Pkr|u|||tunsq{kdclujZSbe{ywvzxeQY\[`X_TPLQKB>1.01=E`feblop|}}y{{xwntrk_Xfaf_ituvwzv~wxpad[QM_\VHFWURFNOXev|~yvu|zŴw[]YVZV^RIEOLD<6C>ET^|y~zlusjqnraThTP_vutķx[266J|ŴvaaZ\PQp]QNUTEhkfk^[QLSeviugT?:5AQQ^sk{ckwq}jposqmoYft~l}ƻ{yidRG.=NR}δypm^M\P=KKQ4LTQRFDG?K[~}o]SVV[][qbkkyrjtfs~s|psznwuosfXsmwþ˳wphP@-&7AqƳyelq[bZgJWZRV\[T?CFkhgztze]]fnkt{liu|oxqylr|yv}tcgYqdi{lbO>&!GLqé~anljuxy|yvhNNEefo~e}{qePRXaoqsnojjZ_OJUafavr|usfmrid_TF.5,?Z_°{ntpcqvta^[zmtuo[YY]fjrw|pYVBGYZSG[WU^_tk||uqqrvxuw~mkaTUUUJ9<+MbrƼ{pjktxx|{oogkumnhjeouxmjSZf^SEM=RUee`ebc`iiYfaywqrhwhbGB>B?#$%#OW}Ƽѿzs}zwpYZEdgo{|jetj}yaSlngXSUSe_tffZXUbp\T[Tsfpwx~yq\JC;A6&0-?Mtѵ}Z]STdUu}}zpXrhwukfJ^nhiZXI^]svqihjiyoZdQbcfstzrzoZVNYT7>,&:5Zsij}qlYmYhwq{|rvqºprqrvxSNd]]KD:FR\wvthg_q}enhf|mp}z~wrfq]mrpabZU[D?G-A?Vtotbimcqz}wqvdtn{WcdZ\NG:IE\ibTTSTnibwfqnr{vtmuiYhdYKI@KI0A96IBaqʽ˷ƻtdgVjds|yzs{aXci|gxstl]OIQH`geYYKPcRW_Oc\pzjbi^MKNFO='9(,29Zj;qd\L]Rfq~w~XSVcytqrjaUYYVuzoecPW_FY_WcXpw~}~onsiZYUOW>*6!18E^jİŸxo`LVRowv~|^VTa~ofpdhe^V`aZtyplkZ_gRdi`jbvys{v~uqxpqxhTTVT`J4D3>GVltĮ|mXec{ytmsyi`_qpjvge`VNVSFcngfk^ajMYgapmu{{wvknk`fXFEJI[R9E38L`}ǹ~`cbx|}sjpowwfsdhotrjg[NS\J\md^bWLWDB[Xkr|rm~q[g]F;>:GL,60,F_ǵĽrxXTYoyvsppYoo[\cĤtyppf[blV^rg_`VKXG:QL\hzyysjodJ=<3;G0;4(?Y~ź}o}aXYk~wtzrpVhj[`io{uqtn`doZavoimfU]M>QJWd||yn{zxioeMCA:ES0B]lbar~}no|xs[stjkwpwlih^TbiUcvpppcU_LIWPbtrhswlmsbk[HEE;FK=RA9Lopil}hnz{uwn_wnjoĹzvhgh_XhcOaiafgYPXDRXTdymhopgvkbcH<:4.B>AS=AY~Ĺr}lix{hym{wkoV\j`dr¬ytto`er\Sd_ckeONHJ3.Gkzzf`nw~wsuv^ii[]ku}trombsifmaowq[XIJWGKax}xjhpir}qy\ECB;=C@VI6<`w¼xmrzswqpzwi`pa^jz~xqocaxo]g__rvm]WCSPFSr~pkkmezlmhE?;>4??MV?3Ij|vowzsju}{~ymo[fh\bvz~~uq^jyjeiUbmp^SE@QECZzxpslkxfq`D=87-74KK62Pku}}ynptxlwsx}xmjWd\Q[st{|vexoplVeko]WDDN?=Xrzxovnv{nx`J?:4+37QI65Qgu|w{rxtv}pzq~zpk[dWN`zx}yfw|ovp\ipwd]HJP?;Wq|{tiqkwwitaQB>:28LVQUqz~{tttw}uxlZRWITJNcotuyt}|rhc`y}z}[O\qwpnle|tuxoi_w~yeOGIUaMX[MAETVUjyvy~xtrpz{zzoaRUGRMFZmusw{{tjf_rzwcPUlvrqnawtq|qj\q{lSHGQbOVZPHGUYWiuxt{|vrp}v~v}yqfWXHTQDUlvsvrji`oyxkVTivturbwwr}{riZlvpSFCMbRY[VRMX]\kr{r{~vrp~~twupf\WFVPFUp{w{mhibru{iYVlxw}uf{uv|xvdZkq}lNC?N_R^XWURZ^bps}}o{vrm~roomb]QDWMGVs|u}igfcpm}{gYXnxypk|u||yy^_ip|gJA@UQ[XRZXTW]ki}ü~koynxs{|lkohg[DTMFJexrspilksk}}k`Vbpuqitp|xw}hfjm|qSE=OTY_QX\WQUchjtlwqp}zttglliiLPTKJWrun|jmktlsrj^^js}{iqlv|s}zuckgtybM@FSScWT]_UQ[ibzmo~py|t{yiclhn_KVNKL`ursthkotg|}pj__kutpqn~zs{njiitqZG=JLXaRWaaRPafeukqzs{xy|y|cciioTOQJKOespy|pjkvnh}tng_^ixrvlqws}opikqlXEALH^\QWb_OPdblqjqxuxx}}y{{`afjmSSMJKPbpqzysjlxjixpnia\fy|rxhr}uqzssiknk[FEJG`ZRXfcOQeaqpjp{xvx{v{|_^dmmWWLKNR^mt|ywjl{kjvppng\bx|v|jq{tn~xvujjjh_JGJF`^W\jlXTfdsump~yww~ssbZ`lo\\NLRW^ly||kizmiqlpqn`_w{oqztmwx}wxigdcbOHKC]`[]ksdYijx|oo|~vt{vljVXep`aSKS[_fwqdrpemgmquj^l{zpywpq~z~v}ngc^bZJMAR`^`hwschnvzrmuxuv|kyt\R[oed]LQZaanzfksflcgovsac{zsxwqouz~uwke[]`QPCL^_divzmjnuz~xot~yur{kvzcSVnghdOQ\ecimlshnbdnwwgbvyvvsomp|{t{nf[ZbYVGM^ahmx}upouzytv}|tnykt{{gUSlhmjUU_iehpnqjpb}{qnoijquhuvsbT]faZIVm̾uS67F;Umt}µp|v^rmGKRl}x||<6wqbeTKV[ppil~ŷ;$̮F)UgqXB6-0;DTbxoU]kͻf\bc{ple]`eb^qþyzS07<[ytY>RiaTR\TQ`yèc* 3҆;"(F_WWKBEMS`s{md`F=UqƾwdTOnqxƾpr|o{}}`K:JSU[jtdWb]kp}sbWNf|zr^UatMWwP;NPQULLW]bitWHBEB-@VzqNQ__p{ǽ~fTQklr~rnpiaelhSZ^l}xgeUZssqyxO-n⾔ha\MNCID7RUXnùztvwk`\Qb}~i~{{a`J=>EPi|ikprZp|jkW\WDCPTEEQfzw||lXnfw{jgNADQG9LANQb~oyynusvhm|c\HD.AHBLL]`jz̶u|{urj`eg{~lo}qp]P>86Fr~µkp~s}ovbRWTUEGAVZJ^y|rxzsvtl\ngngUF443PEG\pp~z~r{~zaZ\klŷ{xlg_qnOH?>0Qeordo{}|cjQGFE:7RMWRf}{txtm_WZsYN?<&5HFQOg{ǻϴwpozqfXJa|w{pwdSob\A)4=MXǴsepwzpedHI30[]}oVejuntnqlb_RQGB6?49MR|İ}ttondgug½~{}vxqtdKXEQEC3_b}fnshzfrn_rZVKHFKT,? -KkͺƷƒvqzm|y]ygquxεylmx|nt~Ob?6FIXCENcq˦qtrsxZr^@F2'B\Uzzzje^cn}{s\eX6CAC9+%>;^w{ͼģxrvfpr`u]xtuɾvadkkedKPB@VKOVPeŷ~ruq|mh`R]?/=9?9I]xttqebnpyqiTHEJ.>I3-7DK{϶¹~zzlnbkfnpiqedvdfoVW?<4H]K`Yj}Ũs{usp\rSB?A?2]g|vd_Zy`xVPSG>)D?@7=MXŲǪ}refdrhrr}µglhkZ`YhcDG0MKNfUZ|οįjrwhntGWGC:2Eb{}~cW\ferqYjGI0/M@C:*6e~xmgqde_YbƿylgvX^_dU>#4H=drb{ĹlqtlmkTZZ;J>N^jtqbhZ_fif_eZ8 >14RICSgzʶ}jbl[krcdy|xgLQQRi`I9<-PeVys|mqkgwzj^hX^19VH\j}z]dTG\mZcfU7H;7A.HFKhy˹ȩ}{|ftgH[VwuūdaWQfBFF3-0SWm}ƹmimawe[UUM;ZYh`l_jkVZ_VqfUM?7)01CYZs^|μzqfjtY_ZuukǙxZFJ]NJLGG5@WVwwºð~ojkhmpur]^KLMNUjxvqV\bOMZ^ZO[\G.01DRfdlpİÿrzu`QVcdrsm{yhbVME=C=D@4GolųɠynxipnafSHbVHRekwz}usibjdcTchM]JI:0EHacacҺнp{xyWa]ZYhfn`acBBC8;SK=HKZ{s¼z~inuhlzX[^LMESsp{y|jqa`UHN]eOcC,:.?`N`gs}Ҹɭv~ygbUeXRbal¿k}`GJ84JBFP=6ShrĴ}{~pgg_giUXJAQ[u}}wr|rbt\WU[V[N:P9+DEFTa|{˪x}[_YQPPd}~vYe>,8HFPVH;A_vʹƽ~ythrdmkgXH\U[ojw~|uzZRaHY[hMDXCDNLTX=CB>UqxvįʵzcygyihbGKKR\h}x{r~fbg]_JhRTcBP?%3JWSqok´yvYRLKKfz~sCGN>?[J4O>CTdoqƺĥwifrWkdSiVSWQZp{ogj\YZ[A\X;^E7@=GNVYfw~rkrxQUQAU[{vs]TPSB6ODCD9D3A\{Ѹkit`lchiRTUc`t}lVOSWSKjeUWBI5.>T^XjsǶsvvutQKNUTcjbMUJ57;?8BGDW`òua`fihwrj_M[]q{s~daZX[HOUXMIN?>;Pjiyå|uvyy~ybVFUY[{tvr_OOIA<:OIHRDL_tva_h^juhma`\bqpkcXNQUSVR_ZLSH7@BZ`sұ{kxto{h]YSVRexstzbTS62,6?DNS\^wom_e]^_de\mqo{z}|qeneRVKDOIQHLHJCGbfr~Ű}rrfd^]RXcXcrvvji]NHFA1:>8CKhmq{}{vgYfb]bi|mciqn|~xkcSXNMKQLOQ??=NREOo~ɫtvtiham\MP`[n|su^E57F4.:ICWau}zu}tYR^\Vdjzqgxyz{lgT?:7QMFCN>CMR]S`ɹxcbjYe_aYGUtwrnmabP9A=D4)BY]`vxypuskeLNjuotymwkxkcs~fUQO7GCSN6APNDOPc{ɵbUnypbbMZVi~l|~XQTG6996GF^`ag}k{uqVWSYopq}|}{}kq{yzN=KI@AN\LJDHdeu׼Į|jtn^]N^`XWm}ȴoofgoSA3,@6:BcVgtnp^cbUO]k`~z}a~|xfg\XdMB91JEC=TBJS[{nپt}zh\ekVj\ocLm}ulzi^LS2466J2Cgqstqw~k{dq]B_ksnsqztmdp]P@R8>AFZ6?Z]ZOXf¼}kMgljXVSinyƮzsyupzOKULB226PLkypyby~|g[Teggzjuqw}qloqdpD?TOM?BBTEZiXXmrѱvsfmphsVYY_qrh]fKMXD9FA[iYb^rxc\XWzqlrfxuwo|f~~j\NWFI]LCVM\eLOFMm|ڲȾ|pj_~whgO[`ge~xrxkgjC:2+DIURqhvswz_Y___fnsbz~zfjj_PaG:<3LSXK__[olѭyhlehc}dj_cy\xot`ilfPKO>QBa_Jjp||}{|nzphYvZaxmkty|biZV\UJ@T@XRblDVbfkfvDZ}cwjWvwygZ[t{n{|}mR@MGO@CSsjztgy|z}xv_ux~{_vmgyxkkkp`ljowtrp{uveYfjA_^P[DHYgc|s|s_U7TLR[FjwxqiXuxstxo^]xj{mdvtgxaUbNP]mg|zp{coLCER>\gle_SReey~bxmyh]hio\WHEXmxzuyeRz^dnSsoitzjs|V`l`rxrhb_wyTXZjmMOW6PgdfaJorlûӴ{{}fpq_VlmzyqZQK\Z|~kmkk\L\_ped|qlv~raSWYMVdx}rSuzjksb~dQCMUPtunansyƻѽ~xbad@RHcjvwdOfdz}|zjmVib`z`jxfo{{lL_LEP_s}yftvzoyhkflpWKZFbg}l_mrz~׻tgpX`SRR[}k{dSf^un{|oiaeZbn_|ntsy`QFLK?j~si[orlz}pib_EVCfqvz{qr~sĭҦ|pfeFRFZuerzoWUssh|mhvrYubivk{v}{Y\WKKTS}{k{td|wxgvj}g_aSZlnxtuvjmɳ~twgU^RHVvikrbK]k}qst}h{fZ^ojgy}tcXODIHIm{z{iqbiynwstsd[eLTyjn{zxw{ʭsjgIVMMxZmxW\go}ns{}itdmmow_suyx~z\nVKT:Yqworjdzwti|tepeaasm~hw}dssɶtleONNFq`rn|tMSvw|zoxi[cmu`ztvz\^_JA>P[y{r``ozsayf[nTfivn{z{oˤz}`OX>^`Zsp~f^mpxw~wxwtpjdkqlyr{m_wQMI5Srt{~~dhwz~{~uhvsiluskztqwo|l{||}yz|W?PDS]iovTXh{zikkljmoes{q\iTH48_i}yqjYpp}~iqxkhkrif}xyuwouuyTVIBdBhqx}}ljq}|ryshzs]wgzsxtfjjV^2Gfnunhhuz||pfx[s|qjwpxixiwsuJI?HYLqyw`rnv|mlureaxbtgueMX;7]w~ingVhzrymjxgok}fin|syw|u~na\CQYRknyqrwtttvofb{|ywmwXV^9Ghj}zsorkf{oyz~fxx}klu|vym{r|{xysn~~XZQ6WY]u`tu|}j}}ellupxwkqLPN;Vgtzkc_glZ{rz[syk|xjrzka~{pynrhnxuk`WCXN`uuot|{`f|qd{lymjzRXWOkg~z~bbj^mcg~|kpkfjovtuh~rryyksmd~cWk@HNer{zuz{zxpm}mpghxm}lZdSFiqs}qxefTemQZysvmtwgfmyo|{vyp{qe_hednWTNnto~zn|x}~pu~sknysv~roabKYljz{~tjc\gy\]zmme}xgsw~}n~k~pyvw^qinaNZM`xy~r}otvupgqcm}wzuzuVaPOvnu}uqj`^jWhm~{z~{nxqspyqpkz~oxbuxgjrcgycfaPO[ngr|~pv~ta~}jmgs{r}s]bdLhv~vwcnfLmaRvsu~xxtwiny{q|qswrp}wtcegoegzcdlIXMTcswvqo[~b_p|tvZt[aii}sxsgWY_Zbcj}t{sfqhuolpxwx~nfua_tbdjevZSY`\j~{|rvux]y|he{}h`dYZgr|tsyglgYf^gv`xx}ootqop~{wr{vQ|}kdpurmezuTGOc]fzsnovqmju}xngsUW_\|xropgfhOgjhl}yk{{yqyyj|yw}{w[_sllgeslZfDNV[avyttimzyiowrpxkdcZc]k~y{xb`iO]j\jxv{pp~ilo{llsnt[jwrn\ffldlwWIEd`Vts~u[trqymfoyrzbejjnxw|ztwl[[RWlb]rw|~xvzrdotjfvno`uv|l]^ZcyXfxaNSfldstig{tvklqd|n]hhe~}zxtlt`YcSajfjs}xnnwjquxay||zcWuf~fg`^`yjkun]_\bg|okh}vuyskkkguqle`mt~vlpUg\mgXar}ttsuzung|{ogWfcyxefdf]gbszde\Vfbsz~dv}lslkj}zrZzlbju|nsvugZVgbWXmyx{|jmuemj~xzgJcluvstjWdRWfczri^^[ixw|luudn{|s\jzemsc}vyiaqucbZXf[No|~vuufgc}gk}~vXVpwpd}im^[KRZjkdolcjwy{}tlsmp}zldypr}ppm_^grt_PZpdTguyo^lfxqsziTchr~l`krrUVTS`h|fq{oplx}z{nlupclvtqiw~rxsy·d9*7-(:Vewylutzȳop~u}}v^`XXQ;8G^r}pv|kg]W\eɿw_XRej^^klέ{p^@@Q]u~_D44W`ohlpfSZF1'-FNWx_Wffu}}lKKC9?_{Ÿ~xpswklXWQ_eadsvtla[Scs|ĺjb^pn`c[H8NWmru|qtoWMF]_V]~uO4=AY~wv}{ywqkx~tp[kjt~|lm_dlepiXNNLMUvu~iPEAYlw}}hQXah^xΧyr~vs\^e|mey{Ż}iF>GDYdxpz~y`kryrplejfmq_[[J[TTP?16?UfzaWOUcdesaVX^obI?HPMLqšr{{~tfs~q_drkwsnxn}{}lpf`q~~rmlPVNOMCOitiWZSKabqrkhmjlhsphwo`S[gYEDP^\d~n[fbhQAIcxxwy|zztp`iVOjʴ`\RJRT\g{zsaWOIR?AS\O]|nqeflso{dN?+T]R;DLLm{gXgyo\H_vtfPY~}}tzwtP<6>IqƾwX\bbUMA:Wmrdcmn~gc|{\Xnt{l_GPesopiWFD5G]VuθrYWmrzebd{g[HL;LKWvxѴzhl^Q>E4@`~ɵvqnv~cRU[BYuna?*2;_{vx~gtrrqmx|hk[Q0!6Ocnl_Z\auhU]huxnxswkpvikZA./6MRU_lǿ~h{nsq}UUIV^mbixoon|sluilu{yekpTT`{{fXRNC`qvwsap\]W]VgucP1BXnxӼ{VVQwvtona~ofWCI55>KQ_zmki~lT\jWTNYEMmhy}kvf\?K^}zpdcguaTbjmt]W^~y~z~wT`CeqZu~ytxWte`V\;7eTUWf}սȽؼ|mcpmT_WeL]uc_cyurrW{lwwz}qxd>ZEG-;DDX[Stekdmgzbm`huvbkrvfezkdoww{fGJ8HYbXsqxxwyjrV]hD8K8Ygjsyribnmweq|zuivznndjyVhurqvx{rpmpidzphS^W[ysé|r~{mpaJ[FRNULIUZVoóvw{kRkmy|olSD`GIDesvxqmwyouep?IA=?^S_oq{wt_Xo_zbsxhx~vsr{u|zVhbSKTZD@OQKo}ĶayloOJphuoXQmopjoc\ify}x[rvPRxp{zj|~}qWQ;W^hl~zɴklr|lbjnrN(C`tnr|qwyt|mfho^g\Tal~|r\rlQMe~{]IJbԹ}fglz~oujjkyubR29Sw||ufN^}scUHXpxobUP[nUQ_x{zndgm^N^pxQB9@8G_WRE122MOOWpѲcYNSNUfdnma\]^LALimkdlhWGvyslpUA;/8CXq|vOK^ov{}Ա}ygZcgjvi]HdopstoeZRAKID\`x}~dv~}{cOPxz}tyH+=PQXƦuwtvtlpsqm]SI=*5[]|n_~}ɽktekuw}\gjUhjo^c}lp|qgwxQO]^TBX\Rah~˷gya_wkq{rqfxv`O1;FOphrwiIYoxmc]^U_eR]C@<>_eg{tŹzye]>Zydwwĵpm]2-(-VYx{pWnziu~rT\uwkbTjgejQGJv}rfaZ_eaz{}wisxdLSRhbied_\hp{߿qrqfvvqzqs|rǣ|nfzso[9QWTcO[:%1*4@`Ztʱ{pOhjoWNHN79GLi]nrnh}jsu~zt}n^zpff[LDYjbŦu}ngML[]PTYyyw]iaf]egXfxnjmsWJD[\kkdK]`RTPjj~m}rb^T>KUNdea`\N?ZTpjt}ʳp`_n{tt|v{z}rk|apsuovotaOVDWE2;@D8dssrougl~ybgUqrifssttpy~~xtuwgv|ovlZ]BF070@Yqy}Ǹ|}xT[nun~hvyi|taZjfoZ{sstmsoaiaUF;OpowsV^XW\l_noc`gW\vst^eYcowqXSV}|wv\JZ\fj_svјmmio{lX_YRZdYRvx~z{xzxnlgR3;RRYMWyvbYiMeuwȖfbdM^aHf}ɭ~}~|fnLC[myy]@7qyz~wo]i~ysiQYX8Od^{zlNIszz{FFYtwhfHOgsdKTTTshnuhrngqfpvf]a\xplduhbL6AxveOSpzϺzqbM_qXKe`i̺̬ŵU"J˴^45L6&#?ifnj~vlj{rpfMal\XX_LZT:I`~om\Soƺwx|}qpnlR;=_wyxuusYL\R]opgMKF^i[gttȿkE9A5$/U™kbag[sjV`l~aJK:6JnwgB2&.K]nykcKXJHUfTXMQSj~~_3DLk¦h<->DO]t|}isȱuv`Z_r{~QOLbLJQFF1 328Ar|{`A=3/&0a`czW]PTmb]`fzlXlmsxX]lbttNIUaPTdpzv_EPIZröntv{jMV_RK;Lf~UTKLjxuqmwuuoN'6994AZip^Opr{zVW`xpgtrwuzg{xjcb}|t|~tgabel~RGH\qu~{zyĹunpmaw|Ĥu7**&+4adpcYV^mZOLRa>AJclbk^hipyyzwqfMgeyru_ZifkU]ROgqnfgvr}gdlgfmvhx]]hkal}zz|ccllobvfYQXXJJ.0/:\vymYOWZgU`PE[^hd{sl`]Xr\FDOs~xvqvwnok[azνp^QJJ6Eeqq[YKdsy|fhbe_lzzpz`\Wkwl}t~kz}upr{vtnoqqĽzZf]dtmnimllm^^nvwîp}z|\]OX\Kbwdj[OI9:NVip_{gehtp`Y`Pd]lǢ|z|}}xzrb?VMHgzpirxw~}zpnnjkZL0GQF;09/9EEQP`gnvrzsqy|tvkaaadv}[Wdpta\YZT=_pʽtgn}vu~x}m`kt}{yj`lb^^\Yic@I[UUSA8LCWjv}kfszot}ssa[]YWswlvQGUirilp{|npzhmxmhxib[Uru|~ʺuwx{{mhkb_\VZpgOat|}t{xoxdcothqzg`U944.-MQ\oX_zzz~r}_MV_XZrwzsx}wjidfy}tYXcFHHMU^exkyfgx|n_zjY^knk~}r{k[gpq}~xpvnlrdvn`^^\l`_wndiǺ{xrk}yrke_[e{wex{y~{snqptr}ghZflZf|}||lQSib]BLauvȾĿǷh\P83BQtxwaHSP^ZOVQQ]Yefb^BACJCBC9;V\m{}yio^owrqcZXLYW_kSGCNRPYZO\HDE?FNU[y{ztg|}tl{|xp^Z`qsbmh{olutwvuSFDG;.9EF^Vf}r\cXQdfi`TWRV`ZtskeltvzxkdgnvvƷýyn_LJNg|~ξl_f^w{rhXSY[j]dcI>@F=2?EH[MVcfgmj_M[SRgnxqinlrum}vmnpmtouq`YF99=A@NhiursĸukmmziihK936, *4>[Wevyyq}{kjegeVgb_XVWT\j]fg[ZH=?EHEPifzyhYIEFZvz˿sxizpceh{tp|iZRWTIMX[wvw|u~s~~zxspihvmjn\\J52:?AHnyƵ~tqxpcVRT^|}t{mpcg}nZWWgk]k]OBAA99HLlz{qyv}vnppzgjohonmgeuyx{|hbkpolxdRLNUu~ÿ}giYXlqveQLL^g]lbYNIG>:C@Yc`g__lwxe]cT_^eohcfjxfhmgqvwplz~|w{t\MSWVRqrkmpwxofaosgp`VH@90/9:\ks}ryqsyqjjnzfhg_iomb\hgkufnhREJNHC_ex~uwynWRR[swqxmgdwxwtqibZPRTRpszsr{sy{u~pirjrsdj_F:=A9=[e~{zytw^JIIWcg}yyxqhhypxufbXM?7>9Fbfy|w~}}{wzimxylour||ub]``Scq|{~ueH9:>SWnƺ{pist~xcVMY`]l`]\VJ>?C;U_gulgvtxx||~~zowxmumw~owq^OMNFD[b||fcdry{u~xfYXe]jeZYUI807.GHIcq{~~tk^gbclenz~}vz~vwtpm]jjghkk^LF8@IF[gmvwyz}yjRWXVkt~xtzy{q{}|skwn~|{\DE=>BHOe\V\^WUkhsnOZV`b~~ҹupSQUReYLILHLGEMA=Nl}zyfVS`[fwwtdb{vlu]Znurp}yt{~kYg^XfXSO>11MYeqn~uwƷ|vwr^gYn}fLIcit_Ven{~{}j_uvufUD00/685CIFIJVkwuvbemhmnkrzuqot^PYK`{yu|o|nfq{tgbOZw|eUcisnhbc^f~~ſiOALP^{zYNPcu~n^[rm]KOOM[GA=9-)@?=10BV_TJKcq~kfwlkg^ap{|~zzaYXba[i[g|}lcrnwyjnmhub_kvlVZNS`~~maz~{i^moxsҸ|{rw_>:FZe~}{`I>IJYQD8!$#)>ObVSZq|{}{{~|cfb^EC@H`wƩj[hr|wsYM<3BFN>P^vjif}}sojp~ytkw{xqmXRDRy}}st~h\BDUcmqlRQES_mfVfewdfhzx}~{iqzyqx~vvlos~mjd|xqykt^ORRcp{xjjwn[\Xin}iluz}ztz|ym||wq_\dlspgc{slTOKXzrhyqu\Vkuy{rRTOTU^ZKa`}zb]PYUsur}uwxzrz}nsnprvt~}ykg\ydX_Q_WgZRPIavгfqwdcXXYiU^hw|wku{pn\g|{t}qZaVd~qksib;;R[dqgLPHLPhaZfapb^_lrs{||lrs~xcqmkqffouzqj_cltoowlq[ZQWp|~{{nokgimrzz{s}xww|{qgZikqzu\p|gY]SkpnveLcqu}\PSRS\fP]Zo~edUXPjyj|zln|tropx|}pwmhryo~}wki[|p[[OaX]YWUJ_m}ʶq}oqkdftakhwzzlom}qjWhtv{t`i^{wttzlAJ[aiw^LSOIPi]fbi{gdWZ\ywo{xyypmr{zwrirgsncjqh}}miZwwekdwomg`YLbp{v|nouoiu}}psqxu}xujYijlwz]io`eZw|uutUhuziVZUP[m^bXevhfXVOhzk{ygfuqrphiu}s~vytgpzm}~vli\xw_YM_YVTVQFY`jxûu{uil}qsjozylid}}~vzwmYgkqwt~hlhzzwXYbglyfOWUJMdchbavkgYURi|owuv|rjm{wp{v~pvlvtegrdk|ql]j{b]YkqhdebMYck{|o|zznk{wnluut|~uuxbclio|tXq}jdii~v{r^mxj_aVO`qljXgpleXTRrzp|tb`kitoaar~{ovvwipuf~zrkf_vs^SP^ZQQ[PJ]]fxr}ƶ}{nqpm}{ojcw}{~wnz|tzw^gihtjslqwuqbgloxw[X[RM]kkj[lrnbVQTyojokxsdgu~zouzyvpszkailZsurle_qmYPVhjabo]Wecnt{znrxoh}w~t{xq|{nuxbilhp~vZq{mgpwv{xektqfeXR]rwr\bnpf[SRo}y{scW__pq^]mprw~~yifn]lwnia\hp^OOZ^TPa[Pbahuzsƽ~sr~mzslfq|z{u}|siowymvghkekzixsyuxlkmvu{g\]TQYnuq]aqvi\RPj~rbfdsu`brpqy|~xmoxl`]hWc}njjd_el[LNZfb`tl`lfkww}ss{~tiuvsu~u}qjr{|lw{emjgmzy`p{ukww~~ois~tlg[X_u~v_cqsd[RVp}xp_SWZqmZapptz{xvhegVn~lmf_]eiWLSZbTYk[]dakxt{Ľxwx{r}rih}svuuv}}{}xgjnxokuiodgm}pj|xu{qm{z}seaUTVgzzeYixnaVP`txu__]mw_\j|skyxrr{oeXbWZxkehaabi_MOSab\tpdmeipw~yxyvmzuop~{s|ommxzl}{hmdeixudu~{vl|{ukzvpb^]d|q^gxoaYR_qqoVPP^q\Uapxjyv}yz|tbh^Yvohmb`]daPQR_`ShgY`[`g{{pŷ|{{u}wnhwtpp~vp{~}z{ijhtsg|yjmbceurgzv{q{qgWYXb|~mYezqe[T`ptwa[Ziy_XaqufwuzwsmZ`YUrldiab_ecQPN\bYordh`cfx|s~}|~yxpzynlwynw{wsnirzl|kjbbbqvirz|zlyszgdfd}zde|wh^U`n~tuaRQXrbR[f~|gtxyx~lhiYovhoifa`eYRQVeXbo]\XX[jyovízzv~tgn{klljowxzvztgfdtlj~siga`evoky~z~z|m[a\ip\j|pgZYfp|rt_\Xlz^Z`suixt|{yr]dZXrlgjdc^bcVRM\aYosee`acw{t·}zyxny|njo}jr|{t~|xmggvlrsjd`^ixppy}uw}|vfmetr`rshW[gq{rqZUOfrWW]trjyuzzyfka^tnjljg_`bWSO^b[op`]YZ\tyt}ú~|}x~peqyxjgkkpz|r~wpspf`_pjl~sidb^ewrpw~{v_e`hw_h|rjZYfk}}or`[Vf}f]brmwvztyv`_\Shlfhfe]\b\VOVc[kxlgbdbt{yyr{kt|olhoqxx~rvztvwng`onl|neb^avvtx}~{gmjm}dh~wo\Weh{prbYQ]wdY^nmuvyq{zzfecXjnhiii`]a]WOWd_mylb]_[n|z|ystcmu|{lgeqrxy|nu~wnnnh_[kim~xjab]dwuvw~~|ciblv`j{rjVZgi}ynn_ZTj}gbdzqyv{{t{zt_aXVghggie[[`^WO\basxldcdd{Ź}~~~yvpixzuogq}pr{xum|~}}}sorsl`fokxuhaa[mwvyxojnj~mbwyrcUcfpqqg^VYtm\]gqqtw}qvx{jdcZemkjjlc]_b\PTbamzqd\_Yjy{~|tucpt{xogfqqwzynv}vmloj\\ihq~wh]`Yfvvys~xhmfuq`qxqeS`gl~sph`XWsucafvruw|~uvy}nc`V_giihmc[[a_RS`_kwre\a[j{}|ï}|txhv{{sjjqouyvnw~~}vnntpablkvxi]aWesuxq~ylqm|udtzuhWdhl}sqjbYWpo]Y_{smpty}utw}pge]dlonlqg_^dbSR^`lxseX\Tarv|uñv|gqwxzskf}rnq~{qv|}{plpp`Zefp|ykZ\V^psxounqktydlxulW^hgyvpkd]Ujvf__vzpqtyz|twxif[]dilhpj_Z_dXOZ^ftukY[Z\rxzyź}yom{yxsgt|ql}zrpx|}tlmun^din{~wc\]Whqwwm~ypqptguxvfYggmzrpheY[rn`Y`{snmsu{vrw~qic^eipnnti_\efUS\`nwugW\T_pv}r|ĵzkpxuzvofwwom{rsw|{rklthZadmy{t^ZYViqzumyrpp|ndqtsbWddkwqnfdX\qod^gwpovv|upv~pi`[`bkjjqg]YbdSS[aowvgW]Wdt|wķ{|koyw}yshx}to}wnorwyrhitm]cfo|~yc[]Xir|zn~ppozwgpuuhXcfgsqngf\Znrg^`yzrlrttxnozrhaZ_ajojtmaZ`j\S[_mxyq\^Z^px{yĻxpkws{{vuirwo~yooqs~y}ujgrp]^ajx|zgZ]Veq{p}rpowudiprhV_cboomfg]Ykrkbc{~umsutymlyrjaZ]\fkfqk_X^i[RY_oxzr\][`r}}Ļxnkvs{}zxkv{sz}vklmp}x}rgero]ado}}f\^Zjtp~qnpytgmqsdXcceoolfg\^oqk`f}{rksquuinxoh\[^]llmuk_YdjYW[cty{m[_Zftxù{{}kqvs|~wyqk|vt}}rkplw}x{oejvg]`dt{|t\]X^lvvtxrov}zifkplY\c_glmfhbYgpne`tymptq|nhvtmcZ_[clgqnbY\j`TX\nxzu^\Z^nzzĽz~onvsz{{ymx{sxvkokp}xrfetm]`bs}~{b\Z[jtzo}sou{qilqq_]eagmohhf[eooh^m}xllrm|}pftune[_\bpltth]]leWZ]nyzx`\[]kv}s}sryuzz{xnxyp~~zxmrmt{uiixo`act}}y`[X[hrvm}urx}~pijqq`_fbglqjki^fnoh_n~zllrm|rgtvqh^b]bpltui]]lgYZ\nz{zc[Z\it~s}}vryvzsz~s~{~vzmrmo|zlhvudcdtj^\[gp|lwxqv{wnlqwhbighmtolobfnpoaiz{zofoksvfmwsm_a`^oqu{se^jq`^]j||n]][forw}tyzy||wu~wvx~~|ztpslyujp|ofdj}xa^Zaivrnztsz}~rkjsqcehfhmtlpldloskbq}{qxxosntfPC>ME96AX`bkWIFLUYkdT`q}~yͯ}ozmRGFFGB?@M^UQXYUTYTR]]ik\djLnTaaeysųkbZ`h`oeFUpalu|tywqqzqqwʧi?CscA59EUwxfUPBESUae_VYjg^]azupr}}y{ɾsiwo]]\]^XRNVc]\fls~z}yppqzpm|~sXLNFC8@KOiz}}c[YetjUSo|xmehxiRA9LzsV?+8ITWPXUTdflrnb^nxpps|qiebicMKTenpu}qmpqos~ztwmiu{~uyyxzlrjaJ=<0?;08:=U^irnd`nvlgdp}{yfRGDIN^_QS^oxwyxqoka_ls}{unx~{sw|}zz|uq{{xkesxxjee[fpt{zpfm~zyyzxlb_iø¸ocL32=N_|sVRMG6),#):ALOH==MOGBCXbcbOD?FN[mg\`l{|z{zsdelnupg\Vaelqomfnxrtvwx{vqtyz}ttvoeSYdljahegzynlsoe`ctscWO82:JZmynlfZDA90=CJNJ>7AKFA>Nbei_QKOX`svhdjz~zw{zsz{}sh[bgkqqpil{{}{oosvw~z|shQLUY[ORURes~y}z~~tgdmne]]jxnaVS?6>LWbebWS_ed]\n|~~rfZ^aftn`WZhi`WXevty|~tt~|~{vslvxvukfYSTGBDMV^r~}umv~swxp{|omstxnjxzqnwvhdejlt}hdefXMJ@CO[dfaVT_dcZ\oy|znaW]^eqh]TZgg`Y\j{}rwz{|~}pqyy{wrngqssqhcUPQGDHS]g|voxx}~uqottwmhw{tq{vung`[oswztnqqlS_WJ\agtiZTdjxqYvmu}sfY]Rim`\FJa[TO[gy{|phjmv{pjqavuunmgp|kkdMPPA=L\qtr{}ssjkyuf`ck~lpf{rv{[a\h}qsjd]^X`\Xlfnoihoohs`kszs^GQ\[u`QK?^ficcdq}}{vmlz|zo{w`xnmvg[Sjnwt[_KHURXY_e~~uyrimfy|ypcbvoxy}d_raamhtojzyj[S^UouksdUXrsmigwzzwi`^UNecNTF[kcMSnswqloh\ozxqaiputkkhfaenX_LAWRHDcpz}~}pkucxzkQXmu~vyĻ{l^bahfgr}oqspXedV]\ly|vhmzpj^mn}otcLDRU]XTN^f`Yhww~{{}||utq|rohhflufjgVG_``_\UOTNJXns{xmsr|ujhpqyuvk}}}u}ojed`dzu~wybTUbl}zqnkf_sbbk|x`N]b[^URDY]a]XUjw}}jtrj[pl_`lryq_S\rhg[ZKNNOPQM`}zliputqkZ^zrbs|xxzmYpkeT_g~~h__\_m|ndf|kgjsz}peRTG]bZIBNbr`[bwqs}~wx}vgbope[bhtfcTWVdlaRFFJcXVTlmyl`lj}tdp|pobp|~~jwisaeX[fuzovnghhx{rmionslqhsts|\[TcYWODJ^db\_ecv|xtrpdghrje\[m^q^X\jeZWHEKUW[T_]uubgkxx|lintrkepv}mq~ngcgb^Tswyvhjoquq~otsukfoiwsuj`VXTZST;ScbjQ_a|ouessfjpqWaT`gba]V_]^XVE:IK_]YWMQNVlisp{fcklue[mtzx{vmny|iettpXM]\lgcjcf[Wloyv~z}xoysp^XYQXTP[_somskpvw}skZQ^`f]VWVVE>HHTTW[RQNPdnrispiihkbhmpw{npvyxkkhlq[ZY^dUbmhhYZ_n|vz~zpvuz|kaTQRH_]bldrrlqh~{y|vZZV^d^^NNTGRFMRBUXRWL[[dnlsy}vjkdWqqytkww{qqpmohic\j]dZMafhgXa\gt|xs|~rxu{}ygVVIM^cn_aqjonoz}y{tg_W\`aVRPCSVRUCHNJURY\XYbrqx{ujWgi|zdu}|}remijgq`^t]tTF^]`ib`lfcrkj|sw|nuc|~uo`X[V^k\dcT`knq{~x~od[d`qURW5OU_bLBIKIU]V[VLTdqrdq`infztzrx{{xeipnl]Zky^]YS]XO[]`rneonjpw~ustii\Ydhhb`cQ^l{vexox}zw}zqeVOfUTVCa`CDE8IU[`TPSS`yub}pjsnxlttmsvvvy^pza]]dkhjk\VNTcloec`\uhmvypxu|z_eePkcQ[YaWnmpqk{uzaKSWW`_\bOQYRQGBWMQ_ONS]Mlwv|vrbngijqkm}}~r}remon[omVbQMRYZ\kZd\]uuyywuxt_x_ZaP[dhf]lui]gqy_inW[^]_UNYLK7IN@KNTSddUiytuh~swmwl_hw|l~wsnklxhNrjdZQSS]aiiibXUlgvtx}p|v}zaaNXZdhP]gjTdofpvvsnlPVe]eISTY_VPTD9BMNThO_cnmct}wzjvsobfdorx~~bqgjpua`[YQJVVc]iZZb[eyt}zrzvoYVVTZcXic_[zykwk~sx}tzidebYN[OKVU]J@4DAFQ\Zhgggj{~qnmcvxsof~|xoixomkcVa^PAUUV^b^cxbtci|}~|xxt~y}cie^_GS]e]bnmhxtqykt~}t`hNW_RBRQ]U>?AJ@\LQ[_nfiu{uxsuujgupppn|thjj}sc^]iT]KICMbdeedqiszgz~l}sukf_bS[N^P`{jzo|soznvr~tbO^fS_FIHHRTA9APRbSViS~y|lxbjyvyj{p|xw|Vmikvq_VRdbY:KRRvexklpoqrptufn~wjXUT`JWict~pms{~{{vm{yqd]VZb`Y1GITODRCSN`\Sjppfgltsws|wy|lWobxdX]a\KZDM\]y}h~ros~{z|ul|f`caTZfZobvvjk{s}~xnrlxWbYbSOC=SJSeOMTXdYkbql|yu}~ygwsgsvqv}t{pp[hqclr_`_hWZPQnburmityyh|q}xxwzxhc]S[ljV]eqwmp|xpweY@?6)?FXdigwqmstvsytd?FAGF`|{z_QSdk~[XVL^WQORXigb^LGES9CMIrtdd{xwywnhyYhn}_AK_hnkt~zw{TMMLuqw||rU^cȤY@gPGZpz|pp[OCGFFa`^mquyjdl}ɿžzfiqq|yhUOQ[rzwdqkcfOH`kkimgf~xt|nyhHOaZkhccioqzqrw~oTWS[txmZfqvvzX?.)eṐ|lRNAQrszh^WSNgranrutstjcYRMWYlyryypcb[Rgf`lm}{ocq{|kkvf|~p|qmgOE<4,17(%+A>;JK][L68LRbdjoojcdciaiq}||ymkkcXT[[ap|wpsssmettrefjs~{j`b`yuaZj~vvtgkx~zefdicprixw{ǽͱȻ}dMFZH8)3HjõpijsxfX@A/%)8?8BDW\Q?6JOdjv}|knmukiu||zqrsocnffn}|qronxv|{|jT\cw{{r[KYd~zpin\ai}}x}{spX^`gkx~qzszxʺxgodlpTB8SI?4:UtŵzzzxpWYH<=EQNUYjj_NG[^tvswu|okx{~w|otw~rqdO\l|xqQDT\zoj^X\LTWinov~ivu|ookeQ`agr~y~r{x~~yaWcRe^G99VHF7Ba׾x}dfPPOYebenvnZaiqt}wzdpxz|~vewlZKau|pHNPbubePSGCHL_acmwhbkkzfmhiTTb^n{{vu}|zsy{ziKZTShND2JPKF7Rn̹wpd\`[osnu~riqmu{{}lfwq~zcovjcGXmWQVZxcgUKD;D=T_[azzpi]e^usdfghZPbZfv|ys{osv{}{z}xnLVWLePJ7DQKI0Gdѿvmch^s}v{|qwqvz{{kdvn||zckslcARg|b^^c|ekWKB>C9R^W`ztld[_[so|^fghUQ`Uds{yvt}qrpz|vytzxoM]YOeSQ;EONI,B\vĿynjoaxy~vwqvz||levky~y~{edplg@J`pwhheordOE@I;QaYZuuki\^Wlo|y`agfXL^T`pxx|vosrzouwys{vpxvUXaKaVTC=NOT18Tfũ}rjscr{{zq|t~}taslr{twoYggnP@[f~ptez|pv[MBNCEbaWh|mnd_X^pr{|lXdd`KXZWktz{~nv|w~tsoywqxxjwywlObRMYPR:BKWK0J^rĹzlrpe}|wspwzyfgsj}~lzrtcVahlGJblwugyxw\IGSAPgaZozloc]VbpywybZcdZJ[X_rx}|m{z{ztoqwwryvewsugNaLJPMN7?K\L7Teyözlysj|utnuyxdhrm}|fpko`PZgoNQhr|vg{z{{bKIWFRie[lzmrh\W^oyvubV^b[K[Zbv|oy|ytnpwtuxt}dmqqkNZK>FGM:6E]ZBYm}ƹǷínwzkwrpouxietpv~ebdhcMPbt^Vlwygs|{mQIZLPij`fynsl\WZkxuqcTZ`]MZ]cy~qwxslowr{}yyqwbf|nmlRUK9?DL=4C_bNauǻȶp{~nus~pnsyjiwttx_Z^e`MNdwa^r}xgt|~oQN^MWllahwnukZX\l{rn^UXa[O^^m}oz}}~phqtqx|yumuz[g{shjeOSC5;EI:5KhaVj{Ƿ´²qyuyuu|onvwhqv}ytlVV_dXISqs_hvskz~gLY[Odmk_nqov`VX`tzmfYU[cTV_ay}tqzvkkuqrx}ymmyeXptieiZOL74?JD6<^kYbr˸vwtvysmq{nmuv|spZQ[d_NLiwddo|}mvvTXcTbmpcgrnxfVZ[n~nh]VWcYU^]tzun~~|nktqpt|mjrkUhqhdhaPN:1=IG:7Xi\^i{˹ƶ}|uyywop{rnttzup\P]ebQKiudchwr{zY^fXgosegppycV[\p|mg^UXcYW[]su~~pm~|nnurou}lkq~gUhmhehdSN;1@JI;6WeYY`t~ǹʼu{y{qq}uqssyxq`P\fdWIetgbbsxx{c`k^govkdooziU\[k~ngbXUb^XZXmrx}piy|rntsppmhkyoS`ifedhWNA0;IJD3Me\YYlvǹƮw|zvnz{tuq|~qgRRcdbLUrnf`jv~xx_lhdmsweinvw[Z]_vuif`R[c\[Xbqr~~whp{yzorrrluyfhkx_Sbbd`ddPI8/BHL=9\d^Z_uz̿v}ypp}yvtuuyk_LUab_I^unh`rxzvepijnxwejoyrX]\czrgh_R_d_^Yhswxht{yyrtrslw~vfgjv[S_]d^dcNF61DHO?>`ge_e{~˽v|yno{zxsvsz}|wf[IQ]``J]uqndu}z}iqmkny{ijnzv[]]autghbS]ddb]iw{kt~x|turtks{ggfvaQ[Za^_fQE8.?GPH=]nmihétyv}rkwz{xuusyxwzdYIHX\cOUtwvluzmqplnwnjlw{`\^`pwghfV\ehhahy~prw}utptkn}hfcteQYW_^]fRD9.IVIF]hnfjx|zv|~rtttw}~}v~omestu~lUHDKRa[N_msnhvx||wx{lusgbenu}pkrdabjvhcmxxk{v}|~~stnd|}rk`kn]YQXa^jaI93:APUFOcknerxĺ}xv|}suvs{}xyxxnhetqwzeODEKUcWQdoulmx{|}v|yjxpgahnv{mlrccbmwgen|vn{u}z|~~qtje}||qj_kk\XNY`_i_G74;ARUHSenqiv{º~wv{|ttvu|vwvwmfaqouyeMCDJScZTcoxrq{~|}vy|jtqhaflqynjrfdbkzmgo{}o{v|wz}rrlav{|qk^ek^WMR^\gcL94:@PZOSeqypy|ys||uvtz|xxzq}ynf]jnpxkQBCHOb`Wbp{yu~}|xv~lnsiackmvpirieci|siozqzwztvzsol_p~yzrl^bi_XLO\\edO94:@P]SVfs}v|{yr{~|wwt}~uwxozvnd[hlpviPACIPcaZdq~|y||ww~jo~qj`dkmtmjrigcl~rkq}rzu||ru{~pog_s|ywpi\cf]VIQ[]faJ76=BV]U\ix~x}vt}}{ywx}q~qryrk]\jkss`H@FIXf^^ivy}{}t~vgxymg_ijosjophgdv|mnswvxuuqvxnn_fyyysna]fa\NJX[cfX?4;?K]XXbo}y|{|s{}}{zt~tvwowtobYgkquiN@EHQc`]co}zz~}w{~is|qkahklrlmrjibp~plo{txu~ypu}oobbuzytqe]dc_RHV[bg]C4:>GZYV]hxwx}}t|~}{t}uwxpwuqcZgkrvjP@EHPb^\`jyvwyy~ju{rmbjllrlorkibq}nkmytvt~xpv}qpcev{{usf_edaRJW\dh]C4:>FYWVZdtsux~v}|t}~wv|qvvtfZekrvmTADHM_^[^etutwz||nq|tqeinkpnnsmkbk|rjjx~tus{~pstqfbqz{vtl_dddXJS[chbL68>ATWUW]nsrvy{z~wy}|}tstuvn\_iovsaGAHHW`]]_lvsvvz{lyxtmenllpnsqnhcuzmjmxtru}umv|rmaguzzttfaddcQLX^gh]D6>?IYXYYcsrvv~y~w}wtrutwk[dktxs^FFKM_baabrwwxx~~zvmyvukgoklppusqigz{plqwtpu{rkuyrk`gtzytteccfdROZakk_F:BCO]\_^gvvzxy}x}vsstsxm]dkuyubJIOPafffet{z{y~}xxlvvungnkkoqwvumhyuoq{vpsx~tiqzqk`dpxzsuhabehWOZalmfP?FIPabechz{~{{{|}|zwpysrqws`bjszxkRKSR`iikht~z}v|~{kruvqgmlinrxxwqiwzsqzxqsvu|jY[hd\YSZiw|ttg]^bk^Vahv{xhZbceqnoln}||spu~|{{tz|vo|yvpp}{yxnrymipksuqqkv|wrgmr~{w|}xmtvyrjrrt}}uzsqt|pjddok\PXga^\Ydqy{leekrcajr}mbkimurtns}xvkpx|}~xuffwwqpjq~~srs~tlu{~mrsqzwwsp{xvnnw{~yz~|wefkmx||wyqvwiWQ^b[ZTXdoypphZYZfbW`fs{zq`bfblkljepppkhsvxx~~mdqxrrlny~onmyxltysquoxwwvpxyxunwzy}zz|yfcklvz}w|vx}sbWbkcc\\enxpmiZXXceZafr{ztcae`iiijdmoomforzw}~pgs{vwpqy|nmmyzpx{trtnwuwvpxxyunux|}yy~zgfkltw}zw}w{ue\hoiiabhnwnlgZYYfh^eiu|zscac_gfijdlmolfmp|x}}uju~y{srx~}{ommy~tz|vqtmttuwqvxxwntv}zw{}~lejjqu{|u}yyzmahsnngcilupjk^\[emchjr}{vhad^dggkfjmnpfknxv|z}pq}}yrwzz~snntyy}~qtopusxtsxw{rqvz}}xw~~wfhimtx~xx}v}~vidruqpfhjotklg^^`mlgmmx}yrddb`gejjfllqmhnp|w{zzpx~xtxz~z|qppyy}}zstnsttysuwx{qtw~}|wu~|seiiou{y{}w~~vjguutrhjkorjmgaacpnkonz|yqefcahflkhmlrnjorzvzz{qy~yuxy|z|srqy{~~{tunsttyuuvx|tvy{|vsy{|~~ueiiow|}|w|~xnhtwuujkknsknkcccoqlqnx}zujhfbihlnjllrrlqt~~uzy~tu}uxx~~y}usrv}~vwprutywuvv~ww{~z~|vruzy{|vgikox}}w{~xpitwuwmlmnslomeeeornsow~{vljhcjilokmlstnsv~|uzz~uw}wyy~}y}usrw}xxpsutzwvuwxx|{~~~ztpv~xyyzrfklrzw}}xplwwxwnooqsmrlgghrrqtq{~{ummgfkinolmmvspux~wxz||u}zyz{z|{tts{}}|yupvtvzvvt{}w{}|~}}|vrp{|}xyyxkimmw|~{z|wnszw|uqrquqqrjihntqvrv}yrolflklqnnlrvptv}}{vzyx{z|{|{}vusx}~~zxquuuzwvtx~wy{~||~|}wsox|xyxynhlktz~~z~zrt{y}ystrvrqtlkimtrwtu~}{tqoglllqonkpvpru{||vyxz}|~|{{~vutx~~~|yqvuuzwvsx}vxz}}|~||vrpy{yyxxmiljsx}}|{ux}{{vwuwssumlkoutxtv~{usohmllqonkpvpqty~||vyv}}~~}|{~wvtw~~}zrvvuyxwru}vwy}|}}{|wsou{zyxxpiljpv|~{}xx~|yywxvtwpnmnuuywu~|xtsjmnlqqplmvqqsw~|vyvy~~zzvvu|}}~}uswtwywtq{yvxz{}~||ytqpz|{|yxwvkkkktx|~|x|~~~{{yytwvoonrvw|vz~|wvqkpmnrqokrvqsuz}{{vyv|}|ywvw}~~}suwtxxwss|xwy|{~}|z{wsoq{z|{ywwsjmlmuz||y~~~}|zyuyvppotvy}w||xxqlqnpsrpksvruv||zzwyv}|{ywvw|~~~tuwtxxwss|yxz}z}||}yywrooxyy{xwvtlnnnv{~}z~~~}{zuxxqqpsvy~xz~zzumrpossrlrytvx}|y}|w{x|}{{www{}~vtxtwxxtr{{y{}z~~z~|{xwvqnmuww~zxwutmnpow|~{~~~|{vyysrqtwyzz{{vnrqpstsmszvxy~~{x}{w{y}}{zwww{|~vuxtwxxtr|{z|~z}{~z{}vwtpmnvvx}yxvusmqpqy~~|~}{v{yssrvw|z}~||totqrttrnvzwy{|yxxy{y{~xxwy{~|txvuxxwqv}y{||{{|~}y}yuvromrvu{|xwuuonrou{}~xy|vutuwz}{}}zqssqtutorzwy{~}zx|zx{x|}|zxxx{}uwwtwwwrs|yz{~~z|{}~~y|ztvrompvuz}yxuupmrosy~zz~xuuuxz{~|sturtuupqywxz}~}zx|~yw{w{}}zyxy{}uwxuwwwrs{xyz}}{{{}~}y|ytvromqvu{|yxutnnqnrx~{{~xwvvy{||suuruuupqywxy|~}zx|~ywzwz~}|yyy{}wwxuvwwrrzxxz|~{|{}~~y{ytuspnpvty}zxvtomqnpw|}{{wwwyz}vuwstuvrpxxwy{~~~zyz~zvzxw}|zzyz{{vyvuwwupvzwyz~{~{|}~{z{uttqonsuu|{ywurmoqnsy|~yxxyz}}uwvtvvvptzwyz}|~|zx|~}xvzwy}}zzy{|xwyuvwwsqxyxz{{~{||}zzzssspnottv|zywtqmqqpu{|yyxyz~}uxwuvwvquzxz|{}{yx{}~|ww{xz}}zzy{|ywyuvvwtqxyxz|z~}z{||zyysrronnsst{zywurnqsqv{}~zyyyz}wxxuvwwst{z{}z|{xwy~|}yv{zz}zzyz{{vyvuvwuqwzx{|~~~~z}~zz{{yxyrqronmrssz{ywusorusw|}~{zyz{}wxxvwwxtu|{|~y{zxwy}{|xw|z{}~zzyz{zwyvuvwurxzy{}~~}~}z}}zzz{xxwqpqnmnsrt{zywurotutx~}{zz{{~wzxwwxwsx|{}|yzxww{|{~{vy|z}|zyzz|wxxuvvvrtzyz|~~|}~~{{{zzzywxtoqonmqsrx{yxvtpqvtv{~{{{{}zyzwxxxuu|{|~~xzywwy|{~|ww|z{~~zzz{|ywyvvvvsryyy{}~|}}~{z|zzzzwxuoppnnptrv{yxvtpoutuz|{{|}{z{xxxxvu{{|~~xyyxwz}{~{wv{yz~{zz{|yxyvvvvrrxxxz|~~~|}}~{{|z{zzwyuopponqtrw{yxvsootsty~|||}~|{{yxxxvu{{{~~xyyxwz}{~{vuzyy~{{{{|zxyvvuvsrwxwz{~~}|}~|{}{{zzxxvoppooqtrv{zxvtpnsssw|}}|}~~{|zyxywuz|{}yxyxxy|{|~|xtxzx|}{{{{~xyxvuutquxwyz}~}~||}}z}|{{zywxropooostsyzywurnptsuz~}}}~|||yyyyvw|{|~}wxxxxz|z~}zvuzyy}|{{{||xywvuusrvxwy{}|~}||}|z~~{{zzxwwpnpooptssyzywurnrutw{~}}~|}|zyyyvx}|}|wxxwwz|z~}zvuzzz~|{{{||xywvuutrvxwy{}{~|{{|{y}~{zzywwwqnoooosssyzyxvsorvuw{~}}~}}}{zyywx}}}}wvwwwy|z|~{xuz{{~|{z{{~}xyxvuutrvyxz|~{}|z{{{y|~{zyywvwqmnnnosssx{yxvtprwvx|~}}~~}}{zzzxx}}~~|vvwvwy{z|~{wv{|{|{zz{|xyxvuutrwyxz|~}z|{zz{zy}~{zyxvvvomnnopsssyzyxvsptwwz~~}~~}~}{zzzwz~~~zuvvvwz{z~}zvw|{}~{zzz|zyywvuustxxy{}~{{|zzz{yz~|zzywvvsmnnnortsvzzywuqqwwx|~~~~~|{zzxy~~~~|vuvvwy{z|}{wvz{{|{z{{{yzwvuussxxxz|~{z|{zz{zz~~{zyxvwtnnooprutvzzyxurqvww{~~~~}{{zyy}~~~|vvwwxz|{}}{wvz{{~|{{{|{zzxvvussxxxz|}{z|{z{{z{~{{zxwwtooppqtvtw{{zxurqvvwz~~~~}|{{yy}~~}wvwxy{}|~~{wvz{{}}{{{|}z{ywvvtsxxxz{}{z|{z{{z{||{ywxvpopqrtwvw||{yvsquwwy}~~~~}|{zx|~}yvwxy{}}}|yvx{{|~|{{{~{{{xwvusvyxy{|~}y{|zz|zwvtstuvwyzywusqqrtvz}~}||}~~~~|zxvuttuvvwyz|}~}}{}||}~~}||{{||}}}}|{zywutrqqqrrsuwz|~{qksztrstvwwvtsqppprtvy|~~~|{zz{}}|zxxwxyz{}|ww}~~~}{yxwwxy|~~}~~~~}}}~zsnlou}|zwurpmjhfefgilnpstttrqpprtw{~~{yxxy{}}|}}}||}}|xwx|~|zxvtsstuwz|~}||~{wspnpsy~}zwtqnkihhijloqsttsrpooqsvy||ywvvw{~||{|}~~~~}zxwz~}zxvtsstvx{}~}|}~~|yuqoorv{~{xuroljiijlnprtuusrqppqtwz}}zxvuux{~|{{|}~~~{xwx|}zxvtstuwy|~~||}~~|yvrpopty}~|yuromkkkmnqstuvusrppqsux{~~{xvttvy}}|{|}~~~~|ywwz}~|ywuttuwy{~~|||~~}}zwsqooqv{~{xuqomllnoqtuvvutrqpqrux{}~{xvttux{~|||}~~~~~|ywvx{}zxuttuwy|~}|{|}~}~~~~~}zwspnnpsx}}zvsponnoqsuvwwvtrqppruwz}}zwuttuwz~~}||}~~~~~|ywuvy|~{yvuuvwz|~}|{{}}}}~~}}~~}zwtqonnquz}yvsqppqstvwxxwusrqqrtwz|~}{xutstvy}~}|}~~}~~}zwuuwz~~{xvvvwy|~~|{{|~}||~}}}}}~~|zxurpnnpsx}}yvtrqrsuwxyyywusrqrsvy{}~|ywtsstwz~}}}}~}}~~}{xvuvx|}{xwvwy{}}|{{|~~|||~~}||||}~~}{yvsqonoquz}zwtsstuwxzzzyxvtsrsuwy|~~|yvtssux{}}}~~}}}~~{xvuuvy}|zxxxyz|}~|{zz{}|zyy{~}||}~~}{yxwvuuttuuvxz||xurqqrtwy|}~}|zxwvvvwxz{||||{{z{{}~~~|zzz{}{wsrrtx~}xtrqruy}{wutux}~yvuwz|{{}}yvuuvx{~|xtpmllmpuz~}}}}}}}}}|{yxvutttuwz}}yvsqpruy}{{|~~}}~~|{{{{|}~~~~~}}~}yvttuwy|~}|||}~~}zvtsstvy|~~|yurommorw}{wtsrtwz~}yurppsvz{vrpprv|{yxz}}{zz{}}zwusssux|}||}~~}{zyxy{}}{yyz}~~}{xvtssrsstuvwxyyz|}}zwvuvwz|~|ywuuvx{~|yvuuwz}{yz{~|zxxxy{}|zwuuuwz~}yvuuwz~|yvvx{~zwvwy}~|yxwwwxz|}~}{yuromllmpty~~|zxxxz{}}|zxxxyz|}~~}|zyyz|~~~~~~|{ywvvvwxz|~zwvwy}}ywvx{{wuuvz~~}|}~}zxvtssstuvwxyyxwusrrsuz}||~~|yxxyz|~}{zxwwwxyz|~~}}}~|zyxyz|~|xtrqrtx}{yxy{~~{xwxy|}|||}~~|zyxxxz{}~~}{yvsqppqsuvwxwwwx{}|}~{ywwwxz}}zwusrrtvy}~~~~~|zyyz|~{wsqrtx~~|zzz{}~~}|{zyxxxyz}~}}}~|yvttuwz|}}{xuqonoqtvxyyyyz}~|{|}~{yxwwwxyz{{||||}}~~}||}|yxxz|}zvsrruz}{zyxxxy{}~}}}}~}|}~|xustvx{||zwtpnnoqtvyz{|}~}||{{{{{||}~~|ywutuvx|~}}}{|}~{yxxy|}~~}{xvttux|}{zzz{|~}{zxwwwy{~|{|~{wtssuwy{{zxuronmmoqux|~|zyyyyyz{|}~~~~}}}~~}~}{{}~{xwvvvwyz{|}~~~}{yxxyz|~~{xwwx{~|{{|~}zwtrrrstuwxxxxwvutsstvx{~~}|{{{|~|ywvvwz}~}||~}zxwxz}{xurqqqsvy}}zxwx{~~zwuvy|}xutuy~{{}|wtstvx{~{wsolkklnpsuwxxxxy|~}~}|{{{{{{|~~{zy{~{xwx{~|xutuwz|}}zwtrrtx}~}|{{{zzz|~|xtrruz~zxx{~~zyy{}~}zvrnlmpsw{||zwtqpprux|~|zz{}|wtsuy~}|}}zwvwy|~{ywvuuuvwyz|~~~}{{{}}{yyy{~}|{{}{xvuuvwy{~~|yvsponnoqsuvvuuttvy~}{|~yvvx{}yvutuw{~|{zz|~~{yy{}~{wsqrty}{xuvy~{yxxy{~~|zyxwwxz}}{{|}{|{vqooruxz{ywtqpoopqsvx{}~{zz{~|xvwz}||}~|{yxvuuuuvxz}~}{{{}~|xwx|}zyz|~~{xutuw{}{zyyyz{}|yurommnptwz{zxtpmlnsz~~~{xvutuvx{~~|{|~{xxy||yvvwy{{zwuttw{~~~~}|zxwvvwy|~||}}|}~yussvz}~~zvqnlmoruwyyxwuutuwy|}{zz}{xxz~~~~||~|yvuuvwyz{{{{{{{||||{{zzz||zz}|zz{~}yusstw{}{zyz{}~|{ywvusrqqrsuwxxwtrpnorvz}}zxwvvwy{}~}|}|xvvx{~~{vrppsx|~~}{yxwvvwy{}~|{z|}zyz~}xutvx{}}{xurpooprtuvwwwvuuuvwx{}|{{}|ywy|~|yxwvvwxy{|}~~~}|zxwvvvx{~||}~{zz}}zwutux{~~~}||}~~{xusqppqsuxyzywtqnmmpswz|{{z{~}{zzz{{|}}~|z{}}ywxz|~~{xtqqrvy|~~~}~~|{yxwwwxz}}{yy{~~}}{xwwy{~~|yurpooqrtvvwvutssrstuxz}~|z{}}{|~}{zxxwxxyz{|}~~}|{ywvutuvy|}yxxz}}zxwwxz{}~~~~}~|wtqnnorvy|~~|xtoljkmqtwyyyz|}|{{{|~}}~|xwx{}||{yvtqqruy}|zwwwy|}|}~|yvttuwz}}zwwxz}|ywvx{~ytomlmpswz|}|{xuromlklnquz~}}{wvw||{|}ywvwxz}~{xuronmnpsw||xwx{|xussuy}{xuttw{|zyxxxyz{|}}}|{yvroljikmqtwxyyyz|~~||}~{yy{~|yxy|}zxxxyyyxvuvx{~|yvtstux{~~zxwy{~zvtstvy}|ywwy}~}|yvsqppqsuxz|}~}{yvspnlkkmoquy}~~zxy|}{{|}~|{zz{|~~{wtqpoopruy|~~|{z{|~~{yxwvwxy{|~~}|{zzzzz{}~}zwspmlmnpqsttuuwxyzzxwvwy~~}}~~}}|zxvuuvy|~~|xurppsv{~{yxyz|~~}{zzzzyxwvuuwz}}zwtrqqrtvxz{|{zwtqmkjknqvy|}}|{z{}~{yyyz||}~~~|{{{|~|xusrsuxz||{ywuuwz}~~~|zzyxxwvvvvvwxz|~~}{yxxxyz|~|xtqonpswy{zvrnkknrx|~~|ywvx|~~~~~}{{|}~~|zxvuvxy{{zyvutuw{~|{zzzz{||}}}}}~~~|zxvvwxz|~~{xvuvx{}~}{xusrrtvxxvtqnmmosw{~~}||}~~}||{|~}{{}|xtrrsvy|}zwvuvy|~~~|zywvutuvxz|~}|{|~}zxwxz|~~{xtqppruxyzxwtsrqqrsuwz}~~~~|zyz|~~zwusstuxz}~~~~}}|{{{{{|}~}zwutux|}zyy{|xvuux{}~|yvsqppqrtvxyz{{zzyxxwwxxz{~~}|}~~~~}~|zyyz{|}~~}|zywvutuvxz}~{zyz{|~~{yvuvx{}}}~~}zzywxywvwwvwy{}}|~~~|zyyy{}~~}zxwvwz|~{zyy|~|{|}~}{zyywvty}zxvutuwy|~~|xtpmlnqv{~|zz{~~wu~zwuuuuvxz|~|ywvvwy|}zwvuuuwz|yxxz|}{yyzz{|}}~}yvv{}{zz{|~~|zxwwxy{~}}~{yy{~|wspnnryysnkjjknquxz{{zwurqoopqsuwxyzzzzzyxwusrqqtx~~yvuvx|{xwwz{xwwy{|yvuuwz}}ywuuvy~}|}|vpkiilr{~{yyyz{|||zwtpmkiijlosvz|~~}{xvsqppqrsstuw{~zwuuw{~~|wspprx}||}~}|zzz{|}}zwvvx{{vqmjjmrz|wutuvy{|||zwtpnlklmpsvy|}~}{xurpoopqqrtvy}}zxvwxz}}||}~zwuvy~~{z{}}yvuuwz}~|zxvwx|}|{|~ysnkjlqy}|||||{zxvtsqppooprsvx{}~}|zwtqonnoprsux{}||{{||~{uqopt||{{|}zwvwx{~}|{zyyz|~}}}~||}~}yuqmklpv~{z{|~~{xurppppqrstuwxz{|||zxuspnmlmnqty~~|{zyzz|}~}}~~yussw}}{|}|yvvwy|}{{zzzz{|~}|{{|~|ytoljlpx~||}~zwtqppprsuwxyzzzyyxwvvutsrqpppqtw{~}{zz{|~~~{wtsuz~~~|zyyy{}~|{yxxwxy{~~~~|yxxy{}~~{wrmjjms|}|}{wtqpoprtvy{|}}}|zxvtrponnnopruy}~}|{{||~~~|xutux~}{zyy{|~}||{zzzz{|~~{xvwxz|}|yupljkpw~~{xvtsssuvwyz{{{zyyxwvutrqqppqrux|~|zyz{|~~{xustx}|yxwxy{~~}{zyxwwxz}~{xwwxz|||yvqmklou|}zwutstuwy{}~~~}|zxvusrqpppqqstwy}}{zzz|~~~~~{wsrrv{}{{z{|~~}}|{{zzzz{|~~{xwwxyz{{yurommpt{~{yvuuuvwxz{|||||{yxvtsrppopqsux|~|{zz{}~||}}yusrtx}~{yxxy{}}|{zyyyzz{|~~{yxxxz{{zyvrnlkmqw}|zwvvvwy{|}~~}|{zyxwvutrqqppqsvy}~|{{{|}~||}~}zvtstw|}{zz{|~}{zyyyyyz{}~~~~}zxvuvwxyzyxurommptz~zxvvvwy{}~~}|{yxwvutsrqqqrtwz~~}}||}~~|{{}~}zvsrsw{~|{{{|}~}{yxxxxyz|~~}}}~~{ywvvwxyyxvspmlmpu||ywwwxy{}~}|zywvutsrrrrsux{~~|{{{|}~|{z{|}~~|yvttux}}|{{|}~~|zxwwwwy{}~}}}}|yvtttuvwxxwtronoqv|}zxwwxy{}~}{ywutsrrrrsuwz}~}||}~}{zz{|}~~}{xvttvy}{{{|}~~}{zyxxxxy{}~}|||}~~{xvtsstuvwwvtqpopsx~~{zyxyz{}~~|zxvusrqqrsuwz~~||{||~}{yxyz|}~~}zwustw{|{zz{}}{zxwwwwxz|~}|||}~~|yvtssstuvwvusqpprv{}{zzz{|~~|{ywvusssstuwz}~}|{|}~~{yxxyz|~~}{yvtsux}}{{{|}~}{zxxwwxyz}~||{||~~{yvtsrstuvwvusqooquz|{zzz|}}{ywutsrrstvy|~}}|}}~~{yxxyz|}~}|yvtstw|}{{{}~~|zyxwwwxyz}~|{{{|}~{xvtsrsuvwwwuspnnosx}~||||}}{yxvuttttuvx{~~}}}}~~{zxxyz|}~}{yvtstw{~|{{|}~}{yxwvvwxy|~~|{{{|}~~{xusrrstvwxwvsqonoqv{~}|{|}~|zxwuuttuuwxz}~}}|}~~{zyxy{|}~}|yvtssuy~}|||~}|zxwvvvwxz|~}|{{{|}~~|yvtrrrsuvwwvtronnpty}||}~~{yxvutttuvxz|~~}||}~~|zxwxy{|}~}{xvtstw{~}||}~~|{yxwvvvwy{}}|{{{|}~~|zwtrqqrtuwwwvtqonoqv{~}}~~|zxvuutuvwy{}~}|}}~~{yxwxyz|}}|zwussux|~}||}~}|zywwvvvwy{}~|{zzz{|}~~|ywtrqqqstvwwutrpopsw|~}}~~}{ywvuuuvwy{~~}}}}}{xwvwxy{|}|zxvttux|}|||}~~|zxwvvvvxy{}~|{zzzz{|}}{yvsqppqrtvvwvtrqpqtx}~~~}{yxvvuvwxz|~}||}~}zxvvvwyz|||zxvuuvy}~|||}~~|zxwvuuvwy{~}{zyyyz{|}~~|zxurpppqrtvwwvtrqqruy~~~~~~|zywvvvvwy{~}}}}}~|ywvvvwy{||{zxvuvw{}||}~~}{yxvvvvwxz|~~|zyyxyz{|}~~}{yvsqpopqsuvwwutrqrsw{~~~|zywvvvwxz|~~}||}~|zwvuvwxz{||zxvuuvy}~|||}~}{yxvuuuvwy{}~|{zyyyz{|}~~~}|ywtqpooqrtvwwvtrqqsuz~}{yxwwwwxz|~~}}}}~|zwvuvwyz|||zxvutux|}}}}~~|zxwvuuvvxz|~|{zyyyz{|}~~~}{yvtrpopqsuvwwutrqqruy~~|zxwwwwxz|~~}}~|ywvuvwy{|||zxvttuw{~}}}~~|zxwvuuuvxy{~~|{zyyyz{|~~~}{yvtrpppqsuvwwvtrppqtw|}{yxwwwxz{~~}}~}zxvvvwyz|||{yvuttvy}~}}}~}{ywvuuuvwxz|}{zyyyz{|}~~}|zwtrpopqrtvwwvusqpprvz~~|zyxwwxy{|~~~~{ywvuvxy{|||zxvttuw{~}}}~~|zxvuuuuvwy{}~|{yyyyz{|}~~~}{xvsqpopqsuvwwvtrqpqsw|~|zyxxxyz{}~~}~}zxvuuvwy{||{zxvttvx|~}}}~~|zxvuutuvwy{~}{zyxxyz{|}~~}|zxusqoooqstvwwvusrqrtx|~|{yxxxyz|~~~}~~}zxvuuuwyz||{zxvuuvy|~}}}}~|zxwuuuuvwy{}}{zyxxxyz{|}}}|zxusqonoprtvwwwvtsrsux}}{zyxyy{|~~}}}~|ywuttuwyz|||zywvvwy}~}||}~}{yxvuuuuvxy{~~|{yxwwxyz{|}}|{ywtrponoprtvwxwvtsstvz~~}{zyyyz{}~}}}~~{yvuttuwy{||{zxwvvx{~~||}}~}{ywvuuuuvxz|~}{zxwwwxyz||}}|zxvsqonopqsuwxwwutssux|~|{zyyz{|~~~}}~|zwuttuvxz|||{ywvvwy}~}||}~}{yxvuutuvwy{}~|zyxwwxyz{|}}|{yvtqonnoqsuwxxwutsstvz~}{zyyzz|~~~~~|zwuttuwxz|||{ywvuvx|}}}}~~|zxvuttuuwy{}~|zyxxxxy{|}}}|{yvtqpoopqsuwxwvusrrsvy~}{zzyz{|~~~~|zwvuuuwy{|||zxvuuux{~}}}~~|zxvutttuvxz|~~|zyxxxyz{|}}}|{yvtrpoopqsuwxwvusrrsux|~|{zzzz|}~~}zxvuuuwxz|||{ywuuuwz}~}}~}{ywvuttuvwy{}~|{yxxxxyz|}}}}{zwurpoooqsuvxxwvtrrrtwz~}|zzzz{|~~~|ywuuuvwy{|||zxvuuvx{~}}~~~|zxwutttuvxz|~}|zyxxxyz{|}}}|{yvtqpoopqsuwxxwutrrsux|}|{zzz|}~~~{xvutuvxy{|||zxvuuvy|~}}}~~|zxvutttuvxz|~}{zyxxxxy{|}}}|zxvsqooopqsuwxxwvtsssuy|}|{zz{|}~~}{xvuttuwy{|||{ywvvwy|~}|}}~~|zxwutttuvxz|~}{zxwwwxyz{|}||zxvsqonnoqsuwxxxwutstvy}~|{{{{|~~~~}zxvtttuwy{|||{ywvvwy}~}||}~~|zxwutttuvxz|~~}{yxwwwxyz{|||{zxusqonnoqsvwxyxwuttuwz~~|{{{|}~~~~~|zwutttvxz{|||zywvwxz~}|||}~}{yxvuttuvwy{}}|zxwwwwxyz||||{yvtrpnnopruwxyyxvuttvx|~}|{{{|}~~~}{xvtttuwy{|}|{yxwvwy|~}||}~~|zxvutttuvxz|~~|zyxwwwxyz{|||{ywurponoprtvxyyxvuttuwz~}|{{{|}~~~{xvuttuwy{|}|{yxvvvx{~}||}~~|zxwutttuvxy{~~|zyxwwxyz{|}}|{ywurponoprtvxxxwvtsstvz~~|{{{|}~{xvutuvwy{|}|{ywvuvx{~}}}}~~|{ywutttuvwy{}~|zyxxwxyz{|}}}|zwusqoooprtvxxxwvtsstvy|~}|{{|}~~|ywutuvwy{|}|{ywvuuwz}~}}}~}{yxvutttuvxz|~~}{zxxxxyz{|}}}|zxvsqpooprtvwxxxvussstw{~~|{{{|~}zxvutuvxz{|}|zywvuvx{~~}}}~~}{ywuttttuwy{}~|zyxxwxyz{|}}}|zwusqoooprtvxyxxvusstux|}||{|}~|zwvutuvxz|}}|zywvvwy|}}|}~~|zywutttuvwy{}~|zyxwwxyz{|}}|{zwurpoooprtvxyyxwutttvy|~}|||}~|zwuttuvxz{|}|{yxvvwy|}|||}~~}{ywvutttvwy{}~|zyxwwwxyz||}|{ywurponoprtvxyyywvutuvy}~}|||}~~|ywutttvxz{}}|{zxwwwy|}|||}~~|{ywvuttuvwy{}}{zxwwwwxyz||||{ywtrponoprtwxyyyxvuuuwz~~}|||}~~{yvuttuvxz|}}|{yxwwxz}~||{|}~~|zxwutttuvxz|~~|{yxwwwxyz{|||{zxvsqooooqsvxyzyxwvuuvx|}|||}~|zwutttvxz{}}}{zxwwwy|}|||}~~|{ywvuttuvwy{}}{yxwwwxyz{||}|{yvtrpoooqsuwyyyxwvuuuwz~~}||}~}zxvttuvxz{}}}{zxwvwy{~}|||}~~}{ywvutttuwy{}~}{yxwwwxyz{|}}|{yvtrpoopqsuwyyyxvuttuwz}~}||}~}zxvutuvxz|}}|{yxvvvx{~~}||}~}{ywvutttuwxz|~~}{zxxwxxy{|}}}|{ywtrpoopqsuwxyyxvutstvy|~}|||}~{yvuuuvxz{}}}{zxvvvwz}}||}~~|zxvutttuvxy|}}|zyxwxxyz{}}}}|zxusqoooqrtvxyyxwutstuw{~~}||}~|zxvuuuwy{|}}|{ywvvvx{~~}||}~}{yxvutttuvxz|~~}{zxxwxxy{|}}}}{ywtrpoopqsuwxyyxwutttvx|}}||}~~|ywuuuuwy{|}}|{ywvvwy{~|||}~}{ywvutttuwxz|~~}{yxwwwxyz|}}}|{ywtrpoopqsuwyyyywvutuvy|~}|}}~~|ywuttuwxz|}}}{zxwvwy{~|{{|}~}|zxvutttuvxz|~}{yxwwwxyz{|}}|{ywtrpoooqsuwyzzyxwuuuwy|~}}}}~|ywuttuwxz|}}}|zxwwxy|}|{{{|}~}{zxvutttuwxz|~~|{yxwwwxyz{|}}|zyvtrpoopqsuwyzzyxwvuvwz}~}}}~}{xvuttuwy{|}}}{zxwwxz}}{{{|}~~|{ywvuttuvwy{}}|zxwwwwxyz||}|{zwusqoooprtvxzzzywvuuvy{~}}}}~~|ywuttuwxz|}}}|zxwwwy|}|{{||~}{yxvutttuwyz}~~|zyxwwwxyz||}}|zxvsqpooprtvxyzzyxvuuvxz~~}}}~|zwvutuwxz|}}}|zxwvwy{~~||{|}~}|zxvutttuwxz|~~|zyxwwxxz{|}}}|zxvsqpooprtvxyzyxwvutuwz}~}}}~|zwvuuvwy{|}}}{zxwvvx{~~}|||}~~|zxvutttuvxz|~~|zyxwwxyz{|}~}|{yvtrpooprtvxyzyxwuttuvy|~}}}~}{xvuuuwxz|}}}|zxwvvwz|~|||}~~}{ywvtttuvwy{}~}{zxxwxxz{|}~~}|zwusqpopqsuwyyyywvuttuxz~}}}}~|ywvuuvwy{}}}|{yxvvwx{~}|||}~~|zxwutttuvxz|~~|zyxwwxyz{|}~}|{yvtrpooprtvxyzyywvutuvx|~}}}}~~{ywuuuvxy{}}}|{yxwvwy{~~}|{|}~~|zxvutttuvxz|~~|zyxwwxxz{|}}}|{yvtrpooprtvxyzzyxvuuuvy|~}}}~~{ywuuuvwy{|}~}|zxwwwy{~~}{{{|}~~|zywuuttuvxz|~~|{yxwwwxyz|}}}|{yvtrpoopqsvxyzzzxwvuvwy|~}}~~{ywutuvwy{}}~}|zywwxy|~|{{{{|~~|zywuuttuvxz|~~|zyxwwwxyz|}}}|zxvtqpooprtvxzzzzxwvvvxz}~}}~}zxvutuvxz|}~~}{zxwwxz}}|{z{|}~~}{zxvuttuvwy{}}{zxwwwxyz{|}}|{ywusqoopqsuwyz{zyxwvvwy{~}}}~~{ywutuvwy{}}~}|zywwxy{~~|{{{{}~~|zxwutttuvxz|~}|zyxwwxxz{|}}}|zxusqpooqruwyzzzyxvuuvxz}~}}}~~|ywvuuvwy{}}~}|zxwwwx{~}{{{|}~~|zywutttuvxz|~}|zyxwwxyz{}}~}|zxvsqpopqsuwyzzzywvuuuwz}~}}}~|zwvuuvxy{}}~}{zxwvwxz}}|{{|}~~}{ywutttuvxz|}}|zyxwwxyz|}~~}|{xvtrpopqrtwxyzzywvutuvy|~}}}~}zxvuuvwy{|}~}|zxwvvwy|~||{|}~}|zxvutttuwy{}~~|{yxwwxyz{}}~~}{ywurqppprtvxyzzyxvuttuxz~~}}}~~|ywvuuvxz|}~}}{yxwvwxz}}|{{|}~~}{ywvuttuvwy{}}|zyxwwxyz|}~~~|{yvtrpppqrtvxyzzywvutuvx{~}}}~~{ywuuuvxz|}~}}{yxwvwy{~}|{{|}~~}{ywvuttuvxz|}}|zyxwwxyz{}}~}|{xvtrpopqrtvxzzzyxwuuuvy|~}}~~~{ywuuuvxy{}~~}|zywwwy{~}{{z{|}~~}{yxvuttuvwy{}~|zyxwwwxz{|}}}|{yvtrpooprtvxzzzzywvuvwy|~~}~~{xvuuuvxz{}~~}|zyxwxy{~}{zz{{}~~}{ywvuttuvxz|~}|zyxwwwxz{|}}}|zxvsqpopqrtwyz{{zywvvvxz}~~}~}zxvuuuvxz|}~~}|zxxwxz|~|{zz{|}~~|zywuuttuwxz|~~}{yxwwwxyz|}}}}{ywurqpopqsuxyz{zyxwvvwx{~~}~~~{ywuuuvxz{}~~}|zyxwxy{~}{zz{|}~~}{ywvuttuvxz|~}{zxwwwxyz{}}}}|zxusqpopqsuwyz{zyxwvuvxz}~}}~~|ywvuuvxz{}~~}|zywwwx{}}|{{{|}~~}{ywvuttuvxz|~}{zxxwwxy{|}~~}|zxusqpppqsuwyzzzywvuuuwz}~}}~|ywvuuvxz|}~~}|zxwvwxz}~|{{{|}~}{yxvuttuvwy{}}{zyxwxxy{|}~~~|zxvsrpppqsuwyzzzywvutuvy|~}}~}zxvuuvxy{}~~}|zywvvwy|}|{{|}~~|zxwuutuuwxz|~~|zyxxxxyz|}~~~}{ywusqppqstvxyzzyxwvuuvxz}~~~~|zxwvwxy{|}~~}|zyxwxy{}~}|||}}~~}|zxwvuuvwxz{}~~}|zyxxxyz{|}~~}|{ywusrrrstvwyzzzzxwvvvxy|~~~~|zyxwxyz{|}~~}|{yyxyz|~~}|||}}~~}|zyxwvvwxyz|}~~}|{zyyyyz{|}}}}|{ywvtssstuvxyz{{zyxxwxyz|~|{yxxxyz{|}~~}}|{zyz{|~~}||||}~~~}|{zyxwwwxyz|}~~}|{zyyyyz{{|}}}|{yxvuttttvwxyz{{{zyyyyz{}~|{zyyyyz{}}~~~}|{{z{{}~~}||||}~~~~~}|{zyyxxxyz{|}~~}|{zzyyzz{||}}||{yxwvuuuvwxyz{{{{{zzyz{|~~|{zyyzz{|}~~~~}|{{{{|~~}}||}}~~~~~~}|{zyyyyyzz{|}~~~}|{zzzzz{{||}||{zyxwvvvvwxyz{{|{{{zzz{|}~}|{zzz{{|}~~~~}}||{||}~}}}}}~~~~~}}|{zzyyyz{{|}~~~~}|{{zzz{{|||}}||{zyxwwvwwxyz{{||{{{zz{|}~~}|{{{{||}~~~~}}|||||}~~~}}}~~~~~}}|{{zzzzz{||}~~~~~}||{{{{{{||}}}||{zyxxwwwxyyz{{||{{{zz{|}~~}||{{||}}~~~~}}||||}}~~~~~~~~~~}||{{zzz{{||}~~~~~~~}||{{{{{|||}}}}|{{zyxxxxxyzz{{||{{{{{{|}~~~}|||||}}~~~~~}}|||}}~~~~~~~~~}}||{{{{{{||}}~~~~~~}}|||{{{|||}}}}||{zzyyyyyyzz{{||||{{{{||}~~~}}||}}}~~~~~~}}}}}}~~~~~~~~~~~}}||{{{{{||}}~~~~~~~}}||||||||}}}}}||{{zyyyyyzz{{||||||||||}~~~}}}}}}~~~~~~~}}}}}~~~~~~~~~~~~~}}|||{{{|||}}~~~~~~~}}||||||||}}}}}||{{zzzyzzz{{||||||||||}}~~~}}}}}}~~~~~~~~~}}}~~~~~~~~~~~~~~}}}||||||||}}~~~~~~~}}}|||||||}}}}}|||{{zzzzz{{{||}}}}}}}}}~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}|||||||}}}~~~~~~~}}}|||||||}}}}}|||{{{zzz{{{||}}}}}}}}}~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||}}}~~~~~~~~}}}}||||}}}}}}}|||{{{{{{{|||}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}||}}}}~~~~~~~~}}}}}}}}}}}}}}}}|||{{{{{{|||}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}}}}||||{{||||}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}}}}}|||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}||||||}}}}}}}~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}|||}}}}}}~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~}}}}}}}}~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m43.bin b/applications/SprPoP/assets/packed/MUS/m43.bin new file mode 100644 index 0000000..0aba339 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m43.bin @@ -0,0 +1 @@ +{{pɃhea||xz|ɰTq\auXSoƇ_iwY>~{i}2}iyO}9m~oymeo{_^bE~|ssOgqudl=^~yOlMKoaLPjigFWkylzcdfqOmwz~v]gd^ulpgv´pY]fgZ\yzu:Gse~``Gvdak|k\_uʫ|̲XNt_iqrrv}Tgjn[\k[f{3M{c7Hwbb`Qh[n`kyzwV~rX`msojWxÜag\/!OOnyoydt}U`x~~etR:=wDdsqikqpckVD~JMQ~D=uvVft`Vbz>]Yh̍؝edyTq_[ijl~z~{{k^|fƆgqmS]i#,sygZvjbQ`n|ìpF*(B#WF~~tWj|m}gRyzkyi}pTaa}̭so|xTWlxzmU}LQqQPUC}vvs\vZVkgbieG2SXH_zaz|]p}rȦllu|cYLp{uqIJpxl1Y8s~H=Jnh0Qyn_@Qxk?.B\I%L|uéŷkm}zv;fլ̰ars\Ilqzz6Fq9Cg||moUJmw}m>v}AZai{lO78Rb9`G}ȑܯphɩ]X{yp|mY{mߎ}}uj[m}c6FCqjVbbw[MpdNb}nVr{U:!$E;~WnZTѰuם_yǤzŀQFfydxol~rc\UQG>6tHUFAUL%T{pCLnJnQ9O`al}wRci_boskxrMyYm;=kldUKrUw[}~fLLThxdtv[e{D.645WfMBJ[Rj;Wz~cgqneND\kK^JCduueXykģkr}x~{y~|B;d?tHbtpK-%+N{~vwQNyj>Ek[ilf_iOPkiպ~pȬq~uxfpj\hl_XSH87^wxrLPoVZ{mISvpE.Xs_KXgvxqdepqp~o\Hde|]Omuvzsserl}kDQy}bI;9HhoN~ef]ryqaLFXjg[\`WV{orozqxgbr|w|ust~–aejQOZitysV#(k}xr[]~nn|UZz]pS^pqvX\wrfsrxdUws}vqpzrn{j_lwtdukopeb_^behn|x]ql]xcm}rg]QRg{zgZVRb~a{tZo]cdU]{kqqtxfvrd\XUOEKqsnOOyuzz}vlo|qdkvjdnvd_jPfjuuu}~}mtug_kyea}}i}zrrqlmso\ctb\m}xw~w}qMOyli{[Ruqri~rmxxmgkvY{um}lw_vpY[gnomntqdGh~Ynvr~|eOIXs\>Mh_djup}mqpk|ijqovrqygr|ox~tmnv~yoaOHkuTZ|sppmktthpxlnfysz^xkl~uelr\jgak}uom]Ou\QytplXR^q~WQ{olfNaz}w_`vxgyksjqj|sd]dx~mgsqzpks}|iTNXksG>b~n[X}pigkwrTDOxbt~_erwkluyiyzgcov]ysoh|jrympzxlb_cxjQyq~~j[YhkLau`huVcuublxhvzjtj^i}}Xqgmz|ywts{yqo`Vdwdgq~~|}}a^t|kvfylXw~x}~ruu|}wz~r`v]wtucJRo}|~}qbfztwfCnstxph`[\j~|\GZtlm~|nvv~x~soszi{gvrgelzzhbdd_WQMTl~{pnyvw}|pffckv_[}z\eztt|ihs|y|c`xtys}{zvxzY;epwnYcqe`i`LiqZc{pXPU~v}{mq|uvmxo\qwh|wohdeoiVcsU\wtr~xg^Z^o\EZxpkz{`soqxYZ{^P\v~rz{bjzt_e~r\NP`yh_}}}ysusqmhjwzYaypguh~eUrnzqgr|kf{}texiqzd\cry\JRl|b[tngmkj{]jtgh{~z{kyaZ_nv~~vxfg|haiwvim`ZvlZrzow{ywqkge`\bxoJYvj_swdzyx~w~u}netxhqxlby L]Z]AFAqma_Hb|cbud^w`[͘cW_|¶|u`QW`}^zfl=$pJMg@M`^u|qkϤ`VjcbkakMvzzp}z}kN^nW<$`x}rUpOIR{䦊gbʡct{qprICsPUtg|'#flmE=z69/Wp^Vgzfp|hxq]x}duRU~Q8yq6`uR~b*?dENu{e}~Faw_^Ūo}ɱtqra46sadfic%@izx_uZfP5ro~uupW`ư~bYn|Zhm,3p\GQN[h}hsI,Se;#fJkVGr+HϷƣoof~ض٪ʅXfsCJEQv͜ӺnohX;">dH0R[KiFp^Tn\TVFmNI>A^}rsMt|iRi}i;fnmzW\{{zpvyi<5ekKsZ\fsludon{eceh{i|FJqsyxWcfg|iHiqV]JWgK-TʥT6xa^tfFvGPȧxo=>ehcN.ߨ̚nKjzbQɊDZq="ekq}acVymafqnFo};*7'7"t}ġӦuaflerƗn^EeysZZxXLolzxt~q[zq{arKtO+;[qzv~Gd}EBe9!6M?;USg}S˭jF̻еoɰc@Gorrq\yw_azk>bfI;'G]X)+\2O}k]seCMMQdJnV@n҅xVK|չzKʒ}ytoj{^bf/:4nnzunmtdZWdFEiuVLWB8Ia9rXV`W{_xzkz~U҉cg8heyzC 8&^XO.InOhiXmpWczmmqtc('!-?V{}vxdťtxޚ~b~ãPBhjkQ|dWqxYYefcRZ_Re4KrqD0atoe2BoT>uoER\o{mi͹ª~^sft|}̒zϽMAqv[Oi]^Urdv?:Divggg^nxqsua>P|dk`YY)@pF/ivȆ~Ũi|y{mtf|ä~y\p]eMei=)1spwJE;tc[]slLR{|?TZQ5%b_o=kLJ~fǖiiÍ[לͤpnh|mmbkZriXj7<16\__EN`jS@a\JZROjkpeOGKjrw~obkοt\TºƬ|ètLVs|a=x[Z/\|ehNnC^kfaku]iSQqV?Z.&kK_{Zxű~^Wcn_LtmxYF}KLr{`f79twWeqvimȾOievkz\OinwJ[\Eʁ]sb'PtAO^krri}Y}zkTygwOqi`[{EwP_fJNZ}WWPlj\gƆ{yZwpLrx+3lidv|zeuvkas֖gV[XECbnS6fsRyxTiAR0GwfVv_]̔`NyَtbmkvU,wP\|u}bH}b,}aa`@)9lkyQzgfggknsG~~wgԄGk}Ɗ8SkX`}|w9h#epkN;Vjusy~kQptzkxFGySro`i`j2æ\fS^=[qS(M˒z\XXd~YJƸ{cqfYoafa!Dëù\IwTyQTm'(FuyW,gg+SͬɚUε~d̙73Ue|чVBA9B6DGC\XuPBZcMV}xej3PihI3J\חՃziZvܰrsxdMYtb[iv_U|rtbk^9HtPAC|`ihwk}jlxƜ}am}ҢqZJCbB`wborh[VO^NbQwQHIcxC1cz=U{t{raYe٤…sxYEu|qMuthgdy{ipgͨYd_hLq*Ue^bxfMTlisDzqI/ ?0@cx׏yrXl|jSxzre[VZ|w˥vkirQZw_jH;jNV8pyicJb`S_QUipWOWXd^t}~mgmtWvhX[ahr`dr_yu~znbgzLx|bSj~|te_mzznmqaweMDPgo^OPc|ufjxgx~}mznahyz}|vokw~scXX_egp\cbZxftkoxnc[kh~]~hebewtzyut{wq`Yq~nhg\kcWwihqnx}ewka{_]ie{qqyirfdu}mldYV_oqmr}mankhlcw_rgp|~wjjrptegvq{h^^gmdMAUz}ibizy{{vkm|z~whyiv_~nmlheehpcZ[ogk}cXotnt_niz^iwpkr|ixuszryrekfXd\hyxw~yx]]t^snjyyypfvyhyyW|pxxeY_ofUcyyjjyywitiwn]Pa~rgoj[zv{xf[W\dhc_iqcU|piypzpptupbiz~}}euΚr_g^p]zkbv?LF$P|tnof\DLu`GAvoDywtqpp,bmnR}lk0tsnt{eBayqOPoQgyii(`gWy_DbPRxWxouqQrsrLr¡n|n[_=f~fitmW_[fidIp[y^qlڴh1k$xz0[{}4]nKLRNnj~ágTkvt8i]IUkmjfY{qS,[nZzqk{jeUaKAOAotoh>ɢXK~U`mq[{;ygZ{{{wuyUE~°tl\Z]z~~ows~mxfazi9;|xOV94>M~p|{̰rtxouΆs}`_78Peze/Znr~kvdJjQN?\yc8beu׾U}q͜}{{mJrkW28W]Jc`8^}[e}Xf|yxMdbSlebxx̢q}zmtu|NsXrbSZuwC9R]k}uIJxjScWUwCqvs}zgif|ltzodk{e}^vyr]M_i^dp}wncUzkuobiHWq\l{uyvmqzkzhtw}{pbro]Qmogjbj{lxpRGZg{juqnoxpcRwq]Cy}j_YffQX}qoke}7Lztuyths{oeWryRG]ee|i}bH^lYpayrolf[Nbti`{jjp`yȵcrovwJclYS^yrF;egJMYg\PTnef7Xϛnqnpklm_hija~kzr[`^XolywsbfVm_lhwug{{ggsh]vsvY_{kNU}~[Mu]Vxl|}Q4_ussltjmpkjzmpr`_besdjuciy~mdtv~{tegkFixkp|vqppj}o^tde_z~ZBGQGKe~x`cFgr~Z`nq{|]wty{tqxttyQnvm~r|gjvXgba}yg\WVwrgqq_ziuu\]Gjyn{f^YwhIEfpYR{{u|iY4G\|~}Ʃrmx~tz}\pOiaIyxql[[KLU^zrf{}`nhl_o|~w{ehsauk~n{_OmeUlhMgdggRhqZS]ymn~xrlzzv}¬xqum}~n}kRYxt]\dx~h`@]ih|iz~qsqv~qw~qnmaTZ~wsh@BW`ec^gzqwe|trmo}pKgwz~uijortpukD~]{sciy{v{vnoj{zp~pudD`~ojoYerZe\pod®yfozc_mcF?Xx\IZqkvb_sx[;kojind~obwgqZiUppTirXY_emqy}~|cQUezmk|nqosrZm}iTl|}zly^Wrnl`diWdkoaqfpyuejrvr{som~ydT{bjx{kZa}sKDYaNap|{otw{{uuntZwcabseIUK14GTY[cq}syrgjtywu|sUi{uw{t`Qb{cvl|gyhcv}ripfelbtudsua_Z`YkrVGWRi{v~~zzwtm_YdrspolwxumkrmZ?oqn{o|p`\_^]igWiQFdO?WrdctS>M`qrxtwsmpw}{~yM_ahxkamolfwrwfP^~dVFRL?M_msjq|rbXcwt~ɨ|lijw{dn{k]_szhURg}z{hbswdunzxn~}]`igWHJd]JZWIQfsmbY^}Yenw^Gio^^d`YXruc{k}qmqwm^dsmuwfnpekjsjt^V_vcZgmjxmuq^SNRizxtnkhXa`LLNXmyhuyvqrx}dcjhdj}zskMhnUSbmRFZKB^z{|w~xuqqrigq~lmlvp[sqhsttxwsns}pUwyp|u^]em~pW^_XfvwPi|htnebcefbrétpwrWUowp^hhbcrfJC\s}rXVmzpuu~~gynjyhd]U_T.5JIKX\WX[o|}adhlblmocVZ{|~pthsyywrsjjrgixzl~dq|n[V_p~cMOVluikxzlelx|gV`s~mdfXSTWke_qno{eONU]k}{xoBMeTDI^o]N`WMev^wznu|~^Jjrdiqtob[g_i~lq{bON^jy}^QTPVbst\fVx|n]KARn|l˿~{{fhy~\gmY[dgkwp]X_lq{}wvtrzbecp}itw}t^nzdpmaTBAQC7YqnnnjdX\||fwpzrau~iYVSQaouys|lb[apqrz}luxg[V\ililkx}uuz}snu^Waqn`_mwkULC=>BSy\n~~s~{p|f`irvo]ZtxUfvgYXbl\>CJR^ow[KX~Ƶ{slfegjhb_bjledoxoq~thfqrggnrttuwxxy{}~miovywqljnxuow|mcafq~{trpjccm|uaPOciUVf~|pbYX_gmpomllkjhhjntuoihkmnorriZOMT_jqssu}|yxzqaWYcmssqprw{~yqmp~zxukilquurptz~xtpoqyz|yusu|wrw{tmihjmonmptrfXT]ly}wy}sic]WRSXYSJEEJR\hznNE\{uqnmlhb^_homffpz{xkcgttkkoqppsy}qghloomkms|{oiq}vkfgjpyzuqmiipyzodZ[ko]Yew{od\Y[bkqspnjgddhowytkfhlmnpqndVLKQ\djnt~}pdbfknonnptxzyx}~vnlryik{xja`djoruy~}|}|tsuwxz}~zursz}x|ytrstusrtwuk^X[eovzz{~zy|}vnhc_]^_[SLIKQZdo}tXQhz~ztqomljjmtxtnpz|skgjs{{smmqtuuvxzz{}~usuwwuttw|~yqoxzqnnnosy~|xsnjks}|skebhwydX\j}ypic__emstqmjhfginrqkddhkjhghgbZUU[ckqu{ykbbfloqqrsvxwvx|xqkhly|igvwkcachmrv{~~}yrnoruvwxz|}~~}}{xx}{xvwxwspopoib_`fmruvvw{|uqqstqlfa][\][VTUY`ithUZvzx}|ywuqmkmtyxsqwwpjilqsqnpsvvuuw{~|urrrrrtx|~yplr{}unkmpqruz}~{vqnpwymebfrzd[_m}zrlf_[^fnqplieddhotsmggkmlihiif`ZX\biov~xjefjmnnnoqtutsv}~ysmhkukaj}shb`bglqx~|zxupmlnopsvz}~z{~|||zwrponlgcabeikmoqv~~xsppqrokfb`ab`[XY^enxm]^sytty}zwsru}|y}~ytnlknqrporw{{zz{}zwvuvwz||wokpz}xqnorsrrsx|}{wstx|pgdivk\Zet}vojd_]`fmonkgedgkprmfcglmkgffgfdbadjpv}{ztidegiklnprsssrv}~yupkgfirmck}}pgbadiou|}xtplijlnpruxz|~~~~||zxtolkhfeddegiklorx}}wronnonkgdccb_]]`gnxwf`jzsps{}yuuy{{|wrnlmopnjjpw{|zy{|~yvuuwz||wpos{}xrnortuuvy||zzxlcagvp`\cq|vpjd__afjkifcbdhmpnhdfknnkhgghhgfgjnt{{~}rjhikllmnoppppqu|~yuqmihjq|}hcm}qhcabfkqy{vqmighjkmorux{}~{wsoljhggffffghkou|{xz{wsolkkkkifeeeecbcgnvqdcrzroqv~zxy~zvrnkkmppmijpw|}|{|~|{{}}wpnt{|wqopsuuuuwy{|zyzvjccl}k^^hvysniebbdhjkigeehlppkecglnlifefghiiknsy}|vmihijklmnppppqtyzuplhfhnykdl}|pgbabflsz{upkgefhjmoruxz|~{wsokhgfgffeefgjouz{xtsw}|wspliggggfeefffegkqyvhcluompv~~~}{xuqnllnqrniglt{~}|{}~~~ysrv|~yspqtvwxyz{}}qhcdnma`iu|vpkgdcdeghgfdehlonieejnpnkihijlmopsw}}}}tmjijkllmmnnnnpty~wrmjgfhnyqfivxngcceinu{~~~|yupkgeegilnqtwy|~}ytpligfgggfeefhmsy{ytsv|}yuromkiiiiiiijiijlqywibi{unlns{~|zwtpmkkmprpkhkrz~~yspsy|ytpprtvwxxyz|}~wlfdlzvf`ep}}wrlheddefgggffhloplgdgloomjihijmorvy}|~yqliiijjkklmmmnquy~zsnjfefjq|ogkx{qjedehmsx}|{|||{zwtpkgedfiknqtwy|~~zvrnjgffgggfeefjotxwropv{|{wtqnljhgfgghikklmot{}oder|rmkmsz~{ywtqnljknqrokhkrz~~~{usw|~|wsrsvxyz{|}~~sjeem{zjcemy|vqlifeeeeeeefhkopmhdfkpqpnlkklnqtwy}~~|uokjjjkkllmmmmorv{~xrmigfhmuzkgo|xoieefjoty}~{{{{zyxurokgeefhknpsvx{}{wrnkhggghgfeegjouwuqoqvz|zvspnljihghhjklmnorw}zldgu{qljmsz|zwurpmkjloqrnjhlsz~|wvz}xuuwy{|~~xohfisviehp{}wrnkihgggffghknqplgfinrrpnmmnortwz~~~xqlkjjkkkkkllmnptx}|vpkhffiowykgmz|slhffimrvz}~|yyyyxwusqmjgeefiknqtwy{~{vrnkigggggfefhlpturmlotxxvspnljihgggijlnpruyrgenwojjmsz|zwurpmkjlnqrplikrx}|xwz}yvuvxz|}~wnhfjuvjfjr||vqmkihhggffgiloqokggkpssqonnoqsvy}|uoljjkkkkklllmoruz~~xrmiffgmw~ogkuzrlihikoswy{|}~}{wvuuutrqomjgfeghknqtvy{}~}yuqmkihhhggfghkostqmkmrvwvspmkjihgghjlnqsvy}rhel|{rljkou{|ywtromkklnqrqmjkqw|~zy{|xwxy{}wpjhjt|ohhnw|vrnlkjihgggilorrnjgjnsttrqpqrtvy}zsnkjjjjjjkkklmorvy}ztojgffipx|ngkvzsmjijlptwz|}~}{xvvuutsqomkigggiknpsvxz|~~zvrnljhhhgggghknrtrnklptwvtqomkjiihijkmprux|xmfhswojjlqw}~{xvsqolkkloqrpljlrx}~z{~|zz|~}unihluqjimt~zvromlkjiiijmpssplijnsuvutsstvxz~}wqmkkkkkkkkkklmpsvz~{upkhgginu~vkhny{toljkmpsuxy{|}~}{~~yvtsssrqomljhgghjloqtvy{}~~zwspmkjihhhghilorrpljkosutromkjiihiikmpruy|ukgkwwokjlptz}zwuspnlklmprrokjmsy}||~{zz|}{sliioyzojjpx~yuqomlkjjjjlortrokjlquvvutttvwy|ztolkkkkkkkkklmoruy||vqmiggint}xlhmx|uplklmpsvxz{|}}|~~yvtsssrqomkjhhhijmortwy{}~~zwspnljjihhhikmprqnjikortspnlkjiiijkmpsvz}yohhq~unkjlpty~}zwurpnmllnprrpmkmrx|~~~~|}~wqlikq{zokkpw{wtqpnmlllmnqtusolloswxxwvvwxz|}xrnlkkkkkkkkklmpsvy|{vqmihhjnt||oiksztpnmmoqtvwyz{|~~{y{}xurrqqponlkjhhhikmoqtvyz|}~{xtqnmkjjiiijknqsrokjkorttrpnlkjjjklnpsvz}{qiho|wpkjlosx}~zxusqomllnprrqnkmqw{~~}|}ysmjkpyslknu}}yurqonmllmnpsutqnlmquxxxwwwxy{~|vqmlkkkkkkkkklmpsuy|~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m50.bin b/applications/SprPoP/assets/packed/MUS/m50.bin new file mode 100644 index 0000000..00a7ce1 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m50.bin @@ -0,0 +1 @@ +}~}}~||||~}~}}|~~}~~~~~}~}~~~~}~}}~~~~~~}~}~~}~~~~}~}~~~}~}~~~~~~}~~|~}~~~}~~|~~}}}|~~~~~}~{~}{z{~|~~~{}||~~|||{~}~~~~z{|yxx|z{}|~{~~{|}~{}{{}}~zx{wvvxxw~~|~z~}z|~{{|~|~~{{|}~{wywtuvwuz}|{{~y{|}{z~{z|~}~}|{}~~~~{wwxsttvtv|z{x|yy||{y|}y||}}~{~~~~{xvxtssttszyzxzzx{{{yy}y{{~}~~|~~~~{yuwurtsurxyyxxzwy{zzx|zz{|}}~}~~~|zuvvrtrurvyyxwzxx{zzx{zy{{~|~~~~|zvuwrtrtsuyyywyxw{z{xz{y{{~|~~~}zwuwstrtstyyywyyw{z{xz|y{{~|~~}{wtxststssyyywyywzz{xy|y{{~|~~~~}zwtxttsttsyyywxywz{{yy}y{z}|~~~~}zwtxttsutsyyywyywz{{yy}y{{~|~~~~~~}zwtxttsutsyyyvyywz{{xz}y{{~{~~~~~~}zvtxtttustzzyvyxwz|{xz}y{{~{~~~~~}zuuxsstvsu{yxvzww{|zw{}y{|~z~~~}~}ysvxssuvrv{zwv{vx|}zx}|y{}}z~}}|xswwssuvsx|yvw{vy|}yx~{y{}}{~~}}|wsxwssvusx|zvwzvy|~yy~{y{~|{~~}}|wsxvssvusy|yuwzvy|~yy~{yz~||~~~}}|vrxvtsvvty|zuxzvz}~yy~{yz~{{~~~|}{vrxvttvvsy|zuwzvy}~zy~{yz}|{~~~|}{wrxwttvvty|zvwzvy}~{y~|yz}|{~|}|xrwxttvwsx|{vvzvy}~{x~|yz}|z~|}|yrvxutvwtw|{wuzww|~|x}}yz|}z~}~|}{ystyutvxuv||xuyxv|~~y|~zy{}z|~}}}|ztsxvtvwvt{|yuxyv{~z{{y{}{{~~}~|}{vrwxtuwwty}{vvzvy}{z~}y{||y~~}}|xsuyuuwwuv}|xuzww|}z}yz|}y}~}~}|zusxwuwwvt{|zuxxvz~~{z{z{}{z}||zvrwxuwwxuz}{vwyvy~}z}z{|{y~~||zwruyuwwxuy}|xvywx}~z~~z{{|y}~}}}{xstyvwwxvw}}yvyww|{}z{{|y|~}}}{xtsyvvwxvw|}zvxxw|{|zz{}z{~}}}{xtsywvwxwv}}zvxxw|||{z{|yz|~|{xtrxwvxyww}~{vxxv|}|{zz|zz~|}|{xtrxwvxyxw}~{vxxw|||{z{|yz|}|{xtsywvxyww}~zuxxw||}zz{|yz|}|{xssywwxywx~~zuyww||}zz{|y{~~|||zwrtyvwyzwz~yuywx~|yz{|x{}~{|{zvruyvwyzw{}wuyvy|~yz{{x}}}|{{zvrwyvxzzw|}vvyvz~|}xz|{x}}|~~|{yuqwyvxzyw}|vwyv{~|}xz|{x}}|~~{{yurxyvxzyx~|uwyv{~}|xz|zx~~~{~}{zytrxxwx{zy|uwyv|~}|yy|zx}~}{~}{zyuqxywy{zy}vwyv{}|xy|zx}~}{~~{{yuqwywy{zy~~vwyv{}}xy|{x|~}|}~{{yvqwywxz{x~~wvyvz}~yy|{x|~}||{{zwquzwxz{x|xuyvy}yx{|xz~~||z{{zxrtzxx{|z|zuywx~zxz|xy~~}~}z~|{zxsryxwz|zz|uxxw}|xz|zx}~}}~z}~~{{yurwzwz{{ywvzv{~xy{{w|~~||{|{zwrtzxy{|z|zuywx~yyz|xy}~}}z~}{zxtrxyx{||{~vxyw}|xy{zx|}}}}y|~~{{yurwyw{||zxwyw|~~xy{{w{}}|~zz~{{yvruzxz|}zzvywzyyz|xz}~|~{z~|{zwstyxz{}{}|vyxyzxy|xy}~}~|y~|{zxtsyxy{}{|}vyxx{xy|yy|}}~}y}|{zxtsyxy|}|}~wyyx{xy|yx|}}~|y}}}{zxurxyy|}|}wxyx~|xy{zx|}}}|y}|}{zxusxyy|}|}~wyyx{xy|yx|}|~|x~}|{zxtsyyy|}|}~wyyy{xy|yy|}|~~|x~}|{zxtsyyy|}|}}vyyyzxy|yy|}|~~{x~}}{{zxttzxz}~||vzxzyxy|xz}}|~~zx|~{{zwsuzx{}~|zwzx|xxz|x{}}|}yy~|~{{zwrwzx{~~|xwzx}~wx{{x{}}|}y{~|}{{zvrwzy{~~}xxzx~}wx{{x|}}|~}y{~{}{zzvrxzy|~}}xxzy}wx|{x|~||~|x{~{}{{zvsxyy|~~wxzy|wx|{x|~|}~~|x{~{}{{zvsxyy|~~xxzy}ww{{x|}||~|x{~{~{{zwswzy|~~~xxzy~}ww{{x|~}||xz~{~{{zwswzy{~~}yw{y}~ww{|x{~}|}yy~{~{{{ysvzy{~~{wzy|xvz}yz}}|~}yx~~||{{{ztuyyz~~}wzz{zvy}zy}}|~}~zw}~}{|{{zvtyyy}~wyzy|wx|{y}}|}~{w{}~z~{{{wswzy|~zx{y}wwz|x{}}|~}yx~~{}|{{ytuzy{~~}wzz{ywy}zy}}|}~zw|}|z}|{zwtxyz}xy{z}ww||y|}||~{wz}~z~~|{{xtwzy}~zx{z~ww{|y{}}|~|xx~~{}|{{yuvzy|~|x{z|ywy}y{}}|}}yw~~}{{||{zvuyy{~~xzz{zwx}zz}}|}}zw}}|{||{zvtyy{~~xz{{{wx}{z}}|~}}zv|||z}|{{wtyyz~xy{{{wx}{z}}|}}~zv{||z}|{{xtxyz~yy{{|ww||z}}|}}~zv{|}z}||{xtyzz~yz{{{wx}|z}}|~}~zv{||z}||{wuyyz~xz{{{wx}{z}}|~}}zv|||z|||{wuyy{~xz{{zvx}{z~}|~|}yv}|{z|||{vuzy{~x{{|yvy~z{~}|~}|xw~}}z||||zuvzy||x{z~wvz~z|~}|~}|wx|}~y}~|||zuwzy}zx|z~ww|}z}~}|}~~{wy|~}y~~|||yuxzz}zy|z}vw|}z}~}|}~{wz|~}y~}|||yuxzz}zy|{}vw|}z}~|}|~zvz{~|y}|||yuxzz~yy|{|vw}}z}~|}|}zvz{~}y~}|||yuxzz}zy|{}vw|}z}~}|}~~zvy|~}y~~|||zuxzz}zy|{~vv|}z}~}|}~~{wy|}}y}~|||zuwzz}{x|{wv{~z|~}|}}~{wx||~y|||}|vwzy|}x{{~xuz~{|}|~||xw}~}{zz||}|wvzz{x{|}zux~|{~~|~|}yv|~{~{y|||}yvyzzyz|||vw}}z~~|}|~zvz{}|y}~||}{vxzz}|x|{wv{~{|~}|}}{ww~}|~y{||}|wvzz|x{|}yux~|{~~|~|}yv|~{~{y}|}}zvyzz~zy||}vw|~{~~}}|~~zvy{}|x}~|}}{vxzz}|y||wv{~{}~}|}}~{wx~||~y{|}}|wwzz|~x{|~xvy~{|~}|~||xv}~~{zz~||}}xvyz{y{|}zvx~|{~~|~|}xv|~{~{y}}}}yvyz{yz|}{vw~}{~~|~{}yv{~{~{y}|}}zvyz{yz||{vw}}{~~|}{}yvz{}{y~}|}}zvyzz~zz|||vw}~{~~|}|}yvz{}{y~}|}}zvyzz~zz|||vw}}{~~|}{}yvz~{~{y}|}}yvyz{yz||{vw~}{~~|~{|yv{~{~{y~}}}}yvyy{yz|}{vx~}|~|~~{|xv|~~{~zy~|}}}xvyy{x{|~yuy||~|}{~{wv}~}{~yz~|}~|wwzy|~x||wv{|}}|||~{ww~{|}y|~~|}~|vxzz}|y||~vv|{~}||}}zvx{|}x}~|}~{vxzz}{y||~vv}|~}|{}}zvy~{||x}~|}~{vxzz~{y||}vw}|~}}{~}yvy~z}|x}}|}~{vyzz~zy||}vw}~|}}{~|yvy~z}|x}~|}~{vxzz~{y||}vw}|~}|{}}zvy~{|}x}~|}~{vxzz}{y||~vv||~}|{}}zvx{|}x|~~|}~|wxzy}|y||wv||~~|{|}zvw~{{~y{~|}~}wwzy|~x{|xuz}}~||{~{wv|~}{~zz~|}~~ywyy{y{|~zuy~}|~}z~{xv{~z}{y~~}}~~zvyyz~zz|}|vw~|}}~{~|yvy~{||x|~~|}~|wxzy}}y||wv{|~~||{}zww}~|{~~yz}|}~~ywyy{y{|~zvy~}|~}z{xvz~z}{y~~}}~~{wyyz~{y|}~vw}}}|~{}|yvx~~{|}x|~~|~~|wxyy}}y|}wv|}~~|{{}zvw}|{~yz~|}~}xwzy|y{|yvz}~||{~{wv|~}z~zy~}}~~ywyy{yz|~{vy~}|~}z~{xvz~z}{y~~}}~~zwyyzzz|~|vx}}~}z{xvz~z}{y~~}}~{wyyz~zz|~|vx~}}}}z~{xuy~z||x}~~}~{wyyz~{y|}}vw~}}}}z~{yvy~z||x}~}}~{wyyz~{z|~}vx}}}}z~{xuy~z}{y}~}}~zwyyz~zz|~|vx}}~}z~{xuz~z}{y~~}}~zwyyzzz|~{vy}|~|z~{xv{~~z}zy~}}~ywyy{y{}yvz~~||z}zwv|~|z~~yz}|}}xxyy{y{}xv|}~|{{|zvw}~{{}y{~|~|wxyy|}y|}ww}}~|~z||yvx~~{{}y|~~|~|wxyy}|y|}~ww}}}|~z}|yvx~~{|}y|~|~{wxyy}{y|}~vw~}}}~z}|yvx~~z||y}~|~{wyyy}{y|}}vw~}}}~z~{yvy~~z||y}~|~{wxyy}{y|}~ww~}}}~z}|yvx~~{|}y|~|~|wxyy||y|}ww}}~|~{||yvw~~{{}y||}~|wxyy|}y|}wv|}~~|{||zww}|{~y{~|}~}wwyy{~y{|yv{}~||{}zwv{~}{~zz~}}~~xwyyzyz|~{vy~}|~}z~{xvz~z}{y~}|~~zvyyy~zz|}}vx~}}}~{~|yvx~~{|}y||}~|wxyy|}y|}xv|}~~||{}zwv|}{~~zz||~}xwyyzy{|~{vy~}|}{~{xvz~{}{y~~|}~zvxyy}{y|}~ww}}}}{}|zvx~{|}y||}~{wxyy|}y|}xv|}~~||||zww||{~~z{|}~|wwyy{y{|yvz}}|}{}{xv{~~{~zz}|~}xvyyz~yz|~{vy~}|~{~{xvz~{}{y~}|}}yvyyz}zz|}|vx~~|}~~{~{yvy~{}|y~~|}}yvxyy}zz|}}vx~~|}~~{~~{yvy~{}|y}~|}}zvxyy}{y|}}wx~|}}~{~~{yvx~{}|y}~|}}zvxyy}zz|}}wx~~|}~~{~{yvy~{}|y~~|}}yvxyy}zz|}|vx~~|}~~{~{yvy{}|y~}|}}yvxyy}zz|}|vy~~||~~{~{xvz{~{z}|}}xvyyz~y{|~zvz}}~|}|}{xv{~{~z{||}|wwyxz~y{|yv{|~~||||zww}||~y|||~{vwyy{|y|}ww}|~}}|}|zvw}||}y|||}zvwyy{{y|}~ww}|~}}|~|zvx~||}y}||}zvxyy|{y|}~ww}~|~}}|~~|zvx~|}}y}~||}yvxyy|zy|}}wx~~|~}}|~~|zvx~|}}y}~||}zvxyy|{y|}~ww}~|~}}|~~|zvx~||}y}||}zvwyy{{y|}ww}|~}}|}|zww}||~z|||}zvwyx{|y||xw||}~}}y}y}xt~|{{~x~~{~|{xvww|}wz{{u}{~{}z}yt}~~~}}}~y|~{{zytyz||x{|}}u{~||{}~zxv|~}|~~|~y}}ztxzx}xy{z|x{}~}z~}~}yzx||~}|~z}z}~yxy|y~~}y{z|xz{|{z}}|wwty~}}~~~|zyvvx}{{y}~yuu}}||}~|}~{{xvy~}zx}{~{yvurwy|~z~~}xw~}{y~zx{z|yz{||{wzywuy{|{vxy|z{~~}{z||y|{}|~|}}~||z||xyz}|zyx{}|}{}z|zy|~ytvv{~|}}}~y{xwx|}|~|z{xwvz}|}{}{zwwy}~{z|~}|yuuttz}~~{||xx|}~}y~}{x|z{{x{}z{xwxvvw}~zww{~y|~~||~||x~}}|}}|}{}|zz|wyz~wwy|z~~}z{zx~}wtxy{}~|~}}x{zyz~}~y{{~{vyy{}|{{wwwyz~~{{~}{wtvtw}~}}{}zyyz~}~~}||z{z{|z}yy~{yzuvwwu{}xv|~~{z}}~}~}y}~}{{~}y}{||zw{ywz~vuz~z}}zz|x}vuz{y|}~~|{|}wy}{{}~}uz~yuz|y}}zz~~vty|z~~}|{~|vswxx|{~}zwz|z|}|{|}}uy}|~{y|{yyw{utwxv{}|xx~z{}|~{}~~~{}||{z~}z|yz|zux{x{|zxt{||zx||~}xvz~{y~}~z~zz}yx~|||uvzvy~yz||z~||ysy}}{~|~}|z}ytt{z||y|~vx}}y~|~z~~xt}~~{y}||}wwztuyzy{{|{|}v|~~{{zz}~~z{~}{~xy{xsy{z}wxww|}}~yw~~~|}zxz}y~~~~x}}yy|zx}|}}zvw{xy~zx|}}{xyuy~~|~|~}}x|zut{}{|x{}}ww~~yz}{~~}yt{~~|x}|~xuyvsy|{|{x~~~~vy~}{{~~zy}||}}y}~}|}~{wzzut{|}yuyx|{|}|xz|z{}{y}~|z~zx}|xx|z{~~zzyv{}{zz~xy}ywyv{~~~|}}~{x|yvv}}|}~zx}z{wy~}yz}}|{xv}}|y~~}}xuxutz}|}yy{w{~}}||~~yz~}{|}y}~~{|yvzyuu}~~~vuz{|z}}{y~|~x}~|z~|z}yx}zxx}{{wyyz|}}z{|w{{uxzy~~~~|{}xy|yx{|{z{y}yyzy}zx|w{|v{~}z}|zwtxuw~{~yy~|}yx{}~~{{z|}{||}~|{yyyxxtz~}vx|~~y|}~}}x~zz}~{|~~|z{|{yv||}~xy||yzz~}yzv}zw~}}||zwxzx||~y~|~}uy~{z|x|{v{}~~~|zuruwz|}}zyx}|~}~}~||~yy{~~}}{yxttuy}xy}~}{~|~z}~~xx~}}z~~}}yxwvwx~|yw{{}~~}}{||w|xsx|z}}~|yvuw{}z||w{{}|y}|~}wx{swzy~~|{wtsu{y{}|zx}~z{}}|x}vu{{}~}zvttuxyx~|vv}~|wzwyzz}~}}yttux{y|~}vry~}xuz{{yyvz|~~~~ywxwyxx~~|ss{}{}vxzyxxvv}|z}}wwxwxtw~~{uw{~~z}yw|{}~zxuz|~{|~zuvvwvty}}wuyx}}{z{|x~{w}}y~~z{~wvxvwvv|~vuvv~}|z|yw}zy|yx}|~}vyyyzww{}}wxuv~~~yy{xy}{yyzvz}{~z|yxzvvz{|{{vy~~~}{xz{x|~|{zxv}{z}}}|}xxxtvzz~~|uy|z}{y|~z}{y~~~zw~y{~|~||vyxsw{{|uxxw}x|}{}zw~yw~}~x}}~{w{xv{{y~}|vyuv|w|{z|wx}wv}}}{}|}{}vv}zy~|~}}|z{uyyv{z{|w{~~wx}|||{~|{}|ux{vy|z|}}ys|wx{z}x}{~y}~{{}}z~{{~xt{zv{{||~ws{~z|zzzy~~|~{~||~{{|}~~vx}yy}y}|}~~tv|{~{}y}~w{~{}~~y||~|{}x||w{{x}y~~{v{||}}zw}zx~}|~}|y~}}|||zz~zx{vy~yz~{y|y}~~xz}y}|~}}{}|~{~yy}|w{yu||y~~{}zw~~~z~{z~{~~{~|~~~}|z|{{~yy}xz}xz}|}|vy~~{||y~z|~}}}|z~{}~{}~y}yw|yw}}|~}zx}}|}xz}{~|}}y|{zz||wzyty}{~y{{|}~yw~}}~{|||{||{|wyztv||~|{zx}x}~~}|}~{~|z}z~~{}xx|wu{{}~~{{v|yz~~~z~}~|}|x|z}}y|yswz{}}}}xx}y}}}{{~{~~||xy}}~~y{{ttxx|~{~w|}{z{}|~~|}{|zwzz{~||~{x~yqzxwz{~w|}xz~zz}}|w~~~zzy|}|}~y{rpxvy|}xxw~}~}{~zxz}}z{z~~~x}zv~wszxtz}zqs|}~||{|y~}yy}x}|~||ru|wx{}{}{~vu{}{{}vwzz~zuu|}|~~}~{uy{t||xx~}sw|}~{x}~|}zxxyx||z{}y~vz{uqwxv~~vxyy}{~{{{zz}wy~|||y}{|svysyzuv||wut|~~}x{}~~||}x~~~~x{{{wr{zvy~yxvt{~~{}{wv{{}~yvw}}}|{|}yvzxs{z|vs{xx~yy}{y|||wy}||||}}v{{tqy}y~~yvy{{~y~xz{{|}wz~|zz|zsvxrx}zy|y|xwv|{|{}}y|~{{|z~}}{}yzzvr{}z|~{vvvv}~y}zwv{~|~xy||~}y|{wu|zy~{|}}~xuu~|vy}xz|{~{{zw~~~|{}xv}{uu}z}|~ywv~wz{x||}{~xx|y~{tszww}{x~wzz{|}w~|{|{|~z|||~}|||}xwzyuz~z||zttxz}z{|{xyz~|z|~{{v{|zw|yyz{|zzwv{~{wy|w|~x||y}}|y|xz}yuxz}z||xy}{|}w}yx||x{{{}}yuwzuz~zx~~xw~~~xx}{}{|}{||}{z|yx{zuv}z}}zuv{z~z~~xzy~~z~{|z|{v{}yv{|w|z}w{{wz{}xxzzxy~y~}}{z~xw~zuv||xz~zyyz}x||w||~yy~}}~{~ysv{uy~|x~{~zv~}xw|{~~~~~}{~|z{}~y||uz{us}|}~}|ut}~}y}z}|z~|x|z}~|~~vv}zvx~y~~z|xw{{zx~|x{}yyw}|{}}xz|ty|yv{{}vz|{|{v|~z~yx~z~|x|}|y}}wrvyv|{z}z|yxz~~z{~{|~~}{||{z}|wzztxyuv|~}ztu~||~|y}}{~~~x~|}||~|vt{ywyz||z}wu{}{z}wy}yyu{||}}xx{ux~{wy~~|tw}|~}x{z|~zw~w{~{z~~~yy}xst{wy{}xy{w~{}|x~zz~|w~|}}|~xvzuuzyuz~{|}wt{{}|~w~~~|~|z~~}}~}z}ytv{yx{|zxztw~}z{vz~{~yw}|}}|zwxzvx}z{}|zsx~~}xz~yz|y{{y{z}}v|~wps|w|y}zwwz~|{x}yx~~}}|y~z|{ux{tquzu{}~z{z|z}||z}zz~|~}~}~w}~z{~w||tsv{yz~~zxvx~~}{|}{v|~~{}}uy}~}~z}~xutx}{|}z~~ztx~||x{}xt~}z{~zv}{z||vrvzw|w~{wz|}yyw}wu|~|}}yy~}|~{x{ysqy{u}zyx|z~}~yxzw}~|~|{{}~z~xx~xrs{|x}{wuy}|~{z}}x}||zy||w{zutyz{{y}zvw~|}y}|yt}~|}z}|wz~~y{|zwtxyx}}~x}~|w{~~z~zw~~wt{{|zw~||zzzxsqzzw}zyu|z}~wyxy~}||z}}{vz|xpt}{{{{}vu~}~|y}{x|}~~~}}z~}~~}u~}xrx|x}~x|yxx}|}~|{~}yw{~|}||{x|}yzvtxzw|~|z}}zzz~{|{z||{vz~{|~~xx~{}{~xusvzy||~||yxy}|x{}x}}}~|z{}}{z|yvqu{z~z}|ww}}y~z~{w~~|z|~|x{{xsw|x}x|{vy~{||yzv}~~~{yz~y}|vsx{w{}x}ywz}}~{x~}zw{}}}}}zx~|}}y~{urvzw|~|z|yy}~||}y|}}y||{~}y}}~}x|zvssyy|~||}zw{}~{{{}xy|~{x~yz{yvtz{z}}z~~zu{~{~|z{xw~~~{w|}|z}{uqz|x|}y~yv{|~|{y~~yw}~~}~|yy}}w~|vqv{wz}y}|xy~|{~}y|~{x|}|}~|y~}}v{|wrtyyy|{~zw{}{|{|{~zw|}}x|~~}yy{yuty|x}z~zvx~z|~x~{v}~|}yx~~}y}|vrv}x{~y{{vw||x|{u{}{~}zw~~w{~xru{wx~zz~yw~|}|}x{|wz}{}~|y}}wy}xrtzxx}z~yw|}{}|z{~xx}~~}x}}}wy|xtt{{x}z~xuz}{~{yyw~~}}}x{~yz~ztsy{x~|x~yuz~|~||y~}xv~~}||x{~zy}{usyzv||x~zwz~{~|{x~}yv~}|~|y|}zx|ztryzv}~zyw{{}|{yzw~~}}y{~~|xx}ystz{x~|zyvz}z~~|y~~zw~|~y|~~xz~zttzzx~|y~}xvz~|~}|y~}xw}}||x|~~x{|ytuzyx}~{y}yw|~||}zy~}wx}{}~{y~~x{{xsu{yy~{{}xw~~}{~~x{}w{~|{z~}~}w{zvrw|y{~y|{wx~||~y}{w}~}~~{{~~|w|{wsx{x|}y|ywy}{}|z~~zw}~}~}{z}|y}zvsyzx|~|y}~yxz}|~|z}}zw|~|~}yz}{y}yusxzx|}|z~~zxy~}{~|y}~{x~~~|{{~{y|yvsw{y}~~z}~zwy~}~{~z~{w}~}~z{|x{zwtx|x{~~y|zwx~||}y~~{w}~~}{z~y|{vsw{w{~}y|~zwy}|}y}~{w{~}~~{y~x||wsv{wz}}z{|xx}}~|}~y{}}y{}|}}z~~y{|wttyxz~~|z~}yx{~}||{{~~yx}~~}y}~zy|yvtyzx|~}y}~zwz~{~||y~zw~~}}z{~~y|{wsw{xz~}y{zwx}~|~y|{w{~}~~|yx{}xsu{xw}}zz~yx|}||}zz~}yy~}|~z}}zy|ztsxzw|}y}zxz~{~{}y~{w}~|~zz~~|x|{vtx{wz~}z|{wx~{}|~x||w|~|{yx|}wsv{xy~~zz|ww~}}|~y{|wz~|~~|yyz}ytuzxw}~{y~yx|}{}}zz~}yy~}}}z~~zz}xttyyw}~|y~yw{~{}|{z~yx~~}}y}~zy|yutzzw|~|y~ywz}{~}{y~~zx~~|~z|~{y}zutyzw|~|x}~yvz~{~||y~~yw~~~|}y{~{y|zutyzv|~|x~yw{~{~}{x~}yx~}|}z}~zy}zttyyv|~|y~xw{~{}|{y~zx~~}~z}~yy|yttzzw}~{y~yw{}{}}{z~}yx}}}z~x{}ytuzxx}~{y|xw|}||}z{~}xy~|~|y~y{{wtvzxy}}zz~|xx}}}|~~y{~|wz~|~~{z~~y|zwswzwz}}z|{wy~}{}y|{x}|{{~}y|zvsw{x{~}y|zwy~|~|}y}~zw}}~{||x|{vtyzw{~|x|~ywz~{~||y}~zx~}~~{|~|y}zutyzw{~{x}}yx{~|~||z}}zx}}~~z{~|y}zutxyw{}|y}~zxz~|~|~|y}~{x~|{{~}y|zvswzx{}}y|~zxy~|~{}z}~{w}}~{{}y|{wtw{xz}}y{~{wy~|~|}y|~{w||~|{y||wtv{xy}}yz~{wy~}}|~~y{~|xz~}~~|zyz|xtuzxx|}zy}yx|}|}}~zz}}yz~}}~z~~{z}yutyyx|~{y}}yx{~|}||z}~zx~~}~z{~}x|zwtxzwy}|y{{xx~|}|~y{|x||~|zy{|ytuzxw|}zx~|xx}~||}zz~}yy~}}}z}~{y}zutyyv{}{x}yxz~{~||x}~{x~|{{~}y|{vswzwz}|y{zxy~|~~|~~y|~|w|||zx{|xtv{wx}}zz|xx}|||~y{~}x{|~}zz{}ytuzxw|}zx~}xx|~||}zz~}yy}}}z~zz}yuuzxv{}zx~~yx|}{}}zy~}yy~}|~z~{z}yutyyv{}{y}~yx{~{}|{y~~zx~~}~z|{y|zutyyv{}{x}~ywz~{}||y}~zx~|~{}{z}zutyyw{}{x}~yw{~|}|{y~~zx~~}}z}{y|zutyyv{}{x}~yx|~{}}{y}}yy}}}{~zz}zttyxv{}{x~}xx|~{|}zz~~yy~}}~z~yz|xtuzxw|}yy|xx}}||~z{~|xz|}}zy||xtwzwx}|yz{xy}|}|~~y{~|x{|~|z~~y|{vtxzwz}|x{~zxz~|~~|}y|~{x}|~~{{}y|zvtxywz||y|~zxz~|~{|y|~{x~~|{|}y|zvtxzwz}|x|~zxz~|}||y}~zx~~}~{}|y|zvtyzvz}{x|~yw{~{}|{y}~zx~~}~{}}y}zutxzvz}{x|~yx{~|}||y}~zx~}~~{|}y|{vtxyvz|{x|~zxz~|~|~|y|~{x}|~||~y|{vtwzwz}|x{~zxz~|~~{~}y|~|x|||{y{{wtwzwx||yy~{xy}}||~~y{~|x{|~}{z{|ytuzxw|}yx}|xx}~||}zz~}yy}}~{~{y}zuuzyvz}zx}~yx{~{}|{y}}zy~|||~~y|{vtxywy}|xz~zxz~|~~{~~y|~|x||~|{y{|xtvzxw{|yx~|xx|}{|}zz~}yz}}~{~|z}zutyyvz}{x|~yx{~{}||y}~zx~|~||}y|{vtxzvy|{x{zxz~|}~|~}y{~|x}|~}{y||wtvzwx||yy~{xy}}||}~y{~}x{~|~}zzz|yuvzxw{|zx~}xx|}{|}zy~}yz|}~{{z}zuuzxv{}zx}}xx|~{|}zz~}yy}}~{~{z}zuuzxvz|zx}~yx|}{}}{y}}zy}|~{}|z}zutyyvz}{x|}yx{~{}||y}~zx~~|~{}{y|zuuyyvz|zx|~yx{~{}}{y}~zy}|~{~{z}zuuyxvz|zx}}yx{~{||{y}~zy~}}~{~{z|zuuzxvz|zx}}yx|~{|}zy}}yy}}}{z{}ytuzwv{|yx~|xx}}|{}~yz~}y{~|~}{y{|wtwzwx||xy~{xy~||~|~~y{~|x||~||~y|{wtxyvy|{x{~zxz~|~~|~|y|~{y}|||}y|{vtxyvy|{x{}zx{~|~}||y|~{x~|||}z|zvuxyvy|{x|~zx{~|}||y|~{y~~||}}z|zvtxyvz|{x{}yx{~|}||y}~zy~~}~{}}y|{vuyyvy|{x{~zx{~{~}||y}~{y~~||}~z|{vtxyvy|{w{}yx{~|~}||y|~{x}|||~y|{vtxzvx|{x{~zxz~|}~|~}y|~|y}|~}|z||wtwzwx||xy}{xy}}|{~~z{~}y{|~}{z{|yuvzxw{|yx}|xx|}{|}zz}}yz}}~{|z}zuuyxvz|zw|}yx|~{||{y}~zy~}|}~y|{wtxyvx|{xz~zxz~|}~|~}y{~|y}|~}|z{|xtvzww{|yx}|xy}}||}zz~}yz~}}~{|z|zvuyyvy|zw|~yx{~{~}||y|~{y~||}~z||wtxzvx|{wz~zxz~|}}|~}y|~{x}|~}|y{|xtwzvw{{xy~{xy}|||~~yz~}y{|}~{{{}yuvywv{|yx}|xy}}||}zz}}yz~}}~{{z}zuvzxvz|yx}}yx|}{|}zy}~zz}}~||z}zuuyxvz|zw|}yx|~{||{y}~zy}}~|~|z}{vuyyvy|zw|}yx|~{}|{y}}zy}||~|z}zuuyxvz|zx|}yx|~{||{y}~zy}}|~|z}zuuyxvz|yx|}yx|}{|}{y}}zz}}|{{}zuuyxvz|yx||xx|~{|}{z}}zz~}}~{{{|yuvzwvz{xx}|xy}}{|~~zz~}y{|~}|z||xtwzvw{{xy~{xy~||~{~}y{~|y}|}|z|{wtxzvx|{wz}zx{~|}}||y|~{y~||}~z|{wuxyvx|zw{}yx{~{~}|~|y|~{y~~||}}z}{vuyyvy|zw{}yx{~|~}||y}~{y~~||}}z}zvuyxvy{zx{}yx{~{}|~{y}~{y~||~}z}zvuxyvy|zw{}yx{~|~}||y|~{y~~||}~z|{wuxyvx{zwz}zx{~|}}||y|~{y~~|}}~z|{wtxyvx{{wz}zx{~|}}|~}y|~{y}|~}|z||xuwzvw{{xy}{xz}||~|~~y{~|y||~~|{{}yuvywwz{yx||xy}}||}~zz}}zz~}}~||z|zvuyxvy{yw|}yx|~{||{y}~zz~||~~z|{wtxyvx{{wz}zxz~|}}{~}y|~|y}|~}|z{}yuwzwvz{xx}|xy}}{|}~zz}}z{}}||z}zvuyxvy{zw{}yx{~{~|||y|~{y~~||}~z||wuxyvw{zwz}zxz~||}|~}y{~|y}|~}|z||xtwzvw{{xx}{xz~}|~{~~z{~|y||~~|{{}yuvzwvz{xx}|xy}}{|}~zz}}z{}}||{}zuvyxvz{yw||xy|}{|}{z}~zz}}||z}zvvyxvy{yw|}yx|}{~||{y}~zz}||}z}{vuyxvy{zw{}yx|~{~||{y}~zz~}|~|z}{vuyxuy{yw{}yx|}{~|}{y}}zz}|||z}zuuyxuy{yw||xx|}{||~{z}~zz}}||z}zuvzxvy{yw||yy|}{|}{z}}z{}}|{{}yuvywvz{xx}{xy}}|{}~zz~}z||~~|z||xuwzvwzzwy}{xz~||~{~}y{~|y}|~}}~z||wuxyvw{zwz}zx{~|}}|~|y|~{y~~|}}~z|{wuxyvx{zwz}zx{~|}|||y|~{y~||~}z}{vuyyux{yw{}yx{~{~|||y|~{z~}}~}z}{vuyxvy{yw{}yx|~{~||{z}~{z~}|~}z}{vuyxux{yw{}yx{~{~||~|y|~{z~|}~~z}{wuxyvx{zwz}yx{~|}}|~|y|~{y~|}}~z|{wuxyvw{zwz}zx{~|}}|~}y|~|y~|}}{||xuwyvw{{wx}{xz~||~{~~z{~|y||~~|{{}yuwzwvz{xx||xy}}{|}~zz}}z{}}||{}zvvyxvy{yw{|yy|~{~||{z}~{z~||~~z|{wuxyvw{zwy}zxz~||}|~}y{~|y}|~~}{{}yuwzwvz{xx|{xy}}{~{}~zz~}z{}}|}z}{vvyxux{yw{}yx|~{~||~|y|~{z~|}~z||wuxyvw{zwy}zx{~||}{~}y{~|y}|~}}{||xuwzvvzzwx}{xz}|{~{~~yz~}z||~~|{{}yuvzwvz{xw||xy}}{{}~zz}}z{}}||{}zvvzwuy{xw||yy|}{|}~{z}}z{}}}|{}zvvyxuy{yw{|yy|}{~||~{z}~{z}}|}z}{vvyxux{yw{}yx|}{~||{y|~{z}|}}{}{vvyxux{yw{|yx|~{~||{y}~{z}}||{}zvvyxuy{yw{|yy|}{~|}{y}~z{}}}|{}zvvywuy{xw||xy}}{{}~zz}~z{}}|{{}yuwzwvzzxx|{xz}}{~{~~zz~}z||~~}{||xuxyvvzzwy}zxz}||}{~}y{~|z}|~}z||wuxyvw{ywy}zx{~|}||~|y|~{z~~|}~~z}{wuyyuwzywz}yx{~{}||~|y|~{z~|}~~{}{wuyyvx{ywz|yx|~{}|||y|~{z~}}~}{}{vvyxux{yw{|yx|~{~||{y}~{z~}}~{}{vuyxux{ywz|yx|~{}||~|y|~{z~|}~~z}{wuyyuwzywz}yx{~{}||~|y|~|z~|}~{}|wuxyvwzywy|zx{~|||{~}y|~|z~|}}{||xuxyvvzzwx|{xz}|{}{~~y{~}z}|~~}|{}yuwywvy{xw|{xy}}{~{}~zz~}z{}}}|{}zvvyxux{xw{|yy|}{~||~{y}~{{~|}~{}|wuxyvwzzwy|zx{~||}{~}y{~|z}|~~}{{}yvwzwvyzxw|{xy}}{~{}~zz}}z|}}}}{}{vvyxux{ywz|yx|~{}|||y|~{z~|}~z||wuyyuwzywy}zx{~||}|~}y{~|z~|~~}{|}xuwyvvzzwx|{xz}|{}{~~z{~}z||~~}|{}zvwzwuyzxw||xy}}{{}~zz}}z|}}}|{}zvvywuy{xw{|xy}}{~{}~{z}~z{}}}}{}{vvyxuxzxw{|yy|}{~|}~{y}~{{}}}}{}{vvyxux{ywz|yx|~{}||~{y}~{z~}}}{}{vvyxuxzxw{|yy|}{~||~{y}~{{}}}}{}{vvyxux{xw{|yy|}{~{|~{z}~{{}}}|{}zvvzxuxzxw{|yy}}{~{}~{z}~z{}}}|{}zvwywuyzww|{xy}}{~{}~zz~}z||~~}{|}xuxyvvzzwx|zxz~||}{~}z{~|z~|~~~{}|xuxyuwzywy|zx{~{|||~|y|~|z~|~~~{}|wvyyuwzyvz|yx|~{}||~|z|~{z~|}~~{}{wvyxuwzywz|yx|}{}||~|y|~{{~|}~{}{wvyxuxzywz|yx|~{}||~|z}~{{~}}~{}{wvyxuwzywz|yx|~{}||~|y|~{z~|}~{}|wvyyuwzyvy|yx|~{}||~|y|~|z|}~{}|wvyyuwzyvy|zx{~{|||~}y|~|z~|~~{|}xuxyvvzzwx|zxz~|{}{~~z{~}z}|~~}||}yvwzwuyzww|{xy}}{~{}~zz}~z|}}}}{}{vvyxuxzxw{|yy|}{}{|~{z}~{{~}}{}|wvyyuwzywy|zx{~||}|~}y{~|z~|~~}||}yvwyvvyzww{{xz}}{~{}~zz}~z|}}}}{}{wvyxuwzxvz|yx|}{}||~|y|~|{~|~~{}|xvxyuwzyvy|zx{~|||{~}y|~|z~|~~}{|}yvxyvvyzwx|{xz}|{}{~~z{~}z}|~}||}zvwywuyzww{{xy}}{~~{}~{z}~{|}}}}{}zvwywuxzxw{|yy}}{~{}~{z}~{{}}}}{}{vvyxuxzxw{|yy|}{}{|~{z}~{{}}}}{}{wvyxuxzxvz|yy|}{}||~{y}~{{}}}}{}{vvyxuxzxwz|yy|}{}{|~{z}~{{}}}}{}{vvyxuxzxw{|yy|}{}{}~{z}~{{}}}}{}{vwywuxzxw{|xy}}{~~{}~{z}~{|}}}||}zvwzwuyzww{{xz}}{}{}~zz~}z}|~}{|}yvxyvvyzwx|zxz~|{}{~}z{~}z~|~~{}|xvyyuwzyvy|zx{~{|||~|y|~|z~|~~~{}|wvyxuwzyvy|yx|~{|||~|y|~|{~|~~{}|wvyxuwzxvz|yx|~{}{|~|z|~{{~}}~{}{wvyxuwzxvz|yy|}{}||~{z}~{{~|~~{}{wvyxuwzxvz|yx|~{}||~|z|~|{~|~~{}|wvyxuwzyvy|yx|~{|||~|y|~|{~|~~{}|xvyyuwzyvy|zx{~|||{~}z|~|z~|~~{|}yvxyvvyyvx|zxz}|{}{~}z{~}{}|~}||}zvwywuyzww{{xz}}{~~{}~{z}~{|}}}}{}{wwyxuxzxvz|yy|}{}{|~{z}~{{~|~{}|xvyyuwzyvy|zx{~|||{~}z{~|z~|~~~||}yvxzvuyzww{{xz}}{~~{}~zz}~{|}}}~{}{wvyxuwzxvz|yy|~{}{|~|z|~|{~|~{}|xvyyuvzyvy|zx{~|||{~}y{~|{~|~~||}yvxyvvyywx|{xz~|{}{~~z{~}z}|~}||}zvwzwuxzww{{xy}}{~~{}~{z}~{|}}}}|}{vwywuxzww{{xy}}{~~{}~{z}~{|}}}}{}{wwyxuxzxvz|yy|}{}{|~{z}~{{}}~~{}{wvyxuwzxvz|yy|}{}{|~|z}~{{~}~~{}{wvyxuwzxvz|yy|}{}{|~{z}~{|}}~}{}{wwywuxzxvz|yy}}{}~{|~{z}~{|}}}}{}{vwzwuxzww{|yy}}{}~{}~{z}~{|}}}||}zvwyvuyzww{{xz}}{}{}~zz~}{}|~~||}yvxyvvyyvx|zx{~|{|{~}z{~}{~|~~{}|xvyyuvzyvy|zx{~{|||~|y|~|{~|~{}|wvyxuwzxvy|yx|~{|{|~|z|~|{~|~~{}|wvyxuwzxvz|yx|}{|{|~|z|~|{~}~~{}{wvyxuwzxvz|yy|}{}{|~|z}~|{~}~~{}|wvyxuwzxvz|yx|}{|{|~|z|~|{~|~{}|wvyxuwzxvy|yx|~{|||~|z|~|{~|~{}|xvyyuvyyvy|zx{~{||{~}y|~|{|~~||}yvxyvvyyvx|zxz~|{|{~}z{~}{~|~~||}zvwzwuxyww{{xz}}{~}{}~zz}~{}}}~}|}{wwywuxzxvz{yy}}{}~{|~{z}~|{~}~{}|xvyyuvyyvy|zx{~|{|{~}z{~}{~|~~||}zvxyvuyzww{{xz}}{~}{}~{z~~{|}}~~{}{wwyxuwzxvz|yy|}{|{|~|z|~|{~|~{}|xvyyuvyyvx|zx{~|||{~}z|~}{|~~||}yvxyvuyyvx|{xz}|{}{~~z{~}{}|~~}|}zvwywuxzww{{xy}}{~~{}~{z}~{|}}~}|}{wwzwuxzwv{{xy}}{}~{}~{z}~{|}}~}|}{wwywuwzxvz{yy}}{}~{|~{z}~||}}~~{}{wwyxuwzxvz|yy|}{|{|~|z}~||}}~~|}{wwyxuwzxvz|yy|}{}~{|~{z}~||}}~}|}{wwywuwzwvz|yy}}{}~{}~{z}~{|}}~}|}{wwywuxzwvz{xy}}{}~{}~{z}~{|}}~||}zvxzvuxyww{{xz}|{~}{}~z{~}{}|~~||}yvxyvvyyvx|zx{~|{|{~}z{~}{~|~|}|xvyyuvyxvy|zx|~{|||~|z|~|{~|~{}|xvyxuvyxvy|yx|}{|{|~|z|~|{~|~|}|wvyxuwzxvy|yy|~{|{|~|z}~|{~}~~|}|wwyxuwzxvz|yy|}{|~{|~|z}~||~}~~|}|wvyxuwzxvy|yy|~{|{|~|z|~|{~}~{}|xvyxuvyxvy|yx|~{|{|~|z|~|{~|~|}|xvyyuvyxvx|zx{~{||{~}z|~}{|~|}}yvxyvvyyvx{zxz~|{|{~}z{~}{~|~~}|}zvxyvuxyww{{xz}}{~}{}~{z~~{}}}~}|}{wwywuwywvz{yy}}{}~{|~{z}~||~}~|}|xvyyuvyxvx|zx{~|{|{~}z{~}{|~~||}zvxyvuxyww{{xz}}{~}{}~zz~~{}}}~~|}|wwyxuwzxvy|yy|}{|{|~|z|~|{~|~|}}xvyyuvyxvx|zx{~{{|{~}z|~}{|~|}}yvxyvuyyvw{zxz~|{~|{}~z{~}{~|~~}|}zwxywuxyww{|xz|{z{}{z}~{z{zz||yuvxwrvzxux{xz|zyy}~~}z~~}~~~~}z~{xy{{{|{vxzyzz{z~~{{|{y{|z~|w|}x}~~}{}}xtuxvwxrt}zy|}}|}}vx|{z}~~~~zwyzyrrwvuvyvxzxww{|~|~}{y~~zwwz|}}{wx|zzz{|zz{{yz}||zv}~y|}~|{||xuuxwwvqu~{z||~||~~|wy|{{~~~~|yxyyxsswvuwxwxzwwx{~|~~~~}{||xxx{}~|ywx|{zz{}~}yz}~{{{}}{yx}{}~}}z||zwvwxxvtsz}zz|}}|}~|zyz|z}~~zxxzxuuuwuvxxyyzwxz}~|}~~{{{xxy{}~{wxx{{zz{}}|zz~~{|}}}yx{~|}}~~}z||zwwxxxusu{~|{y{}~~|}}~|z{{|{}~~}yxyyyuvvvvvxxzyyxxz}~|}~~{{|xxy{|}{wwx{{zzz}}|zz~~||~~}yx{}}}~~~{{|{xwyyyustz~}{yz|}~|}}~}{{{||}~~}zxyyywvvvvvxxyzzywz{||}|{}~yxxz|}|xvwz{zzz{}|{y|~|~~~{yy~}}~~|{||{xxyzwtsu|~}zxz|~|{}~~}{z|||~~~|xxyzyxuvwvwxxy{{xxy|~|{~~|{}}zxw{}}{xvw|{yyz{}|yz|~|}~zyx~|~~||{}{xw{{wuru|}zxx|~{{}~~{z}|}~~|}}{xxzyzxtwwwxxwy}zxyx|~{{~|z~}zwx|}|zwuy{zyzz{}zy{|}{}{xy}}}{|}{wy|yvtrw~~|zwy~|z{~~~z|}|}~~~~}|~ywyzz{vvxwwxww||yyxz|z~}z{{ywz}|{yuw{zyyzz|{x{{~|~~{yx}}~}|{}|xx|{wtru}|zww}}zz}~{|}|}~~||~~ywyzz{wvxwwxww{}zyyy~}y}}{{{ywz}|zxuv{zyyzy|{xz{~|~~{yx~}}|||}|xx|zvtsv}~|yww}}z{~~{|}|}~~~}~|~}xwyzzzwwxwwxwx|}yyyz~{z~~|{}}zyw{||zwux|zyyyz{zy{}~}}zyz~}{{|}zyz{xtttz~}{wwz}{z}~}|||}~~}}}}{wxzzzyxwwwxxx{|{xyz|}z|}{||zyy{{|yuvy{zyyyzzzzz~}~|yy|}{{}}zy{zxttv{}}{ww{|{{}~}}||}~~~}|}}zxxzzzxxxwwxxx{|{yyz}}z|}{|}zyy{{{yuvy{zyyyzzyzz}~~|yy|~{{}}zy{{xutuz}}{wwz|{z|~~}}}|}~~}}|}{xxzzzyxxwwwxwz||yyz{~{{~||~{zxz{{{wuw{{yyyyzzzz|}zzz~~}{|}}yz{{wttv|}}zwwz|zz}~}}}}}~~}||}zwxzzzzxxwwxxwz||yyz{}{{~||~~{zx{|{zwuw{zyyyy{zy{|}~zzy~|||}}yy|{wutu|~|zwv{|zz}~}}~}}~~}||~zwxzz{ywxwwxxwz}{yyy|}z{~|{~{yx{|{zwuy{yyyyy{yy{|~}}{yz}}{|~|xz|zvtsw}}|yvw|{y{~~|~}}~~~~}~{~|xwyzz{xxxwwxww||zyyz~|z~}|||zxz|{zxuw{zyyyy{zxz{~}{zy~}}||}}yy|{wusv|~|zww{|zz}~}}~}}~~}|}}ywyzz{xwywwxww{|zyyy~}z}}|||zxz|{zxuw{{yyyyzzxz{~}~{zy~}|||}|yy|zvtsw}~|yww{|z{~}}~}~~~~}~|}|xxyzz{xxxwwxwx||yyyz~|{||~~{yx{|{zwvy|zyyyyzyyz|~~}zz{}{{|}zy{{xttuz}}{xwy|{z}~~}}}}~~~}|}zxyzz{yxxwwwwwz|{yyz|}{}}|}|zyy{{{yvwz{zyyyyzyzz}~|zz}~}{{}|zz{zwttv{}}zwwz{{{}~~}}}~~~}}|zxyzz{yxxwwwwwz|{yyz}}{}}|}}zyy{{{yvwz{zyyyyyyyz}~~|zz|}{{}}zy{{xutuz}}{xwz{{{}~~~}}}}~~}}}{xxzz{zxxwwwwwy{{yyz{~~||||~{zyz|{zwvx{{zyyyzyyz{~~~{zz~|{|}|zz{zvttv|}}zwxz|z{}~~}}}~~~}|}{xyz{{zxxwwwwwy|{yyy|}||~||~{zx{|{zwvx|{yyyyzyxz{~}~{zz}||{}|yy{zvttv{~}zxwz|z{}~}~}}~}~}}}zxyz{{zxxwwwwvy|{yyy|~|}~||~{yy{|{zwvz|zyyyyzxxz|~~}}zy{||{|}{yz{yvttx}}|zwx|{z{~}~}}}}}~|}|yxzz{{xxxwwwvw{|zyyz~}{}|}|zxz|{zxvx{{zzyyzyxz{}}~{zz~}||{}|yy{zwtsv|~|zxw{|z{}~~~}}}}~|}}yxy{{|yxxwwwvwz|zyyz}}|~}|}|zxz|{zxvx|{zzyyzyxy{}}~~{zz~}|{|}|yy{yvttw|}|zxx{|z{~~~~}}}}}}|yxz{{{yxxwvwvx{{yyy{~~||~||~{yy{|{ywwy|{zyyyyxxz|~~~}zz|~|{{||zyzzwttuz}}{xxz|{{}~}}}}}}}}{yyz{{zxxwvvvwy{{yyz|}|~}|}}zyy{{{ywx{|{zyyyyxyz}~~~|zz}~|{{||zyzywttv{}}{xxz{{{}~~}}}}}}}}zyyz{{zyxwvvvwz{zyyz|}|~}|}}zyy{{{ywxz|{zyyyyxyz|~~~|zz}~|{{||zyzzwttuz}}{yxz{{{}~~}}}}~~}}{yyz{{zyxwvvvvx{{zyz{~~|}|||zyz{{zxwy{{zzyyyxxy{}~}~{z{}|{|||zyzyvttw{}}zxxz|{{}~}}}}}~}}{yyz{{zxxwvvvvy{{yyz|~|}~||~{zy{|{zxwy|{zzyyyxxy{}~}}{z{}|{{||yyzyvttv{}}{xxz|{{}~~}}}}~}}{yyz{|zxxwvvvvx{zyyy|~|~~||~{yy{|{yxwz|{zzyyyxxz{~~}}zz{||{||{yzzxvttx|}|zxy{{z|~~~}}}}}}~|zyz{{{yxxwvvuwz{zyyz~~}}}|~}zyz|{zxwy||zzyyyxxy{}~}~~{zz~}|{{|{yyzyvttv{}|{xx{{{{}~~~}}}}}}}zyz{{|yxxwvvuvy{zyyz}~}}}|~|zyz|{zxwy||{zyyyxxy{}~}~}{z{}|{{|{yyzyvttw|}|zxy{{{|~~~~}}}}}}|zyz{{{yxxvvvvwz{zyy{~~}}~||~{zy{{{yxxz|{zzyyyxxz|~~}|zz|~|{{||zyzywttvz}}{yxz{{{}~~}|}}~~}}{yz{{{zyxwvvuvy{zyyz|}}}|}}{yz{{zywy{|{zyyyxxyz|~~}~~{zz~}|{{|{zyzyvttv{}}{yyz{{|}~}}||}~}}{yz{{{zyxwvvuvy{zyyz|}}}|}}{zz{{zyxy{|{zyyyxxyz|~~}~~|zz}~|{{||zyzywttvz}}{yxz{{{}~~}||}~~}|zyz{|{yxwvvuvxz{zyz{~~}~~||~|zz{||{yy{|}{zyxxwwxz|~}}~~|{|~}}}~}{z{yvsruy{zxwwyz{}~~~~~~~~}|zyxvuuuuwz||{|}~~~}}||||}~}zwttuutrruxxxz||}}~|{|~{zz{~~}}|{zxxwuttvy||yvuwwustvwwvvvvvx{~|yyzywy}}}}}{ywwxvtsux~~{yxxyxwvtsuwvspooppprvy{|~yvvy}}|zxwxxyyzzzzxvutsqprvwvuuwz{|~~|zyyxwxwwuuuuuw}}|{yvtv{~|ywwyyxy{|~~{{|{yxxyzyxxwvuuuwwz||yxyxxww{|yvuutttrqsuvwxz}|}~}||zxvwyz|||{yy{|zvsswzyxzzz{}}~~|}{xxxxxvuvwwy|}||~}|{{~}}~{|~~|xvvvuttuwxxy|}~||{z{zy|~||}|{}}}}~}zyzxvttsnjhjnrstsqrv{~}|~~~|{|}~}{|||ywy||{|}{z}~~||~~~|}}{z|~~|yyywx{zwroprsqppprux|~~}~}{{|}}}~{xwvurppuyxvuuuspqsvvuutrrty~~}|}||zyz~~}{xwwwwvtsrsx~}~}|{xwvwwsrtvuqooprpptvx{~~{wtuxz}}zxy{zyxy{zwwvtroptyzxwxwxz~~}~|yvwy||yxywuuz|yy{xuuwyzz{{{zz{{z{~}{|}|{|zyyzzxvtsuuux|}|yy|}{wy}~|{zyvuvurruz{zxz~}|~~}zwwxxxz|~|z{||xutuyzyzzxxz}~~~}|~~{{zyvtvwyxvx{{{|~~{yz|}~~|zyyzvtuuux|}{}|zzzyz}~~}}}}|{|~z|zz{xxxvspnmmooswvssw~{zz~|ywy~}{||{xvwz}{{||zxy{|~~z{~}{z}~}~}yxzywy{|yuuuwwttspps{}}~{{~}~~~}~|z~~|||xwwuqnnswxzzwtsuwyywxyustx}~}}~}||}|{~{yy{~~~~~zxyz{xuwuqps}~~~}zwuyywwwuusstvuqqrrsw}~~{z~{yvuuuy|z{{yww{|{|zxtpqtwzy{{xwy~~zuux{~{z{ywvy~}|yxzyyywusv{~}z|}zz~~~~~}~{yxzywtruutwz{zz|~{zzy{{xxvtsu{{xz{{~}}~{yxvutx~|zxyz{xw{zwwy~}zzy{~~{wsuy|zvvxxz|~~}zxy|~~|~~~{}~}xvvtsv}|}~}|z{zy|~||}~}}}|yzyy{}~|{|{wrpsttpmquuuw}}ywy}}{wvz}~}xvxz}~}}{|}{xwwvyyy}~{|~|}|~~~|~~zz{yz|{{z{|}|wvxtqqu|xz}{|}~~~}z{zy{}|ywutportx}~{vuy|~{y{{wuv|}|}}~|zyxy}~|yy~|yy}}}z{|~}xuwusrv~~}yy|{{|yuruyz{vrsqprx~}}}zx{~}ywvwuw}}|z{zy}~|vrtvzzx{~|zz~{z~~}{wwx{~{z|{xxzz|}|yy{z{}|wrsy{|}||~|{}~{xyyywuwwvxyyxx}}|{xx|~}|||{{xutw}~||yvz}||yvutv|{z}~~zvx{yyz}yxvvz~zuvx{{vuvtux}~~{xwx{{}~~xxvttx~|}~|zz{}|}}{z{}~~|zzzwvy}}|}yssvyysoqrsvy~yvw{}~}ywy{}zwy{}|y|~~zx{}~zwvtwzy{~~}{}}|{{~{xx}~}|~}{yyzyzwtsx{wyz~~}}~}~~}z}{{yvv{~{yvvtsutuz}}yw{}{{}|zz~}}~~|zyux}~||~zvw{|~~~}~}}xxwuuv{~~}}}|~~~|vrv{xvvsqpv~}yz}~~}~~~yuvzyz|{{~~}{{zz~}wxyzzvx|}}}}{{y{|~}z}xy{{|wvzzyy}~}~yy{~~{z||||vtwz|}}{|{|~~~{xvxyy{|{}|xtt{}|}yvv~|}~~~~~|{zx{}|xvx~{tuy|z|}ywtty|~}}wwxxz|~~~{zwuy|~}y{{{yuuwtst}}~~{xwwx}~|{xtsy}~zww|}{yxx{~}~|y~zzyvux}}|yz}}wvyz}ztssqqv}wvxz~~{|||~{~|vw|}~~ywyz}|zwsvy{}~|yx}}|z{~|wuz~}yvz||{xwx{z{}}zy{~{xz}~{wuw}zxxvvyyy{|{xy~|}~||}}{}}}~}zwy{~z||z~yvwx{||||}}|{xyxvv{~||{}|vvy}~zzyurs|~~~}||~}~|wx}~|x{~}zvvy{}|z{|}|}|{|}}}zuuz{|}~y|}z{{|{{~|{}~}wtxyxx|}}xxz}~||~}|}~}yxz|~|z|~|{}}}}{z}{xxxy}|urx}~|}{xv{||~}z{}{{{{xy}|~~{yzxwx||z|~|wtx{yxxwx{{z}|}}}|zvwz|}{~~{tsvvtrw~}~|zywx}~yuu}~yuv{~{wvy|}xw}}|yvuz~|yxz{~~xy{}}yvwvrquzxyy}}~~~~wuxyz}}zxyz|~|wuwz}~xtx|~|}{z|{wwy|}wv|~}zy~~~~~xtu|~{z}{vy~|zvv~|}}zzxvy}~~zvw}{|}{|zx|}~|{||{wy|~}xy|}zzyy{~}}}{xz}}}}}{zzxvv~~}zy{}|~y{|}~zz}}yvx~|xy|z{|{|}vw||vx{{{~~{vvy{||{z{}}{~}uswxxz{z|~}|{}~ytvwvvz{uwz~{ww{~}}xz{vy||{|~ywy~{zxv{yuxz~}}|yw{~~{ww{~{xz{zy|~~}}}|z|zzz||yx{~}yyz}~|{{wuw{xz~|~~|xy{}{z~urtuttz|{~}|}|yy|~}xzyvvy~}ywwwz}vw|zvx}vux{}|{~~{vwzxus{~}|}~}~{|{xxvux}y|~zz{}~}~}{yyz}~xux|}|~}||{|~zyzz}}vx|~|}z}}vsuz}yy{|}wy}}z|}zyzz}~~}|ywz}|uv}{|~{|xwz~~~~}~ywxxy||wy|~{{||||{z}}zxy{{}|~{|{xuy~{}~}yy{{||}~zw|~{{}~~wux~~{z|{z}yvz}~wwyxxz~~xwxz~~~|yy|}{}~ytvvvw}||~|}~|}~zuwxvux}uux}|vtw|~}~xz}vwz{z{yuvz~~~{wx~}y{|||yz~{w{|wvy|}xxzyz}~}}~~{y|z{|~|wvy|~}|}{~}}~{vu~zy{~}~~~}|z|~zx}{uttssx|z{~}{}|z{~}~~|~zxxz~{zxuy{wz|~~|x{}|usw{~}{~zuwzyvw~~{|xy~{{yut|~yy|~}~~|}}{~~}}||~}zvy|}~{y}}|~}}zyz|}~z{}~zz}{}|xuuw|}zzyxz~xz}{xz||{}}{yz~|yy|xvz~zz}~||~xwz}}|{yw~~xyxvuzzxz~zy{~~zy||zyzxx|{{}|xv}|y{}~|zyxz}xtx|}|~~}~xuvy{{{zy||{|||zwxywuw~{z{{~{yz{z|}zyvst{|z|~}{|~{{~wwxvtwzvvz}xuvx|~|~xx}ywyxxy~wuw{zy{~}{ttz}}z~~}xwx{~}{yvw|{y~~|y{zz}xuw{~{xz}}}zx~}~~{|~}}~~~~xw||zxuqqy~}{|~|z|}|}~{}~~~||||~yvx|zz}}|yw|}}||vuvz~~~}{{~wuxxwx~}~||}|}zw{~|xuy{z{}~~|z|}|~~{vuz}~|wvz}|{~}xx{~~|ywz|}|y{~zyyy}|{zwvz|z}{wxzzy|~|{|~}|~~~~{{|zy|}}yy|||}|z{}{}{z|wu{}yyxuszzz||yx{|zzyyzzxx{}|x{|{y{|{}~}|xw{}}vuwy{}y{{wvwy~{{zy{~~}~|xuvyxvu}~{|}}~}{{zy{}~}ytsy}xy~{x{~yv{{yxvuw{yz}|ywwx{~|}zx|zyyxvx|xwy~}||}~vsux{|~~zwy{~~{wuy}{wz}~|z{{z|{wwz}zvx~}}~~|{ywz~~|z{~ywz~|z|zuqt~~{}}}~{z{{|}z|~~~}}}}}}~{zz{|}~}zutx}||~zxwz~|z|z{~xwwvvz~~}}}{yz}~~}y{}|y|}|}{z}~|z{|{{~vrv{~~ytvxzz~~}{wx|}}{uuxzz{zz~~~}}}}~|{xwy|~zvwyxw{}z{}zy|~|}~}~~zz}|xwzzz{~{yz}|zz{~~xwz~}|zwuv~}|zxy{}yvwzzyz|~~~zyzzy{~}||{xz}}~yxvuw~xx|~yyyz}}zy{~~{}yssvwvw~~{{~~}||{yz}~~zvxzwy}zxyz~zuw|~{xuxzvvxz|~~|{}}~|yy{yww~}{|~|}{vuttw|y|{z{}|yxz}|wx|}~~}|~~{{}zyz|~vuy~|}~}|}|vvz}zxz|~xvz}}zy{|yvx}}z{~~|zyz~zx{}}|~z{~~~~~}}}~}|zttuwz}{}~~}{|}}~{yz{}~|~{zywuv}~}~~ywyz{~|}~}|z|~|{wx}~|yzzz{~ysuy||xuuvvx~~|yy{~}{|}}~{utvwx|||||~{yz{}{vvwwwyyx{zxy}~}}}|xy}{vvxyy{yvx{~|zx|~zz{{|{|~|yw{}yxz|zvvwxy{}~~|{{zyy}~{{|}|{}~|~|{yvtw}yy}~|||}~~}z{}~~zzwsstuw}|{}~~|{|{zz|~~}~}}{|}zy{}zxwz|wwz}{xy}vtvy|~||~~zwwyyww}~~~}yy}ywurt{~zz~}}~}~}|{|}~{xx{}~}{}~}}~}|zz{|~zvxz}|z{}}|}yvw{|yxyz{wx{}}yxy{{z||z}~}zy{~}xx{|}~~}~}y{~}|~}zxzyuuuux||~}}}|}~}{zyy{~~}z{{xuw~}|~|xwwy|}{wx{~~~~ywy~}zyyyz}ywxyzxvvvuuy}{{|~{z{{|~}xuttv}~~|||}~}}}~~~|}|wvvvuy}yy}|yxz~~|~}xvz}|xuvvwyxvw{~{y{~}~}zwvz}}{|{xxz}}xvuvy|~~|y{~}}{yz}~}yy|~}|zy}||{yvy}|}~~~~~~{xy}{xvsru}~~~{yz{z{|}|{~~|}~~~}||}}{xwzzz{|{zz~}}|vuwz~~~~}~{{|wvwwwy~}}}}}yvy|{xusw}{|}||}~}{xvx{||yxz|}}{yz|}~|{{zxvxz||~~~}zyy||zxxz~yz||{wvxyy{||}~~~~~|{|}{xx{}}{z{||}|{~~~}zz}~xtw}zxwutw}}~|}}|{zzzyy{{z{{yy}{|~zwvx|~~xuwy{}~~|yy{|zyxy|~||{zxutvvuuy~}~~~{yyy{~~~{xtsw{z}}zxz}}zx{|xwvuw~}{|~|yyz|~~ywy{}{xvuuux|xwy}|||}~~zutvz|}|yy{}{xvvy|~}ywz~~~|{~}|~{xy||xvx}}}}{yz~}{{~~}}{xx{~}{zvssy|yyyy{}}~~}zy|}}}}~~~}zxy{}|}}{xwy}~{xwy|~}{|~|{~}yvvvvx}}{zz|~{wx{~~}|{xvx}~~~|{{|}~|wtvz||yvwz{||yy{}~~~}}~~{wtuwyz}|~}|{{}|{yy|~|||{wvvxxy~~||~}}~~~}}~~~|xxz{{zxyz{|~~~|{}}yyz|}wtv{~|zxvvz~|{}|yxyyyy{~{z{zz|||~~~zwxz}~zwvvwz}{}|{{}}{yy{}~|}~{vssuuux~~~~|zyxx{~~~~{wuyzy{{wwy|~zvx}}zwvx~}zyy{|~|yz{{zwvvutu{~{zz}~~~}zuttvy}{{|~~{xxy{}~{wwz}}}~~~~~}~{yy{~~xtvz}~~}}}||~}ywz~}z{}{wwz|~}{||{xwz~|zyxz|~~}{wwz~}|}}|}}{}~||{|}~~~~yvvxz~~}~|{{}~~~}{z{|~}~~{yxvuw|}ywxz{~zzz{{z{}}{z|~|||zzz{}zutwz{yvvwxz~{yz|~}|{{|}~{ustuw{~}~~}~}|{|}|xuvvwy~}z{~~{{}~~~|ywxz{zwuwxy{}yxz}~{yxz~~xvwy{{{||zyz~}z{}~ywwxy{|}}~}|{z|}}~}|}~}{zz|}}||}zxwuw}|{~~~~~|{{|~~|{}~ytsstuy~|{zyxxz|}~~~~}zz}{y{~{xvwz}xwy}~|yy~zxxz|~|{{|{yvuvuuuz~}}}~~|zz~zwustw~~~}~~|{z{|{ywwz|}||}~~|zzz{~~wuwy{{z|}}|~|xwz~|zz|~ywx{||zz||{{}~~|zyy{}~}{wuw{~~|||}|}{{~~~~~~}||{z{}{xvvwz}~~}}}}~}{zyz|~{zyxvw|~|xwwy||||{zxxz|||~|{}~{yyz{~~xvwyyxvvvvx|}{{|}~{yyz{}~~yusstw~}}~~~~~~zwvvvw{}{||z{}~~~zwwy{zxutvwy|}xxy|~|zyz}}yxxxxwy{||{~|z{}{xwwy{}~~~|{}~}|{}~}~~|z{~~}{{{zyz}~{zyww{~|}~}}~~}{y{~}yvtstw~}{yyyxyz|}~||~~}}~~~}{{}}zwvw{~zyz{||}}}}yxy{~}|}~}zwuuuuvz~~}|}}}~~ywy}~{xvttw~~}}~}|zxwwxz{zyz{|}~{zz{|}~{yxxwwwy{|}~zxy{~{zz{|yzz{yxyz{|~~~|{{{|}}{wuvxz{zz{||}|||{{}}zwvy}~{yxww{~}|{{zyyz||{{zyy|~|}}ywvx{}}~~{wvvxz}|{}~{zyz||zzyxvuvvvwz~}}~~|zxwxz}~}}zwtsu{~}~}{z{}~|{{~zxwvw{}}~|{{|{wwyzywuttuw|zxxz~~|{{|~}{{{yvtux{|}{{}}zxwy{}~}{yy|~~~~|yyz}~{xvux}~}|{yy{~~~|yxy}~{ywttw}~{yyxyz{{||{z{~}}}}~~}~}{xwxz~}|||{zy{~}zyz|~~}~~|}~~|xvuuuwz~|{zz|}~zwx{~}|zxvux~~}||}~}zwuvyzzyxyz{}}zz{|}}}~~~|{zxvtuwy{~{zz{~~|{z||{{{ywwxz{}~~}|||}|zxuvwxyxyz{{}~~~}}}{z{}|xuuy}}{zxxz~}{zyyyz{}}|{{z{~|||ywwy{~~~}yvvvx{~~}}~}{zz{}~}}|yvtuvvwz~~|zxwwx{~~}~~|zxuuy~||~|zxy|~}zy{~{ywwz~|{{|}}zxxyyxvttstw~|zy{}~}}||}~~}|}}|yustvy|~||}|zxyz|}}zxy{~~~~|yxy|}yutvy|~}}}|{{}}{|~~}~}zwxz}~~||{xww{~{yyyyz{{zywx{~}|||}~~}}{yyy{}~~}~~|zxxz}~|{|~~}||}~~}zxwvuvz~}zxxy{~}zxyz{{{{{zxy|~|{{|~{vuvyzywwwyz}}{{{|~~}||||}~~~}zvsstvy~~}||}~~}||}~}zwvwxz}~~~~~~~~|zwvvwwvuvwyz||zz{~}zyz|zvuvy{||}|{z{~}}}zxxyz{}~~~~}|{|}}~~{zyyz||||~|ywvvx}}}}|||}~~~}}}yuttuwz~~|zywwwx{}}}~~}}|zyz}}{|~{xwxz~{xy|}{z|~{z{|~~{zzzywuutttw}~|||}~~}|zz{}}zvtstv{~~}{{{{|{yxy{}}|}~~{yyy{~~yutvxyz{|}|}~{yz}||}{xxy{||{||{z{}~|{yyz{{{ywuvy|~}||||}}|}~~}|{zzz{{|~|ywxy|~}}~~~}{{|}~{yxwvw{{xvwy|}{zzzyxyz{{{|~~|zz{}|wvwxyxwwwxz~~|||}~~}{zyz{}~~~~{vtssuy~}}}~~~~~}ywwwx{~~~}~~{xvvwwvttuwy{~zyy{|zyz}|xvvwxyz{|||}}}~|yxxy{}}~~}||~~}|}~}~~}}}~}{zyyyyy|}zywvw{~}~~~}}}}||||xvuuvy~}{yxwwxy{}~~||}~}|}}||}}||~~zxwxz~}zyz|}~~}~}zz{}|{{{zxvuuttvy~~}||}}~}zxxz}~}zwutsv{~}{{zyxxy{{{{{}~}{zyz{}{wvvvvvxz{|~}zy{~~|{|~{yyzzzz{||}~~}{{{{{{zxutuxz{{{{||~~}}~||}~~~}|zywwz}}{yxxz~~}}|{{{|~}{zyxy{~}~|ywvwz}}||{zxvwyz|~~|{z{}}zxxxwvvwwxz~}}~~~}{yxxy{}~}}}ywtstx~~}|{|}~~}}~|yxwwz~}~|ywvwwvtsstvy~~zyy{~~|{z{|~~{yyxwuuwz|}~}}~|yxxz|}~}|zz|~~|zz|~~|zwuux{~}|{zyxz~}|zyz}~|ywvux}~|zxxxxxyz{{z{|~~||||}~}}~{xwxz}|{{{{z{~|{{|~~~}||}{ywvuuuw{~}{zzz|~~zxwy{}}{yxvuv{~~}{ywwxz{zyyz{}}{zzz{|}~|zxwuttuxz}}{z{}~||}~{{zzyxy{|~~}||||{zwutuvxyyz{{|~~~~~}{{{}~}zwuvx|~~}{zyy{}|{{{{{}~~|{zz{}~}}~{xwwx{}~}}}|zxvvwy|~~|{{}}|{yxvvvwxy|~|zxwwwz}}|}~|zxvuvy~~~}{zz|~~|{{~|zyxy}~~~~~{xwxxwussstv{}zyy{~~}||||}~}|{zzxutuwy|~}~|zyz{|}}{zy{}~|zyz|~|xutux{|}||{zz|~}~}zyy{~~}|zxwx|}{yxxxyyyxxxy{}~}|{{|}~~~~~}{yxyz|~}}}|{zy{}}||}~}~~}}}{ywvvvwz}}{yxxz}}zxwxz{{{zywwy|~}}~|yvvxzzyxxyz|}|{{{|||||}}~~~}|{ywtssuw{}{{|~~}}~}}{yxxyz}~~~~}|{yvttuuvvwyz{}~|{{}~|zz{}~|xutvy{||||{{|}{zz{{}~~~~}|{|~}|}}zyxxyz{|||}~}{xwvwx|~~||}~~~~}|yvuuvxz}~|zxwvvxz}~||}~~}}|{yxy{}}}~|yxxz}}zy{~~|{{~~}}~~|zyyyxvtssstx~|{{{|~~~}||{{{{||{xuttux}~|{{{||{zyz{|}}}}{yyy|~|xttuvxyz{|||~~|{|~~}~|yxy{|}}}}|{{}}{zyzzyyxvuvx{}}|{{{|~~}}~|{zzzz{||}~~}zyyz|~}~~}}}}~|zyxwwy|~|ywvwz}~{yxxyyyz{zzz|~~||}~}ywvxyyyxxyz}~}|||||{zzz{}~~}}}}{wtsstw|~}}}}~~~~}{xxxy{~}{yvuuuuttuwy{~|zy{}~|zzz||xvuvwyz{|||}|zyz{|}~~~~}}~~}}~}}~~~~}|zyxxxxy{}~}{yxwwx|~~~}}~~}}~~~}zwvuvx{~|zxwvvxy{}~~|||}}}||||{{|~~~}|}|yxxz||zz{}~~}}|}~}{zzzywuttstw{~||{||}~~}|{yyz|}|zwuttvz}|{{{zyyyz{{{|}~|zyyz|~zwutuuvwy{|}}{z|~}}~~{yyz{||}}}}}~|{{{zzyxvttux{|{{{{|}~}~~}~~~~}|{zyyyy{}~~|zyyz}~~~~~}|}}}{zyyxy|~}zwvwy{~}{zyyxwxyz{|}~|||~~zxwxxxxxxyz}~}}}}|{yxxy{}~}|}~~|zwussuy~}||}~~~~~{yxxz}~}{xvvvutsssux{}zyy{~}{zz{}~}zwvvvvwy{|~|zyz|}~~~}{{|~~~~|{|}~|zxwuuwz|}}{zyxxz}~}|{|}~|ywvvy|}{yxwwwxyz{{{|}}}|{{||}~}}~~{yxxz|}{{{{{|~}|}~|{{{zxwvuuuwz}}{zzz{}~~{ywwy{||zxvuux|~|zyxxyz{zzz{|~}{zyyz|}~}zxwvtstuxz}}{z{~~}}~~|{zzzz{|}~}|||{zywtssuwxyz{{|}~~~~~~|||}~~|zxvvx{}~}|{zz{~}}}||}}~}{zzz|~~}}|ywwwy{}~~}|{{zxwvwy{}~||}~|zyxwwwxyz{~~~}|zywwwy|~}|{|}}|zxvuuw{}{{{|~~}||}~|zyz|}{ywwwvusrrtvz~{zyz|~|{{{|}~}|zyxwuuuwz||{z{|}~}|{z{|~~|zz{}~|zwustwz{|{{zyz{~~}{z{|~~}{yxxz}}{zyxxxxxxyyz{}}|{{{{}~}~~|zxyz{}~}||{zz{}}}}~}||{yxwvvvxz|~}{yxyz|~}zxwxy{{{zywwx{~|yxwxzzzzyz{}}{zzz{{|}~~|zyxvtssuw{}{{|~~}}~~}|{zyz{}~~}}}|{zxusstuvwyz{|~~|||~}{{{}~~|ywuuwz|}||{{{}~}||||}~~|{{|~~}}~}{yxxxyz{||||{zxwvwx{}}}~}|{ywvwxyz|~}{ywvvwz|~~}{{||}|{zyxwx{}~~}{yz{}~|z{}~}|{|}{yxxxvussrsvz}{zz{|~~~}||{{{{{{{zxvutuwz~}|{{|}}|{zz{|}}~}{yyz|~~{wtstuwyz{{{{|~}}~}zyz|}~~}}{zz|}|{zyyxxwvvvxz|||{{{{}~}~|{zyyz{||}}}|{zyz|~}~~~~~}|zyxxxxy|}~~~{xwwx{}~|yxwxxyzzzzzz{~~~|ywwxzzzyzz{~~}||{{{zzz{}~~}|||{yvtsstw{~|||}~~~}{yyy{}~~}}{ywutttstuwy{~}{z{|~|{z{|~~|yvuuwxz{|}}}~~|{{|}~~~}}~~}}~~}~~}|zyxwwxxy{||{yxwwx{~~~~~~~}{xwvwy{~}{ywvvwy{}~~}|{{||{{{{{z{|}~~}~}zyxz|}{zz|~~~~~~}{yyyxwvttstvz~}|{{{{|}~~~}|{zzyz{{{ywutuwz~}||{|{{zz{{{||}~|zyyz|~~|yvtstuvwy{|}~}||}~~~{zz{|}}~~}}}~}|{{{zyxvutuwy{|{{{{|}~}~~~~~}|{zyyyz{|}}|{zyz|~~~~}{zyyyy{}~~}zxvwxz}~~}{yxxxxxyz{{|}~~}~~{xwxxyyyzz{}~}}||{zyyyz|~}|{{||{yvtssuy~}}|}}~~~}{zyz|~~~~}|{yxxxy{}~~}}|||{||}~~~~~~}||}~~}|{{{{|}}~}|{zz{~}}}~~~~}|{z{|~|yvtstvxyz{|}}{zz|~|{zyz{}~|yxwvusrrsvz~|{zyyz|}~}{yxyz{{zxwwx{~{yxxxxyz{|~~}{zyxwxz|}~}|||{zz{{|~~}|}~~||{{{{{||}}|zzz{~}}}}~~~~~~|zz{|~|yussuwxy{{|}|zyz}~~}|zyz{}~|yxxwusrrsuz~|{zzyz|}~~}{xxy{|{zywvx|}zyyyxxyz{}~~~}{zxwwyz|~~||||{z{{{}~~~}}}~~~}||{{||{|}~}{zzz|~}}}}~~~}~~~|zyy{~~{vstuwwxz{{||zyz|~~~}{yz{||yxxxvtsssuz~||{zyz{}~}{xxz||{zywvy~~}zyyyxyzz{~~~|{yxwwy{}}|}}}{z{{{}~}}~~}}~~~}||{{||{{}|zzzz~}}}}}~~~~~~~}zxx{}|xtsuwwxz{{{|zyz|~~~|zz{|~}yxyywtsssuz~||{yyz{}}~~{xx{}}{zywvy~~|zz{zxyzz{~}|{ywvxz{~}|}~}z{{{{~~}|}~~}}~~|||{{||{{~|{{z{~}}}}}~~}~}}~|ywx{}~|wttvxxy{{z|{yzz}~~|z{||~~|yyzzwttsrt{~~}}|zyy{|}~~{xx{~}{{zwvy~~~}z{{{yy{zz~~~|{yxvwyz}}|}~{z|{{}~~{{~~~}~~~}||{{}}{{}~{{zz|}}}}}}}~}}|}~zwwy|~~~zutvxxwz{zz}}yyz{~}~{{|||~~~|yz{{wttsru|~~}~|yyy{|}}{xy|~~{|zwvy~~~~}{{||yz{zz}~||zxvvyz|~|~|{|{{|~~zz}~}~}~~~~|}|{}}|z}||{z{~}}}}}~~}~}|}~|xvw{}}~~xtuwyxx{{z{{yyz|~}{|||}~~~~{y{|{vuusrv~~~~~~{yxy{||}{xz}}{|zwv{}}~}}||}|y{{zz~|{zxvvyz}}}{{|{{}~}yy}}}~}~~~}|}{|}}{z~~||{z|~}}}}}}}||}~zwvx{~~}|vuvxywy{{z}~}zyzz~}}|{}||~}}}|y{||xuvtrt|~}~~|yxyz{||{xz}~|}{xw{}}~}}|}~|y{|{{~}{zxuvyz~}}{||{{~~~|xy}}}~}~~~}|}{}~}z{~|}{z}~}}}}}~}~~}{|}}xuvz|}}yuvxzxw{{z{~{yzz|~|{}}|}|~~}~z{|}zuvussy~}~~}zyxyz||}xy||}|ywz~|}~}}~}y{|{{~}|zxuwyy~|}{|}{{~~~|wy}~}}~~}~~}}}{}~|z|}}}{z~~}}}|~~~~|{|~|wuwz|}~~xuwyzwy|{z|}~~zyzz~}}~|~||~|~}}|z|}~yvwtst|}}|yxyy{||{xz}~|~|xw|}|}}}~|y||{{~}{zxuwyz~|~{}}{{}~{wy}~|}~~}~~}}}|}~|z|}}}{z}~~}}}|}~~}{|}}xuuy{}}zvwxzww{{z{~}{yzz|||~}|}|}~}z{}~|vwvssw~|~~~zyxyz|{}~yy||}~{xx~|}~~}~{y}|{|~~}{zxuwxz|{}}{{}~zvy|}|~~}}~}}}|~~|y{~~~{z}~~}}~|}~~}{|}}xuuy{~|{vwxzxvz{zz||yzz{}~}}~||||~~}~|z}~yvwust{~}||zxxyz{{}xz}|~}zx{}{~}~zz}|{}~}}{zwvxx{}|~{}|{{}~yvy}~||~~~~}}~}~}|~~{y}}}{z~~~}}~|~}~|{|~|wtuy{}}zwxyzww{{z{}|{y{z|}|~~|}|}}}{{}}wxwtsv~~|}{yxxy{z|zy|~|}|yy~~||~~~{z}|{|~~}|{xuxxz~~|{}|{{}~yvx|~||~~~}}~}~~|~~{y|}~{z~~~~}}~|~}~{|}~|wtuy{}}zwxyzvw{zz||}{y{z}~}}}|~~|~}}}z}~{wxvssz~||}zyxxz{z}y{}~|}zx|~}{~}~y|}{{~~}|{zvwxy}~|~{}}{z~~{vwz|{}~}}}}~}~~|y{~~~{z}}~}~|~}~||}~|wtuy{|}yxyyzuw{zz|{}~zz{z}~~~~}}|}|~~|}|{}ywxust|~||{zxxxzz{{y|~}|}zy~~||~|y}|{|~~}|{yvxxz~}|{}|{z~yvx{~{{~~~~|}~}~}~{x|}~{z~}}}~|}{|}~{vtvy{|~yxyzyuwzzz}{}~zz{z}}~}}|~~|~~|}}{}~{wxvssz~|{~}zyxxyzz~}y{}~{~~{y|~~}{}~~y{}|{~~~}|{wwyy|~|}||}{z|}wvy}|{|~~}}}~~~}yy~}}{{}~~}~}|}{|}~{utvx{|~yxyzyuwzzz}{}~zz{z}~}~}}|~|~~|}~{}~{wywtsy~|{~~{yxxyzy}~yz}{~~|y{~~~~{|zz||{}~~~||zwxyz~}{{}|{zzvwz~}{{}~~~|}}~~~|x{~~|z}}~}~}}~}~|}~~yutwx||~yyzzwtxzyz~{}}z{{{~}}~~~}|~}~}|}|{}zxyvst{~{|~}{yxxyyz~|y{}}{~~{y}~~~}{}~~y{|{{~~~}||xxyy|~~{|}|}{z|~xwx|~{z{~~}}}}~~}yy}}}{{}~}~}|}|}~{vtvx{|~yyzzxtwyyy~{}~{{{{}}}~}|~}~}|}|{}zxyvst{~{|~}{yxxyyz~|y{}|{~}{z~|{~~|y{|{|~~~}|zxyyz}~|{~{}|zz{wxz}|zz}~}}}~~{xz~}~~|z~~}~}}|~}~|}~}wtuwy~}}{yzzytuxxy||||{{{}~}~~}|}~}|}|{}zxxvsu|}{|~}{yxxyxz~{y{}~{|~}{{~||~{y|{{}~~}|zxyy{}}{{~{}{z{zwx{}{z{~~~}}}~~~yx|}}{z~~~}}|}|}~{vtvxz|~zzzzwtvxxy~{}~{{{{~}}~~~||~}|~|{~~yxxusu|}{|~}{yxxyxz~{y{}~{|~}{{~||~|y{{{|~~}|{xyyz|}{z~{||zz|wxz}|zz}~}}}}~{xz~}~~|z~}}}|~~~}}~~xuuwx}}}|yzzyutwxxz}|}||{|~}}~~~||~}|{|~}yyxusv}}{|~}{yxxxxz~{y{}~{{~}{{~||~|y{{z|~}}{xyyz|}{z}|||zz~}xxz||zz|~~}}}}~~|xy|}~~|z|~~~}|}}}~{wuvwz~}~zzzzwsuwwx}|}|||{}}|~}|~~|}~{|~|xywutx~|{}~}{yxxxx{~zz|}}z|}|{}~~||~{y{zz}~~}}zyzyz||zz~~{|{zz|xxz|{yz}~}}}}~~{wy}}~~|z~}~}|~~~~}yvuwx|~}}z{zyutvwwz}|~}|}||~|}~~||||{|~}yyxutw}~}{|~~|zxwxwz}zz{}}z{}|{}~~||~{z{zz}~~}}{yzyz||yz}~||{yz|xy{|zyz}~}}}}~~~~zwz}~}~}{{~~~}|}~}~}xvvwy}~}~{z{zwsuvvx}|}}}}|}}|}~}|}|~}{}~~zyyvtv{~~~{|~}{yxwwy}~{z{|}z{}|{|~}|~{z{zz}~~}}{yzyz|{yz~~||{y{{yy{|zyz}~}}}}~~}ywz}~}~}{{~~}||}~{wvwwz~~~}~z{{zvsuvvy~|~~}}||~}|}~||}|||}}yyxvtw}~}{|~~|zxwwwz}zz{}|z{}|{~~}}~{z{zz~~~}}zzzyz{{yz~}||zy{{yy{|yyz}~}}}}~~~ywz}}}~}{{~~||}~~~}xvvwy}~}~{{{zwstuvw||}}}}|}}|}~}|}|~}{}~~zyywuu{~~|{}~~{zxwwx{}}z{|}{z|}||~~}}~~zzzz{~~}|zzzz{{zxz~}||zy||yz{{yxz}~}}}}~~~ywz}}}~}{|~~}|}~~~~yvvwx|~}~|{{{xttuuv{}}~}~}}~|}}~|}~|}||}~|yyxvux}~~}{|~}{yxwwy|~{z{||yz|||~~~}~~}zzzz|~~}|z{zz{{yx{~|||zy}{zz|{xx{}~~}}|}~~}xxz}~|}~}{}~||}~}ywwwy|}}~~{{{{wstuuw}|}~~~}~}|}}~}|}|}~|}~~{yyxvvz}~~|{}~~|zxwvwz}}z{||zy{|||~}}~zzzyz~~}}{{{z{{ywy}}||zy||zz{{xxz}~}}|}}~}yxz|}||~}{}~||}~}ywwwy|}~}~{{|zwsttuw}|~~~~}~}|}~~}}}|~}|}~|zzywux|~~~||}~~|zxwvx{}{z{|{yz|||~~~}~~|zzzz}~~~}{{{zz{zxx|}||{y{~{z{{yxy|}~}||}}~zwy|}||~}||~||}~}ywwwy|}~~~}{{|zvsttux~}~~~~~~}|}~~}}|}||}~|yzyvvy}~~}||}}{yxwwy|~}zz{|zy{|||~~~~{zzyz~~}}{{{z{zywy}}||zy|}{{|zxxz|~~}||}}}yxz|}||~}|}~|}~|ywwwy|}~~~}{||zvtttux~}~~~~~}||}~~}}}|~}|}~|zzywvx|~~~||}~~|zxwvx{}~{z{|{yz{||~~~~~}zzyz|~~}|{|{z{yww{~~||{zz|{{{yxx{}~}|||}~{xxz||{}}||~|}~~~|yxxxz|}~~~}{||zvttttx~}~~~~~}||}~~}}}}|~}|}~|zzywvx|~}~~||}~}{ywvwz||z{|{yy{{|~~~~~~~{zzy{~~}}|||{{zxwx|}||zz|~|{|zxwy|}~}|||}~zxy{|{{}}|}}|~~~~{yxxxz|}~~~|{||yutttuy~}~~~}||}~}}}~|}~}|}}{zzywwz}}}~~||}~~|zxwvxz|~{z{|zxy{{|}~~~~}{zyy|~~}|||{{{ywvy}~||{zz}||{ywxz|~}}|||}|xxz{{{|}||~|}~~~|yxxxz{|~~|{||zvttstx}~}||}~}}}~|}~}}}}{zzyxwz}}}~~||}~~}zxwvxz|}{{{|zxy{{|}~~~|{zyz~~~}|||{{zxvw{~~}||{z|}||zxwy{}~}|||}~zxy{{z{}}|~}}~~~~~{yxxy{|}~~}{||{wttstv}}~}||}}~}}~~}|~}}}}{zzyxwz}}}~~||}~}zxwvxz|}{{{{yxz{{}~}~~|{zyz~~}}|}|{{zwvx{~~|||zz~~}||ywwy{}~}|||}}yxy{{z|}||~}}~}~}zyxxy{|}~|{||zvttstx~~}||}~}}~~|}}}}}{zzyxx{}}}~~|}}~}zxwvxz|~}{{{{yxyz{}~}~~|{zyz~~~}|}|{{zxvwz}~|||{z|}}|zxwx{|~}||||~~zxyz{z{|}|~}}~~~}|zyxyz{|~}{||{xutssu{~~~}||}~~|}~~|}}}}|zzzyxy|~}}~|}}~}{xwwxz|~~|{{{{yxyz{}~}~}{zyz~~~}|}}|{zxvvy|}|||{z|~}}{xwxz|~}}|||~|yyzzzz|||}~}~~~~}~}{yyxy{{|~~|{||{wutstw}~~~|||}~}|~}|}}}}|zzzyxz}~}}}|}}~}zxwwyz|~~|{{|{yxyz{~~~~}{zy{~}}}}}|{zwuwz|}|||{{}~}}zwwxz|~}||||~{yyzzyz|||~~}~~~}|zzyyz{{}~}{|}|yvutsuy~~~}|||}~~|}~|}~~}}|zzzyxz}~}}~~}}}}{ywwxy{}}|{{|{yxyz{~~}}|zy{~}}}}}|{zwuwz|}|||{{}~~}zwwxz|~}||||~~zyyzzy{||}~~~}}~|zyyyz{|}~|{||{wutssw|~~~}|||}~||~}}~}}}{zzzyy|~~}~}}}~~|zxwxy{}}|{{|{yxyz{}}}|{z{~}}}}}|{zwuwz|||||{{~~~}zwwxz|~}||||~zyyzyy{||}~~~~}~}{zyyzz{|}~}{{||zwutstx~~~~|||}~~||}}~~}}|zzzyyz}~~}~~}}}~}{ywwxy{}}{{{|{yxyz{~~~}|zz{~}}}~}|{ywuwz|||}|{{~~}zwwxz|~}||{|~zyyzyyz||}~~~~}}~|{zyyz{{}~}|{|}{xvtssv|~}|||}~|{}}}~~}}{zzzyy|~~}}~}}}~}{xwwxz{}|{{{{zxxyz|~~}|zz|~}}}~}|{yvuwz{||}|{|~}zwwxz|~}||{|~zyyzyyz||}~~~}}~~|{zyyzz{|~}|{|}|yvutsuz~~|||}~}{|~}}~~}|zzzzy{}~~}~~}}}~~|zxwxyz||{z{|{yxxyz}~~~}{zz~~~}}~~}|{xvuwz{{|}|{}~|ywwxz}~}|{{}}zyyyyyz||~~~~}}~~|{zyzzz{|~~}{{|}{xvuttv|~~}|||}~|{}~}}~}{zzzzz|~~}}~~~}}}}{yxxxyz|{z{{{zxxyy{~~~|{z|~}}~~~}{yvuvy{{|}||}}zwvxy|~~}|{{|~{zzyxxz{|~~~}}~~|{zzzzz{|~~}{{|}{xvuttw|~~}|||}~~|{}}}~~~|zzzzz{~~}}~~~}}~~}{yxxxy{{{z{|{yxxyz}~~}{z{~}}}~~}|zwuvxz{{||||~{xvwy{~~~}|{|~~{zzyyxz{|}~~}}~~}|{zzzz{|~~|{{||{xvuttw|~~}|||}~~{{}}}~~}|zzzzz|~~}~~~~}}~~|zxxxxz{{zz{{zxxxy{~~~}{z|~}}~~~}|yvuvxz{{|||}}zwvwy|~~}|{{|~~{zzyxxz{|~~}{{yzzyyz{yzzzzz{zxvvwwxxyzz{~~~}}}~}}}}|zzzy{}~~~~}zywwx{}||{zyz{}~{zz}~|{z{zyxvvy{}}}||{z|}~~}~~}||{yxy{{{zzzz{||z{||}~|||~~|{zyxxwwvuuuttwxwwz~}{z{|}|{}|}~}}}|~}||}|yy{zzyxwvuwzz{{{yy{|}zy{|}}~}}}~}}}|{{}~~~~}|}~~~~|{{||}~}~}||{{}~~~|{yz{zyxyxyyy{|~~~~~|{zzyyxvsqqssrtuuuw}}~|z{{{{|}}~~~}}|}~}{zzywvuvvtuwz{{zxyyy{|}}|zzzyywvuvwxz{}~}|{zyzzxvsrsuvwxyyyz}}|}~}{{{{ywwvvussqpppqqrpppty||{||{{|}}~}}~~}|{{}}||{xxxyyvuuvwy|~~~~~~~~}{{}~}}}|zxwuuuvwyxvvxz||{||{{zz|}{{{z{{{}~}}}|{zwvvvvutstttvvwwxz}~|~}|zxwuutrqpppstttuxzyxxyxwvxyzzzzyyxwvvxxxvvwvutuussssrrsvwxzyyyz}~}}}~{{{|zxvuussssrrrqsrppruwwutuuuuvxzzz|~~~~~|{|{zyz{zzzzyxvwyz|{zzy{~~~}|||{zyxwuttuvwxxzxvvx|}}{z{{{{|}~}||{|||||}{yxxyxwuuvvxxyxvuuvxzywwxz}}|}}|{{zywuutsssstrooptvwutuutvy|~}{{zyyyyzzywvwyywvvvuuuvutrsttvvuvwz}}{yywvvwutuvwvuuuttuuusqqruxyywxxwwy{}}||{zzz{}}{yxxz||{{}|{{{|zyxz{|{z|}~}}|}}|zzyyxvvvuuutuusstwy{|{}}||}~~}|{{{|~~|zzzzxwvxwvvutsrrtuvvuvy}~}|{z{}~|||{zwwvuuutssqqrtwxxwvwwvwyzzywwuvvvxz{zzyz{zwvwxwwvvuttvwxywvx{~~|{ywvvwvvvvvtrqrrssqrqpqswz{{zyyyyz|}~||{{}}}~}|z{|{zwwwxxxyxwvwy{~}{|~~}}}|||{{{{yxvvvwyxwvuttvz}}|{yyxxz|||{yyz}~}{||{xwvwwwxxwvuuvxywvx{|}}~|{{zzzywyxxvsssuvuuutrstxzz{xwxwwy{||zwwwxxy||zxwwwvvtsuuuvwwxwwyzzyx{~~~{yxxwvwuvwwwussuvvtssrqrtwxyxvvwvwy{~}||}~~~}|{zyywxzyyyyyyxy{{zxy|}{zyxwyxwxxyxvvxz{zxwwvvxz|}}{z{{z{|}~|{z{|}}~}|{yywvuuxyxxyxxvwyyyxwz}~~}{zyxyzyyzzyvtuvwxtsrqqrtwyywvwyyyz|}{yyxyz{{}|{{zyxwutuvwvwwwutvwxxwy|~}{ywvussuuwxyywtuwyyvttsstvy{{yxwyxxz{}}{zy{}}}~|}||{zyxxzzz{{zyxzz{|{|~}{zzywwxxxyzzxuvwyzxvvuuvx{~~}{{|{z|}~|{{|~}}{zxwvtuvvuvwvvtvvwyy|~|zyyzxxyyz{{{xuvxyyvtutstvxzzywxyxwxy{zwwwxz{|~~|}{zxwvuvwwwxxxxvwxyzz|~~~}zxvvvtsuuvwxwustvwwusuutuwz{{zyy{zyz{}~|{{|~~~}}|{xwwvvxyxyzzzyz{|}}}~~}{yyywvxxyz{{yvwz{|zwxxwwx{||{yyz{yyz|}|||}~~{yvvutwxwwxxxuuwxz{|~~~}zyxxwtuwwyzzxutwz{zwvwvvvxz{zywy{yxyz{{yyyz|||~|{{ywuutstwwwyzzxvxy{}~~}~}zxwvvtrtvwyzywtuxyzxuvvuuuvyzyxwz{yy{}~~}}}~~~}{yxxwvxyxy{|{xxyz|~~~}~~|ywwwvtsvwx{}{ywz|}~|zzzzzy{}}|zz||yy{|~~}||~~~~}{xwvvvuwxwxzzxvvwy|~~|zyxxwutwxy{{ywvxz{{xvwwwvuxzzyyz|{xyz{}}|{{|}}~}|zxwuuutvvvwzywuuwy}|}}zxvvuttrsvxy{{ywwz{||xwxyxwwyzzyxz|zyzz|~}}||~~~~~|{yxxwxyyy|}|ywy{}~~~}~{zyywwutvxy{|{ywx{|}{xyzzyyz|}|{{}}zz{|~~}}~~~}{ywvuutuvuxzywttwz~~}~}zzyyxwvtuxy{}|zxx{|~}zxzzxwvyzzyxz{yxxy|~}|{|~~~}{zxwvvuuvvx{{yvtwz~|}}yxwvuttrsuwx{{ywvy{}~{yz|zxwy{{zz|}{zz{~}}~~}|zywwwwvwwy}~|yvy|~}~~~||~{zzyxvvuuwy{}}{zy{}|z{}{ywx{{zz|}{yz{~~}}{zxvvwvuuvx{{yvtwz~}~~}~~~|xxyxvuutuwxz||{zy|~}z|}{xvx{zzz|}{yyz~~||}}}~{yxwuuwvuvwy||yvux|}}~|}~}}{xwxxuttstvxz||{zx{}}z{|zwuwzzz{|}{{}~}{{ywwyxvvxz}}zwux}}|}~~|}~||~~}}|xxyywuuuuwy{}~}}{||}}ywx{{{||~~{z{~~|}~}|zzywvxxuuwy{|zwuv{}{}~~~}~}}zxyzxvuvuvxz{}}|zy|~{z||xvux{z{|~}z{}}}~}~|yyxwuwxvtuxz{zxuty~{{|}}~~|||{z{yvvxxutuuuvxz|}~}z|}~|xuvyz{|||{}}~~~|z{zxxzzwvy{|}|yvx}|zz{}|z}~~}}}|||yxy{xvuvwvwz{}~}{~~}yvx{|}}~|{~}~}~~zyzxvvxwttwyz{ywux{yz{}~|~}||{{{yxz{yvuwwvxz{}|{~}}}zvtvz{{{|~zz|}|}}}}zyyxwwxwuuyz{{ywuy~{yz{~{z}}|{zyyywwyyvtuvuuwyz}||~zwux{|}}}~~~~|yzzxxyzxvx{||{zxw~{xwx{~~|yz~}{{{z{zyz{zwvwxwwz{}}}xvvz}}}~~}~~~~}zyzyxxyxuvyzzzywvy}yxxz~~{z~~|zzzzzyyzzwuvxwvxz|}}}yutw|}|}~|}}|||}|zxyyxxyywvxzzzzywy{yx{~~|z}~{yyxxyxxzzwuvwwvwz{~~zvtv|~}~~~~~}{yzzzzz{xxz{{zzzxz~yvvx|}{{|}zx{~~{xxxxzzz{{xvwyxwy{}~zwtw}~~}|}}|zyyzzzzyxwyzzzzzx{~}}yvvy}}||~{y|~zxxxyz{{{zxvwywwyz}}|xusw}~~}{{||{yxxzzyyywwxyyyzzy}~~~zww{~~||}{y}}ywwvwyzzzywvwxwwxz}}{xusv}~||}||{zz||{zzyyzzzz||{~~~}|~}yuuy||yyz}~}ywz~~ywwwx{||{{ywxyxxxz~}{wtu}|{|{{zyx{|{zzzyyyxyz{|}~}|{|}zvtw{}~zy{}}yz~{wwwwz|}|{zyxyyxxy}~|zwtsy~|{{zz{zxz}|zzzzyxxxz{|~~}||~}yvw{}{xy|~{y|}xvvuwz|{yyxwwxxxxz~|{yvsu}{{{zz{yy|~|zz|{zyyz|}}{z{}{vux{}}yxy|~~|yy}{wvwwz}|{{zyxyyyy}}{yvsw~~|{zyz{yz}~|z{|zxxwx{}~}|zy{}yuuxz||wwz}~~|yy}zwvvwz~~{{{zxxyyxy}{zxusx~}zzxxzzyz~~|z{}zxxxy|~~|zz|}yvwz|~zwx{}~~|yz~~xvuuw{~}zzzywxyyxz}zyvtuz|{zyz{{z}~{{}|zxwx{}~~zyz|{wuwy|~|wvx{}}}{y{{wvuwy}|z{{yxyzyy~{zxutw~~{zxxz{z|}{}}{xwwy|}~}|zxxz|yvvxz|}xvwz|}}|yz~~ywvwz~~|||zxxzyy||zyvtv{{yxwy{{{~~{|}|xvvx{~~~{yyz|{wwyz|}yvwz|}~}{z}~~yvuvx|~{{|zxxyyy||yxvuv{|zxxz||}|}~|yvwy|~~~~{xxy{zwwxz{~~{xuvy{|}|zy|~~zwvwy}~|{|{xxzzy}|yxvvw{{yvwy{|}~{|}{wuvy|~~~~~ywxy{zxwxz{}zwuw{|}}}{z~}zwvx|}|}{xxzzz~{xvuvw|~zwvwy|}~|}}{vuvy}~}}~~yxxy{{yxyz|{ywuwz{|}}{z~~~~~}yvvx{~|{|zxxzz{~{wvvwx}{wvwz|~}~~{wuwz~~}}~ywwxzzyxxy{~zxvuwzz|}}|z}}~~~~~{xwx|}|}|yyz{|~|xvvwx{|wuvy{~}}}{wtux}||}~zwwxy{zyxxz~{ywvwy{|}~}{}}~~~~|ywwz~}|||yxz{{~~xvuwxz}xuuwz~~~|yuuw{|{|~}xwwy{|{yxy||ywvvxz{|}~|{~{|~}}}~{xwz}}|}{yxz{}{vuwxy}{wuwy|~}{wuvy~{{}{wwwx{|yxxz}~zwvvwxy{|~}{|}{~~}}~~{xy{}}}{yy{}zvvwyz~zuuwz}~|yvtvz}z{}~zwwwy||zxx{~ywwwxyz{}~~|}}|~}}~~zxy|}}|{yy{}yuvxyz~xuuwz~}{xutw{}{|~}ywwxz}|yxx{|xvwwxyz{}~}|~}zz~}{|~}zy{~}}|zyy|~}wuwyz}|wuwy}~{wuuy~zz|~|xvwx{}{xwy}~yvvwwxyz{}~}}~~|z|~|{}|z{}~}}{zy{~zuvyz|xuuwz}{xutw||y{~~|ywvwy}|ywx|{wvwxxyz{}~~~~}z{|{|~{{}~}|{yz}{uuwy{~zuuwz~~{xutv{~}z{~}{xwwy}~{xy||wvwxxxy{}~~~~|z{|z{~~|{|~~||{yz}{vuxz|zvuwz|yvtv|}~}zz}}|{xvwx|}{xx{{wuwxxxyz|~}}{yz~|{|~}~~}||z{{vuy{}yvvx{~zwutw}}|}~|yz~}|{xvwy}}zxy}~zvvxyxxy{}}}{z{~{{|~}}||{z|~yuvy|~~xuvx|}yvuux}|}|z|~||{xwwz~~{yz~}yvvyzxxy{}~||zz{~zz|~~~~~}{||z|~yvvz}~yvvy}}xvuuy|z|~~|z{}||{ywwz}}zxz}~|yvvxzxwyz|~~{{yyz~~{z|~||||}~zvwz}~zwwy|~yvuux|y{}}|z{~~|||zwwy|~|yz}|zwvxzyxxz|~|{zz{}|z|~~||}||~{wvy|~{xwx{zvuuw}zz}~}|{}||||ywy{~}{z|}{ywxzzxxy{}}{zyz{~}zz|~~~}||||~|ywwz~}zxxz~~yvuvz|y{}}}||~{||zxwy|}|z{}~|yxwxyxwwy{~|zxyz{|z|~}|}}~|yxy|~|yxx{}wuuw|zxz|||{||{||zxxz|}|z{~}{yxxzzyxxz|{yyy{||{|}|}}~}zxxy}~}{yxy|{vuux~zy{||||}|{|}zxy{}}|{||zyxyzzyxxz|}zxyz{|~{{}~~}||}~~|zxy{}|{yy{~yuuw{}yy{|||}}{{||yxy{}}||~~{yyxyzyxwy{~|yxyz{~}{|~~~}|}~}zyyz~~|{zyz}{vtvz~yxz{{|}~~|{||{yy{|}||~~{zzyzzzxxxz}}yxyz{}~||~}|}~~}zyyz}}{{{zz}|wtvy{xy{{}~~~~|||}|zz{|}}}~{zzzzzzyxxz|}ywxz{|~||~~~~~}|}~~|zzz{}}{{|{{}|wuvz{yyz{}~~}~~|{|}|zzz{|}~~{yzzzzyxwxy|}ywyz{|}}~~~~~}}}}{yz{|~|z{|||~zvtw|~zxxz{}}|}}|||}|zzz{|~}zz{{{zyxwxz~|xwy{|}~~~~~}}}~{yyz|}~{y{|}}~yvux~}zxxz|~|{||||}}|{{z{}~|zz{||zyxwxz~~|xwy{{|~~~~}}}}}~~{yy{}~~zy{}~~~zvuy}zxxy|~{z{||||}}|zzz|}|zz{||zyxwxz~~~~|xwy{|}~~}}|}}~zxy{}~zxz}~~zvux}{ywx{~{yz{|||}}|{zz{}|{z{}}{yxxxz}~~}zxy{|}~~}||}}~{xxz}{xy|{wvw}~|zxwz}}zyz|}}}~~|{zz}}|{z{}~|zyxxx{}}~}{xxy{|}}}||}}~ywy|}yxz~}zwvz~}{ywx{~{xxz|}}}~}|zy{~|{zz|}}{yxxwy}~}}}|zxxz{|~}|{|}~|xwz}|xx{|xvw|~|{ywx{~zwx{|}}}~}{zy|}|{z{|~}{yxxxz~~}}}|zxy{|}~}|{|}{wwz~{xy|{xwy~}|{xwy}|ywx{}}}~~{zz}|{z{|~}{yxxx{||||{yxyz|}~|{{|~~yvx|~yxz~}ywx|}||zwwz~~zwvy|}}}~}zy{}{zz{|~~|yxxxy}~|||{zyxz{|~}|{|}{vwzzxy}~zxwz}||zxvx|{wvw{}}}~~{yz~}{zz{|~}zyxxy||{|{zyyz{}~}{z{}|wvy~{xx{~zxxz}||{ywx||xuvz}~}~~|zz}}{yz{}~}{yxxy||{{{zzyy{|~}{z{||wvy~{xy|~zxx{|{|{ywx|{wuvy}~~~|yz}|zyz{}~{yxxy||zzzzzyy{|~}{z{}|vvzzxy}}yxy}}{{|{xwx}}yvtvz}~~~{yz~{yyz|~}{xxxz~~{zzzzzzz{}|zz{~yvw{~yxz~|yx{|{{|{ywy~|yvtvz}~~~{z{~zxyz}}zxxy{~{yyzzzzz|~|zy{~yvw|~zxz~{yy|~{z{{{ywy~{xutvz}~~{z{}yxxz}}{xxx{~zyyyzzzz{~|zy{~zvw{~zyz~|yy|~{zz{{yxy}|xutux|~|z{~zxxz}~|yxx{~|yxyz{{z{}~}zyz}{wvz{yz}}yx{{zz{{zyy{}yvttw{~~{{}{xxy{~}zxxz}}zxxyz{zz|~~~{yy{~ywx}}zz{~{yz~}zzz{{zyz}{wutux|~|{|}ywxz}~|yxy{~|ywxyz{z{}~}~~{yz||xwz|zz}}zxz{yyz{zyyz~~yvttuy}~|{||ywx{~~|yxy|~{xwxy{{{{~~}~~zyz}{xxz|z{~|yy|~zyyz{zyy{|xvttvz}~|{~{xwy|}{yxz}}zwwxz{{{|~}|~|yy{}yxy}}{{}}zyz|yyzzzzzz}zwutuw{~~}||}ywxz}~|zxy|~|ywwyz{{|~}||~~zyz}{yy{~|{|~{yy~}yxyzzzzz|{wuttvy}}||zwxy|}{yy{}zwvxz{||}~|{}{yz|{yy{~|||~{yy}}zxyzzzz{}|xuttvx|~}}zxwy|}{yy{}zwvxz{||}}{z}{yz|{yz|~||}}zyz~}yxyzzz{|}|wuttvx{~}}~zxwy|~}{yy|~}zwvxz{|}~}{z|{yz}~zz{}}|}~{yx{{xxyyzz{}~zvttuvx|~}}~ywxz}~|{z{~~|zwwx{|}~~|zz}~{z{~|zz|~||}}zxy|{xxyzz{|~~yvttuvx|~}~}ywxz}}|{z{~~|zwvx{|~~~{yy}~{z{~{z{}~||~|yxy}~zxxyyz{}~yuttuvx{~}}}yxxz}}|{{|~|zwvx{|~~{yy|~{z|~{z{}||~|yxy}~{xxxyz{}zvttuvwz}~}~zxxz}~|{{|~~}{xwxz|~~|yx{|{{~~{yz}||}}ywx{|yxyyz{}|wuttuvx{~~}~|yxy{~~}|{|~~}|ywwy{~}zxy}}{{}|zz|~~|}{xwy~~zyxyyz|~~zvttuuvy|~~}~~{xxz|~~}|{|}~}{ywxz|~~|yxz~}{|~~{yz}}|}}zwwz}zxxyyz|}yuttuvwy}~~}~}zyy{}~~~}|||~~}|{ywxz}}{yxz~}||}zy{~~||~|ywx||yxxyy{}|xuttuuwz}~~}|zyy{}~~}|||}~}|zxxy{~|zxx|~||}~{yz|}|}~zwwz~{yxyyz|}zvttuuvw{}~}~~{yyz|}~}}||}~~}|{yxxz}}{yxz~~}|}|yy{}||~{wvx}{yxxyz{~~zwuttuuwy|}}~~|zyz|}}}}||}~~}}|zyyz|~{yxy|~~}}|yyz~}||~{xvx}|yyxyy{~~{xutuuuvy{}}~~|{zz|}}}}||}~}|{zyyy|~~{yxy|~~~}~|yx{~~}||~{wvx}|zyyyz|~}zxuttuuvx{}}~~|{zz||}||||}~}||{zyz|}{xxy|~~~~~~{xy{~|||}~zvvy{yyyyz||ywuttuuvx{|}}|{z{|}||||}~~}}|{{zz{}|zxxz}}}~~}yxy}~|{{|}}xvw{~{yyyyz}}{yvutuuvwxz|~~||{{|||||||}~}||{{zz{}|yxxz}~}}~|yxz}~|z{|~}xvw{{zyyy{~}zxvuttuuvxz|~}||{{||||{||}~}||{{{{|}|yxxz|~}}~|xwy}~{zz{}}yvw{|zyyyz}}zxwuutuuvwy{}~|||||||{{{|}~}||{{{{|}}zxxy|~}}~}ywy|~{yy{}zvvy}{zyyz|~{ywvuuuuvwxz||{||||||{{|}}||{{{|||~~{xxyz}}|}~zwwz~}zxy|~|xvw||zyyy{~|yxvvuuuuvwxz}}{{||}||{{{|}~}|{{||||}}zxxy{~}|~}ywx{~{yxy|{wvy~~|zzyz|{ywvuutuuvwx{~}{{|}}|{{{{|~~}||{||}}~|yxxz|~~||~|xwy|}zxxz}~zwwz~{zyyz}~zxwvuutuvvwy{~|{|}}}|{{{{}}||{{|}}}~{xxyz|}|}~zwwz~|ywx{~|xwx}}{zyz{~{ywvvuuuuvvwy}}{{|}}|{{z{|~~||{{|}}}~|yxxz{~~||~{xwy}}ywwy}}ywx|~|{zz{~}ywvvuutuuvwx|~{{|}}}|{z{|~~}||{|}~~~}zxxy{}}|}|xwx|}zwvx|~zwx{~|{zz{}}ywvvvutuuvvx{~{{|}~}|{zz{~~}|{{|}~~~}zxxy{}}|}{xwx|}yvvx|}zxx|~|{zz{~}ywvvvuuuuvvw{}{z|}~}|{zz{~~}{{{|}~}zxxz{}}|~zwwy}|xuux}|yxy~~|{zz|{xvuvvuuuvvvx||z{|~~}{zz{|~||{|}~~|yxyz{}~||~}ywwz~}zwuvy}~{yy{~}{{{}{wuuvvuuuvvvy}|z{|~~}{zz{}~|{{|}~~{yxyz|}~|}}ywx{}zvuuy~}{yy|~}{{{}zwuuvvuuuvvvx}|z{|~}{zz{}}|{{|}~}{yxyz|}~}}}ywx{~}zwtux}}{yy|~}|{{}{wuuuvuuuvvvx{}{z|~~|{zz|~|{{{}~~|zyyz|}~}}~~ywwz}~zwutw{}{zz{~}||}}yvuuvvuuuvvwz~{z{}~}{zz{~}|{{|~~|zyxy{|~~}}{xwy||yvtux}~|zz{~~|||~{wuuuvuuuvvvx|}{z|~~|{zz|~|{{{}~}{zyyz{}~~}~~zwwz}~{wutvy~}{zz|~}||}~zvttuvuuuvvwy}|{{|~~|{z{}~|{{|}~}{zyyz|}~}}~ywxz~~zvttvz}zzz}~}||}}yvttuuuuuvvwz~|{{}~}|zz|~}|{{}~}|zyyy{|}~}~{xwy||xutux|~{zz|~}}}{wutuuuuuvvwx|}{{|~~}{z{}~}{{|}~|{zyyz{}~~~~}yxx{~}yvttvz~{zz{~~}}|xuttuuvvvvvx{~|{|}}|{{|}|{|}~|{zyyz{}~~~~zxxz~}yvttvy~|zy{~~}}|yvttuuvvvvvx{~|{|}~~|{{|}|{{}~|{zyzz{}~~~~}zxx{~}yvttvy}~{yz|~~~|yvtttuvvvvwx{~|{|}~~}|{{}}|{|}}{zyyzz{}~~~~}yxy||xuttvz~}zyz}~~~{xuttuuvvvvwy|}|{|~~~}|{|~}|{|~}{zyzz{|}~~~{yyz}{wutuw{|zy{~}zwuttuuvvvvxz}}||}~~~}|{|~~}||}~|zyyzz{|}~~~~~{yy{}~zwttuw{|yy{}zwutttuvvvwxz~~}||}~~~}||}}||}|zyyzz{|}~~~~~~{yy{}~{wutuwz~|zyz~}{xvtttuvwvwxz}}||}}~~~}|}~~||}~}zyyzz{|}~~}~~~|zyz}{xutuvy|}zyz}~{yvuttuvwwwwy|~}|}}~~~}}}~~}||}~{yyyz{||}~}}~~~~}{yz|~}zvttvx{~|yy{}zxvtttuvwwwx{~~}|}}}~~~}}}~||}}zyyzz{||}}}}}~~~~|zz{}|xuttvx{~~{yy}|ywutstuvwwwy|~}}}}}~~~}}~~}}}|zyyz{{|}}}}|}~~~~|zz{}~{xutuwy|}zyz~~{ywutttvwwwxz}}}}}}~~~~}}}}}~~{yxyz{||}}}||}~~~}{z{}|yvttvxz}~{yy||zxvtttuvwwxy{~~}}}}}}~~~~~~}}}}zxxz{||}}}|||}~~~~{z{|~}zwutuwy|~|zy{}zxvutttvwwxy{~}}}}~}~~~~~~}~}zxxyz{||}}|{{|}~~~|z{|~}zxutuwy{~|zy{}zxwutttvwxxy{~}}}~~}}~~~~}~}{xxyz{|}}||{{|}}~~{z{|~}zwutuwy{}|zy{}zxwutttuwxxy|~~}}}~~}}~~~~~}zxxy{||}}||{{{|}~}{{{}~|ywuuvxz|~~{zz|~{yxvutttvwxyz}~}}~~~}}~~~~|zxxy{||}||{zz{|}~~|{{|~}zxvuuvxz|~}{z{~}{ywvuttuvwxz{~~}}}~~~}}~~~~|yxxy{|}}||{zzz|}~|{{}|zxvuuvxz|}|{z{~}zywvuttuvwyz|~~}}}~~~}}~~|zxxy{|}}||{zzz{}~|{{}}zxvuuvxz|}~}{z{~}{ywvvutuvwxz{~~}}}~~~}~}zyxyz|}}||{zyy{|~~|{{|~}zxvuuvxz{|}}{z{}~{yxwvuuuuwxz{}~}}}~~}}~~{yxxy{|}||{zyyy{}|{{|~{ywvuvwyz|}~~|{{||zxwvvuuuvwyz|~}}}}~~~}~}zyxyz|}}|{zyyyz|~~|{{|~zxvuuvxy{|}~}{z{}~|yxwvvuuuvxy{}~}}}}~~~~}~~|zyxyz|}}|{zyyyz|~}{z{}}zwvuvwxz{|}~~|{z|~}{yxwvuuuvwxz|~~}|}}~~~~~}{zxxy{|}|{zyyxyz|~|z{|~~{xvuuvwyz{|}~~~}{z{}|zxwvvuuuvwy{}~}||}~~~~~~|zyxyz{|}|{zyxxy{~|{z{}|yvuuvwxz{||}~~}|z{}|zxwvvvuuvwy{}~||||}~~~~}{yyyz{|}|{zyxxyz}}{z{}}ywuuvwxz{{||}~}|z{|}zxwvvvvvvwy{}~|{{|}~~~~~}{zyyz{|}|{zyxxxz}|zz{}|yvuuvwxz{{{||}|{z{}|zxwvvvvvvwy{}~|{{|}~~~~|{zyyz{|||zyxxwxz}~|zz|~{xvuuvxyz{{{||||{z{~~{yxwvvvvvwxz|}{z{|~~~~}|zyyyz{||{zyxwwx{}}{zz|~zwuuuwxyz{{{{||{zz|}zxwvvvvvwwxz}~|zz{|~~}{zzyyz{||{zyxwwxz}}zz{}~zwuuvwxz{{{{{{{{zz|}zxwvvvvwwwxz}~|zz{|~~}{zzyyz{||{zyxwwxz}}zyz|~zwuuuwxyz{{{zzzzzz|~{xwvvvvwwwxz}|zyz|}}|{zzyz{||{zyxwwxy|}{yz|{xutuvxyz{{zzzzzzz||ywvvvvwwwxy|~}zyy{}~~|{zzzzz{||{yxwwwx{~|zz{}}zvuuvwyzz{{zzzyyz{}}zxwvvvwwxxyz}~{yyz{}~~}{{zzzzz{{{zyxwwwy{~{yz|~|yvtuvxyzzzzzyyyyz{~|zxwvvvwwxxy{~}{yyz|~~|{zzzzz{{{{zxwwwxy|}zyz|~{xutuvxyzzzzzyyxyz|{ywvvvvwxxyz||zyyz|~}|{zzzzz{{{zyxwwwxz}|zz{}}ywutvwyzzzzzyxxxy{~}zxwvvvwxxyy{~}{yxy{}~~|{zzzzz{{{{zxwwwwy|}zyz|~zwutuwxyzzzyyxxxxz}~{ywvvvwxxyz{}~{yxyz|~~|{{zzzzz{{zzyxwwwy{~}{yz|~~{xvuuvxyzzzyyxwwxy|~{ywvvvwxxyz{}~{yxxz|~~|{zzzzzz{{zzyxwwwy{~}{zz|~{xvuuvxzzzzyyxwwxy}~{ywvvvwxyyz{}~{yxxz|~}|{zzzz{{{zzyxwwwwy{~}zz{}}zwuuuwyzzzzyxwvvxz~}zxvvvvwxyz{|~}zxxxz|~~}{{zzzzzzzzyyxwwwxy||zz{}~|ywuuvwyzzzyyxvvvx{}zxvvvwxyz{|}|zxxxz|~~|{zzz{{{zzzyyxwwwxy|~{zz{~~{ywuuvwyzzzyxwvuvx{|zxvvvwxyz{|}|yxxxz|~~|{zzz{{{{zzyyxwwwxy|~|zz{~~|ywvuvwyzzzyxwvuuwz}zxvvvvwyz{|}|zxwxy{}~~|{zzz{{{{zzyyxwwwxy{~|zz{}~|ywvuvwxyzzyxwvuuvy}~{ywvvvwxz{|}}{yxxyz|~}|{zzz{{{zzyyxxwwwxz|~{z{|~}{xwvvvwyzzzyxwuuuw{}zxvvvwxyz|}~|zxwxy{}~~|{zzz{{{{zyyxxwwwxy{}}{z{}|zxvvvwxyzzyxwvutux||ywvvvwxy{|}~{yxwxy{}~~|{zzz{{{{zyyxxwwwxy{~|{{|~~{ywvvvwxyzzyxwvutvy~~{ywvvvwyz{}~}{yxxyz|}~}{zzzz{{{zyyxxwwwwyz|~~{{{||zxwvvvwyyzyxwvutuw{}zxwvvwxz{|~|yxxxy{|~~|{zzz{{{{zyxxxwwwxy{}~|{{|~}{ywvvvwxyzyyxvuttvz~}{xwvvwxy{|}|zxxxyz|}~~|{zzz{{{{zyxxxwwwxy{}}{{|~}{ywvvwwxyzyyxwuttuy~~{ywvvwxy{|~|zxxxyz{}~~|{zzz{{{{zyxxwwwwxy{}}{{|~}{ywwvwwxyyyyxvuttuy~~{ywvvwxy{|~|zxxxyz{}~~|{zzz{{{{zyxwwwwxxy{}~|{|}~~|zxwwwwxxyyyxwvtttvz}zxwvvwyz|}~{yxxxyz{}~}|zzzz{{{{zyxwwwwxyz|~}|||}}{yxwwwwxyyyyxwutttw{|zxwvwwyz|~}{yxxxyz{}}{zzzz{{{{zxxwwwwxyz|~}|||~}{yxwwwxxyyyyxwutttw{~|zxwwwxy{|~}{yxxxyz{}}{zzzz{{{{zxwwwwxxyz|}}||}~}{yxwwwxxxyyxxwutstw{|zywwwxy{|~}{yxxxyz{|~~|zzzz{{{{zyxwwwwxyz|}}||}~~{yxwwwxxxyyxxwutstvz~}{yxwwwxz|}~|zxxxyyz{}}{zzzz{{{zyxwwwwxyz{|~~}||}|zxwwwxxxyyyxwvutsuw|~|zxwwwxy{}~}{yxxxyzz|~~|{zzzz{{{zyxwwwwxyz{}~}||}~~{yxwwwxxxxxxxwvtstuy}~|zxwwwxz|}|{yxxyyz{|~}|zzzz{{{{zyxwwwxxyz|}~}||}~}{yxwwwxxyxxxxwutstvz}{yxwwxyz|~~|zyxxyyz{}~}{zzzz{{{zyxwwwwxyz{|~~||}~~|zxwwwxxyyxxxwvtstux|~|zyxwwxz{}|{yxxyyzz|}~|zzzz{{{{zyxwwwxyy{|}~~}|}~}zxwwwxxyyxxxwvutstw{~|{yxxxxz{}}{zyxyyzz{}~|{zzzz{{{zyxwwwxxyz|}~~}|}}}{ywwwxxyyxxxwvutstwz}{yxxxxz{}}{zyxyyyz{|~~|{zzzz{{{zyxwwwxxy{|}~~}|}~}zxwwwxxyyxxwwvutstw{~|{yxxxyz{}}{yyyyyyz{|~~|{zzzz{{{zyxwwwxyz{|}~~||}~|zxwwwxyyyxxwwvtttux|}|zyxxxyz|~~|zyyyyyzz{}}|zzzzz{{zyxxwwwxyz{|}~}||}~{ywwwxxyyyxwwvutstvy}}{zyyxyz{}}{zyyyyyzz{}}|zzzzz{{zyyxwwwxyz{|}~}||~}{ywwwxyyyyxwwvutttvz~}{zyyyyz{}}{zyyyyyzz{}}|{zzzz{{zyyxwwwxyz||}~}||~~{ywwwxyyyyxwwvutttvy}}{zyyyyz{}~~{zyyyyyzz{|~~|{zzzz{{zzyxwwwxyz{|}~}||}~|yxwwwxyyyxxwvutttvx|}|{zyyyz{|~|{yyyyyzz{|}~}{zzzzzzzzyxxwwxyz{|}}~~}|}~}zxwwwxyyyyxwvutttuwz~~|{zyyyz{|}}{zyyyyzzz{|~~|{zzzzzzzzyxxwwxyz{|}~~}||}|zxwwwxyyyyxwvutttux{~|{zyyyz{|~}{zyyyyzzz{}}|{zzzzzzzyyxxwwxyz||}~~}||}~{yxwwxxyzyyxwvutttvy}}{{zyyz{|}|zyyyzzzz{|}~}|{zzzzzzzyxxwwxyz{|}}~~~||}~}zxwwwxyyyyxwvutttux{~|{zzyzz{|~}{zyyzzzzz{|~}|{zzzzzzzyyxxwxxy{||}~~~}||~~{ywwwxyyyyxwvutttuwz~}{zzyzz{|~~|zyyyzzzz{|~~|{zzzzzzzyyxxxxxyz||}~~}||}~|yxwwxxyzyyxwutttuwz}}{zzzzz{|~~|zyyyzzzz{|}~|{{zzzzzzyyxxxxxyz{|}}~~~}|}~~{yxwwxxyyyyxvutttuwz~}{zzzz{|}~~|zyyzzzzz{|~~|{{zzzzzzyyxxxxxy{|}}~~~~}|}~}{ywwwxyyyyxwvutttvx{~|{zzzz{|}}{zyyzzzz{{|~~}|{zzzzzzyyyxxxxyz{|}}~~}||}|zxwwwxyyyyxwvtttuvy}}|zzzz{|}~}{zyzzzzz{{}~~}|{{zzzzyyyyxxxxyz{|}~~~~}||}|zxwwwxyyyyxwvtttuwz}}|{zz{{|}~}{zzzz{{{{|}~}|{{zzzyyyyyxxxyyz{|}}~~~}||}|zxwwwxyyyyxwvuttuwz}~|{zzz{|}~}{zzzz{{{{|}~~}||{zzzyyyyxxxxyyz{|}~~~~~}||}~}{ywwwxxyyyxwvutttvy|}{zzz{|}~~|{zzzz{{{{|~~}|{{zzzyyyyxxxyyz{|}}~~~}|||}~|zxwwwxyyyyxwvtttux{~}|{zz{|}~}{zzzz{{{{|}~~}||{zzzyyyxxxxyyyz{|}}~~~~}||}~}{yxwwwxyyyxwvutttvy|}|{z{{|}~}{zzz{{{{||}~}||{zzzyyxxxxxyyyz{|}~~~~}|||}~}{yxwwxxyyyxwvuttuwz}}{{z{|}~~|{zzz{{{{|}~~}|{{zzyyyxxxxyyyz{|}}~~~~}|||~~|zxwwwxxyyxxwutttvx{~|{z{{|}}{zzz{{{{||}~}||{{zzyyxxxxyyyzz{|}~~~~}|||}|zyxwwxxxyxxwvuttux{~~}{{{{|}~|{zz{{{|||}~~}}|{{zzyyxxxxxyyzz{|}}~~~~}|||}~}{yxwwxxxyxxwvuttuwz~}{{{{|~~|{zz{{{|||}~~}}|{{zzyyxxxxyyyzz{|}}~~~~~||||}~}{yxwwxxxxxxwvuttux{~~}{{{{}~}|{zz{{{||}}~~}}||{{zyyxxxxyyzz{{|}~~~~}|{{|}~|zyxxxxxxxxwwvuttvy|~|{{{|}~}|{z{{{|||}~~}||{{zzyxxxxxyyzz{||}~~~~}|{{||~}{zyxxxxxxxxwvuttuwz}~|{{{|~~}|{{{{{||}}~~}||{{zzyxxxxxyyzz{||}~~~~~}|{{|}~}{zyxxxxxxxxwvuttuwz~~|{{|}~~}|{{{{{||}}~~}|||{{zyxxxxxyyz{{||}~~~~}||{{{|~}{zyxxxxxxxxwvuttuwz}~|{{||~~}|{{{{{||}}~~}|||{{zyxxxxxyyz{{||}}~~~}|{{{{|}~|zyyxxxxxxxwvuutuvy|}|{||~}||{{{{||}}~~}}|||{zzyxxxxyyzz{{||}~~~~~}|{{{|}~~}{zyxxxxxxxwwvutuvx{~~||{|}~~}|{{{{{||}~~~}|||{{zyyxxxxyyz{{||}}~~~~}|{zz{|}~|{zyyxxxxxxwvvutuvy|}||||}~}|{{{{|||}~~}|||{{zyxxxxxyzz{{||}}~~~}|{zz{|~}|zyyyxxxxxwwvuuuuwz}}|||}~}|||{{|||}}~~}}|||{{zyxxxxyyz{{|||}}~~~}|{zzz{}~~|{zyyyxxxxwwvvuuuvx{~}|||~~}|||{{|||}~~}||||{zyyxxxxyzz{{||}}~~~~}|{zzz{|~}{zyyyyyxxxwwvuuuvxz~~}|||}~}||||||||}~~}||||{{zyxxxxyzz{{||}}}~~~}|{zzz{|}}|zzyyyyxxxwwvuuuvwz}~}|||}~}||||||||}~~}||||{{zyxxxxyzz{{|||}}~~~~~}|{zyz{|}}{zzyyyyxxxwvvuuuvx{~}}|}~~}||||||||}~~}||||{{zyxxxxyzz{||||}}~~~~}{zyyz{|~~|{zzyyyyxxwwvvuuuwy|~}||}~~}|||||||}}~~}||||{zzyxxxyyz{{|||}}}~~~~}|{zyyz{}~~|{zzzyyyxxwwvuuuvwz}~}}}}}}|||||||}~~~}}||||{zzyxxxyyz{||||}}}~~~~}|{yyyz{}}|{zzzzyyxxwwvuuuvxz}~}}}~}}|||||||}~}}||||{zzyxxxyzz{|||||}}~~~~}|zyxyy{|~}|{zzzzzyyxwwvuuuvxz}~}}}~~}|||||||}~~~}||||{{zyyxxyyz{{||||}}~~~~}|{yxxyz|~~|{zzzzzyyxwwvuuuvwy|~}}}~~}}||||||}}~~}}||||{zzyyyyyzz{||||}}}~~~~|{zyxxz{}}{zzzzzzyyxwvvuuuwx{~~}}}~}}|||||||}~~}||||{{zyyyyyyz{{|||||}}~~~}|zyxxyz|}~|{zzzzzzyxxwvuuuvwy|~}}~~}}}}}}||}}~~}||||{{zyyyyyzz{|||||}}}~~~}{zyxxyz|~~|{zzzzzzyxwwvuuuvxz}~~}~~~}}}}}}||}}~~}}||||{{zyyyyyz{{|||||}}~~~}|{yxxxy{}~}{zzzzzzzyxwvvuuvwy|~~}}~}}}}}}}|}}~~}||||{{zzyyyyzz{|||||}}}~~}}{zxxxyz|~}|{zzzzzzyxwvvuuuwy{~~~~~~}}}}}}}}}~~}||||{{zzyyyyzz{{|||||}}}~}}{zyxxxz{}}|{zzzzzzyxxwvuuuwx{~~~~~~}}}}}}}}}~~~}||||{{{zzyyyzz{||||||}}}~}|{zxxxxz{}}|{zzzzzzyyxwvuuvwy{~~~~~~}}}}}}}}}~~}||||{{{zzyyzzz{||||||}}}}}|{yxwwxz|~}{{zzz{{zyxwvvuuvwz|~~~~~}}}}}}}}}~~}|||{{{zzzzzzz{{||||||}}}}|{zyxwxyz|~~|{zzzz{{zyxwvuuuvx{}~~~~~}}}}}}}}~~~~}|||{{{{zzzzzz{{||||||}}}}|{zxwwxyz|~|{zzz{{{zyxwvuuuwx{~~~~~~}}}}}}}}~~~~}||||{{{zzzzzz{{||||||}}}}|{zxwwwxz|~~|{zzz{{{zyxwvvuuvx{}~~~~~}}}}}}}}~~~}||||{{{{zzzzz{{||||||}}}}|{zyxwwxz{~~}{{zz{{{zzyxwvuuvxz}~~~~~~}}~~}}}~~~}}|||{{{{{zzzz{{|||||||}}}||{yxwwxyz}~|{zzz{{{zyxwvvuvwy|~~~~~~}~~~}}}~~~}}|||{{{{{{z{{{{|||||||}}}|{zyxwwxy{}}|{zz{{{{zyxwvuuvxz}~~~~~}~~~}}~~~~}|||{{{{{{{{{{{{||||||}}}||{zxwwwxz{~~}{{zz{{{{zyxwvuvwx{}~~~~~~~~~~~~~~}}|||{{{{{{{{{{{|||||||||||{zyxwwwyz|}|{zz{{{{zzywvvvvxz|~~~~~~~~~~~~~~~}|||{{{{{{{{{{{|||||||||||{zyxwwwxz|~~|{{z{{{{{zyxwvvvwy|~~~~~~~~~~~~~~~}}|||{{{{{{{{{{||||||||||||{yxwwwxy{}~}{{z{{{{{zyxwvvvwy{~~~~~~~~~~~~~~~}}|||{{{{{{{{{{|||||||||||{zyxwwwxy{}~}{{{{{{{{zyxwvvvwy|~~~~~~~~~~~~~~~}}||{{{{{{{{{{{|||||||||||{zyxwwwxy{}}|{{{{{{{{zyxwvvwxz}~~~~~~~~~~~~~~}}||{{{{{||||||||||||||||{zyxwwwwyz|~~}|{{{{{{{zzyxwvvwy{~~~~~~~~~~~~~}}|||{{{{{||||||||||||||||{zyxwwwwyz|~~}|{{{{{{{zzyxwvvwy{~~~~~~~~~~~}}}||{{{{|||||||||||||||||{zyxwwwwxz|~~}|{{{{{{{{zyxwwwwy{~~~~~~~~~~~~}}||{{{{|||||||||||||||||{zyxwwwwxz{}~}|{{{{{{{{zyxxwwwy{}~~~~~~~~~~}}|||{{{|||||||||||||||||{{zyxwwwxyz|~~}|{{{{{{{zzyxwwwxz|~~~~~~~~}}}||{{{{|||||||||||||||||{zyxwwwwxy{}~}||{{{{{{{zzyxwwwxz}~~~~~~~}}}||{{{||||}}||||||||||||{zyxwwwwxz{}~}|{{{{{{{{zyyxwwxy{~~~~~}}|||{{{|||}}}|||||||||||{zyxwwwwxyz|~~}||{{{{{{{zzyxxwwxz}~~~~~}}}|||{{|||}}}}|||||||||||{zxwwwwwxz|~~}|{{{{{{{{zyyxwwxz|~~~~~~}}}|||{{|||}}}}}|||||||||{{zxwwwwwxy{}~}|{{{{{{{{zzyxxxxz|~~~~~~}}}|||{{|||}}}}}}||||||||{{yxwwvwwxz{}}||{{{{{{{{zzyxxxyz|~~~~~}}}|||{|||}}}}}}}||||||||{zyxwwvwwyz|}~}||{{{{{{{{zyyxxxy{}~~~~}}}||||||||}}}}}}}||||||||{zywwvvwxyz|~~}|{{{{{{{{zzyyxxyz|~~~~~~}}}||||||||}}}}}}}||||||||{zxwwvvwxy{|~}||{{{||{{{zzyyxxyz|~~~~~~}}}|||||||}}}~~}}}||||||||{zxwwvvwxy{|~}||{{{||{{{zzyyxyyz|~~~~~~}}}||||||||}}~~~}}}|||||||{zywwvvwxyz|}~}|{{{|||{{{zzyyyyz|~~~~~}}}||||||||}}~~~~}}|||||||{zyxwvvvwxy{}~~}||{{||||{{zzyyyyz{}~~~~~}}}|||||||}}}~~~~}}|||||||{zywwvvwwyz|}~}|{{{|||{{{zzyyyyz|~~~~~~}}}||||||||}}~~~~~}}|||||||{zxwvvvwxyz|~}||{{{|||{{{zzyyyz{}~~~~}}}|||||||}}}~~~~}}|||||||{zyxwvvvwxy{}~~}|{{{||||{{zzyyyyz|~~~~~~}}}||||||||}}~~~~~}}||||||{{yxwvvvwxyz|~~}|{{{||||{{zzyyyyz|}~~~~~}}}||||||||}}~~~~~}}||||||{{zxwvvvwxyz|}~}|{{{{|||{{{zzyyyz|}~~~~~}}}|||||||}}}~~~~~}}||||||{{yxwvvvwxyz|}~}|{{{|||||{{zzyyz{|~~~~~~}}||||||||}}~~~~~~}}||||||{zyxwvvvwxy{|~~}|{{{||||{{zzzyzz{}~~~~~~}}|||||||}}}~~~~~~}}||||||{zyxwvvvwxz{}~~}|{{{{||||{{zzzyz{|~~~}}~~}}||||||||}}}~~~~~~~}}|||||{zywvvvvwxz{}~~}|{{{{||||{{zzzzz{|~~~}}~~}}||||||||}}~~~~~~~~}}||||{{zywvvvvwxz{}~~}|{{{{||||{{zzzzz{|~~~}}~~}}||||||||}}~~~~~~~}}|||||{zyxwvvvwxy{|~~}|{{{{|||||{{zzzz{|~~~}}}~~~}}|||||||}}}~~~~~~}}||||{zyxwvvvvwyz|}~||{{{{||||{{zzzzz{}~}}}~~~}}|||||||}}}~~~~~~~}}||||{{zyxwvvvwxy{|~~}|{{{{{||||{{zzzz{|~~}}}~~~}}|||||||}}}~~~~~~}}||||{{zyxwvvvwxy{|~~}|{{{{|||||{{zzzz{}~~}}}}~~~}}||||||||}}~~~~~~~}}||||{zyxwvvvvwyz|}~}|{{{{{||||{{zzzz{|~~}}}~~~~}}|||||||}}~~~~~~~}}|||{{zxwvvvvwxz{}~~}||{{{{|||||{{zzz{|}~}}}~~~~}}|||||||}}~~~~~~~}}|||{{zyxwvvvwxy{|~~}||{{{{{||||{{zzz{|}~}}}}~~~}}}|||||||}}~~~~~~}}}||{{zyxwvvvwxy{|~~}|{{{{{{||||{{zzz{|}~}|}}~~~~}}||||||||}}~~~~~~}}}||{zyxwvvvvwxz{}~~~}|{{{{{||||{{{zzz{|~~}}|}~~~~~}}}||||||||}~~~~~~}}|||{zyxwvvvwwyz|}~}|{{{{{{||||{{zzz{|}~}||}~~~~}}}||||{{||}}~~~~~~}}}||{zyxwvvvwxyz|}~~}|{{{{{{||||{{zzz{|}~}||}~~~~}}}||||{{||}}~~~~~}}}||{zyxwvvvwxyz|}~}}|{{{{{{||||{{{zz{|}~}||}~~~~}}}||||{{|||}~~~~~~}}}|{zyxwvvvwwxz{}~~}}|{{{{{{{|||{{{zz{|}~}|||}~~~}}}||||{{{||}}~~~~~~}}}|{{zyxwvvvwxy{|~~~}|{{{{{{{||||{{{z{{|~}}||}~~~~}}}||||{{{|||}~~~~~~}}}|{zyxwwvvwwxz{}~~}||{{zz{{{|||{{{{z{|}~}|||}~~~}}}}|||{{{{||}}~~~~~~}}}|{zyxwvvvwwyz{}~~}|{{{zz{{{|||{{{zz{|}~}|||}~~~}}}||||{{{{||}~~~~~~}}}|{{yxwwvvvwxy{|~~}|{{{zz{{{{||{{{{{{|}~}}||}~~~}}}||||{{{{||}}~~~~~~}}}|{zyxwvvvwxyz|}~~}||{zzzz{{{|||{{{{{{|~}}||}~~~}}}|||{{{{{{||}~~~~~~~}}|{zyxwwvvwxyz{}~~}||{zzzz{{{{|{{{{{{{|~}}|||}~~~}}||||{{{{{{||}~~~~~~~}}|{zyxwwvvwxyz{}~~}|{{zzzz{{{{|{{{{{{|}~~}}|||~~~}}}|||{{{{{{{|}}~~~~~~}}|{zyxwwvwwxyz|}~~~}|{zzzzz{{{{{{{{{{{|}~}|||}~~~}}|||{{{{{{{||}}~~~~~~}}|{zxwwvvwwxy{|~~}|{zzzzzz{{{{{{{{{{{|~~}|||}~~~~}}||{{{{{{{{||}~~~~~~~}}|{yxwwvwwxxz{|~~}|{zzzzzz{{{{{{{{{{|}~}}|||}~~~}}}||{{{{{{{{||}~~~~~~~~}}|{zxwwwwwxxz{|}~~~}|{zzyzzz{{{{{{{{{{|}~~}|||}~~~}}}||{{{{{{{{{|}}~~~~~~~}|{zyxwwwwwxy{|}~~~}|{zzyyzz{{{{{{{{{{{|~~}|||}~~~~}}||{{{{{{{{{||}~~~~~~~~}}|zyxwwwwwxyz{}~~~}|{zzyyyzz{{{{{{{{{{|}~}||||}~~~~}}|||{{{z{{{{{|}}~~~~~~~}|{zyxwwwwwxyz|}~~~~}|{zyyyzzz{{{{{{{{{||~~}||||}~~~}}||{{{zzz{{{||}~~~~~~~~}|{zyxwwwwxxz{|}~~~}|{zzyyyzzz{{{{{{{{{|}~~}|||}~~~}}}||{{zzzz{{{||}~~~~~~~}}|zyxwwwwwxyz{}~~~~}|{zyyyyzz{{{{{{{{{{|~~}||||}~~~}}}||{{zzzzz{{||}~~~~~~~~}|{zyxwwwwxyz{|}~~~~}|{zyyyyyzz{{{{{{{{{|}~}||||}~~~}}}||{{zzzzz{{{|}}~~~~~~~}|{zyxwwwwxyz{|}~~~~}|{zyyyyyzz{{{{{{{{{|}~}||||}~~~}}}||{{zzzzzz{{|}}~~~~~~~}|{zyxwwwwxyz{|}~~~~}|{zyyyyyzz{{{{{{{{{|}~}||||}~~~}}}|{{zzzzzz{{||}}~~~~~~~}|{zyxwwwxyyz||}~~~}}|{zyyyyyzz{{{{{{{{|}~~}|||}~~~}}||{{zzzzz{{{||}~~~~~~~~}}|{zyxxxxxyz{|}}~~~}|{{zyyyyzzz{{{{{{{||}~}}}}}~~~}}||{{zzzzz{{{||}}~~~~~~}}|{zyxxxxyzz{|}}~~}}|{{zyyyyzzz{{{{{{{|}~~}}}}}~~~}}||{{zzzzz{{{||}}~~~~~~}|{zyyxxyyz{||}}~~}}||{zzyyyzzz{{{{{{{|}~~}}}}~~~}}||{{zzzzz{{{||}}~~~~~~}|{zzyyyyyz{||}}~~}}}|{zzzyyzzz{{{{{{{|}}~~}}}~~~~}}|{{{zzzz{{{||}}}~~~~~~}}|{zyyyyyz{{|}}~~~}}||{zzzyzzzz{{{{{{||}~~~}}~~~}}||{{{zzz{{{|||}}~~~~~~}|{{zyyyyzz{||}}~~}}}|{{zzzzzzz{{{{{{{|}}~~~~~~~}}||{{{zz{{{{|||}}~~~~~~}|{{zzyyzz{{|}}~~~}}}|{{zzzzzzz{{{{{{||}~~~~~~~~}}||{{{z{{{{{||}}}~~~~~~}}|{zzzzzz{{||}}~~}}}||{{zzzzzz{{{{{{||}}~~~~~~~~}}||{{{{{{{{{|||}}}~~~~~~}|{{zzzzz{{||}}~~~}}}||{{zzzzz{{{{{{||}}~~~~~~~~}||{{{{{{{{{|||}}}~~~~~~}||{{zzzz{{|}}}~~~}}}||{{zzzzz{{{{{{|||}~~~~~~~~}||{{{{{{{{{|||}}}~~~~~~}||{{zzz{{||}}}~~~}}}||{{{zzzz{{{{{{||}}~~~~~~}|||{{{{{{{||||}}}~~~~~~}||{{{z{{{||}}~~~~}}}||{{{zzz{{{{{{|||}~~~~~}}||{{{{{{{{|||}}}}~~~~~}}||{{{{{{||}}}~~~~}}|||{{{zzz{{{{{{||}}~~~~}}||{{{{{{{{|||}}}~~~~~~}}||{{{{{{||}}~~~~~}}}||{{{{{{{{{{{|||}}~~}}|||{{{{{{||||}}}~~~~~~}}||{{{{{|||}}~~~~~}}}||{{{{{{{{{{{|||}}~~~}|||{{{{{{||||}}}}~~~~~~}}||{{{{||}}}~~~~~}}}|||{{{{{{{{{{|||}}~~}}|||{{{{{||||}}}}~~~~~~}}|||{{{|||}}~~~~~~}}}||{{{{{{{{{{|||}}~~~~}}||||{{||||||}}}}~~~~~~}}||||||||}}}~~~~~}}}}|||{{{{{{{{||||}}~~~}}||||||||||||}}}~~~~~~}}}||||||||}}~~~~~~}}}}|||{{{{{{{{||||}}~~}}||||||||||||}}}}~~~~~~~}}||||||||}}}~~~~~~}}}||||{{{{{{|||||}}~~~}}|||||||||||}}}}~~~~~~}}}|||||||}}}~~~~~~~}}}||||{{{{{|||||}}~~~~}}|||||||||||}}}}~~~~~~~}}}|||||}}}}~~~~~~~}}}||||{{{{||||||}}~~~~}}}||||||||||}}}}~~~~~~~}}}|||||}}}~~~~~~~~}}}||||||{||||||}}}~~~~}}}||||||||||}}}}~~~~~~~}}}||||}}}}~~~~~~~~}}}|||||||||||||}}}~~~}}}|||||||||}}}}~~~~~~~~}}}}|||}}}}~~~~~~~~~}}}||||||||||||}}}~~~~}}}|||||||||}}}}~~~~~~~~}}}}||}}}}}~~~~~~~~~}}}||||||||||||}}}~~~~}}}||||||||}}}}}~~~~~~~~}}}}}}}}}}~~~~~~~~~~}}}}|||||||||||}}}~~~~}}}||||||||}}}}}~~~~~~~~~}}}}}}}}}~~~~~~~~~~}}}}|||||||||||}}}~~~~}}}}|||||}}}}}}~~~~~~~~}}}}}}}}}}~~~~~~~~~~}}}}||||||||||}}}~~~~~}}}}}||||}}}}}}}~~~~~~~~~}}}}}}}}}~~~~~~~~~~~}}}}|||||||||}}}}~~~~}}}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}~~~~~~~~~~~~}}}}|||||||||}}}}~~~~}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~}}}}}||||||}}}}}~~~~~}}}}}}}}}}}}}}~~~~~~~~~}}}}}}}}~~~~~~~~~~~~}}}}}}|||||}}}}}~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~}}}}}}|||}}}}}}~~~~~}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~}}}}}}}}}}}}}}~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~}}}~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~}}~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m52.bin b/applications/SprPoP/assets/packed/MUS/m52.bin new file mode 100644 index 0000000..f9fa0ff --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m52.bin @@ -0,0 +1 @@ +vkc`dltxpidabeks}}wsnjgddcbbaabbbbcegimpu}{ridbchq{xpjfcbbdgimqvyz}}{yvsoligecbbcehlqu{{uoifccdglqx~ytpnkigfedddddddddddcdcddddeefgikloqsvy{~|yuqnkhfeeefhkosx||zywutssrqppppppppqrrsstuvwxy{|}~~|zxvtronljigffffghjlnqux{~~~~~}}}~~~~~~}|{{zxwvutrqonmlkiihhhhhhiijlmoqsvx{~~~~}|||{zzyxxwvuttsrqponmmlkjjiiiiiiijjklmnpqsuwy{}~}}|{{zyxxwvuutsrqqponnmllkkjjjjjjjjkklmmnoprstvwy{|~~}||{zyxwwvutssrqqpoonmmlllkkkkkkkkllmmnopqrstuvxyz|}~}}|{zyxwvvutssrqppoonnmmmllllllllmmmnoopqrstuvwxyz|}~~}|{zyyxwvuttsrrqqpooonnnmmmmmmmnnnooppqrrstuvwxyz{}~~}|{zyxwwvuttsrrqqppooonnnnnnnnnoooppqrrstuuvwxyz{|}~~}|{zzyxwvuutssrrqqppppoooooooopppqqrrsstuuvwxxyz{|}~~}|{zzyxwvvuttssrrqqqppppppppppqqqrrrsttuvvwxyyz{|}~~~}|{zzyxwwvuuttssrrrrqqqqqqqqqqrrrsssttuvvwxxyz{{|}~~~}|{zzyxxwvvuuttsssrrrrrrqrrrrrrsssttuuvwwxxyz{{|}~~~}}|{zzyxxwvvuuutttsssssrrrrssssstttuuvvwwxxyzz{||}~~}}|{zzyyxwwvvuuuttttsssssssssstttuuuvvwwxxyzz{||}~~~~}||{zzyyxxwwvvvuuuttttttttttttuuuuvvwwwxxyzz{{|}}~~~}||{{zyyxxwwwvvvuuuuuttttttuuuuuvvvwwxxyyzz{{||}~~~ytrruy}~zwtsrsux|}{ywuttsssrrsssssstuvxz|{wusstw{~zwutssstuwxz|}}~~}|{ywvuttssstuvxz|~~|ywutsstvx{}}{yxwvuttttsssssssssssssssttttuuvwwxy{|}~~}|zxwvuttttuvwy{}~}}|{{zzzyyyxyyyyyyyyzzz{{{||}}~~~}||{zyxwvvuutttuuvvwyz{}~~~~~~~~~~~~~~}}||{{zzyyxxwwvvuuuuuuuvvwwxyzz|}~~~~~~}}}||||{{{zzzyyxxxwwwvvvvvuuuvvvvwwxxyyz{|}}~~~~}}}}|||{{{zzzyyyxxxwwwwvvvvvvvvvvvwwwxxyyzz{{|}}~~~~}}}|||{{zzzzyyyxxxxwwwwwwwwvwwwwwwwxxxyyyzz{||}}~~~~}}}||{{{zzzzyyyxxxxxwwwwwwwwwwwwwxxxyyyzzz{{||}}~~~~}}}|||{{{zzzyyyyxxxxxxxwwwwwwxxxxxxyyyyzz{{{||}}~~~~}}}|||{{{zzzyyyyyxxxxxxxxxxxxxxxxyyyzzzz{{{||}}~~~~~~}}|||{{{zzzzyyyyyyxxxxxxxxxxyyyyyyzzzz{{{|||}}}~~~~}}}|||{{{{zzzzyyyyyyyyyyyyyyyyyyyyzzz{{{{|||}}}~~~~~}}}|||{{{{zzzzzzyyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~}}}||||{{{{zzzzzzzyyyyyyyyyyyzzzzz{{{{{|||}}}}~~~~~~}}}}|||{{{{{{zzzzzzzzzzzzzzzzzzzz{{{{{|||}}}}~~~~~~}}}}||||{{{{{zzzzzzzzzzzzzzzzzz{{{{{||||}}}}~~~~~~~}}}}||||{{{{{{{{zzzzzzzzzzzz{{{{{{{||||}}}}~~~~~~~~}}}}|||||{{{{{{{{{zzzzzzz{{{{{{{{|||||}}}}}~~~|oe_`fox}rkda_afmvxtojgdbba``````aabdgimqvulea`djt~|skfca`adfinrvyz}}zyvtpligdcaaacehmqv|}vqkfcbbdgmszzuqmkifedccbbbbbbbbbbbbbbbccddefhjlnpsvx{~}yvrnkhfdccdfhkotx}~|zxwusrqqpoonooooooppqrrstuvwxz{|}~|zxvtromkjhgeedeefhilnqux{~~~}}|||||||||}}}}~~~~~~}}|{zzyxvutsrpomlkjihggffffgghjkmoqsvx{}~~~}}}||{{zzyyxwwvutssrqponmllkjiihhggggghhijklmoqrtvxz|~~~}||{zzyxwwvuttsrqpponmmlkkjjiihhhhhhijjkklmnoqrtuwxz|}~}|{{zyxwvvutsrrqpponnmllkkjjjjjjjjjjkkllmnopqrstvwxz{}~~}}|{zyxwvvutsrqqpoonnmlllkkkkjjjkkkkllmnnopqrstuvwxz{|}~}|{zyxxwvutssrqqpoonnmmmllllllllllmmmnoopqqrstuvwxyz|}~~}|{zyxwwvutssrqqpoonnnmmmmmmmmmmmmnnoopqqrsttuvwxyz{|}~~}|{zzyxwvuttsrrqqppooonnnnnnnnnnnoooppqqrsstuvwwxyz{|}~~}|{zyyxwvuutssrqqqppooooonnnnoooopppqqrssttuvwwxyz{|}}~~~}|{zyyxwvuuttsrrrqqqppppoooopppppqqqrrsstuuvwwxyzz{|}~~}}|{zyyxwvvuutssrrrqqqqppppppppqqqqrrrsttuuvwwxyyz{|}}~~}||{zyyxwwvuutttssrrrrqqqqqqqqqqrrrsssttuuvwwxyyz{{|}~~~~}|{{zyyxwwvvuuttsssrrrrrrrrrrrrrrsssttuuvvwwxyyzz{|}}~~}}|{{zyyxxwvvuuutttsssssrrrrrrssssstttuuvvwwxxyzz{||}~~~}}|{{zyyxxwwvvuuuttttsssssssssssttttuuvvvwwxxyzz{{|}}~{vrrswz{xusrrtvz~}{ywvtsssrrrrrrrrstuuwxz~}yusrsuy}|xutsrrstuwy{|}~}}|{zxvutssrsstuwy{}|zxutsssuwy|~|zxwvuttssssssssssssssssssstttuuvwxyz{|}~}|zywvuttsttuvxz|~~}}|{zzyyyyxxxxxxxxyyyyzz{{{||}}~~~}}|{zyxwvvuutttttuvwxyz|}~~~~~~~~~~~~~~~~~~~}}}||{{zzyxxwwvvuuuuuuuuuuvwwxyz{|}~~~~~~}}}}}|||{{{zzzyyyxxwwwvvvvuuuuuuuvvvwwxxyyz{|}~~~~~}}}|||{{{zzzyyyxxxwwwwvvvvvvvvvvvvvvwwxxxyyz{{|}}~~~~}}}|||{{{zzzyyyxxxxwwwwwvvvvvvvvvvwwwwxxxyyzz{{|}}~~~~}}}|||{{{zzzyyyxxxxwwwwwwwwvvwwwwwwwxxxyyyzz{{||}}~~~~~}}}||{{{zzzyyyyxxxxxwwwwwwwwwwwwwxxxxyyyzzz{{||}}}~~~~~}}|||{{{zzzyyyyxxxxxxxwwwwwwwxxxxxxyyyzzz{{{||}}}~~~~}}}|||{{zzzzyyyyyxxxxxxxxxxxxxxxxyyyyzzzz{{{||}}}~~~~~~}}}|||{{{zzzzyyyyyxxxxxxxxxxxxyyyyyzzzz{{{{|||}}~~~~~~}}}|||{{{zzzzzyyyyyyyyyyyyyyyyyyyyzzzz{{{{|||}}}~~~~~~}}}|||{{{{zzzzzyyyyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~}}}}|||{{{{zzzzzzzyyyyyyyyyyyyzzzzzz{{{{{|||}}}}~~~~~~}}}}||||{{{{{zzzzzzzzzyyyyzzzzzzzzz{{{{||||}}}}~~~~~~}}}}||||{{{{{{zzzzzzzzzzzzzzzzzzz{{{{{|||||}}}~~~~~~~}}}}|||||{{{{{{zzzzzzzzzzzzzzzz{{{{{{||||}}}}}~~~ujb`cku~xohcaadjs||vrmifdcbaa``aaabbdfilpt|zqicabhpzxoiebaadfhlquyz||zxusokifdbbbcegkpuzztnhecbcfkqw~}xsomkhfeddccccccccccccccccddefghjlnprux{}{xtpmjhfdddfhjnrw|~{yxvusrrqppooooooopqqrssuvvxyz{|~~{ywvsqomkihffeeefhiknptwz~~}}}}}}}}}}}}~~~~~~}|{{zyxvutsqpnmlkjihgggggghijkmnpsuxz}~~~}}||{{zzyxxwvuttsrqponmmlkjjiihhhhhhiijklmoqrtvxz|~~}}|{{zyxxwvuutsrqqponnmllkjjjiiiiiiijkklmmnpqrtuwxz|}~}||{zyxwwvutsrrqqponnmmllkkkjjjjjjkkklmmnopqrstvwxz{}~~~}|{zyxwwvutsrrqpponnmmllllkkkkkklllmmnoopqrstuvwxz{|}~}|{zzyxwvuttsrqqppoonnmmmmlllllmmmmnnoopqrrstuvwxy{|}~~}|{zyxxwvuttsrrqppooonnnmmmmmmmnnnooppqrrstuvvwxyz{|~~}|{{zyxwvuutssrrqqppoooonnnnnnooooppqqrrsttuvwxyyz{|}~~}|{zzyxwvvuttsrrqqqpppooooooooopppqqrrsttuvvwxyyz{|}~~}|{zzyxwvvuttsssrrqqqqppppppppqqqqrrssttuvvwxxyz{{|}~~~}|{zzyxwwvvuttssrrrrqqqqqqqqqqqrrrssttuuvvwxxyzz{|}~~~}}|{zzyxxwvvuutttsssrrrrrrrrrrrrrsssttuuvvwxxyzz{||}~~}||{zzyxxwwvvuutttssssrrrrrrrrssssttuuvvwwxxyzz{{|}~~~~}||{zzyxxwwvvuuuttttssssssssssttttuuuvvwwxxyyz{{|}}~~~}||{zzyyxxwwvvuuuuttttttttttttttuuuuvvwwxxyyz{{||}~~|wsrsvz~}yvsrrsvy}|zxvutsssrrrrrrssstuwxz}~zvtrsux|}yvtsrrstuwyz|}}~}|{zywvutsssstuvxz|}{yvtssstvx{~}{yxwvutttssssssssssssssssstttuuvwwxy{|}~~|{yxvuuttttuvxy{}~}||{zzyyyyxxxxxxxyyyyzz{{{||}}~~~}|{zyxxwvuuttttuuvvwyz{}~~~~~~~~~~~~~~~~~~}}}|{{zzyyxxwwvvuuuuuuuuvvvwxyz{|}~~~~~~}}}}}|||{{{zzzyyxxxwwwvvvvuuuuuuvvvwwxxyyz{|}~~~~~~}}}|||{{{zzzyyyxxxwwwwvvvvvvvvvvvvwwwxxxyyz{{|}}~~~~}}}|||{{{zzzyyyxxxxwwwwwwvvvvvvvwwwwwxxyyyzz{{|}}~~~~}}}|||{{{zzzyyyxxxxwwwwwwwwwwwwwwwwxxxyyyzz{{||}}~~~~~}}|||{{{zzzyyyyxxxxxxwwwwwwwwwwxxxxxyyyzzz{{||}}~~~~~}}}|||{{{zzzyyyyxxxxxxxxxxxxxxxxxxxyyyzzz{{{||}}}~~~~~}}}||{{{zzzzyyyyyxxxxxxxxxxxxxxyyyyyzzz{{{|||}}}~~~~~}}|||{{{{zzzzyyyyyyxxxxxxxxxyyyyyyzzzz{{{|||}}}~~~~~}}}||||{{{zzzzzyyyyyyyyyyyyyyyyyyzzzzz{{{||||}}}~~~~~}}}}|||{{{{zzzzzzyyyyyyyyyyyyyyzzzzz{{{{||||}}}~~~~~~}}}}|||{{{{{zzzzzzzzyyyyyyyzzzzzzzz{{{{||||}}}~~~~~~~}}}}||||{{{{{zzzzzzzzzzzzzzzzzzzz{{{{{||||}}}}~~~~~~}}}}||||{{{{{{{zzzzzzzzzzzzzzzz{{{{{{||||}}}}~~~~~~~~}}}}|||||{{{{{{{zzzzzzzzzzzzz{{{{{{|||||}}}}~~~~~|}pyae\XpwZlchh`zZPUUiwewz}u~dtdu\ehxytfYz}{vriyjnhbvSMPQgqgv~oz`vay_mwel~a}tqhq{ssigudsckdbvUNQXtjqq|vznntup_|orp|g`mifklelfdm`k_htQRR^}d~nx}|saxdjutzwU_ZkUisb^l}\fhak\poPVUfboxzqmX`tc{mMjuNqpOl~ndayoXkbej^xlTY\vvj{yz|kd^MlpjeS{fS{^Svygaf~a_lbkgf~aX[eiznwyl]YPWxhw\gXc{T]{vkfwvXkhendn~^]`tenueZXLhthsYztRxlPm}pijd_mdjlb}pZ_fmrs}xwo^YRSvhs_h\a}YXxzqmztWlgfmfhe`dw}emu~hYWNmqg}s]~vTwoPi}upp`amejnazw^bjkss|u}ws_XP[xcscp\h~X[zyrm}qYmghmghiei~ugnu||{i[WVwfiymgmZ~iRm}xsyzWjigjma~tcgtdl{uzq^[Rmq`tz|fZtyUb|zvta`nfincpdfkmmwtry~~wh]X`z^mtnsjbhUo}|x~rVpgjin_vhlzzdj{s~yua]Vslbx|Գscywr|~d]`emmqvtolc|o||~u^tzJSd[\gw~|m`z\i|{PJU7XZfSlQ>4UlVMix|ui}zvc|fpVo]YTXn}wĩr~|qWSn>FVCKJh`K\y[y\qxt~xOLzjW|TtryUbiaQIJ/J{igc^exz}zΗ]p~pRQhZmCbwO`ShsyavwpDQ`VotXn[vz~sƙy|f~Qq{_l\c}fUMrR7aLi`xhhntqlkcxXjwzdRgeVpNlygaNxZc{qDSO[GEjaY]xfzvgwoub@g`nxȰmn^onOybxSUhhiR[nV:[tos||o`f]FAlpovblk[xzor|ich}aQ|oZWhlFrzr~dxpx_7LMtoo|wpRpp^faxn_nnyhmmZ[c{htx{phqvn>I8[iMxTu\boJ^m}irl}pm{p{zloxsyed~kM?,JtP{zXTjStefwrjntolxlisvvwg{fY91?j]fqsgbmPiZZtltbZfcph}k}zqvvŲlsS=H{ibuwkJzaAqpRfngmiLmutpalywpzrokRLOq|{]RwdQdeOLkvu}gJ_iri\sxuz~q~rzrtscp_}rt^Kei@XtJLugsvTNkgfdZ_{iuqw^xn²өb]|BiNNE?nxe6U\RJZ\esolirYkaQfnalzdh]ZN\cI@F&J<][qmuws}YiwmVbL|_N^gZXTXykbg˛nrtk53-a>3GNtuy~T_bDJpRc^oa~pqimsΏtj^lmo67`aQXyr–eyc{gtaexy{UQauio[@c~~n~ydr=Dcu{PzL}{bfrcvterS[`qb]Oq?eTezvqxk`tl[__nmu|qtnrf}sttd}W^_rvYO^DXMMhlRWihkgus_\hfln٣ncW[FYX._Rr~z;czGjjP`SJpu^rm|d\r{}Y˴f]qRN=@UT\\zWnmtTjszill`dQoMiuommyʗķxixwr^q_MjxqHtKFM:`owqbklciznSYmr{|provhvwr}w`ePef?BdMQnWGVLM\onafw}sup^rs\yk~xloyLgk;gy]a[YIVRYVU_@M_lrbfx{v_6@`RYco~V9kh__mSKlQoSOnXdnuVQ_[tgǛle]_dE?QZ~ŻuvXH|iinOVEWoCkpZ[`tp^^svxةŤv_Wo[Y][se~e~aQxjl~\RFL_S[plYnugipntp}~~||p~twmksozeY|o{c\MWgOQl][]ji_oqjc|wÍmnm`ubprj}iEddw`tggdUzrf`gLOqW}hkizt۶d^Zrttwq[@XWf~Mk|uxg]|lXsxcetvzq\YYyfdpqwoqn>zxF}sn&"*]7ZvCԧq\oSfYfcr~Qh{^XJ4EU?``|~fw\bOSS\icQeyڻݶyl~Hf{ax{tj|r|bneTY7,.JE89 =EbZJw_w˲䱠]q|m}advjHYh?J:/F$;#4ZnUxwZͻƬt[A4N8mpzAn{6mc)MnkmlfQM_\oCeqT\^]qwqѨzb]d]deQ<)0:qw²|x[}a`qxi{`Ktv~ieZGaP_lab¥šÿ~vjtqYadfeiRxrUJo-EW(%6TQ]amq}u||b[m>GsE=kpfjtH_kaw[^kqpltvu୙Քpqa\=Zqe°yc|c`xwhgxmm^{pockvUOiXynnh||ttwaTSb{GP[_AG6H_On{j{rc}d}Ȳ~t`_R}Bic]AMg_hnDapml~wwnxezuy~{se`V^^W@Xi_oglDEj[W~~vzkenzjxh~VkybOQ\Lj`Ymhy_^j~wsfU{wa]{ȖkWXnj`RBEB]boa~dwsMCP_nOIZMq]M9chgu}fUor|Ʊ~zspgzu{mc~aŷhlwd^]o|{sMVQi_Y@fwimuh^piz}q{myp`T\\]yjLomwrwns|}luRjty~Xhdz]dvqx{g`\ysff}utqUy{r}qbj^o\B@Rcr]TT_ztmRwzqzbpnwt~ytztnfot`Yjsxv`qgtkQenxzjeepec}hWYj~oj}u\up_cZezwzo\iispqZxkllqghrŤus`duqleYXXxxi{zkqfsYVdnPh`auslMkiltlbWpsS|~rr˷yyvjkqWy~uxe]_cinh|ilLdnfeZYTg~ev{ɣ~wsqx^|uh_fT~~tr|fP`zbUa]|ih?Ka_camatm||ewq^r}usnu[vpi}jWXhveVZIj^gFMqivwqswr{~umqzx{s|kz~iVV`deLASHvxqrh|h]dzwg{{rl{n{zn~|az~yq|bHJ\fl]P[Z~qrmrpzmhiy{t||z|sesx~vlverfYfwxtr|}kuka~ph]r}rvzjnu||n[^Nsttg~}{t]Z}w^rd\[usu~q|noysqmzz{zmvvk\YEr~ysbrx|~s~~tzvwehxzqsyb}kazjajztp}izzon|xrl`Rv|pvyrshwdozMcmjlyh^}yai}j_lxuyrztc\NTzk}sozxfy\uz^ttwslygn}slwyvrhhy_WZkg_VXBXuozvxtzebv~m}u}zqsyxwecu]`ltokdSu~yehsuwiaiSurvXfgnr}cwvfwqpkuvw|oyjt}yk|zv}}lnwfyzaqvzy}h}os}ogesxlm{nulfoxcmwyjaQtqx~{wuqwgjyttxwqq~}nm|wy{~wmiZvk|yvnynqwtgflVvu[jrtty[u}uji}qhcr{xmuwtmawu|}ro}c}elqxy}]xxm`ne`at|uh{ulicivth[[R`zqz~z}xth{npxjsz|o}rs~mt]bqvib^V_z}v~zvw|twxxm`jYayqnbfkmvaauwjz|uvuh^gs}vnecfx~xjrlfvvbjt|n`}x{~{}sy}ou`n~WX]kje\VQt~q~|rson]{}ut~ot}|}wej\`gic]XUi|x~wupw|tuof_i[iywzpxrz}}~p}{h|Y^fqmhbcmu~{lsslxi|zuwxpl}q|ft_a|VMR`e`VX]|v~||~p{|rqr|pzn]iRR_faYUUm}}z{sptqy{vyew|ayyy~~i{x}v~}\zXU[b`^[]r}y}nx|{fnw||}w{cmrnymkvRjnHEJUWWTT`}~tuuwry}t~nixVixRLPWWUSVcu{qqfiy}|omw||}~pt}r~qk}`l~VNS^aa]^ozwnlnewyy}}}~yzebpguwi^pU]wSJKV]_[\hy~~tpun~kmzr}zmf{]g|[UV^`a_ans~wvig{{x~pkvz{xz~hkup~omfu`]aihfbduywjmnnp{~uwvxuvaiqky{ki|^t|WSV^^][_p|ykwjxs||{}~xev{wpv}_~vXY]b`\X\t}~|ufy|{~cp{xy{yi{{|k{xcnWWY\[ZX]x|sg|}xb~tzxyxn~yzjnebVXZYXVXd~}wlg{}wfzx}tn~zse|lg^TW\]ZVXd|{}xmj~wl}~to|w}se{gj`WW\[WRTb~{}}~wkkxpnl{x}~qg~gl]UUXWTPScyz~ylj}pohwv}qd|ge~[RSXWVSVb|{x~spo}{rjxw{}o`wh`_TVZWRPTb{{vvsny~z}rhuuw{o_un`aQTXYTPP\v~v~yqlt}|~sdost{~talrY{kRSSUROOZrv}|rn}t|{|welvu{|tdfxYpqQQRVSPNVkwz~qwwtw{yx||ifqry~td\v]exUPQUTQPUf~tslw|xw{|ndoqrx|ujXmkWx`ORUVSPUe~vol}pzxx|~sagppv{xn]`uWopPRU[ZWV`v|qwxpw~yww{~wfbnorxyqdXsa`|\UW[[ZY_p~qpiw|vsw|yj`korwysiZro\}dUY\^[Y]lsn~kousv{|oahoryzo]jw[ynUY[^[Z[h~wo{nwnvstz}tfjstx}{rabz\quVYZ]ZXYd{~vmwrspwutyxghrux}{ra`z^mvUVX]\ZXavxmtuqrxwx}zihrux|{rb\w^hyXWX[ZXYbvxmsyow|yx}{jgqtvyypb[wadwWVY][YYasxnt}tx}zx}yhfosvzyo_Yv_gwWWY\ZWW`sxow{ww|yx}wggqstxwo_]w]itTUWZXVWau~xp{zyv|xw{udeosvywn]^vZkqRUX[YWW`t|ztusywx}qdhpsuxsiXcsWsjRWWYVUXdy{{zwqvxuw|}ncjpsuwqgWloYv`QWYYUTWfzz|yzqxxuy}yibkqtvwocUpg]xZSVXWTSYi|y{y{p}|}wtx}whclqstunbVrd^vWRVXWTSZj}z~x|rz~{vsw}vfakqsvum`Vsa`uVSVYWTSZk~{~x}rwzvsx}ufbkprttk^Wt`buUTVYVTU^o|x}qy~{urv{ueaiorttl_Vrc_uWUY\[XW^n|y|qzy{urvzufaiorttlaUpg_y\VZ]\YX^n|}x{n}x|uqtyvgahnqstneWpl]y`V[]]ZY]l~|}yyoruqswyj`ensuwqhXit[whV\]^[Y\hz}{{xs|ovrrv|}pdfnsuwtk\ax]ppV[\^[Z[fv|}wyurxsqv|tfemsuwum`[wbfvXY\_]ZYaq}~wzo|w~wsvzykdiptuvoeVmn[xbU[]][Z^l}~}ywrsytuy~pdfmrturiZ`vZmnTZ\_\[\fw~}v}uw|vtw|tfbipttsj^Vrd`vYW\_][[cr~~wzrywsvzwibgmqrrlbUkkZu_U[^_\\ao~z{tuxstxyjadkprsmdUcqWqgT[]_\[^j{~~{x}sytsv|{nbbioqqneW[sYjmSY\_]\^hx~{{ztysquz{naahmpqnfYYsZfnTY\_]\]gv{~wtytquy|ob`gmppneYVr]cqTW[_]\]fu{tvztptx|pc_eknonfZUp^`qVW\_^]^fu|uuysptx{ma_eloomeYVq]bpUX\`^]^gw{}vtxspsxzma_floomeXWr[eoTY\`_^`jx|{xswqosxyl`_flopmeWZrZhnU[_b`_ajz|v}pvqptywi_agmopkcUarYqiU]_b__bm|~~rsupqt{tg_cioqqlcVjn[ubW^`a_^co~~~q}w|tprv}~re`emrrrkaVph_v]Y^a`^^dq}}}rzz{tprv~sfbgmrsrk`Wrg`u[Y^a`^^dq}|}sx|{tqtxrfbhnssrk_XsdbuZZ^a_]]cp|{~tw{vruy~qfchossrj^YtcdtXY]`^\]dq{{~uz}vsuysfchnrsrk`Xre`tZX]`_]]co}{{~~u}}wsuysgcgnrsrj`Vpg^t[X]`_]\bn|{z}u{xsuxthbflqrqjaVmj[t]V\__]\alz{z}vvxstw}viadjpqqlcVepYqdU\^`]\_iw|z|ztztsu{xkbbhnpqmeX^sZjkTZ]`^]^gt~{|zvztqtxzncaflppng[Wq_bpWY]`_]^dq||t~y~vqsv|}re`cinooh`ThjYr_V\_`^^bm{}wswrqtyvi``eknokcW\qYikT[^b`_aiwvvzsprv}yla^bimnmf[Tnb^rYY^ba`agtr}w}toptz{oc^aglmmg_SiiZr_W^bdbcgrtruppsx~}re^_ekmniaUboXogW_beccfp~x{qvqoqv}~th_^djmnkdX\s\jnX^bfdden{zoourzukmszvy~onkfZQLOZiv~ŭ~sgbfovywtpquxgc|jmYNLMOSWYYaxvusncZSU^hprqlkpw}|uv||wqpruf]xgtzz{|{pltumpv{{{~q|wlryf^XY[bed\OECGNQRQJ?=UDIV?CHMMNQZiy~xohggjkh^QFELTZ_a_Y]yjvov{}}}~tqvttq{wux}tkjotxyvncg|eqw_adfcbahr}yqmklms~vs}pgfimnniaVbnWkgU[^`__bly~}etravmb\[^chjf\RMPV]ac`[Tjo`zmelqttv{xzxstv{xma\^cffe^UNdZQePJNPOMMR\hpspjfehkpyzxtvz}}{tke{pi|fbehfefku~}}xy~ncxefvjc_`djnnh^URV\acc^WXpcbwcafjllow~}ytuw{}{sg\XZ^``]ULLaPP`JGILKKLR]hqsqliimpwxtvz}}{tliqmjegjihhmw~}wxpcyigxmfbcglppkaXUX]bdd_YWne_ud_chijls~}|ztsuy{zsh]WX[]][UMJ^WJ`RGJLNMNS]irwvronpsyyssvy{zungw{h|vfhjkjjmuyw|zfpxbswlgehlqsph_XW[`ceb]Wbq]lr`beijkoxyxstvyzwodZVX[]]ZTLOcQSbNKMPQQSZdpx{yurruxvqruxyxslhzxi~sfiklkloxxw|zgrxctxojikosurjaZY]adeb]Xer^ltcbfikmpy~z|yttuxywoe[WWZ\\ZTMPcURdUNPSTUW]gs{~~zwwy|}tooruvtoigztg|rfgjkklpyyx~xhxwfyypkkmptuqi`ZZ^bdea\Yio]pqbdgjkns||u}wstuxxtlaYWX[\\XRMWcO[cQPSVVX[cnx~{yy|wolmprroidn{go{jfgijkmt~}z}~npkp}uplmpsusld\Z\_bca]Yaqafufbehjlpx~z{yttuwxund[WWZ[[YSNTeTUfWQSVWY\cmw}{{}vnjknppnhcjyhlzjefiijms|}oqlq}vpmnptusld\Z\_bca\Xbp_ftdadhikowyv|{wxy||yria^`bdca\Ydo\fpa]_bceipzxtzwxz||xpga_acdd`[ZhlZjm_^_bcejr|xtyxy{}|wof``addc_[[lh[nj^^`bdis~{ww|{wpllnqrpkhgjnqroibcqcZk_UTUVWZ_hqy||zwx{~{sooppnib[XZ^bbb^[aqd`rg_^_``bgnv|~}zwwy}xpjihge`YTXeUTe[UW\aekt~qrpo}zurrtwyxslecdgijieckyjk{piikmnqw{qicbccb`[W]j\Zk`WWY[\_enw~~zzyyzzwpg_ZYYZZXTQVeZVhbYZ^adhox{v{zz|}|vogbbcddc_\aof]oj_]_`adipy~}~}zrmkkllkgcerpaprfbcefhls|upx{vsrstsoh`ZWXYZZWTTagVal`\]`cfkr|}~}|}~}zskd``aba_ZXal\]lbZZ[]^agow~}|{}|~wokijlljgdgundushfgijmqy}r~mv}ytrqrsrng`ZXXZ[ZXUWdhYcnc_`behmt}~|}{{|{xrjc_^^_^\YW_j]Zkd[Z\]_chpy~y{tmhghiihechumdutjgiklot|yr|lw|xsqqrrqmf_ZXY[[[XVZhgYhncabegkpx~y~~{zz{yunga^]^^^[XXdhYala\\^`cgmu}{z|~|voiffghgebdouekxpiikmosy}syqp{zvrrrsrojc]ZZ[\\ZXYdl^apiccegkou~}z|zzzyvpib^]^^^\YYbj\]lf^]_acgmt|||z~}~~|voiffgggeccmugjwpjijlorx~tzrq|{wtsstspkd^[[\]][YZek]cphcbegjnu}~{|}|{{zvpic_^__^[Y[fiZald^]_adhnv}|y~~~ztniffggfdbfqpcoulhhiknsy{vpy|xvuuusoic^\\]]\ZY_kd[jmdabdgjowz}}}|ytmfb```_][Zak`[jh_]^_aejqx~z|wqkgffgfebbjsgesphfgiknszy}zs}|ywwwvsnhb^]]]][Y[dk_^mjbabdfjpw|~}}|xslfb```_][\dj]]kg_]^`bekry~{~{wpkgeefecabkqecqnfcdegkpv}x~||{{xsmgcaaa`_]]emb^mldabdfinu|x~|{{{xsmgcaaa`_]]emd^kmfbbdfinu|x~}|{{xtnhca```_]]blg]hngcbdfint{~y{}{{zyuoida```_]]`jj^eoidcdfimsz}{y}{{zyupjeb```_^]_hl`bolecdfhlrx~w}{zzyvrlfb````_]^dmf^kohddfhlpv}}wz~|zzywsnhda``__]]bkj_fpkfefhkou||{v|zyywtpjeb```_^]_hmbannhffhkntz}u}|zyyxuqkfb```_^]^eme_lpiffhjnsy~}vz}zyyxvrlgc```_^]^cli_iqkgfhjmrx~|wx}{yxxvrnhca``__^]bkk`fplgfhjmqw}|yw~~{zyxvsnida`__^]]bkk`dpnhghjmqv}|zv~~{zyxvsnida`___^]ajlaconighjmqv||{v}~{zyxvsoida`___^]`ilbbonighjlpu||{v~~{zyxvsnida`___^]ailaconhghjlpv|}{w~~{zyxvsnida`___^]ajk`domhfgilpu|}zw~~|zyxvsnida`__^]^bkj`domhggjmqv|~yy~|zyxvrmhda``_^]^dlh_gokgfgjmqw}x{~|zyxuqlgca``_^]_fle_iojffgjmqw}x}~|zyxuqlgca``_^^`glc`kniffgjmrx}x~~|zyxupkgca``_^^`hlb`lmhffhjnrx~x~~|{zxupkgcaa`_^^aikaalmiffhjnrx}y~|{zxtpkgcba`_^^bikaalmhffhjnrx}y~~|{zxuqlgdba`__^bikbalmiffhjmrw}y~~|{zxuqlhdba``__ahlc`kniffgjmqw|y}}{zxuqmhdba``__ahld`jnjgfhjmqv|z{}{zxvrmieba``__`flf_gnkhghjmqu{{z}{zxvsnjfcaa`__`dki`enmighilpuz}y~}{zyvsokfcaa`___cjjabmnjghiloty~x|~{zxwtplgdba`___agle`iokhhiknsx}zy~~|zywuqmieba`__^`ekh`ennjiiknrw|}w{~|zxwurnjfca`__^_cikcblolijknqv{wz~~|zxwurojfca`__^_bhleajpmjjkmquzxx}~|zxwusokgda`__^^aflg`honkjkmpty~yw|~|zxwusplhdb``__^`ekiaeooljkmptx}~zv{}|zywusplhdb``__^`ekiaenolkkmptx}~{vz}|zxwuspmieb``___`djjbdmpmkkmpsx}~|vz}|zywusqmieba`___`djjccmpmkkmpsx}~|vz}}{ywvsqmieba`___`djjbdmomkkmpsx||w{}}{ywvsqmieba`___`ekjbdnolkkmosw||w{}}{ywvspmieca``__afkiaenolkkmpsx}{x|~}{ywvsplheca``__bgkgagnnkjkmptx}zy}~}{yxvsplheca``_`chkeahnmjjkmptx}y{~}{yxvsokhecaa```cijcajnljikmptx}z|}{yxvsokhecaa```dijcbjnliikmptx}z|}{zxvsokhecaa``aejibbjnljikmptx}z|}{zxvsokhecba``aejibbjmkiikmptx}z|~|zxvsokhecba``adiibajmliijmpsx|z|~|zxvsplhecba``adijcaimljijlosw|{|~|zxvsplhecba``adijcahmljjkmosw{{z~~|zxvtpmifcba```chje`fmmkjklorw{}z}~~|zxvtqmjfdba```bgjgadlnkjklorvz~y|~~|zxvtqnjgdba```aejibcknlkklnquz~zz}}|zxvtrokhecaa``adhjeahnnlklnquy}{x{}|zxwurolifcba```cgjgbemomlmnqtx|~xy||zxwuspmifdba```bejicckonmmnpsw{xx{|zywuspmjgdba```adijebioommnpsw{ywz{zywusqnkgeca```adhkgbgnonmnpsvz~{wy{zywusqnkhecaa``acgkhcfmpnnnprvy~|wxzzywusqokhecba``acgkhcemponnprvy}|wxzzywutqolifcba``acgjicelponnpruy}}wxzzywvtrolifdba``abfjiddlponnpruy}}wx{{yxvtrolifdba``acfjiceloonnoruy}}xy{{yxvtrolifdbaa`acgjhcelonmnorux|}xy{{yxvtrolifdbaa`acgjhcelos~|pgbaeozwg][\^bhow|voha]]ZQJKPRPOOPSWZ_cglqu{{vuyneb`][[\\]^_``_acdffhlmgceihda^\\]^`cfilpv}zqh`YSLHFHLNMQ^jqv|~y{{yyz{{zyxwwwwwvusqmjjmplfhlopqrstttssrsstuwy}||~|||||}~{vpje`]ZWVUUX[YSRY]]\\^adhlpuy|zxxvsokfb]YTPLJHHHILQWXVYcjnptx}yy{{zywvtqoligedcbbdgjhbbgkjhghijloqsuvvwxy{||yv{~}~~zxvtrqqsvtnlquusrstvy||wuvwqighhfda_]ZWTRPONOOQTX]^[\cjmopsw{~|zwtqnjgca_]\[[]__ZWZ_`^]]^`bdgjmoqrtvy{}|z{}{yxwvvwz{xsuz|{zzz|~}yuqqtrkeeggedb`^\ZXVTTTTUVY^ba^`hmopqtw{}{xurokhda^\ZYXY[]\WUY]^]\]^`cfiloqtvxz}|zxwvutuwz{xtu{}}||}~~|xtqpsrlfeggfedba_][YXWWWXZ\addabioqrsuy|}}}{yvspmifb_][YXXY[]\XUZ^`___acfilpsuxz|~{ywutssstvyzvsv{}}||}~{wtqrtrkggiihfedb`^\[ZYYYZ\_dfebelqrstwz}}||{yvtqnkgda^\ZYXXZ\^[WX]aa`abdgjmqtwy{~~|zxvtsrqqrtwxvrsx|||||~}zvssuuoihjjihgedb`^\[ZZZ[]_cffcdkprrtvy|}||{ywtrokheb_][ZYZ[]_]XY^bbbbcehknquwz|~}{yvtsqqpppruwuqrwz{zz{|{xvvxvpkkllkjhgeca_]\[[[\^`dfebekoqqsux{~}}|zwurolheb`^\[[[]__\Y[`ccccdfilosvx{}~|ywusqpoooprutqorwyxxyz|~}zyz{vpnnonmkjhfda_^]\\\]_beebbglnopqtwz~~~|zxurolieca_]]\]_a_[Z^bcccdfhjmqtvy{}~|zxvsrponnnnopmihlpqppqrtwz}|{{{zywurpmkhfedcccegigcdhlmmnoqsvy||{{{zywuspnkigedcccegigcchlmmnopsvy|{tmheefgilnopppqrrqljnqrttuxz}{||wqnnnnljhgedbaaacehknrutolmljgdbcglrzzusrpnkifdba`__`acfimqrporwzzzz{|~|xsojfb_\[ZZ[]]ZWY^abbcdfhknqtwy|~}zwspmjgdba``aa]YY]acdegkoty~~{usttsrqomljhfedcccdfhkkhgkprsstvy{~~|zxvsqomlkjklmmjgimpqqqrtvy{~}wuutsqnligda_\[YYXY[]_^[\afhjknptx|}zzzywusqoljhfdcbbcdfhgdcfkmmnoprtwz|~||}}|{zxwusqonmllmnprromotwwwxy{~~~~|vqpoomljgeca_][ZZYZ[^`b`^afjlmoqtwz~}{{zywusqnligedcbbcdfgdbcgjlllnortwy|~{z{{zyxwusrpnmlkklmnqrpmnsvxxyz|~}}~|wqooonljhfdb`^]\[[\]_acb`afjmnoqsvz}~||{zxvtqoljhfdcbbbdffdbbfjkklmoqsvy{~{zz{zywvtrqomlkjjjkmopolnruwxxz{~~~}wrqppomkigeca`^]]\]^`ccb`bgkmnoqsvy}}}|zyvtromjhfdcccceffcacgjjkkmoqsvy{~~{{{{zxwusqpnlkjjjjkmonlknruvvwy{}|vsssrpnljhfdb`_^^^^`bcc``dhklmnpsvy|~~}{yvtroljhfedccdffebaehjjjkmoqtvy{~~|||{zxwusqomlkjiijkmnmjjnrstuvxz||wuttsqomkigeca`____acdb`aehjlmnpsux|~}{ywtromjhfedddeffebbehjjklmoqtvy{~|||{zxwusqomljiiiiklmljimprstuwx{}~yvuutrpnljhfdba`__`acdcaadhjlmnprux{~~}|{ywtrpmkigeddddfffcbdgjkklmoqsvy{~}{{{zxwusqomljiihiikllkijnqsttvxz||wuutsqomkigecba```abdecabfjlmnprtwz}~{zyxvusqomjhgeddddeghgedgknopqruwz|~zxxwvtsqomkigfedddefhhfefjmopqstwy|{xwwvtsqomkigfeddddfghgefilopqrtvy{~{xwwvtsqomkihfeddddeghhfehlopqrtvy{~|xwvvtsqomljhfeddddeghhfegknpqrtvx{~}ywvvusqonljhfeddddefhhgegknpqrtvx{}}ywvvusqpnljhgeddddefhhgegjnpqrtvxz}~ywvvusqpnljhgeedddefhhgfgjmpqrtuxz}~zwvvusrpnljhgfedddefhhgffjmpqrtuxz}~zwwvusrpnljigfedddefhhgefjmoqrsuwz}~zxwvutrpnljigfedddefhhgefjmoprsuwz|~zxwvutrpnljigfedddefhhfefjmoprsuwz|~zxwwutrpnljigfedddeghgfegjmopqsuwy|~zyxwvtrpnljigfeddefghgeegjmopqsuwy|~{yxwvtrpnljigfeddefggfeegkmnoqrtwy|~~{yyxvtrpnljigfeddefggfeegkmnoqrtwy|~~{zyxvtrpomkigfeeeefggfdegkmnoqrtvy{~~{zyxvtsqomkigfeeeefggfdegkmnoprtvy{~~{zyxvusqomkigfeeeeffhdp|zyxxxxxy{|}~~}yyzxvxxtqmjlpw}|{yxtokhfdcccdfhlotx|}ywtqonmmmljkov|sy|}|xtqmjhfdccdefhknrx~}{yxuqqruxz}|su||~{xtqnkhfedccdfjotxz|~~|upr~~|}~~}|zxvtqomkigedcceghhhjnrw{{uppv~{yzz{|||||{ywtqnkihhhfddfimquy|{vpjgiq}}{yxwusrpnllllkgeeefhhijlmopqtvy}{vsrqonpv~~}}|||}}|ytqonlkiihijlnqtx|}ysmhedgmu}ywvtqmkjjjjjiiihhhhhijklnqrsstw|~{yvspnlmpu|}||}~~}{xurnkigghjnqtvy}|vpjfcdiqz~zvsqnlkihgggggghjkkjijlopqrrtuwy{~~~~~|zxuqoopu}}|{{{{|}}}}|{xuqkhhjmquz|vpkhghmv|vqnkihhhhhfddfhijjkkmnoprstuvwwxxxy{zwvuvz~{wusrrrsrrtx}|wrmjjmsy~xrlhhltztnjhhiihhgggghhijklnopqrrstwyzyyw{|zzz|~}xupjfc`]]\\]`cgmrx|~|yusv||urquule`]\\]_acfilnqsvxy{|}~~}}}|{zyz|u{~wz~yvtstuwyyxvroligfgijmpty~}yy|yskc\YZ`kxzvssstuwxyz{{{zzyyyyyz{|~yoil{{snnquy}~{xtqnkiijmqv{~~|z|wqou~}|zxtpkfa`cly~}}|{{{{{|}}}}{xsokjlr|}wtsuzyrnlnqux{}~~~|{zyxxxz}yoheehmt|~|zzz{|~{vrpsz}|zwtplgb^[[_foy}yvspnkjiilpw}{uqpquz|zwusrqqqrrsuy}{xusrqqrstuwy{}}zz}xsmhc_]_ciqx|~}zwsqnmlllmmmnnmmlkjjjmrx|{}~wqjecdipyyogcdhmruwvutsrrrrsuwy|{xy~|smkmqw}~ytojgdba`aabdegijlmopqrrrrrstvz|zwutsux}|xusstuvvtoic_`djpuyz{{zz{{}~xog`]`ivztokhfdca``____`abdfhkmpruwz|}~~xsommqw{vrnkihgghjmpsvwuqlgegmt{~yuromkhgedbaaaaacfgimoqvzz}}xxxx||qfcabglt{zvqmjhgffefffgikmoruy|yrnoty||}ung`[XY]fr~}{ywusqpoppqrux|xx{tuwyz{{|{|~~{uniginty}~|zyxwxy{}~|{||}vjr~~|zwspligfhkpw~~zxx|xmehw}rkhhjlnoprrrqomjfa]YVUV[cn{umkrxttux{|{xsmhfhkortsqnkhgginu}}tja[\fx{tolkjjjiihhgffeeeeeeeeffgikot{|wpkgglu|zywvtsrqpooopqrux{~~{uojhjpv~{upnou~}wpkfccflt}}yvtsrsstttutttsrqqqruy~xrnjijmrw}}zxvtsrqpppppqrsuvxz|~|tlgfjq{~{yxvvuuuuuuvvvvvtsqooqv~|{zz{~xqkgfipyzsmihilptusojfddgjnrvy{}zqkkq{}wrnljiijklnprtvxz|}xurpnlklot{{uokhedccccccccbbbbcfjow~~ytnifdflu}wpha]\^bgknpqqqqqrtvx{~zpe\VUZet}wrmifca_^]]]]^_acfhknqsvy{}ytu{xphc^\\[VRUY_k|}wrqsxzqid_[YWWVX]ix}qdYPKMU`my}unf`]ZYXY[\`dinqu{~xs^WWY\_bdfhlrx~zvw|~}~}ric]ZUMEBFMSUWWWZ\\[YWVX]dlty}z}wrmg_UI>746;CLV`irz{pbUPVduzvtromkigeb_]^djielx||yrlhc`\YVSQRW_goz~zvqlfa\Z[_foy|xtqnnquztja[YYZ\^^]ZWRLGEJSQN[ffir||}|y}~}ytojhfeeffffeca`^^`chmsx|yuuwyvmnujhojefjqx}~ytokiijnsx}~wmc\WV\gtsq|vknlf_YUSRQQRTUVVUTTSSUY`iqy~|||~{ywuttux{~~yqsuhonjv{vqmjhhkqx}zxwxyyzyxvtqnkhjt||z{~~}~}vojfc`_^_chnty}}|{zxurnieb_][[\^aa[Xe`glhtz{yxxz|wttw{~|umcZTPPRW\`dfilosuxyyxvtqmjjqshpjojdklonpprtvvuqmjilrz}yvuuvrfppu|~{{~{xvuvwyt|~{wsnid_[WTQOMLNQYbmx~{yyz|~~||{{tnjfdcbceip|vvzxvzypnkkjjkmorstrqoorw}~wpkkpx}{y{~}}zwxz}yrlgeehknonlkkmpsutqke`]\]`bdedeiqkjomrwqqsuvxz}}xqkhhlqvxwuqnlkkmosx~zt|vwvu}{zz{|}|zvpib^]_dimpsv{~|}xqnvopvw~ytoic]ZY_Z`rz~wohc`adgjjgdaaeltz}}|{}yrlikqy~wspnlkifc^[Ygikqsukjiiijlotz}~{urqsuvtqlhfdefilqx~|{~{wwzurorw~zqjggknppnmnrw{{xrjc_]]^`accc`^Yagbfgjdjpv|}yvuz~{qifhntyzwrpqu{~|yvux}xw}{}~xpkhjov||zz~zvttvxy{{{zxss|}~~}ytokhea`alqmuxx~|zxtokhhkqwzxqha^bkt{~}{z|{urswyzzz}ytssuvvvutrogeoklnilrstsrppqu{ytpmmlmmmlnrysnlmquwupljmt{zsmknvyspsz}xwy~{vsnj`]fbffeouy|~~snpw|zywwwuvyvmgddeea]\_guzwvvx}uxxxxopqqqrty~}ywz~zy~~}zvqmjjiibgrqyxzxz~}vqpsx~{wtstuxz|}~~wsqszzwx{~~|zz{~|ywuvwxvpib_agpxzyvtvz}vngefinswy{|~zr}|~~~vpkigfeecccrvu}{~wtw|{vsuz}|{{~}zyxxwwxz}~}yrlhiow~|xqhronqkqvwxwvtssu{}vpmjiiihjq~wrqsx{|ytnmpw~~zxy~ytrpnllmnnlga[X[bny{xzxz~|vpjgeefiknr}{smihkpstpjedhovzyvrprw}}|z{{~~zx{{z{|~~~}yumwzvz{wtz{}|{yxy|}vsuy~~wpkkoty|{wrnkhhghgjn}|}ywy}}xx}}~}~zrlknt||{~ztqpqrsttspmfiunqqummrsttsstv{zwx}~zvtsrsw}yx{ztprw}ytrsw|~u|{{||}|xrlebciqx|}{z{}|~|yt}yz|}}|{{}xrmjggfimyzs~~|vqnnrvyytnhehmsxxuplkmry}wqoqw~|ytpo|ususxmmnprstvz~{|{ursw{~}{vqlhecddiozvs~}yxy}~wrpry|unifhmu{~}zxx}}wrpooonljfdfunnsq|uqtvxyz|}~~{{|xussty}~|xvw{~ytru{~zy{xw{}}w}{{|||zvpjecejry}}{xx|}ywwxyzzzxx||z{~ytpmmmpu{so{y}z{}yurrtx~{uqqtz{vtuy~{{uollpw~~~}zz|z}{wvuvvxz}zvux}ztppsvz{zxtplihhkputjw||{xz~}~}ytmgbadjqy}~}{{~{wutrqonpxrvyvv}vutuwz}}z{~xuux|}zwustv|~~~ztruzzvuvy}{vs{yzzrs~||yywvtplhdcekt}~|zyxxwy}z~{|xspqux|}}|zxwwx{u}yvtuw{|vqnosz|xuuvy}}yu{}t}xtqru|~{yxwvw{v~|~y}|xyy{}yspptz}wroorvz}~}{ywvwy}s~}|}|xwz{yxy|~|xu{z||xrnjjmsz}zxwvvv|u}|}zz~zzz|~{xy}|wuux{~~|{z{|~|q}|{|~~xsqsx{vtsux{~|{~~w}yupkhhkqy~{ywvvw}q|zzzu||zurrux{}}}{{z{|~{xp}{{|{uqprx~~{yyz}~|}||zt}}zvrmighkrz|zxxx|}px~|w|z{~~xsqqswy{||{{{{||xrp~}{z{}{vstx|{{}}yz}zuu{~}{xtnjggint{~|}}|{|~x}{{|yx{{uqpqtwy{|{{zzzzwqlxw~~~}}}zxz~|zyyz|}}}zwx~v{~rtw{||zwsokgefiou|}zxzww~yww{ztpopsvxzzzzyyxwqkj}}uz~ywy}{xwwxyyyxxwyvyrrtxxxvsplhecehnu{~|yy|w}u|xvw{xromnqsvwxxxwwvsnjm|y}|xvx|{xwvwwxxwvvzuw|soptuuspmjfddfjpw~~~~v{v{{wuvy~~xrmkkmortvwxxxvtomtz}xvw{}ywvvvvvuuuyryvvnnqrssqoljhhjnt{~vw|v~yussv{zupmmnqtvxzzzzxuqmrw~~yvvx}~zwuttttsstws|rtpqstusqoljijmrx~}}v~wvw~zvssuz|vrpoqruwxxxxwtplnz|{{wuvz|yvttstttux~uwupqrsrpnkihhkot{}}zvtsw{xwwz~ztpmmnprsuuuusqmlrx~~yutvz~{yxwwwxy|x{xxtnmmmmkihgghlqw}~~~uz|w~tz{xwx{|vqmkklnpqrssrpnlo{x~{wuvy|{zyyy{|tqyokkkjihgffgjou{~}~yyx{yzwuux|~wrmjijkmopqqpomkmwv~{yy{}{zyxyz~}r~qw}rljiihgfeegjntz}|}}{{x|}yvttwz~xsnjhhhjklmmlkiimxu~}zy{~~{zyyy{~}q|ssumihggfedefimsy~~}~||{wy~~{wtstvz~}xrmjggghijjjjihinzu|yxz~~{zyyz|xq~qs~tligfeeddegjot{zxww||yusrsvz~|wqligffghiijiiiksw{xx{~|zzz{~rr~mu}sligffeefgjnsy}zx}vx~}zvsrrsw{zuplihhijklllkjkoyy{~}zxy|~|zyyz|zoxvn}{rmjiihhhhjmqv{}}{||{yuy|xurqrtx}{vqmkjjkllmmlkjjpzu}||yxy|~{zyyz|yq}vr|snkjiihhijmqv{}{|~z}z|xuz|yvsstw{|vqnkjjkklllkjijozs}{|ywy|~|{zz{~{r}wr|smkiihgggilpuz|zz}z{{zyuy{xutuw{|vqmjiiijkkkjihimxtyz}yxy|~}||}~rzzo}}smjihgffghknsx~|zz{|zy~wz}yvttuy}}wrmjhhhijjkjihhksys{~|zz|~|||}urptvnjhgffffgilqv{}{zz}|{zy~}zvtssvz~~ysnjhgghijjjihginys{z|{|~|{z{||pvzmy|rkhgfeeeegimrx~}{z||zxy~{wtrqsvz~|vpkhfefghijjihhjq}~s}~{z|}{zyz|xnysm|xnifeddddegjou{}}}}{|wz}yurpprvz~xrmifeefghijjjijnxzz~|zz}}{yyyz}}pp{jt{qjgeeddeegjotz~{yww|~zvspopsx|ztokgfefhiklllkknux}zy{}{yxxy{sm|mo~~umhfffffghkotz~~{zxv{~zvspopsw{|wqligfghjklmmlkmtw~}zyz~}{yxwx{~umzpn~xojggfffghknsy}}{~zxvz~zvspopsx|}xrmigggijklllkklsv}|yyz~}{yxxz|un~opwniggffffhjnsy~||~{ywv{{wtqqruy~|wqliggghjkllkjjlt~u~|yy|~|zzz{~rq~lu~slhgffeefhkotz~|z{~{ywz~{vsqqsw{ztojgffghjkkkjiimxvxz|z{~|zz{|znxul|ypjgffeeefhkpu{|zy{~}x|}yurqqsx|~xsmigffghjjkjihinzs{y}|}}{zzz|wnzrm}xnigfeeeefhkpu{~{yy{|y}|xurpqtx|}wqlifefghijjihginzr|y~|~}{zyz|wmyrl|xnifeedddegkouz~{yy{}x|}xurpprv{}wrmhfeefhijjjihhmxtyy~|}}{yyy{~ymuujyyoifeedddegjotz}{z{~{xz}yurpoqty}~ysnjgeefhijkkjiiluzu|||~~{yxxy|qn~~kp~}skgfeeeefhjnsy~|{{~~|zx}~zvspoorv{{vqlhfffhiklllkjlq|u}{tlilrytdXPJGGKQ[hvysrrsuwy{{{zzzywsmedo~xn|pcYSRV_l{{naXQMLLNPTVX\bm|w{wqlgdcflt~}}ule`_agp{zp~yh`aehijihec_[XTSSUX]adfec^ZVTSTWZ_cgknpru{~xrlfb_^^aeks|}uz}{xvtrsuwz}|xsonnosx}yu}wz|~{z{}~xqkgc`___^\[YVUU\gog]myll{~}{{|~qky}jkzzqjfddddefhkotz}|~ump}{glyob`ejmopppomljhhimrx~}}~}~{xutsrtvz|wj`lsa]kmcZTRPPOOOQSW[ahmqstrponptz}}|vpljiijklllkjijp{|o{ys~{vpkgfhkptxzzyvsqnmllmpsysotvzwvwxyz{}yx{~|xtpkfcaacfjmnnkf`[VRQPQSTVWXXXX[cpxphyuu~}v}zrnlkjihijkosx|~zvsqqsw{~wnkusiy|ngiotwxxxwuspmkkknsx~{wtsstvxz|~~~~~}yurpnnnptvsh\epaXfld[VSSSSSTUX[`flrvyyxusrsuz{uqnmlmmnnnmkiinx}nuzpzzupmlmpty}~|yvsqpppqtx~tlxsp}vrqqrrsuvy}|y|}yuqlhdbbdgkoqqnje`[XVUVXY[\]]]]^dpzwjtzs{~xxronmlkjklnrvz~|xtrqrtx}xpq~rp}plpuy{||{zxuromlmptz~|xtqppqsuwyzzzyz~zvsqppprvywl`gtg\ipi`[XXWWXXZ\_diotx{{zwustvz~~|wromlmmnnnmljiks~uost|vrpprvz~{yvtsssux|}qootyroooppqrtw{|y}~{wsnjgeegjnqssqmhc^[YYY[]^``a``bgr}zmu}ux}swpmkjjiiikmpty}{wtssuy~ws{~q~ypqvz}~~}{xurommorv{}xtqonnprtvwxxxxz}yvsqppqswyuj`jtd_mpia\ZZZZ[\^`dinty}~{ywxz~|zyz}}wrnkjjklmmmmlkknvusuy~yussvz~|ywutstux|zno{lu~vpnmnnopqtw{}zz~|yuqonnquy|~~{xsnjgffgiklmnnmmovw~~}{ywwwy}}pky|klz{skgfffgghjlpuz}}{z}}yuromnptx|~~|xsokknolhdbaadhkq{~}}~shqweanoh`[ZYZZ[\^`ekqx~toruwyzzzywvspnnorvy}}xrlgca___`abbbbcfn{x~{yxyzzsf_lrbevytpopqsstttuwy}|xtqqsv{ztyt{~ssx}}{xvvwz~~zvsrrstuwwwwutu{vzw}zxvvx{~}vihwsckvsjda`aaabdfinsx~}}~yz~}zvrnjfddfinrvxxwtpmkjkmortvxxxwx{~}{yyyz}wmurjw|umhfeddddefilpuy|}|yvsqpruz}zwwx{~ytoljijklmnonnmnr|u|z{zz}}yvsqpooqtx{xnfpzkhw{tmjiijklmorvz~~{vsonnorvy{{yupkfca``acddedccdhr{wktxq{~u~xs|tomllkkllnquy~|zyz}|}|zwsvz}~~}|zwtpmjiikoswz{zxtolihhikmoqrrrrsw~~{yxwxz~~tio{odqwqhc`_____`beinrw{||{xusstx|}zxwx{}ytpnllmnppqqpooqx{v{||yy{~}zwusrrsux|}xlixxhmzyrkihhijjlnqty~}yuqnllnptwyxvsnieb```bceffffefku|pw}u~sx{p{}upmlllllmoquy~~|z{}{x}uuy}~|zwspmkjknrvy{{zvrnkihijlnprrsrsu{}~{ywvuvy|~{pgpyjervogcaaaaabdfimrw||yxxy}}zwuuvy|{vromllmopqrrqpqt|{y{{yy{~}zwussrsvy}}vklzvhp{xqliiijklmorv{~}~{wsolkkmqtwxxvrmiecbbcdfhiiiiikq||s|z~}}}vq~qrxqmlllllmnptx|~|{}|zwuy}}{xurnlklnquy{|zwsoliiijlnprsssstz}~{ywvvwy|~zogryigtvohdbbbccdfhkpuz~}zyy{~{wustvy}|xsomkklmopqqqqpqvw{|{|}zwutttvy|~zoiv{klyzslihiijklnpty~~|xtpnllnquxyyvsnjfdccdeghijjiilr|zs{{~}|}sr{ls}zrlihhhhijlnrv{}|||~|xtqooprvy|}|yuqmjhghiklmnnnnnrzyz{}{yxxy{uls~qjv{unihgghhijlosx|}{{}}~{wtqooqtwz}}|xtpligghiklmnnnmnr|w}{}{yxxy|~tlu}okx{tmihgghhijlosx}}{{}}~{wsqooqtx{}}{xtoligghiklnnnnmns|v~{}{yxxz|~tlu}pkw{umihhhhhijlosx|}{{|}{xtqpoqtwz|}|yupljhhhjkmnnnnmnr{w{{}{zyxy{wmr~sju|vojhhhhhijlorw{}{{|}~|yurpopsvy|}|zvrnjhhhiklmnonnnpw|w|~~|zyxyz}{pmzzkozyrlihhhhijknquz~}{{}}~zvspooqtw{|}{xtolihhijlmnoonnos}w}{}{yxxy{~wlq}tit{wojhhhhiijlosw|}|}}}|yurpooqtx{||zvrnkihhiklnoooooqvw}|zxwwx{~}skt|ojwztmjhhhiijknpty~}}~|~~zwsponoruy{|{xupmjhhijlmopppops{{z}}zywwxy|xmmzwipzwpkihhhijkmosw|~}}}}|xuqonnpswz{{yvrnkihhikmnopppprwx~}{ywwwx{~{pkwzklxyrlihhhijklorv{}}~||yuronnpsvy{{zwsokihhikmnoppppqvx~~{yxwwxz}|rku{mkwysmihhiijklnrvz~}||yuronnpsvy{{zwsokihhikmnoppppqvx~~{yxwwx{~{pkvzllxyrlihhiijklorv{~}}~}|yuronoqswz{{yvrnkihhjkmnoppppqwx~}{yxwxy|ynmzwjpzxpkihhiijkmosw|}|}}~{xtqoopruy{|{xuqmjihijlmnopoooszyz|}{yxxy{~~uls|qjvzunjihhiijkmptx}~||}~~{wtqpoqswy||{xtpljhhijlmnooooos|w}{}{yyxy|~tlu|okwztmjhhhiijkmptx}}{{}~~{wtqppqtwz||zwtpljhhijlmnooonot|v~{}{zyyy|~tlu}pkwztnjhhhiijkmptx|}{{}~|xtrppqswz{|{xtpljhhijlmnooonos{x{{~{zyyy{~vmr}sju{vojihhiijkmosw{}|{|~|yurpppsux{|{yuqnkihijkmnoooooqx|w}~|zyxyz|zpmyykoyxqlihhiijjlnquz~}||~~}zwsqooqswz{{zwsoljhhiklnoopoopt}x}|}{yxxxz}~vmp{sjszvokihiiijkmpsw|}|}~~|yurpooqtwy{{yvrnkihijkmnpppppqwx~~|zxwwxz}|sks{ojvztnjiiiijklnquy~}}~}}zwsponoruxz{zxtpmjiiiklnopqqpqt{|{~}zywwwy{~~wmmyvjpywpliiijjklnptx|~~~}~{xuqonopsvyzzyvrnkjiijlnopqqqqsxy}{yxwwxz}~zpkvylmxxrmjiijjklmpsw{~~}|yurpnnpsvyzzyvsoljiijlmopqqqqrwy~~{yxwwxz}~{rkt{mkwysmjiijjklmorvz~~}|yurpnopsvyzzyvsoljiijlmopqqqqrwy~~{yxwwxz}zqlvzllxyrmjiiijklmorv{~}}~|yurpooqsvyzzyvrokjiijlnopqqpprxx~{yxxxy{~~xnnywjpywpliiijjklnpsw{}}}~~{xurpopruxz{zxtqmkiiiklnopppppszz{|}{yxxy{}}uls|qkuytnjiiiijklnqtx|~||}~{wtqppqsvy{{zwspljiijklnopppopt|x}|}{zyxy{~}slt{okvytmjiiiijklnqtx}}||}~{wtqppqtvy{{zwsoljiijklnoopoopt|w~|}{zyyy{~}tmt{pkvytnjiiijjklnqtx|}||}~{xurppqsvyz{zwtpmjiijklnopppops{x||~|zyyy{}~vmr{rktzuokiiiijklmpsw{~||}|yurpppruxz{zxuqnkiiijlmoppppprx|x}|zyxxy|~zpmxykoyxqljiiijjkmorvz~}||~}zwsqppqsvyz{yvsoljiijkmnoppppqu}x~|}{yxxxz|~}vmp{sjsyuokiiijjklnptx|}}~|yurpooqtvyzzxuroljijklnopqqqqrwx~|zxwwxz|~{slszokvytnkjjjjklmoruz~~~~}zwtqoopruwyzywtpmkjjjlmopqqqqrt{|{~}{yxwwx{}}wnnyujpyvpljjjjklmnqtx|~~~~{xurpnopsvxzzxurnljjjkmnpqrrrrsxyxj]VX_ghmsd^^ecW^mmgn}z}zvwodefca`_`de_emozyyojaZWW[dow}|nemukionof\__]\\Z\aeemxwx|{umhb`c^Yadbbejnpplorgabdmncgvwpoyvuwzzwqssfba]Z[]bkuy~xxvrmjggjkhqyvxzxrkfa`c^\fhghlqwyxv{|pllnwvjp|zpr}}w}v{|miqmimhcacgkmljomccb_^_bgpxyzuvwpqyyxy}usrx}tmyypx{u|~}yvwtqpnmnngfpqryymirk_bgbdgb__chllkmrjdecaacfmuyzx~wvw~srvq}usyzz||pooljigghg`bllourfhpf]ceaegcabgloonqulghfcdeipwy||yxztuur|rqxxxz}~xmmljhgffge^bllouqfipg_egchjfdeioqqptwnjkgefgkqxy|~{yzwuvq~~|spvxxy}|~ynlljhhffhf_almoushirkafieiljggkpsrruyrkkiggikqxy{~|yzzs~{qy~}votxxx{|}|qlmjihhghhb_jpptumgqrgdjhhnnkikqttsu{xomlihikovzy~zy{txss}zqqwyyy~}|}wnmliiihhif_eprrwsilvofilimqolkotvuuy|uonkijlnsyy{~|xx||s{{pu~|wprwzyz}}}tnnljkkjkkeaisstwqipwnglmkpsqmmrwwuw||tpokjkmovzy}|ywyws~wpz}{tpuz{y|~}{ronkklkjljbdpvuwvmkvvkimlnsuqmpvyvvz~yqpmjkmosyyy}|{ww{|sy}ps|xqrx|{{~~vpomklmllmfblwwwxrjrzqimnnrvtoouyxvz~|tqokkmnqwzw~|}xwx~tv~qq}yrrw}|{~yqqnlnonnoickwxwxtkqztkmoorvvqptyyvy}vrpkkmopwzw|}{~xvw~~uuqq}yssx~}{~yrrnlnponphcmy{xysks|vlmnosxxrqvzyvz|vrpkkmopwzw}|{~|xwx~|su}pr}z~{~{vsvuv}}~wwwutyvqx}~yvxxuxwoq~y{xtturnlkmr|zw|~w}ujq}~wz~{~}~{wppsrnqmdgt{xwxmn|}rmprtx}wqtyztu|ztoleadgfjpln|~|urptvnitvhdq}|x~}{|~~zqv~|puqortuy}uquzysx~zvrogeilkqvqv}}zvtst{xuy|{tx|wpljiopifstgft}z~|z{zz|y{~~|~{}zwnlptqw{vz}}|wz}{|v}}yxzzsnmmmsytpswvpsyuokjhmqlgsxkht{|{z}{~||soswtxy{||z{tv||x~{swy}wswxrlklloyupquwqqzwrnmjlrpho{qir}|zzz||}~xqrxwv}z~w~}urx}wy~us}xzzrtxvmkklluyrptxuox{uqnlkqtmkyylm{}}{zz}|z{|~tquzvz{|||w~{stz|v}}sv~v{vqvyslkmmozxqquxsq{ytqpmmstln|vkq|zzy~|~zz~|}rqwzu|y~|xx~xrv}{wyrx{w|sswyqllnmr|vqswxqu}xtrqmpuskt~qlw}{zzy{~z|||{}woszww|z}~t|ssz~w{~ttv{xqvzvmlnonz{ssvzuq||vtsontvon}ymq|yzx}}z}yw|{{ppxyt~~x~tyvryzzxt}wy{quzyolnqox~usuywpz~wutrmsvql{|op}}{yzy{~z~yw~y~~}pow{s|x~~}vvxrx{yzt{xx|rt{{plnqowvsuyxpzwvusmrwskz}op}}{yzyz~y~xv}y~}|ooxzr|~w~~txwsz{zyu}wy{qv|{pmpsp{wtvzxq}xwvsmswrl|{nq~zxywz|y|y~vzy~xmqzxs|w~~q~tt}xvxu}xqy~xmmrrq~uuw{ts|xwvpovwnpvnv}~~xwyv~x{}x|wzwz~rmu|twxz|~xs|sxy~v{|w~st{~tmotqv{uuyzqyzyxvnqwvkvqo{|~{vxxu}w}}x~u}w|}~pmv|rzw|{wu}v}}z~}wzwrtyv~~zz~~u}}|yru{xn|ss}yvxwu|{t{z{t{}|}oyyrw}~}x{ljsyow}ty{xu~v|}z|wyyrtyv|zz}u}~}|{rt{zoytr}}~{vwxt{~}tyx~tyzpv|rv~~}y}mirzpt}twyzqw{|{zzw|rsyxyzz|wy}}}ury|rsxqy~}~vuxs}zuwyvwyvqss{}~~}ythmxvo|yt~||rzzw~|zu|urv{u}zz~|t}~~yqv|xn~rt}xuwvu{{t{y|u{~{~ox{qv~}}x}mir{qt~tw~x{owz||~x{u~~rsyzx|z{yw~~vqx}up{qw}vtxsx{}~wu~||yv~z}{o}wrx|}wzjkvzox|sy~xvrv}|{{wxzqt{w{{z{u|~~ss{}pvvr||{{tuwrw~txxvyxssrs|~~~~{{qhp{tp~vt~~y}~o|yy}}}y~t}srw{v}zzzu~xqw}wo}qw}~uswsvz|~xu~{{ywz~zo~vry|}xxilxyo{zr|}{xqu|w~|{u{vqv|v{y}}t{qu}zn}ru|}vsvts|yzt}~y{v}{|}o{yqw|}x{jkv{ox|ry|}vtr~w}{|~vzwqu|w{y}}t|qu}zn|ru|}wsvts|zyt}}y|w~{|}p{xqw|}xyilx{oy{rz|~zvqt}x~|zu{vqw|w{y}|t{qv}yo~rv|~}urusty{~xuz{yxyzpvry|}~yuhnzxo|xr~}|~wy~n{yz|}~y}v~rryzx|zyxxvqy~srxr{}z}rsvp}v~tyww{ysurt}}~~ynit}rt~sv{}|svnw~z||xyzqu|x}|y{u~rs|}pxts~{{xqstp~v|t{v~w}~{qy|qv|}y}kjv|pv}rx{}{ttpw{|}~wzyqu|w{y{~t~rt}|ozsu{{vqtsq|x{t|~w~w~}|qz{qv~|}x|kkw}pv}rxz|zsupw{|~}xyzqu{x}|y{~urs|~qxtt{zxqstov}t{vw}{swru}}~~zmju}st}suz{{r}xmx}z|{{w~rtzzz}zyx{vrztsxr{~y~rqtoyv~uxw}xzyxsusz~|}~|uip|yp|xr}|z|vvov{y~~}{v|vrw{w{y}|v|qv~{p|rv|~{uqsrq{x{u}~w~x~~|ry}ru}|}~z~mjv~tt}suzzzr{ym~y}z}{~w~ssyzx}zx~yyxqyxq{ry~z~sptpux{xvzy{y{}r}yrw~{}zzjmy}qx|qxyzxruoyz|~yy|ru{x}|yz~wts|svvs~|y{qqso}uuzvx|zvutt{}~~}|qis}wr}ur{yzsx}nzz{{~~{w}usx{x~{x~{xyqxyq|rx}{}tpsptx{xwzy{y{}s~xsx~||{xjo{|qzyqz}xyusqs~z|}|w{xrw{x{x|}v}rv~|q{~sv|}{vprqrzy{v}w}y|~s{|sv}{|{zkmy}rx{qwxyvrsqz{|}x{yrv{x{x{}v}rv~}qzsv|}zvprqqzx{w|w}y|s|{sw}{|{ykoz}ryzqy}xxurqs~{|}|x|xsw{x{x|}w|rw|q}}rw}{{tprpsx{yxzy{z{|t~xsx}||}ujq}zr{wq|{xxrv}ny{}{}~|x~utyzy}zx~z{wszvsxs}y}qpro{vw{vy}{wwtu{|}}|~nlwuu|rtxwvp|vozz|{z|sv{y~|xy~xtu}sxtt|yypqqp}w}w}vy}uztv|{|~||lmy~tw{qvxwuq~tp{z|zz{sv{x|xz~x~sv~~szsu|}zwpqqq{x|w~}w~y~~u{~tv|{|~|{lny~tw{qvxwuq}up{z|{z|tv{y|xz~xsu}txtt}yzppqo~}v~w}vy~}wyuv{{}}}~nlwvu|rsxvupzxo~{z}~}yuuzz{}yx~y}vs{wtxs}xrproxvx{w|z|{{vxty||~{~tks}{s{wp{{vvrspu}}~}}}y|xtw{x{w{}y|rw}s{}sw}|zvpqpq{x|x}v~z~vzuv{{|}}}nmxvv|rsxvupyyo|{z}~y~vuyzz~zw}{{xrzzs~zrz~z|tpqoux{zyzx|{}u}|tw|{{}ylo{~txzpvwvtp|up{z||z}tvzy}|xx~yut}vvut~x|qpqo{vx}w~z~|ywwuz{}}{~qluzt|uq~yuupu|oy}{~~~~y}wuxzyzw||zzsy{s}{ry~z{uopnr~tsxvx{{ss{u{}idff]\eouwfaicbdf`coyvy{vojkmgrxttpdY`kmdhkdk}mfmgdfgacozxywuslpvkb\^b^ipnpncX]jndfh^ctyntmiqyucZYWWdgdhfe[Zhhjornnx{xtqljusnokg[Zfdfjniku~yx|}pcgqqfig]csvmskjs|wg``_bqrrussikwvx{}vv{y}w|z}youy}sqywe^]Z\iihkhf\`kjmrsnqzxy|}u~vrrpv}rz~z}y|{||zzy|||}xu{y{zkfnvpind_l|~pppfluyj]Z[Yblimmlg_kqptwupw~u~}~|{z~x|xy}||xw|{stwuz}uopupx}|yo`akpfeg\^m{xkokenxwg^]]]iomrqpherstwzusz|v|~|~y~y}z|vv{wqrtryyqlnrnw}zzuk^akneff[_p}wlqkhr{xh`__`nqpusrihuuvy{vt|{v~{}~y~y}{|vuz~xpqtqvyqlmrnv}zzvm_`kpgfh]_n}znrnir||lbaaamtrvutmiuwwz|xu{}v~{|~yy{}zyuy~zqqsrt|smkqor}{zxqb_hpjeja]j{rqrjpzsfcdbkvtwxvrisyxz|{vyw{{}~}}z{y~y~uw|}uprsqzvpkoqoz}{zui_dnpgig^euzqvonx}meeeftwvzxwnnzyz|~yw~}w~{~|~y}yz~yztx}{rqsss~~uolqqr~}|ztf`hrogke_j{xtwoq|zkffejwwx{yvmr|z|~~xy{y}|~~}|}x}x|{{wuz~yqrtrv{snnspw}}ypccmslilbcquwuovujghgqyx{{ysmx|{}|x}x}|~~~~|{}z{yyy|ux}}tqsts~wqmrrr~}|vjbisqimhak||vyrs}}oihglxyz}{xos}{}~yz{z}}}|~}{~|~~}y{y}z~vw|wrsus{zsnqtr}~~ynchrtkmkbiyvzts|qjihjwzz}|ypq}|}~zy}y~|||~|{|}|}y|y}{}ww|xstut{{toqus}~zodhrtlmlciyx{ut}skjikx{{~|zqr}|}zz}y}|}~z}{r{ylo|~v{wz}~~}|{xxqlorr}v~{umljmxyy|yuloyxyzzuwzy{xqmqrtzvz||}nhgekvuwywskrywyzxsv||stxrty~ytzvpsxxonpqr}~vqpvv{vs}~{y}~z}}trsru{soout|~tu{}{vut}~uqz|z{{wsx}xqvuqu|ww|uty~zstvv{zuv|z}rs}{z{sw{xxx}z{~w|zvyxx|ssw{voprqw{tqrxw|ru~~w|}||~|~z~{||z{||}yz~}{qt}xxxou}trqq}}ts}||||wuz}wqwtqu{}uwzrrw{vpqtsz}wsuzy|qt|||uz}|}}}~{|~}qr{xvwoq||ronnx~|}{soyzyzzvrw|wpuuptz~wv{tsw|yrsuuzyuuzy}rs||z|uw|{{}{tpxzuwpn{{snmlr|{}}zunuzxyywru{zrswrsy~{u{xsw|}utvvw|wuz{}vqxy|ws}|{z~{~ypt|}uvtmt}|xomlmy||~{yppzyyzyusy}wqwurv}xy|uuzzuuww|zvw|{}rs|{y{sv~{zy~~{upv~{tvqmw{}uomkp{||~{wos{yyzytu{}usxtsyw{|uw|yuwxxzvy||zqu~yzyrx|zyz~||}qqy~xtuno}{}rnllu}{~}ztox{yzzxtx}{svxsv||x~yvz~wvxx{}yw||uqy}xzuszyx~}~~{~vou}{tvqmv|}vomlp{}}~|xpt|z{{zuv|~wtyuuzy}|vx~zvxyyzwz|}yqvxzwrz{yx{~~{}yps{|uusms~}yqnmoz}}}zrs|{{{{vv{ytyvuzz|~wx}|wxyy{wy}}{qu}yyxrx{xwy~~~~{|yps{}uusms}yqomoz~~}zrs||{|{wv|yuzwuzz|}wy~{wxyy{wy|}yqu~xywqyzxwz}~~{}xpt||uusmu~~yqomq|~~}zru}|||{vw}xvzvv|z~|wzzwxyzzw{|~wqw}wyur|ywv||~}z~~tpw}ytvpo{}uponv~}wrz~|||yvz}vyzux}{yx|xxyy}}xx||}srzzwxru|xww|~~~~{{~|rrz~wvvptwtss}zx}{z}z~||{{}|~zz~~{rs{~wvvpuwtst~zy}|z}{|}{|}}}y{~~{rt{}wvvpvwutu~zy~|{~|}}{|~}~zz~~{rs{}wvvqvxutu}{y~|{|}|{|~}~zz~~|ssz}wvwruxvut||y|{|}|{|~~~zz~~}tsy|xuwsuyvuu{|x~|{~}~|{|~~{y}~urx|yuwtt}zwvuy~x}}|}~~~|~||~|y|~xrv|zvwus{|xwuxy{~~~}|~~}}}{~}y{~zsu{{vvvsy~xwvw~{z~}{~}}{}~zz~~~tsy|xvwtvzxwv{}y}~~}|~|~||{y|~xsw{zvwvu||xxvxz{~~~~~|~}}}|~}z{~{suz{wvwtxyxww}|z~}~~|~~}|~~zz}~uty{xvwuwzxxw{}y|}~~|~|~|}{z}~wsx{yvwvv}|yxwz~z|~}~}}~}}|||y|~ysw{zvwwu{}zyxyz{}}~}~}}}||y{~ztw{{wwwuzzxxy~~usx~ut~zuw|yw|~t~zx~}}ur}wuty~x~|}yuttwyywwyyxyxzxw{zysqtx|xlstzry}v~t~xvxzz}~~}~ypnxw~}|~t|np||{{xrpx}vxt{~t~yrt|ly{u~wnswwsuz}lgqzmw{}{yu{{|~rjp~vr{t}rsuutyurzz~svzhvvnz{pr}zzohszkt~szx{u~{zqu|{|rzvx{t~ptzyw}{wjm|xm{wp}loz|~{|rdiuywts}||uzojuosux{r|zu|y{xnbgvjg~joy|y~|no}yl{~s}yso{}{{lbnxhlwlmwzv|wx|siqvqzsop{}x{zqq}hrvmv}}x|yoer|mswy}muzzxqfoysu|yvjty~~prktzqy~yoy||||wnu~ml~yyuzgkvutvfj}{pv||xr{{z|||ojztxts|emzyt{upzstsiyyu|yqp}~y~|~kiy~mwyxr{yu|vtz}}rz|xxr~kmz~y{}}lm}jv|t{|tjw~wz~xs}}sq|x~x}tww}v|}oo}lyuz{}pw~wz}~wu~lcmzovjm}y{|po~o{~u|vxozxs|~leqmjxrtwu}|y~z}s}ojzzo|vrzpm|{stut{wnt{ro|w~{~|zms~{fdx|nr~y}nk{uqys{{jmw|~}~u}osopxy}|~yynksqsp{yyrx{|xupsnjwtswul_w{uw|kgxxwtp~~{}w|x}|xvzzxzt}pjmg|ulttksyjujv{~||lp}~~{z}vsz}z}y~yxqs|vox~sm}|~orxynq{orw~|xmmtjpzmzykq~}to~qq}vluy}zuz{~{uyyyvxmr}zstrju}jrdcwzv{rlf{}}~}}wu|zuwzh|{~fs}tv|obholqtjvqu|{xkoontzq|ujr~~tv|lv}rxxko|{|}wpjqn~{{rrowzvsdko_py~ny{qlwzv{|rrqqy~|y}|~zsumk{{zzxkutlpnbkq_nyx{qek{vtznqw}y{{|~xwkn|xl|~gpnlxxjhkjdhy{qjqvu{qzmk|}x~yt||r{rt{uj}z{{nnimw{zpolcsjkrvu{yt{zb`wywy{rz~zywrtvxthq{tv|~{utnkvnc{gmuoowxuzn^cyz|ustxsqzmoljw~|nnsldxqvsnrty{{xnjdaku}rr||}~vv}movq}}y~~{ytqhq{mllnoltuxwcfy|ldox|thj}~~z~uu}wd_osrxksxquzrgeigstsu}wimtrrv~zxoluxz|z{|||}y{}{opjz|}|pxhpzovsifiq}zvz{tmxvvttjehuzwmvwwu{{lr{wkqqs~x|vnlpggvulzom{rw}olwvyvqtpusyjiulo~wtwxuzy}|xixfb{xf|ytxw|mkbkvyvqlxki{u}}q|xqu~vn|o\y|gxw~t{x{mttm|wnjw|tw|~v|xsrv{zy^urZlsgr~|w}}}}oxep~~~||xuwyqpsxtvznlrp_fxsz|wwv|zif~ql|tx|vszyxz}puwewppzjqoywuszuo}ntfdsvwzyzrwwwr`zv{rt~mv~r~~yy{}{uw}rzwmu~}px~xylnsw|`spwssw|gr|u|yvwz}vvr}pzxxm}vttpyxjznszgh|yu~stmlry|{rsk}xy~~utx|{}prrizkjyu}vzz|uppp~nvunl~vxnzv~xy{rwxzz|d|usxs{j}yuw{nwxk~oswqqn}}x{}sw|zvzyh~}m~m{}qswm}xs~ar{}lq}y{ysi{u{ypm|yd{|hYghisr~npugmwoDJ[]d{sr}js~tkzgp~xSN`^XhiXjsqmzk}~xZ_eZV]JXwtxfipanupOUdVOcWLfrsk~h_|j]hgcgnclyulitbkvuVI[_`kfWhqqfxsiksh_h\Rnulevd\s}wn|v}|kzxenoiafe`jrraplk}sWisimycewyrwrxifpqt}|rs|||zz~}x}xrygguskmo`mxwgqj\ozu^fkeaiah~|{}lv{x|}|vq{~|~szx{zyt~nutr{{xymez~xm|st}{b[kd^glWcvzswxbu~vbfgb_gfcxxzptpgw}jux~{uguyx}t|ponprrphzu{~ssso[bffgrcXjtrcqgcpthdomfgtejyzpitgqefrlforfzx~r~zs}{rp~uoqn{oeopnroUbtslmxhnqk^eb^`fe[o{}sssoz}vchjggurm{u}sozr}~|vr|{{|~z|ickpkoqdt~}u|~jiro^efggmcXlvpamldpxqfqnho|lj|}vp|rxpqvolpxnz~|uz|rx|x|yiwzvt|wkw}vhqqrmj__qsrktogloi_aacembi~|s~xxokolnuquzu|rzr|}}||}qt{{x{v{xcjqnjpogx||v|uajuk^ghihm`]vuliumiv~upumqx}pr}xtzsruqou|q{~|~{~swu|zvyxjtvxvvvmu|vhqtrggearutpwqflunaefhgpkr{t}yokqqt|ov{zv|y~{{ry|zzvvz}obpqnltpj{}~z|jcrvjemkkhjbkwomnumo}sqrty{~ov|y}~tvussz|t|}z}~yzr{x~zxw{nkvzzv{qs||orxrfdneowvssugkxt`djkiqvq{zy~vmtuz}tu~xzz}{~~}sw|zs~vxyoisronusn|ysjjtxmgomkgmjmsoqrxpvtqv{{||px|||{rutuu}|u}}zw~~uu{|}yywxin||xwzrw~zqxtidopiuxvsvoivziallmn{xx}~y~~qpx}|{t}{|z~}z{~}y}wv}}y~}z|{uy{yx}xy{y~}u{zwr{}tz|}yy|}wwxwx|~yuytvyytxrv}|zz~tv}~|uzy|w~z{us~}~z|nw~{x{xsqmmvwonz}z~wotvnquqoy{|uy|xsszpsv|stvtrv}yy~~}qqvolpmps{w{w}mpvvtw|z}unz|{vwtoy|yv~rkporqvu|yy}vszxkjjnqsumwz|{{z}|uvxuomjftyvuz~y~{v~{{~{y~{ry|tu|qvwutx{szrv{}yyzt{zuw}wz{y{pu}|xz}{~~|ru{wpytuwu}z{zysiqysptuyz}zy{|syz}z~xqv{|z|v{~ryx|~|yy{pxxx}xy}~}}~}ut{wtmpmnxxvt~trzvsnpou~{z~~|tv~|~~~|z~~z|~vrr{~wux{wuprtvwvysxvuw{px~|yxrmryqqxs{yx}}{|qp{~vuy|~}x|{~}}wzxt}{|y~tp}xyzyvvtmyvrwztv|{z}vz{zy|}|yvvxvrnppqx}~zzwx|xuovux{}vq|{z}~}|}}|z|youu{}|||tqy|rnpsstx{|~wsvvy|uq~{xwspxzu||wyzy}|yswuw}~yyz{~{yz}|}s{|z}{wpz}wyxwurqu|tqvzwt|z{xu|zy|yx}xwxsqpurw}~}z{zvpwzx}|~up~|z~~}w~|~xz}{~toywz{{z|sp|}nlrutt}}~||ytvy~|tu}{wxusz{|zz}zy}zxxy~uy~ww}~wzzw{{|yx~~y||ytw|{uxwwqrwwtpuy{w|~x}|uy|y|{uwxutquxt}~~yzwsv{{x{~yr{}~y}}}w|~~|ww}sqzzz||zz|ur|}pltwvv|z|}vv|xvz}~zwxwwx}~z}{z~|wy|z~}xzyqz~{v{ww}{~}{|{|{~|{uzz}zvwyvpwzrqsxy{}|y}zwy{{wy~xvvttxxy}}}}wytwy|zv|zuy|~|{zy{{y}}z~}uyxrx{}zz{x{yvx}xpovxw|~y|~wx~v{}{~xww{yxz|}|}~|yu|~|||{|uo}z}x}|vx~|~~{x~~||~xt~}}}wuzvsx|qntxy{z~yzxy|zz|tuuwxy|}}{yvtx{||vxwu}|~|z|wx~z||~{}|ww}}xsz|}~y{z|{z}~zvuw{|~~}}{{}}~}~}}wz~{}~ypt~{yzxy~|utxzy|~~~zy{|~|vw~~}|{~~tw~~yw{wx~}rqy|}~}}xyz~}}}vv|~~|xyst~}~}zwzuuxu{{z~~{yxx~}}~ytxz|}~ur{~~zyy{zxxtwyy|}}{z{|{|wx~zz}{qt{~~}{ywz{uuxyz{~~{~yx|}~{t{{z}vt~~}}xyxx}~vqt{|}|~~yx|}|ty~~yy}zpz~}}yyxtyztw}{zyxx|}}uwz||{zpx~|ywzzw||su{zz~~}zzz~}zxw}~x~}qt{|~}||{vy}ttyzz|z~|w}~~|wz~}x}}tt~~{|zyvx~urtz|}}~}zw}~|~{uy~}y{~wr|}{|yzvu{wtx~}{}}}zw{||{uy|}}z|vq|~{yvzxwxrw|{z~~zyy|{xuz~{zuq{{~|{z~xvvrw{{{{~}z{~~{y{|y{~ts}{zz{vvvrux{|~}}|x{|~}{wz}~y{~wu{~zzz{vv|}vsx|~}}|}{x|}|~}zvz}~{z}tt|}{xwywywrx|||~}yxy~~{uv|~|}}rv||||z{}wy}tsz|{~~}|||~~~|w{}~|z~wpy~{x{zu{{rtxy{}}|z{~|}{wz|{z~xt|}|zx|yv{~wru{|~}}|zz~}|{wz|}y{xsz||zwxwv}zuuz|}||yw{|ysx}|~wsz}~}{zy{zy}ysuz}||}{z~~}{v|{|upz}~|w{yw|zsuyyz}}{z|~~}zvz}|{~xs|~~}{w{zv|wruz|}}{{{~~~zvz}yyxsz}~}zwywu{zuwy{}~~~zxy~~}}{uv{||ysx}~{zxzzx||uux{}}{{z}}{wx}|{~|qs{}~}{xzxz}wtwzz|||zz~~~xv|}{}~ut}|zxzyx~~uqvz|}}zz}~}zu{~yy~wrz~}{wywu{ztw{{|{yx}~~{vvz~}{{rw}}|zxxzw{}uty{{|{{z~~{xv{~z}try||{zzxx~xtvz{{||y|~~|vy~{|wry~|{yzyx|zrsy{||y{~}~|vx~{y}|rw}~||yxyuy|utz|{~{zy|~|wvz}}{~}st}}}zxxzxyutx{{~~{zz}~{wv|~z}~ssz|~|zz{wyvtxz{|{~|y}}zwzz}~ts{}{zyzwywruy{}}~|y|~~}zvz~~z{~wrz~{{yyxv|wtv{}|~{y{}~}zuz}~z|vsz~~{xwyxx~ysw{|}~|zy|{wuz~{|urz{~}{zx|xxxsw{{}{}}z||yx{~{|}sr{}{zyzxxvsuy{}}}{z|}|xw|~~z}}uu{~~{yzywy~}usx{}~~}{y}}~~|xw|~|z~|sv|~|{wwyv{}vty||zyy~}yux|}|~zrw|}~{zxzzx|ztuz||~~|{|~|zv{~{|vqx}}zxzyx}zsuxz|}~|z|}|zvz~|{wsz||zx{xw}~xsuz|~}~|z{~|ywz}~z{wsz|{zwxww~yuvz|}}~zy{~|yty}{}wsy|~}{yxzyy}ytvz|}}~|z}{zvz||vqx|}{wzyx}{tuyz|}|{{~~zvz~|{~xry}~|{xyzw|ysty|||{{~}{vz}{z}ysw|~}zxxxvz|uuy{}~|zy}~~~{wv{~~{~|sv{}|zxxzx{~wtx{|}||z~~{ww{~{}~try|~{zxzxyxtvz{|||z|}zv{~||wsx}~|zxyyx|zstx|}}{{~~~}{wx~|y}{sv{~}|xxyvz}vty{|}}{y|~{xvz~~{|us{|~zywyxyxtv{|~~~{z{{yvz}||wrw{}|zyyyx}zuuy{|~~}z{~~}{wx~}{yrv{~}{xyyx||tsxz|~}{z~~{ww|}z|}su{}}{ywywy}wtx||}}{z|~|ww{~~{}~ut{}~zxwyxywtw{|~~}{z}zwv{{}tsy{~}|yxzxzxtwz{}|~}z|~{yw{||~urz|{zxzxyxsuy{}}|z|~|xw{{|~vsy}{zxyxx}~xtvz}}~|z|~|yv{~~z|uty}}{xwxwz~xtwz|~~}{y}zwv{||tsz{~}{ywzxz~xtw{{~|~|{}xja^`bgqxxu}|rjdcbchmignwxllnnokgcbcbehd]`egknx||}}xnhgcejlhhpy}ug]_bbb^\Y[\\cgc^_ehkouusqpsvwsnklkkoojbcgffb_^`ehnwwssy~}tqrolgb_`cejrsopv|zvusruvnfhjklpusqnosvwrmnnnquvqkosttqonptv|{y~}|{|~~ytrqoptrlegjiiecbdhjqxxtuz}~~zxy}|yvz~||vquy{zwtrsrrvwqjkmopsxuronsuvqkkkjlprlfgklmjhginpv}~yw||xuw{~}ywy{z{}zqjmrstqnkkkilpkcbfiilrrpmkpsuqljkklqurljoqrqmlmpsv}|y|}}xuswz|yuuxxyzzsihmnqnkigighmlfacfhjospnknsvuqmnoorvwrlptstqpoquw}|{}}zwstxz{vtuvvwywnggmopnkhhhgimkcadgikrsqpmqvxwqoppqtyyrotwwvsrqtwz|}}}ytrtxzyusuwvxyulfjmopligghgloiccgikotsrootxzvrqstuz|xrrwxxvssswy}}}{{vsswz{wsuvvwxwofglnpnlhhihknmfbeiknsvtsqty|{urtuux}|vrwz{zvutwy{}}~|||xsrvxzwstvwwyyrigloppmjhiikopiegjmntwutqty|}wtuvwy~~xtvz{zwutwy|~~~{z}xtsvz|yuuwwwzysjgmorqnlikjkqpjegklouxvusuz}}xuuwwz~~ytw{{|xvuwz|~}~{{~}xtswz{ytuwxy{{tjinqsqokjkjmqqjfiloqvzwutv{~~yuwxx|yuy{|{wvtxz|~~|z{}~{wsuy|}xvwyyz|ypikprtromlmmptpihknpsyywvuz}}xwxxz}|vvz|}{xuvy{|~|y}|zvtv{}{wvxyy|}xokquxyvustuv|}xstxz|~{xx||yvz~}yutqpssmhjnrv}{wtuvw|xvwyyxzwssrw|~}xvssutmhjnrv~{{|{zurnorty~|vtwxxyzxsqosxzxttvwwyzulglorromkmmntuplntwzwqtwxwspmmlmrtokmqvy}}~~{|}}{|ytqnqvyxtsvwxz|xpkosvwtrqssty|wrswz||yvx}~zwyzz{|ypikortqommnnswtonswy~|y{|}{sty{~|yxwxx{}vtvz|}~{y{||{}~}|}xtqpuwyvrsuvwyyskinqttqonppsxyspswz}}{vz}}{yzzz|vvz|~|{}ywvxz}|xy{zyzytpmptwxtrsvwx{yqjlqsvusqqssv{ysruy{~zx||z{{}{uwz}z{~~{xuuwy~ywyzzyzxrpnrvxxtsuwx{|xpkoswxutqstty|xsswz}~yz}zz{{~xvy|~|y|~~|xvuvx{}xxz{yzztqnpuxzvtuwyz}|tmnruxwusstux||vsvy|zy}|z|{~zvw{}~y{~~~ywuvxz~xwz{zz{wsoptxzxuuwxz}}wonsvyyvtstuw||wsuy|~~{x||z{{}{vwz}yz}~~zwtvxzzxz{{z{xspoty{yvvxy{~~ypnsvyywusuux}}xsuy{~~~zx}|z{{}zvwz|~y{~~zwuwy{yxz|{{|xtqqvy|yvwxz{~~xpotwyywutuvx}|vsvy|}~yy}~{z{{~yvy{}}z}~}ywuxy|~yy||{|{wsprw{{xvxz{}}unpuxzxvttvv{~zutwz|||wz~}zz{|}wwz|~{{~}yvwy{|yz||{|zusquy|{wvyz{}{rorvyzxvtuuw|}xstx{~~|{w{~~|zz{||ww{}{||ywwz||z{}}|}yurqvz}{wwy{|~zroswyzwutuvx}}xtuy{~}|zwz~~|z{z||vwz|{|}zwwz|}z{}}}}zvsrvz}|xwy{|~{soswzzxvtuvw|~yttx{}}||wy}~|zzz{|wvz|~~||~}uqosy~~||{z|}}}{ywwwvxyyy{zvx}y~|xvyz}{}wtsw~~{|}wx{}|yyy~vq{yy~~}}}}~{}}~x}ypquwxwustwwuw|}v{~}zvz|{zwuvz}|||zvsrstx{~~|zvty~yv|}{{~|yxz~~zvuuvz}~z{zy}~~}tr{|tsz}yxx{~}}w|{potvwvttvyxwx~wz~~~}|ztwzzyvuvz}}}||xtrstwy|{yxusx}yv||{{~~}zxx{{vuuwy}|y|zy}zrs||usz}yxy{~~}}{w~ypptuvuttwyxwz|w{~~~{~xuxyywuuw|}}}}|xtssuxz{zxvsu{|wx}}z|~}{yxy}zvuvx{~~}zz}x{|tqx~ytw}{yy{}~}~~}w{}sortuuttvyxwy~wy~{}{uwyxwuuw{}}}{~zussuxz{ywusuz|xx~}}{|}}{yxy}{wvvx{~~}|zz}yz~{sqx~ztw}|zy{~}}~{w|}sprtttstwyxx{|vyz~zvxxxvuux|}~{|~zuttvyzzwvtsv{{wy|{|}|yxx{~zwvwz|}||{z|{y{~wqs|~xuz~{zz|~~~}~~wyxqrtttssuxxxy~wv}{|wwxxvuuv{}}~z~}xttuwzzwvutuy|yx{}|~|}|zxxy}~~ywwx{}|{{z{~zy|zsqw}ww|~~{z{}~~}~~{w{wqrttsstvyxy{}uw{}xwxwvtuw{}~|z~~yuuvxzyvuttuz|yx{}}|}|zxxy}}zxxx{}{{{z{~zx{~~zsqv~yw|~~|{{}~}}}~}{wyzrrsssstvxyy{~~vv|{{zwyxvtuvz}~~y||vuvwyzwtttux|{xy~|}}}zxxx{~~~}xxxz||z{zz}|xy|}~|uqsz}xz}~~~~|||~||}}~}~xw|wrsssrstwyyz|{tw~{}yxywutuw{}|y|{vvwwyyutttuy|zxz}}}~|zxxy|}~}yyyz|{yzz{}|xy|}~|tqs{}x{~~~~}||~||}}}~xw|xstsrrstwyz{|{tv~~{}zyywttuw{}|x||wwwwyxttttuz|{xz}}~~|yxxy}}~}zyyz|zy{zz~{xy||~{tqt||y|~~}~}||~~{|}}~}|xx}vstsqrsuxyz{}xtx}{~yyxutuvy|yyzwwwxyvttttw{|zy|}~{xxy{~|{zyy{{yz{z|~yxz||~}wrsx~zz~~}~~}|~~{|}}~}yx{ytttrrsuwyzz{ztv~~{~zzyvtuvy|~zx}{xxxxywttttv{|zy|}~{xxy{~||zzy{{xz{z{~yxz||~}wrsy~z{~~}~~~|~~{|}}~}~yx|xtutqrtuxyz{|ysw}|zzyutuwz}xyzxxxxyututtx||yy}}~yxyz||}{{yz|yy{{z}|xy{|}~ztru||z}}}~}}|{}~}~~{xz|uuurqstwxz{{}|tt||}{zzwtuvx{~zw|}yxxwywstutu{}{y{~|xxy{~{}{{y{{xy{zzzxz{{~~~wssw~{{~|~~}}~{{}}}}zx{zuutqqtuwyz{{~zsu~|~{zzvtuwy|~yw}|yyxwyvstutu{}{y{~|xxz{~{~|{yz{xy{zzzxy{{~~~xssw~{{~|}~}}~{{}}}}~zxz{uuurqsuwxz{{}|tt}|}|{{wtuvx{}{w{~zyywxxstutty}|yz~~~yxy{}|}||zy{yx{{y|}yyz{|~~{usuz~z}}|~~|~}z|~}~}}yy|xuvtprtvxyz{{~xsv~|~{{zvsuwy|~yw|}yyywxwstuttz~|yz}xxz{}{}||zz{xx{{y||xyz{|~~zusuz~z}}|~|~}z|~}~}}yy|yuvtpqtvxyz{{~ysu~|~|{{vsuwy{~yw|~zyywxwstuttz~|yz}xxz{}{}||zz{xx{zy}|xyzz|}ytsv{}{}}||~|z|~}~~~|yy|xvvsprtvxy{||vsx}}{{yttvxz}wx|yzxwxtsuusv|~{y|~zwy{|~}{~}|y{ywz{zzyxzz{~~zulb`a`ais}vqlmldacdcgowyspifffbcgfddhlnqxxuw{~}zzqaZ]``a``ejoyyuutqoib`abgdabc`_acccfijknia_gpruurqpkkhdeecchjgdglpsz~{xuplh_YYZ`b_ckoqw~{}zpjiiijnjgimmoqnlnqvwokjhddfhjmqtvy|voqvxyyvtvutsnosvw~~~wpputrpmihdba\\`cdiprnlmprv|}}}wux}|vuussuwxxz{}{||z{{vsqoosroqutokea`bdhb[YYWX_eimqrrswsjiouuuutvww{zxy{|}yw||vtuuu{}~zvsoknruw{}zwxwmgjrspjc`a_^]YY[[]grussvwuw{xwy}{wsmilqw{yxxuqrvy{|xtx~||ywxwux~{{~}{sgabgnlggfb_bhmptvwvwuljpxz{wrpqqsrpstojmpojkorsstqt{ywz|~{rs{ztpmpnklkfdhjjimprswupquvtuqjfhlsrljjf``bfinquwzuhdjtvwuqnnmqvx}}|~zwusvtpnje``dhnw|rnqvutrnkicefdfikntuqlmopry|vrtxxwslggiouuz~~~}upljorqry{xtv{~xqmllopoqrpmpqnjjlmlpuoihnrwzvrrnkljovyy{zvxyz|~~~|xy~zy}~zxyxuvx|}~~{vuopuuqrwxphffhinpoqrnknnkikmojfgjijnty{~wtx{|~}~}}wqrvxzzuuwz{z}yrqqmknsxyxvuwyrkmssnkiikied_[^bgp|}}{{{~{zvsvyzwvvtuy~~z{~{yuy~}zzurqsw~|xx~}z}}ujdfkmqnhhhgipvww{{xx{vopy{{}}y{{zxuppuvttwxurv{|}|y{{}~~}z}~{{ups~{ssuutponkgjqvusuxwwyvtuz|zxrmqw{~zwvoeaflot}~{tpsy{{yursvx}|{|z|{srv~vsof`biqxvqsxyzwrnmnpnjlqrpswxtruz}}wx}xnhimw~~||}|~~yrprytu}}~|tprsttutx~wuuz~{xwz{{}rs~{xvy{~{~}{|zxxyz}|{{{zwrooruuxy~~|xrrtwyxuy~yqkostw|uwzxx{{|}wuzzvxvu{soqvxz{wuxyxzyvrvz}{vvwvuvutpsy{z|{vrswy|ztvzxuojnvxvxzuqrw~{wrrw|}{z|~{|}wrt{{y}}~||{ww|{tposy{~{{|xwvqpx}rlllmnsuuz|}xtu}{yzz{~yttvxy~~}{z{xqrv|}|~|zwz{ussttuy~~|yz{{||zwy|yupos}yxy}~wvx}~{yz||{zsx{~|zwsrsqt{}x{|zy|{wx}~{ytsvy{|umfchkqz~}z{}|{|x{}xz}|~}}~~~|x}|{}}{z~trzzsmgdjs}vru{{yvqrxuspnrqmmu|ztx|~|zz|~vokov{touxxy|wtsxwqppmpx~xoijrzzuuzzqmmppotz}~xlis|vutuz}{z|{wz}~z|~zurv}~|{zsprsuz{uw~{xsszxwx{|xurooopw||rqw~zsoru|}}~yttux|~ywzyxuty~{|ywy|zxxtuz{xyz~~ytoqsrsv}}{ypotz{wupllijpvw{}zzz}xy}{{zyzyzxz{w}}~~vtvwz~~~zvrnrvvw}vtvwv|y{xpmonqonrwzyx{{yzyzz}|tsy~|vsx{wx{vw}zv|vsv}|}ytw{||vu|{zz~ywwzzxuw|~~|xtpmnquw}~|{zyyxz}|}|}|z~|z}ytrmin{}{{{|~{{{|~~{wy}{}~}~|zz|{yyy}xuyvrojipy|~xuyzzyw{~|xwxupmnt|}zvz|xrqv|{|}~~}~{}}ury}xv{vz~}xsnkmpx~~~~xokrwy{y|{uppsrpwyvru{}yxy||}~~~~{|~~~}|upu~{|zzz~uorzzzvsrsvy{tu|{vx{zwv|zuuwvps{xuzzssv{}{xrtx{{|z{vrx~~~}z{yxwx~~xxzyy}|xwy}yxtopty~}{pqz~|vtsqrqvwv|~zyy~|{wvw|zvx}wu{xux}~~}vrqw{~}xvyyxwz{|{x~wsrsxxx||~|}wuy~xvvzyzvu{~z}{uttvz{{{}|wopw{{|yy{xuxwy{x||{{xz~|~}vtttz~~~~}}{yqmvyvz|~wuuwz~yy}vou~ztv}|{yuvy{~{zyuu|}zz}~|yvtv{}}|~|}tqrwy~zvz~yvw}}}|{yxz}z{{{|yzz~zwx||wwyz|{~~xwwvw{y}{}~|voopt{xxyyz~{xwxxux~{|{|~}}}|~~}{{z}z||~vmnw~{|{trqs}}{~~rnv~{vuvuvzzvy~|{|}y{~|x}~~~|xx~wrty}~~|{vwz{|xv~~|xx}{vvx|}~{yxtx~~|}}xwx|xy~~z{zxxy{~~wx~snrz}~~y}}{{{z}zx|}}}|xqsx}yx~{tsy}|{uu||yxv}|zy|~~~zy}vmmqx~~{}~{z}zvv}|x{yposv|~~}|yz}zrw{vxxtx{wz~}|}}xppszxz~~{uuxyxxy||{~{sstw~||zww{~|xu|zvuuuz~{||~|yvwy|}z|wwy}}wtsu{~{}vw{{vw~|zwtuvz~|wutz~~yz}}|~}}|~}}}ww~yxz}vrttszwv|~~zy~}zzyx|zxvw}~||ss{{vwy{~z~}|usz}{ytqpqv~||}wv{|wx{xuz}z}~yy|}~|}}|||yyy{xwstwy}~~{tqov~~yy~zsuy{}~xuvwwz}{w|~z}{yz|}{|{{|z{|uqsx|}y}~|ysrzzuwy~wsvxzxvv{~|{|wt}}z|yvuwyy|~y{}torwy~}}|~yz||}|yy{vqtyy{~usw}~~~|}|xw~{xwy{z{xz}zu{|tnmqy}{|}|yw{}z{z{vposx~~{yyy}zw|}zyxtt}zy}}}~~~vons~z{||~xx}{wvuu{xrqs|||zvvyz{}rs}xwuvx~{|}}|}|}|ywuwzwy~}yutwz{~|~}}{{xvz}}{xtstuz|||xtx{|}}z{}z}}}|xx|yw||wy}yvurryxx}|x}{zy{}ywuu}yxysqx}{yxx{}}~|uv|~zy}~xvtqsy}zz|yv||xyywx}zwzzsu|}zy{}}~zw{}y}{{|xtuxy|~zuquz|ywy{}|{{wwyyywvw{}}yy~|vuy}{z{x|~ztqv}~}uopxzvuvz|vsv}}xux~~|z~~~}wz|xuqswz}{vspr{||z{~|yxy~zwv|~wtstx|vtw||zzzz|}}y{zvvw{~}~xw~{wsoqz|}}}|vtw{x}ytprz}zyxy~}|{~|{vuz}yw|~}zy||}yqov}zy{|{vsprw~zspw~~|wvxxv{{uw~~{yxz~}}}}~zx}vnoy~z|}{wxwuwz}~yqqw|zxtttux~zy~zxz}zy}~||}{y~|}xux|}yx{~{|~{{xrsy~~}{{|~}{||~~{{}|xvtx}yuyvruy|}}zxyzy|{z{|zxvy}zuz{ywvuy|}}{z~{{{yy|{yw{}wswvru|}zwx{}{y}~~{tqquz~~yvvyyy|zzyx}{uttuyxx{~~~wv{|wtvzz{xw{|~zuuwy|~||~unpw|~xvvzzuvzz|zx|~||{{}~~}~}xsrru{~}zvor{{xxz}{xvx{zvy{vqs{~wtx||zyvvz|~}~~{xuty}{}|{}|xuru|~}}~vrsx}zuxzusu}~|zyz||}~~|{~zx{}}{xw{|ywx{|}~xtuxzz||}}ztooqwztuy|~|xwxxvx|{{{{}|}|~~|||yy}~}qlqz~{{xusty}~unpy|yvutuw{|yz}~}{z~|{}~zz}yvv{{vvz~~}|yuuy{|{}}zyx{}yyz}~}~|yvuy~~~wvz~yuw{yyzzxyz|~~yw|yrrw}xw}|{zyy{|{|||~~}vtv{}yz~ytsx~zusx{ywy~~|{}vdVRTZcq~xwuspmjecb`abiqy|yyz{ytmfeijjkkgeejnsvsrw{~xtpkijig`\]dnv}|ric^[Z[_bbabdgd``ekllqsnifhpwwrjd_[ZZ]dihhjhdbbdgmrqpqv~~vledghe^Z\abaaacgnwywxwx{|{xsnnpnjijjkjiggfeipsuwwxyyvolmov|}||}wqorvy}}||{{{|yrmmljc\\affed`]^`dkuzz}}xwx~}{yww{}xwxwuw|{|}xy|~}}~}}}~{vtsv}}{}{upmjhkqwumc\Z\_befhjknqqomosuvvuuuvvroou}}zwx|uoosx~~|ytswyvrponihigea_blrpje`^`ekpw{zxtmfcdhmqpnllnsvwx~}{}|}~|{wvw{~wmeeilmnppngbbfijkkigfbbhrwy{xrnjgffjkkjkptsqswzyyxz{|{x}}xx|zz}~~xvzwronklqvwrmkkmnlllllklquwuspnoprronib^^entxz}~vnlnprx}~{uppssrtvz~}~~xusqponqx~}{vmihlqwzxwvrmhdbdilpqpmnnmntzywvxvplkkmmlmostsqonopu}{vtttyvstxy||{z{zwvtpnqtuspnmmmligglruttsplkoswurssqlfdipsrrqnllou|~~}~~~~|}{usvz~|zxusstttvupljmruwwxzwsponjiovywoiggefilnruy}|yz||}}||}{wsopu|~}~~~}{|~wqmotyz{{}}vtvvsrrrpnnnmhb`envxuojghks{{tnknrwxusrvyyyz~yy|}~~|{}{wtv}zpigjkpuusnigjmmlmnnnoonquz}xurpqpopqty{{xy{zxx|}|}{vx}xttzyw{~~~}|{|zwuqoptwvqoqtspnmlmprty}}}|ytrtvzzytmgejrw{~yusrv||vuwxurqqty~z|xz~~}|xrkinu|~{ytooty|~}zvrokjklnqvz|xutwz{zz}}zyvrmklu||zyvtstv{ysppty{|utvwwy{zvsqrrsxzvvvvwvuuutpnnqw|}~ww~~zvroqtwy}|yvvx{||~}ulilrvwvspmkjkntx}}xvuw{}|||~~ysonooopqqqrvxxvsv~}z}~wtrsvy~{vuuwyz{{yussv{~xuuuwvvx{}|{|{z{{|||yspsx}}ysonrtux|~{tpqv|~||}~|z|xpnry}~wsqsxyrmknqssqswyxx{~}}~~}~zvqpv~zwwxz}{xxz~~zzyuqqv|}xssuvutsrtuwy{zxz{xvtqnmotyzzzzyvstx||~{y{~zy}~z{|~zvtx}|}~|}}yvxyyxutwzzwsqoprw{zvuwz~~ysmjkosx}~{uru{~~zwwzyvspnqu}|~~~{wz}|skjpw{~}{wwz}}zvsponmnry}yyzyxz|}yqiinuyxwurqqtx~~}~|zwsrvz}}~~{xwxz|~|xuuvyyyyxuqpruxz|}voosw|zsooqrstttttuwz}~{|}yy}~}}~|{|~~zvvz{vrsz|yvvx{|zywvwxwx|zxyzxwwwwwuvxzwrlhhkpv~|{{}|xvvwwy~~}{xwy|}|}~{xy|~~}~}xusrtv{}vstwyyz{zunhgjorvzzxtstx~}xtstuwy{~~}}~~|y{~~|}}yxx|}yy}~zyz~{wvx{}zz}~ytonrwywrqsutrqpoqtx||{}~{wtrty}}xuvx{~|{|||~~|{{|~xwz~}|}{}~~~}}~~}~{{zyuqqtxzzxtpnnqw~~zww{~zvtsrsx~~zz|}{zz|}|xuqmnqx~}~~~|z|}yxy{xplmqux{}~~}~~|zwvrnmou}zwx|{rlmrvyzxvuuvy~~|||{|~}{xvvvvuvz|yvuw|{utux{}|yvttwyz{|yvssw~|wwwvtsstuwy}~z{ztty}~~~|vvy}~zuux{}~}{yvv{{yxz{{zz{}|yyz}}|zz|||}}zvrty~~{vpkikrz~}}|xtqqsw~~{wuy~|{~}{xxz{}}vsrqsy~~|yuqqtw|~}xwwxy}~}zz{}~~}~~xxy||~~|||{{z|}xx{|zx|{{wuwz|~|y{{~{{}}z{|}{yzyww{~|}yuuz}~~|||~|vqsw}~}|xuvwx~}}~{xtrx{|}~|~~~}|}|xvz}}zyyyvtx{|~xussw~}|xwz}|xyxspqw}|xxvxz~~~xuvyzz}~}|}~~}}|{||zx{~}{{{w{~zvw{~~~yvuposxz{~|{}}{{}}}}}|zzzxz|}||yxx}|~|}}zuonrx|~zxwvv|}{zwtqvxyz}}~|{yyvx{}|{{{yvxzz}~zxuvz}{yxwy{}z{|wtsw{~}{yyz|~}~|xsstwx{~}{z||}~}|||{{|{zwu{~~yuz~}}~}~~ywtorw{|~{|}}|{}~{z{|yy{||{yx}~|~}|xslmry||xuuuv|}~yvsoswy{}z}}||xy}}{{|{vvxwz~{ytsv}|{{yz}~{}yustwz~|{xxw|~{||{vstwz{|{{}|~}z{|zyyvw}~zx|}{}}}~yytqtwz{~}y{{{~~~~~|yxz~~|{|}yvw|}~~}|}~|xrmot{}~{xvwwx|~zwspvy{}~~z~~}{wz}~zyz{ytwxwz}zwrsw~|yxz~{z{vsqtx{}}{xvvx}|{~~|vuw{z|~~}~~~|yw{}}~{{|zw|}wxz|}~~{yvpqswxz{|||}}||~~~zxz}{||}zxw{|}~}||~{uoosz}~}zxxvw~}z}zvrwxz}}~~|}|yux{|zzz{zvyyy{}{wtty}zwwy}~{|{usswz}}{xww{}~~yuvwyx}}}~}~~|{wx||~}||xy~~~xvzz~~~|xwqpsuwy~}~|}}||{~}zz~|{zzz~~zy{~{|}}~|wompwz~~zwvuu|~~|}{vquwx{}|}|yvy|~{{|~{vyxx{~|wstz|zwwz~|~{vttwz}~|yww||~}|wtuxzy~||}{~}{x{}}}|{zvy}}}xx{{~}~|}zxwqrtwy{~~}{|~}}}||zz}~}{zz||yy}}z|~}|~~ztnnry{~|xvwuv}~||ztqvwy|~{~~zwz}}{||~zuxwwz~|wrsx~{xx{|~|vssvx{}}xvuy}{}~}xuux{z}|}|~{xx|{~}|{|ww|}{wzy~}~}|~}zytqsvxx}|}{{}|{~}yy|}{{{~zxz}||~~~|{{~zsopu{|}{ywwuy~||}{utwxz~~}~|~~zwz{|z{|}zvxvwz~|wssx~~{xxz~|}{vssvxz}}yvuy}|~~zvvx{z~~}~|~~zwwzz}~~}}yx|}~|wxx{~}~{yuqrsvvz~~}{}}|z{~{z{}{zz}yy|~{|~~{z}~xqoqwz|~}{xxvuz}{}{tuwwz~}~~~}xwz|{z{}zxyvw{}wtty}zvwz~|}zvtuwy{~~yvv{}}~yvvxzy~~}}{~}zwx{{~~}xx{{~{xxw|}}~}zytqrtvw||{~}{z|{z||zy||z{~{z{}{z|toosxy~|xwvsw|}}}}vtuvx|~}~zxz||{|~{xyvvy~xtsx}~zvxz}~{wtuwyz}zvuz|}~yvvyzy~}}|{~~zwy{{~~}|wxz{~|yyx}~||~|{ytstuvx}~z{}{z}}zz}~~|zz~{z|~}zz}~{y{~xqoqvy|~~zwwttz}}ytuuw{~~}~|yz{|{{}}xxvuw}ztsv{~|wxz~}~|wtuwxy|~yvty|}~ywwz{z~}}|{~zwxzz~~~|wxz{~|zxw}~|}|{xtstuux~}z{}zz||zz}~|yz}zz}}zz~}zy}~wqpswy~~}ywvtv{}~}xuuux|}~~{zz{{z|~|xxuux~~ytsw|{wx{~}}zvtuwyz}~yuuz}~zxxz{z~}~zpc]__aflrz~zwroljlnqtvvtsw}xvvxwmbdhifdfhjmopsx{|xkb[XY_fjls~wog_ZZ^a_[eorngd`^^aeilnppnkhighmspd_adca_^^^acefhmrx}}wt|~}wromnqpmhdaaeltyz}~zrlea^`bdfikjhffeeluwndbgjjjklnosvvwz}xsv~}}{yz|thcglkhkrvuqonoortsolhfiotyyyyroosw{~zqrvyyxyyyz}}~xvxz|wqnoqvy|{|}sqvyvrswxxxy|}zrf[[cgecgnrropqsvxzxuqkhjnrvuw}zx|}xstvy~~ytpjggilprqtz~}sjjopkfhijlnoswz}~yqf^`hieekptsqsrtwz}|zvqquy}ztw{zutvx~~xpkecdfgklmruvqhejookjmorvwy{|}~}wqf]]elkjpv{|z{{z|~zuqux{~~||~~~|~wrsruzxtnifdfijnppuxzvlfhmoljmoqtuuxz{}{vka_fnnkosx|z{}|}|vpqsuyyy|y|{vrrsuzxupjfcehhknntxzxpghlokgijmoqruy{{rgeippmosw{yz{yy}~{uvy|||{}}ww{{wsuux~|yrjebdffilnsuvslfhmqompqux{{|~|wmdekssruy~~~~ztwz|~~~{{y|}{{}~~}}~}wsssvywtnieeikloqty{zumikoroprswyyyz|~{vjdgousqtw}}}vqtuvwx{~}}}}z~~zutttxywrlhdfijloqw||{tlkmqpmooptuuwy|}wlhltvsrtw}}|~}}~wvz{}~~yxwy}~~|~xw}~zvvvx}}ztnhdehiloqw{zxqkkossruvw{}}}~}ysjgmvxvvy}}wvz{{{|}zxvx}~xy{|{y|}~|wusuyzxsnifhlnqtu|{snmpssrvww{||||~zulgmvxvtvz}vtuvvvv}{{yz~~~~{~~|~{{~ywuuyzxtpjffilorsy}vomosrqsusvxxzz}}zslnvyxuuw|~}xtpsz~}zwyz|}xvvx~xjbbhoswz}~zwtx{zvqkdbdilpru~ytrty}}|w{~{wxz~zqljfflsvxwtqonntz{yvx}|vmedegiikmnlfdgkq{|rheipvz}~~{{~zsllswxvtv{~~zxvv|{wussx{zvux~|wohhknonprrnihlrzyposz~~}~{tqquy|~zwy{{z}}|}~|}zvrnqwzzxx{~|{}uqrvwvwy{xsnot{tnouz~}vv{~zwuz}zwpkjkptxy|xsqqtvvy}~|}}||xuqrx}~|}{truyxxz||wsrv}zqosy}~ywz~~ytqsyzxupkjkotwy{xronpstw|~~}}|wtsw|~}}}}~~}}zwz~~||}}ysru|wsv{yvx|~{urrwyvsoifgjosvy}yspopstvz}}{}~|}}yvvx|~{{{||x{~|}~~ztsv||uqsx|{usuz|}ztrrxzwsnigikptwz~xrpoquvy~~}yvwz~~{{z{}xuy}}{|~|xttxzsqtz}}|xwx|}~ysprxzwrnkjmpuy{~~xqooruv{~}xvx{}~}{z~|zz{}~zz~|~}zuruzzuvy~yxwz}~~xsruyytnlhilotw{}wqpptvw|}}xwz}~|}yxyz~}z{~}}zusv{}vrtw{|uuux{}}wssvzzuolijmpux||vpprvxy~|ww{~~|}~ywxz}~ywx{}}|~|yusw||urtw|wwxz}~}xsrvzzvpnlnqtx|~~xqpquxy~}xvy~}|}|zzzxxz}~|z{}~}~|xtru{ytvy}xwxz|{vsvyzwolkloquy|{tqqtxx{~{wy}~~|zwwx|}yz|}~~~{vssxxsqux}ysuwz|~|xtuy{ztnlknqtx{}wqqsxz{}yx|}~~}}~wvvy~~}~xvyz{}|}|wtru|{uqswz~xvxz|~{xtvzzyrnmosuy||uqqtxz}|xw||}}{z||vvw{}|~yx||}~}~{vrrv}{usuy|yx{~{y|~~{tppsux|zvvz}~{{~|}|{z~}uqrsy~|zz}{{{xsuxxyzzzwqopv~}tpptx{zuvz}}z{~}xroquw{~}wvx}|{~||}{{|yrqqu{|xwy}}z{}ysqvvvxyzwsopt|wqoswz~yx{~{z}}zuqrwy|~yuv{~}{||{{yxy}zsqrtz~ywx|~zz}}wswywxyzxtoosz{trtx{~zxy~}|}}zuposwy}zwvz}~||~|}{z{zspqtz{yy~{z}~wtwyxxy{xtootzyqorvy|xvx}~~}}}yupotwz~~zwv{~~}}~|}{z|ysopsz~xvw|zz}{uruwvuxzwsopu}xposwz|zxz~}~|xuqqwz~|yww|~}|}|{{yw{}vqoqv||xvx~}yz{vuxyxvzyvrorxwqrvz|}}zx|~~~zuroqvy}{ywx|~~}~}}|yy~ztpoqw~~zxz~zzzvuxxwvyyuqosy~uopuy{|~~{xw{~~~zuroqvz}~zyxz}}~~~~}|{yyzspnqw~|xvx~~zzyttuwvtxxuqptz~vopuy||}}}{y{}~{usqrw|zyxy|}|}}~||zxw|{tqopv|}ywx|~{z}xvwxxuwxuqpsyzrqvz}}}~}{z{~~~|urppty}{yyz{~}{|~~~|zx{|uqpos{}zy|}z|zwwwxvvxwsqrw~|torvz|||}~{zy{~~~ysrpru{}yzz|}~{{|~}}{yy~yspoov}~zwy||{|wvuvvtuvtqqt{yrpsx{|{{~}|{{}~~~wsrruy~{yz{|~}zz|}|{ywx}}wrpoqx}zxy}|{}xxwwwuvvtqqv}ysrvz}~||}|||~~~|urqrtx}~zyz||~|zz}~~|zy{ztqpos{~{z|~{|{yxvwvvvusqty{uqrvz}|z}{{{{|~~~ysqqsuz|yz|~~}zyz~}}{yz}~wrpopv~~|yz}}||xxvuuuutsqrw~}xsqty|}zz~||}}~~~}|urrtvz|yy{~~{xx}}}|zxx{wrqppt|~}zy|~~|~{zxvvvvtsqrw}zvtuy}~|z}|z}}~}~}~}vqpstw}|zyz~~~{xw|~}}|zz}xrqpqt|}{}~}~|{yvvvvtrqrw}~xustx|}|y}yx{}~}~~}~vqptuw}{yy{{xw{}}}{yz}wqppqt{~~|{}~~|zzxutuusqqsy}wutux|}{y}yy}~}}}wrrvwzyxy|zww|~}{|{yxz}}tppqsv~~~}|}}~|||xvuwurpqu||wwww{~~{z~|wy~~}}ztpruwz}xxz~~yww}~}||{zy}|rpprtx~~~}}|}|yutwuqpqv}zuvvwz}}{z}zuw|~}|}zuprvxz|wxz~ywx}|{|{zz}|rpprux~~~~||~{|{xtsvuqoqw~zuvwwy|}{z|{vx}}|}|wrswy{}wwz~zvv{}|zzzxx{~toprux}~~~|{~|}}{wsvvrnpu}|wwyyz|~|{{}vv{}|||xrqtxz}~xvy}|wvy~~{z{zy|xpoqux{~{{}|}|yttvtpnsy|wuxyyz}}|{}wtv}}|{}~zvqrvz|{vw{~zwx{|zzzyz~}toorvy}~~|z|~{{}{wsturnou}zvvyzy{}||{~}vuy}||}zvstx|~yux}}yvx|~{yyxxy}{sopsx{}~|z||}}{wsuuqnpw~zwx{{z|}}}||vvz}{||ytrtx|~~xux~}xwy~~{yyyy|xroquz}~{z}}|}}zututpnszzwvx{{z|}}}|wtv{~{{}}zwssv{}{vv{~{wx{}zxxxz~|uporw{}~|yz}{||{xsttrnpw|wuwz|{{|}}|{vvy|{||yvuuz~}xvy~}yxz~~zwwwx|}wrorw{~~}zy}}}}|zuttsoou~xvwz}}{|}~}~~wvx}{{{yvutw}}xvw~~}zxz}~{xwwy}}xsoqw|wmaZZ_mx{nhdehhc^^bjry||y~~~}yustrnikpolggigfhms{}|}}xusrnkie`^_aiu~||unid`^^^]\ZYYYXZakv{ztkddegjjhffggfhnpoolg`ZZ]dlpmjjnuz}|}~}vqld_^`dhkkkkheeefmw|yuqlhfdeks{}yqhabehkkhgilllqwyzywrlijnt{}yutw{~~{yy{{z}zz|~}}|yqjeda\Z\bgmpqqpmjjjjpy~~wy|~}{}yuuy~{y|~{yxyxw{}yxwy}y{}{vsqqpoopprrpnoqtvvwwwuohddb_]`gmsvwutqnmmmqy}}|~|ywyywy~}ytrqrw|{yzyvrrutpljjjiijilnmklnqtvwxxwsmggfcabipvz{yvtpoooqw~~}ywwxvwz~zwrpoorw{||yuuutpnqrojhhiiijijmnmmoruwyzzzxsljjhediqw|~|xvspqqqu}~{wvxwvw{}zwspoopsx{|{wuuuspoqqmihikkkkjmpqppruxz}}|{xrlllifhov||xurqrrty~}zwwxxwy}|xuqonopty||zwuttrppqpljijlllkkmqrqrtwz|~~}zsmmljgjrx~|wusrstu{~~~~~{xuvxwwy}~zvspnnopty{{yvttsqppqpljjlnnnmmoststwy|~zsoonkjov}{wusstux~~~~}|zwuwxwwz~|wtqoopqrw{||zwuutrqqrolklopponorvvvwz|}xroomklryxusstuwz~~~}|{xvwxyyz~yurpoprrv{}}|zwuutsqrqnlloqqpnnqvwwxy|~{tponkkpwytsstvwz}~}||zyvvxzzz}{urppqssuz}}|{xuuttrrrpnmpstrpoqvyyyz|~}wqpomlqw{tsstvxz}~~}|{zyvwx{{{~ztqpprtuv{~~}ywvuusrrqonquvsqprw{{{|}}wronmmqwzsqrtvy{~|~}|zzywxy|}}ytqpqtvvx|~zwvwvsrrqopsvvsqprx||{|}|vqnmmnrywqqrtz}zwuutv|}z}~wuutstuuwyxyz|~}yxy{|{|~yxwxwtsutsuy}~{{{~xtqprwz|{unilpu{}{{~~wpllmnomnnmjhmu|{vpmlmrvwvuttwyzz|}ztpqtvwxyz{}|{zzz|}~}wpkiikou{~}xqlmquz~yxxwwutsrolov}ztomlmquwwxwwz}{wx{}~~~~}{zyzz|}|y{~~~~~|yz|xtoihhkou{|unmquz~~|zxyzwwvx}||~|zxwxyz|~xtuw{|}}zvuwwwy{}zwttx}~~}zwsmjjmqv|zrnptx}}}|{{{~~~{xvwxyz|~ysqsv{}~~{yzyursttvx{{xusuy}zxsmlnquz~wpnqvz~}}}}}zwvwxyz}{uporvz|}}~}yxywsqrttvx{zwtsv{}{xsnlosw|}vporw|~~~~|yvuvxyz}~ysoorw{}}|~{xxxvrprtuwz|zwtux}}{xrnnrvz~{tppty~~~}}~~{wuuvxy|{vpnpty|~~}~zxyxuqqtvwy||zwuw{|{wqnpux}xroqv{~~~}~}}~~~}xuuuxy{|wrnnrx{~~}~|yyxvrqsvxy|}|yvwz}{xsnosw|yspptz~}~}}~}~~~~yuttwy{~|wsnnqw{~~}}zyywsqruxz|}|zwwz~~|yupotx|ztposz~|}~~||||}~~~yuttwy{~|wsonqw|~~{zywsqrvy{}~}{xy{~|yuppty}~ytposy|{{}~}||}{|}~~~yutuxz}{vroosy}~~{zxvsqswz|~~}{yz|}zxsoquz~|wsppu{~|{{}~|{}{{|~~}wusux{~xtpopuz~~}{yxurqsx|}~~}{yz}|zwsprw|~zvrpqv|~{zz|}|{}~zz|~~~}xusux|xtpopu{~|zxusrsx|~~}|{|~|zwtqsw|~zurpqv|}zyy{|{{}}yy{}~~~~yvtvy}{wtstx~}zxww|}zwttx}{wtrqtz~{wutvwwvx|zttvy{{{z~|{~~~zvtux|}wtssv|}zxwwz~{xvuw{|xusqsw~}yvtuwwwwz}vstwz{{z{~{|~~}xutvy}zussty~~{yxwx||zwuvy~}yvtrrtz~~{wutuvwwx}ztsuxz|{z}}z}~~|xuuw{xtrsvz~{xwwx}~{ywvvz{xutrsu{~~{wutuvwwy~yssvy{|{z}|z}~~~{wtuw{}wsrsw{}zxwwy~}zxwwx|}zwusstw~~~~zwttvvwx||urtwz||{{~z{~~~}yvtvx}ztrsuy~~{xwwx||ywwwy~{wutssuz~}~|xuttuvwz~xsrux{||{}{z}~~~~{wuuw||vrrtx||yxwwz~|ywwxy}{wutstux~}~}yvttuvwz~ytrtx{||{||z|~~}}{xuuwz~wrrtw{}ywwwy}}yxxyz}{wutttux~~}}}zvtttvwzysrswz|}{|{y{}}}{yuuwz~vrrtx||ywwxz}|ywxzz}zvtttuvy~~|}|zvtttvx{}xsrtw{}}|}{z|}||{xvvw{|uqruy}|xwwxz~~{xwyz|}xtsttvwz}|}|yvtttvy}{urrux|}}}}}zz}~||{zwvvx}ztrsvz~{xwxy{~}zxxz|~|wtstuwx|~}|||yvtstvy~ztrrux|}}}~|zz}~|{zzxvvy}ztrsw{~{xwxz{~~|zxxz}{vsrsuwy|~|{{{zvtstvzzurrtx{}~~~}zz}|{zzxwwy|{ursw{~{xwwy{}}|zxwz||wsrsuwy|~~~}|{{{wutsuy~{usrtwz}~~}{z|}{yyywwx{|vsrvz}}ywwy{|~|{xwy|}xtrrtwy{}~}|{{{yvtstw|}wsrsux|~~~{zz}~|yyyxxxz}zustx|~{xwxz|~~}{yxx{~{vrqsuxz}~}}}{zzzxussux~zurrsvy|~}{z{}{yyyxxx{}xtsuy}}zxwyz|~}|zxwx{~~yurqsvy{}~}~}{zzzxutsuzytrrtwz}~}z{|}zxxxxxy|{wtsvz~}ywwy{}~}{zxwy||xsqrtwz}~~||{zzywustw|}vsrsux{}~{z{~|yxxxyyz}~yuttx|~{yxxz|~~~{zxwx{~zurqruy|~~|}}{yyzxvtsuzytrrtvz}|z{}}zxxxyyz}zwutw{|zxxz|}}~~{zxwwz}~zvrpqux{~~}||zyzywutuy{usstvy|~}{{}~zxwxyzz|{wuuw{~~|zxxz{}}}|yxwxz}~zvrpqtx{~}||zyyywutuy{urrtvy|~|z{}~zwwwyz{}~zwvux{~~|zyxz|}}}~~{ywwxz~~zvrpqtx||||yxyxwutuyztrstvy||{{~}ywwwz{|~}ywvvy|~~~}|zyyz|}||~}zxwwy{}xtqpruy~~|}~{xxxxwutv|xsrsuwz}{{||xwvxz|}{wwwxz~~}}|{zyz{}}||~|ywvwy|{wsqprvz~|}~{xxxxwuuw}wsrsuwz}~{{}|xvvxz|}{wwxy{~~}||{zzz{}}{|}|yvvwz}{vsqprvz}|}~{xxwxwvux}xsrsuwz||{}|ywvwz}~{wwxy{~~}||{zzz{}}{{|}yvuvy||wsqpruy~~|}|yxwxxwvw|ztrsuwy{|{|}ywuvy|~|xwxy{}}{{{{zz{|}|{{~~zwuux{ytqpqtx}||}zxwwxxwwz}wsstvxz}~||~{xvuw{}}ywwy{}~|{{zz{{|}|{z||xvuvy}|vrqpruz}|}|yxwwxwwx|{ussuwy{~}|}~zxvvx||xwxz|~}{zzz{{{|}|{z|{wutwz~{urpqrv{}|~ynfcejpuxz|{}wpg`ZXY]bhpw|xtssuwwwvutsqpnljfcbchpw}zpga`aelsy~|qh_[XXY[]`dgkmmnpqqokjknrtsttspmjgc_\Z[^acdeffdcaadinsx}~{zyz{zyvrpppolhdbbfkry|zzyxxy{{{zxxwurnkjloqrrsrqomlifdcdhlprtuutromorvz}~yvuwz~}ztlfb``adgijjjjlnpomkiimqw}|ywttwz~~{xuuw{~}zyyz{|}}|zxxy}~|zvrmheegjoqqnic]ZYZ\`ehklmmprtusqnnrvz~~{yy{~}yvttuvwwwusrqrux{|}~}{ywtrokfb``cgknnlgb][Z\_chlnpqrtwyywtrruy}~~|z{}}{wtrrstuutrponpsvyz{{zxvtqolhda_adimoolgb^]^`chlorstuwz{zxustw{~}||}|{||zvsrrstuusqonnpswyz{{zxvsqolhda`bfkopolhc`_`bfknruvwxz||{yvtvy}}|{{{}||}~|zvsrrsuuutqonnptwy{{|{ywtqoliebadhmpqpmida`bdhmptvxyz{}~|zwvwz~}|zz{||{}~|zwsrrstuvtqonnpsvyz{|{ywuromjfccdhmqsrolgdcdfinquxy{{}~~|ywx{~}|zzz{~|{|~~}zxurqrtuvuspooprvy{|}|{ywtqolifdehmqttrnjfddfimqtxz{|}~}{xxz~~}{zyz||z{|}|yvtrrtuwwurpoprux{|}~}{yvsqnkhfegkptutqmifefimqtwz|}~}zyz|}|zyyz}~zyz}~|{xusrstvwwtrppqsvy{}~~}{yvsqnkhfgimruvtqmjgghkoruy{}}{z{~}|{zz{~|yxz}~|zwusrsuwxwtrqprtwz}~~}|yvsqnkighkptvvtpmjhhjmqtw{}}{{}~}{zzz|}yxy{~}{yvtssuvxxvtrqrtvy|~}{xurpmjhhjnsvwvsoljijmptwz}~|{|xrnklot{}{{{||zwrmigintz~~~|yvttuwz|~|yvtrpppsw}~{wtstx|~~ztolklmnprtvx{~|ywwxz}~{ywusqnmkklnpsvxwuqnlnrw||xvtuvxz{|}}~~}|zxvvx{~{yyyyy{||~|ywvwz}~|zxwutrqpprtwz}~}zvrqruy~~}~}zwutux{}~|xtpmllmpsuwy{}~~|{yx{~~|zywvwx{}}xtstx|~}{yyy{}}{zyz|~{xtqonnpswyywsolkjknqtvy{}}|z{}~}|}~}|zz{|}xutvz~}{zxwwwy{||zxvvwy|~~{xtqnllmoswxxuromllmpsvy{~~|{|~~}|{{|}}||}~}xuuw{~|{ywvvwxz{{ywuuvy{}}zwspnllmptwyxvromlmnquwz}}|}}|{zz{|~||}~yvuwz~|zywvvvxy{zxwuuvxz}~{xtqnllmptwyywtpnmnorux{}~|}}|{zzz{~~}|}zvuvy~}{ywvvvwyzzywuuvwz|~|yvromlmpswyyxurpooprux{}}}~}{zzz{}~~~}||~|xuux{}{yxwvvwxz{zxvuuwy{}~{xtqnmmorvyzzwtqpoprtwz}~}~~}|zzyz{~~}~}|||~zvuvy}|{ywvvvwyzzyxvuvwz|~}zwtpnmnptxz{zwtqppqsvy{~~}~}|zyyz|}}~~}{{|~}yutvy}~|zxwvvwxy{zywvvwx{}}zvspnnorvy{{yvsqpqrtwz}~~}|{zyz{}~|}~~}|{{}zvtux{}{ywvvwxyz{zxwvvxz|~~|xuronoqtx{{zxurqqrtvy|~~~}{zyyz|~||}~}{{{|~{wutwz~}{zxwvwxyz{zywvvxz|~}yvspnoqtx{|{yvsqqrtvy|~~~}{zyyz{|||~~|{zz|}|xutvz~~|zxwwwxy{{{yxwwxz|}zvspooqtx{|{yvsrqrtwz|~~|{zyyz{~{{|~}|{zz|~{wttvz~}|zyyz{}~~}{z{|~~{xustvz}}zwvuvx{~~|{yxwwxz}}ywwxz|~~{yxwwxy{~|xustw{}{zyyz|~~|{{{}}zvtstw{|yvuvwy|}{zyxwwx{|xwwy{}}zxwwwxz|~zwtsux|~|{yyz{}~}|{{|~|yvtsux|~{xvuvxz}}{zxwwwy|{xwwy{}|zxwwwxz}}zvtsuy}~|zyyz{}~~}{{{|~|xussuy}~{xvuvx{}}{zxwwxy|{xwxy{}|zxwwwxz}}zvtsux|~|zyyy{}~~}{z{|~|yvtsux|~{xvuvxz}}{zywwwy||xwwy{}}zxwwwxz|~zwtsux{~|{yyy{|~}|{z{}}zvtstw{|ywuvwy|~|zyxwwxz~}ywwxz|~~|yxwvwy{}|xustvz~}|zyyz|}~|{z{|~~{xussuy}}zxvuvx{~}{zxwwwy||xwwy{}}{ywwwxz|~~{wtstw{~|{yyyz|~~|{z{}~zwtstvz~}zwvuwy{~}{yxwwxz}{xwxy{~|zxwvwxz|}zvssux|~|zyyy{}~}{zz{}|yvtstw{~{xvuvwz}~|zyxwwy{}ywwxz}~{ywwvwy{~~{wtstwz~}{zyyz|~~|{z{|~zwussvy}}zwvuvx{~}{yxwwxz}zxwxy|~|zxwvwy{}|yustvy}}{zyyz{}~|{zz|~~{xussux|}zxvuvx{~}{zxwwxz}{xwxy{~|zxwvwxz}}yussuy}}{zyyz{}~}{zz|~~{xussux|}zxvuvx{~}{zxwwxz}{xwxy{~|zxwvwx{}|xussvy}}{zyyz|}~|{zz|~~{wussuy}}zwvuvx{~}{yxwwxz~~zxwxz|~~{yxwvwy{~~{wtstw{~}{yyyz|~~|zz{}}yvtstvz~|ywuvwy|~|zywwxy||ywwy{}}zxwvvxz|~}yvssux|~|zyyy{}}{zz{}|xusstw{~{xvuvxz}~|zxwwxz}|yxxy{}}zxwvwxz|}yussuy|}|zyyz{}}{zz{}~{xussux|~{xvuvxz}~|zxwwxz}|yxxy{}}zxwvwxz|}yvssux|~|zyyy{}}{zz{}|xusstw{~~{xvuvwz}~|zywwxy||yxxy{}}{ywvvxz|~}zvsstw{~|zyyy{|~}|zzz|~|yvtstvz}|ywvvwy|}{yxwxy{~zxxxz|~~|zxwvwy{}~{xtssvy}}{zyyz|}~|{zz{}~{wussux|}{xvuvxz}~|zywwxz}|yxxy{}}{ywwvxy|~}zvtstw{~|{yyyz|~~|zzz|~}zwtssvy}}zxvvvx{~~|zxwwxz~|yxxy{~}zxwvwxz|~}yussux|~|zyyy{}}{zz{||xvtstw{~~|ywvvwy|}{yxwxy|~zxxy{}~{yxwvwy{}~{wtstvz~~|{yyyz|~~|zyz{}}zwtssuy|}zxvvvx{~~|zxwwy{~{yxxz|~|zxwwwxz}|xussuy}}{zyyz|}~}{zz{}~zwussux|}{xvvvxz}~|zyxwxz}|yxxz{~}zxwwwxz||xussux|}{zyyz{}}{zz{}~{wussux|~{xvvvxz}~|zyxwxz~|yxxz|~|zxwwwxz}~|xussuy}~}{yyyz|~~|{zz{}}zwtssux|}{xvvvx{~~|zxxxy{{yxyz|~|yxwwwy{}~zwtstvz~~|zyyy{|~~|zyz{~|yvtstvz}~|ywvvwy|}{yxxxz|}zxxy{}}{ywwwxz|~|yusstx|}{zyyz{}}{zyz|~~{xusstw{~~{ywvvxz}|{yxxxz}|yxxy{~}zywwwxz|~|xussux|}{zyyz{}}{zyz|~zwussux{~~{ywvvxz}ujdbdhov~yrmgc``acgmt}~zusplifccccdfhkkjihkpsx}~xrkc^ZXZ^dmvztniea^\ZZ[^bgmquwvtojhggiijmnpqpqpomjfdccfimoqqponmmmnpruy~ztnhebacdedca_^_aekqx}yvtsuvy{}~|xrmhfeefhkmoprsuutrpoooruy}}{zxvuuuvxz}}|~|zxxyyzzzzxvsplhc_[YY[^beghgfffgjnsy~}|}~~}zywwwxz|~}zvtrtvz}}zxvsqppqrssrpmjhhhjkmnooonljgda^\[\_chlnnnmllmpsx|~{yxwwxy{|}}{xtqnmnqtxz||{zxvtqomkiiijkllkigeddegjlmooonmkifca``bfjnqsrqpooqsw{}{ywvwxyz{|zxtpmkkmpsvxyyxvtrpmkiggggijkkjgfddfgjlopqrqqomjheccdgkosuutsrqqsvy}~{ywvvwxz{{zxtpljjknqtvxxwvtrpnkihgghikllkigffghknprsttsqomjhfefhlptvwvtsrrsvy}|zxwvwxyz{zxuqmjjknqtvxxwvtrpnljhgghjkmmljhgghjloqstuutsqoligggjmruwxwutrstvy}|yxwwwxz{{zxtpmjjlnruwxxxvusqnljihhikmnnmkjhhiknqsuvwwvurpmjihikosvxxwvtsstvy}~{yxwwxyz{{zwtplkkmpsvxyyxwusqomkjijkmooonljijkmpsuwwxwvurpmjihjmquxyxwusssuw{~}{yxwxyz{||zwsolkmorvxzzzywusqomkjjkmopqpnlkjkmortvxxxxwuromjiiknrvxyxvtsstux{}zyxwxy{|||zwsomlmpswy{{{yxvtronlkkmnpqqpomlklnpsuwyyyywuspmkijloswyyxvtsstvx|}zyxxxy{|}|zwspmmnptwz{|{zywusqommmmoqrsrpnmlmoqsvxyzzzxvtqnkjjloswyzywutstux{~}{yxxxy{|}}{xuqnmnptwz|||{zxvtrpnmmnoqsssqonmmoqsvxz{{zyxuromkklorx~zvtrrtvz~|zxvuuw{~~~~~|ytpmkklotwy{{{{zywvuuuvxz~zvrmjhhjntz}yussuwz|}~|{ywvtrqqrtvz~}xspnmnprtvxz|}~~}|{z{}~{xwwwyyz{{zyxvtqomlmosx}~{xvtrqqqsvz}yurqrsuxz}~~~}|||~}zxxy|~{ywvuvwxyywurpooprtwyz{{{zxvtrpoprvz~~}{{{|~{yxy{}~{yxwwxy{}~~{xtrpqsvz}~|zxusqpppqsuuutrpnnprtwy{}}~}|zwurqprtx}~}}~}||}}zxvuuuvxz{{{yvrommorvy{}}}|zxvtrpoooqrtvvusqppqsux{}~~|yvtrqrtx|}|{|~~~}zwvtttuvxzzzxuqnllnqtxz|}}|zxvtrqpopqsuvwvtrqqrtvy|~~|zwtrqrtw{~|{z{}~~|ywutstuvxzzywtqnllnqux{|}}|zxvtrqppqrtvwxwusrrsuwz}}zwtrqrtx|}|zz{|~{yvutttuwyzzywspmlmosvy|}}}|zxvtrqpqrsuwxxwusrstvy|~~|yvsrqsuy}~|zyy{}~~zxvtttuvxyzzxvromlnqtx{}~~}|zxvtsqqrsuwxyxwusssuxz}~{xusqrsvz}}{zyyz}~~}zxvtttuwxz{zyvrommoruy{}~~}|zxvtsrrrtuxyyywutstvx{}~{xusqrtwz~}{yyyz}~~}zxvutuvwyz{{yvspnmoruy|~~~~|{ywusrrrtvxyzywvtstvx{}~{xvsrrsvz}}{yyyz|~~~~{ywuuuvwyz{{zwtpnnoqux{~~}{zxvtsrstvxyzzxvuttuxz}|yvtrrsuy|~|zyyy{~~}~}zwvuuuvxz{|{yvronnptw{}~}{ywutsstuwyz{yxvttuwy|~~{xusrrtw{~}{yxyz|~}}~~{ywvuuvwy{||zxuqonoruy|~~|zxvutsstvxz{{ywvuuvxz}}zwusrsux|~|zyyy{}~~~}}~|zyxwxy{}~~{xusstw{}{yxxxz|}}{yxyz|~~{xusstvz}~}zxwwxy|~}{zyz{~|zyz|~~{yxxxyz|~~|yvtssvy}~|zyxxy{}~~|zyxy{}|yvtssux{~~{yxwwxz}~|zyyz}}zyz{}|zxxxxz{}}zwtssux|~|zyxxyz|~~|zyxyz}}zvtssuw{~~|zxwwxz}~|{zzz|~{zz{}|zyxxxy{}}{wtssux{~|zyxxxz|~~|zyxyz|}zwtsstw{~~|zxwwxz}~|{zzz}~{zz{}|zxxxxz{}}zwtssux|~|zyxxyz|~~|zyxyz}|yvtssux{~~|yxwwxz}~|zzz{}}zyz{}~{yxxxyz|~~|yvtssvy}}{zxxxy{}}{yxxy{~~{xusstvy|}{ywwwy{~}{zyz|~|zyz|~}{yxxxy{}~~{xusstw{~~|{yxxxz|~}{yxxz|~}zwtsstwz}~|zxwwxz|}{zzz|~{zz{}}zyxxxy{}}{wussux{~|zyxxxz|~~|zyxxz|~}zwtsstwz}~|zxwwxz|}{zzz|~{zz{}}zyxxxy{}~}{xusstw{~~|zyxxxz|~}{yxxz|~}zwtsstwz}~}zxwwxy|}{zzz||zz{|~}{yxxxy{}~~{xusstwz~}{yxxxy{}}{yxxy{}~{xusssvy|~}{yxwwy{~~|{zz{~}{zz|~~|zxxxyz|~}zwtssux|~|zxxxyz|~~|zxxxz|}yvtssuw{}~|zxwwxz|}{zz{||zz{|}{yxxxy{}~~|yusstvz}}{yxxxy{}~{yxxyz}|yvsssux{~~|yxwwxz}~|{zz{}~{zz{}}zyxxxz{}}{xusstw{~~|zyxxxz|~}{yxxy{~~{wusstvz}}{ywwxy{~~|{zz|~}{z{|~~{yxxxyz|~~|yvtssvy}}{yxwxy{}~~|zxxxz||yvtssux{~~|zxwwxz}~|{zz{}~{zz{}|zxxxyz|~}zwtssux|}{zxxxyz|~~|zxxxz|~}yvtssuw{}~|zxwwxz|}{zz{}~{zz{}|zyxxyz|}}zwtssux{~|zxxxxz|~~|zyxxz|~}zvtssuw{}~|zxwwxz|}{zz{}~{zz{}|zyxxyz|~}zwtssux|}{yxwxyz|~~|zxxxz|~|yvtssux{~~|zxwwxz}~|{zz{~~{zz|~~{yxxxy{|~~|yvtssvy}}{yxwxy{}}{yxxyz}~{xusstvy|~}{yxwxy{~~|{z{||zz{}}{yxxxz{}}{xusstwz~~|zxwwxz|~~}{yxxy{}}zwtsstwz}~}{yxwxz|}{zz{}|zz{}}zyxxyz|}}zwusstw{~~|zxwwxz|~~|zyxxy{~}zwtsstwz}~|zywwxz|}|zz{}|zz{}}zyxxyz|}}{xusstw{~~|zxwwxz|~~}zyxxy{}}zwtsstwz}}{yxwxy|~~|{z{}|{z{}}{yxxxz{}~~{xusstwz}~|zxwwxy{}}{yxxxz|~{xusstvy|~}{yxwxy{}~|{z{|~}{z{|~~|zxxxyz|~~|zvtssux|}{yxwwyz|~~|zxxxy{~|yvtssuw{}~|zywwxz|}|{z{}|{z{}}{yxxxz{}~~{xusstvz}~|zxwwxy{}}{yxwxz|~|xvsssux{~~|zxwxyz}}{{z{~|zz|}|zyxxyz|}}{xusstw{~~{zxwwxz|~}{yxwyz}~zwusstvz}}{yxwxy{~~|{z{|}{z{|~{yxxxy{}~~|yvtssvy||zyxwxy{}~~|yxwxy|~|yvtssux{~~|zxwxxz}}{z{|~~|z{|~|zxxxyz|~}zwtssux{}{yxwwxz|~~|zxwxy{}|yvtssuw{}~}zyxwxz|}|{{{}|{{|}|zyxxyz|~}zwtssux{~}{yxwwxz|~~|zxwxx~wi]WSSYbnw}{zywwwwuphn}~{vsqonooplc^kzwjcbdjqv}~yurpnmmlidcp|tmigefgilllkihhiknqsttojlvwmfa`acddddccbabbcegjnrw{zsljr~{ywtrolkjijklnoomlhecgs~}zwtompw~vnifffghiijjjjjlnpsvz~wqmnv~zwvy~~wpljiiiihhgfedccccdfhjlmnnlifeekt~}}||||}~xroqw}|{{{}}yvsokgdcflrvuqkfb```abcccccddeeghjmoqrsrpmjhhlt{uqrv~|yxvvvwy|~~|zwurokhd`^]_elpqnjeba`abcdeffgghijklnprtuutroljjmt~|vsrv}~{ywuutuwyz|}}|{ywurpmjfb_\[^cjprplhecccdefghijjklmnoprtuvvuspmkjms|~xtsv|~|ywvutuvwyz{||{ywuspmjfc_\\^cjpsrolhfeefghijklmmnopqrsuvvwvtrolkmr{~yutv{|zxvuuuuvxy{{|{zxvtqnjgc`]\^cjptusoliggghijlmnoppqrsttuvvvvtromlmqy{vuv{}{yxvuuuvwxz{{{zywurolhea^]^cjqvwvroljiijklmnpqrrsttuuvvvvvtrpmlmqx~{wuvz~|zyxwvvvwxzz{{{zxvtqmjfb`^`djqvxxurolkkklmnopqrstuuvvvvvvvtrpnlmpv|xvvz}{zyxwwwxyz{{|{zywtrnkgda_`djqvyywtqommmmnopqrstuuvvvwwvvutrpnlmpv}|xvvy}{zyxxxyyz{||{{yxusplhebaadjqvz{yvsqonnnopqrstuuvvvwwwvvusrpnmmpu|{xvvy~~|{zyyyzz{{|||{zxvspmifcbbejpvz{{xvsrpppqqrsttuvvvwwwwvvutrpnmmpu{{xvvx}}|{zzzz{|||||{yxvspmjgdccfjpvz||zxvtsrrrssttuuvvwwwwwvvutrpnmnpt{}yvuux|~{xurpooqu{~}}}}}~~~~~~}}}|{zywusqpoquz}{zyyyzz{{|||}||||{zyxwvwz~~zwuttw|}{xurpooquz~~~~~~~~~~~}}}|{zywusqppquz~|{zzzzz{{||||||||{zyxwvwy}}zwuttv{}zxurpooqty~~~~~~~~}}}|{{zxwusqppqty}{zzzzz{{||||||||{zyxwvwy|}zwuttvz}{xuspooptx~~~~~~~~~}}|{zyxvusqppqtx~~|{{zz{{{||||||{{zzxwvvvx|}zwuttvz}{xusqoopsx}~~~~~~~}}|{zyxvusqppqsw}}|{{{{{{||||{{{zzyxwvuvx{~|zxvttvy~}{xvsqoopsw|~~~~~}}|{zyxvtsqpopsw|~}|{{{{{{||{{{zzyxwvuuuwz~|zxvuuvy}~{yvtrpopsw|~~~~}|{zywvtrqpoprv{~}|{{{{{{{{{zzyxxwvutuwy~~}|zxvuuvy}~{ywtrpppsv{~~~}||zyxvtrqpopruz~}||{{{{{{{zyyxwvuttuvy}~~}{zxvuuvx|}{ywtrppprvz~~~}|{zywvtrqpopruy~}|||{{{{{{zyyxwvutttvx|~~}|{zxvuuvx|}{ywurqppruz~~~~}|{zywvtrqpopquy~~}||||{{{zzyyxwvutttvx|~~}|{zxvuuvx{}{ywtrqppruy~~~}|{zywvtrqpopqtx}~}}|||{{{zzyxxwvutttux{~~}|{yxvuuuw{~}{yvtrqpprux}~~}|{zywvtrqpppqtx}~}||||{{zzyyxwvuuttuwz~~}|{ywvutuwz~~|zxvtrqppqtx|~}}|{zxwutrqpppqtx|~}}|||{{zzyyxwvvuttuwz~~~}|{ywvutuwy}~|zxvtrqppqtw{~}}|{zxwutrqppqw{rmjjmsy}~}vqnkjklnmgao~|yvvtqpnmj`SRbpkdgmu~yrmifffeb]]l}xtpmkkkkkkkkjkkllkid\VXfrqkhghkmnmkihgfffghjkmnopqrtuvvurleeq|zywvtrqpnmlkjjihfb_]am}~zvpjc\Y]fputqnmnoooonnnnooqstuwxz{|}}}|yupkhjr}{tnlou}|yxvtspnmkjihhhhijjkkkkkjhfca__dnz~xtsw{wrmkls{|xtnhb\[^dlqsrpnmlkjihggfgghijlmnopppponkhebbemx~}~ytomns{~}||||||||||{zywuqmhb\XVX_fmppomlkjiihhiiijklnoqrstttsrpnjgdcfmwzuqnosz~}|{zzyyyyyyyxxwvtrokfa[WVX^fmpqponmllkkkkklmnoprstuvvvutrolhedfmv{vroosy}|{zyyxxxxxxxwwvusqnkfa\YWY_fmqssqponnnmmmmnopqrsuvvwxyyxvsplighmu}}{xuqnlknt}}{wspnnry}ytpmkmry}zwsokijnt|~}{xvromlot|}{xtqopsy|yuqnmnrx~~|zwspmkknt{~qf\XUUX]ekv{vsqruxytnv|vpmkjkou|~}|{{yxqfbo~|rnorx|{wutuusnmxzsmga^\\aglruwxxxxyxuqibcnz{skebba`_\ZXVTSQRTY_gntxz{|}}~}{vqpy}zuplheefhmquwwwwvvvurnmpz}wpjfhox}zuojfdca`__^]\\]_djqx~~yust|~~}|{{}}{~|wsolheb_]ZXWXZ_ekptvwwwvusqoos{~{zxvtsqpoopsw}|xuv|~|zxwutrqoooqty~~zuokikqx}~{vplheb`^][ZYXXY]agnswz{||{ywtrqtz}|zyxy{~zww{|yvtrqomljiijmquz}~}zvrniecekrxzxtokheca`^]]\\\^aekqvz}~~}{yvsrtz~}~{xw{|yvsqomkjhgghjnrvy{|{ywtplgcbdiqwzyvrmkhfdcba`__`adhmrw{~~|zwtsty{xx{}yvsqomkjhgghjlptwyzzywtplhdcdipwz{xtpmjhfedccbbcdfjnsx|~zwuvy}{yxwuttstuwz~~zwvx|~|zxwvutsstux||yvvy}{yxwuutstuwz~~{xvx||zyxvuttttvx|}ywwz~|zxwvutttuwz}~{xwx|}{yxwvuttuvx{}zxwz~|zyxvuuttuwz}~{ywx|~|zywvuutuvx{}zxxz~}{yxwvuuuuwz}|yxx|~|zyxwvuuuvx{uh_\]dozzrkgdcdgkmih{}{|~|xtponlllkjd[^sysrtv{~~xqlhffghgdh{ztqqtvxxwusqomlkjjjkjgd]Y`p|zrlheca^[WUVY^djnqrqpponnnoopqpmjgkz{xwwx{~}zvspnljjiihgecdkz}ytogb]]coy~{uojgda^\[]aflrvz{{{{{zzyxwvspliinz}|{|~ytqry|vqlhda`beimpqqponmllkjjigeccfnz}xuw}~zwtssuy~}zvrnkkpy{xvtstw|}{xtpkgb_afoy~~{vqlgc`]\\^afknqrssrrrqqponljgfhnx}yuqmmpx|xurpooquy|~|zxvspmid`\Z[`irxzyupkgda_^^`dhmqtvwwvvvuutsqnlihinw{vronpw|xtqonnoruy{|}|zywusqnkgc_[YY^gpwzyvrnjgdbaacfjosvxyyyxxwwvtrpmkijnu|xspnpv~}yuronnoqtwy{{{zxvuspnkgd`\ZZ_fovz{xtpmigeddfhlptwyzzzzzyzyxwtrollou~|zxwwy||yvromnry~{ywwxz}~{xtqnmou}|zxwxy{~|yvronnry~|zxxyz}~{xuqonpu}}{yxxy|~|zwspnorx|zyxy{}}{xuroopu|~{zyyz|~|zwsqoorx}zyyy{}}{xurpoqu|~|zyyz|~|zwtqpprx}{zyz{}{nc][_gr~|vnkhgiilnlmv|yvvx|{vsooppsvy{uhhzumlnpsuuvwz~vplklptxwst~wqmkmqtwwwvsqomlkkmosuvslfjxvke`\ZWTQNMOT[cinqrrqponnoqtw{}}yuv~}~zvrppruy}}yvspnllmnprtsqqv~|yupjhmv}}vnf`\YVTSTW\cjqvz{||{{{{{{|}}|yvuy{xvtssty~}}{slgb^[YZ\agkoqqqponmmmmnoppoprxzuqnllmqv|}{ywwz{vromllosy}|zxvspmjkpv}|wpjd_[WUUVY^dinqrssrrrqqrrssrrqsx~{yxy{}zxwzzupmjiijnrw{~}|zxvtsqnlifdeipvzyuoid_[YWWX[`flptuvvvvvuuuuuutsrsw~}}~~}~~{xxzzupmjhgikotwz||{zywusrpnligdbcgnuyyvrlgc_][[\_chmruwxyyyxxxwwwvusrsw}}{{|~{xwy~{vqmjihiknrvxzzzyxvusqpnligdccgmtyzxtojfc`^^_aejnsvxzzzzzyyxxwvutssv|}{{{}~{xwx}}xsoljijknruxyzzyxwutrpnljgecdgmsy{zwrmifcaaacgkptwyz{{{{zzyyxwutrsv{~|{|}~{xwx|zuqnlkklnqtwyyyyxwutrpomjheddgmty||yuqmigeddfilqtxz{||||{{zyxwvtssuz~|{{}~{xww{{wspmllmortwxyzyyxvusqomkifeehmsy}}{xsoligfghjmqtwz{|||||{zyxwutssuy}||}}zxvwz~yuronnnpruwyzzzyxwutrpnljgffhmsy}~}zvrnkihhiknquxz{|}}||{zyxwutrrtx~~}}}}zxvwy~{wtqpopqsuwyzzzyxwvtsqomjhggimsy}|xuqnlkjkmoruxz{|}}}|{{zxwutsrtw}~}~|zwvvy}}yvsqqqrsuwyzzzzyxwusrpmkihgimsx}~zwsqnmlmnpsuxz{|}}||{{yxwutrrsw|~~|ywvvx|{xusrrstvxyz{{zzywvtsqnljhhjmrx}|yusponnoqsvxz{|}}}|{{zxwutrrsv{~{yvuuw{}{zyyz{}}{ywuttux}}{zyz{|~~|{yvuttv{~|zzz{|}}{ywuttux}}|zzz{}~~|zxvuttvz}{zz{|}}{ywuttux|~|{z{{}~~|zxvuttvz}|{z{|}}{ywuttux|}|{{|}~~|zxvtttvy~~|{{{|}~}{ywutstw|}|{{|}~}|zxvtttvy~}|{||~~}{ywutstw{~||{|}~~|zxvustuy~}|||}~~}{ywutstw{~}|||}~}|zxvtstux}~}||}~~|{ywutstw{~}|}}~}|zxvtssux}~}||}~~|{ywutstvz~}}}}~~}|zxvtssux|~}}}~~|{ywutstvz~}}~~~}{zxvtssux|~}}}~~|{ywutstvy~~}}~~~}{yxvtssuw|~}~~~|zywutstvy~~~}~~~}{zxvtsstw{~~~~}|zywutstvy}~~~~~}{zxvtsstw{~~~~}|zxwutstuy}~~~~}{yxvtsstwz~~~~}|zxwutssux}~~~|{yxvtsstvz~~}|zxwutssux|~|{ywvtsstvz~~}|zxwutssux|}|{ywvtsstvz~~}|zxwutssuw{~|{ywvtsstvy~~}{zxwutssuw{~}|{ywvtsstvy}~}{zxvutsstw{~}|{ywvtsstvy}~}{zxvutsstwz~}|zywvtsstux|~}{zxvutsstwz~~}|zywvtsssux|~~|{zxvutsstvz~~}|zywvtsssux|~}|{zxvutsstvz~~}|zywvtsssux{~}|{yxvutsstvy}~}|zywvtsssuw{~}|{yxvutsstvy}~~}|zywvtsssuw{~}|{yxvutsstvy}xneaacjr|zvrpopqrnjl~}{{{~|yxwvvvwvnbar{pfegjoqtvzytqpqrtqlm}~yurqrrtvwxyxxvutssstutrnf_bpxpe_YWUTSRQRUY^chmqtuvwwvvwxyz{{ytno|~|||}~}ywuuuvwz|~~}{ywvtsssssrolkp|~yslebfowwqib\YWUUVX\`ekpuz}~zvqps|{wvttuvy~}yx|~voiea_]]_adhknprstttttsssrqomkmr||}{vspoopruy}{vsrt{{wsponopsw{|xtojffjpvywrlfa\ZXXXZ]aeimpsuvwwxxxxwvtronnrz}{{{}}xusuz{vrolkklnqtx{}~|zxuqmhc``cjpttpje`][ZYZ]`chlpsvxyz{{{{{zxvtqoory~}~~yutuz}wsolkjkmoqtwz|}}}}|zywurnjfb__bioturnhda^]]^`bfimquwz{|}}}}|{zxurporx~}}~zvtuy~xtpmkkklnpsvxz{||{{yxvtqnjfb`_bhotvtqlhdb``abehkosvy{|}~~~~}|zxuspprw~}|}~zvtux~{vromllmnpsuwyz{{{zyxvtrokgca`chotwvsokgedccegjmptwy{}~~~}|zxvsqprw~}}}~~zvtux}}xtqonmnoqsuwxz{{{zyxvtrokhdbadiouxyvrokhgffgiknqtwz|}~~}{yvsqprv}~}}~~zwttw|~zvrponopqsuwxyz{{zyxwurolifccdiouyzxuqnkihhijmoruwz|}~~~}{yvsqpqu|~~~~zwttw{|xurqppqrtuwyz{{{{zywuspmjgdceiouy{zwtpnlkjklnpsuxz|}~~}|{xvsqpqu{~}zwttv{~zwtsrrrstvwyz{{{{zywvsqnkhedfjpvz||zvspnmlmmoqsuxz{}~~~}|{yvtrpqtz}zwttvz|xvtssstuvxyz{{{{zyxvtqoliffgjpuz}}|yvsqonnoprtvxz{}~~~~~}|zxvtrqqty}zwttvy~{xvuttuvwxyz{{{{{zxwuromjgfgjouz}~}{xusqpppqstvxz{}}~~~~}|zxvtrqqtx~~|xutsuy~}|{{{|}~}zxusrsv{}|{{||~~|ywtsrtx}}||||}~}zxusrsvz~}|||}~~|ywtsrtx}}||||}~}zxusrsvz}|||}~~|yvtsrtx|~}|||}~}zxusrsvz~}||}~~{yvtsrtw|}}|}}~}zxusrsuy~}}}}~}{yvtsrtw|~}}}~~~|zxusrsuy~}}}}~}{yvtsrtw{~~}}~~|zxusrsuy~~}}~~}{yvtsrtw{~~~~~|zwusrsuy}~~~~}{yvtsrtv{~~~~~|zwusrsux}~~~~~}{yvtsrsvz~~~~|zxusrsux}~~~}{xvtsrsvz~~~}|zwutsstx|~|zxvtsrsvz}|ywutsstx|~|zxvtsrsvy~}{ywutsstw|~|zxvtsrsuy~~}{ywutsstw{~|zxvtsrsuy}~}{ywutsstw{}|zxvtsrsuy}~}{ywutsstw{}|zxvtsrsux}~}{ywutsstvz~}|zxvtsrsux|~|{ywutsstvz~}{zxvtsrsux|~|{ywutsstvz~~}|zxvtsrsux|~}|{ywutsstvz~~}{zxvtsrsux{~}|zywutsstvy~~}{zxvtsrstw{~}|zywutsstvy}~|{yxvtsrstw{~}|zywutsssuy}~}|{yxvtsrstw{~}|zywutsssuy}~}|{yxvtsrstwz~}|zxwutsrsux|~}|{yxvtsrstwz~~}{zxwutsrsux|~}|{yxvtssstvz~~~}{zxwutsrsux|ʹѰcpblvbZrXlciyqWby{zhgqowweslc?ggtrybN{^Xdfn]_k^hgGSs]VUb_ovVOYyoyv/=jNzmvzp_Vs|ciyoeeriowu]hzdYevo~ic^::luMIJMfeqLTYuūz~z~gDi|r`jPTJNTSw{ke]K]oVe`uql }[amueT5*BXvh_KOQLOjXl\moXqbmsa:SZ{k`^b[nu}eb{d^fnst]t¼zpzfd`{NPYANi}ph`jQ^oiFU|qoi][LiWaum}`rkN]z}}mtuco{Zmwd{mmtylrU`]]z}c=U\\tpc{h\krvx|[hO_J>ONKOTZ?Y\Rupyxi{g{mzwvojgaWga|sqtu~wf_gd\zpx_f^aPEA:Q7QCHaM\alv}q{ss}y}guffoSYvu}d|ipuowloco{gTjRb[?FTGKHS[LlXYh]{rpzhyrv|lq\USa{n}vvws~|gz]kvuh^i_iPL@TI=P3LMQd^idwz|{wswfpt~|xeyeQcmi{|s}ohuxxmhxpSh`^pSO_TKIAQAW]NlWn{sjxycw~zzzt\XZqZusnz}xk~wqqprak^hUKFXWCX7@BD\Xd_e|rw{zigl|~{~qycoj^hbvys{zqu[pa^pUVdVVTM\M[RHcLaquixyzso~}w~w`ZglSn]]ipx}isz~hhlep\SSjTR\GLGidb}ottnlknsuodrqq]^uan}s}y~wy~q}ryiad]YjfeJSY@MZOP__cqki{vwvyru{zfzjXR^h_dkcirjrzzycctgYq\QL\\RTRBIHEdYZwr~y~tplgkyxsfpsmXZp]eztr|x|ow}y}jehe^npjQU^ADPCFUY_ooh|}||{xytlr{m{zcX`i^agg`ng~m}{n{tZmiWQ^cVTSF:A:W[Pnw|v}wkxieyz}zro}udUk^Sfhexy{}y}ukkh^dpiVQdMEQFDQQT_kaqvzzzzp~oz{qzj_fn`_``R_]umxvx`rp_Wfe[YYQ@D=UTE`ktsk{nzki}yy}}|rp~ubZn\Sd`ctstwt|x~tuqqemwlX]fKEL=GMMP]jcuzi~{zzm~uh~stogwmebd]QZ\xrf{uuk|obdsbcZ]I@?FYFHbhrblr{yt|xmx~rxyt}ts`mkPRUOegoo{~n~~wpfwqa[o_MUJITJKH[Uaobh~{w{{lukiuqz{p~xmkdbIMNgl]s}r}yvu~mjxhjdi[KKM^J@TX`R]gprwy}utyr{gipv~tguv\XWOb\c_owswlsxgevdRVJQXPSL^U\bS]vuqw~xl|mknk~}ywrrnTTSf\Oeowhnwy~tz~lrfoZPOZcLN[]XFWVd^j|xuz}}{{{}gjnwxrw_]SV`U_Wnpryhiz|yjp}j^`Ud]XUO^RXSDUjkj~~|}|skhfe{s~~vwqYXYiZNajp^glnui}{u}t{nzh^^fjQOZ\SARKZV^rnk~~yhhj}{m~|wljccmX^O^eem`]u}y}z{yt``Xhh^cUhb`YCF[``qxnx}wxnpj~rttuzlgfphMS\bWVhasfs}us|{xw`afseT_ccKQPJRDX__jv~|}orylgw{zih`rjcbUidhdT^qtsxzwjhdwgi`Yh`bTCNZYYmfmxv}}tlinnzox}igkxjV]abPYa\kYgnkry}kjoyfX_a]IXRQUDUWWf|wlqs}}mp{ojv}|s}roi{ojiVe`a\OZnro|xsxpiexjnlbrjh[IO[ZWe^bsmxv|vqpryqk~~yypr{t]]`^NP`Vk_bjglvqtvnp{r`fkiTW\N[EHMLVp{qsvxuwufmsugzxqjuwhvblphaONbnowwi~tw~}tukr_poj_LP_a[bZTke~zsqpqvynxn}~srzxeeklZT`PbXWb_dwzuvvbchhW]`RdPOQLRfmfimxw{tirxvgzxx{|wykvanrlbNO`ijtoexlzvvwgyxtdQTaebfTWaXsrs}zxqznl~|zmhysbeigWc^TdRX^_gvujlo}vjnpj`q]\^JPLKWfc^gqlflqhk{k{wzwr{qj{wr_QXbdhn^pmg{rv~|~kryvo_\ehei_QbSblit~z|wzzl|rel{nknpcjnXcQJPQ\pwkhl~u|tppk`tkhoVVUTZc]QRZvvy}yrsslp~ivqfot|xiwvse[bheddSgheyoq{}pu}}~tdepslm`YgQ\`Xaq}uv{q}zyogoy}vvpmjaspes[RPOXhkb_`suqu~sp~isr\YSWdl`UT\latzpvvwsfvyomgjlx}}|ufclrmh\QdW`pfgpztotyx{thlx}yt]akYhd\_gidhqwt|||lhs}}|xj|~vk{qiv`XQMVde_`drhf|ls~~y{wpy|kjc_fneYTXcScoi}~w}{u}zkqeccl||xifpxxtfRbWWkhjnpgcionmqow~nfo[el`]`bYZgqr{{wwjn{{hrzo}wy{sy{i|tec][`bXTXgl\olk}|rrtngghlqnfdcj[RdV_tzzux{yyv{vvqpljrzxt{|zjiszzwedgO[a]dmma^gpoxrzy{lvt^nojig]POV`f{zzsuz~{kyqi}}|z}kykca`flg\Y[ie^p`^pwwno}wolmu{rb]cm]XfU[jstpv{z}jhkov|ytv~{sqtxxvfksZ`e`bgi`[Z^cur|lv{gokeileWRVY\st||xt||xn|{iz~~~|xsvwgffjpqdWV_^Tlkajnrpowpkjoz~ulfmg[iZP\adfo}zyzpjlv|qnr{}|qr{|wsigwebj`]`b]]bdbivyyx|pr}ks|rjji^UPQR_rm|sv{jwmwyvz~qi`^fqvpjddaQ`bZfkgdiwzruzyrnmobWneY_a_Z`sw|sokq~|wwsqsqyxfevfitogb`VPT\^gtiy~z{w{~hryurld]\]ZVdj\txz}w|q{vp{z|uou||~u~vngiprkfglZQeYWfmj``p~}}~usy|e`hY]b^WT[k}}zyyz{zwuvzwhy|syx}|jrl`qpllg[RRTYatnauvrtvvuzzztbTUYX\gZcrmuzzju}|ytmlpz}~nnqrstonotm[bXMW\a_`fo{xxsszolu^\hh_ST`jr}z{ut{vylsufp{~z|tvdrvtrqfTKLMPgiduliqw~||xyzpe][]goX]fZ[fvxy~}vgcegnyss|zlgkujaq^RZ[WSZgoosrwxzmv{ebad`ZZ`fihf~|~zt}zqqhrttuyzt{pvpptrh`ZSPVcX_pc[\jzynjg`Xeo_jgZZ[fz}|z{pjhik||{{wsytrxngsc[[VSLP^lqmaiw{sx~push]\aggecZiwqzpxilmlfk|x{y~ustma]\UP_WTheba_iyzw{umormmo_ho\SSZes}}xqnlgcqvj~||ywpbuqcih\NLS\fmj`nnm~xqkuvm^Zfooi[[mbm|xvuxyqidgsry{tngfecafcOZ]RV]djqw{wunjjlyu_mpfa[]fjhoz}nottpxphwstxy~{veqgcigcWNU^`cacufe}~z}xtwvsidcjqpb_j\bqyjqyxmcgqx~|}~uc\^a^goZ]aVSVanskhruutrmx}kqgZ^_bfjjin{yngko}yhxwmpyyu{|qyl_imh^[\\_a[Wnneoqyx|rwungioqkjgcqd]oqs|ttwvnjltxxt|yy}vha_ahrcbhSNOS_ourns|y~xpmrxkv|i]\emofadlut}{yrkzpwrfggs{l{xbbcda\aig_[Tbecvxrvzuw}{uidlvxpa^sjajilov}~{|y|tlkpwyvvnoxnkh_Y_pdgqdXQV]cfimqsk}}w|}irujd\\jtqigoqez{utwwrxts{likqy{n{sqriaZ^ejmjYTeYYmqsrqtx|}|~_NMG`t~~xoab{ázuvSRoUot^c]`MQVgT=Dct[FbwaTNebZgf~žƼrv]SwKrePKMANjlyt|mrTYx[La|`k}Zcw^UY\alu`>JheV^oumy}zsrhZfoA_~pklusw{|_dmtk]T[wo`N^ltrlgj|kSbsx|x{zdJ=Ef{}gh}h`dURb^J_||{nbk~j^lnblipyhZ\lic{mSIYozkUSFBOdosu}uhopxqqssso[U^jkfhllo~yhxyt~uruyx}ws{~z|jSTMS_emtkWNKKAMc]_ittleemwǸ}{{v]YXM[\UbfghegqwxwwwvvodYSUUYf[ZYOLIFEHMON]qghldis®o`^WLFGNQ^iconhffoz~bNGKUZXTKEKB>OLOWSQU[cfirs½~nZ\]GDJNJL]jpvvjr|yww|tdUJ[WJVUJFHCBBFZZS[VZbhpz¸udVSVXbTO`ZWakr|zvz{}~yjehni_WX^`QRV??GFFIRZ]`kksuõu||f]^`YQNT]`bev|s}~xthuq[^\\^[TLC<>AMQQeaUXdp{}|~xqlcWYh_V[SPQYl{~{vwx}~qkmsl[SU]SU]OJIJE@IYcc_]o}s{w|zlkbXVVY\[]]Wlwr~}}~x}pwljk]WY\VNONEFSHI[WURTfx~}~tmmja^c[egVRTYbqxv{w{z|~zvuusndbYR`VMTNE@BHT_d]WilstuxqaRWaeb\T]gas~}z{{wu{xkvwlb\VROMNMQKHUQRWY[`kptxzttsut\]g\UOQ\dhqz~x~{|zy}rmprcT`\VZYUF?GORYZ[fZ^txy{|wxxqf^_hidUZbXdsz}yst|usywzuvti[SVYVWaPGPGCGQ\c`amw~rw~{vs}ooha`]ZZ^\[fwztx~}}wxu}yyshgVV][UPMKORPKVeZ[dltxuory}vrroiii`e^S_beryxwyxy|~}wyzzzl[TTV[cYYYKHGIT_`YWcrmrzs}x~v`Z]bec^]`hpn||v{{{}y}xuz}}ts~i^_`^USXXRQKKZU[c\_jqnknv||tw}}ti`jm\^bbciqonswu~~}v}ytqka\[hcXbYIBEKS[^``ccfwojt|~}|qe_flg]X]d\h|txyu}upqt}swynib]XX[^b^RGRTL\eda`efhijqyx}scjibhb]`bafoty~{xzxv}sjkkb]e_`i^YRJMSRPW_fZ\pmpuvxytrniejlifde[`jhsy}|xz{{tu{}~}~zmsthZTYadc`RSTJSX]bd^[cmrr}}~~~zpsh^mnhca_Y\hqu{zu{ssmlnnobWc_WTUUTUTSXed^f_drwurx~sqrspf`cgj_[ibdt|{z~{zzxx}~z~rkmnk]W[aecZ]aNQZWZbd^WYemxzypwxecfhigaZ\`fq~~}}t~ztmgftraf`[\Z[`[PNQXYZkf]fqvts{~tolqxvkcfohag]]dktvzwz~zuuy}zunhfebaac]`fXWVTUW[]_`_ewx}qxxotlehkf[VY]au~~~|}v|zx{|{{tpvmmn\TTY_dd^VV\SUc^`bbhmu}}wutvtoiefakl]`efelywryyzzvz~{rgdlqjc\[ia]f_WWWRPW\]^nry}||~osxqideca_]^hok}~x~{{zvwzyyx}tkwtg]Y]de_]\ZRP\WYdf`[ds~{}~zvwxtaclaa`_^]ew||}z{yq|~}~tkkqvsf\fb_fd`ZUQMOV]ckaexurrxxz}{ysg\]__[ahanz{}}y~yxtos}~swmjha`dhfefiYQYNNW]^YYds{|v|~wu{zmnhaihbYX_htxs{pq}|xwwvvwqkm`dmigf^OIJLO\i`cjjorsz~yyztg_affke\hfdnv~}|qkjls}unmppkeadhja``QRVVWX\`dlusxw}tzr`bgga[]djntxx}w}rosw{xw|{wps|kdihiigaWQQQVa]bh\[dmv|~{xqkc^_kpchicchu||qmnnoz}rkioxxmgipkclaTSTROT_ghfgn~xvvvtojedbbdfhjgs~z}mnpnlqy~znuyrtojjlh`\YROY[Veic_^hu{xw|}upppkellgnd\]`iy||ztokilvsz~zvqpsuslhkehpecaYSOPYdjkbevsy}zyznqqk`\ckmjhckvvs}wvrojgn{vyulvwnhggdbcc]^\T\][^_aiv{}yojjlkphbqogbafmsz~uruvsovqkwxustwwurtwuialgbed]RPV\bhgfojq{tnrrmc]blpogfrkpz{rtxtlfhmu~y{rqvvvpg_]`a`ehX[aZ[_ejllrz~wqomltuffgcdcdjprv~qknrrxskurpuwy|zrlnvsglfbiie_XV[^``cqlfryvopmkhgffjokjqimw|tqwxsjhlqsww|vwvpptqe_`deknba]TST]iqomsuoljrsmtkacgjlljlrzvqpvunsjfgoy~{vtvpqtc_dfd_`b`^_Y^lhnrqw}z~yvqieinnigfprflqqw|xuqqpoppqrnn}x}vrqoib^]bojgocXVW[cijjktw}|tvwkpsh_[alrolnuywyyxtnwzu|tkihmw}yxxyqkwtkiedabejhbZYc^bnnlmuyz|~ypjinssmagqjoqnpsvz}~ulmswvrnjsuu}{xsmjmmhemjgnf_WSX_bdjosjn~zz~~~ruvomgcelljilojw}|~~vr}ulffjpuz|~yqumnqkc^bimmk_[bZ^gjmmiglsvw{rqstvskmhdookjkkks}}sns{|xnltlp}~}zphhlmmofkrjfd_\][Y]fmfjonw|{xyy|vkjklljimorlepnn}}||w}yvrmmonlrzyy}pnrtncafmpnggfX[`_ciicairw~{||{xtpwqdkopokfbcis~|ywwz}wrvlkqsx|{wromnt{mhlfdcdgf`\]agchmaeouurvxrnosskcfnpglmgmu}xnoqstronq{|wxppnnmkijloliqh_b__`cc`__ep~{{|rszpnmgjnmfbdhlz}{zxu|zmqsprxytqqnkrwrvpffgikle\Z__[hjeghmorv|yroptwsnjmmjqhbhkns|wpqvyumkmrovxwxsmhlrqnlhksiii_\]]\^cfdjxv{yy}vwxouwoiijgcabeny{{|{qy|surnmnprtvvuxtlvthb`elomjfd`Yaa_fhedju}wuuroooohcpnhjjjhm{zvqpv{xtrtojvutwwskhmtvrhepljpmg`]YVX_cgols||z}qnsrplfcdhjhktou}{xzrw|vqomghnrsx}qrvqoidfjkihki[^b[`gjgcgt~}yxtpqusghhejkiefmx|{zywtrtuvpirnmwyvqnnprvrjnhfnmkid[WX[`grnisuwy{wsvrqqne]_dgkrlq{z}rrxwurmhfhot{uvsihjkkkjjmpped`WZ^acdhlt}~tpnqpkqiahlkfdjrxzww||tsx}tnriglrtqruvsrorvhhnnmmjaXUVX`liosmqw|{wroonieacgqsioplpz{z{vvxwogdefl{zxxojjosnfeipkioc[]][[ainosv~yxvlprgdceffhlptvy|||wsvwsyqillkmquyzwpmwsmniiklhda\Z\d`dnidemy}wtoigfc_fokqqjkmt|~wsutpljijrst}wqmjlrvsnloljphc`[XUXajnnio~|{qttlnle_aglnnomyvr|tmljheit{||uqvpsyqlkkgdcb]\a[`hffeelvz{}{tmkmmkmheoldbdjs~|vpoomikqkq|xvtsrrpqrqpglrjjjdYTV\bhkfjony}z|ulookc^ckonjgrru}v{uuvpjeekt{|wx{ppvusnjffhihigZ]_Y[_cgkpv{}tollmtoelligefknqywpqttsulkpmoqsvyvrsx{okogikid\XZ^`cbhnelvz|}}{}vtsplgeeinnhmmhqywoturjgimpvwwytwtuwtjccegipiad]YY]dihejt}z{}wuqtxonkaadfiklns~zsooryxnrpjkpuyzvrptvrvogjlkfca`__^]hiejkovzyx{{xtojjmmjiijqkhprv~~wutsollnoonuwxzuqrspkgghmsjjh[UUW^fjjinwv|z~xsuwoutibafkmihkry|}|zvryzruogeelv{zyyyuowtjihgeegigb]Zbdcmmkmsvuvy}|tmilppmfdomjnmnqw}{{volorsqokjvvx~|xuqmkliefollrjbZX[_adgknls}}{|qnrkgb_clonmpurx|xxywvxrw{qkggjpuvwyzqrupqoidbejllg]_b\cikllmpv{~smlnpqnfijhqqooqqs{|~~xojouvtnjqmnyzxxvpjkopmojgoida][^``dkrnktuxwtvnjigfegiknrmq{z|{{|rrupmihjnnrx}~vwtjmqngacimnlefb[aegkmkgjry}uqrqpnkphbknnnnmmr||}|zuqpsvwqnrklsvyzxrlijlqujjojhfeda][]cjinqls{}||ukgfhlkgflrrkqrow|zzz|tpponnlijpyyw}uppookgfilnjjnc^a_`dhhghmu~}yuqkprijijmolggkpy}xwxxuutsxpjomnswvsrpnowsnofcdehkha_aebenhgkqvw{yqljlnlfdhmjppjosw}|zoklqtsnmnssu}wsspljlonlljkqjhga__`_acefo||yqsslpmgeijhghko{{y}zsr{uqsomnpqqqrqququyohegjljfb`a\agdhihhlt{zsqrrqpnmifolfijkmu{uprvupmnpkpzvwxuojkqusnhmqjmlf`]\Y[aefkony|wx{{|vpusokfdcdeegqrw{wztvwrnmljmsvvy{qquplgeinnmllg\_`[`debahs{~~}}zwqopsmekjhkjgegp||zxvvwwwvumlqmrvvsomosvvompknqnkf^XVW[_glfmuuvwyvwurpnhabfiinnjswzyszzvroieglru}xuxrppnlkkjkmohbd\[_ab`aemuzy|}xuvwonkbeiiebfmt}~~~{truyvnrojnsurqrstttrvpinnmljd\YY[^hiejhhnrw}~ytrqkd``dhpkjrprx}yxuvurlhhjmv}wxypkjnrrnkmprkki\Z[[[]bfilrt|}{wwuosrgefgfdejosx}|}~xvyysvohjkmoqtwxvroxvnpnmmmic^ZXZbcdmkfhmt{~~~wspmjhfdenmjojhkq}zutupkhggjtux~xsolnssmjjollqic`\ZX[cilmls~|{rprkigcabfkmorqy~|zxwqvxomljghnuyzxqsuptrlijjgffc^`b]dheddis|{unhfggdhihqpkjlqy}~zurpoonlkqnnxvrpooqrssqqkjpjggb[WX]dimkkuv|wtulkljc^ahnppnr{{~~ssuopnjfdhpwzytwvntvsokhedeedeb]cdbeghlqw||sljjkkohdkjhghlsy~|wnklnmnqkmsrtsstspnpttkkoijkhbZY\`dhhnqlu}}ulkkieaaekonlrts~~~~yzrosrmgehlrwww|uqsqrrnhdfiiknd_a[Z]bhmnpxypljinpikifghjloqt}~}ysooqswpkojhlptwwtrsxtpqhfiigc`__`aafnjlqu{~xtqmhffgghkjppkrvz}zqpqpljjlnopt|xvyurrsnheegjpkih_[Z\agihjqz{zurtrloja_agkmmot||xuroqytrtmihlrwwuttuquxnkkjheefea_\agdikjmswxz~xrliknmiffomjooqu||xsonopponkpyuxzvsqpoomkjnqkmkaZWX]begimooy|x}z{vquqjeachjiikrsx}}}ywytuvmhfglswxyzzrsvqpmifehkljd\_b^eiihiknqux|~uooqrqkfjiimkjlorw{tnosutqlmrov{zxvrmlnommplpqkf`\[]]^chjfmtsx{||wwslifdehjklonlux|}}}}xuzvqmjjlmptx{wuxrqtrlffjmnmgcd\]bbeffdgnux}|wvutrmmldjmlkkjimu~ysqtwwtnqokquwwwtonorsvqjolhfdba_^^bhhfkhjrvwwzwpmkkifdfjokkros}}ywqonlmnmmpw|xyyqopqojhjmoojkj__a`adebadkrz~}zwsorpghhhjkiginu~~{yxwvvuruqlopquwuqonmowvprmjihhhd^\]bcdligkptuw|{rmjknmifhnmkpkkqv|wqooqpnjjmstw|vtspnmmmmmmklpiec^]_abcfhks}|}xqpslmlggijhfhkpy}yy{{xtquwoonllorrrssqtvqtqhedgjljebbc_ehehiknszvpmmljhghhhpoknpru|}{tolnrspnnqnpxvtusnkkprqnjjplklfa_^\]aegktv|wttlmnjfeefhjlnt{|{{|~}zrtvqtsnllkknqrsvupuvpkgfikihgfb]cdbhjihks|wrqommmnhekjilllms}}zvrpqqpoppkmtw~r_KBG`xq`cϪuodKobTPRdaVmny~nf^haTVggZ\V[|hMslSLNWtM^uCUx|jYuky£{rfZ]qqLvxNYqzvj]f|||\fuu\ZxWZhmg[ThnWP\f`QOlzO>f||{yi[qĩð~m\][SPbwMKpyfh|wqlm}{cXYXYczmMTseSVj}dNVnyeOEDHO]vootzuztYTcx~n\QNS^r}xg`ixzfW]nxkcbgp|nda`gu~gKAJ_opto`VSYhxxv{{cMGRfmpska\]hx}x{xcSQ^mpusg\SSZbfggf`TTZbpvrjdfls{~xpj_W]co|~{ursw}ukggknnlhd\]chu}o^NCFKLUWRNLPX`dhlrtw}qcVNQRTYUQPRYdnty}|{naWUYYafa^ZWXZ\[YXXTZadnttroquz~~~yrjbfhku{{xx|zrnmkihgf``ims{~~zskbXQNFGKHHIJNT[_chmnx»ukc[XUNSVRQPQV\bipx|}{uog_^YU[\[[Z[\\\]_a][cfjrutpnqux{||wrtqsy|{wvx}}zwspopqllpov|}|z{{qkea\ZWMNNJJLORTUZahnozwngbb^UVTQRRTX]`foywojgde`\_]]_abdc`_bggekkmtwwspnoprst|yx~{xzzyvuuuwzz{}~xuuwtv{ww{}|yxy{}~~zrjeb``b[WUMKKNTY[\`hpsz}rifkssy|vuux{~~~{Ư{mlrxLbQ{|xxYBpQX|{PnjWW_ZNa{Vyr[bfce{nuzxtf{cWUV`q]~gekvswnknw^[udakkmtikzhDrpIOepdM5:auZ@H`qrmfxxͱ̺jftOJW\O>9Puz^IL]ihiwƴrTkpTLUUICLh~dQPUUPZr\[yfTRgdbzv~{{hh}xaY]qxiy|}~xplhqx_dwpa_ezlVQe}oXQMIKTcpqgZTTW]uzp}pryc]XW\epxwmc_afr~vc]jxjgaafp{xspqs|r]TYeputl]RONXeloldXT]rredioqmf[WST`hjjga[_prd]^fmold]]Y^inpmfZRTar~zxrppqrspmlpvxtlmv}}ustwyyxxz~~y{sigmx~||}~}zm`VSW\^^YVTKLOPQSQNOXhyymfdca^Z\WRUWXYWTSXdurga_`ac`^_YY\]`a`\Z]dn{|zxyyxtrswzz{zwslikqx~~~|xwz|xx{~xvx}yqia\ZZ[[X[YQPNKKLMLOT]j{»yrkfa^`[WVRQSTSSX`jx|snkheb`bc]]][[^`__bfkt}~|{{||xsqstrxytsoligilpuy~}{z{z{zwvtrqty~}|~}umfedca_\``[ZWQMMMMPU[amw|ypijfab^ZWVVUW[aiw}vttrnifjfdda^^_`cgkotzz~~yvuwzzxvtrmnpllkgcadimqtv}|zyywrwxsspmhim|x|fexmm~\TjWVVXvW|[PbRCl{tu~Nq_n\jqqƲfS{RJmfUtqnnt}vwrlqZgTPjqYr]ju\TZl~XKhrXSZkpJtge|ð}{uVWr~onu]Wvq|}|fgU_ywVefXY^VPYpeI>@DCH_}^SrƵxqmb`niREDGKYv|a_}wvyidd^fylYPPUavuZWj|zk[RV`nwtsobOFZ{sklsxǸ}iYTZcpwsrk]OPi~mjt|}paXY`fquqme[V`xypqqr}zl`YZcq}jZQVeqz~}xtv{qkhlu}h\U[hu~xrsx~nbZY]add]UPQVW[_WPJEFQeyþ|rg_\\Y\]UPKKQ^p|pkiiiga\XY\[`^WUSU]k|}zyxxvuk^XQMOU^eikmrz|||{yxmd_XVX^gpuwz~|qkd[WUUXZZXVWWTVUOMJJOXft~~}zuoid^YZVRRQSXcq}xpjhhfdcba[Z]XVWX[bn{~}|zupk`\XRPOQV]aejpuw}yumdb\VSTX`hnu{~{yplke`]ZZZYZ[\[VWYUUVUTU\fpx}~{xpfd^XXYYZ]fpy{vrnkiijeab^^`__afpz~{z{{xupc]YQOOQTX\bhnrrx}}{xxpge_[XWX\`elu}~{qmjdbaabcbacef``_Z[[[ZY\ahmrtz{zzvlfca_^`djpvx{~|xvvupokdcddccgmsz}xvvwvwsjd^S|ŮypjfhuSari}huk{nvyyfvu_noPnuUDG^fjozi\[h{zvme]YWf{weieszϾsniUOTROTYWWB>HWQUSW`nmmhbb~wmjh`ZRL@M[eftqplXPTTKVd|lmd]Wlvynrh`htŸynijWQ=77@Nanoeg_WUigtd^dzy|upwxvneSYTfvǺzujksuotxs{kmyv|ʼȼ}sfZORQbq}u~wYLNUXjpxvty{vjq~twmnmea_ZSCCDJSuzvcYa]bdf{vpan{}~sbJXZõyn^\UWTR>DDKRGLSUKQJUZ]ipuqm]XSQZ`komhwj`cz{rkkqgZT]RVNezs}plc_MMJRoxrmba`aN]Zjnhjnieg^t~zy||skV_ró~|xpaZlh|pmgokujiyʾgdSMNBHMNM][mmjocnw|zzwwz}xv}p}pvv~uv^_hacWLU_pqmkmaffnztpvt||xq}wvl\WKPp}ywkYOJ=QJWI??IFF:3N\~jtmkraZVTVe\cihy{{{prmbrpukaUPLUUQovzva^Z_my|~mjqi\cajzr|feckl{ns|xvuqjmx}zogndwqmhbie_YYMm~deh_gU=CHEIKJghxry~uhwr|{u{{elbbqn|}z{dc_fgo\fnvu{{rt~uytkinry{ewzkdlmq|pqk^bbZbO[QQFCBRRfz}zj|tklYX`TEOPg~wrerlxxi^VYUSXQ:O[ms|ziswoqtio\c^dimjnbbulotcpyoxukn`[f]aONIO]xpxseiROYD=@77Pd|uwtrtvmwulagY[r|~roqpqjb_YLbk{wpuow|lynrt{xkdupdj^dri{wtv~rgaaiRMCKO^uxv{ad][`E9IIPtvv}sv}zi`przrsunpjkYJNYMexpuu|xgidnljokf^`ljS]bhzunnp{|ebmcfWSZg~y{spX]_NMJ=Tm}om~|{skkfkgga_mvsknp|njd[PHGZZ_ois~w}~xkqsyxqhptnksfnzzvfgyuzrij``c]lwtsuggnyvaR_XQQDIYSm}tvm{vquzjhm{{|xdTQPIKccYclüq~}djgZ^innVMV^[NOfdrz}|mmlo}xmgzm]_ep{{|}m]\hVJMKUnrzv~wql]eif^OVev{px{uz~~{`QRSVUU`QReinxp~z|r|z}xoookifstwkhkvss{}fbkuvlrqqy{zmrqsyohcdg[W[NUZc{zx|}|}v|w|}wvur}~riXPV\[HGWXcefx¶ww~kdk[crid]_jeUQV^ck{zohnwyvlilswpnxws~~ydkvtbO]]_o{~}u~t~}}~~|kYrpf``gjlvt}vxofji\U`_UEIa[XZdqýxv{xyvin|uabpxptnnsqrjSL_gqtn{}m||ybdriryzm`[ejaWM`l}}p~}swn|shmtojjmkeWSHFHALADMZqssmjrtehZ^dfc\SRi~m{zh]Vdr|q|w~w~~~zpwvuh_`\_Sdxlkbvnaj|wx~jce^fo~yyjkiuyaQ\`aLLTT]q~{jozqofoqmlvsoozmoaZYRFRhkj\r|{pomphwrll|xhT]gkx~ypshu{}zq_opyjkn`glwlVIQRG?5HObuvz~z~zijgtj[[br{~}}||ogbidb]fhbnm}|z|{}vqsp|rwjgtsteZTcr}pttm\Yihrv~w}ymph|~}qqikpmtllihYWPMTNlttpijtfh^]eov|yph`QWNW^YJFVam`iu}uq\ZQRXQbsKuwywj}yMNVdQ`nq{mSawtljhpiNGLVXIUA=Xferg]mfsϞ~unpxv]zw\DH^zpuhiponKCtxOchT[UanmϺrsooNfu_\\acaG`~iWucissumxN_nltrsylkybktQFhh>1CK_epuptxji}sSXn~{ǡtarpkcureG>4:IgzV88Vfy{{ľ~§tp\fh}hTKUO^ilrwt_^c}zaCNdqbbl~}txm]Y`buz}fVH@M\vudM^kq~ǭj_eiifqu|v}~w|n`_Wf{xkECEGDSfmvxitxvr{z}pzsshz~~v~wzu\UNWZb^aOBEAWr½tlou~|]UZUX]fXNCOJFPb~pecqwe`Wb`ZU]ei}}uvhmltjWUML<25P_xwyz}h_rvrYR]baW]KB:AAMgvǼüwhptaaSVZc^VIFPIL>KYm}ongrpcd`hbp}w{x{}zheai]QHB=Fjx}u|~~mesk\[Sb^]SG::85JN]\oôsb`[^RScaWDPPSDEKP`xvZ_olhR]diq~vy{ugWhd^QIQIJYquzxsggdocehaKFF>CGTIHUox~´kYe^`b_cSST[OHAKPXu{z~uzv{~nlcktv~~jpmqfZ_[biogx|vq}wy{~h_mmm^]VFE;<=5DV^lox{޲oa_fjlVRWa[SKJPNhq|||}mmfmhk~}sytqgmnhWXSZWlulhkohsw|z|vozysrWUUSIPSNA8LLTP]ryżve^oldQOa_XGNLP[nsrx~yts}spxuzrm|w~w|uvuyoadkg~mlt}pu||q|pvxy~ximk\LBHAIFG6.6=D8@Ldw÷ϼxtmp\`c`bVXV]dzx~|y~yvxxytnm||nx}}unsttomwnk\\[Yku~yygZgyomexzx~rrc]\KU\^OBCKMDEDU]{ĽvykTV^d]KQUYXouvxytgqz{qju}v~piilfk|~~n~~r~mrmsqu~uusrjsx~tvssn|voqqy}~sZIQIHBIL?36C=;1BITsɷhZfqhdXcisx||}~njsmuxvot~wwzvvlgpvrsqvehmqtheeln~~vktxkW`kx}vzw|yso]UZV_]QKFSOPDGLTewy{w{odb`\`W`]_wwqiwyigh}uko`[`Vbkslhjyoqssnmr}svx|xz~vf_p|yuqntnqz|xgot~||ndZNAPTSD3EKL>?CKQh|}uw{mboqx|}sp|fbbb`csof]pwyqrxbha`es|o\\ollaeqp~yspu~mjehgqvzodstz{yqnn`[\eWPPU\TONXXnw}~{gnsyrc[dnw|xwekjsqtzgTNTO]^^[Wekslrwüysqpm{y{rsruslvr}z{pr{wlluefbcmw|uol|z}rtudi_aa^QJGOVIIFMLThqtxv{~~~yvr{u~y~xsicTZbfcQ[elddgpt{pki`mkk``rsh\bdmow{{|lgmrxoi[[\b`dx|ym}wwppmwwgPSec`NSZ]gv|{xytylz}xsnxsu{rwywungW`]TNXYNKM^^ZS^gow{zrgmyxjerrwwz{waQ\\_ihofiq{xsrv~pfjainlcRQS[ONJKVfqnxuqgq{}yx{kedeije\WP`_^\Y^_q}tsrxrg_`mjmge^f{wmhvrspm^RKIW_i^Yetyvvw}|rpxxzjimk_\\_b_w|vzsu{~~xusrv|~|sc[TURadYBBORTELSX`w|kuyvrmzzx{{gebXQ]hb]Virrjnwyrtkifnj^Z_jaREQU[jmvz|omnxvjjiy~st~|}{iif^hkqcWU__WWU`g{}|{{{vdikpg^bgs}stuxplmZUPSPQORQN_`hhkryľ}|xxrhhvssgb^gx|~tp||upnw}||v|w}}vb]WRVUZOLQTLHFGQOex|y}|vts{qg\_Z]gbUJXbldalt{yuqgmpk]R`ae[Z_[Yi|zztrrzvnchjtqstiprmk~}|qphfcng]V_ni\Q\^gv}{}y{pjrvoicnjwvww{tslaLLJENRYNBEY``act~}lloysmnrz~}uut|xwzlfenorsxuo}|~|kaZYRZVTGDRTZQLIS_r{y}{~h`YVXZbYVU[Z_acontuliqto`\^ga_Z^dh~{zttv|oidnmkje\T]\jvxolx~~qhmrnVWfȳ|U:E=5NareOE\~vT2EBJfsanzssT[uyJG`rj:1E9_}|l^r~YatrŞcpvVLI~Z^{scaRZ^^{I9{aw}t}~ltSenZ]bSF;J=CQat\pw{vKmhHZmqywnld~`aqoebubimsX`uxBdԟvdXRjgnx`[agNMditnP:SfoxVbmk~}wrĽvvztG%;ȷqUQ]RYQESLUhxmQXGFU?>5% .Pfnpb]lýɽι}UFkgR\RCIBUohz~e]icqucqpnn~ǻ~xhuaR>CˮtkY\e\ff^yv}mUUB=IA@BbnopeYWo{[>OtrhjZ_UKffqn{rrsopqts}_n}}nX<64aԾzr}wvei^^Udscekl^^muzooiquwlqkC;Qp}yoc]OWmc||}zynflyylxsz}|hODF(3U~Һtxiicg^yrlonkYggf]V]ad_i}~v}qiuef]>Ie~xruq_hieyxfzyxpts{u|ooszskMG9$@bôuoiikkj_^edZnhlaVWZYQ]vf{znm{x~}gaR`VFWh|hpbcnww{s{rsulipr^S2:*BfԿtefb^oUVVba^zpmVEMTQKXum|pmldlyx[SATK;Qhwrys|ssf^_YHL3;+Ab̹za[TSmTUR][Un^_N9CMNMUj]utvroks{{]U=NMJj~tvvuxpf[][LP7;3 ?YdzeY\NhUTW[]Ihgh_>@KJJC]ZryrpumeOXk`~swyrrjipgmfe\^W148:S]νnhX>HAQSf}yeir^ng[[QIPmnrrkarh_jG@>SsƯw~lop^SYSUcF=?,73CN]ʹï~ntTKJLXWt~i~qnyfaZNJ`pv{weft`nfJIIgzɳu~xs|zzz_jbQNTP]^:8MK3.MoȾ}u|tm{j\W`dissppvzzfZlfXimjnkha`HSPMZnw̹ɺyghs]Yt{kfonw{mvZBB?5-:;TK53Rn~~uu{rghjmp|zup{{zaaoa[lhqpkbb[LXMOey|Ž~yq]mkW[~}ujelg|ihkG?=>/35BSB3>]x|z{qmrnuy~pqz~ybljX_b_mfdZ`PRUHOlu{ŸǺ}vrb\o`Tf|tfhglvfq^D?<9+13NM=8Phw{unupqxzl{nin\U]Tfjf[ZVKRC>Wno{Ƨ{svkWhdQSr~pjnkorpSH>B476JVG;GWsűwu{zmsrgosmsyykqdV_V]mgaTYJPG:Eegnuȸ~uv``iZPe|qxp{}q{gYKJG;=@XRA@N^þryyrmxgfl{xdrwrrp]X[LahiZXWRUB9Qggmvƫ{vbnk]]urvmuuve]GL>=:KZNDNQhž{lqx}lt{glo~~oir|qtfSXPJbfiX\VWP=<]jprªwksj\dvzutppskbVBF8:7NXLGRNj~peivxkzzgin{pmpuwdV]NM`ghU[W]P?Efr}xŵ}sjmcZgq|yrq}lmcdWBC574JPGHTLj~~occsuhyzkjo|urrl\cSQ]gkY_^fVCGer}÷xzrll`Xeix|wpn~{lmbe]DA24/CMILZOe|}uebpxl}wrq}}xrp]dWU\fs^`ak]GGbqļvzvom`Wcelr}uphvwlj]^bIC263@LMPcYdx|jfkwr}|oyz^^[TYXn`\]kiRI`m»xx}t}ssmobW_kdnwvsdloge]UdQH6566>EMNYw||pb`q{y{xh~eUONUCVZUT`laS`azvl{vpgoqxiYfqizmu{qqjbkc^e[^?:AKML[y{zqccqxxzriyeUHPRCVWYXfod[ebuq}|miry~m_pqq{jvtpnb^h_d_]Z9:COLLd|}}|m`fqu{}|mpyv_PGWKKWZ]^lnceekqs{|emv|gbslxwrlqs|pgY\b\bT`N5;HPHMmu}|~zh`jpurw}q`MRYITT^_folgkc|·youn`mw}p^iiowuktlwm\T]ZbXY^=5>LG@Wmr~xuehnp}|~~}o[Q_OST[dellhj_htsz|]dr~xdafdtiprkrscTX\caScN77FJ@Gcg~y~sinnuzkU\[RYRbgjkhieZt~{qpp\hx~rac\iohlqd{o\RW[gXZcI:?NKARcl}~urrr{udU_RWRRdikhin_bw}tnneZiz{neb\rjnyrmejVPT`hTbaL>EPKFZdyxtqo~y|r_UYLWKNbjjfio\fu}}smmaXgzzoj`^tir|yljjVRSdgVe^O@HQLJ]exrmlzywo]VTKWFI^jgbgp]js}{tnleXdxyqna^tktnmoYRQchWd]UFHPMLZay|tlh{}u{z}vpbZTIWGCViibhudmuz{vrnn\`vzuvh`vnupl}sXPM]hXc]YNHONMY]t~zngt{pwwuqf]WGVMBOhpij{qs|z}vvo|wd[mzy{ubtrt}vesx|^NIOdY_\WVMMOP[]t~pstinsvzqonj`^HQUFI^tpl||rsntzz|l]ctwz}gput~}|cmq|eMEDZVZ[RXTOOQ\]kplz|no~yurkmlddKNUHGWqto}nomsuszl^]ksxkpss~yflmu}iOC?SRZ[PWXRMP^_hrkvrp}~{vuhklfhMMSIHSnup|nnltqnvk`\fow~kqnq|tx~fljryiQCARO^[PX]VNRb`mrnxtu~wx|~rbghggLQPIITlsp~ljkvlnrlb^cly{lsks{rvz{hmiqvhSBDPObZT\d\QWhbupo{vz{t{uk]eekbLRJIJWlqt}ihnwfuyojc_co~vstjzzqz|upoksrcPALLWfXYbgZP_ge}npv|w|uyq~bZbclVNNEHL[koxzwjhupgxqnje_evsznp~wruwmorn`LGOJb`V[geSReapqlsyxuzv}r}qY\`kcQREGIQ_jqwvskszhuvqolc_k}uxtjz{sz}}upsqjYHPHWdXYbiZLY^aw~{no~xwpx|xrdY_hoZYMFJOWdnxrrospzssrmaav{v}kpzuprromgROOKd`[^ihTN\Zktvot{pu}u~p{z`[aqk_]KMQW]hvzupvowtstum_f{v|xitvpqzqojkcQTITd]^cmfRU[^qqupy}pxz~pu_[duhf]NRW[_k}y}~nyznvnruwm^gyuuktsnr}xqkehaSSFVa^`fphVYYcrqwr{rv~}ruaZfuhl]OSZ]]ly~nzuosiotwl\futtnspmq|yribfbXUGY_`ciqj[\Ygsr{u|uu}|sxdXcsho`QT\^[g{ɽo]V\b^bZQbegd\mzN=9=@IZhv|aRfî=>joqb[a|pgûj|VXnax}M70MN>5.)/Wt^xzsXeǾeHVbk]i~^Fbw|èbjxo]15@BGJYo~YJiîZ(gvt}|~unaS^{tZB@e}uoRJE20?Zmw{oպǾOAeSEQGDLRK;:[{mecn}SSdwxlyhSSP`r_Nqk3>¿pvpbTGCGcow}yVK4@Yuuqz³aD6bǖsSQ>++4;>E\lf[JCIPOHFK=Up{vil{~x{cTr`I9YǯzvgXNJNXrv{}xvzzeIIDZly}yɺb70@y۸kdN98ET[_jj|ohTDCFC8Kjɬzyv||sol\VYVdaclqvz}x_Y^jmjqppr^df]ZckhYT;:GM]j|fa\bjs}~|wibidg}~xshXJTMGA=Qoʪ}|tz}uoiYSRO`aepw}|c[^hmlspswacc[V]dcVS95DPduƽʿha\cnv~z~~zlcieew|{uqfUDLGGFCWtάvw~wnfVPNG\bht}}i__gmmtrt|hdc[TX]^RR<1@Pjƹükd^es{}u|sfhgcqtypofU?AADON`{յ~rzrgWKK>Oakz{{re_ejmtvrsfcZRQSVMMC1=PoǵwhheopxqnstrTnff{vldcR80/8DNmtӽ{~sz{xp\LQ4Lhlzrzuhubpvqxd]XNPJQC6B"*Fg~|vsrk~zuun^jbftskZ]Q(,%9b`smrwnxngLcL>Wq|{{vpoojs|t~~fXW?DACRE>2@:]ʯu|r]uzqq}}wunWfUXlcuk]S=%0Eby¥zvxuqrmFDY?TZqyvrrfnvrts{_TPH,=06<(FXu{v~zuqotpivxls|_i_fWa]R\E87+0\u|Źdw}qoQCFKP[{{~y{qmqsgrr_^GHPD87J25GWqz{x{tvmutwjsUa`Yija^iP?335Ym}dz~pk~uviLCKFU]x{mxemsyoyTDHI4+58,2D\ƯrygkuupvouvhmaTVeYT\RJ-.,3F`ն}ivx~vgTHM5U[k}smu||sgp]I9BBB0>D-CAG/BB5DEXfŸ}r}rxvrouqzjYjIOTf\ebQD?4%H6Lpįfx{onex{zsyj\TeNOT^XO`CH*2B\iž`o|slHSLPMRhp~ezwmwtamOMA'*/3)@>Maƭ|xvwwvnfxt}wwv}wPhZSR9NUTE=>/1;2;<-=;TT`}ϭhdq]zp}wjqoqklWFC>LdPQ;;>#ALu{{]nch^TLI]Pv}xqdtovc`;@+&+45=`YrvѶxz|ilkk|x~{lnp{w]\C@?/GJU3B?.E=hû}|{wgobv]dR4UXgry|vzZrghik^FG(-!7Fagpu˻}ZWgbirzzgjljg`a=M74<1KABB37Qkȱ~qYe]cTTHSYfžu}xqhpfoX`bPD,1$NozĬ{{xzxonddww|ktat\S_EG6'$?=;MQCQ_uǪ~vsrt_mq^ZSFdrjwqik}h\eOCI6,0*@i~ֽ}v{i[gcfz|u}v]gkWeZAA912E@;?=>PtzͿ~p|iigPYbEYbmnvum\{qesZKP:1551Vjvz|luzeb^[uyxyjmsgijIEA.*12::IIJh`uƹ}vuxipiXbaSSO]ey{lsh^m^]`RE,()&DYf¿uxvtfWX^lxwnabokZP@388263;>F]\t||t}vycR\U_dVacmqu}{tjppbbWQOB5,2;>VwzǶr|}s~lSeaelwkju_d^?88.)-@CCKCMdqª|vvsz~lheS^]N\dlnv~nbpilpc`^J/!83.P_hnsk[bWRa~|hqr]naCKE8&)94AM@Scetyztkke]af]b]Z]r~szoig^dhZD,;+(GI[v|Ƽƶnv{mpjeISptzwh`ngPM@5$6>9=B;CYdo{òx}t|z{zq`RVbfa`\nntvv|nojpgVJ66.,9:Un{z{¹rytmvdcJUos|jgibe\P='4//5?JGV^]eu~rsuw~o_VgkTYTegnlabunb[L<+43/EUinz~rj|tlSQV`mwsmn\hbQD1A94,+?CL]_ipʭux}}saPWq]_Z`_g|zqdvk[XQL678)AL`enñ|kqjmXf_Zdfz}yvb_ndS9?2/))7ERQN\cͿɹ|ruq~sjXbWVS\`eszytcitcdUSI=@40SPpԳ~mee[olaMVpyvyuoWcXJM:4"+2)INBScɽȼ{xqpqnmhFTKOdnw}|q{h\`NNQROEE/>KZ|̹}ooZ]_e`Jfusu{|iOWRZOH/-"*BDRUt½ô{vbfbv`URFNfu~}WZMIQTTQQI:FKemztpe`f^mVTfgzxz{~h^KVJG?'5/*8(?E[{}οkoaneXSBHVj|{ssfVCJRUYVMAB?]i|||tbbS[Z`kd~~}}zjlSUXNL/4-#9'1EJeh̹gk]hhceEGQXr}{rrZDLRSZ^TJU?KSgvzƤ~}zzpo_`N[kXt~}~szgUMJH7F=,:# ,T>av{uvd[nNcjQi^h~wrog?LQCQ:9-, AMO¿Ʒ|rtvOUbT`[ijn~qy{jurg_]ZPd_U`R;4K[jƵjmWMtac_m^hxcpndKSP@ZMbMhmhqhslq[|gfg_VFtgrznmjY\o\]O4<)@/;.(78SVB@]euioj{upy[ixmyULSZ^Styug`OVZ_:=396;=:;F;HuɻʽŬ_Z\dTYYql|~wpitU[h^LMC@XWx}}u{a^ccei_abhuaor~dgS[PKXGC=.$@7?LUCMh~ұ»ɾ|kfzl`Zhei~}xoY_bddfRNW]{kjonyƝ{uqleo{xh`Zt{Uriv}|kaRORPHY[@7EARFA;5`]rӳȯƼlx_dZchjujr|mkk|gWLJS3@FQIN]ɳƟXanfnhWbnktoswU^ORUSRgr}{dx^ino~pVǶyuwyZmei_]mosolg{bRbFYTL~Ybbiq{wwzvrWiVmrb_RjK]o|tbp\sj\Wdssrusjxy}wtofP{ekXd{txy}ȺhJz^IJ9cbbcYtqrecmZYslma~unihqvqN2`¶_Z^Ioqyffyc{vlfNujObZZKOhwf`ddmwkdUtxwMBbRWQBaakrjQi|z~w\VVAYuļȬviTnttZegrjc{k[O@^HME\XF_WqmO^p[tqcWlm`isoZPjlpb^kGjt|yz{qi8EH=VyȺrpW_as^onwMOZ8@b]||qh{v`RQmvgibguvYJipugl`ojbflt|zu}{xwbkb^I4YEm}wxrl^hhlpvv~ib9I9<_SvyZuzjS_hjwa^^{yg\`vighLekmu}~zy}yzt~s^gSSM0FSg˵jhlitkztzxfW93/QN]hixnVV^llz~gfZMr\K\z{wppbl|u{szypz~vtgjhOR^HD]Jyiyhx]vw|twq~V6B1H[Y}~[n~lek_yeswtaSbo~sVly{f[[me}ss|vʹlw~~{sxid[8W@@HOŭ|ek|{fqw}rzygO70.THj_~^hi`aszg{v^]Nex{zb^h{hs_eqzkpvz¥|~uqxthdghCNYM\Bl~Ƹsg}n~kh~wrvw{jEP,JOM|wpqgqocsinssxd^Opmu]p{{tWbmfm}rxp|y}wh|SGU=QH4>F`lty^mk]_eu{sxnSFfT~snntwj_uy{t{uxuqtĸ|tqgvnheKGPQM=]j̶ym}jzzh~WRK3M?Hnvyn|nsxpt_wo~eqoQIVf}wttk_nv~pk}t{|ǵfzqexmmzS;V7TI:Uuvup{ss~w{OFE4:4]hks~ein^`~gsy{z\:NSlw{zz}{Vnrztprhs`}|Ƶ~oysZjsiiDGIBZOXY•lsxsqqjwPVAP@Aglp}tqpfhgv{xmp|wlPzxxvwpmj}~qx~ikk|svj`f_ts<>H9KYdizsztvuofN:J?M`ipukrtp]{|[qnRhFLSqzu~zzdxvrxh{r]}wzsp}lc]OZqkJI96NKvq{̳fmx}grqZ`79PKSd~uzsgfhpunfcTamWCDqͭsmt]k~p`yf^who{xowhRONQuP:95MW{epvmp{vgYnL9M`m^xwndm`ox~|iVfUVse`VhǼyuuuwia|tofpp[tzpz~zrynUORHKlbSA5Pbr`|{qn{uSv_GGVmmtw{pmZftof{_bZWu^XZrǵ³vqqiyb}shqv^eju{}{}|}aKWKNaROS+Ahxzg}~vtuxw[ceJ:\qi||u|~scdb{wug`EYgZf[sx{yxl^fsNghi|tkYQCHFRTSM5E\xvzhyomj[taBEWtur{{k]w{izjxs^bIP^]Znz}Ľ~tpos|w|gi]Wb\uxy}vsxjKPL<;KONjP3Fvzslhntpq}kte\Tappv}z~iXh~oe}xqqqtNFe`jgt̢{udvm|u|x\aat`YvuqwzjV]M:CWR\X<NcqK23>\zÍcow|zUbwKSflB(;lo{~pmc=&KikBLq|}|ȝvaC?JW4S}fBHQBLN^i}ak{WRgh<-=kpf^oml~wUW\z~n`fmzuqjuV\{p`V}cZdXKYrʱzTl_mzj^xaF^ujqWR[h\ivqhLay~rXWviGHINjqi_QO@QZpvi_ktuWb}wqaro~wa[MZUgqeRCW[\nvr}fIJlK.;R}}zht}f^mbb[EH__}w]N`~پyyiUMNux}wqRMbw|rlJF9DWQGJEYQNNFCD_gahgVjz{jɾx^948XnoƸanyeZmegeMJ\Xl{[?EYjɯv}}ypxued`gɾz\V]SLNJb\VRLHBNOIW[QfzpiaaWvwihuncGJU{s^phXX}x}zvyz_KZlzmosnvs}u}rvu~zvc`[LGHPzyxlvsmg_JYf~zw^VDIMMB,8Mwbipd]{|y{v|uxg_jqflпvjrZabhj^csnwbY_[__aRIX_^wv~hcazhUJ?PP\C.-35Oav~p{{SCLM:Vp}vvwuqdda^Wn||}}xmkmhjlvpfX3:G;G-Mguorbc`b~p^l`uwnr}{wuhi\cgezpvlLTglXgecpYdswz}}sk_UYlbZm]ku}{sk\nwtx{~toxjzdMUkpt¯xupqGDT`sjnreC&7tS5g}u^`Vyppo{wmZ\\agj^@FQuqxkfi|za]jPm{oq^\lʞƲǩwW;9&.a{rzYDQ^QSzsgkwqt}_IK[xohnW]cu{vlcmK?85*Kbrt[j{pU\r}dYdfmq}bZFWXW0#)@n}sŽ{vqp_x~yoC=Uo`XSjdonhp{xlW[N^kz|A.1BHfŨintss}o]LRMMNulbqzsmc}[kpwqw{sqN@D]t÷vlYmuWLIB:SVkn~xadk{kku}VNCXo|bIFIQH^s}|nxxr^sqyaeYYgpke~t{utvt|nacK@I?N>E_{Ży~j~Z\`hmdXR5SYawsz]fbSalkd~mLO7Pwtyeuo¬mhwolx}nuwmpjfiw}omqiqkcxv}~q`SGHZoȶôPJPALTf`p]VP]y˳Ʈp~tvyinrok\cSL]Zrsthtlk{i|mqJE\DPS^GFKSiӏ}pu{b5al;jirrogvaIs~hhmklsgj}p:wx=XzzʼxrbggMdoT:?RO;*+O|wmFGIYVbmoype_vhUGwǧlHdmKiiXC=TV]Woz\;1V}wˢ{|k\^iwzcUAHXsɰte`ynbSZlfgYG;8Xdx~fwx|gzrZY^lrpp[C1OuraYw]JTpl\h{|vxkrjfj[REJYcfS:+UgؿxgXzt~muD4CP<4)+('\`yszxwtthw}t}{r}opyzYZ`m\g`_QA@9@#3D6b`piǻ~yzrkqvp{zxxjipuukJQQh\^uTUV[MCN>\U_XXgrΰŲ|umyzmsQb^XWowvqk}ygcXVoZuwnmSgXyeebWPebj{wñklgeURp^a;<`elq`RXm}u{`x}\[RI,"=2Hxxiat~kŚpJxX[srzpY>gaQuobZpypayi^f}lZPSMVYUWYxxbrȤyzx|^>/@buu_{k~tvtrml}W2Y|zb:’n^rcKMZeN^`fl]lqb~y^vv`|y}xXjmoeGBRtzpgʴzXWqդuJ7MM^x_9ClxdLJMLfvk|Ʒd|[]ai|xHSk{{ro~kjm}~Ѱmww}`J16Pgx`X_FL[WiWA=Wqxnjbool]|mm|}z{oxeZohgps{v`0=_u}dgZbt`;=Xn{q}zgVTJPok}ƭ~|qxsfZPUyƶyOQXm]tol}of_jWABQju{kS;;A\rjYZRUPZV_hS\qmuprs{vmnoxxeXRjt~|}[moxvnosS?ONTVikgYHUwt_iooeZ^gZrdsmf`RVYubaU^muzmpw}lSaTUd\vuzZWƶ̵qj[PK%%9;A5@Fupmdr|qtrx}yw^lbsnznukmWyv~hdgVWTnqlc]pt~olitf;=;4QT{veryvldYReQX{wbqseW_^nĜ{{|f|ywcpkt^|rns[WO^eeUDRPYORVs}]dh^m]yl}cY:LXb{wbVikth^HRhixfTBJIVgi{tilncinpklpoɯåsfaaA6(4CYVYypme^gXxry|zxxhequmxhZgII[aqhcL]kvs[JOLZ]XnXP\P]Xt¥tm^wwsspwzxw{`zi|{~w|qy}x{thUHYxskzr~gflQ[bjwldO`arwcC778KIX~o~y{r}~Wcd[\=:0AFIs~sgmkqsrs}v}kl[l_jmvz{̽lvcRXOZXJ7-6;]e^Tdomcrvhu~}jTstysv{~jlzf^PMW{t]iebluk~hbMIL=QZsvszrʷz|gsouegMVSYqsuuxstvwy|\JcbU`~mvrhsp}o}rgZ[vkli]]kabr\PB2EAWlq~}vtog^etjn]ibG[Vhxy~ib\nnZsuyz{xzqcz~{z~ȡznVHNn[GG80CSVxv}}izvlaonovgtVZ\|s~nnwywerg]avaasnhvxeeWbehr[råmqlu}||fpvttjtyscPLj}iqmwy~~zlfro}thj\IYZgxzxxchm^l}}{xnyl^itVhuhcbxwu{|~{|n||qW]WTB=bQUdjxunx}pW^akihqvk^TZfijup`itnmvVJidOC3%8D7[p}~~ul\lqzn_lsq}fio`j{{yfOS\`Yn}|rJRXfztttu{ʳqyf`ms|v\kdX^ifly\Sz{{q|~x{ld^X^q^_^Spil}ls{pOMX\Sd|mzyp~rp|ofnnvyf_X5?;5?Tbyy~p}vzy~ytgvvlu~jvkhmisz~pi|y|gKIORZZIf]CFc|qpjh{l]baf}~~w}}|oz|i`n`nheotyz~}lbK_s_YLQdtn~narzrrvp\s{xq^pqobeqnunl}|l]J9RVXU`kwyvvx|{zlvlutcnx{x{wkXeyx|q}s[eynie^EOP\YGMXRD\lpuszxbevc[~~{uusqssjhajfZ@@5?A.=[lv{sx}wYZw}wnkfbSdiywljns{x_Zegcix}mikVdfljcHPlgl¾|zjfjkgksnfbtxwipm||mimhTUXWeL05Uckr}}v~wsw}jfhjdjtwgopds~t~yx|sy~urzsyfJGGG>Imnepkyo}}yzzu~bchiYdmurj|k^XSYdYeWF889EKatxtxzpxxlhvrv~n[hq{v}turbum\pvsbY]Z^pf_{xn~~mzxy|zemuo_a\pw{oymdqqzrhRXJAPXC>?\qsm~ttqlglxyss~~|{sr}hfrhmkndlc^^H?U[daen|­vj[Xg_XJJ6;N`~b]tprsptheNWl|rdDFLM8Zcvvmeuqu~{{~uZQS^UJ\kz}tsbryhprquhwi]\PIYcbU[eYjjto^_d^gPU[blu{qo¼{xlbfatdZLYmfXWZ]PMW`~yth_WiizlVT]pql}ȶľ}{h]F5IIGZd|ppnvsfypjbQJ[hk]n{|xzs{~ùwn_\ee~zt[ivofgb`PXUb~{xi`NLTQiZLJWcfk}{|z{zneujhWC=LVSG_gtzyu}u_]P[epkYYf^osk}yutnd[g]x|vfokuob\cc_hln|u}~}juzwu_NQ^cTRb^mirxi_beudaXSdr{o`jjl|~pell|dYeaYXRJ:CEGjw~usidld~~qimssw{v[NSYTCSSbmpuut~omYap}mluoyjnt}~rv~|{oWchfhjdWVdZu~xjpbmyk_XZVT`[]ur}zrkyvzybSWafU\__ohqvmjjxw~nk~°vmqru}qryt{zy|vhWe]`yrocb]Ulid[\]Yak]orptpsyrxoyhNBEIB7D>Q[]nttx}lmypjh`rjvyjlt|~rp|pqw{the]Xd^[njuwyŸhoikuaLJPWNSYTkhioljjs|vbj|tukp}rhY]cwusyt{}xqornaGLNFYeutea^\^L^fhjhifj~xȹ~u_[\\OWTTjfjoptumrwvgrwn~vlqz~uuunjnvugP[WWisusorn^onrqkcXXe[fkkxqn|wlujmpcooyo}yihlnguqt~~~udbfzoqynkm~yoq{|pX`^^ov|y}zftqv{wodcrkuyuvjszzvx{jpykVNKH>MILcfiggno{ojw}vublrjxstz{~{t{uszwkYPZT\e_qpfl~~|~on|xi_\ZP\bZpuvlcdehzvunuwst{zwiPHFZc_nhlnv|s}nfh^oqy|yzrsvp}ujsw{vgqpdVLE9>NF\kuvmpx{xy}|g]Zjyu}}~vhp}odfYiloz{tolxe``S[eeTDIMRieu|zyosxlx|ztokag{uyyv^PJWfdvqs|wy}rscrvv~}yvvrrfo}vehimzy}upy{oylcewtwj\eec_WOAHZXo{|y~qwvq{~to~y}|xk||{{rejvyeRTMWibqywu}}}urt{sywwwmbR]gfz}nghnxn}xttvytqshsokx|w|sbWPacmzqy|{~qk{y}~wrz{orj~|{omjeecVE7IIVflysfhm}zxymacpk}xpu~}rq~tztlu|v{hryyzx|z}zyeemzs_YSRifpyz{}ut}txtt|zn[^kjsrz}v{|rrl`THSPVdX\gtqm|~v|{~tlp~vhfYkqpzwtuv|z{wrquqqkejndOCSPcou|qqw|{|qm}}~~zujour{u|u{~~y~tb`gqkYSEIYT`b`ekqv}xtt|xwop||nYaekxmgsyt{ukp}utole\e]hrhkt}vz|}tpyz~wpsytgx{~~~}srt{sqh`dmgPAKEVacie\WXos{|}tnyyx{sz|~wzw|||{qq{snpz|qj[Sc_imhjtvwxtty}xr{jdkg~ukj}x{{yokwuofacXXRL\TRVbh``ji}z|rq|w{|yx{{uu{|~ykdltmSQPOfjqpop_lPb^M;AJOp|j]qŰƶ{nsYZiqtqf^bbYphVKXiwgWN<5/Sv{SzYqit{sxe_dcqcdqpJ7?Vk~poW=LPeoni}p[RNXtlzsUWUpY)>Ѥrev{lb`s^`dolgly{k`VPCOC8I9H^qmgaOezycgaVi^eWFQԥuTZvkZJQYntlhWQZrxit|}zkckW[gm~ƸvU/:\ĥ|[O\klvdTZNTQ>4+39WncokWZSB49M[pnWSZXai{{~}_[XMg}|zypu}w}mZB%!Km̽h\bTOM;IW^h\Y\^`kpkoeZWipwusimqxq~Ľ˹`IJ?0"8Zxʵ|~w_T@?QRQH9REF34QsѺrvdib_lhgswww}yzovrl||rrcijsq`UE\R[cQZ\uimjar~y}lNaYcpUNO@=1,;>M;Kn÷¸~sbm_e}xs{{lf]iv{raopsanjhwuia~oombnqs~~s|lMaUUZGD.9BUZOpƼ}jlpc}}}{vrnrcklwspuz{o~uihKLUAOC@1'8G^MWtƹ}levjs{r~~tys}yseyw}ir|vpebw`_^_jjxruesyx}{mWfONPJK:EQi_Qm~ʸótk|zuz{y|zlv}yx{{pbypgniurltyknwqkIN@3=4:+/@W]JgµȲy~ozlfx~egkoegti{wp}tbpwflbihqzwx||th|uvXQTAMGI;0=QdSfȻqnvev~y~sonnyyu|zr{qochimw{xtwu~lxnpZQYFLJOG8@Vn`kǼĹsm{m{v|}snykhrvlbql{ju~ysek{eaYbgjtuskprpxyi[cLIHLI:@Um][pzðigzpz}zrwzuz|qhwrwxzuqoz~mz}l[\A=>D?06NeUVm~ȹ̹zxu~~ofl{~{jeile`ohuxj~qs}kgdlkkr{~zywwavvqvfUU??DLC11H\JPgwrq{iuwrp{wtz|zswssx}yu~{eyulnaQSA@ENH85KbSXm{üƲzsq||}~mmt}nimwpfoiwnyzmo|ma[acemuuolhtfvzstkY[JACJG94JdWSakyŴxpz~s{w{~|oirwv{yr{gm~vqsp\YH78AB5+=\WN[iyͽľw~vnjsxogdkk^fgl}}{hm}s}wg`flhiozyotf`vtmnrd[Q==JQH8=[aS^l~ŷƶ}{~sszupu~vz~}srvpmq|{ppwm[pukgh_OJ84@KH:5QcW[gyͽ˼zv~{}{fik}ynebomflftnyvr~wjdjjimz~vmtls|z|kbWGIWVN?Nnngmwɺr~|{vyzt{vwuw}zshjooqw~mnvp^nnihjfVN;0;A?3.Nb[_dvò|}{mokw|rlfcqngmk}nztnvwxvw|ljwrqosi\Q=;LPO?Ghkfit̾||~zoqpyshlxsuz}y}pqtplkzyyhfjwlQ\d``]^MB4&2CFC6Okhilɹĭ|ooox{tiu{w|z{{smrpolv}lpujbtrsprmZO?;NRTFFkxvx|wzx|msqxwln}zzxw}t}zugghggh|}}iderlT]a\^ZbSG;,6CDD6Onptt˼įvxuzvtndsyuwsry}wptruq||ptyjbkeieifSJ96HLSFKovuryŹ|xuyniki}vrp`gsqts|yv|{wnqopjl}{zvbefueQ[Z``_dOC6-@JUQIk~~ȿǮ|joj{yykhwxzy{x||qssrplqlkwu\chjoircQE6BQVWE\xy{vv}wy|xz}~gkgs}rtjcsx{yustrqoojvnmvy^afdibkaOE7>MQUBTtw{vǽŰqspwqsj_mtwuo~q}}{xwtvnyopxgcgdjbkePF7:IMTCNortkw{xtvhjinswpcnuyxs|~|{xyn~tigoy_[^\f_icLA25EMWFRrwzqŽ˹~mmjqvzqepuyvquwvtssi{wmjrz`^bdngskTH9=LSZEOlprjx~~pqovx|sgsz|uu}zurrgzxnjpz`\``jakeOD7:HOVBHglpgtŶrqpurun_howtjtq{{}yuwk{}pmrlcgdnfnnVI>GUKBaosmlªyxkklvvuyggnvzno~|w}}u{rowsl}ycfbmro}lWLAJT``M`uwvnxnmlt|szldls~un|p||srpcy|tkclu]YX[g^jdL?58EPYEIekoep~|toot}svsbglzzmtz~tz{uqzikfsooxcOE?JTbZKctyuns{xhgiq|slufbhp{ojwu|}rtqd{z~md[eoXUQU_Xe`H:14?MVEMirwlxzxqnt{u{|lqtu{zsuxsl~wdc]kmmziRHAIRcaRey~|s|yqv~kggovwlvlafm~wmxo|}yu{yzqln\k~uzrg^_p_TPN_ZbgP=66AO_TNhu|uw~urw}|ynmp}zy~|wn|tivzgd\fmgsiOB=CM_dS^u~v~}xqnyfa`in{tfoi_ch|wir}t}yv{z|rlp_lxzrf[[k^SOM]Y`fR?89BPbYUm~ô­x~snsxwwqqs}|v{qkwoettc^UagesiPCAGPcfXezy||xt|jnvhbcknzphsicelwnxq~z{~txy}pol]qw{qj^ep`YPTb^jiQ?;@J\i\`v}zzrnxzv~{noqw~z}luwkhwmb[Whflq\D@ENb_Xbp|yw}o|vmcknrzppvjhduqotx{|v|qsfd{~|yqf[fe\RIX^ai^C6:>G[ZS[jyyvxxmtxyyv~tqmz}w|||sl~thqumbVbjlunTCEJOdf]ao}~x~}prxqgjqpxsnvnjemvmqu{x}rv}qpi^p{xvpi[bhbYJR^_hfO98>AU^VXdtyrw|zuq{xy}vqqwyq{qpxum]]lnuweJBHIWf`^es~wy}{m|ulblkntlorif`tnlp{v{xyqx~ppbbwzytqcZbb]NET[af]B39=EZYTYeuvu{}~ru|z{|xrz~zo}}lotriX]ilsr^FCJK]hbagw{~xziyzrjbmknslqrkhcxomr{vwu~tmtylk^`rvuom_X`b^OGU[bg^D6AILaiijozyu}y{{z~w{ukr~}likolWVbjrsgLCLLZiijiv|}u{|gqvrlaihfllqsrmewxrsxuv|wiq|pl`anwxrtg_aceTJV_jmgP?GJSgjlkr||sw{w{~wxyzojt}~wijjpdT\fptq^GIPRgmnlm|yv~rkvtshgnjmrtyxvlpxs}~xtz|liytnd]gsytso``ah`NU_jpo_GFOPclmml}zzv~yy~{{~zszqjr~~nmltoZ]gqyxkQKSRblnnix~}yzmwwxojqmkqtyyyomyrxĢaVNFR__juywUSbls}cywskG@VlxeRDYyUFUcs}tvfVis3Vshv~}uxfb[hs~e`ot[ieahz}zn]XJThtyj\HQxkc\_m|ypLctCAatl|smynj{|m_ep{iiucb\SW[gc_kgkxzxlcPKgthkpvx}wzYKguPJ]gef}}rqptnp~ormdefrywryx|vk~nXAEbrh[I=I\ZQE75:EKObgs|kXV\gjjYLVVZa\VEAPYfgnf]pz}xqqj[cu]OGNgoT<;NSKD9;DTej|{z{pZ\`bnoqjeww{y½kH9DCOV\^Uanroef\cxzxqjjjrkajvqR>CMbznRP]ZSJDLXm}zfijp}~|x||}zyuqaH02AVro]fj_VIIP]orz~xxwoptv^V\]jrxxkqvtlad^h~zsmruruyxhb`dhtnfjsrojYC8GYtjgutmd\bk|}~~z}zwruw}x|gYYQB16Mgpvypb]biz|xxv{xinow~qjomkaedh}{shfjhhhsztyud^\bertjjoz{}zyc^^UFJUMOS\bXfxqpv|k\afjorg_kss~~{}sjrpjbV_fle[NHNQWdfjXTlxwor}z}x~}f[bdfdecfniou}}ynY`hebQRV_]QJFJG@PUb`T\bgjezxkkhljljddbnw|upqhibfednicVNNEWdjgVZbfmcm{|v|mpgja\fjk_Xlsyx{}sehecXXb[_WWXPO>;JWd[ZYRY[eqzzzsrfijiiZiv}q{}{nid^pnoiekaXQFTZhb[c_^Z[qrxvvt_[dfm`U_j|vvwt}~~|yqgdQZehfPU_ZV@>AL]Z]YQQI`mt~}|~ilgdkbkiswtz{}zof\nn{ragda\MWQXY]dccUMgpvstum^g`ea\`[jpvtmulzwnTWaeq[U[Y_NDDAKOZXTXFR_mpyx{tnagbopiily{}~ow`ljt|kndZd[`WQMM\_fbO]`u}l{lorg`aUa_cbhqmrng~|qYc]kdYeZTXNQDFEHMNYSN\Wptqymedbqmndgryusrij~kiwd]o\kYFMLRUoW`u^uhe||{hw|}yhr_R_Rbh^Z`xmxundjesoWk[LQM\PTAIL>[ONd[dXn}roi^^\lknogzprkmnmU^oV_NFMKRF`er\s|vz|ytvwunr^jZWb]gXgzrvu|x{x{pLclViJHQLD>G7KSEcUbj\yzy{inbjrtlizm{yr{^qobZUdSc`YVBNHOrltehotz~|vyypym[WZVnZYhYZrzz{sOVdVg^\VBJMXQEC7QNdnUfkXg|hba]Xjzviu{nw~sbeaxoYQ\\H]G>TT\ehrpgs{~}}r|zifh\\YRVpfepunz~v~xol~d^T]TNQDRG>9?GGM`Uivcjxv{psmjfvdltmpgbmbPSaYQTKD]Thdnxd`hl}y{{r|wq[WLTafcelcr~kv}}utx^T__VFOLRQTRGU:FMPbk]fixlk{~xzqcgvebtgzqtkik{fielPS[LQOTZVjnWj_eat~}yzx|}~|ww`ZKXO\lgzdc}tzr||y|vyjgeIPOVGIUSRP8I?HXSedve]h{}~yg{s_kxykt~nqidjpiWS]S[C]SSk]^kujdYlzxu{x}wvvdgc[TT\qdgtjpq{olkzqfLQVXDEYHfOGHGNTNOZeg\cjxkyouulufvum}~rx_dinvXdYca_XHDZYaeej^eipcry~~tmryfp[c_\Y\]krlzvdmp|{m|WSZdPXNDLSWaAGKFT^ZUZUsoqw{rqiqyyqzf}r~zuZcgaruY`WggVLMMRqlm_mejcl~vtf~q{zkf[XcJhg`r{vjqqyr{n}yujYYNd^[DERRYUOBPLcRMehjtntluxz}vyxpx|vT\hgtd___RgIDW[jrjpxkeyvx|{jpsohi^WoYdpjelkxnwz}qtpruWXZZR]DGORc^PNYZS[h_qc~mwskmyt|z~}r|vmW`hinuhVflWWWOdSlpohh{yw}~yt}ui~hmTdcjd]ohiwmpz~ǻ|MVC3IDFGYT]t~|hqåtuvfzr`oԶsR\rqVUi|mSON_u~y^kjnhplkUmot{_v{g}tSpyee}wNDDYu}uNLXemv|bcgrr`VNmupkifnǯW;ǃZUoylkXUHcylvɲneZun\aqYMNmuYX@CGRzt{_OYktx|xtuiiux~Ĵ|yR)+4K״jQ;ETRVF/37:Dhq`pt~jO?NZntkolbemkhfoxpkPR[Y\atozxgb|wuyV116JӸzZEN_ci]IQRS[x~rqcsiowvvwx}vc`ykL*(+?Ŵo^eswp`FNKHQim]js{aQbl}zrrx}rnSV\ZZZhYdwxrmrovwtswobpsu|}aWuqX:24={ųúuel|z^b`Zbwu~rqs~{|vorxwpux}u[JefM5%,6nͻʵy}da^QUbrfhps^lpymvvxpq\Vc^e_n]T`hi^jebp{{nomfmwypbisqzbM`v_K136]͵÷w~yjqadl}zzr|{nv}u}vzd_pi||`JHhWH.*3Mũho`\^jphmyigrzmmqnnnlYefhgjpWZ\ma[hZbr}wvhmw{mm{}|hWiimkYGgkYD-7Esò{wppelq}z}{~|~rtrwr{v[[maxpVADdTK.,Pyñ}~rrgom~zrz~vr}tzq|vxz\QgVjnTB6YRP:1I`ürmbbcdxulzvqsss~z`Ze]iw{ntnirv}d^Yrp]l[_hxhhr|psrQ\fYveW@I`VU9D\xȲxqrfpiwm{zzzsyy~~r{u~tz_JdR^fNB0PPSB4Siȼxoeffaxxn{yssqnzzuZVd]m|uxniszhaZur_l\ciyhjuot~rM\cUo\S;BYXZT_}̽©os~k|wuoxpltyhYe]jv{~zqhxnxtlclkn|z|qsrzbxw}kZgQOTYR;>RdL?U_qʹ¦rl~ot~z|yktqvy|suzv{{uclruoS\OAGLN80B[Q:L[iĹκo{q{zrp~~lnuw_`daux~ylm|pqhfpjszywqumc~yv{d^aKLRYK8@[cGJ[f|Ŵmrn}{y~~x}yjto~xypxwu}xs~~^lynrjRYI=BKJ6.E`RCSbpͼq{u}xrq~zlktu`bcfyzzlp}srhiqlrz{yptka{vtyd\\GENUK7=[fOR`mƴosp~xyz|uvzjsn~xxnvwszxow]cxtjlkTSI7:GJ;-?`^NZiwŸĮrv||rrv|nhmwfafe{}ro~tylhrnnw~rp|u]p}totlYZH=FQN>7Um^Yfuʵxpv|~wxw~tq}|lro}xzouxrvylq]\qogfjVOG44CH@/>bfZao~Źȱuy~{qsuznfkviehirpwzlkspowrpzs\oxqmqlYVF8EQN@8Ypecj{ȴysw}sxu{ro}zospwwnvuqttkqzX^mifdhUMC05FGA0GigbfvǮx}tssx}wneouhigqnuyvlqrqp{~os}g_rqomqgWQ>9KOMGG:5[kddjǻ;qvs{uijwqljlsp}~qqtspuqqus\jnnmmo[SE7EOPE:[ohfg{ù}{{|qws}pn}ytrq|tutusqrnkn|oU`dcdafSH<-;EG@3Rhdcbw~ƻƫqwr|zlixtpkk}to}uuuustrrtv^ilnomr_SG9GOQI:Ykfc`uyü~z~ovr~}nm|xuoowtxyvvrtnnn|nWaaefciUI=0?FJB4Rda^[rvǭrws||mjxvslj{}tn}{{xxvuutt{aiknrmvdUJ;FOSN;Tieb[ntz~otq~|~qkxxvpk{zo}{ywuqsnmyw[_bchbl^LB3;FII5Iba`Wir̵svs|{}thtvuphu|}l{~}y{suv|jflktnvp[PA@OQVBFdgf\dswij~{~spps||yipwwukq}pw~}}wypwoon~jZb^gegkUH;3BFNB8Wcc]\qsĬsvt{}mluwwmkz~vp~{|xuzwtfjimsnxjVK>ENTT?Phgf]lt}z}opmux{tgquxtis}oz~|vxmz}omn|f[`\gbhiRE84BFPB=[egabuwêttt{}lmtxznm{yr{}xt{wsgifkqlwiTH=CMTVBQjllbpx~}xyomkrvxtfnryvjr}qx}}|twlsolizjZ^XcbcjVD82>EQI>Xiljfz}ư~ssq{x}qjrv~unzs~{|pxsxlfedpjroXI>?JQ[JLhptkq|ytrkimyuuvfjowzmp|vu{~}}svlmokeumY[U_a`jXD82=DQOAXlqqk}Ų}trpzw}rkrwyp|u||pxrxkfcbohrnWG=?JR\LPksxov~{wrpkgnxrutfknz{ms}txz|~~{rvho|nifvhZYS`_ciTA64?GUNF_nurq­{tps||x}nnr{utx|~wr|xp}{hg_gmjugPBBHS]OQdntjry{vqpkgpv|pwqgjk|wiowoy{{z}~ytsevwqfjsd^UUb`igP<4:?KVHK^hogov~¾}}xs|{|rttrw~yq~tutlc`nltu`I?BGQ]QO_jqimuz|{osmgpt}~rutijhx{jlrrt{x{{~~tvgo{sifsi`WR``gkXA58>GVMHWcmhisx|{szuurwuz|wv{r~zph^jnqxkRAAFK[XNXdomiru~qvyqijrv~wqxmjhn~rinxozw~x{wuoeyvqdkoe^QWabkfO:5FGSa]_cn{y}}y|juyrmcklmqnrsomfwtpr|wvvvms|qnadtyzsrf`ecbSJV\eg]D5<@IZYZ^hxx|~|u{}|}v}rrxosrrcWbgpsjR?DGM__^`gwxz|{}nqztqfjmkpoqupogpwpo}wvt|{mp}spdbpx{utkaedfXKU\eibL8!-X:Y_$QCSV`I;t;>E2C.ICRD$Ya0kqƲStyiyVordklbLFr^4E?FGGM9T]Cu~smhKjdjNE`TeBvpoHCQHmD6vstkliQO<|zw¶ѷ§϶|ucug[cMTL\hJqsnYhRTVA=JO7`jL;Q{kSN5[]F^4Rwej\P[{]c`adkubxYapm[Xpǫ{zg{fo|sqkKSmY[GNqQpmyz}k[hd`4`TeIWsI}˷שŨҥpgF@.!(3YE׽}{[\ZADRW9PZHLd]OY.B_9H-CbNdHuZY|^`CB=/chN.0ET1/JAB=BmikrSJWmmdum]dft]UCRMDKEanNfwzsdyyePBAM^CFi^Vgq{s{jv~_[a;XacUCfoָεݼѵھκàuuSV:49;W>>^k{~~yYw{]\r[fcFjiWE+aF^cKfIqwoiQ]Ym|tjpiXezvvytzpεśqzszzs^WCQC;WfkYP{||f_jcqbPjt`f~yvbxyZ}}~wuƱ~x}[wuw{c_]Ja[aWBqrn^`f{}fuqkwx^in_:PdKrZqr_xhph^z]gs`pcZQMjhbd`nlmsYlzhjtjl~|hTbT\mt|^zxlhpt`_pdEWls\g[Ack_JL|]vqhr}w{wrkQ`bWcnxcԼ{tj~rXeoL:F[RKV78QB(B9"H1GJ4Q[X:7GLhs]Uo{vwnst~xfup^vwryyqvwxmsbdhշȬyvfgs|duVdqdNKsO\iOgT^|{Znv}̡¤st{oq{vefstmW/&)$9B<%Whȩ~v~}{^nhL+7R5PN@UJSmqdZj[WnpkSGtiJZtcS^xyzdo}dAAB5PdjTRTJi|pbpcmymyuhkvfw}yy{ymx~xjhkahz}ljz{qns}wmqrcnjvjev|tt~}{znc[svlmxxsir{yzwqrtpjuxZUeh`DIRXWLEB=EQMABkigvvy}b`hwbQIGQdcUVTIFZR9SMAQRHU`\]d_[_[SMbvk`Xq|ysnjgpyuwɻĻ~xy}|}ʪtenfu~}pca^UVVH@^vfzv~zplow}sjeVIR^<@VDL]RO`hgmtoiaQIQgaQBG^\ULIB@LVYeumaota|{cx}xxmfu}oZdqreMQ[_YLHLGELF:DcQYt]szouzttunipzn]_jzj]XYanoedc`ipa}tiz|py|}tku}|xyytxujhz||xt}ux~u{sp{~~yv|hn}{bajpohgid__YQZvfkw}ydem}p]RPU_a[ZWQVkeQigXiqbbpxztrtfryrj^X_jt~}otxrxle[W\eg_NCUaeWGNW[XUROIGH@=Wrbú}wnzsz|~rzps~ory}{|{wupdevypou|yrnijwkkpl|uceouz|uogadyxlbt~xpjmsxynyovsl{~yummsxugRN]bbM>CJOONSURQI@HdgWy~puuve\[`fd`\Y]lqVVkZUfdVWbjoqokf\W`v{skex}uos{v{v{ýzy{xx~wuy|xm_`qxyhXW[`^\\[WSMHUwv½}~xngbdnkRXo`Zkl_`ku}}sh^ZbrocXQamojc[VX_ht{qgt~luwo|~{lZZfigSDELQSUY[XSICSoi]{yi~yttwyzmilzvibcgidacgp|wbppppnu}zwztflwzubY]fkmqw{xtlkž}yzvy}z~~z|widpvvjXUZaegjljf_Ya{pumou~vh]Y[_^ZXYaos`byqizvot|}uppvui`ivzwpiegoxum{ooo]cihb\VVXXQG?GW__OBBGMNPSTROJNf~~Ⱦĺxvv~yt~zhbelquxyvphcnxupqsrnkmv}hl~qix{kaemtwungcfqtido|ysnpv}zkk{{gm}makuxvqmmlh^QJQ[^ZJ?>DKOUYZWQMTivhs|wttqmjktmh}whsthfltyysliku|tmu~w{zmlry}ztt{yzyvtv}uw}yv|~xqmkg`VOTclndTOSY\\[XRKDH\sok|iumi{rkov~ytrwvhbkw{xrlikqwxoel~yk|xpzse`jsun\PNQVZ]^\UNKWkm\juovwwpmmkiimw{il~vhsxkhlswwtomq|zsy}|}wxuh_blqpdVRU[`eijfa]dy}y~~~~x~~siemuxrdYWZ_dgigb\Zfyzitzzy}tkfdcaafpzxij~|krwqsx{{wroqyxlaaiqtqmjkouxsgcs}oi{ydZ`dea[WSOG>9>KW\WLFGKOQRPKEAFZnlfý}~z{|uxyjbaejnpojdagyzs{wtqqsyxjssht~vidfikjfcdjt}{ribfqz}}zwwy{xlbgvsadup_\ckoomkgaXNINW]]TGAAEKPTUTQQ\oynk~wvzttwzywuv{~x|}|}tnr{xlefjosvwtonu||{zz~}||znfgow{|{|~{sxtq{g\^cec_\YTNIIQ]fhbVNLMOQRPMJLYn{uwwuvnov|}{yz|}yngn~|mssovxpgchpsrh\USUY\^^[Y\hy|oo{{|zvsrsw~pjyulw|pjijlkjjnvyplqz}smvyiqyjflswwurmf]VU\dhf]SOPU[`deccix~{wv||vnfackqsnd[WX[_acbabk{zqzz~xtqpptzwnyszwsrrqnkkotwsi_WW]diklmostpgbjwsa_liVJIMONLJHD@=@IU]^WMGEGIKLKJKRcx~wü}}uxx|{zrnqxzofabdgijhgis~x~{yy|tlwvio||rifeedccfmuzwpfadkrwxwwvvri`^hrj[`ol_VX]aba_[UNIJQX][SKFFHLQSTUXbr}xo{}~~~~~~~~xy~xqkkqx|{tkdbcfikjjjq}v|}}{ywwz~~uja^ahnqtuwxwrkhp}|lgsvhXRSUUSPMIDBDMXadaYQMLLLKJHIQ_nrkp}}~ypilytjtrmpw{}}zvpkgipvxulc][\^__^^bkx|skx{}wox}oo||smkiijknu|~wnhhmty|}}|{wogfpzteeswka^bfhhfb\VRRX`ef`XQNORTWXY]et}sw~z}utwyzyvrlgdflqtqia[XXZ[\\]akw{skv{y~zs|zx{vsqpopswxtkaXTV\adfhiihc^]gsqb[fkaQIGIIGDA=98:BNW\[UOJIHHHGGHO\jnhk¿yrs}uuxtw{~}{wrootzzrjfddeffgjq}wwsq|zmo{{smiggghlqwyvnfaadjnpqqomia[^hofXYfkbVRSVXXVSOKIJPW\]YRLIJKNQRW`pľñ~ngkosvusojhhghjg[POZaWKNZbb_\_cjq{ùtlpn`MB?>=;84/*().5::6.'"!"&+2>Qq@r]PĿzq^X\^WLB<867:?GOUXXXZaly~hYPJGA;61,)*07<=92-(%'*-19EYrЬv\{Δvq{Ľpmsvqg_ZWVVX[`ege`ZX[bjs{{uolnoqtxȾo^e׾v\PNRWZ]_abejmmi`X[fi]PPW[VKC=86557:==93-*,2:BHNSVYYZ^j{yŴymjsԽwpmi_UOWddXRYcgb[WTRRRTW[\ZTMFDEINRVXZZXVV[huwnlzŻƿ~gUHDGRcxĸredcVD>EKKE=841000232-%#'*++)''/;C=12?GB:57=BKOG;:DKG=63456644359AIORSQOPRUZ`fnyżĽvbPB99@Pd}zod[QH@81.19=5'"(.* + + + + + *177620///265/./0110//16@A?:4/+((()+/6=@<41;HKD?AISZ`dgkmnnmmot|ĺýyocUJDC?4'&6L\enwwne^[ZXTNE<4.($"!!%,363.3CSVRPV_gmoqqqpnljjnv¾x`SRQH=56?N`tui_XRNMOSVTKBCNVRHDHNUY[]^^][XTRSW]dkortvx{|zvdRD80'#!#*8Kb{vpljhaUIISXSIEHOUZ\]^^][WTQRU[bhlnoprux{|yts{ź|rg[OD;66:AIR\iwxpsrgXNMPSUUTSRPMHD@@CHNTXYZ[]_bdeb][anwskkt~~zwtpkfb_][WOE:0)(+3@Qe}ĺ}vpjc]WTTY`hnsuvvvvvuqj`Z]gkd[\fry{}}ocWME?<=EQ_jqz¼ti`[Y[]^]\YWUUUTQJB?EOSKBCLUYXVUVW[`fkqsrqonpuz}voib^[YTK>8AR_dj{µzuqnkjgbZRPXa`UNR[ac`]\\]`einqrpmkkmrx}q_UVWM>33:EQ^m}~xri^UU]aYMHLRTQKEA?>>?BDDB>:89=CJQW\_abcgp{yopyxsqruy}{wtpib\YWX\blzƷ}ma_fkjgdbbcehlopojd^ZYZ\]__^\ZXX^hqphfp~}}yodXMD@BJUbp¸|z{woe]WSPPPQRQLFA<:<>ADFFFDBAENW[TNR_gfb^_bfilnpsw}zpieb`]YVVX^hsſ¹}{xtnf^WSRRTUVUTQNKMS\_YQQ\dd^YXZ]_bcegjow~zrkd^YUPIEJZmx{~qeZRNMMNNNLJFCCGORK?;AGE=511369>AHSaq}wvxui\X^b]TLGFFFFEDCEHNSXZZWUSSTVY]cjrzzurxwlbZSOMMORUW[ajv}pf`\YWTQMLLNRVXWTPLJHHIKNSYaeb[YbormhjrzyphaZUNG>4*# "*4@M\jwtke`\XTQQRW\`b`\VPLHEDCDHOUXUQVdsvrqwĿxlc\VRNKHFEEIQat}wojeb`__adjnmf^`jrogcgnty|~~|{||slhd`\WPH@8216?DA@J^p{yrlhecceilh`XZdje]Y]dimprsttsqnmosy~wne\WVXUH;8@GHFFLUanz~{z{zui^]eidYTV[`cdefffeb_^_ciqw||uqqrn^MCCC;.##*2JXbfjt}{vnhiqvpfafmomhdbaabehkmljgfhlqw}||urqqrststuwz}}zsj_VLD=84259>?<=I]lruy{}qhipvvusrtvy~~~|{}|wsolihhhdYMHNSPHDHR]hr|uptyyvqmjhhilnppnjebbcgjmoqqpnnqy}wz}{yxxz{yre]_c`THDEGIKLNQTX\`fo{¹~{yxyzzzwqlhffhkmnoomkikqz}xpq{}xvwyz{{{|}~}}}~umnuuk_YYZZXUQNJFA=::>FQ^itʽ~{zxuqke`]]_behiihfddhorlb^ekjd][[^adfjmqx~|{z|~xrjaWNGDDGLQUY^cjr{¾ztnjkotqgadmplfbbdfhjklnrw~}umhdccb`]XTPNNNMKHHP_mrsyytsv{|tkjrwuoiffghiihhjnsx}~|yxwwxy{ywwxyxvrnid`]XSJA<@ILGBGTbkrxwxzqkhgggfeccdhlqssqnkihhijmqv|{try~xy~~~~|yumcYUXZTG>?DFECABEJQ[fq{zohecbcccceinuy|{xsokhfedfiouwtpsxuwvl]TTVVQKGDDFJPW^fkosy}{z{}{xvuuuvy}z|yzymhjmlg_YSNKIIIHGDA>=@ENWakt|ºzwusrrtw{~}voq{|uru{}}~yvz~~ztnieb`_][WRJB<98:=@DGKOSXap|xvuuwz|zrjksyumilqvz|~~|zxy}{~~wqlhecbb`]XRKEA@@AAA@?=:9;BMSROTbpwz||~|xustx~ysnkigd`[TME?;;?DJOV\dnz|yxz~|ww}}x{zvuuvwvurnid`adf^RKMQOG>743222247=EOYbinsx~z{xtx}}yusrrsuxz{zwtrrtwz}~~}yuqoqtrgZUYZUKB=;9887668;@EJLMLKLNSX`hs½wx~{xy~{pimrrkb\YWURPMIGGHJLMKGCA?@BEJQZepxz{ľ|~~~|yxy{}}{yzwxxromljheb_]]_`_\WPJDA>;;;=BGKJFFQ_fgho|~~yvtuvy{||{ywuuz}uu|ysponmljhfeegiigc]VQLHEB@?@BC>604=A>:;9;?DHLORV[ahntvx~y|zusrssrqppqsw{}|zvspnmmmnpswxskgkrrjcacfhhgec`\WQLIHIKNOOONNOQTVVTSYdqusu~|tpnopqrstvz|xtrpooquyzvpr{}y{~~{vqkd]WTSTWZ[[ZYYYYXWRLGIQWTNOWbkpuz½~}{z{|}}}}~}~}xsolkmnonlieb`][VPHBDJLG?>EMQSTVZ_fnw~}|}~yzzvx|}zvqnllnqrrpnkhgeb^WOIJOPI?;?DEDA??@CGMTZ`dgims{~{zyxy|ztu}wtv{~zxwx|}{zxtme^]aaYMGHKJE?:64346:=@BBCFKR\enwzvsrty~wqqttk_[\_^ZTPLKJJLNOPNKHFFIMRVZ^bdgjp{}wsv||uljorrnida^\[[ZYVRLFA>>?ABCDDDEFLVagffo~Ŀ~{{}~xrqu{zrkmrutplhfdcbbba]XQKFDCBBA@>;8447>DC>>FRXYY\bipw~zz}soquvsnjgeddefggd`\XVUVWWVTPLFA>@CA90.4::757;BJQX`hq{yx}}ywvvxz}}xtqoooomkgc]WRRTUOD=?CC=62124579=AGPYckqvz¿{vrpopqqpnlhd``cfe\SRWYUMGCA@?><;;<@EJORSTUX\agmu}~}~~zuqooopqqpnkgdcejjcZX\`\UMIGEC@=;878:=>>=:8678;>CJR[dghku¿}||}~{xvwz|~}zvspquund_bfc\URPPPPONNNOQTUTPKD?:642248>DGFFN\hlmqzº}}wpqvwqjda_^\ZXUTTUWYYWSOJGDBA@ABFIJFA?GORNLPXaipv|þ{z|vsrqqomkihikmmlid_ZVSQOMMMOPLE>>EHC<::668=CJPU[agnu|¾}|{zxwuuvy|~}zwtqpoooprtwungglokc]]_aa`^\ZWSNJFDDGJLNOOPRTWZ\]\^er{|{~{yxwvvx{|yskkqsnfaaceedb`]ZUPKGDDEFHHFEDCCCDCA>;>CEDCBCFKQX`gnsvy~ſ¿}yuqopqstsromkiheb\UPRVWOGFJMMKHFEEFILPSVWWWY]cjqxÿ{wsonoqstsqomkjifb\URVZWOHHLNLHDA>==>?AAA?<::<@EJOSX[^bfoz}|zvqicaef`VQSUURMIECBBCDFEDA><;=@CEHIJJJJMT\a`^bmx|~½~yuw{xofdghfb]YWUUUWXXVSOKIHHIJJJIGECBEKPOJIPZ__^`diouz}~wtw{{xtqnljjiiigc^XSPOOONMKIFB@@DIJD?AIOOLKMQUZ^bglrz¼ý~}xtvz|zvromlkkkkjfb\WTRRRQPNKHD@?AEE?76;?>954568:<:75457:=?@?>=>@BFJOV^fmonr}~}zwspoquunfdhkha[WUTRPMKIHHIKLKIEB?>==>@BGMQRPOT_ghfipy~|yuroosvskefklg`[XVUSQOLJIJKLMKGC?;987779=@B@<;AJNLJNU^elrw}Ŀ|snpsrlfbaaaaa```abdeda[VPJFB?<;<>@@<89@HHECFLRVY[]_`abbdgmu~~wspnljhecbbcdedb^ZVSQONMMNPQPJCCHLHB?@DGJLMOPQRRRTW\ckrx~½~|zxwwxyzxuqlheb_^\[\]^ZSLKPRLEAACEEEEDCBA><<<@EKPTX[_chmrvwxz}{yxxyz{zxtokhfdbaabdec\VV[^YRMNPRRRQPOMKGDBACFILOPQRTVY\]]\]cmtusv~zuqnkiijlnmhccimjc__aa`^[WSOJD?;99@BCDEGMTZZWZcmrsuyͿ}pf`^_grż|me]TJA93/-++++*'$ !%).5=GPYaglrx{l]QHBADLWetyhYMF@9-"  !###$&),05:BIPTTU\hprrv}xrk^L9.(! *;M^nyui]PE=61.--0476444+$+AZoĻ|xvuvvvvwwyz{{yurrwytmhilkhd`][ZZ[]^_^][[[^adhknoppqsx~{|zvrnllowŸ{wtpke_[XVVUUTSPMJGFILLFA@DHFA=<;<<===>?BFJMPQPPPQSUXZ^dinpomqz~wojfefjp{ȿsifbZPGA<9630-+))*+,,)&"!$$! (--**.5;AFKPTX\_bfkqzúrf`[RG?<>DNZgvth\RJD@=80(&*,)#!###" $*05:?DJPV[^`cju|}|~tkaWNE=72+##/;DLYj{vkb[VRNJFC@>=>>>;99>FJHFIPW[]^_adgkptxyzzyz|{tkaWMD<60*#$-8FS`p~{yvsokgddefgiihgfddejmkfcfkliecbcdddeefhkosuvvuuuvxy|~{wxyxsmga\WSPONNNOSZetʽ}zwtqnmmmlie_ZUQNKJIJKOQPMJMUXVRQTXZ\\\\[ZXVTTUY]cgkoqux|zoga_^`cjr}Ľzrqoi_WTTTSQOMKHEA>;99:<>?>>===>>><97:AED@AGNRTUWY\aekqvz~yrg[RQTTRRYdr}tld]WPIA95773+''*+*'%###$&(+,,+**,/38<@EHKNQV^gnoot~¿yskbYPHA;75324:HWciq~xqh_WQLIGEBA?<8657;><89?FIIHKNSX]aeimrw}xof^VOIB<4,$!)2=JVco||wsommnnnmkhfedddegimprplkouvroptx{}~~~}{{|yqhaa`ZPHA<8531249ALYgu|wsnjhhhijjhgfeddc`]YXZ`a]XY]accbbbbdfhkmnmlkjkmortuvvvvvy~|~wnhgiga\\ahqzº}vldabb_[WTRQQQRSSROLJIHIJJKKJHFDCFJLJEEJPSRQRUY]aehlpu{ÿ{ʻ}mnbMGD:120+))($#!!$(*'$%*14447;@EKQW^emvysmf_XQKHFFJQ\i{aT[Tptxz~ypllmmjiijnu¡d1]l(3Mevwnf^VMB86784.++.258;?BGKNQU[clwǼ˽~xpic^ZVMCAFNTX\birvxvm_K4 /^νztmf^WTQQQQQQQQTUVUSMC6*  9S_\PE>=@GPYahlmoqu{¾»xeSF?APj¶zreQ:'&2@JNJA6*  !&)+--.16;=:78=BCA?>?ACFHKOSX_ekptx{żxeTICFPbxƶ|jZNF>4( ++'6?KLTEJ2avU<0""-06\s|vme`_cmy|pcWMFBAFMWdq~zIAe<)2:CKS]jszqT;_cWl}Ʒ~rjcba^]ZVRMJGFGJOXgs}rhѽ|vyzlffimqssqmifd_XLA8238@LYervx޾}}uZ?,+>V^VJA?@@=977>L_updbhq{ɷuqȤj^\__VE2$$1BVjz}paP?.   #/;HS[`gq{gTE==GYrt]G73A^}{odYPGA=?EMV[]_`chnty}}zxvuvx{ƿzwxyyyune]WUW]fq}veTE=:=ADA:0&#/F]jmlkid_[\ahr|}{yvsnic\SI@70,,08@JXk}zy{~wqoryµƽyiXJ>634:FR[\WPLLPW_fmrstrolhaWI:03=CA==?CGMT[bjqxhREJ_u{vnmpxvkc\VQNMNOQSUXYYXWWX\bhmnkf`[VSPOOPU\frĺŽ|upoqstspia\YXYZWQG@F^||yvspoorttrkaUH?>CHF@979=BGKOQTWXZ[]^__`aabbdehlpuxvoijsxqifhjjigd`]]_ep|wqkgit~yvx}thf{~ysnhc^YUPLHC=86>Pftuogfq~|wsokhfdccb_XOKY}{skcYOF;2+&&)0;FQZ_`^ZTMGB=:8778:GOTVVUVX_gpvy|}}yvsponoqtxzysiaf}}wpjfcdglt~{~wlecdgjmnnmljhhimszÿyn^L<430&!-:FLLLWp{|wrnifdccb`\WRMJGEDCCCBBBBDDDEGIMS[dlqqkcbjsvuplnt{|wrlgb^[YZ\agpwyrkc]XTRQRTWZ[WNB;Da~{xvtsrpkcXQT]_[\cp~~zwussuw{~|thaj{uoib\UMF@:41/04:CNXaeeehrzzsnnprpljp{|vonptvz}}yuqonnoruuri]TYjywleda[WX[^behknruvvtojffglrx{zvohc_\[ZYXWWXY\_acdefhjjf`^dosnjkpw~zskc[TNIGGJPZeq{zv~y{{upmjijmqv|||yoe^_hrupg_VPWn{wsoligdb`^\[YXVUSQOMKIFDA>;976679;:76=MZ]ZZdw|ywvwwxwtqmifdb```abdhmsy}{shdq{n_SLIGFGHJMSY`gnsvwvuuw|vqpqrstux{}{qbVWg~uljnu~{wsolhda^\\]aejmonjfciu}upt|vqkd]UME>6.("")1:CLSYZWQKOdzvroljhfedba``_^^]]]]^^`cfjnqroiehpuphddfhijihgggilortttxzuqnkkkot{zqp¿wnjlnh^RJFDDEFIMT[bhlov¼ytokgdbabcfjlmljknog\]n}vng_XQKFB@??BEKQX^ejortutrpmkhgfffikmmjd_akstrtx{{zz~zupke`ZTOKGDBAABCC>6**ARUPMPZgu{wsolifdb_ZSMJNXaedfkt~|{yxwwwxz}}wpjhhhe^SJFFHJKKLLMORW]dksy~~zrke`]ZXXY[`fmswyumc]dx}zwtplhea]ZVRNJFB>;86568;AIS]gosvz|{|}~|{yyy{}~x}}voha\XUTTVZ_ekqw||wrmhc]WRMIECCDGKPSSL@1)4Plxtmmu~zuqnkhgggggfecbaaabcehkoswzzwrot|}xx{{xz~}vld_\YWTQMID?;7432246:>BFILNOQTZco|~w|~~zsorvungbbcfilorv{}vnf``gnqpnptx{zupq}{uoib\VQKE@:620..038?GR^it|~{tpv~zvrokgda^\[Z[]`cghgbYPMUhyzrnosz{utvwskd``chntz~|ywuttttvwxwtppu|~ytrsvz~}wvxytkaXSOLIFB?<9878:=??<723@Ysǿ|yuqmieb^\ZXXXYZ]_behkmnpqsux{wpq{}zxuspnkifca^[XVTRQPOOMIB:315860,-2:CMXbkrvwusu|{xvtssssttuwy}~vmmy}ulc^_ba\TNLMQV\ciqy~|zxvtrpnlkjjjjjifb__eovvoeZPLSfy~tkgfgjnprtuwxy{~|xtpkgb]XSMHB=941.--/27=CGJIFDI[u¾~}{ywvussrqqpqqrtvy{|yunkmsupicaaaba_]ZVRNJFB>;9778:>CIOUZ\[[[`fjgb^_fp|~|{zzyyywsnhegnsri]TSZdijils{zuqlheb_\ZWUTTUW[afkopmgabmz|{uoha[UOID@<:989GPX`jqsnlu|{zuqmknsvsld^ZYYZ[]^`bdfilnprtuvvvtsqomkjiikmptw{|ztka_gz||}tlgecba_\YUQMIDA=:86789<==;9;ER\_`chnuz~|yz}yy|}xtpmjhgggiknquy~~{ywtqnkhda]YUQLIEA><:99;=?BCC@8.#%7JUUOKKMRW_jy~wngccec]SIB=:875443469=CKT^irz}}xtvzsonrvvrmhfhkoqroicadhf_VOLKKKKJIHHGGHIKNQUX]`cfgggfeefintz|{|ȿ}xtplieb_][[\]_`a_\[_hsz}~}zwtqnjfb^ZVROMLKLMPTX]begd]TLKTetytjb]]`chknoppomljigfc_ZTOMPW^ba`_aeimqsvx}}{xvtsqomic\VSU[adca`aa_[SMMVgw}wqpsz¿~}|{{{}vpqx}tmiikoqsttrpmjfb^ZUQLGB=940+'$! #'-3:BJRZagjlkhhm~¼|uppsuqjb]\[\\\[[ZZZZ\^adinsx|~~|yxz~~xqljhhhijklnqtwz}|xqhacn|z||wrmhc_ZWSPNLKIGD@:523:BINPSW[_bflv}zywwwxz}{{xy{}}wroortrmf_[XWVTSQNJFB>951-)&#! !"$'*-//,& "0EW`a``cjs|ü}yuqmiea\XTOKGDA><;:;CEFGJOV]djptwz{{{{|~{wuuux{Ŀ}yvsoljhfeeegimqw|}zwtrqponljf`YRLJKMMJE@<;=@EJPUZ]_]XPGBFQ_gg_VOLKMPTX[^`cefhjmorux{{wrniea\XTPMIGEDDEGJNRVY[YWTT[k}{ywvvvvvvusnhcaced_YSONNNNNNMLKIFA:3-))+-,(# !$).39?AA?;3) $5DPX[ZZ\es~}vpor|¾¼~tkeceinsvvtng^UMHC?==>@CGLQV[`ejpuzwnhhq}|}~}xrkc\SLE>95310.,($"$)045447>>>>>?AEN[k|{vrnligggghigc]VQR[htyzyz}~||{|||{{zyxuspnkjiggghjlnprrpkc[UU^lzyvuwz|~}|{{~~vme^YUSQPOOOPRUX\aglqv{}wne\VSRTW\bipw~tmnuukebcfkqw~|wtromjfb]WRLF@:5/)$  '3@LV^cdb_`hx~}~~~|xqkd``bba]WRNMMNPSVXZ\^`bdfhkmptx}}wndZSOMLJD<5/,+-/38=CIPW]begghmt||xvvvwx{~{vqmkiggghjmosw{|yuqlgb\WQKFA<72.*'$!#'+046751+$"'4ET[\ZXZ_gq}|wsniea^ZWSNHA:2,)*-021//28@KWcp{Ľ{yvtrqponljfb]Z[^bdaZQHDFIKJGDBBDFIKMOQRUVXZ\^_aabaa_^\[\]`djpw}þ~zvspmkihgghjlnprrpnmou|~}}wtssrng_WQLIGEDBA@>>===>@BEHLOQQOJC;6671-+,+)#  + !%'(&# %0@Q]bca_`bglry|yuqmifb_\ZWUSRQQQRTVY]bhov}yuqmifc`][ZYWVURNIDBCHMPPNMNOQRSUX]djnomjfcaacehmrx}}zwutuvy}|wsqpooppqrtvx{}}zwsplhd`[WTPLIFCA?=<;9741-*()-3:>@@?=;73.*+2>KUYWRMJIKOU[ahnuzĿ|xtpmifda`^^^_`cfimpsttqmigjt~zvsomkigda\VOHDCCC@;4-($"! #*.000/137:62-)%   )4AKRTQMJLTbs{xvuttsrqponlkjihgggghhiklnqsvz~}yurpmjgd`\YUQMJGECAAAAAA@>;879>EJKJIHJLORTVWXXYZ]aekqx~zslhfhknoonnorv}{wsokgc_ZVRNJFC?<96310///0247:=@BBA=8426@MW\ZUPPSZbioty~yrjb\WTRPOONNNNOPRUX\afjnpqplheekx}|||ytng`\YXXYYYXUPIC?=>@>:3,'$#""##$$$%%%&'()+,-/01233334468;8530-+)('&'')+.1479;:850,,0;IWbggfeehmsz|yuqmiea]YUROLJHGFFFFGGGGFEEHMU^hpvy{{{ywttw}xtokgc_\YVTSRRSSSROJC:2,,06;<93-)'),16CHMRW]bgmqv{~ſ}}~}zuojfddddeffgggffffeedddddccddefghjlmopqppooquz~zqg^YZaku{|ytnifeefhjlmnooonmkjheca^\ZWUSQPONNMNNPRTW[`dioswxxwtrqrtuvx{}|vpligeb`]ZWTPMJGDB?<961,' !(.5:?ABA@@CKXfrz}{xuttw{}zxuqnjgc_\YVSQNLJIHHGHHJKNPTX\bgmsy}yuqliea^[XVTRQPONKHD?<:>=;97541.*((,3;ACA?<<=@EKQY`gntz~{xvsomjgeb`_^]]^_acfiloqrrplhdcfnzý|ytng_XTRRROJC<62/-,+*))('&%%%%&')+.147:=>??>==?DJQW]bfls{~|zzz{{yuojgffilosvxz{||||{{yxwutrpnlkigedba````abcefhijifb\VPNPVakqsnf\SLIIMSY]``_^^^_`bdfhiklmnopqrstuwxz{}~}{yyz|~yvx¿~|woha\XURPMJGC?;72.*%!  "+4=FNTY[YUQNNS_m|xrmigfhjmmlhd_ZWUUUUWXZ[]_`bdfhkmqtx|}}zyyzyvqkd^YURQPPPPQSTVXZ\\\ZXVUW[afhhec`__`abcdefhjmquz~~}|}}wtsssssrpnlhea]XTOKFB=951.*(%#!  !#%(*,..-+'!#0>LV[\\ZZZ\^bgox~zuoib]YWXY[[YVSPOORV[ahou|}~xqkhfghjifaZQH@:669<>=:51.--.0235789;<=>?@AAABAAA@@?@ACFKQXajuÿ}yusux}}}}~}|{{|{vqligedcbbaa`__^^^_`abdeffec^XPHB?AGPX\\WPIC@>?ADHMRW\`fjosvy{{{zzz|~|{yxxwxxyyzzzyxwvwz~|sjcbfo{|unifc`\XTPLHC>941.,+)('%$" !%).4:AGMSX\__^[WSSV_m}¿xrmigfffeeeeeeefgghijjihfca_`bfkpsvvvvxz}~zy}|wtssstvwxyyxxwutrpmkifdba_^]]]]]]]\[ZYYY[`eknpnjfccehkkigeegjntz|zxwvuuvvxy|~{xvutsrokf`YSNKIHGEA;4-'""$'*.158:::741./4>KXbikjhfefhlpu{{wtplhda]ZWTQPNMMMMOQTX\afkptx{}~~yuplhd`]ZWUTRQPOLIFB?>>ABB?:40./49?CFGGFFFFGIJKLMNOPQTW[`ekqw}~}{ywusqomljhfc`\WRNIGFGJMPQQPNMLKJHEA<8559@JRWXVQMIFEGIMQV[`ejotx|{xuqnligecbaaabcdfiknoqqpnkhfgks~~ytmf^XSPONLHB;50,*)))****)'%!"%'((()+.27CFHGFDCBDIS_n{zurqppppoomljhfdb`^\ZYXWWWWXY[]_bfjnsw|~|~}{xurokhea^[YWUTSRQPOLIEA?>?CGIJJHFEEGILNQTVXZ[]^_`abcdfint{~zwsokfb]YTPLHD@=:7532112357:=@BDDB>92*%#&.9EOTVTPKHGIMRYajrz~xrmiecba``__``abcehjmpsvxyzyvrnjhkq{}{zyywurnhaZTPONNLHB;5/,)(''''''''''''()*+-/1368;>AEHKNQSWZ_foz}|}}}}|{ywusqpopqrtuwxyyyyxwvutrqonlkjjiiijjklmooonlhb[TMHGIPYafhfa\WTQPOONNNORV[afikkjiggghjmpsvx{~}zwvwy}{wvy{wvtrnhb\WTQOMLIGDA=951-)%!  &,3:BIOUY\]\ZXX[airz~ywuuttrpmiea^]]_abca_[XVTTUWZ^bfjoty~|{~~yvuuvwy{|~~zupjd`][\]^^\ZWTRQQRUWZ[\\[[[^bgkmlifb_\[[\]^`cfimptx{~~}{zyzz|}zz{|{zvqlfa]YVSPMKHEB?<9630.+*)(''()+-/134430,(# $-8EPX\]]]]_ciov|~|zwurpnljigfeca^[XTRPQSX]cinqtuwxyz{zyxww{|zxvtqnjfa[VPLGDBA@A@>:5/*'%&(+/37;>ACFGHJJJKJJIIHHHHIKNRW^emu~|{}~{xvvvxz}}yuqmkkklmlifa\XUSRSTVXZ]_bdeeda]WQIC??BHPVZYVPJD@>=>@CGKOTY]bgkpuy~|zwusqpnmmlmnoqtwz~yslhgks~}wqlighhfc\TJA93.,,-.//,)$  $'+/49>CHNTY^bfhihfc`^^bkwÿ{upkhfeedddccbbaaa`aabcdfgikmnooonnoqtzztnklpx|wspoooopqqrrrqpomljhfdb`^\[YYXXYZ[^`dhlosuvusnjgfglsz~}{yutuy|wvsrlfhhikkmorux|~~{wsqos||{zywvsnnptxz{yrfYOFCHQZ]YJ5!(5:8-%19@DFFEGD@;6448?JVajorstuwz~ƺɻ~yywsqnmkjiihfda^YTOKHJO[l{yoknw~zuplgc_[YVUUVXZ]`bdd`ZSJB;63111248=CJOSUTROKHDA?=;;;96420.,*('%$"!  "$'*-049?EJPUZais}ÿ{unf_XSPNMNPRTVY\^`begiklmrz{wwvvw|}~}{{z{{x|{umd]TLGEEHNV`hnqoi_SG>88?BIR\ekmlieccfjpv{~~{xussv|¹ÿ}ywvwwxyxxwuqnjgffhlptxzzxvrnjfb]YXXY]biqz}ti]OB81./5FLQSRPLGB=975567;BFJNRUX[]^]][XUROLJJLPXcpxw}yttx|mb]`iuxngdflry{tlda]YWUSPNKHFDCCCCCA?<84357=FQ\flonkf_XRLFA>==@ELU_ju}{z{~zpha]\_emv}zslfehow|vsrtwz}}pdXOJHJMQTVVUTQOKGA:2*! + + + + ").49=BGKORUWY[\^`dkuxpic^YVTSSTVX\`dimqtuwwvtromkiihikmosux{}}||||||{zwrokfb]YVTSSUX\aglorrqnjea^\[]`ejotx||vqnjjloruxy{{{{{{{zzyyxxyzz{}~{sompu}ylaWPJGGHJMQTVWVTQLF@81)!  $*.2579;<<<;;=@ENXclswxvsokhffhjot{}|{zywvsplhd`[WSPMKJJKMPUZ`ekqvz}~|zxusqppsy}{ywuspnjf`ZRJB:40../1111236:@FLRX[^__^[YWVVWZ]`cegijmpty~ÿĿ~|zyvolljje[[ZYXMIIFFC=;;99631-*% "1@JRY\]]\\\\]^]^^^__aeins~||}m_X[fuvka^`ht{qigdcfkrx~õrf\URSVZ^^[XTNGA:3-(#  #(-38=EKNQQQOKF?7/'!"*8J]o{}uja^bn|tponnnnmmnqtx{}|{vqlfa_]]^afjnruvwvuspnlkjklptuwwvtoibYPHA>>CKWcow~ysle^YTRRU[ckswxtlcZTRU]ht{yxy{~{zzz}ysomosy~zwuuuxz~|z||n`SIA=;;<>?@@?=:73/,(%$$##$%'*-059>CHNSW[]^][TLD;4/.29DP\fnswy}zrkea__bflqvz{yuoh`WPJFEGLT^it|vtsv{{uojgefhlqv|}ulbXPIDAABEJPV[_cegjloswz{zwqkc\VOJFA=;97567;?FLT]env}zvvzýxrooquy}~|yvrmga[TMF?93.)%"  "%)-26;>ABA>92*#!)2<:986531/-,)'%#!  !"$&)+/3782.+*)*+,-.///.-,+++-/36:=?@AAAABCEHKNQTVXYYZYZ[_fo{{yxxzzzxvspnlkkllmnooooonlkjhfecbbabcdfilosw{|tld]YWY_fnuz|zvpje`][[]_behloqtuvwwvvusrpomlkjiijjklnpqsuuvusqnljikmpsuvutrqppqsw|~xqjd_[VSOLHDA=951.+(&$"!!!!"##$%&&&'()+.159<>?>;9769>FPZcjoqrsrstwz~}yuqnljiiiijkllmmmllkjiihhhhijlmprux{~}}~|ywurpnljigec`]YUQLHEDDEHJKKJHEB@>=>?@BEHKMPSVXZ\^_acfjnu||yxvvvvwxyyyxvtroliea]YUQMIFDB@@?@ABDFGIJJIGD@;5/*'')-39?DHJKLMMNOPSVZ_ekrx~}{{{|~~{xtqnljjklnpsux{~}yurpptz{uoib[VROMMLKHD?93.*'$#""""""""""""#$%')+.159=BFKOSW[^`cehknqux|¿~|yuqmjhgghhiiihhhhhijkmoqrtuvwxyyzz{{|}}~~~|yuqke`[WVX\bhnstsplhc_[XVUSSRRSVY]bglqtuvvutssttvxz}|yvvw{}sojgcgnpwƼxj\PD;74336:;<<==?BEHJJIFC@>=<=>ADHMT]cjotx|}}|yvronmosy}vrsx~~woh`YSNJGEDEEEFFGGGFBA@@?><;9752/,($(3AN[fnqrokf`[XUUWZ_emt{}yvvwy}wne]XVX]ckrxzzvqjc^ZZ]bipx~|pdWKA942357:;:974323469;741.+)('(),.13679:;<=>?@ABBCCCDGJOU\dkqvyzzyywvuuvwxz|~ſ|xusqomkjhgeca_^\ZYXWVVVWXY[^adhlquz~|zxvtrpnmkifda]ZXVUTTSRPMIE@<840-*('&&')+.1468;=?BEIMRX^elt{~{ywusqpnljgeb`^\ZXWVUUVWXZ\^`bdefghggfeca_][YWVVVWYZ[ZYXUTRQPPQSUX[^behkoqtwy|~~}{zyxxwvvuuuvvwwyz{|}~~~}|{{{|~~xsnjfa]XSMF@:50,(%" #(-28=BFJMPSUWY[^adiow{wsqomllklllllllllllmmmnopqrsuwy{}}zxvtrpnljihgfeeeeffgghhiijkklmnoqrsuvwxxyxxwvuuuvxz|~~}}}}}}}}|||{{||~zupkfb^YUQMHD@;72.+'# !$(+/37:=?ABCDFHKOTZahnswyzzzzz{}~|zxvtqnkheca_]]\\\]^_acegjlorux{~~|zxvspmjhfdba`__^]\[ZXVTQONMLLLLKJIGEDBA@???@@ABCDEGIKMPSVZ^beimqux|}|zywvusrpnljheca_][YWUTRQPOONNMMMLKJIHFEDDDEHKPTY]_abaa````bdgjnqtwy{}~{xtqmjhfedddeeffffgghiklnprsuvvwwwxy{}}zvrmhc^YURONLJGEB>;86420/..----,,,,,--./012469<;;;;;<=>?ABDFILOTY^hoty}~}ytmfa_`gq}{xvtstw{}zxxz~}zyxxyz{}{yvtqoljgd`]YUSQPQSW\bilnnke^VMFBAEKS\ekmlib\VQMMOSZahouz~}zxxy{~|xusstw{ztmifefimquxzzywtpkfa\XTPMJHFECBBA@@@@@@AABDFHJMPSW[_dhmrw{{yvsrrrsvy}{yy{~}{{{{}~zvspmieb^ZWTQOMLKKLNPRVZ^behkllkjgc_[WSPNMLMNQSTVWY[]^acehkmpsvy|~}|{|}zvtrqpooopqstvxz{}~|{zxwusrpnmkjhggffghijlnqrstuvwwxxwwusqnkgeb`_`adhmsz}zywvutsssrrrrrqpponmlkjigfedcbbaaaaabddfghijkklllllllkkigda]XTOLIGGGHJMQUY^bfknruxz|~}{zyxxxyz||xtqmjgeb`^[YWVTRQPONNNNNNOOPPQRSTVWZ\^`bdfhjmoqtvy{~}zwusrrrrrstvwxz|~~|{{zzzzyxvsqmjgc`^[YXVUUTTSSSSSSSTTUVWYZ\^`bdfiklnprstuvwxxxxyz{}~~|{ywvtrqonlkihgedcbaa``aabbcdefgijlmnpqrrssttuuvwyz{|||{zywusqponmmnopqsuwy{}~}|{{{zzzzzyyyxxxxxxyyz{|}~~|zxvtrqonmlkjihgfedcbaaaaabcdefhijkmnopqrstuuvvwwxxyzzzzzzyyxxxxxxxxxyz{|}~}{ywusqpnmkjihggfeeddcccbbbaa``_^]\[ZXWVUUUUUVXYZ[\]]\\\[[[ZZZ[[\]^_`abcdefghhiklmoprtvx{}~}|{zzyyxxwvvutsrrqqqrrrsstuuvwwxxyyzz{||}~~~~~}}}}}}|||{{zyyxwwwvwwwwwwwwwvvvvvvvvvvvvuvvvvvvvvvvuutttsssrrrrqqponmlkihfedccbbbbbba`_^\[YXWVVUUVVVWXYYZ[\^_`abcdeghjlmoqsuwyz|}}{ywusrpomlkihfecb`_^]\[[ZZZZZZ[[[\\]]^^^__`____^^^_`abdfgiklmnopqrstuvxyz{|}~~~}}}}}||{{zyyxwwwvvvvvwwwwxxyyzz{{||}}~~~~~}}}||||||||||||}}||||{{|||}~~~}}|||{{{zzzzyyyyxxwwvuuttsssrrrrqqppoonnmmmmllkkjjihhgfffeeeddccbbaa``___^^]]]\\[[[[[\]^_`abbbbbbbbbbbccdeefhijlmoprtuwyz|~~}}{zzyxxwwvvvuutsrqqppooooppppppoooonnnnnnooppqrstuuvwxyyzzzzzzzzz{{||}~~~~~}}|||{{{|||||||}}}}}}}}~~~~~}}}|||{{{{{zzzzyyyxxxxxxxxwwwvvvuuuuuttuuuuuuuuuvvvvvvvwwwwwwwwwwwwwwwvvvuutttssttuvwwxxxxxxxxxxxxxyyzz{||}~~~~}}|{{zyyxxwvuutsrqponlkjjihggfeddcbaa`````___```````````aabccdeeffgghhhhhhhhhhhhiijlmnprsuvwxxyyzzz{{{||}}~~~~}}}||||||||||||||||}}}}}}~~~~~~~}}|||}}}~~~}}||{zzzyyyyyyyyyzzzzzzzzz{{{{{{{{||{{{{{zzzyyyxxwwwvvvvvuuttsrqqpooonnmmllkkkjjjjiiiiiiijjjjkkkllllmmmmnnnoopqrstuvxyz{{||}}~~~~}||{{zyxwvvutsrqpoonnmlllkkjjiihhggfffffgggggggggggghhiijjkllmmnoopqqrstuvwxz{|}~~~~~~~~~~~~~~~~}||{{zzzzz{{{{{{{{{zzzzzz{{{{||}}~~~}}|||{{||||}}}||{{zzyyyxxxxxxyyyyyyyyyyyxxxwwwwvvvvuuuttsrrqpoonmmllllkkkjjjiiihhhhhhhhiiiijjjkkllmnnoppqrsstuuvvwwxxyyyz{|}~}|{zzyxwwvuuttttsssssssssssssssssssssssssttssssssstttuuvvwwwxxyzz{{|}}~~~~~~~~~}}}|||||}}}}}}}}}}~~~~~~~~~~~~}~~~~~~}}}||{{{zzzzzzzzzzzzzzzzzzzzz{{{{{|||||}}}}}}}}}|}}}}~~~~}||{{{{{{{{{{{zzyxwwvuttsssrrrrrqqqqqqqppppppooooooooooooooooooooooooppppppppqqqqqrrrsssttuuvwwxyz{|}}~~~~~}}||{{zzyyyyyyzzz{{{||{{{{{{zzzzzz{{{{{{{{{|||}}~~~~}}}}}~~~~~~}}}|||{{{{{{{zzzzzzzyyyyyxxxxxxxwwwwwwwvvvuuuttttttttuuuuuuuuuuvvvwwxxxyyzz{{||}}~~~~~}}|{{zyxxwvvuuuuuuuutttssrrqqppooooooooopppqqqrrrsssttttuuuuvvvvwwwxxxyyyyzz{{|}}}~~~~}}||{{{zzzzzzzzzzzzzzzzzzzz{{{|||}}}}~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{{{{{{{{zzzyyxxwwvvuuuutttttttssssssssssssssttttuuuuvvvvvvwwwwwxxxyyyzzz{{{||}}~~~~~~}}}||{{{zzzyyyyyxxxxxwwwvvuuutttttttuuuvvwwwwwwvvvvvvvvvwwwwxxyyzz{||}}~~~~~~~}}}}}|||||{{{{{{{{{{{{{{{{{{{{{{{||||||}}}}~~~~~~~~}}}}}}}||||||{{{{{{{{{{zzzzzyyyxxxwwvvvuuuuuuvvvvvvuuuuttttttttuuuuuvvvvwwwwxxxxxyyyyyzzz{{||}}}~~~~~~}}}|||{{{{zzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{zzzz{{{||}~~~~~~~}}}|||||||||||||||{{{{zzzzyyyyyyyyyyyyyzzzzzz{{{{{|||||}}}}}}}}}}}}~~~~~~~~~~~}}}|||{{{zzzzyyyyxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwvvvwww|}~~|zvsqliiiikquw{|yqllnvumlr}wnkns{xuutttuuwy{~}vpljmqx{snmot{xqljjlqv}{smhhks}|phhp|xnhioyxuqmjihhijklmmmnmmlkjjihijlnquz~{uokiimry{snkijnsy~~zwtrqponooooooooopqrtvy|wplkowypklrzysnkklosw{|yurolllnquy}vqmllnrx~zupmjihhhikmoqtvxz|}~~zwvsromkihhilotz}tnjils{xqljkmqw|ztpmjiijlpuz|wsomllmorv{{vrnlklotz}wqmlnrw~~zwtrponmmmmmmmmmmmmnnopqrsuwz}{wsolkkkmptx|~{xvsqpnmmlklmlmmmmnopqsuwy||vqmkjlotzytpljiijkmpsvy|~}{ytrpnmkjjjklnpsvz~|yvsqonnnnopqstvxz|}}|zxvurpomllllnpsw{{vronmnqtx||yvsqomlllmnpsux{~zvsomkjjlnquz~{xtqomllkjjjjkkllmnnoooooooonmmlllkkkkkllmnpqsvx{}}{ywusrponnmmmmllmmmmmmmmmmnnnnnmmmmmnnopqsuwz}~{wurqpopqrtvy|~{ywusqonmllllmnoqsuwy{~|zwtrpnmkkkkmnpsuy|}{ywvusrqppoonnnnnnnnnnoooppqrstuvxyz|~~}{zyxwvuttssssrrrrrrrrssttuvvxyz{}}{xvtrponnnnopqsuvxz{}~}{zywvusrqponnmmmmnnopqrstuvxyz{|}~}{zywutrqonmllkkllmnprsuxz|~}}||{{{{{{{{{||}}~~~~~~|zxvtsrpoonnnnnoopqqrstuvwxyyz{||}}}}}~~~~~~~}}}}||{{zyyxwvvutsrrqppoonnnnnooopppqqrrssttuuvvwwxxyyzzzzzzzzzzzyyyxxwwvuutsrrqponnmmmmlmmmnopqrsuvxz|~}{zxwutsrqpoooooooooopppqqrrrrssstttuuuuvvvvvvvvvvvuuuuutttsssrrqqpppoooonnnnnnnooooooopppqqqrrrrrrssssssssssssrrrrqqqppooonnnnmmmnnnoopqrstuvwxy{|}~~}|{zxwvuttsrrqqppppppoopppppppqqqqqrrrrrrrrrrrrrrrrrrrrqqqqqpppoooooonnnoooooooooooooooooooooooppppppppppppppooooooooonnnnoooooopppqrrsstuvvwxyz{|}~~}|{zyxxwvvuutttsssrrrqqqqqqqppqqqqqqqqqqqqqqqqqqqqqqqqqqppppppppppppooooooooooooooooooooooooooopppppppppppppppppppppppoooooppppqqrrssttuuvwwxyyz{||}~~}}|{zzyxxwvvuuttssrrrrqqqqqqppppppppppppppppppppppppppppppppppppppppppppppppppooooooooooppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrsssttuuvvwwxyyz{{|}}~~~}}|{{zzyyxwwvvuutttsssrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppppppqqqqqqqqqqqqqqqqqqpppppqqqqqqrrrssttuuuvvwwxxyyzz{{|}}~~~}||{{zyyxxwwvvvuuuttttsssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssrrrrrrrrrrrrrqrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssttttuuuvvvwwxxyyzz{||}}~~~}}}||{{zzyyxxxwwwvvvvuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssstttttttttttttttttttttuuuuvvvvwwwwxxyyyzz{{||}}~~~~~}}||{{zzyyyxxxxwwwwwwwvvvvvwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvwwwwwwxxxxyyyyzzz{{|||}}~~~~~~}}}||{{{zzzzyyyyxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxyyyyyyyyyzzzz{{{{|||}}}~~~~~}}}||||{{{{zzzzzyyyyyyyyxxxxxxxxxxxxxxxxwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyzzzzz{{{||||}}}~~~~~~}}}||||{{{{{zzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzz{{{{{||||}}}}~~~~~~~}}}}}||||||{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{|||||}}}}~~~~~~~~~}}}}}|||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{|||||||}}}}}}~~~~~~~~~~~}}}}}}}}}|||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}|||||||||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}rlkjikqtvyz}wtokijlpv}yqljjnszysokijkmovz}~zuqmjikpwskjp}sjjsvmiimu}~}}}ysnkikou|~vokikov~~wqljiknsx~~wqljjmt}zpjkr}umjkrz{wspmlkjjjjjjjjjjjkkmqtwz~xsnkjlpv|xrnkklpty|zwusqssrrqrrstuwy|~vpkjlrzyqkjnt|{uoljjkmpsvy|{|~~}zxvsqomkjjkmosx|ytokjjlouz}ytrpmkjjjklmoprstuvvwwvvuqpomlkkjjlmpty~{tnkkns{|uplkkmqu{|wromkkkmpty~zuqnlkklnptw{|xtpmlklnrw}}wqmkknrx~~{xvtrponnmmmmlmmmnnprsuvy{}}yuqnlkkkmpsw{~|zxwvttsstttttuvwxy{}~ytpmkkmpuz|wspmlklmnpruwy{~}|zxvtqpnmlllmnprux|{wtronmllmnoprtvxz|~}{ywurpommlmnqtx|zuqnmmnquy~~}|{zzyyyyzz{|}~|yvspnmmmnprux|~}|{{zzyyzzz{|}~zvrpnnoquy~}yurponnoprsuxy{}|uga^\Y[_`dgkz}|ztpjfea`achmu~zsmjknrx~|smgdb`bglry|yvtsrqpppqtyxpou{mfhrunmov{xvyxxxz|}zy{}tmhgimt}|xuuvy~}sjc]Z[`itvnjmvumjlrzzvqkgca``bcfhknpsuwxyzz{}~~|vqkgdcdgmt|ysnkjknptx}{xtpmjhhikou{zustyulggls||xvvwy{~|zxvtrponmmlllmnorux}ysmihhjnsy}zxvtqomkihggghjmptw{||}|{yvroljijlosy~|vpljkpw|tnjhghkosw{~|wrnieba`acfkpv|zupljgffgikmpsvz~}{ywwwy|{tokjknsy|yvspmkhfdcbaabcgjmpty}{vrolkjkmosw|{vrnkighjknqtx|}yupmjhhilpv|}wrmifdcbcdegjmpsuw{~~zvrokhecbaaacehkotx}{vrokifdcbbcdegjmquy}~zwsqommmmmnpqrtvx{~~xtomllmpty~~{ywvttttvwy|~zvspnmlmnorux||ywutuvxz}~{wtqnlkjlnqv{}xsoligfeefgjnrw{ynkea]WUSRQS\]^`bpxy}xqid`[YXY[^djqw|zvsqpquyyrlg`\[[\^aeimpwxoga\Z[`iu|z|{ndacir|}vmieccejr{||xrlihimsz~}||wtsv{yl`VRV_lz}tlijq{|tnhc_[YWUTSSSSTVX[_chmsyvme^ZXX[`gnv|}xtrqqruy~|xtqmnnlkklmoqtwz||xtqpsy~xwz~voifefimpu{}vnjea^]\^`djpx~{zz|}|zyyyyyz{{|}}}}|{ytqolifdbaaabehkptzuka[WW[bku}}xspnorvz~}}}~ztnieb``adglpuz|yvtrqpppqrsuwx{{|{tnhecbdhmu}|xutuy}~}|||}}~~~}|{yxvtrponnnoqtwz|zyxxyz|~~|zwuspnljhgghilotyyqic^[Z\`elsz|yvsrqpppqqpqqqrrrrssstuvwxz|}~|zwurpnljiiikloqux{~|yvsqnljihggghikloqsuwz|~~xsnjfcbbdglryzvsrrsvx|~~~|zyxwwxyz|~}{yxvuuuvx{~xqkfb`_`cglrx~}zwusqppoooooooooopppqrstuvwyz|}~~~~}{zxvtrponmmmnoqsvx{}{tsponc`aabbadfijs||~wpqpjgccjklorw||uokiikqx}zxwvwxyz{{~}~}|yz{xwuuwy~tlltrgdl{~z{~{yxxxxz}}vqmlmoruxzzxuqmjikou}wne`\[]`ekosy~yqib\Z[`juwpot~vkdcfmu|{vqkgdbaaacdfhjlmopqrrssrrrqqqqstwy|~xqljiknsz}{ywusrqqqqrsuy|ysqsxwohgiq|~}|}~~~|{yyxxuuvwyz|}~zvrnkihijmqv|~ytnjfca_^^_`bdfhjloqssrsttttsqomkhfdbbbdgkqw~}wrnnqv}{rjc_]^adinsx||vsqonnnpsvy~~zvsqppqrux{}zxvwx{{uokijnsz|xvsqomkihgggghiknquy~ytpliggghjlosvz}~|yvtqonkklkkkkkllmnoqrsuvwy{|~~zvsomllnrw|xrkfa^\[[]_bdgjnqux{~{wsojgda`__adhlrw}}{zxwvvvvvvwxy{|}~}|zyxwvuttssttvwy{~}xtpmllmpsx}~xsnkhfeefhjloqsuwy{|}~~{xuromkjjjlnqtx}zvsokhfdcbbbbcefgijlmopqrrrrrqonkigeba`_`acfjotz~|{zyxwwvvvuuuutttsrrqppoonnnoppqstvy}{xurpooopqtwz}{vrnjgeddefgikmoqstuuvvwwxxyz{{}~|ywtqomkihhhijknpsvy}|zwvtrqpoooooppqqrrssttttsrqponlkkjklnpsvz~~~}||{zzyyxxxxxxxyz|}}zxvtsrqqrrsuvxz{}}yuqmjhedddefhjlnoqrsssssrrrrrrssstuvwxxyz{{|}}}}~~~}|zxwuspnlkihgggggijlmpruwz}~~~~}}}~~~~~}|{zyxwwwwxy{~~{yxwutttsttuvwxyz{|}~|xuroljhgfgghjkmoprssttttssssrrsssttuuvwwxxyyzz{{||}}}}~~~}}}|{zyxvusrpomllkjjjklmnprtvxz}~~~~~~~~~}|{zzyyyyy{|~}{zxwvvuuuvvwxyz{|}}~|zwtqomlkjjjklnoqrstuuuuuuuttttttttuuvvwwxxyyzz{{{|||}}}}}}}||{{zywvutrqponmmmmmnopqsuvxz|~~~~~~~~~~}|{{zzyzz{|~~|{zyxwwwwwwxxyz{|}}~}{yvtrpommmmmopqrtuvvwwwwwvvvvuuuvvvwwxxxyyzz{{{||||}}}}}}}}}}|{{zyxwutsrqqppooppqrstvwyz|~~~~~~~~~~}||{{zz{{|~~}|{zyyxxxxxyyz{{|}}~~|zxvtsqppooppqrstuvwwxxxxwwwwwwwwwwwwxxyyyzzz{{{{||||}}}}}}|||{{zyxxwvutssrrrrrrstuvwxy{|~~~}}||||||}~~}|{zzyyyyyzz{{||}~~~}{yxvutsrrrrrstuvwwxxyyyyyxxxxxxxxxxxyyyzzzz{{{||||}}}}}}}}}}|||{zzyxwwvuuttsstttuvvwyz{|~~~}}||||}}~~}||{{zzzzzz{{{|}}~~}|{yxwuuttsttuuvwxxyyzzzzzzyyyyyyyyyyyyzzzz{{{{{|||||}}}}}}}||||{{zzyxxwwvvuuuuvvvwxxyz{|}~~~~}}}}}~~~}||{{{{{{{{|||}}~~~}|zyxwvvuuuuvvwxxyyzzzzzzzzzzzzzzzzzzzz{{{{{||||||}}}}}}}}}}}}||{{zzyyxxwwwwwwwwwxyyz{|}}~~~~}}}~~~~~}}||{{{{{|||}}~~~~}|{zyxxwwwwwwwxyyzz{{{{{{{{{{zzzzzzz{{{{{{|||||}}}}}}}}}}}}}}}}|||{{zzyyyxxxxxxxxyyzz{|}}~~~~~~~~~~~}}||||||||}}}~~~~~}|{zzyxxxxxxxyyzz{{{||||||{{{{{{{{{{{{{||||||||}}}}}}}}}}}}}}}}|||{{{zzzyyyyyyyyzz{{||}}~~~~~~~~~~}}}}|||}}}}}~~~~}}|{{zyyyyyyyzzz{{||||||||||||||||||||||||||}}}}}}}}}~~~~~~}}}}}}|||{{{zzzzzzzzzz{{||}}~~~~~~~~}}}}}}}}}}~~~~~~}}|{{zzzzzzzz{{{||||}}}}}||||||||||||||||}}}}}}}}}}}~~~~~~}}}}}}}||||{{{{{{{{{{{{|||}}~~~~~~}}}}}}~~~~~~~~}}||{{{zz{{{{||||}}}}}}}}}}}}}||||}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}||||||{{{{{{||||}}}~~~~~~~~~~~~~~~~~~~~}}|||{{{{{{||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}|||||||||||}}}}~~~~~~~~~~~~~~~~~~~}}}||||||||||}}}}}}~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}||||||}}}}}~~~~~~~~~~~~~~~~~~}}}}||||||}}}}}}~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~~}|{zyxwvvutssrrrrqqrrrstuvwxz{}~~|{yxwvtsrqqpppppppppqqqrrrsssttuuvyz|yz{vz~}}{zyxwuutsrrrqqqqrrsttuvwyz|~~~}}}}|||||}}}~ytsx|wtsrstuvwxyz{|}}~~~~~}}}||{zzyyxwvuttsrrqqpppooooooooopppqrrstuvwxyz|}~{xurqruz~zwusrqppppppqqqqqrrrrrrrssssstttttttttuuttuuuvvwwxxyyzz{{||}}~~~|yusqpoqsw||ywusqqpppqqrrstuuuvvuuttrqppoonnnnooppqrstuuvwwxxxyyyxyyyyzz{{|}~}{xusqppqruy}|zxvutsrqqqppppppppqqqqqqqqqqqqqqqqqqqqqqqqqrrrssttuuvwwxyzz{|}~~{xvutstvx|~{yxvtsrqqpppppooooooooooooooppppppqqqrrrssttuuuvwwxyyz{||}~~|zxvuuvx{~}{zyxwvvuuuttttsssssssrrrrrqqqqqqqqqqqrrsstuuvwxyz{|}~}zwtrpoopsvy~~{ywuttssssttuuuuuuuttsrqqpooooooopqqrstuvwwxxyyyyyyyzzzzzz{{||}~}{ywusqpppqrux{~~}{yxvuttsrrrqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrsssttuuvvwwxyyz{||}~}{ywutsttvxz}~|zxwvutsrrqqqpppppppppppppppqqqrrrrssttuuuvvwxxyyz{{|}}~~|zxwvvvwxz}~|{zyxwvvuuttssssrrrrrrqqqqqqqqrrrrsstuuvwwxyz{|}~~|zwusqpopqrux|}{zxwwvvvvvwwwwwvvuutssrqqpppppqqrrstuvwwxyyzz{{{{{{{{{{|||}}}~}|zxvusrqpppqrtwy|~}{zyxvvuttssrrrrrrrqrrrrrrrrrrrrrrrsssstttuuuvvwwxxyzz{||}}~~|zywvutttuvwz|~}{zyxwvuttssrrrrrqqqqqqqrrrrrssstttuuvvwwxxyyzz{||}}~~~|{yxwvvuvwxz|~}|{zyxxwvvuuutttsssssssrrssssssttuuvvwxyyz{|}~~~|zxvtrqpppqrtwz}}|{zzzyyyyyyyyxxwvvuttsrrrrrrrsttuvwxxyzz{{||||||||||||||}}}~~~}{zxwutrqqpppqrtvx{}~|{zyxwwvvuuttttssssssssssssssstttttuuuvvvwwxxyyyz{{||}}~~~|{zxwvuttttuvwy{}~|{zyxxwvvuuttttssssssssttttuuuuvvvwwxxyyyz{{{|}}~~~}{zyxwvvuvvwxz{}}|{zzyxxwvvvuuuuttttttttuuuuvvvwxxyyz{||}~~~}|zxwutrqpppqrtvy|~~}}}}}}|||{{zyyxwvuuttttttuuvvwxyyz{{||}}}}}}}}}}}}}}}~~~~~|{zxwvusrrqpppqrstvxz|~~}|{zyyxxwwvvvvuuuuuuuuuuuuuuuvvvvvwwwxxxyyyzz{{||}}}~~~}|zyxwvuuttttuvwy{|~~}||{zyyxxwwvvvvvvvvvvvvvvwwwwxxxyyyzzz{{||}}~~~~}|{zyxwvvuuuvwxy{|~~}|{zzyyxxwwwvvvvvvvvvwwwxxxyyz{{||}}~~~~~}}|{zxwvtsrqqpqqrtvxz}~~}}|{zyyxwwvvvvvvwwxxyzz{||}}~~~~~~~~~~~~~~}|{zxwvutsrrqqqqrrstvwy{}~}||{zzyyyxxxxxwwwwwwwwwwwwwwxxxxxyyyzzz{{{|||}}}~~~}|{zyxwvuuttttuuvwxz|}~}||{{zzyyyxxxxxxxxxxxxxyyyyzzzz{{{|||}}}~~~}||{zyxwwvvuuuvvwxyz|~~}}|{{zzyyyyxxxxxxxxyyyzzz{{||}}~~~~}}}|||||{{zzyxwvutsrrqqqrrtuwy|~~~}|{zzyxxxxxxyyzz{||}}~~~~~~~~~~~~~~}|{zyxwvutssrrrrrrstuvxy{|~~}}||{{{zzzzyyyyyyyyyyyyyyyzzzzz{{{{||||}}}~~~~}||{zyxwwvuutttttuuvwxz{}~~~}}||{{{{zzzzzzzzzzzzzz{{{{{||||}}}~~~~~~}|{zzyxwwvvuuuuuuvwwyz{}~~~}}|||{{{{{{{{{{{{|||}}}~~~~~}||{{zzzzzzyyyyxwwvutssrrqqrrtuvxz}~}}|{{zzzzzzz{{||}~~~~~~~~~~~~}||{zyxwvvuttssrrrrrsstuvwyz|}~~}}}|||{{{{{{{{zzz{{{{{{{{{{|||||}}}}~~~~~~}||{zyyxwwvuuuttttuuvvwxy{|}~~~}}}}||||||||||||||||}}}}}}~~~~~~~}}|{{zyyxwwvvuuuutuuuvvwxyz|}~~~~}}}}}}}}}}~~~~~~~~~~~~~~}||{zzyyxxxxxxxxxwwvvuutssrrrrrstuvxz|~~}}|||{{{|||}}~~~~~~~~~~~~~~~~~}}|{{zyyxwvvutttssssssttuvwxyz{}~~~~}}}}}}|||||||||||}}}}}}}}~~~~~~~~}}||{{zyyxxwvvvuuutttuuuvvwxyz{}~~~~~~}}}}}}}}}}}}}}~~~~~~~~~}}||{{zzyyxxwwvvuuuutuuuuvvwxyz{|~~~~~~}}}~~~~~}|{zyyxxwwwvvvvvvvvvvvuuttsssrrsstuvwy{|~~}}}}}}}~~~~~~~}}}}}}}}~~~~~~~~~~~~~~}}}||{{zzyxxwwvvuuttttttttuuvvwxyz|}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}||{{zzyyxxwwvvuuuuttttuuuvwwxyz{|}~~~~~~~~~~~~~~~~~~~}}}||{{zzyyxxwwvvvuuuttttttuuuvwwxyz{|~~~~}}}}}}}}~~~~~~}|{zzyxwwvvvuuuuuvvvvvvuuutttsssssstuvwxz{}~~~~~~~~}}}}}}}|}}}}}}}}}}}}}}}}}}}}|||{{zzyyyxxwwvvuuuttttttttuuvwwxyz{|}~~~~~~~~~~~}}}|||{{zzyyxxxwwvvuuuuuttuuuuvvwwxyz{|}~~~~~}}}}|||{{zzzyyxxwwwvvuuutttttstttttuuvwxxyz{|~~~~}}}}}}}}}}}~~~~~}||{zyxxwvvuuuttttuuuuuuuuuuutttssssttuuvxy{|~~~~}}}|||||{{{{{{{{{{{{{{{{{{{zzzzyyyxxxwwwvvvuuuuuttuuuuvvwwxyzz{|~~~~~~~~}}}||||{{zzzyyxxxwwvvvuuuuutttuuuuvvwwxyz{{|}~~~~~~~~}}}}}|||{{{zzzyyxxwwwvvvuuutttttttttttuuvvwxxyz{|}~~~~}}}}|||||}}}}}~~~~~~~}||{zyxxwvuuttttsttttttuuuuuuuuttttttttuuvwxz{}~~~}}|||{{{{{{zzzzzzzzzyyyyyyyyyxxxxxwwwwvvvvuuuuuuuuuuuvvwwxxyz{|}~~~~~~~~}}}}||||{{{zzzyyxxxwwwvvvuuuuutttuuuuuvvwwxyyz{|}~~~~~~~~~~}}}}}}||||{{{{zzzyyyxxwwwvvvuuuttttsssssssstttuuvvwxxyz{|}~~~~}}}|||||||}}}}}~~~~~~~~}}}|{{zyxwwvuuttssssssstttttuuuuuuutttttttuuvwxy{|~~~}}||{{{zzzzzyyyyyyyyyxxxxxxxxwwwwwvvvvvuuuuuuuuuuuuvvvwwxxyz{{|}~~~~~~~~}}}}}||||{{{zzzzyyyxxxwwwvvvuuuuuutuuuuuuuvvwwxyyz{|}}~~~~~~~~~}}}}}||||{{{zzzzyyyxxxwwwvvvuuuttttsssssssssssssttuuvvwxxyz{|}~~~}}}}|||||||}}}}}}~~~~~}}}||{{zyxxwvuuttsssssssssstttuuuuuuuutttttuuuvwxyz{}~~~}}||{{zzzyyyxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuuuuuuuvvvwwxxyyz{||}~~~~~~~}}}}}||||{{{zzzzyyyxxxwwwvvvvuuuuuuutuuuuuuvvvwwxyyz{{|}~~~~~~~~}}}}}}||||{{{{{zzzyyyxxxxwwwvvvuuuttttssssssssssssstttuuvvwxxyz{{|}~~~}}}|||||||||||}}}}}}}}|||{{zzyxwwvuttssrrrrrrrssstttuuuuuuuuuuuuuuvvwwxy{|}~~}}||{{zzyyyxxxwwwwvvvvvvuuuuuuuuutttttttttttuuuuuvvvwwxxyzz{|}}~~~~~~}}}}||||{{{{zzzyyyyxxxwwwvvvvuuuuuuuttuuuuuuvvvwwxxyzz{|}}~~~~~~}}}}}||||{{{{zzzzyyyxxxxwwwvvvuuuttttssssrrrrrrrrrrssssttuuvvwxxyz{{|}~~~}}}}||||||||||}}}}}}||||{{zzyxxwvuutsssrrrrrrrsssttttuuuuuuuuuuuuuvvwwxyz{}~~}}||{{zzyyxxxwwwvvvvuuuuuuuttttttttttttttttuuuuuvvvwwxxyyz{{|}~~~~~~}}}}||||{{{zzzzyyyxxxwwwwvvvvuuuuuuuuutuuuuuuvvvwwxxyyz{||}~~~~~}}}}||||{{{{zzzzyyxxxwwwvvvuuuutttsssssrrrrrrrrrrrrrrsssttuuvvwxxyz{{|}~~~~}}}}|||||||}}}}}}}}|||{{zzyyxwvvuttssrrrrrrrrrssttttuuuuuuuuuuuuuvvvwxyz{|}~~~}||{{zzyxxwwwvvvuuuuttttttsssssssssssttttttuuuvvvwwxxyyz{{|}~~~~~}}}}||||{{{zzzyyyyxxxwwwwvvvvuuuuuuuttttuuuuuuvvvwwxxyyz{{|}}~~~~~}}}}||||{{{zzzzyyyxxxwwwwvvvuuuttttssssrrrrrrrrrrrrrrssssttuuvvwxxyzz{|}}~~~~}}}}||||||||||||||||{{{zzyxxwvvuttssrrrqqqrrrrsstttuuuuuuuuuuuuvvvvwxxyz{|~~~}}|{{zzyxxwwwvvuuuttttsssssssrrrrrsssssssttttuuvvvwwxyyzz{|}}~~~~}}}}|||{{{{zzzyyyxxxxwwwvvvvuuuuuuuttttttuuuuuvvvwwxxyyzz{||}~~~~~~}}}||||{{{zzzyyyxxxwwwwvvvuuuttttssssrrrrrrqqqqqqrrrrrrsssttuuvvwxxyzz{|}~~~~~}}}}}}|||||||||||||{{zzyyxwwvuutssrrrrqqqrrrrsssttuuuuuuvvuvvvvvvvwwxyz{|}~~~}||{{zzyxxwwvvuuutttssssssrrrrrrrrrsssssttttuuuvvwwxxyzz{||}~~~~~}}}}|||{{{zzzyyyxxxxwwwvvvvvuuuuuuttttttuuuuuuvvvwwxxxyzz{{|}}~~~~}}}}|||{{{zzzyyyxxxwwvvvuuuttttssssrrrrrqqqqqqqqqqqrrrrsssttuuvvwwxyyz{{|}~~~~~}}}}}}}}}}}}|||||{{{zzyxxwvvuttssrrrqqqqqrrrssstttuuuuuvvvvvvvvvvwwxxyz{|}~~}}|{{zzyxxwwvvuutttsssrrrrrrrrrrrrrrrrssssttuuuvvwwxyyzz{|}}~~~~}}}|||{{{zzzyyyxxxwwwwvvvvuuuuuuutttttttuuuuuvvvwwwxxyyzz{||}~~~~~~}}|||{{{zzzyyyxxxwwwvvvuuuttttssssrrrrrqqqqqqqqqqqrrrrssstttuuvwwxxyzz{|}}~~~~}}}}}}}}||||||||{{{zzyyxwwvuutssrrrqqqqqqrrrsstttuuuuvvvvvvvvvvvwwxxyz{|}~~~}}||{zzyxxwwwvvuuttsssrrrrqqqqqqqqqqrrrrssstttuuvvwwxyyz{{|}}~~~~}}}|||{{{zzzyyyxxxwwwvvvvvuuuuuuttttttttuuuuuvvvvwwxxyyzz{{|}}~~~~}}|||{{{zzzyyxxxwwwvvvuuutttssssrrrrrqqqqqqqqqqqqqqrrrrsssttuuvvwwxyyz{{|}~~~~~~}}}}}}}}}|||||{{{zzyxxwvvuttssrrrqqqqqqrrrsssttuuuuvvvvvvvvvvvwwwxyyz{|}~~~}}||{zzyxxwwvvuuttsssrrrrqqqqqqqqqqrrrrrsssttuuvvwwxxyzz{||}~~~~}}}|||{{zzzyyyxxxxwwwvvvvuuuuuuuttttttttuuuuuuvvvwwxxxyzzz{||}}~~~}}}|||{{zzzyyyxxwwwvvuuuttttsssrrrrrqqqqqqqqqqqqqqqqrrrrsstttuuvwwxxyzz{|}}~~~~~~~}}}}}}}}|||{{{zyyxxwvvuttssrrrqqqqqqrrrsssttuuuuvvvvvvvvvvvwwwxxyz{||~~~~}}|{{zzyyxwwvvuuttsssrrrqqqqqqqqqqqqqrrrsssttuuvvwwxxyzz{||}~~~~}}|||{{{zzyyyxxxwwwwvvvvuuuuuuttttttttttuuuuuvvvwwwxxyyzz{{|}}~~~~~}}|||{{zzzyyxxxwwwvvvuuttttsssrrrrrqqqqqqqqqqqqqqqqrrrrsstttuuvvwxxyyz{||}~~~~~~~}}}}}}||||{{zzzyyxwwvvuttsssrrrrrrrrrrssstttuuuuvvvvvvvvwwwwxxyyzz{|}~~~~}}||{{zyyxxwwwvvuutttsssssrrrrrrrrrrrrsssstttuuvvwwxxyyzz{||}~~~~}}}|||{{{zzzyyyxxxwwwwvvvvvuuuuuuuuuuuuuuuuuvvvvwwwxxyyyzz{{||}~~~~}}}|||{{{zzyyyxxxwwwwvvvuuuuttttttssssssssssssssttttuuuuvvwwxxxyyz{{||}}~~~~~~}}}}|||{{{zzyyyxxwwvvvuuuutttttttttuuuuuvvvvvwwwwwwwxxxxyyyzz{{|}}~~~~}}}|||{{zzzyyxxxwwwvvvuuuuutttttttttttttuuuuuvvvwwwxxyyzz{{||}}~~~~~}}}|||{{{zzzyyyyxxxxwwwwwwvvvvvvvvvvvvvvvvwwwwwxxxxyyyzz{{|||}}~~~~~}}}|||{{{zzzyyyyxxxwwwwwvvvvvvvuuuuuuuuuuuuuuvvvvvwwwwxxxyyzzz{{||}}~~~~~~}}}}|||{{{zzzyyyxxxwwwwvvvvvvvvvvvvvvvvvwwwwwwxxxxxyyyyzzz{{{||}}~~~~~}}}||||{{{zzzyyyxxxxwwwwwvvvvvvvvvvvvvvvvvwwwwwxxxyyyzzz{{||}}~~~~~~}}}||||{{{{zzzzyyyyxxxxxxxxwwwwwwwwwwwwwwwxxxxxxyyyzzzz{{{||}}}~~~~~}}}||||{{{{zzzzyyyyxxxxxwwwwwwwwwwwwwwwwwwwwwwwxxxxyyyyzzz{{|||}}~~~~~~~}}}}||||{{{{zzzyyyyxxxxxxwwwwwwwwwwwwwxxxxxxxyyyyyyzzzz{{{|||}}~~~~~~~}}}}|||{{{{zzzzyyyyyxxxxxxxxxwwwwwwwwwxxxxxxxyyyyyzzz{{{|||}}}~~~~~~}}}}||||{{{{{zzzzzyyyyyyxxxxxxxxxxxxxxxxxxyyyyyyzzzz{{{||||}}}~~~~~~}}}}||||{{{{{zzzzzyyyyyyxxxxxxxxxxxxxxxxxxxxyyyyyzzzzz{{{|||}}}~~~~~~~~}}}}||||{{{{zzzzzyyyyyyyyyxxxxxxxxxyyyyyyyyyzzzzzz{{{{||||}}}~~~~~~~}}}}|||||{{{{{zzzzzyyyyyyyyyyxxxxxxxxyyyyyyyyzzzzz{{{{|||}}}~~~~~~~~}}}}|||||{{{{{zzzzzzzzyyyyyyyyyyyyyyyyyyyzzzzzz{{{{{||||}}}~~~~~~~~}}}}|||||{{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyyyzzzzzz{{{{{||||}}}~~~~~~~~}}}}}|||||{{{{{{zzzzzzzzyyyyyyyyyyyyyzzzzzzzzz{{{{{{||||}}}~~~~~~~~~}}}}}|||||{{{{{{{zzzzzzzzzzzyyyyyyzzzzzzzzzz{{{{{{||||}}}}~~~~~~~~~}}}}}||||||{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{|||||}}}}~~~~~~~~~}}}}}||||||{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzz{{{{{{{||||}}}}}~~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzzzzzzzzzzzzz{{{{{{{{{|||||}}}}}~~~~~~~~~~}}}}}|||||||{{{{{{{{{{{zzzzzzzzzzzz{{{{{{{{{{||||||}}}}~~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||}}}}}~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{|||||||}}}}}~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||}}}}}}~~~~~~~~~~~~}}}}}}}|||||||||{{{{{{{{{{{{{{{{{{{{{{{||||||||}}}}}}~~~~~~~~~~~}}}}}}}}|||||||||||{{{{{{{{{{{{{{{{{{|||||||||}}}}}}~~~~~~~~~~~~~}}}}}}}|||||||||||||{{{{{{{{{{{{{|||||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||||||{{{{{{{||||||||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}}|||||||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}|||||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}|||||||||||||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}||||||||||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}|||||||||||||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}|||||||||||||||||||}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}|||||||||||||}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~trqhbb_[ZZYXXXXXXXYYYZZ[[\]abcefhjlnqsvy|~{xvspnljihffedegggggggggggfffeeeeefhjou}i_a`p}~|yvspnkigeedccccdefhjmpsvy}}|zywxxwwvvuvvvwxz{teduuha`bejmosvy|~~}|{zywvtrpomjgecb`_]\[ZZYYYYZ[\]_acehkmpsvz}vlfekxvohec``_``bdeeffgfeeeeddccbaa``^]]]]]]\\\]]^_acdfhiknprtvxz|}{sjc^[[_fq}xqjfb_^]]^`acdfghhhgfecb`_]\[ZZZ[\]^`acdfgijkllmnnoopqrstuwy{}~xrkd_[Z[_gp{{tojgda`^]]]]\]]]]]]]]]]]\\\\\[[[[[[[[\\\]^^_`abdeghjlnprtvxz}}vpjebacipy|vpkgda_^]\[[[[[[[\\\\\\]]]]]^^^__``abbcdefghjlmoprtvxz|~ysmiedeiox}xtqnljhgfeddccbbaaa`__^^]]]\\\\\\\]^^_abcegikmpruwy|~zskd^ZXX\ajtzsnifcbaabbcdeffffedcb`_^]\[[[\]^_acdfhjkmnopqqrrssttuvwxz{}ztoid_[YXY]bjs|}xsokheca`^^]\\\\\[[[[[[[[[\\\\\\\]]^^__`abcdefhiklnprsuwy{}{uojfb``bfls{~xsokgeb`_]\\[[ZZZZZ[[\\\]]]^__``abcdefghiklmoprtuwy{}~~xsnjfddehmt|{wtpnkigfdcba``_^^]]\\\\\[\\\\]]^_`bcdfhjlnprtvy{}{unhb]ZXY[`gox|wrnkigfffffggggffecba_^]\\[[\]^_acefijlnoqrsttuvvwwxxyz{}~}ytokfa^[YYZ]ahov~|wsoligeca`_^]]\\[[[[[[[[[[\\\\]]^^_``abcdeghjklnpqstvxz{}{vqmiecbbdgkqx}ytqmjhecb`_^^]]]]]]]]]]^^^_``abbcdefghjklmoqrtuwxz|~}yupmigfegimsy}zvsqnljhgfdcba``_^^^]]]]]]^^_`abcdfgikmoqsuwy{}~|wrlgc_\[[]afmt}|xusqonnnmmmllkihgecba_^^^^^_`bcegiklnoqrsttuvvvvwwxxyz{|}}yuqmiea^\[[\_chmt{}zvspnkigfecbba``____^^^^____```abbcdefghijklnoprsuvxy{|~~zvrnkhedccehkqv}{xuromkihhfedcccbbbbbbbbccddeefghijklmnoprstvwxz|}~|xtqnkigffhjmrx~{yvsqomkjhgfedccbbbbbbbbccdefghjkmnpqsuvxz{|~~zwsnjfb_]\\]`ekqy|zyxwvvuutsrqonljigfedcccddeghjlnoqstuvwxxyyyyyyzzz{{|}~|yvrokheb_]\[[]_bfkqw}}zwusqomljihhgffeeeddddddeeefffghijjklmnopqrstuvwyz{|~~zwtqnjhfdcccegjnty~~{xwusqpnmlkjjiiihhhhhiiijjkklmnnopqrstuvwyz{|}~{xuroljhfeeegilpuz~|ywtrpnmlkjiihhhhhhhiijklmnoprstvwyz{|}~~}{ywtqnjgc`^\[[\_cgmsz~}||{zyxvusqonlkjiiiiijkmnoqstvwyz{|||}}}}}}}}}}~~}zwtqnkheb`^\[[\]_aeinsx~~|zxvtrqponmlkkkjjjjijjjjjkllmmmnoopqrsstuvwxyz{|}~|zwtroljgecbbbbdfilpuz~|zxvusrqpoonnmmmmmmnnnnoopqqrsstuvwxyz{|}~}{xuspnkigfddddfhjnrv{}{ywvtsqponnmmmllmmmnnopqrstuwxyz{}~~}{{zyxwvutrpmkhec`^\\[\^aejou|~}{ywutrponmmmmmnoqrsuvxyz{|}~~~~~~~~~~~~~}{xvspmkheca_]\\[\]_adglpuz~}{zxvutsrqppoonnnnmnnnnooopppqqrrstuuvwxxyz{|}~}{xvtqomjhfeccbbcdfilosw|}{zxwvutsrrrqqqppqqqqqrrssttuvvwxyz{{|}~}{yvtrpmkihfedddefhjmptx|}|zxwvutsrrqqqqqrrrstuuvwxyz{|}~~~}{zxwvvuttsrqonljheca`^]]^_beinsy~~}{ywutrqqpppqqrtuvwyz{|}~~~~~~~~~}{yvtromkhfdba`_^^_`acfilptx}~|{zywvuutssssrrrrrrrrrrsssttuuuvwwxyyz{{|}}~~|{ywusqomkigfedccddfgilorvz~~}|{yyxwvvuuuututuuuuuvvwwwxyyz{{|}}~~|zywusqomljhgfeeeefgikmpsw{~}{zyxwwvvvvvvvvwwxyyz{||}~~~~~~}}~~~~|zxvutrqqpoonnmlkjigfdba`___`behlqv|}{yxvuutttuuvwxyz||~~~~}}}}}}}~~~~|{ywusqpnljhfdcba```abcegjmptx|~}|{{zyyxxwwwwvvvvvvvwwwwxxxyyzz{{||}}~~~}|{yxvtsqonljigfeddccddfgilnqux|~}||{zzzyyyxxxxxyyyyyzzz{{||}}}~}|{zxwutrpomljhgfeddcddefgilnqux{~}}||||||||}}~~~~~}}|||||||}~~}{ywusrponmllkkkjjiihgfecba`__``bdgjosx}~|{zyyxxyyz{|}~~}}}||||||||}}}~~~}}|zyxvusqpnljigfdcba````abcegiloruy}~}}||{{{zzzzzzzzzzzz{{{{||||}}}~~~}|{zyxvutrqonlkihgeedcccccdegikmpsvy}~~}}||||||{|||||||}}}}~~~~}}|{zywvutrqonlkihfedccbbbbbcdfhjloqtx{~~}}||{{{{{||}~~}|zxvtrpnmkjjihhhhggggfeedcba``_``bcfilpty~~}||||||}~~}}||{{{{{{{{{{{{||||||||{{{zyyxwvusrqonmkjhgfeccbaaabbcdfhjloqtx{~~~~~}}}}}}}}}}}}}}~~~~~~}}|{zyxwvutrqpomljihgfedccccccdeghjloqtwz}~~~~~~~~~~~~~~}||{zyxwvutsqpomljihgfedccbbbbcdefhilnpsvy|~}||{{{{{{{{|}}}~~}|zxvtrpnlkihggfffffeffeeeddcbaaaaabdehknrv{~~~~~}||{{zzzyyyyyyyxxxxxxxxwwwvvvuttsrqponmkjihgfeddccccddefhikmprux{~~~~}}||{zzyxwvutsrponmlkjhgffeddcccddefgijloqsvy|~~~~~~~~}}}|{{zzyxwwvutsrqpnmljihgfeddcbbbbbbccdfgikmoqtwz}~}|{{zzzyyzzzz{{|}}~~~~~~}}|zywutrpnljigfeeddddddeeeeedddccbbbccdfhjmqux}~}|{zzyxxwwwvvvvuuuuuuuutttssrrqpoonmlkjihgffedddddddefghjkmortwy|~~~~~~~~~~}}}}||{{zzyyxwwvutsrqponmlkjihgffedddddddeeggiklnqsux{}~~~~~~~~}}}}}|||{{zzyyxxwvuutsrqponmlkjihgfedccbbbbbbbcdefgijlnqsvx{~~}|{{zzyyyyyyyyzz{{|||||||{zyxvtsqomkjhgedccbcbcccddddddddcccccccdfgjlosvz~~}|{zyxwwvuutsssrrqqqqpppooonnmmlkkjiihggfeeeddddddefghijlnprtwy|~~~~~~}}}|||{{zzzyxxwvvuttsrqponmlkjiihgfeeddcccccddefghjlnprtvy{~~~~~~}}}}||||{{{zzyyxxwwvuutsrrqponmlkjihgfeddcbbaa`````aabcdegijlnqsuxz}~}|{zzyxxxxxxxyyyzzzzzzzyxxvusqpnljhgedcbaaaaaaabcccddddddccccddegikmptw{~}{zyxwvuttsrrqqppooonnnmmmllkkjjiihggffeeddddddddeffgijkmoqsuxz|~~~}}|||{{zzyyxwwvuttsrqpponmlkjihhgfeeddccccccddefghjkmoqsuwz|~~~~}}}|||{{zzzyyxxwvvuutssrqponmllkjihgfedccbaa``_____```abcdfgiklnqsuxz}~}|{zzyyyxxxxyyyyzzzzzyyxwvutrpomkigfdcbaa````aaabcccddddddddddeeghjlorux|~}|{yxwvutsrqpponnmmllkkjjjiihhgggffeeedddddddddeffghiklnoqsuwz|~~~}}}||{{zzyyxwwvuutssrqpoonmlkjjihggfeedddccccddeffgijkmoprtvx{}~~}}}||{{zzyyxxwwvvuttsrqqponmllkjihgffedccbaa````__```abbcdfgijlnprtvy{}~}|{zyyxxxxxxxxxxxxxxxxwvutsrpnlkigedbaa``````aabbcddddddededeefghjlnqtwz~~}|{yxwvtsrqponmllkjjiihhggfffeeedddccccccccddeefghijklnpqsuwz|~~~}}||{{zzyyxwwvuutsrrqponnmlkjjihggfeeedddcccdddeffhijkmnprtvxz|~~~}}||{{zzyyxwwvvuttsrrqponnmlkjjihgffedcbbaa``________``abcdefhikmnpruwy{}~}|{zyyxxxxxxxxxxxxxxwwvutsrpomkihfdcba`______``abbcdddeeeeeeeffghjkmprux{~}|{yxwutsrponmlkkjiihggfffeedddcccccccccccccddeffghjklnoqsuvx{}~~}}|{{zzyxxwvvutssrqpponmllkjiihggfeedddcccccdddefgghjklnoqsuwx{}~}}||{zzyxxwvvuttsrrqponnmlkjjihgfeedcbba``__^^^^^^^^^__`aabdefhikmoqsuwy{}~}|{{zzyyxxxxxxxxxxwwwvutsrqonljhgedba`__^^^^__`aabccddeeeeeffffghijlnpsvy|}|{yxwutsqponlkjihhgfeeddccbbbbaaaaaaaaabbbccdefghijlmoprtvxz|~~~}||{zzyxxwvuttsrqqponmmlkjiihggfeedddcccccccddeefghijlmoprtuwy{}~}}||{zyyxwwvutssrqpoonmlkkjihgffedccbaa``__^^^^^^^^^__``abcefgijlnprsvwz|~~}|{zyyxxxxwxwwwwwvvvutsrqonljigfdba`_^^]]^^^_`aabcddeeefffffgghijkmoqtwz}~|{zxwutsqpnmlkjhgffedccbaaa````____````aabbcdefghjklnpqsuwy{}~}|{{zyxxwvuttsrqpoonmlkkjihhgffeedddcccccccddeefghijklnoqstvxz|~~}|{{zyxxwvuttsrqpponmlkjjihgffedcbbaa`__^^^]]]]]]]^^__`abcdefhiklnprtvxz|~~}|{{zzyyyxxxxxxwwwvuutsqpomljhgedba`_^^^^^^^_`aabcddeeeffffggghijkmnpsux{~~}{zywvtsqpomlkjhgfeedcbbaa```_______```aabbcdefghiklmoqrtvxz|~~}|{{zyxwwvutsrrqponnmlkjjihhgffeedddccccccdddeefghijklnoprtuwy{}~~~}|{zzyxwvuttsrqpoonmlkjiihgfeedcbba``__^^^]]]\]]]]^^__`abcdfghjlmoqsuwy{}~}||{zzzyyyxxxxwwvvutsrqpnmkihfecba`_^^]]^^^_``abccdeeffffggghhijklnprtwy|~|{zxwvtsqpnmljihgfedcbaa`___^^^^^^^^___`aabcdefghjklnpqsuwy{|~~}}|{zyxwwvutsrqpponmllkjiihggfeedddccccccccddeefghhjklmopqsuvxz{}~}|{zyyxwvutsrqpponmlkjihhgfedccbaa`__^^^]]]]\\\]]]]^__`abcdefhiklnprtuwy{}~}|{{zzyyyxxxwwwvvutsrqpnmkjhgedba`_^^]]]]^^__`abcddefffgggghhiijlmoqsuxz}~}|zyxvusrpomljihgfedcba``__^^^^]^^^^^__``abbcdefhijlmoqrtvwy{}~}|{zzyxwvutsrqpoonmlkjjihggffeeddccccccccccddeffghijklnoqrtuwxz|~~}|{zyxwvutsrqponmllkjihgfeddcbaa`__^^]]]\\\\\\\\]]]^__`abcdfghjkmoqrtvxz|~~}||{{zzyyyxxxwvvutsrqpnmkjhgecba`_^]]]]]]^__`abccdeeffggggghhijklnoqsvx{~~}{zywvusrpomljihgedcba``_^^]]]]]]]]]]^^__`abcdefhijlmoqrtvxz{}~}|{zyxwvutsrqpponmlkjjihggffeeddccccccccccddeefghijklmnpqstvwy{}~~}|{zyxwvutsrqponmlkjihgffedcbba``_^^]]]\\\\\\\\]]]^__`abcdeghiklnpqsuwyz|~~}||{{{{zzyyxxwvutsrqpnmkjhfedba`_^^]]]]^^__`abccdeefffgggghhijklmnprtwy|~}|{zywvusrpomlkihfedcba`__^]]]\\\\\\\]]^^_`abcdefhijlnoqstvxz|~~|{zyxwvutssrqponmlkjjihggffeedddcccccccccddeefgghijkmnoqrsuvxz{}~}|{zyxwvutsrqponmlkjihgfeddcbaa`__^^]]]\\\\\\\\\]]^^_`aabdefgijlmoqrtvxy{}~}}||{{zzyyxxwvuutrqpomljigfdca`_^^]]]]]^^_``abcddeffggghhhiijjklnoqsuxz}~}{zyxvutrqonlkjhgfdcba``_^^]]\\\\\\\]]^^_``abcefghjkmnprsuwy{|~~}|{zyxvvttsrqponmlkkjihhgffeedddccccccccdddeefgghijklmopqstvwyz|~}|{zyxwvutsrqponmlkjihhgfeddcbba``_`____^^^^____``aabcdefghijlmnpqsuvxz|}~}}|{{zyyxwwvutsrqonmljihgfedcbbaaaaaaaabccddeffgghhhiijjkllmnpqstvxz|~~}|{zyxwvutsqponmlkjihgffeddccbbaaaaaaaabbccddefghijklmopqstvwyz|~~}|{zyxwvutsrrqponnmlkkjiihhgggfffffeeeffffgghhiijklmnopqrtuvwyz{}~~}||{zyxwvutsrrqponmmlkjjihhggffeeddddcccccccdddeeffghhijklmnoqrsuvwyz|}~}}|{{zyyxwvvutsrqponmlkjjihggffeeeeeeeefffgghiijjkkllmmnnopqrstvwxz{}~~}|{{zyxwvuutsrqponmmlkjjihhggffffeeeeeefffggghiijklmnopqrstvwxz{|~~}|{zzyxwvuutsrqqpoonmmllkkjjiiiiiiiiiiiiiijjjkkllmnoopqrstuvwyz{|}~}|{{zyxwvvutssrqpponnmllkkjjiiihhhggggggggghhhiijjkklmnoopqrtuvwxy{|}~~}}|{zzyxxwvuttsrqpponnmllkkjjiiiiiiiiiijjjkkkllmmmnnoppqrsstuvxyz{}~~~}|{{zyxxwvuttsrqqpoonmmllkkjjjiiiiiiiiiiijjkkklmmnoopqrstuvwxy{|}~~}}|{zzyxwwvuutssrrqppoonnmmmllllkkkkkkkkklllmmmnnoppqrrstuvwxyz{|}~~~}|{{zyxxwvuutssrrqppoonnmmlllkkkkkkjjjjkkkklllmmnnoppqrrstuvwxyz{|}~~}}|{{zyyxwwvuutssrqqpponnnmmmlllllllllllmmmmnnnooppqqrsstuvvwxyz{|}~}}|{{zyyxwwvuutssrrqppooonnmmmmmmllllllmmmmmnnooppqqrsstuvwxyyz{|}~~}}|{{zyyxwwvuuttssrrqqpppooonnnnnnmmmmnnnnnoooppqqrrsttuvwwxyz{|}~~~~}||{zzyxxwwvuuttssrrqqqppoooonnnnnnnnnnnnnnooopppqqrsstuuvwxxyz{|}~~}}||{zzyyxwwvvuttssrrqqpppoooonnnnnnnnooooopppqqqrrsstuuvwwxyz{{|}~~~}}|{{zzyyxwwvvuuttssrrqqqppppooooooooooooopppqqqrrsstuuvwwxyzz{|}~~~}}|{{zzyxxwwvvuuttsssrrrqqqpppppppppppppppqqqrrrssttuuvwwxyyz{||}~~~}}||{zzyyxxwwvvuuttsssrrrqqqqpppppppppppppqqqqrrssstuuvvwxxyzz{|}}~~}}||{{zzyxxwwvvuutttssrrrqqqqqqqqqqqqqqqqqrrrrsstttuuvvwwxyyz{||}~~~}}||{{zzyyxxwwvvuutttsssrrrrqqqqqqqqqqqqqrrrrsssttuuvvwwxyyz{{|}}~~~}}||{{zzyyxxwwvvvuutttsssssrrrrrrrrrrrrrrrssssttuuuvvwwxyyzz{||}~~~}}||{{zzyyxxwwwvvuuutttssssrrrrrrrrrrrrrrrsssstttuuvvwwxxyyz{{|}}~~~}}||{{zzyyxxwwwvvuuuttttsssssssrrrrsssssssttttuuuvvwwxxyyzz{||}~~~~}}||{{{zzyyxxwwwvvvuuuttttsssssssssssssssttttuuuvvvwwxxyyzz{{|}}~~~~}}|||{{zzyyxxxwwwvvvuuuuttttttssssssssttttttuuuvvvwwxxyyzz{{||}~~~~}}||{{zzzyyxxxwwwvvvuuuutttttttttttttttttuuuuuvvvwwxxxyyzz{||}}~~~~}}||{{{zzyyyxxxwwwvvvuuuuuuttttttttttttuuuuuvvvwwwxxxyyzz{{||}}~~~~}}||{{{zzyyyxxxwwwwvvvvuuuuuuuuuuuuuuuuuuuvvvvwwwxxxyyzz{{||}}~~~~}}}||{{{zzzyyxxxxwwwvvvvvuuuuuuuuuuuuuuuuvvvvvwwwxxxyyzz{{{||}}~~~~}}}||{{{zzzyyyxxxwwwwvvvvvvvuuuuuuuuuuvvvvvvwwwwxxxyyzzz{{||}}~~~~}}}||{{{zzzyyyxxxwwwwwvvvvvvvvvuuuvvvvvvvwwwwwxxxyyyzzz{{|||}}~~~~~}}}||{{{zzzyyyyxxxxwwwwwvvvvvvvvvvvvvvvvwwwwwxxxxyyyzz{{{||}}~~~~~}}|||{{{zzzyyyyxxxxwwwwwwwwvvvvvvvvvwwwwwwwxxxxyyyzzz{{{||}}}~~~~}}}|||{{{zzzzyyyyxxxxwwwwwwwwwwvvvwwwwwwwwwxxxxyyyyzzz{{|||}}~~~~~~}}}|||{{{zzzzyyyyyxxxxxwwwwwwwwwwwwwwwwwxxxxxxyyyyzzz{{{||}}}~~~~~}}}|||{{{{zzzzyyyyxxxxxxwwwwwwwwwwwwwwwxxxxxxyyyyzzz{{{|||}}~~~~~~}}}||||{{{zzzzyyyyyxxxxxxxxxwwwwwwwxxxxxxxxyyyyzzzz{{{|||}}}~~~~~}}}||||{{{zzzzyyyyyyxxxxxxxxxxxxxxxxxxxxxyyyyyzzzz{{{|||}}}~~~~~~}}}||||{{{{zzzzyyyyyyxxxxxxxxxxxxxxxxxxyyyyyyzzzz{{{{|||}}}~~~~~~~}}}||||{{{{zzzzzyyyyyyxxxxxxxxxxxxxxxyyyyyyyzzzz{{{{|||}}}~~~~~~~}}}||||{{{{{zzzzzyyyyyyyyyxxxxxxxxyyyyyyyyzzzzz{{{{||||}}}~~~~~~~}}}||||{{{{{zzzzzzyyyyyyyyyyyyyyyyyyyyyyzzzzzz{{{{||||}}}~~~~~~~}}}|||||{{{{{zzzzzzyyyyyyyyyyyyyyyyyyyyzzzzzz{{{{||||}}}}~~~~~~~}}}}||||{{{{{zzzzzzzzyyyyyyyyyyyyyyyzzzzzzzz{{{{||||}}}}~~~~~~~}}}}}||||{{{{{{zzzzzzzzyyyyyyyyyyyyzzzzzzzz{{{{{|||||}}}~~~~~~~~}}}}}|||||{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{|||||}}}}~~~~~~~~}}}}}|||||{{{{{{zzzzzzzzzzzzzzzzzzzzzzzz{{{{{{|||||}}}}~~~~~~~~}}}}}||||||{{{{{{{zzzzzzzzzzzzzzzzzzzzz{{{{{{|||||}}}}}~~~~þ~hd]RPOPVUU^`\`f`^b_YYYRMKHB@>:876679<@EKS_gqz|qe]VOLHC@<:87666678889::<=@CMYfxŸ~LWcÿzsld[UOIEA><;;BFKQV[afjnrvy|ueUG>=CSi{k]RIB=:8766667777777666554444444678:<>@CGJNRV[_djoty~rdVMKP]p|m_UMFB><;:::::;;;;;<<<<<<==>>?ABDFGJLOSVY^bfjoty}reYQOT`szpf_ZVRPOMLLKJIHGFDCBA?>==<<<<=>@BEHLPTX^chmsx}sbQC:7==>?ACGJNRUY[^`bdefhikmoruy}sfXK@847@Pd{rf[SNJEB@>=<;;;;;;;;;;<<<<==>?@ABDFGJLORUY]`dimqvz¿xk]RHDEKYj~qe[RKEA=;987666677899:;<=>?ABDFHKMPSVZ]aeimrv{¿|pcYQMMTarüvld]XSPLJHFDCA@?><;::9888899;>>>>??@@ABCEFHJLNQTWZ^aeilpuy}}rg\SLGGLSaq~tjb[UPLIGECBBAAAABBBCDEFGIIKMOQTVY[^bdhlosw{{qf\TMJINVcr}tmf`[WSPNKIHFEDCBAAAAABBDEGIKNQUX]aejnsw{ym_RG>869AO`t{pg`[WUTTTTTSRPMKHEB@?>>@ADGKOSW[_begjkmnoqrsuxz}~ukaVMD>:9;BLYizxog_YTOKHECA@?>=======>>?@ABCDFHJLNQTVZ]`dgknrvz~|si`WPKJKQYfsxog`ZUPMJGEDBBAAAAABBDEEGHJLMPRTWY\_bfilptwz|sjbZTPOQV_kyľwpje`\XUROMKIHFEEDDDDDEFHJLOQUX\_cgkpsw{~xncXNE>;;@IWfyû}upkhgedcba_\YVSOLJHGGHILORVY]`dfiklnoppqrstuwy|~|tld[RJB=:89=EO[jyyqke`[XTROMLKJIHHHHHIIJKKLNOQRTVX[]`behjnqtwz}{sjbYRLGEEHNWcp~|ungb]YUROMLKJIIIIJJKLNOPQUWY[]_begkmptwz}}ume^WQMKLOU^iv~wpje`\XUROMKIHGGGHIIJLMOQTWZ]`dhkorvy|~wne[RIB><>CLYhyþytqnlkigfda_]YVROMKJJKMORUX\`cfiknopqssstuvwxz|~~wog`XPJD@==@ELVbo}ysmhfc_\YVUSQPONNNNNNNOOPQRTUWXZ\^`cegjmorux{}yrjc\VPLIIJOV^iv}wqlhda^\ZXWVUUUUUVVWXY[\]_acegilnqsvx{~|vohb[VQMKKMQXalxytokgc`^[YXVUUTTUUVXY[]`behknqtwz}~{xsng`XPHB=::=BLXfu}zywuroliea]ZXVUUUWY\^beilortvwxyyyyzz}~~yrkd\UOHC?<;==@DJQZdoz{wspmjhfdba`___^^___`aabcefgijlnpqsuwy{~zvqkf`[VQMJGFGILQX`jt~}yuroljhfedccccccdeeghiklmoqrtvxz}~zvqlga]XSPMKJKMPU\dnx{vsoligecba```abcdfhjloqsvxz|~{xuspomkhfb^YTOJEA??@DKS^jw{xtplheba`_`acehjmpsuxz{|}~~~~}}}}}}~|wrmgb\WRMIFCBBCFJPW_hr|{wtqoljhgedccbbbbbbccdefghjklnpqsuvxz{~|xtpkfb]XTQNKJJKNRW^enw|yvsqomljiihhhhhhijkkmmoqrtuvxz{}~|yuqlhd_[WSPMKKKLOSY_gpy~zwtrpnmlkkkklmnqrsuwyz|}~~~~}~{wsoljhfdba_\YVRNJFB@??BFLU_jw{wsqomkkkkmoqsvx{}~~~~}~~~}yuqlgb]XSOJFCA??@AEIOV^foy~{zxusrponmllkkkkkllmnnopqrttvwyz|}~~{wtplgc_[VSOLJIIIKNRW]dmu~~{ywutrqpooonnoooppqrsuvvxyz|}}zwsokgc_[XTQOMLLLNRV[aiqz|ywusrqqppqqrstuwxy{|~~}|||||}~}ytqmjgecb`_^\ZXUQNKGECCDGKQYcny|yusponnnpqsuwy{}~}|{{{{{{||}~~|yvsokgc_ZVRNKHFDDDFHLPV\dlt}}{yxwutssrrqqqqqrrsstuuvwxyz{|}~~|zwtqnjgc_[XTQNKJHHHJLOSY`fnv~}|zyxwvvvuvuvvvwwxyzz{|}~}{xurolhea]ZVSPMKIHHHJLPTZ`gnv}|{{{{{{|}~~}}|{{{||~~zvqmifb`][ZYXWUSQOMJHEDCCDGKQYblw{yvtsssuvxy{~~}||{{{||}}~~|ywtqlhea]YUROLIGEEEFGJNRX^elt|~|zyxwvuuttttttttuuvvwxxyz{{}~~}zxvsqnkheb^[XURPNLKJJLMPTX]ciqx}|{zyxxxwwxxxxxyzz{||}~~~}{ywuspnkheb_\YVSPNLKJJJKMPTY^dkry~|{zyxxwwwxyz|}~zwsokgc_\YWUTSRRQQPNMKIGEDDDFINT[dnx|{zyyz{}~}|{zyxwwwwwwwxxxxxxxxwvutrqnljgda^[XTQNLJHFFFFHJMPUZ`gnu}~}||{{{{zz{{{{||}}}~~~|{yxvtqomjgda^[XVSPNLJIHIIJLOSW\bhov}~~}}|||{{||||}}~~|{yxvtrpmjheb_\YWTQOMKIHHHIJLOSX]bhov}}|zyxwwvvwwxz{|~}{wtplhd`\YVTSQQPPPPONMLJIHGGHILQV]fox~|||}~~}{zxwvuuttststttttttssrrqonljhec`^[XUSPNLJIHHHIKMPTY^djqw~~}}||||{||||||||}}}}}~~~~~~}}}|||{zyxwvtsqomkigdb_\ZWURPNLKJIIJJMORVZ_ejqx~~~}}|{zywvtsqmligeb`]ZXUSPMKJHGFEEFGIKNRW[agmt{~}{zxwvvvvvwxyz{|~~~~}|zwtqmiea]YVSQONMMLLMLLKKIHGFFFGILPV]emw}{zxvutsrrqpppooonnmlkjihfdb`^\ZWUSQOMKJIIIJKMPSW[`flrx~~~~~}~}||{zyywvusrpnmkhfdb_]ZXUSQOMLKJJJKLOQTX\aflrx~~~~~~~~~~~~}}}}|||{zzyxwvutrqonljhfda_]ZXVSQOMKJHHGGGHJLNRUY_djpv|~{zxwutssssttuvwxyyyyxwurolhd`\XTQOMKJJJJJKKKKKJJIIIIJLPTY_gox}{ywusrponmllkkjjihhggfdcb`_][YWUSQONLKJIIIJKLORUY]bgmsy~~~}}}|||{zzyxwvutsqpnmkigeca_\ZXUSQOMLJIIIIJKLOQTX\afkqw}~~~}}}||{{zzyxwvvusrqonljigec`][YVTRPNLJHGFEDDDEFHJMPTX]bgmrx}{zxwvutttuuvwwxyyyxxvtrnkgc_\XTQNLJIIIIIJJKKKKKJJJJJLNQUZ`how~|zxvtrpnmljhhffedcbba`_^][ZYWVTSQPOMLLKKKLMOPSVY]afkpv|~}}}|{{zyxxwvutsqpomljigeca_][YWUSQONLKJJJJJKMNQTW[_chmsy~~}}}|{{zyxxwvutsrqpnmkjhfeca_][YWUSQOMKIHGFEDDDEFGIKNRUZ^chnsy~|zxvutsrrrrssttuuutsqpmjgc_[WSPMJHFEEEEFGHIJJJJJJJJKLNQUZ`gow~|zwusqnlkihfedcba`_^]\[ZYXWUTRQPNMLKKJJJKKMNPSVY]afjpuz~}}|{zyxxwvutrqpnmkjhfdba_][YWUSQPNMKKJIJJKLMORTW\_dimrx}~~}|{{zyxxwvutsrponlkjhfecb_^\ZXVTRPNLJIGFEDDCCDEFGILORUZ^chmrx}~|zxvutsrrrrrrssssssrpnlifc_[WTPMJHGEEEEFGHIJKKKKKLLLMNQTX]bipx|zwuromkigeca`_]\[ZYXWVUTSQPONMMLKKJJJJKLMOQSVY\`dimsx}~}|{zyxwvtsrqonmkjhfdca_][YWUTRPOMLKJIIIIIJLNORUX\`dimsx}~}|{zyxwvutsqpomljigedb`^\[YWUSQOMKIHGEDCCBBCCDEGILORVZ^bglqw|}{ywvtssrrqrrrrsrrqpnmjgda]YURNKIGEDCDDEFGHIJKKLLLLMNOQTX]biow|zwuromjhfdb`_]\[YXWVUTSRQPONNMLLKKJJKKLMNPRUW[^bfjosx}~}|{zxwvutrqonmkihfdca_][YXVTRQONMLKJJJJJKLNORTW[^bfjoty~}|{zyxwutsrponlkihfdca`^\ZXVTSQOMKJHGEDCBBAAABCDEGILORVZ^cglqv{~|zxvutsrqqqqqrqqqpomkifc`\YUQNKGECBBBBCDEGHIJKKLLLMNNPRUY^cipw~~{xuspmjhec`^\[YWUTRQPONMLKKJIIHHHHHHIJKLNPRUX[_bgkoty~}|{yxwutrqpnljigedb`^\[YWUTRPONLKKJIIIJJKLNPRUW[^bfjnsx|~}|{yxwutsqpnmkjhgeca`^\ZXWUSQONLJHGFEDCBBBBBCDEGHKMPSW[_cglqv{}zxvusrqqqpppppoonmkigda^[WSPLIGECBABBCDEGHJKLMMNNOOQRTWZ_djpw~|zwtqnkhec`^\ZXVUSRPONMLKKJIIHHHHHHHIJKLNPRTWZ]`dimqv{~}{zxwutrqomljhfeca_^\ZXVUSRPOMLKJJIIIIJJKMOPSVX[_cgkotx|~}{zxwutrpomljhgeca`^\ZXVUSQOMLJHGEDCBA@@@?@@ABDEGJLORVZ]bfkoty~}{ywvtssrrqqqpponmljgeb^[XTQMJGECBAAABCDEGHJKLLMNNOOQRTW[_djpw}~{xvspmjgda_\ZWUSRPNMLKJIHGGFFFFFFGGHIJLNPRUWZ^aeimqvz~|{yxvusqpnlkigedb`^\[YWVTRQONMLKKJJJJJKLMNPRTWY]`dglpty}~|{yxvtsqpnlkigedb`^\ZXWUSQONLKIGFEDCBAA@@@AABCEGIKNQTW[_cglpuy~}zxwutrrqpppoonmlkihec`\YVROKHFCB@@@@ABCEGHJKLMNOOPPRSUX\`ejpw~~{yvspmjgda^[YVTRONLJIHFEEDCCCCCCCDEFGIKMOQTWZ^aeimrv{}{zxvtrqomkihfdb`^][YWVTRQONMLKJIIIIIIJKLNPRTWZ]`dglotx|}{ywvtrpomkigedb`^\ZXWUSQOMKJHGEDCBA@@????@@BCDFHKMPTW[^bfkotx}}{ywvutsrrqqpoonlkifda^[WTPMJGECAA@@ABCDFHIKLMNOPPQRSTWY]aekqw}}{xurolifc`]ZXUSQNMKIHGFEDCCCBCCCDEFGHJLNPSVX\_cfjosw|~|zxwusqonljhfdb`_][YWUTRQONMLKJJIIIIJJKLMOQSUX[^behlpty}~}{ywusqonljhfdb`^\ZXVTRPOMKJHGEDCAA@???>??@ABCEGIKNQTX[_cgkotx}~{zxvutsrqqponmlkigeb_\YUROKHFCA@???@ABDFGIJLMNOPPQRSUWZ]aekpv}~|yvspmjgda^[XURPNLJHFEDCBAA@@AAABCDEGIKMORUX[_bfjnrw{}{ywusqomkigeca_^\ZXWUSRPONMLKJJIIIIJJLMNPRTVY[_beimpty|~|zxvtrpnljhfdb`^\ZXVTRQOMKJHGEDCA@@?>>>>>??@BCEGILNQTX[_cgkotx}~{zxwutssrqponmkjhec`]ZWSPLIGDBA@???@BCEFHJKMNOPPQRSTVXZ^bfkqw}}{xurolifc_]YVTQOLJHGEDCBA@@@@@@ABCDEGIKMPRUX[_cfjnrw{|zxvtrpnljhfdb`^\ZXWUSRPONMKKJIIIIIIJJKLNPRTVY\_beilptx|}{ywusqnljhfdb`^\ZXVTRPNLJIGFDCBA@??>>>>>?@ABDEGJLORUX[_cgkosx|~|zywvtssrqponmkigeb_\XURNKHFDBA@?@@ABDEGIJLMNOPPQRSTVX[^bfkqw|}{yvspmjgda]ZWUROMJHFECBA@??????@ABCDFHJMORUX[_bfjnrw{|zxvtrpmkigeca_][YXVTSQPNMLKJJIIIIIIJKLMOPRTWZ\`cfimqty||zxvtqomkifdb`^\ZWUSRPNLJHGEDCA@?>>=====>>?@BCEGILORUX\_cgkptx|}{yxwvtssqponljhfca^[WTQMJGECA@????ABCEGHJKMNOPQRRTUVY[_bgkqv|}{xvspmjfc`]ZWTQOLJHFDCA@??>>>>?@@BCDFHJMORUX[_cfjnrv{|zxvtromkigec`_][YWUTRQONMLKJIIIIIIJJKLNOQSUWZ]`cfimqtx|}{yvtromkifdb`^[YWUSQOMKJHGEDBA@??>>===>>?@ACDFHJMPRVY\`dhkptx|~|zyxvutsrqomljgeb_\YUROLIFDBA????@ACDFHIKLNOPQQRSTUWY\_cglqw|~|zxuspmjgd`]ZWTQOLJGEDBA?>>====>>?@BCEGILNQTW[^bfjnrvz~}zxusqoljhfca_][YWVTRQONMLKJIIHHHHIIJKLMOQSUXZ]`cfjmqtx|}{xvtqoljheca_\ZXVTRPNLJHGEDBA@?>>==<<==>?@ABDFHJMPSVY\`dhlptx|~|{yxwutsrpomkigda_[XURNKHFDB@????@ABDFHIKLNOPQRSSUVWZ\_cglqv|~|zwurolifc`]ZWTQOLJHFDBA@?>>>>>>?@ABDFHJLORTX[^cfjnrvz~~|ywurpnkigeca_][YWVUSRPONMLLKJJJJJKKLMNPQSUWZ\_adgknquy|~|zwusqnljheca_][YWUSQONLKIHGEDCCBBBBBBBCDEFHIKMORUWZ]adhkoswz~}{zxwutrpomkifda_\YWTQOMKIHGFFEFFGHIJKMNOPQRTUVWYZ\^adhkosx|~|zxvtrpmkhfca^\YWUSQONLKJIHGGFFFGGHHIKLNPRTVY[^adgjnqux|}{ywusqnljhgeca_]\ZYWVUSRQPPOONNNNNOPPQRTUWYZ]_adgiloruy|~|zxusqomkigeca_]\ZXVUSRQPNMLLKJJIIIIJJKLMNOQRTVX[]`behkorux|~|{ywvtsqomljheca_][XVTSQPOONNMMNNOOPQRSTUVXYZ[]^`bdfiloruy}}|zxvtrpnljhfdb`^\ZXVUTRQPONNMMMMMNNOPQRTUWY[]_bdgjlorux|~}zywusqomkihfdca_^][ZYXVVUTSSSRRRRSSTUVWXY[\^`bdgilnqtvy|}{ywusqomljhfdca_^\[YXWVUTSRQQQQPPPQQQRSTUVWYZ\^`begiloqtwz}}|zywutrqomkihfdb`^][YXWVUTSSSSSSTTUVVWXYZ[\^_`bcegilnqtwz}}|zxvusqonljhfeca`^][ZYXWVUTTSSSSSSTTUVWXZ[]^`bdgiknpsvx{~~|{ywusrpnmkihfecba_^]\[ZYYXWWWXWXXXYYZ[\]^_abdfhjlnpsuwz|~|zxwusrpnmkihfecba_^]\[ZYXXWWVVVVVVVWXXYZ[\^_abdfhjloqsvx{~~|{ywvtsqpnlkihfdca`_]\[ZYYXXXXXXXXYZZ[\]^_`abdeghjlnqsvx{}}|zxwutrpomkjhgedba`_^\\[ZYYXXXXXXYYZ[[\]_`acdfhjlnprtwy{~~|{yxvtsqpnmkjigfedcaa`_^]]\\\[[[[[\\]]^_`abdeghjlnprtvxz}}{zxvusrpomlkihgedcba`_^]]\\[[[[Z[[[[\]]^_`bcdfgikmoqsuwy|~~}{zxwutrqonmkjhgfdcba`_^^]]\\\\\\]]^__`abcdefgijlmoqsuwy{~}|zywvtsrponlkjhgfedcba`__^^]]]]]]]^^__`abcefgijlnpqsuwy{~~|{zxwutrqpnmljihgfedcbba```________``abbcdeghiklnpqsuwy{}}|zywvusrponmkjihgfedcbaa``___^^^^___``bccdeghiklnoqsuvxz|~~}|zyxvusrqpomlkjigfeedcbbaaa`````aaabccdeffgijklnoqrtvxz|~~|{zxwvtsrqonmlkihggfedccbbaaaaaaaaabbcddefgijkmnpqsuvxz|~~}{zywvutrqponlkjihhgfeeddccbbbbbbbcccdeefhijklmoprsuvxz{}~|{zxwvusrqponmlkjihggfeeddcccccccccddeefghijklmnpqstvxy{}~}|{zxwvusrqponmlkjihggfeeddddccddddeeffghhijklmopqstvwyz|~~|{zyxvutsrqpnmllkjihggffeeddddddddeeefgghijkmnoprstvwyz|~~}|zyxwvusrqponnmlkjiihhggffffeeeffffgghiijklmnoprstvwyz|}~}|{yxwvutsrqponmlkjjihhgggffffffffffgghiijklmnopqstuwxz{}~~|{zyxwvutsrqponmlkjjiihhggggfffgggghhiijkklmnpqrstvwxz{}~~}|{yxwvutsrqponmmllkjjiihhhhgggghhhhiijkklmnopqrstvwxz{|~~}|{zyxwvutsrqponnmllkjjjiiihhhhhhhiiijjkllmnopqrstvwxy{|}~}|{zyxwvutsrqpoonmmlkkjjiiiihhhhhiiijjjkllmnopqrstvwxy{|}~~}|{zyxwvutsrqpoonmmllkkjjjjiiiijjjjkkklmmnoopqrstuwxyz{}~~}|{zyxwvuttsrqpponnmlllkkjjjjjjjjjjkkllmmnoopqrstuwxyz{}~}}|{zyxwvutsrrqpoonnmmllkkkkkjjkkkkkllmmnnopqqrstuwxyz{|~~}|{zyxwvuttsrqppoonmmmmlllkkkkkkllllmmnnoppqrstuvwxyz{|}~}|{zyyxwvuttsrqqpoonnmmmlllllllllllmmnnoopqqrstuvwxyz{|}~~}|{{zyxwvvutsrrqppoonnmmmmllllllllmmmnnoopqqrstuvvwyz{|}~~}|{zyxxwvuttsrrqppoonnnmmmmmmmmmmmnnnooppqrrstuvwxyz{|}~~}|{zzyxwvvuttsrrqqppoonnnnmmmmmmnnnnooopqqrrstuvwwyy{z~{wzxvu{~w{s{xnrso}ukhw]nl_uq]empVcvh]jmqoprsqnWYm{n~W\mxo|wxxl}rw}z~ns|^|giplsfyvbvrosooyS}}eUZyurTUkmQ_zff]j]vxnuevrzpo|}pszq~yh{xYry~}q~o`ipqwtuigyr`]^XpZ\hxzdpgji[nqs}rkcbu}y~}{wxny~e}sfxkomchos{kuaqiiWdq]PPZxYobkxwpytx|j|oqz}e|sxxmts}}pzuul~ryqzqq~\fyxnvgnybwcWm}tg[bdo|su\mzyz[\uVoic{g{upmi`x~{tjz~pmklv~`wyhhanay{Lrh_dx|xflvfbvjexzh}xuidwVc~yq}q|}pytctsy]lwm}~ucvmyUweaO`kcIabtHe]tmivOnsKxuhmVayn}aZjxhwoUkc^wl~dgsqeqv|n]eld|ZFj~Zvm\htbboc{nXpcoTbqrtdo_pnlp|[puygn{{l~ztb{ns|ZwpiztnTRNC~k|m|w|u@RWcqojcipn}s`jkzvxo}km~z||dsy]x\jirxf}g{gr[og~jeZ_kftdx}dbiuemudamfO~pnlZi}l[ixfeft]{m}}TrXmmfwNQn~_tmYp[Wi|butfLc\gvjn|skpyql[[sm`qagkrye`|yeey`]o~~kyOy~shSXVpbrb]RH{kvrux_f~lg{xwwdVeifeq|cV~g|{ozoxq~iqġuwphɥTpmylusfPTnjkj[uuw~pajlajep~yh~Vqun>[L5xj}IvuvtՎ_|orltc`kzpzOh`|hd{@lrg~v:auiargthtbjvewk`vexXxjj_}Vqk`dRp<|uzlnl^sgWhr|Y}U~kU\u_^pzUYz_lqy]fsT[_}tkTRceohAjVuwkzozzǙ}glyyrkqlwvrai|kq`cnyp_qy`gVbnexq[V}aT}ywu{}YsioW_\ms||mivwtgvM}ppn{|XhBx^fhqyw^E\RGivbcxkkW_miw9t\s|j~Zouy}v`khap}Ns~|ip~dRYpodk>{tRrnQe~ssw|vasos~Iuyt}~cozlod_R^ep`oego~zko`mwrdixw~yxRtf/|zpwS{vYyewg}IsujUujv`PiXme|Sth[Ytnwm}RiRftd{mkj`Ѓqe_~nf[zzfzSotrv`|zhF}Zu}^qymuaJbvyTr7xypTkeftapnNz^{~{~s~Tj_}_gYhWz~sUsjvLjmkil`Xm~Slk\Pc|i~xkpg\qZnwZgwbuYtjm~~nWotIlp}XI{Ertvas^qi}Jz}_W~iwdfYRy}}kyy{~F^WhmkboG~xJinhxxvi^[uOotvXxWgzqjMqv{lZX}zrNvnctXezwttwk|sjlg|dyoK|_bwmrhatZZlysLoysTuOsxb{|uceg~^pWfjptnn}|qp}qk}ZO_nl^uHVwfuqytwapbtzbi}qzVe{0zx}}Ztdwoxizfh_[|uOi~Yurp}k]u}eX}rzzcxnIj@ywxm]~zrw_vphdm[yg{opu`sd{u`kw}nwh{nrhh^_gs{ngnxiqmqnwiQQgsvznyfvrrzwZ}r{phl`iLPYjn|gy^lsFxo~iqogrbVij~g}uuksbzz}qlhuU}\x`qeolFml|vj}rtrqmtlfzyMnovgrowGvk{bop~~k|n~XzzyiyuiqvzatJswXs|YOtde`cdoctqyq]a~qwwTdqNkzq|o~bhtRiexassnOwRgYpk~x_teibxrp_ssmoqmxgsyjw~why~iq~rg|fqhid|mddnu}amTY|qw~fws^k{kYWlrqi~Wovs^sdM]xoxjuv|\nsaWtu{yxlh{doS~jlfzfvwzyic^z{nz^[man|]x}st{~wpas[{xbtmdvw{uyorrvx{brwuw~foX]Xfvicl}au|yyjkvVw~ylTbv~}tmyfhwguw{wu_tRz\ayY{xdy}\~rntfvmjpumtgrod{_|{xwh_{vts|xevnhpmiz~aurwr|r||mkxt~~cgsxmsh|jkjy~nn}spxyqy|vvvyuzgf]lpi{~tdxcRsawqlgpwy`vybykegpRxm|hp|j}xz~cl|oiwbmqoi|te~yzjk|qcksRnpudyur[yu\qmninr~{|{nykowgbugku{||R|cc}{{ypa{woz{rr|wjzxknj{`nwvnkjq~aywpv{s}uqmk~szvzwskdvu~_ijn|xkgXgftzyf~prxlbc~x~x~}olhutsz~`|}sxoowx}nxfmda~mvw{yor~}o]ztt~{~~|{ZPtavfvsz{inltpspkvsw\uzuwv}k}~_xzwj~sdito|}{k}lpvtmlrxx|q\k}r[~yxvmm}m}gkfu}~yo|s|o|ys}nuzb|v}twYr}R~wwvj~xty}pmsvlu`ej`h|uus}~uqni{rsfy}~vnmrdZmslehu~sr_hvukswqc}x~~{xlnhdhz|rrrs~rr~yrw~nr|`y|b}y}xvzwzoizzy~o{ywUbx{utlisp|d|ri}knqoout{u{ntfzwo||~|xjm|orvasp}qjsau~ufx|wrv|iuywvtxwcw~m{qrgxxzdr|}}~w~ty}wq}koznxvswys|ygun{|{j~trt~kq|~q|{j}`m{mexz{yexxtljxth}vrqsiv{jxtymyynwm_xnccszr|ukrsxwxzz{t|vyrs~umqluvmzs|w|rgzmwuy|xey}q{t~r|s|ipr`~wc{|~ry~|wtrjx||xjqtpympqvkwmx}ux}y|fh{~zvwwvghzhsnxsx~jk}}zmmm~rlq|{}yp~|x{n{l`nu}|m{cv{nwzro}zt}nj}x|ropu{mh~wwruwyq}wowox}|udretivlnl\y~~}}vso}}kw~}tn|~wrmsm}ofr_kwkmm{uw}xnqwzyvrzwv|h}q~|qzqphzfhtxsx~|shyqtnu~w|gry|r}r{wziqx|qixjo~niux{vzg~izoucuwxrxy}tqx{m}z~rvx}}lhfxqowzzl}}srok{ntxy|vpt~{uqhuf~{vzwu}yn{}}`|zg`{qk}|{u~yq|y}w|}_uvv|vutzjxz|s|vnoisltsq~u{uv{q~zr{sp}|~nvu|lb|sUgvisuo~xyr}||yut|{xjv~|t|{nwvzsonjtmy{t|duqvzvurys}z~e{jy|}yz}ztmorp{y~z{gmmx~s|a{y|g~sqmr{{}}jw{pzp{}izryltmv}{gttev|qzv~}~}tl~}xx{jrp}|kvoytwu|i{x|lyr{u||s}|xnm}}x|{~}v|xxzyhz|w|uuxsxs}x{mq~oywzx|s~lu~}sw{vvtyom|~oxyrq{zrtqpsewqwu~|tswm{}{~yuut{sn|q{zvwxr{l~{z}v{lpz{whsz|w}zy|~||~{szxvp{k~zorwt~v|w}rmvosvvwyv}{|||py|x|}x}}owuwsr}~}z{nsvzy|mwvrz|vw|r~qy{w~m{t{tzyzru}v|v{gzzgpl|w{}zz{tm}|x}~xpzqzz||zzox{gy|xnzmpy|zrn}zsxlyn|t}xu}|wut|ocwo{xzo}{ro~pux|~snvolq}{wntxrwnky}~wp{rr|zzwlw{om}n|w~{xxz{}z~}sp|v}wz}vs|qrv||~~zytyupuwutwgyt~sp~vwyov{z}xoorsvy~wov|zk~oun}vyuuly{{nvpokz}sowm~{~sln|nw~ok|nqruvy~uuofzyb{wkux~}tz{wxyypt~|k|{o~tzuzo}typsu|}wv|ywqw~~{uouwfy{uz{~luz{zpx{{q}qm{m}~q|umsr}oojwj~z}lj|tvvp|~}||t|kxw|nqxe{tyxlxns||~rzuwv{|vtzqusxb}sjksnq}youu~|yxttju}{~u~toto~sor|zn|uqoyz}~xy~zpx{ik{vtv}wf}t}|x{{~v{s{xot|~~uz~wjq~}~pjmlz||pcvp}xt|q{o~l`~}ov|zrrr}vlnpsuuz}yywnxqv~sx||x~th~ru]{q}ssu|z}r~vy{yswjv{tu{a~|wv|to|xrzrty}yx{{uksqq}}vreyxt{|tx{~~xy|qw{wsjyxuzxuu~sqxkwmw}{}`ttj~pz~}qpwo}vyz}|shxsytts}ut}|ppri|}lxl{}q~wpzmuyoplz|zw|s|~n{pwz^n{zwzzspsz|mw|uzoz}vy{ph|uwxz}gxvq}}ejkw}~zqvm~yxuwxyzt}}dvuvpv{|sqls}pt~dou}xv~zxzzz}|}o~}{~zxyuvyxvy}me}zsyyxmnrwzs}|x|v{}}xrwiryt~v|~yv{{tu}sazxz~~yswyxtt{om~loi{krt~{z{|rsqysxqjq~uq~swumyxs{z|uup}zxhxlv}}}t{^wxzyi|y~trw}x}}tnu~mz~oxzmvtvwowmfwucxow||~x{r~}~wstp}p|nvp~tksnmy{tny{rysgu}~po}jsqzxn{zz{jx}xjtwnwv{tq}ji}~{{vsorso}pxz}eyp{zwzxiywt{}|}s~y}|u}vwzlm|y{q{ps{lu|y{{~ykuq{|y}{~v{xt}|q{sqwxqzp}ty}{|kwxp~z}v}q|l~v{}z}{~}yupset{{pwm}lvxhr~|||z}fqz{xsv~|s~zsrlytotc|vsxvzoxwxw{r|svu|xpyuzmsm}{xhjxn~~{ywx{y{y|ww|n}q~yqxr{uqf{}qtn}|zxzwwvy||mwyv|xl}u{ztxtxi|xz~xqsut}~vyto}ztp|x}vrwotz{uhu}{u}t~riwxz}v~rwoxi}x{}txxr}uz{voy~{xzzixx}uxy{w}o{m}xpux}t}yvtvo}tuxts|}zo~tqs}{{vz~vzl|xzx{|{y|}nowvrx}t{|~p{u}r~t~y~|ymiw|{zyv}{}{||qk}p}{{{w|mxuzxwxw}z|~{nzv~|xour}uzz|ptx~xs{wizuw~tz}vpyuwu|~xr}|x|zynst}|y{psz|t}upvot{q{m}|}yz}w|t}~z{tvxy{{~~yv{y}uurvxyux}|viy|~~wx~lru}|tz{z}y~z~xo~pus{w|u{}}xe{|mx~~||zm|w}yn{yw~|t{||ps{v~x~wxv~yvq}i~~{xo{ps|{qwqt|{ww~tuosx|u|y{s{}rruxvvytwr{|s{~uus|~ywnyznuzw}|z{ws~y}}~}yxj~}|yz~u}s|yyz{wsxxrqv~ulz|qyz}u~y{}vyvro}ry|}vww{xw~y||}yyyu~vx{{zs{|ts{}qzu{qu{y{wz}w~xuwz~}}{|~myw{~xvyxm~}uzymu}|wxxoww}}zxx}t~z{v||t{t|{x{zi~zq|q{xtw{xrj~~|{~{w|x{}{{tys{wz{}s}pyru}x~pi{}pw{w|s}}r{}x|}z}r~u}x~vl~}x{w{z|tqs~t{~r~~~wzvy~~yx~wtuwz}svwfxxtt{y||{}{ztux}t|xtz}}~}zux|}{}w~~{ts{vw~v~w}~s|ymzrvuxywwxvz|z{}{{sy~{wvx}t|z}|ux{}}ssv}~}u}zwzq{zvx|}ywt{~yxx~|{uz}ps|{{{|w|uzxrv}z|tn|s{s~xs}zzrlwzs{yzxy|o}v~zz|zv|{{xx{}z|t~||}|sx{p}xwxp~|xkq~z}wy~y}}~~yzwp}s{ozw~wyswq|y{yyx~z{|viy~~}~}|}{}zu|xwzt{|tv~r~}zwwwwz{~||}{~~}~{~r{{v{vpxrs}}zsz}zzqxyt}v}yv}{x}z}~z|xv}wx~z~~{wxouz~u{tv|xzyyx|xy|||x}x}usx~urw}zrw{|y{~|w{{vsxxx||w{u|u|wxzvz{xy}wy}x}|yxx}|w{||~~sw~y}x{}w~z~ot{suv}y|}v}{x}{~svyw}}zvy~yy{}~wtxzw{}yv}ys}{}||x|z~u|~~t~w|{wtuw{xzrx|~~|wtx{xy~}x|}z{vuwzy|{u~~y~~or|vxv|yrv|yw}~zx~~~}|yxx|~~{z~uw}}{{yqx{t}{s~w|twu|{}}}|{z}x~zy|{}x}wzr|}{oq{}}r}y{t{uxzwywwyyu}~~~uz~}{pq}}||w{}vn|}||usz{z~~{xwyz}~z|w|uous{}}x~w~}}v}|qso~{~~{wy}zxuzy{{xxuyyy{x~|yvzyvyuz|~z{~w}}y{{xz}wsvw~|x~~}ut}zxw{~y~px|~~z~qy{vy|~||yszyy|z{||zzw|tu}~v{{w{~}vwxz||{|vvlz|||yw}wxvszs|xxzt~y~zw{s~{pxxz}uz~~~|qz{|}~xz}{~xy}}|uyp{{r{||{wv|~|}~|{xx~{y}xsx~uvu{z{|~wsx~|||y~tqz{up}{{|rx|x|{wp{s~~~w}~vy{}|yv{y}{z|z~~{z}wv~}{~~{~q{~~w~t~~z}}}~uuux~wx~~{z}~uv}~|}{yy}}s{s}|zx~~~~{tv}zw~~~|~ou~~svpu{y|zvutu~|}{up|~~t{|||{{~zt|y|||yyx|~}}yo}x{~xt~{|~||v}{x{zyyz{yw|wx}}v}|{}~ypur~~}t}yxv}|~wyyv}|xzzsw}w|u~}|}~|||~}wy~}zxk|||}uvwz~|uwvuy|w~}}vrx~~z|}~}~}z~ox~}z}uyy||~vo{xz||}sw|v{x~}~x~qzzx~}}~w|}u{|y}||zyv~|u}}uy~z{~x{w{{w~}{~s}}}wzyvzw}z|~uww{y{uxv~~}|{y{xz~{wxsy|vwz~v|z||}}{|vx}|~~}zy}~{w~wzy~z~y~z~}xumy}~z||nuzyv~~xyx~}~{|{x}{y|}|{~z}y|ww~ywwntvyy~y}w|~xy|v~yy~zu{}u|{~{~qy}yz{y~xy}}{}xz~}z~z~yx|zy}|y{{{zy{~||~u{xu~x}y|~~~vz{|{{x~}xx{xx{u~zs|upsu|{}~w~y{~|u{v~~{||~wvzuxst|outpzv}{|~yz{x}}|~z}vv{~~}x{}}|yrr~~~{uwxv}v{~~}||z{z|v~|}~wyr~z~||zz~y~|}{z{y~}|~xx}|yzwxx~~{ovqy{}}~|{z|{|vzx||v{w}}}zzxtxyzztnxv{~~|v}{y}~}y}y}|y}|{yz~yv~}}}|{|vx{|~}z~ss{zy|{x~|}|x}z{w}~~}}|~}x}tz~w}}{rs|~~yuz~ypv|o}~q{{~~zz|}z~~xz~swv|}{y~}}w}v|v|}|~}xvtqx~|wz|~~yz~}~|x|wzv}{~w}v~}~}v{szx}||~zu~u}~}zx|~{{xvrtz~~x}{}}}{rxw}y}}vx}{}~{~{u|{zszs{z~{|t}x}~~}w|zy}z}~y|y{~vy|wv}|s{vzx~{~yzxw|~x~{xuxy}~}}yv{z}|{{~~xxxs|x}}}}z}wxy~}wxs}z~xux}zy}yy}z}xws}~|txv|{~vz~~~yxs~wz~|~yy|{}~}{~~|}ywvy{{~~~|sz}|x|yx~xt|z{zyxzy|}~}szw{w}||w||y}~z{|x~{~s{s{z||xtxxy|r}v||~||}{v{|}~|z~{~z{wyxz||w|~v~x~~}}|~r|v|ws~~}u}x~{~xy}~{{{vzy}|uz|yw{~|~{x~}xw}~u}y~xyvx|~zzyow{~y~zxzu~x}}{}zws}u}yszv}~}yy|u{|}}}{|yz~~~}}y|w|zy~|}~|zwx~{}~}v}|}xw{|||~||~{xy}~}x~}~~|zyu}v~~{{|{~|{}|}{zyv|uy|z}y{uz{{vutz~}~~{}vz{~z~z|~|{v~u}{~~y{{||}}}y~tzyz|vw|yyxyx|{y}~z{|v|y}y~}{|}~}}t|}~|}{w{x}v~yxx}zz{~}}zy{ypyx}}x~|z||{}~{w}~yvv|~{}wvzxyx|~}y}zx{~{~zz}{}|~{{z{{v}u}y{~|}tvx}|}{wwwu}~}zy{x|~yx~zvw|~~}szyyuur{w|}|zx~|}~zy~}}}~~w{|z||~}w{{}~{ux~~|ww}{~}{~}~{}zy}{w~x|~~z{{{{}~}}}}~|uy{|~x}z|~xt}z|~}}wwsvyx|}z~z}|{|{}|~~{{}u{{y|vyy}|zrxvu~x}z|}}w}~z}~~z{z{||z|}|wzw{~y~wz}z||{{z}}z|w~||}}{~x~|~z|}}zz{|}z|||||{xy}z|}|~~}~{~}tuy}|x|zw{zy~xz||s|~vy}}}{z{|tyv}~z{qtrw{~||~v~~|x}||x~{y~}}|{ww|~|}y~~{zw}zyzz{~~~{~zvyw{{{{}{|||y{}~}}zyv~}zz{}~~{yyywyrxuz|z{{yy|{{{}{}~|~~|u{{x{~}wuzy|}}v}}x}y{{z||}~|}v~~|}~~x}|vz{z|~}~||{|zw|}{{}|w~{~~u~}~~{{~||~u}zw||wu~zz}||t~x|{zu{~{{}}z}|~}|}|}|~z~~z{|||~~~zw{v{}~v~uxuz|{|}{}~~}~}~~{}zx}y|}{x|~|~x{~|w{yzz{~|{{qy{}}}|~~z}~~~ywuy||{z~~}uu}}z|z{w}{|{yz~~w~|y~zx}{x}}{}}~z{w}{~{{}~}|}~zzyy{z{z}~zyx|x{|}}|}|{~zz{{}zz~|~}z|||xz|tz{}xzy~}z{y}~x}w{z~|{}~~|wz~x}x{{|z{xz}~}|~}y}}yzy~{|{~}||~{~{}z}{{yz~}||w~|}}{~{~{yv}|u|v}}~~xuz~|}~~~|}{}||~z{|~zy{v~~}~x{x|}|}{}y|{||z{|z}z~{~y{{|{zx{{~|w~}~|{{}~|~~vz~{}{~~}y}zyyzz}|~~~yvu~~|~~~|{||u~|z}z|~~zw~wzy}}~{}~|yy~xx|{x~}||~~|zy}wy{~{|~{~|~y|zy}|~{wz~~}{}{}{x|}}~|v}{|{{x~~{|zz{}{x}z}}}~~|{{}~~z{|z}~~}|z{x|{y{|}}|{w}x{{~y{{|{z||{~|~~||{y}~~z|~}}|}|{~||}~||}~~xz|{z~{{z|vz|{{{~z~{zyy}}||~}~|}~y}yz}~|zz|~|~{}|{{}|~{w{{~y{xv~|~~|z~z{}}yv~}|y}{~z|}}z}}z|z{~~{y|{zw|}}~yzx|{~{~}|wz|y|}~}zww|~~|||~{z~}~~z~yzyzzy~{{~{wvv~|z~}{}}~~||}~{|y~{}||y{}z|{|||||y~}}~{}}}}~x~}xy{}}||}|}}}|||}~}}{|~~~|}z}x~~{{~~{}{}t{{yy~~}~}{}~{||}}||~{{}zy}{~x~{zw}z}z}yy~y|~}|~~~z}|~y}y~{{}|t}~~|~{~~z~{}{|}~}{~|~}|{~}}|~v||~~~z|{zyz|~|~~}|~||ywy}~~~~{}~~zz}}~|}z}z}|~{}~~yy}~~~v~{{~www||}}||~~~~}|~y{~z}}||~y}}~|}}|{zyyz~vy|}}~}z}}}}z~|~|~~|{zz~|}}~zz~xz}||}}|~||~~~yz}}{{~~z}|{~z|z~~}~}~~}{{{}~||}z|~~|}yz~|z~||~~}~{}{x~~~}{}y}||{z}~}{{|~~{}x{~~|~}x~}~{z||}{|~}y{~~~~}|yz{{||}~|{~{w~~|~|~}}{||~}~~~y||~~}}|}~|y}z|}~{~~zyz{|~~~}z~y}z}}y}~|~}~||~}}}}z{{~z~|xuz|z{}~||~~x}|}}{~}~}}~~}z{{~~~w~{|~{~z{}}~~|~{~~}z~{z}}~}~~}~~|{z{~~~{{~~}|}}{|~{~{{|x~|z|}|}z}~~~||~}~|{|z~~y|x{}||~{}z~z||}{z~~~~{~~}}~}~y~}|}}~~{~|~}y~{|}~}|}}||}y~}{~}~~~|~~}|}}z||~}z}zx~}~}y~}~{yz|{~~~{|y{|||}~}~~}~z|~}~}zy{}~~z{}y}}z|~~{~|}y{~|}}~}}|}|}z{}|~|~}|~}~|z~~|}|{~xzzz|}~~}~}zz{{~}}~{~{{|~~|}|}z}|w{~|}z}{~~z{{{}}~~~~~|||~{}~|{~|z|}~x~}~}~~~||~||~||}}{|~}|{|||}~{}~{z}~{x}~||~~~}}~}{{~}}y~~|||~~}|~}}||}~}~{|}y{||~}|~}}~{z|}|~}}{}~}~|}}~}~|||}}}z}~}||~~|x{{}{}||}z|}|{}|}|}z}~}~y}|~}}{}}{||~{}}|}{{z~}~|~}yz~~}|z|~|~~}}|||{}~~yz|}{}|~~z}{~}~{}~~{|{~~}}~z|~}z|~}{~~~{~~~~y{~{~~|~~~~~~|{{z}}~}~|{}}{{|}}|~~~|{}~~~||{~~}|}~y~}}}~}~~~{z}~|~|~}}x}~}~}~{~~}~~|{~}|~|~~~|{}|z}{}|~}}~}z}}}~|~~{{~|}~~}~~}{{}~}~}z}~{~~|}{{|z~}~||{|{}}}~}~~|~~~}}||x|~}~{}{}~~}~~}|~~{x}~}~~|~~}y|~}||~~}~z~~{{~|~}}~|}y|~}}}~}~~{|}z~~~|}}}}~}}|~~|~}~}~}{|~||~z~}~||{}~|z~}~|~~~|~}}}}~}|~~{z{y~~}~~{}~|~}}~~}~{~||}~~||~}}~~~||{||}}}|~~}~{{~{~|~~~|~z|~~~~~~}}{|||~|~~}}|z|}~~|{~}|~|}||{~~~}~~}|{~||~|||y}}}}~}~}}|}}~~~~|}~~}}~y{}~~|~}{}~~~~~}~|{{~~}~|~~~{|~}||~|}}{}~z{}~{}~~}yz~~}~~}~~}}~}{~{}{}}{|~}~}|{|~{|~~~}~~}~|~}~|}}~}{}|}~~~}|{}{z~||~|~}|~~~{~}}~|}~~~}~~|z||~~{~~~}|}~z~~}~~}~}~~~~}~}{}~~~}~~~~~{}~|~~~~~z|~~|~~{~~~|}}}}}|~}}~~}|{}}~~~~|{z~||}~~|}}|}~~|~}~~~}~}}~z~{~~~~|}|{~~~~~~~~{}|~~z~~|}~|~~}}~}}{}{y}|}}}|}}~}}~}~~~~}||~|~{}~~{{{}}~~|~~}|~|~}}~}~}}~}}~|~|~}|~|}~}~~}z~{}}||~~~~|}~}}~|~}|~}|~~~}|}}|{{}}|}|~}~}~~~~}}~}|~|~}~}}~~}}z~}|~}}~~}~}||~|}|~~|}}~}~}{~|~~~~}~{}|||}~~|~}}~~}~{~~~|~}{~~|~~~}}~z}}~~}}~|}}}}}|}|}~~~}|}}~~z}}~}}}~~}}~~~~~}~}|}~|}~~~}}z|~}}~}~~~|}}}~~~~~~~}~}~~~}|~{|~}z~~~}}~}~~}~|}~~}}~~}}~|~}}}{}~~~~~~~~|{|}~|~}}}}}~}}}}~~}~}|}}}~~~|{~~|}~}|}}~|~~{|}~~~}~}~~~~~{}~}}~~}}|~}~|~}}~|~}~{~~}}}}~~~|~~||~~~~~~~~||~~}}|~~~}}}}~~}~~~}}z|~~~~}~~~}~~~~|~~~}}}~}}~~|~|}}}~~~}}}}~}~~~~}~}}}~}~~}|~~{~{}~~~~~|~~}~|}~~}~~|}~}}~}}}~~~}~~~~{}}~~}~~~|~~~}}}~~~}||~~~~{|}}}~}~}|~~}}~}~}}~}|~}~~~~}}}~}~~~~~}}}}}~}~~~~}~}~}~}{~}|~|~}~}~{~~~~}~|~~}}}}~}~~||~~~~~~}~~{~~~~~~~~}}~~~}~~}~}~}}~}|}}~~}~~}~~}~}~}~~~~~~~~~}}}{~z~~|~~~}~~|}}~~~~}~~}~}}~}}~}~}~}~~}|}}}}~~~}}}~~~|~}|~~~~}}~~~}~~}|}}||~~~}~~|~~~~~~|}~~}~~~~}}~~}}~}~~~~}}~|}~~}}}}~~|}~~}~}~}~~}~~}~}~}~|}~}~~|}~~}~~~~~~|~|}~~~||~~}~}}}}}~~}~}~~~~}~~~~~~~~~~~~~|~{|~}}~}~}~~}}~~~~}~}}~}~~}~~~~~|~~~~~~~~~~}}~~~|~~~~~|}~}~}~}~~}~}|~~~~~}}~}}~|~~}~~}~~~~}}~}~~}}~~|~~~}~}}~~~}~~~|~}~~~~~~}}~}~}|{~~~~~}~~~~~}~~}}}~~}}|~}~~~~}}}~~~}}~~~~~}}~~}~~~~~~|~|}~}~~~~~~~~~~~~}~~~~~~~}}}|~~~~}~~}~~~~~~}~~~~~~|~}~~~~~~}~}~}~~|~}~~~}}}~~~~~~~~~~~}~~~~~|~}}}~}~~~~|~~~~~}~}}~~}~~}~~~~~~}~~|~~~~~~~~~~~~~}}~~~}}~~~~~}~}~~~~~~~}}~}|~~~}~}~~}~~~}~~~~~~~~}~~~~~~~}}}~}~}~~~}~~~~~~~~~~~}|}}~}~}~}~}~~~~~}~~~~~}~~~~~~~~~~}~~~}|}~}~~}~~}}~~~~~~~~}~~~~~~}~~~~~~|}~~~~~~}~~~}~~}~}}}~~~~~~~~~~~~~~~~}}}|~~~~}~~}~~~}~~~~~~~~~~~~}~}~~~~~}~~~~~~~~~~}}~~~~~~~~}}~~~~~~}~~~~~~}~~~~~~~~~~~~~~~~|~}~}~~~~}~~~~~~}}~~~~~}~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m54.bin b/applications/SprPoP/assets/packed/MUS/m54.bin new file mode 100644 index 0000000..5d9b1ae --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m54.bin @@ -0,0 +1 @@ +~}||{{zzzzyyzyxxxvuusqpoptz~||}~}{ywutsrssstvwxz|}~~|{ywusrpomlkkkkkmrv{|o|{emx|zxvtsrqrstuwz}wqz|z{~}{yxwwvvvwxyz|}uhfttjfegiknqsuvwxxxxxwuuuuuuvvwxy{|}~~~}}}}}~~}xrps}}|||}~~~~~~~tlffmzwqligeeeeeeffgghhiijjklmnoprstvwxz{{|}~~~}{wtpoqv}~~~~~~~~xrmhffjq{~yuqnljhggffffffgghijjlmnoprstuvxyz{{|}}~~~|zwtqpoqv}~~~}xrmhffipz|wspnkjihgfffffgghiijklnopqrtuvwxyz{||}}~~~}{yvsqooqv{|wqlhffiox~zvrpolkjihhgggghhijjklmnpqrstvwxyzz{||}~~~~|{xvsqooqu{~zupkhffiow~zvspnlkjihhhhhhiijjklmnoqrstuvwxyz{||}}~~~}|zxvsqooqtz~ytokhffinv|xurpnlkjiiihhiiijkklmnoprstuvwxyz{{|}}~~~~~}{ywuspooqty}xtokhffinu~{xvsqonlkkjjjjjjkklmnopqrstuvwxyyz{||}}~~~~~}|{ywusqooqtx~~zvrnjgffint|~{xusqonmlkkkkkkkllmnopqrstuvwwxyz{{|}}}~~~~~~}|zywurqoopsx}}yuqmjgffims{}zwusqonmllkkkkllmmnopqrstuvvwxyz{{||}}}~~~~~~~}|{zxvtrpoopsw}|yuqmjgffhmsz}{xvtrqponmmmmmmnnoppqrstuvvwxyzz{||}}}~~~~~~}}|{yxvtrpoopsw|}zwspligffhlry|zwutrqponnnnnnnoopqqrstuvvwxyzz{{||}}}~~~~}}|{zywvtrpoopsv{}yvsoligffhlrx~|ywutrqppoooooooppqrrstuvvwxyyz{{||}}}}}}}}||{zxwusrpooprvz|yuroligffhlqw|zxvutsrqqppppqqqrssttuvwwxyyz{{|||}}}}}}||{zyxwusrpooprvz}zwtqnkhgffhlqw}~|zxvutsrrqqqqqrrrsttuuvwwxyyz{{|||}}}}}}||{zyxvusqpoopruy~~|yvspmkhgffhkpv}}{yxvuttsrrrrrrrssttuvvwxxyyz{{{||||}||||{{zywvtsqpoopruy}}{xvspmjhgffhkou{~|zyxwvutttttttttuuvvwwxyyzz{{{|||||||||{zyxwvtsqpooprux}~{ywtqoljhgffhkot{~|zyxwvvuuttttuuuvvwwxxyyzz{{{||||||||{{zyxwvtsqpooprtx|}{xvsqnljhgffhkotz}|zyxwwvvuuuuuuvvwwwxxyyzz{{{|||||||{{zzyxvutrqpooprtw{~|zxuspnlihfffhknsy~}|zyyxxwwwwwwwwwxxyyyzz{{{{|||||||{{zyywvutrqppoprtw{~|zxvtromkigffghknsx~~}|{zyxxxwwwwwxxxxyyzzz{{{{||||||{{{zyxwvutrqpppprtwz~}{zxusqomkigffghjnrw}~}|{zyyyxxxxxxxyyyyzzz{{{|||||||{{zzyxwvusrqpppprtvz~~|{ywusqoljigffghjnrw|~}|{{zzzyyyyyzzzzz{{{{|||||||{{{zzyxwvusrqpppprtvy}~|{ywutrpnljhgffghjmrv|~}||{{zzzzzzzzz{{{{{||||||||{{{zyyxwvusrqppppqsvy|~}{zxwusqomkjhgffghjmqv{~}}||{{{{zz{{{{{{{{||||||||{{zzyxxwutsrqpppprsvy|~}|{yxvtsqomkjhgffghjmquz~~}}|||||||||||||||||||||{{zzyxwvutsrqppppqsux|~}{yxvusqpnlkihgffghjmpuy~~~}}|||||||||||||||||||{{zzyxwvutsrqppppqsux{~~}|{zywvtsqonljihgffghjmpty~~~}}}}}}||||||||||||||{{zzyxwvutsrqqppqrsux{~~}||{yxwutrqomljihgffghjlptx}~~~~}}}}}}}}}}}||||{{zzyxwvutsrqqppqrsuwz}~}}|{zywvtsqpnmkjhggffghjlpsx|~~~~~}}}}}}}}}|||{{zzyxwvutsrqqppqrsuwz}~~~}|{{zyxvutrqonlkjhggffghjlosw{~~~~~~~}}}}}|||{{zzyxwvutsrqqqpqrsuwz}~~~}||{zyxwvusrqonlkihggffghjlorv{~~~~~}}}}||{{zzyxwvutsrrqqqqrsuwy|~~~}||{zzyxwutrqpnmljihggffghjlorvz~~~~}}}||{{zzyxwvutsrrqqqqrsuwy|~~~}}}|{{zyxwwutsrqonmkjihggffghjlorvz~~~~~}}}|{{zzyxwvutssrqqqqrsuvy{~~~~}}}||{zzyxwvutsrponlkjihggffghilnquy}~~~}}||{zzyxwvutssrqqqqrstvy{~~~~}}}||{{zyxxwvutrqpnmlkjihgfffghjlnquy}~~}}||{zzyxwvuttsrqqqqrstvx{}~~~}}||{{zzyxwwvutsrponmlkihhgfffghjlnqtx|~~}}||{{zyxwvuutsrrqqqrstvx{}~~}}|||{zzyyxwvutsrqponmljihhgfffghiknqtx{~~}}|{{zyxwvvutsrrqqrrstvxz}~~~}}||{{zyyxwwvutsrqonmlkjihggffgghjknqtw{~~}}|{{zyxwwvutsrrrqrrstvxz|~~~}}||{{zyyxwwvutsrqponmlkjihggffgghiknpswz~~~}||{zyxxwvutssrrrrrstvxz|~~}{zxwvsrqnlkigedbbbbdgjmopppprty|ywwry~}{zyxxxxyzz{|||||||||}~}zxwvutttuuuuuuutsrqonmkjjklnprsne^fvj]bgnsx|~|~}{zxwutsrrrrrrstuwyz|~~~}xwxyyzz{{{zzzzzyyyyyyxxxwvutsrqpnmmlkjigeb``dkqtssrsux{yuy~wtqpppppppppppoonoooopqrstuwy{}~~}{yupke_[[_hqy{zvspooprux{~}||~~}}}||||||}}~~{{|yvtrqqpppppppppqqqqqqqqppoonnmlkihfdcbbeinsvxxyz{}zwvx~{wtrponmllkkkkkllmnopqrtvxz|}~}{yvsoje`]\^dlt{~}{ywwwwxz|~~||}~~~}}}}}}~~~{z{}zwusrqqpppoopppppppppppooonnmlkjihfdccdglquxz{|}~|xvvz}yvtrponmmlllllmmnopqrtuwy{|~~}{yvsplgc`^_bhpx}}|{z{{|}}||~~~~~~~~~~{yy{|yvtsrqpppoooooopppppoooonnmmlkjihfeeegjnsx{}~}ywvw{|xvtrponnmmmmnnoopqrtuwxz{}~~~~}|{yvtqmieb`_aelsz~~~~|||~~~{xwx{~{xvtsrqqpppppppppppppooonnmmlkjihgffgjmqvz}~{xvvx}~{xvtrqpooooooopqrstuvxyz|}~~}}}~}|{ywurokhebaadiov|~}||}~{ywwx{|zxvusrrqqppppppppppoooonnnmlkjjihghilotx||yvvvy}}zxvtsrqpppppqrrstuvwyz{|}~~~}}||}~~~}|zywurpmjgdcbdglry~}|||}}{xxxz}~}{zxwvuttrrqpoomlljihgfeffhjmpstwxz}~|mm}klx~|zyyyyzz{{{{zyxwvvuttuvwy{~~zuz}~}|zyyyzz{||}}}}}|{zyxwvvvvvwwvnc[fsd]enx|xv{~~}}~~ulmyxstx~~|zxwvutsssssrrrqqppoonnnnmlkhfcdhntvwwxz}zqmpy|z{}}{yxvtsrqqppqqqqrrsttuvwxxyyyywvutuy~}xqib]\_gpvzzxxxxyz{||||{{{{{||}}~~|zxwwz}|vqmmpv}}{ywusrpoonnnnnnnnnnoooooonnmlkjhfefhlquxz|}ztpnpu|~|zxvtrqpooooooppqrsstuvwxxyyyyxwuuuwz~zuoid_]_dksy}~~~}}||{{{{|||}~~~}{yxwx{}xrnlmqx}{ywutrqppooooooooooooooooonmlkjhgghkosx{}~|vroorw~}{ywutsrqqqqqqqrsstuuvwxxyyyyxwvuuux{}yupkfb__bgov|~~}}}}}}}}~~~}|zxwxy||xsomlnsy~|zxvutsrqpppppppppppppppponnmkjihhjmqvz}|xspopty}|zxwuttssrrrssstuuvvwxxyyyyxwvvuuvy||yvqmhda`aekry~~~~~~~~~~~}|{yxxxz}|xtqnlmpu{~|{ywvutsrrrqqqqqqqqqqqqppoonmljjijlptx|}yuqooqu{~}{yxwvuttttttuuuvvwwxxyyyyxxwvuuuwz}|zvsokgdbbdhnu{~}|{zyxxy{~}yvromlnqw}~|zywvuttssrrrrrrrrqqqqqpponmlkkklorvz}~zvspopsw}~|{yxwwvvvuuvvvvwwxxxyyyyxxwvvuuvx{~~|yvsplifdcdfkqw}~~~}|||||zwspnoqw}}{{zxxyyz|~~uhbuzf^cjptx|}|ysrsuwz~|{zzyyxwwvuutssstuvy{xou{jknswz~~|xrnotwsmhffhlqv||sq{}rllmqtx|~zwvw{~~{vsqqtw|{vohcfoz|umgddehlpsvxz|}~~|{|~}zxwxz~zuolknvzurpprsuwxyz{{{{{|||}}}~~~~~~~~~~~}|{zxurpooqtuusqonnqty}}xtqpty}ywuuvwxyzz{{{{|||}}~~~~|zxvuvwz|}|zxvvxz~}zvrmieegltz~zvrpoooqrtuvwxyz{}~~{zz{}~{zyz||ytpmkmry|ywvuvvwxxxyyyyzzz{{{||||}}}}}|||{zxwtrpnnoqtuutsrqrtw{{vsqqu{|zxxxxyyzzzzz{{{{||}}}~~~~}{zxvttuwz|}|{yyyz||xtplhffiov|}zwtsrrstuvwxyyz{|}~}{yxxz|~}|||~}yvrnllot{|zxxwwxxxxxyyyyyzzzz{{{{{{{{{{{zyxvtrpnmnoqtvvvuttuwy}|xtrqsw}}{zzzzzzzzzzz{{{{|||}}}}}~~~~~}}|{ywutssuwz|}}|{{{|~|yvrnjhghlrx~}{xwvuuvvwxxyz{{|}~}zywwxz}~~~~zwspnmnqv}}{zyyyyxxxxxyyyyyyzzzzzzzzzzzyxwvtsqonmnpsuwxxxwwxz|}yvsqrty~}|{{{zzzzzzz{{{{{||||}}}}}}||{zywvtsssuy}~}|{{yxwusqomkihhjnsz~|zyxxwwwwxyz{{|}}~~}}~~~trxgmt{}x|}tonorv||yxwwwwxxyyyyyyyzz{|~xnn}{lfefhiknpsuwwxxxyz|}{xvutuw{wpq{{tollkkkjjjjkmprtvwxxyz{}~|yvtqooprv|~xqnov~xurqppppoonnmmmoqsvxz{|||}~|zxwwxz~~xsnkkow~yuronmlllllllkkjjjijkloqsuwwxxyyz{||}~~{umijnt{~zwtqpnnmmmmnnopqqrrplijqxwrpsw{~}{yxx{{|tt~~|zyxwvtsrqpoooopqstvvtpkoxytqrtvxyz|snxtc`ciqz}ywvutssttttttttttttuuvvwwvtpmkntwurqsx~xj_^gsvtsv|{yvtrqppppppqrsstuuuvvvvvwwwvutqnjghlt{~uleadn{|xutsrqponnnnnnooppqrrstttuuuuuutsrqonnopsvz~}zvuvy~|ywusrqppoonnnnooppqqrssstttssrrrsuwz|}|zxvuvz}zqkghlsy|}|zywvuutsrrqqqqqqqrrrrrqqqqqsux{~}{ywussux}wpidbchow|~}{zyxwvuttsrrqqppoooprtwz}~~~}}|{zywuttvy}ysnhdbdipw}}|zyxvusrqpoopqtvy{}}}}}||||||||||{zywvvwz~~}{{{}~yuokgefjpw~}zxwussstuxy{||{{{{{{{{|||}}}}~}}|{zywvwy|}{yxwwy|{wrmiggjpv}~|{z{|}}~}}|{zzzyyzzz{{||}}~~~~~}{zwutssuwxyyxwwx{}ytojggimsy}|{zzyyyzz{{{||}}~~~~~~~}{ywuqnkjikosx}}ysnifeglt}~}|{{{{{{{{|||}}}}}}||{yxvtqnljhikou{|xspmlmotz~}}|||||||||{{{zyxwvtsqpoopqsuwxyz|~~~~~~|yurppsx~~~}||{{zyyxwutsrpppqsux{}~~}|ywuttvx{}~~~~~~~~~}xtpnoqv|~}|zywvusrqpopqsvy|~|zwurpooprux{}~~~|xtqooqu{}{xvtrqppprtvy|~~|{yxvusqpnmmoqtx{}|xuqooqtz}zwutsstvxz|~~}|{zyxwvvutsrqponnoqtx{~}yurpoprw|}|{{|}~~}{zyyxwwwvvvuuutssrpooooqtwz}|xtqnllnqvz~}{zyxwwwvvvvvvvvvvuutsrqpooopruy|~~zuqligghlrx}{zyxwwwvvvvvvvvwwwvvvutsrqponopsw{}yuqmjhgimry~|{zyxxwwwwwwwwwwwwwvvvuttsrqqqrsux{~}zxvtsrqqrtx|}|{zyyxxxwwwwwwwvvvvvvvvvvvwvvvuuvxz}}zxusrrtw|~}|{zyyxxwwwwvvvvwwxyyzz{zzyxwvuuuwy||xuronnpsx}~}|{zyxxwwwwxyyz{|||||{{zyxwvuutuvx{~|yuromllnqv{}|{zzzzzz{|||}}}|||{{{zzyxwvuutuvwz|}zwtqnmllnquz~~~~~~~~}}}|||||{{{{zzyyxwvuttttvwz|~~{yvtqomllmptx}~~}}||||||{{{{zzyxwvutsrqqrtvx{}~}{yvtqomllmorw|~}}}||||{{{{zzyxwvutsqpoooprux|~{ywtqomlklorw|~}}}||{{{zzyyxwvutsrqpoopqsvz~}{yvtrpnmnoquy~~}}|{{zzyxxwvvutttsssttuvwy{}}{zxvtsqppqrux}~}|{zzyxwwvvvvvwwxyyyyyyxxxxy{}~|{xvtrqppqtw{~|{zyyxxxxxyzz{|}}}||{zyxwvvvwxz}}zxvtrpppqsvy~~}|{zzz{{|}~~~~~~}|{zyxwvutttuwy{}~|zxvtrpooprtx|~~}|{zzyxwvuttstuvxz}}{ywusqponoprvy}~~}||{{zyyxwvuutssstuwy|~~}{ywtrpommmnpsw{~~}||{{zzyyxxwvuutssstuwy{}}{ywusqomlklmorvz~~}}||{{zzyyxxwvuttssstuwy|~}|zxvtrpnmllmnquy}~}}|||{{zzyyxwwvuutttuuwxz}~}|zywvtsrqpppqrtwz~~~}|||{{zzyyyxxwwwvvvvvwyz|~~~}|{zyyxvutssrrstvy|~~}}||{{{{{zzzyyxxwwvvvwxy{}~~}|zywutsrqrsuwz~~~}}}}}}|||{{zyyxwvvvvvwyz|~}|zxvusrqppqsuy|~~~}}||{zzyxwwvvuuvwxy{}~|{ywutrqpopprtw{~~~}}|{{zyyxwvvuuuuvwxz{}~}{yxvtsqpooopqsvz}~}}||{{zyxxwvuuttttuwxz|~~}{zywvtsqpooooqsux|~}}|{{zyxxwvuttssstuvx{}~}|{zxwutrqpooooprux|~~}||{zyyxwvvutttttuwxz}~}|{yxwutrqpooopqsvx|~}||{zzyxxwwwvvvvvwxyz|~~}|{zyxwvtsrqppppqsux{~~~}||{{{zzzzyyyyxxxxyyz|}~}}|{zyxwutsrqpppqstwz}~~~}}}}}|||{{zzyyxxxyz{|~~~}|{zyxwvtsrqpppqrtvy|~~}}|{zzyxxxxxyz{}~~}}|zyxwutsrqppppqsuwz}~~}|{zzyxxwwwwxyz|}~}|{zyxvutrqppoooprtvy|~}||{zyyxwwwwwwxz{}~~}}|{zxwvtsrqponnooqsux{~~~}|{{zyxxwwvvwwxyz|~~~}|{zyxwutsqpoonnnoprux{~~}}|{{zyxxwwvvwwxyz|~~}|{zyxwvusrqpoonnopqsvx|~}}|{{zyyxwwwwwwxy{|~~}}|{zyxwvutsrqpppppqstwy|~~}||{zzyyxxwwxxyy{|}~}}|{{zyxwwvutssrrqqqrsuvy{~~}}||{zzyyxxxxxxyz{}~~~}}||{{zzyxwvvutsrrrrrstvxz}~~}||{zyyxxxwxxyz{|~~~~}}|{{zyxwvutsrrqqqrsuwy|~}||{zyyxxwwwxxyz{}~~}}|{zyxwvutsrqqqqqrtvx{}~}|{{zyyxwwwwwxxy{|}~}}|{zyxwutsrrqpppqrsuwz|~}|{{zyxxwwwvwwxyz{}~}|{zyxwvvvwxyz{|}~~zvty|wwxz{|}~~~~~~~~~~|zyxvvuuvwz}~yvpyqdqsvvxxyz|}~~}~~~zuvyxyz{|}}}}}}}~~~~~}}}}}rjo|jdeikmprtwy{}}zvtv}}{zz{{|}}~|rjhq~zogdcdfiknqtwy|~|zwuuw}~}||}}~zslhjr{}vojgffgiknpsvy{~~|{ywvvy}~~~ztojhkqy}xrnkihhikmprtwz|~~|{ywwy|~~~~~ysnjikpw~|wrnljjjkmoqsvxz|~}{yxwy{~~~~~~ytokijnu|~zuqnlkkkmnpsuwy{}~|zxwxz~~~~~~ytokiimtz|wspnlllmnprtwy{}~|zxwwy}~~}ytokiilrx~~zvrpnmmnoqruwy{}~|zxwwy|~yupliikpw}|xurpooopqsuwy{}~}|zxwwx{~}zupljijou|{xusqqqqrsuwxz|}}|zywwxz}~zvqmjiknt{~zwtsrqrrsuvxy{|~}|zywwxz}~zvroljknsz}ywusrrsstvwyz|}~}|{yxwxy|~{wspmkknry{yvtsssttuwxz{|}~}|{yxwxy{|xtqnlkmrw}}zxvutttuvwxyz|}~~|{zxwwxz}|xuqnlkmpv{|zxvuutuvwxyz{|}~~|{zxwwxz||xuqnlklotz~{ywvuuuvwxyz{|}~~}|{zxwwwy||xuqnlklosx~}{yxwvvvwxyz{|}~~~}|{zxwwwy{~|yurolklnrw|}{yxwwwwxyz{{|}~~}|{zyxwwxz}~|yvromklmqu{~|zyxxxxxyzz{|}}~~}|{zyxwwxz}~|yvspmllmptz}{zyxxxxyyz{||}~~~}|{zyxxxyz}~|yvspnllmosx}}{zyxxxyyzz{|}}~~~~}|{zyxxyz|~|yvsqnlllorw|~|{zyyyyyz{{||}~~~~}|{zyyxyz|~~|yvtqomllnqv{~|{zzzzzz{{|}}~~~}}|{zyxyz{~~|yvtqomllnquy~}|{{zzz{{||}}~~~}}|{zyxxy{}~|yvtqomllmptx}~}|{{{{||}}~~~}}|{zyxxyz|~}{yvtqomllmosw|~}|||||}}}~~~}}|{zyxxyz{~}{ywtronllmorv{~~}}}}}}~~~~~}}|{zyxxxy{}}{ywurpnmlmoruz~~~}}}}~~~~~~}}|{zyxxxy{}}{ywuspommmoquy}~~~}~~~~~~~}||{zyyxxyz|~}{ywusqonmmoqtx}~~~}}}~~~~~~~~}||{zyyxxyz|~}|zxvtrpnnnnqtw|~~~~~~~~~~~~~}}||{zyyxxyz{}}|zxvtrponnnpsvz~~~~~~~~~~~}}||{zyyxxxyz|}|zxvtrponnnpruy}~~~~~~~~}}||{zyyxxxyz|~}|zxvtrponnnorux|~~~~}}||{zyyxxxyz{}~}{zxvtrqonnnoqtw{~~~~}}||{zzyxxxyy{}~}|zxvtrqonnnoqsvz~~~~~}}||{zzyyxxyy{}~}{zxvusqpnnnopsvy}~~~~}}}|{{zyyyxyy{|~~}{zxvusqponnnprux|~~~~}}|{{zzyyyyy{|~}|{zxvusqponnnpqtw{~~~}}||{{zyyyyyz|}~}|{yxvusrponnnoqswz~~~~}}}|{{zzyyyyz{}~}|{yxvusrponnnoqsvy}~~~}}|{{zzyyyyz{|~~}|{yxvusrponnnoprux|~~}}||{zzyyyyz{|~~~}|{yxvusrpoonnoprux{~~}}||{zzyyyyyz|}~}|{zyxvusrqponnoprtwz~~~}}||{zzyyyyyz{}~~}}|zyxwutrqponnopqtvz}~~}}||{{zyyyyyz{|~~}|{zyxwutsqpoonopqsvy|~~}}|{{zzyyyyz{|}~}|{zyxwutsrppooopqsux|~~}}|{{zzyyyyzz|}~}|{zyxwvtsrqpooopqrux{~~~}}||{zzyyyyyz{}~~}|{zyxwvusrqpooooqrtwz}~~}}||{zzyyyyyz{|~~}|{{yxwvusrqppoooprtvy|~~}}||{zzyyyyyz{|}~~}|{zyxwvutrqppooopqsvx{~}}||{{zyyyyyyz{}~~~}|{zyxwvutrqppooopqsux{~~~}||{{zzyyyyyz{}~~~}|{zyxwvutrqppooopqsuwz}~~}}||{zzyyyyyz{|~~~}|{zyxwvutsrqpooopqrtvy|~}}||{{zzyyyyz{|}~~}|{zyxwvutsrqpooopprtvx{~~~}}|{{zzyyyzz{|}~~}|{zyxwvutssrpooooprtvy{}|yxy|~}zyxz{{ywvuwz~wrsx{qjhltz{wsppqrrpmiikoqomnuytz}vqnmmory}vzz{~|}xrnouojpy}~~}||||~}ywzzqhdgkopqqrtv{zpszpkjloty~rgi|}~vk}{srsrqoomkjo}oguzx~qtrlquxyytmn|ynv}usx}vu{~pxvvqwjj}oo}}kz|ttxxgacobnwsy}z}yppxy{}zvop~urczrs}vzp~uhgthmq_qjhra]||louwz~pvvw~|z|zphw{ssjko}qpvnmni^e~t_`n[r}ylWLUly{qxbermzw|ovbn{wqru{{jt]KI\lTOSXgsaeot~pu~wysxdg|yh~{zoddjpyzslqaRukd}~wsfyzqdvkiyqsz^]{mochifjzk]qnzvfYowonjv|rryzsvuljv~yvuzfCew^jxcQKVh|tvz}m}xqkjz}u{e[b~|mwpZVVMMoqTZU[jrmc}pf{rnz~xxzrin}}tN^p^^apuklrhymuspy|~~~x~}q{fcz~{vwlxapqjnPQ^lnqh_~tn|lt|txd}mzpvtc[`dhgubaf_pl|}vsqr}qtmu~x~x~|nl|uzu[Y{nq{|voWjmgqbol]ccyqep_tx{lx|ukqfvr~p^jc`Uqyb}svoutvt}yh{|moonox}y|xy|psw|txp}jivwswz{ohociyto`ohp|djwrnjr|hk~hkydje]Y\kkxq{ntv{y~tryzuv|pzysvu~vumxjsjxvip{|lnu{kl{w^[buybkuscrvd{let}vyu}yo~rspkik[oquyx||zpoqwfyrzn{sd||j~wrzwzwuvgxx|ut~]mw|xiroqfrpgr{nfs}wx|r|iwtr~~vjwze`jlcTotmoxotvtu}o{|vswq~vu|yspvlu{}v`_zzrx|vjvvpZgoxha{wqjtaenol|}|yvx~qvup{udfsoY_usv~zrytz{oy}|{ygw}zl}jw|lt~qg~~lqnfsorcj||iuublxwpo}~{xv|yw{zoxfsqejj`fli_Vovzh|zz~k|~~{x~{cezlop|xwyxdl}~yzicxwYqwplgnxutzjrolo~zvrym}|svugcpmf`u~wz|}ywkxxy}tu|xr|z}|~ji{qvuwztrwmkrvugjuowosiqbu~w~zn~svzdqyvvttpecfdb[uvoyz~zut{w|s{yzlfqtljpu{r}vkk}}tiuyyessqdcymt}psiro{vsvvzz~rborgbawutvmvysu{px~ozo~uqnrtkrwzs~r|vstzi}sukluruuqtzkmz{zzw~~xtpwnqqywb_poeVemol~txz}yqznz|z}tihnpetppqo~{uosmuy~yn~muonksmyr||wqz{}~}}~wxx|wrvzbiwriXjozptt{suvi{u~yz{~v}npqogyqpnt~vtv|xr|wppzo{p}zxtvtvzzuzouurpjl|v^`pmaWjit{h}z{~wxt|qv{yu~vnokhaqonlhy~snt}~|yxtqyonyp|rv~~{}vu~lxysnpf]ovlZnstp~|~x{{nxrt~{}ru~rxzorieapvnve|{~}~nxyqtzyxr}|{wow}}ootqrqkdvnU_llaeqi}suyy~wuw~u~}}|~uu~|pnh`]bxlrhk{}zut}~ul}vl|vpz~~{rv}{o}vuqmqd\lpjcupyx|~z{}mzts{|t{{x|wpxtloe^\nynxg{|vpwp~xs~|}ymu{tk~vsmfdo~w][fjfbsnz{{y~r|~u|{x}}|z}ysoxxiglaYXoorxd~|z|~qrqrw}zw|wr~uwwywpkiws_dilitzzxs~pqyzus~u}u{}}zpwwrkowphlh`Yevo~otp{p}yv|}xnt~xy{nzwumgdk}zb\dihl|rxzwvyo}{x~x|~}xysvtlltpdilaW[snxnh|~uru}}sy}~rs~|~w}yzvniokafjnqxxyq{lw|{tw{v~v{y{o}qrojkuscclf[]snxvi|r{~~tz~|rp{}|xuyqwskek{zf]afin}r}{z{t~ox|}x{w{yzs}oonjmun_cjd[_qiuohy~}|p|~zux~uu~~wz}pywpjq~{jddgkwu~vyynvyvs{yp}xxv~txollkrvh\gld\goj{ko{~wu|~~qv{}j~vkurkjw}qecacg|{xuqtpvzxyxty}z}zpkknusc]gha\kgltcq|x}}u}r~|~uy~xmyoyvop}tjgdfpz~wsqptvvzwp~w|}|w{|ynhglvra]ehc_lfotevz~x|s}~}vy|t~jykrsnpz{pheabmw|~suvruww|~r~}~~z{{qhgktsb[cgcameiqcr~vxzwrzzwlnrvss|ulkfel}}vuzrsttxrt}}|zz{|t~wuuhcfqsdY_ghcmigugo~}~~yz}|~z{~xttosjorpu|vkjgbdwvy{~x{vstux}q{z}uypvpfcktl]Zagfjobpngxzwxv|}|~sn|pntsu}}snnifn~zvurrsy~vp|wxuz|l|ylskccmpfZ\cghrmfvjm}y}|{}||~{o|l{{mioqu|xnlkedrzy|}x}ustu|ws~yzxykulsi`dnn`W[bgkuikugs}x}}{~~uuvt}ymjqu}woomil{wxsqrv~||rrs|sxw}hp~hjlc_gmfZX^elvtgwpm~{}~{~|}ym}|qwxqfjpw}yomnkiu{}~{||~xsrt~}~zr~zywvxqgqemh`dli\W\bitxhrsiy~}x}~|p{uzyxljrz}sprplt}~}}z}ztqrz|zzrv~pvqxv}tayvchjbcji^Y[ait|lruix~y~~{ny{t{svnfmw~{qmpokry{}||}|vst}|z|uwrxsywsczwefkedkh]X[`hvzirqgvy~x}zp|yxswqhn{|sqsrpy~y~z{{uru}xwztwxu~tqwzukfyrcejefjd[YZ`j{vjukk{z{|~rr|q}|nungo|wooqop{vy|y~|vty~xy}xv~ut{w}~gnyqcekiki`YY[bo|omsdm{|y~z}}|qx{q~pwrjtwqsttw}s~|~||wuz|uv{x~w~}{rqw~~s{zfmtobbjjjg_YY[ap|mprdo|~z|~~yoyyp}ntsjq~vpqrsw{o{y}}~yw|}ww}}{|~~utw}t||hnsqg`jnmh`YY[`mzoptdkyx}|yuyzk{kkqijx|upoqtx}qx|}z}w|x||y~~x~vqxpnqv}}ruzefjhdX]hkjfcd`\bvarhk{~{wyvwtz|ntskq{xpmprv~wp|vy{nkszztps{}v~r{{utw|ys~rgokle]hpof[TSWe{v|ufr}}ytwti`kjav{jemjhsytoprv|pwzy}uywxuy|xu}yz}}xyz|sluyp`RMQ]ppclruwyxsq{y~||~~xs~vqtqzxv{}|sq{qz|rjfksuhj{lfw~~{uollouxumvwfmlgja]iog[UW]fw~ss~{yy{}{kmrajyn`bkhiqqmmptzsozz{xs~~u{vwsv}~}}|w~utqddnk^RQWbroty{{ywvsz|{{wy~}~}xsvpzpx~z~~qs{p}~tkhlwyej~kfu}~}yrnllmrtolyqfpjejc]iqi]X[al}}uy{yy{|xnvocszocdnnlppnosx~nv}t}vxzxvzyvzztu~}}|u~zoqncgpi[SU]j|zx}~{wvxy{u~xw}~|z}}uzous~wu{nv{qlnx~je}sfox||xrnmllnplkxrgrmdhjbfrob[[akzzy{}~~ywvv{~w~rix{thdoupmmnpt{{nyxp}uw{r|~yw|vyy~}~~z|yxnqrhjrk\UV]jz{~{vw}~vt|xx}x~{vuz{~syvu||~{slz|z}usxzgs{okqx{xsonnmlljhuwhpuicjlfksma\^dpxr~{~yxsopxpy|wliv{rklosxqsymqyu{||ws|x{~yzv}~|{xxwumqummrk^XX_kro|w{tx{xy{}v~yssxt{wz{||yrmywty{y~|ovxsqpuxwsooonlifguvhrwlcgomjppga`eotm{~|w~{snmr}w}}vmsznjotytt{mmz{vx{v|r~|vv}|{{}zvsw|zolsunmng_\_hqlu{{~w~{z}t{w|urszzx}zy{ytunsytsy}}xvzsuurtwuqoppokgcixpiwvkdgqqlnoidektrn~|}uz}yvw}z~zrq||oglt{uw|ljv|vsu~qwz|{x{z{v}|z~}}~}zuolw~pr}vkflwwqmlage_sqy}r{pt~usyv}wzyu{yz|iq|umkqz~}}xur{xuwyvtttsoiccqsfpxqgcipkdhqsstyzq||~vfm~{nkovtv|{vqw{oms|zwtjr|yutv{yin|~{{w|oikj_\fqvx{|ql}w|zsqv~wx{u{z{np|or}y}zzwh_v|y{wqqvwnpxigsrllmllmonke_csnfswnd_eongjswy}~uxo|ykgr~~{lu{wuvsot|vkjq{s{}mjs|{ustux|uiv~{y{y{mb`gi`_ku{v}|{topzwqvsw|||tn}~vqw~~z~{xuunbm}vuyyor~odpwpkjjkmoomg`cqmerxqhcdnsjfnx|~v~xr{vnilz|pz~ywyyutzyplr|{t|llsz~}xuuwvvyulw~z}w}zzofelma\fs{|z|{spu~vny~p|~}}~pw~xwtv~~zurlhrux{}}tp}jhtxpihjlopojcbmodmyvohejsthdmx~v}~~~w~{rqllu|}}w{zz}~yvzyrov~tzskouz{xutwyvtuttzx||{}yyvypiiooaZcq{x~{tsyunzr||~|}xs}zuzwtz~~yronoqlstroiqzukghloppkddomcmywqlhjrwndiuu}}~~xzprqknw{|~}~~|yw{ztv}}tyvlotwxwtswyxsqsy{sv|z{{~zwtx{slilpk_]ixy~yvx}ruvy}}}{~~sy~tu{wsy||}zsljounhy}qy~okrzvkfhloqojeiqhcrzvqmjlrwpgkx~w}~~{v~tkpsmmu|~~|v{~zx}{s{wnptvvurrvywqnq{rs{|||x{zw~qrzslhhlngaiy~}xy~wt|{}}~{|sw~tpy|vu||yxyxrjejvviquswnnuzsigjnpqmhhqnbkwz~{{|}}wtx{~~||}zuxqceqtpu~|z~|~xv|~yxulo{thinsvwspnvybbkz}tzy||zxr{~{rorwxstyssx}}vrzywzxvwz~}zupuye\grrou}~z{|xsljpy|ujbhyzjrx|yw}wtw|~wgkrxxppqmipwzx{{xtuz{svwikttopyzxuof`_gvznlwzfbu}xwzxz~{}~ww{~{wx~tfn{~~}{yz|{uxynhiptrzwtyxngkqsrrqpqyw~y~uuurnihlsyyrjl}pm}|~{uvxi\erx|ws{zrz}rou|~yutx}~~~~wstxunuznnvzsns|~|troic^^eq{|x|ply|xwy}|s|~zz}vuy||yw}~nhs{{z|~~||~~yywnklojj|z}ulnstsrpnrxy~u{wuvuqkgglrvtony}nt~|~}yy~uccnrsv}|~uyvmpw||zutvy||{}z{{|{vuvtkl|~sot{|smrz|xvuqjb^_eouxxzwx~}zz|ut~}}yw}|usvyxvzximwywwz}~}}~|xqnokci|zz}rquwvspow~u||}r|}ttvtoiefjprqno|uw~||~|z|wefoqqsx}w}xnpvz{yutvyzyy|wuzz~|vuurijy}sorzzqov~}ywvsme_^cjqtux}~ws~{~|vv|~ysrtttw{kiswvuw{~~|}}upomecr}x{~ssxyxtqs}xv~}ws~urstqkfcfkoqont|z}{zzyx~pcipqqu{{zworwzzxuuwyyxx~ttzx|zuttofl{|rpsy}tpv~}vttqkd^^ckquvzxtz||wsv|{tppqqu}{kgpvvvx|}~~{|upnmgcp{{ztvz{xtt|{u}~ry~voprpkfbcinrqps~~}xvusw}tccmrsv|}ysruyzywvwyzxwz{p{xx|{vssphhwwqrv||tsz|tppnjd_^bjrvx{{t}z{}ztrx{xqnnnrz{memuvvx|~z|uponhdp}|wuz}{wv~{u~~~ty|smopojebcinqqqu}}vsqrx|oezztrw~}tkm~|uw{~~}}~}vw~~|zsjo}|rmmpv{xqpwtz~{tvyyuplknrtrprxxr|~}odwummoqpnou~{on||~zx~yqv|wx{}us~}z||uy|snpttqmiiloolhioplw|wtvwmizzwuqnnt}xkj|}uuy{|{{|~}yw|ywyqxzvvy~z{}z|~wx~zz|xuyxoknrrplijmpoljnutp|zqllssjj{zsnmrz|rdi|xtvz|}}}~zzx{xu|{{}~wwzyuu{yqw|zyux|tjjnqqmjhilmkhhorkk{wokovqgn|wv{rfp~vsvxyyyz|~}zwy}u{|rt}wvvx{yrsvwvy~xp{{xw|~tkkorrolklopnlnuwnn|vmjounel}ytsx~}odn}{utwz{{{}}z}x~~ux{z{|}usvwwz}umwxst}~siimpomihjlljhkrriiz}~yokovpgm~{y|vio||vtvxxxwy{~}zwy~w{|ss~wvvvx||topstx}xow}vu~xljnrrpmklnonlnuxokz{y}yoikrpfiyyvw|xjjw{vtvxyyyz}~z{{~xt|zzzz}zqoqsv|{qqupw~ohjnonkiijkjhhntpgnz{~}uljptmiv|{~vmt|zvvwxxwwx{}{xv{|zzqv|vvvuvy}{rkknrx~xr|~ts{njnqrpmllnnlknvypm{ztuxvnghpqjm|{xx{{qmuzwvwyzyyy|~}zz{t}~zyyxy|~{rjilrz~xuyorwkilnoljiijigfktumn}ztuxumglttow~|}|sszzwvxyxwvwy{{xuw~~tr~~wtttstwytkefkuy~wpzulloqpnllllkilt{vpxzroprnggotop~|zy{~}vrwzwvxzzzxxz}~|yy{u{}xxwvuwyyqhdgp{|z{npymiknnljiiihfgmwwoqwporrlipwtr}~~{vy{wuwyzywvvyzyvu{zrv{ussrqqtvqhbclz{tqsmmppomlllkijpy{ts}vnjmolimsqnx}||}}yuxzvsvy{zxwxz||yy~}uy~xvvussuwtldcky~yrn|rkkmmljiiihfgmwwoo{yqlnqpnqvrpz{y}}urvyzywuvxyxvv|yrwytrrqooqtrjbbky|ytruonopommmmkiks{yps|umiknnmqrklz}zwzzqqvzzywwx{|zy{xvyvvutrrtvskddo|~vx|pt{qmlmlkjjkjggktxpkr~voknrrtvqjp{zypqvzzxvuwyyxw{~{|st{trrqpnnprpibdp|{ty~tz}upooonmnnmkjpxzrnv~{tlhjnqstmfm||wxxnpvzzxwwx{{yz~}}vy}wuutsqqrtslefq{ysy|rx{tomllkkkkjhhmvwnjr~}wojjotxxphn}zz}qotyzxvvwxyww|~z{su|tqqqonmmpqmedlxytwxzytqponnnomkkox{snt~~}{vngfjpuxqgiwxv~rlqwzyxvwyzyy}|xu~ytssrpnnoqpkeiuzvvyv}zupmlkjkkkigjqxumnx~{umghnv|zojt~x{|potyzxvvwxwwx~||xrvyrppomkjjlnlgemxyv|z|{wspnmmnnmkkpx|upv~}|{zyunfbemv|vllxxt|xnntyzxwwxyxx|xt{ysqqpnlkklnlhir{zy~x{~{xtpmkjjkkigioxyrow~~}|{yungdgq|yptxwvnpuyywvvwwvw|}zrs|{rnmmkjhggjljhlv{z~}|~|zvqnlllmlkjow}ysv}}{zyxvtngaaiu~soyyty~qmqvyxwvwxxx{xszyroonljhgilmkls{{{~z}|yyvqmjiijjihjrzzrrz}}|{zxvsmfcgs{sy~vxvnotxxwvvvvvx}vpuxpllkihfeehkllryzz~{|{vqnlklmlkls|}vsz}{yxwvtqleacm|{ru}uuxnmrvxwwwwwwzxswzrnmmljhffilnosxxx}{wxyupkiijjjikrzzrqx}|{zyxvsnhdep~}tx~vxzpnrvwwwwwvvy~|tpvwolkjigfddgknptvtv|y{|ysnllmmllov}zss|}}zyxvusplfbdp~wpwztyxonrvwwwxxwx}~|tt}vpnmlkigefimqtvtpvxvyzwqljjkkjkpwzsns|~}{zyxwurmheivus|zwxqoruwwxxwvx~{wps}zrmkkjigecdglquupmuyw{}ysnlmnmlnsz{tpu~}{yxwvuspkfcgs~|rrzxvxqoquwxxxxwz|xrv|sommlkigeehntxwoltwtx{xsmkkkkjlryyqms|~|zyywvtrnifit~~vu|{x|tppsvwxxwwy{wps}{rmkjjigecbekrxxqjp{ux}|wqnmmmmnsz|uot|~{yxwvuspmiffny|vtz{v{~vqoqtvxyxwy~}|sr{wpmlkjigecdipx|vmmz~tsx{xsmkkkkkovzwoow}}zyxwvurpmjhlv~|xzyy|uporuwxxwwz|uot}xpkjihgfdaadjt||rkr{tv{|ysnmmmlnt{|vqu}|~zwvutsqomjhhnx}zx~|vzzsonqtwxxxy}tqwxpljihgfdbbfnx~snwwqtyzvplkkjknu{zspv~{~ywvutsrpnljlt|}~{w~ysonqtwxxwy~yppx~zqkhgffeca`bgq||qq~usw{{vpmlllns{~ysu|{|yvutsrpomkjkow}||}wy|wqmnquwxxy}upu~~wokihgfecabemyxqxwqrwywqmkjjkpw}{trx{z{wuttsrpommmpw}~}yy~ztomosvxxx|vor{}woigfeecb``cju{qu|srvzzvqnmlmqx~}vsx}zx~|vtssrqomlllouzzzyw~}{vpmnrvxxy|yqt|~xpkiggfecaacju|rt|rpsxyupmkkkow}{tqw|xx{wuttsrponnorwzyzzz~|wqmnquxxy}~wpt{}vnigfeedb``cjuzqt}truyzwsommnsz~{sryzvxzvtssrpommmoswwtw~y|~}|wqmnrvxyzwsy|tnjhhgfecaaemyvpwyrqtxyvrnllnt{|uprz|wu{yvuttsqonnorvxusy}{~~}xqmnrvxy||~ut{~yqkhgffeca`aenz}tpxztruyzxtqnnpw}|upt||vu|~xutssrpnmmorvvrow|{}}}xrmnrvxy}yx}}voieeecb`^\_en{uqw{zzvpjghlsy{wmgiqw|sljo}ysv`_nw~{ytmhn{zrjfktz{wtrrsx|zz{m`[amsne]YX[akrrmo{zsx|upmkhedflstrw~{~~}z||sqrqnifdegjnsz}usxxxvy{xqpsy{wrt}rq~sonlgb_^`b`ZXcvsn}}zrlmtzzto{mtyz}zjcjwvq{jZkoigffly~~{{zvvz~{nt{eo|qklvtwqg{tj~ujgiq{x}zumxalpaca`ervolkgfyytx}~zz}pzwkqptrpxwklqsur}zwofdee{|z|yljpg{hde_ZbphYY\\[tpxq}~|~uvrx|tvt}liqqklzukt}tzpqfzpmmqkf}~vyspyrw|tmzjsjw|s}{yyuz||mz{wysrry|{lvgmeutimtyhl{piwxs~yt~~|fkqzozzvwwstwxizzmkxmff}~|jsx{kqsqrps^^bpgZkrhfi{r}~xxvvz}wtx|{tp`gnzlgy|v{ov}sfb|zzx}ztxx~vpo~sllllj~uyzx}v~w|s}y{}vy|{volhjfi|yset|jju{xr~y{sytp}tpp~s{{v~pr~xn|}|nys]dr{rxyso|orwxsdplfZgtgfnpldxt{wy}}{{m{zx}nkvqh_r|u{v~tzwxmkw~uxw}~|{ryrjxp|sumtvny}}|uvz}mvyw{}yz~~y}y~x{|v}rshvlinxwfo~ytsx{t{pypltvpftluuz|y}|w~z|pbqxus{yyuwusdksnsn|lfitpdqwjgnuytvwvzyytt~x|{}{tzmkmujm|~zt|zrupxyvswlvzwmsszun|zunz|xy|~v}s~sv~{|pys~nmtpigxxmpqtkuzt|{y{~pztrvquqtvuy{v~|zyklwpjpqpgz{}tq{u|nqfkxrjgyvadr{pvxt{{v{t{}||v~rwkt{rgi}yjv|vv|s||u}}~n{{wnxuuvvomyksotu}|x}pwtx}u~o{x}wxy~owq|pqyyluutz~}~w}s}s{tfqzuizt|ww|y|}xw{ugr~rou}~q{}q{slrnz~k`mvtlqsi~on}zxyz|p||{xl{puhekagxk`qq~puvpqmnhtul{{vq}wkatvql{}rzwz}vxsrzvn{z|ytm|y}z}}}wizvp~~lt|uzzvrzysy~mfwzhwbtrwyq}}zmxvh|tsow~ul{mx}rseswy\uyvollp{{{dr~kpzzs}jq}|{{zr~}yxf|~~Zigfojxszyurz}vz{httvugkvswv{ypyz{}ksro}|||xzqqquoqup{vzyxvuztlizvs{vzzmuxwwlsooewor|y}wtpvy{rn|yv{rvtxmoxxkuvu~{mpxw{uxwytuy|pswrz{z~mx|wvvogsshvsr}{txt}~t|o}}wo{wnlrt~~z|ts{~~}~zu}|w|xltwn}}w{}}~tm|sdr~psvww|ssussr|{kr|so|x|xw||xv~|vzkmvrmwtkv{||}|{zsrjl}svmsuzu}hzt{~y}twytxtrpnmknsjnxsyzz~|}~w~ts}vpzps}}vv}ptvomw|~x~~xywx}u{vzzly{r}|oo}|{y{{syuwvx}w{pxztxywlnhew}wu~x{|xvy}{wxptxin{r|v~wv{{uq|~ywrvuv|mt|yrsuyxz|}~w~y~uzwp}tsyfkukhxwm{xqyxkxyuyw|zyzuoz|wqxzy~}z}zsulopxpyypiq{szo}s|v}w~yqt{x|q}zovpbpymuln~{}}{{ymyvmvpqsollyy{|uuwsoxstvs|siu}o|u~}wwy~~wtx{}|vyrnpvi`mhhptuutzsw|u{|tmzvtyxv~~v~yz}|ruotxvwp~{vr~xvjw}}v{}u~zippdnyhkfhxyvs|~}}{~wzzw|tnxnjeutnzv|u~t}yu{|sv}v~v{}xr{ysz~~y~tv~|y{rwnmxocmmjumxx{}|~~z~svuswq{{zq}us|twxu|xu}~rw}nvmqyut}x|ofz|~~{{x~ypzwqwyq|ykmhs{{~w|}}~x{q|x{uxhbpnojxun}ukrowv~nsu{~~zxqttrttptfnz{|}up{|pp~tyvipllsrowsmdoz~oo~zghkiyz|{s}}wzyole}uwxyv~}xko~tketrkj`\lumpzxmt~ywzvs~wtjgwuup|sw~z{q~xs~ox}ry|by~konyrjpupt}{v{pv~{|mmk}mpoi{rqgx|qqkuqw}v}pnsyrfjhrhtvfsyz|}vls|qkhwwqprq}psvvw~{~sngtroxyqrrhaunehaar}v|s}}{w}vpsrp~lizs~|fn|{w{wxsqn~q{t~tqq[fnpfumv~y~zyxmzz{lzwmmvsdxt~|wt|x}zotnws_r}zuqfrkrxjy}~uz|z~}~|zx{~vp{}nzo~zvvstx{w|wzz{|gmvpqt{vmwrz{nfo{t~~{|pton}w|}opmt}wvraqs{ztv}sxvxxwvpfgzf_dnkgy|~uy}w|x}zyyno|z}{k{rsgxz|~}||}zwvv~rm|r_qrvekpsrysz~~~||u}prup{xfx|vv{rqy|}z}xw}|vwn{}|yhx|wmiwmrryl}{st|s|nx~|qjxy|x|oyzwp~{su}z}v{~kot~{ktovhsz~pkvpkvq{{wz|qwsyyzszyfutjshrzq{pyurr||sy{mhntgywyjfvxqrv}|vzwwz}kgr|ujwovz|{xv}xpxvzxxypxpsup`oywzsto}}w|~{rrzxluuojusuz{x{|tuy~gku{wpsfwo}tfjuwturx{{|z}xnyskwzz{r}rx~|xu{y{{gutsgooommfzzrgmyy{vt~{vv~~tvvt~uuo|uv|{j{~q{|}~tyytq~}|r|zr{}tksrdirwqlyqpzsuzzyykvo~rh|~{|}xwsuxoyyvosng~sr_fupfiuy~ww}{{~tr|wnwkz{mrxy{y}uvxuxylyzzl|wep~wurtlsn{}~}~~soo{tlmrnrtmyz{tsy{~ro|u{k{vxwgip~uiorpszxr||}skjsqkemZlmom`gpup{~~}{s~tuqr~wpkhyvomww|vco||}u}p}v{|}soks|xjtzxkgxl]kic~ggm~ut]gsnhggu}x}~{{yryttsi|woxrvkytgyrayz~zlwvxz~~sx{{jlnlq{|v|{~xmnbspl`Wklsgkytmr`k{rd_^e|}peppkjqqfsqoutrzwjrihgptbrlz}|vp~dl}irvjjtrlnfXipqhfzgj{eOmWfURbPWQt~^`dhfmvrg{xzy{oq~qlrbfe]nl{~z{m}wrmpz|xzuvtnvuxbVj{~ys]bueVndbKZiUd^muVmzzymsaowv}uyy~vmxzq~sxfjsw`j}hzz{{nrxp{w{tkz|{b]im~ym\aumseaUY|vghVlparz}~lwemzxskuzoqrw~rp~j}yhvqmhowb}{us||~i~~wy|}}|{~xks{}m[^[mttonsp^\_byykd\um\jqzqivhoj{sfl}}yzs}|wrt~llpmkc{vh~{|y}v|{vyury{yamzt^bXt|{uryshc\]mvc[apzTYnwnf{bno~gqn|}v|v}jvy~ujpnsdkxi{oxsl{z}u~yogswtkjdls}k~suqjeRitnUcmriYmvqftihzjm{wx{|nu|~rn~|t|vwzwugdts|{zynuwmz}p}~}xadoyvjqc|qtxm}kf__tzbZqur[aosgttiojsgo|vy|{pw}srt|{zzxq`wq{u~s~wuuzv|zj[mnuihii|v~pz~ojVkvuY_ppr[cgiavnhlwtmsvzz}wwt{vtutstrhg{wy}k}zy}~p~}vvqz`[krnbrgsz}nvjj\rtmR`toobdmgg|spevt}qz~szzq{xxvlktspdsw~~|v}tz}vvrzsfq|jW`dqkp{j|kpkoclttcTs{ne_boiz|ohhyyrx~z}yiswzzzrqmtzsllw{{v{y{yz~up}}~}|kl~wb^glll}m~~uivxhdrrt\lleabpjurmgzs~vsv}{yznuz{}|zmgrzuohyy}ux~~ws~z|v~}{vjhvweZ]imj}lt|lyo`pwyegrdi`kfnrre{vytr|{{qpuwxxysgpwojdxr|xxwsztw~{wy{xjiqpbZ]dkk|lwn}nisxwcer`ofd`p}pqd|{tvxz~pqrpprx{pipvrki}vyzu~|trxrx{pwxvljqod^bfcnwizp|~mnwunclmalgec~|wqk|zpvv{}xkrsqrt~kdptvksyr{wu|qu{x|yq}{qijpogc_a_rqo~n|~osxvnmxkfpe`juwgp~{pjzz~w|oqpruy{fbqyum{rvx|{v}o|~|u~}~vsyvlfmqnie__aslq|xrrxutoqyjasj_m}qufr|nk~y{z~n{svvv{zgdowtlyn{|{|uymyz}qz{~wrxsnikoifd_^atim{ttqvytptw}o_noaj~sxjt~ql|xyxpsttru}kclsrkxnu|z}~t~ymsy~wv}wxlssokmjeef__]nnlu{uxzysu||xglsfe{wzqnukw}wruzwwrpq{sceqtpzypx~vw~{qpv|tzrhkmnihlfdifa^htg~z}}}x|wtr||tenn`u}w|ly}kl|~zw}szwwrqx|kdiqss~n~x|qz}ypytz~uz|ozhmnlhigbcie`]rqpv~}zzupvzrfonf{wxtl~vho{x|z{xxsqqywfckrtz}o{~wq|}wr}sz|rywzmseijffjdadgc_cwp|z}||yvr{}wpgrno~s{pqrjw}xzwxuytrry{ocemtuyyvqt|yqss}xp}slxhbcdcghb`ghebrvp~|{|xtxvuljsm|ww{m~}km}zs{}qwwsou|xkfgnt{uzz|qwwsp|wv~su|~o}xkscceeghc]ajjejxn|z}{vu|uvjrsyzs}nxqkx~}swquwtpr{zofejqzx{upvvur|q{yq~||rw~h}xebccehc\`jlgiwn}~~~ywtwmlu~}r~outkv|ssrpvsps{{rheioxw}wqtuus|tx|r|z|rtzjxujcbbdgd[^ilhivl~}~~yyrwpjv}s|owslx~xsssnuuqt{ypiegnzu{ysttss}sw}t{~w{qutoxrkb`afhb[_gjgmrj~~}y|qwqm|wvxn~qq}{xmzmpvusvzumifgm~wv|sutrwqx{y|xt}pupxtoja`ahh^Zahkjrjoy}{rzsos{ts|quu}ilouuuzzunkggpw|w{tsrqz~qv|~sryrqnzookb`cjg^Y_hkkrfqx~}|rzvpu{qt|sws~konuvv{zsnkffpux{uqqs}~ss~~~~qp~xupl~okmc`bhf^Y^fklreowswzqx}sr}vxsyo{skqvwz{tnlhfl~w|xvsqq{wp|{{snzzurf|tgng``fg_Y\cjmuilwvt|u|}woxxustuzllsw{|wpnkgiyzwxzuqqv~}rtzzxkpsvdn~gilc_chd\Y_glusf|}y}tw}x}~ow{x|{o|~r}~tjoux|yrnmigp|t~{yspr|~~zpyv|pitvtp`yvbii`_eg`Y[ajq{ooz}|sz~}{n|~zzvpwu|zsinty{xronjiu{vy~xrps|}yq}~wz~|livqvk`{q`gh``ff_Z]dmw}mx{}{t|wq|y|~ps~syzxqhmt{|uppnkm{x~w~|vrqx~z|vru}xsgkzuqu_i|g`ge`cgc]\aisxp|~xvox}xzvm{wv|vvlhpx}zsppnmu{uy}ztqtz{|txsx}zkgrzovh^wr_bhcafe_\^eo}q}||t|ss~yzymwxtuuqhku|{upppns{tz|}wrs}zy|wur~yy|nfn{{oum[pvd^ggcffa]^clzt|}|~wzyr|wyymvwrvrujjt||uqqqptzr{|~}xss|zw{zvt}vx|nflyynvm\ouf]dhefe`\]bl{t{|~xzyt{vywmxusvpulis||uqqqqvwpy|~ytvyw{|yw{ryzmgnyunxi_prf\dihgd^\]cm}~s{}~y|{vy~xu|}royoyqpuljv}zsqqqrxqrx{}yvz}vw||~}|voujirz|qrvbgqnd\ekjgb\\^dq~uxz}~{yw}|w{quwiz|ech_]hnkffhkptiu|}y|yz~~{{mhx}~|vldfow|}wlrq^eig`V^hjheccaclsmxzuy}toyzqzzqrntnjrwrmkmotviwy}}vmrz{vrt{|{ok|~tmnt{zovtbjlkg\alni`XUV_s}{~znhqzmagk`t}memmgnutonorwxjr|vztyw|{xl|yrrw}x~qw|xzpmw{reXQPXlyzy~}}~y{|{yupq}vv}ozqs{vv}{yz|zwu|zky|qkkrzufrs]csyyvofdgmruumlvidnhih^bnoe[WX]i{~||xtpmlvznukbvyi_fjehmmjjlpvsety|xwuvxw|}wzlz{vv{{t{|tvqfksm`URV`q~|yv{~~x{}{xurqzwu~qtwpy}y{~}}xsv{mvvnkpz|hhye\kvxvphegknrsnjtocnlfjf_grmb\\`jz~wzxurqotrttco|se`jmjkmllmqxzlmz|~y}u{r{wyrpr}wwz}zvv~ursjdmqg[X[et~yz{wx~ty~|ywsqxzvtq~rt|}~~z~sv{mvxpmq|jfxi]hrwuohfhlnookjuneqmdiibfrqf_`eo~ut|zurpovu{rfw|thblrnklmnpu|yjuv}zw{}y{tuzx|spuzwz}~{ys~}qpskfnpf][`k|uzw{uq~|xsor}y{o{sy||}||yvx}no~~~vqr{tespcenttoifimnnlihuperqffljentmdaemz~{uo{}wqmjp}}wlzzpfjvumjlnrw~xkw|ryzv|ywxru|z~{y}v}uw}yx|}zvz{u|pnspimqh_\`kxy}uzx}zos}uolq}uv|u{|y|}ysxyjtxux{y}porkjjorqkginpnlheougnwmdgomjoslfdhqwqouvmgfo~u||tlq|wlilpv|{mr}rq|~wv~ur~{q{zuw}}~{~|y{z}zw{~zutt~xnovtmnpia_clqm~}~{{}xmu}tlkr~ux}{wz|vp}umvsos}vwolpnnpojhkpqnjderrfryoffosmmqoigjqsl}xuvi_Z_kw|qz{sq{}qghlqwuhmyomy~~txvy|tru}|wx|xs{{xxxtqsy{{wtuwzo_pxwqssnijpsly}|zz|vxs}rlnwtw{zw{wuzuhjorvqpt~{lpvqnmjghmqqmf`dmce{xnmtxrosspnrwpo|vddouof_`it~w~wt|ukkqy~ymx|mjqw|{o}y}ww|z|ognnewvsupd_`_]^dg`l{~y{z{~~}~uxvprzuqu{pcpyhflheho{~z{yx|kfstnkjhhlpqld\]kjex}upt}|sqvvuw|yswkkrrj`\aku}~sx}}zuu}~slnv~}psshlt{ut{|y||}~{qe^fois{vy|recfeehjchzyw}{|~~}zw}{tsz~qlqxvhoymkqlhioy~{|xzmdqxrkhghmqqmd\_ligzwsuxrv{{}zt}yqnrtlcafnv{}|~r}}~~yw}voqyyozohmtzw{|{~y|yy{~|vk`_kkgz|y}vhfjlmnibr~wy}x}}~~||~{yux~|vkmrytszrpuqjhlt}}xxvekyxngeglqrog__khcu{ww~utz{s~zy~|usvqiegmswxx~s~yz|sry~wo|pjmrvz|}~}z}xyvx|~|vkacjccx~~pkostqfdw}w|~uzz|}yy~zw{qmnqtwy}zrz{rkjnw~}vphp{yneejptsneahj_hz~{yx{vv~~rz|ww}|xxsmkmqsttyuzz{}vw|ypxxmmortvvx|}~~~{{y~utx}~{shdjf[hy}}}~vrw|{pcm{vs|~|~v{|xy~~|{~z|pooppqsv|yxvonryywrkpzzofejqtsnfelh^jy{yyy||w~s}|vuz|zxtqrstsrxv{~y}}y{xox{onpqrssuy|}|{{|{|uqtz|{tkfkg[ctyz{}wxyjn|xrx|~y}~yx{}~}~y{tnmmmnoqv|~v~xssw~~u||qmqyyneflrtqkgknb`q{z~|{|~{~}~zsoptyxu{~}~}{{ywsrxzpz|}y|zwoiu}pghkmquxxyw{hy|stx{ysljqncky{{uv{qsy}}zvssvz|ztv{{{xvwy~tqvkv~ztv{~~usskkkjhggnz}xyzxz{~}xx}vrv}}zkpvw~|st~|qz}x}|yyz||yrkilt||wvy{{{zxv{}z~ywrlecfq{{hr~~xuv{|}zz{qnquvrlilk]]o|{xzz~skinw{|skmqsttrpr}}x|pm|rigfddeiqz~x~{|{ttteZfu}y~wnx~tpu{}ywx{}~|{}wuuxzzvojinw|zxv{}{zxtolgcbdjuqyzww|z{}zxwz}xpmqtsoklpg[eu}{z}wnjlrwv~tmqvwvtqpwv~|rjvukhhfdcejr{~|~{}zuuwj\dqx}zr{vosy}~|ywwxzzz|w|xttw{|yrljmsur{}vz||~wuqnieaaenwz~~{{}w{zzyuuyxrnprromqse]htx{{yqmnpnm{yru{|ytpr|wvz}}lm}|pkkjgdbbfmtxz|~}zy}vdbmrrv}x}~rpty{{yvuuvvvzwrvtvz}}xrnnppkm||{}y}yzvtqkea`cipvyyvzxzxrrvwsppqonqwn`costv|~}ysqqnfiz{}~wz|vsx|szz|xks}vmllkhdaadinrtz~}{y|veforqsx{truxzzxvuuuttyvn|xsuz}}ytppqohjx}ww}z}{~zvvqoy{}|umkr{xrtuvz~|~}vqrxzwutokrjjwws}ywz~~|{zxwzuyozwtuy~xuzsty}uu~v{vprtsqnmmoqqoowvhi}rhefkuynhr{|{|}~x|{{~~}|yus{~z|}z{{vpu~uuz{z~{||qfmuoijmnmjhhjmoonq}}~vu{~|wusty|oq}{vssu{|zxgjx~yqorx{{ywwxwqmw~wonrx|{tnry|yuwx|wxz{zxwwz}~~yxz}}||rpsrlffosspppt|xmt{{mn~}{{nq{uy~}|sqruz{zut}zvtuwy{{xpkmtnglidgovyrbkialmlmvwln}|qmnt~~rnv{yvtux|{xxxwusqpponqz|gam~vu{vmmwo^dsuru{|~}|nebba^\\bku}}zxywoggt~wqpsrd[i}||{z|z{vjq}~tliknqv|x{vrsvz~zwz|}}spppqruwwvsnkox|vy|urnf[U[hrtstx|npwywwx{|}|~}w}{ov~zz}y{xqqruy|yuw~}yvsrrrv}~qlkjhebabdghjq}{~~|x|xtx|siqyr}|{ywspnmmnr||qs{zwwwtrqqqkejwwpnquy~~~uqpppqrtx{|{z~z{z|}{}zxund\_jrrlhiqtibjsuuvy|~}z||z|wusrrrngdhmqx~{xtniffq~{nhijkjgfefhjkoz}wyty}qpz}xuvz{rgp|ywusomox}rfdhnv{{{{||zyxwriitxqlmprv}~xvtrnkiijkkkox|usvv~~tmmomh`[_kssonu{ujktwvuvy|zuspkejz~|}~}{|}zsnorw~zpt}}{ysjcfxznklmmkihiknqt{}yrry|uqtwoeivxloz{vsstttttttsqps}vkklmry|||~|ss}yuvxy{xlk{xtvyywtrqrsttxwnu|tt{xwxpnookd]]eqwvv}vu~yx|}|wnmwysrrqoorutnkms|ur|}}}~}vlgu~utvwvtqooooqu{}xy}{}phowzz{~~vqw|yuuvvutssstssu|ytvvuzyrvxuwyyz}}qrunmmlkihikmorywqyyv{}wvvurmgejt{{z~uu}~|~{{~|zzzvomw}vttsqoquwsmms~~y{ofnxtuwwurpnmllnv|qq}~~|ysmkpx|zwz{rimw}~~~z~||}~}|{zyyxwvz{suwvx~vlktwplmqtvz~qjw|pllmlkkklnpsyx|~|wtttrojfgnwzyy}ynjry{{{~}z~}z|~{z{|ztr{~||{yww{~wrt~y~ynadx~rlloqssttttux|y~}}{xsnkmtz{z}}unt~}yz}}}{yxwwusrw~|pjnpnow~vxwsstttuz{z{nl}vsssssrstuvyx~}vx~tljjkjihgjr|uw|~{tx{{{zxvx}}uputu~{njyxututspnmllnvxsz~{wrqt{xos~||xppsuutsstvwwy{uy|yuxwps|{tpprrqrv}|yrn{wsrssrrqrstx{u{~{~}~wutspnkgfjry~vov~~~~{}|~|uqy}wtuutqqtz~|vt|~{odfvxtrsssrpomlmq{{rt~~|yuqnqw~|onv~~}~~y{|zxwvuuyypsxxsqu{ssz}wqprsstv{wmr~~wqnmmlkjkkmqz~yz}urqqonljfdgoyzpr|}|}{vw}{|{wqu{{}~}{{~{xz{wkdkw{yvtssuuuutsrtz|uz}{yvtplhhoy{pr}}}zsuz||zxvuttuzxpsyzvrs|xy|ustutstx~umq|}zwvuutssrrt{z|yy~vsrqpoonlihnyvu~zyxy~}}~}|{tt|}wuwxxwwx|{|{yvmmv||{zxwvwvvusrrv~uu|~|{xsnnuvns}ywqty||{ywwwwzytzzvw{v{xrqrrpppsy~}|wqv|{yxvsqppoooopt|~y}|~zvutsrqpnkilwvt}{tszy}||xy}ysrz~xuvwxwwx{|wnmuzyyzywutsrqponqy~ury}|zxvusplimx{opy|}wuy}~|zxwwx~{su||xuyysu}{ustuvvvx|voqxyvvwvspooppqru|}~}{zusrqoonmkggo}xrx|w~wy~~}~{x}}xw{{vu|}yyz{zzz|}z|ulltxwvz||zyxxxwvvyzv{}zywutssqmjmx~st}z}wsvz}|zwuttv|upu|~{y}~y}}vtttsssux}{}tqx|yuwzyvtrrrrstx{y{}vtssrrsttqnpy|z{tuytv{~}||y{~zz~}wsx~ywxyyxxy{~zuz~usz{wy}~|ywvutttv|ysw~|{zxvuusplksutzxyystx|~}|{zz}}vz~{{zv{|usrrrqrsuy~}y|tsy|vpqvxvspoopprv~{x}{ywvtsssspmmvyzxrw{vw|~}~|x}}yx{xqpx~xttuvwxy{~|{wqu}|tptz|zwutsssuz~uu||{yxvutsqmjktvqu{~|zz}}{z|vw}|zz~{vxyuttttttvx|wy}uqv|wmimsuspnnnprv~|}ywvussssroloy{y~}ut~zuvy}~}||~wy~~{wvy~vrvzxxyzz{|}xz|trzzpmry||yvuuuvy|ux~zxwutsssqolnv~~xw|{{zxz}~}{{}wx~}{}}wzzvuttssstux~st~vtz}rkmtyyvrppqsv}y|zyxwuuuuusqqx}{}urz{urswz{{{{}{z~{z}zrs|zvvwwxxy{|~{v~ytxxomsz}|xuttuw|vty~|{zxwvvutqorx|yvy~~y|{xz}~}}}x{~|}zv{~wtsrrqqrstw}~sv}ut|{ohjqvwuqooprw~}x|{yxwuuuuusrsx}~{{|urz{wvx{}~}}zx}}zxy~{srz{wvvwxxz{|~w|ysv{plov|}zvtssuzyuz~}zxwutsssrpoquxwux}{{zxz|~~}|~wx~}|~xy|xwvvuuvvwx|xs{|tqw~sjglsxxurpqrv}{{~{xwvtsttttstx|}{|xty}xttvyz{z|yuy~~|yx{wtz}ywwxxyz{|}wxysvuopw~|ywwy}{vz}z{{xvutrrrrrqqswxutzy{zwwy{|||}x{{~{xvuutttuuw|urzyrr{}rjhlsxyvrqqsx~x|}}|zyxwwxxxxy{~|z{tt|~|wsrtwyzz}}vw|~|yx{ztv~~yvvvwwxyz{|}x~xx{rory~zwvw{|vx~{xx}ywutsrrrrqrtvvqov~xz{yz}z}|~{wvutttttuv{xtyxqr{}riehnuwuroorw{w|~~|zyxxxxxxz|~|vu~xsw}xtrsvxy|}yplotsnkiikllnpqrtz~uu|ytuxzytnjikqx|wpou{~~~xrqywnx}yzzx}|{zxtqpsx|~~~wuy{xroooopqrsstw}}x{vpr{ytrty|}zwttw}}xy~|wv|zx}~~}vy|ww~|y|}||}}}{z{~|xx|}zusrrrrssttuw}|wy}}xvx{~zxwz~z|zrqw}yuuvxvojmv~~|{zzz{|{yuqqw~{yy{{xtqpqstw}~|{{||}{z||smiilorqomlnsxxtopuy{|}~}vru|}yxz~yz}xvtrrv|{wuuvurruwupnmmnnopqrrtx~}yyz|~}zyz}}|}{xz|zvtw}}}{vnjnuz|zxwvvwxxxurpsy}|yxy{{yvsrrtuy~}}~}{z{~}vpmkmoqqpnnptwwspquy{{|}~}vsuz}{yx{zz}wtqprv||xuttsrsuwuronooppqrstvx|}zzzz{{||}|{z|zutvwuux~~}yrmmrxzzywvvwwxxwtrsx|}zxxz{zwtrqqsv||ywvwy~zuqnnoqrrqpqtwxurruxz{{|}~}xsrvyzyy|~||vqonpt{~{xutsstvwvtqppqqrrstuvx{}~{yyyyxxyz|~~xsrsttuy~ztooswzzyxwvvwwwvtrtx{{ywwyzywtrqqsw|}yvutux}{wsqpqrssrrsuwvtrsvxyzz{|}~~{vstwyyy{~~~ytpnnqv|~{xvtssuwwurqqqqrrsttuwy|}{zzz{zyyz{}zusssssuz~ytrsvyzyxwvvvwwvtsux{{zxxyz{ywussuw{}zwtstuy~|xussstttttvwvtstwyzz{||}~~|xvvxyxxy{~}yuqooqtx{~~{xvuuxyxusrrssttuvwxy{}|{z{||zyyyy{|~yvutrqprv{|xwxz{{zyxxwxxwvuvx{|{yyyzz{yxvttvxz||}}zvtsrsuy}}zxwwwwwwxxxvuuwxzz{{|||}}}|zxwwxwvvwy{}zvsqqrstvy~|yyz{{ywutttuuuvvwxz{{zyy{|{zyxxxyz|~~zwvtrpppsw}}|||}}|{zyxxxwvuvxz{zyxxyzzyxvuuvwyzz{~~{wussstvz~}{zzyyyyyxwuuwxyzzz{{{|||{zxxxxwvvvwy|~{xusrrstuw{}{{||{ywvuuuuvvvwxyz{zyyz{{zyxwwxyz|~|ywvtrpqruy~~~~}|{zyyxxwvvwyzzyyxxyyyyxvuuvwxyy{~~~|yvtsrsuw{~}|{{{{zzywvwxyyzzz{{{{{{{yxxwwvuuvxz|}|ywtrqqrtux|}|}}}{yxwwwwwwwxxyz{zyxyzzzywwvwxz{~|zwutrqpqsvz~~}}|{zzyxxwxyzzzyyxxyyxxvuuuvxyz{~~}|{ywurqqrtw{~}|||||{yxxxyzzzzz{{{{zzyxwwwvuvvx{}~|{zxusqpprtvx|~~~~|{yxxxxxxxxyyzzzyxxyyyxwvvwxz|~~|zxvtsqppqsw{~}}|{zzyxxxyzzzyyxxxxxxwvuvwxz{}~|zzywusrqqrux{~}}}}|{zyxyzzzzz{{{zzzyyxxxwwvwxz|~~}|zyxvtrppqrtvy}~}{zyyyyyyyyyzzzzyyyzzyyxwwxy{}~}|zxvusrppprtx|~}||{zzyyz{{{zzyyyyyyxwvvwxyy{}}{zyxwutrqpqsux{~}|{z{{{{{{{{{{{zzyyyxxwvvwxz|}|{zywvtrqppqrsvz~~}|{{{zzz{{{{{zzzzzzzyxwwwxy{|~~}|{ywvusrpoopruy|~}|{{{|||{zzzzzzyyxxwwwwwxy|~~|{zyxwusrqpqqsux|}}}}}||||||{{{zzyyyyxwvvwwy{}~}|{zywvtsrqqpqrtx|~}}|||{||||{zzzzzzyyxwwwwxyz|~}|zyxvutrqppprtwz~~}}||}||{{{zzzzyyyxxwwwwwxz|~}|{zyxwvtsrqqqrtvy}~~}}}}}||||{{zzzyyyxwvvvwxy{}~}|{zyxwutsrqqqqsvy}~}}||||||{{zzzzzzyxxwwwxxy{}~}|zyxwvusrqppqsux{~}}}}}||{{zzzzyyxxxwwwwxy{}~}|{zyxwvusrqqqrsuwz~~~~}}}|||{{zzyyyyxwvvvwxz|}~}|{zyxvutsrqqqqrtvz}~~}}}}|||{zzzzzyyxxwwwxyz{}~}|{zyxvutsrqpppqsux{~~~~}}||{{zzyyyxxxwwwwxy{}~~}|{zyxwvutsrqqqrsux{~~~}}}||{{zzyyyyxwwwwxyz|~~}|{zyxvutsrqqqqqrtwz~~~}}}||{{zzzzyyxxxwxxyz|~~~}|{zyxwutssrqppqrtvy|~~~}}|{{{zzyyyxxxxxxyz|~~~}}|{zywvvutsrqqqqrtvx{~~~}}||{{zzyyyxxwwxyz{}~~}}|{zxwvutsrrqqqqqsux{~~~~}}||{{{zzzyyxxxxyz{|~~~}}|{zyxwutssrqpppqrtwy|~~}|||{{zzyyyxxxxxy{|~~~}}||{yxwvuutsrqqpqqrtvy|~~}}|{{zzzyyxxwxxyz|}~~}|{zyxwvutsrrqqppqrtvx{~~}}||{{zzyyxxxxxyz{}~}}|{zzywvuttsrqpppqrsuwz}~}}||{{zzyyyxxxxyz{}~~}}|{zyxwwvutsrqqppqrsuwz}~}}||{{zzyxxxxxyy{|~~~}|{zyxwwvutsrqqppqqstwy|~~}||{{zzzyxxxxyyz{}~~{{}}zyz{{xtqpqtx|}zqgado}yw}siky~phddflv~~zz~unlsmiqx{{|}vqtrllnqqrsv~zmqxzx}wp{{iizjdcbbcefoytxjiwxwzgixuqqtsppzolnhu~}zzt{pyxux~ru}}}kurnzyprw{vzuw{yzfsdkxo|g]^kxpd\kqt{tt}wqq~wzn{wwzw}uirz}t}z{}~~{jzmmsovvxsfsnxy|qacfxwix~|}xtssooppkqa_kplyivqy}qlsusqjiprhmxkrrqxmhwvwpwrbixagxww_kwxsjjnz{}xzz{ysrslxojc_\qtnvtxu}~g|v|qnqt}nvp{br_|hg``eeokSixgkmswzco~}yk_~s~heko}`tgsirzvvm{nsuoZO]lua[[bilXpwtsZbYcoo_s{mw|ow{xszysyooZX|~~yuuu|wzx]VUZkg|zwb``jtsZdiSiudhtehxz|iz~`fzyutaVzp{x}~p}~lbTJJSfnumnxnzqj`tqTspj|xgm|{axs|tzu\p_o}lns{u~o}yyy~}xhc^bmgpsrl}~kkpmlya_xhp~bjzys{sp`|tlymq~qbq~p{rx~}w}u{vv{xxijcsmi~~n~mgjqdq]Z}lmtwirwiet|~_l~ut}~q}sXewpg{sy~xj|zvwvtmjfa\v}yvefsljcn^mo^fpkrvmbuwwnmyrvujqzXVqsluxv}v{zm|{~{qrdYfYty}xxmmj_Yj`mq_cscf{tkm|yyjxsisovosy]Xdmktw{xvzujZe\hwon~znujeWj[_xgekgbpypgwq}`suupqtvyorw`jvktxw|z||vh`ia]tpvyknrm`_fYnoeceVhwocrstqq}|v~lphmrqy{z{v{y}tpj^ePbjuxynrno[jYbyhdk_]vvo~z~osynsyejnpt|~}}y}~}y}xtzkjiabXOnd{tt{xkinYs~rlm_nzry~rk{uwqh{m^nuku}~z}yw{~xqwpkhc^gV_rqr{|mpjaztmlmar~rk~ht~wtwqshy{jlsroq{z{vwvvv~r~tpolek^qxzs}x~hqhc~obmjcoutl}yfzxqs{ymjwshksnpw{v|{}wwzw{|t~wpngekYzwzu}zvcugfvmfkhbpusmz~furm~wthfxtfbmprwyw}~|~yovopg`^jawy{vzrzesjcwmfje]p{ri}}}fr|~|qo~rjedurceotzy}~xesphd_]f_z|vzszgrf`zp`fcdtwrew{zbr~xy|rn|plek{shjv|~||}qcnkfb_ag[~ryxw`pckzlab_fsvnj{why~zusojq}ymoy~|~yu{kchbfc^]`e{{{{zsapbs{idkclx}rrvtu|omhw}sjqx}~|x~s|vz_`fdcbZ_\qxy}mqsg|mkkix~r{pw}~sxkgfyymks{}|}y|y{{zuytwoZddeb[\e`}jxkuvmlfpzwp}to~{z}uuoidnxulqtz|{yx|~xw}|t}txcgjegf`gcv|srqk|mjjguz}ny~ziv~y}ynwkdfyxnmrv{~u}{v|{{~w}v~qemigfddegz{kxgyrkherv|pq|}mpyzx{q{~mfcsxoinsyvv|z}|{{vywemiih`_dcumwiusjidpw}rk}~mkz{wzqv~mfcpumhnqttz~~}wyvxelkhec^b_ywwoumpxkhgnwxwiz~}qhv{|t{wn~sjekwsknruvz|ytzozlgmjgb]^]h{n~ynwk}ojijwv{mq~{xjsxzz~q|~pjjxztprv{z||xwxsstckhfe^X\]ymw{oypwwnmisy}{l|uoz~zztynhr}xopuyy}|{~vouluhaigd`ZXYfyh~t}pypmn}}urpu{|yv{rkiw|tmqtyw}|z|vyxpqjsadgeb^X]\qtq||zzvvqms}pvynx{xzr}nhjxxqnqtwxyw~}~tzrqnkqbiiffc\_a|v{x|wyupnw~|nx~voy{vz}pzkeiwxomqtwxz|v~~sssmpscjjjhd`bfvw|w{uqmx}}mx~}tnwxsxzpwjdivvomquxxy~ws~rsmmschiihd^cgwx{vxupmw~|mu}{sluxquzn|wicguvoosuy{{yr{ppkipaghgfc`dhyx{vyuomx|z~lu{yqktv}ouzm|xkdiwysswz}~}z~o}|lnehl_fffeb`dkwu{t|~spozz{}kv{ypnvw}qzyqxlgo{{vvx~}{||yl}{|hm`hi_eeffb`esuu}s|tqr{{n|~|qsx}{swvtjht}ytwz~~y~{ypmy}qfi^lb`feed`bg~w|}y|vs{~ut~ypxytur{~nijw{vtx{~~|x}{{zxitvzhjaaj^ceffc`enw}zzwwp{~|rsxyzo|wqujgpzytvx|}|x|w}v~nk}wzpgj]kdbggggbdiy~~yv}ts|{vpvvpu}o{|mfjw{usvy|}{|wy}y|wjx~yzhmahlbhiiiecgvt|zvz}znzzwptt{to}svohhszvrtvzyzx~}~z{kux|kmedncgiihdaeps|{wx~ny{ypsuyvm|usrhfpyvqstww~}z|y}nqw{mlh`nefiiheacity~~yxpuzysquu{lw{o~xlflwysrtwx{yyqk|yxuil^jjdhhhfa`dv|n|y}ypzyvpvt{qq~ryqjjv{xtuwzy|{||xwis}uxilcamdfhhgc^ak~mzz||qw|{ttwx{n{|r{niq{{vuwyy|}~wv{}ohywtrgk]ficghge`_ctzj~}r||ztxv|tpwxulku|yuvwzx~z~w}twilzrtjie[jedggfc_`g|sq}zt}|xtyv~our{rkmw|xvwxyy~zyxzt~tfmxosgib\kefhihfbdnrz~wu|{wuwu}mv~q}~oimx{wuvxyyzuzxt}qeqwpsgk``mfhjljgdhtruu{zutvt{kv}p||nimwzwvwyyz{wzyt}rfoworhkb^lgiklkifittvtzyusus}zjs|oz|nilvzxvxy{{{wyzs~|qenvmphja\kgiklkjgkvvvsxwsstq|xit{nz|ojmx|zyz||}|uzxs}zndoslofj]]kfhklkihlytssxwsttr}vixzp~|plq{~{{|~~~rzur||wjbopjlehZ_jfiklliiosrwyxtvsvrl}wsxomu}~{|}}~|{rzpu|yqcdrjjgfdWehgjllkilt|w~tzzwuxs{}lsrytmoy~}|}~~}{suvnwzvj`jpgjeh]Zigikmljio|tvw{zvwut~tk{yq{pms|}|||~|}|zpyorywobcpiiggeWejhkmmljmv|x~tyzwvwr{{ks~pyslpx~|yzy|v~~{gnlk{pfpqjmhk\_kgjlmljkssty{zxytw}ml|rswolpu~~|{vakqZ]b[`^cXUddfhjjhhn{o{y|}}~yxnlmt|qswtvwywvz}x}{sorgo{wkovkmlog^nppsttrrv}}xzy{rnssrtrozuiw{o{|ohkrx|loddr}vryneda`OYcacefecem{ujw}z~|}thdjqx}zyutz~vv{|xlnohqyztmvyoqpsgcstuwxwuuzx|}{~qnrsqsqpzsiyzo|znimuy|wfledrz{uuwnnliW^hfhiigegn{uhu~~zy{wuiejrx~}{wux~~wt~xxolrinxzunt|qrrvncrwwyzzxvz}~}z{~zynrssssnvxks~qv~tllsz}~hhjcmx}yt{~qpnobZhkjllkiimx~kuyy~}|t~ty{nfgpx}zvv{|tyx~~y{iooiszzsnxyortwjgwyz|}|zzx|xwz|}vnrsstrnxvjv|ox|rlow~wchkfr}ys|{opoqb\kmnppommr|myyy||}zrzpuxmgjt|zwvz~{uxy|xzhmrlt{{tmvyoruznhx|~~}z~yuv{}}xortturnwvju{ow|snr{~|qaimkwxs|ynpqrc`nqqssrppvm~xz}{|}|xzo{zltvmjp||yww{}yt|w{z~sgqqox}yqoyvotxzjm|~w{~zuuxzsqsttuoqzpl{uo{yrqx~|wfdpmr}|ux~sorvn_jttvvusrv~tru~}zz}}~~w}~pu|nowpkoz}zwvz}{uxyzx}whntnu{zsoxypsy}pl{v|y~ytsv||vqtuuvppyrlzwnz{trw}{wgcomp{|vzurvzrcmvwxxwutwsqt|{xx{}~}v}|ov{mqwplpy~{xx{}zu{x}~z}tgqrnu{yrq{yqv|~no~twx~{vrpsz}usuvvuosyoq}sq}yssy~|rbhnjqz|ywuw|~nhuzzzywuv{}k}vu}|xvvz}xxvr}umwvnlr{~{yz}|wx~mowopx{us|vxyo}~z~s|v}ztqqvseu{mcbppiwynw|vru|~~|w~zjaljhqxzx~|u{qjv}g^zyjox{xtqprwv{x|uxtr{wppv~vioznuuptvwxobhngkqrnnyypt|rlz~}{z~v}v}~zurrwnjvukpibnndntnknty|}}{yz{{wxqitspx~~zy}wqnqy}n}hhorpmjhjo~~|yz~yyyt~}ww|yurnkrxvwvvoackeekmjirxopyvit|||||~|~vz|wutx{u~wovz|{nrwjkurmmrw{}~|}|zxwxzxwxkqxsv}~zmuwtrsuzx~zmqttqljjmu|~y~z~u|sw|xz|vrponp~zzyywlaimfinnjlvums|pkw}}}~w~uz~}zwuvzuvyu}x|xntuinvroqvz~|}}zxvuwyxywlu{vy~}tirutux|vymqutqmkkox||~|~x~}t{~rw~{~}wrnnprvzyyyxncjpjkookkuvmq{riu|~vtx|}{xvx}}sv|w{xnuvjowurty}|}|ywutvyz|vnx}x|}~y|ofpuwzuxnruuqnlmq|{y~{w~zt}|rz~~xsnmosv}zzzzwkeopkmpnjnwrlt~}mkx~uyty||zxwzxr~zx|tqyqkuxutw|}{|ywtstxz{|qs~|y~}{{wxgkux{|zqptvtpmmow||{{~v{}vztv~xsomorv{}{||zpfnsmlonkluulq|qjv~t}sw{|zxwy~zr|}zytzuluzwvx|}{~|yvtstwyz}qr~|x|}{yzvzhlvy|~}sruvtqnmowz{z{~~~w|~w|tw~xsonoruz~~~{ogqslmomjnvsltolx~uztx{{yvvx~ws~~~zy~spy{xwy|~{z~{xvtstvwyxow~xwzzxzwzsjsyz|{tuxwtpmmq|v~x{~zz|zzt~|wspopqt~vjnuplmmjktvmp|wkt}wuvz{zwtux}tx~y}}}rw}{xy{}~}{y|}zwuttuuvzsq{{uvxwv|{v~onx{{|~|vxzwtpnntyv{y{z~z~|~wv{vspppqtukrvolmljmvtmrtlw~vuvz{yvttx|tz}z}{z|}||{wux}|xtqmkjkouxxws|~uuvtqmklr~xs}|wpx}{yt}{qecuywmqzupqqnowyqr}qv}}~zwtwywurrv~lft}{uqw{vyzooxzxwy|}~}zwx}|xurnlknt~~wtttsypy|ljlmkhedgp|ymq{{umt~zz}zpgbbjv|zz{wmgptnklkjnwtmsrjs{yxx{}|yxy}{rjeq~w{{uxuknvwuuxz{{zwuw{{xsoljkou{z{|obit}y~mkmmkheehq|ymqz|yuqz}zz~|skebdku{||xohpvpmmlknvumq}sir~xxwz||zxy}|tpmmzx||wywlowywxy{||{xvw{{wrnlknsz{}tv~{iamzw{mlnnliggku~wms}z{yut{yz}umgdejs~~|}|wmkuwpmmllqwrlt}nkxxvw{|{yxz}}~wrqpu{z~{y{sltzzyy{|}|zwvy{xtpmlnry~{~{{uqx}qchw{|smnonkihjq||oo{y}}zxu|}x~{|}vnheejr|~~~zqksxrnmlkovtlp}rkuwwvz{{ywx|{|~ytrqs}{~{}vnsz{zz{}}|zwvx{yuqnmnrx}z}}|yytow}sdhw}~vooqomjikr}{oo|y~}{xt|}yz|~|uniffjqzzpmvyrnmllqwrltplxxwwz{ywvx}y}~ytqns~~tpw||{{|}}{yvvz{xtqnnorw|~y|{ywz{qs}}mdmz}tqrrpmjjnx~umu}{}zttz|~x~ztnifgjqy~vnszvpnlknvvnp}wlr}xzvxzzwvuyy|}~xsnlw|ru|~|||}}|zxvx|{xtqonorw|yz}zxww|vqxyigr{uttspmklr|}qny{}}xruz~{y~}~ysmighkpx~uov{uonllpwunsvmu~xyvxzywuuyw|}~xsnlx~tw~}}}}}|zwvx|{xtqooprv{yy}ywuv{~upwzkhr{wuvurolms|~snx||~ysuz}|x}|}~ysnjhhkpwxpu|xqnmlovvop}znr}y}vwyywutw~ww{}zupmtwu}zwwyvsqqqrsuv{yzx}yt{}wvvw||{~yp|xtvxxurqs{}st~|}}}~zsxvrpooqvwvttttuvwy~{xzyy{|xx~xkcryljmopnmmqzznp{|yrrxxsomotvtsrrstuvx}}wxyyv|{{||mnytjwrnprrqoorzylm|~~xifp|ytqquvusrrrsuwy~y}}z}{zzyztiouju}ojlnomllow|ujm~~uijw~xuuyzxutsttuvx|yuyyw{yxx}|tx~ohsypspqsrpnnry}tjqyx~{wmdix|wtuxyvtsstuvxz|z{{{yxy~zsx|mkxpl}ymkmonmlmqyzoit|~~yngq|xvx{zxvuuuuvwy~uw}|v{|xwvy~~urzujnrwyqprsqonpv|xll|yuy}zwpefszvvyzyvuttuvwy~~y~|z|yww{{su~~qjutlz~pkmoomllpwzqiq{{~zogo}zwy|{ywuuuvvwyvv{|vz|xvux}}trzvjn}syzrqssqooqv|xlm|~wty}zwpefr|xx{{ywuuuvvwz}x}{zzwvw|yrw}okvtp|olnonmlmryynit}y}~xlgp}{{}~|ywvvvvwx|{tvzyv}yutuy~yru~~qiq|uvqrssqopt{}sjr~ytv}}zvjciu~}{|~|ywvvvvwy|xx|~y|xvvz}uvvkoz}q{unnoonllpw|ujm{~yy}shjs}~~|yxwwvwwzwsuz}vx{vtsv{}vrz|nkuz{urstrppqw}{plv|wsx~~{thdkt}}}zxwvvwwy~~wx{}zzwuuzzswtkp{}tuooponlmqx{sjn|}xy}rhjs|~{yxwwwxzwsuy}wx{vsruz|ury|okt{{wsstsqpqw}|qlu~{vrv|}{uicis{|yxwwwxy}wwy~z{wttx}{tt~xlnyu|zqoppommpv{wlkx}wu{}ujhpy}zyxxxxy~{ttwyv}~xtrrw{xqs}tkp{y}usttrppsz}wmo|~|xrqw|{wodenx|zyxxxx{|vw|{zyustx|wpt~~rlr~}uxqpppomnqx{sjn|~zut{~zqhis~}{zyxxxz~zsuy~wv}}vrpquyunq{|rlsx}usttrppsy|vlp~|zvpotyxumdfq}}{zyyxy{}wy~zyytrrvyunqz}smt|tzqpppommpvzsko~~|xrqw|{xphkw~{zyyxxz~ytw|}vv}|uqoptwrlpz{qnx~y|uttsqppsy{tms}{xsmmtxwskejw}{zyyyy|{x{~xy}vrpquwqlr{zqp{xwwqpqpnmnrxxplv}ytorx{yuliq~}{zzyxy|vuzyty~xronqutmlu|wotywtttrpprw{xop}~{xtmkpvwuofhs|{zzyy{~xzyx~~wrpptwrloy|tpx{vzrqqpnmnqwyrls~|ysnpw{zvmho|~|{zyxy|wtxytxxrnnputmlu|woszyuutsqprx|xop}~zxsmkqwwunfgr~}|{zyz||wx~xx}vqoptvqlr{{royzzysrrqonotyxplv}{wqnry{ztjhp|}|{zy{|ttx~wu{|upnnrvrlpy{snu|wuutrqrv{|smt|yuoknvyxricju~}|{{z|wux{x}ysppswunpy}unr}zvssrqoosx{tlo||ysnpw||xogis}~|{z{}xrty{vxysonpuuomt|yonw~}wvvtsrty}zqnx{xsnlqxzwpfcku~~}|{{}~utxzywrpptwsnr{|sms}|wttsqpptz{slp}~{xrnpw||wnfir|}|{{~xrsy{vx~zsomptupmt|ypnvxwvusrty}{rnw{xsnkpwzxrgcis|~}|{}wtw{x}ysposwuopx}vnq{|zutsrppsx{wnny{xsnmtz|yqhgoy~}{{}|trv}wv{}vpmnrurmoy{tnr}~{xwvtrsv{}woq}}yupkkrxyvmeeoy~}{|~|uuz~xx~}vqnosuqmqz{sou|xutsqpqty{tmq}~zvqlmtz{xogis}~|{{}{tsx|vu{{tnlmqspkox{tpv}{wvusrruz|vos|xsnijpwxvnfhr|~|{{}}vv{}ww|}vpmmqtqlox{uqw{ytsrqopsxzunr}xtojkry{xpikv~|{{}{tuzztu{~zsmklprnjoy{urz~zvutrqrv{|upu~zvqlgjqwxumgkv}|{{~{vx~zuw}zsnlmqrnkqz{ut}|}wtsrpoquzysoxzvqlimuzzumjq|~}{z{wtx|vsw}|vokjmqpklu{xsv}~xvusqqty|yrr}~|wrmhgnuywpiis~~}{{}~ww||vv{{tnllprokox|vsz~|yusrpoqtyztpv{vqlhltz{vnjoz}|{|xtv~}vsw||vokjmqpllt{xsv}xvusrruz|yrs}~|wrlhhovywphir}}||~|vv{{vw|zsnlmqrnlqz{tsz}yutsqprw{yrpxzupjiow{ztkhpz~||~}usw{uuz~{tnklprokoxztqw|xwutstx||upvzupjhltyysjeku~}}~wsv~xw{~wqmmqtqmow{vpt~~}xvusrrvz|vpr}}xsmilsz|xngir|~}~yrsyyux}~yrmlnrrnmszxqqy|ywvttv{~{sqyytniiowzxqgeku~~~~usw}xx}}vpmnrtqmqyztpt~}xwutrtw||ups}|wrmilt{|wngiq{~~yrryyux}~yrmlnrrnmszxrqx}yxvutv{~{tqxztoihnvzyriejt|~vru}~xw{~xqnmqtrnow{vps{~zwvtssv{}xqqz~ysnijqy|yqignx~~}tru{vv{~|uollprplowzupt~{xwutux|}xrt}|vqkgiqxzwoggoy~~|tsx{vw||uolmqspmqyztqv~yvusrtw{|vqt~|wqlhjry|xpiir|}~|trwytuz}zsnkloqolpxzurwzwvtstx|}xru{upjfhpx{xpiiq{}}~vtyzuuz~{uoklorolpx{wtx~~xvtrqsv{|wru~{upjgiqy|yqklt~~}}|tty~xsty|yrljkoqnlqy{vu{~yvusrtx||wsw~ysnheiqy{wojlv~}}{uu|~wtv{}yrlklpqnmrz{vv}~{wusqqtx|{usy~xrmhglu{|wokpz}|~xtv~ztsv{{uojjmpolnu{yux~zwutrsv{}ztu}}|uojegnw{zskkr|~}}~vuyytuy}zsnkloqompz~~yusoosy}{tnow|}|}}sicfszqlq~oqy}zwxxsmkr|nbcpywl`\gwysysmlmpuyz~zytsnb]Z\ckoqvyni||zuttw~tsq~zyurrtqcYo{~z}vwz{nnpohn~ozwt{y~wxvoctwuyv}}ovqwzoqtzrfeq~wymn{~yeopkwb]czzzxvyxt|~uq|xwnoXWVgxojdhngtqxy}z}{zwwlukb[^tx~xtr~~{o|vekepwyx|}yzr~yqqkz}|}t}|zowufvuzynujpwptiprmkoeo}q{}~vxwwrv~qkl^bqfo}mnohpxxzt}~v~~}}spyihtejvsz|rux{oz|o{{zwlwwo}t|t{w{}~|vz~x~y}}itwjz~{}txtk{xn{qzpknjiyuszx}~xvuxryunwromfongwpakdeqw~|}~~zxzyipkWdjm{z~wv|qmynysyvpyvwpt||moxr|w~pvvnw|og}uwxs}vs}ngqhlnopvt{yp|zwns|{stppqnk~ymvnlur}yuuwu|u~||w~{~~{xkuucipo}y|sv|wsm{rtxvvu{}{vxut~uytu{}|xmxwo|yvxwzzys}ovsoukqrnl|wx~xxyilslsw}sptmcjpfw}hklpy}q{{}}w|vjbi]cwoo{{y}{w{rzymvvtvsyy}u~x}vppxuy~~ysx|x~sxuz}vv{umqsgx|ghfhvsp~|xskwtyyz|~sqjvtknzzgv}}z|z|w}t{{ysvnyzohutrufttwz|}z}v}rw|puyvpvvtyut}z~xwysxr{{|qqsi~ssnq~xqz|qg{{w{tty}{}vqsdo}lilrxvqnzqrsqztnxv{~}z~w~zuwusxqjridjj{}x~y}wpvnozlnhvzuu~w|vx~|~w|}}ny|}}x~wsz}xt{znsnvukonrxw~wxr}|v{|il}xxusxpyt|wz|puv}{uvuov{z|z~zwwuqvjqions}yuruszxvu}{xx{tzy|w|v~xzo{{fdbrrxwfkjs|}po}vetuuhpvvw{vt|y}rpyuzwz~vur{u|}}xxyocrxrmw||put{}xwzuknt`ffc`zt}|t|uro{}xov||z}{yzmun~qryy~u~q{r|{{szyt~olspmt|sdn{ywxz|mmvzobdy}vgiwwnywnx{tsxs{wxz|xp}w}sunhvurklw{u}{~}rnopkw~yr}ylv}q|zqwknqptsuv~lwqpwot{zysw~||}~w}q|gcdavxy}{sxswrtq{xvqxyzw{}sy|{uw{wz}sueku{s|}}|}mrlqsnkrrz||wszz{}~z{tisnu{vvqyxztzw~sov|twtux~}yztvkljruvwvz~w~stx~yu~xw~t{y}utzm}}~w||xwsy{qvbryx|oz|}wssuxxnn|vyy}t}{u~~suteolm}nw{{|~}uwwwswx{yvu{zwp~~wzv{z}x}{tnokqrrvu}yuvv~qvnsyv{zv}s~~z{qxtx{v~{kj}zwsp{||~{rul}mmrtq}tv~{z}~t|}uyxopmi{rru~}~|~v{yxz}y}}q{vumw~tvs{~z}~z{yrnoprotst|~|vswszqvz}y{x~st{~z}twvt}u||jlyumlyvx{|vtts{pmwvvs~w{~tx{zlojk}lkm{|~~zzywyy{yuw|}up}wqso{}~x{~urqvrsout{ovyz{zt{{wxwu{yrvw~}}|{uw~ws}xzl{xrmqxorxwwrqor~{p}xq{w}xw|rqmi}thgq}v}~|z~|~~}v~{qwxpnmv{ux|x||vtszwvqrsynpy~{~wx}z|ux||suxzx~~}{xvt||m{zunothin|yyxyttor}vwp~{~{|y{|vrpk~vffnunuz}w{q{vpqjwtpuw{zwxxyswxymnx{qx~|}x|zwuv~yuv{ywy||y{vv{{r~urttfgnvppu}{ysq{}yrv}zw{x~xv}|vuqrrjhtumu{q}y{x{rnlyrow}{ytpu~~zgfq|vxwnknryr{ypljrnq{upt~qy{v{ysyyztw}vv{wleuy}vxwqlyytolqpqzzwnuvrkhhumkvnnyu~v}~~{}}zf|gipzto|zzsr||rprytgymvyjm~zwvumq}{opzmz||zuvtv|wyq}vwqfmt}wn~v~}}~}x~tsmwsnq}{~yynoiifcsvos{wyzvz}wvy}~zrnrrym~|vv{}~wzyi{}mnlqtljq|{~~wxq~{{~u|{{~~|}tz}rzwvtnkytcqpcrpo}{~yt~~x{ov}mu{vr}~~xmyqwvy{rfvzpw~~zy|r|onmkv{||}~~{~x{zzwju|er|nupit|t~z}}~zw}xsxq|w}yy|}z|~pnwnnruyzmvlw}vwv|uuyv|xsvmy{y||vwkvqo{rvpmsznx~vw~{~}vzwqyz}}xuz~~zywytttxzlpubdkhuux}uy~{}u|w~to~y{|xzywquxsvrjinfqvz}|~trzuqyzy}x~z~{w{z{sys}yyzhwvyxqptsyw|}xo|vp|sxo{{t{xrxzotvyv{pyygpyrwq}x{w|z}}vwu|qr{qy~|svvn}osnlwtjryq}tl~~zxxu~{oown}zs}vy~uxvszkesmnnrx}z}z~|yz}~n}uy~~y~yy|zpzwnfkanmhon}~v~{|{z|~|t|t}z}{ww{x{x~mxywpmtqqumz{py~sw~prn~}uttzw|w|zy{z}t|wnwtuskwurozs{||}{}u}|pwsx{{~}v~su{oolzzqm|z{vdsz~|{v{upvmozsupw~|zuzt{wmrorqqtotp|xsyr|}zt}zs|yxyr~|ukrlk}tdicqwsr|{~}zsxs}~~v}{zz}v~su|tpwviolsuiptv}z~|xtupz|utt||~}~y{z|qtzy}lprpjjniswxz|zwuyu}v|y~~~}|}}svqotysy{qgz{w{srrty~{}{uxmwvmmn{}z{pzsnnjrlrmhqv~}s}zyt}rutvr|}|ywxwshquw{r}y_n{}rrpnwvqecu`q~|l}akyuxss|{znwwyvlzkf{yrqipsytovzxzzjqw{xsyxuqbvmheqpuq{xiupvnijtm{{xvtw{trlpr|rvtzwhpsynr||oiOkk{zfmqpsojpjrX^fnziaioiwyv}w}t|sfmbjlqut|{z~~tqrt{yw_iZfyxs`kmfmbhUgk^qlqvxbiu|~us|quw{k|dhontr{}zztq~tsg]ohh{w`fxgecnhonWnsmxvekz|ivt|ltry}vi|e_txuo}|rzikg^n]k}}tkx{e`iqdsc`qmiopbs}pmwzmxsz}}vk}pZbr||}x}u}||}refdck^r|{zotdiim\l\ewgeohfr~qr{{hzswvyzot|h[n}x}~}uz~u}~}}hfk\gil}trzghln`ai[lqiisemzvmz{spz~}uxwzzixwhix{u}}~v~yz|t~znleZljxlxufljo]pebwidnlbo~rp|v~|m~xtu~pjztfs{y|w~{}}xyturmg]fhjyvnnonben[lsghk[gvzpx{yoqvo~~uhywhftwy{|x|}t~r}whf__n`w}~uvlmon_oYcskij``w|orxi~|rxwsgt~lcpux{~s}}}t~o~vjgb^f_q|}~hpmrco`^xncibcv}pl~{h~}tzuuir|qeowv{~}w}y|~rvzokh_h]f|xmsrwckg_xrcdgar{tl{gw|yrxtnmvejstt|}z}z{~zs{pmia_b^yrwvyxnbr_pyfdg`hvxmv~ynn}u}|rphv{ogqprv}|{y}{kvvmkc[bYpxvv~xxeohdxocge_pxum|}~i{|wsvshj{tjmqqu|~{|~zx}ukvqng_^c\zp}{|ysdt_lyjde`gv{qrzrl}}}rytpes~rhopuy~||||}}~kqvmjc]d]fypy~y{ljp`vxhfg^nxykv}jo{r|rlfy{lgpsw{|}~z~|jrqjfc^d\n{txzjnka{uheebryvjx~ht~|tynji{ykkrtxz|}~}~{||~}grnje`\b\uz|y{gmke}tfecevyxj|{gw||sxogiywkktuy|z}{~}}||y{elkgea[c^x|{|gmlf~vhgfduyxmz~|jw~~|q~yoifvwlltx{z||~}yyvybhjgca\dc{~z}inme~viheeuy{my{jw~~{q~ymggwynnvx}y{|}~}}xutu`jifd`]fe}}gqjivjidixyzl{~xjx~z}ypwmehxvmox{w~z~~zxxqto_igdc`^gh~}gtfrsjhcoz|ul~}qmzz|utqjemztlsx}||zyrvlvebiedb_dft}}tjrf}oigfwx~or~}kr|{z}q{|mheuzqnwz~yz}z~{rqms`fgedb^gfirkoxlifo{{yl{}}tly~y|ystlejyvnsxz~{{zupujugbifec^adtzvltiqkiiyy~os|zkt{{y|q{|mhftxqovw}yy~vurnop_hgfd`^dg{kvkxwmkhvz}wm}}}ooz~~w}suqjepysosvz}{x}w{zqpksbghgd`\ac~z~nwot{olit|}zl{|}rmx|w|xsukemyumqtw{y{zwosiuffjgea\_`w|uvutqrnjp}{~mu~}vlwzxy|qzmgiwwonsuy{{|ysqtipmcjhfc^\_ipqznxoll|{un}|{nry{t~twqjfsysnrswx|x~uzxqojtdhigeaZ^`zuwvyuwtplv~|mw}|tmwzuy|qylgjwxpostxz||x~rorhnmbihgd^[^hot}r|roo}|ym{|zorx}|s}vusjfpyuoqswy~~y|}}vs|mpergeigfc\]`s{pw{tyros{sq}{xntxxt~rzoigtzsnrsxy{w}}suummeqefhgfb\_bzyu}zzwyspx{pr}{vnvwuu}q}}mhhuyspstz{{w~~~ox}qmheobfggeb\`e~w{||yyxsqz|os|{unvwsu}p||mgivysqtv{|{w}~|nu{okhbnbeggfc_bg||}{zyztrz|qs|zvnuwss}p{}mghuzurux}}{w|~y}mqy~oig_mcdgggeadj|~|{y{us{|qr|zuouvqr}p{|mgiv{vtvy~~{w||zzksx}kjd`laeghhebfn||~z}zus~}ot{zsouvou{n}zlgkx{vux{}~x{}z{viu~vzhj`akaegiifchtz{xyuvnwzyqruwzlxxpujgozzuvx}~y{x~v~oi{xxsei[gfbghiieekx}yyv{|xozyvott{sn|qvpiit|yuxz{xy|uxin|txjgc\kcdhiihdhp~|}xwpuzysquu|lvzo~xkgmy{vvx|{{w~v{}ngywurei[fhcgijieek~w|zx~}yozyvpus|qo}qwpiiu|xuwy~}|x{zzutgq{swgi`_kbfhiifchtw}}z|owzxqttxvl{vrukhqzzvwx||}yx|twhm{rvkhd\keehiigcfow}{{qv{yssuvzkxypxlhoyzuuvz{~z~v}t|yjjzstnhg[igehiigbdkzu}|vs{zusvt~mt~p|}oilw{wuvy{{{w|wv}ofuwrsgk]ckdhijhdbgwo}qz{ysvtysm}utuljszyuvwzy~z}x|s~ugmzqtkjd\kggijifbensx~vt{zutvt}lv}p}}ojnx{wuvxzz|v|wv|netvprgk]akehjjhechwn~rx{ytvtwul|wrwmjrzzvvwzy~~|u|syvhhwqqmihZhifijjhdel~zq~rz{xtwt{po~swslku{yvwx{y~{{v{qz}sejwnpjidZiigjkjhegpwv{szzwuvs|~mq~qyrkmv{ywxy{z{wwyq{{odlumohja[khhjlkhfjtt{zszyvuvr||lr~pyrlnw|zyy{|{{wvypy}yncjtkmhiaYjhhkmljhlvx{{syyvtvr{}lp}pxslnw|{zz|}|~zwtyov{xmbhrjkhibXhjilnnljnxz{|syyvuuqz|kq}pxsmox}|{{}~}ztuwnwzvk`jqhkfi_Zjijmonlkp|xxtyyuutq{yit|oz~rmqy}||}~~|qvtnwyth`kogjfj\\kiknpommtvuuyxuvsr}tixxo~{pms{}||}~~}~}pxopxwobaojghggWcljmopompxwswywuvqv|mm}rtwoow}}}}~}~{trwmswtj_fofhfiaXikknppons~yyuyyvvtq|wiuzo|}rnrz~|||~}oxpnvunbaniehhhXblkmpponpyutxywvvqw|ln}quvopw}}|}}~}|qtulsvrg_inehgj^Zkkloponnttwvyxvwrs}qk{vqzqou{}|||}{}urwmqvsi_foeggkbXillnponmr}t}{vzzxxtr|vjyyo}|rosz|{{{|{xpxooutkadogehkfXemlnoomlpyvuvzzyxvqzzkt}pyupry||{{|{{pwslsuodalkdhmqel|{z{zvsqw|kx{{}|zysvts}{{y~szypv~}vtxmnspg`hn`]aroaknnqrqonqzowvuslqrrrplwuiu|p{vqrx{{zzzxv{y}{{vpvjWVcxpklc_```_bjvsc|u~}{}}}}wyyu}xssr}{snzzqsx{~slulkpof\^f\[eqshszwwwvtsv|xy{jjkkjjekqehvln{unntxywvtrnu{}{{|vi[\^TXfwwp|~wsojecgpvfixr|~}yxytmhpw~zyywzr{}z}~|losiknj`X`d\bmxqjy|zzzyww|x}~pponmjfnpdmvkr|vqqwzzywtqpy|}trrk]SX]Wbqzq~{xtpljnvzhozt|}}yyzupkiuyxyv{rz}y|}|lmshhkh^W^e`fs}vkx~~~~}{{}}pooonkfnqfmwmr}xrsx{zyvtrr}xz{nmkdWPY`^jz|s}zwspos{|iuzv{~|yx}~{vqmjn|w{vyxt~|z}~wippgijd[Xcfcnzso}~zw{ypqqpoiisnhutly|vsuy{ywutrx~t{rije\QUaafvv{|xtrt{rkt{}}|~}y~{}}~~{wrnklxzz{v}t|~z|~{kmrhgif]Xaidkxyo|{wx~sssrqlisqitvmw~xtuy{zxutqv}szsihe\SUaaduy}|xuw}rktx}|{}{}~y{~~|}}zvqmkkw|~|y|u~z|~xjorhhie]Zdhclzwrxtxvuutslmunlxtn{}wuwz{ywurpwvvzoiicZT[a]fwyzy|}jtzrz}zyzz}~{zz{~~~}yuplhmx}zx{||~}olumhjha[aibdr}q|z|xv|~|}zyxwqlsultyou~{vvy{{ywtpqz~szwljhaXW`_\i{z~}}vits{}zxxzxy|}}|~~~|xtokhowz|y}}zlnulijg`]ehafuzrv}~}uu}||}|{yrnvunvyov{wwz{{yvspqy|~}rywljhaXXa_\izz}xjury~~|xvvxw~y{~|z~}}{xtpkhmvy~~{~}|omuohjhb]cjcdr~s}zy}~{z|wqy~z~}{votyqs|rr~~ywy{{zwuqpw}}stynhgb[W^c]duzosru{~}yvuv{xw}x{z|~{{zxuqmijsz~|~}wkpukhie__hjdjz|vv{zwvzppz~~~~}{uovxqw{qt~}yxz{zywtqrz}vnke\Zce`jz|pvxisxtst{wwsnwqnorssqnkikv}|~uyylpvniie_^gkek|zky|mgegkwvggt~}~|||ztmppit||{z{||zxvtv|us_`bXTTRNP\a_j}{}jsvp|}xuv|vzssnlzoihgedcaagt{}{xtzz{{pvwnkkhdhstp{kvvw{phejri]et{x~{yxwrigooq{~}{{ut{{ynpgZWTOMU`^aqw}mhtw}yw{|x{rgippzyuqmjgedfpy|y~zsz~|tvzrlljghrwrx{fn{v{soqyrcft|y{}zzxtlhorr~{~}|z~}st{{}twob\YTRYb`aqw|nitu}yx{|x~ypfiooy}xtpmjgehqxz}zzu|~|uy|snmkhkuvqyudrur}|vrv|qdjx}x|}{{ytkkrqs~{}{z}{rwzx}|{|pea\WW_c^dv|uzirzou}zzx|}wlgmmn{~}xtplhgltvxz}}x{{{zyzqomjjrwqs~ij~xkp|}yz|kit}yv}}|yqkqspx}x|z|~wtzxu{}~phd^Z]db^j{uvsi|snu~|{}x|tjjnlozy}{wrnjiouvwy{~}~y~{{z}zsqnkluvpt~yfm~uiny~|~}lmxzw~}}yqmstry|w}{}}~vtzwuz~tlhb]agd`k{uutj|uns|}{}x~{tihnmoxyzzvqmkpvwwxz}~}y}}{{|~urplmtxrs~|hg{ykku}~rmw~x~}}zsmpusw}x{||z{wrwyvx{pkfaahicgw|s||kqqqy~||zz~|zuleinmu|y~zuqnouyyyz|~|{y}~z{tqompxxrw}sbk~vlp{}{~oo{~{||{xpkqvu|z~|~~vwspwzy|}yojfbdklfk{}u~yjrtu|}{{z{zxuqhchoqz}~zwsqrx||||}~z|zxy}{tqompyztw}~qai~|ru~|z}po|}{zyvokqxy|}zrsppy}}|wnjfdgnnio~wviuvx~|z{y|}vtqmebkrv}zwtsu{~~}}~~~~}x{xzyytqoot{yt|}yi`q{uz}zzzou}zyxslmvz}|~~|~rpqnt}}~tnjggnrmlyyz}mm{w|}{z}xvsqmgagqv}}zwuu{~}}~~~~~xzyyz~}urporz{tx|~zm`l~uw|{rt~|vmluxzx}{yy|~}|usuu{}rfdtws{{sw}no~x{~}yvv|yqx{romkeaisyrhszpnpruvwxy{uxxz{xuplo{~{ldux{lit}zus{{sqqqmhkux|}y}xuz~}}uuvvzoebku{qt}~~|odp}qpuy{ywvw~zmp|xvupjku{ypjmyyusrqrw~{qqssy~}y|vqmjkt{zxjqzx~|ieq~zzupoolgemsuzztw|yvu{}xzy|ulebhqtxxiexvmnswxvuuxtlu{xxupkoy|{uolo{|zxx~{rsst|}|~|w}~upljmvywvjuwv{~~xhgtxrqqmggouvzyrw|yvuz}xzy}tmfdiqswyjfxxnnrvvuttxuls}yxvqlox}}|vpmpy|zz~tstu||~|wzvpmjltyw|zlp{uy~|{}{kfq~}usrpjgmuwz{ts}|yvux~xyy{vmgdgpvw~rfosnptvutstz{nm{zxwtomt|}|wrppv~|z|{stux~yu|{rmjjox{yukwzwz~~{xx{}tghw|utsojipxz}{tu~|zxvw|~}xyz}}|piedkv||~rgruoruvurqsx~xln}yxwtonv~}}|xurru|~|{||ttuyytzzqlijq{|vlv}xz}}{wtsvysgj{}vtspkjry|~|tt}|{yxxz~}}xyz~y}wlfdfp|~qhuupruusqprw|vlq~ywwtopy{||xutsuz~|{~ztuv|}vtz}vnjilu}rn~{xz{zvspquwnhr{vusokmw|~yrw|{yyyz}~}|~{xz|xx}zphefmyxkn|rqstsqoptzyom{{xxuqou|x|xvttvz~}vvwz~wrx}xpkiks}}umz|vwyxurootwqho}z~}|tknw|}}~~~~}|{}~}|zvrr|wsux}x{zwwx~||~znq~tsvxyxxyxt~xvurlipy}xx~tz|sqsx|uuw|zx~wtrswztmw||ok|{y|zx|zts{xutspmq{ztz|xw{}||{z{~zy|qhkwvs|zqu~tmyyssutrompz~~~}zvyvuxyz|wwyxvuttuvvx{{xy|}{xsr|}xwyz{}qvqedjs|roy~y|~z~|zxtnmsyz|{w}|rnlow}|yqlkmrvspy}zxkl|||zxwy~~y~skhffkxyrv|~~}|{{zxwx|~}|||{zz||~}xuw||zy}~zvtw|vmmnpoopu}w{v|{jnx~}vwxrnlnt|~~~xy|z{zvqs|{vttw}}x~px{qoquvvvy~~v{zv{ummrqklsx{zsou~~||}|}{vsw~|slgegnst{|wvz}wmrzqpsuuuux~yytt|~xnjottrpoptz{wurpnnpuywtx{}wtzvhb`_^_dn{{y{||upryysoquvtsz{qq{}z|{sqrtsv~~unkkosqlp|yrt|}{|voyzvy~~wsuxwtszzw{~}{ywvutrqswzywvutrpoosz}yux|}wtv}vomkiffjqvsklx~z}~vpqwxtty~}|{{uoigjt~yupihnusmihjpuupr|zpqxx|z|~|y}}z}}ukhouxxxxxwvtstwyxwwyzzxwx}|xx{~|x~wwmjkjihimtxrjlx|vz~}~{|{{ztomov~zvpjjquqljlqy~|x}~wz|ywutsrqt|~tnsyzy{{|}rijqvz|~~|{}~|zyxvtuz~|{xy~ytw{||~y{~qllopqruz~ur{}ww~|{{xspt|}~|xuqnlnu{urx}{uqoqv{{vywx}~~tq~|||~~uorxzxy}}sry}~~~}|{z|~zwurnkjnv}y|{yy|~|ysvyz{~{trrqopsy~}ur{}|yrloy}xu{~zvtrpqu}ysry~zsomnruqlpz~wrxzw}vuvwwwyzzw|~{stz~~}zxwtrppty|}}~~|zxx{|{}~yx~~yvx}~|ytrpmkkmsupjo}zvy~yvwxwuy|tv~}zvssw}vnimturoosyzzxv{yt}xttuuux}}wx~{y{~~}znlry~}{|~}zwvtsqqsy|xz~}z~~}{}x{}vuvwvvx|wrx}|ztsz~{vtuust{~z~~{wsqptz~{wyzw{|uomnrwvru|tu{~~~wv{{{zz|wprwwsquz}zw{}{zyxvuw{|{xvusqppsz~|{}{{}|zrty|}~{wuspoqv{{tot~|z~ytt{}{{{yzx{~}zxwvuvw||uqu{{uollpuuqr{xx~|wzyyxxx|yw{{~zz{||~ysv{~~}{zywvuw{~|{zxvvw|||}xv|zutv|~{~~{}wtsqoorx|yrs}zvy~{zzywzvrw}~{wtqprx}xrrx~}wsrsx|zvy~wx~wtwstuwy|~vqt{}{yz|||xuz~|zyz~~|yxvussu{~xwz~}z{z|~utx|~~|{yxvtux}|tt}}}vqt{}zwwwvvz|~|xurpnnqy}wuz~xrpquyxtv}zqqv{~|{zzyxy}{zzsnr{zvy~}|zxvvyzxusrqqqty}xy}xvw{~{vyzrsy~ywusrsv{}wopy~{x{~yx}|z|xv|}{}~{xvusrtyxssy|xqlkmswvu{~y{yyzwwvvvx}}x|ztsy}tsw{}|{{{{{{{~|zxwutw||}~xvz{xx||zvrv|}vsrqpqv|yw~ytv~~}zxxyuy~{xusqpqu}|ww~|wttx|zvx|tuzxv}yyyyy{z{yrpu}}~}|{|z~|zyz}}{xvutrrtz{||y}{wwy}~yw|}torx|~~|{ywvy}~vsz}|}wru}~}}{z||x{~|{}~{wsqomlns|~xw}~xrprvywtwyst{z{}yxxwwz~}wz{}{upqyyy~~}{ywwz||zwvuutuy|}xx}|vstwzxtv~{ttz|yxvuvz~zsu}|}xw|~|{xx}wv|}|zz}~{yxvttv|ywzztqquzzwxxw{yv~xvuttuyyy~wsw~}|||||x{~}{{||zxwuuvzyy~}xux|yx{}{ztv{z{zxwwz~{v{~zuv}}yxutu}{ux~|yvtrqqtz|{{{}wtux|zwy}vv{{ts{zzzz|{{zsqu||zyyyz|}{z{~}zwvtsrsw~|wx~z||wvx|}zw{wrty}{}{yy{zuy~|~xssz~{zyxzzz}{{}}zwtsqppsy~||z}}wssvyyuu||uty{x|zyxwx|yw}~}{uqt|~||{{{|}{ywx{}|zyxxwwx|}z~}xx}|wttx{zvx~{tsx}|||zxvwz~|ut|~{x|}{xw{zvy}{z{~}zwusrqrw~}|}xx}zutuz}|y|xy~wwywvuuw|xx~~ysqu}~|{zz}x|~}}}zywvuw{{y}{wx~}xuvy|yvx~xqqv{{{~|{zz|zy}xuy}zxvz{wz}{yy{|zwtrpooqv}}}}}yvvy|{wy|vuzyw{yxwvx|~wv|~zz|~{vpot|~~}~{z{}~}zxvuttv{}{{wx}zursvxurtz{tnou|}{zyz~}ww~|}{vv{}zy}xuy~}|ywuvxyvtrqqqrv}~z{{vsuyzxvzzuw}xz}xvuttw{~xsu|~{y{~|wsu||{~|zy{~~|zxwwvwzz{{vtx}{vrqtxxuuz|urv}}}{yxy|zw|~~yvy|ywyysu{~|zyy{}{xutsrruzzz~xuvy|{xz|vv{wv~xvuuux}}wx~{{~|wrsy}~}{~{z|~~|ywvuuux~~xy~yw{zutvyyvty}zsptz|~}{z{~zw|~}}wtx}zwvy}wz~|yxxz{yvsrqpqty~||}xuvy{ywyytu{~xz{yxwwz~{uw}|yz}}ztqrz}|}}~|zy|~}zxvuuuvz~z}|xx||vssvxwsuz|vppv||~~|{z|~xx~|~{ww}~{xyxw{~|zwvxzzwusrqqrv}|~}{zvux{{xyxw|xw|xwvvw{}vuz~zz}}xsrv~}{yz|~~|{}}{zyxxzyy~yvx~ytsuxyvux}{upsx~|x}{z{~~z}{xz~zwux}vw|~|zxxz{zwusqqprv}zx}}}}zz}~{}~xx||uv{xwvvx}}wv|}zz}~{vqqv}|zxwx~}~}zyxwwy~~ww}ywyzustwxutw|zsoqw|{w~~{}{wx~}zvuwxw|}{ywvxzyvsqpooqu{|y}~zy{~}{{xvz~wu}ywvvwz}wtx}|yxz|{vrpt{}{zz~||}zxxwwx|zwzzvv{{urrtvusty{vqpu{{|~}|z}yw{}yvuzzuw|~}{yvuvxyxvtrrrru{|{~{xy|~|z|}wx||uvzwuttvz~{uty}|yz|~|wssx~|z{~~|}|zxxwwy~yw{~xtv{zussvxwtv{|wrrw}{|~||~|{}xw{}yvuyzuw|~|zxvvyzxvsrrrru{~|}~|yy{}|y{|vvz}vv{xvvvx||vty}{xx{|zvrrx}{|~}{|~}{xwvvvy~zx|zvw|zurruvtrtyzupqw}|~}||~zz}~{ww}~{xx}zvx}~|zxutuwxvtrqqqsw~~~|~zwx{|zx{yvw}}w{zwutux|~xsty{xvxz{xtru|}|~}}{z|~~|zxxxxz~zz{vux}|vrpruurrvzwrpt{}|}|{||y|~~zy|}ywz|uvz~~|zxuttvxwusqqrsv|||}|yy{}|z|}xx}~wxzwuttvz~~yssx{ywwz{yursz|z|}~|~~|zyxxy}yy{vtx}|wrqruurrvywqosz|z~}}~|~zx|{wvz{uvz~|zxuttvwvtrqpqru{}{~}z{}~}{}|wx}{uyzwutux|~}xstxzxvwzzwsqt{}{z}}}}{yxxxy~{wyyuvz~zuqqsutrswytopu{z}~}}{xy~|xuw}xux}}{yvtsuwwurqppqsx~z}|{|~||~xw{|vv|xvttvz~~zusvzywwy{yurrx~{yz}~}zyxxx{wv|}wuw|}xsqruutsvzxspsy{x~}~zx{~yutw}vvy~}{ywuuwyxvtrrrruz{x|||~~|~}xx|ytu{wvuux|{vuy|{xy{|zursx|ywy~}zyxxx{}vu{}wux}zustvwutwzysprx~yw~~zxz~yusv~xwz~}zxvuwyxvsrqqrtx{wz}|}~|}~xwz{ut~~zwvvx{}wtwzzxwy{zvrqv}}ywx}~}}{yxxxz~yvyzvw{|wsrtvustxyuppu{}xz~}}yx}~yvu{|vw{}{xvttwxwtrqqqrv|zx~~|{}}|~|xx}{uw|xvuuw{~~zuswzywwyzxursx}zxy}~}{zyyy|yvz~xuvz~~zurqsutrtxxtpqv|}y{~~}}zz~zvv{{uvz~}{ywussvwvtrqqrsw||y~~||}~|~}xx||vw|wuttvz}}ztruxxvvxyxursy{yz}~}{yyyy|yvz}wtuz}}ytqqrtsqswwspqv}~z}~~}|yz~yvv|ytvz}~|zxusrsuvusqqqrty|{~{|}~}|~{wy~yuyzvtstwz}|wrruwvuvxywtrv}|z}}|~~{yyxy{~wx~ytsw{}{vqpqssqruxvqpt{||~~|~}zy}{wuy{utw|~~|zxvsrrtvvtrrrsux~~{~|{}~}|}|xx|{uw~}}~|{}~yrmmtvsu{~}~yuw{tpoqv|~{xwz}|tos{vuuvx|{y||ups~ojkqw{||||{{{|wrrx{wux|~{yz}zolwunklnrwz{xvx|~ztpovxvy}}sor{uoorvwvsrtvyyy{~|||uooy{yxy{~volpz~xvwxxxxwwvvwy|}||||||~xqpu}{{}~}}vnls}ywvxyzxuux{{{|~~|}{uqps{||~|snpv|{||{xutvxyxy{}~|{}~ysps}}|{|~yrnnt}~}{zyxwwxz{||{|||}~}wrrw}||~~}~{tnnu~|{{|{ywwy{{z{}~}}zvrqtz~~{sppv}}~}|ywvwxyyy{}}|{|~ytqt{~}~ysoos{~}{zyxxxy{{{{{||}~}xtsv|~{upns|~}}}|{yxy{{z{|~~}}}~{wtrty|uqptz~}{ywwyyyyz{}|{|}~{vssyzuqoqw~|{zyyyyz{{{{|||}~{wtuy}xropw~}}}|zyyz{{{{|~~}}}~zwttw|~xspqv~~~}{yxyzzzzz|}}|}~}zvtu{|wspptz}|{{zzzz{||||}}}~~~zwuv{}zurpry~}}}}{zz{||||}~~~}}}zwuux~~zvrqsy~~|{zz{{{{{|}}}}~~{yvux}}yurqsx~}||{{{{||||}}}}}~~~|yvvx}}zwtrsw~~~}|{{|||||}~~}}}~~}zxvvx|~}zwtrsx}|{{{||{{|}}}|}~~}|ywvw{~|yvtrsw}~}||||||||||}}}}}~~}|ywvx|~|zwussw}~~}|||}|||}}~}}}~~~}{xwvx{~|zwtstx}}||||||{|}}||}}~~~}{ywvw{~|yvtssw|~}}|||||||||}}}}}}}{ywwx|~|ywtssw}}}}}}|||}}}}}}}}}|zxwvx{~}{yvtstx}~}}}}||||}}||}}~~}|zywwx|~}{xusstw|~}}}|||||||}}}}}}|zxwwy}~|zxvtstx}~~~}}|||}}}}}}}}}{zxwwy|~|{xvtstx}~~~}}|||}}}}}~~}}|zywwy|~|zwusstw|~~}}}||}}}}}}}}}|zyxxz}~}|zxutstw|~~}}}}}}}}}~}}{zxxxy|~}|zxutstw{~~}}}}}}}}~~}}|zyxxy|~~}{ywutstw{~~}}}}}}}}}}}}|zyxxy|~~|{ywvtstw{~~}}~~~}}}}}|{zyxxy{~~}|{zxvtstwz~~}}}}}}}}}}||{yxxy{~~}|{ywvtttvz~~~}}}}}}}}}||{yxxy{~~~}|{yxvuttvz~~~}}~~}}}}}|{zyxxyz}~}}|zyxwuttvy}~}}}}}}}}}||{zyxyz|~}|{zyxvutuvy}~~}}}}}}||||{zyyyz|~}|{zyxwvuuvx|~~}}}}}|||{{zyyyz|~~}|{zyxwvuuvx|~~}}}}}|||{{zyyyz{~~}|{zyxwuuuvx{~~}}}}}|||{{zzyyz{~~}}|{zyxwvuuvx{~~~}}}}|||{{zyyyz{~~}}|{zxwvvuuvx{~~~~}}}|||{{zzyyz{~~~}}|zyxwvuuuvxz~~~}}}}|||{{zzyyz{~~~}|{zyxwvvuuvwz~~~~~}}}|||{zzyyz{}~~}|{zyxwvvuuvwz~~~~~}}}|||{{zzzz{}~~}|{zyxwvuuuvwz}~~~}}}|||{{zzyz{}~~}}|{zyxwvuuuvwz}~~~}}}||{zzyyz{}~~}}|{zyxwvuuuuwz}~~~}}}||{{zzyz{}~~}}|{zyxwvuuuuwz}~~~}}}||{zzyyz{}~~~}|{{zyxwvuuuuwz}~~~}}||{zzyyz{}~~}||{zyxwvuuuuwz}~~~}}||{zzyyz{}~~}}|{{zyxwvuuuvwz}~~~}}||{zzyyz{}~~}}|{{zyxwvuuuvwy|~~}}||{zzyyz{}~~~}}|{{zyxwvuuuvwy|~~}}||{{zzyz{}~~~}}|{zzyxwvuuuvwy|~~}}||{{zzyz{|~~~}}|{zzyxwvuuuvwy|~~}}||{{zzyz{|~~~}}|{zzyxwvuuuuwy{~~~}}||{{zzzz{|~~~}}|{zyyxwvuuuuwy{~~~}}||{{zzzz{|~~~}||{zyxxwvuuuuwx{~~~}||{{zzzz{|~~~}||{zyxwwvuuuuwx{~~~}}|{{zzzz{|}~~}||{zyxwwvuuuuwxz}~~}}|{{{zzz{|}~~}|{{zyxwwvuuuvvxz}~~}}||{{zzz{|}~~}|{{zyxwwvuuuuvxz}~~}}||{{zzz{|}~~}|{{zyxwvvuuuuvxz|~}}||{{zzz{|}~}}|{zzyxwvvuuuuvxz|~~}||{{zzz{|}~~~}|{zzyxwvvuuuuvxz|~~}||{{zzz{|}~~}}|{zzyxwvvuuuuvxz|~~}||{{zzz{{}~~}}|{zzyxwvvuuuvvxz|~~}||{{zzz{{}~~}}|{zzyxwvvuuuuvxy|~~~}}|{{{zz{{}~~}}|{zzyxwvvuuuuvxy|~~~}||{{{zz{{|~~}}|{zzyxwwvuuuvvxy{~~}}|{{{zz{{|~~}}|{zzyxwwvuuuuvwy{~~}}||{{zz{{|~~}}|{zzyxwwvuuuuvwy{~~}}||{{zz{{|~~}}|{zzyxwvvuuuuvwy{}~}}||{{zz{{|~~}}|{zzyxwwvuuuuvwy{}~~}||{{{z{{|~~}}|{zzyxwvvuuuuvwy{}~~}||{{{z{{|}~}}|{zyyxwvvuuuuvwy{}~~}||{{{{{{|}~}}|{zyyxwvvuuuuvwxz}~}}|{{{{{{|}~}}|{zyyxwvvuuuuvwxz}~}}|{{{{{{|}~}}|{zyyxwvvuuuuvwxz|~}}||{{{{{|}~~}}|{zyyxwvvuuuuvwxz|~~}}||{{{{{|}~~}}|{zyyxwvvuuuuvwxz|~~~}||{{{{{|}~~}}|{zyyxwvvuuuuvwxz|~~~}||{{{{{|}~~}}|{zyyxwvvuuuuvwxz|~~~}||{{{{{|}~~}}|{zyyxwvvuuuuvwxz|~~~}||{{{{{|}~~}}|{zyyxwvvuuuuvwxy{~~}||{{{{{|}~~~}|{zyyxwvvuuuuvwxy{}~}}|{{{{{|}~~~}|{zyyxwwvuuuuvwxy{}~}}|{{{{{|}~~~}|{zyyxwwvuuuuvwxy{}~}}||{{{{|}~~~}|{zyyxwwvuuuuvvxy{}~}}||{{{{|}}~~}|{zyyxwwvuuuuvvxy{}~}}||{{{{||}~~~}|{zyyxwwvuuuuvvwy{}~~}||{{{{||}~~}|{zzyxwwvvuuuvvwyz|~~}||{{{{||}~~}|{zzyxwwvvuuuvvwyz|~~~}||{{{{||}~~}|{zzyxwwvvuuuvvwyz|~~}}||{{{||}~~}|{zzyxwwvvuuuvvwxz|~~}}||{{{||}~~}|{zzyxwwvvuuuvvwxz|~~}}||{{{||}~~}|{zzyxwwvvuuuuvwxz|~~}}||{{{||}~~}|{zzyxwwvvuuuuvwxz|~~~}|||{{||}~~}|{zzyxwwvvuuuuvwxz{}~~}|||{{||}~~}|{zzyxwwvvuuuuvwxz{}~~}|||{{||}~~}|{{zyxwwvvuuuuvwxz{}~~}|||{{||}~~}|{{zyxwwvvuuuuvwxy{}~}|||{{||}~~}|{{zyxwwvvuuuuvwxy{}~}}||||||}~~}||{zyxwwvvuuuuvwxy{}~}}||||||}~~}||{zyxxwvvuuuuvwxy{}~}}||||||}~~~}}|{zyxxwvvuuuuvwxy{|~~}}||||||}}~~~}|{zyxxwvvuuuuvwxy{|~~}}||||||}}~~~}|{zyxxwvvuuuuvwxyz|~~~}||||||}}~~~}|{zyxxwvvuuuuvwxyz|~~~}||||||}}~~}|{zy}wf^]dn~|yo`\`o}xuutucZ_UO\qzkN9?]|oirҭgR{.EWŽ~^G>@FLNKDDScW>D_mqvn\TWg|tqk]afgecbdku}vpwvrs}w\jƿļppa.-Oaw{^J?<>LYM5MW;Hdtvxzpi`RU{x_x{bTRSV`vupomo~z|}okis}ƺPKE0Sr־zjebT9PU>a\KTaikszssk^[x{j{wtq|nrwmp|xll}pvtsinz_vpnCS4&@]|԰|iTcjLhRZseaeuxsyyz_wvvrruvm~kqmr|bishof{xr[cI9K1-9\ҼfqmbwVhw~~tk|{pxsnppx{e{|}saThsZN`tjuwwjmffnw|}|ovYf=?LNV\e̻Ǽrkwda`lvnyuxnzw~~c}|zmfcl{vZZqm^X_lvuujtm}rgih_K^:+%/Io|ɸumvnqvxywxmltkq|iU^qbIO^jmrayri{fefrqrtsvi|cm_G[VTLCGrƴ}}qmp~nx{ruzur|ngn}zqs{jUdseUOWhr}wmz{krnpjnbuYadB6.6J[Wdqykzt{uyt}utdovp_lv^JQ]ca]Pvuvo|lhkxntzoorkfv^N_\VH89^|ɷztw~jz}tv}~wosjggz|valxjXOR_xeyzqwxjrtr}}ipfx]amPD;>HNAFrǠ}rzqusuvvt}c}uxm|jsrXW_c_XIhwlmsooznmyuxikqsScdaTA5IbdjǾ{|ts~tswmsyftbsr~milzhYW[rhboxtqumzpigup`u[I@AKRD8Qvɪuzwq}r{vvy~hwsopx}y^\eke[G[h]qxpo{unyvzfpruS_c`S@3E[VQiƼtuxx~on}h|yijs}n|{vx|l`^apY]xjxw}ptwnymqhxfezaSJLSR:/Hdmwʭ~tsrstvqvfnw}f|{wiekmdVJj\_qc{}mty||pc~o]jicSCAPQ?>Vuʮ|}wzo~pikz}vspj~fw~|{rjlwqTk[lm{uou{prp|axo]SPV\N79KQQYoƭwz~qzwwvxvwvmio}|ywty{thXk`QmVoyy|vtzznmwrZiol\HBT]H6:K[hsɾ{|xntmz}yojv~onqtqs}yywZt`cnYp{uus{{qn{w{a|xicchgQ;BTRF@Kcǵss|y}vszxv|zcozynj{{zic~jbiOlupurz||yzks`knmbVVa\G=AHIJKjʽl{jp}wharw~lxsw}mwyp~_tjTcp{{yw~}qzp~jrgbgpq[GLXTIADPZKt}oxv}~ijlqwyslm}{s|ztnrxZmi_`mx}qxrscnroaW\miPABFC;3Lf^t{zuqw{xplvuhn}yrs|nlrrzvZbimkiryx~qsxnsknvvcOTb`TJHKQ=UBSqjɿvq{~{piopadp{~}z}qgvrvdfgd_cw}zm{vumotq`V_f`UQTWH/NLPurȽ~upn~z~~{qjm|zmmu}}{}l|rhfmuslt|vqnvzugZ[mudQKKJEQjp|ü~x{{wu{vkmqpifruzhizs}vuwvnehx|ofo}|ywjY`lkb^_`R9MG>YHBQcs|tyoizsvzigmstqx}zp]`s~srmoqontzdVSTTPMWV8OS>R[^fx}tq~xggmlfcyzktkuu|}}yros|~qdclw~y}}|lhlmjdceaFGS;MO<@JTZf|uzg|vo|zqgjprrz|r||pgluxy{wvrliozcURQLJXfKKO@BDMaxyh~rjuv}|mhhhit|xo}|pt}xzz~{y~|soxynhb[VUddMaW>PYVRT[^[c}³wurjgsukybjuv}qxp}vt{vnkjgj{yqwxrn_LbcN]M>EQZVLIPar~~ynvvdacchxxttux~gpquhrl}~|xumopmo}|\efM\lkc\[YLBFUcjs~}zw~m~|lz~wtwyzxv~{i{vhppqlkrwtvx}mxs~~ulxaUlaPOOI@CS]XJDIR\oy{{nykfb^^exzwsmn}vk|lotwzwmr~upoqt~kgrXUbeeebXMGISZ`foyzvx{fn{yuicr~p~cruprwus|{v}p~nz|xj~}_gfRIIKHFIV^VJCFMWk¿vu|mllgbfr{~}zy{yuzxqqdyvu~w{ypznu~xojm}sp~_VfiffhbP=@Sbfdhpplruqz}ujbevzhzpwzigmpqv~|y~||{j{vpxg{nVOLJKQYZQJHIIIQdzwy}}|ichifhs}yootuv|pvohxpqtwxpou~y|}vul}hhjifcc`UIEM[bdc`Z[{xts`\hv{uqo}hdips{zvvzwu}iyqwlXOMIHScfVC?DGHOfl_|t~sjgkry{ukddmvz{ozq|~|ywy~~njllsxw{nymm|zplh]PKOWY[bfcULenbzt|pednvyvy}mrfaagtqprs}~|zm|xkcXRSX`d\OB=CJS_WJhslzwunfm~nb_^cr}rk~xoq~w~|snos|}|pswtldUKR]b^WZYLI_[Smz|z~qklswyzzzyrvy|rsnghsztmms|yxwqymif\Waif]TNF>CUeTF]ZPhz|}{x}ug`_ag|s}|q}zqmqzxp|yunozmw{gVOS]hkh^RJMaRD`jbhwmkv~~xvsh_ww{mhmplnzzsrplvwzvnr|}~vtomnllj_QNPOKMZTG\ZGTft~yw{}ume_ds|pt~steetyoz{|x}~||z}zup[U\acgh`K=VcMS^VPS`qsox|pdbvxntmlp{{usqpty}xqvpvwmnpor||obVMKOV`]FKbSDPbls~unpolluu`l{t~ohn{xj}t}vqw{sv~}nf`ahjfebUKUSFYfWMPV_s}~}t`aypdzsqliovllprywpr|tvyxonsz|iWQNKRaeLBZWLU]bjpt|xjkqw{ehyml}}rnrty~}vquyxzsrrzzylhnmgfcYNK]cEHb^RJJR]ntyshrt_jytuwuxthfkuswv}~{pko|zn]QORZcXM\R?L_ffgkigtypirlcytbn{mju||}yq~qzuz~{rrv~xzpjkpwzqgaRFWcNLYYUSRQXhw~z~sll}aj{qjpqry~{ukgm~uoswr~w|{{vzylf`UU_`JJd\EFXeihda`ct}|{yxps}h_kw|pkq~woqw~|vu|ysw~qhlvzyynZLN_ZKZcPDINRVbs{{}{gquguypfgqx}vnptpq}szvxwzvrzuwyqhfgfi]IU_NKSU]dd\QQ]m{y}ln~efsy{~wvwx{mdv}v{pqrv~~tpkq|{r^Qa_EOfbSEDNXafhjhixvwyo~mca`hyvmmu~ksr~|{~yqu}topnnvx_K^bLLZ`\W\\UMO]kt~yo}lotwzwturp{zmw~nvxorxzv~|o|y{~~pehpaOYa[SMFBN_ig^Z\`m~omyicehpzsnr}fuzt}wr{{zs~spt|n]gbLJWcd^QJJKPV_inqx|~yoy~ywsiiqxumskp{vvqq~~wrsr{v~{npnR_j]QRSROT`d^TNS\h{y}skdeow~yxyx}uzpjyxpmoz{oyxtxtgysWQX\adaTDCLV[[]`\\vqn~}qha_k}sj|uvf\bo~z{jcXG:?SknU>Fj{hB0<\tmfp^YmbgrPiyiVOU_jm[wykhywsxwjq|lYEFbyc5#BpzfL97Cbzμr_aeoyxm[LGHWunTi~c[h{blwnuphomG7@UoxgJ7C_ppg`[GBdvil~fVUZ_cfjnq}qYdsc_fs|plodc}xzvyvWMIJZiqm\D2.?\psjet~ln˷zrpzg\juoo_[aju~}qlkjku{mVaulnqppqohammXVb`XPR_krslX@/3H`u{tþysvwqb]w}tmb[]a`cea[Zh}vf~thelyx]PafWT`ptttk\PKF?81-5K[NNi{qt|~mdhmojd^ULUnsdm~y||}~}yyugZRKD@EI>=PULPbz|yrty|zusqi`WW^b]Vd{{pq}z~yl^[ZYZXN=.8NI8BX`^crpuzzyxrfZXbge]QIJ[oibvzyzpeYSUWWK76MVC;GVbq|~~xqlls|~vidc`^_d_NVttfjw}~}qeaa^YTKACNI>FNPNKLTiyopo`]dkjcZUVZdqwwusw|{wrqxrf]VTY_`[UVXY[`eaXe~tlzrnprrppomnv~wm}~w|{|{|xg]ZYVPLIFBBHOOJFObkdh}t~unkiilibZWY\aimko}sx~~w}xib_^adbZQPV]`\PHVp{vt}|s~~rmqxzxrnkkswq}zz|}}}spme_YQLKLJFDFFEKZbWRhuptvrifda^\]``aflypnz|ofeeefd_WQU_aVFK_f_cnyy~wuz~zsljkpz~tlyur{~{wx|{vwwqdYQKJNRPIBBIW`UGPewxsw{zshcddc`]\XYh~{pw~|}{wnkhc_^]ZXXXMFQ`_UTbr||}~{unnrtqll|rms}zwy}x{zx{|ysfXQMLMOME=BWcXHM_jnu~{xyzzxqg_bhjg`[Zatumyv{z}}}wpjd^\^a_UHESb]OLS_ny|~ywwwutsm`]txrrvz}ww|~z~~vvz|wpg[RPPNMKHHNYWKL[fjko{|}{xurmfbdhf`Y\jvrjqyz~~~{|~tha][^b^N>E]eYMNV_gr{~vvy|{rd\fzwptxyxxz{yx|{uzxxxunhbYRNJEDIU^TFJ[fgeglv{uononjfgfdeouk^g{yz|~||~xja___]QGKXZRMOTZ_cis|yx~ub\kyunosx|{wuw{|zz{}~wvytpmjge_VNIFL\aQ@G\hifejq{|vrrtrlcaep{wf]hyxrv}yux|~}{|{tjdc^QEJ[_PGJQVWZ]`gt~|qfjvtgahrwyxtppv~{wxv~~vqnnlie[OIMX`XIHQ[abbbfnv~}ywwyyskfpk^fu{~}{qrvz~~yssy}}yuu{{vneWHI[aTD@HPTSQRW_lzzkgsxh_`gqwwvtrqtz}xsxzuporrojaXXaaPFO\^[[_cegjos|~|zxtsyohovxvv}y~}rqtuvwwursy}|xv}~oYKP_aVJDCGNQPMNSZcnw{rjx}k``cfkpqqrttuwzz|}~~sx|urssoifhlhWHN\b_XUZafhhjms}{mkwzwvzuu|xuttsstvusruvw|xo[VacVJHIHEFIKKLPV[bmxtxxlfdeffinqrttroqz|yws}~}yusqorzzfPMWadb^YX]dfedflt~~}mo}{vuw|x}}{}|wqomnswxtpryyq|~mdgmfUIGJMKFDGKMNPSV\l}qllkhecackruwvsqyvuvwzz}}yrmovr[T[a`^]]\Z]dggfhntwvxvv~~zyz{xuyz}{snmmpturnq}ryrjqyoZLGGIKJE@BIPRRSWatzzkimolgdbbfnstsswx~}uwyst|}|~~vptyg`dfea]\_`_\]afilnoier{z~||}}swysw|~|vomnpqstuzvwqip}~oZMIIHIJIEBELPQRXgy~~vmiknlgbabejqtu{ywpmsrty}zzzxx}mflrmd^[Z]bc`]_elnle_e|{y}{z{~twwtw{~~|ytommlms~{szvkrpbYQLJFDDFGFFJOSZfstknyurmlmmjecccejq{ws}ss|trsuvy~{y}viku|ypf`^]^``^\^ejf[Vcx|{}yw~yyywwxyzzzwrnlnvpm|ws~}oe`]UNIFDEGHEBEO]lpg_g|}xyyumiijjhedcfr|szyprzutttttux~{{qlu|umeba``aa_^__YSZjtqms~}|}{wrruty|zwvxz{{ysmlrwpv|qjhfc\QJFEFHIGDGUfi]S[m}~|}}zsljllieabiyzu}wmqtmptvsqopt{yu}|vmgc`^_ba\RJJXhle^`iw~xngo}tqsx{ytsuz||{wtyzps~yvnmnlhbXOJHFFFGIR^aWOUcov}~{vpmnomigmx~zrsukoyoknsvtqooqu{}}wnhcaab`VHCQcg]TSXalv|{ncev~vsssuy{xttvyyy{|nnzvpopnje_WPMJGHMW]ZNHP^hmpt||wqmmmlow~yliuuozyrpnmnqrqooqru{~{~wmgdb`[PEGUa`WPOTZ`jt{|mdky|vssutrruxxvuwy|tjp}|wsqpnkgbZQKHGN[c[IBN^giilqz~}}{uomqytecqxyunnqqomorrqonoqz~{rjfaXMHNZ\VONOQTW\bmx|oho{~umnruwwtstwywtsw~op{{|{vplkjhe`WOLQZ`[MDIVcjklnrx|{zvpmu}kcjv|}wnjloqokjmrtrppt}xqg\NDHXb\NGJOSVX[`gr|~rlq|wlegnsuutsruy{ywzyw|ww}yz|ztnjigea[USY`\PGJSZ_eilnrx~}{zxwx~qgju}wzskjlnpqokknqqpr{{tfTHHT_]RHCGNSUVX^enysho}oecekruvvussuxz}{|ur~}zyyytnjihfcaacd[KCJX```cimpruz}yx{|okqx|}}vzyplkkklnnmlnqtxx~xkYLO[_XOHFDGMRUVX]biswpvzmfcdehnrtvvutv|{xxos|zzzvpkhgfhnrkYIFNY``^]ahmpruz~tio{~~zxzvqmlkkmnnkiltus~vh][`b[NGFFFDEJOSUX\bk|{x|slheeefjostutsv|u}zst~}zxzzwqmlpx~xfQLS[`bb`^^bhkmotx{||~qq{}}}y~}zzytnkjjlopnlq~}pp{xlekroaRHEFHGECFKNPQU]k|pmljgdccflrtuvz{vxpoxzuw}}zz{ytrvo`Z\`a`_``_^`ehjlmlhgrz~~~xryzxz{{xsmkjjkmosz~rpy|qmt}|m[NIGGGGDBBGLOPVao{||zomnnkhecdglpsy{z}pjsyrorx}}||}~wigkmjgdaaba^\^cghd^X\n~uu}vuxzzywrmlkknt}|rpwyorqaTMJIHHGEBBEINVamrmjt}uomnmjgeddgmu~{syxnluxpnorw}|z{ulnuxtlfbaabb`^^bd`XSYi{~xsxyvvxz{zytokjlr~zmmxvxsh`WPKHEEFGECELWckjb]fz{wrnmmliecbdkx~tv}uopyxspnoprw}~vlpy~zrjeb``aa_]\ZUPTcsyxx|~|yusx{yzyvvvxzzyupnrz~pjq~z}yngb]VOJGEDEEDELZhldZ[hy|zxuokikkifegn{wv~vljuvqpqqpqqsw}tou|{vngca``aa^WPMR^lrplmu~{ulip}vvy{zwvwyyxwtty~snqyz~{oifc_YQJGEDEGINVaf`WWbq~|zywsmiiiiggmv}vvynktwmknqqpopquzxx|}yrjd`___^WLEJ\lpkdcgp{}siel}|vtuwzywvwyz{|wllv{rkhgea\UMGDCDIQZ_]VQVamv}|zxvsnjiklou|~xnnwymnz}unjjnqrqpopsy|y}zumfb`]YRIEM]hg`[[_fnxzndeq}xvuuvx{zwuvx{~pjoz~unkigda\UNIGIPZa]PGKYfntx}~|ywsnjkpxylfl{xw}{somkjjmoppopry{~yske`YOGFNY`_ZUTUZ_foy{rjjs}~yutttutsuxzyww{qkq{|xsomkiea[SNNS\a\ODEQ^gkmqv}~}{xtru~vhdkw|yypllmmkjlnonnot~zqg\PHHS]^VNLMOQSW]epyzplpz~umlortuuttvxyxzzrt{{w{||{xsomljgc]Z[ab[OFGNW`fjkmpu{}|{|}ogirz{z~rljkllkiilopqt{viYLIP]b\QGEHLOQSW\dnwxnp{yngfinqsssstwz~y|zroy|{}}ztpnljhffhhcVJGMUY\`dgjlpu{vmlqx}~vyumkkkmnljhjnqv~}{{o_SPX``YOHCCFJMORV[cmz{rs~{ogdehmqsttssv|}zzqox|{{{zvronmorusgVIGOX^_^`cgiknrxwmnu{~~~zz|uokjijlmljkpxvrzth]Y]dcZPJFDCEJNPRUY_jyyz}tlfdcdhmqtuvvy{w~zsot}{{{xsomnsy|veRILSZ_`_^`ejmprvy{}qkq{zz{xtolkjjkllloy{qq{yoecfjg^SJFEEDCEJPSVZal|z{uokhfeefinrtvy~xz~vqpv}|~}{yzzxtsv||n]RQV[^`aa`_bgkoqrroowxrsy~wy~{zxvqmjjjlnpszypqzznfgnvsfVKFDEFFEEHMRV\esumjihfdcdhmrw|}z~}skkvyuw||yyyzz}}ob\\^____`aa`bfjmlieenxx~yty{yyzzxtnkiilqx|tpt}{okpy}vhYMGEDDEEDEHNU]gs{|yzvmjjjigeddgmu}}x}~wokp|ysqrv{~}|}viegklheb````_^`ceb\Y]l|yvy~ywxyyxvrnlmpxxnlt~wqutg[QKHFEFEDCEKT`kqpigp~vpljjjhfeehnzytz{tmls}xrooqty}~znkouwtnhdbaaa`^\ZXTRWds}~|yurr{~{xwxzzzxtporyxliq|}}xnf^VOJGFFFFFHP[fic[Yaq}zuqmlljhffjsxtx|slmuztqonmnptz~tqv}}vohdbaa`\VOJIP]krqoqv||voknx~zyzywvxyzzzxx|xnkox|}vnid_XQLIGFGILS\caXRT_m{ɿxlsrwwozyxplcUSU[n~zbXkuqs{}i]kSLzpAC_cSIJNMKQ`wpjϴphx\EPntbXZmt]`{zi`\_hrtokhq|pwtx_F;F\h^F46Kdttmgis¤rntsWN\un]SRXaiq}uo`etuhZQRaxwossfh{dGGZkhWHELRPLKTcwvovǷt{~`NMZp}obZX`ovoosurmjovzwmhlzzozwdN;37EWhv|whZTZfuȿvlilurhddeb[TT\fmqu}zu|wmaWRUasndeo|~oZD4-1;DGE@=?GSezohlquw|{ric_^bjxwmkq~uqu|~yqlhfhlrx|{vk^TOOOMC6.2?P`osha]\^bjw}vw~~wuyxuwyvpifgkpu{xsw|l]PGB>BJPTVY`kzsou|tmgdgrvkc^[YVSQQTW[`hr~~uqrvxyyyxx{tf\[ajmib]]afjpv}ö~uj^TRXeoqkc]Z\^__^]]_cgls{ymebcfikmpuz~{ywvvsk_WW^gpx~~||{slhffinsvtmbYVZ`ejpv|}{|~}tk`SE97>JQQJA:8:=@BDHNVbo}zvvyzvrjb\^hywrnljhd_ZXXY\`b_TG;7;BGIKPV]dmvtqtolcYOJIAC?;=Ibu{gU`ePTampuniiqjj~fSQYennmljcSCLvoeĜZGW{o[NLT^p|aFXcpyVP_r|maZ]oqjz{{vojrzdpW[iswy{~lWNZsn[\vwhw|j~yYKKQTTSS[kqvsjd^[]fwr_r{dfzļ~uypTA=EQXZYXVSMB7+#)Bcrj^gʵ{siedkywjtifs~slmu|k\X[cilptvuph\UU_rk^`hlje`]\ZZ\_gqz~{qdYV^rǿǿúh_dnx}}zuomovsnopniea^[VOIGLWiy{ncaflqyyleeo{qljjigaZPFAI_~~|}xm_TNJGEDHQ_p~unlmot}{qh`]dt|rnp{j\USSSSTTSUZbkrutojip|~zy}xqlmt}|zwsmiip}qf]Z^iz~{~{vwywm\L@;9;?EJPUZ\ZWVWXY[al|~|wrmg_YUU[gx{uvzspqw}wph]RLMQVXXWVUTSPMLMMKHHN]q{upmlljbYRQWajrw{zumfdjsyvmaTH?953235:CRcuú~zz{|zwsolknruunc[[blsxz|~~~}yuqmhc^\^gqyzumgcbbeimpty{xvutsrpnlhedlyrfdhnrsrpoppomljigedfny~xqlihhhiklou~|qia\XVUSPLHFHNTXUOJMYjz{rkd^ZYZ\^ciszvz~~~}|pbWTX^ab^YTQPOOOMJFCCGP^ksvwx{}Ǻzx|}wroprqkbXTXblswyz|ywhYO<71,038HXliL?Gb~ʻy|y~}n\MEOjjZndT^xvp[deledelyrlhk~zsqs{~pP8JgQ(7mqYRZhqvzxspwunrxz|yrifqiRkoqwgevfSXnxumZu|aQID?:55;IYc`Q@L`s}{lXH@AHR]gqzyrkfb_^bn~wiem~~k]TPPRSUW[bo|xvusqoliecabfklfWF97?KV^djqxvh]X]kwlea`beimrzvrrttsrsux}{wy{}~~~}|zulcXOIFEEA:32:JZhs{~xvuvxzxx}wpmljgeegikmoqqnkjjntz|xpjkr}}tgZMB;:AOaqyytld^[[_fozʾxlffgfcabdgjmoprsqonptxyuj_XZbksy{{zyywtokhfiq|xvtsrqqty}}vj_Y[bjquy~{wspliggiljbWONU`lu{}~~}}|{xtqoqx|yxz}q`QHFIKHA:668;@EJQYajqz}xrlghp}|uu{{rljiheb^\YVSQPOOKC91/29BKRVZ]`ekrx}¼|~ww|vkb\YYZ[ZYXZ]`ekquvrmjlryzvrnic[SNPXdosqjc^]^`bfkr{¼~phhnrpia[XZ^adeffgintz~|tkggjnpstuuusqpqrqpnqy~}~}tdd̻qspgnu}~r\G:;LYk|muy_ym]iukxxgyiZf|vjhaQBOqsIJq|ZUpxagvvknVB]{xMBTXcsqrs{jspnwt}wytyn{lmo}qr~vgm{qn\bmlzqzrVWTeqöƹusggy]ZT^aVhtqwZZ_VN;>3&45a{{phz|mjpcf`eq[bcouu˻j{mpttt`Ra]\OVZNSNXlTUTdf[hihbI^\\WWkqprhv]SRZfl~wurüdmf]F77+.ARutU[g_[JLOCICTUFMQ^^^jm|kqlwtnbji]aZnwcehzwqpaz}vlsUMIU]eyzxkt^]e}xwu}ysusokQ]ZTA>IKUht}}`frleX[_SWSdfX]amnrĴyy|upXn~|f\ih`PUSFE@M]EBCSRITXXRFi{urmze\[hpuvtiz}ksƵ{zpjJKLG;=DO;9;JQ\s{~ej~qqruneed|k\Ydh]\jmwkld]gdRS^pwrd]_zpszvyzphid_L>PY^Ygnhgiu~z|~|bsywlehgWWVg\SXm~~svyui\wypuqoZ^ijYTZYONJ\\FAGRLLbszzuqshZX^iXV]ot}~sxxojjcZMcqteije_fq}~|sW`jfXONPA=>PQMYt{pryooxum\tǻ|litvgbjj_ZWhmWQ[kov}wshUSWaPJPciv|vop|{g_dg^Z[dijhrutoiny|}xzqYgojZUVZKKSmsyuhjobVYa^SOqѿyzqY[dcQQRN@;;OK:;QdoyxuwmabkmYS]kox}u~ngogb^it|ttkafhcbio_ki\MIHE9AUppu}xzngqlbZdeaSgvjxthlhbUS\k\Yjwspr_YYbSINbfqv{z||mltxf\`fahp{zhljf[`oz}}|dluqa][b[cwz{pbij`TV``[gtysiZcldRWVUFBI_[Zkw~qqoscWV`YMN``ehw~}~uv~shlww{ef`^TXf}rv}ejsnZVVbbiz~~vzyuikw|g[ci^WXlrsxv}~wgjro]dccUPZu{urdNLUUG@KNPZcx~{yzgr}opkpb^blbVYqxrnnnh`ew|mj}}~}kalna[drwop|r^W`UQLbtxr~{|j\bgYQTNLAKe}zswvln\V\g\MJXS[Yervt{|||ljpwmlzz{p]ecfYU\qtowmhqsgq{{|szxs~{yfcnzjWXc]fl~}}kgrua`beii{z}libfTRW[K?ESKUTj~ɹtv{mlz}ljllkX[ixx}tuzsemggTOZqsmr}~xxdgztzxvedkscSZlmzyv~y|se^efTVbl{xx}qz~o_d^hYRSYQGHUKX\v®|wxwnxvqrmsemkltp]bWVJFUowom~sxvx|renzvelek^UYge]g{ywivtiY_ifhvqvs`c]`U?@JJ;4FJWoŻ}uwm{}mZ`on[`[ilqwpy}i__Z_R[mzoesvuk}~xvhkolfRUdsrt~ryo|xyntwjY\lxw~xkamtgee[\IJRUIBSfqzu|xzuhjvxgour}qcggTITP]TWcttpz~y}~yuqnl|zoejobejcgYayvwkyx~oxonwqjq{ytd[gpbXSJUD@EMJJdurxvhftn`]jqhy|~wtc`aSO[XcRO_ttjs|~w~ptqtzg`lsbefgohzxznxtkj|z}v{lt~}~pgtk^^jkTTUUZEGUaenx}symmbqxk]fy{|xrYLQTDORNNBQgsml|ww}jb[pult]_ghtliyzoUTVdzrIBhf-@Z]XYZU_rnʯ~}giZ_iuWCux|m|vZ_fosn\=9[ft[B?LW_yz|ykNCZy{tmxytRHi{^axxqfjg[hf|pub\]KPGWxxeSHOthT?9^tz}q[glo^N_|~mssfn]iwuke[; 1=_d^grqpƮf[enmszsh\eUA?Vs}smxwyej`OMhrkZry{±v~v~w~gUMC5Oblu`[`f_Yklqwdsz}tusg_kmWXFQ`lwvg[G.;=4'1#39@dôuaf^_aN]bak}q{~nvadmjhu~iiXfdWar{{hxyxqmv}ws|phSa[E35A@Vx{{uezw|rr~v}nr|yipkcobleN?CG6*7/CL[v~zxsrkk`u~onZdnihoseZm`gcYgy~}s|uv]]homir`PZKSL<7CXfy}vslqj[QYWN_JFD=51/$53Q``s{|rvrynk}ymdej`k^Zi^kxou}pqw]dYR\ihYSUIUB=FA?OmyxƷ~kb`^SRbSUI:NL>14;2J`dxqs{zxr{wty}yvxldj^nkWN\j^\g`y}{hm]`bV`hbPPbROK@UZcr{ŵ}wwYTXVQOXB6A6A;-(/;AVibvlesx{w~vm{gfb\\ae[]qj|svqqcP`WUeXVSUVT\IGQF`sttty{tpxѶ}in]Y_e_SRDIU>CD@>Ndakzitlo}w||sln}xm{ypfqkddGU_YQS_^k|lf`WiaiiPRag^W_R]gm}}vrx{ǿwv^bdU\bZFCRADKCoĚO%nA=_eoSNPTzt{}vb~yskepwXSWeok^_mknq{nc}ɷƩgr||mC!69Rac{{{fspkQ8S[qx|жV:VщU@9Goik`[uqWS\k]~aZekn{o\RRhsx|ֹrwKF,,5>YW8>KK0>QUn|}ˮhm`gvqgh_[pch~{mUB=i罚}qj}}zr}}[ssmL8;(0Gdtn`Vk}~vm]X[q~xageTc^93;KMfjattz~tqie]`nttimqsp~}v|qtg\O8.,^rϸhc`]YXeACEYPO\W\ckscjdxy{xr]SJKOIgnmwq}r~|prmhq{vhjw_stos~{wxekg|z[QND9 &[mýxvySYPaUhpga`eaNNEkry~ngddjmomvup^^s}xjt}p}zmxslnuziVZVyoupSH=84"$,O]Ľsvu}|rplURSSo_v~kvzvlsz|w||u_|}{zzgXWDRkophtajtzx}{ebV]qi{gYPQP;3/EGcikZoohxsygdldig}~y~yebH:PYWIFID]c~yxsoyxkmczx}~kxlvyynb[PRG:C+@O^´ô~}|wvllek}kwcpsvqq|mxiFQZSTIL9KLbgc[XY^xxjzhwwvx}sum`mfbMFcѴŴ_\QSdUmu}wSc^rbmtgi\]KYVfyvme^TeaPg[acYv|lyso`^TRN/--!=7NbffMV[^{~wqzwYe[v}qQ^_Z[RRKaXisrklb^qictbofgz|xvv}uso~ym}rcW\V[T5@<6JFamöqqXkhr|zu~vvtvakknYlf]YNHCOB[gfaf`br_[qevnwtr{wnhvk[e\OGOMYW9A75NRq|ʻtx\ecrz}ssvxutdolµixqfaQFCTJ^hbY\SM[LIb[oqulwj[kbN@E?JH%/+)EQvĸȰhkLTXf}yy~gsnTZ[}iy{opcZWeVask]ZTL]R>WR^hn}wjrnZDE=@L205%?Moʴhr]U[^}{wxigqW`d|dkwmtm`Vc\]xwighY`_BTUVfpq~w|jmr`KIDGYB8B.AQnzn`de~{{qlzmljlodfmcf_RN]WVu{qlgV^bGY\auuzmq|uks}bbiYILGCTC>L9K`xvfjq|odv}unojnvªllk^dbVP]PMjk__\MVYD]^crnqvniy}]]^H;925I7>J3Gc{q|d^bvus|kigUnh]curopiamlUeulmlZKQ9AQL\k}w~}r{yilTEC@7D>4E16NrĻxy`[i~tzrxccudbgz{qhiiafyi[mmmuoYVRBWQVey|}ijrnkptfHBB>?M=KJ8Ecshk~nmvsytm^vmeiz~nig_ZniQ]eaorfY[BOTP_uolrqevnhdE;;<7IEFQ77Qwý}metxll~vkmWfoehr}}uoiYfuaZe[eniSOB=OFOgssxnmwgoX=9725@5I>+6]|¾yz~n`ds~{|zptnbTh_Y_ovúyuje|xcid\jkcRR;FG>Lizxswzn}wttPA;8-8;BP7)=cz¾}upcjq{wwzvs]ZcWZjuv{zhjuircetwj[S@OEMU;.Ed}vnst|ss~vv_ei]bx}~}yso\gxlir`irte\RFVICYzxnhljg}yeogKF@?1;;SYC9Ro|z~|s~kqrp{}qucgeX`~~xst`fvjfnX\gnbYPFXMCSvsgghgzcicH@56(02MZKCWm·xrxx|y{hoojyuvil\_`RY|z|hgvleo[[fkcUN@PG<4-.8VUP\nz¿pus||uqip`mswnhjfo_Tkuvsfuvlwpaghq`]MTYOMdr{~~vubf[j}oekYU@<813<[a^ky~nqrtykbjZenwndhiufZnytseoldpnaecqdaRVZSTis|xsyghYdui^aQT?9616?\aasȲilovw{f^aQ]and^bjuf^w{yqfoifsjea^j\[PWWRYotwy}mkZgpb^]QV<9//1=W\a{Ůz~dej}|xd`\OZ]oa]^lq_Yu~vsvjkwjm]_e\[TZSN]joo|zsl`ppef[XW>:*13HXZe{źvdbo}vybdWZ\fp_`bsjYb}uxpgtwrt_kdc^^ZOPfex}qi|tvfgqhccVaRD929?RY\r{}nghvskcaV]Woibbpxf\s~wttfhxt~kdhbeceYQckkzetvvm_if_cX\_ME16:LUXg|·{~kfsot_]QZQ`mbdjym[gvxzjbrszu`f^edjaSYjczmlyvwdhh_`\T`PJ3/6ANR\x{xlcdy}yg^WWXMfc`_mraZmqyeertme_\bfgVM_ce|iwxyjne^cVZ[RJ/28FJM`wt}kck{vg`V_QTe_`dsn\`ipvenvniZ_ckgURe]jwtjxxtlla`cXdZZI16>IHNjxzofpz}u{f\WcPYddejwp`fgsuemsrkYbislYYg^nrliosqlg[_aZbV_H4:FOLTszqit{uvdVVaOY^bdlwsgnkz~p`fmumW`htn]^jctsjhloskbTVYW^Q^M7;IQLSt}sjsx}w|}xfUP]KRT[cjuuoumy}~rcdhunQWcqpb`kfwqpklkznaQQWW_N\R97DPLOp~zwjnru|~lZLYLOPSahrtrxqsyiddwuxYP^ntjaicrsszglb{vhVMTVbQT\D9ASTPf~x|pmrtz|rdPXQNTK]hruvtxlebnxhQXkwrhldqzngj\n}nXJMQbWRaQDCV\Vd|x{tvu~||{tshTVOMVIYlwy{ztje`kxoTVjyxrthspiiXey{nVFFK`XRaVKFWa^h}w}}xwu}xw}q{zofSSJKUFUmzz{qgd]fzsmTShzyvwgvsnjVet{pTCAG^SR\SNHXa`m{t}tx}yus}v|qvtmeVUEMQCRkywyofd]jvtiUQgwvwvdx{v~sjXis~qSC?G]PWYTQLX_`nvv|oy{tpq~tvsod[TCTKCUqzuzlhgbss|}dSTlwv|nf{sx|yyc_muiMD?S\S^VWRPX\dpvxp~|qqzn}sqpmb`LJXII^yzszijeitsucU_sx{}fpvs|{~zqYejxz]IAD[T[[U[UU[_mmnuyp|ozvmlmfdYFWOGOl{twlghfrnl^Wiuxqjxs}{z~ccio|kNCHBU_SVcm^Tgmoxij{{w}z{ofV]cn\SPGMQYiujcosamlkllb\lzzn||rsxvvmffdbS?F@K`WW`pjYbopmjsx|u}lytYV]leXVGLSZbruehvehjhlol_c|~ovzslxuwwsed``_JHED_^\`pwgbssump|}zp{mo|`RWjk^`MNW`cn|iduhiifnsufaxstxslqu|uwwgd]\aRLGC\`bfs}situ}{{pr~{myml}}dRQflbgSOXdfk~mcrijhcmuylauywxsnozu{sxzidZW_UPHD[`fjvyouv{v}rs}zlykmy|fSOgieiSPZggjofqflcalvylbu{yxuqopy~y~r~|meYV_YVGI[_hmxztswyv~su}wnykryzgTPhfkjUS]hfh̽v^jl|n\_\NQX_^`carbK<*8F_r|ǻdeL_lufaz~²S/TtTILDVVUbv{Ȱs:Uߔ17]`\L26DSULTbuyygjÔXFOflm|sqlhjhfa[uzԷ|ws~mgaGT`ekq|X=?qmn^XTB?Memrͪ\%mx;)/=SGGSae]XdqyfY[TNpo[XXNfrŵmarkvw}sgQHCKR^xwipgvbJGXt_W`falMBժwS8DA>Mbqofhjwf^UF9;F=WxqkOK[b{|_H\fm|y®xf[VTbvilmrnQOPl{yqyV!)[ݿkRO:7F\eb^YZvrohRFE@60DGDb{zkGIHPgz|~zzyqZUmkwxyyssvZLNTv|b<3lɨtaO4-7GJFF68QLSkpRPEL]lonwycmnmojlv|ydepmxw}hJHMZ|̽t^O-!>yҴt[?39DC?=-VP\px`XIO^kjfqo]fdjmjqlklkwxs]?FPa~m\HD**H־gN@BJIFD1KTfojpvaVOoxvu{ypy~q]ZE?[tsz~m[H7$#77Mj϶tkcfjlsm]cet|qb\YR?DOeõ}rv}r_VJkt{vrv~xlpv}|ztt|xkWYLE`yr}wv{oxscVF4"7A[w̺xpkmos|yilhwxj`YR@CLeŹz}{txxj[WKjtwwqs|wkmotzulltsfSXQLe~rwz~qntgo{{|m]SE3 8Hh̺zuqrsw~rqiyylbXO?EJiĹvoppaZULosyvqt{uilir~xpffnm_PYRVksvv~zkjmalqwwfWPC2%:Os˸|xwvuzzuk}zncXK?GJp¸~ojihZ[RQvtxsw{sjkdt{wjadljXQZS_pvts}tfjfajjvp_TOA-.<[{ȹ}{{xw~qpvm`TCCFP~ujff_W[LZ{z}yv{{plhez||se^ejeRWWXft~zot~~mdh^dejuiYSL>'$3Bh}~~}yzmx}ul^NBHD_xlfcbW[SLd}}yqnco{||k`_idXTNXWRzzsgeztgQhd]mywd_TH6)11OqpǠsy{~f\QARRkĶ~tgbPU]EKGg~}rrqjabr{ng]Qb[RZTnh_rxqzzy}}n]ac`akwfY\DB33KIYl~Ҳ{}n}}dleKIYny̬rrlesaXUP15e}~xtphW`mtlrrZ[ZP8U[Zptzwxz}xfT^V\cku`YR//+L`nǬww{|W\ZQZKdͽ}dN]MVYMXKJf|~v{xfck{daiZh^][OQ]jfzqknu|acc\FehbcRVC0)?Q^yʾz}{lXQ]ONeǻ}tcTVZLPRLIj}uh}lmqqueWf]V[YOQeems~t}mms]d_de]^hpSNLA&)7J`rynSURQJZxηvmZKNGGAIL]swpmzemz}a]^WTIJK\^pt~|mrjT[]WJ`\P@98596^tʿ~z{rne\OHdYiŸe^Y\HKWDORcfykxplfbmh_KZ[T[\c_wqknxsdo\VJIOV_WbG=8906[pľv~}ru|snMJOOh˦xn_QT?HFM]htqêtmtvwfrmlzzlNPBKVcmr}|}u]\qxtafNMHLKQ[HIEH;;Liʯ}{mfsuv_VM^wsɳzn_S;:.PZ`vylro]dor[N?YKVd{||{acdvnndVWUT?FADM6A@MGvƲ˻|{ftpchbVBch}ɸ|dRGG32PUrlvxuwlfp]njd=CGHYQp|sirucfNYQBE@B5O@C=1HXuϽ|ywfl^kUS\]enżz{lV.657;Tk~}}kz~zboZe^V_PJ?OPft||r{Zehs[[\JO-.8><6>=RUrw|~tv`d^]\YflŹz{}neHH=DFKWkiv}|i{wh^\]fUWMPRcpvnl[Sg^WDE:A9ALG=8BQnkȵ|{ywnfjgnfSjkưê|wt[H?P=@MU||z}zzy|tw}qeghqc=D9Rcih}}}odWajjr`N?=8J>AF3H=P[xѺϾnjU_g[d[kdz}c_<;9B@Gln}lnZ[d^eQQ:T[i{o~v`[^zm_\>G254CA9G7PFT|ļ÷z}_nijkJ[_m{ƱxvrpGI00DEYcr|ijp{uz{cpeimGLABIR^y~cdSdfl\KN4A(-GCE::>VZt}k`_\Ycd]xȽãt~||w\@0<2?Xdpxoypc_XYb`IUVSZjtz~uvTc^XkXQ@2'30>RKDMP[yuɿͻ{zx{seadZkle~ʵqvwvRQ>@KBRUhu~|uoq`pm_mM0QG\_ls}k\TmrOaU0?<7C6=;7SI^lغԽ[jcY^ZY[vzdzz{qoP<2/DIOeqèrfsR\`RCWN]cl}r|wiSemisQB8-+(:DHA[GUn]ĽƷ{qwpmcXqfkZRpq԰īzvunnmb_R3M23Qats~vxzh}`kkbW?FKYrstq|ytWdMa`MREH.6.'DMKKVUyʼǪlmVScWNXtmϭ~ich[X@34:JRlȸēt}~xtqRb]RYZLNkml|kuuxjq_WwTTL4<3'*:BMR^]fxӽηwxrk}dPmm]Xecxjlf`k`fpT@24Cad¹}rlsdiv`[\LUgVozxgvxwaROOYfGLN5&"(NHCfUpϿum}y[mSKXWsk|ʺ~x~}mmY]nLRJ.?F[a{sw~fs~ojqTc_UX[YPgsdclwirmZZ\LPJ<>%&1?VadȰοx}kxmbdnXOOktiȮjmcL[hdTYBDAOjdulzswsqwqb]Q`cj`Spy}wqjv`YaNSMVO:0$1 5CP^t¼ƧzmvytjgkJEajwrkǯx|wwZ^\^]JZOKPGHi~§wva{{~krb`PgfIM`g{fo`_hxis_TIJiCB,%'$HR^|ջùýʬzogltaWMNQqtvuwu`Tl^PLUGQbOPdljx|h|vvriZEjp]cXZr|}oYvhrsQTLSBF)'6$'8;_uu࿱ϵnwguvcdV[`_Wrw{~}yeXo[VRVXbXMF@Y|qwvcvf^eO[UKSatuinjalPZM\QN76#2"+CYis˦zv{bvaOYQTLozh{e\`@NKZcTUQefxw}fovbc[mS_`aV\uwvtygkjsWN_:B48,+51:3Y`cͼ}w{arevi`_=cjyxyn~a`V[lOnaLP<]\]rŸs_syyrpvK[JRaYXss}zso`nWcWWV4E847*1&4Dpsùpv_kd]@PdYz|vhPU[W_QbIM^DYcmsz}tx]c]%3+FWvjڻ«lng[nS[RVoa|{oXiK\oMSJ^ICRK^iĭytuqukWERQbs}c\ziqoW^>1SE>AE++53A;eh~{musih\q_Ko\xw{~fgx^]bWcHW]SZ:Jirnu~xnH^[Ziqmgjmujkrc^kO;IQYSD7,5/1;SFkҸƯqlnqiZcg[RHagorfgdaVN^\TKKZDMflwvryz{j`S]eUmskp|s\]^_SAMULB/7)8R[OPVOBWXvyrqo}rdceWWjaskfylvrj^X^C@TD72/(/5WXl˾ʸkekfT]TV^CV}iusbeaO@LEXYC_jX\wòykvwqo]l[Ife`pxfbo\V[T<:9@P3(<)&5V_xѵwpLWjX\jw^]bvxhjcwoN\S>CW\O_iy{wʸznx^Xzrr~hXVXhq{r`aV`jNR\908I8:8>BAIXsüɳ˺}^\U[^`iknf|}ta`slc^VVOMPRZ^}tlfeovrrmfXUmns{qwqXV_hZ[RNE@;32$5EXh\nnxmMfjg`V[l`JjtxbqjVSfKGU?3% 3;%%GATeҴgbmcGX\U`ow{whSHYdI7OBNWtvtt}Ųr_`rnWis`bdtebgm{ibP@NXC-?27;LI8/:La{Դͺxitd\Xhoztgus}}ewhcbqoUBBMYjfqzspmi{qooyrwiV`RgqVh^]ZgiQ:48AL==;.IIQkԾoW]KXf`atne{rlknve_R=QKLWoXp~xzr]j_mse[g[Kpp|o|ddcekVWN4?304E"2=9HFgžȺ~gj_Hf]b\Nfvtrlud[[mGFL?H>Jnymm|uinmUwplaHWaWLhy~_i^TNaD=C+2#&AA0+CEctһ»®|TXbWCS^grvs~cbOKecK:JCUVu}vU[kh[agdkd{sdlbrYWI=Y^J-2+76CVJ?RbuϹ˳xwdvr\ShoryfbZZ_`tbMRbgylmquynvgutdpnvz`jli}y_USLULg_B:IHUG3D:I]ixƷƿz|b_fThlpnv}hqlvqRUJDZ^cmvv{q}nbxfgkh`lkazvu[bcbiLDC*:?CMbdfamvcca`mzrvl~{[hc[eYsXROp^^|ʼx}pmgssUY5*:8WrperupzZ[T5;5[GL]ru`quiS\[]rymvnl]Zl^RyfWeekkmv~z{wdbmpf^Q?:;@\ɹll}prxhicPM6VQAQToaa~oee\_oi~akrhppekVeYVUfihy|ĥҷuu~hhvgcUC;<Ij̾|yzqzu}tklOEF-MT@Son]yv|P`cYcm|pitlm}{bhfl\bj_s\|nqup}y}|~uhYhpJWG?<5NoƯ|}s|~zoyubZN=:W;VUctZzpnYfjm`yzbpy]vz{mzWfXZhbgm}d~Ǿ|}k}udjp[XH1G(*Owĵhtt}psq@P=1H=VUurvsi`ccjebpkTsjjhokgicQqlgzsntmvy~̷|{iseWaWOL;KD;W¾u~q|~|umznIRDFGD]\{nonswX\pheo}|Pwhqn~qv__^Yolpmqony}{~ϦhzotmlbYML:=;AXgϼqquyoxSP>9Q7NknsytxWj]akXkv~mwXRdxĦ~waxkaa][spyuunt{zzvùton`ZfQ1R;6TG`tɠ{{qudAC>JDZjoe}iu_ihdpmplsws[Ukg|zcmcfYuc}w_rlf~t{}uvov_]b_T;D5?DLqmƻ{h~}|_IM'MEIcuq}eqohd_ki|{|szhmiNYeƼjwzz_Ueh]}rpcpupwzkŰƭ~lzvbZaJeX*88EPns|z|k^^6A;Yoftzutnk\_{{fruZpbLac|ľŨ~|zrjkhjyurgfl\`yyuwǫ~pt}bWYPM_FEB#7LbtƶuyvuV\L=Tdkkqwzr\_uaryv{lmk^bXbanpqljhvzzplJblak~{z}[MVXBCUPa?-;doy|vutrt^OHblcr}t|j\hxhit{wo_Qg]Qgnkx{n}xov|ddVYaa\urjUKIAFEEcC2:Vxõ~xzgwezdRVaehv~~wab~xerx{{oxg\RYZWcot|zuvtiv}ggORfYPmy}zoUADD2EBUO<2^xrvqlsnfuhj[Zhu{{~xboy[criysq]aI]fMX}¨ö|owuvjtyl]^TZVXltsr}pLA<>><2U]I@]yy|}bcso|mmskc}zmzrgroUa_mzfiegT\i]Y}Ĵ}oturgd{se\]hRWry}tv{reUIA@UTSiṭ~vuypZD/19PKCYttxh}q]c\ZvĮ{ƪlkkaegV[_riZX\WqnYFTPeo}ohSP9%!#Ldl|opqulʼxTF?MSJUrs~zme]IK\WhxkQT^`d_ȸospsxcYYK[ZexvgjKE99:G]abvrthPQPNLvĪkmwsezxndmzvsyqehhva][d[@5ISijlſ~i]TIUpxxnk]S`gpɶupj{}pk\_lfz~zZOTZQOhx}|tkX^vjvnZYxdcVTSWdKCXmzyb]ORNWj]`expY[mthtyfc[XVXVIIbdq|coggXSbkz}ycSNRpz}]LNP?Fsz~i}hTQD::I[Sf}nrgtrwoz~q`L:OIfzncii{ilqnf[fsxd`]n{vyyX2'FkbfaSh||oXE?YPQ?B]lon{nacREHHfz|_QJJYVt_ciyowgeid__{{¢wxpfakyqzq{ȽvQYgql`w`EKJ:$EGZPEUyŪe\BWTe`]=EWYQA?QMHNR`nrsp~rpey|hvzlXmeqvfXGWrγpdP_iddspfhdojc]qhkVSd{{uokuejnzc[odvuT@]VGEPKg\{ugs||wnv]M\xs|rr[JZg_gxubXY[:@837>O]βz\hlXDZ]Ydy||iotyjcexvwhZ[^^[is{yoak`OgKI@YXYu}°x[ikan^j[`tlxluƹnabskkv}~aiskQDK7YjsyˮwyaXlizaRVN:,ESRUsxwmqdztmRZUdsbouqxuk{uu^|zxvh]gfrk\RTak~}neTBZVYYx\Rmeubijf^b[n}}}YvYTaVKY[gvxxpzrn\ZOLVh^V;?8Nszìs`N`cX@K`pc[inotP[]Yh}ty~UNWmZ'*E_R57Io¯gXddoPHKG[rgNHVsmd^Sn{ll`fv|aUD.G_}~|Ľpjv_^Xdmjne\Uc}tgs\=<>ESfrl`LQTRMLPRxrhwmkclo_TD5;,0/Qutyylw~wqkk^SSRX[nyür^WjtPRXsytmgy~~zvannyvs}yd]81)-:CcoejZaz}OI^ijC1:In{yvspp}zmvqi^cL+:8NVnt}žsdotlqzqw~ze^gRgol{qssnywTWS@IV^`D[szy~emzqysgth]bn|h@:FLbptsPWWYcuaad[ff\_VTYMH?RkxǸynEOa]iaūt}|zdFF#-JWqysjYUqx~wcecyuuuoggVfhQR2^smkdkcjygpyjmvwdE,\K\]`vbnüǩbmpvPj{~v|vsZveVjnTTMC.$.$-=DZ^پ[]kk]RZPGS;6M\zy{~}j|vuox~iyrgiFGMI[ayǝrxTOMX]`hXp|}xfnaSlpX_kcuzvv`Qd]`dowz^SUCUGS]dzs|iR^lXNVhQXx[T8IQ\aTrpĨqnzimlrzoujydufkz{vvkzNWO_LXT[YdIBHLYUglsh^M?UPMR4IQ`Tauyk_hYTS`ujfShm|{~to~m}kxuNgo|ly{sqmġ~of~rON>I:76?=@F@c{_zybkgRQI_`aGIT]c[VXlX3D9?>7Xa}q|xuS]\Gcūxm^jax[_EUvtwms]RNZsvnkqkt`]yrkrgcdtzrPZW^WREQdHFfowp|nkl|ykuy~|wxpohaJ::T]lrpxnxztxWHnhwnZ_ZiQ_xvgl~g`UVar`GV[ky}tt^Viidj[ַ~imjwvnouc`BCBPzglcY|~sgQjpf_svxXSsmocZU\?KRUX9EQ?AATrqm`bfUltq||~tzá·jf^h_Pytp§|cx~}kfcW8P\RI^flwRC^Zk^UG?J4>G[s`u}vd^dw^fa^kvmy{mqyjzkmv`W`__nɾ|z|rtx|ez{{T`m^JQTR[8//GMZafqcafjcZbrhr~jjUKVHPQepinNe~znp\Z_Papqsȷond{y|oL[dpsgjzVOheVYitvx|vsius`YoomgWVhecp]\P3CDTb̵ŵxw~}ttstklch|}ptrvs|}zKJLN`YHJf[Rwwwjr}|}deihiXtxaWgsrjVQdlqyqkcOoeKELfwönkki{zmtrd`QXSZgAGTao|}qmnnhn^ex~jdfbaFdl]e[]U;,?\`s{u~zw}utt|vlmzp~}}xq{e^lTVa\^U_acnqmsmdc``vsaiPXivw`[YYQ9[``igwzjc|ieryzfahumu}~xzo[JNXJf{~p{dbp_q{~t||suiecZXl\SR06CQL;>EWV\opy}y_W`fYezffX`tptskj~xjrjcbe`jvXKfnw|rtkcqyn~zimd\a_V_p`pcTjuhozqf\`hm}lp^D=IC;Pg{wv¸|k}~}ztdg`afkuys|tk}|s`botcdiTPO=EORVypdz~tkzrwybbhkzzky_Sdpp}yr}aoxrjb^UXkniWlxk{Ҷx}xd_{qhRLTb]^plm~rtu~zcmyuqkXintx}mT[UHa^>;AM`}yluhm~jdzw{z|z}yij_reYerkbck`P:Rbhf`sw~l^r|vpl[Zhjtpsw_XcW`wlpy~mqqnnz{vbphQbsqsomddTnǽvvrhmzc^r}ttzyzluthalh}pTLclg[UH<)69J^^j}zxvswprr}~|^UcTKgwxpuvpjmh[N`b`LLD[kalƷ~{nfkl]Zaf}mURjrtqtngb|t|tjli}xwe^USOVbf[``MMSjvq}Ǻrnqujg~km^[u~x|ynsuqpyrnmy\SPO=9=Ie]QYwm{ppl~}ql\\Zcryv}xttqshbsxjYjhdGBJWZ`wvvohpesyb_q^YZc^^gy~ncvry{|mVafc_MKAQThlbcngp|xr}ys}rl~|qhyxǴm|xw_O]epbijyzķzqnvfgkkMDNy\TZyý~rwF>f`J^pc^Wp}źgcx~\Q\RwjgP9STddZRVRSNmy}\QGQ[ar{}xxabjsluwg`x~ȹ|xdiiPPGUTfnacl|ter~xw\LH?FADbt|iQKO>OVcoidmoso`I;:AFTuyvhhje\oyncTejxvfnyqfulPPO\gp}]WEF?5Hc|iiTP\asofhmjb_|iKE@FGJ^jaicyreimul}us}|tnswfl]HHUeen|yfSQO`Xlunp{uxjm}jt`XRNTHCXeͺiQFDECNmro_JRNYZC@H^fbjiagLNOB6>JTNb`j{aZVaibyuuw~ymxworYTCB>.0EYδszw{ug~kaalnpƼƱxrxvolNMF70:IU\{|sfoh_kba_lm`[]Pc_]WC:IX^MTXNQGONBHWk|mb_fb`vtiXPWNe^QNYhlp~t|mqlb`gptwwpzyynert{xliUSK:;Hb׿pexxyyqddmifw}zmfmc{|rmt}~~usrYSC4-08=GeduobT`WOVNUXdhcedYnnxyh`n~}xwrdckϾr[TZYUg~~ulbhXhne^]cbama\dROB5059>A^]ckmxo^UJUXP]Vakx~kxwzn]`lullrdlYLE706HnwĹx~{|vovp]eRI;&!&/5ZlvyymthfovytqrZ[^Yh\JGQ]\\j^jeTNC:=Fhq}aOPVTZt}ļx}ml{wzvwsnxykwjh`KCHMQRr}}utqyxyqyd\ahdbl\eaND83:Foʾ~kjpj[\dcfz~yt}ywpwfdpkrpoh`ih[fXXQ9/7=CFlqxhbn|{|jkicuqa]dnov|vhcekƸ}|l\Z^\cw}žuojoX[_[efe]Wda_i[aX@6;=::[anxnpoih`e`V`NO^qurx{gnefyvihmuq~wp^MIKWw̿}|}yt{}smugkfY]J1*03/@blyxs^`n}zu~uhl[foh\\_`^rknypcN@?=P_i~{t}wusgd`_ml|u|s}}quxrsvxcPPSQG`nxsqis~swolldlvhtpeQ9117PXwÿnkmvnc^ZU[^]xw~x|}|}zsynysip`F:;;0@;6OUj{xwswymgovr||xvrpfv|u}wbH?=EUWxĬ{vx{soxut{qp{mdeYhc\cXC643(2FOnx||}xt}|ybgihhgd^]tq|}fROM[\f~|z|wl_\cXdsuzzkp~ttqo{yh_\YL[alxuxlkx|kqpptrl`ctlywsiL711A=Qhlyywjjobpuxwt{zyoo}qwtfUJC<3EG\rvzstvtsxuxylumZTVcZnz|yoz|zrhedVFFK@RWbwyw|~znpv~}sf\TLCSQbuwxker}rllxy{uvyhmws}t[E;;IBVfm|{|jhl`mnt{nnxohYUcZa_TI?4,$79Khr}zw{xpo|wq\OKZR^oqxzw}qnpl[PWMW_c|wsxxnbspw}umi^THTY]wss}ompxsqhafrqaTa[iqmk^K=4CFKem~~yjmdfoj|{t|xqs~xn[hfiqi`]TH8BMMiw}|~~~|sjn}wivrpaS]^\op|wz|vxwszlwwlikoeVWPTa_u·lwvz}tsl^KQZVluslpnkmsu~~vkm{ziqit}zn^QAHJK_bs|~~}nl`cjct|}ypv}~xw}p\e^elc[[UG5AHLer{}xu}pcT\UZfesy}x~p|pvgUVT:C>>EKTwu{~^`behzpPEB]i~|^LM=Wgjqustpfob^NTCB[RlvujQE?jsng{~yo?>Pdu{NQp}r`g\ReXmvzn^dJO`xɫnD\ӽyGHcvsvgWEJD\m]_a\\RBELe|ue[RV]ssxt\WWFNK=Odlrtulrtzq}wbUianucWPYXlx}y}z{~cjmgy~YO9=IXq}~a:%SڳdU>@9Ckuojkqjv{_cdd|{{tl]XVbduyweoi\MONGYSQhrǾ»~m_lqiNJN@^t|yxlVSPMGYeIILOghVFOdw{|}dd^^}a<.5/Yмj[b^`cYZUKGXp`chbndL49GMciik_V\dqvqoturVYWSVQPWep~yuvzpcsysyhz{|l]JCI\SXgeyzvn|jgpbpx̹˾ȳ{kraK0)/iǺfSS]c\K5703QUeigggiinksjmt|rpvwskua^`YPOY\dpxogjqy{|sssv}zbNC;_rzzu}x{{vdbag`j`[s¼Ϸοé{rYEM[C/%0Yѿ̵s_PR:30<87>Bd~a]hlup^YZRbgvvtt|~{itn{}trkcKTQZhxzu{v~vvvp[eR\\@1)H@=,(?bҿpra^[Zb\]btqkWKPPfe}u}|}}y{|qwzf\CPegW[Yxnm^SP=A:HOMRgvolXbczphrvxdqko}~}tnsp~tfm}|}bRg\rrWH;QBC46Tm}~xwbcdi}xqvyufgoxsrsvr{wqys\oldX?MXr[benmoVNCAOMdhaexqic\c]qj~zbkjiTIVM_s{w|iz{k}nz{yuRX[I\D9%-@>F/=Yxϼ{thuis{oo|kl^nq|wwyl{~nanacDD_u}fl\xomjQL=G8>PHG^kbfXYQer{{g^jkk\ohlxxjqnyphjqurjz{|^PdPgfYL3=IH;6Uk__hw̷Ⱦ~y|str|ts{szwyhV[geYI\rf`dr{wvh\j^bnrkbpoyrYZXd{si`VRabZa^nxzugu{rkpoklxvms}]bqmljlZPE5:JKB/?\WSXhwɻȲtvu~~ulfsxpwuzncPSdd\ERpkdbluzp\kdepvq`fdliORS^w~nc_ZTad[_[iv{~o{qjponly|{j_dqtX]jfgdl_SH59IJF3Eda][jvɺʵwyvzsz{v~ylX[hgaKYqhaZgq~{vdqhhovsbimyuZ][bysig]Q_d^`[irt}yoaox{}uospniw~ujmw}_]hfkgncQH68JLK5Zjje`wxǯuwu~{piqx}tr}srsvvx_RDFS[bLTorrfs~zzwtr~uml{y^ZZ^kzsdieT\dnmckwpy|}}yvnrel|~ygc_qaQVS`^`iSB82@IVM@\mqnmĬrtu}y~nmw{z~x}vprmtyss]RHOYccM]twvjz}yxvuttjm~uabaiuqfmcZbhrkcp{kzyx|xxqopbux~pf`hv_YXYe]hfNA66CMXGKgorhp}Ž|vtxz{kps~qz|||zmnkytz~eRFBMVcTNirukn}~~{yt}|qjz~g^\aiwvfijZ]aordjvquz|}}yptej}~zjd_pcTUP_^bjTA74?JXND\jqllĭ~usx|{noty{xzzu~ooivuvkVKGPXf\Ocpuokzzzzu}uiwlcbgoz}llo^acpudgrrq}ty}||xnsfj}}ykeasj\[UcbemYD:7?HWNDZgngetv®zv{}qrrqt~||xpmgwvylUHDKSaWK^kphdsux~xjyoe_djtxijo___ltden|ro~x~~~rvig~|oh`oiYVO]`bl\D75=EVQCSbjfbrvƵ~ztx}trr}zw|v}srnfqwurYJGMTcaPZipmdqs|~zynqvicfot~tktgabhylbjt}kyvyx|}sqnaw|~sl`hqc^UZfblhP=8CUWLRanpiswyv{vuuvyu~wszqf`opvzjPCFIQaZS]juqovz{}krslbekltlhpgd`h{oglypyv|ux}sql`q~zyrl^cjaYKO\]feO95;>P[RT`nvov{zzr|||wvr|~tvunywoc[jnsxkPAEIQeb[bnzxu{~yyko~rk`dkktolskgbj~sko{szv{}orzqng\nzywqj\afaYKR^`igQ:7>BT^VWbrzt{~}yt~}|zwu}~qztpxvo`]kmtvgLBHITd^Zan{vw{x}hu{qk`ijkpikogf`p~nkn|txu~zqv~oocauzxtpe[dd`SGT[ag_F59>EYZV\fwwv|~tw}{}}zs|}yq{msurgXbkpwqYCCIL`d^ahx{w{}|xol|tocdkioolsnjch}wlmxuxu{oozsog]lxyvrl]`cbYJQ\bifR:8?BU]XY`pxuz|zw}~}vw~~{q~rsvvn\akpwt`FCIJ[b\\`owswxzul{vrhemimnjqmjbdzvjiruusyrq}wqi`my{wsn_ade\KPZ`gdQ87>?QYUW[lsquwzy|~vx}||spstvm[`jqwt`FCII[a^^`ovsuu~~{vm{xtjfnjmpouroffzxljrvurxqmywqj`iv{xtrbbdf`NQ]dljZ?:AAQ\YYYgrpst}}zzw|~szwuvyubalszykOEMKYd`_\gsqsqyx|nx{wqhonlposspjbr|pjj}ysprzxlttoces{}vwjcegiXPZakldL:ACJ[Z[Y_ppsqw{~z~}w|xtstuzn^fmx|xcKKOPbedbbrvvut{x~{wnyxxmjqlmqsxvukj||qlrxsovzpivyqiahs{yvvgeejhTT^hqqeK@HHT``a]fttvs{{|~y~z~tt~vww}oaiq|~ydMOSUehhdcrvwuuzx~|wpxwxmlqmmqtxvujj|}slrytovxmfpįzhkrubU8*.,5BKK@J_YWOVOOkorǟwbgk}pPHE=AITZbdYaqgWKRZ\echpqpb_ixfgpMm{}hiv|m`LUo|kNcyhdclhcoz}{~mcfδu5&YĒI(+Hh|skecMAKVfjiWGLOVejpnsĶ~rafo}ub]\UU[chkmcj~~~}xugVg~lfoy}odWFEI\aYgxzuxw|lnplidnqrgh~o^chpqrxb? GƵs\SOJUSW]fp`X^gsur^OW\gx}xnaUcgemkmo{c[Xal}~ub\beswl[^xuw|h[RWH7?FV\[N:;BFW]cc_q~srryô~~}÷yiQC;-%5i¶{fXQA?=JOBHUgstmXQYYellh[]c`ZMMDJ`fouuvwg[VY]hc[ckwyy|tjqrv|r`Uh{ops}zu{jeju~wbV^dq}pmptǽeVG::3+-Qt\M@?E3,3@OQL9+13;FIMCAMORHJHKcow~|x}qfecjeVUXad_efdfr~wz|||w{sbR]|wspuxqvoljqsnvpvy}njmq|ƼŷsWLB8;6-)Cso_Z]I=?GRPJ8'+-6@EKBBOUZRUSUmxy{yxu{tgcckkfiiegt|z{q^MXu~~x|zpoc]V^j^\esvhnqz}yzlgjmv|mSJC;>:0(9c{uzgYX^ig`O<>?EOSZSN[bh`a_^r|yytry}wyxutxyqaMNi}xuh]TR_WMS]pushXY]dou}woy{upivylegkr{vlVGE<>?8-/Qtmjlx|wlWNQR\`hh\bltrkleoy{~uqpfUHWt{uf\OU[LIN\ijeVKOR\cjoedmtvljccsz~zrfciox~{zzwmcKC@;A@91EHGNo}~ukkmuwtu{zvmr~{~fr^T`N\YlvlaeqwY\_armfTXK]h_oaScde[^W_n^q`bldcizxvjmrzkm_tx}wwmmt|xqkstl`[9=>BGRLRhw£y|ppwy{{yxzbdp|zwx}xU_\Oc[Ycn}oniqa\qkqoYGNY^oee_OblrgaMOi`pmbmbY]xv}zt||fpqq{qc`oxrzslcyg]_7@AYZpʻ~}~~~qoqht~~pur~{[\^RORX[htslznhcy}wbW\i_^cfYe^hc[IM`]dcejsgfo{vfvtpcm|oefz~r{xuzla[pi^RJ:HDDHWXK`uu|{{mlkfuyqnsnXLLRW\\f~w~wepvulaYPZegdU[XxpcY]ZR]efjo_el|mwyrp|{h_ox|skc`kjqm}tjhm_\SP@C:AHOHFZk}̺ur}}~smiouzwzpv}s~lRQLRLX_`{|zuphyszze\`hXg\[Ydmja[NPYdd]tdqekvouq|yk^jsqkhezzxiopx|rhbXbWY>C=@M@INae}¾rxuvgcun}}z{{vuo]W\O[MTZgynplyzxvo\eZ`dgTWfirVQJ^ZkfYiggmu{}~syvlg]gquqqdhvv{wm`unl\U\fX]<69;GQMWYoƸp|q~mZin{~}}||qq|}|\WNTY`YVjmzm{o|}ryV]Xfho_KYghhWJTUilkZj_j}txyuhclc~ksdZjvzx\`iqojQUY\YT9/;7XWeds}ɻyr~rvnXmi{õtiovj}`]MPO[`Wmz|rtoyuun]\cmoi[IiXcYQTQ_ai[ejj~t~jomviio[nlgmebw|rrYdi_iSZSUKO8.;CSkjkr{unjWgr}y}aitptpWQX\[hhk~stvei_g^i`eQKf_]bVN[gfdp_rl}|}{vwlpynjrldkVa`ottkkeWjhhZJSQZWMD4FMS``wz|¯ruirf^r|~~y|zpmjnp]STPa]foi~py|~xo``[jvhe]`hRTLCR_migrmrx{jmhrwxsfhjzuad[gqivg^kfk^XWUZW\N=H;GMSogg|Ǻryv{`]dl{ywjityf`UIVN^d]u~p|o`cmojqa\cZ`PFYSYbevzqpz~~vzprtzpan|vlhZbahtijtfc_dh]S\[MML?@=N]R\ltĽ{xw{cefdr}zjn}qlgXYHJWQ]poq|hovehkjlXaeTZ\RMTkvvrwoe{ywu~ukvymskffXfjg{wjb_mqg[]VLUOLF7IIBW]fryuzmrfeis{~{{v{xnkhefUIMGVhbci}ukdboui^_]ffQMN\uoq~pbft~pvuliktn`^^au|mgflthhhQLNQXRKE>HGWgt{wŽ}ps{orpsqbvyxw}|qu~{u|t\`kol`YNRWKWbu}uhioopomeehY\Y``Tdphlfnp{ov|yucintuld`de[kqzufig]ZINT[^WQF>ABWcssluqo{xwi_k|}w|vrqpz~zrdeenicbaZMFT^txw~zzrylhqsp_Va_h]WYZdcexv}|}~xvmhtovllqjg[R`h}tlla]US[P[YXXPO@>TZljp¿´rk}y|icjiuqty~}ylm}|yhiuil`aiegQER[moxx~lhqrwcV_dk`[WOWWgv}wz{tblwxxfgtrmVT[cqrqkb]NZa_[PVYQUHHNW]`p}°~yyu{ummiifbzt}mut{x~qcrr}gXghidVTL]airt|u{mvnrnfeZhde^RUGSko}y~wihsumipkug[[WcconeiTY^apUQWNWOVVIQQ^jwpz{utotpmjUiw{uztpoo|{iuo|yafddhYaXSW^cdtto~urkfogecgaZ[JL[e}wzsv}yutuoqxklhidZ]_baim[dabl__WIRPY\TIGSWgtk{zsotqxkZhg{|zz}s_onw~ozpdkhf`ZdYfcQ\Ldfpx~~~{egcbkhZ]aK[f[ygs~msl|xvt|~g`gcche_hda[^dsdTfVJ^Mf^RREED`YptǼ̮xzxr{svgriwi~znqbc}kxzwhvjmZ`^bdcnMSSBktesoi{uj[``\dW{j\x[gwtut{vuoc[kthXUN]cewrxdZi[\N`lQZFCGINC_`uqz}r|nkbrduz~~yzyq}tuyun{jyurL\]a]i\FWJUlf}pvvfz{rrR[Yda`pQZfdyvvtm~|h~quzqrjpul^]]Mf|o\UeY]UbPISTbVPL:Zdlvoynyq|cxuiqnx|{ty}wxWjv{qmkpoud\]etdaNNSTsisyoqyllfhynbc_r^cX^bxspwujp}yqrpjm^`_gK\`[shn{fYWduWNLYJV_DPUNXbi{qľu{zrxjyqmxxq}}~~|nuusitw~pvsende_`j`YXURMSkdu{r_jldr[mu`X^PVWntw{uq||zlngcoijrdUihci[qeea]gaKFQKGTaTjV[Z_xrs~v{srwvf~xupy{~}}hpcysrnmkg^[lep_hbgjO\[]u{ozzfckh]qgtwaqadVW]bkyzslt~}ksjbUmjij]ZSVj`Ylat]WW_RAYT[d\ZUPph{|{´÷~{kn{uuvmohy}zxuqwudnli|}u{nY`dm^Zc_cl^pWY\ex}|q|_mdjwcbm[e^aaQVbpsznepodWddlzdhNc^Yc`kdTSXZRN`PZKcgYh_`ttȽ~~pzrkjyivnm}om{j^uqp}uzmdkh`Kl^mm^loffbRhxézpwfsYfncn_c``Rb``n~pprk`uYcihq_TeUcgk[Q`XlQVZ[\^\YX^fdmm~{xxƫv|syw}jgclzzx{zuxitYiq{jjlwefb\_eh~ritaZcgj~q{qtuofe`_iyrZ\Z`Zhm|{nio}om[Uy^rbQUalkaN]^Tht\dY_c^[i^`sjri|w|y}{zysafzl}fk{sz|v_^ouv{xodomi[kkvpyf__anXq~~xv~jqqiZnhpg_aYS[zj|{`nortmiT^ohli\kcaclT[ehpd^[^St^\iagg`tv|vvevrjpn~wwnvmpiertyoloful_qgu|ngrreZle{p~mQ18/1FBFMUYiZVZfs{vq[?TXa\xq]OTZWdkwvz]^UJ\^giffxy|v\H@L8HROljqe\RYǸrqnmyo~uWrqMNZ_lq}qaZdCI]U_dewƬþ¨whMNfqjXa[]{z{}iI:1.2QV>FHQ_TGAUopxkgzhg]B*6uʣ~iSK`hmpZNLHHXp[T][pn]EDT_vytth^chlmmw|~xpd_[CIOEEGMYjkh`hsuo^bmTXY`{oeZRUnwagis}pd{yw{wujz}ûʮͫc??K9$4H|Ь}[QYfiaN65'$-D?0:;OPA*#9Gcloqnlkwy|rxztknRLSRKGMKTi{tsx}uleiuqc{oXOW]j~um|vtgn}|zp~|hmtmwu~ɼƢwvqzoK+'A71+?WԳnjl{p]>;'$+=>047MPE/+>Jiq|yo}wz~{w}`\_e]V^SZm{qs}t}w`vxdUKYfhz}b[iyuoj\fqywx}hwz~~|p`nurvr|zv|˼ȴi`dZk[=#,E::3IcЯ{lTR:/Ieȵ}pb[a]pmbh}ug]cctur~~tzv{qTfhaT=Pb]_^lotVK=;CBZZPXqlfZSVWts`psv^^jdw~sutlevmxfq{w|kGYPRfMB(=CC;'C]̽Öwgcfaxxkq{ogihrsqyw~{{~tV_gbX7H\umhfkqw_K?I`WLbqdeVTLZnt{i\mojScbd|}n{yklonjdnwoyugLaLU\ME+SeXQkpefWTK`m|xy]\jmaTf`l|m~zislnfimxpypbM^EPRLC+:DU?5VgʽͿugsjjs||rimlzjt{y}v}yfujjPGTacBOfsysj|{\FEPAUh]Rhpei[TL\n{uv]YfkaSfdoo|}kpqlegmqqxyk|z{gJZEDLGF,3CZP>\pпŨ{itrizwtli~ls}no~{}~sy_gdeVAM^nSTmv}wg|fKFUFRjcUbrglaTMVjyur^T`idUfgqpypovlegmmtwuisvvjKTC:BDF//B_[KexƮkwulxuli{jpyos~~ssZ]^cTAKcrY^r{wh|iKKXHYlfWdofn`SNWj|qnYT^iaWhgyo{ousibkln{wurcu|qtcLR<8>FC/2Kh[Vo~μӽį{oru~~qkmwkr}}n{~ttfTY_aMBRplZjwqo`IXVMgmcYljkmXQP^ryneTWbhY`insr{olcdmirzzwntid|}pnoTOG37AH;.<_gVdvǵľp|}qzwmlv~onxru~~qlUT\cVCIhr]cq{zn}nNV\NbmiYdkhq\OPWn|ngXU]j^]fgvor}qfdmjmv}ymo~m]tzolo]OK63?IA05XgV^n}ʷƴvw}zmntrmuuu{~slVR]dZEGho]akwssR\_Tgnm[djjrZPSXq{mfZU]i]^cg~ppvrfhnilv~yjm{i]sumko_PI52AIC05XbUZfvƷ̹|{{oqtuntwvy|unYO]d^KEdp`_ds|vZ]eXgora`jjt_PUUk}mg^UYha^abx|rl}z~wignljq~lktqZkrmklhTM;/>IG9/ObXW]p{ʹĬotr{rp}wzx}pbOUdbXBVpha_mv~zo[i`amso]fhqnRTT]wshc[Sag^a^l{}{is}~zqgllkjwufkv~`\ljjglaOF31EHG39]a[Zewмyquuznt|{y|ujYJXbaTC`ridbtx||jalagnvm^fiuiPWTa{nfdYRcfab_r{wiw{}y~oilkjhy~pehtyZZfdgbi^KB01EHI5?bec_l}ʿϻvrut{lt~}zxseWGU^`WEauokfy|}{rdmfimxr_ditkQXU_wpff\Qbigfbt~}kv{yz~rkmklfu}tdgly^U`_e`ecLC3.BGN?>bnmin}oup}}nm~}~vz~|vbWEK[]_IYwvuk{y~gnjimwyedhtqVWW[q~ref`R_jkkfsnv}x|{unnkmeq}wefhxaS][c_beMB4-@HRF@dstqs»¨}nso{}nm|~twy{ys^RCJX^`J]yyyo{~kpllnzygehvpVYW^r~qeh`Ubkpmhwny|x{{}sonkmct|sedjw\U[Zd]ebJ>10CJUDHluyrzƿʽvopr|}{jr}|~pws|zzhWJAPXbXLjz{vrwpqmnrshenzeWZYgx{jfjZ[gnskm~xowx}yqqjmgf}~ifbslUZW_a^iVC8-;FRQA\swvp½ǯ~pqp{zqmy}{{qspv{uqZNAJU__K^wyxnttpppzmfk{nZ[Zds}ofmaZdktnixm}y|z{{ttmmjbz{mganrXYV[c]i]F:/8DPTASmrtl~ɶ~rqpzzsmv~~yyzrrnt{urZMBIS^_K[rutjz~ywtrs|pgm}o]]]gt}ogn`\dlukgu|m~x{||wwnojc}|nhaoqZZV]d_k^F:09DPR@Qhnogx~ɸtrq{{unv|~w}wytrmq{uw]NCHQ\aLUmqsgtzzwurujj}ua^]fp}shof\chvoepzlyzyy}yzrnoau|tkciu`ZWXe_jfM<36ALVDHbjofo{~ztqy{}otwv~w|z}qojyuz~gRFCMUbVLcntlkyzyytz|rgt~ja]bkw{lkp`adpxiitvo}vyzzzymqge}}nhapm\ZS^cal]E72;CSRCSfnngw{Ǹwss|{xqu{w~tw}vpijxr|y`LCEMYcRRhqvkq{~yys~|qgx{ha]dkwxjmn`cdtxilusr|uwxyzxlpcf~}zmf`pj[XP^`bk[B53F[]U_o}{{}yprz|z{xv}~zn|ou{qj]ammvr[E@GL]f]`m{zyyrnextha\ghpqhpmggh}}oszuxxwppu|rji[hzxxqk^_haZMM[\ffS;4K[WV]jxtx|z|r{|~~}zurvwpxvrc[imtwjO@EHQb^[`jxvwy~x}~hrypk`gjjplorkibqqmo|uvt}xmr~}onaaszzurg^eecUJW]ei`F5:>EXXVYcstuy~uy~|}v~xqquvul[bkqwr\DBHJ\a\]brxuxy~xsjxtpfckhlolsolff{{nmtwvswqmwvoi^gvzxrpa`dd`NNZ_hgX=5=>M[WX[ivtxy|u}||}v~ssypttteYdksvnUAEHM``__evxxy||zolxrpdekhlnnsonei~zpnwxvrx~nkwtnf]huyvro`abe_MOZaihX>8@AQ][]^lyx||{~u}||x{~qrzpssvgZdkuxqYDHLPceddgx{||}~{wqiurqfdkhjnotrqii~tpvzvqv{ngr~unf\cpwwrrcabedQNYakkaG;DFRaadcl|}~|vz}z~z{z~sny~pqqvn\bjtyveLIPQciijix~}zu|uhrrrjdkhhnpvvunj|ysu}wpsx~pen|vmg\amwwrse`aegUP[cmnfO@IKUefigo|}vz|y}|yx|qnx~pqqwn^dku{wfONUUflmmk{~|xu{}riqqsjekhhnsywxnm{txvmfkmu~vc_n}wskdjrysli[[^jo_bjrywmWOXVbjkrq}x}{{wzmlotyksutnuwru}}xsbihkww{z{yyxzpqrvvinw|~ruvru{|wjs~w{ojlitsbR\|qeVV_infibZ_gvofot~zjX]`aprxx{}}|~|~vxr{~tho~okqzxytkot~s{~tacgftv|~}{}xyvxy~ss|y{pcbZ`f`X]u}ykfhmrhgbXZ^mj_egpsnbORWWeioss{tws||}rrksytknyvy~yzulos}qvw|p_bfgtw}~y{w~}|}vw}{}||ka`XagaY]u|zomotynoj`besmbffopk`PTXYgjqsr~~xpso~|{stlrytlk|xz~||zqrtrut|{rbdiiuy~~wyu}zvz~|{}qccZ^gd\Zm}{|rnps{srrgggrsdfdjpleVU\[glqvrz}|ynpmu{~vvomxwpjwy{||wuu|ussu~{wkdlksz|xvuv~xyy|}w{icaYcfb[ax|}zoqqw{swpkjlwmefdnokcX^_cnowvt{y|smomz~}}uvlpxuom~z{}wwvrspv}zvkjpnx|uvsxyyy|}v~xfd_Ydfc^g{}zprryzuzrnmowmgfeoome]dchqrywvzx|rmnmy}|tukovuqoz{}yxv~stpu}{xnmrpz}vvswzyw{~t|{}yhc`Ybgfbh|}rrqv{u{vponvphhdnqokbfgissy{wzx|vnomu}~|~ttlkuwur}z{z|zw{utqr}|{tpury~xutu{zv~{~~rxx{}zxibaYahigj|srru{v{xrpnvqiiensqnfjjkttz|xzx|wnomt{|}sskkuxwu{{z|{x|vuqs}}{uswt{xvtu~~|yv{}}{rxvzzxufc_Zdikio~stsx{w~xtqqxqmjhrtrnimkovv}|yyx}toonvzzzrripwywx}|}|{x}vvpy~|ztvvv~~vvsx}}}{wy|~~}~ttvuzwxncd\_hklkxyuvv|y}~vurwwppinvtrmnnmvvz~zzw|zppmrw|||stlluwxv~~}~}z}xxru~|{vvwu}~~wvsv{~}~|xv~}~~~vswtyxxqee^^gjlku}wxw}{}yxuwysslnvutoopmuvz|{w{{qpnqvz|{tulltvwu{zzsv~|{wxxu}~}vusvz~}~|xw}}~~~~~uuwuyxxqfg^_gikju~}yzy~|{zvyzuumpwvtpqqnvwz|{vz{qpnquy~|{uumltvwu}}{|tu}|{xxyu}~|wvsux{}~|yu~|~}~}}wswtxxxthha^fikjq|}z{z~~~}zz|wxqowwvsssovxy~|wy}tqoosu~zyvqkquwvy~~~zt{}|zx{vy~~||vtsvw}||vz{}}|}|svutxwwnhg^bhjlmx|}{{|}~~z}|yxptywvsvsrzy}~{v|zrqoqsy||{vvnlsuxw~~xu}}|yz{v}}}yvtsvw}}|{u|{~}~|z|yrvstwvulhf^dilnp{}}}|}~~|~|{yqvyxwuxst{z~|w}zsrorry{||vvnmsvyyyv}}|z{|v}}}zvtsuv~{|{zuz~z~}}{yzxqusrvuunig`djmpr|~}}~}}~||suzyxvzvt|{~~x|}uspqrv|}z~~wvplsvz{|v|}}{|~x|{|vtstt|}z{zzuw}y~}|}}zxxwpsrptttniiaekosu}~}~|~~}~uv{zyw{xu}|x}~usqrru|~}z}|~vvpmtw{}|v|}|{|~x}~|{vtstt}}~zzyytx|y~~}|~{yvwuptqqtssmkhbhmqtx~~~}~~ux{zyy}wx}~x|usqrrx{~{}||{vunpvy|yy}}|{|y{xvsssv|~zzyywu|z{}}{~ywuvqrsorssqkledkosv}}yw|{{z}{w~z|wurrrt~|}~{}}|~}wwpntw{}}x}}|{~~x|}yvssst}|||zyyxtzzz~}|~ywuvrquprssrlmgdjnru|}|x|||z}}w~{|yusrss}||~{}}|~}wwpmsvz}}y}}}|~y|}yvssrt|{{{zyywu{zz~}|~~ywuuqruqsttqmmfdjmqu{~~}y}}|{~~x||yvtsst||{~|}}|~}xxqmsuy|z}}}|y}|zwtssr{{z}zyyxuzzy~~|}ywuurqvqrttsnnhdilpty}~z}}}|~z}~{{wussrz|z|~|~|~~yxumqtw{{}~}|}{{{}wussqv|x|zyxyvw|x|~}}}{xutspttqsttpnmefknrv}~~~|~~}}~{|~ywttsu|z|~~}{}|}~~|xxrnsuy|{~~}|z~~{{wusrqx{x}~yyxxux{x}~}}}|ywtsrousqsssoomehmpty~~}~~~~||ywutsv}z|~}}~}z|{|}}{xyrnsvz}|~~}}{~{{wusrqwzw|~yxwwuwzw{~}}||{}yvtrqottqssspooghmquz~}~~~|~~{xvutt|{{~~~|}|zz|{|||xyuosvz~}}~}}~|}{|wusrquzw{ywvwuvzwz}}|||y|yusrqnstprssqppiiorv{~}~~|~{yvutu|{{~~~}}~|{xz{{||{xyuptw{}}~}}~|}{{wusrqvzw|}xwvvtwyv{}}||~zy{wtrqontsqrsspqpilptx}~~zxvutw}z}~}~}~}{zw|~z{||zxyrqvy}}~}}}{}}~yvtsqrxxx}zwvvuuyww|}|{}|xzyusqpmquqrssrprljorv{~}zwvuu{{{~}}}}{zwy{{||{xzuptw{~~}}{~~|zwusrqwxv|~|wvvvuyyw|}}|}}xyzusqqnpusrssrprnimquy~~zxwvu{{z~~~}}{zwz|{||zxzuptwz~~~~}|~|zwusrqwwv{}~{xvvvvzxx|~}|~|wyyusqqnqvsrssrprmimpty~{ywvv{{z~~~~}{zxy|{||zxyupsvy}~}||{wusrqvwuy|~|xwvvuyyw|~}}}}xxyvtrqnovtrsssproilpsx}|zxwuy|y}~~~}{zxx~~{||{xyxqrux|~~~~}}~}xvtsqswuw{}~ywvvuw{xy}~}}~zwywtrqpnsvssssrqrljnquz~{yxvw{zz~~~~~|zzwy}{|{zxyvpsvy}~~}~}|xvtrquwtx{}~~xwvvuxzwz}}}}}yvxvsrqontvssssqrrllpsw|~{yxww|z{~~~}{zyvy~|{{{zxzvqtwz~~~}~||xvtrqtwtw{}~~xvuuuwzwy}}}|}yuxvsqpomsvssttrrsnlpsx||zyww{{z~}}~}{yxvw}}{{{zxzxrtwz~~}~~}|~}xvtsqswtv{}~yvuutvzwx|}|||yuwusqpomrvttttsrtomqty}|zyxx||{~}}~~~|zyxvw}}{{{zxzxsux{~}~}}|~|xvtsqtvtw{}}xvtttwzwx|||||wtvtrppnmsvttttsstoosvz~|zywy}{|~}}}}~~{yxwuy~|{{{yyzvsvy}~}}|~}~zwusrrvuty{}zvuttuyxwz||||ztuurqpompvutttsrtrnqtx}}{yxx||{~~}}~~}~{zxwuw}}{{{zxzxsux{~}~|~}{wvtrruvtwz|~|wvuuuyywz|}||{utvsqppnouvttttrtrnpsw{~|zyx||z~~}}~~}~{zxxvw}}{{{zxzwstw{~~}~{wvtrruuswz|~{wvuuvzzx{}}}}{uuvsrppnqvvuutsrtrnpsw{~|{yy||z~~}}~~}~|zyxwx~~||{zyzxstwz~~~}~~}xwusruvsvy|~~|xvuuvz{xz}~}}|wuvtrqqopvwvvvustsopsvz}{zy{}z|~}}~}~}{zyxw}}}|{zzzutwy}~~~}zxvtstwtux{}~~ywuuux{yy|~~~}zuvvsrrqptxxwwvuturorux|~}{zz}|{~~}}~~}~|zyyxy~}}{z{ytux{~~~~}}~zxvusuwtvy{}}{xvtrppooqstuwutsqponorux{{|~~}|zyyy{|}|zxvtsstuwz}~{xvuuvz~||}|xvuuuvxy{|}~~~|{zywvvvwy}ywwtrrqrrrtuwyz|}~~}|{yxwvwxy{}~}|zxwwwz|}|{{|}~~}~}{zzz{|~~}|}~}|{{|~~}|yvtrtyzvqnlkklmnoqrssrqonmlllmortwxxwvtrqppqsux|~~~~}|{{}}zxxxyz{}~}|{zz{|~~|{{|~}{ywutrqqqtx{wsqpoppqrsttsrpnmlkkkmoqtvwwvusqpooprux|~~~~~}|{{zz|}{yxxy{|~~|{zzz{}}|{{}~{yvusrqpqrv{zwtrqqqrsttutsrpnmlkklnpsuwwwutrponoqsvz~~~~~~|{{zzz{}}zyyyz|}~|{zzz{}}|{{|~~{yvtsrqpprty}zwusssttuvvvusqomlllmnqsuwxwvtrponoprux|~~~~~||{zzzz||{zz{|~~|{zyz{}}|{{|~~{yvusrqqqrtw||ywvuvvvwwwwutrpnmlmnortvxxwvtrpooopsux{}~~~}|{{zzzz|~~|{{|}~~|{zyz{}}{{{|~}{xvtsrrqqrsvz{yxwwxxxyyxwusqonmmnoqtvxxxvurqonopruwz|~~~~}|{zzzzz{}~||}~~}{zyyz|~}|{z{}~{yvtsrqqqqrux|~|zyyyyzz{zyxvsqonnnoqsvxyyxvtrpoooqsvx{}~~}~}|{zyyyyz|~~}}~~|zzyz{}~|{zz{}~|ywusqppppqsvz~|{zzz{{||{zxvtrpoopqsuwyzyxvtrpooprtwz|~~}~~|zyxxxxyz|~~~~~|{zyz{}~|{zz{}~{xvsqpooopqtx|}{{{{||}}|{zwusqppprsvxyzzywusqooprtwz}~~~~}{yxwvvwxz}~~~~~}{zzzz{}~|{zzz|~~~~{xvutssssrrrstvz~|zxwwxz|~~|zwtqpooqtwz{|}|zwurqpqsvz}{vspoorv{~{yxy|~zvtssuy}zvsrsvz{yyz|zupmkjkmpswz}~|yxwvvvvwxyz{{zzxwvtsrqrstvy{}~~}||}}{z{}~}}}~~}{ywutux||yxxz|~~{ywvvxz}~{zxxz|}|{|}{wrmjhgilpv|~}||}~zvrpnopsw{~{wrolkmptz|z{}~{yxz}|ywvvvwyz|}~}|{zzz{|~}{yxyz{}}{zz{{{||{ywurpnmmnpty|{zz{|~~{xvtsssuxz}~~}zwtqopquz~}}~|zz{}{wtqpopruy}~|}~}yvuuvy}{wutvy|~||}~|xusqpoopqrtvxy{}}{ywwxz|}~~}{ywvvvwyz{{zwusstvy}~~~~~~}{zyxwwxz|}|}~|ywvx{~{wutux|~|zyyz{|~}{xurolkjkmpty{wuuwy|~~{ywuuvwxz{{zyxwwwxz}}|{{|}|yvtrrrsux{~}|~}zxwx{~}yvutvx{}zxusrqqrstuuvvuutuvy}|xvvwy|~|yvtssstvy{}~~}}}}~~}}}~}{xurqrtw|}}}}{zyyz|~~}||{||}~~|zyyyz{}~{wspmllmpsvxzzzz{~{xvuvwz|}{xvsrqqrtvy|~}|||}}zyxyz|}}{yvsrsw{~||||~~|zxwvuvvxz}}zxxxy{}}}{wsolkknrvy{|||}}{zyxxwwwwwwwxxy{|~~}{{|~|ywwxz{|{yvtstw{|zyxxxyz{}~~~~~}{{|~|ywvwxz||zwsolkknquy|~~|{zyyyz{}~~|zxvutuvwz}}{{}{xvvwyz{{zywvvwz}~~~~~~~~|zxvuuvwz}}{z{}~{xvvvxy{{ywtqnlklnqty}~}|{{zyxwwvwxy{}~~}|}~~~|{{}}zwusrstvx{~~|yvuvy|zurqsx~~{{}|xuuwy|}zxurqoopqsvxyzyxvuttw{}xvvx{{xvuvwy{}}{yxx{~||}}zxxz|ztpmmptx|~|}~~|zxxxy{~{wsqruz}{|~}wssuy~}yusstvvvtrnkiijnsy}zwwwz~zuqprw|}zxy|~~}}~|xurqpprtwz~~}~}|{{}}{ywxy{|~~}|||}}~~~|zxusqpopqtvxzzzxusqqtx~~|xvwz}~|yxwwwxy{|~}zyyz~}yustvyzzxvttv{~{xxxz|~}{xvttuwz~|xvvy}~{xwwz|~}zuplihilptx{~~~~|yxxz|}}|zxwy}~~}}~|xvtsrstvx{}~~}|{{|~}zyz~{vttwz~~{yyy{~~}}}~~|zwtrqppqsuxyyxvrollnrx~{{{}}zxwvvvxz|~|yxx{zvttvyz{ywutvy}}{zzz|~|yvtrrsvy|~zyz}}zwvwz}~~|yuqnmnpruwxwwvuuvwz|~~}~{yxz|~|yvuw{}}~~{yxwwxyzzzzyyzz{|}}}{zzz{}}}}yvvwz}~|ywvwy{}}||}~~}|zyxvtrqqrsuwxxvtpnmnqv|~}}~}{zyxxyz{}}||~~zxwxz}~|yuqpquz~}||}}}zxvuuvwy|~~}~~{z||xttuy|~~}yuronnpruwxyxwvtsstux{}|}{xuvx}~}{xwvvwyz|}}}}|{yxwvvwxz|}yxxz}}zwvvvx{}~}}}}~|ywutsstuvwxxxvtqnmmnrv{~~}||~}{zzyyyz{}~}{{}zwvwy|~}{xussvy|~~}|{|~~|ywvuuwy{~}{yyz|}|~|yxxz|}}{yvsrqrsuvxxxwvtrqppprtw{~~}}}zyz}}{{}}|{zzzz{{|}}}}}|{ywvtsstvy|~zxxz}|zxwwxz|}~~~}}}}~~|xurqqsux{||{xsoljjmptwyzz{|~|{zz{}~||~|zz}}|{{yvtrqsvz|yvuuvy}~{xvttux{~~{yxxy{}|ywx{~xsollnqux|~~|yvroljijloty~~|{{}{vstx~~}}|ywvvx{~{xtqnmlmpsw|~zwxz}{wtssvy}~zwuuwz~}zyxxxz{}~}zwsokiijmptwxyxxxz|~~}|}|yxxz~~{yyz}{xwwxyxxwvvx{~}zwuttuwz}}yvvx{~zvtstw{~|yvuwz~}zwtqpopruxz|~~}|zwurpnmlmnprvz~~}~zy{~}{zz{|}~|zzzz|}}zwtrpoopruy}~~~|{{|~}zxvuuuwx{}~}|{zzzzz{|}}zwspnmmnpqrsstvwy{{zywuvy~|{{|}~}zxvttux{~|yurpprv{}zxwwy{}~}|{{zyxvtttvy}~{xusrqrsuwy{||{yurnkjjlpuy}~|{{|~{yxxyz{|}~}{zz{|~}yurqqsvz|~}{yvuuw{}|}}{zywwvuuuuvwxz{}~|zxwwxy{~}zuqnmoruy{{ytplklpuz}~}{yxz}~}||}}~~}{{{|}~{yvutvwyz{zxvvvx|~|zyyyz{{|}}}}}}~~|zxwwwyz|}~{xuuvx{}~|yurqqrtvwvuronnpsw{~~}|||~~}|{z{}~~}{{|~|xusrtwz}~|yvuuvy}~~~|zxwutttuwz}~|{{|~~zxwwy|~~}{xtrqrtvxyyxvtrqqqqstwz~~}}}~~~|zz{}|yusrrtvy|~~}}~~}|{zzz{|~~{xuttvy}|zyz|{xvvwz}}zwtqpopqsuwyz{{{{zyxwvvwxz|}|||~~~~~}{yyyz|}~~}{ywutstuwy|~|zyyz{}}zwuuwy}}{zz{}~~~~}||{z{zyz{|~~~~}{yz~||}~}yvtsuwz}~~}|zyy{}}}~|{zz{|~~}}~{zzzzzzyxusrrsuxz{{xuqnllnrw|{wy|wspnlklnquz~~|{{|}}|}}yvuvx|}zxwwxyz|~|||xvtssux|~zxwwxz}|xvuvy}||||||{{{|~|zz{}}wqlkmt~~yurpnnoqtvwxwurolkjknquy}~|{{zzzzyyz|{wuuvy}~|{{{}{vsqrv}{xvuvwy{}~~~}|{{{{{|}~~zwwx|~{vqmkmqx{uqnmnpsuxzzzxuroljijmqu{|ywuuvvwxy{~|xussuw{}||}{xvx||wutvy}~{wuttvy|{yxxz~}~~~}}~ysollou}~zwuuuvwwwwvtsqponnnoqtx|}{xvuttuuwy|}{zyxyz|~zurqsy{vttvy}}yvttux|}zyxy}~|{zz{}~}ytpmmou}~ywvwxyz{zywusrqpoooqruw{}}zwusqqqsuy}|zyxwwxy{~~~~yutv{|ywxy|}zwuuvxz~~}}}}}~~~~}||}~ytnkjnt}}xvvxz|}}{xurpoopqrtvwxz{|}}}}}|{yxvtsrstw{~|{zyyz{}|xvvx~{yxy{~|zwvvvx{~|zxwwxz~~|{z{|~~|zyz|~|xrnllqx|yxy{~|yuqommnpsvz|~~}{ywvtssrrrsux}}|zzyyz|~|yvvw|}}}~~}|zyyyy{|~~}}||||}~}}}~|yxxy|~}ytolknt}|zz|}~|yvtrqqqstvxz|}}}}|{zyxwutssstvy|~|zyxyz|~|xuuvz~|yvuuvxz}}|zyyxyz}~~~zxwxz|~~}zvqnlmrx|yvtrqqrtwy|}~}|zywvutsrrstux{~}{yxxy{~{wtstx~~}{yxxy{}~}}|{{{{|~~~~~{yxxyz||{yvronosx|yvtsstuwy{|}~~~}|{zxwutsrrrtvy|}{zyyz|}}}~yussvz}zxwvwxz}}|{{{{{{|}~~~~~~|zyyz{||{yvrnllot{~{yvutuvwy{|}}}}}||{zyxwvtssrstwz}~|{zzz{}~}}~~zwtsuy~|zyyyz|}~|{zzzz{|}~}||}~}{xwvvxyz{zyvrpoorw~~zwuttuwy{}~}{zyxwvutssssuw{~~}||||}~}}}~}zvtsuy~}{zyyz{}~}|zyyyyz|}~}|||}~}{xwvwxyzzyvspnmosy}zwvuvwy{}~|{zxwvuuttttuwz}~}{{zz{|~~|{{|}~~|ywuuw{~|zzzz{|~~|zyxxxyz}~}|||}~|yvuttuwxyywurpopty~{xwvvxy{~}|zxwvutttuvxz}~}|||}~}|{{|}~}{xvuux|}{zzz{}~~|{zyxxyz}~}{{{{|~~{yvtsstuwwwvtrqqsv|~|zxwwxz|~~}{ywvutsstuwz}~}|{{{|}}{zyz{}~~}zwuuvy~|zyyy{|}{zyxwxxz|~~}|{{{|}~~{yvtsstuvwwvusqqruy}{zyyz{|~~}{zxwvuttuvwz|~}|{{|}~~|zxyz{}~~{yvuuw{}{zzz|}}{zyxwxyz|~|{zz{|}~}{yvtssttvwwvusqpqsw}|zyyyz|~}{yxvutstuvx{~~}||||}~~|yxxy{}~~~|ywutvy~}{zz{|~}|zyxwwxyz|~~|{zzz{}~}zxutsstuvwwvurpoorv{~|{{{|}~}|zxwvuuuuvxz}~}}||}}~{zyyz{}~~}{yvutvy~~|{zz{}~|{yxwwwxy{}}{zzz{|~}zxusrrsuvxxwvsqoopty~}|{{|}~|zxwvuuuvwxz|}|||}~~{zyyz{}~~}|yvtstw{}|{{|}}{zxwwvwxy{~}|{zzz{}~~{yvtrrrsuvwwvtrpoprw|}|{|}~~|zxwvuuvvxz|~~}|||}~~|zxxxy{}~~}{xvttvy}~|{{{|~~|{yxwwvwxz|~|{zzz{|}~~|ywtrqrstvwxwvtqpoqtx}~}|}}~|zxwvuuvwxz}~}|||}~~{yxxxy{|}}|zwuttvz}{{{|~}|zywwvvwxz|}{zyyyz{}~}{yvsrqpqsuvwwvtrqqruz~}}}~~|zywvuuvwy{}~}|||}~}zxwvwxz{}}|{xvuuwz}|{{|}~}{zxwvvvwyz}}{zyyyz{|}~~}{xvsqppqrtvwwvtsrrsv{~}}~}{yxwvvwxy{~~}|||}~}zxvvvwy{|||zywvvx{~|{{{|~}{yxwvvvwxz}~|zyxxxyz|}~~|zwtrpopqrtvwwvtsrrtx|~}~~~|zywwvwwy{}~}||}}~~{ywvvvxy{||{zxwvwy}}|{{|}~|zywvvvvxy{}}{yxxxxy{|}~~~}{xusqoopqsuwwwvtsrsuy~~~~~|zyxwwwxz|~}|||}~~|ywvuvwyz|||zywvvx{~|{{|}~}{yxvvuvwxz|}{zyxxxyz|}~~~}{xvsqpopqsuvwwvtsrrtx|~}{yxwwwxy{}~}||}~~|ywvuvwy{|||zxvuuwz~}|||}~}{yxvvuuvwy{}}{zyxxyz{|}~~~}{xvsqpopqsuvwwutrqrtw{}{zxwwwxy{}~}}}}~|ywvuvwy{|}|zxvuuvy}~|||}~}{yxvvuuvwxz}}{zyxxyz{|}~~~|{xvsqpopqsuvwwutrqqruz~~|{yxwwxy{}~}}~|zwvvvwy{|||zxvutux{}||}~~|zxwvuuuvxy{~~|zyxxxyz{}~~~}{yvtqpooqrtvwwvusqqrtx|~|zyxxxyz|~~}}}~}{xvuuvxz{||{zxvttvy}~}||}~}{yxvuuuvwxz|~}{zyxxxyz|}}~}|zxusqoopqsuvwwvtsqqsuy~}|zyxxxy{|~~}}}~|zxvuuvxy{||{zxvuuwz~~}||}~}{ywvuuuuwxz|~|zyxxxxyz{|}}}{ywtrpoooqsuvwwvusrrsvz~|zyyxyz{}~}}}~|zwuuuuwy{|||zxwvvxz~}|||}~}{yxvuuuuvxz|~~|zyxwwwxy{|}}}{zwurponoprtvwxwvtsstw{~|{zyyyz|}~}}}~~|ywuttuwy{|||zywvwx{}|||}~~}{ywvuuuvwxz}~|zxwwwwxy{||||{yvtqpnnopsuvxxwvuttux|~|{zyyz{|~~}}}}~}{xvtttvwy{||{zxwvwy|~}|||}~~|zxwvuuuvwy{}~|zyxwwwxyz{|||{zwurponnprtvwxxwuttuwz~}|zzyzz|}~}}}~~|ywuttuwyz|||{ywvwx{~}|||}~~}{ywvuuuuwxz|~}{yxwwwxyz{|||{zxusqonnoqsvwxxwvtstux|~|{zzzz{}~}}}~~|ywuttuwyz|||{ywvvwz}~|||}~}{ywvuttuvxz|~}{yxwwwxyz{|}}|zxusqonoprtvwxxvusssux{~}{zzzz{}~~~~|ywuttuwy{|||zxwuuwy|~}||}~}{ywvuttuvwy{}}{zxxwxxyz||}}|zxvsqooopqtvwxwvusrrtvz~}|{zzz{|~~~~|zwuutuwyz|||{ywvuvx{~}}}~~|zxwutttuvxz|~~|zyxwwxyz{|}}|{yvtrpoooqsuwxxwvtsrsux|~}{zzz{|}~~~~{yvutuvxy{||{zxvuuwy}}|}}~}{yxvutttuwxz|}{yxwwwxyz{|}}|zxusqonopqtvwxxwutsstvz~~}{zzz{|~~~~}zxvttuvxz{||{zxwvvwz}~}||}~}{ywvuttuvwy{}~|{yxwwwxyz{|||{zxusponnpqtvwxxwvuttuwz~}|{z{{}~~~~~}zxvtttuwy{|||{ywvvxz}}|||}~}{yxvuttuuwy{}~|{yxwvwwxz{|||{zxusqonnoqsuwxyxwvutux{}|{{{|}~~~~|zwutttuwy{|}|{yxwwx{~~}|{|}~~}{yxvuttuvwy{}~|zxwvvwwxz{|||{ywurponnoqtvxyyxwvuuvx|}|{{{|}~}~~~{yvutttvxz||}|zyxwxy|~||{|}~~|zywvuttuvxy|~}{yxwvvwxyz{||{zxvsqonnopsuwxyyxwuuuwz~~}|{{|}~~~~~|zwutttuwy{|}|{yxwwx{~}|||}~~}{yxvuttuvwy{}}{zxwvvwxyz{|||zyvtrpnnopruwxyyxwuuuvy|}|{{|}~~~~}zxvtttuwy{|}|{yxwvwz}}|||}~}{yxvutttuwxz|~}{zxwwwwxy{||||{yvtrponopsuwxyxwvuttux|}|{{||~~~}zxvtttvxz{|}|{ywvvwy|~}||}~}|zxvutttuvxz|~}|zyxwwxxz{||}|{ywtrponopruwxyxwvtssuwz~~||{{|}~{xvuttvwy{|}|{ywvvvx{~}||}~~|{ywuttttvwy{}~|zyxwwwxyz||}|{zxusqonoprtvxxyxvutstvy|}||{|}~|zwuttuvxz||}|zywvvwy|~}||}~~|zxvutttuvxy{~}{zxwwwxxz{|}}|{ywtrponopruwxyyxvuttuwz~}||||}~|ywuttuvxz|}}|zywvvxz}~|||}~}|zxvutttuvxz|~}{zxwwwwxy{||||{yvtrponopruwxyyxwvutuwz~}||||}~~~|ywutttvxz|}}|{yxwwxz}~|{{|}~~|zxwutttuvxy{~}{zxwvvwxyz{|||{ywtrponoprtwxyzyxvuuvx{~~}||}~~~~~{yvutttvxz|}}}{zxwwx{~~|{{{|}}|zxwutttuvxz|~}{yxwvvwxyz{||{zxvtqpnnopsuwyzzyxwvvwy|~}||}~~~~}zxvtttuwy{|}}|{yxwxy|}|{{|}~~}{yxvuttuuwy{}~|zxwvvwwxz{|||{ywusponnpqtvxyzzywvvvwz}~}||}}~~~|ywutttvxz|}}}{zxwwxz}~|{{{|}~}|zxwutttuvxz|~~|zyxwvwwxz{|||{zxusqonnoqsvxyzyywvuuwy|}}||}~~~|ywuttuvxz|}}}{zxwwxz}~}|{|}~~|zxwutttuvxy{~~|{yxwwwxyz{|||{zxvsqooopqtvxyyyxwuuuvx|}}||}~|ywuttuvxz|}}|{yxwvwy|}|||}~~|zywutttuvwy{}~|{yxwwwxyz{|}}|zxvtqpoopqsvwyyyxvuttuwz~~}||}~}zxvutuvxz|}}|{zxwvwx{~~|||}~}{yxvutttuvxz|~}{zxwwwxyz{|}}|{ywurpoooqsuwxyyxwvttuvy|}}||}~|ywuttuwy{|}}|{ywvvwy|}|||}~~}{ywvttttuwy{}~|{yxwwwxyz{|}}|zxvtqpoopqsuwyyyxwvutuwz}~}}|}~~{yvuttuwy{|}}|{ywwwxz}}|{||}~|{ywutttuvwy{}~|zyxwwwxyz{|}||zxvsqpoopqsvxyzzywvuuvxz~~}}}~~{yvuttuwy{|}}}{zxwwxz}}{{{|}~~}{ywvuttuvwy{}~|{yxwvwwxz{||||zxvtqpoooqsuwyzzyxwvvvx{~~}}}~}{xvuttuwy{|}}}|zyxwxz}~|{{{{|}~~|{ywvuttuvwy{}~|zywwvwwxz{|||{zxvsqooopqsvxyzzzxwvvwy|~}}}~|zwutttvwy{}~}}{zxxxy{~~|{z{{|~}|zxwuuttuvxz|~}{yxwvvwxyz{|||{ywtrpooopruwyzzzyxwvvxz}~}}}~}{xvuttuwy{|}~}|zyxwxz}}{{{{|}~~|{ywvuttuvwy{}}|zxwwwwxyz{|||{ywusqoooprtvxzzzyxwvvwy|~}}}}~{ywuttuwy{|}}}|zxwwxz|}|{{|}~~}{ywvuttuvwy{}}|zxwwwwxy{|}}|{zwusqooopruwxzzyxwvuuvx{~}}}}~~|ywutuuwy{|}}}{zxwwwy|}|{{|}~~}{ywvutttuwy{}~}|zyxwwxxz{|}}}|zxvsqpooprtvxyzyxwvuuuxz~~}}}~|zwvuuuwyz|}}}|zxwvwx{~~}|{||}~|zxwutttuvxz|~~|{yxwwwxy{|}}}|{ywtrpoopqsuwyzzyxvutuvy|~}}}~~{ywuuuvxy{}}}|{yxwvwy|~|{{|}~}{zxvutttuwxz|~}|zyxwwxxz{|}}}|zxvsqpooprtvxyzzywvuuuwz}~}}}~}{xvutuvxz{}}}|{yxwwxz|}|{{{|}~~}{yxvutttuwyz}~}|zywwwwxy{|}}}|zxvsqpooprtvxzzzyxwvuvxz}~~}}~}{xvuttvwy{}~~}|zywwxz|}|{z{{}~~}|zxvutttuwxz|~~|zywwwwxyz{|}||zxvsqpooprtvxz{{zywvvwxz~~}}~}zxvuttvwy{}~~}|zyxxxz}}{zzz{|}~~}{zxvuttuuwy{}~}|zxwwwwxyz{|}|{zxusqoooprtwyz{{zywwvwy{~}~~~|ywuttuvxz|}~~}{zxxxy{~~|{zzz{}~~}{ywvuttuvxy{}~}{yxwwwwxz{||}|{yvtrpoopqsuxy{{zyxwvvxz}~}}~}{xvuttvwy{}~~}|zyxxxz}}{zzz{|}~~}{zxvuttuvwy{}}{yxwwwwxz{|}}|{ywurqoooqsuwyz{zyxwvvwy|~}}~~{xvutuvwy{}~~}|zywwxy|~|{z{|}~}|zxvutttuwy{}~}{zxwwwxyz{|}}}{ywurqoopqsuwyzzzywvuuvx{~}}~~~{ywuuuvxz{}~~}{zxwwwy{~~|{{{|}~~|zxvutttuvxz|~}{zxwwwxyz{}}}}|zxusqpopqsuwyzzzywvuuvxz}~}}}~|ywvuuvwy{}}~}|zxwwwxz}}|{{||~~}{ywvuttuvwy{}~|zyxwwxyz{|}~}|{yvtrpooprtvxyzzyxvuuuvy|~}}~~{xvuuuvxz|}~}}{yxwwwy|~|{{{|}~~|zxwutttuvxz|~}{zxxwwxyz{}}~}|zxusqpopqsuwyzzzyxvuuvwz}~}}~}zxvuuuvxz|}~~}{yxwwxy|~|{{{{|~~|zxwutttuvxz|~}{zxwwwxyz{|}}}|zxusqpopqruwyz{zyxwvvvxz}~}}~}zxvutuvxz|}~~}|zyxwxz|~|{zz{|}~~|{ywvuttuvxz|~}|zxwwwwxy{|}}}|zxusqpooprtwyz{{zyxwvwxz}~~~~}zxvutuvxz|}~~}|zyxxxz|~|zzzz{}~~|{ywvuttuwxz|~}{zxwwwwxy{|}}|{ywusqpopqsuwyz{{zyxwvwy{~~~~~~|ywuttuwyz|~~~}|zyxxy{}}{zzz{|}~~}|zxvuttuvwy{}~|zyxwwwxyz{|}}|{yvtrpooprtvxz{{{zxwwwxz|~~~}zxvutuvxz|}~~}|zyxxxz|~|zzzz{}~~|zywvuttuwxz|~~}{yxwwwxyz{|}}|{ywtrqoopqsuxy{{{zxwvvwy{~}~}{xvuuuvxz|}~~}|zyxwxy|~|{zz{|}~~|zywuuttuwxz|~~}{yxwwwxy{|}}}}{ywtrqpoprtvxyz{zyxvvuvx{~~}~~~{ywuuuvxz|}~~}|zxwwwy{~}{{{{|}~~}{ywvuttuvxz|~~}{yxwwwxy{|}~~}|zwusqpppqsuwyzzzyxvuuvwz}~}}~~|ywvuuvxz{}~~}|zywwwxz}~|{{{|}~}|zxvuttuvwy{}}|zyxwwxyz|}~~}|{xvtrpppqrtwxzzzyxwvuuvy|~~}~}{xvuuvwxz|}~~}{zxwwwy{~}{{z{|}~~}{ywvuttuvxz|~~}{zxxwwxy{|}}~}|zxusqpppqsuwyzzzyxwvuvwz}~~~}{xwvvvxy{|}~~}{zyxxxz|}|{{{|}~~}{yxwvuuvwxz|~~}{zyxxxxyz||}}}{zxvtrqqqstvxyz{{zyxwwwy{}~}{yxwwwxy{|}~~}|{zyyy{|}|{{{||}~~~}|zyxwvvwxyz|}~}|zyxxxyyz{|}}|{zywutsrssuvxz{{{{zyxxyz{~}{zxxwxyz{|}~~~}|{zzz|}}|{{{||}~~~~~}|zyxxwwxxz{|}~}|{zyyyyz{{||||{zywuttstuvwyz{{{{zzyyz{|~~}{zyxxyz{|}~~~}}|{{{{|~~}|||||}~~~~~}}|zyyxxxyyz|}~~}|{zzyyyzz{|||||{zxwvuttuvwyz{{|{{zzzz{|~~|{zyyyz{|}~~~~}||{{{|~~}|||}}~~~~~~}|{zyyyyyzz{|}~~~}|{zzzzz{{|||||{zyxwvuuvwxyz{{||{{zzz{|}~}|{zzz{{|}~~~~}||{{||~~}}}}}}~~~~}|{{zyyyyz{||}~~}}|{{zzz{{|||}||{zyxwwvvwwxyz{{|{{{zzz{|}~}|{{{{{|}}~~~~}}|||||}~}}}}~~~~~~~}}|{zzzzzz{||}~~~~}}|{{{{{{{||}}}||{zyxxwwwxyyz{{||{{{z{{|}~~}||{{||}}~~~~}}||||}}~~~}}}~~~~~~~~}||{{zzzz{||}~~~~~~~}}|{{{{{{||}}}}|{{zyxxxxxyyz{{|||{{{{{|}~~}|||||}}~~~~~}}}||}}~~~~~~~~~~~~~}}||{{{z{{{|}}~~~~~~}}|||{{{{|||}}}||{{zyyxxyyyz{{|||||{{{||}~~}|||||}}~~~~~~}}}}}~~~~~~~~~~~~~~}}|||{{{{{||}}~~~~~~}}}||{{{||||}}}|||{zzyyyyyzz{||||||||||}~~~}}|||}}}~~~~~~~}}}}~~~~~~~~~~~~~~~}}|||{{{|||}}~~~~~~~}}|||||||||}}}|||{{zzyyzzz{{||}}}}||}}}~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}||||||||}}}~~~~~~~}}|||||||||}}}|||{{zzzzzz{{||}}}}}}}}}~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}||||||}}}~~~~~~~}}}||||||||}}}}|||{{{zzz{{{||}}}}}}}}}~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||}}}~~~~~~~}}}}||||||}}}}}}|||{{{{{{{{||}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||}}}}~~~~~~~~}}}}|||}}}}}}}}}|||{{{{{{|||}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}}}||||{{{{||||}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}}}}}|||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}||||||||}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}||||||}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}||}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m55.bin b/applications/SprPoP/assets/packed/MUS/m55.bin new file mode 100644 index 0000000..8188946 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m55.bin @@ -0,0 +1,3 @@ +~~~~~}}}{{|}{|}||~~y{x}|zyxwwwvvvwwxy{|}~}{zyxwwwvvuuuuuuuvwwy{}wtzxy|~~}{zyyxwwwwwwxxyz{|}~~}{zyxxwwwvvuuuuuuvwyz{}~wtxyvvxz|~~~}}||||{{||||}}~}xtqt{}zwutttttuvwxyz{{zyxwvtsrrrrrsttvwxyzz{{{||||}}}~~zwutuz|zxwvvvwwwxxxyyyyyyyyyyxxxxxxxxyyyzz{||}~~{xvuux||zxwuttsrrrqqqqqqqqqqqrrrrssttuvvwxyzz{|}~~|zyy{~~}}|||||{{{{{zzzyyyxxxxxyyyz{|}~}yurqqsx~~{ywwwwwxxxwwvusrqppppqqrsuuvwwxxxwwwwwxxyz{|}~~|ywutstwz~~|{zyxxxxxxxxxxxxxxxxxxxxyyyyyzzz{||}}~~|ywuuux|}{ywvuttssrrrrqqqqqrrrrrssttuuvwwxyzz{|}~}{yyy{~~}|||{{{{{zzzyyyyxxxxyyyzz{|}~}zwtrqqtx}}{zyxxyyyyxxwutsrqpppqrstuvwwxxxxxxwwxxxyyz{|~}{zxvtssuwz~~}{zyyxxxxxxxxxxxxxxxxxyyyyyzzz{{||}~~~|zwvuuvx|~|{yxwvuttsssrrrrrrrssssttuuvvwxxyz{{|}~}|zyyz{~~}||{{zzzyyyyxxxxxxyyyz{{|}~~{xusqqrtx}~}{{zz{{{zzyxvutsrrqrrstuvwxxyyyyyyyxyyyyz{|}~~|zxvutstuwz}}|{zyyxxxxxxxxxxxxxxxyyyyzzz{{||}}~~~|zxwuuuvy|~|{yxwwvuuutttttstttttuuuvvwwxyyz{{|}~~~|{zyyz|~~}}||{{zzyyyyyxxxyyyzz{||}~~|zwtrqqrtx|~}||||||{zyxvutssssttuvwxyyzzzzzyyyyyyzz{{|}~~}|zywuttstuwz}~}|{zyyyyxxxxxxxxxyyyyyzzzz{{||}}~~~|zywvuuuwy|~}|zyyxwwvvuuuuuuuuuuvvvwwxxyyzz{||}~~~|{zyyyz|~~}}|{{zzzyyyyyyyyyzz{|}}~~|zxvtrqqrux|~~~~~}}|zyxwvutttuvvwxyz{{{{{{{zzzzzz{{|}}~}|{yxvuttttvxz}~}||{zzyyyyyyyyyyyyyyzzzz{{{||}}~~~|{yxvuuuvwy|~}|{zyyxxwwwwwvwvwwwwwxxyyyzz{{|}}~~~}{zyyyyz|~~}||{{zzyyyyyyyzz{{|}~~~~~~}|{ywusrqrsux|~}{zyxwvvvwwxyzz{|||||||{{{{{{{{|}}~~|{zxwvutttuvxz|~}|{{zzzzyyyyyyyzzzzzz{{{|||}}~~~}|{yxwvuuuvxy|~~}|{{zyyyxxxxxxxxxxxyyzz{{{||}}~~~}|{zyyyz{|~~}}|{{zzzzzzzzzz{{|}~~~}}|||||{{yxvusrrrsux|~}|zyxxxxxxyz{{|}}}}}}|||{{{{{||}}~~}|{yxwvuutuvwxz|~}||{{{zzzzzzzzzzz{{{{{|||}}~~~~}{zyxwvuuuvwxz|~~}}||{{zzzzyyyzyzzzzz{{{||}}}~~~}|{zyyyyz{|~~}}|{{{zzzzz{{{||}~~~}|{{{{{{zzywvtsrrrsux{~}|{zyyyzz{{|}~~~~~~~~}}}|||||}}~~~}|{zyxvvuuuuvwxz|~~}}||{{{{{{{{{{{{{{{||||}}}~~~~}|{zyxwvvuuuvwxz|~~~}}||||{{{{{{{{{||||}}}~~~~~}|{zyyyyyz{|~~~}||{{{{{{{{||}}~~~}|{zzyyyyyyyxwvtssrstvx{}|{{z{{{|}}~~~~}}}}||}}}~~~}|{zyxwvuuuuuvwyz|~~~}}||||{{{{{{{{{{|||||}}}~~~~~}}|{zyxwwvvuuvvwxz|~~~~}}}|||||||||}}}}}~~~~~}||{zyyyyyyz{|~~~}}||||||||}}~~~}|{zyyyxyyyxxwvutsssstvx{~~}}||||}}~~~~}}}}}}~~~}|{zyxwwvvvvvwxy{|~~~~}}}||||||||||||||}}}}}~~~~~~}||{zyxxwvvuuuvvwyz|~~~~~~~}~~~~~~~~~}}|{zzyyyyyyz{|~~~}}}}}}}}}~~~~~~~~~~~}{zyxxwwwwwwwwwvuttsstuvx{~~}}}}~~~~~}}}~~~~~}|{zzyxwwvvvvvwxy{|~~~~}}}}|||||||||||}}}}}}~~~~~~~}}||{zzyxxwvvvuuvvwxyz|~~~~}||{zzyyxxxyyz{|}~~~~~~~~~~~~}}}}}}~~~}{zyxwwvvvvwwwwvvuttsstuvx{~~~~~~~~~~~~~~}|{{zyxxwwwwwwxyz{}~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~}}||{{zzyxxwwvvuuuvvwxyz|~~~}||{{zyyyxxxyyz{|}~~~~~~~~~}}|||||}}~~~~}{zyxwvvuuvvvvvvvvuuttttuvx{}~~~~~~~~~~~}||{zyyxxwwwwwxxyz|}~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzyyxxwwvvvuuvvvwxy{|~~~}}||{{zzyyyxxyyyz{|}~~}}||||||||}}~~~~~~}}|zyxwvuuuuuuvvvvvvuuttttuwx{}~~~~~~~~~~~~~}}|{{zzyyxxxxxxyyz{|}~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}|||{{{zzyyxxwwvvvuuuvvwwxy{|}~~~}}||{zzyyyxxxxxyyz{|}~~~}|||{{{{{|||}}~~~~}||zyxwvuutttuuuvvvvvuutttuvwy{}~~~~~~~~~~~~~~}}||{{zzyyxxxxxxyyz{|}~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||{{{zzzyyxxxwwvvvvuuvvvwxxy{|}~~~}}||{{zzyyyxxxxxyyz{|}~~}}||{{{z{{{{|||}}}}||{zyxvvuttttttuuvvvvuuuuuuvwyz}~~~~~~~~~~~~~~~~~~}}}}||{{{zzyyyyyyyyzz{|}~~~~~~~~}~}}}}}}}}}}}}}}}}}}}||||||{{zzzyyyxxwwwvvvvuuuvvwwxyz{|}~~~}}||{{zzzyyxxxxxxxyyzz{|}~~~}}|{{{zzzz{{{||||}|||{zyxwvuttsstttuuuvvvuuuuuuvwyz|~~~}}}}}}}}}}}}}}}}|||{{zzzyyyyyyyyzz{|}~~~~~~~}}}}}}}}}}}}}}}||||||||{{{{zzzzyyyxxwwwvvvvuuuvvvwwxyz{|}~~~}}|||{{zzyyyxxxxxxxyyz{{|}~~~}||{{zzzzzzz{{{||||{{zyxwvuttsssssttuuvvvvuuuuvvwyz|~~~~}}}}}}}}}}}}}}||||{{{zzzyyyyyyyzz{{|}~~~~~~~}}}}}}}}}}||||||||{{{{{{zzzzyyyxxxwwwvvvvuuuuvvvwwxyz{|}~~~~}}|||{{zzzyyxxxxxxxxxxyyz{|}~~~~}||{{zzzzzzzz{{{{|{{{zyyxwvutssssssttuuvvvvvuuuvvwyz|~~~}}}}}|||||||||||{{{{zzzzyyyyyzzz{{|}~~~~~~~}}}}}}}}|||||||{{{{{{zzzzyyyxxxwwwvvvvvuuuvvvvwxxyz{|}~~~}}}|||{{zzyyyxxxxxxxxxyyzz{|}}~~~}||{{zzyyyyzzzz{{{{{zzyxwvuttssssssttuuvvvvvvvvvwwyz|~~~}}}}||||||||||{{{{{zzzzzzyyyzzzz{{|}~~~~~~~}}}}}}|||||||{{{{{zzzzyyyyxxxxwwwvvvvvuuuuvvvwwxxyz{|}~~~~}|||{{zzzyyyxxxxwwwwxxxyyz{{|}~~}}|{{zzzyyyyzzzz{{{{zzyxwvvutssrrrssttuuvvvvvvvvvwxyz|~~~}}|||||{{{{{{{{zzzzzzzzzzzzzz{{{|}}~~~~~}}}}}}|||||{{{{{{zzzyyyyxxxxwwwvvvvvuuuuuvvvwwxxyz{|}~~~~}}}|||{{zzzyyyxxxxxwxxxxxyyz{{|}~~}}|{{zzyyyyyyzzzzzzzzyyxwvuutssrrrssttuuvvvvvvvvvwxyz|~~~}}||||{{{{{{{{zzzzzzzzzzzzzzz{{{|}}~~~~~}}}}}|||||{{{{{zzzzyyyyxxxxwwwvvvvvuuuuuvvvvwwxyyz{|}~~~~}}}||{{{zzzyyyxxxxxxxxxxyyzz{{|}~~~}}|{{zzyyyyyyyyzzzzzzyxwwvutssrrrrssttuuvvvvvvvvwwxyz|}~}}|||{{{{zzzzzzzzzyyyyyzzzzz{{{||}~~~~~~}}}}||||{{{{zzzzzyyyxxxxwwwwvvvvuuuuuuvvvvwwxyyz{|}~~~~}}|||{{{zzyyyxxxxwwwwwwxxxyyzz{||}~~~}||{zzzyyyyyyyzzzzzzyyxwvvutssrrrrssttuuvvvvvvvvwwxyz|}~}}|||{{{zzzzzzzzzzyyyyyzzzzz{{{||}~~~~~~}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuuuuvvvwwwxyyz{|}~~~}}}|||{{zzzyyyxxxxxxwwxxxxyyyzz{|}}~~~}||{zzyyyyyyyyyyzzyyyxxwvuutssrrrrrsttuuvvvvwwwwwwxyz{}~}}|||{{zzzzyyyyyyyyyyyyyzzzz{{||}}~~~~}}}}||||{{{zzzzyyyyxxxwwwwvvvvvuuuuuuuvvvwwxxyzz{|}~~~}}}|||{{zzzyyyxxxxwwwwwwwxxxyyzz{{|}~~~}}|{{zzyyyyyyyyzzzzyyyxwwvutssrrrrrssttuuvvvwwwwwwwxyz{}~}}||{{zzzzyyyyyyyyyyyyyzzzz{{||}}~~~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvvvvuuuuuuvvvvwwxxyzz{|}~~~}}}|||{{zzzyyyxxxxxwwwwwxxxxyyzz{||}~~~}}|{{zzyyyyyyyyyyyyyyxxwwvutssrrrrrrsttuuvvwwwwwwwwxyz{}~~}}||{{zzzzyyyyyyyyyyyyyzzz{{{||}}~~~~}}}||||{{{zzzzyyyxxxxwwwwvvvvvuuuuuuuvvvvwwxxyzz{|}~~~~}}|||{{zzzyyyxxxxwwwwwwwwxxxyyzz{{|}}~~}||{{zzzyyyyyyyzyyyyyxxwvuutssrrrrrssttuvvvwwwwwwwxxyz{}~~~}||{{zzzyyyyyxxxxyyyyyyzzz{{||}}~~~~}}}|||{{{zzzzyyyxxxwwwwvvvvvuuuuuuuuvvvvwwxxyzz{|}~~~~~}}|||{{zzzyyyxxxxwwwwwwwxxxxyyzz{{|}}~~}}|{{zzzyyyyyyyyyyyyyxxwvuutssrrrrrssttuvvvwwwwwwwxxyz{|~~~}||{{zzzyyyyyxxxxyyyyyyzzz{{||}}~~~~~}}}|||{{{zzzyyyxxxxwwwwvvvvvuuuuuuuuvvvvwwxxyzz{|}}~~~~}|||{{zzzyyyxxxxwwwwwwwwwxxxyyzz{{||}~~~}||{{zzzzyyyyyyyyyyyxxwvvuttsrrrrrrssttuvvvwwwwwwwxxyz{|~~}}|{{zzzyyyxxxxxxxxxyyyyzz{{||}}~~~~~}}|||{{{zzzyyyxxxxwwwvvvvvuuuuuuuuuvvvvwwxxyzz{|}}~~~~}}|||{{zzzyyyxxxxwwwwwwwwxxxxyyzz{{||}~~~}}|{{zzzzyyyyyyyyyyyxxwvvuttsrrrrrrssttuvvvwwwwwwwxxyz{|~~~}||{zzzyyyxxxxxxxxxyyyyzz{{||}}~~~~~}}|||{{{zzzyyyxxxwwwwvvvvvuuuuuuuuuvvvwwwxxyzz{|}}~~~}}|||{{zzzyyyxxxxxwwwwwwxxxxxyyzz{{||}~~~}}|{{zzzzyyyyyyyyyyyxwwvuutssrrrrrrsstuuvvwwwwwwwxxxyz{|}~}||{{zzyyyxxxxxxxxxxyyyyzz{{||}~~~~}}}||{{{zzzyyyxxxwwwvvvvvuuuuuuuuuuvvvwwwxxyzz{|}}~~~~}}||{{{zzzyyyxxxxwwwwwwwwxxxxyyzz{{||}~~~}}|{{{zzzzzzzzyyyyyxxwwvuutssrrrrrrsstuuvvwwwwwwwxxxyz{|}~}}|{{zzyyyxxxxxxxxxxyyyzzz{{||}~~~~}}}||{{{zzzyyyxxxwwwvvvvvuuuuuuuuuvvvvwwwxxyzz{||}~~~~}}||{{{zzzyyyxxxxxwwwwwwxxxxxyyzz{{||}~~~}}||{{zzzzzyyyyyyyyxxwwvuutssrrrrrrssttuvvwwwwwwxxxxyz{|}~~}||{zzyyyxxxxxxxxxxxyyyzzz{||}}~~~~}}|||{{zzzyyxxxwwwwvvvvuuuuuuuuuuuvvvwwwxxyzz{||}~~~}}}||{{{zzyyyxxxxxwwwwwwwxxxxyyyzz{{||}~~~}}||{{zzzzzzzzzyyyyxxwwvuutssrrrrrrsstuuvvwwwwwwxxxyyz{|}~~~}||{{zzyyxxxxxxxxxxxyyyzzz{||}}~~~~~}}||{{{zzzyyxxxwwwwvvvvuuuuuuuuuuvvvvwwwxxyzz{||}~~~}}}||{{{zzyyyyxxxxxwwwwwwxxxxyyyzz{{||}~~~}}||{{{zzzzzzzyyyyyxxwwvuutssrrrrrrsstuuvvwwwwwwxxxxyz{|}~~}||{{zzyyxxxxxxxxxxxyyyzz{{||}}~~~~}}||{{{zzyyyxxxwwwvvvvvuuuuuuuuuuuvvvwwwxxyzz{||}~~~~}}|||{{zzzyyyxxxxxwwwwwwwxxxxyyyzz{{|}}~~~}}||{{{{zzzzzzzyyyyxxwvvuttssrrrrrssttuuvvwwwwwwxxxyyz{|}~~~}||{zzyyyxxxxxxxxxxxyyyzz{{||}~~~~}}|||{{zzzyyxxxwwwvvvvvuuuuuuuuuuvvvvwwwxxyzz{{|}~~~~}}}||{{zzzyyyyxxxxxwwwwwxxxxxyyyzz{{||}~~~~}|||{{{zzzzzzzyyyyxxwvvuttssrrrrrssstuuvvwwwwwxxxxyyz{|}~~~}||{zzyyyxxxxxxxxxxxyyyzz{{||}}~~~}}|||{{zzyyyxxxwwwvvvvuuuuuuuuuuuuvvvwwwxxyzz{{|}~~~~}}}||{{zzzyyyxxxxxxwwwwxxxxxyyyzzz{||}}~~~~}}||{{{{{zzzzzzyyxxwwvuuttssrrrrrssttuuvvwwwwwxxxxyyz{|}~~}}|{{zzyyxxxxxxxxxxxxyyyzz{{|}}~~~~}}}||{{zzyyyxxxwwwvvvvuuuuuuuuuuuuvvvwwwxxyyz{{|}}~~~}}}||{{zzzyyyyxxxxxxxwxxxxxxyyyzz{{{|}}~~~~}}||{{{{{zzzzzzyyxxwwvvuttssrrrrrssttuuvvwwwwwxxxxyyzz{|~~}}|{{zzyyyxxxxxxxxxxxyyyzz{{||}~~~~~}}||{{zzyyyxxxwwwvvvvuuuuuuuuuuuuvvvwwwxxyyz{{|}}~~~~}}||{{{zzyyyyxxxxxxxxxxxxxxyyyzz{{||}}~~~~}}|||{{{{zzzzzyyyxxwwvuuttssrrrrsssttuuvvwwwwwxxxxyyz{|}~~~}}||{{zzyyyyxxxxxxyyyyzzz{{||}}~~~~}}|||{{zzyyyxxxwwwvvvvvuuuuuuuuvvvvvwwwxxyyzz{{|}}~~~}}}|||{{{zzzzyyyyyyyyyyyyzzzz{{|||}}~~~~~}}||||{{{{zzzyyyxxwwvvvuuuuttttuuuuvvwwwwxxxyyyyzz{||}~~~}}}||{{{zzzzzzzzzzzzzz{{{|||}}~~~~~~}}|||{{{zzyyyyxxxwwwwwwvvvvvvvvvwwwwwxxyyyzz{{||}~~~~~}}}||||{{{{{zzzzzzzzzz{{{{{|||}}}~~~~~}}}|||{{{{zzzzyyyxxxwwwvvvvvvvvvvwwwwxxxxyyyzzz{{||}}~~~~}}}|||{{{{{{{{{{{{{{{||||}}}~~~~~~}}}|||{{{zzzyyyyxxxxxwwwwwwwwwwwwxxxxyyyzzz{{||}}~~~~~}}}}||||||{{{{{{{{{{{|||||}}}}~~~~~~}}}||||{{{{zzzyyyyxxxxwwwwwwwwwwxxxxxyyyyzzz{{{||}}~~~~~~}}}}||||||||{{||||||||}}}}~~~~~~}}}|||{{{{zzzzyyyyyyxxxxxxxxxxxxxxyyyyzzzz{{|||}}~~~~~~}}}}}||||||||||||||||}}}}}~~~~~~~~}}}}||||{{{{zzzzyyyyyxxxxxxxxxxxyyyyyzzzz{{{|||}}~~~~~~~}}}}}}||||||||||||}}}}}}~~~~~~~~}}}||||{{{{zzzzzzyyyyyyyyyyyyyyyyyzzzz{{{{|||}}~~~~~~~~}}}}}}}}}||||||}}}}}}}}~~~~~~~~}}}}||||{{{{{zzzzzyyyyyyyyyyyyyyzzzzzz{{{{|||}}}~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}|||||{{{{{zzzzzzzzzzzzzzzzzzzzzz{{{{||||}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}||||||{{{{{{zzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}|||||{{{{{{{{zzzzzzzzzzzzz{{{{{{|||||}}}~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}||||||{{{{{{{{{{zzzzzz{{{{{{{{{|||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{{{{{{{|||||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{{{{{{|||||||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}|||||||||{{{{{{{{{{{{||||||||}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}|||||||||||||||||||||||||||}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}|||||||||||||||||||||||||}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~vtpigfedccffdfhffihgjkkkmoprvxz~zvsokheca`^]]\\\\\]_bhqyxipw}yvsqomlkkllmmoqtvy{}~|zywusqonllkkjjknqrux|~faxoffjouz|~~}|ywwvvvwwxy{}}|{zzzzz{|}|sq|zwwx{}~|zxwutsrrrssuvxz|~tja]`mxqjea_]]]^``bdfghiiiihgedba```acfhknruxz|~xrpqu}|yxwvvvwwwxxxyxyxwwwwwxxyyz{}~~wpkfdejr}~wrnjgdb`_]]\[[ZZZ[[[\\]^_`acdfhjlnprtvxz|~|xtpoqu|~{yxwvvuutttssssssstuvwy{|~vme_\]blx}wspnmmmlljifda_][ZZ[]_acehjklmmnmnnnooprsuwy{~{wsojgefiox~{yvtsrqqqqqqrrrrssssttuuvwwxy{|}}wqlheehnv|wsolifdba`_^^]]]^^__`aacdefhikmnprtvwy{}~|xtrppsy~|zywwvuutttssssttuvwxz|~{tmfa]]_fp||xutrrqqpnlifca_]]]^_acehjkmmnnnnnnnooqrsuwy{}|yvrnjgeehlsz|ywutsssrrrssssstttuuvvwxyz{|}~~ytokgedfjqz}yuroljhfedcbbaaaaabbcdefghjklnprsuwxz{}~{xtrppru{}{yxwvvuutttttuuvwyz|~~ytmgb^\]bis|zxwvvusqoljgeca`aacegikmnoppppooooppqstvxz|~}zvsoligfgjov}~|yxvutttsssstttuuuvvwxyyz{|}{wsnjgdddhmt|}yvspnljhgfedddcddddefghijklnoqrtuwxz|}~}zwtqpopsx}}{yxwvuuttttuuvwxz{}~}zwrmhc_]]`emw}||{zxvtqnkhfdccdefhjlnpqrrrrrrrqqrrstvwy{}~|zvspligfgimrx~}{yxwvuuuutuuuuvvwwxxyz{|}~~zvrnkgeddfjpw~}zwtsqomlkjihhhgghhhijjklmopqrtuvxy{|}|zwtrpppruz}{zxwvvuuuuvvwxyz|}~}|zxurniea^]_biqz}|zwtqnkigffghikmopqrsssssrrrrrsstvwy{}~}{xuroljhggilouz}{zyxwvvvvvvvvwwwxxyzz{|}~~{wtqmjhfeefimsz|ywusqponmlkkkkkkkkllmnopqrstuwxyz|}~~{yvtrpooqsw|~|{zxxwwwwwxxyz{}~}|{zyxvtqnjfb`^^aelt}}zwtqnmkkklmnprstuvvvvvuutttttuvwxz{}~~|yvtqnljihiknsw}~|{zyyxxwwwxxxxyyzz{||}~~|yvspmjhfeefhkpv|}{ywutsrppoonnnnnnoopqqrstuvwxyz{}~~}|ywusqpooprtx}~}{zyyxxxxyyz{|~~{ywvuttsqpmjgda__`ciow}zwtrpooooqrsuvwxyyxxwwvvuuuuvvwyz{}~}{xvsqnljihijmpty~}|{zzyyyyyyyyzzz{{|}}~|zwuromjhfeeegjnrx~~|{yxwvuttssssssssttuvvwxyzz{|}~~~|{ywusqponopsvz~~|{zzyyyzzz{|}~|ywusrqqppnmjheb```bfkrz~{xvtsrrrsuvwxz{{{{{{zyyxxwwwxxyz{|}~~}{yvtrpnlkjjkmosw{~}|{{zzzzzzzz{{{||}~~}|ywuspnljhfeeegilpuz~|{zyxwwvvvuuuvvvwwwxxyz{{|}}~~}{zxusrponnnoqsvz~~}|{{{{{||}~}zxusqpoonnmlkifdba`bdhnu||zxwvvwxyz{|}~~~~}}|{zyyxxxxyzz{|}~|{ywtrpnmkkjklnqtx|~}}||{{{zzz{{{{|||}}~~~}{zxvtrpnljhgfeffhknrw|~}|{zzyyyxxxxxyzz{{{||}}~~~}|zywvtrqponnoprux|~}||{|||}}~~{xuspnmllkkkkjhgedbbbdgkqx}{zyyz{|}~~}|{zzyyyyzz{|}~~|zxvtrqomlkkklnpsvz~~}|||{{{{{{{{{|}}}~~~}|{yxvtsqomkihgfeefgjlpty~~~}|||{{{{{|||||}}}~~~}|{yxvusrponnnnoqsuy|~~~~~~}}||||}~~|yvsqomkjjjjjjihgfdcbbcehmsz~}|||}~}|{{zzzz{{||}~~~}|zywvtrqonmmmmopsvy|~~}}|||||||||}}}}~~~~}}|{zyxvutrpomljigfeeefgiknrvz~~~~~~~~~~~~}|{zxwvtsrpoonnnoprtwz}~}|{{zz{{|}~~~}{xuspnkjihhhhhhhgfedcbcdgjou{~}||{{{{{||}}~~~}|{zywvtsqponmmmnprtwz}~}}}}|||||||||}}}}}}~~~~~~~~~~~~}}}||{zyxwvutrqpnmkjhgffeeeghjlpsw{~~}|{zxwvusrqponnnnoprtwz}~}{zyyxxyyz{{|}}}}|zxvtqnljhgffffggggfeddccdfhlqw}~}||{{{{{|||}}}}}}||{zzywvutrqpoooopqrtwz}~~~}}}}}}}}}}}}}}}}}}}}}}}}}|||{{zyyxwvutsrponlkjhgffeeffhilnruy}~~}|{zyxwutsrpoonnnnopqsvx{~~}|{zyxwwwwxxyzz{{{zyxvtqnljhfeeeeeffgffeedddegjnsy~}||{{{{{{||||||||{{zyxwvutsrqpooopqrtvx{}~~}}}}||||||||||||||{{{{{zzzyyxxwwvutsrqponmkjihgffeeffgikmpsvz~~~}|{zzywvutsrqponnnnopqsuwy|~}|{yxwvvvuvvwxxyyyyxwusqnljgfedcddeefffffeeeegilpu{~}||{{zzzzzzzzzzzyyxxwvutsrrqpppqqstvxz}~~~}}}}||||||{{{{{{zzzzyyxxwwvuutsrqponmlkjihgffeeefghjlnqtx{~}}|{zyyxvutsrqponnmmmmnoqrtwy|~~|{yxwvuuttuuuvwwwxwwutrpmkigedccccdeefffffeeefhjnrw|~}}|{{zzzzzzzyyyyxxwvvutsrrqqqqqqrsuwy{~~~~}}|||||{{{{zzzzzyyxxxwwvvuttsrqpponmlkiihgffeeffghjknpsvy}~~}|{{zyxwvusrqpoonmmmmnoprsuxz|~}{zxwvuttsssttuuvvvvutrpnligedbbbbccdeffffffffgiloty~~}|{{zzzzzyyyyxxxwvvutssrqqqqqqrstvxz|~~~}}|||{{{zzzyyyxxxwwwvvuuttsrrqpoonmlkjihhgfffeffghikloqtwz~~}||{zyxwvutsrqponmmmmmmnoprsuwz|~}|{yxvutssssssttuuuuutsqpmkigecbaaabcdefffggfffgikmquz}|{{zyyyxxxwwwvvvuttssrrrqqqrrsuvxy|~~~}}||{{zzzyyyxxxwwvvuuttssrqpponmllkjihhgfffefffghjlnprux{~~}}|{zyywvutsrqpponnmmmmnopqstvx{}~|{yxwutsrrqqqrrssstssrqomkigecba`aabcdeffgggggghjlosw|}|{{zyyxxxwwwvvvuuttssrrrrrrsstvwyz|~~}}||{{zzyyxxxwwvvvuutssrrqpponmllkjiihggfffefffghikmoqtvy}~}||{zyxwvutsrqpoonnnnnnopqrtuwy{~}|zywutsrqqqqrrrssssrqpomkigecba```abcdeffggggghikmptx}~}|zzyxwwvvvuutttsssrrrrrrrsstvwxz|~~}}||{zzyyxxwwvvuuttsrrqpponnmlkkjihhggffeeeffghijlnpruwz}~~}|{zyxwvutsrrqponnmmmmmnopqrtuwy{}}|zywvtsrqqqpqqqrrrrqqpomkigecba````abcdefgggghhijlnruz~~|{zyyxwwvvuuuttsssrrrrrrrsttuvxy{}~~}||{zzyyxxwwvuuttssrqqpoonmmlkjjihhggffeeeeffghikmoqsvx{~~}}|{zyxwvutsrqpponnnmmnnooprsuvxz|~~}{yxvusrqqppppppqqqqpponljhfdba`___`abdefgghhhhijkmpsw{~|{zyxxwvvuuutttsssrrrrrssstuvwyz|}~}}|{{zyyxwwvvuttsrrqpponnmllkjiihggfffeeeeffghijlnprtwy|~}|{{zyxwvutsrqpoonnmmmmmnooqrsuvxz|~~}{yxvusrqqppppppqqqqppomljhfdca`___`abcdefghhhhiijlnqtx|~|{zyxwvuuttsssrrrrrrrrrsstuvwxz{}~}||{zyyxwwvvuttsrrqpponnmlkkjiihhggfffeefffghijkmoqsuxz}~}|{zyxwvutsrrqpoonnnmmnnoopqstuwy{|~~|{ywvtsrqppoooopppppoomlkigecb``___`abcefghhhiiijkmoruy}}|{zxxwvuutsssrrrrrrrrrssttuvxyz|}~}|{{zyxxwvvuttsrrqpponmmlkkjiihhggfffeeeffgghiklnprtvy{~~}|{zyxwvutsrqqponnmmmmmmnnopqstvwy{}~~|{ywvtsrqqpppppppppponmlkigedba`___`abcdefghhiiijklnpswz~~|{zxwvuttssrrqqqqqqqqrrstuvwxy{|~~}||{zyxxwvuutsrrqpoonmmlkkjiihggffffeeefffghijkmoqsuwy|~~~}|{zyxwvutsrqpponnnmmmnnnopqrsuvxz{}~|zxwutsrqppoooooppooonmkjhfeca`____`abcefghiiijjklmoqtx|}|{yxwvutssrrrqqqqqqqrrsstuvwyz{}~}|{zyyxwvuutssrqpponmmlkkjiihhggfffeeefffghhjklnoqsvxz}~}|{zyxwvuutsrqppoonnnnnnoopqrstuwxz|}}{zxvusrqppooooooooonnmljigedba`____`abdefhhiijjkklnpsvy}~|{yxwvutsrrqqqppppqqqrsstuwxy{|~~}|{zzyxwvuutsrqqponnmlkkjiihhggfffeeeeffgghijlmoprtwy{}~}}|{zyxwvutsrqppoonnnmnnnoopqrstvwy{|~}|zxwutsrqppoooooooonnmlkigfdba`____`abcefghiijjjklmoqtwz~~|zyxvuttsrrqqqqpqqqqrrstuvwxz{}~~}|{zyxwvvutsrrqponnmlkkjiihhggfffeeeefffghijklnoqsuwy|~~}|{zyxwvuttsrqppoonnnnnnoopqrstuvxy{}~}{zxvusrqppoooooooonnmlkjhgecba`____`abdefghijjjkllnprux{}|zywvutssrrqqqqqqqqrrstuuwxyz|}~}|{zzyxwvutssrqpoonmllkjjihhggffeeeeeeffgghijkmnprtvxz|~~}|zzxwwvutsrqppoonnnnnnnoopqrstvwxz|}~|zxwutsrqqpppoooooonmlkjigedba`____`abcefghiijjkklmnpsvy|}|zywvutsrrqqpppppqqqrstuvwxy{|~~||{zyxwvutsrqqponmmlkjjihhggfffeeeeeeffghijklnoqstwy{}~}|{zyxwvuttsrqppooonnnnnoopqrstuvxy{|~}{zxwutsrqpppoooooonnmlkihfecb``____`abdefghijjkkllnoqtvz}}{zxwvutsrqqqppppqqqrrstuvwxz{}~~}|{zyxwvuutsrqponnmlkkjiihggfffeeeeeeffgghijkmnprsuwy{~~}|{zyxwvutssrqppooooooooopqqrstvwxz{}~~|zywvussrqqppppoooonmlkjhgecba`____`abcdfghijjjkklmnpruw{~}|zywvutsrqqpppppppqqrstuvwxz{}~~}|{zyxwvutsrrqponmllkjiihhggfffeeeeeeffghijklmoprtvxz|~}|{zyxwvuttsrqqppooonnoooppqrstuvwyz|}}|zxwvtsrqqppppoooonnmljihfecb``____`abdefghijjkklmnoqsux|}{zxwvtssrqqppppppqqrsstuvxyz|}~}|{zyxwvutsrqponnmlkjjihhggfffeeeeeeffgghijkmnpqsuwy{}~}|{zyxwvutssrqqppoooooooppqqrstuwxy{|~}{zxwutsrqqpppoooonnmlkjigedca`____``acdeghijjkkllmnprtvy|}|zxwvusrrqppppppppqqrstuvwyz|}~~}|{zyxwvutsrqponmllkjiihggfffeeeeeeeffghiiklmoprsuwy{}~}|{zxwwvutsrrqppooooooooppqrstuvwyz{}~~|zywvutsrrqqppppoonmlkjigfdcba`____`abdefghijkkllmnopruwz}~}{zxwutsrrqpppppppqqrstuvwxy{|}~}|{yxwvutsrqpoonmlkjjihhggfffeeeeeeffgghijklnoqrtvxz|~}|{zyxwvuttsrqqpppooooooppqrrstuwxy{|}}|zxwvutsrqqppppoonnmlkjhgedca``___`abcdeghijjkkllmnoqsux{~~|{yxvutsrrqqpppppqqrrstuvwyz{|~~|{zyxwvutsrqponmlkkjiihggfffeeeeeefffghiijlmnpqsuwxz|~~}|{zyxwvutssrqqpppoooopppqrrstuvwyz{}~~|{yxwuttsrrqqqppoonmmkjigfdcba`___``abcefghijjkllmmoprtvy|~}{yxwutsrrqpppppppqqrstuvwxz{|~~}|{zyxwvutrqpoonmlkjjihhgfffeeeeeeeffgghijklmoprtuwy{}~}|{zyxwvutsrrqqppppoppppqqrstuvwxyz|}}|zyxvutssrrqqpppoonmlkjhgedca``___`aacdefhijjkkllmnoqruwz}~|{yxvutsrrqqpppppqqrsstuvxyz{}~~}|{zywvutsrqponmllkjiihggfffeeeeeeeffgghijkmnoqrtvxy{}~}{zyxwvvutsrrqqppppppppqqrrstuvwxz{|~}|zywvutssrqqqppoonnmljigfdcba`___``abcefghijjkllmmnpqsux{~~|{yxwutsrrqqpppppqqrrstuvwyz{}~~}|{yxwvutsrqponmlkjjihggfffeeeeeeeeffghiiklmnpqsuvxz|~~}|{zyxwvuttsrrqqpppppppqqrrstuvwxy{|}~|{zxwvuttsrrqqqpoonmlkjhgedcaa`___`aacdefghijkkllmnoprtvy{~}|zyxvutsrrqqppppqqqrstuvwxyz|}~~}{zyxwvusrqponmllkjihhggffeeeeeeeeffgghijklmoprtuwyz|~~}|{zyxwvuttsrrqqqpppppqqrrsttuvwxz{|~~|{yxwvutssrrqqppoonmljihfedba``__``abcdeghijjkllmmnoqstwy|}|zywvutsrrqqqppqqqrsstuvwxz{|}}|{zyxwutsrqponmlkjjihggffeeeeeeeeeffgghijkmnoqrtvxy{}~}|{yyxwvutssrrqqqqpqqqqrrsstuvwxy{|}~~}{zyxwvuttssrrqqponmlkjhgedcba``_``aacdefghijkkllmnopqsuxz}}|zyxvutsrrqqqppqqqrrstuvwxy{|}~~}|{zxwvutsrqponmlkjihhggffeeeeeeeeefgghijklmnpqsuvxz|~}|{zyxwvvutssrrqqqqqqqqrrrstuvwxyz{|~~}{zyxwvuttssrqqpponmlkihfedba``````abcdefhiijkklmmnoprtvx{~~}{zywvutsrrqqqqqqqrrsttuvwyz{|~~}{zyxwvtsrqponmlkjjihggffeeeedeeeeffghhijklnoprtuwyz|~~}|{zyxwwvuttsssrrrrrrssttuuvwxyz{|}~}{zyxxwvuutssrqpponmljihgfedcbbbbbccdefghijkklmmnopqstvxz|~}|{zyxwvvutttsssstttuuvwxxyz{}~~}|zyxwvutsrqpoonmlkkjiihhhggggggggghhijklmnopqrtuwxz{}~~}|{{zyxxwwvvuuuuuuuuuuvvwwxyzz{|}~~}|{zyxxwvuutsrrqpoonmlkjihhgfffffffgghijjkllmnopqrstuvxz|}~}|{zzyxxwwvvvvvvvvvvwwxyyz{||}~~~}|{zyxwvuttsrqpoonmmllkkjjjiiiiiiijjjkklmnnoprstuwxy{|~~}||{zzyyxxwwwwwwwwwwwxxxyzz{||}~~}|{zzyxwwvuutssrqqponnmlkkjjiiiiiiijjkklmmnoopqrrstvwxz{}~~~}|{{zzyyxxxxwwwwxxxxyyzz{||}~~~}|{{zyxwwvuttsrqqpoonnmmmllllkkkkklllmmnnoppqrstuwxyz{}~~~}}|{{zzzyyyxxxxxxxyyyyzz{{||}~~~}||{zzyxwwvvuttsrrqpponnmmmlllllllllmmnnooppqrsstuvwxz{|}~~}||{{{zzyyyyyyyyyyyzzz{{||}~~~}}|{{zyxxwvvuttsrrqqppooonnnnmmmmmnnnnooppqrrstuvwxyz{|}~~~}|||{{zzzzzzyyyzzzzz{{{||}}~~~~}||{zzyyxwwvvuttssrrqpppoonnnnnnnnnnoopppqrrsstuvwxyz{|}~~~}}|||{{{zzzzzzzzzz{{{{||}}~~~~~}}|{{zyyxwwvvuttssrrrqqppppoooooooppppqqrrsstuuvwxyz{|}~~~}}|||{{{{zzzz{{{{{{{|||}}~~~~~}}|{{zzyxxwwvvuuttssrrqqqpppppppppppqqqrrssttuvvwxyz{{|}~~~}}}|||{{{{{{{{{{{{||||}}~~~~~}||{{zzyxxwwvvuuttsssrrrrqqqqqqqqqqqrrrssttuuvwxxyz{||}~~~~}}}||||{{{{{{{{{||||}}}~~~~~}||{{zzyyxxwwvvuuuttsssrrrrrrqrrrrrrrsssttuuvvwxxyz{{|}~~~~}}}||||||{{{|||||||}}}~~~~~}}||{{zzyyxxwwvvvuuttttssssssssssssssstttuuvvwwxyyz{{|}~~~~}}}}|||||||||||||}}}}~~~~~}}|||{{zzyyxxxwwvvvuuuttttsssssssssttttuuuvvwwxxyyz{{|}~~~~~}}}}}||||||||||}}}}}~~~~~~}}|||{{zzyyxxxwwwvvvuuuttttttttttttttuuuvvvwwxxyzz{{|}}~~~~}}}}}}}}|||}}}}}}}~~~~~~}}}||{{{zzyyyxxwwwvvvvuuuuuttttttuuuuuuvvvwwxxyyzz{{|}}~~~~~}}}}}}}}}}}}}}}}~~~~~~~~}}}||{{{zzyyyxxxwwwvvvvvuuuuuuuuuuuuvvvvwwxxxyyzz{{|}}~~~~~~}}}}}}}}}}}}}}~~~~~~~~}}}|||{{zzzyyyxxxwwwwvvvvvvvvuuuvvvvvvvwwwxxyyyzz{{|}}~~~~~~~~}}}}}}}}}}~~~~~~~~~~}}}|||{{zzzyyyyxxxwwwwwvvvvvvvvvvvvwwwwwxxxyyzz{{||}}~~~~~~~~~}}}}}}~~~~~~~~~~~}}}|||{{{zzzzyyyxxxxxwwwwwwwwwwwwwwwwwxxxxyyyzz{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{zzzzyyyyxxxxxwwwwwwwwwwwwwxxxxxyyyzzz{{||}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{{zzzyyyyyxxxxxxxxwwxxxxxxxxxyyyyzzz{{{||}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}||||{{{{zzzzyyyyyyxxxxxxxxxxxxxxxyyyyzzzz{{{||}}}~~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{{zzzzyyyyyyyyxxxxxxxyyyyyyyzzzz{{{{||}}}~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{{zzzzzzyyyyyyyyyyyyyyyyyyzzzzz{{{|||}}}~~~~~~~~~~~~~~~~~~}}}}|||||{{{{{zzzzzzyyyyyyyyyyyyyyzzzzzz{{{{|||}}}~~~~~~~~~~~~~}}}}}|||||{{{{{zzzzzzzzzzyyyyyzzzzzzzzz{{{{||||}}}~~~~~~~~~}}}}}|||||{{{{{{zzzzzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~~~~~~~}}}}}||||||{{{{{{{zzzzzzzzzzzzzzzz{{{{{{||||}}}}~~~~~~~~}}}}}}||||||{{{{{{{{{{zzzzzzzzz{{{{{{{{|||||}}}}~~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{{{{{{{{{||||||}}}}~~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{{{{{{{{{{||||||}}}}}~~~~~~~~~~~}}}}}}}|||||||||{{{{{{{{{{{{{{{{|||||||}}}}}}~~~~~~~~~~~~}}}}}}}|||||||||||{{{{{{{{{{{|||||||||}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||||||||||||||||||||}}}}}}~~~~~~~~~~~~~}}}}}}}}}|||||||||||||||||||||||||}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}|||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}||||||||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}||||||}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~}{|||}}~|smha_bo~rmmquy~zvrnjgfecdfhjnsvz}ytnic^YTOMJHFEDEGJPXcsYZ`Ke}yvtrrrtwz|}xuqnkigffhlnsx~vdoxnpv~}yvsppnnnoqsvz~tUBLwp^ROOSX^cfillmifecb`^\[ZYYZ[\]aeimqvz}{zzyyy{|~{sgZSYl{xwy|~}}}~}kZKDI\zyk`WQMJJIHHIIJIIJLMNPQRUWZ\`cfjmptwy|wpf_Z^jw{vronmmmnopqstvxwx{}yj[ND>AK_x»re[SMHFEEFHJKMNOOOLIIHIJKNRVZ_diosx{~{|{||||}}}|zwsmgb][^gtsfZPIIO]ryoe]VQLHECA@????@ABDFHJMORVY]`cgimoruwy{|~|wqib\YZ_jx|wtrqppqrsuwy{~|o`QE<;APf{qida_^[XTOJEB?>?AEINSW[^`abbbbcdegilpsvz~}wph`ZVW[er|xvtsrrrrrsttuvwxz{}|qeZQKILWgzxne]WQLHFCBA@@ABCEFHJLORUX[^behlortwz|~}xrlfccgp|~{xvuttttuvwy|~zocUJA=AK]sztoljfc^YSNIEBABDHLPTX[]_`_`__``bdfilpswz}}ysmf`ZVUX_jw~{ywvuttuuuvwxyz{}~yod[RLJLTaqwng`ZVROLJHHGGHHJKMOQSVY\_begkmpsuxz|~}xsnhc`_bhr}zwutsrrstuwy|~zqfZOE@AHVj}yvsokf`ZTOKIHILOSWZ]`abbbbbbbcdfilorvy}|xsmga[WUW\ep}|yvutssssttuvwxy{|~}ulcZSNKMR\j{wpjd_[XURQPOOOOPQSTVX[]`behjmpsuwy|~|xsniebabgoz}{xwvuuuvwy{~~{wrkaWME?>BM]q}zwusmga[VRPOOQTX\_ceghhhggffghikmpsvy|~zupjd_[YY]dmy}{yxwwwwwxyyz|}~wph`XQLJJNVbp{tnid`\YWUTSRRRSTUVXZ\^acfhkmpruwy{}~|xtojfcaaekt~|zyxxxyz|~|yvsnibYQIC@BJVh{~zuoic]XTRQRTW[^adefhhggfeeefhjlnqtwz}~{wsnid_[YY[ais~}{{zzzzz{|}~~wphaXRNKKNT^ky|vqlheb_]\[ZZYZZ[\^_acegikmpqtuwy{|~}{xtplgc`^^afmv~|zyxxyy{}zvspmjfaZSLFCCHR`q|vpic_[ZY[]_cfhjkkkkjihhhhijlnqtvy|~}zvrmid_\YYZ^dmw~}{zyyyyyzz{|}ysmf`YTPMLNS[fr~ytpmjgedbaaaaabcdeghjkmoqstvxz{|~~|zwtplieb``aeks|~|{{{|}~~xsoligda]XRLFCBEMXhy}wqkgcaaacfhkmoqqqppnmmlllmnprtvy{}~{xuqlhc`]\]`elu~~}}|||}}~~ytnhb]WRNKJKOV_jvzvroljhfeddeeeffghjkmnoqrtvxyz|}~~}{xvsolheb`_`chow~~~~}wrlgeba^[XSNIEDEJS`pztojgfefhjmoqrssrqponllllmnoqsvxz|}~|zwtplhda^]]_cipy~~~zvqlga\WSOLLLOU\fq}}yvsqnmkjjiiiijjklmopqrtuvxyz{|}}~~~~~~}|{yxuspmifc`_^^adjqy{tnieb`_][XUPLHEFIPZhxzuqnmmnprtvwxxxvusrponnnopqsuwyz|}~~~}|{xvrokhda^]\^aels|~~}}}}~~}zvrnie`[WSPNMMPT[cmx|zwutrqppoooppqqrrstuvwxyz{||}}}~~}}|{zywtrolifc`^]]_bgmt{{toid`]\ZYXVSPLIFFHMUao~{wusstuwy{|}}||zxwusrqqqqrsuvwyz{{{{{yxvspmjgdb``acglsz}{xuqmiea]YUQNLKKMQW^gq{~|zxwvutssssttuuvwwxyz{{|}}~~~~~~}|{zywurpmjgdb`_^_aejqx~xslfa]ZXVVUTRPMJHGGKR[hu|yvuuvxz{}~~~}|{ywutsrrqrstuvwxyzzzyxwuspmjhecaaacfkpw~~|yvspmiea]YVROMLLNQV\dmw}{zyxxwwwwwwxxxyyz{{|||||||||{zyxvuspnkifca_^^^`chms{}|||}~{wqke`[XUTSSSRQOLJIIKPXbo|~|{|}~}zxwutsrrsstuvwxyyyxwvtromjgeca``bdhmsy}|zxvspnjgd`]YVSPNMMNPTZair|~}|{{{{zzz{{{{||||||||||{zyxwusqomjhec`_]]]^adiou|}{yyxxyzz|}}}|zvrmgb]XURQQPPPPOMKJIKNU]hu~|zxwuttstttuvvvvvutsrpnkigedccdfimrx~}|{zywusqolifc`]ZWTQOMLLMORW]dlu~~~~~~~~~~~~~~}}||{zywvtrpnkifdb`_^^_adhmry~|zxvvuuvwyz{{zxvrmhb]XTQONNNNONMLKJKMQXbmz}{ywvutttttuuuvuutsrpnljhfedddfhlqv|~~~~~~}~}}||{zyxwvtrqnljgeb_\YVTQONMLMORV[biqz~~~~~~~}}}||{zyxvusqomkhfdb`_^^_`cfkpu{~{ywutssssuvwwwvsplgb]XSPNMLMNNNNMLLLMPV^ht~{ywvutttttttttssrqomkjhfedcdegjnrx}~~~~}|||{zzyxwvusrpomkhfca^\YVTRPNMMNORUZ_fnv~~~~}||{zywvtsqomjhfca`^]]]^`cgkpv|}zxvsrppoppqrsssrpmie`[VRNLKKKLMNNNMMLMPT[cny~|zxvuttsssrrrqponmkjigfeefghknsw}~~}}||{{zzyyxwwuusrqonljhfda^\YWURPOMLLMNPSW]cjqy~}|{zxwutqpnkigeca_^^^_`cfjnty~}{xusqonmmmnoopponkhd_ZUQMJIHIJKLMNMMMMORW^gr~~|zxwvutsssrrqqponlkjhggfggiknrv{~}|{{zzyxxwvuutrrpomljhgeb`^\YWUSQONMMMNPRV[`gnu}~}|{zxwutrpnligeca`_^^_`behmqv|~{xuspnlkkkkllmmmljgd_[VQMJHGHIJKMNNNNNORV\cmx}zxvusrqpponnmlkjihgffffghjmpty~~}|{zzyxwvvttsqponlkigecb_][YWUSQPNMMMNPRUY^dkry~}|{yxvusqomkigdba_^]]]^`beimrw||ywtqomkjjjjkkllkjhfb^ZUPMJHGGHIKMNOOOOPRUY`hr}}{ywutsqqponnmlkjihggfgghjlosw{~}|{zyxwvutsrqponlkihfeca_][YWUTRPONMMMMOQTX\agnu|~|{yxvtrpnljhfdba`_^^_`behlpuy~}zwtqoljihgghhiiiigeb^ZVQMJGFEFGIKMNOOOPQSW\clv}{ywutrqqponmllkjihhhhhijlorvz~}|{zywwutsrqponlkjhgedb`^][YWUSRPONMMMNOQSW[_ekqx~}{zxwusqomkigecba`__`abegknsw|~{xuromjhgffffggggfeb_[WRNJHFEFGIJLNOOPPQSVZ`hq{~{xvtrqonmlkjiihggffgghiknqtx|~}{zyxvutsrpomlkihfeca`^\ZYWUSRPONMMMNOPSVY^chnu{~}|zxvtsqoljhfdca`_^^^_`behkosw|}zwtqnkigfeeeeffgfeda^ZVRNJGFEEFGILMOPQQRSUY^dlv}{xvtrponmlkjiihhggghhjkmpsvz~~|{yxvutsqpomlkjhgedb`_][ZXVUSQPONMMMMNPRUX\afkqx~}|zxvtrpnljhfdca`___`abdgjnquz~{xuroligedcccddddcb`^ZWSNKGEDCDEGJLNOPQRSTW[ahpy}zxvtrponmlkjiihhhhhijkmoruy}~}{zxwutsqpomljigfeca`^][YXVTSQPONMMMMNOQSVZ^chntz~}{ywusqomkigedba````abdfilptx||xuroligeddccddddcb`]ZWSOKHEDCDEGILNOPQRSTVZ^dlu~}zwuspomlkjihggfffghijlnqtw{}{zxwusrpomljihfdca`^\[YXVTSRPONMMMMNOQSVY]aflqw}}{ywusqomkigecba`___`abdgjmqtx|}zvspmjhfdccbcccccb`^[XTPLIFDCCDFHKMOQRSTUVY]bhpy|ywtrpnmkjihhggggghijkmpsvy}}{zxvusqpnmkjhgedb`_]\ZYWUTSQPONNMMMNOPRUX\`dintz|{ywurpnljhgecba````acdgiloswz~~{xtqnkhfdcbaaaaaaa`^[XUQMJGDCBCDFIKNPQRSTVX[_elt||yvtrpnmkjiihhgghhijkmorux{~|zxwusrpnmkjhfecb`^][ZXWUTRQPONMMMMNNPRTWZ^bglqv|}{ywusqomkigedba````abcehknqtx|{xtqnkhfdcbbbbbbaa_^[XUQNJGECCCDFHKMOQRSTUWY]bhow{xvsqnmkjihgffffggijlnpsvy}~|zxwusqpnlkigfdb`_]\ZXWUTSQPONNMMMNNPQSVY\`eioty~|zxvtrpnljhfdcba```abcdgilorvz}}yvsoligecbaaaaaa`_^\YVSOLHFDCCCEGJLOQRTUVWY\`els{~zwurpnlkihgggfgghijkmorux{~}{ywusqpnljigedba_]\ZYWVTSRPONNMMMNNOQSUX[_cglqv|~|zwusqomkihfdcbbaaabcdfhknqtx{{xtqmjhecba```___^]\YWTPMIFDCBBDFHKMPRSTUWX[^bhov}zwtronlkihhgggghijkmoqtvy}~|zxvtrpnmkigedb`_]\ZXWUTSQPONNMMMMNNPQTVY]`einsx~|zxvtrpnljhfedcbaaabcdfhjmpsvz}|xuqnkhfdcbaa````_^\ZWTQMJGDCBBCEGJMOQSTUVXZ\`ekrz|yvsqnlkihgfffffghikmorux{~~|zwusqonljhfeca_^\ZYWVTSRQOONMMMMNOPQSVX\_cglqv{~|ywusqomkigfdccbbbbcdegiknqtw{~~zwspmjgecbaa```__^][XUROLHFDCBCDFILNQSTVWXY[_chov}{xurpnljihgffffghiklnqsvy|~|zxvtrpnljhgeca`^\[YWVUSRQPONMMMMNNOQSUXZ^afjnsx}}{ywtrpnlkigfedcbbbcdeghkmpsvy||xuqnkhfdba`____^^\[YVSPMIGDCBBCEGJMORTUVWYZ]aekry~zwtromljihggggghijlnprux{~~|zwusqomkigedb`^\[YXVUSRQPONMMMMMNOPRTVY\_cglquz~|zwusqomkjhgedcccccdefhjlorux{~}yvroligecba```__^][YWTPMJGECBBCEGILOQSUVWXZ\_chnu|}zwtqomkihgffffghijlnpsvx|}{ywtrpnljhfdca_][ZXWUTRQPONNMMMMNOPQSUX[^bfjnsx}}{ywtrpnljigfedccccdefgikmpsvy|{xtqnkhfdcaa``__^]\ZXUROLIFDCBCDFHKNPRTVWXZ[^afkqx|yvspnljihgfffghijkmortwz}~|zwusqomkigeca_^\ZYWVTSRPOONMMMMNNOQSUWZ]`dhlquz}zxvtrpnljigfedddddefgijmortwz}}yvroligeca``__^^]\ZXVSPMJGECBBCEGILOQTUWXY[]`chnt{|yvspnljhggfffgghjlnpruwz}}{ywtrpnljhfdb`^\ZYWVTSRPONNMMMMMNOPQSUX[^bfjnrw|~{ywusqomkihgfeddddefghjlnqtvy|~zwspmjhfdcba``__^\[YVTQNJHEDCBCDFIKNPSUVWYZ\^afjpw~~{xuromkjhggffgghikmoqtvy|}zxvtqomkigeca_][ZXVUSRQPONNMMMMNNPQSUWZ]`dhlpty~}zxvtrpnljihgfeeddefghikmprux{~|yuroljgedbaa`__^]\ZXUROLIGECCCDEHJMORTVWYZ[]`dhmsz}zvtqolkihggggghijlnpruwz}~{ywurpnljgeca`^\ZXWUTRQPONNMMMMNNOQRTVY\_bfjnrw{~|zwusqomljihgfeeeefghikmoqtvy|~{wtpnkhfdcba`__^]\ZYVTPMJGEDCCCEGILOQSUWXY[\^bfjpw}}zwtqnlkihgfffghijlnpsux{}}{xvtqomkifdb`^][YWVTSRPONNMMMMMNOPQSUWZ]`dhlpty}}{ywtrpnmkjhggfffffghikloqsvx{~{xuroljgfdcba``_^][YWTQNKIFDCCCDFHJMPRTVWYZ\]`chmsy|xuspnljihgggghijlmortwy|~|zwuspnljgeca_]\ZXVUSRQPONMMMMMNNOQRTVY\_bfjnrv{~|zxusqpnlkihggffffggijlmprtwz|~zwtqnkigedbba`_^]\ZXVSPMJHEDCCCEGILOQTVWXZ[]_bfjpv|}zwtromkjihggghijkmoqsuxz}}{yvtromkifdb`^\ZYWUTSQPONNMMMMMNOPRSUXZ]adhlptx}~{ywusqomljihggfffgghjkmoqsvy|~~{xuroljhfedcba`_^][YVSQNKHFDCCCEFIKNPSUVXY[\^`dhmry}zwtromkjihggghijkmoqsvx{~|zxusqnljgeca_][YXVTSRPONNMMMMMNNPQRTWY\_bfimrvz~}zxvtrpomkjihhgggghijkmoqsuxz}}zwspnkigfdcbaa`^][YWUROLIGEDCCDEGJLOQTVWYZ[]_bfjou{|yvsqomkjihhhhhiklnprtwy|~{ywtrpmkifdb`^\ZXWUTRQPONMMMMMMNOPRTVX[]adhkosx|~|zwusqpnlkjihhggghijklnprtvy|~|xuromkhgedcba`_^\[XVSQNKHFECCDEFIKNPSUWXZ[\^adhlrx~~{xurpnljihhhhhijlmoqsvx{}}zxusqnljgeca_][YWVTSRPONNMMMMMNOPQSUWY\_bfimquz~}{ywusqonlkjihhhhhijklnprtvx{~|yvsqnljhgedcba`_][YWTQNLIGEDDDEFHJMORTVWYZ\]_bfjotz~{xurpnlkiihhhhijlmoqsvx{}~|ywtrpmkifdb`^\ZXVUSRQPONMMMMMMNOPRTVX[]adgkosw{|zxvtrpomlkjiihhhijklmoqsuwz|~{xurpmkihfedcba_^\ZXUSPMJHFDDCDEGIKNPSUWXZ[]^adglqw}}zwtromlkjihhiijkmnpruwy|}{xvsqoljgeca_][YWVTRQPONMMMMMMNOPQSUWY\_bfimquy}~{ywusqpnmlkjiiiiijjkmnprtvx{}}zwtqoljigfdcba`_][YWTROLIGEDDDEFHJMORTVXY[\^`beinty~{xvsqomkjiiiiijklnoqsvx{}~|zwurpnkigdb`^\ZXVUSRPONNMMMMMMNOPRTVX[]`dgkosw{}{yvusqonmlkjjiijjklmnprtvxz}~{xuspnljigfedba`^\ZXUROMJHFEDDDFGJLNQSUWXZ[]_adglqv|~{xvsqomljjiiiijklnprtvx{}}{xvtqoljheca_][YWUTRQPONMMMMMMNOPQSUWY\_beilptx|~|zxvtrponllkjjjjjkklnoqsuwy|~}zwtromkjhgedcba_][YVTQNKIGEDDDEFHKMPRTVXY[\^`beinsx~}zwurpnmkjjiiijklmoqsuwy|~|zwurpnkifdb`^\ZXVTSRPONMMMLMMMNOPRTVX[]`cgjnrvz~}{ywusrpomlkkjjjjkklmoprtvxz}|yvsqolkigfedba`^\ZXUSPMJHFEDDDFGILNQSUWYZ[]_adgkpu{|yvtrpnlkjjiijkkmnoqsuwy|~}{yvtqomjheca_][YWUTRQPONMMLLMMNOPQSUWY\_beilptx|}{xvusqpnmllkkkkklmnoprtvxz|~}zwurpnlkihfedba_][YVSQNKIGEEDEEGIKMPRTVXY[\^`beimrx~~{yvtrpnmkkjjjjklmnprtvx{}|zxuspnkigdb`^[ZXVTSQPONMMLLLMMNOPRTVXZ]`cgjnruy}~|zxvtsqpommmllllmmnoprsuwy{}|ywurpnlkihfecb`^\ZXVSQNLJHGGGGHJLMPRTVXY[\^`behlqu{}{ywusqponmmmmmnopqstvxz|~~|zxvsqomjhfdb`^\ZYWVTSRQPPOOOOPQQSTUWY[^acfjmptx{}{zxvutrqppoooooppqrstvwy{|~~{ywusqomljhgedba_][YWVTRQONMMMNNPQSTVXZ[]_abegjmquy}~|zxvusrqqpppppqrstuwxz{}~|zxvtrpnljhfdb`_]\ZYXVUUTSSSSSSTTUVXY[]_bdgjmpsvz}~}{zxwvutssrrrrrsstuvwxz{|~}{ywutrpomkjhgedb`_][YXVUTSRRRRSTUVWYZ\]_abdfiknqtx{}{zywvuttsssssstuuvwyz{}~~|{ywusrpnljigecb`_^\[ZYXXWWWVWWWXYZ[\]_acehjmpsux{~}|{yxwvvuttttttuuvwxyz{|}~|zywutrpomljigfecb`_]\[ZYXWVWWWXXYZ[]^_abdfhjlnqtwz}~}|zyxwvvuuuuuuuvwwxyz{|~~}{yxvtsqonlkigfdcba_^]\\[ZZZZZZZ[[\]_`acegikmpruwz}~}|{zyxwwvvvvvvwwxxyz{|}~~}|zywvtsqpnmkjigfecba_^]\[[ZZZZZ[\]^_`acdegikmoqtvy|}|{zyyxwwwwwwwwxxyzz{|}~}|zywvtsqpnmkjigfedcba`_^^^]]]]]^^_`abcefhjlnprtwy|~~}|{zzyxxxwwwwxxxyzz{|}~}|{zxwutsqponmkjigfedcba`__^^^^^^__`abcefgijlnprtvy{}}||{zyyxxxxxxxyyzz{||}~}|{zxwutsqpomlkjhgfedccbaa``````aabbcdefhikmnprtvy{}~}|{{zzyyyyyyyyzz{{|}}~~}|{yxwvtsrqonmlkjigfeedcbbaaaaaaabbcdefgijkmnprtvxz|~}||{zzzyyyyyzzz{{|}}~~}|{yxwvtsrqonmlkjihgfeeddccccccccddefghijlmoqrtvxz|~~}||{{zzzzzzzzz{{||}}~~}|{zyxwvutsqponmlkjihggfeeddddddddeefghijklnoqrtvxz|~~}}||{{zzzzzz{{{||}}~~~}|{zyxwutsrqponmlkjihhggffffeeefffgghijklmnpqstvxy{}~~}||{{{{{z{{{{{||}}~~~}|{zyxwvutsrqponmlkjjihhggfffffffgghiijklmnpqstvxy{}~~}}|||{{{{{{{|||}}~~~}}|{zyxwvutsrqponmllkjjiihhhhghhhhhijjklmnoprstvxy{|~~~}}||||{{{{||||}}}~~~}}|{zyxwvutsrrqponmmlkkjjiiihhhhiiijjklmmnoqrstvwyz|~~~}}}||||||||||}}}~~~~}|{zyyxwvutsrqpponnmllkkkjjjjjjjjjkklmmnopqrsuvwyz|}~~}}}||||||||}}}}~~~~}|{{zyxwvuutsrqpponnmmllkkkjjjjjkkkllmnoopqrtuvwyz{}~~~}}}}}|||}}}}}~~~~}}|{zyyxwvuttsrqqpoonnmmlllllkkllllmmnnopqrstuvwyz{}~~~~}}}}}}}}}}}}~~~~}}|{{zyxwwvuttsrqqppoonnmmmlllllmmmmnnoppqrstuvwyz{|~~~~}}}}}}}}}}}~~~~}}|{{zyyxwvvuttsrrqppooonnnnmmmmnnnnooppqrsttuvxyz{|~~~~}}}}}}}}}~~~~~~}}||{zzyxwwvuutssrrqqppooonnnnnnnnoooppqrrstuvwxyz{|}~~~~~}}}}}}~~~~~~~}}|{{zyyxwwvuutssrrqqpppoooooooooopppqqrsstuvwxyz{|}~~~~~~~~~~~~~~~~~}}||{zzyxxwwvuuttssrrqqppppppoopppppqqrrsttuvwxyz{|}~~~~~~~~~~~~~~~~~}}||{{zzyxxwwvuuttssrrrqqqpppppppqqqrrrsstuuvwxyz{|}~~~~~~~~~~~~~~~~~}||{{zzyyxwwvvuuttssrrrrqqqqqqqqqqrrrssttuvvwxyz{|}}~~~~~~~~~~~~~~~~}}||{{zzyxxwwvvuutttssrrrrrrqqqrrrrsssttuuvwwxyz{||}~~~~~~~~~~~~}}||{{zzyyxxwwvvuuuttsssssrrrrrrrssssttuuvvwxxyz{{|}~~~~~~~~~~~}}}||{{zzyyxxwwvvvuuttttsssssssssssstttuuvwwxxyz{{|}~~~~~~~~~}}||{{zzyyyxxwwvvvuuuttttttststsuxtr}{ywtrt}vu|xx~|yxw{~|m{~|xvx|xw~~rs|uvp|uyyorl~|w~uov~x{vz|bdjuhtojemljn{ustuqw~~xx}l{xxtywtxuyw{lxy}yszs{~~t~u{xl|{vqpn|wtk}zv~yx{syec~ltitrpdjihv~utturrz|{w~l|x}w~tuzsyuwnx{|oxnzy~}sv||k}|vqqox~u~g}rw~zx}mzuehjyivttbjdituuurny~~wuyox~yl}xsy{|v|z}p{|xqunxux{pyns~y|nuq~{wzvf~oyz~y{wl~srdq}izlxxvaj`r|tvvuri|vqrsss|izu|{r~zu|t}zurtnsutyoziw~z}pyu~{wzvdzkw|}{wyf|nrjsi{l{~zak_t{twuvrbysmqtssiyv~zq{ut{v{{ypumunsv}ownt}{vzzw{vwdz~cw}|~s`xmopqkzk{ekcm~twvvvdlyin|lxzf|v{wqzytwzv|nyk{fuovu|wy|}~{pvoYouksZ|pypTxzzrpon~|lkIdqowgPy|ymivi}tn|{rvsc{ntkwu{|jyxwqwsrsgmvwvp~uyjiyppzc^pr[}uf|Zj_~}jrefbv{u|ef}us|xysy~tzswzsmgrvzwjgxixy~}yx}ijv{uvsslysq}{vyvjk}niu|jrpkgovwaqn[lzo}rr}hwtjx~satw{kksz|nwzwyuqqxuo|yfw|i|u|g|usoywlni~offmyqvvptf^j}so}imwylu}jilhh{vxmmm|du}r|lsuv||}xstvzmox}|d|zuihwovy{|gpfqxpdhluzn^{tivwx~Rvyy~ymgppdTh~~m|y}wxzu{x{nmyzvrm}z~suuivpk}Wt{}vwiir{wzux]}to}iwsufp~ir_s~~x`uvshxvZjr|er{wnlzh}on{}s~yj~|zuyzxm|wsxponzz}s`{qzp]lfqrlnsl{sssf{eitrywgshwlwozobntrn~rooxm~kmiv|hctcltk~pm}byopzpzsoqzs|pmq_kqi{{}jfvp{anskchkzvx~hmzttqrcmyjav]Pzn`hrzwgi{zyygmuy{ZcunĂ|}ysYirk^rxpvszbqt|~zwcTr|yzc{^rozs~^c_uwvb[tz~~{{^yewvfu|nijto^u}kWuv~vlqwmycnS~ziuv|||ms]vYxitbj~|yxox}hy{ow~}jz{ezvbbys~xmvofvius{z|r_uekf^kw{gsauz|ydtfylzytlvhhk}pk}ek|kv{}~t|z^s{zrYgcru~yinYwq]wz}uyopXvi}ts~fdxc}wx}}x~p|}|txky~uiy_opceusur`|}|xuou|~}wqz~wu{}tu`dwtsj{xZlhsdls|ksvyugjhrztsq~n||v~pxknz}|~v{og|wxzuiv{zbq|z~oz}ek{vyxlyxxlrr|s}huvmvfk~~y~uv}h~~zo~y}t{|wvzvsnk{~v{tygZpx~}usm}wvrq{rywywms|pJXQ\~y{p{pPEI@V7CgHW}xn|lɽ׸hlXfzpsRTS\{bmgo}vdawl̻f=`wtqdHr\ynyqwzwszPly\gp~skbnfUSXeqXp{lVmgISPLlgVrTr}wwRCuzmYHRMȽ~qwY{j^m^t٤bXL/^mjzL,66[>7~hhHxec{ysIJMK;,BM]h^NXcbl|vt{gp[:blx}wR@QTd|]U`eLTER{ƬºsS1B!Q}{ž̾o~tBOSFCfGkpMZv@TjSSXP\T\]czd[OI\5Ooujszk˅mxj\<>78WZjwauSaPMoln||Ş_1- !<=ĺbKQIUP>gfZHDcQYj_|ziRXRD_<8]B6]\^u~rpaeKXjq^wsmuxL`\[zmVvaPY8Vzuy^v`_qű٬ɨϩv^T6:Y_mνn~sLfjvJf[9UGFTGHjidzvkvy}}erZsOFcqkwlunmxVkp_zs|olnTaW>_Thgfrmczwkzs}}kuy{ũ^^I_eOxrÞxtxopNTZegA9,AT^BFIcGZtyxjttWSI[[>5:B1=3C%bLX_S39MDFhuX_[[]gngna\KpZQJCVf[NdU??\]YgiiMsv|zxyf~zkab~xl\yvqun}rãuoezyf{|ֿųs|hVkkkj[pJqv}oc{vey{qlfvs}uioahy[ldKGamW`blTin}{rrhqxd\Rgi`LflVh\rZx|rs|tyeu~n{koymRa_;'4A4AKePlwf\ttynp[xrxr~wpuh~ϫzzusmw~xmw}xvxgkwgH[]:/EUFQU^Jmxo}qcVeZD:OODL7H3MNL]U@>\[COeXZ[Q\frf{ts{u|kkbvzqƣ~˷pwUN^YMWaaXufn`OHYMXXTdQf]a`TABWJ;Wh`eZZdxoclbiPf]WVM`cNNbV?C\[\kiY\rh|txlivrfkrstvo{wwrbkwuwxnȽϴv{l~pvifvt~obtnm{ugnzsy||~vvvehscLThbgkiO_ggyzwup_di^Zmo[lXg`dvuuo}y{vpuocseonkvmRXdJ5>H<:AE5Zmwp}sy~teu|hl|~o}rasr°{wtz{~swyspx|uvxaKV_G9FSMRXP9\eh|{ilj^Wf`MbIPECQPVTGCY`KH_^\]XZdv`ogv{q}~zzlwrco{t|{e}xzhRSZUX][Igz~zi^ghRj_hl`mknfULZ]MHY`agbitfqmfu]lfdc_aTHK\TCIZZ[bfR^rg~v~}nktomwr{uo~vrz_\otqvwgoƿºѾ}~ulqzzzriskozw}vo}x{f_osXLX_]acUMmetyv}u|n`dd^auj]rcpecqw{przz{z|ydt]gl[lgkkpq[KOW@*/:7IOTWS_|q{xuszmmv`ol\ZZ\VS`nm\Ziqtqquwgdyewtdsy~}{o`amvg[aimnnixutg|thu{}o_\ecK=BKQUXYt~m|vxvtodakqhVZkw}{}mzydye[dghc^NCIY\JDMV\_][v}tyxx}t}z{tuyytyv~uwtt}~{ve[aniXV^dijfjƷzwxwtzzoyzrvvmp{}yqb^fpgVSWZ[XSYsint}tonov~pj|jp|olloop{{r|{udXiUYdLQ\dfe\NJOUG3.259<>Nqm{˾~wo{{}{o_\fpgYZagjjgt~xwxutwmv~zlj|elt]_fjgcWIGPZQB@EJNNLYsivzsyogffcajtvi_grxyyzwkg|hlzegqwxuma^gqi[Y]ab`]gv{~qhyfdt``ktvtk]Z_eZF>?BFHK[zxŻ}~yuy}oagrzzs{rodinmg]NIO[ZMHMRVXW^xwrwyvttusysu}o|jvfkqspi]V[fi[RTY]_`d|~ƹ¾|zyvyuq||ozl||imuzxrg\_hmbURTVWWWhufxt{vtwotnzuooqqtzqw~}ytiWUdOSaMNYac`WJGLQG6037<@GZyu~µ~qu}pswwqh[W^jk_XZ`dghq|}vtsqupks{}yobkr\hkVV\`_ZOEGRYQFDIOSV[pv||qnmlmw~|phmw}}vjm}jjzjdkppmd[Zcmi]WY]_`bp{z~|}|}{pa[laVj_T\ehe^RMPWSE;ESjxqwxsqroh_XY`fcZUTVY]hzuywwz~rjkquvtnd_io\^k]RRUTQKHLU[XPJJMPU`szo}|stzzrlv~llzobbggc]X[cig]VTUVYapwjs}{||~}rortsnf[SZaQO`ZPPUXVQLMRVQF>;=AIWlzuĽwty~}zzpjihb[TSY``XQOQTXap{su{{vuw|wmilqttqiacpgYgiZSSUTPLMT\`\URSVZcr|yy{yqr|kt|ndced`[Z_fjd\VUVX]hurhyyzvxzzxy~rhfhigbYOJTYGJZSIJOROJFGLPLB;:=CM]r~wľ{w{{ukigb]WW]df`ZXY\ajy~}}~xvvz~}vlefjmmib\]ifT[dYNMONLHHNW][URSV[cpyvux|~|womx|ljxre```^ZX\ciib\ZZ\akx|qx~yx}}vkeefd`YPKS\PIZ]QMOQPLHHLQPICCGNYh|ºyux~~{wthdb^XSRX_b_YWW[`jx}z{}ywwz}|ukeehkjga[_jeU]f[PNOOMKLS\b`\Z[^dm{yy|~{uor}zinxnb__^\XX]dhf_[YZ]doyum||~yzz~{{}~vj`\\\ZUMGJVUGM\XNKLLIDBEJMKFCEKUbt{y}|~ogeb]XWZafd_[[^ckw{}|xxz~}vlecehhe_Y]gdTWc[PKKKJHIPX^^[XX\akx|yxz||xsnr}xhmwoc^^][YY_eifa\[\_grztn}|}|~~wkb_^^[UNIOYSGQ\VNMNNKHGJOPLGEGMVev¼yy||}zzmgc_[WX]bd`[XY\cmy~vy{{z{}~yqhddfgd`[\eiZTabVNMMLJKOW^`]YXZ^fq}x|}|wsu~olxwkca`][[_ejid_\[^clurisw}zxzpfa_][VOIKUTFGVVNKOW[agnywytuzyyz{yqmnmhefhsynos[BL~aV_js~}zumlzvt{qdXRMJHA957?<-/>@;=GXjzyL$@BCCDJW^UP\c\TPONNNRX^ba^]^ahq{~wz~|z{{yzzxrjeceffdaahqlagsrjghknqwɾunheqŷxhZPLIE@:69B@20<>5/-./14;CKNNMMPU\fnlen~}uwxi]`f^[m}mc\YVRMJMX]SP^fb\[\\\\_eknnnqwɿƷ~jgmqt|ƻznhjk]NRVL>62.*(*.477445:ALX]Y[mxss|ytxvlaZVX^fmu}yrsvk\`id[VUVWX\ckqrqqrv}ȴxneYNFEN`u}iZPHA;89<=:50./28@E?;IUPLS]ejmmlijow{{|}{xqeUG<62127CYr|y{oha[USTXZYXWZ`htüsjbXOFAHV^`u|uk_SLGFHNPIBMYSMR[bfihfddipx|~{sswt~}}yqkhhhe`YSTWM;=CML@CPQJGGIKNRYagjhgghms|}v~vppt{ô||~~}{y}}wka[YXURPQV[`mxi]VPJC<78@A55BF@;;<<<=@EJJIGHLS^juwr{}}{memxvz}{~}|xhZWZOBFR[cn}z{yj\`bYNHECBADJOSRONNQW_hkdeu|vt|uputoihkr||~uollkkmrx|{wutuyxnpneZQJFEC@>AJXhwzrkd\WTTSOHCACGOZba[bolbcioswz|}vi\UTX^enx}{|~}ytpopt{|}}ysk_QE>964229I]fk{uqoqtuldmungjqvyzywtsw}~ypkifc]VQRYXNQcmor{{y{|swwxznd]XRKC:8<ITao~vwyyzzywvy|tntyztstuvurr{vkptk^VRPOQXdq}~xuwzpaad[MC=83017?IPXcqʾxooxynluzyvuvy{{wqmmnpqommszujmurha^[XUSTVVSMJIMUap|ywxyyxvv}z}yqke_WRONKE>7559AMWY\p|}vmfdegiiiltvy|vsqomlnqrohb]ZYZ]]UMWb`_jz||~~~}|{|zsnjhgifZPUYPHLT]fozƺ}vngededb_^dlh]bljc_^__`chnqrnkiijnssjbincZ[^_^][XX\ftzqjhhhhgfhq{xr{|{{yxx{~{yz|~tiffda^YURTZclszĿ|xtqrzvox~wokigedfjmnlgddehoutln{|trx~~ztnlmpponmlkhhu}zvxws~{utuuwy~}wtttrnie`YOKT^ZZk}½{tkgdb`_bfiiea__`djnibhsphjqvyzyvrprv{}}|zwqgbhk_X_eggiow~yy|wutrpoptvurpprx~phkk]V\aa^]_cinprw¶}wpkhginpibjtoilty}~~{yx{{xvpuxtnifdca\VSTZbjqw~~~z}~zrlgc`\TJA=>@CFJQ`s~{ps|ulkptvvtqnlntz~yv}w}}ywutpi_UNKHEB>=BMSOViux{xlc\XSMGBCKMECQZ[]dny||{uz~ty~}~~xqlkkkiea`eg\RX\UNKMPTZdq~yvtsojdacf\NNSL@853227?JT\ckt|{{v~|yxxxvphb__``_\\bkkacmne^ZVRNJJKMMKKOVap~}}{xvyzt{~vke`ZUPMLKHB<868>HT\^h¾zsonoppommszulpyvnjihgfgjnomgb^\\^ce`\huuu~|}~~}zx{vt}|tlifda_`bc_YRKGDDEF?59DC>CO\hs}~vmhffedbafotmjuzuokjhedehkkigefimsxujgnj\VY]aejot{}}~~}|~|zz{}{voida`ac`UNUVKDFJKLKJIJNXer~ºÿ}{~tx|tpoooorv|~~{xvvx|zt}yrswxwtpjebdhmpsw{}{vywsqqpprvz}{wspoorvvmekpg^^ab`]XRKHHKNOPQSUTT[lxx|xtpmkmpstrqqty|~ttx{}~}|zz}zuof][dkfhx~}xuttw|}wpwxqsx{{zwrlhhjlkieb]UKHOPECLSVX]fp{~~zqovuhdikhdbbehkkkmrz~zwvwz~wrzysv{~~|xtrtx{|{zxtmdbifYTXXRLGDBA?;659@IR[dqyzw{|vlb[WWWWXZao{{}|{{y~}}{uyxmotsnifdb`ZRJEBA@><:>HNIL]hkpx}ztmiggea]YZ`^SQ[]XUW\agnx}~|y}zvsqole\SNKIGD@=@GF<;EF?;;<=@EMXckrz~ywvtqnmpwullutmfdb_]]_cggfegkqz}|upmllkhdadieYW^[RJGDB@?BFHIGFGKR\htwz¾||}|zwux}rpxxpifdb`_`cdb]WTRRUZ_\Xaopov}wpjfeffdcbfnpffopha]ZVQNLMMJE@=<>BINLFKVVQU_js|ǽ~~}{|~}wpjfdcde`VU]\TRX^chknpt{}|||{xuv|wpv{vokjigggjmmjd`\ZZ[^ZPKSTJEHLOQRRSTZdp|¾{yxvvvy}}yurqqsvvngmqh`adffda]YX\binsx}|zzywtsxwxztqoliggjkjgdccfjormddjcVQRRQONKIHKQW[^_`aa`an~ļ}{{}~ur{|sorvxwvrmighkmmlkjhc_bnpkp|~|{yyy||xutuw{{tmrxphhlmmjfa[WVXZZXVTQLECLQKJU_eiow}{z~}yvvy|~~|{ytlfjof^afebaadhkmllou{|{|~~|yxxz~{y}qkmpqqqpnmnquvtpkf_ULMTPGJSWVUVX[^^][]bjs{ÿ~ww~|pntuqmjhhhgb][\_cgilr}}~zvsuxzzywurldbhg[W\^ZURPOOLGA=<=@BDEJTaedo{~xtqonjd\VSRSSRQRXbb\bnqnnptwz~wnkqtkipvvsqpomibYPJHEC@=98777:@GMPRUZajuzustttsqrx~yqu{vmgb^ZWUVYZYWWZ^dmw{xy}unkigda^^dha\bgb[WUSPNMOPNID?=<>CIJFJY`_er~|yxvutuxzxtokhghjlg_akicdlsx}yvutsqmkmrqfdkkc]ZXVTSTWXVQLHEDEGHB:>EB<=DJOSWZ^clx¼}zz|}{xtrstx}ypswnc`abba```birz|y|wwzvttrqpqsspjc^[YXYYQHKPH@@CEFEC@>=AGMSX]bgklryx{|{yupkggilmlllkieeovrr}yvtrppqtutpkgedfhic[]d_VUXZZXVQMJJLOQQQPPMIGPWSR[ejmrx}zy{~yribdha[ajnprw}|xutuvqhfmkb_bdedb_ZVVWZ\[ZYWSLGIPKBELNMLNRW\_`bfmu~}uu{xmkpqmjgfggfc_]^bfjlos}}v{xqruwvurnjhilnonmkhc[X^^SNRSOJFDCBA=8558>???>?CLNIMX]]_cipv}~{vpjgfffecbekledkmhdccdeglrx}|slnphadijhgghihd]WRNLJFB>?DF?=FJHFHKNRV\dlqtvx}~zy|~vosvqjfedcceimoonnorw}xvz{wsponmje_ZXWVURNMOSMEGLHB><<;;<@EHHHHILQYaggiuwsokgeefggdbbcejosqkowuopv|}vqmjgd_[[_aZU[_ZUSRQPOOQRQNIEBABDHGBBLPMNV_flrwz~}xurqrsslfjojdfknqrsssuy|zywtpmnrpfdiha[XVTRRRTUTPLIFFGIJF?BIGABGLORTUVY^gpw~zvzvkffhhihhggintxz{{|{xv{x}}xtsrpnnopnjd_[WVVVSJEJKC>?BCCB@><<@EJNQUY\]]cpvu{}ytolmnpponmlidenpkltz|~~|ywvwyyvrmjgggiha[_b\UVXYXWTPLJKNPQPPONKFFMPJJRX[]`ekrw{}}xriejkdbiopqrux{}|zxxz}~{z{yqikng_^`a`^[VRPPRTTSRQOKEDJKDBHLLKKMPTWXWY]bhnsw}|vrvwtqnmmmkhdaabdfghjpyyu{||}~}zwsnkkmnmligd^WUYWMIMMJFCABBA>;9:>BEHKOYbcbjuwvw{Ľ~wqnkifb_`ehdaippoptx{|xwxyzywuspidfje]_bb^[YYXXUPKHGGGFDBDJLFEMQONOSV[`gpx~~}{zwspqtsjgllfb````aejnpppqtw}}wprspjfca`^ZTOMLMMLJILRQIHNMGB?=<;@GIFEGILOSX_dghilotzzz~{upnmlkkmpqqonmnpty|zx}}yvtsrojd`]\ZXVRPRUQIJNLFBA@@?@CGIIHHHJNSY^]^gppry{xtrqrstsponnprutnjoqjgjosvy{}|xurnidbee_Y]`\WTTSRQRTUURNJHFFHJHAAHJEEJPUX[^`chpx~}~~yrrvslkmoppomkkmqvz|||}tqutmgdb`^\\^^\XTQONNPQLFHMJEEHKMNNMMMQV\`dhmuy}̹xvvuxz|~}}~|zwsnhdda`a^XUTMEEKLIMWco{xĿxne]XPD743+#!#%'()+,/4:AEIKMNLIIORLJNRRQRSX\_`bdgmsy޸uң|yuqmie`_ejffnvz}{}{slhffdcehjlklnrxn]QK\}zcUOOPRY_fkmnonkfbed]WX[XUSRSUVVVW[ahpx~¾u^PParcTKECBBA@>>@BCDDDHNPMMSWWVXZ]`chmsuvtsstwz|zutzxqnooolie`[YYYZYWVVSOLOUSOSY[ZXVSNF<2,.;Pgz~vpu{}}ƿ~pjeXKB>?GVlƾtqncZVUSPLGB=:99:98642.)',-'%*-.--/2577779=BEHJMS[]Z\egeefilpu|ľ}|}}tj_SIA=BFKQX`jqsuzofbdlwyqkhgefhloppopqsw|~|zufZTK<, /G^s}utnd^^__`aaabeinrsttsqmjmsrnrz~{xvutnc[ZWNKP[hx}}~{wtstttsrsw|zuy||}~zsmhc_dsµzurrlbac_XSQOMLLNPQQOMLMORVYVU[a`^bgloqsssux}}vmd^ZYZ_fqzqib\YXZ[ZYWWWXZ\]XSVYVQSX\`dhknsz»vnfa`cjs~ùytmf_WPIDBCCCBBBA?=>EIEFMSVWY]aehihijmqsttuy~}xy~ywuutssuwwvsoljiiif^UTRG=9778>;6;IZj{vkihaXRMIFCCCFGFECCDFHKKEBGHC?BEHJLMNOQVZ]_```^ZWX^^Z]ejlnquy{|yuqolifbaenz}unga\XSOMNU`ouja[VSOJFA=81,/1+&)-.--/1578991'&'(*1=N_p}{ujfhfa][[[\^aehiihhiknrtrosywtv{~zvsplf`[XYVPS]flu|y|ztnjfb^ZXVTPJECBFMXfrŽ||{sllmjfdbbbba^\\^`cdeeinojjopliggghilptvwxy|~{wwvsplgb_`ciov~~}{xsnkihgda__bc^\becbcfilotz~snmida`cir{ysokihea\XUTSUWVQPVYUTX]`cefffilqvy{|~~}z|}}}zuqnljfa\WUSLCBEEDGOXbku}zyywrmquqoqvy|~}wroke^WPKHGILMNXhrzyxxpiilkigghiigd`^\\[YWUUY[VTY]\ZZ\]_`cgjlkigffghjhb`dd^[[\[YVRMIGGJMQW^hs|}vppnd]\ZUPLKLNSX^gsytpnkga\WSQPPPLFGLJEFJLNOOONOQUZ]`befgeekqpnsz}~xyxsoorx~}~}{wtqojc[SKE@><:66?HLTap|xpnolebefdbabceedbbdfikmnpv||y~{uojfdba__acfiq|xtoh`[[WMFFC>95223468>HUdsľ{utuvwwwvvvx|~~|xtrvxsqv{||}~~{yxwurnkklia_b`[XXZ]bit}zvrnlkmopppooliioqmlquvvvwz|}}|{|~zupligfd]Y]aabju»|zwrmifefecbbehgcekkihiklnquy|}}}}~~zvpljheb^ZVSOLNV[\bnx~}{xutvxtnpsqmkklmosx~{xxvqlheb_[UPMLNQUZaky~{}{tppqpomkhffgikllllghmv¼som]UWUPQUX[[\begr|x_NNcujM;9CN]iryysqf_\YXVTQOMNPSX_hs}ͥ|wqjb[VSK?762-)&&(,18BLWeoxzoW=Ptjeeo|}cYkrXMOV_got|}vrpqsuwvsjaZVUV[agjnwzkwqgmu~mdmiYTXbgefjhb^]`cglqvz|}~¿cA'0Os{hWKD?<;;=?BB?>==>?AB?;>CB@@DIMOSW[ahpw}~{yx~vqxwqmkkjhc[OC7//8HXfpvobVOIEA@DM[ju~~zuplgb[TNGA;79@Ob{}yvtrnjfb_ZNA:71/3CDISYYY]bfhjkkkmquy|~|vqmmovxf[W\h{tzpaK0"0AKL_gdbillqx}wmjtl}zPHP^ql`9/+/;Plqbi~qaanwlP.,F^oxww{Źnj{}{{kVLRgfuu}zx{z`@*6^f]t̽ztrrttsplpwЯuYK\g=% )3>FHE?5*$"&*+,5EYdjv´bG:>TrȽujc[SJ@6-&"!$,8FWgs{~zqdXV`zоiVUjνjWKGJMKD7%5RcosiUC73:GWepw}wsqoqsvy}r\JGIJWqzrqu|̻{z|}_:  =f¬{`H>A;0-2:AGLPUZagoyzeQFKZgnwpedgnvzxqg\SKEA???EJNTZ`fjmopokdaohSDCK]us_OC94578515?C:1.15:@FLQTVX[dp}|rjddk{Ѷue`cglpt|ueP9)))!"@s¬ypieddhkotx~wi[L@60/4?Pbr}zsmjhjlib^hnghr|}xuuusmkrjSA86;FTakrw}zri_WPKF=7?OTXgz{vqmheba``aeks|ȾxmdahztdcyyocZSONNPRVZ`eimpqqrrpjdfg\POXbjjeittkjnqqponrzwof^VRQSXXROg~{xsme[OA3%2Ojx{wuy~yqieba]UOVtysokjklmmo¿r\MF@7,,9AJdǽvmd\TNHB=840-+)((),29CMQMB@O]]VU[dlrwy{{zyvsoje_ZWVY`jvymaUJI]yywzvsojgjy¶zriaZWWY[[bw}yvvuttrpmieb^[WOD>H\iq}}|{zzzzz{}}{yumgb_^irg_XQIA82/6Hfyrke^YTRQQPI@?HJDDLXepz|vvxnd]YXYY^rƺ|xqjc__h}Ƶs`RO`|yodYQKHGGHJKLKIHHKPSROLIIIJLORW\]YSW]SA@Scir}ytojd^YUQPRX`imry}wjdcba_\ZWUSTYe}{yxy|}yvz~{wtsrrx~tnknw|wysle\RF;0%&4Giƴztoic\UOID@<987789;<>?@BFNUYUSZ_XTW^fkosvwumb\bkjir¿yxz}yppzzz¼ypg^WRNNNNLNXeffqy|}unhc]YTNHB=72/.0481,&! %5Qs}xusoh_Z`hf_]_begjnsy}}x|~z{}~}zvsqprw~yy{}~ysnhb]YVVY]ce`URexqkd^XRLGB>:98:=@BFSemlowtllj_PFB@@@BFMU^hpx}|slourkhlptwy{|~xvutrojd_ZTNJQf{}{tmf_YUSTX_ht~zxvtroliebaaa`^\[[ZVMFGLH>;?DILKD:4:L^o|ume^WQKFK^t}~ztokijlnpqsuurmimttldbbcegjpw~zrt~|xqkd]WUYf{{ty}xrpqsttrokhededc`]ZWUTQNJFB=82+%'6N^fnx~}{xvutssux|xsw}ztsuvvspmifc`^\[[\[XQGBCFCDRi¼~zvspprux{|{wst}smrxtnmortwz{wspnnmp~~xsnhc^YTOJFC@><;::=FWlzpjhgfd`\WPJEBBFKPSQQXgooptxzyupic\XUUW]fq}~xsmhd`]ZXVTQKDAEJMWl¾|x~xuyzsonmkhd_ZVRQPRUY^bgkmoqtwzywv~xvyyqf`_abb_[VQLIGGFEA947==87;?DKTezxpnoqttu{}yuqmigefjq{{tmfb_^^^__]Z[fqtqqpmjlrslc]]al}zqkoqkcabdfggffghigaZWZ[ULGFGGFDB?;989;?CL_}zusssqnligedddeeedeirȿ|xuqnljiiiihc[TU]aadmxzuqmjgdb`_^^^__^\`oyrjc]YWUTQLD>?FJGCCGKPWbt~{wtqnmkigeb\UPQX]\Z]cimt}wpib\WRNJGDBABCDGIKNRYdyĹ|yvtsrsvxvpjkome_]]^^\ZWUTTVY]^^^bnx{vsqrrqqqrtxzzxzysmhea^ZVRPNLHA:9@ILKJJKQ^gghrſ~{yxyz|}|||ywvvwz~zusrokf`ZTOHA;510135436ANRRV\chjkmtzw|~wokjkkklkjgc_[WTQNKIGEDDFHIIGFJWgoqs}¼~{z|~|toomdZZ`b_]^bfjllkijr|vsqoljgc_\XURQQRRPLJQ]fgjqz~|{zxsldaeiha[YZ\]]\YTOSbu{vqkfa\YWUTTUUVVUTRQS\m}yuqmhb\WSQQSW[^^_gs{wqt{tuzxnfcdgikkhaVKDDFC;30148;=>=;9:?L^q¾{usvz~zsosvqhb``a``_^_`cglqv{~{yvuttrpmmrxyz|tmf`ZUQMJHFFGILORSPMO^y~zvrnjfcfs{slmswvrsx||}~}|zwtplhd`][ZXSJ>3,-,'#,48;ANZ^\\bpþxpnrw|}}{xuqnjfb_[XTQPOOQTX]cipv{}zvroljhfaYRPVYUONQUY[\]_eo~zuromlie]SLKPTRPRXahlq{}ytplieb`][ZYWVUUTTTTSND<>Oeolfdegijlmoqstsrqrwÿ}xsmga[VRNLKLORTUUXcr}¿{wvvvuuuvvvslc\\_\TLHFDA>;741.,+++-037:=@CHQ_tĿ}{yxxz}~vjacloja\_foxytokgffhkoqqps|vja[XWUTSSSSUVXYZ[]cm|~vplklllllljgb`ft}yuqnmmnquz|z{{zxupkfa\WQLE>70)#(3@LTXZ_k}ſ{uuy{uoklnnlf_ZZ\[TMIIMRX_fmtz}tnmong\TOMMNPRSTQNMS\`^[\^ckw}{}xrlgc^[XVVWY]bir{~||zxwtqnkheb_\YVSPMJHFDB?;722;KZbehlqvz|zvtvľ}{xuqlcYPKJJF>746=BL[m~|zzyxxyzzzwtsx~yurolhb[SMLPTUROMLKNV_db_^`cfhhiiiihhhfda^[[^gt{wspmkheb_\YVSQNJFCBJZkv||uqrrkbZVUTSQOLJHGEB=8315:<:51/03;GYozof``egb[VUUVVWXY[]`cgkorvxz||{{yxvussrrqnihmx{ywvutrqolieb^\YWVUUUWY\_acdb`_fv~~~~{{~uoljigda]XSNID@<864221110.*&%*28966>Ocqx}zurpnljfb^ZUQNKIHHILOTZair{|vrolhea]YUQNLJHFB<759@C@>?BEGIJMS^m}ÿ|zxwwwxyyxwz~xrlf`[UPKFA=97557:>ACCCGNROJKS^ehjlpuy|}}}{wwxvoha]\[[[[ZZYXWVVVUTU[j}|yyzzxrjdcec\RKGEDCA@>=<<<=>????>=?DN^q¼{vrnljigd_ZY]a_VMKQ[ejkkmpuz}}{yvtromkifc`\WV[htyxtqonmmkhd]WRSW\][XY\aglqtvwz~vstvuqnmosvwuuzyututmbXSRTTTSPLGC>950,)%#! !!#&*.38=ADEFJVm{rmkkklkkie_XRPSY[ZWUW\bipv|}ytplifb\UKC>?BDB?>?ABCGOX\YSPQV`n~~|{yyyz|~vmfb^[XUQLGB=841/./1355423:EMQRV]fnu{ü}xtpkgc_\[ZYYXVRMIHMSWWUTUXar}yvtsrssrlbUKJPUQG=88;AFKORSTSPMJHJPZfs|Ƽ}jYMKPUbsx~t`VQNJOUZ\aehhghgfn|}o^MCAGTdpsk^QKLQ[iwxiXI=89?IT_jt|~{wtu|ɽϴտ}slgffnx~{zwodZV\l}qmnu~~~|unjihe_YRMF:'-@IA/+E[`Q:)%4NmðvaSPT]is{~}yskb_\WUTUX[_dhjmqy}pkkq|ooy}hTD9314559=BGMRWZYSI@9659FYnxpjc^]dryldbfnu{}uwĺ~{yvrmdZPHB<2$  #4CP[gsyvlbYTTZfx|~weXRSZckpqnh_ULD=9667:>DIPV\fv~wsqsvz}wph]PC:7773./28?FJLKKMTaqwoloyrjd`[SLIKR\fov|{wrnjgdba`__^_]ZZYYXXVTQKC90)%#$(2@NZdmuz{wqh_XUUX_gqzypga[XWWX[^bfjosvwwtrszǼ{tpoqv|}qe]UI9+  "(09AKYk~~ysmklqw{zxvw{~|wqkea_^`cglqty|vkbZSMHD?<::;=AFLSY`ekoqsu|~wsrtvz~xsmjgffhknsy{tldZQH?6.&  +",6>CGMZk~½xpkige_VKC<8544579=<<<=>>><:;?FOWZ[\^bhnswy{~}zwvttssrpmkknsyzwrqtwvtqszzuqmkiiikmprrqmjilpqnieddeda\TNIHHGB:3,(&%%$##! ,;FKNOQSTUUWZalzƿ|vqlhdbaabdfgikmry½}xtqnmkhd^XUUWTMC==BIMMKJIIIIHHIMS\gr{}|zyxwutpkd_^aefc_]^`dgjlmmmmlkjifb\TLGEFIJHECCEFFFGLS[^][[]`eimpsuwy{|}}}|{ywwz~{yvspor|}rh_\ZXQH?9766652.' #(,28@JU`ly|upmorsnga^]]^_``abdegjlosvz~|ywtrpnmmmnorvz~zwx~}|~zvuwy}{upmmqturolklpty~|vrssph^VPLIFC@<840,)'%$##" #%$##&.940//010//39?A?;7557:?GQ\fotvvvuvw{~zvrnid_ZUQMKIHHIJKJGEDHOX]`cgnw~{xuroljhfc_YSMHFHKLKHEBABCEKTcs~{zxwusoibZSOLLLJE=51026;?CFIKLMMLKIHGIMT^jwľ{snnpqplihjouz~}~zvtrqponlie`ZTPQV]aa]YTQNLJIHGHILOSW\_ab`][[^djpv}{wtqonmnpqrqnic_`gouupljlqy|rf\TPPRSRMG@<9889:<>@BEGJMOQSUUTSTXbtý{qic_\YVSPMKIHHIJLNOPPOOQW`ipstrollpw~|{z{{{zwtqlhb]XRMHD@=;::==@FNUYZYZ\_bfjov|yvrnid_ZVQMJGDB@@@@BEHMQUXYXY_k{|yvtrnjd]XVX[^]ZWTSRRSW\`b`\WTUZcnyú}{{{zwsnihjmnkgcaacefghggfdcb`_^^^][YUPJEBACCA;5029BJNOOOPQSTVWY[]_acdeeeeegkr|}ywtssstuutqoory|rida_ZRIA;765556789::98779?FLPQOMLMQXblw|urplga][[\]^__^^]]\\[[[[[\\]_adhlptwwvsqqssqonoppoljhggggfedb`^\[ZZZ[]_behihe`\[`is|{smhc_ZTNHA;4.)$ "$'*06>DJPV\`aachq}¿xropomhc^ZXWWWXYYZ[]^`bdfilpsvyz{|ztqnmllllllkhd^YWX\__\YWVWXZ[\]afnx{xusrrrsrqokhfegkorqmgccdgknpppnkhea]YUQMIFB>;864310.,,07BMRRPOQX`gkmmmmoqux|ÿwoifddfilpsvwvutv}}umcZQJGGHFA91+(&&&'''()*,.024579;?DKUanyyvwyzwpf_[\agmppnljjkmoqrstttttuuwxyyvrmihjnpnjfeehlorsrokfcbdhklkheddfhknqsuvw{yuv}vi^UQNMLJHEA<73.)%!"'.4:?BDDGMWbmu}zrmjjjkllkhd_ZWX[`eggedegkpu{}{ywvuspmhc^\\`fkljc\WW[bffb^ZXY]cjs}}||~}yvtsrstuvvtqonrx~zuplhffhkmlgaZTOLJIHGFDCA?=;:852//2;HT\acehlpswy|~~yuqnkhfc`\YWY^flpolikqzzupligedb_YQG=522210-)&$$&*.38;>@AAAACGMU_js{ž}|}~}ysonnquy}~ysomnpqpmieccdefeb`_aeiie`][[]_acdefghiklnpqrrrqonnpv}zxvutsqnkijnty}ytssqmf^XSOKE?9533431,'#!!#',29?EKPTWXY[al{ýxohc`_^]\[ZZYYZ[]`cgkotxz||{yxxy{}|xtokgc_\XVTRQQSUX]afijjhffffc_]`ht~||~~voieba`^][ZXVTRNJD>8312467631//026>JZiu|~ztnhc]XTPMLKKMQU[aglptw}ztoid_[WSOKE>6/++.01/..04:?CEDBA@BIR\fov}~|}{wsnjfb^[YXWXYZ[[YUPJFFJQWZWRMKMRX`gmsw{}~~}{yvtstw|}zwtqoljgd`][\blv~vokifaYOF?;864322123469<>@A@??AEMV`iry{rmnrsph^VQOPTW\`cfhjlmoppqqqqqqppppomkiginv}vpkjjkljf`[VTSTUXZ]`cgjnruwwvrmhgjq{zy{{ysmgb_\YWTPLHC?:63/,*('()+.27<@DEEEGNYgt}¼{vsrrrojc\VRONNOQTW[_diotx|}}}xspnkhb[TOLJJKKIGDABEILLIEBA@ABDGKQXblv~|{{zywtqmhc^YTOJFDA@???><9768>FMQTUWY\_cglsy}~}zxxzyspoppnjfa][[[\\]_eny|tkc]YVRMGB@?@ABCDEDDCBA?>;988:>EO\kzſ~umgdeggfb_]\\\[[\_ciorrqnlkklmnppqqqomjfa]]_eknmjfb`^^^^^_`abccb`__afkqtutsrrux}~~zvqiaZUSQOJE@;86433222111123568;=>>><BIS_lx{ungb_][WRJB;51/0147;>@@?>>AGOUXYZZ\`dhlptx~}xtokheca```abcddb_\Z]dp|yvtrokf`ZWVWXVQH@;:=CIMONKHEBAABDIOWalw{uokijlkhd_\[\^adgilnpstvwvtqmhc`_`bcb^WPHDDIR[cffedcbccccddefghjklmnopsw|~|{{{{{zxutuy}}yof^ZWUPIA:411134420-)&&(-38;<<<<>@DGKOQUZcp}wspnnmlkihfdb`_^^^_acglqx~zy}yusstuuvusqnjfb^ZWTRQQRSUXYZXURPPTY^`abdiowyrnjfb^YUPKFB>;8531.+(&&)08@EHHGHMV_fjlnptz{wtrqqppomljhfdba__^__abdefgimv{wtrponmmlkjhd^WPKIKMNMJGEDFIMPSUWWWXY[`foz~zxwxyxtokiiklkihhjmqtwyyyxuspliea]YVSPNMLLKJHEA?@CJPV[_bdeeeegilpsvxz{||~|wsqpqrsuwyyxvronpuz~~}}~ysmf_XTRRQMG>60+)((()*+,-/258<@CFHJJJKLOV_kxztpnnnnljhgfghjlmnoppqqqrsuwxyywtplkmquwvsqonmlkhfdcdfhhea]YVVWZ^bfikkjfa\Z\blv}zqjfdba`]ZUQKE?94/*'$##$',18>DIMORV[adedeir}zxvsokfbaabddb_[XVVWZ^chlprstuy~xrlfa_`cfiigd_YURRU[`cc_[WTRQRTW[ahouz~ÿ}|{||}|{ywvwz~~xqliknswxwsokgeba_][YVSPLIFCA=:52//4;730/./0248?IUajprsttuvwxz|}wqkf`\XVUUVY]bhmqtvvvx{|vqlhc_YSLD=8568;;:86545579=BGKNNNMMPU]gr}þ~}~}yvuuuvvvutrpnlkihggffedb_][ZYYXWVVVVVTQOOQUZ`ejnqtuvvutromkiikou~~zwtqmiffipy|vroke\SJB=:8777788876420/039@GLQSUVX[`gr|vof^YVVX[^adfghiiiihhhhhijkmoqsuvvvwz~ztnida_^]]]]^_`begjmpqpnjea`aejosw}{yy|}xrlgca^[YURNIEA<95310001245654336=;:87544446:@GP[fpyzwvxywrkb[VTVZ`fkmmljhgghilnpqstuuusqmiecdiotwwtqonnoooomjfa]ZY[^cgjkkkklnpqqqppsyyttuvuqjc[TNKHGFFEDCA?<:7420///0246789:=CMXdow}Ž~}}|zwsmg`ZVTUX[^`bcegkosvxz}|xuqnjfb^YUQOPQRRQMJGFGHGEB?>?AFLS\dnv~¾|xvttuuurme]UMHDBAABDGIJJIFC@?BGLQTTTUW\agmsx{~|ywusqolifb_]]^`cfkrz}ysle^YVROKGDBBBCBAAAABCDEEEEDEGKQZdo{Ž{tnlkkjfa\XVWY]bhmquxyywtpmjijlnoomid^YTQQTZbjprqokhfedcbba````acehknqsvxyz{}zqjecba_ZTNIEB?<9520014677520/026;@FJMOPPQV^jyƿ|wspnljhec`^\ZZZ\^bfkqvz~~}{||{{}}ytoid^YUQNMMMOQSUUTSRRTVWUQLHGKTanzxsoljhgeb`]YVQLF@93/-/38>BDDB?=>CKU^ejlmnpsvz}zwsokgc`]\[[\]_`bdfkr{~|zywurojd\TNJHIKKJGEDEGJNPQPNJGEFIOV^fmt{~umgdehkmlkiijknqsuuuusqoligdb`__^^^^\XSMFBAENXafhfb_]]^aeimpsuvvuutsrrtx~}ytrqtx{}~~~ztmhdba]XPG?830..../02368:=?@@@>=<<>CHOTZ_gq~yspppqu{~wvxshjmi___\W[bghkmkccfiku{jZK<88>N]ivl_TG8569;?CGMS\fp}pzv}}ut{|yeXTV^jt{~}wncXLC:68689=GUft}~vnd]ZZ_houyzz{|ub\dws]ORar{zmZH<:?HQX]``a_\]\\[ZZYYYZ[\`emzĺ|xsmheehnxxfWRYk~vdWQU_kvytmf`ZVRNKIIJPXcmtwvqi`XQLLPYetvmhio{¾tkhlvp`WUZeq|xsonorvz}q`QF>;<>@CEFHJKKJJGC?:62.*'$!"'0;IYizŹÿwmd\VRPPTZ`is}zpe]WTTVY^cfijlnrvz|}xqlgdb`^]ZXUSPOORX^fmuz}|vmaUKECFO]ovompv}tcTJFHNW`fhf`XNB7,#!&+169::989GMQSTTUVX[^adinvĻ}ywvvvxyyxuqjc]Z[airx|~~yrjaXOGC?<71( -:GRZ^_^\YVTSSUY^emu{|vsrrrqmhb\XVVWY[]`cfimqux|~~|zxwusnf]SJDBDHLPSVY^ciouy}~|zywvvwy{~}~}}|yx{zwpz~woif`_bdba`YTL?0'%5GWekh]M?8DJMLB5( -FbxvdSNYnþúvjdekt~|pdYPKIJMRTVY\agpy{xx}|jcfr|l[MECEIOTY]__]YSMIHJMRX^dhkmkgc]YVUX]ep|tkgjszgYTXevvj^SJD?<;;<>?@A@@@BEKRY`eimpstrnf^VQOPU\eox|rmou}obXSSXbnz|vuv{~wmaTG<3-+*+-/13567787789;;<;:97569ANayȿzvsqmhaYRLHGILRZbjrz~zy}xqibZSLFDEJT`n{xme_]]_acehkotx||yvuuy~}{zxvtrpnmlmmoqtvyz{{xussv}xi\RJD@>=>@BDFHHHGFEEEFGILNPSVY_gpxwlb[TOKGEDEHOYdp{|yvvvwxyyyywurnic^XUSSV[afiifa]ZY[^__]XRMIEBA@ABDGKPU[`eimptw{ļ{wvvvtqkd\TLFB@?@BEIMRVY[\[YVTTW[`dgiikmquz~{tnhdbbekqy}{yvsokgc`_`dinswxyz~}{yxxy{}}xrkfbcfjnnlf]TJB:51.-,-.02479<@CIPXalwĿ}zwronnqtwz{zxtnhc^[ZZ[\]^`behlortvx{}unfbmrx}}oe^VJHJM\dil~yppzvlgjo|xuwwvrpWR^stiZNE?<=@CHLOPQPLD:/$&8L^hi`RC;>Md̾u\[khODJ[n{~wph]UPNOQTW[^acfhnv}tmmt}uaM=43;GWenuupg\QHA<731/.16:AJVdu~z~zwngegmx~snmqx{qf\SLFA=976757;=>=:5/("&2D\qrbTMRbvxsw¾yustw}wj]PGCBFMU_jt~¾{sprys]J?;ALYemnh_RF<4.,)&&&')+,,--/3;FWi}ÿź~zvpnmmqx}pbVNLQ\ix}qdYRNNQTW\_aa`_^_bgmtwxskc[VQNMMNQUZ`gnu|{wronnpsx~{xwx||ma[\co|xndZPF;2,()-3:BIOTXZ[YVRMHDBBDHNSVXXYZ^fsyncZRNLLNQTY\`dhlptwz|~}zvqnkjlnpomhc_]]`cefeb]XRNJIIJOV\dltz}vl^PC931148>FR_n~|rib\XUSRRSTUVWVUTROMJIGEB?<60,*+19AHKKIGEEJPYerý}vqpqsttroje`[YXXZ^chnty}~zuolkloswz}}zupid_\ZXUPIA92-*)*,/37<=AHQ]jvytqnkhe`]XRMIFDDFINTZbhnsvxyz}{tk`VOLNT[ckpsutsrpopqtx~¼zvssuy|zqib\WTPMJGDA>;740.,+-05;AFIKLNSYbksx|~|zxwvvvy|ĺ{upkea][YZ\aflqw{|xutstuvwvurnida__`bba^ZVROMKIGC=70)#$,5?IT^fow¿~{wtsrqrqpmjfa[UOJEB@ACGLS[bhlnonmmosx|~{xusqppqrrrqpnkihfeehlqx}xutuwz}~|yvsstuvwuqiaWNF?93.*&"!#%')+.26;@FMV`kv}{yxwvusrqonnopqtwy|~}wqlheegkpuz|zzz|~~xtrsvz|yvtrpppprstuvvvutsrqqrsuutqlf`ZWTSSSSQOKF@92+$"(.39=CIQYbkrx}ļ}xrnkjlotyzqha\YWWVTPKE=5/+)+/38:;975210//..../25:@HQYbjpvz}~~}}}|ywwxz|}zxvtsrrqqqqomjfcbceinruvwwwwyz{|}~{rkea__```^ZVROORXakt{}{zz{|~}yrkc]XVVWWTOG>5-(%%&(*+---,*(&$###%).4=FPZep|¿}||||{{zyyy{}~~|{yxxy{}~|zywtrokgc^YUQOOPSW[]][WRONPV^hqy~{ywwx{~~|yxy||zxxxyz|}~~}|{{{}zslfbabdfggd`ZTNIDA?>>@BEHKNPQRRSV[bjt|¿|yvtqnjgc_[WUTTW[`fjnonmlmpty}ztmf_XSOLJJJLMNMKGA;6336:?BCB>953259?GOXbjqw|}wqkgdba``^][YXVUTSRSUY_fmqtutrqpqrtvxz{|~zslf`[WUTUWY[]^][ZY[_fpz|ywurplgb]XUTUXZ\[XTNIDBCEINSWYXVRNJGFGKR[eq}}yusrrstvwwwwvvtrpnkgd`]ZXWVUUUUVX[^`aa`_^_acfikkkifc_[WSPONOQUY^bgjmoqty~{yy|}zwqjd^ZXWXYZZYWSNHB=:88;>BEGHHFDBABFNZhx}uolkkkmmmkifb]YURONOQUY_ejnqrssstvy{|}}}}}~~~|zxtqnkheb`][YXWVVUTRNJEA>=>BFJMNNNOQU[bku|yuqlgb\WSPMKJIHGFDA>;99:=CIPTVURNLKLPV]emu}}wsnjgda_]\[[ZYXVTRRTYakw}{wrmifeeeda\WQMJIJLNQSTTTRQONNPT\fsþzrljjmpsspke_[YZ]bglpsuutqmhc^ZURPNMLLKJHHHJNSY]__^\[]`dhloonkhd`]ZYXY[^chov}wpkihhfc_ZVRPONNMMKIGDA>;98779:<>?@?>==?FQasypjedegjllkhe`]ZXWX[^chmquxyxvspmmoruxzzyxwwwyzzzxurolkjjjkjihfeddcb`][XVVX\`dgijmpv|~~vng`\YWUUTRPMJEA=964223568:;<=?BHNV\`a`][[]clwzuqmjhhhhhhggfffghikou}ytpljihhhgeb^[XWWWWVTQONNOQSVXY[]_adhmsz|xsnkhhjnrvwvsniecbdhmquvurmg`YSOLKIKHMVVXYYYZ`ekrwz{{ollnb]bc`\[VVRNLRVY_ksyʼ~wpjedbbb_]\\YXXZan|mdr~Ʒ~{}wrlfb__aehjg_RA- ):JW[TF5'"'5EU^^VI<429GZpƳ̽Ǻpu}iYNIGHKOSVWVTQMKJMS`q}l]SQYhz~k[PMS]kx{rh_XSNLJIHGFDCDJNU`lzymhp~rnnt|{uu{qhdcglquwvsojdYQKD=71+(').6@JNKA3'#*:Oblj_OB=ALYjyɾnaYUTX]dkqvy{||||{|~|~zsszsh]UOJGECA?=;:99864347=EOZhr{~yutuzumikr}tg]WTUY_gouy~~ysle^WRLHB<61+&"!#+6DTdr|{tmf`[YXY]ckt|þtkfehnt{}zxxxz}zvtsuwz|}}|yvsomkhd`[VQMJIHGHHJMNQSVXYYXVSOLJKMR[ft»~yurruz~ysmgb\WSPMMMNQSVY\]]ZWSOKIIKNRVX[]_ciou{}wog^WQMLMPV\ckry~yvsrrstvwxz|}{zz|vj_ULD=83.+(%" !%*/38<@ELU`mz¾~{vqjb[TMIEDDGKPV\cintz{rib[UQNLKLLMNNNMMLLNPSVYZ\^aekqy~~~ztmf_XQJD@<98776666679=BJR\dlsx|~ysld]WRNKKKLNPRUWXZ\_bgmrx}zvrmhb]WRMIECCDGJNRUVVVUTTTSSRRRQQRRTW[^chntzľ}zvqmhd_[XURPNLJHFEDDEHKOQSTTSRRRSUWZ\^```^\ZXWVWX\`ekrw}|wroljhfecb`^]\]_cjqz{vpje`]ZYWVVUSRPNLJIHHHILORV[`ekpv}Ŀ|vqmkjhfd`]YURPONNOPQRSSSRQPONNOQSVX[]^_abegknprrrqpnmlkkjhgd`\XTPLIHGHIKLNNNNMLKLNSX_fmtz}zwsojd_XSMGB>952/-+)(('''(*-28?FLSX]bglrx~}wqmifca^\ZXURNKHFEEGIMQUZ]`cegkot{~yupkfc`___``_]ZXVTTUWZ^bgkpuz¼|xtpljhgggggfedba`_^]\ZYXVUTRPNLIGEDBAA@?>====>?ACDEEEEFHJMOSVY[^`acdfhkotz~}}}~{wsoid^YUQONMMLKIGDB@>===?ADHLQV\bgmsz|wsoljgea^ZWTQPOOPQSTUVWXXWWVVUUVWY[]_`aabcegjnqsuwxyzz{{||{{zxwvuttstuvwyzz{zzzz{}}yvsplhd_[WSOLHEA>:62.+(&%#""!!!""$%')-16;BJQYahov}~{ywusrpnljihggghjlosvz}~{xurolifc`^]\\\[ZYWVUUWZ^ciouz}|zywvuuuvwwwutqoljhfdca_][YVTQOLIGDB@?=<<<<<==>@BDGILNQSW[_dhmrw|{xvsqponnnnopsvz{uoic]XSOKFB=96310/...-,,+,,.149?FMU]elsy}zwtrpnljhgedddegikmopqrsssrrrrrrssstttttsssstuvwxxyyyyyxwwvvuuttttttuuvwxz|~~~}}||||}}zvspmjgd`[WRNIEB?<96420.,++**++,./0257;?DKR[dmv}zxvtrponmllmnoprtwy{~}zxtqnligeca_^\ZXVTSRRRRRRSUWZ^chnsx|~~}}}}|{zwuromkihfedb`^\ZWUSQONMMMMNOPQRSUWY\^adfhkmqtx}}{yvtrpnmlkkklmoou~{xwwxz|~|xtpkgda```aa`]YWUUTQKD=879=CIOUY]__^[XURRTX_it~}|{zz~|xuux|wqnpt{vnkmni_UNLOU\elqrpjbXOHEEHPYcmv|}zvtwth`]^ckt~~wqmkklnquy|xleeihb]\`ejllid]WPKHFGILPSUWXXXYYYZ\`eilpy|vroptzsjly}ysnhc_[XWVVWXYYWTPJE?;88;AIQVWXaouojjmopqsw}}}zpifhox|tonqx}xusstvxz{zxsme]UOKIILQW^dfc[Zch_WVX\ahpx~}{tnjjjloqsuwwrp{}~~}{zyyz||wrnkihgdb_ZVRPPRWZYROU^ux~h\[[_dhdovu{~Ƚ}sf_]i|zutw|}shadn}qcfynQEQeol_SQV`iosrs~oZPHKXmuqty{wj`Z\hzl`_iy|qoyÿwwz}_Tczj]UOKMPRQQPOKD>=ESgu{k^YY[]ZVSWdu|hTJLWcnx~{pe\YZ_kpdbnqp|ol{yia^`flqtvyyl`WUXZWWfs`NA::@GOSTSSW]afhkh\QIFFIOWan||eQXu~qjn|Ƽ}xzvsojghlrxxiVB1.8Oi~zvmV9.=Wjy|x~}n^VSRQQUcsyrkhhjlmrv}yĿ~xu{}zwuuyvu}hQJ`~rbULKNRUUVUU\eihkyymiovmba^YY]````elu{}{vf\drttz}xvukls}~nsvw~wlc]YVQI>@MLLWZ]s}}~z|z||qdct~{yvxusiabhnqqppu{|{n^PHEFHIIF@76BKDFVTT]^VKQZSPXbjpxsmxna_aflruwyzret}ysjaULMOKJKOSVUh}z}|ogghgeb^ZUTWXQYjfhklonebiv}upr~ttlveNV_Y[``affceiostsqprvqky{x}uicrukihc`_bfgd\QJah]cku|~{ttwuqu~{unihjldlxlfk}zhflsx{|{xvvy~}or~uv|xigrmhkkgdiprsvnr{~~~zx||unhefhie]YSTa]\`^iwqjkjhgffedb``bfi`W`YT]cdb_\WLGWaTJHMWcoy{yxyvvxyzwnagbQNMMR[_ZSMIWf_XWVW\epqkttjorspmjijklmmmopjiuywxsz{~~{xwxz}|}~}{yxz|wldadlt{zq{w|zvuzw~{yvuuvx|z||~{zyxurokhfgjnlbgi^ahlj`X`e]^cda[UPORXXRKX^ROOPSX[[XWVTOVc\PMOU^b_jmbdggfc`agljea^_fq{~{z}}zy|}qs~{uwz|yvqjjmmnnnmkjhgecbcfjjmwwz}z}xuuy~~vomsxyzwsyry~ywwyzxtnhddhmldkmbcffcbb`]dia^`bcegijkhdq{rqrrsuvvwx{|}||xsrnmpsrj_clgbdda\WVWVPWa[[^\VNIHKRY]\WOHL]bZVVS]lfdefghhfdccdffd`\Z]enpem|u|~{z|~{{{wy~v}}~~|}|wuuvrq~{}tuxyzyvuxywtrpmgesvmkkkkjhfda\W_ga[[\]^_[T]b[]`cc_XSSVZ]_^]^`_csymeq{uv|y~y{|pozsgfgknokc\WVX\__]YSNUd`W``]chjifdbccddeedbdgijjfixvho{z{~yustuvwwwxz{vpy}w|vt}|z{x}{z{}{|xyzyxxyzyqipqjmqrokgdbbdhibWVcfbdgd_hj^YZ]afgd]VQNNQTVVWX[[UVgj]^eiijmoppomiggjpw|{t||vojimswz|yldw~meflrx|{vpkgffffffgjlgbmqnsvrqz{tt|~vrquz{u}sorxtkp}{tqpqtz}tw{sqrrrqppppomjijkiiuyqnz}vuvz~~{xvwyyy{~|xwz}~yrlgeeghhcZ\ii]]lmghhea][^dgf`XPLMSZaed^\c]\eb_chkkjihgfdcaacglmhfrrknuzyss~~zvvy|{vx~|wxsu{|zux~{wvx{yv~{{xtqsx~xnmy~xwxxus|urqrttsokgeddccdefc_cimlfhmnlhfegjllid_\]afknmlxujl}wuyrptnquwxvrlfddefe`ZURUZ[Zenhb^]gle`adhkljhgghjkjfbmulffinu{{sjmwvsvz{yxy{yu}~yvvzztx|~}~}xv}{xx{~|ro{|rny}}ztpoqsspkfdegjmni`dophbdkqroib^^`cfea\ZZ[WS_jggkjc`jrmhiiiikotxzywqmy|~|yxxy|}{uldlxtoprsomtyoc`bfjmonmmlgagqnjnqqmjjntxwnkrvst{{sw|vw{yrmnrw|}|yuqxzu|zw|~{yxvtqooqtvxwtqx}sz}|~~vv|}}~xqooqrqomklkfeqtjekuqhgkpqpke_]_dimlf\U]b\Z^ekonf`dkf`_adhloqqkekpkkostrqpqsuwwsln}{z~x|{}}rpt{|z~sprw|{tomotwuor|~zyvx~|}}}~~xqmmry~pksqu{~wronoppooqpkfnuqpqmkrvroruupjffjpuyysntyqiipy{ru~}||zvwsuusvvmkmoonlihilmhfmoe_`behknqpkccjjdcgmqromv|rigiouzzxrmjkotwvrnv}tzytsw~~~}|vux~~xu~{}{{}{xvvy}~xwyxz~ztpqsvxvpltzqiimtxvqrupiimppnlklmkipxrkigeegkquwumd`kwxvwwus~~~yvx{~}wqtuolkloqqponnmjfjttnkjhly~zyywspprvyywrnlnsy~}umt{x{}}~}yvvy{z~~y|}~}xx{yzz{{y{xvx}|vswwv|~{xx|~zxwrow{vvusrsvxvqqurkintxwskgoxtnnoppoljhhjmpqoifoyvlfnwww{~}zwuuvxxvtstx|~y~}~xx~z~zwvwyzyxwwxww}|qt|yuv{{tporuxyxvvwxwuz~|~{{}|tq{{uux}~wrw~{xyzywuvx{|wolrxxy}}vsz|vvzysoptz}wv|~|~||~~~zyxz~|xutropy}xtuvrou{vrsuvvusqoligghmrvvrkkqy{rkkowxqmmosvxxxxwtr{wx|{~z|ut~{|y{|toorw|{ss{z|~{vtv{{v}~ww~z{{}~usw~}vqsx~yspqtvspu|}xspt~}vstuvwyzzwrkfdgnv}~uil}squ||zz{}|~{yxww|ut}usty~~xqkiikmpsvywpioy~{uqqruz~voklqw|~{trz|wwyz}{|~zx{~{}{z~~zy{~|y~z}~~~zrmlpwyy~}}||}}}}zxzyv}}|~~}}{yyz}~ytoorwzywyyvz~ytsux{}~|yurqomlklptwtsxzqnw~{trty~zvtvy}~}xv~|y}~y~||}~|x{}~ztonptxzyuwxlmy~{xvvx{~}zwvwvst}zy{}}|xw{~||z{~~zy}xv~||}{urrv{~tnz{vx}{yz{|{xussuvu|wsx~}~|vz}{~~|y}ywzwvwxyyyy{|zrlw{vwyzz{{|{yvsqqrttrojkwxplny~upoqw}~{wrov|z{{|~|{~}}~{yy|wvz{|yxz}}zxupms~zuwzvtuy{rs~yx{xsx}~~z{}{ustwz{{{{{yutz{y||yy{~}yussv{~~zrjiv{z{|{{zrquz{v}~{x||~yuux|~wv~|sv~}wtstwz|}|ztlgnxywy||ytrx|qkkpxztx|usv||zxuv~||yy{{}y|{xxz~zwzv}{vuw|umqzzuuz{y~{vuw{}{z|zut}~{vtw}vvzz~zsprw~~vrzxpoy{xutuvxz|~~{tnqwuprw{~}zvx~vnmrz}wsu{}{|~}}}{vw|}v}{||}~{x|xx~zvtuzysw|wnlpxxonw~|wuvy|~}|zuuvy|~zwwwx{~x}}{wvwzyz||{vux}zv|}yxxzzwwy{}}~|y|}tprv{wsw|{yz}}|xvx~~{z{~ytuz}||}}yv{}z{{xvz}wtrsw{zuyuljmu}|tmkt|wvvuu|~zxwx{|wuw|~~}}~}xz{y|zwy|~}z{xsqruz}skmy|vru|z{}}zxxy{~}xx|xwy}zv~~~~|yvu}~zz|}~|{~}uruz~xu}}tpqty~|zvsszzsqx}}~zxx|yu~}z~ztv{wy}~ww|~{yz|~~}yuv}xusqrv{zuomr~{pmw~~zvuvx{}~ws|~~yzyy}yz~yuy}}~{|~|yy~zux}zyz{|{zyxy{}}zvxws{}zwvwzzw}}}yuv|z}ywyysx|xx{yrov|}~|wtx}}|zxwvwyyyxxyzzy{zx}zwy{vtuy}{|{twzxz|y}|}~{sqw|vu}|yz~ztporw}}xtuz}|wvy~ztqrv|zxzz}z{}y~vloogacfl}ud^bm}jZ\nq[]m|zqb[`q~pd^dt|h`eovyqmqk\_smVZttim}|dXWZajsyzvnd[WW\bhlsytnoopv}qjiov}ztmfa`dfaZW[aioru}rhgr}h]cvwdbjmnt~t`TYkwwxwg]]es|wwod]Z]hxyk__ccdmiX[qndbhr}oeaeoz~xqmhb`hy~k^[`qthbeqse__bglong^Y[bdbbceffl{~oc\\blu}|uollmpole\UQRYbjpqoow|qifiste]`inoszzyzqaUSXessia_hswspqsu{|vtrrkb^akz{mc^_epz~{xy{~}ui^[^dkt|~tr|}wpic`bhs|~uh`_adhmswywrlhddhkljechok_X_o{{oilqphbabegggjqtof_afknmha_frvqmmutkkpqleacqypiebbbcdhlqu{|tppppkc`eox}{qidabejllic]XX`muqdZ\itvuw}xt}vlhhilnoomjfccdfhigden~~}zvph`Z\iuyyz{xvtqnifddeecaclwyqnu~uu|zpfcfknomlnrz|wzyoheflu~zpjo{}z||ywuttttsstwyz{~}xuttsqnje``fjlpuutv{xpq||vrnlmrxti^XY^emswvqjhowwssuy}|spv{tkiovtkedgjmmkhgjpyzutw}~|xzwppw|zskd_`ipokjloqstsqnkikpw}wlgo|wiaagpx{zsja\Z\`eilljffnwvlgjtzyxxywtokjmsxzvmaY\ehdbdjpttqpv|~|{~~|xwwxzzyulc_bgfbbekorqnjjnplfdgmsusontyth_]`fmpplgcbbbabhqtmd`gqsnjilpsvwunf^YZ_hqvul_W]mwsg``gqz}|}wpmnrvy{xpfaemrqkd`aelruupkebcfjmkfadmtqjd``dnwxpf`_aehlopplggloia]^cjquvri`\ahlkjigdbgs|zphegmty|{yxy}|rouwpjegp{}wnhecegkmliebbejmlheeiospibadiptusnid`__adhklihlokccjnligikllkiiikllje`_hsvqlknsvwz{x{||~|vngektwsmjjklljkornfabflpplgelvwoe^]`fmqrplhc_`fqwti_[bmtspnmkjjjjigdbbekqtrj`^k{wlikry}}}~yvwy|}vlfkx{qjhilprtsplighijjhc_akwyqf]]er{ypf_^agntwvqia]bknlhfgimpqoiefikjknqqmgfny}yrnmosvxyyz|~ytw}zvpmqz~{uqnlkjkkllkjjkkkjknnkjmmiddiqw{ysjc^^afkoqqmhdeilmnomjiloqpnjhhjostqja]cpyzvroopv}tqsy}{|zpgfmvywtrpmjjpwysh`]bjsxyslhlpnfa`ekruurmga^ajtxvohccjrtqmlmoppolhdbbfmtxvofbhv|qhfkuytruzysqu{ymfkzsiefkry}}ypha`chorpkfhpwti`^fs{zuoiedeinsvwtmebekligjnqsrokjmomheglqtrpqw~{rifipy~{yyz{}yuz{ywvtsv~ulfeglqtusojgfghkqvtlgkqsmgfipw||wof`^_elrwwrjddkswtngeglrwywrmihimppmihny}zsmkmuzuuy~~}{xtoklt|}wqmkiiox~{qhccgmswvrmlqvtj`]`isz}zrh`^eqyysleacmz{slighkoqqnjhhkptsokjow}|unlnu}{xwx|~|ywx{|tkir~}rkijnswz{ysmgdehloomknvztg`doxzvqlhginswywsme`bktvqmkloponptuqjfhntwvqmoyxniiow~|xvy|{}{{~~|{||zvqqw}tlhfhlqvxvrlgdfnx~xkcentrooruxyvpidbdhmrvxvof`dr||skghlquwwtqmlmoqqnjfgrtjho}{sqs{{}}~}wohho|ypifju{oeabhr{xmegnrnifhnu{|xpf`cmuxwtplgflw}{upmlllmnonmmnprqpqssstwwrmms}}uppt{}}{wwz}vqrw}}tlims{zqhcaemtyyunlputmimsvwwwtpljkouz|{vmd`epxxvsrpnmqx~|rhbdlv~~wrtyztlils||uqqy|z~{|~xvw{}z|ypjilrx}~yrkedkxzngjtzysoqu{~yphcchpy|sgbjzujehow~umgfjqwywros|~sijs}xttw}{z|{x||x|y}{yyz||{wst|}rkip{xqkikpxzqmottnijpywmhksxyvtsrqps{}rkhjouz||xtpnopsx~~xrsz}ytqt{xqnpv{z~~~}~~{~|{~}{|~|zz{{{~}wrprw}~ulfeiqz{uqruvvwwvtuwyyxwwwxyyxvrmjlu~~wronou{qkjnu||wv{wniltzqou{vx~~}}}x{|wwz~|yz{pjjnu~{phfn|~rihq~}xvvxyywspopty}~woimy~tnmpv|ysnmotxyxvv||qmszwwzxtyyy~}{zy~}vwyy}}|||{zxutx~rknx}wqnos{vlioy}{vsuy~~xsruyyyyz{yurrz{snmoty~|vqopwyppw~~|{}{ursx~{uw~}{{~wyyx{{{}{}{|}{{~|xy||ywx|~xqlknu}yqnry}xuvz}}zwvx{~~ztmimx|snmtvmjlt~vru{}yusw}|tpu|xy|{~xvy}usz}{|yrrv}}xy}~zursw|ypjlvwpnt~~zy{}~|xtqpty}vqqw{snpv~|slknv}yvy~zuv|~zxy}vrv~xx}}wx~}xw||x}}zy|~vw~usy{usuy~zy|~uqu}}xtrsx~slnv|}zyz}|zz|}zvtuy}~|{~uomqw~yrmmtwps||y{|tppv~tqzzuw~}xx|}uv}yy|yz~usw~}zxxy~|vw}{xy}wojkr|slo{wppu}}yvvxz{zwtu{{qmpyzsopt|{vv{}ztrv}{ttz~|z}{vvz}}~{|xssxzy~{{zy~|vuzxstx{{|upqw~xpnt}}vqqyyrquz~|yxy{}}{zyz|~ytsv|ypklqzzwy|~~~~}|}{vtx}yy||vv|urv{ss{xtx}|~}z{yuw}zusuy~~}ywz~}}|{{{}|tnnv~ywxz|~{vtvz~z{vnlowwnkq~{pls||~|xwx{ysuyvx~~yxz~{uy{y{wru~{|}zrqv~{usv}rpv}zvssv|vmmz{rqu||zyzz{zxvuyvnoy|uqrv~|rnr|}yzzx{}zz}vtw|{xy~zx}zutw}|ts||yz|wuz|rpxupqwurv}~zxy}{utx}~xst|yutw{}~}||}|{{|{z|~}yx{yrnnt|{srx|y{}}yrpwytu~|spt}|y}~yx|~urwyux~}~}wuzxqorx{xy}~}~}zy{vmks}xuw|xpos{z{}vrrw~~tmmw|spu}~zvuw}xuy|uty}uqry~{y}}zy|xpqz~wsv~}tqt||tpqzzrry~~~xsqrx~rmsumnvwrquz}z{|sqw}xttw~}sns{~}~~|z{zsrv}wst{{ux~~vqqw~spxystz{vvz||{qqyyssv}ztuz~}{y|{y{}~||}~~|z{|rnqx|ywvx|xw}}yz~zqlms~xop{}usw~~||}{xxxqs|xssx{{yvxwtx~zxz|utw}~|{|~}xw}~uoos{~vtx}xvy~}|}}xtszyrqvwqqv}{|}vqrwzqouwqr{}vux}||}}|}|wvz|spqx}|xsszzww|wsu|xpou~snq|ypow{xx{~~{yz~}wtyzsqu~{ursw|}}yy}~|{{~yqot~{z{}{xx|~~tpqx~tos~vpt~{xy|zss}ytuz}xvx|~~ysu~}xuw|}sor{}|}~~{yz~{z~voot|ysrw|sqw~zvtuzunq~}tsw~||}~~}{yy~wqt~~wtu{|srx}~{{}{x|ytsw~|xwz~xx~~|uqrw}tou}wux~}|}{uru~snr~xpot|xsu{}zz}~xux}zuu{~wvw{~~~}{|}vqqw~wuz||~ysqvxst|~tpr{|x{}ywz}tpt}~vrt}{yz|}~~}}}~|{}}vsv~yroqw|xy|~~{z|xpntzvw|}uru{~~{vuyxqpxwru~}ywy}yuxwsu|vqqv~}}}yz}xwx}yqnt~~|z{~zttxxqpu~xqpuwru}}xtsw}{qpyvpqyxtux~~|}vu{~yuv{}trw~}|}zsqu}|urt|zuy|~|tppv{qnwxqqwzvuy}}zzzqovysqt{{uuz~}{|}z{}~|y{wqqv~|yxy}}z}|z}vpns}~sqx{uv{~}~|xw||spu|tqt{|z|{wvzzsszzuuzxrsw}~{z{|~~xuw}zrnpv{vv{}yx{~|wsu~yrrxxssx}xstz{rqwxrs|xvy}}}}{{|vtx{soqx}|~~}~wqqxyuuywrty~ypns}unoy{qnt}zz{~~{z{xtw~uqt|~xttx}zy||{{~|sot~}{|}~zy|~~vopvuoq{xpq{}yvvzyqpyyrrw|wuw{~||yrs{}wstz}snq{~}~}{|~zzwpou}ytsx}tszzvtv}snt}tsx~}}~|yxztpu{tqu}xqsz}||yz}zww~|tqsy~yvw|zwz}}wqpszwpq{zuv{}~}wss{xoox~snqx}uuz{z}zuv|{utzyvw{~|yz~~upot}~vtx{{~~}xqouvprz|rmqzzx{|vux|ros}|tpt~~zyz|~~}~~|{}|urwxqnqy|y{~}{|unoxxvx~{sqv~~~xstz}snq|{rpx~~~zwwy{utzyqpvyqory~|~~yx|}yvw{{qmq{|z{}|usw~{ros{zros}yrs{yusv}|rowxoowyttw}}~usy~xtty}spt}}{|~zrorz}uprz{tv}|}}toot~|pmuzqpu~{vvy}~zz}rnt{sqsz}vuy~~||~{z}|yz{ros{}yxx|~z||z{xpmqzupt~vtw~~}}|xvxvopyvppvyy}~yuv}vrt|~wsu}~ursy~{{|}}wuxxpnqyzuw}}yy}{usvxrrywrsx~~wrtzyqpwvpr|xvx}}}|yz~{tsw~zroqx|{}~~}|}wqpvzuuxztsx~|snqzxonuunq||z|~|{{ztt|yrqwzutv{|xy~|yy{vopx{{|~|yz}~}{qnqyzqnttot~{wuw|vor}wqrx{wwy}}|~xqs|}vrtz}sos|~~~}~}z{xpou}zuty~vuzyusv}snsvsv}}}~|ywyuos}|tprzyrry~|~}xx{zvv}vqsxzwx|}z|~||yrpryyrqy}wwz~~xss{{qovvppvxvy{z|{uuz{tsxzvvz~|xx|vpot}~wtx||~}}wqovwqrz}soqz|z}{wvy{rot~|squ~{z{}~}~zz}{trwwqpt|{z|~{z|{qns~{vvzxsty{usv}wpovvps|~|ywx}wsvvprz~upqw}}{xzzvvyuopy}{z}yvx}~upry|tps||tsyzutw~}rowzqouzuux}~}}vtx}wstz{sqv~~~~}~~}}zros{{tqt}zvx~|}|tpqxypoyzrqw{wx{|z}zqpwyrqu}zuv{~}~|z{~}yx{wpqv{xxz|{~}zz~{snowxqr{yuv{}}}yvw~ypovyqpt}{y|{ww{xst{xsszxrsx~|{|~~xuw~yqnqxzuv|~zy|{urvyrqwyrrw~}~xstyzqpvwppyyvw{~}|yy}|uru}|soqw}{}}|}yrot|vtw~}utw~vopw{qns~xoox}z{}}{{~}usx}tps|}wtuy~zx{~zwx|{rns}}zz|~{z{}}wons}vopx|rpwzvux~~snsvpryzwx{~}|wqt}}urtz{rot}~~~{y{woot~ytty~vuzytru|snr~wtwldo|{vrhYQT]eowzrgfltveYZ`n|i^VTU]diifdabk|ta_qkjrUQdzmkpxzwojkq}wkb]`nx]OUmuaWZfvxdX]unbnjcmwkdaadgigb^]_fmppmihmyq_W\k}vjb`fqwmlrwvoeYST]iruqjgkx|f]dxqgl}}pkmqssqpu~vmfcdgmtzs^TVe{wtrpmhdbglqx{~|tlebdjt|}pilxxh]]hy}o\RTcvpbZY`m{wmcaiywoklrzvdWTZdklida^^_chmonmowtbURZizyjcejmlhddfhkliecfn|yla^gzqaZ[`hnsw}{spquyysi^X\iyzsmhdbcegilr||x|rf`_elqqoov{tmfbaguyqrysaUU_mx~}yvxxvxzulc_dryronnlgbadhjiffiov{{tj^VTXbmuwqidfsxrolid^ZZ`jsuodZV[frxvm`VSWbnzwj_XZetxnc\\bktyytmjksxlcciqurleelz{rmlmke^YY`iqvsj_XXanz}vnlrzslgfjosrkbZX]fpwyume__frzsmgbadkt|{y}~|th]WZcox|yqifkx{nedjswtldbivytrrqlfbchlopmidbbfnw~}uoms|zyywsnlntxwod[X]fqz|yqhbdn~~ztmf`_ckvyw~~}}sg^^fpwyuojhlvtuqzxnltnYNPXdp~tcSFAFQcv}}zvjhseav~[IWz]GDTpylhlwpd][]frylbaiz|idjz{skc^]eszoutlx}vpkhhhfaXNFFQaq|ncgvlYPQW_hs}vg^\`eiid^[[crwjkxs]SYkq_SQW`jopnmov}||yskfeir|{uokgfhqxqry|tont{wnhe`YOHHQbx~mcep~}{|xsrxwi_YVTW]epy{rorw~uf[X_n~zwwwvph]QMQ]kwzuleemy{snllmlieenwlffjoqokfeglrrqu~ob\^hzuomps{|z{zsh]XZco{ulihd_YVW[aeggedhp{~spwpd`fr{nbYV\grxyy||wxwrmiecbbcejsrcWRUar}oijpuyxsmhfeffd_ZX[agf_VQUbt}ywsmf`^_aa^YUSUZ`dgfb^[]dpz~zrkkszqh_ZZ]aceiq}~xqjecccdb``dn|}vtvyyti]TSZgv~}wqkhfeda_]\^aeea^^eqxpifeffecaadgiifa\]er{y~|pgbaa`\XY_kz{~|xrlgffhhgc`agq|}{~|zwsoje`]^dlvtjeeilmiebcgkjd^[_k{{rmkkjheb`afkoojc^`iwxst|}tmiggd`]`gqz~}~}{vpiedehihhjpz}ww}|ywuqkc^\`hs~}~hURNPPRN[msx|x~rltxeWRU_lz~qf[PGHN^p|}{zuvyop}vTK^{pSI]j_brt]NJNZiwyqhcft}jcj|~ria[Z`pockn`XWXTOJHIKLJIKS_n{zmktlYPQYeq{wi]WZcmqlcYW^o{jadqzePHSlm`_ivzqmhc^YWZalwyqic__gvwnot|}wsu{~slea\VPLMSatymm{tqsutrru~~{|~~}vsxuf[VTSUZalwwmhjpw~rbUQUctvjbckv{qgaafnw}}|}tkfeecbdl{{mccipsqkecdgjmnrynccqyl_VRV`mwz|{z}zz|rjfgozsib^\\[YWWZ`gnqtv|wtwwkccl{yuph_XW^iw~~{uleachjidchv|vqkfgn|o`TOPW_dc]WUW]a`^]`fnstpicafr{qnorrmf^WSTVYZXVX^hprmcZX`n|wrt||uple^YY^gouy{~xsqpomid``befefkt~vpljjjhedfozjaaeknmf]WVY\\YWY`ku|}yspt~zz{|zvoiecbba`__aflomhcbhswrpolhc^]_dkqv{xnjkorrmgcbehifbags~~~{upljjlmlhfgo|xlgglpqmga^^`aaacglqtuvuvz}{|yofa`dikjgfhnstogbclz{sty{yrjc`bfknqsw}|smlnppmjggilkhefmw~|ysnjhikllmqzx`PE>AJLUZ]^r~sr{nYLJTdv}toj_QHDL]nz~uty}zuumTRhnTOf~nozxw}yiVHFRg|znfekvyqsscWWh}q{bQWozbPJILLLKLPVZZWTT[gvzuyxbSLQ]m}{sje_]agmomga_dotjdcju~ufZWbvpfgsvh```cda__eq|rkggilqxxkgnxzusv{~yqkbZVQPRYfxur{tb[^ht{~}{|uv}w`QLPYcjmot|te`cks|{pcYUZh{ujdelv|}xmaYV[epzypjeccdhmu~qgbchmpnh`[\blwrgfoyvtph[PKP]lx|pfdly{j_[Z\\[YY\cjnnnnt~~z{vh_^gu}umfb``cit|{~}wnfcbdfgghktznedk{yhXLIMTZ]]\^adffeefhikmnnlkkq{ukhhkkg`XSTX[\[[]ckqsoh`\]eq}xsuwqoppmhcacjs~vqpolgb_`cgjjhhjr}|wqnlmlkjlsqbZZ`fif_XUW\bgijjknrwyyxv{xrsx|}yphbacca][]cluywqjglw~smlmmliecdgmrvz~{|tjdcegheb_^`cfilnrxxojknqqmhefinpnlmsxnms|{rkhimqsttuy~yz~~yustutrsuy}{qllostqkd`afmsuuvzxuxulioy{m_WX_glkgb_`cfhhggjpy|tpt{qnr{{pf_\^addcbdhnrrqqu~yz}|~~{wz{okmsy{xrkfdehklkkms{xnhjs~thbagqy}xmaWTX_fhfdbceghhhhknruwwtplms}wjbagovyvnd\WX]dikgcadkqvx{~smmqvzzwqlhgiloqqonoqqokhiov|~ype\Y]eovvodYSU^gmlga]]afkllkknruuqjdbgq|tf\[cnxzsgZRQXblpoidbflruuvytmovzneaemuyxtolnpplfbbhqy|xnbYW^jtxsh[RRZenqmga_`cfhhggjnrtsnhdelu{zrf]Z`jtzyqeZSSYbjonkggjnsuwz~}~xonu~|qiegmtwwtpprtsnhegmsxwria\\bktxul`VRWajoojebacgknonliikmonlkntyzuj`Z\dox{vmaXV[epvuneadlu{}}}|}||smpwypihls{~|uolmqrqnlnsx{yrh_[]eqy|vk^VXboxwod\[`iqvwsokklopnkhjpy~}uh_]dp{zocZW]gryzumgfjpvxyz}}|qmr}}qhejs|{tporsrnjinu|~{rg``gqy|wmbZZboy}yoe``dkqttrpopqrrpooquz{wogcdlv~|rf]Z^gqy{wqkimsz|z~}~|{{vopwtkgipx~|wsrsttrqqtvxwtokhimtz}zqf^[`kv|{tlecfmuz{xrmkmruwussv{~{tjbagr|wk`\alxymeelw|yz{}y}yolpzxnhiq|umjntyzxvuvxywrlgfjqzwka]co|tibbir{~ztpopsutrprv}~vkefny|qf`ahs~zpihmw~}zy|{|x}wnnu{ohhoz~tnmqvxwusux|}yslikqx}~yqhcdlwukeejry}}zvrqrtwyywvwxywrnlov~{qgabit~{rkjq{}{}~yy}wz{}~z}{wzysrwwmhjr||toptx{{ywvwwwvspoqv|ypfbeo{}rieiq|~umjmt{|xwxzzvpkkpyymecit|pihp{|}{|zw{{{~~vv}|tprz|qijr~{pkmt||ywxzzxsomou||sieitymeelwwolnsy}}{xxz||xrnou|~vmhhnwtkimw}|~zuw{y~~z{}uvzw||trwvmkox{qmou{~~{yyz{zxuttvy||zvqmmr{yngflvxpmou|yuuwyyvttx~wnggmwtjho|xsu{}}ytw{vx~}zw{zwz}xwzyojlt~smnu~ytrtx{|{zyy{}|ysmknw~riflxtljnwzwvyzyurrv}zpjjq{zoijsyttyzwy~vuz~|uu}}vw~{{xsu|~rklu|pjlv|vstwz{zxwy|}wqnpw}sjhmw|rlkqy~zxxzzyxwy|~~yustypilplknmgjs|zutss~wuvwxrprxwpnjd^`flprribj|sXVnthv~lkytmmquwtokjro\TWdt}ma^dq{hYTXfzy~oa\`ntcctl_dw|sqstof[VV]gpsojd``enxxdVYixgbgt|smjjjgecceinsvz}r`]msjpvhadlx}vtw{|wl^STavshbbdis|ug`_eklhb[[gwynhiouwri`]cqwicgsxojijmr|xqjeadm~~nfgmu{}{z~womqvy{|sd_gyxsnic^[]cjnmf\SQWbp{~ywzre^]_cgnvxl_WUZdmrpg^VSXdt|mb^bkx|rg[RR[l~|~zrkffimpqs{uqtz~zpd\\es}tg^Z_l}sd\]gxzwxtf]Z]cimpprwyy}{}{{yskc`clvyfXTZfrvrg\X\fs}}tia^`eijihhlsz~~~teZW\fnrrnjgghjllljhffghiikq|rcZYap|n_WX^gnpnkikpxwlhhiifccfmu|wrrw}znaWTYcmstpia]_emtxwuux~zldfoy~{qf_^cjqttqnnquyzz{~veZX^iu|}yutw~{ttz{l_WW\djlkhfeglqvz|~}vu{yk_\`glmjfdflu~wkgku}qhfjtvtzvposy~|la]`hqvupjfglsx{||}~~}xnggnywj^WX_iprme^[^emsttstx{}ztons}vkfiq|teYUYcmrqlfehnu{zzywz~~qeafpy|wk`Y[cmtvtpnoqssqnlmrx~zqf`ajv}{qdYUYcmttog`\]agkllklostqkgglt{|yqha_clu{yqdYTW`jqsplijntzriglu~|rf][_fmonkiikllkihilorsqkd__foxzuj^VV\fosrld^[^dkoplheehjihfgkpttpg^YY_isxul_VU]iuzwpifjrz|phhnw|rg^]bjqspjebcfijjihjmpqng_[\doxzth\VXbnwxqg\WYajrtpjeceghfcachotsnd\X[dovwqf\W[fs|~zrlknt{|pijr||oc]_fpvvrkfdegihgefimqqng`]`hrwvnbZW]gszzsi_Z[aiorplhfghihfegjnpoke_]`gqxzula[\eq{~wqnou}zpkls|tia`entwtokhhikmmmlllmmljgeflsyysh^Z]fqy|wne^]cltxvohddhlnmkjlpsspib^`gqz~zpe^`jwuljnxzoijr|viabit|}wnfcekqturomnopolgegmv}|ui`\bmy~uj`]ajt{|wpighlnnlklptwvpidchpx|zsjbaht}sllqz}~~}tmlr|wkcbju~ulffiosutrpprsspljkov{{vmdadmx}sh`_enw}|vojilorsrqpqqrqnljlqw|}ypgbdmzzqlnv~{|{~|~}~vrrv~{ofchr|zqjhjpvzzwrnmorsrpoqv{~zrhbbiuxlcafr}wleeksy{xtrqstspljkow~}tjdfp}vlinx~z{~|{~|z|~wqpu~}pgelxwlefmu}}xspprssroorx~{rhdhr}|pfbeo{vlfgmtyzwustuvtrpoquy||wqjhlvvmimv}yy~|z~~y|zyzxxstyzohip|zohglt{~~zurqrtvvvuvwzzwrmknv~ticdlw{pihmu}}xrppsvwvttvy}~{tlgir~ynilwvrt}|y}{v{z{{wz|xwzukgkuukgjs}~unkmsz}}zwwy{zvojjpz{pfdjv}qgfkv|vrrtvvussty}~yqklr}yoilv{srw|}wu{}}wywu{yx~~yuuy}qjjrxlehq}}tmlpuz|zxwy|~{vomow~ulgir}|qhgmv|wtsuwyyyxxxyzywsqsxyoilu{srx}}}xy~uu~|wy{wzzuvzx}~zyz}yqmowynghpzvpnpv||xvwyywtrsyxnggnzsigmy|smnry~~{yxz{{xtppu}qjlvwppx~}~z{{uv~ww}}z|}usxzwz}|~~}~{smls~~qihp{tlkpx~yxy{{xspqvzpijr~{oghq}}toosx|~|{yz|}|yustyyqmpzyqpv~}~{tt|xw|}|{wy|tt|{tu|||~}{{}{soqxxnjmv~uonrx~}{zz{zxvux||uporzzpjls}}vrruz}ywwyzzyxy}yqnr{xrs{|xz~|~yvyxvz}~{{}xw|yux~}~~|xuuxunlqy{tpqw~{vuvy||{{|~ztppu}vomr{~vppt{{yyz{zywwz|urt{wsu|zz}|y||wx~~}zwz|wx}|zz}}zvtw|xqos{xqory|xvwy{{zz{~{vstx~}vqqu}ytrty}}{zzz{{||}~{wvy~yuuz{z}|yz|xy}z{~{y|ywz}{|}}zyy|{vstyxsrty}ywxz|}}}}}}}|zxxy||vrrv}ytsv|zwvx{~~~~~{xvy}yvw}|yz~}{~zy}~{{~||{xz~{{}~|||}}}}~|xttx~zusu{{wvw{~~}}~~}{ywx{}xttx}wttx~{xwx{}~~~~~|yy{}yxz~~zz}|z{|{~}|}}zz|yz~}}}}}||}~zwvy~zvuw{~zxxz|~~~~~~~}|{z{|}zwwz~{wuw{~{yxz|~}}}}|{|~|yx{|z{~}~~~{{~|{}}~~{{~{z|~~}}}}~~~|zyz|zwvx{~zxy{~~|{{||}}}~}zxxz~{wvy}|yxy|~~~~}{{|~|zz}}zz}~~~}|}|{}~{{}|{|~}{|}~}zyy{{xwy}|xwy}~}|||||||~}{yy|~zwxz~}zyy{}~~~}|}~~|{||{|~~~||~}|}}~~~|{}~{{|~||}}~~~}{z{}}zyy{}zyy|~~}}|}}~~~}{{{}{yxz}~{zz|~}||}~~}{|~|{}~}~~|}~||~~~~}}~|{|~~}|}~~~}}||}}{yy{~}zyz}~|{|}~~}{{{}~{yy|{yy{~~}}}}~~~~}|~}|}~}~~~~|}~~~~}|}}||~}|{}~~~}|||}}zz{~}{z{}}|||}~~}|||~~{z{}}{z{|~}}}~~~~~~||~~}~~~}~}}~}}~~}~}|}~||}~~~}}}~~|{|}~|z{|~}|}~~}}}}~~~|{{|~~|{{}}||}~~}~~}}~~}}~~~}~~}~}|}~}|}~}}~~~~}||}~~|{|~~|{|~~~}}}}~~|{|~|{{}}|}}~~~~~~~}}~~}~~}~~~~}}}}|}~~~~~~~~~}|}~}||}~}||}~~~~~~~~}}}~}|{|~~}}~~~~~~~}~~~~~~~}~~}~~}}}~~~~~~~~}~~~}||~~}|}~~}}~~}}}}~}||~}|}~~~~~~~~~~~~~~}~~}}~~}}~~~}~~~}}}~}|}~~~~~~~}}~}}}~~}}~~~~~~~~~~~~~~}}~~~~~~~~~~~}}~}}}~~~~~~~~~~~}}}~~}}~~~~~~~~~~~~~}~~~~~~~~~}}~~}}~~~~~~~~~~}}~~}~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/MUS/m56.bin b/applications/SprPoP/assets/packed/MUS/m56.bin new file mode 100644 index 0000000..5268478 --- /dev/null +++ b/applications/SprPoP/assets/packed/MUS/m56.bin @@ -0,0 +1 @@ +{zxtssru{|urqppqrsttuwxxz|}~{yywvxzz}~uhhzqlovyn|m|~}}}~~~|thcm|znggjmpsvy{|}}~~~~}}}~~~|{{{{|}~~{umhm|~{{}}yogl~~nffiloruwy{}~}yrjddmy{tnkjkmorsuwxyzz{|}~{vv}yogceny}zrjecccegjloruxz}~|yvpjecfmw~ytqooppqstuwxyz{}~}~woihjpw|~}ytoliggghiklnprtvxy{|}}~~~~~}}}|zywurnjfddgnv}~{xvuttuuvwxyz|}~}{|~~xsooptx|}zwsqnmlllmnopqstvwxyyz{{{{{{zzywvspmjfdehmu|~{xwvuuvvwxyz{|~}{{}zuqpptw{~~|yvspnmmmmnopqrtuvwxyyzzz{zzzyxwuspmjgeeglsz|zxwvvvvwxyz{|~}{z{~{vrqqsw{~~{xurponmmnopqrstuvwxyyzzzzzyyxwusqnjgeefkqx~~{yxwwwwxxyz{|}}zyz}}xtrrtwz~}zwtrpoonnopqrstuvwxxyyzzzyyyxwusqnkheefjpw}}{yxxwxxxyz{|}~}zyy{~~yvsstwz}~|yvtrqpooopqrstuvvwxxyyyyyyxxwusqnkhfefinu{}{yxxxxyyz{|}~}zxxy|{wuttwz}}{xvtrqppppqrrstuvwwxxyyyyyxwvuspnkhfefhmsz~|zyyxxyyz{|}~}zxwx{~|yvuuwz|}zxvtrrqqqqrsstuvvwxxxyyxxwwvtrpnkifefhlry~}{zyyyyyz{|}~}zwvwy|}zwvvwy|~|ywutsrrrrrsstuvvwwwxxxxxwvutrpnligefgkqw}~}{zzyyzz{|}}}ywuvxz}~{xwwxy|~{ywvtssrrssstuuvvwwwxxxwwvutrqnljgffgkpv|~|{zzzzz{||}~}yvuuvy|~~|zxwxz|~}{ywvutssssttuuvvwwwwwwwwvutsqoljhffgjotz}|{zzz{{||}~|yvttuwz}~}{yxyz|~}{ywvuttttttuuvvwwwwxwwwvutsqomjhgfgjnsy~~}|{{{{{||}~|yvtstvy{~~}{zyyz}~|zywvuuuuuuvvvwwwwwwwwwvutsqomkigfgimrx}~}|{{{{||}~|yvsrstwz}~~~|{zz{}~|zyxxwvvvvvvvwwwwwwwwwvutsqomkigggilqv|~}|||||}}~|xusrrsvy{}~~}|{{|}~}{zyxwwwvvwwwwwwwwwwwvutsqpnljhggilpu{~}}||||}}~~{xusqqrtwz|}~}}|||}~}{zyxxwwwwwwwwwwwwwwvutsrpnljhgghkoty~~}}|||}}~~~{xurqpqsvy{}~~}}}}~~|{zyyxxxwwwxxxxxwwwvutsrpnljihghknsx}~}}}}}}~~~{wtrpppqtwz|}~~}}}~~|{zzyyxxxxxxxxxwwwvutsrpomkihhhjnrw|~}}}}}~~}zwsqonopsvy{}~~~~~~|{{zyyyxxxxxxxxwwvvutrqomkjhhhjmqv{~~}}~~~|yvsqonnortwz|}~~~~~}|{zzyyyyyxxxxwwvvutrqomljihhjlpuz~~~~~|yvspnmmnpsvy{}~~~}|{{zzyyyyyxxxwwvutsqpnljihhjlpty~~~~|yvspnmmmorux{|~~~}|{{zzzyyyyxxwwvutsqpnlkiiijlosx|~{xuspnmlmnqtwz|~~~}||{{zzzyyyxxwvutsrpomkjiijknrw{}{xurpnlllnpsvy{}~~}||{{zzzyyxxwvvusrqomkjiijknrvz}zxurpnlklmorux{}~~}}|{{{zzyyxxwvtsrpomlkjijknquy~|zwurpmlkklnqtwz|~~}}||{zzyyxwwvutrqonlkjjjkmpty}~|zwuromlkjkmpsvy{~~}}|{{zzyyxwvutsqpnmlkjjkmptx|~|ywtromkjjklorux{}~}}|{{zzyxwvuusrqomlkkkkmpsw{}{ywtromkjjjlnqtwz|~~}}|{{zzyxwvutrqonmlkkkmorvz~~}{yvtqomkjijkmpsvy|~~}||{zyyxwutsqpnmlkklmoruy}~|zxvtqomkjiijlorux{}~~}||{zyxwvusrponllklmoquy}}|zxvsqomkiiijlnqtwz}~~}|{zyywvutrqonmlllmoqtx|~}{zxvsqnlkihiikmpsvz|~}}|{zyxwutsqpomlllmnqtw{~|{ywusqnljihhijmorvy|~~}|{zyxwvusrponmllmnpsvz~~}|zywuspnljihhhjlnqux{~~~}|{zyxvutrqonmmmmnpsvy}~}{zxvtrpnljhhghikmqtwz}~}|{zyxwvtsqponmmmnpruy|~}|{yxvtrpmkjhggghjmpsvy|~}|{zxwvusrponmmmnprux{~}|{zywusqomkjhggghjloruy|~~}|{zyxvutrqponmnnprtw{~~~}|{zxwusqomkjhggghiknqtx{~~}|{yxwvtsqponnnnpqtwz}~~}||{yxvusqomkjhgfggikmptwz}~}|{zywvusrqponnopqtvz}}~}~~~}}}zwvqjfdhs|zqidcdfhjmoruy|pmlsy|~|zxwvvvwxyyyxwvsrrstw{{~|{zz{|~o[^|jUNOSW[_beilptx{{{~|{yxwvvwxyz{|}~~qs}rmllmnoppqstuwxz{|}~~}}||{zyxupjcaht||vokihikmpsvz}sp|wokhgggggghhhiijkkllmnoqsvxz{zyww||qe[X]l|{pe]YVWX[^bfjnquxz}}zy{{tnmr}~xtpnllllmnoqrstuvwwwwwvtrokfcbdjsy|zwsqpoqrux{|uppwxsoljhgggggghiijkmnprtvwxxwvvx~{tkc\XZbnz{slgcaabdfiloruxz}|zy{~ytolmt~yurponnnopqrstuuvvvvutrplhebadksy|{xusrrsuwz}ysoqx|vrolkiihhhhiijklnoqsuvwwwvuvy}wog`ZWZco{}wqligfghjlnqsvx{}}{yy||wrnlnu{wtqpoooopqrsstuuuutsrpmjfcabelsy||zxvuuvwz||uqoqy~ytqnlkjjiijjklmnoqrtuvwwvuuvy~xqjc]XX\dp{{vqnlkjkmnpruwy{}|zxy|}ytpmlnv~yvsrqpppqqrrstttttsrqoliebabflty}}|zxwwxz|~~xsonrz{wsqomlkkkkklmnoprsuvwwwvuuvy}ysmg`[WX]fq|zvrpooopqrtvxz{}}zxwx|~zvrnkkov|yvtsrqqrrrsssssssrqomjgdaabgmtz}~}|{zzz|~{vqnns{~zvsqonmlllmmnopqstuvvvvuttvz~zupjd^ZWY^gr|}yvtrrrstuvwyz|}~}{ywwy}~{xtpmklpw~||}~|zxvsqppsw}xsqt~}~~rfssja[YXYZ[]_`cgjmruvxyz{~|x|yurnkhdb_]]_enxm`re_cjosuvxyz{|}~~}}~zu|~}|{zywvtttv|whnn`\\^`bdefgfgggggfedcdfjouy}~~|ytomr~|}viiytf_ZYY[]_acfhkmnoppqruy~{vw~}zxuqomlmqx{mccnxpkhhhijlmopqqrrrqrsuy~{wuy{plp{ypida_^^^^^^^^^]]\]^afkquwxwtqnmpw~tnnuzrlgdbbbbdeghjklmnpsw||xwy}{yvtqpopsx~~vmgfkv|wsponnnnopppppqrsvz~~zwvz~uootzsnifdba```__^^^^_`chlqtvwvsqooryzrnpxxrmjgfffghijkmmnpqtx|~zwwz~}{zxvsrqprtx}{tmhhnyzwtrqpppppqqqqqrtwz~|xvw{{tppw~wqmifdcbaaa``___`beimpsuutrpnos{wrorz|vqnkjiiijklmnnoqrux||xvvz}|zxwusrqqrux|}xqlikr}zwusrrqqqqrrrrsuwz~~zwvw{xrpry{upligedcbbaaaaabdgjmpsttsqpopt{|uqpt|{vromllllmmnoopqsux{~}zwuvz~}|{yxvtsrqrsux{~}zuokjmt~zxvutsssssssstvxz}|yvuw|{uqpsz~xsoljhfeddccbbcdehjmprssrpooqv}yspqu~zvsqoonnooppqqstvxz}~}{xvuvz~|{zxwutsrrrsuwz|}|zwrnkkow~{xwvuuttttttuvxz|~~|zwuuw|}xsppt{{vrqrstuvxyz{{|}~}zvsqqtyxf_lzfZVX[`cdgjknrux{~~~~|{{}zq{zvvwxwvsqommmoqrttsrqpsxjtj^_chkmnnopqsvy{}~~}|{z|{tx~~}~~~|{}s`by}fVPOPQSUWY[^aehkmoppqrty~z{ugesxi`[Z[[]_adgjmprsssrrruy~~xrpt~}yvtssssuvy{}~qmtxmfb`__abdgjlnpqqpppruy~zusv}wkfhr|rh`ZWUTTVX[^`ceghijlotz}zz~|rkkq|~vojgeddefhjlmnnnnopsw{}xspqw~}ywuttuvxz|~}uqs{xrmigffghjlmnoooopruy}|xttw~|rkhlvzrjd`\[ZZ\^`bdfhijlnquz{yzzqmnu{uokihgghjkmnnooopqsvz~~zurprw~~zxvuuuvxz|~{tru~|vqmkiiijkmnoooppqsuy|~zvttxxpkjoywoiea_^^_`bdfhijkmorvz}zxzwqnqxztpmkjjjklmnooooprtwz|~~{wspprw~~{xwvuvwxz{}ytsxzuqnmlllmnoopppqrsvy{~~{xustx}uoklr{{tnifcbbbcdfgijkmnpsvz}}zxwy|uqos{}|zvvtpqpmnqsty|}~~|yusrqsuwzxyz{{|}~|mv~xxyvtromlklnqsvxz{|||}}~|tii|~{zyz{~y`]x~opw~~zuokgdcdegikmnoooopqsvy{ytno|}~~t{yg^_fpz}yuqnmlllmmnnooppomjgfm|{vttw|vkiwxld_]`fox|yvtrrqqqpolheekw}{{}xngiu~wsnid`^^bir{~yupmifc`][]dny~|skim}~zwolic_]]_enwyqk`XUYk~xwvxy{}~}yzgegijkmnoqrsrrqqrtx~{usv}|u}{ttvxz{}}y|jsjWSUX]afimprssrqppqtytlo}zxy{~|{z|zdV\trbYWY\`dgjmnnmkifefjq}yrlmw~xqkjo|zl^UXi~~qgb__acegggda__ckw{uqqw~{vqlkmu|si_YZfw|rkfca___`djqvz{{}{wvyyrkgeir|uld^]cp}ricachpw{|zwtssuz}xwy~|skeflw~{{|xphb^`ht|z{|~}|zxvtsrqrty|tnjjloqrv}|zyxwwxz~~vmd]Z]fu}yvuttttsstvy~zod[W[gxzvsrrstuvwyz|~ulb\[`m}ywvvvvvvvwxz}{slggkr{zuqoopqsuwyz|}~~yvtrpqt|~{yyyyyyxwwvwxy{|{zyz|zvwz~|xurqqrtuwy{|}~~{smlpz}|zyxvtqmjgeehnxzqkghmtz}~|ywuuuvwxz{{{{zzz|{smjlt~zvsokfb][]blyzslgeejqx~~|yxwvvwwwwwxz~~wpllpzyrkea^^agow~|xsnjgfiov}{xvtsstw{{uomnt~vngdfjqx}|yvuuwz}~{xtokiimsz{xvvx}yrljkpy}zy|{vrnlkkmpty}|xtoljjntz~xog`]^dnz|wroljhhhiknrw{~zupkhfipyyoe]VUYevztpmkihhhhiknquz}}yuqnmnsz~xrlhfgjov~xtpnlkjjjjjlmpsvy|~~~~~~~}{z|~}~ztpnpw|wsqomlkkjjjklnruz{xxz~|zxxy}}tlfbcir}{wtqomljihhikpu|{xwx|~|zxvuvx}~vohb__cku{vsomkjjlnrvz~}zxxz{wuttvz{uoid`^`fpz}xtqqqsuwxzz{|~|ywxz~|xvwz~{vqlfb__bis}~~}|{zyxxxy{~|xtrqswz~|xtoid`^_cku}zyxwwwxz|}xsnjijnv~~{vqke`]]`gr}{zyxxxxy{}{wrnkjlqy~~~~~~}zvqlhdbcflu|{yxwwvvwxxyzzzyxwvwwyz|~~~}~~~~~~~}{ywuromkjlqy~|zxvtsrqppqruw{|{|~~~~~~}}}}~~ztpkiikqz~{wtqomkklnrx~~yvtuw|~}}}|||}~zuplighmt}{vromllnquz~zvtrrtx}~}|}}{vrmjgginv}yusstvy|~~|yvsrqruy~|xsojgeehnu}~||{|}}~~|zwurqqrvz}ytojfb`adjrz}zyxwwxy{|}~~~}{xvsqpprw|~{wrmhb^[[^dny|yxvvuvvxyz|}}}|{xvsqppsw~}zwsojea^]_ckt~{ywvuuuvwxyz{||{zyxwvvvx{~|{ywtrpnljjijlot{|zxwvvuuvvwyz{|}~~~~}{zzz}~}|zyxxxxyyzyxvspmkkmry~{ywvuttuvwy|~~|zxxz}~}{zyyyz|~|wrnjgfgjpx}zxwvvvxz|~~|yxwxz}}|{{|}}yuplheccfkrz}|{{||}~~|zxwvwz}~{wtplhebbcgmt|~~~|zwuttux{}{yvspliecaachnv~~}}}}~~}{xvsqppruz~}|zxvspmifca`adjqz~}}}}}}~~~}|zxvsqonoqu{~}|{zywtqnkgdbaadipy~}||{{{{{zzyxvutsrsuw|~}||{yxvtroljgfeegjov~~|{zyxwwwwwxxyyzz{{{|~~~}|{zyxwvtsqoljhggilqx}{yxwvvwxy{}}|{z{~~}}||{{{{zyxuroligffimry|zyxxyz|~~{yxwxy|~~~~~}{yvspmjgfefimt{~~}~~|zxvuuwy|~|{yvsqmjheddfint{~|{ywvttuvy}~}|zxvtqnkhecbbdhnt{~~}|{ywvtsstwz~~}{zxvsqnjgda`_`chow~~~}}||zywvtsssux|~}|zywurpmifca_^`chow~~}}}|||{zyxvutttvy}}|zywusqnligecbcdgkqw~~}}|||{{zzyxxwwwxy|~|{yxvusrpomlkjihhilotz~}}|||}}}}||{zxxxxz|~|{zxxwvvvuutrqoljihikotz~~~~~~}|zywwwxz}}||{{{{{{{zywuroljhffgjntz~}{zxwvuvwy|~}{ywtroligedegjou|~~|{zxwuutuvy|~|{xvtqnligeddegkqw~~}|{yxvussstux|}{ywusqnligecccehmsy~}|{zywutrqqrtvz~|zywusqoljgecbbdfjpv}~}|{zyxvutrrrsux|}|zywutqomkhfdccdfint{}||{zyxwwvuuvwxz}}|zywutrpnljhgeeefilqw}~}||{{{{{zzzyyyyz|~}|zyxvusrpomkigfffgjmrx~~~~}|{zyyyy{~~}|{zxwvusqoljhfeeegjnty~|{yxwwwy{}~}{zywvtromkigeddegjot{~|zyxvvvwx{~~}{yxvtrpmkigedccegkpu{zxwtqrrrtw{}~}||{zyvtqonlklkkmooqux|}sw}~|{zzz{||}~~}}}}~}}{yurnkigedddefhknrw}sprtvwwwvvuuutuvwwxxyz{}~{~|{zxvtromkjiijjlnquy|womlllmmnnnoopqrsstuwx{}}y}|zywvtsqpooopqrtwzzunkiiiiijjklmnopqrtuwz||yz~{zxwvvuuuvvwxy{}y|}uolkjijjkkllmnooprsuxz}}zxx|~|{{{zzzzzzz{|}}xyzuqnlkkkkkllmmnoprtvy{}~{xvx}~}}||{{zzzz{{}}xw{~ytqomllllllmmnoprtvy{}~~|ywvy}||{{zzyyzz{}~zvw~}wspnmlkkklllmnoprtvy{}~~}{yxy~~}|{zyyyyyyz{}~|xvz|wspnmlkkkkllmnoprtwy|}~~}|zz|~}{zzyyxxxyyz|}zww|{vromlkkkkkllmnprtwy|~~|z{~}|{zyyxxxyyz{}|xvxyuromlkkkklmnoqsuwz|}zy{~|{zyyyxxyyz{|~}yuuy|wspnmlllmnnoqrtuwy{}~}zxx{}|{zyyyyyzz{|}~zvsuz|wtqpooooppqrstuwxz|}~~~|zxwx|}|{{zzzzz{{|}~~zvsru}}yvtsrqqqqrrsttvwxz{}}}|{ywvx}~}|{{{zz{{{|}~{wsqrw~zwutsrqqqqrrstvwxz{|||{ywvvy~}|{{zzzzz{{|}~~|xtqqsz~{xutsrqqqqrrstuvxyz{{zywvuw{~}|{zzzyyyzz{{|}~~|zvsrrw~zwusrqqqqqrrstuwxyz{{zxwvvy~}|{zzyyyyyzz{|}~~|yvssu{}ywusrrqqqqrstuvwyzz{zyxvvwz~}{{zyyyyyyz{|}~~|yussv~|xvtsrqqqqrrstuwxyzzzyxvuuw|}|{zyyyyyzz{|}~}zvtrsx{xvtsrrrrrsstuvwxyzzyxwutux}}|{{zzz{{|}}~~{wtrqtz~{xvutsssssttuvwxxyyyxwuttuy~}||||||}}~~}{xtrpqu|}zxvutsssssttuvwxxyxxwutssvz~~~}}}}}}~~~~~|{xurpoqv~{ywuuttsstttuvvwxxwwutsrsw|~~}}}}}}}}}}}}|zxurpnory}{xwuttssssttuvvwwwwvtsrsuy~}}}||||}}}}}||zyvsqonpt||zxvuttssstttuvvwwwwvuttux}~}}|||||||||||{zywtronnqw|ywvutssssttuuvwwxxwvutuvz~}}|||{{{{||||{{yxvspnnosz~{ywvutttttuuvwxxyyxwvuuux|~}||||||||||||{zywtrommot|~{yxwvuuuuvwwxxyyyyxwvutvy}~}}||||||||||{{zywurpmlmpv~|zxxwwvwwwwxxyyyyxwvtttvy~}}|||||{{{{{zyxwurpnllmry}{zyxxxxxxxxxyxxxwvtsstw{~~}}|||||{{{zzxvuspnlkknt{~|{zyyxxxxxxxxxxwvutsstx|~~}|||{{{{zzyxwusqolkjlpv}|zyyxxxxxxxxxxwvutsrsuy~~}||{{{zzyyxxwusqomkjknsz}|zyyxxwwwwwwwwvutsrrtv{~}||{{zzyyyxwvutrpnmkklpv~}{zyyxxwwwwwwwwvutsrsux}~}}|{{zzzzyyxxwutrpnmllnry}{zyyxxxxxxxwwvutssrsvy~~}||{{zzzzyyxxwutrpnllmot{~|{zyyxxxxxxwwwvutsrrtv{~}||{{{{zzzyxwvusqomlkmqv~~}{zzyyxxxxxwwvutsrqrtw|~~}}|||{{{zyxwutromlkknrx~}|{zyyxxxxwwvutsrqqruy}~~}}||{{zyxwvtrpnljjkot{~|{zzyyxxxwwvutsrqqqsvz~~}||{{zyxwutrpnljjjlpv}~|{zzyyxxwwvvutsrqqrtx|~~}|{zzyxwutrpnlkjijmrx~|{zyyxxxwwvutsrrqrsvz~}|{{zyxwvusqomkjiikot{~|{zzyxxxwwvutssrrsux|~}|{{zyxwvusrpnljiijlpv~~}|{zyyyxxwwvuutsrstvz~~}|{{zyxwvutsqomkjiijmry}|{{zzyyxxwvvutsssuw{~}|{{zyxwvusrpnljihiknt{~}||{zzyyxwvutssssux|~}||{zyxwvutrpomkihhilpv}~}||{zzyxxwvtssrrtvy~~}|{zyyxwutrqomkjhhhjmrx~}|{zzyxwvutsrrrtw{~}|{zyxwvusqonljhgghjnt{~}|{zyyxwvtsrrrsux|~}|{zyxwvusrpnmkihggilpw~~}|{zyxwvutsrrrsvy~~}{zyxwvutrqomljhgghjnsz~}|{zyxxvutsrrrrtw{~}|{zyxwutsqpnmkihhhilpv}~}|{zyxwvutsrrrsux|~}|{zyxwutsqpnlkihhhjmrx~}|{zyxwvutrrrrtvz~}|{zyxwvutrqonljihhikot{}|{zyxwvutsrqrrtw{~}|{zyxwvusrpnmkjhhhjlqv}}|{zyxwvusrrqrsux|~|{zyxwvtsqonljihhhjnrx~}|{zyxvutsrqqrtvy}}|{zyxvusrpnlkihggikotz~}{{zxwvusrrqqrtw{~}|zyxwutrqomkjhgghilqv}~|{zyxwutsrqqrsvy}~}{zxwvtsqonljiggghjnrx}|{zywvutsrrrstw{~}|zyxvusrpnmkihghdjimvz|uvqorqv|{ztikpuxyxw|~~xe^div||}wlh[Yes}~nfoohc]]_\en|}wsnd`dgsyzw`Racceejsuy~z{n^hoaY^ehr|ts{~hszkfhaZ]^[[Q_cRkxrr{}yq|jy{hhjrxocixqtgulswlrwv`k_aljdT]ungkj~s}x~~m}soqku_mjknnxhg`k}jgZfsj_n~mnc^vy{rlgjw{~uyutak|lgy}pnqxy}txrpix||}ksx{~~zg]Wktikvussz{{xzslnjct~b^Yinlw~~qenw}ebmiu{sv|||lt{{xzvvgojgfnqlo}o}zWGwyxgTPVUaijzt^lg~uy}q{~}jc}||rxtdhxwywy~~z{izv|irowzwiS{pg\_d[fwsmYathq~flvwyoprzvyo~{xlw~ikijgsro~xuzzyrr{xzv[WSjuczgtmzrsTB``ilTkbgo|{injuqhupxy{qlvejhm[rqpnqtqZYSKimhmorxuq`]a[]pYjempr~qqcr{nZ~oz|x~gst^kku^vw~ymoqyvnZomMPmwvgyqssdkW\a}~]mtkpmrluhhwsg{z}qtzqxg~ondfsixvmrtfy|~xelq_MTw|nquutn_hoW\m^`uphes|jcvzppvysrzhvtkm]mr{yjx~qzutv}}xfbsmZSn}mstvqiSkjW^rs[uvoodgwzdj{{wuqv{{yi}znhadwk|x{m~|}n|||~q|W_ykcWusiuyrg[[wZUfuiZunqojn{wo}ry~vz|yxormnnbgvqr}zs~|rsz{whThtie]tomwqfXcvTZnzjZqr~reozrr}v{wq}pklimj`kzzuw{}ywv|}}zqx}YTnqfao}ukp{nh[mkVrvq_fqxngzvv|r|~r}um~ykijuo_h~xp}x{w|uxugzrSXifnd}php^eza`~rm]io|rfoo{}p~{~or|zxqhmtkitr~zvrz~t~w~ygsxVRcgmmpkxyf`vqXtrlh\du~nezss{ox{r|wpz|}uhlwphs}{w~~wp}ww}|fiycP\hnphsm{kcpqXhgjZ\l{qdnxznt~~rpzz~{g`xzgmut}}l}|wnfrkZ_djtnverbmq_aeh^Xosdpuxrt|pl{uxziauwgop}k|~r|~lfsp__aitn|dvclpgffhjVk{vim{xos~u}lpwrx}i^syirt{{nzt{~mhqna[\lqiy|juhvrjggikWp}vip~xip~vx~lovozk[t|ouo|yxgz}r~y~xoklia][kljvp{mxqnmhgkQp~sdmsep}tv~qoyt~k_t{rwn~wuiwr{p|ynihgd^Wgjiwtpynork^oVl{thkudpn~wytxr|o^p{qtnw}{x~qu}yvkvvojfhlaSfpkwysovpquys[m\e{tkk}hmrx}txxrux`etrypyx~|}vzwowrutpumnjejhY^qhxt|wvvnywwchk[wytjzmg}wzwyv{spq}e_oryuryv}oy~vrxdsnpldgl`Xphqv~~w|ntyr`nbn{wnsxbqqvyzztqnyr_fry~oz}u|~qrqm~ungnlod]ilY`mi}r}~}xp~ujdmgvwql|ie}uuzt}xtvps}{lcmryzyzsxo~zu|m}{}alhji^`leXkiu}x}}rtypegnlstos}dmuyzrtvumuyiemxv|swt}ttt|{qwq}^ecidZamc_qlz{||ryqpffoprsnx}zer}x|uvnxtpy~ugdlv|tsz}sxruzvon]cdkaXdqdlps{v}volpcjvrvsq~nh~{z|m{jwnt{xndgr{u~r}vurn~x{yw~b_bhg[]nnhsm|z}ryzmoav|qto{hr~zv{mipvpwytifi~zuuvwp|sxyx{v{s{p__di_Xesipmy|y~wwjshkttpw}nm~{xwhwewpuxvpieu|q{qwqyp|v|}yvvwtf\_fcY]nmqpo}|~}|wsmsc{orq{vkxz{rpsdstu{xslgmtququrzz|x}puyqj\]feYYjoqqk~~|wziub|oqo{ylw|~qxpfowv{xtohjutuqtvt{rqq{ml^[feZWgptqi~{xgufxqpm~zqy|~r}|nljwx{vrpjg|wvupvyqxppn~inc]ed[Xgovpi{yzfvi}roq}zt||~p}zplkwxzurphg}yxwtoyzptonmgod^ge[Xfnxljwx~iwn~vor~zw}}|}o{ysnguz{urqhh{xyxtox~}m~vnqjgmj^fd[Wblymg}uxktt{zoq~yx~~||ovzu}sgr|}vqslhyuy{tmxzm{xlvejgm_ec[V_iyne{txqnxzpm~|xy~|}ptzyzwjm|~xstphusx~wntyuw|lv~~czv_pcac]V[etweyw{|zzkw|xj|~|wz{~urwz|wshw~zsstipq|z|qr{zp{po}ikaik`c`YY`m}jszwvpont}xx~~ypuuvwlk||vqtomrqzyozvw{zyjv|yazt^lgbb\X]ezxm}vyx}nv}}l~zu{}~qrq|pvfr~yrrtmrm}xsrzvsx~pl~z~mg}gamec_ZY_mqx|zv{ym{tq~us}xnsnwptev}wquursmx|pvvx~wyz~loy|flybamfa]Y\btr{u{vnzpw{qu}tnqppqrfx|vrwtvrsz{|zq{rz}}wzxks|zucpr_cld`[Z_f~|z{{v{ss|n~~voz~znrkyjvlk|{ttxvymu}vvxs~~}tqlxvzigrj\glc^Z]arzvw}|nvyv{pq~snqkvjwhr}xsww{sqy|{}u}~sv~}quwyrdknd]pne`aflyzwy}xz{mV^`dfmtywj}vzvnpvq|pow|uqqchij^lujechl{yxllwwny|xmu|moccyftz|~vuy{~l|ofmtx~uqeZYbjrmgl^daf]^qh_[_bs}}~zwz|weZn`gqgddhVbtsu|}q}y}v|yx|zlbdx~|wtx}zrysvtmtptx~|hafyvqhtpsuxywrgj}wde]Zvne|r{k][nvq{keefkfbgYbX[]RhhZUZ]lswyy{~jhXWld]w{blrsqryrwyxrzz|yjcdz{n|{z{||}squynsoru}{y}yxlqwrnv{umu}xvnfmwgk]cuk`r}q}k`]qrc~zuqlnggh[hXXaUel\VY]lmwxz~}|nm[[jdYelrrrsl~xwuxwow{zledw~f}|z{z|tum|qyyoqt~{uyyzwu|xuxt|nk{vofiz}ila_qoeb|~}x}rd_iw`m}z|soigm_heS`_ZkhYX[gkly~~|~|}{vofXgfbXm~tzo}|nqzvz|szoovsgcjri{x|zvx{nq}s|vpqxy}vqx{{z}}z}xqho{tjel}vjl[gongd|{~~la^msbmyunfkl`ndT`e]jj\\_khq~}~}y{zzzsoaXfce]l}}p}qp~px{symmtrgcj|slt|{wtw}qm|zw{rrx~u~zsnu{~|}mqgvkejyykm\dnloez}{|~k_[gqjluzpfipcomZ[iedog`alkk~}wwzv|vleS`beigsxxpwwlsyqjnxxiceuxtrx~vo{|v|nrz{ytvu~xsln~ovfv}oegr~nn_]ljroq||zwzm_Zcontptflvz}vuqi|ystlhb^fvqv~xt|~zty|to^lpt{i^wb``fxvb{nrjbfr~qln|{|wr{jlwxwvzlx}|wqko³wl~zomXcn]i{vf]any}tvekws}}{sn|y}pouqiiv{~v^WvmUJVcX]c\[c\axty{wsmlwx|ywql|z~n\spaZdYNng_bVV\lzykb`jqo{jr}}yphuz|~~|xzx}pp{kqf]epdVT^ipslpd\mjnxvyyz}|lzyyvxury}~tg\gtitm\bc\`ZVmlowsnhdkzuz{~y|p|~~|wcmyxt`prhfXUYgvymc`hmhxjdxxx{{rek~|{x{v|xwyztnxp^Zainplte`niluxsw}|}kt}xuxyu||xxv{l[ctzy}fglfeX[nhqwtngdjw|oq}{ywz}yejv}pvsrj[X\iwyneclifwf`|sqv}}sbitw~~wz{v{~}yw~vzwf`eknmmwgdpjkszrwww|mqyyuw}x~zuzo~s_`knuhrpl]]ngoyxqhdhr{rhz||x}zkin{}uzvd\]gtztkglkcso^lwkoyxhdnlz}uwww~wvz}uihlmmlwrdqojoy{pxvz}torzwu||}s}rt{w~|k^gewuoyxm\kmhv{yogflu{nn}y~{{|zkkp{txf_alwztmjogeukbl~vglw}tfiklzyy~~wxx~wv||yomoomm{riuokpz~rtxxxrpryxv|~~q}utzpxyiahdxq{saooiv||sjgltzso~y}~}}{~z{lmpzqmddluzwrnrjcsohhv{ieq{|vijmi{w~yw|~zwwzvrrqnkxylwunow{pzxztyvqswz}vytvyvvnkuzwndkjpytjmujq{zpjkqxzr}{{}}|}{}~|vpqux|~|rgunpxnks}}ufpotptvtwk|yr~wp{~obomow~xx{xzz~|yup|v|xuz|u{}vwwz~zxy{}yo^sfZerupipijvty~qxzqmkjq~wwz{xz~sefwygasndggjnty}uk~sv}tryrgpmwuw~vzwoug]Yas~olnrrkehuz~~jn~kmwzxuqrzvwliqvz{{|pZ_`i|~|uihhZj|}~~{vyupyp|rnw}~vkdcelu}~{rb[ap}tpmihjovytil~mz~wru|udlln~}|}~o}yi]WV[hznjq}xzniuseq{zupn|zt{ojnxy}}|{}o[_]_s|yvr_jy{}~~xv~~r{zutnv}yphfefn}{~|wg_`itw|zunihlswtkosw}{vx}ugngixx|~n}|nc\WVayytntyr|}mluteq~|vor}uy{qmou{yw|{ww}qadYZjouwdpxx{~~yv~wxw|spv|~zrlhedkxw|}necflop}vmhkrutlq{vy}z{~xkqgesuq}p|tjb[V[rqo}uvxpy~omr||jk|yqq|twvporwxs{~xuxzgh^Vckjumpyxy}|vwwsy{puz}|vplgcey}m}{vkeehmmqvmjnttpo|~~tz~~~pqnbkurn~yvzph`XUevjtz{uqxnps|zjm~xqw{q~vqqswxu}wtxrggXXfmjsqy{y{~|vxvm~xruy{zuplgbh~{n|||ytjeeinqtyplottps}ytw~}psmblvunzyy~xog_XUgyou~~vrzprrx|pkz|sxzo|wrqsw{z|zuw}sfhZWeqqqu{{|yv~jw~stwyxuqmgcf{u||~|rvlfehpx|~tx|pw~}|wwzg`{xsx|vi`ztYLUmlcn~y|~t~vvtminwuu|qv~ffphoxp|vs}qvofntnbqx{teaxye]lufckvsls}|~}{tr}}r~regz{jbib]qn_dsz{xw{{|pnsrjg}z~zyj`aqqbcrldit}wv~wozylidbk|v~plhflz}myrkw}ww{tmqc\cjnlw~z{}j`^dszokluslqz}}}{qzuqpms{yqkjjnwll|scjy~|xw|w~uhjprnn}|xnd`cm{|vuts{|{}prznihdes|w~}eacfkt|xrq|xzyotl]`gmmnp}lc_bl||wqrzurw|}~}qt~uppmlwvphikms|wo~nhw|y|xxjjnqokv}{yribagt~yysu}psznjjfclz{xf`bcflvzwp~~z|xrxm`afiihuvzohbbgttr{zuwz{}ymtzpookgp~{x{ojkkjmuzwtmy~{~{zvklooliqz~}tmeacm{|~ur~~pvymllgbh{~yth`abben{}r{|}~vs{occeggfl|~xrmfbdm}}qy{wxy|ymrwmlnkeh{}xprghjikq{zru~|y{nlnnnkl{{yspicagtrvtszpjljdbp~n}t`]_`bhs~}v~zrzxhcdfghjtwookfdhtzr}}wwz~~rmuphklgdloqkcehjowvyx|vmlmnopr~z}nllgcdmztvrvwlikidet~jzp]Z]agoy~wwr{vgddfimqx~qgkjghn{~szywy}}rmtmehigelpkk`agmu~xxzwmllnrxy}x~y~hchhgjrzt~ttwlfijhitjl|s`WZclv}|yyovyjddgls{~xuwaailovxsxw{~vkqoccgiimzwfq{pc_fq|}|}yv{nlmpu}vypwiZailqyvz~uswldgklmulgrpcZ[eq||~z~yntxkdfiow~}rn~x^\hqv}|t~zwz|tkqmabgjlp{~rfnsmebiu}vwwmmosyt}riv}dV`kqw~x{~}ruuhdhlnqyyhimic\_iu}{~snxthfilr|{wipoY^lu{|wyx{xmnqe_cgjmtyjjnljddmy{t~|pnoqu}uz}ufk~qXWeouzy~~tuxkdfjmou|mlkffa]cny|vowvkgiknw|ximw^Zfrx~z~xyvmore^aegjpzxlnkfigcgq|{v|qoppryv|~}}~|rdk~rZVblqv~{~{swxjdfijlr|yprjcfc]_hr}}tr{tkiiils~|ugpw`Ydouz~zytlrrd_acdflvunrjaghbbju|x|rppopvz}}||}{qcj|u`U]hosy~zrxylefhhjoxyrwm_cg`]blw}xs{wmkjijoz}whj|{j[]irw}zulpti`_abdiq{xnsq`_hgbemyxuqqqps{{~{z{zugaqxl\V_krw~{ss{sgefgils|stxeZchb`fpz}uw|slkkkmt~|pdmzug[]ju|yqjrre__acflt|}}rmuo\\hkghq|z~sqrrrv~}zxxxqb`nrj^V^lv|yprzqfdfhjov}|prxfW^iifjs}|vv{sklmnpw}||yobgsrka]hw{qinsg__adiov||z}rirs`Wcoppuy~tprtvy}yvuupcZemjd]\jy}qmvuidfimrx~||olvoZWfqqqwzxtzulloqtz|zywn`amnkhbgwrgkri``cfkqx|t{pgoueW^pxxzz}upsvy|~{wtsrm`Xbiffc`j{~okushegjot{y~xkkvo[Wfuxwzy}~vv{skmqtw}}zwvsi]bljjkgjyofmpf`adhmtz}{t}wjfssbW`s}|}~}z|rqux{~|yusqogZ[fedhfdn~xkoumffilpw}w|phsvfX[lyzy}y{xv|xnlosvz~{xvtl__jiflliowhjqjbacfjpw{}t}ymfpvhZYiy}|~~~|{tqswy}~|zvsqog[[fd`fhdgu|mounfegimtzz}pitxiZXeuywy~zxvx~yoloqtx~{xvsj_blgcjmhjwyjmqhaabcgnuy|wxkhtugZWdtzxz|z~~sqsuw{~|zwtrndZ_ha^ehdco~}nrulfefgkqx|~|olwxi\W`pwvv{}vty{omnprv}~{ywsi_clf`gmjgo~~noribaabekrwz}ymhtvj]W]mxxw{zw}uqrtvy}~|zwusof[^hb[`hhcgutqungeffhntz}sktzobZZftxvx}xqttnnpquz{}{ywtma_ji``jojisxormdbbbchntx|}shmwqe\Y`pz{z~vt}trsux|{}zxvsqk`Yag^Y`jkfjxttskefejxppmjlidebee^Z_dqx|wg^flwwonuxufSFQ`fbepsz~rou|lb]Ve~}z~|yhfrsgirrqz~ttumiuxe^``Z_`entz_]igv|jdfkpmfzxncRj{tosxlkqzt`j_`ryudeksqbZrg|uwtx{|pyz`Xlh_\O[GP_mwurwy}txfq__ip|~{mzusm}}wpmeidZcWZld\qu~||u|sp^^iruguvumtr}fiaHTenvswqn`ddcSZd]jua_`\s~~}wwqn|y}{~w`T^\d|xxwrwpew}vv~pxfvzmha\`gnu}x{vrv||w~{zslhb`a^lweez||~sxynfbXjwzwqxt|xzx~hTahi{~xkm`gqkimfXfi`mo^twy~{s{u{umgRZnn}~~srqoxj{ow~}|y}|tp{vshknk_n}z|ztx|}yuvsxsu{}rqto\\jfp|qtxr|r{oxkZrqirxwlruo|nthek_\ilw~zduvcbc_blWXjbViqqv|||tzw}sxnh]XdVWt|r|zxsxyqvq{}z~qtfiumknzp|ynlq|wz|yxx{vvttqmvegvorw{~y{yvtfur[Ze`fmwvtsujse^nelvkfphggiaeflo^Zmsvxvvw{}{~|~ztqb_^biacwq{y}|swvnrdeedq{r{my{~s~~{zi|{x|pnkgip\fwrftyx{|x~uaepnmflyu~~{ztx_YhaR^pyvs}zplrqn\aceolendpz~~~}}sss~{~wzrnXRY\nyz{~xz{w~{iyreojiingkxu|yyxzm}}|~w{kqwzxxtlstyrdm~|~nrqptjagWetoxvxvtkWSertuiexjkfvofuoh{zsuuwzv~xj~{|q}ydwje]gsapqs~w~so}teprz}k~y}vhuvncivvxxx|rqvwutszsnunzzvimxymuy{jnvbmn^ksqqj{|zxfbl^`bagl{`kocfra\tqi`xstuoy~{yyzz|mwmh\Rb\o]nvpq}yzx{m}|sw~nrgmxvl|ywvvv}xsu|f`m~odgo~jrtytkxw|t{xvcr{rqhe_jers{|yx~wzpsrltdc|~tvifmosvvwnv~xew~omm}khmz}y}vnqkkcXe_Umn}svt|s~t}~y|vvuxwthrw{qz}{}}zu~xztj{hfk^gs{gtprhsoozs{fr~ttjibk|ry}~}tlsnjdRqz~mn|~lxxp}jqkttov^xltk}w~xy}}yxlrphl[Xb\b|uz}}xm{vrtu{yotjmroyrvv|ur~z|yiqjgkr~~~|npnq~sv}nv~tbljnshsjn{{{{xvwsu\a{syrk}|{tynwvwup~zotnlxqhkvtqxr{v|nooYVaNkmeqs{~uq|xysy{wviwuwgp{|wzy|t}poqhfkqhkq}ytrwrttnx~mtwpqgpfdtt~~}y}|~pdxvjkah}z|s|zxlt}lpoa}ggg|wk}~sx{tvyhggUghTTYp|x|v}v~wupxyxlfupk{n{t~yv|ypvsuvsxjhjynq{ys~}vput{krkre`ijli{|x}wxxh|vthfoejm}{~rzznopijghsz|gwz{w~ojo^fpWTM_jau}~x}}yv}slyoinelyuuzuy||uxy|twmpkvmcnsywowq~romupfpkrempvzz~zyr~}xteni\adrkx{}zpwmslhfvwvpm|~|zuztvqfyk]XZf`^o{|r}w}iwvooe_c]m|owywus}otqtst}|s^pgjuxsw|zw|rgllh]jktup}xzf||opqcmack[hqpr}yswo}~j{issr{o}msuuw|}}}|opvurigcfaaqxfps}~~qz~|zwnp^eaxh`guz}krm}oinyul]o{ond`u}zvz{vy{}rhylobnkfy|nr~zypswj^f`kZjcwt{~nwopcxyoiozpuox~~}wrsob^seSlybf^d{}|~vsxqspndrwjbz}xvyw{ku|qihnafocaqotpuxd~moogpvkz{sq~zu~|lj}leokei[cshpzr~xzpwwymi}ukn||z~|yn}hwygqmlxtgwo\gws}wyw~}|{epttlqg[vks{{|xsy|onxnk\kkd\[mz{z~w{}z}wjo{zsiwuu{x{{u{mvye\xoV]\Ygqiyx~u|{~us{onupv}ly}zx|lx}p{zojvjmjZgmgqyvzkuvglcmrky~y~jkz{~z}s~|opymti]j`Ydxdd|~{trz{qq}sqmoznzt|suzro|}tyy~q[gyehnihvsynwr|pdt{cgecvzu||vl|zrqjyohbdqmajqyuto|yopmlqqzuk~z}~v|xeshbbWah`dvtp}rqy{vtuzu}kjxrkqxi~y~z}xqwlpgpj_chclemvw|votp}{t}qfxq{{xwzzv~vtlskeglW[qk[bav|x~zv|r|vryoxnswsr~|v{qxy}yhqpnpldel^khoy{|{}sulkiaexujmuu{y}s}tsqlupahljk\lrnm~y~y{wvsvqwqhnppzr|ww|nzulkapxohrxnscYo{uz}vxwfaY[YP^^VQXVdz|y~xx~tjnvw|rX]cx}TMZems~zvzsl~xik}fy~pf]afkrpciwxgqnnnj{ayskmqj`txaUyeXbfjck~|xwz||xotyorjodmhcsoseozlwgemocXkuy|mdotmmrsvqhz{}qxl~yupt{M\tlZoqrfbsq}~~ztq|pbdjZ\dU][F_eIVIch\^gmmd}ux}x{vfhvY~~pt|lwvkryuywvwx}zswv^f\Wd[QUPtfU]cgmfu|njwqxdl{rZXWjsYmuz|sx}wysxtloyxq}}p}~hqlW]QLTUKGJnSI^_bkl~{zvjtscke}wr{u}{xvwqvxp|wsvzvxpmz}lymgg\XidXOevM[lcilq~vy}s|qrp^nqa^gaye~}~}e|}xy{~wwt`h~zxp|jcn_aYLSkUKJm[Efgbkmu}tokzsjsmxvvv}~sovz|sxv}yr|je|ltlnl^YsnUVitM]nclnlx|v|y{}lwp^jkehhg{m}}h|v}u~no}mZj~~x~sjgdeiZO]pLK[mSJf_joix}zxmntqrsm~x|qw{t~x}o|z}nk~x_h|slpzj^d|[ShqcNebjrhpvu{wsoyhcghljesxv~xuyz||}pfwwZ^vx|uzynbgudVWrXEbf^KZ]frfk{}~}wwnmrxqmy~yw~zyzv}xyxtv~ldxy_bwxz|iuqcf|_QtkeUZ_msekxrrst}ypf`irhfoxz|zv|}{ldvtY^nnxov_rzjY_qVJp`\RQZlpbhzx{{}pgs}qoux}y~t{}|||}|ss|jarv_`nq~xiyygixfSzmaaS\ptee{|zpuyuvzyxk[fuigmuxzw|{wobnta\ehsqvdmva_mdLlmU`NSjrf`{}}ty}zdi}wovy~|{qy}}yx}wxpwqaerh_dhv|xyntfrq_f^f_Qivnbn}ynt}nszuzxaXmsgom}|x{}wv~}twwgcmk```ivqcw~v`hjaVugTdMXrpeb~tsy}}w^j~uuy~~|~}xyo}y}xu}qisj`dkgccey{xsvismgk`edRpwpdovkv}yp}tyqxzbUjsktpz{x|y{ux|kprfafgdc_hxvhsz{fhidanrTeYUspkbzro}u~}x}baxzy}z~|t|mw||kxx`mia_deihcwq~lrmnnyt\m[eyslf}jmxu}oxuopzpW]rqxw~~t~|}x|v|ltbme``bejfd{zspu{qfldjltdZhWmroepxjy}}}u}m]o{~~|~|~ry|pz~q}{{ph^cf`_`akpgux}}qrnmxuxbhhexrof|ji{syszywonyt\[ns~zsw|vyw~xg~v]ha``^dojh}{vwr{rjldqspi[hcppoftvj|z~~{u}m`nx~|y~nywp{nrxki}[a`_`]`ori||~svmttuehmqtrjn|fo|ruzsxvnrzn\`myzv{xty|w}y~vyfh`a_b]\jrgtz~xtynpim~qng_lrooil}lp{ww}wzxffp}{}urqwpzvx|}}}kvaxi[^\a]Ygtjt{{xzrt}prcp{qqlm{~kkzlqr|pryrb^ety{|tusyu~ozi{o___d]Weqhqs~~vyrumrwio^n}lngnz~mqqvxx|xifj{zwqqsqx~|}v}zirhsl^Z\aZUeqjsq~{}u}~mwdunqhsz|kp|~|iop|rvxpd``tzxxtruuyouqtrd]_d\Taniql}yxtxqx~grdlmmeoyzprm{p{}xmhev{xvpqqv}pxkkpmmeX[a[R]mlsm|~zy~nupkvpio{xrp~~tj}~gxvwysid^i~y}u~qss{vxvlstlo^\baUWilrmq~{rzvvshtcljhzw~yr|vztpu}|{smghvrqox}vtl}thgudkcW]`WRbmstnx|{lxpvupgwzz}swvukxhtvywpjf^pzx|{tqu{pvuetseq^[b_UYhpzmyypzw|uhukphj{w}yvr{uspz}ysqij~~wvrnzr|rhxti^wcehW\_YS`ly{rwx}rryv~sgu{y~|wzvo}qv{kjtyxqmlbmy~~y{wqrwrmwge{`jgZ``YWepyxnuy{kpvyzmfxyy|zyru~v||no{|utrjz~v}vot{w~~~{gjspZhrXiaZ`^W\jxy~r|~nx}}jn|yzyx{~xkvututflxztopjkx{zw{uq~x{uiw~um[xg_l__b\Yco{oqx|sky|tevzyxw{qrxzyxuiw~vvvoz{uyot}uy}zgg{pqZcsXce]`^Y^j|~~r{qvjq{yxuw~~rkstyoughy{tpslqu}v|~}utzzqixvvh]wd^jcba[[ermsyslwpkzxxtu~zmtsuvtgy|uxuuzzzy|~xq|y}zywqdm~tqkWmjXdea`[Zbnuv||pwzkzyyss}~wiqn~tmuco{xprrqurzw||t}{{xxjoytq]lp]bjeb\Z`jtqw|oxu}kyyysq|{lsn|m{jp}uvwwzt|y||t|w}wqtgkysqm\gl]\ie`ZY_hzv{twp{yyqp}uiqjxhwhkzwprrusoy{{~xyyswizt{lb__fzuzxp|}s{}qbr{p~tzpwvunq{||xcz{tu{ylq{s~z~}{~wnpcs~}w~|qsjfhp}{Xdoiajmrumtsskl}|u|tt~y}yqokqwux~qs|s~z~o^i~nbghk^ovka^ah{xs~~v{zxy{uadtvv}yy~}}vt}pkhil^khdtc_mcljjqpv}yqr|}}gx{mfm~}x{wqtwvua`o{lyuchejhhc]f|wiqktyp~wtztomotg~x~{v}z{kbgyvuqbtteZ[\jqrrryy~r{ukyu{ndgv}{|~vz{pkhqis~o{tkveijclciory{tnq~u|dwvkjtz{{wsu{wryrsdfq}|pgmqkia]g|ujsix|p|}swspntuixz||{}}p~vidl||vzhr{j^]_koqusyy|{~wtyett{rhju|v~wxwtmjhtotqw~lrmmncmdfnu|}vnox}qim{|vlmw{x}tvy{fyyovklr|{jgxqle^bu|krlnupxopnos|p|u{~{mpvulfnszvkyxf``gpktzy{yz}tvkd}uxzoossyv~wlihiuxuxpwkoruemhclt~zpnsn{rhrxwnnyzxwy~bnvmwppvysazyog_bqnsml{mz~mjlott~w~{~{|plprkhqux}pv|ldcjojw{zzww}xr~mdyryyqrur~wv|~yvfdgkxwz{s|kl{vgpfdlwxonvquqmovsmp}zzx}~{~yapnovnrxvpe{qe_fv{puhtzl|}jfloy{tx~w}y{vqknngiwx}tyzlfgpgtz|{zxtw~uqzgosr|rqr{{o{tz}x}maehrvp{{ygxmolchrrnqyrntnpsmlw|z{|v}llmkullq}{gxyjadr~swjp|rssdinwyp~~}sw||qyokleep}~w~tlkqgrz~|zvqs}{nunqpr{pnow{s{v~eehpjd{v~yy\lxdostx}~qnr}si{sptrtnfzuqsophnvmnr~}rt\]vywxjx~z~rn|p_enzus{{nozppurbumddadoy|}z{rcduv}}zupr}}mntvluvuwc_|sq`ltrwphsnXYalgdwpxnvjkjozwruzf|}rsrvzihzvmeenrb[eiY]h_^`jyvqyuc]jnw{wqox|womousovropTal\WUXaknnj}ylpkhqn}wt{}pxzv~usxjam}u~sstidm`QVbeZkmfw|j{upqjq}}y~rnutwnloktx{qpvs_YmfRaa[]bnx~xpxsdcqt}zsr}~oqvntpx}sxpTdm]STZenokjzjqppsnvt|~mxvw{rlskepvtvodkdTWdd]qmhw}{pxtvsms~~~tmuqvmeopxx}rputcUnlS_b^`gqz~y|qrvkitx~{|rz||smooyuzsuwZ\neTQYfpspk{}kkuwzqzuzsoszsvhkpkr}xsuvhcj^U_g_pvlt~}wyutqx~uv}zmxpr}}zw`ft}unptnX]v^Ycacit}txylrtpv}|~yvvppojj}}vq{eXfn\PWdqvuny~}{tfs}v~wy|lv~witkdnqv{stxpbggZ^g`qzou}~trxymw{yzstro{|uzxbcuwpqsp\]ud]baelw}ou|mnttz~{~x}rjluggtuzg]fo^QXgsvso{w}{}ugtv~x{zp{~}ofukbkry|wx|rcgj^bccyupw|{qpyh~{{}vxop~ytys`ezywstum]kqcc`bhr|~{zxk|{kmtx{|~}|{ylevt]qz~u~rgblmXTantsmuuq{ok}w}|uz|vcqtc`ks~z|qesoadgkorv{yspwxwyktz~y|kfsx{um~~|ygnf^`^emw{x~z}x~~vq{}|topxzyzz{}~wv~umd}oRGSbiifoii}x|smjmu|npv}yxshnybPSh`^dgjmqw|zsrw{{olro|zmt~|wutwja`i{e]^dkkecjy|vuk{}pyzu~ytmnxuuz~~~~~lchds~rmomf`g][ryy{{yzv~ytyukggkp{~y|}}sgdmz~vn|rgigfehmstnh|yx}zpirlu~~|y{zq}mc__dqwtwzyphiryrzyinrm||vxz|{qns~~t}~|}}sagcgx|{{{vlnk\iwwyz{xzx}{v}}slihkr|z~~nffp{~~uzrnnkgfhnsrkky{~~lnpky}|~uvxjb^^dwyx}|skluvp|uio}rk{{tzvzqouu}~{{|nagai|}|xptn_mwwy{|xw{}|{u{|slihjs~z~{}nffp|z{vrspkghmsrmk~~{|nnrky~zuvjb^^cv~w|yqouwpzzkmv{kqvwvy}tpu~v~|z|~r`feg{~{ytvweiwwy|~|wy{x{uw{~|unjikq||}}t|~pggo|w{sutpjhkqsokwy|rmtnv~u~|tjb^_dttlnvvd_nupgdmcbrlb_akw{z~|z~r]eyxsrtrwxefqprv{|yyv{stx||yqe\d}~oXfurtt|s|qmpnjechnqnm{}~xw}rq~zny}ydezqYLHMc~vq{qu}{usvrmyukmpx{ogqupqyw{yfttmgTMUNVkvrp~wuv~uhswuvyytkhrzts}}}{wtqpqlcbozqo\^jd\Y]ep{vyxtld``_]^p||~uvshpguqhijigebaaetyme\Z{zrpusgjwxnmqdj||yx}~znmlgzsu{y}wrzsu~us~~wtt~nhrvbszqsuwwxxxwwbp|f]bf`dtysuxkryuvyxgZZfsvzujzprxw{vdf}yw}oZptnopomifa[WcyxyyqbXjtv{xtwzqsw}|~ucWZh|okw}vomok\Z^Wau{|}ujqkeivn|zyvttw{~td\p}zorsjicS^nqssssty~}|x{|vmb^ahq{}t}}rwqjn|jnymhlpqpmhb\Xbusry{mcqut}{y{tjq~tf^^ep~xzttsfcgZWbmqmnu{xnkow|}{}~}|}}zwxhX`jl{~zwtbbpqmjiikqz{~sfZUW_k~z|mlka`k~w}snkhedcc`]`s{v}~~yvqekyv{~}tw~{vxyzungcejtx}{tsvrhom`bjqrkjzu|}|}sqv}v|}{h]inrxz{~xflxwspmjilsxuovkb^`eo}|~u{psqgdkyzwrlgca_ZV[pxx|rbfpmt~~|y}u{zxqg]X[bo{{plmjcmrhjrxyupy{zupyvoov~||{zz|}ytg]jsvtmpvwhjz}{xvtrt{st{uxyumfcejr}u|~~}lmsminz}~zunjgfeabpzu}|~yjeqpox{xu}~|{utog^XX]gx|vurhkrgcjrwxtu{vlttljoy~}~|uplbWcrwst|mgsvttuutw}yp}poolgcdhoy~xtqbfqqpv{uwuqkhghhfgu|u}yx~qnyvvz}zv|uyttqkc__dn{ytjqtgdhmpqpp|wr|{tsw|~}{~xuqd]jpstvlmvurrqpqu~yxvttpkedeit}~{nbkojip{vtxtmgccfhis~|}~||{rgosow|wu{{zsljfa\Z]cq}}|zrt~vmortvwss}ukv|wstywx}vtrh]ekis|t{w}|{zww{xnyttrniecdk~~|qclqkfgmw}|tnkkjip{vs{x}}|rfklekv}~}ywnie_XUSWd~|ps{qjloqsutpvqhtxsppu|{{sqoc\egdp}}{{}}}{yx{rm|~upoonliggp}vw~lftwqllnu~|wqonmlu|qp|}uq}wouphmuz{|}}xvqnhb]XXbx~sv|qjjkkmqspsxp|{vssu{z~}}|}|xui`eb[bnw}}||{yy}yuwsqolhdahyy~ytfotojggkr|}zsppqrz{uwy}}vlqpefnuwy|}wniea\XUZkyv~~{tssrqsvtnq~kr}|xtrruz{}}}vtmabe[Xakrx~~}{rx|z}upnliea`kyuuys{keopjfccgp{zvsrt~uvwy}zxnipjbfouy}~|z|tib^[XUU]qzy||{sssqpswumm}yhpzzwvuvz|z}|soi`ei`_gqx}~yrxyry}wpnmljggq{z|{~xospfpsokjjmt~|wtrs}vs}|y{smurghouxz~w}|qga^[YW\mzwy|srsssv{}wpxqoy{xvuuw|~{{~|{|}ypi__gb]cnv|~z}wu~}sligfddl{}xunyvcglkihims|}}zxyx||}woiqrhgmsvx{}orxqe]YXWW[kxu{ywxwuvy|xoqsnuxvutsuz}~z{xme[\e`[akt{wtoy|sjeccbcm{{y}|}|rkuqbehgfgjnu}ywy|y|z{sjhsqikqvz}{|}whmtmaZXXY[ctzwxxxvtuyzumrrtyyyxxxz}|}~~wzxnb]fh``gov|wyoq}~ulhgffkx|u{~~~|zz}~ypp~}nkmkjijkov|xxx{oiqsjgjosv{z~oktvk`YWWW]ksnw{{|{yz}|usxwyxvvutuyy}}~}y}paZce\Z_fls{{|pmx~vlfeddiuvov}}{rpxwonmjhgffinv|{z}|{lhqpfdfilpv{}|lgqul`XVUU\hhdp~|yy|}{uu||x{zvutrqquz|xw~x{~p_ZcaWVZ_emwy~pjt}wlecbcjuslu||}}ztqtrorojggffhmsxvx~{}~nirrifgilpu{~z}nejtqe\XWX^jiblz}zy{|{wvxxyzxwvutvy{vtzx{h^ef\XZ]agow~{{ymo{wlgeejtukmz~|{|~~{wvrpwwpjhgffhmqqo|~zkntleefgjoty|||}mgmupcZVUXbibanz||xt|xuutrsuwwqt~zv|wc]daXWY\_fnv|}xmnx}vkdcdktqhny~}}}|zxtkkvwohfedeglonn~}~yknqiccefinty|z|ldgoodYTSWahaanz~~~~wnwxtsrrsuxxsv~zrt{xf\baYWZ^bhqy~|pjpyyofbciuvlo{~|{{{zxxujboysjffghkptsq|zrnsohfhikouz|w~rfbgolaXUWalhcmz~~}opxuttuwyyts}{~vqwznacc\Y[^bgow}wnnv{ujdchsvmmx~}|||{z{zm`kzwmgefgjosrpz}}tormfeghjntx{yshackkaXSVakfbmypp|z||}{rrzy}}rdjwrx~wpvunmppoqvy}~x{wqr|{vxww|}||}kr|}~}}}|yyzvf_p~xvvxz}zyxdu{{urz~y|}xtttrqrtuvz~tpv{||rkgltupklt|mlzokjkmoqsxxldr}|}~~wrz|vwzvkdnyy~{}~xwz}yqs|tjjuwpjfdhquqmlr|}ttz}}|}}ymj|~~~~xmwymem}|ywvxxupotvuwvpmnonnoppprwxqopr|}xtv|~yx~yli|}xtrruujbkz|wvvxyzxrow}wppsslehu~{y{}~~urqq{}~uojfgmrplkqxwqsxzzz{{{}}xlftyr{{xxumegv|xroqrtwsmlmnooprrruywrsspt}yuuy~~zy~~ymfs{yzxmekv|{wttvwxvqpx|vqqsupifn{}}~{yz|}~~wutpsysojhkpqnlpvuprwyzz{{{|}{qhn||tx~{yxsjelz{vqooswuolmopqrsttvyxstvsqv~|yuv{~}{}|shjx}}|ukjqx{yvtuwxxtrv|ztrrtsnhhp}|{~{{}~xturnr~ytqlkmrsqquxtrvyzzzzzyzzvmiq{{w|yvusohfl{}xspqvzvpopqqrsttuvxtprurpu~|yy}~|zvohkxwooty|{xvvwxwtrw{xsqqqqniip}}|||}}~~}}zsoqqor}}yuqnortssvwtsvxyyyyxwwwtnkqz}z|~~|vrpomjin|{vrquzxsppqrrssstuvsopttru~}zy{~~{wuspknx{trsw{{ywvwxwusuzxtqppppnmr|{|~||}~~~~|{zxrlmqrt}{xuqoqtuuvxvsuxyyyyxvuttqorz{|~}|vqnllllp{}yuqtz{vsrstttuutuvsnnrttv}~|zz}~{xtqonmox}wuuwz||zxxyxvtvyxtqonnooor{}|~}~~~}|zywunikoqu}}{xursvwxyzwuwyyyxxvusrqppsy~~}|~}xrnkjjkmr||wtv{{wttttuuuttuuqmmqstv|~|{~~~~}zxtqnlklqyzxxy{||zxxxwuuwxvrpnnnnopt}|}}}}}|{zxwuqkhknqu~}zwuuwyyzxvvxyxxxwutrqppquz~|{||ytpmjijknt{wvy|yvuttuuuuuuusonpsttw~}{|~}}~~|{xuromkknsz~|{zz||{yxxxvuwywtrqoooooqw}|}}}|||{yxwsnjkmnpu~~{xwxz{{ywwxyyyyxwutrpopruy}|}}zvroljiijnvzx{|{xwvvvvvvvwwtqpqrrrsx~||~}}~}{yvspmjikmqy}}}~|{zyxwvxywusrpooonnsz~~~}|||{zyxvsnkklllou|z{|}}zxxyzzyyxwutrpoppqtz~|{||yvspmjihhjoy}~~}{yxwwwwwwwwurqqrrqqsx~}~}}~~}|zxvspmjiikmqz~~|{zywwxyxvtsrpponnpu|~}||{{zyxwvsolkkkklov~}}~~|yxyzyyyxwvusqppqpqu{~}|{{{yvtqnljihikqz}{yxxxxwwwxwtrrrrrqqtz~}~~|}}}|zywuroliijkmr{~}{zyxxyywvusrqppoorv|~}|{{zzyxwurnlkjjklpw~}zzzzzzyywvusqppqqrv|~}{z{zxuromjihhilrz~}{zyyyxxxwvsrqqqqqrty~~~||||zywuspmjhhikms{~}|zyyzyxvtsqpoooprv{~|{zzywvuspljiiijmqw~|{{{zzyxwvtrpooqrsv|}|zxxxvspmkigfgims{~}{zyyxxwwvtqpppqrsvz~~|{{zxwusqnkiggilpt|~}{zyyyxvtsqpoopqtw|~~}|zyxwvtsqnkihhhknrx}{{{zyyxwutrpopqstw|~}{yxwvtroljhgfgjnt{~|{zzyxxwvtrqppqrtw{~~}{zzyxvtrpmkhggimpu|~|zzzyxvtsqpooprtx|~}}|zyxwutrpmjhgghjnsy~}|{{zyxwutrpoprsux}~}|zxwvuspnkigffgjou|}|{zyxxwusqqppqrtw{~~|zyxwvtrpmkigfgilpv}~|{{zywvtrqpooprux|~~}|{yxwutrqnkihgghjnsy~}|{zyxvusqppqrsuy}~~}|{ywvusqoljhgffhkpu|~|{zzyxwtsrqqqqrtw{~~~|{yxwvtrpnljhffhjmqw~~||{zywvtsqpppqsvx}~}|{zxwvtsqoljihgghknsz~}|{zyxvtsqqqqrsvy~~~|{yxwvtrpnkihffgilpu|~}|{zzxvutsrqqrsux|~|{zyxwusrpmkiggghilqw~~~}|zywvtsqqpqrsuy}~}|{yxwutrpmkjhgggikotz~}|zywvtrrqqqqsvz~}|zyxwusqomkigffgilpu}~}|{yxvutsrqqrsux|}|{zxwutrpnljhgfggimqx~}{zywvtsqqqqrsvy~~}|{zxwutsqnljihgfgikot{~}|zywrsqqpptrvu{~y~~|tronloqsvkg`]cmpz~}xovwgg}srpfkt}tsinxmgahV[ahZexolcz}z}uwvwvv}qr|{t|jz~{}}ztxseqaZfgYQXnnbnwzwpxjlyyunkhpu~pn}n{ggjxztpnljfruYlxuoi|}w|okgkviopt~vhwd~nZaZnmg^b]lu~{utvwz~qv}zwxwfpxo}uzvvw}zxttgl^uY^^Yppml{sv}n]qilqkv|scu{u|x{^wyovpuu\rnp}nyzor{q\wbP[Z[srovrxt{n|vewz}xs{y~txzxw|fokllgu{a]{o\KS\f[lfvsmyhw~zcutdwy|ru~~cus]h~yxwzlvdj_[nd]q\_qegqgmutvstsusq^f{yvbytz|`_kmxrcliotV]gOjWVd{r}v{y~vkpzurhwjx{}{z[\bzuxkvifOlMpOTPgt~r}t{~{mmnwrd|yr|ty{z{x]`^grl|my]bXX~WaZgyutg~|od}wwwerqqtr}}|ne]fal{yxrlzjehZdhe\s}|}ni~{zuawpmypo~ov|p{nfVgWeqqukkZuigkeqxty|u_wsz}fzxkwyl|}nvus|}{||}pcVhbaltssplbvhdkakspw{[{|u}lzrfsxpz}tmt}~yyyxcW`_ao~xtki]uf^hgnoor~aywxl|xfltnr~tnyv{zt~n[dccp|ytjiavlZfdnsut}kq{{|nszgepqsyoy}zxuyyz__\^mxxkf^ox^egntxsuri~}yzsmmflno}zztr|qve_`Xfp~uldjgcdjswxt|ya}|||~~d{tilrq{wl|puw}vxmZ`W]j{}qfa{vbihqqxtv}eqyx|{nr~okomquwxwvuzryv\]\Tfpxmco~ceennvyvzq_~z~|ewskosl|wr~u}wssyhTaVbl||rgd{sbilootxvfpyxz~rlzkhpon{y{~{yxypvxZY_Zjrynhlfcjprszx}z_|zyz|grohjyny|sxyy~qpymQ[Zaszrigxcgouqtuw}pdzxxw}wjumfnyp~|t}yzxuykoxfP]Wixpjmx`grttuwu|hly|xx{pkumfu{y|wt{}x~sqglu\R\\r}jgptalsrtutuzex|yzyyzlqvlgzyzpyv|~wt|heopVV[azygkvibmssxwtwpfx|}zzshtrknu|}s|w{ozubfpeQ]_ondm|egsquwwvygt~t|{zxlkspiyz}{wwy}xl{hbjpZW_cx~iiuqcquqxzwwqcuxzyzsformpu|s}wyktsaeocS\apqgs~glwqtyzvvcu|ryyyxjjqpk}{}szz|thwealmWV_h{~jp{ogvvqxywxlfsvzxyphppmux{t~}x}guk^hp]R]evomzxeszpuxxvqbvs|yyuinpnpvq~zvlmo^dreRXaotjy~gmzrtxyutbx{q{zwwllpom~ysz|z~~sis`aokVV_j~zjxlj|sryztufq|qz|wxmkpoj{z|uv|}z|vhub`noWT]eylxriztsy|tsgo~owxxokopkyyxxv~yzyitd_lqZS[ctoxvi|urx}vthk|quyxqknpkxyu|t~wy{jsh^js^RY`msv}jzyqv~yrlf}tq{wvkmpmr|psvy|mon_ereSW`f|zsmv{pu|}snc||xn|xwmknoo}p~t}x~~yrjp`bpkVS]cx}suqssyuqfqzpuxxqkmolzvuzwvuzgpf^jr^R[amx{n~xsv~|rkgzvo~}vvmknnt}qsuxynkl^cqhTUae|}utusuzuoev|yovwxplloo~tyxy}vsxgnb^kq]S]bqx~oytw}qkhxvn}vvnjnou~rsu~}vvohk]aokWUce~uxtsuyvogq|wqrxvrlkpp}yx|us~pwgkc[fqdU\en{zs{{sw}slg{wwmxvvoklqtuw{~yv}zosek^\jp_Ucgwx~rxtw~qjiuvk}}uunknrzvt}tzz|sqndh[_mm]Yijvtuvyzohm~utlzutmkqtzs~qzzlrgfcYbpi[_mpwyz~sw|ulfxvwmruvqllsxzxy}yr{|z|vkpbg\[ipd[in}yuwuw~piirwi|}tuokouzt{qyw}w|mnicdX`on_`ppw}ytwzwmgsvvonvvrmlsy}zx~ypyyxxuiocf\[irg]lp~zxwwwpikqwh|}tuokpw|t{oz~v|vyjnheaYbpm`dqrz~~}uw|ukhwrwmpuwrmmt}|w{{urvzwwojlceZ]lqe`pnz|uxyzmhpwttixvunlr{}|wzo}xxyuthmef\[hqh_ln}}}wwwpil~pxg}}uvpkox}tznyzu{ttjlhf`Zeql`hoszwwrjj~ozixuxrknv{uzow{u{ttkkjga[com`enn|xwtkk|n{ktuxsllt~zvypw|u{usmkkhb[bnmacmk}yxvlkzo{mqvytlks}zwzru}|u{vrojkjc\`lncalgyx|{mkuswtkxxwnjp}z}{zvr{}vyyqqkkke]^jog_ke{yzyokqyq{g|wyqjmzy|x||oy|wv{qplimh^]foj`igq{{{wskon}jxwztkkvz|}wzpv{yt{tnoiklb]bmnddkf~{y}{mmvrxukyzxojq|{}}{wqxzuwynplhnf^^hoiakex|{{xrlq}o}h|xzsklyy}}xzouxwtzrmoflm`]cnnegki}|x~{mmwqxtkyzxojq||}}{vovwtuxlnkeog]^holdmgx|ynjputosyqvqijw~pk|{siiW\acbjhbkeioc^bmqkjl_xxkkkaicbf`[artryzxi||ntutt|sszq|~st}~ygYy~v{y{v}vd^f}yt|}{sowh`|mn^V]`_ch^ce]kf]^golbd\mpYTftzjicZYftmts|xz{yssiuutxzyuzx{tu{zl_\U|w}~x~xtso{s`[d~x|vyzjfd{o\````g_\fZdi\\ajjbcckzyydSVj~|wqfahwqtuyywxtrrmuxrwxx|rzzpvjhkldZ^\|}}{tunyub\e|~z~vw{xugigxceddcib]j]fn`_dlmfgfnssubSYm~xlfm{rwvzyvwsrsnxvruvw||p{xosihkkc\_^|xusnzsa^iz~wzvxphhjvefeefk``l\ln`afnmgidu}|otq]T]sxlhtzq{s~~uvsu}qrmrutvx}wopuniilja_\e{}swnszl^bp}{w{t}jidrnigefji[ke_ugadkplifdupwiXWczvlo|sxwtz{tuptsmqytuuw{}n{{lwlklnha\Wxvwrp|sc`hx|x{~|tpldgyolghkm]hm[uncejpnkf^wzrzq^W^nx~rp|tuyox}sto~xvnlvuswz~pvj~nmnplf^Si}|vvn{yiaepxy{y}}u{wngasuqkklpe`t^lwffioqnj]i|~rxxgYZcqv{sz|r}px}wvsqswtktywstx}xltquonqqkfWYtx~v{pu}sedjtv}z~~|ysrnce|xqlnpp`lp\xrfhlqqof]sy{qztcZ]grx|wywm}zzssny{xqkyywruy}tomxronppkdT_vwuznv|qeelux~{~zvs}qmbiyrmprp_syozpx~zrz~yzjdjurhU|wZRM\mab^\l~zxtx}}kdwututogWby|lnu[aYaibXZcox|qtqmh{xkibhxeKhzmXWY`bcYRivr}{}ymwthagski[|iYPUi`^]Yf|{xvpnonifwttwurdhmivsk^Yd`VT]hty}yrr{tzritqqnooe^[[gsgacfebS`vuu}v|rryrikv~to^hpvpe\kmac^bv||woiceipm}}~yvxyyrizmslq||vejkb[_iu|}|twtmztllvrtrzoaWX]eixropon`ay}w}ry}unwtlkvvkk`iywzndnvhjfgw~}}zojbchppyyuwyzvj~wgqpu|jlph`clv~|sttm}tlj}wsvtym_VX`il{uqrrqcc{~x|}pv~}vowuonxtjj`l|y}pgrwkmijz~z{wmhabhpo{}yvx{|vlrgqpw}kpqicfnx{zrtpnztkovvwvuj]VZbjo~wtvvsbj}}wy}}~ny}}rqxuopzqkh`sx{|pkzumoin|urle`cjomzzx{}~qrklrpyzouriekq}~yvrtjv~u}rl{~uzvzrfZY]dfzyyyymcv|xvv~sq|z~~qxysot|{pmbgwo{qyrtnlw}zwxojbbgkjsz{~wluksoszuxxpiinwt{tulo|o~xpxw{xvwn`[]``juds{wr~u{~~xp{yz|uy{vqrxvqedrgvz~xxsnu~z}s{qldbfifi}{kz}nurpt}~}~wolnsuwwvnkynpux{{{u|}ug__`__zjo{wstxu}|ox{xz||x}ztquz}umboke||ss}wq{tphbdhgctyrqssupqtwqpq|~n{wsisvgy~}vzyxzpd_``]fyjyzwpxsw{~vqzyxyz|~zutw~ztgeqej}uwwo}rnfbehfeyzow~ottpru~||sng^ki\rzq}yjfpkl||vw|}{|nsmb]ktglpw~mmxwyysqids~xxux~~hkzqfxtdvtljdbglkl~}{trwx}yv{zwi`n}xmYptnvxupnjdrtgxzvoumjyztluvfdm__ekufbmoqtx|hcekw}}yoofvvyzblpfd^[_eggvyzpnu||~u__hxnsqtvuokh`hthnyv}qdjqo|y~qin}szrcgito_gmnru|}yg_gpy|{~|i_jqywlxohd]]chip}xxomv~}{|r^_n|tw{yunldcuqj|~qwreest~{vx~pipz{|jhjrucenptw}yyzl_gr|y~uwi^hv{lwrie_]cijo~wypnw|zzyr`_oswzyvplfctuixprredtw{uw}~~}pjq|zlgjrweenqtx|wwzmait~x}}}qth_kw}mwqie_^cikp}}vyqpz{yuxq`brux{zvpmdewriy}mrqehvwzuw}~~}|olt||~kilwuchprvx}swzjbkv|~wy~wpqebpwzqyoje_`fjkt|ywwqs}{~|yuyk_ft~z|zupkbnwkl}vlsmfqvw}wuy|{}wnny|yjkr{lenqsvwxr}ueeox}x}~vprncjsttyumjc`ciiowyw|tqv~z|zs|ub`jw{}~}ztodhwnguzlqsinuqyuw{{}snr~qms~rfnqsuu||p}|iejqz{xzt~srtikpm||{ypmfbchimtvxzyst|~{|{{s||h`enz}xrgfvqen{omunovov{vvz{{ypov~}{ps~{imrsuvvovqfglt{~}vuvznuojqlo}upkedgijqswx}wtx}xy}~}xttcbgp{}wncpwifuwiqtovsn}~xuwzy~~~upqyz~xrytjs|xzux{|}|{dw|le|qvlkmhjfcdhimrmjp{ncrhqs_gxmglsy~vt}wmfw|rv}fuykoqmsqrspptvw{uos`Qi|khpbZrujkquyyzvcwv`kvwnlkffc`aefiickxt_m|}zbfwmeiov|pu|qk{uv{yvhe|wxqtqqqmlnqoqlky{nmmZYs{ucu|omptwvuwvey|z~rvokibt|urjiea`bbbeco~|niooessi|xllqv{}}xsvjtvpzsnrklkj{ywrsokklkklk|xd`gd`umoypqtvwtsz~ny}u{vmijdhjuxuojhc``a`dfm}zqbepnshsrmpv{}|}u{om}qsxhkihoq~zysrpkiihgllz|}ydZbjgvvjsqtvvtrvwvt}|tymbfbhpy|wqkjea`a`dio}t{p_`nsvkowoqvz|{{z|ql}~rpzyfeggqv}{ysqpjggfflnzzxwcXbmmzzkurtvvtqvzx~s{{rv|k^cckt}~yrljfa```fkqyqwm\`osykrwqrvz{z{z|pn|ppzubdfjtx}}yrqnjgfegmn~vvt`Yenownustvvsqyw{zqvouye]ceotyqmkfbaabikutpuf[eqq}}j{urtwz{yzxluumrzm`ehosy}upplhgffjlo}sxm\_kkqoy{ttuvvsu~{rsv|ppyq^aejpswomjecccgjjznsp__lloqs{rruyzz|v}ppznnxuccimqn~xqqnjhhhjlgzuwuc_iggzwuwuvwwuu}zmyus}qowvc`gkmiy{qolheeehjdu|oqtd`ihewysustxzz{tyto~|pmuyhbjoqis|rqpliiikmenyuzlahhantzvvwxwu{}omzq}tnryl_fknhkvrplhgghlfgumtn`eibi}t}zutvyzzyq|owumoxrbfnrlh|wqqnkiijnicxsvwgdkdct|zywwyywxxjstt|pmtwe_hnnequrpkhghklcn|plsi`ggamuzuuwzz{vsyp{rloxn`foskkvqpnkiijnge}|quteejbfy{~zwwyywyvkwruynlstb^honguurplihhllcq~{oiph_ffbpv{vuxzzzwtxoz~rkmvn^cmsmmwqpmjhhinhe||opsecjeh{|{xwxxvx~ynvrrznioscZdnpkwvrpliggjnfo~zpfli^cieqx~~xvwyzy|v{ou~ukiqq_\issq~|qpnjgfgkmfw}pioh`ikk{y~ywwwvu|vuvnx|phhojX[issw{rpmifegnmkysfcia_jmrw{wvwyx{}ro{ylgjqeXapywtomjgddhokr|rfikafpp{|~zwvvutz|xymr|rgdjkZVeuz{spmifdfmqn}xsf_fc_kqtw|xvwxxzsmxymefngW]o||}vomjfdchpnq{rdejbgrt}}{wuuutzzxlqzqfchjZUew}~somiedfnro|}wqd]ecamrvy}xvvxx{~qmywkdfmeV_r~}~tnlifddjqnu~}yobejcjst~{vuuuu}}|uksymdbhhXWjz|}rolhfeiqpp~~zun`^fbdopy||xvwxyymr{rgdhl_Wfx}|}qnkhedfnok~~zuiaihenpuzwvvvyy~{nnytgbdkaT_rzywqokhfgnqlx|wrh]cfbilo~~zwwxy||rozwkefmgX^qzvvpnkhfflohq~{xnbfkfjll{}yxxwy~vx~rnywjddkgX[mvrz|tqnkiinqio~yul_ahdfgft}yxyz|yzvpzzngfll\[mwrv|romjhhlohi}ysfclihifq{yyyz~vt|wnvzofdhl_Wftqpwsqnkjmrlg{|wqe^fhefbiz|yzz{{w}}qv}tjfinfYctto|wqomjijpldr{wmbgmijegw{zzz|{sv~rqzvjddjiZ[mtntvsqnlkpqhkyuoa^hhfebm~{zz{}xxyqy|qheincZgvrpvqomjilpjdv~yujahmjieiz{zz{~ysx|pr{thcdjgY\ntowvrpnllqqhm~xtm`]hhgeco|zz{}yyxqx{pgdhlcYfvtsvqnljikplev~xuk_dmkkgk{{zzz}|vz|ppytibbgh[Zlvryvromkkprjm}xsmaZejihep|yyz||{yot{rgcdjfZ`tyvzqmkigiophq|xtm`^jmnlmz{xxx{z{}rlsvkb_bgaWdwz{yroljilsql{~{zsofY]ikmlp}ywxy~~|qnwuib`eh_[m}}~rmjhfglrnn{~xsnc[dnqrqy|xwwy}}tjnumb^_edY_u~}rnkihkruoxy{rngZYgnppr~ywwy}|qltuja_bhb[j~~slihfgltqo{|xrndZaosutz|wvwy|tjmtmb]]cd[_v~qmkihksvpy~yzqmfZYgprqs}ywwyzoluth`^bga\m~rligfgntppz~vqlbZcquut}{wvwzzqiotj`\]dd[cy{pljiinutp}{zwplcX[krrpu|xwx{vlowpd__dg_`t}pkiggjrsmv~z|soi][jturszwwy~}~vlltqe^]af`\l}~volkjlruory}sni^Wcprnnz{xxz~{pnvuia_bgd]jz|unkihjosln{vqmbZdrtomx}zyz}z}zolutia^`ed\evx|{romlmqtmm|vplaX_nqkgq}z|{~xtqpikdb`n|{y}vtpln}wkzqu|ma`vvxwkjz{}}y~~}ois_LObomgtzgqhlm^X^bl~xs}wwpiwq\xxtshenqrvvukci]TX[Yis{xlywooquu~vvwmryojomwddxz|wr{slw}}mfae\b{yxz~kbbihejooev{uxnv|~slv{upovuhnxuvtcpw|wyoltr`cr{xqjs~tyxh~y~jbfighinzznnjoyrv}xzqjhffko{|zuluiecb`hqq}|rvyrvmjorutsys{}nxsju{vsqh{~{oym|tyr{{~r}wrehZjpxwxqv~r|}vtdibl{vtfnej]o~uzruxu~lrtvfppytt}oqr~vpqgntkfqmx|~u|vuy}wqrwwwq|x|~mulkiid`zuxx{r|osu{vkkgjcz{{nqrXZ]p}~z}t{}}|st|ul|igmwy}}~qjqfvzqyj{{yu|zyv~wvtm|w}hknnf_y|wuqz~~zz|s|~|sippocz|{rqbhspx}zzt{}}z{tso{uumn}x||z{}~kx|~u}ssoj}nuzzyvo~~yws|nx|unpz]Z[gu}}~t{|uzw~wp~c[_j{sopbhdlrzw~}uww~|xv{xyqwlsuy}~v~vlxkjmuwu{{xw{wu}yw~n{|zwm~zxz~~~|{tgqqjkw~}yxz}x~z}pnuzioupq|~}ruwkaw|wvyzrw}~|}rtywu|{o{xw{loosqpw|~|utyu~~zspuwxilnvz{}wxnojfdo~x~~vvqqqx|~vsqnjjt~u}nb]mucox{|ztw~zuhfwpw~yypuqltx|xy{y}zqmnqww}zxqy{x}odwzun}zusyzz~yz}z{wqtp}~{}snmlivt^g~txqqrts_]lu}{t{zsyt{v~ppr}ju{}|vz|wuzyporez|{duyk^\irz}~q|uyzxwsxsmxottpw}jhhlll~x}rwzzxw|jpxtu{juo}rr~ooqjeyw{ysp}|yqrzwmq{m{yx}sm{{m|}z~}uudgrvspt~{pormltapu{x~yysy{kt{okvus}hnmkorsxw|npyztw||qz|jp|tyrvxmmyzv}v|v}wz~srr||zsm||s|tulgZast~|y~~vvns}z|zsy~|z{wzunqpfyqs||rwxxjpz~{zxtr}}|}~ylrqqowrxxrxz~}zu|ww|uqyztsm]flvzxu~qnrzvoy{|tw~x}z{twrio~tuz||tsxnx}r}wtu}{~}}mprleg{~z~}~~~twx~|v|uyxqir~{}xiggiu~}z|zzzxs|luwtutszzv||{}~}~tgu~vsx|}xurthvynzwyy|~~|uz|v{ogapu{x}~~zzytrskzvpv|nzvykmpov{xvt}y}xxv~wqtoox{yzu}~rnzrp~}z~|}ttlrsp|sy~{~zpv}~~lefxniz}}yyz~{oxrl|torqxuyywquuvtuu{{w~xs{{|u}wstmlsu~vyx{yw~pxpw{y}}tpqmwn{}os{||stvojzpev~v}|~w}}v|unwmhh}sv}{w~y~}{tszpu{sy~zy|xznjrrt|z|u~utvyx{ptvn{|y~tronxw}jq~|}ut{|zvuoctto}|vxq{lfd{~mw}r{|~nszp~|{|{|~|w{{qpvpnsu|z{}vrs}sx|qmttr~{zrnwukxzvsx}xx|||z~|vw{p}m~||umwygag{`fy~~wwi{w{tyws~|~vp|y~ytwl{znps~w~wf|yp~nzwjanz~s}tr~o{j}zpmtv}u~v~|qp}~~x}pk~||vyof]w[`rtsu}~~ynpt|yzw{v|pzrtki|it{uvzzx~~sw{sozq{tux}~~yzqlrhqliyxw~v|t{rqw|}~~~rtv||l{cmr}wyx|z{{y}xvx^pxsnq}w{|t}stzvlrrm{vyrlrx}}vw}qzuzz~xymyuinbqpl{|ru{{}~yuvmu~|}t~nnsppgstiy{xu||}{xyvm~}|yx~~mttco`ptewvzvwvtw}{utryx~{~ly|yvctsvy{x}w}xvpshiw}{}~sr|~sx|rpvnnv~rzzlmsxs}{xlzyt{~toynhhwjr|uphxy{vu~m|tv~xxxvxpwkjlllxy{zvx|s|y|yk}{~u~~rywfvaf{hgh}~p{{|uy}xyysnysqvypkpfquxu~}}}~~pmll~tyz}~x||uuykisukqq|sxuol}wuyvpy{~~wvuttlhsxozy~kk~{~y{}{|q~xntmywy}uuznnomfmuqtyxx~zs{{s~~s}zyu~oofeyf_bxxsx{~z{~rz{uuvzs~uivojlsoqx{~trlvp{{~|}~}zw}skroipqirhxtmv~~|~~yqryz{zwxwllxm}ydmv{sx{z}|}~}uspjxpt{z{{sqnrprmmtvmx{yvztwux}u}xzvznqetue_ewkqp{wu~zr||~s}qvuklrjfkquz~|z|~~wz~wov|}soljutnsimospkroj{yrxujv}voszu{js}j|{rbhv~xTwypppk|}wunyno{|w{yZioe^YlyjipWx{zw_ttodPXhurnuwsws{l]y^ldbys~nju|nwym~p{}tdeSkuomsq~}mnws{~hjdyuspvxlgeuzjxu^{srprzyv\`_l||{ibqmX_bmceh`fk`ptbdv}zytxyiy{w~zyy|v|~t|olbVHVfpz~lmnzfqqer}kpzd~~}rw}uix^njcr~qpvpul~}zzjdV_kpwupefZocagYgna^eo[pxxzzzzsqyvpvt}{yzwvmZYVN`kwxrgx}euhhzwlmyjqyv}rhoirbhx|ptjt}mrbcVcn}xpd`[v^abYdoicjmZv{}vypr{yq|zpx{{}w~wkW[LOal|todwxhtin{uotfmxxun~ggndj{}|puhowq}j]dXblz|shb[y`[`^hkhjppYu}{}orvytwpx}{}vryvhOXSO_mvohwanlqywtxokyzwhmdlji}{uppm{kzhUcZbl~~ufg]vmV`_hnmjlu]h|}uutqzuzy~v}uyurnslSLVPgonokqdgkvxwz}{b{z~fptjlslyv~my}i|uy|tw|tW\a`q|}nd]fzXZ]dknqoth[|{}oysyy~y{srx~yqvelmZIYRdtspjzbiisvx}|dq}|}|{kltikwuw|xoulxxvxysntt[[cav}shdb{_W]cklqrvq[}}p~zuz}~wssz{{mzvagl]KZXivvqm|_fkww|{et|~~zxkmtnkxuw{{nwlyux{xokurV\aeyzrgck}^Z^fknuqwm`|~~xo|xv}|x{xuxzjwkZdlYNYZqzwqpv_emwx|{b}x}}vtioqqp~p}up~zl~qx}ekxlU_^n||rgdwwX[akltxrw]n{~y}}rsyzwzs|x|wny\[kiPUWbr~uq|idlttz{mkyz~xtpjoqmw|qzxxnxzw{ujcsv\\_fw~~njr~bW`ilq{txgaz}xz|xvrx{yvy}x{jyeZipWRU\kt~xs{qaluvy}qgzy|trknrps|kz{}vrs~t~vqcqzbY_cnx~tktmWalln{vtm]|yt~wxquy{ys~xw}yornZesbPTYcpwt|eguvv}yc|~~s~vvnlouolt{~|y~zo}uzx~ejzs[\_eqylq~d\lolu}ts^l{~rxzyvrs{v|vx|~kxd\kpXQW]etx|xzcpztx}pf{tuumlqst~h}w|}wq~t{~xywdmzm[\bfuz|kw|a`pnlx~uq^sz}pzyzurs{vx{w~}}|~zju`^lmWRY^gvyyzyeuzuzzlhzr~uumkqru}iy|~~xp}u{|wvuckxlZ[dgx}~myf`romwwp_q{|qvzyupqzx{yy{~{y{gpaZho\RY`gzz|whs|vyzof~|ovvojnssk}}}~zozwz}|owcesp]Zcgvous`qrmt{qdf|wpwxqnvyuu|{w}yykgiW^leUVbeztuk|wv}ugt~sqzurkjrsys}~|vr}z}|{mqj^isg[`kpwn~fivnp{tk`yz{nxxysorz|zv|~{yz~~}qqajYYgj[Vbhvw}kzzuzvjk{xm~ttlhpt}r}}zq}z{y{{okk\erl^`np|mngwpqxwmasz{przyuopz|~xy}{v|}~}nrah[Xfl_Wckwx}n{{vzvjjzykuulhou~s}|zp}{yxwynki\frl^aoq{nnmxoqyujbwy{ntyyunpz~|vz{wwz|{~xlnagXZhk_Ygk|vqyw|shpyvj{utliqvzw~|wr~{wrwjmc^jsh]gnvuwmwvpspfguyi~wyrmszztyrw~ynpgeaV`lfZ]jkzy{wyymi~w{luuwpikszs~~rzxw~}xsqkj]dpna_mktxt{rs{wiewuynqxzunnv~yw}xuxw}y|slmfeX]ji\Ygd~{|xy~}nixyzqnxxtkhpxs{twzyz|{rtml`bnoc\jf}xv}tsw{keqyvtj{yxpls{z}}y{zsyz|yxknig\[fkbWbbpz|z{skpt|hwxohlt}vt~}rxu|}ssqoe`iqk^chj{~}~zttrgi~q|jxw{tlmu~xvxryv~yxqllicZ_ji]Zf`~}{zolxxyulzyvmhnx|tx~yv|{v{~{{qqqmbakqh^hfszxvuogmo{h|xzskmw{zvxqz{u}vvnjkjaZakh\^fcz{z|nmzuzsmyyulhnz{tz~yw}ytz|xxoopkaakpi_ift{xwuphln|hywztllv|{xypw}zszurohjkaZ_jja_ic|{}olvxuxh{xwnhmy}vz~}uz}q{yx~vvpkomb_hpmchko~zwu}vijyoypmxzwols||{~upyysuvnohfje[[fmh`jgv~||yulom}isvyskjtzz~v~{qs{ztywqqjjof_bnrkfrlwwvmhoyn{e|~xzsmp}~wx|lsvspumljcgh]Zbmmeioo|y~zmmxoxrjxyvnjr~{yy~uowxtvwnpjgnh_aksohsnwyupin~k|gvx{uno{xykptrnslijafj_[bmpikqo~vxpbakyelg]tjif_asw]YqfPSQP^WPUakkhtpxyvwu}{tWYpirswnssw~vxx`al~~nimbkmaaiuyu|y{wxnysfifp{neiwkynmwwm_`x_U^uw_]ONYMJQ^ebdkiz~|~|nZTi}x}lillyxyrs~yzyc\ep{tvpgwnfku~}|~tvmhgfvrdbjzrkvcvwpnd^hxbRZfwzs]h`RS[ddahbsv{~{~t}}qk^U\zrspu}xvouzxk\bgq||y|iwwijr{}yxut{rrjwyjciwxjxhqzrri_`vxhUX\izmtr^[_ghdhblv~}}y~|xxupdWUjyvssyyo{z~~zs_aahsxptmlpy}z}xz|ryvxxmt}qehr|mtqkzuuoc^i}}zu\WW]lqlaafkhhfb}~t}z|{{u{un_U[szywq{{ssy~yzh`aajwymzlot|}{|stwt{{wn|{lflwxmwlrzvvmb`q|~}zmXWV^nyzkefkmkldhxw}~{z}z|v~xtl]V`vyzwr{|svy{xxe`_akywnzoqw~~{trwq}~wq~zlhoywovmtyvvlbbu{~yxiWWV`qy}niinpnoekyw~z|{y}z|~u|wsl^Wbx|{ys}~uv~yxvxe`_akxxmrsx~t~{ouqz~{q}}pinwypuorxvvndct~~|utjWVV`qwumlptssjjuw|xx|z{}svuplbZay}yr}~xs||vswj^`ajw}yq{utw~wx|zopvu~rwwllsztrsnuvurieo|{rlm[UY`pqpnouwwtizx|yx~}u|}|y|zytornlf_bvzut}}uz}vqrq``bivu~n~vx}}y}suvplxvxr~{olqxwqsoruttmhn{|oil_UZapw|uqqvzzylv}x}t||u{~{yz{wtnomkhbhy|vjgg}y}vzxzyc`jy~ztetxd~suztzyno}xvtsoxuzzzy}ri{~v{xuzZWp|iTPfQ^sdeiry}}q~~~x{yq`k}gdodplfei}x~~uyt`alyyi{vhw{vmxxtq~kmhhwvnnt{{wvxyzytmo{~|zu\UathgmPfm`bfotyskz|zv{ws~wofeuy~vltifbpx{xt|qlsg_elyzuoy~xxritz}|opfpvpkntxxqsutuuqmr||lodV\evwoe}oiimtxxjs~vrvxrz~~}}~uroidi~y}ynil}{|v{{yktn`bdm}u{o}~vtsjptuwxkoxtnntx{srzuvwsmkx{lqjXXZg|kznlntxzkpu|ssxsy{t~}zvtmdan~rlkz}vyykstda_dsyo~}|rtmnsj~}rmwwqnsv|xo{xuyvpjn|~}zror_WVZlt|vpnsw|ri}yt{ovtt}qy|{vvrjacs|}rkwzzw|tkwkb`^gw}zy{ssqlskk}nsyupptwrr~vwyunjryzkrl[WU\puxsrw{}no~vuunurxzn}~x~uuphafu{~sn~{zy|~olvhb_^iy~x~{~ppmkshn}ovyuqruyottwxtmktxzhqiZXV^rx{vty}~nr}vttmsqxyn~t}rsnhchv|to~{{y}|ojtgb`_iy|||}nkjhrkmruzwssuxrpuuwtoluz{fkhZXX_ryxvz~rpxr~xkpou|p{}sz~oomifjx~xpz|zxxerkbbaiw}v}yrfheormwqyyurtv~{j{zqvurpu~~~}{}l_i]Y\apz}yy~}o}}tvnjmo|xv~{m}riljjmy}}ss}||y}gioddeit}x|uzwcccjwr~~rwz|~vmvowiS^[X`gwshj~qcYbUJQUekitv~k{vilmot}vgwjioeer\Xd\^cht~|xrtvb^`hzxl`imfkX[nbdikmqz{{zor~wu]hfbjq~ygi~k^WcZPV[knj}qmylyotvy~n`iozfdkRS]UX[`lx|yx}ytxuxxeeiu~o[cu{^co`beghmtrz|wtyoht{scpjiouvhftogm\TX_nhi|~zkq}{tytxz|~}o^aottxoV_]WZ[amwxwu~vu}u}sehl}~|vb_lzj|tillkkpptvrwvkm||gjnfjms|ngl{tvr^]`moes~rlu~psvwyz|wgamny}eae[[Z\erwwy|zuxx{{hfjw|sk_eo|soonmklllu~uuypmyx|pjpghhkwwljs~z{f`bmqcly}vkpv~vxwxxwz|~~~pfnknrfk`][Y`kst|}{~wv{zpgjt}qxxedhr~~zrrpmlkfwww|ryunwxt}krkgggmzslnwz~~vedjtherz{omqv~wwyyxxzw{mnphvmni`_[[dnqw|{vx~{{}kin|wo~odfjt|xvtrnmgi}r{usyrqzswvoriighp|}rmqywtghrueju{|}{mnrw~}}xzyyyxu|~wlqnizoqhb`\^fnqx~}|zvx~|{|kjr~uqleglw~zxvtpnfmzq}ssxqtzt}vrihddiqtx}{uwvnny{qux~wek}iX\hs{qsz|uyf}qomk|}~l^vwc]ajkjtz}nlu{u}z~jcfmynZamdU[VYc^ex}wzsrlcdvvu|yvlS_~ppq\XjdX[bkv~vuqpx|ww{u|i`etuke_^badeovqnr{|tkpim{w{in}qdXdj]\kk_gljuvs}wwuv}{y|xp~onuk\}sZck\W^hr{s~~zss}xzzkpmlwvz`Pmr[]aeilvuopyv}|noy}in}n|whjtuag|bR[dVV`_hqn|{yvlfcp||x}{xfVrpgmlV\lc\bku}uxxwsqw~y}lprr~o\U\twogbbaknjjr|{{rwzjcjslro}rs}xldfs}~hfjZS]^enjv|vpkcchs~hafday}~w]`od]bjry|os{pnosz~otwyvl]W[evztrlssmmt{wzznssc_jsmuqtvujfgnw{xyye_ffnqmzz{rngaejsn]^hdf}}dnvgcgmuz}l{xonptyyymuw~rfZY]hx~}wtzvqrxzuunwoacpojzuw{txpighnr}|wdgjmtmq|zxqlcbdit~h_gh_mriyqeeipvz~tpsnopssxrry|{o`ZY^k|x||usvysz{osufalpemtuvzzojhhipmiopwpm{yzvoeaabjwqejj]bum}yjghmsw{xowpooolmxr{~}wg^ZY`o{vvxsu}rqxmckrfftuyuw}unjgdd{|mruyulvyyztiba_blz~ljob\ixvztkikptxoysppokfssx~{qc]YZcs|xyvp}}xnuugfqlajvzxwy}snjf`hxqx{}qp{wz{qeb`_dn{xjnn^_m{vsmknruy{n~|qoonheysz{~la\Y[fu~{{ur~}vmutfhribmw|}xx{zqmje`lwtz~qs~}uz{oda__eo{uink]ao}xtnmptwzzn}{poonhf{sz|}k`\Z]hw||vs~|vlstggpibmx~{zy{zokjgcm{szus~sw{qd```eo{{vgjl_bq~yxqoqtxz}nw~qmnnkgwsv}~~l_\[_iw~zt|ynnujdmmckxw|z{|}njjkilrv~{r{vqzvg``bfozwxxhckeapxuqqux{~rn~ulmopmqzq{}z}ra[]bkxxz}{rjqodhohixx}{{z{ohimoovr|uyyouxk``cgpywrvkaiidpwxssuy{~vlyxmlprpq~~px|t{sb[^cmyzy~{simpefojiw{z}|ywpginqqyr|xyzmsyl``dipyuosjahjeqyzutvz|~ulwxllprpr~~qyxrzr`[_enzy{}zrhnpehohkyz}}|vvngjoqqyux|wktyj``diqzqnrhcjgfu}zvvx{}qn{ulmpqmu~zr}rt{m]\`fp|~x{xnhpmfllfo{y||~t{yjhknmsw|yon{td_adjs|ymqqegibiyzwwz|~ynt~pknokk~~twxp{wc[\`gr~z}}{simrjkmeht}~~|~vxphikhi~|{tkz{h__aelu|pptkijacp|zy{}}qstlmmhev~zw~}p{}i][]aiu|||wlktnlnednv~||ywwkiifbr~{lupb__afnyumtrjkc^hs~||~urz}pmmjcj~}~v~ssua[[]blx{}{riptnojbhqx|~~wzqjigae{sl}}i`_`bhp}~|onuokj_`lu}~~st~ynmlgbp~|~wnvp_[[^dn{~}zqirtoogbjry~}~{vz~oihf`h~pm{g`_`cir~ymmtolh^bmv~}tt~ynmlfcr~|~w}lto^Z[_eo}~}zqhptoohbjsy}~ztwohhgbh~rk|~j`_`djr~~ymiqokh_amxtsz|ollhdp|~wlm}s`Z[_fp|~~{tiktqokcisz}}|srzshhifg{yirrb_adjq{~~~zpejpkjd`kxxrt~tmmlhk~}|wresyg[[`gp{|xmfospnffqz~|~sor|xkhjjivlk|yf`aejqz|}zredmljgajxzsrzxnmnjk{}~v~tdmyk]\ahq{|xoflsqohfqz~|}snoyymhjlkvniz{i`aekrz||xqdblmkgbkyzsryynmnjl}}~w~rbmyj]\air}|xnemtroggr{~|}}qmoyxlhjkky}kj|yg`bfls|~|zwnbcmnkebm{wss|wnmmio}}y}kbrwe[\bjt|vjgqusldju|}|~vomr{tihihlvhtqc`cgnu}|}xti`hpnh`dr}~vtv~rmlihw|}ztciyo^Z]dlv~zqgmwvpeeow~}}zqopyymggeev|koxg``dipz|}~yvmaeppj__ku~xwvvnlgdq~z|~xggxsaYZ_fo|}ujlwyrfcksy~}}sqov{qheb`l~qm}mb_bfkt{}{xrebmsnb\dow|yw{}rmhciy}yy}}~ndqyj\X[ahr{pir|ymbfouz~wsqqzxlfc^bt~{mtyh``cgnx~z}~yvmbeqsj]]hqy{zxzpkfcn{zwyzietvfZX\bjuznjv}xjbhpv{~urprzvkeb]ew~}ymvwf``dhoz|{|}xukafssh\^irz{zxzpkecp|zwyxhetueZY\ckvzoju~xjbhqv|{qje_btystvy{rz{pxuqry{kvlttnnmg_ds{tjjsz~|tyxu{xvttnpyrsvxwqfaoyoecgkqxw~{slt{ty~hswmmrqighgqwrmkhjw~}~uttt|uvzsnywvmhge][gsqhcktz{y}}~~}qgt~yvsof]bopfabgls{~x||}uv}|{trq{|zwnggfirrliggo{}ywwwzxx}w{x{}tuxuw}snke_dprjdhpvz~}yx|~~|}|uomnoz}xoedorjddhmry~|uv~~xv|y~}unmsxyqnllsuplkjpz}zwuvyz{~w}{wx{~ulnuwzzuqjdgqtnhkrw{~~|xw|~}z{~wplkor}zrihpslggkoty~{tv}~yw|z~zrlmtxyrpmmtupmllsz{xvuuxz|}y~{xy{}~{qkntw{{vrlgksunjmsx{}}zww|~|z|{toklosyqjkrrlijmquz|xtw}~}xzz{~|vokntwytqnputomlovzywvuvyz~|{zyz|}~~wolpst||wrljpurlkpuxz|{xvy||~}zz~}wrmkmotxplptpkjlosw{yuvz}|}|z|z}}ysmmpqvxurptuqnmnsxywvvvxxy~{{yy{|}}yrnoqpr~}xqmpvtnlosvxzzywx{yy}{z}~yuqnmmmv}uprusnlloruyyvvy|{x~~{~}z|~~~}zupnonny{vsuwtpmmqvwvvvvxxvy}~~zzz{|}{uqpqoms~xrqvwrmortwxyywwzyvy{z{~}zvsonmkny|vsuvrnmnqswxwuw{{wx}||{}~~}{xsonnko{|wuxwspnptwvvvvwxwv{~|zzz{|{wrpppmlu~xsuxvqoqsuwxxwvwywu{}{z|}|xtqnmlkoz}xvwvroopruwxvvxzyvx}~{{|}|{yupmnmkp}}xwxwtqpquvvvvvvwvv{|zzzz{yuqoonlmvzvwywrqrtvwxxwvwyvv|}{z{|zvspmlkkp{~ywxwsqpqsuwwvuwyyvy~~{{||{yvsnllllr~zxyxurqsvwwvvuuvvw|}zzzzyxtommmmnw{wwzxtrsuvwxwvuvxww|}zyzzxuqnkjklq|{xyxvsrstvwwvuuwxwy~|{{{zxuqmjjlns{xyywtssuwwvuttuwx|}zyyxwvsnkjkmqx~yxyzwttuwwxwvttvxy}~{yyywtplihjns}}yxyxussuvwxvttuwy{~~}zzyxvsplihlpv}zyzyvutuwwvutstvy}~~{yxwvurmihjnrz{yy{yvuvwxxwvtsux{~~~{ywwvsokhgjnu~{yzywuuuvwwvtstvy|~~}zyxwurokhhkqx{z{zxvuvwwvusssvz~~~~{ywvusplhgint{}zz{zwvvwwwwutstx{~~{xwvtqnjhgjov}{{zxvvvwwwvtstvy}~}|yxwuspmjghlry|{{zxvvvwwvtsrsvz~~~}{ywutqnjhgjnu}~{{|zxwwxxwvussux{~~}zwvuspliggjpw}|{{ywvvwwvusstvy}~~~{xwvtrolighmry~||{ywwwxwutsstwz~~~|yxvtspmiggjou}}}|zxwxxwwvtssux{~~~{xvutqnkhghkqx}}{yxwwwwvtsstwy}~|zxwuspmjhgimrz~~}{xwwxwvutssuwz~~}zxwusqnkighkou~|zyxxxwvutstvw{~|zxvuspmjhgilpx}{yxxxwwutttuvx}}zywvtrolighjmr|}{yyyxwvutstuwz~|zxvuspmjhhikov|zyyyxwvuttuuw{}{xwvtqnliggilqz~{zyyxxvutttuvy~~|zxwusqnkhghjms}}{zzyxwvutttuvz}{ywutrolihhikow}{zzyxwvttttuw|}zxwuspmkiggilr{~|{zyyxvutttuvy~{yxvtromjhghjnu~~|zzyxwvutstuw{~}zxwusqnkiggikpx}|{zyxwvttttux}~|ywvtroljhggims|}{zyywvutttuvz}{ywusqnligghkov}{zzxwvtsstuw|~|zxvtrpmjhggilqy~|{zyxwutsstvx}~{ywusqnkigfgjnt}~|{zyxvutstuwz~}zxvtrpmkhgfhkpw}{{zxwutsstvx|~}{ywusqnligfgimsz}|{yxwutsstvy~~}{yvtrpmkhgfgjou~~|{zywvtsssux{~}|zxusqoljhffhlqx~|{zxwutsstvy}~|{ywtrpnkigfgint{}|{yxwutssuwz}|zxvsqoljhffgkpw}{zyxvtsssux|~}{zwuspnligffhmry~|{zywutsstvz~}|zxvtqomjhffgjou}~|{zxwutssuw{}{zxusqnligffgkqx~|zyxvtsstvy}~|{yvtromkigffimsz}{zxwutsstwz~}{zxusqnljhffgjov}}{zxvutssux{~|{ywtrpmkigffhlry~|zywvtsstvy}~|zxvsqoljhfffint{}|zxwutssuwz}{ywurpnkigffgkpw~}{zxvusstux|~|{yvtqomjhfffimry~|{ywvtsstvy}}{zwuspnligffgjou|}|zxwutssuw{}{yvtqomjhgffhlqw}{zxvutstvx|~|zxusqnlihffgimsz~|{ywutssuvy~}{yvtromkigffhkou}~|zxvutstux{~|zxusqoljhfffilqx}{yxvttstvx|}{ywtrpmkigffgjnt{~}{ywutttuwz~}{xvtqomjhgffhkpv~~|zxvutttvx{~|zwurpnkigffgimry}{zxvutttvy}}{yvtromjhgffgjou|~}zyvwur|yturibc[WWPRRPRZW`ipwxyzxyyoqpoqvt{||zrhhkjstsrtmokhd\kpeszqknvy|qihefknn{}u{ql\X]hlodcfgjqsv|oXUrxf_^blnvzvrc]essnchjmrzxphr|umtsrrtlnqx}q{xuiVMdvmplms{{}uk{wpqxsqtxt{y~~sfx}tx}iea]^wljjgkal}oqumieff\V^`ZPd\V_hjccnv{w{rwwne|rtw|w`VZaaaxzv{}~lks~zuy~{~{{}w{vxxiNQThnytxwi^ZPW_TCILZVXaTgkplbgy~xz~yzdhblhlwqxv|umuv`NXShhy}~xmz|ieqo|{}|uw~qltkOS[[pyt~~{fdaNM`VFPVT\]c[edekdcz}t}w|gmk_giwtwqxfmxdRb^cmzjuhhwut~~{y{|reqkP\g`l|zy|xdkfQIdVLYcY]ec_`^]dbb|{}tyzito\djxwxzoudlzbYlegohx}fn}zs}z{{}wmesfOcgcl|xpdmdKQkTR`g[dhc^^_Zc\h{|rwtizg^dpyzyotibsxYbjekv{~xlqhuvzv~|xxxhjuVWicfr{fhjZIgdRYfa`lf`Zc[]_[r{w|~kuu^cf{u~yyf}m`hd[jegpzl}hs}v~}|v}vxnjx^Thddj}mfl`KfoXYdc`phbXf_Y_Wiu}znszaedyvvewpbd~oZgbem}yx~s|lqwu}u|y}{tktmQeheewxhklTXudZ_h]oofZ^hW`X]mvz~{lmdfn|vx}phwe`p__b^fw}tuyox}t{qsx{pkx_Xjggl}ukojRfubZbc_xmgYjh\cXbpzz~uqijfszx~~vjlsd_u|[b`_h}|uzvp||q}||vos~wvomx\]ljkq|vlojSitcZbba{oiZok`f[cp}y}vrilgszx|t}~jjpc]r|]a__i~vyxrz|qz|~xj}~qxuztnjwc\nnot}ypoqWewi\`e]{tm^mrbj_cm|t|~onlll}t|xv~pfoe]fe_a_e|zvru}{tz|x~hu~v{~wyyrtliqq[krsw~~xnufXsscZg]l~rjc|lmkbizy~|sx|irhwx{yqy~ueij`[vv_cbds~y~vt~~|r~y|~v{li{~|r}xqyonjilwbhtvyzzqqnXlviZcbduqbzunqehtwzxu~jslr{vz}rs|uggjcZp|bcceo{{xu{zt|z}xyofx~|p}~wovokiilxeiuxz||tsq[mwm]edgxtd}wqsghuxzxv|jtls{wx|qr|rhhhc[szdcdes}wxv}|s}w{uwhfw}yqw}{tmunghgpwdmuy|~svm^rwm]kapyqhsurii}y|s{vkvlwxzv}|ntzliiia_{rdcfe}|uyw|}v}~wsqbmy|q{s}yxonuiehixmgqwxt{djxygeld~zhv~rwmhnt~|u|lsso{wyx~um{rklii\pzibcdn{u|y{t{t}tsegv}ttq}zxskvnchfupfmsu}y~mjx~pgqf~nrsxqjiu~~v|opwoyz{|v{zlyvkphl_jzmbadf~x|~|}xwx{u~xrhcn{vqsv|wvulowbeeovflnus}zjt~|hrlsyjxvvoew~~z~~xzvlvuq}xww~tm}lqlkh`uuj_eau{}t{|~wy~s}tndfu{rvm{wwsthvp`gevohlqsz|toxylvktpwzvnfz}w{rnxssxvv|nq{jsildcwti_fa}{{v{~}|wx{u}sjbewxpv|lzuuqrexnagfwmjlss}rqyzovmtryzvng{}wzqoxtrxvt{mo{jthkdcvul`hc~~|wz||wx}t|ujbctyqsju}tunrdst`heuqkmsv{}vqxqxozq||ysg~~{}xzrotwo{wrzyqi}krkgh`rwsdhfw~v{{}xx|t~|}ymb`jyqrvmlw~vsooke{eefnxlpp{w~|pt|wvuuty~|ymozxwppxquxztr~xwfsvjsdjaixyofmjyz~z|zxxxwz}~}rc_burpr~tgpyypqjpavqciiyqrq{{~rsyvyt{v~zrk}}~xwpovtpy}uo{xvilzitfidfwzuhoj{xy{{yw{ux}||}|sd^`rsoqywfmvzpripartcjhyssrz|~ttyx{v}v{sl|xvpoutpy{smyutfnyiueibhw|vjpmzz~zzzxwww~w|y|z}qa^`toppzqekvwpphp_vrfjkystq}{|su||z{|x~|pp~yrpnxpuxxpoyrsbvsnsdianx~pmntx~|~x{w{tzzy}w|wzi_]ivltn~gfmyqsinid}ijhsvsst~|xvxzz{}zl|{zvooryl~}ukvttii|kvhgcfs|ylpl{|{xzxxvw}~u{v{w}~p_^`vlrnyldgwsqmho_yohhnvrsp{xzxx~x~}nx~|xopo|l}wkstsme}nungedpy|mpk}vxzxnxy}xrquq|vuyv|z||}~v\TRrq}uhhv}uvhr_jq`^[eab]giuwvqu}oe||{y{{sts|xqzqtansiqceamx{phd}qtsxsu{}v~zy{~~ufROnz|rpggURWjfibafWpib_gklljtt}~}v|nuy{vmpnu|wpbUctdi{}zyzpx~lgkltoux}{qzt}|uw|~zr|v{t|t]RQb{x~iaeunshkkaypkirtwtv}{qky|egoytqhhfb}ooptujg]a`qeqv~}vjnroqdzvtvieahmugdcs|}}qtoiUJM_~{j\Wd]^UQWH_^VS[^a`_gh{zvvuqy|wxyzwzx|wmdfemc]_k|s}vwokyoafelkmqt}ztnqstzqst~{yozvrqlmrofUX]|{wicpurshthpvrt||{tpkwvvrz|lqywuhodlnspvpkc]`crby~yzshpnplf|rvshaafntjln}~pndiecSSZu|fYZe]aTXXOeb\Zcchegkoz{opkxv{~x|{w~}nb]^clehluv{vvnm{icffnlqpwtz~ktqxvpru|vvlxy}klelloa[_p{pfgvrwootj~yv~~mkdmvyy|qrw{znnnevtrusmh]aasfpx{timnnnexvtvkbbelvpqr}|}kg_cbeWW^wk]^hbfZ]]Ugh`_fekhijq{z}lnjxx~xz~|x~}xh]^]hkhjrv~xymuyehelnqrr{nrmssyprpyuvmtsrmghkmj\^d}zmeruvvmwptx}~rkegsy|yz{zoxh{wvtunl_a`plh||~}vkknmpfsztwqccdjvqpny}mgacbdUUYsvgjqks{~{tol``]frllyu~kwzyy~~~{xs{z~vnb`ccicl{y|}qpsv{rxzxu{xspluw~tvv|yvux|u~ujdlylvuov{qggoghfs}yuqwv}|y~~}~vtq~slojle_afvjs|zwpglkqpm}~}rvu{yytz|}v{~wqvx}vwt|z~xz||vcb\Z]bytlcab^^\hpkh}tp{zuwxyxzu|~wpnv{z|romxtxuyy{~wijmvywxtvnnqur]pc[_egbcbkgmdk{s}zupss{vjfh{vu}tqzwj]R\_hlmy|{yxogbkpt||{uc^Zdg]mvlltnj_]`agcgvxr{ploqx}r~}ohmt~xvy}~|~{{jZJQT_hn|{xvnidmqme{rkwvzpsrz|~|}usvrsv{pjhqz|~wroxx|w{{{o_bfuz}zzwpuuzbkn\^biceahhjhcxvw}yqrrvznjft{ry{npvkaRU]epry~yvvwpb_`ot{rd`^kgbtxkptni^a`egek{xu}opouy}}o{xwgglx}y}wz~}}{oul^OHS\lu}{}xtmlsvnjqpvxypsr{zz~{vrynnqtiggu|usq~z{z{~qxspa[am~}~{~vvx{wbqj^_gheecjgmdj{uy}wqsqyvlhjyws{viqoeZQ\crx|}qklma^^mwzjeakmeo~qmunlaa`difjy}s~tqqsz}luttgeju~ywx~z}|wgmf[NIU^px|yqqwyrlrpvxwqtqzzx~{wqxljqqhghv|wtu|z|z{kqmi[^btz~|ohtf^cjfhdhhjkct{vz}trptyplhtzuu}njqh`SW^jty}}whhmd_]eqzunhotmmzmvqoddimx{|xzqhqY\^`VSWarz}}zyuw~w{q^f_WKJS^ox}xlnoqge{pv~zj^aw\Zwv|yuqz{|teepmjinzztov~~}uozwhbccZSV]mv|vfVSjp_egbnqkmkwuoinx{vz~krsmd]cgw||ypu{|~|k_gha`^ip}xfYMVb|x}wah_[U\flkempwgnk[`ccY\]js}}~rrvpss}t{sm`ber}sh`]SV[j}uog\UZcqmooutedaSQ]YYVZbispw{w~}{}yvqqv|{prtngik{z|nXYWTZ]jtrlafgb][fjkfsc_f`\QUWelrs}zvrrtovw~|kirllgmt}jh_\WZak~tkahkoghjnnak`WZ_ZVX[ipzu~|xyhmoouvu{upeijy~wj^Z[W_dpxtg_bjvqsqvtgcbTP\YZWZbjtrv{w}|zywursx{~oospimm{vtpZUYV_dow}rhflfc]fjngnn]da_VVX`mrvyt|pmrqu{{~~}uelolnlv|rubc^aako}~ujeknphllrkcl]X[`YZ[bow~x|{u{sdmnt{}yvxtpinrqrtbZ[_bns|wugceowtvtzsgccSS]Y\Z`fqxu|{yv~wuwtwv{}~qZRSIGIP`ivt~~psqooihcjfgspiyrt}xqedblqtqzswwzuzv|rzfpyxg`iceaglx|{|xyuupnpidhvpyw}thejcbbfr{{lcuyqzqoisttoqwzxp{lfgea\adu}|xkiggsx{ttzopt{uwggpnrpv|{|lvzsmgj~nmrqhgoswqutzrkqg^`e_baju|rq~~xyztv}{y{{||yshowxqoqw~zwebZQOScejkormdgg[ehonov||ujninswny~yu{|zr^df_iusxrunaVZtjQMWf~ww|t{{|xz~zz{xsttxs~~{ypo|~x{|zwomjpjks{wyp_PMasrdT`l`clnfmryxy||wuwpfjhedbmwyspow~oidb`irf[TQ^jwwuaXaXWZhqtwtwqh[`UR^eqt|~}}}}}wxxxztmt}|vqtmXWS]dr{zwwm`Y[XYU]bpru|wtumlhmwz|zyvzx}}xjfgu{{|xuux~w{j^\\blr|nni\`frvz~~zttrehiikhqxxw}stt|}~ys|{tqrlmjt|wtwgYZXcgr|x|tqu~|ykjm_delknov}ufb]^`cq{{vxxkoprts|yfm`PNP]gwpmmmnkrs~ww~vompkmjs}vgbhaV_ewwxwnmp~xszmoqxzz|~~oxzpfinurqqxxx{y{uuzmmqquqy~|pzuquvtxy~}r~vus{|~~{~sossxv}y|yywzz|zfefo{yvsq|~pxqjlnsopnwvrowzfoy{ty~|{lcefnmtz~}{|wytw{v{y}{{vhre_`kxwxvxwx|}z|}shda[XR\bijs~|}thfocZ_borz~qzxut}||~rrryxz|rzzpfmsyyw}yzwuulcabSUW^dhuxszxu}{}~}uj[hd[\cou||yyy{}|rlvpelr|}yoijqptopmndeqjmowvusw|wvwwq}yqslmjqrwuirwnchktvx{uwxwfqmffp~~zxqceejou}~ojidc]fkpmr}vkq~vvuwvuzx{tjfskbeivxqyzy{|pwymchlvw}}~wlol^_]b_agpuptxxckvttw~|}xhame`^ipwy}}paahWTUbmv}y{wo`gb\Y^babdonpqqxwxrqosvwzmlusccgpwz|qfQY^XWbsvsnonoqrzrorrc_`__[fkrpu|nsxxvxuvywzulhxrikr~~ytqrvw~uqzpjns{||xtvwyzvywpgpi_^`eafkvttv{sansts{}nqyrijty~~w~z{lusb^`mqz{vz~{{lelf^\dcdainnotyr{{|}yzzjkmut{zusyuuyn^ge[T]emr{{ytwvzx|vrvsb`^`\^hnttvrp~rifovzvyzskqvxz~|}|{~qgkr`WY_gjw|~}{}{uhpncadliotnu|~ojutheot{{|z~~}~||xq`al^WVciqz~}{lrtnfmppnpyx{z}{zzy~|u|ohhnqr{~~~{w|yu`ZdcWUagqs~}}}lkilfjqxzys~~|mlynd`jnrw}|vz}}{|zwtvwvzpehrbYZailxtz{nhjplps~}}~opx}|~|j^gkaYbgpqz{wuxtt|}~y{ywvpdXge[Xamtwqwvkhoopluwyy~xy}zupootryukhvufgkvx~vryyy|rx}}}}ytmZ\hh^dpy~ylkjkelpvuw}wjuvwuuttzx}{ykruoo{~y~zqxy{w{|}|ttqvvy{qiq{jfisy~s|xlehkgjnvsttyudhqxyojuxljsy~~~wusvpuzx{z{xsccrleesznorl`efgehoptvz{{y~~zqvqiiquwzxquyw{xx~~xt`\hncdpx~{u~ugefjfnt|}v~||~{wfjwpgfru}~|y}|sz|{}utqvtxxofoyiegsw~|t{rlsttw}|nox||~~|ubcopchowz}}{x{yw~y~}wzxvvsj[ing_gqwy~rpttsqxwzyxv{{tsruxyzpl|uheiqr{~xx}z~xqu}|~}{usaW^i`[dktwsmlnhnrxwz~ouy~{{yy}z|zvehtodcmqy}~}u{{}yy}vqonrouqh]jm^Z]hkw~~{{ojpmpqzzz{|sjkwy}~||n`dqkahmuw}vtxrwzvuy}{xuruprom\Xfh_[gnyyu|xknoqmquuxyrx{zxrsryw~ymq~qhenpw~~|rwwxy~~|ohpvw{w|z{vuo^Zfobdjsx|pjmkiosxv|}zosy~{|y~~~xgoxsfipt{z|wrtvuryx{zzqyyztrqtww~zqftvhadlnzz}pkpnprzzz{{vljw|vhftsgjmuv}{qmplnsx{{|ux~z{{zxl^hoh]djryvypnoqopvvzz|v~~yyw|}vm|ymehmpy||z~qotswz~so{~~xxj\[ji^bgps}|rmqmoty||}tv~|tfnwqdinsz}}x{yuxzzyv{{|trprssyul`mqe^ahlw}~wpvtvw~|qkv|vgbotghjqsz}~|uwuty|sqv|xxsvustrm\^jk_`hnx{sttvrxy{|uwz}zuuvzyyknzrgcjkrx}tvwwy|~}}thitv{wzzyuspbW\lfafmty}sprmruz{}yqs||{yy|y{zzkbltlckow}zvttvsuyy{{vnrttmljnmquqf`pphbhnuurtsvx~|~}}~wlgsx~|~~z{ocbssjmqy|tormpsxyz~}wlksusqospqqrg[domdkqz|}xsstrrwwzzyoswyssrwx{vm{~ulptx|sr}vv}{{ytlcoqd_cdWW\c\bbgii{ii|xx}}rxsccjiiqx}zp~n}z}oopvuicvg_xjemvvtvcimld`flx~xizfamx~y||}}{ffku{lq}wrgw~xsqqksxzqlk|ovsviUct_`jpymqmtmzoo{yzppk}vvuqwmtpkxo}}qylu{{}r}xzw~zjZ\p~wti]`]VNa\XohZceerqszxr~ydosft}}}~q{scupp{yu{zm|}|u}zy{ehytvun|w[`]R][\^bYhn`kpdkykknniy|n~~px|stoistpx{tookp|y{utg_bacyz~~qkiVZaWVf_fwonll^kiW`nag}yyvtwqwno|r|squtijwp}|vludcj`]ox|ekcWdi`ac_fn`\pc\omfcb]ejrw|uknsh`tru~kmemtqddnjnsz~tpch]LZZOYicifdbijmvdcvwz}uxyimriuonooxxfftvzs{xu}sdgfZmvt{nqeS`^^\\^bmkyq]^oihdbruuiywy}nmeh{y{y}qs|pv|lv}}~~~z}tfteX`gqznha`agocSgqmpimrvg]c`hpkgqe}jqslqyk|rtpnl|ln}u|xomclbRfrmsugoqjg\`lnbZigoujg_Wnsrtr}s}~lzxnlppw}fkuiWint|y}seqqZ[d_chvxuyvr]Wa^krmhh\smhgkn^qupxppmnlq`fwlbloopsqq~{~~~}xpyssomdchhqjg{xoqtdbbTlkmiswirulsyaso`dkprouzfhkhkjoiwv~ydi|x|xzzzv_Tbrsvss{ohlp\goftxmzrdfeafk[dytfrvxsxskru|obi~~rtyx}kx~thvvwmdlkq``lxt}unid_gmgihlxj[fce`o{q|zzwtxpkahfsjmznxor}z|ot`kqjYdljp{wwr\gjjdchsbjlqx^djoibhpfok{vv}y]pktkepq~xow}~~}vvvuusopnqg[rxr|svro\_ttshmnmfg^ig^gjhvy}k}~vmkqskqlpciu{~|zw|~}u{tlptlgWtrxrknqgcrr~sioljnXdo`ghgt{{ls~yyjtugjvh}shypvy{yz}{ssvyunq`oldi^ryu{{nctpwqestpydizhgfhjcr}tzxrnrgitarogznmqy{x|xx{yqhrdmicm^kuwui|usnetvozlopcidfnqrxvv}uljmjntbzvipirliuw}z}|xulo]lskm\ahiw~}r~yyupyupzhnv`^\eopr}ovzoinmmrfxoeojorz}yw}~{y~~|rvilrkq_aaYdkfv}ot}nvqu{hkjiciioo{so}uyxidkZmzilesvruyz}s}}z~jfzouh^hhjqgx~yyouxjsokmfejlggutxxl{ntzlmimwksotsvqmnwtt|{yy|xwalnpe^le]f^ltrx{ws~pjhehfljqn~u|~|lwvihfc~mcej{tu~|ul~}{ydq~vzmekb^_aohv~xwkvjrpkbpvmkzusfh~{}rqjrmdnlqozyxs{sl{xv~z}knnqhdnb^aVj{iipy~|lvr~rwyuunympkxmo{}|tzoqg_zobbkto|l`yz~u{}in{mkjajh_bdndmq}}vxrs{rjnznkvvxshyyqoorlenlsmtsutyxo{v~}}~{phqrgboh]c\[pb\^q{wv~u{vw~yvspscov}qyxxvjxw`^\krjwfs}{}~wx{|vixtooeamZ\_niirs{~wwjppns}xbq}w}~t~srvximhskntrwv~llt}pv}}}~y{~kvxs\[^adfNspUgcqy{uyxjcfkmzwtmxtylnysWWvxwxxlqrvw}}sy|wz~kx}{eyzd]k__Ue]^ojhjv~srgqyz}xdvzsp{kk}{grfl`gkh~vkt|gz~y{|s}{wywpsu``kVYcTPr]]_kyvy}yxz~wvfwvlv}zupqylm_gwgdysiy}xmz|u}w|uu}zx`d`[T]_mf_ntfttytyn}~qtut|o|fmtfcrluwruyo|m~tt}x{v|}~u}}ptuhlpa\hXfzb]gghmqw||||v}ckxhdvitv{{rxjo|wgvyyyloer}szoxtwv|vgkp]f^bhbZ_lWi{yqxy}zt{nkkzz}t{jxikuxmiwjnzzfpeis~||~x~rrtlXSXT\jcVkcalvqw}~}xwto~oy|}ov|pekjg}sfwsrzwny|uz{m{|~p|~}~{wwvm\ur]^_\hibdlldh\fym{jpz|pu{mymkdrsgj{~rmhz{utw~xpwv}s~lpfmk`[iueiXXnvjywjes}qktafttk|yotwxifytllwlxpvxxisqjup[\tifpf]`tcmkglnk|ozomkjz|rfw~uml{swzqprhrykts|z{}yzwrv~ym|yqa_ki[hhei_^g]_aj}uyr~}ls~zykkslurhpwttkyxssv|}qz|}xqu~ygoageYXd^e`brhmepypp~{ut|z|yxy}sdknnqiwxtpytklrywyqtyzlzwy{nkpelY^b]e]hnhaiiwll~orw|zwy{ykunuroxumuy{~jyytktxmvx}vyr|~tz~wyvwkoach^Wejobbpf^]i\gwqtqvt}yilrptdtvoktsdrrknm~~qkRVZeq`ouvmqkc}yssyw~sqxmy}odicd`MZa^]\i[Zuu`juo~{t~opd^lrhozwzrZa_VS`jZgs^e^Wrrlt{nylmo_b`Tfpqsrozscz|}xziusx~x~yx||}x_cn`WkgjzkcsbT^h[Sqxden\jw|~w|vvxx~{{ogwtjs}~ozj^eT?QI=IXR@R_LBLDJ`ddt|qwrbfv{|t~zybpugm~x}}tluykdniocn{yyyunowzru}y~zdetclx|tuka`]ODXeULYRWfi`ttfibJZpegz|ssjfqeizqpt|msprek{zrytown\qognqo[Tf\EQ_`zz{~zcafWWfeapn~wsxperiaXWV?>[R>ORI\bS\qbkyfgo~ru}{xoxx~~stsqpx~sz~}xdwxURaWntz{w~|lyw~xviejomvppsurfjs]TqhIOQE_kcinhmRGhlaorfikvoyzx{vkgjikhny|uovvhmsnuqftsj^ldMaw]TaRYrqs~ywplg_UY[[^m|{gspaVjaNcqSFH8CWTWjupzq]i|wu~wzwy}zx{{~{r~{w}w}vkqymdxnV[\Ysyxyzvx}}}zzsxx{~~qgoeactuqnyaVjo][[Tac]\enmph[csrimed^uzo~xosm\cmfnlxvvvk{vzurrSake_km[_qiWURWloxw~owrbcdRTe_ekpqixmedoc\gjTEB@PZ__cuqprho}zlex{q}s}v}w{{zx}|q|xoothgx{habbqv{z~{||r{|~t~}yw}~hah_drr|vqfmmgnt_]Y]hfa\stnsdXlyjqxjhmxdpnhuwhmvkrpfvoqqxzvumk]Zlgj^d^T^si^ffo|{uyzooiYdZ^haWbdbso|sy{tk[cXTgqZTWScicYdunxyjw{nlk|}{xywzrwu~~p}yzkgoce|ybdeh~||}}||}yl}|ouzs|tukinkciqs{ywfvodthahas~pdcndhiZcz~w~pkhlujpkqvqjmnktit|qzrr{rhpwnuglom^R]OQnuejkf|~xy~{qh[hZ\]^b_WXcj|w~wo^i^Ymub]\Shneeizx{tek}}~wwzvx~{x|ysxvzwnddRYrriomsy|u}}}|x~|lxxv{xyus~imjhmndiuu~suejzymnknul``nmojbiy{vyqvo{noeekfbqstzpzquup|xkmuptmarjgZ^ZTdof^`_v~xxsocsi^bWW]SPcjt{wsi`hof]_at|soey{vwlhz{t{v~|rx|u}xsx|yzm\\Vasxlhgox~~zywyy~{y{yywzvw}xr}uylij^Ubiqwzu|pkgn|urfxwyj\hyt{~uyuolfamnfjpitrkytfahhmfpyooYZVUeqe\bcz|~{yz{nz}mncnb^[ZcaXX]\mqqwrjbp{ngjfwzqlnmkx{xv}}y~{uxx{y}x|zo}x{peldj{wcbajx}zonlw}yyx}stvr}~v|{wvuwtwqhed`\_io}{yxsqm~vpq}y{qfm|~mrhqzosrwysifehqi~}~}xf_jlmnesml_[b^l{q`\Ub{v{{xz}y~yraa^]rdba^`^VT^bqr}txqvzqie{trtu|mjvzv~y}wvowo}|u}{z{m~|ypef^ixwomkt~}yykoquyukz{zzyzurwuowzrmcuidignskgonw~}}yq|ynrh~ulu~~usposstpvvkiqlwtoypyv{~uortqq^clag^`bhuwl_]az~~~{{y}|~zsntmvwlj]^X`hX]Y]jjdknkzoyxxu{zqqx|}qp{qvyzp|zxvpp|qwkxijf]divsno{~~{}z}wqx}{mqxvprt~wqcw}wp||{mefbqhjhispjkru}~ztyvro{npyvljriooy|tqmjwhryq|}~{sjlpnjbsmliVYX`ryonsz{w}{{yv}|nz{wte`lvuwg``T_WTV]konmnmysvooqw}xyuquxz}~{{yzzx~wisjmi^fdm~~ux|~xwts||vww{}}mhij~y~nmdsy|wihs{~prlcn^Y[cnuvw|yu~|{ro{twepvmruzzvmnr{~vtsunrpioywvkgpei]Y\^p{vppo|soir~yvmguvukdfptqfZ`Sag[^^afd^dkv{vw{}|z}vor}y||~y|yxyuzyyxm~|xteb]huyxz}{y~|uw|yv}zyy}qxx~tpnt|ror{wxjxyri_^m{~}prdbiY\^eprkntzv}{sx{x{t~xvptumksuyqsruvolprqpcpypxg^Y\ellip|y}{ljlx}vu~xrwvn_SYjmrkeg[e^WWWbgcchixyw~y~zsv~xpsvyw{~uortrcfis~tqs~}yu}{xyv~v{okqvyt}sghiu}sstzqpbpttrfcntrqacc_pjigjokedis~{qwxuywvv}{tpmsxj|yvzogjjidj}u{r`]Yaoqkos}x~zvxy|omm}|y}qyvqdUU`iml\`\Wieceije^X[exv|}y~{vtxxtuqmii}z|mjemzzsuw}qjpsump~rxrmoqyld_mqiulqxtgSKN^r~oicWkkfjqy~|xz~~ust}uylzwvYmy~|y|qlio|~ulfgwns~{|qw|wzoz|rkqsuvp}pkhp{}wqout{}vv]JOl{QD3?VT^dhmkbbkt{}||}ogmz||{n}{~xttruor}uv|pbnnumbc`fie`fgpmgukotxysqtxxufmvjurmou}}te^apjflYae^XSWj}{jg`\pnqux~usx|w}vngmnhacjg`^[Y]KU[Xgouphcbbc[Yohjpb`bktysot{to|zyvuz{}poWWWNIBF]v~msfZTS[[XY\_kabrhsv~}xpdtx|{v|~~qmsnmgkwssyvlc\W`ZTjbdopnhb`figamvkuj_`blvsklmvla]KQUY[War}|kpmgyurqx~|opkg_`tvnnttqia]`VLaXZghhe^_ikkdjo`e\V\dvywuyqwlt|{sxulZFGGGCJ\rrw}mgffd`XUZddaxst~wywuzuxinxzsms}{wfjga]ex{xuoddhnhcn]\^^ca`honlahnbjeaaiuxrkdfrdbfV]b]SNQ^oyzxyxuoorzrjrohcep~yuj_abibcnabbb`YYbinnbmm^e_Z\huzxsr}}~jqrlggo}qoUJQLGBCOfuulcehcbhjridrhkt|~z{~ut|{skdh{~|njsojc^i}~xnjmnwodqb[adc`_`dhe^oxlwo`]]ckmgks{oieQVZXVPSdqvyszxt~|{z{uxof[R\t{uokpkYjf]hnmhd_ac`Xbrgrtgggksslox~qmpnmfgv}{}ojgVZXTI=AQbr|stspjui`blpqz}y}{}}skwmushq|~turvvifbemjcfn~}w||ynls{vu|m{nj]Ttu}|~{yvvpnx~z|~yuv|{o^WXZ^_Yglcwxnlnkgitx{xb[klkn`fhhidbjppom{wpskhir|~vyxy||usxpkms}~~xxurps}uou~||}t}||~rh`_grvzwunddjlonr{|~}~xv}uqs|}vtonpy~zvowshablvyplgdZUXYYWOZ_RZZTU^knlc\^gZ]i^gspf]W[cea_jb]oifjnxvwtokktzyvttlc_[VRGI^X]kebdjnokflz|rvo{{wros{{|kswmmnx{zxwwp~~wzrdlcXTVdr~zttssqh`qngwwuzts|v~~q|{|unshn}{zx|}{}|nsqg`cq~v||ui`WTTOJ\cWdaWVY_ff_bipfehY]bbb\^jrplad\Tdfgjuurtg^^juty|zunda\RJY`Vbc\^bmxzuvxznhmdlxyx||yumwtm|}tosxy~~{}~~yz}{qoWV_XV^kt~~tmookiv}qyvmlmv}xzllu}~{znupgyyqpu{{}vylkl]Y`kw~xpfdgd_Zb_QUSQT]lsoidch\\e\ajle^_bgidboebsmddgksy~itwjhikqw||oeddba`mk^a[UT_owzywy{ijockvxrpry|{ojtjejpz}~{yv|}}vukU_`WWY_hpy{tjuthruqqyzv}u{ty}vqpu}~z|xutlikt}z~~|kuvkfadikrzoklmnj`ihTZ[ST\eijeenuhjnZY^[ZZ^jutnil_Zhhghltww|wuug]W]hq}{tokeZcn^emdcglpqkhpxsrspuqmgen{~{kqrlijsvx}juskox~zvqxojtobUQQWo{xqfgue^dVLJMU_aenwujnfZ`cec^bihd^\cX_u~ohstxptz{{sts_bh`ZY\`r}yrganpdnmda_bhfdkw{z}njkljcfov{}{~jVOirtpox{~{}~usttvts~rgpgWNQXi~sqtrpwxptjfflzz~v|h[V[omlheda`fkihry|{~~uty}|qnpYW`WPVbqvtplmz|urxrokVKO\q~polmkedgc_^hohpxyy{}|~yomnrtmdkcQ^bZ\ckwz|~{zzw|}zyvqyj_jbWX_m|ztg^\YXZbwws|zrms{~tw}zuuuwyreccMM[WZdo~~yxuvtz~~yzqXYZPR\mywxyqjgb\ZVerkv{vx}}}{z}}{y~xqmmf\Zc[Tbb^`dq{yuyqpnYV^Z]dr~xlb_]^b`l{nnwpkow}yvnxzzxttvobZ[VJWacfkw}yqm|{tp^OUTTZkxog_^]X`sjhwtrw~{{~vp~}x~vjhkhdaddVX_][^m{{su|qfeZO[aabm{xnidbffbjylgpkiouzpvtow}{rpohb\[^PR^^]eu~|wx{mj[ISZZ_o~~zwpmle]alacqrvy|yvy|{vwwxvlfikkkeghUU^XU]kwyy}v{`VVJKUab[_db``dpkk~zwtqpnmtzz}|qw}ropnkdX\WFTcdisxy|z{x|qcZ_k|}}nlu{z|xyutzp~}u~{tqxwwwpiknsxvpyr^gmhlt{mq|ge]RZbfju}utsfmwnoxyrpopsrnt~pn}yrqoqvx~{|lmxu|y~r{jptjc``anm^`b]Ybntyyvwucem``ikhhimttoqwf^kjjlntwtt{{|xqsrv~upv||tg^^bmwc]]^UQ^qrcXdqisxxwifpc\ca]YWZdggmwpcijeeivvwyotj|wrce{xoie_Xel_YYVMKXm|{oq|njxyw|~u{qa``_aagprpqx~ypeq}sywtmg]TXfrzjbcccgjjiZXgaVZ^\]fpy}to|zn{|}w|smljlmvvi`i~qxvty~{wp`OOMHTex{qjinc`lme^]\[`gmupdnp\TWW[dmv}}|vttz~wsotobWPPNIO\kt|vkbYMGLSX]]ceTUbc`elmoqooo_ZjfZ_dhotv|}xx||xz~kknwz_ZZ\cn~xoeWSZTNPRNEDGKRX]ml`krjfiijmnotobkwjbcbdghox{}½sf_]afkv}pe`WQQTYYQVVGCHJHMT[ac_fk[]oposuuvtpqn^Yig^`djoqum`^J?nU@LVP_P`[K[XYcr|poX`^gtvrulcvcgeytyxOKVZtov|rh}qov_nlNHGEIʹtz[CO:DpRSS]v\au`d@Dlc|zimgYxskxyz~znuYjb@QbczuZx`ts__`;G\c{wx^ePLbQIWkma`v\WKNROor`x]^^{pk{n]P]M]fzkzvrv`mqm²z`UWD6G{hoNBtQBHNse_~snJ?n]fpav{rn]uupk|pp{s{bpsKJP;z]yru|xqtd^tSDc]gauspfOa[SGgg`]qnVYNU>[yht}]ecZya~b|[eZKTW|zwz~unnkkepĶxcqZG9PbkzLqoDDBilXx~ug:blWe^T~|]rrvl}|yvnoulg`MX7Ir{rxq~{urn|tl`ygMfebSFűy_macO_d\XatU^bdHPiVcy\{hf]uhva|eokLSK_\|zwoninik}liVELL@hģg}QMGopZ{|lBem]bYLpprduq|l}wyykrzlh}bX_?PuU_is{}um}xjki]zmjJ6p{ĥuvyaq_[U_jScnpJX\CVgUiebhZj}jMPTTElxxvd{ogc|uzieN\>4Pox|_VamlXMtc`fTUmO^mux}yjswrm}awm\g\Hs]fkgSayupo~rwwxps^\Bl]:?]Veût}kjfdnx}i\eGDQ>>X\nơvwgfvqQif_\\O`lb}x}gpccWf^k~hyyc8Zcljy~o|\zX^|ny{bkkVY^7JEC\`ʰwqytqeXfXL]TLglmytx^kY[swz\ssU[bSZ_`rq{aisdl~yawZO=/R*(Htz}slerd`odYpZMjNgUlp~m`~noqjVUWCN\Ys¢p|gurlx|xrt_c~QIXaI_]K5m̸|{upmeuQU6XX9Gt~rjs~|npuagkY^bNXkSq~|r}{lurjtuh`wroC]ZQ?@`]uycgNnsNNkg[wrYQmovhq{mu}in|Zb^AD2f]Wwzu}sp}{`~}jaqVQO=8KP`ȯk^`|udOqfeNfupw|s~p{hypXUIqT;KhShn{zrkfYrw|z{|vzxbX[JEM?F[Sfmg}exr_h]MVccz|qucY^_Oh~cwi@Vgdbg~pl]|`a{vtyknrahhLRWGY̵ѽx{rbgaVb]Yoljwo`{ecqmSxxsZ_jT]_U`zxoj|bv~fm~on^nM;,BU32\njưytt~rw~efpWeiRTYizmmzd{vjvfSUTGTX[yygyxv}vz~f[SubBMiOQe\:Dqyǻ||ocQD\pNLt}on}z~jckbdmahkRV_Mfnagxxnqqgvx}vkqlj~|\@acYCI[Naon}ufirXlfnrP_~wtxvn{vuvixe`lL;2SbHUv~zrmufckw}}mdn_UiPJFJB6M^mгvse~URXueV||pbw~hvob\eb_RocX^ajT~tlravptvx}Ycf@AH(Kd7FDcnб{q}whkm`G_wgvozZjhkfl_iwbh9Wvcyse|y~yjteve^}sxq~adq^eRLD3LMERzodhWxpVu{nUxqk]nm_Rln[aRaCTo\t˚|pfvumwrvqd}WHV47i<36BfOkȨvu~m\Sn_fovspY~`kevivzSCybajSX|t~fhvwNyXihj~s`{pdhUU;EHB8Nfintjkdo~Un|r~~ofyjc^xeadg[:[RJlnnyslqmosi|my}Z\Z1Uc28-JW?hzqſ°ñ~pe`oXdkwspnpc`hpzUVz^^]JXn`uwhn}vSyNUWstgoeea^COHF4HNN\nʧ~s}n~Wou|~k{wm|ikbyldlsa<[L?]ZV{}rmjmi`xx{|q|bee8Yj47/GW@[ootVų~zl|a]jxlbv}b^bxthU|ibaMOkT`gyou{~[|ROImw}~g{vajakPVRQ>:M=IRxuš|xm^|~moymskhrxbnorLT^BFS>ps~òwrskv[hnyt}qy`fODpM5.@]JGhgjSSҶchmoRpssodboTrjj]F``R`hwgyv|qfoeRJbzlxrp_ecnYW]TR;M>BAageŵ}}^rbxt{spr}^foqV_jOEI4Wz^fq˿x}szafirrm~zifSKtT<2CcOBch`KAxʺnsqlUlwjp{qzmjp]wvn_K`_S\alXnu|ltdxp]Rrzah|{xgigsZZ][RDU>EBXQSl͸~gscqpz~{~{^emmVnoRFE2\gMOeȿ~{^lftkux~z~}lkTf}Q?8V^GHhf^:Hrr~ǫz~|dayk`ffwynwxi}nYRlXY]dXR^fyrwmign`hpXp{k{zúxqnp`knbbcZdfgrupqpx{qqtr{vsnvlrx{zf_`s~~da\antw~x~ul^gtwgiwRa^bnluuwpqmfq}{}xwqchwsf[gridKSNDKLMcnkxǾZ-TȷqO58LRYbeyxoavv~lknejim{up|{ygN`x`eg_mtqvlztxG_`EHEMZWorc|~is\i{tvruvvήqSI'?ĪvneaG5KR?[_HXak{k]isbqnOuwiLRYelYJ``hv}v~oebamL~|t~vmaVfkM}jmpuk_oavz^bflird|txln§tz|PB8!>/o׼wUae^VDJONknEKNX{[lIe{oXCTbikeGS^t~{thtUeRP_Zy}zwP[G[f`toyclmvnptdlli~crnonĭhsmAIB"48vıxx\`m\KPXRJofBUWXrccJoleyUAZb_`nNOeuyhjXs]QX`ttw{wujL[HTgjr}|kjvu}jjgrqllprwlbfe;IN,3>uŧque]saJYbZGkoDRYQlhdOjtdx_=Ua\VpaPh{j~q[zsXYZ{i}xxuwhnESJLjp{~xxiv{|vgnk~nnyrkzlkXe?=V?9ChǩƭxopWmmOPfcOY}SEWJ`ogYXmtrCC\_VfuYd}uq]kc_Xpk{|tqty`nI?H:Vws}z{yl|u}wyyw~wyyu|~ppaPPH/OZRhhejxeQfq_Uyq?HCGombSwvY6L]ZY~wpzylgsolbvk~}ghb{pVXuF+;6_~uw|{l|qq~vuyxy|yxvypicYOQ<7YS[lqdzv_[ys^c[>K>Vuk[_~}F=V^Pduo~fzozduziw{}tos_nzdQji/05U`U[swpstw{x{u{is~wpwrc|l_aulV]rC'50U}Ηlqruxw|uuw|mnwupumltzixhFHGTSFQ_XYqzzq{pw~zx~exwoyo`vm^^mmXUpP&44Oբqmo~y~xpu{q}ohpkujtht}kipEB@LRK?IXPwr~~}v{r`ISIi{pak~HF]a[n{wpuyysnymuy}qtjds\Y^j]Mai7.LQLH]em{}{}|YTP[un\|^BViktv{|xhv|~s}rk~mVmZQMVUKXoQ7DHxÒurwywk}nntzqgnuujflrzWfvsqxrhQfO79COQKLcbz|}}{VYPptjb}OH`no~|}wm|ru{xmp~ZbhTONXPMeh@;DTѭruxxewvzmztkexsjjb{s_YlupwsnWXb@9>KPNGUadsd[Vayt_|`EXlpy~{z|lw|v{ql}cYmXRMURL]nL7@Fzлxw}x}yermyxleplrmeyogTfsquun_QeH:=HPOGK\Zys}w^]\xjltKPfptz|hwvtlsqVkaTNNTNTk_;=ET[\Wbmyykgw}|fpiR_u~{j|zvhdkroWSeNNEDKRcujRW`ź|zrvn}flkvrrwiehzı|o|~TONdzmZ`_nw|pWb~sxMXX@LJ^kkW\lxssiORQl~|]_]mzyxi~`VAYDHPMcojZxz{}Y_]dtuxzmSVYMaX\ppYXiVwv}dW_Tjkqsj{cwerevsqm^jlѝq`wfe|{|smQ[WV{|lk~~dh|jigezkcvpUTgKctrjdm_p{qg}`jYB\oťdvVH^firo[KXRJmpf|ej~{vfnqR`ywzu}xguiMSdH]qlwruyi{z}n^I[E>[qdUcdlwqWJVIGn|e`qVfsqm|q]geGcv}zysfq^EU`F`qiuq{yi~zx}~uRE[ABZqgYddp}uVLWFGpza^kQfqnk{o^gbEhs{||uiq^EW_H_liszoyvg~{{w~vSE[CBWln[d_n{YKWEEn|`XcMdpnjzxejbEex{|wiq_EU_H]hiovzozwf~}~w{t~wUF\JASfv]d^jaKZHBh~dS`K\pmjwjkfG\z|{kqeJRfMYe~pmwruu|gt|}on}z]EVRAR]egbcnJXN?]mPXKQplkotmjLN~~}zppkOLeSVbuyjxrqlkv}jm~{fHO\DPVxoej^y{STZBNxyVPPChomlpo[Bj|}}yop[H^bRchntxxnygy}srxozpQDXNKXejqdljL_LCegKP@Trmlu|rjFP~yrqgMPgW`e{|rs~qlntwgiĵzs_CKZJY\~ypwbz}WU_ELv~WKJ>dqmmwr]>`yyro\I[fYjjz|vp{ixzupieƮ|tjQ?RUQdez|weuO_XDYtMJ?DpoopwqP@n|{tqjRLcadqs~~lujqsdwlm½um]@EZRch{|kt}aSgMKq^HH8[unn~ue>Tuzvso^KYh`tn{xu{lwzpkdaİtodK=RXXome}]eunbhrrO^uoZ]j_dkji~iovxnxiaGq|sxuo|}{bl|_eybHgsLIj|ff^oc_\>&Qovaak`s|yulvnipxvK]\E}`m{plmjtwituy}n{zerb7~boY[jr{zksYWOUzuyd}v·ARFkjKE5YRJPryvwrP8nmozx}ps}sf_}lkK[cEWxdk|gpG_p|kE9^p̖|i]FZPctv]bxYtq\HTLU>9UNtn~r~{e`yge^N^_oĮh[tseur{\hoxxZGU<5]}Z^gL`xag^bpVetmj5V[2HXa`z\fpr_l^HsOKcSi~~c~jp{zů~iu\G[K[Kizugv[^|uTfhKP\Y^B3>!Xpkl|i}xkVxiwfboa^SiisǢogovxmxznr_j}wxranD42>WuŖcc{[idbsUdrfp?ep9?:9Tp`~qjlqyirkV{hZoTAi^xĬanyz}V{yx^t\Z8%pjįgX]flNMQ_b]GJ*4N?eshxjQdrrntvmhfW7rĝt{y~q~wu}{wq{\qŽ~ohP@BABBU}ʮvlgpfh_ufmNJ~[MMD8KSh~~rlzy}uq_o\o_nwMcYB]eº`spuewchw̫tv{h^0CZ@T{ŲkrczjUGV\bWKM4RG,T]a{qtxyktfqxmxwwuU=ip|~gwjzv|}dox}~ĸzrXKVML37F\Է{ynvnlzohIQx^S[TDSDUe|nuh{xtv^eTyart[r]MQRwĹszhsfT]|yp`?KdA=XjȌhy_Q\]bRIM?cS4EEFss~yiwjpui|~~zd?nln_rpqb|yvq}mkbǘr~bQWVV?55;vž̨~ykk[IthQ\_O[RSX`\PwldppkUmggvflnRZVgpk{|j|q|`Uexn|fS=fVANY]rqob`ki`HI>YhLBN@Oc\yzttxj|{olyw}xDXsqiSmox}qzi]^|l\W`ZL742[ol˯kwyyt~oQ<6+'Rv~\iTyhlus}^dm~mhT+3QgnsDZp|n^pt{~d+&1N_k~zqDTUN=9qt]RRA'XTXTjnQpknY}LCF8IvǨ|gps^kuxfqM_x`PW{ȳu{sYNWnpU[QQoVeKALjxq}inowYvxZqjcALoofG:`_kk²olwsq~{KPT<62hƝydTs_RDf~oH1P{fekoxfv|luxfc{{xrc^?-Bkf~y\qugdlebŪwϻursYL3?UZwȦ{a_fsj\onbaT[*IhZdGnvnUKvfk{of|sZm[5Kqhjxknxkwwpykkal;+,$bȾk{h`pfoVwlHQLUIZgck}{qeyj_k|yquyse^4(\Xtrxxvym]w|iRdʧr{oc9KI;LShķVldOOrlY;Kn]9:HRMg~xkiezpgurqhZQTP[hzodmYzkkxzmvhS-"]¦{nkWbcWgzy[\>>kWiijm|nsxy}~WljrS{eMrqI-6S`s`tf`{}prwecTF>LAFjm_bTF]zui_{q`Vh`IXxtvrko|||rnujdl}lZU4Hpm[w{WXpk¹svp`[1WE.Wnii`e]`pzsPGMKY^zVnze{vdZ{a^YQGs|tiggU[iff˸zoux}xfovhwahtXarU88aZ]xyMR}wgguNR6O|Vt|ois{d}bf^_`xxaKLWctįliZeda~kvogahvh^[]O?E\T~yh@txrmXgZC+Ezjfqh]f{rkZPqunydSG?pzovzw\jw~n[ZVNUxeR:J`^rԵosWgalu{mOKRMTZ~nxuzvkjso|Eo{hrRfZE3@yhqqo|mqjYfhdYVWC/;_Ĕni\MX^PYmXM9\x}td|xiiSXXcm}fPJJLNfŢku^oomazVOllQCH=GQc}y\ignm[JPSULc_sxmv_g{NGSRMTkUG?fpvm~}p^j|ywdDybTI1GFFR~a_fUPh]vizpoplxwo~ynqsGvwbxk^QUZNVzÙp]}utvl[zxppu~a{T`\cabU41ASj}̮}ovbk{TV=Qad]_|a`dZbqzlWmuXGV[e}s}}xnbp{`\yeLQH76KA?@bʧðbklg^]`TLRirt{umyw}j`mq|}UipHI5F^di{it~uuyogw^ZXPL73?Wfiþn`hr[LB4?h^hawze}nkLv\eYXSRJ?UrsvWtwctg~y|shk|~~|Y;IYSKOr~Ǿ||y{si[TVX\murc|hleYFPzpRH=-;__nprzphgl~|v_Sgqwve8AP7FF^}˳srcEKmxtzktrvwqbhifl|uXz^AKHOi{o}xqw`SKPZ{~ckT`TE<3;IZcm}uwnL=q_klcwpid|tifiejiNoh>2Riu£sz`p|Zows|qSWOLBWviD%")[àgrPHKYh}wyvw~}xknrh_]mqkcM,Migzow{|`Qgq\MR\Z[bR-LH;[q~tfrse_m{iGDIUeopitmtgmdTy\@DLdy~|~{yvqj\xtqd]S\qTXKPwqi_O<7bɼtx~yioOEzgswv|_z`fLPXVOMgjQ@BWit}\n~cjtmmk_WVfbVLJMILRFnĵ|sfl}g{vnrh\pqzytrilg[W[gbXZR7ayvzr}stp]Rd{ppxzxdWcsifgiZVD2YTMf|~}wZVX`ntrnlpqhi|wcQIHau`iRUofflkkvw|zxujecua~{}dFiXJS=D?3+8]kiq}˯³{hndYBx~wvx{jq]][]cnvbPIN\i~mbce`]ykkYQOP\VOJHH@I^Svwhk\QG?[wuooqvzVJYu~|}u^VL?pw|z~mY]qsnqhgycI;5?[mTQ86Zhw´hZZW`mpx~{kUGrkbiKF=29Uoj~bhiey|~}zzL^gFUW[\ZU?8E[zȰrosvebD`rtspo^V]emq]NFEMTsvh~gQJMSdywsmwhM8F`snc]H?PGouwrpd`bwo{opodm~lQMLRgv[PC;bw}sbT^ju~wmmb_UH=^jOR8&,/?d̷v{tlnlzyxppUP{fepeb\ZTN`uztzsolu`jcb}owrJdiSTS[ZWL64=Ofz©íujkQQV`h}zpsutk|qjXRetvjbcUfuns|voiqzvx{ypui`YJSgi^KFA=PT=HFRħz|sqfb`lz}wzoacafg[TokNOA9HWfsyigx|{t~li^VTXIEn]IPFEPZgk}̳zwp^\g~mv|lr|ilL]~lhb\YRSLOcs|y}{^lz[lhakkqyuqurkDSaeh`S;:KT^crz{µvillu}qgYMIKg|{kTH@Gfbe}t{zulgahtswx~o~zof]GAUZleevowstsq|i`skgemua\VPS^{}]mǺncepkbesgx~yjUIUxuR?\gkmijjwtw{khyFCHPWdvhazôgIV]lvh^IEEGH]}hsСxgg]`vgnkm{jaQabZƸzrzjM~c?MsegF(2MADQRgwzhdvmXuw_jyxnlH]txd_k`^Wamvtlejkvw\fgUZ\ѽpei~jѻYNir˒v^NLDD?ELRMTK1tqdy|~uk\i[bfkmYWUEHhyrtxm|Y[>̥qcv|_[vn|tsȎ}gK,6Kiסr[P/SnPY-WiO_giRTvq|k}pcgffzugflxn[ezaRSdwsZuoc_tqgD]yssĽ}q}zz_]J@P̼ʡiqI@KemSCLJE[iqXwx_E@_sh|jOYcU\xkJnqwbiW_ƣtk{{qom|zv_fxdotpgH(\bo|¼pJng\UalFAmzpSFv|L:HVYUqz|pQli_]|y{ZaG\Wisx~jZ[lb~q{z{iXvna]bE1fǢxomiT[\KZo}|fiTBnXgnuxkl{TSvbn^^>itP:Nmx~btljse{rmjvoY^ROOeU\Įg^YZaTH[[NtoXP`fLLxxs{vwpjRhlnnzb_nYKEvatñhs^]tqTU|sw«||yumVOT;,EFfngúҵniugJ35BfrF+2KO]wb`v|ac\hqaN?e]CJ[rhҹ˯tco`^iukpu[xntxvmSFS60Sqpū˷sZvYNNUTc[T@9ME_c~tWyvaus_hvgHRlH7?TƭrUXizih|f{{xfl`:GgSSTT|کhts8Xrh^jWVWZN:zisgY\vgyysSfm>OudUQjȚakvx|pv]|oxwmyX{W@PTKeevʶfSThtzmSSimdOvmhFXjk\Wwtd`L:moUb\yȖ}hrxtuixv~xSyj3/`ZQ_^џfv|WZN^ig\eR\zVqqoXkb}a_yq]zANoXazxlymi}l|x`jk{}okrqxZ5\Z?_tu׳~tzY`[^hlPRRVs\LpNxlc|mYqfZo{q@D{_@]Ƨr}qf~yuhwzbyzxv~^\{nq]:NZ:Glx~ǽtwmVNeka[K`jW|Tf^ntc~_ruL:hW?c}zv~|jsmvuuibsybVuyb9Tg9skCXov{|v|~uswoindsmets_GdP=[Ydpµ~{r}yhjZWePridrzjknf_wdJPQO2*U}oeimYlsrVqmcr??D^\:3FIUp˻mrrbnq^@ohUnzpzztk}cpq}|pqc>HdgsRxs~y|}lih|wgN^}pqkqn}ZEwfJ:LZEEIį͘~sr_RXadNbfiz~he`SzcThpaAn^N-L~b}̲|e`t`ix}{u\Y^c_PoZ: BFSHEƟcjm]\ppf`{{v^lSn|fl;/3SbU\~|jnjmxkYtbOmydMo[[ac_OHA27cԧzv|yhsgSE`lua_sq}vRhmddq~qF`gHGO{x~xbyHQXuwokzW.TygGCPI8WNRaēæo|q~yqckWPkvyYgmnaia`oqiGbVH4QwuĖvwwmhpaf~omwwnQ`JThDI'%2WŻZ\paliO>nnpgs^zu}a]P~j\b_RHUWZpī{r~}vahd]`v~iq~u[~iH^{T\llL&GrRL]ҸwujgjW@Mfetu`yrjI;uaE@MB+g|Ӱqɫzxbt}otakvsnv|dbaj^IR_Za`b,1\L]aЯqrHcwfqxoxzz~g^dtyhlaKmIOKEYxy`TUZeWHzyceWVeQWDm[9<=EDcx͸qzQQygsqva{buqSqp_PmpivoI0Svuzt}ybbgearhrg]B`f^@0bk;%0EFJݺԬ|ykzYe[bb~}sv|s`W\kh[kmjf`d,Ywu}rwfWH}usv~ysdjTHQ_k`QX,CV@\`pû}uZpVEW`UmomgZ[xlmX`v>FIW`sro~u}y|cG{Z|jfj[S__·}u{{jPbcpwk|f`JEO`gPLRFOI`BK±ոh~fNk~nuvqaem[_mspVVJ7mdo~~yajNweLWkljwuNYxkXPdnPS,WQ2R\tȫ~uv{rqp`luofbYUHCiun\`ylabp\QqyusomaR{yqnx~s{kmnt_?Ritxy|}}q}w`BPW[WTSYnkk~ohv~y}h(JV[`px{izxx~Wm}z{txrjYz}~|v`4.ER^]eckrol\kz/wbTUQ^YYLSv`c^^wutp~zʢrtjipxva3C[psw~zp`]s}`YY^,Zxxvfc\v]hv[idnt}uDzkhjWSbz^DeóǙx[:/OXKB>vp{rSHot^_`|}v{jT^hsd\Pbnmìzz}uwxrrM|~pj\nseprmbnĻŬ~rtgKLUb[PJPn_¬}z{mhi_ha2UeOM6`{yz^FXzO=DVJE7j}u~g|fG_f`aq~uztXXdrl\SNiW~~wtwp{|NpqrX~k{xfl~sn_ǺqstmOKTa^MEB^N±vondkh9WlTW9Twts\ER}S>BSHB7`yk}coL[jaamz~wzx\Wboo[SF`K~xztzVfrw\hlqhfxskbzµwzwstXHR^eQK:SIlutjiuOFt]\H?ovufLGmbD=MOC>F{gxgxZQpxfcd|w}jU[fr`VFSRW{|yumT~tqV~tjpckvhgeòt|rriKHR`[KC@XDxvktqF`u[a@Krqo[IS|~VA?NFB<]|e{_}TXwujgoyxvy_PZgo\UD[Ni{|uwe]xwjXomjamqfeluvnnaCDM\SH@GYKª}zpzoIiu]cANrpmYLY~|UA>LB@>e~g}`~V[wypmuy~wtv]KTajXTH`Uj}~wyi_{xqUzonl`gnbel²srjh`@=GXVJFGcPį}|qywRe}ahKGnqo^NWw[D?I?FNxetVd~}zwqw}qmigIAJ[bWXXxc}wbpv|iX~zlv}eYcc`i}szuyvkc][M15?SOOPguo~wmVyxdkHOljhVTcvQ@I@JXygvXdzov|~xujhdbG>EU]UY`~i{cxu}jXw|}rkxwbWb``lvs{v}pznm_[UVC-5ASLQTupĽfgrnhG]ihbT^qiN@EICOgx{li\m~ptyyoifdY>BJ]WVVmtnpiv}\dxwwjql]Zc[gznzmmupke[YUQ528JMIN`vfȯwdmuXQighY[i{YHAKBJYugz\fzmu|{vshhdcG?DTWOQ\t_zfue[ttwkl{p`XcZbo~pxmqsslh\[VU;27FLDHRmZͷjr{cRgfh]]k`NDLDJYoh{agxo~yy{ttijfgOBDOVJMTmXixpYorumjywdW_[]jvtsqzntjj]\XYG36=K@BFdXpŭs{}{vUbjid\gxnWHKKFSnnuhncqzns}vwmigg^EDFTKIKb_`rrx`bpnqhrl^Z`WdxwqzhmpkgbYZWV<46CE=BQdRкsymXihjabpgUIPIM\lksgiv|qr}tyqsghegVBCHTHJMhYnoz~||\fnongtiZ[]Xfwp{xeimed]XWUR955C@=CXbWζxzm\igjafsfVJQJQbplvhkx}qr}rvnpefcfVCBGRGJQj\pq{|adllmgtj\Z^Ygvnw|{ybxgic`[SUSR:34AC>FWiYϺx}u\ihkderl[LROQbquplis|vjnsklcbbb[DAAOIJRjigvtwl^jhmfo|o`V^Xcw{np{v{fdn`bYZPQORD349GAITpfzƼĮ|~gbigibnz{cUPVQ`vn|fn|}ivrnlhf]`]_K@@IQKTbyfpzz^fhkihwwfX\]_qyrjsrxl[uu]_VVOMNOJ645EDIVpsqÿȱ}qaifjdlxhZPWS`u}t|glw|kntjjcd[^\_OA@FRLUc|lp|{abfghfuzhY[^_rwthpoukWpsZ\TTMKMMK756EGKXrwsÿɱ~rbjgkenyj\SZWe|u}hny||~gnphfa`Y][^LB@GPLXi}krx~{_dcgfiwteW\\bvxmhpzmtdWslXXPQIKLOG657FEN]xq~îmgjikhu{gXV[Zm~svjqxwc{iibb\Z]]ZFB@LLO[stow{wp`edifr}m^W^\lw|glvmqoVbu]WROLINMO=64>CESivi˶iminjr~qbV\Zf{}yzloxz|~glpgd`_Y_]_ND?EKIUgve~xwzaechemzpbU[YdyzywiksonqZ\ubWTOOHNMQA739A?L^qaͼnplpnuvgX]Zdyx|vpqy}}kjvif`a[a_bSF@BJFQ`q_iigfel{weUROXuuiXhzceqt_ogo~p_achdVTU`\eqpzs~tcunkx`^bgmq}gigXIauoynrznnpqmZTNWSV`rkgr|kzy{nsszznb`Vgbezqdn~wjhRiy[QKPB>=GMO]msarop|{eijruxaZMTwwbp|fjtob|ys~~y|{~uhedokuqjkTdq][jURUY`dv}kYRQRjcjh`jzx{jM]nfw~xtaZU^]`l{vj~bHD?U~v{zm^GKThpy~~{fgjSJhgbiinhmkneXTS[Wamyfu{vzvoqs{~qijk{lk\?Mkiz{cUJLJGR^^Ktqwuwomp{}hjipqzwcNNM^uhirvezvr|x~~{ywzxswvbneTVS[\ju}gWILLaxj`pr^dmlkiQMqz{vgb`hcnzo~~z_I@HTzy}pZJQ[mq|{{p^e`IQqllkijgmlpc[W[`]jvycx}utrw||kknz~}x~h\^N@aycYPUPS_hZUqv{~x{iuo~zjkhoozjYLT\zuxflu}ll|xx~xs|~tuwqngs`YVX^bpxq_NKQZxv}`e{yd^erlag[Kgpjdkim{zr}vzv|y^MBNY{zuaNSZkpzyvm\c`KOrsolhliqoti`\^c`nxwa~z||~yvsx}|lmqvsww^X\LDilcZ^W]glX]|pu|{}}}vwi|zpukihno|zeTOWb}zofnz{zfy|v|yz{nzv}xptv}mxq`^Y`bnw|gWLQSl~{kcr{i[aiz~~qahbMV{{rkmnm{}l}twuz~~eSDJQl~kUW\krz{vo^edNLlplkionusxmc^_c`nvs^|x{~|z~zxsy}|lnm}pqt~s]_\GDgtxxngh_l}|zoipyved{{cai_koolryiXWVdu}wPU{xogvojj]br~ftypvwxtrieox{|fdsq]o|}qaaak~|`bvz|m\tz}{xta^mrt~|vnv}}qwishgqv{ytc]^b}}SCX|qa_kaQbztvuxnndesyv~{zviotv}cid`qwg]__vx{fZgyptwj\O]qyvujkecWX_ix}vfkyacmbhkw|p__]jv{r_LVj}na{}~|yqrku{wdR_[aqr\QOPgz~hyecttfQMZmzus^k|zw~zoa_Y[m|{{{nptwqxv}vxv~yeTEUl}dVmskifqt~~xjc[]Wcosewxw}}|yriostj|gXhgjtnefhy~jery{l\VRTj|}sn_]`hsyun_eyqZ]iafhrx}o_`_l{qrnaP[mue{}|ruozzhrxdN\gkzm[[Xi|qon`f{~}viZMYf{~s~|ha}~zod\^\o{zrx{|u{nwz}~pu{wj_TVl~x__yxsmlsv~{}pf]Z\]oyuetw{~{{rotxxggys]\rs||kijtyx{kp}wk_SVXi~|tpcffntz{ogZkwhUbnkpnwxzmafg{~zjl~~|uja[l|qjwrwvwe^nlWSmu{h``dzl{}d^k{xyneVWdruxtbo{~of[_anvyz~rn{x{||hhwuslc\Zoib{ysnuw{}rg_\`as{wfttz~{{sosu|rcewr]\txpnpzyuzlr}vyf]SZ]pztgjhqt{xmc\pvdUgnnqpy{yldhlyrfn~zzph\^pmt~tvv}~wk[gpbP_qvzjjgxsrn`dxzzrfZQ^hz{w|hf|yvka_bg|zxvx|s~ps{|{idrurmaYRbtuh}}wpvx~vkc^a`ovrd~xv|{zqloq{~{qdizs\Zn{nZZZg|zqpqzl^lv~woc{||iqenuwzvy{}qvqfopztopwteefqy{zyXYq~nqptqsh~~}t}r~zw}}zrlqr~|{gmm~~zzmlnw{{uoZ_n|xxhyzfeug^ijmpolejkv}}usxfqux}|~qkmqyy~lVUiv{xfn|xxsv{y~||]^fl}qdY\]p}|jqthm{m]U`k~{|li~}~rligw}x}}|{poqt~}uro~~}|iZMYl|jZkzuqiqvuphjepwtgw{s}{}}ztlpoy~zrpl[ipw~voqtxvzifq~{vhd`ev~{{{snaa`elsxnicss]`nntr|ympo}|ysq{l_S`nui}}{}yy~~tq{dScmxqhhhyxftmck{{}rfWT_k{~y}|ik}}wmjil|{y|rqru~|uqpzr|zuf[P^oq_m~zvlrwuohjfpyvhq~s{||~zvlpou{vpk|nZdt|~tvv|xx{ogr{ysibefy|}|yskceckowqicfzyfYiswxwupsuuzlo}zzqg]_o|qn}|}z~~|qfjxo[Xp}qmlqvh||jbmxwwlcW]gr}}|xxhs|wphlmy}~{rusy{wtmvnhsrpld_^p}ncvxqxz{rlimm|~ug|ys{x|{ysnqpwzwngj{vd\s{xy}yt{purt{|}}oo}uusv|o\U~z[^oqrlswvwuqpm]h|rg[`cnv}ttxym|yor{qhp}kdv{|}~vtzj\ertxogZ\aiszz{zwchz~}|y~u~oq}trnedh|nz}zryregwz|ukirxwzwrvz~|zvyz|yhbzjo}~qqnpv|xxxzvpu{~lnwkk}|pdhhptzp{}ywxtypb]}qq{vjbo}wd_oruu{xu{xp|rfYajyps}}}{v{|r~u|z~{ujhuvwtzqkfn~xif|~y}yqa[grx~zlmnu|pztex{ketythactxlz~{{|xzvrn|}tehlvoluz~spmsqyym`ry~}ukchlx~vqwrfu}~~umkzmpyy}unachoz{fjx}ujc^lwsigxxiYdptspx|{rntwx{mn|}|uiejny~}{~kh}||ljv{}qst|~||zrnhuw~xm`TXXafqsv|~h^p{}~}xic|~wxpkabjr|xoguy~{}}{|wkqwnYbv~zrur{{n_lwkqoqqmhaghsxyokoshw~xppqxt~zmgjlv{spxt~{{utlg]bht}|jgstunsw{qrox{qtxq|ul`efpv~|o^iytgy{~}xqjpstrhfgy|wm_Y]`jnwrst}s`\s|{mhxrsnqmmgkqx{xql|~{}prsy~{}~q]ey}z}yjZcxmstttniaghpv|zposzhnz~~x}qrp~xvj`depxwj{|qtumlhebbnu|p_jt{zx~~tnpru~pmww{wqhekmvxzy{wd^urjy~z~zysuz|~zzyylkmv{}yn_]_cjoupsvq_czvglsrqnnjienrz~no}|~~~vkpowy|wx{u`Uh|wh]rvjmhjhgefoqxy~uppztgt}{pns{{y|uhfjmuy{}qromojkhkikisy~}jhuy{swz|rutx{vnw{}|tj`ddlpxxv{~j^q}of~|~y|yxtu{{}~|p|ymlpy~tk]]`emrwquxp_dz}viqqoljlijhsv{z}~ms~ytmpovw|vtu|q_[steaqlmjmjidhknsvxoosolz}zpss}yoeijrw~|vjzrixkkdfccafovuiu{{||rqqxyysv{{|tmadfkqvxsw{}j[f}shzwyuwvusryz~~q|xnmpwzqcccflpsnrxqah{~obq}opmnmjgemp{|mwwjijmrvztx{l[_u}l`nqqmmmifaffnszwrt}uenyxttu|z|sggknv{|nnylsrnjfgca\dhs}qt{uvu~}vo~wryy|yrhchhosyuv{wd^p}{mm~yyvzvtmopsz}wtxoomv{xt}qfigmpvssy}j^kyxid{poikhe_`eiu~{p{}}uikknrvxrvz|iX]q}qeqpokklifbfelrzxswyhnz~yuvu~yvymhklsv|y|sm~yjiumkfidc]adkvzs~yzw|~wpvuzzzzri`ccimtuszm^fwri{{{vwwtplpov{}nt~upqpzyvuznikkqsyuw}zhaq}vhgxomhkgf`cgku}zr~{}|tijjlpsuoty|iY^q~tgstqmlmjgafdjpwxsx}kmyzsvt|}yvzqhlkqt{zzuhs|mdxzjhaba`^_hkwsy{xyy~xs|w{y{voeaeeknsorxud^p{mkwvrvttopst{}~xlvzrrsx}xkjjkorwryveew~nbirmjgiggdkmt|sq}}}}tlcfdikpoms{ueYfypi}sqklkjgfkjptzutyufozwvxz~}~wljmmsv|x}nj|refofc_cadaint}{tz|z{t{wzz{zrjaedhkppnv~zi]i{qjwvpsrqnmrrx|}}yjqzxqqtw~ymlllqswsyuedv}mbjpjfdfegdmou|sr~|}}~zrhadbfgmijr{qbZk}{mk|rpknllgiklrvztx~ogs|}vyx~~z{riljorxyyvhruf`uwhf_babacknx~s|~~}~{uv{yzxyrlacdfknqnv}n_cutkzzwqqrpnkpntw~|zzioyzsrtu}zxz}qppptvzv}wfev|lcoplfefedaghpvswyw~}xqecddhjnjpw}k[Zmzxnxzuomnkidhfkowxw~|jmwzvwv|xttzomnnsuzw~}mhx~mbhngb_a__\bcjs}|z}|up~z~z|xqhbedikqnr{wf]jxzooxupsppjkklqw|xrjt}yrtrvvqo{zqsqtv{{{sem{thd{}plegee`addls}|s{ztv|}uncddehknks{|jZ[lxzq}}xqopmjeheimuvv~}lmx|xyvzupo}snposu{x}pgspde~tid_a__Z^`cluz~~uov}~}||tmdfehjoppy}l]bq}tm}{wpppnkgkhmqyyz}kmw|vstqvropwsuswx}zoeq}qffynjcfdd`bdfnu~}t}ysw~{{slbcbcfikirzziZ\mzyq|xropnkgjgkovxwlkv}xzv{xrq|smpmqsyx{tgn|rd`szjf^___]^demtz~zts}~y{vpfcfdijpms|ue]jxzooxtmommhjkjptzw|tgnwztutuytsxtttsvy}z{kgv|lcjtlfbecdaghms{yt~~wt~~zxvog_a_bchgis|teYarvq|wnonmjilkpszx{yimw{zzy}xuwyomnmqsxv}}mfsyh^b{qfa\`_a_eimu}yxt|~{yxqjbdcfhlmnx{l_eu~plzummmliilkosywy|pkkjfkpw~zvyv}~}rwyzy~xzzussuz~vw|xt{|x}zxry}~zzzuqsojpq|~ylgkoz~}xvz~~tyu}qyyskr}{~|wyy{ssuxty}}spvzwtz}|~wy{|~t}xsuyprvz{zzywsw}yx{|~}||}x~rozns~}wqv{}xvtqooyz~~|vwy|trz|{}xrnklnkdoypz{|{|yyw}|{urnus{|z~txstuu}uzzu}t~|zzzutsnlhroryx~{v{~zzz~xt|kqwwsx}zy~|wzxpsy|~y}ytzx{}tu~}~|xtjhqlqpq~~|yypq~}}xrtspyx}~~}{~xw{y{tsx~||y}x}yqos|vpoujklourz{v}}{z||}}piipr|wz|z~}tnpz}y}}zy~xw}{~~tvmhqjpsssw}w{}{yy}z||wquvq{wuw}x|ut|}{yyulr}~~{u}{}|tmtw|xtlsknmky~yw|zwouz~r{yvmqvz{w{~~}|{}xssx{~}|zvw}zvwpo{zpvrokivwst|}wxv{t}}{~}xytsoy}~zz}~}}~~xtyxxwyy~~|ytu}}|zz~~{z}rssjrow|{}}|w}|{|{~}upooqyxy|{vxnz}py}{srw|~|xvzr}|yv{xu{pqnojqv|tuqwxx|{}z{umkw|~{~xyzyrtvxzvvw}}zxuuuzx}z}{rvklwx{vwqv}}wvszw{|}~{rnoz}{}|z{{|vzx~|yz}~}m|}{~u~~vrz~}toort|x{x~xwzxz}|y|~{xrrvvvy{zrz}}{}xxs{z{z}z~wwsx}}|||~xv|}zpsotsuu~}z{yuw{{|vuwwupw{zz~~{x|{x|s}~yx|yz{{xqrmvy|~u{qwyqvuzy}y}x|yvx}~~|z{qyxzywutw|z~{||wzx~zw|}|~zyw}x}uxz~}}}roysqvyy~ywsxxw~w{}wzzry}qx}xuuzz~|v}{}z|~}|xsr{xvtzrs||}ywxwyzz~xquyquz}tstxwsv~xz|y}{{x}x{~z}s{w}v}v~zzrxgmry~{xtsvyzpx~{wx}~vv~u{x~zx~ywywty|{z}~}xswxyx}v~|zwyv|v}wx}xzxy~}xt~y{|uwv}zzzwyvpqt}ywy{vwqxw}|y|vwstmux~v|}}}uuwypq{z|ztqtsy~|rvuyx~|}puypsy|zwsu|zyzz~zswrw~~}zyw|||~zs~xxy{~||~vtonmiwxvrvxytzwvuswv|}~~|umttwzy~}{tz|z{rz~xtxuvxyuyyx|~tt|zrqu{sv~~svsz{yxwvwqly}}tyruw{{{|||uxmpu~}y|{xv|y|~~{}wnw|yyx}|yzwrvqx|}ysvx}{z}vommtuw}~~|svxww~|xvuwwuu{x|zwx~wy}}vvxxrxw~wqpurqwy|uyrx}~y~yxqw{vy~~ypwpzx}uyxust~~~yx}|{rtwz~~{ysx|xu{zvsyz|{~{w}xvxzpuz}zw|yww~~~sttvrq~{}{vmvstv|}w}y~~~xw~}}xttvuvwyys|u{sq|~yyx||w{zwuu~{}}u{yrsv{vwyw|}|yz|~}syyvszz|{}x|zoyv|y~ytmruvyz|{z|}~}zxz~pvzwvv{{wttpy|}|}uv~|~{~xxy~|}yw|wzyrovsqqu~{yuvxv{~|zvz|}~}ywxzyz}yst{xxx~|{zykyu{||~|~w|}yz~}}y|~{xumovusstvzwsu||}~{|yx~}|u}swv}~|swvryv|~xsxu|~v~wtz~}}|}|}||~{y}urltytwuz|pwr}xz{~~||wuqs~}}~}|ptyowpz|zq}z}}}y{{{~|vsux}ssxols|{~}xzw||xrvuxvu~z}yw|vzxtwo{||{{rpo|~z{}}x{ztkqqqoquvtt~y|wy{~wz}vvrv}{~~zsvqzzs~~xxuxzuru|}}xz|{~vyqyvrorty}wtwzpvnwxty|{}~~{u}{uys{vturutty~{rx{xx{~~~{v~|zyrq}}zulu}xomus~|xyt~{u|y{v~}wsu{{}xz~wyvup|}yuvpnps|x|zy|{}wzy}|yonutu|zvw{r~uvvtxx~|v||wqjv~|{y}su~{yv|}|zt}{qoxz~zy{}{|}x}r|}wuvurnwxwu~xumjs{}{u~~~vow{y}qtzuzz|y~uuxzw~~}|zy~|{~yptyy{|{sosrwrqr}w}yszyqx}}|~y~|x{ztvz~wyswrsv}y{tslqt~}~tv}zw}y~s|wlnnryu{v|tpxvtxyx~||zwnty~~xr{}{suw|vwxywpo{xt}|~|}x{}v||yzxuytrw~t|vvoohlq{~{~x~~tqwv}}|~~zqvuy|wxwqttxw|~}{zzrqtx~}||stvytlzzzyvw}}{vmr|}{yw}zww{yzytxru{|}{wurmwx~y|~~{tw}|}|zvojkvq{vv|wrvvxzw}yxvqwy|{uqtu|xx{}~~{}up|~~tvx{~{|~yzz|y{usrupttv{uxwrqmnv|zz~x||z{}skpt}|vv{{vpt}u}~zz}|~yqsvz|uyvkovw~}}z}vlnosw~{yz~z~x{yvwkeljbklnxw{uwx{}|yzxpspqvlnrlhvy|mkovy~~x}yt|{y~z|~~zlpunowt{{vxyxxzvy~w|z{|tvuqwrlrqhr{~|xqv|}{|yx{~~~pmuroyy~}ww~zozxxqtrglngmy{{}xxpfnuuyzv~}~urqmwut|{{}wmttpxyzvunluv{z|}y|v}{}vx~y}z{sqkrqlprwx~vhnokqqnxwy~}xyypjmghrqt~y{{u{z|{z~~wzx{{}uv{wz|{z|xz||}tnomw{vyy|zw~sy}{~z~||z}~{xxt|sz{{~z~qrwsv|y|~ytswqllnr}~|}{y~nkmnz|x|yzzu{pwyxyspwtu}x{z{z{y|v~|}tz||y~}rpoq{zvwrrx|xouutrjhonoz~}yuqvwnsxtuxt|ww~{u|~zrwyxw{}}|~~ty}zy|}~poqt}}y|w~v~~ut|}|~~}}|}uxq}y||zwy|{|zw{~xy}{{psxtplt{zvwznloq{|}w{v}~sy||xpkrrow~}||y}{{~z{}~~sxzz~~}~upppv|wwxpzxzuvzzwohlplr{}||~www~tszxuusu}yw|z~v{{~stxw{}z|{~x|}}ytz~~zxqprw~{|z}}}z}uv}||~{{}||syry~|}{|~{xz~xx}zxpvzvtpuz~wuwzpmnqy|~}w{v~uy|{rlpsnu~|{y|||{}x}}twz{~{wplmpyyuxst~}|vz|~umkqpny~z}{|yv|}sz{xttsz|wz~|}w}~{}zqwwy}||}yy~zw}|{vz~~{{vrpsy~y~y|}}wx~{~zyyzxz|~||t|u}y~||~~{xz~wy}zvqyzxwsvz|uuyxrnnrz{~|x{x}vy|zqmprox|zy}{~|z{}~syz}}yy}snilpxvuxsz~zy{upntpr}{|||}zw|w~zyttv|yy|~zu}y~tsxw|{~~zwz|vz~|yx{{zvprv}||z~}||zw}||yww|zvxx}|~wzwy}~|zz~xy~{vsz|xzvux}vvzytonrx~z}|x{yvxzyrnprpy}z{~{~~~|z|~}~uzz~~zyzyqlilqwsvvt}z{|}uqsurz~}~{~}xzz|}zxuvz}w{{}{yz}{|sww{}|}~{xw{ww}~yw~~}~xsqty|~~z}z|~|xz|z~{wxz{twv{}{}xwy}|~|z~yz}vt{}x{yuw~xx{ztonrw|y|{y{yvxyxsnqqq{|}{~~}}y~~}v{{~~|zz|vpjjlrtqutu||~|wsvtv~||x}{~|{xwx|{wz{{|w|}{txx}|||zxxzuy}|vx~}xrru{}z}|||~|{~{w{z{~}zxx{ztuv{|{{zvwv~}}y}~vw}}y|zux~yz|ztoorxzw{zz{~zvxx}ytprqr|~|}}~}{|~wz{~~|{|{yzzqlikotpstu}{}~~~{wwxu~~~zy|{zxyzyyz~}{}w{~wvyzz|{|zwzvv|~yv|~~{vrtw}y{}{z~{~~{y|x}~~{xxw{xstvz}y{yvyv~zwy~~z}{ux~{|}{tpprxyvzz{{yvww{yuqsrs}}}}~}}z~yy|~~~z~zy{yy}|rmikmspruu{~{x{v|~}z~|}{zzz~~xz|{}w~~}}uxy|{z{{}{yy{uz~~xy}yusuy}x|||}z||}~z{|y~{|zywwv{vrswy~y|yx}x{~w{~~z~{vy~|}{uqrsyxvzz}z|z|twvzxurvsv}}~}~z|yz}}{z}}ww~|yxx}zpljkototvy{{|x~|z||{z|{|y{~~|{u}~ztyy~~x{z||{x|yw~|x~}}wtuv|yy||}||z}}|z}y~~z|{|zwvvuyxrsuy|y|zy}y|x{z~}wx}~}vrrsyxvzz~z~x}tvuyyvswuw}}~}~z|y{}|y{~z|}vv~~zxyx}wokjkpspvv{}~{|}||}|{||~zz|~{}yz}vwz{zy{z}|zz|w|~y{}~zvuuy{w|{~|zy{{~}y|z{~zy|y{wvvvwzsstwyzzzx{{~~z|{}xx~wtrrwxuzy~z~x~}tvuxywsxuy}~~|yy|}~{~x|~~y|{uv~~|zyzy}uokkkqqpuv}|}}}}~~||~}z{|}|{~w~~vzz}yzz{}|y{{w~~|x}}|~zwvu{yx|{{~{x{z~~{y}y~~~yyyzyvvwvxyrstwy~wyww|x{{yy~xtssxxuzzz}y~}tvtxxwtyvz~~|z}z|~~}~{~xz}}yz|uty||zy{y}xpmkkorntu{|~|}|}~~}{|~~}}z}|x~zx{{|x{y}}|y~y{}zz}~|}zvvv|w{{~z|{yzz~|zy}y|{wy}wzwuuxvzwstuvz|vyvy~x||yy~yutsxxvzzz~y}}tvtxxwtyx{~|~{zvxkrvwxyyxxxz{{||||}}|w}zyyzyz{}}x~|}}sztrqsty}zyyv{}~~}{s~{|{}{}}z{w}z{}xq|z~~|}|{vxxz{~~{~}~y~ysxpport|{|xx~yy}vnrrluq|}zuypqywz{|z}|}y|}}{uxy||x{yuxxsrsst{|z~~z{upvorrr{wsrtjuwyz|wvux}{{v|~{}}|||z|y{}{}{~|}~y}~w~xxwzv{{}|z}y~~{{|w|u|zvpuor{{~xwpw}y{x~{{yv{u|{vvyvsyz~|y{|z|||~}~y}xvtwrw|~z~z{~xrstq}|~~usx~{z{||zx{uzty~xytuwxotu|}{x~y}{zzyvvyurx}~~yz|z{uqxs|}}~~~{sv{}x~z{|{z|wu{wzvxz}rrq}}|}wy{xz~{zy{yx}yswy}}zvvzz|vrzv|{yyz{}vuy~xz|z~~~yuz~wzy{}wuox~{|x~yyux}x{y|{{}vyv|~zwvzy}wrzw|yxxxy}xuw~xyz|{ux}w{z}xvow}z{z~ywsx{vz{~}|}xzv{}zwwyz}vt|y|xwxuy{ytx}xv{~|vw~x|}~yyox{zx}y|tryxu}y}~~}|{zv~{|{wxy{~vv~{{wvxsxyysx|yu|{vv~y}~yzowzyw~y|tqyxu}z|~~||{{v~z~|yyz|xv~|{xwxsxyysx|yv||uu}z|~y{ovzxx~y|uqyyt}{|}~{|y|u~y}{y{|yu}|xwwuwzxsu~ww{~vt{z|~{{rr{wz|~z~vrv{r|z}}{w}u}z}}y||}u}}zyvyu}xurw{z~wsx|z|~zvo}}w}~x|}xss}s|~~y{{z|v|vz{}z|}~wz~|zw|s|zwp|z{x~{ttz{~~xznwyyx}t~vnyuzvtk~}ytz{|z{zu}xx~~{qny{vvwz{wzxyvwuzv{yx~u{z{z|toq|y}{}ty{{s{{{x}|~x{zxywxzz{z~}}y}{z|y}}}xyz||z~}vz{llnxvw||{xv{z|{tv|~~syy}}w}wvs}~~}{}u~~wyw~{|zuskdqot{tp{zny{wy{{wvvxy}{}~wrv~w{xyx}w|z|y}{o{tt}xvvpjmgprxw{vx|u{xzx}z{~{vv{|~{}x{nt~}~|}wz{{||qwwuxx{~~{{sroyssswuykz{rt{}v}yzyv}u{{xvt{}|~zzw~{}zwr|ytvo~}|smtpr~tvuvty}{}su{qx~|wy}{u|{zuy~z~yxy{ysuquy}xzz{xshyst~uxzvruu}z}}vvywt|x|ytx~ztv}|~~~~tytxoyus{z|zzttpoukv|vzso|{|ur}xws|xyuu}|~yyvzzy{{{z~~|{}rzwutlw|~ypmstvzp|~uvy}}zxz~{twuv|z||~{vz~zx|~~{{~~}{{~{}vyqs{vv{|{x|trru}zy{u~}ty}|s{px|v{ztu~}{sswyyvy~x|w||~~sxzruvx~vtuozz{{xqovz~{yy~n{xvzrt}|wy{vxsuq~|~w~|~~}}{vxspmrzup}uv{s|}xyqu~~}{}{~yyvr}|yyz~~}xusz~p{y}|}~~~|}{~{spwxxvxv~{z~xyvr{{{xxxtz}zt{{swx|{{|{vsztt}~{~{|z}}z|yrtsy|~y}}ytw|~{yuxxx{~s|~t{~puu~y~|zzz}}zy}y~zz}~{zpk~}}u~{z~wwnvx|z}}v{x{ywzw{}yz|xx}y~~~~~}z|ypuvlxuurqxzz|{rvoqtvy}x~~wz}y}|yw{y{~{z|tyv||wojxzvwvvqutzx|~x|xnoklkqyzz|{~{x|}~wwxyzv{}{y{s{{z~{~{{vklu{zzxxzy{w}}}lloiikrz{y}{|}}xy{xzy}{vww|}vx~{~~wlgy{r|ywuvx}z}|{tnrljjnv{w}{|w}zw|zyzx}}x|~wy|~xrlqt}{wrrvy}y}|{qqqmkmqww|~xyxz{{|||vzw~~||~ww~yzvomyy~{tuz|}y}}rnrpmopsvy~|~vtzw|{{~~{tzx~~}xx~x~|woj}|w}uxzz}{|~~|rmqrnoorvv{|{~xvyw}z|}yt}~w~~yy}}upiz|v~|vwx{zz|tmrrnnqsuu}z|~|uxx}y|||{v~{}{}~yy|}|uoizxt~wz|}{~~uprrlostvw~}}}|ytxy|x{|}|t}zwxxz~xxtwzwxv{vy||y~xr~tp|uzxyv|||~|v{|}{~y|{|{tquz}{~}}vv{{q~}yos||~~|}|}|~xstvzyvyx~}yzxquuvtz~~~zty}|}ylx{wyz|{st|}uw}{|}~{||}}voypr|{u{{|x{{sxywyz}xvquywzttxz}~|rsyzuv{yy~}{wx{~~|}y~vy{}uyzxv~}|zuv}~xwt{w{xz|}ut}|ys~~|{s}xuyz~{}tsq~~{xwuzvvxu|}wv}~ytuz{yx|x{{~|xnm|t~{y~zywntw|{zw~}zw{}rmuupvrntsr~}~|srzvt|~zmq{sw{z|~{{~{{|wzyzx}~{}y}v~xwvx~}znsqjiq{{wur~}wrruovx|~vx~}}vv|~|t{~wzt~t~wzv|utvwz||pvvkrwnsz{~|vrrpvuw~}~{}zu{~{|v|||}|y||x~~}~|{x{x~zyvphdkqotuyxwzrqqoqvwuysx~}zox~z{x|yz}{~~sx}~~~t}|}u{~~vzyrszt|}zmw|||}u{~u~{|uzsvrruxyx~{~{{~|x||x}~}~vznmv{rnkpux|zyx}ww{{|uswqv~yz{x}{nzx}vyy{}wx}{{xx{y{x~yxwtvtsv|y}v{|~slt|vvuuyx|z~|{w{|xwzt{}y~}~{wx~~{wywzxtpxzyyx}~uossusu}{vqy{{x{{xsuvx{w~{{vx|ws~~|zwy|wyv|qqy}st}ukv|rwrttssyx{wvsu|x}wyw{~}y{}zzy|~ou{}y}wxz}||y{w~yqxusou~}z{{urz~}z{|{pwx|w~{w{|~stqqnvyxz~x}~}yu{z|{ynxtz|y~~x{y~uttsxwyvtrmwz}u}xx|}~}~zz|u|zw~}||spwws{~x~zx|wptw}ysty|}|wwwsyvlv|z}tx}|}vuuz{zuyyuw~~||z|z~|~{zv~}zumrqppuzyx{{tz~|y{|{trvu}y}~~ysw|{x~{x~yt{}zv|}~~{}|tw{~|yzuzw|wxw~~w|rx{{}xwvrrrnsw|xzxu||}y~{{vu}~xwz}~z}{}vrz~}}zyx}sqpu||z{}xxx|}~|||uwtwux{z|z~vuys|urwwt}{}~~uy~~y{yxyz~~vtlwxsvyw~z}xwumqvrox}~wv}zyw}}y~{xy|z{|y|vsw{~z|{{~}znxtv|u}{ssy{~{~yzom||vx|yw}xy|}}|vvss~zwxzzyz}}|w{|w~uxzsz{rtqpw}}}yy{{|{xvwyuuu~ywx~yutyz|}|t{|~|yu}~yzxwx~|xxyzvuuw|~z{||z}zwwoquqsyz~|v{{vxy{vuz{}||z||~vuwuz}~}wptpyy{s}{{zssw|zxzz~wy~uqz~yy~{|{|{||}~wsvqtx|xtou{x}}|zzxw~}|ty~yssrvywz|z|~wru{x|}~~{}z}y{}|y~yxzwyytxxtq{}xvt~{y}}~~|zvqs|||~ruvux}|}{wwz|z}x~|z}}}~ywxv{yqqzty}zw}}~xnvxt~|{}}}qqy~}|vuwx~{zxyw{|v||pv~wqpzyw||}y~xx{yvvvwyzz~|{~|{xwvvy{~yv}~{}~|xtz~{y~|vxtuvvv~|xwvv~{~|~~z||zxzvuwz|}~~zzxuyzxv|~z|}|~y~|z|~ztspt}}yztpty~~z~{usuy~}|wy~z}|x~wq|ywwz~|}~|}wv}~}{ysu}{zzwtxwv{z}}}xttx|y}~{|yv}{{xuwuy~|{zx|~~~~|xu|~|zxz~|y~{xyut{wrv~~~}}{|~sp{||~}ytqrw~|xv{~x~}y|{|}|wwuw~|vvz{urzxsty}~}|}~wv~vw~||~{||~|wvw{{yz}z{}|{x~|~yt{|yz}}~{v{xpx~{ysvwuz~~{zzz|yuw|zz|||~}|}{}}{|||zy|y}yw|}}{{{}|wux{}~{xuonvz~~|{||z~~}}}ywww}~zzz|{|vv}wuz|x{}~|}|x||{~zwvuz{wzxttwvyzxzz{}{~~|{ztrxz~~}z|z{xtx~{{|}~|{~~~~{t}{~|}~zuvqqxy{{xxzwwy}}}|{}}zx{x{~yzzzx}~~}|xvxty|z|~~|{||yusspsx{zvry{su~~{z}|sw~~~}z{{x}|zywsy}}~w||wtu{~xuqqyzttxvrsx|||}~~~wzxty|yuvtwzz|~~~~~|~|xzy|}~z|wtxxootxz|xxytuvz~{z|~z{{zz}{tuuv{~{{zz{~~~}}|||}~{xustppuwzzwuwtx}{~{xtv}~|z{z~}zxxv}}vv{uu|~~~{w}}y|~zwutvwuwsrrstz||~||||~zz}}|{{yzvrsxy}}~{zz}y~~}zwxzuvy{|ztusrtuw~}xy~|}yx~~z~}}vuuuwz{}|{|~||{~|{zxz|}{|zwwusrplpuzyst{vt{|{~}{~}tw}|~|}xy|zxvsv{|~~y}{yxz}vps{xqtxvrpqyz{|||~}}}~{z~~~~zz~~|yxwu{||z{}wxwx{~z}wtvxtrssrstv}|||z|}}}z}vw{}}}{||xu|z}zxsnonrs{sqrwz}~{|~{{~|zvzzy}oqmrzz{zutujsvvx~}~}|~}ywz~~xqtrnpmrw||~~x}|{~~}ytnpqw||zqvxropzy|y}~{yy}z|zrwy|zxsryxuv~usovtw}}z{~~vwxuuy}t{yrmnyxwsyswz|~zw}~||wzrmutrv~~~yklptqxwx{us}}{}{|~yuwxxv}ruvtsyx|yy~}{z~w|}ypnrnsv}}zsuxz}vx|wtzyu}z{y~|vt}vzz|~{qqpxsvwxuywv{y{}~}zy}|xtyxz|s}yns{yyuy{}z{}}x~y}zx~yvinqtwyzz}uxyzzxusyzz~wx|rxvst{z~xyrxz~syx||}~vzz}~yxw}zxwvtxowx{rvyu{|u~{{~wsz~{}}v||z}uvsvuyy{|x}~w{|~}y}~x|rtymmwzu|vzyyz{yxxtz~|}}ut}txx}}wu}xzlq{}ywxw|||}~|~yxsx{}~~|sqvszrttyyz{~|zy~}xot|zyzy{{|}}}xuywx|y~}}w|vu~{yyy{~vuppqupux|xtv||xyzxywqww~~{y{}zxwx}~yz~{||xv~y~uy~||y{~~|~z~yxr}utqlvx|x}xv~sy{wxtxzzz}|~vxv~}|}||}y~xvoyz~}|}}w~x}}|~}z|y|yrquuuu|~xzyyy{}~|~}usywz}xsqnopvv|}|{~wx{~x|pr~vx{wt|~zvzyxzz}vzu}}zzyytuz{|vytwy}w|~yuwtpryx||~|w|~~}}zqrnlwwuv}|~x~oyzyz|}}y~~|txtvu{w~zz{||zsxv}}}}u{~yu}trljnux}yx|~{~{}rwxxy}y{wzzztu~{wt~yyvz{{~}|s}{t|vz~}y}vpjljtu||{|}|}syxt~ytz}|r~~}wvu|~ws~ytzyzz~|x|zv}|{||z{|x~uuqqnqt~{{}}y{vwwy||x}}zu|{wup}~sz{tyt|||~ww{sw~{}~}t}{ytookpu{z{~~zzv}yy~vzy{twvvquuzw{}vzz|}z{}ww{tx|{z{}w||v~spinqsx~~{}{||w{yy{{x}|u|yvtp|~v|ztyu}}|}uxywx{y{z}~|z}{yxnnmpv~}|}~y{xz}||u|ytwwutzwxwvw|~{}|xtztsv}yz~|}~y~yvmnpsv~{|||{~z|x~{zvzwyvy{uwr~y|~t}qw{xwwwvvx{u}~||~}x~vqlrpt{{}{|}}zy||wu|~yu|{usr}v}vtvv~~~y{r{stsyvw|}}~{~{{xmorst~|{~}~}~}}ys{zuy}vus{|y|w~u||}w}uwvprwys{~}}~z|yqksqsx|~{~}|~|{txx~yxz{squy{zzvu}}}{{wrxussyt{}~}}|yxkrqvzz|}~}{{trx{zx{||spu{~|xyt|z|~}yyr{vspzsw~~|~~|{lqvvz|}}}~}~~yzwp~{y|yz}wss~~}~}wyr{}{zyzq{utrztv~}}~z{oputx~}||}|}z|wp}y|xy~xsq~z}wzs|}}~}}yyoyttoyvt}{}}{~~~prutz}|{~~~}v{st}y|z{z}s|xwvy~zxwz|{x{{~|yx}qvuju|}~xv||tqp{uqzz|xy{yuvsy}xyw~~wrv~yux{|{qs|}~wzxn~xmuttx~{{yvu|~|~~zy|}z|z~}wpyz{||xzutwxr{{{}||uzyx|zz}xtyyvznqvyu}~}usuxzxq|y}ysqyywvt~}xyov~{xy{wz{}zyz{|~~{||kkusv{}wz|w|}vytz|}y{vzzvttx{~yvqzvjt|y}ux{yzv|}~||spvjjrhnx}~{u}~|}w{~y|zw|}yyrtsvpx||zwytw~yt{y}~}xy}ursz~ossnuzxsz{}{~~x{{{{}z|x|y{zv}|z~ox{|{~}r{t{|yw|}}}}wvyhnyx{{}yzzvwtq}}}wvyw|vv|zzztnyvtww|zzsvzry}~zu~~}z}tqrriqkio~{}x{|~xx{vv|{~}}~zo{{xyvvuytux|{t{x{tpxz~~zu|sozppls}}~}||}{|{{z~x~zp}u|xq{}xyyr{|sw}{s||}w}}{zs|||tnlhlosoy|}}zr}uzuywuvovtuvzz}|tz~~wy{}}u{y}|w|||zrugk|~u|xv|zy}|~ywtt|}|}|{xzzu{{y~x~{~yuwxxru{{{~}yztpwtqqipx~{y{~{{{|v}}|~{xqox|vwzz}xu{{{~vx|~{zzwy{|~nr{mmmfgnqqy{{u{}|||xzux}}|ww|~xp}}zzv|~ztrxywx~}zst{suqnos}w}y{zxvvvz}}}z|ytxz}sx||wz|~vvztq}~|z~{ssiuuw~stnspxwvrott{wwwv~zv{}vqunwz{vrwv{|tty|w~~|vwwttsmrt|vwp{~uv}~|x~w~xv~{tyxpmpv}}wyxw{~y~{}~z}~{||}}zzz}wyquu|uv}{{yzyszzxx~|{xwuw}x{y{{|xpn|{ryzpniilu{tsknvqrrpmqqtrz|sz{z}t|y}vrv{ztwyv||}~~}}yv~wxzwtzywzz~|zx|zxyzyrv|~}zxwtuz|tqltyxvvrqsrvtvvvvytou}yvp||xxxzy~z}|~utx{yu|~y~~}}|z~}wv|z{}zz~uvxxpy{}{~wqqsrs}yqplqrsnppmosvuxrvz{|~|v}|zwzwy~{tv}zxu~}~{}}|}zz~wxvwtxxs{y}}yxv~xo{}}{|wyy~ytovwtx{uttutqwqt{yxuv|yyq~~xvx~}ux}}~z|}ystxxw|{y|}}|z}}|x|yxzw~{}wtwy{ux{~||wxqqqxxtsqvusvuutsxvtvuz{|w}|ty~{xz}z{~~utu|{~z{{z{z}}z{w|xvsvyuxxy|~}|~xzyw}|xwvz{wwqyyy|ytrswvuvsv{}zvyzwzztw~|xxxzwyz}{|tmr|zxz||{}~{~yzz|{|{x}}}|yzru|zv~~||z|wsstx~yxuouuvyvrtvxzwyvz|~|x~{}w|zxzz{xy~vrx|}}}xxz|}|z|y~{wwv{z{xw{{z}~|tyyx}~{xzy{|yuzzyzxuvsuuuxu{|ut|y{|yw}|xxv|}uuy||zzppw~|}~y{z~}{||zzzy|x~~|}~}z|vuz~ws{}}}|{ytwx{}|zuvzz{zvvuvxxzw|ytz}zy|zy|{wz{upv|~}yzx{~~~xww}zyvuxyzvz}}}|{{w|{v{~~}{~}{ww}~|yzxxyzzw|~~wpv}y|~xv|~zwvv~zuxw|~xwqms}}xy{}~}|~}{z|zyz{{wy}~|zwru|{st}}|zxx{}~|wu{||{xwvwxzzxw}~~|tu|~z|~yxx{{z{{xqr|~z~}|}|}|zxy|xwwyxwu|}y|~xw}yt{~~~~xy|~{{yxwyzzw{~}xqx|v{~ywy|ytuv{xxx{~}yxrmt}{y|}|~|yz||{zz}|~}{ysrwytq{~}{z{}~zvx{}}{zyxwyzzx|~|vt~|{z{|wy{|||}}|vqu||z}}}zx||ywwwxxx}}x}~~|{vvz}yv}~y{~~}{zz{{{y|~{vr{y}zxz|~zuuxyxwz|}|}}{ywsos{}z}|}}}zz{{{{~|}{{{}|zyurvzxuw~}~}}}~{y}~}zzyyy{{{{{wx}zz|zwy}~{xy|~~~|zxtsz||}zyx||zxxyyx{}}y}~|{|}}|{xzzx~{}~}||z{{{z}zuu}x}~zxx{~{wsuz{xuvxz|{|{xutppv~|{~~{~~|{||}|~}}{yyz~~~~|yxvtv{{yw~|}~}y|}|zzy{{{||vw|}zz}}zwy~~{xy{|}}~~|yxssz}}~|{y}~{xxyxy}|~y}|~~~|zz{{}{{ywz{z~{}~}|{z{||}~yuy|zzxy}|yvuw}~ywuvxyzz{zwutqt|||~||||}}}|~~}~}zxwxz|}~}}}{xwttx{xuz~|}~}{{{{||}}ww||{}~{wx}{xy{{}}}|{xvssz}}~|yx|}zyyy{~||x~}}|~~|yyy{~~~|{zxx|~{x{~}|}|||wv{|zxy}~zxvwz|xuuxyzyzywusqrx{{}}}}}|}}~{~~||~~{xwwx{|}}||{yxvtw{zvv}~}}|}}~zw}}}zz}~|zy||zxyz||{|zxvutw~~z~}{|z{~}zyz{}}}yy~|}~}~|zxwx{}~~~|zywx||yv{~~~~|ww{|zxx||ywuvz|yvvwxyyyxwusrsy|z|{}|~~~~{|~~}|}~{xvvvyz||||{ywutvyyvt|~~}|}}|x{}|y{|zxz}{yxzz{{{zxwutv|}|}|~{{|~}{{|}|~~yy~||~~}||}~{ywvwz{}~}}|zyxwy|}zw}~|xy|}{xx{~~|zxwx{}yvvvwxxxxvussuz}z}{~}~~|z~}~}|{{}~}{xuttvyz|{{{yxvuvy{yvx~{y~~{z|}zyx{}zxyyzzzyxwvuuy|}}}}zz}~||}~}{|x{}||~~}|{{|~~|yvutvyz|}}|{yxwx{~}zz{y||~zxx{~~}zxwvx||xvvvvwwwvutstw~|}~|}}}z{||~~}|{{|}~|yvtstvwyzzzyxwvvy||yw}z~~}zz}}zyy{~}zxxxxyyyywvvvz}~~}~{{}}|yxyz|}yy}{x{~||}}}}|{zxxxyz}}ysoszyvxz|}}}{z{~~zy~~~~~zv}ww}}{|~{yxx{~|{{{|}}|{ywwx|~z}}~}|}~}}~~{|{y~}~|tmlt~zurqqqqqruyzwuz|wv||yxz}~}{xwwy}|zz{{|}}}|{{|}{vsrtx|}{zzz{}~zz~{wz}{|}~~~}|{zzzzyvrpprw|~~|zywwx|}{xz}ytsxyutuvvvutsqpprvz}}|yvtttuvvvvuutvy~||}z{~|~~~}|zxuqmmotz}yussuvsopw}|xy|}yvw~|{|~~~}{yyz}~}}~~~}}}{z{}~|zyyyz|~}ywy|{vuz~{z{|}~~}|{zyyyzzxurpprvz~|zxxz}}zx||xtsw{yyyxwvusqpqsvz|}|yvttttuvvvuuuvz|{}yz~~~}|{zyvrnmmqv{}zwvxyvrpv|}zxz~~~~~{ww{~||}~~}|zzz|~~~~~~|{~~}~}|~~}{zz{|~~{wvy|yuu{}zz{|}}}||zyxxyyywurppswz~}{yz|~~zx~~}{wtty|zzzyxwusrqqsvz}}|zwuttuvvvvvuuw{|{}yx|~~~}{yxwvurpnorv{~|yxyzzvru|}zz}~~~}|ywz}||}~~~}|{zz{~~~~~|xx{}~~~~}}}|{{|}}zww{|yuv||zzz{|||{zyxwwwxxvtsrsvz}~|{z|~~z{~|{zywux|zzzyxwutrrsux|~~|zwvuuuvvwvvvwy~{|{wx{~}~~}{ywvuutsrqqsw{~~{zz|}ywy~|yyz{}~~~~}|ywx~~}{y|zxz}~}|{zyxy{}}{vvx{|}}~xwz|~}zz}}zuv|}{z{|~~~~}|{{{{zxvtssuxz}~~}|{{|}~}}|zxx{~xsrw~wolloswyzywutssttttttuvz~|~{xy}}{{}|}|{zzz{zyxxy||xvvvx{xuvxyyxxxyz|~}|{{}~~~~}{yy~{xwwy{~~~~~~~~}|~ywxz~}{wroostplnsxxvtstuvwwwvuuuuvwwwusssuwz|}~}{ywwx{|{{~~|xuttvx{~}zwsqonnnooooopsx|}zvvz|zutw{~|zz{~}}~~~~~|zyz{~}zxwxxvvz}{zz{|}~~}{yxxy{}~~~~}}}}}~}{xwy}yxwxxz|}~~~~~~~~}~}zzz}zustvtompvxwussstuuuuuttuvwxxwusrstvy{|||zyxxz|{xy~~~ywuuuwy|~{wtrpppoooopqtx||zvvz|yutvz~|zz{~}|~~~}{zyz|~}{yyyywvx}}}}~~~}|zxxxz{}~~~}}}}~~}|ywx}~{xwwxyz|}~}}}~~}}~~|~}zyz|~{wvxxuposxyxussttuuuuuuuvwxyxwutstuwyz{{zyxxy{|{yz~~|xvuuwy{}~zvtrrqqqppqruy|}zvvz|zvtvz}}{yyz}}{~~}}}~~|{zz{}~~}{yxxy{zyz}{zxyz{|}~~~~~~~~~|zxx{{xwwwyz|~~}}}}}}}~||~|xwwz}{xxzyuqqvz{xvtttuuuuuuuuvwyyyxvuuuwxyzzzyxwxz}~|{~~}|xuuvxz|~{wutssrrrrstx{~}zwx{}zvuw{~~{xwx{~zz}}{{{||}}~}~~~}~~}{yxxxy}~}|{zyvtrppqsuwyzz{{|||{zxutuz|yz~}}{xuvwx}~~~}}~}}{xwvvx||~}xsrvz{yvutuvwwxyyzz|}~~}{zzz|~|xw{~{|~~~{z}{vrpqv}}zxwuuttstux{{z|}zy|}xsqsx}zxxz}|xux}}|||}}}}}}~~~|||}~~~}{ywvvxz|}~}{yyyz|~{upmnqw~}zxvuuuuuuuvx{~z{~~ywy|~zvsqrty~~|xw{~|{||}~~~~|yvtsuwxy|~}}}}~~}|zyxxyz|}~~~|zxy}~}~{vsppruy}}{zyxxxxyz}}~~|}}xtqpqty}zz||yttw|~~|zyyyyzzzz{{}~~|{zzz{{{{zywvwxyyxy~~|{{{}~~~~xsonnpty~|ywvuttttuvy|~zy{~~zwwy||yvsrsvy~|y{~}||}}~~~}{ywvvvvuux~~~~}|{yxwwxz{|}}}~~~~}|zxwx}|{}~{wsqqqsvz~}|{zzyzz|~~|}|xurqrtx}}|~~zvtw{~~|zxxxxyyyyzz|}~~~}|zyyyyzzzyxwwwxyywwz~}||}~~~zuqonoruz|ywvuuutuvx{~~{xy|~|xvwz}|yvtsstw{|{}~}}}zywvutsrponoprssty~~~~|xtt{{z}{yy|~ytstvuspmlmoqux|}yxz}|yusstvz}wsu|}{zyz{{||||}}~~|zyxwwwwwwvuuuvxyxwz~{yxxxy{}~~}}~}yuqnnorvz~}yxvvvvwx{~~{z|}zy{~}zwvvx|}xttx|~}{zyzz{||}}|{zywusrrstuustx{urpprtvy{|}~~~~}{yz}||}~|yurqqruwy|~~{{}~}zz|~|yuqonorvz{zwnqx}}}||||||||}~~}|{{zzzyxwvvwxzzxx{}{{}{z{|{xusstwz~}xyysonnptx{}{xvx{}zwuux{~}{xusqqruy}~}~~}}|{zzzz||{{vsqppqstuvwxxyyyxvtstw{~}yvvxyxvsrrsuwz}~~{yxy{~~ywx}|xussrrrsstvwxyyyxwuttttssssssuwyzyxy}{zz|~~||~~|zz{||yursvxvsppsvy{||||{{yxvsqppqsw{}~~~}}}~~}~}zxvvvvwwwwwwwwvtrqsv{|zvtuwxxusrqrsuxz|~~~{yyz|~~zxz~|ywvuttttuvwxyyxwvuttsssssstuwxyxvux~}{zz{|~~|{|~zvtvxxurpqtwyz{{|{{zywusqpprux{~}~~~~~~}~~~~|{zyyyyxxxxwvusqqsw{|yutuxyxvtrrrstvxz}~}~|zyz|~~{y{~}{ywvvvvvwxyyyxwvuttttttttuvwyyxvuw|}~~|zzz{|}}||}zwwyzxurqsuxz{{{{{zyxvtrqqrtwz}~}~~~~~~~|}~~~||{{zzzyyyxwusqqtx||yutvxyxvussstuwy{}~}~}{zyz{~|zy|}{yxxwwxxyyzzyxwvutttttttuvxzzzxvw{}}~~~}|{z{||}~~~yxy{{xtrrtwyz{{|{{zywusrqrsvy|}}~~~}}}}}~}|~~|||}~~}||{{{zzzyxwusrsvz|{xutvyzywvuuuvwyz|~}}~~|zyyz||zy{}{zyyxxyyzzzzyxwvuutttuuuwxz{|zxwy~~}}~}|}~}~}{{{{||}}~|yy{|zwtstvxz{|||{zyxvtsrstvy|}|~~}}}}}}~|{}|zz{|~~}}}|||{{zywusrsw{}|xuuwyzzxwvvvwxyz{}}|~}{zyyz|}{yy|}|{zzz{{{{{{zxwvvuuuuvvwxz||{yww{~||}~}||~|}}|{zzz{{|}~|{}~}yvtuvxz{||||{zxwutsstvx{}~||}~~}}}}}}~~{y{~{zz{|}~}}|{ywutsuy|~|xuvxz{zxwvvvvwxy{|~~||~~|zyyyz|~~|yyz}~~}}}}}}}||zyxwwvvvvwwyz{||ywuw||{|}~}|{}}|~~|{zyyyzz{|}~}~~~zwvwyz|}}}}|{zxvutstuwy{}~}{|~~~}}}}}~{yy||zyz{|}~~|zxvuux{~~{xvwy{{zywvvvvwxyz|~~||~}|zyxyz|}}{yxy|~}|zyxxwwwwxxy{|}|zwuw{|{{|}~}|{|}|}}{zyyyyyz{|}~}}~}zxxy{|}}~}}|{yxvuttuvx{|~~}{{|~~}}}}}}~}zxx|~zxxyz{|~~}{ywvvy}|xwxz||{yxwvvwwxyz|~~||}}{yxxxy{|~~}{yxxy|~}|{zyxxxxyyz{}}}{xvwz|zz{|}~}|{{}~{{}}|zyxxxxxyz{}~~~}zxyz|}~~~~}|{ywvuuuvxz|}~}{{{~~}}|||}}~~{xvx|{xwwxz{}~}{ywvx{~{xwy{}}|zyxxwxxyz{}~}||}}{zxwwxyz|}~~}|{yxxy{~}|{zzyyyyz{|}~~}zxvx|~{zyz|}~|zz||z{}~|{yxxwwwxyz{|~|yyz|}~~~~~}{zywvuuvxz{}~~|{z{}~~}}||||}~}zwvw{~~{xvvwy{}}{yxwy||yxy|}}|{zyxxxxyz{|~}||}~~|zywwwwyz{|}}~}}|{zxwwy|~}|{{zzzz{|}~~|ywwz~|zyyz|}~|{z{}}zz|~~}{zyxwwwwxyz{}~~{z{|}~~~|{yxwvvvxy{}~~}|{zz}~}}|||}}~}zwuwz~~zwvvwxz|~~|zyy{~|yyz|~~}|zyyxxxyz{|~}||}~}{zxwwwxyz{|}}}||{zxwvwy|~~~}||||||}~}{xvx|~{yyyz|}~}{zz|~{z{}~|{yxxwwwwxy{|}~~~~}}}~~||}~~}{yxwwwxyz|}~~}|{zz{}~}~~}}}}}}~~{xuux|~~|yvuuvxy{|~}{z{~|zz|~~}|{zyyyyyz{}~}||}~|{yxwwwwyz{{||||{zywvvwy|~~}}~~}}}}}~~~{xwx{~{yxyz{|}~}|zz||zz|~~}{zyxwwwwxyz{|}~~}}}}}~}~~|{yxwwxyz{}}~~~}|{zyz|}|~~~}}}}}}~~}yvuuy|~}{xuuuvxy{|~}|{}|z{}~~|{zzyyyz{|}~}||}~~}|zywwvvwx{}}|{{~}{zxy}~|{}}zz}~|{{{||}}}}}}}||||||}~~|zxwxz}~~|ywx||wv~{optwy{}~~}|zxuuy~}}~~~}}{{{{}~|zz||wuz~wrqsuusrqrsuvxy{}~~}}~}~~~}{z|~|zzz{}~~~~~~xsompv~zwsrpoonnoqtwyzyyy{~~}|{{|~~~~{yxyz{|}~~zxvuttvz~~{xvuttttuuuvvwxyz|~~|zxvuuvx|~}|{zz{}|zxuttux{}~{wtqpppqstvxy{|}~~~~~{yyyz{|}~~~}yvspnoqty~}zxusrqqrtvxxwwxz~~}|{{z{|}|{z{{|}~|xvttttux{~}{zxxwwwwvvwwxy{|~~}{ywvuvxz}~}|{{|}|zxvuttvxz|~{xusrrstuvwxy{|}}~~~|zzzz{{|}~~|yvsqoopsw{~{yvutttvwwwvvwz}~~}|{{{{||}~}||||}~~{ywvutttvx{~~|{zyxxwwwwxyz|}~}{zxwvwxz}~}||}~~~~|zywutttuwy{~~{xwvvvvvwwxyz{||}~~~|{{{{{{{|}~|ywtrpppruy}}{yxwwxxxxwwxz|~}}||{{{{|}~~}}}~~|ywvutttuwy|~|{zzyyyyyz{}~~}|{yxwwxy{~~~~~~|{ywutttuvxz|}{zyxxxxxyyz{{|}}~~~|{{{{{{{|~}{ywusqppqtwz~~|{zyyzzzyxxyz}~~}||{{{||}~~~~~}zwuttttuvxz}~}|{{zzz{{|}~~}|{yxxxxz|~~~}|zxwutttuvxy{~}{zzyyzzz{{||}}~~~|{zzz{{|}~}{zxvtsrqqsux{~~}{{{{{{{{zz{|~~}||{{{{|}~~~}{yvtssstuwy|~~}}||||||}~~}}|zzyxyy{}}|zywvutttuwxz|~}||{{{{{||}}}~~~}|{zzz{|}~~}|{zxvusrrrrtvy|~}}|}}}||{{|}~~}}|{{{{||}~~~~}{ywutsstuvxz|~~}}}}}~~~}|{zyyyyz|~~}|zywvutttuvwy{}~}}}}}}}}}~~~~~|{{{{{||}~~~}|{zxwutsrrrsuwz}~~}}||}~~~}||{{{{||}~~~}|{ywutttttuwx{}~~~~~~}|{{zyyyz{}~~}{zyxwvutttuvwy{~~~~~~~~~~}||{{{{{||}~}}|{zxwvussrssuvy{~~~~~~~}}||{{{{||}~~~}|{ywvuttttuvwy{~~~}}|{zzzzz{|}~}|{zyxwvuuttuuwxz|~}|{{{{{||}~~~}|{{yxwvutsssstvxz|~}}||{{{{||}~~~}}||{zxwuutttuvwxz|~~~~}||{zzzz{{}~~}|{zyxwvuuttuuvxy{}~}|{{{{{|}}~~}|{{zyxwvuutssstuwx{}~~}|||{{{||}~~~}||{{zyywvuttttuvxy{}~~~}}|{{zzzz{|}~~}}|{zyxwvvuutuuvwyz|~~~}|{{{{{|}~~}|{{zyxwwvutttttuvxy{~~~}}||{{{{||}~~~}}|{{zzyyxwvutttuvwxz|~~~}}||{{{zz{||~~~}|{{zyxwwvuuuuuvvxy{|~~~}||{{{{||}~~~}|{{zyxxwvuuttttuvwxz|~~~}|||{{{||}}~~~~}||{{zzyyxwvuuttuuvwyz|~~~~}}||{{{{{{|}~~~}}|{{zyxwwvvuuuuuvwxz{}~~}||{{{{||}}~~~}||{zzyyxwvvuutttuuvxy{}~~}}|||{{{||}}~~}~|}zx}khtjcgejjqxb\e|~~lsPPOKS\`ybqkwxvcntcUxdVr{yuavfj|{Wh{^dkZbxeX`oXZ[\`Tvh\||_M8cJ1VkV[mzrdz~\x|ypz||yq~dQbjW^rmV]Ux~hsu{cmjozjqqjnplZag{zloq~vzzgkX_|}zszϿųdZ\1LħhquaR1]HN;7HJUa_RqlWbuxwq_a]~tcVhlcyeu~zthSgffw_cakruo||xr_Pgjkj}mbixlo{cizvrtu{yo{w~pm|DE7=NUrybcR^Z4<-bqёnvogzgSYXm_oajysjhb]wm\M<`e1)WXUhzklw]q{}sv|bplvZdMtMHnlN{_Z|o~fzzouhljlpZx|pqweqw_uZBN]FW_kyz`r]qS>le\iltSi{IPZAUYcWz}~nyzgj^yxYHwg{plozwly_Q\QV\sblxumtrl}|{zmxwsswĥ}oiNmF4J0EOE~zlձigyrc]r[my^]lWbydVpbWUI_NEOdpdrvaYtps~xq}voipL\sNIPWCIvny{ppw|~jp|wox}mkxczmv`kYxxAG[YTBCYTjȵʨh|m]Pe_Olwp}Ta}f]nHaRLKYyrtpvwzwgTogojemfnrhkqX\jd]\Ljpr[y|uinyy}dx`jXqyu•|tt|rqlnnk=DWC\_E``°z}x|nr|dxfotbT]h]gNA7@egGLs}Xull}mippcrcUaV~g5C[gHt}vzvlsgsyyX`voksupZruRpqP;HRBLSn^k¸zvig\dPr`^wnWt~obuNLqioqy{zvhlxiu[x{mnlu|xakUPj`JFRFsj}˜zplpnhz}uvq~y{h{}qx}zffiijoHMJT?GK6onrusǨɚ|o}zpwwh|{`jyioTWc^@6=hv^_~vthhz|~jy}ztpgrbmV\XV=P{`ILbhwʺ||w|u|qhiy{Snvlx{adĩrz|pe_Q_VeyWbkL1MifSPfùgbrbevmmfT{g`GgmSn_tx{rq}k`[vxYei}pqgf|rYZYHYYIaqj|mz~t}vrwr{q_~|zyj{qs}yqvZ[`USGE/LRA]ae}ş°r|quejh{tkksaU^XXN_pu[{trt~mzt~stlujjtJ`aYU@K`DO^qbnòy|~|}ouh}wxpgr}XwjwvpM[us_c`L9a^?5Jl`dUn~srkxyzmy[dywTQe]qq]^l|ujadzdzuisWcndYzck]FCdckusy{ytdl}mffqik]zpx|zrmudldfotYXLSc_F5Q7dl^clɫïq`xj}tWYZ]diQFZxecr|lyvsn{wxo|rWihdxTV=GkW>KovhuŹzyj{sflr_}dj}uszu}t~whi_tydX\mH4IPB?NUK\b{zwt{fmnyqpmgy\qkton{s{xwssu}btze[wmoh]dMTdLN[Vm}ou~l|r{_vjdaory~u}ti}|fltf|rYLVQYRQLUVJTRs{t{|ymj]Qeyut[qx{xi~q[qsgdrf_Kcvc\@U^_k_Xvy~ybxl_{}~xeZgxYwx|tzzvnYboifNxjgYF>RNDJKYWgQ\_zʳƶyu~r|k|fhZXqth[|upyjppntheigzrXI_;]dLF?d}ortlurwu_ezsb{{ygltzvqxpY[aZoUbI@e^HF\VJ2FLSPvv~yp[omcjnpra|w~~zv}n|~{ynUTj`WYbQaKQoPdqrq|tkwwovys{ktsxish]e\|ynnktdVihjjUfX^aEKG>F`PRV]bqjtƭnnylxrsnkUi~or~{j~ix~|idtkirifk`WJ8Y^^M?pzz|yreg|__zj{esmvhz}zp~z~l}c^dCcpbS7HbcMMVG7IN@Egxdo{Ud|~jf`|wdqzrxqmXX`QHo[LJMrfadiv|{w`hPMMd}sfhv\UQWWeKfnfhobRL\Y^mng~^klggcaGZ]GAmiXb_vw{{{sPa_Txv|yozltwvypox{~vx~fT&]ZWage|t~^n{xzlfflZfxu|xv=G`TXM`b^QqbjuyrqrrZmfdLC{vx]M>L^RJmts{zm}krmcmrc`{zslSIrB1A?<6,cQYpd^oy|y~}anj{xgyjqv[I\TKgp}toy{}poVqtjmu~|]O9Zdn໪qde\g`}wrcryh]^gJ|smVSOsxnq}smRPeh{tv~bot~ukmlvmd¬~QVJP\mt^R_dOVjH_RP[VbskZ]Ujk{}zpPfaem\WgP\fe\ZhdLCRILj{}rhWYih¸mj^Yc_jK_oncjtaT7BM?Fzngqbwgcnsqty{~qgo[jimertgcqst{~zxml{^f\A/30GQdtgSLdNK\cJbae}uwpy{vp{znz|mk}ZTS[dl{um{zx`PWdyxvkY`pKcuV`a_QG3IcYcroxx}hbfasch|}qnvq]MIfbtscPjrgdWyywvws}kjkxbTC`үd[gm]_art~qmq{`jc]{ttrg~sh\rp}jjkfRO^\HLBr{}uuokghUABSr{ta^?BU=;\NHH?^SECKW[gpwkwzoagj||uVbi{mty}ڽ}th`RM2Iqpotfnr|xt}g[ia]^^Wrgq{toTntgkju[]esd]_QV[_pzq~}~jssloyn}}xxzqopVjlz׾ѳ{ubepltotyrylUSHd\e|v|o~v~s|unc\FYlv}rgug\vktiULM3.C`fzybxfgz`dfVSpeeXJZrmmsk^`]HGXZZXP{gjudZ_YMaLTpyyIJ~|tv~ihZitn~~skxcg_idTc_PA>Ek\yor|keoxisxw`y|j`[ctnqhqnvtuvttsrtpmUL@QN4<Rw_JUZVTmpjqpr||snO??Jw~nr{{s~wfep}^fn^fuwqi=9Car\kT[sxwveqfcfkvlvy]tlr|to}lT=ORnqv{}{m^Tl~y~~xzbF8@dt}uZPlt`h`iwxrubr^\RUl{s|}y}vUsmKIAY||yir|u}r[LUE1?Akyvyjpg\VWUmgvo]w{qlzxjzr~q~vf^]G9;Mh}t{~y|}odRF*/Nhw{{wqobUvf[iuZR\^m\rfjsnnvvizbeWM>Bxwvmxriot}~sctQRO>EZt||pltqZrtrnfj^P{bYcp{zv}loh_ogJZQ5Sg~|~t{qkkM@<.)N~qube\SXd^ddhtgskciqqzauwwz}}zcL?B69Uzy|{}{vr~{ytqkIG6=RT|sxwvehbM]dNOc\hayzqkxyqedc{mcwXK_\Ucjvltrqumif]be/?VLWnebf}helflr{gVkYTgsqwzx|tatdcca@0DXH_}~{yztwcbTB).6AQ|vnojgb[WNd{c[ajp{~id|ug{rl}|tlepSBTDFOau~{vs{}~ytpypp]fpRFRAQ^tyrpiWof]q\LURX|}wddi`_gfifPqeLTQQcn~gvxpz{uqrhUKOU@9KI[ƹwovqzea|uoivr\Ogo^q{qzt|hndPRUO:@ISEd}s|yxbMo^68A:=N}yhb^Y\`ZptNgvpkr{o|s^[vzfqmnspqxLCO:AJ`~sm~zqtzvu{bkqllP:rsYGhZQWZlwuzwlpbiSLwwzisswuprso>dejo{i~tfU\~r]Ydp~}csyv}pysjx|WG|R\ryyu|twxux|n~~~^-?omqu{{lvzwztbkmxigxzq'-[jiU[yvfaxuj[xntyvkgu]_olyuottvtj<2x}tq}roxvvxa_{scDDkxj`_UDb`GclnjjmrZ_yiQLdpvwwuzjmgegeZdV?Njsyomzn}zr_LC1$WtvfSWP>BNQWaM^kdkdM[kj~|w{xwfhNJI-9c}~|z|yw|wyk]BEJ2VgsiaTV[GJJGHP[[@P~osvddg~xgrhacl^KQwvrw|{cj~}xrtQF\XCHthO^yZN^\`gjhaLZ\=;Rhw}y{`|~ijg^OBFdI=g}nm}uy`{}fPC38?1PcprbTPNIFSeQSV[h]]z~{}z~xtx}~wrq\K]OKDB`{xwyuy}zuqjYNHCFNypxta_PNhVMPKACJM]genyfbjktkUgeWRZUWizplwsj~yhYLSN1ESOrǻwf[gv]Iekf_\ZW>HlNLn{ww}mlt]UOFA=?\RJt~}ps}z^`eC52>CBcmeuaOHJOQR^IBYbiZbwyibmsruwri^CDHHIQww}{rklnnwhZI>@JFfvloicT]QBA89LVVkqttv|la\X_jQLLEWnk{}zqwt|tkeYQKAD6.\kuŰz^]iofS`a^bc\UIZaJ`uy}|}{r[guYPLB42Ggdrtsxz|y~}mRQB)(5JSnŹxY[VWSJEM^HCZecnyyvvoe}djyukaZU>1X`R`||usoppnw|m|z_`YOKIL_Wa|xhgcLXdKECENKIvxqsslZW\[[`EBSTacb{t|tuzn{gc[J=/PdzuhN@MTjoU^`YSOPUSeyspNM}jWQn|qqjt{hc_XTS\ĵaKoLJWrvnlk^Qli[lxcMOϮxyed^XMA[rWdsdb{u<(cjH9>HMQJ[t]Xrzrz\AMxs_fzvlgrzrǫ~Yg|]MJLE>OG;;?H:+UyyzcVnc\J4+Rwn|m~sac׼kfq[SWGE?82+.Pbelxl^YGDsysmlrdbjt~yyreS=0(6wƶxhg[ceRLMNbupswo^Xlwf`i[agVXsŹzfZE+KtmnoZTL<1>N\W_yvmdbb_WsvXetrf_lIJ|zytont³tnoS0%;moSRu}vzxtqmwoxlkk\PjdMMRB"&Vqgfa_^^]p~oV95micwkRM\inrtgawdTMA/ %Wtzumifzqj[H=GvviTVWD=919Ij˻xo`KF>742>X^rxku~}ondR;:ETx}|{rsxfZL;5Ed´~~cS_UA:;N`Vdnfdloc\m~pdfqitg^ggWUmmstPE9) +W|mh^QFVs|vusbh|[alhcnynzk^XPMN]bQTI6?o~]Rlja\URT]yiqwhgfTUlZGDGH>G¹~riinyqdWJDNreOKYWQ@>Tg{yvju^LD:01GywfWdx~~vqtfc_VPYzɿ~jjmXME?I^nsuu]_V?1:WpV^bGC@+(J~jgvhJ@947AYrisvxti`NB^sy¯lRaY8*!"D½zXQYULQk|gitohivx\Qz~g^XRTiulxn__hokyscyS?86@]|kbYTPXpqtme_^YFMoiPDO^oξ~od[UV_uru~^B/1BPhrgolic^bm{ukYhoUHDGUf{|zt_SWd{~{|ma[W[l}jZa\UT@9Oj}pgegr_E?:6;Q{{jcyzntzZOVUT_~Ǽy_]aK=4/5Klvwz_XaUEKi}v}vrv{vfP^\A=D=3Mǿ|q^OGEITn~p~}zujaYK@Tymqher~l^kU=3,(1TiODNTOWrpgx{tmp|xW`}qgc^[dxmffowrzZenX?.%%6^}tlhdauyqnhUQlk\HOd¼vgdjqfku]TMECPmtlse`bRCNf~ncwfQLF>=Nvxi]f~}tndXnqJ@KOMUr˻ôz}xaUKEFQm}fpxj|uQKWQ>=X}w}~zdZm_GEKD2=qǹvaND@@F\a]zzo|u|rngXJUwl^frydagM=6/',H~ƺ|dTZfacrlryxzagtkgd^W[r{nwsbZX`lkplsrfZF709Y}ļypgc`\cpxzrnhVQjh`ZFIbzpowna^j|}hWpiZUOJNc̾}~kgjaPRn}cSs}YD=716Mq|wlm|tbVptM7?dȸjXQRYmvk{zotmnze^ZSLOmyv}jZbsyxodiU=6304Iuƾs^LN`kurft{rkkmedj]W]_]au|vf^Zarf]bMEI@37Oyyoihlu|z}tphTUi]TYQ@Ilyv^TOQXgln}kXQI?IttZZago~w}sTTdRA=<>Mrydfppmx}nIRj\NKLP_~qst`UV^`\ouXe~mTHDFSpxplkk}umeTCVfS;1=QkzdWOMR_zsevvZE;DXg{}xxuw}jSEUP815BXnºupjYOTbzpwug_e}va[[]dsxuztp_Yk}jYNWaE-),2B`zk\Xlwxqng^msSGQZ_muw{i\UT^t~p[a`@8HNJVu{qiglwutqhbRDVZGFRUN\|zdZZborsueZkfPIIGH^jUWest|~dXeXD@@COm|aSZkpv}q}|roslNH[UNOT[lȷl^ZamounT[ibVIBDTswpoqt~rj`LAV[OC58Mmu^RKLSb}qpxaSILdrlnnr{t[BHTA105B\{Ǽqqpc\aswvs[Vr{eZXXYe{ypzwlmi\[pjRYtcB767?U{ȽqcX\ww|toeYj}_EITYbz~n`XUZhv~kYdb@0;FBF_~vtw}zo\UhcOMUXORv{ydWV[fz{stpn`csdQLJDARw^ZfruvvcbeP@<;9B_p`ct|yz}wwlWWXMKLLN[yͼqd`eqtw|{n_if`]QDANkunllnvx}zofYCEXSJF70@`Ǵre_]ammwp]RE@Rqphknrx|^FRWB523:Opʿtnrla_lvlr}]Tr|ja_]\f{~spqj^dlNKkhI7214EeǼ}rg`j|ukZb~rQCHPXip`VQRZmv~wchs\@9DOR^x|wvzyucR]hVHJRVU]{yeYW]kypqre]VfnYLIGDFX||e[fwwsoxqVIFDDNjm[Vexqtunj`Y_S@BILP`ǰtg`bo~tmaj_OTUJBMi~wqqu{vvl_JEXTHKJ=GYħePIFQJC2:F+&:dth]][W_`tTU>0IRXPTfyZP~xhYNQWqqi}`Semimvp||tdɗP`sb~nauxrfWkpYXstxYSy{lo}YiYMfUHEZwp}fcMmvkfg~ZE,bvTPs|Uqpxl`jwl]vhYcY_<34m||ca~eUni`Sp{nbm^eu|nox~SF@yqlF7AIE^Xm|}mM[c^Te|{siO_dVFY[tRXVp|Ƕnna]TYkVy_fX_^dñti=017CCHTNos{drp~msTW}kkq\wtvtzjVN(SUBht˾tlxezhw~uwʹ˶t[[Ch|^d<36%PK?U<0VQcjnipylUYht|o[hgFDcomwd{wu`ppx}|`oU]|iqNYb`Z8myekL=LY]XOUCw}}|xzv]e_`}msR_~elesk^`[Sb}zlN[SS16z|ÿɫhPtg]?Ui[Z`Yb_KW9UmVptOgkcm]swio}wh^kkghwĸb`ecfJWSeulɱ~]`xiQ.MMPmcyhVvaek`gLHihjNQpf{jhI>rrxcrqx}qW`O{uog^iqjzҽt{ze[tUUfLfwV`yT[gclzI^xkInpggquzqkOpkOifSxlgfQa[Wwx|uzżnV|hkzcpiq}wIIODOmʗƽkwV=2?GNHV^Psh^tepzolzWTlapup|igE;jbmYkl~t|}opcODSUXUpmf][fNOmed85_ECXdhrw^okTMVmx{soJ_hK^iV~hzcâzkfoi|{\UTZn^i˟yjnr|TXOTL:5;OW_ctc{r}}fsk}{jqX3ehMY{tq^{ffeswsxtet{ca>@KZ=Uֺпs^ZC_bMj~it[FWUhf\pKrp^fufs{xfbMcMHJqxajhtx}zeǬrqgusO4.%MONvcRnZgtTWSXmzi^xpvdaYf_WHMB^xhyrljRdgìv{}ns[rpsxstvt_dNH;B{v~}f^swgakyhSDKBgte~yg~xjqgllXbs}i/N[fxÛ]vs_khy”¹pyxp__`\f<3szĈ~m\PUmtE[n{fa_X]bzp{gpy~|xT{wKJZH%^^hŸt}|pzwovly~it|^NLC<\_hӦkavuSVhfqhkRntvt|nltmahhnP`S=X:|mZkqg{n`dyhpkKhIZNEZXiƽu{mry][uLMNk^r}ybcb]dmcgvnYZVJ[sZi{zsu{~}tobnMvRO]P8^hdFxqowo^Ef^^ZGldkv{xW|ycat{{_GV[Wejayqn^irixxoqkc}RN)5b;4J{~ookogYej`n{q}k|dfWgzZsVosATXUbl`pyjtmUrvtsmpeevvmqV8EM?ZURoiͼrypi`PZldgyzwmiWUH}fMQ}weneWBormbcf|szwi~oijidgXB@LMZoiww|rzppnZcyjm{}u{zvhinY`z_kZXf=6IkZvyp{gyu~abxc`XADIAF\oþusbjf}Vlys[kx{snfbql_nrbqiCIV?QOy|ǽ{ljntgSIapWv~l;@[S`Qmòvom]BepQLokfvtvsfieWYhiejYN`aSm~~voqtso|weunPLAopH5DD3^wzj|x~ec\Tlk]ggriv~XqtlYpogeR;CYbmwohkt\~|qms_WdWSa?'%2gbZzıкqy}jbf`m~swsbdw|R]xa\n~]^|k`KTig~pjnb[hs{}ZZSn]DTm]^E9L`KYs~ZZovib|x|}t|s~wPPWq}C_~ID]_KVaf^^w_|cT_]DTL[E('3Xesʳoqo~glo|mr{slwvxrwSRXS\ljfNXPPZh{žy|qkmx~~m{ipv`hPyvdW?9?8Vxbħuilz[yk?^juo|opjhW^^V}mkbQP;SiSooŸt}lzisbyolhWueFX\[UMkG:UWinq}qcHQzZnmy]onrYY]]m[@XGRfZv~ryvycptv|txS\_FE6AbbL=ObCs̬|`uqzmnxutvd|n}~vgpfjnpoLSW@L_YgmnipYxufiqZONBi\A@-=GXzyЫįr}wsuptq{|~~kweZafia`rs]cDWjikmy|wfvyotgyrSWngtYT_ULL1ROGmv|пtr^`n|jm|urnzomNYiY`ZWIM_ldvww|ufZ|gghnkedrhXX862FeYWtmȥp}uk{nybu|zzx{plX]v`WfBA[HQGdux|ͥ|~zqdlvtspcVx`VHOoiTWGH>7s{~ʽqpkl\}ieypn{krxqcIvZ?WR`kdo[|}wsThcf`upNct]`[kK>C90;jnpt}xo|xvnhzvag_eVqtrfs`@OSaZuqt}vsy]w~qx||\XU`NSfL98WVcZvxd|nbztayg}{skJXRXLZeRG?V@I`ti޵~ntxxxmgujN^qtUeZ;__azzym|exvy}VonKihXkp~}|z{~\_q|{Z|ytw}¤~wzt|z|pTvQHlrNT\ozxTeqmjyuqT?~tpe`mved`cmotVzfpbheJdebvvq}nv|awxdvqLjhYbguwx|zafz{P}|{nvz}w|wzfQlAFmiHQYmr}mUhmfhwpqJE{kkbcuvejkpxY|[qZ`QCiZf{n|y|t}qvx~XsYEkyWV^nu~z[iwvunVzxzb~|tx{~~jsN\Z6TpXJVfp{[`rpk|wiBqsseqmkmho|q\heYfS`FT}n^zzv~~zj~xu}t{g^sJ[s\bmtyz}s}kd~z]~~zxkp|r~yt|utdRjCNqjOQ^gp{zzhu|}oH_qsegrgkcezslcv\waeQQup\opiz}}|nu|w}w`{W[{vYWdilrv~mk{^n}y{~ngv|xonNiKDdjOIV`^ypx}jvxsSPzsp_ovogy|[}]lh\TF_nXdhfkzpvt}^ndMhxaRXg`}vy|qoy}zk\xuwsant{}wp|zmnXV]>Rk^KM`Yr{vrznRpy|klzvwhgzxfdqWq_ZLLjfcomjsvtzwprYsZYuyaWehgr|zf~qhkrzqxxobwv|ok|xjnW_\E\m]LPbWytu|xhTyv|envxtdhxzapraxe[NRlbiljfr}suy~tu`v[]uv^Sc_dk{x~gv|cgn|uzul`q~t|~vrn~xijWY\CUh]KL`Rtvs~jWs{}ni~~miz}iiw\thWLKc`hpkkn{q|wmt\m[UmuaS^d`q|hz~~hdu{p~tuh^fzsypsipruk^]M_GNegTL`\kszw`ey}a|sfqyxZvagt]TM]kk~tyn|jzl}zpoeblUdxr_Zl^zr~rs~~ucvo~vsqneavyz}zb|tetnv^dR\VI]k_OWe[v{f]xhpzjjx`pp`yhWPVjh~xwpwrory}zkn^nY\qsbUf^jmz~qvcklxoryla]mu|~t|cuwbqjs]]SUWFWibRReY}}j`}}rmnkw~`kq]ukUOTgh{yuxtmpuz{wfj]kX[nsdWh`no}|svfqmt}{lnwh]\mr~u{z^qm_nfmX[QYUJ\leTYf[x}kfzmt|kju{^okaxiVS[hoz}t~molxw{qiielYbtscZkZzo{r~of|myr}pwnc]`nq~zwl_wxcgmjiX[U^NPcj^PaYjnȵvgrycvjr}mhvfoz^SSadtyn~h}vq~vzijdlaZiskX``\k~qwflyktwzp}rskYYdg~v}ny}_oxjaogiZVU\RKZfbRX^Wp~Ǽmpym~mnysgsli{fRR[_wr{nlxxo}vvmeelcYeqn]^dVqw{mkozr~tr}{oyupqXX_bxt|mcjtl`nid`UV^WLVefWT`Owr~zǴus}prktziqqivuWSX]luyqwto|py{rtheok]anrfZfSsfw~n}rl{}hzntpvwrvaVY^e{tsl~s`opdfpce\S^_PP]f_Q\Q_d~мs{zmpo~qrsrn}iSUY\~pyjmw~xrwqrdhrd\doo_]`Qqnsono{}pwow|pvtpwzZTXYlrumqkcnkbim^bXPb[MQ^e^S^Mleѹw~nopnspqm|dRUX`tznlwztr~snq`jsb]epoab^Vnss~qor{}pultworposxXTXYotwoqi_ig_ekZ_YNb]OR^gaV_Ohf|Ѽñy|rqn}nspsm{nVX[`wrkqvsoztjsbewe^dpqeabSvjsyrֽ wwSO`w]]hLyxpoYITy|yteWZWbqki_|_R:MfOUJcsegtzyz}mwjvrqitvfotdoHUbw`^dd}}}}hio~mnmv{pwh||r[[gwvpwgoh~vlx|yy^Wu|xsyy\[^^u~sP.*KYI1+JA>J]ihgo`yes{{wxw_L_r}kX\]jz|prS`pyˮrK1Mhem^BF@NdUENe{aph]uqfTTl{yu}`X[Yuqtil}p|_W2-Gdw_zrdiu}x}k}qļϷƭj_LIcpOICImaSVJbqlv_nhaT9IYkz|rdqpnu|}ro}xkga[HDgxrulwyir{uvxeeIC!'EfiiVT[`[VUCksVuj]ec^Kdpzyuusiqk\Zqiidt~~uvcsf^SI4A`xƮ}a_N_eNDNdjThR[qedVJPpvvyprsXVT[|mpbuyii`UH,;Niuytot~tu|yͯǾpndVIQxq^VPqu\_RZulskei_^>@KZmorexnr|~tgms`b[NA\bzzpq~ko~xuo}~y~gaMG%"3KozjefjfaWGnqTojV_`XKa_v}}vustpvlYRdsph|{oflt~a]UF5GIfշwh\oeOLWhbZaRflgcPDLjivtpxnWWPloogdw}kh\\=05>Xyx{y{|i}~wƸЮ|shTDJoĴscntkdZn|lwmgagT>AAOf}wwg|usu}}evvqi_b[IPTS{m~vktqly~}i[UE%%'9\yxysnUV{jXqeT^_SU`Rp}zoyqv~xznZP_||}vp}pihj}~|`ZVH:H?RzŶƚ|jzt^U]hi_`YckifVFF]^hoytx_YQfsqkdxt{tcY^G233Fdl~xw{Զ˿~xzrk\ID[u|ǰxsvtejxqzj_ebEB?CXo{nhwwynfivy[Z[OJXJ`ȶ|sroz|np}twy{rYMP8$'+Cf}wűy_YTEajow{fiXctrr~lal~w^hq~rz~d_^bwv}|ufap_dtZTYQK[OZzQN|EAL^jrsj_ayumvtm|n`h\CBALe|z~r^ezckoV]e`fvjį|pyoly{vnv¼ŮzpKfh\XB>PC8?DXr|vrrw|m|pX^^Vci_{XLRWmuhhilnj`\jymaUozrrtjdjqƖnSHOnĒssP9>;7MKPsxojR`moxzw|}xnnx]IHko|grnXZ\f|y}{ƮiT:.I^t~tjd_VH?EYScsWlaX_H:/Ic]puvrvvZVdSHMPavzyhphYPVjgkjfyzur}}jJ65_nϼ_b\A>Yjy}z{wmgoz~pbYu{svvks\KAKd̬~]]UP`VTruyyxvb]HXhmttu}y~yqquyiPAJrvïm|t`aaj}~|WM26Qf{xoj_QIRYPnhWmXWYD66Y^`tu|p}qR^cNNNRev|o\~cWR_szjpkup|stsV?3QgnӲumrYLFCO`i^j}Tki^V[gvpkzjyiS[Z`sbgp`PUm}s|oggi\HPY^i}~cWȕvmm|si{ɯukTEW|~gncOjq^PIP]dY{x^^QWPX^aMGSY_k}xoaZdnwzbhhVQijdygHLoq|}hWp|hef[DJW`nϸ˵aCAvi[;%0+#3NIThm~bllVK=MA434C\nmkvk]sukjvy{rTPQec`rmzvx|qvϵt_\ALkx~|{zcgrdT_js{v{wmhbbn¥|xp~|nllxòuPFFYSWkl`B;BGLSZbu^DjdRSNKKTUbw_nnV]`U`xs|d`_LMQcxorq~sse[TVetyhfSL\WQcehtqboSCL;548Jbmmsvhfzunq}{|{uaMLYgXipu}tzvù`aL??G_zf]af[QnsfZN^N67BTB@]p{~jbuoY[elt|llib`cnsw}njel{VIL\NJo~{ZPQQTX]fz`HqlWVQMJLG\tdttZ_d\kxmzge\KHFRntqvr]yYidMKXdlyurdbz}clujglcn{pykLd~NDRemwph\NbehXEWfq{nqjqwKeLKSZRY}zizwp]XPKTdp{m`cc[GPjo[et{xwslgpv~|pnljjceiAJsdzVUH=IDHKKTcmt|s||n{lTVju_dyotVD`{`^v{yl|ucfo|eUU~pQ^\NK?NWDF[scWflnmhizqnrppsuyk~{wpdMMeWMnxw~vskcafjyoglV.)Akŋ{^P[fmomjzyOZwdWPKJGBJ||w|^[VZvtkfluq]baU\WYZRX|uzoxt[\zƩzrkmtP/QZRgn{UZj[ZdV]syYV^hlYISQaqtzvrxvwh]QTuys~]`{noy|qlzh~~mamr_QEMi|yf^QLfT5HOII:BL;:SofbupzgwtqmmkjPVZru}ռmxrYYmvcnp8:M^{pZJIO/!NZMR\fkgYmn]fnVYx}wqh\]]dspy{|xv{|rqxsstsibugk]42F\}wstp[SYQQJEEBFThvv{tlhq[HQgca_q[kuipkz|una^YbkmsyxrmVKJF`tyt`Q=HPFQrplx|pbfQy{wz~}tx{dZpsmr{y|{}vqY]spap}yllc=Jtxy~~zvtxbfootxvidojyv^iushbWX_ep}vjh\iuv{uymh~|xyc}btkmtxti{rWTU:3Tumrqoidgeeiprwrcmn{wf\h[ISlg[kvyxqipmSo{z}Zk|omjjo{|tk__a`eloswhkrnwems__u{edpvth_OmmRGHNTU[c\pvkrtodbeho{|rnu`Sf{sxjPty]xwqy}kyuchuaUm~{u`IJRUSWbhnx}jrih}~o~jdXUlngywmqr}qluhPb~uigd`bglsr`lmgy{jzyxqeniRUibUdr{}xQTqlhloqsu|bmzzouxwis¿wjjjlt}rwxjzouvcfztbhnoomfheABejq^]xqYQTXWLVxwd^hbBD^ysiw}rjmonrx~xƵĮsqoxidvpvkji`KE_^LL`o[\ujUY\K:889@L[lymrqs}~~xsdfulh}rv|yzwkgz}hTe}jgnbNIMPXcnwsk~tn|oeRDMD4BPEI^lwxVetklu~wav}uwmmjinu}n~|lxzoamxmv|nuxxywqsrRQjgdksz|wv~nPTqt\RTVREQstgfsvYRg}~{yy{z}ɸ|o{yeitz|~eK^f`hv{wxr_ShpZOT_N9HavutwZSbXA:<>DP]m|tk|nz~q~x}|pombP[tjXU`aHKju~n\XUQPRV^^JUi^dv|~~ofSKWMDTUGO`jvjxwx}p}sWJGFHP]l~zox|ikqabm_T^einokmmROhjiowvXUms]PT[[Rawt~k`vywx|~m^gf[isfixw[g{y}jswqtrdR_p`ON[[CGe}fTeo[LNQU]hut|zn{xrtqcZq}k]]h_HUv}pi[YWUVX^g^Nbm`m~wkWV`VUe`Yiwvl{roofmxj^yĿwrsYQPPSX^gm[Tmkg}yiqvjpve_hjkliejhTb}t[ay|g`fke\o~oo|klpziggegksqj_h`Va\NVbhnrpouh[stwrto^WnubW[iaOa|_WmjSLPW_jt}r}sckwqx{yedy}g\`c\P_ujYVagTUwq|jgfcbdgpjVdypusjXRa`\jg^jw~ui|xv|yypuwg|fcxmRJJLOU\hn\Yuwqx|oksuuvsnqn^g}s`ez|g_cf]WnqddooY]~}usrqru{vg~t_Ze][bUP]fkqrnoqce}r`j{mZV`hVRrkQ_oYEEHLS[ftrarzr~|pw}}|xrpgtu`]_YKSnm]Ycqh[tskheddhrr]b}xpsjXGPVOWTJVgr}zx|~yikpinzsaqeaymPHIKQXbptaew|ru||||vpqj]dprtw|}bW_tsa]cdZa~wqv}gotrsjkklos}kj{t`cmghbSXdipvvtwvlq~leyyeXXb`JMnzvh]wz]PPSX_iwrl~rrqq{~}~tr}xvvdb_SMcrf\^kra[y¿qkgedejp`PdvgcszfZgieeVS`jqy~||xqx~~~tftzjgoyn_uhonSKJKOU`miYiv|wiotsuuqmnmfiswy~tkn~|e\\WKSnrfdkyyej|uvihhjms~nl~ganlh_NQ]cjqtrtvqnu|}ogzsfdlrbWppVew_HEGLT]kyqdz{|o|xq|~~}vx{txi`^UITkgZW^kiXbwtsstv~{dc}oo~~xmXUegd\NTbjqy|z{~{v{rk~xkiqymby~bmhMECEJP[g_Pc~yn{w}~wpmlfcjorvoijy|h]]XNWrthdjwyhkyokkmpt}~ke~zxaUbfaXGFRZ_fighmpouvgrr`Z^hbS`y[ZuoSHIMT\gvwfnxvos|~~yvxxsxviemxkXSQGD\kbZ\huogynkjjkpwkWc}zlt{seRVgifZR^ls{~~yjj~mbbkqd`}eQft]IEHMT]jrd]w~xqx{zzwpjjliirx{}zjgl}}kdc]Vg|wkho||ny|{yjffgimvp[^ztvznZVgmh\NTagmrroouyx|lfx{i[Y`i`XqnXg|lUOQW^gs}rfzpyogotuvuojkqppypklz{h\YQHVkfZW^lqgrrljjknunY\xttxlXUhqnaU^kry~~{z}wpaauwg]^hrkfjUfu`HACHNVck]Wq¼vqy|{|yrllrpnu|zmjm}}ldbZVj|tigo{{o|tzsfdeils{o^k{}tePRdi`NFR]cinmjlv{xyksxf^`il`c|a\vzaQOSZakwucjwt|ns{}~~yrnsxrqzpjgnym\UNDLdfZUYfqljþ|rppqszwa\s{o|}taUetrbRXelrxyurx~zxii|}k`^emh_siVdtaH?@ELTbgWUpzƾyx|skmuphow{yroq~{j`YOSkshadmyyquifhjnuubYm~nu}ubPZllZFGS[agigekyzs{ļtpsfbfnl`k|u^f{qWKKQXaowkcyy~pktyy{yrihrtfiɷs`U:?;:2%52?ND;Tx\7]zs||{fuxoocdpacPPY_w|^DXtx||~ozQBsˈUXZqpukc|znc^O_\ZWHNK[v}jnY/Sur}os~peebf|tyjb`XdrfU`s},aЫ}dWaegdTfgXKJ?OZW^POC>KYYgvzvyyp|f0Ivrb`inqecedmwɾzxh/ Uó{mjS^cN;86@VNWOPK<16E]kv~}xdzvgj]YQYpyJ?h]ktreex{~k\EBPK:ïĨzgbt`C399LE@?:BCA3.FjljfsthidZL>EktHiʱwbg~xdtóq{whL3/LA(w²˫{qoatuW;:>EI76000@D6-`t}}bq{ormskaI=FvlTɳo_v|vosxya·IJpr|~yiH1+P@E̯Үme`\[zqR9ABG:+*')-BF5:y{|buoopqsj]F:Jnoйhcqggdpwl˻Ǯ}gw~u]C,2ZGlŵٻr`[UO`}kJ;GE@)#!$)AM5Vyte{ktprkfSF:[rؽwarpe`aZvlӻ¤knxmTE,H[Tֵƕ}a]TLLpz^>CJA. "%"1IH8tgkpnqolg[JDC~ȯjczg`cYbqȹʽƳqdwxr[M:8aT}Žӣj\XNH]|nM>KF8%%',+BQ5dz|sbt{imkkfaKG;g|׶r`}i^^ZW{mԺθĸtcn|xtbOD2YXgѷղu]^RKVyx[@KL>*%&0!%=P11HK19|x\^k^bgflPF7G{r;zdtjWVXNth¹ĭohlxuueRM6L>Eĭκxzf^d~m[unM?:B=Ba_3Fpgq^mu~mb=PaNior^ikyoXac[GHnҾӾȺnhx{bY]^bZ=4+EFJ۲~pmg^k^qn:@odazeV:Wn[~ڪ~xvuQ[sXIP\P^uy0neSqv{ZZk|sbKbaJ^FDIYePvʯx_zymy{w{suŸϸɖrorrtgdbpeN;UkCnx¨towewqEN3:C@oiN_gVf|nF_^vd{]]nulOLiwWe״˩sh\xsyepŮuv{imXKU;P3@U\vcwͿudymkvXNQ[jY^zpHXXoxrj_|i[xqeir|kWQ~Zkxzhu~neZQi]ˬj[tp~PFPWedG:5-2`zjk}á||_d>fj]us|ru]`XumE;9C[`[HWkfXjʭxZg}```WrySwsgasmL.GlM7/7 LpcOX֩ɱ||y|i^_IPydVvmurspdivb[fV?:KE41jcRxηx{dYoup~|tLegHR@9;?-BZODKfݻsfG|{gjaVkr|qrogXU*?UkJNbWXTx~gkcZfo`Wõ}kLUgg>H.GAOF3GivVvvZX`CcsH[sp¨{stvdbh\S[>>Z}w[Xdÿ|ng[olXIt[pqrOcWhW@7KB?O`^YbuɫszoinRT{v`Mbqec~jfZmpgW2^~ZMXul_Lyw^Whtcujzpkdd{qB_WF,WX8LUM9mɿԹ{}~pa\qbxULX~~zqRd^ConJMcrqɟ~}Yq\daDk~}ls_`waXNAAE27F_W``Sϴ~nitY:lt][cT~Ll}quWQ>dR=KQ[jw]sǩv~|}bQyjll{jYq}WivT%=gM=;TMYW}ttѼɴvcnty^blvhUjzMUq^hs\CTqWZbV:^mvwV{idgcv|uv{[hslM]vH&)3+HGEizʳ}zv}c`hxxy[wyq_qY?XZC%?lTFZlMmƮyydzSccp|lp|dZaymg`TMU;L==9f}mL̴Ķ`qicxlxn~cwkUqaFGRWCFOYgtVƯǚuuuzixynQxp{|}ugf[ltoQG@'N:8;9Rqh~ھԻ}v^gcI`pifx}Yvmv`URW?FPYN_geoª|vez[Bp`YngPw\slhssPG/]4*74@Yofn~ݼ\dzdbc_Q}tta>cnSBTTMS^df|}tkso]gnwXvuvdQMCUzjMA^WITN*-mvgg|dl_h~rivw~aSrnB;?+;VNBWȸŮyviclrgm{htnw~dxzku]z]>AEqsvtuiQtk`bpsage`_R^hSHsuToyMrxpz̼~uMYuqihQMuJ$Gzv{u`iwxjinmPm[K_{p}wwx|ypѶǫy|pF=?3B.>QOXfpbns~tpn~fSQYieTƴszrnhxVHfyZUM_uwf>EdNToeZjkzvqvxejukyvgRIU%^}x[0". 7Nrzw~zoyugc`bTsbZA2^M=(^WNS^_[s~]qmrqmw^^I>NX_J@I>r~̸u}j}igupvqmln^dtlv´{QeWSLWm]L9<>PjumQ^ku{mQFMpznu=C~|}I7.j{ZrfZ\]sgwu|^LnmkPbkL/8mƾu{r]auax`k{tcf|¼ůlwm~N\dF:aY]F=Ugx֭qy\v}hYOX^QgbjO]c[evwH0W}{mvzUwdZeknykWhvqowkk]cfej}^E^\Ūz[[n`gk^yashRh|{M}_V=6HPN?>glVWjjzp}zv}xx|~mlchO8@DXbaP_fpzij{rPytklp]qyrZvubMtqæ{zxwO\X]E9!0Y̽|Ǥsy]a}cVbW_yl~xrvlhy=P`HaD<02EFVmrZ\m]ZGpx^bZTU_ss[IKI1=>n:aµyq\Mnqp?$:2FL\ki|vfgm}rk>AwXphm{iyYTbuZYLf|ossl~zZ]`TRd`Fpu|XRƚux_iq>qVXSUZVvrpjoh~Ƌour_VqOLbmvxnWHE'Zvz}jdl^xymhzs^j~b|dzsisѢbRWUI_Onkw~gbdŭUoz`N^G}fv~fRE\tz`YQa6cxacClzxwaqr?>KQBN1tgdx|iYo}^jbvQZ{wzqøf|lc7[sxtYsztvrVQ >ns~^n][mywEYtys`Zp|haFv[Ecru}Yexbf4rt|epr~a[m~rТ~?Jo|iXzQUWdtykXM--^hdd]FX6eykb^P[zqhaz|ui}]hnaLboil^`SZuǨt¬byzVtsf^@RETx}bWprmkP-ucgLBmvnnXLv[;=HEA9-x~u`~vdPmrkn~]lqYnb3SqspbEg}{hX2JqulgbjmCTy}tf[raiAsgB[rwzdbu`g:]yuk~fx_^tvݲK?h|s\mhbdhvvj`E/LkmgeJPFD~v\]OTgugcmWpSvubNCqpjc]R~q¿pl]qzmbPAT>nxzim~ur;A{_lADerebGM}^>=B?=3Ej\~{m_xtos|jrxS|j[.Hfni_Fnz}vjYD_~tqijiGP~xods[j>enH`wvx{h_tu_fMU~btkkUz~}vxʽwT7Oywrfot{zpkZ>>Xooi[OZBlti^RQ[hqgp]fvUTnzi_G[xjic`]tx˹|_VdncXFHXPzz|pi.]sbg;KclaY>Z}\<=AA@Dnz{][zwl{rzpxuH`_@*Gdmo`ZqtneX\orirw^G\~vs|pZ_8ofYr~two`^owhbiPyjlw[ivsyxwwu|v]?8h|yvw~mhrcRVqukVz{h76Xnjn~jzf}u~TqdKrnmvlXoxɃSFEorIyzqftzajefnhzy}Vbw~wzlqnYx}|POeeK_vwldV1^fsxcvfmoqؤƗ94D{S:9Q9$OCbgwqknx}dS@nWadqmgigy{\ar\cYl\BiqtL_̶]ƺԹl~1DuO(@59OxsUFojYS`EcmsH7e~mux{rkuXUiIihn}w;HS~|ZA[iyxɓ|xe}orG6?5fӬcWcF*BPt}bPgyxqT?hY2nafZedu{w_inSejtKbpZk}ay_trywj_~qz{ɴehW;?cݷY=Obm=Rjil`^]OkpJjQLfoY^hTJmgn{z{zy_2ErWQUrqetpnm|qιtϋsxeNjc>QlўfYD^uG3h}qERjkzIZRJYv^{}c\dMNTs}h|lU_RAwqƋcTm}{d{~Ű{zffl9.1Va\˶v]P]WMvXSWUUcqQO\okym__^HoraGZd\`DqqIl~cczWfq_ms|eUMKPQ`{p^fuunoqryɰ{yDGkfI<`ftڣinkrbWWc`niG^dd;>dzhNfkAF^bSSyaNDVj[ETxm||qa]oHC{ki~wyal{tV\mxW@h_i~Y{}|uQpXVmrtmelrsUayOsxnXamo}|S8®ovs_jqlik~zNJ[}|liqhNamxgeYnqf{ztm~wvnV4&Y)NoB;C6sfnsesx{v]mqo]sw}^X[4LtUkZw[`byzpWcNg|ˠnF616n˩ilFBtf^yK5~ljpcMmg]V8?VckjZn{nucwnl6*cgT_{}ow|ki̤Nmi##zӦcOZkoUdjFX_gm\MheK7>zunefla}RKQ;SM-]̐~msm{p]ukty¼Œlnc9F_FbmտvoeiWIUSJah]hUdKC5,DJYhwnwlKvyly\or}nhj5?uʲztx~z{rhtumyy{xSOf=3Wuֻkrk\@go`\Qns[QIJ]9FrkitrsQd]slehaYZTA2`mov~x~|zfvdqǚ°w|`=NK9LLp̷daMUcXZkdnX<_[KKM&$kjzumwa{p[U?@PSa›wwzfuwtMpVWhyǰɴ||theJ.BTYm|ȸpgivQcrYF^cARoC*1*MdXgnms~pe{~y{oTXgVS4Jn}vuvixnuhp|f^Ʊֹ~snjVDJ,8=cľzodkH>r}RODBa^NWo^beMk|vwmqzmsqfiVD;SZ_|~[wrbd`}ddƬzw^ia@.6Ap{Ū}TWLczaNd}moS9]qNQY[\~jc|zuhxuzlTYVYN;j~trs{jS\~{}jy}wzo78VaFL`I@waassvt|giq|lm@[h:K`{|aKTMXrtphaeq}roOA[oY]g|ϭzI{M8A;@57Nu}kxx{_Ntvko{QIvPHFO\rzvuYdzoknssxxnlbgvlɻs5K~jolT9;BSWAfwRnvqd^[OHjoy~er}opow\GM1t`Xo|kL|}o~w{lWRQgȶ~\KG:/gsb}SJxloqyxk_myWQTx}|ozzcnr~\o¯vϤaVhbGCH{mF_]JSk^7X^>OD>]ZKt}flj{h{}r}rcioTTJn}gwqmvftokJc}uӿÝx_`kTA.6hf^apzppisxv|i]jmT[X~lx{{hXgx|`xʽx_ewfVMM}~nSvaHKaS4\[BP<9abXZjkz|ujj{qx}qyxXQAhalkjykmrx^q~ĸĤfZcUF@9\\Oybggvztoz~swm_hmUWLaxtudtntYhìz_Wg_GF=qVoxcVjkHK|f/Qųbd~gQ\qS=gsF?B.B\T`^w~p{sn}swwu[P]YMWNyf}~qjiegbYúqmfLOaTIJJ{Xun[Rk_?Oy[BLBCeeZrhcv|mvcrnnzr[aiVYSavktrxemn[gƻùy}ywaJTXGNEhbbziW`x]Kp|J<=0Id_caskzumsssvbR__S^Sxl{qfjd`V{ȻzupjOESLBQPf|kSagHHsgAB=6\ngot]~unxtyosmPK[SS\`ns~zrvamcnʶwr~slI?TI3Wi]ζ~PrKWoiTjv@3@BZmWbLw~sdnxdwp`q3GeYupǵy|jcc|ZRUYɿsdsV,$69LqpєpgB^y`cz:@F+jsY[fh~sowtcYn|z^=I\pzq~xpem]YVukĠ{ojiWetS&2DD?gvxvRnYGo`E=>'OjzsinuplWpzo]X`I[^j{\ygzhcPuɾkt|gMi:?B6jU^Ş}cl`^n_9Z[2:>_`c|r]d~whr{zjsi^6RYZ[Tđmxy|jhmjsYvdrycyfB8B_EAu}ĵƦdoY_ihqbkII4K}UYn{twybtwlzrTRVK@HkviΨ~pzrjqsrYig|ZsqtnJawJ;HM\Qcȓzgb_^kO~rNFTWPc|hzwo[_nivd>bSSOa~zyyirquZRr~iccpYaJG?0LY_oòf}]UegKQ~UTIro]fkorwlwyw`heopssAGTcoSxftl|hnshqyyn}pudvuj:0N@8Caþ[]R`phrWZ1TieZVy}~sZr]injRJkkWXhvjͼlq]vc]\r{|utTuk]ZSM7ISRSoƽƴuuZpo_wKGTZSXny{}t}wl|b^[nQFfUX?Kl~x̾xstqkiwtii`UZSiMYM1>Ym{mxXOuzpgbU:krf[bsmoj]sviwgeJEbOSvxw{fkwlambsuth~naacF'5a`\^Ѵºtw~jQef}hk[J[gx}wjsUZ{hXFjmIENF?`p~|qvgvepyl~{{ruggmTO>C:+YS]Ofپxw]xr|dC?jvwv~{n|jcgtpY]BUcY]NInv~gumNll^`gjsw|vYWHpGIZRTJa}uͳ|ygnrjaT`6g|nkMkeu{sd^nYj}re_^D1Xtez}~l^r}rttwqxOYeS<1U`F24GEW;gmaxUjhxkmztxtp{_an||d[dOGDSLC{|t}luwfx[c}twziaurbERVR99RRao¹‘kx`?[pt]S^TXkoeryChisYq\^XQzwy||zzvy}nsudP\|~vqr^SUUQ/^dJ[jh[bʱӽɪs\D^yzOZW{skuu{kaxrhAuwR8ZUN8@]Rfcaȭwjn~_g~{supz^jll:*>PR]G7KY_c[Ĭluchongz\smrCbTxnntdNiiNO[bD Gp^jV̽~|vwp\`dnmi{vJ|gtW;&+LidJNkSZa~ѵkxpm}w`KohlXl^uVXj[`U]Q7,Q`ameƠ~z_Ybfgjqp}{ѯ_bnH5Obsp_fmsh~ioźtzy[aINjC?Z^y̢ZcȌSV^gfp~t_qzpr_[zt}zwfan\eITOdp`h`x{ef!'TsIEgt_Y_qseĠ}|yxdfbbY~KR_}qy}g^QSelUafnylrv\fkeYfZh~ջªb4kڳr.';Zg82jwNHPeykm|~~Uc{RcI~~LNayqgn}seTcaGNgsuu~vqsͻwL|Q68^cJA?dhfolpR~~k`|uru|\o]YeSz\Pqge_rwwxehwyxt|gn||~URmnPDƵwmtzsM5LN61C]fHtw}rcqkkR[yvzsj[haryVZevjutsjXhobmyz|frvªm}kuomqnR~˟jnsuW-O{cJTSomehPb\|~}v{ky{jI[V[wrhLZyq|zaYZPv~ƵvqQ`r^eyj˩WJZlraiafroSGikB4:PbRxkhMReVZPgnnefzZdAftdstswgsutotauyjd|nyã¬yv|}}fv|ZT`\SVwfF2Sn]ZkkqQe]q|i}twkj|jnw\dN|~x\Ymbru]Z\oyuSMzrhpykk|ɾ~qtzv]WgngQ|ո}z}mYehNBIPhQim\dG^`d`Xzrmj]PxjrgggiswaU{~mdx{crlY\vrkzrl{xrxgf}eQSXWV˰tXGhmSZa\ZSR}gpfkmlviu{YaMftlh^[qt~{`TRYro`N~lis{p~rլ}oxbQUccbTp̸|{{`QbXRXMXFUmqIS_YTImipvlPnzslX{_HmpmyРozsgkrltfneov]R}wRACA<_ӺoVYgad~}TZC}ubonwnm|wdc_`wqeUWg|naqjTQTqhxR]teqrfY}ŰskiUPXjnpiڽq}pgdSRW^d7bpcfAV^[SHwtiqjXNwqgYk|vz^Xs_[wȞyln~dxvlqbrRHrdA36;AnּoKUo_Wur`jOmQ]]kuv}jnb`tqlc`kyyi^heagokDZyYisYagg\{Ī|w~uwnqoSIL]blwٸxzsahdfkmr;Wu`eE^rn_Lqu[`\RKvwda~zVHmoXSsңzsn|y{Z}|{ȷ}zqth]^^W[L+GEdvĮˮ}XlvtYSWNgf>alOngjwytt_r|vV_hQ_wtXWMEehrto[gjZsjv}Įoz|fuwR6QVSkv۷r~~{z^JMd`FSik\_FEsqbr~mZpfz}]bwuovdGNxi]fsŶxvqt~wsypg~}{qiuUYOJXiFDQkg^rpdahTP}~NCnX`yrq}hjbok]o_Rkja^`G]i~|h{fmu\c{j{oģnov}cbqV>HVTo{Ǔu}~qmNLceEQncY^MCytϥֽqyl_pf`\{vqstYU`UstUurqd=PVXdebiavchV|x|i{yo\lldXbdMYYlSdo~’ek|~n|sr}}{zpVsYAR@3hI27YICguxklŰŲuwclqsfty{nsjp~inNtypmVKf\]cp~wxoejU~_VWwwhztdc]aEPGK;GPJP]uių}crwf}xyyzub`dhUTgT@IA\ror{ygeknom~t|`^TR{\A3H^I=Zd`PIķltrn`zmjql~qztvVx~s]Md^UX[cUoy~kk\~h[X{xeswwnofkMQOTIHTHLM_KXl~ͩvqjlu|tfkj^XnbG@>CsfZXy{emgqd{y}~zs`_TzwVDFcU?Id[Q5Uw}||zeq}o^cb{v|ggq[YjWYT_MMYhy{pkn~hYon\y{wsop|d\]n]VmmcRnoy{\M_q|naqryVHaev~aeloxkup*dUfwųv{yVYqq}~W'KI\aUFPLRE1Wnrxj~<t]U_UiTV~ywh~rc~qZHftqy_dwvq]ecReI`tgjzyrmy^NnulfwmxfPJo-g~vxz~rtllt]prsdsmvm̽ɴ}x|iG%4cAϰooaGMEJ7#6];3$JL$_[VBM~rpk}qZqfufrlXddiaLqunxz]ih^?.Omb|{gsoqeEo}^kRof|orb{vlkxvj||hڴٯݻfaa^O<;esɾudj]cH']HBF0k7[iSmKqsp`ssxfXWve|wynS[dzTW`^tSLjrtkx~znxcHWMQ::Gtiz\ffa|f}{ytlթ¢ɞ{TOQOH7?_{|wWbS_LI@/@[5HgGjJrksuhy|uih`eog]lhiSy{vx{tMYU]EEPunstuRXZqQn[}zvnmyxxj{lo˷˩͠WQTYNCKsȬžireuZ3dLHG3o>cpXpO{dno{zUfht\WV}k~syqS_p~WZcf}VUg{eylvpsktY9GKP:AOs|\wh|Xt|x{sƓäxxl?dzj~tj{jfqd~yiu~weq|q_]y{yxusllRAKUJ?K}|вxux^kYekeRk}rimlrXQ\eSGQ|fxurw}hkijg`mvaaxo_zocd^]=;GN=;Tiȷzoiyuqwnjzzp|hjxd_ZYJ1:FD7>l|tĩynhr[_ZV}hbQc|vjnoudVbncU[{~x{y{upvsrzg~{kmfhLBNVIDZrǶxqvd]tid~y~~~cfqr`\yr{zumpmiedW?HTQGPҷ}}htbdoeUi|uhmkp\P[cVJVh{ww{|lpmohdtwe`{rfZsyi\_YZ>9Ol̾zsq_~|Ro{eYSpyoiqprZ[fhXTsl}|}ztvy|nwfm~ljfgZCKTNDN}w~Ⱦxkepui_v~s|xiakpaXmoxyzlwge{|jcd`\BFPPEJvzҼ¾sjiseXj|whomr`S]cVJX~bz{qsprij~sj^|l\Ypte^`[Z>?HG;>htgı~|uwj{~|luzocpz{zue}n]WlqaX[TU98@A45]n^Ųpyl`tbUcwwhppujX`h_Q[l}~xwvn{lcxuhngiOHPRD?\x^±zx|cm_e|quyyw_dlfVZvp~{wurq|i[jxrah`dQAIND>S{cǺʶi~qa]s}qltptZX__OKkt\w{tytkrmwqaydU]po]a^^U>BIC8Bm_lx{si{zwjrvj`qp{|~vcviXRel]U[SV<7?A54WiV}zc|gZ_syjpstrZ^eaTY~ro{xvqk^jzvenfiZFLQH@Rw]~yhtra\p}tmzs|f^eg[Unbv|zzezjYXjq`^c\`FCKMBEkugͿĮnn_dw|kqtrqY[b^PTxlizxt~to{mkb~r]PZjhV`Z\R=CJDB;<]r[÷gym\[oylmwpx`^eg\\{k|}il\\mracg`cIHPQHNuul|yf~hZ`rwhstru\_ecY_ur}x|urn_yq[LSccQ]YZWAGPPL\v˾Ǹ{rj]hwwhwpvmY^d^Wgh}z}ustxobc{hSIVc[P^S\K>GNJKgg¸s~ygdvvvxngnphir}u}pwoWhoUCCRXGMPKR:;FIFQywrõ|ki[bswhvtvv_ckhbnxzqjv^Q\ieWf\bYGOWTUlojop\Wfsjiwkxe[be_`|gxw{r{oxqYjsZGGW]NWZU^FHRVUcĹsl^fvviyrvr\afc_nl~|~uuwxq}aawfOFR^VO^R]NBKRPTrkrudct}pxwkgnninu|w~u~kz}dSieK;Alp_UfaOIOVVgkUc]MLRe}jehpsnkdcvfgzw{}iY]SF=Kstrrz~uqk{Ŀ|pahUOXnƦӥtl^fkVgonv|ta_dVME]usqw}{||{~]|dY]\SKJLqxl~pjovwwsry~yzbNPG=9HusokpyzVRjOU[YQNJ[xrszznmstvqhrb]exǛ}tsxpih\QQND??Juve[jdSMQVWihTc]QQYmwlehprmjbeubgxux|vbV[QIBUyk~ox}zspmzxztz~`cdTVb|˜xlete]ppryxj^f_TJLjsps{~{~}gdvWXZVMIF[{{pwvkltv}|ytuxn{|xgRQM@:;_y{uw~aN~wRSZ\TOIQmjxuu{nkprtvhuh_amͧvszpf{d[RROE>9Bfmýnevz~kffk|\hs[`ggbYHRaay{pt{vhlre]Sf{{rtkkw}yuoqp_hqvmc`atjZjaWU[nzxeUlVbf`end\O]omox~so}ji`gke]VSiijfhrwy{khrh}mhnd\PXptz¾ͻgb~R47>@=;8NVOm~x}~qcOYegouuq}lxrnmvv|i\ig~xli~kjonhd^nzϾzUKXbx\T]OGEIXf^FndV\a^VLDUVSt|pvvkbWhrnr~tkr~xqikv{xoӾu`KM]Z|}plZK9;IFDLW]hzgcsg]XZgwtZxp[_dc^ZUhmc~ufT`ry|vqz|~snuf_itogXL?AP@Hbhpvthdk`VFBTXYblotxl~zrps~oxwyui^S^ok}ns~vw{ztpm~o]IThr{}k_Y[em[Do\OTYUMDATSSrwnvtkbYjtpr{ypmy{tmpy{xo}ueVIPhg}{j\JETTNQY^eym_pk`\]j{}aj}^\bd`\XetfuqfW\v||tlz{~s|uhdnwolcWMFTWH\knsuncefYQDPceejnoyoqyvv}jkjjd[TUp}{p|}qtxwsqu~|o`UPly}ogekvuXO}{VMQSNHBKc]c|~sowrldbtyuv{s{qpx|~}wrn{z~qtrjg_TMNh~~zk`PUb]XZ_bnycbofaak}y]}nZ]cc`^_uujuwrf`[ryyz~vs_s|nfjgdb`s|{mtruiadgca_l{luoa\]qsoqw}mnOasfVQRfl_q|}xo}u`vtfgls}vbx~}{~yof_oxuv}vh^iX[aaaam|mpncnx}q|~ɲoN6Myoc`\Wbjgg`kyvrtzhsaafdbbj~{wddhzphhkrvrsg_j|zqa`t|wrmW:Y{`MR]a_^`plYekkruhqzusv|}zz|tVS{ndgufd{yoxvpw©|wlYG9DPS|phfgsu^ah^XWbu~hSvYD@DNahel\Wkruxunkqliip~ryquy}{zz~ukfb]dhS`iun_KSuk@:S_adedmt||rmnr{ozX[v_fv}yvy|xfagsrtnkgkuwy`HYk|vj_]ny[68kiIBHPQRS`n]_ty}}rmoltut}}z|vf^X^ucbp|øz}lnuxy{y\GDIdz`PHK\UI^hikox}pOVmKCJWdr}ww}ytuźupqxzwssu_ipr{~o^`F=;?Nf~vpja_e`ZRISWTXdnxyetnstntyvrp{vqouw{r~z}shggiq~}umehbEI`pncKGKNRXduh[jqrtrliqrv}~}yx|{spgVUZ`l}~{utuyyyy}pitz{~{ooytnfZNPY`t}u~{~nTpuZUY]ahsmx{|ywt{y\[mvvqsqlin}seu~uputv|~l]zO729E[wyx|vrkdnsmjjkp|o]qoqrinssqpzjcnkd^WQ[o}u{[e|_[cmsz~ridaitc^zèndfgc_^kjU\nty{wptyz~{vvy{w}}q^||\TYbmz}~yu{zroeiwzyyz{svsyw{|yyv}vne\[kztwiWzeONW_goyublpmlkin|vpjhqjXkpdg[`jmllumfv{{{yty`arNDNat}wkfonhedemvc\snWsvquvrnnzv_bkgc^XVbox|{u_ya_gqytlfcml[niefd_\bn]O\`\XTNRakx||vqNNqeB6@Sfx{~{}ur|}xuuviinhca`exxvytsrhM>f}dSU_hnt}yfpwvwvsu{wrk_chb]]]boiZpvqufgkjiiupo]fmLEPbtvnomfe_dqsomlmwydg~hUvmWTY[\_hwjYbgb^[W[ku|qtxjnwtkd`em\\zrojnolikvoY_igeb][dikpkoupihjrv|wWKowUACN[ix|{yeahc\VOLVak}vYFgyZA=BHOYj}tfryxxwtu|xtoekpjebaepeVkuuxvvxwx}\C]nO65BRdz}ul_ae`[YZ`nfWgy`ei\]aa`bnr]Zgiggdcjqu|junqtwyklpkgffkwp]ipiga[YbiWQaggfc^`caefiw}yvuw}ssYkwWKMSZg{wqlpqqs}yiqtnhb[Zabju}mOU{uUEDGJO[p|lkx{zzzz~upwxspnox}hcx|}}zy~py}{}{}{`SsyV>8@EM]v~nxo|rjgcbfpq^T]bbbbchjjorvp]pqa^_`er|{ysh__`]\\_ikZWj}ƻ~pmvyxxwwzxvxuvqWKfpWKJMR^q~~y}~yyzsvhc^YZ`g[HJRTTUW\cfltxjfg]\\_hx~rzzsiehgffgnvm^h}|xvyvjqxyyyz}~|}}zy~wjRZ\Cuxs}vgz{pbizdwvx{_OTO$,SlVrʬ{mswtovv^tcUVnwqINrxfb_dpb~lnpl^zmyspdYXW;Ueb~̦w`b}vf{dxgz|u]UcZ=MAKGD_ndrj~oesY~xITY65CuhYwz`uppqp|x~rcqeOucobG{wz}i~|untrkĿxyqno}}meeCFZ5>BjglhbqQVGudDYGU]V^nbv}}wtmrsisk`~|QDZ:JG}~v|u}x{fo{rnkmcs[UiG7BMOiעuil[{UmdeujfjSCkINNjpztne|m^_YM`p?MMNegw{}~urw~\~lhmu~{{pliVAU=?^Ksesʢw}v{uSutbadoXnhOPY]Zqduw`e_haretodga;S^N_rūxvy}fl_r`xn{gue|wwE[aaA9aPjtuxtkuvxiidQPbEbnmkpnh^y]prrvvlX}i`Xd`Sk`WgyƯ–n|lkwmrzw{y\}|~oU]Z751]:9Vb}vtn]utb{vNkrrRXv_kji{v{glnjkpvlb^XCLZCqǫ{pkzzwi~ur}wir\Wc~IHJGVLGUTHv©~uhZafEDEtVOgomzxu{wqnlj[lrVyutoD]u~pvzsxwyxsfmppkdhjnziaeR;UOB@X`aªĴjxy[X_ZLUVgd]kp_w{~}}zvgzy`^sxUI^?>E|n{{yotltxzx~v^sr^S`]JWREJMRXħƶvotxail{vekgNVk^Yit}qlkym^vZLHjd9@G[qo|{s}~tbxvdkswluo[XOH=SE:ek}q~hnoahs]dk`eimjtuvlgZ]|TX\hlVUWEEgmo~lw`^y^]j~}}vlcv[RqkqSSV@5A\aahylxahggu{xKmixvrugh~sjZwfi^Y_9M\EUvvmrm~ykwltujxjtZUgW8kp?F:6X>;iewtzxkRbn[]cie^`mgiyr~H`aan_Jdf[oo~pvtnK}K[Ydzyd}|fvklbOZYH<4NHLHgwpmzpWmgitlbuhqn]olltpuIR^=D\SAzytiyntujz}yo{yyfapbE:MaJMi`aX2^{ijzleqpxQm~mqzgjpguXdlisgSnlO]T^]Tp}u{yqz``[NSqkas}iwpus]acVX:OKABB`ZSn{Ԣ|e{yicgpx~vvg|zepx{tUncKFP4qp}Ȳkvp{]a}rhu|eynfq~s\ptlZWqUXW]fR\tr{urqfs}^WNqq_evpnwqk\k^ZNAV?F>M`OTivqǛkkhqdss{zswdrw}b`w_IEG+MpPMUlü||z}icuiyhos~v}s{px]\hC=4TaNGembW2Epl|ozruXlzneqqh{ui{xsi~kUUjNVW]eS^mostoubveWX{o[_tqwntq}h^i]cKKX@F>QSAP`qvsȷix|slfUb_^ggegd`jsn|}yxuwxnsrtujtr^_ayysXSST\giptsz{jcQdjr{~{yuWr{w~wv||{u}ljgflvtz{vztkzr~tiZfn][CGOG@IGJYcuwbtb16ɻ|NKHGT`^cdb|~lh|zmyofkedh\^w~~~w|{{ofLVxqg_kgakqnrk^up}lNtrgLBLP^mgipot~stZg}w|myǨeT?3H7z±fgfdgU>@\NL]M>NZbyvXP[_|]{I^|xsPIUYii[Lamovlncq|eTZPUmKtx_TRGvlsz\Wfkz{}agfhznhjmgmkzfqnrs|~ğ|phFDD6E`˵õ~tdjOUaYOBMQSKooKG]Ua^nHb{d{yVB`cdamfL`lbjx[UjeGaRvs|{}ZW]Eaokvme~svrr^elou~v^}}dsvgyrouzlͩkaJY^5-:Ṵ{lUd\Qh`FH`][AjqILb_YuggMct]ox\@aeZV^sNUh|nh}]\x}WVTnih~}r{v\[dRRkgvsi~unbenuvbreuosnxxoezeķhxkIUdL.6Aõud`lPijW@aabHV{WES\OgpgXU}mm|mDOm\YQubP^xf~p[tsZ[Zy[q|~u~uuqhMSP?_iprr|~zuvfhztwbrfqrutxgrqg~~˧gwXH\bB8MKGlngUgz|b<7byŤs|xsqj|x}|ktk|xpo{}{{pyzguw~zvYdP9O`RL[]|xvOslaFisgRZ^=FJ@_ti]XytC@_ZTIuvjms~w`v{koZ}gjhhd^\03:5ZǴ}r|kwn|mv~q}j~lrq}zy|yyity}r}rlkVZO5I^YWng~SqpcMl~oZYe>ELAXuk_ZyyF=]YQFq}ss|wycx|py]zllg|e|x`Y}W+0:8`ɷ~r}m|p~i|r|v|mlsryuvmpwzp|~}eo]aRXF6O`Xbwo}X|odRw}oYg\>HJBewk\a}p=C^XPO~{zsz|{sjwxw`{ir~j}sbrUcwA(5<@tʪvwtm}iuo{zssonxqrx||nxrtpula_XYTT:AW\Vvrho~k_dzk\{GBMDQxqfX{}T9V\VJhzx}z}jysdztsm|gtctu^UxX*.:8]yrudrkxz|ok|rquotmltwmr{qwW]QYUXA=QYQlupwlnhaq_~PAKGKvtiYs`:S]VK`|yyu{y{}pysksqxo~y~h|pfpvbUv]++75Tȑ~xxcniv|{pjxtrumthlsulox|zotQXMWVXC>OUMerepsnhzf}WCONKtwm^niFOSJ@L[^hrxztxy{WMSNjso\~S@[ddf~yy|zk}q|w}sx[sm]UTf]RQm_71?DjƘ~tlluuz~xjzm~tmu}pel}prchczqpYryuo~}wfXrJ>;AMRNDM^fl{{}wu|}_PUPhuq\yWA\gjl|}}{m|r~x|qv}XojXSO_XPQlc=5CJqėwnnxuy|xf|l}rmv{mel~qqdgbngYqwuq~ws]XlF=:DMSNGSdivz}w||]SWSwto]yNGckou}}fvz{vrwmXt^XKPZSMYpX:E`ƥywz{uzzrkkqxxntxkbeq{f~~igd}kxcO`msryqk_MgR@GVc_OVew{ʰêh_`TPe}xfbci|W_xh9T:VHBVKQattgvrx}jr}]jf^p\bIWCYRNm`_hzr^gne_{srmwWjf[iz~~rQacm]rlryzs\GgǤyl~gFr{ev^WmemG`_V_|}zrstyH]ddTf^lvrmT_d_Syjz|p|}^}xnrz}`y¬sRWWQDnƧtuPvwcL^UL_YW:[VMTroquxep|w{hX]i@atoaqhyoxZGWa^V{tu}z{bysz{dÿ̛srJ:JVRPyniTgSWaZL9\LHQvfmpp\t|vmzydS`_?N[L_xx{e]hMb][>@XCGYtchka\~xsjwbWhMCukhxrqsdDPaiUp|x|s_r}krlvڱssS9IZZMnrr_g_Uk[T3RNAIjebdgRm~uisp^_c=YxdvtznrNG[ma]z}xyxr_plw{cͤ|tf>DTaKat{|limTmc\=@V@BXn^\aPZumh~kahGC~npyqu\EVijVrx~y|{~\uvhnne|۳vrIBSfTSlx|sku`isaO9YIAJvx]XZUHwwqctfiX:d|jxwslHQdu[htz{xjdkhyzqvveˬzw`?K\dKhixwua|mbAIYCB[jXQXAZ~rkhrihDC|ryy~sw[IZpmZvpz}~}~_w{ih{w}ceu׻tsLCRi[Unk{~okj]?ZUEEm`RPS;myqdw|ok_:I}RRT}wkxY4qn{Zmz{{sog]%Ktyec^sw_{\njf`_nzşh?)8IUੂ}sZJb;9DBSOKkgbZjpcM:SIIny`iwZckxh|x^|TQPCFİvbOq{q}pxeG|tǩձZ`Z8*-Zxϲos~QLWE"Qg;GJ@ieV|o[_-6yp~iyls~qt{mzskWZ7/WSnŇ|otsc[x|ylm_{|}lHH+?+dǢι{x\\PEODKj``O_q^VGTN9GZz|swXTxb|fwraZ^T;8]_~t`oxgun~qVjPz}Ͱ˨zãuzeO.*C>\s˻plxL,fT1K;GmUbnug-K_Dbxxjrml{zzzl}|{np>?N6Kɿ|Spyxp^o͸qfeG=N0+T_ིtu^YQBVb\UQl_bS]lJ@GCcTurN}Yjbp}yk}ut_bqy}jjvvkzyhSVOk̾~mY?I330VǛsOrg<@9Jg[Vx}xwLMkLCW^^zzr]w}vzl{{}X\a;6LCCjk}yqhtshsxuwrd`oU7LPH\Ol̽qM[bW_Nie\WaudQTJN2Emoyf\YZgrogxX\\xhnƹt}xtypq]kğ|}sud\NbQLB&UkocdZXr_eYj|`]fWR1BRMU}ys`_Opxt`n~fkjJLCLNOrx~gszj}whxsZe{gcɩ|kybR?4J<8_tĵiPSsgXdyzaQuojV?5^Ynp{nvl}tjwx{gtTEF'7`RP`slhv{hP?@D=CPN1)Xl{vp{ivwe^u}uj`gbDSƧ\KZ3?[s|qp~ry[Ywtmi[~srgrmUKevsl]VqaAWϴ]Bp9+@Ubb`jqcY_̭onqymWps_omIcxbVuj]glpgfp^s\]unzkUPPz||vYp}zd|ȷmfdS\WRP@AMfxpGHlKHYVgwx[CC9mw|VfjrXh|wmocehqw|~jisoW~gZ^f~iPTEr·wj[]VP}nyxwtVNU[gjP,QD43Jgpbs~G4?*MsY^~yl]p~u\\kmbwpytr{|~Uup\Yvj[hSnϳþgJM^f[}x~mei_dd^2LL41Wtx_guA6F2CxvZ]xm~j]sjR]rzjgrwx}t}YsuZ\jft_oͤɲ}}[ERhnfszmli_cc[3QK28g{{Ykg==L4Lxwv|Sjqh{g]_Rcyxrjvu~|yyWzpZd~dht\|˝zʩ~}vPEUkjmvkrheciS4WF4Cs~vRyV9CI3[ws|rP|lirbb~{uTUh|q{|p|uyi_}gZu{ubqm^ɾ{zgGL\k`zzovkpjpG2@mztXkqesi\qx}_Wesin}wtujwh\wT{r_svʤvnk_R|hgyyzttJDpuonkf~_NlSIRzergvtjct~|oE,[mS>F\IJRm}{m[kllu|b}OMhjdt{h}²۫Ȳpi{tu`xkTw5?]ji|dbPeh˲\m~{wWRk`kfgo_M\jngOR}g}nW?padvpx|gWXbwciuNUytҿÒZ\^kgL[DK/;YWbQG_~lz~{jk8Hm^ws>Uu~q\kw{yPP\t{}yevqwtkyweXel\i`brjĨŜqvgUXgSpm`^F'4O`bN7dOvx}`F@K[]DX~xk{ezxmVmkui\kzLoX>FEdc}ymvnuiiFJ{٨Ʒ}asKC$0`zŬ_]QY2FnROZ^fW5U|t`Z_wvl{vtzcocft[@AR{}hjkyxMjkrtY\g|||`?>HG^ɚr|K78L`XDSicWffigLgOD+7dzxdplcr}}jfmoWpiBLsnZnФrajeYnx|_ES}~{z^MFC8,xŴvRI|qehikYZM|irulfO9NdbxUUOKthenvwifZ]F?l]wiuUqcsy^pStʚsȻurKgPHO5EesLn`WB5XZgT>UgC5]@Y\Ujgqo`dqrbmv[l|]Ac{aHBVoxusbVprqsw{xfUQKQ:MlkLPTE4OjZ]I|{shhP/.ifmZ{~rcqvnvn\N:hap~zvu]Kvqzg{bQjʭlqoB`V8GXpyoHmh\hVQTiwj_lWGFANPDWj}~zqchzinq|qjjn{^ZXSM\h|`{ebYnsmdzstxãwkio`Q=%XcTVumfzti{mYnsipxFQ3P`yu^yx}ut|bmov}tsmwziZyv]N8cg]h}̺yYjTcaab\pjW\agcUNc]ckf_txv|}yv|aVvhZsnaG0Tv{|n_|lvXg~v`Z^VL5BZo¦uPBhqQUed_Qky]M:]hgpvflkpqtXhz{{oRFP\^ioxi{fgyrdZuŦntdlcF8DVAd[{roZcdmlbYMRWO?OhW\ksyiPrh[gkYBN_NN@oyzanyi`bs}{MbkCD'9Tejftȵsb~v|z[SrjF99TYd}ruuuno{wqywtr~]~]`SC5BTmwzwurbggosn[Npomxžz||rhoS[P@>9/5l|~fm{nwqp_gCF==J\{{s|zWVjsyuhyur\v}sgiX-Clɞ~|igmyypr|rHg[=HP^fyxcoo\myydR[upG&,7?Qpºvx|~vqXXXYp|wjmt{sYX^YTLJE&_Ϊwnf[X~pp_wwoU79nTcgsq~qk}ne}]OYiqwhqemzhgZ6cQ.:F]uquolfusjnvnfeaDTzv`[|r[]WKMktwbs{lcmcNU}sppo}~nO~|t{m]sko^gl\>/:VW=;Rcr}ξycutxMbqDCTatpu{z}}bdsrmoxfPOPV]cuhcmfhhc{}vthwsjlov{hZ\XSF:EP2bqùawhnvm\BCaj[NQfkiwylwV]y}y|sZZ?9k`Xdtvv{yr`fln|{w{eRSMLSquZQ3@VYYi~zryļ¸{swvxsfji[SP^ZQ~x}y\VuklOOO?//Ixwsy|ycuirQe~UnYdf^YTVVA3:Kdx|~oabb\jqhGduvvyyc|`^^hmtzjTMJQSVhsxhxlkd]LPv~ru}uYPa{l\WTJDTHnj{tvul]JOfwktrmxyh[c{y`[ZUSUpaJK9Zj{{j\aejvssruvrqQPxUIC$,8=67M[TJDNbr«|byi^^o|{}}{}rlmmljv{qdWSUPSZWUw{phfvvn{vfX^x}lqhc\`c`ZNLOHBBF=4g}v|prkabjzzg_ciyzilY`y~hX\:Bja`o{x{znkdeeejcuwihfio}dOHFKhsU[K/DPU\`gfe~ȽʬŰzutkahbKqv\Vzwgq`PE>6/A`rxrowx}koooZJlytw~tw}]\vaT]d`adYJ90;HVkuux|u|zreiidz}]``^ehm{mvsznSPOi{w{t{|royw|d^tybndp~|jjbpog`RH::Q^^kcU?Rnywtnwy^OwD4J[lxrø|jfnUHOfn|ojX\jtzxuvozeblmOHRfysy}xnYIlucjoѹuF2IwjBNK3,P]NLTLc|}sjkvixYLk|TE\~wnrh]RVlwLQbdytu}s{pZSUPTuu`_ktuǜIJy_AS]hs^ZVafiXRTV_]Y}jVi`rXNY]m_LWix~}oMczxu_ddoikzkpcNlS\6DlJip~wgjyss|[K'1?Vmi£t^glaQzdb8lqo}SCeqQRWmvipYys]chz|shyrluN44Gaxww{^|gkw}x}ziN{uԯôŢyr\IDG8M̞_szwPFMUotWctidkceY7dTI6?q{lGLfdplYsuCRoMEXxnms\HOa}ŝ}aIMM= :|½i[UOPfkkYoadT`UGO^Xc{w^p\YRUntqo]b4QaWWig[q]{~Y\{˼v_Ch?@3)?hup~wtXi]BY{eBTlD8@OT>M{uSugi}yZtw}exV`PW[Ylvjd[`o||x{PpsǬzl`^^B3|sxv}{fXi^Xi~]W?>bFZLJSZo|t^lxvwjaqiC^{d>3Ndrlq|ht|o~jsvptZEG@EO3`gy_dhfM?xwp]obNN][I5[m`Yck{hr|nRW1Rm^|sYyeOt`lxɵy¤reF?f0=Oeon`iUNRVm{rVXUMGQWmM\suQplTd^ynt_tWPSleVeȫ~w}|wunfy{oC3_WAN}yXFt{_]UnoNR4jWD]ksuosubi|vZ~xpvy|[P[QR]fm~k{nbvsypxt~vťzoefgaVBFN>ws~lCwufXzvjO&5pmUKatoVQ{nyv|jz_ZcqS[6arNspPatu~ysnt}r]eѫ{sizoar_VuP=I\oǿxkB33Qcd`PJ[cmma>Qnisuolcfw{zY^vKVbL8Df̳p}c[dap|~stme}HuʻyjydL?4bs;,HfwͰ̼©I;N\S^ZPKJUUOP[RHu|{{zwx~b]wrVhoS7hzJV~yyIRgx|k|fW}qmfluZ4KQ+WuMR~_mrn_pth_kvT(DhQ[]K_en~Vmyfof|]pul^Fgk-[yZa|pytplw{Pbv~Ưwt{mposH?J0geBNj·`izrvpkj}~=@^EX`fa]guQaxl|nczTzibWNK1loU_pqyxgmYY|ni˟roomX\cUA#\_ZzwnzqYYnm~{pkqV@XKSZzcLXctSsti{pjvVWneV;e`_v{vjyeNxlfpmsi\xuK=JD7'[k\w¯p`Obr}iedaY@h^FJe{\isftWdq]f^NUXWGZecywRZ}xpUvn~yzvlzF@VT;(GdXƟqbNZmmj{pqbV/NXMP\lrus^potvs]eQY\OFe}dugPa{~xLlo~znnieXHOJ83Z|l̦lIQm|nfXZ@]_SKphqacrizdntqm_|[Ve^G9Rve}{^dzv|Psz}wunxuiclG=PN>/Pzu¬vdP_zlspnka9YZ]\`rjdtw~ep|dmcT`PIHlzk}vcb|uqxsX}vshguYgnF9XOC6fvz~cRYuq|fZ\MFW[G]u{]xijXahgS|i~|{moEvmJ[^]TIJL39GYlyvê\ofPtz~tV]oprhtqxfmvPXwP8?Ii^ulylzxubWSepvj|mrvsI0\sQ?NO:,PHIGɺépzxmpl\_np|xk[prr~l4hVIF@g|yh{~}~bmbVXkx{|zlimnoc]oJ\p@H8+-:Rjtp˱m_piomSAgspqqnt{onfld`kZaWQJCZRNbysw\W`mUgx`th}xcpi\Gsnnlw_5%JeVAH[ʴqmiZVUplo]v}xbKcW8AB>0;npwuҾ}tlorrhbagujn~kygX]ci]Rgga`WWATLKXM{ť~omi`djYXey}vnqrtm_e|}xyz|vo_eoL=;;.%,JWKZ~̱v|Ztjjz~hhmiqsvywpjlhsfav~{akmhhG]xMJVI, *L_UpvUu~S+F]Z`iuwuxotsNZ|pylbcyqpptpll`r}fkJNYXeB'(2K\Id}aӱpG[iavx}yxR\}vjrqjl{ec~f|Ŭfi_^p{vyUw۵yMQYDTeiZ??>[c?e5'`JapiVGo~SELdus}cGIswvlIrtkhdieqFymKGSZisaQzppʣ|{_[r\j}qWXUeizqCpmelep̰łyl~X{zxrt`ϭs_zhbwodI?;1``G]!HNQpUGsi==SzymL@ysvUS{qdc[[cyoj~J|tCjgktXZwxlƮqpZlyayygTXLah~bQjmubnco}áæ}qwTuy~toteȦ{ognm~qdF@<5b]K]'NMUlQFrg;;QzwmJA{ryVRwl_\XZh{qk~J{Joop{[Xuuo}±zzop`ok~ygPUOal{nPjo~~cgeq}˯ʱ{mSowlywneˮyvlqtjM@@8ZiKf9C[J}mUCgsF8CmwxnP;suwgFok\SWXwr|k}Kv~TixudRusvvxynlgsuxhOSO\rxwR~npzddes~϶̷xnQkthu~rqfɮyzu{wmOCDBdiPi;_lnnYDQ|ixcEfcNKWecrx[ku{wSZk}uq[vzxxyhksqc^zets{bYz}jqhrmyɼpz{wSNqw]grpyukgxYgsrvj~eHjV`ry~i[WlfJ8Mhhm]JIsqnwHXjWKVam`^z{}nx~`Oopn~yn}j\{{{tpvySsojxrpY`pguo~ls~{Ƴ}|ueFZv|i[oquypfnoQyuxpSXiTkt~xe\]x|^D:XfggTHY|jvkGbiTQ^mei_xtzxXVvks|}tq~_ex~wxuyUxknxumWc{klop{mwz¯ut|}z^D[txf\ootvlfrmT|~|tT\kViq|wfab|~aF:VdeeSI\{iukGbhSR`qij_{u{yXSrkp~|~ttaaw|{yUuoizywsZ[x|xhjwhpx|zstvvwaCQlujYhmoumiruWs~reSq\cmxzjedxlP;K`_cTJXxmnuKXl\Vdssf_ztyy[Ol~mk|z}ss~i]v|}Wrrgxzxt[Xsy|wgixdrz|wq}ussr]BPhqhXflntkjvuXt|weZt^dlwxjhh}mR=M^]`QK^yjpsLZn_]k{ri`}uwsVRm||il{xxqv~basy~Yymhu}zzoVZqrwmbnnh|r~xi|wprtxkQ?Vin`ZkkqqilyjY}{Zkq_hnzrllrfK@UZ]YLTowfzeMhn`ivitomz{xxxeM]s|tfsvxzpq{sWr{rdh{govz{~}bRdnppaf{axvzz}yk|xrp{zm[AKclgXdkkqghtrVnukcycgmyxopppUAO[[]OQi{jvsOaqcfrlk{izwxjQXpywgovtynnxuXg|m~qc~klq}y|~iS^jjmad}|cqyzwt}j|qnwxk[BJajgXahgmdeqoTh}prh~jjnyyrtuuZDPYY[OTmziwtQbtgjugg{m{ywgOZqyugputwllvpTiwklh}kmp|}x}gR^ffh^gycsy|qp{o~~smvuhXAKaifY`fei`cpjQexlylqmoy{txxy_HPYX[PVo|lwzV`ullvfbm}yvhQWnvvhmtqujitqUawh{eployy}~lTXdcg^ayzgk{yrgj{unpth\FE\eh\\eag`^lmTXvkwl}qpu~xz|nSLXW[USi}sofXsuoxq^pm|zyxmZPfsxnhroqmeou`Sqnjhv~mmp}z{|aR]_baZkv|avy~fmqux|rlsmaQ?N`fbWacce\bndNftgtxsr|~y~iPRYWZS[t}nw_`yvvkaxpyzshUUksvkiqlpifrrYYtinh{|onq|y|y_S^]`^[o|uv`wz{dnkxtzoiph\M?P`eaXabbc[doaPishļw{ur|{jRSYVZS\t|mw_azwxocyn~xy}qgTRhrujhqlnifssZWtljkxrmnyx{}dSZY[\Xk{q|_p}ijlmuy|ndifZN>J]ccX^b`d]cqiScxi{¸swsx|tZRYUYUXq}oqh]x~|wetlywsyoeUPeoslfolmjdpu_Vqnfnusmlxxz~fRXXXZVg}zn|^l~jg|ikru|madaVK>J\bbX^a_c]erhTeyjzżvzsy}u[UYTXU\t|msh_{vgwmxtqujaRRgosjgnilgftsZYtkhj{smmxw|y`QXTWVWn}rnr\s~ydk~}atj}ug_d\RF=Q^c_Xa_ab]kr`Toui{u~mWYWVYVi}wl~}_jmjtx|osod\N\lqqfjmusi|]^ygYaggpvzfQTY[dexkO[j}mn|}ylbR\mssbZZhxjisiUfzitcH|nCET^nws]\cckkz{clvyd_xprtwokalm[Wd~zhR`zovsfW}xiinluzpZW^^gj}ujbdfbkjdRRypk|pqrhbV^osqa^afwzw]i~nvp{XNz|vrkpt}kUTYY`bs}s~xysvykRJMXujmu]b~}ztwoprvngxXMmtslprpx|lVUXW_avy~xxcdzuvwyql`iy}|nlop}vo_nRElybG@B?FGXrshvjbzvh]kuxea}swvxoj\_nok[TWYkxvyyu}y_{tlqnuvr|}{~zwybSqo\u{o`[_WTJK^df[SVS]er~cb{siivjH_xzcTXV[`ixw}w}uh]hkh\LNO[ov|xmpow}fmsLOjonjrrs{|hTWVX_c|su{ruxcnvytwvokbm|~pppq|zl`oSDg|mRHHCIK\tsgrn]s|~seZgs~whaoxqumi^\kml`UYYguzu~cpnpnptzmo|xzz}v~r[h}dh~xj[][UQIVbfcVWWZfmr`qlsnhx[Opx`Y[X_ctvvx}}rk]VacaVNUYj~yynqpxvcnrNUovslrru~lYZXY`f}zoi{|yuygnzuutunmckz~tswx}zfYmbNmy_PPJMQ[twjnu[iwy|n^bnxxj^zozmpid[TaghbX`eqxy|mopnljor{h]mou|}vbgnf|~s`]]VTKSaef[[`anuycnuo~|kcxiVth]_Z`ftsw|~vrrg`TMZ\^XT_fw}yorpx}o`qqP\u{umsrw~kZ\Y[aj{yegwxwxsi{ztmhbluzvimnpypbt|ccvthmns~sep}^[wuz}qqxfOLZe}usgqxub\wynqnngd\fty|stwx}~xrTU_gywysd~}uph]bfxvwgywn{yb[y{qnrmnhp~}qwtewvprrxxzgkhn|}fYPZkf^k}l[o{~qdmwzjco{rtnmgk|~{~{xyu\jywtnx~sqrZp~{}}rqtmps{~ziYTiyie[YQWgmqifhdmuxbhyj[nzystq|sroYo{y|x}|vngnwswwv|i{shuvlx|nq{eTfryl_arWBXineflluxui[]b^eiwzr{iz}odnx~qaXh|zqpio||vv}qhy{}wzy|pf_fuxzx_jtyyh[OYguz{rbV]x~rsmmgl}||u|nqu_kv|urvXg{{xrnutz|koxzp]RWvpgdquyskkcfnwxabut^esv~}~yqurwvp}Z_u{rxwzpe`lpwsqufgv~ql~vjvz`Zjp}ymaksPF\jjaeilvzreX\__inwtvk|vg_ku}n_Wl~vslt}ws~sft{uxsxx|rh_crwglv{|i\NTan|xysbY\}vuuonkk{~xzlizzbcrxyr`^tw}|pvwy~kclr}|trbWUqxsjq{}~splgnti[hwh\kt{}~trus|{rvpWhyuptrx{zqgist}~ovsdivyps~g`mqYXjuzogrwUK`npdfikuyuk`cdclqvszjxwh\elx}vvk`_sxwqu|pvp|dj{ylmilptzsoo{|njv{qcSR]fuutrd]_||xuqonkx|zi_mvb^q|{uj^ryz|~yz{y|ziZ_cmqklc\_v{xoq}~zutmsylYasm[fs|vpsqw}ztuxZcuwmolrw}ztr{y~~ttuefq{xsy~~}smy{~xnbcafmq~~}vpodd{~up`X^dvtdfx~g`r~ultwxzu}zsnhg`lw}|~}{{ty~}r`_abkpz~}|y|nsx|t|zpchn}so~zgs~wmwsmuulkfigp|v}ryslnktw|spws]mzywknr|w{mhj~~yk[Whq{vjjefbgw}|rzrwwmoksx|x{tU_u}spsjmq{xo{lr~wjoz{x~ynsljpt}xui_a^dioyuxtkim~oS\p~ytyy~sosnrv{r}oq{rjbmzpktzxstqty~zss~hJUk~|legaehqxi`l~t]cnxocW^hvxkgrzystps|zszww{{uxtz}{z{bm~tuzxh`Zhxumst{licd`crw~{wxrxtenmqx}{uxtz}wsvpRYl}|{tmrov|}t~rtvhmx~z|zyvmqzw~{rjlgjmqzvwsigj{oQUhxvotv}phnls|t|rnx~pfZ`o~yqsyxwuw{|us}wSL]qzw{kkiiruveiyj^ho}rgYT_hy~}}ohi}zvvq{~xt}uuzwxzx~{{rbp}}ruyrd[Uetyt}xrnhhchuy|yyrynbmlpv{~~wuyv{~usvqRXk{|zxptqxvxt}vhis~}z}wzxqq{t{~ynpmlpqyyvvlikxz[Paszqsuzwknpq|vwmrwvj`Zgs~rwzyu{tvtqvtz}yy~y\cwtjkfkt|zr~zu|qdTORVjxzrc`eziixqqopsrp_xianv~ypuz~jq~ysqlu|zzz}~mktmihq||}uwyyzvwy}~l[bv}mmuy|yrmwyted~{tuwoacen{|qsnp{z~r|wg`ho~|y~zoicvxjj}~|ooqnss{r]hy}z}xmnllgXc~o`UHNWdqqvqjmxouvtw{{eYf~wzxmhrwb`puwwwigcj{uv|seajrxtw}yrllybcttkrvzpz}xvYQbwxxz}|v~uw~rcX\`lqqwuv~}~qll}}b^nymmqpwx}}w^etvrx|qt~~}kXLAHVhvvyslnzrz}{ttt{~sjuxy}|spwuwwwzxvVZn{~}}vvpwztzllr{~uighs}bS[ouggmqxsnfbigknqwruwu}fdq|twxyyrv}ock{u}xjYX_hvvzysw~~wvxpywmngdcdnq|nfp}zpqpx~sjibelqtgcryv}pfXX`jxy|{swvw|{vtswrdn{y}~|ulisv}on}{zsyqfn|~pv}n\Y]eolmh_ak}luyvnmjnoblsrv{~vpsoppquptx|nlx{|{y~ys~~xihmuztkigmiX_s|qostyrkc]cbfjmuqsts}c^jyzrvyxxqt{pgqts{l\Ybm|}~|tv{|~~wvlpmmnmrnnstumx|}sijehqv|nen}qhowypb]ciuvwwos{|x}vqnky~gjzvyzzumrtuzz{}zs{}xtacozqhum_acmqnphgn}vjt{tpklju~fcrwyzytihkgjjptrxynp}~~yxry~svuhjpz|wy{xqpmqrdl|qmnnrkf^Zcekorxssrpze`l}{~~}turvuiq~upywg`diuuuunqz~|rt}zmmlhiglljqr}sx}|urpksvztcgu|lou~pcVY]hqqwuu}w{|xqqovmdp}~}xpkrqux{rmw~}uuz{{j_hwuq|whafkvvtsklsrku|xuqrozijxxxw{|upffigjinqouv}mq{|w~ss~|zlkow~ywv~vrkjkoxfkzxppouqkd\bbbffmlmstnaiz}yxtqxyzm^fvyyj`dgsxvztu~v~tr}vob^a]abiqqz}tttnmgnswvfguwtw~qh[[`hsrusovqq}~|vvwzvhpwwv|yuqlssuxywmvzzutyxxh\fwy|lbefpqnoimvwow~yvsusxbdswyx}}vpdegdggnqrz|mp~}y~ouznbfjv~}|zps}|sqmqr}okw{wssuni`^caefkqnstwz__l~xxqvzzuccqzz}tgfjqzxzxu|yz{qz~{y|und]a^_`bkiov{{w{stnqwxvean}upszwm_]ahsrtspwsqzywqqrvxks{zy~zungnmoqs{z}woyyxpptrp_Yfy|z}nhjmvutsnt|pks{zwwxxrdl}xvrwrle]bbcgiqpsx{ypx||xzvq~xkfkozyyxsyrnvzxrqrs}nuwtpuplf`ggiklsoprr}jcl~yzux}}zico~ytxyjfimvtuur{|{xlu}{y}wpf_caabcigjns{v{{uw|{xieqxppttk^[_eqqsurzuqy~|wuttznv}y}ytnhnoptu|yy{{tlt{ztsxvsc\hvunqwupsv~|zzuzvpw~zwvs}wgn~zwswtnh`eeeggmkjmn{{r{||umwyy{}phknwxvxtyxmq}zvrnolwvx|uwvni`cfdhhmnkooxwfix|zxsyywm_futtvwklnuzvyvxuxqo{{usumg][`^abfjgkntwxzztx||whjxxrpuum`_ciqoqpowqpy~yxuv{ox~yuzwrmhopquv|yxzzwmt{{vtywsd\guypquxoqrzzwzv{sx{{w~yfiv|ysuvojabfcffjminpxqs}{mr{yzrffipwuwvu}nltwvrrpsw}xztogbhghklqnnpq|lcl~|{vuzyug_jy~ssutlnqyzxzw{uz~oo{zrpqicZY][]^afdinwzzy{~{ufft|usvwpd`dhqoprpxrlr{zwvwwpw}vywpkdikkoovwv{|}mpyvrmrqncXbsyy{{qqsz}y{xzpr|~~{jht~xqoqjf^\badeinlqu|ur|||nn}|}{sgcfjqoopov~mgmz~xxuuvxt{}vxuojchihkkqpotvseiy}|xuzxtg^iyzzz}rikmuwuyx}ux}jly~yponfaXX\Z]^chgotvw{|sekz|wty}rk`adkrorrt}mlt{|xyx}ws~{wysogekjlnpvrvy~okt~wvoprnhZ[jz|xx|vsux||}|xrx}|qhr|uoqlf`Z`_`bcjilrxzrz~~zliw~|{|uidfiqoopov~ldhu{uvtuvwxxzuoibghhkkrpqvxrgl}zzutxuqc_m|zyy}rlnpxwvyx{rwvhp}ztkkha\UZ[[^_ffhotu{|nhuzvt}xqgacfnnnqqytko|~|{zy~rz~wyvqkfklloousswysinyysruql]Zhw{tstwrux~~~vz}rhr~xprmhb\baaccjhiosvnu{mjw|wt~ysidfiqpprrznfiu|xyvuus}{}}ysmfklkmlrposuqgl|~~yw{wrd^ky~urp{~uoqs{{y}|~twykq~~wmmjc]VY[Z]\bcbimz}rx}qhs{wry{uldfgoqotszykku~~~{{w~vzzxwpmegkimlqspuw}jju{xvpsrmdX`o}vrou~tux}yw}nlw}tnpie_\bacdfkgkouqoxykm{{ut}vqfcehomnppzleiv|xzwwvt{~{uoinnmontrquwsgjy|x|xrf^jwwtpw|umpqx{y}}uu~|jlxzokjc_WW[Y\\_daglvrr~xkn|{tt}~uqfegkrpruwpjn{}||zy~vxytojdijjllqpotwsgkzyxtpsoj^Wcqvspy|uwy{~wosnkoqwsx|{xu~~~vpwqts|~yy|{{xvvuqv|vsuxwzuxt}{ynlmhopsux}z~~{~srpokoxuz~}~uvvnxyywsyrs~~{|yy|{zvwzwkrypt|}zvvqphlntz~{}|ssolriegmuz}~}~xtpwzvsqpskonty||vv}wnpuqpu{zzz~}}ry|xwz{rmprspkmkdbhot{|y|~tz{qrsz|{tmplrv}z|}{yuw}vrsqwft~yyyywx~}~|}s|nnw}}uninqrtrwzz~|w|vtspvy~wvssxxwpn~x}}}|ukjz}vsqpnv|}}|rpqtz~zwu{xu}{tqxwqjmssyy{z~|qvnijtv}usrqoyxuzvywvvvv}lwvlzuxuw}|}}}yv|zymokh|}rs{tnklqnx|~|xz|{sw~{|~||xpq|x{tutsx~vzz|xwpnv~y~yz{z}}{~{xwwmsvuxtqpwz}yrv}mvrpnluy{xttrrwqq~x|x|vv{yuorw}{o}utqrzz|x}~y{|v{rz|t|{xyztnnwptyxz{wx~wvvommzvqnvstxq~~~|tv~~{uw{z{uu|y|zxxy}|~wyqngpzzwxruzsrl}yuyxqvtv{vuy}|vqxxows~xrw{zxz{|}urkusy}y}xu{tv~}~{}sqywuq|{prtu}}w|yqrtq{yyy|zusvov|w~z{{}~yy{tpotx|w}~w{yrt|ytyv{}uyoz}sw~}~~z{s{v{{{vnsmpqrvq|tnzrtwsx}y~{sptz}{~}zrxzttuv{}|{|}tsliinp|}|yszxqt}}x}{ulhz~{}z~xowwrur~xpq|zuvz{~yz}w~~w{xqty||u|xnjkoypt~{ww{tqoqpqom}v}}~pz||xw}~yrnppkrz~yrpy||~~}vutu}~zx|}|smsurrwz~{|~y|~msrcedpzy{|{~wytvtxvuwwvogxw{z||}}wxyw|xz{vt{~~}wuriifimv}{tvy}y}w}zltstts|zxxx|zw|tzpoomtpqy}~}zy~}{wtvvxvuw~ssqqqiowztz{{~y{uloltupzx|x{zytwxz{xyuvypgkwxuzz{}z}z{zvozu~zz~tmmmmklx~z}|{|~zuqoklprr}z{uvs}}vwxxtroptrw|~y{vz{sqw}~||~|rptfjfmyx~|}y{}qrtrkkty~{{~wutw}x}qsrorlwx~{~|w|ysxt~|~~y|yy|~w{xljlpxvxz~~{uvxtpszzy|yr~|z|}{sjjojnrz~wz~|yy{sx}y}{xzqlglxrs{~}}~|~~|wwxkjlvyy~|swzyv~}yuvwwmnzt|{|yxzz{}ty~{y~{yrrhmklokt}}}wtmqzwxw}}{|zvxytslpsvxvuyz|~xwuvsu~}yt}|rvttqklsvz}tnnnxnv|y~ywzwzz}z|vwqigozxwz}xyv}y{z{z{{||zyvkmtpqlqxww{zzzytstssss~zyvzy}~{|~xurqqojxx}|||ywuz|wz}~}}~~wlohhnkutv~}|voy{xppwwyyz{z|{{~{nsjwwpz}z~{~}zuqruyryy}z|~|xxwsoononr|zwwwrsss{~w|~zxx}x}~vojmsomorz~~}uwry}{~~|~ty~|njklmmmlv}}{vzolunwyru||z|~}tmjiil|y||ursxvr~{v{x}}{}qlhghqtruvzwt|prwsotw~}wzstuy|zoqsrtwrqvz||~|urxwystww||{|t}~{ywtlixow}~|~rxsuqm}vw{|~{|wx}tmpmksrmry|~}|{prx~{~ysosw~{xrkjgilqrv~~yvjuvpqsyvwrsy~|~uvvqoouzyy~z||truyxu|u}zx~{~||wpkanpjxn{}yv{rtuwy}~|}}wqefgjswxw~~yytrruz~{x}vvvy{v~vvpknsrjnoy{xw|luxqprz}xz|zz}z|yqidltwtvs{|{zsosvz}y{}|{}zqlgsjlu}|wquwtv}zuz}to|{|vxtvvpiiz~}~~yttw}~~wrvvw{~z|{wsphjlinnt}}w{umqy}~zwww{|zxxqmjqtsxzz}~z~|xutts{{ttnw}~|uhpsjogo}z|y}~vruxu}~~y}{x~y}yxutrilssvu|}v{zwsrz~yx{~~{x{|ztrspilmjvvt||{xpnopxz|~qx{}yknwvrlouy~}}vpovr{~}|wx~~~zwthpnelnqmv~~}{tqrzuv~{~}{||rmhn|{yvw{z~tspxux|}}{wvulvpmohmyxz||wqxy}}~||~~|yw||xyvqokmrw|zx~}{|x}tm||wqu}xprskljluvxuz|}|{ytzsjmtvwz{}|wrsrwy|z}xx|{}{~y}}x}}|{z~|~{tlqsijolltpt|pvxz{}{zyv{wxypkvynktvz~}uxiefltw|}}zvny~zwy{|ukstmnuvuyz~{~||z}~wx|wmx}vvvzzy}xy}zrxzqr~~|zx|sruxyz{yytv}sz|pwyv||y|yz|t|yw{yy{xpz~w{vsvuilqkiu}~}yz}wxrhnsutw}w|{}{srlktllstyyv~wywv~{x~zx~ypoqimssy}zz|zyv}}zx~}|~|~w{~}ttppypkniky~kipokpoinskw}|~wolprinqos~~|{}||v{wwupvz|x}~}zxy}|{yz~}~plopt{{zy}x|~|uz|v~~~zvvpo{xsrvtu~|oswtu|zw~~v}|wqtwqolnu}|{qpqqvxz{vsyzvt~y|}|rlroipu||~wt|zwy~w~}v}u}~|rwxw}|{y}psssyzrutnn}{vmswsrldeojlqx|}zuwwzwmsvusyxz|~~zzypv{{wv|z|}xvvvy|u|{~x|onrtxy~}y{~ytz}w{}zxxto|t|sxutx{uy|z|zvv~zvxyz|toywvonx}}{zxxxqttu}z~~wvv~r~{tmmslkou}~w~|x{vy}w~|yz{{~|ruwuyzzssrsu|vtuqjxz~suyzzrjfmokov|y}{~xyw||ptxvsrut~~~|w}pz{}y~vszy|}{{ttvuxsu|yytopru}~zz{w|~~xw~zy~~|zywoq}uwwxzwyz{}~xvx~wuwyyyov{wwosy~vvt|vutvw|}}tz}y{u|}|}smmrkjpv|{}xyvz|y{w{|z}zpuuvz{|}~tqnpqyvrspjv{ww{~wojmqmnv|x|z|{zw{ttzvuqss{~~w|syzy}yqxxz~{xy{squvvvt|zw{sqptw}~w}z{~y{zz}}{|z}~xxvov~vv{zz}|~~ywx}vuyzyvqz{yysux~vuvyvsvw{}}u{}{|w~}||rlkojiqx}}zxwz|z~||zx}zzzosvv}{|{~~tpjllqupqsot}x{}~tplrpnu|zz|yzww~{szyxvtwz}{xzs}}z}uuzx~yvswsosyyyxzz|wtpsu{|}zz{x}|x}{x~zzy~{vxx{|~yyzvu|{y|y~}{yw|yuy|zvrv|yyxsux~}wz~{xsuv{{|}xxywy{||rlhmjir|}yz{}z~~|{|v~{xzqrww}{}~yz|zqlgijprnstsy}y|}|uqotpqy~{|{yyvw~zv|yzxw{~~zzxw~{{|twyy~}~~{wssvprw{{x}{}{vrptv~|{~|y~}y{~}y{|x~yxx{|tuuwz{yy{wv}{z~|~}|x|zvz}}wsx|zwztsv~x{~{ysuvzz||yzyxwzy~zsninjlu}{}|}z}}|{{v~zxzrtwx{}{||y{}uojgijqnnsrt}|}}zwqttqy}~}{{xvzxz|xzwz|}}z~t~z}xuzx~{~{}xurvqpvy{vy~xuptt{|y}{z}z|x|z~x}~~zxyy}tstuyy~zwyutx{~{}}w|~zty||w{yrtz~~y{~}{uuwyz{}z{|x}wyx{|tqjnlktz}~z}||{t~~wzvqwv}{{}{y|y{{{pmgihmplqqrw}|}{wtxtv~~{~|xw|x|zyzx|}}||}u~z~wuyxz}z~{xtrvorwyzuzxuqtt{{x}zz~y{x~|{~x~}}~}{ywyw|trssww}yuxtux|}}}x||uz|}xy|ssw~|{{xuxw~|z~z{{{yzuxv{vrmlokrz~{}~}{|zv~{x{stwx}y}|}xx{|y{y~unjfhhonlrqv}}~}xwzuz~{{xw}y}{z{z~~||}v{~wuyx~}x|x|zwtrvpsy{|w}yvrutz{w|zy~x~v|}{z||{~|~}xxuwuxwoqpuuz|vyxw{|}|zxw||{v|wrt{~}{vww{~x||zzx}~svtvwsplpnp{{}}z{~~s|yzxrxy~}zy~~~yzuw}}{wywzznkfgglqmruw~}~~|w{xx~{yxz{|{}|~||u|}ztxw{~vzwyzwvsxtt{}z}zxsttx{uzyy~wuz~}~{z|~{y|z{{vvtvswunpptu{|v{yy}~{}~vy||yw|usu}}zwxw}|x}z|z~x~wqurvusoosow|}~zz{wv||y|tvz{|}w}}~x{|uux~}zwwxw{qlhghjqnpvv}~z}{y{zxy|{|}}{}v~~|tyy{~}uywxzxwtxuu|}~z}{xtutyzuyxzw}uy~|~|z}~zx}~x{xuttusxsopqtu|xvyvy~~|{x}|}wz{ttw}yyxzy{}{{y{|qtrtvsqnsqs}}~~~{y{{s{~~z|wtyz}~x{}}zx}vtu}}~zxwyw{umjghhonmssy}~{}}zx~~y~|{||~x|~vxzz{{{|~|}{}{~yzxyz~{x~~{~~{|}|~~|{ws}~}|||}wyyrxw{}zz|{|zzvr|xu~~~xxtz}x{~{z|xzzw}|}|zw}wxxvts{uxywzzzv}w}w~~}|x}{uxsv}}z~~zv}{y~uy}}zx~wt||}x}wry~w~|ywtqrzuxyvxxxz}xw}ws}zu~wyxvrsqos}}{y~{~tx}zzy|zxx|{rw|{zq|}{}{|yv{|xss{w{~z{|y{yw}}wy~zszyzxuywuw~|y~|vxnpvrqx}v~|z||uw}}{t~}}u~yrvrmksqs{xy|}}~y}}}}~~ylz|u}|v}}zy~}x{sqyxtwz|{ytywz|y|sqx~}zu}x}tyyrmstrzzx{~zu{ysu~{~~|oxtz~xouxts{~}}~y|vs{{wz~}v{z||~vpw|{vpv~y|||~vw||uqvxu}~{~}y}|vw}{s{x|sy{wv~zzzztwqmturu}|}|x}}}yqz~~wry{}}~w~zqvwplqtq{}{|~y{{}|yo|}vw}}yz|||{wzrqwvuy{}z{swux~zy}roy}}ws}}{{szwqotus}{zzx{vtx~|wqyw~xrywtx|}~z{{tuzxy}zv{x}{|}nqy}xqq|zx|{z}~{|yv}wssyvy}|||vw|{}uzwwx|vw}zzzyuxrortsw}~|x{|z}zpy|wqy||~|~z||quwporvs}{~~{x|}}{yvrzyy{x}z||yzyrsvux}~~~}ztxty{x{}{nrz~{tv~~~wuztrrxvy{{ywsv|~|}xr|~uvxytw~y|}{{|vtwxy~}v{xx~y{~roy{}sq{|zzzy}}}{}~u{yuuy{y|zvw{~}|wxx~|xyxrn{z|~w|yzzvz}xyvw{~w~}}v|v~wxzyyv~~||||{v~{}vvvy{|}yuv~zw{}y|}{zxz~zxxptxy|{yu{~uov~|}zzzxty~yvtrz{~~y|x~{vwxy{xusovzu~{}xqntxxrnv~~wqtrwx~z|xvy}~~~qqx~|yvy~z}|~{{z{~|zurp~{}|z}vswsqtnhpykozqu{x|vxvwz~xy}{zxvwvwy~zx|t|umy}|~~|v}|mw}q~z|{ytqtsposvssqsw|{y|}~{tsty{zxx~yu{{v{y~z~yyuu{w{yv~{|}}xyzsxrw||}}|wvoqzqrrhv}{yw~~y|}ssuw{yv|~}z|{~xst|~uxvy|yx}{|x|syoo~}v~x|{rw}vmrntss~~{u||~xx~u~vxstuw{}}yy{usz|~|}~xyttvv}}|{zquvixso{prouwyzyutspvzzy~|t{|}yrs}|{{wq~|{t{~{{}{uw~rwywwx||{|{xwuqyqtqktz{}xwzx~~}utq}v~vzv{~{yuxyy|yx|{{|}z|~xyqp|z~~~~ss}tvmozuwpw{~}z|vzxrw~xx}s~yvy}}{|w~ysspu||x|}}}{z{p~zvwy~zxytyruutqzv|~|v{xqzr{u|wwwzt}}|w}}||tz~}{v~|x}{zy{vvww~|}uturwpmnqsz}}xz|y{sp|y}xv{|wty~x~~|}{t}y~~}}~}{y~yz|yupltv}xzx{mp{w}{yur~|~|xpxzwyx|zx}}~vv}{z||}{~~|}}x|zv{~z|v~{||}yw}|xwunvss}w|v||x~wvqsxyvp|~vy}~yz}w{{~|pfz}y|yw~{y}||~xwuzz{wxyxsxyu}y}z|{|y~|zu|{wp}~~~v||teq}}~vy{z|uttry{zy{tx{}|zz}|rwx{z~~x||~|{|{wytwxv}z|x{{x{{qrvrjy}~wz||woswrsuqpqluz{}zzz}vuyx}~z}{v}}{}w}u{|}vyww~{x|{~}}vjjsrl~yzzxtvumssnmolryz|~w}|zxssw|}{zu}{{txzw}vzyw~xx~}z}{~xkgpsl{zywyrssqwsmnqmqwu|y{yxurt~~z{w~~||vz|y}zu{zxxw~{x~z||ohpsl|}}y}{{yttvuwtoptqqttz}z{{zsv{}||~~~x{|~}zy}}|ytxzx}{v{y{}~~tjqvov|}~xzwsuuttopuurstwz}{qsx}|~{|{y{{|~{|}yt~|~ztxzx{}xzz}~~}tlrxnuz||x}~trutstoquwtuux~w~{xrtz~{|~{|{y|{||~yz|xt}{vyzwzy|x|~~zrktxox}x~}{|{utvtvtoptvuvxy{v~|zsux|~||~x}|}}~{}~xt}yy~xz|zz{xwwyvowry|{~}}uozxq}ww~xy}yxz{z{{|yz}}vrv|}}|{zv{y}}}}wwxtv|yz{|~{|xy|x~|}op~zxux|~~{y||xwvz{}}yz|~~{yx|w~um}xysnuvuxzxsv}~|v|xx~{~~}~rwxqw|yyx}}~{z|~}}~|y{~yx{sp~}v}}owywtkmtt{{~~{vwyux~~{v}~{wxpxynpwurw|}xxxy}yu}~z~z}zyz}t|v}}}}us{}w~~sv~}xrtzw{~~w{{vx|{}yxzy|~ztxy}{zzyovou}}|}xwvr}z~yx}~|zxrssyyxzuumkunswvr}{uuuyrijp~x|z}x~~}swq~w{zxyxw|}yx~{w}z~|snu~}vw}xrxv~qksnfsxtxur|y|{|{~{zyrjosv~wvx~vs|{~y}y~~}~xonqot{wzwz{lgrqwqx{{}{|xsxx}vx~}~xuxuzyw~|x}~xtvzzzvy{|}}{y}}y~{}stnxojhggpy}yrx}svzvxx{{{y||wliy~{~|z|tz}zy}|}r}}~~z}{{vpzqu|~~wotromcn~{~}w|rzpsplorvx~z~|}{~yywy~|~~|u}~~nswyyvzx}wx~zwnlhnjjwnsv}{~|x~{~{yrv~|{vwwvy~}}yxty}yuz~|}~zxy|y~~|~~}~x|qvkkqzvv|wpxxx|}~~xuw~xyurvtzw{ty~{t}}{|yq~}|z{|}wwvssvxwy~wvqxvmtpfp|z~}||~z~{}}~w}wzzq|{~}t}~t{x|~{y|uxzt{z{{y~ysnx{}zvvxojkhlirqosy~x~~{~v||}rq}{z{{uv|szx|yw|z~~~|xxtpspujqz|~{{vzvwqstqx~vzxr{{vtqsyoswozuxvy||~{y{z}}}}wr~~u~{spxyrz}vyspln{{x{trouwswsy~||~~xzvsuzwxzyx{}zz~zyxutw|z{~}|yywnpuw}wxuwwt~y|{{voy}{v|{~z{|v||sqoq{yywy}v~{~vy~{|z~~|~{tyunoppkssx|~vtyryuiyuy}up~|}z~zqssv{ww|~}|}wyy|{v}~|zv~~{~{~|}tutsxvwwx}vrx|rooyxqwrtqvyuz|z}~yy{zyury}~{xy}|z~|x{w~~zw~|us}~ztv|plomty{yyzt{usts{yzywzuy|~}xz}wor|vvy|~vv}{y}}z~}}|s~||{tu~~~{|svplzzzyywuyv{ynuqu{zwtw{|zy}ytussr|w{x}}z{w|~|}z~z~{rmxxsvyxz{{xz}{utqoy|}tvwyunoqqr{|zzw|{|zz}zs~}}~~|~|{vurxymsvqu{~~|xtsyvqx~}}yqz}z{}xnnq}{|~uxy}|x{~}}{{~|zzyy~{z~zzy}~u|~xvyxsvysw|~|ts|xos|{x{~~{{{y|}~|~xv||wvuuyx{|{}w|{z|}zwz{~{z}}ywy}xy|{vromzus|xqsw}}|~}}}{}~}zwxtvz{|v}}}~|}xy}y|~|{~{}xuv}~xtqrtw~zwqptqty}~vzyz}xz}}yz}|{rvyrn~z{x{~~~x}|w|}|{{}~~{}w||wxvxvuv{zuw~yqnrs~|ywt}w{~~zyvzyttvruzz{}z{xz||z{{x{}{{yy}xvxzvpr}yz}vxy~zvyyyy}zx~xz~~~|zwutns~||}}xz~xxzz{~zwtvssy}~ytsyuqr{~{}}{y}yx}yu~}~~~}zzw||yxvrpy}{|~}~~x~~ztux|~xsz~|{|~{y}~yyttutuw}}}}{~~zvyzxuy}wz|xyvsvxy{~~}}}~|}||~|~}~}~}{~{~z~vv{~zxsoqw}y{zuuz}|vw||{zy|y|~~~ztstoorxy|xuz{}~|{~}|zxwvsxxzwv{{ywwrtvvx{}|}zxtw|}{{~|tt|{xrvvtwy~}|w}~~|z~zx~~}{yyxzzyzz~~}zxvquyzzw~}|~~~zwxz~}~}}{|w{rqrw{~}zx{|y~}~xz{{zx|{}xvuvww~~~wuzwnn}{yz}zwy{{~}~{~zuwtppqsx~||~{|~~~}{~|zu{{{~~}|}yvyut{{~}xvv|~{uvxvx{zwuxxy{}|||}zxywxyvstwtz~}|~~~}z{}|usy~wz|xw||}zxqqrpw~{x|{x|x{~zzz~}zvrqqusw}{{}{}xz{}w}xzzy{wwvwvw{x|}{xtptzy{~}|zwv~}yz~}}~|y|vsqont~}z||~}}wyz}{|y{||{|yw|xxyz|{wwuuxzyz~|}~}zwxyvz{}vttyzuqv|xtx|}|y{}~|{~~y}|w~}|wqsux~zuvvvw{|z{wz~|}z|}}|zz{~}|yxxwvsrquusz|~~~~~|yvz~~}z}}xtrqvxwyyywxvt|}wxztvw}~}|{|y{|~}}}z|zuvtsswyz~~~}yyyx{}~}}|yxxww}|vst|yx~zusuwy~~~}{{zuvz~|{~~}vpooty~~~~y{z}~|zw}||yz{yqp}}xwy||~xvxvuvxw{}}}}~xw~~{~|}|vuwyuywtuz~~y~~}{|zvvvut~~~zovsxy}|{~{zyrsuwr||vrw~rput{~~~}wmvvruy}swsv}~~~}{ywp{~xxy{usnoqoxx{utuvvytowxuy~yy}yupty{}}}~uypszskjp~xvsuz{wuxyyz~~x}~z~{~{|yvpvwtx|~{~u{tz~yxsz|}{yvvoz~vz|popxzqphrpqtw}yxz{vru~}}wzt~|zopw~|~}|~xz|{zvvttuy}|{}z{yn{{|wzuqlkmou|~usy{|wzx}wwvx}z}z}uuulsqqs{|{}xz~{~~}xyuwtttsqqw~|trinpkmsqpw|x|z~~z{~}|vz{yztwy~xxxvvyss}y|~~zy~wvxqy~z{yxniq{yvuuny||~{~|yy~|}tvytz{{{}z~|wzxy|~~~z}xnv~~|||rvxu||zszr{yz~ytrsqioijir|vqwun|}x|z~yz|{}|{xwt{ssqnoy}z}}~|{}|~x~}}~wru{zqprjeqw|pvuy}z|wvv~{z{|ywwz}}zxy~~~}{w~wvwpzy}~yxwvx~}vxzvlhwv{uswu}}{y|y}t{}~~z}}~qx}}~xz|~{}x|{rqluuuxr{||x}rs||z~y}{~twtqrjokiiozvuxp||~~~|y}}~y}zx~}~{yx}z~pr{|}|}yzw}||zwvrtksy}~{zwyotynqjjv{~vxwt|y{~x{{}wwwz}{}~}|uvyt{wpz~{xy}}ty|vyzx}~~~zywv}womgilkojrs}x{x~z~yy|}{}xx~z}wuz}rtsrvtzwz|}~~~wlixuuyz}z}{~{}~nsps|{}x~}~{~yxxv~zzvoxwuzr{zz~}r{|s||xzw|ytsmnvnsuw}~|~vwvtwztu{|{wz}w}|}wtnxuxy}}yz}zv{zs~~sw{z|}xyzuppvlvz}z}~xvznpyvszz}}x|}v}~~ty~ytwpp{w{}||{w|zzuzy{vy~z}u{woqnrt{}~{~xuzppuyuyw~~uz~sz||yt|sutrpxsxxy|~{zwuw~u~z||wz|wolooqu~}wwuqpy||zw~{uw~y|~x{o|xvttrtxv}~~v|{w~{y~tw{|z~snoqqtwx}{~zxpqsvwwtxuw~x{v~~vp~utwyvvuw|zz~~uyzs{~r|{||ytqkjpwu}~~xsvqtosyw~~~}uqyxs|wsosrtt{zyz|w~{v{{nz~}{xwzxtmjlqxv~w}trtu{tvuz|w}{ws{vw~zqontzx}{|{|u}z{||v}|{}|z{wqljipx{~torsvssv}|y}~{~xt}~pu|{wvuwwt|{|{{{v~x|}t{}{wzyy{qmijqsw~~ytttwrrx{zw}{}}|wty|yu}zysuxxz{|{yy{{w~ryz{~sy~}w}tpjmpsv{||}stqxupwz|vu||}|||s|}|r|}vswz{y~{z~xv}xzzz~w|}v~qrhkqv{}}|~z}qvqvwsx~~wtz~}}r|zzyo{}vux{{z|zy|zzz|~xyz}}v|~svtiiquy}~~|~vwpuwtw}yx|~}}}~}yyqyy~yn~vw{~~}{~{yz{|w~y~|~}ywxy{u}vr{vvqyxzuvxy|{rr|yz~~}{vqmqz}r{~vyzv}yzx~z{vuny{utxu}}ty~{rqkkmnl{~{wsruy|uwz{z{yz{}rztuzvywwzzw|~~w{y~zy{~~~~z~wryz~~~zwqus~|z|~wwvvwxwwzvqwzz|~mkxy{~{vwtyy~xo{~}~zqqw}yuuwz|xxysuolux}|x}~}wzv~}~}uxpywklttw}}xvvvuv~|x~y~|v~x~}||z{zvu}yv{zouzwnsmz}z~||~vzyyx||y~{~ss|wpw|yv~z~{yuuxu{}|{||xrtxxw}|z{~u||{|zxvrsvt{~|{{kw|z}~y}yyu~}|vswuutzvwzw~vxy}{}tpy}pzuw||xsvk{|utxvvyuoovsuy{|{~~xz~~~z{yxw|w}t|ztyx{qz}x|w}y|}}yzuur{uu}{|y~zu|w}~}{{{qq{~||y}yy~xuvvy{~x~z~{x|y~vr|}{{yuu}~z~{v~|uzy|~{}||zswxxttu|yysssynnpv~w|y}~|tmz{xwuxxoswmvvtsy||z|xy~z~~~~}ysv~y~|}ppxy|}}~~}rwutwrjw~}{{yxtvovst{z~vyvxw~x~x|s{|u}pstuurx}}ytsvwu{swy{}{~}wtryx{y}|uw|{z~ww|{y}z{}|~ppqzuzxzxvwpzz|~||z~x{|{ux}zptqwu{wmtrtnopu{|}~tnw||zyrtvr{~{{xz~yw||~{xyzvux~}|}yoy{tqwt~{tz~}}u|yy~tvst~zy{sqousuz}{{wzyzwo{{||z}|z||}|rtq}x|}y}w~uxz{|zuzy|{wrfnur|{{||~yx|}z~}|syyt}y}zuy||{{|yvy}||s{}y|~|}y}u{~x~|~zwsplmr{}x~wkkux|z}s{vv~~wvqor}s}~}||{~rrqx~|~y|{ry}}y~rt||zv|s}~ywy{x|}~zwu||y{zouzwqrtvx}~~z{||{}|vrqjotswtvrrs||{wt|vwwy{||txywtpt|}}{vwx}{zzz{y~|}xxxzont~z~w{ux}~|y}|}~||rz{x|uursuzzu~}|~~{|twrvwwuonfkpry{qpz|tw|x~~~vv|}vuuzsywqyzy~~{~xwxusurx~~~ty|~{{{{|z{}zzvz}xos||~{zsuzzyz{xt{zz{}xvtvut{qnlmuy|vqw|qqysy|}wty}}zqpu|}|y}rt}|y}z}~zxxwqyz}}wu|}~{}|{ywz|}~}}|~~}zyxx{urouz||z|v{||y}zzzsyuxqkjinnyxspw|xr}wxwzyxwwzwtvypm|{x}~~wxsvvt}w}}u{x}|}{|zvvzox}~}zvvzx|xwwvyw~ux~}{uxxuzusmosx}{xw|~xq}vost~vzvtty|xu{~rqyw|{||wrvuz}~zv}|{xss{z}}~|}}xx}w~|ttwz|xzyw{{{tw~~~ysvwvytnjosw~{vx~}wvtw|~y{zywy~yusz{qnyywy}~|xstvx}{xv}~xvzy||}|}{}wuwv{}ww{|~wwuz|~|tv{}}zuuxtxvpnqvy{|zq|~ttt{|}zzzwvv{~vttzyr}}{yz|}zwyttvx}|zx~}|{xtyz}|{}{vuwy}uuyy}~yxwzw{|uw{}~}xwxrtqomqux}y{|vxyy~}|}}yvu}}}vttx~tr}~{y~~||wvw{||~yx~~yw|{~~}||z~|wstw|w}vz||~zwvxyx{wz||||yvyvuurqsw{}{wxzsrw}|{zwyxssv}|u||z|}}{zxvy}}z}}{wuyz|~y}{|{~~|vtx~~w|~}wxz}~ywvzz~~zwz}zxzxtvuqrvy{}{xvy}|~~|zyy}~~{usv{|tv}}|y~|zxyz}wuy}yvz{{~~~}x~zz{}ytsw||vz|z{|~yxx{||z|~}}zwzzvxwtvz{{{zxtw|~}zyx|}|urrx~}vz~{y~{yyzy|~ywz}|wtvxy~}x~zz{}zvtx~~vy~}zzz|~~yxy||~|~~|y{zvvvuuy|{~||xx{~~zxw{|~}wttx~ww}}zw{|z|{}}yvx||xy{z|~~~x||y~|}~~{vtvy|vu|}{~}{|}zz|}~~~}|zz{xvvvw{}||ywuw{~~|zww{}ytrv{~{w}zz|zz|}}yyz~}yxxyz~|x}x{}|{~~~~yutxzysv}|{|||{xy|~~~}}~|xvvtvy}{~|zwy|{zxx{}|wtsx}~zx~zz}}~~{xyzzyzz{~~zyy}~|{{~~}~~{vsrwywrv}}||{|{zz|~||}|yxxxz}|~{yvy|}{zy{||wtsx~}z||}{{|~~yyy}~zwwxy|}yyz~|}|~}{|~||}~|wtrwzytu|~|}|~}z{}~~~~{yyxy|}~}|xx{}zyxy{{vtsw~~z~|z|{~|xwy}|yyyz}zw}y{~~{{{}}yz|||~~{vsrwyxst|}|~}~|}~~~~|yxy{~}~||xx|~|zxxz||yustz}{}}}zz}~{yyy~{yxy{}{zx|~|z{|{z{|}{{|{zwtrtyzwsx}~}~||~~{yxy||~{xy~~~{zxwy|}zvttx{~{~~|~}yxx|~zyyz||x}{z~}{z{~~{xyz}~|{|}}yusswyvrt{}|~~~}}|zyz|}~}{xx|~~|zyyz|}{wttv}|}|}~|}|zy{~{zyz||y|zy{yz{}|xyz|}zz{|{xtrsxzwru|}|~}~}{z{}~|xx}~~|zyxy|~{vttv}}y{~{}|~{zz}{zyz|{w{zy|yyz|~{xy{}}zzz{zxusswyvrt{}|~~{z{}}yy}|zxxy|~~{vstv}~zzz{}}~|{}|zyz|~|xz|x|~|yxy{~~|yxz{}~~}{yyzyxusswzxtsy|}~~||}{x{~{ywxy||xutuy{x||y}}|z{~~{zz{}~yx}yz~{xxy|~|zwxy{~~~|zyzzzwutuz{xsu|~|}||~zx{~~}zxwxz}}zvssuz~zy}|z~}}||~|zz|~}yy}~yz}ywwy{~}zxxz{}}|zxxxxwuttv{|xtw~}~}}~{y|~~~~zxwwy|~~{wuts|~}{}zxxz{|}}~}{yvvw{~||}~~ytrvzyttz||}}~}yxxxyxwusrrrrqpqsx{yuv}}~~~}}~{|}~|z|{wutuwz}{uppv{x{|~~|{{~~~~}}~yuw}~ytpooqtwyyxvtstuwwwvtttttttuw||xy~{z}~~|{}}xrnmqy|}|uruz~|ywvwy|~~|yyz}~|{{|~}yz~}xy~~zxxz}}zwwxz}~}{ywwxz{{{zyyyzzyz|~~{xwy|zyz|~~{xx|~yw{}xttx|~}}}}||}{xwwxy{}}{yvsqrv|~{||x{~}yutvy}|wuttvwvtrpooqrrrqpopppppruz|yuw~}~~{zz}~~}|||}}|zxtqoosz{y||yvvwy|~|zxxz}}{{|}~zy|zwz}ywwxz~}zyyz|~~}{yxyz{{{zyyyyzyz|~{xuvy}}~|yx|zwz}}yutwz}}|{{{{{|~{xwwxy{}}|zwtqqsy~}|}y{zvsstw|}zyyzzywurqqqrrrqppppppprvz|yuw}||~{yxz~~}|||}~}{xuqnnpv}}|zxy{}}zxxy|}|{|}{y{{xy~~{wvvwz}}{z{}~|zyyyz{{zzyyyyyz||xutvz{y{}xx{~|wuux{}~~|{zzzzz|~}zwvwxz{}|{yvsqruz}~~{x{~~zvsrsvz}||}}|zwussssssrqpppppqsw{|yvw}|{~}zxwy}~|zzzz{{zxuroopu||zz{}~|zyy{~~|||}~|yz~}yw{|xvuvw{}||}~~{ywsponmmnopsw||xvx}}||zxyz}~~~|zxwvvwx{~}zxwxy{|}}|zxussvz}|yy~|~|xx{~}~~~{~}yxwwxz}~|yvuv|}yxxyz{{{zzyyxwxy|}yy}~{}~zz{vx|~{ww{~~{xwy{}}|{yxwwwyz}~{yxxz{}~~}{yxx{~|z|{vv|~zuqoqw~zvtrqponnmmmnorvyzwtuz~~zxy}}}}~~~||~~}}}~yvwzyusv}zwvvwz{||{yxxxy{}~{y{zz~~{zyy{|||{yvtstw|}{zzyxxwvvvvwy}}}||~|xy||yvttvwxxwvutsttvx{~|ywvvwy{}~~~|zyy{~zvw{}zwv{~~~}}{xurppsx~~|zxvtrqponopswzzvsuz~}yvx{}~||}~~~}}~~~}}}~}wvxzxtsw}zwvwxz{|{zyyyy{}{yy}|z||zxxyyz{{ywuttuy}~}|{zyxwwvwwy|}~~|{|~||~}yvvwxyxwvutsssuwz}~~|ywvvwxz|}}}|zyz{~zww{}{wvy~}||||{yvsqqrvz}{xvtsrqqrux{zwtuy}|xuvy|~}|{|~~~~~}~~}||}~}wvx{yurtz|zxxy{|||{zyyz{}~~{yy|}z{}zxwxyyzzzxvutuwz~~}{zyxwwwwy|}}~~|||~~~{xxxyyxwvutsstvx{}~~}{xwvvwy{|}}|{zz{}}yvx|}zwvy~|{{{{zywusrqsvz~}zxwutsstvz|{xtuz}}xuux{}}|{|~~~}~~~|zyxyz|yuvyyupptyywy{~|yusuy{zvuy~~{xwwwxxxxvusrrsvz}|uu~spv|}{zz{}~y}}yxyz{{{zyxwwxyz|~~~|ywuuuvxz|}~~}}}{xz}}yy|}{vuwy{}~~~~~}}{zyyxxwvtsssvy~{yxz|}|zwwx{~}zww{{wuwy|}|vw~}{zxwutsssuwy|~~}{zyyz|~~}}}|wsuxzyvvy}{{|}}}||{||}~|{}~~~{wuuux{~|xwz~}yutux{}~~|zyz{|~~~||~}|{zxwvvvwy{}~|{{{|~}||}~}{{z{|}~{yx{|z}{xvuuuuuutttuwz}~|{zzz|}}}}}~zxz~~zwwy|~{|~}yx|}{xwuuuvxz|}~}|zxwwxyz{|}||{{|}|wttx{{xvx}~~}}~||~~~~}zwuuuwz}~zy|~yutuxz|~~|zyxxz|~~|}}|zyxwwxz|~|{{|}}||}~}||}}~~}{xwy}|yy}|xvttttuuuuuuvx{~}{zzzz|}~~zz~|ywx{~}{|~}yvx~|zxwwwxz|}}}{yxvvvwxzz{{zzz{}|xtuy||yvwz~}~{ywvvwy{}~~|zz|}~~~}}|{{zywvuttuwz|~~}|{{zzzyxwwz~yz|}}}|{{{|}}{zzz|}}{||yy~|zwvw{}zz}}zxxxxxyyyy{}yvy|wuuvy|zy{~|xuuwz}~}xvw{|vtw}|zyxxyz|~~|{{{|}~}}{uonrz~|{{{||{{zz{{||}|||{||}~||~~~~~}|zzz}~{ywvvvwy{~~zursux{}}||{{{{}|wrnkmpstrommp{|y{}~}}~|zxwwwxz|}~~~~~}{|}~~}|||}z~}{{|~}zxwz}|ywwvwxxxxwutsrrqnlkpw|||~~||}~|tos~~}|||}~}zvrnkjjmqw|~ytrswzyussuxz||{zyxwvvwz|yy|zvsrsy~{|}|wpkhhimqv{|zwtrpooqsuxz{|}}}}}}|{ywtssuxwtqqv~~|zyxwwxz}}{zz}~zwtstvy}|xtrsuvuqnnquwwtrpooprsuvwxyz|~|zwtrrrtwz~~{|}zxyyxursy~|zwuttssrqomkjiiiklmopqrsuxz|{yvvy}|zz|~{zxxxz|~|vqlighkqx{yzzwrmjikmoqstuvvvwvvvutsssuwz|}yurqqstsqqv~}|{{|}}zvsqqsvz~{yxvuuuutsronnquxxussw{}}{ywuuvwxyzz{{{|}~~~~}}|zyxwvwy{~~~~|zyxyywropu}}zwtrqqqrstuvutsqpppqrstuvvwyz}~~|xuux|~}{{}}{zzz|~~}~}xsokiijmqu{}|~~zurqqtvxyz{|||{{zxwusrrrtvy}|xvuwy{yursx~}zwtqpooqsvz}~~}|zxutsuy{zvrrtx{||{zyyz{{|||}}~}zxvuttvx{~|xtqooprplijov~~|zyyyyzzyxvtrpppqsuxyz{|}~~{xy|~||}~}}}~}{z{~~{wtqnlklmpsx~{urrsvy|~~|{ywvvvwy|~~|zxy|~~{xuw|~{yxwxz}}}|}~~}{ywutttuwy|}{yy{~~zwwz~~|zxxxyz{}}~|zxvtttuwy|~}}}~zwsrrtutpmlou|~}{ywvuuvwwxyz{|~~{{}}}~|zywwvvwxz|~}|}~~|yuromllmosx~zxxy{}~~|{zyyy{}}zwsqopsvwustx~~}|{|}~~}|||}}~~}zyyzzyxwvvvwy{}~{zz|~{z|~{yxwwxxxxxxyz{|~}|zyxxxz{~{yy{~~~}zwtssuwwurqtz~}{yxxxxxyyyyzz|||{wtsvz~}~}|{{{|}~~~}}}~}zxusrqrsux|~{z{|~}|zxwussstvy|}zwtstvyxvsrtz~}|}}~~}}||}}~~{zz{{zyxwvvxy|~}zxvwy{|zwuvz}|||||}}}}~~~}{yvutsstuxz}|z{|~|yusqqsvwwtsty~}}}}}}||{zyyyyz{|}~~~}zwx{~{xxxy{{{zyyyyz{|}~}{xvtsrrsux{~{zz|~|zxwwwxy{}~}|ywtssuxzyvtsv{~}}}~~~|zzz{{zxwvvvvwxz{}~~~~{yz}}{ywwwxyyz{|}~}{ywvttttuwz}~~~|xurppqstsomlpu{}{zyxxxyyzz{}~|ywx{|zyyz{|{{zyxxyyz{|}~~~~~~~~~~~~~}|{ywtrponnprtx|~}}~~|{yxwwwxz{}}{yvtssux{zxutw{}zz|~{xuuuvwwwwwwxyz{|}~}}~|zz}}{yxxxyyzz{|}~}{zxwwwwx{~~}}}|{z{{|}~~|zxvttuwyzwtrrv{~|{zzzz{{{|}~}zxwy}~|{|}~~~}}|||||||}}~~}|}~|{z{|}~~|zwusrqqrtvy}}~~}{yxwwxy{}~{ywvvxz||yvtuy}}|{{{{|}~~}}zxxxxxwwvuuvvwxyz|}~}~~|{}}|{{||}}~~}{zxwwvwxz|}zyz{}~~~|ywtqppqtvvtqprv{~~}~~~}zy{~}{zz{||||{{{{{||}}}~~}||{{||||||}}}||zywvtsrrstux{~}{zzzz{|~~|zxvtuvy{{yvttw{}zxy|~}zwuuuuvvuuuuvwxyz|}~~}}~|{{{{||}}~~~}{yxvuutuvx{~~~}|{zzz{|}~}|ywtrpnoqsvvtrqsw}~~~~~~~|yxz}~{xwwxxxxxxxyyz|}~~}~}{zzzzzzzz{{{{{{zyxvutttuvwz}}|zyyyyz{}~~}|zxvtstux|}}{xxz~~~}}~~|ywwy|}|zwuttuvvvvwwxyz{|}~~|}~|zyyyyz{{|}~}{zzzz{|~}||}}|zyyxyz{|}~~}|zxusqqrtwxxvssty~~}}}}}}~}zyz}~|{||}}}||||||}}}}~~~~~||~~|{{{{||||}}}~}}|{zywvutttuwy|~}|{zz{|}~|zxwvwz|}|yvuvy}~}}}}~}zxy{~~|yvuuvvvvvvvvwwxyz{|}~~~~~~}{zyxwwxy{~}||}~}|{zzz{|}~~}{ywtrqpqsvwwurqruz~}}}{{||}}}|ywuwy}~}}~}|zyxxxxz{}{|}}}zxwwwwxyz|}~~|{yxwvvvxz{}~~}~tuz~}||}}~~}~|ywxz|~~|zz}zxwx{~}||}~~~~z{{xwy{~~|z{}~|{zzz{|}}~}|{{|}~~}}~~}}~~}|{zzz{|}~~}}}|ywy}}zxwwvvwwxyyz{||{ywuuwz}~~}{zz{|}}~~~~~~~~~~~~}|{{{|}|}~~}}}~{xwy|~}{zywvuttsstuvwxyz{|||{{zyyxyz|~|{}~|zyxy{~~}}}}~~}||}}{zxxxz}~{xvvwxzzzywwvvvwxyz{|}~~~~~}zxxy|~}}|{{{{{|}|zxvwy|}{zyxxwxxxyzzzzywvvwz|~~~|{{||}}~~~~~}}||{zzyyyy{}}||~~{wuux|~|{zyxwvvuuuvvwxyz{||}}||{{zzz{}~}{{|}~}{yxxxz|~~~~|{yxwvwx{~~|yxxyz{{{zyyxxxyzz{|}}~~~}}}}}~~~}{ywvwy}}{ywvvx{~~}|{zzzz{{{{{zxwwwy{}}}|{zz{{||}}}}}}}}}||{zzyyxxxxyz|~{yvuux{~}{zyxwvvvvwwxyyz{{{{{zzyyyyz|}~~}{zz{}~~|{zzzz|~~|{zyxwwwy{~}{zzz{{zzyxxxxxxyzz{||}}~~~~~~~}}}}}}~~~|zyz{}~~~~~~|zyxwwy{~~}|{{{{{{{zyxwvwxz{{{zzyzz{{||}}~~~~~~~~~}}|||{{{z{|}|zxvvwy{~~}{zyxwwwvwwwxyyzzzzzzzzzz{|}~}}~~}|{zz{|~~}|zyxwvvwxy|}|{{{{{{zyyxxyyzz{||}~~|{zyy{|~~~~~~~~|zywwvwxz}~}}}||||{{zyyz{|}~~}|||}}}~~~~~~~~}}|||{{zzyyz{|~~}|zxvuuvwz|~~}||{zzzz{{{|||||||||||||}~~}}}~~}|{zzzz{|~~}|{zywwvuuvwxz}~~}|||{||||}}}~~~~~~~~~~~~~}|zyxxy{|~~~~~}|{yxwvvvxz|~~}|{{z{|}}~}}|||||}}}}}~}}}}}|||{{zzyyyyyz{}~}}|{zywvuvwy|~}|||{{{||||||||||{{{||}~~~~}|||}}~~~}}|{{zzz{|~~}|{zyxxwwwwxz|~~~}}||||||||}}}~~~~~~~~~~~~~~~~~~~}}|{zyyyz|~~~~~~~~}}|{zyxxyy{}~~}|{{{{|}}}}}|||||}}}}~~~~}}}}||{{zzyyyyz{|~~}}|{zxwwwxy|~~}}||||||||||||||||||}}~~~}}}}}}~~}}|{{zzz{|}~}|{zyxxwwwwxy{}~~}}}|||}}}}~~~~~~~~~~}}}}}||{zyyxyy{|~~~}}}||{zyxxwxyz|~~}}|}}~~~~~}}}}}}}}}}}}}}}|||{{zzyyyyyyyz{}~~}}|{zyxwvvvwy{}~~~~~~}}}}}}}}||||}}}~~~}}||||}}}}|{{zzyyyz{|~~~}|{zzyxwwvvvvwxz|~~~~~~}}~~~~~~~~~~~~~}}}}}}}}|||{zyyxxyz{|}~~}}|||{{zyxxwwwwxy{}~}}}~~~~~}}}|||}}}}}}}}||||{{{zzzyyyyzz{|}~~}}||{{zyxwvvvvwyz|~~~~~~}}}}}|||||}}}~~}}}|||}}}}}||{{{{{{{|~~~~}||{zzyxxwwvvvwwyz|~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}||{zzzz{|}~~}}|||{{zzyxxwwwwxy{|~~~~~~~~~}}}}}~~~~~~~~}}}}|||{{{zzzz{{|}~~}}|{{zyyxwvvvvwxz{}~~~~~~~~~~~~~~}}}~}}}|||{{{{{|}~~~~~}}||{zzyxxwwvvvvvwxy{|~~~~~~}}}||{zzzzz{|}~~~~~~~}||{zzzyyyxxwwwwwxyz{}~~~~~~~~~}}}||{{{zzzzzz{|}~~~~}}||{{zzyxxwwvvvvvwyz|~~~~}}}}}|||{{{zz{{||}~~~~}}}}||{{zzyxxwwvvvvvvwxyz|~~~~~}}}|||{{zzzzz{|}~~~~~~~}}}}}||{zzyyyxxxwwwwwwxyz|}~~~~~~~}}}|||{{{zzzzzzz{||~~~~}}}||{{zzzyyxxwwwvvwwxy{}~~~~}}}}}||||{{{{{{{||}~~~~}}}}||||{{zzzyyxxwwwwwwwxyz{|~{y{zfhdh_cnofRY_jiroihcwz`_}\_qmSB:>JIYexcJf|wt\YnwiVCQxirnXnjżuudXms`px}njmflVScn`TL^NUkYQoo_qKivo~iloϹ\T?JUB?Mnilqer~rwv\ikyypng}YyihcPLazflawyyo|pwgGN|rao~\P]Ujk~xxyokO`}iyt{ouñǵØ~|4.3DxĻr}yQ`sYOPBV\XXehH\^mW=fekidWUSLMwaTGdebo|cv}|uX^mwb}pefc_y{le}out[sQP[f~l{yhZp|j~me_toyh~m{q}yxwúĶeg[PVKN[m|tc]Y[_3_J21FLC_rZf\^d]qOfp`POsxUpc^ZMTMtpfrnd|wbsadS\~}{tmy}zgaSDYhk{af^No}~l[z|tqywfwqcoz}win{@DOBPh´iqelVO9VoTT`WWSeNXfHd|eO`c^z[\?UE0F__p|t~mfvXgonotb|mozjkyqdq]wLQ]ny{|tn]pcnt_ixwo{wxpŰz|pyS@4$ieXy|]Yxc_gaP;X?HPk`drmGebJNhnDRIGolKA_uGX{t`}ehOio_sus}|m{ZN`iF2uyubYonps}smsquf_w}`tnÜ|lD\[GVVf_ҾkfjNFT8]g;79Uw_l{}rcykux~zzh\Ti~YThfmYriltsnnoxb{ssquhk]oy{j}{yq|j~viMKMOR]~yfrkbWS~fOAYf[[qnQPwtgcXk_TgZLTXI_s||ro|mauSYmNb|h|wZfe[BcX>Gkfn—v}wdfsh]tqzy}wukyy}qa]MAII5G:y}½zucxhSrsTM_Ta^i_[qq]fqYRrmO]@AkV7)]oTzoo}njm{vnzsiofpeKNfjDg~|Pmśqn|fslogrs}zogxyrvvVp}zmfm{lwaZkaRUepέ~x||s{zvuj{tXWjeqlytjyxvw~~~Z~vjg[KLYQYVIIA[a}szhrr{ppjpfmwnqvpbb}}|jfjo``fJcUWUPQXWFJjFQ?>jfbnìĶ{l]qjnTVjkip}{ut|}uaszwd\JfpIPT]e\_L^Waw~uvyqvubne]ugp~t`Xghyxvtlh[m~dLRbpeWWUcbaaR;8QIUeI`uexvywͱĝyo}qhnsodpuxZkhVsy~olqkyimxn~bltl[``xvykqefs|v_RSFkz`{UXpvwyxyl|timyqa}od[VpcZm{uz~hYCf`CE8A<-952NtwhW]wrqb_zʬdtvw}zs}dyttk[qYw`eXMVbaKMN]sl~xκtn~ntx{v{|kteSM-MoiaaVbWLHPjWop_zk`~pgi`oXNR\`FaQ`daq`ysqhodWp|w}pnryQd`p}}z_KJcfD`EHtilh[Vbykkn`ti~zz|cXfq^mXO[JS[PWrfVMQVc`dc`jtT\_Tqky}y˯˰tuOSL<||xbZqX_jzhihqoao[^mp{u\OPQZ\\UFQd{wrzuUr~}vuux~|xuvptx}d=bJLokolqvlwbRTOPLiTNnec]EWfhjunj{l~nwy`amkm}su}ypgzzsmnpg~qvfZumfhpsurok^hZvwmufTtphvpty||hYTVZngp~zyehj\emQVZMJiwjvzdl~]pwoya`NinRfgp}rnwcsXlucnpmgnfh_lplk`W}ib~}c[etm[aR_knwvooyeXp[dqjeg{fpvdqOZrzrbp}pb_h[z~~cb|cikSara`lOYfcVB7FLkvwncorjgTgZCaTDa{~}ttn~x~λszswnpo~`Ya`wkf_RYh[cd[_|uotzuTqj~ywz{ij\lofr[egnq`iklrvpOcfR{re|r|nxorqsl[hms|cfwzwc]rl~y{hpwu{}ph{ku~aq|pp~uf]_iMhzXrwzWurm}{x|rg{z}wżʳ{rgnf|v|scZ^_z}nhvbnuUj|ovqnYWkcWMUn_|}|nwyt|py|~v|o`dMU\KPosymZ}|myiki\[qxokxq~~yxm|essmUKXLd_edQRdwzcubyiHl{E*>KC_Yrr}}uosl²hm`e^Nir}{vj|zq|w^e\rsl_XSoT4>^I,8 (NR\]`lw|se^Yvvh|xy|wo{nglfGCWwiilkz|snw{wgTRTXo\Y4$+7A:DRQw~ok}nffMf[Wd`_fwy~~}jm{~fsdjld[_mprzt]E<46UZyrimVVG6JTW\]ZjkVhuc]]_`\aXsu|fouptpgkc]btwyupnfY{}y}s̡yuiJ>XB=RmbigYW\ekxx`agYUcWNRQUeMSr|uixvx~v~njzooutYmy~ymwqYfZTOGKcppeYN?QlcpkZpoy~iRZNMSGG]|}~sy~pcxyhpiu§rzffcUTfyz{qzvsZDbx}Ÿkmufpĺ}cYMC9/'Gl`ȼ~ezhjx]PV]scP]_^^YXnm`k`\Z]acgwh]vgQI?M_^||v}{|wwkcJ653KUu|tlQst_ZTQMJBASYYb~~{hlogwzgvtu~o{upfYNTY_zq~|syrgd{thpdLPD5?do~sszrl}vtwrkdaTIbV:I\Q^uqqqjwrr{ospe^]^[`G7h{|vt~xq~fUQJ:'6dm|cfq]XcelhJXlh_PNefZkljnne^_on~|cbgN@6?Y^yuw{qm||owļ|{h^YO/'GWXln^piUPHFKMECZ[Sx|z~ybn|yrwdkkjidec[Tlqjsywsjh||}tvvn_^T9BH?Pms{z{u~pknrwulebRRO9JZNf{s|uf|qrqi\PJEIUIb~z~t~~vlsq^@93*'+GoƼ||f{r]YZXcgOS]UQXs~uxdfhgjovqr{scHQbI8Cuxbvxyin}mip}sU\ea[RQe|vx|lgb`xnoj[J;@J53AOzϿt~dr{zre^\WBG[Wjjj~zmgztime[QE?@A97>Q{rqrsmtvt}xx|}ogk_QggV\flrݟmtyjz{fhqpe\KX|s`Yjynyxuwyqf[J\qfknie^XihYe~{hSKkԗ{}vzx|tkwp^\ydUQSUKH~qyxysh`jt_UTRQOJQrnq~~{aF9DStYNZjZ\ooruwtmm|yz}eq|qr{iqónkhjidZNRZVs̻ҽmA1YaslVH=),DEJF4Ms|tglxkyxh^cvfiwtknȹqpkZ@)'/+OȵwnhhiYJaa;Mxoc`^OKPqzc`[MZWJ_qnlshb{{o~srmdYQJPRT{·to}praj|txxlflfdջznlUEB=75>NNCHj|jdvXgmYMHCEL?FoyqstvZ^djmc}ĪxkNKHB=96;?(-b|`N]gSIkfc{anqzpqd_XJ7)2THGŰiXdkZTYdWF`~wjkvh]cob\}ychk]WTYpymȾ|ie\J7*2<5Luof^Y[\NFZQFe~~rkh_MXllobcsoc`pvqɳ|wqoqrosx{uZPP7*<@Dh|{}wvyyvzsfurcXljVQOG=BIBSdsmzwddjkjoe_q_SOTUX²wzv_]meWhsswklh_ovbTSOD5+*8FDjĹrnvubxuero_LBBTceynujSJGDB?C^jPfscfubJDI]eczylWer\KGPV?6Sv±x_TQNIAOMIctupheiwqg}ytldbr¸sywhb^Zbusm{yypjTA8/'*C[f˺}wohffebcm}vqdWhtekq]V^^bzzmc`YSRSXhputmZ=IfpkjblbQN[eWVk}p_dUTPB40?\el{yxo_lqyc_fS]_QFJdõzk\TNMJHTuwjouzcLKTA-3Oabvq^kmVF=9??0@qȽ{}~kTLLKFGUUSm{zwwpkd\VOVxyvp}jckWF<2&)Fa~ɽoZ`k^^aZnxrmhdlupnnh`WQRauyy~nfc_^ew}~n`gU?428M]bϹ{tmgcdk_Lm|wqkbQH^eQO``IJ\mǴ}rpkfb`fqhf|i`[O5)?WWUrnmwupmnx|lxg[qybTQNF<;Rshfpdrr`ZalbYsjszpmg\STgvltl_X`j[Slxgnx[fsVNMG=7=PRRoznt}jbv{r\MUULIXjhw˾m_UMKJHMY]cr|p]ZbTIW^Ym~xnaooXKE=7>C>Pbpʻ{e^_]]bUMf}}}y|toldYTSUQUo|uznv}mk¿iZSK>1,3CELz˽zkZ`olZJ\y}xo^c|_eolb[\gunqê~pkgfdi}mf|{zSKWI/$(8B;Lrȹ}h\nZeudSQ\`H>Tnph~üwjhgebclkZe{{kd_UC2Ce\Rrkr{ytu~tgz{_ShgRGD@933Fce^}õysqrzmf|se|nb\VNHJZgcmźwkb]frfarossrwwp]PJD@CSWShò{{zunknx}r`S`g[\VBAJOYmsyӼ|rh`\Z[b^\{{utjdSBJTRXV]z}wcbrgPE@9/1EVV[z°zowiVQRW]OLf}oi}wrqlc^dtueumu|mdttYneH@=5+'0HWXtİzp_^y}a`}ztdUivUTa_UR\r|y}v{j_YWXX^vvtxvXFNR?4A\hjzvtqs~qo|xr`SckYKGJSQBRzlaaa`dpogxuk{ycVOIB=8AdwxrcnyzwislZPJD<;BXpútaassnslo{tnjkib]XRLHJYsDZth`YZfsrnqvhqnVWXF616E\eg}w}oZO_aLGQG26Mf˵nf^XUV^lgfstnebXD=Xswozynvwtrv{\EN]L7/-*&-Ju~vųrw{k_bqtgoiYyvfa_ZUXjv|~zjcfspaobPkvW=64.-6Ngmxùzrnh[_zxhtxrmaPXrwgPNZ\]hxlb]]]bqugtzgS?4=D>Gamr|wwx~wm[YjfRHDBFRTZt{{gVSVYckae|vunwsb[XROR[gtzdgv}qioxnWLHD<8>Qfl~sb_r{jf|zqcWhv[HMPJCDQjz{ʼvia[Z_p{f{zjXddOBEUijj|yxyvb~vfQPbYD>GL9/FizzϾukfc_`jl^d}o~ve_\SB8Oqk]p~|jr|~~dZkeOA<71/8Urokȸor~tgloabyei}nfc^YZewypvqgagtrfpytkZ^d^J7/)',=NNXvŹzy{~q`fvrmeQKSZbryt¹xme_^_bjkh{yn}m]R@3=JLSSWq~~|rlZ\mfRIFB>GZb^`xȽwigiqq``wgeyr`YVOIJU`VU|~lnwgicQNJB<>LYVZ~xoa]rgSczzseRZytOCNSMKTgtq|ʭxqnkotcz`OY_NAGY]Vc~||{~kd{|q]Q^cP?:@G?2BbjkȸwjfedgmdXg~}qmibYQZtwo|rgr|~{v{|j^f\I>93.-6K_jrǺou}vrccx~ztrsojea[UV_osmvrg_\cryrkk|}otm\\XF838FWZ[rθokvc]npa`dUBGYlzwyļqga][]dj`^||hhxr]QK?,,F\\S\z{dhyp[PMHBDYuvcsmpwl`_im_]qhN_yp\RPMIKXmrep~scjrkZ7,44.?]ybewN4\pFKNVlr~ƽxqpkhl:1u~pz}zioo[_ldWKATVar|اxWlvjw}wfpdXeld~g^b^emyy}bO*\uvrgg}]~|bkp~~ibrliv`hQT^v}sL )R{d_kdhnv{v\vqYqpPH\sukT__aPbusw^z̪vg_?Cl{vj~udLUX{PIGUmr{ͯr]82S[rJE>77KDk~u{yybwrnN3Y\Wqjp{h|g|sjit{~ocwo}w{r^qjaqбp_zin{||lkhJU\WоïyVqW0;0J}ĺ\ihibD;OS^s`bicn]]g`PBjp|qnMfurm{sbxqepSAB(chƧ~z}|_r~fjwuwgr|~׶Գa\T>GQEHANuuugkpXC|vUP:NgMQvv溧~~|sbmwZWkw{é[YoI?C>5f|ɦg}c`TZkQEh|Y^oTL[L4k|_upj{pmvz~c^[HL?&5BKkr}wYka]]yd{iOF3FF;bzmZXLid>RzpmH'T]FS^]czwws~}mlBZI39N>Wg}hnkua_|ɞthzsdm<<*;YP[߭ykwq{dJcog\:Q;7.1MJJs|sovtkirjsrI8b]Sj}ġ}|uz}hriuzТ}zǻ~u~mGW4%*1GHa~]qaM[tUEmU/-!A[P[Kf}yxtf[AW>DG`~glcSW}w}{XVztj}{C>DTH8cjt|ƨz|pxdxsm`RE]W??;STpcx}^nyHUje]PG9`E*IVkʲlz}yKmZ]uf__yt:'GH<:\aαǏ]uzSaV@oSahE5`]?Olnjzd]uq}|lage88LTK[~yǜojWusZ~nq}}Ó\OyF#=IG3Mlף{|wdSVelcNF`;Q`LRehxQ_z]f]vOMRW?:VYaxipfhPqgbmpS_:!,%{masɿwddj`qxd8JUOP]_p{sr\v`vns_~jc\{]gP1Yhj~ùfniuI^[wi{kctaIIQ4AQZKd~]pcdjTMIgulzqsj{muG\]iiG_PcVVt}ukV~e_v`}tugdd\LG8,5\[[xɋx|elr}Qqp6fgUdS[kl{q{~{us|}UlgX_,Bd)2:fʛ}{}mjzaigzRdth|Z9E/=HTVsȯ~otRNjQ;P\8XfsiOx}w|wqaSzy_TMD>aQ]`ânwgWosdeétm\]t_kraH"Q[Rlkwkghaz^KS+HdDEF|r}ux_QuwdISNEC\Tnzugeaxnzz}mL^Njj, K]YO@}u~m`rdVdYhlc[ljQ.5166SWcxĤmxkqrFRsgdVQrr~lwstuiYvyRH53]E77a}Ȫȿnj{{ypmun[lx~}q}{y]Eu~q[8?G7@Oa^Ƕc[QcHGdPAdrs]jmwmpfmy[HIG:M\LTjz{muveW\cioypovT@ZpXQ[NA%Gbhqt|ttwr}]F3;Q{[B\~{vl{fe[[pbVF>U_ibmqjYgop^q}r]sb\CIRT' %AW_lϾȮ˵|mimtm`|[SY?/OMCE=hǷΰ{wfi`Q_Tk|zoxjy|t}p}jH\~zhJ;PZbsdwsvfyalR;r]^[tt`uo\cRUS159:=Aalȷġ}ljgfLdsgoUfw}~nleZjclca[>JYUcSwvXwyt^Zoskt~togcIG8XaB'4.'^gRfiɼѤǽtlIZg[[Yz|y}dziLjcH]MN[L>Zflx\p }}tidhx\Wjgr\}|pkqscowgws]`JFgUMPA5Tnm̻|}yvgLPNCxbfnsyrVYudl_UK-;R[gYySzpaONsiUrux{|^Y\NVD]XF92MIDXhݯzakzr^]z{~opL\ow^F]G7>K8c{ſyo~rkckVRgbym}goTLR1@xW)BQ=HѵĬƹ~t}dfWco^ity|prjZo~_N|~f1?fZbioequwQfqXQ]`Wx^ph_t[Z^PUW,7JR=&6 H67pnpp`[rkvcUduprux\ETgQ^^uiv}~Szcldsu|oT\UnǠto_|mlKqh`YWpxZW[`w{{lݨϬLJqxrz~oSrxlOr~wr[<.:a6PVDLZNKYImDPgk|gIew\e[Yd^Nh{qTYm~guapg[ULdtWkuZv|_qab{XWyvvS\T|xbuyc}yhMt|ÊtjhrcUWzbpwzh_h]HjPX=BROBKCJMRmfc^DAjotX|cm{z\nnYs}hlt]eTlnSSokZ}zzzYOum]x~y|m{rgkclZTnwldfht8ksyȡwzΤyltfXplR8Wb]yx`WP|@=?2HD@EGDV%L}gsrqX}idcquzdk~~XF~gELitdjs}v_b]ruhon^|`MizkzlheSbQXNmmfnYulsص÷vt{gjpg}ycMhxpnw^QCS@JB\[^fjis2LlvhjRugm\X|]act[ayq^L}jTfgpow`RbRj|z}tqYvn|pv]uztrfqai^y~Kupw®~ivdr[r|cG\kdwy{u~}ikBSE[XYcb[b,Cy\kV`NlbgeXylyzxotvTyrQQtWofjbiSY`|vu`yrR_i\qu~{|gH\Tpplw|{LR|{yut~ų}\lK\pgAG_\nyz`}8G@MOKT[ZkJ+tltmmkozds\v{txzW}im[XmIBZiT{qobyi|r`{~OZu}YTqq{|mpUMRUjfsOn}oĞ{npvaaVMejOcjq}yqco;ROd`fwuor?Gft`i_n`]gVmhoi{tR{XpVcsW\polte]ejpxiXeaeqoy}zu}w]\bnzrtM{wx{vsjzodNG]zaIY\hp~{~FVUg`bpkgj9Ax`l[aasf_j]rtz[[t`_lNSslfr{brkbwt~b|PZuVSadmt}xwfQ[gthtuKyzr|iv}zq}voHsR:JjgFT_owwtJDK]ZUjvmpP?~{xxmt}{evflpvxyPu]]bIbKLfi~qz|i~drt^f}OTie[lr|}qjwrJTgxmyZ{vmsr{nyc}{~worJv^GVvuTajwrpxXO]qkhzxuTF~{urbhzwcrbdgqrxPx^cmZnY]vxwgoser^yv|dp~V^se\fnrt}y{|y|yVax{p|oT}~yz|sqhlyzfOvNHWtbKXapizVTip`cyynb?Spsiapqs{mqtxq\~VraabQ]|m~kxys|zstwox|RydQ]vuTZ^oe~zpaQhwmlwX`pqsyhpyoiz|boBaY?KcmMT\qjtiMape`twnRR~~jnq}nijvs{yPuZ^iX`NUju~zy{zeoxl~UioP[pa_dvlx{snsSjurfc~jvvv|nsxcfo}uarL`eJVjyYW[nfx|z^uvpqUTzzde}nyldess}xTxaevdeV[mu{tyu{vcn~s^rsYbt^\]oauqunxazwrw^avv|s{yjm}|}{emw}ftQ^eIQaqRMOdYiq}øgxwpzmSPyxxjavxnmz|[xkaxf_RTa|ozyxjzvjzzp|ectU[fz_SRc^dlyrzdotovv_Zr~qpzpcblwh|uhshd[IdGIRl]JMZc[wumzrrxcUp}f}~~qgnxydcsYln^UPWnut~rqnjhvVq`X`uv\Y\nZx}ywtfxus^owwww~lsvxkdiz{|v_amzqZiQ[`KTct[SRgZmnyy{~w^\zxx]x~wjbou||_orbxvbYYbxw~l~~gq|n|ot^w`^cyqZV]iUtx|r~rysjYo~ptvx}uy||ieiyzvvv_es|s}ZhR\]IQ^nWONdUmj~|yysZZwu|]thrx~corcsx`UW`ss|k{~pro|fqZo]W]pqZTXhS}t~{oup~lZj~nys}n}uo|eadp}s}]p{ecwhuZYVP]GLVmcVSdecv~}u~g_vvhvvehqx|aendi|iX[btywyzsf|h|~y|`m]kbX_oze\ZmXozzy{w~s^jeukx|wkwtoieiupyyyWfws`_ugp]YYUaNRZqhYTdbatx~f`vqdrvdhq{|dlqlop]ahxysxungzk}{dmbo`Y^oubX[hTn~~~zrjYlvxermzrrwuq~bcgtxxnsqXmvp^isflVYT[XKQ]n`URfVrnŷŤ}zt]gv`ufqvqhnmkub\ajt}ohsppvyjccdiWYatl_We]dn|vyow`d{sf|iqwxiqoprh[aiszpu~Z^mm`\odd\QSV\KMVji\Ubb`yİzjh}rjwydho}udmiollZahtyw}liy{qj|vpnbcfm][aruf[cdYyuy}r}fbvmlq|gjrygrmtor]bgrvrzq\Yhi`[mga`UT\aPPWik_U__Yxyìʼ~mi}ols{fiqxjsluop[`fovu}kkx{royqqgcmo][`ppcWa[Xprzz~rzcbvjir~hltylukvnoX\ahn}mvsa^ikc_oj^`WP\`NMRcg\QZYRxrưëqj}rxpmlu}jviuk}uY\_hmx}ulwztn}{moi]iq]X\jpfZ^^R}i~{~skaupȿu|`jcgeFEDLJmuWZJAT`evqs[JX\YR`fX[`Vexjfmz}Xsr{yæyhwi^w~n_gr{`leittmfgnnmn]NOCXgokx}wj{pevlfi|{y~mEzqiXbqvxwsrrpxtnWXieqyurQS[^Z_rshvxnp~O?tXOyoolliu|no~cqkywY7-;Rkkʾnbfr]UdI_W[eJDBAFo_HF14Rlju{pvXPSUV]mrciay{vu{|vd[H9j|v~lvĽzrezWN92K{ɴuqga[dj^OjXLzk\\ahhaWO4Qq`lu}{xfixkwvxxup`urW<,Ud_yy|sfn]`uoXJQ[gej{~sxtvxypiccebMGYzyxtwvqrvpbzy|dboaE26?MTc}ĸ~ja\nXc_KD?AgiPQ?9He^e{x~zo^]WV\kpd[e`spstvpgN;_h^j˚{gtmTCENZZ[itxheuzwxlfdpp_Wc{{x`Vw{v`U{fzv~gp`cfdi[RefUFKVbgkuqA]~~{vw{tVl{rktűtS>Xiijj^rmos||c\zv}nu_Lhz˨tl}uimu~}YI8/:^ŐzmhfieX8EbYQTsgYbdZYbEHbkww}jnY`poa[bkt~ww^LJ\bwo^TX{{jpeanmg[Yg|{xlY\U=\~ڿqYbg]MKQYcmch~p]T[hcvqvxg_Yp|{{ns}_QF>Ii|z|ah{}poudL=AXfrZKIfcPTGBMgk\rx]WiXC=22B_qrh{W~zrps{n[~y|qrl_iaMmʲ|kxzopx~qvmJ?66HoĔwpmqoaAQkaZUpiSWYRW^BMht~yzonY`mmb_emu|owhULRfj}q`Wdvpujk{|vic[_uoeaVQ]IJs׸mdml^UV[aps\tx}}ufYXddosnyte\byws~{o~hQKAD[~pcyx~|zziîs|ztnbOBAR^qo]Xqq]aXNUjs_j|_PeYD?52>Ylpk`zxst{p^~vvzgie^k`Jf̼q||suydqdH?53EiÝ{z~zbIdmbYUncOTVSbZ>Qep|~xwjZemjbcglwklz{rcTMTY`whc~t{wnttpj`X`wxw{a^ZT`T;Smӽuyzpebbdmz\byw|znbZ]XZrxw}ncdy~v|i|qYQC9@Wn}ʬjn¬usud_ShkQPX\onQ\lr||v_AHbx{umjngWt|}wtwwuʷuLMmҮ||kYqpPbpa`]UUfytzuafpTGLN^gLOgvͽ~URnx~oIhyglqohb_^Sixpqsp\N[}~|~xwƻaQ[˽k[hV@CHTfQDYdnz}xtZRdx|Yanhllfa_b[hkTU{tkRgbTRJCJ\jpvss~zx~wibO?UzjoooiaZXRKmjk~ekeSKc}|mxzRXdTPMGIZo|rmzpgu`LPTdqWQenx~{cavyq][IKZgke`hs\MrommoneTYxv`ZfpVAGIMbjszpppmowdON1)22>PSh{d_d`_XUYXVZbimusaghW[^[fʹs}mj~~|uþưþygPLVG0;PbɿogbYOA=;50268;C=9RWWef^an|ymzsgcVSdaQWp|ialprvyz[buhcsy~lyzƾurlfgr|~fafdit[Y[B2DISXHK_UQ]^^hy}llzqeru{eJ`jblſªpirbbojMK_tȹ|yrkihffdM=JB6ETawoaiaMObbLKUXZ]]X[eT9TwѾulhcbbdktiT_yvcepw|~zzpakuqrrb\flnqspiee\RW`ejsǿxl{tlmwzgYkykxb]]\]]^cj^FKegUVfsȺ}sy}{{|wpkkeZ[aa`cjwwxxzvh[hxlZPQY\J=Pp}uzô~vtqqqryuairozv~sjpronlf]WUNDEMQV_oƲtfstbZ[dfTFWrxij~}s~ha_^^_ahoaLVoo\Zgqx}}¾xqx||}{urrnedijjmv~weVbn`K@>EH6(9Yf_hĩytpnmnsznX_y~kdox|}xyor|kckppqrnfa`]TRW\_dp¾}m`k}ue]_irhYg~ob\XVTSV\R:EXWawqwn^yuh\@LaegvrlĖuxǮ~m^_KPawQ?EPJatw{kewytuZUB4BT|~w~mdmfptpw{}ir|zNG[fny}hhY^MQaw{qgw_OYgbB;LIUlku{w~cVnsduvcOB>E;>CVdkjn}u{|v}vrnnkxeOWu|pzsnsshputvks½nXZhbJL[Ted[c_hy~}cUpdpkUFMMYWNNLcxmbpr|mi|~waui[dt[Rl~t|{~}r}}cwg¦iYLSe`LZc^dSNTQ^fsxiXwyktUFM_cf][YUkxuekw~l_zdgyh]VasXSz{|xv{p}\uzdîr^PGNdaPffb]JKST^]lqkW~pshEH]ndb^a`]rryevn\y[tqd[W_ryXWzuxwpy~y|uZnygm\PGI^fSjkbXHNZ_dZdjqXvqv`@Smnakuyvknh{ry|qJ\r_t]Wbj_Pjunu|t}r~xu}ucpze`|ovkULRQb}}c^xto^`fszmovl\Qio=?Toseq}|lle~qv~znEXq`s[Vgn`Qnvnw}s}zu}ws|sar{bf|nrlVRXUh~ce{wpchm{owwkTXkf9G[wshy{lgjxqsxf>ahm{nVYjpeSuinhrzsרlho¼úil[Ixv}lqdV9=KIi{ofcz{TFJTgtcw]thxabixj^Vgwzopqifsp|y{ymV7_mx}qmwfyvίty|{{ns|`iy~dylgM:hwxtsYOe]L[hWIEZ_j{kP{`g:Qy`ilvrrutgflnnZ6;\d{^cyxyap{pa[bwun}Xp}SZ\cszy`VMZz[qsDn^\;Jqyg^zg^I'OtqyUhr[U[mRZzwhνo`{n|psxvrhb?hpFYwruoLmoP\k\jmC4ZaJcTT\^XQs^\_n|rwvrIZzhZ^nqwzyrx{Xrvw}{{quX*beosj|Yui]nSa~jOyvh\N_vFdIUku}~{y|{z|Ixxzkk?QnaUkhuNkol}kxrmrr]67Slyokxc|sUlvnfxrXopke]=JmbKK]YcotD$Yrp|ybVnL~hd}w{fŮz~seY~zsooRK~kl{nz_U{xed}y`nx=5jd`zyw}iImMemh~|leanwN=Gg~zKluon|ln|h|zygU{zgv}s|d"!`msc{kedgxh|icXIv{ePU6@lhkn||hnusd}N^kjtg~qGTwtgouoV[unZ}ypbl~~^90:a}yW{||edw_Uy^ir}YXnum\@;drh[pxpyln33pVsZbótwxui^|jgKR||{ipwrytQpkCKoe_ql|XgtdxtD?tyf|tx^TqEtx|udgjF::aulPbypoql`s_tx|rdxwXPxvtyj8"Xkyc˦r{z{ye\WDa`yXG@.dlkhktdfzxyZmu\Hph\gjf{P\}efV{tdYyb_w{w~t\=20V}{lgn~ɣnZzcQY\_mVHUnw_;;h~mtwwI3e|`yi]ϫyshZyyg^UEs|}ws}bhzx~qeFfeG?fcXinp^vzgx|OJ}}ay^bA4Niups|o_srlPUozhfomSxWipr~ea{ahue}ntprtbbbkYdoRCIoz`]u~rĿehln~hoqd`WajWTs{oQctgi]Nbh}r~mWruw]hʹ{x}Nwz|gaWywqgrilUzZe>-Qkn~vvuxvy~}w]fGugdnnN-\yeot}~gazgprugfNYmtzynqogqTt]Cggqpzyj^wjkgdb_sxvo{wl^Ir}FJwrH@QaQHqv^tnzxbfcxqtztclɬu|opk~iFJjnhE^{~gkyrSbkne;=pwzǺuTETmbqVLT^^Eruoh~^|om{zTgbRR}|]fsp|~jxzun;?Orzai|r{no[t}{Z>`zY>Qep[r|suݼƮlg~~mo^NZBt^fjVo~sep\A`wygWp|abnjKzʙ®x_wwvtX^h\ZkluW_j|aegMS\s[hlialZr[gmn_UAjgT`j{y||mvva@D]}wyph||`c{wjǪinpnCXu^\guutsvyibnzyXP\P{ueXgxzʔd]TmxXnwhLO~gbo{lU{olvnnX^pgltux[ut~sxrz}[uuGCTS:[soj{\logf{YTAj~`{mon|zxš~~rgoiOVfcP1Wzan}_y~|yMKnsylrgubpt{ritz|rnhhb8lgd]jShh\fkPSteVAp~qXZ}{ٙXttq_RJUb_Palubs_[]AObVYxi^ytر}}tr}feOHuX__]eoxV_aaD]hjUNlVhxo^Ůϵc}o|ih\[^OR_mvs|t~|vz{p~vulwgLx[hoLjzw|wpxypiUO{IPY^gnwut[l`i]PIWjohz[ξâzz`G_|cVSiukqk~||tv{hk\Ru~{c_n~moəmjc`^_m\]yjemzdOgosr|_{Te__WYaX`utvhɪž|vh[|uMBGD7PnjkroowsrpX]^o}vij}qisvItaQi[L2FwuXUiq^ayte}zRDe{llqtrjuŒƭmxbyl`b_KDfroe]r{uwSi}{rLOjl`][TPO\L]`ac_gag~ko`^o`ĩ{yqjuj_^zq|@'Si`UgVOXfjsdicp~amtkq{r`[cfoteҵxfڰyViqr]ZuM@DGcule[_ays}}|rn|pXSODLn}ths|xcbfodKKg͸n:UkrOVp}xwR]ZDdxf[o{}}rfhmknyllv–y|wuk͘{}~^9QSøj[j}\.+-@:)%F&PlYq\lzxsvjQ~Y@)6IKV{fUvor=y_[i~yON|jxxkccKDJ^m}y{yX]{|d``audxn}|olA|̨}~]YW_wofxGJGUW4,PvbX_r{pPjuTdDvkAJ[hmRMtfY_Sd^ZqdxyreswSUqg]jgvkh~wnuvoosBFRf¤mshnDY\GUft|m_{juh`fȼ{wvuuqn_QtTfhs^clo|OP7KegyhqfSWxhFMjf;VSjmh|J/.Cjmnz^ajgrSy~]wlsqcfy~o}|ZWxykfktmPemOG[|yTfrw{|foowq||}Z{ϴã~и{rsxsv[:Bddm]PP]lkWbrYFJFGTJuv\NpmhM:Mq`TTYJh~uwnci5RMWvskekdi}[]tgkW}oyrm}cczm{TSUNQZvvxtoahpUSeiOq][[rKXz~o}k|spQXPgqS==>IVhlw{kkpgiqedeXCskqdGCS]g]5064IMjŧgtcb{wqmmgrouSV|eYYlwΒx{x}jnu{ntnWJTN7"9dd]jd{vgzmڸ̦yg̕hqcMVtlgYhnum^@Me~nfTXyjoqjqy}jw`ih~hB3LkwyU7?9GP^˴sgQrtaywP>SgZGFPozz}tynuw]ntymVI=PdYA5RUFPN[gd[gr̐feb^yqlhmzrX\^kyzsq^lV|jWYIHgUgeKG`aeK0:?GLMcz}ǹ|}cespxcksakn[jxtecftyɳ|txvjz`z}kv{mbPOTB62Yeajnw}yjc]Ԛzrۭts^f}uihorjDJh~vdXqwylizbxs_kqzsH5Hw\845LZbhm|{v~mlÚ}~|`uZMZgfi}~`lyy_fNntslC6?u]9Kzyytmowbdtrjq[Okx\X@QWg>2iIMK,ilin԰}|w{jepYstPbl`f*%gTQ?8;[mscn̙lk{TS],DW\XWgI}dxG^~sFqtzdm_pop}mUnŨeLQ/_N55>bmxgnx|opyX7ayteMGhǰپ|qty]laJ4\3Xkojdm`fqqaimsYwbfygiZVDp~~vyzv]axls>;Kl|e(YkYwjox{wtvİnpSRT;pnY]KMkedjpjXaW^hsz[wd:3uvjPV[r|VLXlN\uc_ol:Y}uTktflwismzH~sthpüȽč\H,k|Ɩktqm^SD3yTEMz||qso~maghinwVne\/Bqq}{`fdQVdytdI}sWiEn]hGEoum{̩osqvjr}pspzO_u|ro~i_7Pxmq{obxпy\LA}jKO*roYN`rz_1A}{eft{`]twvrrtssmaluZ9G*Ktxgbmwiq{em}nc|e=Eyr^eɺtbzwslc]WgZQ\s~}]M=RPMZkoU@85@lhgx|yp{m]bb`}_SppriVxJB,y^idaiolielͼoqhvK*4h|ǐspzfO:sbK,;wSBLgxsXoI0-3jzz]sp|~w]g~}e?*4Z`^wr`WKwPknɫ}w~iPI?1Kṥu|wnH>97QXHSfgdrw|QtUEBd`rdM]w}aZ|~YyvvQmdCKbq|iryzzv`[vaQP`~}}yrkkT4"jzpF^R?EB^PYSqTa]^YNRfeaso}[ebeMJNe{i{PMeDFQпjtnoCf~ssptgwqdGL?4D[~z^SrMD1XgidD5l}UHSVC!M~qrrcZoV`gvyo\ZW[o}νqbwt]^\fy~sfwodzpr?%Qoccèxyok[LWZMWg{uxoLsTVYAHYsoftzsTzrWd~TjZBOa`IZrQtgad{~yzvxxuxqaZR(dsШrhxZZdbC/[]aUeqfdrrV>Sfeo~tz]rjulnkfjyjdpyzMYiE^\mLjud~pjm~xµ~x{mZQIRXafqq]Y[R<9;Rhtdmsh]^V^A?{jpnMnaO]jgVhqojujOC{jr{|~vjr{~i^{etJ>heC.Ci~]BLhY[jnpSR~PV^irxxt}r}ckmsd_dkhlla]dewSpr]^WfidRry|Ҽ|~xv}~jQHM9suonm`/jnfdGjxpY;Pph_|~nxuZyn`VDPcca[®jooYRdX`PZzscr|}_Cnn]kRFRyN]Y\dl\i|~qvazykuy@gi[C:pibpktzƔ|~pYI@v`{dhba{vyQaz^`^KVmnmib`jYVhwnzuƳya.tzwM,>r]FILFkzqmutyoyrðrlHCmw\9:sr\dto~jzevbffzwtyxdtz~cUH4}Z:UY-",*?jkahoTl~lXprMENa\Q]{uebeypqxk|sWPeh~d?R|vȥT+d?doyp^[ozoi~jr]J:_zlqN7^rXG>F|^bǿ¨U.ՐR5AAECPateUvth]ewjEPjowxW{goZbJZgph]XyX{}zih^wrha|rwnWwvǹйtceCqfECGIBXmiO]PNbMNTkfHA2`y~cwYitsoiqxpQBpc~~ugYp[Hxof~lcY]EY˻{ϽaQTQDx_βtiDo|xwrzsTospwps{|x~XQctk]fg}a{vw_ybjtb{Ħ~qrqPMPVFU\vzlcsM^t`KV{gE8rsO;<-$QeizeU|lzyoqtbN]eaOd`u~Wjku|[WbUoϸzmqRDS]PNgn~oo~fHsf`QmdJ+MoOBMM4OpnhwWxluzgw|trxV[p|hcgp}vnt|l_|k`{kpjXĵwx}zXY`aGINouuCWgXKP{hKEzrIAJ>7^hhto\zpspts~tTUacS^VwvqqyUr|hppdi[hƹjn]FOVP?WV|{sr{~RytkN^waFN|cIFF0=jnjt`^zwxo~}qw_J^j`O[XwgpqatsrnRx˵Ûyr_l|\O[cS2IQȹȕ{fjaFnqvb]VA/FiQa;9G5ozu`yuxvsrefa^VUrhGBTTxPxczphmUMkȡ}TbXRRUT\eWyufWwg>jVL:Eg|Ȭ~r{myh~k\{aFϭzuxgKfP@B6<]làov\eUz^JgaITp{V=D2SfjMuuze~dW^m{T=\mWWbw|h~y_]ZwRYèÝxlXeb;&>]fZNyҧyY_i\lUhlSYTdOKM4<}s~}}rkq`hhhXvc`G]iNZaũqmnql\Vz~_hzw}spst]Fi;><9hjjծ|{dni|nvSGrn@8H]rawq^Oq|hSu_wld{ea'>T\ab^˴b|z}~vpVwunpƮ}rZX~hki?*"?_eRyȫq~caf}_a]cjkQlOQ:Rvpnny|ubjjyaGMLCLhkoov`gly[y}tI`j~z˻}hlvirdE8gjKCU[vɻΦxsydT[SrXyuC5=Vh}st|}w]ik}{VPshX_eN1GqWn`jylgta`zwwimRVOPS0QFLA2k|ͺ|{mvu_fkdUO_rYRcwyrqm~^kQgv\Z829Omcv{WQyjxynYP|hnwY^X`aXLA>VV`kǦrotnqj\QVavAUiSXKoyrhecrcwdavoru]53l}dbrάwlnbzwzbNVyz[kwlJ:dyd?+6=9-f~â~~cgRmtujd\Kkvbn|nc}j^^qgmrc[bUX68qWtUsβ|{dwb~^Ktbcs}y\QM+TMIL>LSocy_=NaAELKOW}}}d|{dofpLlHPT[Y^g~ǹ}pqpv~kvw~xssAzk{qzvw`TtnYVocYUX@+3yպҦlfz|rrSMvYeiY^_x~igpta`{~kM8Xs_?5BVSWҸes}z\grnsdzzP@WlmSB:Th=OV4>ǐϞvt_|l|_rUIdlxwdvih\e]`hec\^3>]_|qȜ|vdk|iLv>bpkflqcc}l@hhZWaCue:Ui|mЫƳszkTUXb`jhPHDX`[wvxsxf]}vEI]q_;vZnTb~Ya¯ypy}qa~dho|Ĝunkr96^E\j\KPK7+RKERy˓Ȩhk~jvIqYp|stz\iujiVzocKbOop]ig\\k|UղeXMLOQmkrgrmlrprVyz][gglcG%=|KZ:Beåǟk|gf{}xrXYz}Tao`y|cG3:VVReeM;KUbP^bhűu°w~iwu\Q|lZK}ivccj\hf^GDlJ6SQJ]HJ`|zrȹ͋zvomntrVpwij}izn|LD{^QcbgjG,dfJYC}«{ܭofn}uxwz]kb`lxnvuɱo|upw{Y<8.Ac]X?GRILR^`zDzğztĥ|kz~wdCjipHnsnq`g\s_nmvqVvTVEJfnifŷ{jdaeb{zlvwqjes{ptyuVC\vS5;Jj^:8[7G0y|Īڻcnsrowopl}si|ydwoue^NKGcmlQP`KZbhrtcziZ|TriOo}]s_~muX\Zdx_]Y>p^==yqyƨ̼w}seblllqbgryinq[ibb_WAF6OdgHHXKFAZniʫϲtįf{hQn~tvQt{xn{ZZgosebD|m;L\[UJKMfhmxxygctupy{ahyg@m{Y?Q\bO6#:^DN3Vu|òׯpgyp^`Xguuuzs[RxzokT{l~oSstdGNwVBKwuysWc^{m7]piMPYbk\g}zrosnPWt}A'/C\lY^ªrw^Omr|pf{jkqpl|tmtZOrWhmhkFRv|u\iTRVm_jkon\γ] ;>ce;9?JigOhpZXrk}gzqshir^Kgrap_J}uux}iUKZcj{{sMVjlkmVS_mxqgzѶ\TBUvpz[L=UmTVssqm_cqsWN\OgYX^T]augWmxyGb}lqb=;2GVZhroy~|n[Opx¿eUUPNE:Avsckax}gctk[ajctdTqkvQ=H>G>5Ire]uwthq[}aEOeqzzt~}}|uiPWwĦwtvVDDGD=holw{|cZsqivvvUTTXoUYTd|iWf|lXcvfTuzkqu]vnG,3Bjzm|z~o]fyex^Uexðqz~aKB?Wjh_gMXrmmzcPRSwbYlRa8*=C]bSPy|kjqr\npo}k^ufKGVhzrtzto_Zqôcf{oM1/@Zkv~niqtQOsto|tPiN^[?SSajiccNamw~{pwoiwQ59Zh~oyowrjrxe`xuzŲʨoowV9-xmfP=Sg_SLqxigihdmv\COujUPo|vywru}`QYwiIZxR(,Fcfnv|t]]fiv|KX9Rb?Sbx|qbwwhbjzvf{odgzkV^E=?Uj}to{gl{|j|jkδɠ˜a][;.1Lmƣo^dyo]tpg_d[hNI[Y<#EQ`[UY|c_ivodrqww]MfyeY]pogwvrhku{xsXGox[0!-@P]º`Mo~jlsahYFsQH[cktpysojx}{qoomr|wkJRMIQeszrloX~ow{rfpqc}ùĞè~rmpI44Mcsyλ{rqzqvsjhstuX1o_PM+Ngo_PGtkVZad^c|zpcHQvkVEVr{{moynkl|xu~fPdV\^J(2Ye±qazeb}ao]ZDlP:eqwmht{|vp{[imbqZQVS=Zww~vhtfutsppws}t|uu]-0Q[Gg}ļws_mjnVmkWMfXb[Y0Lrej|yeeopfiru{xG5OtjIdkrvlbNgvxyZ_9=N\D,QYcyhfbvsg^zzHLeq?\kqma{}ftftfo_vTQKWH@jms|wedug|t|`~p|jyE0-HNJuzu[uolJqxhQedQkfS)Usks}{pkppdq}}~rxA4FffTsr|mptp~f}Wq~mvkxUY//HZP:]eqrq`pay|iWy\YYaHa{v~cxy~mlnk\sZfPFTWS@gw|vc{kyyd{pXy|cxadP26;LGp]vzuuNYzmV\zQaue7Bzy{vmvung{r~kwl[3;Mf^sk~qzeurlk|~vhiOk^K? 5MdS`{Ÿxe_tbgx_^sfdZEVsvj{mv{uQldTR;N[dO_~|hrrxtolkWdu{VldRP5Kdaun||sz]j}~{gtu~yrieAdXE=$4Lh^nɹte`mVixahupp[<]ua}~yqxzuNp]PK[d`}Ȱzpdm_[mit|oEE|_qv||rz~~xWhkLRBFYmb]x}hufk}mrnx[dv{}njlNo\IKYu<=I=tsʣħt~q{tfP^dtjalQt}TpkNiD+dPA]nftƱ|sy~`}eVhkDQrgkvzauoehddY\PLjkc[h}}~~~vthjejdqvVzrgl\TBY`HNQTbD@6ccaӯ}~oymY_ztzg}jdjTtvYy~bmD0fU@RWN_zeu\gsMLjVrg]~vgjgpadXJ_`XR\vowt{ehwlpsjZuwsg}k[GQdQLQK^=EiW^zϠ}jn}`ib_kcmtuv}}wbvcmrDO|S>F7SbNlʰ}outp`uxfsv|k`fWc|e\~wrl7FkNPWI>burĹooq_miEiZWaeziprinhcjVObVTF^rouworhpd}suuR|~mzmimgS[`IIPMV4MdWXwxqœôqycocciYg~tzrit_hkF_{L><;]_OhƵ}o{toe|weqtvotgfXrybatp_:WmQXTB>cdmïlobr]M|X]Vssz|j}kudfdNY^SRDgdkplwnfmpbUtptxexzle\n]DMHPA8[WJVn^ǽyzvsfp^Wpn{vzkh^kPYfGA7X`NRzp¶z}xyykz}ehqtzg{vqmj]xk]xrg?JqcaZE7P^Umoŷ{{ckbS~][Tpx}zmxpzmnjOU]UYFb]_boxuiwo~fWwvvrxe{yqievaMTJO:?WM=BRK|Ļytv][jdth~wvk_eOlcQDHfZHNsrxj_ztyakkqgevsorijkf|u_@]sejXE9ULMY]tqvĹĹ|hp[mt^XYsswpuxpxdT`T^QWeW_Yrui}wv{ryZsyohvxqutxl{rW[YWH=3RIFd>Xc"WPX];ROERvXISEiRINY_nl[|eUlqwgooXtu~ns|fh~gv{vyP\_d~^zhUu{vw{utzupwgPn~nnz}tptzʮzuw~`K[NfZPuTbBX^akSeRgfXaU}\VYffwncy]Mk~arctr]hfY^xpi}q}tzkn_guxyoP`tw~}w\sntmMb]mvaspŜwxratu|yppohq~_hvqrquzrvKOPM`D]dBka4mpK`PRUDjnNJKOpJOV`_x]roTV}{uph[wv{|xnoxlburx`^f]ucuc^zsvyyxyslwp{d{W}lzuq{exhkhkmzʱ|m`hZm[c|Sf|Jk`jgakSofTYUZX^ja|jjxXKp}nxhzhm`mYtnwxs}f|ehcc^fxohTr~sg|uh]i]sfsk|mrshw|x|cs]`^c`j|upyk`dUdP^qHZpDbyT_ZY]GcqXDHHyzNMWcWwciwVLs~vnj{qkmookuxwh}vslqetdwcufzqnrt{w~{rd{tgpaynvtsv^xrZ^^jfpzƲ~vyhudn]cYnhnnloVn~lPSUbX]p]{of|]Ii~z~t}mvgffqruwv|cyz\[[f\bueyuZt|yj~{lugshqnlw{{|no{sv~u~dmzYSSok[^ulr}qxhid]|fLmdT{k\g_iQVmkL@E[hPOc\^|Z~jJTwzkq}xutaeslhz}xqn|z}wqteawky~t}eitwtvncqelewjrosn[r{kNPUfamq}x}nzkamoqqutv]n{tSNSuj\]r]q|afI^z~yz|ssu^|vizpzt}dl~|]ORiq\[rioqaf~tv}rqrke~upuvo{nupnvsxl]rwhGGOy~^YgzeqpsbqbUtec|eeiijSdokIAGgaSUlUh{\jM_~~kvzrvkVZmjh}zusn}uvkfprz~tadmxpzl}q^|~oj^zlkijnokVholKENpndis~vz{a{~nypxw|ep{{^LRir_^rbca}rTZ||uo_oj|lwt{ffuwaEIXu][opcwlbwwmms}mny}tlxsftrmnpsjXimiF@Ikd]egsyxjtnXqqf|lgoooXgnnOAIdfVVmVb}[~nS`lyymweOUjyijuwtv~ttguxyt|vZ`qqmxitie~um]axeoq_ihnZZhj^>CS|yeezpxrn{t}}ql{zrPOZ~f^irT~pg|dRo{njc~stkrsxi_poiECNry`ZhxZtwbz}rwn}kfqyn|kvsc|woyfilodVgeeA:Egv`[g|`|upra{zq}koswn_qopMDMk|bY_qQox^lTi|n}xzxj{xyXLVsnfnfyotzj}z~~rbXbvls`kt\n}mlZqqhr`cgk_Se`bA:Fhwb_lcsy~zp||YPYxkclyVzzakUh|}~uve}~t{iorwg_oijF?Ihs^XeuT~mzexw~o|kbmxjvgxzzfz}qxfimpbVg`cB7B_p\WbxY|st}lzqsy}qexpuVGQk}g]cwSm|\z~oWdy{swx~xj|ty[IRj~ldlbzsr|{o~zzqjU]sxmoeng~y_cyu|r}z`fzjnb]fhePb^bO6>Rpd[`{hjy}rtUXgzig|lZhmyaYp}r~rk{xuhqru^hmhc@ANmkZYnlSsotf|~~rz}fal{wdszgyyxex~pq`cgl]Vg]cC7A[k[YfxVxmwu~ut{xi~tzdLSg|mcg{Zb^q~t]`vvxxyqy|{gxtuiJN^zrfhocw~{u{rt~r|}}ZWcnk{wWsf}jZn|s~}wokZmsfeUZ^dUTdYbD8B^l_`o~[u~vrW\nvmpbdcpxccyuzpizumbjmoZiigcA@Njh\_skTsmxmsx|j_gxu_hzhuuxcizkj\Z_c[QfZbO7?Tka]g}^ko}xyqy~~mz|tSSazrhk|eWgg}zie{z}p~wjquyfrvorOKVrtil~x\tyrqv{swydU]t}mkvzQu{Zsyn^k{uxvwimY`rdaSSX^VPeYcO:BXmddpawvž}wz[]mwosbadlyhh}yyohzrg^eijYlggcBBPif`fydYooyuut|ve_jvwu[vhsj|xwvm\otg`RW[`P\cZbC;F`g_extUpztx|tyyeU\rzmmyxPy|]u|sfu|p|}xhkouhjzlv]JVnkxc}|pavujjugjovnptkYayoudlSjtx|oevgq{udbfka`uhtY7:eujt|Wvfwyjr{|{¯y^cLVizy{jrz{~y~oazoafzyopztF@j~Smhyw~{p~jd|qmryrryv`h~zbTUdoZVUYOF?^eWLTmfdXY`ibcwgnV?X|{zsvxstu{qu~}qvjRHNTU`}gQ]iTE;TVNE>FNQI`WUV51Banythymzxzqtmdbnfpq|y|r{u_b@1?cijxqcjmhSacRrtvy|yjXVobUJ`}poqujurl[av~aiqsgUV[^Xr}ja|yj\`ysmabjrnst}pVZpv`mn]ZezZT^olnwY[f\ki]X`d_^w~xiy{qf}|sjosuj|qfhH?Prx|vo~ux{wo{jbgdUDHPYX{{d[qfVEH]VNBBHPKScP[M6;Slwd|oq}~w}|}{weuyjhoqmx|y{~ywvutephU[A9LsǮ{wkprlXfhTrxx{{whVSkbYRc|nkmqjtrs^br~\xa^c\NQ\efyrf{od^wwpfbhnoouwx_aujU`oekqj]bruu}eRrWac\V`hidw|muvhu~tghkndpo\gRHY{}x~xqpvr~y||\vVOQKAGVdhoatq_RJ`^UIDHOMQdRWV@DYujswn{z}}y{o^myptyxqz|ww|y{yjhgdYh[P[GG_ȥ~xputjZndXvxzzzrbQXk_]Wnxkjlok~sobgxtV|UWXRLVbjjnpzpai|vncdhok{o~pbm|˻kjtd_cgbonzsa_lyxrznquvkzsurzzSSgbtgxuch{|y{|muagvo`Ynj^WXuysheiop|}{h?q<8>BN\c^msx~unmWYtyt~hnkSRbwh~woi{©k^twmklzduurjlsumu]=?g]PJ1/AYhy_rvktwux|mmz[b|rcsrd][w~tpsy{}x|upfJzzl\X[[S^xgz{t|pfwtfXYjjiyʦvuLRmͼx`QIdlh_\`fgqq{{ebkutx}qOvxzvmlolanrrdpgWILa~e]hxmxw{iv}yzmlndabX;iyqeQSeN]ttvtrfXShhZPEWWK=;GZguqqoVP\oymypkvV\vsh~{lu^eaP@=Nl|ihqt|V_kV\`_bheXUgMMhn{{mzzh`Tiyxrnrw{xkmtqpqiEQ{xpa\ujiuyhs}se[bqw|tamXD`pjm{vncbeij~{m|p`bmrtzyYHswzvpqpg_wyl{hcnbTGK]vsmvi|y~sf|~|ukllfecP5i~p[alRcwxzwsgYVlh[PEVWM>:DUcvuuw`Ybr|j}uz~}{jfsUUq}o}{}quu]Z_RD=I`}|v|kU\Z^``ee\O`YB\lustqh\^uyummqwy|lfknlooYAx~zo`nydyz{|rgvxnb`k{}q]WbFHi{qz~shgjlqxqoceorv{tRSssurorphd~xo|~s^Ze]SLUizui}v{~}o|~dy}j]\_\`e\Cowfhx_b{}~{wl_Yjk\TDNUNDAKZhuzr|kagvppsswxxyscZjWNqv|}}}twhSHTRLJWn}vUzfY[^^bbZLW\BUo{sqwme`t}yplnsvz~k`bfgmrfNv~|uhpks|y{tdovphgq}yxuroeSL[HGox}xlijmszp~qcclpu|wXZtqrqqtskjzowujVS`]WUbu|u}hMexnxb]_bjiQC}}{wuy|cUa_SFDJT\ouamqcco|}`~g]Xg{s[EMR7Gbn{|thw~_~rmx^DKNG@BNasp~b[^jv]\bDIdnw|}ukj|{qmdt~vqswzrzuXxgh~k[_|eimdYU\hutg]\cr{s_ZcHIhxn{yrt|gj{{|yzxfBUzcdilqvtlsttzz{v{soyfYUZgrqdclPLgr|yx}xlxvmklmt~jk~zx{oMe{~{nv}zhdy|yhkmcUMPZg{}ornqz~{ofZm~~e@Xs_aglqtphsjn~oa`txvtflofYSWds{la^`jtvnryZXnu|y|}r~yy}i{yuurqmdZe}r{~nqthXLLS^rzz~v{_k|[RQU_jnimy]Voy}owzq|{tLW~fekrx|zsy{w}xtw~~onxò{soqxyrseThptvwtopxuhl{{sifgjp}marrpyzTSpnsy}{qq~v}qz{~us}xk\WZdr~|p~nmqyz}vl_\q}ZCodY\ciooiexxaqug\broqoiuvl^X[esxrgdemttq`fx}|y~t{{hrlllki`Xexjysw{rbVRV`rvu~wzZd|YMKOXchgn}fZp|}psv[Vmeipw||vws|}uty{qlt~urtz}zuz[dtxyxvpluztldqxtib`bftt_gtrulOq}jkotwwqk{uwzmXJ>?K_shl{lxkx~yqwpe{~ymqgkkvztva]~sruvxtllw]fruq||ujantpiglwzwaJKS[cea\lt]h{glwv}h}~ubKDkxwvxuikwxqhfjsu|xygexlnu~rUYyjc^XRRW`m}myvsz^NwdRYm}}qjzxaowfZWeut}{ewpprvunfqvYYlw|zmZPpaXOF=H[MOn~vlkuqbZGFRY_ciovsmrx~kIJwvWKLPVYTNXtsqxxwst{}źzw^bpy{xmr{wyfTK^~agmhir|yWGo_IEN]oyyyxkSecRLSVVhtslgejtg}osyf`uƫ}zZ@guksx\\nolnz{oiZ_fd]]ft{EDdgrxcP>2;H8Ci~x}v}unpt|su|x||xb[Ug|y||w~vQ^plmllnoq|s}gN>=NtfCQ~x\SV^gnnktnbtzyvwz~z~wuyg54_fat|hXkw{zwrpt~}qpyxl]TVgjwyy~sswXBMV^j|y}{qfbck{{u}}nP`wrv{~|unvrkvm\ZfmligjupP`g[Z]bd`\j~pl|}z{sGOvsgqwk|}tkcfrspvpuse]\ezzWdzYLO]owenohbahutovula[[ap~uwxT^jhsu}n]RPZuɿ|T[mbadhjfaiweYkvwtqquubZ`a][bph@AijUTct{nmt`Sj~xqswvlkw}yoihozcmsqlkp~wqs|}o|}~wQHlrSCDMXafhts|utyvmdlphYLEGTluv`Szq\WZbjmko}fo~xvz[?[td]nyw~vnkrzqjbTW\XKA>EXvhPn|bXX]cea^hiLFV]^_biwxv{zrjgjt||{bz|zt{}ui`]ezbd`JA@BEEETkd[m||vpklu~xkckqngbbiuxit|z}sJ>bpZQ[l}~||dm~|upq~|x{~{~a\hkihlvo`WW`r{pw]>SugPLS^fhc^hmVObnsssrt{xseahh^RJJUm|]jtoqw{{xqrIhui_`l|~tkntslfgp|x_j~eWSUVVV^fUCKZbddiupWasknv}ot~umjlpka[QGIQSPOTcyqiiihgnwiU[ippllq|y||zrwv]BIqyidjvypt}{vv~~ae|`OHHJJKT_SAGXaddhsmRX}xdZ\dlqt|io}xrqu|{soh^ahje`ak{z}mhjmmnxs`fsxupos}{~{z~qx~pT;Gm|n_[bqyorxysmmt}v|cnn^YZ\\\em^LQ_eebckymSZ~ydYYajpupuywy}zpkbWW^_ZUV_ozvtǽwononnu~q]_lsqkhksy}y`DHlr_VYes}}vklswsnmsypps_USSRQW`V@?MVWUV]lwZUtl^Zajqwvszwy~wql`]chfa_dr~v~xpdaceejvvb\iuxurs{wow{yz|zq^A4NqsaTR[jxztyyuw~|ygyk\VXYY\fkYKS_db^`huq[hnbbiqv|qtvpnptqhc[QRY\XTU_nxtqŻ~tsutu{|feqxxroqx{zzcF@_zuaTRZgr{zu{{wzpaxyaQLMMLNYZG;ERXXW[fwmWf}i]]dlryqv|wvy}ypkcYY_b_[\et|wsxnlnnovwb`luupmpx~okswwxyvkV;4SstdYXbq~|tx~~xuwwcthWPPRPTZnyrpikostqr}tyabq`WX[^alytfixwWazlqvq~j_^ab]Yezsdgpurnmostokiccmw|||oAU{{meeioryy{nqw}qduxuupcV^~ztromnrz|y~ijpb\]bfkvv|jUVwohloh_dpxxgg}meb`\[dy}x||wsruz}zwuqt}}uwusc\\`diq|~sq|}uqpzyhZNHQm|mc`ac`^]YYajoppt{~rkmpqu~xlw}lb_beiozys{~|xnc_am~w{yo{}qjhffkzskigkxucYVUVX_e_W[ejkhggjkha]\`n~~w{}ryzj_XSPTdz}{|~zyvtw|wqg\VW`vvrtttuvtmaW\o{ug\TQPOR[jqrw~}xuux|}{zxvyvpomlr~qifgilrywqs{{y{m_UNJLXl}}zunid_afjkklpuwqiikllou||uwvkggikou{ysu|~{zywqg`\^l}|yqx}xvutu{vojeben|yngca`cfb\^dhihhikmke`\[cq~~xx}yrwznf_ZUTYdnx|ywvy}ytkaXRS_t~|zywuoe]`nwtjb\ZXWVY`dhq{{wvwzzywvtw|vqolhgjqzxuttvz~z{|y}th_XQMPXcp~xqljlnooopsurkijjihikoqqv}smkklnrvwsrvz{zxvutqjb\Y^l~|{}zxwuv{xoiea_ahs~~xsonnmhdehiihghijgb^Z[bmx|wx{}~ztqwvnhc_[[_go{~}|wsoh`YTU_p}zvqicdlsqkd_\ZXWY]afnx~|ywwxywvutuy}ytrolkmt||{z|~|{~{x{th_YSPQXco~~xsoopppoopqplgffeddgkoqt{yspopqtx{zxz}~~|zywtoidabl{|~{xvuuw}xnfa^\]cmz|wtrqokhhiihgffffc_\[^gr|}~{x{~vmga][]dny~}xrmhc^XUU[hz~{xtnhgkqpke`][ZZ^dlry~~}|{{}}yrligfhnx}|}~|~|z{zy{zsi`XSPPT^l{|wsrsssrqrrqnjhhhhkpv{~{xvvwxz{{yxyzyvtqnkhdabhs}~{xvuvz}yvqkc]YXY^hu}zyxvsppqqpnmmlkhedejs}~~|xwy|{vogb^[\aju~}}}zxuqlgc_[WTUYcr~yuqrvwtojfca`aflsy~~~}|zz{|}}|{zxuojfdcejr|~}zxx{{vtutnf^XSPPT\iw}}|{yxwvtqmjihhimrw|}zxwwxyzywwwwuspnkheb`aeo}}~|yvutux}}{xtqmga]ZY[_gs{ywwusrponlhfdejrz~~~~~~}{xuuwxvqkfb_]^air|}|{z{}}{ywtqmifb^[YY^gu|ywyzxsokhedcdgkpw~}{zyyz{{{zyxuqmifdcdgmv~~~|ywuuy}yxxwsmfa\XVWZ`iu}{zxuqomkihiknsx~}{zyyyzyxwwwvtrpmkhd`^_dmx~~|zwvtstvy~~{xuqlgc_][\_emx}|zxutrqnkhfdfjqx~~~|{xvvwxvrniea^]]`fmw~}|{zyy{~}{yvrokhda]YWX\ep~}||{xtpmjhfefhkpw}~|zyyyyyxwutqnjgdbabejqz~|zxww{{xvusojd_[XVVY^fp}}|zwtqomljklosx~~|{zyyxwvuusrpmkifb_^_cjt}{zxwwy{~|zwtqmid`][YZ\bjt~|zxvtrolihhkpv}~}|{zxutttsplhda^]]_dkt~~}{zz{~|yvtqnkhda^[YWW[alx~|yuqnkihgilpu{}{yxxxwvusromifca`adipx~}{ywwx||xtqomkgc^ZWUUW\dmx~|yvspnlkklosx|~|{zyxwvtsrqoljhfca`adjs}~{ywvuvx|}{ywtrolifb^[YXY[`hq|~|ywurpnkjikou{~}|{ywusrrqnkhdb`abgmu~|zyxwxz~~|ywtqnlieb_\ZXWXZ`hs~{xuqnkjjkmqv{~|{zyyxwusqolifdcdfkqy}|{ywvuvx}}zwspmjhfb^ZWUUW[ajt~|yvsqommnptx}~}|{yxvusqoljhfdccejqz}zxvtstux}|ywtrplifc`]ZXWWZ^emw}{xvtqonmnrv|~|zxvtsrolifcaacfls{~|zxwuuvwz~~|ywtqnkifc`][YWVWY^eoz|yvsqoooqty~~|{zxwusqnkhfdcceinu}~|{ywutstvy~|yvspljhec_\YVUUVZ`gq{~{yvtsrrtw{}|zxvusqnljgecbbdhow~{ywusrrsvy}|yvtromjgda^[YWVWY\bjs~~|yvtrrruy}~|ywvtspnkgecaabeipx}{ywutsstvy}}{xuromjgdb_\ZXWVVY]dmw~{xvtsstvz~~}{yxvtromjgecbbcfkqy}{ywusrqqsv{}{wtqnligeb^\YWVVX[`gpz|ywuttux{~|zxvtromkifdbaacgmt||zxutrqqqsvz~~{xusqnkifda_\ZYXY[^cjs}|yvuttvy}~{ywutrpmjgecaabdhmu}~|zwvtrqqrsvz}zxurpmkheca^\[YYY[_emvx{~{nktjhnmukmjijfga_kcmnx||sslotv}ww~lhhfi\]XVTU]atlP8\aPku~}uquwjuq`^{|{aTPdk^I9HR[YVUTt{]lrxKLZekgt~|dqydo~ywydbih^`s^YqRPer|¾yqrmogn|beQIqm\YGAQbrkdajwosu`m|iwsivdmr{}rutnVpxogof@Hr~tx{pqubtVsYcxl~iczmei]`uoVMacVubqd>vpb|wjbmrvuhVpswt}wfNiJB_e`edPe{shl{~jl{~vvw}{w~_~gihmZ]~yzwgplru`ny~zq|}QdTprexr[_czwlxUaRfurdEF`_JNgpX=f[MyMkrfuzhyy~v|wkwTPVYaApvlzqird}lsp`lyifueznqtrvbOf}rw|hdrIDLdzfDrTapbzmƝ|fq~s``^kuc]c~^Ppt`]mnXwja[wUH]om}rwk~u`viyr{{{vgz\ZVLTKbW}wWuswnr|mevzzu}crefRjt\Wphihm}wzxqc^cȵcpVFgmp|ljdv`Mb^djmv|^VgVCBU][G`RRyXtPWysstzntsqpxZiR\PNyzlijv|^qmd~wz{w~wyxyb}mnl{SRzyfas~~s\Xbg[Hi|^zo{n_\kv_epo^}sylph]_sbkwxecvhQtbPaQ^iag}tm]{uauiuwyp|xrgm8BOXO+U||ojhvn[zufhqqs[PYlqiat~ixhUvT}sk~m`R\l^\XjQtfwYRwpWKZc^@ANZjsvXMtylcpveijhty}vtwpƸpnayxtnYUdlYoxjy}ljqv~qqr}nzsajjwRMs\]daW_dnpYU|feRvi{}|{v~zy}\Vtyw^WvvmeVjwck`}iitapkESfia~zlgy~wi{z~sozAOVI9.U{a_mi}lxz}ysr|q~pr`fvugP`]zy|}h`f{Qwz\fnvqovmJ@|flvyeIStw[JRBijr|aIXqqg]]^dewtc}nnx~Ȱws{foxzwzp[PdbOIuxnyywg]`~rvaUlvpG\olhZPFavcg`mq~uwraXow}{{cgvr|bd{rdWooYfpn_x~f`LtsvnF]quuuezhukd{oyxv}u{{dfn[C,;gjZRMTm^tlvZp}~|z`h|agT^vt~spp|]vwchuiNr}p}iVo|ww~zumu|lekruyqtQas\bQQYsvnm[[nbUdtU~[wwor{k_aVBLpxti~wmqylNzekMQohXO\X]zc]Iehy}tlxc`xq{trrukrstwon}}w›rZapn`RO{~pTx|ztJ[pyo|qtqft~z{unf~|{}d|Sk}`BOo`=4ESSHvr}d}}xgy}sXry_jdkw~yXhsZZ{hq|{uoz^cne}q||}~l_ojnh_tc`SNSl~zaTjwenMLmydlo\u{}~~}bqtznPIXeirzXx|s}ilobk_xxi\Q`UOqRawvolyyXp|mjx}pk|~~puWmy{vbioofsxpcVyoxo||xS[ohqsvz~zpynwxV}u{iY\h?6862Rqeewthyygsvecvjg^Zc|xx}i{nT}ti_`eofUxwoy~el[nrtWzui|rg^hsm\by|osZFl[TZy|q]ayoqgzp|}tvyoomb\^tXk[v{X~jq|{nebOWf^Qi[qwvqTZywneflwz}sgyqo~~wztiyk[hp|upoqmgdnQdylw~}^cdKZckv~kftzstg~mvi|uYheVSI;:`^_]elgsjjv~uXY{vhjtsdgxzsv~o_`qej{q{tkn}wwpP{o^qgrr{enouyqZ]q~KZ]`kefeaTdjSYeq|izxhowinxllvGZzZorhusq|yfXuodhbRWmew~wPHl|{zxljjqtk}qrUq}~sY}km]pmu|p}mi}rt{pYUd[R^vsmtyurz~wsqo_}ujwyrennJMZZO_lWTDedxvjuyzqqoishvt`btuv|isi[gq]ztdwZnzosjtiZsSRZszq~~qkxpnx~|[p\fn^bqcFR}mZ]_[]zvwrmms{vqnt~^X|TSemvy{~yzs||r{n^dgde|msolXg~|po{wm~}|vzvt\yiiy{iotKl~hsgkqk`hw{hg{}vnwehaVl|sd^hv||{a}Zlyx|p^\yn\RFC\phSQsx{[c~vptwt|cmxl|xxupu{orv{zqmrl]wvqjo~{pdIw}^\^bjz}utenxSdpxjVanZDd~^NSY[d{y|uuuwspwXpa^a^`m}k}rzgdy_YZ]r}k`[Krxrvtto}lqww~v^cqo~{u{~mjl_bYdieZapuqq~t_tdZk|tosiadkr{jjvvzcp~}kjtqgum\]_\_tebbYyzxxn\_}y}jptsy|ybj|_]}~yuszljkr{|wrbq|~v{^|uVTYcmwtff{}|tv~a{jrhgoeNOsxm]KCJb~zte}t~zrusy~{Ruvappjfkqnhn}s|~v|yuqge|l}qOhymqc[}{xxtl}py|omx~dSxy]SUUOWsd[ivrkxrv|ysy|g^rylfud\{hrtz|m{hkzjXsdZW[lqfIclOE]~yyc{y|~frw{md{lvjqzxmoystqjb{xqoWvbXYampg_`djxuzs`qacdccqzbJP\aumho]vu~w|{hhbgkrz}sa]i{|}~~hehv{ni}aKaxow_qulr~|vj~lfnlXfihlaaYKBQrwbWSTg~kvrpu~tdoxqebzxzl}wqf[uyiammY]}vpigv}}fx|lQLlv}vxbYqwjiJ8inafRMRUYivlqyyzY]{^jvmqusrpjehqxz~`]{jkvf_\WTciRWc_tvpWVys^Sjuz~yxyeWX`gnzyjUj~_n{a\npfr|ohfqplvcZjl[ww~~plx~~hSm}^_S?@FKN\nn]MHIO`|tprrk~gvz_a``dmsty|b}pwn[UPRazpXVf~su]jp]NSq{oz~wjcht}fLxW]]VTPMTntg`gn{~dgq~oiskmwvhhou{cv~zo}~xpq_Y_b`fzymg_[svhh^mttuundjq{yrlhjiimollvrZa{ru]Yjh\r|qor|xtmvkeq|vg}{~~|xuzdh}ddgRIC?CYyy^LGL_|xgoY]wºixkaog\`o|umsk\ZYVZmiZfouz|{\ey~ud]mwqstst||xy}r`lp]tcIFQYe{yporzu~o~xaq|xww{~|ytpr{i]y]akxvk[ST`yujhoyfweYi~xh\_uz}`sykkghq}||~|if{iwmb\SUpslwv]oyhl{yuyztjv~}}`vwlfRMLKM\qztjd^\fwj`lVSju}qjqvlmj]fwyrpx~jnr]VVZg}slopmxqf[r|tjdo~iz~xppor|wf~suTR[SOioghrzx\e~qrpx{vy~~rlqzoSo{`_baco}qlfbjxqr{dPlmmkae|zq~yzg{s`outsuuu|wfgszq_Z\`huwivz}fm~|wrwkmx|tnipv~n{pyf]_i}pnwdgRH\jmg]V\sypu}sotyzhg]AAOjyi^\jrmzhftvffz~wlh~|ye}rqxxwz~z[neD>[y{snlikwtu_juy{zrlt{|gv{vnllr~tlzbgnruyz{u}qb_kyxk^\anzq}hevyuyzfYqw|iV~}vtuuwtx|yxr|iZz|l`bq~ycSXdgruumPZqzzsf]d~yqybdxts{xejuwwjbiql^QIJXopbodHP_ghd[SZqy`{y{|p]nhn{{{z|~p`VRWdsg_ucGXv{kbadouulSqyvoy{|r_s|i{xtvs[Tb{ujvb[o{ypd[^qyx_|}sopqtzzfrzzzz~g[rsbZavuq{d\p~|k\azygrlazrqty|dozzygdm}|{xzth_`vrbndakqssmdbnu{new}}xvzfh{ilm\VW`ooW\gUZlrng_X[k}qxv}pwtfhxwtllvxdYZfwrZb`N`trkqrb[nrwqxuvxzpi}voltt_drvyngzoZfw{nbbqvuyomqyzd`q|}z{}{sh_exzvrt|~gdvg`q~|smnx~ouyg|wx}xbqxn}x}~p`gtlmxunpz{^l{_Tkzkadr~t}|{xvwa~zxxtjdzynu}pa^iviYdYFTkz}uf[]m}|nf{uml~|w{v^W}vrt}|topyw_`hQK]nz}oZ\uwuwuj|}{}}xn_Xp|ww}zwuopjkbVgz{rmq|wrsyq{}xad}imxxtsqlaSVx}|zsloXUk}yonz|zwwlv}y|t`ztmu{wrtd\]wy|ytx}ovbNd|zorslisphvxnyuxygn|{n_Udqdpu[ceNN]hlmjfhq{zociraryus~kX^o|}xps~qfmYMZgliedhnq{zeg}wl}z{ys~wsvtmjie]Tc~voxtw{lcfwzl`l}{qr~rdnzm}yomzzcOVenty{||~~{umcey~qzr^fs{~|uqxsk}uzqlpzbJP[bgihksxrmsuhlxtu}rjos{wbbzvnxkr|ob^isld^Y_kqomnsxysf`tu{ytwsr|adqwunedoys}kwmjjrxvwib^_cghhlquuneh}}s{vpd`i~~jowyulcbpxtyf[ZeuyyzsifmpdW_ptjZRdthy{z~wi`anpjuopwtvr^Vatwgosnmljf_[^gptm`Ymok|~vt{pbi}|~rhfpr}n`XTYeol|}vrnkf_ZZbmsjVXuo|jbi~zrgl||oo^UT\jpi~tr~ufaelrtsmfmps||~nel~zz}|wofbjypxedgaaq}{yqf_^dlqqkeqwx}ptndeq}yzoecjxth`_gtxuxj_amod_cfplwzqmp{tggt|~xztfl~qhfhoy}|xqlf_\\`dfefxp{xokmv}ik|zzlgn~~yj\W_o|vp~xof_\]`a`_hvu{or||w{}|z{pkqz}}~tkdbjw~styxnqvm`]emlgmvmlt{{nj~zkchtzuph]\st|zod^^bfeesulrws|}wlx}ohmx~|xxmhktq{qha`hofYl|wnkls~}{~{ojwpdpzrkjo{}wstzla]^_^ZVaxnknv~}zzs{{vxs]Zgs||oxuz|pe```]WVj{~tr~|sm{xxz}vneadmyyuuvzqacprf[Ygzspv|}q}yzzusx|v|}{sh_]eswu{zxrnkf_WXo~|wu|qk{}tjr{|xw|xvxxnkijoyw{|urqnfZVjorsmpx|trx~lox|{ywwuvu~}rhccir~|}r~tqtvhdbcqngdelplaYesrivyz|x{kchm||yqc|xuxXcgZ\^ZRZuusxk}yup~xuv|vpkt{p~zkZeRTdkfug\v}vnogszi{fn~s|iw~|z_Y~ljUdVJipls|}pm~|z\ijpwxxvZi|{urspyx}|i`or|n}ymniqlh|gP@NubZpuy^RzxwhijbltmajNMOqn{Wi}lwlzq~hBV}{ylXZOZpxglqynjdy{aodxuyqZvi}y{bmw{{~{ldmaf}}tyqy]Yhm~v]Grpw^`mmtsptf_mzprwcfm~{ppYysj{lura~vy|tyux|~zymcdtzz]pbk{QLNi\{uNhq~{u~ykernv~vs^jTrvqgoux}vreKnskctldssbr}sY]bLe\cXjt}sRl~{u{shfzou`turdvZ^ZLne}vesXlt}t]`sx{}de{d}je{kRRgnjtJonxw~iqpod[ew|oukJjWBA_novu~L{x|umlrvo~rkWcr~rs[~xxncxoQpzm}bxqb}}_`nbUi\lnh{^cqffwubXmrkjzyx{}ypo~vnmsv^Yoa]Vwtonekertt}}ipgqivqoWnsRQkq{twhysx}zl}}loys`V]aet|fbPnZK[r\isYoyyrix{mrzdggn[qs~zxwj{zwz}fawl~oSSwixnkjZeqdle`Ppk[bqZlx|oy}ljsx|yy~z\at}uj}~oeXcU\znawjqjqxywiqruyf~szhe]_lfmy_lnVwx|~|z{tr|~w}~tsdX[dknj\YJwpY{o`z~uaspkpwuzoru`j\Ygju|yx{maxlcuyhazqlrobvrjfYigzzWOdp^|Z{zqt~vunet}y}ijcqh}fqnUbNS|w{ov~odn~vlwqu|ycjtdeddQku|sUZxxwkmttm|nusggvugciu_[n\~}jrey{{}qgozi~cvuZg`ezd[qwq{x{yimsyj|fh~okx~y|{n^x}i\TpnTXyjlV}vvwo`kw}{lrvsj{zi~~q}dozUMShwl~}z}|przoskw}dgqal}xuhnzjj~efws\`rq~wzz||sq{uzoypf`auxz|vqx{it{skstvxzco|{x~|ozxgq`v\NSrn^r|vz}vyzas~viMdxcwypn{z{fXPa|rjurorsvZysuqqx}ihnyvv~srz}yypvdame]Zpg[try{hwjywnamuonZ[scqr_g~ndv{ht}u|{r}s{iZjuopfnvtuvluzy[gmmvpw{sh}zgt~orfWMTvux|w{v{ttlsynprd`gp~tczjtl`XiZojl}{mf}wyvh|zcfho~wpzsu{q~{{}pohjypcUZnlb{x~y|dzwoxwor{quonmvw]gunlaeyzl~yzxftlxy{kjnrpkp`cej|z~ox|qusnp|lcz~utuy{w^fjbZ^zqa}{wvxzyhiuw]]rOcxnv}z}~xhvywoeou`cerxuho|ui`nwu|}w~txmzy}ml|qmvjWDRwqwz|pqutwvcps`i|ravx^oc[nrw{xv{v{pnrxqvze\cYgnzsoqrg|}~}{ssvzso{m}imh_rvtd^mxr{{}w|r}kkv{g]}iKgujet~yvzdjz|tttsquiu}s{|tgswrp{osjrms~tr}||y~y|v|sai^ZU`tt^iovvupfzz|ym\gxyrurc}w[b{|ywr{pqyn|z{vprwwn_\jgwkgi}~{iq|t{opp~oo}pgtqstbOarvyt{z}ooxzxprmbpj`io`bc}{}flpp{q}u_d}qgysx~mlhcxrh{vw{uzwzs|op[atthMShnez}tyx{~nwyvnkrpqprpnadztqfsszz|qn|~zwxm}|rnsqiroWlqlqzjot}v{uo~pxwf{}s}wwoinlqfr~yfv}}yyuxzpb|kyghwy^Tcpenm{sh|iztcoztv{ion}x|rbkiny}tuyzquxuxst}|pboppyf]LWoyiotin}yv{nsggi}rbd|rho|iddv{}{zx}~z}ll|pw{p}oXllcetxeam~w}w}s~xwx}myxxnk`z}l^mztsqir{~typvl~xcjiYZqxjgYmr|sw~ssqjdvztytrlutgstsgxltsv}yy|v{szxtt{i^hbgX_or^g|}piv|t~yvutvxyvopj|p_^olsyu~kty}tztiz}ztruztjokd`mp[mqon|sy~r{~~p{vz|jfwrzrp]jwwwyjvzzy{jzup|||mc\ogbhq\\br{zu~ryierwxmo{yspuuatxvoq{wo~|tznttvpcbTjkjcR^kru{lxsj{louqxwhjl[^vrqatxx~~|yv|wto|}vv}jp}i`pmTeboxphmlpzxz~w~rw{yw}qlzpwkr|wpoxyv}}~vo}x}ctv|uljewgUTecpors~w~{mj}rowlqpjkiszuo~yx|p}{qwpwl~k_be]pebUVtzdolpuxsz~xwjy|pl[mrmnvhdjiy{}|{}yvvnv|ufqhl{r[d[encof_buwz|yuywluv|qhcWw|`tuo~ugtwx{vqvt_oyryvw`aXOYpmknh~zzzo~rffvwqpxv|u[jywskv}m|w}w{zsmvogcZI^f~airfyxp{{_f~~{{uad`X`\obQQb]efewhsu|{l|v}zzxsux|z{u{t{vo`Jbpzjfm{swuxsthkdgvi~xpxn{quyxmMa}m[MD[VRW`S]Zfyggpu|~|}}m|w~s|w~i[[^\xzbt}e]knmvgzrp|vdvjqtgYTLZwgpz}|twkqls[aqx{oY_RNVcX_V]yvtn|}|n|x}rplqy|hvtr}mYKGDemwr}y~wy~|xxz|zyqk[Tyyz{w{jquq}u]cag{vufl]iaSSa\ceVfhvomxup|~stl~tswkuqkbaTiwmpdclthpdm~wnvetvipc\_ePapxuox}yyup|t`ZVuxyq][eYW]^Vkhtnoum{y~|xod||tqpxoxy{pyoiWKKINllwuv{xv{o[`fdqw|swiz}zurkg[f~xw`f`X]i]XZ`ydl}{mvqsw~|tq|z~}yop{vi\\Vmxernffndntuyusozmjv`lrmWTZmnkkurvte\`[qwnn]g]TXjkq`esozx||teeb{~~qzq}zz}}}yutX]WPJNIg~{|yz~oryonjpe[]{}w~}yrx~zrtg_bc{ykblb[WY`rVdwsnpu}k}xo}~~{wq~sx}~o{pqe]^ci{xonpcalrxp|zqwwsup^wsri\bbS[twivuoog^_[jst]bbckl\oxauz}{|xwogeqr~opoyt{t}edjV\VMFNW|{xwl}yuwsn}i^hmaxyrxpq|}}qtvkddhlzohcRZlbXpgtpiqsrvrdy~|ojhtw{xnmlf]XPn~}gmkkuuynf~wxwtozqyv\qo[R`c~s{yw|lrg]X]gvxmpzd[otemgqngststry}vsmpzux{nvvh_pYVTPS]Sazmnzz~l}enxo^[Yu}z~wkwrqv{z~pvog`ZXwzeargWammmVkrnqomuuq}usjdges}|zoqeTO_\iz~r|vsqguinuvy}^yccfhfdc|z}{tuklebgitysktsggj{~p~~if}qz|vopmmxsopvq|xvyagkXfgVFR[nlzvzvz~|h{ure^\\_xozwsotmq|z~vteVQQfyxjfkmj]eh\tpmotzxq~filicc_w{zz_agfRNY{wssjlnkyiouwlqojvqjdcfyzy|xpruoulutng^ayq~rgo{y|qyqvso{wzuppjo|w}stsmq}v}|zuwffk^V\ZNFd}{{{{fihd][Wq{~~|lx|{v|y{he]ZZ`dxu}laadcp_ewt}~w_pqcjgbajvz|yzerqlfVZ`ZXql{uulkpno{n|y{of{psoigdhyiv}soo}t~ym{wwk^YVe}ttu}{z}ytxuw{|uxuyth_\zx~pswy}{k|yvuukoZecQJT[t~m}qj{gggb\_c~{}wlzy~wow]`efdfe}tngbchbnjexrtbfwbdiorob~zsv{wivyW`ibTQSp{wqupllqsp~uysynqqmc^Xpyy{l}npzutkha[X[_|~z||sn~yqpoe`hrk{y{xtq~lrz{~mUsqRRX]_clyu|tbwo`efhlpx{}m|vrvn[sleljfgjyqjkecgfrq{urpti^ukmxoagu~{vxy~xi\xkbZTQUXqyttqpx||uyzrwoni`WZawtns}~xrutqvt[^][Y]f}}xr{hro~s]itm_cy}qmonr|hwynxu[f]Zdcabfwyryn|z|ldyjiruplfutz|pnyytvgokjjiffcwmnjhowzvn~wusrrrbzicitmcj{{tujxfSVUSRUf}yzrsj{zqm_^^`fj~w{olpo}xlfes][\[\grwropz}}`o|qjaholkw{rrlkontuzzrbZwmadc``_l|o{zt}}{|kowvtka^^u{rpx}}}z}mz~ijg^SUh{wsyy|{x}fo}xvuy~itoore_emmzh^oZUVTRW`}~wvv}{jmt_dkmjjn}spkjmoqt}|yq\nmZagkdYd}try}}yxvrketqe][j}o|wpqmmtzj{|ru}nk}jcd`WRPgwtw|~qwpjfb]\_xz||tpm|{b[V[c`\x~{wqs|go~|lgx^ckooo|zs|}zueYn`TY]_cdr~n|vw|~zfe~qnrqlkk{}i}pnmjlqu|zwus_fxkmgZYgkxxx}}}z~_tqic_[[ayvw}qssy~uyz{zfwv`\VPMRb~}|}sxp~vbdc`^`q}~|uw}k{wdrcXfgXR^vu{vqqow|j}vyxbuokrspopz}rr}p|urq_Yoc_fhc^[lymyxzxzmrwprpjd_p}~zqmnnt}o~{wttuhpeWaleZ]|rw~uwg_a`]]c}z~ynp|yd||ghrXPRUY`i|xxx{qhlzeadfiqu|zzsnmp}w}qVayi[V_ghgz{txsppqt|}|qatrrtspnp|vvt{|qwx}xspojYmxgfc^XYc}yz|}}|oonjb[cywuu}pvx{~z{im}ywvzw^ilgg^Z`ko|tbtl]`bcfn{ply~rtzm|xcUf[R\aefoy~u}~wzvmmrmnd_o|wwz{{wsmhvzlfmx}xrzuofiw|idoxvz}zrilsng{pnvvuwzrcZvqyon}fitxoknvaX]_nwjkvy~xtjjdchotiaw|yy{yqarvbm}xb^mou}ywicbiwh{n{{k}vt~nmoqnhu\ajfamyo~io{n|rsfYMMcrlbum`nvzm~~vysyxsuvwupbafZ{ePTV[\MmZLZffZQiuvl}r{|l[Yqq}ktvv~jp||qrvnu{{zkmVdb`dSoc\cosiVg}omzs~tnorlrijttfUSex|ktbyq}{zm`nuwobUld^[J`YUSfnhS_ufzmzzuztywtzl~sfdh}wrs}x|w}wryy~yrdv{|n_}wlfXfkaWkxsaewctnsxnlkspwn}ur|akyj`]Wj}qstkzuvk[hzrr|xnVure`OVb[M]oi]\o`lok~rpn~zqoulj`oznz}~|m}}{s{qbi~{cupn\[in[`uthdqnfwd{vld|xxwtr{|ur^qoadX[}n|zrxrz}np_[owuy~ze`xihZOWg\Sfn``boatgqrjuz~op~lpia|}rxzz~~y}l}wjtj^m}|{hw{pn]Zis_dumfioqjwe~{ke~|xryxqpaljbgY_r{~ytwmzy|~c|t\^ny~tqjarhh]RQieWdj\ddojpnozjs}}vulxlrod{|zvny~|}|xkr{~xy{pbq`lz|trownpdZ_xmbnlaolwnupvvev|syy{ygkdibld_{y~pwuxts{{w{hcf^mym~ygjghffZOZsc]g]]ljtosu}vm{|{ouoopynsn}|p{~{}|tksyzwru]p~dn{wurwompk`Xszhii^lqqxpxxir}}uypcklddond|{vpn}pvtyu}{qj\{obr{sycqk`ghaRYvl`bY\olswoy}n}{~i~qku~tz}ju|}y{|{vilytozrlaq}gu|tshlrma[y}mi`]pto~o||}jx~w{~v}p^ps_hupl{wis}|vsmw{ovzifezlny~wzc}g_liaQhymd[Vgtiyssvswvpqhm~}v|so|x}xwyodnvkm{hghwrq}lvewso\myna[k|nvznstr|~xt|z}p{mce`ttnuqjx|{{kqypkjdloqoyyg}u\omjVeyujYSdvjp{ltxwx{xnnh}|y~vpv~~wsz}scm|pke~gdqlqt{{s}h{xuaqr]Zm}nt}outuzzwpw{~{wxoif`tpr~ryppq}}z|juspiheugntwu~pycsqn[kyznWTgwjm{mwyvz~~~u{}ystg|x{uwuq~z{n{|xdlylmb{l_xfkwyxo{zzfszaYl}sn~s}vtwzx|mp{ztrozr^rqt~kz}}ssptrop|pqfww\ykbwytyorsudhw|zbRausgxvwwpz~{vr|u{{jszwvx{xuqlw~z{z}oftlief{^mu\q}w{{sm|s\aw}os}x}mu}x{wjow}~mtvwmfsovh}~yt{m|wv}nusjmdxoa{ea}zy}|ytuteqw~zaVgxqju}uwj|un|oz{quw|p|x}uppvzx{wwznipdgbduckw[p{ysq}w`bw}rq}~wjry|ziju|zlp}nrlrqyi{wtssv}y|{psodiamrcsp\z~~tzrwgqwz~l[cwunoz{dy}{kr}l~szxx{xwx{s~y{{rusxupspjnb_b^jketlby{{yn}~nboxrrxb~{qcrzyy~|c~ulwsr~lz|xr|uy}xy{xtuqrp`gaekljuemtwzpxiqzyvccvyqmysnlmj}~sqrxx{vwx}}zv{yvqqvvnllkpa\a]eilmse}vzyo~sgqxsn}|by~rboz{y}~d{yg{xqo{~vr}z}z|yuz~wonovm]g_ffplqlotw}swix{z}ugjzwrl}tlm}klquoyxx|~u|}}q|wtznqwtjfhqqZa]`bjqjrl|z}qz~qn|}vpuxwf|~kd{yxtiqjuvvu||rw}}|u|uuv}{qikvy`dbbdhvhpmw|vylt{{yplx{tnp||zhysi}~~|p|lz{zwz}{{qt|isovvlbcqx_`_^bbvhlqrrz}wu~~wpr{yktpfx|zyxlwwf}{usuysxyyztxmyu}{pfgy}bfabcdxgiro}lz}zwtu}yslsyunssm}~}uz|l|u}{w~zyypux|guqwti_cxx_d]a_cvdfsm|r{z~}wnwyssp|pkzv|zxrrwhx}xowvtzww|tv~kw}zoce{fiabbbwjbtlvn|zvw|ztks{ovo}up}y~~zt{qw~s|zz}zx}rp}|hu~twuk_^s~ff^^`]qn]qpow~z{xosmwsu~unxvuy{xpqrk}~qsyv{wzz}z{rzvp}z{sf_nqjf^d^kuagwk}|tvu}wom||qpzpzsz|t~zpzryztx{x}~~~qvx}{iv}vvqdZ`zwggZ`]ark]trtxx}wmz}h{uu{ss|oxyzznmum|oyw||{x|{{t{vo~{xnb]npnc^c_jtef{pyxutxnn}xtl~ryu~zp~~|mvxu{wy~x~xtuzxwnl}{uocY[twjjZ_]_lqbn|u~wzxmw}|lr~s|tt~pqw{{uhutssx{y|yxzxv|vu{pc]e|vtm_jflwyj{|{ys~zupT{|kdmhcwuxtz~r}ocv~}wrslo~ul_TPeukqcagfpywoyy~v|s~{}rssxz|j~zz|oiogk|zjm|szx}ww`dve`ZbYisj`TNVonnm\cafnulo~v{ygfiawfUchc|xgrkw}}|tz|umusij|otroth_`u~{pptv|}msruxwpfh{rfruqtfxyt~zlcguhqqszxrddtmahYirdUKHWhcnaX^]dipjnz~|}oa_nhjwiwropnnj}z|||}{ts{p|hzmjs~|kzpwnc`l~y}putz}}}zys|~ythdqyhmwm|~nm{y~shgvmgz||n}|tvgclyhrkexq]OJUfaniZ_]celqjyxw{|z|sfbofav|qxsxsnj|vwz}{tsysuly{okqvzlzytpcao{vrutxy}~~yrvt~uifrzhpul{zsl~xzrkmzjfqqr|~{wieknt}q{wn}w_QO^dasf^_^bbhpjvqvuu}}{}}tjjtb`im{~|}||nn|stw{rsw~ttsy{pnrqorpzxsecrxt~uvtvuz{xnmwxlhr|jrtkxwxnx{urmp|lgkg|xx{mhljhl~{|xzkXR_e^qkab_a`copmzo}wq}{~ztmlvj_f`u~{zn}vqt|vrvy~sxyy}~sosskjpzs}ldlypxxtuts|{w~skr|tkn|toykrwvyovonnzxhncj~y~}|rikmfch}~~hYZhadthgcab_errpwv}~~ut}}sljovdee_w|zuuqtvtv{zxqx}yyqpvsjepzt~mfqxoz{vvsr{~z~y{smuvmq}ttzjsuu{r|xijn{vjqcj||}{y{zohkoicf}|q`^jbdtjjhec`cpyq|}xy{yw}uwkmkapye]dqvxyhqz{tqwy{~uwr{{|y{{ol|sioud\c~Z^_cdcjy}u}z|jp|xwyxrlt~q{ypnsgfiptpygpxz{zxjrzqjs~xxeXcpwzyqojjr|d`yxpkYcbcmkl~wber}zllv|{vssts|}q}wsfQRgp|nbXRTamihsznxz}~{xnw{zu~|o}mlwzxqqkfs]bzplnou}ym_hxp{zmfj}vghb^lpv~xqowzwsfkptjiqpkys]_p~tyyeipxyvsqp|ny|x~}szzvp|f\[T_kpwjabmungz|i~w{vq{}|~u{|rmrlvvz~zkuu[n|nikpy|te`sps~tmhrrcfcgnk~upr~}xw{lfq~~vmrysqzj\g{vu~kflrwvttrsytyx{x~|t~v{ruu\[Z]ed}wkhnxwgqls~y~{|zpt}u}~~z{qmmwpnwqy}cj~ufgnx{se`plq|tojozcdhjjdzzss|{vzndjzvz}syp`hzsx~jgjossstsrz|yrzwqv{x|}zy~}wy{_\`b]Zu{tpu}|humw~~||{u{zps|w~~{}yiqzlfl~vkmzfdmxzrdcogqzrplo}~eakpg`tv{wv}}|xwrfgqyyvwghwqx~lhilpqqttqs}nwyoo{|~~x|yz{xhZbg^Ujpx~xzspvu~}|}|z{s}~{}urw{}{~~~|x|mh}sfeqyqwscgszujbnhhxtqpout^esp_ksq}y|}yrrnhkqv}xwwjqsrujjilnoruspr{htmqyxx~v~zx~|aXfjY]mk{}vyy}||}{z|~o|uyvuxyz}}}~zs{fh}qggt|wxtdhuysggpcjwrrqqt}s^dvobqqq|zunkmknqu|{uzrspstllkrw}y~pr|xv~}{wnx|qiv}{xz}jYfp`cspefp_jtgs~mqtsvvwxs~}}zrsn^sqebk|~yxmV[hfZZ^ajn]Uipbmrn}|lxtdiorvx|~`Yc|~nc]XQ]oXXondcmzyvuuiq~|xtvt`]rpk|||tmhjxpq[Ufn]Vahkkjo}x}vsz~sl~zap}xtzolk`STkrzvnfbd]LWg]eleo|~yx|{~ww|qvjkxrcmf[do{|qads^Wikebjt|}t{}rkrsniwx~wvwibvvvjddint~ohrxeXclmjgmyozsu{tnxhs}{yvc`_YS^qo|}tmlfW`hcnidpz|yz}vzsv|}qx}hl{fcm`^hq{uhrsbcklfgnu|u{zxz|plpngl|vywvvjkvt{|wgdgkouqrzxa\kqmffy~xsqtpuy||rz~srx}{{~qda\TWjiovppf`gepmais|}zy{trrx}~wxrhv||pfob\ahpxtxxnljmkhlqvv{xzt{vmmlfiwoq|vytnqt~ukgjkkmw|z~l_ktnedv~zwts}qnprsv|y|zyv{{{|xjd]U[f]g|wxsieiuj`fowz{yz{~rsry|zz~~vjw~vytorb^_aelx{zxmoommps|ux|qxzqlidktgl{x{|tmt{qyqjlmkjpyxflwrfeuyvwyt{omnnmnt~|vw||~}~sg_X]fY^q{}vgj{padjrvyyyz|~vzwpvzzxz~pqxutuf__``clzwottrqrxw|w}ro{xngchtiesy}yplst|mlnmjmrz|wktypgpzr}xvzqjlmljlt~ytyx}qbZYeaWfr|weu~ldhmrvxyzz{vtpvyxy{~}yrtx|}wo`_`aadm{xsw~njvwi^[eiZ[fn|njru}wvj]\agjej~ztxx{xmt{uo|z~qmruwx{oUppdfr}w||~v{~nfjxrejhqy}kz}x}uq~pe`bdb\lvmmomptkeotnq~xs~prx|}~yntjo}potzulvypxynwxickuf_hpw|yi~~ml{vlhgg`]pskoonrvmisuotyko}sx~sX_st{||zv|v~vlw~p`\hk[bp|~{ujjuxusnagvokmjhmnddllispi|vsy}~}|~~wtaXtu{tv}nl{zh\`lc^ny{{nzsmv|zwldtxoqrnovsjmqnrvioptz}|zyz{yrogS`~|xylr~r_Zfh]hx~~w|w|ujs|~}zqdlxompmimrkejlnvnksrx||zxxyyurp\]|vty|nkyyfYbj^ct}{}r|~|ou~~|vhhxtotsnowukjnowrhw|youz{yvtuurnn^Tn{{{vmv}n]`kaar|{~vxxkp{~xietrmqpkiptjdhmvsgu|~pty{yvttusrteXnxxv}vkp{p^^ja^nx}{||}~pt~{kfutotuqms{shioyvgqypqvywsqqqpnqcUg|}|{pq|ucaka_nvyzykpzzziftqmrrmiowpcdmysfq{rswywsqqrrsve[lyxx}xmnysbdj^_lrz~}~|nv}z|jkvqouvqmt}vggr~sgr~wpquvtqomnnqsaXhz}}~}sqzwhhl_amqy~z}xjryu}|jitolrsninxvean|sfp||truwuronnoswf\jz|{z}~zqmtvigj^_jnu|y~~~|nu~y|olwqntwsnp{}mcn~xhpzzuortrpmkjjnseYdr}~yqsyqknb_kpuy~~z|{kmzxvsjtqkptrkjr{q`dwzikw}~sruuromkkmun]cq~z}~}yqmrrklc[enrzvx||}~~rm|{|ntwnpvwqmq}|jcsqju}yuopqpmjhggmn][ivzqpurpk^botzv~|{||zx{ugp~~{qmvpkptqkjr{racxxjp{tqsrpmjhgjqh\gt}|~~|zsklpom`Zgqwut~|zz{|z{}ol~}ovypqvwsnox}oam}slx}yonomkhecbgk]\kx~}tmossm^`pyuwzxwwvtwuhnunwtkosrmilvwg^nynq~zqponlhecckj^fw~~~|{wnfioqfZdt}zov|xvuuuuzsk|st|sotwtolqzwfcvwoy~slkkifb_]aia]n|}~xmhnusb_q}yq{yurqqptvjowoxvlnrrniiqvm_hvpt}smlkieb__hi`j|~~{{ypddnui[fwxnq{~zurpqqtzsnsx}sotvsnlowvhestr~}tlhgfc_\[ag_bu~~}xldlytadv|vox~|vronnovsjwsr{slorqlijptkblrp{|rkiheb_]ajdbt~|zzxmadtvc^o|zxzlqz{wronoqxxmvxvypqutqmlqvqhlrpzzz{rifdb_\Z^fa]n}~~}teev}i`o}~zopy|xrommnuvjqws}xnnqqnjimrphjnmx}{xnhgeb`^ahc_n}~{{{sdar{h^kw{wnmv{wronnqxwlszy{qrttqnmottonnmyxv|ukecb`]\_e][kx~vgexjbny}{qow{xsomnqxsgpwywoprqnkkmpromijyyxzohfeca_dh^^ny~|||tebu|h`kt|zyplsywsonorzuir{}|ssutqonoruurjixws{ynfcb`^]ae[Yityzkeuodmv~~{vmsyytpmnqxtgn~z{{qqrqomkkmqurget}s{~ujfdba`ch_[iuz}}}ymbm|qdiqx}wxmmtwtpmmpxxkm}|vtutrponnqwyndpps{vjc`_^]_d_Vapw}~uhj{ykkryzslrwvrnlntxmiwywqrrpnlkjkryrbg{vqz|shca`_`feZ_ox~}}}wkdoynint|yyxljqtrnllpxtjs|vtusqonmloy|lbrmoxvja]\[\`d\Wft{~}uijwwnpu|yuklrtpljlswmkzxxrrqomlkhho{vcdzyms{uha^]]_ee\`r}|||xnektqnrw~yw~zpgiopmiimvvmt~{xttsqonlijtubkphpvoc[YXY]daZew}}}xnipvsuz~x{~zogjonjhhowsmzw||srqomkjhehv~marnjtwma[YZ\ch``r|zzxqiipqsx{zt}{ytidhlkhhktxqvzyutspnmkhfo~|gisfirpeZVVX\dc\h{}|{wpkort{w{~zyqfdjkighoxtqw}{srqnljigcfvodtmgoum`YWY]eh`ex{zyvpkmnnw~vv|vwshaeiighmwwq|~}wutqomljffrygnodhpncXTUY`f`_p~|{zupppnvzwwvwncbgihhluwpwzwsrpmkjhdbl}|ij|uhgprh]WW[bic`o}{zxtppniqxt}}tsvoc`ehhhktwpv~zwusommkgdl~mjyvhdkpi]VUX_f`\j{}|zvstrjq{wutwsf`dhiiltunt~yutqnlkjeck||liwxkfmrl`YX\ch`]l{}{yusuoenwv|rqvrf_cgijnutlu{ywtpnnlgfo~|nlwviejpk_WVZbe[[jw~~{wvyqfoyz~srxui`bgikourkszwuromljfdm{znktzmgjqod[Y]dg^\jw~~}zvvyrcj}xx}rotuk``eikouskr}zxuroomifnz{qnv~wsfgvzrmiljgkpszvtwx{zyzz~yx||}ryeptYdszvpvxw}~xpt~zvvgax|}w{{tqztkdjljpvu~|w}ytxrvvminu{{yt~x{yiqi_rvouw}{y}~aiwynnuzqpwsot\`ivpcelptbiuh}wlrvxy|x{s\Ug|}~kjkZabn{xjom{w}uqxupuckgqx~zv||}{|t{vq{vyhhiboukpwpxqskxqsxhhyrqq`jipq|~}v{wv~o_liq}|kyt|pdfc_YkgalhXcdalqv{u~s~vtobceWcslwzuzrt|x{wu~qsuzrtmfnsn||ylyzvstir~}~}uqvllqab|skptpz}jks`Ze]amv}vvjWftqs{sdonbdgjkmgsyhktgluox~ukune|z{spnnjh^`wwvqsxhr~u|syy}}wvm{wednt}|~~xy{ustmfsy}zquytxelphuykknx{{qq|pfka]nmxzv~eaeZczzswuhphgoi[`m^cyqfojr}wpv}t{xu|}yzvhniWM`^e{{}~}nw}y}zu|witthfdsy}}w~pxx|~uoqzsq|yyry}{wnyumw{pwmpzvgin\evfX__r{{{zjom`dsuqq{hjagsj`hytxn_pzvpx|s}vy~{|wvulitiZZjjtyo|~xyozwv|yyz|ryuqqbptrl|{x~zuu}{wglmgppu|~viol]osljdvypm}xxc\ptn|yuw~~nnjUarednq{pv|ujbplmtkqpfmwpwox{vuo|z|}tvwk]f_Zggvyzxywz|rszjrxhh^kunj{}}uy~|wpvotjk~~w}}t{|~tgyz||umu{krkmna^kgpqsz{y{wmXb{yzpwpqreqiqooxqhrxzzkzxrksoiy|~ohfui_We_kui{~x|q|grum~o|pm{b_alxq~ptqz|t~mnxlt{pvlpwimjqrryrqxmjvfgkvjqv}rs}plcZsxfYt~}{fnqejlncqz{bmuxvlht}oly}wu{{|xwmwgSbV_Ylz|qv|pxtqwgshqsrslwwsjz~ujsnk|ronnuzuztpsk}bdpzvtd~trslai^bt{~wp|qy{k~xwoPu|qlkurjmvry|pkww|kkqdjd|~}{ssunq_adRgm`yozsu{ywss{yovrjuwx|w}}{xwt|s|luukmibk}{~fweld{|l~x{|dzx~olncnqps}}txrnX\wt}||utzxmxqoyqxeu}psr_}fnd{|yzz{s|ofqZYbV`zmvz}|~{v|rsyxxotjhtu{pw~v{|oy|{wz}ntkgiszzx~nqmmyvzmy{|wgmlcngpgo|v{}xzt|zg[yptvl{x|pxsoxt{kxsstdolfvxrzuyxxtjpbV`VUs`irzx}wqy{u|w|omvtvfzyy|xu~~{z|mokjfqplmx|tsn{q{lsywkvpophq`gty|z|jdvrmj`k~~{{u|yvnw|lnod}jghyynsx~y}tvxf`eTehXY]w}u||x{}~wmrwujgttlws~}{wyz~vwpuntnvnlizrw{s|}y|mvqyqpmli\ghqm|y|}y{}fv~steanmps{z|{pusmoh{rkg|tv|ls~|{v{tei`apZUS`oir}y~z}y~xw{zrjvthifm|xy}|}{~sutwqxppkundnrx||lxp{tupsrbkfmgns{yz|p}yodki`dgunz}zzlpizoli~tt}jp|||{yonhlye^Wdh]apuz~||uqrsyyztmhztyubcw{}tx|yqnpkc^Uesoszz{}~{{tn~wrsfswqhyqnvy~}|{qrznqlvmegb[iumvv|{~wzqtuk}wozph|{xy{s}s~nwxr}lwwhnneprlstoy{yznnwvlrm^rxfisq{~~ty~rryn`fUYlc_uvz{~~|u~zwpzxlkvmp|wyxzz}umuqdlf^_bfqtm~xzsszmvvfuw{sgmty{{||y~zkpr\eugcmfq|}v|sjunpwut|wtyiyw~w}w~{p{{{dbdedpbWjngz~x~wh}wuytxsmjvkmwlvwu~rszwynyqdicemmgnxqwqt{zvvnzs_hjfmv}v}ryzz~xrmsfhkYZptxvu{vy}qrz|pjn`kym{{|zz|{p{whkf^`eomofoxqy~x||~lk~rllupw~uz{~z||okscgzo`e_ckmip~rwvstuznnwpstk{v}}puvmiskol\dj^cnju}xvyjwoqkw~wms~wxjd{v|{x|ztotiliaji`Vf`dyy|{s|whoqllgvv|}~y}}owvq~}xq|wub\ovmxmfh~}}{txrioajxfgbr~z}|}vmxqnjjukmelrgdmoy~}~otxpqosqmwtm|vptpbslc_Yjic`qzw~st|y}{ssyquhmxwlkvm~~uzr{zkqhjk`]f`iho}zuxssmx}suvcm{~zy}|szxxqermh\dZVmtcfgs|{|syuvymzpsrq|xq|~x}wts}wkhzzqsjg`rggmo}}xvuviwqdc`uwpl}}vy}wpxwosjsqdbljo`ituu||w}s}ormvtjjqou~|w{nvyhf_ovnfmxpse[n}x|vqozrmgncl|lqqzvnsukdXW[VPVZhjhlv}vs|ztwrsrswvqrbuvkv~nku}z~zyfejutxzcctfuwfpht~uo~oywwu{eqdpq{z{}uqeq~kcui{vfvxxpo}|swstjysm|gyzzb}a`njo~lpvg}nUgVr{acvsyv~z|~}mkzbmps\ql||wszrlkqsvqtxXvptsfvv\ov]_Z`cQ\YVr]_solw{}ox~s~g{u^eYi^i||y~}nzztxzzzuyecsurnd|n^VTYcXVNoiYmvounv~v|ttmi`}c\ZYm_xv{zz|~}qoxrZm~|yepyd^_YacVW[vZeuprr}orz}wqsy_mi\fUdik{y{y}wxufpxf\yw{cqogc_VkbTTjrVpromypsvstusns[vx``gZpgyx}z~|{~~lcqrffxrftkid^Wo^UUonXvyrxnup~tzrstlo_wqac]Zxoy|yxz}~{z\`vqjjvmiqlhbZYnRUcvcYyv|ulw|ozu|yvuvcgwj`c[n|w{qyxx{gXmwoozy}{gjiqkcRfgOajrYdtq~qn}tsx{o~{r~udopgg`^{z|w{z}{{~{|qwV]uquqypigstfXUoRTlqgVnlzzptuu{~tww|ihnijh_m~}}{~}u~~|}ny_Tlqss|yldj{o`RkaIjqp]bluolzyw{o~|tqfggmkbj~}}{~}~}zw|xlpgThrqxstgd{thVflMftib[gp~qk{{xwvur}}vmfcmnekxzv~zy~ylk~nXcmnwo|lczjWeoP^wfh[anshu{xs{qq|~{rd_mrelvz~rx|zji{nYcjmwnoczk[hoUa{bgZ[ougr|~tvppzx{vc^pqdnr{n}~y{|ikyk[chotnph}k^mnWf~^fYVo|qfv~|rxmrzw|xb_ttirq}~m}y}{{~llwn`bbnsntip_kk]c^e^Rn|tgqzow|mqyu~|d[ruirl}lu|x}{kjrncaaoup{hudpmcad`eOhzsgnzls|}s~ryt|~hZoxmuny|~p{t|zsvynjooid^hun}i|jnmhbxrYkT^yulgoow|~oxvtxsZewpurvv|zxo|syxoxojklkh`bunzquqlqign|[dcUtwqgysiz|xpuvs|b\ptsxp}~}wmvwuhrpjhhike\otso|~}munlmxqYkYbytljjpuy}twtovq\dts|s~z}|{{xpzq}nteoijgekladypwx{xqsipsxd]iYrxtjwugz~x{|w{vtp|~i]ksy~s}}}~vqwzo~}qfejghccmm`mzttzxturjvuvaah_uuqlnhy}yzy}ytop}ye_mt}z}}}v}qz}nxtu]cdgh`dpmbxyxzxuwnm|ur_eghvtoqhowww|xusmt}sbdqyzxwyyvstutuhsXbahc_jtil~v{~u}|ywlw}pk]iiqurn}vfz{xr{|nupp{{mbir}z}|u~{r}snv|wm_\_eh^aqsi{xzuyzppvnganqttovhpx|zqppsmv{tgem}zzsyy~sws}wusinX]`h`]kwmv{ys}{}vokm`iwssprpkzzkyhvot{xmeiutu~ut~sp|qznux\Z^hc[dvqt|uv{|}xp~nmfbzvrrpvi{{{znzduqtzxphgptu|uups~xsuqrvbY\fd[asstzryy{~{q~qjm_z|opnzkz{|wrtdqtuzxqjglvuzttq|x|uwqrsseY\fdZ_qtvxp{z{{ssho^|~opnyn{|}ussdpuw{wrlgl}tuxrvqy|xtxorvoh[^gcZ^prxso|{z{vqhq`onpxq}|}trwdpwz{vrlemzsvvqxswurzlrxjk\]fbY\nrypn}|y}xvfsb~pnoys}}~swshlwz|vsogjytxvqxwruo|lm}gl`\ecZZjrzqk|}xy|dsgxsnn{v~|}s{qohv{}wsqig}xv{wpv}oxk~}qeihi[cc\Xdoywh|w}zinppzpk~}w|}~uxqzeoz}xsrnewxxxqsrwkt~yaxv`m^`d^X^lw}h{}zx|tgtotjx~y}~~ysvzmhv~{tsrhpyx}y|to|}n}qk|jebgi]c`ZYfqsp{t}~jnuxomz{}}rx{w}xil{}wstpjxtuyou}uv~}ir{y^uw\lc`b^Y_jzm{u}zhsy{jwyy~~xqxw{xsfq~{usunqww|xvo{{pythxzp\|k\lba`\Zco~tuw}~uiv~~xj}}w{}~ssvy{tses}yttvovtv~~sq~zpxnh{yzh`}c^kcb`\]guz}rx|rj{}snxu~}zotr~wspfx}xtwvsxrt|pvy}{uy|zhmvv]mw[djdb^\bly{ry}}mo~ly{sx~}sprvoujj|{vuytxt{v||vr~vwzy|oguwxj]vjZhjc`]_gtsuzwj|wwo~ts~xnsmxnuft}xtyyxxqzv}|ryuz}zwvvhn~zvr]mr]`mgb^^enysw}nr|~o~wpz{nsmzivlk{zuwzx{ns~uwxv{{r|kjx}tvbfrf[jmc_]cj~su}vnxszxov|oqmslrsix{vvzz}n~s|xwyt}o|}mn~vuytdlwjb`dg{yxypxxz~yqy}|{|yznenu|q{w}mwv`c~swhjnm_aodZUZ`s~re|pia{lzqtxwrhqqtnqrlxmr{wrplpks|}sYWsndOUTXNPcYPNUZnvy}vpzuz~ypz|{xmfd\}ksum}rdvmopnurv~}qnv~ygszi_dsqvmqgeypdagl~mgq}mzyfhhmljcZdy|rekbkqesmhnkiilob}~|{xcW[j|uudaZN`\NIMRbhotwzoq|t{}zttoiddgYszz|julkmgoqu{zpkoopg~{lbcluy~tnterzlcgkyuhix|njvrlf[\oxgjgc~~kjxnjnllmsdkn|zi[Ybm}}fl_Zh^PNR[fet}}toryvswxqokhdjb^|{zwtxioigot{~wlkrnkjz|tfbgnuzpovit|ngjp~}}qij|ok~und[`swglbh~{mhypgjkmptds}nuztdY[em{hnh]jeVSVaffx{|vrtuusux{qihgelfc~uv{{jpjelt~xmjolwphqwrfbipt|}{wlxro~wlmr}y}uno{|d|}rg]^m}}kmgcyzufpyhdhloxms~~z~rknoeY[gqyqgpfcmcYZaje{}s|xwvysxv}q}ze`cekphzpzonndhq}~qkm|topkjopgbhsvz{{ms|rwuqs~vyyrt|hrxl`^iypnj`uyxmhzpaekpzrv~{~ztpkegbY\ityxiqremmb`emf{u|zzsyrxt}pv{e[aenqll~qqndhq~pkm}vnioijldblvwxv|prwvyuw||y|uvsvjv{k`bmz}orffxvynizq`dltzp|z{}{kolbc\X`nuyzwmwwky|wx}pnngjdojbcesslqw~{s{nxxb`ipyqi^a]X^izyomvyyiq|swurv~x}|ny|{z|uxvwo{yrtziU}XIP^infiaXkknpemxg`ku~s{{wp|x}qoronms}v`e}o]Pfhiqnlhflxrgnvum{{rqt}wqjz~|syyrgpzx~i_td^hsre\\fnip[^qfddekuxunwqo{qq}z~zxf_t|{yuoceTQa^bf^_l_V_lqfvwn~}xvqvy}utt|umuhcw}|{zslebhsm_exlduqkkowtyrny|y{|ms~wxyqfviellgs[Wph^^airtpgp}xsn{wjy~}x~}zulhjwye\iccif^ggX[ipetwkvy{{qpvpszy}ouubhwlegptf\vxaorllov{}wwqtqx~}voyx~uwmtvmnrgqmTdreZ]epurifur}zpr~sj}v|}~zlnknzbjkdgmi_ifZ`ojj|pmw}uxxywtrms}vxnuudkyngjrtd]}ubpomnsz|z|~xqnxr||~yxoyv}{prsy{surgumXcshY\fqvsiitm~zpp|wj{u}{~~}wfmqmippgippbfla`njmtpw~}spw|qvio{w}wlnwniskkrvm]v~gmnmou|~zywqfwxv|~w|tu~vzmjt|yxwiqub^mr_Xanvvoisls{rms~ts~uy~}}{whbvuxspwmjnunbjncili~}rvz}uln~uuliuz|uxmiryoz{pmqutdkqlmlou}|wzvtcpx}}~}u|u{vxmeo|{lpwibgthX^mvwqkrjmzsno||tv~|z|xvg^u{wwv{qmpxtehrjjij|swy}~}|zsjj{~xumhtx|yp~vmip{u{||yoilry{tpt}vuo^pwx|smtlxyqnhzruwz~vo}wotb^bkzpdkx~}uwzo~~yqsyunp{zwplpv{~uzmm{~reXbnif`q~qrtuwyz|zrhszx}vqlwrbes|}wy~sygfy~jacbf^Y`hsyvopy}xzgh}sy~|n{rq~xpgwz{~zos~vdedSQ^hjegl^j}|zwtqw~wx{}uqv}zulqytesy|pwqaUerefhhijlooi`gzo~|zy{|miu~{v}ztkho~zm`birupihqzrtubcu{ml~~{xs~~yrg|z}~wtwtv|jbimlfjgZkwvvusnr{ut|~vopy|xmq{~rlwuwn^puhiigefimni`iyo|xty}tls{|y~q~xnghs|igmuxrjhovosucbo{oeu}|zv}y~|ygzy{~~}~}yyrn{mlonimk[fttuvupnw{sy}zzrnrzzroxznywsygmzmkljfceimkccw}tt{}q|}{~uqw{y}tw|ulgkuzkmv{yqklspmwlafrxjfyys~{zqn}y~}{|~||zmtspqnlrf]msstvtonx}uvw|z}}wqptz{tq{}rp|xu|juymnnkfcdiljchxyxuw{r~x{twzys{|{xrlinwppx}{snnsmmwkcfoxoeu~x|q}|vq|z~zz~~|y{{pvtfnmjged_Zbqphk~{|tjp}|v~~}urtm}ns{qqrpjdbfkkfguuxyow~~{~|px}y{{zyzhioegxyopiq{~zusvqkupbadmqgfxvjq{uws|zvjdplmz~shsyrnoqyvfnurrssngempd\x}{x|{znfjs|sqd\nurqv||~xt|rqutnfa`_ZSSdn}snr|okeegijjkmosxru|~wnhf`^rxsuux|umzurnhq~xjvqbjuzzxvx|ridg__zwqjhq{n[nvotz~xhi`bfiqsikzxxw|}}sz}}st{uib_]dngr}on|uxbu|kfd`_hyycivvy}~~|yy{nmxupse`cektqm{{neiy{l__h`fyofq~yvy~yoxzx~|oilk\h~~ztptspwmn}}mgaagf^dnlfi`Zo|zhdfhjotz}ztv~turxtjlnbotnsv]Ohlbeltxyyxurt}}wsvvst{~vsmjmnfk|ohjlpvjcqk_h||ww|wpywow{qzrlstfoullywv|{xssz|wqnuyolsroqdaszzz|pmnliffkpqq|zy|ukoiYds{iatqihhhikmpppxzwsliloowptyrp{f_^_agszuzgds|ztrv|wuzs{}qt|kdnofs{w}tmyyzxxz|y}rpumdiosvigy~prvtwvpkhlswytrzv{}uwq`fsxvitwrqonlklljfi||vssrjs}{|xqr|~vyvhfeb`bkx|}mq~yrpsoitxqx}u~}npsefsvyv}ytolmpmuvxzncfkqj]l}n}|jjmmjgiov{u{ur{zskoc]mu{}mjnmquvusrssqox~{{xvwxsn|{|{tklw|ux}wpeefdcemwxuyzyztq|xosz~~xrzrtrekvvw|zrljklfo}{lkt|req|}~o}kghgdacjr{}{~w}qeeYTfrzzxumprqnllorrrzw|uoosyyu}tqz|sz|j|m^^cgjnu}}woz{z|xs~~sswzyunlxvtwko}~yvonqvsvxt{shn}|ko{~~~}|~xtyljlllmptw}{y}y|nibXgw}z}}nmrrojhilnpx{uqsxxrz}~xrwxu}pvxc[\`dhpy~qr{}zx{yu|{~}xqt}yrpdfx{}u~wv}pggmttwwpt{sei|swumvifilmoruw|xv}uosumh]Whx~|ysrz{wspprss{~|vvqtxupzxx{{vt{{lwtcZZ]`eksy|{~vt~uwz|||xqrzyuj]gvz|yrt~zjabimly~zzlqypznr|ndbeilqvz~~ynglokaT^s}{}kqyyuqpqsswzss~uv{|uz}}wu}x|no{yod`acfkqx|~xr}~trtuuwyuqxxfiy~zw||wzlhkmiq}tq|rz~}xz{pihhijmpvzopwwiU[krv|~mnwytpnpru||xvsusls}xx~yw}zywxzvpe]Z[\`fmw|{zzz{zy{|vor~sbjwyy{vtzzt}zkfhe`kvsx~|z}uz|zzrjgffghipymkv|jU[hkmqxzw~xtssu|y}wysrngnuopx~~~y{xrqi^XVVX\bl|xwwyxwy{ypmxxaeqtuw{~~xtu}oedb\frqt~{uyyrvumgfghjlp}||zpfkzv^Zgnpsw}vx|wtssxyyxw{wumoyuqv}~}}{|~rqtla\Z[]`fux|ytvvuuz~{rrugpxxxz|{{w~~xzsjf`]jolr|x}||syzqheefgiotim|u][fjlpu{vu}}ywwx~}yztohmwsou{}|{|~{~mhoj_XWX\`fv{|{yxz}{qlzufnuwxy{~xzvry}ujd][hokox{~z~nqxticbcegm||z{{thdqvaYagjntzy|}ywv{~|yx~tmfivwrv|{xx~p`dibXUV\bivzz{ywwz{ukm}{jjswz~|}y~~~vnnvxphb^jvtu|yx}vpkuyqifgjmr~}yvwxukclvj`ekotyzx~~zvuy~{{|sjlxztuz}}}~~uyzvbcki_XY]civz{|{z{~zqq~}ootwy|z~w{~}{souyrg^\jtqszux~{oisxqgcdgkr{yyytjdkpebfilrx}|z||~~y}ynfnxtqtxz{|}}~w|u~k\aif\VW[ajz|}}zyz}|votztpstuwz|~{~womtvm`W_mmlryxxwhjuukc`bflz~~zxwxvofgjecffglrw~}~}|zw~~qejvtptx{}u|s|o]\fi`WUY^gw|}|zxy{{wrsvsvywvx{|~z{~~~~zrmsysdYaljhnuzz|xzkkvzrgcdfmz{{}~|yxxyxsnmjglmihjnrw~~~{rfmtnknrux|v|xsb_hldYTUYbr{v{}}~}yxtrz}wttvwz}z|y~woqxueX^fcafmrx~|}miqytib`civ}uv}{zzyuqnfclpjffilpv}~zzwgjqlgimpsw||}vxg]aih^TRT[juqr}}~|{slu{trstvy}z~}||rlqvn\Yba^agmsz{ujirwpe``dp|ys|~~|yxxxvtrk^bpqiegjnszyysioqkjmprv{~wwrd\_hh_VTXaqwqv~}}~~~~}qjywuuvx|y|x}~}tlpvo`_eb`cinsz{xnjqxujcbgr|xs{~~||zyxyxwwvn^`qumhgjnsy~}yxlopjiloqty}~ywwi^\chcZUV_owqtugqzttuw{x}xx~~wmioqe_dc`bhmrxxpr{{tnpyuw~}|}|~|}|{yyxxy|{ngyv}lhpy|urpnmoruwy}}wxr{xmfchrvqlkp|}uvxsqsuvvwyz|~pu{susjn|||}|yv{|{|{w}{xvwyzuntysonu}}wrsyvv|~}zwuv{{zyyyzz|sm{miv|vwxpkpuvwz}|wx~|}|vrrqpooomjinuy|{|wtw~~|us{toljjkmoqwzrgn}{zz{{xrkmtnfgr~xpklrx|xpmu{wrnkjpwwroqy}vquxwtqpqv~}~vwywzshen~zxzzyy{|{wtuyyz{vqpqpoonmjikpry}}yw{~{vqps|wtrqqqtyujiv~zyyz{zunourjfkv~|zvqlloqyvqv{wromjlsvtpqw{vruxwvtrqsz~~~~xwzy|wlgjt~~}{zz{zwsrtwz{uppqqqqqpnmopns}}zxyzvrqwzwutsvxsjkt}}zxyz{zuqtxtlikqx|~~~}zurpnkqzw{{vrpnlmrvusty{vtxyywvtrru{}}~yy||~xnhhmv}~|{{{xurrv||vrqrsrrrqpopoknx}zz~~zvroqv|~{xwyzvons{~{yyyzyurtxtnjimrvx}~~~}|yvsqniju}{vtronptvuuxzvtwyxwvusqqtxx|}z~{{}yqjghnu}}|{xtrrx|ysrrrrrrrqpppljqz}zz~}zwsonpv{~{{|ztpqu{}|yxxxywsrvwrmjknswz~|~}~}}|zwtrpkglw{wvtqoqtvvvyxuuwxxwvtrqruxz|{~~z{{woigiow~}{xurrx|ytsssssssrqqplkr{}{z}|yuqnmou{}|}{vrsvy}}{yyyywtsvvrmkknsx|}~~~|{yvrpnjgkv}yxwtrruxyyzzvuwxwvtsqppsw{~}}ywwtmhfhmv~}{yutw}|xutttssrqpoolkpy~|}}}}{xuqnkjms|~~~zvvwy{}}{yyywtrturmkjlrx~~~}|{ywurolkigjt|zywutuwyzzzwuvwvusqpoorw|~~zvttqkgfhmw~}{wtw{|yvutttsrqooomlpy~}|~{zzxuroljjmt|~{xwxxz||{yxxvtrttqmklnsz}}~|{yxvspmkjigkv}{zxvuuwyz{yvvwwvusrpopsw|~|yutsojgfiox}{wvx||ywvuuutsqppollrz~}}~}{zywtqnkjjms|{yzyz{}}{zyxvttuspmllotz}}{zxwurolkiggmw}|zxwwy{|{xvwwwutrqooprv|~|yusspkhffiox~{xy{|{ywvvutsrqpomknt{~}}}zyywtqnkihims|~||{{{|}|{yxwuuvtqnmlmpuz}~}{zxwurpmlifeipy}zxxz||{xwwxwvusrpooprx~~|zxutspligfgkqy|zz||{yxwvvuttsrqnlnrw|~}|~|zzywurpmjihjnu~~||}}|{zywvvwusponmorv{}~~}{zywvtrpnkgfglry}{z{|}|yxxyxwvusrpoopty~~}{yvuurnkhgfhlrz}|}}|{yxxwwvuttromnpsw{~}~}{zyxvsqnlihhiov~~~~~}{zxwwwvtrpnmnorv{~~|{yxvtrpnliffhlry}|}~~|yyyyxwvtsqponpuz}~}{yvutsoligffhlsz~~}|{zyxwwvutsqnmnpsw{~}}~}{yxwusqnljhggjow~~~~}{zxwwwvsqpnnnpsw|~~~}|{yxvtsqomjgefimsz~}~~|zyyyxwutrqpooqvz~~~~~}|zwussqnkhgffimt{~}}|{yxxwvutsqonoqtx|}||}}|zxwutromkihghjpx~~~|{yxwwvtrponopsx}~~}||{ywvtrpnljgffimsz~}{zzyxwutrqpooqu{~}}}|{ywurqpmjhfffimt|~}}|{yxwvutsqonoqtx|~}||||zwvtrpnljhgghjpw~}{yxwvusqponoqtx}~~}|{zxvtrpnljhfefimsz~|{zywvusrqpoprv{~}|{{zxvsqpnkigfegint|~~}{zywvutsqonoqtx}~|{{{zxvtsqomjigfghkqx~}{yxwvusqpoopqty~~}|{zyxvusqomkihfegjnt{~|{zywvusrqpppsw{}|{zzxvtrpomjhfffgjou}~}{zyxvutsqpopruy}~}|{{zywutrpnljhgfghlry}{zxxwusqppopruy~~}|zyywvtrpnljigefgjot{~}{zywvusrqppqtw{~}|{zywusqpnkigfefhkpv}}|zyxvutrpppqsuy}~}|{zyxvtsqomkigfffimry~}{zyxvtsqpppqsvz~~|{zyxvtsqomkigfefhkou{~}{zywvtsqppprux{~|{zywusrpnljhffefhlqw~}|zyxwusrqpqqsvy}~}|{yxwusqonkjhgefgimq|}~~|qsvwtvuuwoqonu{}mtzlfhizobqos}zz{~|zYjslz}rnjqoqxgjrzpwy|{}uiriot^creeob]lhsv|uz{{~zs{syj|pa{wfcwwtzuqr|}yqgguvVdpki^cil~~xzytiosyltlgrvj{rzttvlqtvgmt}jote[qwxo`zsuuo~zendrxlkukov~}izhmmglbdsj`koaj}}z}yzvveqsjyt{zcv}}r{st{f[p~juqvzrv{yztvulyy}}al~w~}~qyw~|rwxnoy{{vOryyyonqs~w{}zr~~hc|m|imw}|b}}k{~{sr}nguo}vw~~sYvktx}x|z}}xx~{}qjtfhxtVQjyuplvkrirxq~lkda}~yzyzlu|_zxrwzwzWkds{|hVeepqdinoop{|rxuckalto{tqpnp~r~uxw~}dUeauu[\knstpgoxdpwszwfjuUwrx|pxusm}~}nus~qK^ek}aqoytnlipsi|}xpxq[vocuzzwtrolvzyzqtzw~~|y^Mkergbsqutohivdu{rsxxcnvczvvz{tqn}|ztvc{{x|oLeos_lpurnm`qqlyxnq~ph|olxwxs~rujkyuwpz}{{V[vlycilpwtnfiokw}ss{}ltpg{sttprntqu{}sj}xbRrsu}uemtswrpjvnstivnusbwuryxrsrgrwzjx|nKjro{hqwzzvwlnloy{mo|pmtcl~{wt}lupr|pzjyx}{~wHcwru}mt{xrxurqmjuxjkwpoyclzwvxsynk~rwqtu~{|zM\qnu|sqvwsqmlfowqiwqjxhm~zy{uy{qk|muuwyzwU[wpuywovtusvnhktrkywnwpm~uvy|v{qhyzsz}v{wv}{~aTnmqu|o|}typvsnmuwow|lqvj~xst~s{vhuywuzxsxvlSknlpsuy~vywtopsptsksgx|pp{x{|vju~p{x~tytyRanhkx}t|sxuqpsomy{iojmsmu|xyom~o|z}xz|{|rv]Xqljpw~|wxurorqjr~shnfz|ns|z{qh}xty~y~u{qyqoOipmo}z}~vvqqotjlw~jpjospx}||wjnn~~}}}~zwu}qu\Xrsnu{{{xrrntrmp~xkqhx~ot}}{{}qdy~ry~u}p|xp}wPduqs}z~~zzssoxooupmokuly{|xzhit|y~w~~ynxophQlsrx{}||xtppvmrxjnkpoo||{wvfrv|~xz}~}wuotlo~\Vputy||}vwotsns|zhplw~lq|z{xseyyz~}uzylpngrxV]tvwu~|{txntnpvuhmk~|kx{|{zzkg|x{t|t~}gphhwkTkzv{y~{ywxoulsynklsrnyx|yucsx~}{|{~zsmjmhmw]\uzvv{}t{rusov|ilj{mvyxzx{kd~{~u{u||dnjhtlVj}x~x}yv{qvmqxpjjqsozs|xsbry~}y{{}yuigkhot]^v{vw|~s|turptyijj{|m{uwzz{he~||s}yt~vbkgkugYm}v}}yxzrvorzmkjtovzs|woay|}u|yvz~chgismZeyuz|}~u~rvpsvqijqurp|xuapz{yyzzwvifhhpq]atxt{}vvusstxilo}znryz|ydhz|~tz}wvnejgnr`]nun}}w~ztsts{hko{}nrw|z{ef{}t|ww~seihlrb]ktj~z}tsss~jjnzn~ttz|fc}y~t|vx|tfiilqb]hqf~|~utur~linwp}wpyh`zzyv~vy{xggjkof]eqftzxutsyqhmttx~kzn]r|w~x||vy{ylfiknj]amgky}utuuwilr~{skz|t_j|v~|x{~{v{ypfgklm`_ild|wtvrljpwqspz~e`zzyu}}}s{ztjdjkme^dnct{|uwtwsintxwi~zp]n{y}yzz|}uv{uodeklj`akhg|wuxs~kksyqpr~|{bb{}z{u|}zq{xqjbhjke`dmcv{uwvwuiou~yvh{{q^l{{}{xz{zwr{rnecijibbjkfyuysnjswsumz}f_w~|{u~{x{~pwxnladijgbengq|vvxs|kltzrkr||xad}|~~u~y{w{}~mxsli^fiiediph}{uxvwwjnu|{ugwyr_k~~|v~~wzv{}xmynje^ghiefmqk|}tys{rjqvvzyf{xk_r~w{xwv{nrvik_`hihfjrnu}xvys~mltyrlo{{xce}t}wxsx{{jwogh\dhighmsm{szuxvkpu}zw|exwo`n}yw}~vury}pmwgja]hhihkrqtyuzrnltxsnlzyyde{~t|vxqvzyiwmfi[dhigjorn|t{tzulrv~xyyfxwlaq~wz}~}wsqx~kouck__hghimsnzwxys}mouy}u~iqx|tbi~|u{~vvow|rjyeie[ffghlpor|v}rqnuxv}pkxyxedx|u|vwntzxhxjdkZdffgkoon}t}s}untw~yzvhwx}iar{v~~}xxpqy}iupan\afefimnku~uzyosw}|yxhuw~k`nzw||}yxppy~jss`o]`fdeimli}v~wy{qtx|~yyjrwl`k~zx|{}{yqoz~krv^p`_fcdgkjfyx~xw}rty{yzlqw~o`h{y||zzz}xsmx~pmz_me\fbbfjjer}|}tusyy{zpnv|tcdv{xz}x{}xvlu|vi|dgmZebadjjdmysysxz~ztmszxfap{uzw~|xymq{{hxn`q\bdacikfg}{xrn{ojoyxh]hyxzmn}kjpbb\Xfnrehv\og]fabhmi`gvki~b\cjaRTell}rykqyxlt}}ptqoyyy}k_f|{z}xrypmjo}w{~mdo}zkdputrabZXemobepVkc[c__dg^Vf|}leas}qqobPO^ffwkz|odq}}qxv}ssyxrpofSRix~tz~s{otthfjrwkqw^`vpkcZdlod`qVcgWa][_c^Vhym``fxs`W`khwqvy|y~tdo}ts~y}juknea`^URl}zzxzywl~ymgls{}nlz{ue^rpoablqpaniWp__b^_de\c}xq|na^iw~~n^_lms}p~w~|{ik|zpyvpr~hlf^]_[Uj}~uyu{jw{phluztmvxyrk_qrqeakqreiqWmg^d__dgad}ykxoc`ky|sb_korq{wyz{lj||}qwuuliie\\^^Xk|t{}u{js{pjmw{vnvv}wrl`rrsfblrsgjqXni_e_`eicgtjwmdbn|~sdbmovq|~v{y{jl~}~|qxvpp}hjb\\`]Yr~}~ty}wyhwzqkpyzuox{u|rrgauuuffpusfpk[sebd_agi`mmpvkdfrpdgoo|yt~zv|{vfq{|vs}z|izuhh_\_aY_z}}wzw{|pn}vpmt|}}qr|~w{xrq^fz{{qfltvnit_hqbfa`ejdawvkxqidjt{lgnoxpz}w|z|giv}ryyps|lkc]_b[Vl|}{vy}~vl}ztoqv|tqx{z{|tu__nzknvysjteesfgc`dje^pz|m{wnggm|sknpvr}zx{{leo}uwyvn}qmg_`c_Sboux|ly~xspszzquy~z~vyiYdstow{zow}tqt||q|wy~|}|vrv~~ro_d}yh[ZdbhfS[gxv}xr{}}tridfgZYjsw^nmhh\\mljfin}y{w{z{n}uwuqu^\i{xc_yp\UPMS[XP`gy}zwwnz}wrpu~sn]mo^\a]g]NXdsn~yytyzwswrigklagu|jfdzw^^jgebejzo|uppmwvsxzvwydfr{rngdluh\TW]XRaew~}ruxzr~|xw|yndajettmlembRZdrmxszrkouxy~wmjnpgkz{zlcjn{slvrnijnzpx~rkjit{vzzwv}ifrkcehlszncbfcZgkx~w}qsz{ry|zx}pe^\lnu~tslpoZ]fttsvxvkksx|wnhkpli{wqmbku{lvwsposz|w||ukhfm}z|wxr|vgq~n``hqy}rgaeh`bprsyup{zp~~}|~qb\Yhxywtplvf]gr{q~~tzxzlhnxyqiioqlyzkndixoqyutruzrvwkfdh{wxsy}lq}|}p`^ht~vkdfjfdstsuvoz~o{~qb[Ygz|ztqlwl_gq|sys{uzmgmxzrjiosoyzinfk{rqzxwuw|rt~vkdcg{xyszos~y{o_^iuxmfglhhvuqttoy}p}}n`ZZj|}|xsozmbjs|ry~tvuykgny{rjkruq~uimem{rv}zywz~ps~|sidbj~~yxs~~nu~w{k_amxynhloinwv~ovpoz~wuxi_Z\qy}~zrv{hfmwyp}yxrztiiqzypknusumnidp{v}{z|vyww}wnfccs|x~|vxuox{{wf`fpyxomqplvu~sstkry{t~re^Zhutyvrfkqzqt~vuw{nimuzvnntus~uombhrx}}}|p~s|yrieamxr}zwymt{}~ndelry~vrutowuy~wtujmru}x}wkb\dplxzzikoxsn|x}vu}rklsvyy|{rotvs{~}rsddkpywzvof^Y^lfizyohcx|}}zr_fu_ptp~w|wnuliopq{{uumyu{vrulgldf\fqqrkxxomsz{}xug_fuxyyjqzc]vjbkjgb__mslwxhf}|{z{zm]_r{nm{olsuihl`chjmxmmnrv}yl_tvdkonjgtohhotvvq|wh^amx|yikm|soldjhd_\\lliuxpen~}n^[hxwrvuhij^aefj{{rfkpw||xqgc|ux|xpkwrjiotwvov|tf_cpyx|~}~oaksyykomhc^^lpjtxpgn~}zzj^]k|uxxlimaafijx~phiry}{wzqie{|v|tjtvmjmuxyqyvvuh`cq|x{}}}tw_huwmlmid`]fslq||{rlk~~ttma^j}xuxrhnh`gjlp~tqqdpz|wu|~sljutz~xklwqklsy{wq|ntlddp}zw||zpu|cev||ujljfb^brqoyz|}tpm~smncak~}txuimlbfklnzwk~vdp|~|wt~vztnmuvx{mkwsmlsy|yo~lqmefq|v{|y{po{fex{yklkhc`brroxzy}uqn}}|{qjmecmtwtinmbfklmyvj|ufr~{wu}txtpnywz|lmxsnntz|vn|jqmfhsyuyyvy~lqydi{xllkhd`ftpoxxx|uqo~}v}kjldep}vyrkqjchkkmzrn~ohv~~}yuvz}sxsomwwisyrnpvzzoqskrkfjwtvwvt|xjxpcq~tmmkgcbnsls{u|ztmu}||szgnicfuzyxmpqeeijjrymxzhpz|~~{wt}y|uwwpjp}np{wqptyzrj~jrpgfozwtwwsw~ouxeku}pomjfckskowwz~wnm}~}qjnmdbkz{{qqxrw}|y{}{}{o|}{zxv~xco{usgbuxdevwrorwxr`kxtv{lor`Y[heguko{zv~romjfkxuxmiznle^qok}~}hjyrs{q[^n|yi[T^casonz~ro~|{xz~ed{wfm}n^ktqnnrtp_\z~|x|xfkxwjjnenohsy}uzuqmlv{u~iavnj}ho~{z|~zm_u{~wywl[[jq|{pkrnnxpt|~wnu|y~xg[mvpeswrnnpphYc}|~yshds|yz{qxsox|yy|xsnnxwr{~raf~vvku{yzzvg_{{z{~neYapt~vszuu|syuox{x}}~x{td]rxrftxsonpohYe}~vnecvyy|sxupy}y{|xtpoxysz}y|qbgzsmrzxxxth^x}z{~kcYcvzxszyv|vyvqz}{wz}ztusg_sz|{gqyupopoj^a}~vjecvyu|vuxqy}|{wtqnu{tv}rvrgfr~wlz|wuusl`n~}{lb^c{zst|wz{w~~zu|}{zwwx|vlpkepunivxsnnnlf`{~}}zieetzsxztyuw|vtror{wsz}porljuvlt~|wtsrnej~|nb`e|zts|{z|w~~|x~{zywu|wx|~wglmjsvsiuytonnmidy|z~ziegvzsv|wxvw|vtsprzxrx{okqonxuls}|vsrqngl}{~obbh{vt}}{{x~|yzyxvu|~uvz|ueknlxwsjvytpnnmif}w|wgdhzztx~yyvz|vtsqt{uqxzljqoqwylu}{vrqpmfp}}}z|lbcn|vw|zz}zyyxwuv}zrvx|nemmlznoyytponlej~|v|qdcjzv~zwv~zvusryyptx|whnplwvtnz}ytrqogd~}~y}wgacv|x~{y{v{}yxwuz}ssxyzghnip~zmx{xsqpngawvzyhaarz}yv}|~ywvsx{rrxylkpjl{}ny}{vtsph_q}|}~ma]k|}y|tuzyxvz}trxylhmgg}px}{vtrph^lxx|na\g||v}{y}zxvx|tpwysjplfv~pv~}yvurl`e}{ve]av~y~wpz}zywx}wpux{whkkdoxu}~zvtrmbazu{xg\]o|}xyw}|zxw|ypsxz~mlogj}q{~{xvtpf]o{||pa\hz|y}sr~~|zyxz|spvw|qflhduvy~yvtrk`f|sztcZ`t}}w|}v|zxx}wotwz{jkngo{s|~{wvsoc_t{z{}n`\l|}~z|rt~{yxwz{qpuv{oejhgywy~yvtqk`h{qwtdZavw{}w{yxx|wnrvw|jhmipsz}{wuroe`r}yv|pa]mx}ttzwwvy{rmrtw}rcgjiz{w}}zvtqlcf}|oruh]`uywyzxwvzyonsty}ndkmptv|{wtrojbl}|qvuh`ix{yu}vutuyukmpqwwgbknxuy}zvsqojfwz~rksocar~t}}~{vuuwzqkoqsztdhpsysyzwsqolfj|~~ppwmdj{w|xussswukinnswjakqzvw{zvsqplhv~z{~rhpqgdr}tz|uttvyqimopwufgqv|rxyvrpomhk}~|}onvpgl{v}yurrswtihlmqvjaks~wx{yusqpljz}xz|pgpqgevs|{tttvxohmoqwsehrxztxyurpomho|~|zmovnfoxy{y}srrtwpgilmstfbmtwz|yusqojl~~w}ylhrndg}{t|xttvyujjnosxmckr}wwzxtrpnigx{{tksthfxu}w}xsstwujglmpvobemw}z}{xuspkgu}y{}shnqe`os~z~~wvwzynjnprxteelt|y{zwtroico||~ympuiamv~}vw}utuxxmhmnpvsdaho}~{xuqkdn~z{wjkrg\f{v{yxzxx{{qjoqrwxibgm}{||zwtqkbg{~}}~qmuo`dx}zxsyuvxzrikooswl`civ}zxtnffx}z~}qhon`]o}~u}v~yy{}xllqqtysdbhr}}|ywsofbo{|ymoth^j|x~uu|wvwzynimopuvh_ck|}zwsldj}~|z{nhpl]`s~~}w{v}yy{}ukmpqtyqbbiv~}{yvsmecs{{wlosg_n~w|tw|wuwzxlhmnotug^cm|zwrlek}{y~zmgol_buv{w~xxz|ujkoprwrcbiw~}{xvsnfdr{}z}xkltkanx}~vx~wtuxwmfjmmruj_cn~|ywsmfk}||w{~zoelpectt~xwvwzvkhlnotuhahu~|zwusojenzzy~znhqrgluzwzrruwpeejjmsqdbl}}{xvtpkjw~{vvzzthfpnfqxv|~yttwwmehklpuodgt~{yvtsqmhl~y{|vz}{qhmvom~s}~y|rpruqfcgijpsicl}|zwvtrnkvy~utxyuieorkr{s~~yssvwndfijntrggt{xvtsqnjm~zy{ux|{rhlwros{~{}roqupebfhjpsjdm~|ywutrnlxx}~ttxythepsluys~xrsvvkcfijnuqfhw{xutsqnjpxyyty{ypgmwqrs}}{zqpsumcbghkqrhdp|ywvtpln~}x~zsuyxqfgrpl{|xuqvqrnst{|{www{~wrjrxlqlc^WW_iqt|xnz|nj|uml~|ptsxyrxj\Oej[Z\akpzknmlxqknot~ugby|{y}difXb^]fklozs~v~uutpqkSUmeja_kvsjxmu{vww|{o{rwibw}t{x~}vnhjecUO]rtlvzkcesz}|o}z{t]ckyzm_bgc_`mg[ekuy{zu{qfXaacz|u~{q|ymnk~psrqcflrs~}sxwuvqdry}jffeb^ywxy}zfhagnvnZiiolstvikovvqxjtqtsyw~swt}rv~yjn`n|~}wuxyw{xpyuykw~zcjjrvt~{|u~yuqdrytqonrcphjsq~x{ophwzwz~|hf[R_b{{xotuaj]oxjory}xi~}nwt||}a_S^ywp~lxxyrxhmhfkrwv{x|||y~}disttvxowvozx~zuylwihlxr}~|wpn]bdq|wrozlmvsqcysvtvp}}~yrckgid}y~nqppu}{zquplfujkw|}z}|qkjcgurysckay~rs{zxgyzzvs~}}feWZm`iy{~jqssrntiqtx{x}|x{yq~uw~nbhcjnvyzxq||lrfwzfaeukvw~}~wnokgpun{tk|~~py{}{xqrcipx~xwqy~hnoja^vpqporvsu}rv}|u}|johtqpidbgkuzyqtrkk}zr~jn}uwmx{{ysqgzj\]tvozuvxvx{}{}|vuwhoz~z|x{imd_ant|u{xnrwhngsplovq|~}|qtpb_jdnx}ttwv}sqstghkrwz{tsq~u~l`vv~ywxtxzz~x|v~xkk`swjo|vtrkhebfuwu}mfj|qq~m}qwxs|~~|sthYWhfgzx}|stwzwzvvxtgpvqw|z||z}x~pjllcit}xurso{khy~s}wlm}sy}ojbejlzz~zylx}miihir|l{~ql}t{q|qbuenw|}t{sqgppfrdmjfvywomf`f[\qroz~ut|r}qkgirixx}ozsol{ga|yy{{o}r~u{}zqvtlspus|{v~mtpfejvgsyrp|cnxj{~sbsvlwv{~qhpeWkf]c}~urtvq|urqzokxrz}x~wlrkbfhp}swkr{z}v|xiwyfk~~}~{~}lrvytxubh{xzwm|utysktfcdm|yy|||tr}rknd_eyuzxtyuvxpxuder}wuz}|ujwqfjhdmzz~yzpmxwvypdq|q~ssvswwz{gbhkrxwwury|uvrimlnz}|xz|zipkf`g[aqwvyor}pu{om~qmt}{xutnojefgaeyu~yyuvidqukswnv}phljmpfn~x{{tukjhmy{oq}wrtsrfh]\`oys}vtt{vs~rkpwy}}|~un{xowsciqdZvz{~v~~qhl|kexxt~qyzmpifk]_c~~wy|ulrrpz|ujw~z~z}{t|umnbVX^p|}t{y||n||lruqv{t{~wtyur|pjyt``v}w|zwyrhdxzeivpyzx}rtshpjZXeysz~}uryxx{x|sjurr~yvyuooe__cosuqwztzpsroroky|yvmqpozq[evrqv~~wztlkucnwo|{z{ruujpaWWkscr~vuzwy|x|rmwnu{vy~}yx}rtqhgeckjr|{|{}s{~yvwlrtkl{zvxxlz~p{m`rrkev{~x|tm{}ijzoq~zo}}|w{otp`[`p`Zwx}~|}y~xmwqismrty|vxwmjjgifiqlxz}zw{opxojqv}zxlys|uaquocgol}}vnxlj|sr||hr}vttugfasbTdtp}}yw~q{sztei{w~~~mnudliagbgkx}~wxm~rcryzt~r}jzb{zmdaqhfn|{wv{zmzx~{vlqxlry}}u}wbalyZVhgdt}wvy~ztpnvxpswqz~z}osted_bq_[p|wxruysrxwxlxyy}ypw~fkej`fbfyuwkuvpxqczys{|oyv{xsoragrl`jwav|tu|vjneykdyx~{sssnvsqxjsXjworl~~{up~jrkiist~xw}|kopahkf_plwxxlqvqvwy}zk{wrh`{b_dafk}yy}mrztnqyi|{zuulskgbh\[luvolqp~q||mk|}~|}{rvqtcfg[hZpu{|t|twpppvus~}~tnlmtr|payoksesp}}w|j~nfjcd}yv||~uzsopnoff^brrpzt~u{ynrkvpsx}|zsxf{ra[bm_qtznn~sv}wiwqiyi}ozu~oqokbfjfcowy~ysm~}~cyzxt}r|}klt\a[`q\cg}~w{}wryqpwtjy}~ww~vpg{pphcpfgn|xzz~h~|efhvmy}|{~t{|suyphsjcjmekzt~zu|~rnuuhpz{}{ulncbmk_wuoah}{z~znwrjl^otvwpmtgffifhimq}t{}k{~wzly|{x}urvekc[paWYqyt~z~zzvwvvmw{w{xv~|wj~yqogldcemnu{{~uzpfh|qk|oy|{{xysrsqclocaoemst|xuvwmmous~w{p~lfiyknwsjYpxv{xzsx}ikgqlks~z~rtvpeikfekfhtvo{uq{on}rs}{}zp{qgg]slWUbqiw|~~yx}ooxwq{x~|~yoz|yhllbbjgbkv~w|xknvn|kvuqyjhf`hqfpon{zzjtvlhytu}pq}xqioq~{mbzoklrm|nkncyxs|xjqrlrz{{xywxuLfegbd|sx}~|u_qod\NN_wmefomkyt|ymy|{}ij}hpfzvzyyyvtrq}~mbnpko|edjqrxitkxko~umkd}y`lfZqytxw~t[S^l}|ntcYalTVSigcmjiu|u|}k|bgunx~ywx{}pw~v}{wk]g~op|ffankfpci|lkzcrzoquwcpyX`c[mz~~wve]YXjq{nhk^^QdgZkbkxrpvt|rtjlniyuq~t}swyyilkbw~|yki`tq_obhxoilxhr}{ru{fhcdgXgt|~{g^aUeo|xwtu`^T^qTd``tysryplvmvkj{}xsvq}yz|rgj\lu~ugca|eiifstnpyr^utybvm_fa^nx~}}yp[fYZdt}vxrb_TscUb`jrux|iqymptls}wnwv|txvyybkg^muqkcpy\igmsssu}lfzvy{sfzf`fcbqx~|~{|dYeV]eu~zld`\x]Xbepuvv~fn}motjxsps|{vttw~u]geeqzrlfu^acnrsxv{ihxx{{}pdukfiicty}}|yvfXdV`hy~sd_[vdT_cns{||frz}ppyotwm~ty{qt{|sv|x]dcbt}woincacjpszx{q^|y{zwtdqjfhrety|yy}xnrzoU`Y`mywme\luV]`lqw~rq{r}sqx|ryvt~pr|xpzks{iZhbr{tng|uZbhrsz}w|bq}y{{uuiipieqom~|z}upistWW^\pyxqfbf|dZajsw~i~{mwustt~t~rswvtrvzciwpYdcsyniw^^eqty~w}hk}}uxzurjemljrul|~po`mv]T]^s|{vicf}lY`jtwg}zmsuvtu~v}qu}zwtrtw]evoYcdt}ol{`aiqqyx~gm|~uwzspiekmivxpzof^ms[Y^ew}}zkfl~hXbntxzh~ywotwxwt~yzus|{~vlvq\ixhZcgxxmqz]cots}~zy`z{{t{|rofgkoi}oy}ywsx^csjT\_kw~vgjy{]\kss|}kx||ysquys{y}t}z{{yx}yiwbart]]dn|pq}k]lsry{{hh}}stwqlfiolvvkz{nzf`pt\W\coz~jjwk[jsqw~om}{{xqrwu{qww~y~|}w}~jwh^nwaZahqwsu_mvqv~zkd~wqzrpiinnsyg}v|~pwnamwdWZ`fvyrixv^ftps}ugv|}uqtxvo}s~wyu~~qor_gvlZ\ehx~rigxtr}ysaw|}pxttnhjplzkp{zy|oxdesp[W_amxzmtm_rtpyyjr{x|zrpwv~{p{yv{{v}y}jrh_mveZ^ei~}|ugozsuwmc{zl|tumgjoozfwxx}xptbfsmYV_`qx}mzndwsq|~vhwyz~}yqowvws~xv|||v}vzhqf_ltdY_fk~|wirzruwlezzk{tumgioq{hyzx~}|xmradpn[V`aq{}nytexurzujr{u|zqnuv{r{s|ysztxjmh]hsgZ]hjupp}su~yndyz{mtuuoggoq}nu}{}}wzipc^jqaW_el|trisyswyokp{|tnpw{ss{|wuxz~|zqrfl]`np`[hk|v~l{xtxrgjxvi~}ssjelqy{ow}x}xwoii[boj[[hh}ntm|tu}}riyvt}{womvy{}xt|{zr|y}{}msej`\iqg]fowzyqy{twuifuyhvstlfjqxpw|y~|~xqpgi[`mm_[jjo{o}vu{sjt}yozwplty~|zsy{p{w|y|kqeh`[hqi^hqw|{t{{twtigtxgwrtmfjrz~sw|w~z|tpnggZanl_]lkq{t~uw}~qjy}|up|zvomu{{xv{{wpwwyxwioeh]]kqg_lo}yvxvxogl~sve~{sskfls~yvw|tz{~nqjia[foh\dkpzyw|zuwvlmw~m|y{tmowystnw|szuymkjgc[combcon{~~vw~xjhzrykotuphgoy|s~y|v{y{}tnojf\amma^jfx}~vw~|nl|x}tr{|wols}xvzxrs~tzxvqjlie\`lnc_lg~xx||ljvuwrjwvsjfmw{sy|}yxy~}zwmoni^`knd\hcx||xxzplu~w|k{zrkozxz}vyyoz}vvzstkkli_]goh^ggq|{zxrjno|gzuwoghr{tt|~x{t}}yyxqmomc^enj^afex|ywxmotqu{}wnks{x}x~zqs{zsyvrqijme]`kne_jd{z{oksxsyfzwvmfivzsw}z~~s}zyvuokpk`_gni^edlw{zwvmqrnyz}wmluzz}wyotywrytoogjmb]almdbkd{z{~nluvsxfzwvmfjwztx}{}~rz|xw|ssohpl`^fnkaffmy{{wxmpq~qsz}xnkt{{}y}ppvuqvtkogfnd]`johcmf|{ypkq|n{fz}vxphiv}uz|~y}qvyxt{tnqgkoa^cmnefkh{y|pnxwv{j}{{rlp~{xxxkrsqpukkkbkk^^eoogmmsw|pbcozdof`wjjf]\mg[drTSRN^XOS_hheplyzuyt{}}~`Rsvhoq}ymrtu}vz~ebfxuioehpc`frwtw~||~~mwwhhgk{sfgqrrxg~{wrc\koUXjh]XJVUJLWbdaiht}{}|vdU[{xzxhjmnwzpzx~xt]`kxqvkkwkgmy}o|z}jhfixpccl}nnscxsolbaqzs\R`p{tmZe^RT\efcjevxz~{|svzlg^Ydqqprxxo|u|{un[clv~}s{jpzkjq{|wppqjq{odgr}ktnhyqpkcdw~qlUWbq{oeraY\ejhhkfzw{z}mzrke[Zk}sto}z{tsvvvc^elww{yh}uknv~~x~nw~{sqlzxielzulwgswqpgah~ur_SZcsiwq__ckkilduv}y~}z{sxypl`X]yvvpx~zwq~x}xxj]adnz|zk{}mnt|}vrtwwvmz{mflwymwlqyttkbbvyvgUWYfwrz~gdekmklcnv{|~{||z~}zv|tqgZWi}ytt{zr{~zyxs`a_eqwvtspsz~wyxqwr}}quuiks{qssnxwvrg`i|z~}ws]WV[i|twjikpooic|sxxx}{~w|zuobX[rw}{s~wt}{yvzj``_gtz~orsv}~s~~rurt~r}~rjnvzquqrxwwpeaoz{~xwmYWU\l~{wnmpsrrgi{tv|zv~{{~}wzwtnaX_tw~zs~~vv|{vvxfa__ivwotux}s||zpuov~sskpwzrtqsxwwpecry|}{stjXWU]m{zqoruuuik|tt|zy~u}{|{vvurmbZ`uy{txv|}usxh``_ivz~pwwyt}v{wnspss}vmpvztsrrvwwqhdq}|}~zpplYVV]mvtrsxxypiu~ux~zvw|y~{yusrpme^`|rkemzzyuywc`btte{oixruzzlppx{svop~vv|{zz~tfrt{xtdUbrXPfQ]rddgpw{{o{}}{{|yzbambocmmfef~|y|vyyd_ft}kv|fwy~~otzzzn~okketwpmry|xvxyzzwokv||{zgU\ltap[Vqeadjrwxlswuyzsyyrjelt~noqgedyx|vwz}mnqbbjr~t{k}uumj{zvpnfsvokpuyvqttsttqpy|uhl`Xcnsie|mhinuyykv~vquvr}~zzqnljirwyynij{~wzz{joobejr~sk}umoin}xvnkwvpmsw{vpwusutqq|~}{kfj[[do~}ft~olmsx|ukzwyouss}}z|ypoliitw}umhtz~zv}}qjtiaeiu}wtv~~mnkhuxyxkrxtopuy|qszsuusor{xelcW\cr~ylypnqw{zkv}u}oqtq{{z}}yspojei{~~}rkp}}}w|yyhsmabckzyr~rnnhqrq~|oqyvqptw~sp}uvwtnlwzhohYXYdyrvrrw||ls}uwrntqyyqwyrrlfbm|ypo|y{yjpsdb_bp~|s~{mojmrhywoxxtpsu}{l{{txvqkoyz}{pjp_XWYi}}wtv{uk~zr||lqrt|pu|w~ttqjcdqwwoy|}yyvhukba^dt~y{rklhqlhtuzxtsuwunwuxupkqv{{imk[XV[l|yvy~qp}xq~xkpowzmy}sqroicfrwxr|}zzqiuic`^fv}z|~njjisijtv{xtsuxrnvuxuplsv}{fjhZXW\m|y|rr~xq}yjnnvzmy}q|opmiehsxzs|{xufsiba_gu{{|xoegfqljwv{yw{~us}|yz\\aTTX`qxqlr|sbbbNNQXe\f}~zxusrnnljmzkh}pl_klU_aX[\cr|}~~rztaaaktnzlbp{okT`madhhhmuu~}{sy}wxt_id`ekwrhqqbWc[OUYhkeyvmt}m|ostvyyikhrlcmVQ]TUX\gu{y}{{}vuyv|idhp|{a^i{g^qd`dfgiqqyx}sztjo}|~~dlmgmr{oej{zhlgUVZhnes~rkuq}svy{}yh\gpxrydVaZY[]frywz{v{uw{u}ldhq~}p^bpxk}ohlllnsqy~v}qxsip}}}|clmhmqyxkfoqvn\]`ophw}qlwnrux{~}wsd^mr}yf^f\]^`hsxw}yxzuxxu{jdhu{}|m^esusplnnnpso}yxxpwqju}}|wepkjnqzvkissyk]`gtlj{{|lnx~q|sxz|xxoaepp{bgd]_^akuvxw}wtzu{wfgn~}x{f`iurppponqnqtqsvmozwllohklq|rjmy|{zeadqqfr}yrkqxrvwyz{yxjfokuoik``^^eotv~{{|vw~{z~lglz}rnbelxzwtsrookkszuqxpoytxtmrijiktzols{zpfgstemx~z~wlosyx|xyzy{yyz}skqkl}npeb_\`jps{~}|xvz|yvijvrvzgehozxwusplfvut~ovvpxwxwumigdemtv||yuysntts|~yigtw\X`lv}mz~v~iszqphu}|v`hj^_fiewvxqlr}s~}odeiq}vabdtmX\XYc]aqywzsrnebnyuy~sUXvuss\YkdWZ_gpyyxtpr~}x{~x{pe_k{qjbbbaefquonqxwuurqbh|~xzhysa]kdY_md_kkowot{xurv}}{ynpwzxdbhiloqznk~whffgjq|z}zxliaRm~mjc]^[\ahlrwv|{~tmr|{tksws]Tw|nz|ttvutlk}|op}vjtvtvglyzstuuzm_n|wkqqb_kujgrvz||{m~~i^`rt{nbXwuwojhbcbacimpwuyzzznjux}psuWO_ug}{mlnpojamynhryiflfjmq~{~~k]b{ukrh\`onbipsvwxiivh]ev|~piaX`g{{xmkdbb__dhjqqpuputlmy}xtfkgTUhsvpqrqmbfwpfkui]dcitv{}~}yonf`jw{tddqsfipsvwv}ibrkahwz}z|cZZXep~wrkffcbejlrrpuoruop|}{ys}|_dfX[my|tttsodgxqgjuj]cekvxz||{}}|uklfdoy}wghuugkrtwwv}gcsjbky||~x}w_YZ\jrysjggccfjmsppvnrups~{|xsu[eeX_q{}vvvunclynfmuf]dgpvy}y{zz|y|qjlffs{uhnyqhosuwwwyaitgdm{z{~xw}oZ[\blq{rjigdeikptmuuovtpuztx|g`k_Xbu|xxwshfvuihrr``gltr|xyywy|zzzmmkeiwqmxxjmstwxw{kctncfp~~}x{v|zc[_ahhuzoljgfhkounrzquxrqx}xu{~ramfXZfz{{zvketwkgpue`hmsmqxxxvwz}|zspphfmy{r{|nntuwxxzrdsrecht~|u|uz~l^bdhcgtnmighknupn{usyuqsy{}u~|z~eln^X]k~~}zrfozphlvo`fntpjz{vwvuvz}w|psnggowxxvmsuvxyx~~hkwlddkwuyvu|xc`eih`n~sqmjikmsulv|rwztruwz{xx}reqi\Z`o~ymhvwkhowi`jqullxvvttvy}yvwpsmhjqvz~spvwxyyyyfpvjdfmy}s||st|s``fkfas}sqnjjknutkx|rxytsvw|~zzw}mepf[[brylkywkiqvgaksukowvvttux}vutnqmilrx{sqvwyzyyyfoukfgoz|r{zqqxr^]elhcuurolklouukw}ruyutwy}|yw|nalg]]dt{ojxyminvj_hrwnoxuusstvz~up|ujnmjnu{~wqwxyzyx||ijumghp{~su|qmrs`Xajmgtwsqmllnswmp}tqwwvy}~{xwx~}z}t_cia`gu~ujq{rijsq``mwvp~~uttrrsuzxlqwihmmpy|~rtxxzzxx}qdorkjp{vp{rjjpfUYfpos{srollmqwqjxyntxx{{~{|{v|~~~|wwvb\hebivymn{vkhnse]ivzt|~ussqpqrvwkjujdkos|}ssxxyyxw{tdkrmmr|wnw~rhgmgUUdrtutrpmmmpvsiuzoqxy}{{}{{ty|||zuttc[ffekxzmmzvkgmsf\hw|u}~urrpooquuhhtjckpu~tuyyzzxwzselsnns}~umx|ofeleTVgtsw~trpnmnqwpiwynry{~z|||xsy{zzwssq`\gffnzxmq|tjhord^kz{u|srqpooqurektgdmqw}swzzzzwwzofoqmou{oo{wjdgl`S\mtp{{usqonpvwkn}vnvz{~y|}svzzzyvrsk]bgdgo}~snz{oiksn_ct|uv}xrqponosvjespdhorw}ww{{{{zxzwjkromow}smx{offliXWgrnoyvtqpquypk{}psyz{|{|~utzzzzwsspcafdchrwoy}rkkssdbq|uq~}}sqpoooswnesvhgnprux|y}}}}|yzyolrpllqz}tmv|qhflm^Vdqlix}yvtrru{tkxuqxzzyv~|wsy{zzyussibeebcjv}rwxnlpwmal{yov~urqoooqwsflyofkopqqzz}~~~{yzvnpqnkmt}zop|wlghnhX\mpgl~}zwutu{{np~ruzzyuv}}svzzzzwsspfcecbdlyys~smmtvhds}to{~tqponoswmerykfloppp|~{yztoppnlnv~~vms{sifineX`onfo}zwuuw|ylr}qu{zxtw|zrvzyyxurqnfcdcben{xv~smnuugev}sp}~~sqponotvjdrxjfmpqpq|~zyyuoopnmow}ulrzrhdgleW^oohq}zxvuw|ymq~rtz{zvx|zruyxxwtpomfbcddgo|yttmmsvjcr}uq||sponmmqukamwldkprss{~{xxvpnponpw~|vloyshcdigYZkrkp~zxvuu{|ol|tqx||yx~{}rqwwvvsollhbbdfip|}tzxolouociz{sy~xpnmllmrobcrrefotwx~{|~|xvvrnnpprw|xoisvkb`dh_Vbsrqzxvutw|tjszpt|~}{yuotvutsokihcadgkq|uv|qklrsfev~wx{|pmlkjkooc^msgdmuz||{~~|xuutompqsx|xphpulb_afaV_rus{xvutw{vjo}pr{|y|unrutsrnihgeachms}vu|qkkqshduxyz}plkjjjoob]kshcmv{}|{~|xuutomprty{wnhptja^af`Watus{xvuux|tir|ps||x}smrtsrqlhggeacinuux{pklrrggx~w{|zoljjikol__nrfdow{{{}~{wtuuomprtz~ztjjtqf_^bf]Xhuru{ywvw|{okyxpv~|{x{oottsrojgghd`dinvzvvnlotofn}zv{tmkjjjnpe\gtnchrxxy}~zvuwtmnqru||wniqukb_afdZ_pqoz~{yxx{~uktrs{~{wzrntutsqmhfjh`afjoyxzqnoutikzztzyollllori^esreentsr~|wuyxompqrxypjqwnd`afg]]loks~|{{}ymsvqy}zu{}}votvvusojgjlc^cfjq|{~upptwojv{tvsnmllnrnaaovkciqrotzvx|ulnpqtz|ulmwtibadhd\doklx~}}}so{rt{|vt{~qqvwvurmggmj__dgkt|{sprwwmnzxszuyqhosojs}rq|xw}zw}~v\\fb][\biebowtxxuuy~znn{|tz}xoecdffdaemjcflosy{wvqprxxsxzyob]`bZV`moffotsr|yu|xw~zw}x{qih`XVW]b`^gnlq{v}|}}tt}~kk}~slgc`]_gf^`fjnt|{|vux}{}rkw{qmi`Y^imebiopov}rq}|{{qp~|rg`^`ed`dllnv~{ryzw~}qfj|uojedikdafkotz~|yzww{~{{mhvyuoebjqmfipssu}vq{}{}|{|soo|{qhdcfifelppv}~uv}wz~xpij}ytojfimjdgmquz}{y}|yxz|~yoiszvqidhppiiotuv}|wsz~||~{yqno{}tlgfhkiglprv|}vv~yy~}vojl}zupkhinlghnruz~|yy|}zy{~~~~vnjt|xtlgjpqljptuv}~{wt{}}~|vpnp|~unjhilkimqrw|{vw~y{~ysnjn|wrmklpmhjosvz~zyx}~{{|~zslkw~ztmjmrqllquuw}|yvu}}~}|{snmq~wpmkmolkoqtx|}xuy}{~~|vqljq}xsnmpqljmqtw{~|yxx~~||~~|vojmz|uonrtplnrtuy~~}zwtv~|}~vpmlrxspoqpmnqruy}~zvu|}}~~xsnijvytqqspklpsux{~|yxvx~}~~xrkiq~|ustvsnnrstw{~}|{xusz~||xsollvyussspnpqtwz|~~}{vtw~~~zupkhn{{vtvtnmprtvy|~|zywuz~~ztniks~|wvwvroqssux||{{yvsu}|}zuqmjnx{xvvtqpqsvxz|}}{xuty~~{wsniiq}{xwwsnoqsuwz||zyxuv|~~~{vqlilu~~zzzwrqsttvy{{zzxusv}|~~|xsoljoy}yxwurqrtvxz{|{zwtty}~~|yuqlhir~}zzytpqstvxz{{yxwuv|~~~|xtojimu|{zwsrstuwyzzyxwtsw}|}~~}}|yuqnkjpz{zyvtstuwyz{{zxvtty}~~}~}zwsokhjs~~{zyurrtuvxz{zxwvuv|}}~~~}|yvrnjimv}|{yustuvwyzzyxvtsv~|}}}|{zxsoljjpz|{zxutuvxyz{{zxvtuy}}}}||{xuqnkijr|{{xtstvwxyzzxvuuv{}}}}}|zwtqmjimv}|{xuuvwwyzzywvutv}|{||{yxurnkjkpz~{zywuuvwyyzzyxvtuy~|||{zywspljikr~|{zwtuvwxyyyxvuuv{}|{{{zxuroljjnw}|{yvuvwwxyyxvuutw}|{{{zxvspmjjlq{}{{ywvwxyyyyxwutuy~|{{zxwurnkjjms||{xuuwwxxyxwuuuw{}{zzyxvspmkjkox}|{xvwxxxyywvutuw~|zzyxvtrokijlr|~||zwwwxyyyxwvutvz}{zyxwuspmjijmu~}|ywwxxxxxxvutuw|}{zyxwtroljikpy~}{xwxxxxyxvuttux~~{yyxwuspmjijms}~}{xxxyyyyxwuttvz}zzywvtqnkiijnv~|ywxxxxxxwuttuw}~{zyxwuspmkiikqz~|yxyxyyxwvuttuy|zyywvtqoliijmt~}{yyyyyyxwvuttv{~{zyxvuspmjihjox|zyyyyyxwvuttux~}{zyxvtroljhilr||zzzyyyxwvttsuz~{zyxvtrpmjihjnv~{zzzzyyxwutstw|}{zywvtqnlihhkqz}{zzzyyxwvtttuy~|zyxwuspmkihimt}|{{zzyxwvutssv{}{zywvtqolihhjow~|{{{zyxwvutstx}~|zyxvurpmkihhkr{}|{{zyyxvutssuz}{zyxvtqoljhhinu~}|{{zyxwvutstw|~|zyxvtrpmkihhjpx}|{{zyxwvtssux}~{zxwusqnljhghls|~}|{zzyxvutssvz}zyxwuspnkihhinv~}|{zyxwutsstw|~{zxwusqoljhghkqy~}|{zyxwutssuy~}{yxvurpmkihgimt|}|{zzxwvttstv{~|zxwutqoljhghjow}|{zyxwutsstx}~{yxvtrpnkihghlrz~}|{zyxvutstuy~}zxwusqoljhgginu}~}|{zxwvttstw{~|zxvuspnlihghkpw~|{{yxwutstux}}{xwusqomjhggimr{}|{zyxvutstvy}zxvtrpnlihghjnu~}|{zxwvtttuw{~|ywutromkigghlqx~}|zyxvutstux}}{xvtspnljhggims{~}|zywvutstvz~|zwusqomkigghkov}|{zxwutstux|}{yvusqoljhggilry}|{yxvutstvy~~|zxutromkigghjnt|~}|zywvttsuwz~|ywusqoljhgghkpw~|{zxwutstux|}{xvtrpnkihggimsz}|{ywvutstvy~~|zwusqoljhgghkou}}|zywvtttuw{}{xvtrpnkihggilqx~}{zxvutstvx}~|zwusqomjigggjns{~|{ywvutttwz~{yvtrpnljhgghkpv~}|zxwutstux{}zxusqomkigggilry}{zxvutttvy}~|ywusqnljhgggjnt|~|{ywvtttuw{}zxvtromkiggghkpw~|zxwutstux|}tnpkgswv{}|x{yvuvqupstsrz{|xnc\UT[[`\UPZ^k{}|yxytmhed\_\[_`_fnp{{wk`[Zjs|{ocnzocveRHIOQS_bjh]Wiv}|{xvotwuv~ww|{mcovm\XYejlrhZeypwwqmmmdhhruhsz|x|{{pijmltjdlqnusz|}~cb~pi[OKBU`QZeSUj~yrm}pwo^Zbdoweankkln}~~ywbuveTfe^|z{y|txvoizxvhg|}p~|{qelttmsw{}xs|{yggrt^Wb{wk|qtico`hd\UPst}xgkw{lrbyzjYXijivrWhjaimph|vos}go}kjqdqnxmavuxyzvuuovpnmot|||yp{ylz{}vvya[rhaae|mrmwgd_^W_pUcx{vd||wfoqdb`u{siye[q^knrcfoyy{woze|r|sni{z_j~t|rjkkfkzrp{x|{tt|x|yopsVa}udsgvsamrb^bXVxrYp~{iv}oguu^`alxok|Vmhctwu_xty|{|x}y{ogh{y~tjtg`||t|ijog}|qwjt}xrrz~ztl}XYuyaskuvahre^f]Vwz\k}km~rgqza]`f~ysjZhmat{~ctst}v{vynl`|vvmmi[qxlkrmswg}s}xqrz}uskyVWrw_rkzueksfcka[zv\n|hl|qgt}`__i|}vqYoghu}|e{nwzwxuyo{fcwy~ujm]Yt|lotr}wrh~s~{nw|y|~~ptpmU[wk`qmozhqsfmoaei^uxbrz{mhz^d_ryoz[u_tuqm}k{u{s}}wru_oq~xtewnT^w|~ylru|v{ht~p{}v{i~}xsqq{}[Wix]llzuwpnxokukcwu]j{hd|vqiwedchu~xu^sclv~}jjxyyyw}yowfg}ruzriktVUqwyv|stwylltv}viz~t~vnux}_Wbw]ejwvpzu|wlyoity`gwl^wsukuogihs}qhqggzu~j{mt}z~s|zrukfyxrzsmht\Rb{qs|~zxv~}uf~n~{ruysyv~ktw{hX[ri\io}k||oyyoqzkbo~}^ivr~rkhnhnstfx[zs|qr{i}xy{v}qrfq|quvojnhSTvoqo{y{xzzkpot|~|h~z~~tyuivwu^W_t^`iyvp}qyuxzihry\oquyqp~joinusups]q|nytm}yx}xyrohv{pttolodSUzmlqy|y{jt~}mwx|hx}}wxshvwo[Wbs^bj|vv~rzy|{mks{\optzsqnrlnutwru]qzoyvl}y|}}wzrpgtzqotnloeURsmkqvz}}|oo}~qr|x{iwyu~tervkZT]rb`j||vuz{rmqbgrl}uoptnmoxl\{ywsukzzxsqqgr|vkroloj[Rdullw|xz{iw~lyt~{tr~{}u{~~whfvp`UUhn^huww|zrrwx^qjyxtx|trmkx{n}nesypqr|zzwzuqkjxymmrlon`VV{nlz}||~nt|ynt}r}kx}~|n~yy}{vkaqr`TScrbhr|u{vsu_nmszwutunks|tyx`wzsvo{y}ywwqmhvxohrloocYSrqmzxz}rqz{pszr{lz{||}k{wvywsj`ps]SScrcjt|w{yvv`omu{ywvvpku~q~tcvwrtqzz~}yztqijyynhskrn`YUwqk~~{~yotzwos{o}ou}x||zl{tvuuphbtoWSShmcl|z}{|vzybsk~}yzzupi}nfquuwovx~|w|rqeqzwhkrluf^U^~xnpr~}v|}kzuyptwrssuz}lsurusrldhy_QPZqeio{{~||tfmpq~yxytjrryb{vtuqxz}{}tthkyxpdulum^ZUszqju|wzny}}uqs}|n~{xr~uw{qpvrvrslidzfOPTmeflz}utnlsp|{{xnnw|a~vtzpwz{~|vvkfxxvbsprq\\Qiyuiyznx~sqw}}tqxoxsvwxtw}|ltpwrtlldpxTROcjbiwox}zwitn|}zvh{tmk~t{ptx~}~|{utcpwxjfxlxc\UYrwqmizx}lz{v|rryp}xz~t|usy~qopqwrthlbylPRRidekyvpw~|mto}{sizhr}vpuy}~xvrbrtvekxnx^[R]rwqs~g{y}~n|ywzssur~u{{t|}tqx}mnmquprgla{jQRTjdgmvznw~puq~|tk|kqwpty~|~xvraprufhynx_\TZpxuth{z|nyy}x|tr~wq|wy{ux}~vouznkkmunrematsSTShghn|vq{{rvq|xi{zrkyrsv~zx~{twdjsrm`ynxiZXVlv{so||~}ss{x}{~vqx}ov~u{wuy{qpv~xgkhrnpifid}`US^lgoxrvw{usu}|pn}ju|{rszxztvnapnq_nvqw]\Tcs}z~~n}}}oyvwyss~rr~szxtzy~smtyz}giemplmbl_ymVWZmiouysxvut~~tkoq}rsx~|{xutrammpbhyoza\V_q}~~m~~{pwv}xztr}sq|syytxz}ulsx~u|ghdkpklal`wqXX[ljqwytyyvusmoq~}sry~}xx~tvoankp_lxqy^[Ucr~o|~pwu~x}yst}qstxwszw~}}rkrxxsvegbmmlibla{jYW_lkt|sux{uzos|nxwsr|~}q}{sxgemkl]wswq[YXiuq}v|iiikwtqtuozyrzx|xzvzp^]ffs}mkilxqvfmfhuWOL[[agvomp}|wtxmqvzrvu}{jrmq`iwou]XUdr{h_utrjmnyx}~x~v|{~z|zxdYek^jeWTVUfbh_bf_u`TR_djq|yq}z{meq~yk{wotqv~v{|vrw}taNzu_kv~z{v}vvpj|uhjjr{wz|wvu~tw|yytxxy|{|}rgY`fvwih^knnndqfvt_[aloyuzyi\cxietwjra\Z{uouuvfdamowvlwxt{lgnmqbw}yv`WU^fvtp\l|~~|xynfUSZmwmaVb[^SSYPbTGDPT]emg`ne{sxmgrvzu{}{|l_]`grmeRp|zv~}z{qpwrncovfdahovmmsmxsryr|mwrsvqumk\_cyvvshfsouiosm|pcbmq|~}spc`mtxwxsc^atttvpecamnqxj{~uuzhilmndz|ys_UV]hyxp]v}~~{{{ppgcUU\pzrf[e`bXW_Ub\NISXckphbsc~xnzqju{~{zu{zo]VY^nqn\is{|{ykuyrgfxmebdktsrspprxpv~rxsmuqusnnmie]fmt}srdlrsujwru~pfktz|~lnvoel|t}t}ne^iuxuxlgcemsn|ym~|swxgkjokh{yp^SX^n}}rh|}|zxqmee^`V_htqb]fbeY_b[f`RQ[_msvfloh~}ol{rrz{y{~vxcRRUbtwpfzt{|}uj|zpdoykhainzo~mrnwstzznyirmw}poikgjbgmy}tukhsrxot|t}|nlvz{iezwtlp{yuhee|{wyojfemqqv{n}}rwyhkjnlh}~wraTX]nvl|}}{s|s~{mha`]aZclywhemgy|wkklqokXgua_|z|~}owy|}tqzyys{siqi]baidm{wgckqqr|kx}lk}w{z|}}utx{xuzx|xqqtwsmevynrpcicfkxvxmdos}xo}zz|~ms~tppjhgenqvqzrz{ptxijjpqntflqswwqor|u}wrzysuv}x~u|~q~yb]XY_i~d`]dmnaXnoZ^~x}sqr}~v~w}~uw{t|x|ztz|z}wvv{~{w}ndcnt}}sj]bcpdjv^__ieknrfeq]hlt~ytrqrxqrqvtmmvnfiZW]bjku{xz}p{nyqquoi~t{{vjihnuddjhlibmg]__fcgw~qsg_gmtyrz{udfm{}xw|{wyyw~~~oleRSS\agxzj`sn\Z{uw~lkjvy~zxzq~uvy}sux}rys}y|}z~zy|~~n_^epu~zjiirpdxi^_ggioph_pb_lny|wqqmwtsry|qqnopejcW[^hjn{|}wwsv~~qpprgr~qzppnwuefnkmgeqe`^ageiz}otfdirqp|xncdly~yz}|xyyw}wvhgaQRT`fm{qhysc[{xu}lkisv|x~w{qvqu{rtvzqww||}~}z}|{{o[Z]nvvlopyiqwc_dkippn_in[gor{|sqmpxststnqlmhch[Y\eosytpvrvqilmnj|yqqqzqcismleiqfd_fhhn}pvhklzs}mvzdabp|y}xz~|{{wwskg[_WOT[ks|np}tbbwyylljuw}vxxrqmsvosuxq||z{zwt}zs^VXevrqqxxjyrbbklotpfare^mpt}yrokvutsyylnphh_ebZ^cqv|{~xusinngklnk~vuu}vggwqlgitjgafjjmpwlmmys}l{s~}wd`ao{{xtm||{x|zokbha[_hx~vuzqagz~ukfcihj]jhatl]]qxxw{{gckheggdczwo|zymnzz~uwqcQOQcm|}zwrvtjswx~ypbom[comu|rpkt{{}~wytnptikn~wyrbTp~wwuow{||zwfhmfighctx~~~of{{plmzrogknon|pwrrr~}xvsspq|pwq`bmnxxrenyhiz|~z|t|mhelb`_ju|iqwxyxkicglqqk|x{~pyzqryskhtojceklmwtvvst{xpqqrro^WT\irxstmwv~~xjl{meuz}{~zwuu~}{ogb_findnws~nm|~|l`kjklgabxtjs}~~{unx}uqkssvvw{}}zwuzwkx~uwxx~wk_on^ausvzvmu{}}|{m~xhcfmb`]ipvwyx|pngkotsl}~s|{t|~vzqo{wp{wqifmlnowourrtv}riztnopbYUXdktmv}{~sz|}pj{ugm|y{rzywsymjaeflgcstv}ugrz}~}}yvdbljpkf^mymhv}z}vs}zrqwwy{zytt~z|~v}qlzy||xjaqm^`uqswsjrwzyy~|wj}ve`eldc`lryxv|tsknputn}w~}w|oszm~ysq}zq{{ungmlpm}msstrwyzsequmjmf[XVcjtqq|{{|yzju{peXX`ejfprqbn|hbetpqpwq|zql|tttx}{zxnkhV_fkpkgf|tr}nnfkhqw||{}~zxut~~sojow}wzzzrw~ypxy}z}vpyjfptono}lwkjqkeihcd_delgdrxw{uiu|nmvw}vpj{ysyx~txy||yzwty}{zsxzzzpgnp^fkruma\nh]_vqszulux|z|}xn|jdmslhclnttr|||xztywvh_Ypnfd]_^fef[ord^fl|rnrw~z~tg_hg^[_X[_l}~|z|}wxwutgwi]d~inttowvn_gsni^n}qlcetzyqrik|v~zw{wqpzq|{~x~v{w}~xfamkop~}ng^dgnidlqosvktx|xty|}xujlo{qet}p}|thhou}wz{vrsnogYTFL\m~nmcU^ajf_Vdrg]cqet}tpurvu}{zmqva^nw~v|ou|{uttzxfh|t}xnoiiaa\\TTg[LK\``[`jnqo~wjs}synclwpcd]bdiq{}xuUGRZfpyyngkhaWabZTOT_]V[VWTV_gmfit{t{yx|~}{mjq`afvz~r_\hndilt|{og[`ahefefklg^g\\ckph_^ttibrsj||qroomorprj|lsrlkspli~s|rtsk\Z]`cbeae]dqdTYijjcjqutsugn~{}}}ux~poippv~}x{{~o{tYIT^fio~tzxqvsnenrid^]ek^`\[YTZ`g`al|x{v{ssz{{uy|rzyj`am`SQOX[gr~zoqtvwsuqtstfeg\_blha[m~yru|}~}~~xsoet}~uqvznqrx}vdnf\[cnnmp}xhgfhiff`d^eri[cuz{szw~wqrrroxx~z}{vpdps^MYgrwz|~wyuqir{urmmq}qmkhhbhlvrpw{zzy||yz|vzzppy{s}}~}pc\em]ZY`hmw~~}|w~qxuoqx}tjg|wnxvvw}fbqx}x{~}xuuuqtopu}{{l_kd^_mwwu}vwtxwxurvoy~p`jvywr{{~uh__ne[Z]jnz~|}{s|~}|wxolb\jkYShy}wyrnks{nnjljhmsysu~z~~|zwwwunctxwzy~suv{}~{|ouyy{rg`atrijmy|yx~vuxzpgu~ujjwlisyolnmrt|{{xrgu~z~}zv{yuxtsw}z~xiusoo}yzx}|}zw{u{r_gqsngnormryujxznfewnecfmntu}ww}|ywrz~vvlkvs_`r~wmicnl_[WZSW\hklu~|jgtokmyzwqwx|yz{}um||of^juheeqv~~}yyodmkikxxrjuwq|ty~vvqrosnnmfnuiXeq|}~~}{w}}}xgsupmz|sqlpnqmntt|xhr{yu{|~y~|nyxp{}vle^aukc^eilqu|w{{~}{wwopvoZbnz~}~zurvlefd_adkeho|knu~yfnvrjrwsnp{sy}wlc[mk^WY_aimxx}xuywoqvmb^oslbgtoks}uvrrqqsnrnmqtb[gpxv}||z~xz{~{h^gia^ijjj~vyxwvwh\gnskmqvwu{~zm|vr|ukg]btkc^finsz~z{xwqrlijediiW]ky~vpmetqg`ab]_aiehnojr~on{vw~zwu~wzww}vmj_jxnd`ijpt}~qv{vpwri_iuphcosnkxyvwsvuyvx{xv{yfktz|tuuopqxsuzzu~niswllsqop~}~w|}|xykajqumpsxww}s}|}tpdi{tidlmqtz~|}wmt|vyztqrmiilZZerxz|urgswlcbfacdnlnu}rvwmw{u|zxt}}}~owv~~ujcZSfi`X^eiqv}|w~zy~vmiz~xop{{tt}}zwwvxrxwuryrenx||z{yy|{{u~}|xwq_ansintsry~rmu}~w{}~wx|xok^jxqfdllqrxvv}}y}tyt~zt~~zxqplmfhifbgi[am{~~yq~silkghkqmqwvnw~liu}rrvsprx}~u}y{pqzska^QXjg^\gksx~{yphs}yqlvyvnx{ywrroqkmoniloafp~|vzpsrnoqvrw}vlu~}yxutq`_nxosxxxsnu|}vz{{~~s~xou|voif[gwtjjsu{|}tn||sqy{unxwvrmlijejlkellcjvts{ogkgediihnw|jeu}pt{}{w}tv|}|zuq|zsjf]Wiph`hlrx~}zzvmdivsmfmsulpzz}xywzvv{|vryols~}s}{nppllnsou{{mt{zxicp}srtsrzz|pkpxxswy}{}zwqk_jvrdeikpswu|~xpr|sw|wr{{|usppljopkckfbht|~~{ou}xkjlijlrot{ostkszttpnq~w{}w{~|sx~wpheVZil_[bdkoww}{to||srw|zovz}yutstouxxlmpijqzz|y{ruqqotstzvjy~}xzrdbrxmlihhw|{{z~~{yvzqof]itn`cdhkorqz}yquzquy~unuxyrplmihmoj]db^alru}|}toplkjolqvmi|wll~trmkm}~xyzz{~zqpzundbWP]haV\^ejrvzzsls|ztmqu|qmtxzttqsoqwzshomkmw{}x{tpqmmlpnrwjew~|{xvsvkabuwpollqyx{|}}~{~wwzqpg_jvreghlotvv{tmq|ztlnqxpglpsmlhjggorobhjiku|{zwookkinlovrfv}rn}|xrqp~|z}~xvw|z{|}z~|xw}upz{umieX]kpdbfipt{zvpmy|yposy|oqv}zvutuqx|}qlrppt~~{rw~zmlk}~~xpu~uvruypswylp~scj}wpkiilp}{]luppvy{~{hstlzrsf[hl[]jiaciknmosnvlnx{y||yvazte`{w~z|}mc`xpvivmcjyxnu|j||hpnx{miixsx~wo]ogaixrdspupqsim{}vptqyiwsqxwnits~~xwv~srxsx|zztu}}s|qTL\c}nssdXWQ^slewwqzppjrkvuu|eb{pqpmoxyuslsrhywt|~su]]}z|}u~v}t]frs|agl_\cVORRQ]c\pnTY\[kwvx|yw}~xpgpu{cfnckklp|x}y~~}qZKYayxynqx_Y`YWaRRYYdhc_sj_jg_jov~kdmiuy}rvrq{vn{zl{ux}xkmeO[b^siQcfhhkpqlbuufjuforgkopfpb_tthomixnfhqjtyn~okqluwxogdhdtz~~xxqi^biXV]P\jimjXMbhfzqsru}vvz|ehqjgpeni`mmgv}~~dZTM_vyuhj[fsfPQVF[l]UYXeia_intuj}vt|{||{llvlskv||{pq|m{wmpedXYZ\_cuihsagzs]d^fsojmzlnd^wruyhhquhslhnecmpv{z{lc_q}w}~|soyh^ebitib\Vfg\UmstpZOmtvzvstx|r{|tt~qrf}}thvpdhi`jbgzwv{ytt}uo\Mbehjznqv_eqecic]`e\`^_ia\nu|xmtkuv}y~o}|senf]ttso\\[HMb]Tdj|mvlqvdleqsifjahprzhlbhwmhonrlbtrghvqiwpW\cq~xta_Yelf`vpyo`b^dcbgsejY`nmwn{uo{wt{ukn^jhb\^pkbbvswxk|z~orfx~xkpk[e_eqimoyjfh\a\cka[X^a]djnl}|ijkvw|}s}xtrkoyror|nr|y~hn\[a`d_dnswyolojhxlrhotgb_rs|enaelakezuimsansz|kYf|zv~}z}x~|}d]cxk]humyytxy_Z\ek]hknkeViulo~twztyqzpii|rpzrm]ppmspy|z~puwlfofbdQV`p|ysh^va`aca^fdilSnpvoao|}xneyiux~tyrw}}qwv}{r}t|wrcR]VUVP`ky|yz~msquqnzt{ehtptvqdjocusdjblnmrftmxnhpkx}|}|vdnk`e_dnmztztgtekjhrsuyiihixv~}xyp|tunonm|}w{dckgk^gthqyqzpzrkhZlr]^VOZezt{mw~somigjnn]piedbqkhusv~cbhvx~~~ryv}ulxxv{s{xx|{vwpigSQQVY[ajl~|y~|}|}x}l}{onhu{pqlhobhgchnnmkqjj~yf^ikr~u}~y|~~}t|ic\elahvqxnzsrlop|}uyaglu{szpvmqtppu|pp{}ql{slhcljbkdxwyy{ptkribnmh[XWdk|zprwz{sq_pqko`cnfghp}rvnhgt|x~{pttmt^o|zw|zxtxlzk`X\aYWc`i[b}wyyvfmijxpgzk_^ctljztsjdx{sms`hk}|wzrutq|wwvpvjmllefk]jy}q~w|vtrtuq~z|wwxvx|~{ypuoy}ofkf]d[[ubacuw}tsre{tc[^ssdig\`Ww}iowxhfsfadZk{mooyxfnt|uvyovoyhkn{yy|{|uwxzss{qq`gXZX^\R_goi~{vtfkxptmslorkmmvdeoxjm|xolZZuhkkz}~||vturtnn{vjrurmUmrkoz}}tjqnt|ptlm|v|jj{~~plqZafo_bfjrp||t~ltynckiwbfinp_cEYmw|y{|{sk{{qf^rqyyo}~yc]lrzmzsoesjZfrmwv}x~}hWfuyakrhlJMpmswcts~pe{spbookq|ryj\ztolvbzxewtljd|z|x~u~ky|v|uwowzoovvmxa`kodcwh{w|ghyzxxz~xj}zgk{{o`\hk[c[cvnkx~|{hmywgsbhr^Zgmkhc\^f\f~{op~mfnwnhaexqlxnq{{{~}zso~ohxvhsuvxy{qo~~zvuqekrapiVSRNZWYkrfs}vnujzznlpmluv[jp_\gat{nglkn}}z|tqvu|f^pvntnqsohnriwsbqygy{{en~{r{v}vfdq|}qso_gsbloiv|s}{~{}iqqgfjlln`fmb`qia\Q_kpkzxqwuqvyenyer}uqvuvx}t}z{}im~~ilwgryq{{}xjhlmye\ifSNWGUlh_oo~xkvrou}iysrshrj`qsheuiivo\gm|~v{k~}zxv~~tppkj|slv`[vrW`_`hd]jsp~yygk\^md\`oiagzvpoi}~v~v~umeqaXpv]WQWpphbie_`Zovp}upuo{~zvzfr{otqoyt|nnw{t{|}{}{|~~turjsthdw}toTLai\cY[^nkp~rl}t}zih^n{phpys|n]qwlbosp}ruj|kj~zjqq{ws{w~hr}{xn`hragbdf`eql{}~|us|v~wr|ihfgnh\ghjqn|wsl|md{pxoeYifXcfnhSOSWkhqrunv~rz{s{pkyzy~xrwu|ykyotnz}zyp|~}tx~~zurhblnikd\cN^XYZXgeaitu~vkumtujflfvisw|nolfj~yo~ov~~yttw~}}jguodIDKO]UPsws{x{zvwxhorsw^O^afVWTKf{hargqyjvruloj]`MW^UKZcqpQYvskm~yszp|}y~w}sjclpu\Oqsgqky~or}rqrozv{{yu|yuyv{kqmxkdV[h^ZdfUZgZi|gaoxo}yudw~rw_~~v|}{~qdm[\qlpmvmanXCPJ:VfGPiQPlxqu`[xyxpjvmsztpghoiolvg~~dfxzzrz|}pm}q`rub_Zi~|z}sx{ruiWt~qsyoƫn_at^Ztls}mx|bld_g]QKSk]ZYWSPm`[ttuiY[{whpmhlsa\tkupcs~_^kildvUgfelkjuqZvvkpwsnplnuz~{mzqzjL[g\Qdobwjp}zkoejnb]XaW\bmRkmObxrw~xf_xi{|esltorvrrjq~{svysju|~qwu}shvxooticjdotpvx|}u|y|zzvysnvz~vĴvtkUongbjv}lyx}sy|c`dgZGhXTXOHbkbskdzcAbtgxrdggqcgetf]qsdf\sjh_ebpq{ol}h_itpuuqbsbwqp{w~{~igmZWWe\Xg_m]vorjnndQlbN^vR]c^olo}y}]`~{y}mcxs~suttfwpntstz}wq|ooqxz}cf}kUenfu~{uzv|rv~tz~|j|~|~}{triws`fmlbbhwsnzkUgs_RypWWLX_`anygcrbSezlwde[jonucfzhPdomheft^fvictmknesrlu|jbhagts||~~uxaXia]Z_M\YUof~ynvucXumabxv]OVhxmqy|o~mb|}stsul}q{uma{y~uqrex|p~{g|xu~_Zw{e`lus~}vvtmyvpwsr~us~Ź|mgvjngYejVdx}qxjryji~b\MRfqi\f~g\i]]lmroZhmofuahncgljenrjkZsysejyqeWmxphrjgYrffssyssjf|gebakZUL\UWrqywuw}cmtqmtngeaj|mr{nyql~wbmukyr}mtuvuo|{{}u{x~ct}rbwjPbyj_dqt}{zsoqut{}ct}}myur~mlhuu[UjoqzxskqrcpZh[[x~lWhzquiessykx`^evoemql`\`jbmy^ycnyjv{ghottgvxakc`Ywzlpk|q|y{yi{wiQdZ\SYfVYK`kqzvyvv{jqfiz`bganvnrv}q}xzu~svu|kou{pz}tl{{n~xqnvrXfcbw{vtfr~{}l}touw~lzuttejkfi^fugTcp|xlllh]d]iqns]bnqo]ipwxjwpiltfcktshk{crjfu]spswoprf`pefu|fc`Xkx}wmutzzrqxpch]m_deQaZYPZhswu{}lcy~ttyxzqrupjyxyvptq}vls|xur{oyxl{kvusx`US_rtusfw}{lyx~vwlgzq}}}uyvenpwjtq_ckhmy{wpqktulpu|ljjlyy]kq}pplmemurmYdpg\rmcwXuxr{rdiuuvuttqp^`T[kxn{ywyx}yzko{|YXS^[ZeMaWT`dizv~yuy}jn~op|y{lv{gxsszum~{spx|ho~u}gw|ztklchdlyolfs|r|t}omywy}zyz|~}wx{vdegrafki_[\chxy}svpsnitpc_lwtlbnn}unojlqqywapbloZtkstuw]]lkkotoseYhdguent|s{}}yzwz}s}w}zruufj^s`X\O_TQS]`k}{wtszztz~zoo~ssxrmxqnq{iqywxk~l`V]Xewok]tyxw}x}egmpy|w{{{||y}~{~|wyyzv{vtmq]]cnrnpxlurti||t}|blpvz}oppvw^egirienZvqw~oryrow}|miqmo]W\\fywecryu|~z~xpxtpzxwkdrof`XYVtXXXIbicerjjrwpmst}zsvptqvx||seuqtw||~emwswilkc|{npcl}}zu}~w{}rz|v{wsj}r}u|}yqvwuodi^jsiaT^iff}rzusu{t{qvizxo^wsldyzvz{yuwajgbmdvoxyohdem{zcmldxmgooovxvz~{tz}}|pvt{}ujm~iWbrruig`RlTVXN]a_`lursv~~}ujk~~~n{osy{}wqntwy|mofgjZgoe~{jw|{}}yu~{uokmomsvvw~vnyx{yzxrgq|mhpl{wpwdebbimxxm~u~~ww{p~w~lbq}{|r`qmkpyss\hr}x}jqw|zuvowm^rhdk^`er{oou{}zz|}x|{|jqusyz|vk_erpqjaZ^be[]kLc]K^dmvvici}zxzxzzsyy{tglo{|{u~}omgx|y}fbleynoq{wzuw~}q~kuszruqr}qev|n{zux}bclkooddifp\X^cpwl~q~}}|rw}nt|x|wlyr~}vr}ruk`djsj~}spmtokytlw]a~u|yk_^qz|wz|rtfdn{unytozunxi]X^nuujTeQUfPSU_hlmmekxt}}vuux}~}vwz}{urzyi\omni^kdszzyxov{w{mzfupw}zyvmeky|}uwqmohysvX_u~jaZ^qlnopp{lkxs{rzrmv{iphwrs}mruvxyfupxwjunlr]eogrkjh`kwvtq{{oyx~swymmyvgkcctwoXMUahvfQeJUhbejnf__hqx}zw}xtpvq|qontwxr|nyi\hjswrrw~zwvpeoqsyuvxqhlpswhfj{rx`kudfRCLdx~og^Lbfdsx~iv~zrpun}gruhr|}s}{xxffiurgrrywwqr|qyzy~wwxripqvqyytmdemvxcf}jSL_zkEG4EGIT^nui`fm}}y~qx{vwotz}zpqy|~|xrrrlz}hprZkzrlXXZbtt[fc\pl{wwpssquz|totwswoa^co|q`]hogzcTlWPUUbz{oirhvzohqyr|usvxuib\c}pmcRckfbaWXVTjgioiklY]ha_U`phujVU\jzvk||v}zyrowz}vljVWLA??MrqtnyoeVNSXY\aejcsvnuw~yyzp}qu~z}}qzzpqxerkdyrjlmVY][[Peeesof_\\jyl]mm`tvlnjhmniknup[iUJ^ZTNSeztjncu~mwt||pgotvsiovrmdc`][_ddNR`VhvgdhRXjmmhqe^kdgkry{smqrqjzuo{~h[E@LRVTXgydopffd^WVSYjvak|ev}w~w{srdm|xo}}}l`qfjrp~yysiqZRalldp_WcipohdcojWitbgfacjush`_cytVdSEXcdcgkry}ws~~qx{y|lYcjnrv{svz{|uaVW`d^nh[ipbbbZ\^b_hziec\^k|x}jl~qhptx{y{xqv|jhX=BGJS^\qlcbhone_atj\zsku||y{v|p{}usv~{iwz}{mYob]is|osg_\dc^sk_`cedlj^jfPfyozt`X]kvxoietlW`TSVVWW_qyvup}|ou}xptg`^eyz~~xqh^d^[wp]hp^\dhopfUakdurhcj~ytxrs{tv}quqmlkw}|oZ^TNVRKITThumppswjbxfZzyz{rqvxyzyv~y~szybo}qeafmZXfghZmzp~wrqq|q}s|{uoruqqv|}m{kthw{ybpjfsuqhgnegpeitkuvmg^aihhoffp_bgwjNLuhJW`i~x}|~~y|}~sdoyv~szwo{vmktz}yt}ru~r}}hYpicRbrquwtqqoums~hmqjosvt|o|}cemornkmr~sch\^mqZPbfu|ycrwu||xjfanm[idenf`n`S`mshom\ckrungfsq_f|risoor{zutxt{ciwzy|{~zu]LQSTW^j~~`riT[afibTRXdeewni|}syyuvy}|{}z|tgorvz~}^W_dp}~vfabikcZYfhOZn][bjmjid`__^y}bns\Yjx|ocelaeiUT]iaZm{}{|x{~uXjfapwlpnaed]Z_d[lq`^bfgmn`enY[ytwpdeo{yvooqgmov{{z{xb\dTOSPT[jbsvg^^fihgb_f`fwfwpnw|zn}|~z{{tuuuy|}hgcZchmvpfghjndXjbIajclne]ZZbif]cyodlkfciuxtsolcZda`Z_o`ax{{n~wuxW[nsuztlxj_dfioiS\eZemkfcjf_ggfsmo|wunoy~vsifgt~xuz}qwzfTXKKXbaaohv~vtrjb_]cno_fzsho|{snu|~~|{{~|ytobmz||xceXWkwmq}xxvqkgkc\h]^g]bjonc[^chnehsidehksxriddmsa`^Ya[ajko{xy}}gyaPapw~tw|ujejoqefj^ajvq]cj]]dj~xjnjglx~xzyuqxn`kuz|}~~lowgcaFAIPXbkz}z{}uhehowlhsqghvyyxsnnkt|{|~sfpv~z|}zzykllVUbmddzxo~{v~z}vspe^cc_Y`eXU\fqumfhpqor\\i]^flz~yyvqt|ztviX`UPPSY\hz}kuaGWVn|rrwrmxzsks~~ytxzsymleb^_lz}}}x|rlqd]\]aevlZ_~iWIKPOYabdgfhmg^`suu|kovmr}vhjndefejojqx{ws||phstxqafx_Q^TMQNLLMT^UJWc\TdkifbfmnnrrshltZYilgdm{~zuyztzvpkkrkon`_RKVJEbZ{kqUIz_SNJcQZx|]TpmjTdz}dvmba{vme~zqxwmzqCKR8rY~n}gy}smvt_XgTR-KslX\qib]vke\vl^`ZN6djUonX~UTQsm_c~nme^Ybblwyitlp`xjWn{ǛʱwkvYD6M=MwmtXQQvg]vnVOj]`EEh]qv|wewvmtinyr{d`_5PfQnybzv~}{ưzshjoawf;$hаjwhaPql]U^fNXV\;DVARqcaXT{ogfrvecPT=Zwxgzvpkq}wrykcXW]AVtΰofUHcjRi{rk<[t^ggN]ZMmz{ww|uqh|f}cT`A8e@9Kp͸z~{wvz}u|xuw_2Ykjr}ms]_hX[_}a\_dRRhQSi[vyoevhmdg~~imwRWR]KXcnytzuoe_`yencTU7IYiaobVxgrv|VlsqoU`aM_mi~}pzdoi~iftqcvlnMBnN?Bdȟquzwu{lozxvuxX+PUJ[kp|w|rspaft{X\a@CUCd`^^{fsYv~ezjqqqP[Vdn{}swkudhwhjxk¬trm]eW6@IGBwϼrxn\_i\fZQ]=I?Nhaiwsxzu|s{|~erhLHNS[sw~vxz~yudi}MAN9FSdӝsitlWm[[LQu^FSMJI||cztk{pdfuMPRB@;p|~{sqr|sxlbqRLTPMjwhcQ|qFQi]YQVqaZXzyz}xqo~_}]\HTq9/IQH{zizr[ib{{hm^Q]IFMPjpgzongss[pi``[Xasq~xczfc`X]M^pYsoFg_gf||}~f{Z{VUupjl_RcX1K3GQgx{yxrihp^bgSR]^sw~hxToRVtisnvsq^q`][\byp}mpf^qrk|llgHU-S9&QW{̸|}sreh^ZobC^DYQfc^u~oi{rvzsigiV\`T`nwscixzy~zoyfiQBR\DahR8fe~þ~~nvcfhLX8`N08a^tv~{t{sucdXNb^CfWow{~z~~nabvuf`rZWAWSTet°vph`X|SARgNj{lJou}{z|yuxjnh^hBL0PU5Btˬ~zl~j~uy}mme`PEMKNhq|Ȼ|hYqzezYymhZ`ncr|{}}lwkrvX`Kpb;;\JMry`oxomb]\h_ĵvmqg`eSHK=Df`osoz~nphYZa_p{{rw^cVkLUx`iVXl\^Su~wunhlfQh|a{qqZVeJ@M-3/[yhvշrosknq___Qg|kmqexU\kpnltr^oi\cYS`sunrs]snj}yuperLE3GQ)DOIĵywu_iqS^\DKM}|`kzzwsou{o__aYa_Xfhlluvz~~z|gcVqsI@^Q?_fJ:bft{õxl|mSNKmV8JmZpyzosvqzrfjWNaRKdSYc~v{x{|~voacmeaSm]TBQWQenużmdmiPftSf~rRk}{~|z|hkz_afD<4SY;Duz~í{i{yi{ptw|s|qduh``OBEINciyzqp}fyo_gsivfmXRLeXEAW`?GprzTsǶyz}iqoxhdztx¢|qxul\~fI:I?OV`ojpnkn[XYQwv]uvixqpncnUiueWXSMDhtn{yi}Vy{spxfhXhZI[7(<*3rZatٿŽɯ|~m`q`Udvtgi~ds]vq[nM[VxfVwepUM~wvhotXzlwnTxhpQQGGOfsjbv}yztu}ypspzzcWZcSVk[LTQg~{õzrppsrfhblipsXPUMrc;(2QB0Gab_N´{rsvuZl~piri~pqsmajyoW[nSZ]kb|qeW]w`XqzuwıyycIPP[njVc`i~ls\rttfzbkwylvp~|dq]eqlvnnu|aZty|~fcZdprodG_k]iot{tudowzyuz||uzxv|ek]p\fO>[P>Xklx~he{źc?9}XZ?4RXZmpn\rgf|quynpnxkplfWt}woX{}^gynl{zfObpV7Rebbpsr_`qyrphn}~cynsst\`>H6jθƩi\eYONI^VHWKJNN}_n}axQXjbb?[urnbM_XuzsqIb`ln[hj|x^nXtdirhcf^_[qgppcW^sirysŷs{ƗutM"; 9HS秌q`sbV3??5cMGcM~idMKzX|n9Lx{uMFPequ{ew9QZFrZklqtjpLfTQu^r`čk\a_k^ydmYK|brĎ|yv̋{Ǿٰej#:DA@p|Ų֘xhmt_9DR;=h;b`W|c^>qZVV7j}kq}DFXintnQ>pVVgg}vnpwxhy[ZQkDbjf_}z]clfw^|Cjd|v}|my}ȸyuƹ֟}aI L=(CIxȘ{}fxwT8VU7LU@hT^ydSHyVf~H=tzdwyDJ`myszFP|[bco|g|trljsWMRcAfjj_wafribtCsguyultzͰ{zuǸ՜wa{?!O>2GMę}e|wV9YW:PW=aL_{dVGblH:o}fy{HNbvvvzKO{]fflylqu}n`mQyKCX8`qoiĿ|hfvruiIllvz}zzku~{|rºңvZpHBF2QNқj{}f;U^@Eh9YQO}h]DvqdY1_|ikWS_sv^Gxmdsarwvm|tyUuqOZ\0K9L}u{y͒ttkmisvZWt}xo|pr~Вxz{ì¼oy\]_ 'O9U^sǪ౓iuTAfRow`y[^{zwG_}aygtwk|r|~_ZvWCu|16B5v~г͵sqgsv}rwNztrxro~|ysŮp}{|Śn‘tisv[Qa1JFQrkdxe?^[@W^7Qa:)@0mؽ}|yk}i}vSqqwvxiyz{s}}r|Ģm|kfcmUN]5HKT}nǗh|jHbcFYd6N;Sh]K~yK1`zdwjitp|VZ}evkktmzr|dOkR:jyb|ngm~Rizeizmpzlo}tWWiG?uk%26;ܭvlmm{um\uos~giuɎi}fwokqzI[PNYN!4MEnǰ׬zx`YpUQAFE@qgKtxf1Pxoklofut}kYzi}bf|mogviNeX=Z@"8)eψsayt|[tssuxrbyoĮlynvvŒeueQTUMWY.(JBJYϪ{gmuYe{@OBTngNX7_{iqxhiegr|ke~hj_~hp{kmhQdS@e|8#4)k|{xˉu^sp`vxvm|q|p`ygðnulwta{x\PBIHXZ8)E:cy[zãƧivq\wjBTAlndXJ?jygxnner`swimziuv^}{fvmsicQbLAmu/'2/vzvu_qq|bxs|ist}l^|fŦiui{q_xqY}G?CGXX6+E:lyaxɥlys_{jDTCpndWJ@j{k{nrirbqvmmyjyt[|eqmnedM[J>fw4%41w~|Ljx_pqdrvvl}h}o\sipokooZvqsPuR6AASX>*DBb`ǧƳssxbtITHcpmQY:`|qzzqviomyug{kf_ii{_efJPL:UzN$71g֘{ikq~mhzlp\y^d{{eoa~qd_tqYXn49:HWO28OOwԶjxkgdMTKq^^|@Hsxtmiy|ipwrlavct\st^W@N<>jp503QRvßpqpe|MUIwnkNM?h}}{vst{qiyn}q[{cj`fyY\RXE5QX~wĤ¤sqsfQWLwooMQ@hvxw~siyo}q]zbi[hvXZ:C:;cwG4DLٽyiswfnszdw`{gZpn{{lf`v`rNhw`a=fI0:AUWD8TZ|ؿprok{SXPphPIGr|wt|nnxwmar`oQuiZQ7A5Eoo<:C_תsmw}ycsnbbsz^^zz{}~|ei`~z]XQuceLHf58;KYR;AXdyѱnvlziYWfwxWhhAYzltjxsfgxecya]|X_>;:9\wV4BGďrwzijswf|^}gYkhtypgb|~]jLgo_^GVVB6LT{sԺxw~iihxwRRNpwyx{r|sbr_fw|sLrlV]896GjvP>F]ͱ{~ijo~^v^aWgyvdmtuwifvZhdFXw~hS^4[U7=DUYK9LVlwy{okguR{[Lipp{ynfxa`rxvQbwQa@56?ay`CLVuʾ}|reppmmWziX^tbspqigmwpNuIHfzzsO]B?e?:?O\YDGZmvΰzxvlkxkVtNX{{wutxgei[esvdHv^UV357OsyUNTzտx|zlo[FDNkbURerzxv|moXgtgfiftifdkvpfm^rvv{ulsupeuoxqn_\zt`Rjn}`cfnngohq~lqj{y^tk}}vupwih}jjnwow~p}}iW9Prnp}yy{ykzm_}{]tw\nrwtK+Ivɻt{ygHY[_zt{mwmf}{eiz}bn_}{ip|vstXO-8n˥m|lyv{psg}sywmR>>{ٝWWR@Yj^fycThdQdoleTzn~Xux]z}ymk~u~mwgLH6/fǺxpmvwwiu|zyS;66uܱokNZk]^scHZ\FP~|g`^LjjqXn~xZtxpfzvyijzaJH=/]tk}xtwhx{p~qO<:"3mزžytT\m]^tdEWZCKz{f\\Jgfm]py\qunezxwkk{dMJ?0XvjyxmtetzĪq~qP>;"3eҲĖ~wY`nZaucATU=L}{bXSGhz`mf{w\v{kf|zzhn{aML<2Wx«rl~xnnmd}n}ŤumPF>%7`yǩƖw[gmVgx[?XR:Sw^VNHkqUqks^}}hiygty^QR=7Yqƨrrvyhogg|vjĝwmRJ>'9Zrɛ|cpoWly[>WN7TuZPFGknQmsua~ii}}ity_SR?9UmȮvqxximfdyrf~šwlTMC,8UmХhqsWm|^=ZR9RtYMCDjnOjwxb}~jh{kuzaXXF=Sh{ȳyqxtjjjbzsezyĥwpVNE19Phԯrs|[k~k@UX:H|{[J@=brT^}curhxvmp{eXWK>OcwǾsnxap^{ncpwu{tXPJ77Kb~־}tdc~uHNaA>p`J?7Uu\Uim|iqwrm{jZZQCM^qţx~l`|~`pocd~vux^PK=8HZw̤xxZy_CdR:XmO?2AnoRryd}|pkszktt]YUGKZkķzypohk_pbYu}ù{oufMLD8EVoҹ|bitJVeBCu}YC52Yza]im~mpv}nmwdWXOHXe|~kb|bmygZ^¬mloUHH==Tf˧~Zvz~wzoefbD]rmfapiedercWqlsz^Ubrf_krr\gzuxywp[VVRgbp>a}qa\KXcWcWT^Ytt\Me{xnfxiwisXp}ce{|g`lqmyjvuYwUzsleHeWa`f`gs}eS~tUaooN]X*TXaidaab8Iv~l[ukhgxcthtrwp}UU{mu~waR^|pf\{vti{{y[@\gMoìzdU@U]TgtwDbNN[ey]\_{y|iwY\EO\V_w{{ri]hnrpiy}|neuuq_|rfPD]iep`epOTkJ|;T2>lOvwcjMzxcyieYkRhxy{nvZNqc`}tm_mwqyyƓgrl<]SI}x|hxgeet}jhgX=`fbLUiWpwyc{Yvn:erEXDs\}jRknmsxu|vkqjxdsy|v̡}]QYSXTzs_tRNugGDoDQtvOH{utyo}~bveh[Smfcnji^kcMnhMmxprqq~yskxrGdomrXnlcreb`ePWS]oJV`xcapeqYd{JIATrf{ikhYhgwuupswmntzogpwnVMHk^iiWk^oitpXnmw`Gqbfxgdwef}dVkb`Wir}~qtS_XJzshue[uzmx{pskrwci?e}Yp|~l~bJYu\XUbR^yZdwI_ZHFVtT\zy}__ar[d}}|oxvkpbak}dM``P]nsqSYb]fgFzp_h\]sfkxgiemdhYjbZbhvd^CcSNaj}}}}xo|}қ}xqGFyiywrt}syhgcYje[CciUVZ\ym[RLpiRsry}rwxqheaYff{z~u{izytf}w{epLQ\d]pn\kRK{{fRQqe`f|gRmqeYobdTsx}|pthLqlPMR~to}ts~rwc{wIKplskktwmjSdkkMcXIVQK|LJdxcSqzt_Tu{qcP^gm`~orpmtq}r}YRP^XRxyv}pzy}fnksoX@\D|~{ZMPEHWtjn~x}g~}soVW]hsqk}}itnG.tkUS{oxrhvdU9E[fazvefkwrr~~mzurNDU7EozfWo}xwv_Jf}vnzryj|kFEi{uWbvoYNowko~cF?ms~catmrѦ²T9Eɟ~e>IOB-+XhXQqu|nwuZ=N}yUcodqejHVkNj|lqhcHO|}cezxuZFo~dʵüvO;YsϗzgULA2\Q8JeUwiccs|rX=:mTTyfk|jrki}rSZrykxfFgo[|rUfk֧[bl[B`pҵpgpG0Yb==mVcd_[dhlZB.V|yXJzt\}{sukPox_Xup–nV[{\zy{]`qʨNKedG\nyÜype;QmH;moTo^`]ZbZK/IpssbHx`qzzytE_wiTtkofVaofZy¯TC`hKUmlǡpqDNqS9duR|x``]YaZN/AiqplHtclxyvyE\wkRpi~rkYcjhY}|YC`iIQhcĢv{LTvZ:avT|{ba]Y_YO/=bmmqIrfiwxt|}GZwkPjewwm^eegXxóZGfjFOb]~{Qb~^gi>0bYy~qusb^qxKQkk]ld~iChs{yV>m}[n_Ww^|y~upt`xɻҺgWkY"UD_ivyXVwkxeefdebBCdqcut`foZ]SSdmwXOzny~in~ewȟ]aiYzǢ~VCWW+6cdLb^V|]}qhd~pr`{l^zi[]jnptaI`yja]SfUA6~zelnqaz}~}wp}g\jjYam{|w|q{tnp}Zcn}narY3]de_\zyax}¤wqg4.RpGbgeXa|}T,4xOfnVWsptnSet`HndjoyolrfoujJ^jezqwU|o~~z}su_WuqlmektŠ̎gSDXelaE4AVXgWksz{wMY_D`tqU{|yn}sfKSX[NjNexaarzRb`fP_y{̬ſo]`;>ZaASI(9TkaLrhyuwRFSplPaxekkpjmU]m{kfmMI3ryxu{|tnXg{n׶ßqzU/(>I`j˷{b|ljAhdFYcqJ7y]_awfT][xd|}oime}ztR9Fbx`mrb{zbuukxvSƹžB?9',W`SWqiapfT_WVThXG0&@Pi]sylornjZSeoSObzxtv_Snyv[fT=4j`gf}Lqz{W[LofTniLT]JCcg`lrg|atskm[e`kVO8az~vqnjvm|{n^X1":\z\~iY<\\aQ\{c[HJ::hXdSxs|TO}o~gY>RZUlykhzzsVw~mczvȸz~F$Gk]`y|rwdZk]PGbfL[@8Dj}wmwqlh`{jxh~~z^kkCEG:Guv~z{ypzxsrgqYAQIID]ȟ~xrmTiibW]mxQ/1fiO\l~{e}x~m}\rRaTLL/AkzomyY]YceQð\yELCNAMouϸvv^ai|xebSS[FU6HlTaFm|ui^{kguldrzqzkP8R{wvxyy}kpfqqVP~R-/6-8xໞ|`tkh|g^VeHWUSBVcei|shnn~tifynk[R[2BhdztmmsupmiYnzsY;1YAEKVudwuwXuvSDFbkJ5677=w}xsy{j_xXqa?ULSYu{qu{xTez|qʧtihuZWX< Cڳ|}nrzgc]jyXQE7cIJOT`rl~hq|i|tm]5WiN/.DOcq^_ojzphWI<=@O;IϨwjfh[RodL]zcY[lT:L~qsor{~yy\[xhPNV<_nh~geqPjb`nyzĶxb|rWA6k?>Zkr|iolfbatuNPU[[\b9xwtsobfm`af`lnqcgrabjkY[yxpynigr|GkjB%PdPNhĢ^soTpxKU5`g=Y_qr{bg}o}zbXynJ;K[cn{yszq|ypz|dy~}pUTHC?>J_PvT~[YTaSC/DXbeznu\u}ubw{xsmPUtc>4LT`lnrt_Vj`HO[^MXsj~azo}jnnZMAGDVLNOm}{mpo_h~~{1Of9;)I]^atwj?`gSJbd_nTa{|soy}vtfLZaH@KeiapĒwuaYk[Uxrwid}UFVm9<:@Lbz~Ɨ~tjqYUA72Zi[aXXgznvMXSCER_RVni\gtcj~}yŢpl|wxzojm[+-N]ZYsڢdY\JTVSZoplj~hrsvqa_ptiE0+)@NZûvVyucbgt~zutqllaKJB;UlaWWR^ibü~ix~pnliabry{|pxopiduw]G6HcfKROS}pkl[Xfipyt}znmljmaI+Q\DD-3BHZy˲qVn}uxilq^}||yyv_fuIYcUSZfb`o~}eUfgvt_pXe}tyfT{mS=@QLG?>IVjwسûYlmd^PQQW_rdyiwxX9Lafddojd{qk{vxmhnzrmjvxy|h_[QWova:5<2Vf^K_{zjjtnQ~[enfKIdP=NXsrpbhwwb[_~n{κĺhid^dOx_disjgun~{_tl}a`\Pn`6QxrQ}~yevwghNCF2PP]DAj{oirkvs|Zbkt`jk{|⮥Ůq\ogtydoUWmS]q\hlX?QhZA[ESeNO`}teyZgag]cW:WlQRmuV}^{`Pkpr|o}Ŷά|opnjj|v[n\eru~ēwd^sVcLe_SgziBloc^o`oxoVVZJDJNR>iZad]q{n^epqzgl֬z_RxcUdu`zuVdwq[h}VGBWcLbdUej|quRo{~d{RJZSc5RY]u\j[onp{deoxs~wwz|ӿmolo_R{p_v^jlzw\x^EThIGYqiIbp~dbmvmhqiw};-?YG7Yeavk~v|{f[d`mţ͸easXYkvkakyZXYpsBbnwhZLNnzKQtu[pr|cHliP@GO/Q`q^|rjtxftLkyfmIJ˺oTysXPs~ZcXTyxRZkpULZn^idHfqlLPlqi?G^RZLQFb~^uXw\bugnh~m{ɲdb`b]Hkzr^UpkaY]vckJZtjktzYkr`x`fqkNNPOQ9H[smxgaRnvpyz~qgkwvҰ̵UjTKiVQmyWgnW^oyreSfeSS\OKog]rrK\ifT@LZQb~Y\vkjvoxd~|gWdvdawײƜ}sleg~[aXQjl\vojre|ǿtjuvYD.6JkY8Ffd[lz`X^gZOJDWMW|\Fut{a^ziW^y{xojljo{~s^TudajW[uj[\e~leĵz\|^XOOLVZXU^eMYkni`geQcYBT_taNq{seewxzx|VcfwfriθâkRmvfM]YIrm[nuqbgAim^V`XcxyS?r~wmy[LMb|jY5caRgut^}wvu{vryq`mVGbz^Evw{X_{nvnoMhitoZN_YO{|uXpvMB,PiaLS}yLcbt}zph}vp}bybeWxfCsqk?Wz`ovf}^bJZ^[]lm}SifWBO?SidLtwVo~wm~v_zj^nuWPQzh[sjrNtzor­ztecaT\`QO_qwAoejtFLKB^aMcmq^Puqej|x~ww~zT^\`tdIhhrrV\{q~sx||~tmINfl_djo~d\Z\=GE]ziSvyt|Miyy}xu}bo|pj|h²ml\XQs}TN~p~eM|rqįznzjiVN^aZcedqnN~][kEOLCdnXk}Zb}\kkr~~l|XNE`xbFjvi?Y}~twthgVdl_\lpjoOvicH@;>iy[P|||fN~}~wvqsp{jvmzbZEPvqWjqppSYr~}sy~hcSfq^OXehzZb|\YQKCducXw|}tV}x}xzygc|li~b|UPGZ|iI_w~{OL{zĦr{xu\aia]dkgpM]r[SJJ@]m\Ns~tyXt}txmdup]zy_QAPpiTalnqMOzp|nq_kyiW[knzTerg[PHIikVR|~kP|uu}ur}jdvq~}sczVRLf|hOclmnP^r{¶t|w\gpbWbkjtLi}jjWH:Enp]Rt{|k_{}w|sujlhatuYKFm|]E\yioeIb{up{xcnrgaltoqLp{urUREMpiRMzz~_U~{{~xb`gukwrvqTXSMMU?Q^\C}s{zy׿Żv{{zlL^y`rKjssywhgR[bc`LppaN_y{wgukpplc}pmvsl|WzYdePa58Cgdq~oahc|spr|[Yj`E^cpzv~nJX}cgbomgqk\mszl|||}foTcyYc}X~udqpqH8qdV[rjc~~{n~MCKf~wkoaUslp|w_c\^wxzm}}ghllL}q^Q|_ǥptSֵ^z{ӪïUEZjp[V6qQɤpivkUVUXyP5oNP,8xxqEAy]>@YYL:Lc_yxPIykfl}wsvza@J_qaYBkYjxtsr|i^qaLadee}~o`rϽxm̗rs}Y5SXI=Daxfc~\Gmsknn~uxwmCFYqd[CZaK~{usw}T~vuDrf]ibqph_{¸kˣwxwo{m=4C\XLFXJC8uz_}OzKZtpwfk}uU@MdkYNDmBvʯ|zv`qn]Lq\i~ccwti^|~~ȿzoŽppz[37I^PI5XSL΢}xzqQnhd0VolaEBgrH?LWGA@kjtQnLcyv{ckxuxJ>KdcTGMl@˦}|yZnUR}~h[qu`atjc]|wus~{|wlnuT/3EXID7_RU΢|pXpli3Sjh]DFjtI?ITDAFnlyRrNc~zzajuqzwI9E]aSJLsF̭~zazp_Lx~k[n|a^oja_~xntrtggoY-,;RID;]dOө}Vfw?Eed`IE`R@AQDBCcU}SZ~ujyqwtmqyW5:L^QNHt_iȻ|sd}|}Ibzyr]cg\cn`bmsizhi{~wd_dc9#-DPFGM{Wؾ~cniwb8XbaTDSygG?KLBFfit_dRm|~aiuiird92>WUOMl{ZĜ_rWRrsr_^xo\Zj_`hxlq|lvlmzur_Z\bA!&9NEIMf|ľĜjhqtn;Ra`WEQspK@IODIbsZmRi}_g|~~ughmf;0:SVQQi]Ơbr^Omoo__xp]Yi`blzjoygrihupl[X[_?!%7KCITfhtoym>U_^TGXykL?JLDMqmx_eUnwz_fzyzmdfm^509RPNPv{c™{fuTSllkZb~iZZg\cuqgu|[_lonfWW[Y0!(@IEIb]ſشfn\D\]]NNf]ICPHJXlatZ`~ucsnnwusdfilJ02BQIN]_̰htsKahi_[qy`Ua_\h}ypjtlap`njjZWY_F$#/E>CM{ds÷ɣqwxFU^^UL_}mQDMMGRzhs_f\tspbzhxvwifioY62:MFHPzciνo{zOZdfaYmeW^`Zeznxj~sq[x^khi[X[`M)$+@:=Fq``ºϪxOT^^XOb}vXHLPHSui{Xo]psxZetsvjeiob<36JFDImjUɡzsy[ScccWfjZXaX`xgiwzy~Vz`fhh`W[^Z5%%7;7?\mKݹwyjO^]^RZreRISJPbh^mahv`qolsrpejlnN63>H@FXvNy~ѵow}Q[`c\\s{dV]\Zhq}kvlnjWu[gdeXWZ_N,%(;69Dk\Wկ_U`_]Td|aPMTLVprtXy|anu{Y{imopkciliF51?D@GbrMϭqutQ]]aY`xuaT]X\oqsopmmbXoZb_`TVZ^I+$(95;JpY`ÿѮcW_^]VhdROUNZwsw[w~cnvzYwjjnniagjhH50=D@IbwQϱrw}S[]`Z^tycU\Z\mq|iqizi|gPsW^[]RSW[M.$$65;Jpf[ֲrW^]_We{kXMVPZroglfi~|sbdtdjhh^cfiS815D@I\~`pϽt}`TZ[[WkjYV]Yhh~enlnvMc[VXWTKRUV:'".::Hd~\§~^[]^Z[pzcRUWWjpbrdrmtU}bece]\bf_?3/?BGWzxaƣ|wttTXV[Vcyp^S\Xc}wtofgyogxTR|dQTQRHORWC*$)::Ibg}ȩh[\\\ZnfUTYXkwe|xdpn{zTtdcab]Y`daC4/=DGXxb¾Ȧut{VXUZVbwr`T\ZdvwleevkduRNxbOQNOFMQWC+%*;;LghĨi^\]\]s~gVWZ\rsererkrRzab^`YY`e^@40@BK_xgŸx|urUWTXUezm[S[Xh|mp|agzahoGXxYONMKFNSU;*%1;?Sraغda\`\g{vcU\Ydtyjik{tq_]s`a]^W^bfR;26CBTobÿɱd\VTU]szdQOOY{jjHa}``tyjKknsuiZ_fofNGHXXe{turx_yno^STX]c|v^lsa]?^pj{}|]q{ujlrxmPEANLVkkio~guedejlzg`[UpxPagar^kbOqkVPSO701BERjle}zh~|tm[[]dij]TKeY`~i_otzSqksy|zdYTadl|Ŀbi~X^iVs_FGFNQf|p]QSMa_\ykXe{nxtGSq^eu~aOGPUXm`~}yiOH=Lvqr\AHYu{[alFDnaW^dmjqruaLDGPPcz[|z}ylxwzne`ejsnlhlr{x|htU4YcUfv]J@FAI]oRLp`vhitg{qt][Yadxt^POJcxso^jv]myv{|lckprqgduµ}hxvRJCIKYoq]LMHSsmrTisX\llivS@jm_jtcWR\Zikq|vrwwpUECA_|wZGPbznUfe=Kp^Y]elltuw_NFLSVk|Xtxtmz}{pgafkvlkhqtwunamN0Wi^lygUIOJSguVNt^ohhqgyn}b]Y`btu_MOMfow`gwud^}tjisrtwtiyyymhRJHNShz~fRIKMjs~]Xs}eVasv\je=LwpqyubX[acydwnmnrx]EADTz|tULVj}|_ThV6Wtefdklpvxu^RKUWbyr]ntw|zxrjcknyhllros~xUY_;5jwvfWQVSdvxQbn`rzkrztfxsvc[Z`dxhSLQ[uplalpzWoriosxgpm~}v|n~_UKPRato[JMOdpqTcoW[hxY`jH?ryla^gg{k~}wkgik{v]GCJ[zxZPYj}x\ThW7Vxkjflnsz{xbWOX[g}n]jnw~zytkdlowimorinyiM[Z6>sy{m^\__rtOvgbvqkq}jpn~mbY]`l~vaNQUvíjpoUZez|aksM=pw||zf[Ybdzf{uxfwz|oly|qgfjrs[PJ]mYZvs[[i}Vk|hmzqhbmovwx{urdvl{ia`iriTJU^ZvhcnwrjEbqf^gk{zwx`WvvhYNrmayg`]fm}eWV]j`w|e]i}|uj@V}rg_hjxtjtvwhq}pzyvfMDGqoXeyx{rgQIUhkeyhCW}x{|vj_fhsudtquk[ewilqhaiorabb~ts|ozl>?mu~ydUSW[p{iKve]o~|~vnZWasydriyrg^dix~hRONervt_equeY}ztsy|qhXTf}wrfe{gXMNU`u|iTOMXzrfb{v^]h{kn{^?^|x~qgno}olvw~lpz|yqwzu[IGJlwr[KS`v|h]rjDJtspqy|mbdipz]uhvwn\\i~tq}zodio}~jhgy{}ru~hpsN7^wx{k`dbqwS\v[_ux{ypcSZcx}neyjoe_glu_RRWxsl`iw~v\c}~m}kt~wwmfXZkw{hmk]PRXdvygSPP`reb{t^^h{~flx[?avmstnmusot||zotuq[IKPrz}wcOU^r|xi[nnKBm~zzzsifnp_t~egyxzvpgao|yurxlgovriltvznug]m_UoQ:]fIrwpUtWf{o`mKDMPMn{rq|zb|_`cyq`rxj{~uuiYia_afalxx}blvsyxvR{{yyhsjg}vģľomwQVf.2DWzƫƯd`xfbdgO_kT`|lv}Xfj\XoJ?DbV72EpW{xxnufgcinoxprvvybMXumYfqo`ũ}v{owoug^njf||xloiN?ED?[wyxf{k_WhsRF\iYcy^QotdfnZm_XVKVmyzwlsqjlFvuT^ygx}y~Yb`O^fDNfj}ƭvpownq\fxmzzts}pktŹxrmUZ?RorpWouaW\E_FMXaZhvw{gn_n~dIatmtvvjy{numN]QUTob}qvz}tu|hz|z}n}}tvqvrmQoL5K1BL=vsfصijzsa_qZpx\_kVeycWu`ZUJaMBQ~Yszj~qw]_prrtqlxlmJgpOMRW@M{}cwjvu}gsxxjrtnhq}q|mr]~y_iAVa]P?HWJp~vġkdZTz{O\twqpM~VsZU_KMGkngjlx~nXl}ibuzu]qiwrjye^ml_ZISw{^szp~ut~x~gyih\bviɡ}zyxxvuuwIE`FT[7MG~xŵ}{ru~cyhoteX[pahUER\AVdubwz~}}zp}sjs{kguz[gysmK[tn\b]H7a[<6Mndi]xqokwxzjw[`wwTOe]qvbcouhba{eyuh~tVajcVubi^GCehpxwxvvcjkecqil^yt|yqktcj~cbmqXWIQa]F7SUvxkȳtsmx~ndovvduas}r}xpumhcd|o^Vga7FVORcjƶ~zk}nqxktwlscwfeskxg|o}p{p{{vlh|pXosko]dVLdTHYWa~umi}yremu\firv{zv}vmulgxg{v]MVSXURMTWIR;Iv}vǟxn|ktrdYH\tlUBZiyohlfhunky`dbHsoaB2SscZbwkoɶ~qzpp\[zhvlpcz}\~{xkk_mknH[|[PYXJKP=:XZNVXaitdѵs{d{pvrpobWvj}}org|vdhsglpffiZVA>_\_EKzzyx~pamx[b}g|gtrukzxoz|{k}`^aAfm`O5Hb`LMUG8MQCJoĩvdm{Tb|kia{u|dm{qvokWV\REl\KJKsjehmzv_dLJRir^dt\UQVXgPgsr`sjbUM]Y]mpe_jjedbaGXYFIyjUdcyy|}|`Td_Uyt}xozmtsvvrnv}s~w^K'uwUZehj~uw[|uwwidgiXpuv~{i4_UZQRabWYrZyuwppug^pabZZMSzyv©uxosxxyfhuk]g~vf@cW8.7SZm|iqs_{h`h^OhmcbKSgqa~kf]dOFP^V^Kluawk_\cM]XJctqzzr}vhdXsiwzmpsaf^nZYc\I@RI3=@[ozw}{qrt|}|[owprxyy}tuqy||tyij\b|}yxy~w}|mtthlr}}xnos[c[ymRJWKe}}ia_ve][JA`?De[hgZIJXrafs|gsk1HHIrwmyj`jpotgmbKPclgWLgWt{{ZdxzxnhS\vcknj^^LV~zzhhW;Qh~jNHV^VYssq|̰t|lmv{kvVB>?KdVjvh]txdpjZjjmdOpyVA=DTknxH\YOhzztv}q{z|vmuvlYOJmrrsONJJ]Z^h\fmznqozh_^V`u|iPFS\bsglVJ_yflkpuzslesqsw^TjZdnqnibOUhuwzgbktm{y~t|pxsytoyfNP?Xzuiptjskb`be~zwojr^pqbcfjt|~eOFDStlzv_|nnelvbqrvvd?@EMc~yiuyxtvbilnfpq|}mzWb\QOTShbfwjIThZjsuaUjſjcffecdqwmbaahyw|npuiko{bo|}uxanrruzzzzulSZQS_a~w^didimu|Zxu|}znhpjc|uqZCHb]ulvfhd[aolqyzxr~{xuuWbt]`VWjxy|ro[HKZ_rwqz~ù{zekqfka\z~i]dJVhuzqx~rss]oybg}rwinr|uZ_\REGKltszrk[cmcqrnbt{|~uskSYY^hrjgl_Zewweee~xjxsxm}u\ejwmodc`]CXl_lwu`g{gUeizo{ryzvnyoquvwqvvbW[TQSqrdynpmjeb_ory|`gq\UcMSfdmptot}e^XY`iqz}le]UWqgoxtxuuX]Vbz|wypcrqjah_e~}pbLJKAET]ltmv|ocgdh~ju{|ve_u_]hledp{n^k`^twssisr{rc}wraniqc~p]ejddV[h[dqxeejo[Yh}~ie}uogjrt^{h[d]NPas~uegacjinidyvmjoTCKdQQ^ardsgUVXg{|w¿t`XZ_Ych\xjgù|lMKjVUW`rxv}ull_gPZ~vvmruxuquwW@t_pvzjk]Wn~fY\ht}|bt|y~r|tm}jKU]Qg|zzvwxyuzw|kvtP-Ucox~{ow{xz~oatixkfy}]<|r[eW\{yjczzm]ypuyujdm]eokyxqvuni`:?xsprq~vqqr_e|sdV@Qzoc]]RFg\KimokkooUcubNRnwwxszvjmedf_W`KBY|r}}kvpwuiTD=$:nm\RXD>IRQ`VQjegkWPjg~}wwji[GO8.Su}w~yx}s}qdLAO5ErlopbYR]KHKGHOZ_GDwwo{fdey}hrmdeodMLk|ow~}eg{vyWNd[@CijTfzWP_\`gih`Na\:;Pbs{zzta{iki`RGSjB>c{{ltysvycfWH=D<+Rsǿi|s^RQLHE[bOYZfhW`xu~y{yys~u|wuqVZeTO:A[p~~rwtu|}{o`YI=6Jp{}h`YHa_MRQJIOHRXknk{iailrv\hsf`aUGQc|{rwdtĿug\acDLR]Wmu_hlp_Vd``fe^VN_PCbrx~~z}mZrrXUPD68M_Troy{vzwynVXC,,7AGfǻu_}ZWXSJFQ_GH]b\kyywwnfbm{vmc^XA5Z\JW}{|tspqrptn{cb^SNKK[UTjgfQQgRFEELMCgqtsp^W[]ZbP?QS]c^q{wyzr|ptlf_RA52DCL\hȷxeozplU[mhbZUSOLb_d|{zx|p^TGFKV\RHp|uz^Rv|e|nidMF]PNgyw|ڷxe\Pfy]X^`bdclX:Ysptvrz{mf{o9Fl]VJ^t_fvp~HrϜqctub]l|sjnqLDaxr\\iYLbEvzl[O\SHKRcd]{q}rb{d)&_nivjhlovuj|`k~w_e|xlȪ{no`^\Y\[[g^Vmɴod=$Dto]B7;ETX\cdgiJI[oneWKffVY[Z`]T_[kǵz||j{Ƚ~mVEXo\JD/5<1?av{osk{{^[^YNE@H72P_wvpeey|[^{||}[VqۼlYUPOU`p\?d|gyjXQYQ17Njqjt|rjstXDOfYEZw̾tz}WFAAEFIQ61Rlrq|q_mbK_ywkz~vrl^M?GR, JȰt^\keUZQ6BR_jmptWEhppx|woirjE=U|twd]\btwk~Ǻsh_eQ7Ecñ{utmae]UZ^luun\DUaZ]QTdq~~rĵ|igccggmscknvtdfbUN/+@\ecmosxz|`flnlfugiiehmk`Rf̲zwxnnpzlSan{~|w\BZiz}w\SXN>G[obShulVq`UYZRLKWJ9SfsǾ{}|l|kVfxЮuZND??BS^72\zwlziWYaF;K^}of`snlrt^FSgTToԴ{f[[]\c_BLg}lX_rUGUl|nywm^_ytpi[K@GM70Ewȯn[LYeZXK5FYgrvwylboot~|vs}w[\iƷ|roo{}|ŸkVH?D6.MxwpYZqsyzmT\l^bgUJUaqfThmeddgjmuddv][dcI7Rsmlvz||~j^u~~oR_lVNNJBBP]C;jĬy|uv|e\wkvfYSW<-\x_Y]cZP`veb}_zygeaXPP[KElºrxtc{vbzzddnlffred̼g\SNLIP_MA]nn^V_`NFO@Ad|y·xpgca]LBTVh˱~wkULJGJG.;]wxy{}}paZ``lu~ywwffyuyb\VH8/7AA`˳vd[gqnZD\y{wnvokz}tjeno`s|t|k_[Y^kzkfľq{pS>7A@7W~ǿzsnligfJ@qxtiS]o]ZggOFVonhʺtooqpsyfj|tdG5BXD=c}o_hu{{ms}`ps^WTLA=McNKzøwhn}zmonZim|vj\TZaMWǼ|hbdnkZdmbzn]v{jZOC95@C7Q}{|jl{mYMNNN[[VxļnaZTRPR]WXl}ypcY^`XZONpzzsasqWMHBADADFdtd]Z^X@Omrgotqog\UTOIj}zwvjs{}zb\TG;7CEFqɲxhZ[koM=YnxwsodZua[lsnimzplävmhfiq^bbZe]C48B4>dà}{y}mJ[{fawl]bleMIcn`r®ugbccel\Pq}s_HFa]I]{pcluwz}db|foq\VTOFBOeWIaijxq}v^jx{ysh\W_\DFb|iaakobc`]w~x{o`TLMWM=Rqxwnrjxup`MKQZeVRoûumhechjZdrs}xm`QQ[]U;12XtȻ½vsbWiu~pidXbhdbjq\[pvojjmtaaj]efSCGH4HYL1Iuνrlp_L]q}fmurog``gW=T}{qu}zmp~tv}iYOD;=H@8W÷zlk|p[ft~tbr{bUY^_dph^tuollmvyhru[KIME:ARR[wķssfb}}vzZV\YPLTaY\xĪumimvtS^x|`akaLCKVKPp³~tVf{eYjfRRWK53Mc_nŪ~rmjhmgUj{tlfWDC^mY\x~mq|tyhsfOGA829SaPZǵopztgfZJ_xpdz{rpmf`cqo^juqu~ymsrqkdtpaP@4.4EGGhƿ~tkpmoq^mvjXKLS`ojivic``aismt{r^NBDNTWLSvmpj^qoZQOMPX\YRiĻqihojXh}ek}f]WNECIHFjmhusl}}`VQIBFUZZ{thhzp^ssfybIKQMKScbcκ{qllpcY{v_PX]OBFQHMkdij`piSNSQ@4F\\nqliiodUl}rlfYJF\kajnit|yopkmUC=7/-9SZUeǻ|{x`^vwxxnlkf^\fo`Zmzpnt~|ukbv{krkc_O=24BG>Puƻwrdc{tagzrlfTGM]pmdzzqmllqwjoyq_Q@7DUXDB_{qsthyt_VSOOZf\EU{y}vkhn`O_tcm|jd_VPRYLAcu~{r}i{aWPHDKVMNn{qm~maq}qjcJHLKLW_RUoķ~vssxe_wl^bibX[XHPiw\c}zegudQNRQFdzszzmzfYNGGRUHPq{qkuyhgqy}wee~}dLACGP^bV`|~}tq|n\UZ_]]UEOh}xafuybdqaMGHIJFFEF`ƿytvxfXj}uxrmd[X[Z]sskswmsndLC=53;LOSk|úò~yq`p{k[`c^[bmf\jzsjglv^Tsu{z]PWXH=AMIF_}ɳhbwgs|lcgdQDPgge|nfefkl[]}{o{kZRG6/@\[Rd}|}{~~hYTNHK]rj\kuks}ytlWXo{bej_XVPJLXbWSmÿwq_hk`G&'+& EiiXd:TsBIL^k{pqpi3@wxwulz`afXQFQ_tn̍``sfhrldtxµpif\_YWVd{e^38mZ[sY~wgprub_hk|beLWil!.ehbe{qmg}cuxXNguodUWNmqhT}ƘvqQ>V{wxvz|`JVnlKI]]`oϫhFF;nhV;,&:Euy{niCfag]^wzljmomiuuudkch}_jo_]ީq~myskdJZKVv͹ʸyK3(5\h̉ttvWgu~qnglg|{TDwu|m@;C1:-@Wfw}[wgl[:iY9CPu^txn~~zmliwHRL?6"mܼ`mj[ER_dgrtRTGK2>dYjyDSqO[]klFSICVc||srii^fqkuzt}ѺfOM7!=IjsgMyYGPPiN@b]_HGj_{ksileZrniazZ[U^Qhz}xjx{cOlfuȲ_PQ"7V6Rn`xbt{zroJRRI:Dgnpt|otcpLH{^TFKcINqo~pķzy~trZdlwr}³mpSO5)S\Ľ~zfZ\`3;C0Sut~oqroc}ybs90:NWxȯrpnwpRrfhYYz~nuz~nbcU`^K4=Y;6Pzŷ»xs}ovw^mqwr|sRXvjD=A7CSWmƭmdYeN_kFYln]~nzxtwk`hghyqSUPgoRc[D$Bcfu|~jvjKD8daN]ir~r`cbZadSKSNTfdpvxvatop}n_jRBDDaJ$&RKW}ʿDzwxvxiCaLWu_k|p`{kPkuStRU=59[|s{jywhnvkV[s{|wNRUjVO9PPLN3DX1H_p̙gizaa]XZYii}{|yzrujmE3Uf]NVi?Xpf˵znispdbwrfutg]AdeY[75EATuyź}uryrHI}aehvqwzuVO]Hjs:=9XmZvsxawttst}uq}tlem]X`I@e]Y:)a_ux~e}pbUhfSpoqz{zpNndks]fbGPN>ljqlwzce[j~y{zzsb\hLK>CV34INS]õy~xvljJ:h}yyz|{i|mgntf^zaV\EPNb}yk~lqqueht}~gkKGaL7:'MY3LaxѪ}hynn_XXhwoz}tuq_qXLZlvZObPTFQ|szxearewww|uivmfL9kmk^32NM^Xp̷rxh\;hiUem||m}iidm`ToODbU:*MVDYyz{lmfxcb~}hynteQVlJ?pwk=&ZZR]\i¼Ů|dxrRR\Qlc]}pifazhKNQO?]fuctphTD`r`o}fj^^@KZ`Q>O4?Dd]eѬlOxyt{}~mudVisZaf[6MXKUef}˿~}zY]gpZvz|x||^T]D=H8D_5#"Ki_zęҷȞdt\hd~nzg~QDFjbWx~jQ6borw{zxsS_oej{ot_`s|uANh^`bHEO0=Jfa̾t`iI@[翩ȵjsSIUtEEYfmRgndsskxhflpz|t{swg{v;a\rh):=4Vss{{vu|rxf_X}aO0U[ZoGsxrywjhx_^il\i}b{ѫnHQQ\Q>K}]i~\uPzxroTWZFSEtjslwkunz]vsXgqaws{xqKEJ{o[JK_o?7Q/;IQxzt_~ch\Tqe`o}zjrfY?6@_xʹpNPGpVDGHW`Zetg=uo]kxqFWV@DRJnscvf\z`o[zdrbya}amqW7Lhȵz^YQq{JN[ojcjtq@R|rb`emkLDWHH^XY}u^wP[VZ~fRdu~qnkwl;@Tdɺoa]}QUattybm}~}{|~wUrN=S[23BJi{Ƿi]d[9F[b^myziD|pigsj_KTQQSlLhs|v}ftydw^p\~}kfcydeyN[wc:LLbsıua`zZYknovnvxxu~uzXt_CZc:0BKivƴtfi}NViogt|yo?i}rk``lbVKQVZTt]mnr]tsWgZiof\i{x`QJjf:EO_nrmxoTcv|}xYu||xty|]Vd19VI)P[zĮxghNSjzx`|ut{|y|w|slc=\8-R^SRCA][}ʼ}t~w|lZjvtZpp_Vdopkxysurcwwmzb{yjyrzaeSXTCUe\MUjcoyuxyk`}rl~yigxzvl}zqiZw}d_gdfNSAN@;Q]RIZioĶ||zb^qz]hppgX]m|iw{}vhlwnvfdiprun^`Q^ILafZRjj~uv~xyq_fv^z}pfr{yeuwgtVeylYg`gRPFFI8H\[OTnhȹ}{eZputjYyspa^o}txv|tnfx{zpq`{mjqnoY^OZLH\h`Vhpxz{{vvt`bsbv}tho}}}zhqzj{wV\riU_[`QIFCI9F[`VXsmȼ~}~i^qyupY}trc^n~uyw}wrg}}qn\uzgfmhiVZOZMK_kdZnr~y~~wvp`eo~\xy}qfn~~{zftzd~zoO[k`Q^X]MIEII=M`cY_uoøe`xowg\wuo`apu{|zmntyc_usckkjdXXUZJQcja[sjyyxwe^mzrvX{zlfs~~yrd||xiyaOdgWX_WXHHFM@@T`\Sii}}pbik}Xzwykahzz|{y}xzg}nw[ltidofg[UU[PJYfeYflq|xso_ej]tz}qdiz~~~|~yzexgxmN[cZR_VUMDFMC=K[]S^ijzgkmcoz|pbctyy||~}k|r}\fpjbogc_TV^SKVdfZ_ic{vvbe~k}gf{|sdbq|y}~~~jrmxvsQS_[R\YQRFFRI>GW]VUf\mj{wwv]|xg`k~v}~vqyh]lldjoae\Sa\MO]f`Xg[~|{tg`l|k{Wz{l`f}u{}}|wfi|wdMZ\VU`RQO@MQA@LYZP][h³|jpmhp~ufdtv}~~q}z_bjgbmh\cTSdULR`e]]f\ջ¹zrnNO^u\agN}vqlTL]vut}`VXVfnmgcwUC=lPOUOnsgky{uslpt}woqhpiwbmMOXqoZ`diykfi}yrtvmq}vtuh]ZY}~l}llmqorphUbvxrgZ_^n}zzvm5(7jpN@'@I=EUgkendqzog}{utmPR~yxy\Z^`w~q|[Yj}|ãy_4?\rgkNBEA\_JGVsq_wXrqn]S^{}r}qXYY_nrdznwnYJ);Wv~ih}ielzzzynqǸˮªuycYLUz~cJFAMp\UULioou^pdaP?pöa\SQg[FIYoZ\aHhf`^UXlx|oujyeVXVlspjd}zvrX`MO=<]vfprzry;ʳdam\]\eŵ]TMOtmZ^Patitbdb[X@Qcq{{khjuqvri~ibVVSLPztxot~lqs~|}yzwkPO%+6Nr{x¯zpUMXGbst{}ej\_x}|}lcnx|[rtw~f^ci~q|yxkhxs`ctZQVOH[U_lMfYAGRamrsjagtktny|}i]fVADEUn~xe^{{]oiVae_lwoäspunx|x{spǵtt\RY\N=FP<n{ˮsY`U>??H[jh\{hQ}o\UWgyuxgmsVT[Xdug~|^UDBcyy{ywywpkt~{m_Wt}uz{fdyVFBSsɧzWVRM^^Vtzzyw~y^]IXpxzuoyw|}vv~qogL@EuktzeehntzsvUE;6[{|wqfe`VNQ`Z_tWabMXH7/Gmky~v~toz^Qe]PVXct~wmgX}gVTa}hhmk{unbZ>2:oǻ_dgOKJLZhj[osKnv^WXh|{sleu^Q[[fyjvrx]SIEfy~x}|wrw}{to_Yw{wywy]VpWIH\}ʫa^\Yle[vzyxwws_^K[ry|so~w|~xx~xigaK@Pk{yhiiovzjgKF6Ae{sqi_W]`UniRhWNUA47^il~z|o{vR]gUTUWftwiz}Snw_VWl}ion{w|u}x\^J74[zбymw`TPLUci_g{Ua|j]W`q|qyftmRXZdxgivseZN@Wm}~zst}tyt}vd]vz~trlsxv]Zr^MGRlʵrlhjyd_vxzzzwpg]IXfmuqyy|uu}tx{ijcNCZuuɴuwkidl}|{~{|laSF0?Ug}tg`bV^w[[lTUVC8JaXivxumSi_SRNSdolkn^}wcWWf{~qtvquxwq_`L9Babwӷh`UR]ggavqXu~l`Zboueyym}iU[]rr\m{}upgaGBQaҾ{wpqxzh^omtzrplkvwt\HI~{orusyukhdM2.4UpojtvPco\ab_Z[^Tj|bor}mO}}}|{}qz|yzlovwk~pE02Vwstyh~pahZEDRKhx|Veh[\XZj{[BM~¾eKZS@LXszgwiVar|zop~mmeH3>D[ŨtvojbgswqwpniWSb}|ldm{~yW( :eùw\ub[bb`_cYapd|sclaNTlmtrphenxwr|bjr[B7DiŮpormuqpnUEUilZQ3"7QiػwYgYOLFJWa_evi`x{hZZl}vdkt{j{{_PM@&+KoͰtok|ehdQRow}x}f\fW@8IpѶdTVbrZUgloqt}d`eXBNf|u|zjmn[jZ?@4$6^qϯwskgnwwumu{kgwywnLDC2-NxüzsrnieaRcw]Zn\NUJB[v}k|wlja_glfcl^HRYJBOrӼst{q~ziomX[u}yehfA74%1_~ήseo]WQKP[_]fn^Znn`\i{wl`gwogvwuqmvhF=E>6Ryřpxyl~t]c]Ukxsx|vnrdJKTKESwؽ]W\pnU^jkmmtiSZ_NMg}j{vj]tsSU[=26-9eѺ~xrjmvyv}smuqv}}~uyjC9D>9XxwsojcS`u^Sg\FNJE_y~kwthf]Z`e`^bVEJVOKZ|һwu~s{frubf}yqyY][8/3,@mб{qsb]UNR[]]ggZZjla`pvwg^f{dgrqpkiq];>K?@_|oqrl^g_dŵsxcXiedeeirxx{xfV]{|{o~kZcwnj{gIlISet~jy]AVjZQGC]kjw~y||}XIgPQY[dt||}lYliSVdshG2dhd\DMXXm|ul^TeysrhbettlpŴ`IJRyqSDHVC9XaRVdp|wzytrn[hkPR\Ym|nPgxb[ZW\cbgoow||]OöeM03M@2EUCHXVergp|vwokc^ckmsytutukjmeTmutsnĻue`UU\kjyskivw}kqc\_MV|«z~jO)7v}YN\ajpywkov`TbRBHYP4RwtifgvsNPfYUi}~uxÆLQlhTfvvw~t`UXfvupg|}ifk`;MkUVdT[hcdgklnzg]upja\acx~]Qmv|ƼsOWrp|eY[`gns~i~fR?6D\tgjR@PUaqgnzlaMHNeɼhSXXTҿWSoeW[cjpqr}{MCcYLXjz{m\fdJQ^dxunoddWVI?=CYuzosm{zyzsythv~v|³ïl[W_rĦgkghiPJQMQe|iWA86G?Zsgyc`epȯkZH?J]{~g`c[_ptl`KEO;)4JE@[owxrlsfCVrjgmprrpsvNZt`JFKXiqt~uas}{ztoL9EWSa}kYSPPPPXhV8UiSDBFMPHTrj[dudP\cnwhn~je]NA5;J^yrkƾƧuQbY5(3QyطfPXXb`PYedeln]HG?722ANYchfhfZegl~|x{{oxlYJSI9Im{}ypilbXLQcvz}osuh`_\KF\\SSGJR:->SMQhruurspVqldehmsyauj[`qw~wvXSD'(I_fbPg]KKRY^]]hhB:]]GCLZeb]w}cXekRSiz~ljc[S[owk}g@O\E7AWp{zu^fjqmX\hggmqePNPI?9BKT]eitxqp_f^GNmyzwvphnz{[E@8+$2Lhy|~smlgX^si\SACI50J_^o{j~{}mWqwa\jw}jpyyu||hYA6?2'=Y^nixj^ZZ\]\fq\Gb|l\]fpqj~puyfvvprhWS_oupqv}}xwz}}qHJ_Q::J_pwjjt}zfl}g]ebQGLU`nzwr_b_ILdfhoo|w{xpv~ypuxZFJF3)3DZv}ovneTZ`MNa_Xk~z{~~c`yuYMPY_^qq{}jYY\bo~|}n`IDG3,?DC`ģxttwwza[v~gXX]a\g}ui{}nSRWD/0=Pk{yibndXhqfs~~`IXfSADR`gt~y`^ohSMPS\jwx}mmyhcWLZ\NZg]dyttxz{{hxwpsvqlz}hV`]C5:DWpuyq_`hXSaYP`r~mbwre`bjt|nh}hUU[^[no}~{our]Y\bm}}r]MSKMb|¬|pnbYGDQJJ[ZVj}|nskikops|pRI\k[KNY`_nvttmfjpzuk^JLQDIXU_~}qlmoqzwad{n_afe\krlu|fYknky}hLP`O;YQBDRezʵ~m\cvzqbct{xwrnnqu{waTSVfnc^bch{bd}|dXYalvs|~u{|p^[np]QLA14N]]TWozxv}olgct{}zs|t|~vfMF_jWIMYj{}taYkzvfZfv|{zgU\^?6HM]Tfrp|pxtt]}h^]lekghdN^qdShgolhz{bhy~zqdvgawyh_G63HOS\W`KK`bLXin}vǨuqhXjagvs{om]FWg]Yux~vyhg}|ywrpļj6>KSGT^h\~pWNvr`iouxwbS8itԳjT`okqsozʸŭzgohN(P[K̴vZZQT^lZNO[VJ;=DTMIVa`lz|bNYeahoqxhW?@xî}ZXrukishwzIJ}qgym\67cOkȖd[ZTZigWSW^RD>COYIW\aYpRKux_`hmrpVH1iٷgUcqgonly̺yygm~s]F.\[Xɪv^aV[fl^YU_[MDGM^OT]_UyzYFbb[cknuZL1M`sW^qdfla~ξx}hfwy^O-MbQùce\\end]W^aRFHJ[UN]]Wf~~bFSiX]hjuaM78˵|\\urc_j[r|öxleq}|x]T3GcMymlc^gnibj|ZD:A<;GSDFvSrs_jy|aE+jghɥydwor}i^_i}_\9Y}zϾǷoaws_]\a^H.2\TgϜ}~x{gaee\uhXFsjqmamwh[:Dq~jSFfmc>>KBhofƭaxjhmqefqqcƮƸg}ts][?VaM{ʲr}sjkn|}UD\H7#?gX:BjG~wy\Fo_gzpN13isoϬhijnbjb|ro˾mu`LlnSDK>(G=YҬ}]sjvjjm_LOVVtfV8Xx{|{vmdn\KiiOewɟ~tgwvvuyvu±ǿrovniouZ>'j\MbU}wWIG2G=R`Qkn8[nvcZeL~iakv|W:K~lMjzڵƾsk}rvyJrnRaIQB.@LweUb|vwx`PS[m`Wal_`;M|{_gxj}xmnvnP:{c^Zd~pd\Tk_^§{nqgYS\cqgD+JlL7[iڼôn`jbdpnn\ibgd-gdlju|sniMaH[KBZdubdxttgmv~v~wxWdֿɚrs}mzmfikmX?-jaYVfaNI8UEo|nIXlxjU_NUOXdssWznohW[BDgRvy{VupiaVYhF@IXgaL9[ufUsxзwtj_xhhiihla{zux[;N)NG8Uk~ϾppWHK\fdZ_kaiRWznrhvmcea]|qdORPN@3B~wmol^Skz}ncXf?56DSO?-Lmk\x°whZhRRMLHOpuzsM~klwbGX5_YB\rnxfY^pyxmt}{os}|vyfe]_xgT;?:6)!?zxumɫagHPuVnvzbzttlW\[@;=P^\J;Zspc~ӳ{~nz`cb_f}crcU[_:V_NMM6P}mɻw{k`ct`\@Ngxxtk}spo}aMiQIGFAYR94e}{xȷsVju|khaxwxvwMwCLV>+8\@&3K`]˛мj|viykhdkX\|pp_iCSM=iU>$LY`Zqikmigctj{xdGCjX>I4)FAf|vrƳӨvrrot[;xqhpuRixSGVeZL.+Gypknvstvyvrc[zpYcd|÷ígeWB2QVN4/.eqdih~¿thQkYMWWLlg_oeonKiY\kUQINE`w~}ȸ{jikUUv^cf}uRjs\=S^<2(VTqnsĹ{ela[\vJikKiy~||udPww`zqbcqG*[e^^yzwu°spkRSqpssi{yZ]_OE51WL+,VRatvUbÿyfzxcfWslsmN\\pqhrORY?+MnUdrǾҾptz|vZhwUll~p{fgS]t{R7AIMPE LT]bué_X~~ig|pr~zyikyaKR?@JZF=auiqkow`\{wjnmn^YczcJNPJ:S&A>sS[lqzmertz{Xt}G42:0>}hxtur_~yllfZq|yd{qmM5A_LxӶeYv`ews|vrnuxoo]`jzk{]gqmJGvŸoYe^[BHgrlfbgTr~VpQfcP__Y@Mrl][QFFEnshXs|\SfuFh]rz}tqtr^YYYacgcMrjTdisj[z||lyqXRfyÎèŸhf`IW=gis}pjICI`[jauaL[W@Jn}_=[Qt|znENYhdNmfq~{m\ex{uYro}`~t{eUhg[wwx~ɽms]_jrp\vi~l}{fpxaispysͥ~~waMYM;L|pblhdpNNNBM?jmt[M][[9It}m___FDWJ]KqyszbUyiqh|~snrjynzzzJN^r}xv|jVe`bPe|{}nmdĨj]IX;RYLLIw۵ol_Pfzb]^fOXCo~Щrkpw{\YIjr[VL#3MH9CVM=JU[XsQ~l}k~pceSIIYF<@PS|{uwwwn]Ogxlk|~tvu{js`h_Xck]6phqzĠ||v}jlaaZNEAVnq`I_cnnfupZc\it]U~sfgq_Z^ni=6ctl¬ͦupv~~[}o^[ftmhwzdjv[dnyaJb{meKpeRIXfJ26r~uuਢzboeb`NLE`sjS_o}}sUxc|_h`acsiLOal`lpwm\qdJCce]}į£uyl}yciRZelpvxVf{|z˚nXoqPNSd`\blun:qrABKaqP9']~̦ۑ̐yxWsm{|sTJD^sytldmupv~wW]s~vNFRqhnwVCTD37nskƢy^e|noavYdilo}zUiyv~Z[fTSY\^kihQ4{aPaftv_PJv}sݸ័okvokr|wxrrgS<>EX[[kejvqcz~|ouypvyzfIay_hOglUT\F;Ct]uțwsj}uXWx{zRui{ovhlë{_qkGKLNFKgu[{G[g;KZbk_L@8(/_un\p}o`hx[iimvrNd~jzjQm]TSTL[z[f>ByW\dkfdWRVpp}y}ձronjlqp}ukQIRbd]]j[tty`xwryzzjrwzjYHkacKkk`SREE[]WͲü{ilZg|mUu_vdm_tpd\QU\NNZw\_vBhM2/%5csn~`t}iSp`calx}eE^ygeOnbQPSE]|[bA8x^VVgWXSQVfkwxȔywjnlptxubV^ordhg`\}aprfkrhlsn[Ka_bRN~kYLLI\zeVj˾Ť{~mogcw]lXwbhcb{ipr]Y`bP_jg@r^I`;=OoceYK?CQ{ҰӨ~vqavZR]`Zrk^~~xu|so_IozWr]G=,0';c}ufvcJmeZZiuv_DTymxcMgeMGN>WxZ\F+rdRI]OMPPYep}ƬС~lnom}p|lbeq~jsneSlnw\\}sehl{y\QIzhZ[AmofHNO^xxbeϯ~|~tq{gfkxv`sY}{_k]f{yesi`Zh_WjhGezX|[\u^Up˪{}znkyt{bjimf;6QUZU?PsVOulv^hvdnrN=e}h``uxza^aglyzde{mjutXT=1xpxb}faKy¼R6Dzrj`sxuerrm[dSDG`rueN@F(c~rzmfjvtqyy|Zw{~TQc^QF;^³~rhw|fyqzͭʟxdaqrgO8=;=|}{yyvxlnZ2uqd[4HTVNH;nkQTVVIJry^lTSwgaxǧulyrwfa]a`9(DSWULXva^wxkgppzpO;S|qok|wqTucry|nbukbcx~lXNM3r}eglp\s{~W7+V{z}ͩ{xvwujcfMCJdnhXDEB?}qiroz{up}uNok>IY\RSVyzupexwwdgT^wob~uh\o~jQc|Ig^kr}}\_mob[^kwPmq}kuid[[cyQc_gusWTdu]H]dkbͧ~Njssnt{lauztjbhk~rkwwmenm_c1A}pR2jglznsmXtnTm|rÆ[PVL\M7+-Jjb;IovWTau]vPysYexdV{e{xkx~XKEb|u~{yn~qp|}xY|vrdzsd@8Yl^a_NSjnd?NJY|`wX]goyVT}veLb[_^gZrR6ohuX^ybTqruzt|dNSQoe{ޮe2dd[@dsJHPg]SjSERpmunrrruqgmikyh{ielp\TRdVP}tsvyMO~wxrnǤmmP7Hq{P>L_mT:IQLW\jNveKI__gSgdUjzh\nQmufqN?aɤ{l~|u~j}ptfexxlrsoYG{e753S{lYptgcTZP\Zyw|o~nm{m|quyu\bzp̬tqiLB\Uhlft_NOhRN~zS\SMg\ETwtznVzvilwVkilzvzs`vdbRMeq~o~jYomx}azívh]bK2AXoʯ|VsbGCAlmgvuRMrpc~xol~r|r[_hg{nubVYO^NpxldNlymڧk\{|p{{z|pudbC8UNIUzsJBW7W}osxiZhiztvzg]qjjcS`Kirlwl_mmmgMieUH`\\²|[y4I~srXZZCdwphʮlKftcskcWwwivlvs^crenUXa`z}bWfbp`kzwswhHgܛVXn\bh_Wlzqqyur]IMR8AmYzŞWqSNfp[Z{W2;BJtz[kIdtt`}hn\sM2b[hwuÛv}}nj[yzoPXOy˩~anhB -8>`wwֲt}s~TDqqZs]1L4>{iWi~`r}nr]~[vr{sN)Tnzp~ikwokVl}xl[S`K`ity`|xgdgQr®{jlvhF_A5K6pkfĨɜ{~cys]]hg;J|f46?[pbrc]stgtdpdb9E]^jcƞwstw}~t`{tcveyžxY|ik]tG62RaGnªv_rpRcbji__@K3r^fxqqv]~ipytdILQIE`ȏwrwlqg|f\kw±Uqdo\?]i;=M]tn̷sv}]_X^bL~hECU\_urtumW`zibo{\8aXbgzwx~ipiuaXtcXUbRPH?J8^yгcz[OYeNDmYQOllzmvi|lpwyfYe]narI>Sg|u~ngqlx]|kn\vdj[]jdG$BWP`vǩtkQUHjag\VJAywz`|uuv|]kZ^cfXOagbxz{{~zv}iv~gVT[]5\\`^Ypzqnwp[}wg[zbHPDXKJXFKAB\kgw~`vhhkVW:[rinnv_Xmojkn[sVhjdnbvxƯyl[X]d~|pzyw}wLb}pmaWLckM-(@bH1+x~׵ʠƪy{ghS\mt{}}~oTffgL*DP[gCB\e_s~~eb]xe^~tz{N^mPZNfsJP0SNIgcp˙oqRLPZwfeniltpi7W~hIIS_L(D}qj~x{xyped]dtyu{~r|OZ`mV;$!6_gUOwtpŸշo`tapvbJZzxhj]w|ZMZu_YAChLe]hmzϽxkZcj{zakowiv`sE2_wb=1.OG,*SbjirǷҸkSqwwz}iNqcdT9AQWh`H^t{||jLswZ_ibIQygttkzUVOkdOG5lKkzx}rż~ɩpxy|me{bfiw~|vDtgNQADC=DJK2Dgfo~̷̱wjhurZHplhd~gxnj`|r^b=ubCXRbYcoz}tmkwxtblZn}\4AwrM139N3$@c]mlӴͯ|}{oY^_m{wv~ovwbWDLY]fWH\doo|Ơupa_lrdEWsuZ{h[oUVZeSb?Ju7);S]jvĻÕymw}}bqy}ds~bx}ZXmݪet{Q6ZnO7Cv[=Ua{|w^e2&hWkQGV?emwvѼ·ya[h}\dqzoOLe?mj_zlqa=b^_Qaxpogz{xjpUIbrfPZq̤`CIa6:W?]>EJ^nit^dlnhMf]TZ3Q^hVX}}{akwedrUE]WB99ebqxlgoyUqr\c\oo{Ĭӡz~}]Qeecimko|ud[bYG=gZfo|i]|vXgsx{n`]gN-MiaT=;omi{\Pz{pujf[s\hnvgnƩ~cextxvhmq{i^pO`P^_O[WZKS{ccwuYUzadnxwV?K@4>Hu_rr~u}s~fmas׻fa}ofpi_Vu{`a_}usrpJJ9\L]ree{j|m{ubow{Z)FtiKHK{}ht~petur\HeUc{hs~zXKhlnr~}xgr]g\kiBSafTW{wXbqpbh|hXSbfNEU\RNSvtvs}qpu~brSw}Ǥ˚ob|r\`jkb{u\dqvaNJLzikmvnlvfWuqgosv|yp@'KfZGPc{fjuyw^\xnpvkbsOzrtʭ®}wv^n~vvk_|rqvvsn{hUm^eEEfveFNybgkiTsyw]aelZKTWVWdv{pyyoctvtous[enORgwgb}hZUYuZ[KtrgrvupzjSrzjotttwU,LyrD>Moizjnvse]nyjsz^sTg}zwwǩ{`czvumzbt}rwƘsjsgUxu|Z?ZifTJjn[dhiUkylb^ZeZNLHXapz|zxzpy|kx}_js^_`NSef_~_|bOQimiK]ziupe[lapriYmun;=iyTCSloize~mfvqv}cml[~hmyy~aSryoiWaywxIJ}XnyqGXrwiWS|aXftsedfWPXaV]WU^_mxm~tn}rsnqmzyZ`Sksfrv{y\W_[nz`[qxsW^Z^`N}emyqsu~vIKYbM=ZW]{|`xv~zo}}ytd~eesdpv»vX_kixlbnzmz{vjwzwgQPilS^suno[Kwzhr~vVziUekfIA\_|ymszyux}xxxpu~r~}nqmd\Yzzfupog\[gxf`tskkr\[\Rydk}sq{dwvtQESjXPkedeqqzq~wwvc~kt|bo{zɥvVUzpoo{}ksvl{|znwzptTRknO^{mfmZEn~nxtW~nQ`jcSF\_xrr|z|}wsmyuqtŷìytwochigSkzjsrfwufcms_wpkm|gO~kI^_gtyYn|kzcGGiqWjqhnnozpzequun\xxkÿz~aLivm}ysqqvhz{ywgeRctUM|tYdaDb}zu~Vu|VXja\P`_o{p|~tkpsf{vĸ{grxs]]jtVctlqqbm~tntz`|qhmpMqqJSRfy~PbfuiNDhzansp~suoyq|dny{pYvs`suybMknl{vuzqxvj~|alYmtQQqP`[Hj}w~rY~zVZh`Z\lYqwtsjsk~`ws|nphti[VvrReiltj`uyokqoiyeS]KNJsoIuqhtaNI{mfqoxywzot[x_kpudd˴xutz}oV\q\srs}{px~kxwlqfey_IrUO`K\yvt[tfVea_Ww][wz}v{ksjnv_floijmn`QgR^agrl_n}u~xnuksnU|eNLAe|NjueqeYEum_njyw{}jv[zaihqd^ȸxstxwpZ_pOiip|vnzkzqoupo`OyRMXMavos`}i\h`^W}XUlryy}zkvlg~]b_o}uirj`LmuM\Xgqg_r~xorvl}g^[SIClw}zV{omqeVMy[chk}{q}zn{yc~x{^{yfl\gõs{vtmUigJdcqyok{{m{hmtv}ZW~PPQOluipeofmb\YWVdnuyzyi|o}jypszjg|v>j|svfSgnVikznytlvepqb`fYc}jrn{_idhYTgjhhwqjkoxisb}kmwsu~waxvalbkH_nVitTxWbX{}~lyuejqi[c~mtokxRMQLQMtWomzxq||ku`|zaryiLhapaXkYbgjna~~x{`~gqbrb}tV~|eoeo[bkbUcvmglwn~|}|mxztwuvs|uz}~ylwhmksH\_KdkK|XeWtswygqtf]iivz{vsr]\SIHQvZj}f|u~x|g~evkQkxsuQtilWQo`^bcl]rttlj}kieuk}Npy}iq``ngYXmfjjwxnuu{{w{c|quwyx}lw~jrkuRXmLL_Ed]^Znlǯ~rnpg`ykoxjmxlWtMKIkd\o]zt}{jtmitSmglmbrpsdK\hYj`l\appgjoifi}pjW~ypYkfdUmkeaxq~~l~x|ozbrt{n}iv{omnu^Qo\JWDIdVTestüzs{tjyp`oxiruuY\UQmjVswVs|srqcoWhe[Z}os|mPZhUaYk][hhfdyuumr~qoXvy~enehWnlifrvfyktr~lgoy{py_mr{sxtrs]\q[HRASyWOLllqy|tqdlqofpvqgbWa}kYv{jVys{rnbhS{`XX}vmoVfeVZWcM^`i_kzry~yp{zej}iwyuurjbvhlh{gbnxspinjrztxs|~x~n_lisj|~s^rnSJJ@piMDRwoaygjppp^rqirfvgb}ej{qPh|k~ztx~}ghZnk\St}ijx{jjv^_WaKLU]b`vmtsuvy~uhljv}y}~nrrkzl]hmhdkefhzszt{zxcleocyy}~yn~x_MMEnkOCOoeWjv\wzzubtyhZba|pqoquRfufwu}|nvx{kiawp`Xx|e`muvtmjafOJNTXXqiqomjuzìsnmt}zFRzhshRUv_^yeE/JTgpt>XH>Zf]hrijKXzŨɭtwU#aixq[{cPljUqgN8ScczǸJ3xaE?D=P5Ellm{j[vtiopuhpcYXj{$V_xy{bpmQRsvp~3tVShJQTQqb]vr}}kSyoGy}qh}qlunqŸsWaqԱv|JPEMB//faQL]Vv}sZ{dmnvPOaeR82a6bznvo|y|rZ`om\tYzozhIKjDv^`v`>LJNK0Q}ayhĬкvcnhªjw~}rVH`fjlV]OV?WLGT>~OlczR}b]S]\;LNTB2.H;S}mvq^uvpjy|k]Xvbtv|Y]^\nEE}^zkzsslddfgpojowb`hyƻaj_cg2KiGhEqvZlmnsggdrSN][Z@>I_Kxwp|j{sKp}avPxmyvpjrrgNJ`sflaj}eIZShc:owkgÞƠ}njox]hgoVQRx}b[]WjA5hGXGP}IrjoRoRYS_P2EFP559dMz{s{fqncp|eswu[[fwm}YfcpsEj}d~_pivweby|bwz`wv~_cjwc^bͮ̿vhmfyVDwYcSZUu~^~^genaJJ14FmQywzey|c~|h`jvcVV~~qglhv`Vrvfvqcmwc`dwlx~_k|ww~zgUbngTXxŭƥy|rxjNzqe_VbstazkiihkFHS_F==i]lqo|vuxvbdmrWv]lv{yoO[ecHEUdpy`gcifMvqtg}uxjhsrsx{wznKT_`EDVivxnniKkt^[GeXlqYb~e\^ZaC9ER@0/VVUizvrjku^dprUOZiuxrrnVvrq`tvgtq\Wx~q}rltiixstuvTWclSKRpå~dstoU[zoe~mokpaGQ\W<7NfJn|yw|wzs`tg_nm}hy~twu{\Q]fQ??i`i{urolm]bzwcpw|neps²srwvuupuaJU`U?=^oa{vi[zkZ[]dmoVj~k`a^cF?KU@01ZLV}e|tylkr^alpUKVbzwrby|sbj|enyrYToetqltl~ycpymolqZLXcTDIuryβvu|dyd}{xcxopmpQOY^F:AiMjm{z~{yuhuu^\xqcvprqmNQ\\D;NrTzzurk`{vjqs{ha}uyr~ir~mlilXEOWL;@kmkèzokv^j_gpiTizmab_cJ?IQ?18bO_kwnl|ky}y{]XchSIYh|tjz{ij}sfgps\Tfh~txf\ob[yyjdfecGLX[IHhrտx{ofxjirmmmeKQZVA=]nVy}||wojmht_{o]d~yiihi^DLUP?Cluh~zztmnt|zzlu}scgǿtmrwkXlzo`c^bK@KUIBSr˾xp{xc[rlaSmvj^a]`EBKP?8MrU{}xri{nixqspoSU`bRQst}tszwnqv~}~~v^eniXXu~vxiqg^p`N[oj[^\aSBNYTM]϶tm{{n_vxjoimTLUZJBW{]}{~{{qotfZ{j\Wqug`c``EGQSEFki~zruzuvjqysdiżrhjo`PbogW\W\I>JSKF[s̼}x|l^vk^Vovi_d_bHFPRC>\uY|{|jrbh~~nnnngOV_[PZƽ~yusyth|v~x~i]fj]TilvurhxTov^NLchZU[Y[CFRULQ{|ԾssdomqnpcOV\SFPxcsz~|tr{j^mj[Rgqg[c]aJDNRFB`}bw{~rryiounagºrdijWK]i`S\U\F=GMCA_eɹ}|dk[^svfghfcILSN@Dkb`w~xpszh]n|ugpjo[OX\RLdgômxjlxy}yy_`gcTUztj~vjyaZvfSFTc`P[V\N?HPICWhǹȵv~ni{xqyqv\U[[LHfpXw~qzr^|xaQWkm\a`a[CGMH>Hp`k|w{ul~|viorf^sl|y|^ry`OM`fVU[UY@?HH=Ahkb|mk\ewwenkleNRWOCLpYhs{orwe[kxqdqho\OVZPJbaoxhfyvu~v|c]ccVTsv`wlvhTmkRBGY]LUVVU>BKIBNwjtȻȻ~thtp}tykX]aWM]zZ{x{}|tbj}hUN^j`XdZbLBJMDA]qVs|mm|}mhonbanu~pZqoWGJ[^MUVSU=@HHAJril}pj]fvvfslohQTYSJXw\pv|omv`Vcqk`odm]MTYSPgeryzgar~to}q|g[acZWp_uymm~mR]kTA=NXLLWNZE@JOKPsk|~lk{sw|s{a\baWZyvdzs}r\qt\KM]bR[]X^EFNOJTxokrtfm}s~}ghmjdny}}n~bVndK=FUSFWOUQ>ENMNdkþxv|e\hunfuiscTZ]WVn_~hpkWRak__k`lWPY]Y_pnp`aqxjsvox_\ba[bvkzu{q|xdru]Ka`F7=LPCSPSXCISUWjzɽò~rfp~yps|q^cfa`uf{uvx]bv`JDS]RT_TaNFOSQYy}hqtcfv|o||xgfljgqzz}|{k|y`QhaG:AONDWMVTAIQRUmmyy|e]juljxjwfY_a\_zcz|dr~eQP_fYegalUT[^^k|||oh[drpgznuq[_b__saxz|tsvhiwhNQeQ:5CNDJWL]LEOTVbs{jm{~q{z~eeifdrm{||t}d\tgNCNZTPbTaVGNRRWsatb^hsivyr{bYWW^uyfeh}~uqxmkzobDM[nl]XPOhaqpajopuoyilw|kvzjl~z]j]iR\}|x}ixxvy{yrzimnkmgzb[aoynw~mn}|pl`Ykoriyu{qk|pUK@@d_@KAKSDMVY^tdvqt|{zsqxm\Q_n~|uzwk~u}z_hxlkyŢ|WDR\l[h`NFabNGSefg}p{v_]XT[xijzuvudsomxtwzaARYrŬu\\n[HFT_YipdtaTRNO^~td|gzxqskbufq|uzenhQ8CVPUk~rmpoqtu~|ɱ_SMM\̞zt_bfWkghx~fdsfQD]_SWiyvuumpqqwbly|mf^J\frtp}ieq~{utqsdc_H/1QV[ptc`][bvdJ~oXmwrjh`Umj{u}mmz{yl\[[f¢}tv{hu}~p{vfWRFJkpùoZfnXHLY]ZqgdoWPPR]uiqmgwztpqdgsbtvth\fXF8Tifk{vtuv|l|xquiQOTb~{sane^qjp~wagm]KKih_boxq|t}pssu}|`cqum`]VRswqwxght|x|rsuzfRXM=1Kjmtrjgdfs}T``^nqjdcWdxrrujozxytxv^]amtxomlaij`ROJLqyzeguaOMX^Znj_nZQRUaxmkrcsyuopcfs`pvr{~dYbWH=Xomp}|zzzkzwȽx|y|hpeQSZkžwgvhevouuakk\KQmh`eqxrw|tuuymer`lne[ZPZwup~rfkwy|wstyxr}{pUTUF78\jkxxuoqɻrbSMc\jr~~qbpxnlmy~l[fu}zz{maz{{xsh[kVy[domc^XNgipti]piEB_jbyndeht~mcmmrirgWFXi``m~~iVb]aovqokbz~xixwywztst~˶sTSRmSM[VL@Sf^^l~~umfoumktwoidWimldOZ\e}|hxoaa`iz|rz}ve]k`zyscpxwmWGNqʼnqmfZPK>NVQq}}jZkmdl|{sqnsus`O_[q~~o^_tmdl~zv}|ĺwZ=ERSd{bVNMUfcDn\]gjbZPAOMLoz|hnpcTMbaUYixzyyd[SXk}wki|n}z|}ʢo^FJTQ~Ƿt_JUYIEQafqow{g_YXcwx^nluvojcXkgdmWIZZXhsnjn{viyyleuhusu~tzq[OIRjy̭uiWYbPeuy}zvhT^g\]l~ztrs~j~{naO[]]obtuggt|~|ybH=PRUklaZ[foTLsV_fe\UHDTFVszvjtmbRVg_V\kuywrbZV`vy¡xkq~nw~udUBMXXŶzgT`cSNWcgsmv{h`[\i{w^ojqtnibZmjd|{hTFZa`mztqu{gx{~vkcvnuxsvvt|nXOKWqǻ~rd^n]by{u~wiXUhgckyzxvv~wttx|vi_QSid~{il~vmr~~~|rr_N;Fahs}ogdkvsOaeS[`\SL@LYPk}nmujaS`nd^eorztqyf\Zaw{ul}us|{ook]QHDakxxgZjhZW_ghy}gvrc^]fzufggoqkg``uin~puj[LMiru{wwrsrx}undlutz|rrxxxqcm[PPZtͦyoblr`r~tuzlaR`ronu~~}{zivjnne[TOjtxuk}us{{z~~vfi\NAFfszxqms|{WZmRW\[RKBM_Tj~pnwmeXapg`clnupkvf^]g~}wpvtrndebWMFIhn~mdq|vzoeehtb_{\_ghc]LM^Ztsszyjjsh_S^vy{u|rhs}{vrmyceltrgb_ns[fgYUWe|z}pTmX]fb`mh`SSknmtxouuux_dki`ZQ_mc~nem}y{sepkqtgnj_UPhtv˺Ŭq_nk:5=A?=8DXM`{|yuiTTbcirvrvsqvolqqq^ffn~zpfx}okopje^g}v»`NTYkfV`RHEGTb^CfgW]b`XODSSMn~qwwlbVfojn{xmpĹyrim~pzws{yԵ{fPTTNwzp\K8@G>>IV]oyclsf^XZepbVg^dhd_XXkabwcWehl}yuzo{lryickyzpÜyk[MBNF9R`hswoflj^QBMQKQ`nsn{uqovni~{uk][gYgso}ztyw~wqkyqoUSZW`xzk`]cgUH`Zd`edbyurpqsszo~|{teizghagglqkw{zy~d}{zmXQ\JSzo\eboroaVVY[pwbjn_pd~}ounqchz}gy}b`wvsgmqvyyzlu{oh}lk~tu|YOfgPVjvpqVnqlrpget_[]hng~}tu`vn}vbn|sdtwo~owumh~{ppvg{xuj~~dwy}{poz~}up}bK[s{c`hjw{kcuqqc]Zj|c^goy_i{ssbdv}erps{r|rxkppmkunf~}yg|ov|yezvy{UR`zskb~qwzgls{s_UXgpZ[mrfZv~{m`e}o_y{wzsvrn|r{nkuk[k~{lh{rsx~|}~{uqUXvwjtk||xsdfvzfYJ]ndUxooX`vl|fditwv~xqu|ttilmn]ax~iuxwp~y}tpdYepimlw~yylge}kTUdnQ]jmalnvwhlxfr~zi{l~wxqpsrkY`s{lxuq{vtou~tj|`^nlerc}|pbgykMhniPsjq_jewtgt~jvqy{{~n}v~urjqs|m]i~tz|{z|gryo|~lkl_`jefie~}`i}lYpmjMr}eqbjhw~t\u{mtum}~pu|x{~zxds~jdfxq}xy{gsvvxifvj`blhmkl~_kzk[ploVo^gdadzzn`rytql}{wo|}~}sZkommwz{vyyitx{{oeoha]hglsw|hk~{s[knl^j}[dk\g||rdmtsqs{j|z}y||Zd|{ppouwxwwwl{{w~xscljdadet|zvgz{w]kkilnykYm^Zy{xpl}vk|r|usv}nu~eXl|utzwz}zxm}lyvxn~vjjcfha`m{~gxygklfhwssddmUn~upy}zjuxo|ywuxnovu\dwyszw|{{}omvw}rxf{usji^dne_lyjwu~otpffz{qk_pXe}|wr}|}cm{muzynkqyaaw|xx~{wm{{nuuuw|xeupplgX`qlaf|zoxqswsfbzjpap`c|ysty|cm~quv|x|rkoxhgt{}uytro{pzrtxxvepmlifT_rpam|{uwm~t}tidblbmeitvrwz{`o{swrrz~olq|qkhr{y}|qx|nyurywwr|fmklkaRatqat{~sszusgku`ndmlpsuq~}{vbxx|}oxgxpnxwqlep|y{iutxz|qq|wofhjko]Rftliztntu~pjzhbnantouuuy~ki~{y|~iyh|qqszrohgozwxo|zuu{n}{}ujbfkpgT\lqftr~}puyvmr_licwvqur{vyhzz|rvuatwqszwrodfxypwuzs~o~s}{{vqe^gnm[Wbokmnnrvw}qnl`q_ipsqzwmry{wxlzcxuswzpsneh~tzsywtv|q~u}}umsaainfXZgoirhywtyxlufes\wkqqw|nz{p~qlixutyusulcoyqwxts~|xr~}tm}ju_`hn`X]jooqdzq}xp~ehs^~mos{wr}{|p~{woivwvxqsuncs{~vyvssx}|q~qgxgv``hl]X]inwrfzmxr~hgubolt|vw}}o{yz}qhtyxwqrtoev{~{|{}vpsuxn~~m}dz`td]gj]X[hp|sh}~m{{vmcsi{ujqw{}z~qwxxuhm{{ynpushs~}||yworvtpunwhz~]mk^di^XZdozk~mu|wtclrx}lm~{v}|}~rvrvqpew}|potwmq}y~{w{nn{oz|l|soqkg_ne`ga[Z`k{pwmx{zzlcqz|wh~yt{{ysun~hvhj{~smtytp{vu|~slyrpq~||n~~yjxq^em_cb]]_fvxmswz}tfj{~i{ypw~y~ttmthqocvwnryxs{s}t||vmwrn}yz}~pz}xkspd]leca]_aft|qruz~xlfwnyynsz~sslpmkteo~xory{w|q|t||{|ynwsmx||z|~ryz~vpqkh[khc_]`bev}srt{~yogvp|wjt{|pthrmiugnworz|}{owyy}x~yq{~qmwxyys{x~qsngjYjmd]\`dgyuqs}}yoj~wtsix}xpsezjjvip|tqt{|usry|twtyomwpvx|x{oyehh[mlaZ]aei}upu{ymww|{z{lj|qtng}hlvitzsrv{l|t}|xv}w{slo|m{w|}y}rtu^kb^pl]Z^ccq~{sp{~{vnv{|~shuvptewtfsskwvssx{uqr|}qz{yxnmrzqzw|}}}{xo~ebm\gsdZ[_bhzx~ypv}|t}~x{xjmznuim{mhxnpvtrux~k{{|ut}~||qmn|{myz}}y}ozw]hh]nq^Y\abqw|vq|~{xt}rhttosew|xjkynptssvzuqw~~ov}~womouzs}w~|}z{wom\jf_rl[Y]aftpts}~qyliznslh|xximzpnrsuwpz||zov|tmlqtwwyxz|v}rrh\ih_ql[Y^aisotp|tjjinitn`}|ujl||krgm~y~kcmt{voqvxy|yqu{}tquyextlgp}~xz{t|mr|fZfldutechjtvsxw}{|ujzqt|znVdrufaknbsos{{ypsy~rmlq~jisxyzwwr|osk^bpgoylfkp|~{~qlb_n~~vqrvovd}{sijxosehrehdXnv|z~~}vr|uf^ixnrqg_]Ye]]n`SM^`ZgcYZ_ipg|~~uisbZ^X^~rvrds}}wqv{m{vuviikuqfcbovrqg~{vnyzq~qhl~xx{uyyr||qox{vpnp|zdsrgzrhcmywpj\ld\eidxyz|nd_eifpaz}{maV_bXjh\ROaf[ef_`hsiw|x|}r^i_SYccpmq{~zxrypktwjggn~whbakpl{gr~visyqwpiwxz}zunt{x{yuqqs~imoqsugcmw~xuj\ld[allpw}|~kb_gbbsaqueUa`\oh_XQ^m_^jfckrg{xu}~|l`j[SXeinsw||{ztunp}zrgfgptgcfqgnxbuqhwqy}xqlzww}~|pnu~xttv~upimztochsz}|xwahl^\conqx|z|vichgYjh]zt^^d[nnd_XVgm[`liiqfr}ytxz|zveobWU^hfo~~}}|}yqxuptgefm~ylfireiudgtjunz~zuot}p}{}rpt}ut}wx{vvgiuyrdisy{|x|zdnna^bnpht|t~{y}{ojpf[iaYj|fiebvphc]Yfo][klmmbu|xxuypw|wnuf[X^he`rxx~|rquyz{ugefn}|pjntdipees~u{xnukz~{wsrpt{ztrty~uh||z|lc}}szwiirwxxt|~iqqe`aktkeuutzxzv{sq}xlid\qumigcbpo\cpolW^ifrknt{sl{xuz]ig`_blukev{zvjeuzi{ogb_dypltleqjlkrujtx|vw{fpYSV[bh__~iitwyqppmilz}ujgjuujx~x}lbhkcNENFMQVljamvxkh~}vtrnkouhcszoZegd|{ssuy}wuwpmrv`SOT^[MXkju}{ywwz|wk`tgo{hYSXfyriqhpvrtw~|`hlitjdaa^]ip||yuv|pfrzhjlihjlnrxqotvp{zfZ[i}t^`TS`Z]lg_ovvn_rzutstqklsfarv_`mfs}yyy{|tutonptx{ka_dkeX`uvyv{vstx||vha}}foqeZXctkqlgz~v~w{|quiapjvunidchtku{w|ux}}lmuwbdnnmmnowrszxu|{vul_[dvfe\Rai`amhfvzzxclxuuuwwqhlrffvjcpio~|{zzpfqsonprujfhmne]o{o{~vpqw|}xldv{pkii`\ds}oumgy|u{}~wowjgphr}voienmosyxz}}qrs|{d_otqmlm{wx|~u}~tkjb^fumn]Xgof]hmjtzvftwtvx{{tijtnhphoqfq}zxkdrvpkip{ropqqhaqvj}tnry}|tiovsvidhcbkuz~|wwhpyq|~y{vomirgcs|skk~hzyq{~{|uvut{u`dvxpjfr~}{xifgbcksy||m`jsqa]koox{|kuxtuy|~ymgqxnjhttdiv|xveoztiaey|}{xwrgkxhvoqx}|vmutq~obijgkprt}|ls|lszyqgowd^iv}svmwzov{w{ytxwgfw|shcpvv~x|{kfkhfijltumtzwf[hspszz{zo{ytuz~{pho|vgf{vdgoyz|kmzxk^`om|}{rl|zl{}|xppw||vqzulycenmt}liquo]Wksnryzz}}xfkyik|~}xnn|limtliy|~}{\^yUg}}|tp|xu}~}}{zrpqmt{y}nsy}vd`t~z|xzvY`}ojlqsqkdj}wommuply}~~y}|onuqjmu|yy{vr|zocbdoynrd[cjpl\Wlxrpsyy~|v}y[jxkmzvnv|{v~~l_cfemwqnjghljdfhqsjr~wqsx}}yupzc[hu}~rx~uk{|uQXmgm|wj`ftlkk_gnhlzxs}xynjplbisqp~z|}{~qyvaOYm|yggkmmeX\qwmim}u}xz{spx|a\ssrz}tv}zvhfh]Xdos}vokmrqg_p~lgt~zsprvxvy|kqwq_[owv{wr|qnscQamrykfsrkuo`gsrs{qx}tx~}sfbkmcgtw~|}vpmnj]Qa|qjlmmlf^cvzmgsvzyvz}vonsm`hot~yv~{}xf[`f][jw~uonsys^dxfmx|uqpqrrulmopj]d|}y}z{|rkjc[aeqrmxulyxgbqzxx{}pvyou{{re]epjblw|u~pmhgcX^ywrrqomkfdr|qgs{r}zvx}~zupnmhikgr{}{tdY_hbZdvysrw}t]iwhlv||xtqponnyvmojllbg|}~~}zrkd_gb^qw|ptwgdv{wx{uryomvzwl`]isi`hz~|twrmegd\e||zxspoojiuymmvou|wvy|}{xuqlektdcu}z~yk^]hk]Wf|y||fg}{nkqwywtrpmkkwworkjokeq~~yqdapfUdwwz}ofsvtx~ttxkluyvmc_htn^`tvzw}zyrhikbbq}zutuqkqxqqqiw{wvxz{zywtkbrzb]nz}~||zws}rbf{ykzsnqms|ztomlljgcdsuu|y|xlmiqzzw{|~ymj}pSfz{y|sRPxv[NLVgoc[kznl{~}zuz|wxu~whgha\ciieYbvyurqrstuvxxrbg{m^s{uv{~m^pv`TT^bUL_mel}vmmis{vsqnifp~x|qtqn|zqhnwzrfu~}}|xkX`th^or_^WTh}ndbhl`Vfnekvvv{wwzw|y~}xmx}sdblqdUartqonnnnprsrgV`wtjt}{~yjoliyxnhjmeWakgq~qrtkv~|ywurlir~z~|zj_ksxtnxzgq~|zyyywjVYptmpvusgZ`canvniinocclho{zxzux{}}w|z}}sgssdevw_]ovutssqonprrlZWn}}z~||||tgmwu|zpiefjgbfgorp|sywwwvrmn}y}|{~zm^fxrk|tjz|xutttn\Tgx}}{ymjeZ`oouwplhglokidl}|{~~tt~~xy}|x}tw~~thq}oco~oaluvvvvspmmopm^Xk{}~wrmem}}yqlgcflmial~qr{~zuvxxtprw~{srxxl`j}wlwyoy|wrqrqk[Ylw~{mf_Yhuv{xspkhjrvl`m}zow}x|y{w{poxzphxshr}pkruvwxwtollnoh[`sy~}~tldcu~}ytoiehqvh^txo~|zuvxwtr{yx|{~novsedu}vq}vx|}~~ysppqnbYgrtwj`Xartw}yvrlin{ycbz~~{oq}}z|x~~yyvkpwqgr~ss{vswvvwyyvrnmnoi\aqsy}|~ym`_pwv{wqihs}m\nxo}xxyxuuww|{~qmtrdeqsz|w{{z~}{|~ytqqrn`\jnm{~~o_Valkq}}xqlq}dd{~yls|}}~|zv}zzykmtmfpwx|zxv|~xuwyzwsonoog\dplq~{~o^]looy~wokuu`lyq~|xyyvwvv}{}xmopffonr{vz{z}~{vrrrnb]knhq}|zfW]jilxxqp}sdr|on~|||z~vz{z{tjllflut{{rzxvxzzvroooj_`ooju~}{y}zfYcnmr~woo|pfssv}xxww}~sy~|||wkigdmrowzo|{y|~}zurqpi]bpnis|xxzeWakkr}yrq~ult~ylq{yyyuz}{yyz}wjdderyxnx|wxzzwsponh]bsrmt|}vrz{iZcopwyqoywos{tvyvuv}tv~}{zzyz}{od_aozx|oqzy{{yvrpnh]`qunq~z}{pryp_anrw{tqw{uvx~~|pnwuv{wu|}zxwvtstwsf^^oxl~}wwyxuromi`^p{vt|}x~|nkstfdqvz~{ups{yvzy{yuxssyxsz}zyxwvrruvk^Zkzkxyxxwurpmi_]o{vsyw|rjosjhswz~|wsv|yw~|y~}}~}sp{tsxxsy{xuutrnlptn`Zknx{wwwurpni``syw{wx}phkqlmvx{~}zvrvztvy~xx{srzvtz{xvvvtolpuo_Yl{ny|wvutrpmg]au}wuzwx~rikonsyx|~{wvz{syx{}|~ypv{sr|suyxtssspkintn][szs~|wvutrqmd]i{|wx}vw~ynghkowxv}}zvx|uq~y~zvzsvzsyzxuuutpkkqujY_xuvzvttsrpi^^p{vtw}vyynhhluzvv{z~}sw}y~}~~ypuvs{svzxtsstqlhkrq`Xj~yyvuutsnd\iyxtv{zu{~{qhceoxtpw}{~uqz{wv~uywu{{wuuvuqlkqtiY_r{y{}vtttsph\`qvqqu{wv}|rgciwyqq{~rxy}~}vnv~zu}}sw{xusttrmijpqcYet{~wvvvuod]hwuqsy~vw}~}vk`^kyumq|wp}y~vt{zyuz|xutuurmjlqoa]isy|utttrk_]lvqosx}vv~yna`p~xpuwry}~yoq|zzttyyurrssojginlaajr{~wuuusk_aqwrqv{vu}~{uj\[o~wpu~tqz|{sw~tu{zwsstspkhilleekqz~vsssqi^`pvqquy}xsz~zpa]pwyvr{y~}wos|{}}rrxytqpqpmieehigjnqzytstslabrzutx|~|ru}~{ywpbYg{zwoyxz|tv|tqxzvrpppnjfefhjotsw~|tqqqlb`nzwtvz|}vq{~|{xm`e|}pu{vzzrpyw~smswupmmmlhecdehqzxwxsrrofbo|{wy|~~zqv|}ywwumabv~|ps~~vx~~vt}x|umswuqnmmlifddehs|w|yqppmean|zvw{}}|~~st~}{zzsggx}quvuz{sr{twqkqusolkkjgdcccfux~|trqnfet|xy}~~}~~tu|~~}{xvvvoegwzpvwtz}uwwxqlsvtpmlljhfedbgxx{zspojbfv}xvx|}|{}~ux~{zzxpio|ws{usywpxzsvzkmstpmkkjhedca`i~}x~{urohdo}}ww{~~}{~|v|{}}~|ywvvrihq{~rt||wzywpow{{{|ttrnokgemc_\Z^hmlw}vwpppst~vrnqw~|zd|w\X[aroh^\clqpxwy}ywvz}|jl|}wu}{}zy|xspxknzs]qwptmqtwt{zvz{}~{ipvf]q{|~{x}xqkjpl^t}qpymix}hq`Vbolrpqyzsfitwgw~{{x|qjkty_mu^\eeaik~}m}y|}pafouhfrW[aGKWZVWMUg{w|y]ncdz}}|}|}jfnrok[i{{wljw_ngf}{umcqyupofyr|zu~p`bd~{icnw{vq~}|oZfv|~zl|ussdfenp~nswstxk_ksvyv`jfoabfYg}~togmixljdeZV_OM`f^l}uu}yyxhmfga~wx~udlq][f`Sfnzq{nqnh[gxmhyxa_lpu}|skqqv~jhwjcv}kmuy}xsxeitccw|rylursvmnruntwd\ll\jvhYc_a\nnqw{y{nmkgk~{xfgb^U_gbckic~zxyuntvmuqvsz}}{yx}{w}xsu^eofZmrr{uwzwrnk^gtwdmns{|zuflt_jtx{x{qh~ts{yf`frfj{~}nkzobszhdpkheyrooq_kxp`nomoia`~~zzahbb\ttqxlng`Sgvoltuz|ywurrymfox}{~y|{jk~tfkt_[g}}~xy{ilipiomb_poavvp{ys~urrakxwlur}ps~zsskdeYXjs{|}nhz~mq|tzrm\kwnfuwb^hnjuotq|uy|yuwxvkwkomqjztsymumplrii|||}~zqn|o_nr`dwvy||~zyz~zxpdowkbjmzjv}oevmjbvumqrdnuvgyykg|ypuq|wrvn^imbgpkwsxo|kfqrqwptosdm{whnlnuxeihpyy~yv{~sgkkdlhtxkjilsmrmnvtutvxxvkduuzszy{{vyvg[ahtWQkqn~ws~ojnlcl|ken~xi}vrv~wtwx|vwlmqjz~vymiZifcwrmikolrrps`qmoltw}rsvihhfhijwz~z}~}ujno||][iosy~{~znnn{vos|kwyqxx{ioezhpspx|u}prz{uuorr~rlyggiqyzqpwpwdzwzugpezvaZZ]okn|}~|}vx~zrzzojog^wtw{jwvrksilhgd]~itgbw}pu}rhpt}}}osmhyp`hWUjaaqtgsp~xtv{|nsxuisyfru|t|xvt~w{z|nglsapu`htxztyxq}r|wtpwx{potoazyfinipxvzzz~wkkojwm[djh~zuf|ok|hjxchiotrx}nosax{wzw{}t{m{^mldgg\rsun~}vx}}~w~zs|||rmwngskZqyfvwzyz}vtq{tm}ifugdli~~z~i~{xvzqryyh{sndcsoklr|{|v}xppqln`bser||xxuprj]uhnjj{qrumk~m|st{zposgfcpjW^mnxsn|t{ut~{t}vt~{mxuozgX}ilnxzzy~w|kpwqiibf]bxsq|~rortz~zzihutujsspjh}}owv~t~u~yvwxmsbmg]kku}~}w{pfll_jwxp{~uyji}wtspmuu}|v|nnlbcvm\htnxm]|vzv{|o{rmsehqig^v||uxyxytxnoxggmivbmsih}wytywyjvvv|kqmknnsjgy}v~~y}~x}pncijccliurwyxexkggcuxkxxj~{q~|pp}nyy|v}sjjhqpabsnneWjww~vwo|ys}kmreg]p}yx}v}v~x|vyxnmsut_heccnps}zq}}mtry~s|{ztdwhurwws{~szrzr}z|cgfztdYTXc[jho}|jlozyfx|uxmgy{xhaulr{vtwrzzjuxysx{zwo\\vslivvz~~krbdm`ptcZ[^ixwoz|suzx}~v{xfxpnxnlhkky{`|{w{tvys}|px|kpouacr~u{yx~~z}yqv}yhqvbhl`U]br~wu}swr{tt|uvyyzzykvicswvttr}{ntt~|i|mfzhgsejxa_ustvsstr_nqeuwjeh]nx~lzuw{yyq|owrtnbZalxtgWsx}j}~uwtmgnlrvlooykr~pzspxiujbwlo_vpgtj_o}}yk{xwdovrheskeutxyyvszwwywvdh~mkfcifu_n~u}q{lu{vmpyvnlyrzwkluz}zryxpjrm[PvtXVc]izyyx{s|wyu}sxogjutjwnqw|u|}~}jtpvzignjbimdbsoxxuiwwlprbtvkk}wr|m|z~w}|v}rqwvhswpkpaZqfcjrx}vzv{|{uo|{oruquyiur|wz}|puq~nh|wll{hnyiPeumfsw|t}y~~lr}wkop\f~key|l~~}}v{~{uzumt}pnhbqgj][huqp|{v|gvpytoiytlzwy~x{}pv}rx{p`neZ_aVblho}w~xq{{oiqsprquyl|}~{|~{n{uuleqrflb\cm`fq~z{{||iwnrnhyxnsyqyscxx}z{tpw|pprtlvaaoe_bum]mx{{xwtzxmrytoqf|tvvs~yxnvswks|pu{z{jXkxi_omlhxwy~m~ykxp_nmgnhh{~x|x~wpxxmohlvgg^bin`gtxyyy~urzywtm~jlrrsyup|twm~wgdsaacXbkf`mwt{vmjmhlf^nmg\Z]V`rujnuhrurm{~|}vv|}nlyXT_hqgeo{vkjqs]Su|~~}q~|g{y}qcbldnxirx]|dgr{w{{}x~wrzwf_ouh`fvrc_yv[~bmpvmabuu`lzdo~wx}}vt|uhm~{mlWmztw}v}~qmhzfxuqousrhphgu~iau{xytuvuYt|{kZb|dLf{jqn`yj{{y}rwub^da~uvz}`hcWbiaV^RyjYX]_[cUm]`knjTq{ytusdzssk^yxcywiq|u~|op{rbcdi]RW_u~pzfceydfXh]Zgq\idfkotf}}dhws}xy}znuhwx\kuwwlzwUa{qqiiok~{uglqvaUVouxspn^Zfy`\PqrIWjcUcX]dq]Zq{k[ntorsu}zqy_v|sw{dw{|xy{|cZernVNQto{vh`ze_`qRgwjdmcdv~_f{kk}uq|yt{|vh{jpytxvgwnSpwqkwta|Ÿ}hanzo]SV~s|x{r~m_[u{[TatjI]m\_eYYstT`ps`lvxunz|sx|~~q~v`z|{tweyu}xmu~]S^mdVQOs~xmcygVsxtWaqdmqc`|fiw{juvoy~y{|z}}ogqxv|xxiotZnrul}rwlw{sulYaqk^]T{qrui[pzgGmnh\Qe[ej^Ule^joblm}sx}{oxvrivw}r|pwwu{}}j_vlQO_cZ_Tn}nzcq|{Xdin\cdjyn`gmpwmnzvyyupn}vemoytipiomrmumn|zxdqw`V^gcgcl{`}wbdsq^O~f]bVX^qj\Yv|lekdd~~n|u~zorwxvuw}tyrxsgv}|xp\bk[PR\[fckgni{uo[y_p`\f{viay}rrkjsx}v~{u`q{jon}}{mi{p{opirrc}x|xdimb\Z__niv`zfgsjfZvpUh^P`smaZq|wni_dpuyyzfzv{yxwzuwlzrcvzpxk[`^[WTT[jgymmtwqonw^qgVq|uif{yobpszss}z|k_qwpstu{msoynnb{xlczzx|{neg`da[Xempuq}zgsmgjotgWlYTppj]gt{zp`Ztzkquki|z~xz}tyloujnzvufyvoa`YX`YQRfdvyxvwyop|{xiesZm{vngw}q`kyt{w|z|z~r_iywutrutwj{vm_vtjduw{yxzpje[gg\Rbfn|z{~y{owlhr{lmZhbZrmk_kqxt^Zuzoq{{~ljw|vyu}gp|qjusuy}tivrnedUWc^NRa`urzyqr~rscsfqwvnkxyt^jyxsxx}y~tefrsyvr~{~y{i{~p^qpjh{urwtzvqnfWclbR^bkzt}v|pkunki^jeokmblqs~eXn~}t{|n~u~{ukmwzxophvrmrzuo|rxnmlhgZM]eZM\[nusustqterxvpsiwvykbwyko{x~ogflsr}ulgenliuztq~vqpmn]Tcm`Wb_ww{zv||pnngo^hznikdtpr~f^r}|x|ui~l~{uojnzz|sqjqoo{qv~w|{|zpeeegWKVe^U^Ztwzzxv|kyliznqj{wvqfv|spuk||xyplfdov~wmqfkikws{~zzkhhmaQXgj`f_u~~}su~tp|ejp\{fkbsuovtdnz~zoy}jxq|~vuohmrnolnwryxotixk]^d_MLXeag_k||sy~yjznqvqisvxqs}~wpp|pu~z{zqpofay}|}nz{omghqzwwxyoouzdaggVRZijrhnyjyyuyzdlphvlbkzrrvxory|{uk|uywq|~~tuvof|wxtlkuyq}owanetaX_aRLR`gshk}n|}oozsvgqyxzzu|~umyy{x{mtz}znprlbuz|~}~qx~urhhsvp}szhrixi^edWSXdn|luxiuxy}jhtu|q_pztsrqr{|{{nmrz|s}mt}{qtxpj{|y{{vkl{v~qzvn~oejcocY_YONU^mxhz{xq{~pp~tg{|zxvs{}}lrpwr|iq|smqshkty~s|vxyphmwt}vuylukjsfadZUU\e{vq{mov{xjm}xfiywvqmo~~z~pknqnwxiv~rrvumvsy}||pmyuvx|zo}|sgurbgk^_YQOSXjxkyw~sx~yoy{j|||xsp}ylrk{l~nk{smpsjnmu~r~x~v{tkpvq|{~{vzovyqeqlebYUUYd{rw|xrqv~|om~tioxxvpip{{knirms{iq|qpusqtm|~}||uq}utyxyr{zrmopi_lfaWQPRUhum|s|wv|s{yp||}yqm|wlrgjvwgsunornrknsy~s{yqrwpuryysxovkdqldYVUW\stxu}trz{n~q}xpxwxrih{~{mlme~jtwiv~uprutykxyv}wyxrwmvx~||zotfpg]lk^RPQSZpn{}tyvux||~|}vln~mqlj}lqxjszrnpqswfxv{~xpzzwz}upsl~z~~|t{gpn_mrdWUWX_sryxryvzxw{zxxrhg{{injf~}pjxooytpqtx~lz|}s{~}wqt}yov~w{}y{woycem\aqdTPST[nmzvz~|w}|xmj{nonf{}wirvmrroorunsy}|}nsy|wppvs{oz~}|}p~pani^pq^VW[^opvsny{yuldopiobryypgwtnqrrux{u}sryzspsz{pzwz}z~ytzkws]aj]aqbTSWZimv~w~vt~yrhowkrcpxwwfpznjopsu|s~t|polahZW_it|{rlko}yy}}~}}~|}nfggqpt{zz~lqZmrmqxvxj{tmfm~yr~wwv|zk~y~||zoxiflwxwyyq|xcrufbptkxwg`bdpltyy{nxngj}w{oyqjtyhdw|ma\esfajwhek^]bo}tmmo{u{rww~xqrvwka\c{z|m\q]WrtjaYWcrnplfzwzuu~mqvvx|mz~~~x}stzphouzncmoeji\WWac\vyw}xwwrmvvur{xmkjpv~zrwtuuqtkdvp{qwyc^xp_g[UX`qsigivosz}ypu~zxuxxjb^`moypamzqcXVamxoqjdxst~|roktw|}nzxrr}rsx{~plruttvsuqimd\Zd`\tsx~ywtnjstzxyzzxokimz}~xqx|tvyy{iipbr}}jarfhYVWarrhfjx}{jp}y|wry{vr}}w{t{mf``gv}xrp|ziZXcmu|{puhewpqyvzuhpw}o|tk{v}rruy}tqvxtngm{mnof`ga[rou|xtmho{zpvxszqmkmu|}rv}~qwzpitcdugwpo`XW\k~zmght{sze|zttrw~vkt|~{w{~~yvkdbemt{|}~uzg[`jqvysvtcrtmqzxxrgoysvlpzywrryywsptxxsk`qomtmhk]erkyyrjhoz{itsqvpmnsyz|}~tz~opx|jnucgsu{|zvb[Z_m~{pjkwwozjvomowwityu}{|s|qiddiqsv{}|z~pbemrtvqzxguumovuwzkiuqwpry|srwwjiovxupdl}kowrqacsiswmhlt}rfmwsomou}}z}x{z|yjlwlhwkis||wwqa]_gvyqnuzmw}tsw|heo{~rp~zw}vy~||pyqulekqrspqpmvi``k~~nerrmysz|}xx~}qrm]Vbpy{yol{xw}seunjxxpmkip~~uqnlnpvzuxs{n|~n]OXZWlhblr{~{tqgeju{wxyecmsr_fuvq{s|pktweQIJMVckigo}ryvsvstwwuostv{kb_h|}qtppzv}gipyvVR[aa_WZe_u}pl|nhwllx{ma]`gr}snpt{{~rfcnvm]^j\\cfkrx~op~l~|neelw{zwtprj\ijslrv||txidekwmdabhnkc`hxyoyoluxxwuokywp|re`bnz~xox~}hclinputmb]eZa}y}~pr{lyrjs{uhaachpxpqu|zxvhbfson{jdhhhimt~snsy}wmjox{zxxwyn^gcgxtw|yn}oheeip}qnosvoe^bpztyuit~~{vnlzutviccjv|uzs{}}igf^q~~wifhVavy}y|wu{t~plu|wlfdddfq|vu{xu|{mfflw~{|w~topmheelwts}zz{tpt{|yy{}qcj`bw{||zwlumhdadyz{x|xkaak{x|yvzkpxomztu}phfiqxysvt~|xxpjhZdx{upr^^psv||wy|~yxmsy~{rlhfcagqwz~wxukghmtxu{|yyvqiddnxysww|xx|~{xy~}ikg\jz~vwpw|slf`\e~u}~{ohhswsvz{~}krwntyn}{pjilquytv~{|vvmmdWdu~yxyzvbisru|}v|zvxpuy|}ztokgc_hszz{||vkggjowyy}}wogelv|}xv}zqz~~{wx~loh\hx{r|t{{tmf_Za}x}xpnuwp~v{}{|wmz|psylwvnklory}z}zuz|t`_ioiWP`hbaggnynp}}}p~~|vsvopslagv||z|iqbz~~youylrsoy}wxw{{~sq{tcizyrsty}y}{lqjjmmkgcssnkmpfdp}~z{{uy~|~xx|w}{t~qjoypil][enqiXXkqhcdkyqm}~z}~ycz{{tq|y}wngqw{wpljhgjlfabcle\fpuvrmlrz~v~|hhzxusp}|oWozxwmb]lsikb\dc^dosrqtz{py||tvzqo{~{yx}t^]q}gdimmfWSdof\\hshn|yux}yte|~~uvy~rqshcnw{|uolnpk`Zjr]Zfosrnjkqwzzu{ik~v{stz|{~caqryrgmxmnp_\ghdioppsx{po~yyupx}spy}wxx|~z}n]dxvlnoolbW\moaYbpgjzxssy{}}mvzyzyw|rpwsfemqvqruteVixbYblpokhhkpstvqjy{~xtyz~~likiqz}sz~puwd\gnjjlmosxvimxqu~}snwyotx~|sxxz~uddu~~yywtpi`_mrdYdlclyvpou}~|}|wysv|yxtpx|ncflxzvyx{|lWh|h[`hmmjgfhkmo}}w}znt}x{}x|ttkcksxvyqacqtnjkmqwwjjtpu{~xvywzoc{tiovxyy~{{xxn`drvsv|wrmfhw|txo[jY[iw}~~}{zuv|ojvzysffprcX^o{s}nseWnp]UTVX[]^fxxjoqryxpv~umu||yryoiwlhp{~}tbVTY`hdY_bZ^iooicgvvim~w|y}~~~}|~vsqbVX]UQUMUhjfceiortuwwtlusfr}|ywx}{rjuyect~wuq]P\c^WTY^```aa`_g}x~~vvynhty~}|}~}}{swrjbUVi]ENfwrt~uv~{wr[]w}pc[Zanp[Klyizzj`jfYVNN]lmnx}y~rjly|ustnj|x[Zr{qfacku|~~niYUertmd^]cjcQSinlwmbZ[jxd]``an}~zxwor|}p{~tjeit{o[M_lXLYlw|}|yw~{qaWU\_X]wyw{ypmpstrmgcfwxqrh\[flgfsz}{|z{~t`Parg_k~{}wqx}lei_\gsvria^`hkb^owz~xsnjfci}{xxk`_lxww|{ytppx}rfsxqkggkqx}yg]pzl`duqqujit}yogbcjplm|~qz~ulhfdb_\[e|{ujjt{slr}xu{yy{yc_ruibhw||~~x{tuxpfmj_dpy|xqlhgji_^bdw}yxvtpkjx~{k_`lrll|yssyumy~iq{zuqnnosy~m[bokc_i{|z~qhmtxunf`]`jqrxz||qvvnkjhd]W[mrltztnmwuq{~ytonpw~nm{zskhs~|xwmdi`QS]isxyyvsrsokddywspmgabs~zomw}umt{{yoyyhmrolkllmqxukuyrkaaluvw{xumotwwvtqkhjnokeu{~jjwzqiilmke^aookx{sottjr~}}vrrw~|lmvrolfjx~||rlriZW[afjnrrposxwhbxzxvpidmvnq{tqwuq~|vv|zyvuxvrpmiecfny~woy~xwrihqwwwy{}yysfehknptvupmqzxff|nr~~rgccc_ZZfkdm|}xx~ulsvz~ztqsywpzxopneepy}|ypfkpdZ[^bfjpuuqmqzv^^xzuutpjkuukr~yros|~up|}{~~ut|{x{yurqplgegmuwolwwotxqms|w{rgeeefgkopmjppl~|~poyykcaa]XYcd[dtz{{|ww~~{ytnkkqz~wtstzvmq{~|o`bi_UV[`eksy|yss~rbr|pkjifityor{ww|{w|wvppyxplmopnlmqx}wq|tpwzpinx}~xv{okkkjknrtrlhr}jryulkt}ymecb_^goges|}{zz~yx~|zwsqrw{vmu}qjr|zrs~~lcki][]`bdjqvwsouom|}qljgfovmht||zxwy~tl{zokkkjhilsxvpzyos|{piox||{zovtieggggjossnjo~rq~wonuxnb\[Y[fnfdq{}|||}~vwyy~xwwtrprvxqkv|oflw{toxw}kcje\\^``bgnstpmstu~~rjgehrthfqvvuuvx}oh|}pkjjiggjoqlm}wqwyps|wrulcbba`afmssqqz|}womsvmaYVV]if]fsz~}wzx{ztrqonosvtnv~pnvwqx{v}tefe\Z]`abfnuyyvx||}{ywlfehsymgqxyz{}~~}zscrxpnnnmmnqrlm~zrt|~qlt|||rjihfbaekpqppx~{|~{toszwmc^^foi_eotw{sv{~}zwtrpqpjerynmt}yps~~|rqj_]^^\[^fnstu{~~~sjgkutfclqruy|~~yszlx~snmkjjjkici|xru|uknwz|{nfffc_]`fknnpx{v|{rouxpe]]eog]bknqv|sq}y}xvtqpome_kzvljnw~}sow|}wvma]^^[YZ^eknqw~{|y{oghrviafklmrw|}~wzrr~}smkjiiig_^p~ztuzzqt{tgccb^[[_ejmpx~yy{qmsvpd\_jk_]floszuw{|{urpppplccs}voosyyqt~zwvzyk^\__^]^cjorw}{zsw}{phjtviekooqu{xxtt{yqnnnookcbtzttw}~sou}zjefea^^aehkow|tryoovxoecmpd`hmorw~~v{tqppplb^lyunmoszxquyt{yf^^_^]]afkot|}uzrv|ynhmsiaflmnqw|~~y~{}}xokklnmf`k|}vtuw{~rns~vypc``_]\^behkrzwmtvljpsldgng_dmoqtz~~unlmnld\etunkmpt{|vz}noo`\\]\]`dilqyv{}rlqvrjkog^biklot{~}{~skijmlf`k|~xvyz}~sr|qqsc^]]\\_bfimu~zpwymgjqoikne_gprtx~|}wolmold_kxuonpruzvw|lnqd`__`aeilou~~u}{pjnuuqspd`hmnnqv||wpnoplcdt}wtuxy{}rr|owqfa^]\]`bdgnx{po}zojoutttk`dmpqsw~yx~vommkc[crrkjmoqtz{tzvo{zmd`^^_adgjoy}tzwmkqvwwqc^fkkjlqwz||~z}}sonni`brxroquwy|ws|outib^[Z[]^_bjuukm{}rkmty{xj`elnnptz{y|xpmlg^Ydnjdeimorxx}wr|{qga^]^`bcflwxs}xnhmu{|ra]dihhinswy|~y}~xqomg_bquomptxz|yrs{|vne^[Z[\^^aistkmzynjpzobelpppty}wz|{wqnjaZbnmedgknpry}zwy~}zrjc``bdefjs}zrw}tkhmy~l`cjkjjlptvxz|zy~wy}|~zxunkuzqnoqqqsv|znemztwyz|ysommoomjjorjags{l~{uz~vrxz}{zyvu~n^h~||y}yrnwypmnquw{|hcw~~z|{|ztrrtutstx|umqzxmq{torw{~eWczvuxwz|x{|xqnv~zqnnoonllnnlip}zz|~~}}|vnhnzutuuutqmgfo~zootwxxxzzzzywrkktxrjmz|~|{xtmgb`adm~{uqrqlmrsojijlnnosxvnlquuuxzyy}y}{sh`djjhn{xtz~~zuqsy~~zupkoy{upoppnkhghlpv~wpqplhilpv{ywvtrokgis~}tqtwxyz{||{zxtlhmttqqv}yvsnha\Z^fq}|wtvuolpuuqnnoqqruywpmqtsrsvy{~~}xpe_afgioyzw{}yutx}}xrnrzzuqpppnkhffjpx}}soomieeiou~}{ywuspmnu|{vuvwwxyzzzyxvqkimqqqrv~|yvrmg`\Z^gr{}xwyvomquurpopqrtwxtnossqpqtwy}~ztlcacddhoy|}~|xttw|}ztopwzvsqqqpnlihhmtz{|wrrqmhefims|}zxvtqprxywvvwwwxyzyyxwtoloppoprw}zxuqlgb^_dlqt}zy{xqnosutrqqrrtwyuqruvtrrstuw{|wpiggdbcflw}zvtvz|zuqrxyvtssssqomkknsuss|zwwuqmhffhjox|zwutuwxwxyxxxyz{zzyxuqpqqonmmpv}{xtpkfcacgiio~~}~{uqqtvvussstvxyurtvvusrrrsrty}yrmjiebabdjv~{xwy||xstxywutttsrqomlmqsqns}{xwvsnjgeefhmu}{yyyxwyzzxxxyzzyyxvrpqqommmmpv~|yvrnjebbegfgp~|wtsuwwvtttuwxwssuvutrqqqrrtw}~{vqmkhdaaceku}zxy{{xuvyxvutttsrqonmnqsqpt}~ywwurnjfeegjnu~}|{zwwy{zyxxyyyyxwvsrrromnoprw~~|{xvrojfccdggip{~zvuuwxxvuuvwywutvvvtrqpqrtuw|}zvpmkhcaacgmt|zyz{zxxzzxwvvuusrpommorrru{~zwutqmieccejou|~}zxy|}|{zzzzzyxvsqqqomlnqsw|~|zxurnjfcabdgkpy{ywxyzzyxwxyyvtuuutrpoopsvy{}{xtpkigc``bgmt}~|{{{zzzzywvvutsrpommnpqsvz~~|xusqokhebbdhnu{}yy{}}|{zzyyxwvsqqqollmptx||zxvspmifcabdgjqy~{yyyz{zyxxyxvuuuusrpooprvy|}{yvsnkifca`chnu}}|{{{{|{ywwvvutrqpnnopqsv{~}}zwtsqnkheccdhntz~{{|}}|{{zzyyxvtsrqommnqtx|}{ywuspmifdccdfjqy}{z{{{{zyzyxvvvvutrpoopruwz}|zxuqmkifcaacgmt{}|||}}{yxxwwvtsrppopoprvz~~}{xutrpmjgeccehlqx~~~}|{{zzywuttspnmmoqsw{~|zywtrolifedccejpw~~~~}|{{zywwwwvusrppoprsuy}|zxuromkhdbaadglry~~~|zyyxxwvtsrqqpoopsvy~~}{xvutqolifdccegjow~}|{{zywvuutrpnnnoqsv{}|zywuspmkhgedbcfjov~~}}|{zxxxxwvtsrqpppqruz~}|zxvsqomjgdbbbdhlry~|{zyyxwvutsrrpopqsvy}~}|ywvusqnlifeddegjpw~}|{{yxwvvtrpooopqsvz}{zxvtrpmkigecbdfjou|~~|{zyyyxvutrqpppqsvz~~}{zxusqomjgdcbcdhlqx}|{zzyxwutssrpooqsux|~}{xwutrpmkhfdccdgjov}~}|{zxwwvtrqooopqsvz~~|{ywusqoljhfecbcfjnt{}|zzyyxvutrqpppqsuy}~|zywurpnligdcbbdglqw~~|{zyxwvtssrpooqsvy|~~~}|zxvtsqoljhfdccdfjov}~||zyxwvusqpoopqtwz~~~~}|zxvtrpnljhfedccfintz~|{zyyxvutsqqppqsvy}~~}|{ywusqomkifdcbcdglqw}~}|{zywvutsrqppqsuy|~}}|{zwutrpnljhfdccdgjou|~|{zxwvusrqpppqtvz}~~~}{yxvtrpnljhfedccfinsy~|{zzxwvtsrqppqsuy}~~}|zywusqnmkifdcccdgkpv|~}{zyxwvutrqppqsux{~}||zyvtsqomkigfdccdfjntz~}{zyxvusrqppprsvxz}~|zxstmqooqjj`a_^bblpxsyvnv|yz|rkavoxw~|}wz}wvy|ty}}~q{wgR[fuce[[gXbntqujtsmofstqj}|vfpyzqlrpyzwkixqsystmyxw^Zf~hnsv}}isleholgeqqrbm|}p~||zosndmpXwtc`ajnksox{v~z{{vkq~lgroojfv}k{x{x|tv~lurqvhWoZ]xZ\[bv|~{xqdkrors`enx~q}xw}txloyz~~rnbq}zmx}sgxVezlkfwr`ai~wpvcZdxsiqp|}y~vixg_ts{y|plwx[bgiytry{x{i{vad~___gkqn]}xl~elys|u~mzsv]htwlstv}zajsljkswwvky_Ysil[xa^uhfqgMibZwwyts|n{k}}vbdv~zzi{sp|~y|{tp}ptom^vzlpq_no^_vGkP``[vzxxxqk~~jlzwclu}moxr}vzww_kj_uo~yexpdvqSpjO{^Wk]eyow{nykwzl|}y|ix~spkisy{s{fWlbrqrd}nnjf`wZiuPil\mrsvxb~wo||jp{s|}u}hswy}~nZ\g^w}bt{knhafnWtn`ifhyzmuwom~~zy{}kvcozzwx|xsfm~ryxrfWjnew{_{oldjjd^d~`gjbl|wqxb|w|js`oowv{wwpwwyyz}uucadn_rmurgpkjbbTynamcpmzzy|sjwzu|]}vfnuuxz{urwpw~|yvjb]rjghxiiljieYmw]tlfiw{za|ts~ch|dlpzz|q~rzzz~|{yocYhhhkvpluekkb[{cgqfmqz{yzfj{n{zr_ldsz~|v~r|t||xtyhS^rhups~nphkih[wm^sbmsurz~u]qzz}dwpdo|{x}un~svxwxxlUTlgp{nsukjjhWszUifoptruy{\{uz~hwuenwuyut{u}{}ssvpWSlbjpusslbc_p|Wecnqssw|{evzzjowjhky|qupuw{wuquaScbgnyy{skea^o~Zejmmwztv}eq~{gryegg~vwyqtuwtosv_Uc_fszy~uoebcmagdoqv{wv{}c~xw{x}cqwghhqwruov~xuqtx_Qaa`vv}tulbarbifpptzsy|x`u|v{y~hrtimh}pu{qpuyxqqts]S`^^qzvqdbofdftplxwwwx]wsxy{kkxkoj~qu|np|zw|vppvaP\]\n~xsbep~gbhopjtvwvybsw}w|zsixlofwxzxjz{|}uwnmuiTY[`m|usgdj|q^gmuityut~md~z{yzxcxoigr~qpx|{qnjvvWS\^h|zujbkw{cfgvprvxvzvar|{zv}ipriijp}zovzwy{okn{dQZ]ds~wtgespagtvowywv{dmuz{ytzqgtkkizxzpxy{~rvjguqRW\^ixkco~fenyoqw|tvqc}q|xwt|hlrnlms{zmt~zvu{lgeubQX[ct~~vfhwtajurlvzwruekvwy|xzreropkzz~qt||jxtd`hqXT[bk}{oeoyndmxmnw{tssdwoxz{uzlironkz{mz}wfyia^nmSWalt~yleu|{gesumuyzuvje{oz|xuthmmooywxr{u|~hpr_`ft]U]ir{|ten{vcm|rov}xvscunvzxrwmgnmpp}o{y~}{xdte`_plV[cqu{mf|~jgzxosz|uthiwoxzsstglmsq}~tr}uvfnk^^gr]Y\lsy~sgwtet{npy~tsmd{{mt{wtvikpqrx}|l{wz|neq_[_tdW[fr{ykq~{hn{qpu}wqodq~qn{|rvojoqttl}{~~utbqa\[pjWZctx~{okki{ros}zsqdm}tmz~tvqjnnrt~qz}y{qxaob[\onY[brt}qjoh|uqr}tpfl|vmxsttjjmsr}tvvzo{cme^\kp[Z`qq~tjqi~upr}uqgkywmu~tusjjlrq}su}v{q{cnd^]mp[Y]nouotkuqs~soflxvkuturkjmqr|qw}x{qzdod^\mpYV\mlxpul~trr}toekwwktvttnklqs|stu{nyene\\jq\WZmi|pzi~vqqzvoejwwnrwuuokkqsxwt~t{lyhkf\\gr^WYkh}o~k|xqrwypfj|ywsm{uvqkiqr|y}p}s~~ovngj\^brfZXhjvtzowqsu}sjhutxj}~tvqmhnqxzq}}wzz~wpuem_]^pm]Yanmzuzrsusvmfprzjsuvsohkqv}}tv|~szlwhke]]hsbZ[nitqzstxzrfixxtug{turmhos|~p|t{~z|rpqfk][`qj^Zgnp}x{usvqujfpozittvuqils{~tvz}sx{|ishiaZ\grd]_pkvs~zssu|rfiwyqyf~}tvtohpuq|~uw}wzzrjodh\[^noc`htquttup}wmen|owpiwvvskktzzrzq{wyuxjlied[[bsmdbrs~z{{{}rvruhhsl|ettvuriovsyzvr~|t~ytrglfh][^mtidkxswxuvr{znemyotrexuvumkt|zsyoxuwtujjigcZ\dtogfvt}~~~|svqtfhr|j{cvtwwsirzs{yst}yt~vrngjhf[\apskfqwwy|swpwjep~izlkvwwvknwuuvyqz~{s{zpphhhh]Z]jtmgmyrz~uvrx{ndmwosvc{wxxoluysz~pw{{sx|pojfgi`Z]gtpijxr}wvtv~rflvtozb}}vxwqjs||t}}ouzzsv{pmlfgkc[]fsqkjwrzvvttfktxl|cxvxxriq{~t~zptx{qv~}rnti`ahvvnm{wyuvs{qy|xpdssvusqtwv}|v|rutmdv~nmegod\\cnmhgtpmnuukjki~ymbityoeyxzzsfgk{{{jZivfROXNILUbc__kfy{yxvznu}zogcov|mfilkwuvrkvvkkigco|rlvjbcjuxsr|x}ufac]rrfZ^ixudydn}tvtm^_gyyume_Ze||jo`PNR]a]Yc_hz{~}xvv}v`Y]nsvspnpzrsim~r}lgffdixzp{xhgju{zu{{{~~yxopenyoa[en{hpsc|ywvte^dr{{vfa][^pyx{d[Y_ef`bf_xv}}{|}s{wxiYYdttzvtn{uvpjstzgfcddn|w|puiioy}{xxy|r||pqetym_^hryfunh}xxwrc_gxzna\YZbuv{}f_]dijehfb}x}~||z}}t{x|ufWZhxyzvsn~vxplut|~wedacdq~w}oxllq{}ytxo{}qrfszpb`iryisqhzxwvse`h{~l]YW[eyxvkb`flollld{{|{}x~}yuy|z{}yshYYh{vrnuzwtlpzu}}yzfb_bfqx~l~onq{~otowruio|ugahpynouhvywwthai|~z|kZWU\h||qqechnrqqpg{~}yy|v~xvx|zxzvqgZ\l}vsnv{xumqyv{ywxda^agtx~lsru}~ymtnztwiq|uhcjpxpqtjwyxwthcm~~uxdXVV_mywrjhmrvtuoi|{u{xx~{|vz|vy|wtodZ`u|vsnx{rovzuztypaa^cjyunttyz~~pprl{ytjx|rffmtwotpoxxywqfft~{}vvp[XTYcurqmmrvyvvhuzw}yw|rxyx~~|w{yvrk^\i~{wqz|qnjjpzziy~lko{y{w}qt{{ut}vdzxlpipvznb^h|ymlmim{~p_mqlcgurxyrqtzkjjmvwil~tfnngkfuukehvo~{ravmcflsqprrw{~}tr}qdr~rs}fehovupZXX[cs||nfl{}fj{vnsnox{vwuôo{yxvka^fw|rd`iwt~mcZipozry{spvziifgo~z~{ztdbofqxstdo}~}ol_o}wvwyxsswx||snwzq|xz}ghpos~qi`[\fqvovpxrgdjuwxwusoxzvu}pzx~zuofdmuynnwqvu{}yia[d~{|yxps{lgebfr}~~vuicsmjzvuopbi|}~~ztyyz~~unq}}stxw~rftrnu}sofdhrvtn}m}mgip}w~s}~lsxws|uo}w~zusmdktyztzzstx}re^\s~~wrmyuhhccjx~w~rfhuio}r|xqkapvt|x{~{sov|yq}y{nlyrn|xtngfmuwptrrxlgkt||{sxkrwuu~qty{}yutndpw{zw}yw|u~uzoc]`z~vnlzrhhcem|~u|rgjxlqstpjbs~~~tt~wz~{tqx}wq~x{om|vn}yuoihovxqtrpxlimw}||r}zjputusuy{{wstres}||x}zw~r~puob]a{w}pixtiiefm|~t{}slhztq}t|~woner}}}yo~zw}~xtxvpz~vvwl}qy}vrkinwzupyg}|okoyx~sv~mlrttys|yzurswhqz}|vq|mood_b{zysgvvkkhhn|tx}toj{zr|v|{wppht}|}zm~{v|yvzuq|}uvznrz~wsliowytpwgz{omr|z~tv~mkrtvyu{zxsqvzhv|zxo|}kmnd`gxzpgwtkliiq~s}|uol~zt}uz~ssojy}}~toyw}~zx}{sr{w{wrq{xrlksyxptnk}xppv{|qzzjmrs}s{x~}xts|vh~wvquhpjbaqvgoyolkhkurrr_\abirceuy}{vrihask|rw{|yx{~i`uu}u}fjyrpqnov|{usnuryhbhjr~rr`]xkhjtu^mxlqwzxvv{v|tswwh\d~nl|uqrpr|sfsrxni`^j^X`dlxkiy}up_do|y|kX]{}ygmt]]mgdebbjtxvwtxmkwyvrjps|yy}oY]xz{tYhreioqommpjtvo}mpobZetuzwwtt}zjep|kdm_V\`irdbqvz}~vtpm^buu|xtbV^yzw}d_ngcc_^dnsrs~szynkrxylqu|zu{tn][nmppmqsqnmnhnwlwolqf]ax|q|wwsqw}ngk}nun]^bhti`nux{}}yttse^k||x~|~suj[Zo~{u`mmeea^ajpqqyvutmny{~rnsxs}|wtgYcvk}oquuqonkgy~nm|yiomb]m|syxwuqry~wkirzrxj^aeptdeqvy||~tqup`bqux|voqdZbwofrkffa_clppr{{tu|rnqypot|}ttsrc[i}|n|psvurpoii}{lm{ufmkaass|xxuqrzz}tkkxzsyk`chtudhswz}|}pouoaeut~wyqilb[g}qhrlhgc`dlppryxts||spuy~rns|~t~mnre_nmrswwtrpjh{|ljwvdgjccvvzyxvrrx~~}vuztmp|~qyrbbgsxhgrwz||zqirtghwyzw~vwrafc^h~zhqoiifbckppqwvspu{vtxzywmqyt|~hgqlerm~wtwxwtrlgx~nhrwebiffxzxyxwsqv|~|ytouuquqwvebhtzkhrwz||ypeqvkmzyx~v|rr{o]bcan{krpjjgcdkopqvssps{xw|{yxnr{u|zcermjwnxuxyxvrjh{{lhrubaigkyz~xxvrqvz{z}yuqltutyszuedjxxhktwz{zxjeuulq~tzsynqxg[cdex}rlkkmjmqotuxyv~\ZbXcq|}y||}tbarfiwremss}xuqa`_]`fikptqvtx{ytkp}zbXz_]cinqrvztzmiqmxuejsu~ori\SMNTY\bihpsw~{}{v{xcUxskmrwzz||n}y{zumc^qrlvzknz{unvxj_[^acglhnpp|~zy}}ru|zpqeXv{vsssqs}wl~|tlfv}u||kkuwsntzqe^^``eifors|xy{|w}vyxpqdTn}p~yzxtmc\kyu}}moz{pmrx~qjiiilniprp{}xvy~}tuy~unqfUk}zwwxl|~{ukbnynkuw}yort|ogeeehlfntr~}yy{zsz}ww~zrriS`q{{zwqg\c}w|smx|ujorypmlkmpjnupx~zwx|~yyu~y}xpqmW]}zx~lx||}|wnbd|}wjrx{pqs~ynihginijvsx{yzzts}u|~}spp]Tuxw{{zumb\r{ynq{~lipyvomllpnittq|~zwy|{xwx~y{rmpfUl~{{vmz{{ysi_mzrkuzzmrzwnkjjmphoyt{{yzyrx}zu~{~vmllZ\v|~zzxsk`a{zz}ns}yhit~tomlnrmivur}zwy|{|xy}y~~|vmingZu|}wnyyyvqhar}x}rju|vluwolkkorip|uz{xyyu~}~{t}y|~{xpghl`byzyxvsldf~zxpq|xiiz}snllmrogtxqz{xy}zw~v{|ywrjekoew}}}nyyvvtqkfr~vwxjq|znwxolklotmm}yw{xx{{~ytxxw{yvrlccmli|wxvtroijzv}tnz}wll}tnmlnsqhs{rx|yz~|w}uxyvsngckun|~~~oxytsrqmiu}tvykp}zr|xqnmnrvmn~zv{wy}~}xtyuuwurnhacpqp|xwtsrplpywso{~}zump{snmmpuohw{ry|y{~zxxtwvsokedpwrxp|vqqqplk}ytxujswswqooqwukuxyyxz}yz{t}zqturnjd`htpyx}}uttrolxwy}os||zxrkwxronotuin}wt}~zy|x}~suvspmhciusxru|rqqpmhs~vv~|mmzzqx}vsqrwynp}w~|yyzz~wuy|qstqnkfadoop|{wttsoiqzyso{{}{ysim}vrpquwlk}|s{|yz}}~z|suvtqojegqpquutrrpkemxwply}znp{wuuz{ppw~~zyxuxvyzprtromhbdlij}}yvvsmen||tp|z}zref~{wtuyzmn}u{}zzyw|~~tvwusqlfgnlkvvurrpjahzyslx{{mj|yy}~spy}{yvrzx~xx{pqtrpnjdcihez}}wwunei~}zoyz}{tg`s{xw{}rl}wy{zxu{}utwvtrnifkmgw{u}ysrqlcbv{y~{mrz}~sgs|{~yo{~z~{xst|y~w}~sosspolgbejem~ywwsjds}sq}y}{xoae~zxz~zmsv{~{yvuzruvtrpkfgmkmyxvqqoiai~~zzvlvx{nh}}|vq|{}zwryyz|x}zonqpnlidbgkivxvuqigz|rrzy}{yvnbj}zy{yltv{}{yvx~wpstqomiegopsyxuppoicm~yyvlux~zok|}vq}y}zxu|yw}w{xlknmkigcbhpq{wttrkk||}to}zv}~{yxvqgl|yxz~{mpvw~~|{z{|}wnpqnlkhegqxz{w|wooomhnzwz{lpwy~~}xp|{yo{w{~|{y~zs{~xywkhkkhfecbity~wstson|{zvmx{ty}|ywwwumo|yxz~|nowu|~}}}~{|}~ukmnljihfis}{wzwnnpnjqyvy{mouw~~}}~zt||xo{vz~~}|{yrz}xxtifihfedcdlx~vrttpqzztmywsx{ywwwxwot|yx{ylrvu|~~~x|}{pilmjhhgglxyw|tmopmkw}wvzwkr|sy~~}|}~~xv~}~sq|v{~~}||ts||xwnefigeddcgp|~tsusow|y|}op~rtzzxwwxxsn|yzsm{}tx~~}}x~}tjjmkihhgiq|}zy{pnonjpyvx~}om|tu|~}||}~yryp|wy~}{yyry~zxqeehgeedcdkvwstrnr~{{tn|tsy{ywwwwsku~||wmxuw}~|zx~vkjmljiigglv|zrnnlgm{xyrm{uu|~}|||{vmxzr}xy}yv~{sz}ypffjigfeccgqzxtsojp~|~vp~tuz|zywwunepxp{wx~}yv~yvkkonkjigfhq{~|tonkei{{yzunzuu{~}||{ztjq~t}{y~ytztx{rgfjjhgfdbdlv}|uspil}}~{qzwty|{yxwuoeh}qx{v|~zuyz|ymjnolkjheflw~zqnlfet|yz~|ptysy}}||{zvmjwxy|{uuxt~~wkehjiggebbfq{xtrlis~}vs}svz{zywvskdrwrxw~}wtxsiknmkjigegp|}wpnjdj|{yzypyutz|||{zytkou|x|ztxuu}sgdhigffdbcivwsqkky}|ttzrvyzxxwvskgxusww}}xww~}pgilkjiigfit}vpmjfn{yz~xoxtsx{{{{zzvnstzvz{v{tr}}rebfgfeeedelzwsqmmz}{}vq}zqswxwwwwvqlyvp{xt{{yux~pefjjhhhhhku}{yqnmin~|xwz~{prtpuxyyz{||vvtvvv}z|uow~te_ceddeefhn|ytrpoz|zz~wpxypquvv~yxrnrlka[[[cfxwvz~|}x_eja[]W]^cqy~v|lythalysy~}swwy}znpwuxibwxlknhjiglvp{|q}}wlsrsovsc|~yfSpjcgx|yp{phrtkotyvjxhvvkoquls}}}{|~e\{wfbgUWXc]Vaoj~rxydYg~{swjffrmknmgY^bwmdhtpojywwlpxtoqtw~xw_\g{vg|~uilrnpmlyedeimqryswii~|~zyqkaz|usk]mhqostzs{yzwwwjejhws}wnj]ino~~rjqgkb^vqyqdnuzxzkrkhv|xz~{s]mjnws{mrqipv~vwqzovpryhbgXnztw~|~~~ntww}ln[ftdllu}u{|p{rhmjqxp~~z{snfcg`koutrvckf]hcenzzzsup~|{|zrnfS^`k~w{qz}kmmc~xnpnxrhqolh^}x{~pegZv}sp\mzwtquoqumknrrny}tswufZix~xw}ztpxhmnlniyzltpowokp~|}x~z}l`danryvtq~xp{~zue|nkunskgri}zuzrrxptjzxirdfoorw~}vxywrjtl_kkw~|}sdg___r~y~}jhh]wqdcexvp}j}|x{y|}{~qr]hmY]c|}qzzuxozlmqoonupnkxmx~~zu~{uqthnl|}tzsisuyq|tsuj}lheuoer|lljh`cw{vts{zig~z{zqwv{u|pjqq{~z~~xtiTlxu{uztxywtx}muvxlnthbh^u|}}|{xulpinrqpigpifqzvzjzvkhws|}uuers[VTly|txxlpnwjmotwu~x{wpnv|}~}vq{~u{jd`ehezyrw{qmpex~hearsgm|{u~~z~}qfnieowwvi|wwttyvjnhpiuy{}yv{ydhwruy|wqtqvxsmu|wzmd`njde~}}xv}{r{}~doqqlyyv{~yugnx|yutvpnlamtjhw~~^Wm_Z_w{o{|itqtoyo~{{syw}yxutyttggmb`im}npwwemgyilsvxkxvsn}~|wz{ujfefYVnz~yxssto}hjv~elktq|{z|z}tzptslrk[ptuw{y}ywwmajs_ezy}~}s{xw}vmfkgpq{jlzyrgypqpq{zzvq|v}qkjknU[env~xsr|v~uyz~umy`qkqgpz~z~yxzpked\fvz}|~{{{wv{}m|pqjwyyt}~xx{{vyrchdgd^ytv}{wp~kkjpz|{xo{uw{|}t{xq^\\dgz{y~}yv|vsfrqcnsxervv~{x{s}~}uzni]pnuks~}{z}xpmtposthlvzwxztzv}tn|heTdwq{x{t}tqpnx{rnkjjqy~~|vveed`inqux||sxrojv|}smldlm|s}wvwxtjmuoianr{}{{msi}oqz{htwwf|z~|}v|thaYoh]bw~}}mlmr}yttopp{mgs~x|{komkjrklhcr{tsqz|vppmajltwwpxtyorzvrclkor{orm{olyshxuas{uxgi`uh\\nnsutv|~zxvwomyifgp|z~}}yntwqnqonl^kvv|wuu{ztvphhkpv||{vwq}srzudfgdiqvy~~pwp~}nny|l~zcoy~w~njdvoYTekgzz}{~}}yqzvnutegf{pxz}vxyvpvsqqd_mmktwvpuxqlmnrvvz|{sw{}m{qhlcajkkx|ztuwntusrf|}zuv{wxphk}fY[odgx}}ww~trxlicjzmj|vx|vvupon`ekfk{z~wz~uwyxlmqmqov}|zy}}ymyxgjpZfihkz~}}krxswwst}vjxx}}|}wgk|}YXiv^bzr~u|xvr{ufmevkjqpj~}{s}|wss{ntnqjrd]q|znqx}lqssfjk{pv{gp{uaehdecno{tystquv}zt~mupsnrv~yplr{na]wbZimkj}w||sp{|p|}prbxo~x~zw~}|xlvi_i[Vxgbmx|uwpvtqpturo~qz{q}zwscz|lsf`eeptvyw{supjuqlfwpt{zv{pm{{ohnt^mqkgf~uwuuittold{wsyuyiksjmngnlnhTqyq}z~qnzplqdvwy||vzwlxfgnd]clknu|yt{x|zwst|whwx||xy{ortknc[xfSbewxy~svntx{lpspttz~zy}yylv{nbqga^\rmt{}|hxkngs|ltz{n}}wxtwkoklf\mfdxox{~}ztznpqoppyw~}wmytji~tn}{y_[{pprm}~|l~irpayriih|~{|z~xlvlohmdaaolawzn~sm{mnrrzpz|wwqwdnw]]Tckcr}x~||}urow{rfzvry~~~x~nt|ggdqd`eiqnv|jlsus|ug|{~~}}}otwydfi`ecSqxfnx~z~{syqxqpvrqp}vv{tlzty{fgxpnncjpkvx}qxqhqd^gytu}}~}uyqzqulnnok[inpiy~p~t|tnsotkxv~~}sxlqkbYepffstvdi{s{}tlxrejmp{zwzuonmom_hci`mnrzyytvhqyzyq|xu{rjuocbY]u`\\ou{y}qzvuoyyny~|||uxv~ftytum_kiafm{q~xynwohdswjoy}}yyy|ryssnvjdjjf`mpnsxyywuzy|}z||xvrjn{ynTl}pos~~xnhn~zlssyuj[iqiz}hfqzcmqzvyy|}{~tq{}~^lptcpusudytys`qrnv{vbswzs}pvlipmdyrznyr~~zwszo{qz}ypwknsxiyok~w|{{utz~rRes{eUgc`jXaWx^Q[hxo}x|trrr}n~qbi{sopwqnqxjpwxnga~vqh{naaho\eYurmrnmw~hxvl}qloznZsoVTdh|{|jiYh]a{}l~dau\V\hV]Visby|sw}xqzudxc`qovy{sztuz|ssemsh}dwogjmchdc{gqxmk|znmvew}rmuvb]wbWdje~rdYZgYt||~Udl`cf_Z_Wyhn{mpw}q{uueofrwnxx|o{vz~~xvgexgwlowkpnldcVyjctkhpyqwthf}yjrxtYwjX`mjuu|zg[[mYg{feo_ge`Y`Sri_xlkvytoy~frdozt|vulxq{tuwvhiwersr|qtmjghVwh\nehmvpuxjf}}qwvv`zi]cniq~u{}|kZ`lYhqjnvhjb`[^StcYjeqw~{sr}~kskmvstuslvpvuvyaesco{}wwznlefZzpXjahmuuwtoc}uyy~u]ujaeonf}zwz}|{~s\[gXcq~{oynuif`aTmpTf_js|}ik}xnsu{j}rpzmuq~z{}~japfhx}}~vqoj_hZcbclry{x|z[}x{}t{udhqbihwdvz}|{{ysy}hWa^Ylsy}wttjld_[{bYdbqr|nx~{xm}orn~wvxz~mwwqtt|swx_gnes~~svpjavvTd^int{zvt]~{}{yuuqvapngjjyez~w{ypnu{bU`\bry}w{sklb_a~`Yaett}j|ytmzpvnwzvy{nxyuxuyxlpu[cjhxvyqidxzVa^jos|{u~s_{|wzvulr`knjlkj{y{y~ielzcS_`gv~x{xmqeab}kWbdsr}oryyvivrxruv}qr~yv}~ryegu|`_ijw~{yvhgoa\aiqozx{x\zxsywykqddliojt}z}}}sg^cufR]bkx|v|{ouicg|tXaftryqm}wuvhssxtyxs|q~zu|m|u`drz``jo|}ztfitc[amqnyz{u^{xpvxxjpeeklqou}yz}xp}b^dtbT`epzzv|rtfdnpZalws{ju}|uvsjsszt~~y||pyy{xyi~kaevr[cjv~~|{pfp~|_]fsnq~y{id|yow|sknbhjrq|s{zjvo\_krYZajruyvnajw~e]guttthv}~tymoqwx}vz|urq{yu}msu`blyb^fox}}wgn{l\drpnz{xn_|{ps|xkqgehnrtrw{|pnu^^dt`W]enzx~ytdix~o]dttq}uhxxuyqnpuxzv}}xosxzu|pnvabjye]clt~zip~q]fuqny|wl`{{~pq}ynsjfhmpsov{{pot_`gt^VZdiux}uen}r^hwrq|}rixxvzrmntwyt}zovv}t~{rluabiyg]_hpx~mqyaevrov~vlaw}}rl||psmfglqrp{t~|xjwb`dtgWX_ik~}zkj{dewtqx|shx}ry{wnmqwwx{}}nz|y~~x{yx}gwgadtq]^apmvlobtupqxqck{zkrurrifgpp|ypyz{vlrm^`js]YYgfzzxhx~wapyrs}ylkwsx{snluv}s}ynyvs|xysir`afvi]]gossqkh{sqswmcqxxixrspieiprvwv||}s}hqf]_kp\Z[kgzwh~~xduyss~wjnvpxzsmkuww~yn{vr~{}tuqgn^`duj^^irvtqoi|srrwkbotxhsssqjeiquyywy~x|oyhlf[]gra\]nkxzlx~~it|styxkk~{{jwztojrw{}}mzyztwu|zltem_]`prcbhxtznzi{urq}ynbktwlhuqrmggqv{{u{u||nskggZ\dsg`_op}x{ov~ot~suvxkjyw|i~wyvqjrx}}nx{xtv~tz~wiqek^]apsgel{v{p}n~usq}ymblqvlgurrnghry~yv{uy{kqhhcY]fsgbbtp{|o}p{{tuyvhm}}wzgwyuojuz~zn}x{oyxvv}nklfh[^dupgetx~yxxvrtqugermycrrtqnelt|t}{wxw{|uingi\[_noeajus~ys~vuws{nhuq~mqxywtknw~quuuq~{r~yvtgmgi^\`ntjflytuvtup~ylco~lwkgutsqghq{|wxz|tv~wwimii_Z]jqgbfsn~txvws}pgrq|sj{yxvllu~~tuuvo{~zr{ztthkij_\_lsjdiupy{uwq~yldo~lxmfuusqfgozzxzyzu~v}vujkkk^[^jpf`eokx~vys|ohsp|sj{zywkks~|vvuwox{yrzyrsihkl`\_jrjdgrm|wxs{|odn{nttbxutsiemwx~{x|~s|}u|~tumikmb\^gpjbcng|yyuzthq|vu}fzzxpiqy{{t|{nuyzsv|rpngine\^eqngdpl{}wxsvgju|j}druvtpdjq{{wztv{}wv~wqrjgoj]]`myxx|wyzqemptltk|pjz||{rt~yllkdkp~xt|}w~|pomenr`UOezgfoi}g]}bMVcueaoVosostogp{|zsu}uyw~~pSFSz{b^T]{lwtfmspkul~po~x{xkgxycbptpucr~t{khnrqowvllm`ffRNXoxuwpv^cnfcfq`XhPenjmniaiuw}xmpzz~{|q_eUFNc~{mjws|{zw||rhvw|szs}}o}hfo{qytylj~sxndjlmjptjcj[ZgXU_r}vrwqdkzwrt{s^o]armpqoeisz}x||nr||~~mlgP\YMXkyrw|{zvy~tkyz|ryuzo|v|hhs~t{x{ql~tzoejjkipsh`kZXi\[fwxnvsfq~wsu{rap_dspstpgkvy~vyznt~|||}xfiaK^ZS`tyzzz|zs{qo||zt{t{u~xytvfkw|xw}os{tzjghiikqpbbkS`j\am~{tnynfy~wxz~lio_orsuumho}vu|vsov~{}|{y~|nblTQdVYh~w{}{|}~s|lwzwz~wu}}yovlem{}~{zozszqfigihori^n^Vob^fvymxucox}~xjtflutwwrhlxuysu|xmpx}z}zzx}~tbn`Nf\V`s|~w}~}skrv{|}yy~|~vpsncfq{|ryszsfigghorj^ocVqe]cqzozwakpz~qwkovvxxrilxswrtzwllqwz|yxx}~scsaRk\T[lv{|ujppv|{|}urrpcbirxyt{ugifggorl_qhVrj^alz{rz|cgkqxzsrwxzzukku~s}znwvznjmqy{wwv{}vdpmPieVXdvw}}z~x~nnpo~}{pxnrf`cizwy|kjhffmrpagtVisb`fssrcisocaiqxttww|zssynu{|yvy|o`qojQWxniinqwxzrp~~|vvwpv~paghohffg}y}vu}{~rslf`gwwzcsquy~twwus}ggf_nupghpw{xwxz}unotmzxuudVWk``qa[[agnprfjz{yxsy}|qeghqwvib__v|qwonvxwuvig^Z^n|ymzx{||fp{tnj_krneekquuqrwwz~}xlfjut|ysg\RR_vzpthegkqstig{wu}yun}{~~qb]gsyzniduuyuouxxqzxge^Z_o~yn~z{zuvdlssektsjgkqvxtpzyy}}zogjwy|x~~{n`YQTczn}ojkntxynh}vtx~|v}um~z{ob^jw~|pievvzvpuyxnxvfc][arzp~~~~trsbovtfluskimrvzso|yx||xnhlyz|xxx{g\VQWi~~s~rnoqw{{lm|vrw{xu}po{}}vj`apx{oii}~v|urywqmzoda[\fwzq~{}xpulbx}{phrvrklptx{nswy|{tkiry{{yqzr`ZSR\qv}tqrv|~wiz~xxyqwyt{yjyy~}ype`grwwoiwx|zsv{{xjuvfc]Z_n|{w}xzxqsr`mykqwunlosv~rmyw{|wnimut|xqwwd]VPUgzzutv|zju}zs}ptwty{irz{|~{shackm~tmyz}|uw}y|itwhe_Y[hu|z||xyysvs_hw~otywpnpswrmzw|}xpjlpm|ypzwf`WPR`q{~zxzyix|xr|nrvt|{hq|{z}}ujbbfd}zr}|wxx~iswjfaZZdpvw|xvwswvaerytvzyspqsvvi~uz}zrlkniz~zqvzjb[QPZhsy}|z{uuqroy}}uqqutqhns}kipl_ioYZh`]\XXanwy}z~xlmlmte_owvfmk]^iq^k|pqvwtomplq|vq|{lyqeb\X^mzqipnhb_Zig[_dgtnamv|~mqz~{toqqrkip{sprg_oiT`f\\YTV`krs{uy}xrut{lp~g_lzorkSgqdhmmifdfaoywz}sin{sumjdbjyzliwsg^idVY]coe[hqw~tkw}zvxvyyf]n}wiulU`cZYVQS^ipq}|wwzruu~psvcfw}`mzjjnnjgfgbo{uxvzqho|o|umkfdkyslp~ugmm][_dpk]isyxnxt~zxxyx|yse_pptw^^g^\ZVV_krtzyzyz~oprytq~{wii{fh}pjmomjhjfmt|zpvqjonxvmlhflytpvyilqa[_dqn`ju{||zpzr~~|yzzyvrpeburtx`_g_][XXalrsz~x|{|y}nnqztswyxkofjrknpnlkkfp|s{vmtpkt}nxsmliho{}~ut{yjoqa\`htlbnw}w{wr~~t|}~|z{x{ooldhsyt_fg__\Y\fnrt}{x}{~{zwlotqyzu{tkv}dxpmpqomlifyur~nmsnm~sswomlhir}|{uwtlum__dqtdgsx~zu~qvr|}{zwsuloifu~ypifjkfinkjkwwrsx|qouyy{}R[skgknmmmjesvnupgonlyv}vttsszxsvnf[ddVT[jsghswz||{umxu`a|u}pbQU}tltgajffgdbgmpouwrmgq||{ugtqacksz}||cmwosvvusmdpwdYcxzyzrz{wrhan{kacn|{xlttdafsvd`efkvtsqyth^]|s_`\V_kky|tl~m]WWWW^bbhir{ywzyzvdlsr}ykyrowybva^]Xeuj`erlakqpxwj~volovzz~||zYVxbRY_dikkr}pap|qkqy}suyowtvpsyw|q\m{eUORVX]cbhkmy{wv|wosjetvzx{z~uyxkv~lYTdcUTai[Yegjwyxxxmuvrtxz}nhd[ixomhggffpta]qsfflt|hn|{|t}v}s{~sohj{{ngeb`ddajimwvst{zjhqkjx~w|uaszvkjuk\[fj[[egmywxwsvr{xtwy{~qaeiep}{vsqnmvwb_tthhnv|enzszu{~qy~mnms~smkhfjhenlnyyvw~~xifqomy|t~q^p~nlxo`]fm_Zejo{yxxpvv~xtvxz~~~p_coov|{wtspntzi]pxmkpv|gg}s~~uwrt~mlt{unkigjkdmqlv|zz{vkcowsy{wxs\lqjwsc^dneZdkq||xyov{~xttwy|}}zo`ctw{||xutros|m^p{qmqv|~~ge}r}}tusr}}mmxuoljimlensnw~||ytjcpyvz{vyo]oqnzsd`gpe\enu|ywov~}wtuwy}z~~uk_gy{~zxvtqw|i`uzpnrw|~|~yckxsyrvptykp}vqommqjgusp{}}|}vqfeuyv~}r~eb{~ou|ncbloa^jr|x}qoy|vuvwz~xyqe`o{{|zxtu}vbj|voptx}~||lcw~s{~ss|vozpju~xtrqtqgrxqw~}{wsjbmwtx}uyn`qvxvhdksh^gqyxxuls~~yvvwyw|uj_hvw~}xw}{fh{xporvz}|}pcqu{trzynwshp~~zwuxtjs{rv}yxulbitptvxobo}{mhovj_hr{uwujp{}y{zxxx{v~xl`frqx}z}ij|ypopty{{}qep~v|urzznvvhlz|{}ymuvv~~xyvocfqnoyv}~tdl~slpxqbfr|wtykkx|ww}|zyy{v}zqcbnpq}qhx|soorvz{|wgk|}}zzxrvqq}}lgt~|~sq}u}yy{wrgalpmvs~}~~jft|rovzlcny~tzqgp|zux}}{zyz~{v{uk`fqpwmo}zspqtxz{~ofp|xury{ntvgiz|rx|x}w}xupdcoqo|~t}|}wggx{rqyyjep}|t|}nfszuy}}{zyzyvwrh_gst|nq}ztrruxzz~~mgpw~try{nstei}ryx}w~wtoebots}~t}{|uggw}sqx{oeo}~u{~peqxz}}{yyz~zt}~uqj`fv{sp|}wtsuxzz|ogmw}uruqo|~wig|tty}wzxsphalxx~u{}{z|~}{~vhgttqv|tgm|vzsfo{z}}zyxy}{r{|sokbewvq{~xutuwyy{~phmw|uqt}qn{|wihtuz}~wzwrphbmz{~v{}zyz{zz}thiw~usx}thov{}qfs{z}|zyxy~xr|yxpnibi{ut||xutuxyy|zmiq~w|sqv~|no||}sfms|{zv}tqnedr||}w~~{xxyzyz~{mgm~{tu|}pjv{wyjg}yz|{zyy|sxw}~rolebp~uy~{wutwxy{rjlyx|wrs{ulwz~wje{xx~~}v{vrpibky{~x{{xwxyyy}}pgiuyw|wksw}|ndtxx{|{zz|uuw{tomf`izyz~}yvuwyz|tkktz}ysszwmuy~wiat{z~}v|xsqiahuy~}y{~{xvwyyz~}oghr|{ynux{lbs}tuy{|||uw}v|tpmf^eu|}|~zwvwyz}tlksz~zttzyntx~xi_o{~}~w|ytrkadrx{~||z}~{xvvxxz~~qhhn~~rty~~pblsrvz{||xt~~vywpnh^_oy~}ywwz{}ynlo|}||~vtw~qoy{{o`e}zy}yx}vsoe`kwzz~{{}|yvuwxy|wkgit{r|}{xhcwyqswz||~tz~yt~|rnld\dt||ywxz{smmr}z{uuzzosw}}wi^lyzw|zusncboy}}z{{|{xuuvwx}}sihkwzs||vfe{zrtwz|}~}s|}vs~ypmkc]fw}yxyz|rmnszzuuzzos}w|~|vi_m||w|ztrndbo|~z|yzzwtstuw{|riikw}t}}|xidx~uuxz||}~rx{vqzzplkf_dw~~}zxxz{~tnor~z~{usv}}ro}vx~~}{wnbi}xy|trphbl|{~~~}xwxvsqrstx{tjjmuuz{ylevyvxz{|}ruz~upxyolkhaew~~~{xxyz}tops~z~{tsu{|rn{~tw|~}}|{wnck}wy{trpicm~{}}~wvwurpqrtxzriknxv|~|xjgzyvxz||}{qwy}|royvmkkgah|}~}zxxy{|qoqu}yxssv|zpp~ysx|}||{ytjcr}v~xsrofes}{~|~{uuvsqpqruzwmilp}~x|}rfm~wvx{|}ur|yvns~{qkkjcap}~}zyz{~uoqrzy~}vsuz}vox}tvz|||{zvmchx{{usqicl~~y{}vsutqpqrtyyohknvz~~uhh{wuwz|}xr|x}xor}|rlkid_i}~sprqpqruz~}tjjhit}yvw|}whwhfmuupy|~}wusnhmuw{|xx}{nnisuplkmrwtnw}wlkuwouzv|wtosxsrrqompwxqpuwwxz|xssqpuvv|z{~|uuqgmxrrofbkx|uou|~zux|wurkaajsy~tpnn{sjffinnhlvxtv{~{sgahpppy|xi`ckr|ysnmrunjmpqrsux|}vomjgkruzqdbhjkwynnv|xpouywssz{vsplc^dow{~vogdiouxsqstplrwutw{}~~}{vmdclqpt~}vi^\eowyttxvnlprssuwz|zsomjhmsv|vh_bhkq~zstz}wprwyvsv|~yuspkc`gry|zsldcjqyytstuqnsvuux{|}|{ytkdelrtyyqf\\gs|yuuyvonqsttuwy{xromihmtz}sf^aipx{uv{}xrrvxvtv{|xtrojcaht|voibbju{vtuvsprvuuxz{{{zwsledlty{und\\gwzvuxxroqstuuvxyxsnliglv~xoe]_jv}vux{ztruxwuuy{xtrokfbhu|slgabky|wuuwvrsuvuwyyyywuqlfelv~xrkc]^j{{wvyytprtuuvvwxwrnljhmx{tlc^amz~xvy|zusuwwuuxywsqnkfdkx~vnidaco}~ywwxwttvvvxyyyxvtpkfgoy{uohb]ao{xwzysqsuuuvvwwtpmkiip{|voha_eq~~zy{|yutvxwuwyxurpmjfgo|~zrlgcags{yyzwuuvvwxyyxwurnigjr{|vqke__gu|z{{xssuvvvvwwurnmkilt}|wqkd`aju~|}~|xuvxwvvxxvsqolhfku~{tnieaclx|{{ywvvvwxxxxwusplhinu|}wrmgb_cmy~}~zutuvwwwwwvspnljjpx}ytmgbaenx~zwwxxwwxxvtrpmjgjqy~{vpkgbbgpz~|ywwwxyyxxwuspliilqw}}xtojd`airz}xvwwxwwwxwtqpmkjnsy}ytoicaciqz|yxyxwwxxwusqokiinty~|wrmidbdks||yyxyyyyxwvtrnkjkosx~zvqlgbaelt}{xxyyxxxxwusqoljlpuz}zvqkfbbfls}|zyyyxxyxvtsqnkilqvz|xsnjfcbgnv|zzyzzyyxwusqnkjlpuz~{wsnjeabhov{yyyyxxxwvtrpnlkmrw|}zwsnidbchnw|{zzyyyywvtrpmkjmsw}~|yuqmiebcipy}{zzzzyyxvuspmkknrw}~{xtplgcacjrz|zzzyyxxwvsqpnlknsy}{xtplgdbdjr{}{{zzyyxwusqomkkotz~|zwsnjgdbdkt}}{zzzzyxwvtrpmllotz~}{yuqmjfcbelu~~{zzzyxxwusqonllou|}{xuqmifccelu~{{{zyyxwusqomllpv|}|ywtplhfdcfmw|{zzzyxvusqomlmpv|}{ywsolheccgnx~|{{zyxwvusqonmmqw~~}{yvsokhecdhox|{{zyxwvtrpomlmqx~~~}|zwtqnjgeddhpz}{{zzywvtsqommnrx~}{ywtqmjgdcdiq{~|{{zyxwvtrponmosy~}|{yvspmifdcejr|}|{zyywvtrpommotz~}|zxusolhfddfks}}||{zywvtrpnmnptz~}{zxurnkheddflu~~}|{zyxwusqpnnnpu{~}|{ywtqnkheddgmu~}|{zxwusqpnmnqv|~}|zxvtqmjgeddhnw}}|{zxwusqpnnorw|~}{zxusolifdcdipx~}|{yxvusqonnorx}~}|zxurolifddeipx~}|{zxvtsqonnpsx}~}|{ywtqnkhfddfjqz~}|{yxvtrponnpty~~}|zxvspmjheddfkrz}|{yxvtrqonnquy~~}|zxvspmjgeddgls{}|{ywvtrponoquz~|{ywuroligeddhmt|~|{ywvtrponorvz~|{ywurolifddehnu}~|{ywutrponprv{~}|zxvtqnkifddeinv~~|{ywusrponpsw{~}|zxuspmjhfddfjpw~|{ywusrpoopsw|~}{yxuspmjhfddfjpw}|zywusqpooqtx}}|{ywuromjgeddfkqx~|zxwusqpooqtx}}|zxvtroligeddglry~|zywusqpooquy~}{zxvsqnkigedeglsz}|zxvusqpopruz~~}{zxvsqnkifedehms{~|zxvtsqpoprvz~|{ywurpmjhfedehnt|}|zxvtrqppprv{~|zxvtromjhfedfinu}}|zxvtsqppqsw{}{zxvtqoljgfeefiov~}|zxvtrqppqsw|}{ywusqnligeeegjpw}{zxvtrqppqtx|}{ywuspnkigeeegkqx}{zxvtrqpprtx}|zxwurpmkhgeeegkqx}{ywutrqpqruy}~|zxvtromjhfeefhlry}{ywutrqpqruy~~|zxvtqoljhfeefhmsz}{ywusrqqqsvz~}{ywusqnljgfeefimt{||x{xs}x}vrnnfb`\[\U\\ZV]^V^bgitzwsustsxmmkh_\[]gtyyootqz|}yxqfa\Yestpjaljor~nmjlkptsx{{}~zvtgYWX^YXKOZ^ZRU\l{dYlyVRPTXafiv~wwkpwpj\`bcemx}wop~sfUTdnk\\huy~r|^Kn~m\R\^adnss}|iq~rut{ukdfpvwpkq~o|ttfM}nz{yooli{ua[k|lmqaYV\GHRAT^aVHAIZasxons~|vz|dz~syxt|d_etrMwkXwq}y|jlnwpwdpiptsf\X^vUyykwwrkxhTonmeHXZbeUTdĘpZl`Boi=A@}sawny|zj~wynnzvbuu[pdTU,EmTjsqo^|lfbcxg\jej@?b^`zxY\ZNTum]ytuy{`eV_cXwiu|xdyjzav~skZs}rлǦplpNM3@G9Nzv[\Qfycj}o?`d\YQ:a^Vkttfzow{qopdsyrldpZ:ee@]ukj~un|{xuovkg_}v[h{jqH,Znx֪z|qyZ_pdiUegKWbdW7QKAUn`qc]Lpwylm~iP`X^IFev|vkszrtseqtDz}}s|eQSL^TD_yʽvf]QnnSbtvb=apZ]]XNqYTcx~{zs~vvp}lu]R\C6_U9JTƳlv~|z|n}~shdv|``c^EFHU|ΰsxua_rihlV^wmxcOVfaMdocr}|swspcrxuwrit~iQefUTR9nr̡|ykjwz|xkkcs__kNE&$]b|yhyhxsiZlyZtc_naZOim^xk\ys\lzieqX~xemagdXBHibwl^kdm~o\}|itwgWGg72AD-^xqèrn}xqyr{xjlyhUlR?A0tbeugf`tc}}}ibtoenlWZhapvyqjl|cm{ng|hxup\yb\vbW(QQM`R{~{zon}SdLBTF6QRJ|bjt~zw`ywo~|nlr\OhDZ`Wltxm~~}rl}w|reieqUHZJSHNGdŵzlwGVTRnLpnjF?mUlgbnq|yv|~msYh6^m<;=kZ~vsoqrmm~ztkVJVRGPYT^ʴ{vo`gs~oJxnfhH`]\_`X[{}tf~Wg`[MnVXqcV3flrm\dfc\n|vx~maf[JR\HUsb֫d{ct{run`L\a[`mfjm{b^naYxpg{Olu_hFR^Xdltsv}pak`zzf[u~wzo\pKC; _N>Foǟ¥ekuxq~vkmr\_me\~qmx^mcqcg`yqb~srjcFHSXWo{aqhqmvbyb|cmFj|K;9V4B^^TJq|zepqKU7O{VSZw|a}gy}|m\krnnwh`g`Qbsoǟzxi{uxv]hu}zsbeYqtTqug?;\KQBAkt{ybcH{ySESgD_uvc]shfs|x}}sjskXf]=G4fuwyuvkx}x{ur|r~XmnnNQ`QPMA1G`wʳ}qjg\yuXw~qMMpdi\YrwyyyqoUS=mlD6HaFgzs{r~ah\khuutnXBIOMMYRWmuuɩġ|q`j{za[sffgaN\q}{t`[T^QhmI]laB;gmvzv~heSxiYgr~~s`gqoovidi\GOOEWl\dlǶzq~{qTV^ebrrskf[j]s]n{^LoneY@X``lwp̝{xluxebtq\b[pljƕqo{n_yxdlWVE8_h\nq{zrj|Zsxl`_b}q{skiltiZUTqrFKL9DWpv~ռu}nre\|phvnzqlqtiTMSY@he'9B>iP]ɱkeYHLpj_~xns~vwu}]wd_o^vpij_YwtTaoKoɔyqkeuyhgnusnpT^pzpmdd{`ThR@O?Pd<;acj~{erkcsqbSJhpiqo{~|onhZc\RgVGDVYWw~urn]tqu|qs_ZejRYYV3Wk/Dpb`wrƮ}odw}lWvDejkuyjpszk}|ahcat]=JK@OHOi|Ųxnbtm]{cpt~{acUSuT\zcP:KPHcilgy̥pjqwm~gnkp{q~}|mrxlRvc=STM`3BUZKFKjؿijskdWoZ[Wp}mxeq{yo~~fS]|nUnZRucZizkzx}pbsn{vMSjqwzudx}i_WVkfEIL507+[]^ï˔wkjuNc]Oy|ttnwcy_yoYPVqSVVSFhdi|xw{~{{z~wqnvplkpn\f|J7OMfYD^^V43?r|¿ε|{fr~WfoQKJqhkn}yme}iXdA]tdVfgRZ_swtu^ot}Ym}vxipzn{mnjkygJOXHCC=aIPulq~Ǔlekk~dWuuvvv^vi_|kQJNfIMILJ\kINqxni}qto\kaxlmmm{|qihcM_fyjC\`aC?;Yvaeȱheva`^|jx{~t{fYerVgLKd_V_veb[b}}r{~]Wlr[[n|wfiyzhukiw}f`\T9;)FQ^g[vxO6elpgn[^si|ytZxKAX^P{Uxxjj~~wUVdOywTBx~i[`Xr`wzybWWvufrīr|{ˠ~hݧ]h"!=3.vmݤǠaiYmsMn[[-BKJ3[YHevQsdZ>PYwq6G|ojhZbhx|nUa5OsUPrZgnm|h}[VJcdIDmyY_Yn[kjcOvivŠw}nbtssʾҏaix+"GX-WiܙzxeUN}Sdj_2?\T=:kC^|`acbOQ".D<{}yhNkftw`n}pcnrz^ez~crqi{[|v}RT:IBKU\3.CTQЭrvs\~p`]IBSF\kpMx|T1ctocxzxpfqv{ehusrj\vk_up\UhTAMX$*>8q̈zpzReer~~bkxpcxe~a`uqƎekqdramtUtk6H@JR_>,;NL|jöҽw_vvf\^CPOMvqZ_m3Ruuiypcfyxqfrzlg\gvf{hlJbWF@rr4$68Qئufm]jm{idryo|pZ}rZjiscnd|k|WtoPH;@ALYV12DPXuϪryaymcjSITHjluMyS7dyypx}znprxkjvvvk\uizZrk\I]L?J}_,);8n|Γz~qbeir~dgutxhZ~g[lhlcl_cmWvsbQ|@==BKZQ06KTdqͦtydwkcqTJSHtjuKzO8f{}xy|stsxmkvvzi]thwVnfZBSD;Hzd2->>v}̓znjfjsfertvj{X~hZjj|p`g]cpPxvieDvG5:=HVX87K\fЬwbtlelbMTMfnwSd^7[{}kq{~virysk_~eoWs{Zd>HB:=lwE1?GeҨwgbmo}sbkun__oyvYcwt{}bcalqgOd^k=d[099FR^E;LchĖżѸhoogiqQRQ[wt`Sk;S{~ns}}}ioyrciuel{YiVc<@@8@Ubqápsp{h_boxuLsISx{prozzfrpbfpwOnyQe@656FjzZ@DPr{ʩnvwwxbbmnx]^f~lT]mb~uqpseabtfRfCVs~aVa0\[9:=LX^J>H]mҳxtquedfsaWcGamorzuve~q_gs{s]vlP5$1SvhV{iOMgsnvi5ZGnV8K+BcuwZINii~qgkSnjwju^Ko}pṵµ}~Y$LCjL5R/Dd}|`T]fq}o~iwVozZKiwTq]Gg}ǓGd[~\MqM\xg\]adzx{\ihL*Wsh~Y{gNjӬ~Lb]~_T]ktiimmx|pH>m}Ua^s6WP2Hf|vw|tƟ|{Zcd{eUjj|_SPXTvT[]M!Nyf}aXf~̤}yqsrZVRG>vnxtNZe2UhzlyS`uĢɹyp`i|zLWd^7Yp`pk9EXueijgQYoicmshpy`i{qppvaUngtyIAQgi}ZivgAbg|dxQb{uĚȶg\p{{kQbnWh۫hreLCeVJPahXZ|~jmne^coT_dblcCMeS[}pa~^{cqsA=tgsztowawy~c|ox{ʼa`n[Q)GkdsȽw~jZuUGmg1UTIbRsUkzdb,{~XzquQYcg{rp|xsxnaaeIOR[lơgoZepslo{uh͖ƝtMM>1^<7ȥ͏wuamgSr\Qg[dupoe~iWKPtbBgjqnlqWbd;eed|uqyYefYGbkxxholu}}nvmljyɥǧqqaQBKMEnf^p~Z[qb=ipIUHU}mnimnK`kajnzp|\_TdnktrhqIPp>Nwil_yujl~}vsxts]G4ORCPoȢkt^danaKdwef\v{iUJea[[LwfL}myn:Pe_f^popnjQWsBcwmw{rdo|sim`dtūkm\EE_I;TtknuMTZCXFZsen|qd>eO\z][zuZdYY||z|wJ_dTIT][uuo}~yvz”{tvYUdJEnYZ^ğ~w|eouU]ondWyona`jURj^SLbggmoJomIYVio}jQdjXNS{pw~x}}~{zemzYdzy|kNcOT@9mhiȫrqT}\GDcy`\yGPzqo\WU]or~zbLbntrg}r|mjpVLeA*zv]fqyolu{n`NgrgK3WŦsc|j_qbuhogt]RaarBDlO@imd|MLDeqiyijtZm[J\U`~p~msvqojtytwcXPW\;Jae˫qik[R{x_jjQqwm{m>JqH`tg]h^peyywmTSYHQbYJLylwt{}zeQC740Hg\(Omdwkg|k{scko`^yhC_s|z3iďC?[g{{w{vnfRxuvvnVWih||cP[w~n`Ukb@qkxƽ+k]=EI]eclz|pYyxXpɶoqwoty`ktdodLZp].ysvkplivxXun^kavoVXYsjq{_qxmrűweL&rXTFDTcjc>OULmRNai\EHBfmr{~\msiay\wsm_X^Aoo}acm_tchuueRXKqzĞ|h_M@SB~`Zb`\_A6nJ6FIOYPwsD7A-Klm{{\j{}mlyoicg_NbQ{z|r[y]lw|ti\jUhƱĨ}YTVWC]V|pwg]\Z7d\6DY]cN_tC7E66hqz{c_xi~uj}]_emTc`y{ytr|`_i}ocpj[ǟ}QOTaP]eʵnyg\U_?Uk=A`hkXN|F7EE2ax{||mWyerzkYZao^fi«zy}wrbygjun_mn]ʠxKJP_Ucmͷnzj`WdATmBEdmmXIzC5CF5_{~}nYydp{k|{SV^mbkoī{zytdxinql^lo_ȡpFFL\Vkrжp{oe^j@ZmBKemkTJt>2BB7d|~i[wcv{m~y|sOU\i`spŧ{|{^iuwwb\njcëÿxbAFNZWqrͱvsifm?jeETnqjLSd74E>=j}~cep`}usz{vxeMU]ebvq|_j|m\]m`nɳx{tzP@FPU^rwí}wit^GyTK[pj_AlM18C4Ms}{Z}ij|jvswz~TPW`Ϯ|x{|iuwuphqrtxaX\pi|oJBfSaZvg^_lnS-*D3>:3R[UXi^ewtVUvfsENxx-:O`_iV{vz{jjufp`^xp^cA@UdejiccTgw}jT=pM>GNEavx{ɞeMNL_?55CH.@Ucal[l}rq|zy|higeqpri~μ~lgX_^p_K`ovr{h{uutazotym`UB1_ʯnIFU;=5CI_F@Vbca_L{xWNOaytzyyjsuym~|mlxսntpdݺpchj5+CKZk{}{y>=djoUV@JdmuZ`k}ž_.EV1:AFaryWpt|:pqxhetxyqc[Z[ixa?N`ydfW{TWkwyrcS2XontzNPw~kîȤ[X2(SeaCB(BON4Ep}yvg#WaC_vNj~kirbhlé{WMdp`hQILcqnqkv|{mn}[rĦ|omudCNZouCKZIu[|u^butypQSgfai\gjbvrXjzdokeuRS6Ajo~VrykvaròzKgsx\/DHWhܰiEoxF8"lp`jsvwODu\N^yhYqm|}sW]uaKhy]J8J|ze|pgoZslsp[r|m}rpmboӿv[G@AJkΝbioRWYZtu\dn\MYR\aYgO9ERYqaT`ybdt}~u}mkuuYmzTgs|z|jZqyLTyi~ufea>Ou`ٯyU;k{RXj~^jS~sjzu}c4=_KRyqZcI|lpgPQxsidacCYKdg[so{fbxtX}w}clԴwlXSy\_CFVlêydk_cS^VZGSbeeF7U{yf^aRQO^tbzmcl{r^Zuh}pYbpfwYYOd}M{nort{zpS"(ڦ{plmr}pLDggYgZcQXrA`_AJedpk]Z^^y_Y~qlNq{Y6Bik]vzvsxwkY|ou}~rxxfWVQAADnIWyi`Y^VWsoYbrkkwa;=]{}{V_Qnx{}zbiDKmo~pwqvzKpjh~Zq|yvpyƤzk\O|F4Rmp~lthh{WohQO[p}wXei^UDDg9Hxko~oV?mhQjwb`qhVU]SPűy{\yuyw\[}rzhG'@XTZSlthRRukbdrlTcEm_Vlfs||f`e}{t{Srngx}yicsfOax}qiwjSviby|wgq{wsvymquO6<_s~qz{xdftvvmaTWqoxkvxq\Qfl^UZeuzhd{uyeNVm~ptZh}~|~{Nr{d^h^G8=]iC7P\an`goveW]>ZeamutzzxtuxqerzutzrXU\d_Vl\xunt[jvpqiymgsbctyzt\USVUOOUAH{rxwckjgWO`pvoYW^aizxblwccwxvwlfjH[qhkl{^x||vvrwyyfhcY`ZTRc}a\MLon\inlʽwr|jpywm[bvVtjzvyrevtvu`ryY[e]M7>un`izwvTj~nwsx{vXowri`[diWD7Cftv~wxphu~xpYor~o`o~z|cyvvyw}|i[[^]JOmzYlvxovt]Hbspwplsvd[mzhdegK?KJup~~tt~wTIeokt{kp}xxbl}}eSkyg`ZUQk|`b@9pf|olcj|yjscncMteOX=94.=GWnr{g]{dY||y|{xp|zimjqpggca`f^S\dizp`tpmf`nrk^|}s|jqao_[ZTZa[J6;^jgt{tkwxVGZfUPiof_\_rlQ{nXYu}q`dxltiqz}}}|tosn{f}umaUV[`x}aM@=R_\^fzztvxhowv|wvuywlYdxuorsUNoYMTPAFk|vs{|v{}ic^WTa{tub^obk}jjx^[^\\VUegesxa{\lweekmq{zkabmxt`v``xthVB7HX_vȭ_ji_cy~~gavo\aid`X_x~kXHC96`~zm|uqqfoxu{qeyzdavsdXU[WVYP;AaeypxylYUqrpqp~|pntohnoiiiiO^zWOmtvwypjtwcQQj}yxtkhotw~f_`>VpZOIGa}~qOTvonrkebo~wqbPT|y{rVm|{|wkQ:Y}~wcl~j~yqqxvzuYgsgtdRKEA7@]meK;Qei}et]d}~~ytzwsytrbm~|ufXZcbcZQWsy~rep~g}jbjrv~ge|hX[lrnfSLNJKPWU2O{}tsqiacnnkpgdkernOX|sZfZGjf\s{~rqrtkgyyemg_]Y`dIMTR`xfSY<@]ig_kxlkĹȤzrvxssp]ixxoto_ZrwpnXJGJEKZjsmpz}twy}]\wy|ohlim|iBd{delfcgc^TJLIPo}vl~|{kugdj\]tr}oe_Xdzy}vz|ubWSjtudkuwssnndgs}âwdlz{mbxpaiz{q_XC=Nhxt[1@jkeigyzpPctB(.>[civnĢjfbE?`lmqdd[]bha\a~q}qokYQc{emq`t{f^Lv~wmxorvlͪ^BFhnjH.6D;-7GIe~zemwu{]f|aW^zn{tbsy~tbZiʼnY[fjmo{uqmww~s{jecOa}dXd{}þɽôgRLcntlNCodAH^c[8Jsxji[r^Kjlh~{bmmrgZ]uv˽vlp|`W[k~||cūyaţtwlmb;3pmpħsvpjpkXdaJUhmXH@{[^Svksp_kewhu|~u|osfX`Dphcur~u~~|S{soYU~hxzwuqANq?KUey{{Vg^hOBBH_||ZXQU\basEc|h}hKw{cbt}mRnsrZ`gvkS^Ųlwo}}rz~mjt~{K":leMGp|un^D^uTN\m{LT@ktK^gx}~qbg|oz_~pxge`tlI\d_bnzjeU}^kz{p[zsΦ}}xof`a]K\K_|{uuuqDLzZp_qzl`K1^hQ\w}\izowprvaWtm_m^]_Gd_y_Olffngmy~}eךtocUYqsvrKazRL\nϹqwvuX4+-KTV[RR\bt|aGklxydmlp~hYwowlQ;hkX^h{ID_|ó͕cpmY^YThry}zr`nf^Ωwt~qQgyfM3Fb5Dhrʹٸ̰vVbaZW`ʽuqlgfYOI_}nbruiqy~|rfu]QofMXfirJcmFK;h}åzks|~tO[`eykxkfXXScSV}\<:]blRL~ۥl~}}cZzOJV~iimgqhiL\wh7%(Azrbiu~n_rghQZyz]rwqwhVq`_d_zjRCC6<`ʫzktuyqmWAbl\mv}xn|rTph[ew~hOogCHNgu}lsp^~K>Mf~sp`mDBrTLX[H-e]\JVĶެkwq{dbro]u{qz\WXqqdhhabksPKdOO>rzjo̦sxfrviX~gosyegx~_b[PEB3'4Qq|pϱ{rZr}hdigRVyv^|led`pihQsvhpVWPT`h`Zwijv{nw{Kttog||vnI{jWLnQ[vwR+nuTLSeȡ¾~ylUmgNT\p~oomQm{yW;d|G3G`[;Wvlǟwop|~dnkav{tv~p|~WOKTgnZS_cc`TFJ(UeZU[ϻlssjL_wdqw{~|mnul^anpvR7kM\^OO`zo}juq_yYPk}|]e}Vr~XOKYrTcKi](7ARVZe~{ȵȤrputprvSps}opXmanxc@`ucbOfUWgdGDr~ɱxlupjM|smpuyDrlZOCklre9(PXD3Cdnc^ּɤ{lquwb[`iryv|gjjhVOL_irgefTNakzeM}ƶhw{^Vzpzajt`z{fb`c[UT]PJRipbE5P:T~a{ry{l]8fcbvhq|ZIxkURU{xYR-pgOgp|kugccn~diju~]~kdfGHVS8/W[^MJeJGTnp_{Ĵ³ĭʎ}y~}xnmjry}{~NHy[fPb|sX6%Ug`~ͪl\|qt|ZlTl|xwmiEjfKQD>i[zk\f{UNŷg]ds{r{EHYktzto|}kiLyf^`vf{}uy^M]o_MZPve`|[Ɏ{ޠwrߨz|npj_rB|olbskyxf]]XpW$oGE1)muxo?@y>@_w~FPlStoXHQ`}hIŶ[OLQhNP~|}ve}rKx{qe`Sbktwurl>yibIuGXrdhğ֡pso峋}yx˕rjw~VJqoxri`i}rkhbU_w.Ph@NR~y|}\+#eZ9Kmg5m{LaPKSf~LWȜjXUQ_uo[dd|woqgpXslh]YUxe{wxqp^U`xN]^GzmmcŐvΒt~rܨqu|{f{f]Cthiat{znhg]qi(l[GI!]xxuL 1yR=SmxT;xvjObKOYpvMeȚmZWYxq~iccmvvjnafgqk`XWZ~m}uvouR\byLaYDmlcsȓ~py٭rvvyboe[Frcb`ypjg]tk*jcJU"PwzsO$0u]@Ki{rT;{mtHfONXq|Xạv_\[wvxuZuczroga}vXm`SUWt~rtppaLgybLbEq|ik}őʝs|~淐vxtzz^cmSSqca`vqff_po-cpIb,CryuT).rfFIfrpW;hyDjSQ\qaa̫d`c}yv|Vdynmcvbzv[p_QW_zvtrx\Ph|fK`Gx{hh{ŞwณqwlwrY[|eYRo_`etiidzk/siQb)Jt~mM'9~bEOhu|gNDsnjIfTV`}}_lȢ|eemklcvmtvg_xv]bpykZUWmy~ttrHrdWUtQRrlzfk{ӭƔfsujaR_pRpnY|kbevqljrHKRjP-_wv]7+axSC[iumY?p`}Jhu`V^mi\úvjnoUg~{lo[gaozTtcUYeztt|_Tl}rGpz\M|{h{|gw~}ܼȧjvxgxgOQrpUdxMrfiuvvz]Dbde/Op}v_@,T\ETeokWCdayWT~i[_llX|tvlYky|msY\}z^k{O|}h\]hyyfUr{zGhxZKy{gwzfvz}vڿɦguugxfMPn~{hOcnJtjm}~}\Wbph4Uq{r[<3a~YFVckdSDt^|QYk`co~aT}~sXkwwlpX]zt[mtMyk_bq~}f^t~}IcsXNyzfssexrp¨̧ky~tethKJh{zhO]qFvtjm{oUsmvWtVR^ecVLaphtAlzlfm|rW^v\o}|npcNXq~r^Vx_Yv~ngnb{r{M`zygSZyhq{~vjjxof|műě}i{|qzj`lzgH?QenjZK\rIypww_maF]opbMBX{[S\baVMavcyC^~qkp~y^^zYpv{ml~hNPeuyo^TtkPtwlrll}~WUn}xiWW{kmuzuify}VoǴwrflxxi]dtiJ;I_jj^NWxQkt{ycopJXlodRFXcW]ccXP`|_zGStmseb|Zutxkk{hML_nsj[VvkQr|rxkr~[VlxtfVZ~lltwqgisxRoŴtkfis}{rdZdrfH:GZecWL^vNurhtkM]mnbPJbaY_daWQlofuA^}spw{chs\qvshm}{`JN_lmaT\{[_wz|veuV[lsl^Vgykmrridvp`]unǽ}~Woisxsh[[joX>;K[aYMNpfPu¯s\TdmhZMV{r^]bd\T]\x[Brzrtl_zyay}q|xjhulOHVeleWSpnMmx}qxeUdombV_nkprkemnsO{yj~vckzgovsi\Xeo`E9DR[VLJhlJo~}nV`ki^QVu}d^ac]V]`ti?f}uu}m]pwinpxwjgs~pSGP_f`TQmoKj}vykVbmlaW^njmojclmsNwsdywblzhmrpgZXen_D9BOVQGIhfGlp[dmj_U^~}gbde^Xf_yeBjww~|f[q}gtrw~|tihu~nTHP\a[PRojIhyqYaji`W`oilmhcnkuLoo`qvgghkrog[WcnbG9?LSOFFcgEviaenmcY^{neefa\fetsE^~yw|}jZgyuiuv}}vkgr~u[JLV]ZPOgpJniwz`^fhbZ]wvjjkhciurRZ~s_dq|Xnglnh]V^kjR<:CLMFBUmMWsppcjni_\ozjfge__x}bYHp}wyu`\x|csx}ypgjx}jQHNX[RKWr^Mgzw^agf^Zfqijjecsg~tJe~~j\lsi_heihaXValdL::CJHBFaiEilwoekmg_byykhhe`erhRLs~z}r_`}w`~quxtkejx{gOFLTVOKZtZQ|hxv^`fd]Zhohhhcbtc}qGa~~~}i\k{qg\faee^US_jcK:8@GFAGbkHiovvgjlgacy~mhge`fvdZGk~xeazxbypptqjcgtzkSFHOSNKXscLh~{c]cc^Ycshfea_meozJRw~o_eipQh^ab\TQZhfQ<7>FGCF_pN^xqjjlicdwphgd`ezaaFg}idyxdtpmpngbfszkUFHL^nsqdlwsmnzsxv~sba`[Xd~l`^^\^qeu}Q^pytaxqvt~r[NJSdc[jlE[_Wpvqpyxu\U;;@CMjzkhjjit{qaTyzrn\xz~}~lREKeywwvMH}nWpmqj`^\XZmpiijjulmcm{]oWhurvvspw{`MIR_gjugL{Zg}||hwnspg]RO]zlcaa`f}gzPa{}Y=6@Wl}Yw~PSgiegp{}pactsXjtjmb`]WUa|oda``e|j~PVrzsmov|zzwxrh|~{s_Y_iqxg|zxU\pc{ofh{z`PJHFJ_{Qbj>Acqpmnpne`mwxtg{}~{wy{`IAFS`nflh~xxr|{{yrp{}ywvzvSSntlfeikkqǾmm^Tmbcdbafs{qZHADJPZpsRxcbzuuz^opd|x{woiqqd_^^fjW_tr¬w[C9P{}qeWhVngiwoeup{z~\odaDOeDAP`Zl_k[:T1;ERIHWJMpKfQag`z_OERmhfPmI^TQeL|jxpxdz`ovhwsy{xkwc[jk|iTzrz\Zurbq_cngfy|emyxeur}~YLSFNVvwuklUT:SpQFSTZRm[bhJk~jSci_ya]DeVAKbVX]xsyeixPlbableum}kpxukj|INJXQ~xc_hgqVz{uuu}lftt}SZ/Dk<1Xȸ_tZkic?W[LK]bQamVHkUJPuRDZ@ZyU:?m_5q|k~i~ntShxu^|up{wv}{yjfsp2@|o~nt|ku~tjomirn~ngbrt~zswyr«ël\z\JSQHN԰yhvyEaSCkfI/StFYrgDyvmjKrohYpQKVUIHpf`yxw`~^i]lhh[gvgxpiof`NN^[cqwb^`vniy\t{u|zg|sz}Ǘu[`wD,H8csrlɮv~[iq[caeOWdkI`ibXxaPeeTQlOH@LpH26d\Fl{~~q|cgkWvO;j`p{sLvvesYKs^CNY@G\eamotwdn[~fYYqg{}{qwsjhnC\DWTluuv_n}zpyo|wwv~lΎ~~w~b]cf1KK.9SO}zxūѫv~]uqh_YsX[qk\_kMXxYYdgSLJ_aCGRqil}ftst|~uvgUsEI`^@C`i}Ysdv{zhpcqruiwjoc~p|z|z{vsjeROu``]?SUZc}Ǹmz~dRXjbPqvfrN_xr`ebLcTCOJ]ixtmuyqTqfxr{hmoawvqZ[i_NfPDer`iref}ovkuptw`q|azwwuot{zoqxaMLOH[VM.`mOCVzw|woth}~htyrsjZegT8Z\W>byobksr{sxm_e{aaspq`zxu}p|^vuryVNU`[H@RqV_ģ|sdikiOevUgniY{r|_e{WIbj^dvlprjrl_n`xs~j~xzvnta\_fWBEI7`ag{lvknp}ww|vy|tptty~~rdkn]o}VUXPAMFG3dhakt~ykzglqdoyqgrmhyRSmZV3;Nkfik~vvwyoyx{~tracsxWRC^~YKW`V`|éruq{uvffbYk}h]|s[bj{x`x^V[we`lsrWlxW0GlpjfNya[v{fwglowfI``THVk\ap^w{vxqqn\Xuagynuut^`odeMQBR\Dfptlw}h~pquwxduxwks~nu{ygsktqiriFkM\UG@3AUSgksl}ʣ÷qpvrpzqm]elsvuvXlm`^kRUTXnuu{x||xygvvh{VZjSJIJP\L:b{nwwmyyg~shwmtnv|rswUylmj~igNZvgUfrWB9IbH4R_|qy}ļo|}br}tr]pm\ihmQLjVa}}ih|{bytiw{{~n_|vT_uww_W^db_dZFChn}wm~~~bmwpg_atp|kjy~{~zrvfct_gtmwhoQ[j^YeBL^aQTMO[xyϲ}nnxlkisdr{[RWA^u[dE_{{rp|ungzst~g_cc{qdHVe|`yptl~xzjunkypto}wcUbv{|dykr]m]UhV^WVX^WE\fBS/HaVUcpwkdtmnQ[kfgzgxwswz~tdw{|f^MisMUXbi\_LZQYmsthnwy{tt{eyqn[wjn~waU`cnxvtrk_fmRPapmZ\VcfceZC5KMLdJNm]fmjmxͧyuli~y||cpq|d_pQrwm{vky|g|yyf{qzvwqqmi}rmwv~}ihmc|_pmnzxypm~YZj\ufsu~vqgfz`wu`IM`XRj]T@F@]_?APHZTN\WUwҠvvqpdvw|{a~c~rx{~ygjk~~ibmvnPffcpjihpuqzznxxq{||}°dGNnmg=-F7@JKGOuyzxy{~vfszOYmNXV[ZPecvvcjpjjyG^ZE]r|~©tsgz̸|djMLM@TGO`i]PZJ[{RjbNQJOFXswyrruynsxinghmu_r^Njlsjgumrx`[;J;:8<]klUB`E4@Whmw_n{~zf~iyufik|vto{edHPdV^giƸyy{pr}ttwhkn`Y^pif\OYQ[Q??RoPUW4_ZQSORdrj~x~gidlyjur~mTZE@QO_znrrte[VT`xbese~}w~ͫűm]YWRBi~°drcaThtlzo~_[mQTWbdR`ksik[[rf|xe^bfqqoaOS]mokƬv|s}u~xq{gX[GaXWrpxqUF;CFFV?CCIGRdBN\GI65ZowztgmiZivvs[esutv|x}ǽxte8MD42AZslo`^womg`iiOfnYjbUYcsujleo^WdggmkzmsldkZM[iT^jns}np^q_jljsou[m}o~}z~|oxjkξg}vwvdYa_`g^^R\WNMBI}}wzvykm{stbnkec[Xiz}qaTc}yn~|ua^V.IQ?J@P¿xq\S`tpe`IbwkXgSEWU>RNIk|{mWW[`od]NSlquihyY^mJLajdkuɱ}ulptOX]ZUVnҾwpqao_LPGBWWOVL@Qj`^aowxmgloy|tt}v`fW^MOewsyzsqs}kz}hvo{sryu{qhobh]\C7==HTL}wzxmmeS\[Zdmhy~t|q[y~{piovYzxUjcj~Ź||~s~u{ZypyeNcN=RTR{vkaYW^TK_PWiIPhVXbm^cacxokmryeyqs~wkm[a{LUS:??HI]rvjn||~}vx|odYG7>D8?lv{{uhjqhrz~fsjsod;Iwv_YvP`r[Weznkwb`fOcYf|r]fkw}dh{l{ύwtoz|tzrobz~tz|}xaaurwsnmgkS::cpjPZkgvrof}wwwifunpNdteiv~}wpqwˡwGMuġsYXT]tujgir}h~qj~jZ_D;Gc]m|lkeapu~t{{VOFF<[bQ_CY|[bdhcndORYjtŮ}bTVkӼhoMC[TFNVPsra_\d^MQ5BC>P]j{w{epqtaag?C53Yfeu|Zoh^Y[hl}qxlzwn{^C?YTX{ʲvoafkmtrSTKET78=-57H=Jzquruz}{sez|zajXQTKSOWwd|wVm{}y{xhnmffg|ũó_d^VUYlڬnYJ^pahZOYknO]T/>02:9Jo}|zxozpWaC6UD=HMnpmo_U\p}xqnnt|gg{ɼl}\AGJEIcntspWbibPDBFgOCY8:JSQObowy{}xm~_bnJBFA::Qr|}yzq|hmlo~uuvdlzȦɤgZJP;Bb̸ukkoq_C]`VKDQ2)/2LBBg~lort||rsu~tUPMII:Fulkmz}~yfafceil|˸o~TVTJP]o̮zobfr]fm``e^GZM#DL8(4==LEI~z}s~xnfvq_s\JW[VZ^c|kllqkbpqlfl|ų}fir<@ZWZbu˷ö}f[Yty_a[YhiT_\;8I<:GQisrzkX^D/4QTKf{|~mkfm|y~utmv~xjhUS6.6DS_ûbwqejdXLJMKPSE>@JYfl{vömaePGK5NSQajmr|uzmTX]^M[o]prs}pykdkyyxs~rj|xnqwqkmqqlbJ[wULi`V`inwwBC\fjmqraqzx{k~wuwy~}\kxU`psv|yvǢlarpruvdUp}eitwxtkjmwbww}{]HlrwjfhaKUUPT?LSGSbylkv{tqlnnh_Vjzomk{}|sVB^~Ǖ{vzzhhxn[`lopovvvbdILuưsdbRQ]gQN];?MFPQP[lrmo\l}{~ll~idrr\fmYt|obf|hq|nqeu{mrertaQU{Ĺdzv^PJV_=JPJUD@ACO[\VdnZkuixpn^lnYZrqkrptvpagq[z}{y}ks}|gryzí|sx~|qppW[cSRdbq˴znj__eSKOV^c``f_bfMUaObwsqubuxmnjbZXV[cN^nmzwxn[kudrljy~lWUND;8J_fvrXP`M=>BFZYK`jfdkxrxzwqmmp}Vszw{|zucTnnPEPdYfe{}[]fghlr|tzy||ztrmfV;D_TZbioV@JgYILSXTNG`ajq{zocWjj_fsg]qtqgZThrZ}}pvzwzqxxopéxuytgZTd_FHEOoxƻóyxxg^oVL`fga\^d^Oad^jbq{}}~}fth\YNGKMDA95Vwy\yupslqoraUkcT\ba^pwĈ~{yq~dy{glwyvvY>fw^cncqvrv~xwy}e8Wyqhb\TdfSVowr{rxl4Xͩ{utwz{pnO\|eZXUI7]wwd|iacsfWWWWVQOhjIxq|q^Q56xdWaXJjqkptxzws|xaxpdtpqtr}̼uv{hlnmg]V\PKrį̻uHEYEbv_P>)/9CB@Yd_WV_SX~q_qrNZzna]YRG:-8Uiµ|{ŶzcS]dxƦ|kOBC1+,((;UO^xx|vvvs`\UH85A@U}zzytqtwvohsxzr~kphV\ks©}sh_`ZROPNPRCe~ePXLKRZ~rg]u|ȭ{lc^Q:#!8CGx{cilL=PM=AEGL<;e|zmewqoo`[luavpluy|omdN3-0:mºrkfgePMgS:8?@&(Z~bSejPDcgfu]nnwtqgc]Oʲ~gZjiYX`hQD`z{_vxkcmnUT{ufohZVR]tpn½vol_L;6?3*Fk}of^]`\KVZ=Hg{{snhUQgikd[ntkfoygkĮztpsxwx|uzn]`N6>?.?`}y}}ww}~{cl}zm[jp[UVPEFOFLVVoƿnwyeemqpuhVkob\\RCZvfandWjwy|ib~ldtwcXYVL?76@>+Gyʿ{flwfnl{wj[SR\RD`̶mXQMKIEMfY9ZtvcrzaRPWaQHfdrkaxt\RQ]U;A]j]fҸ}b][XSLLSA3KlwnrsmkiaN=UdDJrqzom­zqg_UH;5CVQ[Ǯ~pWUeXGNP9=Yktslgkp^g~|unlxum~Ǽ|uhd^]l{nry~yqZOF;11=;>b͹}uooolcZ^n}|tc\qqcoo\_gcgsnsŶ|ngc\YY[^e]]½|gJKOFML\jrtkfffin`XythZ]rgSPPK>9I]Z[~ö~w~opsy}mk{updRFEV[IkyjrkVNVe[Tgy~nw}`~p\[ZO@79LQMk~{|yhgcro^vwY`i^OISkqwοŨ{g]VSRMMcp[jzpq\MXT9/FDf°}q^]no_JSq~{sb^bcond\\esmjƯrligfi|oa|~WMYL1'*8?5BhȺgYo[hveTR^`H@XnkaxľvkjigefoiVc~{mgaWD4IdPIk}nx~{vzpez~~ZYndQIFB:57LaVT{ijw|syyef~lp|ld_XPKQ^[Ub|vlcessclqgvtyzxhWRLFDMTIMh}vnjlu~n[`nb__KBKOSagauǼ}sjfcbfaTg}~y|pl_JKWSSMH^xuoiyrZNJC74CQKDZ˺xr_ZZ_`LAXozqkzwwridhsjRiyrzoj|uwwcfOJG=1,3B@:YƵwfmtRYuye\ymU^ibYWbogax{kc^^^]f}z]jv[S_V>8FRILjǷ|~r_et_`rjWONTZLB\pgoļ~mikjhmo^[sx}{h_XQH>5AWRUqnn~v|uu|l^WQIB@FU]fržser}spj[g{ustoie_YQKLWffhô{qhcfsxndf}xr}n[a[H;68COJMiqnwa]mgRQYI3:K\jhsϺvogb^\ag[X|x|skh^H?UieXc~r|~zxyzvhOTeW@73/'&9]gXrϽ}~wgeoq`ayu\umfe`XV_rtcprhhtwfg|tXgiI=:4-.;QWZwĻǾ|wre\nwbdzytkYSjyr[MX^[^nǼvldbbahw}z}rpycP=9DA9H[]j~|||}zyl[`ocRJFCISPYpy{fXWY[df[c|ywp|rc^YSNQXak{flyyrkrznXNJE=9>N`dx·ueatygby}teYix]JPRKCCNdss˿zld^[]l|cr{sZbhTCCPdhex}zyx}~dp{nXM]`K>BMB/:\vyȮ|ohda_end]ursla]YK;?csad~olx}}wY`lZG>:4/1Cewmyzlw{lfok_kv`rvida[X\l|xtzlc`jvlgwyxyaY`bW@2,((2GSSfy{}{i]lunk^LMV\hxyzsia^]_dlinqqxdXK84DKOVUd~}twubWem[LGD?@Paa^kõmghnth\loco|f[WSKHP]\Piqk{yyodlUOME>=HVUSqzse]ojQ^v|viUYxxSDOTNKRdpls˯{tqnqs`}|bQ[aOBHXYP]x~~~hbz~t_UcePA=BH>1D`bc|Ƿwmjigkn_Ug|sojcYQ]smfxolzuv}hfjYH@:4.0:MZ_fyĹ}t}xk\g{|wxuplgb[W[gnedp{|ofaco{wlemyyzgaaQ@76>LPIUrϾývdwnbpxici_IGVfrngyyoidbbfkaUlsn~}hYSH3+@TRBC^z|lpwbWSNFFWojR_¹uysffmkXUj|lWnuaYVQKKVc\J[quuzs_wsVJGA;=UB Y}[kyerqt`szzbYRixtxw~ex~Ytphmfgp}vsģspxnw]j_lv|˻ɓvggH[jRa{kq^?orq]DGcWfxwze}~fWtyfmhimOq~^PZB@zqbxw{Rf}aqtp{¦ndj_df<5`cn|ӫ}wpPOG4IQHB@dchTO\OPVUm[tncsT/o]_Xh{ogreOléyk^ingrmowlkus}vu`C)=LFQ\ka|aRg_8_uLVP;Y]Tyv`|bh_dhWh}w|lvXQndUl}wYaXWUzvauhvlqɧ{iqWK^_Byt|QFyp^mjR]ualRMUEQ_Zkc{mztpor[Pq\Obb;`]M:9jm]лʡhvqy}my{}||xtw\3]WES]]|uiDXOA?:VPDcw`PxkaptYuezxsnc{sf[S``HWp{~q_~kapvX~eVafzɬysxngY\Nhspʭgk[HG<\aIbyqK4is]kzzuzqwyanXbjXcYpxh}q_FgWHXrew`ixvǽÞokphbzQ;B6WoiɑjSpYLUUaNMkd`DA^HNBWoesf}~h_\aVnu{suPdvu{outbmwsu}|sr}lk~wyLFP3@@YdnںŬ|erqwUWlZ\rmBc}VOFR}~wdZ}`gbn[lnsklkZkuvhogWdgutwy}~tp}ʮfzmlcmtnsJ?ZgfZnjllglci\Dh[;HH_wiwmw[{}iko_jvwdXGHkrHnsӬvfgmoalghrztx̥{v]xh[P:EMc^GƯopdyc_`gmd~~r[?crthz||ocxxe\e_ng`ke[yw^W\rm[|IJy~kq}j}m]lwqr͵y~WZZu88IL]VyĩpciqIBC\cYq~mGgmgzy}oskYgvoI`nrYfwjRJMXNXjо|ykneqzlq{wi~orxMLL=CJj̫|{^d[]cO[aBE?IVevw~seksoekTijcgBX_e`Lussp{i|hyr~vmy[QYSfvoXTdUdl{tatcnUMuylRSGhXRgSxxsxyjfmfcM{rqiXOGX_\qkũfibfxlhrҺ\|ZX[ZwYV0 @AZoĥuxZabl`md\quc@6uvj{e_fhhvxsprb^hsC<8b~h}mxv\mtxyqŖjhqj^=Xy{c(.Kd~w~og`mwWRFTnNI^q`p~^t^r[OWMYkuwrfNMKamĜ~kfmltcv|yfjVwvaY`n]CJ=D:X̨{kytx{^h\h^gf>JHX}}|q|]bkknajPWQBK_Nbyvuy{p|{vrqqrp{Ěwrc{tVdhc]O<,ENaa̼zwi`RfmpxgIbvozrlvkhjtXcmh^ra8C@Ohbjųysuzjkv|x|veU_kZv|tSWycRO>ZbYsow{cfhecAB_y^qu|lkubfei`crjkdZEVe^XƗ|ixvyerpnti[}uu?[qjQI=T^Ggz´~~aymUUVGyXbeqzxv|u}yagpRdkvmGKeabZrzyl|\zgjymtS}y{zdhq_NZ``D0KEHZXqtw۱uivx^_gvkd~lwo]MpsbjbkpZwc6DbPG}htm{wzib}nrY[na9eoDSF&GC;sntzپvtjkFLtnnyyygiYaLjqOr}fgNy\Ldpel~~rer~j|k]vnmxQahj\KgXPP8CFIjx}}pstSa]KEtld|^ngjiYdtig\jSD=Kontwiu^wcyPwhjsaZG]\LX^gW"ERFZ{orүk{mZ`[nqjkv|tZ]vWPca@l{83>BnwnɣȨs{x}lqimmnopw_]^EM:N\?Vxa^Qq||k}q~fOjq~{~ygvqXEQ<7cU8<4II8?q}Ȱȯvwpgjxkr||hin_NkvaUsyokM_x]`ifa|spwyoxZPicf}{rX_rpfU@V[NZFD:6V_pzqxhguiOhwok}ruiqcO^X]]\w[QG_sdmzpdvjgd~uk{fxefWaeIGRXY7CGB60>6BkSA8S}}~qrĤîĢwkinsrtfxavakxMftyv~f^|tlru{h6>E7HL]?Tk^~wud~Kq~qYG.&?{zOjrkjzq{UptSt|YWpe|SmhjkihJNy]CKLwt|bbgjykiavodw^Y]W|شnwjIpydogu_vr`ɹX~r~uiǙd|}~iV+WcơqjkelUlC1D5GFM(/W\X`h_N_iwpŧ`ִɮsvevrcno|}gjǵouT~~kqOP=HEJPXG@?\Meo`njKOWdf7mjnkZ\wxii]YovxfPWhcIp`Q}pr~[|ud^RXmZa~t}xzha`em}t`ǘ}ǜypsgyylgjsvhdVyb\~qd`0QIMMID5-Eq8JWM`VGIX`g0%Pg1[Zgxiiqz|pEr{xhpu^a_BZw|hw~whabfpyrzvci[by}gf~npsxs[mGz`;N^T~yoTOlYLmgRZtCKoeYnvpXhpnptnoh`aSL]kmvj~l~sw|um{vijjUIv[Tlep|}z_Sd|[s}lWmr{xtuypmobyf^iuXFKxNXi_~~~|ZUuoVtwe_yUDnc`Y|qhWY~qjnnqis`x\Ye{}}q|xu}nsyqeuvvwo|_NgoUfh`{w|zw]hijm|Wkfrutz{rqwyzjhf_rU@?u|OR`[nyĪ`QsoQiqcYqT>hdzbR~wmbeyzwylwazUQ^uzv|luttyu|k{yvuqfuoQC^iR_a^|qRc_dzziy}Vno}xim~mvmr_pZZUfG6?yuQXcb|{VQycUltghxUUzwdevk`luwqrlfm_iLLcw|rru~phxxk|bm_s_JHygaiio{gS}}bwxu`oy||{niy~~v^xxkgf[cXe`C=aiZbgeyY{bmzzkzlOk{}tVrxf``wxuuouguk{^R^w{w~|lv}v^vx{spsmpqQHcv^_d_v|y[who{uy\si|~yzxdm~p|}brqvee__Zb@7LnSV\Zxw^ufhr{mumPc}|v\jnikw|ntpu`KTlv|}y|qn|gyzqlljflIAXsZY_[xu|Zudktu~u]ts~~~pYczwarmpbXZYUW63NjUT\\~|`{gltu}q[o{gmmil|{{ztwipmp_EPgq}zzq~dqu|miejaiIDY~d^cavuscwmqx~gtwt|x{}{[byrz~_c|iq`dTcU_@9J{]VY]ktx|xvyzahvnamfhwyx{vynourvONeq{zxwqrkczt`xeoUFN~i]Y`^vzuvm|ptjgzl}uwgWjymu\vxuroQbXZN69]fVOZU|u¹usxzl`tsmgnnw}~~yotuveEQemtx{}yn|gs{skdobdEAU}bWZ\ho}wuxoqozgp|o~z}|{ZQgs|}~qm]unqhmqH^QVC2:dfYTaZ~õxwy~rlxujnozy~yzrlnpr{cDTfpvp}bov}pe`kb`EDZj]aalqy~v}}txsltxm}ytwwwx~aSivg}kSi{mlifrL\XYJ7>bq`VcZ~uü{{ukxp{almuw{z|wpqsx|vPSiry~~tnhf~ywv]ndeLCP{o_[c[z|v|{{zvq~jnxytk~twxyv}|kObpyyew}sUgyslodpQTXVJ5:XtbU`Ztrĸy~vulwndrs{z|{~vnoowuuPPfov||qli~xuypYfa_F@Oxp_]c^|z~uun|~lrwxn~vz|qunyv{eMamvwf~kSeu}jii`eKQTUC4?aue[g]xʾwvucps~txv}mnipuspNYjtxuxbl}v|pvd\efZDH`o_f_mpszv}rwnxkfyrt{wyyvpqm|szZUhqv~ueyzXWhxtfned]NVZS<;Nvn]``bz|μ|t|mmpssyywxynsk}rz`Qenuzmmgxyytp`bidJETxub_`Zyutq}x~q{ksk}rgwt{}x{}nvh{pucM_gnp~d}eWgvyinj`_QOYW=7Ei|o^[_Xxz}u}t|m}yy{}owf{ptcM`hoqttiwz~ynlb\hcJB\tziwuUfnhZct]pe\przlJD=bt{p~u}{qkzqI6I}\UPJt`SlK=K^jhWvƸz\R‚F9Rqh}wkwsTXXkkneuZ;C|[NBokZ{fUfxq^gklp|krv]{wT<+;lԟaVddSaOfsmaq~||kvwPAQthTutXseIUjvweyQS_ynpd`sTyxpz|yupqOOWetå¼ro>!.dbfk?HJ&)?NTEMjf|smdutVR]cxot}^{]opx}sc`>-6F[ɳ}mrystX\yv̻dkkM=Wcưan\LfUT]hZUZg[V`HbPIA)253-C^wvj{vnsTUZfgfifQW]9XX=D`u[^p`rkObxxs}y|Y[xdXw{xv}j}{z}zn|claguɲjQ_<4WADcȾ|rkrPE]edTck`yqcpcLkqO^fislq{fwin{u}zw}ivgOfE@B@Si{ѵty~s^jvrmkgDRnV^}ǯuvnajbclrcY]gUYaA[O;E>FS\wt~tqv~}}xaZi=ReKVv{rwyYayzv}yxgtLNR.78AXnDzzo|eV[`fdhgYKfDEgKFX{i\|{hgtXVq{xo|pxdRuoVrww~sy{z~~wsseikir|ȳ|ubI_@0ZHF`ùt{_McjgZnk`zqcnfOoqM^_fvptykrmquvzv~zgzaRgD>EDO_rƲyuojUskZowzm{|qtr{{gudO^wdhg\_kla{hcmo}yxtrj||immqmlh\FbQ?o`S`{ǩzX?at=+MUR{|{u~{vex\QhJIVTX]h`hkNQVadnql_uhx}}ntamkp|w{q{¾_ACndC6=91OA;]IJ\\]\_~i|yvxuxvmXshM|sbkmLNS[w{mVt{zxoyr{|uwr}vt˽kEZt÷{f\PLIC/IO.\`JM_pyzscLfm\LT^Rp~nuvk{qvxpt^OWovqlYlkPnjViokjh~ŧynqvZB;QPoz}zp`WeZUzaT[IXoug[Z`d\QSgr~ytwelyVboPWeaa`xa]{{uqrqc]df}s|yx|m~{|wmfYIB\wĴi[lMBZB8KKFAHtxkzqnpxhUp}xvqpnqpiWroPz}jng|v`LN`Zzsvvxqv{s~}yysmNX~ŨxpkcNNhCLnZPYjryuiKSj\PCXVXv|npzoy}lp}lu}sokeXPo~ljd^yd\rtnit}ó~uwYkbO@Gcf|wg`n`a{_^ZI\mpaWUZ[[MUuv}wsq`quTdoRXliec~yyad|wtutgdlh{wv|n}raXUTSKnĸqbvaH^W=NXOHIn{jwrihnmXk{vrmkhkmjZfyVkur~~[ho^WRrwyx~r~{~~{yqzrvjej^^Ŭzroj\Ql[Hml\]jqwxmOId[UIUi`o|yytf{pqs~vrlsf__bfYxtju_ww^n~vop}umIX\PPQvthrtezkdcMWhnaTOQV`VT}|xqm\nxX]pYUongb|{sqwlm~xvwvkkrkqw~rtqvaSNOY]VŷppaThZDX_TKVzqkxmggni[y|zqojgeijcZrpYxwxfVnd_W_z||qywzusisxmmyvhbfj_x~{wn^jqRcwibjqvztZF__VPIdeeuyxdyum~~vyuetse]\`lZi}n{|}vl`ZYgxbhyosvuumS;T[UUMkxwin`tt|vcmzsieinzfayrjY?M^^ecD?ZYNIer_lneejrfh}t_ncIox|}w\`jk^QKOWiZ[ƸcjulP^XMLmzjy|truyigmupj`TMJP\ylh|wns{ta{zymˠgXjϠe_S@Nbmhdeluolud|w|m{ra[vzhutkimsgiqaY]mrqtx}{eSmpe`N_icnx}cmronqzmxͷ|Y>Vdlm[or^lphfinb[~ezwqh]Y[^by|z|}}gPfodcSbvrwvysrv``kqh^Z\br`N{w|jYq{l|}{s_LBCL\ĮtWfp`\HL`Y]jqdepmjhk~~vYWcke\VX_phOznUfwl{lxrvxsrwr`tlszyrhccfhw|mW_\]bqĿwz~wh^Y[fiEX{kovgOUn`[ulkzm[a{nhytnmrugmuzk_[\boh`xrjcPamdlvly~}z{~|waE<]dezxppqeYuxZpzkjf^SPQSWl|{z{~}iTirefUXroozpnusswumtztjcchwpOryp\otwuj[J?@IXtecvibXEWaV[dtkWehihinz{gT[fiaZX]ix[[qXVwyvyv~z{~tjlyxulcbekt}wvvw}RMVYcpп}yka^aojFamm}yjXDVm`_thdsi[fyhizxsrw{oruwsj]TV`nmg~xolXcvmjqgs|qzgT>9by{|wtvrbodgue_\TIGLU^n~zzzyx|}pYexlmeXpzmoth\jq{Ȱos{yqjglz^hp\kİm[I32CVbUYryuyspbURMA2*,6Lcn}ij|qpeWixibt|gnsssvʱÿv\:0M_fɶvgif`UA960&")DLXdrzc|iadikllp|y`fogfbWE/6_v}sprqmnxgYd]B>OI3;O`sȥ~{voheisiKBXeWMSYYP;2M`YV\ktcYfs~jspelukXSUTKFJZsvl½nf~|yum\B4Njnu~z}toqobX_lbK@@?711;Magd}x{rh{||rfsmbpqVNX`hsĦrat{[HB=3+,8Qot~phXFN\NDU\D>IPSW^frmk|now~|o||xxtgO?Tnnp}wlmldZW\iy|xzeir_OYW:19>CJWhzvxz_R`uqc`baU>/CUNJRf~}p{uyeXWTKA?GZsɼudm{netv^Zekot~ĭyhpze[VO?&.IMSdq\]oiOBDFC>?GWinj|ucgwm_lqZT_fimt~sck|{viz|d^nlO>=<6,(-=Tdi¸ypdQRee[hs_U^ccbdir~}ms~{pYBI``[aq|yxzwnggo}}}veZgiVS\I49@CFMYkup~xtupaIH_eZX_r~r~|q{x\RQMD;9ARcckt}zlmvxvuw|qek{yg\XSE-#9KIKYrƬpneVWXSKGKWegay|r`\kj[_cK>EJJKQ\mzzyePWmmddnn[gu\?662*&*:SiożòqaivkfqgSV`cdglu~|{}~g^duve[ZWK74PdehtƻnlgXZ^\XX_m}x~vjVNZ\NQUA9EOU[etĹso{zvwraLQhj`^ct}mvs|~~yhVd~qWPSSNIKXl}~~xn`eyxr|xccnsuv{rfgvrc\YN:/E_dgsˮ}orYTUQJEHUekak}xlyzmakvkglZJQZ`dkvxs{{zvhTWpyrptx^_vkI99:75;Le|ǿǹyfaooeicMJTY[\`fn{}x{omyzib^S?4I`bahuuht]Z^\XV\k}{yo^OU_URTA5@MW`m}ûrko~oda[K9A\eabjz|qY`wgF9::66=Ohzz}rcevwrxm\cqy}j`_kseWROE48Wlmr|˿|b^y{]MPUTRT^p~{u|wumbRJX_X[UBAOY_eo{~uuwc[o~xx~x_QhsU<79858D[r{zƽ|ofrz{xc]hoppsx}}{yvuxoecjzxh^ZQ>4H`bagspgadc_]cr{wl\LP\VRN80::ASgpm}¿xkq~jbmvxxz~oa[[ijZLG>, 2KPPXg¶Ǿq}_WWUNJMYgg^lw|rj]PUcca[E>ISVY_jw~|saUewtlkq}qYDTfN1)++*,6Kbkmľ̶´{kn{{xq[T_ghggkpy~}rgdfu|ob]UD9I_`[^h}Ưn}iadd`]`lxxmuvtg^QBCSXWS@9EQWZ_iv~~wo]MWif[WYh}vyoYctUHHHC@DQahcoƼ}pn~mairspmorw}z{ugdbm{scZSC3;RYTU]pñýywr^[[VQPXdi_^x|~vm_VannjXFJV[\\ajt~zsfRPckbYX_rv|bSiuZ@99733AL]hef½ym`fw~}n[]ksutvz|h_ZVcm`QJ@/,ATTRVbyͻļtoaa`\Y\gsqfm~xofXQ]kneNBKW\\^emw~{yk[_tzqjio~xkSEZiS912215ATdef~Ϳvt|d_jssqqux}vmjem|veYO>5DZ\XY_oywyebb^\^huxnlv||mcYK>CSZT?.5EOTZcp|vhVWkumfdhxwaP^qaE:998;FXghfyŸ~pkzy`Zfoqpptx|{xzxdYVOVfdTH?1+>W]\aj}·Ϲ¸tpfQFQNVfRU`o}vl|v_tXVP?OefhY\c[ZdldalnvcXWe{r^lf`^qv|mfssijpkYaknsRnܢXMQQo}}|nozsr~w{qge{”fYQFDکrdTjz{nur[Z]g^m}}~xqrq}ZPZXXN[zyWdssh^qtypembNNVbhvo}}p~yv~{zj\chjQ7b;}~yynmycKELPNfgiylgjoj]dqVCZcdZJ\r~qs}vir~[}tjtvxlmtWJMds~q~clqyhO=>K_HA߾w\MDSO[_S]\LNXokad|l_R[oskRJ`h{k|tnnib|eiknhh|lPL\yzo|±bgzoQA7X]:bÓy}~zszjREU[YZEENAΧ~w{vnnbOI`VWD7AC81>bslV~stt}nfvx_P]bnnl|k[|rybkrla`ylQKa|mtӾåaque;FY9TҬuz|qhpz^IU_SJ48GC5'=\we\njv~eh{sXPUghz~f^hj|oyzk`gdSMq~zml϶w`{u]8PU7fġpyzlftq\I]_TA/8GC3(D]tXdigx~el}mRPTr}f{||qaYsi}uqfp~^QP|xyyn̲ϼre|qT5TO:s澡s~|nl~s`Mc`WA35=\yh[JDuҜcwilnfp~pwsfpvt̰ÉaibL@Ppou`QLUmmqvhűciv_k~lS~tznt׫{ZVQtwmPMjet[LICAQpYfȱdQT_cuz~gedWeovdyPmxxT3chGS_zyoS\nKT:ump–vϹppmmysfm{{ioyw|ÿ~ctfgVO@Odvp]cMB?Jcjvqjž־lswXbyp]Cj`ci\}cuihlLHEhooSKnaufWOLN]hlؼg[[ei}yuzkw}uzɨƠl@clFLUhj]GE^=C2Zuq׭ͨxxtnc~edn]X[jggzws{uXgbe\UGMcs{fi_RRSjzmƶ}Q{ukfkyke|oOrbkj^kF^jkd3)?KY`trkúwpeWlTZUO{thgw~mEep_Z_eFch[RAq?7djzuqxqiQo\~{]™^\V_dYM:X\54CcP`vkȶΞ}pz`fUrp~aPqpJ\wz^rWZqoejjhXvqeWNLGXWephҶ¦{uedeotmXsmmtPeZZWa=Cb]G=FW7Kiv{̺ĕrwvzMmz]diYkr{hkuumpRbcOZMkKFF^P\wu~hyxu~nV}nfyKZ²zphgiiTikIFeid@@<0VPdS}ʺзunxsZ_p_[jh\si~twdnjtW\HS[c`DYiH:bwvzbu{r{gibum~x~zv\KfqNHNhrQO/;6Serp]ķ~ba|qtqW\l||m~_q[vc{qARn~eSfcDSf{t^~`zqkoixtqm_qlfxxd`a`@>SA\A]Q*3Tthlǩʢ|jii~Zbgyg`u}vsSYicV=\~z`VRQ;YidvaƬwt_ut{mn}\Xy|yyy~djiw]A]YiGWUB/,]r^Piǯs}vyoj]\{|}cjwyyrckss[KekVdcsQBQVrZ|wiynwhukzpxNU^J^GfcUXXN4/XeVcd}yco{zq`eyyhvxxo]qfMYsz[fdHXLsjVZrd{~v\YzxqZQ|tt{t~_u\qUKZSOBYeH.5ObQgt}Ĵx{nrPQtak~qQcnb^p{avYjL@`deUf{}yfz~n}heoee|}uyxpekkY\eXDV|_EC[mK^M|xoWns{wnq~stbfgYEc^[YdqZT\VOe}km{|m~jv~qkvxxTWfZD0Tn`MML8;NPTU~ɿĽŖxt|lamyyut~|uurKq]mYXvjN+GtpTN\inūn{w|z^^lsqn~ypl`drlXFTPJS`c1M>BIBnvwl˯zpwiuzdyjlZ[u]d`^`qqZ=HZ]in]tywh^|yrh|uvcZtpxdiq\JLX[RUXD]F@LMN]vҾy`tvw{nϴr`cbWXStER^dv~}iw|rbvixwlxI{iJ7CkV@=SA 2]UR8selv|[n|wb֟n\obqelqF4QPh{hOblasfLLVfTyawPs|tmoncMb|vi\]kkqQUlcSSTcisq}pf~hoWU}{jf~hqhicYlzbzvTxy{vs{3K3;_R@z~r|zatt~jy{n]xxbvtntՠœv|[OW6yID`O-6RKQM'\A\Qo_IYrgti}_TXJlq]`mQPAWXPoxwm\ae\Hahn|L}wz`ha4/Y|}eZldpYiqtdWPssprywuyjΧ|tnvu]LFX\L^pZAaOWI2GvkNIOK:T{cp{{sVFw]~m}|x{]kTjfexSKbĬzegWhLtlnrSe`fXDZtwxsNmۦ~mvn`PB2@!Ew~qv}xh[yr`OFqg]\bibJ?pzjmyc_UFuX_ntq^UusqbeVVozugfeyiyonomklVjsIIW]mnztYXjxvcwbvntmnVyʲ~kx~~uwwX|q\SX]J9P¼שouuvyu\dXFD]}pRRV`pzaDD[yv_f[N:HPZDY}mg|}s[Fv}t_gpg[y|ck|hj;_`Hfvp{b|cTWy{sztt}agsY\n~țĠ{h_jmiV9MFNrotfwZNkvPBfmdP%/rx{Z9I~{fJh^h_2~_geipyr?8Kht_VGhyq_aqprveahqyQyS`KVavwSd]ZKBff~ģuhSo{Yr]yxu}gsmMpcizz|x~{vhhWCFYONP[KF`]ɛǸsraEz{zmRCzyd=|mWPP_\Z{CHePVNKCHhog]q|konqzVxVYicM`W>4Qk{q[o~Yevu{^^b{gxt|]GhsmbկwûdyePCJ7gdFBE>AWmpwŲo|ifjw[ssomdAIvzUK{zocelvvF'[]I]pb|uzmiwcjx^kVMPQY}ALkUU:Im{Ωg[bwtxtV]f}rbbin_cyͳv}ny~SqN4,A[tjC4>>8Clq}ٹnh|hUo}ugMjuIE_]mou|o|dobmsfkbd\l{wWIRXdYlz~yr^y\g|qU_~izrz`K]|e[`fȑ~wPdwf]JMHMRWN9JLKWVlsϺnqwhpYIy||zhnX[j\F;ykODPQn{~v~yzt{zhisn|p``ZUfnrSMYYc`rzfƢ{enTi}jSglohi]hPdgx{Q`oa[FKLPYX>-CGLShxײpcho{}kkk`ohb\@4:RrxWne`jgofx~dF@FOh}Y\Wg|w|zzȡcMpsammZSgzrp^`joleulflzѝx}z|ihnV~yFRTYegR61@6-=9NIHmËp͸ԲͷU`g_txcXX7:hv\QJX[s}ptb]xrnbItxaJDOZnnKEHMlv{ʧ^keYicd{W]h|ole~jemx؟wrlysqmxq~|j|aa\SXR?+2W[Tecv{pn’fΕ{oϛld}v]^wuustndwD?Ys]VtfuvbchvpfUxz~P8?crzf8128Q\ztnY\|w]tiCGajWNPeȣr|~{_xo^yrcPB=[bR36RMJWYpuocɥϦͻϽZnnQ[qbbZqxccuTUgyzuxr{ycb^v|c\^GQfOabHH]]cL7DK[agǹ}xZ\jm|bcsdlrXcrtikr||Ķykrgifqj{^~rp|yabvd_TLUB6/Ujdgmx}m䤩zlqӳnvbf{rfsxqrENh~ht{}sijauo_Y{qpR67h|~a6,3;GKl}n_b}}[jtd@LZ`XQRdx{VtYXyv^C85[f]<9EBKYgsniWǘįӬvcm[moa_\y\wiUUpvuvx|yt|Qy~qo_aLGbQNgq]EV`f]FDP]ccxužszZciykXhsuU}fcdeeiu{s}Œokjldfnzfa~ijuwXYzocYYMWH>8Neeejv}tn׷v}ƾ~txu|j{yKQhujmv{]{{erxqTRuot\<5`j=+,;?=\~sni}Vn_lNPYUVTRaUqpfTkyjG3-OlgP8;:HVjqgbL]ַϽqvhlqhbZwqc\Ocwnqww|~ze]jydiXD`ZLWpziYK`fhSLM\fan|\Ѿwqu^bpfOmvfUff[Xdkolzʲmcgl[jk~[gye\nmLaq`]RSMYF@AT_bbftstqkиudxxPYnidr|Yy|lnxoMLpfzpkW16KOPQ[Rat^`zkrtxjwǔt{m]Xmz{p|c~xoueXlejyls|m|rhwn{_KTiqod|jrwrjPinQ_bi|eToenjFKbUk~_8Sss}|}t{r\=nfe[cqVkgn_lhdrzeɩsR\Ԋ0*:ARU_tnt|ypy?p~sYRf{ppl|uRC}wpbVXqSo~{no~\jysitsm{ucNc{YbЮ[GQr{fT.1*evxl\`tgnbtmLkT6Y`Dg}nZ\^o~xdpsnCVakɞch}h_iw{{nouʡk|z©xeYEMEAiywV@EU71Xqwg7C[CbJt{jZO^qcx^gtjypvye[^?JbWptuxsumj|tgj`]p|[N4Obŗ||ckz\zOjY.6VcgV@]tlZXOUicvhkrp^\x~\gy}ggpq_NQ_}vϵrjutnqi}lr~̥xTBLR9FxǺv|VmfdofMfvdZ\K[ZTnX`EPau}_g~rmu{fszUMWiuqPUd_Yn{zmhmryuxpzxyĪöu`aidN@xsjalqeIIuzvxlwLGE@`Nu}s]D@]rjiw`8R_hzy|gh]rdcv^o^yr{waznjqwiYx͸mdXrZlIOgPaЬffemx^~mKTRe`IUxf`ktmTUrg~bRMXisfxulikWrnn]LRjVo_zx}ytixdnx¹¢y~gYYWI1/c١hidRb^JG\uuX0CBIfPqcUix]rv[NW\wefnmn^YmOOcwmrsw}nnyl{ry|sP75->;CrZutp~v]hyY\?Zp`VKLI=8k{UmX{xupqyWQXFi[msmstyn|m|eVrykuxŶdrEXQB^lvgq`[SYwzd_o\JF?EG>ZolbNCfqz~}q~a\xd\RTPBBoŴo|`xzxpuz^n`~ftxmV<9edLhnCAXfZ:Qf[yxslLQazgvkyPVrqzcgt]crsVcb/Otq´bhyagy~umaf~paxð{qfYpW=_TT9Jc[Kh~vjyqZ}yj|ywysqUq_XUV{~Pok`s+ϩm==EK=A[qsT|nm`eq}tJThknz`xsgxvOwSTcpca]qX}rjfc|ypgnrxcP|r·ũuP;oN@=KI[mZAmFW^HMHg^SF;jpvnrjPzk{eooy~TRSWs_^u~F]{]hzvp|Wb^Enq̭ȩ|kZeIRZJůcODOUS':~fYPb`E5lY1,5'etjnŚqLtvrvuxxpmjJ]v`zwX\guyj~mix[~jthM˦lήŹxa]giFH__||~mmlvP2c^PI\]R;jX@M`6KjkhrU}xwr_wykabji_mPxuz|nv_zfsdohrUbެ̾ϲ~||e`aSI6QY˺hdra`]X7esOFmxHIvZ9>N5<v`Mpk{xsyTEPZgZqtznuk}\~biivomزƵvbfVKbtqQmq~tq|]^gsIcgI?`tc>MoHQcMAxro}[UkdywwcrvzvX^jlUpytxuVyvmpysnn[Ѯ~oqX>I]gVoo¾kkW[ch;czgQltJJl9.JBCyp~z^a{][vtwfvfSYbe^pq}s}g|cekiy_mǭpw·iiJ@TgbTvw|pmztMuoaK`k`=SX8ET8Gv}vrp{tUntmukvzup~\KZpphvs}~f{uZadqbôobjLFKVNWqů~ypbkTOxeWGsrS2_tN=<6$PruvqNwtywzyyvumfDJZh[mryyfrxspf_Saadt_dW;G]hWp}ɿ|qilzIfk_M_rS24l]AEWCCq{qw]\wi~o{i|xk{hP`stfww~y|`v\zt`ihnZsȳ}}xmvVLVaUU^q~xuqyNHiYRHvtW=kwK8CC8_spqtY~{k~utsz{ztSRXaVfevpn}Wzyhhqdh[gķzhgS@LVRD\cxwpo}zW}ogIcq\CY|YBFG1HnrnYgz{tszxpuVJ[j]V^d}umlqaxafh\ȸ~úxv_hLFU\P\Ph|zHXpbiWbC9fgE@]83qUeycKwvi{dh\a[9SfsNGEdŃ}~{}}YoaUzпwkze@8Uh\Tkl||}ChiiLBkt4eqYgM@=YxWn~k~tqpg|^cf\R\SKtUdp|p}qv}{VxkV]dy˧Ȯ}hbka;MX@QFQֻmtw~P^kS?BfS1KXFTooyav|nvPw`h`h:bqD@Zkiwyokyx|aoˠ~xtmgcW\K`\>?dm|]R}QYwfFNgJVLEK[vVn~{prwSvWO]QWL>Zrqapmmbbjkz`myq{ϫĨuuoqqnTSs^5AR]gmVîjsntY}fQKZyOa[^Y^ds}hSdtbSpf{|c;5om\oġ{|rku~{}_ixgzwPO^lnWAHD7;IcľxomxkGrsT]OscGGOghb}mqdlx\OYheaGF[bovkhxdOubDXonMqj\Zbl1=6Oouĺq`yTHANmjM]VBRrl`amoi]ic~rhRos_Q,^dYaϸq{nttjv{gzrqeolJaa<4E?C8+Rϥ~kH_}bgswwGW>wznaxjrS{p{UFDW_]PNgiosp~daqdoq\Ux`\Ģ|sg|~ur\JZJ.0LjtyoѼĬxyvooI`z`uIS__RBQwy`wk~{uqSN{rXMOtcTmeuk|~lZeds|}f{u|lLj_IPNB/=TBG8d£ŤoltwsjcsmNGVnyzttc[hr{jgsgr[@|b[;2]gjcNxugQsyggt|mlhFriUU7(#;Wacfʮ|}akjPT\cNJnaYQwiz~}nxSbpngeWOWEEV~{~}s[fymAb}moŦtlmwlg|yyvlnoL(=]0(Wվ͟xfuq]rjcTPayRtR}IoiE9oDKY"I*?dprrʷwƧsmlr`gnpO[txw`qXKbp\KU@2s{~¸|jeoXTmj9de[s|{FV}wOAPVa]g_g]ɭy~\YRWrb\kbyvwI@tw`7ZO@*$HjX3Gˉv_`]QhptfZn{~u}~y`VD=Zqi]Br]^wy_^~|vXLIlUNsz~bKd[n}P/OmR'.DEQSPWp{żĶšvkj||}Tnigri~kB~xE\iSEA2Jla;8ppc~`glpz}glZ|y}ffkgRI;.Q`[LL'JVS~t}ʨfxzaQJXZRvlr_|xxsjQ_yJ=DNYTYOvn^tjdrpWowX|{]jub?jxmBNdU6"$>sn@Ysf\o{[Hxz~jvxlyT<=<[c[lVd}}qzbYai}w}ìǭ̴td_V\YB8jhʞu^f}luuaTRUx^glVf99DESL=Fw~pot}lRm~dtnkvWNMRr_|~oqyyqbVix|u{eF=?GHKy}tsy[O~vur{|PhXbjHXRVXVRqsWZyh{y{sy}qpE3EOcjnvwuqjf{[qtxtwU==>R;Xgb{cyuxS`p[^tNhrd59vm`b}z~~jrqfd}z~_9J_nOab]vmxqnnyccx~s~regC2N]V0GJNhȨ{{epf|p[yed^`H]kewjyx~mkunc|aoUQ_`P6W[atf|ziydmSrjuSB@EB9_doc~tLn}oVtxToy\-Pxkgrx{{rvufmvz{{wv{U@I[^Phgnjxwv`zy]z}ztja]]:/CYU@NYGOr¯ȯpjszan~q^qrnxgE`zmvkn~vvzkt{xkQ}h]]KHU]P`¨oqyu^xumR~{}jrzs{]IiIG?27NYQe}ɸ{lpe[xk|T:nw~rntw}o{|odgyUWJDPhdUexzvuk~agVx_y}~h}~xxacf[N@_XBPSa\Mϫelw{fSiWXihpjm{tgs~zhjOcpNEyS4K8Xn{uww{kXN}qh|\qUh\kwQfV'[[:Rfcdǻtza|lVhpHFr`ky}`vtdlgi]_QMhh`Wbttuyyrkmfqcxt|T}qpn^RFc`JSPXX4Y][~жr~rbxbZe_mi`zzm|daitVC{gK^Ipzwrrem_Uppujx}~gjewZUlEH;ScNLqsz~fv}zkxkiom{e{nurcmtdwvMNxeY^I6FXHbduylrwRkcUSRyvrwqywvip`Vi]\IPdV][yy{ssg~vWg{pt{ylytgkv\NUNV:CXH9JQD{º}vns\Znfqh~qidoRciMG=]`LMrqup\|~p}ghpos\}wystpc}sd|xpGLukgaI7HVFZYo~wżlolW|bYPbznxtuu|uqsYVdY`J\`WZ[zzs}yqpwwxXt~tpuy|oruufwsVWUPK6RQA3E>Ov¶Ķx}qXeeglfq}eh`V[SCZiUEZrloV^z}{rdnjq]lyks|ougwkq{wXFkshjVB>WDMOXjbpɶ{mr]xp_V^{mrws~xxrzdWcVaQ[fX^Vnnau{w~s}]qymdv|wsuzn{x\[]XP;TPA-82BvufnuĽ}a]`hmopzzp@dgciwhLBigJorW:%/YBR{[}yh}{jgxvcpnyabPsvj{djeioQ>pfWpVzhvnmg`rccrixoz||oYpq~aVVbhǟyv{lEetUhUXbmuhplpnucG^_mWBDCkDqziqm}uwxw{Tlfsh^wms}}YknxgJMFvWy}r|gba^bb?Ov\eg[rxqui}}}qu~|~X^hmkJLEqjovunmmMHr\VZ@tvJj|_kpbbcafADRXZ792XPI_~l~~pk}inmix{}YVOttirsmmmVMzodmVdsl~fbZz|fxjzdfyy|vzv}aWiltSOKlzh¡}gVvfnSzZwrrwosovYM`dlKD;Z^Fe|o}w|yzzi_st\`wz{{eYikoLE?bcRvooihfTMypfjVf{rlfq~sq|x{u{]UcfiGB>d`Y}yq\W~od`Mz|OymxaizmcgbiMFUZ^=71UN@zYvpqnmorfux|ZQKml[~zvqbY{olYb}yjz}eZWuu]|{pff}ro}qtpvaP`blODBcnUĩȮwjzw`dw~tnswsxhQadmQB>Wa@|fnu~z{~vnbyyb~isynwywyoSaclT@=PhC}yy|~uvjdSrnq]rqp~ugdof{yrlxptnqOSYaW::DlJnxwft{tabtVoq\rzoelflMLU\X96:aCQzZy~s|{unw~~kaknqQIJpd_}tifyqae}xz{ftvdUTpwY{qjkl}wnbziikifITXbR>>UtRʯǹuo|zh|yhzsztxY[`g^B@LnIge}}zukznobxyfw{mvnuZQY\]?9@hU\s~~xmfg{peh|p|o`aivwqzqd}khkfgGMQZM79OrPèzn~how\on\p|ocmdlQKSXX;7>fNYg}x}xpuwv~x|mV`ahPELptbĹvqm{iqn}vn}biktePRk\~zrjzq]~jeUlyte]f]eHGNWT=AVbӹüzj}{ix~o{pxaU]`cGBJr\bq~~tqvundtvrcgxdmgj]FPRZD:Aij[pnm{mrtyukrt}m[`}krufneWpxta_c\`BDHRI7=ZxZǫymvaoiZq~{keoemPMQYT<HMWB>MxppŴħu}zloo|uxnU\]eOEMqg\ywx|zshkvj\]uwr]jce^DJLUC9CjhZwwz{trs|qq{hnpyfY_kulgvgZUmplU_][Z=CFQB7@enZǾwql~pd`x}ydpmkkOSU^L>CfgPx|}wruhe}~ivqpnQUW`ODMrq`øwqotihpz{y^bclYMTvo]xx|tm~`]{k\OMdhdO_YZW=CFQ@9Dlh_ǭ}|ooqz{x\`ahUIPrhWw{||vlgxi\Zpuq\jeccFJLTC8@dbO|~wunxljwiklueY_~|cunbxdVOekiT_^Y^@ACM@5?`fM|nth`v~~jtvowYWYaTEJfkK|p|~muj_s}}joujuXSU]UEJdwTxjwmasttsi^agdPQd|Uxq}xzizpWexaSCP_bUPaP_M=BHJ:>SsUp~ŷƼ|ltvyx`cehTQ^|_es~x|jiudUWkojYl`dbFHJQABIF9@[pM|yr}n_l{son}lY[^_MOazWjp}rs~l\aswpcwhojPRU[LKZ{fdxmrd`t||k|yt~a^_f[QZutSsx|x{wg}}hRfqYJ>MY[LQ]M^I=BJJ?HbzXƸķĺ{ks~uy{bdfiYXfhh{zymcxcRNagfUg`\fHFHPGBPomT~|mxj`q}q{updfkg\d|`|~mqZcxaO@KX\ON`M]P=AGKAI`|\vzyrbfw|ukqxv\]_cVVehd}uijYVhnl\pieoTQT[SQ`xb}krcZltwgzrjsrrwdep_rj`^ott`L[mYH:BLBFg{ljstpnopuyo}{wy~t`npkmmkikvq}}qhYQX`dlz}qvnqrojegssbk}yyx}kispha_eptkg{xlboxyz{xtvztmfZYcgioxcot_ZZVNE@FRJHd~~zz{{ny}~~zvtz~kq~~zqkkeXPC=J[iyʽh[}bXZ[WRNRbfZfwnsyuomnou}eeqqnmq}~˼xrr|ket}~|xz~zvrimx~z_ofRNOMIEFTb\`~{~vqoptysuarkfgfb^^fpbS_lpsxyy|}sm`\hryw[`~jgjkgb`hundu~x~|yy|ntzuoloyuζ}xz{gl|{xzxnh]U[dfipw}syogpcb`ZSNQ_aVcxqw~}yy{~{vqyyzywxwvxrrod]TKSbmyſp_|eWVVSMIKVXHLbqx}z}}||}sr}|vpmqvsd`|wstjfu}~~xqfjswzzflZVTOHCERZPXxxsrrxjhuwrmjmso`Yxwxuqnq|~jdrzzzytnnmd[RGKV_hvp^{sddfda`dsxjm~~nkvwsoot{ykj»~jcmrqpokimplie]aknpwdTq~`RQRPLJMY[KPj~z{~yxy}tn~|rmy~}yuwukcYMOX]cphYwpddge`]`jjWUhx{sijpollot}rq}~ysoprjYU}{wyql{{pqxz{zf~p\VTNGABLPBAYp}}|q[Mkyifhfcbgtyh_kuvvwtprrkd\POV[]fsj|olnnkiku{kbq|zy}pdnrngbac_QEaz}|~xoi\VZ]\^fp}tY_v^UTSNIHOYPDRj}|hozx}}ggrusqmhde`VPG?FQYcr´ºsil][ZVQPVb_NPdrzwnouuqqru}mn{~|xvxyp^Zzq||rqxzz~iRbePKLKHFJU]RMay~~u}}vcO^slmmjfgpyn]botuwwvwyulg\UZaelwnlynmmjfekvp_btzprwwuuw{oo{}ytqrsk[[{r|~{||ung\Z`dejr|q]nv`XXUPMOYbWO`w|p\fvwxvtv~mlw{zxuplmh_YPHMW^gttgqa]\YUSXccSQcs|vtz{xwxysmy~zvwyte]||ls}~|}~yrmd`gnsyv\bpVIFB=9;FSOHYttz~p^dtsrolls~xecouvutqoqnfaZSYdltj_}pa^_^]_guxjj}zwyxsonns|v`[gnnllotpc_rx}~{uofbiorwnXet]TSPLJMXbZRby{~~}|jy{yvqmmt{n[]gkjihdcd^WSKGP\eprruhdb^Z[cnhZ_rzrtyywwx{}imxzxtsutiZa}{{wt|ulnuy|y]Rmy[ICA=:;CRWLQivb]vrrpmms}|h`iqqpomkkia[ULNW`gr{nohgc__doqb]l|uqtsonmnt}u_[gnnkjlni[[{{undbhmpv}cShhSKIEBBHU]SPd{|}i_{}wvrnloxyfY`higfcaa`ZUQJKU_grrogda]\alo`Xet}|rnqrpopszlhv~~{{{ra]~ry~ysjgmqsxy^PgcOHFC??FSWJH\qq\Utsnnliinyxg]gqtuwvvwvpjd[Z`ejr}xjngeb^^dno_Wds|wsvvsrrtz|galstqmmkbRNpxqfbehimt|o_szd[XTONR]aTKYkx}wbUm{uutqpt}n^bkmlkiggf`ZTLHNU[co~vvjfb][^gm`RYit|~~{nv~yvskZL_wwzuleglnqxoWXyv[KFB>;>HSOBG[m{p\`}{{xvw~mfouvuusssohd\TU[`enytl~lec`]_fpo_Xeszwsttqppqwwb^jrspmkh^MIkqn}qdadb_fnz|oaqvjifa`bjn`PUbkrzwpyuahxligcbenvpbbnvy||une\Z]^`dlvlt~|pjknnnqx{lgq|ykXRlxvy{{|}}wjo{y{}zsoruvx}vhtwmifcbfnsk`esnep{mbgsz{zywqcV\{{qopoory}pmu{~zuohgkosx{|kdca_afnoe_hs{vkfirw|zqlhb\WPMPUZ_elvy~pcisiecaadjqnehs|zrns~zz{qfj}}||~~{wjaZURTYYPKR\chmquy{yvrmlpstvx{{w~xnixwrnigjszyy{vrlilquy~~|smhjt{wxvsnjf`VNVm{wmfeddehnroggmrtvwxz|{wtplnrvz~unifeejnmfgow|~tleaagry}~xog`YWY\_bflt{ztu||tjguyojigghkpqkiov{~}wrorz|~}|yskl|}}{y{vnf`]\^]VRW]bgkoswyxusonqtuwxz}yvz~~}ztmkx|uqmjikqvvx|xtporw{}vojfir~zvqmibYV`qxrlhhhhjlqrmiknqrstuxywurnnptvy|}~~|||~~|~|xusrtwxtqu{wqkfcdhnqwyqiedeefhkpuvrrv{}}|zwqjhp}yqmkjjjlpplilquwy{}~~zuqmnt|ztt~|zyvtstwz}{upmlib]^`cegjnqttrpnnprtuvwy|{wuwz{{zxvqkjrzvtrpopsvvxzwxz|~|wqkgglt~{uoh`\blrokhhhijlopmjjmoppqsuvusqonpruwy|~~~}}{zzzwtux|ysnjgegjlow}volkjjjjmprqoqtwxxwurmikszytpnmmnortsqqtwz|~}yusty~~yuw}xwurpoprux}yusohddefghjmpqqpommoqstuwy{{yy{}~|xtsy}zwusrrsuvx~}zz|}~~|zxvrmieeipy{vpieelqqnkjjklnprrpprtuvwyz||{ywvvxz{|}~|}}|{z{|}~|||{xvx{}~|~|uokgdcdgkou}xtqppoopqtuttvy{{|{zwtru{}yvuttttvwustvxyz{{||zxurruz}zxz~}{xsommllnquz~}zxuqnnnpqrsuxyyxwuuvxyyz{|}}{zz|}}||zxutw}{wtrpoprvx{~||}|zyywusqomkhfeglt}~yuqpuyzxutssstuvvtstuvvwxyz{zxwvvwxzz{|}}|ywwwwwx{}~~}|{|~}yvvyyuojfcbcejou|}{zyxxxyzzyxyz{{{zywtsuz||yvutttuuvuttuvvwwwwvusrqsw||{}~}}|zywvtqmkkklosw}}ywvvvvwwyzzzxwvvwxyyyz{{{yyzz{zzywustwz{yusqpoprux{~}~~~{yxwvtrqonmkjjlpw|xvx{|{xwvuuvvwvutuuvvvwxyyxwvuuvwxxyyzzywvuuuuwx{}~}||}|ywwyxuqmjhghjnrw}}|||||{zz{{{{zxwutvyzzxvuuuuuvvuttuvvvuuuusrppruz~||~~}{yxvtqnmllmnquy~|{zyyyyzzzyxwvwxxyyyzzzyyyzzzzyxvttuxywusqonnopsux}~{zxwusqpnmkihhkou|||}~|zxwwwwwxxwvvvwwwxxyyyxwvuvvwwwwwwvusssrrstuwy}~~~}zwwwvtpmjhgfgilpt{~~}}}}}}||zywvxz{zxwvuuuuuuttstttttttsrqonoqtx}~}{yxvtrpmlkjjjlnqv{~~}}~}|{zyyyzzzzzzzyxxxyyxxwvtstuvvtrqonnnnoqsw{~|zxvtsqonlkjhgfginsy~|{zzzzyyxwwwwwwxxxyxwvuuuvvvvuuutsrqqqqqrtvy}~|yvtssromjhfeefgjnrx~~}}}||{yxwxyzzywwvvuuuuutttssssssrrqppprvz~~|zyxvusqpnljihghijmpty~}}{zzzzzzzzzzyyxxxxxxwwutttvvutrqqppqsuwz~~}|zywusrpnmkjihgffghlpv|~}|{{|{{zyyyyyyyyyyyxxwwwwwwwwwvvutssttuwy|~}{ywurponmligeddefilqv{~}}|zzz{||{zyyyyyyxxwwwvvuutttssrstvy}~|zxvutrqomkjhgfefgikosw|~~}}}}}}}}}}}||||{{zzxwvvwwvtsrqqqrtvy|~}{zywvtrpomkjhgfeeeefgjnsy~~}}}}}}}}||{zyyyyxxwwvutssrsstvxz}~}{zywvtromlkjigedccdfhkoty~~~~}||{zzzyyxwwvvuttssrrrrsuw{~~~}|{zxwusrponlkihfeddefgjmqu{~~}}}||{zyxwvvvutsrqppqqsuwz~~}|zyxvutrpomkjigfeeddeegjnsx~~~~~}}|{zyyyxxwvuutsrqqqrstvx{~~}|{zyxwutrpnlkjihfedddefilpty~~}|{{zzyxwwvuutssrrqqpqrtvy}~~}}{{zywvtsqponlkjigffeffgilnrv{~~}}||{zyxwvvvutsrqppppqrtvy|~~~}}|{yxwvutsqpnmljihggfffggilpty~~y|{}w}tlfZgfcps~|}wyrnh^kinuw{sv~y}~}upgaoo|sfc[_SRPQW`s{cEKbG`xvy|fP[rwvxvb]cf|zzteih^UEIMXalyywkw|bMFJPQ[]_\__az{dQLo~{{{yt{s]\]v|thduy~{yzqc^mosrs~qbxz`K`~vtopmhgnz{utyvz||z~pe}~|wwo^b^[gloVDQm}y`Wwpprp^vqyo\pvWaydcmoeWg{x}pekOhjrypdQ[{wjwo_l~{zp[gyikg8SufSd~xvyyjYV]^\usuwz{dht]{nnlcqq[Zirs~rwr{sttSuZuy{ruw~d_dAWMUdjaKShhXT\ZHDr^Qav~o{ko|mmq^XeTeDdscapynNh``vioznrq}|r|veO_~{t{rzUuYHRj|kEftg|h|wvfqZ\\v`}|jIhrjQe}mbl\}}o\pxHQdxrdss{jshpqpx{uvierPbR[TU]cmezz{usXsmdasv|||hrgUlgOQplcghr~xrbd}r|¯|^pRPrupqofxZGTVv^mmz|[SbM=D[daJaQ]gwU]uzegmzysqor~[]MTUQ}}cqy\tn}~mxz|vvqt~_tnmy_Lrl^fzwy~]Y\f]HXk}iiddaqaxci|kyYol]UOm}fapuoZ~vZb~Ykb_nl`s{dmspxjxvyherZzggE,GQ`G?k{xal~vXduverygSUitt`l{vpyqozm]Yex~j|{rvl]`pw`šaeR`scfIcqYJTfgS=MM}|n{cKfxpnggfvs^{zj~t]anwsug_kwksq{t_Xwqmvjhmslzu`qo\L|_[`[QS}`lnTQ}~qjYv|~xpu}{PWsvpRP}zmlc]Okweednu{k{kFfyj^g~vdrhw}xqzd}b8TTF1.ebhpm|pw{|qiquz}}qix{bW^_{p~s|rchpjblix}~o~{r~[>b}rqz~vtkIIjmNSCXqi{dLGuyvscabgkp~Ztp]{pȶ~pwoixwryqbRbgWMon|vswmi_azwinYr{}RVuoi\QEWuabYlm|ppyiYvuz}egvwzǠeqvdTocQajfVsweZRx~nHrot}{kni{e~gmwszwv|g}_sq]A-JoeSMI[i[qgV~wwxd{fjOqzsupi|xt}w`wtiv_Wnsa^pvz|}xqo{uffmouttiJqe\[OUcxrrjZjud_xi]{f\z}zzhtfacOBX|uw|ynjpz\W~otmM^mhY\hWcybZHttcrn~ptjuftw}vxsoitsx{rpxęw\dyjj]OKx{kU|{xyO`pyo}vyte~mwquzxwjfz|VdhEHleA2BSWGqzowv}joowyff_gbdlzZzzpxqUkn~zvq{g[|obls~|t_e}qcpV|`cUPL_xoZa|siv>r{ssb}d]x}zk}uolmx~~x`IM]ejt~^~{{|}hsmjjierg\XhQ[}lOmssjpyrZo|kl~rr~z{ymv[yvzn]hpm`y{qsrzcX~mvm~{Takhqruy~{ryszVxs~kXTti>4;;8J}nl||wsbqzkp{{|Xglb^_tq|zqr[kypgdjkyuObvlorrjxvow}y|d_~_swk^_lpaZo{rAq[_Xt~l_wpzwukhs~}rjv|lhiaZWjpY|hoZvh{zhmwVTbmTevccuox}dQqr{mq]gn{~}~nuu{{~yu}dlXzj{Ŧxkpmepgo~WW|}pq~pWKV`hqshqq~pwqpuls{[Y}fRPI=3Ub_efoi|pjgpv}Z]{vgkvufhutpu~rbduel|myohmwxnUnezp}}xurfijpw~jX_pzHg[hjefhaXmgU[fqxnxrkqzucm~tgkj?hr]wqjtrwvtY\jgg]Nk|khhHTy|zonkoylqrnc||zZqmq]czjuswcyvt~x\z_fhZ]pylnwxrt~v{qZ{gtxsegxOJVZOWoYVFWlovls~zxwj}iwixwa`o}upygqo^fr`}wfvYl{qvkti]sVT[ttw{}mk{~onux{|tX{[li\fs[D]iZ]\U\~qtqsxwsqrwShtO\dkrw|zzos{}uqfaghcossqv~e\sxnqc}}vt}~[dt|qhu^P~rmoajj`Zt~zzxbr}||jgo_by|obalx{ywpbgnu|y~vhXchZNDGnjdH[utyrSoxsyqs~}jwxto||vruw|~|dzvtv}opul_xsitxm^L}x`acfjw{xqeosQepzkV_n[FgaQUY[dw|s{{~yvt~tlu\eh^b]_jmwwzh`{cYY\k~t\{jLguwzoszop~ytz^h|vn{vx{z{ubz]dV]gf\[tzr{k|x}buqbeyzpupaajqu`olnrtgnuki~dZ]]Zgr\e[^||wvtgZhs{|nsxt|ucquXf~zzuqsvkijvy}vpdv}}r~t\{nVX_hqzwih{zsty|`}kseemcMQvxn_MFKd{ue~rvuou{~~Rkw`opifltrlru{uwzuricyzqq|UZ|tnoW}z|uxuwusmt~lPkwzeOPRQSkvchy}{vnrt{}vt|s]fyqemtZzqvxy|{ordsu\bo]WV]{smPKst\GNq|wwsg}}{phvxzcvnvpjw|um{|tp{q}ybmlq\bh[W\fomdaekuqubk|yb_abak{oRL\aqvfscjox~{y}mtp`egox}ve[an|na{yxohkv{qds\d~rs~^ktms}{tzlx||onvx_milmbd\NFXwvbYUWl}|x~wvzr`lzlh}T`koyxx}{tokwr^j{sjbbwtyq_tyvvwiTObv|~szrztznvx~Yf}}u__bekvsyxz]qx}nY}|lnrpjgcZV[ceelvi~jxzmic]c}vYUa_hzp_Mou`cot{aw{piovsidfimpmaqx~}p|vqx}ovbcw{xiS}xljnvxqrx{sk~~rg[{qugRRX\_l{ygXSTYkxx{~m}xwqhrwRtjcmrx}pkvxnoidfxr^aqxuo[ur\IKe}}{qljjpztuw_cjvlgc^]kymkxx}cmsoy`rxgbfny|nZPRX^eqcS}{|iimmlwxng^ajkdev{~zk]e{pwrt{{qq|taX^kqrm`Ro|~fn}vryodm|skanrvuuy|xtw~~z|w~lru{xasmws]UPKOgz_OKQfznw`n}}vsw|~{u^omj{rt||smpuy}rnmikzwdmvy}df}u]KNbutcog^w~yyuy~~{rejiz[R[dl{y{|v}~~e\smjggkmiea\WXcdPnlqmb_duqjkvgot]d{ubV[r|~{sv]}~yj_[]djfYhssupken~n{fg{lr|ht~yifrxmjpxtm~vyqY}wtlZVUTWew|tjd^\gyifr]d~u{yy|jtq{}r|rdcltyysfnoiilwvyysum_vzk\\jwxsqqggsZb|}{wx|}qbr{fYdbXh~sqyuou|}pfvkknprncWV^fikgJTykfsy}~}wqqxtyzU^{rqdX`vyutvsvsio{xnnrpi^RJesqxf^airz|syrXxshu|snqpmmmr~uyxbgj~iWPS_s~l]W]mdcaK[s~{ro||z{oyuwtuskgimnf_zx}vpruovqtgpye[cww`Qa[Sxtnpwwt~}thts~tmk^Yrywtu~zs~spuxpdXOKKZqkkgS_jqxzunrvU\txv|y^Oe}srrk|{zsmvxrklprpeT]xkjugacl}]oqfmqy}evy}yqzn\]dflyu~{~ukwzzkXMYvS>ISWWQGDTqrlmuwfm~tg^o~{pdg~g_tziZTPS_nw{zra`bDLqupnnpv{{vmb\hxxt{bPYt{xi\Yg~z_r}mutfZSONQ\dZpxxutvwrjgkuzh]fw^HK_giwv^Wh[Vywswoedfo~~qzy}yq~u~vkknr|~ob\jvfak{zuz`bwpmpz}eX`_U\txvy~~vm~}cQJOazzrov|tgYKAFVgs{zyx{uryzmvuy{u}snoxxc[d|zssdLJ`vxk`mmTcth_`hpvwqgbchow~wqnp|dR]n|}k\]lzntvsuymZPPZkscjs~zpfdiqvwuvvu~vg[X^pnbjzkQj{tqrv||uolsxypvvysaZ]i{whbjxfajyx|~j}tqqrrokif\Xnf}{~sfi{~uqfYWh~dar||r_KEOaovwpgr|q{~||w|~j{pjhho|ysrxsq|}dNVkqosytipt\dtg`_elnhbagt~{|wmjlt}|qgglpw|pimx{{oxrxx_Yempwwfrmqxsiglqsohq{w}w~~wxhckyzsrqnn_cxnmpsurjfiswyqtprylc\^isrjderzvmq}ynxn{ztsttmbZ\gyo|v|xqxzvrqw}fT`{velrrnh`Z]fmpqlccsi`oiv~jaajv|vh`dvyx}sidghky~f\mych|vspke]WW]frz{ypmt}}rcZ_tzypio~nk}wmvhgmjbppswz{{{ywwwvrldaux~oowvyxnnxrblwgj|}topqld^^etx{vqvonqwypaVVawmdx{qvkezsia]^drzyr|y{ub_iowou~~vhYS\n}{o`\lfSj~xko|zsntsqtyvk_Z_r~o`]h|ulpxsW[{xy{zrdWPS`r~vuzvv~yj_^g{xtlkq}}ws\rtf^fzwc{|tliox~|pdmny~rknwudhujjvx|cX{sn{~rf^\^dpzttwtxpgt}uqqx}k[SZoomyv}|enqfbdkuvhs|vy~y|ywx}uf_g{n_WZfs||plscQi|trwulu}xy~|}}{obkm^Y_o}tfbhrzsnyuw|sokmtni{puxjipyukhn{yjci|zrrbiPhtrry~vpnorvzxzrlknstmcpq{xmfbfq~g]j|{zkqncsqipzwmnurg``ivxjswqopng`b}|}gafoz}rklta[~jcqofaaelqrokiioxxiu}omrspq}jabkzzvz~srtri^Zdpmvk_YY[_adgf_`tiwwmhhkoxseiy}}{pigea]_jops_X^gifaadkuzo]n~~rgbhtwpqjhmyojrqfbjure_aegfddiq}savxxxh^[[[\^ivgaaht}{pkigglu|meisq_U`nvywgb|szpd^]^_am{tundgxqkkjgfpqkihfcadlvjm{u{wfhrpfcm~qign{~ufZXZ^bfm~|{lfdc`\Z^iw}okx~ysnjggovonvyvsidccdgks}oqwpc]_bgmuslymtxrpokffzukkr}zqg^_nzvq~zrle_^air}p{so~vruqg_\[\`u|pijs|{wy{qpsvvtrwypecjojhwxk}prx{}}{wqlgc`_i|qbh|{y|wmilquvtr{}tolfa_bkyt|ysx|}|ywvqe_grs{{y{{w{}bV`lmhcck~u{tmf`\]blyvo|~|wtsrnjs~}ylegnsrnijxv{nnvscY^kyvi}zttuwyxuqoonmjl~{zzrfdluvkcl}}sg_^do{zls{yww~xd\dlh^Zi|{}zz}~wswzrorvzzywx|vkcete\wquwxuojgilmicg~zyz}|{}t|zlddjqssrs}|ukfeipvsgr||||zunhfjonbZpxxsklme`foomxt}rhccgnpe_}vo{}rmnprqoms{~}oc]_fmqrrw~|reissmnnei{|~xphcbdhihhw~us}}|mb_bglqtswytpnpqi^p}zofhmgZV]i}z{vprz{|}tb^hrvwv||yvutsnmyzunf_\]`cfp~uoq{x~raXX_flor|{|uqswsc`~zpg`^^`cixvv}~q`VYfqmaax}yvtuvm\`{xu{~rb\`gjnvu{}w{h[WZ_beir~~~}vlkzx\ay}ynbYWY]ai~}tz~m_Z]aegku}}p\fzv|{pdYW_ihaoxolpz}xzp]\glgdjst_kqluyl`ZYY[]f}sps|w|uh^[\^aelz|ptn`rsqy|qf_]]^_jzv}sx|qf__``cl{l`tvfgnw}yj[[eg`XYk{~yvy~yuw}uvyupnpqqqr}wtbcir~vht|pwzrjd_[^pyy}vz}v~nkt}}yxz|}}}qe_ckt}xgopjqz|uoje_]jwwxz~qrz{uwnb`dlu}zjatwgl{~umptqjda^_hx|}uxwwysupffpqcaxnaagmrttqmieb_`nwp|{ty|v|qfr~smmqxyk_n{f_bgknpqlbYZelmx~otxxssk`rxssx|wd\o~zqidfmqqnkjjhdakqkw~|w|~z{v^czyuz~nbsmbadhklkihhifbeuwwtou||zx}|c\ryx||rbhvfabhsxn^Ybjjcanlgry{wxvzn[g{~uYUvjbcfghfddgjkgdkufmux|zutzyai}{}|xj^nuhfhiigdbdikdYZvmepwy}|y}}~m\hx}|rab{a]kwtib`bfhhggqscirtw~x{uru}dar{}ug]mxlllkhd`^_beeei{hdtvs{ztrw~{mpwx|}q`\vqlmnkfcflj_WYcrxeiqqpv}|zzxquk`lvy}|tjm{|pmpqnkhc_\\_bejzsgnrqqyvvpqx~dcpyzfYjvpqsqnic^\\_bfn|wlv~vmo{}u}snt|s|wahvz{wb[qtplfemupbXX\`commrpmq||qy}truz~~}~qco{}~d\s{ompstqjb[WXZ]bqzknror~zy~~wux{}|{~]Vqp[_yukkpstqj`YZbie]gzosutsxz|~}yotyvv{~gfwnYc|e^fsyvmd][ZZZauwovzxw|yz}xwxxwtruxcfyp]g~ofjpvxuog`][YZavrdiu{}xw~|xvvuuy~~xg]jralofjrwuk``gkeZU]tpny~z~~xw|~}{{zwsor|obnwfi|fagnsutoic^ZVV`w{ko|uu|~||}|wrnr|{jbp}lfxud`elqsrmhc]XV]qj]nuv|yqmtxlmvr`_u}jj}qinsmffmpmga\Y[j}okyv~sqw||zxz}~zskhouhYc|{nwoccglqtsplgc^[arzlp}{toswywuvy||wngk{kV^xuoldeinsusi^X]filvylco~}pq}rglyzqlpvlYZskh|uhbdimnljgea\]ixudcs{stwvtsuz~wpouubUeuopc`bfjlkigfc_[`ryonqvyuwwussx~wljsmRQk}nu{i^or_X\][VQS]n}|}}vlqzsqlx~sldrqhjku~|oSUpeY]_`blyc{~qhdYtjnd_`hvvodjzf|~zsnnjlYgkt~xzxxk~{gxpixri|yr|vXDUqsxkktw|p{{ovvoXt|\i`js}xvrv{ylxuyxrYdn`]zroYZea]XA?V^JSpril{zyxll~|t~{ogM:Dmzpw}v~uu|gpn{zxs{vxl~hYYhkz|lfavmlk}vi_`qXUXe`urj}]Wmtxyt}twysUX]piyyzadSgbSos{{iiQ[W|{mgejdvnz^ftpoqxUrxwqnlk~wyovvztygwaixm}d[rkm{ynacmlzTMGYZoyutw{fo{pkexv}c{zr{iaOTwlesoq|x|}~xwku}pjspq}vWjsfXdV]TfbLRb|zzg~kmk[bvw~aehwx|~m}zdX[P^QZdtm{ig_nctc]bpvz]trizigr~eXYfktht}gti_}z{t{~zt}whX^\fzvsh\iI@Gdrtfw_{y{j]b|v~qx}kKL^hi|p{t|}ss|zwpitap{r~sxhmyWhjbQZ\_`ZbSdtpt~rxd|zyhjn}yxmqsx||wrtwnyjXgdJFjolxxqycSayhjp~u{svitjare[dkV^p~{r}~}lddmfooxmMN[e]t|c\UgeVftfhpwixlxvmx{qe||lU``JOvemx~mq||poqy}s]qktnz|niR`kf_jlULRtgX]oqt}tft{ntws~zmavwrrd{hUZQ=fw`wyxjfgxstxq[stvqthg\VTgsiqvoe{ne~|yr|xyqweu}zverfNN]iqzwe`eWUvfn|zsmerdctzw|wknbbhKW}i_qwnysoiluoyyrzyrmon\\zt`]W[\e_JQclmo{utwrmlfiuxmpm|{}~v~|rhmbSXDDksxt{vt]fsfltk_dc`ieTf|}~{i`u|{zqtpem|wkjym\huk^oxwza^~oc{xmpxyupugh}n^jum^d\\eVYp|on~~x~rj~vwsp}xvqeyx|w}odqvbPSf\KZr|sumzz~w~nYg{ymnzypsz}u|zcdqkSKPepag{uvsppmvmmburfky}tlrphuukp|xgqjr}mrr|{u}ot}tkygcfks}rh}|xnsw}t}}pw|dZovrxmrz^djugRL[dV`zzei}}eZy_kpv}piztmaQVikdokl~urrmhuwoxufevzzs~|zyppsr^hpdX`l]Qf{qjkuxo|~~zh\o}x}r\\{xgnmhj{mtzklr{u~{w~j{y}~{aewrhdqsgms{ml|qnrmuposjs{zwawne`ZKPgrhnsq~y|wwysr|utongozhery}{{llkbVgqUu{j||y~pe{`hrvxrwoqz|yzpz|w|}rxooqpdSX`Y[}u~yfuls|pgzzlfospy}l[ilnunvuv{tn|pwfvf|x{cl}whnf^[iyx{z{~~oq{}tn^zpo}jo{|eb`bZZpzeYu{rw~onyxkh_hslm|yxno{~jkoxqce|r[{nktpt}vyvfi}zzw}n{yv{|uwqwuef}{kle`IJdtghv{}z{rqp}w|ni|gfrrbn~ogz_rkhz~t}wxx}||wz{tjyy`de_boyexwpp{}{inzsnyis}hjudflup]`jmp{{|wv{yyeougrcWu}fikwt|uvtesrsvmpp|~|pzxtecwqs}rldpjz||~|}}n|{qqqa\VZTbkbRj~r||z~}gl{wo`ovym|vizai~t{}zl{~qylu~yty}qwwc^onteiwf~}sv}xudxup{qo~yvdburqxoeQaz~ktzyv||v|sxzsrkrwfusgekZckzzw~miji}mi|oumoulpfwsp{zf|{}}nxrxzrmt`]hmjNR`e^sz|~~yxyzqrpwtqkmlu{wwml{qumrluyr{opzwxsllvxlxs]kmloqhwy|vycxuiwmlzojvlfalqfpsh]stxpnipqlkqzcYgibsrvba}{vl|yxwktnz{txxg|hlvznrxuuy{|s~l}~r`dxoth`ZNbnj_ndkz}xvz}mz|zdvqgxogyslvr`lozqzyx~v}t{w}lsdfzg_exeXa||~}{wyq|yqroumqbkwww_foof}|qm~}{}|}v}yui{}~pkuf_jxgj^dln|zuu~nwzyir|t~tgwi||rc|sityr}{~z|lttlo|p\c\j\\hiW]suti|wtqyyvu{trmxpyg_liqtv`j~y{}v{|q{x}|xts}qhjqhUjidiznqx~x{rz||qag}sytoq`n}~knovqw}{~}{z~mzzy}|uqjhypjklT`hv}mz}~pwqnxvutxvx{{yk{xq{mhs||tx}m{rqksyidfWfchdRZcilpa~wq~nyttsnt{qouecr~lqenqmyx~{sv{vmmujtx\b^iugahmi{}z|moupr}yjujxqpxqi|~h~~}smwy}mqxvwiujyu`T_agwlykp|{{lyvqm{iv{jtuevtpuvsz|yx{szuwv~l]\j^ee]bSguf\|phy|rtnt~wssjf{wspo\lku{ny}|||wyuvylzaxobm`maej[Yl{||lulvnumk{sf`{gtruku}yv|o~q|yvydj_Y`qeikgwm}v}x|y|pn{ytzrt|ouqqwljni{~p~w~y|hioebjfYdZrmXeyhbtss{qu~qxv{slzrxe_kclslwfry~y|x}vriumtqomhucf]Uechqudusyv~rlvws{bsykl}ysoeyo}tq{rmxx{}xkh[qohek\ilo}iiy}~~wu{|l~y~~zsvtsb`pyx|yt~|~zwv{itdrqe^c[jv]hiickmzoitj}wu}z~yzt|ziqfchobpdm~xxzx~nmxf|~ogtxomlXe\ksdZ_]bwp}|uywycmqto{yqhwpnudsqs~rrr}}{vxeptnykbcjbugbimxrzs|x~np|~u{cgropytry}|}zux{xn{hoj_btf]mjo\fuwjqkgym|xml|v~xz}ttdtnhei\pyuvjwy|ylqk~v|lp{nvwpgqdtoaeWS[d^tir~ufo{ntoygpvrtvcpeqz|vvz}~}{{{qy}rknpgelaXedxcfrx{u{~xvwsfuizzqltm{|wy~z~sywpu`puo_dhpc]xmnqqkxoot_kkp||}zyosxitjbcrowhrlq|~x}yvz{yr{xqbvpr|koqstllbR_ZX[aXnpysusypjmqlukswsmudw|vtloxt~}you}qyqhtk`WX^tYc{onpsxs}xvx~}v|nzqzpmswq~kz~{uzv|}fxvtp`im^Xukttiqwvww^j`g||~~{w||~~m}jhoosp`khry}wxm~oejgmxetmlyzxhWfT\^WS\_uj|qyhp}lnqf{qpsso~yyionloys~}{~m~wtt|gj[WcfOepgpaisxupzu|~}}x|nzurnga}w}w~~t|z{{whfziY`hjzfquowxojgZupt~zzz}{~zk{mz{fbulgtn}}oyx}trnihndooe~z}}vim]]fRZWYcn\k{y{yk|vkidzmnxls|irkbpmknqu~tm}pow]\odS\deiTgjhpspz|{}}xz~~zryoaatnru|~}}|q}y{}gyeffkllexlo|mlhehtfxujwvx}|u~xugtwidevrvlx{jrqkgibw}wwrpokp\^cSfi]Sagsfmw|njmlj{gs~}~yq`thdkfix~~~zpqn}ic}i^^fc\P_bXldemv|zzkwzz|}zx|yyaiqobdqnp}x|~wwvxv~qwlpyrlmjo}foi`iqtzsfyjmsjbivx{s{xkipxq~tzt}s{~s|lrliltuxoymprqmmZnr^ZYd`UXyrz{qwqmnins}ytwpbtccqvslkt|ytw~|t|kocniZSVQ`cRggnz||igxiqutw}ysrozrtdepqcrki~w{w}ywwxtorwroongt^^pnv{ywxevq]__mqglrm{{wwtyu~rty~y|zus}sqmuvy{rtybrtoqqpzvfkYdfUR_cr|l{uuuolqqx{uwxuxhe{puldp{mxu|~x}zgta]ZVTVPa`[wvwvsmrcjwfpwzzzv|arvlb`_uli|x{s{yvuy~{|qwsng`Yldh}wyzzyzbgcYoj^fwxx{oz{wwwuojxs}xx{qqzpv~vnhewgivns|{lXtpSW\befh~qxyqvtw|~}qq}uwxwxzps^qwmfl{wwu{x|{{xzvdxsquhnx{~fv{tiqtx~}~v}tqtv|_Xsigmmr{wv|v~zoghksq}nu~|shsxg{}|wuxvvz|voji|kosqpofrqgmo{|nlvkvnrx}|vnjki|xn}zeluuvbgbYWenyel~|}~kx{mx||yso}iernzt{ums{tzvf}hraRVTV^dqgnvquyy|vplktyodqo|xyx~|t~ixy{v|bbih\U\xrZr||}mighkrq|m~~topq~w~{|~a^{vxvtk`YU[uks}dxuogpwokt^err}rsononzwwrTcp{{iejlaVo|htqosvvykpxywoheapk[pvqysmsiWq{knmnmifs_arorv{qxtsykdjpyqw~tz|wttsemmmzyicll]joippyelsuvwu{n{quyuqonor|z~mmm`inyqfacfsnzxyxwkkyukeamr\jrvyrspot{t|siwocxy~tdgpnxe^rpqtw|slkgdcbevhn|v{y}~x~un{lrrip}xme]goZjsuzlsockqtvtn|{uwzsp~~{{~znYhfjtzobckxr|ubuqwxwvuvgay|tvspqtz{rz|wu]kzywwvutr|o{us|nf}okeccans`t}ux{wvooo}iyz{w}~shaqj]qsu|su|ededbej|hpu~yv||xymr{h[iflvukebeudjwygtvnwwvutyigroyuq{xywlorW_iiuvrtvtq~~zwv{sr}uxvrmdmxmxtr~uwwzzt}zkdwzfv{}{yv~eqx}wyyicdcabdxym}~~qtr{tsstuuwgxwvy~qheeukby|eowddehmsrxfh{uxqpxvvqyzqiq]Ugiqxqlgejxvu{{tivwvurmkvb_w}wwzp}}zpgaanZXpz|~zx|vp|vvztttmd`fvba{zyy}ohiksij}}xqqxgu}v{fvrbdcdfjvnlzx}wuyzxrwwvrnt|bdor}vojfdp}m{|zmdypgptqe]rghv{wssx}yv~wj`ZdePar~vsompvau|y~zz}utoha^nqd{}rvsrlf_[Yig_y||ywucu|w~ndcjqlarsn~yuw|u|oknpoln{jewy~wlfsyky|qbutdgknpos{bdvy{upv|syzwrmhllRbxuqmmpv~{{zykj|tmabnnql]v~uy}u|uld^YXgcXs{}vl}wz{wske`^`j~toxy~wzxv|lhd`^_l|c\o}umt|iu{lzokun``k}}aiwz}wsqrxvyuwtolitkUeqzxrv~{tq}|zveq~prtrlfese\rz{xuy~z{xtmdZ^pggps~|{|~rs~{{|}ywtaktma^q{kzsshea^\_sndx~zpwtir}|ebdgjmvrp}}~wsr}v~qyiijlniig[ozsz}uprvzgqyeclrnitjZluzywusu~t}yvrkdckXVnywqv}|||}unuqlfb`js`i}|une^bkvjQg}~kxmk|rgaciq{l}|{s|lgfffiq~i^mpt{yyz}wfvuhlopppzgeryzwttvvp{rvth[^uxX\mw}|~|}{s{qquofaapl[m{yz{~rvpi`Z\mf]s}~{m}wogdb``i{ro~xxxxt|iejpj\athgtsv|}xqu{vxxcfkopq}jnywyws{xysnwjlnnliqmU^mqyzx~yjlzrsrpmksm]u|yyxx{szpc_fkeiaVm}xzgx{mgc`_duof|{yx|uha\Z\evkcv~yoqz~icccejwm]s{v{{ynw|rsj\YboybXjrx~zx~~ht|ppqqons|f_qyxv|}xk}sqojb]akYXo{~xpuqnha]jskt{{}{~yzychnfXTbrdisr~w|gba``dtqu|sxqw~rs_]]^`cqvcjy{}yxt~pj}sfgkoojnpi{~~xsrw}{luy`\cgdajeTdv~{|{}wju|rqojffqj]r~xr~{~p|yle_YVZjd]xttshb^blw{dc{ys{dvseYUX^lyhj~{yl{vecdfjp~mo~zwwyngwnbcdcbdqnXbv{z~{jowqsma]jzam}xuuw{}tqvlkjc[V\j_Yrxw~uvnic^]iwjmy}tzvu~w|f^ZWVYkugnyzpyxvqbfooeau{oz|y|s}yt~f[]abcjwk_ox|{quhw}mknpppv}hdwusyzkf|zkhfc^\dfYm~rzk`clpmskav~{}urf|se^YVV`odd|}wkc`_agxms}~}qztq~o`]\]bkwiRey}is{uj`bmzocsz~}zwyxdsxkhhfccjnXWkvrpwurniittak~{|wz~{{ouxjd^VT]us]bq|z}~notmb_k|rl~~uvsu|f][ZZ_nyhhy}x|yqvfdfikp~~ilwx~{zzx{wlk{pdehga\fkdntt{zrudhpropym_n{}{{|s{|qhxtjfb]Y^h[Vl{~w{qkgdepzlnysxp{eZWZcimgVd}~nmtfabgo~lq||wxokzo`^_adkxs_gtz~lnvllmmmq{p\j{yxzvgpvmg\SXjzjTbtstyvsmgelxldw}zy~xn{pc]WST_mc`t{{ujfddiwxmty~v~unwirr`^ee[Taulhv}{jdgjloymfu{~}wtwwm}mcsvhcdedems`^r~}qktomjgemtjr{z|~||qn~dUV_dbfhZbzznrukfccgv|nvuw~w}pm{q]URRU^psdm~{xwyjghkpxt^o}xuxq|r_cxuj]RS^lzq_k{lovnmmllq{n_n}wy}{zodq~qheb^]eqe`vxqvkf`^dxtgs}{tviplbedZSVhqekz|~lfdehqsy{swktxffreWUWY]fuo_iwowxoprohixxr||vsruvyslj{z`U\cddkqb_sy}povokhefpsci~~zus|z~ymqzh`[XY`pqfuzu{ue`dlswxkf}|}q}h\ro\SQU]lwidu|rojkmpwoo~{{yt{}p_`qj^\]]^cneUf}rvlb`k~o`o~~vvwz}t~}ralslgc^Z]in`e{x{qhd``hwuhs~x}u{qz}own_ZYZ`n|o_gt|{|p{}qqtoeduwr~}|~}xyspxm[XZ\^dqo\^nv|{swvttsswwfpv~wtxw{tj_lymb^[WV^lhguxx}rdhqsorufgzzu|}sm}pejk_ZXY`osfmzz}kcbcfp~zip|~~s|{jnzna^`cglo`Vnxl|sebitugs~~}}|l]jsh`_^]_gkZVhv{xwspmpz}lo}~vw{~xt~}{ysghyucYSQVeyv\[o{t~yvoebjyyjq|s{}{~t}z~|pr}p`ZZ[`kzvgp}|qvuifgimusbjw~r|}yqdnzpgec\W_rpgnuy}rinstszpgv~zjy|qe_pyk`\YVXbj^[n~{|smjimzxs|}~~~vku}~~}xkcoq_TU]egij_e|qp|iaaem{pkx{{~yusxuw|mdqte^^`dkw{jfvqk{rkjjjmvveh{{|}}uhg{}l^TPYjzv^]qvn~{uqmkp||mtwzwuodhwp_VRQT^mmak}}rkjkp|jlzt}zy~yiq~yk\amgbd`WUcxvlv|rfegjnxqep|}xsu|si{xi`owldccdhsxjfx}pnulhecgu~vw~}}x~xmmyoYT\cddki]g{|~xp}wnjilus}~{strpx{}}ynahreVQQTZhumbm{w{qoqtw{yij||~xusvtn{}vk]]t}lYPS^lz{ihxqn~~rlkjkpysbhyz}|{yyodn}vica_bkwpgvyr~xib`dqrbp}|s}}j]gokf_WSZjqdfw|tlkmq|tx~twziqwi\cmbWUVY_jreYfx|}wytnfgxyqw|~~wssu}ur|~{wusmnuj[[adfkupah{}xo{rkhegowndqxq}tuw}|qlzpc]\]dr{ojuy|~xtoimtutwuks|xzwg^kwaTQSZfrm^dsz}|zursuy{mv|yzq{yoaYgpe\YXY\fi^`w~mceo}~fcsxz|vvx{qnw}v_]qvmfa__gsqdlv~~pRhp\[G=LG7GOANeYVppcwn{|c~{uyyormB*fbZiamuah[MnfqM6zG5,5I<:USUƳ˖hdfRWtlfGgkhZhjpqarqvtq_`gjJWT#HaSqtf~agҲˡcaȰoC$-(?T7iKOoj+MrOyf|dJZcrWXfshMnjGn]>SYGʲseu|qpcjxbWwēzyKpeZLUwyiMQWjZdiNNeItI<|_y}wZppe|bz^dNcd@L^A{|itbZmwoUcRo^Huۛ]u*#bc]~ш[dWi]fhTAlIYX_p`yi]t}vZywb8Va}¤}xtuhRYN59iokxRtebgel\xa8<OwzuxoQkomydQ`SVxK8O\Vviќ}gktdw[Fb}Žrned`dRA&mnU}oahcY]zB>U]ak˖gVcz^]ucJ>`˲~rcgbdjRB[K7TrçTq|idof_jmo>;5(x~zTKowcyQnmWL[~i7AZ_uxt_q{^yjhMMyӧqtzG\LYV9;U?2l}z~Sniix_guv_;L3TJmtov_yd]OrT7P`gӧ|d_Vn|ZSP_=\cĩ}safqXzf`ZD٩grw9XIVQ67[LAN}u@Woc`qZYcObu^:I^mĽwlxYjZaKiMUˤxyy]P=SIYC0EbBTqėt|ojcsfS[KAJXxQXxx]aXm\dwhDF\grgzTVqRVJWRCѨv}}dd=WO^O;FkQXmɡͪqmcnfPVIaHNW|ɧokcpeVUOBIgrpYa}R_PpfgriMFRbyt{nSTfQbMQUKέxz~qo_ARU[L=GfRVQ̫δqmdne\TN?DkdvgWZx~G_DhdbkgQBL]smxaWh]icNcSֹ{ximy^@GPQG6;YQLBpŭoncmg`]PKDuiwnldQvEjoilj[GKZogrcNYV_kA]TtҾxrlp~dPEVSO>AX`XGdĸГtscngekJOINI=7GZXHFʱĨ~sdiifuOVF_dtrlv|Y]Khpgee]JKfc}vZOMXofBbjs˺ly]{sXRATKF=GYg_E}͸ɐuoakbpcMQJ|ogjukzxs_mAOfk_aaVDYifǮ|YQTj\O{u˧rli^VU?@K@;:IZjNQѽraeceSWRgkpux{ubiD[ph_ceSLjeqdOCUpusKZmeqYpxMX=JHA=HWmvRxݻm\f]nvNWVvjk{zuyxzj|rc}X?Vl_ZafRYpiŽteQEb{vwMsrǹ|bvzNxeEO4E>:9KXvqVٯ{dZb[wnT_hpq{wo}toyTC]n^]fmUiqyŸ}pyxXCAgytnnJqgcfWRLL:IfwkgtSnea~zaV|PGN:E<>J_ouܨrXZYgaZtf}qxwy_ji~beI?^dU_qleupŻwo{oSA]}shyisvzy[uunKs`>K?;;4ATcx޶ZYXbq\pvtuyfkfqb`>Wi\[ouhtkvfwnY?Ovp_lqf}py]ottPnjEIK<@6BSc}ʟ^ZXbs]jzjruxcc^xVe>Jb\UitiofǴyl}qfHQwwbkzmprdmsrRhkIAM<;19L\xyϛcZXcxdjlx|zhg_VmHJc`VitnlbɲrlxleGQsqZdvpkifouoYljOCRA=3;N\zԟd]Yivck{}ltzrdcYQjIF\\Sfnnc_˳rwyqkQ\xr\h||kgluzvh\ldP?PA7-5GWrrМf]Zmwhp}rzrig[TnTH\`Tfosa\˳nwvkkRZqpV_wyl\rrwxi`ndYAPJ934HUrrتq_\lvlkwquocdR|Tc[BR\R^kscV˹wz|ms^asu[`vyyXztuxi^na\CGO70,@Ljiӵ}_]gxulxxywclUqiZkKL\Y[kupSoiod]jr]UhsWnt}vrrZpd^Q@UB5/=Kgmz̠i_b|xo|yyvz|_gVZ~PeXBPXTdp{VnɮopmpemtjVcmn\|z|jxZhjXZ@GN5/0CVniͲx_^pxw|~ywnahN|hVkOFVY^np[ƽ~xyvgkjgmkZTjnXoz{|{knkWt\ZS>QG52=Pmn˧o^eyzxztz|ZdSVUYcCFUXdv`vŰt}minnpogW]rZ|{ysarXbmQZH>P=23CZtnf\l}|t|tuYgLizR`bCHX_meuy|xfeknoh^Tajs^|yvsg_mQkdOZFBS=8>Pl{~ǿyc_s}ospcV]EppJ_Z>GYctpŻtogfqtre]Yoojl}wsm[baRrUOU?EN7:CZwxóqv\e}wzqzZ_PV~eLiQ?NblyȾ~|~sxccettk\X\|xcu~y~~pm^WeQbjJUQ@NK[mŽr{wtaeh}qY[awwvr}yvqgQUXMheHNVDDN=GRrt´vh{n}kue`JSqrxYSjI:Skp|wr_aeyrYW^~xmyqsxswfPRWJghMLZL@OCISps¿~m{}q}jrgaFQmmv`NgM6Lh|r}{tbdk~w[Wa~h~vryuzeOTUJkfMIYN;ON^aaƮzz~txpI]ocov`QbGeiVLJ^R7DRWf[wx|xu]NmgfsvWY^CFkwd\`sx_WlzrY{jYer]IQ:_dUNI[^FFclxfw~xw}wYQga\lvbOdWNj|}fW_qjdt}qgqpOUm|yZHK1Kc\PKOaXBTmyrqƻ{zw{ytOag^bswXXiW[~~ypZVd{tei}n}ve~ZJ`t~jJM89]`UOLXbOHgx}kŵxy{|xV[f_[kxeMebVn~qzcV`v}lizwsnqlJSn{rTLA/T^YOMPaZGYvoȹz}|~weYib]dwtT[m]e{|}p~hXZnqjtvktjyOJgw~u\ME.N][RON\`MRss|{{|~|}wiZj`\_svYQla`qv|tym][ovmvvlznzWHbw|x`RG/JZXRNMV`QMlrx¯}{z|{~zn`oc`_ryaNkhaktyuun`Zn|wnupi~{psZH`wzvaWE0LZXTONS_TLi~o|İ}{{zx{{kfpa_^pw_Jih`brzuypd^s{yrxml~{~ss\Jbyzub[B4QXVTNMQ\SJeyj®z{|{w|knucbaqydKgmb[oyxvqf`syyswgjyw~wl}aK]wxtd`C4PWTVONN[XM_whű|~{}zw{{kpwd``kwjK]qdUgzuujbqz{xygiyt~kykPXtwtffG5OVQVONKWZOWrg~Ŵw{|{}u~nr}kadiwrUTsmWZ{s}ymfl~|x{zkavpynnsXPivriiU3HVPTTOKQ^YSlkuĽu|q}ztlq`acpucLhv`Nyx{qkiyx}~u_sqr|gteR^trlic8@UQOUPMK[_U_ml±yu|~ztzg}}g`divqXWyrRczvuumjo{x}ffxk|ofp\RdpjfkO2NRMQUOLPbb[ikþǤs{~~p|xyolr``dlujSegO|trpllwx~w`tqnfikWWjmfhf:?TNLTTOKXhcakqön~~nu~eyl^cgqvhVra\x{nnlmnz}xpcyls~`ggVVfe`i[3FQKKUSON`ojgmôĹ{n{mxqva~|g\cfrvh[|]k}xzhkjnq|~|{kjzi{y\geUWe_aiM4NNIKWSPRiunjqɦrosprviksc^gjwvffy^tqaijqu}{{zfuqkiXi_SW`Ye_;?RIIPXQQ\vzpinxlx{m|tayj^agmytdvjn|vfciotz||~mnxhw~[]iZR[Z]hL9OMGJVVRTj~zjqʥvmvoowzeptd^hkvzoj{isp[flvw|z}ukvkmgShdTSYVgUU\^YNKJMXge[r{||z{vz{riax~wwsqw{sw~gmoko}vfix}qzyrrnikltr^MbpO/EU[\XMMMR^ld_||{xytzyn`a|~}vxtu|}p{tbnkhotdl{yrvtrlgkltnYLflD1KV[\XNPQYeqdh~}|yvws{wfYh~~|vzwz~wpfcnfgr}ncpsxqupjfkntfPQn\6;SV[[UNSU`mna}|~zvuut}~qYXu}~~zx{z}rwq^hkciyzhfw}qxsumfhkpo\J^lH4IUX\[RRW\hrgj~|wsvsw{aQg}~}w||~~vrz`amddqqcnq|~pvphekmqdPQlV8BTTZ\WPWZdomc|xrsus{mNZ||~~yy~~zte]jhaj|yggyvusttkdimpkXMda@>QTW\ZQU[bknb||ztrwsxuPUw}}~{w~~{wh^ikagx}kdtxstrvlehnon\O^cEAOTT[ZRRZ`hkbv}|sqvuv}{SPr}~{v}}~|zk^hmbfvoco|xsuqwnfgpooaS[bIDOUSZZSQZ^ff`t~~}trxwv}}TRu|}{v|{}~~~i`jmbfvobnyvttqwnfhqppcW[^KJQURXXROY]b`^vó~}tryyw~|SUw|}zu{{w}}}jckndgwqcjttvtqwogisqqf[Z\OOSUQWWRNX\_[]uô~}usy{v|YSs|}zvyzt|~|mckogdtuefprv~tquqhhrsqk`ZXQSTUQTWSNV\]YZr÷~}vsw}v{_Rn||zwwzr~}z~rdjnjcpzjbkowvrstjhpupne\TSVWTRQVUPS]]XVlĺ|~~{su|yvmRc|{{yw|rw~z{xfhlldizqcejtwtrungktqojbSOV[UTOTVSQ[^ZSeľ|}~tsx|tz|YYr{|zyzvq}w~~jfjkhcqzkafl}|usssiiptnniZIR]ZTRPUVSWa]UZzı}}zrtzxriUbwz{zz}q|~yxqegiidfwuf`etxursohjrpmmeNFY`WSPQWWU^c[UkŻ~|vqtzss^Xiy}z{||yq|ux~hdgffbizsd_f|}vvrslhkqmll`DG^aUSOSXZZde[ZzŲ|zspswossZZjy|{|xw~yrysccecdak|rb^izvvqqjgjojkkYQairuuy~yu{x{}{~tkijosoihebfga`n|opuqkkihptokmmoph\WSOS\bl{{fL:8G[fntvw~}wx|xzwjginssligddhc^grottmkjgjsqlmppqk`WUPPX`gv}oT@8@Scjrvw{yvw}{|{ldhlrupihfcfe^bwvorvplkhfnqmmqqqncXUQOU_er}uZE:=M_gouwy~zu~x{}y}mbfmsvsjgfdde^_s}xpswsmliejpmmrsqneYUSPT^eo|x_H==J\elswx|zt{z{xo`emswukgfdbc]]p}zrtxvnmidfmlmsuqneZTSQU^fp|x`I@>IZdiquvywrz|}wn_eovyvkfec`aZ[p}zuvzxpnicdkkmtvqldYSTRV_hr}w_JB@JZcgottutoy~~wn^eqxzwledb^^YZp||wxzzrnibahjmtxqldZSTSV_iq}xbLEBIWbemssssnwwp^bqx{ynedc^\YXl}|zy{|uojc_ejmtyske[STTV]iq{yeOHDHT`cjqsqqnux}t`_ox|{sfdc_ZYWg~}}{z{|xpld^`hlszwmg^URUU[fowzlVJGFO\bfosqpor~}zzd[gv||xkdda[YW`{|{{{||tng_\djqy{qhaXQUUXalsyq]MJHKV`ckssopq~zl[^pz}{rfed^YY[o~}y{zz|ypkc[]gnw}yle]TRVV[fputfTLKIOZaepurprx|}v`Wdu{|ymefc\YZe~{|x{zz|vnh_Y`kt|tibYQSVW^jqsm]OLKJR^cjtvrrv|}lYWhw||wkfgc[[_ozvxzyz{tle]Xbnxrg`WPSVW_kpofYNMLLU`dnxytv}y}ycUWjx|{vjhid\]eyy~swyxyzrkc[Yer|pe^UOTUW`kmj`UNOMNWbgs{{v{wnZR[pz}{tjjib\bo{zvryxxzyoh`X[lxzjbZQOUUYdlidZROPNQ\elx}zyyyxcSRbv|}ypjkh_^i{x}}qtyxx{ukd[Was}se^VORUU]hjd]VQRQOVahr}}y~~v~jWQYn{}{vlkjc]eu|ytqyyxzzog^WZm{{i`YQOUUYdkd]XTRTPR\fmy~z{wzp\QTex}}yqkkf^aqywqw{xz|sh`XVfxoa[TOTWX`je\XVTVSQWcjt|{xzyt^STau}}zskjg^^o{~yrv|zz|viaXTavrb\UPSXY`if[XXVWVQT_gpzzw|yx`UT^r}}zulif^\m~ztv}|{|xjaXS^ttc\VRSYZ`ifYWYYXWQR\emvwu~{x`VV`s}}yukgd\[nzvy~|}xi_VR]tsb[WSU[]bieXV[[ZXQPYcjsts~w`XYas~|yukeaZZn{yz}}yi^UR[rtc[XUU]_cieWV[]\ZROVagorq}|bYY`o}}yvmeaZYk{z{~}zl^VQXnvf\YVV\`cheWT[`^\TNS^elppzfZZ_k{}zvpfa[Yg{{|~|q`WRUg|zk]ZXVZacffZQY``^XOOZbgnpvn\Z]eu}{xtjb]Yayzz|}}vfYSS^u}raZYXX_cee]PT_c`]SMT`dkoty`Z\alz|ywqe_[^pywz|}zo^URXi~xk\YYX[bdd_TNYbc`ZPOZcgmr}l[Z^dq|{xvmc^]f~~tw{||vhYTT^s}tdZYYX^cc`XMP]ec_WOS_ejqyzbX[_fu|zywkb_bqvpv{|~zscWTXdy{qaYYXX_ca[RJQ`fc`WPWchmur]W[`hv|{zwkcbiz|onvz{}yqaWV\i}yn^XYXW_a]VMITbhd`WR[gkq{|iXW]bky|{{vidfr|tiowz|{vk[VYaq}thZXYWY`_XOIKZgid_UVajmvq`VZ_ep{|||sgfm|{vkhryz~yseXW]gyypaXYYW\a[QIGPbkhd\U[glq~xgYX^civ||}ylfkx|vmelw{|{uk\W\dq{shZXZXYa^SIFK\jlfaWWajmx}k^W\afp{|}|rfiuvnfgt{|}voaWZbk{~ul_XZZY_`UIEHUgnic[V]gksmbY\belx|||tghtvmgfr{}~vodXZajw~vncZ[\Z^bVIDHSenkd]UYchnod\\beit{{{vhfswlgeq{~voeXYait~voe[\^]_cWHCHRdold]UV_ek~of^^cfhryzytgetwkggs}umdXZbis~|unf]^`_adVFCISeolc]TT[bh}}oga`efgpxywqeeuwjghs~tlcXZbiq|{tnh_`cbbdWFBJSdpmc]TQW_dz|pieaegflwxvrgdtyjfiq~vle[Ybinx{uojb`ddcdYGAIRaooe^UPS[au|}sjhcfgfitwvrjcq~kdhn{xme^Y`hmt{vplfadfdd]J?FP\lqi`YQPW_m~zxkifdgfenwvsnflocekv}pgb[^fkowwqmjccffd`O@AMWfqnc]TOS\gy|niieggehtwupki{xdaho|vjd_\djmrxtnlhbegeaWD=FR]mrja[RPWas~~xtiihegfdlwwspkrl^air~}pgc_`hmnsvplkfcggb\M=>KWdqqgaZRS\i~yxmhjgfgeepxwsqo}{bZbjtzmec`dknnttnlkecgf_VF:?NZhsqgb[TVbr~wxxhfiffgdfszxutur[Xbjuwkeecgnonsrmkjdcfd[P@8AQ\ktqhe]VZh|}txpegjfggdiw{xvw}gUXdlx~rhefekonmrokkibdf`TG::GWbpuoie[X`r}xs{zieijfgfenzzxx|u[R\gp|zmfghioolnqljkgbfdZL?7>O\htunicZ[j}suoegliggehu{yx}dRVckw}rhgjimpmjomjkkdef^OC8:HYcpwrkg]Ybx~}wr{thfklggeenzzx|nVR]hq~}wkfjllpniknkkmhdgcSE;8BT`kuunjbY]q{{rwwkfknigfdjvyw{sZQ[go||wlfjnlpnhhmkknkfheVE<8@Q_hsuojcXZlzsvwmhkpkgfdfrxvyw^RYfny{wmfiomonhekklooijgXE=9?O^fqtpicXWiyuwwojlqmgebdoutxv_T[hozzulfjpnomfcjlmqqklhWD=;AP_fosng`UTh~yvxwqlosnfdbclrrxwaV\ipzxtlfjponleahlnrtomiXC=BKZcforkb[Uazz{y|wqsrtvodba`foumaY^iqx|rnlkpwunia[alpvzuncO<:@EQ_dirriaZZp|yz~yqqsruukcba`ir|xg]Y_kry~yommntztlf^Zcmqvzsj\G8;BHUbflusic]c|zz||xymprsuuiccbblvqc[X`lrz}wnnoqw{skd[YdmrwypeUA67=HNXdjq{zplp||x~}y}tlktxwvshedbeutfb]]fprutnmtx{~ykaZV\jrwztbOA:>HPXahnwyrmq|~z}||tnntyyxtlfdbdqwjebaforsromsy|~znc[X\isx{vfSF@BJRY`gltvrnr}|~|topuyzxtmgdbdpxnifdhnqqpnmsy|}ync\Y^jty{vfTHDGNUZ`ekqsqnr~}{sqrwz{xsmgcadpyqmihjnppomnsy||wmc][aku{|vgVKGJPV[_ejoqpot~{trtw{|ytnidbdo{tpljjnppnmnsx{|xof_]ajsz|wjZOKMRX\_dinppot~|ustx{|yupkfcdm}wsollnoonmnrw{|yqha^aiqx|xm_SOPTX\`chloppt}}vstwz|{wrnieekyyurommoonmmqvz|ztld`afnv{zrdXRQTY]_bfkoqru|~xttvz||yuqlhfit{wtqnnnonmnpty|{wpicbekrxzuk^VTVY]`cfjnqsu{ztsux{|{xtpliio||ywtqononnmorw{|ztmgcdhntxwpe[WWY]`ceimqsvz|vssvy{|zwtpmjmv~zxvspoonnnnpty{{xrkfceiouwulb[XY\`cehlpsvy~xsqsvy{{zxurnmq{{xvurponnnnorvz|{wqkfdfjpuvria[Z\_behkosvy}{urqtwz|{zxurppv|xwvurpooonoqtx{|zuojffhlqttof_\]_behknrvy}}xsqrux{|{zxusqt{}ywwvtrpooooprvz{{xsmifgjnrtqkd_^`behjmqux|~zurrtwz||{zxustx~zwwvusqpooooqux{{yupkhhilqtsngb`acfhkmqtw{{vsrtwz{||{ywutw~{xwwwusrpoooqtwz{zvrnjiilorspjebbdfikmpsvz~}xtrtvy{||{zxvuw}{xxxxwusqpooqswy{zwsoljjlorsqlgddegikmpsvy}~yustwy{||{zxwuw||yxxxwusqpoopsvyzzwtpmkklorsqmifeghjlmprux|~zvtuwz{||{zxwvw||zyyzywtrqppqsvyzywtqnmlnprsqmigghiklmortw{zvuvxz{||{zxvvx||zzzzywusqppqtwyzywtqonnoqssqnjihijklnoqtvz{wvwyz|||{zxwvx||zzz{zxvsqppqsvxyywurpoooqstroljjjklmnoqsvz|xwwy{|||{zywwx|}{{{{{ywtrqpqsvxyywusqpopqstspmkkklmmnpqsuy~}ywwyz||||{zxxx{~{z{{{zxusrqqsuxyyxvtrqppqrssqomllmmnopqsux|{xxxz{|||{{yxy{~~|{{{{zywtrqqruwyyxwusrqqqrssrpnmmmnooprsuw{}yxxy{||||{{zyz}}{z{{{zxvtrrrtvxyyxvusrqqrsssqonnnoopqrsuwz~~{xxyz{||||{{z{|}{zzz{zywusrrsuwyyxwvtsrqqrssrpooooppqrsuwy|}zxxyz{|}}||{{|~~|zyyzzzywussstvxyyxwutsrrrrssrqpoppqrstuwx{~|yxxy{|}}}}|||}}{yyyzzzxwutstuwxyyxwutsrrrssrqqppqrrstuwxz}~{yxyz{|}}}}}}}~|zyyzzzyxvutttvxyyxwvutsrrsssrqqqqrstuvwxz||zxyz{|}}}}}}}~~|zyyyzzzywvtttuwxyyxwvutssssssrrrrsstuvwxz|~}{yyz{|}}~~~}~~}{zyyzzzyxwutuuwxyyxwvuttsssssrrrrsttuvwxz{~~|zyz{|}}}~~~~~}{zyyzzzzywvuuuwxyyxwwvutttttssrrsstuuvwxy{}|zzz{||}}~~~~~~|zzzzzzzyxvuuvwxyyxxwvuuuttttsssstuuvvwxy{}}{z{{|}}}}}}~~~|{zz{{{zyxwvvvwxyyyxwwvvuuutttssttuuvvwxyz|}|{{||}}}}}}~~~|{{{{{{zyxwvvvwxyyyxwwvvvuuutttttuuvvwwxyz|~|{{||}}}}}}}~~}|{{{{{{zywvvwwxyyyxxwwvvvvuutttuuvvvwwxyz|~~}|||}}}}}}}~~}|{{{{{{zyxwwwxxyyyxxxwwwvvvuutuuvvvwwwxyz|~}}||}}}}}}}~~~||{{{{{zzyxwwxxyyyyxxwwwwvvvuuuuvvwwwxxyz{}~}||}}}}}~~~~~}||{{{{{zyxwwxxyyyyxxxxwwwvvuuuuvvwwxxyyz{}}}|}}}}~~~~~~}||{{{{{{zyxxxxyyyyyxxxxwwwwvvuvvwwxxxyyz{|~~}}}}}}~~~~~~}|{{{{{{zzyxxxyyyyyyxxxxxwwvvvvvwwxxyyzz{|}~}}}}}~~~~~~}|{{{{{{{zyyxxxyyyyyyxxxxxwwwvvvwwxxyyzz{|}~}}}}}}~~~~~}|{{{{{{zzyyxxyyyyyyyyxxxxwwwvvwwxyyzz{{|}~~}}}}}~~~~~}||{{{{{{zzyyyyyyzyyyyyxxxxxwwwwwxyyzz{{|}~~~}}}}~~~~~}||{{{{{{{zzyyyyyzzyyyyyxxxxwwwwwxyyzz{{|}~~~}}}~~~~~}||{{{{{{zzzyyyzzzzyyyyyyxxxxwwwxyyz{{||}~~~~}}~~~~~}|||{{{{{{zzzyyzzzzzyyyyyyyxxxwxxyyz{{||}}~~~~~~~~~~}}|||{{{{{{zzzzzzzzzzyyyyyyyxxxxxyzz{{||}}~~~~~~~~~~~}|||||{{{{zzzzzzzzzzzzyyyyyyxxxyyzz{{|||}~~~~~~~~~}}|||||{{{{zzzzzzzzzzzzzzyyyxxxyyzz{{{||}~~~~~~~~~~}}}|||||{{{zzzzzzzzzzzzzzzyyyyyyyzz{{|||}~~~~~~~~}}}|||||{{{{zzzzzzzzzzzzzzzyyyyyzz{{{|||}~~~~~~~~}}}|||||{{{{{{{{{zzzzzzzzzzyyyyzz{{{|||}~~~~~~~~~}}}}|||||{{{{{{{{{zzzzzzzzzzyyyzz{{{|||}}~~~~~~}}}|||||{{{{{{{{{{{{{zzzzzzzzzzz{{{||}}}~~~}}}}|||||{{{{{{{{{{{{{{{{zzzzzzz{{{||}}}~~~}}}||||||{{{{{{{{{{{{{{{{zzzzzz{{{||}}}~~~}}}}||||||{{{{{{{{{{{{{{{{{zzzz{{{||}}~~~~~}}}}|||||||{{{{{{{{{{{{{{{{{zz{{{||}}~~~~~}}}}||||||||||{{{{{{{{{{{{{{{{{{{||}}~~~~~}}}}}||||||||||{{{{{{{{{{{{{{{{{{||}}~~~~~~}}}}}|||||||||||{{{{{{{{{{{{{{{|||}}}~~~~~}}}}}|||||||||||{{{{{{{{{{{{{{|||}}}~~~~~}}}}}}|||||||||||||{{|||{{{{{{|||}}}~~~~~~~}}}}}}|||||||||||||||||||{{{||||}}}~~~~~~~}}}}}}||||||||||||||||||||||||||}}}~~~~~~~~}}}}}}|||||||||||||||||||||||||}}}~~~~~~~}}}}}}}}}||||||||||||||||||||||}}}~~~~~~~~}}}}}}}}}}|||||||||||||||||||}}}}~~~~~~~~}}}}}}}}}}||||||||||||||||||}}}}~~~~~~~~}}}}}}}}}}}}||||||||||||||||}}}}~~~~~~~~~}}}}}}}}}}}}}|||||||||||||}}}}}~~~~~~~~~}}}}}}}}}}}}}}||||||||||||}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}||}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/assets/packed/PV/pv.arc b/applications/SprPoP/assets/packed/PV/pv.arc new file mode 100644 index 0000000..a5de9d7 Binary files /dev/null and b/applications/SprPoP/assets/packed/PV/pv.arc differ diff --git a/applications/SprPoP/assets/packed/PV/pv.pal b/applications/SprPoP/assets/packed/PV/pv.pal new file mode 100644 index 0000000..ec99c2b Binary files /dev/null and b/applications/SprPoP/assets/packed/PV/pv.pal differ diff --git a/applications/SprPoP/assets/packed/PV/story.pal b/applications/SprPoP/assets/packed/PV/story.pal new file mode 100644 index 0000000..1cb8dc0 Binary files /dev/null and b/applications/SprPoP/assets/packed/PV/story.pal differ diff --git a/applications/SprPoP/assets/packed/SHADOW/shadow.arc b/applications/SprPoP/assets/packed/SHADOW/shadow.arc new file mode 100644 index 0000000..1c0f9b1 Binary files /dev/null and b/applications/SprPoP/assets/packed/SHADOW/shadow.arc differ diff --git a/applications/SprPoP/assets/packed/SKEL/skel.arc b/applications/SprPoP/assets/packed/SKEL/skel.arc new file mode 100644 index 0000000..be05df5 Binary files /dev/null and b/applications/SprPoP/assets/packed/SKEL/skel.arc differ diff --git a/applications/SprPoP/assets/packed/SND/snd.arc b/applications/SprPoP/assets/packed/SND/snd.arc new file mode 100644 index 0000000..b543331 Binary files /dev/null and b/applications/SprPoP/assets/packed/SND/snd.arc differ diff --git a/applications/SprPoP/assets/packed/TITLE/title.arc b/applications/SprPoP/assets/packed/TITLE/title.arc new file mode 100644 index 0000000..3cf9f33 Binary files /dev/null and b/applications/SprPoP/assets/packed/TITLE/title.arc differ diff --git a/applications/SprPoP/assets/packed/TITLE/title.pal b/applications/SprPoP/assets/packed/TITLE/title.pal new file mode 100644 index 0000000..2b69603 Binary files /dev/null and b/applications/SprPoP/assets/packed/TITLE/title.pal differ diff --git a/applications/SprPoP/assets/packed/VIZIER/vizier.arc b/applications/SprPoP/assets/packed/VIZIER/vizier.arc new file mode 100644 index 0000000..ffb7818 Binary files /dev/null and b/applications/SprPoP/assets/packed/VIZIER/vizier.arc differ diff --git a/applications/SprPoP/docs/BUGS_OPEN.md b/applications/SprPoP/docs/BUGS_OPEN.md new file mode 100644 index 0000000..95119d2 --- /dev/null +++ b/applications/SprPoP/docs/BUGS_OPEN.md @@ -0,0 +1,1231 @@ +# SprPoP — ОТКРЫТЫЕ баги и незакрытые оптимизации + +**Здесь ТОЛЬКО незакрытое.** Всё закрытое (и, что важнее, разбор корней) +живёт в [`BUGS_CLOSED.md`](../../PoP/roomtest/BUGS_CLOSED.md) — прежде чем заводить новый баг, +грепни там по симптому. Сырые формулировки пользователя с прогонов — +[`bugs_level1.md`](bugs_level1.md) / [`bugs_level2.md`](bugs_level2.md). + +Приоритеты работ — в [`TASKS_OPEN.md`](TASKS_OPEN.md) (закрытые задачи с +протоколами — [`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md)), а не здесь. Правило +проекта: механику сверять с `../SDLPoP/src/` ДО кодинга. + +**Ревизия 2026-08-11:** файл вычищен от закрытых записей (правило «в `_OPEN` +только открытое»). Уровни 1-4 приняты smoke-тестами; крупных багов нет. + +| ID | что | тип | статус | +|----|-----|-----|--------| +| [SPRITE-ZERO-W0](#sprite-zero-w0) | кадр персонажа изредка отдаёт спрайт 0x0 (залипание уже вылечено) | **редкий** | открыт: нужна трасса маппинга W0 | +| [GATE-FORE-KID](#gate-fore-kid) | Кид в проёме ворот виден поверх решётки | окклюзия | **фикс есть, ждёт проверки в MAME** (2026-08-13) | +| [FORE-DUP](#fore-dup) | передний слой тайла рисуется дважды при перекрытии объектов | оптимизация | открыт: **сначала замерить**, потом чинить | +| [TORCH-ANIM-RIGHT](#torch-anim-right) | под запечённым пламенем застывают не только челюсти чомпера | **низкий** | открыт: на уровнях 1-4 такого соседства нет | +| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт | +| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводим: ни сценарием, ни попиксельной подгонкой X не поднимается | +| [BUG-CHOMP-JUMP-1](#bug-chomp-jump-1) | прыжок с места вплотную к чомперу: кадр с отступом назад | **низкий** | маловоспроизводим: на повторе не поднялся; на пререлиз | +| [GRAB-KBD-TIMING](#grab-kbd-timing) | зацеп в падении срабатывает менее стабильно, чем в оригинале | **после всех уровней** | открыт: подозрение на клавиатурный модуль, не на физику | +| [GUARD-ENTRY-DEATH](#guard-entry-death) | вход в комнату вплотную к стражу = мгновенная смерть (не портирован `bump_into_opponent`) | порт | **фикс есть, ждёт проверки в MAME** | +| [BUG-SHADOW-SET](#bug-shadow-set) | Тень на 12-м уровне дерётся спрайтами СТРАЖА: SHADOW.DAT у нас нет вовсе | порт | открыт, разбор готов | +| [PAL-L1-AFTER-INTRO](#pal-l1-after-intro) | вход в игру на уровень 1 после интро — игровая палитра не установлена (чёрный экран) | палитра/fade | открыт: воспроизведение нестабильно, корень не установлен | +| [PAL-DUNGEON-STALE](#pal-dungeon-stale) | переход 3→4 (подземелье→дворец): остаётся палитра подземелья | палитра/fade | открыт: КОРЕНЬ ЯСЕН — fade_in восстанавливает из протухшего снимка | +| [BUG-CORPSE-AIR](#bug-corpse-air) | труп Кида висит в воздухе, когда убившая его плита улетела вниз | физика | открыт | +| [LOOSE-ROOM-CHANGE](#loose-room-change) | расшатанная плита не доваливается, если Кид ушёл в соседнюю комнату | порт | закрыт 2026-08-13, кроме [LOOSE-SEAM-ANIM](#loose-seam-anim) | +| [JUMP-FLOOR-LEGS](#jump-floor-legs) | в прыжке с пола ноги Кида рисуются ПОВЕРХ кромки пола, а должны за ней | окклюзия | **корень найден и починен, ждёт проверки в MAME** | +| [MOB-CLIP-RIGHT](#mob-clip-right) | окна и узор кладки рисуются ПОВЕРХ падающих плит | окклюзия | открыт: корень найден 2026-08-13 | +| [MID-OVERLAY-LAYER](#mid-overlay-layer) | оверлей кромки всегда поверх плит и персонажей (у оригинала — midtable с сортировкой) | слои | открыт: нужен разбор архитектуры | +| [GUARD-FALLOUT-VICTORY](#guard-fallout-victory) | страж, выпавший из комнаты, не засчитывается убитым | порт | открыт: две строки | +| [KBD-STUCK-WAIT](#kbd-stuck-wait) | потерянный break-код клавиши вешает игру НАСМЕРТЬ в межуровневой заставке | клавиатура/клин | открыт: диагноз полный, чинить не сейчас (решение пользователя 2026-08-24) | +| [L10-BUTTON-DEBRIS](#l10-button-debris) | ур.10 к.1: плита упала на кнопку (1,8) — щебень есть, а решётки закрыты | кнопки/ворота | открыт: **не воспроизводится**, проверено многое (см. разбор) | +| [LOOSE-SEAM-ANIM](#loose-seam-anim) | плита в шве (col −1), уроненная в прошлой комнате, исчезает без анимации падения | **мелкий** | открыт по решению пользователя: «пока пусть будет так» | +| [SND-PACE-DEAD](#snd-pace-dead) | пейсинг сцен по насосу CBL не включается: признак «часы идут» вычисляется двумя чтениями подряд | **тайминг/звук** | открыт: диагноз полный, симптом обойдён подгонкой (2026-08-27) | +| [PV-RENDER-BOUND](#pv-render-bound) | сцена с принцессой рисуется дороже бюджета: ~49 тиков/с вместо 60, музыка уезжает от картинки | производительность | открыт: замер есть, лечение — удешевить кадр | +| [MUS-LEFT-TEAR](#mus-left-tear) | `pop_mus_left` (16 бит, пишет прерывание) читается из главного цикла неатомарно — возможен ложный «трек кончился» | **потенциальный** | открыт: хазард показан рассуждением, в прогоне не проявился | + +--- + + +## L10-BUTTON-DEBRIS. Плита, упавшая на кнопку (ур.10, к.1) — щебень есть, решётки закрыты + +**Наблюдение (пользователь, 2026-08-24).** Уровень 10, комната 1: Кид стоит +на кнопке `(1,8)` и роняет на неё плиту `(0,8)`. Кнопка превращается в +щебень, но решётки, которые она открывает, остаются закрытыми. Важная деталь +из того же прогона: на 10-й уровень пришли **телепортом Shift+L**, а не +проходом. + +**Ожидаемое поведение (оно же оригинал).** Плита, севшая на opener, жмёт его +ОДИН раз с `button_type = tiles_14_debris`; у `trigger_gate` это отдельная +ветка «открыть насовсем»: `modifier >= 188` -> сразу `0xFF`, иначе возврат 2 и +`animate_door` доводит створку до `0xFF`. Ворота с `0xFF` заморожены +открытыми, обычный opener их больше не трогает. Сама кнопка съедается в +щебень. Разобрано в [BUG-LOOSE-BUTTON-1](BUGS_CLOSED.md#bug-loose-button-1). + +**НЕ ВОСПРОИЗВЕЛОСЬ** (2026-08-24, отладочный старт `make LEVEL=10 ROOM=1 +POS=18`, MAME): Кид стоит на кнопке -> решётка `(2,7)` открылась; удар головой +роняет плиту `(0,8)`; кнопка стала щебнем; **решётка осталась открытой** и +после ухода Кида с тайла. Пользователь тоже не смог повторить в этой сборке. + +**Что проверено и совпадает с оригиналом:** + +* цепочка кнопки — ровно две цели, обе в комнате 1: `(2,9)` и `(2,7)` + (`bg = 20`, терминатор цепочки — бит 0x80 в LINKLOC, как в seg007:0C09); +* `trigger_gate` построчно равен оригиналу, включая обе ветки debris; +* `add_trob` дедуплицирует по (room, tilepos) — как `find_trob` оригинала, + двойных записей на одни ворота не бывает; +* `animate_door` гасит анимацию при `modifier == 0xFF` и доводит тип 2 до + `0xFF`; +* таймеры связей в файле уровня 10 нулевые (не «jammed» 0x1F); +* `pop_dl1/pop_dl2` перечитываются из файла при каждой загрузке уровня, а + `pop_level_reset_tiles` восстанавливает LINKMAP из эталонной страницы. + +**Живые гипотезы (по убыванию правдоподобия):** + +1. **Ветка `return 2` (створка ещё не «полностью» открыта).** Визуально + решётка выглядит открытой уже при `modifier ~ 140` (проходимость считается + как `(modifier >> 2) + 6 >= высота кадра`), а мгновенный `0xFF` требует + `modifier >= 188`. То есть глазами «открыто», а по коду — ветка с + анимацией; её в MAME поймать пока не удалось. +2. **Накопленное состояние после телепорта Shift+L** через несколько уровней + (у нас уже был такой класс: [GUARD-DROPPEDOUT-CARRY](BUGS_CLOSED.md#guard-droppedout-carry)). + Отладочный старт сразу на 10-м даёт чистое состояние — и баг не всплывает. +3. Кнопка была нажата/отпущена ранее в этой же сессии, и падение пришлось на + момент, когда trob кнопки уже умер, а таймер связи ещё не истёк. + +**Как ловить в следующий раз:** как только симптом повторится — сразу +**QuickSave (F6)**, не выходя из сцены. Сохранение везёт с собой LINKMAP +(`pop_level_qsave` пишет `pop_dl1/pop_dl2`) и модификаторы комнат, то есть даёт +ровно то состояние, которое сейчас не удаётся собрать искусственно. + +--- + + +## KBD-STUCK-WAIT. Потерянный break-код вешает игру насмерть в межуровневой заставке + +> **Решение пользователя (2026-08-24): не чинить сейчас.** Записан разбор и +> варианты; вернуться, когда дойдут руки до клавиатурного слоя. + +**Наблюдение (пользователь, 2026-08-24).** Нажат Shift+L (чит «следующий +уровень») — игра встала намертво: картинка прежнего уровня, реакции нет. + +**Диагноз (снят в живой сессии MAME).** Программа НЕ рухнула и не потеряла +кадровые прерывания — IRQ приходили штатно (вход по вектору виден в +`history`). Стояли в `pop_pre_cutscene_show` (pop_intro.c:949), в его первом +цикле: + +```c +while (kbd_raw_any_down()) { kbd_raw_sync(); gfx_wait_vsync(); } +``` + +Цикл намеренный: клавиша, которой закончили уровень (тот самый Shift+L), не +должна тут же сойти за «пропустить заставку». Внутри — `gfx_wait_vsync` +(опрос бита 5 порта #FE), он отрабатывал нормально; не выходил ВНЕШНИЙ цикл. + +Причина — залипший бит в `_kbdraw_down[]`: код `0x72` в PLAIN-половине карты +(то есть **keypad 2**; стрелка Down — это `E0 72` и живёт в EXT-половине, +байт 46). Состояние на момент разбора: `_kbdraw_active = 1`, +`_kbdraw_pending = 0`, `_kbdraw_overrun = 0` — то есть штатная страховка по +Rx-overrun не срабатывала. Гашение бита отладчиком (`_kbdraw_down[14] = 0`) +сразу вернуло игру к жизни: заставка прошла, уровень 2 загрузился. + +**Чем бит может залипнуть** (разбор `libc/irq/_irq_tramp.c`): + +1. **Потерянный break при переполнении FIFO SIO.** Приёмник глубиной 3 + байта; когда прерывания запрещены надолго (загрузка уровня через ESTEX — + ровно момент Shift+L), пачка `F0 code` не помещается. Защита есть + (чтение RR1 → `_kbdraw_overrun` → `kbd_raw_sync()` снимает held), но она + срабатывает, только если SIO успел выставить сам бит overrun. +2. **Разъехавшийся префиксный автомат.** `F0` (break) и `E0` (ext) — отдельные + байты, состояние копится в `_kbdraw_pending`; любой сброс между префиксом + и кодом переворачивает смысл, и break декодируется как MAKE. Ветка + «fake shift» (`E0 12` / `E0 59`) делает это по построению — она гасит + `pending` и пишет код как make даже у break-формы. +3. **Make и break у разных владельцев канала.** Пока `_kbdraw_active == 0`, + байты уходят DSS. Клавиша, нажатая до `kbd_raw_open()` (или в момент + close/open), даёт make одному владельцу, а break — другому: снимать + нечего, бит остаётся. +4. **Держащий вход эмулятора.** `set_input ... frames=0` из MCP-моста держит + клавишу нажатой, typematic шлёт make повторно (наблюдалось: погашенный + отладчиком бит возвращался). Это артефакт отладки, а не игры, но именно + он мог создать исходное состояние в этой сессии. + +**Варианты решения.** + +| Вариант | За | Против | +|---------|----|--------| +| **A. Таймаут в циклах ожидания** (ждать отпускания не дольше ~1 с, дальше идти) | Дёшево, локально; снимает КЛАСС отказа «вечный клин» независимо от причины; заставка от лишней секунды не страдает | Лечит симптом, а не причину: залипшая клавиша дальше продолжит «нажиматься» в gameplay | +| **B. Анти-залипание в `kbd_raw`** (гасить биты, которые давно не подтверждались typematic-повтором) | Чинит класс причин целиком, помогает и в gameplay | **Модификаторы typematic НЕ повторяют** (обжигались: `kbd_overrun_wipe_modifiers`) — значит Shift/Ctrl гасить нельзя, нужен список исключений; плюс хранение «свежести» на 512 кодов дорого, придётся огрублять | +| **C. Ужесточить страховку по overrun** (чистить held не только по биту RR1, но и по «подозрительным» ситуациям: длинный DI, возврат из загрузки) | Бьёт по самой вероятной причине (1); дёшево по памяти | Эвристика: можно снять РЕАЛЬНО зажатую клавишу (Кид перестанет бежать посреди разбега) | +| **D. Ничего, считать артефактом отладки** | Ноль работы | Если это не мост, а SIO, то на железе получаем намертво зависшую игру — худший класс отказа | + +**Если возвращаться — начинать с точной причины:** лог байтов SIO в +трамплине (кольцо на 32-64 байта + дамп по клавише) и повтор сценария +«Shift+L во время дисковой загрузки». Без этого выбор между B и C — гадание. + +**Смежное:** `kbd_raw_fifo_drain`, `kbd_overrun_wipe_modifiers` в memory — +там уже закрытые случаи залипания того же битмапа. + +--- + + +## GRAB-KBD-TIMING. Зацеп в падении срабатывает НЕ ТАК СТАБИЛЬНО, как в оригинале — РАЗБОР ПОСЛЕ ВСЕХ УРОВНЕЙ + +> **Решение пользователя (2026-08-12): отложено до готовности всех уровней.** +> Механика работает, это вопрос ощущения, а не проходимости. + +**Наблюдение (пользователь).** Вход на уровень 7: Кид влетает в комнату сверху +и, пролетая мимо края пола, МОЖЕТ за него зацепиться — но получается заметно +реже, чем в оригинале. «Похоже, это проблемы нашего клавиатурного модуля». + +**Что уже сделано и почему это НЕ закрывает вопрос.** В ходе разбора +[GRAB-BELOW-ROOM](BUGS_CLOSED.md#grab-below-room) восстановлена ветка +`ACT_MIDAIR` в `check_action` (кадры 102..105 — начало падения, где `fall_y` ещё +не разогнан): раньше на этих четырёх кадрах `check_grab` не звался вовсе, то +есть окно зацепа было короче оригинального. Это должно было добавить +стабильности, но саму гипотезу про клавиатуру не проверяет. + +**Куда смотреть, когда дойдут руки.** Порядок именно такой — сначала +измерить, потом чинить: + +1. Снять, ЧТО видит движок в момент падения: `pop_ctrl_shift_held()` покадрово + (Shift зажат заранее — читается ли он каждый кадр, или FIFO SIO отдаёт + состояние с пропусками; см. memory `kbd_raw_fifo_drain`, + `kbd_overrun_wipe_modifiers` — оба прошлых бага были именно про модификаторы). +2. Сверить с оригиналом ширину окна по кадрам: у нас `check_grab` доступен на + 102..105 (midair) + весь `ACT_FREEFALL`, как в seg006 — расхождений в коде + быть не должно, значит расхождение либо во вводе, либо в темпе игры + ([L1-SPEED](TASKS_OPEN.md#l1-speed): игра идёт на ~39 % быстрее оригинала, + а окно зацепа отмеряется В КАДРАХ — то есть по времени оно у нас короче). +3. Хост-тест `t_grab.c` уже умеет мерить ширину окна (карта исходов по фазе X и + задержке нажатия) — на нём и проверять «стало стабильнее». + +### Протокол замера (написан 2026-08-12, чтобы следующий заход начался с фактов) + +Пользователь связывает с этим же и «двойные срабатывания не вовремя». Прежде +чем трогать драйвер — доказать, что он вообще виноват: **сегодняшний случай +«Кид ходит как с зажатым Shift» оказался артефактом MCP-моста, а не драйвера** +(`pause` освободил 6 незакрытых входов, которые плагин переустанавливал каждый +кадр; как только они отпустились, драйвер снял бит сам). То есть break-коды +драйвер обрабатывает верно, и ложная тревога здесь стоила часа. + +Что мерить: + +1. **Поток скан-кодов**: кольцевой лог в трамплине прерывания + (`libc/irq/_irq_tramp.c` / `kbd_raw_poll.c`) — байт кода, флаг make/break, + номер кадра. Читать из MAME по адресу буфера (`mem`), как читаем `_Kid`. +2. **Что увидел движок**: `pop_ctrl_shift_held()` и битмап `_kbdraw_down` + (адрес — из `.map`, для SprPoP 0xAEF9) на тех же кадрах. Расхождение + «код пришёл, а движок не увидел» = потеря в драйвере; «кода не было, а бит + стоит» = потеря break (то, что подозревает пользователь). +3. **Сцены**: бег с отпусканием, разворот, вис + отпускание Shift, падение с + зажатым заранее Shift (вход на уровень 7), быстрое повторное нажатие + (проверка «двойного срабатывания» — typematic, см. memory + `kbd_overrun_wipe_modifiers`). + +Что считать нормой: каждому make ровно один break; между make и видимым +`kbd_raw_down()` — не больше кадра; при удержании typematic-повторы НЕ должны +доходить до `pop_ctrl` как новые нажатия (диспетчер ждёт фронт). + +Отдельная гипотеза, которую замер тоже закроет: окно зацепа отмеряется В +КАДРАХ, а игра идёт на ~39 % быстрее оригинала ([L1-SPEED](TASKS_OPEN.md#l1-speed)) +— тогда «менее стабильно» это не клавиатура, а темп, и чинить надо темп. + +--- + + +# С приёмки уровня 2 (2026-08-07) + +Всё найденное тем прогоном закрыто, кроме двух записей ниже +(`BUG-SPIKE-1` — уровень 2, комната 6). Карта содержимого уровня 2 (что где +стоит по `res2002.bin`, какие кнопки какие ворота открывают) — в +[`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md#l2-pass), по ней видно, «механика не +сработала» это или «так и задумано». + + +## BUG-SPIKE-1. Пики залипают выдвинутыми — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ + +> **Статус 2026-08-05, вечер.** Пользователь **повторить не смог**, а замер +> (ниже) показал, что чистый пробег по убранным пикам убивает штатно. То +> есть в смертельности бага, похоже, нет вовсе: наблюдался частный случай — +> пики, УЖЕ выдвинутые полностью (h = 1), для бегущего безвредны и в +> оригинале. Запись оставлена открытой ровно из-за визуального расхождения +> со скриншотом SDLPoP (у нас острия торчат, у него убраны) — см. конец. +> +> **Как получить состояние нарочно:** подойти к пикам вплотную (выдвинутся), +> отступить на полшага, НЕ выходя из зоны срабатывания, и пробежать по ним. +> Пользователь пробовал и это, и попиксельную подгонку X читом `[`/`]` — +> не поднялось. Вывод для будущего разбора: состояние **не чисто +> позиционное**, одной шириной габарита его не объяснить; следующий +> подозреваемый — момент, в который `process_trobs` застаёт модификатор +> относительно кадра Кида. + +**Наблюдение (пользователь, 2026-08-05).** Уровень 2, комната 6, пики (1,3): + +| действие | что происходит | +|----------|----------------| +| длинный прыжок с ряда 0 на пики | **смерть — правильно** (путь `fell_on_spikes`) | +| пробег по ряду 1 прямо по пикам | **урона нет** | +| после уборки пик | **на экране остаются белые остатки остриёв** (в оригинале чисто) | +| прыжок на месте, стоя на пиках | урона нет | +| просто стоять на выдвинутых пиках | можно сколько угодно | + +**Замер (MAME, чтение `room_modif` комнаты 6).** Пока Кид стоит на тайле, +модификатор пики (индекс 13) = **0x8E**, то есть «пики ПОЛНОСТЬЮ вышли и +идёт обратный отсчёт». Дальше вся арифметика сходится с оригиналом: + +``` +is_spike_harmful (seg007:1178): 0/-1 → 0; <0 → 1; 1..4 → 2; >=5 → 0 +check_spiked (seg006:0658): убивает при h>=2 на кадрах бега 7..14 + и при h!=0 на кадрах приземления 43/26 +``` + +То есть **при h = 1 (пики уже вышли) бегущий не гибнет и в оригинале** — +смертельно только окно ВЫДВИЖЕНИЯ (модификатор 1..4, h = 2). Наши +`animate_spike`, `start_anim_spike`, `is_spike_harmful`, `check_spiked` +сверены с seg006/seg007 построчно и совпадают дословно. + +**ВТОРОЕ НАБЛЮДЕНИЕ (то же место, сравнение с оригиналом).** Кид уронил +плиту-потолок и спрыгнул вниз; пики выдвинулись и «спрятались не все — часть +артефактов осталась». Скриншоты рядом: +[наш](bugscreens/l2-r6-spikes-ours.png) и +[SDLPoP](bugscreens/l2-r6-spikes-sdlpop.png) в той же позе. У нас из-под +щебня торчат белые острия, у оригинала пик не видно ВООБЩЕ. + +**ГИПОТЕЗА «ТРИГГЕР СРАБАТЫВАЕТ РАНО» ПРОВЕРЕНА И ОПРОВЕРГНУТА (замер +2026-08-05, MAME, watchpoint на `room_modif[13]` комнаты 6).** Чистый +пробег по УБРАННЫМ пикам убивает штатно: + +``` +запись modif=1 : кадр 11 (беговой), x=112, curr_col=2 ← пики пошли вверх +запись modif=2 : кадр 12 (беговой), x=117, curr_col=3 ← Кид уже НА тайле, h=2 +запись modif=3 : кадр 177 (frame_177_spiked) ← напоролся +``` + +То есть `check_spike_below`, `check_spiked`, `is_spike_harmful` и тайминг +выдвижения работают правильно, и «раннего» триггера нет. + +**Настоящий корень — пики ЗАЛИПАЮТ выдвинутыми.** Пока габарит Кида +накрывает колонку пики, `check_spike_below` каждый кадр зовёт +`start_anim_spike`, а тот при отрицательном модификаторе переставляет его +обратно в 0x8F — отсчёт до уборки не доходит. А выдвинутые пики (h = 1) +для бегущего БЕЗВРЕДНЫ по правилам оригинала. Отсюда обе жалобы: пробег +по уже вышедшим пикам не убивает, и они же остаются торчать на экране. + +**Что осталось выяснить (и это единственный открытый вопрос).** Код +`start_anim_spike` у нас с оригиналом совпадает дословно, значит оригинал +тоже удерживал бы пики, стой Кид там же. На скриншоте SDLPoP в похожей +позе пики УБРАНЫ — то есть его Кид стоит чуть левее и его габарит колонку +пики уже не задевает. Разница в 2–3 пикселя посадки, а у нас такие +расхождения по X уже ловились (см. заметку в BUG-GRAB-1: после касания +площадки SDLPoP уводит Кида на 134, мы — на 141). + +**Как закрывать:** инструментировать SDLPoP (печать `char_x_left/right`, +`left/right_checked_col` и модификатора пики каждый кадр), проиграть ту же +сцену — падение плиты-потолка в комнате 6 и остановку на щебне — и сверить +с нашей трассой ПОЗИЦИЮ КИДА после приземления. Если позиции совпадут, а +диапазоны колонок разойдутся — виноват габарит (`kid_fp` против +`set_char_collision` текущего кадра); если разойдутся позиции — это отдельный +баг посадки, а пики — его следствие. + +--- + + + +--- + +# Оптимизация отрисовки (записано 2026-07-29) + +Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к +перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый +кадр), а у нас каждая такая пометка превращается в реальный heal (копию из +ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем +безусловно» корректен, но дорог. + + +## T-1. Пики: перерисовывать по причине, а не безусловно + +**Сейчас:** `pop_process_trobs` зовёт `pop_spike_redraw` каждый кадр для +каждой живой пики в комнате (порт `redraw_21h`, который `animate_spike` +вызывает вне всяких `if`). Это корректно, но лишнее для пик, до которых +Киду дела нет. + +**Надо:** перерисовывать тайл пики, только если +1. **сменился её видимый кадр** (шаг выдвижения/уборки), ЛИБО +2. **её кто-то стёр** — а стереть у нас может только heal, то есть тайл + попал в прямоугольник `kid_heal` этого кадра. + +Это и есть модель оригинала, просто выраженная флагами: `redraw_at_char` +(seg003:0576) каждый кадр помечает `set_redraw_fore` тайлы персонажа, причём +**объединение текущего и предыдущего** прямоугольника +(`MIN(char_top_row, prev_char_top_row)` и т.д.), а `animate_spike` помечает +свой тайл. Итог = {тайл сменил кадр} ∪ {тайлы Кида}. + +**Как:** слой Кида и так считает `cL..cR`/`rT..rB` в `pop_fore_over_kid` — +пусть публикует их (плюс предыдущие, как в оригинале), а цикл trob'ов +сравнивает `tilepos` с диапазоном целочисленно. Никаких пересечений +прямоугольников (см. память `manual_hints_over_auto_detect`). + +**Приоритет:** отдаётся почти бесплатно ПОСЛЕ T-2, отдельно не окупается. + + + +--- + + + +## BUG-CHOMP-JUMP-1. Прыжок с места вплотную к чомперу: кадр с отступом назад — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ + +> **Статус 2026-08-08.** Наблюдение пользователя на приёмке L3-CHOMP: Кид +> стоит вплотную СЛЕВА от чомпера лицом вправо, прыжок с места — в анимации +> проскакивает кадр, где он «чуть отступил назад», и только потом идёт +> прыжок. В оригинале прыжок идёт прямо с места. **На повторе в тот же +> заход не воспроизвёлся** — отложено до пререлиза. + +**Что уже установлено (чтобы не разбирать заново).** + +1. **Это НЕ анимация.** `seq_3_standing_jump` (SDLPoP `seqtbl.c:382`) + состоит только из положительных смещений: + `act(run_jump), f16, f17, dx(2) f18…f22, dx(7) f23, dx(9) f24, dx(5) dy(-6) f25`. + Ни одного отрицательного `dx` — отката в последовательности нет вовсе. + Значит отступ даёт `bumped()`, то есть физика посчитала въезд в + препятствие и выровняла `Char.x` назад. + +2. **`is_obstacle` для чомпера у нас совпадает с оригиналом** (seg004:037E): + препятствие только при `modif == 2`, причём именно `== 2`, БЕЗ маски + `0x7F` — то есть окровавленный чомпер (`0x82`) в ванили не бампит вовсе. + Проверено, расхождения нет. + +3. **Прямая трасса прыжка отката НЕ показала.** Ввод «UP и RIGHT + одновременно» (mame bridge, `key UP 24` + `key RIGHT 24`) даёт чистое + движение вперёд: `x` 148 -> 155, кадр становится 178 (перемололо). Ни + одного кадра с уменьшением `x`. + +**Главная гипотеза — ПОРЯДОК НАЖАТИЙ.** Если ↑ приходит на кадр раньше →, +то `control_standing` уходит не в `standing_jump()`, а в `up_pressed()` — +вертикальный прыжок с зацепом, а он **выравнивает `Char.x`**. Отсюда и +«отступил, потом прыгнул». Проверять надо `check_jump_up` / +`jump_up_or_grab` / `grab_up_no_floor_behind` (seg005:0836 и далее), а не +прыжок с места. Внимание на `can_climb_up` (seg005:0828): там у чомпера +ЕСТЬ спецкейс (`seq_73_climb_up_to_closed_gate` при взгляде ВПРАВО) — он у +нас портирован (`pop_map.c`, ветка `TILE_MIRROR || TILE_CHOMPER`), но именно +вокруг него и стоит копать. + +**Как ловить.** Брейкпоинт на резидентном `_kid_tick` с +`{printf "f=%d x=%d act=%d col=%d",b@Kid+0,b@Kid+1,b@Kid+6,b@Kid+4; g}` — +одна строка на кадр, адрес `_Kid` из `.sprinter-cc-build/.sprinter-cc-sprpop/sprpop.map` +(после КАЖДОЙ пересборки другой). Плюс стоп-кадр `1` в момент отступа и +чтение `Kid` из памяти. Метод — memory `z80_profiling_method`. + +--- + +## Заметки (отладка) + +- Тестовые клавиши осторожного шага: **J** = шаг влево, **L** = шаг вправо + (эмуляция Shift+стрелка), см. `pop_ctrl.c` `KBD_DBG_STEP*`. Первый шаг в + сторону = разворот (как в оригинале safe_step), движение со второго. +- Читы (`pop_cheat.h`): **K** — убить стража, **I** — бессмертие (toggle), + **S** — выдать меч, **Shift+L** — следующий уровень, **`[`/`]`** — сдвиг + Кида на пиксель по X. +- Респавн после смерти — по **↑** (или авто через `RESPAWN_DELAY`); ставит + Kid в стартовую позицию УРОВНЯ (`pop_start_level`, порт do_startpos). +- **ROOMNAV (`=`/`-`) — тоже наш чит**, которого в оригинале не было, как и + `S`. Все они со временем съедутся в общий блок читов, разрешаемый в + настройках; пока просто включены (`pop_cheats = 1` в `sprpop.c`). + Известный баг этого чита закрыт — [BUG-CHEAT-FIGHT-1](BUGS_CLOSED.md#bug-cheat-fight-1). +- Комнаты **13, 18, 24 уровня 1 недостижимы** в обычной игре — это свойство + данных уровня (разбор — «НЕ БАГИ» в [`BUGS_CLOSED.md`](../../PoP/roomtest/BUGS_CLOSED.md)); + приоритет багов в них низкий. Аналогично 23/24 на уровне 3. + + +--- + + +## TORCH-ANIM-RIGHT. Под пламенем могут застыть не только челюсти чомпера + +Открыто 2026-08-10 при закрытии +[BUG-TORCH-CHOMP-2](BUGS_CLOSED.md). + +Пламя факела запекается в фон и рисуется в ячейке ПРАВОГО СОСЕДА. Оригинал +после каждого кадра факела метит этого соседа (`set_redraw_anim_right`, +seg007:0101) и перерисовывает весь его слой `anim` поверх огня; мы метим +только когда сосед — чомпер. Слой `draw_tile_anim` (seg008:0644) рисует +также **пики, зелье и меч** — если такой тайл окажется справа от факела и +будет в статике, пламя накроет и его. + +На уровнях 1–4 такого соседства не встретилось, поэтому расширять пометку +(она стоит перерисовки тайла каждый кадр на каждый факел) заранее не стали. + +**Как проверять:** поставить факел слева от пики/меча/зелья в тестовой +комнате и дождаться статики; либо пройти уровни 5+ и смотреть на клетку +справа от каждого факела. + +**Как чинить, если встретится:** там же, в ветке факела `pop_process_trobs`, +добавить в `trob_rcode`-проверку нужные коды и соответствующий вид пометки +(`POP_RD_SPIKE` / `POP_RD_FLOOR`), а зелье — переставить в порядке обхода +так, чтобы оно рисовалось ПОСЛЕ факела. + +--- + + +## FORE-DUP. Передний слой тайла рисуется ДВАЖДЫ при перекрытии объектов + +Найдено пользователем 2026-08-10 (вопросом, а не по симптому — картинка +верная, страдают только такты). + +**У нас.** Fore-проход зовёт КАЖДЫЙ рисующий по своему прямоугольнику: +`pop_char_fore` для слотов Кида и соперника, `pop_mirror_draw` для отражения. +Окно клипа (`pop_fore_set_clip`) у каждого своё. Если футпринты двух +объектов накрывают один тайл, его передний кусок рисуется два раза. + +Когда случается: +- **Кид и отражение** — ВСЕГДА (стоят на одном тайле зеркала); этот случай + создан фиксом fore-прохода над отражением 2026-08-10; +- **Кид и соперник в одном тайле** — то есть весь ближний бой; +- Кид и падающий кусок плиты. + +**Картинку не портит:** куски переднего слоя идут прозрачным блитом-копией, +операция идемпотентная. Портило бы при XOR/mono с накоплением — таких в +fore-слое нет. + +**Такты тратит,** и в самом дорогом месте: fore-проход исторически самая +тяжёлая часть кадра (memory `pop_fore_layer_cost` — было 78 % кадра, окно +клипа и кэш кладки дали 3.2×). + +**Как устроено в оригинале — дубль НЕВОЗМОЖЕН по построению.** +`redraw_at_char` (seg003:0427) ничего не рисует, а только помечает тайлы +своего прямоугольника: + +```c +for (tile_row = x_top_row; tile_row <= char_bottom_row; ++tile_row) + for (tile_col = x_col_left; tile_col <= x_col_right; ++tile_col) + set_redraw_fore(get_tilepos(tile_col, tile_row), 1); +``` + +а `set_redraw_fore` (seg007:0550) — это `redraw_frames_fore[tilepos] = frames`, +ПРИСВАИВАНИЕ. Два персонажа на одном тайле оставят там ту же единицу, и +единственный обход тайлов нарисует передний кусок один раз. + +**Что делать.** Механизм пометок у нас уже есть и прямо назван портом этой +архитектуры — `pop_redraw.h`. Fore-слой остался единственным местом на +прямом вызове. Приведение к оригиналу: `pop_char_fore`/`pop_mirror_draw` +вместо прохода ставят пометки, а один проход в конце кадра их разбирает. + +**СНАЧАЛА ЗАМЕРИТЬ, потом чинить.** Это самый горячий путь, и окно клипа +(вместо перебора тайлов) в своё время дало 3.2× — переход на пометки может +часть этого вернуть назад. Замер: брейкпоинт на листьях fore-слоя со +счётчиком, сцена «бой в комнате 3 уровня 1» и «Кид на тайле зеркала, +ур. 4»; сравнить число нарисованных кусков с числом уникальных тайлов. +Если дубль мал — оставить как есть и закрыть запись. + +--- + +--- + + +## SPRITE-ZERO-W0. Кадр персонажа изредка отдаёт спрайт 0x0 — РЕДКИЙ + +Всплыло при разборе [BUG-CHAR-STALE-PAGE](BUGS_CLOSED.md#bug-char-stale-page) +(тень застыла на двух страницах в разных позах). Залипание слота вылечено — +`cd_sig_make` больше не берёт снимок, если кадр не рисовали, — но САМА +причина нулевого спрайта не найдена: `atlas_image` для валидного кадра +изредка отдаёт запись 0x0. + +Подозрение: конкуренция за окно W0. `pop_char_draw` маппит страницу атласа +(`gfx_w0_map(pages[page].page)`) и держит её до `gfx_w0_unmap()` в конце, а +внутри этого окна успевают отработать `cd_splash` и `pop_sword_draw`; рядом +в кадре W0 маппят чтение данных уровня (`pop_level_tile`, `pop_level_set_tile`) +и mob-тик. Если какой-то путь размапливает окно раньше времени, чтение +заголовка спрайта даст нули. + +Как ловить: watchpoint на порт окна 0 либо счётчик «w==0 при непустом кадре» +в `pop_char_draw` с записью кадра/страницы/idx в отладочную ячейку, дальше +читать её из MAME. Симптом редкий (пользователь поймал дважды). + + +## GATE-FORE-KID. Кид, стоящий В ПРОЁМЕ ворот, виден ПОВЕРХ решётки — ФИКС ЕСТЬ, ЖДЁТ ПРОВЕРКИ + +> **Портировано 2026-08-13** (повтор нашёлся на уровне 10, комната 7, тайл +> (2,6) — подтверждено чтением памяти MAME: `Kid.room=7, curr_row=2, +> curr_col=6`, код тайла `0x04`). Сделано так: +> +> * **решение** — `pop_gate_over_char(ch)` в `pop_map.c`: персонаж стоит на +> тайле ворот текущей комнаты (колонки 0..8; девятая отсекается, там бары +> легли бы в соседнюю комнату — у оригинала это `Kid.room != room_R`); +> * **отрисовка** — `draw_gate_fore` в `pop_bg.c`, точный порт seg008:1153: +> только прозрачные куски 51 + ломти 52, без непрозрачного 50 и без +> хвостового `DOOR_FRAM_SLICE` (фон под барами уже нарисован). Зовётся +> ОДИН раз на персонажа за кадр, не в тайловом цикле; +> * **тесты** — `tests-host/t_char.c`: `char_gate_covers_char_standing_in_it`, +> `char_gate_ignores_neighbour_tiles`, `char_gate_ignores_other_room`, +> `char_gate_never_in_last_column`. Отрисовка в харнесс не линкуется, +> поэтому проверяется именно решение — ради этого оно и вынесено в карту. +> +> Расхождение с оригиналом (осознанное, в `docs/impl_diff.md`): оригинал +> спрашивает жёстко про `Kid`, потому что foretable у него одна на проход +> тайлов; у нас fore-проход идёт по персонажу, поэтому спрашиваем про того, +> кого рисуем — страж в проёме тоже уходит за решётку. +> +> Шовный случай (ворота у СОСЕДА слева) работал и раньше — он ниже. +> Банк 2 +601 Б, банк 3 +92 Б, резидент не изменился. + +Нашёл пользователь 2026-08-11 (уровень 5, комната 24, верхние ворота): +Кид стоит на тайле ворот, а решётка рисуется ПОД ним — он виден целиком, +хотя должен быть за прутьями. + +**Как в оригинале.** `draw_tile_fore` (seg008:0D15) первой же строкой: + +```c +if (tile_left == tiles_4_gate && Kid.curr_row == drawn_row && + Kid.curr_col == drawn_col - 1 && Kid.room != room_R) { + draw_gate_fore(); +} +``` + +то есть когда Кид стоит ИМЕННО на тайле ворот, их решётка дорисовывается +в **foretable** — поверх персонажа: `draw_gate_fore` (seg008) кладёт спрайт +51 (низ решётки) и дальше вверх кусками по 8 px спрайтом 52, до +`gate_top_y`, прозрачным блитом. + +**Что есть у нас.** Портирован только ШОВНЫЙ случай (`pop_bg.c`, +`pop_fore_over_char`): если футпринт зашёл за левый шов и у соседа слева в +этом ряду ворота — их бары перерисовываются поверх персонажа через +`draw_gate_back`. Случая «ворота ВНУТРИ комнаты, персонаж на их тайле» +нет вовсе, отсюда симптом. + +**Почему не чинится одной строкой.** Правка идёт в `pop_fore_over_char` — +самый горячий путь кадра (memory `pop_fore_layer_cost`: когда-то 78 % +кадра, окно клипа дало 3.2×). Добавлять проверку надо так, чтобы она не +стоила ничего на каждом тайле футпринта: условие дешёвое (`тайл под +персонажем == ворота`), но рисование — это ещё один блит на кадр, пока +Кид стоит в проёме. Плюс нужен свой расчёт полосы (`gate_top_y` по +живому openness), а не готовый `draw_gate_back`, который рисует ЗА +персонажем. + +**Проверять:** уровень 5, комната 24 — встать в проём верхних ворот +(колонка 1 ряда 0) при частично поднятой решётке; прутья должны +перекрывать Кида. Тот же случай — любые ворота на уровнях 1-3. + +--- + + +## GUARD-ENTRY-DEATH. Вход в комнату со стражем вплотную = мгновенная смерть — ФИКС ЕСТЬ, ЖДЁТ ПРОВЕРКИ + +**Наблюдение (пользователь, 2026-08-13).** В некоторых ситуациях (особенно +после телепортов) Кид, входя в комнату, сразу оказывается чуть ли не в одном +тайле со стражем и немедленно погибает: меч ещё в ножнах, а безоружному любой +укол смертелен. + +**Что в оригинале.** Стража при входе НЕ отбрасывает — проверено по SDLPoP: +`enter_guard` (seg002:0112) ставит его ровно в тайл из данных уровня +(`Char.x = level.guards_x[room-1]`), никаких проверок дистанции до Кида нет. +Ситуация «вошёл вплотную» возможна и там. Защит четыре: + +1. **`bump_into_opponent` (seg003:08AA)** — главная. Безоружный Кид + (`Char.sword == sword_0_sheathed`) при вооружённом сопернике + (`Opp.sword != sheathed`, `Opp.action < 2`), лицом к лицу + (`Char.direction != Opp.direction`) и `can_guard_see_kid >= 2`, на + `ABS(char_opp_dist()) <= 15` не получает укол, а ОТСКАКИВАЕТ: `Char.y` + прижимается к полу, `fall_y = 0`, `seq_47_bump`. +2. Кид сам достаёт меч — `control_standing` (seg005:0351). +3. Страж поднимается в стойке покоя с мечом в ножнах — `seq_77`, ему нужно + сперва заметить Кида (`autocontrol_guard_inactive` → `move_down_forw`). +4. Вплотную страж не колет: `autocontrol_guard_active` при `distance < 12` + (или `< 8` с убранным мечом) разворачивается/шагает, а `check_hurting` + требует `min_hurt_range = 8` для безоружного противника. + +**Корень у нас.** Пункты 2-4 были портированы верно (`pop_ctrl.c:288`, +`pop_guard_cold.c:107`, `guards.c:553`), а **пункт 1 не портирован вовсе**: +в `kid_phys` (`pop_map.c`) между `determine_col()` и `check_collisions()` +было пусто, тогда как `play_kid_frame` (seg000:1238) зовёт там +`bump_into_opponent`. Без отскока дистанция успевает вырасти в рабочий +диапазон удара 8..29 — и первый же `move_6_shift` стража даёт +`hurt_by_sword` с `take_hp(100)`. + +**Сделано.** `bump_into_opponent` портирован в `pop_map.c` (рядом с +`bumped_floor`) и вызывается из `kid_phys` на штатном месте. Резидент не +вырос, банк 3: 11066 → 11156 Б. Опциональные фиксы SDLPoP +(`fix_painless_fall_on_guard`, `fix_jumping_over_guard`) намеренно НЕ +портированы — оставлено базовое поведение движка 1989 года. + +**Проверять:** войти в комнату со стражем вплотную (телепортом `+`/`−` или +`Shift+L`) без меча в руке — Кид должен отскочить с `seq_47`, а не умереть; +падение на стража сверху по-прежнему безболезненно (это оригинал, не баг). + +--- + + +## LOOSE-ROOM-CHANGE. Расшатанная плита не доваливается, если Кид ушёл в соседнюю комнату — ФИКС ЕСТЬ, ЖДЁТ ПРОВЕРКИ + +**Наблюдение (пользователь, 2026-08-13).** Уровень 10, комната 2: в (2,9) +стоит плита, которая под бегом Кида должна разрушаться. Если Кид пробегает +по ней и уходит в правую комнату, плита не успевает разрушиться и упасть — +переключение комнаты обрывает алгоритм. + +**Что в оригинале.** loose-пол — обычный **trob** (`make_loose_fall`, +seg007:0EF6 → `add_trob(curr_room, curr_tilepos, 0)`), а `process_trobs` +(seg007:0000) крутит ВЕСЬ список каждый кадр, независимо от `drawn_room`: +`animate_tile` делает `get_room_address(trob.room)` и работает с таблицей +той комнаты, где trob живёт. Фаза лежит в `curr_room_modif`, то есть прямо +в таблице уровня. Поэтому брошенная плита досчитывает до +`loose_floor_delay`, `remove_loose` убирает тайл, `add_mob` рождает кусок — +и всё это без Кида в комнате. Ровно так же ведут себя `mobs[]` +(`do_mobs`, seg007:1063) — их список чистится только в `start_level`. + +**Корень у нас.** Падающие КУСКИ (mob) чужую комнату умели давно +(`pop_loose_mob_room_changed`, [BUG-LOOSE-2](BUGS_CLOSED.md)), а вот ФАЗА +тряски/отсчёта жила в `pop_loose_modif[30]` — массиве ТЕКУЩЕЙ комнаты, — +и `pop_loose_reset()` при смене комнаты обнулял его целиком. То есть +плита, брошенная на полпути, возвращалась в покой. То же для плит-потолков +(`pop_ceil_modif[10]`). + +**Сделано** (двухуровневое хранение вместо переноса всей механики в trob — +`pop_loose_modif` остаётся быстрым массивом отрисовки текущей комнаты): + +1. `pop_loose_leave_room()` (`pop_map.c`, зовётся первым делом в + `enter_room_side`, пока карта ещё описывает СТАРУЮ комнату) — сдаёт фазу + всех loose-тайлов в персистентный `room_modif` (это и есть + `curr_room_modif` оригинала) и заводит trob на незаконченные. +2. `animate_loose()` (`pop_trob.c`, case `TILE_LOOSE` в `pop_process_trobs`) + — досчитывает фазу заочно; на провале ставит тайл в EMPTY + (`pop_level_set_tile`), кладёт в модификатор тип уровня (`remove_loose`) + и рождает кусок через новый `pop_loose_mob_spawn_at(room, row, col)`. + Пока комната ОТРИСОВАНА, trob снимается сразу — хозяин фазы + `pop_loose_tick`. +3. `pop_loose_reset()` теперь не обнуляет, а ЗАБИРАЕТ фазу новой комнаты из + `room_modif`: вернувшийся Кид застаёт плиту на той же фазе. +4. `pop_loose_forget()` — старт/рестарт уровня: забыть фазы БЕЗ сдачи + (у оригинала всё сносит `load_level`). Без него первый же `enter_room` + после `pop_trob_reset()` занёс бы в свежий уровень дрожь из прошлой жизни. + +Резидент не изменился (20569 Б); банк 3 +451 Б, банк 6 +181 Б, банк 7 +62 Б. +Хост-тест `phys_loose_survives_room_change` (`tests-host/t_phys.c`) закрывает +передачу фазы. + +**Проверять:** уровень 10, комната 2 — пробежать по плите (2,9) в правую +комнату и вернуться: дыра должна быть на месте. Регресс: комната 7 +(две соседние плиты подряд), комната 12 плита (0,1) → уход в 15 +([BUG-LOOSE-2](BUGS_CLOSED.md)), плита-потолок, рестарт уровня после смерти +на дрожащей плите. + + +--- + + +## LOOSE-SEAM-ANIM. Плита в шве исчезает без анимации падения — МЕЛКИЙ, ОТЛОЖЕН + +> **Решение пользователя (2026-08-13):** «пока пусть будет так». + +**Наблюдение.** Уровень 10: Кид расшатывает плиту (2,9) комнаты 2 и убегает +вправо в комнату 7, где та же плита видна в шве как (2,−1). Плита +доваливается правильно (см. [LOOSE-ROOM-CHANGE](#loose-room-change)) и с +экрана исчезает, но **мгновенно** — без дрожания и без падающего куска. + +**Почему так.** Заочный досчёт (`animate_loose`, `pop_trob.c`) сознательно +не рисует: комната не отрисована, а фаза живёт в `room_modif`, мимо +`pop_loose_modif`, на который смотрит отрисовка дрожащего кадра. Исчезновение +видно только потому, что провал меняет ТАЙЛ, а по этому событию +(`pop_neigh_dirty`) главный цикл перечитывает кромку и перерисовывает шов. +Падающий кусок (mob) рождается в комнате 2 и честно летит, но `mob_tick_one` +рисует только `here` (своя комната), а комната 2 — не отрисованная. + +**Что надо, если браться.** Шов — это НЕ обычный тайл: чужая колонка 9 +рисуется внутри нашей колонки 0 (`draw_tile`, ветка `lcode == 11`), а +дрожащий кадр берётся из `pop_loose_modif[row*10 + (col-1)]`, то есть при +`col == 0` индекс уезжает за границу массива (для ряда 0 — в −1). Значит +анимация шва потребует не «включить рисование», а дать чужой колонке +собственный источник фазы. Заодно там же чинить и этот выход за границу. + +**Проверять:** тот же сценарий уровня 10; в оригинале плита в шве дрожит и +роняет кусок так же, как в своей комнате. + + +--- + + +## JUMP-FLOOR-LEGS. Прыжок с пола: ноги Кида поверх кромки пола — ПОЧИНЕН, ЖДЁТ ПРОВЕРКИ + +> **Корень (найден трассой 2026-08-13).** Проход оверлеев в +> `pop_fore_over_char` обходил **только два крайних ряда** футпринта: +> +> ```c +> if (action != 2) other_overlay_tile(rB, c); /* опорный */ +> if (rT != rB) other_overlay_tile(rT, c); /* верхний */ +> ``` +> +> Это буквальный порт `redraw_at_char2` (seg003:0645), и в оригинале он +> верен: там `char_top_row` и `char_bottom_row` ВСЕГДА соседние. У нас между +> ними появляется дыра — `char_footprint` форсит `rT = rB − 1` (голова торчит +> в ряд выше), а затем окно fore-клипа расширяет `rB` ещё на ряд вниз. +> +> На спорном кадре трасса из MAME дала `rT=0 rB=2 cL=0 cR=1 do_ov=1` и **ноль** +> вызовов `overlay_mid_tile`: ряды 0 и 2 обошли, ряд 1 — тот самый, где стоит +> плита пола, — пропустили. Отсюда и подсказка пользователя «глубже в падении +> плита рисуется правильно»: там `y_to_row` даёт уже 2, ряд 1 попадает в пару +> крайних, и всё сходится. +> +> **Фикс:** обходить ВЕСЬ диапазон `rB..rT`, сохранив правило оригинала «при +> `action == 2` опорный ряд не трогаем». После фикса трасса на том же кадре: +> один вызов `overlay_mid_tile`, исход для (1,1) = «нарисовали», коды тайлов +> `curr=3 left=0` — байт в байт как в отладочном выводе SDLPoP. +> +> Пилларный оверлей, который «работал и раньше», к этому отношения не имел: +> это `fore_id = 95` из отдельного fore-прохода, а не midtable-оверлей. +> +> Банк 2 +28 Б. Хост-тесты (6 наборов) проходят. + +### Разбор, по которому искали (оставлен: метод пригодится) + +**Наблюдение (пользователь, 2026-08-13).** Уровень 10, комната 1, прыжок с +пола. В оригинале нижняя часть Кида скрыта ЗА передней гранью пола, у нас +рисуется весь Кид поверх неё (за передней колонной он при этом уходит +правильно). Скриншоты — в переписке сессии. + +**Состояние кадра (снято из памяти MAME на замороженном кадре, сборка +2026-08-13):** + +| поле | значение | +|------|----------| +| `Kid.frame` | 103 (`frame_103_start_fall_2`) | +| `Kid.action` | 3 (`actions_3_in_midair`) | +| `Kid.x` / `Kid.y` | 69 / 127 | +| `Kid.direction` | −1 (влево) | +| `Kid.curr_col` / `curr_row` | 0 / 2 | +| `Kid.room` / `cur_room` | 1 / 1 | + +`pop_y_land[2] = 118` — то есть ноги на 9 px НИЖЕ уровня пола ряда 1, ровно в +полосе его передней грани (`draw_tile_bottom` рисует её на `63*row + 65 = 128`). + +Тайлы комнаты 1 (`room_fg`, прочитаны там же): + +``` +ряд 0: 01 01 00 00 00 00 00 00 04 0B +ряд 1: 04 00 03 13 10 11 13 01 07 0F +ряд 2: 0C 01 03 01 01 0F 01 01 04 03 +``` + +Кромочная колонка левого соседа (`lcol_fg[0..2]`): `04 0C 04`. + +**Что УЖЕ проверено и исключено.** + +1. **Гейт `redraw_at_char2` (seg003:0645) портирован верно.** Сверено строка + в строку: наш `do_ov` в `pop_fore_over_char` даёт то же множество поз + (захват 78-79, `action` 2/3/4/6, `bumped` 102-106, начало подъёма 135-136), + и climb-ветка 137..144 тоже совпадает. При `action == 3` оверлей + ВКЛЮЧАЕТСЯ — дело не в гейте. +2. **`draw_other_overlay` (seg008:1492) на колонке 0 не сработал бы и в + оригинале.** Обе его ветки требуют пустого соседа: `tile_left == empty` + (у нас `lcol_fg[1] = 0x0C`, doortop — не пусто) либо `drawn_col > 0` и + пустой тайл ЧЕРЕЗ ОДИН слева. Значит окклюдер — НЕ он, и наш порт этой + функции здесь ни при чём. +3. **`pop_tile_code(row, −1)` кромку читает правильно** — из `pop_t_lfg`, а не + «стена всегда». + +**Две живые гипотезы, проверять в этом порядке.** + +* **Это вообще не отрисовка, а физика.** У нас `Kid.y = 127` при уровне пола + 118 — Кид «утоплен» на 9 px, и ноги торчат ниже кромки просто потому, что + они там и есть. Проверяется дёшево: снять `Char.y` у SDLPoP на том же + кадре 103 той же последовательности. Если там 118..120 — искать надо в + seqtbl/`fall_speed`, а не в слое фона. +* **Окклюдер — другой тайл/другой механизм.** Тогда нужна очная ставка: + прогнать SDLPoP с трассой (`dbg_shots`-fprintf в `draw_other_overlay` и + `add_kid_to_objtable` уже стоят в нашей копии, см. memory + `pop_pixel_diff_vs_sdlpop`) и сравнить СПИСОК тайлов, попавших в midtable + на этом кадре, с тем, что рисуем мы. Гадать дальше по коду смысла нет — + два прохода по seg008 уже не дали ответа. + +**Проверять после починки:** тот же прыжок в комнате 1 уровня 10; за передней +колонной Кид уходит и сейчас — это не должно сломаться. + + +--- + + +## MOB-CLIP-RIGHT. Окна и узор кладки рисуются поверх падающих плит + +**Найдено прогонами 2026-08-13 (уровень 13, комнаты 16 и 23).** + +**Симптом.** На падающей плите проступают элементы ЗАДНЕЙ стены — узор +кладки, а на ряде 0 и целое окно. Плита при этом цела: элементы ложатся +поверх неё, не стирая. + +**Корень.** `mob_render` (`pop_room.c`) каждый кадр вызывает +`draw_tile(r, m->col + 1)` — перерисовывает ЦЕЛЫЙ соседний тайл поверх уже +нарисованного куска, чтобы спрятать его правую часть (спрайт 42 лежит в +столбце `col+1`). `draw_tile` кладёт всё содержимое тайла, включая декор +пустого тайла — окно и узор. Ряд берётся из координаты куска, поэтому на +ряде 0 перерисовывается тайл с окном. + +Правка [#6](#) (клип перерисовки окном коридора heal) сделала артефакт +ЗАМЕТНЕЕ: раньше перерисовка размазывалась вокруг, теперь попадает точно в +прямоугольник плиты. + +**Как в оригинале.** Такой перерисовки нет вовсе. `redraw_at_cur_mob` +(seg007:1094) зовётся ТОЛЬКО из `loose_fall`, один раз в момент сбития, и +лишь ставит пометки на тайл и соседа. Правая часть куска прячется +собственным клипом объекта: `add_mob_to_objtable` (seg007:1161) задаёт +`clip.right = 40`. + +**Что делать.** Портировать клип вместо подпорки: + +1. выяснить, во что превращается `clip.right = 40` в наших координатах + (у оригинала `obj_x` в удвоенных единицах: `char_x_left = obj_x / 2 + 58`); +2. блитить правую часть (env 42) с ограничением ширины — примитив с шириной + есть для персонажей (`gfx_blit_cols_part_w`), для env надо посмотреть; +3. убрать `draw_tile(r, col+1)` и окно fore-клипа вокруг него; +4. вернуть коридор heal к 64x32. + +**Побочная выгода:** уходит по одному полному `draw_tile` на кусок за кадр — +при шести падающих плитах это шесть отрисовок тайла. + +--- + + +## MID-OVERLAY-LAYER. Оверлей кромки всегда поверх, а должен сортироваться + +**Найдено разбором 2026-08-13.** + +`overlay_mid_tile` (`pop_bg.c`) выполняется в проходе ПОСЛЕ персонажей, то +есть безусловно поверх всего нарисованного. В оригинале это `draw_other_overlay` +(seg008:1499), и он переключает таблицу на **midtable** — +тот же слой, где живут персонажи и падающие куски (`add_mob_to_objtable`, +тип `0x80`), а внутри слоя всё сортируется по `y` (`compare_curr_objs`, +seg008:1572). + +Следствие: узор из оверлея выигрывает у плиты, даже когда плита ближе к +зрителю. Пока у нас нет сортируемого midtable, точечными правками это не +лечится. + +Связано с [BG-ONCE](TASKS_OPEN.md#bg-once) — там та же техническая +сердцевина: привести наши слои в соответствие с таблицами оригинала. + +--- + + +## GUARD-FALLOUT-VICTORY. Выпавший из комнаты страж не засчитывается убитым + +**Найдено разбором 2026-08-13** (при проверке чита `K` на Джафаре). + +У `on_guard_killed` в оригинале ДВА места вызова: + +| место | что | +|---|---| +| `play_guard`, seg006:1494 | HP кончились — **портировано** | +| `check_guard_fallout`, seg002:264 | страж выпал из комнаты — **НЕ портировано** | + +Наш `pop_guard_fallout` (`pop_guard.c`) просто гасит слот. Значит столкнуть +Джафара в пропасть на 13-м уровне можно, а выход на 14-й от этого не +откроется — расхождение с оригиналом. + +Правка: позвать `on_guard_killed()` в ветке «не скелет». Мешает только то, +что функция сейчас `static` в `guards.c` (банк 1), а `pop_guard_fallout` +живёт в резиденте. + +--- + + +## LEVELDOOR-STARTROOM-WIPE. В стартовой комнате за входной дверью рисуется лестница вместо черноты + +**Найдено разбором 2026-08-17** (попутно к фиксу +[LEVELDOOR-PALACE-CLIP](BUGS_CLOSED.md#leveldoor-palace-clip) — то же +`draw_leveldoor`). + +`draw_leveldoor` (seg008:1D29) в приподнятой створке различает две комнаты: + +```c +if (modifier_left) { + if (level.start_room != drawn_room) { + add_backtable(..., 144 /*level door stairs*/, ...); + } else { + short leveldoor_width = (tbl_level_type[current_level] == 0) ? 39 : 48; + sbyte x_low = (tbl_level_type[current_level] == 0) ? 2 : 0; + add_wipetable(0, 8*(draw_xh + 1) + x_low, ybottom - 4, 45, leveldoor_width, 0); + } +} +``` + +Смысл: за дверью, **через которую вошли на уровень**, лестницы нет — там +чернота, и оригинал её ЗАТИРАЕТ прямоугольником, а не рисует марш 144. +Ширина затирки дворцовая/подземная (48 против 39) и подземная сдвинута на +2 px вправо — то есть здесь ЕЩЁ одно расхождение подземелья и дворца, помимо +уже починенного `leveldoor_right += 8`. + +У нас (`pop_room.c`, `draw_leveldoor`) стоит безусловно: + +```c +if (modif) + pop_env_b(144, x, ybottom - 4); /* лестница (комната ≠ стартовой) */ +``` + +— условие названо в комментарии, но не реализовано. Различие стартовой +комнаты у нас есть только в логике входа (`pop_leveldoor_enter`, +`pop_map.c:1166`), не в отрисовке. + +**Когда видно:** только пока створка входной двери приподнята +(`modif != 0`) в стартовой комнате — то есть в анимации входа на уровень и +если дверь снова открыть. При закрытой двери `modif == 0`, и обе ветки +ничего не рисуют, поэтому баг и не попался на обходах уровней 1-4. + +**Что мешает сделать прямо:** у нас нет понятия wipetable — нужен чёрный +прямоугольник в проходе backtable, то есть либо прямой `gfx`-fill в +`draw_leveldoor`, либо запечка. Проверять на уровне 1 (подземелье, ширина +39, сдвиг 2) И на уровне 4 (дворец, 48, сдвиг 0) — иначе поймаем только +половину. + +### BUG-SHADOW-SET. Тень в бою рисуется спрайтами стража + +**Симптом** (найдено пользователем 2026-08-20): в бою Тень выглядит не +Кидом. В SDLPoP она Кид всегда. + +**Корень.** Набор спрайтов выбирает не charid, а поле `sword` САМОГО +КАДРА: `load_frame_to_obj` (`seg008.c:1752`) держит `chtab_base` жёстко +равным `id_chtab_2_kid` и прибавляет `cur_frame.sword >> 6`. У всех 241 +кадра `frame_table_kid` эти биты нулевые, у всех кадров `frame_tbl_guard` +— `0xC0`. Тень берёт таблицу стража для кадров 150..189 (`seg006.c:533`), +значит идёт через chtab_5. + +Дальше и кроется наша ошибка: **chtab_5 — это не «страж», а «соперник +уровня»**. Он грузится из `tbl_guard_dat[tbl_guard_type[уровень]]` +(`seg000.c:1117`), а `tbl_guard_type[12] == 4` → **SHADOW.DAT**, и это +графика КИДА в боевых позах (палитра SHADOW.DAT побайтно равна палитре +Кида; у GUARD.DAT там серая рампа под перекраску). + +**У нас** `pop_guard_load` (`pop_cdraw.c:98`) разбирает только типы 2 +(скелет) и 3 (визирь), а всё остальное — включая тип 4 — уводит в +`guard_names`. SHADOW.DAT в ассетах отсутствует, каталога нет, в +Makefile правила нет. + +**Починка** — часть работы по атласу Тени, см. +[`../docs/shadow_atlas_plan.md`](../../PoP/docs/shadow_atlas_plan.md) Ш0/Ш1: +довезти SHADOW.DAT и запечь его во вторую половину атласа Тени. Отдельно +чинить смысла нет: как только Тень поедет своим атласом, ветка выбора +набора для типа 4 исчезнет вместе с проблемой. + +**Оговорка.** `pop_guard_set_palette` для типа 4 звать НЕЛЬЗЯ — она +затрёт палитру Тени палитрой стража (`curr_guard_color` у не-стражей = 0, +`seg002:183`). + +### BUG-CORPSE-AIR. Труп висит в воздухе после падения плиты + +**Симптом** (найдено пользователем 2026-08-20, ур. 12 к. 15, старт с +`POS=15`): Кид встал на проваливающийся пол (0,6), плита обрушилась и +убила его, но ТЕЛО осталось лежать на уровне исчезнувшего пола, а плита +улетела дальше вниз. Труп висит в пустоте. + +**Где смотреть.** Кадр смерти — `actions_5_bumped` (см. seq в +`kid_data`), а гравитация в `check_action` (seg006:05CD) для действия 5 +включается только на кадрах приседа: у оригинала есть отдельные фиксы +`FIX_DEAD_FLOATING_IN_AIR` (кадры 177..185) именно про этот случай — +значит **в оригинале 1989 года труп в воздухе тоже висит**, и SDLPoP +чинит это опциональным фиксом, по умолчанию выключенным. + +**Отсюда вопрос к решению**, а не сразу правка: чинить ли вообще. Если +чинить — портировать `fix_dead_floating_in_air` и записать как +осознанное расхождение в `../docs/impl_diff.md` (мы уже отказались от +двух других опциональных фиксов SDLPoP в `bump_into_opponent`). + +**Приоритет низкий:** ситуация возникает только когда труп остаётся на +проваливающемся полу, играбельности не мешает. + +--- + +## BUG-SND-FIRSTRUN — при ПЕРВОМ запуске первый эффект слегка искажён + +**Симптом** (пользователь, 2026-08-20): после загрузки системы, при первом +запуске программы, самый первый звуковой эффект идёт с искажением; при +повторном запуске программы искажения нет. Наблюдалось ещё на тестовых +примерах CBL, до PoP. + +**Причина, скорее всего, уже устранена.** Аппаратный буфер CBL (256 +слотов) железо не чистит, а запись в порт управления сразу пускает +воспроизведение с нулевого слота — то есть первые 23,4 мс играет то, что +лежало в буфере раньше. При первом запуске это остатки после загрузки +системы, при втором — наша же тишина, поэтому и слышно только один раз. +Разбор — `../docs/sound_plan.md` §10.2. + +Лечение сделано в libc: `_cbl_prime` заливает буфер тишиной сразу после +включения (`cbl_open`). **В MAME проверить нельзя**: эмулируемый буфер +стартует нулями при двухдополнительном ЦАП, то есть там этот дефект нем +изначально — записи до и после фикса совпали побитово по огибающей. + +**Что нужно:** проверка на РЕАЛЬНОМ железе, первый запуск после холодной +загрузки. Если искажение осталось — значит мусор приходит не из буфера +CBL, и копать надо в системных буферах DSS. + +**Приоритет низкий:** один раз за сеанс, на играбельность не влияет. + +--- + + +## PAL-L1-AFTER-INTRO. Вход в игру на уровень 1 после интро — палитры нет (чёрный экран) + +**Наблюдение (пользователь, 2026-08-23).** После заставок (title → intro → +demo) нажатие клавиши в attract-demo начинает новую игру на уровне 1 — +экран ЧЁРНЫЙ при живом геймплее. При этом: + +- Shift+L с уровня 1 на уровень 2 (тот же маршрут через pre-cutscene) — + палитра появляется (закрыто быстрым фиксом `fade_in_pending` в + `sprpop.c`, маршрут CUTSCENE → LEVEL_LOAD → PLAYING); +- Restart Game через меню — уровень 1 с НОРМАЛЬНОЙ палитрой. + +**Контекст.** Маршрут demo_new_game (`sprpop.c` ~996): dispatch(NEW_GAME) +→ `pop_level_switch()` → dispatch(LEVEL_READY) → PLAYING. По коду палитру +там никто не трогает, а демо-ветка перед этим делает +snapshot+dim(4,0)+fade_in(4) — то есть к моменту новой игры палитра должна +быть яркой. Контрольный прогон в MAME (сборка с фиксом) демо и уровень 1 +за настройками показал ЯРКУЮ картинку — воспроизведение нестабильно. + +**Гипотезы для проверки (порядок дешевизны):** + +1. **Тайминг нажатия.** Клавиша, СОСКИПНУВШАЯ intro (intro_run абортится по + `kbd_raw_any_down`), остаётся в held-state и первым же кадром демо даёт + `demo_new_game` — но fade_in демо-ветки блокирующий и завершается ДО + входа в игровой цикл, так что в эту гипотезу код не верит. Проверить + трассой: снять обе экранные палитры (`gfx_pal_get` 0 и 1) на первом кадре + PLAYING. +2. **Снимок демо-ветки снят с чёрной палитры.** Если нажатие клавиши + приходится МЕЖДУ `intro_load_palette` (fload+sync+snapshot+dim(4,0)) и + `intro_restore_game_palette` — аборт оставляет экран чёрным, но restore + всё равно выполняется после... сверить фактический порядок вызовов + трассой. +3. **`font_ready`=0 в момент fade_in** — dim/fade молча выходят (guard в + `pop_ui.c`). Маловероятно (шрифт грузится в `pop_boot` → + `pop_menu_init`), но дёшево исключить. + +**Правильное решение, скорее всего, — этап B из +[`../docs/palette_plan.md`](palette_plan.md):** явный +«load → apply/fade» контракт на КАЖДОМ входе в PLAYING, а не точечные +fade_in в отдельных маршрутах. Точечно чинить только после стабильного +воспроизведения. + +--- + + +## PAL-DUNGEON-STALE. Переход 3→4 (подземелье→дворец): остаётся палитра подземелья + +**Наблюдение (пользователь, 2026-08-23).** Shift+L с уровня 3 на уровень 4: +pre-cutscene отыгрывает, уровень 4 начинается — но env/wall-цвета (слоты +0x50–0x6F) остаются ПОДЗЕМНЫМИ. На переходе 1→2 (оба подземелье) всё +правильно. + +**Корень ясен.** Быстрый фикс `fade_in_pending` (`sprpop.c`, маршрут +CUTSCENE → LEVEL_LOAD → PLAYING) восстанавливает яркость из СНИМКА, а снимок +делает `intro_restore_game_palette()` ДО `pop_level_switch()`: + +``` +pop_pre_cutscene_show(pre) → kid.pal + bg(СТАРЫЙ набор) + snapshot + dim(4,0) +pop_level_switch() → pop_bg_load(дворец): в палитры записаны + 32 записи ДВОРЦА (pal_tile.pal) +pop_ui_fade_in(4) → восстанавливает ОБЕ палитры ИЗ СНИМКА — + то есть ЗАЛИВАЕТ НАЗАД подземные 0x50..0x6F +``` + +fade_in по построению пишет все 256 записей из снимка — он затирает и только +что загруженный тайлсет. На 1→2 не видно, потому что наборы совпадают. + +**Как чинить (в порядке правильности):** + +1. **По плану (этап B `../docs/palette_plan.md`):** разделить «логическую + палитру» и «яркость» (pop_pal: load_* обновляет снимок ПОСЛЕ bg_load, + apply/fade только показывает). Тогда снимок на момент fade_in всегда + актуален. +2. Точечно: переставить обновление снимка ПОСЛЕ `pop_level_switch()` + (снять snapshot заново перед fade_in). Работает, но это ещё один пластырь + поверх модели, которая и так уже в трёх местах по-разному управляет + яркостью. + +**Проверять:** Shift+L 3→4 И 5→7 (дворец→подземелье, обратное направление), +плюс штатный проход уровня 3 через дверь (там же смена набора); после починки +сверить цвета кладки дворца с `pal_tile.pal` (таблица в palette_plan.md §1.3). + +**Решение пользователя (2026-08-23): пока не фиксить** — закрыть вместе с +этапом B плана палитр. + +--- + + +## SND-PACE-DEAD. Пейсинг сцен по насосу CBL не включается + +**Замер (MAME, 2026-08-27).** В сцене `intro_pv_scene()` зонды показали +`snd_done == 0` на всём прогоне — то есть ветка `if (snd_pace)`, ради +которой и заводилась вся арифметика 57/40, **не исполняется ни разу**. +Сцена идёт по запасной кадровой ветке (`POP_T60` + `gfx_wait_vsync`). + +**Корень.** `pop_intro.c`: + +```c +snd_prev = pop_snd_tick; +... +snd_pace = (uint8_t)(pop_snd_tick != snd_prev); /* часы реально идут? */ +``` + +`pop_snd_tick` — счётчик порций насоса, он растёт раз в 11,7 мс. Между +двумя этими чтениями проходят единицы микросекунд, поэтому значения +совпадают почти всегда и признак выходит НУЛЕВЫМ, даже когда звук +исправно играет. + +**Чем плохо.** Кадровая ветка не имеет догона вообще, и вся работа по +привязке сцены к часам звука пропадает впустую. Симптом — [PV-RENDER-BOUND](#pv-render-bound). + +**Как чинить.** Проверять «часы идут» с реальным зазором: снять +`pop_snd_tick`, подождать кадр (`gfx_wait_vsync()`), снять снова. Одна +строка, но включение ветки меняет темп ВСЕХ сцен — трогать только вместе +с прогоном каждой. + +--- + + +## PV-RENDER-BOUND. Сцена с принцессой не укладывается в свой бюджет кадра + +**Замер (MAME, 2026-08-27).** Реплика Джафара `m53` — 1403 порции насоса, +то есть 16,42 с реального времени. За это время шкала сцены прошла +**811 тиков вместо 985**: анимация идёт со скоростью ~49 тиков/с при +номинале 60. Отставание пропорционально времени, поэтому одинаково на +любом наборе записей (flac / mt32). + +Зонды исключили другие версии: длина трека отдана насосу полная +(`pop_mus_left` на пуске = 1403), курсор дошёл до последней страницы +(10 из 11) — **трек доигрывает целиком, отстаёт именно картинка**. + +**Следствие, которое важно понимать.** Звук, выданный САМОЙ анимацией +(створка ворот, дверь покоев), разъехаться с ней не может: у них общая +шкала. Уезжает только внешний по времени звук — музыка. Поэтому +эффекты дверей совпадают, а кода реплики приходит раньше молнии. + +**Обход (сделан 2026-08-27).** `PV_MAGIC_LEAD` в `pop_intro.c` двигает +жест заклинания целиком (замах, шаг назад, вспышка) на подобранную на +слух величину. Это подгонка, а не лечение. + +**Как чинить по-настоящему.** Удешевить кадр сцены: сейчас он +перерисовывается целиком (`gfx_copy_page` на весь экран + актёры + +факелы + звёзды). Кандидат — перерисовывать только изменившиеся куски, +как в игровом слое. + +--- + + +## MUS-LEFT-TEAR. Неатомарное чтение `pop_mus_left` + +**Хазард.** `pop_mus_left` — `volatile uint16_t`, который УМЕНЬШАЕТ +прерывание насоса (`pop_sfx.c`), а читают его из главного цикла обычным +чтением: `pop_music_busy()` (`pop_music.c`), `pop_ctrl.c`, `pop_music.c` +(освобождение трека). На Z80 это две загрузки по байту. Если насос +успеет между ними при заёме из старшего байта (`0x0100` → `0x00FF`), +сложится **ложный ноль** — «трек кончился» за 256 порций, то есть за три +секунды до настоящего конца. + +**Где выстрелит.** `pop_music_busy()` ждут `hof_wait_music()` (финальная +тема) и титры («держим логотип до конца темы»): ложный ноль оборвёт +ожидание на три секунды раньше. + +**Статус.** Показан рассуждением; в прогоне 2026-08-27 не проявился +(проверено зондом с устойчивым чтением — результат не изменился). Окно +узкое: несколько тактов из 245 000 между прерываниями. + +**Как чинить.** Геттер, читающий до совпадения двух подряд: + +```c +uint16_t pop_music_left(void) { uint16_t a, b; + do { a = pop_mus_left; b = pop_mus_left; } while (a != b); return a; } +``` + +Вариант с `di`/`ei` тоже годится и стоит те же копейки (десяток тактов +раз в кадр, задержка прерывания ничтожна против 11,7 мс периода насоса), +но двойное чтение не трогает состояние прерываний вовсе — на фоне +[cbl_w0_bios_conflict](../../PoP/roomtest/BUGS_CLOSED.md) это плюс. diff --git a/applications/SprPoP/docs/KID_PLAN.md b/applications/SprPoP/docs/KID_PLAN.md new file mode 100644 index 0000000..cc24ab3 --- /dev/null +++ b/applications/SprPoP/docs/KID_PLAN.md @@ -0,0 +1,214 @@ +# Prince of Persia — Kid (персонаж): анализ и план + +> **Статус: РЕАЛИЗОВАНО (2026-08-01).** Kid играется целиком: интерпретатор +> `seqtbl` + `frame_table` (`src/pop_kid.c`), диспетчер `control()` +> (`pop_ctrl.c`), коллизия/физика/зацеп (`pop_map.c`), бой и HP. Модель +> персонажа стала общей: `Char`-окно (`pop_state.c`) обслуживает и Кида, и +> стражей. Таблицы кадров и `seqtbl` уехали из `_CODE` в EMM-страницу +> (`kid_data.bin`, см. `layout_plan_v2.md` шаг 1). +> +> Отступление от §2.1 плана: выбран ПАДДИНГ кадров (общий канвас), а не +> per-frame offset — компромисс зафиксирован в `PORT_PLAN.md §6.1`. +> +> **Документ оставлен как СПРАВОЧНИК по модели персонажа** (`char_type`, +> категории `actions_*`, устройство `play_seq`, объём спрайтов) — он нужен +> при портировании остальных акторов (скелет, тень, визирь). Текущие +> задачи — `../TASKS_OPEN.md`. + +Составлен 2026-07-16. Опирается на разбор `SDLPoP/src/seg006.c` +(ядро физики/управления Kid), `seqtbl.c` (таблицы последовательностей), +`types.h` (char_type, seq_*, SEQ_*, actions_*), `SDLPoP/data/KID` (спрайты). +Фон уже готов и проверен на MAME (`applications/SprPoP`, см. +`memory/pop_background_strategy`) — Kid развиваем в том же `SprPoP` как +новый PoC (решение пользователя: старый `poc/` не трогаем). + +**Копирайт:** спрайты Kid (`SDLPoP/data/KID`) — Broderbund/Ubisoft. +Использование настоящей графики Kid — сознательное решение пользователя +(в отличие от плейсхолдера в старом `poc/`, см. PORT_PLAN §5.1). + +--- + +## 1. Как устроен персонаж в оригинале (что портируем) + +### 1.1 Состояние — `char_type` (14 полей, types.h) + +``` +frame текущий номер кадра (индекс во frame_table_kid) +x, y позиция (byte; x — с учётом direction) +direction -1 влево / 0 вправо +curr_col, логическая клетка (тайл), где персонаж +curr_row +action КАТЕГОРИЯ действия (actions_*, см. 1.2) +fall_x, скорость падения (fall_y<22 = 1 ряд, <33 = 2 ряда) +fall_y +room комната +repeat счётчик для удержания-ввода (напр. повторный прыжок) +sword есть ли меч (бой — вне Фазы 1) +alive жив/мёртв +curr_seq УКАЗАТЕЛЬ в seqtbl (байткод текущей последовательности) +``` + +Состояние крошечное — легко живёт в W2. + +### 1.2 Категории действия — `actions_*` (9 шт) + +`0 stand`, `1 run_jump`, `2 hang_climb`, `3 in_midair`, `4 in_freefall`, +`5 bumped`, `6 hang_straight`, `7 turn`, `99 hurt`. `action` определяет, +как `check_action()`/`play_kid()` реагируют на ввод и физику каждый тик. + +### 1.3 Движок анимации/движения — ГЛАВНОЕ + +**Движение НЕ физика, а байткод + per-frame смещения** (подтверждает +PORT_PLAN §6). Три уровня: + +1. **`seqtbl`** — байткод-программа на действие. Опкоды (types.h): + `SEQ_DX`(0xFB) сдвиг x на amount×direction, `SEQ_DY`(0xFA) сдвиг y, + `SEQ_FLIP`(0xFE) разворот, `SEQ_JMP`(0xFF)/`SEQ_JMP_IF_FEATHER`(0xF7), + `SEQ_UP`/`SEQ_DOWN`(0xFD/0xFC) смена ряда, `SEQ_ACTION`(0xF9) задать + `Char.action`, `SEQ_SET_FALL`(0xF8), `SEQ_KNOCK_UP/DOWN`, `SEQ_SOUND`, + `SEQ_DIE`/`SEQ_END_LEVEL`/`SEQ_GET_ITEM`. **Байт < 0xF0 = НОМЕР КАДРА** + → ставит `Char.frame` и play_seq возвращается (один кадр за тик). +2. **`play_seq()`** (seg006.c:570) — интерпретатор: крутит опкоды из + `seqtbl + Char.curr_seq`, пока не встретит кадр. ~15 case — портируется + 1-в-1. **Квирк:** seqtbl использует АБСОЛЮТНЫЕ DOS-адреса в JMP; + `SEQTBL_0 = seqtbl - SEQTBL_BASE(0x196E)` — при порте пересчитать + базу (JMP-адреса в наших данных). +3. **`frame_table_kid[]`** (seg006.c:127, ~180 кадров) — на КАЖДЫЙ кадр: + `{image, sword_flags, dx, dy, flags}`. `image` — индекс спрайта Kid; + `dx/dy` — смещение позиции ЭТОГО кадра; `flags`: 0x1F weight_x, 0x20 + thin, 0x40 needs_floor, 0x80 even/odd-pixel (влияет на x-рендер). + +**Тик персонажа:** `play_kid()` (диспетчер по action+вводу) → `play_seq()` +(двигает curr_seq, ставит кадр, применяет seq-dx/dy) → `frame_table[frame]` +даёт image+собственные dx/dy → позиция и спрайт. У нас это ложится на +`sprite_frame`+`sprite_move` (НЕ `sprite_anim`/`sprite_moveto` — см. +PORT_PLAN §6: авторские таблицы, не автопрогрессия). + +### 1.4 Управление — `control_kid()`/`read_user_control()` (seg006.c) + +Читает ввод (у нас — held-state `kbd_raw`, уже готово, §2 PORT_PLAN) и по +`Char.action` выбирает последовательность (`seqtbl_offset_char(seq_id)`). +Логика «что можно из какого состояния» — ядро ощущения PoP. + +### 1.5 Взаимодействие с картой — collision (seg006.c) + +`check_on_floor()`/`start_fall()` — пол под ногами / падение в яму; +`in_wall()` — упор в стену (сдвиг наружу); `check_grab()`/ +`can_grab_front_above()` — зацеп за уступ; `fell_out()` — вывалиться из +комнаты; `check_spiked()`/loose — ловушки; `fall_accel()`/`fall_speed()` — +ускорение падения. Всё читает ТИП тайла (`get_tile`) — у нас это уже +разобранные `fg[]/bg[]` (level.h/room1_data.h). + +--- + +## 2. Спрайты Kid (219 шт, 16 цветов, 177 КБ) + +- 219 PNG (`data/KID`), 16-цветные (палитра `res400.pal`, 16×RGB как env/ + wall), макс кадр **53×35** — влезает в лимит движка 64×64. 177 КБ в + 8bpp. +- `frame_table_kid` отображает кадр→`image` (индекс спрайта). Число + РАЗЛИЧНЫХ image — уточнить (≤219); паковать те, что реально используются + платформинг-последовательностями Фазы 1 (не все 219 — бой/катсцены + отдельно). +- **Палитра:** Kid 16 цветов → слоты Sprinter `0x70-0x7F` (env 0x50, wall + 0x60 уже заняты; Kid не пересекается). Пиксель i: 0→0xFF, i→0x70+i. + Тот же пайплайн, что `pop_pack_bg.py`. +- **Атлас:** прямая адресация по номеру image (как фон): `kid[img>>5]`, + idx `img&31`; ~7 EMM-страниц (или SHIFT=4). Свой пакер `pop_pack_kid.py` + (переиспользовать код `pop_pack_bg.py`). + +### 2.1 РЕШЕНИЕ ДО СТАРТА: per-frame offset vs padding + +Кадры Kid — РАЗНОГО размера, а `sprite_t` рисует от угла фикс. w/h. Два +пути (см. PORT_PLAN §6.1, `memory/png_strip_padding_tradeoff`): +- **Padding** (bottom-center) — просто, но 219×53×35 ≈ 406 КБ (раздув ×2.3). +- **Per-frame offset** — хранить XCO/YCO кадра (у оригинала он и есть, + `APPLEII_RESOURCE_FORMAT §2.2`), рисовать `blit(x+xco, y+yco)`; паддинг не + нужен, память по факту (177 КБ). Требует лёгкого расширения хранения + (offset рядом с кадром) ИЛИ ручного смещения в коде рендера Kid. +**Рекомендация:** per-frame offset — оригинал так и делает (frame_table dx/dy ++ image XCO/YCO), даёт точное позиционирование И экономию. Хранить xco/yco +в нашей копии frame_table (добавить 2 байта/кадр — ~360 Б). Не тянуть +расширение `sprite.h` — рисовать Kid прямым `gfx_blit(x+xco, y+yco, img)` +(как фон), НЕ через retained `sprite_t`, раз позиция и кадр всё равно +задаются вручную каждый тик. + +--- + +## 3. Данные для порта (объём) + +- `frame_table_kid` → C-массив ~180×(5+2 offset) ≈ 1.3 КБ (const, ROM). +- `seqtbl` (нужные последовательности) → C-массив байт. Весь seqtbl ~1-2 КБ; + для Фазы 1 можно взять только платформинг-последовательности (вырезать + бой/гардов 55-92) — оценить после разметки. JMP-адреса пересчитать под + свою базу. +- Спрайты — атласы (EMM, не W2). + +--- + +## 4. Фазы работы (по твоему списку, порядок по зависимостям) + +**Фаза K0 — конвейер спрайтов + отрисовка одного кадра** +- `pop_pack_kid.py`: 219 (или подмножество) → `kid*.atl` + `kid.pal` + (слоты 0x70), таблица кадр→image + xco/yco. +- Отрисовать Kid ОДНИМ кадром (stand) в SprPoP поверх фона на верном + тайле — проверить палитру/позицию/прозрачность на MAME. +- Артефакт-цель: Kid стоит на уступе комнаты 1 как в `1.1-2.png`. + +**Фаза K1 — движок анимации (play_seq + frame_table)** +- Портировать `play_seq()` (интерпретатор) + `frame_table_kid` + минимальный + `seqtbl` (stand/run/turn). +- Прогнать несколько последовательностей вручную (stand→run→stop) — + проверить, что кадры и смещения совпадают с оригиналом (сверять с + SDLPoP/скриншотами, тайминг 50 Гц). + +**Фаза K2 — управление на месте + ходьба (твои а, б)** +- `control_kid` подмножество: stand (2), run (1/84/13), turn (5/6), + standing_jump (3), crouch (50/49), safe_step (29-44 — аккуратный шаг). +- Held-state через `kbd_raw` (готово). + +**Фаза K3 — коллизия с картой (твой п.3)** +- `check_on_floor`/`start_fall` — падение в ямы (тип тайла под ногами из + `fg[]`); `in_wall`/стоп у стены; `fell_out` (край экрана — пока без + перехода комнат). +- Падения/приземления (seq 7/17/19/20) + `fall_accel/fall_speed`. + +**Фаза K4 — прыжки и повисание (твои а-прыжок, в)** +- run_jump (4), jump_up (28/14), grab (8/16/24), climb_up (10)/down (68), + hang (25/6), release (11/23). Это самый «PoP-овый» кусок — сверять + дистанции/тайминг с оригиналом (не на глаз). + +**Фаза K5 — прочее (твой г)** +- drink (78), level_door (70), crouch_hop (79), spiked/loose/chomped + (ловушки, если тайлы есть в комнате), death (71). + +Бой (меч, seq 55-92, стражники — seg005) — ВНЕ этого плана (отдельная фаза +полного приложения, PORT_PLAN §7 Фаза 3). + +--- + +## 5. Риски/решения ДО кода (правило defer_unexplained_quirks) + +1. **Per-frame offset** (§2.1) — решить до K0 (влияет на формат данных). + Рекомендация: xco/yco в frame_table, прямой blit. +2. **seqtbl rebasing** — JMP-адреса абсолютные (SEQTBL_BASE 0x196E); при + порте пересчитать в оффсеты своего массива. Проверить на 1-2 seq. +3. **Тайминг** — оригинал (DOS) фиксированный тик; наш 50 Гц. Если + логическая частота кадров иная — пересчёт dx/dy (PORT_PLAN §8.4). + Сверять дистанцию бега/прыжка с эталоном. +4. **Число реально нужных кадров/последовательностей** для Фазы 1 — + разметить (вырезать бой/катсцены/гардов), чтобы не тянуть все 219 + спрайта и весь seqtbl. +5. **Копирайт графики Kid** — подтверждено решение пользователя (§вводная). + +--- + +## 6. Что переиспользуем (готово) + +- Фон комнаты (`pop_bg.c`) — Kid рисуется ПОВЕРХ (сейчас — прямым blit; + heal против фона — когда/если понадобится через RAM-копию, фон её уже + заполняет, `GFX_BANK_TRANSPARENT`). +- `kbd_raw` held-state (§2 PORT_PLAN) — готов и проверен. +- Пакер спрайтов/палитра (`pop_pack_bg.py`) — шаблон для `pop_pack_kid.py`. +- Разобранная карта комнаты (`fg[]/bg[]`, level.h) — для коллизий. +- `gfx_blit`/`gfx_w0_map` из W0-атласа — проверенный путь (bgtest/SprPoP). diff --git a/applications/SprPoP/docs/PORT_PLAN.md b/applications/SprPoP/docs/PORT_PLAN.md new file mode 100644 index 0000000..9af3f17 --- /dev/null +++ b/applications/SprPoP/docs/PORT_PLAN.md @@ -0,0 +1,555 @@ +# Prince of Persia на ZX Sprinter — план порта + +## СТАТУС (обновлено 2026-08-01) + +Документ составлен 2026-07-15 как план «с нуля» и с тех пор во многом +исполнен. Читать его надо так: + +| Раздел | Что с ним сейчас | +|--------|------------------| +| §1 возможности библиотек | актуально как обзор, но **спрайтовый движок `sprite.h` для персонажей НЕ используется**: Kid/страж рисуются прямыми блитами атласов (`gfx_blit_cols_part*`) с ручным heal — так требует модель оригинала (§6) | +| §2 held-state клавиатуры | **сделано** (`kbd_mod_state`, ``). Открытая проблема — потеря байт при аккордах Shift+стрелка; диагноз и план в `../PoP/SprPoP/TASKS_CLOSED.md` (KBD-1) | +| §3 форматы данных | актуально; уровень читается живьём (`src/pop_level.c`) | +| §4 стратегия фона | **сделано** — тайловый рендерер в рантайме (`src/pop_bg.c`) | +| §5 PoC | **закрыт и превзойдён.** `poc/` (плейсхолдер-персонаж) — история; активная разработка ушла в `SprPoP/` с настоящей графикой | +| §6 модель движения | **сделано**: `play_seq` + `frame_table` оригинала, не физика с нуля | +| §7 фазы | см. отметки статуса прямо в разделе | +| §8 риски | п.1 закрыт, п.3 закрыт (28 страниц-атласов Кида), п.2/п.4 — см. отметки в разделе | +| §10 режим памяти | **сделано и переросло план**: `huge` + четыре банка кода; актуальная раскладка — `layout_plan_v2.md` | + +**Где смотреть текущее состояние, а не план:** `../SprPoP/README.md` +(что играется), `../TASKS_OPEN.md` (что в работе), `levels_plan.md` +(следующие уровни), `layout_plan_v2.md` (раскладка кода по окнам и банкам). + +--- + +Опирается на +`APPLEII_RESOURCE_FORMAT.md` / `MSDOS_RESOURCE_FORMAT.md` / `README.md` в +этой папке, на текущий sprinter-cc/libc/libbgi (см. §1) и на локальные копии +`applications/PoP/SDLPoP` (github.com/NagyD/SDLPoP, GPLv3) и +`applications/PoP/PR` (github.com/NagyD/PR, GPLv2) — используются только как +справочник по структурам/константам оригинального движка и как источник +готовых распакованных ассетов (`SDLPoP/data/`), не как код для копирования. + +--- + +## 1. Что уже есть в sprinter-cc и библиотеках (используем как есть) + +Собрано из `docs/TODO.md`, `docs/libc-reference.md`, `docs/sprite-api-design.md`, +`libbgi/include/{gfx.h,sprite.h,graphics.h}`, `examples/rpgwalk`. + +- **Графика 320×256×256** (`GFX_MODE_320x256x256`, режим 0x81) — разрешение и + глубина цвета совпадают почти впрямую с VGA-ассетами оригинала + (`SDLPoP/data/VPALACE`, `VDUNGEON` — уже 256-цветные PNG). Не нужно ужимать + в EGA/CGA палитру. +- **BGI-слой** (`graphics.h`) — примитивы, палитра, текст, `getimage/putimage` + — Фазы 1-2d готовы и проверены в MAME. +- **Спрайтовый движок v2** (`sprite.h`, ветка `sprite-engine-v2`) — ровно то, + что нужно персонажам PoP: + - retained-модель (`sprite_update`/`sprite_flip`, double-buffer, dirty-биты, + heal+blit за один проход); + - кадровая анимация по ленте (`sprite_anim`, LOOP/PINGPONG/ONCE, + горизонтальная/вертикальная лента) и tween-перемещение + (`sprite_moveto`, DDA без knowledge-heavy арифметики); + - Y-сортировка слоями (`gfx_sprite_ysort`, `layer`) — то, что нужно для + «Кид перед/за стражником» без ручной пересортировки; + - атласы в EMM-страницах (`atlas_t`/`atlas_load`) — на восьмерых + персонажей в `rpgwalk` уже работает: прямой прецедент для Кида/стражника; + - ограничение кадра ≤ 64×64 — с запасом (см. §3: кадры Кида в оригинале + ~12-30 × 39-42 px). +- **Frame pacing** (`gfx_set_fps_div`) + цепочка кадровых IRQ — стабильный + логический тик независимо от рендер-нагрузки экрана (проверено MAME). +- **EMM-бюджет**: ~3.3 МБ свободно на старте (`memory/sprinter_emm_budget`) — + с большим запасом на все спрайт-атласы и предрендеренные фоны комнат (см. + §4) даже без выгрузки неиспользуемых уровней. +- **Файловый ввод-вывод** (FILE* v2, `fopen/fread/...`) — для загрузки + уровней/атласов/палитр с дискеты, по образцу `rpgwalk` (`atlas_load`, + `gfx_pal_fload`). +- **Клавиатура (событийная)** — `kbhit/getch/getkey` (ASCII + `KEY_*` скан-код + для стрелок), см. §2 — это НЕ то, что нужно для управления Кидом один в + один (см. ниже). +- **Звук** — `cbl.h` (потоковый CBL/COVOX, callback-модель, verified MAME) — + подходит для оцифрованных эффектов (`digisnd*.dat` — PC-звук + ~11 кГц 8-бит, см. `MSDOS_RESOURCE_FORMAT.md` §4). + +Вывод: **движок отрисовки и анимации почти не требует нового кода** — +самый близкий по духу пример (`rpgwalk`: атласы, анимация, tween, дабл-буфер, +FPS-делитель) переносится на PoP почти без изменений архитектуры. + +--- + +## 2. Единственный принципиальный пробел: удержание клавиш + +**Спайк проведён (2026-07-15), вопрос закрыт артефактами — не догадкой.** + +`getch`/`getkey` — это события ESTEX WAITKEY/SCANKEY (по нажатию), без чёткой +информации о СОСТОЯНИИ (что зажато прямо сейчас, несколько клавиш +одновременно). Prince of Persia на управлении требует именно состояния: +держать направление (бег) + одновременно нажать вверх (прыжок вперёд), держать +Shift (модификатор) + направление и т.д. + +### 2.1 Находки + +1. **`docs/converted/ProgrammerManual.txt` документирует функцию, которую мы + раньше пропустили: `CTRLKEY` (ESTEX $33h)** — «Получить состояние + клавиатуры». Дословно: «данные берутся не из буфера клавиатуры (как в + остальных функциях), а непосредственно из результатов ПОСЛЕДНЕГО + сканирования» — то есть это НАСТОЯЩЕЕ live-state, не событие. Но + покрывает только модификаторы: Left/Right Shift, Ctrl, Alt, + Rus/Lat, Num/Scroll/Caps Lock, Insert (не обычные клавиши вроде стрелок). + Готовое решение для «держать Shift = бежать» — тривиальная обёртка, + без архитектурных рисков. +2. Для ОБЫЧНЫХ клавиш (стрелки, буквы) такого live-state нет нигде в ESTEX — + `WAITKEY`/`SCANKEY`/`TESTKEY` ($30/$31/$37h) — все три отдают ОДИНАКОВЫЙ + формат «очередное нажатие», без release. `TESTKEY` не удаляет событие из + буфера (полезно для «подсмотреть, не потребляя»), но это тоже разовое + нажатие, не состояние. +3. Автоповтор клавиатуры (typematic) не годится как замена held-state: + `MAME_MCP_GUIDE.md` фиксирует задержку до первого повтора ~1 секунда + (типично для PS/2) — на порядок медленнее кадра (20 мс), не подходит для + платформера. +4. **Решающий артефакт — `libc/irq/_irq_tramp.c` (сам трамплин прерывания, + не гипотеза):** вектор 0xFF общий для кадра/клавиатуры/CBL. Ветка + клавиатуры (бит 0 порта 0x19 = SIO-A RR0 «байт принят») делает буквально + `jp 0x0038` (прямиком в DSS) **до какого-либо чтения порта данных 0x18 И + до нашей кадровой цепочки (`_irq_chain`)** — наш `irq_chain_add` + вообще не видит клавиатурные прерывания, они физически не доходят до + цепочки (см. `tr_notkbd`/`tr_frame` разбор в файле). Значит текущая + инфраструктура (тот же механизм, что несёт FPS-делитель) НЕ дает + зацепки для клавиатуры без правки самого трамплина. +5. Регистр данных SIO (порт 0x18) — аппаратный приёмный буфer, чтение + деструктивно (дёргает байт из очереди); кто прочитал первым, тот и + владеет байтом. Значит «подглядеть, не мешая DSS» технически + невозможно — необходимо либо совсем не трогать этот путь (статус-кво), + либо взять его СЕБЕ полностью на время геймплея. + +### 2.2 Рекомендация (конкретная, не три равнозначных варианта) + +**A. Тривиально, почти без риска — обернуть `CTRLKEY` ($33h)** отдельной +функцией (например `kbd_mod_state()` в ``) — даёт настоящий +held-state для Shift/Ctrl/Alt. Можно делать хоть сейчас, не архитектурное +решение. + +**B. Для обычных клавиш (стрелки и т.д.) — по прецеденту CBL.** В +`_irq_tramp.c` уже есть пример «приватного» пути на том же векторе 0xFF, +который сознательно НЕ чейнится к DSS (CBL: бит 7 порта 0xFE, свой +хук `_irq_cbl_hook`, полный сейв, свой `reti`). Предлагаемый новый +компонент `` — симметричный: ветка по биту 0 порта 0x19 читает +порт 0x18 САМА (декодирует PS/2 make/break, `0xF0`-префикс — протокол +уже задокументирован в `docs/samples/sprinterKeybLib.asm`), ведёт битовую +карту «клавиша N зажата», и НЕ прыгает в DSS, пока путь активен — +жизненный цикл `kbd_raw_open()`/`kbd_raw_close()` один в один как у +`cbl_open`/`cbl_close`. + + **Важное следствие (сообщить пользователю явно, не прятать):** пока + `kbd_raw_open()` активен, DSS вообще не получает клавиатурных байт — + `kbhit/getch/getkey/CTRLKEY` заведомо не будут работать, ESC для выхода + в DSS-смысле тоже (нужно проверять raw-битовую карту самим). Это + нормально для активной фазы геймплея (у самой игры и так свой цикл + ввода), но означает: экраны/паузы, которым нужен ESTEX-ввод (например, + диалог сохранения через `fopen`, если тот когда-либо потребует ввода + с консоли), должны на это время `kbd_raw_close()`. + +**Не рекомендую вариант «таймаут-эвристика поверх SCANKEY»** — after +находки о typematic-задержке ~1с он не даёт нужной задержки для игры; +рекомендация A+B закрывает потребность без компромиссов. + +**Статус: A+B РЕАЛИЗОВАНЫ (2026-07-15, по согласованию с пользователем).** + +- A: `kbd_mod_state()` — `libc/conio/kbd_mod_state.c` + `` + (`KBD_MOD_*`). +- B: `` (`libc/kbd/`) + правка `libc/irq/_irq_tramp.c` + (новая ветка на бите 0 порта 0x19: raw активен → сама читает порт + 0x18, декодирует make/break, НЕ чейнится к DSS; raw выключен — + поведение как раньше, без изменений). Трамплин вырос со 150 до + 220 байт — `_IRQ_TRAMP_BUF_SIZE` поднят с 224 до 288 (было 4 байта + запаса, стало ≥60). `make -C libc` (fast+safe) — чисто. +- **Верификация в MAME** (`tests/kbdraw`, полный цикл open→держать→ + отпустить→ESC-выход→close): `KBD_LEFT` (0x16B, расширенный код + E0 6B) — down на нажатие, up на отпускание, ТОЧНО совпало с + константой из ``; `KBD_ESC` (0x76, обычный код) — + корректно закрыл raw-канал и вернул DSS (`IM` вернулся в 1). + Побочно найдено и задокументировано в `docs/libc-reference.md` + (``): MAME-мостовой `press_key` дёргает ОБЕ клавиатуры + (PC+ZX) одновременно и через ZX-путь давал паразitный незатухающий + бит — не относится к реальному сценарию (пользователь подтвердил: + матрица на Sprinter давно не используется), но означает, что + будущие MAME-тесты этой функции надо гонять через `:kbd:ms_naturl:*` + напрямую, не через удобный `press_key`. UP/DOWN/RIGHT/SPACE/SHIFT + константы — НЕ перепроверены поштучно (тот же общеизвестный + стандарт PS/2 Set 2, что и подтверждённые LEFT/ESC — проверить перед + использованием в PoC, если управление будет ощущаться неверно). +- На реальном железе — не проверено (только MAME). + +--- + +## 3. Формат данных — что напрямую переносим из docs/*RESOURCE_FORMAT.md + +- **Уровень** (`BLUETYPE`/`BLUESPEC`/`LINKLOC`/`LINKMAP`/`MAP`/`INFO`, + 2304 байта, 24 экрана × 30 тайлов) — читаем один раз при загрузке уровня + в свою C-структуру (прямой memcpy дампа файла, поля читаем по офсетам + из `APPLEII_RESOURCE_FORMAT.md` §1). DOS `levels.dat` даёт то же самое + +1 байт в конце записи — отбросить. +- **Графика фона/спрайтов** — кодек сжатия DOS `.DAT` не восстановлен и + восстанавливать не будем: используем уже распакованные PNG из + `SDLPoP/data/{KID,GUARD,VPALACE,VDUNGEON,...}` (см. + `MSDOS_RESOURCE_FORMAT.md` §5, §7 — тот же контейнерный формат/нумерация, + просто другой релиз сборки данных). Измерено локально: кадры Кида — + ~12×39 .. 30×42 px (P-режим, 4-бит палитра), фоновые тайлы подземелья — + 32 px по ширине (10 колонок × 32 = 320 — сходится с шириной экрана), высота + тайла 20/60/62 px (неоднородные ряды пола/потолка/арок) — укладывается в + лимит спрайтового движка (кадр ≤ 64×64) без всяких изменений движка. +- **Звук** — `digisnd*.dat` (PC-звук 8-бит ~11 кГц) — конвертация в сырой + PCM и проигрывание через `cbl_open`/`cbl_push_*`; `ibm_snd*.dat` (PC-спикер + тройки «частота×2Б + длительность») — тривиальный бипер, не требует CBL. + MIDI-семейство (`midisnd`, `mt32snd`, `prince.dat`) — вне скоупа (нет + синтеза MIDI на платформе; не блокирует геймплей). + +--- + +## 4. Стратегия фона — ПЕРЕСМОТРЕНО 2026-07-15: тайловый рендерер В РАНТАЙМЕ + +**Было** (первая версия плана): офлайн-склейка каждой комнаты в готовую +растровую картинку 320×~193, `gfx_blit` целиком при входе — обоснование +было «ноль нового кода в libbgi». Пересчёт по факту наличия структурных +данных комнаты (§3.4 формата, `level.h`) показал: 16 уровней × 24 комнаты × +~60-80 КБ/картинка — это **30+ МБ**, при том что одна и та же картинка +тайла (пол/стена/колонна) переиспользуется в десятках комнат — офлайн- +склейка печёт её заново в каждую копию. + +**Стало**: тайлы — переиспользуемый набор картинок ОДИН на визуальный +стиль (не на комнату), структурные данные комнаты — компактные (60 байт: +30×foretable+30×backtable, все 16 уровней ≈ 37 КБ, см. `level.h`). +`room_draw()` (applications/PoP/poc/room.c) проходит 30 тайлов комнаты и +зовёт `gfx_blit` для каждого, читая картинку из таблицы по типу тайла +(`tile_images[TILE_TYPE]`). Итог: десятки-сотни КБ переиспользуемых +тайл-картинок на весь визуальный стиль + ~37 КБ структуры уровней — +вместо 30+ МБ. + +**Почему это НЕ бьёт по бюджету кадра**: `room_draw()` зовётся ОДИН РАЗ +при входе в комнату (смена комнаты — не every-frame событие), не в +игровом цикле — это не `sprite_update`, тактовый бюджет кадра не +затронут. + +Анимированные тайлы (факел, шипы, дверь-плита) по-прежнему рисуются как +отдельные `sprite_t` поверх фона — движок это уже умеет (Y-order/layers, +dirty-биты, heal против фона через ОЗУ-копию); `room_draw()` кладёт в +ОЗУ-копию именно статичную геометрию (пол/стены/колонны Фазы 1 — §5.2), +поверх неё heal спрайтов работает как обычно. + +`toolchain/room_compose.py` (генерик-компоновщик тайлов в одну картинку, +§6.1) остаётся полезным ИНСТРУМЕНТОМ конвертации отдельных тайл-картинок +(PNG → getimage raw), просто теперь его выход — 32 маленьких файла +`tileNN.raw` (по одному на тип тайла), а не один большой файл на комнату; +сама раскладка/повторное использование по комнатам — в C-коде +(`room_draw`), не в офлайн-склейке. + +--- + +## 5. Proof-of-Concept — цель: доказать, что порт вообще ощущается как PoP + +> **Закрыт (исторический раздел).** PoC в `poc/` свою задачу выполнил и +> дальше не развивается: управление ощущается как PoP, held-state работает. +> Всё, что ниже про плейсхолдер-персонажа и приблизительную дугу прыжка, +> — уже неправда для активной ветки: в `SprPoP/` стоит настоящая графика +> Кида и авторские таблицы кадров (§6). Раздел оставлен ради истории +> решений (в частности §5.1 — почему сначала был плейсхолдер). + +**Объём**: одна комната (например Level 1, экран старта Кида), без +переходов между экранами, без стражников (стретч-цель, не обязательна). + +**Что показываем**: +1. Кид на экране, с закреплённым офлайн-конвертированным набором кадров + (подмножество: idle, walk L/R, jump-начало/дуга/приземление, стоп-на-краю, + возможно повисание на краю) — атлас в W0-странице, по образцу `rpgwalk`. +2. Управление: держать влево/вправо — идёт; отпустил — тормозит/стоит; + нажатие вверх во время бега — прыжок вперёд (дуга по авторским таблицам + смещений, не по gravity-физике «с нуля» — см. §6). Здесь же проверяется + решение по §2 (реальный held-state). +3. Столкновения: пол/край экрана/провал — по факту чтения тайла из + `BLUETYPE` под ногами (без LINKLOC-триггеров пока). +4. Стабильный кадр 50 Гц через уже готовый `gfx_wait_vsync`/дабл-буфер + (без FPS-делителя — Кид анимируется каждый видеокадр, как в оригинале). + +**Критерий успеха**: субъективно «прыжок ощущается как в PoP» (дистанция и +тайминг прыжка сверены с оригинальными таблицами, не подобраны на глаз — +см. §6), управление отзывчивое (не событийное с задержкой), сцена не мерцает +на стыке спрайт/фон. + +**Не входит в PoC**: стражники/бой, звук, HUD/таймер, переходы между +комнатами, ловушки/триггеры, титры/меню, сохранения. + +**Расположение**: `applications/PoP/poc/` (свой sprinter-cc проект + Python +конвертер ассетов, по структуре `examples/rpgwalk`). + +### 5.1 Статус (2026-07-15) — первая итерация: управление + коллизия края + +Сделано и проверено в MAME (`applications/PoP/poc/`, `make run`): +держать LEFT/RIGHT (`kbd_raw_down`, raw-канал из §2) — идёт непрерывно, +отпустил — стоит на месте (не событийно, реальный held-state); +столкновение с краями экрана (клип по `MINX`/`MAXX`); анимация +ходьбы/разворота лицом по направлению (`sprite_anim` пинг-понг); +дабл-буфер + `gfx_wait_vsync` — без видимого мерцания. Сборка — +`--memory huge` без `--bank` (§10, подтверждено рабочим). + +**Важное отступление от плана (осознанно, не молча):** персонаж — +ВРЕМЕННАЯ заглушка (лицензированный спрайт-пак +`third_party/16x16-RPG-characters` через `tools/gen_kid_placeholder.py`, +тот же источник, что уже использует `examples/rpgwalk`), а НЕ +конвертированная графика оригинальной Prince of Persia. Причина: +исходный набор кадров Кида (`SDLPoP/data/KID`) — копирайт +Broderbund/Ubisoft; автоматический конвейер, который систематически +извлекает и переупаковывает его в новый формат, — это на практике +внутрипроектное решение, которое стоит принимать пользователю явно +для каждого шага, а не проводить асинхронно агентом без лишнего +подтверждения. Сама графика — не то, что проверяет PoC (§5 явно: +цель — ощущение управления/коллизий, не визуальная точность). Замена +на настоящую графику Кида — отдельный шаг, на усмотрение пользователя. + +**Ещё не сделано** (следующие итерации §5): авторские таблицы +смещений кадров (§6 — движение при ходьбе линейное, px/кадр), +реальный уровень/фон по `BLUETYPE`/`LEVEL1` (сейчас — плейсхолдер: +плоский пол на весь экран, без ямы/выступа), `kbd_mod_state`/ +Shift-бег не подключены к игровому циклу (обёртка готова с Фазы A). + +**Прыжок/присед добавлены и ПРОВЕРЕНЫ (2026-07-15)**: состояние +`jumping`/`jump_t`/`crouching`, своя приблизительная дуга прыжка +(`jump_height[]`, 40 кадров) — не авторская таблица, см. §6.1. +HUD-текст статуса (нет отдельной позы). + +Живое тестирование пользователем нашло реальный баг: держа UP чуть +дольше 0.8 с (длительность дуги), получали ДВА прыжка подряд — код +проверял `kbd_raw_down(KBD_UP)` как уровень (держится, пока клавиша +физически зажата), а не как фронт нажатия, поэтому в момент +приземления «UP всё ещё зажат» тут же триггерил новый прыжок. +Исправлено edge-detect'ом (`up_prev` — предыдущее состояние UP, +триггер только на переход 0→1). Проверено брейкпоинтом в отладчике +MAME на адресе входа в код прыжка: за одно длинное удержание UP +брейкпоинт срабатывает РОВНО ОДИН РАЗ — фикс подтверждён на уровне +кода, не только «на глаз». + +Побочный урок (см. `docs/libc-reference.md` ``): моя +более ранняя попытка проверить UP/DOWN/RIGHT по скриншотам после +`press_key` ошибочно решила, что скрипт их не нажимает вообще — +на самом деле нажимает исправно, просто скриншот ловил случайный +момент дуги. Брейкпоинт/watchpoint на конкретный адрес кода — +надёжнее скриншота для таких проверок. + +--- + +## 6. Модель движения: авторские таблицы кадров, не физика с нуля + +Оригинальный движок PoP не считает прыжок как непрерывную физику +(gravity/velocity каждый тик) — движение персонажа задано таблицами кадров +анимации, где у части кадров зашито фиксированное смещение (dx, dy) для +ЭТОГО конкретного кадра последовательности (структура видна и в +исходниках Apple II — `SEQTABLE.S`/`MOVER.S`, и в SDLPoP `seg003.c`/`seq*` +таблицах). Практическое следствие для нашего движка: +- **Не использовать** `sprite_anim`/`sprite_moveto` для основного + персонажа как есть (они лианейно тянут по таймеру/тянут к линейной + цели) — вместо этого приложение само на каждый логический тик: + переключает кадр (`sprite_frame`, атлас как лента поз, не «прогрессия + первый..последний» автоматом) и одновременно применяет dx,dy ЭТОГО + кадра к позиции (`sprite_move`). +- Готовая автоматика движка (`sprite_anim`/`sprite_moveto`/tween, + Y-сортировка) остаётся полезной для декоративных/фоновых элементов + (факелы, патрулирующий стражник вне боя — почти один в один паттерн + `rpgwalk`). +- Источник таблиц смещений: переснять из `Prince-of-Persia-Apple-II/01 POP + Source/Source/{MOVER.S,SEQTABLE.S,FRAMEADV.S}` и/или + `SDLPoP/src/seq*.c` — задача Фазы 1 полной реализации (§7), не PoC + (для PoC можно взять урезанный набор смещений вручную по количеству + пикселей на кадр, посчитанному по видео/скриншотам оригинала, и уточнить + позже). + +### 6.1 Инструмент конвертации кадров разного размера (`toolchain/png_strip.py`) + +Кадры персонажа в оригинале — РАЗНОГО размера каждый (bbox зависит от +позы; `sprite_t` нашего движка (`libbgi/include/sprite.h`) хранит ОДИН +фиксированный w/h на весь спрайт и рисует от угла, без per-frame +смещения — в отличие от оригинала, где на каждый кадр было своё XCO/YCO +(`APPLEII_RESOURCE_FORMAT.md` §2.2). `toolchain/png_strip.py` (генерик, +не завязан на PoP — принимает произвольный список PNG) закрывает это +ПАДДИНГОМ: канвас = макс. w/h среди кадров ленты, якорь по умолчанию +bottom-center («ноги на месте»), остальное — прозрачность. + +**Компромисс, не полноценное решение**: один сильно выбивающийся по +размеру кадр в ленте раздувает канвас (и память) ВСЕХ кадров этой же +ленты. Смягчается группировкой по похожим размерам в отдельные атласы +(не одна лента на все позы актора — так уже сделано для ходьбы отдельно +от прыжка). + +**Полноценное решение (кандидат в будущее расширение библиотеки, НЕ +делать без предложения и подтверждения пользователя)**: per-frame +смещение в `sprite_t` (аналог XCO/YCO оригинала) — тогда паддинг +не нужен вообще, экономия памяти по полной. Делать только если память +станет РЕАЛЬНОЙ проблемой (не гипотетической) — тогда предложить как +отдельную правку `sprite.h`/движка. Подробности компромисса — +memory/png_strip_padding_tradeoff. + +--- + +## 7. Полноценное приложение — фазы (после PoC) + +Порядок — по риску и зависимостям, не по геймплейной важности. +**Отметки статуса — на 2026-08-01.** + +**Фаза 0 — инфраструктура порта** — **СДЕЛАНА**, но иначе, чем задумано: +- Хелд-стейт клавиатуры по §2 — сделан. +- Конвертер уровней не понадобился: `res200N.bin` из `SDLPoP/data/LEVELS` + кладётся на образ как есть и читается по офсетам в рантайме + (`src/pop_level.c`), уровень живёт в EMM-странице. +- Конвертер фона в растры **отменён осознанно** (§4): фон собирается + тайлами в рантайме. Спрайты — `toolchain/pop_pack_bg.py` / + `pop_pack_kid.py` / `pop_pack_guard.py` → атласы `.atl` (Kid — 28 + страниц, риск §8 п.3 закрыт). + +**Фаза 1 — Кид, полный набор действий** — **СДЕЛАНА**: стоять/идти/бежать/ +тормозить/разворот/прыжки/повисание/подтягивание/спуск/приседание/ +осторожный шаг/питьё зелья/смерть от провала и от пик; переходы между +комнатами во все четыре стороны. Осталось: **старт по данным уровня** +(`pop_level_start_*` реализованы, но не подключены) — задача L1-START в +`../TASKS_OPEN.md`. + +**Фаза 2 — мир и ловушки** — **СДЕЛАНА**: кнопки/ворота через +`LINKLOC`/`LINKMAP`, шипы, loose-полы (тряска, обрушение, щебень, пробой +потолка), зелья, дверь уровня (открывается), факелы. Подробности и +справочник — `gates_spikes_plan.md`. + +**Фаза 3 — бой** — **СДЕЛАНА в объёме обычного стражника**: подбор и +выхватывание меча, стойка, удар/парирование, коллизия клинков, HP обеих +сторон, смерть; ИИ стража (замечает Кида, подходит, боевые ветки), +персистентность трупа между комнатами. + +**Фаза 4 — разнообразие противников** — **НЕ НАЧАТА**. Скелет нужен на +уровне 3, толстый — на 6, тень — на 12, визирь — на 13; привязка +«уровень → тип стража» (`tbl_guard_type`) описана в `levels_plan.md` §1. + +**Фаза 5 — звук** — **НЕ НАЧАТА**: CBL-эффекты (шаги, удары, двери, +падение) из `digisnd*.dat`→PCM; PC-спикер тройки (`ibm_snd*.dat`) как +опциональный дешёвый бипер без CBL, если формат подтвердится простым +парсингом. Опкод `SOUND` в `play_seq` пока просто съедает свой аргумент — +точки вызова уже на месте. + +**Фаза 6 — оболочка** — **НЕ НАЧАТА**: титры, меню/выбор уровня, HUD +(таймер/жизни), сохранение прогресса (FILE*), финальные катсцены — по +минимуму, геймплейно не критично. Полоса HP — единственное, что уже есть. + +**Между Фазами 4 и 5 вклинивается то, чего в этом плане не было: +переход между УРОВНЯМИ** (загрузка следующего уровня, второй тайлсет +palace, потабличные различия уровней). Отдельный документ — +`levels_plan.md`. + +**Фаза 7 — стабилизация**: полный прогон всех 14 уровней в MAME +(`mame_interactive.py`), затем на реальном железе; профилирование бюджета +кадра по методике `sprite_engine_perf`/`sprite-api-design.md` §9д на самых +насыщенных экранах (несколько стражников + ловушки одновременно — +проверить лимит ~21 спрайт/кадр и Y-sort лимит 32); при необходимости — +банкинг (`--memory big/huge`) для кода/уровня, если размер вылезет за +tiny/small. + +--- + +## 8. Риски, требующие спайка/артефакта до架构 решений + +(по правилу `defer_unexplained_quirks` — не гадать, проверять) + +1. ~~**Held-state клавиатуры** (§2)~~ — **закрыт** (``). Открытый + остаток — не «есть ли held-state», а потеря байт при аккордах + Shift+стрелка: `../PoP/SprPoP/TASKS_CLOSED.md`, KBD-1. +2. **Бюджет кадра** — риск подтвердился, но не в том виде, в каком ожидался: + спрайтовый движок для персонажей не используется, поэтому лимит + «~21 спрайт/кадр» неприменим. Реальный бюджет упирается в heal+блиты и + перерисовку тайлов; замер 2026-07-30 — типичный кадр ~371 К тактов + (~86 % периода). Инструмент замера уже в коде: полосы бордюра `PROF()` + в `sprpop.c`. План выжимания — `../PoP/SprPoP/TASKS_CLOSED.md` (CLIP-1) и + `../BUGS_OPEN.md` (T-1/T-2). +3. ~~**Ёмкость атласа на актора**~~ — **закрыт**: Kid разложен на 28 + атласов-страниц по 8 спрайтов (`pop_pack_kid.py`), страж — на 5; + мульти-страничного формата `.atl` не потребовалось. Побочно + подтвердился компромисс паддинга (§6.1). +4. **Тайминг оригинала** — **ОТКРЫТ, и сверка 2026-08-01 показывает + расхождение.** Цифры оригинала (SDLPoP): базовый таймер `BASE_FPS = 60` + (`types.h:1373`), логический кадр игры — `base_speed = 5` тиков + (`data.h:869`), то есть **83.3 мс (12 лог. кадров/с)**; в бою + `fight_speed = 6` → **100 мс (10/с)**. У нас (`sprpop.c`) — три + ожидания `gfx_wait_vsync()` на итерацию, то есть **60 мс (16.7/с)** и + без отдельной скорости боя. Значит **игра идёт примерно на 39 % + быстрее эталона**. Точное соответствие даёт 4 ожидания vsync (80 мс + против 83.3) и 5 в бою (100 мс — совпадает точно). + Проверять не «на глаз», а секундомером по одинаковому отрезку + (SDLPoP рядом на том же экране), и только после того, как кадр + перестанет иногда вылезать за период (см. п.2) — иначе замедление + спрячет проблему бюджета вместо того, чтобы её показать. + +--- + +## 10. Режим памяти сборки + +Пользователь предложил `huge` (горячий код в W1, данные в W2, редко +вызываемая логика — банками в W3) как целевой режим. Согласен, с уточнением +по срокам принятия решения. + +**`huge` — правильная цель для ПОЛНОГО приложения**, но не то, с чего надо +стартовать: + +- Layout `huge` (см. `memory_modes_implemented`): CODE_LOC=0x4100 (W1), + DATA_LOC=0x8000 (W2), банки — W3 (порт 0xE2), `crt0_banked` + + автодетект W2 (как `small`). Состояние приложения (структуры Кида, + уровня, массив `sprite_t`) остаётся в обычном W2-heap ДАЖЕ если код, + который его трогает, забанкован — `malloc` из банка возвращает + W2-указатель (`bank_local_data_pattern`), так что данные не привязаны к + конкретному банку. +- Оверхед `__banked`-вызова (trampoline: +3 байта на стеке между ret и + аргументами, виртуальный 24-битный адрес, см. `sdcc_banking`) — фиксированная + небольшая цена ЗА ВЫЗОВ, не за такт. Это не страшно для функций, которые + вызываются РЕДКО за кадр (AI одного стражника, диалог, переход между + комнатами) — страшно было бы забанковать что-то, что дёргается ВНУТРИ + горячего цикла отрисовки (там уже и так основной бюджет уходит на + `sprite_update`/блиты — см. `sprite_engine_perf`, ~19.5К тактов/спрайт). + Правило простое: **не банковать код на пути "раз в кадр на объект", + банковать код на пути "раз в кадр на комнату/раз в переход/раз в + редкое событие"**: логика ИИ стражника целиком, диалоги/катсцены, меню/ + титры/выбор уровня, парсинг уровня при входе в комнату, сериализация + сохранений — хорошие кандидаты в банки; тик Кида, чтение столкновений, + вызов `sprite_update`/`gfx_wait_vsync`, обработка ввода — должны остаться + небанкованными (W1/W2). +- Гранулярность банкования — целый файл (`--bank N=FILE.c`), это уже + системный паттерн проекта (тот же принцип, что и «1 файл = 1 юнит DCE» в + libc) — значит выгодно с САМОГО начала Фазы 1 (не задним числом) резать + исходники приложения по границе «горячее/холодное» файл-в-файл: например + `kid_tick.c`/`collision.c`/`room.c`/`input.c` — неизменно вне банков; + `guard_ai_*.c`/`dialogue.c`/`menu.c`/`levelload.c`/`combat.c` — кандидаты + под `--bank`. Тогда переход на `huge` позже — это правка Makefile/ + sprinter-cc-вызова (`--memory huge --bank N=file.c ...`), а не рефакторинг + логики. + +**Уточнение (проверено в `bin/sprinter-cc`, строки ~342-350): можно сразу +собирать PoC на `--memory huge` без единого `--bank`.** Скрипт сам +подставляет стаб `const unsigned char n_banks = 0;`, когда `--bank` не +передан ни один раз — `crt0_banked` линкуется и корректно пропускает цикл +загрузки банков при старте. Layout при этом byte-в-byte совпадает с тем, +что делает `crt0_small` для режима `small` (CODE 0x4100/W1, DATA 0x8000/W2, +автодетект W2) — разница только в том, что попутно линкуется сам +`bank.s` (таблица `_bank_pages` + trampoline-инфраструктура), это +незначительный довесок к размеру, не к рантайм-цене. Значит **PoC можно +сразу собирать вызовом `sprinter-cc --memory huge` без `--bank`-флагов** — +и когда в полном приложении появятся первые «холодные» файлы, переход на +банкование — это просто добавление `--bank N=file.c`, без смены +`--memory`/адресов/crt0. Сборочная конфигурация не потребует миграции +между PoC и полным приложением. + +Единственное, что стоит сделать уже в Фазе 1 полного приложения (не в +PoC) — планировать структуру исходников с расчётом на будущий файл-в-файл +сплит под банки (см. выше), раз гранулярность банкования — целый файл. + +--- + +## 9. Что нужно от пользователя, прежде чем двигаться дальше + +- Подтверждение направления по §2 (какой из трёх вариантов held-state + клавиатуры пробовать первым, или сначала спайк-эксперимент в MAME). +- Подтверждение объёма PoC (§5) — устраивает ли «одна комната без + стражников», или сразу закладывать хотя бы одного патрулирующего + стражника (это не архитектурно сложнее — Y-order и tween уже есть, + просто больше конвертации ассетов). diff --git a/applications/SprPoP/docs/PoP/APPLEII_RESOURCE_FORMAT.md b/applications/SprPoP/docs/PoP/APPLEII_RESOURCE_FORMAT.md new file mode 100644 index 0000000..a08fed1 --- /dev/null +++ b/applications/SprPoP/docs/PoP/APPLEII_RESOURCE_FORMAT.md @@ -0,0 +1,283 @@ +# Формат ресурсов Prince of Persia (Apple II, оригинальные исходники 1989) + +Источник — официально опубликованные Джорданом Мехнером исходники +(`Prince-of-Persia-Apple-II/`, 6502-ассемблер). В отличие от DOS-версии, здесь +формат восстановлен **напрямую по коду**, а не по догадкам о байтах — +уверенность высокая везде, где указана ссылка на конкретный файл/строки. + +--- + +## 1. Формат уровня (`01 POP Source/Levels/LEVEL0`…`LEVEL14`, 2304 байта) + +Файлы уровня — это побайтовый дамп структуры `blueprnt`, которая грузится по +фиксированному адресу `$b700` (`EQ.S:28`) и объявлена как `dum blueprnt` в +`EQ.S:258-266`. Никакого отдельного заголовка файла нет — это чистый образ +структуры в памяти: + +| Поле | Размер | Смещение в файле | Описание | +|---|---|---|---| +| `BLUETYPE` | 720 Б | 0–719 | 24 экрана × 30 тайлов: тип объекта/тайла | +| `BLUESPEC` | 720 Б | 720–1439 | 24 экрана × 30 тайлов: доп. байт состояния объекта | +| `LINKLOC` | 256 Б | 1440–1695 | Таблица связей нажимных плит/дверей, часть 1 | +| `LINKMAP` | 256 Б | 1696–1951 | Таблица связей, часть 2 | +| `MAP` | 96 Б | 1952–2047 | 24 экрана × 4 байта: граф соседних экранов | +| `INFO` | 256 Б | 2048–2303 | Метаданные уровня: старт Кида, стражников и т.д. | + +Сумма: 720+720+256+256+96+256 = **2304** — точно совпадает с размером файла, +что подтверждает: это чистый дамп структуры, без обёртки. + +### 1.1 Сетка тайлов (`BLUETYPE` / `BLUESPEC`) + +Каждый экран — ровно **30 тайлов** (10 столбцов × 3 ряда): подтверждено +таблицами `BlockTable`/`BlockEdge` (`TABLES.S:74-154`) и логикой перехода +между экранами в `CTRLSUBS.S:218-234` (при переходе через край экрана +`tempblockx` меняется на ±10, `tempblocky` — на ±3). + +Функция `CALCBLUE` (`GRAFIX.S:1757-1784`) вычисляет для экрана 1–24: +`BlueType = blueprnt + (screen-1)*30`, `BlueSpec = BlueType + 24*30`, +используя таблицу `Mult30` (`TABLES.S:131-140`). + +Байт `BLUETYPE` упакован битовыми полями (`EQ.S:484-486`): + +``` +бит 7-6: secmask (%11000000) — назначение не установлено по доступному коду + (возможно, служебное поле редактора) +бит 5: reqmask (%00100000) — флаг "необходимая опорная плитка" + (проверяется в BREAKLOOSE, MOVER.S:395-397) +бит 4-0: idmask (%00011111) — тип тайла/объекта, 0-29 +``` + +Перечень 30 типов объектов (`MOVEDATA.S:8-37`): + +``` +0 space 8 pillarbottom 16 exit 24 window2 +1 floor 9 pillartop 17 exit2 25 archbot +2 spikes 10 flask 18 slicer 26 archtop1 +3 posts 11 loose 19 torch 27 archtop2 +4 gate 12 panelwof 20 block 28 archtop3 +5 dpressplate 13 mirror 21 bones 29 archtop4 +6 pressplate 14 rubble 22 sword +7 panelwif 15 upressplate 23 window +``` + +Проверено вручную на дампе начала `LEVEL1` (`00 00 00 21 01 21 21 21 34 34 +33 33 21 23 00 34 14 14 14 34 14 34 34 2e 23 0b 01 21 34`) — например, +`0x33 → id=0x13=19 (torch)`+reqmask, `0x34 → id=20 (block)`+reqmask — +декодирование по таблице сходится чисто. + +`BLUESPEC` — доп. байт, чья семантика зависит от типа тайла (единой схемы +нет, разбирается объект-специфичным кодом): + +- **gate** (дверь, `FRAMEADV.S:2222-2234`): на диске — маленький enum (1 = + начинает открытой сверху, 2 = снизу, …), который через `initsettings` + (`FRAMEADV.S:22-23`, диапазон `gminval=0`..`gmaxval=188`, из + `MOVEDATA.S:56-57`) при инициализации уровня превращается в живой счётчик + "высоты двери" 0–188. +- **loose** (шаткая плитка, `FRAMEADV.S:2224-2237`): при инициализации всегда + принудительно обнуляется, независимо от значения на диске. +- **flask** (зелье, `FRAMEADV.S:2226,2239-2246`): значение×32 выбирает + цвет/тип зелья. +- **spikes** (шипы, `MOVER.S:365-382`, константы `spikeExt=5, spikeRet=9` в + `MOVEDATA.S:45-46`): 0 = безопасно/убраны, 1–8 — кадр анимации + выдвижения/втягивания, `$FF` = навсегда заклинило (тело наколото). +- **pressplate/upressplate** (нажимные плиты, `MOVER.S:425-464`, + `FRAMEADV.S:2059-2098`): значение — это **индекс в цепочке связей** + `LINKLOC`/`LINKMAP` (см. ниже); младшие 5 бит `LINKMAP` по этому индексу + одновременно служат счётчиком таймера плиты (0–31), определяющим + состояние "поднято/опущено". + +### 1.2 `LINKLOC` / `LINKMAP` — граф триггеров (нажимные плиты → двери и т.п.) + +Два параллельных массива по 256 байт кодируют цепочки "нажатие плиты X → +сработать объект на экране S, блок B". Восстановлено из `MOVER.S:506-537` +(цикл `trigger`) и `MOVER.S:1549-1581` (`gettimer/chgtimer/getloc/ +getlastflag/getscrn`): + +``` +LINKLOC[i]: бит 7 = флаг "последнее звено цепочки" + биты 6-5 = младшие 2 бита номера целевого экрана + биты 4-0 = номер целевого блока (0-29); $FF = "никуда не привязано" + +LINKMAP[i]: биты 7-5 = старшие 3 бита номера целевого экрана + (вместе с LINKLOC биты 6-5 → полный номер экрана 0-31) + биты 4-0 = таймер обратного отсчёта плиты (0-31, значим только + по индексу самой плиты) +``` + +`BLUESPEC` плиты хранит индекс `i` её *первого* звена; `getlastflag` идёт +вперёд (`inc linkindex`), пока не встретит бит 7 в `LINKLOC`. Сверено на +`LEVEL1`: байт по смещению 1440 (`0x89 = 10001001` → флаг конца цепочки, +целевой блок 9) и параллельно байт по смещению 1696 (`0x60 = 01100000` → +старшие биты номера экрана) — согласуется с этой раскладкой. Заполнены +реально используемые уровнем звенья, остальное — "мусорные" повторяющиеся +байты-заполнители. + +### 1.3 `MAP` — граф соседних экранов + +24 записи × 4 байта = 96 байт: для каждого экрана (1–24) +`MAP[(scrn-1)*4 + 0..3] = левый, правый, верхний, нижний соседние экраны`, +читается через `GETLEFT/GETRIGHT/GETUP/GETDOWN` (`CTRLSUBS.S:244-274`, +индексация `MAP-4..MAP-1,x` при `x = scrn*4`). Экран `0` зарезервирован как +"нет экрана" (проверка `beq ]rts` в этих же процедурах). + +### 1.4 `INFO` — метаданные уровня (256 байт, база = смещение файла 2048) + +Объявлено как `dum INFO` в `EQ.S:272-288`: + +| Смещение (от начала INFO) | Поле | Размер | +|---|---|---| +| 0 | "число экранов + 1" (используется в `SETINITIALS`, `SUBS.S:1441-1445`) | 1 | +| 1–63 | резерв/не используется | 63 | +| 64 | `KidStartScrn` | 1 | +| 65 | `KidStartBlock` | 1 | +| 66 | `KidStartFace` (направление; при загрузке инвертируется XOR `$ff`, `SUBS.S:1516-1518`) | 1 | +| 67 | заполнитель | 1 | +| 68 | `SwStartScrn` (стартовый экран меча) | 1 | +| 69 | `SwStartBlock` | 1 | +| 70 | заполнитель | 1 | +| 71–94 | `GdStartBlock[1..24]` — стартовый блок стражника на экране; **≥30 = "стражника нет"** (`AUTO.S:1832-1834`, `SUBS.S:1677-1679`) | 24 | +| 95–118 | `GdStartFace[1..24]` (86 = "стражника нет", см. `ShadFace cmp #86` по всему `AUTO.S`) | 24 | +| 119–142 | `GdStartX[1..24]` — пересчитывается заново из блока при старте уровня, значение на диске почти не используется (`SUBS.S:1674-1690`) | 24 | +| 143–166 | `GdStartSeqL[1..24]` | 24 | +| 167–190 | `GdStartProg[1..24]` — "программа"/поведение ИИ стражника | 24 | +| 191–214 | `GdStartSeqH[1..24]` — обнуляется при старте (`SUBS.S:1685-1686`) | 24 | +| 215–255 | резерв/не используется | 41 | + +Проверено на `LEVEL1`: байт по смещению файла 0x800 = `0x18`=24 (число +активных экранов = 23+1); по смещению 0x840 — `01 00 ff 00 00 00 ff 1e 1e +11 1e 1e ...` → `KidStartScrn=1, KidStartBlock=0, KidStartFace=$FF, +SwStartScrn=0, SwStartBlock=0`, далее 24 байта `GdStartBlock`, в основном +`0x1e`(30, "нет стражника"), с реальной расстановкой только на экране 3 +(`0x11`=17) и экране 23 (`0x06`) — согласуется с уровнем, где всего два +стражника. + +### 1.5 Как уровень попадает с диска (важно: имя файла — не игровой механизм) + +В рантайме нет чтения "по имени файла LEVELn" — это чисто утилита для +экспорта в этом репозитории. Реально `LOADLEVELX` (`MISC.S:795-809`) +использует фиксированные таблицы по номеру уровня `bluepTRKlst`/ +`bluepREGlst` (`MISC.S:776-787`), дающие физическую **дорожку (1-33)** и +**регион (0/1)**, затем `rdbluep` (`MASTER.S:598-616`) вызывает +низкоуровневое чтение `rw18` (`RdGrpErr`) 9 физических групп по 256 байт +(`$b7-$bf`) — 9×256=2304 байта — прямо в буфер blueprint; регион 0/1 выбирает +половину 18-секторной дорожки (два уровня делят одну дорожку). Файлы +`LEVELn` в этом репозитории — реконструкция этого сырого блока для удобства +работы с инструментами. + +--- + +## 2. Формат изображений/спрайтов (`IMG.CHTAB1-7`, `IMG.BGTAB1/2.DUN/.PAL`) + +Каждый такой файл грузится целиком по **фиксированному адресу**, заданному +константами `chtableN`/`bgtableN` (`GAMEEQ.S:9-18`): + +``` +chtable1=$6000 chtable2=$8400 chtable3=$0800 chtable4=$9600 +chtable5=$a800 chtable6=$6000 chtable7=$9f00 +bgtable1=$6000 bgtable2=$8400 +``` +— то есть смещения внутри файла один-в-один совпадают с адресами в памяти +после загрузки. + +### 2.1 Раскладка контейнера + +Восстановлено из заголовка-комментария "Image table format" в `HIRES.S:181-186`, +процедуры разрешения указателя `setimage` (`HIRES.S:263-277`) и +`GETWIDTH`/`PREPREP` (`HIRES.S:283-339`): + +``` +Смещение 0 : 1 байт — число изображений в таблице (максимум 127, + в образцах встречается 0x7f) +Смещение 1..254 : 127 × 2-байтных little-endian указателей + (указатель на изображение N — по смещению 1+(N-1)*2, + N=1..127) — АБСОЛЮТНЫЕ адреса в адресном пространстве + фиксированной загрузки этой таблицы, указывающие на + запись данных этого изображения +Смещение 255 : заполнитель (таблица указателей занимает ровно 256 байт) +Смещение 256 (база+0x100) и далее: + последовательно идущие записи данных изображений: + байт 0: ширина (в байтах на строку) + байт 1: высота (число строк) + байты 2..(2+ширина*высота-1): сырые байты пикселей, + слева направо, сверху вниз, БЕЗ сжатия +``` + +Проверено вручную на `IMG.BGTAB1.DUN`: с точной арифметикой индексов из +`setimage` (`Y = image*2 - 1`, `HIRES.S:264-267`) первые ~30 записей дают +строго возрастающую последовательность указателей `0x6101, 0x6133, 0x6159, +0x618b, 0x61c9, 0x61fb, 0x6221, 0x6313, 0x63c9, ...` — указатель +изображения #1 приходится ровно на `bgtable1 ($6000) + 0x100`, то есть точно +на конец 256-байтной таблицы указателей. Это независимо подтверждает и +размер таблицы, и семантику указателей. + +**Важно: сжатия в этом формате нет.** RLE/дельта-упаковка (`SngExpand`/ +`DblExpand`/`DeltaExpPop`/`DeltaExpWipe` в `01 POP Source/Source/UNPACK.S`) +применяется только к полноэкранным изображениям (титры/пролог/катсцены), но +не к CHTAB/BGTAB — спрайты и фоновые тайлы хранятся как чистые упакованные +байты hi-res/double-hi-res экрана Apple II, без какого-либо RLE или дельты. + +### 2.2 Параметры отрисовки (не часть файла ресурса) + +При выводе спрайта (`LAY`/`FASTLAY`/`PEEL` и т.д., `HIRES.S:658-1740`) +используются zero-page параметры `PAGE/XCO/YCO/OFFSET/IMAGE/OPACITY/TABLE/ +BANK` (описаны в `HIRES.S:155-178`): `OFFSET` (0–6) — горизontальный сдвиг на +под-байтовый пиксель, `OPACITY` выбирает режим совмещения (AND/OR/STA/XOR/ +маска-OR) плюс отдельный бит горизонтального зеркалирования (бит 7). Это +чисто рантайм-параметры отрисовки, не хранящиеся в файле ресурса. Точный +механизм барабанного сдвига для `OFFSET` (таблицы `HRTABLES.S`/`YLO`/`YHI`) +не прослежен до конца — при необходимости требует отдельного анализа. + +### 2.3 Инструмент DRAZ (авторская утилита создания спрайтов) + +В `04 Support/DRAZ` нет исходников самой утилиты DRAZ — только файлы данных +(`PAC.*` — позы персонажей, и уже скомпилированные `IMG.*`), поэтому +внутренний пайплайн DRAZ (как позы превращаются в CHTAB) напрямую не виден. +Формат контейнера выше выведен полностью из кода движка-потребителя, что +является надёжным, но косвенным источником. + +Отдельно: в игровой логике списков объектов (`ADDBACK`, `GRAFIX.S:191-214`) +встречается **рантайм-упаковка ссылки на фоновую картинку** в один байт: бит +7 выбирает `bgtable1` или `bgtable2`, биты 0-6 — номер картинки в таблице +(0-63). Это соглашение для внутриигровых списков объектов (`bgIMG` и т.п.), а +не свойство самих файлов CHTAB/BGTAB на диске. + +--- + +## 3. "Главного индекса ресурсов" не существует + +В отличие от DOS-версии (см. `docs/MSDOS_RESOURCE_FORMAT.md`), в рантайм-коде +Apple II **нет обобщённого справочника "имя ресурса → расположение на +диске"**. Расположение каждого ресурса зашито напрямую как таблицы +дорожка/группа-секторов прямо в коде загрузчика: + +- Уровни: `bluepTRKlst`/`bluepREGlst` (`MISC.S:776-787`), используются + `LOADLEVELX`/`LOADLEVEL` (`MISC.S:795-809`, `MASTER.S:467-481`). +- Альтернативные наборы фонов/персонажей: `bg1trk`/`bg2trk`/`ch4trk`/`ch4off` + (`MASTER.S:522-528`). +- Массовая загрузка при старте (chtable1-7, bgtable1-2, seqtable и т.д.): + прямые вызовы `rw18`/`RdGrp`/`RdSeq` с литеральными hex-списками + групп-секторов в `MASTER.S:1250-1360` и `BOOT.S:100-118`. + +Весь дисковый ввод-вывод идёт через нестандартный низкоуровневый драйвер +`rw18` (`rw18 = $d000`, `EQ.S:11-12`; папка `02 POP Disk Routines/RW1835`), +реализующий нестандартный формат **18 секторов/дорожку** (вместо 16 у +стандартного DOS 3.3) — этим объясняется, почему регионы уровня (9×256Б) +идут парами на одной физической дорожке. Символические имена +`chtableN`/`bgtableN` в `GAMEEQ.S` — ближайший аналог "индекса ресурсов", но +они связывают ресурс с **фиксированным адресом в ОЗУ**, а не с положением на +диске; связь с диском — отдельная, вручную сопровождаемая таблица, +сопоставленная с ресурсом лишь порядком вызовов загрузчика. + +--- + +## 4. Что ещё не восстановлено (открытые вопросы) + +- Точное назначение бит `secmask` (%11000000) в `BLUETYPE` — не встречено + использование в доступном игровом коде (возможно, поле только для + редактора уровней, не читается движком). +- Механизм барабанного сдвига `OFFSET` для суб-байтового позиционирования + спрайта по X (`HRTABLES.S`) — не прослежен в деталях. +- Внутренний формат авторских файлов `PAC.*` инструмента DRAZ (как позы + скелетной анимации превращаются в растровые кадры CHTAB) — исходники DRAZ + отсутствуют в репозитории, можно только косвенно восстановить по + результату (уже скомпилированным `IMG.*`). diff --git a/applications/SprPoP/docs/PoP/MSDOS_RESOURCE_FORMAT.md b/applications/SprPoP/docs/PoP/MSDOS_RESOURCE_FORMAT.md new file mode 100644 index 0000000..580e92b --- /dev/null +++ b/applications/SprPoP/docs/PoP/MSDOS_RESOURCE_FORMAT.md @@ -0,0 +1,301 @@ +# Формат ресурсов Prince of Persia (MS-DOS, каталог `MSDOS/`) + +Документ описывает бинарный формат `*.DAT`-файлов ресурсов DOS-версии PoP. +Исходников для этой версии нет, поэтому всё, что ниже — результат +структурного (эмпирического) анализа реальных файлов из `MSDOS/`, а не чтения +кода. Уровень уверенности указан для каждого раздела. Все находки проверены +скриптами (Python), которые разбирают файл и валидируют согласованность +(например: смещение+размер последней записи таблицы точно совпадает с +началом самой таблицы — то есть данные и каталог стыкуются без дыр). + +**Основной источник спецификации формата — `POP-DAT-FormatSpecifications.pdf`** +(и его текстовая конверсия `POP-DAT-FormatSpecifications.txt` в этой же папке, +для grep/цитирования): *«Prince of Persia — Specifications of File Formats»*, +Princed Development Team, 2008 — каноническая спецификация формата `DAT v1.0`, +на которой построен и SDLPoP, и Princed Resources. Разбирает контейнер, индекс, +чек-сумму, кодеки изображений (RLE / LZG), палитры, формат уровней (room +mapping, wall-drawing, room-linking, guards, start position, door events), +звук (digital waves / MIDI / PC speaker), бинарные файлы и Mac-варианты. При +любом расхождении между эмпирическими находками ниже и этим документом — +источником истины считать спецификацию (сверять §-номера: её §3.x). + +Дополнительно как справка при реализации (порт на ZX Sprinter): + +- **SDLPoP** (github.com/NagyD/SDLPoP, GPLv3) — open-source реализация + DOS-версии на основе дизассемблирования оригинального `PRINCE.EXE`. Содержит + рабочий код чтения `.DAT`-файлов и полный кодек изображений/уровней. Точные + структуры (`dat_table_type` и т.п.), процитированные ниже, получены через + автоматический пересказ содержимого файла третьей стороной, а не через + прямое чтение исходника — поэтому такие детали помечены как "требует сверки + при реализации", в отличие от эмпирически подтверждённых байтовых оффсетов. +- **Princed Resources / PR** (github.com/NagyD/PR, princed.org, GPLv2) — это + профильный инструмент именно для распаковки/запаковки `.DAT`-ресурсов PoP + (версии DAT 1 и 2), сделанный тем же автором. В его документации + (`doc/Dataformats.md`) официально описаны экспортные форматы ресурсов — + это подтверждает и уточняет часть находок ниже (см. §3–4), и является более + надёжным источником, чем самостоятельная догадка по байтам. + +**Важная находка:** репозиторий SDLPoP в папке `data/` содержит не только +код движка, но и **реальные ресурсы игры** — как сырые `.DAT`-контейнеры, так +и уже распакованные поштучно файлы (PNG-кадры спрайтов, `.pal`-палитры, +`.bin`-дампы уровней), см. §7. Это готовый источник ассетов и одновременно +независимая проверка формата, описанного в этом документе. + +--- + +## 1. Общий контейнер `.DAT` (уверенность: высокая, подтверждено на 28 файлах) + +Каждый `*.DAT`-файл (кроме служебных `config.dat`/`setup.dat`, см. §5) — это +простой архив-контейнер: блок данных + оглавление (каталог ресурсов) в конце +файла. + +### 1.1 Заголовок файла (6 байт, смещение 0x00) + +| Смещение | Размер | Поле | Значение | +|----------|--------|--------------|----------| +| 0x00 | 4 | `tableOffset`| LE u32. Абсолютное смещение в файле, с которого начинается таблица оглавления. Совпадает с "концом данных". | +| 0x04 | 2 | `tableSize` | LE u16. Размер таблицы оглавления в байтах. | + +Инвариант, подтверждённый на всех 28 `.dat`-файлах в каталоге: + +``` +tableOffset + tableSize == размер файла (без исключений) +``` + +Данные ресурсов идут сразу после заголовка, начиная с байта 0x06, и +заканчиваются на `tableOffset`. + +### 1.2 Таблица оглавления (по смещению `tableOffset`, длиной `tableSize`) + +Таблица — плоский массив записей по 8 байт. Количество записей: +`tableSize / 8` (округление вниз; в файле почти всегда остаётся 2 "лишних" +байта в хвосте таблицы — назначение не установлено, вероятно, служебное поле +инструмента-упаковщика или паддинг; на итоговый разбор не влияет). + +Запись (8 байт): + +| Смещение в записи | Размер | Поле | Описание | +|---|---|---|---| +| 0 | 2 | `size` | LE u16 — размер данных ресурса в байтах | +| 2 | 2 | `id` | LE u16 — идентификатор ресурса | +| 4 | 2 | `offset` | LE u16 — **абсолютное** смещение данных ресурса в файле (не относительное!) | +| 6 | 2 | `reserved` | во всех проверенных записях (сотни штук) всегда `0x0000` | + +Проверено на `levels.dat`: 16 записей, `id`=2000..2015, и `offset[i] + size[i] +== offset[i+1]` для всех соседних записей, а последняя запись заканчивается +ровно на `tableOffset` — то есть данные абсолютно плотно упакованы, без +пробелов, для этого файла. В других файлах (например `guard.dat`) между +записями изредка есть небольшие зазоры в несколько байт (вероятно, выравнивание +или "мёртвые" байты от инструмента-компоновщика) — не является нарушением +формата. + +### 1.3 Диапазоны `id` по типам файлов (собрано эмпирически) + +Похоже, что числовые ID образуют условные "пространства имён" по типу +контента — вероятно, глобальные константы в оригинальном коде: + +| Файл(ы) | Диапазон `id` | Кол-во записей | Предполагаемое содержимое | +|---|---|---|---| +| `levels.dat` | 2000–2015 | 16 | id=2000 — служебный блок (16 байт, см. §3); id=2001..2015 — 15 уровней | +| `guard.dat`, `fat.dat`, `skel.dat`, `shadow.dat` | 750–784 (варьируется) | ~30–35 | id=751(750) — служебный блок; остальные — кадры анимации спрайта | +| `vizier.dat` | аналогично guard | — | кадры анимации визиря | +| `kid.dat` | ~400+ | 220 | кадры анимации игрока (намного больше — герой умеет гораздо больше действий) | +| `guard1.dat`, `guard2.dat` | 750 (1 запись) | 1 | вероятно, дополнительные/альтернативные кадры/варианты | +| `title.dat` | 40–55 | 12 | картинки титульного экрана/логотипов | +| `cpalace.dat`,`epalace.dat`,`vpalace.dat`,`cdungeon.dat`,`edungeon.dat`,`vdungeon.dat` | 200–1343 | 205–238 | фоновые тайлы дворца/подземелья, отдельно для CGA(`c*`)/EGA(`e*`)/VGA(`v*`) | +| `pv.dat` | 800–981 | 103 | доп. графика (возможно, "Prince/Vizier" катсцены) | +| `digisnd1/2/3.dat` | 10000+ | 20–44 | оцифрованный звук (Covox/Disney Sound Source) | +| `midisnd1/2.dat` | 10024+ / аналог | 16 | General MIDI музыка | +| `mt32snd1/2.dat` | 10000+ | 24/7 | музыка для Roland MT-32 | +| `ibm_snd1/2.dat` | 10000+ | 44 | музыка/эффекты через PC-спикер | +| `prince.dat` | — (1 крупный ресурс) | — | MIDI-тема (вероятно, финальная тема "Принц"/титры — см. текстовые события "The Princess awaits") | + +Во всех файлах первая (наименьшая по `id`) запись — маленький "служебный" +ресурс (6–44 байта), стоящий перед основным контентом. Скорее всего это +локальная мини-таблица/палитра/список ссылок для данного набора ресурсов — +по аналогии с тем, что у уровней id=2000 отдельно от самих уровней (см. §3). + +--- + +## 2. Формат уровня (`levels.dat`, id=2001..2015) — уверенность: высокая + +Каждая запись уровня имеет размер **2305 байт** и по данным полностью +совпадает по объёму с уровнями из Apple II версии (`01 POP Source/Levels/LEVELn` +— ровно **2304 байта** каждый, см. `docs/APPLEII_RESOURCE_FORMAT.md`). + +Вывод: формат карты уровня в DOS-версии, судя по всему, **унаследован +практически без изменений от оригинального Apple II формата** (Джордан +Мехнер писал игру на 6502 и данные уровней переносились как есть), с добавлением +одного лишнего байта в DOS-упаковке (2304+1=2305 — вероятно, контрольный байт/ +маркер конца, добавленный DOS-упаковщиком ресурсов, а не часть игровых данных). + +**Практическое следствие:** байтовая структура самого уровня (тайлы 3×10 на +экран, 24 экрана, таблицы стражников, дверей и т.д.) должна документироваться +один раз — по исходникам Apple II (см. соответствующий раздел), и напрямую +применяться к DOS `levels.dat`, отбросив 1 лишний байт в конце каждой записи. +Байтовые значения тайлов в дампе (в основном 0x00–0x39) визуально согласуются +с диапазоном небольших целых кодов тайлов, что для формата карты и ожидается. + +Первая запись, id=2000, размер 16 байт — не уровень, а отдельный маленький +блок (возможно: количество уровней, начальный уровень, версия формата, +стартовые координаты игрока/охраны по умолчанию). Точное назначение не +установлено — требует сопоставления с диз­ассемблированным кодом загрузчика +уровней (в SDLPoP это, по всем признакам, отдельная процедура чтения +`level` ресурса). + +**Сверка с независимой распаковкой SDLPoP (`data/LEVELS/`):** там лежат файлы +`res2000.bin`…`res2015.bin` (16 штук — количество совпадает). Байты +`res2001.bin` содержательно совпадают с тайловыми данными нашей записи +id=2001 (та же последовательность значений тайлов) — это подтверждает, что +нумерация id верна. Но есть нестыковка по размеру: у SDLPoP `res2000.bin` — +**2305 байт** (как и все остальные), тогда как в нашем локальном +`levels.dat` запись id=2000 — всего **16 байт**. Скорее всего, это разные +релизы/сборки игры (см. §7 — размеры некоторых `.dat` у SDLPoP и у нас уже +отличались), и в версии SDLPoP маленький служебный блок либо отсутствует, +либо пронумерован иначе. Это не меняет сам формат контейнера, но означает, +что **точную семантику 16-байтного блока id=2000 в нашей копии игры пока +нельзя проверить через данные SDLPoP** — открытый вопрос. + +--- + +### 2.1 Кросс-подтверждение по исходникам Apple II + +Фоновый анализ исходников Apple II (см. `docs/APPLEII_RESOURCE_FORMAT.md`) +подтверждает и объясняет структуру уровня напрямую по коду. Уровень на Apple +II — дамп структуры `blueprnt` (`EQ.S`): `BLUETYPE`(720Б, 24 экрана×30 тайлов) ++ `BLUESPEC`(720Б) + `LINKLOC`(256Б) + `LINKMAP`(256Б) + `MAP`(96Б, граф +соседних экранов) + `INFO`(256Б, метаданные/старт Кида/стражников) = ровно +2304 байта. Учитывая, что DOS-запись уровня — это ровно 2304+1 байт с +байтовыми значениями тайлов, укладывающимися в диапазон 0–29 (id тайла) плюс +служебные биты (аналогично `idmask=%00011111`, `reqmask=%00100000` из +`EQ.S:484-486`), можно с высокой уверенностью считать, что **DOS-версия +использует ту же самую раскладку `blueprnt`**, лишь с добавлением одного +байта (вероятно, контрольной суммы) в конце DOS-упаковки. Это снимает +необходимость отдельно реверсить формат уровня для DOS — таблица тайлов, +enum id (0=space...29=archtop4), формат `LINKLOC`/`LINKMAP` и `INFO` из +Apple II документа применимы напрямую. + +## 3. Графика (спрайты и фоновые тайлы) — уверенность: средняя/низкая + +Файлы `kid.dat`, `guard.dat`, `fat.dat`, `shadow.dat`, `skel.dat`, +`vizier.dat`, `title.dat`, `c/e/v-palace.dat`, `c/e/v-dungeon.dat`, `pv.dat` +хранят по контейнерному формату (§1) множество мелких чанков (десятки—сотни +байт каждый). + +Что подтверждено: +- Наборы `shadow.dat`/`kid.dat` и `fat.dat`/`vizier.dat` содержат **побайтово + идентичные фрагменты** данных в начале файла — это ожидаемо: "Тень" (Shadow) + визуально копирует анимацию Кида, а "Толстый страж" (Fat guard, пасхалка) + переиспользует модель Визиря. Подтверждает, что персонажи одного "типа + тела" используют общий набор геометрии/анимации. +- Отдельные чанки *не* имеют очевидного унифицированного заголовка + (высота/ширина/палитра) фиксированного размера — попытка интерпретировать + первые байты чанка как `{height:u16, width:u16, flags:u16}` не подтвердилась + на реальных данных (получаются нереалистичные размеры для маленьких чанков). + Вероятно, как и в Apple II версии (см. `FRAMEDEF.S`/`SEQTABLE.S`), геометрия + кадра (ширина, высота, точка привязки) хранится **отдельно от самих + пиксельных данных** — в таблицах внутри `PRINCE.EXE`, а не в `.DAT`-чанке. + Сам чанк, вероятно, содержит только упакованные пиксельные данные + (RLE/дельта-упаковка, по аналогии с `UNPACK.S` в Apple II исходниках). +- Точный алгоритм упаковки пикселей **не восстановлен** в рамках этого + анализа по сырым байтам — байт-в-байт разбор распаковщика без + дизассемблирования `PRINCE.EXE` надёжно не сделать. **Но для практических + целей это не требуется**: см. §7 — в SDLPoP уже есть тот же самый набор + изображений в готовом, распакованном виде (PNG), которым можно пользоваться + напрямую как источником ассетов, не реализуя свой декодер `.DAT`-пикселей. + Писать собственный декодер имеет смысл только если понадобится читать + оригинальные `.DAT` "на лету" (например, для точной сверки контента именно + нашей копии игры) — тогда ориентир — исходник SDLPoP (`src/seg009.c`). + +--- + +## 4. Звук — уверенность: высокая (по структуре), низкая (по деталям кодека) + +Обнаружено 4 параллельных набора звуковых ресурсов под разные звуковые +устройства DOS-эпохи — типично для игр начала 1990-х с "звуковым меню": + +| Файл | Устройство | Формат чанка | +|---|---|---| +| `midisnd1.dat`, `midisnd2.dat` | General MIDI / MPU-401 | каждый чанк = 2-байтовый LE-префикс длины + встроенный Standard MIDI File (`MThd`...`MTrk`...) | +| `mt32snd1.dat`, `mt32snd2.dat` | Roland MT-32/CM-32L | тот же формат: префикс длины + `MThd`/`MTrk`, с MT-32-специфичными SysEx (видны строки `MT-32.mff`, текстовые мета-события вроде `"The Princess awaits"`) | +| `prince.dat` | (аналогично MIDI) | отдельный крупный музыкальный ресурс, тот же MIDI-контейнер — вероятно, финальная тема | +| `digisnd1/2/3.dat` | Covox / Disney Sound Source / Sound Blaster (оцифрованный звук) | чанк начинается с нескольких служебных байт, среди которых слово `0x2AF8` = 11000 — похоже на частоту дискретизации 11 кГц; далее — сырые 8-битные PCM-сэмплы (значения кластеризуются вокруг ~0x7A–0x90, типично для беззнакового 8-бит аудио, смещённого к середине шкалы) | +| `ibm_snd1.dat`, `ibm_snd2.dat` | PC Speaker | чанк — последовательность троек байт похожих на (длительность, делитель_частоты) — простой формат "бипера", отличный от MIDI | + +Подтверждено разбором первых чанков в каждом файле (см. байтовые дампы, +проверялись скриптом). Точная семантика полей внутри `digisnd`/`ibm_snd` +(разрядность, порядок байт служебного заголовка) не выведена до конца — при +реализации порта достаточно распознавания по типу файла и (для MIDI-семейства) +можно напрямую воспроизводить встроенный Standard MIDI File, пропустив +2-байтовый префикс длины. + +--- + +## 5. Готовые распакованные ассеты в SDLPoP (`data/`) — практический источник для порта + +Репозиторий github.com/NagyD/SDLPoP содержит папку `data/`, где, помимо +самих `.DAT`-контейнеров, каждый ресурс **продублирован в виде отдельно +распакованного файла**, названного по его `id` из таблицы оглавления (§1.2). +Проверено через GitHub API (`api.github.com/repos/NagyD/SDLPoP/contents/...`): + +| Подпапка/файл в `data/` | Содержимое | Соответствие нашему разбору | +|---|---|---| +| `GUARD.DAT`, `GUARD1.DAT`, `GUARD2.DAT` | сырые `.DAT` | размер **побайтово совпадает** с нашими локальными `guard.dat`/`guard1.dat`/`guard2.dat` (6950 / 117 / 117 байт) | +| `DIGISND1.DAT`, `MIDISND2.DAT` и др. | сырые `.DAT` | размер **не совпадает** с нашими локальными файлами (48545 vs 50101, 18408 vs 18958) — другой релиз/сборка игры | +| `GUARD/res751.png` … `res784.png` | готовые PNG, по одному на кадр анимации, имя = `res.png` | id-диапазон (751-784) точно совпадает с нашим разбором `guard.dat` | +| `VPALACE/res200.pal`, `res201.png`, `res202.png`, … | палитра (JASC `.pal`) + PNG-кадры фонов дворца, **VGA-вариант (256 цветов)** | id-диапазон (200+) совпадает с `vpalace.dat` | +| `LEVELS/res2000.bin` … `res2015.bin` | сырые дампы уровней по 2304-2305 байт | id совпадает с `levels.dat`; содержимое `res2001.bin` **сверено побайтово** с нашим id=2001 — тайловые данные совпадают | +| `KID/`, `PRINCE/`, `SHADOW/`, `SKEL/`, `VIZIER/`, `FAT/`, `TITLE/`, `VDUNGEON/`, `PV/`, `IBM_SND1/`, `IBM_SND2/`, `font/`, `music/` | аналогичные наборы для остальных ресурсов | не проверялись по отдельности, но структура (папка на каждый `.dat`, файлы `res.ext`) наблюдается одинаково | + +**Вывод:** это данные из немного **другого релиза DOS-версии**, чем те, что +лежат у нас в `MSDOS/` (см. расхождение в размере `digisnd`/`midisnd`), но +формат контейнера и нумерация `id` — те же самые. Практически это значит: + +1. Для получения играбельных PNG-спрайтов и VGA-фонов **не нужно + реализовывать декодер сжатия пикселей** — можно взять готовые файлы + `data/<ИМЯ>/res.png` напрямую как исходный материал для конвертации + под видеорежим ZX Sprinter (в т.ч. `VPALACE`/`VDUNGEON` — уже + 256-цветный VGA-арт, что прямо отвечает на вопрос про полноцветность). +2. Если в проекте важно использовать именно ту версию контента, что в наших + `MSDOS/*.dat` (а не версию из SDLPoP) — распаковку своих файлов всё же + придётся делать (кодек пикселей по-прежнему не восстановлен для сырых + `.DAT`, см. §3), либо принять решение работать с версией SDLPoP как + мастер-источником ассетов вместо своей. + +--- + +## 6. Служебные не-ресурсные файлы + +- `config.dat`, `setup.dat` — 28 байт, не являются ресурсными контейнерами + (не проходят проверку §1.1 — "размер" получается больше самого файла). + Скорее всего простые бинарные структуры настроек (звук/видеорежим, + выбранный на этапе `SETUP.EXE`/`INSTALL.EXE`), не связаны с игровым + контентом. +- `desktopd.cfg`, `setup.cfg` — текстовые/бинарные конфиги DOS-инсталлятора, + вне скоупа игровых ресурсов. +- `PRINCE.EXE` / `PRINCE.REM` — почти идентичны (отличие в единичных байтах + в районе смещения ~0x4ED0), похоже на кряк/патч одного байта проверки — + не относится к формату ресурсов. +- `old-games.nfo` — ASCII-арт NFO релиз-группы (old-games.ru), не игровые + данные. + +--- + +## 7. Итоговая таблица уверенности + +| Раздел | Уверенность | Как подтверждено | +|---|---|---| +| Контейнер `.DAT` (заголовок + таблица) | Высокая | Проверено скриптом на всех 28 файлах, инвариант offset+size выполняется без исключений; независимо подтверждено именованием `res.*` в SDLPoP `data/` | +| ID-пространства ресурсов | Средняя-высокая | Наблюдение по диапазонам + сверка с `res` именами файлов SDLPoP и побайтовым содержимым `res2001.bin` | +| Формат уровня = формату Apple II | Высокая (по размеру и содержимому), служебный блок id=2000 — открытый вопрос | Совпадение размера (2304 vs 2305), тайловые байты сходятся с `res2001.bin` из SDLPoP | +| Формат изображений/спрайтов (сырой `.DAT`) | Низкая-средняя | Контейнер подтверждён, кодек пикселей — нет; но практически закрыто наличием готовых PNG в SDLPoP `data/` (§5) | +| Формат звука (тип контейнера) | Высокая для MIDI-семейств, средняя для digisnd/ibm_snd | Явные MIDI-сигнатуры `MThd`/`MTrk` видны в байтах | + +**Рекомендация для дальнейшей работы:** для получения арт-ассетов (спрайты, +фоны, палитры) — использовать готовые распакованные файлы из +`github.com/NagyD/SDLPoP/tree/master/data` (§5), это быстрее и надёжнее +самостоятельной реализации декодера. Декодер сырого `.DAT`-формата +изображений и точную семантику служебных полей `digisnd`/`ibm_snd` +(§3, §4) стоит восстанавливать только если понадобится читать именно нашу +локальную копию `MSDOS/*.dat` "как есть" — тогда ориентир прежний: исходник +SDLPoP (`src/seg009.c`, `src/data.c`/`data.h`). diff --git a/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.pdf b/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.pdf new file mode 100644 index 0000000..0d5c219 Binary files /dev/null and b/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.pdf differ diff --git a/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.txt b/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.txt new file mode 100644 index 0000000..c2aa09a --- /dev/null +++ b/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.txt @@ -0,0 +1,2345 @@ +Prince of Persia +Specifications of +File Formats +Princed Development Team +January 5, 2008 + +Contents +1 Preamble + +3 + +2 Introduction + +3 + +3 DAT v1.0 Format Specifications +3.1 General file specifications . . . . . . . . . . . . . . . . . . . . . . +3.1.1 Some definitions . . . . . . . . . . . . . . . . . . . . . . . +3.1.2 Index structures . . . . . . . . . . . . . . . . . . . . . . . +3.1.3 Checksums byte . . . . . . . . . . . . . . . . . . . . . . . +3.2 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +3.2.1 Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . +3.2.2 Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . +3.2.3 Run length encoding (RLE) . . . . . . . . . . . . . . . . . +3.2.4 LZ variant (LZG) . . . . . . . . . . . . . . . . . . . . . . . +3.3 Palettes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +3.4 Levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +3.4.1 Unknown blocks . . . . . . . . . . . . . . . . . . . . . . . +3.4.2 Room mapping . . . . . . . . . . . . . . . . . . . . . . . . +3.4.3 Wall drawing algorithm . . . . . . . . . . . . . . . . . . . +3.4.4 Room linking . . . . . . . . . . . . . . . . . . . . . . . . +3.4.5 Guard handling . . . . . . . . . . . . . . . . . . . . . . . . +3.4.6 Start Position . . . . . . . . . . . . . . . . . . . . . . . . +3.4.7 Door events . . . . . . . . . . . . . . . . . . . . . . . . . . +3.5 Digital Waves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +3.6 Midi music . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +3.7 Internal PC Speaker . . . . . . . . . . . . . . . . . . . . . . . . . +3.8 Binary files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +3.9 Levels in POP1 for Mac . . . . . . . . . . . . . . . . . . . . . . . + +3 +3 +3 +4 +5 +5 +6 +6 +7 +7 +8 +10 +11 +11 +14 +15 +16 +16 +17 +17 +18 +18 +18 +18 + +1 + + 4 DAT v2.0 Format Specifications +19 +4.1 General file specifications . . . . . . . . . . . . . . . . . . . . . . 19 +4.1.1 The master index . . . . . . . . . . . . . . . . . . . . . . . 20 +4.1.2 The slave indexes . . . . . . . . . . . . . . . . . . . . . . 21 +4.2 Levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 +4.2.1 Room mapping . . . . . . . . . . . . . . . . . . . . . . . 22 +4.2.2 Door events . . . . . . . . . . . . . . . . . . . . . . . . . 24 +4.2.3 Guard handling . . . . . . . . . . . . . . . . . . . . . . . . 24 +4.2.4 Static guards . . . . . . . . . . . . . . . . . . . . . . . . . 25 +4.2.5 Dynamic guards . . . . . . . . . . . . . . . . . . . . . . . 26 +5 PLV v1.0 Format Specifications +26 +5.1 User data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 +5.2 Allowed Date format . . . . . . . . . . . . . . . . . . . . . . . . . 27 +6 SAV v1.0 Format Specifications + +28 + +7 HOF v1.0 Format Specifications + +28 + +8 Credits + +29 + +9 License + +29 + +2 + + 1 + +Preamble + +This file was written thanks to the hard work on reverse engineering made by +several people, see the credits section. In case you find any mistake in the text +please report it. A copy of this document should be available in our official site +at http://www.princed.org. + +2 + +Introduction + +There are two versions of the DAT file format: DAT v1.0 used in POP 1.x and +DAT v2.0 used in POP 2. In this document we will specify DAT v1.0. +DAT files were made to store levels, images, palettes, wave, midi and internal +speaker sounds. Each type has its own format as described below in the following +sections. +As the format is very old and the original game was distributed in disks, it +is normal to think that the file format uses some kind of checksum validation +to detect file corruptions. +DAT files are indexed, this means that there is an index and you can access +each resource through an ID that is unique for the resource inside the file. +Images store their height and width but not their palette, so the palette is +another resource and must be shared by a group of images. +PLV files use the extension defined to support a format with only one level +inside. +Both versions of DAT are supported and may be manipulated by PR. This +program works like an archive manager (i.e. pkzip) and extracts the files in +known formats that may be handled by other programs. For more information +about PR check the Princed home page at http://www.princed.org. +In this document you will also find the SAV and HOF format specifications +and the algorithm used by POP1 to draw the dungeon walls. + +3 + +DAT v1.0 Format Specifications + +3.1 + +General file specifications + +All DAT files have an index, this index has one entry per item with information +on each one. +The index is stored at the very end of the file. But may be located reading +the first 6 bytes (as headers) of the file, that are reserved to locate the index +and know it’s size. The sum of the location and the index size should be the +size of the file. +3.1.1 + +Some definitions + +Let’s define the numbers as: + +3 + + SC Signed char: 8 bits, the first bit is for the sign and the 7 last for the number. +If the first bit is a 0, then the number is positive, if not the number is +negative, in that case invert all bits and add 1 to get the positive number. +i.e. -1 is FF (1111 1111), 1 is 01 (0000 0001) +Range: -128 to 127 +1 byte +UC Unsigned char: 8 bits that represent the number. +i.e. 32 is 20 (0010 0000) +Range: 0 to 255 +1 byte +US Unsigned Short: Little endian, 16 bits, storing two groups of 8 bits ordered +from the less representative to the most representative without sign. +i.e. 65534 is FFFE in hex and is stored FE FF (1111 1110 1111 1111) +Range: 0 to 65535 +2 bytes +SS Signed Short: Little endian, 16 bits, storing two groups of 8 bits ordered +from the less representative to the most representative with sign. If the +first byte is 0 then the number is positive, if not the number is negative, +in that case invert all bits and add 1 to get the positive number. +i.e. -2 is FFFE in hex and is stored FE FF (1111 1110 1111 1111) +Range: -32768 to 32767 +2 bytes +UL Unsigned long: Little endian, 32 bits, storing four groups of 8 bits each +ordered from the less representative to the most representative without +sign. +i.e. 65538 is 00010002 in hex and is stored 02 00 01 00 +(0000 0010 0000 0000 0000 0001 0000 0000) +Range: 0 to 232 − 1 +4 bytes +Sizes will always be expressed in bytes unless another unit is specified. + +3.1.2 + +Index structures + +Offset + +Size + +Type + +Name + +0 + +4 + +UL + +IndexOf f set + +4 + +2 + +US + +IndexSize1 + +1 IndexOf f set + IndexSize = f ilesize + +4 + +Description +The location where the index begins +The number of bytes the index has + + Offset + +Size + +Type + +Name + +IndexOf f set = α + +IndexSize + +↓ + +F ooter + +α + +2 + +US + +N umberOf Items + +α + 2 = β2 + +N umberOf Items ∗ 8 + +- + +Index + +β + 8i = γ + +8 + +↓ + +Entry + +γ+0 + +2 + +US + +Id(i)3 + +γ+2 + +4 + +UL + +Of f set(i)3 + +γ+6 + +2 + +US + +Size(i)3 + +Description +The DAT index +Resources count +A list of NumberOfItems +blocks of 8-bytes-length index record called Entry +The 8-bytes-length index +record (one per item) +Item ID +Absolute offset where the resource start +Size of the item not including the checksum byte + +Table 1: DAT file blocks +Note: POP1 doesn’t validate the DAT file checking IndexOf f set + +IndexSize = F ileSize, this means you can append data at the end of the +file. +PR validates that IndexOf f set + IndexSize ≤ F ileSize. It also compares +IndexSize with 8 ∗ numberOf Items + 2 to determine if a file is a valid POP1 +DAT file. +3.1.3 + +Checksums byte + +There is a checksum byte for each item (resource), this is the first byte of the +item, the rest of the bytes are the item data. The item type is not stored and may +only be determined by reading the data and applying some filters, unfortunately +this method may fail. When you extract an item you should know what kind +of item you are extracting. +If you add (sum) the whole item data including checksum and take the less +representative byte (modulus 256) you will get the sum of the file. This sum +must be FF in hex (255 in UC or -1 in SC). If the sum is not FF, then adjust +the checksum in order to set this value to the sum. The best way to do that +is adding all the bytes in the item data (excluding the checksum) and inverting +all the bits. The resulting byte will be the right checksum. +From now on the specification are special for each data type (that means we +won’t include the checksum byte anymore). + +3.2 + +Images + +Images are stored compressed and have a header and a compressed data area. +Each image only one header with 6 bytes in it as follows +2 so IndexSize = 8 ∗ numberOf Items + 2 +3 with 0 ≤ i < numberOf Items + +5 + + 3.2.1 + +Headers +Offset Size Type + +Name + +Description + +0 + +6 + +↓ + +ImageHeader + +0 + +2 + +US + +Height + +2 + +2 + +US + +W idth + +4 + +2 + +- + +ImageM ask + +The header of an image +The height of the image in +pixels +The width of the image in +pixels +Information on the compression algorithm and bitrate + +Table 2: Image headers + +ImageMask is a set of bits where: +⋄ the first 8 are zeros +⋄ the next 4 are the resolution +so if it is 1011 (B in hex) then the image has 16 colours; and if it is 0000 +(0 in hex) then the image has 2 colours. To calculate the bits per pixel +there are in the image, just take the last 2 bits and add 1. e. g. 11 is 4 +(24 = 16 colours) and 00 is 1 (21 = 2 colours). +⋄ the last 4 bits are the 5 compression types (from 0 to 4) as specified in +Table 3. +Dec +Bin +Algorithm +0 +0000 +RAW LR +1 +0001 +RLE LR +2 +0010 +RLE UD +3 +0011 +LZG LR +4 +0100 +LZG UD +Table 3: Algorithm codes + +The following data in the resource is the image compressed with the algorithm specified by those 4 bits. +3.2.2 + +Algorithms + +RAW LR means that the data has not been compressed in any way, it is used +for small images. The format is saved from left to right (LR) serialising a +line to the next integer byte if necessary. In case the image was 16 colours, +two pixels per byte (4bpp) will be used. In case the image was 2 colours, +8 pixels per byte (1bpp) will be used. + +6 + + RLE LR has a Run length encoding (RLE) algorithm, after uncompressed the +image can be read as a RAW LR. +RLE UD is the same as RLE LR except that after uncompressed the bytes in +the image must be drawn from up to down and then from left to right. +LZG LR has some kind of variant of the LZ77 algorithm (the sliding windows +algorithm), here we named it LZG in honour of Lance Groody, the original +coder. After decompressed it may be handled as RAW LR. +LZG UD Uses LZG compression but is drawn from top to bottom as RLE UD. +3.2.3 + +Run length encoding (RLE) + +The first byte is always a control byte, the format is SC. If the control byte is +negative, then the next byte must be repeated n times as the bit inverted control +byte says, after the next byte (the one that was repeated) another control byte +is stored. +If the control byte is positive or zero just copy textual the next n bytes where +n is the control byte plus one. After that, the next byte is the following control +byte. +If you reach a control byte but the image size is passed, then you have completed +the image. +3.2.4 + +LZ variant (LZG) + +This is a simplified algorithm explanation: +Definition: “print” means to commit a byte into the current location of the +output stream. +The output stream is a slide window initialised with zeros. +The first byte of the input stream is a maskbyte. +For each of the 8 bits in the maskbyte the next actions must be performed: +If the bit is 1 print the next unread byte to the slide window +If the bit is a zero read the next two bytes as control bytes with +the following format (RRRRRRSS SSSSSSSS): +· 6 bits for the copy size number (R). +Range: 2 to 26 + 2 = 66 + +Add 3 to this number. + +· 10 bits for the slide position (S). Add 66 to this number. +Range: 26 + 2 = 66 to 26 + 2 + 210 = 1090 +Then print in the slide window the next R bytes that are the same slide +window starting with the S th byte. +After all the maskbyte is read and processed, the following input byte is another +maskbyte. Use the same procedure to finish decompressing the file. Remaining +unused maskbits should be zeros to validate the file. +7 + + This is the modus operandi of the compression algorithm +For each input byte we take a window containing the 1023 previous bytes. +If the window goes out of bounds (ie, when the current input byte is before +position 210 = 1024), we consider it filled with zeros. + +Figure 1: Distribution of the sindow size. +The algorithm works as follows: +While there is unread input data: +Create a maskbyte. +For each bit in the maskbyte (and there is still unread input data): +Compare the following input bytes with the bytes in the window, +and search the longest pattern that is equal to the next bytes. +If we found a pattern of length n > 2: +Assign 0 to the current bit of the maskbyte. +In the next 2 bytes of the output, specify the relative +position and length of the pattern. +Advance output pointer by 2. +Advance input pointer by n. +Else: +Assign 1 to the current bit of the maskbyte. +Copy the current input byte in the next output byte. +Advance output pointer by 1. +Advance input pointer by 1. + +For a better understanding of the algorithm we strongly recommend to read +the PR source files lzg uncompress.c and lzg compress.c that may be located at +https://gforge.lug.fi.uba.ar/plugins/scmcvs/cvsweb.php/PR/src/lib/compression/?cvsroot=freeprince in the PR +repository module. + +3.3 + +Palettes + +Palette resources store a palette for the VGA and patterns for the CGA and +EGA. Each palette resource is sized 100 bytes distributed as explained in Table 25: +Length + +Offset + +Block Name + +8 + + 4 +0 +unknown (TGA?) +48 +4 +vga palette +16 +52 +cga patterns +32 +68 +ega patterns +Table 4: DAT 1.0 Palette blocks + +The vga palette block stores 16 records of three bytes each that is the palette +in the RGB-18-bits format (6 bits for each colour). Each colour is a number +from 0 to 63. Remember to shift the colour bytes by two to get the colour +number from 0 to 256. The format is 00rrrrrr 00gggggg 00bbbbbb where rrrrrr +is the 6 bit red, gggggg the 6 bits green and bbbbbb the 6 bits blue. +In the case of EGA and CGA, palettes are not stored. The palettes are +the standard ones defined by the adapter. In Table 5 the standard palettes are +shown. +EGA CGA1 CGA2 Color name +HTML +0 +0 +0 +black +#000000 +1 +blue +#0000aa +2 +1 +green +#00aa00 +3 +1 +cyan +#00aaaa +4 +2 +red +#aa0000 +5 +2 +magenta +#aa00aa +6 +3 +brown +#aa5500 +7 +3 +light gray +#aaaaaa +8 +dark gray +#555555 +9 +bright blue +#5555ff +10 +bright green +#55ff55 +11 +bright cyan +#55ffff +12 +bright red +#ff5555 +13 +bright magenta #ff55ff +14 +bright yellow +#ffff55 +15 +bright white +#ffffff +Table 5: EGA and CGA palettes + +rgbRGB +000000 +000001 +000010 +000011 +000100 +000101 +010100 +000111 +111000 +111001 +111010 +111011 +111100 +111101 +111110 +111111 + +Where EGA is the only one palette used in EGA mode of the game and +CGA1 and CGA2 are the two palettes used in the CGA mode. As the palettes +are always the same, but the graphics are in 16 colours, some patterns are used +instead of colours. Remember EGA has 16 colours, so is represented in 4 bits +and CGA has 4 simultaneous colours represented in 2 bits. +The cga patterns block stores 16 records of one byte each, separated in four +parts, so the format is a0 a1 b0 b1 c0 c1 d0 d1 where aa is a two bit colour in one of +the two CGA palettes (palette 1 is normally used in the dungeon environment +and 2 in the palace environment). +9 + + The pattern is arranged in a 2x2 box and each pixel is denoted: +a0 a1 +c0 c1 + +b0 b1 +d0 d1 + +So for example if the entry 1 is 00101000 (0x28) in mode CGA2, the pattern +will be a checkerboard of black and green like the following: +Bin +00 01 +01 00 + +Dec +Colour +0 1 black green +1 0 green black + +The ega patterns block stores 16 records of two bytes each, this time separated in two parts. So we have again, four parts per record in the format +aaaabbbb ccccdddd. +Now, using the EGA entries 0-15 (the four bits are represented) the same +patterns as the CGA may be used. +For example, with 00101111 11110010 (0x2ff2) you can create the following +pattern: +Bin +0010 1111 +1111 0010 + +3.4 + +Dec +2 15 +15 2 + +Hex +Colour +2 f brown white +f 2 white brown + +Levels + +This table has a summary of the blocks to be used in this section, you can refer +it from the text below. +Length +Offset +Block Name +720 +0 +pop1 foretable +720 +720 +pop1 backtable +256 +1440 +door I +256 +1696 +door II +96 +1952 +links +64 +2048 +unknown I +3 +2112 +start position +3 +2115 +unknown II +1 +2116 +unknown III +24 +2119 +guard location +24 +2143 +guard direction +24 +2167 +unknown IV (a) +24 +2191 +unknown IV (b) +24 +2215 +guard skill +24 +2239 +unknown IV (c) +24 +2263 +guard colour +16 +2287 +unknown IV (d) +2 +2303 +0F 09 (2319) +Table 6: DAT 1.0 Level blocks +10 + + All levels have a size of 2305, except in the original game, that the potion +level has a size of 2304 (may be it was wrong trimmed). +3.4.1 + +Unknown blocks + +Blocks described in this section are: Unknown from I to IV. +Unknown III and IV blocks does not affect the level if changed, if you find +out what they are used to we will welcome your specification text. +Unknown I may corrupt the level if edited. +We believe unknown II has something to do with the start position, but we +do not know about it. +As unknown II were all zeros for each level in the original set, it was a team +decision to use those bytes for format extension. If one of them is not the default +00 00 00 hex then the level was extended by the team. Those extensions are +only supported by RoomShaker at this moment. To see how those extensions +were defined read the appendix I will write some day. For the moment you may +contact us if you need to know that. +3.4.2 + +Room mapping + +This section explains how the main walls and objects are stored. The blocks +involved here are “pop1 foretable” and “pop1 backtable” +In a level you can store a maximum of 24 rooms (also called screens) of 30 +tiles each, having three stages of 10 tiles each. Screens are numbered from 1 to +24 (not 0 to 23) because the 0 was reserved for special cases. +The pop1 foretable and pop1 backtable blocks have 24 sub-blocks inside. +Those sub-blocks have a size of 30 bytes each and has a room associated. So, +for example, the sub-block staring in 0 corresponds to the room 1 and the +sub-block starting in 690 corresponds to the room 24. It is reserved 1 byte +from the pop1 foretable block and one from the pop1 backtable block for each +tile. To locate the appropriate tile you have to do the following calculation: +tile = (room − 1) ∗ 30 + tileOf f set where tileOffset is a number from 0 to 29 +that means a tile from 0 to 9 if in the upper stage, from 10 to 19 if in the middle +stage and 20 to 29 if in the bottom stage. We define this as the location format +and will be used also in the start position. Always looking from the left to the +right. So there is a pop1 foretable and pop1 backtable byte for each tile in the +level and this is stored this way. +The pop1 foretable part of the tile stores the main tile form according to the +table below. Note that those are just a limited number of tiles, each code has a +tile in the game. The tiles listed are all the ones needed to make a level so the +missing tiles have an equivalent in this list. +Each tile has a code id, as some codes are repeated this is how you have to +calculate the codes. A tile in the pop1 foretable part has 8 bits in this format +rrmccccc, where rr are random bits and can be ignored. m is a modifier of the +tile. For example modified loose floors do not fall down. The rest ccccc is the + +11 + + code of the tile tabled below. Tile names are the same as the ones used by +RoomShaker to keep compatibility. +Hex +0x00 +0x01 +0x02 +0x03 +0x04 +0x05 +0x06 +0x07 +0x08 +0x09 +0x0A +0x0B +0x0C +0x0D +0x0E +0x0F +0x10 +0x11 +0x12 +0x13 +0x14 +0x15 +0x16 +0x17 +0x18 +0x19 +0x1A +0x1B +0x1C +0x1D +0x1E +0x1F + +Binary +Group +Description +00000 +free +Empty +00001 +free +Floor +00010 +spike +Spikes +00011 +none +Pillar +00100 +gate +Gate +00101 +none +Stuck Button +00110 +event +Drop Button +00111 +tapest +Tapestry +01000 +none +Bottom Big-pillar +01001 +none +Top Big-pillar +01010 +potion +Potion +01011 +none +Loose Board +01100 +ttop +Tapestry Top +01101 +none +Mirror +01110 +none +Debris +01111 +event +Raise Button +10000 +none +Exit Left +10001 +none +Exit Right +10010 +chomp +Chopper +10011 +none +Torch +10100 +wall +Wall +10101 +none +Skeleton +10110 +none +Sword +10111 +none +Balcony Left +11000 +none +Balcony Right +11001 +none +Lattice Pillar +11010 +none +Lattice Support +11011 +none +Small Lattice +11100 +none +Lattice Left +11101 +none +Lattice Right +11110 +none +Torch with Debris +11111 +none +Null +Table 7: POP1 Foretable codes + +The pop1 backtable part of the tile stores a modifier or attribute of the +pop1 foretable part of the tile. This works independently of the modifier bit +in the code. The tile modifier depends on the group the tile belongs which are +wall, chomp, event, ttop, potion, tapp, gate, spike and free. The group event +allows the 256 modifiers and will be described in Section 4.2.2. +In the original game all elements are allowed in all levels, but it is possible +to set it up Hex-editing the uncompressed version. To do that, read the Hex +12 + + editing documentation. +Group +none +free +free +free +free +spike +spike +spike +spike +spike +spike +spike +spike +spike +spike +gate +gate +tapest +tapest +tapest +tapest +potion +potion +potion +potion +potion +potion +potion +ttop +ttop +ttop +ttop +ttop +ttop +ttop +ttop +chomp +chomp +chomp +chomp +chomp + +Code +0x00 +0x00 +0x01 +0x03 +0xFF +0x00 +0x01 +0x02 +0x03 +0x04 +0x05 +0x06 +0x07 +0x08 +0x09 +0x00 +0x01 +0x00 +0x01 +0x02 +0x03 +0x00 +0x01 +0x02 +0x03 +0x04 +0x05 +0x06 +0x00 +0x01 +0x02 +0x03 +0x04 +0x05 +0x06 +0x07 +0x00 +0x01 +0x02 +0x03 +0x04 + +Description +This value is used always for this group +Nothing1 , Blue line2 +Spot11 , No blue line2 +Window +Spot31 , Blue line?2 +Normal (allows animation) +Barely Out +Half Out +Fully Out +Fully Out +Out? +Out? +Half Out? +Barely Out? +Disabled +Closed +Open +With Lattice2 +Alternative Design2 +Normal2 +Black2 +Empty +Health point +Life +Feather Fall +Invert +Poison +Open +With lattice2 +Alternative design2 +Normal2 +Black2 +Black2 +With alternative design and bottom2 +With bottom2 +With window2 +Normal +Half Open +Closed +Partially Open +Extra Open + +1 Dungeon environment +2 Dungeon environment + +13 + + Group +chomp + +Code +Description +0x05 +Stuck Open +Table 8: Background modifiers by group + +Note: Some modifiers have not been tested, there +may be any other unknown tile type we have not +still discover. + +3.4.3 + +Wall drawing algorithm + +This section does not have a direct relation with the format because it describes +how the walls must be drawn on the room. However, as this information should +be useful to recreate a cloned room read from the format we decided to include +this section to the document. +Wall drawing depends on what is in the right panel. If the right panel is not +a wall (binary code ends in 10100) a base wall will be drawn and other random +seed will be used. If the right panel is a wall then the main base wall will be +drawn and the described seed will be used. +When the base wall is drawn, the modifiers should be blitted over it. There +are 53 different types of walls using the same base image. We will call the seed +array to the one having the modifier information of those 53 panels. This array +has indexes from 1 to 53 included. +To calculate what value take from the array this calculation must be performed: panelInf o = seedArray[roomN umber + wallP osition] where panelInfo is the result modifier information that will be applied to the base image; +seedArray is this array that will be described above as a table; roomNumber +is the number of the room the wall is (from 1 to 24) and wallPosition is the +position the wall is (from 0 to 29), using the location format specified in section +3.4.2. This means the first value is 1 (roomNumber=1 and wallPosition=0) and +the last is 53 (roomNumber=24 and wallPosition=29). +Modifiers affects the corners of a stone. There are three stone rows per wall. +If the modifier is activated this corner will appear different (seems to be darker). +Another modifier is the grey stone. +Modifier +Grey stone +Left, bottom +Left, top +Right, bottom +Right, up + +Seed Positions +(First row) +2, 5, 14, 17, 26, 32, 35, 50 +2, 11, 36, 45 +37 +27, 33 +4, 10, 31, 37 + +Grey stone +Left, bottom + +(Second row) +none +34, 47 +14 + + Modifier +Left, top +Right, bottom +Right, top + +Seed Positions +9, 10 +2, 8, 25, 35 +6, 12, 23, 29, 39 + +(Third row) +Grey stone +none +Left, bottom +none +Left, top +16 +Right, bottom +none +Right, top +none +Table 9: Stone modifiers on seed position + +Another modifiers are saved in the seed too. Those modifiers are not boolean +values, they are offsets and sizes. As each stone has a different size the stone +separation offset is saved in the seed. +For the first row, the stones are all the same size and the seed is not needed. +For the second row we have got the first 20 values, ordered from 1 to 20. +Field +Values +position +1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 +offsets +543315421 1 5 3 2 1 5 4 3 2 5 4 +separator size +011000110 0 1 1 1 0 0 1 1 1 0 0 +Table 10: First 20 seed values of the second row separator + +We will be adding the next values as soon as we count the pixels ;) This +information can be found in walls.conf file from FreePrince. +3.4.4 + +Room linking + +This section describes the links block. +Each room is linked to another by each of the four sides. Each link is stored. +There is no room mapping, just room linking. +The links block has 24 sub-blocks of 4 bytes each. All those sub-blocks has +its own correspondence with a room (the block starting at 0 is related to the +room 1 and the block starting at with 92 is related to room 24). Each block of +4 bytes stores the links this room links to reserving one byte per each side in +the order left (0), right (1), up (2), down (3). The number 0 is used when there +is no room there. Cross links should be made to allow the kid passing from a +room to another and then coming back to the same room but it is not a must. + +15 + + 3.4.5 + +Guard handling + +This section specifies the blocks: guard location, guard direction, guard skill +and guard colour. +Each guard section has 24 bytes, each byte of them corresponds to a room +so byte 0 is related to room 1 and byte 23 is related to room 24. This room is +where the guard is located. The format only allows one guard per room. Each +block describes a property or attribute of the guard. +The guard location part of a guard describes where in the room the guard +is located, this is a number from 0 to 29 if the guard is in the room or 30 if +there is no guard in this room. Other values are allowed but are equivalent to +30. The number from 0 to 29 is in the location format specified in Section 3.4.2. +The guard direction part describes where the guard looks at. If the value is +0, then the guard looks to the right, if the value is the hex FF (-1 or 255) then +he looks left. This is the direction format, and will be used in the start position +too. +The guard skill is how the guard fights, style and hit points. Note that the +hit points also depends on the level you are. Allowed numbers are from 0 to 9. +The guard colour is the palette the guard has (see Section 3.8). The default +colours are in this table: +Code +Pants +Cape +0x00 +Light +Blue Pink +0x01 +Red +Purple +0x02 +Orange +Yellow +0x03 +Green +Yellow +0x04 +Dark Blue +Beige +0x05 +Purple +Beige +0x06 +Yellow +Orange +Table 11: Default Guard colours + +Other codes may generate random colours because the game is reading the +palette from trashed memory. This may also cause a game crash. It is possible +to add new colours in the guard palette resource (see Section 3.8) avoiding the +crash. +3.4.6 + +Start Position + +This section describes the start position block. +This block stores where and how the kid starts in the level. Note that all +level doors that are on the starting room will be closed in the moment the level +starts. + +16 + + This block has 3 bytes. The first byte is the room, allowed values are from +1 to 24. The second byte is the location, see the section 3.4.2 for the location +format specifications. The third byte is the direction, see 3.4.5 for the direction +format specifications. +3.4.7 + +Door events + +This section explains how the doors are handled and specifies the blocks door I +and II. +First of all he have to define what an event line is in this file. An event line +is a link to a door that will be activated. If the event was triggered with the +action close, then the event will close the door, if the action was open then the +event will open the door. An event line has also a flag to trigger the next event +line or not. An event is defined as a list of event lines, from the first to the +last. The last must have the trigger-next-event-line flag off. This is like a list of +doors that performs an action. An event performs the action that it was called +to do: open those doors or close them. This action is defined by the type of tile +pressed. Each event line has an ID from 0 to 255. An event has the ID of the +first event line in it. +In section 3.4.2 it is explained how a door trigger is associated to an event +ID. Those are the tiles that starts the event depending on what are them: closers +or openers. +How events are stored: Each door block has 256 bytes, one per event line. +Each event line is located in an offset that is the event line ID, so event line 30 +is located in the byte 30 of each block. There is a door I part of an event line +and a door II part of it. We will define them as byte I and byte II. You can find +there: the door room, the door location, and the trigger-next flag. The format +is the following: +Let’s define Screen S = s1 s2 s3 s4 s5 as a 5-bit-number from 1 to 24 (5 bits) +where sn is the bit n of the binary representation and whose value represents +the screen number; Location L = l1 l2 l3 l4 l5 as another 5-bit-number from 0 to +29 where ln is the bit n of the binary representation whose value is according +to the location format specifications (See Section 3.4.5) ; Trigger-next T = t1 +as one bit having 1 for “off” or a 0 for “on”. +Byte I has the form: t1 s4 s5 l1 l2 l3 l4 l5 +Byte II has the form: s1 s2 s3 00000 + +3.5 + +Digital Waves + +Read them as raw digital wave sound using the following specifications: +Field +Size of Format +Format + +Value +16 +PCM + +17 + + Field Value +Attributes 8 bit, mono, unsigned +Channels 1 +Sample rate 11025 +Bytes/Second 11025 +Block Align 1 +Table 12: Wave Specifications + +GNU/Linux users can play the raw waves just dropping them inside /dev/dsp +As DAT headers are very small it is valid to type in a shell console with dsp +write access: cat digisnd?.dat>/dev/dsp to play the whole wave files. + +3.6 + +Midi music + +Standard MIDI files. There have been reports that some versions of MIDI did +not work, but we believe this can bi fixed saving in other MIDI format variant +(like type 0). + +3.7 + +Internal PC Speaker +Offset +Size +Type Name Description +0 +3 +↓ Header The file header +0 +2 +UC Junk 0x00 (or 0x80 sometimes) +2 +1 +US +bps +Beats per two seconds +0 +3numberOf N otes ↓ +Body The file body +3i +2 +US +F req frequency in hertz (0 if no +sound, 1 or 2 if marker) +3i + 2 +1 +US length length in beats +0 +2 +↓ +F ooter The file footer +0 +2 +Junk 0x12 0x00 +Table 13: DAT file blocks + +3.8 + +Binary files + +Some binary files contains relevant information The resource number 10 in +prince.dat has the VGA guard palettes in it saving n records of a 16-colourpalette of 3 bytes in the specified palette format. + +3.9 + +Levels in POP1 for Mac + +In the case of Mac, executable and resource data are embedded in the one runtime file. Level data is a part of resources, for examples graphics, icons and +sounds. Level blocks are very similar to PC but not exactly identical. Following +18 + + table has a summary of the blocks of DAT 1.0 for Mac. +Length +Offset +Block Name +720 +0 +pop1 foretable +720 +720 +pop1 backtable +256 +1440 +door I +256 +1696 +door II +96 +1952 +links +64 +2048 +unknown I +3 +2112 +start position +21 +2115 +unknown II+III +24 +2136 +guard location +24 +2160 +guard direction +24 +2184 +unknown IV (a) +24 +2208 +unknown IV (b) +24 +2232 +guard skill +24 +2256 +unknown IV (c) +24 +2280 +guard colour +4 +2304 +unknown IV (d) +Table 14: DAT 1.0 Level blocks for Mac + +All levels have a size of 2308. Also there are two different things in comparison with DAT 1.0 for PC. DAT 1.0 for Mac does not have any index and +checksums. 16 levels including demo and potion level are only chained in sequence. See 3.4 for reference on each block. + +4 + +DAT v2.0 Format Specifications + +4.1 + +General file specifications + +POP2 DAT files are not much different from their POP1 predecessors. The +format is similar in almost each way. The main difference is in the index. As +DAT v1.0 used an index in the high data, the DAT v2.0 indexes are two level +encapsulated inside a high data. So there is an index of indexes. +We will use the same conventions than in the prior chapter. The checksum +validations are still the same. +Offset Size Type +Name +Description +0 +6 +↓ +Header +The file header +0 +4 +UL HighDataOf f set The location where the +highData begins +4 +2 +US +HighDataSize the number of bytes the +highData has +Table 15: High data structures + +19 + + This is similar to DAT v1.0 format, except that the index area is now called +high data. +The high data part of the file contains multiple encapsulated indexes. Each +of those index is indexed in a high data index of indexes. We will call this index +the master index and the sub index the slave indexes. Slave indexes are the real +file contents index. +4.1.1 + +The master index + +Offset + +Size + +Type + +Name + +0 + +4 + +UL + +IndexOf f set + +4 + +2 + +US + +IndexSize1 + +IndexOf f set = α + +IndexSize + +↓ + +F ooter + +α + +2 + +US + +N umberOf Items + +α + 2 = β2 + +N umberOf Items ∗ 8 + +- + +Index + +β + 8i = γ + +8 + +↓ + +Entry + +γ+0 + +2 + +US + +Id(i)3 + +γ+2 + +4 + +UL + +Of f set(i)3 + +γ+6 + +2 + +US + +Size(i)3 + +Description +The location where the index begins +The number of bytes the index has +The DAT index +Resources count +A list of NumberOfItems +blocks of 8-bytes-length index record called Entry +The 8-bytes-length index +record (one per item) +Item ID +Absolute offset where the resource start +Size of the item not including the checksum byte + +Table 16: DAT 2.0 Master index +Note: POP1 doesn’t validate the DAT file checking IndexOf f set + +IndexSize = F ileSize, this means you can append data at the end of the +file. +The master index is made with: - Offset HighDataOf f set, size 2, type US: +NumberOfSlaveIndexes (the number of the high data sections) - Offset HighDataOf f set+ +2, size N umberOf SlaveIndexes ∗ 6: The master index record (a list of NumberOfSlaveIndexes blocks of 6-bytes-length index record each corresponding to +one slave index) +The 6-bytes-length index record (one per item): Size = 6 bytes - Relative offset 0, size 4, type sting: 4 ASCII bytes string denoting the Slave Index ID. The character order is inverted. - Relative offset 4, size 2, type US: +SlaveIndexOf f set (slave index offset relative to HighDataOffset) +1 IndexOf f set + IndexSize = f ilesize +2 so IndexSize = 8 ∗ numberOf Items + 2 +3 with 0 ≤ i < numberOf Items + +20 + + From the end of the DAT High Data Master Index to the end of the file +we will find the High Data section contents (where the HighDataOffset relative +offsets points to). This content has a set of second indexes each called Slave +Index. So, to find a Slave Index offset you have to add the file HighDataOffset +to the SlaveIndexOffset for the desired index. +There are different 4-byte-length ASCII strings called Slave Index IDs. When +the string is less than 4 bytes, a tailing byte 0x00 is used. We will denote it +with the null-space symbol “ ”. The character order is inverted, so for example +the text SLAP becomes PALS, MARF becomes FRAM, +becomes empty or +RCS becomes SCR. They must be in upper case. +ID +“cust” +“font” +“fram” +“palc” +“pals” +“palt” +“piec” +“psl” +“scr” +“shap” +“shpl” +“strl” +“snd” +“seqs” +“txt4” +“” + +4.1.2 + +Stored +TSUC +TNOF +MARF +CLAP +SLAP +TLAP +CEIP +LSP +RCS +PAHS +LPHS +LRTS +DNS +SQES +4TXT + +Description +Custom +Fonts +Frames +CGA Palette +SVGA Palette +TGA Palette +Pieces +? +Screens (images that have the full room) +Shapes (normal graphics) +Shape palettes +Text +Sound +Midi sequences +Text +Levels +Table 17: Slave Index ID strings + +The slave indexes + +All encapsulated sections pointed by the Master Index are Slave Indexes. The +slave index is specified with: - Offset SlaveIndexOffset, size 2, type US: NumberOfItems (the number of the records referring to the file data) - Offset SlaveIndexOffset+2, size NumberOfItems*11: The slave index record (a list of NumberOfItems blocks of 11-byte-length index record each corresponding to one +slave index) +The 11-byte-length slave index record (one per item): Size = 11 bytes Relative offset 0, size 2, type US: Item ID - Relative offset 2, size 4, type UL: +Resource start (absolute offset in file) - Relative offset 6, size 2, type US: Size +of the item (not including the checksum byte) - Relative offset 8, size 3, type +binary: A flags mask (in “shap” indexes it is always 0x40 0x00 0x00; in others +0x00 0x00 0x00) +Finally, we can locate whatever item we want if we have the - Slave Index +21 + + ID - Item ID this is not a unique key, unfortunately we have found repeated +pairs of IDs for different items, so we have to use the “order” as a third key. +So, the way to find an item is: first read the High Data Offset, go there, read +the number of slave items, iterate the master index to find the desired slave item +comparing it with the Slave Index ID. Then go to the found Slave Index offset +(remember to add the High Data Offset) and read the number of items for this +index. Finally iterate the slave index to find the desired item comparing the +Item ID and read the offset and size. Now you will have the offset of the desired +item’s checksum, increasing the offset by one will give you the beginning of the +item content. + +4.2 + +Levels + +This table has a summary of the blocks to be used in this section. +Length +960 +3840 +1280 +128 +32 +3 +4 +3712 +1088 +978 + +Offset +0 +960 +4800 +6080 +6208 +6240 +6243 +6247 +9959 +11047 + +Block Name +pop2 foretable +pop2 backtable +pop2 doors +links (as explained in Section 3.4.4 but having 32 rooms) +unknown I +start position (as explained in Section 3.4.6) +unknown II (00 01 00 02) (check pop1) +pop2 static guard +pop2 dynamic guard +unknown III +Table 18: DAT 2.0 Level blocks + +All levels have a size of 12025. +4.2.1 + +Room mapping + +You should read section 3.4.2 before reading this one. A POP2 level can store +a maximum of 32 rooms of 30 tiles each, having three stages of 10 tiles each. +Rooms are numbered from 1 to 32 (not 0 to 31) because the 0 is be reserved to +the null-room. +The pop2 foretable block has 32 sub-blocks inside. Each sub-block has a size +of 30 bytes and has a room associated. For each byte in this room there is a +tile in the game. Each byte has a code to represent a tile. There are additional +attributes to this tile also. +To locate the 7th tile in the bottom floor of the room 27 you have to do the +same calculation as in 3.4.2: tile = (room − 1) ∗ 30 + tileOf f set = (27 − 1) ∗ +30 + 2 ∗ 10 + 7 = 807 +Dec Hex Bin +Caverns +00 0x00 000000 Empty + +Ruins +Empty +22 + +Temple +Empty + + Dec Hex Bin +Caverns +Ruins +Temple +01 0x01 000001 Floor +Floor +Floor +02 0x02 000010 Spikes +(?) +Spikes +03 0x03 000011 Pillar +Pillar +Pillar +04 0x04 000100 Door +Gate +Gate +05 0x05 000101 (?) +Raised Button +Raised Button +06 0x06 000110 (?) +Drop Button +Drop Button +07 0x07 000111 (?) +Tunnel +(?) +08 0x08 001000 Bottom Big Pillar +Bottom Big Pillar Bottom Big Pillar +09 0x09 001001 Top Big Pillar +Top Big Pillar +Top Big Pillar +10 0x0A 001010 Potion +Potion +Potion +11 0x0B 001011 Loose Floor +Loose Floor +Loose Floor +12 0x0C 001100 (?) +Slicer Left Half +Slicer Left Half +13 0x0D 001101 (?) +Slicer Right Half Slicer Right Half +14 0x0E 001110 Debris +Debris +Debris +15 0x0F 001111 (?) +Drop Floor +(?) +16 0x10 010000 Exit Half Left +Exit Half Left +Exit Half Left +17 0x11 010001 Exit Half Right +Exit Half Right Exit Half Right +18 0x12 010010 Magic Carpet +(?) +(?) +19 0x13 010011 Torch +(?) +Torch +20 0x14 010100 Wall +Wall +Wall +21 0x15 010101 (?) +Skeleton +(?) +22 0x16 010110 (?) +Sword +(?) +23 0x17 010111 Lava Pit Left +(?) +(?) +24 0x18 011000 Lava Pit Right +(?) +(?) +25 0x19 011001 (?) +(?) +Squash Wall +26 0x1A 011010 (?) +(?) +Flip Tile +27 0x1B 011011 (?) +(?) +(?) +28 0x1C 011100 (?) +(?) +(?) +29 0x1D 011101 (?) +(?) +(?) +30 0x1E 011110 (?) +(?) +(?) +31 0x1F 011111 (?) +(?) +(?) +32 0x20 100000 Torch w/Debris +(?) +Torch w/Debris +33 0x21 100001 Exit Door Top Left (?) +(?) +34 0x22 100010 Pressure Plate +(?) +(?) +35 0x23 100011 Exit Door Top Right (?) +(?) +36 0x24 100100 Dart Gun +(?) +(?) +37 0x25 100101 (?) +(?) +(?) +38 0x26 100110 (?) +(?) +(?) +39 0x27 100111 (?) +(?) +(?) +40 0x28 101000 (?) +(?) +(?) +41 0x29 101001 (?) +(?) +(?) +42 0x2A 101010 (?) +(?) +(?) +43 0x2B 101011 (?) +(?) +Blue Flame +44 0x2C 101100 Rope Bridge +(?) +(?) +45 0x2D 101101 (?) +(?) +(?) +23 + + Dec Hex Bin +Caverns +Ruins +46 0x2E 101110 (?) +(?) +47 0x2F 101111 (?) +(?) +Table 19: POP2 Foretable Codes + +Temple +(?) +(?) + +The pop2 backtable is an expansion if the pop1 backtable and it is sized 4 +times bigger. For each tile there are 4 additional bytes in the pop2 backtable +tiles +block to specify further actions or attributes. This block is sized 4 bytes +tile ∗10 f loor ∗ +loors +∗ 32rooms that is 3840bytes. We call background mask to each block of +3 froom +4 bytes associated to a tile. To locate a background mask you have to do the +following operation: 960 + (room − 1) ∗ 30 ∗ 4 + tileOf f set ∗ 4 Background masks +are stored consecutively each after another until the 960 tiles are specified. +The first byte is an unsigned char (UC) association to one of the 256 door +event registers (see Section 4.2.2) if the tile is an activator. In any other case +this byte is an extra attribute information byte. For example in wall (0x14) +having this byte in 0x04 means the wall is curved. +The second byte in a background mask is the attribute byte. For example +0x18 modifies the tile 0x01 and adds two small stalactites. +We believe the special images uses the 3rd or 4th byte. + +4.2.2 + +Door events + +This section explains how doors are handled and specifies the block pop2 door. +The pop2 door block has 1280 bytes. It is divided in 256 registers of 5 bytes +called door events. Like POP1 events have associations to doors and activate +them. In POP2 events can also activate a floor shooter. +An event is triggered when an activator button (0x22) is pressed. As it is +specified in the Section 4.2.1, the first byte of the attribute mask belonging to a +button tile points it to a door event that is triggered when the button is pressed. +There is a maximum of 256 events because of the unsigned char of the first byte +if the attribute mask in the pop2 backtable block and the 256 registers in the +pop2 door block. +Each event register is of the form “LL SS TT FD FD” which activates the +normal door (0x04), right exit door (0x11) or shooter (0x24) located in the tile +LL of the screen SS. TT is 00 for normal activation and FF for exit doors. +4.2.3 + +Guard handling + +This section explains how guards are handled. In POP2 there are two different +types of guards. We will call them static and dynamic guards. Static guards are +the normal guards that are waiting in a room like in POP1. In the other hand, +dynamic guards are the ones who appear in the room running from one of the +sides. Each type of guard is attached to a room and is handled in a different +way, so a room can have both types of guards, one or none depending on the +specifications. There is a block for each type of guard, pop2 static guard is the +24 + + specification of the static guards and pop2 dynamic guard is the specification +of the dynamic ones. Each block has different specifications and sizes as it is +mentioned bellow. +4.2.4 + +Static guards + +In this item static guards are explained and the pop2 static guard is specified. +For each screen there is reserved memory space for a maximum of 5 guards. +The pop2 static guard block has a size of 3712 divided in 32 sub-blocks of +116 bytes each. As there is a correspondence between each sub-block and the +room with this number, we will call them “room guard blocks”. +Offset Size Type +Name +Description +0 +6 +↓ +Header +The file header +0 +4 +UL HighDataOf f set The location where the +highData begins +4 +2 +US +HighDataSize the number of bytes the +highData has +Table 20: High data structures +A room guard block has a size of 116 divided this way: +⋄ 1 byte for the number of guards present in this room. This byte may take +values from 0 to 5. +⋄ 5 block divisions of 23 bytes for each guard. The first divisions have the +guard information, if the number is less than 5, then the latest divisions +corresponding to the missing guards will be filled with zeros or garbage. +If there is a static guard corresponding to this division of 23 bytes, the +following bytes from 0 to 22 will specify the guard: +Byte 0 is a UC showing the location in this room (same as explained in +3.4.5) of the current guard. Bytes 1 and 2 are a SS with an offset in pixels +to reallocate the guard in the floor. Byte 3 is the facing direction as specified +in 3.4.5. Byte 4 is the skill Byte 5 is unknown Bytes 6,7 and 8 are always 0, +probably because 5 is a long from 0 to 255. Byte 9 is the guard colour in levels +where it is needed (0-3), ie. 1 white+blue, 2 white+black, 3 red. Byte 10 is the +guard number (0 for the first one, 1 for the second, etc). Bytes 11,12,13 and 14 +are unknown, mostly 0, but in 10 guards it is 0x52756e2d. Byte 15 is the type +(0-8 and 84), but does not apply in all levels, ie. 5 head, 8 snake. Byte 16 is +the hit points of the guard (0 to 8). Bytes 17 and 18 are the activate triggers +for skeletons, byte 17 is (0,1,-1) and 18 is (0,-1). Normal value is 0x0000 for a +sleeping skeleton. When set to -1 (0xffff) a trigger will be waiting to wake the +skeleton up, for example the exit door open. Other possible values are 0x0100 +that is the skeleton already awake and 0xff00 that seems to be similar than +0x0000. Bytes 19,20,21 are always 0. Byte 22 is unknown (mostly 0, but 1 and +3 where found for some guards). +25 + + 4.2.5 + +Dynamic guards + +The dynamic guards are the ones who appear running throw a room’s corner +and they are defined in the pop2 dynamic guard block. This block has 34 bytes +for each of the 32 rooms, so it is sized 1088 bytes. Each room has a specification +about those guards. There is only one different type of dynamic guard per room, +but it is possible to set the number of guards that will appear running. +Offset Size Type +Name +Description +0 +6 +↓ +Header +The file header +0 +4 +UL HighDataOf f set The location where the +highData begins +4 +2 +US +HighDataSize the number of bytes the +highData has +Table 21: High data structures + +A room guard block has a size of 116 divided this way: +The bytes are from 0 to 33: Bytes from 8 to 17 may take the value 0x5a and +0. Bytes 8,9,10,15,16 are always 0. Byte 18 activates dynamic guard. 1 is true +and 0 is false. Byte 19 is the skill (0-7, 8 to make it passive) Bytes 20, 21 and +22 are always 0. Byte 23 is the dynamic guard mode: 0 or 1 to make the guard +wait until all guards are dead to spawn and 2 to spawn even when the prior +guard is still alive. Byte 24 is the floor the guard will appear on. 0 is the upper +one and 2 is the lower. Another number will kill the guard playing the sound. +Byte 25 is the initial location of the guard. ie. 0 is the left side of the screen, 9 +is the right side, it is possible to locate them in the middle of the screen, they +will magically spawn in a position between 1 and 8. As they will run to the +center of the screen, this byte also sets the facing direction up (0 to 5 is right, 6 +to 9 is left). Byte 26 is the time in ticks the first guard will wait to spawn. Byte +27 is the time in ticks between guards spawn. Bytes 28 and 29 are unknown. +Byte 30 is the number of guards that will appear, there is a maximum of 5 per +room, including static guards. Byte 31 is the hit points the guards will have. + +5 + +PLV v1.0 Format Specifications + +PLV v1.0 files are defined in this table: +Size +7 +1 +1 +1 +4 +4 +B1 + +Offset +0 +7 +8 +9 +10 +14 +18 + +Description +Magic identifier +POP version +PLV version +Level Number +Number of fields +Block 1: Level size (B1 ) +Block 1: Level code +26 + +Type +Content +text “POP LVL” +UC +0x01 +UC +0x01 +UC +UL +UL 2306/2305 +- + + Size Offset Description +Type +4 18 + B1 Block 2: User data size (B2 ) UL +B2 22 + B1 Block 2: User data +Table 22: PLV blocks + +Content + +Level code is the exact level as described in Section 3.4 including the checksum byte. Note that Level size (B1 ) also includes the checksum byte in the +count. POP version is 1 for POP1 and 2 for POP2. PLV version is 1 for PLV +v1.0. Only one level may be saved in a PLV, the level number is saved inside. + +5.1 + +User data + +User data is a block of extensible information, Number of fields is the count of +each field/value information pair. A pair is saved in the following format: +field\_name\\0value\\0 +where \0 is the null byte (0x00) and field name and value are strings. +There are mandatory pairs that must be included in all PLV files. Those +are: +Field name +Description +Editor Name +The name of the editor used to save the file +Editor Version +The version of the editor used to save the file +Level Author +The author of the file +Level Title +A title for the level +Level Description +A description +Time Created +The time when the file was created +Time Last Modified +The time of the last modification to the file +Original Filename +The name of the original file name (levels.dat) +Original Level Number Optional. The level number it has when it was +first exported +Table 23: PLV Mandatory Fields + +The content values may be empty. There is no need to keep an order within +the fields. + +5.2 + +Allowed Date format + +To make easy time parsing the time format must be very strict. There are only +two allowed formats: with seconds and without. With seconds the format is +“YYYY-MM-DD HH:II:SS” Without seconds the format is “YYYY-MM-DD +HH:II” Where YYYY is the year in 4 digits, MM is the month in numbers, MM +the months, DD the days, HH the hour, II the minute and SS the second in the +military time: HH is a number from 00 to 23. +27 + + If the month, day, hour or second have only one digit, the other digit must +be completed with 0. +i.e. 2002-11-26 22:16:39 + +6 + +SAV v1.0 Format Specifications + +SAV v1.0 saves kid level, hit points and remaining time information in order to +restart the game from this position. +SAV files are 8 bytes length in the following format: +Offset Size Type Name Description +0 +2 +US +i +Remaining minutes +2 +2 +US +ii +Remaining ticks +4 +2 +US +iii Current level +6 +2 +US +iv Current hit points +Table 24: SAV blocks + +Remaining minutes (i) Range values: 0 to 32766 for minutes 32767 to 65534 +for NO TIME (but the time is stored) 65535 for game over +1 +Remaining ticks (ii) Seconds are stored in ticks, a tick is 12 +seconds. To get +the time in seconds you have to divide the integer “Remaining ticks” by 12. +Range values: 0.000 to 59.916 seconds (rounded by units of ≈ 83ms or +1 +seconds) +0 to 719 ticks +12 +Level (iii) Range values: 1 to 12 for normal levels 13 for 12bis 14 for princess +level 15 for potion level +Hit points (iv) Range values: 0 for an immediate death 1 to 65535 hit points + +7 + +HOF v1.0 Format Specifications + +HOF files are used to save the Hall of Fame information. +All HOF v1.0 files have a size of 176 bytes. The first 2 bytes belongs to the +record count. The format is US. The maximum number of records allowed is +6, so the second byte is always 0x00. Following those bytes there is an array +of records. This array has a full size of 29 bytes distributed according to the +following table. +Offset Size Type Name Description +0 +25 text +Player name +25 +2 US 1 +Remaining minutes +27 +2 US 1 +Remaining ticks +Table 25: HOF blocks +1 similar to SAV format + +28 + + In case there is no record, the 29 bytes spaces must be filled with zeros in +order to complete the whole file and give it the size of 2 + 29 ∗ 6 = 176. + +8 + +Credits + +This document +Writing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Enrique Calot +Corrections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Patrik Jakobsson +Hubai Tamas +Reverse Engineering +Indexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Enrique Calot +Levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Enrique Calot +Brendon James +MAC Levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dongsoo Lim +Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tammo Jan Dijkema +RLE Compression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tammo Jan Dijkema +LZG Compression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anke Balderer +Diego Essaya +Sounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Christian Lundheim +Palettes and Speaker Sounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . David +PLV v1.0 +Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Brendon James +Enrique Calot + +9 + +License + +c 2004 – 2008 The Princed Project Team Permission is granted to +Copyright ° +copy, distribute and/or modify this document under the terms of the GNU Free +Documentation License, Version 1.2 or any later version published by the Free +Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no +Back-Cover Texts. + +Indexes +List of Tables +1 +2 +3 +4 + +DAT file blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . +Image headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +Algorithm codes . . . . . . . . . . . . . . . . . . . . . . . . . . . +DAT 1.0 Palette blocks . . . . . . . . . . . . . . . . . . . . . . . + +29 + +5 +6 +6 +9 + + 5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 + +EGA and CGA palettes . . . . . . . . . . . . . . . . . . . . . . . +DAT 1.0 Level blocks . . . . . . . . . . . . . . . . . . . . . . . . . +POP1 Foretable codes . . . . . . . . . . . . . . . . . . . . . . . . +Background modifiers by group . . . . . . . . . . . . . . . . . . . +Stone modifiers on seed position . . . . . . . . . . . . . . . . . . +First 20 seed values of the second row separator . . . . . . . . . . +Default Guard colours . . . . . . . . . . . . . . . . . . . . . . . . +Wave Specifications . . . . . . . . . . . . . . . . . . . . . . . . . . +DAT file blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . +DAT 1.0 Level blocks for Mac . . . . . . . . . . . . . . . . . . . . +High data structures . . . . . . . . . . . . . . . . . . . . . . . . . +DAT 2.0 Master index . . . . . . . . . . . . . . . . . . . . . . . . +Slave Index ID strings . . . . . . . . . . . . . . . . . . . . . . . . +DAT 2.0 Level blocks . . . . . . . . . . . . . . . . . . . . . . . . . +POP2 Foretable Codes . . . . . . . . . . . . . . . . . . . . . . . . +High data structures . . . . . . . . . . . . . . . . . . . . . . . . . +High data structures . . . . . . . . . . . . . . . . . . . . . . . . . +PLV blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +PLV Mandatory Fields . . . . . . . . . . . . . . . . . . . . . . . . +SAV blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +HOF blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + +9 +10 +12 +14 +15 +15 +16 +18 +18 +19 +19 +20 +21 +22 +24 +25 +26 +27 +27 +28 +28 + +List of Figures +1 + +Distribution of the sindow size. . . . . . . . . . . . . . . . . . . . + +30 + +8 + + \ No newline at end of file diff --git a/applications/SprPoP/docs/README.md b/applications/SprPoP/docs/README.md new file mode 100644 index 0000000..e27a301 --- /dev/null +++ b/applications/SprPoP/docs/README.md @@ -0,0 +1,69 @@ +# `SprPoP/docs` — индекс + +Актуальность на 2026-08-26 (перенос из `applications/PoP/roomtest`). +Сюда взято только то, что ещё читают. Исполненные планы остались архивом +в `../../PoP/docs/`, закрытые задачи и баги — в `../../PoP/roomtest/`. + +## Входные точки + +| Документ | О чём | +|----------|-------| +| [`TASKS_OPEN.md`](TASKS_OPEN.md) | **Что берётся в работу сейчас** — начинать отсюда | +| [`BUGS_OPEN.md`](BUGS_OPEN.md) | Открытые баги и незакрытые оптимизации | +| [`impl_diff.md`](impl_diff.md) | **Осознанные расхождения с SDLPoP**: где сделано не дословно и почему. Новое расхождение — записью сюда, а не только комментарием в коде | +| [`keys.txt`](keys.txt) | **Целевая раскладка управления**, к которой подгоняем SprPoP | + +## Производительность + +| Документ | О чём | +|----------|-------| +| [`perf_registry.md`](perf_registry.md) | **Реестр оптимизаций**: всё отложенное одним списком | +| [`perf_backlog.md`](perf_backlog.md) | Отложенная оптимизация отрисовки + **как мерить** (wait-state'ы, границы кадра) | +| [`perf_green_phase.md`](perf_green_phase.md) | ЗЕЛЁНАЯ фаза (слой фона): раскладка тактов, способы ускорения G1..G6, журнал правок | +| [`perf_cyan_phase.md`](perf_cyan_phase.md) | ЦИАН фаза (персонажи + передний слой): раскладка тактов, C1..C7, журнал правок | +| [`perf_l11_room15.md`](perf_l11_room15.md) | Сцена и замер: факел + чомпер + страж (ур.11 к.15) | +| [`perf_l13_room23.md`](perf_l13_room23.md) | Сцена и метод замера кадра: каскад плит (ур.13 к.23), зонды, канал `clog` | +| [`resident_budget.md`](resident_budget.md) | Бюджет резидента W1/W2: как мерить и как освобождать | +| [`layout_plan_v2.md`](layout_plan_v2.md) | Раскладка кода по окнам/банкам/страницам + замеры скорости отрисовки | + +## Движок и содержание игры + +| Документ | О чём | +|----------|-------| +| [`full_game_plan.md`](full_game_plan.md) | Полноценная игра: автомат состояний, title/intro, demo, таймер, cutscenes, ending, Hall of Fame | +| [`levels_plan.md`](levels_plan.md) | Уровни: загрузка, переходы, тайлсеты, читы | +| [`levels_12_15_plan.md`](levels_12_15_plan.md) | Уровни 12/13 (тень, Джафар, падающие плиты) + что такое 14/15 и 0 | +| [`midtable_analysis.md`](midtable_analysis.md) | Слои отрисовки: back/mid/fore и objtable в оригинале, чего стоит порт | +| [`room_model_plan.md`](room_model_plan.md) | `kid_room ≠ drawn_room` (straddle): сделан S1, остальное впереди | +| [`quicksave_plan.md`](quicksave_plan.md) | QuickSave/QuickLoad (реализовано): формат снимка `POPQ` v3 — справочник | +| [`menu_settings_plan.md`](menu_settings_plan.md) | Pause menu и Settings, `POP.CFG`, VANILLA и задел под ENHANCED | +| [`palette_plan.md`](palette_plan.md) | Карта всех 256 слотов палитры + механика fade | +| [`status_line_text.md`](status_line_text.md) | Строка HP как статус-строка: инвентаризация ВСЕХ текстов SDLPoP | +| [`sound_plan.md`](sound_plan.md) | Звук через CBL: разбор и архитектура | +| [`shadow_render.md`](shadow_render.md) | Вид Тени (OR+XOR) — отложено: почему XOR несовместим с прозрачностью `#FF` | +| [`roomnav_skip.md`](roomnav_skip.md) | Комнаты для отладочного телепорта `+`/`−`: какие пропускать и почему | +| [`host_tests_plan.md`](host_tests_plan.md) | Модульные тесты движка под ucsim_z80: два шва, регрессии, дифф против SDLPoP | + +## Справочники по оригиналу + +| Документ | О чём | +|----------|-------| +| [`PORT_PLAN.md`](PORT_PLAN.md) | Общая карта фаз со статусами; §6 модель движения, §10 режим памяти | +| [`KID_PLAN.md`](KID_PLAN.md) | Модель персонажа: `char_type`, `actions_*`, устройство `play_seq` | +| [`gates_spikes_plan.md`](gates_spikes_plan.md) | Раскладка объектов по комнатам, декод `LINKLOC`/`LINKMAP`, ссылки на seg-код | +| [`PoP/POP-DAT-FormatSpecifications.pdf`](PoP/POP-DAT-FormatSpecifications.pdf) | **Каноническая спецификация форматов `.DAT`** (грепаемая копия — `.txt`) | +| [`PoP/MSDOS_RESOURCE_FORMAT.md`](PoP/MSDOS_RESOURCE_FORMAT.md) | Наш разбор ресурсов MS-DOS-версии | +| [`PoP/APPLEII_RESOURCE_FORMAT.md`](PoP/APPLEII_RESOURCE_FORMAT.md) | Наш разбор ресурсов Apple II | + +## Заделы + +| Документ | О чём | +|----------|-------| +| [`ideas_backlog.md`](ideas_backlog.md) | Осознанно отложенные гипотезы (мышь, PRNG) | +| [`prng_alternatives.md`](prng_alternatives.md) | Запасные генераторы, если упрёмся в бюджет кадра | + +## Не переносилось + +Исполнено целиком, лежит в `../../PoP/docs/`: `frame_pacing_plan.md` +(фиксированный логический кадр), `l9_invert_plan.md` (зелье переворота), +`shadow_atlas_plan.md` (запечка атласа Тени). diff --git a/applications/SprPoP/docs/TASKS_OPEN.md b/applications/SprPoP/docs/TASKS_OPEN.md new file mode 100644 index 0000000..0408060 --- /dev/null +++ b/applications/SprPoP/docs/TASKS_OPEN.md @@ -0,0 +1,1511 @@ +# SprPoP — доска ОТКРЫТЫХ задач (обновлено 2026-08-11) + +Что берём в работу сейчас и в каком порядке. Каждая запись: что сделать, +почему именно сейчас, чем подтверждать результат. + +- закрытые задачи с протоколами и замерами — [`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md); +- открытые баги — [`BUGS_OPEN.md`](BUGS_OPEN.md), закрытые с разбором корней — + [`BUGS_CLOSED.md`](../../PoP/roomtest/BUGS_CLOSED.md); +- планы фаз — `../docs/PORT_PLAN.md`, `../docs/layout_plan_v2.md`, + `../docs/levels_plan.md`. + +**Состояние на 2026-08-11 (сверено с кодом, не только с доской):** + +- **уровни 1-4 приняты smoke-тестами** (пользователь). Полные обходы всех + комнат делаются по готовности ВСЕХ уровней — политика приёмок в + [`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md#pass-policy); отдельных задач + `L3-PASS`/`L4-PASS` больше нет. Уже сделанные полные обходы уровней 1 и 2 + остаются регресс-базой; +- **[L4-MIRROR](TASKS_CLOSED.md#l4-mirror) закрыта**: зеркало, отражение, + прыжок сквозь него и рождение тени проверены в MAME. Хвосты — вид тени + (отложен, [`../docs/shadow_render.md`](shadow_render.md)) и + [MIRROR-FG-STALE](BUGS_CLOSED.md#mirror-fg-stale) (закрыт как не баг); +- **[L3-CHOMP](TASKS_CLOSED.md#l3-chomp) и [L3-SKEL](TASKS_CLOSED.md#l3-skel) + закрыты** (2026-08-08 / 2026-08-07); +- **тайлсет palace сделан** — `pop_bg_load(type)`, `pal_*.atl`, дворцовая + кладка `wall_pattern`, решётчатые тайлы 25-29 и в `tile_table`, и в + коллизии (`tile_is_floor` совпадает с seg006:0628). То есть шаг 2 + `levels_plan.md` закрыт; +- **libbgi:** блочные AND/OR/XOR/NOT акселератора (2026-08-11, `tests/accop`) + — задел под вид тени и под любые эффекты «поверх того, что уже нарисовано». + +**Обход уровней после оптимизации фаз — идёт сейчас (начат 2026-08-17).** +Оптимизация зелёной/циан фаз тронула порядок пометок и перерисовок, поэтому +уровни проходятся заново подряд: + +| уровень | статус | что нашлось и закрыто по дороге | +|---|---|---| +| 1 | **принят** (2026-08-17) | мерцание торцов плит/полов/кнопок (запечка шире копируемого прямоугольника); потолочный fore поверх падающей плиты (не было ряда −1 в проходе foretable); соседний пол под плитой (потерянная половина `draw_mob` — `set_redraw2`); залипший блеск меча (`trob_drawn` не совпадал с тем, что нарисует полная отрисовка) | +| 2 | **принят** (2026-08-17) | чёрные бары поверх стража — `bar` с `x + w > 320` заворачивался в соседнюю страницу видеопамяти (`dd40c24`); [BUG-CHEAT-IMM-1](BUGS_CLOSED.md#bug-cheat-imm-1) — боевая стойка без меча под читом бессмертия | +| 3 | **принят** (2026-08-17) | критичных багов не найдено — правок не потребовалось | +| 4 | **предварительно пройден** (2026-08-17) | [LEVELDOOR-PALACE-CLIP](BUGS_CLOSED.md#leveldoor-palace-clip) — во дворце проём двери уровня на 8 px шире, и контур Кида в анимации ухода обрезался раньше времени (`89b603a`) | +| 5 | **предварительно пройден** (2026-08-18) | багов не найдено | +| 6 | **предварительно пройден** (2026-08-18) | [SHADOW-STALE-FRAME](BUGS_CLOSED.md#shadow-stale-frame) — тень залипала на страницах дабл-буфера в РАЗНЫХ позах (кадр брался из кэша, снимок писался по `Guard.frame`, `085a198`); [BLUELINE-NOBLUE](BUGS_CLOSED.md#blueline-noblue) — кусок синего узора на стене: модификатор стены не переводился при загрузке (`af4e644`) | +| 7 | **предварительно пройден** (2026-08-18) | [SPIKE-BAKED](BUGS_CLOSED.md#spike-baked) — выдвинутые пики консервировались в фон запечкой соседней кнопки и оставались навсегда (`980d48c`); [DIED-ON-BUTTON](BUGS_CLOSED.md#died-on-button) — смерть на кнопке не ломала её насовсем: до `check_press` труп не доходил, самой `died_on_button` не было, а таймер связи переживал респавн (`6feab5d`) | +| 8 | **предварительно пройден** (2026-08-18) | [GUARD-RESPAWN-COL0](BUGS_CLOSED.md#guard-respawn-col0) — при возврате в комнату страж телепортировался в колонку 0 и падал насмерть: колонку несёт `guards_x`, а не тайл (`c40ae3f`); [SEAM-FIGHT-FLICKER](BUGS_CLOSED.md#seam-fight-flicker) — бой у шва перерисовывал комнату туда-обратно: `leave_room` запрещает уход ещё и на кадрах меча (`a498255`, сверено с SDLPoP покадрово) | +| 9 | **предварительно пройден** (2026-08-18) | багов не найдено | +| 10 | **предварительно пройден** (2026-08-18) | багов не найдено | +| 11 | **предварительно пройден** (2026-08-18) | [MOB-NEIGHBOUR-ROOM](BUGS_CLOSED.md#mob-neighbour-room) — плиты нижнего ряда пропадали без кадров падения (кусок в соседней комнате не рисовался, `ed5615a`); порядок куска относительно соседней плиты и Кида — порт корзины 30 (`4db60c7`) | +| 12 | **предварительно пройден** (2026-08-18) | багов не найдено | +| 13+ | не начат | | + +«Предварительно пройден» ≠ «принят»: уровни 4-6 пройдены прогоном по +сценарию, а не обходом ВСЕХ комнат — полные обходы делаются по готовности +всех уровней ([политика приёмок](TASKS_CLOSED.md#pass-policy)). +Регресс-базой остаются полные обходы уровней 1 и 2. + +Критичных багов на уровнях 1-6 не осталось. Следом — регресс тактов на +23/13 (документы фаз: [`../docs/perf_green_phase.md`](perf_green_phase.md), +[`../docs/perf_cyan_phase.md`](perf_cyan_phase.md), сцена и метод — +[`../docs/perf_l13_room23.md`](perf_l13_room23.md)). + +Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга; +диагноз платформы подтверждать артефактом (брейкпоинт/дамп/.asm), а не +гипотезой (memory `defer_unexplained_quirks`). + +--- + +## ТЕКУЩАЯ ЦЕЛЬ: уровень 8 + +Уровни 1-7 играются (smoke; зелья уровней 2 и 7 приняты пользователем +2026-08-12). Уровень 8 **новых тайлов не приносит вовсе** (инвентарь +`res2008.bin` целиком покрыт уровнями 1-7), тайлсет — подземелье. +Спецсобытие уровня — мышь, [L8-MOUSE](TASKS_CLOSED.md#l8-mouse) закрыта +2026-08-12 (хост-набор `t_mouse` + живая проверка в MAME). + +Остаётся smoke-прогон самого уровня: он длинный и с возвратом через два +чомпера комнаты 4, а стражи там сильнее прежних (комната 5 — skill **7**, +это максимум из встречавшихся; 12/22/24 — skill 2/3/3). + + +### L9-INVERT. Зелье ПЕРЕВОРОТА — ЗАКРЫТА 2026-08-26 + +> Проверено пользователем в живой игре. + + +> **Подробный план реализации — [`../docs/l9_invert_plan.md`](../../PoP/docs/l9_invert_plan.md)** +> (задачи libbgi, задачи приложения, порядок работ, риски, открытые вопросы). +> Ниже — механика и обоснование решений; план исполняется по документу. + +Единственное новое на уровнях 9-11: зелья типа 4 на `(1,7)` комнаты 7 и +`(0,4)` комнаты 10 (сверено по `res2009.bin`). Уровни 10 и 11 не приносят ни +тайлов, ни спецсобытий — только дворцовый тайлсет (есть) и стражи skill 3-4. + +Механика оригинала (seg000:15E9 `toggle_upside`, seg006:1886): +`upside_down = ~upside_down`, снимается смертью Кида (seg000:1224, при +`alive >= 0`) и стартом уровня (seg003:38/188); второе зелье переворачивает +обратно. Управление НЕ инвертируется. Переворот у оригинала — чисто вывод: +`flip_screen(offscreen)` перед копированием прямоугольников на экран и обратно +после (seg000:939/946); полоса HP рисуется прямо на экран (y = 194) и НЕ +переворачивается. + +**Приём оригинала — переворачивать буфер на выводе КАЖДЫЙ кадр — нам не +подходит:** страниц ровно две (`gfx_set_visible_page` → ESTEX $54 SELPAGE, +бит 0), рабочей третьей нет, а переворот в видимую страницу порвёт картинку +(проход не влезает в vblank). Значит переворачиваем ОДИН РАЗ в момент смены +флага, а дальше рисуем зеркально: +`y' = POP_YOFF + POP_PLAYFIELD_H - (y - POP_YOFF) - h` плюс вертикальное +зеркало самого спрайта. + +#### Момент переключения — accel-копия экрана (ОСНОВНОЙ ВАРИАНТ) + +Решение пользователя 2026-08-12. Уже нарисованное переворачиваем не +перерисовкой комнаты, а построчной копией акселератора: + +1. **проход A → B с реверсом Y**: строка `y` читается, пишется в `191-y` + (смена Port_Y между burst-чтением и burst-записью). 320 байт в один burst + не лезут — две скобки на строку; +2. **проход B → A без реверса**: вторая страница дабл-буфера получает то же + изображение. + +Почему это работает на нашем железе, а не только на бумаге: **accel читает +ОЗУ-КОПИЮ, а не VRAM** (memory `accel_block_ops`), то есть копируется ЧИСТЫЙ +фон без персонажей, а запись банком `GFX_BANK_NORMAL` идёт и в видео, и в +ОЗУ-копию приёмника. После двух проходов обе страницы имеют перевёрнутый фон +в обеих плоскостях — **heal не меняется ни на строку**, он и дальше +восстанавливает правильный фон по фактическим координатам отрисовки. + +Бюджет: 192 строки × 2 burst'а × 2 прохода = 768 burst-строк. По нашим +замерам строка-burst стоит ~300-530Т (`gfx_heal_noclip` 22×22 = 11 658Т → +530Т/строку; у заливки линия 51-53Т, но там нет данных) — итого +**230-410 К тактов, то есть 0.2-0.3 логического кадра** (логический = 3 +растровых ≈ 1.29 млн). Перерисовка комнаты в обе страницы стоила бы под +миллион тактов И требовала бы vflip-версии тайлового блита — здесь она не +нужна вовсе. + +Что дописать в libbgi: `_bgi_copy_rows_raw` умеет только ИНКРЕМЕНТ Port_Y на +строку (`y0` + шаг вперёд, страйды патчатся SMC) — нужен вариант с +декрементом одной стороны (параметр «шаг Y» либо отдельное ядро +`_bgi_flip_rows_raw`). Перед реализацией подтвердить ДАМПОМ, что обе +страницы одновременно адресуемы в W3: `gfx.h` говорит, что страница 1 +начинается на 320 байт дальше (0xC140), но это комментарий, а не проверка +(memory `defer_unexplained_quirks`). + +Зеркало по вертикали стоит по-разному в зависимости от раскладки спрайта: + +- **row-major** (весь слой фона: тайлы, кладка, факелы, зелья, обломки, + двери) — бесплатно: внешний цикл идёт по строкам источника снизу вверх, сама + строка копируется вперёд. В libbgi такого варианта СЕЙЧАС НЕТ (флип есть + только у `gfx_blit_cols*`, и он горизонтальный) — нужна пара + `gfx_blit*_vflip`; +- **column-major** (Кид, стражи, мышь, меч — 34 атласа, 228 563 Б) — реверс + ВНУТРИ колонки, а accel копирует блок только вперёд. Нужны перевёрнутые + копии в отдельных EMM-страницах (реверс через стек `pop`/`push`, ~10 тактов + на байт). + +**Когда делать копии — решать при реализации,** варианты в порядке +предпочтения: + +1. **ленивый постраничный кэш**: страница атласа (8 кадров, 3-10 КБ) + переворачивается при первом обращении в перевёрнутом режиме — ~0.2-0.4 + растрового кадра на страницу, размазано по времени; реально нужны 5-10 + страниц из 34, и если зелье не выпито, не тратится ничего; +2. на входе в уровень, где в данных есть зелье типа 4 (скан `bg` уровня) — + вся пачка разом, ~5-6 млн тактов ≈ 0.3 с, пауза прячется в загрузку; +3. офлайн в `pop_pack_kid.py`/`pop_pack_guard.py` — рантайм ноль, но +228 КБ + на диске и +34 страницы всегда, а на дискете это заметная загрузка. + +Не забыть при реализации: зеркалить надо и heal-прямоугольники, и футпринт +fore-слоя, и клип (`clip_char`), и брызги; полоса HP и лейбл комнаты остаются +как есть (они вне поля 192). + + +### UI-SPRITES. Деления HP — из атласов персонажей в константы резидента + +`pop_kid_img_blit` (**334 Б в W1/W2**) существует ради двух картинок 6×5: +полосе HP нужен блит по image id из кидовских атласов, а те column-major, и +обычным `gfx_blit_noclip` их не нарисовать. Деления Кида (images 216/217, +`kid27.atl`) и стража (idx 0 в `g0.atl`) — по 30 байт каждое. + +**Решение пользователя 2026-08-12: положить их const-массивами в РЕЗИДЕНТ** +(~68 Б данных), а не подселять в фоновый атлас: резидент всегда ниже +0xC000, значит `gfx_blit*` их видит (буфер обязан быть вне W3), EMM-страница +не тратится, упаковщики не трогаются. Чистая экономия ~266 Б. + +Побочный плюс для [L9-INVERT](#l9-invert): страницы `kid27` и `g0` сейчас +смешанные — деления HP (борт, не переворачиваются) лежат рядом с брызгами +крови 28×26 (поле, переворачиваются). После переноса обе страницы +становятся чисто «полевыми», и постраничный кэш перевёрнутых кадров получается +однородным, без спрайтов-исключений. + + +### MEM-COLD2. Второй шаг разгрузки резидента + +> **Пункт 2 сделан 2026-08-12**: `guard_over_kid` с хелперами +> (`objtile_at_char` / `char_x_left_of` / `tile_div_mod`) уехал в банк 8 — +> куча 1005 → **1574 Б**. Зовётся раз в кадр, то есть цена — один трамплин. +> Остались пункты 1 (`enter_room_side`) и 3 (блок редроя шва). + +После [MEM-COLD1](TASKS_CLOSED.md#mem-cold1) куча 1707 Б. Когда упрётся +снова, в банк 8 переезжают следующие кандидаты (в порядке выгоды): + +1. `enter_room_side` — самая большая функция `sprpop.c`, зовётся раз на + комнату. Требует вынести наружу рабочие массивы комнаты + (`room_bg`/`lcol_*`/`rcol_*`/`below_fg`/`above_*`) — сами данные останутся + в `_DATA`, уедет только код; +2. `guard_over_kid` + `objtile_at_char`/`char_x_left_of`/`tile_div_mod` — + зовётся КАЖДЫЙ кадр, но ровно один раз: цена выноса — один трамплин; +3. блок редроя левого шва в `main` (`seam_row_sig` + разбор сигнатуры). + +Если и этого не хватит — следующим уходит сам кадровый порядок отрисовки, +но тогда уже стоит мерить, а не выносить наугад. + +--- + +## ПРЕДЫДУЩАЯ ЦЕЛЬ: уровень 5 + +Уровни 1-4 играются (smoke). Дальше идём по порядку уровней; уровень 5 — +следующий. + +**Хорошая новость по ассетам: уровень 5 не приносит НИ ОДНОГО нового тайла.** +Инвентарь, снятый перебором `res2005.bin` (fg & 0x1F): + +``` +ур. 5: empty, floor, spike, pillar, gate, closer, doortop_with_floor(7), + bigpillar_bottom(8), bigpillar_top(9), potion, loose, doortop(12), + debris, opener, level_door L/R, chomper(18), torch, wall, + lattice_pillar(25)…lattice_right(29) +``` + +— всё это уже встречалось на уровнях 1-4 и портировано. Единственное новое +на уровне 5 — **спецсобытие «тень крадёт зелье»**. + +| # | Задача | Что | Блокирует | +|---|--------|-----|-----------| +| — | [DRAW-COST](#draw-cost) | **кадр уложился в бюджет 2026-08-09**: 470 964 -> 425 600 тактов, период цикла 4 растровых кадра -> **3**. Дальнейшее — запас, не срочность | плавность на ВСЕХ уровнях | +| ✔ | [L1-SPEED](#l1-speed) | **закрыта 2026-08-19** режимами скорости (NORMAL = 4/5 растров, как оригинал) | — | +| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности | +| — | [TUNE-2](#tune-2) | параметры СТРАЖЕЙ (6 таблиц по 12 градаций мастерства + HP) → cfg-файл | подбор сложности боя без пересборки | +| — | [SND](#snd) | звук: эффекты через CBL, музыка на AY | разбор готов — `../docs/sound_plan.md` | +| — | [MEM](#mem-next) | следующий шаг разгрузки W1/W2 | берётся по факту нехватки места | + +Сделанное — в [`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md): L4-MIRROR, L3-CHOMP, +L3-SKEL, L3-CHKP, L2 (машинерия уровней), L2-PASS, L1-PASS, DRAW-CHAR, +MEM-BANK2, MEM-BANK5, CLIP-1, KBD-1, DBG-CHEATS. + +--- + +## Ждёт ФИНАЛЬНОЙ приёмки (полные обходы по готовности всех уровней) + +Сюда попадает то, что уже работает в проверочном прогоне, но должно быть +подтверждено на сквозных прогонах уровней — потому что задевает механику +шире, чем собственный сценарий. + +- **Зацеп ПРЯМО В ПРЫЖКЕ** (`POP_ENABLE_JUMP_GRAB`, `pop_tune.h`, сделан + 2026-08-06, предварительно проверен пользователем). Почему нужен именно + финальный прогон: точки вызова стоят не только в `check_action`, но и в + ОБЕИХ ветках `check_bumped` — то есть код вклинивается перед обычным + ударом о стену. Регрессия проявится не в самом зацепе, а рядом: удар о + стену с зажатым Shift, осторожный шаг у стены, отскок в прыжке. На + уровнях 1–3 это надо специально потрогать в паре мест каждого уровня. + Напоминание: в ВАНИЛИ этого зацепа нет (у SDLPoP — `enable_jump_grab`), + так что сверять его с оригиналом «как есть» нельзя — только с SDLPoP при + включённых enhancements. + Прогон 2026-08-07 (уровни 1 и 2) регрессий рядом не показал, но специально + на удар о стену с Shift не проверялся. + +## P0 — делаем сейчас + +### CHAR-PARTIAL-REDRAW. Неподвижный персонаж перерисовывается зря: метка «фон трогали» слишком груба — ОБЯЗАТЕЛЬНО + +**Постановка (пользователь, 2026-08-19).** Проверять, нужна ли отрисовка +стража, когда он НЕ ДВИГАЕТСЯ. Если движется — лишние ~150 000 тактов +приемлемы: в оригинале во время боя число физических кадров на логический +тоже растёт на единицу. + +**Измерено** (сцена 11/15, чтение `pop_cd` из памяти машины): + +| объект | x | y | +|---|---|---| +| страж, спрайт | 257..284 | 18..56 | +| страж, клинок (накладной) | 241..261 | 31..37 | +| пламя факела (колонка 6 → рисуется в ячейке 7) | 232..247 | 5..22 | + +**Физического перекрытия НЕТ.** По x клинок и пламя пересекаются +(241..247), но по y расходятся: пламя кончается на 22, клинок начинается с +31 — девять пикселей чистого зазора. Пользователь увидел это на экране +раньше, чем я в числах: «страж полностью правее пламени, только меч может +пересекаться». + +**Почему тогда он перерисовывается.** Метка «фон трогали» (`pop_cd_touch` +в `pop_tile.c`) хранится как битовая маска КОЛОНОК по 32 px, отдельно на +каждый из ТРЁХ рядов по 63 px (`cd_row_of`). Пламя и клинок попадают в +один ряд 0 и в одну колонку 7 — и `cd_quiet` считает слот задетым. +Расплата: **148 302 такта, 23 % работы кадра** (85 524 спрайт с клинком и +снимком + 62 778 fore-проход) за перекрытие, которого нет. + +**Решение — поднять вертикальную точность метки.** Вместо «маска колонок × +3 ряда» хранить на каждую колонку ДИАПАЗОН y (`ymin`/`ymax`): 10 колонок × +2 байта × 2 страницы = 40 байт. `pop_cd_touch` расширяет диапазон +затронутых колонок, `pop_cd_hit` проверяет пересечение диапазонов. + +Проверка решения на обоих случаях сцены: + +- **страж:** клинок в колонках 7-8. В колонке 7 у метки лежит y 5..22 + (пламя), у клинка 31..37 — не пересекаются, колонку 8 пламя не трогало. + Слот остаётся «тихим» → экономия 148 302; +- **Кид в тяжёлой позиции:** пламя левого факела y 5..22, Кид y 15..55 — + пересечение НАСТОЯЩЕЕ, и он честно перерисовывается. Так и должно быть. + +Вариант с 8-пиксельными полосами вместо диапазонов тоже работает +(24 полосы), но 16-пиксельные УЖЕ НЕТ: пламя и клинок снова слипаются в +одной полосе. Диапазон на колонку точнее и не требует битовой возни. + +**Чего делать НЕ надо** (проверено расчётом, не повторять): частичную +перерисовку персонажа по пересечению и обрезку фона под ним. Обе правки +решают задачу, которой нет — перекрытия не существует. + +**Проверка:** замер 11/15 в обеих позициях Кида (лёгкая x = 99, тяжёлая +x = 106; разбор — [`../docs/perf_l11_room15.md`](perf_l11_room15.md) +§7), плюс визуальный прогон боя и прохода Кида под факелами: персонаж не +должен оставлять хвостов и не должен просвечивать сквозь пламя. + +**Место в очереди — [`../docs/perf_registry.md`](perf_registry.md), +позиция P15.** Смежное: P14 (fore-проход — вторая половина той же цены). + + +### HEAL-WIDTH. Ширина точечных heal'ов не совпадает со следом перерисовки — ОБЯЗАТЕЛЬНО + +**Постановка (пользователь, 2026-08-18).** Ширины heal'ов взяты «на глаз по +клеткам», а не по реальному следу. Сжатие до фактического следа даёт около +**10 % площади** самой дорогой операции акселератора; по оценке пользователя +5-6 % реального выигрыша. Задача обязательная. + +**Измерено** (каталоги атласов, спрайты плиты): + +| часть плиты | подземелье | дворец | +|---|---|---| +| верх (`41/69/70`) | 32 × 13-14 | 32 × 13-14 | +| правая грань (`42/71/72`) | **26** × 15-16 | **25** × 15-16 | + +То есть анимированный след ОДНОЙ плиты — `32 + 26 = 58` пикселей в +подземелье и `57` во дворце. + +**Что стоит сейчас.** В коде уживаются ДВЕ разные договорённости: + +- **60** = «свой тайл + свес соседа» — `pop_floor_bake` (там это записано + комментарием); +- **64** = «две клетки» — `pop_loose_shake_draw`, `pop_spike_redraw` + («ширина 64 = обе ячейки»). + +Для трясущейся плиты 64 избыточны: хватает 58 (подземелье) / 57 (дворец), +то есть **6 пикселей из 64 — впустую, 9,4 % площади heal'а**. + +**И обратная сторона, которую тоже надо разобрать.** Ни 60, ни 64 не +покрывают того, что перерисовка РИСУЕТ: два полных `draw_tile` красят до +**90** пикселей (32 свои + 32 соседа + 26 правой грани соседа, уходящей в +третью клетку). Видимого артефакта отсюда пока нет — содержимое третьей +клетки статично и перерисовывается одинаково, а трясущийся сосед накрывает +эту область своим heal'ом, — но несоответствие «heal уже, чем след» надо +либо закрыть, либо записать как осознанное. + +**Что сделать.** Свести ВСЕ точечные heal'ы (`pop_loose_shake_draw`, +`pop_spike_redraw`, `pop_chomp_redraw`, `pop_floor_bake`, `pop_loose_bake_empty`, +`pop_ceil_*`, коридор `mob_heal_*`) в таблицу «что рисует / что чистит» и +привести ширину каждого к фактическому следу. Ширины брать ИЗ АТЛАСА, а не +из номера клетки — они разные у двух тайлсетов (26 против 25). + +**Проверка:** замер 13/23 до/после (heal'ы — зелёная фаза) плюс визуальный +прогон комнат с плитами, пиками и чомперами: сужение heal'а — самый прямой +способ получить «недочищенный хвост». + +**Родня — [G8](perf_green_phase.md) (пометка соседа узкой полосой).** +Это две половины одной темы, и брать их логично вместе: G8 про ширину +ЗАПЕЧКИ соседнего тайла (60 вместо 28 нужных, да ещё `draw_tile` соседа +дважды на пометку), HEAL-WIDTH — про ширину HEAL'ов (64 вместо 58/57). +Числа там уже разобраны: 60 = 32 свой тайл + 28 собственный свес, и для +запечки САМОГО тайла 60 минимальны — сужать можно только пометку СОСЕДА. + +**Место в общей очереди — [`../docs/perf_registry.md`](perf_registry.md) +(позиция P8).** Там же собраны ВСЕ отложенные оптимизации из трёх фазовых +доков, отсортированные по измеренному эффекту, и разложена цена одного блита +(6 126 тактов фиксированной накладной на любой блит, независимо от размера). + + +### L12-SHADOW. Уровень 12: тень, бой, слияние — ЗАКРЫТА 2026-08-26 + +> Проверено пользователем в живой игре. + + +> **Статус 2026-08-13: код написан, хост-тесты зелёные (45 проверок, +> `tests-host/t_shadow.c`), ЖИВОЙ ПРОВЕРКИ В MAME ЕЩЁ НЕ БЫЛО.** Для неё +> нужна сборка с `FIRST_LEVEL=12` и полный рестарт MAME после `make hdd`. + +План и сверка констант с оригинальными данными — +[`../docs/levels_12_15_plan.md`](levels_12_15_plan.md) §1 и §7. +Сделано ровно по нему: + +| кусок | где | порт | +|---|---|---| +| подъём тени в комнате 15 (условие «меч уже подобран») | `guards.c` `pop_check_shadow` | seg002:0070 | +| `init_shad_12` + вход ПАДЕНИЕМ (`seq_7`) | `guards.c` | data:0EFA | +| ИИ тени: ждать Кида / драться / сойтись / слиться | `guards.c` `autocontrol_shadow_level12` | seg002:1184 | +| «ранил тень — ранил себя» | `pop_guard.c` `pop_do_delta_hp` | seg000:1518 | +| «убил тень — убил себя» | `guards.c` `pop_check_killed_shadow` | seg006:2033 | +| таймер вспышки слияния (0 → счётчик → −1) | `pop_guard.c` `pop_shadow_timer` | seg003:0735 | +| меч исчезает при уходе вправо из комнаты 18 | `guards.c` `pop_sword_disappears` | seg002:0536 | +| появление плит в комнатах 2/13 после слияния | `pop_map.c` `floors_appear` | seg006:1063 | +| бесшовный переход 12 → 13 (комната 23, без двери) | `sprpop.c` + `pop_start_level` | seg000:0900 | + +Осознанное расхождение: мигания Кида спрайтами тени во время вспышки нет — +запись в [`../docs/impl_diff.md`](impl_diff.md). + + +### LOOSE-SHAKE-RUNS. Дрожание плит: пометки по сменам кадра, а не по кадрам + +**Идея пользователя 2026-08-13, на этап полиша.** + +`pop_loose_tick` на КАЖДОМ кадре отсчёта ставит `pop_set_redraw(pos, +POP_RD_LOOSE, 1)` — полную перерисовку тайла, десять раз за отсчёт. А +кадр дрожания меняется не каждую фазу: таблицы +(`POP_LOOSE_FRAM_BOTTOM` = 43,73,43,74,74,43,43,43,74,74,74) дают + + фаза 1 2 3 4 5 6 7 8 9 10 + кадр 73 43 74 74 43 43 43 74 74 74 + ^^^^^ ^^^^^^^^ ^^^^^^^^ + парами и тройками одно и то же + +С фазы 5 кадры идут ТРОЙКАМИ. Значит вместо шести пометок с `pages = 1` +хватит двух — на фазах 5 и 8 — но с `pages = 2` (обе страницы дабл-буфера, +иначе на второй застынет старый спрайт и пойдёт мерцание через кадр). + +**Выигрыш ~20 % работы зелёного блока и ТОЛЬКО на дрожащих плитах.** +Наивный вариант «ставить лишь на смене кадра» выигрыша НЕ даёт: пять смен +по две страницы — те же десять перерисовок (проверено арифметикой +2026-08-13, до правки). + +Вопрос к этапу полиша: стоит ли неочевидный код такого узкого выигрыша. + + +### BG-ONCE. Задний фон рисуется ОДИН раз; вместо чёрных баров — heal + +**Предложение пользователя 2026-08-13. Не делать сейчас — обязательно к +проверке и реализации на этапе полиша/оптимизации.** + +Суть: элементы САМОГО заднего фона — факелы без пламени, окна, узор кладки, +решётчатая кладка и что ещё найдётся — впечатываются в фон один раз и больше +не перерисовываются никогда. Ни один объект за ними оказаться не может, а +значит и возвращать их поверх чего-либо не нужно. Если такой элемент оказался +затёрт, это САМО ПО СЕБЕ сигнал об ошибке: либо бар/прямоугольник слишком +велик, либо что-то рисуется не в своём слое. + +Вторая половина предложения — и она про скорость: там, где мы гасим область +чёрным баром перед выводом спрайта, звать вместо бара **heal по координатам +бара**. Бар всё равно приходится потом закрывать фоном, то есть выходит две +операции; heal делает то же одной и сразу правильным содержимым. + +**Оценка: идея верная по направлению, но есть два известных препятствия.** + +1. **Узкий heal уже пробовали — и откатили.** В `pop_room.c` (`pop_clip_sprite`) + записано: per-спрайтовый heal не используется, потому что выравнивание + блока акселератора оставляет 1-2 px слайверы правой грани, и на одной + странице дабл-буфера это мерцает. Поэтому борта чистит полоса + `pop_room_clip_borders` с гейтом по флагу. Прежде чем менять бары на heal, + надо решить именно эту задачу — иначе получим мерцание вместо ускорения. +2. **Полный запрет «не перерисовывать фон» противоречит foretable.** + `draw_tile_fore` (seg008:715) кладёт в передний слой ГЛАВНУЮ ГРАНЬ стены + вместе с её узором — она обязана перекрывать персонажа. То есть запрет + должен различать «декор задней стены» и «передняя грань», а это ровно + различие backtable/foretable оригинала. Фактически предложение сводится к + «привести наши слои в соответствие с таблицами оригинала» — тот же вывод, + к которому пришёл разбор `overlay_mid_tile` (см. ниже). + +**Известное исключение**, которое надо не сломать: Кид, уходящий по лестнице +в дверь уровня, действительно уезжает за передний план — но он и рисуется +отдельно (`clip_char`, правая грань doortop; см. memory `pop_clip_char_todo`). + +**Критерий приёмки этапа:** пройти уровни с падающими плитами (13) и с +факелами/окнами (10-12) и убедиться, что ни один фоновый элемент не +перерисовывается в кадре, где его тайл не менялся; замерить кадр до и после. + + +### L12-SHADOW-SPR. Тень дерётся спрайтами СТРАЖА, а не своими + +**Найдено прогоном 2026-08-13.** `tbl_guard_type[12] = 4` — это SHADOW.DAT, +отдельный набор спрайтов (силуэт Кида), а `pop_guard_load` (`pop_cdraw.c`) +разбирает только тип 2 (SKEL) и 3 (VIZIER); тип 4 сваливается в набор +обычного стража. Ассеты есть: `SDLPoP/data/SHADOW` (32 кадра + `res750.pal`). + +Правка по образцу VIZIER (набор в `pop_pack_guard.py`, каталог в Makefile, +ветка в `pop_guard_load`), НО сначала проверить две вещи, иначе спрайты +поедут: (1) какую таблицу кадров получает `CHARID_1_SHADOW` в +`pop_frame_tbl_is_guard` — Кида или стража, и (2) сколько кадров в наборе +(32 против 34 у стража) и не выходит ли индекс за его пределы. + +Это НЕ полный «вид тени» (OR/XOR-блиттеры, [`../docs/shadow_render.md`]) — +только правильный набор спрайтов вместо чужого. + + +### L12-SEAM-POLISH. Переход 12 -> 13 выглядит коряво + +Механика работает (комната 23, без заставки и без сброса HP), но сама +анимация прохода рваная — замечено пользователем 2026-08-13. Полишинг, +берётся вместе с остальной шлифовкой переходов. + + +### L13-JAFFAR. Уровень 13: Джафар, победа, выход — ЗАКРЫТА 2026-08-26 + +> Проверено пользователем в живой игре. + + +> **Статус 2026-08-13: код написан, хост-тесты зелёные (44 проверки, +> `tests-host/t_jaffar.c`), ЖИВОЙ ПРОВЕРКИ В MAME ЕЩЁ НЕ БЫЛО.** + +Своего ИИ у Джафара нет — это обычный страж (seg002:085A), skill 9 и 6 HP уже +лежат в `pop_level_cold.c`. Сделаны только события и данные: + +| кусок | где | порт | +|---|---|---| +| фора после встречи (`guard_notice_timer`) | `guards.c` `pop_meet_jaffar` + `autocontrol_guard_inactive` | seg002:0544 / 0736 | +| победа: белая вспышка + «выход разрешён» | `guards.c` `on_guard_killed` | seg006:1927 | +| выход: кнопка комнаты 24 при уходе ВЛЕВО | `guards.c` `pop_jaffar_exit` | seg002:0517 | +| гряда плит сверху при входе в комнаты 23/16 | `pop_map.c` `pop_check_fall_flo` | seg000:1317 | +| фазу со старшим битом на 13-м НЕ гасить | `pop_map.c` `pop_loose_tick`, `pop_trob.c` `animate_loose` | seg007:823 | +| сотрясение на 13-м плиты НЕ трясёт | `pop_map.c` `do_knock` | seg007:949 | +| плита бьёт и В БЕГЕ | `pop_map.c` `fell_on_your_head` | seg007:1218 | +| СПРАЙТЫ Джафара (VIZIER.DAT, своя палитра) | `pop_pack_guard.py`, `pop_cdraw.c`, `Makefile` | seg000:1092 | + +По ходу нашлась и починена смежная ошибка: цвет стража из данных комнаты +применялся к ЛЮБОМУ набору, хотя оригинал берёт его только у обычного стража +(`curr_guard_color = 0` при `tbl_guard_type != 0`, seg002:183) — скелета это +не задевало случайно, Джафара покрасило бы. + +Осознанное расхождение (представление фазы отложенного старта) — запись в +[`../docs/impl_diff.md`](impl_diff.md). + +**Не портировано намеренно:** `is_show_time` в `on_guard_killed` — это показ +ОСТАВШЕГОСЯ ВРЕМЕНИ, а часов на 60 минут у нас пока нет вовсе. + +**Что проверять в MAME** (`make LEVEL=13`): + +1. комната 23 (стартовая): плиты сверху сыплются ВРАЗНОБОЙ с первых кадров, + и они бьют, даже если пробегать под ними; +2. приземление Кида плиты НЕ трясёт (иначе гряда посыплется разом); +3. уход вправо из комнаты 3: Джафар виден СВОИМИ спрайтами и своей палитрой, + ~2 секунды стоит, не доставая клинок; +4. смерть Джафара — белая вспышка; уход ВЛЕВО открывает дверь уровня. + +**Что проверять в MAME** (по порядку прохождения уровня): + +1. комната 15: пока меч лежит — тени нет; подобрал меч, вышел и вернулся — + тень СВАЛИВАЕТСЯ сверху, а не стоит готовая; +2. бой: каждый удар по тени отнимает HP и у Кида; добить её нельзя — на её + смерти умирает и Кид (белая вспышка); +3. слияние: убрать меч (`Shift`+вниз), подойти — белая вспышка, тень исчезла, + потолок HP вырос на единицу; +4. после слияния в комнатах 2 и 13 (верхний ряд) под ногами появляются плиты; +5. уход ВПРАВО из комнаты 18 убирает меч из комнаты 15 (вернуться и взять + второй раз нельзя); +6. комната 23 — уровень молча становится 13-м, HP НЕ сбрасывается. + + +### L7-FEATHER. Зелье МЕДЛЕННОГО ПАДЕНИЯ — ЗАКРЫТА 2026-08-26 + +> Проверено пользователем в живой игре. + + +> **Статус 2026-08-12: код написан, ждёт живой проверки в MAME.** Сделаны все +> шесть шагов ниже плюс синие пузырьки зелья «−HP» (тип 5) — они всплыли по +> ходу: пользователь напомнил, что такое зелье стоит уже на уровне 2 (комната +> 13, тайл `(1,3)`; ещё ур.8 комн.2 и весь ур.15), а цвет у него был красный. +> Хост-тесты: `phys_feather_fall_is_slow_and_harmless` (скорость ≤ 4 и HP +> целое против обычного падения с двух рядов). **Осталось**: проверить в MAME +> — ур.7 комн.1 (зелёные пузырьки, зелёная вспышка, медленный спуск в шахту) и +> ур.2 комн.13 (синие пузырьки, −1 HP, экран краснеет один раз). + +Зелье на `(2,8)` комнаты 1 уровня 7 (сверено по `res2007.bin`: modifier 3, то +есть `potion_type = 3` — «slow fall»). Сейчас `pop_proc_get_object` +(`pop_map.c`) для типов 3/4/6 не делает НИЧЕГО (явный TODO): зелье выпивается +без эффекта. + +Механика оригинала (прочитана в SDLPoP до планирования, правило проекта): + +| что | где в SDLPoP | значение | +|---|---|---| +| включение | `feather_fall()`, seg000:15F8 | `is_feather_fall = 1`, зелёная вспышка (`flash_color = 2`, `flash_time = 3`), `stop_sounds` + `sound_39_low_weight` | +| физика | `fall_accel()`, seg006:057C | ускорение **1** вместо 3, потолок скорости **4** вместо 33 (`FALLING_SPEED_*_FEATHER`, types.h:1435) | +| анимация | опкод `SEQ_JMP_IF_FEATHER`, seg006:586 | в seqtbl есть ветки `stepfloat` / `bumpfloat` — «плавные» кадры падения и удара; таблица у нас из данных оригинала, ветки УЖЕ ЛЕЖАТ в ней | +| длительность | `do_timers`, seg003:517 | ваниль: пока играет звук (или 225 тиков); фикс SDLPoP `fix_quicksave_during_feather` — таймер `FEATHER_FALL_LENGTH = 18.75 c` | +| сброс | seg003:189 (`start_level`) | на старте уровня и, по фиксу, при смерти Кида | +| вид склянки | `draw_tile_fore`, seg008:740 | типы 2..4 — БОЛЬШАЯ склянка (id 13) — **у нас уже так** | +| цвет пузырьков | `draw_tile_anim`, seg008:652 | типы 3/4 — **зелёный** (`color = 10`), 5/6 — синий, остальные — красный (12) | + +Шаги (в порядке выполнения, каждый проверяем отдельно): + +1. **Состояние.** `pop_feather` (счётчик кадров) в `pop_map.c` рядом с + `fall_accel`; экспорт в `pop_map.h` для `play_seq`. Длительность — по + таймеру (ванильная привязка к звуку нам не подходит: звука нет), значение + пересчитать из 18,75 с в наши кадры и завести в `pop_tune.h`. Сброс — в + `pop_start_level` и по смерти Кида. +2. **Физика.** `fall_accel()`: при `pop_feather` — `FALL_ACCEL_FEATHER 1` / + `FALL_MAX_FEATHER 4`. Только для Кида (`charid == CHARID_0_KID`) — так + правильнее по смыслу, но это ОСОЗНАННОЕ расхождение с ванилью (там эффект + ловят все, и SDLPoP чинит это опцией) → запись в `../docs/impl_diff.md`. +3. **Анимация.** Опкод `0xF7` в `play_seq` (`pop_kid.c`) сейчас БЕЗУСЛОВНО + пропускает адрес; сделать как в оригинале: при `pop_feather` — прыжок по + адресу (это и даёт `stepfloat`/`bumpfloat`, то есть отсутствие урона и + «парение»). Правка на 3 строки, но именно она даёт весь визуальный эффект + падения. +4. **Вспышка.** Сейчас цвет вспышки — булев `pop_flash_red` (жёлтая/красная); + расширить до кода цвета и добавить ЗЕЛЁНУЮ (`flash_time = 3`). +5. **Зелёные пузырьки.** `pop_pack_bg.py` уже красит пузырёк mono-цветом + (`POT_BUBBLE_COLOR = VGA16 + 12`, красный). Добавить второй набор кадров + 16..22 в зелёном (`+10`) под своими id в атласе `pop_pot` и выбирать набор + по `potion_type` в `pop_potion_draw` (`pop_room.c`): 3/4 — зелёный, 5/6 — + синий, иначе красный. Цена — 7 маленьких спрайтов. +6. **Проверка.** Хост-тест: падение с трёх рядов под пером — скорость не выше + 4, урона нет, Кид жив (сцена в `t_phys`/`t_char`). MAME: комната 1 уровня + 7 — выпить, спрыгнуть в шахту, убедиться в плавном спуске и в том, что + эффект кончается по таймеру. + +### L3-COLOR. Палитра КЛАДКИ уровня 3 (в оригинале он зелёный) + +**Наблюдение (пользователь, 2026-08-07, со сравнением карт VGA).** В +оригинальной VGA-версии кладка уровня 3 ЗЕЛЁНАЯ, а уровней 1-2 — серо-синяя. +В SDLPoP все подземелья одинаковые, поэтому по нему разницу не увидеть. + +**Почему в SDLPoP её нет — проверено, не гипотеза.** Механизм там ЕСТЬ +(seg000:1140, «Level colors (1.3)»): + +```c +int level_color = custom->tbl_level_color[current_level]; +if (level_color != 0) { + byte* env_pal = level_var_palettes + 0x30*(level_color-1); + byte* wall_pal = env_pal + 0x30 * custom->tbl_level_type[current_level]; + set_pal_arr(0x50, 0x10, (rgb_type*)env_pal); /* chtab_6 environment */ + set_pal_arr(0x60, 0x10, (rgb_type*)wall_pal); /* chtab_7 wall */ +} +``` + +`tbl_level_color` (data.h:842) = `{0,0,0,1,0,0,0,1,2,2,0,0,3,3,4,0}` — у +уровня 3 цвет **1**, у 7 тоже 1, у 8/9 — 2, у 12/13 — 3, у 14 — 4. Но +`level_var_palettes` — это ресурс **20** из `PRINCE.DAT` (только версии +1.3/1.4), а в `SDLPoP/data/PRINCE/` его НЕТ: там лежит лишь `res10.bin` +(палитры стражей). Значит `level_var_palettes == NULL` и вся ветка молча +пропускается — отсюда одинаковые подземелья. + +**Данные у нас есть.** В `../MSDOS/PRINCE.DAT` ресурс 20 присутствует: +offset 22790, **240 байт** = 5 палитр × 16 цветов × 3 байта (6-битные +каналы, как res10). + +**Что делать (когда дойдём до вида уровня 3).** +1. Достать ресурс 20 из `MSDOS/PRINCE.DAT` (упаковщику придётся читать сам + `.DAT` — сейчас все скрипты берут распакованные PNG из SDLPoP); +2. сгенерировать таблицу палитр рядом с `pop_guard_pal.h`; +3. при загрузке уровня заливать слоты **0x50..0x5F** (env) и **0x60..0x6F** + (wall) — у нас ровно эти базы (`pop_pack_bg.load_indexed`: `pal_base = + 0x60` для WALL, `0x50` для env), то есть совпадение со `set_pal_arr` + один в один; +4. `wall_pal = env_pal + 0x30 * tbl_level_type[level]` — для подземелья + (`level_type == 0`) обе палитры одинаковые. + +**Грабли, уже пойманные на цвете стражей:** `gfx_pal_load` отдаёт указатель +в BIOS (`$A4` через `rst #0x08`), а BIOS читает только `#4000-#BFFF` — +таблицу нельзя передавать прямо из банка (0xC000+), надо копировать в +W1/W2 (см. [BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1)). + +### DRAW-COST. Кадр НЕ УКЛАДЫВАЕТСЯ в бюджет — нужна оптимизация + +> **ШАГ 1 СДЕЛАН 2026-08-08: пропуск неизменившегося персонажа.** Комната +> 1.3, труп стража, Кид стоит: было **210 %** кадрового периода, стало +> **116 %** (500 772 такта при бюджете 430 000). Отрисовка перестала быть +> узким местом: персонажи в покое не рисуются ВООБЩЕ (ноль вызовов +> `pop_heal_fast` за кадр), весь фон — ДВА блита факелов (44 136 тактов). +> Механизм и почему метка позиционная — в шапке `pop_cdraw.h`. + +**Исходные замеры пользователя (полосы бордюра, до шага 1).** Уровень 1, +Кид СТОИТ — то есть НЕ худший случай, ни боя, ни движения: + +| комната | синяя (ввод+heal+логика) | зелёная (фон) | циан (спрайты) | итого | +|---|---|---|---|---| +| 3, страж УБИТ | ~80 % | ~20 % | ~110 % | **~210 %** | +| 2, стража НЕТ | ~60 % | ~20 % | ~60 % | ~140 % | + +Разница ровно в теле убитого стража: +20 % синей и +50 % циана. Труп +сохраняет `charid != 0`, поэтому каждый кадр честно проходил весь путь +живого персонажа (heal → спрайт+clip_char+брызги+клинок → fore-проход), хотя +его кадр постоянен до выхода из комнаты. + +**Что сделано (шаг 1).** Не спецкейс «мёртвый», а общее правило: у каждой +страницы дабл-буфера свой снимок ВХОДОВ отрисовки слота; совпал снимок, +спрайт этой страницы цел и фон в его прямоугольнике не трогали — heal, блит +и fore-проход пропускаются целиком. Покрывает и труп, и стоящего Кида, и +ждущего стража. Детали контракта — `pop_cdraw.h`, реализация — +`pop_char_skip_mask` / `cd_quiet` в `pop_cdraw.c`, метка фона — +`pop_cd_touch` в резидентном `pop_tile.c`. + +Грабли, на которые наступили по дороге: сначала метка была ФЛАГОМ «фон +трогали хоть где-то» — и выигрыш оказался ровно нулевым, потому что факелы +анимируются каждый кадр и гасили пропуск для всех персонажей сразу (замер: +597 684 такта, как без оптимизации). Метка обязана быть ПОЗИЦИОННОЙ. + +**Остаточный эффект от объединения прямоугольников.** Метка одна на +страницу — объединение всех правок фона. В комнате 1 два факела дают +прямоугольник x 40…88, а стоящий там Кид занимает x 32…44: пересечение 4 px, +и он перерисовывается каждый кадр (140 % против 94 %, если отойти). Если +понадобится — держать не один прямоугольник, а 3-4 отдельных и мержить +только при переполнении. + +### Шаг 2 сделан частично: ЛОГИКА (синяя полоса) 2026-08-09 + +Пользователь: «на стоящем Киде с двумя факелами на логику уходит 60 % +кадрового периода — недопустимо». Разобрано брейкпоинтами в MAME +(`z80_profiling_method`), сцена: уровень 1 комната 1, Кид СТОИТ вплотную к +левому факелу (то есть пропуск персонажа НЕ срабатывает — худший случай). + +**Калибровка, которую надо знать заранее.** Один такт `totalcycles` в MAME +— НЕ один номинальный T-такт Z80: у Sprinter на обращениях к ОЗУ есть +wait-state'ы, и замеренная стоимость выходит **≈ 2,4× номинала** +(`get_tile`: 574 номинальных против 1 422 замеренных). Считать бюджет по +таблице T-тактов из справочника нельзя — только мерить. Кадр растра = +430 000; главный цикл спейсится тремя `gfx_wait_vsync`, поэтому работа +СВЫШЕ 430 000 стоит сразу целый лишний кадр. + +**Что нашли и починили:** + +| правка | что было | стало | +|---|---|---| +| окно перебора коллизии как в оригинале (было: все 14 колонок каждый кадр) | `check_collisions` 60 888 | 50 940 | +| `get_tile_div_mod` — таблицей (`tile_div_tbl`/`tile_mod_tbl`), было `/14` и `%14` | 5 400 тактов на вызов, 13 вызовов за кадр ≈ 70 000 = **16 % кадра** | ~250 на вызов | +| разрешение ряда вынесено из цикла колонок + грань шагом 14 + `wall_type` таблицей | `check_collisions` 58 026 | 42 750 | +| `move_coll_to_prev` — `memcpy` (LDIR) вместо цикла на C | 14 байт за 5 514 тактов (390 на байт!) | ~1 200 | +| окно режется на непрерывные пробеги (левый сосед / своя / правый), пролог ряда вынесен на кадр | `check_collisions` 44 022 | **38 334** | + +Замер одной итерации перебора: **пустая колонка 750 тактов, колонка-стена +~1 700** (две 16-битные знаковые сверки граней — SDCC пишет их через +`jp PO / xor 0x80 / jp P`). Ловушка, на которую наступили: «быстрый путь для +окна внутри комнаты» не срабатывал ПОЧТИ НИКОГДА — Кид, стоящий в колонке 0, +даёт окно с −1, и шёл медленный сбор во временный буфер с тернарником на +колонку (1 340 тактов на колонку). Отсюда разбиение на пробеги: вопрос «чья +это колонка» решается раз на пробег, а `coll_scan` сам двигает `scan_left`. + +Самое дорогое было НЕ там, где ожидалось: `/14` и `%14` SDCC разворачивает +в `__divsint` + `__modsint`, а `__modsint` внутри зовёт `__divsint` ещё раз — +два полноценных 16-битных деления на каждый вопрос «в какой колонке точка». +Оригинал делит таблицей (seg006:702) — мы просто не портировали это место. + +**ГЛАВНОЕ: логический кадр уложился в бюджет.** Главный цикл спейсится +тремя `gfx_wait_vsync`, поэтому работа сверх 430 000 тактов стоит СРАЗУ +целый лишний растровый кадр. Было 470 964 (период цикла 4 кадра), стало +**409 956 + ~15 600 на ввод = 425 600** — период цикла **3 растровых кадра**. +Игра стала быстрее на треть (16,7 логических кадров/с против 12,5). + +Что дало последние тысячи (по убыванию): + +| правка | экономия | +|---|---| +| `pop_y_to_row` — цепочка сравнений вместо `/63 % 4` | ~12 000 | +| расширение окна fore-прохода арифметикой вместо перебора 10 колонок и 3 рядов | ~8 500 | +| `col_from_x` — таблицей (те же `POP_TILE_DIV`, вынесены в резидент) | ~11 000 | +| `pop_cd_touch` — развёрнутый цикл по страницам, `x+w`/`y+h` один раз | ~8 600 (зовётся с каждого блита фона) | +| `tp / 10`, `tp % 10` у факелов — таблицей | ~4 000 | +| пустой слот соперника считается «тихим» | ~8 200 | + +**Ловушка SDCC, на которой я потерял один прогон:** в `pop_y_to_row` одно и +то же выражение `t / 63 % 4 - 1` стояло в двух ветках, и компилятор поднял +деление В ВЕРШИНУ функции — быстрые возвраты не спасали, `__divsint` звался +всё равно. Лечится выносом медленного хвоста в ОТДЕЛЬНУЮ функцию. Тот же +эффект уже был описан в `pop_loose_tick`; теперь ясно, что это правило, а не +частный случай: **любое деление, встречающееся дважды, SDCC поднимает выше +всех проверок.** + +Приём, которым это ловится: брейкпоинт на `__divsint`/`__divuint`/ +`__divuchar` с печатью адреса возврата (`printf "%04X", w@(sp)`) — сразу +видно, кто и сколько раз делит за кадр. + +**Хвост подобран 2026-08-10.** После переписи `pop_y_to_row` в pop_room.c +осталось ТРИ места, считавших `(y+60)/63 % 4 - 1` вручную (927 в +`mob_tick_one`, 976/977 в `mob_render`) — сгенерированный asm подтвердил +пару `__divsint`+`__modsint` в каждом. Это ~16 200 тактов (3,8 % кадра), но +только пока кусок плиты в полёте — то есть ровно в самом тяжёлом кадре. +Заменены вызовом `pop_y_to_row`; в банке 7 теперь НОЛЬ `__divsint`. +Эквивалентность закреплена тестом `geom_y_to_row_matches_formula` +(перебор −400..400 против исходной формулы) — вызовы разбросаны по трём +банкам, и соблазн написать деление «по месту» возвращается. + +### Полная инвентаризация делений 2026-08-10 + +Способ (повторяемый одной командой из `.sprinter-cc-SprPoP/`): + +``` +awk '/^;[a-z_0-9]+\.[ch]:[0-9]+:/{s=$0} /^\tcall\t__(div|mod|mul)/{printf "%-22s %-12s %s\n",FILENAME,$2,s}' *.asm +``` + +Найден **31 вызов в 9 модулях**. Прибрано три места, остальное разобрано и +осознанно оставлено. + +Убрано: + +| место | что было | почему стоило | +|---|---|---| +| `pop_cdraw.c` `calc_screen_x_coord` (2 вызова) | `x * 8 / 7` = `__divsint`, **2 400 тактов НА ПЕРСОНАЖА КАЖДЫЙ КАДР** | последнее деление в горячем пути; ~4 800/кадр при живом сопернике | +| `pop_guard.c` `guard_col_from_x` | `/14` + `%14` **безусловно**, мимо `POP_TILE_DIV` | единственное 16-битное деление, у которого таблица вообще не была подключена | +| `pop_trob.c` `animate_chomper` | `tp / 10` = `__divuchar` на чомпера каждый кадр | таблицы `TP_ROW`/`TP_COL` уже лежали в ЭТОМ ЖЕ файле, но ниже по тексту — чомперы их не видели | + +Приём для `×8/7`: **таблица** `SCRX7[1152]` (int8_t, хранит `x/7`) в банке 4, +индекс `x + 448`, результат `x + SCRX7[i]`. 1 152 байта, резидент не тронут. + +Два решения по дороге, оба проверены, а не угаданы: + +1. **Диапазон — весь, включая отрицательные.** Первая версия крыла 0..255 по + тождеству `8x/7 == x + x/7` с байтовой таблицей `x/7`. Ошибка: `obj_x = + 2*fwd − 116` уходит в минус, как только `fwd < 58` (левее `x_bump[5]`) — + то есть у ЛЕВОЙ КРОМКИ комнаты, а это не экзотика, и там мы продолжали + делить. Границы взяты из фактических данных: `kid_data.bin` даёт `dx` + кадров Кида −5..+10, стража −2..+10; при `Char.x` типа uint8_t и + `render_dx ∈ {−140, 0, +140}` полный диапазон `obj_x` = **−416..695**. + Таблица кроет −448..703, деление стало недостижимым (оставлено + страховкой на третьего персонажа / другой `render_dx`). +2. **Хранить `x/7` байтом, а не готовое `x*8/7` словом** — по тождеству + `8x/7 == x + x/7`. Первая версия хранила готовое, «раз всё равно + индексация двухбайтная». Собраны ОБА варианта, такты посчитаны по + сгенерированному asm: + + | вариант | хвост после проверки границ | тактов | байт | + |---|---|---|---| + | `int16_t` готовое | `add hl,hl / add hl,de / ld e,(hl) / inc hl / ld d,(hl)` | **132** | 2 304 | + | `int8_t` `x/7` | `add hl,bc / ld a,(hl) / ld l,a / rlca / sbc a,a / ld h,a / add hl,de` | **131** | 1 152 | + + Расширение знака и 16-битное сложение стоят ровно столько же, сколько + лишний `add hl,hl` при двухбайтном индексе, а обращений к памяти на одно + меньше — под wait-state'ами Sprinter (такт ≈ 2,4× номинала именно на + обращениях к ОЗУ) байтовый вариант ещё чуть выгоднее номинала. Итог: + байтовая таблица не хуже по скорости и на килобайт меньше. **Урок: + «двухбайтный индекс съест выигрыш» — гипотеза; она не подтвердилась.** + +Кодоген проверен глазами (`bank4_pop_cdraw.asm`): `ld hl,#0x01C0; add hl,de`, +16-битное беззнаковое сравнение — индекс полный, старший байт не теряется +(грабли `sdcc_z80_const_ptr_index_bug` обойдены отдельной +`uint16_t`-переменной). 131 такт номинала против ~1 000 у `__divsint`. + +Проверка таблицы: все 1 152 записи сверены питоном обратно из `.c`, а ПРАВИЛА +генерации (тождество + усечение к нулю) — тестом `geom_mul8div7_table_rules` +на целевом компиляторе: округляй SDCC к минус бесконечности, вся +отрицательная половина уехала бы на пиксель, и поймалось бы это только +глазами на левой кромке. + +Оставлено сознательно (НЕ трогать, это не забытые места): + +- **Хвосты за таблицей** — `guards.c:84`, `pop_bg.c:497`, `sprpop.c:163`, + `pop_map.c:412`: срабатывают только при x вне 0..255, то есть когда + персонаж в соседней комнате. Убирать их — это расширять `POP_TILE_DIV` до + −140..395 (+280 Б резидента) ради редкого пути. +- **`pop_geom.c:21`** — намеренный медленный хвост `pop_y_to_row` (см. выше + про подъём деления SDCC). +- **`pop_geom.c:52`** — `v % n` в `pop_rnd_fit` для не-степени двойки: + оригинал зовёт `prandom(1)/(255)/(0xFF)`, все три идут веткой с маской, + сюда управление не приходит вовсе. +- **Холодные, раз на комнату/уровень/событие**: `pop_guard.c:266/268/269` + (вход стража), `pop_map.c:310` (пробуждение скелета), `pop_trob.c` дверь + уровня, `sprpop.c:473/663/887` (читы и старт), `pop_level.c:131/132` + (имя файла уровня), `sprpop.c:976/977` (отладочный HUD номера комнаты). + Каждое — единицы вызовов за секунды игры; таблицы под них только раздули + бы код. + +Итог: **в горячем пути делений не осталось**. В банках 4, 6, 7 — ноль +`__div*`; всё, что видно в списке выше, либо за быстрым путём, либо холодное. + +### Замер в MAME: A/B со сборкой `ec3cca5` (до правок) + +Обе сборки прогнаны через полный цикл (`make hdd` → рестарт MAME → загрузка +уровня 1), сцена — **комната 1, Кид стоит, соперника нет**; скриншоты обеих +сборок идентичны. Фазы сняты брейкпоинтами на инструкциях `out +(_io_border), a` профилировочного бордюра, медиана по 60 логическим кадрам: + +| фаза | до | после | Δ | +|---|---|---|---| +| ввод + heal | 62 361 | 62 364 | +3 | +| логика | 79 878 | 79 878 | 0 | +| фон | 119 502 | 119 502 | 0 | +| **спрайты** | **129 568** | **127 510** | **−2 058** | +| РАБОТА за кадр | 391 258 | 389 221 | −2 037 | + +Счётчик делений (брейкпоинты на `__divsint`/`__modsint`/`__divuchar`/ +`__moduchar` с печатью адреса возврата): + +| сцена | до | после | +|---|---|---| +| комната 1, только Кид | **1,00 `__divsint`/кадр** (возврат 0xD3AA = `pop_cdraw`) | **0** | +| комната 3, бой со стражем | **2,01 `__divsint`/кадр** | **0** (82 кадра боя) | + +Всё сходится в одну картину: единственное деление горячего пути — `×8/7` на +персонажа, по одному вызову на каждого Char. Экономия ровно в фазе спрайтов +и ровно на стоимость одного вызова: **2 058 тактов** (документированная +оценка была ~2 400). С соперником на сцене — вдвое. + +**Чего этот замер НЕ показывает, и это важно:** + +- Период цикла как был 3 растровых кадра, так и остался — 2 000 тактов его не + двигают. Выигрыш ушёл в запас: до нижней границы 430 000 стало 40 800 + тактов вместо 38 700. +- Остальные правки (три `y_to_row` в `pop_room`, `guard_col_from_x`, + `tp/10` у чомпера) в этой сцене **не срабатывают вовсе** — им нужны + падающая плита, переход стража между комнатами и уровень с чомперами. + Их стоимость известна поштучно, но в бою я их не ловил. +- Работа в комнате 3 между прогонами **несравнима** (391 204 против 318 145): + страж живой, фаза боя и срабатывание `pop_char_skip_mask` от прогона к + прогону разные. Оттуда взят только СЧЁТЧИК делений — он от таймингов не + зависит. + +**Профиль работы за логический кадр СЕЙЧАС (409 956 тактов + ~15 600 ввод):** + +| блок | тактов | % растрового кадра | +|---|---|---| +| see_kid + ctrl_tick + skip + heal + kid_tick | 52 536 | 12 | +| физика Кида + страж + боёвка | 73 452 | 17 | +| `pop_loose_tick` | 27 438 | 6,4 | +| **`pop_process_trobs`** (два факела) | **75 720** | **17,6** | +| `pop_redraw_needed` + шов + skip_mask | 10 932 | 2,5 | +| **`pop_char_draw` Кида** | **56 250** | **13** | +| **`pop_char_fore` Кида + борта** | **113 628** | **26** | + +Синяя полоса (ввод + логика) была ~60 % → стала ~29 %. + +**Оптимизация закрыта по решению пользователя 2026-08-10.** Всё, что +осталось неcделанным, вынесено с замерами в +[`../docs/perf_backlog.md`](perf_backlog.md) — там же протокол «как +мерить», чтобы не наступать заново на wait-state'ы и на устаревшие адреса +символов. Что доделано после таблицы выше: `pop_cd_touch` развёрнут, +tp/10 у факелов таблицей, пустой слот соперника считается тихим, ранний +выход в fore-проходе (нулевой эффект, оставлен как порт), быстрый путь без +клипа в `pop_blit_b` (факел 41 778 -> 31 218 тактов). + +**Что осталось (запас на будущее, срочности больше нет):** + +1. **`pop_char_fore` 113 628.** Внутри: `char_footprint` + расширение окна + ~21 000, дальше шесть `fore_tile`, из которых два реально рисуют (по + ~32 000). Дальше резать — кэш «в этом тайле переднего слоя нет вовсе». +2. **`pop_process_trobs` 75 720 на два факела** (~31 000 на факел). Внутри + одного факела: `gfx_blit_noclip` 8 700, чтение w/h и клип 6 400, + `pop_cd_touch` (теперь дешевле), маппинг окна 0 и возвраты. Пламя + перерисовывается каждый кадр обязательно (`TORCH_ANIM_DIV = 1`, кадр + меняется), так что пропуск тут не поможет — только удешевление блита. +3. `pop_loose_tick` 27 438 при полном отсутствии падающих плит. +4. Одно `__divsint` осталось в `pop_char_draw` (`obj_x * 8 / 7`) — ~2 400. + +### Шаг 2 (СЛЕДУЮЩИЙ, назначен пользователем): ДВИЖУЩИЙСЯ Кид + +Пропуск закрывает только покой. Наблюдение пользователя 2026-08-08: как +только Кид побежал, циан-полоса (спрайт + fore-проход) вырастает почти до +**100 % кадрового периода** — и это на ОДНОГО персонажа. То есть цена +одной перерисовки персонажа сама по себе непозволительно велика, и её надо +резать по существу, а не пропусками. + +Куда смотреть (мерить каждое, метод — `z80_profiling_method`): +1. разделить замером спрайт-блит и fore-проход: у fore уже была история + 78 % кадра до кэша кладки (`pop_fore_layer_cost`), он и сейчас главный + подозреваемый; +2. fore-проход перебирает 2×2…3 тайла и в каждом разбирает кладку заново — + кэш «в этом тайле переднего слоя нет вовсе» снял бы половину; +3. расширять перебор ТОЛЬКО по накладным спрайтам (клинок/брызги), а не по + всему окну клипа: габарит кадра уже покрыт футпринтом + правилом меча; +4. heal + блит спрайта: сейчас это два прохода по одной площади; посмотреть, + нельзя ли стирать только РАЗНОСТЬ прямоугольников при мелком сдвиге. + +**Куда идти дальше в ПОКОЕ — там это ЛОГИКА, а не отрисовка.** Разбивка +работы кадра брейкпоинтами (комната 3, труп, Кид стоит; всего 500 772 +такта): + +| участок | тактов | % кадра | +|---|---:|---:| +| ввод + `check_skel` + луч видимости + `ctrl_tick` + heal | 65 862 | 15 % | +| `kid_tick` + физика + страж + боёвка | **260 022** | **60 %** | +| `loose_tick` + `process_trobs` | 119 562 | 28 % | +| `redraw_needed` + шов + спрайты + метка | 55 152 | 13 % | +| — из них два блита факелов | 44 136 | 10 % | + +1. **60 % на тик персонажей** при том, что оба СТОЯТ — первый кандидат. + Смотреть `pop_phys_tick`/`pop_guard_phys_tick` (банк 3) и `pop_guard_tick` + (банк 1): сколько там работы, которую неподвижный персонаж делать не + обязан, и сколько стоит трамплин на каждом шаге. +2. **28 % на `loose_tick` + `process_trobs`** в комнате БЕЗ единой ловушки — + явно перебор; разобрать, что там сканируется каждый кадр (список trob, + `pop_trob_modif` соседа для шва, `pop_room_link`). +3. Запечь труп в фон (как щебень: тело в фоне + fore-часть поверх Кида) — + тогда бесплатным станет и проход Кида ПО телу, который сейчас снимает + пропуск по правилу «слоты рядом». Предложено пользователем 2026-08-08. + +Мерить в ЭТОЙ точке (комната 3, страж убит, Кид стоит) — она воспроизводима +и даёт нижнюю границу; худший случай (бой + бег + падающая плита) считать +отдельно. Метод — memory `z80_profiling_method` (брейкпоинты с +`totalcycles`); полосы бордюра показывают только одну картинку из периода и +годятся лишь для раскладки по фазам. + +**Наблюдение пользователя на приёмке DRAW-CHAR 2026-08-08:** циан-полоса +профиля (спрайты + fore) выросла — тогда «в пределах». + +**Отчего именно.** Раньше перебор тайлов у Кида шёл строго по футпринту +кадра (`char_x_left/right`, seg006:1021), а он УЖЕ спрайта; расширение +перебора окном спрайта (клинок и брызги уходят за габарит кадра) было +только у соперника. DRAW-CHAR сделала его общим — то есть у Кида теперь на +колонку-другую больше `fore_tile` за кадр. Это не регрессия «лишней +работы», а недостающая ранее корректность: тайл, который спрайт задевает, +обязан вернуть свой передний слой поверх него. + +**Если fore-проход снова станет узким местом** (сейчас он в покое не +выполняется вовсе): расширять перебор ТОЛЬКО по накладным спрайтам +(клинок/брызги), а не по всему окну; кэш «в этом тайле fore-слоя нет +вовсе»; считать окно клипа в тайловых координатах один раз. + +## P1 — берётся в любой момент + +### PERF-SWEEP. Поиск узких мест по ВСЕЙ игре, а не в одной сцене + +**Постановка (пользователь, 2026-08-18):** «пока мы тестируем на регресс +только одну сцену; надо придумать механизм проверки узких мест по всей +программе — ручной проход уровня, в логи пишутся такты каждой фазы с +информацией, в какой комнате это происходит (возможно, в каком тайле Кид), +ищем максимумы по уровню и оптимизируем уже конкретную комнату». + +Сцена 13/23 закрывает ПИК, но она одна: механики, которых в ней нет +(чомперы, бой, мышь, зеркало, толпа стражей), в бюджет никто не проверял. + +#### Чем НЕЛЬЗЯ это делать — и почему + +Тем, чем меряем сейчас. Брейкпоинты с `printf ...; g` на четырёх границах +фаз роняют эмуляцию в проценты от реального времени — **ручной проход так не +сыграть**, а именно ручной проход и есть суть задачи. Плюс две мелочи, +которые уже стоили времени: адреса зондов надо переснимать после КАЖДОЙ +пересборки (`.lst` + база модуля), а кольцо `clog` переполняется — сегодня +первый прогон принёс один «покой», каскад успел вытесниться за 75 с +ожидания. + +#### Куда смотреть + +**Вариант А — тап на порт бордюра в Lua-мосте (рекомендую).** `PROF(n)` +это `out (0xFE), a`, то есть границы фаз УЖЕ размечены в железе. +`install_write_tap` на порт даёт колбэк без остановки CPU — накладные +несравнимо меньше брейкпоинта, играть можно. Главный бонус: **тап не +привязан к адресам кода**, переснимать после пересборки нечего. «Где» берём +оттуда же: Lua читает `cur_room` и `Kid.curr_col/curr_row` по адресам из +`sprpop.map` (их выдавать в маленький файл при сборке, чтобы не парсить +map руками). Пишем CSV: кадр, комната, тайл Кида, синяя/зелёная/циан/работа, +период. + +**Вариант Б — программа меряет себя сама, в РАСТРАХ.** Тактов Z80 изнутри +не видно, но нам они и не нужны: бюджет сформулирован в растровых кадрах, а +позицию луча видно поллингом бита 5 порта 0xFE (то же, чем живёт +`gfx_wait_vsync`). Разрешение ~1/256 кадра ≈ 1 700 тактов — для поиска +узких мест с запасом. Дороже варианта А по месту в резиденте, но +единственный, который работает **на живом железе**, а не только в MAME — +а такт MAME это 2,4× номинала Z80 (memory `sprinter_wait_states_2x`), и +рано или поздно сверяться придётся с железом. + +#### Что считать результатом + +Не «дамп кадров», а сводка: максимум каждой фазы **по комнатам**, число +кадров сверх 400 000 и сверх растрового кадра 430 000, и топ комнат по +худшему кадру. Дальше — точечная оптимизация той комнаты, как делали с +13/23. + +Смежное: сцена и метод — [`../docs/perf_l13_room23.md`](perf_l13_room23.md); +фазы — [зелёная](perf_green_phase.md), [циан](perf_cyan_phase.md). + + +### T-RENDER. Тесты слоя отрисовки: «в запечку не попадает транзиент» + +**Откуда взялось** (пользователь, 2026-08-18, по итогам +[SPIKE-BAKED](BUGS_CLOSED.md#spike-baked)): «такой сценарий (и другие с +пиками) можно внести в наши тесткейсы?» + +**Что есть сейчас.** `tests-host/` — это ЛОГИКА: геометрия, физика Кида, +`Char`, спецсобытия. Отрисовки там нет вовсе (`stubs.c` глушит все блиты +пустышками), `pop_tile.c`/`pop_room.c`/`pop_bg.c` не линкуются — таблицы +деления на ширину тайла в стабах даже продублированы копией. **Ловушки не +покрыты ничем**: ни пик, ни чомперов, ни `animate_*`. То есть SPIKE-BAKED +тестами поймать было нельзя в принципе. + +**Что покрывать.** Не «картинку» (её проверяет глаз в MAME), а инвариант, +на котором держится вся наша запечка: + +> при `pop_t_bake_rest = 1` ни один слой отрисовки тайла не выдаёт +> ТРАНЗИЕНТНЫЙ спрайт — ни своей ячейки, ни соседней. + +Он проверяем механически и стоит ровно того: именно его нарушение дало +SPIKE-BAKED, а до того — мерцание плиты, ради которого флаг и заводился. + +**Как.** Новый набор `t_render`: линкуются НАСТОЯЩИЕ `pop_tile.c`, +`pop_room.c`, `pop_bg.c`, а `pop_env_b`/`pop_wall_b`/`pop_fore_b` подменяются +**записывающим** стабом — списком `(id, x, yb)` за вызов. Тогда проверка +пишется в одну строку: «`draw_tile` при `bake_rest=1` и modif пик 4 не выдал +ни одного id из `spikes_fram_*`». Тот же стаб сразу закрывает и чомпера, и +плиту, и будущие ловушки — по одному тесту на каждую. + +Отдельно и дёшево — характеризация автоматов `animate_spike`/`animate_chomper` +(сверка полного цикла модификатора с SDLPoP seg007), но она НЕ ловит этот +класс: автомат был верен, врал слой отрисовки. + +**Чего это стоит.** Не одна строка: набор тянет за собой атласы и +`pop_cd_*`, а `stubs.c` придётся разделить (сейчас он подменяет то, что +здесь нужно настоящим). Прикидка — отдельная сессия. Место в `DATA_LOC` +проверить заранее: набор линкует ещё три крупных модуля (см. грабли +раскладки в `tests-host/README.md`). + + +### QSAVE. QuickSave / QuickLoad — ЗАКРЫТА 2026-08-22 + +**Закрыта** (F6/F9, POP.SAV + POP.BAK, коммит f4b4852, тэг v0.6-pop-quicksave). +Протокол и устройство — [`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md#qsave); +формат снимка и разбор SDLPoP — +[`../docs/quicksave_plan.md`](quicksave_plan.md). + +### L1-SPEED. Игра идёт быстрее оригинала (найдено 2026-08-01) — ЗАКРЫТА 2026-08-19 + +**Закрыта** режимами скорости (`pop_pace.h`, клавиша **P**): FASTEST 3/3, +FAST 3/4, **NORMAL 4/5 = 81,9 / 102,4 мс** против 83,3 / 100 мс у +оригинала. Условие боя взято буквально из SDLPoP (`seg003.c:363`): +`Kid.sword == SWORD_2_DRAWN`. Дефолт — FASTEST (решение пользователя). +Проверено в MAME: период ровно 4 и ровно 5 растров. Разбор и замеры — +`../docs/frame_pacing_plan.md`. Осталось из исходной формулировки только +сверить NORMAL секундомером рядом с живым SDLPoP. + +Исходная запись: + +Сверка таймингов: оригинал — `BASE_FPS = 60` при `base_speed = 5` тиков на +логический кадр (`SDLPoP/src/types.h:1373`, `data.h:869`) = **83.3 мс**, в бою +`fight_speed = 6` = **100 мс**. У нас `sprpop.c` ждёт **три** `gfx_wait_vsync()` += 60 мс, и отдельной скорости боя нет — то есть примерно **+39 % к скорости +эталона**. Соответствие: 4 ожидания (80 мс) обычно, 5 (100 мс) в бою. +Условие «делать ПОСЛЕ CLIP-1» снято — CLIP-1 закрыт. Проверка — +секундомером по одинаковому отрезку рядом с живым SDLPoP, не «на глаз». + +### TUNE-1. Параметры движка — в конфиг, а не в код + +**Что уже есть.** `pop_tune.h` — все настраиваемые числа собраны в одном +заголовке: чекпойнт уровня 3 (`POP_CHKP_*`), отладочное окно решётки +(`POP_DBG_GATE_HOLD`), включатель зацепа в прыжке (`POP_ENABLE_JUMP_GRAB`). +Правило уже действует: новое «особое событие» или тайминг заводится ТАМ, а +не константой по месту. + +**Что нужно сделать.** Читать их из ФАЙЛА рядом с exe, чтобы менять без +пересборки — под отладку («растянуть решётку, чтобы успеть пройти») и под +моды. Формат: простой ini/`ключ=значение`, парсер на ~50 строк (числа, +комментарии `;`, неизвестные ключи игнорировать), файл необязателен — +нет файла, значит зашитые дефолты. Секции по смыслу: `[level]`, +`[debug]`, `[enhancements]`. + +**Ориентир — SDLPoP.** У него это `custom_options_type` (types.h) + +`SDLPoP.ini` + меню Settings/Mods; наши имена намеренно совпадают с его +(`custom->имя`), чтобы сверка оставалась механической. Осмотр его меню и +опций — часть задачи: у него уже разложены по группам стартовые +HP/минуты, номера «особых» комнат и уровней (шадоу, скелет, зеркало, +чекпойнт), тайминги ворот и пик, скорости, а отдельной группой — +`fixes`/`enhancements` (включая `enable_jump_grab`, который мы уже +портировали). Брать всё подряд не надо: переносим по мере того, как +константа реально понадобилась в игре. + +**Оговорка по памяти.** Парсер и таблица параметров — холодный код, +исполняется один раз при старте: кандидат в банк, а не в резидент W1. + +### TUNE-2. Параметры СТРАЖЕЙ — в CFG-файл + +Частный, но самый востребованный срез [TUNE-1](#tune-1): сейчас весь ИИ +стража зашит константами, и чтобы попробовать «а если он будет блокировать +пореже» — надо пересобирать. + +**Что настраивать.** Всё, что индексируется мастерством (12 градаций, +`NUM_GUARD_SKILLS`), — таблицы в `guards.c`: + +| таблица | что решает | дефолт для навыка 4 | +|---|---|---:| +| `STRIKEPROB` | вероятность удара | 61 | +| `RESTRIKEPROB` | добить из своего парирования | 5 | +| `BLOCKPROB` | заблокировать замах | 200 | +| `IMPBLOCKPROB` | то же, когда Кид только что пробил блок | 100 | +| `ADVPROB` | наступать | 255 | +| `REFRACTIMER` | «отдышка» после ранения, кадров | 8 | +| `extrastrength` | надбавка HP (сейчас зашита как `skill == 4 ? 1 : 0`, `pop_guard_cold.c`) | 1 | + +Плюс не зависящее от навыка: `tbl_guard_hp[16]` (`pop_level_cold.c`) — база +HP по уровню, и навык скелета (`SKEL_SKILL`) / тени (`SHADOW_SKILL`). + +**Формат — как у SDLPoP**, чтобы правки переносились в обе стороны без +перевода: секции `[Skill 0]`..`[Skill 11]`, внутри `strikeprob=`, +`blockprob=` и т.д. (`options.c:439`, пример — `SDLPoP/SDLPoP.ini:562`). +Имена ключей брать ЕГО, регистр игнорировать. + +**Чего НЕ делать.** Навык конкретного стража живёт в данных уровня +(`level.guards_skill[комната-1]`, читаем в `pop_level_guard`) — его в CFG +не тащим, иначе разъедется с файлами уровней. Переопределение «в комнате +N навык M» — отдельная надобность, если понадобится, то как явная секция +`[Level N]`, а не подмена данных. + +**Зачем это стоит сделать.** Прогон 2026-08-20 (11/22, навык 4) дал +ощущение «страж тяжелее, чем в SDLPoP». Сверка кода расхождений не нашла +(протокол — в `../docs/impl_diff.md` и в ответе сессии: дистанция, +диспетчер, три ветки ИИ, шесть таблиц, `justblocked`, `check_hurting`, +`parry`, `swordfight`, порядок тиков, откат seq_74 = −10 — всё совпало +побайтно). Проверить такую гипотезу можно только подбором чисел на живом +прогоне, а для этого их надо крутить без пересборки. + +**Оговорка по памяти** — та же, что в TUNE-1: парсер холодный, в банк. + +### SND. Звук — эффекты через CBL, музыка на AY — ЗАКРЫТА ИНАЧЕ 2026-08-26 + +> **Решение пересмотрено.** Музыка пошла НЕ на AY, а тем же PCM через CBL: +> записи DOS-версии укладываются в формат железа без единого преобразования +> в рантайме (`../docs/sound_plan.md` §4). Озвучены заставка, сцены между +> уровнями и игровые джинглы — 21 трек. Единственный неозвученный кусок — +> финал `won`, см. [MUS-WON](#mus-won). + +
исходная постановка (путь A на AY) + +Полный разбор с замерами — [`../docs/sound_plan.md`](sound_plan.md). +Коротко, почему это оказалось дешевле, чем выглядело: + +- эффекты уже лежат **8-битным PCM на 11 000 Гц**, а у CBL есть режим + 10 937,5 Гц (расхождение 0,6 %) и ровно тот же формат сэмпла — + **играются как есть, без конверсии**; 31 звук, 103 941 Б ≈ 6,3 + EMM-страницы; +- музыка есть **нотами PC-спикера, 7 КБ на всю игру**, причём нота задана + прямо в ГЕРЦАХ — пересчёт в период AY это одно деление; MIDI разбирать + не нужно; +- AY и COVOX сведены в один ЦАП, поэтому играют одновременно; +- отдельный таймер не нужен: секвенсор музыки двигает CBL-callback + (11,7 мс), а короче 12 мс во всей музыке всего 2 ноты из 1469. + +Фазы З1..З5 и список «проверить артефактом до кодинга» — в доке. + +
+ +### MOB-SPR-EMM. Композит падающего куска — в EMM (1284 Б W2) + +`mob_spr` (`pop_room.c`) — самый крупный буфер в W2: 4 + 64*20 байт под +композит падающей плиты. Это половина нынешнего дефицита W2. + +**В БАНК ЕГО ПЕРЕНЕСТИ НЕЛЬЗЯ** (разобрано 2026-08-26). Цепочка рисования +— `pop_room` (банк 7) -> `pop_mem_b` (банк 5) -> `blit_b_clip` (резидент), и +трамплин на `pop_mem_b` переключает W3. Указатель на данные банка 7 после +этого показывает на страницу банка 5: блит нарисует мусор, причём молча. +`--bank-data` вдобавок действует на весь модуль, а `mobs` из того же файла +читают снаружи. + +**ЧТО РАБОТАЕТ — EMM-страница с маппингом в W0.** W0 не переключают ни +трамплин (он трогает только W3), ни звуковой ISR (насос живёт в W3), значит +адрес `0x0000+off` валиден и в банке 7, и в банке 5, и в резиденте — ровно +как у атласных спрайтов, которые тем же блитом и рисуются. `blit_b_clip` +окно не трогает, маппингом занимается вызывающий. + +**Цена.** `mob_spr_build` читает три части через `gfx_w0_map(a->page)`, а +приёмник должен быть в том же окне — держать оба нельзя. Сборку придётся +вести построчно: строка части в 64-байтовый буфер на стеке, перемаппить +окно, записать. Вызывается это лишь на смену тайлсета. Плюс +`gfx_w0_map`/`unmap` вокруг каждого куска — около 1350 тактов, при шести +кусках меньше 2 % кадра. + +**Чем подтверждать.** Падающие плиты в комнате 1.2 и каскад уровня 13: +ошибка проявится не крашем, а мусором на месте куска. + +**Дешёвая половинчатая мера** (если 1284 Б не нужны целиком): буфер объявлен +с запасом 64x20, фактический композит около 58x19 — точные константы дают +~180 байт без единого риска. + +### PAL-ARC. Палитры — в архив своей группы (полишинг) + +Файлов палитр в игре шесть: `KID\kid.pal` (1024 Б), `PV\story.pal`, +`PV\pv.pal` (по 1024), `BG\pop_tile.pal`, `BG\pal_tile.pal` (по 128), +`TITLE\title.pal` (64). Всё, что лежит в `poc/res/tiles/` (36 файлов) и +`bg/pop_bg.pal`, на образ не попадает и кодом не читается — осадок ранних +экспериментов, его стоит просто убрать. + +**Что предлагается.** Класть палитру в архив ТОЙ ЖЕ группы, где её +графика: `kid.pal` → `kid.arc`, оба `*_tile.pal` → `bg.arc`, `title.pal` → +`title.arc`, `story`/`pv.pal` → `pv.arc`. Тогда правило одно на все +ресурсы («файл лежит в архиве своей группы»), и уходят последние шесть +открытий: `pop_bg_load` берёт тайлсет и палитру за одно открытие, заставка +так же. Отдельно ценно, что `kid.pal` перестанет читаться своим `open` на +КАЖДОЙ загрузке уровня (её перезаливает `pop_pal_game_load`). + +**Чего это стоит.** Элемент архива читается только в EMM-страницу, +поэтому нужна маленькая функция «палитра из страницы»: `bank_read` по 256 +байт в буфер и четыре `gfx_pal_load`. Буфер уже есть — `pop_pal_buf` по +0x4000. + +**Что теряется.** Возможность подменить палитру файлом рядом с игрой +(`pal_file_load` сейчас откатывается на `a:\kid.pal`). Решить явно: +либо файл остаётся приоритетным и архив — запасным путём, либо fallback +выбрасывается. + +**Почему НЕ отдельная папка `PAL\` и не один общий атлас палитр.** Папка +не уменьшает числа файлов и рвёт связь «палитра ↔ её графика». Общий +атлас экономил бы открытия только при загрузке всех палитр разом, а они +нужны в разное время: `kid.pal` — каждому уровню, заставочные — только +заставке. + +### HOF-ENTRY. Таблица рекордов — ЗАКРЫТА 2026-08-26 + +> **Сделано.** Оба показа оригинала: между credits и attract-demo и хвостом +> `end_sequence` (с вводом имени, паузой 120 тиков и возвратом на титульную +> картинку). Экран — рамка story с логотипом, время в формате `59:43`, +> строки с тенью, мигающий курсор. Ожидание конца победной темы переехало +> за таблицу, как в оригинале. Протокол и грабли — +> [`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md#hof-entry). + +### MUS-WON. Финальная тема — кольцевой стриминг — ЗАКРЫТА 2026-08-26 + +> **Сделано.** Кольцо из шести страниц (96 КБ = 9 с), насос заворачивается +> по `pop_mus_ring`, подкачка в `pop_music_service` по дистанции +> «проиграно / прочитано». Разбор и грабли — `../docs/sound_plan.md` §8. +> Проверено в MAME на сборке `LEVEL=14`. + +
исходная постановка + +Трек 56 (`won`, 115 с) — это 1,2 МБ, 78 страниц EMM. Ни в `POP_MUS_PAGES` +(20), ни в разумный бюджет памяти он не влезает, поэтому единственный +оставшийся неозвученным кусок игры — экран победы. + +**Что нужно.** Кольцевой буфер: держать в EMM несколько страниц (8 = 128 +КБ хватит с запасом — это 12 с звучания), а загрузчик дочитывает в те, что +насос уже прошёл. Курсор насоса (`pop_mus_pg`) при этом обязан +заворачиваться, а загрузчик — держать дистанцию от него и не обгонять на +круг. Чтение страницы 33 мс против 1,5 с её звучания — запаса вдоволь, +вся сложность в аккуратности индексов, а не в скорости. + +**Заодно.** Тот же механизм снимает ограничение `POP_MUS_PAGES` со всех +треков: сейчас каждый должен помещаться в память целиком. + +
+ +### ARC-ATLASES. Атласы — в архивы PBA1 — ЗАКРЫТА 2026-08-26 + +> **Сделано.** Восемь архивов: kid (58 атласов), pv (78), shadow (32), bg +> (25), title (20), guard (10), vizier (5), skel (4). На образе было 232 +> файла, стало 8 архивов плюс шесть палитр, `kid.ani` и таблицы уровней. +> Индексы печатает упаковщик (`ARC_<ГРУППА>_<ФАЙЛ>`), серии код проверяет +> статически. `atlas_load` разделён: `atlas_attach` принимает уже +> прочитанную страницу, поэтому чтение из архива не дублирует проверку +> магии. Грабля, стоившая отладки: имя архива обязано лежать в том же +> банке, что и код, который его читает, — строковый литерал из чужого банка +> после переключения W3 показывает на другие данные; поэтому наружу отдаётся +> НОМЕР архива (`pop_arc_open_id`). +> +> Остаток на будущее — палитры, см. [PAL-ARC](#pal-arc). + +
исходная постановка + +**Что сделано.** Формат PBA1 и читатель готовы и работают на звуке: +`toolchain/pop_pack_arc.py` собирает архив (заголовок 512 Б, записи по 4 +байта — offset в секторах, size в байтах), `pop_arc.c` открывает его ОДИН +раз на группу и читает элементы прямо в EMM-страницы. Набор эффектов +уехал в `SND\snd.arc` целиком. + +**Что осталось.** Перевести остальные группы: `BG\` (25 файлов), `KID\` +(60), `GUARD/SKEL/VIZIER/SHADOW` (~50), `TITLE\` (21), `PV\` (75), +`LEVELS\` (15). Каждая группа — свой архив, потому что таблицу держит +вызывающий на стеке (4 байта на запись, у PV это 300 Б). + +**Зачем.** Замер 2026-08-25: `open` 51,4 мс против 32,6 мс на чтение 16 КБ. +Стартовая пачка ресурсов — около 142 файлов, то есть **7,3 с чистого +`open`**, который архивы убирают почти целиком. Второй мотив назван +пользователем прямо: «не нравится огромное кол-во файлов» в корне диска. + +**Чем подтверждать.** Время от запуска до первого кадра уровня (сейчас +меряется по MAME), число файлов на образе, и что каждая сцена по-прежнему +собирается при неполном диске (пути ошибок в `atlas_load`). + +
+ +### MEM. Следующий шаг разгрузки W1/W2 + +`pop_ctrl.c` уехал в банк 5 ([MEM-BANK5](TASKS_CLOSED.md#mem-bank5), куча +180 Б → 2298 Б; после снижения `--max-allocs` — 2751 Б). Следующий кандидат +по тому же критерию (**не размер, а частота вызова и отсутствие горячих +банк→банк переходов**) — **расщепление `pop_kid.c`**: холодная половина +(загрузка страниц спрайтов, `pop_kid_load`) в банк, движок кадров +(`load_frame`/`play_seq`, 2×/кадр) оставить в резиденте. + +Брать по факту нехватки места, не заранее. Таблица резидентного кода по +модулям и разбор, почему `pop_level.c` в банк НЕЛЬЗЯ, — в +[`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md#mem-bank5). + +--- + +## Отложено осознанно (не брать, пока не появится причина) + +- **KBD-1, остаток** — «иногда при зажатом Shift стрелка всё-таки + пропускается» (ощущение пользователя, не замер: счётчики на 35 нажатиях + подряд потерь не показывают). Решение пользователя 2026-08-01: отложено до + финальной полировки. Где именно осталась дыра и что делать, если вернёмся, + — в [`TASKS_CLOSED.md`](../../PoP/roomtest/TASKS_CLOSED.md#kbd-1) (там же весь протокол + замеров и список того, что делать НЕЛЬЗЯ). +- **Quickload (Shift+F9) и остальные читы SDLPoP** — оценка сделана + ([DBG-CHEATS](TASKS_CLOSED.md#dbg-cheats)), код не написан. Самое ценное и + самое дорогое: сериализация `Char` + `room_modif` всех комнат + trob'ов + + стражей (`levels_plan.md` §4), зато даёт воспроизводимый регресс «вот кадр, + где баг» вместо ручной подгонки позы. +- **Звук** (CBL-эффекты, Фаза 5 `PORT_PLAN.md`) — геймплей не блокирует. +- **Таймер уровня / HUD времени / меню / сохранения** — Фаза 6. +- **[T-1](BUGS_OPEN.md#t-1)** (пики: перерисовка по причине) — отдаётся почти + бесплатно после [T-2](BUGS_OPEN.md#t-2), отдельно не окупается. +- **Отключение мыши на время игры** и **замена PRNG** — + `../docs/ideas_backlog.md` (оба дают доли процента кадра). +- **OPT-1** (хирургический редрой шва) — решено НЕ делать, стоимость + транзиентная; разбор в [`BUGS_CLOSED.md`](../../PoP/roomtest/BUGS_CLOSED.md). + +--- + +## OPT-BLIT — цена отрисовки: чистка кодогена (СЛЕДУЮЩАЯ СЕССИЯ) + +> **2026-08-17: контрольный замер снят, задача разложена по фазам.** +> Работа теперь ведётся в трёх документах, которые живут между сессиями — +> туда же писать результаты каждой правки: +> +> - [`../docs/perf_l13_room23.md`](perf_l13_room23.md) — сцена +> (каскад 6 плит, ур. 13 комната 23), рецепт воспроизведения, зонды, канал +> `clog`, сводка по кадрам, разбор габаритов спрайтов; +> - [`../docs/perf_green_phase.md`](perf_green_phase.md) — ЗЕЛЁНАЯ +> фаза: пик **805 000** при бюджете 400 000; позиции G1..G6; +> - [`../docs/perf_cyan_phase.md`](perf_cyan_phase.md) — ЦИАН фаза: +> пик **632 000** при бюджете 400 000; позиции C1..C7. +> +> Порядок работ: **G1** (окно клипа для запечки, −250..400 тыс.) → **G2** +> (расколоть `draw_tile` + лист для ряда −1) → **C1** (убрать избыточный +> `pop_cd_touch` в `mob_render`, −81 тыс.) → **G3/C3** (`blit_b_clip` на +> байтовые габариты) → **C4** (разобрать `pop_char_fore(KID)`: 1 722 → 150 978). +> +> Синяя секция (**142 830**) в бюджет укладывается — не трогаем. +> Ответ на вопрос про `uint8_t`: горячий путь можно переводить целиком, +> максимум по всем игровым атласам **56×63**; шире 255 только восемь +> полноэкранных подложек титров/сюжета (320×200 и т. п.), и они рисуются раз +> на экран — им отдельный банк / прямой libbgi. + +Открыто 2026-08-13 по итогам разбора зелёного блока. Контекст и все замеры +— в memory `blit_cost_model` и `sdcc_z80_stack_locals_hot_loop`, протокол +разбора — в коммитах `f89b7dd` / `b27b313` / этом. + +**Что уже сделано и чем это подтверждено.** Цена блита разложена регрессией +по 339 замерам: `такты = 8791 + 198,2*h + 5,96*(w*h)` (такты MAME = системный +клок ~21,5 МГц, НЕ такты Z80 — растровый кадр 430 000). Байт на пределе +железа (через акселератор дважды по 3 такта), строка ~198, а вот 8 791 на +ВЫЗОВ оказались нашим кодогеном: `pop_blit_b` держал `w`/`h` как `uint16_t`, +из-за чего вся функция уезжала в 14-байтовый стековый кадр и `w = img[0] | +(img[1] << 8)` разворачивалось в два десятка IX-относительных пересылок. +После перехода на байтовый габарит: noclip 13 679 -> 11 530 (-16%), +клипованный 18 853 -> 15 340 (-19%), `_CODE` -42 Б. + +### а) Где ещё `uint16_t` можно сделать `uint8_t` + +Габариты спрайтов и всё, что из них считается. Признак: значение заведомо +<= 255, но объявлено 16-битным «на всякий случай», и живёт в горячем пути. +Начинать с: +- `blit_b_clip` (`pop_tile.c`) — `dw`/`dh`/`sx`/`sy` объявлены `int`, хотя + ядра принимают `uint8_t`; это ВТОРОЙ по частоте путь (33% блитов). +- `pop_cd_touch` / `cd_cols_of` — четыре `int`-аргумента на вызов. +- `_gfx_blit_sprite_noclip` и обёртки libbgi: `stride` уже `uint16_t`, а + `w`/`h` — `uint8_t`; проверить, не расширяются ли они обратно у вызывающих. +- Кандидаты в `pop_room.c`/`pop_bg.c`: локальные `int x, dby, dmy` в + `draw_tile` — часть из них помещается в байт, но осторожно: координаты + бывают отрицательными. + +**Как проверять:** после каждой правки смотреть пролог функции в +`.sprinter-cc-SprPoP/*.asm` — исчез ли `ld iy,#-N / add iy,sp / ld sp,iy` +и сколько осталось `-N (ix)` в теле. + +### б) Проход по asm за неоптимальным IX-доступом + +Механическая проверка, даёт больше всего за единицу усилий: + +``` +grep -c "(ix)" .sprinter-cc-SprPoP/*.asm # где сгущается +grep -n "ld iy, #-" .sprinter-cc-SprPoP/*.asm # крупные стековые кадры +grep -n "pop.*\n.*pop.*\n.*push" ... # чтение спилла через стек +``` + +Признаки беды (все три встречались сегодня): пролог с `iy`-кадром больше +~6 байт; пары `pop bc / pop hl / push hl / push bc` в теле цикла (SDCC +читает спиленный указатель через стек вместо `ld l,-N(ix)`); повторный +пересчёт адреса `arr[i]` под каждое поле структуры. + +Приоритет по частоте вызова: `pop_blit_b` (сделан) -> `blit_b_clip` -> +`pop_cd_touch` -> `draw_tile` -> `pop_redraw_needed`. + +### Что НЕ делать (проверено сегодня, отрицательный результат) + +- **Не откладывать запекание на другой кадр.** Запекание пишет ОЗУ-копию + фона, из которой восстанавливает `heal`; отложенное даёт призрак плиты на + месте дыры. Идея «бюджет одного запекания на кадр» снята. +- **Не ускорять передачу пикселей** — она на пределе железа (3+3 такта на + байт), см. модель выше. +- **Не искать проблему в W3-скобке** (`_bgi_begin`/`_bgi_end` — по пять + инструкций) и не списывать разброс на прерывания (внутри размерной + группы разброс 3 такта, код прямолинейный). + +### Хвосты этой сессии + +- Пакетная пометка `pop_cd_touch` (`pop_cd_batch_begin/end`, скобка в + `draw_tile`) — сделана, но выигрыш замером НЕ подтверждён: в захваченных + кадрах скобка не срабатывала (блиты шли из холодной отрисовки комнаты). + Переснять на кадрах ЗАПЕКАНИЯ. **2026-08-17: цена НЕпакетного вызова + замерена — 4 502 такта, 28 % всей цены блита; в `mob_render` он к тому же + избыточен (см. C1).** +- Снять временную оснастку: `pop_dbg_m9..m16`, `pop_dbg_b1..b6`, + `pop_dbg_wh`, `pop_dbg_kind`, `pop_dbg_rdmax*`, `mame/v306/run_bridge_log.sh`. +- Цель по кадру не достигнута: зелёный пик был 792 012 при цели 400 000. + После сегодняшних правок не перемерян — начать сессию с контрольного + замера, а не с новых правок. + +**ГРАБЛИ (стоили сегодня часа):** проверять, что запущен РОВНО ОДИН MAME +(`pgrep -f mame.arm | wc -l`) — несколько экземпляров пишут в один +`error.log`, мост говорит с одним, замеры собираются с другого, и точки +«не срабатывают». И не обрезать `error.log`, пока MAME его держит: она +пишет по старому смещению, в начале остаётся дыра из нулей. diff --git a/applications/SprPoP/docs/full_game_plan.md b/applications/SprPoP/docs/full_game_plan.md new file mode 100644 index 0000000..c19e5d5 --- /dev/null +++ b/applications/SprPoP/docs/full_game_plan.md @@ -0,0 +1,367 @@ +# От SprPoP к полноценной игре — сценарий и оболочка + +Статус: **частично реализовано; аудит обновлён 2026-08-24**. Ранее пометка +«завершены FG0–FG12» была неверной: для многих этапов уже есть код и +host-тесты, но их критерии приёмки на Sprinter ещё не выполнены. Фактический +статус каждого FG приведён в [§14](#14-этапы-реализации). + +Этот документ описывает превращение текущего игрового цикла +`SprPoP` в законченную игру: заставка, интро, демонстрационный уровень, +сцены между уровнями, таймер, финал и Hall of Fame. План меню и постоянных +настроек вынесен в [`menu_settings_plan.md`](menu_settings_plan.md), +детальный план QuickSave — в [`quicksave_plan.md`](quicksave_plan.md). + +## 1. Зафиксированный scope + +- Целевая последовательность — оригинальная SDLPoP/DOS PoP с уровнями + **1..14**. Уровень 14 — скрытая финальная часть после Джаффара: его номер + игроку не показывается, победа наступает в комнате 5. +- Уровень **15 удаляется полностью**: не пакуется на HDD, не загружается, + отсутствует в переходах, читах и UI; специальная логика potions/copy + protection level удаляется. +- Уровень **0** остаётся только демонстрационным (attract mode), а не частью + новой игры. +- Title sequence повторяет SDLPoP. Перед ней допускается отдельный + пропускаемый экран с информацией о Sprinter-сборке. +- Первая версия использует текущий профиль поведения `VANILLA`. Сейчас это + означает **существующую реализацию SprPoP**, включая уже встроенные + исправления. Аудит и разведение `VANILLA/ENHANCED` — будущая задача. +- Программа работает **только с HDD**. Варианты без сохранения для floppy не + проектируются. +- Моды и выбор levelset в этот план не входят. + +## 2. Что делает SDLPoP + +Источники истины в локальном SDLPoP: + +- `src/seg000.c`: `start_game()`, `show_title()`, demo mode, общий кадр, + проверка финала; +- `src/seg003.c`: `init_game()`, `play_level()`, `play_level_2()`; +- `src/seg001.c`: cutscene engine, `pv_scene()`, сцены 2/4/6/8/9/12, + `time_expired()`, `end_sequence()` и Hall of Fame; +- `src/data.h`: `tbl_cutscenes`, параметры уровня 0, win level/room; +- `data/TITLE`, `data/PV`, `data/LEVELS/res2000.bin`: ресурсы оболочки. + +Штатный маршрут: + +```text +boot + -> title / story screens + -> Princess + Jaffar intro + -> credits / Hall of Fame + -> demo level 0 + -> title или новая игра + -> levels 1..14 + before 2 -> princess cutscene + before 4 -> princess cutscene + before 6 -> princess cutscene + before 8 -> princess + mouse + before 9 -> princess + mouse + before 12 -> scene selected by remaining time + -> level 14, room 5 + -> embrace + mouse + -> ending text/music + -> Hall of Fame + -> title +``` + +Кроме уровней, здесь есть глобальный 60-минутный таймер, сцена истечения +времени, пропуск сцен клавишей, fade/flash, ожидание музыки и возврат в +attract loop после демо или финала. + +## 3. Текущее состояние SprPoP + +Уже реализованы игровой кадр, комнаты, уровни, тайлсеты, Kid/Guard/Shadow, +Джаффар, специальные события, checkpoint, переходы уровней, бесшовный +выход 12-го уровня, перенос максимального HP и звуковые эффекты. + +Поверх игрового цикла уже добавлены автомат оболочки, title/story, demo +уровень 0, global timer, сценарный интерпретатор, level-flow, ending и Hall +of Fame. Полный маршрут также собирается в HDD-образ. + +Однако это **не означает готовность оболочки**. На момент аудита остаются +существенные незакрытые места: + +- lifecycle палитр: gameplay-переходы используют чёрный барьер без fade; + cold start и полный набор dungeon/palace переходов ещё не прошли приёмку; +- PV intro Princess/Jaffar уже покадровый (актёры, факелы, звёзды, часы, + молния и foreground-колонна); сцены перед 2/4/6 и длинной веткой 12 + анимируют факелы, звёзды и песок, а сцены 8/9 и короткая ветка 12 пока + используют статические позы с исходной длительностью; +- demo отображается с игровой палитрой, проходит второй разворот/зацеп и + доходит до боя; после смерти Кида корректно завершает цикл; +- time-expired, ending и Hall of Fame имеют маршрут и реализацию UI, но не + прошли сквозную MAME-проверку вместе с ресурсами и возвратом к title; +- нет полного регресса EMM/FD для каждого перехода состояния. + +## 4. Архитектура: автомат состояний приложения + +Нельзя наращивать все режимы условиями внутри кадрового цикла. Текущий +цикл должен стать реализацией одного состояния `PLAYING`: + +```text +BOOT -> BUILD_INFO -> TITLE -> INTRO -> DEMO + | | + +---- NEW_GAME <-+ + +NEW_GAME -> LEVEL_LOAD -> PLAYING <-> PAUSE_MENU + | + +-> CUTSCENE -> LEVEL_LOAD + +-> TIME_EXPIRED -> TITLE + +-> ENDING -> HALL_OF_FAME -> TITLE +``` + +Минимальный контекст оболочки: + +```c +typedef enum { + POP_APP_BOOT, + POP_APP_BUILD_INFO, + POP_APP_TITLE, + POP_APP_INTRO, + POP_APP_DEMO, + POP_APP_LEVEL_LOAD, + POP_APP_PLAYING, + POP_APP_PAUSE_MENU, + POP_APP_CUTSCENE, + POP_APP_TIME_EXPIRED, + POP_APP_ENDING, + POP_APP_HALL_OF_FAME, + POP_APP_QUIT +} pop_app_state_t; +``` + +Переходы задаются результатом состояния, а не прямыми рекурсивными +вызовами наподобие SDLPoP `start_game()`/`longjmp()`. На Z80 это проще для +стека и позволяет освобождать ресурсы каждого режима в одном месте. + +## 5. Ресурсная модель + +Title и cutscene-ресурсы нельзя постоянно держать рядом с игровыми +атласами. Для каждого состояния нужен явный lifecycle: + +```text +enter: pause sound -> unload incompatible set -> load set -> apply palette +run: process input/timer/animation +leave: stop sound -> release EMM pages -> clear transient state +``` + +Новые группы HDD: + +```text +TITLE\ title/story images, palette, optional build-screen assets +PV\ princess room, Princess/Jaffar/mouse frames, palettes +MUSIC\ intro, cutscene and ending tracks/samples +LEVELS\ res2000..res2014.bin +``` + +Конкретный формат атласов выбирает упаковщик. Runtime не должен разбирать +PNG/DAT: как и игровые спрайты, он получает подготовленные `.atl`/`.bin`. + +## 6. Экран Sprinter build + +Отдельное состояние перед оригинальной заставкой: + +```text +PRINCE OF PERSIA +SPRINTER SP2000 BUILD +version / date / build id +``` + +Требования: + +- пропускается любой клавишей; +- выключается в Settings; +- не запускает музыку оригинального title и не меняет её тайминги; +- данные версии генерируются сборкой, а не правятся вручную в C; +- отсутствие экрана приводит прямо к `TITLE`. + +## 7. Title и текстовая подсистема + +Порядок переносится из `show_title()`: + +1. основной титульный экран; +2. Presents; +3. название игры и Jordan Mechner; +4. story frame / “In the absence…”; +5. intro Princess + Jaffar; +6. story “Marry Jaffar…”; +7. credits; +8. Hall of Fame, если таблица непуста; +9. demo level 0. + +Нужны общие примитивы: загрузить full-screen image, вывести строку, +показать экран заданное время, transition left-to-right, fade in/out, +прервать ожидание клавишей. Текст и меню должны использовать один renderer. + +Критерий: последовательность и музыкальные точки совпадают с SDLPoP; +Sprinter build screen не сдвигает оригинальный soundtrack. + +## 8. Demo level 0 + +- Добавить на HDD `res2000.bin`. +- Загружать уровень обычным loader, но выставлять demo HP и demo mode. +- Воспроизводить `demo_moves` как синтетический источник `control_*`. +- Пользовательский ввод прерывает демо и начинает новую игру. +- Достижение demo end room (у SDLPoP — 24), смерть или конец скрипта + возвращают в `TITLE`. +- Pause menu, QuickSave и cheats в demo недоступны. +- RNG демо и начальное состояние должны быть детерминированы. + +Критерий: без ввода attract loop не требует перезапуска процесса; +title -> demo -> title повторяется неограниченно. + +## 9. Глобальный таймер + +Состояние: минуты, тики и флаг показа. Таймер создаётся при New Game, +переносится между уровнями и входит в QuickSave. + +Правила `VANILLA`: + +- на pause menu, загрузке HDD, QuickSave/QuickLoad время не идёт; +- игровые тики следуют темпу логического кадра, а не частоте render loop; +- поведение во время level-end sound и cutscenes сверяется буквально с + SDLPoP; +- после Джаффара/на финальном уровне время не должно вызвать поражение; +- ноль времени переводит приложение в `TIME_EXPIRED`. + +Критерий: одинаковый игровой отрезок в NORMAL даёт то же уменьшение времени, +что SDLPoP; сохранение/загрузка не добавляет и не отнимает тики. + +Реализация FG4 живёт одним модулем `src/pop_timer.c` в bank 9: +`60:719`, 720 тиков на минуту, счёт только в живом игровом кадре. Settings +хранит `TIME LIMIT: 60 MIN / UNLIMITED` в `POP.CFG`; старый семибайтный v1 +payload по-прежнему читается как `60 MIN`. Читы таймера повторяют SDLPoP, +но из-за занятого `+/-` используют F7 (−1 минута, не ниже одной) и F8 +(+1 минута). Состояние входит в QuickSave v4. + +## 10. Cutscene engine + +Сцены SDLPoP состоят из небольшого набора повторяемых команд. Вместо набора +крупных C-функций нужен компактный интерпретатор: + +```text +SET_ACTOR actor +SET_POS x,y,dir +START_SEQ seq +WAIT_FRAMES n +PLAY_SOUND id +WAIT_SOUND +SET_HOURGLASS frame +SET_SAND state +FLASH color,frames +FADE_IN / FADE_OUT +CLEAR_ACTOR actor +END +``` + +Скрипты — `const` в холодном банке или подготовленный бинарный ресурс. +Interpreter обязан: + +- исполнять один шаг/кадр без блокирующих длинных циклов; +- поддерживать пропуск сцены; +- при пропуске выполнять cleanup и выходить в заранее заданное состояние; +- освобождать PV-ресурсы перед загрузкой игрового тайлсета; +- не разрешать pause menu/QuickSave внутри сцены. + +Порядок переноса: intro, 2/6, 4, 8, 9, 12, time expired, ending. Сцена 12 +выбирает короткий или обычный вариант по остатку времени. + +## 11. Переходы между уровнями + +Таблица сценария должна быть отдельна от таблиц механики уровня: + +```c +typedef struct { + uint8_t level; + uint8_t pre_cutscene; + uint8_t show_level_number; + uint8_t ending_rule; +} pop_level_flow_t; +``` + +Особые правила: + +- New Game начинает уровень 1; +- перед 2/4/6/8/9/12 запускается сцена; +- 12 -> 13 остаётся бесшовным; +- после победы над Джаффаром переход идёт в 14; +- номер 14 не показывается; +- вход в комнату 5 уровня 14 переводит в `ENDING`; +- значения больше 14 недопустимы и дают диагностическую ошибку, а не + попытку открыть файл. + +## 12. Ending и Hall of Fame + +Ending: + +1. загрузить PV-набор; +2. встреча Kid и Princess; +3. объятие; +4. появление мыши; +5. ending music; +6. финальные story/title экраны; +7. переход в Hall of Fame. + +Hall of Fame хранится на HDD в отдельном версионированном `POP.HOF`. +Сохраняются имя и результат; ввод имени использует тот же текстовый/UI слой. +Повреждённый или неизвестный формат означает пустую таблицу, но не мешает +запуску игры. После показа — возврат в `TITLE`. + +## 13. Удаление уровня 15 + +Отдельный ранний этап, чтобы новый flow не наследовал лишний маршрут: + +- убрать `res2015.bin` из `LVL_NUMS` и HDD image; +- заменить последний игровой уровень на 14; +- остановить Shift+L и прочую навигацию на 14; +- удалить `POP_POTIONS_LEVEL` и специальный половинный урон синих зелий; +- исключить copy protection из конфигурации и меню; +- добавить тест: после уровня 14 приложение входит в ending и никогда не + запрашивает `res2015.bin`. + +## 14. Этапы реализации + +Легенда аудита: **✓** — критерий этапа закрыт; **~** — код существует, но +критерий приёмки ещё не закрыт; **○** — не начат. Статус отражает состояние +исходников и последней MAME-проверки на 2026-08-24, а не только наличие +модуля в bank 9. + +| этап | статус | результат и фактическое состояние | критерий приёмки | +|---|---|---|---| +| **FG0** | ✓ | `POP_LEVEL_LAST=14`, HDD содержит `res2000..res2014`; `t_flow` отвергает 15 | HDD не содержит res2015; переход выше 14 невозможен | +| **FG1** | ~ | автомат `pop_app` и `t_app` реализованы; сквозной ресурсный lifecycle и контроль EMM/FD ещё не измерены | старт/рестарт/выход проходят без рекурсии и утечки EMM | +| **FG2** | ✓ | QuickSave/QuickLoad с `POP.SAV` и `POP.BAK`; отдельно проверен в MAME 2026-08-22 | критерии `quicksave_plan.md`, включая POP.BAK | +| **FG3** | ✓ | pause menu, Settings, подтверждения и двойной буфер реализованы; меню проверялось в MAME; добавлены SDLPoP-звуки навигации и защита CBL вокруг полного redraw/файловых операций | Resume/Save/Load/Restart/Settings/Quit работают | +| **FG4** | ~ | `pop_timer`, настройка unlimited, F7/F8 и состояние QuickSave реализованы; есть host-тест, но нет буквального сравнения темпа со SDLPoP на всех переходах | совпадение с SDLPoP и корректный save/load | +| **FG5** | ~ | text/full-screen/fade примитивы есть; для входа в первый уровень и границ уровней выбран мгновенный чёрный барьер без fade: CBL и яркая новая палитра включаются только после подготовки обеих страниц; Level 1 проверен в MAME | тестовые экраны и переходы на Sprinter | +| **FG6** | ~ | title-ресурсы и порядок кадров реализованы; Enter на title и Esc на первом story в MAME переводят прямо в `FIRST_LEVEL`, минуя demo; полная cold-boot приёмка fade остаётся в FG5 | основной титул/Presents/название/Mechner идут в точном порядке `show_title()`; Enter/Space/Esc/стрелки прерывают ожидание; story/intro продолжит FG8 | +| **FG7** | ✓ | level 0, исходная таблица `demo_moves`, demo HP=4 и блокировка игрового UI реализованы; исправлены зеркалирование auto-control, боевой AI Кида и завершение после смерти; в MAME demo проходит разворот/зацеп, доходит до боя и возвращается в attract-цикл без повторного убийства | `res2000.bin`, исходная `demo_moves`, demo HP=4; бесконечный attract loop, любой ввод начинает чистую новую игру; Pause/QuickSave/читы/таймер отключены | +| **FG8** | ~ | data-driven interpreter и покадровый PV intro работают; в MAME проверены актёры, факелы, звёзды 1x1, часы/песок, palette-0 lightning и foreground-колонна; Enter/Esc переводят прямо в `FIRST_LEVEL`; временный темп 12,5 FPS и TODO точного pacing записаны в `impl_diff.md` | story/PV intro проходит, любой raw-ввод пропускает его без удержания EMM-страниц | +| **FG9** | ~ | `pop_flow` корректно маршрутизирует 2/4/6/8/9/12 и ветку <=5 минут (`t_flow`); 2/4/6 и длинная 12 уже обновляют часы, песок, факелы и звёзды каждые 5 кадров Sprinter; длительности всех веток сверены с SDLPoP: 2/4/6/12 — 2,6 с, 8 — 6,0 с, 9 — 7,2 с; входная клавиша gameplay/Shift+L поглощается до сцены, а новое нажатие делает skip; анимации мыши/Princess в 8/9 и разворот Princess в короткой 12 ещё статичны | таблица flow переводит в CUTSCENE ровно перед 2/4/6/8/9/12; scene 12 выбирает короткий вариант при <=5 минутах | +| **FG10** | ~ | переход TIME_EXPIRED и экран существуют, но это ещё статическая PV-стадия; сквозной MAME-маршрут не принят | PV-сцена истечения с пропуском, затем возврат на title/attract; новая игра сбрасывает таймер | +| **FG11** | ~ | room 5 уровня 14 переводит в ENDING (`t_flow`); объятие/мышь заменены статической стадией, полный маршрут не принят | room 5 уровня 14 переводит в ENDING; PV-финал и Hail-экран возвращают управление оболочке | +| **FG12** | ~ | версионированный `POP.HOF`, ввод имени и восстановление после повреждённого файла реализованы; нужна сквозная MAME-проверка ending → HOF → title | версионированный `POP.HOF`, ввод имени raw-клавиатурой, повреждённый файл = пустая таблица, затем title/attract | + +## 15. Проверки + +- Host-тест автомата: все допустимые переходы и отсутствие уровня 15. +- Host-тест cutscene interpreter на синтетическом скрипте и skip в каждой + ожидающей команде. +- Host-тест demo input: одинаковый seed даёт одинаковый поток управления. +- MAME: cold boot -> build info -> title -> demo -> title. +- MAME: новая игра -> принудительный переход по всем pre-level scenes. +- MAME: time expired и пропуск сцены. +- MAME: 13 -> 14 -> room 5 -> ending -> HOF -> title. +- Проверка EMM/FD до и после каждого состояния: число страниц и открытых + файлов возвращается к базовому. +- `make size-check`; крупный cold-код размещать в банках и отдельно следить + за лимитом 16 КБ каждого банка. + +## 16. Не входит в план + +- уровень 15 и copy protection; +- моды и выбор levelset; +- replay/recording; +- точная эмуляция SDL video/controller options; +- профиль ENHANCED и индивидуальные switches fixes. diff --git a/applications/SprPoP/docs/gates_spikes_plan.md b/applications/SprPoP/docs/gates_spikes_plan.md new file mode 100644 index 0000000..3734abb --- /dev/null +++ b/applications/SprPoP/docs/gates_spikes_plan.md @@ -0,0 +1,279 @@ +# Интерактивные объекты (кнопки/гейты/пики) + HP/смерть — ПОДРОБНЫЙ план + +> **Статус: РЕАЛИЗОВАНО (2026-08-01).** Все фазы плана (P0 персистентный +> per-room `room_modif`, S пики, B кнопки+ворота) сделаны и играются: +> `src/pop_trob.c` (trob-диспетчер, `LINKLOC`/`LINKMAP`, ворота, дверь +> уровня, факелы, зелья), `pop_map.c` (HP, смерть на пиках, урон падения), +> `pop_redraw.c` (пометки перерисовки вместо прямых блитов). Ограничения +> из §0 закрыты: тайлы персистентны, HP/смерть есть, loose обобщён в trob; +> L3-вверх (climb-up в комнату сверху) тоже сделан (`pop_leave_dir = 3`). +> Из §5 остаётся открытым только **переход на следующий уровень через дверь +> уровня** — он вынесен в `levels_plan.md`. +> +> **Документ оставлен как СПРАВОЧНИК**, а не как план: §1 (раскладка +> объектов уровня 1 по комнатам, декод связей кнопка→цель) и §2 (точные +> ссылки на механику SDLPoP) продолжают экономить время при отладке. +> Текущие задачи — `../TASKS_OPEN.md`. + +Составлен 2026-07-20. Документ самодостаточный: рассчитан на старт +«с чистого листа» (пустой контекст). Всё сверено с +`applications/PoP/SDLPoP/src/` и данными `res2001.bin`. + +Правило проекта (см. `applications/PoP/CLAUDE.md`): **SDLPoP — источник истины**, +перед кодингом читать соответствующий код seg*.c, не гадать. + +--- + +## 0. КОНТЕКСТ: текущее состояние `applications/SprPoP` (что уже готово) + +SprPoP — живой прототип порта PoP: комната 1 уровня 1 живой композицией +тайлов + Kid (анимация/управление/коллизия/падение/зацеп/переходы). Собрать: +`cd applications/SprPoP && make`. Тест в MAME: см. memory +`mame_mcp_bridge`/`mame_hdd_test_disk` (канонический цикл: `make` → пересобрать +`mame/v306/IMG/test_hdd.chd` через `toolchain/make_hdd.sh` со всеми ассетами → +рестарт `run_bridge.sh` → `resume` → ~13с бут → `type_string("d:{ENTER}SprPoP.exe{ENTER}")`). +Отладка: клавиши `1`=freeze / `2`=resume в SprPoP; MCP-мост `mame-z80` +(read_logical_memory, set_breakpoint, disassemble); адреса символов — +`.sprinter-cc-build/.sprinter-cc-sprpop/sprpop.map` (сдвигаются при пересборке!). + +### Модули (все в `applications/SprPoP/`) +- `sprpop.c` — главный цикл (дабл-буфер 2 стр.), `enter_room(room)`, + обработчики переходов. File-static рабочие массивы (W2): + `room_fg[30]`, `room_bg[30]`, `lcol_fg/lcol_bg[3]`, `rcol_fg/rcol_bg[3]`, + `below_fg[10]`, `cur_room`. +- `pop_level.c/.h` — **уровень из файла** (Фаза L1): + - `pop_level_load("res2001.bin")` — читает сырой blueprnt в EMM-страницу + (данные с offset `0x100`, ISR-стаб как атлас). + - `pop_room_load(room, fg,bg, lcol_fg,lcol_bg, rcol_fg,rcol_bg, below_fg)` — + извлекает комнату (fg маскирован `&0x1F`, bg raw) + срезы соседей: + leftcol=col9 левого соседа, rightcol=col0 правого, belowrow=row0 нижнего. + - `pop_room_link(room, side)` — связь (side 0=L,1=R,2=U,3=D; 0=нет). + - `pop_level_start_room/pos/dir()`. +- `pop_bg.c/.h` — отрисовка тайлов (порт seg008 draw_tile), fore-окклюзия над + Kid (`pop_fore_over_char`, порт set_char_collision+redraw_at_char/char2), + **loose-полы** (shake/bake/mob). `draw_tile` — статическая, знает + `draw_gate_back` (грань гейта из левой комнаты). +- `pop_kid.c/.h` — анимация Kid (интерпретатор seqtbl `play_seq`, порт seg006), + `Kid` struct (frame,x,y,dir,curr_col,curr_row,action,fall_x,fall_y,repeat, + curr_seq); `knock`-флаг; `kid_cur_dx/flags`, `kid_fp_*` (футпринт). +- `pop_ctrl.c/.h` — ввод (порт seg005 control) через ``. +- `pop_map.c/.h` — коллизия/физика (порт seg005/006). Ключевое: + - `pop_map_set(fg)` — карта текущей комнаты. + - `pop_map_set_edges(l,r,u,d, lcol_fg, rcol_fg)` — связи + кромки швов для + коллизии (`get_tile(col=-1)`=lcol, `get_tile(col=10)`=rcol; порт + find_room_of_tile). + - `pop_phys_tick()` — кадр физики (fall/land/wall/knock/leave). + - Переходы: `pop_fell_out` (вниз, y>=211), `pop_leave_dir` (1=left,2=right, + x∓140). + - **loose-состояние**: `pop_loose_modif[30]` (публично, читает pop_bg), + `loose_bake[30]`, `loose_rest[30]` (static); `pop_loose_tick()`, + `pop_loose_reset()` (сброс при смене комнаты). + +### Что сделано по фазам +- **L1** — данные уровня из файла (room1_data.h удалён). Коммит `21f978d`. +- **L2** — переход в комнату снизу (провал/спуск), фиксы окклюзии. Коммит `7f3e32d`. +- **L3** — переходы вбок (право+лево) через швы. **Не закоммичено** на момент + написания (вместе с этим планом). **L3-вверх (climb-up в комнату сверху) — + НЕ сделано.** +- **Loose-полы** (тряска knock / падение mob+окклюзия) — коммит `8b30dc2`. + +### Известные ОГРАНИЧЕНИЯ (важно для этого плана) +1. **Нет персистентности тайлов**: `enter_room`→`pop_room_load` каждый раз + перезагружает ИСХОДНЫЕ тайлы из level-страницы. Изменения (упавший loose, + открытый гейт) при повторном входе ТЕРЯЮТСЯ. Для кнопок/гейтов это + блокер (см. P0). +2. **Нет HP/смерти** Кида (нужно для пик). +3. Loose-механика — частный случай trob (нужно обобщить). + +--- + +## 1. ДАННЫЕ УРОВНЯ (формат, offsets, объекты) + +Сырой `res2001.bin` (2305 Б) = blueprnt DAT 1.0 (Table 6 в +`POP-DAT-FormatSpecifications.txt`). Читается в EMM-страницу с offset `0x100`. +Тайл-код = байт `& 0x1F`; верхние биты (модификатор BLUETYPE) сейчас отброшены. + +| Блок | Offset | Размер | +|------|--------|--------| +| foretable (fg) | 0 | 720 (24 комн × 30) | +| backtable (bg=modifier) | 720 | 720 | +| **LINKLOC** (doorlink1) | **1440** | 256 | +| **LINKMAP** (doorlink2) | **1696** | 256 | +| links (roomlinks) | 1952 | 96 (24×{L,R,U,D}) | +| start_position | 2112 | 3 (room,pos,dir) | + +Тайл-коды: `0x00`empty `0x01`floor `0x02`**SPIKE** `0x03`pillar `0x04`**GATE** +`0x06`**DROP-кнопка(closer)** `0x0B`loose `0x0F`**RAISE-кнопка(opener)** +`0x10`lvldoor-L `0x11`lvldoor-R `0x13`torch `0x14`wall. + +### Объекты уровня 1 (по комнатам) +``` +room 5: DROP(0,2)m11 RAISE(0,4)m9 GATE(0,5)m2 RAISE(0,6)m8 GATE(0,9)m1 +room 6: RAISE(0,2)m7 SPIKE(2,3) SPIKE(2,4) <-- тестовая +room 7: RAISE(0,2)m6 GATE(0,9)m2 +room 8: RAISE(0,6)m5 GATE(0,9)m2 RAISE(1,7)m4 +room 9: RAISE(0,0)m3 (+ lvldoor(1,3)/(1,4) — выход на level2, отложено) +room12: RAISE(0,3)m2 GATE(0,9)m2 SPIKE(2,4) +room10/13/14/16/19/24: только SPIKE +room20: DROP(1,4)m1 RAISE(1,7)m0 +``` +(m = modifier тайла = ИНДЕКС в LINKLOC/LINKMAP.) + +### Room6 (тестовая) — раскладка +``` +fg row0: 13 01 0F 00 03 01 01 13 01 03 (0,2)=RAISE-кнопка, (0,0)/(0,7)=torch +fg row1: 14 14 14 00 14 14 14 14 14 14 (1,3)=empty +fg row2: 14 14 14 02 02 14 14 14 14 14 (2,3)(2,4)=SPIKE +links: L=8 R=2 U=5 D=0 +``` +- **Шахта пик**: col3 (row0=empty, row1=empty, row2=spike) + col4 (spike). +- **Гейт, видимый у ЛЕВОЙ кромки room6, — это гейт room8 (0,9)**, отрисованный + в col0 room6 через левый шов (L=8, leftcol=col9 room8). В room6 гейта НЕТ. + +### Связь кнопка→цель (декод LINKLOC/LINKMAP), проверено: +- `get_doorlink_tile(i) = d1[i] & 0x1F` +- `get_doorlink_next(i) = !(d1[i] & 0x80)` (0 = конец цепочки) +- `get_doorlink_room(i) = ((d1[i]&0x60)>>5) + ((d2[i]&0xE0)>>3)` +- `get_doorlink_timer(i) = d2[i] & 0x1F` +- где `d1`=LINKLOC@1440, `d2`=LINKMAP@1696. Цепочка: idx++ пока next. + +**Кнопка room6 (0,2) mod=7 → цель: room8 tile(0,9) = ГЕЙТ.** Подтверждено +в SDLPoP-скринах: нажатие кнопки поднимает решётку у левой кромки room6. +Связь КРОСС-КОМНАТНАЯ (кнопка в room6, гейт в room8) и задаётся таблицей, +а НЕ позицией. Кнопка может открывать НЕСКОЛЬКО гейтов в разных комнатах. + +--- + +## 2. МЕХАНИКА SDLPoP (точные ссылки) + +### 2.1 Trob-система (анимируемые тайлы) +- `add_trob(room,tilepos,type)` seg007:0A5A — в список анимируемых. +- Каждый кадр `redraw_needed_tiles`/`process_trobs` продвигает; диспетч по + типу тайла → `animate_button/animate_door/animate_spike/animate_loose` + (seg007:0033+ таблица `animate_*`). +- Состояние тайла хранится в `curr_room_modif[tilepos]` (per-room modifier). +- У нас есть частный случай для loose (`pop_loose_tick`+`pop_loose_modif[30]`). + +### 2.2 Пики (spike) +- **Триггер выдвижения** — `check_spike_below()` seg006:1199 (зовётся в + физике Кида каждый кадр): для каждой колонки футпринта Кида + (`get_tile_div_mod_m7(char_x_left)`..`char_x_right`) идёт ВНИЗ от + `Char.curr_row` через НЕ-floor тайлы; если встретил `tiles_2_spike` → + `start_anim_spike(room,tilepos)`. → Кид у края (0,2) правым краём задевает + col3 → скан вниз col3 (empty/empty/spike) → пики вылезают. +- `start_anim_spike` seg007:596: если `modif<=0`: `modif==0` → add_trob(type1) + + звук; `modif<0` (кроме 0xFF disabled) → `modif=0x8F`. +- `animate_spike` seg007:317: автомат по modif — выдвиг `++modif` (1..4; на 5 → + `0x8F`; на 9 → 0, trob кончился); убирание `& 0x80` → `--modif` (на 0 → `=6`). + `0xFF` = disabled (не двигать). +- `is_spike_harmful` seg007:1178: modif `0/-1`→0 (безопасно); `<0`→1; + `1..4`→2; `>=5`→0. +- **Смерть**: `check_spiked` seg006:0968 — если тайл под Кидом = spike И harmful + И кадр бега (7..14) / старта прыжка (34..39) с harmful>=2, ИЛИ кадр приземления + (43/26) с harmful!=0 → `spiked()`. Падение на пики — отдельный путь (см. + `is_dead` seg006:1907, frame_177_spiked). Осторожный ШАГ по невыдвинутым — ок. + +### 2.3 Кнопки +- `trigger_button(playsound, button_type, modifier)` seg007:0C53: modifier = + индекс LINKLOC. `link_timer = get_doorlink_timer(mod)`; если `!=0x1F` + (не заклинено): `set_doorlink_timer(mod,5)`; если был `<2` → `add_trob` + (кнопка нажимается) + звук; затем `do_trigger_list(mod, button_type)`. +- `do_trigger_list` seg007:09E5: идёт по цепочке LINKLOC от idx, для каждой + цели `trigger_1(target_type,room,tilepos,button_type)` → если >=0 + `add_trob(room,tilepos,result)`. +- `animate_button` seg007:0D3A: `timer=get_doorlink_timer(mod)-1`; + `set_doorlink_timer(mod,timer)`; `timer<2` → кнопка отжимается. +- Когда Кид ВСТАЁТ на кнопку: `check_press`-путь seg006 (opener → trigger_button, + closer → тоже; если Кид мёртв — `died_on_button`). RAISE=`tiles_15_opener` + (0x0F), DROP=`tiles_6_closer` (0x06). + +### 2.4 Гейты +- `trigger_1` seg007:0999 → для `tiles_4_gate` → `trigger_gate`. +- `trigger_gate(room,tilepos,button_type)` seg007:092C: modif = высота открытия. + opener: `0xFF`→игнор; `>=188`(открыт)→держать `238`; иначе `modif=(modif+3)&0xFC`, + return 1 (открывать). closer/иначе: `modif!=0` → return 3 (закрыть быстро). +- `animate_door` seg007:0522: анимация открытия/закрытия; `door_delta[]={-1,4,4}`, + `gate_close_speeds[]={0,0,0,20,40,60,80,100,120}`. Гейт медленно закрывается + после истечения таймера кнопки. +- Отрисовка: кадры гейта; у нас `draw_gate_back` в pop_bg (грань из левой комн.). +- **Проходимость**: Кид блокируется недостаточно открытым гейтом (коллизия + как стена, порог по высоте открытия); проходит при `modif` открытом. + +--- + +## 3. Поправки к описанию пользователя (что важно) + +1. Гейт НЕ в room6 (0,0) — он в **room8 (0,9)**, виден через левый шов; связь + кросс-комнатная (по таблице LINKLOC, не по позиции). +2. Кнопка может открывать несколько гейтов в разных комнатах. +3. Пики выдвигаются по `check_spike_below` (Кид над колонкой с пиками), + имеют состояния (не всегда смертельны), убираются со временем. +4. **Кросс-комнатное состояние тайлов ДОЛЖНО ПЕРСИСТИТЬ** — блокер (см. P0). +5. HP/смерть — новая подсистема. +6. Кнопка сама анимируется (нажата/отжата). + +--- + +## 4. ПЛАН РЕАЛИЗАЦИИ (фазы) + +### P0 — Персистентное per-room modifier-состояние + trob-каркас (ПРЕРЕКВИЗИТ) +**Проблема:** нажатие кнопки в room6 меняет modif гейта room8 (не текущей +комнаты); при входе в room8 нужно отрисовать гейт в текущем состоянии. Плюс +это чинит «re-entry восстанавливает тайлы» (loose/гейты). + +Дизайн (предложение — уточнить в реализации): +- Массив `room_modif[24][30]` (или lazy per-visited-room) — modifier каждого + тайла каждой комнаты. Инициализируется из bg уровня при первой загрузке + комнаты; далее ЖИВЁТ (не перезагружается). ~720 Б — влезает в W2 (или в + EMM-страницу уровня рядом с данными: остаётся >10КБ). +- `enter_room` берёт modif из `room_modif[room]`, а fg — из level-страницы + (fg почти не меняется; исключения — loose→empty, надо тоже персистить: либо + отдельный `room_fg_override`, либо флаг «loose упал»). +- Обобщить loose-trob: единый список trob (room,tilepos,type) + диспетчер + `animate_*` по коду тайла. Loose (`pop_loose_*`) — перевести на него. +- Кросс-комнатный trigger: `add_trob` в НЕ текущую комнату меняет + `room_modif[room][tilepos]`; анимация продвигается даже для невидимой комнаты + (в оригинале — да; можно упростить: для невидимой комнаты гейт сразу в + финальном состоянии, анимировать только при видимости — решить при реализации). + +### Фаза S — Пики (самодостаточно; отладит HP/смерть) +Порядок: +1. `room_modif` для пик (из P0 или временно локально). +2. `check_spike_below` (порт seg006:1199) — в `pop_phys_tick`. +3. `start_anim_spike` + `animate_spike` (порт seg007) — состояние в modif. +4. `is_spike_harmful` + `check_spiked` (порт seg006:0968). +5. **HP/смерть**: ввести `hitp_curr` (старт напр. 3); `take_hp`; при пиках — + мгновенная смерть; seq смерти (`seq_22_crushed`/`frame_177_spiked`..185); + анимация смерти; респавн (kid_init на старте или чек-поинт). +6. Отрисовка: кадры выдвижения пик. В pop_bg есть `SPIKES_FRAM_RIGHT` — нужны + pop-out кадры (spikes_fram по modif) + fore над Кидом. + +### Фаза B — Кнопки + гейты (нужен P0) +Порядок: +1. Доступ к LINKLOC/LINKMAP из pop_level (добавить геттеры doorlink1/2[i] с + маппингом W0 или скопировать таблицы в W2 при load — 512 Б). +2. `pop_map` детект «Кид встал на кнопку» (check_press-путь) → `trigger_button`. +3. `trigger_button` → таймер + `do_trigger_list` (обход цепочки) → + `trigger_gate` для целей → изменить `room_modif[целевой]`. +4. `animate_button` (кнопка отжимается) + `animate_door` (гейт откр/закр + + авто-закрытие). +5. Отрисовка гейта (кадры по modif) через ЛЕВЫЙ шов (гейт room8 в room6) + + при входе в room8. Расширить `draw_gate_back`/добавить `draw_gate`. +6. Коллизия: закрытый гейт = стена (порог по высоте открытия); открытый — + проход. Учесть кросс-комнатный гейт на шве (проход влево room6→room8). + +**Порядок фаз:** P0 → S → B. S в основном независим (кроме HP/trob-каркаса), +но проще и отладит смерть/анимацию; B требует P0 (кросс-комнатное состояние). + +--- + +## 5. Открытые вопросы / грабли +- Персистентность `fg` для loose (тайл→empty): решить в P0 (override-массив или + флаг), иначе упавший loose «вернётся». +- Анимация trob в НЕВИДИМОЙ комнате: упростить (финальное состояние сразу) или + портировать честно. +- Двоебуфер: любой транзиент (пики/гейт/кнопка) финализировать перерисовкой + «покоя» на ОБЕИХ страницах (урок из loose — см. memory `pop_loose_floors`). +- Дверь уровня (lvldoor room9) + переход на level 2 — ОТДЕЛЬНО, отложено. +- L3-**вверх** (climb-up в комнату сверху) — ещё не сделан; можно закрыть до + объектов или параллельно. diff --git a/applications/SprPoP/docs/host_tests_plan.md b/applications/SprPoP/docs/host_tests_plan.md new file mode 100644 index 0000000..6f4c5ad --- /dev/null +++ b/applications/SprPoP/docs/host_tests_plan.md @@ -0,0 +1,142 @@ +# План: модульные тесты движка SprPoP под ucsim_z80 + +Обвязка общая — `testkit/` в корне репозитория (там же объяснение, почему +прогон именно под z80, а не хостовым gcc). Наборы лежат в +`../SprPoP/tests-host/`. + +Задача плана: **перестать чинить одно и то же дважды**. За два прогона +уровня 1 (2026-08-03) закрыто восемь корней, и часть из них — регрессии +соседней механики, внесённые предыдущим фиксом. Такие вещи ловятся тестом +за миллисекунды, а в MAME — часами ручного вождения Кида. + +## Что уже есть + +| набор | модуль | статус | +|-------|--------|--------| +| `t_geom` | `pop_geom.c` | 39 проверок, включая побитовую сверку asm-LCG с 32-битной формулой на 128 шагах | + +`pop_geom.c` выбран первым, потому что не тянет ничего за собой. Дальше +начинаются швы. + +## Фаза 1. Два шва (блокирует всё остальное) + +### 1.1 Доступ к странице уровня + +`pop_level.c` ходит по абсолютным адресам: `gfx_w0_map(lvl_page)`, затем +разыменование `(uint8_t *)(LVL_DATA_OFF + …)`. В тестовом бинаре это +обращение в никуда. + +Нужен макрос `W0PTR(off)`: + +- на таргете — `((uint8_t *)(off))`, то есть ровно как сейчас; +- в тестах — смещение в обычном массиве-подложке. + +Правка механическая и компайл-таймовая, на размер продукта не влияет. +Заодно снимает магию абсолютных констант из тела функций. + +Тестовая подложка должна уметь: загрузить синтетическую комнату (10×3 +байта fg + mod) и целый синтетический уровень на 24 комнаты, чтобы +проверять межкомнатные вещи. + +### 1.2 Журналирующий рендерер + +Вместо `pop_bg.c`/`pop_cdraw.c` в тестовый бинарь линкуется модуль с теми +же прототипами, который **не рисует, а записывает вызовы**: какой тайл +помечен к перерисовке, каким кодом, с каким счётчиком страниц. + +Это не обход проблемы, а самостоятельная ценность: `BUG-GATE-ANIM-1` был +ровно такой формы — ворота меняли состояние, но пометка на перерисовку не +ставилась. Проверяется утверждением, а не глазами. + +Минимум, который надо перехватывать: `pop_set_redraw`, +`pop_set_redraw_above`, `pop_loose_mob_spawn`, `pop_gate_redraw`. + +## Фаза 2. Регрессионные кейсы из `BUGS_CLOSED.md` + +После швов `BUGS_CLOSED.md` превращается в готовую спецификацию: у каждой +записи есть симптом и ожидаемое поведение. Кандидаты, которые ловятся +логикой (без отрисовки и без железа): + +| баг | что закрепить тестом | +|-----|----------------------| +| `BUG-LVLSTATE-1` | запись тайла переживает выход из комнаты | +| `BUG-RESPAWN-1` | рестарт уровня возвращает ВСЕ тайлы из эталонной копии | +| `BUG-RESPAWN-2` | рестарт возвращает таблицу стражей; убитый снова жив | +| `BUG-GATE-ANIM-1` | смена состояния ворот ставит пометку `POP_RD_GATE`; закрывающиеся — на обе страницы, открывающиеся — на одну | +| `BUG-COLL-1` | `check_collisions` сканирует ряд справа налево и выбирает НАИМЕНЬШУЮ занятую колонку | +| `BUG-STANDUP-1` | `bumped_floor` у трупа (`alive >= 0`) только выравнивает и не трогает последовательность | +| `BUG-DEATH-1` | `hitp_curr == 0` при живом Киде переводит его в «умирает» ровно один раз | +| `BUG-LOOSE-2` | кусок, начавший падать, долетает и кладёт щебень ПОСЛЕ смены комнаты | +| `BUG-CEIL-2` | loose-плита ряда 2 верхнего соседа живёт как «ряд −1» | + +`BUG-LOOSE-2` стоит взять первым: он до сих пор помечен в `BUGS_OPEN.md` +как непроверенный именно потому, что гонку «уйти из комнаты раньше, чем +долетит плита» через мост MAME воспроизвести не удалось. На уровне логики +это несколько строк — заспавнить кусок, сменить комнату, тикать до +приземления, проверить щебень в данных уровня. + +Не берутся (нужна картинка либо железо): `BUG-DOOR-CLIP`, `BUG-CEIL-1`, +`BUG-CEIL-3`, `BUG-OCCL-1`, `BUG-KBD-4`, `BUG-3`. + +## Фаза 3. Сценарные тесты + +Сейчас шаг кадра размазан по `main()` в `sprpop.c`. Вынести его в +`pop_frame_tick()` — тогда появляются тесты вида «поставить Кида в +известное состояние, скормить N тиков ввода, проверить итог»: + +``` +дано: комната 5, Кид на кнопке (0,6) +когда: 40 тиков без ввода +тогда: комната по-прежнему 5, Кид на полу ряда 2 +``` + +Это тот самый BUG-STANDUP-1, который ловили потиковой трассой в MAME. + +Ввод подаётся не через `kbd_raw_down()`, а через подменяемый источник — +это же даст возможность проигрывать записанные сценарии. + +## Фаза 4. Дифф против SDLPoP + +`SDLPoP/src/` лежит в дереве, собирается на хосте, и там **уже стоят +отладочные трассы** (`DBG kidobj tilepos=…` в seg008, `DBG make_loose_fall` +в seg007). Значит эталон можно заставить печатать потиковую трассу +автоматически. + +Схема: общий формат скрипта ввода и общий формат трассы (тик, frame, x, y, +room, col, row, action, alive, hp). Гоняем обе реализации, диффим, первое +расхождение — номер тика и есть баг. Это ровно то, что делалось руками +через MAME, только бесплатно и повторяемо: `BUG-COLL-1` и `BUG-STANDUP-1` +такой дифф нашёл бы за секунды. + +**Лицензия.** SDLPoP — GPLv3, правило подпроекта — читать и переписывать, +не линковать. Оракул обязан быть **отдельным исполняемым файлом**, +общающимся через файлы трасс, а не слинкованным с нашим кодом в один +бинарь. + +Требование к детерминизму: сиды PRNG должны совпадать. У нас +`POP_PRANDOM_EXACT` даёт ту же последовательность, что в оригинале, и это +уже закреплено тестом `geom_lcg_matches_reference`. + +## Чего эти тесты не поймают + +Отрисовку, банки и W-окна, тайминги, клавиатуру — за этим остаётся MAME. + +И отдельный класс: **баги порядка вызовов**. Свежий пример — окно +fore-клипа (`pop_fore_set_clip`) одно на всех, и его ставит каждый, кто +рисует персонажа; когда порядок «Кид/страж» стал переменным, окно осталось +стражьим, и Кид нарисовался поверх передних столбов. Это не «функция +вернула не то», unit-тест такое не видит. Ловится инвариантом, +вкомпилированным в safe-сборку: «в момент `pop_fore_over_char` окно клипа +принадлежит Киду». Отдельный инструмент, дополняющий тесты. + +## Порядок работ + +1. Шов `W0PTR` + подложка уровня. +2. Журналирующий рендерер. +3. `BUG-LOOSE-2` — закрыть висящий вопрос. +4. Остальные кейсы из таблицы фазы 2. +5. `pop_frame_tick()` + сценарные тесты. +6. Дифф против SDLPoP. + +Правило приёмки: тест не считается написанным, пока не проверен мутацией — +сломать проверяемое место и убедиться, что набор краснеет. diff --git a/applications/SprPoP/docs/ideas_backlog.md b/applications/SprPoP/docs/ideas_backlog.md new file mode 100644 index 0000000..87a0878 --- /dev/null +++ b/applications/SprPoP/docs/ideas_backlog.md @@ -0,0 +1,91 @@ +# Идеи и вопросы «на подумать» (PoP) + +Не план работ, а список того, что осознанно отложено: каждая запись — +гипотеза с причиной, по которой её стоит проверить, и с тем, что мешает +сделать это прямо сейчас. + +## Зелье «переворот экрана» (upside-down) + +**Вопрос пользователя (2026-08-01).** Тайлы фона у нас лежат строками, а +кадры Кида/стражей — КОЛОНКАМИ (`transpose_cols` в `pop_pack_kid.py`, ради +бесплатного горизонтального зеркала). Значит вертикальный переворот для +персонажей заметно сложнее, чем для фона. Верно; но прежде чем это чинить, +надо знать три факта. + +**Факт 1 — когда оно вообще нужно.** Зелье переворота — тип 4 +(`proc_get_object`, `seg006.c:1885` → `toggle_upside()`). Скан всех уровней +по данным (`res200N.bin`, тайл 10 = зелье, тип в backtable): тип 4 +встречается **впервые на уровне 9** (две склянки), и больше нигде. Тип 3 +(перо, медленное падение) — уровень 7. То есть **до уровня 9 механика не +нужна вообще**, и «на первом этапе просто не реализовывать» — не компромисс, +а точное соответствие данным уровней 1..8. + +**Факт 2 — что именно делает оригинал.** НЕ переворачивает спрайты. +`flip_screen` (`seg009.c:1042`) → `flip_not_ega` (`seg009.c:1023`) меняет +местами СТРОКИ готового offscreen-буфера (top↔bottom, порядок пикселей +внутри строки не трогает — это вертикальное зеркало, не поворот на 180°). +Вызывается вокруг отрисовки кадра целиком (`seg003.c:296..301`): перевернул +буфер → дорисовал → перевернул обратно. Так что в оригинале это +post-process всего экрана, и вопрос «как перевернуть колоночный спрайт» +там просто не возникает. + +**Факт 3 — почему нам этот приём не подходит как есть.** У нас нет шага +«готовый offscreen → экран»: рисуем прямо в видеостраницу, а heal берёт фон +из ОЗУ-копии этой же страницы. Переворот всей страницы построчно — это +320×192 Б копирования КАЖДЫЙ кадр, что мимо бюджета на порядок. + +**Варианты, которые надо будет взвесить (не сейчас):** +1. **Предпечённые перевёрнутые атласы.** Второй набор кадров + Кида/стража, перевёрнутый по вертикали ещё в `pop_pack_kid.py` (там уже + есть транспонирование — добавляется одной строкой). Рантайм: выбор + набора + зеркальная арифметика Y. Память: ещё ~28 страниц EMM при + бюджете ~3.3 МБ — не проблема. Похоже, самый дешёвый по тактам путь. +2. **Фон рисовать с обратным Y** — для row-major тайлов строка остаётся + непрерывным accel-прогоном, меняется только адрес назначения; цена — + вызов на строку вместо вызова на тайл. Померить, прежде чем закладывать. +3. **Аппаратная помощь** — до проектирования проверить, есть ли у + акселератора направление копирования «вниз» (обратный инкремент адреса); + если есть, вариант 1 может и не понадобиться. Смотреть + `docs/new/06-accel.md` и `docs/reference/accel_r.txt`. + +**Почему не сейчас.** Уровни 1..8 этого не требуют, а к уровню 9 у нас уже +будет ответ на вопрос «сколько стоит кадр» (задачи CLIP-1/T-2) — без него +выбирать между вариантами выше бессмысленно. + +## Заменить генератор псевдослучайных чисел + +Сейчас стоит LCG оригинала, шаг на ассемблере (~1 020 тактов), бит-в-бит +совместимый с SDLPoP. Есть более дешёвые Z80-генераторы (86–148 тактов), +но потолок выигрыша — 2 814 тактов за кадр, 0.65 %, и он растворяется в +обёртках вызова. Тексты процедур, разбор качества и порядок действий — +`prng_alternatives.md`. Первый шаг там не про генератор: слить приведение +к диапазону в ту же asm-процедуру, чтобы на вызов был один `call`, а не три. + +## Отключать мышь на время игры + +**Гипотеза.** Мышь на Sprinter — источник прерываний (обёртки RST 30h, +см. memory `mouse_api`). Игре она не нужна вообще: управление — +raw-клавиатура (``), которую мы и так забираем у DSS целиком. +Значит каждое мышиное прерывание за кадр — украденные такты в бюджете, +который у нас и без того занят на 86 %. + +**Откуда взялось (2026-07-30).** При замере бюджета по 100 кадрам три +кадра выбились до 552–647 К тактов (1.28–1.51 кадра) при типичных 371 К. +Причиной оказалось движение мыши на ХОСТЕ: при неподвижной мыши 225 +кадров подряд прошли без единого превышения. То есть эффект реальный и +измеримый, просто в тесте он был наведён извне. + +**Что проверить.** +1. Есть ли у драйвера мыши (RST 30h) функция «выключить/включить» — + разобрать список из 14 обёрток; если нет явной, посмотреть, что делает + «hide cursor» и снимает ли она обработчик. +2. Сколько тактов реально стоит одно мышиное прерывание на нашем железе + (замер: breakpoint на входе ISR + totalcycles, при движении мыши). +3. Не ломает ли отключение выход в DSS: состояние обязано + восстанавливаться при `exit`, включая аварийный (atexit). + +**Почему не сейчас.** Выигрыш проявляется только когда игрок реально +двигает мышью, то есть в норме его нет; а риск оставить систему без мыши +после выхода — заметный. Делать после того, как закроем стражей и +займёмся бюджетом всерьёз (там же, где батчинг кроссбанковых вызовов и +возможный возврат `pop_bg` в резидент `--w3`). diff --git a/applications/SprPoP/docs/impl_diff.md b/applications/SprPoP/docs/impl_diff.md new file mode 100644 index 0000000..c660128 --- /dev/null +++ b/applications/SprPoP/docs/impl_diff.md @@ -0,0 +1,649 @@ +# Осознанные расхождения с SDLPoP + +Правило подпроекта (`../CLAUDE.md`): расхождение нашей реализации с +`SDLPoP/src/` — по умолчанию **баг у нас**. Этот файл — список исключений: +мест, где мы сознательно сделали иначе, потому что платформа/ABI/бюджет +кадра требуют другого, а НАБЛЮДАЕМОЕ поведение обязано совпадать. + +Формат записи: что делает оригинал → что делаем мы → почему → чем платим и +что проверять при регрессе. Если запись перестала быть верной (портировали +дословно, отказались от обхода) — удалять, а не оставлять «для истории»: +история в git. + +--- + +## D-1. История флагов перекрытия у бокового шва: сдвиг вместо тега комнаты + +**Файлы:** `src/pop_map.c` (`pop_coll_shift`, `pop_coll_invalidate`, +`check_collisions`), `src/sprpop.c` (`enter_room_side`). +**Связанный баг:** BUG-GATE-PASS-1 (`PoP/SprPoP/BUGS_CLOSED.md`). +**Дата:** 2026-08-09. + +### Как в оригинале + +`check_collisions` (seg004:0004) вместе с `get_row_collision_data` +(seg004:0185) держит **10 слотов** флагов перекрытия и рядом — +**параллельный массив номера комнаты**: + +```c +row_coll_flags_ptr[tile_col] = curr_flags; /* tile_col — колонка ВНУТРИ разрешённой комнаты (0..9) */ +row_coll_room_ptr [tile_col] = curr_room; /* и номер этой комнаты */ +... +for (short column = 9; column >= 0; --column) { + if (curr_row_coll_room[column] >= 0 && + prev_coll_room[column] == curr_row_coll_room[column]) { + if ((prev_coll_flags[column] & 0x0F) == 0 && + (curr_row_coll_flags[column] & 0x0F) != 0) + bump_col_left_of_wall = column; + ... +``` + +Ключ слота — пара **(колонка в своей комнате, номер комнаты)**. Решётка +комнаты 8 и до перехода 8→6, и после лежит в слоте 9 с `room = 8`: история +переживает смену комнаты, переход флага 0→1 виден, `bumped()` срабатывает. +Комнату оригинал резолвит на лету через `find_room_of_tile` (seg006:005D), +никакого кэша всех комнат у него нет. + +### Что делаем мы + +Индекс — **колонка ОТРИСОВАННОЙ комнаты**, диапазон −2…11 (14 слотов, +`COLL_C0`/`COLL_N`/`COLL_IDX`), номер комнаты рядом не хранится. При смене +комнаты тот же физический тайл менял бы слот на ±10, поэтому раньше история +просто выбрасывалась (`pop_coll_invalidate` → `prev = 3` = «уже +перекрывал» → бампа нет). Именно это и был BUG-GATE-PASS-1. + +Теперь при **боковом** переходе история не выбрасывается, а +**перенумеровывается**: `pop_coll_shift(∓10)` сдвигает `coll_curr`, +`coll_above`, `coll_below` на 10 слотов и заполняет освободившиеся +тройками. `enter_room_side` зовёт её сразу после `pop_map_set_edges`. + +Корректность держится на том, что `check_leave` двигает `Char.x` ровно на +∓140 = 10 тайлов по 14 px, и координата грани (`pop_x_bump[col + …]`) +сдвигается на те же 140 вместе с габаритом Кида, — **сами флаги +инвариантны**, меняется только номер слота. Сдвигаются `curr/above/below`, +а не `prev`: `prev` на следующем кадре всё равно перезапишет +`move_coll_to_prev`, выбирая источник как раз из этих трёх. + +Переходы **вверх/вниз** и все прочие входы в комнату (старт уровня, +респавн, чит-навигация) остаются на полной инвалидации: там колонки не +сдвигаются, но тайлы под ними принадлежат другой комнате — история +действительно недействительна. + +### Почему не дословно (вариант A) + +Дословный порт — 10 слотов + параллельный массив номера комнаты, индекс по +колонке разрешённой комнаты, бамп только при совпадении номеров; тогда +`pop_coll_invalidate` не нужен вовсе, история сама «не совпадает» там, где +колонка сменила комнату. + +Не взяли по одной причине: **десяти слотов нам не хватит**. Оригинал +перебирает узкое окно вокруг Кида (от `col(char_x_left_coll) − 1` до +`col(char_x_right_coll) + 2`), поэтому коллизии слотов у него практически +не случаются. Мы держим четырнадцать колонок (−2…11) — при узком окне это +не мешает, а вот в десять слотов колонки −2/−1 и 8/9 сядут поверх 8/9. + +**Окно перебора с 2026-08-09 у нас такое же, как в оригинале** (было: все +четырнадцать колонок каждый кадр). Признак годности слота при этом не +массив номеров комнат, как у оригинала, а ГРАНИЦЫ окна — четыре байта, +которые `move_coll_to_prev` переносит в `prev` вместе с флагами; сравнение +идёт по пересечению двух окон. Очистки массивов нет вовсе, то есть это +дешевле оригинала, а смысл тот же (у него слот вне окна помечен +`row_coll_room = −1` и в цикл бампа не попадает). `check_chomped_flags` +тоже ограничен окном — иначе протухшие слоты дали бы фантомный перемол. + +### Чем платим + +- Расхождение структур: если в будущем понадобится знать, из какой комнаты + пришёл тайл конкретного слота, этого у нас нет — придётся идти в вариант A. +- Границы окна надо переносить везде, где переносятся флаги: `pop_coll_shift` + двигает и их, `move_coll_to_prev` снимает их в `prev`. Забыть один из + переносов = молча потерять или, наоборот, разрешить лишний бамп. +- Сдвиг работает только для чисто горизонтальных переходов на ровно 10 + колонок. Любая будущая диагональ/иная ширина комнаты его сломает молча. +- `coll_last_row`: `pop_coll_invalidate` прячет прошлый ряд, чтобы + `pop_coll_shift` мог отменить инвалидацию. Порядок вызовов в + `enter_room_side` (сначала `pop_map_set_edges`, потом `pop_coll_shift`) + стал значимым. + +### Что проверять при регрессе + +Это сердце коллизии, вокруг которого разбирался BUG-SEAM-PINGPONG. После +любой правки здесь — прогон швов: + +1. Уровень 1, комнаты 6 ↔ 8, закрытая решётка, **обе** стороны. +2. Оба режима подхода: мелким шагом (упереться) и с разбега (не пройти + насквозь). +3. Проверить, что пинг-понг у шва не вернулся (экран не перескакивает + туда-сюда на кадре бампа о ворота). +4. `make -C SprPoP/tests-host` — наборы `t_wall`/`t_char` ходят по этой же + геометрии. + +--- + +## D-2. Кнопка в шве: перерисовываем, хотя оригинал не перерисовывает + +### Что делает оригинал + +Тайл-«трансформер» (кнопка, ворота, пика) перерисовывается только если он в +ОТРИСОВАННОЙ комнате: `redraw_11h` → `redraw_tile_height` → +`get_trob_pos_in_drawn_room` (seg007:0258), а та для `trob.room != drawn_room` +возвращает 30 — заведомо несуществующий tilepos, то есть «не рисовать». +Исключение сделано ровно одно — факелы (`animate_torch`, seg007:03CF, ветка +`trob.room == room_L && tilepos % 10 == 9`). + +Кнопка соседа слева при этом ВЛИЯЕТ на картинку: `get_tile_to_draw` +(seg008:253) подменяет нажатый `tiles_15_opener` на `tiles_1_floor`, а +`load_leftroom` (seg008:360) кладёт результат в `leftroom_[row]`, откуда он +приходит в `draw_tile` как `tile_left`. У пола правая грань есть, у кнопки +нет — значит в оригинале нажатие кнопки, видимой через левый шов, меняет +картинку только при следующей ПОЛНОЙ отрисовке комнаты. + +### Что делаем мы + +Перерисовываем шов сразу: `seam_row_sig` (sprpop.c) подмешивает в сигнатуру +ряда бит «кнопка нажата» (`pop_doorlink2(mod) & 0x1F > 1`) для тайлов +`0x0F`/`0x06`, и change-driven редрой `pop_room_redraw_seam_left` срабатывает +на нём так же, как на openness ворот. + +### Почему + +Голый порт давал видимый залип (SEAM-BUTTON-STALE, PoP/SprPoP/BUGS_CLOSED.md): кнопка +(1,9) комнаты 11 — она же (1,−1) комнаты 24 — оставалась нарисованной в том +состоянии, в каком была на входе в комнату, хотя связь срабатывала. Сигнатура +шва следила только за `room_modif`, а у кнопки `modif` — это ИНДЕКС LINKLOC, +константа уровня: нажатие живёт в `doorlinks2` и в сигнатуру не приходило +никогда. Добавить кнопку в сигнатуру — те же три сравнения на кадр, что уже +делались для ворот; воспроизводить артефакт оригинала смысла нет. + +### Чем платим + +- Резидент +200 Б (`_CODE` 24 739 → 24 939), куча W2 1795 → 1595 Б. Если + станет тесно — `seam_row_sig` переносится в банк 7 к + `pop_room_redraw_seam_left`, ценой одного трамплина на кадр. +- Сигнатура ряда стала разнотипной: для кнопки это булев бит, для остальных + тайлов — modif. Значения между собой не сравниваются (сравнивается только + ряд сам с собой), но при добавлении нового типа тайла в шов про это надо + помнить. + +### Что проверять при регрессе + +Уровень 5, кнопка нижних ворот комнаты 24 (она же (1,9) комнаты 11), оба +направления: нажать её из комнаты 11 и войти в 24; и наоборот — войти в 24 +поверху и наступить на неё, стоя в шве. Картинка кнопки обязана совпадать +со статусом ворот в обоих случаях. + +--- + +## Перо (медленное падение) ловит ТОЛЬКО Кида + +**Оригинал** (`fall_accel`, seg006:057C): `is_feather_fall` — глобальный флаг, +и медленное падение достаётся ЛЮБОМУ персонажу, который окажется в `Char`, +пока эффект жив. То есть страж, сошедший с уступа в те же секунды, парит +вместе с Кидом, хотя зелье пил не он. SDLPoP считает это багом и чинит +опцией `fix_feather_fall_affects_guards`. + +**Мы** берём поведение С ФИКСОМ: `pop_feather` проверяется вместе с +`Char.charid == CHARID_0_KID` — и в `fall_accel` (`pop_map.c`), и в опкоде +`JMP_IF_FEATHER` интерпретатора seqtbl (`pop_kid.c`), чтобы физика и анимация +не разъехались. + +**Чем платим.** Сцена, где страж падает при живом пере, будет выглядеть иначе, +чем в DOS-оригинале (у нас он падает нормально, там — парит). На уровне 7, +единственном с этим зельем, такой сцены нет: зелье в комнате 1, стражи — в +других комнатах. + +**Что проверять при регрессе.** Уровень 7: выпить зелье в комнате 1, тут же +столкнуть стража в провал — он обязан падать БЫСТРО, а Кид рядом — медленно. + +--- + +## Синее зелье («−HP») не ставит свою вспышку + +**Оригинал** (`proc_get_object`, seg006:1892): ветка `case 5` только глушит +звуки, играет `sound_13_kid_hurt` и ставит `hitp_delta`. Экран краснеет не +здесь, а общим механизмом «Кид ранен» (`flash_if_hurt`, seg003:0AFC). + +**Мы** раньше ставили в этой ветке ещё и `pop_flash_*` (красную вспышку на 2 +кадра) — то есть красили экран дважды: своей вспышкой и кадром урона. +Приведено к оригиналу: ветка правит только `hitp_delta`, краснеет `pop_kid_hurt`. + +**Что проверять при регрессе.** Уровень 2, комната 13, зелье `(1,3)`: выпить — +HP убавляется на единицу, экран краснеет РОВНО один раз (без двойного строба). + +--- + +## Переворот (зелье инверсии) применяется НА ГРАНИЦЕ КАДРА, а не мгновенно + +**Оригинал** (`toggle_upside`, seg000:15E9): `upside_down = ~upside_down` и +`need_redraw_because_flipped = 1` — флаг переключается прямо в момент глотка, +то есть в середине кадра. Оригиналу это ничего не стоит: он ВСЕГДА рисует в +offscreen неперевёрнутым, а зеркалит только при выводе на экран +(`flip_screen` вокруг `copy_screen_rect`, seg000:939/946). Внутренние +координаты у него от переворота не зависят вообще. + +**Мы** offscreen-буфера не имеем (две видеостраницы + теневая ОЗУ-копия на +каждую), поэтому рисуем зеркально сразу — переворот «зашит» в координаты +каждого слоя. Из-за этого момент переключения важен: зелье выпивается из +`play_seq`, то есть в СЕРЕДИНЕ кадра, и остаток кадра рисовался бы уже +зеркально поверх ещё неперевёрнутого фона. Хуже всего пламя факела — оно +ЗАПЕКАЕТСЯ в ОЗУ-копию (`pop_torch_draw`, у него нет heal: каждый следующий +кадр непрозрачно накрывает предыдущий). Кадр пламени, положенный в +зеркальную позицию на старом фоне, оставался там навсегда — по комнате +рассыпались лишние языки огня. + +Поэтому у нас два флага: `pop_upside_want` (пишут зелье, смерть Кида, чит U) +и `pop_upside` (читают все слои отрисовки). Переключение — ровно одно место, +начало кадра, вместе с перерисовкой: главный цикл делает +`pop_upside = pop_upside_want` и зовёт `pop_flip_screen`. + +Сама перерисовка при этом СОВПАДАЕТ с оригиналом: там на +`need_redraw_because_flipped` вызывается `redraw_screen(0)` — полная +отрисовка, а не отражение уже нарисованного. У нас то же самое — +`pop_flip_screen` рисует комнату заново (и получает чистый фон по +построению), а вторую страницу дабл-буфера отдаёт копией акселератора. + +**Что проверять при регрессе.** Уровень 9: выпить зелёное зелье — картинка +переворачивается ровно один раз, лишних языков пламени по комнате нет. Чит +U даёт тот же результат (он идёт тем же путём). + +--- + +## Окклюзия воротами: спрашиваем про рисуемого персонажа, а не жёстко про Кида + +**Оригинал** (`draw_tile_fore`, seg008:0D15) первой строкой: + +```c +if (tile_left == tiles_4_gate && Kid.curr_row == drawn_row && + Kid.curr_col == drawn_col - 1 && Kid.room != room_R) + draw_gate_fore(); +``` + +То есть бары ворот попадают в foretable — поверх всего нарисованного — когда +на тайле ворот стоит **именно Кид**. Это следствие устройства оригинала: +foretable ОДНА на весь проход тайлов, персонажи в неё уже добавлены, и +отдельного «переднего слоя на персонажа» там нет. + +**Мы** ради скорости не рисуем foretable целиком, а возвращаем куски тайлов +поверх ТОЛЬКО в прямоугольнике персонажа (`pop_fore_over_char`, см. memory +`pop_fore_layer_cost`: полный проход стоил 78 % кадра). Проход идёт по +персонажу, значит и вопрос естественно задавать про него — +`pop_gate_over_char(ch)`, а не про глобального `Kid`. + +**Чем платим.** Наш вариант — надмножество оригинального: страж (или тень), +стоящий в проёме ворот, у нас уходит ЗА решётку, а в оригинале остался бы +нарисованным поверх неё, пока на том же тайле нет Кида. Визуально это +правильнее, но формально расхождение. Обратной разницы нет: во всех случаях, +где оригинал рисует бары поверх, рисуем и мы. + +**Что проверять при регрессе.** Уровень 10, комната 7, тайл (2,6): Кид, +стоящий в проёме ворот, виден ЗА прутьями. Решение покрыто хост-тестами +(`tests-host/t_char.c`, набор `char_gate_*`) — отрисовка в харнесс не +линкуется, поэтому проверяется предикат. + +--- + +## Тень рисуется спрайтами КИДА, а не XOR-силуэтом + +**Файлы:** `src/pop_cdraw.c` (выбор набора атласов по `charid`/`frame`). +**Дата:** 2026-08-18 (решение принималось раньше, записано здесь). + +**Оригинал** рисует тень тем же кадром Кида, но ДВАЖДЫ — вторым проходом со +сдвигом на один пиксель и через XOR. Получается тёмный силуэт с контуром, +а не «второй Кид». + +**Мы** рисуем тень обычными спрайтами Кида, обычным блиттером — она выглядит +как Кид. + +**Почему.** Приём оригинала — read-modify-write по уже нарисованному, а +читать данные из ВИДЕО-ОЗУ (там, где спрайты) на Sprinter нельзя: читается +только ОЗУ-копия. Блочный XOR у акселератора есть и работает как раз по +ОЗУ-копии, но с нашей прозрачностью он несовместим: прозрачность сделана +подавлением записи 0xFF, а XOR прозрачные пиксели тоже смешает — под него +нужен набор с прозрачным 0x00 (замер: memory `accel_block_ops`, +`sprinter_vram_transparency`). То есть «сделать как в оригинале» всё равно +упирается в отдельный набор спрайтов. + +**Чем платим.** Тень визуально неотличима от Кида (уровни 4/5/6/12). На +механику не влияет: слот, окна `Char`, ИИ и коллизия у тени свои и от +картинки не зависят. + +**План.** Отдельный АТЛАС ТЕНИ (готовый силуэт), а не воспроизведение +XOR-прохода: один набор спрайтов вместо второго пути блита. До тех пор +расхождение сознательное — багом не заводить. + +**Что проверять при регрессе.** Уровень 6 комната 1: тень стоит слева +через провал, поза совпадает с позой Кида-в-стойке. Родственная запись — +«Слияние с тенью» ниже. + +--- + +## Слияние с тенью: мигания Кида спрайтами тени нет + +**Файлы:** `src/guards.c` (`autocontrol_shadow_level12`), +`src/pop_cdraw.c`. +**Дата:** 2026-08-13. + +**Оригинал** (`draw_objtable_item`, seg008:20CA) во время вспышки слияния +(`united_with_shadow` считает 42 → 0) рисует КИДА как тень на чётных +значениях счётчика: тот же кадр уходит не обычным прозрачным блиттером, а +парой OR+XOR со сдвигом на пиксель. Получается мерцание «Кид/тень» +примерно полторы секунды. + +**Мы** рисуем всё это время обычного Кида, а само событие обозначаем белой +вспышкой фона (`pop_flash_color = POP_FLASH_WHITE`, 18 кадров) — она в +оригинале тоже есть и ставится тем же кодом. + +**Почему.** У нас Кид и соперник рисуются из РАЗНЫХ атласов своими +палитрами (`pop_cdraw.c`), а «тень» — это персонаж слота Guard с палитрой +комнаты; блиттеров OR/XOR в libbgi нет вовсе, прозрачность сделана +0xFF-подавлением записи. Воспроизвести эффект — значит завести Киду второй +набор спрайтов и второй путь блита ради 42 кадров за всю игру. + +**Чем платим.** Момент слияния читается только по вспышке и по тому, что +тень исчезла, — без «двоящегося» силуэта. На механику не влияет: счётчик +`pop_united_shadow` тикает и уходит в −1 независимо от отрисовки, а от него +зависят и повторный подъём тени, и появление плит в комнатах 2/13. + +**Что проверять при регрессе.** Уровень 12: после слияния экран белеет, +соперник пропал, HP-потолок вырос на единицу, тень в комнате 15 больше не +появляется. Логика покрыта `tests-host/t_shadow.c`. + +--- + +## Отложенный старт падающих плит (уровень 13): фаза 0 у нас «не анимируется» + +**Файлы:** `src/pop_map.c` (`pop_check_fall_flo`, `pop_loose_tick`), +`src/pop_trob.c` (`animate_loose`). +**Дата:** 2026-08-13. + +**Оригинал** (`check_fall_flo`, seg000:1317) раздаёт шести плитам ряда 2 +верхней комнаты модификатор `−(prandom(0xFF) & 0x0F)`, то есть 0..−15, и +заводит на каждую trob. Фаза считает вверх, проходит ноль и дальше идёт +обычным отсчётом до провала — плита падает через `n + 11` кадров. Ноль там +безопасен: плиту держит в игре СПИСОК trob, а не значение модификатора. + +**Мы** списка trob для loose текущей комнаты не держим — плита анимируется +ровно тогда, когда её фаза не ноль (`pop_loose_modif[pos] != 0`). Значит +счёт, дойдя до нуля, оборвался бы навсегда. Компенсируем двумя правками, +которые работают только в паре: + +* тик перескакивает ноль (`if (m == 0) m = 1`); +* стартовое значение берётся на единицу «отрицательнее» (`−n−1`). + +**Чем платим.** Ничем в наблюдаемом поведении: суммарная задержка остаётся +`n + 11` кадров, проверено арифметикой на обоих концах диапазона (n = 0 и +n = 15). Платим связностью — две правки в разных функциях, и убрать любую +одну нельзя. + +**Что проверять при регрессе.** Уровень 13, вход в комнату 23 (она же +стартовая): плиты сверху сыплются ВРАЗНОБОЙ, а не разом и не «никогда». +Логика покрыта `tests-host/t_jaffar.c` +(`jaffar_negative_phase_counts_through_to_fall` и парный контроль на другом +уровне). + +--- + +## Чит навигации по комнатам не запускает бесшовный переход уровня + +**Файлы:** `SprPoP/sprpop_cold.c` (`pop_dbg_roomnav`), `src/sprpop.c`, +`src/pop_state.c` (`pop_nav_hold`). +**Дата:** 2026-08-13. + +**Оригинал** (`play_level_2`, seg000:0900) проверяет `Kid.room == 23` КАЖДЫЙ +кадр: уровень 12 кончается самим фактом присутствия Кида в комнате 23, двери +у него нет. Никакого «как он туда попал» там нет и быть не может — +телепорта между комнатами в игре 1989 года не существует. + +**Мы** держим этот триггер, пока Кид попал в комнату ЧИТОМ навигации +(`+`/`−`), и отпускаем на первой же смене комнаты обычным ходом. + +**Почему.** Чит перебирает комнаты ПО НОМЕРУ (1..24 с обёрткой), то есть +любой обход уровня 12 неизбежно наступает на 23-ю — и уровень молча +становится 13-м. Поймано на первом же прогоне 2026-08-13: проверяющий час +смотрел «комнату 20 уровня 12», которая на самом деле была комнатой 20 +уровня 13, и сравнивал её с картой не того уровня. Комнату 23 уровня 12 +читом не посмотреть в принципе. Это ровно та же болезнь чит-телепорта, что +BUG-CHEAT-FIGHT-1 (выход из боя), и лечится там же. + +**Чем платим.** Ничем в игре: в обычном прохождении Кид входит в комнату 23 +ногами, флаг снят, переход срабатывает как в оригинале. Расхождение видно +ТОЛЬКО при включённых читах. + +**Что проверять при регрессе.** Уровень 12: пройти в комнату 23 ногами — +уровень меняется на 13-й без заставки и без сброса HP. Обойти уровень +читом `+` через 23-ю — уровень НЕ меняется. + +--- + +## Чит «убить стража» (K) идёт через штатный путь смерти + +**Файлы:** `src/pop_guard.c` (`pop_guard_kill`). +**Дата:** 2026-08-13. Решение пользователя. + +**Оригинал** (seg000:786) ставит `guardhp_delta = -guardhp_curr` И +`Guard.alive = 0`. А гейт события смерти в `play_guard` (seg006:1490) +требует `Char.alive < 0` — то есть у оригинала чит убивает стража В ОБХОД +`on_guard_killed`. На 13-м уровне это заметно: победа над Джафаром по читу +не ставит `leveldoor_open = 2`, и выход на 14-й не открывается. + +**Мы** `Guard.alive` в чите не трогаем: применённая дельта обнуляет HP, и +`play_guard` сам переводит стража в «умирает», вызвав `on_guard_killed` — +брызги, вспышка, флаг выхода. То есть чит даёт ровно «как будто убил Кид». + +**Почему.** Отладочный прогон 13-го уровня иначе требует каждый раз честно +выигрывать бой с Джафаром (skill 9, 6 HP) — это дорого по времени, а +проверять надо совсем другое. + +**Чем платим.** Ничем в игре: читы включаются флагом `pop_cheats`, в +релизной сборке они выключены. Расхождение наблюдаемо только с читами. + +**Что проверять при регрессе.** Уровень 13: `K` на Джафаре → белая вспышка, +уход ВЛЕВО открывает дверь уровня. Честная победа в бою даёт то же самое. + +--- + +## Страж, вытесненный за правый край комнаты и там убитый, не виден нигде + +**Не расхождение, а особенность оригинала.** Записано, чтобы вопрос не +возникал повторно (спросил пользователь 2026-08-19: бой шёл в комнате 15, +Кид вытеснил стража вправо — из-за края торчал только меч, — убил его, и +труп не появился ни в комнате 15, ни в соседней справа). + +**Почему так.** Три механизма складываются: + +1. **комнату страж не менял.** Его физика работает только в полосе + `x ∈ [44, 211)` (`seg000:1254`, у нас то же условие в + `pop_guard_phys_tick`), поэтому своим ходом за край он не уходит — + Кид вытолкнул его туда толчком, а `Guard.room` остался прежним; +2. **мёртвый за Кидом не идёт.** Единственный способ сменить комнату — + `follow_guard` при переходе Кида, и первое же условие там + (`seg002:0346`) — `Guard.alive < 0 && Guard.sword == sword_2_drawn`, + то есть ЖИВОЙ и с вынутым мечом. Мёртвый уходит веткой `leave_guard`, + которая сохраняет его в **`Guard.room`** — в старую комнату. У нас + ровно это же условие, `pop_guard_cold.c` (`pop_guard_follow`); +3. **из соседней комнаты страж не рисуется.** Оригинал при + `Guard.room != drawn_room` просто ГАСИТ слот (`seg000:422`: + `Guard.direction = dir_56_none`). Механизм «видно из-за шва» + (`xpos_in_drawn_room`) работает для коллизий и для Кида, но стража из + чужой комнаты на экран не выводит. + +Итог: труп приписан комнате, где страж стоял, а его `guards_x` — за +правым краем. При возврате в ту комнату он честно восстанавливается там +же, то есть за пределами видимого поля; в соседней комнате его нет, +потому что в её данных стража и не было. + +**Живой страж в этой ситуации ведёт себя иначе** — при уходе Кида вправо +он идёт следом, если стоит достаточно близко к краю (`Guard.x >= 165`). +Это портировано и работает. + +**Чего я НЕ проверял:** живьём в SDLPoP этот сценарий не воспроизводил — +вывод сделан чтением трёх мест кода. Если понадобится подтверждение, +сценарий короткий: любой бой у правого края комнаты, вытеснить стража за +край и добить. + +## ГСЧ разведён по доменам (у оригинала он ОДИН) + +**Оригинал.** `random_seed` один на всё: кладка стены, анимация тайлов, +броски боя, модификаторы падающих плит — всё тянет из одной +последовательности (`seg009` PRNG, 32-битный LCG). Поэтому в оригинале +бой воспроизводим вместе со всем остальным: тот же сид — тот же бой. + +**У нас.** Сидов несколько: `pop_t_seed` (кладка, `pop_tile.h`), +`pop_fight_seed` (броски боя, `pop_guard.h`), отдельные у trob и loose. +Сам генератор тот же (`pop_prandom`), таблицы вероятностей — +побайтно те же, что в `data.h`. + +**Чем платим.** Конкретный бой у нас и в SDLPoP разойдётся: порядок +бросков другой, значит блоки/удары лягут иначе. Статистически поведение +то же (те же вероятности, тот же генератор), но «сверить бой кадр в кадр +с SDLPoP» нельзя, и QuickSave обязан сохранять ВСЕ сиды, а не один. + +**Что проверять при регрессе.** Если страж кажется сильнее/слабее +оригинала — сначала проверить не таблицы (они сверены), а **режим +скорости**: `fight_speed` у оригинала 100 мс, а в нашем FASTEST бой идёт +61,4 мс, то есть в реальном времени на 63 % быстрее, и на глаз это ровно +«страж давит сильнее». Режим NORMAL (дефолт) даёт 102,4 мс — см. +`frame_pacing_plan.md`. + +## PV intro: единые 12,5 FPS вместо переменных 10/7,5/8,57 FPS + +**Оригинал.** `proc_cutscene_frame()` двигает последовательности через +`cutscene_frame_time`: 6 тиков 60 Гц в начале, 8 после первой речи и 7 во +время заклинания. Это соответственно 10, 7,5 и примерно 8,57 FPS. + +**У нас (осознанное временное отличие).** Один логический кадр PV держится +четыре физических кадра Sprinter: номинально 50/4 = 12,5 FPS. Молния живёт +на отдельной физической шкале и не растягивается этим делителем. Если полная +отрисовка пересечёт дополнительный фронт, реальная частота может упасть до +10 FPS — это допустимо на текущем этапе, но должно быть измерено. + +**TODO.** Перевести PV-сцену на тот же anchor-based механизм точного темпа, +который gameplay использует через `pop_beam_sample/pop_pace_end`: измерять +число реально прошедших фронтов во время сборки кадра, держать период ровно +четыре фронта при укладывании в бюджет и явно учитывать overrun. После замера +можно вернуть точные переменные интервалы SDLPoP без накопления фазы. + +## Межуровневые PV-сцены: сохранён реальный период 100 мс + +Это правило не относится к временному темпу основного Princess/Jaffar intro +выше. `reset_cutscene()` SDLPoP задаёт для сцен перед уровнями период +6 кадров при 60 Гц, то есть 100 мс. На Sprinter тот же период получается +ровно как 5 кадров при 50 Гц. + +Суммы вызовов `proc_cutscene_frame()` перенесены без изменения реального +времени: сцены 2/4/6 и обе ветки 12 содержат 26 логических кадров (130 +физических, 2,6 с), сцена 8 — 60 (300, 6,0 с), сцена 9 — 72 (360, 7,2 с). +Fade in/out в эти числа не входят, как и в оригинале. + +## Gameplay: загрузка уровней через чёрный cut, без fade + +**Оригинал.** На границах игровых уровней использует fade out/in. + +**У нас (решение пользователя 2026-08-24).** Вход в первый уровень и +переход между уровнями выполняются как `старый кадр -> чёрная палитра -> +подготовка -> новый кадр с новой палитрой`. Fade на этих двух маршрутах +отсутствует. Сюжетные title/story/PV переходы сохраняют собственные fade и +left-to-right эффекты. + +Чёрная палитра устанавливается до любого HDD I/O. Загрузчики guard и +tileset сами физически правят отдельные цветовые слоты, поэтому после них +чёрный экран подтверждается повторно. Зеркальные атласы уровня 9 готовятся +до финального источника палитры. CBL открывается последним: старый порядок +`level_switch -> CBL open -> BIOS fade` давал скрежет повторяющейся половины +аппаратного буфера на входе в Level 1; после перестановки баг исчез в MAME. + +## Тень: кайма силуэта не подкрашивается фоном + +**Оригинал.** Спрайт Тени не хранится — он кладётся ДВАЖДЫ: обычным +прозрачным блитом в x и «блиттером XOR» в x+1 (`draw_objtable_item`, +seg008.c:1600). XOR идёт по 24-битному RGB того, что УЖЕ на экране +(`blit_xor`, seg009.c:3190), поэтому там, где спрайт прозрачен в x, но +непрозрачен в x−1, цвет получается как `фон XOR цвет спрайта`. + +**У нас.** Пакетный блит наложения на себя не умеет, поэтому результат +запечён в отдельный атлас (`toolchain/pop_pack_shadow.py`, +`docs/shadow_atlas_plan.md`). Запекать пришлось для КОНКРЕТНОГО фона, и +выбран чёрный: на нём `фон XOR цвет == цвет`, то есть запечка точна. + +**Чем платим.** Ровно одним: **кайма в один пиксель по ЛЕВЫМ кромкам +силуэта** на НЕчёрном фоне. У оригинала она принимает оттенок фона, у нас +всегда «свой» цвет. Внутренность силуэта и правые кромки совпадают точно — +там первый проход уже закрасил пиксель, и от фона результат не зависит. + +**Почему это приемлемо.** Тень бывает на четырёх уровнях, и почти всегда +на чёрном: у зеркала (ур. 4), в проёме (5), над пропастью (6), в бою (12). + +**Что проверять при регрессе.** Если Тень окажется на светлом фоне и +кайма станет резать глаз — вариантов два: запечь второй набор под светлый +фон (ещё 32 страницы EMM) или считать эту кайму прозрачной (силуэт станет +на пиксель уже). Оба хуже нынешнего; трогать только по факту жалобы. + + +## QuickSave/QuickLoad: лейбл печатается ДО дисковой операции, а не после + +**Как в оригинале.** SDLPoP печатает `QUICKSAVE` / `NO QUICKSAVE` (и пару +для загрузки) уже ПО РЕЗУЛЬТАТУ операции — `process_quicksave` (seg000:497) +сначала делает save/load, потом зовёт `display_text_bottom` и ставит +`text_time_total = 24`. На PC это незаметно: файл пишется мгновенно. + +**У нас.** `pop_qsave_process` заявляет строку ПЕРВЫМ действием, ещё до +`mem_alloc_pages`/ESTEX, через `pop_status_show_now()` — та печатает её +немедленно в ВИДИМУЮ страницу, не дожидаясь конца кадра. Отказ уже потом +переписывает строку на `NO QUICKSAVE`/`NO QUICKLOAD` обычной заявкой. + +**Зачем.** Запись снимка на диск занимает доли секунды, и всё это время +игра стоит. При порядке оригинала игрок видел сначала необъяснённый фриз, +и только по его окончании — надпись, объясняющую то, что уже прошло. +Решение пользователя, 2026-08-25. + +**Чем платим.** Строка успевает мигнуть даже там, где операция потом не +удалась: сначала `QUICKSAVE`, следом `NO QUICKSAVE`. На практике отказ — +редкость (нет места/диска), и «заявка → отказ» читается не хуже. + +**Что проверять при регрессе.** Что после неудачной операции на экране +остаётся именно `NO QUICKSAVE`/`NO QUICKLOAD`, а не первая строка: отказ +идёт обычной заявкой и печатается кадровым проходом, то есть на кадр позже. + + +## Смерть Кида: ждём кнопку и перезапускаем УРОВЕНЬ, а не игру + +**Как в оригинале.** `play_kid` (seg006:1383) печатает «Press Button to +Continue» с `text_time_total = 288`. Тик — это логический игровой кадр, +720 тиков = минута, то есть 12 тиков в секунду: 288 тиков = **24 секунды**. +Последние 72 тика (6 секунд) строка мигает с периодом 12 тиков, и на каждом +появлении играет звук 38. Дальше развилок ровно две: + +* игрок молчит все 24 секунды — `draw_game_frame` (seg000:958) зовёт + `start_game()`, и игра начинается ЗАНОВО, с title, а не с уровня; +* игрок нажимает **Enter или Shift** (не любую клавишу!) — seg000:584 + подменяет их на Ctrl+A: `if (rem_min != 0 && Kid.alive > 6 && (control_shift + || key == SDL_SCANCODE_RETURN)) key = SDL_SCANCODE_A | WITH_CTRL;` — и + уровень перезапускается. Условия важны: время не должно быть исчерпано + (иначе отработал `expired()`), а `Kid.alive > 6` даёт трупу улечься. + +**У нас.** Обе развилки сведены к одной: 24-секундного выхода в начало +игры нет вовсе, строка висит бессрочно (`MSG_HOLD`), а перезапускает уровень +ЛЮБАЯ кнопка, а не только Enter/Shift (решение пользователя). +`pop_start_level()` возвращает игрока на уровень. Место возрождения выбирает сам `pop_start_level` — на части +уровней это не старт, а пройденный чекпойнт. Esc за кнопку продолжения не +считается: он открывает pause menu. Логика ожидания живёт в банке +(`pop_dead_prompt`, pop_status.c) — резидент W1 переполнен. + +**Зачем.** Решение пользователя, 2026-08-25: возврат к title после каждой +смерти в отладочной сборке съедает всё время прохода, а прежний вариант +(авто-респавн через 400 кадров либо стрелка вверх) не объяснял игроку, чего +от него ждут. + +**Чем платим.** Двумя вещами. Первое: смерть больше не заканчивает +партию — счёт попыток фактически бесконечен, тогда как оригинал через 24 +секунды бездействия отправляет в title. Второе: любая клавиша вместо +Enter/Shift означает, что случайное нажатие (например, ещё не отпущенная +после боя клавиша) перезапустит уровень — отсюда требование сперва отпустить +всё. Когда дойдёт до «настоящей» игры, обе развилки придётся выбирать +заново: вернуть таймер на 288 тиков со start_game и сузить клавиши до +Enter/Shift — либо оставить как есть уже осознанно. + +**Что проверять при регрессе.** Нажатие принимается только после того, как +отпущено ВСЁ, что игрок держал в момент смерти (иначе зажатая при падении +стрелка перезапускает уровень мгновенно), и не раньше `RESPAWN_SETTLE` +кадров — труп должен успеть лечь. diff --git a/applications/SprPoP/docs/keys.txt b/applications/SprPoP/docs/keys.txt new file mode 100644 index 0000000..5c4966b --- /dev/null +++ b/applications/SprPoP/docs/keys.txt @@ -0,0 +1,58 @@ +* Left: turn or run left +* Right: turn or run right +* Up: jump or climb up +* Down: crouch or climb down +* Down+Left/Right: hop +* Shift: pick up things +* Shift+Left/Right: careful step +* Home or Up+Left: jump left +* Page Up or Up+Right: jump right +* Up while running: running jump +* Shift while falling: grab onto ledge + +* Left/Right: walk (advance or retreat) +* Shift: strike (attack) +* Up: block (defend) +* Down: put sword away; press Shift to draw your sword again. + +=== + +* Esc: Pause game. +* Space: Show how much time is left. +* Ctrl+A: Restart level. + +* Ctrl+R: Return to intro. +* Ctrl+S: Sound on/off. +* Ctrl+M: Music on/off. +* Ctrl+V: Show version of SprPoP. +* Ctrl+Q: Quit game. + +* F6: Quicksave: Save the exact state of the game. +* F9: Quickload: Load what the last quicksave saved. +* F12: Save a screenshot to the screenshots folder. +* Backspace: Display the in-game menu. (Esc will also display the menu by default, but you can turn that off.) + +* Shift+L: Go to next level. +* -: Decrease remaining time by one minute. +* +: Increase remaining time by one minute. +* R: Resurrect kid. +* K: Kill guard. +* Shift+I: Flip the screen upside down. +* Shift+W: Slow falling. +* Shift+S: Restore a lost hit-point. (Like a small red potion.) +* Shift+T: Give more hit-points. (Like a big red potion.) + +=== + +* H: Look at the room to the left. +* J: Look at the room to the right. +* U: Look at the room above. +* N: Look at the room below. +* Ctrl+B: Go back to the room where the prince is. (Undo H,J,U,N.) + +=== + +* [: Shift kid 1 pixel to the left. +* ]: Shift kid 1 pixel to the right. +* T: Toggle display of timer (remaining minutes:seconds:ticks). Also shows the total elapsed ticks during playback. + diff --git a/applications/SprPoP/docs/layout_plan_v2.md b/applications/SprPoP/docs/layout_plan_v2.md new file mode 100644 index 0000000..d32a4ee --- /dev/null +++ b/applications/SprPoP/docs/layout_plan_v2.md @@ -0,0 +1,532 @@ +# SprPoP — план v2: размер кода и раскладка по окнам/банкам/страницам + +> **Замер 2026-08-08, после MEM-BANK2 (актуальная сборка, ALLOCS=3000).** +> `_CODE` 22 556 Б, данные 4 375, куча 4 301. Банки: 1 (`guards.c`) 2 289, +> 2 (`pop_bg.c`, ГОРЯЧАЯ половина фона) 5 872, 3 (`pop_map.c`) 8 483, +> 4 (`pop_cdraw.c`) 5 664, 5 (`pop_ctrl.c`) 2 040, 6 (`pop_trob.c`) 2 889, +> 7 (`pop_room.c`, ХОЛОДНАЯ половина фона) 6 035 — все из 16 384. +> +> Слой фона разрезан надвое по ЧАСТОТЕ ВЫЗОВА (`TASKS_CLOSED.md#mem-bank2`): +> общие листья — в резидент `pop_tile.c` (W1 замаплен всегда, таблицы видны +> обеим половинам), горячий fore-проход остался в банке 2, холодная отрисовка +> комнаты и точечные перерисовки уехали в банк 7. Стык — три тонкие +> `__banked`-обёртки, чтобы трамплин платил только холодный путь. +> **Банк 2: 90.4 % -> 35.8 %.** Резидент вырос на 2 КБ (листья) — куча +> 6 333 -> 4 301 Б, это плата за то, что таблицы должны быть видны из двух +> банков. +> +> Число банков дублируется: `--bank N=` в Makefile И `const uint8_t n_banks` +> в `sprpop.c`. Расхождение — не ошибка сборки, а зависание до первого +> кадра. +> +> **Замер 2026-08-01 (историческая отметка).** `_CODE` 25 119 Б, `_DATA` 3 709, +> куча ~2.4 КБ. Банки: 1 (`guards.c`) 1 896, 2 (`pop_bg.c`) 13 792, +> 3 (`pop_map.c`) 6 331, 4 (отрисовка стража) 2 236 — все из 16 384. +> **Резидента `--w3` больше нет**: отрисовка уехала в банк 2, и это сняло +> главное ограничение резидента (из банка его было не достать) — банк→банк +> работает, трамплин сохраняет страницу окна на стеке. Отрисовка стража +> вынесена из банка 2 в собственный банк 4, потому что банк 2 подошёл к +> потолку (16 021 из 16 384) — коммит `2f3e854`. +> +> **Свободного места в банке 2 теперь 10.5 КБ**, в банке 7 — 10.3 КБ; туда +> просятся чомперы, зеркало и второй тайлсет (palace). Прежде чем начинать +> `levels_plan.md` §3 — всё равно посчитать, куда это ляжет. Следующий +> свободный номер банка — 8, гранулярность — файл. +> +> Из плана ниже **не сделаны шаги 5 (данные: `room_modif`, `dl1/dl2`, +> `_kbdraw_down`; потенциал ~1.5 КБ) и 7 (дедуп `draw_tile`, отложен по +> решению пользователя)**. + +Статус: **план для отдельной сессии**, составлен 2026-07-29 по свежему замеру. +Заменял `size_optimization_plan.md` (v1, 2026-07-21) — тот удалён 2026-08-01 +как полностью перекрытый этим документом. Документ самодостаточный +— рассчитан на старт с пустого контекста. + +Повод: перед стражами и боёвкой (новый код ~5–8 КБ) надо понять, куда он +поместится, и заранее развести код так, чтобы банкованные модули не упёрлись в +ограничения окна W3. + +--- + +## СТАТУС ВЫПОЛНЕНИЯ (обновлено 2026-07-29, коммит ecf5ecf) + +| Шаг | Статус | Факт | +|-----|--------|------| +| 1. `kid_data.h` → EMM-страница + `load_frame`/`cur_frame` | **сделан** | `_CODE` −3 247 Б; попутно найден и обойдён баг кодогенерации SDCC (см. ниже) | +| 2. `pop_geom.c` (дедуп геометрии + PRNG) | **сделан** | −41 Б `_CODE`, −11 Б W3; ценность — не байты, а bank-safe слой | +| 3. `pop_map` без вызовов графики | **сделан** (фазы 1a/1b) | через пометки перерисовки, см. ниже | +| 4. Разгрузка/перебалансировка W3 | **сделан** | резидент = `pop_bg` + `pop_gdraw` (отрисовка стража, 2026-07-29); W3 14 376 → 12 819 (свободно 3 565 Б) | +| 5. Данные (`room_modif`, `dl1/dl2`, `_kbdraw_down`) | не начат | потенциал ~1.5 КБ | +| 6. Контракт банка стражей + пробник | **пробник сделан** | `tests/w3bankgfx` — модель подтверждена в MAME, см. ниже | +| 7. Дедуп семейства `draw_tile` | отложен по решению пользователя | «мороки много, выгода не так велика» | + +**Замер сейчас против замера §1:** `_CODE` 24 881 → 24 421, куча W2 2 076 → 2 592 Б, +W3-резидент 14 376 → 11 632 (свободно 2 008 → 4 752 Б). Сумма кода упала +на ~3.2 КБ (данные Kid уехали в EMM), остальное — перераспределение. + +**Замер 2026-07-29 (после стража).** Появление стража съело кучу до 805 Б; +разгрузка — вынос ОТРИСОВКИ стража в резидент (`pop_gdraw.c`, `--w3`), логика +и состояние остались в W1/W2, чтобы банк `guards.c` их видел (R2). Итог: +`_CODE` 26 149 → 25 703, куча **805 → 1 245 Б**, W3-резидент 11 656 → 12 819 +(свободно 4 728 → 3 565 Б), банк 1 — 236 / 16 384 Б. Граница «что резидент» +теперь формулируется одним правилом: **резидент = только то, что рисует и +зовётся исключительно из главного цикла**; всё, что может понадобиться банку, +остаётся в W1/W2. + +### Что сделано вместо §5.3 (вынос loose в W3) + +Вместо переноса кода между окнами выбран (по обсуждению с пользователем) +**порт архитектуры оригинала**: логика ставит пометку, отрисовка идёт +отдельным проходом — `set_redraw_*` (seg007) + `redraw_needed` (seg008:0178). +Появился `pop_redraw.c/.h`; `pop_trob` и `pop_map` больше не рисуют тайлы. +Наши самодельные счётчики (`spike_rest`, `button_rest`, `ldoor_rest`, +`loose_bake`, `loose_rest`, `ceil_rest`, `ceil_bake`, `land_bake`) удалены — +их роль (вторая страница дабл-буфера) взял счётчик страниц в пометке. + +**Два исключения остались** (обе — функции ТОЛЬКО главного цикла, звать из +банка нельзя): +- `pop_process_trobs` — пламя факела и пузырёк зелья (покадровый оверлей); +- `pop_loose_tick` — падающий кусок (mob): spawn/tick/pos. В оригинале это + отдельная подсистема (`mobs` + `draw_moving`), разделение на логику и + отрисовку — задел следующей фазы. + +### Пробник банка (2026-07-29): модель ПОДТВЕРЖДЕНА + +`tests/w3bankgfx` (huge + `--w3 res.c` + `--bank 1=bank1.c`, графика 256): + +- банк рисует примитивом libbgi НАПРЯМУЮ — работает; страница W3 внутри + банка до блита, после блита и после возврата из вызванной им W1/W2-функции + одна и та же (0xF0), резидент — 0xF3. То есть `_bgi_begin`/`_bgi_end` + корректно возвращают ИМЕННО банковую страницу (правило R4); +- вызов W1/W2-функции из банка работает, и она тоже может рисовать; +- резидент W3 жив и вызывается после возврата из банка (R3). + +**Дополнительно выяснено (важно для стражей):** писучие статики +`__banked`-модуля линкуются В СТРАНИЦУ БАНКА (0x1C000+) — снаружи их не +прочитать, из W1/W2 по 0xC000 видна резидентная страница. Значит всё +состояние банкованного кода (позиции стражей, таймеры боя) обязано жить в +W1/W2 как обычные глобалы, а банк — только код. + +Ещё одна мина, найденная там же: инлайновый `in a,(#0xE2)` посреди тела +функции затирает A, куда SDCC уже положил параметр (у нас из-за этого цвет +заливки стал номером страницы, и «резидент не рисовал»). Читать порт +отдельной `__naked`-функцией. + +### Найденная по дороге ловушка компилятора + +`(const T *)КОНСТАНТА + var*K` SDCC 4.5 может собрать неверно: умножение +делает в 16 битах, а потом берёт только младший байт (`ld c,l` / `inc b`). +Кадры Kid с индексом ≥ 52 читались из чужой строки таблицы, у бега/шага +пропадал `FRAME_NEEDS_FLOOR` и персонаж проваливался сквозь пол. Лечение — +считать адрес в `uint16_t` и кастовать один раз. Тот же паттерн в +`pop_level.c` компилируется ПРАВИЛЬНО, т.е. полагаться на «у соседа +работает» нельзя. Подробности: memory `sdcc_z80_const_ptr_index_bug`. + +--- + +## 0. Что уже сделано из v1 (не повторять) + +- `--opt-code-size` и `--max-allocs 100000` **уже включены по умолчанию** в + `bin/sprinter-cc` (v1 §2.1 закрыт, выигрыш получен). +- Лишние блиты переднего слоя убраны (v1 §7 п.0): `fore_tile` больше не рисует + `bottom_id`, `_CODE` −388 Б. +- `gfx_blit_noclip` в libbgi (v1 §8 шаг 1): фоновые блиты в 2.9× дешевле. +- `--w3` как резидент окна 3 реализован и обкатан (memory `w3_resident_code`). + +--- + +## 1. ЗАМЕР (сборка 2026-07-29, коммит 1214785) + +Команда: `--memory small --gfx 256 --w3 pop_trob.c pop_map.c --w3 pop_bg.c`. + +### 1.1 Окна + +| Область | Занято | Свободно | Примечание | +|---|---|---|---| +| W1+W2 `_CODE` | 24 881 Б | — | 0x4100…0xA231 | +| W1+W2 `_HOME`+`_GSINIT`+`_DATA`+`_BSS` | ~4 240 Б | — | до 0xB2E4 | +| **W1+W2 куча** | 0 (никто не malloc'ит) | **2 076 Б** | 0xB2E4…0xBB00 | +| W1+W2 стек | — | 1 279 Б | 0xBB00…0xBFFE | +| **W3 резидент** | 14 376 Б | **2 008 Б** | 0xC000…0xF828 | +| EMM-страницы | 37 атласов + 1 уровень | ~215 страниц свободно | `sprinter_emm_budget` | + +**Итого запаса до стены: ≈ 4 КБ** (2 КБ в W1/W2 + 2 КБ в W3). Стражи туда +не влезут. + +### 1.2 Код по модулям (точно, из `.rel`) + +``` +W1/W2 (_CODE 24 881): W3 резидент (_W3CODE 14 376): + pop_kid 6 963 pop_bg 11 643 + pop_map 6 157 pop_trob 2 733 + SprPoP 2 583 + pop_level 1 425 + pop_ctrl 1 145 + crt0 333 + libc+libbgi ~6 275 +``` + +### 1.3 Крупнейшие функции/данные (из `.lst`) + +``` +pop_bg : draw_tile 3080, other_overlay_tile 1146, wall_pattern 944, + mob_render 720, mob_tick_one 661, overlay_mid_tile 498, + fore_only_tile 409, climb_overlay_tile 391, tile_table 371 +pop_map : check_loose_fall_on_kid 674, check_bumped 567, jump_up_or_grab 413, + get_tile 266, do_knock 243, check_press 238, check_leave 212 +pop_kid : kid_seqtbl 2310 + kid_frames 1205 + kid_seq_off 230 = 3745 Б ДАННЫХ + (в _CODE!), собственно кода ~3.2 КБ +SprPoP : enter_room+main ~2.1 КБ +pop_level: room_bg_ptr 1084 (+ 515 Б таблиц LINKLOC/LINKMAP в _DATA) +``` + +### 1.4 `_DATA` (3 710 Б) + +``` +pop_trob 963 (room_modif[24][30] = 720 + trobs + rest-массивы) +pop_level 515 (копии LINKLOC/LINKMAP уровня) +pop_map 163, SprPoP 141, pop_kid 130, pop_bg 115, pop_ctrl 13 +libc: _irq_state 818, _kbdraw_state 515, _gfx_pal_buf 256, прочее ~200 +``` + +### 1.5 Находки замера (мелкие, но чинить) + +1. **`--w3` берёт ОДИН файл на флаг.** В `Makefile` написано + `--w3 pop_trob.c pop_map.c --w3 pop_bg.c`, и это значит «W3 = pop_trob и + pop_bg», а `pop_map.c` компилируется как обычный исходник в W1/W2. Судя по + `.sprinter-cc-SprPoP/w3_pop_map.rel` (устаревший артефакт), когда-то + pop_map был в W3. **Решить осознанно** (см. §4) и записать явно: + `--w3 pop_trob.c --w3 pop_bg.c`. +2. `libc` тянет `_irq_state` 818 Б + `_kbdraw_state` 515 Б в `_DATA`. + `__irq_vec_buf` (513 Б) — таблица векторов IM2; `__kbdraw_down` (512 Б) — + битмап клавиш на 512 скан-кодов. Оба можно ужать (см. §5.4), это ~0.7 КБ + в самом дефицитном окне. + +--- + +## 2. ПРАВИЛА ПЛАТФОРМЫ, ОТ КОТОРЫХ ПЛЯШЕТ РАСКЛАДКА + +Это главное, что изменилось по сравнению с v1: модель банкинга уточнена по +`bin/sprinter-cc` (справка `--w3`/`--bank`) и по коду libbgi. + +**(R1) Резидент W3 (`--w3`) и банки W3 (`--bank`) делят одно окно.** +Резидент лежит на своей странице 0xC000…0xFFFF; трамплин на время вызова +`__banked` подменяет страницу W3 на банковую и возвращает резидентную назад. + +**(R2) Из банка резидент W3 НЕДОСТИЖИМ — и транзитивно тоже.** +Пока исполняется банк, резидентной страницы в адресном пространстве нет. +Значит нельзя не только `bank → pop_bg()`, но и `bank → pop_map() → pop_bg()`. +**Это ключевое ограничение при выборе, что делать банком.** + +**(R3) Резидент → банк работает** (через трамплин в W1), резидент → W1/W2 — +тоже. + +**(R4) Графические примитивы libbgi звать можно откуда угодно.** +`_bgi_begin` читает текущую страницу W3 из порта 0xE2, а `_bgi_end` её +возвращает — то есть скобка корректна и из банка, и из резидента. Нельзя +только одно: **звать `_bgi_begin`/`_bgi_end` ИЗ кода, который сам лежит в W3** +(после подмены страницы исчезнет исполняемый код — проверено, белый экран). +Поэтому `pop_bg` (резидент W3) обязан пользоваться готовыми примитивами +(`gfx_blit*`, `bar`, …), а батчинг скобки на весь `draw_tile` (v1 §8 шаг 1) +для него **невозможен** без переноса самого `draw_tile` в W1/W2. + +**(R5) `--w3` кладёт в W3 код И rodata модуля** (`--codeseg/--constseg +W3CODE`), а писучие статики оставляет в `_DATA` (W2). То есть `const`-таблицы +переносятся в W3 бесплатно вместе с модулем (так уже лежит `tile_table` 371 Б). + +**(R6) Данные в EMM-странице читаются, только пока страница в окне.** +`gfx_w0_map(page)` / `gfx_w0_unmap()` — окно W0 (0x0000…0x3FFF), первые 0x100 +занимает ISR-стаб. Так уже работает `pop_level`. Цена — пара `OUT` на +маппинг, поэтому годится для «пачками», а не для чтения по байту в горячем +цикле. + +--- + +## 3. ЧТО ДЕЛАТЬ НЕЛЬЗЯ (анти-паттерны, чтобы не потерять время) + +- **Нельзя банковать `pop_bg`.** Он вызывается из pop_map, pop_trob, SprPoP, + pop_kid — то есть из главного цикла на каждом кадре; плюс он сам держит + `tile_table` и всю отрисовку. Банк дал бы трамплин на каждый блит. +- **Нельзя банковать `pop_map`, пока `pop_map` зовёт `pop_bg`** (R2). Сейчас + зовёт: `pop_loose_tick` и компания (~30 вызовов графики). +- **Нельзя тащить `kid_frames` в EMM «в лоб»**: он читается несколько раз за + кадр из коллизии (`kid_cur_dx`/`kid_cur_flags` → `dx_weight`, + `char_x_forward_edge`, …). Нужен кэш кадра (см. §5.1) — иначе маппинг + страницы окажется в горячем пути. +- **Нельзя «причёсывать» семейство `draw_tile` ради экономии, не имея + пиксельного теста.** Мы неделю выравнивали слои по SDLPoP; любой рефактор + этой зоны проверять диффом страниц (заморозка кадра клавишей `1` + сравнение + VRAM обеих страниц, приём из memory `mame_mcp_bridge`). + +--- + +## 4. ЦЕЛЕВАЯ РАСКЛАДКА + +Принцип: **W3-резидент = «толстая графика, которую зовёт только главный цикл»; +W1/W2 = ядро, которое должно быть достижимо ОТОВСЮДУ (включая банки); банки = +новая холодная логика (стражи, боёвка, будущие уровни)**. + +``` +W1/W2 (всегда отображено) W3 резидент (стр. 0xC000) Банки W3 +────────────────────────── ───────────────────────── ───────── +libc + libbgi pop_bg (отрисовка тайлов) guards.c +pop_kid (интерпретатор+рисование) pop_trob (анимации тайлов) fight.c +pop_map (коллизия/физика/предметы) pop_loose.c (loose+потолок) debug/roomnav +pop_geom (общая геометрия/тайлы) enter_room-часть SprPoP? +pop_ctrl (ввод/диспетчер) +SprPoP (главный цикл) +``` + +Почему так: + +- **`pop_map` остаётся в W1/W2** — его зовут и главный цикл, и (в будущем) + банк стражей; в W3 его класть нельзя именно из-за R2. Для этого из него надо + вынести графическую часть (loose/потолок) — она уезжает в W3 к `pop_bg` + (§5.3). После выноса `pop_map` становится **чистой логикой без единого + вызова графики** — тот самый bank-safe API. +- **`pop_kid` остаётся в W1/W2**: `play_seq`/`kid_set_seq`/`Kid` нужны и + стражам (у стражей ТА ЖЕ seqtbl), а `kid_draw` зовёт только libbgi (R4). +- **`pop_trob` остаётся резидентом**: его зовёт только главный цикл, и он сам + зовёт `pop_bg` — идеальный житель W3. +- **Банк стражей не зовёт ничего из W3.** Рисование стражей — либо через + libbgi напрямую (R4), либо (лучше) резидентный `guard_draw()` в W1/W2 рядом + с `kid_draw`, а банк только считает состояние. Тот же приём мы уже + используем для `pop_item_taken`/`pop_loose_fell`/`pop_ceil_fell`: банк + выставляет флаг — резидент рисует. + +--- + +## 5. ПЛАН РАБОТ + +Порядок выбран так, чтобы каждый шаг был проверяем отдельно и давал место +следующему. + +### Шаг 1. `kid_data.h` (3 745 Б) → EMM-страница + порт `load_frame` — **самый большой выигрыш** + +Сейчас `kid_seqtbl` (2310) + `kid_frames` (1205) + `kid_seq_off` (230) лежат в +`_CODE` окна W1/W2 — это 15 % всего дефицитного пространства. + +Как переносить: + +1. `pop_extract_kid_data.py` дополнительно пишет `kid_data.bin` (те же три + таблицы подряд, фиксированные смещения). +2. Грузим её в отдельную EMM-страницу тем же способом, что уровень + (`pop_level_load` — готовый образец), хэндл держим в `pop_kid`. +3. **Порт `load_frame` (seg006) и глобала `cur_frame`** — в оригинале ровно + так и сделано: раз за тик кадр копируется в структуру, а весь остальной код + читает `cur_frame`, а не таблицу. У нас `kid_cur_dx()/kid_cur_flags()` + станут чтением из `cur_frame` (5 байт в `_DATA`). +4. `play_seq` оборачивается в один `gfx_w0_map(kid_data_page)` … `unmap` на + вызов (в тике, не в отрисовке — конфликта с атласом в W0 нет). + +Выигрыш: **−3 745 Б из W1/W2**, цена — один маппинг страницы за тик и 5 байт +`_DATA`. Дополнительный бонус: `load_frame`/`cur_frame` — шаг К СХОДСТВУ с +оригиналом, а не отход от него. + +Риск: сломать `play_seq` (сердце анимации). Проверка: прогон по комнатам с +эталонными позами (вис, подтягивание, прыжки, подъём меча). + +### Шаг 2. Модуль `pop_geom.c` — дедуп + bank-safe фундамент + +Сейчас продублировано между модулями: + +| что | где | сколько | +|---|---|---| +| `y_to_row`/`y_to_row_mod4` | pop_bg + pop_map | 2 копии | +| `char_dx_forward` | pop_kid + pop_map | 2 копии | +| `x_bump[20]` | pop_kid (uint8) + pop_map (int16) | 20 + 40 Б, РАЗНЫЕ типы | +| `y_land[5]` | pop_kid + pop_map | 10 + 10 Б | +| `tile_is_floor` | pop_map (+ проверка кодов в SprPoP) | 2 места | +| 32-битный LCG `prandom` | pop_bg (`prandom`) + pop_trob (`trob_prandom`) | 2 копии по ~60 Б + 2 сида | + +Собрать в один W1/W2-модуль `pop_geom.c`: таблицы `x_bump/y_land/dir_front/ +dir_behind`, `y_to_row`, `char_dx_forward`, `get_tile_div_mod(_m7)`, +`tile_is_floor`, `prandom`. Выигрыш прямой — сотни байт (оценка 250–400 Б), +но главное — **это и есть тот «чистый» API, который потом сможет звать банк** +(R2): вся геометрия оказывается в W1/W2 по определению. + +Осторожно: `prandom` у pop_bg и pop_trob — РАЗНЫЕ последовательности с разными +сидами (стены vs фазы факелов). Объединять функцию можно, **сиды — нет**: +передавать сид указателем/по индексу, иначе поедет раскладка кладки. + +### Шаг 3. Вынести loose/потолок из `pop_map` в W3 + +`pop_map` — единственный модуль W1/W2, который зовёт графику, и делает это +ровно в одном логическом блоке: `pop_loose_tick` + `check_press` + `do_knock` + +`fell_on_your_head` + `check_loose_fall_on_kid` + плита-потолок (~1.4–2 КБ). + +Вынести их в `pop_loose.c`, собираемый `--w3` рядом с `pop_bg`/`pop_trob`. +Тогда: + +- `pop_map` = чистая логика (bank-safe, R2 соблюдён); +- W1/W2 худеет ещё на ~1.5–2 КБ; +- W3 растёт на столько же — а место там появится после шага 4. + +### Шаг 4. Перебалансировка резидента W3 + +После шага 3 в W3 будет тесно (14.4 + 2 ≈ 16.4 КБ > 16 КБ). Разгружаем: + +1. **`wall_pattern` (944 Б) + `mob_render`/`mob_tick_one` (1381 Б)** — кандидаты + на переезд в W1/W2: их зовёт только `pop_bg`/`pop_loose`, но сами они уже + пользуются только libbgi (R4), значит из W1/W2 работают и остаются + достижимыми из банка. +2. `tile_table` и мелкие const-таблицы pop_bg (371 + ~300 Б) можно унести в + EMM-страницу **уровня** (там ~13.8 КБ свободно) — но только если чтение + происходит под уже замапленной страницей. Сейчас `draw_tile` читает + `tile_table` ВНЕ W0-контекста → потребуется явный маппинг на тайл. **Не + делать раньше замера**: 30 тайлов на входе в комнату × map/unmap — терпимо, + а вот в покадровых редроях (пики/loose/кнопка) — уже горячий путь. +3. Если и этого мало — `enter_room` (~2.1 КБ, зовётся только при смене комнаты) + переносится в резидент W3 или в БАНК (он вызывается из главного цикла = + резидента, значит банк допустим по R3). + +### Шаг 5. Данные + +1. **`room_modif[24][30]` = 720 Б** (pop_trob, `_DATA`). Нужен произвольный + доступ каждый кадр (анимации, ворота) — в EMM не годится. Но 24 комнаты × + 30 байт хранятся ЦЕЛИКОМ, хотя одновременно живут modif'ы только текущей + комнаты и соседей по швам. Вариант: хранить полный массив в EMM-странице + уровня, а в `_DATA` держать кэш на 2–3 комнаты (свою + левого/правого + соседа) с записью обратно при смене комнаты. Выигрыш ~600 Б, цена — + аккуратность на швах (кнопка в одной комнате открывает ворота в другой). + **Делать последним** — это самая «тонкая» правка по семантике. +2. **`dl1[256]`+`dl2[256]` = 512 Б** (pop_level, `_DATA` — копии LINKLOC/ + LINKMAP уровня) — читаются при нажатии кнопки + и при отрисовке нажатой кнопки. Кандидат на чтение прямо из страницы + уровня (она и так маппится) — но проверить, что `pop_doorlink2` не зовётся + из отрисовки в тот момент, когда в W0 атлас. Выигрыш ~500 Б. +3. **`_kbdraw_down[512]` 512 Б** (libc): проверено — это **байт на скан-код** + (`libc/kbd/_kbdraw_state.c`), хотя комментарий называет его битовой картой. + Упаковка в биты даёт −448 Б, но добавляет сдвиг/маску в ISR-трамплин и в + `kbd_raw_down`. Трогать осторожно: raw-клавиатура уже дважды была + источником залипаний (memory `kbd_raw_fifo_drain`, + `kbd_overrun_wipe_modifiers`) — правку сопровождать прогоном docs/kbd-games. +4. **`__irq_vec_buf` 513 Б** (libc IM2): таблица векторов обязана быть + выровнена и полна — не трогать. + +### Шаг 6. Контракт банка стражей (проектируется ДО написания кода) + +Когда дойдём до стражей: + +- `guards.c` собирается `--bank 1=guards.c`, режим `huge` (или `big` с + `BANKED=W1`, если W3 окажется тесен для трамплинов). +- **Банк зовёт только:** `pop_map` (чистая логика после шага 3), `pop_kid` + (`play_seq`, `kid_set_seq`, `cur_frame`), `pop_geom`, libc/libbgi. +- **Банк НЕ зовёт:** `pop_bg`, `pop_trob`, `pop_loose` (резидент W3) — ни + прямо, ни через промежуточные функции. Нужна отрисовка — выставляет флаг, + рисует резидент (идиома `pop_item_taken`). +- Первым делом — **пробник** (`tests/` или `--bank` на пустышке): банк зовёт + `pop_map`-функцию, та зовёт libbgi-примитив; убедиться в MAME, что скобка + W3 корректно возвращает банковую страницу (R4) — это проверка модели, а не + веры в неё. + +### Шаг 7. Мелкий дедуп в `pop_bg` (после того, как появится тест страниц) + +- Пять функций-редроев (`pop_spike_redraw`, `pop_loose_shake_draw`, + `pop_floor_bake`, `pop_button_redraw`, `pop_leveldoor_redraw`) отличаются + только прямоугольником heal, банком и набором тайлов — свести к одному + параметризованному хелперу (оценка −150…250 Б). +- `env_b/wall_b/fore_b/pot_b` — четыре одинаковых обёртки над `blit_b` + (оставить: экономия единицы байт, читаемость дороже). +- `overlay_mid_tile` / `fore_only_tile` / `climb_overlay_tile` / `draw_tile` + — общая структура «взять code/lcode, посчитать x/dmy/dby, разобрать слои». + Тут экономия потенциально сотни байт, но это **та самая зона риска из §3** — + только с пиксельным диффом до/после и по одному слою за раз. + +--- + +## 6. Ожидаемый итог + +| Шаг | W1/W2 | W3 | Риск | +|---|---|---|---| +| 1. kid_data → EMM + load_frame | **−3 745** | — | средний (сердце анимации) | +| 2. pop_geom (дедуп) | −250…400 | — | низкий | +| 3. loose → W3 | −1 500…2 000 | +1 500…2 000 | низкий (перенос как есть) | +| 4. разгрузка W3 (wall_pattern, mob) | +2 300 | −2 300 | низкий | +| 5. данные (room_modif, LINKLOC, kbd) | −1 000…1 600 | — | средний/высокий | +| 7. дедуп редроев pop_bg | — | −150…250 | средний | + +Суммарно: **W1/W2 освобождается ~4.5–6 КБ**, W3 остаётся примерно в нынешнем +объёме, но становится «правильно заполненным» — в нём только то, что банк +никогда не позовёт. Плюс открывается путь к банкам: стражи и боёвка получают +до 16 КБ на банк, не трогая резидент. + +--- + +## 7. Как мерить и проверять (обязательно к каждому шагу) + +1. **До/после по `.rel`** — точные размеры на модуль: + `for f in .sprinter-cc-SprPoP/*.rel; do grep '^A ' $f; done` + (области `_CODE`/`_W3CODE`/`_DATA`). Итоги окон печатает сам `sprinter-cc`. +2. **Функции** — из `.lst` (метки `_name:` и адреса), скрипт в истории этой + сессии; полезно ловить «функция распухла после рефактора». +3. **MAME**: любой перенос кода между окнами/страницами — это класс «молча + ломается» (`sprinter_memory_modes`). Минимум: комната 1 (loose), 12 + (вис/подтягивание), 15 (меч), 9 (дверь уровня), 6 (кнопка/ворота). +4. **Пиксельный дифф** для правок отрисовки: заморозить кадр (`1`), сравнить + обе страницы дабл-буфера через `vram` (см. memory `mame_mcp_bridge`) и/или + сверить с эталонным рендером `render_room.py`. +5. **Скорость** — после шагов 1 и 4 замерить кадр маркерами в порт 0xFE + (приём из v1 §8), чтобы маппинг страницы за тик не съел бюджет. + +## 8. Ссылки + +- `bin/sprinter-cc` — справка по `--w3`, `--bank`, `--memory`, `--memory-manual`. +- `runtime/crt0_banked.s`, `runtime/bank.s` — трамплины и захват резидентной + страницы W3. +- `libbgi/common/_bgi_begin.c` / `_bgi_end.c` — механика скобки W3 (R4). +- memory: `w3_resident_code`, `pop_banking_architecture`, `sdcc_banking`, + `bank_local_data_pattern`, `sprinter_memory_modes`, `memory_modes_implemented`, + `sprinter_emm_budget`, `mame_mcp_bridge`, `avoid_32bit_arith_z80`, + `libc_one_function_per_module`. +- `applications/SprPoP/TASKS_OPEN.md` — что из этого берётся в работу сейчас. + +--- + +## 9. Скорость отрисовки: замеры и запас + +Перенесено из удалённого `size_optimization_plan.md` §8 (замер 2026-07-27) — +единственная его часть, которая не была перекрыта этим документом. +Профилирование в MAME: маркеры в порт 0xFE + `wpiset … totalcycles` (приём из +memory `mame_mcp_bridge`); в самом `sprpop.c` для этого уже стоят полосы +бордюра `PROF()`. Кадр Sprinter = **430 080 тактов**. + +**Стоимость блита почти НЕ зависит от размера** — платим за проход по цепочке +`gfx_blit → gfx_blit_part → _gfx_blit_full` (16-битная арифметика, клип, +пересчёт src, нарезка полос >256), а не за пиксели: + +| путь (спрайт 32×3) | тактов | +|---|---| +| `gfx_blit` (общее ядро, с клипом) | 13 288 | +| линейное ядро без клипа | 4 617 | + +Отсюда `draw_tile(0,0)` тайла шва (9 блитов) стоил **183 690 тактов = 43 % +кадра**; сам `bar` — только 13 308. + +**Сделано:** `gfx_blit_noclip()` в libbgi, фоновые блиты `pop_bg` уходят на +него, когда спрайт целиком на экране (~2.9×, подтверждено в MAME). Позже +тем же приёмом закрыты спрайты персонажей (`gfx_blit_cols_part_noclip`). +**Не закрыт heal** — задача CLIP-1 в `../PoP/SprPoP/TASKS_CLOSED.md`. + +**ВАЖНО:** W3-скобку (`_bgi_begin`/`_bgi_end`) ставит САМА libbgi — вызывать +её из модуля, собранного с `--w3`, нельзя: после `_bgi_begin` окно W3 занято +видеобанком и код вызывающего исчезает из адресного пространства (проверено: +белый экран). + +**Запас, когда перестанет хватать бюджета кадра:** + +1. **Батчинг W3-скобки** — одна `_bgi_begin`/`_bgi_end` на весь `draw_tile` + вместо скобки на блит; нужен публичный batch-API в libbgi. + **Осторожно, и это стало важнее, чем было:** между begin/end стоит `DI`, + длинная серия задержит кадровое прерывание — а по разбору KBD-1 + (`../PoP/SprPoP/TASKS_CLOSED.md`) длинные DI-окна и есть причина потери байт + клавиатуры. Батчинг эту проблему УХУДШИТ, если делать его вслепую. +2. **Решётка ворот одним спрайтом** — `draw_gate_back` рисует бары по одному + (до 7 блитов). Сгенерировать в атласе «столб решётки» и выводить одним + `gfx_blit_part` с обрезкой по фазе `gate_bot_y & 7`: 7 блитов → 1. +3. **Не перерисовывать статичные части шва** — грань ворот, пол и кромка при + анимации решётки не меняются (см. OPT-1 в `../PoP/SprPoP/BUGS_CLOSED.md` — + решено не делать, стоимость транзиентная). +4. **T-1 / T-2** (`../BUGS_OPEN.md`) — перерисовка пик по причине и + idle-skip Кида: самый большой оставшийся резерв, потому что убирает работу + целиком, а не удешевляет её. diff --git a/applications/SprPoP/docs/levels_12_15_plan.md b/applications/SprPoP/docs/levels_12_15_plan.md new file mode 100644 index 0000000..877eb30 --- /dev/null +++ b/applications/SprPoP/docs/levels_12_15_plan.md @@ -0,0 +1,462 @@ +# Что осталось на уровнях 12, 13, 14, 15 и 0 + +Статус: разбор по `../SDLPoP/src/`, 2026-08-13. Продолжает +[`levels_plan.md`](levels_plan.md) (машинерия уровней и тайлсеты — уже +сделаны). Здесь только СПЕЦСОБЫТИЯ, которых у нас ещё нет. + +Правило проекта: источник истины — SDLPoP; все ссылки ниже даны на функцию и +строку, чтобы порт начинался с чтения, а не с гипотезы. + +--- + +## 0. Что из этой области УЖЕ есть + +Проверено грепом по `SprPoP/`: + +| механика | где у нас | статус | +|---|---|---| +| слот соперника, общий на всех Char | `pop_guard.c`, `pop_cdraw.c` | готово | +| `check_shadow` (спецвход тени) | `guards.c:310` | готово для уровней 4/5/6 | +| `do_init_shad` + таблицы `init_shad_5/6` | `guards.c:284` | готово | +| ИИ тени 4/5/6 | `guards.c:599/658/680` | готово | +| диспетчер `autocontrol_shadow` | `guards.c:711` | ветки 12 НЕТ | +| боёвка (удар/блок/парирование/HP соперника) | `guards.c`, `pop_ctrl.c` | готово | +| `flash_color` / `flash_time` | `pop_map.c`, `sprpop.c` | готово | +| `add_life` | `pop_map.c` | готово | +| таблицы уровней (`guard_type`, `guard_hp`, `entry_pose`, `level_type`) | `pop_level_cold.c:41..52` | готово, включая 12=SHADOW, 13=VIZIER | +| loose-полы, `make_loose_fall`, mob | `pop_map.c`, `pop_room.c` | готово (без спецкейсов ур. 13) | + +То есть каркас есть весь; ниже — недостающие спецсобытия. + +--- + +## 1. Уровень 12 — тень: встреча, бой, слияние + +### 1.1 Подъём тени в комнате 15 (`check_shadow`, seg002:0070) + +```c +if (current_level == 12) { + if (!united_with_shadow && drawn_room == 15) { + Char.room = drawn_room; + if (get_tile(15, 1, 0) == tiles_22_sword) return; // меч ещё лежит + shadow_initialized = 0; + do_init_shad(init_shad_12, 7 /* fall */); + return; + } +} +``` + +Отличия от наших веток 5/6: тень поднимается **в падении** (seq 7), условием +служит содержимое тайла (меч уже подобран) и флаг `united_with_shadow`. +Таблица `init_shad_12 = {0x0F, 0x51, 0xE8, 0, 0, 0, 0, 0}` — то есть +x=81, y=232, вправо, колонка 0, ряд 0. + +**Что добавить:** ветку в `pop_check_shadow` + константу `init_shad_12` + +глобалы `united_with_shadow`, `shadow_initialized`. + +### 1.2 ИИ тени (`autocontrol_shadow_level12`, seg002:1184) + +Самая содержательная функция уровня. Три режима: + +1. **Первый кадр в комнате 15**: пока Кид не подошёл (`Opp.x < 150`) — + `shadow_initialized = 1`; иначе тень ещё раз падает (`do_init_shad`). +2. **Кид с мечом** (`Char.sword >= sword_2_drawn`) → тень дерётся обычным + `autocontrol_guard_active` (у нас есть, `guards.c:535`). Особый случай: + если тень уже ранена (`offguard != 0 && guard_refrac != 0`) — она убирает + меч (`move_4_down`). +3. **Кид убрал меч** → тень тоже убирает и идёт навстречу; на дистанции + `< 10` — **СЛИЯНИЕ**: + +```c +flash_color = color_15_brightwhite; flash_time = 18; +add_life(); // +1 к максимуму HP +united_with_shadow = 42; // время вспышки Кид-тень +Char.charid = charid_0_kid; savekid(); // Кид ПЕРЕЕЗЖАЕТ на место тени +clear_char(); // тень со сцены +``` + +Плюс «если Кид бежит к тени — тень бежит к Киду» (кадры бега 3..14 и +шага 127..132). + +**Что добавить:** `autocontrol_shadow_level12` в `guards.c` + ветку в +диспетчере `autocontrol_shadow` (там уже три ветки, будет четвёртая). + +### 1.3 Общий урон (`do_delta_hp`, seg000:1518) + +```c +if (Opp.charid == charid_1_shadow && current_level == 12 && guardhp_delta != 0) + hitp_delta = guardhp_delta; // ранил тень — ранил себя +``` + +Три строки, но без них бой с тенью теряет смысл. У нас `do_delta_hp` +портирован — добавить условие. + +### 1.4 Таймер вспышки (`do_timers`, seg003:503) + +```c +if (united_with_shadow > 0) { + --united_with_shadow; + if (united_with_shadow == 0) { --united_with_shadow; /* → -1 */ ... } +} +``` + +`united_with_shadow` живёт как счётчик, потом как «уже слились» (−1). +На него смотрят `check_shadow` и `check_can_guard_see_kid`. + +### 1.5 Луч видимости (`check_can_guard_see_kid`, seg003:702) + +```c +if ((Guard.charid != charid_1_shadow || current_level == 12) && ... +``` + +У нас (`guards.c:103`) условие про тень **нужно сверить**: на уровне 12 тень +ОБЯЗАНА быть видимой (иначе Кид не достанет меч и бой не начнётся). + +### 1.6 Меч исчезает (`sword_disappears`, seg002:0536) + +```c +if (current_level == 12 && Char.room == 18) { + get_tile(15, 1, 0); + curr_room_tiles[curr_tilepos] = tiles_1_floor; + curr_room_modif[curr_tilepos] = 0; +} +``` + +Срабатывает при уходе Кида ВПРАВО из комнаты 18 (`leave_room`, ветка 1). +У нас есть `pop_level_set_tile` — порт на пять строк. + +### 1.7 Переход 12 → 13: НЕТ двери уровня, есть «бесшовный выход» + +Проверено по данным (`res2012.bin` / `res2013.bin`) — портала действительно +нет, уровень кончается **фактом попадания в комнату**: + +```c +// play_level_2, seg000:900 +} else if (custom->tbl_seamless_exit[current_level] >= 0) { + if (Kid.room == /*23*/ custom->tbl_seamless_exit[current_level]) { + ++next_level; + stop_sounds(); + seamless = 1; + } +} +``` + +`tbl_seamless_exit[12] = 23` (пара «уровень, комната» читается из оригинального +`PRINCE.EXE`, options.c:724). Дальше геометрия складывается так: + +| | комната | ряд 1 | что происходит | +|---|---|---|---| +| ур. 12 | 13 | `floor bigpil empty empty wall wall …` | Кид бежит ВЛЕВО с колонки 0 | +| ур. 12 | 23 | `empty ×6, floor floor floor floor` | попал сюда → **уровень сменился** | +| ур. 13 | 23 | `floor bigpil floor ×6 bigpil floor` | старт: ряд 1, кол 9, `seq_84_run` | + +Связи: `комната 13.left = 23`, и в комнату 23 больше ниоткуда не войти. +Номер комнаты у обоих уровней **один и тот же (23)**, стартовая позиция +уровня 13 — `BP_START = 23, поз 19 (ряд 1, кол 9), dir 0`, а +`tbl_entry_pose[13] = 2` даёт «вбегающий» вход (`seq_84_run`, seg003:172). +То есть Кид вбегает в комнату слева-направо… нет, `Char.direction = +~level.start_dir` — влево, тем же ходом, каким выбежал из уровня 12. Швов не +видно. + +Что делает флаг `seamless` (всего два места, оба косметические): + +* `start_level`, seg003:158 — **НЕ сбрасывает HP**: Кид уносит на уровень 13 + то здоровье, с которым добежал; +* `show_level`, seg008:1861/1870 — не показывает заставку «LEVEL 13» и тут же + гасит флаг. + +Двери уровня при этом на карте есть, но не при делах: у уровня 12 она одна +(комната 3, tilepos 23) — это ВХОД (стартовая комната 12-го — 3), а у уровня +13 их две (комната 3 tilepos 13 и комната 5 tilepos 24) — это выходы, +открываемые кнопкой из `Jaffar_exit`. + +**Что это значит для нас.** Наш переход уровня сейчас идёт только через +`SEQ_END_LEVEL` в двери. Для 12-го нужен второй триггер — проверка в главном +цикле «`pop_current_level == 12 && cur_room == 23` → `pop_next_level++`» плюс +флаг `seamless`, который пропустит сброс HP в `pop_start_level`. Обе правки +маленькие и локальные. + +--- + +## 2. Уровень 13 — Джафар + +### 2.1 Кто такой Джафар + +`tbl_guard_type[13] = 3` → `VIZIER.DAT` (у нас в `pop_level_cold.c` уже 3). +`tbl_guard_hp[13] = 6`. Отдельного ИИ у него нет: + +```c +void autocontrol_Jaffar() { autocontrol_guard(); } // seg002:0697 +``` + +То есть **бой с Джафаром — обычный бой стражи**, отличаются только спрайты, +HP и три спецсобытия ниже. Это хорошая новость: боёвка у нас есть. + +### 2.2 Встреча (`meet_Jaffar`, seg002:0544) + +```c +if (current_level == 13 && leveldoor_open == 0 && Char.room == 3) { + play_sound(sound_29_meet_Jaffar); + guard_notice_timer = 28; // Джафар ждёт 28/12 ≈ 2.33 с +} +``` + +Срабатывает при уходе Кида ВПРАВО (`leave_room`, ветка 1). +Пара к нему — в `autocontrol_guard_inactive` (seg002:0734), она у нас уже +портирована (`guards.c:401`), но **без условия по уровню**: + +```c +if (can_guard_see_kid) { + if (current_level != 13 || guard_notice_timer == 0) move_down_forw(); +} +``` + +**Что добавить:** глобал `guard_notice_timer` + его тик в `do_timers` +(seg003:509) + оба условия. + +### 2.3 Победа (`on_guard_killed`, seg006:1929) + +```c +} else if (current_level == 13) { + flash_color = color_15_brightwhite; flash_time = 18; + is_show_time = 1; + leveldoor_open = 2; // ← ключ к выходу + play_sound(sound_43_victory_Jaffar); +} +``` + +и парная `Jaffar_exit` (seg002:0517), срабатывающая при уходе Кида ВЛЕВО: + +```c +if (leveldoor_open == 2) { get_tile(24, 0, 0); trigger_button(0, 0, -1); } +``` + +То есть смерть Джафара не открывает дверь сама — она ставит флаг, а дверь +открывается кнопкой, «нажатой» при уходе влево. `trigger_button` у нас есть. + +### 2.4 Падающие плиты (`check_fall_flo`, seg000:1319) + +```c +if (current_level == 13 && (drawn_room == 23 || drawn_room == 16)) { + curr_room = room_A; // комната СВЕРХУ + for (curr_tilepos = 22; curr_tilepos <= 27; ++curr_tilepos) + make_loose_fall(-(prandom(0xFF) & 0x0F)); // ОТРИЦАТЕЛЬНЫЙ модификатор +} +``` + +Вот это и есть «плиты появляются»: при входе в комнаты 23/16 шесть плит ряда 2 +комнаты СВЕРХУ получают отрицательную фазу — то есть отложенный старт, и +сыплются на Кида вразнобой. Зовётся из `check_the_end` при смене комнаты. + +**Три спецкейса уровня 13 в loose-механике**, без них это не работает: + +| место | что | зачем | +|---|---|---| +| `animate_loose`, seg007:823 | при `modif & 0x80` НЕ останавливать тряску | иначе отрицательная фаза не досчитает до нуля и плита не упадёт | +| `loose_make_shake`, seg007:949 | на уровне 13 сотрясение НЕ трясёт плиты | иначе отложенные плиты сбрасываются в 0x80 | +| `fell_on_your_head`, seg007:1218 | плита бьёт и в БЕГЕ (кадры 5..14) | на прочих уровнях бегущего не задевает | + +У нас первый пункт критичен: `pop_loose_tick` трактует бит 7 как «тряска» и +на `>= 0x84` гасит фазу — отрицательный старт умрёт, не начавшись. Заодно +это ровно та же ветка, что мы правили сегодня в +[`LOOSE-ROOM-CHANGE`](BUGS_OPEN.md#loose-room-change), так что +код на виду. + +### 2.5 Поза входа + +`tbl_entry_pose[13] = 2` — у нас в таблице уже есть; проверить, что режим 2 +(`seg003:172`) реализован. + +--- + +## 3. Уровень 14 — принцесса и конец игры + +Боя нет вовсе (`tbl_guard_type[14] = -1`). Всё сводится к одному событию: + +```c +// check_the_end, seg000:1299 +if (current_level == 14 && drawn_room == 5) end_sequence(); +``` + +`end_sequence` (seg001:573) → `end_sequence_anim` (seg001:332): катсцена +«Кид добежал до принцессы» — обнимаются, появляется мышь, затухание, Hall of +Fame. Персонажи там играются ТЕМ ЖЕ интерпретатором `seqtbl` +(`seq_108_princess_turn_and_hug`, `seq_101_mouse_stands_up`), то есть движок +у нас уже подходит — нужны спрайты принцессы (`PRINCESS.DAT`) и раскадровка. + +**Оценка:** это не игровая механика, а ролик. Логично делать вместе с интро +и межуровневыми вставками — отдельным банком, как и договаривались +(см. memory `pop_banking_architecture`). На проходимость игры не влияет: +достаточно довести Кида до комнаты 5 и показать заглушку. + +--- + +## 4. Уровень 15 — «уровень зелий» (защита от копирования) + +Не часть сюжета. Это экран проверки подлинности из оригинала: после +уровня `copyprot_level` игра подменяет номер на 15, показывает комнату с +**14 зельями, на которых нарисованы буквы**, и требует выпить нужное. + +Механика (всё под `USE_COPYPROT` в SDLPoP): + +| место | что делает | +|---|---| +| `play_level`, seg003:53 | `level_number == copyprot_level` → грузим 15 | +| `redraw_screen`, seg003:273 | поверх зелий рисуются БУКВЫ (`copyprot_letter`) | +| `load_alter_mod`, seg008:1204 | одно зелье в комнате делается «открытым» (тип 6) | +| `animate_potion`, seg007:259 | на уровне 15 своя ветка перерисовки | +| `up_pressed`/`do_pickup`, seg005:657 | выпитое зелье убирает букву из таблицы | +| `seq` эффект зелья, seg006:1896 | синие зелья на уровне 15 отнимают ПОЛОВИНУ HP | +| выход, seg000:700 | из 15 возвращаемся в `copyprot_level` | + +**Рекомендация: не портировать.** Это анти-пиратский экран 1989 года, +требующий книжки-манускрипта; SDLPoP держит его выключенным по умолчанию +(`enable_copyprot`). Единственное, что стоит взять — **половинный урон +синего зелья**, если вдруг захочется полной совместимости; остальное только +съест банк. Если решим делать — это отдельная фича «уровень 15», а не часть +основного прохождения. + +--- + +## 5. Уровень 0 — демо-уровень (аттракт) + +`res2000.bin` у нас распакован. Это тот самый ролик, который крутится на +титульном экране: Кид сам бежит, дерётся со стражем и убегает. + +Как устроено: + +| место | что | +|---|---| +| `do_demo`, seg006:1409 | на уровне 0 вместо чтения клавиатуры зовётся `do_demo()` + `control()` | +| `autocontrol_kid`, seg002:702 | Кид управляется тем же `autocontrol_guard` | +| `do_auto_moves`, seg002:1089 | проигрыватель ЗАПИСИ ходов: таблица `(time, move)` | +| `on_guard_killed`, seg006:1928 | на уровне 0 после убийства стража Кид убегает (`checkpoint = 1`, сброс демо) | +| `demo_index` / `demo_time` | позиция в записи; у нас уже объявлены в `guards.c:267` | + +**Хорошая новость:** движок автодвижений (`do_auto_moves`) у нас уже есть — +он нужен был тени на уровнях 4/5/6, и `demo_time`/`demo_index` объявлены там +же. То есть демо-уровень — это в основном таблица ходов + ветка «Кидом +управляет ИИ» в `pop_ctrl`. + +**Когда делать:** вместе с интро/титульным экраном, не раньше. На +прохождение не влияет. + +--- + +## 6. Порядок работ + +Порядок задан пользователем 2026-08-13: **строго по номерам уровней**, а не +по дешевизне кода — уровень 13 не имеет смысла раньше, чем на него можно +попасть. + +1. ✅ **Уровень 12** — тень: `init_shad_12`, `autocontrol_shadow_level12`, + `united_with_shadow`, общий урон, «убил тень — убил себя», + `sword_disappears`, появление плит. Сделано 2026-08-13, хост-набор + `SprPoP/tests-host/t_shadow.c` (45 проверок); живой проверки в MAME + ещё не было — доска [`L12-SHADOW`](TASKS_OPEN.md#l12-shadow). +2. ✅ **Переход 12 → 13** (§1.7) — room-триггер вместо двери и флаг + `pop_seamless` (не сбрасывать HP). Сделано там же. +3. ✅ **Уровень 13** — Джафар: `guard_notice_timer`, + `on_guard_killed`/`Jaffar_exit`, три спецкейса loose (§2.4) и + `check_fall_flo`. Плюс СПРАЙТЫ визиря (VIZIER.DAT) — без них он + рисовался обычным стражем. Сделано 2026-08-13, хост-набор + `SprPoP/tests-host/t_jaffar.c` (44 проверки); живой проверки в MAME ещё + не было — доска [`L13-JAFFAR`](TASKS_OPEN.md#l13-jaffar). +4. **Уровень 14** — довести до комнаты 5 и поставить заглушку вместо ролика; + сам ролик — в общую задачу «катсцены». +5. **Уровень 0 и 15** — отложить: аттракт и защита от копирования на + прохождение не влияют. + +--- + +## 7. Сверка констант с ОРИГИНАЛЬНЫМИ данными (сделана 2026-08-13) + +Всё ниже снято скриптом прямо с `../SDLPoP/data/LEVELS/res2013.bin` и +`res2012.bin` — не из головы и не из констант SDLPoP. + +### 7.1 Падающие плиты уровня 13 — константы сходятся ТОЧНО + +Комнаты, у которых в ряду 2 вообще есть loose: + +| комната | loose в колонках | комната снизу | +|---|---|---| +| 17 | 2, 3, 4, 5, 6, 7 | **23** | +| 1 | 2, 3,    5, 6, 7 | **16** | +| 14 | 2 | 24 | + +Связи: `комната 23: up = 17`, `комната 16: up = 1`. То есть +`loose_tiles_room_1 = 23` и `room_2 = 16` — это ровно те две комнаты, над +которыми лежит ПОЛНАЯ гряда плит, а `first_tile = 22, last_tile = 27` — ровно +ряд 2, колонки 2..7: + +``` +комната 23 → сверху 17: 22:LOOSE 23:LOOSE 24:LOOSE 25:LOOSE 26:LOOSE 27:LOOSE [6/6] +комната 16 → сверху 1: 22:LOOSE 23:LOOSE 24:empty 25:LOOSE 26:LOOSE 27:LOOSE [5/6] +``` + +Два вывода: + +* диапазон 22..27 — **надмножество**: в комнате 1 тайл 24 пустой, и + `make_loose_fall` его молча пропустит (проверяет тип тайла). Копировать + константы можно как есть; +* пара «комната 14 → 24» НЕ входит в спецсобытие намеренно — одна плита, это + обычный loose. + +**Важное следствие, которого не было в плане:** стартовая комната уровня 13 — +**23** (`BP_START = 23`, поз 19, dir 0), а `check_fall_flo` зовётся из +`draw_level_first` → `check_the_end` (seg003:217). Значит плиты начинают +сыпаться СРАЗУ при входе на уровень, это его первый кадр, а не событие +где-то в середине. + +### 7.2 Джафар — один страж со skill 9 + +В `res2013.bin` заполнен ровно один слот стража: + +| комната | тайл | ряд, кол | направление | skill | +|---|---|---|---|---| +| 1 | 7 | 0, 7 | 255 (влево) | **9** | + +И это сходится с `meet_Jaffar`: событие срабатывает, когда Кид уходит ВПРАВО +из комнаты 3, а `комната 3: right = 1` — то есть ровно туда, где стоит +Джафар. Skill 9 у нас поддержан: `NUM_GUARD_SKILLS = 12`, все шесть таблиц +вероятностей (`guards.c:411..415`) имеют индекс 9. HP = `tbl_guard_hp[13] = 6` +(в `pop_level_cold.c` уже стоит). + +`Jaffar_exit` тоже проверен: **комната 24, tilepos 0 = `opener`** (кнопка), +модификатор 0 — то есть `trigger_button(0,0,-1)` жмёт реальную кнопку, а не +пустой тайл. + +### 7.3 Уровень 12 — меч на месте + +`комната 15, tilepos 1 = SWORD` — условие подъёма тени +(`get_tile(15,1,0) == tiles_22_sword`) на наших данных выполняется. +`комната 18: right = 19` — `sword_disappears` срабатывает при уходе вправо. + +Комната 15 целиком (по ней видно всю сцену встречи с тенью): + +``` +ряд 0: floor SWORD floor torch torch LOOSE LOOSE floor empty empty +ряд 1: wall wall wall empty empty empty empty wall floor floor +ряд 2: wall opener pillar empty empty empty empty wall pillar empty +``` + +### 7.4 `pop_loose_tick` действительно убьёт отрицательную фазу + +Подтверждено чтением кода: `m = ++pop_loose_modif[pos]; if (m & 0x80) { if (m +>= 0x84) → сброс в 0 }`. Для стартового `0xF0..0xFF` первый же тик даёт +`m >= 0x84` → фаза обнуляется, плита не падает. **Это и есть та правка №1 из +таблицы §2.4**, и она обязательна. + +### 7.5 `init_shad_12` и байтовое переполнение `y` + +`init_shad_12 = {0x0F, 0x51, 0xE8, 0, 0, 0, 0, 0}` → frame 15, x 81, +**y 232**, вправо, колонка 0, ряд 0, action 0 + `seq 7 (fall)`. Поле `y` в +`char_type` — `byte` (беззнаковое), то есть 232 лежит НИЖЕ поля (192), и +падение уводит его дальше с переполнением байта: тень «падает» из-под экрана и +появляется сверху. У нас `pop_char_t.y` тоже `uint8_t` — поведение +переносится без правок; при портировании просто не «чинить» это как ошибку. + +Отдельно про ряд: `curr_row` берётся ИЗ ТАБЛИЦЫ и равен **0**, хотя +`pop_y_to_row(232)` дал бы −1. `do_init_shad` копирует семь полей как есть и +ряд не пересчитывает, так что «согласовать» их — значит разойтись с +оригиналом. Закреплено тестом `shadow12_rises_when_sword_gone`. diff --git a/applications/SprPoP/docs/levels_plan.md b/applications/SprPoP/docs/levels_plan.md new file mode 100644 index 0000000..524175e --- /dev/null +++ b/applications/SprPoP/docs/levels_plan.md @@ -0,0 +1,209 @@ +# План: от одного уровня к нескольким (загрузка, переходы, тайлсеты) + +Статус: план, 2026-08-01. Продолжает `PORT_PLAN.md` §7 (Фаза 1: «переходы +между экранами» → теперь между УРОВНЯМИ). Текущая точка: `SprPoP` играет +уровень 1 целиком в одной комнате-за-комнатой модели, но уровень нельзя +ни выбрать, ни закончить. + +Источник истины — `../SDLPoP/src/` (правило `../CLAUDE.md`). Ключевые +места: `seg000.c: load_lev_spr/play_level_2/init_game`, `seg005.c: +up_pressed/go_up_leveldoor`, `seg006.c: play_seq → SEQ_END_LEVEL`, +`seg002.c` (спецсобытия уровней), `data.h:835..850` (потабличные различия +уровней). + +--- + +## 0. Что уже готово (не проектировать заново) + +- **Формат и загрузчик уровня.** `pop_level.c/.h` читает сырой + `res200N.bin` (2305 Б) в отдельную EMM-страницу; путь — параметр + `pop_level_load(const char *)`. Мультиуровневость здесь стоит одной + функции формирования имени. +- **Стартовая позиция уровня** уже разобрана: `pop_level_start_room()`, + `pop_level_start_pos()`, `pop_level_start_dir()` — реализованы и пока + НЕ вызываются (см. `../PoP/SprPoP/TASKS_CLOSED.md` L1-START). +- **Страж по данным уровня**: `pop_level_guard()` (порт `enter_guard`), + сохранение состояния между комнатами (`pop_guard_state_save`). +- **Палитра разложена по слотам ровно как в оригинале** (`pop_pack_kid.py` + `build_palette`): env 0x50, wall 0x60, pot 0x40, kid 0x70, меч 0x80, + страж 0x90. Это тот же раскрой, что `set_pal_arr(0x50/0x60)` в + `seg000.c:1140..1148`, — значит смена тайлсета не требует переиндексации + спрайтов Кида (см. §3). +- **Все 16 файлов уровней распакованы**: `../SDLPoP/data/LEVELS/res2000.. + res2015.bin` (0 — демо-уровень). + +--- + +## 1. Что реально различается между уровнями (замер по данным, не по памяти) + +Таблицы из `../SDLPoP/src/data.h:840..847` + инвентарь тайлов, снятый прямо +с `res200N.bin` (маска `fg & 0x1F`): + +| Ур. | Тайлсет | Страж | Новое против предыдущих | +|-----|---------|-------|--------------------------| +| 1 | dungeon | обычный | — (текущая база) | +| **2** | **dungeon** | **обычный** | **ничего нового: тот же набор объектов минус меч** | +| 3 | dungeon | СКЕЛЕТ | чомперы | +| 4 | palace | обычный | **тайлсет palace**, зеркало (спецсобытие `mirror_level`) — **СДЕЛАНО** | +| 5 | palace | обычный | новых ТАЙЛОВ нет; спецсобытие **тень крадёт зелье** (комната 24) — [L5-SHADOW](TASKS_OPEN.md#l5-shadow) | +| 6 | palace | ТОЛСТЫЙ | падение на входе (спецсобытие) | +| 7 | dungeon | обычный | — | +| 8, 9 | dungeon | обычный | — | +| 10, 11 | palace | обычный | — | +| 12 | dungeon | ТЕНЬ | seamless-выход (комната 23), исчезающий меч | +| 13 | dungeon | ВИЗИРЬ | мышь, особый выход | +| 14 | palace | нет | — | +| 15 | dungeon | нет | финал | + +Прямое следствие для порядка работ: **уровень 2 не требует ни одного нового +ассета и ни одной новой механики** — он проверяет ровно машинерию перехода. +Это и есть первый шаг. + +Прочие потабличные различия, которые придётся завести массивами по 16: +`tbl_level_type` (тайлсет), `tbl_guard_type` (−1 = стражей нет), +`tbl_guard_hp`, `tbl_level_color` (вариантные палитры, 1.3), `tbl_entry_pose`. + +--- + +## 2. Шаг 1 — машинерия перехода (цель: уровни 1 → 2 → 3) — **СДЕЛАН 2026-08-04** + +> **Итог.** Всё в этом разделе портировано и проверено в MAME: уровень 1 → +> Shift+L → уровень 2 (комната 5, дверь захлопывается за спиной, большие +> колонны рисуются) → уровень 3. Разбор что именно сделано и что по +> уровню 2 осталось — `../PoP/SprPoP/TASKS_CLOSED.md`, запись **L2**. +> +> Сверх плана пришлось доделать две вещи, без которых уровень 2 не играется: +> **`find_start_level_door`** (стартовый тайл уровня 2 — это правая половина +> двери уровня) и **большую склянку** `add_life` (тип зелья 2, комната 20). + +Порядок именно такой; каждый пункт проверяем в MAME отдельно. + +**2.1 Выход с уровня.** Портировать `up_pressed()` ветку двери +(`seg005.c:410..423`) + `go_up_leveldoor()` (`seg005.c:497`): дверь рядом +(при/за/перед персонажем) И `drawn_room != level.start_room` И створка +открыта полностью (`curr_room_modif >= 42` — вариант `fix_exit_door`) → +`Char.x = x_bump[...] + 10`, направление влево, `seq_70_go_up_on_level_door`. +Затем оживить опкод `0xF1 END_LEVEL` в `play_seq` (`../src/pop_kid.c:418` +— сейчас пустой `break`): `++pop_next_level`, как `seg006.c:662`. + +**2.2 Цикл уровня.** В `main()` после тика: `if (pop_next_level != +pop_current_level) → load_level(pop_next_level)`. Порядок сноса/подъёма +состояния (порт `load_lev_spr` + `play_level_2`): +`pop_level_free` → `pop_level_load("LEVELS\\res200%d.bin")` → +`pop_trob_reset` → `pop_guard_reset` → сброс tile-override'ов +(`ovr_*` в `sprpop.c`) → `enter_room(pop_level_start_room())` → +`kid_init(поза/позиция/направление из данных уровня)`. +**HP через уровень переносится** (в оригинале `hitp_beg_lev`), не сбрасывать +в максимум — сверить с `seg000.c` `init_game`/`play_level_2`. + +**2.3 Стражи по уровню.** Завести `tbl_guard_type[16]`/`tbl_guard_hp[16]`; +`-1` = стражей на уровне нет (уровни 14, 15) — `pop_guard_enter` обязан это +понимать, иначе на 14-м полезут стражи из мусора. Для шага 1 (уровни 2, 3) +достаточно обычного стража, но проверку `-1` заложить сразу. + +**2.4 Чит «следующий уровень» (Shift+L).** Реализуется ровно тем же +`pop_next_level` — и без него отладка уровней превращается в прохождение +игры руками. Делать в этом же шаге, не позже (см. §4). + +**Приёмка шага 1:** дверь уровня 1 → уровень 2 играется целиком → его дверь +→ уровень 3 стартует (чомперы могут быть ещё не портированы — тогда +фиксируем как известное ограничение, а не «баг»). + +--- + +## 3. Шаг 2 — второй тайлсет (palace, уровни 4+) — **СДЕЛАН** + +> **Закрыт (ревизия 2026-08-11 по коду).** В дереве есть всё, что +> проектировалось ниже: `pop_pack_bg.py` печёт ДВА набора атласов +> (`pop_*` из VDUNGEON, `pal_*` из VPALACE, каскад с обменом приоритетов), +> `pop_bg_load` принимает тип набора и ставит флаг `pop_palace`, по которому +> `pop_room.c` выбирает дворцовую ветку (`wall_pattern` дворца — сплошная +> заливка + пять mono-полос, `PALACE_WALL_MONO_IDS`), решётчатые тайлы +> 25-29 есть и в `tile_table` (`pop_tile.c`), и в коллизии — `tile_is_floor` +> совпадает с `seg006:0628` тайл в тайл. Уровень 4 проходится smoke-тестом. +> +> Текст ниже оставлен как справочник по раскрою палитры и по тому, почему +> смена тайлсета — это перезапись 32 записей, а не переиндексация спрайтов. + +**Ассеты.** `toolchain/pop_pack_bg.py` уже читает PNG каскадом +VDUNGEON→VPALACE (та же логика, что в игре), но печёт ОДИН набор атласов +(`pop_env0..4.atl`, `pop_wall.atl`, `pop_fore.atl` ≈ 75 КБ). Нужен второй +набор из VPALACE (`pal_env*.atl` / `pal_wall.atl`), плюс `torch_debris` — +тайл, который встречается только на palace-уровнях. По EMM это ещё ~6 +страниц при бюджете ~3.3 МБ — не проблема. + +**Палитра — главный технический вопрос, и он уже решён раскроем.** +Тайлсет живёт в слотах `0x50..0x5F` (env) и `0x60..0x6F` (wall); Кид, меч, +страж, склянки — в других слотах. Значит смена тайлсета = перезапись 32 +записей палитры (`gfx_pal_set` на обе страницы, как `flash_bg` в +`sprpop.c`), а НЕ перезагрузка `kid.pal` и не переиндексация спрайтов. +Сделать `pal_dungeon.bin` / `pal_palace.bin` (по 32 записи) и грузить при +смене типа уровня. Проверить артефактом: скриншот palace-комнаты против +рендера `render_room.py` для того же уровня. + +**Вариантные цвета уровней** (`tbl_level_color`, `level_var_palettes` — это +уже 1.3, в 1.0 их нет): по той же механике, тот же диапазон слотов. Решение +на будущее — сначала базовые два тайлсета, потом при желании цвета. + +**Выбор набора в коде.** `pop_bg_load()` сейчас грузит фиксированные имена; +превратить в `pop_bg_load(type)` с двумя таблицами имён + выгрузка старых +атласов при смене типа (`atlas_free`). Переключение — только на границе +уровня, не в кадре. + +--- + +## 4. Читы SDLPoP: что взять на следующем этапе + +Из `../SDLPoP/README.md` (раздел Cheats). У нас уже есть: **K** — убить +стража, **I** — бессмертие (наш, в оригинале нет), **S** — выдать меч (наш), +**+/−** — обход комнат (`ROOMNAV`, наш). + +**Брать сразу вместе с переходами уровней** (без них отладка дороже самой +работы): + +| Чит | Что даёт | Цена | +|-----|----------|------| +| **Shift+L — следующий уровень** | единственный вменяемый способ тестировать уровни 2..15 | тривиально: `++pop_next_level` из §2.2 | +| **R — воскресить Кида** | у нас респавн по ↑ + таймаут; порт `resurrect` ближе к оригиналу и не мешает управлению | низкая | +| **Shift+S / Shift+T — +1 HP / +максимум** | отладка боёвки без «ровно трёх попыток»; честная замена нашему читу бессмертия | низкая, HP-машинерия уже есть | +| **[ и ] — сдвинуть Кида на пиксель** (debug-чит SDLPoP) | прямо бьёт в наш класс багов «окклюзия/шов на один пиксель» — воспроизведение позы без ловли момента | тривиально | + +**Брать во вторую очередь:** + +| Чит | Почему позже | +|-----|--------------| +| **H / J / U / N + Ctrl+B — смотреть соседние комнаты** | требует честной модели `drawn_room ≠ Kid.room` (наш S3-straddle, каркас есть: `update_kid_render_dx`). Зато потом заменяет самодельный `ROOMNAV` и попутно закрывает straddle-задачу | +| **Shift+W — медленное падение (feather)** | ветка `JMP_IF_FEATHER` (опкод `0xF7`) в `play_seq` уже есть, но не проверена ничем — чит станет её единственным тестом | +| **C / Shift+C — номера комнат** | у нас номер рисуется палочками именно потому, что текст тянет 2 КБ знакогенератора в W2 (`sprpop.c`). Ждёт своего шрифта | + +**Не брать:** `Shift+I` (переворот экрана), `Shift+B` (blind mode) — +развлекательные, к отладке порта отношения не имеют. `−/+` (время) — нужен +таймер уровня, которого у нас нет (Фаза 6). + +**Отдельно, дорого, но очень ценно — `F6`/`F9` (quicksave/quickload точного +состояния).** Это сериализация `Char` + `room_modif` всех комнат + trob'ов + +состояния стражей. Даёт то, чего нам сейчас сильно не хватает: +воспроизводимый регресс в MAME («вот кадр, где баг») вместо ручного подхода +к позиции. Кандидат сразу после того, как заработают уровни. + +--- + +## 5. Риски и что проверить артефактом до кодинга + +1. **Размер кода.** Замер сборки 2026-08-01: `_CODE` 25 119 Б, + куча ~2.4 КБ, банк 2 (`pop_bg`) 13 792 / 16 384, банк 3 (`pop_map`) + 6 331, банк 1 (`guards`) 1 896, банк 4 (`pop_gdraw`) 2 236. Чомперы, + зеркало, скелет и второй тайлсет пойдут в банк 2 — там осталось 2.6 КБ. + **Прежде чем начинать §3, посчитать, куда лягут новые тайлы**, иначе + повторится история «банк 2 упёрся в потолок» (коммит 2f3e854). Свободные + номера банков есть (5+), гранулярность — файл. +2. **Спецсобытия уровней** (`seg002.c`: `level3_set_chkp`, `sword_disappears`, + `Jaffar_exit`, зеркало, мышь) — их НЕ надо портировать заранее. Для + уровней 2 и 3 нужен только чекпойнт уровня 3. Остальное — по мере + подхода к уровню. +3. **Чомперы** (уровень 3 и почти все дальше) — отдельная механика + (`animate_chomper` + коллизия + смерть); шаблон работы тот же, что у + пик/ворот, см. `gates_spikes_plan.md`. +4. **`tbl_guard_type = -1`** на уровнях 14/15: без проверки страж + «появится» из неинициализированных данных. +5. **Уровень 0 (демо)** существует в данных, но в скоуп не входит. diff --git a/applications/SprPoP/docs/menu_settings_plan.md b/applications/SprPoP/docs/menu_settings_plan.md new file mode 100644 index 0000000..e8b8d14 --- /dev/null +++ b/applications/SprPoP/docs/menu_settings_plan.md @@ -0,0 +1,529 @@ +# Pause menu и Settings для Sprinter PoP + +Статус: **MS0, MS2 и MS4–MS8 выполнены** (2026-08-23). Pause menu, CFG, +Settings, диалоги, Controls и build screen находятся в bank 9. Решение о +рендеринге и затемнении — §10. + +Связанные документы: + +- [`full_game_plan.md`](full_game_plan.md) — автомат состояний, title, + demo, cutscenes и ending; +- [`quicksave_plan.md`](quicksave_plan.md) — состав и восстановление снимка. + +## 1. Решения + +- Программа работает только с HDD; настройки, QuickSave и Hall of Fame + всегда могут быть постоянными файлами. +- Основной pause menu обязательно содержит QuickSave и QuickLoad. +- QuickSave имеет один слот `POP.SAV`; предыдущая корректная запись хранится + как `POP.BAK`. +- Первая версия имеет один профиль `VANILLA`. Под этим именем пока понимается + **текущее поведение SprPoP**, включая уже встроенные исправления. +- Дизайн файла и API предусматривает будущий `ENHANCED`, но аудит и + переключение fixes сейчас не выполняются. +- Уровень 15/copy protection отсутствует. +- Моды, levelsets и меню Mods отложены. + +## 2. Что есть в SDLPoP + +`src/menu.c` содержит: + +- Resume, QuickSave, QuickLoad, Restart Level, Settings, Restart Game, Quit; +- General, Gameplay, Visuals, Mods, Controls; +- toggle/number/key controls, пояснения, scroll и confirmation dialogs; +- большой список fixes/enhancements и custom level options. + +На Sprinter не переносятся SDL-специфичные параметры: fullscreen, hardware +acceleration, scaling, aspect ratio, rumble. UI берёт структуру SDLPoP, но +набор настроек соответствует платформе. + +## 3. Pause menu первой версии + +```text +RESUME +QUICKSAVE (F6) +QUICKLOAD (F9) +RESTART LEVEL +SETTINGS +RESTART GAME +QUIT GAME +``` + +Поведение: + +- `Esc` в `PLAYING` открывает меню; повторный Esc или Resume возвращает игру; +- игра, логический таймер и звуковой насос ставятся на паузу согласованно; +- QuickSave/QuickLoad только взводят запрос, фактическая операция идёт на + безопасной границе кадра; +- QuickLoad disabled/показывает `NO QUICKLOAD`, если нет валидных SAV/BAK; +- перед QuickLoad из меню лёгкий probe проверяет заголовок и checksum обоих + файлов: валидный `POP.BAK` при отсутствующем/битом `POP.SAV` требует + отдельного `LOAD BACKUP?`, а не загружается молча; +- Restart Level и Restart Game выполняются сразу, БЕЗ подтверждения + (2026-08-22): Restart Level перечитывает уровень, Restart Game завершает + gameplay и возвращает к первому экрану title/intro; новая игра создаётся + общим LEVEL_LOAD только после skip/attract; +- Quit требует подтверждения и закрывает файлы/каналы штатным путём; +- меню недоступно в demo, cutscene, time-expired и ending; +- отдельная debug-комбинация немедленного выхода может остаться только в + отладочной сборке. + +## 4. Settings первой версии + +```text +GENERAL + Sound ON / OFF + Show Sprinter screen ON / OFF + Restore defaults... + +GAMEPLAY + Speed NORMAL / FAST / FASTEST + Gameplay profile VANILLA + Cheats ON / OFF + +CONTROLS + Show key bindings + +BACK +``` + +`Gameplay profile: VANILLA` показывается read-only: место в модели уже есть, +но пользователь не может выбрать ещё не реализованный ENHANCED. + +Изменения применяются немедленно к скорости, читам и звуку, но `POP.CFG` +записывается один раз при Back/Esc. На экране есть итог `SETTINGS SAVED` или +`SAVE ERROR`; во втором случае runtime-значения остаются рабочими. + +Отладочные параметры `ROOMNAV`, border profiling, stop-frame и переключение +double buffering не являются пользовательскими Settings. Они остаются +compile-time/debug функциями и скрываются из release UI. + +## 5. Модель настроек + +Игровой код не должен читать UI-структуры. Единственный runtime-контракт: + +```c +typedef enum { + POP_PROFILE_VANILLA = 0, + POP_PROFILE_ENHANCED = 1 +} pop_gameplay_profile_t; + +typedef struct { + uint8_t sound_enabled; + uint8_t speed_mode; + uint8_t gameplay_profile; + uint8_t cheats_enabled; + uint8_t show_build_info; + uint16_t enhancement_flags; +} pop_settings_t; +``` + +В первой версии загрузчик принимает только `POP_PROFILE_VANILLA`. Значение +ENHANCED из более нового/ручного файла заменяется на VANILLA с диагностикой, +а не включает частично реализованный режим. + +Будущий профиль задаёт маску возможностей централизованно: + +```text +VANILLA -> текущий согласованный набор +ENHANCED -> будущий рекомендуемый набор fixes +CUSTOM -> только если позже действительно понадобится +``` + +До отдельного аудита существующие `fix_exit_door`, feather guard behavior, +jump grab и sound priorities не переключаются и считаются частью текущего +VANILLA. + +## 6. Файл POP.CFG + +Бинарный, компактный, версионированный формат: + +```text ++0 "PCFG" magic, 4 Б ++4 format_version 1 Б ++5 payload_size 2 Б ++7 payload фиксированные поля little-endian +.. checksum 2 Б +``` + +Требования: + +- путь рядом с exe/в выделенном каталоге игры на HDD; +- неизвестная версия, неверная длина или checksum -> defaults; +- неизвестные будущие хвостовые поля можно пропустить по `payload_size`; +- запись только после Apply/выхода из Settings, не на каждый шаг курсора; +- ошибка записи не завершает игру: показать сообщение и оставить runtime + значения; +- Restore defaults меняет RAM только после подтверждения и затем сохраняет. + +CFG не содержит состояние уровня, QuickSave или Hall of Fame. + +## 7. QuickSave / QuickLoad в меню + +Детальный состав снимка и порядок восстановления — в +[`quicksave_plan.md`](quicksave_plan.md). Здесь фиксируется UI и файловая +транзакция. + +### Один слот и backup + +Файлы: + +```text +POP.SAV текущий слот +POP.BAK предыдущий валидный слот +POP.NEW временный файл во время записи +``` + +Безопасная запись: + +1. записать полный снимок в `POP.NEW`; +2. закрыть файл; +3. повторно открыть/прочитать заголовок и checksum; +4. старый валидный `POP.SAV` перенести/скопировать в `POP.BAK`; +5. `POP.NEW` сделать новым `POP.SAV`; +6. при любой ошибке сохранить прежний `POP.SAV`. + +Точную последовательность rename/copy выбрать после характеризации DSS. +Если атомарный rename не гарантирован, использовать copy + fsync/close и +никогда не удалять единственную валидную копию до проверки новой. + +### Загрузка + +1. проверить `POP.SAV`; +2. если он отсутствует/повреждён/несовместим — проверить `POP.BAK`; +3. при валидном BAK показать `LOAD BACKUP?`; +4. несовместимая версия — `INCOMPATIBLE SAVE`, без частичной загрузки; +5. после успеха закрыть menu, перерисовать обе страницы, перезапустить звук. + +### Сообщения + +Минимальный набор: + +```text +QUICKSAVED +QUICKLOADED +NO QUICKLOAD +SAVE ERROR +INCOMPATIBLE SAVE +LOAD BACKUP? +``` + +Сообщение показывается UI-слоем, но операция завершается до возврата в +игровой кадр. + +## 8. Restart Level / Restart Game + +Restart Level: + +- использует существующий штатный reset текущего уровня; +- не перечитывает CFG; +- не меняет `POP.SAV`; +- сбрасывает состояние, которое сбрасывает текущая реализация SprPoP. + +Restart Game: + +- выполняется сразу, без подтверждения; +- завершить текущий gameplay session и вернуть автомат в TITLE; +- начать title/intro с самого первого экрана; +- создать новую игру с `FIRST_LEVEL` и новым глобальным таймером только + после пользовательского skip либо ввода в attract-demo; +- настройки оставить; +- QuickSave не удалять. + +## 9. Controls + +Первая версия только показывает активную раскладку. Переназначение клавиш +откладывается: raw PS/2 канал имеет особенности Shift и расширенных кодов, +поэтому generic key-binding UI требует отдельного проекта. + +Экран должен перечислить минимум: + +- движение и Shift/action; +- Esc/menu; +- F6/F9 QuickSave/QuickLoad; +- Ctrl+S sound; +- P speed; +- доступные cheats, только если они включены: K/Kill Guard, I/Immortal, + Shift+L/Next Level, U/Flip Screen и F7/F8/Time −/+ на отдельных понятных + строках. Нижней подсказки `Esc or Enter: Back` нет. + +## 10. UI renderer и ввод + +### 10.1. Выбор способа отрисовки: текст против спрайт-атласов + +Ограничение платформы: стандартный текстовый вывод libbgi (`outtextxy`) +не годится — он тянет системный знакогенератор в `_gfx_font_buf` (2 КБ +статики в W2) плюс жирный резидентный код, а W1/W2 забиты игрой +(тот же вывод зафиксирован комментарием в `sprpop_cold.c`, где отладочный +борд рисуется палочками именно поэтому). Значит, любой вариант требует +СВОЕЙ реализации вывода меню, живущей в отдельном банке (память на банк +есть; скорость не критична — меню работает на паузе). + +Рассматривались два подхода. + +**Вариант A — текстовые строки + собственный растровый рендерер.** + +Плюсы: +- минимальные данные: шрифт 2–4 КБ + таблицы строк по сотни байт на язык; +- весь динамический текст бесплатно: значения опций (ON/OFF, + NORMAL/FAST/FASTEST), сообщения (`QUICKSAVED`, `INCOMPATIBLE SAVE`), + диалоги (`LOAD BACKUP?`), экран Controls, будущий ввод инициалов + Hall of Fame — без текстового движка HoF вообще не сделать; +- правка формулировки = правка C-строки, мгновенные итерации; +- локализация = вторая таблица строк (+ вторая половина глифов); +- **решающий аргумент: так сделано в самом SDLPoP** — см. §10.2. + +Минусы: +- надо написать рендерер (блиттер глифа + строка + центрирование + + подсветка) — небольшой, но свой; +- вид определяется качеством шрифта-ассета. + +**Вариант B — готовые спрайт-атласы** (атлас главного меню с активными/ +неактивными пунктами, атлас вложенного меню, атлас каждой опции +On/Off и т.д.). + +Плюсы: +- аутентичный вид: любая типографика/декор запекаются при упаковке; +- вывод = существующий блит атласов, текстовый движок не нужен; +- язык = другой файл атласа с диска, ноль логики. + +Минусы: +- комбинаторика ассетов: 7 пунктов × состояния + вложенные меню + значения + всех опций + все сообщения + все диалоги ≈ десятки КБ raw на язык до RLE; + второй язык удваивает; +- любая правка текста = перегенерация ассетов + перекладка ресурсов; +- динамический текст (HoF initials) всё равно потребует шрифтового движка — + получили бы ОБЕ системы сразу. + +**Решение (2026-08-22): Вариант A**, шрифт — ассет. Спрайты остаются только +для нетекстового декора (рамка/фон меню, маркер выделения — как arrowheads +в SDLPoP). Титульный экран — полноэкранная картинка, тема `full_game_plan.md`. + +### 10.2. Референс: как устроено меню в SDLPoP + +`SDLPoP/src/menu.c` + текстовый движок `seg009` — источник структуры: + +- **Текстовые строки + встроенный пропорциональный bitmap-шрифт** + `hc_small_font_data[]` (menu.c:2488): символы 32..126, каждый глиф — + монохромное изображение переменной ширины; `font_type` + {first_char, last_char, space_between_chars, height_above_baseline, chtab}. + Никаких per-item атласов, хотя SDL_ttf доступен. +- Вывод — портированный движок оригинального DOS PoP (seg009): + `draw_text_character` → `method_3_blit_mono(image, x, y, textblit, + textcolor)`; `get_line_width` для центрирования; перенос по словам. + Тем же движком рисуются in-game тексты и copy protection. +- Пункты меню — data-driven C-структуры `{id, previous, next, required, + char text[32]}` + таблицы `pause_menu_items[]` / `settings_menu_items[]`; + `required` — указатель на флаг disabled, такие пункты пропускаются при + навигации (prev/next пересчитываются). +- Выделенный пункт = смена цвета текста (bright-white против обычного) + + рамка-контур `draw_rect_contours(selection_box, lightgray)`; НЕ отдельный + спрайт «активного пункта». +- Фон меню — затемнение замороженного игрового кадра: + `draw_rect_with_alpha(black, alpha=120)`, внизу просвечивает «GAME PAUSED». +- Settings — декларативная таблица `setting_type` со стилями TOGGLE / NUMBER / + TEXT_ONLY / KEY, геттером/сеттером/increase/decrease значения, строкой- + explanation внизу экрана, скроллом длинных списков и фокусом «левая половина + (список) / правая половина (значения)». +- Диалоги — один общий `draw_confirmation_dialog(text)` + обработчик + результата; диалог возвращает решение автомату меню. +- Мини-спрайты только для декора значений (arrowheads up/down/left/right). +- Навигация озвучена (menu tick), ввод клавиатура+мышь, hover по прямоугольникам. + +### 10.3. Наша реализация + +- Банк 9: код рендерера, + шрифт, таблицы строк, автомат меню. Резидентно — только request-flag и + вызов процесса на границе кадра (паттерн pop_qsave_io). +- Рендерер повторяет минимальный контракт seg009: пропорциональные глифы, + baseline, `draw_string` и центрирование по сумме advance. Блит идёт через + W0-атлас, в `GFX_BANK_SPRITE`: `0xFF` в атласе пропускается, а UI временный + и не портит теневую копию игрового фона. Перед каждым кадром UI `gfx_copy_page` переносит чистый + shadow видимой страницы в скрытую, затем готовый кадр показывается только + на следующем фронте. При выходе чистый фон тем же способом возвращается на + обе страницы и восстанавливается исходная visible-страница. Поэтому + перемещение выделения не показывает поэтапную перерисовку и не оставляет + следов на back buffer. +- Шрифт — АССЕТ из **оригинальных** `hc_small_font_data[]` и + `hc_font_data[]` SDLPoP, не системный ZG и не TTF. Паковщик + `toolchain/pop_extract_font.py` делает `FONT\\font.atl`: 95 ASCII-глифов + малого и 95 крупного шрифта (7667 Б). Номер ленты вычисляется из ASCII, + поэтому это один текстовый движок, а не атлас готовых надписей. +- Двуязычность (eng/rus): строки храним в CP866 — латиница и кириллица одним + байтовым порядком, одна кодировка на оба алфавита. Локаль = пара + (указатель на таблицу строк, файл шрифта); переключатель — одна настройка. + Русские строки длиннее английских ~10–15% — раскладку экранов и ширину + колонок закладывать по русской. Второй язык можно добавить позже без + переделки: сначала eng. +- Визуальная композиция MS4 следует SDLPoP: замороженная сцена остаётся + открытой, поверх неё компактный центрированный список без чёрной карточки, + выбранная строка обведена тонким светло-серым контуром, а крупное + `GAME PAUSED` лежит в нижнем борту. Цвета текста и контура берутся из + стабильного диапазона палитры 0x37..0x3F. +- Фон открытого меню: снимок текущей палитры, затемнение всех слотов кроме + UI 0x37..0x3F и точное восстановление при выходе. Снимок хранится в + свободном хвосте EMM-страницы шрифта, не в W2. +- Навигация MS4: вверх/вниз, Enter/Esc, edge-triggered поверх `kbd_raw`. + Left/right и menu tick добавляются вместе с настройками на MS5. + +Первый UI может быть визуально простым. Критично отсутствие потери клавиш, +предсказуемая пауза и отсутствие повреждения игрового back buffer. + +### 10.4. Затенение экрана под меню — решение MS4 + +Режим меню виден сразу: bank 9 делает динамический снимок palette 0, +затемняет RGB-каналы вдвое и пишет одинаковый результат в обе экранные +палитры. Девять стабильных UI-слотов 0x37..0x3F не гасятся. При Resume/Enter +палитра восстанавливается из EMM-снимка. Это выбранный вариант Б ниже; +ступенчатый fade для роликов пока не нужен и остаётся отдельной будущей +задачей, а не причиной раздувать MS4. + +**Как сделано в SDLPoP** (`seg009.c`): + +- Меню: `draw_rect_with_alpha(&screen_rect, color_0_black, pause_menu_alpha)` + (menu.c:1364) — альфа-заливка чёрным поверх замороженного кадра средствами + SDL; нижняя полоса рисуется с alpha=0, чтобы сквозь неё просвечивало + «GAME PAUSED». Прямого аналога на Sprinter НЕТ (альфа-блендинг в железе + отсутствует) — это SDL-специфика, переносить нечего. +- Ролики/переходы: `fade_in_2/fade_out_2(rows)` (seg009.c:3947+, вызовы из + seg000.c) — ПОШАГОВОЕ затухание ПАЛИТРЫ к чёрному и обратно: палитра + копируется, каждая строка по 16 цветов гасится за несколько кадров + (`which_rows` маской выбирает, какие строки участвуют: 0x800/0x1000/...). + Вот этот механизм на Sprinter воспроизводим один в один. + +Отсюда рабочая гипотеза: наш примитив = «снимок текущей палитры → ступенчатое +приближение к затемнённой копии (кроме резервного блока для UI)», статично для +меню и анимированно для роликов/переходов. Варианты: + +**Вариант А — единая основная палитра (глобальный рефакторинг палитры).** + +1. Собрать ВСЕ палитры игры (уровневые наборы `pal_env*`, kid.pal, палитра + Тени и пр.) в одну общую 256-цветную; использовать её целиком всегда. + Сейчас переиспользования цветов НЕТ — каждая загрузка ассетов перезаписывает + слоты (см. pop_boot: kid.pal затирает тайловые цвета, приходится + восстанавливать `pop_bg_pal_apply`/`pop_shadow_pal_apply`). +2. Для затенения — затемнённая копия основной палитры, КРОМЕ зарезервированного + блока из 16 цветов для самого меню (кандидат — стандартные 16 цветов VGA). +3. Выход из меню — возврат к полной основной палитре. + +Плюс: решает попутно существующую боль с перезаписью палитр при загрузках. +Минус: большой разовый рефакторинг упаковщиков и всех загрузчиков атласов; +нужен аудит, что все цвета всех уровней влезают в 256. **Против говорит +план перевода камней подземелья на цвета VGA-версии PoP: там ряд уровней +несёт ДРУГУЮ палитру, отличную от SDLPoP (VDUNGEON/VPALACE каскад, +levels_plan.md), — единая палитра этому прямо противоречит.** + +**Вариант Б — динамический снимок текущей палитры (сейчас выглядит +предпочтительным).** + +1. При открытии меню прочитать всю текущую палитру, сохранить. +2. Записать затемнённую копию (кроме зарезервированного блока для меню). +3. При выходе — восстановить сохранённую. + +Плюс: локальная правка внутри меню, ничего в пайплайне ассетов не меняется; +работает при любой текущей палитре автоматически — включая будущие +уровне-специфичные палитры VGA-камней; тот же примитив ступенями даёт +fade-out/fade-in для роликов и переходов между уровнями (как fade_*_2 в +SDLPoP). Минус: чтение/запись 256 записей палитры при входе/выходе (раз на +открытие — дёшево); затемнение «на глаз» может по-разному выглядеть на разных +уровнях. + +Резервный блок 16 цветов нужен в ОБОИХ вариантах; текущий диапазон 0x37..0x3F +(стабильный, проверен) даёт 9 цветов — этого может не хватить на +текст+подсветку+рамку, тогда резервировать отдельный блок. + +**Следствие для архитектуры:** работа с цветом/палитрой должна собраться в +ОДИН модуль (сейчас она разбросана: gfx_pal_* вызовы в boot, pop_bg_pal_apply, +pop_shadow_pal_apply, вспышки урона в sprpop.c и т.д.). Модуль палитры — +единственный владелец записи в палитру и предоставляет примитивы, которые +понадобятся и меню, и роликам: + +```text +pal_snapshot()/pal_restore() — снимок/восстановление всей палитры +pal_dim(step) / pal_undim(step) — ступени затемнения (кроме резервного блока) +pal_fade_out(rows)/pal_fade_in(rows) — анимированное затухание по строкам + (порт fade_out_2/fade_in_2, seg009) +``` + +Меню уже использует snapshot+dim локально в bank 9. Когда появятся ролики, +выделить из него общий palette/fade-модуль; вспышка урона сможет переехать +туда же после отдельного аудита. + +## 11. Диалоги + +Общий диалог подтверждения: + +```text +QUIT GAME? +RESTORE DEFAULTS? +LOAD BACKUP? + +YES / NO +``` + +Диалог не выполняет действие напрямую: он возвращает решение автомату меню, +который формирует команду приложению. Так UI не зависит от gameplay-модулей. +По умолчанию выбран `NO`; Up/Down/Left/Right меняют ответ, Enter подтверждает, +Esc отменяет. Реализованы все три вопроса: Quit, Restore defaults и backup +QuickLoad. В Quit-dialog вопрос и `YES / [NO]` заключены в общую рамку; +отдельная строка `Enter: Select Esc: Cancel` не выводится. + +## 12. Будущий ENHANCED + +Не реализуется сейчас, но дизайн обязан позволять: + +- добавить второй профиль без смены всего UI; +- хранить `enhancement_flags` в CFG; +- отличать технические исправления порта (всегда включены) от изменений + оригинальной механики; +- провести аудит уже встроенных исправлений; +- покрыть каждый переключаемый fix host/MAME тестом; +- при необходимости добавить Advanced screen, не раздувая основной menu. + +До этого момента нельзя рассыпать проверки `if (enhanced)` по горячему коду. +Сначала составляется реестр и выбирается минимальная битовая модель. + +## 13. Этапы реализации + +| этап | результат | критерий приёмки | +|---|---|---| +| **MS0** ✓ | определить команды app/menu и структуру settings | UI возвращает команду главному циклу; прямых gameplay-вызовов нет | +| **MS1** | проверить запись/rename/copy на HDD DSS | crash/power-loss сценарий не теряет обе копии save | +| **MS2** ✓ | `POP.CFG`: defaults, load, validate, save | v1 codec, будущий хвост, checksum; повреждённый CFG даёт defaults | +| **MS3** | QuickSave hotkeys + POP.SAV/BAK | полный критерий `quicksave_plan.md` | +| **MS4** ✓ | текстовый рендерер + два шрифта (малый для пунктов, крупный для сообщений) + минимальный pause menu | SDLPoP fonts в одном W0-atlas, центрирование, dim/restore palette и tear-free page flip; все семь пунктов видимы, навигация и Resume/QuickSave работают в MAME | +| **MS5** ✓ | General/Gameplay Settings | значения применяются сразу и после Back/Esc записываются в POP.CFG | +| **MS6** ✓ | dialogs + backup recovery | подтверждения default-NO; QuickLoad спрашивает перед валидным POP.BAK | +| **MS7** ✓ | Controls help | показаны движение, action, menu, save/load, звук, speed и conditional cheats; MAME проверил отдельные K/I и Shift+L/U и возврат Esc ровно на один уровень | +| **MS8** ✓ | build info | CFG читается до первого показа; включаемый build screen получает ID и дату из Make/git | + +QuickSave (`MS1/MS3`) можно реализовать раньше визуального menu: сначала +F6/F9 и сообщения, затем подключить те же команды к пунктам UI. + +## 14. Тесты + +- Host: CFG round-trip, defaults, bad magic/version/size/checksum. +- Host: меню navigation, disabled items, confirmations, команды приложению. +- Host: рендерер строк — вывод глифов обеих локалей, центрирование, + ширина строки для малого и крупного шрифта. +- Host: SAV invalid -> BAK valid; оба invalid -> NO QUICKLOAD. +- MAME: F6, изменение сцены, F9; затем рестарт программы и повторный F9. +- MAME: прервать запись/испортить SAV — BAK остаётся загружаемым. +- MAME: pause на бое/падении, Resume не меняет состояние и таймер; смена + выбранного пункта не показывает промежуточный кадр и после закрытия не + оставляет меню на второй странице. +- MAME: Settings сохраняются после полного выхода и запуска с HDD. +- MAME: включить Show Sprinter screen, перезапустить `SprPoP`, увидеть + build ID/date до первого игрового кадра и пропустить экран Esc/Enter/Space. +- Проверка лимита 8 DSS handles на каждом error path. +- `make size-check`; menu/text строки не должны съесть резидентный бюджет. + +## 15. Не входит в план + +- Mods и выбор levelset; +- уровень 15/copy protection; +- несколько save slots; +- replay/recording; +- key rebinding; +- SDL visual/controller options; +- фактическая реализация ENHANCED и individual fix switches. diff --git a/applications/SprPoP/docs/midtable_analysis.md b/applications/SprPoP/docs/midtable_analysis.md new file mode 100644 index 0000000..a0df25e --- /dev/null +++ b/applications/SprPoP/docs/midtable_analysis.md @@ -0,0 +1,275 @@ +# Слои отрисовки: как устроен оригинал и чего стоит порт + +Разбор 2026-08-13, по `../SDLPoP/src/seg008.c`. Повод — семь дефектов +падающих плит на уровне 13, из которых три оказались не багами кода, а +следствием того, что у нас нет слоя, в котором объекты и куски тайлов +сортируются между собой. Решение по этому документу ещё не принято. + +--- + +## 1. Как это работает в оригинале + +### 1.1 Три таблицы, а не «слои» + +`draw_tables` (seg008:1373) рисует ровно в таком порядке: + +``` +restore_peels(); +draw_wipes(0); +draw_table(0); // BACKTABLE +draw_table(3); // MIDTABLE +draw_wipes(1); +draw_table(1); // FORETABLE +``` + +Это грубое разделение на три уровня глубины. Куда попадёт кусок тайла, +решает переменная `ptr_add_table`, которую вызывающий переставляет перед +`draw_tile*`: по умолчанию `add_backtable`, в оверлее кромки — +`add_midtable` (`draw_other_overlay`, seg008:1499), а `add_foretable` +вызывается явно и точечно. + +### 1.2 Объекты живут НЕ в таблицах, а в objtable — и привязаны к ТАЙЛУ + +Персонажи, падающие куски, мечи, брызги попадают в `objtable`, и у каждой +записи есть поле `tilepos` — тайл, которому объект принадлежит. + +Ключевое: объекты рисуются **не отдельным проходом после фона**, а ВНУТРИ +обхода тайлов. В `redraw_needed_tile` (seg008:207) стоит: + +```c +if (tile_object_redraw[tilepos]) { + if (tile_object_redraw[tilepos] == 0xFF) + draw_objtable_items_at_tile(tilepos - 1); + draw_objtable_items_at_tile(tilepos); + tile_object_redraw[tilepos] = 0; +} +if (redraw_frames_fore[tilepos]) draw_tile_fore(); +``` + +То есть на каждом тайле: сначала его фоновые куски, потом объекты ЭТОГО +тайла, потом его передние куски. + +### 1.3 Порядок глубины складывается из ТРЁХ независимых механизмов + +| механизм | что даёт | +|---|---| +| порядок обхода тайлов: ряды **2, 1, 0**, колонки 0..9 (seg008:129) | тайл, обойдённый позже, рисуется поверх | +| сортировка объектов ВНУТРИ одного тайла (`sort_curr_objs`, seg008:1553) | кто из объектов одного тайла поверх кого | +| три таблицы back/mid/fore | грубая глубина для кусков тайлов | + +Сортировка внутри тайла (`compare_curr_objs`, seg008:1572) — пузырьком, и +правил в ней три: + +``` +объект типа 1 (ТЕНЬ) — всегда первым; +оба объекта — падающие плиты (0x80): y1 < y2 → по УБЫВАНИЮ y; +любая другая пара: y1 > y2 → по ВОЗРАСТАНИЮ y. +``` + +Обратный порядок для пары плит — не описка: две плиты из `loose_fall` летят +в 6 пикселях друг от друга, и верхняя обязана лечь поверх нижней. + +### 1.4 Что из этого следует + +**«Сортируемый midtable» — неточное имя.** Глобальной сортировки среднего +слоя в оригинале нет. Есть привязка объекта к тайлу и сортировка внутри +тайла; всё остальное решает порядок обхода. Это принципиально дешевле +общей сортировки: объектов на один тайл обычно 1-2. + +--- + +## 2. Что делаем мы + +Наш кадр — жёсткая последовательность проходов, без привязки объектов к +тайлам: + +``` +фон: pop_loose_tick (физика кусков) -> pop_process_trobs -> pop_redraw_needed + -> редрой шва +объекты: pop_loose_mob_draw (куски ПОД Kid, отсортированы по y между собой) + pop_char_draw(OPP/KID) (порядок задаёт guard_over_kid) + pop_loose_mob_draw_over (куски ПОВЕРХ Kid) +перед: pop_fore_over_char — ТОЛЬКО в окне вокруг персонажа +``` + +Отличия, из которых растут все три оставшихся дефекта: + +1. **Объект не знает своего тайла.** Глубина «кусок против Кида» считается + отдельной формулой (`pop_room.c`, поле `defer`), а «кусок против КУСКА + ТАЙЛА» не считается вовсе — куски тайлов рисуются раньше всех объектов. +2. **Передний слой считается только вокруг персонажа.** Это наша + оптимизация (memory `pop_fore_layer_cost`: полный проход стоил 78 % + кадра). Падающая плита в чужом углу комнаты передних частей тайлов + поверх себя не получает — отсюда «плита перед колонной». +3. **Оверлей кромки идёт после персонажей** и потому безусловно поверх + всех, тогда как у оригинала он в midtable и сортируется. + +--- + +## 3. Что затрагивает порт + +| участок | объём правки | +|---|---| +| `pop_room.c` — куски | привязать к тайлу, убрать `defer`, убрать собственную сортировку | +| `pop_cdraw.c` — персонажи | то же: объект вместо слота, привязка к тайлу | +| `pop_bg.c` — `overlay_mid_tile`, `fore_only_tile`, `ceil_over_kid_tile` | вызов из обхода тайлов, а не из отдельного прохода | +| `pop_redraw.c` — пометки | добавить «на этом тайле есть объект» (порт `tile_object_redraw`) | +| `sprpop.c` — главный цикл | вместо трёх проходов один: обход тайлов с объектами внутри | +| окно fore-клипа (`pop_t_fclip_*`) | смысл меняется: клип по тайлу, а не по персонажу | + +Плюс новая структура objtable и её сортировка — но маленькая, на тайл. + +--- + +## 4. Плюсы + +* **Уходят разом** MOB-CLIP-RIGHT, MID-OVERLAY-LAYER и «плита перед + колонной»: все три — следствие отсутствия привязки к тайлу, а не + самостоятельные баги. +* **Уходят подпорки.** Перерисовка соседнего тайла поверх куска, + `defer`, ручная сортировка кусков, отдельный проход `draw_over` — + всё это заменяется одним механизмом. +* **Совпадение с оригиналом по построению.** Дальше любой вопрос «что + поверх чего» решается чтением seg008, а не экспериментом в MAME. +* **Возможный выигрыш по кадру.** Сейчас fore-проход считает окно вокруг + персонажа и всё равно перебирает до девяти тайлов; при привязке к тайлу + передние части рисуются только там, где реально есть помеченный объект. + Но это НАДО ЗАМЕРИТЬ, а не обещать. + +--- + +## 5. Минусы и риски + +* **Риск регресса широкий.** Трогается порядок отрисовки ВСЕГО: Кид, + соперник, меч, брызги, зеркало, куски, оверлеи, полоса потолка. + Уровни 1-11 приняты и держатся на текущем порядке. +* **Наша оптимизация fore-окна может не пережить порт в прежнем виде.** + Она даёт 3.2x на самом дорогом проходе (memory `pop_fore_layer_cost`). + Если привязка к тайлу заставит рисовать передние части шире — можно + потерять больше, чем выиграть. +* **Дабл-буфер.** У оригинала один экран с dirty-rect, у нас две страницы + со своими копиями фона и heal. Пометка «на тайле есть объект» обязана + быть счётчиком страниц, как остальные наши пометки, — иначе объект + перерисуется на одной странице и не перерисуется на другой. +* **Банки.** Отрисовка размазана по трём банкам (`pop_bg` 2, `pop_cdraw` 4, + `pop_room` 7) плюс резидент. Единый обход тайлов с объектами внутри + означает, что цикл обхода зовёт код из всех трёх — надо проверить, что не + упрёмся в границы банков и трамплины. +* **Объём.** Это не правка, а этап: сопоставимо с тем, что делалось для + fore-слоя. + +--- + +## 6. Развилки + +**A. Полный порт** — objtable с привязкой к тайлу, сортировка внутри тайла, +объекты внутри обхода. Максимально близко к оригиналу, максимальный риск и +объём. + +**B. Частичный: только привязать КУСКИ к тайлам.** Персонажей оставить как +есть. Закрывает MOB-CLIP-RIGHT и «плиту перед колонной», не трогает +проверенный порядок персонажей. Дешевле и безопаснее; MID-OVERLAY-LAYER +остаётся. + +**C. Отложить** до этапа BG-ONCE и делать вместе — там всё равно +пересматриваются слои, и два пересмотра подряд дороже одного. + +Рекомендация: **B или C**. Вариант A целиком оправдан только если мы +одновременно берёмся за BG-ONCE — тогда это один пересмотр слоёв вместо +двух, и замер кадра делается один раз. + +--- + +## 7. ЗАМЕРЫ (сделаны 2026-08-13, уровень 13) + +Метод — маркеры-пустышки в РЕЗИДЕНТЕ вокруг измеряемого вызова плюс +брейкпоинты с `printf totalcycles` (memory `z80_profiling_method`). В +банковый код брейкпоинт ставить нельзя: 0xC000 — общее окно всех банков. + +| что | такты | доля логического кадра | +|---|---|---| +| логический кадр целиком | **1 289 526** | 100 % | +| `pop_redraw_needed` | **978** | 0,08 % | +| **fore-проход, ОДИН персонаж** | **128 778** | **10 %** | + +Плюс два счётчика за прогон ~229 логических кадров: + +* fore-проход вызван **10 раз** — на 96 % кадров он не выполняется вовсе + (пропуск неизменившегося персонажа, `pop_char_skip_mask`). Средняя цена + по кадру выходит ~0,4 %, но КАК ТОЛЬКО персонаж движется — платим все 10 % + каждый кадр, и при двух персонажах это ~20 %; +* **максимум объектов на одном тайле = 2** (комната 16, пара из + `loose_fall`: плита сбивает плиту, дальше летят обе). В комнате 23, где + гряда падает в пустоту, максимум 1. + +### 7.1 Что эти числа меняют в оценке + +**Сортировка внутри тайла — бесплатна.** Два объекта, пузырёк на два +элемента. Возражение против варианта A, которое закладывалось в §5, снято. + +**`pop_redraw_needed` можно не считать вовсе.** 978 тактов против 128 778 у +fore-прохода — соотношение 131 к 1. + +**Единственный настоящий риск порта — окно клипа fore-прохода.** Если +привязка объектов к тайлам заставит рисовать передние части шире нынешнего +окна вокруг персонажа, мы потеряем 10 % кадра, и потеряем их НА ДВИЖЕНИИ, +когда бюджет и так самый напряжённый. + +### 7.2 Насколько узко оригинал помечает передний слой — ВЫЯСНЕНО + +Пометки `redraw_frames_fore[]` ставит ровно одна функция — `set_redraw_fore` +(seg007:0550), и зовут её из трёх мест. Ни в одном нет «пометить всё». + +**Персонаж — `redraw_at_char` (seg003:0576).** Помечается ПРЯМОУГОЛЬНИК +футпринта: + +```c +for (tile_row = x_top_row; tile_row <= char_bottom_row; ++tile_row) + for (tile_col = x_col_left; tile_col <= x_col_right; ++tile_col) + set_redraw_fore(get_tilepos(tile_col, tile_row), 1); +``` + +с двумя уточнениями: при вынутом мече прямоугольник расширяется на колонку в +сторону клинка, а для КИДА берётся объединение с футпринтом ПРОШЛОГО кадра +(`prev_char_*`) — чтобы освободившиеся тайлы тоже вернули свои передние +части. + +**Падающий кусок — `draw_mob` (seg007:~1147).** Каждый кадр помечается +СОСЕД СПРАВА (`++tile_col`), и второй тайл, если кусок висит на границе +рядов: + +```c +++tile_col; +tilepos = get_tilepos(tile_col, tile_row); +set_redraw2(tilepos, 1); +set_redraw_fore(tilepos, 1); +top_row = y_to_row_mod4(ypos - 18); +if (top_row != tile_row) { ... то же для top_row ... } +add_mob_to_objtable(ypos); +``` + +**Анимация тайла — `draw_trob` (seg007:01E6):** один тайл. + +**Вывод: пометка переднего слоя в оригинале НЕ ШИРЕ нашего окна.** Она +пообъектная — футпринт персонажа и 1-2 тайла на кусок. Значит полный порт +objtable **не отнимает** нашу оптимизацию fore-окна, а формализует её: +вместо «окно вокруг персонажа» будет «тайлы, помеченные объектами», что +как минимум не шире, а для одиночного куска заметно уже. + +Риск, вокруг которого крутилась вся оценка, снят. + +### 7.3 Побочный результат: готовый рецепт для MOB-CLIP-RIGHT + +`draw_mob` даёт точный ответ на вопрос, как оригинал прячет правую часть +куска за соседним полом: он НЕ рисует сосед поверх куска (наша подпорка) и +НЕ полагается только на `clip.right`. Он помечает соседний тайл СРАЗУ +двумя пометками — `set_redraw2` (фон) и `set_redraw_fore` (передний слой). +Дальше порядок делает всё сам: фон соседа рисуется ДО куска, его передние +части — ПОСЛЕ. + +Это же закрывает и «плиту перед колонной»: передние части соседнего тайла +(колонна) ложатся поверх куска, потому что тайл помечен. + +**Рекомендация после разбора: вариант A (полный порт).** Оба возражения +против него сняты замерами и этим разбором — сортировка внутри тайла +бесплатна (максимум 2 объекта), окно переднего слоя не теряется. diff --git a/applications/SprPoP/docs/palette_plan.md b/applications/SprPoP/docs/palette_plan.md new file mode 100644 index 0000000..f25e914 --- /dev/null +++ b/applications/SprPoP/docs/palette_plan.md @@ -0,0 +1,297 @@ +# План: консолидация работы с палитрами + переход уровня через fade + +Статус: **этапы A и B реализованы; визуальная приёмка полного маршрута ещё +идёт** (2026-08-24). Палитры выделены в bank 10, а renderer cutscene/intro — +в bank 11, чтобы не переполнять bank 9 оболочки. +Обсуждение велось вокруг `SprPoP/` (банк 9 — оболочка, fade из `pop_ui.c`). + +--- + +## 1. Текущее состояние: карта палитры + +Палитра Sprinter — 256 записей по 4 байта (B, G, R, 0) = 1 КБ на страницу. +У страниц дабл-буфера ДВЕ раздельные палитры (`gfx_pal_load(0,…)` и +`gfx_pal_load(1,…)` — почти всегда парой). BIOS читает буферы только из +#4000–#BFFF: банковую rodata напрямую отдавать нельзя (копия в стек/W2), +см. грабли `pop_guard_set_palette` и `bg_load_tile_pal`. + +### 1.1 Игровая палитра `KID\kid.pal` — раскладка слотов + +Собирается `toolchain/pop_pack_kid.py build_palette()`, грузится одним +`gfx_pal_fload` (перезаписывает все 256 записей). Атласы запекались под эти +индексы — менять раскладку нельзя без перепаковки ассетов. + +| Слоты | Назначение | Источник | Динамика | +|---|---|---|---| +| 0x00 | Цвет фона + **вспышка молнии** (подмена записи 0, `flash_bg` ← do_flash/set_bg_attr SDLPoP) | — | меняется в игре | +| 0x01–0x2F | Не закреплены (нули) | — | свободно | +| 0x30–0x3F | VGA16 — базовые 16 цветов для mono-блитов: пламя факелов, пузырьки зелья (+12 красный «лечение», +10 зелёный, +9 синий), кровь чомпера (12), дворцовая кладка mono (+6) | `VGA16[]` | статично | +| ↳ 0x37–0x3F | Поддиапазон **UI**: текст/рамка меню; единственное, что `keep_ui` не затемняет (`MENU_BORDER`=0x37) | — | — | +| 0x40–0x4F | chtab_1 пламя/зелья (`POT_PAL_BASE`) | VDUNGEON res150.pal | статично | +| 0x50–0x5F | **ENV фон тайлсета** (`POP_PAL_ENV`) | res200.pal набора | **меняется при смене тайлсета** | +| 0x60–0x6F | **WALL тайлсета** (`POP_PAL_WALL`) | res360.pal набора | **меняется при смене тайлсета** | +| 0x70–0x7F | Kid (`PAL_BASE`) | KID res400.pal | статично | +| 0x80–0x8F | Меч chtab_0 (`SWORD_PAL_BASE`) | POT res700.pal | статично | +| 0x90–0x9F | Страж chtab_5 (`GUARD_PAL_BASE`) | res10.bin guard_palettes | **меняется по КОМНАТАМ** | +| 0xA0–0xAF | Тень (`POP_SHADOW_PAL_BASE`) | RGB-сетка pop_pack_shadow.py | статично | +| 0xB0–0xFF | Свободны (5 слотов) | — | — | + +Итого динамических зон три: запись 0 (молния), env+wall (тип здания), +стражи (per-room). Всё остальное одинаково всю игру. + +### 1.2 Полноэкранные палитры заставок + +Каждая перезаписывает ВСЕ 256 записей: + +| Файл | Где используется | +|---|---| +| `KID\kid.pal` (+ fallback `a:\kid.pal`) | BOOT и возврат в игру после заставок | +| `TITLE\title.pal` | экран TITLE | +| `PV\story.pal` | INTRO и HALL_OF_FAME (одна палитра на обе фазы) | + +### 1.3 Тайлсеты: подземелье ↔ дворец + +Оба набора используют ОДНИ И ТЕ ЖЕ слоты 0x50–0x5F/0x60–0x6F, заполняя их +разными цветами (атласы обоих наборов запекались под эти индексы). +Переключение = загрузка 64 байт (32 записи env+wall) в обе страницы +(`bg_load_tile_pal`); остальные 224 записи не трогаются. + +Какие уровни дворец — `tbl_level_type` (`pop_level_cold.c:44`): +**4, 5, 6, 10, 11, 14**; остальные подземелье. + +Палитра дворца `pal_tile.pal` (расшифровка, формат записи B,G,R): + +ENV 0x50–0x5F (пол, ковры, факелы, ворота, пики, арки): + +| Слот | RGB | | Слот | RGB | +|---|---|---|---|---| +| 50 | 0,0,0 чёрный | | 58 | 202,190,178 серо-бежевый | +| 51 | 121,89,60 коричневый | | 59 | 153,133,129 серо-лиловый | +| 52 | 161,121,76 светло-коричневый | | 5A | 76,64,56 тёмный серо-бурый | +| 53 | 194,149,89 песочный | | 5B | 153,97,89 кирпично-красный | +| 54 | 230,178,113 яркий песок | | 5C | 137,80,72 тёмный кирпич | +| 55 | 246,202,125 кремовый | | 5D | 48,125,125 бирюзовый | +| 56 | 255,234,170 бледно-кремовый | | 5E | 12,56,89 тёмно-синий | +| 57 | 255,255,255 белый | | 5F | 202,56,28 красно-оранжевый | + +WALL 0x60–0x6F (вся палитра песочная): 61=(218,170,89), 62=(226,165,93), +63=(226,170,97), 64=(218,161,85), 65=белый, 66=(226,165,93), 67=(218,165,89), +68=(226,170,89), 69=(218,170,97), 6A=(255,210,137), 6B=(255,218,149), +6C=(255,210,137), 6D=(255,218,145), 6E=(194,153,80 тёмный песок), +6F=(238,186,117). + +Чем рисуется во дворце: +- **Тело стены — НЕ спрайты**, а сплошные заливки; цвет разыгрывается на + комнату prandom'ом (`gen_palace_wall_colors`, `pop_bg.c:140`, порт + seg000:1942): подряды 1 и 3 берут случайный из 0x61–0x64, подряды 0 и 2 — + из 0x66–0x69; соседи по горизонтали не повторяются. +- Декор стен id 3–17 — mono-силуэт цветом VGA16+6 (0x36). +- Верх дверных проёмов дворца — спец-id 78–84 + полоса 145 («полоса под + окнами», pop_room.c:478). +- Остальное (пол, ковры, порталы-факелы, ворота, пики) — env-куски + pal_env*.atl с ENV-таблицей выше. + +### 1.4 Стражи (0x90–0x9F) + +Цвет задаётся на КОМНАТУ (`level.guards_color[room-1]`), при входе в +комнату зовётся `pop_guard_set_palette(color)` ДО отрисовки (слоты общие +на экран — смена посреди кадра дала бы стража в новой палитре с полосой HP +в старой). Только для обычных стражей (`tbl_guard_type == 0`): скелет и +Джафар имеют собственную палитру, зашитую в kid.pal; им зовётся с color=0 +(не трогать — иначе Джафар на ур.13 покрасился бы в цвет стража своей +комнаты). Внутри одного уровня слоты могут перезаписываться многократно. + +## 2. Текущее состояние: механика fade + +### 2.1 Наша реализация (`pop_ui.c`, банк 9) + +- `pop_ui_palette_snapshot()` — снимок всех 256 записей через + `gfx_pal_get` по 4 чанкам × 64; хранится в хвосте страницы шрифта + FONT.ATL ([0x3C00,0x4000)), map/unmap W0. Требует `font_ready`. +- `pop_ui_palette_dim(step, keep_ui)` — готовит ОБЕ экранные палитры из + снимка. Шкала без умножений (только сдвиги): + +| Шаг | Формула на канал | Яркость | +|---|---|---| +| 0 | x | оригинал | +| 1 | `(x>>1)+(x>>2)` | ≈3/4 | +| 2 | `x>>1` | 1/2 | +| 3 | `x>>2` | 1/4 | +| 4 | 0 | чёрный | + + `keep_ui` пропускает 0x37–0x3F (меню остаётся ярким). +- `pop_ui_fade_out/in(steps)` — проигрывание ступеней за `steps` кадров + vsync (`step = i*4/steps`, целочисленно): steps=4 — канонический (по кадру + на ступень), steps<4 — перескакивает ступени, steps>4 — повторяет (плавнее), + steps=0 у fade_in — мгновенный restore. +- Контракт map/unmap: обращения к EMM/W0 и BIOS-палитре строго после unmap. + +Стоимость одного dim ≈ 15–25 тыс. тактов (~4–7 мс при 3.5 МГц) — +укладывается в кадр vsync, на практике лагов нет. + +### 2.2 Как сделано в SDLPoP (seg009.c, USE_FADE/gmMcgaVga) + +- fade_out: каждый кадр КАЖДЫЙ ненулевой канал каждой записи −1; до нуля. +- fade_in: `fade_pos` от 0x40 вниз; канал +1, пока меньше оригинала. +- Уровней затемнения до 63–64 (VGA-канал 6 бит), полный фейд ~63 кадра × + wait_time=2 тика — медленно и кинематографично. +- `which_rows` — битовая маска групп по 16 записей: можно фейдить часть + палитры (в оригинале используется). +- По завершении принудительно восстанавливается оригинал; после out экран + заливается чёрным. + +Это осознанное расхождение (скорость/такты vs плавность) — ЗАПИСАТЬ в +`docs/impl_diff.md` (сейчас записи нет). + +## 3. Зафиксированные решения + +1. **Ступени затемнения: остаются 4.** Вариант 8 ступеней той же сдвиговой + техникой — рассмотреть отдельно, сейчас не внедрять. +2. **Предрасчёт fade-вариантов палитры отклонён.** Аргументы: чтение файла + с диска на порядок дороже вычисления; 3–7 КБ постоянной RAM при + MEMORY=small непозволительны; предрасчёт привязан к конкретным палитрам, + а снимок работает с любой текущей автоматически; keep_ui удвоил бы набор. +3. **Считать на лету**, хранить один снимок (уже есть, бесплатно в хвосте + страницы шрифта). +4. **Буферы на стеке**, не статика (W1/W2 мало) и не 1 КБ: обнулить 64/256 + байт дешевле, чем держать килобайт резидентно. +5. **Контракт `gfx_pal_load(pal, start, count, data)`**: count — число + СЛОТОВ, буфер обязан быть `count*4` байт; count=0 означает «все 256». +6. **Leaf-applеры остаются на месте** (`pop_bg_pal_apply` — банк 7 со своими + таблицами, `pop_shadow_pal_apply`, `pop_guard_set_palette`): банковая + rodata чужого банка не видна, перенос сломал бы доступ к данным. +7. **Молния (`flash_bg` в sprpop.c) не переносится** — игровой эффект + записи 0; после вспышки восстановление записи 0 из снимка ложится на API. +8. Модель состояния: разделены «какая палитра логически загружена» (load_*) + и «с какой яркостью показана» (apply/fade). Любой load_* обновляет снимок; + apply/fade показывает его с нужной глубиной. Это позволяет грузить новую + палитру «в темноте» (экран остаётся чёрным, пока не позвали apply/fade_in). + +## 4. Целевой API `pop_pal.c/.h` (банк 9) + +```c +/* сброс */ +void pop_pal_black(void) __banked; +/* все 256 записей ОБЕИХ страниц = 0. Стековый buf[256], обнуление циклом, + * 8 вызовов gfx_pal_load (4 чанка × 2 страницы, паттерн как в dim). + * Зовётся СРАЗУ ПОСЛЕ initgraph в pop_boot (раньше нельзя — нет гарантий + * состояния графического режима): закрывает кейс «мусор/палитра предыдущей + * программы при включении графики». СНИМОК НЕ ТРОГАЕТ (контракт: + * чёрный экран без изменения логической палитры). */ + +/* загрузка (пишет полную палитру в обе страницы + refresh снимка; + * видимую яркость НЕ трогают — экран меняется только по apply/fade) */ +void pop_pal_file_load(const char *name) __banked; +/* gfx_pal_fload + fallback "a:\" + gfx_pal_sync (fallback сегодня + * скопирован в каждом из ~6 мест вызова) */ + +void pop_pal_game_load(void) __banked; +/* file_load("KID\kid.pal") + pop_bg_pal_apply + pop_shadow_pal_apply. + * Сегодня тройка скопирована 3 раза (sprpop_cold ~958, pop_title ~88, + * pop_intro ~183). Единое место инварианта «kid.pal затирает слоты + * тайлсета 0x50..0x6F и тени 0xA0..0xAF». */ + +void pop_pal_level_load(uint8_t full) __banked; +/* палитра уровня: kid.pal/shadow + tileset 0x50..0x6F если набор сменился + * (сравнение через pop_level_type()). full=1 — ПРИНУДИТЕЛЬНО перечитать + * kid.pal/shadow (один экспорт с флагом, не две функции — меньше банковых + * точек входа). СТРАЖЕЙ (0x90..0x9F) НЕ включает: это компетенция входа + * в комнату (pop_guard_set_palette до первого draw). */ + +void pop_pal_story_load(void) __banked; /* PV\story.pal (INTRO и HOF — файл один, функция одна) */ +void pop_pal_title_load(void) __banked; /* TITLE\title.pal */ + +/* отображение */ +void pop_pal_snapshot(void) __banked; /* переезд из pop_ui, тело то же */ +void pop_pal_apply(uint8_t fade) __banked; /* = dim(fade, 0), 0..4 */ +void pop_pal_fade_in(uint8_t steps) __banked; /* переезд из pop_ui */ +void pop_pal_fade_out(uint8_t steps) __banked; + +/* меню продолжает звать низкоуровневый dim(step, keep_ui=1) — отдельный + * тонкий экспорт, чтобы не тащить флаг в горячий apply. Старые имена + * pop_ui_palette_* / pop_ui_fade_* УДАЛЯЮТСЯ (без алиасов — меньше + * экспорта банка). */ +``` + +Соответствие старое→новое: snapshot→snapshot, restore→apply(0), +fade_out/in→fade_out/in, тройка kid.pal×3→game_load, fload+fallback+sync×6→file_load. + +## 5. Этап A: рефакторинг — выполнен (2026-08-24) + +1. Создан `src/pop_pal.c/.h` в **bank 10**, добавлен в Makefile. + Он владеет политикой `load logical palette → snapshot → apply brightness`. + Низкоуровневые snapshot/dim/fade остаются физически в `pop_ui.c`: там + владелец страницы FONT.ATL, где лежит снимок; наружу они доступны только + через `pop_pal`. +2. Заменены call-sites: + - `sprpop_cold.c` ~958: black → game_load вместо тройки; + - `pop_title.c` title_restore_game_palette → game_load; загрузка title.pal → title_load; + - `pop_intro.c` intro_load/intro_restore → story_load/game_load; + - `pop_hof.c` (2 × story.pal) → story_load; + - `pop_menu.c`: fade/dim → новые имена (dim с keep_ui — низкоуровневый экспорт); + - `sprpop.c` demo-start (snapshot+dim(4,0)+fade_in(4)) → новый API. +3. Старые вызовы не остаются в коде приложения; внутренние функции `pop_ui` + сохранены как реализации одного владельца памяти снимка. +4. Сборка и host-тесты пройдены. `make size-check` неприменим: меняется + приложение, а не libc/libbgi. +5. MAME smoke-тест полного цикла смен палитр: boot → title (title.pal + + fade) → intro (story/kid) → demo fade-in → игра → HOF (story.pal). + Проверить: отсутствие мусора при включении графики (эффект black), + меню с keep_ui остаётся ярким при затемнении, молния (запись 0) + восстанавливается. + +## 6. Этап B: переход уровня через fade — реализован, ждёт визуальной приёмки + +Сценарий (обсуждён, детали уточнить по SDLPoP перед реализацией — как +оригинал делает смену уровня, есть ли там fade в DOS-версии): + +``` +fade_out // последний кадр уровня N темнеет +рисуем комнату 1 уровня N+1 // во ВТОРУЮ страницу, в темноте +pop_pal_level_load(full=0) // новая палитра: железо+снимок обновлены, + // экран всё ещё чёрный +флип + копия второй страницы обратно в первую +fade_in // = анимированный apply 3→2→1→0 +``` + +Экономия: реально переезжают только 32 записи (env/wall) при смене набора +dungeon↔palace; guards_color обновит вход в комнату. Kid/shadow не меняются +— потому full=0. + +Реализация находится в `sprpop.c` / `sprpop_cold.c`: последний кадр +уровня N темнеет, `pop_level_switch()` подготавливает первый кадр N+1 и +обновляет логический источник через `pop_pal_level_load(1)`, затем главный +цикл показывает кадр только через fade-in. Восемь ступеней и отдельная +анимация смерти не входят в этот этап. + +**Этап B закрывает два открытых бага** (разборы — `BUGS_OPEN.md`): +- [PAL-L1-AFTER-INTRO] — вход в игру на уровень 1 после интро с чёрным + экраном (маршрут demo_new_game; корень не установлен, воспроизведение + нестабильно); +- [PAL-DUNGEON-STALE] — переход 3→4 оставляет подземную палитру (корень + ясен: fade_in восстанавливает из снимка, снятого ДО загрузки тайлсета + дворца; быстрый фикс `fade_in_pending` 2026-08-23 сам же и проявляет этот + дефект модели). + +Быстрый фикс 2026-08-23 (маршрут CUTSCENE → LEVEL_LOAD → PLAYING, +`fade_in_pending` + `pop_ui_fade_in(4)` после `pop_level_switch`) закрыл +чёрный экран на переходах с pre-cutscene внутри подземелья (1→2), но модель +«кто и когда меняет яркость» остаётся разношёрстной — её и приводит в +порядок этап B. + +## 7. Этап C: документирование + +- Запись в `docs/impl_diff.md`: наши 4 ступени vs SDLPoP ~64 (что делает + оригинал, что делаем мы — сдвиговая шкала ради тактов, чем платим — + грубее градации, что проверять при регрессе). +- После этапа B — дополнить запись про сам переход. + +## 8. Не трогаем + +- Молнию (`flash_bg`, sprpop.c) — включая обход SDCC-бага + `gfx_pal_set(0,0,0,0,0)` → ручные `gfx_pal_set(0/1, 0, r,g,b)`; +- leaf-applеры: `pop_bg_pal_apply` (банк 7), `pop_shadow_pal_apply`, + `pop_guard_set_palette` (данные своих модулей); +- хранилище снимка в хвосте страницы шрифта FONT.ATL (бесплатное место, + guard `font_ready`); +- раскладку слотов 0x00–0xAF (зафиксирована атласами). diff --git a/applications/SprPoP/docs/perf_backlog.md b/applications/SprPoP/docs/perf_backlog.md new file mode 100644 index 0000000..d522c7d --- /dev/null +++ b/applications/SprPoP/docs/perf_backlog.md @@ -0,0 +1,335 @@ +# Оптимизация отрисовки — что НЕ сделано (замеры на 2026-08-10) + +Список отложенных идей с измеренной ценой. Всё измерено брейкпоинтами в +MAME (`z80_profiling_method`) на роомтесте, уровень 1 комната 1. + +Прежде чем брать что-то отсюда — перечитать «Как мерить» ниже: половина +прошлых гипотез не подтвердилась, и подтвердились не те, что казались +очевидными. + +## Как мерить (иначе цифры не сходятся) + +- **Такт `totalcycles` ≠ номинальный T-такт Z80.** У ОЗУ Sprinter + wait-state'ы, замеренная стоимость ≈ **2,4× справочной** (`get_tile`: 574 + против 1 422). Считать по таблице тактов нельзя. Подробности — + memory `sprinter_wait_states_2x`. +- **Растровый кадр = 430 000 тактов.** Главный цикл спейсится тремя + `gfx_wait_vsync`, поэтому работа сверх 430 000 стоит СРАЗУ целый лишний + кадр. Граница дискретная: 3 растровых кадра на логический или 4. +- **Адреса символов меняются после КАЖДОЙ пересборки** (`sprpop.map`, + `bank*_*.sym`). Маркер со старым адресом молча не срабатывает, и разбивка + выглядит правдоподобно, но врёт. +- **Сцена между сессиями не воспроизводится точно**: позиция Кида до + пикселя, состояние плиты (2,6), фаза факелов. Сравнивать «до/после» можно + только по ОДНОЙ функции с одинаковыми входами, а не по общей работе за + кадр. +- Кто делит: брейкпоинт на `__divsint`/`__divuint`/`__divuchar` с печатью + адреса возврата — `bpset ,1,{printf "ret=%04X\n",w@(sp); g}`. Если + адрес возврата в банке (>= 0xC000), поставить тот же брейкпоинт с условием + `w@(sp)==<адрес>` и БЕЗ `g`: машина встанет с нужным банком в окне, и + `dasm` покажет вызывающего. +- **Брейкпоинт по адресу в банке ловит ВСЕ банки.** 0xC000..0xFFFF — общее + окно, и один и тот же адрес есть у семи модулей сразу. Либо ловить через + трамплин (`hl==<адрес>&&(de&0xff)==<банк>`), либо перепроверять, что + срабатывания идут из нужной фазы: иначе в интервал попадает чужой код и + цифры врут (так я намерил несуществующие 134 730 тактов в прологе + `pop_char_fore`). +- Трасса вызовов графики с параметрами: скрипт в истории сессии, ставит + маркеры фаз на трамплин `___sdcc_bcall_ehl` (условие `hl==<адрес>&&(de&0xff)==<банк>`) + и брейкпоинты на листья libbgi с печатью аргументов + (`__sdcccall(1)`: arg1 = HL, arg2 = DE, дальше стек с sp+2). + +## Профиль на 2026-08-10 + +Сцена: комната 1, два факела, стража нет. + +| сцена | работа за кадр | период | +|---|---|---| +| Кид в покое, факел не задет (пропуск работает) | 244 026 (57 %) | 3 кадра | +| Кид стоит на факеле (перерисовывается каждый кадр) | 366 240 (85 %) | 3 кадра | +| Кид в щебне (2,4), движется | ~357 000 (83 %) | 3 кадра | +| Кид (0,5) в движении | 421 254 (98 %) | **4 кадра** | + +Одна перерисовка персонажа = **~137 000 тактов = 32 % растрового кадра**, +из них полезной работы (heal 20×19 + спрайт 12×41) — меньше трети. + +## Профиль дворца (уровень 4) на 2026-08-10 + +Сцена: уровень 4, Кид НЕПОДВИЖНО стоит на (1,7) (комната с решёткой), +стража нет. Разбивка одного логического кадра брейкпоинтами на границах +фаз (адреса `PROF()` из `sprpop.lst` + базы `_CODE = 0x42AD`): + +| фаза | тактов | доля работы | +|---|---:|---:| +| ввод + читы + heal | 59 340 | 11 % | +| логика (kid_tick, phys_tick, страж, боёвка) | 85 356 | 16 % | +| `pop_loose_tick` | 27 396 | 5 % | +| `pop_process_trobs` | 106 107 | 21 % | +| `pop_redraw_needed` | 447 | — | +| шов / смена уровня / вспышка | 5 448 | 1 % | +| `guard_over_kid` + `pop_char_skip_mask` | 4 788 | 1 % | +| `pop_char_draw(KID)` | 52 206 | 10 % | +| соперник + `loose_mob_draw_over` + `hp_draw` | 4 086 | 1 % | +| **`pop_char_fore(KID)`** | **171 693** | **33 %** | +| `pop_room_clip_borders` + прочее | 742 | — | +| **ИТОГО работа** | **517 609** | 120 % растрового кадра → период **4 кадра** | + +По цветам бордюра: синий (`PROF(2)`) 144 696 (28 %), зелёный (`PROF(4)`) +139 398 (27 %), циан (`PROF(6)`) 233 515 (45 % работы = 54 % растрового +кадра, начинается на 74 % первого кадра и кончается на 123 %). + +## СДЕЛАНО 2026-08-10: метка «фон трогали» стала маской ТАЙЛОВ + +Было: один union-прямоугольник на страницу. Три факела трогают по пятну +16x18 в колонках 1, 6 и 8, а их объединение — полоса `x 40..280` на всю +комнату; Кид, стоящий где угодно между крайними факелами, в неё попадал и +перерисовывался каждый кадр со всем fore-проходом. + +Стало: `uint16_t pop_cd_dmask[2][3]` — бит на колонку, слово на ряд, набор на +страницу. Колонка берётся сдвигом (`x >> 5`), ряд — цепочкой сравнений; +проверка в `cd_quiet` — три `AND` через резидентный `pop_cd_hit`. + +Гранулярность тайла — это гранулярность ОРИГИНАЛА: пометки там тоже по +тайлам (`redraw_frames_anim[tilepos]`, `set_wipe`), персонажи привязаны к +тайлу через `tile_object_redraw[tilepos]`, а единственное подтайловое +уточнение (`wipe_heights`) — по высоте, не по ширине. Полутайл (16 px) не дал +бы ничего: пламя рисуется с отступом 8 px и шириной 16, то есть занимает +середину тайла и задевает обе половины. + +Замер на той же сцене, где снимался профиль ниже (Кид неподвижно на (1,7)): + +| | было | стало | +|---|---:|---:| +| циан (спрайты + fore) | 233 515 | **24 781** | +| работа за кадр | 517 609 | **306 553** | +| период | 4 растровых кадра | **3** | + +--- + +## СДЕЛАНО 2026-08-10: деление в луче видимости стража (Кид у шва) + +`tile_at_kid` (`guards.c`) считала колонку честным `/` и `%`, тогда как везде +уже стоит резидентная таблица `POP_TILE_DIV` (это и есть `tile_div_tbl` +оригинала). У SDCC z80 это `__divsint` плюс `__modsint`, а тот внутри снова +зовёт `__divsint` — ~5 400 тактов на вызов. + +Зовут её В ЦИКЛЕ по колонкам между стражем и Кидом +(`check_can_guard_see_kid`, seg003:761). Когда Кид стоит У ШВА, его +`curr_col = −1`, луч тянется через всю комнату, и за кадр набегало ВОСЕМЬ пар +делений — около 43 000 тактов, 10 % растрового кадра, в фазе ЛОГИКИ. + +Замер: брейкпоинт на `__divsint` с печатью адреса возврата дал `ret=C033` +восемь раз за кадр; остановка на нём и дизассемблирование с правильным банком +показали `HL−65`, `ld de,#14`, `call __divsint` по смещению 0x24 банка 1 — +`tile_at_kid`. После фикса пар `C033` не остаётся ни одной. + +**Заодно снята ложная тревога.** В прошлом замере я записал, что на шве +пролог `pop_char_fore` разбухает до 134 730 тактов. Это была ошибка зонда: +брейкпоинт на `fore_tile` стоял по адресу, который совпадает с кодом ДРУГИХ +банков, и в интервал попадали чужие срабатывания. Чистый замер (Кид в +колонке 0, спрайт свисает за левый край, окно `x −8..5`): пролог **16 950** — +ровно как в середине комнаты, весь fore-проход 41 803, `pop_room_clip_borders` +14 364. Урок в «Как мерить» выше: адрес в банке нужно либо проверять на +уникальность, либо ловить через трамплин с условием на банк. + +--- + +## 0. Дворцовая кладка в fore-проходе — **СДЕЛАНО 2026-08-10** + +Все три шага выполнены; замер после — в конце пункта. Ниже сохранён исходный +разбор: он объясняет, почему предфильтра `fore_tile` мало и откуда взялись +габариты кусков. + +**Было: 139 863 такта за кадр (32 % растрового кадра), полезных пикселей — +ровно ноль.** + +Замер (уровень 4, Кид на (1,7)). Окно fore-клипа в этот момент — +`x 229..241, y 106..147` (прочитано из `pop_t_fclip_*` брейкпоинтом). +`pop_fore_over_char` обходит 6 тайлов: + +| тайл | тактов | +|---|---:| +| (0,6) (0,7) (1,6) (1,7) | 2 800 – 4 600 каждый | +| **(2,6) — стена** | **74 310** | +| **(2,7) — стена** | **65 553** | + +Ряд 2 этой комнаты — стена, и он лежит ПОД ногами Кида, то есть попадает в +его fore-окно всегда. Внутри одного тайла стены `wall_pattern_palace` +делает 6 × `wpp_fill` (≈ 3 100 каждый) + 5 × `pop_wall_b` (≈ 9 760 каждый) +≈ 60 000 тактов. + +Ни один кусок в окно не попадает: +- верхняя заливка стоит на `dmy - 59 = 157`, окно кончается на `y = 147`; + все остальные куски ещё ниже; +- тайл (2,6) занимает `x 192..223`, окно начинается с `x = 229` — он + промахивается и по горизонтали тоже. + +Тайл всё равно проходит, потому что предфильтр в `fore_tile` (pop_bg.c, +`x0 < fclip_x1 && x0 + 40 > fclip_x0 && y0 - 8 < fclip_y1 && y0 + 70 > +fclip_y0`) намеренно грубый — габарит 40×78 на тайл. Дальше `wpp_fill` +честно режет по окну и выходит с пустым прямоугольником, но 3 100 тактов на +арифметику клипа уже потрачены, а `pop_wall_b` о существовании окна не знает +вовсе: он идёт в `atlas_image` + `gfx_w0_map`, читает `w`/`h` и только там +обнаруживает, что рисовать нечего. + +Что делать (по возрастанию объёма): +1. **Ранний выход из `wall_pattern_palace`**: самый верхний пиксель узора — + `dmy - 59`, самый нижний — `dby + высота нижнего декаля`. Один + `if (pop_t_fclip_on && (fclip_y1 <= dmy - 59 || fclip_y0 > dby + h)) + return;` плюс такая же проверка по `x` убивает оба тайла целиком почти + даром. +2. Прогнать `pop_wall_b` в этом узоре через ту же проверку окна, что уже + есть у `wpp_fill` (нужны размеры кусков — см. п. 2 ниже, «размеры из + каталога атласа»). +3. Сузить сам предфильтр `fore_tile` до реального габарита узора вместо + 40×78 — тогда лечится не только дворец. + +Порядок в подземелье тот же, но дешевле: `wall_pattern` в подземелье делает +до 3 блитов и ни одной заливки (~29 000 на тайл против ~60 000). Это же +объясняет, почему после перехода на дворцовый тайлсет период вырос. + +Сверено с SDLPoP (`seg008.c:1943 wall_pattern`, ветка +`!is_dungeon && GRAPHICS_VGA`): состав узора у нас дословный — 5 +`add_wipetable` + 4 декаля + нижняя заливка + нижний декаль. Расходимся не +составом, а тем, что оригинал складывает всё в `foretable` и рисует одним +`draw_table()`, у которого «посетить тайл» стоит копейки (см. п. 6). + +### Что сделано и сколько дало + +1. **Ранний выход** из `wall_pattern_palace` по окну fore-клипа (узор целиком + в `x [xh*8, xh*8+32)`, `y [dmy−59, dby]`). +2. **Отсев каждого декаля** (`wp_blit`) по реальному габариту вместо + заведомо большего 64×64 в `pop_blit_b`. Размеры сняты из каталогов + атласов: `pal_wall.atl` — группы 3..5 = 8×7, 6..8 и 9..11 = 32×12, + 12..14 = 30×5, 15..17 = 32×3. +3. **То же для ПОДЗЕМЕЛЬЯ**: ранний выход `wall_pattern` (габарит там выше — + левая марка уходит на `dby+POP_YOFF−67`) плюс `wp_blit` на RNDBLOCK + (32×21), обоих разделителях (9×21) и обеих марках (`pop_wall.atl`: + 16/17 = 7×10, 14/15 = 14×5). + +Замер: уровень 4, комната 18, Кид сдвигается читом `]` по пикселю (skip +выключен, идёт полный путь), в fore-окне ШЕСТЬ тайлов, ТРИ из них — +дворцовая стена. + +| участок | тактов | +|---|---:| +| пролог `pop_char_fore` до первого `fore_tile` | 17 208 | +| обычный тайл | 4 000 – 4 600 | +| **тайл стены (было 60 000 – 74 000)** | **~12 700** | +| хвост + чистка бортов | 6 055 | +| **fore-проход целиком (было 171 693)** | **70 681** | + +Кадр целиком в этой сцене: работа **419 839**, период **3** растровых кадра +(было 517 609 и 4). + +Остаток в проходе — пролог 17 208, это уже пункт 1 ниже (футпринт из физики). +Отдельная находка: когда Кид стоит НА ШВЕ (окно `x −8..5`), пролог разбухает +до **134 730** — 86 % прохода; причина не разобрана, см. пункт 1. + +--- + +## 1. Футпринт персонажа — брать из физики, а не считать заново + +**Цена: 11 574 такта на каждый fore-проход** (от входа в `char_footprint` до +первого `fore_tile`). + +`redraw_at_char` (seg003:0430) берёт ГОТОВЫЕ `char_col_left/right`, +`char_top_row`, `char_bottom_row` — их в этом же кадре посчитала физика +(`set_char_collision`, seg006:0723). У нас `char_footprint` (pop_bg.c) +считает их заново внутри fore-прохода. + +Мешает то, что физика (банк 3) держит их в статиках, а слой фона — банк 2. +Надо опубликовать их так же, как уже опубликованы `pop_cd[who].fpx/fpy/fpw/fph`. + +**Заодно:** оригинал расширяет футпринт ТОЛЬКО на одну колонку при вынутом +мече и объединяет с футпринтом ПРОШЛОГО кадра (`prev_char_col_left/right`). +Мы вместо этого расширяем окном fore-клипа и посещаем 6 тайлов там, где +оригинал посетил бы 4. Разница видна в замере: fore-проход стоит 58 764 +там, где реально рисует, и **112 758 там, где не рисует ничего** — вся +разница в числе посещённых тайлов. + +Осторожно: окно клипа заводилось под клинок и брызги (они уходят +вперёд-вверх за габарит кадра). Менять — с прогоном боя и падений. + +## 2. Размеры ленты — из каталога атласа, а не через окно 0 + +**Цена: ~750 тактов на `atlas_image` + часть из 6 396 на «чтение w/h и +арифметика клипа», на КАЖДЫЙ блит фона.** + +Сейчас `pop_blit_b`, чтобы узнать размер куска, зовёт `atlas_image` (тот +мапит страницу в W3, читает запись каталога, возвращает W3 назад), потом +`gfx_w0_map` и читает `w`/`h` из шапки ленты. + +А размеры **уже лежат в каталоге**: запись 8 байт — `offset u16, fw u8, +fh u8, nx u8, ny u8, резерв u16`, и у всех фоновых лент `nx = ny = 1`, то +есть `fw`/`fh` в точности равны `w`/`h` из шапки (проверено по +`pop_env0.atl`). `atlas_image` их читает и выбрасывает. + +Вариант A (0 байт памяти): `atlas_image_wh()` рядом с `atlas_image` — +вернуть заодно размер. +Вариант B (без маппинга вовсе): снять каталоги при загрузке в резидентную +таблицу. Объём: фон (env0-4 + wall + fore + pot) = **313 лент**, по 2 байта += **626 Б**; всё вместе с Кидом и стражем = 600 лент = 1200 Б. Свободной +кучи на 2026-08-10 — 2873 Б. + +Ожидаемый выигрыш скромный: ~2 000–3 000 из ~16 000 накладных на блит. + +## 3. Один `gfx_w0_map`/`unmap` на группу блитов + +**Цена: ~5 500 тактов на блит** (unmap плюс возвраты по цепочке +`pop_pot_b` → `pop_blit_b` → трамплин). + +Куски одного прохода часто лежат на одной странице атласа, а мапим и +размапливаем на каждый. Мешает то, что `pop_blit_b` — общий лист для всех +вызывающих; нужна форма «открыть страницу, N блитов, закрыть». + +## 4. Единый проход по тайлам вместо трёх + +У оригинала за кадр ОДИН обход тайлов — `redraw_needed_tiles` (seg008): +контекст тайла (`curr_tile`, `curr_modifier`, `draw_xh`, `draw_main_y`) +ставится по разу на тайл в `load_curr_and_left_tile`, а `redraw_needed` +смотрит **семь** независимых счётчиков (`wipe_frames`, `redraw_frames_full`, +`redraw_frames_anim`, `redraw_frames2`, `redraw_frames_floor_overlay`, +`redraw_frames_fore`, `tile_object_redraw`) и делает только помеченное. + +У нас **три** обхода: `pop_redraw_needed`, `pop_process_trobs` и +`pop_fore_over_char`. Плюс один `kind` на тайл вместо семи счётчиков — две +разные причины перерисовки одного тайла конфликтуют. + +Это большой рефакторинг всего слоя фона; браться только если понадобится +ещё заметный запас. + +## 5. objtable: персонажи, привязанные к тайлу + +Оригинал кладёт персонажей в `objtable` и рисует их в +`draw_objtable_items_at_tile(tilepos)` во время обхода тайлов — порядок +окклюзии получается сам. У нас отдельный fore-проход НА КАЖДОГО персонажа. +Со вторым персонажем (страж) цена удваивается. + +## 6. Отложенные таблицы back/mid/fore + +`add_backtable`/`add_midtable`/`add_foretable` только КЛАДУТ запись в массив, +рисование — один `draw_table()` в конце. Поэтому «посетить тайл» у +оригинала стоит копейки. У нас блит идёт сразу из обхода. + +## 7. Мелочи с известной ценой + +| что | цена | где | +|---|---|---| +| `pop_clip_char_top` — трамплин банк 4 → банк 3 ради одной проверки тайла над головой | 8 892 | `pop_cdraw.c` / `pop_map.c` | +| `pop_loose_tick` при полном отсутствии падающих плит в комнате | 27 438 | `pop_map.c` | +| `obj_x * 8 / 7` — единственное оставшееся `__divsint` в горячем пути | ~2 400 | `pop_char_draw` | +| `cd_sig_make` + возврат из `pop_char_draw` | 7 944 | `pop_cdraw.c` | +| `pop_loadkid` + расчёт координат кадра | 7 410 | `pop_cdraw.c` | + +## Что уже проверено и НЕ сработало + +- **Маска «у тайла есть передний слой» (`FORE_ANY`) + контекст тайла один + раз.** Сделано (коммит `a9f4521`), эффект **нулевой**: в футпринте Кида + тайлы почти всегда С передним слоем, а снятое второе чтение кода съедено + проверкой маски. Оставлено как сближение с оригиналом. +- **«Быстрый путь для окна коллизии целиком внутри комнаты».** Не + срабатывал почти никогда: Кид в колонке 0 даёт окно с −1. Заменён на + разбиение окна на непрерывные пробеги. +- **Флаг «фон трогали» вместо позиционной метки** — нулевой выигрыш, + факелы гасили пропуск для всех сразу (см. `pop_cdraw.h`). diff --git a/applications/SprPoP/docs/perf_cyan_phase.md b/applications/SprPoP/docs/perf_cyan_phase.md new file mode 100644 index 0000000..09188aa --- /dev/null +++ b/applications/SprPoP/docs/perf_cyan_phase.md @@ -0,0 +1,172 @@ +# ЦИАН фаза (персонажи + передний слой) — анализ и оптимизация + +Рабочий документ: живёт между сессиями. Внизу **журнал правок** — каждая +запись с замером до/после. Сцена, рецепт воспроизведения и зонды — +[`perf_l13_room23.md`](perf_l13_room23.md); там же ответ про 8-битные +габариты. Парная фаза — [`perf_green_phase.md`](perf_green_phase.md). + +Границы фазы в `sprpop.c`: от `PROF(6)` (строка 620) до `PROF(0)` +(строка 677). Содержимое: `pop_check_mirror`, `pop_loose_mob_draw`, +соперник, `pop_char_draw(KID)`, `pop_loose_mob_draw_over`, `pop_fore_needed`, +`pop_hp_draw`, `pop_char_fore(KID)`, `pop_cd_clear`, +`pop_room_clip_borders`. + +**Бюджет: ≤ 400 000 тактов (растровый кадр = 430 000).** + +| состояние | было (2026-08-17, `c312e4a`) | стало (`af189a1`) | +|---|---:|---:| +| покой, Кид пропущен | 20 550 | 20 550 | +| Кид перерисовывается, кусков нет | 193 000 | 192 000 | +| **пик каскада (6 кусков + Кид)** | **631 800** | **270 510 ✔** | + +**ЦЕЛЬ ФАЗЫ ВЫПОЛНЕНА** (270 510 при бюджете 400 000, запас 32 %). + +--- + +## 1. Что решило дело + +### C1. Пометки «фон трогали» в `mob_render` подавлены — −55 000 + +`pop_loose_mob_tick` помечает **весь коридор** куска одним вызовом, а три +блита внутри `mob_render` метили подмножества того же прямоугольника по +**4 502 такта** каждый. Механизм — `pop_cd_mute()`/`pop_cd_unmute()` +(в `pop_tile.c`; отдельное значение того же флага `pop_cd_batch`, чтобы у +`pop_cd_touch` на общем пути осталась ОДНА проверка). + +Добавлена пометка в `mob_spawn_copy`: кусок, рождённый ВНУТРИ тика +(`loose_fall` сбил плиту), получает слот с начала таблицы, то есть уже +пройденный циклом, — своей пометки в этом кадре он бы не получил, а нарисован +был бы. Без этого пропущенная пометка = стёртый и не перерисованный +персонаж. + +### C4. Кусок клипуется САМ, вместо чистки бортов после — −138 000 + +Самая крупная и самая неожиданная статья. В `mob_render` стоял +`pop_clip_sprite`, то есть кусок рисовался в борт целиком и взводил +`border_dirty`; `pop_room_clip_borders` потом стирал ДВЕ полосы во всю ширину +экрана (320×28 и 320×28) — **150 978 тактов в КАЖДОМ кадре**, пока хоть один +кусок торчит выше поля. А гряда 13-го уровня рождается ровно у потолка +(`y = 2`), то есть почти весь каскад. Стало 1 722. + +Теперь окно клипа (`pop_t_win_set(0, POP_YOFF, 320, POP_PLAYFIELD_H)`) +ставится ТОЛЬКО когда кусок реально задевает борт: внутри поля блиты идут +быстрым путём. + +### Композит куска: три блита → один — −163 000 + +Части `env 70 / 74 / 72` складываются в ОДИН getimage-блоб при загрузке +тайлсета (`mob_spr_build` в `pop_room.c`). Мотив прямо из +[[blit_cost_model]]: у блита ~8 800 такта постоянных накладных против ~5 000 +на пиксели, а шесть кусков в воздухе давали 18 вызовов = **258 708 такта**, +больше половины фазы. + +Тонкости, которые пришлось соблюсти: + +- части **перекрываются** (74 и 70 обе идут от `mob_x`), поэтому композит + собирается попиксельно с пропуском `0xFF` — ровно как три прозрачных блита + друг поверх друга; +- габариты частей **читаются**, а не берутся константами: у тайлсетов правая + часть разная (26 px в подземелье, 25 во дворце); +- блоб лежит в обычной памяти (W2), поэтому блит идёт мимо `atlas_image` и + `gfx_w0_map/unmap` — ещё ~1 350 такта на вызов. Новый резидентный лист + `pop_mem_b` (`pop_tile.c`); +- страйд блоба = его ширина; сначала считается точный габарит, потом + копирование. Промежуточная версия объявляла блоб шириной 63 при + фактических 58 и переносила пять прозрачных колонок на каждом кадре; +- собирается на КАЖДУЮ смену тайлсета; резервный путь на три блита остался + (`mob_spr_ok`). + +### Общие правки, попавшие и в эту фазу + +- `blit_b_clip`: байтовый габарит + file-scope вместо локалей (кадр 22 → 12 Б, + обращений `(ix)` 211 → 51). Подробности — в + [`perf_green_phase.md`](perf_green_phase.md) §G3. +- `pop_blit_b`: аргументы в file-scope (76 → 11 обращений `(ix)`). + +--- + +## 2. Раскладка на 2026-08-17 (до правок) — для истории + +Подфазы (три готовых `PROF(6)`: 0x4BCE / 0x4C26 / 0x4C93), пик: + +| участок | покой | пик | +|---|---:|---:| +| `pop_check_mirror` + `pop_loose_mob_draw` + соперник | 23 250 | 154 512 | +| `pop_char_draw(KID)` + `pop_loose_mob_draw_over` + `pop_fore_needed` + HP | 3 726 | 445 284 | +| `pop_char_fore(KID)` + `pop_cd_clear` + **чистка бортов** | 1 722 | 150 978 | + +Разбор одного `pop_blit_b` (157 замеров быстрого пути, зонды b1..b5): + +| участок | такты | доля | +|---|---:|---:| +| `atlas_image` + `gfx_w0_map` + чтение габарита | 1 086 | 7 % | +| ядро блита (libbgi, `gfx_blit_noclip`) | 10 422 | 64 % | +| `pop_cd_touch` — пометка «фон тронут» | 4 502 | 28 % | +| `gfx_w0_unmap` + возврат | 264 | 2 % | +| ИТОГО | 16 273 | | + +Клипованный путь тогда же: ядро `blit_b_clip` 14 088, итого 16 409. +После правок: клипованный блит 11 848, быстрый ~13 900 (у него больше +пикселей). + +--- + +## 3. Что осталось в запасе (если понадобится ещё) + +Фаза в бюджете, поэтому это задел, а не план. + +### C5. Один `gfx_w0_map`/`unmap` на группу блитов + +1 086 + 264 на вызов. Для композита куска уже не нужно (он в обычной +памяти), но остаётся для тайлов фона: куски одного тайла часто лежат в одной +странице атласа. Мешает то, что `pop_blit_b` — общий лист для всех +вызывающих; нужна форма «открыть страницу, N блитов, закрыть». + +### C6. Размеры ленты — из каталога атласа + +`fw`/`fh` уже лежат в записи каталога (8 байт: `offset u16, fw u8, fh u8, +nx u8, ny u8, резерв u16`), и у всех фоновых лент `nx = ny = 1`, то есть они +равны `w`/`h` из шапки. `atlas_image` их читает и выбрасывает. + +### C7. objtable вместо отдельного fore-прохода на персонажа + +Позиции 5 и 6 старого `perf_backlog.md` — большой рефакторинг. Оригинал +кладёт персонажей и куски в `objtable` и рисует их при обходе тайлов +(`draw_objtable_items_at_tile`), порядок окклюзии получается сам; у нас +отдельный fore-проход НА КАЖДОГО персонажа. + +### Снять временную оснастку + +Шесть вызовов `pop_dbg_b1..b6` внутри `pop_blit_b` — ~400 такта на блит +(`call` + `ret` × 6), плюс `pop_dbg_kind`/`m16` в `pop_redraw_needed`. +Снимать ПОСЛЕ того, как оптимизация закончена: без них не мерить. + +--- + +## 4. Что НЕ делать + +- **Не ставить W3-скобку из кода с `--w3`** — белый экран + (memory `gfx_blit_noclip_fast`). +- **Не ускорять передачу пикселей** — предел железа + (memory `blit_cost_model`). +- **Не возвращать клип куска по `clip.right = 40`** оригинала + (`add_mob_to_objtable`, seg007:1161): единицы этого поля не выяснены, + буквальные 40 экранных пикселей срезают правый задний угол плиты + (прогон 2026-08-13). +- **Не сужать коридор heal «по палаццовому следу»** — габариты частей у + тайлсетов разные; брать высоту СОБРАННОГО композита (так и сделано). + +--- + +## 5. Журнал правок + +| дата | что сделано | циан: покой / Кид / пик | коммит | +|---|---|---|---| +| 2026-08-17 | базовый замер | 20 550 / 193 000 / **631 800** | `c312e4a` | +| 2026-08-17 | C1 подавление пометок в `mob_render` + пометка в `mob_spawn_copy` | — / — / **577 050** | `a3c473d` | +| 2026-08-17 | C4 кусок клипуется сам вместо чистки бортов; `blit_b_clip` байты+file-scope | — / — / **438 546** | `a3c473d` | +| 2026-08-17 | `pop_blit_b` аргументы в file-scope | — / — / **435 180** | `b2da0b8` | +| 2026-08-17 | **композит куска: один блит вместо трёх** | — / — / **273 078** | `b2da0b8` | +| 2026-08-17 | точный габарит композита (было 63 при 58) | 20 550 / 192 000 / **270 510 ✔** | `af189a1` | +| 2026-08-17 | замер после фиксов уровня 1 | — / — / **379 482** | `40f0d46` | +| 2026-08-17 | **регресс после фиксов уровня 2** — без изменений | — / — / **379 488 ✔** | `ec1f384` | diff --git a/applications/SprPoP/docs/perf_green_phase.md b/applications/SprPoP/docs/perf_green_phase.md new file mode 100644 index 0000000..00324c0 --- /dev/null +++ b/applications/SprPoP/docs/perf_green_phase.md @@ -0,0 +1,327 @@ +# ЗЕЛЁНАЯ фаза (слой фона) — анализ и оптимизация + +Рабочий документ: живёт между сессиями. Внизу **журнал правок** — каждая +запись с замером до/после. Сцена, рецепт воспроизведения и зонды — +[`perf_l13_room23.md`](perf_l13_room23.md); там же ответ про 8-битные +габариты. Парная фаза — [`perf_cyan_phase.md`](perf_cyan_phase.md) (её цель +достигнута). + +Границы фазы в `sprpop.c`: от `PROF(4)` (строка 450) до `PROF(6)` +(строка 620). Содержимое: `pop_loose_tick`, `pop_process_trobs`, +`pop_redraw_needed`, шов, смена уровня, сигналы провалов, вспышка. + +**Бюджет: ≤ 400 000 тактов (растровый кадр = 430 000).** + +| состояние | было (`c312e4a`) | стало (`af189a1`) | +|---|---:|---:| +| покой в комнате 23 | 35 760 | 35 760 | +| дрожат 6 плит-потолков | 366 000 | 335 400 | +| **пик каскада** | **805 000** | **546 900** | + +**ЦЕЛЬ ФАЗЫ НЕ ДОСТИГНУТА: 546 900 против 400 000 (1,37×).** Что осталось +сделать и почему это именно раскол `draw_tile` — §3. + +--- + +## 1. Раскладка ПОСЛЕ правок (замер `af189a1`) + +Пик — кадры 24-28 (посадки плит), больше не кадры провалов. + +| участок | покой | дрожь | пик | +|---|---:|---:|---:| +| `pop_loose_tick` | 36 234 | 36 234 | **156 762** | +| `pop_process_trobs` | 1 050 | 1 050 | 1 050 | +| **`pop_redraw_needed`** | 924 | 288 800 | **380 568** | +| хвост (шов, смена уровня, сигналы, вспышка) | 9 336 | 9 336 | 10 776 | + +`pop_loose_tick` изнутри на пике: два цикла по тайлам 9 852, +**`pop_loose_mob_tick` 154 074** (heal шести летящих кусков), остальное мелочь. + +### Цена одной перерисовки: было → стало + +| вид | было | стало | чем | +|---|---:|---:|---| +| `RDA_CEIL` — дрожащая плита-потолок | 48 785 | **43 536** | G2 + G3 | +| `RDA_CEIL_GONE` — запечь колодец | 251 335 | **138 318** | **G1** (клип полосы) + G2 + G3 | +| `RD_FLOOR` — щебень на месте посадки | 198 805 | **179 914** | G2 + G3 + снятая двойная пометка | + +Штук за кадр: `RDA_CEIL` до 6, `RDA_CEIL_GONE` до 2, `RD_FLOOR` до 2. + +### Детальный профиль `RD_FLOOR` (179 914) — главная оставшаяся статья + +Снят зондами по каждому блиту (`pop_dbg_b1/b5`) и по рамкам +(`pop_bar_black`, `pop_cd_batch_begin/end`): + +| участок | такты | +|---|---:| +| вход `pop_floor_bake` + `gfx_set_bank` | 2 382 | +| `pop_bar_black` 60×39 (включая `pop_cd_touch` 4 502) | ~15 500 | +| **контекст `draw_tile` #1** (5 чтений тайлов, `63*row`, индексация таблицы) | **13 584** | +| 4 блита тайла #1 | 50 718 | +| **диспетчер между блитами #1** (все `if (code == …)`) | **11 388** | +| **контекст `draw_tile` #2** | **13 584** | +| 4 блита тайла #2 | ~54 000 | +| **диспетчер между блитами #2** | **11 388** | +| хвост | 6 474 | + +Итого: **105 500 — сами блиты (реальные пиксели), 74 400 — накладные**, из +которых 27 168 контекст двух `draw_tile` и 22 776 их диспетчер. + +--- + +## 2. Что сделано (с чем сравнивать) + +### G1. Окно клипа для точечной перерисовки — −90 000 + +`pop_t_win_set(x, ytop, w, h)` / `pop_t_win_clear()` в `pop_tile.c`: ставит +уже существующее окно `pop_t_fclip_*` на прямоугольник, который перерисовка +восстанавливает. Работает в обе стороны — и предфильтр `pop_blit_b` +отсеивает куски мимо окна ДО `atlas_image`/`gfx_w0_map`, и `blit_b_clip` +режет остальные по нему. + +Где сработало: `pop_ceil_bake_empty` — куски ряда 0 высотой 63 px рисовались +целиком, хотя восстановить надо девять строк полосы. **Блит 21 447 → 7 619**, +вся перерисовка 251 335 → 138 318. + +Где НЕ сработало — см. §4, отрицательные результаты. + +Побочно: пока окно стоит, `pop_blit_b` не ставит пометку «фон трогали» +(признак fore-прохода), поэтому вызывающий обязан пометить прямоугольник сам. +В `pop_ceil_shake_draw` добавлен явный `pop_cd_touch` на область heal'а; в +`pop_ceil_bake_empty` и `pop_floor_bake` метит `pop_bar_black`, а лишний +второй вызов на ту же область снят. + +### G2. Контекст тайла — file-scope, а не локали `draw_tile` — −55 000 + +Порт `load_curr_and_left_tile` (seg008:0339): у оригинала это +`curr_tile`/`curr_modifier`/`draw_xh`/`draw_main_y`/`draw_bottom_y` — +переменные модуля, а не локали. + +Причина в кодогене: в `draw_tile` **57 вызовов**, и каждое живое через вызов +значение SDCC спиливал в стековый кадр — 26 байт кадра и **513 обращений +`-N(ix)`** (при ~46 замеренных тактах на обращение это ~23 600, что и +намерено). Стало **33 обращения**, кадра нет, банк 7 −703 Б. + +### G3. `blit_b_clip` — байтовый габарит + file-scope + +Два шага, и важен порядок наблюдений: + +1. **Байтового габарита ОДНОГО НЕ ХВАТИЛО.** `sx/sy/dw/dh` → `uint8_t` + (корректно: кадры атласов ≤ 56×63) дало 211 → 173 обращения, а + 22-байтовый кадр остался: значений, живых через шесть вызовов ядер libbgi, + всё равно больше, чем регистров у Z80. +2. **Решило вынесение из локалей** (`bc_*`): 51 обращение, кадр 22 → 12 Б. + +Клипованный блит 14 088 → 11 848. Заодно `blit_b_oversize` больше не ходит +через `blit_b_clip` (там теперь байтовый габарит) — рисует напрямую +`gfx_blit_part`; это путь под полноэкранные подложки интро/финала. + +### G4. Мелочи + +- `pop_blit_b`: аргументы в file-scope (третий и дальше SDCC передаёт стеком, + каждое чтение шло через `-N(ix)`) — 76 → 11 обращений. +- `pop_loose_mob_tick`: пометки всех кусков ОДНИМ пакетом + (`pop_cd_batch_begin/end`) — было по 4 502 такта на кусок. + 176 772 → 168 600. +- Коридор heal куска — по фактической высоте СОБРАННОГО композита (было 24 + строки константой, стало 20). 168 600 → 156 762. + +--- + +## 3. Что осталось: раскол `draw_tile` (позиция G5) + +**Оставшийся разрыв: −147 000.** Он весь в двух местах. + +### G5. Расколоть `draw_tile` на узкие части, как в оригинале + +**Ожидание: −50 000 … −60 000.** + +У оригинала `draw_tile` (seg008:01C7) — это девять независимых вызовов: +`draw_tile_floorright`, `draw_tile_anim_topright`, `draw_tile_right`, +`draw_tile_anim_right`, `draw_tile_bottom`, `draw_loose`, `draw_tile_base`, +`draw_tile_anim`, `draw_tile_fore`. Для ряда −1 он зовёт шесть из них +(`draw_tile_aboveroom`, seg008:01F2), для полосы у потолка — те же шесть плюс +`draw_tile_wipe(3)` (`redraw_needed_above`, seg008:02C1). + +У нас всё это — ветки `if (row >= 0)` ВНУТРИ одной функции, то есть контекст +(13 584) и диспетчер (11 388) оплачиваются целиком всегда. Расколов, каждая +точечная перерисовка сможет звать только нужные части: + +- `pop_floor_bake`: вместо второго полного `draw_tile(row, col+1)` — только + его правую грань и базу; +- `pop_ceil_shake_draw` / `pop_ceil_bake_empty`: дословный + `draw_tile_aboveroom`; +- `pop_loose_shake_draw`, `pop_spike_redraw`, `pop_gate_redraw` — то же. + +Риск средний: у `draw_tile` собрано много инвариантов (BUG-LOOSE-3, +BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1), проверять придётся прогонами всех +уровней. Поэтому делать отдельным заходом, а не хвостом другой правки. + +### G6. Меньше блитов в `RD_FLOOR` + +**Ожидание: неизвестно, надо мерить.** 105 500 из 179 914 — это 7,6 блита, +и они рисуют настоящие пиксели. Сократить можно только сократив то, что +восстанавливается: бар сейчас 60×39 от `yb+26`, а плита занимает по вертикали +меньше (её куски: левая грань `POP_LOOSE_FRAM_LEFT` 32×13 на `dmy = yb+62`, +низ `POP_LOOSE_FRAM_BOTTOM` 32×3 на `dby = yb+65`, правая грань в соседе +26×16 на `dby−1`). То есть плита живёт в `yb+47 .. yb+65`, а бар начинается с +`yb+26` — **21 лишняя строка сверху**. + +Проверять осторожно: бар заодно стирает и то, что рисует ДРУГИЕ куски тайла +(орнаментная лента `stripe_id` на `dmy−27 = yb+35` попадает как раз в +«лишнюю» часть). Сузишь бар — надо убедиться, что ничего не осталось. + +### G7. heal летящих кусков — 154 074 (28 % фазы) + +Шесть кусков × ~25 700: сам heal 64×20 (по модели ~19 700) + пакетная +пометка + накладные `mob_tick_one` (16-байтовый кадр, 99 обращений `(ix)`). +**Сам heal у предела железа** — это 1 280 пикселей на кусок, оптимизировать +нечего, кроме площади. Площадь уже подрезана до габарита композита. + +Остаётся `mob_tick_one` (~4 500 на кусок = 27 000 на кадр) — то же лечение +file-scope, что у `draw_tile`. + +### G8. Пометка соседа — узкой полосой, а не полным тайлом (идея пользователя) + +**Ожидание: заметное, но не мерено. Взять ПОСЛЕ обхода всех уровней** +(решение пользователя 2026-08-17: пока идёт отлов багов слоёв, каждая правка +добавляет переменных в картину). + +Когда плита (1,8) падает, помечаются ДВА тайла: + +| пометка | что делает | +|---|---| +| `(1,8)` → `RD_LOOSE_GONE` | бар 40 на своём x, бар 32 на соседе, `draw_tile(1,8)` + `draw_tile(1,9)` | +| `(1,9)` → `RD_FLOOR` | бар **60** на x соседа, `draw_tile(1,9)` ЕЩЁ РАЗ | + +То есть сосед перезапекается ЦЕЛИКОМ и ПОВТОРНО, хотя потревожили у него +только левые 28 пикселей — там, куда свисает правая грань упавшего тайла. +`draw_tile(1,9)` при этом вызывается дважды на одну пометку. + +Что такое эти числа (чтобы не сузить лишнего): + +- **60 = 32 свой тайл + 28 СОБСТВЕННЫЙ свес.** Правая грань пола (кадр 42, + 26 px) рисуется в клетке соседа с `x+32`, занимая `x+32..x+57`. Для запечки + САМОГО тайла 60 уже минимальны — сужать их нельзя; +- сузить можно только тот случай, когда тайл помечен ПОТОМУ ЧТО ИЗМЕНИЛСЯ ЕГО + ЛЕВЫЙ СОСЕД: тогда нужна полоса 28 px у левого края, а не весь тайл. + +Почему выигрыш не символический: `pop_floor_bake` стоит **179 914** тактов, +из них 105 500 — сами блиты. Узкая полоса срезала бы и площадь бара +(60×39 → 28×39), и часть блитов — окно клипа там теперь стоит обязательным +(см. §4), так что отсев достаётся даром. + +**Условия, из-за которых это не «просто уменьшить число»:** + +1. `pop_floor_bake` — ОБЩАЯ функция: её же зовут кнопка (`pop_button_redraw`), + зеркало, подобранный предмет и щебень на месте посадки. Там меняется сам + тайл и 60 нужны целиком. Значит нужен отдельный вход (напр. + `pop_floor_bake_edge(row, col)`) или параметр-прямоугольник — именно под + пометку «изменился мой левый сосед». +2. Прежде чем выкидывать вторую пометку целиком, сверить ВЕРТИКАЛЬНЫЕ + диапазоны: `pop_loose_bake_empty` кроет `63*row+46 .. +65` (20 строк), а + `pop_floor_bake` — `yb+26 .. yb+64` (39 строк). То есть сосед покрыт + ВТОРЫМ баром не полностью, и просто снять пометку нельзя. +3. Ширина полосы = свес ЛЕВОГО тайла, а он зависит от типа тайла (у loose это + 8 px по комментарию в `pop_loose_bake_empty`, у пола 26). Брать по + максимуму (28) — безопасно. + +### G9. Снять временную оснастку + +Шесть `pop_dbg_b1..b6` внутри `pop_blit_b` — ~400 такта на блит; при 15 +блитах зелёной это 6 000 на кадр. Плюс `pop_dbg_kind`/`m16` (2 вызова на +перерисовку) и `pop_dbg_m5..m15`. Снимать ПОСЛЕ окончания оптимизации: без +них не мерить. + +--- + +## 4. Копия второй страницы — и почему она ТРЕБУЕТ окна клипа + +Точечные запечки ставятся с `pages = 2`, срабатывают два кадра подряд (по разу +на страницу дабл-буфера) и оба раза считают одно и то же. После первого раза +нужный прямоугольник уже лежит в ОЗУ-копии первой страницы, и его можно +скопировать: `gfx_copy_page` берёт источником ОЗУ-копию НЕактивной страницы +(то есть ЧИСТЫЙ фон — спрайты рисуются банком без тени и в копию не попадают), +а приёмник обновляет и в видео-ОЗУ, и в ОЗУ-копии. Идея пользователя: тот же +приём, что при перевороте экрана (зелёное зелье), только без зеркала. + +| | полная запечка | копия | +|---|---:|---:| +| щебень / кнопка (60×39) | 179 914 | ~35 000 | +| колодец полосы потолка (64×9) | 138 318 | ~17 500 | + +**ДВА УСЛОВИЯ КОРРЕКТНОСТИ.** Оба нарушались и оба дали видимые баги. + +1. **Запечка обязана быть ОГРАНИЧЕНА копируемым прямоугольником.** + `draw_tile` рисует тайлы ЦЕЛИКОМ, то есть пишет ШИРЕ бара; копия переносит + ровно бар, и всё, что легло вне него, на второй странице остаётся прежним — + страницы расходятся, это видно как МЕРЦАНИЕ через кадр. У полосы потолка + окно стояло с самого начала (G1), у `pop_floor_bake` — нет, и он мерцал + торцами полов, плит и кнопок (найдено пользователем 2026-08-17: уровень 1, + комната 6, Кид на кнопке (0,2)). Поэтому в `pop_floor_bake` окно теперь + стоит КАК УСЛОВИЕ КОРРЕКТНОСТИ, хотя по скорости само по себе убыточно + (см. §5) — снимать его нельзя. +2. **Копия годится только если содержимое тайла между двумя кадрами не + изменилось.** У анимированного тайла (кнопка с идущим таймером связи) + пометка обновляется КАЖДЫЙ кадр и картинка каждый раз другая. Поэтому + `pop_set_redraw`/`pop_set_redraw_above` гасят слот копии при ПЕРЕпометке + (`pop_bake_slot_reset*`). + +Плюс слот `bake_pg`/`bake_pg_above` помнит, НА КАКОЙ странице сделана первая +запечка: копируем только если первая была на ДРУГОЙ странице и дабл-буфер +включён. Это покрывает переплетение двух запечек в одном кадре, однобуфер +(чит SPACE) и смену комнаты (`pop_bake_forget`). + +--- + +## 5. Отрицательные результаты — НЕ повторять + +### Окно клипа в `pop_floor_bake` — по СКОРОСТИ проверено ТРИ раза, каждый раз хуже + +**Но оно всё равно стоит на месте: без него ломается копия второй страницы +(§4).** Ниже — только про скорость самого окна. + +| попытка | было | стало | +|---|---:|---:| +| до G3 | 187 266 | 198 279 | +| после G3 | 182 124 | 188 460 | +| после `pop_blit_b` file-scope, с детальным зондом | 179 914 | 188 417 | + +Третья попытка объяснила причину: клипованный путь стоит **+1 500 такта на +КАЖДОМ** из 7,6 блитов (+11 400), а режет он только редкие высокие куски — в +трассе такие нашлись (34 878 → 25 872 и 28 818 → 24 090, всего −13 700), но в +среднем по 12 перерисовкам их нет. Запись стоит в коде. + +### Прочее (проверено раньше) + +- **Не откладывать запекание на другой кадр** — запечка пишет ОЗУ-копию, из + которой восстанавливает heal; отложенная даёт призрак плиты на месте дыры. +- **Не батчить смежные колонки в `pop_ceil_shake_draw`** — плиты стартуют со + случайными задержками, в кадре дрожат разрозненные колонки, пробег почти + всегда длиной в одну. +- **Не ускорять передачу пикселей** — предел железа (3+3 такта на байт, + memory `blit_cost_model`). В пиковом кадре «железный» минимум всех блитов + ≈150 000 из 916 458. +- **`LOOSE-SHAKE-RUNS`** (пометки по сменам кадра): наивный вариант выигрыша + НЕ даёт — пять смен × две страницы = те же десять перерисовок. Работает + только версия «пары и тройки», ~20 % и только на дрожащих плитах; оценка + 2026-08-13, не перемерена. Подробности — + `TASKS_OPEN.md#loose-shake-runs`. + +--- + +## 6. Журнал правок + +| дата | что сделано | зелёная: покой / дрожь / пик | коммит | +|---|---|---|---| +| 2026-08-17 | базовый замер | 35 760 / 366 000 / **805 000** | `c312e4a` | +| 2026-08-17 | G2 контекст тайла в file-scope | — / — / **747 954** | `a3c473d` | +| 2026-08-17 | G1 окно клипа в `pop_ceil_bake_empty` | — / — / **663 250** | `a3c473d` | +| 2026-08-17 | G3 `blit_b_clip` байты + file-scope | — / 335 400 / **575 730** | `a3c473d` | +| 2026-08-17 | `pop_blit_b` file-scope; пакетная пометка кусков | — / — / **557 706** | `b2da0b8` | +| 2026-08-17 | коридор heal по высоте композита | 35 760 / 335 400 / **546 900** | `9a50ab2` | +| 2026-08-17 | **копия второй страницы вместо второй запечки** | — / — / **423 558** | `5ef721e` | +| 2026-08-17 | `mob_tick_one` в file-scope; снята оснастка из горячих путей | 35 760 / 326 130 / **414 456** | `18ee60e` | +| 2026-08-17 | фикс мерцания: окно клипа в `pop_floor_bake` как условие корректности копии | замер после фикса — ниже | `35b7cd5` | +| 2026-08-17 | замер после фиксов уровня 1 (мерцание торцов, потолочный fore, сосед под плитой, блеск меча) | 35 760 / — / **417 630** | `40f0d46` | +| 2026-08-17 | **регресс после фиксов уровня 2** (чёрные бары, чит бессмертия) — в пределах шума | — / — / **419 526** | `ec1f384` | diff --git a/applications/SprPoP/docs/perf_l11_room15.md b/applications/SprPoP/docs/perf_l11_room15.md new file mode 100644 index 0000000..24c59b0 --- /dev/null +++ b/applications/SprPoP/docs/perf_l11_room15.md @@ -0,0 +1,238 @@ +# Сцена и замер: факел + чомпер + страж, уровень 11 комната 15 + +Вторая целевая сцена для оптимизации (первая — [`perf_l13_room23.md`](perf_l13_room23.md), +каскад плит). Здесь узкое место другое: не разовый пик на каскаде, а +**постоянная** цена статичной комнаты, в которой одновременно живут два +факела, чомпер и страж. + +Такты — `totalcycles` MAME (не такты Z80, ≈2,4× номинала, memory +`sprinter_wait_states_2x`). **Растровый кадр = 430 000.** Хвост кадра — три +`gfx_wait_vsync`, поэтому логический кадр занимает 3 растра, пока работа +укладывается в один; при работе 1..2 растра период становится 4. + +## 1. Сцена + +Уровень 11, комната 15. Проверено чтением состояния машины: +`pop_current_level` = 0x0B, `cur_room` = 15. + +| кто | где | кадр | +|---|---|---| +| Кид | (0,2), x=98 | 15 (стойка с мечом) | +| чомпер | (0,3) | застывший (trob мёртв) | +| факел | (0,2) — пламя рисуется в ячейке (0,3) | анимируется каждый кадр | +| факел | (0,7) — пламя в ячейке (0,8) | анимируется каждый кадр | +| страж | (0,8), x=170 | 171 (боевая стойка) | + +Ни Кид, ни страж не двигаются: сцена статична, разброс замера — сотни тактов +на 800 000. + +## 2. Замер (`09f32ce`, база модуля `sprpop.c` = 0x42AD) + +867 кадров, зонды A/C/D/E; детализация — тремя отдельными прогонами +(m1, m5..m7, M/F). **Период кадра: 4 растра во всех 866 интервалах.** + +| участок | зонды | медиана | доля работы | +|---|---|---:|---:| +| **синяя: ввод + heal** | A→m1 | 141 048 | 17,6 % | +| **синяя: логика** | m1→C | 152 190 | 19,0 % | +| синяя, всего | A→C | **293 238** | 36,6 % | +| зелёная: `pop_loose_tick` | C→m5 | 28 872 | 3,6 % | +| **зелёная: `pop_process_trobs`** | m5→m6 | 92 448 | 11,5 % | +| **зелёная: `pop_redraw_needed`** | m6→m7 | 190 260 | 23,7 % | +| зелёная: шов/ворота соседа | m7→D | 9 336 | 1,2 % | +| зелёная, всего | C→D | **320 916** | 40,0 % | +| циан: `check_mirror` + `loose_mob_draw` | D→M | 37 458 | 4,7 % | +| **циан: Кид + страж + fore + HP** | M→F | 148 566 | 18,5 % | +| циан: `char_fore(KID)` + борта | F→E | 1 740 | 0,2 % | +| циан, всего | D→E | **187 758** | 23,4 % | +| **работа** | A→E | **801 768** | 1,86 растра | + +Циан здесь **не** выделяется: 187 758 — это 0,44 растрового кадра, а разброс +за 867 кадров всего 174 такта (187 674..187 848). Впечатление «циан ~150 % +кадра» на глаз не подтвердилось — при периоде 4 растра полосы бордюра +размазаны по кадрам и на глаз не читаются. + +## 3. Главная находка: чомпер справа от факела — 190 260 тактов/кадр + +`pop_dbg_rdmax_tot` = **1**: за кадр перерисовывается РОВНО ОДИН тайл, и +стоит он все 190 260 тактов зелёной фазы. + +> **ПОПРАВКА 2026-08-19 (после реализации P1).** Механизм ниже описан +> верно, но ГЛАВНЫМ источником 190 260 тактов он НЕ был. Зонд +> `pop_dbg_kind` показал, что все 312 перерисовок в прогоне — вид +> `POP_RD_CHOMP` (полная), и ни одной от факела: собственная пометка +> чомпера просто перебивала пометку соседа. Настоящая причина — в §6. +> Урок ровно тот, что уже записан в `defer_unexplained_quirks`: механизм, +> который правдоподобно объясняет цифру, ещё не доказан цифрой. + +Цепочка: + +1. `TORCH_ANIM_DIV = 1` — факел меняет кадр пламени КАЖДЫЙ логический кадр; +2. пламя запекается в фон (`pop_torch_draw`, `GFX_BANK_NORMAL`), а канвас + пламени 16×18 лежит **в ячейке правого соседа** (seg008:560) — то есть + поверх чомпера; +3. поэтому `pop_process_trobs` метит соседа: `if (trob_rcode[i] == TILE_CHOMP) + pop_set_redraw(tp + 1, POP_RD_CHOMP, 1)` (порт `set_redraw_anim_right`); +4. `pop_chomp_redraw` отвечает на пометку **heal 32×64 + полный `draw_tile`**. + +Расхождение с оригиналом именно в шаге 4. `set_redraw_anim_right` метит +слой **anim**, и оригинал возвращает только его (`draw_tile_anim_topright` → +`draw_tile_anim_right` → `draw_tile_anim`) — одну графику чомпера поверх +огня. Мы вместо этого стираем и пересобираем тайл целиком, со всеми слоями +(`draw_tile_right`, `base`, `bottom`, `loose`), которые пламя вообще не +трогало. + +Цена по модели блита (`blit_cost_model`, 8791 + 198·h + 5,96·w·h): +heal 32×64 ≈ 33 700, значит на один `draw_tile` уходит ≈ 156 000 — сходится +с известным замером «полная запечка щебня 179 914». + +Чомпер при этом **застывший**: своей анимации у него нет, поза не меняется, +возвращать нужно ровно ту же графику поверх свежего пламени. + +## 4. Что это даёт и куда смотреть дальше + +Ранжирование по цене (доля от 801 768): + +| # | участок | такты | что делать | +|---|---|---:|---| +| 1 | `redraw_needed`: чомпер под факелом | 190 260 | вернуть только слой anim, как в оригинале — без heal и без остальных слоёв | +| 2 | синяя: логика двух Char | 152 190 | графики нет вообще; разобрать `pop_check_can_guard_see_kid` и два `play_seq` | +| 3 | циан: Кид + страж | 148 566 | оба будятся каждый кадр — метки фона от чомпера/факела накрывают обоих | +| 4 | синяя: heal двух Char | 141 048 | следствие того же: skip не срабатывает ни разу | +| 5 | `process_trobs`: два факела | 92 448 | ≈46 000 на факел при блите пламени 16×18 ≈ 14 000 — разобрать накладные | +| 6 | `loose_tick` | 28 872 | в комнате нет ни одной loose-плиты | + +Пункты 3 и 4 — одна тема: пока фон трогают каждый кадр, `pop_char_skip_mask` +не может пропустить ни Кида, ни стража. Пламя метит узко (16×18), а вот +`pop_chomp_redraw` метит весь тайл со свесом — то есть пункт 1 чинит и часть +пунктов 3/4. + +Связанные задачи: `HEAL-WIDTH` и G8 в [`perf_green_phase.md`](perf_green_phase.md) +— та же болезнь (полный тайл там, где хватает полосы), но на другом +материале. + +## 6. Настоящая причина 190 260 тактов: перерисовка неизменной позы + +Найдено при реализации P1, сверкой с `animate_chomper` (seg007:0448). +Функция оригинала заканчивается так: + +```c +if ((curr_modifier & 0x7F) < 6) { + redraw_at_trob(); +} +``` + +То есть чомпер перерисовывается **только пока фаза меньше 6** — пять кадров +из пятнадцати (`POP_CHOMPER_SPEED = 15`). Это не оптимизация оригинала, а +следствие таблицы поз: `chomper_fram1 = {3,2,0,1,4,3,3}`, и начиная с фазы 5 +и до конца круга поза одна и та же — 3. Рисовать её десять кадров подряд +значит рисовать ровно ту же картинку. + +Мы же метили тайл БЕЗУСЛОВНО, каждый кадр, пока trob жив — то есть платили +полный `draw_tile` плюс heal 32×64 за неизменную картинку в двух третях +кадров. А trob у чомпера живёт, пока Кид в том же РЯДУ (`animate_chomper` +снимает его только при фазе ≥ 6 и ушедшем Киде) — в 11/15 Кид стоит в (0,2), +чомпер в (0,3), ряд один. + +**Что сделано:** + +1. пометка только при фазе < 6, и на фазе 5 — на ОБЕ страницы дабл-буфера + (она последняя рисуемая, её поза обязана лечь на обе; вторую страницу + пометка догоняет в кадре фазы 6, где поза та же самая); +2. пометка от факела (`set_redraw_anim_right`) переведена на новый вид + `POP_RD_CHOMP_ANIM` → `pop_chomp_anim_draw`: три блита графики чомпера + поверх свежего пламени, без heal и без остальных слоёв — порт ветки + `redraw_frames_anim` (seg008:0211); +3. приоритет полной перерисовки над anim в `pop_set_redraw` — у оригинала + это два независимых счётчика, и `full` побеждает. + +**Результат** (замер, 552 кадра): полная перерисовка теперь в **40 %** +кадров, лёгкий возврат челюстей — в 60 %. Зелёная фаза: 291 888 в дорогом +кадре против 183 414 в дешёвом. + +| | работа | зелёная | +|---|---:|---:| +| до P1 | 768 684 | 294 510 | +| после P1, медиана | **657 882** | **183 420** | +| после P1, дорогой кадр (40 %) | 765 936 | 291 888 | + +**−110 802 на медиане** при ожидании −160 000. Разница в том, что 40 % +кадров по-прежнему платят полную цену: там поза реально меняется, и это уже +не лишняя работа, а честная. Дальше её можно резать только раскладом +`draw_tile` на части (P7) или сужением heal (P8). + +## 5. Журнал правок по этой сцене + +| дата | правка | работа | синяя | зелёная | циан | +|---|---|---:|---:|---:|---:| +| 2026-08-19 | базовый замер (`09f32ce`) | 801 768 | 293 238 | 320 916 | 187 758 | +| 2026-08-19 | **P5**: гейты холостого хода в `pop_loose_tick` | **767 928** | 285 864 | 294 384 | 187 764 | +| | | −33 840 | −7 374 | −26 532 | +6 | +| 2026-08-19 | зонды для замера P2/P6 (временные) | 768 684 | 286 518 | 294 510 | 187 761 | +| 2026-08-19 | **P1**: чомпер — перерисовка только при фазе < 6 (медиана) | **657 882** | 286 503 | 183 420 | 187 761 | +| | | −110 802 | −15 | −111 090 | 0 | + +Оснастка P2/P6 стоит 756 тактов на кадр — замеры до и после сопоставимы. + +Циан не изменился (+6 тактов — шум), и это ожидаемо: `loose_tick` целиком +лежит в зелёной. Синяя просела на 7 374 без прямой причины в правке — +скорее всего перераскладка кода банка 3 компилятором; проверять отдельно +не стали, знак верный. + +## 7. ТЯЖЁЛАЯ позиция: Кид на шаг правее [замер 2026-08-19] + +Поставлена пользователем: один осторожный шаг вправо (x = 106 вместо 99, +колонка та же). Спрайт Кида начинает пересекаться с тайлом (0,3), где +одновременно чомпер и пламя факела — и `skip_mask` перестаёт его +пропускать. + +| фаза | лёгкая | **тяжёлая** | разница | +|---|---:|---:|---:| +| синяя | 259 500 | 257 520 | −1 980 | +| зелёная (медиана) | 181 068 | 180 870 | −198 | +| **циан** | 187 761 | **319 842** | **+132 081** | +| **работа (медиана)** | 628 542 | **758 358** | **+129 816** | +| работа (максимум) | 744 384 | **876 612** | | + +**Период кадра: 4 растра в 335 кадрах, 5 растров в 123 (27 %).** Это уже +не «стабильно медленно», а рывки: каждый четвёртый кадр длиннее соседних. + +Разбор циана показывает, куда ушли 132 тысячи: + +| участок | лёгкая | тяжёлая | +|---|---:|---:| +| `check_mirror` | 3 198 | 3 198 | +| `mob_draw` + `guard_over_kid` + `skip_mask` | 34 374 | 18 672 | +| **`pop_char_draw(KID)`** | **204** | **54 738** | +| соперник: `char_draw` + `char_fore` | 145 896 | 149 748 | +| **`fore_needed` + `char_fore(KID)` + борта** | **4 356** | **93 486** | + +То есть Кид из «пропущен за 204 такта» превращается в полноценного +персонажа за ~144 000 — ровно столько же, сколько стоит страж. + +**Главный вывод замера: самая дорогая единичная статья кадра — это +fore-проход персонажа.** 62 778 у стража и ~89 000 у Кида, вместе около +**152 000, то есть 20 % работы кадра**. У Кида он дороже потому, что в его +футпринте лежит чомпер, а у чомпера есть собственный передний слой +(`POP_CHOMP_FRAM_FOR`), который перерисовывается поверх персонажа каждый +кадр. + +## 8. После P15 (точная метка «фон трогали») + +| фаза | лёгкая до | лёгкая после | тяжёлая до | тяжёлая после | +|---|---:|---:|---:|---:| +| синяя | 259 050 | **223 902** | 257 520 | **245 808** | +| зелёная | 181 494 | 181 494 | 180 870 | 181 761 | +| циан | 194 262 | **59 406** | 319 842 | **192 090** | +| **работа** | 628 542 | **464 796** | 758 358 | **617 487** | + +В лёгкой позиции не рисуется НИ ОДИН персонаж (циан 59 406 — это уже только +`check_mirror`, проверки и передний слой по пометкам). В тяжёлой рисуется +один Кид: он действительно стоит под пламенем, а страж — нет. + +**Пятирастровые кадры в тяжёлой позиции исчезли** (было 27 %), период стал +ровно 4. + +**Полная очередь оптимизаций с оценками — [`perf_registry.md`](perf_registry.md).** +Там же разложена цена одного блита фона по этапам (замер 2026-08-19, 1603 +блита) и модель зелёной фазы этой сцены. diff --git a/applications/SprPoP/docs/perf_l13_room23.md b/applications/SprPoP/docs/perf_l13_room23.md new file mode 100644 index 0000000..3a64012 --- /dev/null +++ b/applications/SprPoP/docs/perf_l13_room23.md @@ -0,0 +1,271 @@ +# Сцена и метод замера: каскад плит, уровень 13 комната 23 + +Общий документ для двух фазовых: [`perf_green_phase.md`](perf_green_phase.md) +(слой фона) и [`perf_cyan_phase.md`](perf_cyan_phase.md) (персонажи + передний +слой). Здесь — как воспроизвести сцену, чем мерить, сводка по кадрам и +разбор габаритов спрайтов (он общий для обеих фаз). + +Сцена: старт уровня 13. Комната 23 стартовая, ряд 2 комнаты СВЕРХУ (17) — +шесть loose-плит в колонках 2..7 (`res2013.bin`: коды `11` в позициях 22..27), +`check_fall_flo` раздаёт им отложенный старт `0xF0..0xFF`, и они сыплются +вразнобой. Кид стоит у правого края и не двигается. + +Все числа — такты `totalcycles` MAME (системный клок ~21,5 МГц, **НЕ** такты +Z80: у ОЗУ Sprinter wait-state'ы, ≈2,4× номинала — memory +`sprinter_wait_states_2x`). **Растровый кадр = 430 000.** Логический кадр +спейсится тремя `gfx_wait_vsync`, поэтому работа сверх 430 000 стоит сразу +целый лишний растровый кадр. + +Сборка: `make LEVEL=13` на `c312e4a`, `_CODE = 0x4100`, база модуля +`sprpop.c` = **0x42AD**. **Адреса зондов меняются после КАЖДОЙ +пересборки** — брать заново из `.sprinter-cc-build/.sprinter-cc-sprpop/sprpop.map` и +`sprpop.lst`. + +--- + +## 1. Как воспроизвести сцену + +**Только перезапуском программы.** Проверено и отвергнуто: + +- **выход из комнаты и возврат** (чит `+`/`-`) — не работает: провалившаяся + плита-потолок уходит в страницу уровня насовсем (`pop_level_set_tile` в + `sprpop.c` по сигналу `pop_ceil_fell`, плюс `animate_loose` в + `pop_trob.c`), и при повторном входе `check_fall_flo` не находит ни одной + `TILE_LOOSE`; +- **рестарт уровня** (`pop_kid_dead = 1` + чит навигации) — не работает по + другой причине: `pop_start_level()` сам заходит в стартовую комнату 23, + взводит гряду, и она доваливается ЗАОЧНО (через `trob` комнаты 17), пока + телепорт уносит Кида в комнату 24; +- **поставить сцену руками** (записать `pop_ceil_modif[2..7]` и копию ряда + сверху `pop_t_above[2..7]` отладчиком) — записи ложатся, но пока машина + БЕЖИТ, их успевает обнулить тот же доваливающийся `trob`. + +Рабочий рецепт (идея пользователя, самый чистый): **`ESC` → зонды → +`SprPoP`**. `ESC` выходит в DSS, запуск заново стартует уровень 13 с нуля, +Кид сразу в комнате 23, каскад начинается через ~5 логических кадров после +отрисовки комнаты. Зонды обязаны стоять **ДО** набора `SprPoP` — за время +набора (9 клавиш ≈ 1,8 с) и загрузки атласов каскад успевает пройти целиком. + +## 2. Канал вывода замеров + +`printf` из действия брейкпоинта в `error.log` **не** попадает. Читается +verb'ом **`clog N`** плагина `mamebridge` — а его нет в MCP-обёртке +(`mame_mcp.py` знает только `cmd`). Годится прямой файловый IPC: +положить `/tmp/mame_mcp/req_<ЧИСЛО>.txt` с телом команды и прочитать +`resp_<ЧИСЛО>.txt`. **Имя обязано содержать ЧИСЛО** (`init.lua`: +`entry:match("^req_(%d+)%.txt$")`) — с буквенным id запрос молча не +обслуживается. + +Скрипты сессии (в scratchpad, при необходимости пересоздать): `mrpc.py` — +клиент IPC; `run.sh` — цикл «`bpclear` → `ESC` → зонды → `SprPoP` → `clog`»; +`parse*.py` — разбор трассы по кадрам. + +Форма зонда: `bpset ,1,{printf "<метка> %d",totalcycles; g}`. +Для `pop_dbg_kind` — `printf "K %d %d",a,totalcycles` (аргумент `uint8_t` +приходит в `A`, `__sdcccall(1)`). + +## 3. Зонды + +Адреса `out (_io_border), a` (полосы бордюра) из `sprpop.lst` плюс +однобайтовые пустышки `pop_dbg_*` из резидентного `pop_state.c`. Резидент +важен принципиально: у банковых функций один адрес 0xC000+ есть у восьми +модулей сразу, и брейкпоинт ловит все банки (так в прошлой сессии намерили +несуществующие 134 730 тактов). + +| зонд | адрес | что | +|---|---|---| +| A | 0x43E8 | `PROF(2)` — начало кадра (ввод + heal) | +| — | 0x46AF | `PROF(2)` — начало логики | +| C | 0x47D1 | `PROF(4)` — начало слоя фона (**зелёная**) | +| D | 0x4BCE | `PROF(6)` — начало спрайтов (**циан**) | +| M | 0x4C26 | `PROF(6)` — кадр Кида | +| F | 0x4C93 | `PROF(6)` — fore поверх Кида | +| E | 0x4CB7 | `PROF(0)` — конец работы, ждём vsync | +| m5/m6/m7 | 0x4DC6 / C7 / C8 | границы внутри зелёной | +| m9..m12 | 0x4DCA..CD | внутренности `pop_loose_tick` | +| m13/m14/m15 | 0x4DCE / CF / D0 | `pop_ceil_shake_draw`: вход / heal / draw_tile | +| kind / m16 | 0x4DD1 / D2 | вид и цена одной перерисовки в `pop_redraw_needed` | +| b1..b5 | 0x4DD3..D7 | участки одного `pop_blit_b` | + +Полезные адреса состояния (из `sprpop.map`): `pop_t_room` 0x95F2, +`pop_current_level` 0x9945, `pop_ceil_modif` 0x9C9E, `pop_t_above` 0x95EE +(указатель), `pop_kid_dead` 0x9C4E, `pop_dbg_rdmax` 0x95B1. +Запись в память через MCP — по адресу `0x10000 | addr` (логический вид Z80); +присваивание выражением дебаггера (`print b@... = 1`) **не работает**. + +**Грабли:** проверять, что запущен РОВНО ОДИН MAME (`pgrep -f mame.arm | wc -l`). +Мост говорит с одним, замеры собираются с другого, и точки «не срабатывают». + +--- + +## 4. Сводка по кадрам + +**Базовый замер (`c312e4a`, ДО оптимизации):** + +| фаза каскада | работа | синяя | зелёная | циан | период (растр.) | +|---|---:|---:|---:|---:|---:| +| покой в комнате 23 | 190 860 | 134 550 | 35 760 | 20 550 | **3** | +| дрожат 6 плит | 537 400 | 142 700 | 366 000 | 28 700 | **4** | +| провалы + полёт, ПИК | **1 437 150** | 142 700 | **663 250** | **631 200** | **5–6** | +| максимум по секции | | 142 830 | **805 000** | **631 800** | | + +**После оптимизации (`af189a1`, 2026-08-17):** + +| максимум по секции | работа | синяя | зелёная | циан | +|---|---:|---:|---:|---:| +| было | 1 437 150 | 142 830 | 805 000 | 631 800 | +| стало | **916 458** | 142 830 | **546 900** | **270 510** | +| | −36 % | — | −32 % | −57 % | + +**Регресс после обхода уровней 1-2 (`ec1f384`, 2026-08-17), 418 кадров:** + +| максимум по секции | работа | синяя | зелёная | циан | +|---|---:|---:|---:|---:| +| после оптимизации (`af189a1`) | 916 458 | 142 830 | 546 900 | 270 510 | +| после фиксов ур. 1 (`40f0d46`) | 873 930 | 158 874 | 417 630 | 379 482 | +| **после фиксов ур. 2 (`ec1f384`)** | **878 550** | **158 880** | **419 526** | **379 488** | + +Фиксы второго уровня (чёрные бары, чит бессмертия) на бюджет не повлияли: +разница с предыдущим замером +4 620 работы и +1 896 зелёной — шум прогона. + +**Регресс после обхода уровней 3-7 (`3bcaf51`, 2026-08-18), 2435 кадров:** + +| максимум по секции | работа | синяя | зелёная | циан | +|---|---:|---:|---:|---:| +| после фиксов ур. 2 (`ec1f384`) | 878 550 | 158 880 | 419 526 | 379 488 | +| **после фиксов ур. 3-7 (`3bcaf51`)** | **880 170** | **159 774** | **419 520** | **380 244** | +| разница | +1 620 | +894 | −6 | +756 | +| | +0,2 % | +0,6 % | 0,0 % | +0,2 % | + +Все четыре секции — в пределах шума прогона (сравнить с +4 620 / +1 896 +выше, которые уже признаны шумом). Зелёная совпала с точностью до 6 тактов. + +Что за это время добавилось в горячий путь: `pop_spike_frame` и +`pop_chomp_pose` (SPIKE-BAKED) — один резидентный `call` на слой и ТОЛЬКО на +тайлах-ловушках, в этой комнате их нет; и снятие раннего выхода для трупа +(DIED-ON-BUTTON) — цепочка физики на мёртвом Киде, а он тут жив. Замер это +подтверждает: цена не сдвинулась. + +Распределение периода тоже совпало с эталоном кадр в кадр: **3 растра в +2408 кадрах, 4 в 24, 5 в одном** — против «3 в 392, 4 в 24, 5 в одном» +у `ec1f384` (кадров в этом прогоне больше просто потому, что дольше стояли в +покое после каскада). То есть за бюджет вылезает ровно тот же кусок сцены и +ровно на столько же кадров. + +**Регресс после обхода уровней 8-9 (`0dd2f6a`, 2026-08-18), 2701 кадр:** + +| максимум по секции | работа | синяя | зелёная | циан | +|---|---:|---:|---:|---:| +| после фиксов ур. 3-7 (`3bcaf51`) | 880 170 | 159 774 | 419 520 | 380 244 | +| **после фиксов ур. 8-9** | **880 272** | **159 822** | **419 562** | **380 202** | +| разница | +102 | +48 | +42 | −42 | + +Разброс ±100 тактов на 880 000 — это 0,01 %, то есть чистый шум прогона +(циан вообще ушёл в минус). Период снова совпал кадр в кадр: 4 растра в +24 кадрах, 5 в одном. + +Что добавилось за это время и почему не подорожало: фиксы стража +(`c40ae3f`) правят только вход в комнату — кода в кадре не прибавилось; фикс +боя у шва (`a498255`) добавил два сравнения в `check_leave`, а в этой сцене +Кид неподвижен и до порогов не доходит. Отладочная трасса `DBG_KIDOBJ` +выключена дефайном и в сборку не попадает. +Скачок циан на фиксах ПЕРВОГО уровня (270 510 → 379 482) объяснён там же: +восстановлены потерянные половины слоёв (`set_redraw2`, ряд −1 foretable), то +есть это плата за корректность, а не регрессия. + +Период кадра по прогону: **3 растра в 392 кадрах, 4 в 24, 5 в одном** — то +есть за бюджет вылезает только сам каскад. + +Цель — каждая секция ≤ 400 000. **Синяя и циан в бюджете**; зелёная 419 526, +то есть 1,05× цели (и ниже растрового кадра 430 000), остаток разобран в +[`perf_green_phase.md`](perf_green_phase.md) §3 (нужен раскол `draw_tile` на +узкие части, как в оригинале) и в идее G8 (сузить инвалидацию соседнего тайла +до 28-пиксельной полосы). + +Где что расходуется и как это чинить — в фазовых документах: +[зелёная](perf_green_phase.md), [циан](perf_cyan_phase.md). + +### Общий вывод по пиковому кадру базового замера (1 437 150) + +| | такты | доля | +|---|---:|---:| +| блиты (все 27–28 вызовов `pop_blit_b`) | 488 100 | 34 % | +| из них «железный» минимум пикселей (модель `198*h + 5,96*w*h`) | ~150 000 | 10 % | +| синяя (ввод + heal + логика) | 142 700 | 10 % | +| **наши накладные: `draw_tile`, IX-кадры, диспетчер, пометки** | **~1 150 000** | **~80 %** | + +Узкое место — НЕ передача пикселей (она на пределе железа, 3+3 такта на байт, +memory `blit_cost_model`), а 16-битная арифметика в стековых кадрах. + +--- + +## 5. Габариты спрайтов: можно ли всё перевести на `uint8_t` + +Просканированы каталоги ВСЕХ `.atl` (109 файлов) и исходные PNG наборов +`TITLE`/`PV` — тех, что понадобятся для интро, финала и роликов между +уровнями. + +**Игровой кадр — весь укладывается в байт:** + +| набор | максимум | +|---|---| +| фон подземелья/дворца (`*_env*`, `*_wall`, `*_fore`, `pop_pot`) | **48 × 63** | +| Кид (`kid0..27`, `sword`) | **56 × 63** (kid3, idx 0) | +| страж / скелет / Джафар | **53 × 42** | +| спрайты комнаты принцессы (`PV.DAT`: персонажи, песочные часы, факел, звёзды) | **49 × 60** | + +**Больше 255 — только полноэкранные подложки титров и сюжетных экранов.** +Их восемь, и все рисуются ОДИН раз при показе экрана: + +| ресурс | размер | где (`data.h`, `full_image[]`) | +|---|---|---| +| `TITLE/res51` | 320 × 200 | `TITLE_MAIN`, xpos 0 ypos 0 | +| `TITLE/res41` | 320 × 200 | `STORY_FRAME`, xpos 0 ypos 0 | +| `PV/res951` | 320 × 200 | фон комнаты принцессы (`chtab_9_princessbed`) | +| `TITLE/res42..res45` | 272 / 267 / 264 / **256** × 134..142 | «presents», «Prince of Persia», «Mechner» | +| `TITLE/res54` | 272 × 65 | заголовок Hall of Fame | + +Высота нигде не превышает 200 — в байт лезет. По ширине не лезут ровно эти +восемь, и ни одна из них не участвует в игровом кадре. + +**Вывод: горячий путь можно переводить на 8-битные габариты целиком.** +Для подложек — решение пользователя (2026-08-17): работу с роликами вынести в +отдельный банк с версиями блита под большие спрайты либо звать libbgi напрямую +— клип и проверка выхода за экран им не нужны (рисуются в x = 0/24/48/96, +заведомо внутри 320×200). Ширина 320 всё равно потребует ДВУХ burst-скобок +акселератора на строку — как уже сделано в `pop_vflip`. + +Существующая страховка уже есть и остаётся: `pop_blit_b` уводит кадр с +`img[1] | img[3] != 0` на общий путь `blit_b_oversize`. + +**Регресс после дня оптимизации 11/15 (`d0ac4b1`, 2026-08-19), 2367 кадров:** + +| максимум по секции | эталон `mob-order-B-done` | сейчас | разница | +|---|---:|---:|---:| +| работа | 913 848 | **911 862** | −1 986 | +| синяя | 159 810 | **149 106** | −10 704 | +| зелёная | 440 418 | **436 494** | −3 924 | +| циан | 393 000 | **382 770** | −10 230 | + +Период: **3 растра в 2341 кадре, 4 в 23, 5 в 2** — как в эталоне. + +Почему сумма минусов по фазам не равна минусу по работе: максимумы разных +фаз достигаются В РАЗНЫХ КАДРАХ (пик синей — не тот кадр, где пик зелёной), +а «работа» здесь — максимум СУММЫ, а не сумма максимумов. + +Что из правок 11/15 сюда дошло: P16 и P2b дали синюю и циан (они про +проверки и луч видимости, а те работают в любой сцене), HEAL-WIDTH дал +зелёную (плита 64 → 58 на шести heal'ах кадра). + +**Зелёная по-прежнему выше растрового кадра** (436 494 против 430 000). +Главный оставшийся кандидат именно для этой сцены — **P9 (G8)**: при +падении плиты помечаются ДВА тайла, и соседний перезапекается целиком и +повторно (`draw_tile` соседа дважды на одну пометку), хотя потревожены у +него только левые 28 пикселей. При шести падающих плитах это умножается +на шесть. + +**ВАЖНО ДЛЯ ПРОЦЕССА.** Этот прогон вскрыл регрессию, которую не поймали +ни хост-тесты, ни сцена 11/15: гейт `loose_any` (позиция P5) не взводился +в `check_fall_flo`, и плиты уровня 13 дрожали, не падая. Сцену 13/23 надо +прогонять после КАЖДОЙ правки loose-механики, а не только когда меняешь её +сознательно. diff --git a/applications/SprPoP/docs/perf_registry.md b/applications/SprPoP/docs/perf_registry.md new file mode 100644 index 0000000..18fbbc5 --- /dev/null +++ b/applications/SprPoP/docs/perf_registry.md @@ -0,0 +1,871 @@ +# Реестр оптимизаций: всё отложенное, в одном списке + +Собрано 2026-08-19 из [`perf_green_phase.md`](perf_green_phase.md) (G1-G9), +[`perf_cyan_phase.md`](perf_cyan_phase.md) (C1-C7), +[`perf_backlog.md`](perf_backlog.md) (позиции 1-7), +[`../TASKS_OPEN.md`](TASKS_OPEN.md) (HEAL-WIDTH) и из +свежего разбора сцены [`perf_l11_room15.md`](perf_l11_room15.md). + +**База для процентов — работа кадра в 11/15: 801 768 тактов** (замер +`09f32ce`). Где эффект относится к другой сцене, это сказано явно. + +Оценки помечены: **[замер]** — измерено; **[модель]** — посчитано по +измеренным составляющим; **[гипотеза]** — не мерено, нужен прогон. + +--- + +## 1. Цена одного блита фона — разложена [замер 2026-08-19] + +Метод: брейкпоинты на резидентных адресах внутри `pop_blit_b` (0x5C50), +`temp0` на входе, разница `totalcycles` на каждом вызове. 1603 блита. + +| этап | такты | постоянство | +|---|---:|---| +| пролог + аргументы + грубый отсев | **810** | ровно, всегда | +| `atlas_image` | **672** | ровно, всегда | +| `gfx_w0_map` + чтение шапки ленты + арифметика клипа | **2 400** | ровно, всегда | +| ядро блита (пиксели) | 4 380 … 26 592 | по размеру кадра | +| `pop_cd_touch` | **2 069** (пакетный путь) / 4 115 (настоящая пометка) | почти ровно | +| `gfx_w0_unmap` + эпилог | **175** | ровно, всегда | +| **весь блит** | 10 458 … 32 718, медиана **16 674** | | + +**Фиксированная накладная = 6 126 тактов на любой блит, хоть 8×8.** +У самого дешёвого блита (10 458) это **59 % цены**; у пламени факела 16×18 +пиксели тянут ~1 700 из ~14 000, то есть **12 %**. + +Это и есть ответ на вопрос «почему маленький блит стоит 14 000». Причины +ровно те, о которых спрашивал пользователь: + +- **810 на пролог** — `call ___sdcc_enter_ix`, IX-фрейм и шесть чтений + `N(ix)`: третий и четвёртый аргументы (`int x`, `int ybottom`) идут + СТЕКОМ, каждое обращение 19 тактов Z80; +- **2 069 на `pop_cd_touch`** даже по пакетному пути, где вся работа — четыре + сравнения. Сигнатура `(int x, int y, int w, int h)` = 8 байт аргументов, + два из них через стек; `w`/`h` никогда не больше 64, `y` не больше 255, + то есть три из четырёх могли быть `uint8_t`; +- **2 400 на map + шапку** — `gfx_w0_map` (1 086) + `unmap` (264, платится в + конце) + ~1 000 на чтение четырёх байт заголовка и арифметику; +- **672 на `atlas_image`** — маппинг W3, чтение записи каталога, возврат W3; + размеры при этом читаются и выбрасываются (см. §3, C6). + +**Блитов за кадр в 11/15: 8** [замер] — все в зелёной фазе (6 на `draw_tile` +чомпера, 2 на пламя факелов). Синяя и циан через `pop_blit_b` не ходят +вовсе: heal и персонажи идут своими путями. Значит фиксированные накладные +блита стоят сцене **8 × 6 126 = 49 000 тактов/кадр (6,1 % работы)**. + +--- + +## 2. Модель зелёной фазы 11/15 [модель, сходится с 320 916 замера] + +| статья | такты | доля фазы | +|---|---:|---:| +| 8 блитов фона (из них 6 126×8 = 49 000 накладных) | 133 400 | 41 % | +| диспетчер `draw_tile` (один тайл чомпера) | 56 200 | 18 % | +| цикл `pop_process_trobs` без блитов пламени | 59 100 | 18 % | +| `pop_loose_tick` (плит в комнате НЕТ) | 28 872 | 9 % | +| heal 32×64 в `pop_chomp_redraw` | 34 000 | 11 % | +| шов / ворота соседа | 9 336 | 3 % | + +Больше половины фазы — не пиксели, а обвязка вокруг них. + +--- + +## 3. Список приёмов, отсортированный по эффекту + +### P1. Чомпер: перерисовка неизменной позы ✅ СДЕЛАНО 2026-08-19 — −110 802 + +**Диагноз, с которым позиция заводилась, оказался неполным.** Я приписал +190 260 тактов пометке от факела; зонд `pop_dbg_kind` показал, что все 312 +перерисовок прогона — вид `POP_RD_CHOMP` (полная), а пометку соседа она +просто перебивала. Настоящая причина нашлась сверкой с `animate_chomper` +(seg007:0448): оригинал перерисовывает чомпер **только при фазе < 6**, пять +кадров из пятнадцати, потому что с фазы 5 поза не меняется +(`chomper_fram1 = {3,2,0,1,4,3,3}`). Мы метили тайл каждый кадр. + +Сделано три вещи: условие фазы (с пометкой обеих страниц на фазе 5), новый +вид `POP_RD_CHOMP_ANIM` → `pop_chomp_anim_draw` (три блита поверх огня, порт +ветки `redraw_frames_anim`) и приоритет полной перерисовки над anim в +`pop_set_redraw`. Обе половины работают: замер даёт 40 % полных +перерисовок и 60 % лёгких. + +Работа 768 684 → **657 882** (медиана), зелёная 294 510 → **183 420**. +В 40 % кадров цена осталась прежней — там поза действительно меняется, и это +уже честная работа; резать её дальше только через P7 (раскол `draw_tile`) +или P8 (ширина heal). + +Полный разбор — [`perf_l11_room15.md`](perf_l11_room15.md) §6. + +
Исходная (неполная) постановка + +Разбор в [`perf_l11_room15.md`](perf_l11_room15.md) §3. Сейчас пометка от +факела обрабатывается как `heal 32×64 + полный draw_tile` (190 260 тактов на +единственный перерисованный тайл кадра); оригинал в этом случае рисует +ТОЛЬКО `draw_tile_anim` — графику чомпера поверх свежего пламени. + +Останется 1-2 блита челюстей ≈ 20 000-33 000. **Риск низкий**: это +сближение с оригиналом, а не отход от него. Побочно снимает широкую пометку +«фон трогали» вокруг тайла чомпера — см. P3. + +
+ +### P2. Синяя фаза разложена [ЗАМЕР 2026-08-19] — гипотеза не подтвердилась + +Замер зондами внутрь обеих половин синей (286 518 тактов): + +| участок | такты | доля работы | +|---|---:|---:| +| ввод + читы | 14 154 | 1,8 % | +| три спецсобытия уровней (skel / mouse / killed_shadow) | **1 650** | 0,2 % | +| `pop_frame_timers` + **луч видимости стража** | **37 032** | 4,8 % | +| `pop_ctrl_tick` | 18 648 | 2,4 % | +| `skip_mask` + **heal двух Char** | **67 734** | 8,8 % | +| `mirror_heal` + `fore_heal` | 2 040 | 0,3 % | +| `kid_tick` (play_seq) | 10 098 | 1,3 % | +| **`pop_phys_tick`** (физика Кида) | **61 266** | 8,0 % | +| `pop_guard_tick` (логика стража) | 19 932 | 2,6 % | +| **`pop_guard_phys_tick`** (физика стража) | **43 980** | 5,7 % | +| боёвка (`sword_hurting` / `sword_hurt` / `delta_hp`) | 9 558 | 1,2 % | +| `guard_fallout` + уход из комнаты | 366 | — | + +**Что оказалось не так, как ждали.** Я предполагал, что дорогие тут +банковые трамплины на спецсобытиях (по аналогии с `pop_clip_char_top`, +8 892 такта за трамплин ради одной проверки). Замер это отверг: три +спецсобытия уровней вместе стоят **1 650** — они гейтятся внутри и на +уровне 11 честно выходят сразу. + +**Настоящие статьи — три:** + +1. **Физика двух Char — 105 246** (61 266 + 43 980), при том что оба + персонажа СТОЯТ и кадр позы не меняется. Это 13,7 % работы кадра и + самая крупная статья синей. Нужен ещё один уровень разбора — внутрь + `pop_phys_tick` (позиция **P2a**, отдельным заходом). +2. **Луч видимости стража — до 37 032** (вместе с `pop_frame_timers`, но тот + заведомо копеечный: три счётчика). Считается КАЖДЫЙ кадр, хотя ни Кид, + ни страж не сдвинулись. Кандидат на гейт «пересчитывать только при + смене позиции или комнаты любого из двоих» — позиция **P2b**, + ожидание −30 000, риск низкий. +3. **heal двух Char — 67 734.** Отдельной правки не требует: он платится + ровно потому, что `skip_mask` никого не пропускает, и уйдёт вместе с + P1/P3. + +Новое, найдено 2026-08-19. + +### P15. Точность метки «фон трогали» ✅ СДЕЛАНО 2026-08-19 — −163 746 / −140 871 + +Постановка пользователя: не перерисовывать стража, пока он не двигается. + +**Две правки, и вторая оказалась решающей:** + +1. **метка**: вместо «маска колонок × три ряда по 63 px» — диапазон y на + каждую колонку (`ymin`/`ymax`, 40 байт на обе страницы). Прежняя + гранулярность склеивала пламя факела (y 33..50) с клинком стоящего + стража (y 59..65), между которыми девять пикселей зазора; +2. **проверка**: `cd_quiet` сверяет спрайт и накладной (клинок, брызги) + ДВУМЯ отдельными прямоугольниками вместо объединённого bbox. + Объединение включает пустой угол: спрайт стража в колонке 8, клинок + уходит в колонку 7 на y 59..65, пламя метит колонку 7 на y 33..50 — + и прямоугольник «спрайт + клинок» цеплял метку углом. + +**Без второй правки первая дала почти ноль** (632 676 против 628 542 до +неё) — это стоит помнить: точность структуры бесполезна, пока запрос к ней +остаётся грубым. + +| | лёгкая позиция | тяжёлая позиция | +|---|---:|---:| +| синяя | 259 050 → **223 902** | 257 520 → **245 808** | +| зелёная | 181 494 → 181 494 | 180 870 → 181 761 | +| циан | 194 262 → **59 406** | 319 842 → **192 090** | +| **работа** | 628 542 → **464 796** | 758 358 → **617 487** | + +В тяжёлой позиции вдобавок исчезли пятирастровые кадры (было 27 %). + +Проверено в MAME: статика чистая, динамика (пробежка, бой, переход в +соседнюю комнату) без хвостов и просвечивания; хост-тесты зелёные. + +Побочно исправлены два собственных дефекта первой редакции: обе страницы +обновлялись по условию, проверяющему только страницу 0 (после +`pop_cd_clear(0)` метка второй переставала расти), и отсутствовала явная +инициализация — пустая колонка обозначается `ymin = 255`, а нули от crt0 +читались бы как «затронута строка 0». + +### P16. Цианные проверки ✅ СДЕЛАНО 2026-08-19 — −25 818 + +Раскладка остатка цианной фазы (59 406) показала, что 47 883 из них — три +вызова, а не отрисовка: + +| вызов | было | стало | +|---|---:|---:| +| `pop_loose_mob_draw` | 978 | 978 (гейт `mobs_live` работает) | +| `guard_over_kid` | 14 424 | **0** | +| `pop_char_skip_mask` | 28 605 | ~21 000 | + +Три правки: + +1. **`cd_sig_same`** — сравнение снимка БЕЗ построения структуры. + `cd_sig_make` записывал тринадцать полей в стековый кадр (через + `-n(ix)`), и лишь потом шёл побайтовый цикл; теперь сравнение идёт прямо + с источником и выходит на первом расхождении. **−8 016**; +2. **`guard_over_kid` по условию** — вопрос «кто поверх кого» не имеет + смысла, когда не рисуется никто. Вызов перенесён ПОСЛЕ `skip_mask` и + идёт только при `skip != 3`. **−14 118**; +3. **`pop_cd_hit_slot`** — проверка «задет ли слот» брала пять аргументов, + три из них стеком (45 % тактов на IX). Теперь координаты берутся из + `pop_cd`, а сравнение вынесено в `hit_rect` с file-scope аргументами. + **−3 684**. + +**Отрицательный результат внутри третьей правки** (не повторять): первая +версия была обёрткой, которая внутри всё равно звала `pop_cd_hit` с пятью +аргументами — стало ХУЖЕ (1799 тактов Z80 вместо 1318). Снимать аргументы +со стека надо у того, кто их читает, а не этажом выше. + +### Отрицательные результаты 2026-08-19 — НЕ ПОВТОРЯТЬ + +Три попытки подряд сделали ХУЖЕ. Общая ошибка в двух из них — я оценивал +правку по СУММЕ ТАКТОВ ИНСТРУКЦИЙ в листинге, а не по реально исполняемому +пути. + +**1. `cd_sig_same` блоком вместо тринадцати сравнений.** Снимок был +переложен так, чтобы сравнивать непрерывные 10 байт начала `pop_char_t` +циклом `do { if (*a++ != *b++) return 0; } while (--i)`. По листингу +функция стала короче (1939 → 1290 тактов), а на машине **стало хуже: +438 978 → 450 426 (+11 448)**. + +Причина: сумма по листингу считает каждую инструкцию ОДИН раз, а тело +цикла исполняется ДЕСЯТЬ раз. Тринадцать линейных сравнений выполняются по +разу каждое и выходят раньше на первом же расхождении. **Урок: короткий +листинг ≠ быстрый код; цикл надо разворачивать в уме.** + +**2. `cd_touch_pb` — пометка «для блита» из file-scope.** `pop_cd_touch` +зовётся из `pop_blit_b` с четырьмя аргументами, хотя тот держит те же +значения в `pb_x`/`pb_top`/`pb_w`/`pb_h`. Специализированный вход без +аргументов дал **438 978 → 442 242 (+3 264)**. + +Причина: в зелёной фазе блиты идут ПАКЕТНЫМ путём (`draw_tile` открывает +`pop_cd_batch`), а там нужны все четыре значения сразу — и в регистрах +(`x`, `y` приходят в HL/DE) они дешевле, чем чтение из статиков. +**Снятие аргументов со стека помогает не всегда: если значение и так живёт +в регистре, статик его туда ещё и загружать заставит.** + +**3. Обёртка `pop_cd_hit_slot` поверх `pop_cd_hit`** (описана в P16): +внутри всё равно звала функцию с пятью аргументами и добавила свои — стало +1799 тактов вместо 1318. Помогло только когда сравнение переехало внутрь. + +### P14. Fore-проход персонажа — от 4 122 до 117 570 [замеры 2026-08-19] + +**Самая НЕСТАБИЛЬНАЯ статья кадра.** Замеры на одной и той же сцене: + +| ситуация | fore-проход | +|---|---:| +| персонаж пропущен (`skip`) | 4 122 | +| стоящий страж | 62 778 | +| живой Кид у чомпера | ~89 000 | +| **труп Кида в челюстях** | **117 570** | + +Растёт от двух вещей: ширины футпринта (широкий кадр смерти, вынутый меч +добавляет колонку) и числа тайлов с передним слоем внутри футпринта (здесь +чомпер со своими зубьями). Отсюда практический вывод: **в бою проход будет +ближе к сотне тысяч, чем к шестидесяти** — кадры выпадов и ударов широкие. + +Замер трупа сделан по просьбе пользователя. Сама по себе эта ситуация не +игровая («когда Кид — труп, игры нет»), но именно она показала верхнюю +границу цены. + +**РАЗБОР 2026-08-19: P14 сводится к P4.** Fore-проход Кида в тяжёлой +позиции (89 268) разложен зондами: + +| участок | такты | +|---|---:| +| вход + `pop_fore_set_clip` + `char_footprint` | 10 872 | +| арифметика границ окна | 3 786 | +| шов ворот + overlay-цикл | 3 294 | +| **цикл `fore_tile` по тайлам** | **67 854** (76 %) | +| `pop_gate_over_char` + хвост | 3 462 | + +А счётчик показал, что цикл обходит **всего 4 тайла**, и 3 из них реально +рисуют (`FORE_ANY != 0`). То есть 67 854 — это НЕ перебор лишних тайлов +(их четыре) и не проверки, а **цена самих блитов переднего слоя**: около +четырёх блитов по ~16 000, из которых 6 765 на каждом — фиксированная +накладная (см. §1). + +**Отсюда вывод для плана:** отдельной «оптимизации fore-прохода» почти нет. +Срезать там можно ровно три вещи, и только первая крупная: + +1. **цену блита (P4)** — 4 блита × 6 765 накладных = ~27 000 из 67 854; +2. `char_footprint` из физики (**P10**) — часть от 10 872; +3. слияние двух трамплинов в банк 2 — ~4 000. + +Иначе говоря, **P4 ускоряет и зелёную фазу (8 блитов), и fore-проход +(4 блита), то есть работает и в статике, и в динамике** — в отличие от +P14, который я считал самостоятельной позицией. + +`pop_char_fore` = два трамплина в банк 2 (`pop_fore_set_clip` + +`pop_fore_over_char`) плюс обход тайлов футпринта, в каждом `fore_tile`. +У Кида дороже, чем у стража, потому что в его футпринте лежит чомпер, а у +чомпера есть собственный передний слой (`POP_CHOMP_FRAM_FOR`), который +перерисовывается поверх персонажа каждый кадр. + +Что можно пробовать, по возрастанию радикальности: + +1. слить два трамплина в один вызов (мелочь, ~4 000); +2. **P10** — брать футпринт из физики, а не считать заново (−11 574 на + проход, то есть до −23 000 на двоих); +3. гейт по сигнатуре: пропускать проход, если не изменились ни кадр + персонажа, ни тайлы его футпринта. **Это расхождение с оригиналом** — + он рисует foretable безусловно; +4. **P13** — objtable и отложенные таблицы: у оригинала «посетить тайл» + стоит копейки именно потому, что таблицы только копят записи. + +### P3. Персонажи будятся каждый кадр ✅ ЧАСТИЧНО СБЫЛОСЬ + +**В лёгкой позиции — да:** после P1 `pop_char_draw(KID)` стоит 204 такта, +метка от чомпера до Кида больше не дотягивается. + +**В тяжёлой позиции — нет:** стоит Киду шагнуть на 7 пикселей вправо, и его +спрайт пересекается с тайлом чомпера и пламени, `skip_mask` перестаёт +пропускать, и он снова стоит ~144 000 (54 738 draw + ~89 000 fore). То +есть выигрыш P3 держится только пока персонаж не подошёл к анимированному +тайлу — а в игре он к нему подходит постоянно. + +Исходная оценка (−70 000) была: + +heal 141 048 + отрисовка 148 566 = 290 000 тактов (36 % работы) уходят на то, +что `pop_char_skip_mask` не может пропустить ни Кида, ни стража: фон трогают +каждый кадр. + +- **Кида** спасает P1: широкая пометка вокруг чомпера исчезнет; +- **стража спасти нельзя** — пламя правого факела (0,7) рисуется в ячейке + (0,8), где он и стоит. Там фон честно меняется, и оригинал персонажа тоже + перерисовывает. + +### P17. 16 бит там, где хватает 8 ✅ 2026-08-19 (замечание пользователя) + +**1. Границы экрана — беззнаковыми сравнениями.** Проверка «спрайт целиком +на экране» стояла как четыре ЗНАКОВЫХ 16-битных сравнения, а знаковое у +SDCC z80 разворачивается в `sbc` плюс `jp PO / xor 0x80 / jp P`. +Беззнаковая форма делает то же двумя: отрицательная координата становится +очень большой и проваливает условие так же, как `>= 0`. **−378.** + +**2. Габариты спрайтов в байтах.** `w`/`h`, `ow`/`oh`, `fpw`/`fph`, +`cw`/`ch` в `pop_cdraw_t`, параметры `cd_overlay_add`/`cd_clip_add`, локали +в `pop_char_draw`/`cd_splash` и чтение габарита из шапки ленты были +`uint16_t`, хотя спрайты атласов не крупнее 64×64 (memory +`pop_sprite_size_limits`). **−276 в статике, −1 134 в циане динамики**, +плюс 24 байта `_DATA`. + +### P6a. Кэш указателя модификаторов ✅ 2026-08-19 — −840 (ждали −20 000) + +`pop_trob_modif` объявлен `__banked`, а звался на КАЖДЫЙ trob внутри цикла +`pop_process_trobs`, хотя комната у них в подавляющем большинстве кадров +одна. Указатель теперь кэшируется между итерациями. + +Цикл trobs 78 726 → **74 964**, работа кадра 438 324 → **437 484**. + +**Оценка в реестре была завышена в двадцать раз**, и стоит понять почему: +я перенёс её по аналогии с лучом видимости (P2b), где трамплин звался +ДЕВЯТЬ раз за кадр. Здесь trob'ов в комнате всего несколько, и кэш +экономит два-три вызова. **Урок: «тот же паттерн» не означает «тот же +порядок величины» — считать надо число вызовов, а не узнавать шаблон.** + +### P6b. Кэш префетча кодов тайлов — НЕ ДЕЛАЛОСЬ + +Префетч (`pop_level_access_begin/end` плюс чтение кода на каждый trob) +стоит **11 058** за кадр. Кэшировать мешает инвалидация: код тайла меняет +`pop_level_set_tile` (кнопка → пол, loose → empty), вход в комнату и +добавление trob'а — пропустить хоть один источник значит получить +застывшую анимацию. С учётом того, что P6a дал 840 вместо 20 000, +ожидаемый выигрыш тут тоже стоит считать скромным, а риск он несёт +несоразмерный. + +### P10. Футпринт из физики — РАЗБОР 2026-08-19 (без реализации) + +Идея из `perf_backlog.md` §1: `redraw_at_char` (seg003:0430) берёт ГОТОВЫЕ +`char_col_left/right`, `char_top_row`, `char_bottom_row`, посчитанные в том +же кадре физикой (`set_char_collision`, seg006:0723), а наш +`char_footprint` (pop_bg.c) считает их заново внутри fore-прохода. + +**Разбор показал, что «просто передать» не получится: величины разные.** + +| | `char_footprint` (банк 2, fore) | `set_char_collision` (банк 3, физика) | +|---|---|---| +| ширина | габарит КАДРА `w` из атласа, `wh = (w+1)/2` | то же `fpw`, но затем **`FRAME_THIN` сдвигает края на ±4** | +| меч | расширяет диапазон на колонку (`sword >= DRAWN`) | не расширяет | +| колонки | `cLraw` до клампа (нужен для шва), затем кламп 0..9 | `coll_xl`/`coll_xr` в пикселях, колонки считает уже `calc_coll_window` | +| ряды | `rT`/`rB` от ВЕРХА и НИЗА спрайта, с форсом `rT = rB-1` | `Char.curr_row` — опорный ряд, это другое | + +То есть у оригинала обе задачи пользуются ОДНИМИ величинами, потому что он +считает их один раз в `set_char_collision`. У нас они исторически +разошлись: коллизии считают своё окно (с поправкой `FRAME_THIN`), fore — +своё (габарит кадра плюс колонка под меч). + +**Значит P10 — это не «передать готовое», а сперва СВЕСТИ обе величины к +одной, как в оригинале.** Работа не механическая: `FRAME_THIN` влияет на +коллизии осознанно (узкие кадры не должны цеплять стену), а fore-проходу +нужен полный габарит, иначе передние грани в крайней колонке не +перерисуются. + +**Чего не хватает для решения:** отдельного замера самого +`char_footprint`. Сейчас известно только «вход + `pop_fore_set_clip` + +`char_footprint` = 10 872», а оценка −11 574 в backlog взята из старого +замера другой сборки. Первым шагом нужен зонд между `set_clip` и +`char_footprint`. + +**Оценка приоритета:** низкая. Даже если `char_footprint` окажется всеми +10 872, он платится только когда персонаж рисуется (в статике fore-прохода +нет), а сведение двух геометрий к одной — это риск для коллизий, то есть +для физики, которая сейчас работает правильно. + +### P18. Метка «фон трогали» огрублена по X — ОТЛОЖЕНО (решение пользователя) + +**Найдено 2026-08-19 пользователем:** Кид перерисовывается, хотя с пламенем +не пересекается; на пиксель левее — перестаёт. + +Разбор по памяти машины. Кид `x = 156`, спрайт занимает **x 213..224**, +экранные y 43..83. Метка колонки 7 — y 33..50 (пламя правого факела). +Колонка считается как `x >> 5`, то есть по 32 пикселя, и спрайт достаёт до +224 — ровно первый пиксель колонки 7. По вертикали пересечение с меткой +настоящее (43..50), поэтому слот считается задетым. + +А по горизонтали пересечения НЕТ: пламя лежит в колонке 7 на x 232..247, +между ним и Кидом восемь пикселей зазора. На пиксель левее спрайт +кончается на 223, `223 >> 5 = 6`, колонка 7 не задета — и перерисовка +пропадает. + +То есть P15 исправил огрубление по Y и оставил его по X. + +**Почему отложено (аргументы пользователя):** + +- x лежит в 0..319 и в байт не влезает — нужен `uint16_t` на границу, то + есть 4 байта на колонку (80 байт на две страницы), и **16-битные + сравнения в горячем пути**. А они у SDCC z80 дороги ровно настолько, + что могут съесть весь выигрыш (см. отрицательные результаты выше); +- огрубить x вдвое (`x >> 1`, диапазон 0..159 влезает в байт) — это лишний + сдвиг и при записи, и при проверке, плюс точность падает до 2 пикселей. + +**Непроверенная идея на будущее:** хранить границы НЕ в экранных x, а как +смещение ВНУТРИ колонки (0..31, пять бит). Тогда байта хватает и сравнение +8-битное, но запись усложняется: прямоугольник, пересекающий несколько +колонок, даёт частичные диапазоны у крайних и полные у средних. + +**Когда браться:** если после других позиций бюджет всё ещё не сойдётся. +Выигрыш будет именно в пограничных положениях, а их в игре много — +персонаж почти всегда стоит рядом с чем-то анимированным. + +### P4. Накладные блита — ОТКАЧЕНО + +**Правка сделана и отменена по решению пользователя.** Критерий: если +выигрыш получен ценой сильно усложнённого кода — откатывать. + +Что было: `atlas_image_w0` в libbgi читал каталог из уже подключённой в W0 +страницы. **−408 на кадре** при ожидании −5 400. + +Почему откачено: цена — вторая публичная функция в API libbgi с НЕЯВНЫМ +контрактом («страница обязана быть подключена до вызова»), которую легко +вызвать неправильно и молча получить мусор, плюс дублирование чтения +каталога. 408 тактов — 0,09 % кадра, меньше разброса между прогонами. + +**Что осталось знанием:** сам `gfx_w0_map` стоит всего **324** такта, а 672 +у `atlas_image` — это почти целиком вызов функции и арифметика `idx * 8`. +Значит непробованная часть P4 («один map на группу блитов») имеет потолок +~2 600 за кадр, а не 10 000, как считалось. + +Ожидание было −5 400 (672 такта × 8 блитов зелёной фазы), и оно НЕ +оправдалось: цена блита 16 107 → 16 005, то есть −102. Причина в том, что +эти 672 — почти целиком вызов функции и арифметика `idx * 8`, а не само +переключение окна. Замер после правки показывает, что работа просто +переехала между статьями: + +| этап | до | после | +|---|---:|---:| +| пролог + отсев | 810 | 762 | +| `gfx_w0_map` | (в составе 2 400) | **324** | +| каталог + шапка ленты + клип | | **2 694** | +| ядро | 8 508 | 8 508 | +| `cd_touch` + `unmap` + эпилог | 2 883 | 2 883 | +| **фиксированная накладная** | **6 765** | **6 663** | + +Правка оставлена: не вредит, убирает лишнее переключение W3 и делает +контракт честнее (страница мапится один раз). Но как способ снять +накладные она не работает. + +**Что осталось непробованным** (и во что я теперь верю меньше): один +`gfx_w0_map` на ГРУППУ блитов — судя по замеру, сам map стоит 324, так что +потолок этой правки ~2 600 за кадр, а не 10 000, как считалось. + +
Исходная постановка (модель −28 000) + +| правка | на блит | источник | +|---|---:|---| +| `pop_cd_touch`: `uint8_t` вместо `int` для `y`/`w`/`h`, ранний выход пакетного пути | ~−1 300 | новое | +| один `gfx_w0_map`/`unmap` на ГРУППУ блитов | ~−1 350 | C5 / backlog §3 | +| размеры ленты из каталога, без `atlas_image` и чтения шапки | ~−670 | C6 / backlog §2 | +| `pop_blit_b`: аргументы в 8 бит, где хватает | ~−400 | новое | + +Все четыре — низкий риск, механическая работа. Вместе снимают ~3 700 из +6 126 фиксированных. + +
+ +### P5. `pop_loose_tick` при пустой комнате — 28 872 → 2 760 ✅ СДЕЛАНО 2026-08-19 + +**Получено −26 112 внутри функции, −33 840 на кадре** (замер до/после в +11/15). Оценка была −28 000. + +Раскладка холостого хода (замер зондами m9..m12) и что с ней стало: + +| участок | было | стало | +|---|---:|---:| +| два цикла по тайлам (30 + 10 позиций) | 9 852 | **132** | +| `pop_loose_mob_tick` (обход 14 слотов) | 12 090 | **996** | +| `check_loose_fall_on_kid` (трамплин + обход) | 5 868 | **546** | +| вход + хвост | 1 062 | 1 086 | +| **итого** | **28 872** | **2 760** | + +Сделано двумя гейтами: + +- `loose_any` (статик `pop_map.c`) — «идёт ли анимация плит». Ставится в + пяти местах записи ненулевой фазы, снимается САМИМ циклом по факту + прохода, где не осталось ни одной живой фазы; +- `pop_mob_busy` (резидент `pop_state.c`) — «занят ли хоть один слот + падающего куска» (`active` или дочистка `clean`). Ставит `mob_alloc`, + снимает обход по факту пустой таблицы. В резиденте, а не в `pop_room.c`, + потому что читает его `pop_map` из банка 3. + +**Важно про границу:** гейт отвечает не на «есть ли в комнате плиты», а на +«идёт ли анимация». У лежащей плиты-потолка фаза 0, и крутить нечего — +вопрос пользователя 2026-08-19. Асимметрия намеренная: ложная единица +стоит одного холостого прохода, ложный ноль — застывшей навсегда плиты, +поэтому взвод стоит рядом с КАЖДОЙ записью, а снятие только по факту. + +Покрытие: `phys_loose_floor_breaks` (взвод от шага и сотрясения) и новый +`phys_loose_gate_survives_room_change` — на пятое место взвода +(фаза восстановлена входом в комнату), которое не покрывал никто. +Мутационная проверка: со снятым взводом тест падает. + +### P6. `pop_process_trobs` разложен [ЗАМЕР 2026-08-19] — 89 784 + +| участок | такты | +|---|---:| +| вход + префетч кодов тайлов (маппинг окна 0) | **11 058** | +| цикл: два `pop_torch_draw` | ~36 000 | +| цикл: обход самих trob'ов | ~43 000 | + +Цена одного `pop_pot_b` (пламя факела) измерена отдельно, брейкпоинтами на +резидентных адресах: **17 346 тактов**, и это ЕДИНСТВЕННАЯ группа в +распределении — то есть `pop_pot_b` в кадре зовут только два факела. При +канвасе пламени 16×18 сами пиксели там 1 716, то есть **10 % цены**; всё +остальное — накладные (см. §1) плюс ~6 900 сверх `pop_blit_b` на самом +`pop_pot_b`. + +Направления: + +- **P6a**: `pop_trob_modif(room)` зовётся банковым вызовом на КАЖДЫЙ trob + внутри цикла, хотя комната у них одна и та же — вынести наружу; +- **P6b**: префетч 11 058 маппит окно 0 каждый кадр, а коды тайлов trob'ов + меняются редко — кэшировать с инвалидацией по смене тайла/комнаты; +- **P6c**: цена факела — это цена блита, то есть позиция P4. + +Новое, найдено 2026-08-19. + +### P7. G5. Раскол `draw_tile` на узкие части [оценка дока: −50 000 … −60 000] + +Диспетчер + контекст оплачиваются целиком всегда; у оригинала это девять +независимых функций. В 11/15 это те самые 56 200 на один тайл — но если +сделан P1, `draw_tile` чомпера вообще не вызывается, и здесь эффект пропадёт. +Ценность приёма — в ДРУГИХ сценах (13/23, любая комната с плитами). + +**Риск средний**: в `draw_tile` собрано много инвариантов (BUG-LOOSE-3, +BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1) — только отдельным заходом с прогоном +всех уровней. + +### P8. HEAL-WIDTH — ширина heal'ов по фактическому следу [оценка: 5-6 % цены heal'ов] + +ОБЯЗАТЕЛЬНАЯ по решению пользователя (2026-08-18). След плиты 58 px в +подземелье / 57 во дворце против используемых 60 и 64. +Постановка — `TASKS_OPEN.md`, якорь `heal-width`. + +### P9. G8 — пометку СОСЕДА ставить узкой полосой (28 px), а не тайлом [гипотеза] + +Парная к HEAL-WIDTH. Относится к сценам с падающими плитами (13/23), в 11/15 +не играет. Разбор — `perf_green_phase.md` §G8, там же три условия, из-за +которых это не «просто уменьшить число». + +### P10. Футпринт персонажа — из физики, а не считать заново [замер: −11 574 на fore-проход] + +`backlog` §1. С двумя персонажами — ~23 000 за кадр. Мешает то, что физика +(банк 3) держит `char_col_left/right` в статиках, а слой фона — банк 2. +**Риск средний**: окно fore-клипа заводилось под клинок и брызги. + +### P11. Мелочи с известной ценой [замер, `backlog` §7] + +| что | цена | где | +|---|---:|---| +| `pop_clip_char_top` — трамплин банк 4 → банк 3 ради одной проверки | 8 892 | `pop_cdraw.c` | +| `cd_sig_make` + возврат из `pop_char_draw` | 7 944 | `pop_cdraw.c` | +| `pop_loadkid` + расчёт координат кадра | 7 410 | `pop_cdraw.c` | +| `obj_x * 8 / 7` — последнее `__divsint` в горячем пути | ~2 400 | `pop_char_draw` | + +### P12. G9 — снять временную оснастку [замер: −6 000] + +`pop_dbg_b1..b6` в `pop_blit_b` (~400 на блит), `pop_dbg_kind`/`m16`, +`pop_dbg_m5..m15`, счётчик `rd_cnt` в `pop_redraw_needed`. +**Только ПОСЛЕ окончания оптимизации** — без них не мерить. + +### P13. Крупные рефакторинги — брать, только если понадобится ещё запас + +**Чем P13 НЕ является (вопрос пользователя 2026-08-19).** Это не «рисовать +комнату заново каждый кадр в скрытый буфер». Такой вариант исключён +арифметикой: 30 тайлов по 5-6 спрайтов при цене блита 16 674 (и 179 914 за +полную запечку одного тайла) дают порядка **3 000 000 тактов — семь +растровых кадров**. Оригинал так тоже не делает: у него та же +инкрементальная схема с пометками (`redraw_frames_full` / `_anim` / +`_fore`), перерисовываются только помеченные тайлы. + +Разница не в объёме отрисовки, а в цене ПОСЕЩЕНИЯ тайла: у нас +`fore_tile(r, c)` сразу блитит (со всеми 6 126 фиксированных накладных), а +у оригинала `add_backtable`/`add_midtable`/`add_foretable` только кладут +запись в массив, и рисует один `draw_table()` в конце. Плюс у него ОДИН +обход тайлов за кадр против наших трёх. + +- **C7 / backlog §5-6: objtable + отложенные таблицы back/mid/fore.** У + оригинала «посетить тайл» стоит копейки, потому что таблицы только копят + записи, а рисует один `draw_table()` в конце. У нас блит идёт сразу из + обхода, и fore-проход отдельный НА КАЖДОГО персонажа. +- **backlog §4: единый проход по тайлам вместо трёх** (`pop_redraw_needed`, + `pop_process_trobs`, `pop_fore_over_char`) и семь счётчиков причин + перерисовки вместо одного `kind`. +- **G6: меньше блитов в `RD_FLOOR`**, **G7: `mob_tick_one` в file-scope**. + +--- + +## 4. ПЛАН РАБОТ — состояние между сессиями + +Рабочий чеклист. Правило: одна позиция = один заход = один коммит с замером +до/после на сцене 11/15. Замер обязателен даже когда «очевидно» — из семи +закрытых позиций ТРИ дали не то, что ожидалось (P1 — вдвое меньше, P2a — +почти ничего, таблицы порогов — регресс). + +### Закрыто + +| # | что | факт | +|---|---|---| +| P15 | точность метки «фон трогали» + раздельная проверка клинка | **−163 746** лёгкая / **−140 871** тяжёлая | +| P16 | цианные проверки: снимок без структуры, `guard_over_kid` по условию, `hit_slot` без пяти аргументов | **−25 818** | +| P5 | `loose_tick`: гейты холостого хода | **−33 840** (ждали −28 000) | +| P1 | чомпер: перерисовка только при фазе < 6 | **−110 802** (ждали −160 000) | +| P2b | луч видимости: колонки + один банковый вызов | **−26 448** (ждали −30 000) | +| P2a | `coll_scan` в 8 бит + снят с IX | **−2 892** (крупной статьи в физике нет) | +| P3 | Кид перестал будиться каждый кадр | сбылось само после P1 — но только в ЛЁГКОЙ позиции | +| P2/P6 | замеры синей фазы и `process_trobs` | гипотеза «трамплины на спецсобытиях» отвергнута | +| — | замер цианной фазы | крупного лишнего в отрисовке персонажа нет | + +### Осталось, по убыванию ожидаемого эффекта + +| # | что | ожидание | риск | комментарий | +|---|---|---:|---|---| + +| P4 | накладные блита — 6 663 на КАЖДЫЙ блит | частично сделано: **−408** | низкий | из четырёх правок сработала слабо; разбор ниже | +| P14 | fore-проход персонажа | сводится к P4 + P10 | — | разбор ниже: цикл обходит всего 4 тайла | +| P11 | мелочи с известной ценой | −26 000 | низкий | `clip_char_top` 8 658 подтверждён замером | +| P10 | футпринт персонажа из физики | ? (нужен замер) | **высокий** | разбор ниже: величины физики и fore РАЗНЫЕ | +| P6a/P6b | `trob_modif` из цикла, кэш префетча | −20 000 | низкий | тот же паттерн трамплина в цикле | +| P7 | раскол `draw_tile` (G5) | −50 000 в 13/23 | средний | в 11/15 не играет | +| ~~P8~~ | HEAL-WIDTH | ✅ сделано: плита 64→58, чомпер 64→61 | — | эффект ждёт прогона 13/23 | +| P9 | G8 — пометка соседа полосой | не оценено | средний | для сцен с плитами | +| P13 | objtable + отложенные таблицы, единый проход по тайлам | не оценено | очень высокий | большой рефакторинг слоя фона | +| P12 | снять оснастку | −6 000 | нулевой | **последней**: без неё не мерить | + +### Текущее состояние бюджета + +| | работа | синяя | зелёная | циан | период | +|---|---:|---:|---:|---:|---:| +| до оптимизации | 801 768 | 293 238 | 320 916 | 187 758 | 4 растра | +| после P5 | 767 928 | 285 864 | 294 384 | 187 764 | 4 | +| после P1 (медиана) | 657 882 | 286 503 | 183 420 | 187 761 | 4 | +| после P2a | 654 990 | 283 215 | 183 798 | 187 812 | 4 | +| **после P2b (лёгкая позиция)** | **628 542** | 259 500 | 181 068 | 187 761 | 4 | +| ТЯЖЁЛАЯ позиция (Кид на шаг правее) | 758 358 | 257 520 | 180 870 | 319 842 | **4 и 5** | +| **после P15, лёгкая** | **464 796** | 223 902 | 181 494 | **59 406** | 4 | +| после P15, тяжёлая | 617 487 | 245 808 | 181 761 | 192 090 | **4 везде** | +| после P16, лёгкая | 438 978 | 218 052 | 181 494 | 39 438 | 4 | +| ~~после P4~~ | ~~438 570~~ | | | | правка **ОТКАЧЕНА** | +| после P17, лёгкая | 438 324 | 217 590 | 181 098 | 39 192 | 4 | +| **после P6a, лёгкая** | **437 484** | 217 704 | 180 252 | 39 306 | 4 | +| **после P17, тяжёлая** | **603 684** | 241 956 | 181 464 | 180 270 | 4 | + +Итог восьми позиций: **801 768 → 464 796 в лёгкой позиции (−42 %)** и +**758 358 → 617 487 в тяжёлой (−19 %)**. Отдельно важно: в тяжёлой позиции +исчезли пятирастровые кадры (было 27 %), период стал ровно 4 — рывки ушли. + +### Достижима ли цель — арифметика на 2026-08-19 + +Цель: работа ≤ 430 000, тогда период станет 3 растра (хвост кадра — три +`gfx_wait_vsync`). + +- в ЛЁГКОЙ позиции снять надо **7 484**; +- в ТЯЖЁЛОЙ — **173 684**. + +**Лёгких путей больше не осталось.** За 2026-08-19 отвергнуто ЧЕТЫРЕ +правки подряд (три с регрессом, одна почти без эффекта), и все они целили +в накладные проверок и блита. Фиксированная часть блита 6 663 держится +ядром `gfx_w0_map`/`cd_touch`/чтения шапки, а не «лишними» вызовами. + +Всё оставшееся в списке, кроме P13, даёт по оценкам **порядка 100 000** — и +это оптимистично. **Арифметика не сходится:** сцена с двумя персонажами, +чомпером и двумя факелами в три растра не укладывается без одного из трёх +решений: + +1. **P13** — переход на objtable и отложенные таблицы, как в оригинале + (единственный резерв нужного размера, но это переписывание слоя фона); +2. **осознанное расхождение с оригиналом** — например, не перерисовывать + передний слой персонажа, пока не изменились ни персонаж, ни тайлы под + ним (гейт по сигнатуре футпринта); +3. **принять 4 растра** как рабочий режим для сцен такой плотности и + выравнивать период, чтобы не было рывков 4/5. + +Решение за пользователем — это выбор между точностью порта и скоростью. + +### Как воспроизвести сцену (важно для следующей сессии) + +Сборка стартует прямо в ней: `make` (дефолты `LEVEL=11 ROOM=15 POS=2`) → +`make hdd` → **полный рестарт MAME** (`chdman -f` даёт новый inode, memory +`mame_hdd_rebuild_restart`) → в DSS набрать `d:` и `SprPoP`. Кид встаёт в +(0,2) лицом к чомперу, справа факел и страж — та самая сцена замеров. +Штатный старт уровня возвращается через `make ROOM=`. + +Проверка, что программа ЖИВА, обязательна перед любым чтением памяти: +`cur_room` (0x97AA) должен лежать в 1..24 — на этом уже был сорван один +замер (прочитаны два случайных байта остановленной машины). + +### Метод замера + +Зонды — `out (_io_border)` в `sprpop.c` (база модуля 0x42AD) плюс +резидентные пустышки `pop_dbg_m*` из `pop_state.c`. Адреса брать ЗАНОВО из +`.sprinter-cc-build/.sprinter-cc-sprpop/sprpop.map` после каждой пересборки. Скрипты +сессии: `perfrun.py <сек> tag=addr ...` и `parseseq.py <файл> ПОСЛЕД`. + +Цену отдельной РЕЗИДЕНТНОЙ функции можно снять вообще без пересборки: +`bpset <вход>,1,{temp0=totalcycles; g}` плюс `bpset <точка>,1,{printf "… +%d",totalcycles-temp0; g}`. Так разложен блит в §1. + +--- + +## 5. Сводка: что сколько даёт в 11/15 + +| # | приём | эффект | тип оценки | риск | +|---|---|---:|---|---| +| P1 | чомпер: только anim-слой | −160 000 | модель | низкий | +| P3 | Кид перестанет будиться | −70 000 | модель | следствие P1 | +| P2 | логика двух Char | −40 000 … −70 000 | гипотеза | ? | +| P4 | накладные блита (4 правки) | −28 000 | модель | низкий | +| P5 | `loose_tick` без плит | ✅ −33 840 | ФАКТ | сделано | +| P6 | цикл `process_trobs` | −20 000 … −40 000 | гипотеза | ? | +| P10 | футпринт из физики | −23 000 | замер | средний | +| P11 | мелочи (4 штуки) | −26 000 | замер | низкий | +| P12 | снять оснастку | −6 000 | замер | нулевой | +| P7 | раскол `draw_tile` | 0 здесь (−50 000 в 13/23) | оценка | средний | +| P8/P9 | HEAL-WIDTH / G8 | 0 здесь (сцены с плитами) | оценка | низкий/средний | + +Верхняя часть списка (P1 + P3 + P4 + P5) — **около −286 000 из 801 768, то +есть 36 % работы кадра**, и вся она низкого риска. Этого хватит, чтобы +сцена ушла с 1,86 растрового кадра до ~1,2 — но НЕ хватит, чтобы период +кадра упал с 4 растров до 3: для этого работа должна уложиться в 430 000, +то есть нужны ещё ~90 000 сверху (P2 или P6). + +--- + +## 5б. Цианная фаза разложена [ЗАМЕР 2026-08-19] + +Фаза 188 004 тактов, и она НЕ менялась ни от P1, ни от P5, ни от P2b. + +| участок | такты | +|---|---:| +| `check_mirror` | 3 198 | +| `loose_mob_draw` + `guard_over_kid` + `skip_mask` | **34 374** | +| **`pop_char_draw(KID)`** | **204** | +| **соперник: `char_draw` + `char_fore`** | **145 896** | +| `fore_needed` + `hp_draw` | 2 598 | +| `char_fore(KID)` + борта | 1 758 | + +**Кид уже пропускается** — 204 такта, то есть надежда P3 всё-таки сбылась +после P1: метка от чомпера до него больше не дотягивается. А страж +перерисовывается каждый кадр, и это ЧЕСТНО: пламя правого факела (0,7) +рисуется в ячейке (0,8), где он стоит, и реально накрывает ему голову +(пламя занимает y 5..22, страж 12..62). + +Отрисовка стража (148 302) по частям: + +| участок | такты | доля | +|---|---:|---:| +| **`pop_char_fore`** (два трамплина в банк 2 + обход тайлов) | **62 778** | 42 % | +| клинок: `pop_sword_draw` + `cd_overlay_add` + `cd_clip_add` | 27 522 | 19 % | +| блит спрайта + `clip_char_right` | 20 982 | 14 % | +| загрузка кадра и геометрия | 12 696 | 9 % | +| **`pop_clip_char_top`** (трамплин банк 4 → банк 3) | 8 658 | 6 % | +| снимок прямоугольника + `cd_clip_add` | 7 890 | 5 % | +| `gfx_w0_unmap` + `cd_sig_make` | 4 968 | 3 % | +| вход + `cd_heal` | 2 946 | 2 % | + +**Вывод: крупного лишнего здесь нет.** Единственная явно лишняя статья — +трамплин `clip_char_top` (8 658), и убрать его непросто: функции нужны +`get_tile` и таблицы деления из банка 3, а перенос в резидент вернёт тот же +трамплин внутрь. Всё остальное — работа, которую персонаж действительно +делает: рисует себя, клинок и передний слой поверх себя. + +## 6. Иерархия референсов (уточнена 2026-08-19) + +Сравнение трёх реализаций луча видимости показало, что источники не +равноценны, и это важно для ЛЮБОЙ будущей оптимизации: + +| источник | что берём | чего НЕ берём | +|---|---|---| +| **Apple II** (`Prince-of-Persia-Apple-II`) | как это делается на 8 битах: таблицы вместо делений, борьба за такты | ничего — но код на 6502, читать сложнее | +| **SDLPoP** | эталон ПОВЕДЕНИЯ (декомпиляция DOS-версии) | реализацию: она нарочно «расслаблена» под 32 бита | +| **mininim** | разбор краевых случаев, второе мнение о замысле | алгоритмы — переписан с нуля, механика местами своя | + +Доказательство на конкретном месте: `get_tile_div_mod` в SDLPoP содержит +комментарий + +```c +// DOS PoP does this: +// obj_xl = tile_mod_tbl[xpos]; +// return tile_div_tbl[xpos]; +``` + +а вместо этого делает `x % TILE_SIZEX` и `x / TILE_SIZEX`. Таблицы в файле +лежат, но нужны только для эмуляции чтения DOS-версии ЗА ГРАНИЦЕЙ массива. +Apple II (`CTRLSUBS.S`, `GETBLOCKX`) читает ровно `BlockTable[x]`. + +**Правило:** сверять поведение по SDLPoP, а реализацию под 8 бит — по +Apple II и по комментариям вида «DOS PoP does this» в самом SDLPoP. + +## 7. Повторяющийся источник цены: банковый трамплин в цикле + +Уже трижды крупнейшей статьёй оказывался не алгоритм, а вызов `__banked`- +функции ИЗ ЦИКЛА, идущего в другом банке: + +| место | цена | лечение | +|---|---:|---| +| луч видимости: `pop_tile_at` по колонке (P2b) | 36 786 → 13 002 | один вызов на весь отрезок | +| `pop_clip_char_top` — банк 4 → банк 3 ради одной проверки | 8 892 | не сделано (P11) | +| `pop_trob_modif(room)` на каждый trob в цикле | не мерено | не сделано (P6a) | + +**Что проверять в первую очередь при новом «дорогом» месте:** не сколько +там арифметики, а сколько раз за кадр пересекается граница банка. + +## Фиксированный логический кадр (2026-08-19) — МЕНЯЕТ ВСЕ ЦЕЛЕВЫЕ ЧИСЛА + +Период логического кадра больше не `ceil(W) + 2`, а `max(n, ceil(W))` +(`src/pop_pace.c`, разбор — `frame_pacing_plan.md`). Поэтому: + +- **Бюджет кадра вырос с 430 080 до 1 290 240 тактов** (n = 3, режим + FASTEST по умолчанию). Все записи этого реестра, где «работа сверх + 430 000 стоит сразу целого растра», СЧИТАТЬ УСТАРЕВШИМИ. +- 13/23 (максимум работы 911 862) теперь укладывается в период 3 растра — + проверено, ни одного кадра длиннее. Прежний профиль был 3/4/5. +- Оптимизация из спешной стала плановой: смысл резать такты остался + (режим NORMAL при n=4 и бой при n=5 дают ещё больше запаса, а FASTEST — + верхнюю планку скорости), но «свалиться за растр» больше не обрыв. +- Цена самого пейсинга — ≈4 000 тактов на кадр (0,9 %), замерено A/B. + +Приоритет P9 (G8) и остальных позиций от этого не меняется, но их +СРОЧНОСТЬ падает: они больше не спасают от скачка периода. diff --git a/applications/SprPoP/docs/prng_alternatives.md b/applications/SprPoP/docs/prng_alternatives.md new file mode 100644 index 0000000..b5ed432 --- /dev/null +++ b/applications/SprPoP/docs/prng_alternatives.md @@ -0,0 +1,148 @@ +# Генераторы псевдослучайных чисел: запасные варианты + +Что сейчас стоит в порте, какие есть альтернативы и сколько на них реально +можно выиграть. Заготовка на случай, если упрёмся в бюджет кадра — +**сейчас менять ничего не нужно**. + +## Что стоит сейчас + +`pop_geom.c`, ветка `POP_PRANDOM_EXACT=1` (по умолчанию) — LCG оригинала +`s = s*214013 + 2531011`, шаг написан на Z80-ассемблере (единственное такое +место в порте). Схема Горнера по разреженной записи константы: + +``` +214013 = ((((1<<1)+1)<<2 + 1)<<4 + 1)<<10 - 3 +``` + +17 удвоений, три сложения, одно вычитание; величина `3*s`, нужная в конце, +попадается по дороге на втором шаге. Тело — **≈1 020 тактов** по статическому +подсчёту. Бит-в-бит совместим с SDLPoP, поэтому по картинке можно сверяться +с эталоном. + +Вторая ветка, `POP_PRANDOM_EXACT=0` — xorshift16 + шаг Вейля на C. +Совместимость теряется. + +Замер в MAME, комната 3, 175 кадров (медиана кадра): + +| вариант | кадр | prandom → torch_draw | +|---|---|---| +| C, бит-в-бит (16-битные половины) | 403 632 | 10 933 | +| C, xorshift16 + Вейль | 397 986 | 7 927 | +| **asm, бит-в-бит (сейчас)** | **400 800** | **9 331** | + +## Вариант A — комбинированный LFSR + LCG, ~148 тактов + +Период > 4 млрд (lcm(65536, 65535) ≈ 4.29e9), младшие биты не вырождены. + +```z80 +prng16: + seed1=$+1 + ld hl, 9999 + ld b, h + ld c, l + add hl, hl + add hl, hl + inc l + add hl, bc + ld (seed1), hl + seed2=$+1 + ld hl, 987 + add hl, hl + sbc a, a + and 101101b + xor l + ld l, a + ld (seed2), hl + add hl, bc + ret +``` + +Устройство: `seed1` — LCG `x = 5x + 1` (по модулю 2^16; `inc l` вместо +`inc hl` — экономия байта, на период не влияет). `seed2` — 16-битный +LFSR Галуа: сдвиг влево, и если выехала единица, XOR младшего байта с маской +`0x2D` (примитивный многочлен `x^16 + x^5 + x^3 + x^2 + 1`). На выходе +сумма обоих состояний — она и разрушает регулярность младших бит LCG. + +**Что мешает взять как есть:** сиды зашиты в код (SMC), а нам нужны ДВЕ +независимые последовательности — раскладка кладки и анимация тайлов. +Пришлось бы передавать состояние через указатель, как сейчас у `pop_prandom` +(это +20…40 тактов, не принципиально). + +## Вариант B — xorshift(7,9,8), ~86 тактов + +Самый быстрый, период 65535. + +```z80 +xrnd: + ld hl, 1 ; seed must not be 0 + ld a, h + rra + ld a, l + rra + xor h + ld h, a + ld a, l + rra + ld a, h + rra + xor l + ld l, a + xor h + ld h, a + ld (xrnd+1), hl + ret +``` + +**Две оговорки.** Ноль — неподвижная точка, а сид раскладки кладки у нас +считается как `номер комнаты + смещение ряда + колонка` и вполне может +оказаться нулём: нужен либо guard, либо шаг Вейля поверх. И тот же SMC-сид, +что в варианте A. + +## Чего НЕ брать: RND из Apple II + +Оригинальный `Prince-of-Persia-Apple-II`: + +``` +RNDseed := (5 * RNDseed + 23) mod 256 +``` + +```asm +RND + lda RNDseed + asl + asl + clc + adc RNDseed + clc + adc #23 + sta RNDseed + rts +``` + +Полный период 256 (`a ≡ 1 mod 4`, `c` нечётное), и для своего движка он +работал. Нам не годится: у LCG по модулю 256 младшие биты вырождены — бит 0 +просто чередуется. Наши вызовы это увидят: раскладка кладки берёт +`prandom(1)` (ОДИН бит) и `prandom(4)`, то есть вместо шума получилась бы +аккуратная шахматка. + +## Сколько реально можно выиграть + +Меньше, чем кажется по числам 86/148 против 1 020. Тело генератора — уже не +весь расход: остаются обёртка `pop_prandom`, приведение к диапазону +`pop_rnd_fit` и ABI вызова. Верхняя граница выигрыша видна из замера выше: +между нынешним asm-LCG и самым дешёвым из проверенных вариантов разница +**2 814 тактов за кадр (0.65 %)** при двух вызовах за кадр, и это ПОТОЛОК — +любой из вариантов A/B ниже него не опустится. + +Порядок действий, если понадобится: + +1. Сначала убрать обёртки: слить `pop_rnd_fit` в ту же asm-процедуру, чтобы + на вызов приходился один `call`, а не три. Это ничего не ломает и не + трогает совместимость с эталоном. +2. И только если этого мало — менять генератор, начиная с варианта A + (качество последовательности у него не хуже LCG, в отличие от B). + +Важно помнить: число вызовов вырастет с боёвкой. Сейчас их два за кадр +(факелы), а `guard_advance` / `guard_block` / `guard_strike` дёргают +`prandom(255)` каждый по разу за кадр боя — то есть при драке станет 5–6, и +цена вопроса вырастет во столько же раз. diff --git a/applications/SprPoP/docs/quicksave_plan.md b/applications/SprPoP/docs/quicksave_plan.md new file mode 100644 index 0000000..a5b671c --- /dev/null +++ b/applications/SprPoP/docs/quicksave_plan.md @@ -0,0 +1,328 @@ +# QuickSave / QuickLoad — разбор оригинала и план реализации + +Статус: **РЕАЛИЗОВАНО и проверено в MAME** (2026-08-22; F6/F9, POP.SAV + +POP.BAK — см. коммит `v0.6-pop-quicksave`). Документ оставлен как +справочник по формату снимка и разбору. Задача на доске — +[`../TASKS_OPEN.md#qsave`](TASKS_OPEN.md#qsave). + +--- + +## 0. Важная оговорка об «оригинале» + +**В оригинальном PoP 1989 года (DOS/Apple II) QuickSave/QuickLoad НЕТ.** +Там вообще нет сохранения посреди уровня: игра рассчитана на один заход в +60 минут, а «продолжение» — это только пароль/чекпоинт уровня 7. Поэтому +`Prince-of-Persia-Apple-II/` и `MSDOS/` тут не источники — искать в них +нечего. + +Источник истины — **SDLPoP**, где быстрое сохранение добавлено как +enhancement: `seg000.c`, блок `#ifdef USE_QUICKSAVE`, клавиши **F6** (save) +и **F9** (load). Ниже разобран именно он. Это значит, что правило +«расхождение с SDLPoP = баг у нас» здесь работает мягче: мы не обязаны +повторять его байт-в-байт, но обязаны повторить его **устройство**, потому +что оно решает ровно те проблемы, которые возникнут и у нас. + +--- + +## 1. Как это устроено в SDLPoP + +### 1.1 Точка вызова — отдельная фаза кадра, не обработчик клавиши + +Клавиша только взводит флаг (`need_quick_save` / `need_quick_load`, +`seg000.c:558`), а вся работа делается в `check_quick_op()` — она вызывается +из главного цикла **между кадрами**, когда движок в согласованном состоянии. +Это принципиально: загрузка посреди тика переписала бы `Char` под ногами у +`play_seq`. + +Отказ штатный, не фатальный: `quick_save()`/`quick_load()` возвращают +успех/неуспех, и игра печатает `QUICKSAVE` / `NO QUICKLOAD` внизу экрана и +продолжается. + +### 1.2 Формат — плоская последовательность переменных, без структуры + +```c +#define process(x) ok = ok && process_func(&(x), sizeof(x)) +``` + +Один макрос и один и тот же список обходится **и на запись, и на чтение** +(`quick_process(process_save)` / `quick_process(process_load)`). Поля +пишутся встык, без имён и тегов; совместимость держится ровно одним +средством — **строкой версии в начале файла**: + +```c +const char quick_version[] = "V1.16b4 "; +``` + +При загрузке она сравнивается, и при несовпадении файл просто отвергается +(`quick_load`, возврат 0). То есть формат нарочно хрупкий и нарочно +одноразовый — это снимок конкретной сборки, а не сейв-формат. + +**Это стоит перенять целиком.** Мы платим за версионирование одним байтом +и получаем право менять состав снимка при каждой правке движка. + +### 1.3 Что именно сохраняется + +Полный список — `quick_process`, `seg000.c:257-366`. По смыслу он делится +на пять групп: + +| группа | поля | +|---|---| +| уровень | `level` (2305 Б целиком), `checkpoint`, `upside_down`, `drawn_room`, `current_level`, `next_level`, `leveldoor_open` | +| анимируемые объекты | `mobs_count`, `mobs[14]`, `trobs_count`, `trobs[30]` | +| Кид | `Kid`, `hitp_curr/max/beg_lev`, `grab_timer`, `holding_sword`, `united_with_shadow`, `have_sword`, `kid_sword_strike`, `pickup_obj_type`, `offguard` | +| соперник | `Guard`, `Char`, `Opp`, `guardhp_curr/max`, `demo_index`, `demo_time`, `curr_guard_color`, `guard_notice_timer`, `guard_skill`, `shadow_initialized`, `guard_refrac`, `justblocked`, `droppedout`, `is_guard_notice`, `can_guard_see_kid` | +| прочее | кэш коллизии (`*_row_coll_room/flags`, `prev_collision_row`), вспышка (`flash_color/time`), звук (`is_screaming`, `is_feather_fall`, …), **`random_seed`**, время (`rem_min`, `rem_tick`), весь блок управления (`control_*`, `ctrl1_*`) | + +Два наблюдения, важные для нас: + +1. **Состояние ОТРИСОВКИ не сохраняется вообще.** Ни экранных буферов, ни + пометок перерисовки, ни того, что уже нарисовано. Вместо этого при + загрузке комната перерисовывается с нуля. Это резко упрощает задачу и + ровно то, что нам нужно при дабл-буфере. +2. **`random_seed` сохраняется.** Без него загрузка не воспроизводима: + после неё факелы, чомперы и `prandom` в боёвке пойдут иначе. + +### 1.4 Что делается при загрузке + +`restore_room_after_quick_load()` (`seg000.c:395`) — это и есть вся +«сложность» операции: + +- `load_lev_spr(current_level)` — **перезагрузка графики уровня** (тайлсет + мог смениться: подземелье/дворец); +- `different_room = 1`, `next_room = drawn_room = Kid.room` — принудительно + «мы в другой комнате», чтобы движок перерисовал всё; +- `load_room_links()` — связи комнат заново; +- `draw_game_frame()` — отрисовать кадр (важно для состояния падения); +- `hitp_delta = guardhp_delta = 1` — принудительный редрой полос HP; +- если `Guard.room != drawn_room` — стража «выключить» (`direction = + dir_56_none`, `guardhp_curr = 0`), как в `clear_char()`; +- `loadkid_and_opp()` — восстановить окно `Char`/`Opp`; +- сбросить таймеры текста и `exit_room_timer`. + +Плюс визуальный приём: перед загрузкой экран заливается чёрным на 5 тиков — +чтобы переход читался глазом и не выглядел «дёрганием». + +### 1.5 Чего в SDLPoP решили НЕ восстанавливать + +- звуки — просто `stop_sounds()`; +- перо (`is_feather_fall`) — без фикса `fix_quicksave_during_feather` + сохранение под пером запрещено вовсе, а при загрузке эффект гасится; +- есть опциональный **штраф**: `USE_QUICKLOAD_PENALTY` отнимает минуту + игрового времени за квиклоад. Нам не нужен (у нас пока нет игрового + таймера). + +--- + +## 2. Чем наша архитектура отличается + +| | SDLPoP | у нас | следствие для задачи | +|---|---|---|---| +| уровень в памяти | `level_type` в ОЗУ, 2305 Б, мутабельный | EMM-страница (`pop_lvl_page`), плюс рабочая копия комнаты в W2 | снимок читает страницу через W0-маппинг, а не `memcpy` | +| модификаторы тайлов | внутри `level.bg` | отдельный `room_modif[24][30]` в `pop_trob.c` (**static**) | нужен экспортируемый сериализатор из банка 6 | +| код | один бинарник | 8 банков + резидент | сериализатор обязан жить там же, где данные, и зваться через трамплин | +| экран | один буфер | **дабл-буфер**, у каждой страницы своя теневая копия | после загрузки перерисовать ОБЕ страницы, иначе через кадр мелькнёт старое | +| ОЗУ | сколько угодно | куча 2969 Б, стек 1279 Б | буфер снимка целиком в ОЗУ не положить — писать потоком | +| диск | `fopen` | DSS: 8 манипуляторов, 9-й ВЕШАЕТ систему ([[dss_fd_limit]]) | закрывать файл гарантированно, гард уже есть в libc | +| ГСЧ | один `random_seed` | **три** независимых: `pop_t_seed`, `trob_seed`, `pop_fight_seed` | сохранять все три, иначе загрузка невоспроизводима | + +--- + +## 3. Инвентаризация нашего состояния + +Собрано по `.sprinter-cc-build/.sprinter-cc-sprpop/sprpop.map` (данные всех модулей, включая +банковые, лежат в W2 — банк влияет только на код). Отмечено, что глобально +(видно снаружи), а что `static` и требует аксессора. + +### 3.1 Мутабельные данные уровня + +| что | где | размер | доступ | +|---|---|---|---| +| тайлы `fg` (провалившиеся плиты, открытые двери, съеденные предметы) | EMM-страница уровня | 720 Б | `pop_level_set_tile` пишет; чтения наружу нет — **нужен аксессор** | +| `room_modif[24][30]` | `pop_trob.c`, static | 720 Б | **нужен сериализатор** (банк 6) | +| `room_seen[24]` | `pop_trob.c`, static | 24 Б | там же | +| `trobs[30]` + `trobs_count` | `pop_trob.c`, static | 91 Б | там же | +| `trob_seed` | `pop_trob.c`, static | 4 Б | там же | +| `mobs[14]` | `pop_room.c`, **глобален** | 210 Б | напрямую | +| `mobs_live` | `pop_room.c`, static | 1 Б | аксессор | + +### 3.2 Персонажи и бой + +`Kid`, `Char`, `Opp` (`pop_kid.c`), `Guard` (`pop_guard.c`) — по 16 Б, +все глобальные. Рядом: `hitp_curr/max/beg_lev/delta`, `guardhp_curr/max/delta`, +`guard_skill`, `guard_refrac`, `justblocked`, `kid_sword_strike`, `offguard`, +`holding_sword`, `can_guard_see_kid`, `is_guard_notice`, +`pop_guard_notice_timer`, `pop_guard_hurt`, `pop_united_shadow`, +`pop_shadow_init`, `pop_fight_seed`, `knock`. + +### 3.3 Прогресс и физика + +`pop_current_level`, `pop_next_level`, `pop_checkpoint`, `pop_have_sword`, +`pop_item_taken`, `pop_leveldoor_open`, `pop_leveldoor_right`, +`pop_leveldoor_ybottom`, `pop_kid_dead`, `pop_kid_hurt`, `pop_feather`, +`pop_upside` / `pop_upside_want`, `pop_flash_time` / `pop_flash_color`, +`pop_droppedout`, `pop_fell_out`, `pop_leave_dir`, `pop_leave_timer`, +`pop_loose_*`, `pop_ceil_modif`, `pop_ceil_fell`, `pop_debris_at`, +`pop_seamless`, `pop_jumped_mirror`. + +### 3.4 Ввод + +`control_x/y/shift/forward/backward/up/down/shift2` (`pop_state.c`) — как в +SDLPoP, сохраняются. + +### 3.5 Что НЕ сохранять (восстанавливается перерисовкой) + +`room_fg/room_bg`, `lcol_*`/`rcol_*`/`below_fg`/`above_*`, `seam_*`, +`cur_room`, `pop_t_*` (весь кэш слоя фона, окна клипа, `pop_cd_*`), +`trob_drawn`, `mob_spr`, слоты `pop_cd`, метки `pop_redraw`, запечки +(`bake_pg`). Всё это — производное; после загрузки оно обязано быть +сброшено и пересчитано, а не восстановлено. + +**Оценка объёма снимка: ≈ 1,9 КБ** (720 + 720 + 210 + 91 + 64 + ~60 +скаляров + запас). + +--- + +## 4. Куда писать снимок: HDD-файл, а не EMM-страница + +Решение: **один основной слот `POP.SAV` на HDD; предыдущая +валидная запись хранится в `POP.BAK`.** EMM-слота нет: программа +работает только с HDD, а главный сценарий QuickSave обязан переживать +перезапуск игры. + +> Пересмотрено 2026-08-21 по вопросу пользователя «почему EMM, а не файл». +> Первая редакция плана рекомендовала EMM — это была ошибка: она взвешивала +> скорость и недооценивала главный сценарий использования. Разбор оставлен +> целиком, потому что довод переносится и на другие «положить в память +> вместо диска» решения. + +**Решающий довод: EMM-страница не переживает рестарт программы,** а именно +рестарт — тот случай, ради которого QuickSave и нужен. Пример из этого же +проекта: сцену каскада плит на 13/23 воспроизводит ТОЛЬКО `ESC` → запуск +заново ([`perf_l13_room23.md`](perf_l13_room23.md) §1, где перечислено, +почему не годятся ни возврат в комнату, ни рестарт уровня, ни запись +состояния отладчиком). Тем более снимок в ОЗУ не переживает перезапуск +MAME, обязательный после каждой пересборки образа. + +| сценарий | EMM | файл | +|---|---|---| +| «переиграть это место ещё раз» | работает, мгновенно | работает, на HDD быстро | +| «вернуться к багу после рестарта» | **не работает** | **работает** | + +Второй сценарий не закрывается ничем другим; первый закрывается обоими, и +разница в скорости там некритична — 1,9 КБ на HDD ([[mame_hdd_test_disk]] — +быстрый путь против дискеты) не заметны на фоне полной перерисовки комнаты, +которая при загрузке делается в любом случае и стоит дороже. + +Доводы за EMM, которые при перепроверке оказались слабыми: лимит +манипуляторов DSS ни при чём (открываем и закрываем ровно один файл, гард +`_fd_guard` в libc и так стоит), а «не нужен путь и права» — экономия одной +строки. + +Обход состояния всё равно писать с абстракцией чтения/записи, как у SDLPoP +через `process_func`, но второй EMM-слот в scope не входит. + +**Проверить ДО кодинга:** пишется ли `test_hdd.chd` из-под MAME. Если образ +только на чтение, файловый путь упрётся в это на первом же шаге и порядок +работ придётся менять. Проверка дешёвая — записать пробный файл на `D:` из +SprPoP. + +--- + +## 5. Формат снимка + +``` ++0 "PQS1" 4 Б магия ++4 версия сборки 1 Б (инкремент при ЛЮБОМ изменении состава) ++5 pop_current_level 1 Б ++6 длина полезной части 2 Б (контроль, что обход совпал) ++8 ... поля встык, ОДИН порядок на запись и на чтение ... +.. checksum 2 Б (заголовок + payload) +``` + +Версия проверяется первой; несовпадение — отказ, как в SDLPoP. Никаких +тегов и выравнивания: снимок одноразовый и живёт ровно одну сборку. + +Обход — один список и один макрос, как `process(x)`: + +```c +static void qs_walk(qs_io_t io) /* io = запись или чтение */ +{ + QS(pop_current_level); QS(pop_checkpoint); ... +} +``` + +Так состав нельзя рассинхронизировать между сохранением и загрузкой — +единственная реальная опасность плоского формата. + +--- + +## 6. Что делать при загрузке (наш аналог `restore_room_after_quick_load`) + +Порядок важен, каждый пункт закрывает конкретный отказ: + +1. **Сменился уровень?** → `pop_level_load_num()`, `pop_bg_load(tileset)`, + атласы стража по типу. Это дорого, но ровно тот же путь, что при + переходе уровня (`pop_level_switch`), — переиспользовать его, а не писать + заново. +2. Залить экран чёрным (приём SDLPoP: переход должен читаться глазом). +3. Восстановить состояние обходом `qs_walk`. +4. **Сбросить всё производное:** `pop_trob_reset` (но НЕ трогая + восстановленные `room_modif`/`trobs` — нужен отдельный «мягкий» сброс, + только `trob_drawn` + метки), `pop_redraw_reset`, слоты `pop_cd`, + `pop_bake_forget`, `pop_cd_clear`, сигнатуры пропуска перерисовки. +5. `pop_room_load(Kid.room)` — рабочая копия комнаты и срезы соседей. +6. **Полная отрисовка комнаты в ОБЕ страницы дабл-буфера.** Это наше + главное отличие от SDLPoP: одной перерисовки мало, вторая страница + останется со старой картинкой и мигнёт через кадр. +7. Принудительный редрой полос HP (`hitp_delta = guardhp_delta = 1`). +8. Если `Guard.room != Kid.room` — выключить стража + (`Guard.direction = DIR_56_NONE`, `guardhp_curr = 0`), как `clear_char`. +9. `pop_loadkid_and_opp()` — согласовать окно `Char`/`Opp`. + +--- + +## 7. Разбиение на шаги + +| шаг | что | критерий готовности | +|---|---|---| +| **QS0** | Проверить, что `D:` пишется из-под MAME (пробный файл из SprPoP) | файл создался и читается обратно после рестарта программы | +| **QS1** | Аксессоры/сериализаторы для `static`-состояния банковых модулей: `pop_trob.c` (`room_modif`, `room_seen`, `trobs`, `trob_seed`), `pop_room.c` (`mobs_live`), страница уровня (чтение `fg`) | хост-тест `tests-host/t_qsave.c`: обход туда-обратно на синтетическом состоянии даёт байт-в-байт исходное | +| **QS2** | Ядро: `qs_walk` + `POP.SAV`, магия/версия/checksum, безопасная замена с предыдущей валидной копией в `POP.BAK` | сохранение и загрузка **в той же комнате, без движения**; порча SAV не портит BAK | +| **QS3** | Восстановление отрисовки (§6), включая обе страницы дабл-буфера | загрузка после перехода в другую комнату; нет мерцания через кадр | +| **QS4** | Клавиши **F6/F9** (или свободные из `pop_cheat.h`) через ``, флаги `need_quick_save/load`, обработка **между кадрами** | загрузка посреди боя/падения не ломает `play_seq` | +| **QS5** | Загрузка с **другого уровня** (перезагрузка уровня и атласов) | сохранить на ур. 2, уйти на ур. 12, загрузить — тайлсет и стражи верные | + +Порядок не переставлять: QS0 первым (он может изменить весь план), QS3 без +QS2 нечего проверять, а QS5 обязан идти после QS3 — иначе смена тайлсета +замаскирует ошибки восстановления. + +**Главный критерий приёмки всей задачи:** сохранить состояние, выйти по +`ESC`, запустить SprPoP заново, загрузить — и оказаться там же. Именно +этого сценария сейчас нет ничем, и ради него задача и делается. + +--- + +## 8. Риски и открытые вопросы + +1. **`static` в банковых модулях.** Их нет в карте символов, то есть + отладчиком снимок не проверить. Возможно, стоит сделать `room_modif` и + `trobs` НЕ-static — так же, как уже сделано с `mobs` в `pop_room.c` и + ровно по той же мотивации (там это записано прямым комментарием). +2. **Место в банке 6.** `pop_trob` занимает 3802/16384 — запас есть, но + сериализатор лучше писать компактным обходом, а не 30 отдельными + вызовами. +3. **Три ГСЧ.** Проверить, что сохранены ВСЕ: пропуск любого даст + «загрузилось, но играется иначе» — самый неприятный класс бага, потому + что выглядит как случайность. +4. **Согласованность `Char` и `Kid`.** У нас окно `Char` — отдельная копия; + если сохранить их рассогласованными (снимок посреди тика), загрузка + воскресит рассогласование. Отсюда требование QS4: только между кадрами. + Урок свежий — ровно на этом стыке жил + [BUG-CHEAT-IMM-1](BUGS_CLOSED.md#bug-cheat-imm-1). +5. **Дабл-буфер.** Самый вероятный источник «почти работает»: забыть вторую + страницу. Симптом — мерцание через кадр + (см. `SprPoP/CLAUDE.md`, раздел про дабл-буфер). +6. **Транзакция SAV/BAK.** До кодинга проверить на DSS семантику + rename/replace. Если атомарная замена не гарантирована, писать через + `POP.NEW`, проверять его после close и не удалять единственную валидную копию + до завершения новой. diff --git a/applications/SprPoP/docs/resident_budget.md b/applications/SprPoP/docs/resident_budget.md new file mode 100644 index 0000000..c0f60fb --- /dev/null +++ b/applications/SprPoP/docs/resident_budget.md @@ -0,0 +1,111 @@ +# Бюджет резидента W1/W2: как мерить и как освобождать + +Резидент huge-режима — окно `0x4100..0xBB00` (стек с 0xBB00): код в W1, +данные в W2, между концом данных и стеком остаётся куча. Всё, что туда не +влезло, живёт в банках. + +## Как СМОТРЕТЬ, а не гадать + +**Карта линкера врёт.** File-static SDCC в неё не попадает, и «дырка» между +двумя именованными символами приписывается предыдущему целиком. По карте +выходило, что у `pop_bg` 1529 Б данных (на деле 289), а у `pop_t_win_clear` +1282 Б кода — при том, что это однострочник, а 1282 Б это два статических +помощника соседнего `pop_blit_b`. + +Точный источник — объектные файлы: строки `A size flags ` в +`.rel` дают ровный размер каждой области модуля, а `S Def/Ref` — кто +символ определяет и кто на него ссылается. + +**Частоту вызовов мерить в MAME счётчиком**, а не оценивать по смыслу: + +``` +bpset ,1,{printf "F %d ...",temp0,...; temp0=0;...; g} +bpset ,1,{temp0=temp0+1; g} +``` + +Обязательна **канарейка** — счётчик заведомо горячей функции в том же +прогоне. Дважды спасала: один раз показала, что перехода комнаты в окне +замера не было (все нули), другой — что зонды вообще не встали (в zsh +`set -- $pair` НЕ разбивает строку на слова, и адрес уезжал в мусор). +Полную перерисовку комнаты форсировать читом `+`/`-`, ходьбой ненадёжно. + +## Сделано + +### 1. malloc вон из резидента (−613 Б) + +`cbl_open` держал `malloc`/`free` в мёртвой ветке `CBL_UNDERRUN_SILENCE`, а +линкер тянет `.rel` целиком — и куча приезжала каждому приложению. Разведены +две публичные точки входа (`cbl_open` / `cbl_open_silence`) поверх общего +`_cbl_open_raw`; `cbl_close` больше не зовёт `free`. + +### 2. Разрез pop_tile: холодная половина в банк 5 (−1788 Б) + +`pop_tile.c` был крупнейшим жильцом резидента (5 972 Б кода). Целиком он не +уедет: его const-таблицы (`POP_TILE_DIV/MOD`, `pop_tile_table`, таблицы +кадров) читают банки 2, 3, 7 и 8, а таблица в чужом банке не видна. + +Отбирали ЗАМЕРОМ, на двух тайлсетах (подземелье ур. 1 и дворец ур. 4 — +`pop_mem_b` рисует композитный кусок и мог оказаться дворцовым). Порог — +пик не больше 3 вызовов на кадр. + +| уехало в банк 5 | пик/кадр | | осталось в резиденте | пик/кадр | +|---|---:|---|---|---:| +| `pop_mem_b` | 0 | | `pop_tile_code` | 296 | +| `pop_cd_hit` (+`hit_rect`) | 0 | | `pop_cd_touch` | 198 | +| `pop_t_win_set/clear` | 0..1 | | `pop_blit_b` (+2 статика) | 184 | +| `pop_heal_off` | 0..1 | | `pop_wall_modifier` | 101 | +| `pop_potion_flask` | 0..1 | | `pop_env_b` | 73 | +| `pop_room_set_above/below` | 1 | | `pop_tile_mod` | 70 | +| `pop_cd_init/clear` | 1 | | `pop_cd_batch_end` | 40 | +| `pop_bar_black` | 3 | | `pop_fore_set_clip` | 2 | +| `pop_cd_hit_slot` | 2..3 | | все const-таблицы | — | + +`pop_fore_set_clip` (88 Б) оставлен намеренно: не стоит отказа от прямого +вызова из банка 4, ради которого он и заводился. + +**Цена трамплина замерена**: 252 такта пролог + 84 эпилог + ~50 на стороне +вызывающего = **~410 тактов** на вызов. Итого ~1 000 тактов на кадр покоя +(0,2 % работы) и ~3 700 на кадр редрава (0,009 растра). + +**Ключ, почему это безопасно:** вызов банк → резидент ПРЯМОЙ, трамплин не +нужен (W1/W2 замаплены всегда). Поэтому `blit_b_clip` просто перестал быть +`static` и объявлен в `_pop_tile.h`, а не переехал следом за `pop_mem_b`. + +### Итог + +| | было | стало | +|---|---:|---:| +| `_CODE` резидента | 24 329 | **21 928** | +| свободно до стека | **129 Б** | **2 535 Б** | +| BANK5 | 2 080 (13 %) | 3 954 (24 %) | + +Проверено в MAME: уровень 1 (подземелье) и уровень 4 (дворец), переходы +комнат читом `+`, ходьба — фон, факелы, решётки, гобелены, колонны без +искажений. + +## ЛОВУШКА: данные банка в его страницу — НЕ ДЕЛАТЬ без разбора + +Отдельная попытка (`--bank-data=SRC`, коммиты 3545826/9025573) **откачена**: +перенос писучих данных банкового модуля в его 16-КБ страницу давал цветной +мусор блоками и ронял DSS. + +У `pop_trob` причина найдена: `pop_trob_modif()` ВОЗВРАЩАЕТ УКАЗАТЕЛЬ на +`room_modif[24][30]`, а зовут её из банков 2, 3, 7 и резидента — после +переноса они пишут по 0xC000+ в СВОЮ страницу, поверх чужого кода. +Def/Ref-анализ такого не видит: снаружи ссылки на символ нет, есть ссылка на +функцию, отдающую его адрес. Но и `pop_room`, у которого утечки указателя +найти не удалось, ломался так же — механизм понят не до конца. + +Нулевая инициализация при этом ни при чём: `mkexe -p 0` был проверен по +образу (прогон нулей 14 304 Б, самый длинный прогон 0xFF — 14). + +**Перенос КОДА в банк — штатный путь, на нём стоят все наши банки. Ломался +именно перенос ДАННЫХ.** + +## Что осталось + +- `sprpop.c` 2 508 Б и `pop_kid.c` 2 418 Б — следующие по величине, но оба + горячие (главный цикл и `play_seq`). +- `pop_level.c` 956 Б кода + 660 Б данных (из них `pop_dl1`/`pop_dl2` по + 256 Б — таблицы дверных связей). +- BANK7 на 77 %: если понадобится место в нём — выносить `pop_redraw.c`. diff --git a/applications/SprPoP/docs/room_model_plan.md b/applications/SprPoP/docs/room_model_plan.md new file mode 100644 index 0000000..ff5e341 --- /dev/null +++ b/applications/SprPoP/docs/room_model_plan.md @@ -0,0 +1,86 @@ +# PoP SprPoP — модель `kid_room ≠ drawn_room` (баг #4) + +> **Статус: ЖИВОЙ ПЛАН, сделан частично (сверено 2026-08-01).** +> - **S1 — сделан:** `kid_room` заведён отдельно от `cur_room`, +> `update_kid_render_dx()` (`sprpop.c`) даёт рендер-смещение ∓140, а +> `pop_kid_set_render_dx` применяет его в отрисовке. Фактически это пока +> каркас: `enter_room` держит `kid_room == cur_room`, так что смещение +> всегда 0. +> - **S2/S3/S4 — не сделаны и не срочны.** Исходный повод (баг #4, +> пинг-понг у шва) закрыт иначе — поправкой odd-pixel в +> `char_x_forward_edge` + `pop_leave_timer` (разбор корня — +> `../PoP/SprPoP/BUGS_CLOSED.md`, BUG-SEAM-PINGPONG). +> +> **Зачем документ остаётся.** Полная straddle-модель понадобится для: +> (а) читов осмотра соседних комнат `H/J/U/N` (`levels_plan.md` §4), +> (б) сцен, где Кид и страж в разных комнатах кадра, (в) остатков окклюзии у +> шва (S4). Брать из `../TASKS_OPEN.md`, когда дойдёт очередь. + +Порт straddle-модели SDLPoP: персонаж может находиться в СОСЕДНЕЙ комнате, +пока на экране ещё ТЕКУЩАЯ (drawn_room). Источник истины — SDLPoP. + +## Факты из SDLPoP (подтверждено чтением исходника) + +- `Char.room` (реальная комната персонажа) ≠ `drawn_room` (отрисованная) — + штатное состояние. +- **Коллизия через ±140:** `xpos_in_drawn_room()` (seg004:0405) сдвигает + xpos на `±TILE_SIZEX*SCREEN_TILECOUNTX = ±140`, когда `curr_room` колонки + (`curr_row_coll_room[col]`) ≠ `drawn_room` (room_L/room_BL → −140, + room_R/room_BR → +140). Т.е. коллизия строится по РЕАЛЬНЫМ тайлам соседей. +- **Смена экрана:** `check_the_end()` (seg000:0FBD): `if (next_room!=0 && + next_room!=drawn_room) { drawn_room=next_room; load_room_links; redraw }`. + `next_room` ставится в `exit_room()` (= `Char.room` ПОСЛЕ успешного + `leave_room`). Значит drawn_room следует за Char.room, но Char.room меняется + ТОЛЬКО при реальном пересечении шва (leave_room, seg002:0504) на «легальном» + кадре/действии (не turn/climb/standup). +- **Отрисовка левого соседа:** только `load_leftroom()` (col9 левого соседа в + левую кромку); правый сосед НЕ рисуется (изометрия). Окклюзия ворот на шве — + только левая (seg008:696). +- **Ceiling-полоса:** `draw_room` рисует доп. ряд из `room_A` (row2, draw_main_y + =-1). (Уже реализовано, баг #3.) + +## Текущее состояние нашего движка (до #4) + +`cur_room` (=drawn_room) ВСЕГДА == комната Kid. Шов подделан: Kid остаётся в +drawn_room с `curr_col=-1/10` + снапшоты соседей `g_lcol/g_rcol` (коллизия ±1 +кол) / `lcol_bg` (openness ворот). Уход из комнаты — `pop_leave_dir`/`enter_room` +МГНОВЕННО при пересечении порога `char_x`. Отсюда #4: экран переключается +раньше, чем в оригинале (Kid должен «отступить» за кромку, оставив старую +комнату). + +## План (инкременты, каждый проверяется в MAME) + +### S1. Данные + рендер-смещение Kid +- Ввести `kid_room` (реальная комната Kid) отдельно от `cur_room`(=drawn_room). +- `kid_x_offset()` = разница комнат: kid_room == left(drawn) → лог. x Kid −140 + (рисуется за левой кромкой); right → +140; равны → 0. (порт + xpos_in_drawn_room). +- `kid_draw`/heal/fore используют смещение (Kid рисуется частично за кромкой). +- Проверка: Kid у шва рисуется со сдвигом, экран не дёргается. + +### S2. Коллизия по kid_room +- Коллизионный контекст (`g_fg`/edges/`g_room`/modif в pop_map) следует за + `kid_room`, а не за drawn_room. Когда kid_room≠drawn_room — грузим + соседа как коллизионную комнату (curr_col 0..9 в кадре kid_room). +- Отрисовка (room_fg и т.п.) остаётся по drawn_room. +- Порт `curr_row_coll_room[]`/`xpos_in_drawn_room` можно упростить: держим + ОДИН коллизионный room (kid_room) + существующие снапшоты кромок для ±1 кол. + +### S3. Отложенная смена drawn_room +- Уход (`check_leave`/`check_leave_below`): ставит `kid_room=сосед`, + репроецирует Kid (x∓140, col∓10) — но drawn_room НЕ меняет сразу. +- `check_the_end`-эквивалент в главном цикле: `if (kid_room != drawn_room && + <условие коммита>) enter_room(kid_room)`. Условие коммита — по SDLPoP: + как только Char.room сменилась легальным leave (не bumped/turn). Для + «bumped назад за кромку» drawn_room остаётся (симптом #4). +- Проверка сценариев #4/#5 в MAME. + +### S4. Полировка +- Окклюзия/ceiling у шва при straddle, BUG-OCCL-1 (глубина), правый край. + +## Связанные баги — все ЗАКРЫТЫ (`../PoP/SprPoP/BUGS_CLOSED.md`) +BUG-CEIL-1 (руки при прыжке вверх), BUG-CEIL-2 (loose в потолке), +BUG-CEIL-3 (потолок над анимируемыми воротами), BUG-OCCL-1 (тень дальней +колонны) — починены без полной straddle-модели. То есть S4 «полировка +окклюзии» осталась актуальной только для окклюзии У ШВА при straddle. +Memory: `pop_seam_room_model`. diff --git a/applications/SprPoP/docs/roomnav_skip.md b/applications/SprPoP/docs/roomnav_skip.md new file mode 100644 index 0000000..f489af7 --- /dev/null +++ b/applications/SprPoP/docs/roomnav_skip.md @@ -0,0 +1,65 @@ +# Комнаты для отладочного телепорта (`+` / `-`) + +Считано скриптом прямо по `../SDLPoP/data/LEVELS/res20NN.bin`, 2026-08-13. +Задача: обход комнат читом идёт последовательно, и в КАЖДОЙ комнате Киду +должно найтись место для материализации без падения и смерти. + +## Критерии пропуска + +| причина | как определяется | +|---|---| +| **недостижима** | до комнаты нельзя дойти от стартовой обходом связей (BFS по left/right/up/down) | +| **нет пола** | ни одного тайла, на котором можно стоять (`tile_is_floor`, seg004) | +| **пол опасный** | стоять есть на чём, но обычного пола (код 1) нет — только пики, расшатанные плиты и прочее | + +Про достижимость важно: считать «сколько комнат на неё ссылаются» НЕ +годится. На уровне 1 комнаты **13** и **18** ссылаются только друг на +друга (13.down = 18, 18.up = 13), то есть входящая связь у каждой есть, а +из остального уровня в них не попасть. Ловит это только обход от старта. + +Чит ищет место снизу вверх: сперва обычный пол (код 1), потом любой +проходимый тайл (`pop_dbg_roomnav`, `sprpop_cold.c`). Поэтому «пол +опасный» — это комнаты, где он свалится на fallback и Кид может погибнуть. + +## Таблица по уровням + +| ур. | старт | достижимо | пропускать | +|---|---|---|---| +| 1 | 1 | 21/24 | **13** (недостижима), **18** (недостижима), **24** (недостижима) | +| 2 | 5 | 24/24 | **14** (пол опасный), **17** (пол опасный) | +| 3 | 9 | 22/24 | **17** (пол опасный), **19** (пол опасный), **20** (нет пола), **21** (пол опасный), **23** (недостижима/нет пола), **24** (недостижима/нет пола) | +| 4 | 1 | 24/24 | **19** (нет пола), **20** (нет пола) | +| 5 | 7 | 19/24 | **1** (недостижима), **3** (недостижима), **5** (недостижима), **6** (недостижима), **19** (недостижима/нет пола), **21** (пол опасный), **22** (нет пола) | +| 6 | 24 | 14/24 | **2** (пол опасный), **3** (нет пола), **4** (недостижима), **7** (нет пола), **8** (недостижима/нет пола), **11** (пол опасный), **13** (недостижима), **14** (недостижима), **16** (недостижима), **17** (недостижима), **19** (недостижима/нет пола), **20** (недостижима), **21** (недостижима/пол опасный), **22** (недостижима), **23** (нет пола) | +| 7 | 17 | 24/24 | **3** (пол опасный), **17** (нет пола), **21** (пол опасный) | +| 8 | 1 | 21/24 | **9** (нет пола), **10** (пол опасный), **11** (недостижима), **15** (недостижима), **17** (пол опасный), **19** (недостижима), **20** (пол опасный), **21** (нет пола) | +| 9 | 11 | 24/24 | **8** (нет пола), **18** (пол опасный) | +| 10 | 1 | 19/24 | **3** (нет пола), **4** (пол опасный), **6** (недостижима), **9** (нет пола), **11** (пол опасный), **13** (нет пола), **18** (нет пола), **20** (нет пола), **21** (недостижима), **22** (недостижима), **23** (недостижима), **24** (недостижима) | +| 11 | 6 | 23/24 | **3** (нет пола), **5** (нет пола), **9** (нет пола), **10** (нет пола), **11** (нет пола), **12** (нет пола), **17** (нет пола), **18** (недостижима), **23** (нет пола) | +| 12 | 3 | 24/24 | **5** (нет пола), **6** (пол опасный), **8** (пол опасный), **10** (нет пола), **11** (нет пола), **17** (нет пола), **18** (пол опасный), **19** (пол опасный), **22** (нет пола) | +| 13 | 23 | 11/24 | **2** (нет пола), **4** (пол опасный), **5** (недостижима), **6** (недостижима/пол опасный), **7** (недостижима), **8** (недостижима/пол опасный), **9** (недостижима), **12** (недостижима), **14** (недостижима), **15** (недостижима), **18** (недостижима/пол опасный), **19** (недостижима/пол опасный), **20** (недостижима), **21** (недостижима), **22** (недостижима/нет пола) | +| 14 | 4 | 6/24 | **7** (недостижима/нет пола), **8** (недостижима), **9** (недостижима), **10** (недостижима), **11** (недостижима), **12** (недостижима), **13** (недостижима), **14** (недостижима/пол опасный), **15** (недостижима), **16** (недостижима), **17** (недостижима/пол опасный), **18** (недостижима), **19** (недостижима), **20** (недостижима), **21** (недостижима), **22** (недостижима), **23** (недостижима), **24** (недостижима) | +| 15 | 6 | 6/24 | **1** (недостижима), **2** (недостижима), **7** (нет пола), **9** (недостижима/пол опасный), **10** (недостижима/пол опасный), **11** (недостижима/пол опасный), **12** (недостижима/пол опасный), **13** (недостижима/пол опасный), **14** (недостижима/пол опасный), **15** (недостижима/пол опасный), **16** (недостижима/пол опасный), **17** (недостижима/пол опасный), **18** (недостижима/пол опасный), **19** (недостижима/пол опасный), **20** (недостижима/пол опасный), **21** (недостижима/пол опасный), **22** (недостижима/пол опасный), **23** (недостижима/пол опасный), **24** (недостижима/пол опасный) | + +## Спецкомнаты — пропускать независимо от таблицы + +| ур. | комн. | почему | +|---|---|---| +| 12 | 23 | **seamless exit**: попадание МЕНЯЕТ УРОВЕНЬ на 13-й. Чит-триггер придержан (`pop_nav_hold`), но в обходе комнате делать нечего | +| 6 | 1 | **falling exit**: Кид проваливается вниз и уходит на 7-й уровень | +| 7 | 17 | **falling entry**: экран сразу переводится на комнату НИЖЕ (в таблице уже «нет пола») | +| 7 | 14 | проходится СВЕРХУ ВНИЗ; чит для неё уже особый — ставит Кида в ряд 0 | +| 5 | 24 | тень ждёт в колонке −1 ряда 0; посадка рядом начинает схватку, которой там быть не должно | +| 13 | 23, 16 | вход роняет гряду плит (`check_fall_flo`) — материализация под падающей плитой | + +## Уровень 15 + +Экран защиты от копирования, не часть сюжета (см. +[`levels_12_15_plan.md`](levels_12_15_plan.md) §4). Портировать не +планируем — **пропускать целиком**. + +## Как применять + +Список — данные, а не логика: держать таблицей в отладочном коде рядом с +`pop_dbg_roomnav` и пропускать помеченные комнаты, чтобы `+` всегда попадал +в пригодную. Пересчитывать скриптом, если поменяются данные уровней. diff --git a/applications/SprPoP/docs/shadow_render.md b/applications/SprPoP/docs/shadow_render.md new file mode 100644 index 0000000..af0a39b --- /dev/null +++ b/applications/SprPoP/docs/shadow_render.md @@ -0,0 +1,160 @@ +# Отрисовка Тени (charid_1_shadow) — изыскания, отложено + +Статус на 2026-08-11: **отложено по решению пользователя.** Тень пока +рисуется как обычный персонаж — простой копией из атласов Кида +(`pop_cdraw.c`, банк 0x5C, аппаратная прозрачность `#FF`). Вернуться к +«правильному» виду, когда будут сделаны все уровни: тогда будет известно, +какими именно кадрами тень вообще пользуется. + +Этот файл собирает всё, что уже выяснено, чтобы не переоткрывать. + +--- + +## 1. Как тень выглядит в оригинале + +Тень рисуется **двумя блитами ОДНОГО И ТОГО ЖЕ спрайта Кида** +(`seg008:1602`, `add_objtable`): + +```c +case 1: // shadow + add_midtable(obj_chtab, obj_id + 1, obj_xh, obj_xl, obj_y, blitters_2_or, 1); + add_midtable(obj_chtab, obj_id + 1, obj_xh, obj_xl + 1, obj_y, blitters_3_xor, 1); +``` + +OR на месте, XOR со сдвигом на пиксель вправо. XOR гасит совпавшее, +остаются края — отсюда «контурный» вид. Это ЗАМЫСЕЛ оригинала, а не +артефакт SDLPoP: подтверждено печатью из живого SDLPoP (метка `DBGMIRROR` +в `add_objtable`) — и тень, и отражение идут из `chtab=2` (собственные +спрайты Кида), `swordbits=0`, обычными кадрами: + +``` +type=4 chtab=2 img=40 dir=0 clipL=137 clipT=3 charid=0 frame=41 <- отражение +type=1 chtab=2 img=41 dir=0 clipL=137 clipT=3 charid=1 frame=42 <- тень +``` + +Единственное различие между отражением и тенью — блиттер. + +## 2. Чем мы располагаем + +Блочные AND/OR/XOR/NOT акселератора подняты в libbgi 2026-08-11 (полный +набор строками и колонками, `gfx_blit_op` / `gfx_blit_part_op` / +`gfx_blit_cols_op` / `gfx_blit_cols_part_wx_op`; регресс — `tests/accop`, +10/10 PASS). Механика и ловушки — memory `accel_block_ops` и шапка +`libbgi/common/_gfx_blit_full_op.c`. То есть примитивов достаточно, дело +не в них. + +## 3. Две причины, по которым «в лоб» не получается + +### 3.1 XOR несовместим с нашей прозрачностью `#FF` + +Аппаратная прозрачность (бит 3 видеобанка) подавляет запись байта `#FF`, +то есть смотрит на **результат** операции: + +| операция | прозрачный пиксель источника | итог | +|---|---|---| +| AND | `#FF & bg = bg` | работает даром | +| OR | `#FF \| bg = #FF`, запись подавляется | работает даром | +| XOR | `#FF ^ bg = ~bg`, подавления нет | **инверсия фона по всему футпринту** | + +Совпадение с «ничего не делать» у XOR получается только там, где фон равен +0 (`#FF ^ 0 = #FF` → подавляется). В DOS-оригинале прозрачный индекс = 0 — +нейтральный и для OR, и для XOR, поэтому там оба блиттера работают на одном +наборе спрайтов. У нас прозрачный `0xFF` (`pop_pack_kid.py`: `0 -> 0xFF`, +`i -> 0x70 + i`). + +Замаскировать `#FF` внутри операции нельзя в принципе: побитовые AND/OR/XOR +не умеют «выбрать по условию», а `#FF` — нейтраль только для AND. Значит +источнику XOR-прохода нужен **прозрачный `0x00`**, то есть отдельный набор +спрайтов. + +### 3.2 Операция читает ОЗУ-копию экрана, а не видео-ОЗУ + +Чтение страниц `#50..#5F` всегда отдаёт ОЗУ-копию (memory +`sprinter_vram_transparency`), а персонажи рисуются банком `0x5C` («не +писать в копию» — на этом держится даровой heal). Поэтому второй проход +**не увидит результат первого**: два блита оригинала выродились бы в +«просто XOR», контурного эффекта не будет. + +Лечится не банком `0x50` (он ломает heal — копия перестанет быть чистым +фоном), а **однопроходным композитом**: всё складывается в буфере +акселератора за один проход по колонке j футпринта + +``` +буфер := s[j] ; спрайт +буфер |= bg[j] ; вертикальное чтение экрана +буфер ^= s[j-1] ; тот же спрайт, предыдущая колонка = сдвиг на +1 px +запись ; вертикальная запись колонки +``` + +что **точно эквивалентно** двум блитам оригинала (крайние колонки: `x` — +только OR, `x+w` — только XOR) и вдобавок дешевле их: 4 burst'а на колонку +против 6. Такому композиту тоже нужен источник с прозрачным `0x00` — уже +на обоих шагах. + +## 4. Сколько стоит подготовить источник с прозрачным `0x00` + +Замер 2026-08-11 (`tests/convbench`, watchpoint по IO-записи в MAME, кадр = +430 080 тактов). Цикл безветвочный (`ADD A,A / SBC A,A / CPL / AND` — +маска из бита 7: прозрачный `#FF` отличается от цветов Кида `0x70..0x7F` +именно им), 59 номинальных T-states на байт, по факту **145.3 такта/байт** +(2.5× wait-state'ов ОЗУ): + +| объём | кадров | секунд | +|---|---|---| +| 1 страница атласа, 16 КБ | 5.5 | 0.11 | +| весь атлас Кида, 28 страниц × 16 КБ = 448 КБ | 155 | 3.2 | +| он же **по реальному размеру данных (186 КБ)** | 64 | **1.3** | + +Последняя строка — замечание пользователя: 28 атласов занимают 186 КБ, а не +448 КБ; обрабатывать по фактическому размеру ленты вместо целой страницы +даёт 2.4× (ценой проверки границы в цикле). Потолок разгона самого цикла — +ещё примерно вдвое (раскрутка убирает `djnz`, чтение через SP парами + +таблица 256 Б вместо арифметики), то есть **~0.7 с** на 186 КБ. Порядок +величины при этом не меняется. + +**Окна:** источник и приёмник — разные EMM-страницы, а окно под атласы одно +(W0), поэтому конвертация гоняется «страница-источник в W0 → +страница-приёмник в W3» целыми страницами; побайтно переключать окно нельзя. +EMM-бюджет: +28 страниц (448 КБ) из ~3440 КБ свободных — не проблема +(memory `sprinter_emm_budget`), и он одинаков в любом из вариантов. + +## 5. Варианты (когда вернёмся) + +1. **Конвертация в рантайме при загрузке уровня с тенью** (4, 5, 6, 12): + диск и упаковщик не трогаем, цена — 1.3 с (или 0.7 с после разгона) на + загрузку такого уровня. +2. **Лениво, постранично** — 0.11 с (5.5 кадра) при первом обращении тени к + странице; рывок один раз на страницу, суммарно меньше, чем вариант 1. +3. **Второй набор `.atl` от упаковщика** (`pop_pack_kid.py`, прозрачный + `0x00`): 0 с рантайма, +186 КБ на образе и вторая ветка в загрузчике + атласов. +4. **Только OR-проход** (то, чем можно обойтись бесплатно): OR с нашим + `#FF`-атласом работает как есть, тень получается сплошным силуэтом в + палитре Кида, без контурного эффекта. Расхождение с оригиналом — тогда + записью в `docs/impl_diff.md`. + +**Ключ к выбору — какие кадры тень вообще использует.** Предположение +пользователя: только бег, длинный прыжок (из зеркала), питьё зелья и +боёвка; прыжки с места и подтягивания — нет. Если так, конвертировать +(или паковать) нужно единицы страниц, а не 28, и разница между вариантами +почти исчезает. Список снимать по факту — когда уровни 5/6/12 будут +проходиться. + +## 6. Что ещё придётся проверить глазами + +Палитра. У нас индексы разложены группами по 16 (`pop_pack_bg.py`): +`0x30` VGA16, `0x40` chtab_1, `0x50` env, `0x60` wall, `0x70` kid, `0x80` +sword, `0x90` guard. Отсюда ожидания (аналитические, в MAME НЕ +проверялись): + +- OR-проход ложится удачно: `0x7X | 0x5Y = 0x7Z` — результат остаётся в + палитре Кида, а младший ниббл получается ровно тот же, что дал бы DOS + (там OR шёл по 4-битным индексам внутри одной палитры); +- XOR-проход уводит результат в группы `0x0Z` (поверх OR-результата) и + `0x2Z` (по чистому фону) — **обе группы палитры у нас не заполнены**, то + есть контур рискует оказаться просто чёрным. + +Значит к «посмотреть глазами» добавляется вопрос, чем заполнять `0x00..0x0F` +и `0x20..0x2F` — по сути это и будет выбор цветов тени. В DOS такого +вопроса не было: XOR двух 4-битных индексов всегда оставался внутри той же +16-цветной палитры. diff --git a/applications/SprPoP/docs/sound_plan.md b/applications/SprPoP/docs/sound_plan.md new file mode 100644 index 0000000..8a02ad5 --- /dev/null +++ b/applications/SprPoP/docs/sound_plan.md @@ -0,0 +1,963 @@ +# Звук в порте PoP — разбор и план + +Дата: 2026-08-20, музыка дописана 2026-08-25. Статус: **PCM-эффекты +реализованы; музыка — путь C (PCM через CBL), первый трек играет.** + +Задача пользователя: добавить звук. Приоритет — эффекты; музыку, если +найдётся способ. Эффекты — **обязательно WAV, а не PC-спикер** +(уточнение 2026-08-20; см. §1а — оказалось, что они и так все в WAV). Ниже — что реально лежит в ассетах, что умеет железо, и +почему получившийся план вышел проще, чем ожидалось. + +## 1. Главный вывод + +**Ни MIDI разбирать, ни ноты сочинять не придётся, и ресэмплировать тоже.** + +- Эффекты уже лежат **8-битным беззнаковым PCM на 11 000 Гц**, а у CBL есть + режим **10 937,5 Гц** — расхождение 0,6 %, на слух неразличимо. Формат + сэмпла совпадает с нашим CBL байт в байт (`cbl.h`: 8 бит, беззнаковый, + центр 0x80). То есть данные играются **как есть**, без конверсии. +- Музыка есть в виде **списков нот PC-спикера — 7 КБ на всю игру**, а нота + там задана прямо в ГЕРЦАХ. Пересчёт в делитель AY — одно деление. +- AY и COVOX на Sp2000 сведены в **один ЦАП TDA1543** (док Ивана Мака, + §5), значит музыка на AY и эффекты через CBL звучат ОДНОВРЕМЕННО, и + смешивать их программно не надо. + +## 1а. Уточнение после разбора ВСЕХ наборов MS-DOS версии (2026-08-20) + +Пользователь попросил, чтобы эффекты были не PC-спикером, а WAV, и заодно +посмотреть `mt32snd[1-2].dat`. Разобрал все восемь `.dat` из `MSDOS/`. +Ответ короткий: **эффекты И ТАК все до одного есть в WAV, а вот у музыки +WAV нет ни в одном наборе.** + +| набор | формат | какие звуки | сколько | +|---|---|---|---| +| `digisnd1..3` | **WAV**, 8 бит PCM | эффекты 0..23, 44..49, 51 | **31** | +| `mt32snd1..2` | MIDI для Roland MT-32 | ТЕ ЖЕ эффекты 0..23, 44..51 | 31 | +| `midisnd1..2` | MIDI (AdLib/GM) | **музыка** 24..43, 50, 52..56 | 22 | +| `ibm_snd1..2` | ноты PC-спикера | **всё подряд, 0..56** | 57 | + +Здесь пряталась ловушка: `mt32snd` по имени похож на «музыку получше», а +на деле это набор ЭФФЕКТОВ для владельцев MT-32 — те же id, что у +`digisnd`. Музыки в нём нет вовсе. + +Частоты WAV: 28 звуков на 11 000 Гц, по одному на 8 200, 14 000 и 2 750. +Итого 112 922 сэмпла = **11,4 с, ~110 КБ ≈ 6,7 EMM-страниц**. + +Только PC-спикером, без альтернатив, остаются четыре id: 31, 34, 42 +(пустые) и **38 `blink`** — четыре ноты. То есть на весь звук игры +спикер нужен ровно для одного писка. + +### Музыка: WAV нет, есть три пути + +| путь | данные | что получится | цена | +|---|---|---|---| +| **A. Ноты PC-спикера на AY** | 7 КБ | один квадратный голос — ровно то, что слышали на IBM PC 1989 | секвенсор на полсотни строк | +| **B. MIDI -> AY, три голоса** | 27 КБ исходника | богаче: бас + мелодия + арпеджио | разбор MIDI + раскладка по каналам | +| **C. MIDI -> WAV на хосте, стрим через CBL** | см. ниже | настоящее звучание, любое | нужен синтезатор на хосте + место | + +Про объём для пути C (замерено по длительностям треков): + +| группа | треков | длительность | WAV 11 кГц | +|---|---:|---:|---| +| звучат ПО ХОДУ игры (гимн уровня, смерть, зелья, перо, победа) | 12 | 74,7 с | **803 КБ = 50 EMM-страниц** | +| заставки и титры | 10 | 248,1 с | 2 665 КБ = 167 страниц | + +Игровая половина в EMM **влезает** (при ~215 свободных страницах), а +заставочная — нет, её пришлось бы стримить с диска. Но заставки идут +тогда, когда игра ничего не рисует, так что стрим там как раз уместен. + +**Предложение:** начинать с A (7 КБ, работает сразу, ноль рисков), а C +держать как отдельную фазу — она ортогональна: проигрыватель WAV для +музыки это тот же `cbl_push`, что и для эффектов, только длиннее буфер. +B имеет смысл только если C окажется неподъёмным по месту. + +## 1б. РЕШЕНИЯ (пользователь, 2026-08-20) + +1. **Эффекты — WAV через CBL, 8 бит, МОНО, единая частота.** Проверено по + `convert_digi_sound` (`seg009.c:2358`): один байт на кадр, то есть + моно, и байт беззнаковый (`(b | b<<8) - 32768`), центр 0x80 — ровно + формат нашего CBL. Стерео в данных нет вовсе: каналы у оригинала + размножаются уже на выходе (`digi_audiospec->channels`). +2. **Музыка, первый заход — путь A** (ноты спикера на AY). +3. **Заставки и титры — потом WAV.** Конфликта с эффектами там нет: + одновременно они не звучат. +4. **Музыка ПО ХОДУ игры** (она может совпасть с эффектом) — открыто, два + варианта: либо тоже WAV с ГАШЕНИЕМ эффектов на время музыки (музыка + важнее — **проверить на слух**), либо путь B (MIDI -> три голоса AY). +5. **Все эффекты привести к одной частоте.** +6. Синтезатор для MIDI -> WAV — решать ближе к делу; годятся и онлайн- + конвертеры, хоть вручную, если fluidsynth/timidity не поставится. + +### Про единую частоту (замер) + +Приводим не к 11 000, а ровно к **10 937,5 Гц — частоте CBL** +(`CBL_FREQ_10K9`). Тогда тон точен, а не «на 0,6 % ниже»: проигрывание +11 000 Гц данных на 10 937,5 даёт сдвиг **−9,9 цента**, что на коротком +эффекте не слышно, но бесплатно избавиться от него всё равно приятно — +пересчитывать три файла всё равно придётся. + +| id | звук | было | станет | дельта | +|---|---|---|---|---:| +| 15 | `leveldoor_sliding` | 2 750 Гц, 4 436 сэмплов | 17 643 | **+13 207 Б** | +| 23 | `footstep` | 8 200 Гц, 996 | 1 329 | +333 Б | +| 51 | `princess_door_opening` | 14 000 Гц, 6 188 | 4 834 | −1 354 Б | +| — | остальные 28 (11 000 Гц) | — | ×0,9943 | −588 Б | + +Итог: **112 922 -> 124 531 Б, 6,9 -> 7,6 EMM-страниц.** Рост целиком от +`leveldoor_sliding`: источник у него 2 750 Гц, вчетверо реже целевой, и +апсэмплинг не улучшит звучание — только уравняет формат. Платим 0,8 +страницы за то, что **CBL открывается ОДИН раз и частоту менять не надо +никогда** — ни между эффектами, ни при переходе на музыку-WAV. + +(Альтернатива для него — хранить как есть и повторять каждый сэмпл +четырежды в рантайме: 2 750 × 4 = 11 000 ровно. Это код в `fill()` ради +13 КБ; не стоит того, но если место когда-нибудь прижмёт — вариант есть.) + +## 1в. Бюджет памяти EMM (живой замер 2026-08-20) + +Замерено `mem_info` из работающей программы (уровень 1), а не посчитано на +бумаге: инструментовка ставилась временно и откатана. + +| | страниц | КБ | +|---|---:|---:| +| всего в машине | 256 | 4 096 | +| система (DSS) + сам exe: база + 8 банков кода | **43** | 688 | +| наши ассеты | **81** | 1 296 | +| **занято** | **124** | 1 984 | +| **свободно** | **132** | **2 112 (2,06 МБ)** | + +Разбивка 81 страницы ассетов (сходится точно): + +| набор | страниц | +|---|---:| +| **Тень** (`sk*` 28 + `sf*` 4) | **32** | +| Кид (`kid0..27`) | 28 | +| фон тайлсета (env 10 + wall 1 + fore 1) | 12 | +| страж | 5 | +| зелья (chtab_1), меч, `kid_data.bin`, страница уровня | по 1 | + +Самый крупный потребитель теперь — **набор Тени, 32 страницы**, больше +самого Кида. Если место когда-нибудь прижмёт, там есть очевидный резерв +(кадры смерти и позы, в которых Тень не бывает), но при 132 свободных +страницах трогать незачем. + +### Что из этого следует для звука + +| статья | страниц | останется свободно | +|---|---:|---:| +| эффекты WAV, все 31, 10 937,5 Гц | **8** | 124 | +| музыка ПО ХОДУ игры в WAV (путь C, 12 треков) | 50 | 74 | +| заставки и титры в WAV (10 треков, 248 с) | 167 | **не влезает** | + +То есть эффекты — капля, игровая музыка в WAV тоже поместится, а +заставочную придётся стримить с диска в любом случае (что и планировалось: +во время заставок игра ничего не рисует). + +Оговорка: 132 свободных страницы — это на уровне 1. На уровне 9 добавятся +зеркальные наборы (`pop_vflip_load_all`: 28 Кид + 5 страж + меч = 34 +страницы), останется ~98. Проверять запас надо ИМЕННО ТАМ. + +## 1г. MSDOS против SDLPoP: чем отличаются наборы (сверено 2026-08-20) + +У нас лежат ДВЕ копии звука — оригинальные `.dat` в `MSDOS/` (версия +1.3/1.4) и распакованные ассеты `SDLPoP/data/` (версия 1.0/1.1). Разница +есть, и она влияет на выбор источника. + +### Оцифровка: берём MSDOS + +Заголовок разный (`digi_new_type` против `digi_type`), но **28 звуков из +31 совпадают побайтно**. Различаются три, и все не в пользу SDLPoP: + +| id | звук | MSDOS | SDLPoP | +|---|---|---:|---:| +| 10 | `sword_vs_sword` | 5 020 сэмплов | 3 504 | +| 11 | `sword_moving` | 1 172 | 1 172, но **другие байты** | +| 48 | `spiked` | 5 069 | **7** — то есть звука нет | + +`spiked` в наборе SDLPoP фактически пустой. Поэтому упаковщик читает +`MSDOS/digisnd*.dat`, а не распакованные ассеты — в отличие от графики, +где источник наоборот SDLPoP. + +### MIDI: если дойдём до музыки — брать SDLPoP + +Здесь всё наоборот. Содержимое музыкально то же (деление 480, те же +каналы 0..7 плюс ударные), но: + +| | MSDOS | SDLPoP | +|---|---|---| +| формат MIDI | **0** — всё слито в ОДНУ дорожку | **1** — 8-9 дорожек | +| размер (звук 24) | 327 Б | 448 Б | +| размер (звук 56) | 13 587 Б | 12 773 Б | + +Формат 1 с отдельной дорожкой на инструмент — это готовое разделение +голосов. Для пути B (MIDI -> три канала AY) оно решает половину задачи: +дорожки можно выбирать напрямую (бас / мелодия / гармония), а не +разбирать слитый поток и догадываться, что чем было. + +**Итог: эффекты из MSDOS, музыка (когда дойдёт) из SDLPoP.** + +## 2. Что лежит в ассетах (замерено, а не по памяти) + +Звук в PoP адресуется как ресурс `10000 + N`, N = 0..56 — 57 звуков +(`load_sound`, `seg009.c:2289`). Наборов три, и они ПАРАЛЛЕЛЬНЫЕ: один и +тот же звук есть в нескольких видах. + +| набор | что это | объём | покрытие | +|---|---|---:|---| +| `DIGISND1..3.DAT` | оцифровка, 8 бит PCM | 103 941 Б | **31 звук** (эффекты) | +| `MIDISND1..2.DAT` | MIDI-музыка | 27 776 Б | музыка | +| `IBM_SND1..2` (распакованы) | ноты PC-спикера | **7 КБ** | **все 57** | + +### 2.1 Оцифровка (эффекты) + +Разбор контейнера: индекс по 8 байт на запись (id, offset, size), **первый +байт ресурса — контрольная сумма**, тело за ней (спецификация +`POP-DAT-FormatSpecifications`, §3.1.2 — на этом я сначала споткнулся и +читал мусор). Тело — `digi_type`: `word rate, word count, word unk, +byte size`, дальше сэмплы. + +- 31 звук, все 8-битные; +- частоты: **28 звуков на 11 000 Гц**, по одному на 8 200 и 14 000; +- 103 701 сэмпл = **9,4 секунды**, **103 941 Б ≈ 6,3 EMM-страницы**. + +### 2.2 Ноты PC-спикера (и эффекты, и музыка) + +Формат: `byte type(=0), word tempo`, дальше тройки `word frequency, +byte length`; `frequency <= 1` — пауза, `0x12` — конец. Ключевое, что +пришлось смотреть в `play_speaker_sound`/`speaker_callback` (`seg009.c`): + +- **`frequency` — это ГЕРЦЫ напрямую** (`generate_square_wave(stream, + (float)note->frequency, ...)`), а не делитель PIT, как кажется по + маленьким числам; +- длительность ноты = `length / tempo` СЕКУНД. + +Замеры по всем 57 звукам: **2212 нот**, частоты 16..65507 Гц, +длительности 1,74..2571 мс. + +Из них 23 звука — те, у которых оцифровки НЕТ, то есть вся музыка: +заставки, гимны уровней, смерть, победа, титры (id 24..43, 50..56). +**1469 нот**, и вот их длительности: + +| длительность ноты | нот | доля | +|---|---:|---:| +| < 3 мс | 0 | 0 % | +| 3..12 мс | 2 | 0,1 % | +| 12..25 мс | 140 | 9,5 % | +| > 25 мс | 1327 | 90,3 % | + +Это число решает вопрос про таймер — см. §4. + +## 3. Что умеет железо (док Ивана Мака §5 + MAME) + +- **AY-3-8910/8912** в ПЛМ, «программируется по стандартным описаниям» — + то есть ZX-порты; в MAME он заведён как `AY8910(config, "ay8912", + X_SP/24)`, то есть **тактовая 1,75 МГц**. Период канала = 109375 / + частота(Гц), 12 бит (макс 4095) → снизу берутся частоты от ~27 Гц. + Точные Z80-адреса портов идут через таблицу DCP, а не напрямую — + **проверить артефактом до кодинга** (ожидаем ZX-стандарт 0xFFFD/0xBFFD). +- **CBL** — COVOX с буфером 256 Б, две половины по 128; бит 7 порта 0xFE + показывает играющую половину, порт управления 0x4E. Прерывание — + когда половина сменилась. +- **Бипер** (бит 5 порта 0xFE) — туда же в ЦАП. Нам не нужен. +- Всё это **сведено в один ЦАП**, поэтому AY и CBL звучат вместе. + +У нас уже есть готовая обвязка CBL (`libc/include/cbl.h`): callback +`fill(n)`, выдача блока через `cbl_push_otir()` (порт 0x4F) или через +акселератор, коды частот, счётчик недоливов. Своего кольца библиотека не +держит — данные пропихиваются прямо из наших EMM-страниц. + +## 4. Предлагаемая архитектура + +``` + эффекты (31 шт, 8 бит 11 кГц) музыка (23 шт, ноты) + │ │ + EMM-страницы (6,3) 7 КБ нот в банке + │ │ + cbl_push_otir из fill() запись 3 регистров AY + │ │ + CBL (10,9 кГц) ──────┐ ┌────────── AY (1,75 МГц) + ▼ ▼ + TDA1543 (аппаратное смешивание) +``` + +**Один источник прерываний — CBL.** Его callback приходит каждые 128 +сэмплов = **11,7 мс** при 10,9 кГц, и он же двигает секвенсор музыки. +Отдельный таймер (CTC) НЕ нужен: по таблице из §2.2 короче 12 мс всего +2 ноты из 1469 — они растянутся на один тик, чего не слышно. + +Почему это важно: `irq_ctc_install` сейчас требует кода в W2 (tiny/big), +а SprPoP — huge, и попадёт ли туда CTC-трамплин, зависит от раскладки. +Обойтись без него — значит не открывать этот фронт вовсе. + +**Пейсинг кадра при этом не страдает.** Наш темп считается ПО ЛУЧУ +(`pop_pace.h`), а не по кадровым прерываниям, поэтому то, что CBL-ветка +трамплина делает приватный RETI и съедает кадровые прерывания, нам +безразлично. Если бы пейсинг остался на прерываниях — звук бы его сломал. + +## 5. Объём работ + +| фаза | что | оценка | +|---|---|---| +| **З1** | распаковщик `pop_pack_sound.py`: DAT → `.snd`-страницы EMM (эффекты) + `.not` (ноты музыки) | формат уже разобран | +| **З2** | `pop_sfx.c`: `cbl_open` + `fill()`, таблица «звук → страница/смещение/длина», `pop_sfx_play(id)` | ядро | +| **З3** | развесить вызовы: 66 мест `play_sound` в оригинале; у нас часть уже помечена TODO (`pop_ctrl.c:408`, `guards.c:992`, `pop_map.c:3035/3107`, …). **Плюс бесплатный кусок**: опкод `SEQ_SOUND` в seqtbl уже разбирается нашим `play_seq` (`pop_kid.c:326`) — шаги, приземления и прочее поедут сами | механическая | +| **З4** | `pop_music.c`: секвенсор нот на AY (путь A), тик из CBL-callback | небольшая | +| **З6** | заставки и титры — WAV-музыка потоком (эффекты в это время не звучат) | после З1-З4 | +| **З7** | музыка по ходу игры: WAV с гашением эффектов ЛИБО путь B — решать по итогам З6 | открыто | +| **З5** | приоритеты и вытеснение: у оригинала `play_sound` глушит предыдущий (`stop_sounds`), музыка и эффект — разные каналы | правила из seg009 | + +## 6. Что проверить артефактом ДО кодинга + +1. **Порты AY на Sprinter** — записать в 0xFFFD/0xBFFD и убедиться, что + MAME отдаёт звук (порты идут через DCP-таблицу, «стандартные ZX» — + это ожидание, а не факт). +2. **CBL в режиме huge.** Шапка `cbl.h` говорит «код/данные в W2 + (tiny/big)», но это скорее всего устаревшая оговорка: IM2-трамплин + давно переделан на all-modes, и наш кадровый путь в huge работает. + Проверить `cbl_open` из SprPoP. +3. **Совместное владение портом 0xFE.** Бит 5 (луч) у нас держится через + `_cbl_port_ref` «немым» кодом частоты; когда откроется НАСТОЯЩИЙ CBL, + владение переходит к нему. Убедиться, что пейсинг переживает + `cbl_open`/`cbl_close`. +4. **Цена fill() в кадре.** 128 байт через `cbl_push_otir` раз в 11,7 мс + — замерить тем же способом, что и остальное (брейкпоинт + totalcycles). + +## 7. Про MIDI — почему не он + +Музыка в MIDISND — настоящие MThd/MTrk чанки, 27 КБ. Чтобы играть их на +AY, нужен разбор MIDI, раскладка каналов на три голоса и таблица +инструментов — это отдельный проект, и звучать он будет НЕ так, как +оригинал на PC. А набор PC-спикера — это ровно то, что слышал игрок на +IBM PC 1989 года: один квадратный голос. Он у нас есть целиком, весит +7 КБ и ложится на AY напрямую. + +Если позже захочется богаче — материал уже будет разобран, и можно +разложить те же мелодии на три канала AY (бас/мелодия/арпеджио), не трогая +ни данные, ни секвенсор. + +--- + +## 8. Разводка вызовов по коду (сделано 2026-08-20) + +Портированы ВСЕ места `play_sound()` SDLPoP, у которых есть оцифровка +(id 0..23, 44..49, 51 — остальные id это музыка, у них в +`pop_sound_tbl.h` длина 0, и вызов просто глушит текущий эффект). + +| id | что | где у нас | оригинал | +|----|-----|-----------|----------| +| 0 | разбился насмерть | `pop_map.c` land | seg005 | +| 1 | крик падения | `pop_map.c` do_fall | seg005:39 | +| 2 | плита рухнула | `pop_room.c` (обе ветки посадки) | seg007 | +| 3 | кнопка нажата | `pop_trob.c` | seg007 | +| 4/5/6/7 | ворота: закрываются / открываются / рухнули / стоп | `pop_trob.c` | seg007 | +| 8 | удар о стену | `pop_map.c` bumped_fall/bumped_floor + seqtbl | seg004/seg006 | +| 9 | зацеп за карниз | `pop_map.c` check_grab | seg006 | +| 10 | клинок о клинок | `sprpop.c` после `check_sword_hurt`, если один из бойцов в кадре 167 | seg000:1353 | +| 11 | свист клинка мимо | `guards.c` check_hurting | seg002:0DAE | +| 12/13 | ранен соперник / Кид | `guards.c` hurt_by_sword | seg002:0C1F | +| 13 | Кид ранен зельем | `pop_map.c` ветка «злого» зелья | seg006:1894 | +| 14/15 | дверь уровня: закрывается / едет | `pop_trob.c` | seg007 | +| 16 | средняя посадка; толчок о стража | `pop_map.c` land / bump_into_opponent | seg005/seg003:0654 | +| 17 | мягкая посадка | `pop_map.c` land | seg005 | +| 18 | пьёт | seqtbl (SEQ_SOUND) | seg006 | +| 19 | вынул меч | `pop_ctrl.c` | seg005:945 | +| 20/21/22 | дрожит плита | `pop_map.c` loose_shake | seg007:0E55 | +| 23 | шаг | seqtbl (SEQ_SOUND) | seg006 | +| 44 | скелет оживает | `guards.c` pop_check_skel | seg002:106D | +| 45 | прыжок в зеркало | `pop_map.c` jump_through_mirror | seg003:0617 | +| 46 | сожрал чомпер | `pop_map.c` | seg004 | +| 47 | чомпер щёлкнул | `pop_trob.c` (кадр 2) | seg007 | +| 48 | напоролся на пики | `pop_map.c` | seg005 | +| 49 | пики пошли | `pop_map.c` start_anim_spike | seg007:08F6 | + +Что осталось не разведено — только МУЗЫКА (24/28 смерть, 25 презентация, +26 объятия, +27/35/40 заставки, 29 встреча Джафара, 30/33 зелья, 32/41 конец уровня, +36 время вышло, 37 победа, 43 смерть Джафара, 50/52/53 сюжетные вставки) +и 51 (дверь принцессы, тоже из заставки). Их черёд — фаза «музыка». + +**Квирк, за которым следить.** Звук ворот у оригинала звучит не всегда, а +по условию видимости (`play_door_sound_if_visible`, seg007:1250): либо +ворота в комнате слева и стоят в 9-й колонке, либо ворота в НАРИСОВАННОЙ +комнате и колонка не 9-я. У нас это параметр `audible` у `animate_door`. + +**Тряска плиты — свой домен prandom.** Оригинал берёт номер сэмпла (20/21/22) +из общего генератора и вдобавок «сжигает» один бросок ради совместимости с +DOS-версией; у нас последовательности разведены по доменам +(`impl_diff.md`), поэтому у тряски свой сид, а холостой бросок не делаем — +на розыгрыши физики и кладки это не влияет. + +## 9. Цена звука в тактах (замер 2026-08-20, MAME) + +Вопрос был поставлен так: звук идёт по прерываниям, значит размазан по всем +фазам кадра — и если фазы укладываются, всё хорошо? Да, но проверять это +надо не по фазам, а по двум числам, потому что **нагрузка от звука +постоянная и от сцены не зависит вовсе**. + +### 9.1 Одно прерывание CBL + +Зонды: `bpset` на входе трамплина (`_irq_tramp`) и на `reti` ветки CBL, +разница `totalcycles`. 398 замеров в сцене 11/15. + +| величина | значение | +|---|---:| +| цена одного прерывания | **7 825 тактов ровно**, с разбросом до 8 299 (среднее 8 001) | +| период между прерываниями | 245 759 тактов (= 128 сэмплов на 10 937,5 Гц) | +| **доля процессорного времени** | **8 001 / 245 759 = 3,26 %** | + +Цена постоянная, потому что работа фиксированная: OTIR ровно 128 байт плюс +скобка сохранения контекста. Ветвлений по данным в насосе нет. + +### 9.2 Дрожание обслуживания — риск для ЗВУКА, не для кадра + +Период плавает 228 804 … 262 734, то есть прерывание опаздывает максимум на +**~17 000 тактов = 0,8 мс**. Это самая длинная DI-скобка в коде +(акселератор режется по 16 строк, memory `sprinter_wait_states_2x`). +Буфер CBL — 128 сэмплов = **11,7 мс**, запас **14×**. Недолива быть не +может; счётчик `cbl_underruns()` это подтверждает косвенно (наш `fill` +всегда возвращает 1, поэтому он ловит только отсутствие данных, не +опоздание). + +### 9.3 A/B в одном прогоне (Ctrl+S), сцена 11/15 + +Один и тот же кадр, звук выключается на ходу — сравнение чистое. + +| | работа min | работа max | работа avg | прерываний CBL на кадр | +|---|---:|---:|---:|---:| +| звук ВКЛ | 453 132 | 572 532 | **498 064** | 1,40 | +| звук ВЫКЛ | 444 348 | 559 434 | **487 372** | 0,00 | +| разница | +8 784 | +13 098 | **+10 692 (+2,2 %)** | | + +Разница на лёгком кадре (+8 784) — ровно одно прерывание, сходится с §9.1. +`CBL/кадр = 0` при выключенном звуке подтверждает, что Ctrl+S реально +ЗАКРЫВАЕТ CBL, а не глушит сэмпл: иначе насос продолжал бы отдавать блоки +тишины и платить те же 3,26 %. + +### 9.4 Укладываемся ли + +Логический кадр (`pop_pace.h`): NORMAL = 4 растра вне боя = **1 720 000 +тактов**, FASTEST = 3 растра = **1 290 000**. + +| | работа | доля NORMAL | доля FASTEST | +|---|---:|---:|---:| +| 11/15, обычная позиция | 498 064 | 29 % | 39 % | +| 11/15, тяжёлая позиция (Кид на 7 px правее) | 750 066 макс | 44 % | 58 % | + +Период кадра за все прогоны: 1 719 936 … 1 720 752 — ровно 4 растра, ни +одного проскока. **Звук занимает 1,1 % бюджета NORMAL и 1,5 % FASTEST.** + +### 9.5 Где 3,26 % МОГЛИ БЫ стоить дорого + +Ответ «всё размазано, если фазы влезли — ок» верен с одной оговоркой. +Пейсинг квантован растром: работа 1,00 растра и 1,02 растра дают РАЗНЫЙ +период кадра (3 против 4 интервалов), то есть скачок сразу на 20 мс. +Значит звук опасен ровно в одной ситуации — когда сцена стоит в пределах +~8 000 тактов НИЖЕ кратного растру порога. Сейчас ближайший запас — 540 000 +тактов до порога FASTEST, то есть в 60 раз больше цены звука. Проверять +эту оговорку заново стоит только если работа кадра подберётся к 430 000 или +860 000 вплотную. + +## 10. Мусор при включении и щелчок на выходе (разбор 2026-08-20) + +Жалоба: «при старте, когда разрешается звук, проходит кусок мусора». +Разобрано записью выхода MAME в WAV (`-wavwrite`) — по огибающей и +автокорреляции, а не на слух. + +### 10.1 На старте мусора НЕТ; это настоящие звуки + +От `cbl_open` до первого эффекта в записи **точная цифровая тишина** +(размах 1 при разрешении 16 бит). Дальше — два штатных звука: + +| что | когда | длительность | +|---|---|---| +| `gate_closing_fast` (6) — решётка в комнате СЛЕВА | +0,30 с после `cbl_open` | обрывается на 80 мс | +| `soft_land` (17) — Кид приземляется | +0,38 с | 383 мс | + +Опознаны корреляцией огибающих с оригинальными сэмплами `digisnd`: +звук 6 даёт +0,72 с начала записи, звук 17 — +0,32 со сдвигом 80 мс. +Приземление на старте КОРРЕКТНО: `start_pos` уровня 1 — тайл (0,0), а он +`space`, то есть Кид падает на ряд ниже, на площадку с факелами. + +Обрыв первого звука вторым — тоже поведение оригинала, а не наш дефект: +`play_digi_sound` (seg009.c:2402) начинается с `stop_digi()`, голос ОДИН. +Ощущение «мусора» даёт именно 80-мс огрызок скрежещущей решётки. + +Звук кнопки (3) при этом не слышен: `pop_sfx_play(3)` случается ДО +`cbl_open` и глохнет. С SDLPoP совпадает (там на старте тоже только +решётка), но держится это на порядке вызовов — если поднимать звук раньше +`pop_start_level`, щелчок кнопки станет слышен. + +### 10.2 Незалитый буфер CBL — дефект есть, но в MAME он немой + +Буфер CBL (256 слотов) железо не чистит ни сбросом, ни записью в порт +управления, а эта запись сразу пускает воспроизведение с нулевого слота. +Значит первые 256 сэмплов (23,4 мс) — то, что лежало раньше. Разбор по +`sprinter.cpp`: `case 0x89` делает `m_cbl_cnt = 0; m_cbl_wa = 0`, а +прерывание «долей половину» приходит только на 128-м слоте и ставит +указатель на ПРОТИВОПОЛОЖНУЮ половину — своими данными звук идёт лишь с +третьей половины. + +В MAME это не слышно: эмулируемый буфер стартует нулями, а ЦАП +двухдополнительный, то есть 0 = середина шкалы. На ЖЕЛЕЗЕ там +неинициализированное ОЗУ — ровно тот мусор, который ловился ещё на +тестовых примерах CBL. Лечение — `_cbl_prime` в `cbl_open`: сразу после +включения 256 записей байта тишины в порт данных (заранее нельзя, запись +проходит только при поднятом bit7). Стоит 6 400 тактов один раз за +открытие; за это время таймер уходит на два-три слота. + +### 10.3 Щелчок на выходе — ГОЛОДАНИЕ насоса, вылечено + +На выходе по ESC в записи было **ровно 11 мс шума на полной громкости** +(размах 33 671 — громче всего в прогоне), потом мгновенная тишина. 11 мс += один блок CBL (128 сэмплов = 11,7 мс), то есть один пропущенный долив: +`pop_shutdown` звал `closegraph`/`pop_bg_free`/`pop_kid_free` (а это +ESTEX на каждый атлас) ПРИ ОТКРЫТОМ звуке, насос не успевал, и железо +доигрывало несвежую половину. + +Лечение: `pop_sfx_close()` первым действием `pop_shutdown`. Проверено +второй записью — всплеска на выходе больше нет. Механизм тот же, из-за +которого звук глушится на время загрузки уровня. + +## 11. Приоритеты и перебиваемость: звук у оригинала НЕ «всегда перебивать» + +Пользователь услышал расхождение: у нас решётка обрывалась приземлением +Кида, в SDLPoP — доигрывала до звонкого конца, а приземления не было +слышно вовсе. Разбор исходника показал, что мы упустили ЦЕЛЫЙ МЕХАНИЗМ. + +### 11.1 Модель оригинала + +``` +play_sound(id) seg000:12C5 — только НОМИНИРУЕТ кандидата на кадр: + if next < 0 || prio[id] <= prio[next]: next = id + +play_next_sound() seg000:1304 — раз в кадр решает, запускать ли: + if next >= 0: + if !играет_что_то || + (перебиваем[текущий] && prio[next] <= prio[текущий]): + текущий = next; запустить + next = -1 // НЕ запустили -> номинант ВЫБРОШЕН, очереди нет +``` + +Три следствия, каждое слышно: + +- **Неперебиваемый звук доигрывает целиком.** У `gate_closing_fast` (6) + `interruptible = 0`, поэтому приземление Кида (17) в этот момент + пропадает совсем — не откладывается, а именно теряется. +- **Внутри кадра выживает важнейший.** Меньше `prio` — важнее; при + равенстве побеждает ПОСЛЕДНИЙ (сравнение `<=`). +- **Два источника не «чередуются как получится».** Челюсти (47, prio + 0x10) всегда важнее решётки (4, prio 0x32): решётка не может перебить + укус, а укус решётку — может. Отсюда и картина на ур. 9 к. 9, где + решётка звучит только в паузах между укусами. + +### 11.2 Что сделано у нас + +`pop_sfx_play` теперь только номинирует; запуск — в `pop_sfx_tick`, +который зовётся раз в кадр в конце отрисовки (там же, где оригинал зовёт +`play_next_sound`, seg000:954). Таблицы `snd_prio` (57 байт) и битовая +карта `snd_intr` (8 байт) — в резиденте, значения из SDLPoP С УЧЁТОМ +`fix_sound_priorities()`: в `config.h` SDLPoP `FIX_SOUND_PRIORITIES` +определён безусловно, значит сравниваемся мы с исправленным вариантом +(звук 10 → 0x0D, 48 → 0x15, 49 перебиваем). + +Створка двери уровня (15) — единственная запись, которую оригинал правит +на ходу: перебиваема при закрытии, нет при открытии (seg007:442/464). +Держим отдельным байтом `pop_sfx_slide_intr`, чтобы таблица осталась в +ПЗУ. Там же добавлен пропущенный `stop_sounds()` на завершении открытия +двери (seg007:455) — без него неперебиваемый съезд (1,6 с) блокировал бы +очередь. + +Звуки без оцифровки (музыка, длина 0) не номинируются вовсе — порт +проверки `if (NULL == sound_pointers[id]) return;`. Раньше такой id +глушил живой эффект. + +### 11.3 Проверка + +Записью MAME, старт уровня 1: + +| | всплески | что это | +|---|---|---| +| до | 135 мс + 210 мс | решётка, обрезанная приземлением на 80 мс | +| после | **один, 455 мс** | решётка целиком, корреляция огибающей со звуком 6 **+0,889** | + +Цена: резидент +~250 Б (таблицы + логика), куча ужалась с 347 до 134 Б — +довод в пользу давно назревшей реорганизации базовой памяти. + +## 12. Ворота: гейт слышимости и «решётка встала» (2026-08-20) + +Проверка на сцене, которую предложил пользователь — уровень 9, комната 9: +кнопка (1,8), челюсти (1,2), а ворота **в комнате 4, тайл (1,9)**, то есть +в комнате СЛЕВА. Нашлись три расхождения сразу. + +### 12.1 Гейт слышимости был неверный + +У нас стояло `audible = (room == cur_room)`. У оригинала +(`play_door_sound_if_visible`, seg007:1239) правило другое: + +- ворота в комнате СЛЕВА и в колонке 9 — СЛЫШНЫ (створка видна в шве); +- ворота в отрисованной комнате и НЕ в колонке 9 — слышны; +- особый случай: уровень 3, комната 2 — слышны всегда. + +Сцена 9/9 попадает ровно в первый пункт, поэтому спуск решётки у нас +молчал. Подъём при этом совпадал с оригиналом — потому что звук открытия +(5) идёт БЕЗ гейта (seg007:386, прямой `play_sound`). Эта асимметрия и была +подсказкой. + +Взят вариант под `FIX_GATE_SOUNDS` (условия через ИЛИ): в config.h SDLPoP +он определён безусловно. + +### 12.2 Потерян звук «решётка встала» (7) + +`gate_stop()` (seg007:05E3) зовётся из ТРЁХ мест `animate_door` и каждый раз +играет звук 7 через гейт слышимости: конец закрытия, открытие насовсем и +ветка «уже 0xFF». У нас во всех трёх стояло только `*type = -1` без звука. +Добавлено. Лязг после ОБЫЧНОГО открытия (seg007:395) остаётся без гейта — +там оригинал зовёт `play_sound` напрямую. + +### 12.3 Кнопка: у оригинала есть параметр playsound + +`trigger_button(playsound, ...)` — в трёх местах он нулевой: вход на уровень +(seg003:170), выход Джаффара (seg002:520) и зелье «открыть» (seg006:1890, у +нас не портировано). Мы играли щелчок всегда. Добавлен параметр `snd`. + +### 12.4 Почему щелчок кнопки слышно через раз — это НЕ баг + +Бюджет сцены 9/9 (длительности после пересчёта на 10 937,5 Гц): + +| звук | длительность | prio | +|---|---:|---:| +| челюсти (47) | 465 мс | 0x10 | +| решётка вниз (4) | 97 мс | 0x32 | +| решётка вверх (5) | 123 мс | 0x37 | +| решётка встала (7) | 75 мс | 0x30 | +| кнопка (3) | 106 мс | 0x66 | + +Цикл челюстей — 15 кадров = 1229 мс (замерено брейкпоинтом на номинации: +25 805 000 тактов между укусами). Значит укус занимает 465 мс, пауза 764 мс. +Кнопка (prio 0x66) перебить челюсти не может (0x66 > 0x10), поэтому слышна +только если нажатие попало в паузу — примерно в 6 случаях из 10. +Подтверждено пользователем на живой сцене. + +### 12.5 Грабли сцены + +Если игра стартует ПРЯМО в комнате с челюстями, они не заводятся сами: +нужно сходить Кидом на левую кнопку и вернуться. Это поведение оригинала +(trob челюстей создаётся событием), а не наш дефект — учитывать при +постановке автотестов. + +## 13. Повторный аудит игрового звука (2026-08-24) + +Проверены три независимых слоя: содержимое атласов, места вызова и живой +тракт `play -> tick -> CBL`. + +- В восьми `SND*.ATL` есть все 31 оцифрованных ресурса: `0..23`, `44..49` + и `51`; ненулевая страница/длина есть у каждой записи таблицы. +- Для всех 30 PCM-эффектов, которые могут возникать непосредственно в игре, + есть место вызова. Последним пропуском был звук 10 при столкновении + клинков; условие перенесено буквально из `check_sword_vs_sword` SDLPoP. + Звук 51 относится к сцене с принцессой, а не к игровому циклу. +- Звук 19 «Кид достал меч» проверен в MAME брейкпоинтами. В момент вызова + предыдущий PCM уже закончился (`sfx_left=0`), номинация дошла до + `pop_sfx_tick`, после чего курсор получил id 19, страницу 5, смещение + `0x1500` и длину 2816 байт. В этом прогоне его не подавляли решётка, + плиты, шаги или приоритеты. Если он всё ещё субъективно не слышен, искать + надо после выбора эффекта — в непрерывности CBL/громкости самого сэмпла. +- После продолжительного прогона title/intro/demo/menu CBL обслужил 3303 + блока и сообщил 0 программных недоливов (`cbl_requests=0x0CE7`, + `cbl_underruns=0`). Это исключает возврат `fill=0`, но само по себе не + измеряет запоздание прерывания внутри слишком длинной секции `DI`. +- Регрессия full-game на входе в Level 1 оказалась именно запозданием CBL: + `pop_level_switch` открывал его ДО блокирующего BIOS fade-in. Demo был + чистым, потому что включал палитру без fade. Теперь загрузчик оставляет + CBL закрытым, а caller открывает его после окончательной палитры/QuickLoad; + скрежет на Level 1 исчез в MAME. Однократный щелчок самого первого + `cbl_open` за всю MAME-сессию остаётся отдельной низкоприоритетной задачей. +- Открытие pause menu у SDLPoP беззвучно; движение играет 21, вход/выход + из подменю — 22, изменение настройки — 10. Эти вызовы перенесены. На + время полного копирования страницы и файловых операций CBL закрывается, + после flip открывается снова: аппаратная половина не должна повторять + старые данные и давать «скрежет». + +Отдельно остаётся игровая музыка и сигнальные мелодии без PCM: смерть +(`24/28`), начало/появление Shadow (`25`), встреча Jaffar (`29`), большое +и малое зелья (`30/33`), Shadow (`32`), победа/меч (`37`), перо (`39`), +конец уровня (`41`) и победа над Jaffar (`43`). Вызовы и AY-проигрыватель +для них ещё не реализованы; наличие всех PCM-эффектов эту задачу не закрывает. + + +## 4. МУЗЫКА: решение пересмотрено (2026-08-25) — путь C вместо A + +В §1б первым заходом был выбран **путь A** (ноты PC-спикера на AY), а путь +C (запись -> WAV -> CBL) стоил дорого из-за строки «нужен синтезатор на +хосте». Это обстоятельство отпало: у пользователя есть **готовые записи +DOS-версии** — `applications/PoP/PoP1_DOS_music` (flac/mp3/ogg/ogg_MT-32, +22 трека). Синтезировать нечего, остаётся `ffmpeg -ac 1 -ar 10937 -f u8`. + +### Что померено по этим записям + +| группа | длительность | PCM 10 937,5 Гц | +|---|---:|---:| +| всё вместе (22 трека) | 339 с | **3 625 КБ = 227 EMM-страниц** | +| игровые джинглы (10) | 57 с | 608 КБ | +| заставки и титры | 282 с | 3 017 КБ | +| финальный `won` один | 115 с | 1 233 КБ | + +Свободной EMM на старте ~3 440 КБ, так что вся музыка разом в память не +влезает и не должна: трек грузится под сцену и освобождается после. + +### Что сделано + +`toolchain/pop_pack_music.py` -> один файл `MUS\m.bin` на трек +(читается порциями по 16 КБ из одного открытого fd) + каталог +`pop_music_tbl.h`. Длина трека хранится **порциями по 128 байт**, +а не байтами: 169 КБ в uint16 не влезает, 1350 блоков — легко. + +`pop_music.c` (банк 9) грузит трек в EMM и ставит курсор; насос +`pop_sfx_fill` (резидент) получил **третий источник**: эффект важнее +музыки, музыка важнее тишины. Эффект музыку не сбрасывает — её курсор +стоит, пока эффект доигрывает, и она продолжается с места. + +Проверено в MAME записью звука: трек `story_1_absence` на первом экране +истории, корреляция огибающих с эталоном **0,836**, RMS 22,6 против 18,3 +(разница — 8-битное квантование). Эффекты двери в PV-сцене после него +звучат как прежде, то есть освобождение страниц и возврат к набору +эффектов работают. + +### Цена и что осталось + +* CBL один: пока играет музыка, эффектов нет. Для заставок это не важно + (их там не бывает), для игровых джинглов — открытый вопрос §1б.4. +* Резидент вырос на 83 байта (третий источник в насосе) плюс 25 байт + данных под курсор и таблицу страниц; запас W2 — 151 байт. Кучи в + приложении нет (`malloc` не слинкован), так что это чистый запас роста. +* Банк 9 занят на 88 %. Следующий модуль туда уже не влезет — либо + переносить, либо заводить банк 12. +* `won` (77 страниц) в POP_MUS_PAGES=20 не помещается: финал придётся либо + резать, либо стримить кусками по ходу. + + +## 5. СКРЕЖЕТ ПРИ BIOS-ВЫЗОВАХ: причина и решение (2026-08-25) + +Симптом: во время затемнения (fade) звук хрипел — одинаково с играющей +музыкой и в тишине. Пользователь заметил ключевое: **повтор тишины обязан +звучать тишиной**, значит дело не в недоливе буфера. + +### Как искали + +Отладочные клавиши, каждая делает ровно один кусок fade: + +| клавиша | что делала | результат | +|---|---|---| +| H | только ожидание 8 кадров | чисто | +| V | только чтение палитры | скрежет | +| G | затемнение целиком | скрежет | +| J | только запись палитры | скрежет | +| L | 512 раз `bios_get_place()` — видео не трогает | **скрежет** | + +`L` и решил вопрос: виновата не палитра, а **любой вызов BIOS**. + +### Причина + +Вход в BIOS — это `rst 8`, то есть `out ($7C),a`. В драйвере MAME он +правит `m_rom_sys` и вызывает `update_memory()`, которая перестраивает +**окно 0**: `m_pages[0]` + `m_bank_view0.select(1)`. ПЗУ ложится ПОВЕРХ +страничного регистра. + +Насос CBL брал окно взаймы именно у W0 (`_io_page_w0 = phys` + `OTIR` по +адресу < 0x4000). Пока BIOS работает, запись в порт `0x82` ничего не +меняет, и `OTIR` вычитывает ПЗУ, отдавая его в звук. + +Побочно выяснилось, почему `DI` вокруг BIOS помогал лишь иногда: он не +даёт войти в ISR (тогда блок просто пропускается, что неслышно), но в +обработчиках BIOS есть `EI`, так что защита негарантированная. + +### Решение + +Насос переведён на **W3** (идея пользователя): это окно управляется только +портом `0xE2`, подмену из прерывания никто не перекрывает, а BIOS во время +нашего ISR не исполняется — окно возвращается до выхода. + +```c +saved = _io_page_w3; +_io_page_w3 = phys; +cbl_push_otir((const void *)(0xC000u + ptr), n); +_io_page_w3 = saved; +``` + +После этого BIOS безопасен везде: и палитра, и любые другие функции. +Временный обход палитры мимо BIOS (`gfx_pal_write`) стал не нужен — он +остался в libbgi как более быстрый примитив (2,5 тыс. тактов на 64 цвета +против 10,8 тыс. у BIOS), но игра его не зовёт. + +Бонус: в W3 нет стаба восстановления окна, который в W0 занимал начало +страницы, — звуковые страницы можно использовать целиком. + +## 6. ВСЯ ЗАСТАВКА ОЗВУЧЕНА (2026-08-25) + +К `story_1_absence` добавлены остальные четыре трека заставки: +**54** intro_theme (титры), **50** story_2_princess, **53** +story_3_Jaffar_enters, **52** story_4_Jaffar_leaves (сцена с принцессой). +`MUS_IDS` в Makefile — 50 52 53 54 55, всего 1056 КБ на образе. + +### Два слота вместо одного + +Реплики оригинала идут ВСТЫК: следующая начинается там, где кончилась +предыдущая, паузы под загрузку нет. Поэтому `pop_music` держит два слота +EMM: `pop_music_load*` всегда пишет в НЕ играющий, `pop_music_play` +подменяет резидентную таблицу страниц и отпускает прошлый слот. Своей +копии таблицы слот не хранит — её и так держит блок EMM, `mem_get_page` +отдаёт номер по индексу (иначе −40 байт W2 у игры, а там их нет). + +Плюс **постраничная загрузка**: `pop_music_load_begin` / `_load_step` +читают по одной странице за вызов. Страница стоит 33 мс — четверть +логического кадра заставки (133 мс), поэтому подкачка следующей реплики +прямо посреди анимации не видна. Кто может позволить себе паузу (титры, +чёрный экран между сценами) — зовёт прежний `pop_music_load`. + +### Тайминги приведены к шкале оригинала + +Все длительности сцен взяты из SDLPoP в его тиках (60 Гц), а ждём мы +кадрами луча (~50 Гц). Пока сцены были немыми, разбег в 20 % не был +виден; с музыкой он слышен сразу — реплика кончается раньше картинки. +Введён `POP_T60(t)` (pop_cutscene.h), и на него переведены титры, +`intro_before_pv`, хвост после PV и пейсинг самой PV-сцены (счётчик +потраченных кадров луча против `POP_T60(tick)`, вместо прежних жёстких +четырёх кадров на логический). + +**Паузы-реплики.** Там, где оригинал ждёт конца сэмпла, у нас теперь +стоит реальная длина нашей записи: m50 — 831 тик, m53 — 985. Отсюда +новая шкала PV: конец m50 на 846, вход Джафара (m53) на 1046, уход +(m52) на 2073, конец сцены 2500 тиков (было 1959). + +**Fade перед PV** (вопрос пользователя: наш fade короче, 4 ступени против +64). Совпасть должен момент полной темноты, считая от пуска m55: +у SDLPoP это 80 (transition) + 600 (wait) + 128 (fade_out_2: 0x40 шагов +по 2 тика); у нас переход занимает 80 кадров луча = 96 тиков, а fade — +5 тиков. Отсюда `WAIT = 80 + 600 + 128 − 96 − 5 = 707` тиков. Дальше и +там и там экран уже чёрный, а трек доигрывает: этой паузой заставка и +стыкуется с PV. + +**Музыка переживает смену сцен.** m54 звучит с титров и до первого +экрана истории (`pop_intro_show` больше не глушит CBL на входе), m52 +начинается в PV и доигрывает уже на экране «свадьбы» — как seg000:2051. + +Проверено в MAME: цепочка 54 → 55 → 50 → 53 → 52 отыгрывается целиком, +курсор `pop_mus_id` меняется ровно на своих кадрах, интро доходит до +демо-режима. Слуховая проверка (нет ли хрипа от диска при играющей +музыке) — за пользователем. + +## 7. МУЗЫКА ПО ХОДУ ИГРЫ (2026-08-26) + +Звуки 24..43 в наборе оцифровки ПУСТЫЕ — в оригинале это Adlib-музыка, и +в digisnd её нет вовсе. На этом и построено подключение: `pop_sfx_play` +для звука с нулевой длиной не пытается его играть, а кладёт НОМЕР в +`pop_mus_req` (один байт). Заявку разбирает `pop_music_service()` — один +вызов на кадр из любого цикла (игрового, интро, катсцены); всё чтение с +диска живёт там. + +**Стриминг вместо загрузки.** Ждать полной загрузки джингла нельзя — это +фриз на треть секунды посреди игры. `pop_music_stream` читает ПЕРВУЮ +страницу (33 мс) и сразу пускает трек: она звучит 1,5 с, а следующая +читается те же 33 мс — запас сорокакратный. Остальные доливаются по +одной за кадр, пока `pop_music_loading()`. Номера страниц известны сразу +после `mem_alloc_pages`, поэтому таблица для насоса заполняется целиком — +данные появятся раньше, чем насос до них дойдёт. + +**Что и где играет** (номера и места — из SDLPoP): + +| трек | событие | место у нас | +|------|---------|-------------| +| 24 / 28 / 32 | смерть: обычная / в бою / от руки тени | `ctrl_kid_death` | +| 25 | вступление 1-го уровня (Кид сидит), тень 6-го | `control_crouched`, `guards.c` | +| — | НА ДЕМО-УРОВНЕ музыки нет вовсе: там одни эффекты | гейт в `pop_music_service` | +| 27 / 35 / 40 | сцены перед 2/4/6/12, 8/9, «времени мало» | `pop_pre_cutscene_show` | +| 29 | встреча с Джафаром | `pop_meet_jaffar` | +| 30 / 33 | большая склянка / малая | `pop_proc_get_object` | +| 36 | время вышло | `pop_time_expired_show` | +| 37 / 43 | меч найден, страж убит / смерть Джафара | `pop_proc_get_object`, `on_guard_killed` | +| 39 | перо (медленное падение) | `pop_proc_get_object` | +| 41 / 32 | конец уровня / конец 4-го (тень) | опкод SND_LEVEL в `play_seq` | +| 26 | встреча с принцессой | `cut_ending` | + +**Вступление первого уровня — автомат, а не «звук при приседе»** (seg005:02EB). +Пока `need_level1_music` не ноль, `control_crouched` НЕ ЧИТАЕТ управление: +Кид сидит, тема играет, и лишь когда она смолкла, он может встать. Наша +первая версия просто играла трек при первом приседе — и тема догоняла +игрока посреди уровня (пробежал, спрыгнул, присел — заиграла). Признак +«ещё звучит» берём у курсора насоса `pop_mus_left`: он резидентный, и если +музыка выключена, курсор остаётся нулём — Кид просто встаёт. + +Темы, которые звучат один раз за заход на уровень (вступление 1-го, тень +6-го), сбрасывает `pop_music_level_start()` из `pop_start_level`. Оригинал +для этого портит переменную двери (`leveldoor_open = 0x4D`) — у нас на это +есть свои два байта. + +**ГДЕ КОНЧАЕТСЯ МУЗЫКА СЦЕНЫ** (уточнено 2026-08-26). Сначала мы отдали +трек «доигрывать в игре»: у оригинала load_intro после сцены просто гасит +экран и возвращает управление. На слух оказалось хуже, чем в оригинале — +музыка спотыкается: загрузка уровня (ESTEX плюс сборка комнаты) не даёт +насосу долить блок вовремя. У DOS-версии этой проблемы нет, там звук +живёт своей жизнью на аппаратуре. + +Поэтому дослушиваем ПОД ЧЁРНЫМ ЭКРАНОМ, до отрисовки уровня: следующий +load_intro у оригинала и так начинается с ожидания тишины (seg001:681), то +есть к новому уровню трек в любом случае смолкает. Пропуск сцены обрывает +и музыку — игрок нажал клавишу, чтобы идти дальше. + +**ДЛИТЕЛЬНОСТЬ FADE.** fade_in_1/fade_out_1 — это 64 шага палитры по два +тика, 128 тиков = 2,13 с; сцена перед уровнем 2 занимает с ними около семи +секунд. Наши четыре ступени укладывались в восемь сотых секунды, и сцена +выходила втрое короче. Теперь `INTRO_FADE = POP_T60(128)`, а ступеней в +`pop_ui_palette_dim` тридцать две вместо четырёх: на четырёх растянутых +ступенях затемнение выглядело бы скачками. Половина от оригинальных 64 — +на глаз от них не отличается (ступень каждые 66 мс), а вот шестнадцать уже +видно. Сумма «fade in + сцена + fade out» при этом совпадает с оригиналом +сама собой: длительность каждого fade та же, что у fade_*_1. + +**Цена ступени** (замеры в MAME 2026-08-26, такты 21 МГц; кадр 430 000): + +| версия | такты | что изменилось | +|--------|-------|----------------| +| исходная | 2 440 000 | снимок копировался побайтовым циклом на C | +| + таблица яркости на стеке | 1 250 000 | 768 умножений uint16 заменены 256 сложениями | +| + memcpy для снимка | 487 000 | LDIR вместо цикла — главный выигрыш | + +Из оставшихся 487 тысяч 136 тысяч — заливка палитры через BIOS (8 вызовов +`gfx_pal_load` по 17 000). Дальше можно было бы хранить готовые таблицы +яркости файлом, но при 1,2 мс на построение это уже незаметно. + +ВАЖНО: ступень пересчитывается только когда она СМЕНИЛАСЬ. Наивный цикл +«ступень на каждый кадр» звал пересчёт сто раз и растягивал fade до +десяти с лишним секунд. + +## 8. ПОТОКОВЫЙ ТРЕК: ФИНАЛЬНАЯ ТЕМА (2026-08-26) + +`won` (56) — 115 с, 1,2 МБ, 78 страниц EMM. В память он не влезает ни при +каком бюджете, поэтому играется КОЛЬЦОМ из шести страниц (96 КБ = 9 с): насос +идёт по кругу, а `pop_music_service` дочитывает файл в те страницы, которые +насос уже прошёл. + +**Кто кого догоняет.** Страница звучит 1,5 с, а читается 33 мс — запас +сорокакратный. Дистанция считается без отдельных счётчиков: страница ровно +128 блоков насоса, поэтому проигранных страниц = (всего блоков − осталось) +/ 128. Пока прочитано меньше, чем проиграно плюс размер кольца, в кольце +есть свободный слот. Файл читается ПОСЛЕДОВАТЕЛЬНО, без `lseek`. + +**Что пришлось учесть.** + +* Насос заворачивает страницу только при `pop_mus_ring != 0`; конец трека + по-прежнему определяет `left`. Обычный трек этой ветки не касается. +* Кольцо обязано сниматься при любом обычном запуске (`pop_music_play`, + `_stream`, `_load_begin`): иначе следующий трек играет по кругу первых + шести страниц — поймано на титрах сразу после победы. +* Живые сцены комнаты принцессы открывают CBL сами (`cut_begin`): + `pop_ending_show` глушит звук первым действием, и «arrived to princess» + (26) не звучал вовсе. +* Тема дослушивается до конца (прерывается клавишей), как `while + (check_sound_playing() && !key_test_quit())` в seg001:637. У оригинала + между титрами и этим ожиданием стоит ввод имени в таблицу рекордов — + когда он появится у нас, ожидание переедет за него (задача HOF-ENTRY). + +Проверено в MAME на сборке `LEVEL=14`: после встречи с принцессой звучит +тема победы (`pop_mus_id` = 56, `pop_mus_ring` = 6), курсор уходит далеко +за размер кольца — то есть подкачка успевает. diff --git a/applications/SprPoP/docs/status_line_text.md b/applications/SprPoP/docs/status_line_text.md new file mode 100644 index 0000000..75cd199 --- /dev/null +++ b/applications/SprPoP/docs/status_line_text.md @@ -0,0 +1,218 @@ +# Текст в нижней статус-строке (строке HP) — полная инвентаризация SDLPoP + +Разбор `SDLPoP/src/` на 2026-08-25. Цель — знать ВЕСЬ набор сообщений, +которые оригинал печатает в ту же полосу, где нарисованы деления HP, +и правила их появления/исчезновения. Это входные данные для порта: +у нас пока туда пишется только `GAME PAUSED`. + +--- + +## 1. Геометрия: одна полоса на HP и на текст + +``` +rect_bottom_text = { top 193, left 70, bottom 202, right 250 } // data.h:217 +display_text_bottom: draw_rect(чёрным) + show_text(halign_center, valign_bottom) +``` + +* Деления HP **Кида** — от `x = 0` вправо, шаг 7, максимум 10 → занимают `x 0..69`. +* Деления HP **стража** — от `x = 314` влево, шаг 7, максимум 10 → занимают `x 245..320`. +* Текст живёт РОВНО в промежутке `x 70..250` и по X с делениями не пересекается. +* По Y деления на `y = 194..200`, текст (`valign_bottom` к 202) — на `y = 195..201`, + то есть на строку ниже. Именно поэтому в оригинале текст выглядит «сидящим» + чуть ниже стрелок HP. + +**У нас**: `POP_HP_Y = 194` (`pop_cdraw.h`), экран сдвинут на `POP_YOFF = 28`, +базовая линия крупного шрифта `POP_YOFF + POP_HP_Y + 8 = 230` — силуэт +ложится на `223..229`, то есть та же картинка. + +## 2. Два примитива и два таймера + +| Имя | Что делает | +|-----|------------| +| `display_text_bottom(text)` (seg008:2644) | стереть прямоугольник цветом 0 и напечатать текст по центру | +| `erase_bottom_text(arg)` (seg008:266D) | стереть прямоугольник; при `arg != 0` ещё и обнулить оба таймера | +| `text_time_remaining` | сколько игровых тиков сообщение ещё висит; 0 — ничего не висит | +| `text_time_total` | **идентификатор сообщения**, а не только его длительность | + +Обработка тика — в `draw_game_frame`/`idle` (seg000:956). Комментарий в +оригинале прямой: *«Note: texts are identified by their total time!»* Значения +`text_time_total`, у которых есть особое поведение: + +| `total` | Смысл | Что происходит по истечении | +|---------|-------|------------------------------| +| 12 | «1 SECOND LEFT» | обычное стирание | +| 24 | обычное короткое сообщение | обычное стирание | +| 36 | смерть на демо-уровне (0) или на уровне зелий (15) — **текста нет** | `start_game()` — рестарт игры | +| 288 | «Press Button to Continue» | `start_game()` — рестарт игры | +| 1188 | защита от копирования (уровень 15) | **не убывает и не исчезает** | + +Мигание: при `total == 288` и `remaining < 72` сообщение мигает с периодом 12 +тиков — 4 тика видно (`blink_frame <= 3`), 8 нет; в кадре `blink_frame == 3` +заново печатается текст и играет звук 38 (`sound_38_blink`). + +Сброс: `init_game()` (seg003:32) обнуляет оба таймера и `is_show_time` — то есть +любое сообщение умирает на старте уровня. + +--- + +## 3. Полный список сообщений + +### 3.1. Состояние программы + +| Текст | Где | Таймер | +|-------|-----|--------| +| `GAME PAUSED` | seg000:1769, пока `is_paused` | **без таймера**: печатается на входе в паузу, `erase_bottom_text(1)` на выходе (seg000:1784) | + +### 3.2. Уровень и оставшееся время (`show_level` / `show_time`, seg008) + +| Текст | Условие | `total` | +|-------|---------|---------| +| `LEVEL %d` | `show_level()` при старте уровня; только `1..12` (`hide_level_number_from_level = 14`), не при `seamless`; уровень 13 показывается как **12** (`level_13_level_number`) | 24, дальше сразу `is_show_time = 1` | +| `%d MINUTES LEFT` | каждая минута, кратная 5, и каждая из последних 5 | 24 | +| `%d SECONDS LEFT` | последняя минута, раз в 12 тиков | 24 | +| `1 SECOND LEFT` | остался 1 с | **12** | +| `TIME HAS EXPIRED!` | `rem_min == 0` | 24 | +| `%d MINUTES PASSED` / `1 MINUTE PASSED` | только SDLPoP (`ALLOW_INFINITE_TIME`), при отрицательном таймере | 24 | + +Что взводит `is_show_time` (все → следующий кадр печатает время): + +* **Space** — seg000:612, штатная клавиша оригинала «сколько осталось»; +* читы **`-`/`+` numpad** (изменение времени) — seg000:762 / 777, при этом + таймеры сообщения обнуляются, чтобы новое напечаталось немедленно; +* **смерть Джафара** — `on_guard_killed()` seg006:1936, уровень 13 + (`jaffar_victory_level`): вспышка + показать время; +* истечение очередной минуты — seg008:1796; +* сразу после `show_level()`. + +Обнуляет `is_show_time`: `play_kid()` при смерти (seg006:1365) и +`show_copyprot(1)` (seg000:2385). + +### 3.3. Смерть Кида + +| Текст | Где | `total` | +|-------|-----|---------| +| `Press Button to Continue` | `play_kid()` seg006:1383 — умер на обычном уровне | **288** (мигает, затем рестарт игры) | +| *(без текста)* | тот же код, но уровень 0 (демо) или 15 (зелья) | **36** (тихая пауза, затем рестарт игры) | + +Стирается: `fell_out()` (seg006:1342, упал из комнаты 0) и чит **R** +(воскрешение, seg000:783) — оба зовут `erase_bottom_text(1)`. + +### 3.4. Сохранение и загрузка + +| Текст | Клавиша | `total` | +|-------|---------|---------| +| `GAME SAVED` / `UNABLE TO SAVE GAME` | Ctrl+G (`save_game`, seg000:2211) | `total` не ставится, `remaining = 24` | +| `QUICKSAVE` / `NO QUICKSAVE` | F6 (расширение SDLPoP, seg000:497) | 24 | +| `QUICKLOAD` / `NO QUICKLOAD` | F9 (расширение SDLPoP, seg000:514) | 24 | + +### 3.5. Ответы на клавиши (`answer_text` → `need_show_text`, все `total = 24`) + +| Текст | Клавиша | +|-------|---------| +| `SOUND ON` / `SOUND OFF` | Ctrl+S | +| `KEYBOARD MODE` | Ctrl+K | +| `JOYSTICK MODE` / `JOYSTICK NOT FOUND` / `JOYSTICK UNAVAILABLE` | Ctrl+J | +| `PRINCE OF PERSIA V1.0` (в SDLPoP заменено на `SDLPoP v%s`) | Ctrl+V | +| `SDL COMP v… LINK v…` | Ctrl+C — только SDLPoP | + +### 3.6. Отладочные читы (`cheats_enabled`, `total = 24`) + +| Текст | Клавиша | Смысл | +|-------|---------|-------| +| `S%d L%d R%d A%d B%d` | `C` | номер отрисованной комнаты и её соседей L/R/A/B | +| `AL%d AR%d BL%d BR%d` | Shift+`C` | диагональные соседи | + +### 3.7. Защита от копирования (только уровень 15) + +| Текст | Где | `total` | +|-------|-----|---------| +| `WORD %d LINE %d PAGE %d` | `show_copyprot(1)` seg000:2389 | **1188** — висит, пока не сменится уровень | + +### 3.8. Только SDLPoP, в оригинале 1989 отсутствует + +| Текст | Где | +|-------|-----| +| `RECORDING`, `REPLAY SAVED`, `REPLAY CANCELED` | replay.c:599/626/628 | +| имя файла скриншота | screenshot.c:62 | + +--- + +## 4. Что из этого касается нашего порта + +Реализовано (`pop_status.c`, таймер 24 тика как у оригинала): + +* `GAME PAUSED` — без таймера, рисует само меню (`pop_menu.c`); +* `QUICKSAVE` / `NO QUICKSAVE`, `QUICKLOAD` / `NO QUICKLOAD` — заявка стоит + в `pop_qsave_process`, то есть в единственном месте, где известно, что + именно делали. Лейбл печатается ДО дисковой операции — осознанное + расхождение, см. `impl_diff.md`; +* `SOUND ON` / `SOUND OFF` — Ctrl+S; +* `LEVEL %d` — порт `show_level()` целиком: демо-уровень 0 и номера от 14 + молчат, тринадцатый показывается двенадцатым, бесшовный переход 12→13 + пропускается и гасит флаг за собой. + +* вся группа времени — `N MINUTES LEFT`, `N SECONDS LEFT`, `1 SECOND LEFT`, + `TIME HAS EXPIRED!`. Флаг `pop_show_time` (порт `is_show_time`) взводит + само ядро таймера на круглых пятёрках и каждую секунду последней минуты, + а также старт уровня и читы времени; значение 2 означает «перебить + текущую строку», как оригинал делает в последнюю минуту; +* `Press Button to Continue` — висит бессрочно (`MSG_HOLD`), уровень + перезапускает кнопка. Расхождение с оригиналом, см. `impl_diff.md`. + +Пока НЕ печатается: + +* номера комнат (`C`/Shift+`C`) — у нас отдельная отладочная строка; +* copy protection и SDLPoP-расширения (replay, скриншоты) — не нужны. + +Отладочная строка вдобавок показывает оставшееся время `##:##` у правого +края. На табло уходит `minutes-1`: у оригинала `rem_min` — это НОМЕР идущей +минуты, а не остаток целых (старт 60 при `rem_tick` 719 = «почти 60:00»). +Секунды считаются делением раз в 12 кадров, а не каждый кадр. + +Нам не нужно: copy protection (уровень 15 исключён из порта — см. +`full_game_plan.md`), joystick-режимы, replay, скриншоты. + +Механика, которую придётся портировать целиком, если брать группу времени: +пара таймеров `text_time_total`/`text_time_remaining` с семантикой +«идентификатор сообщения» — иначе не воспроизвести ни мигание, ни рестарт по +истечении 36/288. + +--- + +## 5. Цена вывода и что делать, если упрёмся + +Блит одного глифа стоит ~4,6 тыс. тактов почти независимо от размера — это +цена вызова, а не пикселей (memory `blit_cost_model`). Полсотни символов = +полкадра. Что уже сделано в `pop_status.c` / `pop_ui.c`: + +* **change-driven**: пока показанное не изменилось, не рисуем вовсе; +* **по полям**: смена комнаты — две цифры (~9 тыс. тактов, 2% кадра), а не + вся строка; подписи рисуются только при полной инвалидации; +* **пробелы не блитятся**: их глиф целиком прозрачен, а стоит как буква — + на полной отладочной строке это девять сэкономленных блитов; +* **заливка только поля** при входе в комнату (`pop_screen_fill_field`): + борта от комнаты к комнате не меняются, это и четверть заливки, и то, что + обе полосы вход переживают. + +Запас, если бюджета всё же не хватит (идеи пользователя, 2026-08-25): + +1. **Растянуть вывод на несколько кадров, не показывая полуготовую строку.** + Печатать по нескольку букв за кадр, держа цвет шрифта чёрным (отдельный + индекс палитры), а по готовности подменить этот индекс на белый — строка + появится целиком и мгновенно. Стоит ноль байт памяти и укладывается в + нашу же технику «два разных чёрных» (`POP_COL_OUTSIDE`). +2. **Собирать строку в один спрайт** в свободном хвосте страницы шрифта и + блитить одним вызовом. Дороже по подготовке (~35 тыс. тактов на + копирование), но выгодно там, где строка ЦЕЛИКОМ меняется каждый раз. + Для меню этот путь уже рассматривался и был отвергнут; для статус-строк + он имеет смысл только вместе с п.1. + +Про QuickSave/QuickLoad оптимизация не нужна вовсе: там игра и так стоит на +время дисковой операции. + +## 6. Ловушка: свисающие глифы + +Зона стирания текста обязана захватывать строку НИЖЕ базовой линии. В малом +шрифте `'p'` имеет высоту 7 при ascent 5, `','` — 6: они свисают под базовую +линию. Стирание ровно до неё оставляло от хвоста «p» в «Speed:» одинокую +точку (поймано в MAME 2026-08-25). diff --git a/applications/SprPoP/gen/bg_arc.h b/applications/SprPoP/gen/bg_arc.h new file mode 100644 index 0000000..581908e --- /dev/null +++ b/applications/SprPoP/gen/bg_arc.h @@ -0,0 +1,34 @@ +/* bg_arc.h — индексы в архиве BG. СГЕНЕРИРОВАН + * (toolchain/pop_pack_arc.py), руками не править. */ +#ifndef BG_ARC_H +#define BG_ARC_H + +#define ARC_BG_COUNT 25 + +#define ARC_BG_POP_ENV0 0 +#define ARC_BG_POP_ENV1 1 +#define ARC_BG_POP_ENV2 2 +#define ARC_BG_POP_ENV3 3 +#define ARC_BG_POP_ENV4 4 +#define ARC_BG_POP_ENV5 5 +#define ARC_BG_POP_ENV6 6 +#define ARC_BG_POP_ENV7 7 +#define ARC_BG_POP_ENV8 8 +#define ARC_BG_POP_ENV9 9 +#define ARC_BG_POP_WALL 10 +#define ARC_BG_POP_FORE 11 +#define ARC_BG_PAL_ENV0 12 +#define ARC_BG_PAL_ENV1 13 +#define ARC_BG_PAL_ENV2 14 +#define ARC_BG_PAL_ENV3 15 +#define ARC_BG_PAL_ENV4 16 +#define ARC_BG_PAL_ENV5 17 +#define ARC_BG_PAL_ENV6 18 +#define ARC_BG_PAL_ENV7 19 +#define ARC_BG_PAL_ENV8 20 +#define ARC_BG_PAL_ENV9 21 +#define ARC_BG_PAL_WALL 22 +#define ARC_BG_PAL_FORE 23 +#define ARC_BG_POP_POT 24 + +#endif diff --git a/applications/SprPoP/gen/guard_arc.h b/applications/SprPoP/gen/guard_arc.h new file mode 100644 index 0000000..f930261 --- /dev/null +++ b/applications/SprPoP/gen/guard_arc.h @@ -0,0 +1,19 @@ +/* guard_arc.h — индексы в архиве GUARD. СГЕНЕРИРОВАН + * (toolchain/pop_pack_arc.py), руками не править. */ +#ifndef GUARD_ARC_H +#define GUARD_ARC_H + +#define ARC_GUARD_COUNT 10 + +#define ARC_GUARD_G0 0 +#define ARC_GUARD_G1 1 +#define ARC_GUARD_G2 2 +#define ARC_GUARD_G3 3 +#define ARC_GUARD_G4 4 +#define ARC_GUARD_G0_V 5 +#define ARC_GUARD_G1_V 6 +#define ARC_GUARD_G2_V 7 +#define ARC_GUARD_G3_V 8 +#define ARC_GUARD_G4_V 9 + +#endif diff --git a/applications/SprPoP/gen/kid_arc.h b/applications/SprPoP/gen/kid_arc.h new file mode 100644 index 0000000..db3d846 --- /dev/null +++ b/applications/SprPoP/gen/kid_arc.h @@ -0,0 +1,67 @@ +/* kid_arc.h — индексы в архиве KID. СГЕНЕРИРОВАН + * (toolchain/pop_pack_arc.py), руками не править. */ +#ifndef KID_ARC_H +#define KID_ARC_H + +#define ARC_KID_COUNT 58 + +#define ARC_KID_KID0 0 +#define ARC_KID_KID1 1 +#define ARC_KID_KID2 2 +#define ARC_KID_KID3 3 +#define ARC_KID_KID4 4 +#define ARC_KID_KID5 5 +#define ARC_KID_KID6 6 +#define ARC_KID_KID7 7 +#define ARC_KID_KID8 8 +#define ARC_KID_KID9 9 +#define ARC_KID_KID10 10 +#define ARC_KID_KID11 11 +#define ARC_KID_KID12 12 +#define ARC_KID_KID13 13 +#define ARC_KID_KID14 14 +#define ARC_KID_KID15 15 +#define ARC_KID_KID16 16 +#define ARC_KID_KID17 17 +#define ARC_KID_KID18 18 +#define ARC_KID_KID19 19 +#define ARC_KID_KID20 20 +#define ARC_KID_KID21 21 +#define ARC_KID_KID22 22 +#define ARC_KID_KID23 23 +#define ARC_KID_KID24 24 +#define ARC_KID_KID25 25 +#define ARC_KID_KID26 26 +#define ARC_KID_KID27 27 +#define ARC_KID_KID0_V 28 +#define ARC_KID_KID1_V 29 +#define ARC_KID_KID2_V 30 +#define ARC_KID_KID3_V 31 +#define ARC_KID_KID4_V 32 +#define ARC_KID_KID5_V 33 +#define ARC_KID_KID6_V 34 +#define ARC_KID_KID7_V 35 +#define ARC_KID_KID8_V 36 +#define ARC_KID_KID9_V 37 +#define ARC_KID_KID10_V 38 +#define ARC_KID_KID11_V 39 +#define ARC_KID_KID12_V 40 +#define ARC_KID_KID13_V 41 +#define ARC_KID_KID14_V 42 +#define ARC_KID_KID15_V 43 +#define ARC_KID_KID16_V 44 +#define ARC_KID_KID17_V 45 +#define ARC_KID_KID18_V 46 +#define ARC_KID_KID19_V 47 +#define ARC_KID_KID20_V 48 +#define ARC_KID_KID21_V 49 +#define ARC_KID_KID22_V 50 +#define ARC_KID_KID23_V 51 +#define ARC_KID_KID24_V 52 +#define ARC_KID_KID25_V 53 +#define ARC_KID_KID26_V 54 +#define ARC_KID_KID27_V 55 +#define ARC_KID_SWORD 56 +#define ARC_KID_SWORD_V 57 + +#endif diff --git a/applications/SprPoP/gen/kid_atlas.h b/applications/SprPoP/gen/kid_atlas.h new file mode 100644 index 0000000..709400b --- /dev/null +++ b/applications/SprPoP/gen/kid_atlas.h @@ -0,0 +1,41 @@ +/* kid_atlas.h — раскладка атласов Kid. Сгенерировано pop_pack_kid.py. */ +#ifndef KID_ATLAS_H +#define KID_ATLAS_H +#define KID_SHIFT 3 +#define KID_MASK 7 +#define KID_PAGES 28 +#define KID_PAL 0x70 +static const char *const kid_atl[KID_PAGES] = { + "kid0.atl", + "kid1.atl", + "kid2.atl", + "kid3.atl", + "kid4.atl", + "kid5.atl", + "kid6.atl", + "kid7.atl", + "kid8.atl", + "kid9.atl", + "kid10.atl", + "kid11.atl", + "kid12.atl", + "kid13.atl", + "kid14.atl", + "kid15.atl", + "kid16.atl", + "kid17.atl", + "kid18.atl", + "kid19.atl", + "kid20.atl", + "kid21.atl", + "kid22.atl", + "kid23.atl", + "kid24.atl", + "kid25.atl", + "kid26.atl", + "kid27.atl", +}; +#define KID_PAL_FILE "kid.pal" +#define KID_SWORD_ATL "sword.atl" /* chtab_0: меч в руке */ +#define KID_SWORD_ID0 0 /* индекс в атласе = sword_tbl.id - ID0 */ +#endif diff --git a/applications/SprPoP/gen/kid_data.h b/applications/SprPoP/gen/kid_data.h new file mode 100644 index 0000000..09debc0 --- /dev/null +++ b/applications/SprPoP/gen/kid_data.h @@ -0,0 +1,77 @@ +/* kid_data.h — данные анимации Kid (frame_table + seqtbl + + * offsets), извлечено toolchain/pop_extract_kid_data.py из + * SDLPoP. НЕ править вручную. seqtbl адресуется базой + * SEQTBL_BASE: kid_seqtbl[addr - SEQTBL_BASE]. */ +#ifndef KID_DATA_H +#define KID_DATA_H +#include + +#define SEQTBL_BASE 0x196Eu + +/* {image, dx, dy, flags, sword}; chtab = sword>>6 (0=kid). + * САМА таблица кадров и seqtbl лежат в kid.ani (EMM- + * страница); здесь только тип и размеры. */ +typedef struct { uint8_t image; int8_t dx, dy; uint8_t flags, sword; } kframe; +#define KID_NFRAMES 241 +#define KID_SEQTBL_LEN 2310 + +/* Раскладка kid.ani: кадры (KID_NFRAMES*5), затем seqtbl. */ +#define KID_BIN_FRAMES_OFF 0 +#define KID_BIN_SEQTBL_OFF 1205 +/* Таблица кадров СТРАЖА (frame_tbl_guard, seg006:372): индекс + * frame + add_frame - 149 (add_frame=70 для кадров 102..106). */ +#define KID_NGFRAMES 41 +#define KID_BIN_GFRAMES_OFF 3515 +/* Таблица кадров КАТСЦЕН (frame_tbl_cuts, seg006:417): + * Princess/Vizier, индекс — номер кадра как есть. Набор + * спрайтов выбирает sword>>6 (1 = PV1 res800, 2 = PV2 res900). */ +#define KID_NCFRAMES 86 +#define KID_BIN_CFRAMES_OFF 3720 + +#define KID_NSEQ 115 +#define KID_SEQ_OFF_INIT { \ + 0x0000,0x1973,0x19A0,0x1A93,0x1ACD,0x1B39,0x1B5A,0x1BDB, \ + 0x1C54,0x1CA1,0x1D04,0x1D25,0x1D49,0x1D4F,0x1D68,0x1DF6, \ + 0x1C84,0x202B,0x1C1C,0x1C01,0x205A,0x1C38,0x209C,0x1D36, \ + 0x1C69,0x1CD1,0x1B04,0x1B1A,0x1D7D,0x1F9E,0x1F92,0x1F82, \ + 0x1F72,0x1F5D,0x1F48,0x1F33,0x1F19,0x1F13,0x1EF7,0x1EDA, \ + 0x1EBB,0x1E9C,0x1E7D,0x1B53,0x1E59,0x1E06,0x1E3B,0x1DFC, \ + 0x1D9B,0x1FB3,0x1FA7,0x20BE,0x20D1,0x20D4,0x20C9,0x19C4, \ + 0x1A13,0x1A2E,0x1A3C,0x1A7C,0x1A83,0x1A63,0x1A5E,0x1A68, \ + 0x1A6E,0x1A75,0x1A5A,0x19F9,0x1CEC,0x1A54,0x20DD,0x20AE, \ + 0x1E78,0x1CDC,0x19DC,0x1A42,0x1A07,0x19A8,0x2009,0x1B1B, \ + 0x1A8B,0x1B85,0x1BA1,0x1BBF,0x196E,0x20A9,0x1A22,0x19A6, \ + 0x19AC,0x1B2D,0x19C1,0x1FCA,0x1FDA,0x1FFB,0x2195,0x2132, \ + 0x214F,0x2166,0x2199,0x21A8,0x216D,0x226E,0x2136,0x21BC, \ + 0x1BFA,0x2245,0x2253,0x225B,0x21C4,0x21C0,0x21E6,0x21EA, \ + 0x21FC,0x2240,0x2257, \ +} + +/* sword_tbl (seg006:2047): клинок отдельным спрайтом chtab_0. + * Индекс — sword_frame (кадр.sword & 0x3F), id 255 = клинка нет. + * Смещения x/y — ЭКРАННЫЕ (применяются после calc_screen_x_coord), + * x — «вперёд» по направлению персонажа. */ +#define KID_NSWORD 51 +#define KID_SWORD_ID_INIT { \ + 0xFF,0x00,0x05,0x01,0x02,0x06,0x07,0x03,0x04,0x1E,0x08,0x09, \ + 0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x13,0x11, \ + 0x11,0x07,0x01,0x1C,0x08,0x04,0x04,0x0A,0x0D,0x0D,0x0D,0x14, \ + 0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0xFF,0xFF,0x1D,0x08,0x1F, \ + 0x20,0x21,0x08, \ +} +#define KID_SWORD_DX_INIT { \ + 0,0,-9,7,17,7,0,17,16,12,13,7, \ + 10,10,22,28,13,0,0,21,14,21,22,7, \ + 15,0,7,14,7,6,9,11,24,19,21,7, \ + 14,14,14,28,28,21,14,14,21,0,8,14, \ + 14,7,8, \ +} +#define KID_SWORD_DY_INIT { \ + 0,-9,-29,-25,-26,-14,-5,-16,-19,-9,-34,-25, \ + -16,-11,-21,-23,-35,-38,-29,-19,-23,-22,-23,-13, \ + -18,-8,-27,-28,-27,-23,-21,-18,-23,-23,-23,-32, \ + -32,-31,-29,-28,-28,-25,-22,-25,-25,-16,-37,-24, \ + -24,-14,-37, \ +} + +#endif diff --git a/applications/SprPoP/gen/pop_bg_atlas.h b/applications/SprPoP/gen/pop_bg_atlas.h new file mode 100644 index 0000000..9e861ea --- /dev/null +++ b/applications/SprPoP/gen/pop_bg_atlas.h @@ -0,0 +1,47 @@ +/* pop_bg_atlas.h — раскладка атласов статического фона PoP. + * Сгенерировано toolchain/pop_pack_bg.py — НЕ править вручную. + * + * Прямая адресация (ноль remap-таблиц в W2): + * ENV фон id N -> atlas env_bg[N>>4], idx N&15 + * WALL id N -> atlas wall, idx N + * FORE id N -> atlas fore, idx N + * + * ДВА ТАЙЛСЕТА (порт tbl_envir_ki[tbl_level_type], seg000:1108): + * 0 = подземелье (pop_*), 1 = дворец (pal_*). Набор id и раскладка + * у них ОДНИ И ТЕ ЖЕ — меняются только файлы и 32 записи палитры + * (env 0x50..0x5F, wall 0x60..0x6F), см. *tile.pal. + */ +#ifndef POP_BG_ATLAS_H +#define POP_BG_ATLAS_H + +#define POP_ENV_SHIFT 4 +#define POP_ENV_MASK 15 +#define POP_ENV_PAGES 10 +#define POP_TILESETS 2 + +/* Палитра: env-слоты, wall-слоты (спрайт-пиксель i -> база+i). */ +#define POP_PAL_ENV 0x50 +#define POP_PAL_WALL 0x60 + +/* Имена файлов атласов по тайлсету (грузятся atlas_load). Таблицы + * видны только тому, кто попросил POP_BG_ATLAS_NAMES: иначе копия + * строк уедет в каждый включивший заголовок модуль. */ +#ifdef POP_BG_ATLAS_NAMES +static const char *const pop_env_atl[POP_TILESETS][POP_ENV_PAGES] = { + { "pop_env0.atl", "pop_env1.atl", "pop_env2.atl", "pop_env3.atl", "pop_env4.atl", "pop_env5.atl", "pop_env6.atl", "pop_env7.atl", "pop_env8.atl", "pop_env9.atl" }, + { "pal_env0.atl", "pal_env1.atl", "pal_env2.atl", "pal_env3.atl", "pal_env4.atl", "pal_env5.atl", "pal_env6.atl", "pal_env7.atl", "pal_env8.atl", "pal_env9.atl" }, +}; +static const char *const pop_wall_atl[POP_TILESETS] = { "pop_wall.atl", "pal_wall.atl" }; +static const char *const pop_fore_atl[POP_TILESETS] = { "pop_fore.atl", "pal_fore.atl" }; +static const char *const pop_tile_pal[POP_TILESETS] = { "pop_tile.pal", "pal_tile.pal" }; +#endif /* POP_BG_ATLAS_NAMES */ +#define POP_POT_ATL "pop_pot.atl" /* chtab_1: зелья, от набора не зависит */ +#define POP_PAL_POT 0x40 +/* Пузырёк зелья: красный набор = id 16..22 (кадры оригинала), + зелёный (перо/переворот) и синий (вред/открыть) = те же кадры + под id 30..36 и 40..46 (draw_tile_anim, seg008:652). */ +#define POP_POT_BUBB_GREEN 30 +#define POP_POT_BUBB_BLUE 40 +#define POP_BG_PAL "pop_bg.pal" + +#endif diff --git a/applications/SprPoP/gen/pop_font.h b/applications/SprPoP/gen/pop_font.h new file mode 100644 index 0000000..1ef09fc --- /dev/null +++ b/applications/SprPoP/gen/pop_font.h @@ -0,0 +1,29 @@ +/* + * pop_font.h — контракт FONT\FONT.ATL, СГЕНЕРИРОВАН. + * Источник: SDLPoP hc_small_font_data[] и hc_font_data[]. + * Генератор: toolchain/pop_extract_font.py. Руками не править. + */ +#ifndef POP_FONT_H +#define POP_FONT_H + +#include + +#define POP_FONT_COLOR 0x3F +#define POP_FONT_DARK_COLOR 0x3E +#define POP_FONT_FIRST 32 +#define POP_FONT_LAST 126 +#define POP_FONT_SMALL_BASE 0 +#define POP_FONT_SMALL_ASCENT 5 +#define POP_FONT_SMALL_SPACE 1 +#define POP_FONT_BIG_BASE 95 +#define POP_FONT_BIG_ASCENT 7 +#define POP_FONT_BIG_SPACE 1 +/* Тот же КРУПНЫЙ шрифт цветом POP_FONT_DARK_COLOR: тень и текст на светлом. + * Набор обрезан по '_' — им пишется только таблица рекордов. */ +#define POP_FONT_DARK_BASE 190 +#define POP_FONT_DARK_FIRST 32 +#define POP_FONT_DARK_LAST 95 +#define POP_FONT_SNAPSHOT_OFF 0x3C00 +#define POP_FONT_ATLAS_SIZE 10841 + +#endif diff --git a/applications/SprPoP/gen/pop_guard_pal.h b/applications/SprPoP/gen/pop_guard_pal.h new file mode 100644 index 0000000..8e394f4 --- /dev/null +++ b/applications/SprPoP/gen/pop_guard_pal.h @@ -0,0 +1,36 @@ +/* pop_guard_pal.h — палитры стражей (guard_palettes из res10.bin). + * СГЕНЕРЕНО toolchain/pop_pack_guard.py — НЕ править вручную. + * + * Индексация как в оригинале: цвет из level.guards_color[room-1] + * (младшие 4 бита), 1..7 -> строка [color-1]; 0 = палитру НЕ менять + * (seg003:255 при curr_guard_color == 0 set_chtab_palette не зовёт). + * Запись — 4 байта (B, G, R, 0), формат gfx_pal_load. */ +#ifndef POP_GUARD_PAL_H +#define POP_GUARD_PAL_H + +#include + +#define POP_GUARD_PAL_BASE 0x90 /* слоты Sprinter под chtab_5 */ +#define POP_GUARD_NPAL 7 + +static const uint8_t pop_guard_pal[POP_GUARD_NPAL][64] = { + { 0x00,0x00,0x00,0x00,0xFF,0x91,0x48,0x00,0xB2,0x81,0x69,0x00,0xFF,0xFF,0xFF,0x00,0x38,0x5D,0x79,0x00,0x50,0x6D,0xAA,0x00,0x69,0x89,0xDA,0x00,0xD2,0xB6,0xD2,0x00,0x91,0x7D,0x91,0x00,0xFF,0xDA,0xFF,0x00,0x4C,0x81,0xA1,0x00,0x71,0xBA,0xEA,0x00,0x79,0x28,0x81,0x00,0xB2,0x81,0x69,0x00,0xC2,0xC2,0xC2,0x00,0x38,0x00,0xF6,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x30,0xAA,0x00,0x00,0x24,0x81,0x00,0x79,0x79,0xFF,0x00,0x38,0x5D,0x79,0x00,0x50,0x6D,0xAA,0x00,0x69,0x89,0xDA,0x00,0xC2,0x69,0x85,0x00,0xAA,0x5D,0x75,0x00,0xFF,0x89,0xB2,0x00,0x50,0x50,0x79,0x00,0x71,0x71,0xAA,0x00,0xBE,0x28,0xBA,0x00,0x00,0x24,0x81,0x00,0x61,0x61,0xCA,0x00,0x38,0x00,0xF6,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x50,0xFF,0x00,0x00,0x38,0xBE,0x00,0xFF,0xFF,0xFF,0x00,0x38,0x5D,0x79,0x00,0x50,0x6D,0xAA,0x00,0x69,0x89,0xDA,0x00,0x00,0x99,0xD2,0x00,0x00,0x7D,0xAA,0x00,0x00,0xBA,0xFF,0x00,0x4C,0x81,0xA1,0x00,0x71,0xBA,0xEA,0x00,0x99,0x28,0x6D,0x00,0x00,0x38,0xBE,0x00,0xD2,0xCA,0xCA,0x00,0x38,0x00,0xF6,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x91,0x0C,0x00,0x00,0x71,0x0C,0x00,0xFF,0xD2,0xFF,0x00,0x38,0x5D,0x79,0x00,0x50,0x6D,0xAA,0x00,0x69,0x89,0xDA,0x00,0x4C,0xAE,0xCA,0x00,0x38,0x7D,0x91,0x00,0x61,0xDA,0xFF,0x00,0x4C,0x81,0xA1,0x00,0x71,0xBA,0xEA,0x00,0x20,0x00,0x99,0x00,0x00,0x71,0x0C,0x00,0xB2,0x91,0xB2,0x00,0x38,0x00,0xF6,0x00 }, + { 0x00,0x00,0x00,0x00,0xFF,0x00,0x61,0x00,0xAA,0x00,0x61,0x00,0xB2,0x3C,0xBE,0x00,0x38,0x5D,0x79,0x00,0x50,0x6D,0xAA,0x00,0x69,0x89,0xDA,0x00,0x8D,0xBA,0xCA,0x00,0x6D,0x8D,0x99,0x00,0xB2,0xEA,0xFF,0x00,0x34,0x6D,0x91,0x00,0x40,0x89,0xBA,0x00,0x79,0x28,0x81,0x00,0xAA,0x00,0x61,0x00,0x81,0x2C,0x89,0x00,0x38,0x00,0xF6,0x00 }, + { 0x00,0x00,0x00,0x00,0xFF,0x59,0xCA,0x00,0xC2,0x44,0x99,0x00,0xFF,0xAA,0x91,0x00,0x38,0x5D,0x79,0x00,0x50,0x6D,0xAA,0x00,0x69,0x89,0xDA,0x00,0xD2,0xB6,0xD2,0x00,0x99,0xB2,0xB2,0x00,0xDA,0xFF,0xFF,0x00,0x4C,0x81,0xA1,0x00,0x71,0xBA,0xEA,0x00,0xA1,0x6D,0x5D,0x00,0xC2,0x44,0x99,0x00,0xD2,0x8D,0x79,0x00,0x38,0x00,0xF6,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0xDA,0xDE,0x00,0xE2,0x30,0xB2,0x00,0x38,0x5D,0x79,0x00,0x50,0x6D,0xAA,0x00,0x69,0x89,0xDA,0x00,0x00,0x9D,0xD2,0x00,0x00,0x9D,0xF2,0x00,0x00,0xC2,0xFF,0x00,0xA1,0x4C,0x75,0x00,0xFF,0x79,0xBA,0x00,0xB2,0x00,0x61,0x00,0x00,0xDA,0xDE,0x00,0xAA,0x24,0x85,0x00,0x38,0x00,0xF6,0x00 }, +}; + +/* Скелет (SKEL/res750.pal): свой набор — свой цвет. */ +static const uint8_t pop_skel_pal[64] = { + 0x00,0x00,0x00,0x00,0xFF,0xFF,0x55,0x00,0x89,0x79,0xBA,0x00,0xF2,0xFF,0xFF,0x00,0x00,0x00,0xAA,0x00,0xAA,0x00,0xAA,0x00,0x00,0x55,0xAA,0x00,0xDA,0xE6,0xE6,0x00,0x55,0x55,0x55,0x00,0xFF,0x55,0x55,0x00,0x55,0xFF,0x55,0x00,0xFF,0xFF,0x55,0x00,0x55,0x55,0xFF,0x00,0x00,0x00,0x00,0x00,0x55,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00, +}; + +/* Джафар (VIZIER/res750.pal): свой набор — свой цвет. */ +static const uint8_t pop_vizier_pal[64] = { + 0x00,0x00,0x00,0x00,0x79,0x69,0xFF,0x00,0x7D,0x4C,0xBE,0x00,0xFF,0xFF,0xFF,0x00,0x38,0x5D,0x79,0x00,0x50,0x6D,0xAA,0x00,0x69,0x89,0xDA,0x00,0xD2,0xB6,0xD2,0x00,0x91,0x7D,0x91,0x00,0xFF,0xDA,0xFF,0x00,0x4C,0x81,0xA1,0x00,0x71,0xBA,0xEA,0x00,0xEA,0x44,0xB2,0x00,0xEA,0x44,0xB2,0x00,0x55,0xFF,0xFF,0x00,0x99,0x2C,0x75,0x00, +}; + +#endif diff --git a/applications/SprPoP/gen/pop_music_tbl.h b/applications/SprPoP/gen/pop_music_tbl.h new file mode 100644 index 0000000..cfd32b2 --- /dev/null +++ b/applications/SprPoP/gen/pop_music_tbl.h @@ -0,0 +1,104 @@ +/* + * pop_music_tbl.h — каталог музыкальных треков, СГЕНЕРИРОВАН. + * Источник: assets/orig/PoP1_DOS_music/flac (записи DOS-версии). + * Генератор: tools/pop_pack_music.py. Руками не править. + * + * Нумерация id — оригинала (SDLPoP data/music/names.txt). pages — + * сколько кусков m_.bin лежит на диске, blocks — длина трека + * в порциях насоса по 128 байт (в байтах не влезло бы в uint16). + * + * Длительности реплик в тиках вынесены в pop_music_ticks.h: таблица + * ниже — 171 байт ДАННЫХ, и модулю, которому нужна одна константа, + * платить за неё копией таблицы незачем (поймано на pop_intro.c: + * +172 байта в банк 11). + * + * POP_MUS_TICKS_ — та же длина в ТИКАХ СЦЕНЫ (60 Гц оригинала). + * Нужна там, где сценарий ждёт окончания реплики: у оригинала это + * `do { frame(1) } while (check_sound_playing())`, у нас — пауза + * длиной в нашу запись. Считается ЗДЕСЬ, а не в C, по двум + * причинам: blocks*40 переполняет uint16 на won (9855 порций), и + * число обязано соответствовать НАБОРУ, которым собрали (flac и + * mt32 — разные исполнения разной длины). + * + * Отношение 40/57 — обратное к тому, которым шкала сцены переводит + * тики в порции насоса (57/40, см. pop_intro.c). Берём именно его, + * а не точное 10937,5/128/60, чтобы длина реплики и ход шкалы + * считались одной и той же арифметикой. Округление — к ближайшему + * (+28 перед делением): отбрасывание вниз теряло по тику на реплику. + */ +#ifndef POP_MUSIC_TBL_H +#define POP_MUSIC_TBL_H + +#include + +#define POP_MUS_COUNT 57 +#define POP_MUS_BLOCK 128 +#define POP_MUS_MAX_PAGES 78 + + +typedef struct { + uint8_t pages; /* 0 — трека нет */ + uint16_t blocks; /* длина в порциях по 128 байт */ +} pop_mus_ent_t; + +static const pop_mus_ent_t pop_mus_tbl[POP_MUS_COUNT] = { + { 0, 0 }, /* 0 */ + { 0, 0 }, /* 1 */ + { 0, 0 }, /* 2 */ + { 0, 0 }, /* 3 */ + { 0, 0 }, /* 4 */ + { 0, 0 }, /* 5 */ + { 0, 0 }, /* 6 */ + { 0, 0 }, /* 7 */ + { 0, 0 }, /* 8 */ + { 0, 0 }, /* 9 */ + { 0, 0 }, /* 10 */ + { 0, 0 }, /* 11 */ + { 0, 0 }, /* 12 */ + { 0, 0 }, /* 13 */ + { 0, 0 }, /* 14 */ + { 0, 0 }, /* 15 */ + { 0, 0 }, /* 16 */ + { 0, 0 }, /* 17 */ + { 0, 0 }, /* 18 */ + { 0, 0 }, /* 19 */ + { 0, 0 }, /* 20 */ + { 0, 0 }, /* 21 */ + { 0, 0 }, /* 22 */ + { 0, 0 }, /* 23 */ + { 3, 305 }, /* 24 regular_death */ + { 2, 196 }, /* 25 find_mirror_and_find_shadow */ + { 9, 1132 }, /* 26 embrace */ + { 8, 915 }, /* 27 cutscene_before_2_4_6_12 */ + { 6, 656 }, /* 28 fight_death */ + { 4, 428 }, /* 29 meet_Jaffar */ + { 3, 299 }, /* 30 life_potion */ + { 0, 0 }, /* 31 */ + { 5, 624 }, /* 32 end_level_4_and_killed_by_shadow */ + { 2, 205 }, /* 33 potion */ + { 0, 0 }, /* 34 */ + { 7, 888 }, /* 35 cutscene_before_8_9 */ + { 10, 1256 }, /* 36 out_of_time */ + { 3, 280 }, /* 37 guard_death_and_obtaining_the_sword */ + { 0, 0 }, /* 38 */ + { 13, 1591 }, /* 39 floating */ + { 5, 596 }, /* 40 cutscene_not_much_time_left */ + { 9, 1043 }, /* 41 story_5_level_end_and_merge_with_shadow */ + { 0, 0 }, /* 42 */ + { 3, 285 }, /* 43 killed_Jaffar */ + { 0, 0 }, /* 44 */ + { 0, 0 }, /* 45 */ + { 0, 0 }, /* 46 */ + { 0, 0 }, /* 47 */ + { 0, 0 }, /* 48 */ + { 0, 0 }, /* 49 */ + { 10, 1184 }, /* 50 story_2_princess */ + { 0, 0 }, /* 51 */ + { 20, 2443 }, /* 52 story_4_Jaffar_leaves */ + { 11, 1403 }, /* 53 story_3_Jaffar_enters */ + { 17, 2064 }, /* 54 intro_theme */ + { 11, 1350 }, /* 55 story_1_absence */ + { 78, 9865 }, /* 56 won */ +}; + +#endif /* POP_MUSIC_TBL_H */ diff --git a/applications/SprPoP/gen/pop_music_ticks.h b/applications/SprPoP/gen/pop_music_ticks.h new file mode 100644 index 0000000..a4fa21e --- /dev/null +++ b/applications/SprPoP/gen/pop_music_ticks.h @@ -0,0 +1,44 @@ +/* + * pop_music_ticks.h — длительности треков в ТИКАХ СЦЕНЫ, СГЕНЕРИРОВАН. + * Набор: flac. Генератор: tools/pop_pack_music.py. + * Руками не править. + * + * Нужны там, где сценарий ждёт окончания реплики: у оригинала это + * `do { frame(1) } while (check_sound_playing())`, у нас — пауза длиной + * в НАШУ запись. Число обязано соответствовать набору, которым собрана + * сборка: одна и та же реплика на flac и mt32 разной длины (m53 — 985 + * тиков против 1044), и захардкоженная константа молча ломает сцену. + * + * Считается здесь, а не в C: blocks*40 переполняет uint16 на won + * (9855 порций). Отношение 40/57 — обратное к тому, которым шкала + * сцены переводит тики в порции насоса (57/40, pop_intro.c); берём + * именно его, чтобы длина реплики и ход шкалы считались одной + * арифметикой. Округление к ближайшему: вниз теряло по тику. + */ +#ifndef POP_MUSIC_TICKS_H +#define POP_MUSIC_TICKS_H + +#define POP_MUS_TICKS_24 214 /* regular_death */ +#define POP_MUS_TICKS_25 138 /* find_mirror_and_find_shadow */ +#define POP_MUS_TICKS_26 794 /* embrace */ +#define POP_MUS_TICKS_27 642 /* cutscene_before_2_4_6_12 */ +#define POP_MUS_TICKS_28 460 /* fight_death */ +#define POP_MUS_TICKS_29 300 /* meet_Jaffar */ +#define POP_MUS_TICKS_30 210 /* life_potion */ +#define POP_MUS_TICKS_32 438 /* end_level_4_and_killed_by_shadow */ +#define POP_MUS_TICKS_33 144 /* potion */ +#define POP_MUS_TICKS_35 623 /* cutscene_before_8_9 */ +#define POP_MUS_TICKS_36 881 /* out_of_time */ +#define POP_MUS_TICKS_37 196 /* guard_death_and_obtaining_the_sword */ +#define POP_MUS_TICKS_39 1116 /* floating */ +#define POP_MUS_TICKS_40 418 /* cutscene_not_much_time_left */ +#define POP_MUS_TICKS_41 732 /* story_5_level_end_and_merge_with_shadow */ +#define POP_MUS_TICKS_43 200 /* killed_Jaffar */ +#define POP_MUS_TICKS_50 831 /* story_2_princess */ +#define POP_MUS_TICKS_52 1714 /* story_4_Jaffar_leaves */ +#define POP_MUS_TICKS_53 985 /* story_3_Jaffar_enters */ +#define POP_MUS_TICKS_54 1448 /* intro_theme */ +#define POP_MUS_TICKS_55 947 /* story_1_absence */ +#define POP_MUS_TICKS_56 6923 /* won */ + +#endif /* POP_MUSIC_TICKS_H */ diff --git a/applications/SprPoP/gen/pop_shadow_atlas.h b/applications/SprPoP/gen/pop_shadow_atlas.h new file mode 100644 index 0000000..7c46fe7 --- /dev/null +++ b/applications/SprPoP/gen/pop_shadow_atlas.h @@ -0,0 +1,71 @@ +/* pop_shadow_atlas.h — раскладка атласа ТЕНИ. + * СГЕНЕРЕНО toolchain/pop_pack_shadow.py — НЕ править вручную. + * + * Две половины, потому что и у оригинала их две (см. шапку + * упаковщика): кадры вне боя адресуют chtab_2 (спрайты Кида), + * кадры 150..189 — chtab_5, а он на 12-м уровне это SHADOW.DAT. + * Какая половина нужна, приложение уже знает — тем же условием, + * что выбирает таблицу кадров (pop_frame_tbl_is_guard). + * + * Спрайт Тени НА ОДИН ПИКСЕЛЬ ШИРЕ исходного: второй проход + * оригинала кладётся в x+1, и суммарный след занимает [x, x+w]. + * Рисовать по тем же координатам, что и Кида — сдвиг уже внутри. + */ +#ifndef POP_SHADOW_ATLAS_H +#define POP_SHADOW_ATLAS_H + +#include + +#define POP_SHADOW_SHIFT 3 +#define POP_SHADOW_MASK 7 +#define POP_SHADOW_PAL_BASE 0xA0 /* слоты Sprinter под Тень */ + +/* Половина «вне боя» — кадры Кида (chtab_2). */ +#define POP_SHADOW_KID_PAGES 28 +/* Половина «в бою» — SHADOW.DAT (chtab_5, кадры 150..189). */ +#define POP_SHADOW_FGT_PAGES 4 + +static const char *const pop_shadow_kid_atl[POP_SHADOW_KID_PAGES] = { + "SHADOW\\sk0.atl", + "SHADOW\\sk1.atl", + "SHADOW\\sk2.atl", + "SHADOW\\sk3.atl", + "SHADOW\\sk4.atl", + "SHADOW\\sk5.atl", + "SHADOW\\sk6.atl", + "SHADOW\\sk7.atl", + "SHADOW\\sk8.atl", + "SHADOW\\sk9.atl", + "SHADOW\\sk10.atl", + "SHADOW\\sk11.atl", + "SHADOW\\sk12.atl", + "SHADOW\\sk13.atl", + "SHADOW\\sk14.atl", + "SHADOW\\sk15.atl", + "SHADOW\\sk16.atl", + "SHADOW\\sk17.atl", + "SHADOW\\sk18.atl", + "SHADOW\\sk19.atl", + "SHADOW\\sk20.atl", + "SHADOW\\sk21.atl", + "SHADOW\\sk22.atl", + "SHADOW\\sk23.atl", + "SHADOW\\sk24.atl", + "SHADOW\\sk25.atl", + "SHADOW\\sk26.atl", + "SHADOW\\sk27.atl", +}; + +static const char *const pop_shadow_fgt_atl[POP_SHADOW_FGT_PAGES] = { + "SHADOW\\sf0.atl", + "SHADOW\\sf1.atl", + "SHADOW\\sf2.atl", + "SHADOW\\sf3.atl", +}; + +/* 16 цветов, запись 4 байта (B, G, R, 0) — формат gfx_pal_load. */ +static const uint8_t pop_shadow_pal[64] = { + 0x00,0x00,0x00,0x00,0xD8,0xFC,0xFC,0x00,0x6C,0x80,0xDC,0x00,0x78,0x44,0x24,0x00,0xA0,0xB8,0xD8,0x00,0xB5,0x7C,0x21,0x00,0x00,0x90,0xB8,0x00,0x5D,0x6D,0xAB,0x00,0x0C,0xF0,0x6C,0x00,0xCC,0x38,0x05,0x00,0x38,0xCC,0xC0,0x00,0x58,0xDB,0xA7,0x00,0xBB,0x9F,0x54,0x00,0x5D,0x18,0x60,0x00,0x37,0xD7,0x37,0x00,0x2F,0x17,0xD2,0x00, +}; + +#endif diff --git a/applications/SprPoP/gen/pop_sound_tbl.h b/applications/SprPoP/gen/pop_sound_tbl.h new file mode 100644 index 0000000..c6d9fb7 --- /dev/null +++ b/applications/SprPoP/gen/pop_sound_tbl.h @@ -0,0 +1,113 @@ +/* pop_sound_tbl.h — раскладка оцифрованных эффектов PoP. + * СГЕНЕРЕНО toolchain/pop_pack_sound.py — НЕ править вручную. + * + * Данные: 8 бит беззнаковые, МОНО, 10937.5 Гц (CBL_FREQ_10K9) — формат + * CBL один в один, конвертировать в рантайме нечего. + * + * Начало каждого звука кратно 128 — размеру блока запроса CBL, а + * страница (16384) кратна ему же. Поэтому НИ ОДИН блок не пересекает + * границу страницы. Больше того: с 2026-08-25 каждый звук лежит + * ЦЕЛИКОМ в одной странице — не влезает в остаток, значит начинаем + * новую (страницы EMM у нас с запасом). Единственное исключение — + * эффект, который сам ДЛИННЕЕ страницы: для него насос переключает + * страницу на ходу, и эта ветка в pop_sfx_fill остаётся ради него. + * + * Смещение хранится РАЗОБРАННЫМ на страницу и адрес в окне: на Z80 + * 32-битная арифметика дорога, а так всё влезает в 8/16 бит. + * Длина округлена вверх до блока — неполных блоков не бывает. + */ +#ifndef POP_SOUND_TBL_H +#define POP_SOUND_TBL_H + +#include + +#define POP_SND_PAGES 10 +#define POP_SND_COUNT 57 +#define POP_SND_BLOCK 128 +/* Первый блок страницы 0 — тишина для насоса (см. упаковщик). */ +#define POP_SND_SILENCE_PAGE 0 +#define POP_SND_SILENCE_OFF 0x0000 + +typedef struct { uint8_t page; uint16_t off; uint16_t len; } pop_snd_ent_t; + +/* len == 0 — у звука нет оцифровки (это музыка, см. sound_plan.md). */ +static const pop_snd_ent_t pop_snd_tbl[POP_SND_COUNT] = { + { 0, 0x0080, 1664 }, /* 0 */ + { 0, 0x0700, 12032 }, /* 1 */ + { 1, 0x0000, 6272 }, /* 2 */ + { 1, 0x1880, 1280 }, /* 3 */ + { 1, 0x1D80, 1152 }, /* 4 */ + { 1, 0x2200, 1408 }, /* 5 */ + { 1, 0x2780, 5504 }, /* 6 */ + { 2, 0x0000, 896 }, /* 7 */ + { 2, 0x0380, 1664 }, /* 8 */ + { 2, 0x0A00, 2816 }, /* 9 */ + { 2, 0x1500, 4992 }, /* 10 */ + { 2, 0x2880, 1280 }, /* 11 */ + { 2, 0x2D80, 1280 }, /* 12 */ + { 3, 0x0000, 3840 }, /* 13 */ + { 3, 0x0F00, 7552 }, /* 14 */ + { 4, 0x0000, 17664 }, /* 15 */ + { 5, 0x0500, 4736 }, /* 16 */ + { 5, 0x1780, 4224 }, /* 17 */ + { 6, 0x0000, 6912 }, /* 18 */ + { 6, 0x1B00, 2816 }, /* 19 */ + { 6, 0x2600, 1408 }, /* 20 */ + { 6, 0x2B80, 1152 }, /* 21 */ + { 6, 0x3000, 1280 }, /* 22 */ + { 6, 0x3500, 1408 }, /* 23 */ + { 0, 0x0000, 0 }, /* 24 */ + { 0, 0x0000, 0 }, /* 25 */ + { 0, 0x0000, 0 }, /* 26 */ + { 0, 0x0000, 0 }, /* 27 */ + { 0, 0x0000, 0 }, /* 28 */ + { 0, 0x0000, 0 }, /* 29 */ + { 0, 0x0000, 0 }, /* 30 */ + { 0, 0x0000, 0 }, /* 31 */ + { 0, 0x0000, 0 }, /* 32 */ + { 0, 0x0000, 0 }, /* 33 */ + { 0, 0x0000, 0 }, /* 34 */ + { 0, 0x0000, 0 }, /* 35 */ + { 0, 0x0000, 0 }, /* 36 */ + { 0, 0x0000, 0 }, /* 37 */ + { 0, 0x0000, 0 }, /* 38 */ + { 0, 0x0000, 0 }, /* 39 */ + { 0, 0x0000, 0 }, /* 40 */ + { 0, 0x0000, 0 }, /* 41 */ + { 0, 0x0000, 0 }, /* 42 */ + { 0, 0x0000, 0 }, /* 43 */ + { 7, 0x0000, 2688 }, /* 44 */ + { 7, 0x0A80, 4224 }, /* 45 */ + { 7, 0x1B00, 4608 }, /* 46 */ + { 8, 0x0000, 5120 }, /* 47 */ + { 8, 0x1400, 5120 }, /* 48 */ + { 8, 0x2800, 4480 }, /* 49 */ + { 0, 0x0000, 0 }, /* 50 */ + { 9, 0x0000, 4864 }, /* 51 */ + { 0, 0x0000, 0 }, /* 52 */ + { 0, 0x0000, 0 }, /* 53 */ + { 0, 0x0000, 0 }, /* 54 */ + { 0, 0x0000, 0 }, /* 55 */ + { 0, 0x0000, 0 }, /* 56 */ +}; + +/* Имена файлов видит ТОЛЬКО тот, кто попросил POP_SND_FILES (холодная + * половина): иначе копия строк уезжает в каждый включивший заголовок + * модуль, а в резиденте W1/W2 каждая сотня байт на счету. Тот же + * приём, что у POP_BG_ATLAS_NAMES в pop_bg_atlas.h. */ +#ifdef POP_SND_FILES +static const char *const pop_snd_files[POP_SND_PAGES] = { + "SND\\s0.bin", + "SND\\s1.bin", + "SND\\s2.bin", + "SND\\s3.bin", + "SND\\s4.bin", + "SND\\s5.bin", + "SND\\s6.bin", + "SND\\s7.bin", + "SND\\s8.bin", + "SND\\s9.bin", +}; +#endif + +#endif diff --git a/applications/SprPoP/gen/pv_arc.h b/applications/SprPoP/gen/pv_arc.h new file mode 100644 index 0000000..d7b9dae --- /dev/null +++ b/applications/SprPoP/gen/pv_arc.h @@ -0,0 +1,92 @@ +/* pv_arc.h — индексы в архиве PV. СГЕНЕРИРОВАН + * (toolchain/pop_pack_arc.py), руками не править. */ +#ifndef PV_ARC_H +#define PV_ARC_H + +#define ARC_PV_COUNT 83 + +#define ARC_PV_S0_0 0 +#define ARC_PV_S0_1 1 +#define ARC_PV_S0_2 2 +#define ARC_PV_S0_3 3 +#define ARC_PV_S0_4 4 +#define ARC_PV_S1_0 5 +#define ARC_PV_S1_1 6 +#define ARC_PV_S1_2 7 +#define ARC_PV_S1_3 8 +#define ARC_PV_S1_4 9 +#define ARC_PV_S2_0 10 +#define ARC_PV_S2_1 11 +#define ARC_PV_S2_2 12 +#define ARC_PV_S2_3 13 +#define ARC_PV_S2_4 14 +#define ARC_PV_S3_0 15 +#define ARC_PV_S3_1 16 +#define ARC_PV_S3_2 17 +#define ARC_PV_S3_3 18 +#define ARC_PV_S3_4 19 +#define ARC_PV_S4_0 20 +#define ARC_PV_S4_1 21 +#define ARC_PV_S4_2 22 +#define ARC_PV_S4_3 23 +#define ARC_PV_S4_4 24 +#define ARC_PV_S5_0 25 +#define ARC_PV_S5_1 26 +#define ARC_PV_S5_2 27 +#define ARC_PV_S5_3 28 +#define ARC_PV_S5_4 29 +#define ARC_PV_P0_0 30 +#define ARC_PV_P0_1 31 +#define ARC_PV_P0_2 32 +#define ARC_PV_P0_3 33 +#define ARC_PV_P0_4 34 +#define ARC_PV_P1_0 35 +#define ARC_PV_P1_1 36 +#define ARC_PV_P1_2 37 +#define ARC_PV_P1_3 38 +#define ARC_PV_P1_4 39 +#define ARC_PV_P2_0 40 +#define ARC_PV_P2_1 41 +#define ARC_PV_P2_2 42 +#define ARC_PV_P2_3 43 +#define ARC_PV_P2_4 44 +#define ARC_PV_P3_0 45 +#define ARC_PV_P3_1 46 +#define ARC_PV_P3_2 47 +#define ARC_PV_P3_3 48 +#define ARC_PV_P3_4 49 +#define ARC_PV_P4_0 50 +#define ARC_PV_P4_1 51 +#define ARC_PV_P4_2 52 +#define ARC_PV_P4_3 53 +#define ARC_PV_P4_4 54 +#define ARC_PV_P5_0 55 +#define ARC_PV_P5_1 56 +#define ARC_PV_P5_2 57 +#define ARC_PV_P5_3 58 +#define ARC_PV_P5_4 59 +#define ARC_PV_P6_0 60 +#define ARC_PV_P6_1 61 +#define ARC_PV_P6_2 62 +#define ARC_PV_P6_3 63 +#define ARC_PV_P6_4 64 +#define ARC_PV_P7_0 65 +#define ARC_PV_P7_1 66 +#define ARC_PV_P7_2 67 +#define ARC_PV_P7_3 68 +#define ARC_PV_P7_4 69 +#define ARC_PV_B0_0 70 +#define ARC_PV_B0_1 71 +#define ARC_PV_B0_2 72 +#define ARC_PV_B0_3 73 +#define ARC_PV_B0_4 74 +#define ARC_PV_A0 75 +#define ARC_PV_A1 76 +#define ARC_PV_A2 77 +#define ARC_PV_J0 78 +#define ARC_PV_J1 79 +#define ARC_PV_J2 80 +#define ARC_PV_H0 81 +#define ARC_PV_T0 82 + +#endif diff --git a/applications/SprPoP/gen/shadow_arc.h b/applications/SprPoP/gen/shadow_arc.h new file mode 100644 index 0000000..09a719a --- /dev/null +++ b/applications/SprPoP/gen/shadow_arc.h @@ -0,0 +1,41 @@ +/* shadow_arc.h — индексы в архиве SHADOW. СГЕНЕРИРОВАН + * (toolchain/pop_pack_arc.py), руками не править. */ +#ifndef SHADOW_ARC_H +#define SHADOW_ARC_H + +#define ARC_SHADOW_COUNT 32 + +#define ARC_SHADOW_SK0 0 +#define ARC_SHADOW_SK1 1 +#define ARC_SHADOW_SK2 2 +#define ARC_SHADOW_SK3 3 +#define ARC_SHADOW_SK4 4 +#define ARC_SHADOW_SK5 5 +#define ARC_SHADOW_SK6 6 +#define ARC_SHADOW_SK7 7 +#define ARC_SHADOW_SK8 8 +#define ARC_SHADOW_SK9 9 +#define ARC_SHADOW_SK10 10 +#define ARC_SHADOW_SK11 11 +#define ARC_SHADOW_SK12 12 +#define ARC_SHADOW_SK13 13 +#define ARC_SHADOW_SK14 14 +#define ARC_SHADOW_SK15 15 +#define ARC_SHADOW_SK16 16 +#define ARC_SHADOW_SK17 17 +#define ARC_SHADOW_SK18 18 +#define ARC_SHADOW_SK19 19 +#define ARC_SHADOW_SK20 20 +#define ARC_SHADOW_SK21 21 +#define ARC_SHADOW_SK22 22 +#define ARC_SHADOW_SK23 23 +#define ARC_SHADOW_SK24 24 +#define ARC_SHADOW_SK25 25 +#define ARC_SHADOW_SK26 26 +#define ARC_SHADOW_SK27 27 +#define ARC_SHADOW_SF0 28 +#define ARC_SHADOW_SF1 29 +#define ARC_SHADOW_SF2 30 +#define ARC_SHADOW_SF3 31 + +#endif diff --git a/applications/SprPoP/gen/skel_arc.h b/applications/SprPoP/gen/skel_arc.h new file mode 100644 index 0000000..eca2b21 --- /dev/null +++ b/applications/SprPoP/gen/skel_arc.h @@ -0,0 +1,13 @@ +/* skel_arc.h — индексы в архиве SKEL. СГЕНЕРИРОВАН + * (toolchain/pop_pack_arc.py), руками не править. */ +#ifndef SKEL_ARC_H +#define SKEL_ARC_H + +#define ARC_SKEL_COUNT 4 + +#define ARC_SKEL_G0 0 +#define ARC_SKEL_G1 1 +#define ARC_SKEL_G2 2 +#define ARC_SKEL_G3 3 + +#endif diff --git a/applications/SprPoP/gen/title_arc.h b/applications/SprPoP/gen/title_arc.h new file mode 100644 index 0000000..e02e7f2 --- /dev/null +++ b/applications/SprPoP/gen/title_arc.h @@ -0,0 +1,29 @@ +/* title_arc.h — индексы в архиве TITLE. СГЕНЕРИРОВАН + * (toolchain/pop_pack_arc.py), руками не править. */ +#ifndef TITLE_ARC_H +#define TITLE_ARC_H + +#define ARC_TITLE_COUNT 20 + +#define ARC_TITLE_T0_0 0 +#define ARC_TITLE_T0_1 1 +#define ARC_TITLE_T0_2 2 +#define ARC_TITLE_T0_3 3 +#define ARC_TITLE_T0_4 4 +#define ARC_TITLE_T1_0 5 +#define ARC_TITLE_T1_1 6 +#define ARC_TITLE_T1_2 7 +#define ARC_TITLE_T1_3 8 +#define ARC_TITLE_T1_4 9 +#define ARC_TITLE_T2_0 10 +#define ARC_TITLE_T2_1 11 +#define ARC_TITLE_T2_2 12 +#define ARC_TITLE_T2_3 13 +#define ARC_TITLE_T2_4 14 +#define ARC_TITLE_T3_0 15 +#define ARC_TITLE_T3_1 16 +#define ARC_TITLE_T3_2 17 +#define ARC_TITLE_T3_3 18 +#define ARC_TITLE_T3_4 19 + +#endif diff --git a/applications/SprPoP/gen/vizier_arc.h b/applications/SprPoP/gen/vizier_arc.h new file mode 100644 index 0000000..8f30b67 --- /dev/null +++ b/applications/SprPoP/gen/vizier_arc.h @@ -0,0 +1,14 @@ +/* vizier_arc.h — индексы в архиве VIZIER. СГЕНЕРИРОВАН + * (toolchain/pop_pack_arc.py), руками не править. */ +#ifndef VIZIER_ARC_H +#define VIZIER_ARC_H + +#define ARC_VIZIER_COUNT 5 + +#define ARC_VIZIER_G0 0 +#define ARC_VIZIER_G1 1 +#define ARC_VIZIER_G2 2 +#define ARC_VIZIER_G3 3 +#define ARC_VIZIER_G4 4 + +#endif diff --git a/applications/SprPoP/src/_pop_bg.h b/applications/SprPoP/src/_pop_bg.h new file mode 100644 index 0000000..5ff9578 --- /dev/null +++ b/applications/SprPoP/src/_pop_bg.h @@ -0,0 +1,63 @@ +/* + * _pop_bg.h — ВНУТРЕННИЙ контракт между двумя половинами слоя фона. + * Публичный API слоя — pop_bg.h; общие листья (резидент) — pop_tile.h. + * + * Слой фона разрезан на два банка: + * pop_bg.c (банк 2) — ГОРЯЧАЯ половина: fore-проход поверх персонажей, + * оверлеи, клип. Работает каждый кадр. + * pop_room.c (банк 7) — ХОЛОДНАЯ: полная отрисовка комнаты (draw_tile), + * точечные перерисовки тайлов, падающие плиты, + * загрузка атласов. Раз на комнату/событие. + * + * Резать пришлось потому, что банк 2 упирался в 16 КБ страницы (90.4 % до + * разгрузки), а чомперам и остальным ловушкам уровня 3 место нужно. Граница + * проведена по частоте вызова: холодная половина платит трамплин (~654 такта) + * за каждый вызов сюда, и на входе в комнату таких вызовов ~40 — 26 000 + * тактов, шестая часть кадрового бюджета РАЗОВО. Для горячего пути цена + * нулевая: он зовёт свои функции прямо, а общие листья — из резидента. + * + * Что осталось в банке 2 и вынесено сюда обёртками: кладка стены + * (wall_pattern — её зовёт и fore-проход каждый кадр) и решётка ворот + * соседней комнаты (draw_gate_back — её зовёт шовная окклюзия). Обратных + * зависимостей (горячая половина → холодная) нет вовсе. + */ +#ifndef _POP_BG_H +#define _POP_BG_H + +#include + +/* Состояние дрожащих плит — живёт в pop_map, читают обе половины. */ +extern uint8_t pop_loose_modif[30]; +extern uint8_t pop_ceil_modif[10]; /* плита-потолок: loose ряда 2 комнаты сверху */ + +/* Видимая высота полосы кладки у потолка (room-space y 0..7): её печёт + * холодная половина, а поверх персонажа кладёт горячая. */ +#define CEIL_BAND_H 8 + +/* Тайл ОБЪЕКТА активного персонажа (set_objtile_at_char, seg006:1833) — в + * момент обработки этого тайла спрайт попадает в midtable. Задаёт горячая + * половина (pop_fore_over_char), читают other_overlay_tile (там же) и + * порядок «плита vs Kid» в mob_tick_one (холодная). Писучие данные банка + * лежат в _DATA/W2 и видны из любого банка — см. sdcc_banked_call_rules. */ +extern int8_t pop_bg_obj_col, pop_bg_obj_row; + +/* ---- Точки входа ГОРЯЧЕЙ половины для холодной (через трамплин) ------- */ +void pop_wall_pattern_b(int row, int col, int which_part) __banked; +void pop_wall_pattern_reset_b(void) __banked; +void pop_gate_back_b(uint8_t modl, int xh, int dby, int dmy) __banked; + +#endif + +/* ---- Слоты «первая запечка была на странице N» ----------------------- * + * bake_copy (pop_room.c) копирует готовый прямоугольник со второй страницы + * дабл-буфера вместо повторного пересчёта. Это верно ТОЛЬКО пока содержимое + * тайла между двумя кадрами не изменилось. У анимированных тайлов (кнопка с + * идущим таймером связи, ворота) пометка ОБНОВЛЯЕТСЯ каждый кадр, и картинка + * при этом другая — такой тайл обязан пересчитываться честно. + * + * Поэтому pop_set_redraw / pop_set_redraw_above гасят слот на ПЕРЕпометке + * (пометка пришла, когда счётчик ещё не отработал). Объявления БЕЗ __banked: + * pop_redraw.c и pop_room.c живут в одном банке, и трамплин тут не нужен. */ +void pop_bake_slot_reset(uint8_t tilepos); +void pop_bake_slot_reset_above(uint8_t col); + diff --git a/applications/SprPoP/src/_pop_config.h b/applications/SprPoP/src/_pop_config.h new file mode 100644 index 0000000..cbd7e74 --- /dev/null +++ b/applications/SprPoP/src/_pop_config.h @@ -0,0 +1,40 @@ +/* + * _pop_config.h — внутренний бинарный контракт POP.CFG. + * + * Владелец формата ровно один — pop_config.c (банк 9). Внешний игровой + * код видит только pop_settings.h и публичные pop_cfg_load/save. Формат + * допускает добавление полей в конец payload той же версии: декодер знает + * первые девять байтов, но контрольную сумму считает по ВСЕМУ payload. + */ +#ifndef _POP_CONFIG_H +#define _POP_CONFIG_H + +#include + +#define POP_CFG_VERSION 1 +#define POP_CFG_HEADER_SIZE 7 +/* Ранний MS0-файл имел семь полей. Его продолжаем принимать: отсутствующие + * поля берут исторический дефолт (лимит времени ON, отладочная полоса ON, + * музыка ON), а новая запись содержит все десять. */ +#define POP_CFG_LEGACY_PAYLOAD_SIZE 7 +#define POP_CFG_TIMELIMIT_PAYLOAD_SIZE 8 +#define POP_CFG_DEBUGBAR_PAYLOAD_SIZE 9 +#define POP_CFG_PAYLOAD_SIZE 10 +#define POP_CFG_TAIL_SIZE 2 +/* CFG остаётся маленьким. Верхняя граница не даёт ручному мусору занять + * стек W2, но оставляет запас для будущих полей версии 1. */ +#define POP_CFG_MAX_PAYLOAD 64 +#define POP_CFG_SIZE (POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE + \ + POP_CFG_TAIL_SIZE) +#define POP_CFG_MIN_SIZE (POP_CFG_HEADER_SIZE + POP_CFG_LEGACY_PAYLOAD_SIZE + \ + POP_CFG_TAIL_SIZE) +#define POP_CFG_MAX_SIZE (POP_CFG_HEADER_SIZE + POP_CFG_MAX_PAYLOAD + \ + POP_CFG_TAIL_SIZE) + +/* Чистое ядро формата: доступно host-тесту без DSS/EMM. encode пишет + * текущий v1 размер, decode принимает прежние семи-/восьмибайтные и более + * длинный v1 payload, игнорируя неизвестный хвост. */ +void pop_cfg_encode(uint8_t *buf); +uint8_t pop_cfg_decode(const uint8_t *buf, uint16_t size); + +#endif diff --git a/applications/SprPoP/src/_pop_draw.h b/applications/SprPoP/src/_pop_draw.h new file mode 100644 index 0000000..1cbe465 --- /dev/null +++ b/applications/SprPoP/src/_pop_draw.h @@ -0,0 +1,47 @@ +/* + * _pop_draw.h — внутренний заголовок слоя отрисовки: выбор ядра libbgi + * (клипающее / линейное «noclip») по одному и тому же тесту для блита и + * для heal. + * + * ПОЧЕМУ ОТДЕЛЬНЫЙ ФАЙЛ, А НЕ pop_bg.h. Обе функции — `static inline`, а + * SDCC 4.5 оставляет тело такой функции в КАЖДОМ TU, который видит + * объявление, даже если тот её не зовёт (memory + * `sdcc_inline_codegen_findings`). pop_bg.h включают почти все модули PoP, + * то есть из общего заголовка эти ~100 Б размножились бы десятком мёртвых + * копий (замер CLIP-1, 2026-08-01: +1091 Б в _CODE и +636 Б в банке + * стража — за код, который там никто не вызывает). Здесь их видят ровно + * три файла, которые реально рисуют: pop_bg.c, pop_kid.c, pop_cdraw.c. + */ +#ifndef POP_DRAW_H +#define POP_DRAW_H + +#include +#include + +/* Спрайт целиком на экране И укладывается в 8-битные параметры noclip- + * примитивов libbgi? Условие входа в gfx_blit_cols_part_noclip: клипающий + * вариант платит ~5.6 К тактов подготовки на КАЖДЫЙ вызов, независимо от + * того, вылезает край или нет. */ +static inline uint8_t pop_onscreen_cols(int x, int y, uint16_t w, uint16_t h) +{ + return (uint8_t)(x >= 0 && y >= 0 && w < 256 && h < 256 && + x + (int)w <= 320 && y + (int)h <= 256); +} + +/* Стереть прямоугольник (heal из ОЗУ-копии) тем же приёмом, что и блит: + * целиком на экране → линейное ядро без клипа. Условие входа у + * gfx_heal_noclip ровно то же, что у gfx_blit_cols_part_noclip, поэтому + * тест один — общее ядро gfx_heal платит за клип и 16-бит, а не за пиксели + * (замер: 11 658 тактов на heal 22×22). + * + * НЕ inline (тело — pop_draw.c, резидент W1): SDCC 4.5 встраивал бы его в + * каждое место вызова по 181 Б И оставлял мёртвую копию в каждом TU — см. + * шапку pop_draw.c с замером. Зовётся 5 раз за кадр, цена вызова тонет в + * стоимости самого heal. + * + * w/h тут int, а не uint16_t: вызывающие считают их вычитанием (h − skip), и + * отрицательный результат обязан быть no-op, а не превратиться в огромный + * unsigned. */ +void pop_heal_fast(int x, int y, int w, int h); + +#endif diff --git a/applications/SprPoP/src/_pop_kdraw.h b/applications/SprPoP/src/_pop_kdraw.h new file mode 100644 index 0000000..f3988d2 --- /dev/null +++ b/applications/SprPoP/src/_pop_kdraw.h @@ -0,0 +1,34 @@ +/* + * _pop_kdraw.h — ВНУТРЕННИЙ контракт слоя персонажей: pop_cdraw.c (банк 4) + * ↔ pop_kdraw.c (тот же банк 4). + * + * ВАЖНО: объявления здесь НЕ помечены __banked, то есть вызовы идут прямым + * `call` — это корректно РОВНО ПОКА оба модуля лежат в ОДНОЙ странице: + * + * --bank 4=pop_cdraw.c --bank 4=pop_kdraw.c + * + * Линкер этого НЕ проверяет: если один из них переедет в другой банк, + * сборка пройдёт молча, а на устройстве получится прыжок в чужую страницу. + * Меняешь раскладку банков в Makefile — проверь эту пару. + * + * Публичные (из резидента/других банков) входы того же модуля объявлены с + * __banked в pop_kid.h — там pop_sword_load. + */ +#ifndef _POP_KDRAW_H +#define _POP_KDRAW_H + +#include +#include "pop_char.h" +#include "pop_kid.h" + +/* Клинок в руке персонажа (add_sword_to_objtable). Возврат 1 — нарисован, + * и в (*rx,*ry,*rw,*rh) лежит его прямоугольник для heal. Страницу W0 + * оставляет замапленной: размаппит вызывающий. */ +uint8_t pop_sword_draw(const pop_char_t *ch, const kframe *fr, + int obj_x, int obj_y, + int *rx, int *ry, uint16_t *rw, uint16_t *rh); + +/* Спрайт из атласов Кида по его id (полоса HP: деления 216/217). */ +void pop_kid_img_blit(uint8_t image, int x, int y); + +#endif /* _POP_KDRAW_H */ diff --git a/applications/SprPoP/src/_pop_kid.h b/applications/SprPoP/src/_pop_kid.h new file mode 100644 index 0000000..ceb017e --- /dev/null +++ b/applications/SprPoP/src/_pop_kid.h @@ -0,0 +1,26 @@ +/* + * _pop_kid.h — ВНУТРЕННИЙ контракт движка Kid: pop_kid.c (резидент W1, + * интерпретатор seqtbl) ↔ pop_kboot.c (банк 8, загрузка ресурсов). + * + * Загрузка атласов Кида и страницы анимации отрабатывает РАЗ ЗА ИГРУ, и + * держать её в дефицитном W1 незачем; интерпретатор же зовут из четырёх + * банков каждый тик — он остался в резиденте. Общее между ними — только + * дескриптор страницы данных: он объявлен здесь и определён в pop_kid.c + * (данные банковых модулей всё равно линкуются в общий _DATA). + */ +#ifndef _POP_KID_H +#define _POP_KID_H + +#include + +/* Смещение данных в странице анимации; ниже 0x100 — ISR-стаб W0. */ +#define KD_DATA_OFF 0x100u + +extern uint8_t kdat_blk; /* EMM-блок страницы kid.ani */ +extern uint8_t kdat_page; /* её номер (маппится в W0 на время play_seq) */ +extern uint8_t kdat_ok; /* данные загружены */ + +/* Файловый обмен с W3 делает общий резидентный bank_load_file(), поэтому + * оркестратор загрузки kid.ani целиком живёт в холодном банке 8. */ + +#endif /* _POP_KID_H */ diff --git a/applications/SprPoP/src/_pop_level.h b/applications/SprPoP/src/_pop_level.h new file mode 100644 index 0000000..8bfabae --- /dev/null +++ b/applications/SprPoP/src/_pop_level.h @@ -0,0 +1,79 @@ +/* + * _pop_level.h — ВНУТРЕННИЙ контракт между двумя половинами уровня: + * + * pop_level.c — ГОРЯЧАЯ (резидент W1): чтение тайлов/связей/дверных + * таблиц, живое состояние стражей. Её зовут все банки + * и кадровый путь главного цикла, местами на каждый тайл. + * pop_level_cold.c — ХОЛОДНАЯ (банк 8): чтение файла уровня, разбор комнаты, + * рестарт, потабличные различия уровней. Всё это + * отрабатывает раз на уровень или раз на комнату, и + * держать ~1.9 КБ в дефицитном W1 незачем. + * + * Общее состояние (страница уровня, копии дверных таблиц, живые стражи) + * живёт в ГОРЯЧЕЙ половине и объявлено здесь: данные банковых модулей + * линкуются в общий _DATA (--bank-data мы не включаем), поэтому обе + * половины видят их как обычные глобалы — в банк уехал только КОД. + * + * Заголовок НЕ публичный: снаружи уровень виден только через pop_level.h. + */ +#ifndef _POP_LEVEL_H +#define _POP_LEVEL_H + +#include + +/* Раскладка страницы уровня (Table 6 POP-DAT); подробности — в pop_level.c. */ +#define LVL_DATA_OFF 0x100u /* данные с 0x100; 0x00..0xFF — ISR-стаб */ +#define LVL_PRISTINE_OFF 0x1000u /* эталон foretable для рестарта уровня */ + +#define BP_FG 0u +#define BP_BG 720u +#define BP_LINKLOC 1440u +#define BP_LINKMAP 1696u +#define BP_LINKS 1952u +#define BP_GTILE 2119u +#define BP_GDIR 2143u +#define BP_GX 2167u +#define BP_GSEQLO 2191u +#define BP_GSKILL 2215u +#define BP_GSEQHI 2239u +#define BP_GCOLOR 2263u +#define BP_START 2112u +#define ROOM_TILES 30u +#define MIN_SIZE (BP_START + 3u) + +/* Живое состояние стражей: 6 байт на комнату (порт level.guards_*). */ +#define GS_TILE 0 +#define GS_DIR 1 +#define GS_X 2 +#define GS_SKILL 3 +#define GS_SEQL 4 +#define GS_SEQH 5 + +/* ---- общее состояние (определено в pop_level.c) ---------------------- */ +extern uint8_t pop_lvl_blk; /* EMM-блок страницы уровня */ +extern uint8_t pop_lvl_page; /* её номер (маппится в W0/W3) */ +extern uint8_t pop_lvl_ok; /* уровень загружен */ +extern uint8_t pop_dl1[256]; /* LINKLOC как в файле */ +extern uint8_t pop_dl2[256]; /* LINKMAP, МУТАБЕЛЬНА (таймеры кнопок) */ +extern uint8_t pop_gstate[24 * 6]; + +/* Указатели на тайлы комнаты r (1..24) в W0-пространстве — страницу мапит + * вызывающий. static-инлайн в обеих половинах: две команды сложения, звать + * их через границу банка было бы дороже самого вычисления. */ +static const uint8_t *room_fg_ptr(uint8_t r) +{ + return (const uint8_t *)(LVL_DATA_OFF + BP_FG) + (uint16_t)(r - 1) * ROOM_TILES; +} +static const uint8_t *room_bg_ptr(uint8_t r) +{ + return (const uint8_t *)(LVL_DATA_OFF + BP_BG) + (uint16_t)(r - 1) * ROOM_TILES; +} + +/* ПРАВИЛО ОКНА W3. Банковый модуль исполняется ИЗ W3 и сам менять окно + * не может. Файлы в EMM читает резидентный bank_load_file(), после его + * возврата холодный код продолжает подготовку страницы через W0. */ + +/* Живая копия стражей из данных уровня (загрузка/рестарт) — в холодной. */ +void pop_gstate_init(void) __banked; + +#endif /* _POP_LEVEL_H */ diff --git a/applications/SprPoP/src/_pop_sfx.h b/applications/SprPoP/src/_pop_sfx.h new file mode 100644 index 0000000..b765214 --- /dev/null +++ b/applications/SprPoP/src/_pop_sfx.h @@ -0,0 +1,22 @@ +/* + * _pop_sfx.h — внутренний контракт между половинами звука (НЕ публичный, + * публичный — pop_sfx.h). Горячая половина живёт в резиденте + * (pop_sfx.c), холодная в банке (pop_sfx_cold.c); делят они таблицу + * звуков, массив физических страниц и насос. + */ +#ifndef _POP_SFX_INTERNAL_H +#define _POP_SFX_INTERNAL_H + +#include +#include "pop_sound_tbl.h" /* pop_snd_tbl, POP_SND_* — генерит упаковщик */ + +/* Физические страницы набора, по индексу из pop_snd_tbl[].page. */ +extern uint8_t pop_snd_page[POP_SND_PAGES]; +/* 1 — набор поднят и CBL открыт; 0 — играем молча. */ +extern uint8_t pop_snd_ok; + +/* Насос: отдать n байт в CBL. Резидентный — его адрес отдаётся в + * cbl_open() холодной половиной. */ +int pop_sfx_fill(uint16_t n); + +#endif diff --git a/applications/SprPoP/src/_pop_tile.h b/applications/SprPoP/src/_pop_tile.h new file mode 100644 index 0000000..2069088 --- /dev/null +++ b/applications/SprPoP/src/_pop_tile.h @@ -0,0 +1,18 @@ +/* + * _pop_tile.h — внутренний контракт между половинами общих листьев слоя + * фона (НЕ публичный, публичный — pop_tile.h). Горячая половина живёт в + * резиденте (pop_tile.c), холодная — в банке 5 (pop_tile_cold.c); + * критерий разреза и замеры — в шапке pop_tile_cold.c. + */ +#ifndef _POP_TILE_INTERNAL_H +#define _POP_TILE_INTERNAL_H + +#include + +/* Клипованный путь блита — статик горячей половины, ставший общим: его + * зовёт pop_mem_b из холодной. Переносить его следом НЕЛЬЗЯ (его основной + * вызывающий pop_blit_b делает 184 вызова на кадр редрава), а вызов + * банк -> резидент и так прямой: W1/W2 замаплены всегда. */ +void blit_b_clip(const uint8_t *img, int x, int top, uint8_t w, uint8_t h); + +#endif diff --git a/applications/SprPoP/src/guards.c b/applications/SprPoP/src/guards.c new file mode 100644 index 0000000..8126101 --- /dev/null +++ b/applications/SprPoP/src/guards.c @@ -0,0 +1,1164 @@ +/* + * guards.c — ЛОГИКА стража, собирается в БАНК (--bank 1=guards.c, окно W3). + * + * Правила банка (проверено пробником tests/w3bankgfx): + * - состояние держим в W1/W2 (pop_guard.c): статики банка линкуются в + * страницу банка и снаружи не читаются; + * - звать можно W1/W2-код (pop_map, pop_kid, pop_geom, libc, примитивы + * libbgi) — скобка _bgi_begin/_bgi_end возвращает страницу БАНКА; + * - звать НЕЛЬЗЯ резидент W3 (pop_bg) — ни напрямую, ни транзитивно: + * пока исполняется банк, резидентной страницы в адресном пространстве + * нет. Поэтому фон/окклюзию за стража рисует главный цикл. + * + * Порт: seg003 check_can_guard_see_kid, seg002 autocontrol_*, seg000 + * play_guard_frame, seg006 char_opp_dist. ДИСПЕТЧЕР у соперника не свой: + * ИИ выставляет те же глобалы control_* (pop_ctrl.h), что и ввод игрока, а + * дальше работает общий pop_control(). + */ +#include "pop_guard.h" +#include "pop_sfx.h" /* звуковые эффекты (../docs/sound_plan.md) */ +#include "pop_kid.h" +#include "pop_ctrl.h" +#include "pop_map.h" +#include "pop_geom.h" +#include "pop_trob.h" +#include "pop_level.h" /* pop_current_level — спецсобытие уровня 3 */ +#include "pop_state.h" /* pop_immortal, pop_droppedout */ +#include "pop_tile.h" /* POP_TILE_DIV — таблица колонок (tile_div_tbl) */ +#include "pop_tune.h" /* POP_DBG_SHADOW_NOWAIT — отладка сцены тени */ + +#define TILE_MIDX 7 /* середина тайла в координатах персонажа */ +#define SCREENSPACE_X 58 /* = pop_x_bump[FIRST_ONSCREEN_COLUMN] */ + +#define TILE_FLOOR 1 +#define TILE_SPIKE 2 +#define TILE_GATE 4 +#define TILE_DOORTOP_FLOOR 7 +#define TILE_LOOSE 11 +#define TILE_DOORTOP 12 +#define TILE_CHOMPER 18 +#define TILE_WALL 20 + +/* actions (types.h:334) — нужны ветке «идти ли за упавшим Кидом». */ +#define ACT_HANGCLIMB 2 +#define ACT_HANGSTRAIGHT 6 + +/* tile_is_floor (seg004): не-пол — пустота, ковёр, верх двери, стена и + * «половинки» 26..29. Дубликат крошечного switch'а из pop_map: тянуть его + * через границу банка дороже, чем повторить. */ +/* Темы, которые звучат ОДИН раз за заход на уровень: тень шестого уровня + * (seg002:0090) и вступление первого. Оригинал отмечает «уже играли» + * через leveldoor_open = 0x4D, то есть портит переменную двери; у нас на + * это есть свои байты, а сбрасывает их старт уровня. */ +static uint8_t shadow6_music; + +void pop_music_level_start(void) __banked +{ + shadow6_music = 0; + pop_ctrl_level_start(); +} + +static uint8_t tile_is_floor(uint8_t t) +{ + switch (t) { + case 0: case 9: case 12: case 20: + case 26: case 27: case 28: case 29: + return 0; + default: + return 1; + } +} + +/* get_tile_at_kid (seg003:761) СНЯТ вместе с переходом луча на колонки: + * единственным его вызывающим был этот луч, а он больше не работает с + * X-координатами (разбор — в теле pop_check_can_guard_see_kid ниже). + * Таблица POP_TILE_DIV осталась в резиденте, ею пользуется физика. */ + +/* Буфер тайлов луча — file-scope, а не локальный массив: локальный уезжает + * в стековый кадр, и каждое чтение из него стоит `-n(ix)` (19 тактов + * номинала, ~46 с wait-state'ами). Тот же приём, что у draw_tile и + * pop_blit_b. Размер — колонки −1..10 плюс запас. */ +static uint8_t row_t[12]; + +/* check_can_guard_see_kid (seg003:688) — луч видимости по ряду. + * Результат в can_guard_see_kid: 0 не видит / 1 видит, но не пойдёт / + * 2 видит и пойдёт. */ +void pop_check_can_guard_see_kid(void) __banked +{ + uint8_t kid_frame = Kid.frame; + uint8_t tile; + + /* МЫШЬ Кида «не видит» (seg003:0965): иначе он выхватит меч на зверька, + * пробегающего мимо. */ + if (Guard.charid == CHARID_24_MOUSE) { can_guard_see_kid = 0; return; } + + /* Первый множитель — порт seg003:702. ТЕНЬ боевая только на 12-м + * уровне; на 4/5/6 она Кида «не видит», и это не косметика: увидев её, + * Кид достаёт меч (control_standing, seg005:352), а сама тень идёт той + * же веткой control_standing — встаёт в боевую стойку и с кадра 150 + * рисуется спрайтами СТРАЖА. Пустой слот (charid 0) — наша добавка: + * оригинал гасит его через direction = dir_56_none. */ + if (!(Guard.charid != 0 && + (Guard.charid != CHARID_1_SHADOW || + pop_current_level == SHADOW_FIGHT_LEVEL) && + /* кадры 219..228 — подъём по лестнице: там страж Кида не видит */ + kid_frame != 0 && (kid_frame < 219 || kid_frame >= 229) && + Guard.direction != DIR_56_NONE && + Kid.alive < 0 && Guard.alive < 0 && + Kid.room == Guard.room && Kid.curr_row == Guard.curr_row)) { + can_guard_see_kid = 0; + return; + } + + can_guard_see_kid = 2; + /* ПО КОЛОНКАМ, а не по X-координатам. + * + * Оригинал (и SDLPoP, seg003:688) идёт по x с шагом TILE_SIZEX и на + * каждом шаге переводит x в колонку — потому что x у него уже под рукой. + * Но начальные x — это РОВНО центры тайлов персонажей + * (x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX), а перевод обратно + * даёт ту же колонку: floor((58 + col*14 - 58) / 14) == col. Значит + * весь проход эквивалентен обходу колонок, и ни одна x-координата тут не + * нужна: уходят 16-битный шаг, 16-битное сравнение и индексация таблицы + * POP_TILE_DIV на каждой итерации. + * + * Тайлы отрезка забираем ОДНИМ банковым вызовом. Раньше на каждую + * колонку шёл свой pop_tile_at (__banked, банк 1 -> банк 3): на сцене + * 11/15 это девять трамплинов и 36 786 тактов на весь луч (замер + * 2026-08-19). */ + { + int8_t lcol = Kid.curr_col, rcol = Guard.curr_col, col, lcol0; + + if (lcol > rcol) { int8_t t = lcol; lcol = rcol; rcol = t; } + lcol0 = lcol; /* БАЗА индексации буфера — фиксируем + * ДО сдвигов ниже: row_t[0] это + * тайл lcol0, и сдвиг lcol/rcol на + * чомпере и воротах её менять не + * должен. */ + pop_row_tiles(Kid.curr_row, lcol, rcol, row_t); + + /* Чомпер стоит у ЛЕВОГО края тайла — свой тайл он не перекрывает. */ + if (row_t[0] == TILE_CHOMPER) lcol++; + /* Ворота — у ПРАВОГО края, поэтому крайний правый тайл не считается. */ + if (row_t[rcol - lcol0] == TILE_GATE) rcol--; + + for (col = lcol; col <= rcol; col++) { + tile = row_t[col - lcol0]; + /* Сквозь это не видно вовсе. */ + if (tile == TILE_WALL || tile == TILE_DOORTOP_FLOOR || tile == TILE_DOORTOP) { + can_guard_see_kid = 0; + return; + } + /* Видно, но страж не пойдёт: провалится / попадёт под чомпер / + * упрётся в неоткрытые ворота / шагнёт в дыру. */ + if (tile == TILE_LOOSE || tile == TILE_CHOMPER || !tile_is_floor(tile)) { + can_guard_see_kid = 1; + } else if (tile == TILE_GATE && + pop_gate_modif(col, Kid.curr_row) < 112) { + can_guard_see_kid = 1; /* ворота подняты не до конца */ + } + } + } +} + + +/* ---- Скелет: спецсобытие подъёма (check_skel, seg002:1042) ---------- * + * На уровне 3 стражей в данных НЕТ ВООБЩЕ (guards_tile пуст во всех 24 + * комнатах) — единственный противник поднимается сам: когда Кид в комнате 1 + * доходит до колонки 2 или 3 при открытой двери уровня, тайл 21 (скелет) + * стирается в пол, а на его месте встаёт персонаж слота Guard с уже вынутым + * мечом. Отсюда и место здесь, рядом с ИИ, а не в enter_guard. */ +#define SKEL_LEVEL 3 +#define SKEL_ROOM 1 +#define SKEL_COL 5 +#define SKEL_ROW 1 +#define SKEL_TRIG_COL1 2 +#define SKEL_TRIG_COL2 3 +#define SKEL_SKILL 2 +#define SKEL_HP 3 +#define TILE_SKELETON 21 + +void pop_check_skel(void) __banked +{ + if (pop_current_level != SKEL_LEVEL) return; + if (Guard.charid != 0) return; /* слот занят (dir_56_none) */ + if (Kid.room != SKEL_ROOM) return; + if (!pop_leveldoor_open) return; + if (Kid.curr_col != SKEL_TRIG_COL1 && Kid.curr_col != SKEL_TRIG_COL2) return; + if (pop_tile_at(SKEL_COL, SKEL_ROW) != TILE_SKELETON) return; + + pop_skel_wake_tile((uint8_t)(SKEL_ROW * 10 + SKEL_COL)); + + /* Поднимаем В ОКНЕ Char — как оригинал (он же и saveshad'ит). */ + pop_loadshad(); + Char.room = Kid.room; + Char.curr_row = SKEL_ROW; + Char.y = (uint8_t)pop_y_land[SKEL_ROW + 1]; + Char.curr_col = SKEL_COL; + Char.x = (uint8_t)(pop_x_bump[SKEL_COL + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX); + Char.direction = -1; /* dir_FF_left */ + Char.alive = -1; + Char.sword = SWORD_2_DRAWN; + Char.charid = CHARID_4_SKELETON; + Char.fall_x = Char.fall_y = 0; + pop_char_set_seq(SEQ_88_SKEL_WAKE_UP); + pop_sfx_play(44); /* seg002:106D — скелет оживает */ + play_seq(); + pop_saveshad(); + + guard_skill = SKEL_SKILL; + guardhp_max = guardhp_curr = SKEL_HP; + guardhp_delta = 0; + is_guard_notice = guard_refrac = 0; +} + +/* ---- Мышь: спецсобытие уровня 8 (do_timers seg003:0545 + do_mouse) --- * + * + * Кнопка (0,7) комнаты 16 открывает решётку (0,3), но с левой площадки + * верхнего ряда до кнопки не дойти — между ними провал в три колонки. + * Поэтому, когда дверь уровня уже открыта, а Кид всё ещё в комнате 16, + * оригинал через POP_MOUSE_DELAY кадров присылает мышь: она пробегает + * справа налево, наступает на кнопку (кнопку жмёт ОБЩИЙ check_press из + * физики соперника, как и любой персонаж) и убегает обратно. + * + * Счётчик — сам pop_leveldoor_open, как в оригинале: он растёт ТОЛЬКО пока + * Кид в комнате 16, поэтому «12,5 секунды» отсчитываются с момента, когда + * игрок туда вернулся, а не с открытия двери. Ради этого он и word + * (см. pop_state.c). + * + * Слот соперника занимаем БЕЗ проверки «свободен ли» — так в оригинале, и + * на уровне 8 конфликта нет: в данных комнаты 16 стража нет + * (guards_tile[15] = 30 = «пусто»). */ +#define SEQ_105_MOUSE_FORWARD 105 +#define SEQ_107_MOUSE_STAND_UP_GO 107 + +void pop_check_mouse(void) __banked +{ + if (pop_current_level != POP_MOUSE_LEVEL) return; + if (Kid.room != POP_MOUSE_ROOM) return; + if (!pop_leveldoor_open) return; + if (++pop_leveldoor_open != POP_MOUSE_DELAY) return; + + /* do_mouse (seg003:0ABA) поднимает мышь В ОКНЕ Char, начиная с КИДА + * (loadkid): поля, которых нет в списке ниже — frame/action/room/ + * fall_x/fall_y — наследуются от него. Комната как раз оттуда и + * берётся, отдельного присваивания в оригинале нет. */ + pop_loadkid(); + Char.charid = CHARID_24_MOUSE; + Char.x = POP_MOUSE_START_X; + Char.curr_row = 0; + Char.y = (uint8_t)pop_y_land[1]; + Char.alive = -1; + Char.direction = -1; /* dir_FF_left */ + guardhp_curr = 1; + pop_char_set_seq(SEQ_105_MOUSE_FORWARD); + play_seq(); + pop_saveshad(); +} + +/* ---- Тень: спецсобытие входа в комнату (check_shadow, seg002:0064) -- * + * + * На «шадоу»-уровнях слот соперника занимает ТЕНЬ, и занимает его ВМЕСТО + * стража: оригинал вызывает check_shadow при смене комнаты и уходит в + * return, не доходя до enter_guard. Важно, что на уровне 5 в комнате 24 + * страж в данных ЕСТЬ (`guards_tile[23] = 8`) — если позвать enter_guard, + * он поднимется там, где его быть не должно. Поэтому вызывающий обязан + * звать pop_check_shadow ВМЕСТО pop_guard_enter и уважать её ответ. + * + * do_init_shad (seg002:0000): первые 7 полей персонажа берутся из таблицы + * init_shad_N, остальные наследуются от того, что уже лежит в окне Char. У + * оригинала там КИД (check_shadow идёт сразу после loadkid(), seg000:1312), + * поэтому наследование сделано явным — alive/sword берём у Кида, а не у + * прошлого обитателя слота. Поднимаем в окне Char и saveshad'им, как + * pop_check_skel и как сам do_init_shad. */ +/* Состояние проигрывателя автодвижений (seg002: demo_time/demo_index) — + * объявлено здесь, потому что do_init_shad его сбрасывает, а движок + * do_auto_moves живёт ниже, рядом с остальным ИИ. */ +static uint8_t demo_time; /* кадров с начала записи (0xFE = стоп) */ +static uint8_t demo_index; /* текущая запись */ + +void pop_guard_ai_qsave(pop_qs_io_t *io) __banked +{ + pop_qs_u8(io, &demo_time); + pop_qs_u8(io, &demo_index); +} + +#define SHADOW_SKILL 3 +#define SHADOW_HP 4 +#define SEQ_2_STAND 2 +#define SEQ_7_FALL 7 +#define TILE_POTION 10 +#define TILE_SWORD 22 + +/* Таблицы init_shad_N (data:0EEA/0EF2) — ПЕРВЫЕ СЕМЬ полей Char в том же + * порядке, в каком их копирует оригинал (`memcpy(&Char, source, 7)`): + * frame, x, y, direction, curr_col, curr_row, action. + * 5: x 55, y 55, вправо, колонка −1 (ещё за левым краем экрана), ряд 0; + * 6: x 81, y 118, вправо, колонка 0, ряд 1 — тень стоит на границе + * (1,0)-(1,1), одного осторожного шага хватает, чтобы встать на плиту; + * 12: x 81, y 232, вправо, колонка 0, ряд 0 — и это НЕ ошибка данных: 232 + * лежит НИЖЕ поля (192), поле y беззнаковое, и тень с seq_7 «падает» + * из-под комнаты, переполняя байт, то есть влетает в кадр сверху. + * Ряд при этом берётся ИЗ ТАБЛИЦЫ (0), а не пересчитывается из y + * (иначе был бы −1): семь полей копируются как есть, и «поправить» + * их согласованность нельзя (см. ../docs/levels_12_15_plan.md §7.5). */ +static const uint8_t init_shad_5[7] = { 0x0F, 0x37, 0x37, 0, 0xFF, 0, 0 }; +static const uint8_t init_shad_6[7] = { 0x0F, 0x51, 0x76, 0, 0x00, 1, 0 }; +static const uint8_t init_shad_12[7] = { 0x0F, 0x51, 0xE8, 0, 0x00, 0, 0 }; + +/* seq — поза, с которой тень входит в сцену: 5/6 встают в стойку, 12 падает. */ +static void do_init_shad(const uint8_t *tbl, uint8_t room, uint8_t seq) +{ + pop_loadshad(); + Char.frame = tbl[0]; + Char.x = tbl[1]; + Char.y = tbl[2]; + Char.direction = (int8_t)tbl[3]; + Char.curr_col = (int8_t)tbl[4]; + Char.curr_row = (int8_t)tbl[5]; + Char.action = tbl[6]; + Char.room = room; + Char.charid = CHARID_1_SHADOW; + Char.alive = Kid.alive; /* наследование от Кида, см. шапку */ + Char.sword = Kid.sword; + Char.fall_x = Char.fall_y = 0; + pop_char_set_seq(seq); + pop_saveshad(); + + demo_time = 0; + demo_index = 0; + guard_skill = SHADOW_SKILL; + guardhp_delta = 0; + guardhp_curr = guardhp_max = SHADOW_HP; + is_guard_notice = guard_refrac = 0; +} + +uint8_t pop_check_shadow(uint8_t room) __banked +{ + /* Уровень 12 (seg002:0070) — встреча с тенью в комнате 15. Три отличия + * от 5/6, и все три существенные: + * — событие одноразовое: после слияния (pop_united_shadow != 0) тень + * больше не поднимается ни при каком входе в комнату; + * — условие на тайл ОБРАТНОЕ уровню 5: там тень поднимается, ПОКА + * зелье на месте, здесь — только когда меча (15,1,0) уже НЕТ, то + * есть Кид его подобрал. Меч на месте — return 0, и это не «ничего + * не делать»: стража в комнате 15 в данных нет, так что вызывающий + * спокойно идёт в pop_guard_enter и убирает слот; + * — поза входа не стойка, а ПАДЕНИЕ (seq 7). + * pop_shadow_init сбрасываем здесь же: тень начинает «ждать Кида» + * заново при каждом подъёме (см. autocontrol_shadow_level12). */ + if (pop_current_level == SHADOW_FIGHT_LEVEL) { + if (pop_united_shadow || room != SHADOW_FIGHT_ROOM) return 0; + if (pop_level_tile(room, SHADOW_SWORD_TILEPOS) == TILE_SWORD) return 0; + pop_shadow_init = 0; + do_init_shad(init_shad_12, room, SEQ_7_FALL); + return 1; + } + /* Уровень 6 (seg002:0090): условий на содержимое комнаты нет вовсе — + * тень встаёт при КАЖДОМ входе в комнату 1. Оригинал играет здесь + * sound_25_presentation, помечая «уже играли» через leveldoor_open = + * 0x4D (переменную двери он использует как флаг). Чужую переменную не + * трогаем: у нас для этого свой однобайтовый флаг. */ + if (pop_current_level == SHADOW_STEP_LEVEL) { + if (room != SHADOW_STEP_ROOM) return 0; + if (!shadow6_music) { + shadow6_music = 1; + pop_sfx_play(25); + } + do_init_shad(init_shad_6, room, SEQ_2_STAND); + return 1; + } + if (pop_current_level != SHADOW_STEAL_LEVEL) return 0; + if (room != SHADOW_STEAL_ROOM) return 0; + /* Зелья уже нет — тень своё дело сделала. Возвращаем 1 и здесь: стража + * комнаты 24 оригинал не поднимает НИКОГДА (return до enter_guard). */ + if (pop_level_tile(room, SHADOW_POTION_TILEPOS) != TILE_POTION) return 1; + do_init_shad(init_shad_5, room, SEQ_2_STAND); + return 1; +} + +/* sword_disappears (seg002:0536) — спецсобытие уровня 12: меч, лежащий в + * комнате 15, пропадает с карты, как только Кид ушёл ВПРАВО из комнаты 18. + * Смысл событийный: дальше по уровню возврата за мечом уже нет, и сцена с + * тенью обязана быть однозначной — либо Кид пришёл к ней вооружённым, либо + * нет. Модификатор гасим вместе с тайлом (как SDLPoP): ненулевой modif на + * полу рисует «фальшивые» узоры. + * + * Звать при уходе ВПРАВО, ДО смены комнаты, — там же, где level3_set_chkp + * (оригинал держит оба в leave_room). */ +void pop_sword_disappears(void) __banked +{ + uint8_t *m; + if (pop_current_level != SHADOW_FIGHT_LEVEL) return; + if (Kid.room != SWORD_GONE_ROOM) return; + pop_level_set_tile(SHADOW_FIGHT_ROOM, SHADOW_SWORD_TILEPOS, TILE_FLOOR); + m = pop_trob_modif(SHADOW_FIGHT_ROOM); + if (m) m[SHADOW_SWORD_TILEPOS] = 0; +} + +/* meet_Jaffar (seg002:0544) — спецсобытие уровня 13: Кид уходит ВПРАВО из + * комнаты 3 и попадает прямо к Джафару. Всё событие — фора: пока + * pop_guard_notice_timer идёт, Джафар не встаёт в боевую стойку + * (autocontrol_guard_inactive), то есть у Кида есть ~2 секунды достать меч. + * + * `pop_leveldoor_open == 0` в условии не косметика: вернувшись к Джафару + * после его смерти (дверь уже открыта), форы быть не должно. + * + * Звать при уходе ВПРАВО, ДО смены комнаты, — рядом с pop_sword_disappears + * (оригинал держит оба в одной ветке leave_room). */ +void pop_meet_jaffar(void) __banked +{ + if (pop_current_level != JAFFAR_LEVEL) return; + if (pop_leveldoor_open != 0) return; + if (Kid.room != JAFFAR_MEET_ROOM) return; + pop_sfx_play(29); /* тема встречи с Джафаром */ + pop_guard_notice_timer = JAFFAR_NOTICE_TIME; +} + +/* Jaffar_exit (seg002:0517) — парная половина победы: смерть Джафара сама + * дверь НЕ открывает, она лишь ставит pop_leveldoor_open = 2. Дверь + * открывает КНОПКА комнаты 24 (tilepos 0 — настоящий opener, проверено по + * res2013.bin), «нажимаемая» в момент ухода Кида ВЛЕВО. + * + * Звать при уходе ВЛЕВО, ДО смены комнаты, — рядом с чекпойнтом уровня 3. */ +void pop_jaffar_exit(void) __banked +{ + uint8_t *m; + if (pop_leveldoor_open != 2) return; + m = pop_trob_modif(JAFFAR_EXIT_ROOM); + pop_trigger_button(JAFFAR_EXIT_ROOM, JAFFAR_EXIT_TILEPOS, + pop_level_tile(JAFFAR_EXIT_ROOM, JAFFAR_EXIT_TILEPOS), + m ? m[JAFFAR_EXIT_TILEPOS] : 0, + 0 /* seg002:520 — playsound=0 */); +} + +/* on_guard_killed (seg006:1927) — соперник только что потерял последнее HP. + * Уровень 13 — единственный, где у этого есть последствия помимо звука: + * белая вспышка и pop_leveldoor_open = 2, то есть «выход разрешён» (сама + * дверь откроется кнопкой из pop_jaffar_exit). + * + * Ветку `is_show_time = 1` не портируем: это показ ОСТАВШЕГОСЯ ВРЕМЕНИ, а + * часов на 60 минут у нас пока нет вовсе — появятся вместе с таймером игры. + * Демо-уровень (0) сюда тоже не входит: его ветка — часть аттракта. */ +static void on_guard_killed(void) +{ + /* Демо-уровень (0) у оригинала уходит в свою ветку — «беги из комнаты», + * без единого звука (seg006:1929). Заставочная демка озвучена только + * эффектами, музыки в ней нет вовсе. */ + if (pop_current_level == 0) return; + if (pop_current_level != JAFFAR_LEVEL) { + /* seg006:1940 — победный трек за стража; за тень его нет: она не + * «убита», а слилась с Кидом. */ + if (Char.charid != CHARID_1_SHADOW) pop_sfx_play(37); + return; + } + pop_sfx_play(43); /* смерть Джафара — свой трек */ + pop_flash_color = POP_FLASH_WHITE; + pop_flash_time = JAFFAR_FLASH_TIME; + pop_leveldoor_open = 2; +} + +/* check_killed_shadow (seg006:2033) — «убил тень = убил себя». На уровне 12 + * Кид и тень связаны насмерть: как только один из них перестал быть живым + * (alive >= 0), второму отсчитывается 100 урона, то есть гарантированная + * смерть, и обоих накрывает белая вспышка. + * + * Условие оригинала `(Char.charid | Opp.charid) == charid_1_shadow` читается + * так: в паре ровно Кид (0) и тень (1) — со стражем или скелетом связи нет. + * Оригинал зовёт это ДВАЖДЫ за кадр (из тика Кида и из тика соперника, с + * уже переставленными окнами); нам хватает одного вызова на кадр с явным + * разбором обеих сторон — окна Char/Opp здесь не нужны, кроме как для + * маршрутизации урона внутри pop_take_hp. */ +void pop_check_killed_shadow(void) __banked +{ + if (pop_current_level != SHADOW_FIGHT_LEVEL) return; + if ((uint8_t)(Kid.charid | Guard.charid) != CHARID_1_SHADOW) return; + if (Kid.alive < 0 && Guard.alive >= 0) { + pop_flash_color = POP_FLASH_WHITE; + pop_flash_time = 5; + pop_loadkid(); + pop_take_hp(100); + } else if (Guard.alive < 0 && Kid.alive >= 0) { + pop_flash_color = POP_FLASH_WHITE; + pop_flash_time = 5; + pop_loadshad(); + pop_take_hp(100); + } +} + +/* ---- Синтетический ввод (seg002:0706..) --------------------------- */ +static void move_0_nothing(void) +{ + control_shift = control_y = control_x = control_shift2 = 0; + control_down = control_up = control_backward = control_forward = 0; +} +static void move_1_forward(void) +{ + control_x = POP_CONTROL_HELD_FORWARD; + control_forward = POP_CONTROL_HELD; +} +static void move_2_backward(void) +{ + control_backward = POP_CONTROL_HELD; + control_x = POP_CONTROL_HELD_BACKWARD; +} +static void move_3_up(void) +{ + control_y = POP_CONTROL_HELD_UP; + control_up = POP_CONTROL_HELD; +} +static void move_4_down(void) +{ + control_down = POP_CONTROL_HELD; + control_y = POP_CONTROL_HELD_DOWN; +} +static void move_6_shift(void) +{ + control_shift = POP_CONTROL_HELD; + control_shift2 = POP_CONTROL_HELD; +} +/* move_7 (seg002:0770): ОТПУСТИТЬ shift — отдельный ход, потому что + * move_0_nothing сбрасывает всё, а здесь нужно снять только его. */ +static void move_7(void) +{ + control_shift = POP_CONTROL_RELEASED; +} +static void move_down_back(void) +{ + control_down = POP_CONTROL_HELD; + move_2_backward(); +} +static void move_down_forw(void) +{ + control_down = POP_CONTROL_HELD; + move_1_forward(); +} + +/* autocontrol_guard_inactive (seg002:710): страж с убранным мечом. + * Пока Кид за спиной — страж не реагирует; исключение — «шум» Кида + * (is_guard_notice), от которого страж оборачивается. */ +static void autocontrol_guard_inactive(void) +{ + int16_t distance; + if (Kid.alive >= 0) return; + distance = pop_char_opp_dist(); + /* В оригинале условие записано как (word)distance < (word)-8 — + * беззнаковое сравнение, истинное для ВСЕГО, кроме диапазона −8..−1. + * То есть: Кид не «вплотную за спиной». */ + if (Opp.curr_row != Char.curr_row || distance >= 0 || distance < -8) { + if (is_guard_notice) { + is_guard_notice = 0; + if (distance < 0) { + if (distance < -4) move_4_down(); /* обернуться на шум */ + return; + } + } else if (distance < 0) { + return; + } + } + if (can_guard_see_kid) { + /* Увидел — переходит в боевую стойку (вниз+вперёд = достать меч). + * Уровень 13: пока идёт фора после встречи (pop_meet_jaffar), Джафар + * стоит и ждёт — это его единственное отличие от прочих стражей. */ + if (pop_current_level != JAFFAR_LEVEL || pop_guard_notice_timer == 0) + move_down_forw(); + } +} + +/* ---- Активный страж (меч обнажён), seg002:737.. -------------------- * + * Таблицы вероятностей по 12 градациям мастерства (seg002:26..38). Бросок + * — prandom(255) с СОБСТВЕННЫМ сидом: у нас последовательности разведены + * по доменам (кладка / анимация тайлов / бой), см. pop_geom.h. */ +static const uint8_t STRIKEPROB[NUM_GUARD_SKILLS] = { 61,100, 61, 61, 61, 40,100,220, 0, 48, 32, 48}; +static const uint8_t RESTRIKEPROB[NUM_GUARD_SKILLS] = { 0, 0, 0, 5, 5,175, 16, 8, 0,255,255,150}; +static const uint8_t BLOCKPROB[NUM_GUARD_SKILLS] = { 0,150,150,200,200,255,200,250, 0,255,255,255}; +static const uint8_t IMPBLOCKPROB[NUM_GUARD_SKILLS] = { 0, 61, 61,100,100,145,100,250, 0,145,255,175}; +static const uint8_t ADVPROB[NUM_GUARD_SKILLS] = {255,200,200,200,255,255,200, 0, 0,255,100,100}; + +/* Бросок «сработало ли»: в оригинале prob > prandom(255). */ +static uint8_t roll(uint8_t prob) +{ + return (uint8_t)(prob > (uint8_t)pop_prandom(&pop_fight_seed, 255)); +} + +/* guard_advance (seg002:0AF5): наступать. Мастерство 0 наступает всегда, + * остальные — только пока Кид не замахнулся. */ +static void guard_advance(void) +{ + if (guard_skill == 0 || kid_sword_strike == 0) { + if (roll(ADVPROB[guard_skill])) move_1_forward(); + } +} + +/* guard_block (seg002:0B1D): блокировать замах Кида. Если Кид только что + * пробил блок (justblocked) — своя, «импровизационная» вероятность. */ +static void guard_block(void) +{ + uint8_t of = Opp.frame; + if (of == FRAME_152_STRIKE_2 || of == FRAME_153_STRIKE_3 || + of == FRAME_162_BLOCK_TO_STRIKE) { + if (roll(justblocked ? IMPBLOCKPROB[guard_skill] + : BLOCKPROB[guard_skill])) + move_3_up(); + } +} + +/* guard_strike (seg002:0B73): бить. По замаху Кида не бьём (кадры 169/151); + * из своего парирования — по отдельной вероятности «добить». */ +static void guard_strike(void) +{ + uint8_t of = Opp.frame, cf = Char.frame; + if (of == FRAME_169_BEGIN_BLOCK || of == FRAME_151_STRIKE_1) return; + if (cf == FRAME_161_PARRY || cf == FRAME_150_PARRY) { + if (roll(RESTRIKEPROB[guard_skill])) move_6_shift(); + } else { + if (roll(STRIKEPROB[guard_skill])) move_6_shift(); + } +} + +/* autocontrol_guard_kid_armed (seg002:0AC1): Кид с мечом. Вплотную и + * далеко — наступаем; в «рабочей» дистанции 10..28 сначала пробуем блок, и + * только отдышавшись бьём. */ +static void autocontrol_guard_kid_armed(int16_t distance) +{ + if (distance < 10 || distance >= 29) { + guard_advance(); + } else { + guard_block(); + if (guard_refrac == 0) { + if (distance < 12 || distance >= 29) guard_advance(); + else guard_strike(); + } + } +} + +/* autocontrol_guard_kid_in_sight (seg002:0A93). */ +static void autocontrol_guard_kid_in_sight(int16_t distance) +{ + if (Opp.sword == SWORD_2_DRAWN) { + autocontrol_guard_kid_armed(distance); + } else if (guard_refrac == 0) { + /* Кид безоружен: подойти и рубить. */ + if (distance < 29) move_6_shift(); + else move_1_forward(); + } +} + +/* Тайл перед стражем / через один (порт get_tile_infrontof_char и + * get_tile_infrontof2_char, seg004) — но для Char, а не для Kid. */ +static uint8_t tile_infront(int8_t step) +{ + int8_t d = (int8_t)(Char.direction < 0 ? -step : step); + return pop_tile_at((int8_t)(Char.curr_col + d), Char.curr_row); +} + +/* autocontrol_guard_kid_far (seg002:09CB): идём, если впереди есть пол хотя + * бы через тайл; иначе пятимся (не шагать в пропасть). */ +static void autocontrol_guard_kid_far(void) +{ + if (tile_is_floor(tile_infront(1)) || tile_is_floor(tile_infront(2))) + move_1_forward(); + else + move_2_backward(); +} + +/* guard_follows_kid_down (seg002:09F8): Кида спихнули с уступа (droppedout), + * страж его больше не видит — идти ли следом вниз? Условия оригинала, все + * «не идти»: впереди стена; ИЛИ впереди не пол и при этом внизу пика, + * loose-плита, стена или пустота; ИЛИ Кид не ровно на ряд ниже. + * + * Тайлы читаются через pop_tile_at — это банк 1 → банк 3, то есть трамплин. + * Здесь это не жаль: ветка срабатывает только в кадре, когда Кид сорвался + * из боя, а не каждый кадр (её гейтят can_guard_see_kid == 0 и droppedout). + * + * Не идём — сбросить droppedout и попятиться; идём — шаг вперёд, дальше + * персонажа уводит вниз обычная физика (та же, что у Кида). */ +static void guard_follows_kid_down(void) +{ + uint8_t below, front; + if (Opp.action == ACT_HANGCLIMB || Opp.action == ACT_HANGSTRAIGHT) return; + front = tile_infront(1); + below = pop_tile_at((int8_t)(Char.curr_col + (Char.direction < 0 ? -1 : 1)), + (int8_t)(Char.curr_row + 1)); + if (front == TILE_WALL || + (!tile_is_floor(front) && + (below == TILE_SPIKE || below == TILE_LOOSE || below == TILE_WALL || + !tile_is_floor(below) || Char.curr_row + 1 != Opp.curr_row))) { + pop_droppedout = 0; + move_2_backward(); + } else { + move_1_forward(); + } +} + +/* autocontrol_guard_active (seg002:737). */ +static void autocontrol_guard_active(void) +{ + uint8_t cf = Char.frame; + uint8_t of; + int16_t distance; + + /* Кадр 166 (неактивная стойка) и всё, что ниже 150, — не боевые позы. */ + if (cf == FRAME_166_STAND_INACTIVE || cf < 150 || can_guard_see_kid == 1) + return; + + if (can_guard_see_kid == 0) { + /* seg002:740: Кид пропал из виду. Если его СПИХНУЛИ (droppedout) — + * решаем, идти ли за ним вниз; иначе просто отступаем. Скелет не + * отступает никогда (у него нет этой ветки в оригинале). */ + if (pop_droppedout) guard_follows_kid_down(); + else if (Char.charid != CHARID_4_SKELETON) move_down_back(); + return; + } + + /* can_guard_see_kid == 2 */ + of = Opp.frame; + distance = pop_char_opp_dist(); + /* Кид падает/приземляется в отскоке — не мешаем ему, ждём. */ + if (distance >= 12 && of >= 102 && of < 118 && Opp.action == 5) + return; + + if (distance < 35) { + if ((Char.sword < SWORD_2_DRAWN && distance < 8) || distance < 12) { + /* Слишком близко: если смотрим в одну сторону — развернуться. */ + if (Char.direction == Opp.direction) move_2_backward(); + else move_1_forward(); + } else { + autocontrol_guard_kid_in_sight(distance); + } + return; + } + + if (guard_refrac != 0) return; + if (Char.direction != Opp.direction) { + /* Кид разбежался — встречаем ударом, а не шагом. */ + if (of >= 7 && of < 15) { /* бег */ + if (distance < 40) move_6_shift(); + return; + } else if (of >= 34 && of < 44) { /* разбег в прыжок */ + if (distance < 50) move_6_shift(); + return; + } + } + autocontrol_guard_kid_far(); +} + +/* clear_char (seg006:1945): персонажа со сцены — направление «никуда», + * не жив, действие 0, HP обнулены. Полосу перерисует pop_hp_draw сам. */ +static void clear_char(void) +{ + Char.direction = DIR_56_NONE; + Char.alive = 0; + Char.action = 0; + guardhp_curr = 0; +} + +/* autocontrol_shadow_level4 (seg002:1131). Вся «логика» тени на уровне 4: + * в комнате зеркала она бежит вперёд (то есть ВЛЕВО — её направление + * зеркально Киду) и при x < 80 исчезает. Ни боя, ни преследования: она + * просто убегает за левый край и растворяется. */ +static void autocontrol_shadow_level4(void) +{ + if (Char.room != MIRROR_ROOM) return; + if (Char.x < 80) clear_char(); + else move_1_forward(); +} + +/* ---- Автодвижения: «демо-запись» вместо ИИ (seg002:1089) ----------- * + * + * Тень уровня 5 не принимает решений — она проигрывает ЗАПИСЬ нажатий: + * пара {time, move}, где time — момент по счётчику кадров demo_time, а + * move — номер синтетического хода (те же move_*, что заполняет + * autocontrol_*). Оригинал использует этот же движок для демо-режима + * заставки, поэтому имена сохранены. + * + * Тонкость выбора записи, которую легко «упростить» и сломать: когда время + * текущей записи ДОСТИГНУТО, индекс сдвигается вперёд, но ход берётся всё + * равно из ТЕКУЩЕЙ (ещё не сдвинутой) записи; пока не достигнуто — из + * ПРЕДЫДУЩЕЙ. То есть запись задаёт «с какого кадра действует её ход». */ +typedef struct { uint8_t time; int8_t move; } auto_move_t; + +/* shad_drink_move (data.h:866): подойти, взять зелье, выпить, уйти. */ +static const auto_move_t shad_drink_move[8] = { + {0x00, 0}, {0x01, 1}, {0x0E, 0}, {0x12, 6}, + {0x1D, 7}, {0x2D, 2}, {0x31, 1}, {0xFF, -2} +}; + +static void do_auto_moves(const auto_move_t *moves) +{ + uint8_t idx; + int8_t mv; + + if (demo_time >= 0xFE) return; + ++demo_time; + idx = demo_index; + if (moves[idx].time <= demo_time) ++demo_index; + else idx = (uint8_t)(demo_index - 1); + + mv = moves[idx].move; + switch (mv) { + case 0: move_0_nothing(); break; + case 1: move_1_forward(); break; + case 2: move_2_backward(); break; + case 3: move_3_up(); break; + case 4: move_4_down(); break; + case 5: move_3_up(); move_1_forward(); break; + case 6: move_6_shift(); break; + case 7: move_7(); break; + default: break; /* −1 «ничего», −2 «конец» */ + } +} + +/* autocontrol_shadow_level5 (seg002:1157). Тень стоит, пока не откроются + * ворота комнаты (openness >= 80), и только тогда запускает запись ходов: + * идёт к зелью, выпивает и уходит за левый край, где растворяется. + * + * Почему проверка ворот — именно через модификатор, а не через «дверь + * открыта»: `pop_leveldoor_open` тут ни при чём, речь о ВОРОТАХ (тайл 4) в + * колонке 1 верхнего ряда, и оригинал читает их openness напрямую. */ +static void autocontrol_shadow_level5(void) +{ + if (Char.room != SHADOW_STEAL_ROOM) return; + if (demo_time == 0) { +#if !POP_DBG_SHADOW_NOWAIT + uint8_t *m = pop_trob_modif(SHADOW_STEAL_ROOM); + if (m[SHADOW_DOOR_TILEPOS] < SHADOW_DOOR_OPEN_MIN) return; +#endif + demo_index = 0; + } + do_auto_moves(shad_drink_move); + if (Char.x < 15) clear_char(); +} + +/* autocontrol_shadow_level6 (seg002:1064). Вся логика — один осторожный шаг + * (Shift + вперёд) в тот кадр, когда Кид прыгает к решётке: тень встаёт на + * closer (1,1) и роняет решётку (1,2), за порожек которой Кид в этот момент + * цепляется. Условие смотрит на КИДА: кадр 43 — четвёртый кадр бег-прыжка, + * x < 128 отсекает прыжки в правой половине комнаты (там Кид ещё разбегается + * и до решётки ему далеко). */ +#define FRAME_43_RUNJUMP 43 + +static void autocontrol_shadow_level6(void) +{ + if (Char.room != SHADOW_STEP_ROOM) return; + if (Kid.frame != FRAME_43_RUNJUMP) return; + if (Kid.x >= 128) return; + move_6_shift(); + move_1_forward(); +} + +/* autocontrol_shadow_level12 (seg002:1184) — единственная тень, с которой + * можно и драться, и слиться. Четыре режима подряд, порядок важен. + * + * 1. «Ждать Кида» (Char.room 15, pop_shadow_init == 0). Пока Кид не дошёл + * до x >= 150, тень КАЖДЫЙ КАДР роняется заново — то есть висит в первом + * кадре падения у потолка. Дошёл — флаг поднят, падение пущено. Смысл: + * Кид обязан УВИДЕТЬ, как она сваливается сверху, а не застать готовую. + * 2. У тени вынут меч → обычный боевой ИИ стража. Исключение: если Кид + * намеренно убрал клинок (offguard) и тень ещё не отдышалась после + * попадания (guard_refrac) — она тоже убирает меч (move_4_down). + * 3. У тени меч в ножнах, но Кид ПРИ мече (или не убирал его намеренно): + * близко и видно (< 90) — выхватить клинок из стойки (кадр 15); далеко + * или не видно — стоять, а если Кид зашёл за спину, попятиться. + * 4. Оба без мечей — СЛИЯНИЕ: сойтись ближе 10 и объединиться. + * + * xdiff инициализируется заведомо большим числом (как в SDLPoP): при + * can_guard_see_kid < 2 расстояние не считается вовсе, и ветка «попятиться» + * не должна срабатывать на мусоре. */ +#define FRAME_15_STAND 15 +#define FRAME_3_START_RUN 3 +#define FRAME_127_STEPPING_7 127 +#define FRAME_133_AFTER_STEP 133 +#define SHADOW_WAIT_KID_X 150 +#define SHADOW_SWORD_DIST 90 + +static void autocontrol_shadow_level12(void) +{ + int16_t distance; + + if (Char.room == SHADOW_FIGHT_ROOM && pop_shadow_init == 0) { + if (Opp.x >= SHADOW_WAIT_KID_X) { + do_init_shad(init_shad_12, Char.room, SEQ_7_FALL); + return; + } + pop_shadow_init = 1; + } + if (Char.sword >= SWORD_2_DRAWN) { + if (offguard == 0 || guard_refrac == 0) autocontrol_guard_active(); + else move_4_down(); + return; + } + if (Opp.sword >= SWORD_2_DRAWN || offguard == 0) { + distance = 0x7000; + if (can_guard_see_kid < 2 || + (distance = pop_char_opp_dist()) >= SHADOW_SWORD_DIST) { + if (distance < 0) move_2_backward(); + return; + } + if (Char.frame == FRAME_15_STAND) move_down_forw(); + return; + } + if (pop_char_opp_dist() < SHADOW_UNITE_DIST) { + /* Слияние. Кид ПЕРЕЕЗЖАЕТ на место тени: оригинал переписывает + * charid активного персонажа (а это тень) в Кида и savekid'ит его + * целиком — с позицией, кадром и последовательностью тени. Отсюда + * и «дёрганый» шаг в оригинале: Кид оказывается там, где стояла + * тень. Слот соперника после этого гасится clear_char. */ + pop_flash_color = POP_FLASH_WHITE; + pop_flash_time = 18; + pop_add_life(); /* +1 к потолку HP */ + pop_united_shadow = SHADOW_UNITE_TIME; + Char.charid = CHARID_0_KID; + pop_savekid(); + clear_char(); + return; + } + if (can_guard_see_kid == 2) { + /* Кид бежит к тени — тень бежит навстречу (кадры бега 3..14 и + * шага 127..132). */ + uint8_t of = Opp.frame; + if ((of >= FRAME_3_START_RUN && of < FRAME_15_STAND) || + (of >= FRAME_127_STEPPING_7 && of < FRAME_133_AFTER_STEP)) + move_1_forward(); + } +} + +/* autocontrol_mouse (seg002:07EB). Весь «ИИ» мыши — два порога по X: + * добежала влево до POP_MOUSE_TURN_X → seq_107 (встать, поднять голову, + * развернуться и уйти), вернулась вправо до POP_MOUSE_LEAVE_X → исчезнуть. + * + * Почему проверка на исчезновение висит на action == 0 (стойка), хотя мышь + * при этом БЕЖИТ: seq_107 заканчивается прыжком на метку `Mscurry1`, которая + * стоит ПОСЛЕ опкода act(actions_1_run_jump) в seq_105 — то есть на обратном + * пути кадры бега проигрываются, а action остаётся стоечным. Именно этим + * оригинал и различает «бежит туда» и «бежит обратно». */ +static void autocontrol_mouse(void) +{ + if (Char.direction == DIR_56_NONE) return; + if (Char.action == 0) { + if (Char.x >= POP_MOUSE_LEAVE_X) clear_char(); + } else if (Char.x < POP_MOUSE_TURN_X) { + pop_char_set_seq(SEQ_107_MOUSE_STAND_UP_GO); + play_seq(); + } +} + +/* autocontrol_shadow (seg002:081D) — по уровням. */ +static void autocontrol_shadow(void) +{ + if (pop_current_level == MIRROR_LEVEL) autocontrol_shadow_level4(); + if (pop_current_level == SHADOW_STEAL_LEVEL) autocontrol_shadow_level5(); + if (pop_current_level == SHADOW_STEP_LEVEL) autocontrol_shadow_level6(); + if (pop_current_level == SHADOW_FIGHT_LEVEL) autocontrol_shadow_level12(); +} + +/* autocontrol_guard (seg002:701) + autocontrol_opponent (seg002:628). */ +static void autocontrol_opponent(void) +{ + move_0_nothing(); + /* В оригинале эти таймеры убывают только в ветке НЕ-Кида. Demo Kid + * использует тот же AI, но skill 10 и таймеры стража не расходует. */ + if (Char.charid != CHARID_0_KID) { + if (justblocked) --justblocked; + if (kid_sword_strike) --kid_sword_strike; + if (guard_refrac) --guard_refrac; + } + /* autocontrol_skeleton (seg002:685): у скелета меч вынут ВСЕГДА — он не + * бывает «неактивным», поэтому ветка inactive ему недоступна. */ + if (Char.charid == CHARID_4_SKELETON) Char.sword = SWORD_2_DRAWN; + /* МЫШЬ — своей веткой (seg002:0643), первой в цепочке charid'ов. */ + if (Char.charid == CHARID_24_MOUSE) { autocontrol_mouse(); return; } + /* ТЕНЬ идёт своей веткой ЦЕЛИКОМ (seg002:0647): к стражьему ИИ она не + * сводится — на уровне 4 не дерётся вовсе. */ + if (Char.charid == CHARID_1_SHADOW) { autocontrol_shadow(); return; } + if (Char.sword < SWORD_2_DRAWN) autocontrol_guard_inactive(); + else autocontrol_guard_active(); +} + +/* do_demo() (seg006:0D49): пока меч не вынут, управление даёт demo_moves; + * в бою Кид становится AI-персонажем skill 10, а страж затем получает + * skill 11. Команды AI уже actor-relative, поэтому pop_control вызывается + * напрямую — без user_control(), как и исправленная таблица demo_moves. */ +uint8_t pop_demo_kid_ai(void) __banked +{ + if (Kid.sword != SWORD_2_DRAWN) return 0; + + pop_loadkid_and_opp(); + pop_load_fram_det_col(); + guard_skill = 10; + autocontrol_opponent(); + guard_skill = 11; + pop_control(); + pop_savekid_state(); + return 1; +} + +/* ---- Урон мечом (seg002:0CD4..0E1F) -------------------------------- */ +#define ACTION_99_HURT 99 +#define ACTION_1_RUN 1 +#define FRAME_154_POKING 154 +#define SEQ_69_ATTACK_WAS_PARRIED 69 +#define SEQ_74_HIT_BY_SWORD 74 +#define SEQ_85_STABBED_TO_DEATH 85 + +/* refractimer (seg002:36) — «отдышка» стража после того, как его ранили. */ +static const uint8_t REFRACTIMER[NUM_GUARD_SKILLS] = + { 16, 16, 16, 16, 8, 8, 8, 8, 0, 8, 0, 0}; + +/* hurt_by_sword (seg002): применить попадание к АКТИВНОМУ персонажу. + * Без меча в руке любое попадание смертельно; с мечом — минус 1 HP и кадр + * «получил удар». + * УПРОЩЕНИЕ: ветку «сбит с уступа» (seq_81, когда сзади пусто и до кромки + * меньше 4) не портируем — ей нужны тайловые запросы ОТ Char, а pop_map + * пока умеет только от Kid. На ровном полу (тайл сзади не пустой) оригинал + * идёт ровно нашей веткой. */ +static void hurt_by_sword(void) +{ + if (Char.alive >= 0) return; + if (Char.sword != SWORD_2_DRAWN) { + /* Удар мимо боевой стойки в оригинале СМЕРТЕЛЕН (seg002: «Being + * hurt when not in fighting pose means death», take_hp(100)), и + * чит бессмертия сюда НЕ распространяется — он работает только в + * бою, то есть с мечом в руке. + * + * Раньше чит перехватывал hurt_by_sword целиком и безусловно ставил + * SEQ_74_HIT_BY_SWORD. Но это анимация «получил удар В БОЕВОЙ + * СТОЙКЕ»: её кадры (150..179) все с мечом, и Кид оказывался в них + * при sword == 0. Диспетчер control() при sword != 2 в + * control_with_sword не идёт, а среди обычных веток кадра 158/170/171 + * нет ни одной — управление зависало намертво: Кид стоял в боевой + * стойке без меча и не разворачивался к сопернику (BUG-CHEAT-IMM-1). + * + * Костыля «снять бессмертие на время вызова» здесь БОЛЬШЕ НЕТ: + * pop_take_hp гасит только урон меньше 100, а тут ровно 100. */ + pop_take_hp(100); + pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); + } else if (Char.charid == CHARID_0_KID && pop_immortal) { + /* ЧИТ, уровень 1 и выше: в боевой стойке удары не отнимают HP. + * Кадр «получил удар» оставляем — иначе бой перестаёт читаться, + * да и оригинал на выживший удар ставит ровно его. */ + pop_char_set_seq(SEQ_74_HIT_BY_SWORD); + } else if (Char.charid != CHARID_4_SKELETON && pop_take_hp(1)) { + pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); /* HP кончились */ + } else { + pop_char_set_seq(SEQ_74_HIT_BY_SWORD); + } + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; + Char.fall_y = 0; + /* seg002:0C1F: у Кида свой звук боли, у соперника свой. */ + pop_sfx_play((uint8_t)(Char.charid == CHARID_0_KID ? 13 : 12)); + play_seq(); +} + +/* check_hurting (seg002:0D56): достал ли АКТИВНЫЙ персонаж соперника. + * Бьют кадры 153/154; если соперник в этот момент парирует — удар отбит. */ +static void check_hurting(void) +{ + int16_t distance; + uint8_t cf, of, min_range; + + if (Char.sword != SWORD_2_DRAWN) return; + if (Char.curr_row != Opp.curr_row) return; + cf = Char.frame; + if (cf != FRAME_153_STRIKE_3 && cf != FRAME_154_POKING) return; + + distance = pop_char_opp_dist(); + of = Opp.frame; + if (distance < 0 || distance >= 29 || + (of != FRAME_161_PARRY && of != FRAME_150_PARRY)) { + /* Соперник НЕ парирует. */ + if (cf == FRAME_154_POKING) { + /* seg002:0DAE — свист клинка мимо цели. */ + pop_sfx_play(11); + min_range = (uint8_t)(Opp.sword < SWORD_2_DRAWN ? 8 : 12); + distance = pop_char_opp_dist(); + if (distance >= (int16_t)min_range && distance < 29) + Opp.action = ACTION_99_HURT; + } + } else { + /* Парировано. */ + Opp.frame = FRAME_161_PARRY; + if (Char.charid != CHARID_0_KID) justblocked = 4; + pop_char_set_seq(SEQ_69_ATTACK_WAS_PARRIED); + play_seq(); + } +} + +/* check_sword_hurting (seg002:0D1A): прогнать проверку с ОБЕИХ сторон. */ +void pop_check_sword_hurting(void) __banked +{ + uint8_t kf = Kid.frame; + if (kf == 0 || (kf >= 219 && kf < 229)) return; /* подъём по лестнице */ + pop_loadshad_and_opp(); + check_hurting(); + pop_saveshad_and_opp(); + pop_loadkid_and_opp(); + check_hurting(); + pop_savekid_and_opp(); +} + +/* check_sword_hurt (seg002:0CD4): применить помеченные попадания. Если + * ранен страж — Киду в этом кадре урон уже не проходит (в оригинале так же: + * его action сбрасывается ДО проверки). */ +void pop_check_sword_hurt(void) __banked +{ + if (Guard.action == ACTION_99_HURT) { + if (Kid.action == ACTION_99_HURT) Kid.action = ACTION_1_RUN; + pop_loadshad(); + hurt_by_sword(); + pop_saveshad(); + guard_refrac = (int8_t)REFRACTIMER[guard_skill]; + } else if (Kid.action == ACTION_99_HURT) { + pop_loadkid(); + hurt_by_sword(); + pop_savekid(); + } +} + +/* play_guard (seg006): ИИ + общий диспетчер. Отдельная ветка «HP кончились»: + * пока страж жив (alive < 0) и HP есть — обычный ход; как только HP дошли до + * нуля, он переводится в «умирает» (alive = 0), и дальше тот же control() + * ставит ему seq_71_dying. Уже умирающий сюда попадает с alive >= 0 и просто + * доигрывает последовательность. */ +static void play_guard(void) +{ + /* МЫШЬ (seg006:0D85, первая ветка): ей положен ТОЛЬКО автоконтроль. + * Диспетчер control() ей не нужен и вреден — он бы гонял её по + * стоечным веткам управления, а вся её «жизнь» уже описана + * последовательностями seq_105/107. */ + if (Char.charid == CHARID_24_MOUSE) { autocontrol_opponent(); return; } + if (Char.alive < 0 && guardhp_curr == 0) { + Char.alive = 0; + on_guard_killed(); /* уровень 13: вспышка + «выход открыт» */ + } + autocontrol_opponent(); + pop_control(); +} + +/* pop_guard_tick — порт play_guard_frame (seg000:1246) в объёме, который у + * нас уже есть: окно Char/Opp вокруг ИИ, диспетчера и интерпретатора + * последовательностей. Физика стража (fall_accel/коллизия/бамп/пики) — + * отдельным шагом. */ +void pop_guard_tick(void) __banked +{ + /* Гейт play_guard_frame (seg000:1248) — `if (Guard.direction != + * dir_56_none)`. Именно им ОТКЛЮЧАЕТСЯ персонаж: clear_char (seg006) + * не трогает charid, он ставит direction = dir_56_none, и вся логика + * соперника перестаёт вызываться. + * + * Проверять только charid НЕЛЬЗЯ (кусало 2026-08-11 на тени уровня 5): + * тень уходила за левый край, clear_char её «выключал», но тик + * продолжал крутиться — do_auto_moves за концом таблицы отдаёт + * последнюю запись `{0x31, 1}` («вперёд»), и тень разворачивалась, + * вбегала обратно, пробегала верхний ряд и падала в проём. */ + if (Guard.charid == 0 || Guard.direction == DIR_56_NONE) return; + pop_loadshad_and_opp(); /* Char = Guard, Opp = Kid */ + /* Симметрично Киду (play_guard_frame, seg000:1248): cur_frame один на + * всех, и до этой строки в нём лежит кадр КИДА — он тикал раньше. */ + pop_load_fram_det_col(); + play_guard(); + /* Анимация: та же seqtbl, что у Кида, но своя таблица кадров — + * выбирается по Char.charid внутри load_frame (seg006:0293). */ + play_seq(); + pop_saveshad(); +} diff --git a/applications/SprPoP/src/pop_app.c b/applications/SprPoP/src/pop_app.c new file mode 100644 index 0000000..3dcaf14 --- /dev/null +++ b/applications/SprPoP/src/pop_app.c @@ -0,0 +1,135 @@ +/* + * pop_app.c — таблица переходов оболочки, БАНК 9 (FG1). + * + * Модуль не рисует и не выделяет память. Это чистая политика переходов, + * поэтому её можно проверять host-тестом без DSS/MAME. Жизненный цикл + * ресурсов остаётся у enter/leave соответствующих состояний. + */ +#include "pop_app.h" + +pop_app_state_t pop_app_state; + +void pop_app_reset(void) __banked +{ + pop_app_state = POP_APP_BOOT; +} + +uint8_t pop_app_dispatch(pop_app_event_t event) __banked +{ + pop_app_state_t next = pop_app_state; + uint8_t accepted = 0; + + switch (pop_app_state) { + case POP_APP_BOOT: + if (event == POP_APP_EV_BOOT_READY) { + next = POP_APP_BUILD_INFO; accepted = 1; + } + break; + + case POP_APP_BUILD_INFO: + if (event == POP_APP_EV_BUILD_DONE) { + next = POP_APP_TITLE; accepted = 1; + } + break; + + case POP_APP_TITLE: + if (event == POP_APP_EV_INTRO_STARTED) { + next = POP_APP_INTRO; accepted = 1; + } else if (event == POP_APP_EV_DEMO_STARTED) { + next = POP_APP_DEMO; accepted = 1; + } else if (event == POP_APP_EV_NEW_GAME) { + next = POP_APP_LEVEL_LOAD; accepted = 1; + } + break; + + case POP_APP_INTRO: + if (event == POP_APP_EV_CUTSCENE_FINISHED) { + next = POP_APP_DEMO; accepted = 1; + } else if (event == POP_APP_EV_NEW_GAME) { + next = POP_APP_LEVEL_LOAD; accepted = 1; + } + break; + + case POP_APP_DEMO: + if (event == POP_APP_EV_DEMO_FINISHED) { + next = POP_APP_TITLE; accepted = 1; + } else if (event == POP_APP_EV_NEW_GAME) { + next = POP_APP_LEVEL_LOAD; accepted = 1; + } + break; + + case POP_APP_LEVEL_LOAD: + if (event == POP_APP_EV_LEVEL_READY) { + next = POP_APP_PLAYING; accepted = 1; + } + break; + + case POP_APP_PLAYING: + if (event == POP_APP_EV_PAUSE) { + next = POP_APP_PAUSE_MENU; accepted = 1; + } else if (event == POP_APP_EV_CUTSCENE_STARTED) { + next = POP_APP_CUTSCENE; accepted = 1; + } else if (event == POP_APP_EV_LEVEL_FINISHED) { + next = POP_APP_LEVEL_LOAD; accepted = 1; + } else if (event == POP_APP_EV_TIME_EXPIRED) { + next = POP_APP_TIME_EXPIRED; accepted = 1; + } else if (event == POP_APP_EV_ENDING_STARTED) { + next = POP_APP_ENDING; accepted = 1; + } else if (event == POP_APP_EV_RESTART_INTRO) { + /* start_game() прямо из геймплея: так оригинал уходит в title, + * когда игрок не нажал кнопку за 288 тиков после смерти + * (seg000:958). Раньше это событие принимал только pause menu. */ + next = POP_APP_TITLE; accepted = 1; + } + break; + + case POP_APP_PAUSE_MENU: + if (event == POP_APP_EV_RESUME) { + next = POP_APP_PLAYING; accepted = 1; + } else if (event == POP_APP_EV_RESTART_INTRO) { + /* Restart Game начинает заново именно title/intro. Сброс новой + * игры произойдёт позже общим LEVEL_LOAD после skip/attract. */ + next = POP_APP_TITLE; accepted = 1; + } + break; + + case POP_APP_CUTSCENE: + if (event == POP_APP_EV_CUTSCENE_FINISHED) { + next = POP_APP_LEVEL_LOAD; accepted = 1; + } + break; + + case POP_APP_TIME_EXPIRED: + if (event == POP_APP_EV_CUTSCENE_FINISHED) { + next = POP_APP_TITLE; accepted = 1; + } + break; + + case POP_APP_ENDING: + if (event == POP_APP_EV_ENDING_FINISHED) { + /* Таблица рекордов — ХВОСТ end_sequence, а не отдельный экран + * по желанию: она же дослушивает победную тему и гасит экран + * перед возвратом в начало (seg001:5F1). */ + next = POP_APP_HALL_OF_FAME; accepted = 1; + } + break; + + case POP_APP_HALL_OF_FAME: + if (event == POP_APP_EV_HOF_FINISHED) { + next = POP_APP_TITLE; accepted = 1; + } + break; + + case POP_APP_QUIT: + default: + break; + } + + if (event == POP_APP_EV_QUIT && pop_app_state != POP_APP_QUIT) { + next = POP_APP_QUIT; + accepted = 1; + } + if (!accepted) return 0; + pop_app_state = next; + return 1; +} diff --git a/applications/SprPoP/src/pop_app.h b/applications/SprPoP/src/pop_app.h new file mode 100644 index 0000000..87c3dbb --- /dev/null +++ b/applications/SprPoP/src/pop_app.h @@ -0,0 +1,63 @@ +/* + * pop_app.h — автомат верхнего уровня Prince of Persia (FG1). + * + * Игровой цикл — лишь состояние PLAYING, а не «вся программа». Каждый + * режим возвращает результат через pop_app_dispatch(), поэтому будущие + * TITLE/demo/cutscene не будут вызывать друг друга рекурсивно и копить + * стек или EMM-ресурсы. + */ +#ifndef POP_APP_H +#define POP_APP_H + +#include + +typedef enum { + POP_APP_BOOT = 0, + POP_APP_BUILD_INFO, + POP_APP_TITLE, + POP_APP_INTRO, + POP_APP_DEMO, + POP_APP_LEVEL_LOAD, + POP_APP_PLAYING, + POP_APP_PAUSE_MENU, + POP_APP_CUTSCENE, + POP_APP_TIME_EXPIRED, + POP_APP_ENDING, + POP_APP_HALL_OF_FAME, + POP_APP_QUIT +} pop_app_state_t; + +/* События не содержат деталей конкретного режима: режим завершился, а + * переход выбирает центральная таблица. Это намеренно делает недопустимый + * переход явным no-op вместо случайного прыжка в код другого экрана. */ +typedef enum { + POP_APP_EV_BOOT_READY = 0, + POP_APP_EV_BUILD_DONE, + POP_APP_EV_INTRO_STARTED, + POP_APP_EV_DEMO_STARTED, + POP_APP_EV_CUTSCENE_STARTED, + POP_APP_EV_NEW_GAME, + POP_APP_EV_PAUSE, + POP_APP_EV_RESUME, + POP_APP_EV_LEVEL_READY, + POP_APP_EV_LEVEL_FINISHED, + POP_APP_EV_DEMO_FINISHED, + POP_APP_EV_CUTSCENE_FINISHED, + POP_APP_EV_TIME_EXPIRED, + POP_APP_EV_ENDING_STARTED, + POP_APP_EV_ENDING_FINISHED, + POP_APP_EV_HOF_FINISHED, + POP_APP_EV_RESTART_INTRO, + POP_APP_EV_QUIT +} pop_app_event_t; + +extern pop_app_state_t pop_app_state; + +/* Начать новый маршрут с BOOT. */ +void pop_app_reset(void) __banked; + +/* Выполнить допустимый переход. Возвращает 1 при успехе, 0 при ошибке + * протокола; в ошибочном случае состояние не меняется. */ +uint8_t pop_app_dispatch(pop_app_event_t event) __banked; + +#endif /* POP_APP_H */ diff --git a/applications/SprPoP/src/pop_arc.c b/applications/SprPoP/src/pop_arc.c new file mode 100644 index 0000000..12ad845 --- /dev/null +++ b/applications/SprPoP/src/pop_arc.c @@ -0,0 +1,112 @@ +/* + * pop_arc.c — чтение архива ресурсов PBA1, БАНК 8. + * + * Контракт и мотивация — в pop_arc.h. Здесь всё сводится к трём вызовам + * libc: open один раз на группу, lseek к элементу и bank_read_page прямо в + * EMM-страницу (тот резидентен и потому безопасен из банка huge — он же + * переключает W3, где живёт наш код). + */ +#include +#include +#include +#include +#include +#include "pop_arc.h" + +/* Пути 8.3, как требует DSS. Держим их ЗДЕСЬ: строка обязана лежать в том + * же банке, что и код, который её читает (см. pop_arc.h). */ +static const char *const arc_path[POP_ARC_IDS] = { + "BG\\bg.arc", + "KID\\kid.arc", + "GUARD\\guard.arc", + "SKEL\\skel.arc", + "VIZIER\\vizier.arc", + "SHADOW\\shadow.arc", + "TITLE\\title.arc", + "PV\\pv.arc", + "SND\\snd.arc" +}; + +int8_t pop_arc_open_id(pop_arc_t *a, uint8_t id, + uint8_t *tbl, uint8_t tbl_max) __banked +{ + if (id >= POP_ARC_IDS) return -1; + return pop_arc_open(a, arc_path[id], tbl, tbl_max); +} + +int8_t pop_arc_open(pop_arc_t *a, const char *path, + uint8_t *tbl, uint8_t tbl_max) __banked +{ + uint8_t head[8]; + int fd; + + a->fd = -1; + a->count = 0; + fd = open(path, O_RDONLY); + if (fd < 0) return -1; + if (read(fd, head, sizeof(head)) != (int)sizeof(head) || + memcmp(head, "PBA1", 4) != 0 || + head[4] == 0 || head[4] > tbl_max || head[4] > POP_ARC_MAX) { + close(fd); + return -1; + } + if (read(fd, tbl, (uint16_t)head[4] * POP_ARC_REC) != + (int)((uint16_t)head[4] * POP_ARC_REC)) { + close(fd); + return -1; + } + a->fd = fd; + a->count = head[4]; + return (int8_t)head[4]; +} + +uint16_t pop_arc_size(const uint8_t *tbl, uint8_t idx) +{ + const uint8_t *rec = tbl + (uint16_t)idx * POP_ARC_REC; + return (uint16_t)(rec[2] | ((uint16_t)rec[3] << 8)); +} + +int pop_arc_read(pop_arc_t *a, const uint8_t *tbl, uint8_t idx, uint16_t skip, + uint8_t page, uint16_t off, uint16_t size) __banked +{ + const uint8_t *rec; + uint16_t sec, len; + long pos; + + if (a->fd < 0 || idx >= a->count) return -1; + rec = tbl + (uint16_t)idx * POP_ARC_REC; + sec = (uint16_t)(rec[0] | ((uint16_t)rec[1] << 8)); + len = (uint16_t)(rec[2] | ((uint16_t)rec[3] << 8)); + if (skip >= len) return 0; + len = (uint16_t)(len - skip); + if (size && size < len) len = size; + + /* Сектора в байты — единственное место, где нужна 32-битная величина: + * архив может быть больше 64 КБ, и lseek принимает long. Путь холодный + * (загрузка ресурсов), в кадре этого нет. */ + pos = ((long)sec << 9) + (long)skip; + if (lseek(a->fd, pos, SEEK_SET) < 0) return -1; + return bank_read_page(a->fd, page, off, len); +} + +int8_t pop_arc_atlas(pop_arc_t *a, const uint8_t *tbl, uint8_t idx, + atlas_t *at) __banked +{ + uint8_t blk, page; + int n; + + blk = mem_alloc_pages(1); + if (!blk) return -1; + page = mem_get_page(blk, 0); + n = pop_arc_read(a, tbl, idx, 0, page, 0, 0); + /* Дальше всё общее с atlas_load: магия 'SPA1', разбор заголовка и + * gfx_w0_page_prepare. При ошибке atlas_attach сам отпускает блок. */ + return (int8_t)atlas_attach(at, blk, page, n); +} + +void pop_arc_close(pop_arc_t *a) __banked +{ + if (a->fd >= 0) close(a->fd); + a->fd = -1; + a->count = 0; +} diff --git a/applications/SprPoP/src/pop_arc.h b/applications/SprPoP/src/pop_arc.h new file mode 100644 index 0000000..20cd249 --- /dev/null +++ b/applications/SprPoP/src/pop_arc.h @@ -0,0 +1,90 @@ +/* + * pop_arc.h — архив ресурсов PBA1: много файлов в одном, один open на всех. + * + * ЗАЧЕМ. Замер в MAME (2026-08-25): `open` стоит 51,4 мс, а чтение 16 КБ из + * уже открытого файла — 32,6 мс. То есть 61 % времени загрузки уходит на + * открытие, а не на данные, и одна только стартовая пачка ресурсов (~142 + * файла) — это 7,3 секунды чистого `open`. Архив открывается ОДИН раз на + * группу, и эта цена платится один раз вместо сотни. + * + * ФОРМАТ (генерирует toolchain/pop_pack_arc.py): + * 0..3 'PBA1' магия и версия + * 4 count число записей, 1..126 + * 8.. записи по 4 байта: offset в СЕКТОРАХ по 512 (uint16), + * size в байтах (uint16) + * данные с 512, каждый элемент выровнен на 512. + * Оффсет в секторах, потому что архив может быть больше 64 КБ (pv — мегабайт); + * размер в байтах, потому что ни один ресурс не крупнее 16 КБ — он обязан + * помещаться в EMM-страницу. + * + * ТАБЛИЦУ ДЕРЖИТ ВЫЗЫВАЮЩИЙ, на своём стеке: 4 байта на запись, у самой + * большой группы 320 байт. В W2 её класть нельзя — там осталось около сотни + * байт, — а стека в точке загрузки ресурсов израсходовано 75 байт из 1279 + * (замер там же). Так модуль не занимает постоянной памяти вовсе. + */ +#ifndef POP_ARC_H +#define POP_ARC_H + +#include +#include /* atlas_t */ + +#define POP_ARC_HEADER 512 +#define POP_ARC_SECTOR 512 +#define POP_ARC_MAX 126 /* (512-8)/4 */ +#define POP_ARC_REC 4 /* байт на запись таблицы */ + +/* Имя у структуры есть намеренно: так на неё можно сослаться из заголовков, + * которым незачем тянуть весь pop_arc.h (pop_ui.h). */ +typedef struct pop_arc_s { + int fd; + uint8_t count; +} pop_arc_t; + +/* ИМЕНА АРХИВОВ ЗНАЕТ САМ МОДУЛЬ, и это не вкусовщина: строковый литерал + * лежит в rodata СВОЕГО банка, а pop_arc.c живёт в банке 8. Указатель на + * литерал из другого банка после переключения W3 показывает на чужие + * данные — ровно так «BG\bg.arc» превращался в мусор и загрузка фона + * падала (поймано 2026-08-26). Поэтому наружу отдаём НОМЕР архива. */ +typedef enum { + POP_ARC_BG = 0, + POP_ARC_KID, + POP_ARC_GUARD, + POP_ARC_SKEL, + POP_ARC_VIZIER, + POP_ARC_SHADOW, + POP_ARC_TITLE, + POP_ARC_PV, + POP_ARC_SND, + POP_ARC_IDS +} pop_arc_id_t; + +/* Открыть архив по номеру: то же, что pop_arc_open, но имя берётся из + * таблицы внутри модуля. */ +int8_t pop_arc_open_id(pop_arc_t *a, uint8_t id, + uint8_t *tbl, uint8_t tbl_max) __banked; + +/* Открыть архив и вычитать таблицу в буфер вызывающего (count*4 байт). + * tbl_max — сколько записей помещается в буфер. + * Возврат: число записей; -1 — не открылся, не PBA1 либо таблица не влезла. */ +int8_t pop_arc_open(pop_arc_t *a, const char *path, + uint8_t *tbl, uint8_t tbl_max) __banked; + +/* Размер элемента idx по таблице (0, если индекс за пределами). */ +uint16_t pop_arc_size(const uint8_t *tbl, uint8_t idx); + +/* Прочитать кусок элемента idx в EMM-страницу: skip байт от его начала, + * size байт, в страницу page со смещением off. size == 0 — весь элемент. + * Возврат: сколько прочитано, -1 — ошибка. */ +int pop_arc_read(pop_arc_t *a, const uint8_t *tbl, uint8_t idx, uint16_t skip, + uint8_t page, uint16_t off, uint16_t size) __banked; + +void pop_arc_close(pop_arc_t *a) __banked; + +/* Атлас ПРЯМО ИЗ АРХИВА: выделить страницу, вычитать в неё элемент idx и + * отдать libbgi (atlas_attach — проверка магии + подготовка страницы к + * маппингу в W0). Полный эквивалент atlas_load, только источник другой. + * 0 — OK, -1 — ошибка (страница освобождена). */ +int8_t pop_arc_atlas(pop_arc_t *a, const uint8_t *tbl, uint8_t idx, + atlas_t *at) __banked; + +#endif /* POP_ARC_H */ diff --git a/applications/SprPoP/src/pop_bg.c b/applications/SprPoP/src/pop_bg.c new file mode 100644 index 0000000..0632cb1 --- /dev/null +++ b/applications/SprPoP/src/pop_bg.c @@ -0,0 +1,1176 @@ +/* + * pop_bg.c — ГОРЯЧАЯ половина слоя фона PoP: то, что работает КАЖДЫЙ кадр — + * передний слой поверх персонажей (порт seg003 redraw_at_char + seg008 + * set_char_collision), оверлеи кромки, кладка стены и клип. Полная + * отрисовка комнаты и точечные перерисовки уехали в pop_room.c (банк 7); + * граница реза и её цена расписаны в _pop_bg.h. + * + * Подробности алгоритма кладки — в toolchain/render_room.py и + * memory/pop_background_strategy. Только DUNGEON-стиль (Фаза 1). + */ +#include +#include +#include +#include +#include +#include "pop_bg.h" +#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */ +#include "_pop_bg.h" /* контракт с холодной половиной (pop_room.c, банк 7) */ +#include "pop_tile.h" +#include "pop_map.h" /* pop_upside — переворот (зелье инверсии) */ /* общие листья слоя фона (резидент): блит, тайлы, таблица */ +#include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */ +#include "pop_state.h" +#include "pop_geom.h" +#include "pop_level.h" /* pop_doorlink2 — состояние нажатия кнопки */ +#include "pop_kid.h" /* Kid.curr_col/curr_row — порядок оверлеев (см. ниже) */ +#include "pop_cdraw.h" /* pop_cd — габарит кадра Кида (y-порядок плиты) */ +#include "pop_guard.h" /* SWORD_2_DRAWN — расширение футпринта под клинок */ + + +/* Область, куда в этом кадре рисовался ОВЕРЛЕЙ (не футпринт fore, а + * draw_other_overlay/floor_overlay/полоса потолка). Оверлей включается + * только в позах виса/полёта/подъёма и рисует НЕ по прямоугольнику Kid, + * поэтому heal персонажа его следы не убирает: на второй странице дабл-буфера + * остаётся кадр «с оверлеем», и он мерцает через кадр. Копим bbox и + * восстанавливаем фон на ОБЕИХ страницах (pop_fore_heal). */ +static int ov_x0, ov_y0, ov_x1, ov_y1; +static uint8_t ov_pending; /* биты страниц, где надо восстановить фон */ +/* Подавить ov_mark на время серии — для вызывающего, который САМ чистит эту + * область каждый кадр. Так работает падающий кусок: его heal-коридор + * прочёсывается всегда, а оверлей соседа и кромка потолка рисуются ВНУТРИ + * коридора (окно клипа = прямоугольник куска). Без этого объединение ov_mark + * по шести кускам растягивалось на полкомнаты, и pop_fore_heal стоил 187 500 + * такта в фазе «ввод+heal» против 55 000 (замер 2026-08-17). */ +static uint8_t ov_suppress; + +static void ov_mark(int x, int y, int w, int h) +{ + if (ov_suppress) return; + if (!ov_pending) { ov_x0 = x; ov_y0 = y; ov_x1 = x + w; ov_y1 = y + h; } + else { + if (x < ov_x0) ov_x0 = x; + if (y < ov_y0) ov_y0 = y; + if (x + w > ov_x1) ov_x1 = x + w; + if (y + h > ov_y1) ov_y1 = y + h; + } + ov_pending = 3; /* обе страницы */ +} + +void pop_fore_heal(void) __banked +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + uint8_t pg = gfx_get_draw_page() & 1; + if (!(ov_pending & (1 << pg))) return; + pop_heal_off(ov_x0, ov_y0, ov_x1 - ov_x0, ov_y1 - ov_y0); + ov_pending = (uint8_t)(ov_pending & ~(1 << pg)); + /* Восстановленный прямоугольник шире персонажа и запросто накрывает + * СОСЕДА — пометить область (DRAW-COST): heal идёт мимо pop_blit_b. */ + pop_cd_touch(ov_x0, ov_y0 + POP_YOFF, ov_x1 - ov_x0, ov_y1 - ov_y0); +} + + +/* ---- Константные таблицы (render_room.py) -------------------------- */ +static const uint8_t TBL_LINE[3] = {0, 10, 20}; +static const uint8_t DOOR_FRAM_SLICE[9] = {67, 59, 58, 57, 56, 55, 54, 53, 52}; +static const uint8_t SPIKES_FRAM_FORE[10] = {0, 139, 140, 141, 142, 143, 142, 140, 139, 0}; +static const uint8_t LPOS[5] = {58, 41, 37, 20, 16}; +static const uint8_t RPOS[4] = {52, 42, 31, 21}; + +/* Wall (chtab_7) id: делители/блок/марки. */ +#define RES_WALL_RNDBLOCK 13 +#define RES_WALL_DIVIDER1 11 +#define RES_WALL_MARK_TL 14 +#define RES_WALL_MARK_BL 15 +#define RES_WALL_MARK_TR 16 +#define RES_WALL_MARK_BR 17 + +/* ---- PRNG (seg009.c:321, 32-бит LCG) — только на ВХОД в комнату ---- * + * Сид (pop_t_seed) живёт в резиденте: его ставит полная отрисовка комнаты, а + * читает кэш раскладки кладки. */ +#define prandom(maxv) pop_prandom(&pop_t_seed, (maxv)) + +/* Отсев кусков кладки по окну fore-клипа — тела ниже, у wpp_fill. */ +static uint8_t wp_vis(int x, int w, int ytop, int ybot); +static void wp_blit(uint8_t id, int x, int ybot, uint8_t w, uint8_t h); + +/* ---- Декали-марки стен (seg008.c:2041/2056) ------------------------ */ +static void draw_left_mark(uint16_t dv, int arg2, int arg1, int row, int col) +{ + uint8_t image_id = (dv & 1) ? RES_WALL_MARK_BL : RES_WALL_MARK_TL; + int lv2 = 0; + int xh = POP_COL_XH[col]; + int dby = 63 * row + 65; + if (dv > 3) lv2 = arg1 + 6; + else if (dv > 1) lv2 = arg2 + 6; + wp_blit(image_id, (xh + ((dv == 2 || dv == 3) ? 1 : 0)) * 8 + lv2, + dby - LPOS[dv], 7, 10); /* pop_wall.atl: 16/17 = 7x10 */ +} + +static void draw_right_mark(uint16_t dv, int arg1, int row, int col) +{ + uint8_t image_id = (dv & 1) ? RES_WALL_MARK_BR : RES_WALL_MARK_TR; + int a = (dv < 2) ? 24 : arg1 - 3; + int xh = POP_COL_XH[col]; + int dby = 63 * row + 65; + wp_blit(image_id, (xh + ((dv > 1) ? 1 : 0)) * 8 + a, + dby - RPOS[dv], 14, 5); /* pop_wall.atl: 14/15 = 14x5 */ +} + +/* ---- wall_pattern (seg008.c:1928, dungeon-ветка) ------------------- */ +/* КЭШ РАСКЛАДКИ КЛАДКИ. Серия prandom для тайла зависит только от + * (комната, ряд, колонка) — пока мы в комнате, это КОНСТАНТА. А + * wall_pattern зовётся из fore-слоя КАЖДЫЙ кадр, и на Z80 это самая + * дорогая вещь в кадре: замер в MAME 2026-07-29 (комната 3) — 18 вызовов + * pop_prandom за один fore-проход, ~10 700 тактов каждый (32-битный LCG: + * __mullong ~8 000 + __moduint), то есть 193 К из 341 К всего прохода при + * бюджете кадра ~430 К. Кэшируем не сид, а ГОТОВЫЕ РЕШЕНИЯ (что рисовать + * и с каким смещением) — тогда в кадре не остаётся ни одного prandom. + * + * Порядок вызовов prandom воспроизведён один в один, включая то, что + * значение метки берётся ТОЛЬКО когда сработало её условие: иначе + * последовательность разъедется и кладка станет другой. which_part=0 + * использует префикс той же серии, поэтому кэш общий. + * + * Упаковка: wp_a = mid_div | mid_off<<1 | bot_div<<4 | bot_off<<5; + * wp_b = rndblk | right<<1 | right_val<<2 | left<<4 | left_val<<5. + * Индекс — (row+1)*10+col, ряды −1..2. */ +#define WP_N 40 +static uint8_t wp_a[WP_N], wp_b[WP_N], wp_ok[WP_N]; + +/* ---- Цвета дворцовой кладки (gen_palace_wall_colors, seg000:1942) ---- * + * Тело стены во дворце — не кирпичи-спрайты, а СПЛОШНЫЕ ЗАЛИВКИ; их цвета + * разыгрываются на КОМНАТУ: сид = номер комнаты, 3 ряда × 4 подряда × 11 + * колонок. Подряды 1 и 3 берут 0x61..0x64, подряды 0 и 2 — 0x66..0x69 + * (это слоты wall-палитры, во дворце они песочные); соседние по горизонтали + * не повторяются. Одиннадцать колонок, а не десять: заливки 3 и 5 берут + * цвет СЛЕДУЮЩЕЙ колонки. + * + * Не static: таблицу читает и холодная половина слоя (pop_room.c, банк 7). + * Writable-данные банка живут в _DATA/W2 и видны отовсюду — в отличие от + * const-таблиц, которые из чужого банка не прочитать. */ +uint8_t pop_pal_wall_col[3 * 44]; + +static void gen_palace_wall_colors(uint8_t room) +{ + pop_rnd_t saved = pop_t_seed; + uint8_t row, sub, col; + uint8_t *p = pop_pal_wall_col; + pop_prandom_set(pop_t_seed, room); + prandom(1); /* сброс, как в оригинале */ + for (row = 0; row < 3; row++) + for (sub = 0; sub < 4; sub++) { + uint8_t base = (uint8_t)((sub & 1) ? 0x61 : 0x66); + uint8_t prev = 0; /* 0 — «цвета ещё не было» */ + for (col = 0; col < 11; col++) { + uint8_t c; + do { c = (uint8_t)(base + prandom(3)); } while (c == prev); + *p++ = c; + prev = c; + } + } + pop_t_seed = saved; +} + +static void wall_pattern_reset(void) /* смена комнаты — сид другой */ +{ + uint8_t i; + for (i = 0; i < WP_N; i++) wp_ok[i] = 0; + if (pop_palace) gen_palace_wall_colors(pop_t_room); +} + +/* Выходы wall_rnd — в файловых статиках: возвращать девять значений + * через указатели на Z80 дороже самой функции. */ +static uint8_t wr_mid_div, wr_mid_off, wr_bot_div, wr_bot_off; +static uint8_t wr_rndblk, wr_right, wr_rightv, wr_left, wr_leftv; + +/* Правила серии prandom по модификатору стены (bg): что вообще есть у этого + * вида кладки и с какими порогами. Столбцы: + * [0] есть «случайный блок» (тогда prandom(4)==0) + * [1] порог условия метки СПРАВА (0 = метки справа нет, prandom не зовётся) + * [2] порог значения метки справа, [3] прибавка к нему + * [4] порог условия метки СЛЕВА (есть у всех четырёх), [5] порог значения + * Порядок строк — по bg: 0 SWS, 1 SWW, 2 WWS, 3 WWW (seg008:1928). */ +static const uint8_t WR_RULE[4][6] = { + {0, 0, 0, 0, 6, 1}, /* SWS: только метка слева */ + {1, 4, 3, 0, 4, 3}, /* SWW */ + {0, 4, 1, 2, 4, 4}, /* WWS: значение справа со сдвигом 2 */ + {1, 4, 3, 0, 4, 4}, /* WWW: полный набор */ +}; + +static void wall_rnd(int row, int col) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* Кэшируем только штатную сетку: колонка вне 0..9 дала бы индекс + * соседнего РЯДА и чужую раскладку (тихий баг вместо промаха). */ + int idx = (col >= 0 && col < 10) ? (row + 1) * 10 + col : -1; + uint8_t a, b, bg; + pop_rnd_t saved; + if (idx >= 0 && idx < WP_N && wp_ok[idx]) { + a = wp_a[idx]; b = wp_b[idx]; + wr_mid_div = (uint8_t)(a & 1); wr_mid_off = (uint8_t)((a >> 1) & 7); + wr_bot_div = (uint8_t)((a >> 4) & 1); wr_bot_off = (uint8_t)((a >> 5) & 7); + wr_rndblk = (uint8_t)(b & 1); + wr_right = (uint8_t)((b >> 1) & 1); wr_rightv = (uint8_t)((b >> 2) & 3); + wr_left = (uint8_t)((b >> 4) & 1); wr_leftv = (uint8_t)((b >> 5) & 7); + return; + } + saved = pop_t_seed; + /* Верхняя полоса (row<0): SDLPoP рисует её как ряд 2 комнаты сверху + * (drawn_row=2 для seed, draw_main_y=-1 для позиции) — seed по row 2. */ + pop_prandom_set(pop_t_seed, pop_t_room + TBL_LINE[row < 0 ? 2 : row] + col); + prandom(1); /* сброс */ + wr_mid_div = (uint8_t)prandom(1); + wr_mid_off = (uint8_t)prandom(4); + wr_bot_div = (uint8_t)prandom(1); + wr_bot_off = (uint8_t)prandom(4); + wr_rndblk = wr_right = wr_left = 0; + wr_rightv = wr_leftv = 0; + bg = pop_wall_modifier(row, col); + { /* Четыре ветки bg отличались ТОЛЬКО числами в одной и той же серии + * prandom, поэтому они сведены к правилу (WR_RULE). ПОРЯДОК вызовов + * prandom обязан совпадать с оригиналом дословно, включая то, что + * значение метки берётся ТОЛЬКО когда сработало её условие: иначе + * последовательность разъедется и кладка станет другой. «Нет метки + * справа» = нулевой порог условия — тогда prandom не зовётся вовсе, + * ровно как в ветке SWS. */ + const uint8_t *r = WR_RULE[bg]; + if (r[0]) wr_rndblk = (uint8_t)(prandom(4) == 0); + if (r[1] && prandom(r[1]) == 0) { + wr_right = 1; wr_rightv = (uint8_t)(prandom(r[2]) + r[3]); + } + if (prandom(r[4]) == 0) { wr_left = 1; wr_leftv = (uint8_t)prandom(r[5]); } + } + pop_t_seed = saved; + if (idx >= 0 && idx < WP_N) { + wp_a[idx] = (uint8_t)(wr_mid_div | (wr_mid_off << 1) | + (wr_bot_div << 4) | (wr_bot_off << 5)); + wp_b[idx] = (uint8_t)(wr_rndblk | (wr_right << 1) | (wr_rightv << 2) | + (wr_left << 4) | (wr_leftv << 5)); + wp_ok[idx] = 1; + } +} + +/* ---- wall_pattern (seg008.c:1928, dungeon-ветка) ------------------- * + * Куски кладки одни и те же для всех четырёх видов стены, различается ровно + * НАБОР присутствующих (WP_PARTS) — раньше это были четыре ветки с + * повторяющимся телом. Порядок кусков внутри вызова сохранён: случайный + * блок, средний делитель, нижний делитель, метка справа, метка слева. + * + * which_part == 0 — только нижний делитель (слой draw_tile_bottom), 1 — всё + * остальное (draw_tile_fore). У SWW нижнего делителя нет вовсе, у SWS — + * ничего, кроме метки слева. */ +#define WP_RND 1 +#define WP_MID 2 +#define WP_BOT 4 +#define WP_RIGHT 8 +#define WP_LEFT 16 + +static const uint8_t WP_PARTS[4] = { + WP_LEFT, /* 0 SWS */ + WP_RND | WP_MID | WP_RIGHT | WP_LEFT, /* 1 SWW */ + WP_MID | WP_BOT | WP_RIGHT | WP_LEFT, /* 2 WWS */ + WP_RND | WP_MID | WP_BOT | WP_RIGHT | WP_LEFT, /* 3 WWW */ +}; + +/* ---- Кладка ДВОРЦА (seg008:1946) ------------------------------------ * + * Тело стены — не кирпичи-спрайты, а шесть СПЛОШНЫХ ЗАЛИВОК цветами из + * pop_pal_wall_col, поверх — пять разделителей chtab_7 (mono, цвет 6). + * Геометрия дословно из add_wipetable(layer, left, bottom, height, width): + * прямоугольник = x..x+width-1, (bottom-height+1)..bottom. + * + * Порядок prandom важен так же, как в подземелье: при which_part == 0 + * разыгрывается ОДНО значение, и нижний разделитель берёт ПЕРВОЕ из серии, + * а не пятое. */ +static uint8_t wpp_d[5]; /* пять prandom(2), 0..2 */ + +static void wall_rnd_palace(int row, int col) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + int idx = (col >= 0 && col < 10) ? (row + 1) * 10 + col : -1; + pop_rnd_t saved; + uint8_t i; + if (idx >= 0 && idx < WP_N && wp_ok[idx]) { + uint8_t a = wp_a[idx]; + wpp_d[0] = (uint8_t)(a & 3); wpp_d[1] = (uint8_t)((a >> 2) & 3); + wpp_d[2] = (uint8_t)((a >> 4) & 3); wpp_d[3] = (uint8_t)((a >> 6) & 3); + wpp_d[4] = wp_b[idx]; + return; + } + saved = pop_t_seed; + pop_prandom_set(pop_t_seed, pop_t_room + TBL_LINE[row < 0 ? 2 : row] + col); + prandom(1); /* сброс */ + for (i = 0; i < 5; i++) wpp_d[i] = (uint8_t)prandom(2); + pop_t_seed = saved; + if (idx >= 0 && idx < WP_N) { + wp_a[idx] = (uint8_t)(wpp_d[0] | (wpp_d[1] << 2) | + (wpp_d[2] << 4) | (wpp_d[3] << 6)); + wp_b[idx] = wpp_d[4]; + wp_ok[idx] = 1; + } +} + +/* Прямоугольник [x, x+w) x [ytop, ybot] задевает окно fore-клипа? Клипа + * нет — задевает всегда. Координаты сюда приходят посчитанными ИЗ РЯДА + * (63*row + …), то есть логические — значит и рамка нужна логическая + * (pop_tile.h, пара fclip_l*): переворот применяется позже, при блите. */ +static uint8_t wp_vis(int x, int w, int ytop, int ybot) +{ + if (!pop_t_fclip_on) return 1; + return (uint8_t)(x < pop_t_fclip_x1 && x + w > pop_t_fclip_x0 && + ytop < pop_t_fclip_ly1 && ybot >= pop_t_fclip_ly0); +} + +/* Декаль кладки: ybot — БЕЗ POP_YOFF (как ждёт pop_wall_b), w/h — реальный + * габарит куска. Размеры взяты из каталогов атласов (pal_wall.atl / + * pop_wall.atl) и заданы верхней границей по группе, поэтому промахнуться в + * меньшую сторону нельзя: хуже отсева не будет, лишнего не сотрём. + * + * Зачем: pop_blit_b тоже отсеивает до маппинга страницы, но по ЗАВЕДОМО + * большему габариту 64x64 — куски кладки высотой 3..12 px он пропускает и + * платит полный atlas_image + gfx_w0_map + чтение w/h (~9 760 тактов на + * блит), чтобы там обнаружить, что рисовать нечего. Пять таких блитов на + * тайл — 48 500 тактов в никуда (docs/perf_backlog.md, пункт 0 шаг 2). */ +static void wp_blit(uint8_t id, int x, int ybot, uint8_t w, uint8_t h) +{ + int yb = ybot + POP_YOFF; + if (!wp_vis(x, (int)w, yb - (int)h + 1, yb)) return; + pop_wall_b(id, x, ybot); +} + +/* Заливка куска стены. x/ytop — уже ЭКРАННЫЕ (с POP_YOFF). В fore-проходе + * режем по окну клипа: без этого заливка легла бы поверх областей, которые + * в этом кадре никто не восстанавливает. */ +static void wpp_fill(int x, int ytop, int w, int h, uint8_t colour) +{ + int x2 = x + w - 1, y2 = ytop + h - 1; + if (pop_t_fclip_on) { + if (x < pop_t_fclip_x0) x = pop_t_fclip_x0; + if (ytop < pop_t_fclip_ly0) ytop = pop_t_fclip_ly0; + if (x2 >= pop_t_fclip_x1) x2 = pop_t_fclip_x1 - 1; + if (y2 >= pop_t_fclip_ly1) y2 = pop_t_fclip_ly1 - 1; /* клип ДО переворота */ + } + if (pop_t_clip_top && ytop < pop_t_clip_top) ytop = pop_t_clip_top; + if (x < 0) x = 0; + if (ytop < 0) ytop = 0; + if (x2 > 319) x2 = 319; + if (y2 > 255) y2 = 255; + if (x2 < x || y2 < ytop) return; + /* ПЕРЕВОРОТ: заливка — такой же кусок фона, как блит тайла, и обязана + * отражаться той же формулой (иначе полоса ложится в неотражённое + * место — так ломались решётка и чомперы). */ + if (pop_upside) { + int h = y2 - ytop + 1; + ytop = POP_FLIP_TOP(ytop, h); + y2 = ytop + h - 1; + } + setfillstyle(SOLID_FILL, colour); + bar(x, ytop, x2, y2); + if (!pop_t_fclip_on) pop_cd_touch(x, ytop, x2 - x + 1, y2 - ytop + 1); +} + +static void wall_pattern_palace(int row, int col, int which_part) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + int xh = POP_COL_XH[col]; + int dbys = 63 * row + 65; /* база БЕЗ POP_YOFF — для блитов */ + int dmys = dbys - 3; + int dby = dbys + POP_YOFF, dmy = dmys + POP_YOFF; /* экранные — для заливок */ + /* РАННИЙ ВЫХОД по окну fore-клипа. Весь узор лежит в прямоугольнике + * x [xh*8, xh*8+32), y [dmy-59, dby]: сверху — первая заливка, снизу — + * нижний декаль, чей низ ровно на dby. + * + * Без этой проверки тайл стены под ногами Кида стоил ~60 000 тактов НА + * НОЛЬ ПИКСЕЛЕЙ: предфильтр fore_tile грубый (габарит 40x78), wpp_fill + * честно режет по окну, но платит ~3 100 тактов за арифметику клипа, а + * pop_wall_b об окне не знает вовсе и идёт в atlas_image + gfx_w0_map, + * чтобы там обнаружить, что рисовать нечего. Два таких тайла (ряд 2 под + * Кидом попадает в fore-окно всегда) = 139 863 такта за кадр, 32 % + * растрового кадра. См. docs/perf_backlog.md, пункт 0. */ + if (pop_t_fclip_on) { + int x0 = xh * 8; + if (x0 >= pop_t_fclip_x1 || x0 + 32 <= pop_t_fclip_x0 || + dmy - 59 >= pop_t_fclip_ly1 || dby + 1 <= pop_t_fclip_ly0) + return; + } + /* Цвета лежат по рядам 0..2; полоса у потолка (row −1) — это ряд 2 + * комнаты сверху, оригинал и сид для неё берёт по ряду 2. */ + const uint8_t *cl = pop_pal_wall_col + 44 * (row < 0 ? 2 : row) + col; + wall_rnd_palace(row, col); + if (which_part) { + wpp_fill(xh * 8, dmy - 59, 32, 20, cl[0]); + wpp_fill(xh * 8, dmy - 39, 16, 21, cl[11]); + wpp_fill((xh + 2) * 8, dmy - 39, 16, 21, cl[12]); + wpp_fill(xh * 8, dmy - 18, 8, 19, cl[22]); + wpp_fill((xh + 1) * 8, dmy - 18, 24, 19, cl[23]); + /* Габариты групп из pal_wall.atl: 3..5 = 8x7, 6..8 = 32x12, + * 9..11 = 32x12 (11-й 28x11), 12..14 = 30x5 (14-й 30x4), + * 15..17 = 32x3. Берём максимум по группе. */ + wp_blit((uint8_t)(wpp_d[0] + 3), (xh + 3) * 8, dmys - 53, 8, 7); + wp_blit((uint8_t)(wpp_d[1] + 6), xh * 8, dmys - 34, 32, 12); + wp_blit((uint8_t)(wpp_d[2] + 9), xh * 8, dmys - 13, 32, 12); + wp_blit((uint8_t)(wpp_d[3] + 12), xh * 8, dmys, 30, 5); + } + wpp_fill(xh * 8, dby - 2, 32, 3, cl[33]); + wp_blit((uint8_t)((which_part ? wpp_d[4] : wpp_d[0]) + 15), xh * 8, dbys, 32, 3); +} + +static void wall_pattern(int row, int col, int which_part) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + int xh = POP_COL_XH[col]; + int dby = 63 * row + 65; + uint8_t parts; + if (pop_palace) { wall_pattern_palace(row, col, which_part); return; } + /* Ранний выход по окну fore-клипа — то же, что у паласной ветки, но свой + * габарит. Самый верхний кусок здесь — ЛЕВАЯ марка: LPOS[4] = 58 плюс её + * высота 10, то есть dby+POP_YOFF−67; самый нижний — нижний разделитель, + * низ ровно на dby+POP_YOFF. По x правая марка уходит до xh*8+46 + * (a = 24 при dv<2, ширина 14), берём 48. */ + { + int x0 = xh * 8, dbs = dby + POP_YOFF; + if (!wp_vis(x0, 48, dbs - 67, dbs)) return; + } + parts = WP_PARTS[pop_wall_modifier(row, col)]; + + wall_rnd(row, col); + if (which_part) { + /* Габариты из pop_wall.atl: 13 = 32x21, 11/12 = 9x21. */ + if ((parts & WP_RND) && wr_rndblk) + wp_blit(RES_WALL_RNDBLOCK, xh * 8, dby - 42, 32, 21); + if (parts & WP_MID) + wp_blit((uint8_t)(RES_WALL_DIVIDER1 + wr_mid_div), + (xh + 1) * 8 + wr_mid_off, dby - 21, 9, 21); + } + if (parts & WP_BOT) + wp_blit((uint8_t)(RES_WALL_DIVIDER1 + wr_bot_div), + xh * 8 + wr_bot_off, dby, 9, 21); + if (which_part) { + if ((parts & WP_RIGHT) && wr_right) + draw_right_mark(wr_rightv, wr_mid_off, row, col); + if ((parts & WP_LEFT) && wr_left) + draw_left_mark(wr_leftv, wr_mid_off - wr_mid_div, + wr_bot_off - wr_bot_div, row, col); + } +} + +/* Решётка ворот (portcullis) из ЛЕВОЙ комнаты (seg008.c:1110). */ +static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + int x = xh * 8; + int gate_top_y = dby - 62; + int openness = (((modl > 188) ? 188 : modl) >> 2) + 1; + int gate_bot_y = dmy - openness; + int yb, gate_frame; + + if (gate_bot_y + 12 < dmy) + pop_env_b(50, x, gate_bot_y); + else + pop_env_b(51, x, gate_bot_y - 2); + yb = gate_bot_y - 12; + if (yb < 192) { + while (yb >= 0 && yb > 7 && (yb - 7) > gate_top_y) { + pop_env_b(52, x, yb); + yb -= 8; + } + } + gate_frame = yb - gate_top_y + 1; + if (gate_frame > 0 && gate_frame < 9) + pop_env_b(DOOR_FRAM_SLICE[gate_frame], x, yb); +} + +/* Та же решётка, но в ПЕРЕДНИЙ слой — поверх персонажа, стоящего в проёме + * (draw_gate_fore, seg008:1153). От back-варианта отличается ровно тем, что + * есть в оригинале: только ПРОЗРАЧНЫЕ куски (51 низ + 52 восьмипиксельные + * ломти), без непрозрачного 50 и без хвостового DOOR_FRAM_SLICE — фон под + * баррами уже нарисован, здесь возвращаются только сами прутья. + * Клип по прямоугольнику персонажа делает pop_t_fclip (см. pop_tile.h). */ +static void draw_gate_fore(uint8_t modl, int xh, int dby, int dmy) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + int x = xh * 8; + int gate_top_y = dby - 62; + int openness = (((modl > 188) ? 188 : modl) >> 2) + 1; + int gate_bot_y = dmy - openness; + int yb; + + pop_env_b(51, x, gate_bot_y - 2); + yb = gate_bot_y - 12; + if (yb < 192) + while (yb >= 0 && yb > 7 && (yb - 7) > gate_top_y) { + pop_env_b(52, x, yb); + yb -= 8; + } +} + + + + +/* ---- Точки входа для ХОЛОДНОЙ половины (pop_room.c, банк 7) ---------- * + * Тонкие обёртки, а не пометка самих тел `__banked`: трамплин выбирается + * ОБЪЯВЛЕНИЕМ, поэтому пометка тела заставила бы платить 654 такта и + * ГОРЯЧИХ вызывающих — fore-проход зовёт wall_pattern до девяти раз за кадр. + * С обёрткой горячий путь идёт прямым call, а трамплин достаётся только + * холодному (полная отрисовка комнаты: ~40 вызовов, 26 000 тактов РАЗОВО на + * вход в комнату). См. _pop_bg.h и memory/sdcc_banked_call_rules. */ +void pop_wall_pattern_b(int row, int col, int which_part) __banked +{ + wall_pattern(row, col, which_part); +} + +void pop_wall_pattern_reset_b(void) __banked +{ + wall_pattern_reset(); +} + +void pop_gate_back_b(uint8_t modl, int xh, int dby, int dmy) __banked +{ + draw_gate_back(modl, xh, dby, dmy); +} + +/* ---- fore-over-Kid (порт seg003 redraw_at_char + seg008 set_char_collision) -- + * Передний слой (fore_id = foretable-кусок) тайлов ФУТПРИНТА спрайта Kid + * — рядов top..bottom × колонок left..right (<= 2×2 = 4 тайла) — рисуется + * ПОВЕРХ Kid: то, что по изометрии перед ним (передние грани колонн/ворот/ + * большой колонны/щебня). Стены и факелы имеют fore_id=0 → остаются сзади. + * Рисуем в GFX_BANK_SPRITE (видео-ОЗУ, 0xFF прозрачен); heal персонажа восстановит + * из теневого фона (fore в нём уже запечён pop_room_draw). obj_x — ЛОГИЧЕСКАЯ + * X кадра Kid (до ×8/7), obj_y — низ спрайта, w/h — размер, dir — направление. */ +static int8_t col_from_x(int xpos) /* get_tile_div_mod: колонка (58/14) */ +{ + /* Таблицей (резидентная POP_TILE_DIV, см. pop_tile.h) — как в оригинале. + * Было `/14` и `%14`: два полноценных 16-битных деления SDCC на вызов, + * а зовётся это дважды на каждый футпринт персонажа. */ + if ((unsigned int)xpos < 256u) return POP_TILE_DIV[xpos]; + { + int x = xpos - 58; + int xh = x / 14; + if (x % 14 < 0) xh--; /* округление вниз для отрицательных */ + return (int8_t)xh; + } +} + +#define y_to_row(y) pop_y_to_row((int16_t)(y)) /* общий (pop_geom) */ + +/* Контекст ТЕКУЩЕГО тайла прохода: код, левый край, база Y. Порт глобалов + * оригинала (curr_tile / draw_xh / draw_main_y, которые load_curr_and_left_tile + * ставит ОДИН раз на тайл, seg008 redraw_needed_tiles) — все рисовальщики + * читают их, а не считают заново. У нас код тайла читался дважды за тайл + * (в fore_tile и снова тут), а координаты — в каждом листе. */ +static uint8_t ft_code; +static int ft_x, ft_dmy; + +/* Есть ли у кода тайла ХОТЬ ЧТО-ТО в переднем слое. Порт ранней проверки + * оригинала — `id = tile_table[curr_tile].fore_id; if (id == 0) return;` + * в начале ветки default у draw_tile_fore (seg008:0D15), — расширенной + * нашими спецслучаями: стена (20), пики (2), чомпер (0x12) и нижняя грань + * loose (11) рисуются мимо fore_id. + * + * Была в самом КОНЦЕ цепочки: до неё успевали отработать чтение кода тайла + * и все сравнения спецслучаев. Замер в MAME: fore-проход стоил 58 764 такта + * ради одного блита 10×2, а в позиции без переднего слоя — 112 758 тактов + * и ноль блитов. */ +static const uint8_t FORE_ANY[32] = { +/* 00 empty */ 0, /* 01 floor */ 0, /* 02 spike */ 1, /* 03 pillar */ 1, +/* 04 gate */ 1, /* 05 stuck */ 0, /* 06 closebtn */ 0, /* 07 doortopf */ 1, +/* 08 bpbot */ 1, /* 09 bptop */ 1, /* 0A potion */ 1, /* 0B loose */ 1, +/* 0C dtop */ 1, /* 0D mirror */ 1, /* 0E debris */ 1, /* 0F openbtn */ 0, +/* 10 ldL */ 0, /* 11 ldR */ 0, /* 12 chomper */ 1, /* 13 torch */ 0, +/* 14 wall */ 1, /* 15 skeleton */ 0, /* 16 sword */ 0, /* 17 balcL */ 0, +/* 18 balcR */ 0, /* 19 lattpil */ 1, /* 1A lattdown */ 1, /* 1B lattsm */ 1, +/* 1C lattl */ 1, /* 1D lattr */ 1, /* 1E debristo */ 1, /* 1F — */ 0 +}; + +/* Контекст ft_* обязан быть проставлен вызывающим. */ +static void fore_only_tile(int row, int col) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* Нажатая кнопка → пол/stuck (get_tile_to_draw) — это уже учтено в ft_code + * (его ставит pop_tile_code_drawn), иначе поверх Kid лезет передняя грань + * КНОПКИ. */ + uint8_t code = ft_code; + const piece *t; + int x = ft_x, dmy = ft_dmy; + if (!FORE_ANY[code]) return; /* ранний выход, как в оригинале */ + t = &pop_tile_table[code]; + if (code == 20) { + /* Во дворце тело стены даёт wall_pattern заливками, а спрайтовая + * грань не рисуется вовсе (seg008:711). */ + if (!pop_palace) + pop_wall_b(POP_WALL_FRAM_MAIN[pop_wall_modifier(row, col)], x, dmy); + wall_pattern(row, col, 1); + return; + } + if (code == 2) { /* пики: fore-кадр */ + uint8_t sf = pop_spike_frame(pop_tile_mod(row, col)); + if (sf > 0 && sf < 10 && SPIKES_FRAM_FORE[sf]) + pop_env_b(SPIKES_FRAM_FORE[sf], x, dmy - 2); + } + if (code == 0x12) { /* чомпер: передний слой челюстей */ + uint8_t cm = pop_tile_mod(row, col); + uint8_t pose = pop_chomp_pose(cm); + /* Передние куски чомпера и его кровь лежат в pop_fore.atl + * (FORE_ENV_IDS в pop_pack_bg.py), а НЕ в env-страницах: блит + * обязан быть pop_fore_b, иначе id уходит в чужую страницу. */ + pop_fore_b(POP_CHOMP_FRAM_FOR[pose], x, dmy); + if (cm & 0x80) /* кровь поверх переднего слоя */ + pop_fore_b((uint8_t)(pose + 119), x + 8, dmy - 6); + } + if (code == 10) { /* зелье: склянка (пузырёк — динамика) */ + pop_potion_flask(row, col, pop_tile_mod(row, col)); + } else if (t->fore_id) { + pop_fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy); + } +} + +/* Полоса кладки У ПОТОЛКА поверх Kid — порт redraw_needed_above (seg008:0221). + * Там draw_tile_bottom(1) и draw_loose(1): аргумент 1 = «добавить ещё и в + * FOREtable», т.е. нижняя грань потолка и кадр плиты-потолка рисуются ПОВЕРХ + * персонажа (иначе руки прыгающего Kid лезут на кромку потолка). Всё + * остальное из этой функции (floorright, anim_topright, right) идёт в + * backtable — под персонажем. */ +static void ceil_over_kid_tile(int col) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + uint8_t code = pop_tile_code(-1, col); + const piece *t = &pop_tile_table[code]; + int x = POP_COL_XH[col] * 8; + int dby = 63 * (-1) + 65; /* = 2 */ + if (code == 20) { /* draw_tile_bottom: стена */ + if (!pop_palace) /* seg008:576 */ + pop_wall_b(POP_WALL_FRAM_BOTTOM[pop_wall_modifier(-1, col)], x, dby); + /* wall_pattern ЗДЕСЬ НЕТ — и это не упущение. draw_tile_bottom + * (seg008:588) кладёт в foretable ТОЛЬКО кадр грани `id`, под своим + * `arg_0`; узор вызывается строкой ниже, вне этой ветки, и уходит в + * текущую таблицу, то есть в ФОН. Мы же звали его прямо в переднем + * проходе — узор задней стены штамповался поверх всего, что уже + * нарисовано, включая падающие плиты (найдено прогоном 2026-08-13). + * В фон он попадает там, где и положено, — при отрисовке тайла. + * + * Не путать с fore_only_tile: там узор в переднем слое ЗАКОННЫЙ, + * это порт draw_tile_fore (seg008:715), где он стоит явно рядом с + * главной гранью стены, обязанной перекрывать персонажа. */ + } else if (t->bottom_id) { + pop_env_b(t->bottom_id, x, dby); + } + if (code == 11) /* draw_loose(1): плита-потолок */ + pop_env_b(POP_LOOSE_FRAM_BOTTOM[pop_loose_frame(pop_ceil_modif[col])], x, dby); + ft_code = pop_tile_code_drawn(-1, col); /* контекст тайла для листа */ + ft_x = x; ft_dmy = 63 * (-1) + 62; + fore_only_tile(-1, col); /* draw_tile_fore */ +} + +/* Пересекается ли габарит тайла с окном fore-слоя. Шаг тайла — 32 px + * (POP_COL_XH[col]*8 = col*32), куски выходят вправо максимум на +8 (дивайдеры + * и метки рисуются в ячейке правого соседа) и вверх на 42+высота кадра. + * Даёт отсев ЦЕЛОГО тайла ДО обращения к атласу: спрайт 27..40 px шириной + * задевает не все тайлы футпринта, а платили за каждый (atlas_image + + * gfx_w0_map + разбор заголовка на КАЖДЫЙ кусок). */ +static uint8_t tile_in_fclip(int row, int col) +{ + int x0, y0; + if (!pop_t_fclip_on) return 1; + x0 = POP_COL_XH[col] * 8; + y0 = POP_YOFF + 63 * row; + /* По ЛОГИЧЕСКОЙ рамке: y0 тайла считается из его ряда и переворотом не + * меняется (см. pop_tile.h про пару fclip_l*). */ + return (uint8_t)(x0 < pop_t_fclip_x1 && x0 + 40 > pop_t_fclip_x0 && + y0 - 8 < pop_t_fclip_ly1 && y0 + 70 > pop_t_fclip_ly0); +} + +static void fore_tile(int row, int col) /* передний слой одного тайла */ +{ + pop_dbg_rdmax[1]++; /* ВРЕМЕННО: сколько тайлов обошли */ + if (!tile_in_fclip(row, col)) return; + pop_dbg_rdmax[2]++; /* ВРЕМЕННО: прошли окно клипа */ + /* draw_loose (seg008:0A38) — единственный кусок ТАЙЛА, который оригинал + * кладёт В ОБЕ таблицы БЕЗУСЛОВНО: нижняя грань loose-плиты идёт и в + * backtable, и в foretable. Значит передняя грань плиты рисуется ПОВЕРХ + * персонажа ВСЕГДА, а не только у потолка (ceil_over_kid_tile). Верх + * плиты (draw_tile_base) остаётся в backtable — поэтому голова и руки + * висящего/подтягивающегося Kid видны НАД плитой, а тело уходит ЗА неё. + * Без этого Kid лез на плиту «с переднего края» (комната 12: вис на + * кромке loose над дырой от соседней упавшей плиты). */ + ft_code = pop_tile_code_drawn(row, col); /* код тайла — ОДИН раз на тайл */ + if (!FORE_ANY[ft_code]) return; /* нечего класть в передний слой */ + pop_dbg_rdmax[3]++; /* ВРЕМЕННО: реально рисуем */ + ft_x = POP_COL_XH[col] * 8; + ft_dmy = 63 * row + 62; + if (row >= 0 && ft_code == 11) + pop_env_b(POP_LOOSE_FRAM_BOTTOM[pop_loose_frame(pop_loose_modif[row * 10 + col])], + ft_x, ft_dmy + 3); + /* draw_tile_fore (seg008:690) — ТОЛЬКО add_foretable-часть. Раньше здесь + * рисовалась ещё и передняя кромка пола (bottom_id) «чтобы перекрывала + * Kid», но в оригинале bottom идёт через draw_tile_bottom в backtable + * (под персонажем), а поверх него попадает лишь при draw_other_overlay. + * Из-за лишнего bottom у основания колонны проскакивала «тень». */ + fore_only_tile(row, col); +} + +/* Определение общих с pop_room.c obj-координат (объявление — _pop_bg.h). */ +int8_t pop_bg_obj_col, pop_bg_obj_row; + +/* Пол-оверлей при подъёме (draw_floor_overlay, seg008:1457): когда Kid + * подтягивается на floor-подобный тайл назначения (floor/pillar/stuck/torch), + * а тайл СЛЕВА пуст (т.е. кромка), передний край пола перекрывает нижнюю + * часть Kid — иначе видна часть, что должна быть за полом. Кадр подъёма + * 137..144 индексирует floor_left_overlay. */ +static const uint8_t FLOOR_LEFT_OVERLAY[8] = {32, 151, 151, 150, 150, 151, 32, 32}; + +static void other_overlay_tile(int row, int col); /* fwd (draw_floor_overlay -> else) */ + +static void climb_overlay_tile(int row, int col, uint8_t fidx) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* тип — ПОДСТАВЛЕННЫЙ (нажатая кнопка = пол): иначе кромка, за которую + * Kid спускается/подтягивается, закрашивается целым тайлом */ + uint8_t code; + if (!tile_in_fclip(row, col)) return; + code = pop_tile_code_drawn(row, col); + int x, dmy, dby; + const piece *t; + if (pop_tile_code(row, col - 1) != 0) return; /* слева не пусто → не кромка */ + if (code != 1 && code != 3 && code != 5 && code != 19) { + /* seg008:1E3A draw_floor_overlay: floor-оверлей рисуется только для + * floor/pillar/stuck/torch, а для ОСТАЛЬНЫХ тайлов вызывается + * draw_other_overlay() — иначе кнопка (opener/closer), за кромку + * которой Kid спускается, не перекрывает его. */ + other_overlay_tile(row, col); + return; + } + x = POP_COL_XH[col] * 8; + dmy = 63 * row + 62; + dby = dmy + 3; + t = &pop_tile_table[code]; + ov_mark(x, 63 * row + 2, 64, 64); + pop_env_b(FLOOR_LEFT_OVERLAY[fidx], x, dmy + (code == 5 ? 1 : 0)); + if (t->bottom_id) pop_env_b(t->bottom_id, x, dby); /* draw_tile_bottom */ +} + + +/* draw_tile2 (seg008:1F48) — но ТОЛЬКО та его часть, что реально может лечь + * ПОВЕРХ персонажа. Ключ (подтверждено трассой оригинала): draw_tile_right, + * draw_tile_anim_right и draw_loose добавляют спрайты через add_backtable + * НАПРЯМУЮ, игнорируя ptr_add_table — значит правая грань левого соседа + * («шахматка» столба 93, blueline, грани пик/loose соседа, кадр loose) + * ВСЕГДА рисуется ПОД персонажем. Через ptr_add_table (→ midtable при + * draw_other_overlay) идут только: «floor B» 42 при левом соседе-поле, + * draw_tile_base, draw_tile_anim (свои пики) и draw_tile_bottom. */ +static void overlay_mid_tile(int row, int col) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + uint8_t code = pop_tile_code_drawn(row, col); + uint8_t lcode = pop_tile_code_drawn(row, col - 1); + const piece *t; + int x = POP_COL_XH[col] * 8; + int dby = 63 * row + 65; + int dmy = dby - 3; + t = &pop_tile_table[code]; + ov_mark(x, 63 * row + 2, 64, 64); /* тайл + правый сосед (грани) */ + if (code != 20 && lcode == 1) /* draw_tile_right: только 42 */ + pop_env_b(42, x, pop_tile_table[1].right_y + dmy); + { /* draw_tile_base (seg008:0A8E) — ЦЕЛИКОМ, вместе с подстановками id: + * у loose верх плиты берётся из loose_fram_left (в pop_tile_table base_id=0), + * у opener'а без пола слева — 148. Раньше здесь стоял голый base_id, и + * верх loose-плиты в оверлей не попадал: передняя грань ложилась поверх + * Kid (foretable), а сама плита — нет, и подтягивающийся Kid рисовался + * поверх её верхней плоскости (комната 12). */ + uint8_t base_id = t->base_id; + if (code == 11 && row >= 0) + base_id = POP_LOOSE_FRAM_LEFT[pop_loose_frame(pop_loose_modif[row * 10 + col])]; + else if (code == 0x0F && lcode == 0) + base_id = 148; + if (base_id) + pop_env_b(base_id, x, t->base_y + dmy); + } + if (code == 2) { /* draw_tile_anim: свои пики */ + uint8_t sf = pop_spike_frame(pop_tile_mod(row, col)); + if (sf > 0 && sf < 10 && POP_SPIKES_FRAM_LEFT[sf]) + pop_env_b(POP_SPIKES_FRAM_LEFT[sf], x, dmy - 2); + } + if (code == 20) { /* draw_tile_bottom */ + uint8_t wmod = pop_wall_modifier(row, col); + if (!pop_palace) /* seg008:576 */ + pop_wall_b(POP_WALL_FRAM_BOTTOM[wmod], x, dby); + wall_pattern(row, col, 0); + } else if (t->bottom_id) { + pop_env_b(t->bottom_id, x, dby); + } + /* draw_loose тут НЕТ, и это не забывчивость: в оригинале (seg008 + * draw_loose) нижняя грань плиты идёт в add_backtable + add_foretable, + * мимо ptr_add_table — то есть в midtable не попадает никогда. Перекрыть + * ею падающий кусок должен ПЕРЕДНИЙ слой (fore_only_tile), а не оверлей. */ +} + +/* draw_tile_fore (seg008:690) — ТОЛЬКО передний слой (add_foretable): он + * рисуется после всего и поверх персонажа. Нужен для полосы кладки у + * потолка: в redraw_needed_above всё остальное идёт в backtable (под Kid). */ + +/* draw_other_overlay (seg008:1492): для поз виса/прыжка/падения/захвата на + * КРОМКЕ (сосед слева пуст, либо тайл через один слева пуст) — перерисовать + * ВЕСЬ тайл поверх Kid. Тело пола перекрывает висящего/прыгающего/падающего + * ПОД ним. Срабатывает только в этих позах (см. redraw_at_char2 ниже) — + * поэтому при стоянии/залезании ложной окклюзии нет. + * + * ПОРЯДОК (redraw_needed_tiles, seg008:1B06 + redraw_needed, seg008:0211): + * тайлы обходятся ряды 2,1,0, внутри ряда колонки 0..9; в тайле сначала + * оверлей (redraw_frames2 -> midtable), потом ОБЪЕКТЫ этого тайла. Kid — тоже + * запись midtable, вставляемая при обработке СВОЕГО тайла (obj_row/obj_col). + * Значит оверлей ложится ПОВЕРХ Kid только у тайлов, идущих в обходе ПОЗЖЕ + * его собственного; всё, что раньше, — под ним. У нас списков нет (рисуем + * сразу), поэтому порядок эмулируется явной проверкой. */ +static void other_overlay_tile(int row, int col) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + uint8_t code, left; + if (!tile_in_fclip(row, col)) return; + code = pop_tile_code(row, col); + /* Пустой тайл НЕ окклюдирует: у него нет тела пола/стены, а draw_tile для + * него рисует лишь ФОНОВУЮ сетку (BLUELINE «силуэт кладки» — правая грань + * пустого соседа). В оригинале она уходит в y-сортируемый midtable и + * ложится ПОЗАДИ Kid; у нас draw_tile кладёт её прямо поверх → сетка на + * висящем/падающем Kid. Фон запечён один раз в pop_room_draw — над + * спрайтом его перерисовывать не нужно. */ + if (code == 0) return; + /* Тайл идёт в обходе РАНЬШЕ тайла Kid → его оверлей лёг бы ПОД спрайт. */ + if (row > pop_bg_obj_row || (row == pop_bg_obj_row && col <= pop_bg_obj_col)) return; + left = pop_tile_code(row, col - 1); + if (left == 0) { overlay_mid_tile(row, col); return; } /* ветка 1: сосед слева пуст */ + if (col > 0 && pop_tile_code(row, col - 2) == 0) { + /* Ветка 2 (тайл ЧЕРЕЗ ОДИН слева пуст): в оригинале она ставит + * tile_object_redraw[tilepos]=0xFF, т.е. объекты тайлов tilepos−1 и + * tilepos рисуются СРАЗУ ПОСЛЕ оверлея — Kid возвращается наверх. + * Без этого правая грань соседнего столба (кадр 93) остаётся поверх + * прыгающего Kid (баг «тень на дальнем столбе поверх Кида»). */ + if (row == pop_bg_obj_row && (col == pop_bg_obj_col + 1)) return; + overlay_mid_tile(row, col); + } +} + +/* Оверлей тайла ПОВЕРХ ПАДАЮЩЕГО КУСКА — вторая половина draw_mob + * (seg007:1149: set_redraw2 рядом с set_redraw_fore). set_redraw2 ведёт в + * draw_other_overlay, а тот кладёт части соседа в MIDtable + * (ptr_add_table = &add_midtable, seg008:1507/1512), тогда как кусок сидит в + * objtable, который вливается в midtable при обходе СВОЕГО тайла. Обход идёт + * ряды 2,1,0, колонки 0..9 — значит сосед перекрывает кусок, если его тайл + * идёт в обходе ПОЗЖЕ, и весь порядок уже проверяется внутри + * other_overlay_tile; ему нужен лишь ориентир «тайл объекта». + * + * Именно этой половины и не хватало: плита, отвалившаяся от (0,2), накрывала + * собой переднюю кромку пола (0,3) — а вернуть её через ПЕРЕДНИЙ слой нельзя, + * у кнопки fore_id = 0 (сверено с tile_table оригинала, там то же самое). + * Найдено пользователем 2026-08-17, уровень 1 комната 12. + * + * Ориентир сохраняем и возвращаем: pop_bg_obj_* принадлежит текущему + * персонажу, и его читает ещё и mob_tick_one (решение defer). */ +void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col, + int mx, int my, uint8_t mh) __banked +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + int8_t saved_row = pop_bg_obj_row, saved_col = pop_bg_obj_col; + pop_bg_obj_row = oref_row; + pop_bg_obj_col = oref_col; + /* ОКНО = прямоугольник самого КУСКА. Смысл оверлея — перекрыть кусок, а + * то, что лежит вне него, и так нарисовано правильным фоном. Даёт сразу + * три вещи: + * - рисуем в разы меньше пикселей (тайл соседа шириной 32 обрезается до + * перекрытия с куском); + * - куски тайла, вовсе не задевающие кусок, отсеиваются предфильтром + * pop_blit_b даром — это и есть вертикальный гейт, отдельного не надо; + * - ov_mark становится НЕ НУЖЕН: всё нарисованное внутри heal-коридора + * куска, а он чистится каждый кадр. + * Замер 2026-08-17: без окна оверлеи шести кусков давали +210 000 в циане + * и +130 000 в «ввод+heal» (объединение ov_mark на полкомнаты). */ + ov_suppress = 1; + pop_t_win_set(mx, my - (int)mh + 1 + POP_YOFF, 64, (uint8_t)(mh + 1)); + gfx_set_bank(GFX_BANK_SPRITE); + other_overlay_tile(row, col); + gfx_set_bank(GFX_BANK_NORMAL); + pop_t_win_clear(); + ov_suppress = 0; + pop_bg_obj_row = saved_row; + pop_bg_obj_col = saved_col; +} + +/* Футпринт персонажа в тайлах (порт set_char_collision, seg006:0723). + * Общий счёт для любого Char (Кид, страж, скелет) — в оригинале это один + * код на всех. Результат — в файловых статиках, а не через 6 указателей: на Z80 такой + * возврат стоил бы дороже самой функции. */ +static int8_t fp_cL, fp_cR, fp_cLraw, fp_rT, fp_rB, fp_rTraw; + +static void char_footprint(int obj_x, int obj_y, uint16_t w, uint16_t h, + int8_t dir, uint8_t sword) +{ + int wh = (int)((w + 1) >> 1); /* char_width_half */ + int xl = obj_x / 2 + 58; /* char_x_left (58-база) */ + int xr; + int8_t cL, cR, cLraw, rT, rB, rTraw; + if (dir >= 0) xl -= wh; /* лицом вправо: левый край внутрь */ + xr = xl + wh; + cLraw = col_from_x(xl); /* до клампа: <0 = футпринт за левым швом */ + cL = cLraw; if (cL < 0) cL = 0; else if (cL > 9) cL = 9; + cR = col_from_x(xr); if (cR < 0) cR = 0; else if (cR > 9) cR = 9; + /* redraw_at_char (seg003:0430): «If char is holding sword, it makes + * redraw-area bigger» — КЛИНОК торчит на колонку дальше габарита кадра, + * и без этого расширения передние грани в той колонке не + * перерисовываются: меч остаётся поверх столба, а его след — на фоне. */ + if (sword >= SWORD_2_DRAWN) { + if (dir >= 0) { if (++cR > 9) cR = 9; } + else { if (--cL < 0) cL = 0; } + } + /* Порт set_char_collision (seg006:0723): нижний ряд = y_to_row_mod4(obj_y), + * причём обёртка -1 (Kid НИЖЕ комнаты — climbdown/провал, obj_y в полосе + * «ряд 3») трактуется как ряд 3, НЕ ряд 0. Верхний ряд считается от + * char_top_y с ресетом в 0, если весь спрайт ниже комнаты (>=192). */ + { + int top_y = obj_y - (int)h + 1; + if (top_y >= 192) top_y = 0; + rT = y_to_row(top_y); + rB = y_to_row(obj_y); + if (rB == -1) rB = 3; /* обёртка → ряд 3 (ниже комнаты) */ + rTraw = rT; /* −1 = верх спрайта в полосе у потолка */ + } + /* single-room SprPoP: ряд 3 (комната снизу) в этом виде не рисуется → + * окклюдер спереди = ряд 2 (передняя грань пола, за которой нога). */ + if (rB < 0) rB = 0; else if (rB > 2) rB = 2; + if (rT < 0) rT = 0; else if (rT > 2) rT = 2; + /* Голова/руки почти всегда торчат в ряд НАД опорным (Kid выше тайла). + * y_to_row(верх спрайта) из-за +60-сдвига может схлопнуться в опорный + * ряд (rT==rB) — тогда ряд над ним не попадёт в перебор и его окклюдер + * (нижняя грань стены / кромка пола) не перерисуется поверх Kid. Форсим + * включение ряда rB-1. */ + if (rB > 0 && rT >= rB) rT = rB - 1; + fp_cL = cL; fp_cR = cR; fp_cLraw = cLraw; + fp_rT = rT; fp_rB = rB; fp_rTraw = rTraw; +} + +/* Передние грани тайлов и оверлеи ПОВЕРХ персонажа — ОДИН проход на всех + * Char. В оригинале add_kid_to_objtable (seg008:22F0) и + * add_guard_to_objtable (seg008:2324) имеют ИДЕНТИЧНОЕ тело и различаются + * только окном (loadkid/loadshad), типом объекта и спецкейсом тени у + * зеркала; redraw_at_char / redraw_at_char2 (seg003:0576/0645) гейтов по + * charid не имеют вовсе. + * + * До DRAW-CHAR тут стояли ДВА прохода — свой у Кида, свой у соперника, — и + * расхождение уже стоило бага: падающий скелет рисовался поверх верхней + * грани пола соседней колонки, потому что порт redraw_at_char2 был только у + * Кида. Тем же путём пошли бы тень (ур. 4/5/6/12), визирь (13) и толстяк + * (12) — поэтому проход сведён к одному. + * + * obj_x — ЛОГИЧЕСКАЯ X кадра (до ×8/7), obj_y — низ спрайта, w/h — размер + * кадра; всё остальное берётся из самого персонажа (ch). Окно fore-клипа к + * этому моменту уже стоит: его задаёт тот, кто нарисовал спрайт. */ +/* Передний слой ОДНОГО тайла — вход для прохода по пометкам + * (pop_fore_needed). Без окна fore-клипа: пометка уже сузила работу до + * нужных тайлов, а кусок тайла обязан лечь целиком — у оригинала foretable + * ничем не режется. */ +void pop_fore_tile_b(int row, int col) __banked +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + uint8_t saved = pop_t_fclip_on; + pop_t_fclip_on = 0; + fore_tile(row, col); + pop_t_fclip_on = saved; +} + +/* Передний слой полосы кладки У ПОТОЛКА (ряд −1) для ОДНОГО столбца — вход для + * прохода по пометкам (pop_fore_needed, ряд −1 идёт там последним). + * + * Тот же redraw_needed_above (seg008:0221), что и над персонажем: обход ряда −1 + * стоит в redraw_needed_tiles ПОСЛЕДНИМ, а его draw_tile_fore кладёт куски в + * foretable, то есть поверх midtable (там лежат падающие плиты) и поверх + * объектов. Без этого кусок, пролетающий у потолка, накрывает собой кромку + * потолка — физически невозможная картинка (найдено пользователем 2026-08-17, + * уровень 1 комната 6: плита (−1,5) перекрывала потолок (−1,6)). + * + * Окно — ровно полоса этого столбца: передние куски потолка живут в трёх + * верхних строках (dby = 2 при клипе по POP_YOFF), рисовать шире незачем. + * ov_mark — как у персонажа: полоса рисуется банком без тени, и на второй + * странице её надо будет восстановить (pop_fore_heal). */ +void pop_ceil_fore_tile_b(int col) __banked +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + int x = POP_COL_XH[col] * 8; + /* ov_mark ЗДЕСЬ НЕ НУЖЕН, и это важно для бюджета. Передние куски потолка + * рисуются от dby = 2, то есть занимают строки 0..2 поля, а пометку ставит + * ТОЛЬКО падающий кусок, и только пока он до этих строк достаёт + * (mob_y <= 18) — значит нарисованное целиком лежит в его heal-коридоре + * (mob_y−18 .. mob_y+1), который чистится каждый кадр на своей странице. + * Пока ov_mark тут стоял, его объединение с оверлеями соседей + * растягивалось на полкомнаты и pop_fore_heal стоил 187 500 такта вместо + * 55 000 (замер 2026-08-17). */ + pop_t_clip_top = POP_YOFF; + pop_t_win_set(x, POP_YOFF, 64, CEIL_BAND_H); + ceil_over_kid_tile(col); + pop_t_win_clear(); + pop_t_clip_top = 0; +} + +/* Банк на ВЕСЬ проход пометок, а не на каждый тайл: помеченных тайлов за + * кадр бывает больше десятка, и две смены банка на каждый — заметная доля + * прохода, который сам по себе дешёвый. */ +void pop_fore_pass_begin(void) __banked { gfx_set_bank(GFX_BANK_SPRITE); } +void pop_fore_pass_end(void) __banked { gfx_set_bank(GFX_BANK_NORMAL); } + +void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y, + uint16_t w, uint16_t h) __banked +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + int8_t cL, cR, cLraw, rT, rB, rTraw, r, c; + uint8_t frame = ch->frame, action = ch->action; + pop_t_fclip_on = 1; /* окно = прямоугольник спрайта */ + char_footprint(obj_x, obj_y, w, h, ch->direction, ch->sword); + pop_dbg_b1(); /* ЗАМЕР: char_footprint */ + pop_dbg_rdmax[1] = pop_dbg_rdmax[2] = pop_dbg_rdmax[3] = 0; /* ВРЕМЕННО */ + /* Футпринт считается по КАДРУ персонажа (char_x_left/right, seg006:1021) + * и потому УЖЕ спрайта, а рядом с ним рисуются ещё клинок и «брызги» + * удара — они уходят ВПЕРЁД-ВВЕРХ и запросто попадают в соседнюю + * колонку или ряд выше. Оригинал этим не страдает: он помечает тайлы + * на перерисовку и рисует foretable целиком, а у нас проход сужен окном + * ради скорости (см. pop_fore_layer_cost). Поэтому перебор расширяется + * САМИМ ОКНОМ fore-клипа — оно и есть объединение «персонаж + клинок + + * брызги», считать его второй раз не надо. */ + /* Границы считаются АРИФМЕТИКОЙ, а не перебором всех колонок и рядов. + * Условие монотонно по номеру (x0 растёт), значит принятые номера идут + * подряд — хватает первого и последнего. Перебор стоил 38 970 тактов на + * кадр (замер MAME): тринадцать итераций, в каждой пара 16-битных + * ЗНАКОВЫХ сверок, а SDCC пишет их через `jp PO / xor 0x80 / jp P`. */ + if (pop_t_fclip_x1 > pop_t_fclip_x0 && pop_t_fclip_y1 > pop_t_fclip_y0) { + int8_t lo, hi; + /* Колонки: x0 = POP_COL_XH[c]*8 = 32*c (таблица линейна, шаг 4). + * 32c + 40 > x0 -> c >= floor((x0−40)/32) + 1 + * 32c < x1 -> c <= ceil(x1/32) − 1 + * Сдвиг вправо у SDCC z80 для int — арифметический, то есть floor и + * для отрицательных; на это здесь и рассчитано. */ + lo = (int8_t)(((pop_t_fclip_x0 - 40) >> 5) + 1); + hi = (int8_t)((((pop_t_fclip_x1 + 31) >> 5)) - 1); + if (lo < 0) lo = 0; + if (hi > 9) hi = 9; + if (lo <= hi) { + if (lo < fp_cL) fp_cL = lo; + if (hi > fp_cR) fp_cR = hi; + } + /* Ряды: y0 = POP_YOFF + 63*r, r только 0..2 — деления на 63 не надо, + * достаточно сравнить с границами полос. + * 63r > y0 − POP_YOFF − 70 и 63r < y1 − POP_YOFF + 8 */ + { + /* Ряды — величина ЛОГИЧЕСКАЯ, поэтому по логической рамке. */ + int a = pop_t_fclip_ly0 - POP_YOFF - 70; + int b = pop_t_fclip_ly1 - POP_YOFF + 8; + lo = a < 0 ? 0 : (a < 63 ? 1 : 2); + hi = b > 126 ? 2 : (b > 63 ? 1 : 0); + if (a < 126 && b > 0 && lo <= hi) { + if (lo < fp_rT) fp_rT = lo; + if (hi > fp_rB) fp_rB = hi; + } + } + } + pop_dbg_b2(); /* ЗАМЕР: границы окна */ + cL = fp_cL; cR = fp_cR; cLraw = fp_cLraw; + rT = fp_rT; rB = fp_rB; rTraw = fp_rTraw; + /* set_objtile_at_char (seg006:1833): тайл, при обработке которого спрайт + * попадает в midtable — база порядка окклюзии в other_overlay_tile. + * run_jump (action 1) — нижний ряд/левая колонка габарита; вис/полёт/ + * подъём — колонка на одну ЛЕВЕЕ curr_col. */ + pop_bg_obj_row = ch->curr_row; + pop_bg_obj_col = ch->curr_col; + if (action == 1) { pop_bg_obj_row = rB; pop_bg_obj_col = cL; } + if ((frame >= 135 && frame < 149) || + action == 2 || action == 3 || action == 4 || action == 6) + pop_bg_obj_col--; + gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, heal восстановит */ + /* Шовная окклюзия ворот (порт redraw_at_char для шва): футпринт зашёл за + * ЛЕВЫЙ шов (cLraw<0) и у соседа слева в этом ряду решётка ворот (код 4) — + * перерисовать её бары ПОВЕРХ персонажа. Иначе он, стоя за закрытыми + * воротами (col-1), рисуется поверх решётки. Бары = draw_gate_back по + * живому modif соседа (pop_t_lbg = lcol_bg, openness). */ + if (cLraw < 0 && pop_t_lfg) + for (r = rT; r <= rB; r++) + if ((pop_t_lfg[r] & 0x1F) == 4) { + int dby = 63 * r + 65; + draw_gate_back(pop_t_lbg ? pop_t_lbg[r] : 0, POP_COL_XH[0], dby, dby - 3); + } + if (frame >= 137 && frame <= 144) { /* подъём: пол назначения поверх него */ + uint8_t fidx = (uint8_t)(frame - 137); + for (r = rT; r <= rB; r++) + for (c = cL; c <= cR; c++) + climb_overlay_tile(r, c, fidx); + } else { + /* redraw_at_char2 (seg003:449) — путь set_redraw2/draw_other_overlay: + * тело пола на кромке перекрывает Kid в позах захвата (78-79), виса + * (action 2/6), полёта (3/4) и старта падения (bumped 102-106). + * Верхний ряд — всегда; опорный — кроме action 2 (hang_climb). */ + uint8_t do_ov = 0; + if (frame >= 78 && frame < 80) do_ov = 1; /* захват */ + else if (action == 2 || action == 3 || action == 4 || action == 6) do_ov = 1; + else if (action == 5 && frame >= 102 && frame <= 106) do_ov = 1; + else if (frame == 135 || frame == 136) do_ov = 1; /* начало подъёма (SEQ_UP */ + /* на 141 — до него Kid ещё */ + /* в ряду 1 ПОД полом [0,3]) */ + if (do_ov) { + /* ВЕСЬ диапазон рядов, а не только два крайних. В оригинале + * (redraw_at_char2, seg003:0645) стоит ровно два вызова — + * char_bottom_row и char_top_row, — и этого хватает, потому что + * там они ВСЕГДА соседние. У нас между ними бывает пропуск: + * char_footprint форсит rT = rB−1 (голова торчит в ряд выше), а + * потом окно fore-клипа расширяет rB ещё на ряд вниз — rT=0, + * rB=2 давали обход рядов 0 и 2 МИМО ряда 1. + * + * Ровно так терялась передняя грань пола у Кида, спрыгнувшего с + * кромки: кадр 103, action 3, ряд по y = 1, а обход шёл по 0 и 2 + * (трасса MAME 2026-08-13: rT=0 rB=2 cL=0 cR=1, ни одного вызова + * overlay_mid_tile). Глубже в падении y_to_row давал уже 2, ряд + * 1 попадал в пару крайних — и плита вдруг начинала рисоваться + * правильно, что и было видно глазом (JUMP-FLOOR-LEGS). */ + for (c = cR; c >= cL; c--) { + int8_t r2; + for (r2 = rB; r2 >= rT; r2--) { + /* seg003:0645: при hang_climb опорный ряд НЕ трогаем. */ + if (r2 == rB && action == 2) continue; + other_overlay_tile(r2, c); + } + } + } + } + pop_dbg_b3(); /* ЗАМЕР: оверлеи сделаны */ + /* draw_tile_fore (seg008:690) — foretable, рисуется ПОСЛЕ midtable, т.е. + * ПОСЛЕ оверлеев выше: передняя грань колонны перекрывает скос пола + * (floor_left_overlay), а не наоборот. Раньше fore шёл первым, и тёмный + * скос ложился поверх колонны — «тень у её основания». */ + for (r = rT; r <= rB; r++) + for (c = cL; c <= cR; c++) + fore_tile(r, c); + /* Первое условие draw_tile_fore (seg008:0D15): персонаж СТОИТ на тайле + * ворот — их решётка возвращается поверх него. Решение приняла карта + * (pop_gate_over_char, разбор там же); здесь остаётся отрисовка. Бары + * рисуются в колонке curr_col+1: графика ворот принадлежит ПРАВОМУ краю + * своего тайла и в оригинале кладётся в проходе следующей колонки. + * + * Один вызов через трамплин на персонажа за кадр — не в тайловом цикле: + * тайл-кандидат всегда ровно один, а fore-проход и так самый горячий + * кусок кадра (memory pop_fore_layer_cost). */ + pop_dbg_b4(); /* ЗАМЕР: цикл fore_tile */ + if (pop_gate_over_char(ch)) { + int8_t gr = ch->curr_row, gc = (int8_t)(ch->curr_col + 1); + if (tile_in_fclip(gr, gc)) { + int dby = 63 * gr + 65; + draw_gate_fore(pop_t_bg ? pop_t_bg[gr * 10 + ch->curr_col] : 0, + POP_COL_XH[gc], dby, dby - 3); + } + } + /* redraw_needed_above (seg008:0221): полоса кладки У ПОТОЛКА (ряд −1 = + * ряд 2 комнаты сверху). В оригинале она рисуется в самом конце обхода + * тайлов — ПОСЛЕ объектов, т.е. поверх персонажа (redraw_frames_above, + * куда set_redraw* уводит отрицательные tilepos). Без этого руки Kid, + * прыгающего в ряду 0, лезли поверх потолка. */ + if (rTraw < 0) { + pop_t_clip_top = POP_YOFF; + for (c = cL; c <= cR; c++) { + ov_mark(POP_COL_XH[c] * 8, 0, 64, CEIL_BAND_H); + ceil_over_kid_tile(c); + } + pop_t_clip_top = 0; + } + gfx_set_bank(GFX_BANK_NORMAL); + pop_t_fclip_on = 0; +} diff --git a/applications/SprPoP/src/pop_bg.h b/applications/SprPoP/src/pop_bg.h new file mode 100644 index 0000000..c35bb14 --- /dev/null +++ b/applications/SprPoP/src/pop_bg.h @@ -0,0 +1,231 @@ +/* + * pop_bg.h — отрисовка СТАТИЧЕСКОГО фона комнаты PoP композицией слоёв + * (порт seg008.c draw_tile/wall_pattern, Шаг 3). Рисует прямо в текущую + * страницу (banked GFX_BANK_TRANSPARENT — в видео-ОЗУ + теневую копию, + * 0xFF прозрачен), ОДИН раз при входе в комнату. + * + * Данные: getimage-спрайты в атласах pop_env0..4/pop_wall/pop_fore + * (toolchain/pop_pack_bg.py), палитра pop_bg.pal. Прямая адресация + * id: env[id>>5] idx id&31, wall idx=id, fore idx=id — ноль remap-таблиц. + */ +#ifndef POP_BG_H +#define POP_BG_H + +#include +#include "pop_char.h" /* pop_char_t — общий проход отрисовки поверх Char */ +#include "pop_qsave.h" + +/* ---- Вертикальный layout (PoP-экран 320×200 в нашем 320×256) --------- * + * Игровой экран 200px центрируем по высоте: сдвиг всего рисунка вниз на + * POP_YOFF = (256-200)/2 = 28px (чёрные борта сверху/снизу). Внутри 200px + * верхние POP_PLAYFIELD_H = 192 — поле тайлов/спрайтов (комната = 3 ряда), + * нижние 8 — статус-полоса (жизни/сообщения): тайлы/спрайты туда НЕ лезут. + * Сдвиг применяется на границе примитивов (blit/heal) — логика рисования + * остаётся в «room-space» y=0..191. */ +#define POP_YOFF 28 +#define POP_PLAYFIELD_H 192 +/* Экранный y нижней границы поля (ниже — статус-полоса + борт → клип). */ +#define POP_CLIP_BOTTOM (POP_YOFF + POP_PLAYFIELD_H) + +/* Экранный верх прямоугольника при ПЕРЕВОРОТЕ (зелье инверсии): поле + * отражается внутри себя, борта не трогаются. ОДНА формула на весь порт — + * блит тайла, heal точечных перерисовок и чёрные плиты обязаны считать + * одинаково, иначе стираем в одном месте, а рисуем в другом (именно так + * ломались чомперы и решётка). Зеркалит ВЫЗЫВАЮЩИЙ; у примитивов libbgi + * семантика координат не меняется. */ +#define POP_FLIP_TOP(top, h) (2 * POP_YOFF + POP_PLAYFIELD_H - (top) - (int)(h)) + +/* Выбор ядра libbgi (pop_onscreen_cols / pop_heal_fast) переехал в + * `_pop_draw.h` — его включают только три рисующих модуля. Из общего + * заголовка SDCC размножал мёртвые копии этих inline'ов по всем TU: см. + * шапку `_pop_draw.h`. */ + +/* Загрузить 7 атласов фона. Звать ДО initgraph (как poc.c — atlas_load + * трогает W3; проверенный порядок). Палитру pop_bg.pal грузит ПРИЛОЖЕНИЕ + * после initgraph (gfx_pal_fload). 0 — OK, -1 — ошибка. */ +int pop_bg_load(uint8_t set) __banked; /* 0 подземелье, 1 дворец */ +/* Пере-применить 32 записи палитры текущего тайлсета (env 0x50, wall 0x60). + * Звать после gfx_pal_fload(kid.pal): та собрана из VDUNGEON и затирает их. */ +int pop_bg_pal_apply(void) __banked; + +/* Освободить EMM-страницы атласов. */ +void pop_bg_free(void) __banked; + +/* Нарисовать комнату: fg[30]/bg[30] — foretable-коды/backtable-модификаторы + * (по строкам, row*10+col). lcol_fg/lcol_bg — 3 тайла правого столбца + * ЛЕВОЙ соседней комнаты (для границы, напр. решётки ворот), или 0 — + * тогда левая кромка = стена. room_num — номер комнаты (зерно стен). */ +void pop_room_draw(uint8_t room_num, + const uint8_t *fg, const uint8_t *bg, + const uint8_t *lcol_fg, const uint8_t *lcol_bg) __banked; + +/* Задать ряд 0 комнаты СНИЗУ — для draw_tile_topright, когда тайл нижнего + * ряда стал empty (после падения loose): «стена вниз» рисуется по коду + * room_B(0, col-1). ОДИННАДЦАТЬ байт: [0..9] — колонки комнаты снизу, + * [10] — тайл (0,9) комнаты СНИЗУ-СЛЕВА (сосед угла (2,0); см. + * load_rowbelow, seg008:368). 0 — нет комнаты снизу. */ +/* НЕ __banked: тело в резиденте (pop_tile.c) — из банка зовётся прямым call. */ +void pop_room_set_below(const uint8_t *below_row0_fg) __banked; + +/* Задать ряд 2 (fg-коды + модификаторы, 10 шт.) комнаты СВЕРХУ — для полосы + * кладки у потолка (seg008 draw_room доп. верхний ряд из room_A). 0 — нет + * комнаты сверху (кромка уровня → пол). */ +void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod) __banked; + +/* Перерисовать передний слой (fore) и оверлеи тайлов ПОВЕРХ персонажа — + * ОДИН проход на всех Char (Кид, страж, скелет, дальше тень/визирь): в + * оригинале add_kid_to_objtable и add_guard_to_objtable имеют одинаковое + * тело, а redraw_at_char/redraw_at_char2 гейтов по charid не имеют. + * Звать ПОСЛЕ отрисовки спрайта, когда окно fore-клипа уже стоит + * (pop_fore_set_clip) — перебор тайлов расширяется по нему. + * + * ch — персонаж (Kid/Guard): из него берутся direction/sword/frame/action и + * curr_col/curr_row. obj_x — ЛОГИЧЕСКАЯ X кадра (до ×8/7), obj_y — низ + * спрайта, w/h — размер кадра. См. redraw_at_char/set_char_collision. */ +void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y, + uint16_t w, uint16_t h) __banked; + +/* Окно fore-слоя: прямоугольник СПРАЙТА (экранные координаты, y уже с + * POP_YOFF), поверх которого fore-проход возвращает куски тайлов. Ставит + * тот, кто нарисовал спрайт (pop_char_draw) — он один знает + * итоговый габарит (у Kid — с учётом клинка и обрезки clip_char). Вне + * fore-проходов окно игнорируется: фон рисуется целиком. Мотивация и + * замер — в pop_bg.c у fclip_on. */ +void pop_fore_set_clip(int x, int y, int w, int h); + +/* Восстановить фон там, где в прошлых кадрах рисовался ОВЕРЛЕЙ + * (draw_other_overlay/floor_overlay/полоса потолка): он выходит за + * прямоугольник персонажа, поэтому его heal этого не убирает — на второй + * странице дабл-буфера остаётся след и мерцает. Звать рядом с + * pop_char_heal. */ +void pop_fore_heal(void) __banked; + +/* Loose floors — динамическая отрисовка (состояние pop_loose_modif в + * pop_map). Звать на ТЕКУЩЕЙ (back) странице из pop_loose_tick. + * shake_draw — дрожащий кадр (heal+SPRITE); bake_empty — запечь пустоту + * после падения (NORMAL, в ОЗУ-копию). */ +void pop_loose_shake_draw(int row, int col) __banked; +void pop_loose_bake_empty(int row, int col) __banked; + +/* Плита-ПОТОЛОК (loose ряда 2 комнаты СВЕРХУ, наш ряд −1): дрожащий кадр в + * полосе у потолка / «запечь» пустоту (колодец наверх) после провала. + * Состояние — pop_ceil_modif[10] в pop_map. */ +void pop_ceil_shake_draw(int col) __banked; +void pop_ceil_bake_empty(int col) __banked; + +/* Кусок приземлился (loose_land) — переехал в pop_state.h: переменную + * читают по обе стороны банковой границы. */ +#include "pop_state.h" + +/* Пики (spike) — перерисовка тайла (row,col) на back-странице по живому + * modif (pop_trob). Звать из pop_process_trobs. См. pop_bg.c. */ +void pop_spike_redraw(int row, int col) __banked; + +/* Чомпер — то же самое для его кадра анимации (все куски в СВОЕЙ ячейке, + * поэтому соседа перерисовывать не надо). */ +void pop_chomp_redraw(int row, int col) __banked; + +/* Вернуть ТОЛЬКО слой anim чомпера — его собственную графику поверх свежего + * пламени соседнего факела. Порт ветки redraw_frames_anim в redraw_needed + * (seg008:0211): оригинал на такую пометку делает ровно три вызова — + * draw_tile_anim_topright / draw_tile_anim_right / draw_tile_anim — и НЕ + * делает ни wipe, ни полного draw_tile (wipe у него отдельный счётчик). + * + * Из этих трёх нам нужен один: topright — про верх ВОРОТ под пустой + * клеткой, к чомперу не относится, а anim_right (пламя левого соседа) у нас + * уже нарисован — pop_torch_draw запекает его в фон ДО redraw_needed. */ +void pop_chomp_anim_draw(int row, int col) __banked; + +/* Анимация факела/зелья (динамика каждый кадр; фон запечён без них): + * modif — живой room_modif тайла. Для факела col — колонка САМОГО факела + * (пламя рисуется в ячейке правого соседа). */ +void pop_torch_draw(int row, int col, uint8_t modif) __banked; + +/* Стереть запечённое пламя факела с текущей draw-страницы (фон покоя). + * Нужно перевороту экрана: иначе отражение растаскивает языки огня по + * комнате — подробности в pop_room.c. */ +void pop_torch_wipe(int row, int col) __banked; +void pop_potion_draw(int row, int col, uint8_t modif) __banked; + +/* Ворота на ЛЕВОМ шве (col0) — перерисовать по живому modif соседа слева. + * g_lbg (lcol_bg) должен быть обновлён вызывающим. Звать из основного цикла + * на back-странице, когда сосед слева анимируется. См. pop_bg.c. */ +void pop_room_redraw_seam_left(uint8_t rows) __banked; /* rows = маска рядов 0..2 */ + +/* Перерисовать ПОЛ тайла (row,col) + правую грань в соседе на back-странице + * (wipe по площади пола, БЕЗ стены ряда ниже). Для смены тайла на месте: + * кнопка нажата/отжата, floor → debris от упавшей плиты. */ +void pop_floor_bake(int row, int col) __banked; +void pop_button_redraw(int row, int col) __banked; /* = pop_floor_bake (кнопка) */ + +/* Дверь уровня: перерисовать её ПРАВУЮ половину (row, col = правая) с + * запечкой — створка анимируется (pop_trob animate_leveldoor). */ +void pop_leveldoor_redraw(int row, int col) __banked; + +/* Ворота ВНУТРИ отрисованной комнаты: перерисовать по живому modif. + * (row,col) — координаты САМИХ ворот; бары рисует ячейка правого соседа + * (draw_tile, ветка lcode==4), туда и идёт wipe+redraw. Порт draw_trob + * (seg007:01E6), которым заканчивается animate_door. */ +void pop_gate_redraw(int row, int col) __banked; + +/* Падающий кусок loose (mob, seg007 move_loose): спавн при падении тайла + * (row,col) и покадровое продвижение+отрисовка на back-странице. tick + * зовётся каждый кадр (внутри pop_loose_tick); клип низа делает clip. */ +void pop_loose_mob_spawn(int row, int col) __banked; +/* То же, но в ЯВНО заданной комнате: плита, расшатанная Кидом и брошенная + * (он ушёл в соседнюю комнату), доваливается сама — см. animate_loose в + * pop_trob.c. Дальше кусок летит обычным путём: mob_tick_one умеет чужую + * комнату (mob_tile_at читает уровень, посадку сигналит pop_loose_exit). */ +void pop_loose_mob_spawn_at(uint8_t room, int row, int col) __banked; +void pop_loose_mob_tick(void) __banked; +void pop_loose_mob_reset(void) __banked; /* убрать все куски (СТАРТ УРОВНЯ, mobs_count=0) */ +void pop_loose_mob_qsave(pop_qs_io_t *io) __banked; + +/* Забыть, на какой странице сделана первая запечка тайлов (см. bake_copy в + * pop_room.c). Звать при смене комнаты/уровня: номера тайлов повторяются, и + * первый же тайл с тем же номером получил бы копию вместо честной запечки. */ +void pop_bake_forget(void) __banked; + +/* Передний слой полосы кладки у потолка (ряд −1) для одного столбца — зовёт + * проход по пометкам, ряд −1 в нём последний (порт redraw_needed_above + + * порядка redraw_needed_tiles, seg008:1B06). */ +void pop_ceil_fore_tile_b(int col) __banked; + +/* Оверлей тайла (row,col) ПОВЕРХ падающего куска, чей тайл — (oref_row, + * oref_col): порт set_redraw2 из draw_mob (seg007:1149). Порядок обхода + * проверяется внутри. */ +void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col, + int mx, int my, uint8_t mh) __banked; + +/* Смена комнаты: куски продолжают падать в СВОИХ комнатах (do_mobs крутит + * mobs[] независимо от drawn_room), сбрасывается только heal-история. */ +void pop_loose_mob_room_changed(void) __banked; + +/* Кусок НИЖЕ Kid по obj_y → рисуется ПОВЕРХ него (порт sort_curr_objs, + * seg008:1561). Звать после отрисовки персонажей, перед fore-слоем. */ +/* Отрисовка падающих кусков, идущих ПОД Kid. Звать ПОСЛЕ pop_redraw_needed() + * — фон кадра обязан быть готов до любого спрайта. */ +/* Передний слой ОДНОГО тайла — зовёт pop_fore_needed (pop_redraw.c). */ +void pop_fore_tile_b(int row, int col) __banked; +void pop_fore_pass_begin(void) __banked; +void pop_fore_pass_end(void) __banked; + +void pop_loose_mob_draw(void) __banked; +void pop_loose_mob_draw_over(void) __banked; + +/* Позиция куска (колонка, y низа) для check_loose_fall_on_kid. 0 — нет. */ +uint8_t pop_loose_mob_pos(int *col, int *y) __banked; + +/* Детектор выхода спрайта за поле: если спрайт [x,w] с ЭКРАННЫМ верхом + * sy_top и высотой h заходит ниже поля (статус-полоса/борт) или выше + * верхнего борта — взводит внутренний флаг «борта грязные» на 2 кадра. + * Звать СРАЗУ после блита падающего объекта (Kid/кусок). */ +void pop_clip_sprite(int x, int w, int sy_top, int h) __banked; + +/* Почистить борта (верхний + статус-полоса/нижний) — но ТОЛЬКО в кадрах, + * где кто-то реально вышел за поле (гейт по флагу от pop_clip_sprite): + * даром в покое, O(1) при падении (не зависит от числа спрайтов). Звать + * КАЖДЫЙ кадр ПОСЛЕ персонажей и fore на back-странице. */ +void pop_room_clip_borders(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_cdraw.c b/applications/SprPoP/src/pop_cdraw.c new file mode 100644 index 0000000..9f9ec6f --- /dev/null +++ b/applications/SprPoP/src/pop_cdraw.c @@ -0,0 +1,1058 @@ +/* + * pop_cdraw.c — ОТРИСОВКА ПЕРСОНАЖЕЙ (Кид, страж, скелет; дальше тень, + * визирь, толстяк) + полоса HP. Банк 4. + * + * Одно тело на всех Char — порт add_kid_to_objtable/add_guard_to_objtable + * (seg008:22F0/2324), различия сведены к слоту; мотивация и порядок вызовов + * в кадре — в шапке pop_cdraw.h. До DRAW-CHAR это были kid_draw в + * резиденте и pop_guard_draw здесь же, каждый со своей математикой кадра, + * своими heal-прямоугольниками и своим проходом окклюзии. + * + * Почему банк, а не резидент: зовёт только главный цикл (раз в кадр на + * персонажа), горячих банк→банк переходов отсюда нет, а резидентное + * пространство W1+W2 делят код и данные — килобайт кода, уехавший в банк, + * это килобайт для кучи. Писучие статики банкового модуля (--dataseg не + * задаём) остаются в обычном _DATA, то есть в W2: pop_cd читают и физика, и + * фон из СВОИХ банков — без трамплина, обычной адресацией. + */ +#include +#include +#include +#include "pop_arc.h" +#include "guard_arc.h" +#include "skel_arc.h" +#include "vizier_arc.h" +#include "pop_guard.h" +#include "pop_kid.h" +#include "_pop_kdraw.h" +#include "pop_vflip.h" /* зеркальные страницы атласов (зелье инверсии) */ /* меч + блит по id — сосед по банку 4 */ /* атласы/кадр Кида, pop_sword_draw, окна Char */ +#include "pop_cdraw.h" +#include "pop_status.h" +#include "pop_ui.h" /* POP_COL_OUTSIDE — фон вне игрового поля */ +#include "pop_shadow.h" /* запечённый набор Тени */ +#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */ +#include "pop_state.h" /* ВРЕМЕННО: зонды pop_dbg_b* */ +#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char, pop_fore_set_clip */ +#include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */ +#include "pop_map.h" /* hitp_curr/hitp_max — HP Кида; clip_char */ +#include "pop_level.h" /* тип соперника уровня (набор спрайтов) */ +#include "pop_tile.h" /* pop_heal_off — клип тени/отражения через фон */ +#include "kid_data.h" +#include "pop_guard_pal.h" /* таблица палитр стражей (сгенерена упаковщиком) */ + +/* Последний curr_guard_color обычного стража (0 — цвет ещё не выбирали). + * Нужен pop_guard_pal_apply, чтобы восстановить слоты после kid.pal. */ +static uint8_t guard_pal_cur; + +/* 16 слотов POP_GUARD_PAL_BASE в ОБЕ палитры дабл-буфера. + * Таблицы — rodata ЭТОГО модуля, то есть страница банка (0xC000+), а + * gfx_pal_load отдаёт указатель в BIOS ($A4 через rst #0x08). BIOS читает + * только #4000-#BFFF (см. корневой CLAUDE.md), поэтому копируем запись в + * локальный буфер — стек гарантированно в W2. Без копии BIOS забирал + * мусор с чужой страницы и страж становился невидимым. */ +static void pal_load16(const uint8_t *src) +{ + uint8_t buf[64]; + uint8_t i; + for (i = 0; i < 64; i++) buf[i] = src[i]; + gfx_pal_load(0, POP_GUARD_PAL_BASE, 16, buf); + gfx_pal_load(1, POP_GUARD_PAL_BASE, 16, buf); +} + +/* Цвет стража (set_chtab_palette, seg003:257). Оригинал держит ОДИН набор + * спрайтов и подменяет 16 цветов палитры chtab_5 по level.guards_color — + * так на уровне 2 стражи комнат 4/7/11/15/24 разного цвета (1/3/1/1/6). + * Полоса HP стража рисуется тем же атласом, поэтому меняется заодно. + * color = 0 — палитру не трогаем (оригинал в этом случае set_chtab_palette + * не зовёт: тип уровня не «обычный страж»). + * Грузим В ОБЕ палитры: у страниц дабл-буфера они раздельные. */ +void pop_guard_set_palette(uint8_t color) __banked +{ + if (color == 0 || color > POP_GUARD_NPAL) return; + guard_pal_cur = color; /* curr_guard_color, для re-apply */ + pal_load16(pop_guard_pal[color - 1]); +} + +/* Пере-применить палитру ТЕКУЩЕГО набора соперника — пара к + * pop_bg_pal_apply/pop_shadow_pal_apply. Слоты 0x90..0x9F динамические: + * общий kid.pal содержит все 256 записей и затирает их палитрой обычного + * стража (цвет 2, тот что запёк упаковщик). Поэтому КАЖДЫЙ file-load + * логической палитры обязан доплатить этот диапазон, иначе скелет (и + * Джафар) остаются в цвете стража: у скелета все пиксели индекса 3, а это + * слот 0x93 = розовый цвет стража — ровно то, что было видно на уровне 3. + * Звать из pop_pal_* ДО snapshot. */ +void pop_guard_pal_apply(void) __banked +{ + int8_t type = pop_level_guard_type(pop_current_level); + if (type == 2 || type == 3) { /* свой цвет — из файла набора */ + pal_load16((type == 2) ? pop_skel_pal : pop_vizier_pal); + } else if (type == 0 && guard_pal_cur != 0) { + pal_load16(pop_guard_pal[guard_pal_cur - 1]); + } +} + +/* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */ +#define GUARD_PAGES 5 +static atlas_t gp[GUARD_PAGES]; +static uint8_t g_ok; + +/* Состояние отрисовки по слотам (см. pop_cdraw.h): heal-прямоугольники по + * страницам дабл-буфера, габарит кадра и окно fore-клипа. */ +pop_cdraw_t pop_cd[POP_CH_N]; + +/* «Брызги» урона Кида — спрайт chtab_2 id 218 (res619.png, в наших атласах + * страница 27). У соперника это image 1 его собственного набора. */ +#define KID_SPLASH_IMAGE 218 + +/* Набор спрайтов соперника выбирает ТИП УРОВНЯ (load_lev_spr, seg000:1092): + * tbl_guard_type = {0 guard, 1 fat, 2 skeleton, 3 vizier, 4 shadow, −1 нет}. + * У нас пока два набора: обычный страж (уровни 1-2) и скелет (уровень 3); + * у скелета и кадров меньше (28 против 34), поэтому пятую страницу он не + * грузит. Уровень БЕЗ соперников (type < 0) не грузит ничего. */ +int pop_guard_load(void) __banked +{ + uint8_t tbl[ARC_GUARD_COUNT * POP_ARC_REC]; /* самый крупный из трёх */ + pop_arc_t arc; + int8_t type = pop_level_guard_type(pop_current_level); + uint8_t i, pages, count, id; + + if (type < 0) return 0; /* соперников на уровне нет */ + /* Наборы разложены по своим архивам, а не в один: имена внутри у них + * совпадают (g0.atl у всех троих), да и грузится всегда ровно один. */ + if (type == 2) { id = POP_ARC_SKEL; count = ARC_SKEL_COUNT; + pages = ARC_SKEL_COUNT; } + else if (type == 3) { id = POP_ARC_VIZIER; count = ARC_VIZIER_COUNT; + pages = ARC_VIZIER_COUNT; } + else { id = POP_ARC_GUARD; count = ARC_GUARD_COUNT; + pages = GUARD_PAGES; } + if (pages > GUARD_PAGES) pages = GUARD_PAGES; + if (pop_arc_open_id(&arc, id, tbl, count) < 0) return -1; + for (i = 0; i < pages; i++) + if (pop_arc_atlas(&arc, tbl, i, &gp[i]) != 0) { + pop_arc_close(&arc); + return -1; + } + pop_arc_close(&arc); + g_ok = 1; +/* Цвет: у скелета он СВОЙ (палитра из его набора), у стража подменяется + * из res10.bin по guards_color — это делает pop_guard_set_palette при + * входе в комнату (seg002:186: у не-guard curr_guard_color = 0). */ + pop_guard_pal_apply(); + return 0; +} + +/* Зеркальный набор соперника — пара к pop_guard_load: те же страницы, но + * файлы *_v.atl (см. pop_vflip.h). Здесь, а не в загрузчике набора, + * потому что gp[] и выбор GUARD/SKEL живут именно тут. */ +int pop_guard_vflip_load(void) __banked +{ + uint8_t tbl[ARC_GUARD_COUNT * POP_ARC_REC]; + pop_arc_t arc; + int8_t type = pop_level_guard_type(pop_current_level); + uint8_t i; + int res = 0; + + if (type < 0 || !g_ok) return 0; /* соперников на уровне нет */ + /* Зеркала есть ТОЛЬКО у обычного стража: зелье инверсии — уровень 9, а + * там tbl_guard_type = 0 (у скелета и Джафара своих *_v нет вовсе). */ + if (type != 0 && type != 1) return 0; + if (pop_arc_open_id(&arc, POP_ARC_GUARD, tbl, ARC_GUARD_COUNT) < 0) + return -1; + for (i = 0; i < GUARD_PAGES; i++) { + atlas_t m; + + if (pop_arc_atlas(&arc, tbl, (uint8_t)(ARC_GUARD_G0_V + i), &m) != 0 || + pop_vflip_take(&gp[i], &m) != 0) { + res = -1; + break; + } + } + pop_arc_close(&arc); + return res; +} + +void pop_guard_free(void) __banked +{ + uint8_t i; + if (!g_ok) return; + for (i = 0; i < GUARD_PAGES; i++) atlas_free(&gp[i]); + g_ok = 0; +} + +/* ---- calc_screen_x_coord (seg008:2602) БЕЗ деления ------------------- * + * + * Логическая X (280 шир = 28px/тайл) -> экранная (320 = 32px/тайл): ×8/7. + * SDLPoP пишет это как `logical_x*320/280`, у нас SDCC разворачивал в + * `__divsint` — 2 400 тактов на вызов, и зовётся оно на КАЖДОГО персонажа + * КАЖДЫЙ кадр (последнее деление, остававшееся в горячем пути). + * + * ХРАНИМ x/7 БАЙТОМ, а не готовое x*8/7 словом. Тождество: 8x/7 == x + x/7 + * (7x делится нацело, поэтому усечение обеих частей совпадает — и для + * отрицательных с трункейтом к нулю тоже). Оба варианта собраны и + * посчитаны по сгенерированному asm: + * + * int16_t готовое: ... add hl,hl / add hl,de / ld e,(hl) / inc hl / + * ld d,(hl) = 132 такта, 2 304 байта + * int8_t x/7: ... add hl,bc / ld a,(hl) / ld l,a / rlca / sbc a,a / + * ld h,a / add hl,de = 131 такт, 1 152 байта + * + * Расширение знака и 16-битное сложение стоят РОВНО столько же, сколько + * лишний `add hl,hl` при двухбайтном индексе, а чтение из памяти на одно + * меньше — под wait-state'ами Sprinter (такт ≈ 2,4× номинала на обращениях + * к ОЗУ) байтовый вариант даже чуть выгоднее номинала. Плюс килобайт + * банка, который в pop_cdraw ещё понадобится. + * + * ДИАПАЗОН — весь, а не «обычный случай». Отрицательные X реальны: + * obj_x = 2*fwd − 116 уходит в минус, как только fwd < 58 (левее + * x_bump[5]), то есть у левой кромки комнаты — это не экзотика. По + * фактическим данным кадров (kid.ani: dx Кида −5..+10, стража + * −2..+10) при x типа uint8_t и render_dx ∈ {−140, 0, +140} полный + * диапазон obj_x = −416..695; таблица кроет −448..703 с запасом. + * Деление ниже поэтому недостижимо и оставлено страховкой на случай + * третьего персонажа или другого render_dx. + * + * SCRX7[x + SCRX_BIAS] == x/7 (усечение к нулю, как в C и как у 8086 idiv + * в оригинале); экранная X = x + SCRX7[...]. */ +#define SCRX_BIAS 448 +#define SCRX_N 1152 +static const int8_t SCRX7[SCRX_N] = { + -64, -63, -63, -63, -63, -63, -63, -63, -62, -62, -62, -62, -62, -62, -62, -61, -61, -61, -61, -61, + -61, -61, -60, -60, -60, -60, -60, -60, -60, -59, -59, -59, -59, -59, -59, -59, -58, -58, -58, -58, + -58, -58, -58, -57, -57, -57, -57, -57, -57, -57, -56, -56, -56, -56, -56, -56, -56, -55, -55, -55, + -55, -55, -55, -55, -54, -54, -54, -54, -54, -54, -54, -53, -53, -53, -53, -53, -53, -53, -52, -52, + -52, -52, -52, -52, -52, -51, -51, -51, -51, -51, -51, -51, -50, -50, -50, -50, -50, -50, -50, -49, + -49, -49, -49, -49, -49, -49, -48, -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, -47, -47, -47, + -46, -46, -46, -46, -46, -46, -46, -45, -45, -45, -45, -45, -45, -45, -44, -44, -44, -44, -44, -44, + -44, -43, -43, -43, -43, -43, -43, -43, -42, -42, -42, -42, -42, -42, -42, -41, -41, -41, -41, -41, + -41, -41, -40, -40, -40, -40, -40, -40, -40, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -38, + -38, -38, -38, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -36, -36, -36, -36, -35, -35, -35, + -35, -35, -35, -35, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -33, -33, -33, -32, -32, + -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -30, -30, -29, + -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -28, -28, -28, -27, -27, -27, -27, -27, -27, -27, + -26, -26, -26, -26, -26, -26, -26, -25, -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -24, -24, + -24, -23, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -21, + -21, -21, -20, -20, -20, -20, -20, -20, -20, -19, -19, -19, -19, -19, -19, -19, -18, -18, -18, -18, + -18, -18, -18, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -16, -16, -15, -15, -15, + -15, -15, -15, -15, -14, -14, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -13, -13, -12, -12, + -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -10, -10, -9, + -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, + -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, + -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, + -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, + 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, + 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, + 18, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, + 21, 21, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, + 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, + 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, + 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, + 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, + 38, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, + 41, 41, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, + 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, + 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 50, 50, + 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 53, + 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, + 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, + 58, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, + 61, 61, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, + 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, + 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, 70, 70, + 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 73, + 73, 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, + 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, + 78, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, + 81, 81, 82, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, + 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, + 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 90, 90, + 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 93, + 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 95, + 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, + 98, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, +}; + +static int scr_x(int x) +{ + /* Индекс считаем в uint16_t ОТДЕЛЬНОЙ переменной, а не выражением внутри + * скобок: на `(const T*)КОНСТ + var*K` SDCC z80 умеет терять старший байт + * индекса (memory sdcc_z80_const_ptr_index_bug), а тут индекс как раз + * больше 255. */ + uint16_t i = (uint16_t)(x + SCRX_BIAS); + if (i < SCRX_N) return x + SCRX7[i]; + return x * 8 / 7; /* по данным кадров недостижимо */ +} + +/* Добавить накладной спрайт (клинок / брызги) в overlay-прямоугольник + * страницы. Оба мелкие и в одном кадре встречаются редко — их объединение + * дешевле третьего heal-слота. */ +static void cd_overlay_add(pop_cdraw_t *s, uint8_t dp, int x, int y, + uint8_t w, uint8_t h) +{ + if (!s->ovalid[dp]) { + s->ox[dp] = x; s->oy[dp] = y; s->ow[dp] = w; s->oh[dp] = h; + s->ovalid[dp] = 1; + return; + } + { + int x0 = s->ox[dp], y0 = s->oy[dp]; + int x1 = x0 + (int)s->ow[dp], y1 = y0 + (int)s->oh[dp]; + if (x < x0) x0 = x; + if (y < y0) y0 = y; + if (x + (int)w > x1) x1 = x + (int)w; + if (y + (int)h > y1) y1 = y + (int)h; + s->ox[dp] = x0; s->oy[dp] = y0; + s->ow[dp] = (uint8_t)(x1 - x0); s->oh[dp] = (uint8_t)(y1 - y0); + } +} + +/* Добавить прямоугольник в окно fore-клипа слота (объединение «спрайт + + * накладные»): по нему fore-проход возвращает куски тайлов и по нему же + * расширяет перебор колонок/рядов. */ +static void cd_clip_add(pop_cdraw_t *s, int x, int y, uint8_t w, uint8_t h) +{ + if (!s->cw) { + s->cx = x; s->cy = y; s->cw = w; s->ch = h; + return; + } + { + int x1 = s->cx + (int)s->cw, y1 = s->cy + (int)s->ch; + if (x < s->cx) s->cx = x; + if (y < s->cy) s->cy = y; + if (x + (int)w > x1) x1 = x + (int)w; + if (y + (int)h > y1) y1 = y + (int)h; + s->cw = (uint8_t)(x1 - s->cx); + s->ch = (uint8_t)(y1 - s->cy); + } +} + +/* Стирание прошлого кадра персонажа и его накладных. pop_heal_fast, а не + * голый gfx_heal: стираем ровно то, что рисовали, а рисовали в подавляющем + * большинстве кадров noclip-ядром; клипающий путь остаётся для straddle и + * выхода за поле. + * + * Тело — static: pop_char_draw зовёт его же «страховкой» (см. ниже), а + * трамплин выбирается ОБЪЯВЛЕНИЕМ, и вызов через `__banked`-имя стоил бы + * 654 такта внутри собственного банка (memory sdcc_banked_call_rules). */ +static void cd_heal(uint8_t who) +{ + pop_cdraw_t *s = &pop_cd[who]; + uint8_t p = gfx_get_draw_page() & 1; + if (s->valid[p]) pop_heal_fast(s->x[p], s->y[p] + POP_YOFF, s->w[p], s->h[p]); + if (s->ovalid[p]) pop_heal_fast(s->ox[p], s->oy[p] + POP_YOFF, s->ow[p], s->oh[p]); + s->valid[p] = s->ovalid[p] = 0; +} + +void pop_char_heal(uint8_t who) __banked { cd_heal(who); } + +/* ---- Пропуск неизменившегося кадра (DRAW-COST; контракт — pop_cdraw.h) -- */ + +/* Снимок ВХОДОВ отрисовки слота. Картинки (image) в снимке нет: её + * однозначно определяет пара (charid, frame), и это ГАРАНТИРУЕТ pop_load_frame + * в самой отрисовке — без него кадр приходил из кэша, который мог отстать, и + * снимок описывал не то, что нарисовано (BUG-SHADOW-STALE-FRAME, разбор в + * pop_char_draw). Тайлы вокруг персонажа (clip_char, fore-проход) в снимок + * тоже не входят — их изменение ловит pop_cd_dirty. */ +typedef struct { + uint8_t frame, x, y, action, sword, charid, room, hurt; + int8_t dir, ccol, crow; + int dx; /* render_dx: straddle-смещение */ +} cd_sig_t; + +static cd_sig_t cd_sig[POP_CH_N][2]; + +static void cd_sig_make(uint8_t who, cd_sig_t *g) +{ + const pop_char_t *ch = (who == POP_CH_KID) ? &Kid : &Guard; + g->frame = ch->frame; g->x = ch->x; g->y = ch->y; + g->action = ch->action; g->sword = ch->sword; g->charid = ch->charid; + g->room = ch->room; + g->hurt = (uint8_t)((who == POP_CH_KID) ? pop_kid_hurt : pop_guard_hurt); + g->dir = ch->direction; + g->ccol = ch->curr_col; g->crow = ch->curr_row; + g->dx = pop_cd[who].render_dx; +} + +/* Совпадает ли снимок страницы с ТЕКУЩИМ состоянием персонажа. + * + * Отдельно от cd_sig_make намеренно: тот СТРОИТ структуру, и для проверки + * это лишняя работа — тринадцать записей в стековый кадр (а значит через + * `-n(ix)`), после которых идёт побайтовый цикл сравнения. А зовут проверку + * четыре раза за кадр: pop_char_skip_mask дважды, и в ней по два слота. + * Здесь сравниваем поля прямо с источником, и первое же расхождение + * заканчивает работу — у ДВИГАЮЩЕГОСЯ персонажа это обычно первое поле. */ +static uint8_t cd_sig_same(uint8_t who, uint8_t p) +{ + const pop_char_t *ch = (who == POP_CH_KID) ? &Kid : &Guard; + const cd_sig_t *g = &cd_sig[who][p]; + if (g->frame != ch->frame) return 0; + if (g->x != ch->x) return 0; + if (g->y != ch->y) return 0; + if (g->dir != ch->direction) return 0; + if (g->action != ch->action) return 0; + if (g->ccol != ch->curr_col) return 0; + if (g->crow != ch->curr_row) return 0; + if (g->sword != ch->sword) return 0; + if (g->charid != ch->charid) return 0; + if (g->room != ch->room) return 0; + if (g->hurt != (uint8_t)((who == POP_CH_KID) ? pop_kid_hurt : pop_guard_hurt)) + return 0; + if (g->dx != pop_cd[who].render_dx) return 0; + return 1; +} + +/* Габарит слота на странице: спрайт + накладные (клинок/брызги). y — + * КОМНАТНЫЙ (как в pop_cd), перевод в экранный делает вызывающий. */ +static void cd_bbox(uint8_t who, uint8_t p, int *x0, int *y0, int *x1, int *y1) +{ + const pop_cdraw_t *s = &pop_cd[who]; + *x0 = s->x[p]; *y0 = s->y[p]; + *x1 = *x0 + (int)s->w[p]; *y1 = *y0 + (int)s->h[p]; + if (s->ovalid[p]) { + if (s->ox[p] < *x0) *x0 = s->ox[p]; + if (s->oy[p] < *y0) *y0 = s->oy[p]; + if (s->ox[p] + (int)s->ow[p] > *x1) *x1 = s->ox[p] + (int)s->ow[p]; + if (s->oy[p] + (int)s->oh[p] > *y1) *y1 = s->oy[p] + (int)s->oh[p]; + } +} + +/* ЗАБЫТЬ, ЧТО НАРИСОВАНО, на ОБЕИХ страницах. + * + * Звать после всего, что переписало экран мимо кадрового конвейера: меню, + * QuickLoad, переворот картинки. Иначе снимок слота продолжает утверждать, + * что нужный кадр персонажа на странице уже есть, и cd_quiet честно + * пропускает отрисовку — персонаж исчезает до первого своего движения + * (поймано пользователем на выходе из меню; рядом с факелом бага не видно + * ровно потому, что анимация фона метит прямоугольник Кида и пропуск + * снимается сама собой). */ +void pop_cd_forget(void) __banked +{ + uint8_t i; + + for (i = 0; i < POP_CH_N; i++) { + pop_cd[i].valid[0] = pop_cd[i].valid[1] = 0; + pop_cd[i].ovalid[0] = pop_cd[i].ovalid[1] = 0; + } +} + +/* «Тихий» слот: спрайт этой страницы цел (heal его не трогал), фон в его + * прямоугольнике не трогали, и все входы отрисовки те же, что в момент, + * когда этот кадр на эту страницу и рисовали. valid[p] здесь ключевой: он + * снимается heal'ом, поэтому решение остаётся согласованным, сколько раз его + * ни перепроверяй за кадр. */ +static uint8_t cd_quiet(uint8_t who, uint8_t p) +{ + /* Соперника на сцене нет и на ЭТОЙ странице от него ничего не осталось — + * стирать и рисовать нечего, слот «тихий». Без этого пустой слот каждый + * кадр честно проходил heal + вход в pop_char_draw + fore-проход и стоил + * ~8 200 тактов из ничего (замер MAME, комната без стража). */ + if (who == POP_CH_OPP && Guard.charid == 0 && + !pop_cd[who].valid[p] && !pop_cd[who].ovalid[p]) + return 1; + if (!pop_cd[who].valid[p]) return 0; + if (pop_cd_dirty & (1 << p)) { /* фон правили — но задели ли нас? */ + /* СПРАЙТ И НАКЛАДНОЙ (клинок, брызги) — ДВУМЯ ОТДЕЛЬНЫМИ проверками, + * а не объединённым bbox. Объединение включает пустой угол между + * ними, и он ловит касания, которых на самом деле нет: у стоящего + * стража спрайт лежит в колонке 8, клинок уходит влево в колонку 7 на + * y 59..65, а пламя факела метит колонку 7 на y 33..50. Прямоугольник + * «спрайт + клинок» (x 241..284, y 46..84) пересекается с этой меткой + * углом — и персонаж перерисовывался целиком за 148 302 такта + * (замер 2026-08-19, сцена 11/15). + * + * Для skip_mask объединение по-прежнему годится: там вопрос «рядом ли + * два слота», и загрубление в бОльшую сторону безопасно. */ + if (pop_cd_hit_slot(who, p)) return 0; + } + return cd_sig_same(who, p); +} + +/* Запас вокруг габарита соседа: за кадр персонаж проходит заметно меньше + * (бег — ~11 экранных px), а прыжок с падением укладывается сюда же. */ +#define CD_MARGIN 32 + +uint8_t pop_char_skip_mask(void) __banked +{ + uint8_t p = gfx_get_draw_page() & 1; + uint8_t q0 = cd_quiet(POP_CH_KID, p); + uint8_t q1 = cd_quiet(POP_CH_OPP, p); + if (q0 && q1) return 3; /* оба стоят — пересечься нечему */ + if (q0 || q1) { + /* Один перерисовывается: его heal (и fore-проход по его окну) может + * задеть «тихого» соседа и выесть из него кусок. Прямоугольники + * рядом — перерисовываем ОБОИХ. */ + uint8_t st = (uint8_t)(q0 ? POP_CH_KID : POP_CH_OPP); + uint8_t mv = (uint8_t)(st ^ 1); + /* valid[st] тоже: «тихий» слот, в котором на этой странице НИЧЕГО не + * нарисовано (соперника на сцене нет), выесть невозможно, а bbox у + * него мусорный — правило соседства к нему неприменимо. */ + if (pop_cd[st].valid[p] && pop_cd[mv].valid[p]) { + int ax0, ay0, ax1, ay1, bx0, by0, bx1, by1; + cd_bbox(st, p, &ax0, &ay0, &ax1, &ay1); + cd_bbox(mv, p, &bx0, &by0, &bx1, &by1); + if (ax0 < bx1 + CD_MARGIN && bx0 < ax1 + CD_MARGIN && + ay0 < by1 + CD_MARGIN && by0 < ay1 + CD_MARGIN) return 0; + } + } + return (uint8_t)((q0 ? 1 : 0) | (q1 ? 2 : 0)); +} + +void pop_char_set_render_dx(uint8_t who, int dx) __banked +{ + pop_cd[who].render_dx = dx; +} + +/* Брызги урона (draw_hurt_splash, seg006:2003) — ОДИН порт на всех: у Кида + * это image 218 своего chtab_2, у соперника image 1 его chtab_5 (у + * add_midtable аргумент `obj_id + 1`, а get_image вычитает единицу обратно + * — тот же off-by-one, что расписан в шапке pop_pack_guard.py). Подъём: + * `((charid == kid) << 2) + 11`, то есть 15 у Кида и 11 у остальных. + * + * Рисуются МЕЖДУ персонажем и клинком: draw_kid/draw_guard кладут их в + * objtable сразу после персонажа и до клинка (seg008:22A2/22C9), а порядок + * записей и есть порядок рисования. */ +static void cd_splash(pop_cdraw_t *s, uint8_t who, const atlas_t *pages, + int fp_x, int obj_y) +{ + const atlas_t *ap; + uint8_t aidx; + const uint8_t *img; + uint8_t w, h; + int qx = fp_x, qy = obj_y; + int fw = (Char.direction < 0) ? -5 : 5; /* obj_dx_forward(5) */ + + if (Char.frame == 178) return; /* chomped: брызг нет */ + if (who == POP_CH_KID) { + if ((KID_SPLASH_IMAGE >> 3) >= kid_npages) return; /* страница не загружена */ + ap = &pages[KID_SPLASH_IMAGE >> 3]; aidx = KID_SPLASH_IMAGE & 7; + } else { + ap = &pages[0]; aidx = 1; + } + if (Char.frame == 185 || /* мёртв */ + (Char.frame >= 106 && Char.frame < 111)) { /* падение+посадка */ + qy += 4; + } else if (Char.frame == 177) { /* напоролся на пики */ + fw = -fw; /* obj_dx_forward(-5) */ + } else { + qy -= (who == POP_CH_KID) ? 15 : 11; + } + qx = scr_x(qx + fw); /* calc_screen_x_coord */ + img = (const uint8_t *)atlas_image(ap, aidx); + gfx_w0_map(ap->page); + /* Габарит БАЙТАМИ: спрайты персонажей и накладных не крупнее 64x64 + * (memory pop_sprite_size_limits — весь игровой кадр PoP <= 56x63), + * а 16-битные w/h заставляли SDCC держать их в стековом кадре и + * считать каждое сравнение парой загрузок. */ + w = img[0]; + h = img[2]; + if (w && h) { + int top = qy - (int)h + 1; + uint8_t flip = (uint8_t)(Char.direction >= 0); + int skip = 0, rows = (int)h; /* поле комнаты 0..192 */ + uint8_t dp; + if (flip) qx -= (int)w; + if (top < 0) { skip = -top; rows -= skip; top = 0; } + if (top + rows > 192) rows = 192 - top; + if (rows <= 0) return; + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + if (pop_onscreen_cols(qx, top + POP_YOFF, w, (uint8_t)rows)) + gfx_blit_cols_part_noclip(qx, top + POP_YOFF, img, flip, + (uint8_t)skip, (uint8_t)rows); + else + gfx_blit_cols_part(qx, top + POP_YOFF, img, flip, skip, rows); + gfx_set_bank(GFX_BANK_NORMAL); + dp = gfx_get_draw_page() & 1; + cd_overlay_add(s, dp, qx, top, w, (uint8_t)rows); + cd_clip_add(s, qx, top, w, (uint8_t)rows); + } +} + +void pop_char_draw(uint8_t who) __banked +{ + pop_cdraw_t *s = &pop_cd[who]; + const pop_char_t *ch; + const kframe *fr; + const atlas_t *pages; + /* Атлас БРЫЗГ отдельно от кадра: у оригинала они всегда из «своего» + * chtab (Кид — chtab_2 image 218, прочие — chtab_5 image 1, + * seg006:2019), а у Тени кадр может прийти из другой половины набора. */ + const atlas_t *spl; + const uint8_t *img; + uint8_t npages, page, idx, dp, flip, hurt, lskip; + uint8_t w, h, vis_w; + int obj_x, obj_y, fp_x, fwd, top, bx, skip, rows, ct, cr; + int bcut; /* срез СНИЗУ (clip_char в перевёрнутом виде) */ + + /* Страховка пропуска (DRAW-COST): главный цикл мог решить, что слот + * «тихий», и heal не звать — а тик после этого персонажа всё-таки + * сдвинул. Здесь прошлый кадр уже точно надо стереть. Если heal в этом + * кадре был, вызов бесплатен: valid/ovalid уже сняты. */ + cd_heal(who); + + s->cw = 0; /* окно fore-клипа: пусто, пока не нарисовали */ + s->fpw = 0; /* габарит кадра: нечего перекрывать */ + + /* Окно Char (loadkid/loadshad) — как в оригинале: clip_char, футпринт и + * брызги работают с АКТИВНЫМ персонажем, а к этому моменту в Char лежит + * тот, кто тикал последним. Выгружать назад нечего: отрисовка только + * читает. */ + if (who == POP_CH_KID) { + pop_loadkid(); + ch = &Kid; fr = &kid_frame; pages = kidp; npages = kid_npages; + spl = kidp; + hurt = pop_kid_hurt; + } else { + pop_loadshad(); + ch = &Guard; fr = &pop_gframe; pages = gp; + npages = g_ok ? GUARD_PAGES : 0; + spl = gp; + hurt = pop_guard_hurt; + if (Guard.charid == 0) return; /* соперника на сцене нет */ + /* Набор атласов обязан идти за ТАБЛИЦЕЙ КАДРОВ — условие берём из + * того же места, что load_frame (seg006:0532), чтобы они не + * разъехались: вне боевых кадров 150..189 Тень ходит по таблице + * КИДА, и `image` из неё индексирует спрайты Кида. */ + if (Guard.charid == CHARID_1_SHADOW && pop_shadow_kid_n) { + /* У Тени СВОЙ запечённый набор (pop_shadow.h): её вид даёт + * наложение спрайта на себя со сдвигом, а пакетный блит так не + * умеет. Обе половины лежат в нём же, поэтому подмены на kidp + * здесь уже не нужно. */ + if (pop_frame_tbl_is_guard(Guard.charid, Guard.frame)) { + pages = pop_shadow_fgt; npages = pop_shadow_fgt_n; + } else { + pages = pop_shadow_kid; npages = pop_shadow_kid_n; + } + spl = pop_shadow_fgt; /* брызги — из половины chtab_5 */ + } else if (!pop_frame_tbl_is_guard(Guard.charid, Guard.frame)) { + pages = kidp; npages = kid_npages; + } + } + /* load_fram_det_col (seg008:22F5 / 2329) — кадр грузит САМА отрисовка, + * сразу после loadkid/loadshad. У нас его не было: kid_frame/pop_gframe + * считались кэшем, который наполняет тик (play_seq -> load_frame), а + * отрисовка берёт готовое. Кэш лагает — тот, кто пишет Char.frame МИМО + * play_seq, кадр не перезагружает, и рисуется прошлая картинка: + * + * do_init_shad (guards.c) кладёт тени frame = 15 (стойка) прямой + * записью, а pop_gframe в этот момент держит кадр 185 УБИТОГО СТРАЖА + * с прошлого уровня (после его смерти pop_guard_tick выходит по + * `charid == 0` РАНЬШЕ pop_load_fram_det_col, и кэш не трогается + * вообще). В кадре входа в комнату тень рисуется картинкой 33 + * (34x38, dx 3, dy 4) вместо 12x41 — да ещё из атласа КИДА, потому что + * набор выбирается по Guard.frame, а image приходит из кэша. + * + * Само по себе это моргнуло бы один кадр. Смертельным его делает + * пропуск неизменившегося кадра: снимок пишется по Guard.frame, то есть + * страница запоминает «нарисован кадр 15», хотя на ней чужой спрайт. + * Тень дальше стоит, снимок совпадает — страница НИКОГДА не + * перерисовывается, и дабл-буфер вечно мигает двумя разными позами + * (BUG-SHADOW-STALE-FRAME, уровень 6 комната 1). + * + * Лечим корень, а не симптом: после этой строки image ОДНОЗНАЧНО + * определяется парой (charid, frame), и снимок cd_sig снова описывает + * ровно то, что нарисовано. determine_col из связки НЕ берём: он пишет + * Char.curr_col, которую тут же читает clip_char, а физика колонку ведёт + * сама (расхождение с оригиналом — ../docs/impl_diff.md). */ + pop_load_frame(); + if (fr->image == 255) return; /* пустой кадр */ + + /* load_frame_to_obj (seg008:1728): НИЗ спрайта на obj_y, левый край на + * obj_x. +render_dx — straddle-смещение (∓140), когда комната персонажа + * не совпадает с отрисованной: спрайт за кромкой. Коллизия НЕ смещается + * — она в кадре своей комнаты, смещение чисто рендерное. */ + fwd = (int)ch->x + ((ch->direction < 0) ? -fr->dx : fr->dx) + s->render_dx; + obj_x = (fwd << 1) - 116; + obj_y = (int)ch->y + fr->dy; + if ((int8_t)(fr->flags ^ ch->direction) >= 0) + obj_x++; /* even/odd pixel (в ЛОГИЧЕСКОЙ X) */ + fp_x = obj_x; /* ЛОГИЧЕСКАЯ X — для футпринта */ + /* Позицию кадра публикуем СРАЗУ: её читает физика (check_spike_below — + * char_x_left активного персонажа), и она обязана обновиться даже в + * кадре, который рисовать нечем. Размер (fpw) — ниже, по факту кадра: + * он же служит признаком «есть что перекрывать» для fore-прохода. */ + s->fpx = fp_x; s->fpy = obj_y; + /* seg008 calc_screen_x_coord: логическая X (280 шир = 28px/тайл) -> + * экран (320 шир = 32px/тайл), ×320/280 = ×8/7. БЕЗ этого персонаж + * уезжает влево от тайлов тем сильнее, чем правее. Через scr_x — + * таблицей, без __divsint (см. шапку DIV7). */ + obj_x = scr_x(obj_x); + + page = (uint8_t)(fr->image >> 3); + idx = (uint8_t)(fr->image & 7); + if (page >= npages) return; /* страница набора не загружена */ + img = (const uint8_t *)atlas_image(&pages[page], idx); + /* ПЕРЕВОРОТ (зелье инверсии): тот же атлас, но страница с вертикально + * зеркальными пикселями — смещения лент и размеры кадров у копии те же, + * поэтому меняется ровно номер страницы (pop_vflip.h). Если EMM не + * дала страницу — рисуем обычной: неперевёрнутый кадр лучше дыры. */ + { + uint8_t vpg = pages[page].page; + if (pop_upside) { + uint8_t f = pop_vflip_page(vpg); + if (f) vpg = f; + } + gfx_w0_map(vpg); + } + /* Габарит БАЙТАМИ: спрайты персонажей и накладных не крупнее 64x64 + * (memory pop_sprite_size_limits — весь игровой кадр PoP <= 56x63), + * а 16-битные w/h заставляли SDCC держать их в стековом кадре и + * считать каждое сравнение парой загрузок. */ + w = img[0]; + h = img[2]; + dp = gfx_get_draw_page() & 1; + if (w && h) { + /* Спрайты нарисованы ЛИЦОМ ВЛЕВО (как в оригинале); seg008:864 — + * флип, когда персонаж смотрит ВПРАВО, и тогда xpos -= image->w + * (draw_mid). Флип column-major бесплатен, но зеркалит ВНУТРИ + * [x,x+w), поэтому при флипе подаём x−w. */ + flip = (uint8_t)(ch->direction >= 0); + bx = obj_x; + if (flip) bx -= (int)w; + top = obj_y - (int)h + 1; + /* Поле переворачивается вокруг своей середины: строка t уходит в + * (POP_PLAYFIELD_H - 1 - t), значит верх кадра [top, top+h) — + * в 192 - top - h, что при top = obj_y-h+1 даёт ровно 191 - obj_y. + * Пиксели уже зеркальны (страница выше), поэтому дальше всё + * считается как обычно: «пропустить skip строк сверху» у зеркальной + * ленты означает «срезать снизу» у оригинала — ровно то, что нужно. */ + if (pop_upside) top = (POP_PLAYFIELD_H - 1) - obj_y; + vis_w = w; + lskip = 0; + skip = 0; + bcut = 0; + /* clip_char (seg006:1231/1749): тайл над головой — стена/пол → всё + * выше линии полосы своего ряда НЕ рисуется, иначе персонаж + * просвечивает сквозь щели кладки при спуске/висе. */ + /* clip_char (seg006:1231/1749): тайл над головой — стена/пол → всё + * выше линии полосы своего ряда НЕ рисуется. Граница приходит в + * ЛОГИЧЕСКИХ координатах поля (y_clip), и при перевороте её нельзя + * сравнивать с уже отражённым top: то, что логически «выше линии», + * на экране оказывается НИЖЕ неё. Поэтому в перевороте тот же + * срез применяется с другого конца — укорачивает кадр снизу + * (bcut), а верх (top) остаётся на месте. Строки ленты при этом + * не сдвигаются: зеркальная лента отдаёт их в обратном порядке, + * так что «убрать последние bcut строк вывода» = «не рисовать + * первые bcut строк оригинала», а это ровно то, что просит + * clip_char. */ + ct = pop_clip_char_top(fp_x, obj_y, w, h); + if (!pop_upside) { + if (ct > top) { + skip = ct - top; + if (skip > (int)h) skip = (int)h; /* обрезан целиком */ + top = ct; + } + } else { + int top_log = obj_y - (int)h + 1; /* верх кадра ДО отражения */ + if (ct > top_log) { + bcut = ct - top_log; + if (bcut > (int)h) bcut = (int)h; + } + } + /* Вторая половина clip_char: правый косяк двери уровня. Дверь + * рисуется ПОД персонажем (вся в backtable), поэтому спрятать + * поднимающегося по лестнице можно только срезав спрайт. */ + cr = pop_clip_char_right(); + if (cr) { + int avail = cr - bx; + if (avail <= 0) vis_w = 0; /* весь спрайт за косяком */ + else if (avail < (int)w) vis_w = (uint8_t)avail; + } + /* КЛИП ТЕНИ СЛЕВА (seg008:1699): на уровне зеркала она может + * показываться ТОЛЬКО СПРАВА от него — + * obj_clip_left = 137 + (mirror_column-4)*32. */ + if (ch->charid == CHARID_1_SHADOW && + pop_current_level == MIRROR_LEVEL && ch->room == MIRROR_ROOM) { + int cl = 137 + (MIRROR_COL - 4) * 32; + if (bx < cl) { + int d = cl - bx; + if (d >= (int)vis_w) vis_w = 0; /* вся тень левее линии */ + else lskip = (uint8_t)d; + } + } + /* Поле комнаты — 192 строки (reset_obj_clip, seg006:0507: + * obj_clip_top = 0, obj_clip_bottom = 192). Персонаж, вышедший за + * него (скелет, падающий в пропасть), обязан обрезаться: ниже поля + * идёт полоса HP, и без клипа спрайт лез прямо на неё. */ + rows = (int)h - skip - bcut; + if (top < 0) { skip += -top; rows += top; top = 0; } + if (top + rows > 192) rows = 192 - top; + gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, без теневой копии */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + if (rows > 0 && vis_w) { /* +YOFF: центрирование */ + /* noclip-ядро ширину не ограничивает, поэтому при обрезке справа + * идём общим путём: это редкие кадры (подъём в двери уровня раз + * за уровень), цена подготовки там не видна. */ + if (lskip) /* тень у зеркала: срез слева */ + gfx_blit_cols_part_wx(bx, top + POP_YOFF, img, flip, skip, rows, + lskip, (uint8_t)(vis_w - lskip)); + else if (vis_w == w && pop_onscreen_cols(bx, top + POP_YOFF, w, (uint8_t)rows)) + gfx_blit_cols_part_noclip(bx, top + POP_YOFF, img, flip, + (uint8_t)skip, (uint8_t)rows); + else + gfx_blit_cols_part_w(bx, top + POP_YOFF, img, flip, skip, rows, + (uint8_t)(vis_w == w ? 0 : vis_w)); + } else rows = 0; + gfx_set_bank(GFX_BANK_NORMAL); + /* heal чистит ТОЛЬКО нарисованное: иначе стирается кромка пола над + * срезом (мусор/дыра в кладке на второй странице дабл-буфера). */ + s->x[dp] = bx + lskip; s->y[dp] = top; + s->w[dp] = (uint8_t)(vis_w - lskip); s->h[dp] = (uint8_t)rows; + s->valid[dp] = (uint8_t)(rows != 0); + if (rows) cd_clip_add(s, bx, top, vis_w, (uint8_t)rows); + s->fpw = w; s->fph = h; /* габарит КАДРА (не обрезанный): */ + /* по нему считается футпринт */ + } + pop_dbg_m15(); /* ЗАМЕР: снимок прямоугольника сделан */ + if (hurt) cd_splash(s, who, spl, fp_x, obj_y); + /* add_sword_to_objtable (seg006:1798): клинок отдельным спрайтом поверх + * персонажа — со своим прямоугольником heal. */ + { + int sx, sy; uint16_t sw, sh; + if (pop_sword_draw(ch, fr, obj_x, obj_y, &sx, &sy, &sw, &sh)) { + cd_overlay_add(s, dp, sx, sy, sw, sh); + cd_clip_add(s, sx, sy, sw, sh); + } + } + pop_dbg_m16(); /* ЗАМЕР: splash + клинок сделаны */ + gfx_w0_unmap(); + /* Что именно нарисовано на ЭТОЙ странице — снимок для пропуска + * следующих кадров (DRAW-COST). Только если кадр РЕАЛЬНО рисовали: + * при нулевом спрайте (`w && h` ложно — пустая запись атласа) блок выше + * не выполняется, и x/y/w/h/valid остаются от ПРОШЛОГО кадра этой + * страницы. Снимок же брался всегда — и слот залипал: cd_quiet видел + * valid = 1 и совпавшую сигнатуру, то есть считал «на странице + * нарисовано ровно то, что нужно», а на ней стоял старый спрайт. Так + * тень оставалась на двух страницах дабл-буфера в РАЗНЫХ позах и мигала + * между ними (BUG-CHAR-STALE-PAGE). Без снимка слот просто не будет + * «тихим», и следующий кадр начнётся с heal — старое сотрётся. */ + if (w && h) cd_sig_make(who, &cd_sig[who][dp]); +} + +void pop_char_fore(uint8_t who) __banked +{ + pop_cdraw_t *s = &pop_cd[who]; + /* Окно fore-клипа — одно на всех, и ставит его КАЖДЫЙ, кто рисует + * персонажа. Пока соперник рисовался строго ДО Кида, к моменту прохода + * в окне оказывался Кид и всё сходилось само; как только порядок стал + * переменным (кто ниже — тот поверх), при «соперник поверх» окно + * оставалось ЕГО, и fore-проход Кида отсекался целиком — Кид рисовался + * поверх передних столбов. Поэтому окно ставится здесь, а не держится + * «по счастливому порядку вызовов». */ + pop_fore_set_clip(s->cx, s->cy + POP_YOFF, (int)s->cw, (int)s->ch); + if (s->fpw) + pop_fore_over_char(who == POP_CH_KID ? &Kid : &Guard, + s->fpx, s->fpy, s->fpw, s->fph); + pop_dbg_b6(); /* ЗАМЕР: pop_char_fore целиком */ +} + +/* ---- ОТРАЖЕНИЕ В ЗЕРКАЛЕ (check_mirror, seg003:0798) ---------------- * + * + * Пока Кид стоит на тайле зеркала, оригинал каждый кадр рисует его + * зеркальную копию. ОТДЕЛЬНОЙ функцией, а не третьим слотом pop_cd — и это + * не экономия, а структура САМОГО оригинала: отражение идёт сокращённым + * путём `load_frame_to_obj(); reset_obj_clip(); add_objtable(4)`, а НЕ через + * add_kid_to_objtable. У него нет ни клинка, ни брызг, ни fore-прохода, ни + * пропуска неизменившегося кадра — гейтить всё это в общем теле значило бы + * добавить ветки в самый горячий путь ради двух секунд за игру. + * + * Char к моменту вызова уже ОТРАЖЁН вызывающим (pop_check_mirror -> + * mirror_image), поэтому здесь никакого «зеркалим» нет — обычная математика + * кадра, та же, что в pop_char_draw (см. там load_frame_to_obj, seg008:1728; + * шесть строк намеренно продублированы, а не вынесены: трогать горячее тело + * ради холодного вызова дороже, чем держать их синхронными по комментарию). + */ +static int mi_x[2], mi_y[2]; +static uint16_t mi_w[2], mi_h[2]; +static uint8_t mi_valid[2]; + +void pop_mirror_heal(void) __banked +{ + uint8_t p = gfx_get_draw_page() & 1; + if (!mi_valid[p]) return; + pop_heal_fast(mi_x[p], mi_y[p] + POP_YOFF, mi_w[p], mi_h[p]); + mi_valid[p] = 0; +} + +void pop_mirror_draw(int clip_top) __banked +{ + uint8_t dp = gfx_get_draw_page() & 1; + const kframe *fr = &kid_frame; + const uint8_t *img; + uint8_t page, idx, flip; + uint8_t w, h, vis_w; + int fwd, obj_x, obj_y, fp_x, bx, top, skip = 0, rows, cl; + uint8_t lskip = 0; + + if (fr->image == 255) return; + page = (uint8_t)(fr->image >> 3); + idx = (uint8_t)(fr->image & 7); + if (page >= kid_npages) return; + + fwd = (int)Char.x + ((Char.direction < 0) ? -fr->dx : fr->dx); + obj_x = (fwd << 1) - 116; + obj_y = (int)Char.y + fr->dy; + if ((int8_t)(fr->flags ^ Char.direction) >= 0) obj_x++; + fp_x = obj_x; /* ЛОГИЧЕСКАЯ X — её ждёт fore-проход */ + obj_x = scr_x(obj_x); + + img = (const uint8_t *)atlas_image(&kidp[page], idx); + gfx_w0_map(kidp[page].page); + /* Габарит БАЙТАМИ: спрайты персонажей и накладных не крупнее 64x64 + * (memory pop_sprite_size_limits — весь игровой кадр PoP <= 56x63), + * а 16-битные w/h заставляли SDCC держать их в стековом кадре и + * считать каждое сравнение парой загрузок. */ + w = img[0]; + h = img[2]; + if (!w || !h) { gfx_w0_unmap(); return; } + flip = (uint8_t)(Char.direction >= 0); + bx = flip ? obj_x - (int)w : obj_x; + top = obj_y - (int)h + 1; + + /* Клип СВЕРХУ — y_clip[curr_row+1] оригинала: отражение не должно + * вылезать из проёма зеркала вверх. Значение считает вызывающий + * (таблица y_clip живёт в pop_map). */ + if (clip_top > top) { skip = clip_top - top; top = clip_top; } + rows = (int)h - skip; + if (top < 0) { skip += -top; rows += top; top = 0; } + if (top + rows > 192) rows = 192 - top; + vis_w = w; + if (rows <= 0) { gfx_w0_unmap(); return; } + + /* Клип СЛЕВА — obj_clip_left = (curr_col<<5)+9: отражение видно только + * внутри проёма зеркала. Настоящей обрезкой колонок + * (gfx_blit_cols_part_wx), а не «нарисовать и вернуть фон»: для + * column-major левая обрезка стоит столько же, сколько правая. */ + cl = ((int)Char.curr_col << 5) + 9; + if (bx < cl) { + int d = cl - bx; + if (d >= (int)vis_w) { gfx_w0_unmap(); return; } /* целиком за краем */ + lskip = (uint8_t)d; + } + + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + if (lskip) + gfx_blit_cols_part_wx(bx, top + POP_YOFF, img, flip, skip, rows, + lskip, (uint8_t)(vis_w - lskip)); + else if (pop_onscreen_cols(bx, top + POP_YOFF, vis_w, (uint16_t)rows)) + gfx_blit_cols_part_noclip(bx, top + POP_YOFF, img, flip, + (uint8_t)skip, (uint8_t)rows); + else + gfx_blit_cols_part_w(bx, top + POP_YOFF, img, flip, skip, rows, 0); + gfx_set_bank(GFX_BANK_NORMAL); + gfx_w0_unmap(); + + mi_x[dp] = bx + lskip; mi_y[dp] = top; + mi_w[dp] = (uint16_t)(vis_w - lskip); mi_h[dp] = (uint16_t)rows; + mi_valid[dp] = 1; + + /* FORE-ПРОХОД ПОВЕРХ ОТРАЖЕНИЯ — обязателен, и без него картинка неверна. + * В оригинале отражение уходит в objtable (add_objtable(4)), а дальше его + * накрывает передний слой тайлов на общих основаниях; у зеркала это + * fore_id 77 — ПЕРЕДНЯЯ ЧАСТЬ АРКИ. Именно она прячет то, что вылезло из + * проёма: ноги ниже нижней кромки и голову с руками выше верхней (видно + * на прыжке). Клип obj_clip_top этого НЕ делает — при curr_row = 0 он + * равен y_clip[1] = 3, то есть почти не режет; работу выполняет фон. + * + * Мы рисуем отражение мимо конвейера персонажей, поэтому проход надо + * позвать руками — ровно как это делает pop_char_fore для слотов. */ + pop_fore_set_clip(bx + lskip, top + POP_YOFF, + (int)(vis_w - lskip), rows); + pop_fore_over_char(&Char, fp_x, obj_y, w, h); +} + +/* ---- Индикаторы HP (порт draw_kid_hp / draw_guard_hp, seg000:1416) --- * + * Оригинал рисует их в статус-полосе на y = 194 (ниже поля тайлов, внутри + * тех же 200 строк экрана PoP): HP Кида слева направо шагом 7, HP стража + * справа налево от x = 314. Пустые деления у Кида — свой спрайт (chtab_2 + * id 217); у стража оригинал печатает тот же спрайт ЧЁРНЫМ силуэтом, а мы + * просто не печатаем — полоса и так на чёрном фоне. + * + * Спрайты Кида (216/217) лежат в его же атласах, поэтому блитим через + * pop_kid_img_blit; спрайт стража — image 0 его chtab (наша страница 0). */ +#define HP_Y POP_HP_Y /* полосу делит UI: см. pop_cdraw.h */ +#define HP_STEP 7 +#define HP_KID_FULL 216 +#define HP_KID_EMPTY 217 +#define HP_MAXDRAW 10 /* больше в полосу всё равно не влезает */ + +/* Полоса перерисовывается ТОЛЬКО когда числа изменились — и тогда на ОБЕИХ + * страницах дабл-буфера (иначе на второй останется старое значение и его + * будет видно как мерцание через кадр). Отсюда счётчик страниц, а не флаг. + * + * Снимки прошлых значений пишем ДО сравнения: `if (n != s) s = n;` SDCC z80 + * собирает неверно — SUB затирает A, и в s уезжает разность (memory + * sdcc_z80_cmp_store_a_bug). */ +static uint8_t hp_todo; /* сколько страниц ещё обновить */ +static uint8_t hp_kid_prev, hp_kidmax_prev, hp_gd_prev, hp_gdmax_prev; + +/* Заставить перерисовать полосу на обеих страницах: звать при входе в + * комнату (фон перерисован целиком и стёр её) и при старте. */ +void pop_hp_invalidate(void) __banked { hp_todo = 2; } + +void pop_hp_draw(void) __banked +{ + uint8_t i, n; + uint8_t gd = (uint8_t)(Guard.charid ? guardhp_curr : 0); + uint8_t gm = (uint8_t)(Guard.charid ? guardhp_max : 0); + + { /* Изменилось хоть одно число — обновить обе страницы. */ + uint8_t k = hitp_curr, km = hitp_max; + uint8_t changed = (uint8_t)(k != hp_kid_prev || km != hp_kidmax_prev || + gd != hp_gd_prev || gm != hp_gdmax_prev); + hp_kid_prev = k; hp_kidmax_prev = km; + hp_gd_prev = gd; hp_gdmax_prev = gm; + if (changed) hp_todo = 2; + } + if (hp_todo == 0) return; + hp_todo--; + + /* Стереть прошлую полосу. Фон под ней — НЕ цвет 0, а POP_COL_OUTSIDE + * (в палитре тоже чёрный): вспышки подменяют нулевой цвет, и полоса HP + * не должна мигать вместе с игровым полем (pop_ui.h). */ + gfx_set_bank(GFX_BANK_NORMAL); + setfillstyle(SOLID_FILL, POP_COL_OUTSIDE); + /* Полосу мы делим со статус-строкой (pop_status.h): пока в ней висит + * текст, его зону не трогаем — иначе он мигал бы при каждом изменении + * жизней. Деления Кида левее POP_STATUS_L, стража — правее POP_STATUS_R, + * так что чистить края по отдельности достаточно. */ + if (pop_status_ticks) { + bar(0, HP_Y + POP_YOFF, POP_STATUS_L - 1, HP_Y + POP_YOFF + 6); + bar(POP_STATUS_R + 1, HP_Y + POP_YOFF, 319, HP_Y + POP_YOFF + 6); + } else { + bar(0, HP_Y + POP_YOFF, 319, HP_Y + POP_YOFF + 6); + } + + /* Кид: сначала пустые деления, поверх — полные (порядок оригинала). */ + n = (uint8_t)(hitp_max > HP_MAXDRAW ? HP_MAXDRAW : hitp_max); + for (i = hitp_curr; i < n; i++) + pop_kid_img_blit(HP_KID_EMPTY, (int)i * HP_STEP, HP_Y + POP_YOFF); + for (i = 0; i < hitp_curr && i < HP_MAXDRAW; i++) + pop_kid_img_blit(HP_KID_FULL, (int)i * HP_STEP, HP_Y + POP_YOFF); + + /* Страж: только полные деления, справа налево. Скелет полосы не имеет, + * мышь (спецсобытие уровня 8) — тоже: draw_guard_hp, seg000:1159. */ + if (g_ok && Guard.charid != 0 && Guard.charid != CHARID_4_SKELETON && + Guard.charid != CHARID_24_MOUSE && gd) { + const uint8_t *img = (const uint8_t *)atlas_image(&gp[0], 0); + uint8_t w, h; + gfx_w0_map(gp[0].page); + w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8)); + h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8)); + if (w && h) { + /* Атлас стража тоже column-major — блит колоночный. */ + n = (uint8_t)(gd > HP_MAXDRAW ? HP_MAXDRAW : gd); + for (i = 0; i < n; i++) { + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + gfx_blit_cols_part_noclip(314 - (int)i * HP_STEP, + HP_Y + POP_YOFF, img, 0, 0, 0); + } + } + gfx_w0_unmap(); + } +} diff --git a/applications/SprPoP/src/pop_cdraw.h b/applications/SprPoP/src/pop_cdraw.h new file mode 100644 index 0000000..8049dc7 --- /dev/null +++ b/applications/SprPoP/src/pop_cdraw.h @@ -0,0 +1,188 @@ +/* + * pop_cdraw.h — ОТРИСОВКА ПЕРСОНАЖА, одна на всех Char. + * + * В оригинале это два входа с ИДЕНТИЧНЫМ телом (seg008:22F0/2324): + * + * add_kid_to_objtable add_guard_to_objtable + * loadkid() loadshad() + * load_fram_det_col() load_fram_det_col() + * load_frame_to_obj() load_frame_to_obj() + * stuck_lower() stuck_lower() + * set_char_collision() set_char_collision() + * set_objtile_at_char() set_objtile_at_char() + * redraw_at_char() redraw_at_char() + * redraw_at_char2() redraw_at_char2() + * clip_char() clip_char() + * add_objtable(0) add_objtable(1 тень / 2 страж) + * + * — различаются ТОЛЬКО окном (loadkid/loadshad), набором спрайтов и типом + * объекта. У нас до DRAW-CHAR это были два независимых куска кода + * (kid_draw в резиденте и pop_guard_draw в банке), и расхождение уже стоило + * бага: падающий скелет рисовался поверх верхней грани пола соседней + * колонки, потому что порт redraw_at_char2 был только у Кида. Тем же путём + * пошли бы тень (ур. 4/5/6/12), визирь (13) и толстяк (12). + * + * Поэтому здесь ОДИН набор функций, а различия сведены к слоту: + * POP_CH_KID — Кид: окно loadkid, атласы chtab_2 (kidp), кадр kid_frame; + * POP_CH_OPP — соперник: окно loadshad, атласы chtab_5 (страж/скелет), + * кадр pop_gframe. + * + * Порядок в кадре (задаёт главный цикл, как draw_people): + * pop_char_heal(who) — стереть прошлый кадр ЭТОЙ страницы дабл-буфера; + * pop_char_draw(who) — спрайт + брызги урона + клинок; + * pop_char_fore(who) — передние грани и оверлеи тайлов ПОВЕРХ него. + */ +#ifndef POP_CDRAW_H +#define POP_CDRAW_H + +#include + +/* Нижняя статус-полоса: строка делений HP. Живёт здесь, а не в pop_cdraw.c, + * потому что её делит UI — «GAME PAUSED» в оригинале пишется в ту же полосу + * (SDLPoP rect_bottom_text = {193,70,202,250}), между делениями Кида слева и + * стража справа. */ +#define POP_HP_Y 194 + +#define POP_CH_KID 0 +#define POP_CH_OPP 1 +#define POP_CH_N 2 + +/* Состояние отрисовки одного слота. Публичное, потому что метрики кадра + * (fp*) читает физика из ДРУГОГО банка (char_width_half в + * set_char_collision, порядок «кто поверх кого» в pop_bg): данные лежат в + * _DATA (W2) и видны всем без трамплина, а вызов аксессора из банка стоил + * бы дороже самого чтения. */ +typedef struct { + /* прямоугольник спрайта по СТРАНИЦАМ дабл-буфера: у каждой своя + * видео-ОЗУ и своя ОЗУ-копия, поэтому heal обязан стирать спрайт именно + * той страницы, в которую сейчас рисуем */ + int x[2], y[2]; + uint8_t w[2], h[2]; /* габарит: спрайты игры не крупнее 64x64 */ + uint8_t valid[2]; + /* НАКЛАДНЫЕ спрайты (клинок + брызги урона) — свой прямоугольник, не + * объединение с персонажем: объединение сильно больше суммы двух + * (клинок уходит вперёд-вверх), а heal стоит ровно по площади */ + int ox[2], oy[2]; + uint8_t ow[2], oh[2]; + uint8_t ovalid[2]; + /* габарит кадра для fore-прохода: fpx — ЛОГИЧЕСКАЯ X (до ×8/7), + * fpy — низ спрайта; fpw == 0 — в этом кадре рисовать было нечего */ + int fpx, fpy; + uint8_t fpw, fph; + /* окно fore-клипа слота (объединение «спрайт + накладные»), КОМНАТНЫЙ y */ + int cx, cy; + uint8_t cw, ch; + /* straddle: рендерное смещение по ЛОГИЧЕСКОЙ X, когда комната персонажа + * не совпадает с отрисованной (порт xpos_in_drawn_room) */ + int render_dx; +} pop_cdraw_t; + +extern pop_cdraw_t pop_cd[POP_CH_N]; + +/* Забыть, что нарисовано на обеих страницах: после меню, QuickLoad и любой + * другой перерисовки экрана мимо кадрового конвейера. Без этого пропуск + * перерисовки «тихого» персонажа оставляет его невидимым. */ +void pop_cd_forget(void) __banked; + +/* Стереть прошлый кадр персонажа (спрайт + накладные) на ТЕКУЩЕЙ странице. */ +void pop_char_heal(uint8_t who) __banked; + +/* Нарисовать текущий кадр: спрайт, брызги урона (по флагу pop_kid_hurt / + * pop_guard_hurt) и клинок. Загружает окно Char сам — clip_char и футпринт + * работают с АКТИВНЫМ персонажем, как в оригинале. */ +void pop_char_draw(uint8_t who) __banked; + +/* Передние грани и оверлеи тайлов ПОВЕРХ персонажа: ставит окно fore-клипа + * слота и зовёт общий проход pop_fore_over_char. Отдельным вызовом, а не + * хвостом pop_char_draw, потому что порядок в кадре задаёт главный цикл: + * между спрайтом и его fore-слоем успевает нарисоваться падающая плита. */ +void pop_char_fore(uint8_t who) __banked; + +/* Straddle-смещение спрайта (см. render_dx). */ +void pop_char_set_render_dx(uint8_t who, int dx) __banked; + +/* ---- Пропуск НЕИЗМЕНИВШЕГОСЯ кадра (DRAW-COST) ---------------------- * + * heal + спрайт + fore-проход стоят ~50 % кадрового периода НА ПЕРСОНАЖА + * (замер пользователя 2026-08-08: комната 1.3 с телом убитого стража — 210 % + * кадра, соседняя 1.2 без тела — 140 %). Платили мы это и за то, что не + * менялось: труп лежит неподвижно до выхода из комнаты, стоящий Кид — пока + * игрок не нажал клавишу, а перерисовывались они каждый кадр. + * + * Оптимизация: у каждой страницы дабл-буфера свой снимок ВХОДОВ отрисовки + * слота (поза, координаты, направление, меч, брызги, straddle). Если снимок + * совпал, спрайт этой страницы не стёрт и фона под ним никто не трогал — + * нужный кадр на странице УЖЕ нарисован, и heal/draw/fore пропускаются + * целиком. + * + * `pop_cd_touch` — «фон трогали ВОТ ЗДЕСЬ»: слой фона копит ПРЯМОУГОЛЬНИК + * своих правок по страницам дабл-буфера, и слот перерисовывается, только + * если тот задел его самого. Флага «трогали хоть что-то» не хватило: + * факелы анимируются КАЖДЫЙ кадр, и один факел в комнате обнулял пропуск + * для всех персонажей сразу (замер 2026-08-08: комната 1.1, Кид стоит — + * 597 684 такта, то есть ровно как без оптимизации). + * + * Метку ставит сам блит фона (`pop_blit_b`, по признаку «идёт не + * fore-проход») плюс те немногие места, что стирают фон голым `bar` или + * своим heal (падающий кусок, оверлей fore-heal). Координаты — ЭКРАННЫЕ + * (с POP_YOFF). Прямоугольник копится сразу для ОБЕИХ страниц: правку фона + * почти всегда повторяют на второй, а лишняя перерисовка персонажа дешевле + * лишнего состояния. Снимает метку главный цикл — после того, как + * персонажи на этой странице отрисованы. + * + * Резидент (pop_tile.c): зовёт и банковый слой фона, и резидентные листья, + * а трамплин на КАЖДЫЙ кусок фона стоил бы дороже самой метки. */ +extern uint8_t pop_cd_dirty; /* биты страниц: метка взведена */ +/* [страница][колонка] = диапазон затронутых ЭКРАННЫХ y; пусто = ymin 255, + * ymax 0. Раньше тут была маска колонок на три ряда по 63 px — она + * склеивала касания внутри ряда и заставляла перерисовывать нетронутого + * персонажа (разбор в шапке pop_cd_touch, pop_tile.c). */ +extern uint8_t pop_cd_ymin[2][10], pop_cd_ymax[2][10]; +/* Задели ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 включительно) то, + * что трогали на странице p. Резидент (pop_tile.c). */ +uint8_t pop_cd_hit(uint8_t p, int x0, int y0, int x1, int y1) __banked; +/* То же для СЛОТА персонажа: спрайт и накладной, координаты изнутри pop_cd + * (см. тело — там про цену пяти аргументов). */ +uint8_t pop_cd_hit_slot(uint8_t who, uint8_t p) __banked; +void pop_cd_clear(uint8_t p) __banked; /* снять метку страницы целиком */ +void pop_cd_init(void) __banked; /* пустые диапазоны на обеих страницах */ +void pop_cd_touch(int x, int y, int w, int h); +#define POP_CD_TOUCH_ALL() pop_cd_touch(0, 0, 320, 256) + +/* Пакетная пометка: между begin и end блиты КОПЯТ общий прямоугольник, и + * настоящая пометка ставится одна (на весь тайл), а не на каждый кусок. + * Скобку ставит draw_tile — выигрыш ~15% цены блита фона. Вкладывать + * скобки нельзя: аккумулятор один. */ +extern uint8_t pop_cd_batch; +void pop_cd_batch_begin(void); +void pop_cd_batch_end(void); + +/* ПОДАВИТЬ пометки на время серии блитов — для того, кто пометил область САМ + * и заведомо шире, чем нарисует (падающий кусок: pop_loose_mob_tick помечает + * весь коридор heal'а одним вызовом, а спрайт куска — три блита внутри него). + * Дешевле пакета: pop_cd_touch выходит второй проверкой, не считая ни колонок, + * ни рядов. Замер 2026-08-17: настоящий вызов pop_cd_touch = 4 502 такта, + * 28% всей цены блита. + * + * ОПАСНО применять без разбора: пропущенная пометка = персонаж, которого + * стёрли, но не попросили перерисоваться, то есть исчезнувший со экрана + * персонаж (ровно этот баг ловили 2026-08-13 в комнате 23). Ставить только + * там, где покрытие доказано. */ +void pop_cd_mute(void); +void pop_cd_unmute(void); + +/* Кого в этом кадре можно НЕ перерисовывать: бит 0 — Кид, бит 1 — соперник. + * Звать ДВАЖДЫ: перед heal (стирать нечего) и перед отрисовкой (тик мог + * персонажа всё-таки сдвинуть — тогда бит снимется сам, а pop_char_draw + * сотрёт прошлый кадр за нас). */ +uint8_t pop_char_skip_mask(void) __banked; + +/* ОТРАЖЕНИЕ В ЗЕРКАЛЕ (уровень 4, seg003:0798). Char к моменту вызова уже + * отражён (pop_check_mirror); clip_top — линия y_clip[curr_row+1], выше + * которой отражение не рисуется. Отдельно от pop_char_draw, потому что и в + * оригинале это сокращённый путь add_objtable(4): без клинка, брызг, + * fore-прохода и пропуска кадра. heal — парный вызов, рядом с + * pop_char_heal. */ +void pop_mirror_draw(int clip_top) __banked; +void pop_mirror_heal(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_cfg.h b/applications/SprPoP/src/pop_cfg.h new file mode 100644 index 0000000..78eefe9 --- /dev/null +++ b/applications/SprPoP/src/pop_cfg.h @@ -0,0 +1,19 @@ +/* + * pop_cfg.h — файл настроек POP.CFG (план docs/menu_settings_plan.md §6). + * Единственный владелец формата — модули pop_cfg_*; gameplay читает + * только pop_settings (pop_settings.h). + */ +#ifndef POP_CFG_H +#define POP_CFG_H + +#include + +/* Читает POP.CFG в pop_settings; файла нет / битый — остаются дефолты. + * 1 — прочитано из файла, 0 — дефолты (штатно), -1 — нет памяти. */ +int8_t pop_cfg_load(void) __banked; + +/* Записывает текущие pop_settings в POP.CFG. Звать после Apply/выхода + * из Settings. 1 — записано, 0 — ошибка записи, -1 — нет памяти. */ +int8_t pop_cfg_save(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_char.h b/applications/SprPoP/src/pop_char.h new file mode 100644 index 0000000..b12e3a7 --- /dev/null +++ b/applications/SprPoP/src/pop_char.h @@ -0,0 +1,32 @@ +/* + * pop_char.h — состояние ПЕРСОНАЖА, общее для Кида, стража и активной + * копии Char. Порт char_type (types.h:302). + * + * Зачем отдельным заголовком: интерпретатор последовательностей в + * оригинале один на всех (работает с Char, а вокруг стоят + * loadkid/savekid и loadshad/saveshad, seg006:809). Пока у Кида был свой + * усечённый тип, а у стража свой, общий интерпретатор был невозможен. + * Поля и их порядок — как в char_type, чтобы порт seg005/seg006 ложился + * один в один. + */ +#ifndef POP_CHAR_H +#define POP_CHAR_H + +#include + +typedef struct { + uint8_t frame; + uint8_t x, y; + int8_t direction; /* -1 влево, 0 вправо */ + int8_t curr_col, curr_row; + uint8_t action; + int8_t fall_x, fall_y; + uint8_t room; + uint8_t repeat; + uint8_t charid; /* 0 = Kid, 2 = страж (см. CHARID_* в pop_guard.h) */ + uint8_t sword; + int8_t alive; /* -1 = жив (как в оригинале), 0 = убит */ + uint16_t curr_seq; /* адрес в seqtbl (база SEQTBL_BASE) */ +} pop_char_t; + +#endif diff --git a/applications/SprPoP/src/pop_cheat.h b/applications/SprPoP/src/pop_cheat.h new file mode 100644 index 0000000..4b24108 --- /dev/null +++ b/applications/SprPoP/src/pop_cheat.h @@ -0,0 +1,49 @@ +/* + * pop_cheat.h — режим читов (порт cheats_enabled, seg000:111). + * + * В оригинале включается ключом командной строки megahit/debug и меняет + * набор доступных клавиш. У нас конфигурации/UI пока нет, поэтому это + * просто глобальный флаг: main ставит его при старте (на время разработки — + * ВКЛЮЧЁН). Клавиши читов обрабатывает главный цикл. + * + * Реализовано: K — убить стража (seg000:786), I/S/Shift+L (наши отладочные), + * [ / ] — подгонка Кида по X (seg000:1828), U — переворот экрана + * (seg000:793), F7/F8 — минута −/+ (аналог оригинальных keypad −/+). + * Остальные читы оригинала (перо, воскрешение, переход по комнатам) пока + * не портированы. + */ +#ifndef POP_CHEAT_H +#define POP_CHEAT_H + +#include +#include "pop_level.h" + +extern uint8_t pop_cheats; /* 0 = выключены, иначе включены */ + +#define KBD_CHEAT_KILL 0x42 /* K (PS/2 set 2) — kill guard */ +#define KBD_CHEAT_IMMO 0x43 /* I (PS/2 set 2) — бессмертие Кида по кругу + * 0 -> 1 (только бой) -> 2 (и мелкий урон) -> 0; + * уровни расписаны в pop_state.h */ +/* Ctrl+S (PS/2 set 2: S = 0x1B) — вкл/выкл звук; порт Ctrl+S из SDLPoP + * (seg000:657). С Ctrl, а не голая S: у оригинала клавиша именно такая. */ +#define KBD_SOUND_TOGGLE 0x1B +/* Ctrl+M — тумблер МУЗЫКИ, парный к Ctrl+S. Отдельный потому, что музыка + * у нас поток с диска: её выключают по другим причинам, чем эффекты. */ +#define KBD_MUSIC_TOGGLE 0x3A /* M (PS/2 set 2) */ +#define KBD_CHEAT_NEXTLVL 0x4B /* Shift+L (PS/2 set 2) — следующий уровень */ +#define KBD_CHEAT_XDEC 0x54 /* [ (PS/2 set 2) — сдвинуть Кида на 1 px влево */ +#define KBD_CHEAT_XINC 0x5B /* ] (PS/2 set 2) — сдвинуть Кида на 1 px вправо */ +#define KBD_CHEAT_UPSIDE 0x3C /* U (PS/2 set 2) — переворот экрана (toggle_upside, + * seg000:0793: в оригинале это тоже ЧИТ-клавиша). + * Без него зелье инверсии проверяется только + * честным проходом уровня 9 до комнаты 7. */ +/* F7/F8 свободны в игровой раскладке. Numpad +/- уже принадлежат ROOMNAV, + * поэтому буквально оригинальные клавиши здесь были бы опасным конфликтом. */ +#define KBD_CHEAT_TIME_SUB 0x83 /* F7 (PS/2 set 2) — отнять минуту */ +#define KBD_CHEAT_TIME_ADD 0x0A /* F8 (PS/2 set 2) — добавить минуту */ + + +/* Старое имя оставлено для обработчика Shift+L; граница одна — pop_level.h. */ +#define POP_LAST_LEVEL POP_LEVEL_LAST + +#endif diff --git a/applications/SprPoP/src/pop_config.c b/applications/SprPoP/src/pop_config.c new file mode 100644 index 0000000..8f23a1c --- /dev/null +++ b/applications/SprPoP/src/pop_config.c @@ -0,0 +1,200 @@ +/* + * pop_config.c — ВСЯ модель пользовательских настроек и POP.CFG (банк 9). + * + * Здесь вместе живут state, дефолты, бинарный codec и DSS I/O: это один + * модуль приложения, не библиотека. Данные остаются в общем W2 (для + * --bank-data нет ключа), код уезжает в банк 9. Игра читает только + * pop_settings либо свои runtime-флаги, которые синхронизирует + * pop_settings_apply(). + * + * POP.CFG v1: + * 0..3 "PCFG", 4 version, 5..6 payload size LE, payload, XOR, ~XOR. + * Неверный файл не меняет уже установленные дефолты. Расширенный payload + * v1 допустим: известны первые девять байтов, неизвестный хвост пропускается. + */ +#include +#include + +#ifndef POP_CONFIG_HOST_TEST +#include +#include +#include +#include +#include +#endif + +#include "pop_settings.h" +#include "pop_cfg.h" +#include "pop_pace.h" +#include "pop_cheat.h" +#include "pop_sfx.h" +#include "_pop_config.h" + +#define CFG_BASE 0x100 + +pop_settings_t pop_settings; + +static uint8_t cfg_checksum(const uint8_t *data, uint16_t size) +{ + uint8_t sum = 0; + while (size--) sum ^= *data++; + return sum; +} + +void pop_settings_defaults(void) __banked +{ + pop_settings.sound_enabled = 1; + pop_settings.music_enabled = 1; + pop_settings.speed_mode = POP_SPEED_NORMAL; + pop_settings.gameplay_profile = POP_PROFILE_VANILLA; + pop_settings.cheats_enabled = 1; /* пока SprPoP — dev-сборка */ + pop_settings.show_build_info = 0; + pop_settings.enhancement_flags = 0; + pop_settings.time_limit_enabled = 1; + pop_settings.show_debug_bar = 0; /* по умолчанию чистый экран */ +} + +/* Применить модель к настоящим владельцам runtime-состояния. Sound нельзя + * применять до pop_sfx_init(): тот поднимает набор и только затем знает, + * можно ли честно включить CBL. */ +void pop_settings_apply(uint8_t sound_ready) __banked +{ + pop_speed_mode = pop_settings.speed_mode; + pop_cheats = pop_settings.cheats_enabled; + if (sound_ready && pop_snd_want != pop_settings.sound_enabled) { + pop_sfx_toggle(); + /* Если звуковой набор не поднялся, toggle возвращает want в 0. + * Файл должен отражать фактически применимое состояние. */ + pop_settings.sound_enabled = pop_snd_want; + } + /* Музыке набор эффектов не нужен: она читается с диска отдельно, + * поэтому применяется всегда, а не по sound_ready. */ + pop_mus_want = pop_settings.music_enabled; +} + +void pop_cfg_encode(uint8_t *buf) +{ + uint8_t sum; + memcpy(buf, "PCFG", 4); + buf[4] = POP_CFG_VERSION; + buf[5] = POP_CFG_PAYLOAD_SIZE; + buf[6] = 0; + buf[7] = pop_settings.sound_enabled; + buf[8] = pop_settings.speed_mode; + buf[9] = pop_settings.gameplay_profile; + buf[10] = pop_settings.cheats_enabled; + buf[11] = pop_settings.show_build_info; + buf[12] = (uint8_t)pop_settings.enhancement_flags; + buf[13] = (uint8_t)(pop_settings.enhancement_flags >> 8); + buf[14] = pop_settings.time_limit_enabled; + buf[15] = pop_settings.show_debug_bar; + buf[16] = pop_settings.music_enabled; + sum = cfg_checksum(buf + POP_CFG_HEADER_SIZE, POP_CFG_PAYLOAD_SIZE); + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE] = sum; + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE + 1] = (uint8_t)~sum; +} + +uint8_t pop_cfg_decode(const uint8_t *buf, uint16_t size) +{ + uint16_t payload_size, flags; + uint8_t sum; + + if (size < POP_CFG_MIN_SIZE || + memcmp(buf, "PCFG", 4) != 0 || buf[4] != POP_CFG_VERSION) + return 0; + payload_size = (uint16_t)(buf[5] | ((uint16_t)buf[6] << 8)); + if (payload_size < POP_CFG_LEGACY_PAYLOAD_SIZE || + payload_size > POP_CFG_MAX_PAYLOAD || + size != (uint16_t)(POP_CFG_HEADER_SIZE + payload_size + + POP_CFG_TAIL_SIZE)) + return 0; + sum = cfg_checksum(buf + POP_CFG_HEADER_SIZE, payload_size); + if (buf[POP_CFG_HEADER_SIZE + payload_size] != sum || + buf[POP_CFG_HEADER_SIZE + payload_size + 1] != (uint8_t)~sum) + return 0; + + /* Сначала проверить ВСЕ известные поля, только затем менять state. */ + if (buf[7] > 1 || buf[8] >= POP_SPEED_MODES || + buf[9] > POP_PROFILE_ENHANCED || buf[10] > 1 || buf[11] > 1) + return 0; + if (payload_size >= POP_CFG_TIMELIMIT_PAYLOAD_SIZE && buf[14] > 1) return 0; + if (payload_size >= POP_CFG_DEBUGBAR_PAYLOAD_SIZE && buf[15] > 1) return 0; + if (payload_size >= POP_CFG_PAYLOAD_SIZE && buf[16] > 1) return 0; + flags = (uint16_t)(buf[12] | ((uint16_t)buf[13] << 8)); + pop_settings.sound_enabled = buf[7]; + pop_settings.speed_mode = buf[8]; + /* ENHANCED пока не имеет аудита: безопасно понижаем до VANILLA. */ + pop_settings.gameplay_profile = POP_PROFILE_VANILLA; + pop_settings.cheats_enabled = buf[10]; + pop_settings.show_build_info = buf[11]; + pop_settings.enhancement_flags = flags; + /* Старый POP.CFG создавался до настройки таймера (и до тумблера + * отладочной полосы): отсутствующее поле означает исторический дефолт — + * ограничение 60 минут, включённую полосу и музыку, а не случайное + * значение в памяти. */ + pop_settings.time_limit_enabled = + (payload_size >= POP_CFG_TIMELIMIT_PAYLOAD_SIZE) ? buf[14] : 1; + pop_settings.show_debug_bar = + (payload_size >= POP_CFG_DEBUGBAR_PAYLOAD_SIZE) ? buf[15] : 1; + pop_settings.music_enabled = + (payload_size >= POP_CFG_PAYLOAD_SIZE) ? buf[16] : 1; + return 1; +} + +#ifndef POP_CONFIG_HOST_TEST + +/* В отличие от bank_load_file здесь нужен точный размер файла: только так + * отличаем валидный будущий payload от короткого/дописанного мусора. Буфер + * на стеке лежит в W2, то есть разрешён ESTEX; файл мал, EMM ему не нужен. */ +static uint8_t cfg_read_file(uint8_t *buf, uint16_t *size) +{ + int fd, got; + long n; + + fd = open("POP.CFG", O_RDONLY); + if (fd < 0) return 0; + n = lseek(fd, 0, SEEK_END); + if (n < POP_CFG_MIN_SIZE || n > POP_CFG_MAX_SIZE || + lseek(fd, 0, SEEK_SET) < 0) { + close(fd); + return 0; + } + got = read(fd, buf, (uint16_t)n); + if (close(fd) != 0 || got != (int)n) return 0; + *size = (uint16_t)n; + return 1; +} + +int8_t pop_cfg_load(void) __banked +{ + uint8_t buf[POP_CFG_MAX_SIZE]; + uint16_t size; + + if (!cfg_read_file(buf, &size) || !pop_cfg_decode(buf, size)) return 0; + if (buf[9] != POP_PROFILE_VANILLA) + printf("POP.CFG: ENHANCED profile -> VANILLA\n"); + return 1; +} + +int8_t pop_cfg_save(void) __banked +{ + uint8_t blk, page; + uint8_t buf[POP_CFG_SIZE]; + int rc; + + blk = mem_alloc_pages(1); + if (!blk) return -1; + page = mem_get_page(blk, 0); + gfx_w0_page_prepare(page); + pop_cfg_encode(buf); + gfx_w0_map(page); + memcpy((uint8_t *)CFG_BASE, buf, sizeof(buf)); + gfx_w0_unmap(); + /* Не unlink: bank_save_file сам делает O_TRUNC и не создаёт лишнего + * окна, когда POP.CFG вообще отсутствует после ошибки unlink. */ + rc = bank_save_file(page, CFG_BASE, "POP.CFG", sizeof(buf)); + mem_free_block(blk); + return (rc == sizeof(buf)) ? 1 : 0; +} + +#endif diff --git a/applications/SprPoP/src/pop_ctrl.c b/applications/SprPoP/src/pop_ctrl.c new file mode 100644 index 0000000..a3dc5dd --- /dev/null +++ b/applications/SprPoP/src/pop_ctrl.c @@ -0,0 +1,863 @@ +/* + * pop_ctrl.c — управление Kid (Фаза K2b). Порт SDLPoP: + * вход — read_user_control/user_control/rest_ctrl_1/save_ctrl_1 + * (seg006.c) поверх held-state ; + * диспетчер — control() + control_standing/turning/startrun/running/ + * crouched (seg005.c), Kid-only ветки (без меча/стражи); + * действия — forward/back/up/down_pressed, crouch, standing_jump, + * run_jump, safe_step (seg005.c). + * + * Map-запросы (стена/край/захват) ЗАСТАБЛЕНЫ «открытый пол» (map_* ниже) — + * реальная коллизия в K3/K4. Поэтому: forward -> всегда бег, down -> присед, + * back -> разворот, up+forward -> прыжок с места; osторожный шаг и climb + * помечены TODO(K3/K4). + * + * Значения CONTROL_* (types.h): RELEASED=0, IGNORE=1, HELD=-1; + * X: LEFT/FORWARD=-1, RIGHT/BACKWARD=+1; Y: UP=-1, DOWN=+1. + */ +#include +#include +#include "pop_state.h" +#include "pop_ctrl.h" /* свой API (__banked) + объявления шины control_* */ +#include "pop_kid.h" +#include "pop_sfx.h" /* звуковые эффекты (../docs/sound_plan.md) */ +#include "pop_music.h" /* pop_mus_left — «тема ещё звучит» */ +#include "pop_map.h" +#include "pop_level.h" /* pop_current_level — вступительная тема 1-го */ +#include "pop_guard.h" /* charid, состояние меча, seq стража */ + +/* ---- CONTROL_* и seq/frame id (подмножество types.h) --------------- */ +#define CONTROL_RELEASED 0 +#define CONTROL_IGNORE 1 +#define CONTROL_HELD (-1) +#define CONTROL_HELD_LEFT (-1) +#define CONTROL_HELD_RIGHT 1 +#define CONTROL_HELD_FORWARD (-1) +#define CONTROL_HELD_BACKWARD 1 +#define CONTROL_HELD_UP (-1) +#define CONTROL_HELD_DOWN 1 + +#define DIR_RIGHT 0 /* dir_0_right */ + +#define SEQ_1_START_RUN 1 +#define SEQ_2_STAND 2 +#define SEQ_3_STANDING_JUMP 3 +#define SEQ_4_RUN_JUMP 4 +#define SEQ_5_TURN 5 +#define SEQ_6_RUN_TURN 6 +#define SEQ_13_STOP_RUN 13 +#define SEQ_26_CROUCH_WHILE_RUNNING 26 +#define SEQ_43_START_RUN_AFTER_TURN 43 +#define SEQ_49_STAND_UP_FROM_CROUCH 49 +#define SEQ_50_CROUCH 50 +#define SEQ_79_CROUCH_HOP 79 +#define SEQ_78_DRINK 78 /* выпить зелье */ +#define SEQ_91_PICKUP_SWORD 91 /* поднять меч (pickupsword) */ +#define SEQ_STEP_BASE 28 /* +distance(1..14) = seq 29..42 шаг-N */ +#define SEQ_39_UNSAFE_STEP 39 /* шаг за кромку (distance 11) */ +#define SEQ_44_STEP_ON_EDGE 44 +#define SEQ_14_JUMP_INTO_CEILING 14 /* прыжок вверх: сверху стена/пол */ +#define SEQ_28_JUMP_NOTHING_ABOVE 28 /* прыжок вверх: сверху пусто */ +#define SEQ_25_HANG_AGAINST_WALL 25 /* вис у стены (Shift в висе) */ +#define SEQ_GO_UP_LEVELDOOR 70 /* seq_70_go_up_on_level_door */ + +#define FRAME_7_RUN 7 +#define FRAME_11_RUN 11 +#define FRAME_15_STAND 15 +#define FRAME_48_TURN 48 +#define FRAME_50_TURN 50 +#define FRAME_67_JUMPUP1 67 +#define FRAME_70_JUMPHANG 70 +#define FRAME_87_HANG1 87 +#define FRAME_109_CROUCH 109 + +/* ---- Состояние управления (seg006 globals) ------------------------ * + * Сами control_* ЖИВУТ В pop_state.c, а не здесь. Модуль уехал в БАНК 5, + * а шину пишут с трёх сторон (этот банк, guards.c из банка 1) и читают из + * банка 3 (pop_map) — это ровно тот случай, для которого pop_state и + * заведён. Сегодня оно работало бы и отсюда: банки собираются БЕЗ + * --bank-data, поэтому их писучие данные остаются в общем _DATA. Но это + * флаг сборки, а не свойство кода: включат --bank-data ради экономии + * W1/W2 — и шина молча уедет в страницу банка. + * + * ctrl1_* остаются здесь: их трогает только этот модуль. */ +static int8_t ctrl1_forward, ctrl1_backward, ctrl1_up, ctrl1_down, ctrl1_shift2; + +/* ---- seqtbl_offset_char: сменить последовательность Kid ------------ * + * Обёртка над kid_set_seq (единая точка в pop_kid — kid_seq_off/kid_seqtbl + * не дублируются в этот TU). */ +/* В оригинале seqtbl_offset_char работает с АКТИВНЫМ персонажем, и + * control() вызывается внутри окна Char. Раньше макрос вёл на + * kid_set_seq (пишет прямо в Kid) — после введения окна это ломало ввод: + * savekid затирал Kid копией Char со старой curr_seq. */ +#define seqtbl_offset_char(seq) pop_char_set_seq(seq) + +static int8_t release_arrows(void) +{ + control_backward = control_forward = control_up = control_down = CONTROL_RELEASED; + return CONTROL_IGNORE; /* seg006: return 1 */ +} + +#define TILE_CHOMPER 18 /* tiles_18_chomper (исключение в forward_pressed) */ + +/* Край/стена впереди — из pop_map (get_edge_distance): pop_edge_distance() + * возвращает дистанцию и ставит pop_edge_type() = EDGE_WALL/FLOOR/CLOSER. */ + +/* ---- Действия (seg005) -------------------------------------------- */ +static void crouch(void) +{ + seqtbl_offset_char(SEQ_50_CROUCH); + control_down = release_arrows(); +} + +static void standing_jump(void) +{ + control_up = control_forward = CONTROL_IGNORE; + seqtbl_offset_char(SEQ_3_STANDING_JUMP); +} + +/* check_get_item (seg005:061F): решение по тайлам принимает pop_map, здесь + * только исполнение. Триггер в оригинале — Shift (control_shift2) в стойке + * или в приседе: первый раз Kid приседает к предмету, второй — поднимает. + * Возврат 1 = управление обработано (caller делает return). */ +static uint8_t get_item_action(void) +{ + uint8_t a = pop_get_item_action(); + if (a == 1) { crouch(); return 1; } + if (a == 2) { + control_shift2 = CONTROL_IGNORE; /* do_pickup: без авто-повтора */ + seqtbl_offset_char(SEQ_91_PICKUP_SWORD); + return 1; + } + if (a == 3) { /* зелье — выпить (get_item) */ + control_shift2 = CONTROL_IGNORE; + seqtbl_offset_char(SEQ_78_DRINK); + return 1; + } + return 0; +} + +/* run_jump (seg005:0AA8). Выравнивание по кромке пола живёт в pop_map + * (там тайловые запросы) — pop_run_jump_align; здесь только диспетчерская + * часть. Отказ выравнивателя = прыжка в этом кадре НЕТ, и control_up + * гасить нельзя: Кид бежит дальше с зажатой «вверх» и попробует снова на + * следующем кадре. Ровно так игрок и «ловит» фазу перед провалом. */ +static void run_jump(void) +{ + if (Char.frame >= FRAME_7_RUN) { + if (!pop_run_jump_align()) return; + control_up = release_arrows(); + seqtbl_offset_char(SEQ_4_RUN_JUMP); + } +} + +static void safe_step(void); /* fwd */ +static void draw_sword(void); /* fwd: нужен в control_standing */ + +static void forward_pressed(void) +{ + int distance = pop_edge_distance(); + /* seg005:0577 — «у стены шагаем, а не бежим», НО чомпер из этого правила + * ЯВНО исключён (`curr_tile2 != tiles_18_chomper`). Он проходим в + * разомкнутой фазе, и оригинал даёт разбежаться сразу: без исключения + * Кид перед чомпером делает лишний осторожный шаг, а это кадры — и он + * не успевает проскочить между челюстями (поймано на приёмке + * 2026-08-08, комната 3.22). */ + if (pop_edge_type() == EDGE_WALL && pop_edge_tile() != TILE_CHOMPER && + distance < 8) { + if (control_forward == CONTROL_HELD) + safe_step(); + } else { + seqtbl_offset_char(SEQ_1_START_RUN); + } +} + +static void back_pressed(void) +{ + control_backward = release_arrows(); + /* Kid без меча -> просто разворот. */ + seqtbl_offset_char(SEQ_5_TURN); +} + +static void down_pressed(void) +{ + control_down = CONTROL_IGNORE; /* без авто-повтора */ + /* У края пола (край сзади) со зацепом снизу — спуск на этаж (seq_68); + * у передней кромки — отступ; иначе присед. Решает pop_down_action. */ + if (!pop_down_action()) + crouch(); +} + +/* Прыжок вверх в стойке (seg005 check_jump_up): чистый вертикальный прыжок + * ЛИБО прыжок-с-зацепом за уступ выше (запрыгнуть на этаж вверх). Выбор seq + * + выравнивание Kid.x — в pop_jump_up_seq (pop_map). */ +static void jump_up(void) +{ + control_up = release_arrows(); + seqtbl_offset_char(pop_jump_up_seq()); +} + +static void up_pressed(void) +{ + /* Дверь уровня рядом и открыта → войти в неё: постановку в проём + * (go_up_leveldoor) делает pop_leveldoor_enter, здесь остаётся только + * последовательность подъёма по лестнице. Телепортов у нас нет — это + * модификация SDLPoP, а не оригинал. + * Иначе: ↑+forward = прыжок с места, иначе вертикальный прыжок вверх. */ + if (pop_leveldoor_enter()) { + seqtbl_offset_char(SEQ_GO_UP_LEVELDOOR); + return; + } + if (control_x == CONTROL_HELD_FORWARD) + standing_jump(); + else + jump_up(); +} + +/* Осторожный шаг (Shift+forward): шаг РОВНО до кромки/стены. distance>0 — + * шаг длины distance (seq 29..42 = step1..step14); ==0 у края — шаг на + * кромку (seq_44) либо неосторожный шаг за край (seq_39). Порт seg005. */ +static void safe_step(void) +{ + int distance; + control_shift2 = CONTROL_IGNORE; /* без авто-повтора */ + control_forward = CONTROL_IGNORE; + distance = pop_edge_distance(); + if (distance) { + Char.repeat = 1; + seqtbl_offset_char((uint8_t)(distance + SEQ_STEP_BASE)); + } else if (pop_edge_type() == EDGE_WALL) { + /* ВПЛОТНУЮ К ПРЕПЯТСТВИЮ. Оригинал (seg005:0604) уходит здесь в + * seq_39 — а это шаг на ОДИННАДЦАТЬ пикселей, который останавливает + * только бамп. + * + * ЧОМПЕР: бампа нет по определению (в разомкнутой фазе он не + * препятствие, is_obstacle требует modif == 2), и шаг обязан пройти + * целиком — иначе Кид топчется на 1 px и не успевает между + * челюстями. Здесь играем оригинальный seq_39. + * + * СТЕНА И ВОРОТА: держим «шаг-1» — осознанное приближение. Наш бамп + * ловит не тот же кадр, что у оригинала (см. BUG-SEAM-PINGPONG, там + * трасса живого SDLPoP: `safe_step(d=0) -> step, на первом dx(1) + * x=62 -> bump`), и с полным seq_39 Кид ПРОБЕГАЕТ сквозь закрытые + * ворота шва комнаты 6 — поймано приёмкой 2026-08-08. Пока бамп не + * сойдётся с оригиналом покадрово, этот шаг остаётся коротким. */ + if (pop_edge_tile() == TILE_CHOMPER) { + seqtbl_offset_char(SEQ_39_UNSAFE_STEP); + } else { + Char.repeat = 1; + seqtbl_offset_char((uint8_t)(1 + SEQ_STEP_BASE)); + } + } else if (Char.repeat != 0) { + Char.repeat = 0; + seqtbl_offset_char(SEQ_44_STEP_ON_EDGE); + } else { + seqtbl_offset_char(SEQ_39_UNSAFE_STEP); /* реальный обрыв (edge=CLOSER) */ + } +} + +/* Автомат вступительной темы уровня 1 (порт need_level1_music): + * 0 — не нужна, 1 — пора пустить, 2 — играет, ждём конца. */ +static uint8_t lvl1_music; + +/* seg003:45 — need_level1_music = (level == 1). Ставит старт уровня. */ +void pop_ctrl_level_start(void) __banked +{ + lvl1_music = (uint8_t)(pop_current_level == 1); +} + +/* ---- control_* обработчики состояний ------------------------------ */ +static void control_crouched(void) +{ + /* ВСТУПЛЕНИЕ ПЕРВОГО УРОВНЯ (seg005:02EB) — это не «звук при приседе», а + * маленький автомат, и Кид на его время ОБЕЗДВИЖЕН: + * 1 — ничего не звучит: пустить тему, перейти в 2; + * 2 — ждать, пока она доиграет, и только тогда отпустить управление. + * Оттого Кид и сидит в начале игры, пока не смолкнет музыка. + * + * Я сперва сделал проще — «сыграть при первом приседе», — и тема + * догоняла игрока посреди уровня: пробежал, спрыгнул, присел — заиграла + * (поймано пользователем). + * + * Ждём по pop_mus_left: это курсор насоса, он резидентный и правдиво + * отвечает «трек ещё звучит». Если музыка выключена или заявку никто + * не обработал, курсор так и останется нулём — Кид просто встанет, а + * не сядет навсегда. */ + if (lvl1_music && pop_current_level == 1) { + if (!pop_mus_left) { + if (lvl1_music == 1) { + pop_sfx_play(25); + lvl1_music = 2; + } else { + lvl1_music = 0; + } + } + return; /* управление на это время не читаем */ + } + lvl1_music = 0; + /* Shift в приседе — поднять предмет (seg005 control_crouched). */ + if (control_shift2 == CONTROL_HELD && get_item_action()) return; + if (control_y != CONTROL_HELD_DOWN) { + seqtbl_offset_char(SEQ_49_STAND_UP_FROM_CROUCH); + } else if (control_forward == CONTROL_HELD) { + control_forward = CONTROL_IGNORE; + seqtbl_offset_char(SEQ_79_CROUCH_HOP); + } +} + +static void control_standing(void) +{ + /* Shift в стойке над предметом — присесть к нему (seg005:0358, первая + * строка control_standing). Идёт ДО остальных shift-веток. */ + if (control_shift2 == CONTROL_HELD && control_shift == CONTROL_HELD && + get_item_action()) + return; + /* Ветку «charid != kid && вниз+вперёд -> draw_sword» не портируем: у нас + * соперник до control_standing не доходит, его перехватывает + * control_guard_inactive в самом диспетчере (порядок как в seg005). */ + + /* seg005:352 — Кид с мечом ВИДИТ активного стража: достаёт клинок сам. + * Без этого игрок стоит с мечом в ножнах, пока его рубят. */ + if (pop_have_sword) { + if (offguard != 0 && control_shift >= CONTROL_RELEASED) { + /* Меч убран намеренно (down в бою) — не доставать, пока не + * отпустят Shift; в оригинале это переход на loc_6213. */ + } else if (can_guard_see_kid >= 2) { + int16_t d = pop_char_opp_dist(); + if (d >= -10 && d < 90) { + holding_sword = 1; + /* (word)d < (word)-6 — беззнаковое: истинно для ВСЕГО, кроме + * −6..−1. То есть «страж вплотную за спиной» -> отступить, + * иначе достать меч. */ + if (d >= -6 && d < 0) { back_pressed(); return; } + draw_sword(); + return; + } + } else { + offguard = 0; + } + } + + if (control_shift == CONTROL_HELD) { + if (control_backward == CONTROL_HELD) back_pressed(); + else if (control_up == CONTROL_HELD) up_pressed(); + else if (control_down == CONTROL_HELD) down_pressed(); + else if (control_x == CONTROL_HELD_FORWARD && control_forward == CONTROL_HELD) + safe_step(); + } else if (control_forward == CONTROL_HELD) { + if (control_up == CONTROL_HELD) standing_jump(); /* keyboard mode */ + else forward_pressed(); + } else if (control_backward == CONTROL_HELD) { + back_pressed(); + } else if (control_up == CONTROL_HELD) { + if (control_forward == CONTROL_HELD) standing_jump(); + else up_pressed(); + } else if (control_down == CONTROL_HELD) { + down_pressed(); + } else if (control_x == CONTROL_HELD_FORWARD) { + forward_pressed(); + } +} + +static void control_turning(void) +{ + if (control_shift >= CONTROL_RELEASED && + control_x == CONTROL_HELD_FORWARD && control_y >= CONTROL_RELEASED) { + seqtbl_offset_char(SEQ_43_START_RUN_AFTER_TURN); + } +} + +static void control_startrun(void) +{ + if (control_y == CONTROL_HELD_UP && control_x == CONTROL_HELD_FORWARD) + standing_jump(); +} + +static void control_running(void) +{ + /* Стену НЕ обрабатываем тут — check_bumped (pop_phys_tick) даёт отскок + * seq_47 при беге в стену (как оригинал), с корректной позицией. */ + if (control_x == CONTROL_RELEASED && + (Char.frame == FRAME_7_RUN || Char.frame == FRAME_11_RUN)) { + control_forward = release_arrows(); + seqtbl_offset_char(SEQ_13_STOP_RUN); + } else if (control_x == CONTROL_HELD_BACKWARD) { + control_backward = release_arrows(); + seqtbl_offset_char(SEQ_6_RUN_TURN); + } else if (control_y == CONTROL_HELD_UP && control_up == CONTROL_HELD) { + run_jump(); + } else if (control_down == CONTROL_HELD) { + control_down = CONTROL_IGNORE; + seqtbl_offset_char(SEQ_26_CROUCH_WHILE_RUNNING); + } +} + +/* ---- Вис (seg005 control_hanging/can_climb_up/hang_fall) ---------- * + * Тайловые решения (можно ли подтянуться, куда сорваться) — в pop_map; + * здесь управление осями control_*. */ +static void hang_fall(void) +{ + control_down = release_arrows(); + seqtbl_offset_char(pop_hang_fall_seq()); /* seq_23/seq_11 (+ выравнивание x) */ +} + +static void can_climb_up(void) +{ + control_up = control_shift2 = release_arrows(); + seqtbl_offset_char(pop_hang_climb_up_seq()); /* seq_10 / seq_73 */ +} + +static void control_hanging(void) +{ + if (control_y == CONTROL_HELD && pop_hang_grab_ready()) { + can_climb_up(); /* ↑ — подтянуться */ + } else if (control_shift == CONTROL_HELD) { + int8_t w = pop_hang_wall(); + if (w > 0) seqtbl_offset_char(SEQ_25_HANG_AGAINST_WALL); /* у стены */ + else if (w < 0) hang_fall(); /* сорваться */ + /* w==0: сверху пол — держимся (ничего) */ + } else { + hang_fall(); /* отпустил Shift — сорваться */ + } +} + +/* control_jumpup (seg005): в старте прыжка вверх ←/→ или forward → перевести + * в прыжок с места. */ +static void control_jumpup(void) +{ + if (control_x == CONTROL_HELD_FORWARD || control_forward == CONTROL_HELD) + standing_jump(); +} + +/* draw_sword (seg005:945): достать меч. Кид переходит в seq_55 (подъём + * клинка со звуком), соперник — сразу в стойку en garde (seq_90). Стрелки + * гасятся, чтобы «вперёд», которым меч достали, не сработало второй раз + * уже как выпад. */ +static void draw_sword(void) +{ + uint8_t seq_id = SEQ_55_DRAW_SWORD; + control_forward = control_shift2 = release_arrows(); + if (Char.charid == CHARID_0_KID) { + offguard = 0; + pop_sfx_play(19); /* seg005:945 — меч из ножен */ + } else if (Char.charid != CHARID_1_SHADOW) { + seq_id = SEQ_90_EN_GARDE; /* соперник встаёт сразу в стойку */ + } + Char.sword = SWORD_2_DRAWN; + seqtbl_offset_char(seq_id); +} + +/* control_guard_inactive (seg006:2123): неактивный страж реагирует ТОЛЬКО в + * кадре 166 (стойка) и только на «вниз»: вниз+вперёд — достать меч, + * вниз без «вперёд» — развернуться (stand flipped). */ +static void control_guard_inactive(void) +{ + if (Char.frame == FRAME_166_STAND_INACTIVE && control_down == CONTROL_HELD) { + if (control_forward == CONTROL_HELD) { + draw_sword(); + } else { + control_down = CONTROL_IGNORE; + seqtbl_offset_char(SEQ_80_STAND_FLIPPED); + } + } +} + +/* ---- Боёвка (seg005:964..) ---------------------------------------- * + * Всё это ветки ОБЩЕГО диспетчера: у стража тот же control(), просто + * control_* ему выставляет ИИ (autocontrol_*, guards.c), а не клавиатура. */ + +/* back_with_sword (seg005): шаг назад — только из стоек. */ +static void back_with_sword(void) +{ + uint8_t f = Char.frame; + if (f == FRAME_158_STAND_WITH_SWORD || f == FRAME_170_STAND_WITH_SWORD || + f == FRAME_171_STAND_WITH_SWORD) { + control_backward = CONTROL_IGNORE; /* без авто-повтора */ + seqtbl_offset_char(SEQ_57_BACK_WITH_SWORD); + } +} + +/* forward_with_sword (seg005): шаг вперёд; у стража своя последовательность. */ +static void forward_with_sword(void) +{ + uint8_t f = Char.frame; + if (f == FRAME_158_STAND_WITH_SWORD || f == FRAME_170_STAND_WITH_SWORD || + f == FRAME_171_STAND_WITH_SWORD) { + control_forward = CONTROL_IGNORE; + seqtbl_offset_char(Char.charid != CHARID_0_KID ? SEQ_56_GUARD_FORWARD + : SEQ_86_FORWARD_WITH_SWORD); + } +} + +/* sword_strike (seg005): выпад из стойки/шага либо добивание после парирования. */ +static void sword_strike(void) +{ + uint8_t f = Char.frame; + uint8_t seq_id; + if (f == FRAME_157_WALK_WITH_SWORD || f == FRAME_158_STAND_WITH_SWORD || + f == FRAME_170_STAND_WITH_SWORD || f == FRAME_171_STAND_WITH_SWORD || + f == FRAME_165_WALK_WITH_SWORD) { + seq_id = (Char.charid == CHARID_0_KID) ? SEQ_75_STRIKE : SEQ_58_GUARD_STRIKE; + } else if (f == FRAME_150_PARRY || f == FRAME_161_PARRY) { + seq_id = SEQ_66_STRIKE_AFTER_PARRY; + } else { + return; + } + control_shift2 = CONTROL_IGNORE; + seqtbl_offset_char(seq_id); +} + +/* parry (seg005): блок. Условие сильно разное у Кида и у соперника — + * соперник блокирует ТОЛЬКО на кадре 152 (второй кадр удара), а Кид ещё и + * по 153 (и тогда последовательность прокручивается сразу). */ +static void parry(void) +{ + uint8_t cf = Char.frame, of = Opp.frame; + uint8_t seq_id = SEQ_62_PARRY; + uint8_t do_play_seq = 0; + + if (cf == FRAME_158_STAND_WITH_SWORD || cf == FRAME_170_STAND_WITH_SWORD || + cf == FRAME_171_STAND_WITH_SWORD || cf == FRAME_168_BACK || + cf == FRAME_165_WALK_WITH_SWORD) { + if (pop_char_opp_dist() >= 32 && Char.charid != CHARID_0_KID) { + back_with_sword(); + return; + } else if (Char.charid == CHARID_0_KID) { + if (of == FRAME_168_BACK) return; + if (of != FRAME_151_STRIKE_1 && of != FRAME_152_STRIKE_2 && + of != FRAME_162_BLOCK_TO_STRIKE) { + if (of == FRAME_153_STRIKE_3) do_play_seq = 1; + } + } else { + if (of != FRAME_152_STRIKE_2) return; + } + } else { + if (cf != FRAME_167_BLOCKED) return; + seq_id = SEQ_61_PARRY_AFTER_STRIKE; + } + control_up = CONTROL_IGNORE; + seqtbl_offset_char(seq_id); + if (do_play_seq) play_seq(); +} + +/* swordfight (seg005:0CDB) — разбор ввода в бою. Порядок веток важен: + * удар старше «убрать меч», тот старше блока, блок старше шагов. */ +static void swordfight(void) +{ + uint8_t f = Char.frame; + uint8_t seq_id; + + if (f == FRAME_161_PARRY && control_shift2 >= CONTROL_RELEASED) { + seqtbl_offset_char(SEQ_57_BACK_WITH_SWORD); /* отскок после парирования */ + return; + } else if (control_shift2 == CONTROL_HELD) { + if (Char.charid == CHARID_0_KID) kid_sword_strike = 15; + sword_strike(); + if (control_shift2 == CONTROL_IGNORE) return; + } + + if (control_down == CONTROL_HELD) { + if (f == FRAME_158_STAND_WITH_SWORD || f == FRAME_170_STAND_WITH_SWORD || + f == FRAME_171_STAND_WITH_SWORD) { + control_down = CONTROL_IGNORE; + Char.sword = SWORD_0_SHEATHED; + if (Char.charid == CHARID_0_KID) { + offguard = 1; /* убрал намеренно — сразу не доставать */ + guard_refrac = 9; /* и страж на это время придержан */ + holding_sword = 0; + seq_id = SEQ_93_PUT_SWORD_AWAY_FAST; + } else if (Char.charid == CHARID_1_SHADOW) { + seq_id = SEQ_92_PUT_SWORD_AWAY; + } else { + seq_id = SEQ_87_GUARD_BECOME_INACT; /* Кид ушёл из виду */ + } + seqtbl_offset_char(seq_id); + } + } else if (control_up == CONTROL_HELD) { + parry(); + } else if (control_forward == CONTROL_HELD) { + forward_with_sword(); + } else if (control_backward == CONTROL_HELD) { + back_with_sword(); + } +} + +/* control_with_sword (seg005:964): пока соперник виден и близко — бой; + * иначе страж убирает меч, а Кид просто стоит. */ +static void control_with_sword(void) +{ + if (Char.action >= 2) return; /* actions_2_hang_climb и выше */ + if (pop_tile_at(Char.curr_col, Char.curr_row) == 11 /* loose */ || + can_guard_see_kid >= 2) { + int16_t d = pop_char_opp_dist(); + if (d < 90) { + if (d >= 0) { swordfight(); return; } + /* Соперник за спиной: далеко — развернуться, вплотную — драться. */ + if (d < -4) { seqtbl_offset_char(SEQ_60_TURN_WITH_SWORD); return; } + swordfight(); + return; + } + } + /* Не видит / далеко. */ + if (Char.charid == CHARID_0_KID && Char.alive < 0) + holding_sword = 0; /* seg005:980 — индикатор HP стража */ + if (Char.charid < CHARID_2_GUARD) { + if (Char.frame == FRAME_171_STAND_WITH_SWORD) { + Char.sword = SWORD_0_SHEATHED; + seqtbl_offset_char(SEQ_92_PUT_SWORD_AWAY); /* страж убит */ + } + } else { + swordfight(); + } +} + +/* control() — диспетчер по кадру (seg005.c:252) для АКТИВНОГО персонажа. */ +void pop_control(void) __banked +{ + uint8_t f = Char.frame; + /* seg005 control(), самая первая ветка: персонаж УМИРАЕТ (alive >= 0). + * Из любой стойки он валится в seq_71_dying; остальное управление в + * этом состоянии не обрабатывается. Через это же место проходит чит K + * (pop_guard_kill ставит Guard.alive = 0). */ + if (Char.alive >= 0) { + if (f == FRAME_15_STAND || f == FRAME_166_STAND_INACTIVE || + f == FRAME_158_STAND_WITH_SWORD || f == FRAME_171_STAND_WITH_SWORD) + seqtbl_offset_char(SEQ_71_DYING); + return; + } + /* seg005 control(): в отскоке (bumped) и в свободном падении управление + * НЕ обрабатывается вовсе — только гасятся стрелки. Без этого игрок + * прерывает присед после удара (medland/плита по голове) нажатием ↑, и + * последовательность не доигрывает свои dy(1)+dy(1), компенсирующие + * стартовый dy(-2) → Kid навсегда остаётся на 2px выше пола (баг «Y на + * пару пикселей меньше, чем должен»). */ + if (Char.action == 5 || Char.action == 4) { /* bumped / in_freefall */ + release_arrows(); + return; + } + /* Порядок веток — как в seg005: меч старше charid, charid старше кадров. + * Пока Kid безоружен, обе ветки — про стража. */ + if (Char.sword == SWORD_2_DRAWN) + control_with_sword(); + else if (Char.charid >= CHARID_2_GUARD) + control_guard_inactive(); + else if (f == FRAME_15_STAND || (f >= FRAME_50_TURN && f < 53)) + control_standing(); + else if (f == FRAME_48_TURN) + control_turning(); + else if (f < 4) + control_startrun(); + else if (f >= FRAME_67_JUMPUP1 && f < FRAME_70_JUMPHANG) + control_jumpup(); + else if (f < FRAME_15_STAND) + control_running(); + else if (f >= FRAME_87_HANG1 && f < 100) + control_hanging(); + else if (f == FRAME_109_CROUCH) + control_crouched(); +} + +/* ---- Модель ввода (seg006) ---------------------------------------- */ +static void read_input(void) +{ + uint8_t l = kbd_raw_down(KBD_LEFT); + uint8_t r = kbd_raw_down(KBD_RIGHT); + uint8_t u = kbd_raw_down(KBD_UP); + uint8_t d = kbd_raw_down(KBD_DOWN); + control_x = r ? CONTROL_HELD_RIGHT : (l ? CONTROL_HELD_LEFT : CONTROL_RELEASED); + control_y = d ? CONTROL_HELD_DOWN : (u ? CONTROL_HELD_UP : CONTROL_RELEASED); + control_shift = (kbd_raw_down(KBD_LSHIFT) || kbd_raw_down(KBD_RSHIFT)) + ? CONTROL_HELD : CONTROL_RELEASED; +} + +/* read_user_control (seg006:1557): «свежее нажатие» (RELEASED->HELD) с + * подавлением авто-повтора (IGNORE держится, пока клавишу не отпустят). */ +static void read_user_control(void) +{ + if (control_forward >= CONTROL_RELEASED) { + if (control_x == CONTROL_HELD_FORWARD) { + if (control_forward == CONTROL_RELEASED) control_forward = CONTROL_HELD; + } else control_forward = CONTROL_RELEASED; + } + if (control_backward >= CONTROL_RELEASED) { + if (control_x == CONTROL_HELD_BACKWARD) { + if (control_backward == CONTROL_RELEASED) control_backward = CONTROL_HELD; + } else control_backward = CONTROL_RELEASED; + } + if (control_up >= CONTROL_RELEASED) { + if (control_y == CONTROL_HELD_UP) { + if (control_up == CONTROL_RELEASED) control_up = CONTROL_HELD; + } else control_up = CONTROL_RELEASED; + } + if (control_down >= CONTROL_RELEASED) { + if (control_y == CONTROL_HELD_DOWN) { + if (control_down == CONTROL_RELEASED) control_down = CONTROL_HELD; + } else control_down = CONTROL_RELEASED; + } + if (control_shift2 >= CONTROL_RELEASED) { + if (control_shift == CONTROL_HELD) { + if (control_shift2 == CONTROL_RELEASED) control_shift2 = CONTROL_HELD; + } else control_shift2 = CONTROL_RELEASED; + } +} + +static void flip_control_x(void) +{ + int8_t t; + control_x = (int8_t)-control_x; + t = control_forward; control_forward = control_backward; control_backward = t; +} + +/* user_control (seg006:0DC0): «вперёд» относительно направления Kid. */ +static void user_control(void) +{ + if (Char.direction >= DIR_RIGHT) { + flip_control_x(); + pop_control(); + flip_control_x(); + } else { + pop_control(); + } +} + +static void rest_ctrl_1(void) +{ + control_forward = ctrl1_forward; control_backward = ctrl1_backward; + control_up = ctrl1_up; control_down = ctrl1_down; control_shift2 = ctrl1_shift2; +} +static void save_ctrl_1(void) +{ + ctrl1_forward = control_forward; ctrl1_backward = control_backward; + ctrl1_up = control_up; ctrl1_down = control_down; ctrl1_shift2 = control_shift2; +} + +/* Общий хвост control_kid: raw-ввод и attract-demo различаются только тем, + * кто до него заполнил control_*. Окно Char и ctrl1_* обязаны быть теми же, + * иначе запись demo_moves перестаёт совпадать с логикой обычного игрока. */ +static void ctrl_kid_dispatch(void) +{ + pop_loadkid(); + pop_load_fram_det_col(); + user_control(); + pop_savekid_state(); + save_ctrl_1(); +} + +static void ctrl_kid_death(void) +{ + /* control_kid (seg006:0CD1): HP кончились — персонаж мёртв. */ + if (Kid.alive < 0 && hitp_curr == 0) { + Kid.alive = 0; + pop_kid_dead = 1; + /* play_death_music (seg006:15B5): траурный трек зависит от того, + * КАК умер Кид — в бою, от руки тени или просто так. Оригинал ждёт + * шести тиков, чтобы доиграл звук удара; у нас ждать не нужно — + * насос держит курсор музыки, пока звучит эффект. + * + * НА ДЕМО-УРОВНЕ МУЗЫКИ НЕТ — и это буквальное условие оригинала + * (seg006:1366: `current_level != 0 && current_level != 15`). + * Заставочная демка озвучена одними эффектами. */ + if (pop_current_level != 0 && pop_current_level != 15) { + if (Guard.charid == CHARID_1_SHADOW) pop_sfx_play(32); + else if (holding_sword) pop_sfx_play(28); + else pop_sfx_play(24); + } + } +} + +/* ---- Публичный API ------------------------------------------------ */ +int pop_ctrl_init(void) __banked +{ + control_forward = control_backward = control_up = control_down = CONTROL_RELEASED; + control_shift2 = CONTROL_RELEASED; + ctrl1_forward = ctrl1_backward = ctrl1_up = ctrl1_down = ctrl1_shift2 = CONTROL_RELEASED; + return kbd_raw_open(); +} + +void pop_ctrl_close(void) __banked +{ + kbd_raw_close(); +} + +int8_t pop_ctrl_shift_held(void) __banked +{ + return (int8_t)(control_shift == CONTROL_HELD); +} + +void pop_ctrl_tick(void) __banked +{ + kbd_raw_sync(); /* снять залипшее после Rx-overrun SIO */ + /* control_kid (seg006:0CD1), ПЕРВАЯ строка: HP кончились — персонаж + * мёртв. Без этой ветки смерть от меча стража не доводилась до конца: + * hurt_by_sword играл seq_85, hitp_curr падал в 0, но Kid.alive + * оставался −1 и pop_kid_dead не взводился — Кид досматривал сиквенс, + * возвращался в стойку с нулём HP, и страж убивал его снова по кругу + * (BUG-DEATH-1). pop_kid_dead — наш сигнал главному циклу на респавн + * (в оригинале это счётчик Char.alive в play_kid, seg006:0BEE). */ + ctrl_kid_death(); + rest_ctrl_1(); + read_input(); + read_user_control(); + /* Окно Char вокруг диспетчера: control() работает с АКТИВНЫМ + * персонажем (как в оригинале), поэтому ввод игрока применяется к + * Киду только внутри loadkid/savekid. + * + * load_fram_det_col ОБЯЗАТЕЛЕН здесь — порт play_kid_frame + * (seg000:1209), где он стоит ровно между loadkid_and_opp() и + * play_kid(). cur_frame один на всех персонажей, и в кадре последним + * его перезаписывает страж; без перезагрузки диспетчер считает + * геометрию Кида (dx_weight -> determine_col -> distance_to_edge_weight + * -> get_edge_distance) по кадру СТРАЖА. Замерено: BUG-CTRL-FRAME-1. */ + ctrl_kid_dispatch(); /* -> pop_control(): может сменить Char.curr_seq */ +} + +void pop_ctrl_auto_move(int8_t move) __banked +{ + ctrl_kid_death(); + /* control_kid() оригинала в demo-ветке делает do_demo(); control(); + * БЕЗ rest_ctrl_1/read_user_control/user_control/save_ctrl_1. Команды + * auto_move уже относительны к направлению Char. Если пропустить их + * через user_control(), то при Kid.direction=RIGHT backward зеркалится + * во forward: во второй комнате Кид вместо разворота бежит в пропасть. */ + switch (move) { + case 0: + control_shift = control_y = control_x = control_shift2 = 0; + control_down = control_up = control_backward = control_forward = 0; + break; + case 1: + control_x = POP_CONTROL_HELD_FORWARD; + control_forward = POP_CONTROL_HELD; + break; + case 2: + control_backward = POP_CONTROL_HELD; + control_x = POP_CONTROL_HELD_BACKWARD; + break; + case 3: + control_y = POP_CONTROL_HELD_UP; + control_up = POP_CONTROL_HELD; + break; + case 4: + control_down = POP_CONTROL_HELD; + control_y = POP_CONTROL_HELD_DOWN; + break; + case 5: + control_y = POP_CONTROL_HELD_UP; + control_up = POP_CONTROL_HELD; + control_x = POP_CONTROL_HELD_FORWARD; + control_forward = POP_CONTROL_HELD; + break; + case 6: + control_shift = POP_CONTROL_HELD; + control_shift2 = POP_CONTROL_HELD; + break; + case 7: + control_shift = POP_CONTROL_RELEASED; + break; + default: + break; + } + pop_loadkid(); + pop_load_fram_det_col(); + pop_control(); + pop_savekid_state(); +} diff --git a/applications/SprPoP/src/pop_ctrl.h b/applications/SprPoP/src/pop_ctrl.h new file mode 100644 index 0000000..8964ae0 --- /dev/null +++ b/applications/SprPoP/src/pop_ctrl.h @@ -0,0 +1,77 @@ +/* + * pop_ctrl.h — управление Kid (Фаза K2b): порт диспетчера control() + * (SDLPoP seg005.c) + модель ввода read_user_control/user_control + * (seg006.c) поверх held-state клавиатуры . + * + * Kid-only подмножество: без меча и стражи. Map-запросы (стена/край/ + * захват) ЗАСТАБЛЕНЫ «открытый пол» — реальная коллизия (стоп у стены, + * падение в ямы, повисание) приходит в K3/K4. Даёт отклик на клавиши: + * бег влево/вправо, разворот, стоп, присед, прыжок с места, прыжок в + * беге, присед-прыжок. + * + * Раскладка (kbd_raw, PS/2 Set 2): ←/→ = движение, ↓ = присед, ↑ = + * прыжок вверх (с →), Shift = осторожный шаг (K3). ESC — выход (проверяет + * приложение сам, канал raw забирает весь ввод у DSS). + */ +#ifndef POP_CTRL_H +#define POP_CTRL_H + +#include + +/* МОДУЛЬ ЖИВЁТ В БАНКЕ 5 (--bank 5=pop_ctrl.c), поэтому всё, что зовут + * снаружи, помечено __banked. Зовут с трёх сторон: главный цикл + * (резидент), guards.c (банк 1) и pop_map.c (банк 3) — банк→банк + * работает, трамплин сохраняет страницу окна на стеке. Диспетчер + * дёргается один раз за кадр на персонажа, так что цена перехода + * незаметна. Сама шина control_* лежит в pop_state.c (см. ниже). */ + +/* Открыть raw-канал клавиатуры и обнулить память авто-повтора. + * 0 — OK, -1 — ошибка (EBUSY и пр., см. kbd_raw_open). */ +int pop_ctrl_init(void) __banked; +void pop_ctrl_close(void) __banked; + +/* Один кадр ввода: прочитать клавиши -> обновить control_* -> прогнать + * диспетчер control() (может сменить Kid.curr_seq). Звать ПЕРЕД + * kid_tick() (play_seq продолжит с новой последовательности). */ +void pop_ctrl_tick(void) __banked; + +/* Один кадр синтетического управления attract-demo. move — код SDLPoP: + * 0 nothing, 1 forward, 2 backward, 3 up, 4 down, 5 up+forward, + * 6 shift, 7 отпустить shift, -1 ничего не менять. Raw-клавиатуру не + * читает: это важно, чтобы ввод мог сперва прервать demo. */ +void pop_ctrl_auto_move(int8_t move) __banked; + +/* control_shift зажат ПРЯМО СЕЙЧАС? — для check_grab в физике (pop_map): + * зацеп за уступ в падении по Shift. Валидно после pop_ctrl_tick. */ +int8_t pop_ctrl_shift_held(void) __banked; + +/* ---- Шина синтетического ввода (порт глобалов control_* из seg005) ---- + * В оригинале ИИ соперника (autocontrol_*, seg002) не имеет своего + * диспетчера: он ВЫСТАВЛЯЕТ эти переменные, а дальше исполняется тот же + * control(), что и для игрока. Значения — CONTROL_RELEASED/IGNORE/HELD. */ +#define POP_CONTROL_RELEASED 0 +#define POP_CONTROL_IGNORE 1 +#define POP_CONTROL_HELD (-1) +/* Оси ЭТОГО кадра (абсолютные): X = -1 влево / +1 вправо, Y = -1 вверх / + * +1 вниз. Для ИИ «вперёд/назад» уже в системе персонажа, поэтому оси он + * заполняет как есть, без flip_control_x. */ +/* Определены в pop_state.c (резидент W1/W2), а не в pop_ctrl.c: шину + * трогают три банка сразу, а данные банкового модуля живут в _DATA лишь + * пока сборка идёт без --bank-data. */ +extern int8_t control_x, control_y, control_shift; +#define POP_CONTROL_HELD_FORWARD (-1) +#define POP_CONTROL_HELD_BACKWARD 1 +#define POP_CONTROL_HELD_UP (-1) +#define POP_CONTROL_HELD_DOWN 1 +extern int8_t control_forward, control_backward, control_up, control_down, + control_shift2; + +/* Диспетчер (seg005:252) для АКТИВНОГО персонажа Char. Игрок идёт через + * pop_ctrl_tick (тот делает user_control с зеркалом «вперёд»), страж — из + * своего тика напрямую. */ +void pop_control(void) __banked; + +/* Снять флаг «вступительная тема уровня 1 уже играла». */ +void pop_ctrl_level_start(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_cutscene.c b/applications/SprPoP/src/pop_cutscene.c new file mode 100644 index 0000000..7c61cca --- /dev/null +++ b/applications/SprPoP/src/pop_cutscene.c @@ -0,0 +1,55 @@ +/* + * pop_cutscene.c — общий data-driven шагатель кат-сцен, банк 9 (FG8). + * + * Один вызов = один логический кадр. WAIT съедает текущий шаг сразу, поэтому + * WAIT(1) откладывает следующую видимую команду ровно на один тик. Команды + * без ожидания выдаются по одной: runtime успевает переключить палитру, + * собрать hidden page или сделать fade до следующего шага. + */ +#include +#include "pop_cutscene.h" + +void pop_cutscene_begin(pop_cutscene_t *cs, const pop_cs_cmd_t *script) __banked +{ + cs->script = script; + cs->pc = 0; + cs->waiting = 0; + cs->ended = 0; +} + +void pop_cutscene_abort(pop_cutscene_t *cs) __banked +{ + cs->waiting = 0; + cs->ended = 1; +} + +pop_cs_event_t pop_cutscene_tick(pop_cutscene_t *cs, uint16_t *arg) __banked +{ + const pop_cs_cmd_t *cmd; + + *arg = 0; + if (cs->ended) return POP_CS_EVENT_END; + if (cs->waiting) { + --cs->waiting; + return POP_CS_EVENT_NONE; + } + + cmd = &cs->script[cs->pc++]; + *arg = cmd->arg; + switch (cmd->op) { + case POP_CS_SHOW: return POP_CS_EVENT_SHOW; + case POP_CS_PALETTE: return POP_CS_EVENT_PALETTE; + case POP_CS_FADE_IN: return POP_CS_EVENT_FADE_IN; + case POP_CS_FADE_OUT: return POP_CS_EVENT_FADE_OUT; + case POP_CS_FLASH: return POP_CS_EVENT_FLASH; + case POP_CS_WAIT: + /* Текущий tick — первый из N: это исключает скрытый +1 кадр. */ + cs->waiting = cmd->arg; + if (cs->waiting) --cs->waiting; + return POP_CS_EVENT_NONE; + case POP_CS_END: + default: + cs->ended = 1; + return POP_CS_EVENT_END; + } +} diff --git a/applications/SprPoP/src/pop_cutscene.h b/applications/SprPoP/src/pop_cutscene.h new file mode 100644 index 0000000..d49303b --- /dev/null +++ b/applications/SprPoP/src/pop_cutscene.h @@ -0,0 +1,56 @@ +/* + * pop_cutscene.h — маленький интерпретатор сценариев экранных сцен (FG8). + * + * Данные сценария статичны, состояние — только pc и счётчик ожидания. Это + * пригодится и PV intro, и сценам перед уровнями FG9: графика/звук остаются + * у вызывающего режима, а не тащатся в общий механизм. + */ +#ifndef POP_CUTSCENE_H +#define POP_CUTSCENE_H + +#include + +/* ТИК ОРИГИНАЛА -> НАШ КАДР. Все длительности сцен взяты из SDLPoP в его + * тиках (BASE_FPS = 60), а ждём мы кадрами луча (50 Гц). Без пересчёта + * сцена идёт на 20 % дольше — незаметно, пока она немая, и сразу слышно, + * когда под неё играет музыка: реплика кончается раньше картинки. + * Аргумент — константа сценария, до 2500, так что 32-бит не возникает. */ +#define POP_T60(t) ((uint16_t)((uint16_t)(t) * 5u / 6u)) + +typedef enum { + POP_CS_SHOW = 1, /* arg: номер готового кадра */ + POP_CS_WAIT, /* arg: число логических кадров */ + POP_CS_PALETTE, /* arg: номер палитрового набора */ + POP_CS_FADE_IN, + POP_CS_FADE_OUT, + POP_CS_FLASH, /* arg: длительность белой вспышки */ + POP_CS_END +} pop_cs_opcode_t; + +typedef struct { + uint8_t op; + uint16_t arg; +} pop_cs_cmd_t; + +typedef struct { + const pop_cs_cmd_t *script; + uint8_t pc; + uint16_t waiting; + uint8_t ended; +} pop_cutscene_t; + +typedef enum { + POP_CS_EVENT_NONE = 0, + POP_CS_EVENT_SHOW, + POP_CS_EVENT_PALETTE, + POP_CS_EVENT_FADE_IN, + POP_CS_EVENT_FADE_OUT, + POP_CS_EVENT_FLASH, + POP_CS_EVENT_END +} pop_cs_event_t; + +void pop_cutscene_begin(pop_cutscene_t *cs, const pop_cs_cmd_t *script) __banked; +void pop_cutscene_abort(pop_cutscene_t *cs) __banked; +pop_cs_event_t pop_cutscene_tick(pop_cutscene_t *cs, uint16_t *arg) __banked; + +#endif /* POP_CUTSCENE_H */ diff --git a/applications/SprPoP/src/pop_demo.c b/applications/SprPoP/src/pop_demo.c new file mode 100644 index 0000000..4aaa18c --- /dev/null +++ b/applications/SprPoP/src/pop_demo.c @@ -0,0 +1,73 @@ +/* + * pop_demo.c — воспроизведение attract-demo SDLPoP, банк 9 (FG7). + * + * Это не отдельная физика и не сценарные телепорты. Таблица custom->demo_moves + * подаёт ровно те же control_* в pop_ctrl_auto_move(), через которые идут + * игрок и автоконтроль персонажей. Поэтому демо проверяет настоящий маршрут + * level 0, а не его нарисованную имитацию. + */ +#include +#include +#include "pop_ctrl.h" +#include "pop_guard.h" +#include "pop_map.h" /* hitp_curr — смерть demo имеет приоритет над AI */ +#include "pop_demo.h" + +typedef struct { + uint8_t time; + int8_t move; +} demo_move_t; + +/* data.h SDLPoP: демо пробегает уровень 0 до комнаты 24. Последний -1 + * означает «больше не менять уже поданный control_*», а не ввод. */ +static const demo_move_t demo_moves[] = { + {0x00, 0}, {0x01, 1}, {0x0D, 0}, {0x1E, 1}, {0x25, 5}, + {0x2F, 0}, {0x30, 1}, {0x41, 0}, {0x49, 2}, {0x4B, 0}, + {0x63, 2}, {0x64, 0}, {0x73, 5}, {0x80, 6}, {0x88, 3}, + {0x9D, 7}, {0x9E, 0}, {0x9F, 1}, {0xAB, 4}, {0xB1, 0}, + {0xB2, 1}, {0xBC, 0}, {0xC1, 1}, {0xCD, 0}, {0xE9, -1}, + /* do_auto_moves читает moves[demo_index] ещё после сдвига индекса. + * Это недостижимая метка из data.h, не команда: она держит индекс в + * границах, пока demo_time доходит до 0xFE. */ + {0xFF, -2} +}; + +static uint8_t demo_time; +static uint8_t demo_index; + +void pop_demo_begin(void) __banked +{ + demo_time = 0; + demo_index = 0; +} + +uint8_t pop_demo_tick(void) __banked +{ + uint8_t index; + int8_t move; + + /* Урон мечом сводится ПОСЛЕ этого вызова в предыдущем игровом кадре. + * На следующем тике HP уже ноль, но pop_kid_dead ещё не поднят: обычный + * pop_ctrl_tick делает это в ctrl_kid_death(), а demo обходит его своим + * AI. Без этого гарда AI снова управлял мёртвым Кидом, возвращал его в + * стойку и страж убивал его по кругу. Attract-demo при смерти кончается; + * текущий главный цикл ещё дорисует один последний кадр и уйдёт в title. */ + if (hitp_curr == 0) return 1; + + /* do_demo(): в бою запись времени/кнопок стоит, Кидом управляет тот же + * combat AI, что стражами, но с отдельным skill 10. */ + if (pop_demo_kid_ai()) return 0; + if (demo_time >= 0xFE) return 1; + ++demo_time; + index = demo_index; + if (demo_moves[index].time <= demo_time) ++demo_index; + else index = (uint8_t)(demo_index - 1); + move = demo_moves[index].move; + pop_ctrl_auto_move(move); + return (uint8_t)(demo_time >= 0xFE); +} + +uint8_t pop_demo_user_input(void) __banked +{ + return kbd_raw_any_down(); +} diff --git a/applications/SprPoP/src/pop_demo.h b/applications/SprPoP/src/pop_demo.h new file mode 100644 index 0000000..e4c0002 --- /dev/null +++ b/applications/SprPoP/src/pop_demo.h @@ -0,0 +1,17 @@ +/* pop_demo.h — attract-mode уровня 0, банк 9 (FG7). */ +#ifndef POP_DEMO_H +#define POP_DEMO_H + +#include + +/* Начать неизменяемую запись demo_moves SDLPoP с кадра ноль. */ +void pop_demo_begin(void) __banked; + +/* Один логический кадр записи: подать синтетический control_*. + * Возвращает 1, когда запись исчерпана. */ +uint8_t pop_demo_tick(void) __banked; + +/* «Любой ввод» без покадрового перебора 512 scan-кодов. */ +uint8_t pop_demo_user_input(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_draw.c b/applications/SprPoP/src/pop_draw.c new file mode 100644 index 0000000..6be995e --- /dev/null +++ b/applications/SprPoP/src/pop_draw.c @@ -0,0 +1,32 @@ +/* + * pop_draw.c — выбор ядра libbgi для heal: линейное «noclip», если + * прямоугольник целиком на экране, иначе общее клипающее. Объявление и + * мотивация — `_pop_draw.h`. + * + * ПОЧЕМУ ОТДЕЛЬНЫЙ РЕЗИДЕНТНЫЙ МОДУЛЬ, а не inline в заголовке. Замер + * CLIP-1 (2026-08-01), SDCC 4.5: `static inline` тут обходится втридорога — + * компилятор И встраивает тело в каждый вызов (181 Б на место), И оставляет + * отдельную мёртвую копию в каждом TU. У стража это раздуло + * тогдашний `pop_guard_heal` с ~60 до 663 Б. Пяти местам вызова это стоило бы ~1.4 КБ + * ради экономии ~50 тактов на вызов — при том что BANK2 занят на 86 %. + * + * Резидент W1 (_CODE), а не банк: сюда зовут из ДВУХ разных банков + * (pop_bg.c → BANK2, pop_cdraw.c → BANK4). W1 замаплено всегда, поэтому из + * банка это обычный `call` без трамплина — тот же приём, что у + * pop_sword_draw. + */ +#include +#include +#include "_pop_draw.h" +#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */ + +void pop_heal_fast(int x, int y, int w, int h) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча, см. pop_pace.h */ + if (w <= 0 || h <= 0) return; + if (pop_onscreen_cols(x, y, (uint16_t)w, (uint16_t)h)) + gfx_heal_noclip(x, y, (uint8_t)w, (uint8_t)h); + else + gfx_heal(x, y, w, h); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ /* и ПОСЛЕ: широкий heal сам по себе ~70 000 тактов */ +} diff --git a/applications/SprPoP/src/pop_flow.c b/applications/SprPoP/src/pop_flow.c new file mode 100644 index 0000000..b96fa98 --- /dev/null +++ b/applications/SprPoP/src/pop_flow.c @@ -0,0 +1,48 @@ +/* pop_flow.c — таблица level-flow SDLPoP, банк 9 (FG9). */ +#include +#include "pop_level.h" +#include "pop_flow.h" + +/* Индекс равен номеру следующего уровня. pre_cutscene повторяет + * tbl_cutscenes[] SDLPoP, но тип значения — компактный экранный сценарий, + * а не указатель на игровую функцию. Уровень 14 намеренно скрывает номер. */ +static const pop_level_flow_t level_flow[POP_LEVEL_LAST + 1] = { + { 0, POP_PRE_NONE, 0, 0 }, { 1, POP_PRE_NONE, 1, 0 }, + { 2, POP_PRE_2_6, 1, 0 }, { 3, POP_PRE_NONE, 1, 0 }, + { 4, POP_PRE_4, 1, 0 }, { 5, POP_PRE_NONE, 1, 0 }, + { 6, POP_PRE_2_6, 1, 0 }, { 7, POP_PRE_NONE, 1, 0 }, + { 8, POP_PRE_8, 1, 0 }, { 9, POP_PRE_9, 1, 0 }, + { 10, POP_PRE_NONE, 1, 0 }, { 11, POP_PRE_NONE, 1, 0 }, + { 12, POP_PRE_12, 1, 0 }, { 13, POP_PRE_NONE, 1, 0 }, + { 14, POP_PRE_NONE, 0, 1 } +}; + +const pop_level_flow_t *pop_flow_level(uint8_t level) __banked +{ + if (level > POP_LEVEL_LAST) return 0; + return &level_flow[level]; +} + +pop_pre_cutscene_t pop_flow_pre_cutscene(uint8_t level, + uint8_t remaining_minutes) __banked +{ + pop_pre_cutscene_t scene; + const pop_level_flow_t *flow = pop_flow_level(level); + + if (!flow) return POP_PRE_NONE; + scene = (pop_pre_cutscene_t)flow->pre_cutscene; + /* hourglass_frame(): frame 6 iff `time_bound[0] (6) > rem_min`. */ + if (scene == POP_PRE_12 && remaining_minutes < 6) + return POP_PRE_12_SHORT; + return scene; +} + +uint8_t pop_flow_is_ending(uint8_t level, uint8_t room) __banked +{ + const pop_level_flow_t *flow = pop_flow_level(level); + + /* WIN_LEVEL/WIN_ROOM в SDLPoP: level 14, room 5. Номер комнаты держим + * здесь, а не в горячем игровом цикле, чтобы таблица оставалась + * единственным владельцем специальных переходов. */ + return (uint8_t)(flow && flow->ending_rule && room == 5); +} diff --git a/applications/SprPoP/src/pop_flow.h b/applications/SprPoP/src/pop_flow.h new file mode 100644 index 0000000..47022cc --- /dev/null +++ b/applications/SprPoP/src/pop_flow.h @@ -0,0 +1,40 @@ +/* + * pop_flow.h — таблица маршрута уровней и pre-level cutscene (FG9). + * + * Здесь нет механики rooms/guards: номер назначения превращается в правило + * оболочки один раз на границе уровня. Поэтому добавить особый маршрут не + * означает разбрасывать условия по горячему игровому кадру. + */ +#ifndef POP_FLOW_H +#define POP_FLOW_H + +#include + +typedef enum { + POP_PRE_NONE = 0, + POP_PRE_2_6, + POP_PRE_4, + POP_PRE_8, + POP_PRE_9, + POP_PRE_12, + POP_PRE_12_SHORT +} pop_pre_cutscene_t; + +typedef struct { + uint8_t level; + uint8_t pre_cutscene; + uint8_t show_level_number; + uint8_t ending_rule; +} pop_level_flow_t; + +/* Нулевой указатель означает некорректный номер назначения. Сцена 12 + * выбирается по тем же границам SDLPoP: при <=5 оставшихся минутах короткая. */ +const pop_level_flow_t *pop_flow_level(uint8_t level) __banked; +pop_pre_cutscene_t pop_flow_pre_cutscene(uint8_t level, + uint8_t remaining_minutes) __banked; + +/* Победная комната не принадлежит механике movement: это правило маршрута + * оболочки. Сейчас оно есть только у скрытого уровня 14. */ +uint8_t pop_flow_is_ending(uint8_t level, uint8_t room) __banked; + +#endif /* POP_FLOW_H */ diff --git a/applications/SprPoP/src/pop_geom.c b/applications/SprPoP/src/pop_geom.c new file mode 100644 index 0000000..318e24a --- /dev/null +++ b/applications/SprPoP/src/pop_geom.c @@ -0,0 +1,262 @@ +/* + * pop_geom.c — общая геометрия + PRNG оригинала. См. pop_geom.h. + */ +#include "pop_geom.h" + +/* data.h: шаг 14 (ширина тайла в координатах персонажа), [5] = col 0. */ +const int16_t pop_x_bump[20] = { + -12, 2, 16, 30, 44, 58, 72, 86, 100, 114, + 128, 142, 156, 170, 184, 198, 212, 226, 240, 254 +}; +const int16_t pop_y_land[5] = {-8, 55, 118, 181, 244}; + +#define TILE_SIZEY_OFF 60 /* сдвиг «ряд 0 начинается выше нуля» */ + +/* Медленный хвост — ОТДЕЛЬНОЙ функцией. Иначе SDCC видит одно и то же + * выражение в двух ветках и поднимает деление в вершину функции: быстрые + * возвраты уже не спасают, __divsint зовётся всё равно (проверено — + * 5 вызовов за кадр остались после первой переписи). */ +static int8_t y_row_div(int16_t t) +{ + return (int8_t)(t / TILE_SIZEY % 4 - 1); +} + +int8_t pop_y_to_row(int16_t y) +{ + /* Было `(y + 60) / 63 % 4 - 1` — SDCC разворачивает это в __divsint плюс + * __modsint (а тот внутри снова зовёт __divsint): ~5 400 тактов на вызов + * по замеру в MAME, при том что полос всего четыре. Цепочка сравнений + * повторяет ИМЕННО прежнюю арифметику, включая усечение деления К НУЛЮ + * для отрицательных (t в −62..−1 даёт 0, а не −1). */ + int16_t t = (int16_t)(y + TILE_SIZEY_OFF); + if (t >= 0) { + if (t < TILE_SIZEY) return -1; /* 0 % 4 - 1 */ + if (t < 2*TILE_SIZEY) return 0; + if (t < 3*TILE_SIZEY) return 1; + if (t < 4*TILE_SIZEY) return 2; + if (t < 5*TILE_SIZEY) return -1; /* 4 % 4 = 0 */ + } else if (t > -TILE_SIZEY) { + return -1; /* t/63 == 0 -> 0 % 4 - 1 */ + } + return y_row_div(t); +} + +/* Уложить значение в диапазон 0..maxv. Вызовы оригинала — prandom(1), + * prandom(255), prandom(0xFF): делитель степень двойки, то есть маска, а не + * деление. __moduint на Z80 стоит заметно дороже проверки n & (n-1). */ +static uint16_t pop_rnd_fit(uint16_t v, uint16_t maxv) +{ + uint16_t n = (uint16_t)(maxv + 1); + if ((uint16_t)(n & (uint16_t)(n - 1)) == 0) + return (uint16_t)(v & (uint16_t)(n - 1)); + return (uint16_t)(v % n); +} + +#if POP_PRANDOM_EXACT +/* ---- LCG оригинала: s = s*214013 + 2531011 (seg009:321) ------------ * + * + * ЕДИНСТВЕННОЕ место в порте на ассемблере, и вот почему. На Z80 нет + * умножения, а SDCC для 32-битного `s * 214013` зовёт __mullong — 8 430 + * тактов на вызов (замер в MAME). Написанное на C через 16-битные + * половины это ~3 500. Здесь ~1 000, при полностью той же + * последовательности, что в SDLPoP. Альтернатива «взять генератор + * попроще» (8-битный RND Apple II, xorshift) экономит меньше и ломает + * совместимость с эталоном, по которому сверяем картинку. + * + * Приём: 214013 = ((((1<<1)+1)<<2 + 1)<<4 + 1)<<10 - 3 — схема Горнера по + * РАЗРЕЖЕННОЙ записи константы. Вместо 12 сложений (по числу единиц в + * 0x343FD) — 17 удвоений, ТРИ сложения и одно вычитание. Величина 3*s, + * нужная в конце, попадается по дороге на втором шаге — её и сохраняем. + * + * ABI __sdcccall(1): arg1 (указатель на сид) -> HL, возврат -> DE. IX не + * используется, сохранять нечего. Клоббер AF/BC/DE/HL. Аккумулятор — + * DE:HL (DE старшее слово); s.lo живёт в BC, s.hi и 3*s — в статиках + * (регистров на всё не хватает). + */ +uint16_t pop_pr_ptr; /* адрес сида */ +uint16_t pop_pr_sh; /* старшее слово исходного сида */ +uint16_t pop_pr_3s_lo, pop_pr_3s_hi; /* 3*s, нужное в конце */ + +static uint16_t pop_lcg_step(pop_rnd_t *seed) __naked +{ + (void)seed; + __asm + ld (_pop_pr_ptr), hl + ld c, (hl) + inc hl + ld b, (hl) ; BC = s.lo + inc hl + ld a, (hl) + ld (_pop_pr_sh), a + inc hl + ld a, (hl) + ld (_pop_pr_sh + 1), a ; (pop_pr_sh) = s.hi + + ld h, b + ld l, c + ld de, (_pop_pr_sh) ; акк = s + + ;; акк = акк*2 + s => 3s + add hl, hl + rl e + rl d + add hl, bc + ex de, hl + ld a, (_pop_pr_sh) ; LD A,(nn) флаги НЕ трогает + adc a, l + ld l, a + ld a, (_pop_pr_sh + 1) + adc a, h + ld h, a + ex de, hl + ld (_pop_pr_3s_lo), hl + ld (_pop_pr_3s_hi), de + + ;; акк = акк*4 + s => 13s + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, bc + ex de, hl + ld a, (_pop_pr_sh) + adc a, l + ld l, a + ld a, (_pop_pr_sh + 1) + adc a, h + ld h, a + ex de, hl + + ;; акк = акк*16 + s => 209s + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, bc + ex de, hl + ld a, (_pop_pr_sh) + adc a, l + ld l, a + ld a, (_pop_pr_sh + 1) + adc a, h + ld h, a + ex de, hl + + ;; акк *= 1024 => 214016s (10 удвоений) + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + add hl, hl + rl e + rl d + + ;; акк -= 3s => 214013s + ld bc, (_pop_pr_3s_lo) + or a ; CY = 0 + sbc hl, bc + ld bc, (_pop_pr_3s_hi) ; LD BC,(nn) флаги НЕ трогает + ex de, hl + sbc hl, bc + ex de, hl + + ;; акк += 2531011 = 0x0026_9EC3 + ld bc, #0x9EC3 + add hl, bc + ld bc, #0x0026 + ex de, hl + adc hl, bc + ex de, hl + + ;; сохранить сид, вернуть СТАРШЕЕ слово в DE + push de + ex de, hl ; DE = младшее слово + ld hl, (_pop_pr_ptr) + ld (hl), e + inc hl + ld (hl), d + inc hl + pop de ; DE = старшее слово + ld (hl), e + inc hl + ld (hl), d + ret + __endasm; +} + +uint16_t pop_prandom(pop_rnd_t *seed, uint16_t maxv) +{ + return pop_rnd_fit(pop_lcg_step(seed), maxv); +} + +#else /* !POP_PRANDOM_EXACT — генератор попроще, НЕ совместимый с эталоном */ + +/* xorshift16 + шаг Вейля. Зачем не LCG оригинала: тот 32-битный, и даже + * без __mullong (см. ветку выше) стоит ~7 000 тактов на вызов — при двух + * вызовах за кадр это 3 % бюджета на генератор случайных чисел. Здесь + * умножений нет вовсе, только сдвиги и xor. + * + * Почему НЕ 8-битный RND Apple II (RNDseed := 5*RNDseed + 23 mod 256, + * Prince-of-Persia-Apple-II): он ещё быстрее, но у LCG по модулю 256 + * младшие биты вырождены — бит 0 просто чередуется. Наши вызовы это + * видят: раскладка кладки берёт prandom(1) (один бит) и prandom(4), и + * вместо шума получилась бы правильная шахматка. У xorshift такой + * структуры нет, а стоит он примерно столько же. + * + * Шаг Вейля (+0x9E37, нечётный) нужен ещё и потому, что у чистого + * xorshift ноль — неподвижная точка, а сид кладки (номер комнаты + ряд + + * колонка) вполне может оказаться нулём. + * + * ПОСЛЕДСТВИЕ: последовательность отличается от SDLPoP — другая (но + * статистически такая же) раскладка кладки и другие броски в боёвке. + * Поэтому по умолчанию собирается НЕ эта ветка: после переписывания LCG + * на ассемблер выигрыш от смены генератора почти исчез, а сверять + * картинку с эталоном стало важнее. Ветка оставлена как запасной ход. */ +uint16_t pop_prandom(pop_rnd_t *seed, uint16_t maxv) +{ + uint16_t s = seed->lo; + s ^= (uint16_t)(s << 7); + s ^= (uint16_t)(s >> 9); + s ^= (uint16_t)(s << 8); + s = (uint16_t)(s + 0x9E37u); + seed->lo = s; + seed->hi = 0; /* старшая половина не используется */ + /* Отдаём СТАРШИЙ байт: у него нет короткопериодической структуры + * младших бит, а вызывающие берут от результата именно младшие. */ + return pop_rnd_fit((uint16_t)(s >> 8), maxv); +} + +#endif diff --git a/applications/SprPoP/src/pop_geom.h b/applications/SprPoP/src/pop_geom.h new file mode 100644 index 0000000..67ef250 --- /dev/null +++ b/applications/SprPoP/src/pop_geom.h @@ -0,0 +1,61 @@ +/* + * pop_geom.h — общая геометрия PoP и PRNG оригинала. + * + * Сюда сведено то, что раньше дублировалось по модулям (x_bump/y_land в + * pop_kid и pop_map, y_to_row в pop_bg и pop_map, 32-битный LCG в pop_bg и + * pop_trob). Кроме экономии это ФУНДАМЕНТ bank-safe API: модуль живёт в + * W1/W2 и не трогает графику, поэтому его может звать и главный цикл, и + * резидент W3, и будущий __banked-код стражей (docs/layout_plan_v2.md §4). + */ +#ifndef POP_GEOM_H +#define POP_GEOM_H + +#include + +#define FIRST_ONSCREEN_COLUMN 5 /* индекс col 0 комнаты в x_bump */ +#define TILE_SIZEX 14 /* ширина тайла в координатах персонажа */ +#define TILE_SIZEY 63 /* высота ряда */ + +/* x_bump (data.h): левая граница колонки в координатах персонажа; колонка + * комнаты col = x_bump[col + FIRST_ONSCREEN_COLUMN]. y_land: пол ряда + * (индекс row+1; [0] — ряд «над комнатой»). */ +extern const int16_t pop_x_bump[20]; +extern const int16_t pop_y_land[5]; + +/* y_to_row_mod4 (seg006): ряд по координате y с оборотом mod 4 (−1 = полоса + * над комнатой, 3 = под комнатой). */ +int8_t pop_y_to_row(int16_t y); + +/* prandom (порт seg009:321). Сид ВНЕШНИЙ: у стен (раскладка кладки) и у + * анимаций тайлов (фаза факелов) свои последовательности, смешивать их + * нельзя. + * + * ДВА генератора, выбор через POP_PRANDOM_EXACT (см. pop_geom.c): + * 1 (по умолчанию) — LCG оригинала (s*214013+2531011) БИТ-В-БИТ: та же + * раскладка кладки и те же броски, что в SDLPoP, поэтому по картинке + * можно сверяться с эталоном; + * 0 — xorshift16 + шаг Вейля, без единого умножения. Дешевле ровно на + * 5 646 тактов за кадр, 1.3 % бюджета (замер A/B в MAME, комната 3, + * 175 кадров: 403 632 против 397 986). Последовательность другая — + * статистически эквивалентная, но не та же. + * + * Обе версии считают 16-битными половинами, а не unsigned long: на 32-бит + * умножении SDCC зовёт __mullong, и это 8 430 тактов на вызов (замер в + * MAME, breakpoint на __mullong). + * + * ЗАДЕЛ: разрыв между версиями определяется не алгоритмом, а кодогенерацией + * SDCC — те же генераторы на Z80-ассемблере укладываются в 86..148 тактов. + * Если процент понадобится, сначала переписать в asm ОРИГИНАЛЬНЫЙ LCG (там + * умножение на константу), а менять генератор — только если и этого мало. */ +typedef struct { uint16_t lo, hi; } pop_rnd_t; + +#ifndef POP_PRANDOM_EXACT +#define POP_PRANDOM_EXACT 1 +#endif + +uint16_t pop_prandom(pop_rnd_t *seed, uint16_t maxv); + +/* Сид из 16-битного числа (старшая половина = 0). */ +#define pop_prandom_set(s, v) do { (s).lo = (uint16_t)(v); (s).hi = 0; } while (0) + +#endif diff --git a/applications/SprPoP/src/pop_guard.c b/applications/SprPoP/src/pop_guard.c new file mode 100644 index 0000000..099e852 --- /dev/null +++ b/applications/SprPoP/src/pop_guard.c @@ -0,0 +1,239 @@ +/* + * pop_guard.c — СОСТОЯНИЕ и ЛОГИКА стража (W1/W2). См. pop_guard.h. + * + * Здесь только то, что должно оставаться достижимым из банка (guards.c): + * поля Guard, HP, вход в комнату, чит-убийство, чтение кадра из таблицы. + * ОТРИСОВКА — в pop_cdraw.c (банк 4), общая на всех Char. + */ +#include +#include "pop_guard.h" +#include "pop_geom.h" +#include "pop_level.h" +#include "pop_kid.h" /* Char/play_seq — интерпретатор общий */ +#include "pop_map.h" /* hitp_* — HP Кида */ +#include "pop_state.h" /* pop_immortal — чит бессмертия */ +#include "pop_tile.h" /* POP_TILE_DIV — колонка по X без деления */ +#include "kid_data.h" + +pop_char_t Guard; +uint8_t guardhp_curr, guardhp_max; +int8_t guardhp_delta; +uint8_t pop_guard_hurt; +int8_t can_guard_see_kid; +int8_t is_guard_notice; +uint8_t guard_skill; +int8_t guard_refrac, justblocked, kid_sword_strike; +int8_t offguard, holding_sword; +int8_t pop_united_shadow; /* см. pop_guard.h: 0 / счётчик / −1 */ +uint8_t pop_shadow_init; +int8_t pop_guard_notice_timer; /* фора Джафара (уровень 13) */ + +/* Сид бросков боёвки. В оригинале генератор один на всё, у нас + * последовательности разведены по доменам (кладка / анимация тайлов / бой), + * см. pop_geom.h — иначе перерисовка стены сбивала бы броски стража. */ +pop_rnd_t pop_fight_seed; + +/* Кадр стража (порт cur_frame для Char). Глобальный, а не статик: его + * заполняет логика (W1/W2), а читает отрисовка из резидента W3. */ +kframe pop_gframe; + +uint8_t pop_guard_present(void) +{ + return (uint8_t)(Guard.charid != 0 && Guard.alive < 0 && guardhp_curr != 0); +} + +void pop_guard_reset(void) +{ + Guard.charid = 0; + Guard.alive = 0; + Guard.direction = 0; + guardhp_curr = 0; + guardhp_delta = 0; + can_guard_see_kid = 0; + is_guard_notice = 0; + guard_refrac = justblocked = kid_sword_strike = 0; + offguard = holding_sword = 0; +} + +/* kill guard cheat (seg000:786): + * if (Guard.charid != charid_4_skeleton) { + * guardhp_delta = -guardhp_curr; + * Guard.alive = 0; + * } + * Скелета чит не убивает — он и в оригинале бессмертен. */ +void pop_guard_kill(void) +{ + if (Guard.charid == CHARID_4_SKELETON) return; + if (!pop_guard_present()) return; + /* HP не обнуляем — ставим дельту, её применит pop_do_delta_hp. Дальше + * play_guard увидит «жив, но HP ноль», сам переведёт в alive = 0 и + * вызовет on_guard_killed: брызги, вспышка, а на 13-м уровне — флаг + * «выход разрешён». + * + * ОСОЗНАННОЕ РАСХОЖДЕНИЕ (решение пользователя 2026-08-13, ради скорости + * прогонов): оригинал (seg000:786) ставит здесь ещё и `Guard.alive = 0`, + * а гейт в play_guard (seg006:1490) требует `alive < 0` — то есть у него + * чит убивает стража В ОБХОД события смерти, и победа над Джафаром по + * читу выхода на 14-й уровень НЕ открывает. Для отладки это дорого: + * каждый прогон приходится честно выигрывать бой. Убрав одну строку, + * получаем ровно «как будто убил Кид». На игру не влияет — читы в + * релизной сборке выключены (pop_cheats). */ + guardhp_delta = (int8_t)-guardhp_curr; +} + +/* take_hp (seg006:06BD) — общий для боёвки и физики; см. pop_guard.h. */ +uint8_t pop_take_hp(uint8_t count) +{ + if (Char.charid == CHARID_0_KID) { + /* ЧИТ БЕССМЕРТИЯ, уровень 2 (pop_state.h): гасим ТОЛЬКО мелкий урон + * — падение с двух этажей, придавливание плитой. Мгновенная смерть + * (пики, чомпер, падение с трёх этажей, удар вне боевой стойки) + * приходит с count = 100 и проходит всегда: у читa нет задачи + * отменять смерть, у него задача отменить «минус одно деление». + * Уровень 1 сюда не вмешивается вовсе — он живёт в hurt_by_sword и + * гасит только удары мечом. */ + if (pop_immortal >= 2 && count < 100) return 0; + if (count >= hitp_curr) { hitp_delta = (int8_t)-hitp_curr; return 1; } + hitp_delta = (int8_t)-(int8_t)count; + } else { + if (count >= guardhp_curr) { guardhp_delta = (int8_t)-guardhp_curr; return 1; } + guardhp_delta = (int8_t)-(int8_t)count; + } + return 0; +} + +/* timers (seg003:0735) — кадровые счётчики спецсобытий. + * + * Вспышка слияния с тенью досчитывает до нуля и УХОДИТ В −1: это «уже + * слились», состояние до конца уровня. Именно на −1 смотрят check_shadow + * (больше не поднимать) и «появление плит» в комнатах 2/13 (pop_map). + * Ноль как конечное значение не годится — он неотличим от «не было». */ +void pop_frame_timers(void) +{ + if (pop_united_shadow > 0) { + --pop_united_shadow; + if (pop_united_shadow == 0) pop_united_shadow = -1; + } + if (pop_guard_notice_timer > 0) --pop_guard_notice_timer; +} + +/* do_delta_hp (seg003): применить накопленную за кадр дельту HP и обнулить + * её. Звать раз в кадр ПОСЛЕ тиков персонажей, как в play_frame. */ +void pop_do_delta_hp(void) +{ + int16_t hp; + /* Спецсобытие уровня 12 (seg000:1518): ранил ТЕНЬ — ранил себя. Урон + * не делится, а КОПИРУЕТСЯ: тень теряет 1 HP и Кид теряет 1 HP. Без + * этого бой с тенью теряет смысл — её можно было бы просто зарубить. + * Проверка идёт по слоту соперника (у оригинала — по Opp, но в момент + * do_delta_hp там всегда пара Кид/соперник). */ + if (pop_current_level == SHADOW_FIGHT_LEVEL && + Guard.charid == CHARID_1_SHADOW && guardhp_delta != 0) + hitp_delta = guardhp_delta; + if (hitp_delta) { + hp = (int16_t)hitp_curr + hitp_delta; + if (hp < 0) hp = 0; + if (hp > (int16_t)hitp_max) hp = (int16_t)hitp_max; + hitp_curr = (uint8_t)hp; + /* Брызги и красная вспышка — только на УРОН: и draw_kid (seg008:1643), + * и flash_if_hurt (seg003:785) смотрят именно `hitp_delta < 0`. С + * условием «дельта не ноль» их получало и зелье здоровья (+1). */ + if (hitp_delta < 0) pop_kid_hurt = 1; + hitp_delta = 0; + } + if (guardhp_delta) { + hp = (int16_t)guardhp_curr + guardhp_delta; + if (hp < 0) hp = 0; + if (hp > (int16_t)guardhp_max) hp = (int16_t)guardhp_max; + guardhp_curr = (uint8_t)hp; + if (guardhp_delta < 0) pop_guard_hurt = 1; /* -> брызги поверх стража */ + guardhp_delta = 0; + } +} + +/* check_guard_fallout (seg002:0241): персонаж слота Guard провалился НИЖЕ + * комнаты. Уйти в комнату снизу он не может (у стража нет перехода), и + * оригинал его просто убирает: guards_tile комнаты становится −1, то есть + * стража там больше нет и после возврата. У нас тот же смысл — запомнить + * состояние с tile = 30 («стража в комнате больше нет»). + * + * Ветка ТЕНИ (seg002:0241): она исчезает, только если РЕАЛЬНО падает + * (`action == actions_4_in_freefall`) — иначе тень, зашедшую низко по + * своим делам (уровень 5 идёт по нижнему ряду за зельем), убрало бы с + * экрана на ровном месте. Ветка скелета — ниже, он не гибнет, а + * возрождается. + * + * Полосу HP перерисовывать отдельно не надо: pop_hp_draw сравнивает числа + * и сам увидит, что стража не стало. */ +/* Куда и как возрождается упавший СКЕЛЕТ (seg002:252): если под комнатой, + * из которой он выпал, лежит комната 3 — он появляется там снова. Убить + * его нельзя вовсе, падение лишь переносит. */ +#define SKEL_REAPPEAR_ROOM 3 +#define SKEL_REAPPEAR_X 133 +#define SKEL_REAPPEAR_ROW 1 + +/* actions_4_in_freefall (seg005) — тот же номер, что ACT_FREEFALL в + * pop_map.c; здесь свой define, потому что тот приватный для карты. */ +#define ACT_4_IN_FREEFALL 4 + +void pop_guard_fallout(void) +{ + if (Guard.charid == 0 || Guard.y < 211) return; + if (Guard.charid == CHARID_1_SHADOW) { + /* Тень исчезает только в свободном падении; иначе — не наше дело. */ + if (Guard.action != ACT_4_IN_FREEFALL) return; + Guard.direction = DIR_56_NONE; + Guard.alive = 0; + Guard.action = 0; + Guard.charid = 0; + guardhp_curr = 0; + return; + } + if (Guard.charid == CHARID_4_SKELETON) { + uint8_t down = pop_room_link(Guard.room, 3 /* вниз */); + if (down == SKEL_REAPPEAR_ROOM) { + Guard.room = down; + Guard.x = SKEL_REAPPEAR_X; + Guard.curr_row = SKEL_REAPPEAR_ROW; + Guard.direction = 0; /* dir_0_right */ + Guard.alive = -1; + pop_guard_leave(); /* запомнить его в новой комнате */ + return; + } + } + pop_guard_state_save(Guard.room, 30, 0, 0, 0, 0, 0); + Guard.charid = 0; + Guard.direction = 0; + Guard.alive = 0; + guardhp_curr = 0; +} + +/* load_frame для стража (seg006:0293 ветка charid_2_guard): таблица у + * стражей СВОЯ (frame_tbl_guard), индекс = frame + add_frame − 149, где + * add_frame = 70 для кадров 102..106 (seg006:535). */ +void pop_guard_load_frame(void) +{ + int16_t idx = (int16_t)Guard.frame; + if (idx >= 102 && idx < 107) idx += 70; /* add_frame */ + idx -= 149; + if (idx < 0 || idx >= KID_NGFRAMES) { pop_gframe.image = 255; return; } + pop_kid_data_frame(KID_BIN_GFRAMES_OFF, (uint8_t)idx, (uint8_t *)&pop_gframe); +} + +/* leave_guard (seg002:02F5): вызывать ПЕРЕД сменой комнаты, пока Guard ещё + * держит стража уходящей комнаты. Мёртвого запоминаем вместе с + * последовательностью — тогда при возврате он лежит там же, а не встаёт + * живым. */ +void pop_guard_leave(void) +{ + if (Guard.charid == 0) return; /* стража не было */ + /* Тень и МЫШЬ в данные уровня не записываются (seg002:02F5): они не + * «стражи комнаты», а спецсобытия — вернувшись, их надо получить заново + * от check_shadow / check_mouse, а не поднять из guards_tile. */ + if (Guard.charid == CHARID_1_SHADOW || Guard.charid == CHARID_24_MOUSE) return; + pop_guard_state_save(Guard.room, + (uint8_t)(Guard.curr_row * 10), /* get_tilepos(0,row) */ + Guard.direction, Guard.x, guard_skill, + Guard.curr_seq, + (uint8_t)(Guard.alive >= 0)); +} diff --git a/applications/SprPoP/src/pop_guard.h b/applications/SprPoP/src/pop_guard.h new file mode 100644 index 0000000..d747acf --- /dev/null +++ b/applications/SprPoP/src/pop_guard.h @@ -0,0 +1,393 @@ +/* + * pop_guard.h — СОСТОЯНИЕ стража и точки входа в его логику. + * + * Разделение по окнам (docs/layout_plan_v2.md §4, подтверждено пробником + * tests/w3bankgfx): + * - СОСТОЯНИЕ живёт здесь, в W1/W2. Писучие статики __banked-модуля + * линкуются В СТРАНИЦУ БАНКА и снаружи не читаются, поэтому банк — + * только код; + * - ЛОГИКА (ИИ, боёвка) — в guards.c, собираемом как --bank 1=guards.c; + * - ОТРИСОВКА стража — в pop_cdraw.c (банк 4), общая на всех Char: её зовёт + * только главный цикл, а из банка резидент недостижим. Так код + * отрисовки не занимает дефицитные W1/W2 (куча). + * + * Поля char_type — как в оригинале (types.h:302), чтобы порт seg005/seg006 + * ложился один в один. + */ +#ifndef POP_GUARD_H +#define POP_GUARD_H + +#include +#include "kid_data.h" /* kframe */ +#include "pop_char.h" +#include "pop_geom.h" /* pop_rnd_t — сид бросков боёвки */ +#include "pop_qsave.h" + +/* seqids (types.h:1052+) — последовательности боя и стойки. */ +#define SEQ_55_DRAW_SWORD 55 +#define SEQ_56_GUARD_FORWARD 56 /* шаг вперёд с мечом (страж) */ +#define SEQ_57_BACK_WITH_SWORD 57 +#define SEQ_58_GUARD_STRIKE 58 +#define SEQ_60_TURN_WITH_SWORD 60 +#define SEQ_61_PARRY_AFTER_STRIKE 61 +#define SEQ_62_PARRY 62 +#define SEQ_66_STRIKE_AFTER_PARRY 66 +#define SEQ_71_DYING 71 +#define SEQ_75_STRIKE 75 /* удар (Кид) */ +#define SEQ_63_GUARD_ACTIVE_AFTER_FALL 63 /* стойка АКТИВНОГО (скелет) */ +#define SEQ_77_GUARD_STAND_INACTIVE 77 +#define SEQ_88_SKEL_WAKE_UP 88 /* скелет встаёт (спецсобытие) */ +#define SEQ_80_STAND_FLIPPED 80 +#define SEQ_86_FORWARD_WITH_SWORD 86 /* шаг вперёд с мечом (Кид) */ +#define SEQ_87_GUARD_BECOME_INACT 87 +#define SEQ_90_EN_GARDE 90 +#define SEQ_92_PUT_SWORD_AWAY 92 +#define SEQ_93_PUT_SWORD_AWAY_FAST 93 + +/* frameids (types.h:911+) и состояния меча (types.h:332). */ +#define FRAME_150_PARRY 150 +#define FRAME_151_STRIKE_1 151 +#define FRAME_152_STRIKE_2 152 +#define FRAME_153_STRIKE_3 153 +#define FRAME_157_WALK_WITH_SWORD 157 +#define FRAME_158_STAND_WITH_SWORD 158 +#define FRAME_161_PARRY 161 +#define FRAME_162_BLOCK_TO_STRIKE 162 +#define FRAME_165_WALK_WITH_SWORD 165 +#define FRAME_166_STAND_INACTIVE 166 +#define FRAME_167_BLOCKED 167 +#define FRAME_168_BACK 168 +#define FRAME_169_BEGIN_BLOCK 169 +#define FRAME_170_STAND_WITH_SWORD 170 +#define FRAME_171_STAND_WITH_SWORD 171 +#define SWORD_0_SHEATHED 0 +#define SWORD_2_DRAWN 2 + +/* charids (types.h:320) — нужны хотя бы для «скелет не убивается читом». */ +#define CHARID_0_KID 0 +#define CHARID_1_SHADOW 1 +#define CHARID_2_GUARD 2 +/* charid_3 не используется: Джафар (уровень 13) — обычный charid_2_guard, + * своим charid оригинал помечает ТОЛЬКО скелета (enter_guard, seg002:196). + * Отличают Джафара набор спрайтов (VIZIER.DAT), skill 9 и три события. */ +#define CHARID_4_SKELETON 4 +#define CHARID_5_PRINCESS 5 +/* Джафар КАТСЦЕН (не игровой Джафар уровня 13!): в сценах intro он ходит + * по той же таблице кадров, что и Princess (frame_tbl_cuts), только свой + * набор спрайтов. */ +#define CHARID_6_VIZIER 6 +/* МЫШЬ (спецсобытие уровня 8) — тоже персонаж слота Guard: своей таблицы + * кадров у неё нет, кадры 186..188 лежат в таблице КИДА (load_frame, + * seg006:0293 — общий case с charid_0_kid), а спрайты (images 130..132) — + * в атласах Кида, страница kid16.atl. Поэтому отрисовка её подхватывает + * без единой правки: pop_frame_tbl_is_guard(24,…) отдаёт 0. */ +#define CHARID_24_MOUSE 24 + +/* dir_56_none (types.h:402): «направления нет» — им clear_char убирает + * персонажа со сцены. Значение 0x56 в int8_t = 86, то есть заведомо не + * dir_0_right (0) и не dir_FF_left (-1). */ +#define DIR_56_NONE 0x56 + +/* Спецсобытие «зеркало» (custom->mirror_*, дефолты SDLPoP): уровень 4, + * комната 4, колонка 4, верхний ряд. Здесь, а не в pop_trob.c: константы + * нужны и постановке тайла (банк 6), и ИИ тени (банк 1). */ +#define MIRROR_LEVEL 4 +#define MIRROR_ROOM 4 +#define MIRROR_COL 4 +#define MIRROR_ROW 0 +#define MIRROR_TILEPOS (MIRROR_ROW * 10 + MIRROR_COL) + +/* Спецсобытие «тень крадёт зелье» (custom->shadow_steal_*, дефолты SDLPoP): + * уровень 5, комната 24. Тень появляется при ВХОДЕ в комнату, если зелье + * (колонка 3, верхний ряд) ещё на месте; ждёт, пока откроются ворота + * (колонка 1, верхний ряд), идёт к зелью, выпивает его и уходит за левый + * край. Боя нет. Как и MIRROR_*, константы нужны в двух банках. */ +#define SHADOW_STEAL_LEVEL 5 +#define SHADOW_STEAL_ROOM 24 +#define SHADOW_POTION_TILEPOS 3 /* (кол 3, ряд 0) — зелье */ +#define SHADOW_DOOR_TILEPOS 1 /* (кол 1, ряд 0) — ворота */ +#define SHADOW_DOOR_OPEN_MIN 80 /* openness, при которой тень идёт */ + +/* Спецсобытие «тень захлопывает решётку» (custom->shadow_step_*, дефолты + * SDLPoP): уровень 6, комната 1. Кид жмёт opener (1,7), решётка (1,2) + * поднимается, он прыгает через пропасть и цепляется за её порожек — и + * ровно в этот момент тень делает ОДИН осторожный шаг с границы (1,0)-(1,1) + * на closer (1,1), роняя решётку. Обе плиты комнаты ведут на тот же тайл + * 12 (проверено по LINKLOC/LINKMAP уровня). */ +#define SHADOW_STEP_LEVEL 6 +#define SHADOW_STEP_ROOM 1 + +/* Спецсобытие «выход/вход падением» (custom->falling_*, дефолты SDLPoP). + * Уровень 6 не заканчивается дверью: Кид проваливается вниз из комнаты 1 и + * этим переходит на уровень 7, где появляется в комнате 17 — но экран сразу + * переводится на комнату ПОД ней, так что он влетает сверху. Комната 3 + * (шахта под комнатой 1) в переходе не участвует: leave_room перехватывает + * уход раньше, чем сработала бы связь вниз. */ +#define FALLING_EXIT_LEVEL 6 +#define FALLING_EXIT_ROOM 1 +#define FALLING_ENTRY_LEVEL 7 +#define FALLING_ENTRY_ROOM 17 + +/* Комната, которую проходят СВЕРХУ ВНИЗ спуском через зацеп: Кид висит на + * кромке кнопки (0,2), отпускает и цепляется за кромку (2,2). Нужна ЧИТУ + * навигации (`+`/`−` в sprpop.c): он ставит Кида на нижний пол, а отсюда + * этот спуск не проверить — для этой комнаты старт в ряду 0. */ +#define GRAB_DOWN_LEVEL 7 +#define GRAB_DOWN_ROOM 14 + +/* Единственный уровень, где тень — БОЕВОЙ персонаж (seg003:702). На всех + * остальных (4 зеркало, 5 зелье, 6 плита) она Кида «не видит»: см. + * pop_check_can_guard_see_kid. + * + * Сцена целиком (seg002:0070 + seg002:1184): в комнате 15 лежит МЕЧ (0,1); + * пока он на месте, тень не появляется. Кид его берёт — тень падает в ту + * же комнату, и дальше либо бой (у обоих клинки), либо, если Кид уберёт + * меч, — слияние. Комната 18 при уходе ВПРАВО «съедает» меч с карты + * (sword_disappears), чтобы вернуться и подобрать его второй раз было + * нельзя. Проверено по res2012.bin, ../docs/levels_12_15_plan.md §7.3. */ +#define SHADOW_FIGHT_LEVEL 12 +#define SHADOW_FIGHT_ROOM 15 +#define SHADOW_SWORD_TILEPOS 1 /* (кол 1, ряд 0) — меч уровня 12 */ +#define SWORD_GONE_ROOM 18 /* уход ВПРАВО отсюда убирает меч */ +/* Слияние (autocontrol_shadow_level12): дистанция, вспышка, её длительность. */ +#define SHADOW_UNITE_DIST 10 +#define SHADOW_UNITE_TIME 42 + +/* Уровень 13 — ДЖАФАР (custom->jaffar_*, дефолты SDLPoP). Своего ИИ у него + * нет вовсе: `autocontrol_Jaffar` (seg002:085A) — это `autocontrol_guard`, + * то есть обычный бой стражи. Отличают его только данные (VIZIER.DAT, + * skill 9, 6 HP — всё уже в pop_level_cold.c) и три события ниже. + * + * Встреча: Кид уходит ВПРАВО из комнаты 3 — а `комната 3.right = 1`, где + * Джафар и стоит (проверено по res2013.bin, ../docs/levels_12_15_plan.md + * §7.2). Ему даётся фора JAFFAR_NOTICE_TIME кадров: пока таймер идёт, он не + * встаёт в боевую стойку, и это единственная пауза во всей сцене. + * + * Выход: смерть Джафара сама дверь НЕ открывает — она ставит + * pop_leveldoor_open = 2, а дверь открывает КНОПКА комнаты 24 (tilepos 0, + * реальный opener), «нажимаемая» при уходе Кида ВЛЕВО. */ +#define JAFFAR_LEVEL 13 +#define JAFFAR_MEET_ROOM 3 /* уход ВПРАВО отсюда будит Джафара */ +#define JAFFAR_NOTICE_TIME 28 /* ≈2,3 с форы (28/12) */ +#define JAFFAR_EXIT_ROOM 24 +#define JAFFAR_EXIT_TILEPOS 0 +#define JAFFAR_FLASH_TIME 18 + +/* Градаций мастерства стража (seg002: таблицы вероятностей по 12 записей). */ +#define NUM_GUARD_SKILLS 12 + +/* Тип персонажа — в pop_char.h (общий с Кидом и Char). */ + +extern pop_char_t Guard; /* страж ТЕКУЩЕЙ комнаты (как в оригинале) */ +extern uint8_t guardhp_curr, guardhp_max; +extern int8_t guardhp_delta; /* <0 = в этом кадре отняли HP */ + +/* take_hp (seg006:06BD): отнять count HP у АКТИВНОГО персонажа (Char) — + * Киду из `hitp_*`, любому другому из `guardhp_*`. Возврат 1 = HP кончились + * (для вызывающего это «умер»); саму дельту применяет pop_do_delta_hp раз в + * кадр, как в оригинале. + * + * Живёт в резиденте W1/W2, потому что зовут ОБА банка: боёвка (guards.c, + * банк 1) и физика (pop_map.c, банк 3) — раньше у каждого была своя копия, + * причём у pop_map неверная (правила hitp_curr напрямую, мимо дельты, и не + * знала про не-Кидов). Чит бессмертия ловится здесь же. */ +uint8_t pop_take_hp(uint8_t count); + +/* Взводится в кадре, когда СТРАЖУ убавили HP (guardhp_delta < 0 оригинала): + * по нему pop_char_draw рисует «брызги» удара, как draw_guard в seg008:1654. + * Снимает главный цикл после отрисовки — стража могло не оказаться в + * отрисованной комнате, и тогда флаг обязан просто пропасть. */ +extern uint8_t pop_guard_hurt; + +/* Видит ли страж Кида (порт can_guard_see_kid, seg003:688): + * 0 — не видит, 1 — видит, но не пойдёт (между ними loose/чомпер/дыра), + * 2 — видит и пойдёт. Считает pop_check_can_guard_see_kid (банк) раз в + * кадр ДО логики персонажей; читают ИИ (autocontrol_*) и боёвка. */ +extern int8_t can_guard_see_kid; + +/* Кид «нашумел» (порт is_guard_notice, seg000:364): заставляет неактивного + * стража обернуться, даже если Кид у него за спиной. Ставит источник шума, + * снимает сам ИИ. */ +extern int8_t is_guard_notice; + +/* Боевые таймеры/уровень мастерства (seg000:295..311). Все убывают раз в + * кадр в autocontrol_opponent — как в оригинале. + * guard_skill — 0..11, из данных уровня; индекс в таблицы вероятностей; + * guard_refrac — «отдышка» стража: пока >0, он не атакует и не наступает; + * justblocked — Кид только что парировал (влияет на вероятность блока); + * kid_sword_strike — Кид только что бил (стража это придерживает). */ +extern uint8_t guard_skill; +extern int8_t guard_refrac, justblocked, kid_sword_strike; + +/* Состояние меча КИДА (seg005/seg006): + * offguard — меч убран НАМЕРЕННО (вниз в бою): пока стоит, Кид не + * достаёт клинок автоматически, даже видя стража; + * holding_sword — клинок в руке (для индикатора HP стража). */ +extern int8_t offguard, holding_sword; + +/* do_demo() SDLPoP: когда у Кида вынут меч, scripted demo_moves временно + * заменяются боевым AI skill 10, после чего skill 11 остаётся стражу. + * Возврат 1 — AI применён; 0 — меч ещё не вынут, продолжать таблицу. */ +uint8_t pop_demo_kid_ai(void) __banked; + +/* Спецсобытие уровня 12 — СЛИЯНИЕ С ТЕНЬЮ (united_with_shadow, seg000:288). + * Значение читается как три разных состояния, и это НЕ флаг: + * 0 — тень ещё отдельный персонаж (или уровень не 12); + * >0 — идёт вспышка слияния, счётчик кадров (42 → 1), убывает в + * pop_shadow_timer; пока он идёт, Кид мигает спрайтами тени; + * −1 — «уже слились», навсегда до конца уровня. Именно по нему + * check_shadow больше не поднимает тень, а в комнатах 2/13 + * появляются недостающие плиты пола. + * Переход 0 → −1 идёт ЧЕРЕЗ счётчик: таймер, досчитав до нуля, уводит его + * ещё на единицу вниз (seg003:503) — отсюда и знаковый тип. + * + * pop_shadow_init (shadow_initialized, seg000:309) — «тень уже дождалась + * Кида в комнате 15»: до этого autocontrol_shadow_level12 роняет её заново + * каждый кадр, чтобы Кид застал падение, а не готовую стойку. */ +extern int8_t pop_united_shadow; +extern uint8_t pop_shadow_init; + +/* Сид бросков боёвки (свой домен последовательности, см. pop_geom.h). */ +extern pop_rnd_t pop_fight_seed; + +/* Луч видимости стража — раз в кадр, ДО тиков персонажей (в оригинале + * check_can_guard_see_kid стоит в play_frame перед play_kid_frame). */ +void pop_check_can_guard_see_kid(void) __banked; + +/* check_skel (seg002:1042) — СПЕЦСОБЫТИЕ уровня 3: скелета нет в данных + * уровня (guards_tile пуст во всех комнатах), он поднимается сам, когда Кид + * доходит до нужной колонки комнаты 1 при открытой двери уровня. Звать раз + * в кадр, ДО логики персонажей (порядок play_frame, seg000:868). */ +void pop_check_skel(void) __banked; + +/* Спецсобытие уровня 8 — МЫШЬ (do_timers seg003:0545 + do_mouse seg003:0ABA). + * Условие запуска: дверь уровня открыта И Кид в комнате 16; тогда счётчик + * pop_leveldoor_open растёт кадрами, и на 150-м мышь появляется у правого + * края верхнего ряда. Звать раз в кадр рядом с pop_check_skel. */ +void pop_check_mouse(void) __banked; + +/* check_shadow (seg002:0064) — СПЕЦСОБЫТИЕ входа в комнату: на «шадоу»- + * уровнях слот соперника занимает ТЕНЬ, а не страж из данных. Звать ВМЕСТО + * pop_guard_enter при входе в комнату; вернула 1 — событие обработано и + * стража поднимать НЕ надо. + * + * Тонкость оригинала, которую легко потерять: на уровне 5 в комнате 24 + * страж В ДАННЫХ ЕСТЬ (`guards_tile[23] = 8`), но enter_guard там не + * вызывается НИКОГДА — ни когда тень поднимается, ни когда зелье уже + * выпито (в обоих случаях seg002:0064 делает return). Поэтому 1 + * возвращается и во втором случае тоже. */ +uint8_t pop_check_shadow(uint8_t room) __banked; + +/* sword_disappears (seg002:0536) — спецсобытие уровня 12: уход ВПРАВО из + * комнаты 18 убирает меч из комнаты 15. Звать при уходе вбок ДО смены + * комнаты, рядом с чекпойнтом уровня 3. */ +void pop_sword_disappears(void) __banked; + +/* meet_Jaffar (seg002:0544) — уход ВПРАВО из комнаты 3 уровня 13 даёт + * Джафару фору; звать рядом с pop_sword_disappears (та же ветка leave_room). + * Jaffar_exit (seg002:0517) — уход ВЛЕВО при pop_leveldoor_open == 2 жмёт + * кнопку комнаты 24 и открывает дверь уровня. */ +void pop_meet_jaffar(void) __banked; +void pop_jaffar_exit(void) __banked; + +/* check_killed_shadow (seg006:2033) — спецсобытие уровня 12: смерть тени + * убивает Кида и наоборот. Звать раз в кадр ДО тиков персонажей, рядом с + * pop_check_skel; на прочих уровнях выходит первой строкой. */ +void pop_check_killed_shadow(void) __banked; + +/* timers (seg003:0735) — кадровые счётчики спецсобытий: вспышка слияния с + * тенью и фора Джафара. Звать раз в кадр из главного цикла. Резидент: два + * вычитания, банк ради них не разворачиваем. */ +void pop_frame_timers(void); + +/* guard_notice_timer (seg000:307) — фора Джафара после встречи: пока >0, он + * не встаёт в боевую стойку. Ставит pop_meet_jaffar, убавляет + * pop_frame_timers, читает autocontrol_guard_inactive. */ +extern int8_t pop_guard_notice_timer; + +/* Боёвка: пометить попадания (обе стороны) и применить их. Порядок в + * кадре как в play_frame: hurting -> hurt -> do_delta_hp. */ +void pop_check_sword_hurting(void) __banked; +void pop_check_sword_hurt(void) __banked; + +/* Текущий кадр стража (порт cur_frame для Char): заполняет логика в W1/W2, + * читает отрисовка в резиденте W3. image == 255 — рисовать нечего. */ +extern kframe pop_gframe; +void pop_guard_ai_qsave(pop_qs_io_t *io) __banked; +void pop_guard_load_frame(void); + +/* check_guard_fallout (seg002:0241): убрать персонажа слота Guard, если он + * провалился ниже комнаты. Звать раз в кадр после физики и HP — как + * play_frame (seg000:884). */ +void pop_guard_fallout(void); + +/* Есть ли живой страж в отрисованной комнате (для главного цикла). */ +uint8_t pop_guard_present(void); + +/* Атласы стража (GUARD\\g0..g4.atl) — грузить ДО initgraph, как Kid. + * 0 — OK, -1 — ошибка (тогда страж просто не рисуется). */ +int pop_guard_load(void) __banked; + +/* Зеркальный ПО ВЕРТИКАЛИ набор соперника (файлы *_v.atl) — для зелья + * инверсии; зовётся из pop_vflip_load_all (pop_vflip.h). Живёт рядом с + * pop_guard_load: там же gp[] и выбор GUARD/SKEL. */ +int pop_guard_vflip_load(void) __banked; + +/* Сброс «одноразовых» музыкальных тем уровня (вступление 1-го, тень 6-го). + * Зовёт pop_start_level. */ +void pop_music_level_start(void) __banked; +void pop_guard_free(void) __banked; + +/* Вход в комнату: поднять стража из данных уровня (порт enter_guard, + * seg002:0112) либо убрать его, если в комнате стража нет. */ +void pop_guard_enter(uint8_t room); + +/* Запомнить стража УХОДЯЩЕЙ комнаты (порт leave_guard). Звать в начале + * enter_room, пока Guard ещё держит прежнего. */ +void pop_guard_leave(void); + +/* Страж идёт ЗА КИДОМ в соседнюю комнату — порт ветки стража из exit_room + * (seg002:0340) + follow_guard (seg002:039E). Звать ВМЕСТО пары + * leave/enter при смене комнаты: 1 = страж уже перенесён в new_room (ни + * leave, ни enter звать нельзя), 0 = остаётся в старой (всё как раньше). + * side — куда ушёл КИД: 0 left, 1 right, 2 up, 3 down. */ +uint8_t pop_guard_follow(uint8_t new_room, uint8_t side); + +/* Полоса HP (Кид слева, страж справа) в статус-полосе. Рисует главный цикл + * ПОСЛЕ персонажей — она ничего не перекрывает. */ +void pop_hp_draw(void) __banked; +/* Полоса стёрта перерисовкой фона (вход в комнату) — обновить обе страницы. */ +void pop_hp_invalidate(void) __banked; + +/* Цвет стража — подменой 16 слотов палитры (порт set_chtab_palette, + * seg003:257): спрайты лежат в индексах POP_GUARD_PAL_BASE + v, а цвет + * задаёт level.guards_color[room-1] (1..7; 0 = не менять). Звать ДО + * отрисовки комнаты, как это делает redraw_screen. */ +void pop_guard_set_palette(uint8_t color) __banked; + +/* Пере-применить палитру ТЕКУЩЕГО набора соперника (скелет/Джафар — своя из + * файла набора, обычный страж — последний curr_guard_color). Диапазон + * 0x90..0x9F динамический: kid.pal затирает его палитрой стража, поэтому + * каждый file-load логической палитры доплачивает его заново — так же, как + * pop_bg_pal_apply/pop_shadow_pal_apply. Звать ДО snapshot. */ +void pop_guard_pal_apply(void) __banked; + +/* Отрисовка соперника — общая на всех Char: pop_char_draw(POP_CH_OPP) и + * компания, см. pop_cdraw.h. Метрики его последнего кадра (в т.ч. ширина + * спрайта для char_width_half) — pop_cd[POP_CH_OPP]. */ + +/* Убрать стража со сцены (смена комнаты / инициализация). */ +void pop_guard_reset(void); + +/* Чит «убить стража» (seg000:786, K): скелета не берёт. Вызывать только + * при включённых читах — проверку делает вызывающий. */ +void pop_guard_kill(void); + +/* do_delta_hp (seg003): применить дельту HP, накопленную за кадр. Звать + * раз в кадр ПОСЛЕ тиков персонажей (в оригинале — в play_frame). */ +void pop_do_delta_hp(void); + +/* Тик логики стража — В БАНКЕ (guards.c). Состояние берёт отсюда. */ +void pop_guard_tick(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_guard_cold.c b/applications/SprPoP/src/pop_guard_cold.c new file mode 100644 index 0000000..35d7f63 --- /dev/null +++ b/applications/SprPoP/src/pop_guard_cold.c @@ -0,0 +1,183 @@ +/* + * pop_guard_cold.c — переходы стража МЕЖДУ КОМНАТАМИ (банк 8): страж идёт + * за Кидом (follow_guard, seg002:039E) и вход стража в комнату + * (enter_guard, seg002:0112 + pos_guards, seg003). + * + * Обе зовутся РАЗ НА КОМНАТУ и обе — из sprpop_cold.c, который лежит в + * том же банке 8: значит в резиденте им делать нечего, а трамплин на + * вызове не появляется (объявления в pop_guard.h без __banked; условие + * корректности — оба модуля в одном банке, см. _pop_kdraw.h про ту же + * связку). Состояние стража (Guard, guard_skill, …) живёт в pop_guard.c + * и видно отсюда как обычные глобалы: данные банков — в общем _DATA. + */ +#include +#include "pop_guard.h" +#include "pop_geom.h" +#include "pop_level.h" +#include "pop_kid.h" /* Char/play_seq — интерпретатор общий */ +#include "pop_map.h" +#include "pop_state.h" +#include "pop_tile.h" /* POP_TILE_DIV — колонка по X без деления */ +#include "kid_data.h" + +static int8_t guard_col_from_x(uint8_t xpos); /* определена ниже */ + +/* ---- Страж ИДЁТ ЗА КИДОМ в соседнюю комнату ------------------------- * + * Порт ветки стража из exit_room (seg002:0340..0382) + follow_guard + * (seg002:039E). Своим ходом персонаж слота Guard из комнаты не уходит + * (его физика гейтится x ∈ [44,211), см. pop_guard_phys_tick), но когда + * комнату меняет КИД, движок может перенести стража следом — именно так в + * оригинале страж из комнаты 3 оказывается в комнате 2 за отступающим + * Кидом. + * + * Условия оригинала, все обязательные: + * - страж есть, ЖИВ и с ВЫНУТЫМ МЕЧОМ (то есть идёт бой); + * - переход ВБОК: вверх и вниз оригинал не пускает никогда (там условия + * `curr_row >= 0` и `curr_row < 3` истинны всегда → leave_guard); + * - в целевой комнате НЕТ своего ЖИВОГО стража (иначе их стало бы два: + * проверка `guards_tile >= 30 || guards_seq_hi != 0` — «пусто или + * труп»); + * - страж прижат к тому краю, в который ушёл Кид: x < 91 при уходе + * влево, x >= 165 при уходе вправо. + * + * side — куда ушёл КИД (0 = left, 1 = right, 2 = up, 3 = down), то есть + * roomleave_result оригинала. Возврат: 1 — страж перенесён в new_room + * (вызывающий НЕ должен звать ни pop_guard_leave, ни pop_guard_enter), + * 0 — остаётся, всё как раньше. */ +uint8_t pop_guard_follow(uint8_t new_room, uint8_t side) +{ + uint8_t tile, color, skill; + int8_t dir; + + if (Guard.charid == 0) return 0; /* dir_56_none */ + if (Guard.alive >= 0 || Guard.sword != SWORD_2_DRAWN) return 0; + if (side > 1) return 0; /* только вбок */ + /* «В целевой комнате уже есть живой страж» = страж по данным есть И это + * не запомненный труп (у трупа сохранена последовательность). */ + if (pop_level_guard(new_room, &tile, &dir, &color, &skill) && + pop_guard_state_seq(new_room) == 0) return 0; + if (side == 0) { if (Guard.x >= 91) return 0; } /* Кид ушёл влево */ + else { if (Guard.x < 165) return 0; } /* Кид ушёл вправо */ + + /* follow_guard: сначала «стража нет» в ОБЕИХ комнатах (в оригинале + * guards_tile = 0xFF). Без этого он раздвоится: в старой комнате его + * поднимут из данных при возврате, а в новой — при следующем входе. */ + pop_guard_state_save(Guard.room, 30, 0, 0, 0, 0, 0); + pop_guard_state_save(new_room, 30, 0, 0, 0, 0, 0); + + /* goto_other_room (seg002:0486) для стража: комната + перенос X через + * шов (влево +140, вправо −140). Ряд при боковом переходе не меняется; + * колонку пересчитываем из новой X, как enter_guard. */ + Guard.room = new_room; + Guard.x = (uint8_t)(side == 0 ? Guard.x + 140 : Guard.x - 140); + Guard.curr_col = guard_col_from_x(Guard.x); + return 1; +} + +/* get_tile_div_mod_m7 (seg006:697): колонка комнаты по X персонажа — + * floor((xpos − 7 − 58) / 14), где 58 = pop_x_bump[FIRST_ONSCREEN_COLUMN]. */ +static int8_t guard_col_from_x(uint8_t xpos) +{ + /* Таблицей (резидентная POP_TILE_DIV: −58 уже внутри значений) — тот же + * приём, что в tile_at_kid/get_tile_div_mod. Здесь он кроет ПОЧТИ всё: + * xpos — байт, так что медленный хвост остаётся только для xpos < 7. */ + if (xpos >= 7) return POP_TILE_DIV[xpos - 7]; + { + int16_t x = (int16_t)xpos - 7 - pop_x_bump[FIRST_ONSCREEN_COLUMN]; + int16_t col = x / TILE_SIZEX; + if (x % TILE_SIZEX < 0) --col; + return (int8_t)col; + } +} + +void pop_guard_enter(uint8_t room) +{ + uint8_t tile, color, skill; + int8_t dir; + pop_guard_reset(); + /* load_lev_spr (seg000:1112): tbl_guard_type == −1 — стражей на уровне + * НЕТ вообще (14), их графика даже не грузится. Без этой проверки + * на таком уровне полезли бы стражи из данных комнат. */ + if (pop_level_guard_type(pop_current_level) < 0) return; + if (!pop_level_guard(room, &tile, &dir, &color, &skill)) return; + /* Цвет — ДО отрисовки комнаты (как redraw_screen, seg003:255): слоты + * палитры общие на экран, и смена посреди кадра дала бы стража в новой + * палитре с полосой HP в старой. + * + * Цвет из данных комнаты берётся ТОЛЬКО у обычного стража (enter_guard, + * seg002:183: `curr_guard_color = 0`, если tbl_guard_type != 0). У + * скелета и Джафара палитра своя, из файла набора, и подменять её + * нельзя — иначе визирь на уровне 13 покрасился бы в цвет стража, + * записанный в его комнате. */ + pop_guard_set_palette(pop_level_guard_type(pop_current_level) == 0 + ? color : 0); + /* enter_guard (seg002:0112). Из ТАЙЛА берётся только РЯД — колонки в нём + * нет: leave_guard кладёт туда get_tilepos(0, row), то есть обнуляет её + * намеренно. Носитель колонки — guards_x, её пишет тот же leave_guard, а + * при загрузке уровня заполняет pos_guards (см. pop_gstate_init). + * + * Считать X из `tile % 10`, как мы делали, значит после ПЕРВОГО же выхода + * из комнаты телепортировать живого стража в КОЛОНКУ 0 — потому что там + * с этого момента ноль. Уровень 8, комната 24: страж стоит в (0,6), ряд 0 + * в колонках 1..3 ПУСТОЙ, и после возврата Кида страж оказывался над + * дырой (x = 72 — это колонка 1 по таблице деления), падал с ряда 0 на + * ряд 2 и разбивался. Наблюдения пользователя — «страж был на 0,0», + * «страж падает на 2,1 и разбивается» — это ровно оно. + * + * Y всегда по ряду: enter_guard ставит стража НА ПОЛ (y_land[row+1]), а не + * туда, где он был по вертикали. Поэтому «дорисовать падение» при + * возврате в комнату оригинал не умеет — и не должен. */ + Guard.room = room; + Guard.curr_row = (int8_t)(tile / 10); + Guard.y = (uint8_t)pop_y_land[Guard.curr_row + 1]; + Guard.direction = dir; + /* seg002:196: на уровне со скелетами слот поднимает СКЕЛЕТА, и он + * встаёт сразу активным — с вынутым мечом (seq_63), а не в стойке + * покоя (seq_77). Стойку ставит ветка ниже, вместе с seq трупа. */ + Guard.charid = (pop_level_guard_type(pop_current_level) == 2) + ? CHARID_4_SKELETON : CHARID_2_GUARD; + Guard.sword = (Guard.charid == CHARID_4_SKELETON) + ? SWORD_2_DRAWN : 0; /* sword_0_sheathed */ + Guard.alive = -1; + Guard.action = 1; /* actions_1_run_jump */ + Guard.fall_x = Guard.fall_y = 0; + /* enter_guard (seg002:214): мастерство из данных уровня, 12 градаций; + * всё, что вне диапазона, оригинал сводит к 3. */ + guard_skill = (uint8_t)(skill < NUM_GUARD_SKILLS ? skill : 3); + /* get_guard_hp (seg002:54): extrastrength[skill] + tbl_guard_hp[уровень]. + * extrastrength (seg002:38) ненулевой только у skill 4 (=1). */ + guardhp_max = guardhp_curr = + (uint8_t)(pop_level_guard_hp(pop_current_level) + (guard_skill == 4 ? 1 : 0)); + guardhp_delta = 0; + /* enter_guard (seg002:0208). Живого поднимаем стандартной стойкой; у + * ЗАПОМНЕННОГО МЁРТВОГО берём сохранённую последовательность — он лежит + * ровно там, где лёг (порт ветки guards_seq_hi != 0). */ + { + uint16_t saved_seq = pop_guard_state_seq(room); + /* X — ВСЕГДА из запомненной (enter_guard, seg002:179: + * `Char.x = level.guards_x[room_minus_1]`), и живого стража это + * касается ровно так же, как трупа: страж, с которым Кид не стал + * драться, обязан остаться СТОЯТЬ ТАМ ЖЕ. Иначе он не просто + * «дёргается» — он может встретить Кида прямо на входе в комнату, + * не дав тому достать меч. */ + Guard.x = pop_guard_state_x(room); + /* КОЛОНКА — из X (enter_guard, seg002:180: + * `Char.curr_col = get_tile_div_mod_m7(Char.x)`). Из тайла её брать + * нельзя, там всегда 0 (см. выше). Колонка нужна не только + * отрисовке: на неё смотрят коллизия и check_can_guard_see_kid, а у + * ЗАПОМНЕННОГО ТРУПА ещё и порядок обхода тайлов — иначе Кид + * рисовался поверх тела вместо того, чтобы пройти за ним + * (BUG-DRAWORDER-1). */ + Guard.curr_col = guard_col_from_x(Guard.x); + pop_loadshad(); + if (saved_seq) Char.curr_seq = saved_seq; + else pop_char_set_seq(SEQ_77_GUARD_STAND_INACTIVE); + pop_saveshad(); + pop_guard_play(); /* до первого кадра */ + /* enter_guard (seg002:218): кадр смерти -> страж поднят ТРУПОМ. */ + if (Guard.frame == 185 || Guard.frame == 177 || Guard.frame == 178) { + Guard.alive = 1; + guardhp_curr = 0; + } + } +} diff --git a/applications/SprPoP/src/pop_hof.c b/applications/SprPoP/src/pop_hof.c new file mode 100644 index 0000000..778838a --- /dev/null +++ b/applications/SprPoP/src/pop_hof.c @@ -0,0 +1,530 @@ +/* + * pop_hof.c — persistent Hall of Fame, банк 9 (FG12). + * + * Порт двух показов оригинала: между credits и attract-demo (seg000:2060) + * и после победы, с вводом имени (end_sequence, seg001:5F1). Экран тот + * же самый — рамка story с логотипом PRINCE OF PERSIA (STORY_FRAME + + * HOF_POP), поэтому фон приходит готовой композицией из архива PV, а + * палитру выбирает вызывающая сцена: обе фазы идут на story.pal, и + * отличаются только цветом фона рамки (см. pop_pal_story_load). + * + * POP.HOF — небольшой самодостаточный формат: magic, version, fixed records + * и XOR+инверсия. Поэтому обрыв/мусор на HDD не может поломать старт игры: + * decode просто начинает с пустой таблицы. Ввод — raw-клавиатура, как весь + * PoP; он не открывает DSS-console и не конфликтует с активным raw hook. + */ +#include +#include +#include +#include +#include +#include +#include +#include "pop_bg.h" +#include "pop_settings.h" +#include "pop_sfx.h" +#include "pop_timer.h" +#include "pop_pal.h" +#include "pop_ui.h" +#include "pop_cutscene.h" /* POP_T60 — тики оригинала в кадры луча */ +#include "pop_intro.h" /* фон экрана: рамка story + логотип */ +#include "pop_title.h" /* титульная картинка после ввода имени */ +#include "pop_music.h" /* подкачка потоковой темы победы */ +#include "pop_hof.h" + +/* MAX_HOF_COUNT оригинала — шесть строк. */ +#define HOF_COUNT 6 +#define HOF_NAME_SIZE 16 /* пятнадцать букв + '\0' */ +#define HOF_REC_SIZE 20 +#define HOF_HEADER_SIZE 6 +#define HOF_SIZE (HOF_HEADER_SIZE + HOF_COUNT * HOF_REC_SIZE + 2) +#define HOF_BASE 0x100 +#define HOF_VERSION 2 +#define HOF_FADE POP_FADE_FRAMES +#define HOF_NO_ROW 0xFF + +/* Геометрия hof_rects[] (seg001:0D92) в координатах 320x200: строка i + * занимает y = 84 + 14*i .. +12, имя от x=72, время по правому краю 248. + * Полоса ввода — та же строка, сдвинутая offset4_rect_add(-4,-1,-40,-1). */ +#define HOF_ROW_TOP 84 +#define HOF_ROW_STEP 14 +#define HOF_ROW_H 12 +#define HOF_LEFT 72 +#define HOF_RIGHT 248 +#define HOF_EDIT_LEFT (HOF_LEFT - 4) +#define HOF_EDIT_RIGHT (HOF_RIGHT - 40) +/* Крупный шрифт: глиф 7 строк, строка 12 — текст центрируется по вертикали. + * baseline = верх строки + 2 + POP_FONT_BIG_ASCENT. */ +#define HOF_BASELINE(i) (POP_YOFF + HOF_ROW_TOP + (int)(i) * HOF_ROW_STEP + 9) +/* Золотой цвет полосы ввода: 0xB7 оригинала = седьмой цвет палитры title, + * а у нас story.pal сделана из неё же. */ +#define HOF_GOLD 7 +/* Паузы оригинала в тиках 60 Гц: показ таблицы на титрах и пауза после + * ввода имени (seg000:2065, seg001:628). */ +#define HOF_TITLE_TICKS POP_T60(0xF0) +#define HOF_AFTER_TICKS POP_T60(120) + +typedef struct { + char name[HOF_NAME_SIZE]; + uint8_t minutes; + uint16_t ticks; + uint8_t limited; +} hof_entry_t; + +static hof_entry_t hof_table[HOF_COUNT]; +static uint8_t hof_buf[HOF_SIZE]; + +static uint8_t hof_xor(const uint8_t *p, uint8_t n) +{ + uint8_t sum = 0; + while (n--) sum ^= *p++; + return sum; +} + +static void hof_clear_entry(hof_entry_t *entry) +{ + uint8_t i; + for (i = 0; i < HOF_NAME_SIZE; i++) entry->name[i] = 0; + entry->minutes = 0; + entry->ticks = 0; + entry->limited = 0; +} + +static void hof_clear(void) +{ + uint8_t i; + for (i = 0; i < HOF_COUNT; i++) hof_clear_entry(&hof_table[i]); +} + +static uint8_t hof_decode(const uint8_t *src, uint8_t size) +{ + uint8_t i, j, off, sum; + + if (size != HOF_SIZE || src[0] != 'P' || src[1] != 'H' || + src[2] != 'O' || src[3] != 'F' || src[4] != HOF_VERSION || + src[5] != HOF_COUNT) + return 0; + sum = hof_xor(src, (uint8_t)(HOF_SIZE - 2)); + if (src[HOF_SIZE - 2] != sum || src[HOF_SIZE - 1] != (uint8_t)~sum) + return 0; + + off = HOF_HEADER_SIZE; + for (i = 0; i < HOF_COUNT; i++) { + hof_entry_t *entry = &hof_table[i]; + for (j = 0; j < HOF_NAME_SIZE - 1; j++) { + uint8_t ch = src[off++]; + /* Имя намеренно ASCII: это также отбрасывает мусор из чужого + * формата, не позволяя строке уйти за свою границу. */ + if (ch && (ch < ' ' || ch > 'Z')) return 0; + entry->name[j] = (char)ch; + } + entry->name[HOF_NAME_SIZE - 1] = 0; + entry->minutes = src[off++]; + entry->ticks = (uint16_t)(src[off] | ((uint16_t)src[off + 1] << 8)); + off += 2; + entry->limited = src[off++]; + if (entry->limited > 1 || + (entry->limited && (entry->ticks == 0 || + entry->ticks > POP_TIMER_START_TICKS))) + return 0; + } + return 1; +} + +static void hof_encode(uint8_t *dst) +{ + uint8_t i, j, off, sum; + + dst[0] = 'P'; dst[1] = 'H'; dst[2] = 'O'; dst[3] = 'F'; + dst[4] = HOF_VERSION; + dst[5] = HOF_COUNT; + off = HOF_HEADER_SIZE; + for (i = 0; i < HOF_COUNT; i++) { + const hof_entry_t *entry = &hof_table[i]; + for (j = 0; j < HOF_NAME_SIZE - 1; j++) dst[off++] = entry->name[j]; + dst[off++] = entry->minutes; + dst[off++] = (uint8_t)entry->ticks; + dst[off++] = (uint8_t)(entry->ticks >> 8); + dst[off++] = entry->limited; + } + sum = hof_xor(dst, (uint8_t)(HOF_SIZE - 2)); + dst[HOF_SIZE - 2] = sum; + dst[HOF_SIZE - 1] = (uint8_t)~sum; +} + +static void hof_load(void) +{ + uint8_t blk, page, i; + int got; + + hof_clear(); + blk = mem_alloc_pages(1); + if (!blk) return; + page = mem_get_page(blk, 0); + gfx_w0_page_prepare(page); + got = bank_load_file(page, HOF_BASE, "POP.HOF", HOF_SIZE); + if (got == HOF_SIZE) { + gfx_w0_map(page); + for (i = 0; i < HOF_SIZE; i++) + hof_buf[i] = ((const uint8_t *)HOF_BASE)[i]; + gfx_w0_unmap(); + if (!hof_decode(hof_buf, (uint8_t)HOF_SIZE)) hof_clear(); + } + mem_free_block(blk); +} + +static void hof_save(void) +{ + uint8_t blk, page, i; + + blk = mem_alloc_pages(1); + if (!blk) return; + page = mem_get_page(blk, 0); + gfx_w0_page_prepare(page); + hof_encode(hof_buf); + gfx_w0_map(page); + for (i = 0; i < HOF_SIZE; i++) ((uint8_t *)HOF_BASE)[i] = hof_buf[i]; + gfx_w0_unmap(); + /* Неудача записи не меняет только что показанную таблицу: следующее + * открытие разумно начнёт с прежнего файла или с пустого списка. */ + bank_save_file(page, HOF_BASE, "POP.HOF", HOF_SIZE); + mem_free_block(blk); +} + +static uint8_t hof_better(const hof_entry_t *entry) +{ + uint8_t limited = pop_settings.time_limit_enabled; + + if (!entry->name[0]) return 1; + /* Unlimited сохраняется и выводится, но не может вытеснить ограниченный + * таймером результат: у него нет сравнимого времени. */ + if (limited != entry->limited) return limited; + if (pop_timer_minutes != entry->minutes) + return (uint8_t)(pop_timer_minutes > entry->minutes); + return (uint8_t)(pop_timer_ticks > entry->ticks); +} + +static uint8_t hof_insert_current(void) +{ + uint8_t i, j; + + for (i = 0; i < HOF_COUNT; i++) { + if (!hof_better(&hof_table[i])) continue; + for (j = HOF_COUNT - 1; j > i; j--) hof_table[j] = hof_table[j - 1]; + hof_clear_entry(&hof_table[i]); + hof_table[i].minutes = pop_timer_minutes; + hof_table[i].ticks = pop_timer_ticks; + hof_table[i].limited = pop_settings.time_limit_enabled; + return i; + } + return HOF_NO_ROW; +} + +static uint8_t hof_has_entries(void) +{ + uint8_t i; + for (i = 0; i < HOF_COUNT; i++) + if (hof_table[i].name[0]) return 1; + return 0; +} + +static void hof_dec(uint8_t value, char *out) +{ + if (value >= 100) { + *out++ = (char)('0' + value / 100); + value %= 100; + *out++ = (char)('0' + value / 10); + } else if (value >= 10) { + *out++ = (char)('0' + value / 10); + } + *out++ = (char)('0' + value % 10); + *out = 0; +} + +/* «59:43» — те же min-1 : tick/12, что печатает show_hof (seg001:0E94). */ +static void hof_result_text(const hof_entry_t *entry, char *out) +{ + uint8_t seconds; + + if (!entry->limited) { + strcpy(out, "UNLIMITED"); + return; + } + /* min == 0 у оригинала означает истёкшее время и печатается как 0:00. */ + hof_dec(entry->minutes ? (uint8_t)(entry->minutes - 1) : 0, out); + while (*out) out++; + seconds = entry->minutes ? (uint8_t)(entry->ticks / 12) : 0; + *out++ = ':'; + *out++ = (char)('0' + seconds / 10); + *out++ = (char)('0' + seconds % 10); + *out = 0; +} + +/* show_hof_text(): сначала тень со сдвигом (1,1), затем сам текст. У нас + * цвет запечён в глифы, поэтому «другой цвет» — это второй набор + * (POP_TEXT_BIG_DARK), а не параметр рисования. */ +static void hof_text_left(int x, int baseline, const char *s) +{ + pop_text_draw_mapped(POP_TEXT_BIG_DARK, x + 1, baseline + 1, s); + pop_text_draw_mapped(POP_TEXT_BIG, x, baseline, s); +} + +static void hof_text_right(int right, int baseline, const char *s) +{ + pop_text_right_mapped(POP_TEXT_BIG_DARK, right + 1, baseline + 1, s); + pop_text_right_mapped(POP_TEXT_BIG, right, baseline, s); +} + +/* Золотая полоса под ввод — draw_rect(&rect, bgcolor) оригинала. */ +static void hof_edit_bar(uint8_t row) +{ + int top = HOF_BASELINE(row) - 10; + + gfx_set_bank(GFX_BANK_NORMAL); + setfillstyle(SOLID_FILL, HOF_GOLD); + bar(HOF_EDIT_LEFT, top, HOF_EDIT_RIGHT, top + HOF_ROW_H - 1); +} + +/* Строки таблицы в ТЕКУЩУЮ draw-страницу. + * + * БАНК ЗДЕСЬ TRANSPARENT, А НЕ SPRITE. Полосовой переход копирует страницу + * акселератором, а тот читает ОЗУ-копию (gfx_copy_page); SPRITE — это + * NOSHADOW + TRANSPARENT, то есть в копию он не пишет, и на титрах + * переезжал бы один фон без строк. TRANSPARENT даёт ту же прозрачность + * 0xFF, но обновляет и копию, поэтому таблица проявляется уже собранной. */ +static void hof_draw_rows(uint8_t edit_row) +{ + uint8_t i; + + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_text_map(); + for (i = 0; i < HOF_COUNT; i++) { + char value[12]; + const hof_entry_t *entry = &hof_table[i]; + int baseline = HOF_BASELINE(i); + + /* Строка, которую сейчас вписывают, уже содержит результат: имени + * ещё нет, а время справа оригинал показывает сразу (show_hof + * рисует все hof_count строк, включая только что вставленную). */ + if (!entry->name[0] && i != edit_row) continue; + if (entry->name[0]) hof_text_left(HOF_LEFT, baseline, entry->name); + hof_result_text(entry, value); + hof_text_right(HOF_RIGHT, baseline, value); + } + pop_text_unmap(); +} + +/* Готовая страница: фон из архива, полоса ввода и строки. -1 — фон не + * прочитан, и тогда таблица просто не показывается: ни игра, ни title от + * этого не зависят. */ +static int hof_page_build(uint8_t page, uint8_t edit_row) +{ + if (pop_intro_hof_bg(page) != 0) return -1; + gfx_set_draw_page(page); + if (edit_row != HOF_NO_ROW) hof_edit_bar(edit_row); + hof_draw_rows(edit_row); + return 0; +} + +/* Имя пишется ПРЯМО НА ЭКРАН, как input_str оригинала + * (current_target_surface = onscreen_surface_): страницы при вводе не + * меняются местами, поэтому дорисовывать фон второй страницы не нужно. */ +static void hof_edit_draw(uint8_t row, const char *name, uint8_t cursor) +{ + int x = HOF_LEFT, baseline = HOF_BASELINE(row); + + gfx_set_draw_page(gfx_get_visible_page()); + hof_edit_bar(row); + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_text_map(); + /* Тёмный набор: на золотой полосе оригинал пишет цветом 0xBE. */ + if (name[0]) { + pop_text_draw_mapped(POP_TEXT_BIG_DARK, x, baseline, name); + x += pop_text_width_mapped(POP_TEXT_BIG_DARK, name) + 1; + } + /* draw_text_cursor(): подчерк той же гарнитуры сразу за текстом, + * мигающий сменой цвета на фоновый — у нас проще перерисовать. */ + if (cursor) pop_text_draw_mapped(POP_TEXT_BIG_DARK, x, baseline, "_"); + pop_text_unmap(); +} + +/* ---- ожидания -------------------------------------------------------- * + * + * Любое ожидание этого экрана держит ПОТОКОВУЮ музыку: победная тема + * (1,2 МБ) играет кольцом страниц, и дочитывать её надо всё время, пока + * игрок смотрит таблицу и печатает имя — иначе насос догонит загрузчика и + * пойдёт по кругу старыми данными. + * + * Прерывание — по НОВОМУ нажатию (детектор фронта, как в сценах intro): + * клавиша, которой закончили предыдущий экран, в момент входа ещё зажата, + * и считать её за «пропустить» нельзя. */ +typedef struct { uint8_t armed; } hof_skip_t; + +static void hof_skip_begin(hof_skip_t *sk) +{ + kbd_raw_sync(); + sk->armed = (uint8_t)!kbd_raw_any_down(); +} + +static uint8_t hof_skip_requested(hof_skip_t *sk) +{ + kbd_raw_sync(); + if (!kbd_raw_any_down()) { sk->armed = 1; return 0; } + return sk->armed; +} + +/* Возврат 1 — ожидание прервано нажатием. */ +static uint8_t hof_wait_frames(uint16_t frames) +{ + hof_skip_t skip; + + hof_skip_begin(&skip); + while (frames--) { + if (hof_skip_requested(&skip)) return 1; + pop_music_service(); + gfx_wait_vsync(); + } + return 0; +} + +/* `while (check_sound_playing() && !key_test_quit())` (seg001:637): финал + * дослушивает победную тему и прерывается любой клавишей. */ +static void hof_wait_music(void) +{ + hof_skip_t skip; + + hof_skip_begin(&skip); + while (pop_music_busy() && !hof_skip_requested(&skip)) { + pop_music_service(); + gfx_wait_vsync(); + } +} + +/* Set 2 make-коды A..Z, затем space/backspace/enter/esc. Храним edge для + * каждой клавиши, поэтому зажатая буква не заполняет имя автоповтором. */ +static const uint8_t hof_key_code[30] = { + 0x1C, 0x32, 0x21, 0x23, 0x24, 0x2B, 0x34, 0x33, 0x43, + 0x3B, 0x42, 0x4B, 0x3A, 0x31, 0x44, 0x4D, 0x15, 0x2D, + 0x1B, 0x2C, 0x3C, 0x2A, 0x1D, 0x22, 0x35, 0x1A, + KBD_SPACE, 0x66, KBD_ENTER, KBD_ESC +}; + +/* Возвращает 1, когда ввод закончен (Enter/Esc); *changed — надо ли + * перерисовать строку. */ +static uint8_t hof_name_tick(hof_entry_t *entry, uint8_t *length, + uint8_t *previous, uint8_t *changed) +{ + uint8_t i; + + for (i = 0; i < 30; i++) { + uint8_t down = kbd_raw_down(hof_key_code[i]); + if (down && !previous[i]) { + if (i < 26 && *length < HOF_NAME_SIZE - 1) { + entry->name[(*length)++] = (char)('A' + i); + entry->name[*length] = 0; + *changed = 1; + } else if (i == 26 && *length && *length < HOF_NAME_SIZE - 1) { + entry->name[(*length)++] = ' '; + entry->name[*length] = 0; + *changed = 1; + } else if (i == 27 && *length) { + entry->name[--(*length)] = 0; + *changed = 1; + } else if (i == 28 || i == 29) { + previous[i] = down; + return 1; + } + } + previous[i] = down; + } + return 0; +} + +/* Ввод имени в строку row. Пустое имя оригинал не отвергает, но пустая + * строка в таблице выглядела бы как свободное место — подставляем PLAYER. */ +/* Курсор мигает каждые шесть тиков 60 Гц (start_timer(timer_0, 6) в + * input_str) — в кадрах луча это HOF_BLINK. */ +#define HOF_BLINK POP_T60(6) + +static void hof_edit(uint8_t row) +{ + uint8_t length = 0, previous[30], i, changed = 1; + uint8_t cursor = 1, phase = 0; + hof_entry_t *entry = &hof_table[row]; + + for (i = 0; i < sizeof(previous); i++) previous[i] = 0; + /* Стартовое состояние клавиш: зажатое с прошлого экрана не считается + * нажатием, поэтому первый sync только заполняет previous[]. */ + kbd_raw_sync(); + for (i = 0; i < sizeof(previous); i++) + previous[i] = kbd_raw_down(hof_key_code[i]); + for (;;) { + kbd_raw_sync(); + if (hof_name_tick(entry, &length, previous, &changed)) { + if (!length) strcpy(entry->name, "PLAYER"); + return; + } + if (changed) { cursor = 1; phase = 0; } + if (++phase >= HOF_BLINK) { + phase = 0; + cursor = (uint8_t)!cursor; + changed = 1; + } + if (changed) { + hof_edit_draw(row, entry->name, cursor); + changed = 0; + } + pop_music_service(); + gfx_wait_vsync(); + } +} + +void pop_hof_show(void) __banked +{ + uint8_t row; + + hof_load(); + row = hof_insert_current(); + if (row != HOF_NO_ROW) { + uint8_t page; + + /* fade_out_2 гасит титульную картинку перед таблицей (seg001:601). + * Палитру не меняем: story.pal с красным фоном рамки уже стоит от + * финального HAIL, и снимок для fade сделан ею же. */ + pop_pal_fade_out(HOF_FADE); + page = pop_screen_begin(); + if (hof_page_build(page, row) == 0) { + pop_screen_present(page); + pop_pal_fade_in(HOF_FADE); + hof_edit(row); + hof_save(); + /* Имя вписано: строку показываем уже обычным золотым текстом, + * без полосы (restore_peel + show_hof_text оригинала). */ + page = pop_screen_begin(); + if (hof_page_build(page, HOF_NO_ROW) == 0) + pop_screen_present(page); + hof_wait_frames(HOF_AFTER_TICKS); + (void)pop_title_main_ltr(); + } + } + hof_wait_music(); + pop_pal_fade_out(HOF_FADE); + pop_music_free(); + pop_sfx_pause(); +} + +uint8_t pop_hof_show_title(void) __banked +{ + uint8_t page; + + hof_load(); + if (!hof_has_entries()) return 0; + page = pop_screen_begin(); + /* Страница собирается ЦЕЛИКОМ до показа — как offscreen оригинала: + * draw_full_image + show_hof, и только потом transition_ltr + * (seg000:2063). Ни fade, ни смены палитры: экран проявляется прямо + * поверх credits, на той же story.pal. */ + if (hof_page_build(page, HOF_NO_ROW) != 0) return 0; + pop_screen_present_ltr(); + return hof_wait_frames(HOF_TITLE_TICKS); +} diff --git a/applications/SprPoP/src/pop_hof.h b/applications/SprPoP/src/pop_hof.h new file mode 100644 index 0000000..dbe34be --- /dev/null +++ b/applications/SprPoP/src/pop_hof.h @@ -0,0 +1,21 @@ +/* pop_hof.h — таблица результатов после финала, банк 9 (FG12). */ +#ifndef POP_HOF_H +#define POP_HOF_H + +#include + +/* Хвост end_sequence: при подходящем результате — запрос имени, сохранение + * POP.HOF и возврат к титульной картинке; затем в любом случае + * дослушивается победная тема и гаснет экран. Ошибка или повреждение + * файла означают пустую таблицу; gameplay и последующий title от этого не + * зависят. Победная тема на входе ещё звучит — освобождает её эта + * функция. */ +void pop_hof_show(void) __banked; + +/* Показать уже существующую непустую таблицу между credits и demo + * (seg000:2060): полосовой переход, 240 тиков, ни fade, ни смены палитры. + * Возврат 1 — игрок нажал клавишу, то есть всю цепочку title/intro надо + * прервать. */ +uint8_t pop_hof_show_title(void) __banked; + +#endif /* POP_HOF_H */ diff --git a/applications/SprPoP/src/pop_intro.c b/applications/SprPoP/src/pop_intro.c new file mode 100644 index 0000000..eafcca7 --- /dev/null +++ b/applications/SprPoP/src/pop_intro.c @@ -0,0 +1,1860 @@ +/* + * pop_intro.c — вторая половина show_title(): story + Princess/Jaffar (FG8). + * + * Готовые сцены создаёт pop_pack_intro.py из TITLE/PV SDLPoP. Каждая сцена + * хранится пятью полосами <=49 строк, поэтому EMM удерживает лишь текущую + * полосу. pop_cutscene_t остаётся независимым от графики: этот модуль только + * исполняет его события и позднее будет образцом для pre-level сцен FG9. + */ +#include +#include +#include +#include +#include +#include "pop_bg.h" +#include "pop_shadow.h" +#include "pop_sfx.h" +#include "pop_music.h" +#include "pop_music_ticks.h" /* POP_MUS_TICKS_ — длина реплик набора */ +#include "pop_arc.h" +#include "pv_arc.h" +#include "pop_cutscene.h" +#include "pop_intro.h" +#include "pop_hof.h" /* таблица рекордов между credits и demo */ +#include "pop_title.h" /* титульная картинка в финале */ +#include "pop_pace.h" /* pop_wait_edge */ +#include "pop_pal.h" +#include "pop_timer.h" +#include "pop_ui.h" +#include "pop_kid.h" /* Char/окна, play_seq — движок персонажей катсцен */ +#include "pop_guard.h" /* Guard, CHARID_*, pop_gframe */ + +#define INTRO_PARTS 5 +/* ДЛИТЕЛЬНОСТЬ FADE. У оригинала это 64 шага палитры по два тика — + * 128 тиков, то есть 2,13 с (fade_in_1/fade_out_1 -> fade_*_frame -> + * do_simple_wait(1) при wait_time = 2). Наши четыре кадра укладывались в + * восемь сотых секунды, и вся сцена выходила втрое короче оригинальной. + * Точное число кадров считает POP_FADE_FRAMES (pop_ui.h): из + * заказанных 2,13 с вычитается то, что съедает сам пересчёт палитры. */ +#define INTRO_FADE POP_FADE_FRAMES + +/* Оцифрованные эффекты pv_scene() (SDLPoP seg001:422): створка ворот + * закрывается, затем открывается дверь покоев, из которой входит Джафар. */ +#define PV_SND_GATE_CLOSING 4 +#define PV_SND_DOOR_OPENING 51 + +/* Шкала ниже повторяет порядок pv_scene() SDLPoP и живёт в ЕГО тиках + * (60 Гц): обычный кадр cutscene длится 8 тиков, до первой смены темпа 6, + * после начала заклинания 7. В кадры луча переводит POP_T60. + * + * ДЛИННЫЕ ПАУЗЫ ЗДЕСЬ — ЭТО РЕПЛИКИ. Оригинал их не отмеряет, а ждёт + * окончания сэмпла (`do { frame(1) } while (check_sound_playing())`), и + * пока музыки у нас не было, паузы стояли укороченными «на глаз». + * + * ДВА КЛАССА КОНСТАНТ, И ИХ НЕЛЬЗЯ ПУТАТЬ. Раньше вся шкала была записана + * абсолютными литералами, и разница между ними была не видна — из-за этого + * смена набора записей (flac -> mt32) молча ломала сцену: + * + * 1) БИТЫ СЦЕНАРИЯ — интервалы самого pv_scene() SDLPoP. От записи не + * зависят и масштабироваться ею НЕ должны. Отсчитываются от начала + * своего куска (например, всё поведение Джафара — от PV_DIALOG1_START). + * 2) «ЖДЁМ КОНЦА РЕПЛИКИ» — PV_M50_END и PV_EXIT_START. Это целиком + * длина НАШЕЙ записи, и она у каждого набора своя: m50 — 831 тик на + * flac против 867 на mt32, m53 — 985 против 1044. Поэтому берём их + * из POP_MUS_TICKS_, которые печатает упаковщик того набора, + * которым собрана сборка (gen/pop_music_tbl.h). + * + * Всё, что стоит ПОСЛЕ реплики, сдвигается вместе с её концом — отсюда + * сложения от PV_M50_END и PV_EXIT_START вместо литералов. */ +enum { + PV_MUS_2_START = 12, /* frame(2), затем «принцесса ждёт» (m50) */ + PV_MUS_3_LOAD = 60, /* подкачка следующей реплики, m50 звучит */ + /* Конец m50: кадр становится 8. Прежний литерал 846 отвечал длине 834 + * тика — на три больше нашей записи (831); расхождение осталось с + * времён, когда длину подставляли руками. */ + PV_M50_END = PV_MUS_2_START + POP_MUS_TICKS_50, + PV_WAIT_END = PV_M50_END + 40, /* +frame(5) -> створка ворот */ + PV_GATE_END = PV_WAIT_END + 48, + PV_DOOR_END = PV_GATE_END + 24, + PV_TURN_START = PV_DOOR_END, + PV_WALK1_START = PV_DOOR_END + 40, + PV_DIALOG1_START = PV_WALK1_START + 48, /* Джафар входит — реплика m53 */ + /* Ниже — биты сценария внутри реплики Джафара, все от её начала. */ + PV_MUS_4_LOAD = PV_DIALOG1_START + 54, /* подкачка m52 */ + PV_WALK2_START = PV_DIALOG1_START + 176, + PV_DIALOG2_START = PV_DIALOG1_START + 416, + /* ЗАКЛИНАНИЕ СДВИНУТО РАНЬШЕ СЦЕНАРИЯ — ПОДГОНКА ПОД МУЗЫКУ. + * + * Сцена render-bound: кадр рисуется дороже своего бюджета, кадровая + * ветка пейсинга догона не имеет, и анимация идёт ~49 тиков в секунду + * вместо 60. Реплика Джафара при этом играет по РЕАЛЬНОМУ времени и + * успевает кончиться, когда шкала стоит на 1854 вместо 2028 (замер в + * MAME 2026-08-27 зондом на конец трека: длина насосу отдана полная, + * 1403 порции, курсор дошёл до последней страницы — трек доигрывает + * целиком, отстаёт именно картинка). + * + * Из-за этого кода реплики приходила примерно на секунду раньше + * молнии. Честное лечение — удешевить кадр сцены; до тех пор двигаем + * жест целиком (замах, шаг назад, вспышка), чтобы он не сломался + * внутри себя. Величина подобрана НА СЛУХ и уточняется прогоном: + * расчётные полсотни тиков (секунда отставания при ~49 тиках/с) на + * слух почти не изменили картины, поэтому взято вдвое больше. + * + * Эффектов дверей это не касается: у них и звук, и картинка идут по + * одной и той же шкале и разъехаться не могут. */ + PV_MAGIC_LEAD = 100, + PV_RAISE_START = PV_DIALOG1_START + 696 - PV_MAGIC_LEAD, + PV_STEPBACK_START = PV_DIALOG1_START + 703 - PV_MAGIC_LEAD, + PV_MAGIC_START = PV_DIALOG1_START + 822 - PV_MAGIC_LEAD, + /* Конец m53 — снова длина записи, а не бит сценария. */ + PV_EXIT_START = PV_DIALOG1_START + POP_MUS_TICKS_53, + PV_MUS_4_START = PV_EXIT_START + 42, /* +frame(6) -> «Джафар уходит» */ + PV_GLASS_DONE = PV_EXIT_START + 210, + PV_SLUMP_START = PV_EXIT_START + 273, + PV_ANIM_TICKS = PV_EXIT_START + 469 +}; + +/* Возврат долга шкалы (см. цикл сцены): сколько порций насоса отдаём за один + * кадр сцены и сколько их вообще имеет смысл копить. 1 порция — 11,7 мс; + * 128 порций — полторы секунды, дальше догонять уже нечего. */ +enum { + INTRO_STORY_ABSENCE = 0, + INTRO_STORY_MARRY, + INTRO_STORY_CREDITS, + INTRO_STORY_HAIL, + INTRO_STORY_FRAME, + INTRO_STORY_HOF, /* рамка + логотип: фон таблицы рекордов */ + INTRO_PV_WAITING, + INTRO_PV_JAFFAR, + INTRO_PV_MAGIC, + INTRO_PV_ALONE, + INTRO_PV_STAND, + INTRO_PV_LYING, + INTRO_PV_MOUSE, + INTRO_PV_SHORT +}; + +enum { + INTRO_PAL_STORY = 0, + INTRO_PAL_PV = 1, + /* Та же story.pal, но фон текстовой рамки красный: + * load_title_images(0) в end_sequence (seg001:586). */ + INTRO_PAL_STORY_END = 2 +}; + +/* Экраны PV лежат в одном архиве по пять полос подряд, ровно в том же + * порядке, в каком идут номера сцен: сначала пять текстовых (story + * res41 + тексты 42/43/45/44 и чистая рамка), затем восемь стадий комнаты + * принцессы (res951 + персонажи) и чистый фон b0. Поэтому имён здесь нет + * — индекс первой полосы экрана считается умножением. */ +#if (ARC_PV_P0_0 - ARC_PV_S0_0) != 6 * INTRO_PARTS +#error "экраны PV в архиве лежат не по пять полос подряд" +#endif +#define INTRO_SCENES 14 + +/* Две текстовые половины show_title(). Между ними PV теперь идёт отдельным + * покадровым renderer-ом, а не четырьмя полными статичными композициями. */ +/* ПОЧЕМУ НЕ 0x258, КАК В ОРИГИНАЛЕ. Точка отсчёта у обоих — пуск m55; + * совпасть должен МОМЕНТ ПОЛНОЙ ТЕМНОТЫ, а дороги к нему разные: + * SDLPoP: transition_ltr 80 тиков + pop_wait 0x258 + fade_out_2, а тот + * гасит палитру за 0x40 шагов по 2 тика = 128 тиков; + * у нас: полосовой переход 80 кадров луча (= 96 тиков) + WAIT + fade + * той же длины (INTRO_FADE = 128 тиков). + * Теперь, когда наш fade идёт те же 128 тиков, остаётся поправка на один + * лишь переход: 80 + 600 + 128 - 96 - 128 = 584. Дальше и там и там экран + * уже чёрный, а трек доигрывает — этой паузой сцена и стыкуется с PV. */ +static const pop_cs_cmd_t intro_before_pv[] = { + /* absence собирается прямо поверх последнего title-кадра полосовым + * transition_ltr(), как в SDLPoP; оба экрана используют title.pal. */ + { POP_CS_WAIT, POP_T60(584) }, /* story 1: In the absence */ + { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; + +/* Файловый fallback сохраняет возможность пройти title на неполном HDD. + * Нормальная ветка его не вызывает: p0..p3 оставлены также для диагностики + * точек сюжета и не являются анимационным ресурсом. */ +static const pop_cs_cmd_t intro_pv_fallback[] = { + { POP_CS_PALETTE, INTRO_PAL_PV }, + { POP_CS_SHOW, INTRO_PV_WAITING }, + { POP_CS_FADE_IN, 0 }, + { POP_CS_WAIT, 0x1A }, /* princess, gate and door */ + { POP_CS_SHOW, INTRO_PV_JAFFAR }, + { POP_CS_WAIT, 0x6D }, /* Jaffar comes/walks */ + { POP_CS_SHOW, INTRO_PV_MAGIC }, + { POP_CS_FLASH, 5 }, /* hourglass appears */ + { POP_CS_WAIT, 0x2F }, + { POP_CS_SHOW, INTRO_PV_ALONE }, + { POP_CS_WAIT, 0x3D }, /* Jaffar leaves, princess looks */ + { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; + +/* Хвост show_title() разрезан надвое: между «свадьбой» и титрами оригинал + * ЖДЁТ окончания последней реплики Джафара (seg000:2051), которая начала + * звучать ещё в PV и продолжается поверх смены картинки. */ +static const pop_cs_cmd_t intro_marry[] = { + { POP_CS_PALETTE, INTRO_PAL_STORY }, + { POP_CS_SHOW, INTRO_STORY_MARRY }, + { POP_CS_FADE_IN, 0 }, + { POP_CS_END, 0 } +}; + +/* Гашение — отдельным шагом: между credits и ним оригинал показывает + * таблицу рекордов, если файл с ней есть (seg000:2060). */ +static const pop_cs_cmd_t intro_tail[] = { + { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; + +/* Статические fallback-сценарии хранят уже физические кадры Sprinter. + * Один логический кадр cutscene SDLPoP длится 6/60 с; на 50 Гц это ровно + * 5/50 с. Поэтому суммы proc_cutscene_frame() здесь умножены на пять. */ +static const pop_cs_cmd_t pre_2_6[] = { + { POP_CS_PALETTE, INTRO_PAL_PV }, { POP_CS_SHOW, INTRO_PV_STAND }, + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 130 }, { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; +static const pop_cs_cmd_t pre_4[] = { + { POP_CS_PALETTE, INTRO_PAL_PV }, { POP_CS_SHOW, INTRO_PV_LYING }, + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 130 }, { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; +static const pop_cs_cmd_t pre_8[] = { + { POP_CS_PALETTE, INTRO_PAL_PV }, { POP_CS_SHOW, INTRO_PV_MOUSE }, + /* 20 + 20 + 20 логических кадров = 6,0 с. */ + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 300 }, { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; +static const pop_cs_cmd_t pre_9[] = { + { POP_CS_PALETTE, INTRO_PAL_PV }, { POP_CS_SHOW, INTRO_PV_MOUSE }, + /* 5 + 9 + 58 логических кадров = 7,2 с. */ + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 360 }, { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; +static const pop_cs_cmd_t pre_12_short[] = { + { POP_CS_PALETTE, INTRO_PAL_PV }, { POP_CS_SHOW, INTRO_PV_SHORT }, + /* 2 + 24 логических кадра = те же 2,6 с, что и длинная ветка. */ + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 130 }, { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; +/* time_expired() SDLPoP: hourglass_state=7, fade in, 2+100 кадров, fade + * out. P7 — готовая стадия комнаты с часами; звук намеренно не стартует, + * так как CBL на lifecycle screen погашен до дисковых чтений. */ +static const pop_cs_cmd_t time_expired_script[] = { + { POP_CS_PALETTE, INTRO_PAL_PV }, { POP_CS_SHOW, INTRO_PV_SHORT }, + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 102 }, { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; +/* end_sequence_anim() — объятие/мышь — здесь сведён к отдельной PV-стадии; + * затем end_sequence() SDLPoP показывает HAIL ровно 900 тиков. */ +/* Хвост финала после живой сцены встречи: только исходный story-экран. */ +/* FADE_OUT в конце НЕТ намеренно: оригинал не гасит HAIL, а проявляет + * поверх него титульную картинку полосами (draw_full_image(TITLE_MAIN) + + * transition_ltr, seg001:590). Гасит экран уже таблица рекордов. */ +static const pop_cs_cmd_t ending_hail_script[] = { + { POP_CS_PALETTE, INTRO_PAL_STORY_END }, { POP_CS_SHOW, INTRO_STORY_HAIL }, + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 900 }, + { POP_CS_END, 0 } +}; +static const pop_cs_cmd_t ending_script[] = { + { POP_CS_PALETTE, INTRO_PAL_PV }, { POP_CS_SHOW, INTRO_PV_MOUSE }, + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 54 }, { POP_CS_FADE_OUT, 0 }, + { POP_CS_PALETTE, INTRO_PAL_STORY_END }, { POP_CS_SHOW, INTRO_STORY_HAIL }, + { POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 900 }, + { POP_CS_END, 0 } +}; + +static uint8_t intro_scene; +static uint8_t intro_pending_pal; +static uint8_t intro_pal_pending; + +/* Полосовой загрузчик рядом со своими вызовами: общий из банка 9 звать + * можно (индексы и таблица — числа на стеке W2, а не указатели на W3), но + * лишний банк→банк на каждую полосу тут ни к чему. + * pop_arc_atlas сам временно занимает W3 только на время чтения. */ +static int intro_blit_arc(pop_arc_t *arc, const uint8_t *tbl, + uint8_t base, uint8_t count, int x, int y) +{ + uint8_t part; + + gfx_set_bank(GFX_BANK_NORMAL); + for (part = 0; part < count; part++) { + atlas_t at; + const uint8_t *img; + + if (pop_arc_atlas(arc, tbl, (uint8_t)(base + part), &at) != 0) + return -1; + gfx_w0_map(at.page); + img = (const uint8_t *)atlas_image(&at, 0); + gfx_blit(x, y, img); + y += img[2]; + gfx_w0_unmap(); + atlas_free(&at); + } + return 0; +} + +/* Экран целиком: открыть архив, вылить пять полос, закрыть. Один `open` + * на экран вместо пяти — при 51,4 мс за открытие это больше половины + * времени показа. */ +static int intro_blit_screen(uint8_t base, int y) +{ + uint8_t tbl[ARC_PV_COUNT * POP_ARC_REC]; + pop_arc_t arc; + int res; + + if (pop_arc_open_id(&arc, POP_ARC_PV, tbl, ARC_PV_COUNT) < 0) return -1; + res = intro_blit_arc(&arc, tbl, base, INTRO_PARTS, 0, y); + pop_arc_close(&arc); + return res; +} + +/* Один актёрский атлас PV из архива. Открытие на каждый — всё равно + * дешевле, чем прежние отдельные файлы (тот же `open`, но без поиска имени + * в каталоге); там, где их несколько подряд, архив открывается один раз. */ +static int pv_atlas(pop_arc_t *arc, const uint8_t *tbl, uint8_t idx, + atlas_t *at) +{ + return pop_arc_atlas(arc, tbl, idx, at) != 0 ? -1 : 0; +} + +/* ---- «Пропустить сцену» — по НОВОМУ нажатию, а не по удержанию -------- * + * + * Клавиша, которой закончили уровень (движение Кида в дверь, чит Shift+L, + * skip предыдущего экрана), в момент старта сцены ЕЩЁ ЗАЖАТА. Считать это + * за skip нельзя: сцена схлопывается в один кадр, и её никто не видит. + * Ждать отпускания безусловно тоже нельзя — потерянный break-код вешает + * игру намертво (BUGS_OPEN.md#kbd-stuck-wait). + * + * Поэтому детектор ФРОНТА: пока хоть что-то зажато со старта, skip не + * взводится; как только всё отпущено, детектор «взводится» и следующее + * нажатие сцену прерывает. Залипший бит в худшем случае делает сцену + * непропускаемой — она доиграет сама, а игра не встанет. */ +typedef struct { uint8_t armed; } intro_skip_t; + +static void intro_skip_begin(intro_skip_t *sk) +{ + kbd_raw_sync(); + sk->armed = (uint8_t)!kbd_raw_any_down(); +} + +static uint8_t intro_skip_requested(intro_skip_t *sk) +{ + kbd_raw_sync(); + if (!kbd_raw_any_down()) { sk->armed = 1; return 0; } + return sk->armed; +} + +static int intro_draw_page(uint8_t scene, uint8_t page) +{ + if (scene >= INTRO_SCENES) return -1; + gfx_set_draw_page(page); + gfx_set_bank(GFX_BANK_NORMAL); + pop_screen_fill(BLACK); + return intro_blit_screen((uint8_t)(ARC_PV_S0_0 + scene * INTRO_PARTS), + POP_YOFF); +} + +static int intro_draw(uint8_t scene) +{ + uint8_t page = pop_screen_begin(); + + if (intro_draw_page(scene, page) != 0) return -1; + pop_screen_present(page); + return 0; +} + +/* У gfx_pal_fload/sync палитра меняется по 64 цвета за вызов BIOS. Если в + * этот момент хоть одна страница содержит следующую сцену, один VSync может + * показать её уже яркой, ещё до штатного fade-in. Поэтому перед сменой + * источника палитры уничтожаем растр на ОБЕИХ страницах, а не только гасим + * их палитры. */ +static void intro_black_pages(void) +{ + gfx_set_draw_page(0); + gfx_set_bank(GFX_BANK_NORMAL); + pop_screen_fill(BLACK); + gfx_set_draw_page(1); + pop_screen_fill(BLACK); + pop_screen_present(1); +} + +/* Для смены полного источника палитры сначала гасим старую сцену и + * оставляем на видимой странице настоящий чёрный растр. */ +static void intro_prepare_palette(uint8_t pal) +{ + intro_pending_pal = pal; + intro_pal_pending = 1; + pop_pal_apply(POP_PAL_DIM_BLACK); + intro_black_pages(); +} + +static void intro_commit_palette(void) +{ + if (intro_pending_pal == INTRO_PAL_PV) pop_pal_pv_load(); + else pop_pal_story_load((uint8_t)(intro_pending_pal == INTRO_PAL_STORY_END)); + pop_pal_apply(POP_PAL_DIM_BLACK); + intro_pal_pending = 0; +} + +static int intro_draw_new_palette(uint8_t scene) +{ + uint8_t page; + + /* Палитра загружается, пока обе страницы содержат только чёрный растр. + * После повторного затемнения можно собрать новую сцену в скрытую + * страницу и показать её исключительно чёрной перед fade-in. */ + intro_commit_palette(); + page = pop_screen_begin(); + if (intro_draw_page(scene, page) != 0) return -1; + pop_screen_present(page); + return 0; +} + +/* В отличие от intro_draw_page() здесь base собирается в shadow ОБЕИХ + * страниц. После show page 1 palette 0 не видна и в неё безопасно загрузить + * PV\pv.pal; первый animated frame соберётся в page 0. */ +static int intro_pv_draw_base(void) +{ + gfx_rect_t full; + + gfx_set_draw_page(0); + gfx_set_bank(GFX_BANK_NORMAL); + pop_screen_fill(BLACK); + if (intro_blit_screen(ARC_PV_B0_0, POP_YOFF) != 0) return -1; + gfx_set_draw_page(1); + full.x = 0; full.y = 0; full.w = GFX_WIDTH; full.h = GFX_HEIGHT; + gfx_copy_page(&full, GFX_COPY_DIRECT); + pop_screen_present(1); + return 0; +} + +static void pv_blit_floor(const atlas_t *at, uint8_t frame, int x, int floor) +{ + const uint8_t *img; + + gfx_w0_map(at->page); + img = (const uint8_t *)atlas_image(at, frame); + /* add_*table() оригинала задаёт нижнюю строку спрайта включительно. */ + gfx_blit(x, POP_YOFF + floor - img[2] + 1, img); + gfx_w0_unmap(); +} + +static void pv_blit_actor(const atlas_t *at, uint8_t frame, int anchor_x, + int floor, uint8_t flip) +{ + const uint8_t *img; + int x = anchor_x; + + gfx_w0_map(at->page); + img = (const uint8_t *)atlas_image(at, frame); + /* draw_mid SDLPoP зеркалит правое направление относительно hot-point: + * экранный x сначала масштабирован, затем из него вычитается width. */ + if (flip) x -= img[0]; + gfx_blit_cols(x, POP_YOFF + floor - img[2] + 1, img, flip); + gfx_w0_unmap(); +} + +static void pv_draw_stars(const uint8_t *phase) +{ + static const uint8_t star_x[] = { 20, 16, 23, 17, 24, 18 }; + static const uint8_t star_y[] = { 97, 104, 110, 116, 120, 128 }; + static const uint8_t star_color[] = { 8, 7, 15, 15, 7 }; + uint8_t i; + + for (i = 0; i < sizeof(star_x); i++) { + /* draw_rect SDLPoP получает полуинтервал [x,x+1)×[y,y+1). + * BGI bar() включает правую/нижнюю границу и давал звезду 2x2. */ + putpixel(star_x[i], POP_YOFF + star_y[i], star_color[phase[i]]); + } +} + +/* У Princess/Jaffar номер frame — это индекс frame_tbl_cuts, а картинка + * лежит в отдельном chtab. Эти таблицы — компактная нужная часть таблицы + * SDLPoP: не приходится тащить в банк весь interpreter SEQTBL. */ +typedef struct { + int16_t x; /* Char.x оригинала: полу-пиксели */ + uint8_t frame; + uint8_t left; + uint8_t visible; + uint8_t step; +} pv_actor_t; + +/* frame_tbl_cuts[1..18].image; 10 — служебная пустая запись. */ +static const uint8_t pv_princess_image[19] = { + 0, 15, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 9, 10, 11, 12, 13, 14, 16 +}; +static const int8_t pv_princess_dx[19] = { + 0, 0, 0, 0, 0, -1, 2, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const uint8_t pv_princess_parity[19] = { + 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0 +}; + +/* frame_tbl_cuts[48..85].dx. image равен frame - 48. */ +static const int8_t pv_jaffar_dx[38] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 2, 3, 5, 5, 1, 2, 2, 1, 1, 2, 3, 3, 0, 2, 2, 1 +}; +static const uint8_t pv_jaffar_parity[38] = { + 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0 +}; +static const uint8_t pv_walk_frame[6] = { 48, 49, 50, 51, 52, 53 }; +static const int8_t pv_walk_dx[6] = { 1, 2, 6, 1, -1, 1 }; +static const uint8_t pv_raise_frame[] = { + 85, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 83, 84, 76 +}; + +static int pv_actor_x(const pv_actor_t *actor, int frame_dx, uint8_t parity) +{ + int x = actor->x; + int logical; + + if (actor->left) x -= frame_dx; + else x += frame_dx; + logical = x * 2 - 116; + /* load_frame_to_obj: нечётная фаза зависит от flags кадра и direction. + * chtab Princess/Jaffar затем проходит calc_screen_x_coord(320/280). */ + if (actor->left == parity) ++logical; + return logical * 8 / 7; +} + +static void pv_walk_step(pv_actor_t *jaffar) +{ + uint8_t step = (uint8_t)(jaffar->step % 6); + int dx = pv_walk_dx[step]; + + if (jaffar->left) jaffar->x -= dx; + else jaffar->x += dx; + jaffar->frame = pv_walk_frame[step]; + ++jaffar->step; +} + +static void pv_princess_step(pv_actor_t *princess, uint16_t tick) +{ + uint16_t pos; + + /* Palert: 2..9, flip, dx(8), 11. Она продолжается и во время входа + * Jaffar, как последовательность Palert оригинала. */ + if (tick >= PV_TURN_START && tick < PV_TURN_START + 72 && + ((tick - PV_TURN_START) & 7) == 0) { + pos = (uint16_t)((tick - PV_TURN_START) >> 3); + if (pos < 8) princess->frame = (uint8_t)(2 + pos); + else { + princess->left = 0; + princess->x += 8; + princess->frame = 11; + } + } + + /* Pstepback: flip, dx(11), 12; затем 13..16 и вечный кадр 17. */ + if (tick >= PV_STEPBACK_START && tick < PV_STEPBACK_START + 42 && + ((tick - PV_STEPBACK_START) % 7) == 0) { + pos = (uint16_t)((tick - PV_STEPBACK_START) / 7); + if (pos == 0) { + princess->left = 1; + princess->x -= 11; + princess->frame = 12; + } else if (pos == 1) { + princess->x -= 1; + princess->frame = 13; + } else if (pos == 2) { + princess->x -= 1; + princess->frame = 14; + } else if (pos == 3) { + princess->x -= 3; + princess->frame = 15; + } else if (pos == 4) { + princess->x -= 1; + princess->frame = 16; + } else { + princess->frame = 17; + } + } + + /* Pslump: один переходный frame 1, затем frame 18 до fade-out. */ + if (tick == PV_SLUMP_START) princess->frame = 1; + else if (tick == PV_SLUMP_START + 7) princess->frame = 18; +} + +static void pv_jaffar_exit_step(pv_actor_t *jaffar) +{ + uint8_t pos = jaffar->step++; + + if (pos < 6) { + jaffar->frame = (uint8_t)(77 + pos); + } else if (pos < 12) { + if (pos == 6) --jaffar->x; + jaffar->frame = 54; + } else if (pos < 22) { + uint8_t frame = (uint8_t)(57 + pos - 12); + + if (frame == 62) jaffar->x -= 2; + else if (frame == 63) ++jaffar->x; + else if (frame == 64) jaffar->x += 3; + else if (frame == 66) ++jaffar->x; + jaffar->frame = frame; + } else if (pos == 22) { + /* Vexit заканчивается flip, dx(16), dx(3), затем Vwalk2. */ + jaffar->left = 0; + jaffar->x += 19; + jaffar->frame = 49; + } else { + /* Vexit прыгает на метку Vwalk2: кадр 49 уже выведен в ветке + * flip, следующий шаг обязан начинаться с dx(6), frame 50. */ + uint8_t walk = (uint8_t)(pos - 23 + 2); + int dx = pv_walk_dx[walk % 6]; + + jaffar->x += dx; + jaffar->frame = pv_walk_frame[walk % 6]; + } +} + +static void pv_jaffar_step(pv_actor_t *jaffar, uint16_t tick) +{ + uint16_t pos; + + if (tick == PV_WALK1_START || tick == PV_WALK2_START) jaffar->step = 0; + if (((tick >= PV_WALK1_START && tick < PV_DIALOG1_START) || + (tick >= PV_WALK2_START && tick < PV_DIALOG2_START)) && + ((tick - ((tick < PV_DIALOG1_START) ? PV_WALK1_START : + PV_WALK2_START)) & 7) == 0) { + jaffar->visible = 1; + pv_walk_step(jaffar); + } + + /* Vstop: dx(1), 55, 56, Vstand. */ + if (tick == PV_DIALOG1_START || tick == PV_DIALOG2_START) { + --jaffar->x; + jaffar->frame = 55; + } else if (tick == PV_DIALOG1_START + 8 || tick == PV_DIALOG2_START + 8) { + jaffar->frame = 56; + } else if ((tick >= PV_DIALOG1_START + 16 && tick < PV_WALK2_START) || + (tick >= PV_DIALOG2_START + 16 && tick < PV_RAISE_START)) { + jaffar->frame = 54; + } + + if (tick >= PV_RAISE_START && tick < PV_EXIT_START && + ((tick - PV_RAISE_START) % 7) == 0) { + pos = (uint16_t)((tick - PV_RAISE_START) / 7); + if (pos < sizeof(pv_raise_frame)) jaffar->frame = pv_raise_frame[pos]; + else jaffar->frame = 76; + } + + if (tick == PV_EXIT_START) jaffar->step = 0; + if (tick >= PV_EXIT_START && ((tick - PV_EXIT_START) % 7) == 0) { + pv_jaffar_exit_step(jaffar); + if (!jaffar->left && jaffar->x > 205) jaffar->visible = 0; + } +} + +static void pv_blit_jaffar(const atlas_t *j0, const atlas_t *j1, + const atlas_t *j2, const pv_actor_t *jaffar) +{ + uint8_t image; + int x; + + if (!jaffar->visible || jaffar->frame < 48 || jaffar->frame > 85) return; + image = (uint8_t)(jaffar->frame - 48); + x = pv_actor_x(jaffar, pv_jaffar_dx[image], pv_jaffar_parity[image]); + if (image < 13) + pv_blit_actor(j0, image, x, 166, (uint8_t)!jaffar->left); + else if (image < 24) + pv_blit_actor(j1, (uint8_t)(image - 13), x, 166, + (uint8_t)!jaffar->left); + else + pv_blit_actor(j2, (uint8_t)(image - 24), x, 166, + (uint8_t)!jaffar->left); +} + +static void pv_blit_princess(const atlas_t *princess, const pv_actor_t *actor) +{ + uint8_t frame = actor->frame; + + if (frame == 0 || frame > 18) return; + pv_blit_actor(princess, pv_princess_image[frame], + pv_actor_x(actor, pv_princess_dx[frame], + pv_princess_parity[frame]), + 166, (uint8_t)!actor->left); +} + +/* Длина логического кадра в тиках 60 Гц — cutscene_frame_time оригинала. + * Сколько это кадров луча, решает пейсинг в intro_pv_animated. */ +static uint8_t pv_seq_period(uint16_t tick) +{ + if (tick < PV_M50_END) return 6; + if (tick < PV_RAISE_START) return 8; + return 7; +} + +/* Рисует кадр и меняет страницу; возвращает, сколько кадров луча на это + * ушло (вспышка живёт на физической шкале и стоит одиннадцать). */ +static uint8_t intro_pv_draw_frame(const atlas_t *princess, const atlas_t *j0, + const atlas_t *j1, const atlas_t *j2, + const atlas_t *hourglass, const atlas_t *torch, + const pv_actor_t *p, const pv_actor_t *j, + const uint8_t *star_phase, + uint8_t torch_left, uint8_t torch_right, + uint8_t sand_frame, uint8_t flash, + uint16_t tick) +{ + gfx_rect_t full; + uint8_t back = (uint8_t)(gfx_get_visible_page() ^ 1); + + gfx_set_draw_page(back); + full.x = 0; full.y = 0; full.w = GFX_WIDTH; full.h = GFX_HEIGHT; + gfx_copy_page(&full, GFX_COPY_DIRECT); + gfx_set_bank(GFX_BANK_SPRITE); + /* y=116 в princess_room_torch — нижняя строка, не верх спрайта. */ + pv_blit_floor(torch, torch_left, 93, 116); + pv_blit_floor(torch, torch_right, 211, 116); + pv_draw_stars(star_phase); + pv_blit_princess(princess, p); + pv_blit_jaffar(j0, j1, j2, j); + if (tick >= PV_MAGIC_START) { + /* В SDLPoP hourglass_sandflow=0 означает первый кадр потока, + * а -1 — отсутствие песка. Корпус появляется ещё при молнии. */ + pv_blit_floor(hourglass, + (uint8_t)(tick < PV_GLASS_DONE ? 1 : 2), 152, 168); + } + if (tick >= PV_EXIT_START) { + /* hourglass_sandflow=0 оригинал ставит лишь после окончания + * conjuring — когда Jaffar опускает руки и начинает уходить. */ + pv_blit_floor(hourglass, (uint8_t)(8 + sand_frame), 160, 164); + } + /* draw_princess_room_bg() SDLPoP помещает image 2 chtab PV в + * foretable с x=30*8, ybottom=167. В нашем h0 это кадр 0 (res952): + * его надо повторить ПОСЛЕ актёров, иначе Jaffar проходит перед + * правой передней колонной. */ + pv_blit_floor(hourglass, 0, 240, 167); + gfx_wait_vsync(); + gfx_set_visible_page(back); + if (flash) { + uint8_t pulse; + + /* Молния намеренно живёт на физической, а не cutscene-шкале: + * пять быстрых white/black импульсов, актёры на это время стоят. */ + for (pulse = 0; pulse < 5; pulse++) { + pop_pal_flash_white(1); + gfx_wait_vsync(); + pop_pal_flash_white(0); + gfx_wait_vsync(); + } + return 11; + } + return 1; +} + +/* Сцена pv_scene() SDLPoP: ожидание Princess с факелами/звёздами, поворот, + * вход и речь Jaffar, заклинание+молния, часы с песком, уход. Тайминг + * воспроизводится на кадровой шкале; пропуск проверяется каждый кадр. */ +static int intro_pv_animated(void) +{ + atlas_t princess, jaffar0, jaffar1, jaffar2, hourglass, torch; + pv_actor_t princess_actor; + pv_actor_t jaffar_actor; + uint16_t tick; + uint16_t frame_end; + uint16_t render_tick; + uint16_t beam = 0; /* запасной путь: кадров луча ждали */ + uint16_t snd_done = 0; /* тиков насоса прошло с начала сцены */ + uint16_t snd_want = 0; /* сколько их должно было пройти */ + uint16_t snd_acc = 0; /* дробный остаток пересчёта 57/40 */ + uint8_t snd_prev = 0; + uint8_t snd_pace = 0; + uint8_t period; + uint8_t anim_step = 0; + uint8_t torch_left = 1; + uint8_t torch_right = 6; + uint8_t star_phase[6] = { 1, 1, 1, 1, 1, 1 }; + uint8_t sand_frame = 0; + uint8_t flash_done = 0; + uint8_t faded_in = 0; + uint8_t flash; + intro_skip_t skip; + int result = -1; + + intro_prepare_palette(INTRO_PAL_PV); + /* commit обязан быть до сборки base: gfx_pal_sync обновляет и видимую + * страницу, поэтому там в этот момент должен оставаться только чёрный + * растр из intro_prepare_palette(). */ + intro_commit_palette(); + if (intro_pv_draw_base() != 0) return -1; + + /* b0_* собирается полосами: на время очередной полосы ей тоже нужна + * временная EMM-страница. Поэтому фон готовится до постоянных + * actor-атласов: порядок сохраняет независимость загрузчика полос от + * числа одновременно живущих анимационных ресурсов. */ + /* Шесть атласов сцены — из ОДНОГО открытого архива. */ + { + uint8_t tbl[ARC_PV_COUNT * POP_ARC_REC]; + pop_arc_t arc; + int8_t bad; + + if (pop_arc_open_id(&arc, POP_ARC_PV, tbl, ARC_PV_COUNT) < 0) return -1; + bad = (int8_t)(pv_atlas(&arc, tbl, ARC_PV_A0, &princess) != 0); + if (!bad) bad = (int8_t)(pv_atlas(&arc, tbl, ARC_PV_J0, &jaffar0) != 0); + if (!bad) bad = (int8_t)(pv_atlas(&arc, tbl, ARC_PV_J1, &jaffar1) != 0); + if (!bad) bad = (int8_t)(pv_atlas(&arc, tbl, ARC_PV_J2, &jaffar2) != 0); + if (!bad) bad = (int8_t)(pv_atlas(&arc, tbl, ARC_PV_H0, &hourglass) != 0); + if (!bad) bad = (int8_t)(pv_atlas(&arc, tbl, ARC_PV_T0, &torch) != 0); + pop_arc_close(&arc); + if (bad) return -1; + } + + /* Проявление — ПОСЛЕ первого нарисованного кадра (см. ниже в цикле): + * иначе две секунды fade идут по пустой комнате, а принцесса возникает + * уже на свету. */ + /* Актёрские атласы в EMM; дальше по сцене диск трогает только подкачка + * следующей реплики — по одной странице за кадр (pop_music_load_step). + * Полноэкранная accel-копия кадра насосу не мешает: leaf режет DI + * бандами по 16 строк. */ + (void)pop_sfx_start(); + /* ЧЕМ МЕРИТЬ ВРЕМЯ. Пока сцена была немой, кадры просто считали + * ожидания vsync — и этого хватало. С музыкой выяснилось, что так + * считаются НАШИ ожидания, а не прошедшее время: полноэкранная копия + * страницы (~0,55 млн тактов) съедает больше кадра луча, и сцена + * незаметно отстаёт — в MAME молния била уже ПОСЛЕ конца реплики. + * Часы берём у насоса CBL: он идёт от расхода буфера железом, 85,4 Гц, + * и ему безразлично, чем занят главный цикл. Если звук выключен, + * часы стоят — тогда работает прежний путь по кадрам луча. */ + snd_prev = pop_snd_tick; + gfx_wait_vsync(); + snd_pace = (uint8_t)(pop_snd_tick != snd_prev); /* часы реально идут? */ + snd_prev = pop_snd_tick; + result = 0; + princess_actor.x = 120; + princess_actor.frame = 11; /* Pstand, facing left */ + princess_actor.left = 1; + princess_actor.visible = 1; + princess_actor.step = 0; + jaffar_actor.x = 198; + jaffar_actor.frame = 54; /* Vstand, behind the closed door */ + jaffar_actor.left = 1; + jaffar_actor.visible = 0; + jaffar_actor.step = 0; + tick = 0; + intro_skip_begin(&skip); + while (tick < PV_ANIM_TICKS) { + if (intro_skip_requested(&skip)) { + result = 1; + break; + } + + /* Один проход = один логический кадр cutscene. Внутри проходим все + * его исходные 6/8/7 тиков, снаружи draw_frame держит результат + * четыре физических кадра — единые номинальные 12,5 FPS. */ + flash = 0; + period = pv_seq_period(tick); + frame_end = (uint16_t)(tick + period); + if (frame_end > PV_ANIM_TICKS) frame_end = PV_ANIM_TICKS; + do { + pv_princess_step(&princess_actor, tick); + pv_jaffar_step(&jaffar_actor, tick); + if (tick == PV_MAGIC_START && !flash_done) { + flash = 1; + flash_done = 1; + } + /* Границы шкалы уже стоят там, где оригинал ЖДЁТ окончания + * очередного сэмпла, — заявляем эффект ровно на них. */ + if (tick == PV_WAIT_END) pop_sfx_play(PV_SND_GATE_CLOSING); + else if (tick == PV_GATE_END) pop_sfx_play(PV_SND_DOOR_OPENING); + /* Три реплики сцены. Каждая следующая читается с диска, пока + * звучит предыдущая (второй слот pop_music), поэтому здесь + * остаётся только пуск — мгновенный. */ + else if (tick == PV_MUS_2_START) pop_music_play(); + else if (tick == PV_MUS_3_LOAD) + (void)pop_music_load_begin(POP_MUS_STORY_3); + else if (tick == PV_DIALOG1_START) pop_music_play(); + else if (tick == PV_MUS_4_LOAD) + (void)pop_music_load_begin(POP_MUS_STORY_4); + else if (tick == PV_MUS_4_START) pop_music_play(); + ++tick; + } while (tick < frame_end); + + { + uint8_t star = (uint8_t)(anim_step % 6); + + torch_left = (uint8_t)((torch_left + 1) % 9); + torch_right = (uint8_t)((torch_right + 1) % 9); + star_phase[star] = (uint8_t)((star_phase[star] + 1) % 5); + if (tick > PV_EXIT_START) + sand_frame = (uint8_t)((sand_frame + 1) % 3); + ++anim_step; + } + render_tick = (uint16_t)(tick - 1); + beam += intro_pv_draw_frame(&princess, &jaffar0, &jaffar1, &jaffar2, + &hourglass, &torch, &princess_actor, + &jaffar_actor, star_phase, torch_left, + torch_right, sand_frame, flash, + render_tick); + pop_sfx_tick(); /* как в оригинале — в конце отрисовки кадра */ + /* ГАРАНТИРОВАННЫЙ ШАГ ПОДКАЧКИ, а не «если останется время». + * + * Ниже страница следующей реплики читается в ПАУЗЕ кадра, и это + * верно ровно до тех пор, пока пауза есть. В этой сцене её почти + * нет: кадр дорогой, шкала работает по принципу «долг не догоняем» + * (snd_want подтягивается к snd_done), и цикл ожидания выходит + * сразу. Замер в MAME 2026-08-26: ОДНА страница за семь секунд, + * то есть одиннадцать страниц m53 не успевали за всю сцену — + * pop_music_play() на своём тике не находил готового трека, и + * реплики Джафара пропадали совсем. + * + * Страница стоит 33 мс против кадра сцены в 100-133 мс, поэтому + * один безусловный шаг в кадр сцену не ломает: он лишь тратит + * часть той же паузы, а если паузы не было — добавляет к кадру + * четверть его длины, и шкала это учтёт сама. */ + if (pop_music_loading()) (void)pop_music_load_step(); + /* Первый кадр собран под чёрной палитрой — теперь проявляем его + * целиком, вместе с принцессой и факелами. */ + if (!faded_in) { faded_in = 1; pop_pal_fade_in(INTRO_FADE); } + /* ПЕЙСИНГ ПО ОРИГИНАЛЬНОЙ ШКАЛЕ. Кадр cutscene — 6/8/7 тиков по + * 1/60 с; один тик = 85,4/60 = 1,424 тика насоса, и 57/40 = 1,425 + * повторяет это с точностью 0,07 % (30 мс на всю сцену) без + * деления в кадре и без 32-битной арифметики. */ + if (snd_pace) { + snd_acc += (uint16_t)period * 57u; + while (snd_acc >= 40u) { snd_acc -= 40u; snd_want++; } + for (;;) { + uint8_t now = pop_snd_tick; + + /* Разность в uint8_t: счётчик переполняется каждые 3 с, а + * заглядываем мы в него не реже раза в кадр. */ + snd_done += (uint8_t)(now - snd_prev); + snd_prev = now; + if (snd_done >= snd_want) break; + /* Страницу следующей реплики читаем ЗДЕСЬ, в паузе кадра: + * 33 мс диска укладываются в ожидание (кадр сцены — 133 мс), + * и подкачка не добавляется к длительности кадра, а + * растворяется в ней. */ + if (pop_music_loading()) (void)pop_music_load_step(); + else gfx_wait_vsync(); + } + /* ДОЛГ НЕ ДОГОНЯЕМ. Подкачка следующей реплики (страница — 33 мс) + * и любой тяжёлый кадр оставляют snd_done впереди snd_want, и + * дальше цикл гнал кадры без единого ожидания: пламя факелов + * мелькало, а события соседних тиков (створка ворот и дверь + * покоев) слипались в один кадр — заявка перезаписывала заявку, и + * один из звуков пропадал. Тот же принцип, что у pop_pace_end: + * якорь ставится ПО ФАКТУ, пропущенное время не наверстывается. */ + if (snd_done > snd_want) snd_want = snd_done; + } else { + uint16_t target = POP_T60(tick); + + while (beam < target) { gfx_wait_vsync(); beam++; } + } + } + /* CBL НЕ ГЛУШИМ: последняя реплика Джафара звучит поверх смены сцены — + * ровно как в оригинале, где show_title ждёт её окончания уже на + * экране «свадьбы» (seg000:2051). Глушит вызывающий, когда музыка + * действительно доиграла. */ + if (result) { pop_music_free(); pop_sfx_pause(); } + if (!result) pop_pal_fade_out(INTRO_FADE); + + atlas_free(&torch); + atlas_free(&hourglass); + atlas_free(&jaffar2); + atlas_free(&jaffar1); + atlas_free(&jaffar0); + atlas_free(&princess); + return result; +} + +/* hourglass_frame() SDLPoP: четыре границы оставшегося времени выбирают + * один из кадров 6..2. В h0.atl номер кадра совпадает с hourglass_state. */ +static uint8_t pre_hourglass_state(void) +{ + static const uint8_t bound[] = { 6, 17, 33, 65 }; + uint8_t i; + + for (i = 0; i < sizeof(bound); i++) + if (bound[i] > pop_timer_minutes) break; + return (uint8_t)(6 - i); +} + +/* cutscene_2_6()/cutscene_4(): 26 логических кадров по 100 мс. У SDLPoP + * это 6/60 секунды, у Sprinter — 5/50, то есть 130 физических кадров. + * Статична только поза Princess; draw_princess_room_bg оригинала + * каждый логический кадр заново обновляет факелы, звёзды, корпус часов и + * одну из трёх фаз сыплющегося песка. */ +/* Живая комната принцессы БЕЗ актёрской механики: готовая композиция (или + * чистый фон, если picture == PV_ROOM_BARE), поверх неё пламя факелов, + * звёзды в окне и песочные часы заданного состояния. На этом держатся и + * сцены перед уровнями, и экран «время вышло» — разница только в картинке, + * состоянии часов и длине. + * + * hg_state: 0 — часов нет, 7 — время вышло (пустые, без струйки песка). */ +#define PV_ROOM_BARE 0xFF + +static int pre_room_animated(uint8_t picture, uint8_t state, uint8_t frames) +{ + atlas_t hourglass, torch; + gfx_rect_t full; + uint8_t torch_left = 1, torch_right = 6, sand = 0, frame; + uint8_t star_phase[6] = { 1, 1, 1, 1, 1, 1 }; + intro_skip_t skip; + int result = -1; + + intro_prepare_palette(INTRO_PAL_PV); + intro_commit_palette(); + /* Обе shadow-страницы содержат чистую статическую сцену. Динамика ниже + * рисуется GFX_BANK_SPRITE и потому не оседает в этом источнике. */ + if (picture == PV_ROOM_BARE) { + if (intro_pv_draw_base() != 0) return -1; + } else if (intro_draw_page(picture, 0) != 0 || + intro_draw_page(picture, 1) != 0) { + return -1; + } + { + uint8_t tbl[ARC_PV_COUNT * POP_ARC_REC]; + pop_arc_t arc; + int8_t bad; + + if (pop_arc_open_id(&arc, POP_ARC_PV, tbl, ARC_PV_COUNT) < 0) return -1; + bad = (int8_t)(pv_atlas(&arc, tbl, ARC_PV_H0, &hourglass) != 0); + if (!bad) bad = (int8_t)(pv_atlas(&arc, tbl, ARC_PV_T0, &torch) != 0); + pop_arc_close(&arc); + if (bad) return -1; + } + + pop_screen_present(1); + result = 0; + full.x = 0; full.y = 0; full.w = GFX_WIDTH; full.h = GFX_HEIGHT; + intro_skip_begin(&skip); + for (frame = 0; frame < frames; frame++) { + uint8_t back, hold, star; + + if (intro_skip_requested(&skip)) { result = 1; break; } + back = (uint8_t)(gfx_get_visible_page() ^ 1); + gfx_set_draw_page(back); + gfx_copy_page(&full, GFX_COPY_DIRECT); + gfx_set_bank(GFX_BANK_SPRITE); + pv_blit_floor(&torch, torch_left, 93, 116); + pv_blit_floor(&torch, torch_right, 211, 116); + pv_draw_stars(star_phase); + if (state) { + pv_blit_floor(&hourglass, state, 152, 168); + /* Струйка песка есть, пока часы идут: при state 7 время уже + * вышло (draw_hourglass, sandflow = -1). */ + if (state < 7) + pv_blit_floor(&hourglass, (uint8_t)(8 + sand), 160, 164); + } + pv_blit_floor(&hourglass, 0, 240, 167); /* передняя колонна */ + gfx_wait_vsync(); + gfx_set_visible_page(back); + /* Первый тик уже потрачен на commit: ещё четыре = 5/50 с. */ + for (hold = 1; hold < 5; hold++) gfx_wait_vsync(); + + /* Проявляем ПОСЛЕ первого кадра: иначе две секунды fade идут по + * пустой комнате, а часы и пламя возникают уже на свету. */ + if (frame == 0) pop_pal_fade_in(INTRO_FADE); + torch_left = (uint8_t)((torch_left + 1) % 9); + torch_right = (uint8_t)((torch_right + 1) % 9); + sand = (uint8_t)((sand + 1) % 3); + star = (uint8_t)(frame % 6); + star_phase[star] = (uint8_t)((star_phase[star] + 1) % 5); + } + if (!result) pop_pal_fade_out(INTRO_FADE); + + atlas_free(&torch); + atlas_free(&hourglass); + return result; +} + +/* SDLPoP transition_ltr(): подготовленная story-страница копируется на + * видимый title узкими вертикальными полосами слева направо. У оригинала + * 2px/120 FPS; здесь 4px/60 FPS даёт ту же длительность (~1.3 с), не + * удваивая число полных копий 320x256 в ограниченном bank 9. */ +/* Собрать «In the absence» в СКРЫТУЮ страницу. Отделено от показа: между + * ними успевает загрузиться трек, а на экране всё это время держится + * последний кадр титров — ровно как у оригинала, который рисует историю в + * offscreen и лишь потом пускает transition_ltr. */ +static int intro_story_prepare(void) +{ + uint8_t target = (uint8_t)(gfx_get_visible_page() ^ 1); + + return intro_draw_page(INTRO_STORY_ABSENCE, target); +} + +/* Полосовой показ подготовленной страницы живёт в pop_ui: тем же приёмом + * появляются титульная картинка финала и таблица рекордов. */ + +static void intro_flash(uint16_t ticks) +{ + uint8_t page; + + page = pop_screen_begin(); + pop_screen_fill(WHITE); + pop_screen_present(page); + while (ticks--) gfx_wait_vsync(); + /* Вспышка ничего не меняет в сценарии: вернуть ту же собранную стадию. */ + intro_draw(intro_scene); +} + +static void intro_restore_game_palette(void) +{ + /* Обе страницы — именно чёрный растр до gfx_pal_fload/sync. Иначе + * синхронизация palette 1 способна на один кадр высветить credits перед + * переходом в demo. */ + intro_black_pages(); + pop_pal_game_load(); + pop_pal_apply(POP_PAL_DIM_BLACK); +} + +/* ================= ПЕРСОНАЖИ КАТСЦЕН ================================= * + * + * Порт cutscene_8 / cutscene_9 (seg001:034D/03B7) и end_sequence_anim + * (seg001:041C). Все три идут через load_intro(1, …): фон — комната + * принцессы (та же b0_*, что у intro), а поверх играют ДВА персонажа. + * + * Движок общий с игрой, и это главное: последовательности крутит наш + * play_seq (pop_kid_play/pop_guard_play), кадры принцессы берутся из + * таблицы КАТСЦЕН (kid.ani, KID_BIN_CFRAMES_OFF), мышь и Кид — из + * таблицы Кида, как в оригинале (frame_table_kid общая для kid и mouse). + * Отличается только ОТРИСОВКА: спрайты принцессы лежат в PV-атласах, а + * Кид/мышь — в игровых kid-атласах, поэтому в pv.pal под них отведена + * своя палитровая строка 0x70. + * + * Слоты — как в оригинале: Kid-окно занимает мышь (или Кид в финале), + * Guard-окно — принцесса. + */ + +/* Набор PV2 (res901..930) не влезает в одну EMM: image 0..16 в a1, + * 17..29 в a2. Номер кадра при этом остаётся исходным image-id. */ +#define CUT_PV2_SPLIT 17 +/* Один логический кадр катсцены = 5 vsync, как у остальных PV-сцен + * (cutscene_frame_time SDLPoP; см. pre_room_animated). */ +#define CUT_FRAME_VSYNC 5 + +static atlas_t cut_pv1, cut_pv2a, cut_pv2b, cut_hg, cut_torch; +static uint8_t cut_atlases; /* сколько успели загрузить — столько и вернём */ +/* Детектор фронта живёт на ВСЮ сцену: она склеена из нескольких cut_run, и + * взводить его заново на каждом отрезке значило бы ловить одно удержание + * несколько раз. */ +static intro_skip_t cut_skip; +/* Фазы «живого» фона: пламя двух факелов, песок и мерцание звёзд. Счётчики + * общие на всю сцену — она склеена из нескольких cut_run. */ +static uint8_t cut_torch_l, cut_torch_r, cut_sand, cut_star_phase[6]; +static uint8_t cut_hg_state; + +static void cut_free(void) +{ + if (cut_atlases > 4) atlas_free(&cut_torch); + if (cut_atlases > 3) atlas_free(&cut_hg); + if (cut_atlases > 2) atlas_free(&cut_pv2b); + if (cut_atlases > 1) atlas_free(&cut_pv2a); + if (cut_atlases > 0) atlas_free(&cut_pv1); + cut_atlases = 0; +} + +static int cut_load(void) +{ + uint8_t tbl[ARC_PV_COUNT * POP_ARC_REC]; + pop_arc_t arc; + + cut_atlases = 0; + if (pop_arc_open_id(&arc, POP_ARC_PV, tbl, ARC_PV_COUNT) < 0) return -1; + if (pv_atlas(&arc, tbl, ARC_PV_A0, &cut_pv1) != 0) { + pop_arc_close(&arc); return -1; + } + cut_atlases = 1; + if (pv_atlas(&arc, tbl, ARC_PV_A1, &cut_pv2a) != 0) { + pop_arc_close(&arc); cut_free(); return -1; + } + cut_atlases = 2; + if (pv_atlas(&arc, tbl, ARC_PV_A2, &cut_pv2b) != 0) { + pop_arc_close(&arc); cut_free(); return -1; + } + cut_atlases = 3; + /* Комната принцессы живёт и в катсценах: пламя факелов, звёзды в окне, + * песок в часах — и ПЕРЕДНЯЯ КОЛОННА, которая по оригиналу закрывает + * персонажей (draw_princess_room_bg + foretable). */ + if (pv_atlas(&arc, tbl, ARC_PV_H0, &cut_hg) != 0) { + pop_arc_close(&arc); cut_free(); return -1; + } + cut_atlases = 4; + if (pv_atlas(&arc, tbl, ARC_PV_T0, &cut_torch) != 0) { + pop_arc_close(&arc); cut_free(); return -1; + } + cut_atlases = 5; + pop_arc_close(&arc); + return 0; +} + +/* Экранная X кадра: та же математика, что у актёров intro — logical + * координата (x*2 − 116) с нечётной фазой по flags кадра, затем + * calc_screen_x_coord (×8/7). */ +static int cut_screen_x(const pop_char_t *ch, const kframe *fr) +{ + int x = ch->x; + int logical; + uint8_t left = (uint8_t)(ch->direction < 0); + + if (left) x -= fr->dx; + else x += fr->dx; + logical = x * 2 - 116; + if (left == (uint8_t)((fr->flags & 0x80) != 0)) ++logical; + return logical * 8 / 7; +} + +/* Принцесса: набор выбирает поле sword кадра (>>6: 1 = PV1 res800, + * 2 = PV2 res900) — ровно как chtab_base + (cur_frame.sword >> 6) + * в seg008:1759. */ +static void cut_blit_princess(const pop_char_t *ch, const kframe *fr) +{ + const atlas_t *at; + uint8_t idx; + + if (fr->image == 255 || cut_atlases < 3) return; + if ((uint8_t)(fr->sword >> 6) >= 2) { + if (fr->image < CUT_PV2_SPLIT) { at = &cut_pv2a; idx = fr->image; } + else { at = &cut_pv2b; idx = (uint8_t)(fr->image - CUT_PV2_SPLIT); } + } else { + at = &cut_pv1; idx = fr->image; + } + pv_blit_actor(at, idx, cut_screen_x(ch, fr), + (int)ch->y + fr->dy, (uint8_t)(ch->direction >= 0)); +} + +/* Кид и мышь — спрайты chtab_2 из игровых атласов (kidp[]). Атласы Кида + * хранятся column-major (ради бесплатного флипа), поэтому блит колоночный. */ +static void cut_blit_kid(const pop_char_t *ch, const kframe *fr) +{ + const uint8_t *img; + uint8_t pg, idx, flip; + int x; + + if (fr->image == 255) return; + pg = (uint8_t)(fr->image >> 3); + idx = (uint8_t)(fr->image & 7); + if (pg >= kid_npages) return; + flip = (uint8_t)(ch->direction >= 0); + x = cut_screen_x(ch, fr); + gfx_w0_map(kidp[pg].page); + img = (const uint8_t *)atlas_image(&kidp[pg], idx); + if (flip) x -= img[0]; + gfx_blit_cols(x, POP_YOFF + (int)ch->y + fr->dy - img[2] + 1, img, flip); + gfx_w0_unmap(); +} + +/* Сменить последовательность персонажа в его окне (seqtbl_offset_kid_char / + * seqtbl_offset_shad_char оригинала). */ +static void cut_kid_seq(uint8_t seq) +{ + pop_loadkid(); pop_char_set_seq(seq); pop_savekid(); +} + +static void cut_shad_seq(uint8_t seq) +{ + pop_loadshad(); pop_char_set_seq(seq); pop_saveshad(); +} + +/* Поставить персонажа так, как это делают init_* оригинала: позиция, + * направление и стартовая последовательность, затем первый play_seq. */ +static void cut_kid_init(uint8_t charid, uint8_t x, uint8_t y, uint8_t seq) +{ + Kid.charid = charid; + Kid.x = x; + Kid.y = y; + Kid.direction = -1; /* dir_FF_left */ + Kid.alive = -1; + Kid.frame = 0; + Kid.fall_x = Kid.fall_y = 0; + Kid.repeat = 0; + cut_kid_seq(seq); + pop_kid_play(); +} + +static void cut_shad_init(uint8_t x, uint8_t y, uint8_t seq) +{ + Guard.charid = CHARID_5_PRINCESS; + Guard.x = x; + Guard.y = y; + Guard.direction = -1; + Guard.alive = -1; + Guard.frame = 0; + Guard.fall_x = Guard.fall_y = 0; + Guard.repeat = 0; + cut_shad_seq(seq); + pop_guard_play(); +} + +/* proc_cutscene_frame(n): прокрутить n логических кадров, каждый — шаг + * последовательности обоих персонажей плюс отрисовка. Возврат 1 — игрок + * прервал сцену (любое НОВОЕ нажатие). */ +static uint8_t cut_run(uint16_t frames, uint8_t kid_visible) +{ + gfx_rect_t full; + uint16_t i; + uint8_t hold; + + full.x = 0; full.y = 0; full.w = GFX_WIDTH; full.h = GFX_HEIGHT; + for (i = 0; i < frames; i++) { + uint8_t back; + kframe kid_fr, pr_fr; + + if (intro_skip_requested(&cut_skip)) return 1; + + /* Кадр забираем СРАЗУ после play каждого слота. load_frame кладёт + * его в кэш ПО charid активного персонажа: у Кида это kid_frame, у + * всех прочих — общий pop_gframe. В Kid-окне здесь сидит МЫШЬ + * (charid 24), то есть её кадр уходит в pop_gframe и затирается + * следом кадром принцессы, а kid_frame держит позу игрового Кида. + * Читать кэши в конце кадра значило рисовать мышь спрайтом Кида и + * терять принцессу — ровно это и было видно в сценах 8/9. */ + pop_kid_play(); /* мышь либо Кид */ + kid_fr = (Kid.charid == CHARID_0_KID) ? kid_frame : pop_gframe; + pop_guard_play(); /* принцесса */ + pr_fr = pop_gframe; + + back = (uint8_t)(gfx_get_visible_page() ^ 1); + gfx_set_draw_page(back); + /* Чистый фон обеих страниц лежит в shadow-копии; персонажи рисуются + * банком спрайтов и потому в неё не оседают. */ + gfx_copy_page(&full, GFX_COPY_DIRECT); + gfx_set_bank(GFX_BANK_SPRITE); + pv_blit_floor(&cut_torch, cut_torch_l, 93, 116); + pv_blit_floor(&cut_torch, cut_torch_r, 211, 116); + pv_draw_stars(cut_star_phase); + /* hourglass_state == 0 — часов в комнате нет вовсе (reset_cutscene + * SDLPoP ставит state=0 и sandflow=-1). Так идёт финальная встреча + * с принцессой: время уже не считается, это победа. + * state 7 — время ВЫШЛО: часы стоят пустыми, и струйки песка в них + * тоже нет (draw_hourglass: `if (hourglass_state >= 7) return;`). */ + if (cut_hg_state) { + pv_blit_floor(&cut_hg, cut_hg_state, 152, 168); + if (cut_hg_state < 7) + pv_blit_floor(&cut_hg, (uint8_t)(8 + cut_sand), 160, 164); + } + cut_blit_princess(&Guard, &pr_fr); + if (kid_visible) cut_blit_kid(&Kid, &kid_fr); + /* ПОСЛЕ персонажей: передняя колонна — передний слой комнаты, она + * обязана закрывать и принцессу, и мышь (иначе мышь «просвечивает» + * сквозь неё — поймано пользователем 2026-08-25). */ + pv_blit_floor(&cut_hg, 0, 240, 167); + gfx_set_bank(GFX_BANK_NORMAL); + gfx_wait_vsync(); + gfx_set_visible_page(back); + for (hold = 1; hold < CUT_FRAME_VSYNC; hold++) gfx_wait_vsync(); + + pop_sfx_tick(); + pop_music_service(); /* «встреча с принцессой» — трек, а не эффект */ + cut_torch_l = (uint8_t)((cut_torch_l + 1) % 9); + cut_torch_r = (uint8_t)((cut_torch_r + 1) % 9); + cut_sand = (uint8_t)((cut_sand + 1) % 3); + { + uint8_t star = (uint8_t)(i % 6); + cut_star_phase[star] = (uint8_t)((cut_star_phase[star] + 1) % 5); + } + } + return 0; +} + +/* Общая обвязка всех трёх сцен: чистый фон комнаты принцессы в обе + * страницы, PV-палитра, актёрские атласы. hg_state — кадр песочных часов + * (0 = часов нет). Возврат -1 — ресурсов нет + * (неполный HDD), и caller откатывается на статический сценарий. */ +static int cut_begin(uint8_t hg_state) +{ + uint8_t i; + + intro_skip_begin(&cut_skip); + cut_torch_l = 1; cut_torch_r = 6; cut_sand = 0; + for (i = 0; i < 6; i++) cut_star_phase[i] = 1; + cut_hg_state = hg_state; + intro_prepare_palette(INTRO_PAL_PV); + intro_commit_palette(); + if (intro_pv_draw_base() != 0) return -1; + if (cut_load() != 0) return -1; + /* Атласы сцены прочитаны — можно открывать вывод. Без этого сцена шла + * молча: pop_ending_show глушит CBL первым действием, а включить его + * обратно было некому, и «arrived to princess» (26) не звучал вовсе. */ + (void)pop_sfx_start(); + return 0; +} + +/* Прервали ли последнюю живую сцену клавишей — нужно вызывающему: по + * пропуску музыку обрываем, а не дослушиваем. */ +static uint8_t cut_aborted; + +static void cut_end(uint8_t aborted) +{ + cut_aborted = aborted; + if (!aborted) pop_pal_fade_out(INTRO_FADE); + cut_free(); +} + +/* cutscene_8 (seg001:034D): мышь сидит рядом с принцессой, встаёт и + * убегает; принцесса поднимается ей вслед. */ +static int cut_scene_8(void) +{ + uint8_t aborted; + + if (cut_begin(pre_hourglass_state()) != 0) return -1; + /* init_mouse_cu8 (seg001:0212) = init_mouse_go + x=144 + seq_106. */ + cut_kid_init(CHARID_24_MOUSE, 144, 167, 106); + /* princess_crouching (seg001:024D): сидит и гладит мышь. */ + cut_shad_init(131, 169, 110); + /* ПЕРВЫЙ КАДР — ДО ПРОЯВЛЕНИЯ. fade_in занимает две секунды, и всё это + * время на экране была бы пустая комната: персонажи появляются только с + * первым кадром цикла. Рисуем его при ещё чёрной палитре — тогда + * картинка проявляется целиком, вместе с актёрами. */ + aborted = cut_run(1, 1); + if (!aborted) pop_pal_fade_in(INTRO_FADE); + if (!aborted) aborted = cut_run(19, 1); + if (!aborted) { + cut_kid_seq(107); /* мышь встаёт и уходит */ + aborted = cut_run(20, 1); + } + if (!aborted) { + cut_shad_seq(111); /* принцесса встаёт */ + aborted = cut_run(20, 1); + } + cut_end(aborted); + return 0; +} + +/* cutscene_9 (seg001:03B7): принцесса стоит, мышь прибегает, принцесса + * приседает к ней. */ +static int cut_scene_9(void) +{ + uint8_t aborted; + + if (cut_begin(pre_hourglass_state()) != 0) return -1; + /* princess_stand (seg001:026A) — поза PV1, лицом вправо. */ + cut_shad_init(144, 169, 94); + Guard.direction = 0; + /* ПЕРВЫЙ КАДР — ДО ПРОЯВЛЕНИЯ. fade_in занимает две секунды, и всё это + * время на экране была бы пустая комната: персонажи появляются только с + * первым кадром цикла. Рисуем его при ещё чёрной палитре — тогда + * картинка проявляется целиком, вместе с актёрами. */ + aborted = cut_run(1, 0); /* мышь ещё за кадром */ + if (!aborted) pop_pal_fade_in(INTRO_FADE); + /* init_mouse_go (seg001:022A): мышь вбегает справа. */ + cut_kid_init(CHARID_24_MOUSE, 199, 167, 105); + if (!aborted) aborted = cut_run(5, 1); + if (!aborted) { + cut_shad_seq(112); /* принцесса приседает */ + aborted = cut_run(9, 1); + } + if (!aborted) { + cut_kid_seq(114); /* мышь останавливается */ + aborted = cut_run(58, 1); + } + cut_end(aborted); + return 0; +} + +/* cutscene_12 (seg001:0525), ветка «времени мало»: принцесса стоит правее + * обычного, через два кадра оборачивается — и всё. Живой её делает та же + * машинка, что сцены 8/9: часы здесь обязаны показывать ОСТАВШЕЕСЯ время, + * а не запечённую в композицию картинку. */ +static int cut_scene_12_short(void) +{ + uint8_t aborted; + + if (cut_begin(pre_hourglass_state()) != 0) return -1; + /* init_princess_x156 (seg001:0230) = init_princess + x = 156. */ + cut_shad_init(156, 166, 94); + /* ПЕРВЫЙ КАДР — ДО ПРОЯВЛЕНИЯ. fade_in занимает две секунды, и всё это + * время на экране была бы пустая комната: персонажи появляются только с + * первым кадром цикла. Рисуем его при ещё чёрной палитре — тогда + * картинка проявляется целиком, вместе с актёрами. */ + aborted = cut_run(1, 0); + if (!aborted) pop_pal_fade_in(INTRO_FADE); + if (!aborted) aborted = cut_run(1, 0); + if (!aborted) { + cut_shad_seq(98); /* princess turn around [PV1] */ + aborted = cut_run(24, 0); + } + cut_end(aborted); + return 0; +} + +/* end_sequence_anim (seg001:041C): Кид вбегает к принцессе, она + * поворачивается и обнимает его; следом появляется мышь. */ +static int cut_ending(void) +{ + uint8_t aborted; + + /* end_sequence_anim (seg001:041C) НЕ трогает hourglass_state, а + * reset_cutscene перед сценой обнулил его: песочных часов в финале нет. + * Это и логично — уровень пройден, время больше не идёт. */ + if (cut_begin(0) != 0) return -1; + /* init_ending_princess (seg001:0251) + init_ending_kid (seg001:0287). */ + cut_shad_init(136, 164, 109); + cut_kid_init(CHARID_0_KID, 198, 164, 1); + /* ПЕРВЫЙ КАДР — ДО ПРОЯВЛЕНИЯ. fade_in занимает две секунды, и всё это + * время на экране была бы пустая комната: персонажи появляются только с + * первым кадром цикла. Рисуем его при ещё чёрной палитре — тогда + * картинка проявляется целиком, вместе с актёрами. */ + aborted = cut_run(1, 1); + if (!aborted) pop_pal_fade_in(INTRO_FADE); + pop_sfx_play(26); /* «arrived to princess» */ + if (!aborted) aborted = cut_run(7, 1); + if (!aborted) { + cut_shad_seq(108); /* поворот и объятие */ + aborted = cut_run(5, 1); + } + if (!aborted) { + cut_kid_seq(13); /* Кид останавливается */ + aborted = cut_run(2, 1); + } + /* Kid.frame = 0 (seg001:0348): дальше Кида не видно — он в объятиях, + * и рисуется только принцесса. */ + if (!aborted) aborted = cut_run(39, 0); + if (!aborted) { + /* init_mouse_1 (seg001:02D5): мышь приходит к паре. */ + cut_kid_init(CHARID_24_MOUSE, 197, 164, 105); + aborted = cut_run(9, 1); + } + if (!aborted) { + cut_kid_seq(101); /* мышь встаёт на задние лапы */ + aborted = cut_run(41, 1); + } + cut_end(aborted); + return 0; +} + +static uint8_t intro_run(const pop_cs_cmd_t *script) +{ + pop_cutscene_t cs; + intro_skip_t skip; + uint16_t arg; + uint8_t aborted = 0; + + pop_cutscene_begin(&cs, script); + intro_skip_begin(&skip); + for (;;) { + pop_cs_event_t ev; + + if (intro_skip_requested(&skip)) { + pop_cutscene_abort(&cs); + aborted = 1; + break; + } + ev = pop_cutscene_tick(&cs, &arg); + if (ev == POP_CS_EVENT_END) break; + if (ev == POP_CS_EVENT_PALETTE) intro_prepare_palette((uint8_t)arg); + else if (ev == POP_CS_EVENT_SHOW) { + intro_scene = (uint8_t)arg; + if ((intro_pal_pending ? intro_draw_new_palette(intro_scene) : + intro_draw(intro_scene)) != 0) { + pop_cutscene_abort(&cs); + aborted = 1; + break; + } + } else if (ev == POP_CS_EVENT_FADE_IN) pop_pal_fade_in(INTRO_FADE); + else if (ev == POP_CS_EVENT_FADE_OUT) pop_pal_fade_out(INTRO_FADE); + else if (ev == POP_CS_EVENT_FLASH) intro_flash(arg); + pop_music_service(); /* подкачка потокового трека (финал) */ + gfx_wait_vsync(); + } + + return aborted; +} + + +/* Ожидание кадров у шагов, которые показываются НЕ сценарием, а напрямую. + * Оно не глушит поток: победная тема играет кольцом страниц, и дочитывать + * её надо каждый кадр, иначе насос догонит загрузчика. Прерывается новым + * нажатием (детектор фронта); 1 — прервано. */ +static uint8_t intro_wait_frames(uint16_t frames) +{ + intro_skip_t skip; + + intro_skip_begin(&skip); + while (frames--) { + if (intro_skip_requested(&skip)) return 1; + pop_music_service(); + pop_wait_edge(); + } + return 0; +} + +/* Титры проявляются полосами, как и всё остальное в show_title() + * (draw_full_image(STORY_CREDITS) + transition_ltr, seg000:2055). */ +static uint8_t intro_credits_ltr(void) +{ + uint8_t page = pop_screen_begin(); + + if (intro_draw_page(INTRO_STORY_CREDITS, page) == 0) + pop_screen_present_ltr(); + return intro_wait_frames(POP_T60(0x168)); +} + +uint8_t pop_intro_show(void) __banked +{ + int pv_result; + + /* МУЗЫКА ПРИХОДИТ СЮДА ЗВУЧАЩЕЙ. На титрах играет тема (m54), и + * обрывать её нельзя: у оригинала заставка и история — одна звуковая + * дорожка, где следующая реплика начинается там, где кончилась + * предыдущая. Поэтому здесь ни pop_sfx_pause, ни pop_sfx_start: + * CBL уже открыт титрами. */ + if (intro_story_prepare() != 0) { + /* Ошибка файла не должна оставить старый title без выхода: менее + * эффектный, но корректный fallback остаётся обычным fade-in. */ + pop_music_free(); + pop_sfx_pause(); + intro_prepare_palette(INTRO_PAL_STORY); + if (intro_draw_new_palette(INTRO_STORY_ABSENCE) == 0) + pop_pal_fade_in(INTRO_FADE); + } else { + /* Картинка собрана и ждёт в скрытой странице; читаем следующую + * реплику во ВТОРОЙ слот — тема титров при этом продолжает + * звучать из первого. */ + (void)pop_music_load(POP_MUS_STORY_1); + /* Тему дослушиваем до конца, как seg000:2027, и только потом + * пускаем историю: пуск и полосовой переход идут встык. */ + { + intro_skip_t head; + intro_skip_begin(&head); + while (pop_music_busy() && !intro_skip_requested(&head)) + pop_wait_edge(); + } + (void)pop_sfx_start(); /* идемпотентно: титры его уже открыли */ + pop_music_play(); + pop_screen_present_ltr(); + } + if (intro_run(intro_before_pv)) { + /* Пропуск по клавише. ГЛУШИМ ВЫВОД: сразу за возвратом пойдёт + * загрузка уровня, а при открытом CBL ESTEX уходит в диск дольше, + * чем играет буфер, и железо начинает крутить свои 256 байт по + * кругу — на слух ровно тот скрежет, что ловится нажатием клавиши + * на первом экране истории. */ + pop_music_free(); + pop_sfx_pause(); + intro_restore_game_palette(); + return 1; + } + /* ДОИГРАТЬ ПОД ЧЁРНЫМ ЭКРАНОМ. Оригинал не обрывает story-трек на + * переходе к PV: fade уводит картинку, а музыка продолжается, и лишь + * когда она смолкла, начинается сцена с принцессой (у SDLPoP это + * общий приём — `while (check_sound_playing())`, seg000:2027). */ + { + intro_skip_t tail; + intro_skip_begin(&tail); + while (pop_music_busy() && !intro_skip_requested(&tail)) pop_wait_edge(); + } + /* Первая реплика PV читается ЗДЕСЬ, под чёрным экраном и в тишине: + * дальше внутри сцены остаётся только пуск, а следующие две реплики + * подкачиваются по странице за кадр. */ + pop_sfx_pause(); + (void)pop_music_load(POP_MUS_STORY_2); + pv_result = intro_pv_animated(); + if (pv_result > 0) { + pop_music_free(); + pop_sfx_pause(); + intro_restore_game_palette(); + return 1; + } + if (pv_result < 0 && intro_run(intro_pv_fallback)) { + pop_music_free(); + pop_sfx_pause(); + intro_restore_game_palette(); + return 1; + } + /* «Свадьба» показывается ПОВЕРХ звучащей реплики Джафара: она началась + * в PV и переживает и fade-out сцены, и fade-in этого экрана. */ + if (intro_run(intro_marry)) { + pop_music_free(); + pop_sfx_pause(); + intro_restore_game_palette(); + return 1; + } + { + intro_skip_t marry; + intro_skip_begin(&marry); + while (pop_music_busy() && !intro_skip_requested(&marry)) + pop_wait_edge(); + } + pop_music_free(); + pop_sfx_pause(); + /* ТИТУЛЬНЫЙ ЭКРАН МЕЖДУ «СВАДЬБОЙ» И ТИТРАМИ. show_title() рисует + * TITLE_MAIN + логотип + Jordan Mechner и проявляет их полосами + * (seg000:2045), и только через 0x78 тиков показывает credits — тоже + * полосами. Раньше обе картинки возникали мгновенно. */ + if (pop_title_logo_ltr() == 0 && intro_wait_frames(POP_T60(0x78))) { + intro_restore_game_palette(); + return 1; + } + if (intro_credits_ltr()) { + intro_restore_game_palette(); + return 1; + } + /* ТАБЛИЦА РЕКОРДОВ между credits и attract-demo — ровно там же, где её + * показывает show_title(): та же story-палитра, тот же полосовой + * переход, никакого fade (seg000:2060). Пустой таблицы это не + * касается: она просто не показывается. */ + if (pop_hof_show_title()) { + intro_restore_game_palette(); + return 1; + } + if (intro_run(intro_tail)) { + intro_restore_game_palette(); + return 1; + } + /* Abort возможен в любой WAIT и не оставляет загруженных атласов: каждая + * полоса освобождена внутри intro_draw. */ + intro_restore_game_palette(); + return 0; +} + +/* Хвост любой сцены перед уровнем. + * + * ДОСЛУШИВАЕМ ПОД ЧЁРНЫМ ЭКРАНОМ, а не «пусть играет в игре». Оригинал + * так и делает: следующий load_intro начинается с ожидания тишины + * (seg001:681), и к отрисовке уровня трек уже смолк. Мы сначала отдали + * музыку в игру — и она там спотыкалась: загрузка уровня (ESTEX + сборка + * комнаты) не даёт насосу долить блок вовремя. Ждать здесь дешевле, чем + * слушать заикание. + * + * Пропуск сцены обрывает и музыку: игрок нажал клавишу, чтобы идти + * дальше, а не чтобы досидеть до конца трека. */ +static void pre_cut_finish(uint8_t aborted) +{ + if (!aborted) { + intro_skip_t tail; + + intro_skip_begin(&tail); + while (pop_music_busy() && !intro_skip_requested(&tail)) + pop_wait_edge(); + } + pop_music_free(); + pop_sfx_pause(); + intro_restore_game_palette(); +} + +void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked +{ + const pop_cs_cmd_t *script = 0; + + pop_sfx_pause(); + /* МУЗЫКА ПЕРЕЖИВАЕТ СЦЕНУ. Треки заставок между уровнями длиннее самих + * сцен (27 — 10,7 с против 2,6 с картинки), и оригинал их не обрывает: + * load_intro просто гасит экран и возвращает управление, а звук + * продолжает играть уже поверх загрузки уровня и первых секунд игры + * (seg001:690). Мы глушили CBL на выходе — музыка обрывалась на + * полуслове. Теперь не глушим: доиграет сама, а следующая загрузка + * трека всё равно освободит слот. + * + * Какой именно трек — как в оригинале: 27 для 2/4/6/12 (seg001:401), + * 35 для 8/9 (seg001:298), 40 для короткой «времени мало» (seg001:388). + * Экран в этот момент чёрный, поэтому читаем трек целиком: пауза на + * диск здесь никому не мешает. */ + { + uint8_t track = 27; + + if (scene == POP_PRE_8 || scene == POP_PRE_9) track = 35; + else if (scene == POP_PRE_12_SHORT) track = 40; + if (pop_music_load(track) == 0) { + (void)pop_sfx_start(); + pop_music_play(); + } + } + if (scene == POP_PRE_2_6 || scene == POP_PRE_4 || scene == POP_PRE_12) { + int r = pre_room_animated((uint8_t)(scene == POP_PRE_4 ? INTRO_PV_LYING + : INTRO_PV_STAND), + pre_hourglass_state(), 26); + if (r >= 0) { + pre_cut_finish((uint8_t)(r > 0)); + return; + } + /* Неполный HDD: статический fallback сохраняет корректные 130 тиков. */ + } + if (scene == POP_PRE_12_SHORT && cut_scene_12_short() == 0) { + pre_cut_finish(cut_aborted); + return; + } + /* Сцены 8/9 — живая анимация мыши и принцессы (cutscene_8/cutscene_9). */ + if (scene == POP_PRE_8 || scene == POP_PRE_9) { + int done = (scene == POP_PRE_8) ? cut_scene_8() : cut_scene_9(); + if (done == 0) { + pre_cut_finish(cut_aborted); + return; + } + } + if (scene == POP_PRE_2_6 || scene == POP_PRE_12) script = pre_2_6; + else if (scene == POP_PRE_4) script = pre_4; + else if (scene == POP_PRE_8) script = pre_8; + else if (scene == POP_PRE_9) script = pre_9; + else if (scene == POP_PRE_12_SHORT) script = pre_12_short; + if (!script) { + pre_cut_finish(1); + return; + } + + pre_cut_finish(intro_run(script)); +} + +/* Фон таблицы рекордов — та же рамка story плюс логотип PRINCE OF PERSIA + * (STORY_FRAME + HOF_POP оригинала). Рисуем в ЗАДАННУЮ страницу и её не + * показываем: палитрой, строками и переходом распоряжается pop_hof. */ +int pop_intro_hof_bg(uint8_t page) __banked +{ + return intro_draw_page(INTRO_STORY_HOF, page); +} + +void pop_time_expired_show(void) __banked +{ + pop_sfx_pause(); + /* time_expired (seg001:04D3) начинается с play_sound(sound_36). */ + if (pop_music_load(36) == 0) { + (void)pop_sfx_start(); + pop_music_play(); + } + /* ПРИНЦЕССЫ ЗДЕСЬ НЕТ. Оригинал не зовёт ни одного init_*: экран — + * это пустая комната и ОСТАНОВИВШИЕСЯ часы (state 7, струйки песка + * нет). Мы показывали готовую композицию с принцессой и полными + * часами — то есть ровно противоположное по смыслу. + * Длина — 2 + 100 кадров cutscene, как в оригинале. */ + if (pre_room_animated(PV_ROOM_BARE, 7, 102) < 0) + intro_run(time_expired_script); /* нет ресурсов — старый путь */ + /* Оригинал ждёт КОНЦА ТРЕКА уже под чёрным экраном (seg001:374). */ + { + intro_skip_t tail; + + intro_skip_begin(&tail); + while (pop_music_busy() && !intro_skip_requested(&tail)) + pop_wait_edge(); + } + pop_music_free(); + pop_sfx_pause(); + intro_restore_game_palette(); +} + + +void pop_ending_show(void) __banked +{ + pop_sfx_pause(); + /* Экран гасим ПЕРВЫМ действием — как load_intro оригинала, который + * начинается с draw_rect(&screen_rect, color_0_black). Иначе загрузка + * ресурсов сцены идёт поверх ЖИВОЙ комнаты уровня 14, и она «мелькает» + * перед встречей. */ + pop_pal_apply(POP_PAL_DIM_BLACK); + /* Встреча с принцессой — живая сцена (end_sequence_anim). Дальше по + * скрипту идёт только текст HAIL, поэтому статический сценарий ниже + * начинается с него; при нехватке ресурсов отыгрывается целиком. */ + if (cut_ending() == 0) { + /* ПОБЕДНАЯ ТЕМА (56) — потоком с диска: 115 секунд и 1,2 МБ в память + * не влезают никак. Оригинал пускает её здесь же, сразу после + * встречи с принцессой и до экрана HAIL (seg001:583). Дальше + * страницы дочитывает pop_music_service из циклов сцен. */ + (void)pop_sfx_start(); + (void)pop_music_ring_start(POP_MUS_WON); + intro_run(ending_hail_script); + } else { + intro_run(ending_script); + } + /* ТИТУЛЬНАЯ КАРТИНКА ПОВЕРХ ТЕКСТА. end_sequence заводит таймер на 240 + * тиков, проявляет TITLE_MAIN полосами и ждёт его (seg001:590). Экран + * при этом не гаснет: рамка HAIL просто переползает в рисунок дворца. + * Дальше — состояние HALL_OF_FAME; тема продолжает звучать, и + * освобождает её уже таблица рекордов, как в оригинале. */ + if (pop_title_main_ltr() == 0) + (void)intro_wait_frames(POP_T60(240)); +} diff --git a/applications/SprPoP/src/pop_intro.h b/applications/SprPoP/src/pop_intro.h new file mode 100644 index 0000000..105d960 --- /dev/null +++ b/applications/SprPoP/src/pop_intro.h @@ -0,0 +1,29 @@ +/* pop_intro.h — story/PV intro после оригинальной заставки (FG8). */ +#ifndef POP_INTRO_H +#define POP_INTRO_H + +#include "pop_flow.h" + +/* Показать absence -> Princess/Jaffar -> marry -> credits. Возврат 1 — + * пользователь прервал цепочку и надо сразу начать FIRST_LEVEL; 0 — intro + * завершилось штатно и далее запускается attract-demo. */ +uint8_t pop_intro_show(void) __banked; + +/* Показать одну pre-level сцену 2/4/6/8/9/12. NONE — no-op. После любого + * skip возвращается затемнённая игровая палитра, далее caller грузит уровень. */ +void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked; + +/* Отдельная сцена истёкшего времени (PV hourglass), после которой автомат + * возвращается в TITLE. Любой raw-ввод сокращает ожидание. */ +void pop_time_expired_show(void) __banked; + +/* Финал level 14: Princess room, story-screen HAIL и титульная картинка. + * Победная тема при выходе ЕЩЁ ЗВУЧИТ: её дослушивает таблица рекордов, + * как в end_sequence оригинала. */ +void pop_ending_show(void) __banked; + +/* Фон таблицы рекордов (рамка story + логотип) в заданную СКРЫТУЮ + * страницу; -1 — ресурс не прочитан. Ни палитры, ни показа. */ +int pop_intro_hof_bg(uint8_t page) __banked; + +#endif /* POP_INTRO_H */ diff --git a/applications/SprPoP/src/pop_kboot.c b/applications/SprPoP/src/pop_kboot.c new file mode 100644 index 0000000..9405989 --- /dev/null +++ b/applications/SprPoP/src/pop_kboot.c @@ -0,0 +1,123 @@ +/* + * pop_kboot.c — ЗАГРУЗКА ресурсов Кида: атласы chtab_2 (kid0..27.atl) и + * страница таблиц анимации (kid.ani). БАНК 8. + * + * Отрабатывает раз за игру (и на выходе — освобождение), поэтому места в + * резиденте W1 не занимает: контракт с интерпретатором — _pop_kid.h, там + * же дескриптор страницы данных. + */ +#include +#include +#include +#include +#include +#include "pop_kid.h" +#include "_pop_kid.h" +#include "kid_data.h" +#include "pop_vflip.h" +#include "pop_guard.h" /* pop_guard_vflip_load — зеркала соперника */ +#include "pop_arc.h" +#include "kid_arc.h" + +/* Серии в архиве обязаны лежать подряд — так их кладёт список в Makefile, + * и только поэтому индекс считается сложением. Если порядок собьётся, + * сборка встанет здесь, а не рассыплется картинкой на экране. */ +#if (ARC_KID_KID27 - ARC_KID_KID0) != 27 +#error "kid0..kid27 в архиве не подряд" +#endif +#if (ARC_KID_KID27_V - ARC_KID_KID0_V) != 27 +#error "kid0_v..kid27_v в архиве не подряд" +#endif + +#define KID_MAXPAGES 28 + +/* Таблица архива живёт на стеке вызывающего (4 байта на запись, здесь 232) + * — держать её постоянно незачем, а W2 у игры и без того тесный. */ +int pop_kid_load(uint8_t npages) __banked +{ + uint8_t tbl[ARC_KID_COUNT * POP_ARC_REC]; + pop_arc_t arc; + uint8_t i; + + if (npages > KID_MAXPAGES) npages = KID_MAXPAGES; + if (pop_arc_open_id(&arc, POP_ARC_KID, tbl, ARC_KID_COUNT) < 0) return -1; + for (i = 0; i < npages; i++) + if (pop_arc_atlas(&arc, tbl, (uint8_t)(ARC_KID_KID0 + i), + &kidp[i]) != 0) { + pop_arc_close(&arc); + return -1; + } + /* Меч — из того же открытого архива: без него кадры 229+ идут без + * клинка, а второй open стоил бы дороже самого чтения. */ + { + atlas_t sword; + + if (pop_arc_atlas(&arc, tbl, ARC_KID_SWORD, &sword) == 0) + (void)pop_sword_take(&sword); + } + pop_arc_close(&arc); + kid_npages = npages; + return 0; +} + +void pop_kid_free(void) __banked +{ + uint8_t i; + for (i = 0; i < kid_npages; i++) + atlas_free(&kidp[i]); + kid_npages = 0; +} + +int pop_kid_data_load(const char *path) __banked +{ + uint8_t blk, page; + int n; + blk=mem_alloc_pages(1); + if (!blk) return -1; + page=mem_get_page(blk,0); + n=bank_load_file(page,KD_DATA_OFF,path,16384-KD_DATA_OFF); + if (n < (int)(KID_BIN_SEQTBL_OFF + KID_SEQTBL_LEN)) { + mem_free_block(blk); return -1; + } + gfx_w0_page_prepare(page); + kdat_blk=blk; kdat_page=page; kdat_ok=1; + return 0; +} + +void pop_kid_data_free(void) __banked +{ + if (kdat_ok) { mem_free_block(kdat_blk); kdat_ok = 0; } +} + +/* ---- Зеркальный набор (зелье инверсии, уровень 9) -------------------- * + * Грузится ОДНИМ вызовом и откуда угодно: со старта игры, из показа интро + * или при входе на уровень с зельем — см. pop_vflip.h. Внутри обычный + * atlas_load (он резидентный и сам мапит W3, банку это разрешено делать + * только через него). Идемпотентна. */ +int pop_vflip_load_all(void) __banked +{ + uint8_t tbl[ARC_KID_COUNT * POP_ARC_REC]; + pop_arc_t arc; + uint8_t i; + int res = 0; + + if (pop_vflip_ready()) return 0; + if (pop_arc_open_id(&arc, POP_ARC_KID, tbl, ARC_KID_COUNT) < 0) return -1; + for (i = 0; i < kid_npages; i++) { + atlas_t m; + + if (pop_arc_atlas(&arc, tbl, (uint8_t)(ARC_KID_KID0_V + i), &m) != 0 || + pop_vflip_take(&kidp[i], &m) != 0) + res = -1; + } + { + atlas_t sword_v; + + if (pop_arc_atlas(&arc, tbl, ARC_KID_SWORD_V, &sword_v) != 0 || + pop_sword_vflip_take(&sword_v) != 0) + res = -1; + } + pop_arc_close(&arc); + if (pop_guard_vflip_load() != 0) res = -1; + return res; +} diff --git a/applications/SprPoP/src/pop_kdraw.c b/applications/SprPoP/src/pop_kdraw.c new file mode 100644 index 0000000..dcc6fdc --- /dev/null +++ b/applications/SprPoP/src/pop_kdraw.c @@ -0,0 +1,147 @@ +/* + * pop_kdraw.c — отрисовка ИЗ АТЛАСОВ КИДА, которая нужна только слою + * персонажей: клинок в руке (add_sword_to_objtable, seg006:1798) и блит + * спрайта chtab_2 по его id (полоса HP). БАНК 4, вместе с pop_cdraw.c. + * + * Почему здесь. Оба вызова приходят РОВНО из pop_cdraw.c и больше ниоткуда + * (проверено грепом), а лежали они в pop_kid.c — то есть в резиденте W1, + * съедая ~1.2 КБ дефицитного окна ради кода, который зовёт сосед по банку. + * Раз модуль в том же банке 4 — вызовы прямые, без трамплина: объявления в + * _pop_kdraw.h НЕ помечены __banked (см. там же условие корректности). + * + * Загрузка атласа меча (pop_sword_load) осталась публичной и __banked: её + * зовёт инициализация, один раз за игру. + */ +#include +#include +#include +#include "pop_kid.h" +#include "_pop_kdraw.h" +#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */ +#include "pop_guard.h" /* CHARID_* — клинок есть и у стража */ +#include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */ +#include "_pop_draw.h" /* pop_onscreen_cols */ +#include "pop_map.h" /* pop_upside — зелье инверсии */ +#include "pop_vflip.h" /* зеркальные страницы атласов */ +#include "kid_data.h" + +/* chtab_0 «меч в руке» (add_sword_to_objtable, seg006:1798): клинок — + * ОТДЕЛЬНЫЙ спрайт поверх персонажа, один и тот же атлас у Кида и у стража. + * Таблица sword_tbl (kid_data.h, генерится из seg006) индексируется + * sword_frame = кадр.sword & 0x3F; id 255 = клинка в этом кадре нет. + * Смещения — в ЭКРАННЫХ пикселях: оригинал применяет их ПОСЛЕ + * calc_screen_x_coord, x — «вперёд» по направлению персонажа. */ +static const uint8_t SWORD_ID[KID_NSWORD] = KID_SWORD_ID_INIT; +static const int8_t SWORD_DX[KID_NSWORD] = KID_SWORD_DX_INIT; +static const int8_t SWORD_DY[KID_NSWORD] = KID_SWORD_DY_INIT; +static atlas_t swordp; +static uint8_t sword_ok; + +/* Меч приходит УЖЕ ЗАГРУЖЕННЫМ: его читает pop_kid_load из того же архива, + * что и остальные страницы Кида, — открывать архив второй раз ради одного + * элемента незачем (`open` дороже самого чтения). */ +int pop_sword_take(const atlas_t *a) __banked +{ + if (!a) return -1; + swordp = *a; + sword_ok = 1; + return 0; +} + +/* Зеркальная страница меча (зелье инверсии) — пара к pop_sword_take. */ +int pop_sword_vflip_take(const atlas_t *m) __banked +{ + if (!sword_ok) return 0; + return pop_vflip_take(&swordp, m); +} + +/* add_sword_to_objtable (seg006:1798). Клинок рисуется, если персонаж в + * кадрах подъёма/ножен, ИЛИ держит меч, ИЛИ это ЖИВОЙ страж (у стража меч + * в руке всегда, даже пока sword ещё sheathed). Страницу W0 оставляем + * замапленной — вызывающий делает gfx_w0_unmap сам. */ +uint8_t pop_sword_draw(const pop_char_t *ch, const kframe *fr, + int obj_x, int obj_y, + int *rx, int *ry, uint16_t *rw, uint16_t *rh) +{ + uint8_t sf, id, flip; + const uint8_t *simg; + uint16_t w, h; + int bx, top; + + if (!sword_ok) return 0; + if (!((ch->frame >= 229 && ch->frame < 238) || + ch->sword != 0 || + ((ch->charid == CHARID_2_GUARD || ch->charid == CHARID_4_SKELETON) && + ch->alive < 0))) + return 0; + sf = (uint8_t)(fr->sword & 0x3F); + if (sf == 0 || sf >= KID_NSWORD) return 0; + id = SWORD_ID[sf]; + if (id == 0xFF) return 0; + + simg = (const uint8_t *)atlas_image(&swordp, id); + { /* ПЕРЕВОРОТ: зеркальная копия страницы меча (см. pop_vflip.h) */ + uint8_t vpg = swordp.page; + if (pop_upside) { + uint8_t f = pop_vflip_page(vpg); + if (f) vpg = f; + } + gfx_w0_map(vpg); + } + w = (uint16_t)(simg[0] | ((uint16_t)simg[1] << 8)); + h = (uint16_t)(simg[2] | ((uint16_t)simg[3] << 8)); + if (!w || !h) return 0; + + flip = (uint8_t)(ch->direction >= 0); + bx = obj_x + ((ch->direction < 0) ? -(int)SWORD_DX[sf] : (int)SWORD_DX[sf]); + top = obj_y + (int)SWORD_DY[sf] - (int)h + 1; + /* Та же формула, что у самого персонажа (pop_cdraw.c): верх кадра + * [top, top+h) уезжает в 192 - top - h. */ + if (pop_upside) top = POP_PLAYFIELD_H - top - (int)h; + if (flip) bx -= (int)w; + /* Поле комнаты — 192 строки (reset_obj_clip, seg006:0507): клинок + * обрезается по нему так же, как сам персонаж. Без этого у падающего + * в пропасть скелета меч оставался виден НИЖЕ поля, на полосе HP. */ + { + int skip = 0, rows = (int)h; + if (top < 0) { skip = -top; rows -= skip; top = 0; } + if (top + rows > 192) rows = 192 - top; + if (rows <= 0) return 0; + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* Клинок почти всегда целиком на экране — noclip-путь (см. gfx.h: + * подготовка клипающего варианта стоит ~5.6 К тактов на вызов). */ + if (pop_onscreen_cols(bx, top + POP_YOFF, w, (uint16_t)rows)) + gfx_blit_cols_part_noclip(bx, top + POP_YOFF, simg, flip, + (uint8_t)skip, (uint8_t)rows); + else + gfx_blit_cols_part(bx, top + POP_YOFF, simg, flip, skip, rows); + gfx_set_bank(GFX_BANK_NORMAL); + *rx = bx; *ry = top; *rw = w; *rh = (uint16_t)rows; + } + return 1; +} + +/* Блит спрайта chtab_2 (атласы Kid) по ЕГО id, левым верхним углом в (x,y). + * Нужен полосе HP: она рисует id 216/217 из тех же атласов, что и кадры + * Кида, а держать вторую копию раскладки в pop_cdraw ни к чему. */ +void pop_kid_img_blit(uint8_t image, int x, int y) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + uint8_t pg = (uint8_t)(image >> 3); /* раскладка атласов: 8 спрайтов/стр. */ + uint8_t idx = (uint8_t)(image & 7); + const uint8_t *img; + uint16_t w, h; + if (pg >= kid_npages) return; + img = (const uint8_t *)atlas_image(&kidp[pg], idx); + gfx_w0_map(kidp[pg].page); + w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8)); + h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8)); + /* Атласы Кида хранятся COLUMN-MAJOR (transpose_cols в pop_pack_kid — + * ради бесплатного горизонтального флипа), поэтому блит обязан быть + * колоночным. Row-major gfx_blit_noclip выдал бы марку транспонированной. + * noclip БЕЗ проверки (CLIP-1): единственный вызывающий — полоса HP + * (pop_cdraw.c), у неё координаты фиксированные и заведомо на экране. */ + if (w && h) gfx_blit_cols_part_noclip(x, y, img, 0, 0, 0); + gfx_w0_unmap(); +} diff --git a/applications/SprPoP/src/pop_kid.c b/applications/SprPoP/src/pop_kid.c new file mode 100644 index 0000000..90d3c6a --- /dev/null +++ b/applications/SprPoP/src/pop_kid.c @@ -0,0 +1,470 @@ +/* + * pop_kid.c — движок Kid (Фаза K1): порт seg006.c play_seq/load_frame + + * seqtbl-интерпретатор. Данные — kid_data.h. + */ +#include +#include +#include +#include +#include +#include +#include +#include "pop_kid.h" +#include "pop_sfx.h" /* звуковые эффекты: опкод SOUND в play_seq */ +#include "pop_trob.h" /* pop_start_chompers — смена ряда будит чомперы */ +#include "pop_guard.h" /* Guard/pop_gframe/CHARID_* — интерпретатор общий */ +#include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */ +#include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */ +#include "pop_map.h" /* pop_clip_char_top (clip_char) */ +#include "pop_geom.h" +#include "pop_level.h" /* POP_LEVEL_LAST — level 15 исключён */ +#include +#include +#include +#include "kid_data.h" +#include "_pop_kid.h" /* дескриптор страницы данных (её грузит банк 8) */ + +pop_char_t Kid; + +/* Флаг сотрясения (seg006 knock): ставится seqtbl-командой KNOCK_UP/DOWN + * в play_seq, потребляется pop_check_knock (pop_map) — трясёт loose-полы. */ +int8_t knock; + +/* Порт глобала next_level: seqtbl-команда END_LEVEL инкрементит его в конце + * seq_70 (подъём по лестнице за дверью уровня), а главный цикл сравнивает с + * текущим уровнем и грузит следующий. Счётчик, а не флаг — ровно как в + * оригинале, чтобы читы «следующий/предыдущий уровень» (levels_plan.md §4) + * легли сюда же без переделки. */ +uint8_t pop_next_level; + + +/* ---- Атласы Kid (прямая адресация image>>3) ------------------------ * + * НЕ static: набор спрайтов слота POP_CH_KID общей отрисовки персонажей + * (pop_cdraw.c, банк 4). Отдан как есть, а не через аксессор: читается раз + * в кадр из другого банка, и вызов стоил бы дороже самого чтения. */ +#define KID_MAXPAGES 28 +atlas_t kidp[KID_MAXPAGES]; +uint8_t kid_npages; + +/* Таблица «seq_id -> адрес в seqtbl» (original_seqtbl_offsets). Определена + * ЗДЕСЬ, а не в kid_data.h: заголовок включают почти все модули PoP, и + * `static const` в нём размножал таблицу мёртвой копией по каждому TU — + * 230 Б × 9 модулей, из них 460 Б в дефицитном резиденте W1/W2 (замер + * 2026-08-08). Наружу таблица не нужна: смену последовательности делают + * kid_set_seq / pop_char_set_seq. */ +static const uint16_t kid_seq_off[KID_NSEQ] = KID_SEQ_OFF_INIT; + +/* ---- Данные анимации в EMM-странице (kid.ani) ----------------------- * + * Таблица кадров (241*5 Б) и seqtbl (2310 Б) занимали 3.5 КБ в _CODE окна + * W1/W2 — самый дефицитный ресурс. Теперь они лежат в отдельной EMM- + * странице, которая маппится в W0 РОВНО на время play_seq (раз за тик), а + * весь остальной код читает cur_frame — 5-байтовую копию текущего кадра. + * Это ровно устройство оригинала: load_frame (seg006) заполняет cur_frame, + * и коллизия/отрисовка работают с ним, а не с таблицей. + * Страница адресуется как у pop_level: данные с 0x100, ниже — ISR-стаб. */ +/* Адрес кадра i в замапленной странице. Считаем В uint16_t и кастуем ОДИН + * раз: запись `(const uint8_t *)CONST + (uint16_t)i * 5u` SDCC 4.5 собирает + * НЕВЕРНО — умножение делает в 16 битах (add hl,hl / add hl,bc), а потом + * берёт только МЛАДШИЙ байт результата и подставляет старший байт константы + * (`ld c,l` / `inc b`). Для кадров i >= 52 (i*5 >= 256) адрес уезжает на + * −256*k, и cur_frame наполняется чужой строкой таблицы: у бега/шага/подъёма + * пропадал бит FRAME_NEEDS_FLOOR, и Kid проваливался сквозь пол. + * Артефакт: build/obj/pop_kid.asm, функция load_frame. */ +static const uint8_t *kd_frame_ptr(uint8_t i) +{ + uint16_t off = (uint16_t)i; + off += (uint16_t)(off << 2); /* i*5 = i + i*4, без умножения */ + off += (uint16_t)(KD_DATA_OFF + KID_BIN_FRAMES_OFF); + return (const uint8_t *)off; +} + +/* То же для таблицы кадров СТРАЖА (frame_tbl_guard дописана в ту же + * страницу данных, см. pop_extract_kid_data.py). */ +static const uint8_t *kd_gframe_ptr(uint8_t i) +{ + uint16_t off = (uint16_t)i; + off += (uint16_t)(off << 2); + off += (uint16_t)(KD_DATA_OFF + KID_BIN_GFRAMES_OFF); + return (const uint8_t *)off; +} + +/* ...и для таблицы кадров КАТСЦЕН (frame_tbl_cuts): по ней ходят Princess и + * Vizier в сценах 8/9, финале и intro. Индекс — номер кадра как есть; какой + * набор спрайтов рисовать, говорит поле sword (>>6: 1 = PV1, 2 = PV2). */ +static const uint8_t *kd_cframe_ptr(uint8_t i) +{ + uint16_t off = (uint16_t)i; + off += (uint16_t)(off << 2); + off += (uint16_t)(KD_DATA_OFF + KID_BIN_CFRAMES_OFF); + return (const uint8_t *)off; +} +#define KD_SEQ(cs) (*((const uint8_t *)(KD_DATA_OFF + KID_BIN_SEQTBL_OFF) + \ + (uint16_t)((cs) - SEQTBL_BASE))) +/* Байт seqtbl по адресу оригинала (база SEQTBL_BASE); ТОЛЬКО под замапленной + * страницей данных — то есть внутри play_seq. */ +#define SEQ(cs) KD_SEQ(cs) + +/* Страница данных анимации: заполняет ЗАГРУЗЧИК (pop_kboot.c, банк 8), + * читает интерпретатор здесь. Не static — контракт в _pop_kid.h. */ +uint8_t kdat_blk, kdat_page, kdat_ok; + +/* Текущий кадр (порт глобала cur_frame, seg006): 5 байт вместо таблицы. + * Заполняется load_frame для АКТИВНОГО персонажа (Char), поэтому на + * выходе из play_seq его надо сохранить в кэш своего персонажа: страж + * тикает ПОСЛЕ Кида, и без разделения отрисовка Кида взяла бы кадр стража. */ +static kframe cur_frame; +/* Кадр Кида для отрисовки — пара к pop_gframe соперника; читает общая + * отрисовка (pop_cdraw.c), поэтому не static. */ +kframe kid_frame; + +/* Char (seg006): АКТИВНЫЙ персонаж. Интерпретатор последовательностей в + * оригинале работает не с Kid и не с Guard, а с этой копией, а вокруг + * стоят loadkid/savekid и loadshad/saveshad (seg006:809..825). Копия, а + * не указатель, — и так же, как в оригинале, и быстрее на Z80: обращения + * в горячем цикле идут абсолютной адресацией глобала, а 16-байтовое + * копирование платится один раз на переключение персонажа. */ +pop_char_t Char; + +/* Opp (seg006): СОПЕРНИК активного персонажа — вторая копия, которую ставят + * рядом с Char (loadkid_and_opp / loadshad_and_opp, seg006:809). Через неё + * ИИ и боёвка меряют дистанцию (char_opp_dist), не зная, кто именно перед + * ними: для стража Opp = Kid, для Кида Opp = Guard. */ +pop_char_t Opp; + +void pop_loadkid(void) { Char = Kid; } +void pop_savekid(void) { Kid = Char; } +/* Выгрузка после control() — теперь ПОЛНАЯ, как savekid оригинала. + * + * Была выборочной (только curr_seq и sword) ровно потому, что pop_map правил + * геометрию ПРЯМО в Kid, мимо окна Char: полное копирование затирало его + * правки — Kid терял выравнивание x и ряд, и спуск с уступа через вис не + * срабатывал (персонаж просто приседал). Приходилось ещё и сравнивать + * последовательность со снимком на входе в окно, чтобы понять, чья правка + * свежее. С переводом физики на Char (GUARD-PHYS) диспетчер и pop_map + * пишут в одно и то же место, и разбирать это больше не нужно. */ +void pop_savekid_state(void) { Kid = Char; } +void pop_loadshad(void) { Char = Guard; } +void pop_saveshad(void) { Guard = Char; } +void pop_loadshad_and_opp(void) { Char = Guard; Opp = Kid; } +void pop_saveshad_and_opp(void) { Guard = Char; Kid = Opp; } +void pop_loadkid_and_opp(void) { Char = Kid; Opp = Guard; } +void pop_savekid_and_opp(void) { Kid = Char; Guard = Opp; } + +/* char_opp_dist (seg006:2135): >0 — соперник ПЕРЕД Char, <0 — за спиной. + * +13 при встречных направлениях: у стоящих лицом друг к другу «ноль» + * приходится на ширину тайла между ними. Живёт здесь, а не в guards.c, + * потому что нужен по обе стороны банковой границы — и ИИ, и диспетчеру + * боёвки (pop_ctrl). */ +int16_t pop_char_opp_dist(void) +{ + int16_t distance; + if (Char.room != Opp.room) return 999; + distance = (int16_t)Opp.x - (int16_t)Char.x; + if (Char.direction < 0) distance = -distance; + if (distance >= 0 && Char.direction != Opp.direction) distance += 13; + return distance; +} + +/* Прочитать кадр из ЛЮБОЙ таблицы страницы данных (у Kid и у стража они + * разные: KID_BIN_FRAMES_OFF / KID_BIN_GFRAMES_OFF). Сама маппит страницу, + * поэтому годится для вызова извне (pop_guard) — но НЕ в горячем цикле. */ +void pop_kid_data_frame(uint16_t tbl_off, uint8_t idx, uint8_t *out5) +{ + const uint8_t *f; + uint16_t off = (uint16_t)idx; + if (!kdat_ok) { out5[0] = 255; return; } + off += (uint16_t)(off << 2); /* idx*5, без умножения */ + off += (uint16_t)(KD_DATA_OFF + tbl_off); + gfx_w0_map(kdat_page); + f = (const uint8_t *)off; + out5[0] = f[0]; out5[1] = f[1]; out5[2] = f[2]; + out5[3] = f[3]; out5[4] = f[4]; + gfx_w0_unmap(); +} + +/* load_frame (seg006:0293): скопировать кадр Kid.frame из таблицы в + * cur_frame. Звать ТОЛЬКО когда страница данных замаплена в W0. */ +/* Какая ТАБЛИЦА КАДРОВ у персонажа (seg006:0293). Вынесено из load_frame + * отдельной функцией, потому что тот же вопрос решает ОТРИСОВКА: набор + * атласов обязан совпадать с таблицей, иначе image из таблицы Кида ищется + * среди спрайтов стража. Для тени это не теория — она ходит кадрами Кида + * (и рисуется его спрайтами), а дерётся кадрами стража. */ +uint8_t pop_frame_tbl_is_guard(int8_t charid, uint8_t frame) +{ + return (uint8_t)(charid == CHARID_2_GUARD || charid == CHARID_4_SKELETON || + (charid == CHARID_1_SHADOW && frame >= 150 && frame < 190)); +} + +static void load_frame(void) +{ + /* Таблица кадров зависит от персонажа (seg006:0293): у Kid своя, у + * стража frame_tbl_guard с индексом frame + add_frame − 149, где + * add_frame = 70 для кадров 102..106 (seg006:535). + * + * СКЕЛЕТ идёт по той же ветке, что страж (seg006:529 — общий case): у + * него свой атлас, но НУМЕРАЦИЯ кадров та же. Пока здесь стоял только + * charid_2_guard, скелет попадал в таблицу Кида, получал несуществующий + * image (180 при 28 спрайтах в наборе) и не рисовался вовсе. + * ТЕНЬ (charid_1_shadow) — по таблице стража ТОЛЬКО в кадрах 150..189, + * в остальных она ходит кадрами Кида (seg006:532). */ + const uint8_t *f; + uint8_t use_guard_tbl = pop_frame_tbl_is_guard(Char.charid, Char.frame); + f = 0; + if (Char.charid == CHARID_5_PRINCESS || Char.charid == CHARID_6_VIZIER) { + /* seg006:538 — у персонажей КАТСЦЕН своя таблица. */ + if (Char.frame < KID_NCFRAMES) f = kd_cframe_ptr(Char.frame); + } else if (use_guard_tbl) { + int16_t idx = (int16_t)Char.frame; + if (idx >= 102 && idx < 107) idx += 70; + idx -= 149; + if (idx >= 0 && idx < KID_NGFRAMES) f = kd_gframe_ptr((uint8_t)idx); + } else { + f = kd_frame_ptr(Char.frame); + } + if (f) { + cur_frame.image = f[0]; + cur_frame.dx = (int8_t)f[1]; + cur_frame.dy = (int8_t)f[2]; + cur_frame.flags = f[3]; + cur_frame.sword = f[4]; + } else { + /* Кадра нет в таблице — оригинал кладёт blank_frame {255,0,0,0,0} + * (get_frame_internal, seg006:507) и этим ГАСИТ отрисовку. Выход + * ОДИН на обе ветки: «пусто» обязано доехать и до кэша владельца + * (ниже), иначе kid_frame/pop_gframe держат ПРОШЛУЮ картинку и + * вместо «не рисовать» рисуется она — тот же класс, что + * BUG-SHADOW-STALE-FRAME (разбор в pop_char_draw). */ + cur_frame.image = 255; + cur_frame.dx = cur_frame.dy = 0; + cur_frame.flags = cur_frame.sword = 0; + } + /* Кэш кадра для ОТРИСОВКИ раскладываем ЗДЕСЬ, по charid активного + * персонажа, а не в save*-функциях. Раньше кэш писали pop_savekid/ + * pop_saveshad, и любое окно Char БЕЗ play_seq (окна боёвки + * check_sword_hurting / check_sword_hurt) записывало Киду кадр, который + * на самом деле принадлежал СТРАЖУ: отрисовка брала его image из атласа + * Кида и рисовал произвольную позу. Теперь владелец один — тот, для + * кого кадр реально загружен. */ + if (Char.charid == CHARID_0_KID) kid_frame = cur_frame; + else pop_gframe = cur_frame; +} + +static uint8_t char_dx_forward(int8_t dx) +{ + if (Char.direction < 0) dx = (int8_t)-dx; + return (uint8_t)(Char.x + dx); +} + +/* Аксессоры к текущему кадру (для pop_map): читают cur_frame, НЕ таблицу — + * их дёргают несколько раз за кадр, маппинг страницы тут недопустим. */ +int8_t kid_cur_dx(void) { return cur_frame.dx; } +int8_t kid_cur_dy(void) { return cur_frame.dy; } +uint8_t kid_cur_flags(void) { return cur_frame.flags; } + +/* seqtbl_offset_char: сменить последовательность Kid (единая точка — + * pop_ctrl/pop_map зовут ЕЁ, а не дублируют kid_seq_off/kid_seqtbl). */ +void kid_set_seq(uint8_t seq_id) { Kid.curr_seq = kid_seq_off[seq_id]; } + +/* То же для АКТИВНОГО персонажа — внутри окна Char (порт + * seqtbl_offset_char, seg006). Нужен стражу: его последовательность + * ставится, когда Char уже загружен из Guard. */ +void pop_char_set_seq(uint8_t seq_id) { Char.curr_seq = kid_seq_off[seq_id]; } + +/* play_seq (seg006.c:570): крутит опкоды до КАДРА (байт < 0xF0). + * Страница данных маппится в W0 на ВЕСЬ разбор — один map/unmap за вызов + * (в тике, до отрисовки: конфликта с атласом в W0 нет). В конце — + * load_frame: пока страница ещё замаплена, копируем кадр в cur_frame. */ +/* Отложенный start_chompers: взводится байткодом SEQ_UP/SEQ_DOWN, а + * исполняется после выхода из цикла (окно W0 уже свободно). */ +static uint8_t chomp_pending; + +void play_seq(void) +{ + if (!kdat_ok) return; + gfx_w0_map(kdat_page); + for (;;) { + uint8_t cmd = SEQ(Char.curr_seq); + Char.curr_seq++; + switch (cmd) { + case 0xFB: /* DX */ + Char.x = char_dx_forward((int8_t)SEQ(Char.curr_seq)); + Char.curr_seq++; + break; + case 0xFA: /* DY */ + Char.y = (uint8_t)(Char.y + (int8_t)SEQ(Char.curr_seq)); + Char.curr_seq++; + break; + case 0xFE: /* FLIP */ + Char.direction = (int8_t)~Char.direction; + break; + case 0xF7: /* JMP_IF_FEATHER (seg006:586): под зельем медленного + * падения — перейти по адресу (в seqtbl это ветки + * stepfloat/bumpfloat: «парящие» кадры падения и удара, + * без урона), иначе пропустить адрес и идти дальше. + * Эффект достаётся только Киду — как и сама физика пера + * (fall_accel, pop_map.c). */ + if (!pop_feather || Char.charid != CHARID_0_KID) { + Char.curr_seq += 2; + break; + } + /* иначе — ПРОВАЛ в JMP ниже: адрес лежит ровно там же */ + /* fallthrough */ + case 0xFF: { /* JMP */ + uint16_t a = (uint16_t)(SEQ(Char.curr_seq) | + ((uint16_t)SEQ((uint16_t)(Char.curr_seq + 1)) << 8)); + Char.curr_seq = a; + break; + } + case 0xFD: /* UP */ + Char.curr_row--; + chomp_pending = 1; /* start_chompers — ПОСЛЕ цикла, см. ниже */ + break; + case 0xFC: /* DOWN */ + Char.curr_row++; + chomp_pending = 1; + break; + case 0xF9: /* ACTION */ + Char.action = SEQ(Char.curr_seq); + Char.curr_seq++; + break; + case 0xF8: /* SET_FALL */ + Char.fall_x = (int8_t)SEQ(Char.curr_seq); Char.curr_seq++; + Char.fall_y = (int8_t)SEQ(Char.curr_seq); Char.curr_seq++; + break; + case 0xF5: /* KNOCK_UP: сотрясение трясёт loose РЯДОМ ВЫШЕ (check_knock) */ + knock = 1; + break; + case 0xF4: /* KNOCK_DOWN: приземление трясёт loose В СВОЁМ РЯДУ */ + knock = -1; + break; + case 0xF2: /* SOUND (seg006:0627). Аргумент — НЕ номер звука игры, а + * одно из пяти событий; отображение взято оттуда же: + * 0 SILENT — не звучит, но стражи Кида ЗАМЕЧАЮТ + * (стоит, например, в seq доставания меча); + * 1 FOOTSTEP — звук 23, и шум; + * 2 BUMP — звук 8, и шум; + * 3 DRINK — звук 18, не шум; + * 4 LEVEL — МУЗЫКА конца уровня (32 на 4-м, иначе 41, + * на 13/15 молчок). + * Второй смысл опкода («Кид нашумел») терять нельзя даже + * без звука, см. BUG-GUARD-DEAF-1. */ + { + uint8_t ev = SEQ(Char.curr_seq); + Char.curr_seq++; + if (ev < 3) is_guard_notice = 1; + if (ev == 1) pop_sfx_play(23); + else if (ev == 2) pop_sfx_play(8); + else if (ev == 3) pop_sfx_play(18); + else if (ev == 4) { + /* seg006:646. На четвёртом уровне уход за дверь — это + * встреча с тенью, и трек там свой; на 13-м (Джафар) и + * 15-м оригинал молчит. */ + if (pop_current_level == 4) pop_sfx_play(32); + else if (pop_current_level != 13) pop_sfx_play(41); + } + } + break; + case 0xF1: /* END_LEVEL: seq_70 доиграл — уровень пройден. + * До 13-го это тот же `++next_level`, что в оригинале. + * Level 15/copy protection вырезан: 14-й позже передаст + * управление ENDING (FG11), но уже сейчас не может + * породить запрос res2015.bin. */ + if (pop_next_level < POP_LEVEL_LAST) pop_next_level++; + break; + case 0xF3: /* GET_ITEM: аргумент 1 — применить эффект предмета + * (порт play_seq -> proc_get_object, seg006) */ + if (SEQ(Char.curr_seq) == 1) + pop_proc_get_object(); + Char.curr_seq++; + break; + case 0xF6: /* DIE nop */ + break; + default: /* КАДР */ + Char.frame = cmd; + load_frame(); /* cur_frame := таблица[frame] (страница ещё в W0) */ + gfx_w0_unmap(); + /* start_chompers (seg006:0603/0607) — ТОЛЬКО здесь, после + * размаппинга. В оригинале он зовётся прямо из SEQ_UP/SEQ_DOWN, + * но у нас seqtbl читается ЧЕРЕЗ ОКНО W0, а start_chompers лезет + * за тайлами уровня и это окно перемапливает и закрывает — цикл + * дальше исполнял бы мусор вместо байткода. Наступили на это + * 2026-08-08: после спуска Кид проваливался сквозь пол. */ + if (chomp_pending) { + chomp_pending = 0; + pop_start_chompers(Char.room, Char.curr_row); + } + return; + } + } +} + +/* load_fram_det_col (seg006:0144) = load_frame() + determine_col(). + * + * Зачем это отдельной публичной точкой. cur_frame — ОДИН глобал на всех + * персонажей (так и в оригинале), и его владелец — тот, кто последним + * прошёл load_frame. В кадре последним тикает страж, поэтому к моменту + * управления Кидом в cur_frame лежит кадр СТРАЖА. А через cur_frame + * считается вся геометрия: dx_weight -> determine_col -> + * distance_to_edge_weight -> get_edge_distance, то есть буквально выбор + * ветки в check_jump_up. + * + * Оригинал от этого страхуется явно: play_kid_frame (seg000:1209) и + * play_guard_frame (seg000:1246) сразу после loadkid/loadshad зовут + * load_fram_det_col() — ДО control(). У нас этого не было, и Кид считал + * свой прыжок по кадру стража (BUG-CTRL-FRAME-1): при distance 2 вместо 10 + * check_jump_up уходил в «пустой прыжок вверх» вместо «шаг назад и + * зацеп». Результат менялся от кадра к кадру — вместе с анимацией стража. + * + * determine_col теперь общий: pop_map работает с Char, поэтому колонка + * пересчитывается ЛЮБОМУ активному персонажу — ровно как load_fram_det_col + * в оригинале. Раньше вторая половина связки работала только на ветке Кида + * (стражу колонку вёл pop_guard_enter/leave) — прямое следствие того, что + * физика знала один Kid. */ +void pop_load_frame(void) +{ + if (!kdat_ok) return; + gfx_w0_map(kdat_page); + load_frame(); + gfx_w0_unmap(); +} + +void pop_load_fram_det_col(void) +{ + pop_load_frame(); + pop_determine_col(); +} + +/* Прокрутить последовательность КОНКРЕТНОГО персонажа (порт связки + * loadkid/play_seq/savekid, seg006). Звать вместо голого play_seq: тот + * работает с Char и сам по себе не знает, чей это персонаж. */ +void pop_kid_play(void) { pop_loadkid(); play_seq(); pop_savekid(); } +void pop_guard_play(void) { pop_loadshad(); play_seq(); pop_saveshad(); } + +void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir) +{ + Kid.curr_col = col; + Kid.curr_row = row; + Kid.direction = dir; + Kid.x = (uint8_t)(pop_x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX); + Kid.y = (uint8_t)pop_y_land[row + 1]; + Kid.fall_x = Kid.fall_y = 0; + Kid.repeat = 0; + Kid.charid = 0; /* charid_0_kid: своя таблица кадров */ + Kid.alive = -1; /* в оригинале ЖИВОЙ = alive < 0 */ + Kid.curr_seq = kid_seq_off[seq_id]; + pop_kid_play(); /* до первого кадра */ +} + +void kid_tick(void) +{ + pop_kid_play(); +} + +/* Отрисовка кадра (load_frame_to_obj, heal, клип, брызги, клинок) живёт в + * pop_cdraw.c — ОДНА на всех Char, см. DRAW-CHAR. Отсюда она получает + * атласы Кида (kidp/kid_npages) и его кадр (kid_frame). */ + +/* Таблица кадров (241*5 Б) и seqtbl (2310 Б) занимали 3.5 КБ в _CODE окна + * W1/W2 — самый дефицитный ресурс. Теперь они лежат в отдельной EMM- + * странице, которая маппится в W0 РОВНО на время play_seq (раз за тик). + * Страница адресуется как у pop_level: данные с 0x100, ниже — ISR-стаб. */ diff --git a/applications/SprPoP/src/pop_kid.h b/applications/SprPoP/src/pop_kid.h new file mode 100644 index 0000000..0578d87 --- /dev/null +++ b/applications/SprPoP/src/pop_kid.h @@ -0,0 +1,133 @@ +/* + * pop_kid.h — движок анимации/движения Kid (Фаза K1): интерпретатор + * seqtbl (play_seq) + frame_table. Порт SDLPoP seg006.c. Данные — + * kid_data.h (pop_extract_kid_data.py). + * + * K1: только проигрывание последовательностей кадров с авторскими + * смещениями (движение = SEQ_DX/DY + frame.dx/dy). Управление (K2) и + * коллизия карты (K3) — отдельно. + */ +#ifndef POP_KID_H +#define POP_KID_H + +#include +#include "pop_char.h" +#include "kid_data.h" /* kframe — тип кадра для pop_sword_draw */ +#include /* atlas_t — набор спрайтов Кида (см. kidp ниже) */ + +/* Состояние персонажа — общий тип для Кида, стража и Char (pop_char.h). */ +extern pop_char_t Kid; + +/* Флаг сотрясения (seg006 knock): +1 = KNOCK_UP (ряд выше), -1 = KNOCK_DOWN + * (свой ряд). Ставит play_seq, читает/сбрасывает pop_check_knock. */ +extern int8_t knock; + +/* Порт next_level (seg000): END_LEVEL в play_seq инкрементит — «уровень + * пройден». Читает и обнуляет главный цикл. Пока уровень один, поэтому + * там рестарт того же уровня; загрузка следующего — levels_plan.md. */ +extern uint8_t pop_next_level; + +/* Загрузить npages атласов Kid (kid0..kid{npages-1}.atl). ДО initgraph. + * 0 — OK, -1 — ошибка. */ +int pop_kid_load(uint8_t npages) __banked; + +/* Данные анимации (таблица кадров + seqtbl) — отдельная EMM-страница + * (kid.ani): в _CODE окна W1/W2 они занимали 3.5 КБ. Грузить ДО + * первого kid_init/play_seq. 0 — OK, -1 — ошибка (без них Kid не оживёт). + * Страница маппится в W0 внутри play_seq, раз за тик. */ +int pop_kid_data_load(const char *path) __banked; + +/* Прочитать кадр из таблицы страницы данных: tbl_off — KID_BIN_FRAMES_OFF + * (Kid) или KID_BIN_GFRAMES_OFF (страж, своя таблица frame_tbl_guard). + * Маппит страницу сама; для горячего цикла есть cur_frame. */ +void pop_kid_data_frame(uint16_t tbl_off, uint8_t idx, uint8_t *out5); +void pop_kid_data_free(void) __banked; + +/* Атлас chtab_0 «меч в руке» (sword.atl) — спрайт клинка поверх Kid в кадрах + * подъёма меча/убирания в ножны (add_sword_to_objtable, seg006:1798). + * Грузить ДО initgraph, как и страницы Kid. 0 — OK, -1 — ошибка (тогда + * кадры рисуются без клинка). */ +/* Зеркальная (вертикально) страница меча — для зелья инверсии. */ +int pop_sword_take(const atlas_t *a) __banked; +int pop_sword_vflip_take(const atlas_t *m) __banked; +void pop_kid_free(void) __banked; + +/* Сама отрисовка клинка (pop_sword_draw) и блит спрайта chtab_2 по id + * (pop_kid_img_blit) переехали в pop_kdraw.c — БАНК 4, к единственному + * вызывающему (pop_cdraw.c). Их объявления — в _pop_kdraw.h. */ + +/* Инициализировать Kid: последовательность seq_id, клетка col/row, + * направление dir (-1/0). Ставит x/y по x_bump/y_land. */ +void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir); + +/* Один логический тик: play_seq() — продвинуть последовательность до + * следующего кадра (применить SEQ_DX/DY/ACTION/...). */ +void kid_tick(void); + +/* play_seq напрямую (для pop_map start_fall — переход в fall-seq в тот же + * physics-шаг, как оригинал). */ +void play_seq(void); /* работает с Char — см. pop_kid_play ниже */ + +/* Активный персонаж (порт Char, seg006) и его загрузка/выгрузка. + * Интерпретатор последовательностей общий для Kid и стража: у них общая + * seqtbl, но РАЗНЫЕ таблицы кадров (load_frame выбирает по Char.charid). */ +extern pop_char_t Char; +/* Соперник активного персонажа (порт Opp): ставится вместе с Char через + * pop_loadshad_and_opp. Читают ИИ стража и боёвка (char_opp_dist). */ +extern pop_char_t Opp; +void pop_loadkid(void); +void pop_savekid(void); +void pop_savekid_state(void); /* Kid = Char, кадр не трогаем */ +void pop_loadshad(void); +void pop_saveshad(void); +void pop_loadshad_and_opp(void); /* Char = Guard, Opp = Kid (seg006:841) */ +void pop_saveshad_and_opp(void); /* Guard = Char, Kid = Opp */ +void pop_loadkid_and_opp(void); /* Char = Kid, Opp = Guard */ +void pop_savekid_and_opp(void); /* Kid = Char, Guard = Opp */ + +/* Дистанция до соперника в системе Char: >0 впереди, <0 за спиной, + * 999 — разные комнаты (порт char_opp_dist, seg006:2135). */ +int16_t pop_char_opp_dist(void); + +/* Прокрутить последовательность персонажа целиком (load + play_seq + + * save). Звать вместо голого play_seq. */ +void pop_char_set_seq(uint8_t seq_id); /* seq активного персонажа */ +void pop_kid_play(void); +void pop_guard_play(void); + +/* load_fram_det_col (seg006:0144) — перезагрузить cur_frame под АКТИВНОГО + * персонажа (Char) и пересчитать его колонку. Звать сразу после pop_loadkid/ + * pop_loadshad и ДО диспетчера control(), как это делают play_kid_frame / + * play_guard_frame (seg000:1209/1246): cur_frame один на всех, и без этого + * геометрия одного персонажа считается по кадру другого. */ +void pop_load_fram_det_col(void); +/* Только load_frame (seg006:0293), БЕЗ determine_col. Нужен там, где + * оригинал зовёт голый load_frame и пересчёт колонки по x был бы вреден — + * например в jump_through_mirror, где ось отражения берётся из curr_col. */ +void pop_load_frame(void); + +/* Какая таблица кадров у персонажа (seg006:0293): 1 — стража, 0 — Кида. + * Нужна не только load_frame, но и отрисовке — набор атласов обязан + * совпадать с таблицей (тень ходит кадрами и спрайтами КИДА, а дерётся + * кадрами стража). */ +uint8_t pop_frame_tbl_is_guard(int8_t charid, uint8_t frame); + +/* Поля текущего кадра kid_frames[Kid.frame] (pop_map, без дублирования + * таблицы во втором TU). */ +int8_t kid_cur_dx(void); +int8_t kid_cur_dy(void); +uint8_t kid_cur_flags(void); + +/* Сменить последовательность (Kid.curr_seq = kid_seq_off[seq_id]) — единая + * точка для pop_ctrl/pop_map (иначе каждый дублировал бы kid_seqtbl ~2.3КБ). */ +void kid_set_seq(uint8_t seq_id); + +/* Атласы Кида (chtab_2) и его текущий кадр — набор слота POP_CH_KID общей + * отрисовки персонажей (pop_cdraw.c). Наружу отданы как данные, а не через + * аксессоры: читаются раз в кадр из другого банка, где вызов стоил бы + * дороже самого чтения (данные в _DATA видны всем без трамплина). */ +extern atlas_t kidp[]; +extern uint8_t kid_npages; +extern kframe kid_frame; + +#endif diff --git a/applications/SprPoP/src/pop_level.c b/applications/SprPoP/src/pop_level.c new file mode 100644 index 0000000..4edd4f4 --- /dev/null +++ b/applications/SprPoP/src/pop_level.c @@ -0,0 +1,136 @@ +/* + * pop_level.c — ГОРЯЧАЯ половина уровня: доступ к уже загруженным данным. + * См. pop_level.h (публичный API) и _pop_level.h (деление половин). + * + * Здесь живёт то, что зовут на каждый тайл и из любого банка: чтение + * foretable/backtable, дверные связи, живое состояние стражей. Загрузка + * файла, разбор комнаты и рестарт уровня — в pop_level_cold.c (банк 8): + * они отрабатывают раз на уровень/комнату, и в дефицитном W1 им не место. + * + * Общее состояние (страница, дверные таблицы, стражи) определено ЗДЕСЬ и + * объявлено в _pop_level.h — данные банковых модулей всё равно линкуются в + * общий _DATA, так что холодная половина видит их напрямую. + */ +#include +#include +#include +#include +#include /* gfx_w0_map / gfx_w0_unmap */ + +#include "pop_level.h" +#include "_pop_level.h" + +uint8_t pop_lvl_blk, pop_lvl_page, pop_lvl_ok; +/* Копии таблиц дверных связей в W2 (LINKMAP мутабельна — таймеры кнопок). */ +uint8_t pop_dl1[256], pop_dl2[256]; +/* Живое состояние стражей: в оригинале массивы стражей — часть структуры + * уровня в ОЗУ, и движок их ПЕРЕПИСЫВАЕТ (leave_guard кладёт туда позицию/ + * направление/мастерство, а у мёртвого ещё и curr_seq), благодаря чему, + * вернувшись, находишь труп там же. У нас уровень лежит в EMM-странице + * только на чтение, поэтому держим мутабельную копию в W2: 6 байт/комнату. */ +uint8_t pop_gstate[24 * 6]; + +uint8_t pop_current_level; + +/* set_tile-запись в ЖИВУЮ foretable уровня (порт curr_room_tiles[…] = …). + * В оригинале уровень — обычная структура в ОЗУ, и do_pickup (seg006:1671), + * remove_loose (seg007:0EB8), loose_land (seg007:11E8) правят её насовсем. + * У нас уровень в EMM-странице — пишем прямо туда, поэтому изменение + * переживает выход из комнаты (BUG-LVLSTATE-1). Пишем БАЙТ ЦЕЛИКОМ: + * старшие биты старого тайла (напр. 0x20 «solid loose») к новому типу + * отношения не имеют. */ +void pop_level_set_tile(uint8_t room, uint8_t tilepos, uint8_t tile) +{ + if (!pop_lvl_ok || room < 1 || room > 24 || tilepos >= ROOM_TILES) return; + gfx_w0_map(pop_lvl_page); + ((uint8_t *)(LVL_DATA_OFF + BP_FG))[(uint16_t)(room - 1) * ROOM_TILES + tilepos] + = tile; + gfx_w0_unmap(); +} + +uint8_t pop_doorlink1(uint8_t i) { return pop_dl1[i]; } +uint8_t pop_doorlink2(uint8_t i) { return pop_dl2[i]; } +void pop_doorlink2_set(uint8_t i, uint8_t v) { pop_dl2[i] = v; } + +uint8_t pop_level_tile(uint8_t room, uint8_t tilepos) +{ + uint8_t v; + if (!pop_lvl_ok || room < 1 || room > 24 || tilepos >= ROOM_TILES) return 0; + gfx_w0_map(pop_lvl_page); + v = (uint8_t)(room_fg_ptr(room)[tilepos] & 0x1F); + gfx_w0_unmap(); + return v; +} + +/* Батч-доступ: смаппить страницу уровня в W0 ОДИН раз, прочитать много + * тайлов через pop_level_tile_raw, размаппить один раз. Экономит N свапов + * окна 0 в горячих циклах (pop_process_trobs звал pop_level_tile на каждый + * trob каждый кадр). Между begin/end НЕЛЬЗЯ рисовать/звать код, которому + * нужно окно 0 в исходном состоянии — только сырые чтения уровня. */ +void pop_level_access_begin(void) { if (pop_lvl_ok) gfx_w0_map(pop_lvl_page); } +void pop_level_access_end(void) { if (pop_lvl_ok) gfx_w0_unmap(); } + +uint8_t pop_level_tile_raw(uint8_t room, uint8_t tilepos) +{ + if (!pop_lvl_ok || room < 1 || room > 24 || tilepos >= ROOM_TILES) return 0; + return (uint8_t)(room_fg_ptr(room)[tilepos] & 0x1F); +} + +void pop_level_room_bg(uint8_t room, uint8_t *out30) +{ + const uint8_t *rbg; + uint8_t i; + if (!out30) return; + if (!pop_lvl_ok || room < 1 || room > 24) { + for (i = 0; i < ROOM_TILES; i++) out30[i] = 0; + return; + } + gfx_w0_map(pop_lvl_page); + rbg = room_bg_ptr(room); + for (i = 0; i < ROOM_TILES; i++) out30[i] = rbg[i]; + gfx_w0_unmap(); +} + +uint8_t pop_room_link(uint8_t room, uint8_t side) +{ + uint8_t v; + if (!pop_lvl_ok || room < 1 || room > 24 || side > 3) return 0; + gfx_w0_map(pop_lvl_page); + v = ((const uint8_t *)(LVL_DATA_OFF + BP_LINKS))[(uint16_t)(room - 1) * 4 + side]; + gfx_w0_unmap(); + return v; +} + +/* leave_guard (seg002:02F5): запомнить состояние стража комнаты. seq + * сохраняем ТОЛЬКО у мёртвого — у живого seq_hi = 0 означает «поднять + * стандартной стойкой», как в оригинале. */ +void pop_guard_state_save(uint8_t room, uint8_t tile, int8_t dir, uint8_t x, + uint8_t skill, uint16_t seq, uint8_t dead) +{ + uint8_t *g; + if (room < 1 || room > 24) return; + g = pop_gstate + (uint16_t)(room - 1) * 6; + g[GS_TILE] = tile; + g[GS_DIR] = (uint8_t)dir; + g[GS_X] = x; + g[GS_SKILL] = skill; + if (dead) { g[GS_SEQL] = (uint8_t)seq; g[GS_SEQH] = (uint8_t)(seq >> 8); } + else { g[GS_SEQH] = 0; } +} + +uint16_t pop_guard_state_seq(uint8_t room) +{ + const uint8_t *g; + if (room < 1 || room > 24) return 0; + g = pop_gstate + (uint16_t)(room - 1) * 6; + if (g[GS_SEQH] == 0) return 0; /* живой — стандартная стойка */ + return (uint16_t)(g[GS_SEQL] | ((uint16_t)g[GS_SEQH] << 8)); +} + +/* Запомненная X стража комнаты (для трупа: он лежит там, где лёг, а не в + * центре тайла). */ +uint8_t pop_guard_state_x(uint8_t room) +{ + if (room < 1 || room > 24) return 0; + return pop_gstate[(uint16_t)(room - 1) * 6 + GS_X]; +} diff --git a/applications/SprPoP/src/pop_level.h b/applications/SprPoP/src/pop_level.h new file mode 100644 index 0000000..5c359c9 --- /dev/null +++ b/applications/SprPoP/src/pop_level.h @@ -0,0 +1,160 @@ +/* + * pop_level.h — данные уровня PoP из сырого файла res200N.bin (Фаза L1). + * + * Уровень загружается ОДИН раз в отдельную EMM-страницу (как атласы: + * данные с offset 0x100, ISR-стаб в первых байтах — страница безопасна + * для маппинга в W0). Файл — сырой blueprnt DAT 1.0 (2305 байт, БЕЗ + * checksum-байта; Table 6 спецификации POP-DAT): + * foretable(fg) @0 backtable(bg) @720 door I/II @1440/1696 + * links @1952 (24×{L,R,U,D}) start @2112 ... + * Тайл-код = байт fg & 0x1F (верхние биты — модификатор, пока не нужны); + * bg передаётся как есть. См. applications/PoP/docs/POP-DAT-* . + * + * Рабочая копия ТЕКУЩЕЙ комнаты живёт в обычной памяти (W2) у приложения + * (мутабельная — loose→empty); pop_room_load извлекает её из страницы + + * тонкие срезы соседей для кромок (правый столбец left-комнаты, верхний + * ряд down-комнаты). Страница маппится в W0 только на время извлечения. + */ +#ifndef POP_LEVEL_H +#define POP_LEVEL_H + +#include "pop_qsave.h" + +#include + +/* Загрузить уровень из файла в EMM-страницу. 0 — OK, -1 — ошибка + * (open/alloc/размер). Звать ПОСЛЕ первого atlas_load (pop_bg_load) — + * он снимает страницу ядра DSS для W0-unmap. */ +/* pop_level_load — внутренняя для pop_level_cold.c (грузим по НОМЕРУ) */ +void pop_level_free(void) __banked; + +/* ---- Номер уровня и потабличные различия (levels_plan.md §1) -------- */ + +/* Единый допустимый диапазон ресурсов. 0 зарезервирован под demo (FG7), + * 1..14 — настоящая игра; level 15/copy protection намеренно отсутствует. + * Эти границы используют загрузчик, QuickLoad и отладочная навигация, чтобы + * ни один путь не мог запросить несуществующий res2015.bin. */ +#define POP_LEVEL_DEMO 0 +#define POP_LEVEL_FIRST 1 +#define POP_LEVEL_LAST 14 + +/* Порт current_level (seg000 load_lev_spr: current_level = next_level = + * level). Читают потабличные функции ниже, стражи (HP/тип) и стартовая + * логика. */ +extern uint8_t pop_current_level; +void pop_level_qsave(pop_qs_io_t *io); + +/* Загрузить уровень НОМЕРОМ: освободить страницу прошлого, взять + * `LEVELS\res20NN.bin` (fallback `a:\res20NN.bin`) и выставить + * pop_current_level. 0 — OK, -1 — файла нет / не влез (тогда + * pop_current_level и страница НЕ тронуты — играем дальше на старом). + * Порт связки load_lev_spr + load_level (seg000:1098/1169). */ +int pop_level_load_num(uint8_t n) __banked; + +/* tbl_entry_pose (data.h:848): 1 — падение внутрь (ур. 1), 2 — вбегание + * (ур. 13), 0 — разворот на месте. Индекс — номер уровня. */ +uint8_t pop_level_entry_pose(uint8_t n) __banked; + +/* tbl_guard_hp (data.h:846) — база HP стража на уровне (get_guard_hp, + * seg002:0044: extrastrength[skill] + tbl_guard_hp[level]). */ +uint8_t pop_level_guard_hp(uint8_t n) __banked; + +/* tbl_guard_type (data.h:844): 0 обычный, 1 толстый, 2 скелет, 3 визирь, + * 4 тень, −1 — стражей на уровне НЕТ (уровень 14). */ +int8_t pop_level_guard_type(uint8_t n) __banked; + +/* tbl_level_type (data.h:840): 0 = dungeon, 1 = palace. Пока читается + * только для контроля — второй тайлсет это levels_plan.md §3. */ +uint8_t pop_level_tileset(uint8_t n) __banked; + +/* Извлечь комнату room (1..24) в массивы приложения (W2): + * fg[30], bg[30] — тайлы комнаты (fg маскирован &0x1F, bg raw); + * lcol_fg[6], lcol_bg[3] — [0..2] правый столбец (col9) комнаты СЛЕВА + * (кромка col0), [3..5] предпоследний (col8) — + * для коллизии на колонке −2 (Kid стоит В шве); + * rcol_fg[6] симметрично (col0 / col1); + * нет комнаты → стена/0; + * below_fg[11] — [0..9] верхний ряд (row0) комнаты СНИЗУ, + * [10] тайл (0,9) комнаты СНИЗУ-СЛЕВА («стена + * вниз»); нет комнаты → стена. + * 0 — OK, -1 — уровень не загружен / room вне диапазона. */ +int pop_room_load(uint8_t room, uint8_t *fg, uint8_t *bg, + uint8_t *lcol_fg, uint8_t *lcol_bg, + uint8_t *rcol_fg, uint8_t *rcol_bg, uint8_t *below_fg) __banked; + +/* Связь комнаты по стороне (0=L,1=R,2=U,3=D) → номер соседней (1..24) или + * 0, если стороны нет. Для переходов между комнатами (L2/L3). */ +uint8_t pop_room_link(uint8_t room, uint8_t side); + +/* Тайл-код (fg & 0x1F) по комнате (1..24) и позиции tilepos (0..29, + * row*10+col); вне диапазона → 0 (empty). Для trob-диспетчера (pop_trob): + * тип анимируемого тайла в ЛЮБОЙ комнате, не только текущей. */ +uint8_t pop_level_tile(uint8_t room, uint8_t tilepos); + +/* Записать тайл в ЖИВУЮ foretable уровня (порт curr_room_tiles[tp] = tile: + * do_pickup / remove_loose / loose_land). Изменение персистентно — уровень + * лежит в ОЗУ-странице, а не в ПЗУ, — то есть переживает выход из комнаты, + * как в оригинале. Отменяется только pop_level_reset_tiles. */ +void pop_level_set_tile(uint8_t room, uint8_t tilepos, uint8_t tile); + +/* Рестарт уровня: вернуть ВСЕ тайлы в исходное (порт load_level, который + * play_level зовёт на каждой итерации — в т.ч. после смерти Кида). */ +void pop_level_reset_tiles(void) __banked; + +/* Рестарт уровня: вернуть ВСЕХ стражей (тот же load_level + pos_guards). + * Убитый страж после respawn снова жив — как в оригинале. */ +void pop_level_reset_guards(void) __banked; + +/* Батч-доступ к странице уровня (один map/unmap окна 0 на много чтений). + * Между begin/end — только pop_level_tile_raw, без рисования. */ +void pop_level_access_begin(void); +void pop_level_access_end(void); +uint8_t pop_level_tile_raw(uint8_t room, uint8_t tilepos); + +/* Скопировать backtable (модификаторы) комнаты room в out[30]. Исходное + * состояние per-room modifier (openness ворот, состояние пик и т.п.) для + * ленивой инициализации room_modif в pop_trob. Вне диапазона → нули. */ +void pop_level_room_bg(uint8_t room, uint8_t *out30); + +/* Таблицы связей дверей (LINKLOC@1440 / LINKMAP@1696, по 256 Б). Копируются + * в W2-RAM при загрузке (LINKMAP мутабельна — таймеры кнопок). i — индекс + * цепочки (модификатор кнопки). Декод — в pop_trob (get_doorlink_*). */ +uint8_t pop_doorlink1(uint8_t i); +uint8_t pop_doorlink2(uint8_t i); +void pop_doorlink2_set(uint8_t i, uint8_t v); + +/* Ряд приземления падающего loose-куска в колонке col комнаты room: первый + * НЕ-пустой тайл сверху вниз; возвращает его ряд, если это floor (→ debris), + * иначе -1 (пролетел/крушение о стену). */ +int8_t pop_room_col_landing(uint8_t room, uint8_t col) __banked; + +/* Стартовая позиция уровня (блок @2112): комната / поза / направление. */ +/* Страж комнаты: 1 = есть (заполнит tile/dir/color/skill), 0 = нет. + * Порт enter_guard (seg002:0112). */ +/* leave_guard (seg002:02F5): запомнить состояние стража комнаты в ЖИВОЙ + * копии массивов уровня. dead != 0 — сохранить и последовательность, тогда + * при возврате в комнату страж поднимется трупом там же, где лёг. + * tile == 30 (или больше) — «стража в комнате больше нет». */ +void pop_guard_state_save(uint8_t room, uint8_t tile, int8_t dir, uint8_t x, + uint8_t skill, uint16_t seq, uint8_t dead); + +/* Последовательность запомненного стража (0 — поднимать стандартной + * стойкой, как у живого). */ +uint16_t pop_guard_state_seq(uint8_t room); +uint8_t pop_guard_state_x(uint8_t room); + +uint8_t pop_level_guard(uint8_t room, uint8_t *tile, int8_t *dir, + uint8_t *color, uint8_t *skill) __banked; + +/* tbl_level_type (SDLPoP data.h:840) — какой тайлсет у уровня: + * 0 подземелье, 1 дворец. Дворцовые: 4, 5, 6, 10, 11, 14. Индекс — + * НОМЕР уровня; элемент 0 — демо-уровень. Оригинал по нему выбирает и + * .DAT окружения (tbl_envir_ki, seg000:1108), и десяток веток отрисовки + * в seg008, и графику стража (GUARD1/GUARD2). */ +uint8_t pop_level_type(void) __banked; + +uint8_t pop_level_start_room(void) __banked; +uint8_t pop_level_start_pos(void) __banked; +int8_t pop_level_start_dir(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_level_cold.c b/applications/SprPoP/src/pop_level_cold.c new file mode 100644 index 0000000..5ba3b67 --- /dev/null +++ b/applications/SprPoP/src/pop_level_cold.c @@ -0,0 +1,326 @@ +/* + * pop_level_cold.c — ХОЛОДНАЯ половина уровня (БАНК 8). Контракт и деление + * половин — в _pop_level.h. + * + * Здесь всё, что отрабатывает раз на уровень (чтение файла res200N.bin, + * рестарт тайлов/стражей, потабличные различия) или раз на комнату (разбор + * комнаты в рабочие массивы, посадочный ряд колонки, страж комнаты). Цена + * выноса — один трамплин на вызов; в кадре этих вызовов ноль. + * + * Формат — порт blueprnt DAT 1.0 (Table 6 POP-DAT). + */ +#include +#include +#include +#include +#include /* gfx_w0_map / gfx_w0_unmap */ +#include "pop_level.h" +#include "_pop_level.h" +#include "pop_geom.h" /* pop_x_bump / FIRST_ONSCREEN_COLUMN — pos_guards */ + +/* Низкоуровневое чтение через W3 выполняет резидентный bank_load_file(); + * остальная одноразовая подготовка уровня безопасно остаётся в банке 8. */ + +void pop_level_free(void) __banked +{ + if (pop_lvl_ok) { mem_free_block(pop_lvl_blk); pop_lvl_ok = 0; } +} + +/* ---- Потабличные различия уровней ------------------------------------ */ + +/* Таблицы из SDLPoP data.h:840..848, индекс = номер уровня (0 = demo). + * Хвоста level 15 здесь намеренно нет: настоящий маршрут заканчивается 14. + * Так диапазон данных и диапазон загрузчика не могут разъехаться. */ +static const uint8_t tbl_entry_pose[POP_LEVEL_LAST + 1] = + { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 }; +static const uint8_t tbl_guard_hp[POP_LEVEL_LAST + 1] = + { 4, 3, 3, 3, 3, 4, 5, 4, 4, 5, 5, 5, 4, 6, 0 }; +static const int8_t tbl_guard_type[POP_LEVEL_LAST + 1] = + { 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 4, 3, -1 }; +/* tbl_level_type (SDLPoP data.h:840): 0 — подземелье, 1 — дворец. По нему + * расходятся не только атласы, но и ветки отрисовки (seg008), кладка стены + * (wall_pattern) и модификатор пустой клетки от упавшей плиты + * (remove_loose, seg007:0EB8). */ +static const uint8_t tbl_level_type[POP_LEVEL_LAST + 1] = + { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1 }; + +uint8_t pop_level_entry_pose(uint8_t n) __banked { return n <= POP_LEVEL_LAST ? tbl_entry_pose[n] : 0; } +uint8_t pop_level_guard_hp(uint8_t n) __banked { return n <= POP_LEVEL_LAST ? tbl_guard_hp[n] : 3; } +int8_t pop_level_guard_type(uint8_t n) __banked { return n <= POP_LEVEL_LAST ? tbl_guard_type[n] : -1; } +uint8_t pop_level_tileset(uint8_t n) __banked { return n <= POP_LEVEL_LAST ? tbl_level_type[n] : 0; } +uint8_t pop_level_type(void) __banked { return pop_level_tileset(pop_current_level); } + +static int level_read_file(const char *path) +{ + uint8_t blk, page; + uint16_t i; + int n; + blk=mem_alloc_pages(1); + if (!blk) return -1; + page=mem_get_page(blk,0); + n=bank_load_file(page,LVL_DATA_OFF,path,16384-LVL_DATA_OFF); + if (n < (int)MIN_SIZE) { mem_free_block(blk); return -1; } + gfx_w0_page_prepare(page); + gfx_w0_map(page); + for (i=0; i<256; i++) { + pop_dl1[i]=((const uint8_t *)LVL_DATA_OFF)[BP_LINKLOC+i]; + pop_dl2[i]=((const uint8_t *)LVL_DATA_OFF)[BP_LINKMAP+i]; + } + for (i=0; i<24u*ROOM_TILES; i++) + ((uint8_t *)LVL_PRISTINE_OFF)[i]= + ((const uint8_t *)(LVL_DATA_OFF+BP_FG))[i]; + gfx_w0_unmap(); + pop_lvl_blk=blk; pop_lvl_page=page; pop_lvl_ok=1; + pop_gstate_init(); + return 0; +} + +int pop_level_load_num(uint8_t n) __banked +{ + /* Имена патчим на месте, а не через sprintf: printf-семейство тянет в + * резидент W1 форматтер, которого здесь больше негде применить. */ + static char pri[] = "LEVELS\\res2000.bin"; /* цифры — [12],[13] */ + static char alt[] = "a:\\res2000.bin"; /* цифры — [8],[9] */ + uint8_t saved_blk = pop_lvl_blk, saved_page = pop_lvl_page, saved_ok = pop_lvl_ok; + + if (n > POP_LEVEL_LAST) return -1; + pri[12] = alt[8] = (char)('0' + n / 10); + pri[13] = alt[9] = (char)('0' + n % 10); + + /* Старую страницу отпускаем ТОЛЬКО после успешной загрузки новой: + * level_read_file сам аллоцирует страницу, и если файла нет — остаёмся + * на текущем уровне вместо падения в пустой уровень. */ + pop_lvl_ok = 0; /* чтобы level_load_path не мешался */ + if (level_read_file(pri) != 0 && level_read_file(alt) != 0) { + pop_lvl_blk = saved_blk; pop_lvl_page = saved_page; pop_lvl_ok = saved_ok; + return -1; + } + if (saved_ok) mem_free_block(saved_blk); + pop_current_level = n; + return 0; +} + +int pop_room_load(uint8_t room, uint8_t *fg, uint8_t *bg, + uint8_t *lcol_fg, uint8_t *lcol_bg, + uint8_t *rcol_fg, uint8_t *rcol_bg, uint8_t *below_fg) __banked +{ + const uint8_t *rfg, *rbg, *lnk; + uint8_t left, right, down, i; + + if (!pop_lvl_ok || room < 1 || room > 24) return -1; + + gfx_w0_map(pop_lvl_page); + rfg = room_fg_ptr(room); + rbg = room_bg_ptr(room); + lnk = (const uint8_t *)(LVL_DATA_OFF + BP_LINKS) + (uint16_t)(room - 1) * 4; + + for (i = 0; i < ROOM_TILES; i++) { + fg[i] = (uint8_t)(rfg[i] & 0x1F); /* тип тайла (верх. биты — модиф., пока не нужны) */ + bg[i] = rbg[i]; /* backtable как есть */ + } + + left = lnk[0]; /* L */ + right = lnk[1]; /* R */ + down = lnk[3]; /* D */ + + /* leftcol: правый столбец (col9) комнаты СЛЕВА, строки 0..2 (кромка col0), + * а в [3..5] — ПРЕДпоследний (col8). Вторая колонка нужна коллизии: Kid, + * стоящий В шве (curr_col = −1), смотрит вперёд на колонку −2, и без неё + * там оказывалась мнимая стена (get_tile → TILE_WALL), которая выпихивала + * его сквозь решётку. Оригинал резолвит любую колонку через + * find_room_of_tile (seg006:005D). */ + if (left >= 1 && left <= 24) { + const uint8_t *lf = room_fg_ptr(left); + const uint8_t *lb = room_bg_ptr(left); + for (i = 0; i < 3; i++) { + lcol_fg[i] = (uint8_t)(lf[i * 10 + 9] & 0x1F); + lcol_fg[3 + i] = (uint8_t)(lf[i * 10 + 8] & 0x1F); + lcol_bg[i] = lb[i * 10 + 9]; + } + } else { + for (i = 0; i < 3; i++) { lcol_fg[i] = 20; lcol_fg[3 + i] = 20; lcol_bg[i] = 0; } + } + + /* rightcol: левый столбец (col0) комнаты СПРАВА, строки 0..2 (кромка col9: + * коллизия на правом шве + переход вправо) */ + if (right >= 1 && right <= 24) { + const uint8_t *rf = room_fg_ptr(right); + const uint8_t *rb = room_bg_ptr(right); + for (i = 0; i < 3; i++) { + rcol_fg[i] = (uint8_t)(rf[i * 10 + 0] & 0x1F); + rcol_fg[3 + i] = (uint8_t)(rf[i * 10 + 1] & 0x1F); /* колонка 11 (см. выше) */ + rcol_bg[i] = rb[i * 10 + 0]; + } + } else { + for (i = 0; i < 3; i++) { rcol_fg[i] = 20; rcol_fg[3 + i] = 20; rcol_bg[i] = 0; } + } + + /* belowrow: верхний ряд (row0) комнаты снизу — ОДИННАДЦАТЬ байт. + * [0..9] — колонки 0..9 комнаты снизу; [10] — тайл (0,9) комнаты + * ПО ДИАГОНАЛИ СНИЗУ-СЛЕВА (room_BL у оригинала). Одиннадцатый нужен + * тайлу (2,0): его «сосед снизу-слева» — это колонка −1 ряда 3, то есть + * чужая комната, и load_rowbelow (seg008:368) резолвит её именно так, + * с дефолтом «стена» только когда такой комнаты НЕТ. Раньше мы клали + * туда стену БЕЗУСЛОВНО, и в пустом углу рисовался клин кладки + * (BUG-SEAM-WEDGE-1, ур. 3 комн. 18). */ + if (down >= 1 && down <= 24) { + const uint8_t *df = room_fg_ptr(down); + uint8_t dl = ((const uint8_t *)(LVL_DATA_OFF + BP_LINKS))[(down - 1) * 4]; + for (i = 0; i < 10; i++) below_fg[i] = (uint8_t)(df[i] & 0x1F); + below_fg[10] = (dl >= 1 && dl <= 24) + ? (uint8_t)(room_fg_ptr(dl)[9] & 0x1F) + : 20; /* кромка уровня */ + } else { + /* Комнаты снизу НЕТ. Оригинал (load_rowbelow, seg008:368) в этом + * случае подставляет РАЗНЫЕ кромки: колонкам 1..9 — `tiles_0_empty`, + * и только левому краю (колонка 0, тайл из room_BL) — `tiles_20_wall`. + * У нас тут стояла стена на всё, и её грань (topright стены) лезла + * жёлтыми треугольниками под пустые тайлы нижнего ряда — видно в + * шахте падения, уровень 6 комната 3 (BUG-BELOWROW-WALL). */ + for (i = 0; i < 10; i++) below_fg[i] = 0; /* tiles_0_empty */ + below_fg[10] = 20; /* левая кромка уровня */ + } + + gfx_w0_unmap(); + return 0; +} + +int8_t pop_room_col_landing(uint8_t room, uint8_t col) __banked +{ + const uint8_t *rf; + int8_t row, res = -1; + if (!pop_lvl_ok || room < 1 || room > 24 || col > 9) return -1; + gfx_w0_map(pop_lvl_page); + rf = room_fg_ptr(room); + for (row = 0; row < 3; row++) { /* кусок пролетает пустые ряды */ + uint8_t t = (uint8_t)(rf[row * 10 + col] & 0x1F); + if (t == 0) continue; /* empty — дальше вниз */ + /* Набор площадок — дословно switch из loose_land (seg007:11E8): + * пол, пика, обе кнопки, зелье и оба факела. На всём остальном + * (стена, ворота, колонна) кусок просто исчезает. Раньше здесь + * стоял только пол, и плита, падавшая на КНОПКУ в комнате снизу, + * пропадала бесследно: ни щебня, ни нажатия (уровень 4, плита + * 16(1,1) -> кнопка 17(0,1), открывающая ворота 23(0,9)). */ + if (t == 1 || t == 2 || t == 6 || t == 10 || + t == 15 || t == 19 || t == 30) res = row; + break; /* первый не-пустой тайл — стоп */ + } + gfx_w0_unmap(); + return res; +} + +/* Рестарт уровня (порт load_level из цикла play_level, seg003:57): вернуть + * в исходное состояние ВСЁ, что игра правит в данных уровня. + * + * Это foretable (из эталонной копии — сами данные мы правим на месте) И + * ТАБЛИЦА СВЯЗЕЙ. Про вторую легко забыть, потому что мутирует в ней ровно + * одно поле — таймер нажатия кнопки в младших битах LINKMAP, — но забыть + * дорого: `pop_tile_code_drawn` рисует кнопку НАЖАТОЙ, пока таймер > 1, и + * ненулевой остаток показывает её нажатой с первого кадра уровня. + * + * Оставить такой остаток особенно легко из died_on_button: он ставит таймер + * в 5 и заводит trob кнопки, но тайл к этому моменту УЖЕ пол — trob умирает + * в первом же обходе (`default: type = -1`), не успев ни разу уменьшить + * таймер. У оригинала ровно то же самое, просто там load_level перечитывает + * весь файл целиком, и вопроса не возникает (найдено пользователем + * 2026-08-18: уровень 7, комната 3, кнопка (2,1) после первого же респавна). + * + * LINKMAP в странице уровня остаётся ЭТАЛОННЫМ: игра правит только + * ОЗУ-копию pop_dl2 (pop_doorlink2_set), обратно в страницу не пишет никто — + * поэтому отдельная pristine-копия, как у foretable, тут не нужна. */ +void pop_level_reset_tiles(void) __banked +{ + uint16_t i; + uint8_t *dst; + const uint8_t *src; + if (!pop_lvl_ok) return; + gfx_w0_map(pop_lvl_page); + dst = (uint8_t *)(LVL_DATA_OFF + BP_FG); + src = (const uint8_t *)LVL_PRISTINE_OFF; + for (i = 0; i < 24u * ROOM_TILES; i++) dst[i] = src[i]; + src = (const uint8_t *)(LVL_DATA_OFF + BP_LINKMAP); + for (i = 0; i < 256; i++) pop_dl2[i] = src[i]; + gfx_w0_unmap(); +} + +/* Рестарт уровня, часть вторая (тот же load_level + pos_guards, seg003:83): + * стражи — такая же часть данных уровня, как тайлы, и перезагрузка файла + * возвращает их всех. Убитый до смерти Кида страж после respawn снова жив + * и стоит на исходном тайле; забывается и запомненная поза трупа + * (guards_seq_hi = 0 -> «поднять стандартной стойкой»). */ +void pop_level_reset_guards(void) __banked +{ + if (!pop_lvl_ok) return; + pop_gstate_init(); +} + +void pop_gstate_init(void) __banked +{ + const uint8_t *p = (const uint8_t *)LVL_DATA_OFF; + uint8_t r; + gfx_w0_map(pop_lvl_page); + for (r = 0; r < 24; r++) { + uint8_t *g = pop_gstate + (uint16_t)r * 6; + g[GS_TILE] = p[BP_GTILE + r]; + g[GS_DIR] = p[BP_GDIR + r]; + g[GS_X] = p[BP_GX + r]; + g[GS_SKILL] = p[BP_GSKILL + r]; + /* pos_guards (seg003:0913), сразу после load_level: X стража + * ПЕРЕСЧИТЫВАЕТСЯ из колонки тайла, а то, что лежит в файле, оригинал + * выбрасывает (в комнате 24 уровня 8 там, например, 255). Дальше X + * владеет leave_guard, и именно она — единственный носитель КОЛОНКИ: + * в guards_tile leave_guard кладёт get_tilepos(0, row), то есть + * колонку обнуляет намеренно. */ + if (g[GS_TILE] < 30) + g[GS_X] = (uint8_t)(pop_x_bump[(g[GS_TILE] % 10) + FIRST_ONSCREEN_COLUMN] + + TILE_SIZEX); + /* guards_seq_lo/hi в ФАЙЛЕ не используются — там 0xFF во всех + * комнатах (оригинал чистит их в reset_level_unused_fields). + * Осмысленное значение туда кладёт только leave_guard, поэтому + * стартуем нулями: 0 = «поднимать стандартной стойкой». + * Без этого в интерпретатор уходил curr_seq = 0xFFFF и он вис. */ + g[GS_SEQL] = 0; + g[GS_SEQH] = 0; + } + gfx_w0_unmap(); +} + +/* Страж комнаты (enter_guard, seg002:0112 + pos_guards, seg003). Возврат + * 0 = стража в комнате нет (tile >= 30). X оригинал пересчитывает из + * колонки (pos_guards), поэтому guards_x из файла не читаем. */ +uint8_t pop_level_guard(uint8_t room, uint8_t *tile, int8_t *dir, + uint8_t *color, uint8_t *skill) __banked +{ + const uint8_t *g; + uint8_t t; + if (!pop_lvl_ok || room < 1 || room > 24) return 0; + /* Тайл/направление/мастерство — из ЖИВОЙ копии (её правит + * pop_guard_state_save); цвет статичен, его читаем со страницы. */ + g = pop_gstate + (uint16_t)(room - 1) * 6; + t = g[GS_TILE]; + if (t < 30) { + *tile = t; + *dir = (int8_t)g[GS_DIR]; + *skill = g[GS_SKILL]; + gfx_w0_map(pop_lvl_page); + *color = (uint8_t)(((const uint8_t *)LVL_DATA_OFF)[BP_GCOLOR + room - 1] & 0x0F); + gfx_w0_unmap(); + } + return (uint8_t)(t < 30); +} + +/* ---- Стартовая позиция уровня ---------------------------------------- */ + +static uint8_t start_byte(uint16_t off) +{ + uint8_t v; + if (!pop_lvl_ok) return 0; + gfx_w0_map(pop_lvl_page); + v = ((const uint8_t *)(LVL_DATA_OFF))[off]; + gfx_w0_unmap(); + return v; +} + +uint8_t pop_level_start_room(void) __banked { return start_byte(BP_START + 0); } +uint8_t pop_level_start_pos(void) __banked { return start_byte(BP_START + 1); } +int8_t pop_level_start_dir(void) __banked { return (int8_t)start_byte(BP_START + 2); } diff --git a/applications/SprPoP/src/pop_level_qsave.c b/applications/SprPoP/src/pop_level_qsave.c new file mode 100644 index 0000000..02c5e6b --- /dev/null +++ b/applications/SprPoP/src/pop_level_qsave.c @@ -0,0 +1,38 @@ +/* + * Сериализация уровня для QuickSave (банк 8). + * + * Код выполняется только по F6/F9 и вынесен из горячего резидентного + * pop_level.c. Данные уровня и снимка используют одно окно W0, поэтому + * foretable переносится небольшими блоками через стековый буфер W2. + */ +#include +#include "pop_level.h" +#include "_pop_level.h" + +void pop_level_qsave(pop_qs_io_t *io) +{ + uint8_t tmp[30]; + uint16_t off; + pop_qs_bytes(io, pop_dl1, sizeof(pop_dl1)); + pop_qs_bytes(io, pop_dl2, sizeof(pop_dl2)); + pop_qs_bytes(io, pop_gstate, sizeof(pop_gstate)); + if (!pop_lvl_ok) { io->failed = 1; return; } + /* W0 одновременно не может держать страницу уровня и страницу снимка. */ + for (off = 0; off < 24u * ROOM_TILES; off += sizeof(tmp)) { + uint8_t i; + if (!io->reading || io->apply) { + gfx_w0_map(pop_lvl_page); + if (!io->reading) + for (i=0; ireading && io->apply && !io->failed) { + gfx_w0_map(pop_lvl_page); + for (i=0; i check_on_floor(): кадр с флагом + * FRAME_NEEDS_FLOOR (0x40), под персонажем не floor -> start_fall(); + * стена -> in_wall(); + * freefall (action 4) -> do_fall(): fall_y-ускорение (в fall_accel/speed), + * достиг pop_y_land[curr_row+1] -> land()/inc_curr_row. + * + * Координатная система PoP: экран.left = 58, тайл = 14 ед. (TILE_SIZEX); + * col = (xpos-58)/14 (округл. вниз), xl = остаток; get_tile_div_mod_m7 = + * div_mod(xpos-7). Карта: fg[row*10+col] & 0x1F; col<0/>9,row<0/>2 = стена. + * + * MVP: без spike/HP/grab/gate — падение + мягкое/среднее приземление. + * Бег В стену (check_bumped) и осторожный шаг у края (get_edge_distance) — + * следующим шагом. + */ +#include +#include /* memcpy = LDIR: цикл на C тут стоил ~390 тактов на байт */ +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */ +#include "pop_ctrl.h" /* pop_ctrl_shift_held() — для check_grab */ +#include "pop_sfx.h" /* звуковые эффекты (sound_plan.md) */ +#include "pop_geom.h" /* общая геометрия (x_bump/y_land/y_to_row) */ +#include "pop_tile.h" /* POP_TILE_DIV/MOD — деление на ширину тайла таблицей */ +#include "pop_redraw.h" /* пометки перерисовки (порт set_redraw_*) */ +/* pop_bg нужен ТОЛЬКО падающему куску (mob): spawn/tick/pos — это движущийся + * ОБЪЕКТ, а не перерисовка тайла (в оригинале он и живёт отдельно: mobs + + * draw_moving). Из-за него pop_loose_tick — единственная функция модуля, + * которую нельзя звать из __banked-кода; вся коллизия/физика/предметы, то + * есть то, что понадобится стражам, чиста от графики. Разделение mob на + * логику и отрисовку — задел следующей фазы. */ +#include "pop_bg.h" +#include "pop_state.h" +#include "pop_trob.h" /* room_modif + trob-список (пики/ворота) */ +#include "pop_level.h" /* pop_level_start_room — вход в дверь уровня */ +#include "pop_guard.h" /* SWORD_2_DRAWN — ветка приземления с мечом */ +#include "pop_tune.h" /* POP_ENABLE_JUMP_GRAB и прочие настройки */ +#include "pop_cdraw.h" /* габарит кадра персонажа (pop_cd) */ + +/* Габарит последнего нарисованного кадра АКТИВНОГО персонажа (char_width_half + * и char_top_y в set_char_collision, seg006:0723). У каждого слота он свой: + * до DRAW-CHAR отрисовка была продублирована, метрики Кида лежали отдельно, и + * коллизия СТРАЖА считалась по ширине спрайта КИДА. Отставание на кадр — + * как в оригинале (cur_frame берётся тем же кадром, что рисуется). */ +#define CD_ACT (&pop_cd[Char.charid == CHARID_0_KID ? POP_CH_KID : POP_CH_OPP]) + +/* ---- Тайлы / действия / seq id (подмножество PoP) ------------------ */ +#define TILE_EMPTY 0 +#define TILE_GATE 4 +#define TILE_DOORTOP_FLOOR 7 /* tapestry (пол сверху) */ +#define TILE_DOORTOP 12 /* tapestry top */ +#define TILE_MIRROR 13 +#define TILE_CHOMPER 18 +#define TILE_WALL 20 +#define TILE_LOOSE 11 /* проваливающийся пол */ +#define TILE_DEBRIS 14 /* щебень (упавший loose) */ +#define TILE_TORCH 19 /* факел */ +#define TILE_TORCH_DEBRIS 30 /* факел + щебень (loose_land) */ +#define TILE_SPIKE 2 /* пики */ +#define TILE_CLOSER 6 /* кнопка-закрывалка (DROP) */ +#define TILE_STUCK 5 /* заклиненная кнопка (died_on_button) */ +#define TILE_POTION 10 /* зелье */ +#define TILE_SWORD 22 /* меч на полу */ +#define TILE_OPENER 15 /* кнопка-открывалка (RAISE, 0x0F) */ +#define TILE_LEVELDOOR_LEFT 16 /* ЛЕВАЯ половина двери уровня (на ней modif) */ +#define ACT_STAND 0 +#define ACT_RUNJUMP 1 +#define ACT_HANGCLIMB 2 +#define ACT_MIDAIR 3 +#define ACT_FREEFALL 4 +#define ACT_BUMPED 5 +#define ACT_HANGSTRAIGHT 6 +#define ACT_TURN 7 + +#define SEQ_7_FALL 7 /* stepfall (stand/run/step/crouch) */ +#define SEQ_17_SOFT_LAND 17 +#define SEQ_18_FALL_STANDJUMP 18 +#define SEQ_19_FALL 19 /* stepfall2 (run frames 9/13) */ +#define SEQ_20_MEDIUM_LAND 20 +#define SEQ_21_FALL_RUNJUMP 21 +#define SEQ_45_BUMPFALL 45 /* удар В ВОЗДУХЕ: отскок и падение */ +#define SEQ_46_HARDBUMP 46 /* удар после прыжка: отскок + ПРИСЕДАНИЕ */ +#define SEQ_47_BUMP 47 /* удар в стену: dx(-4) отскок + 50-52 + stand */ +/* K4 — зацеп/вис/подтягивание/спуск */ +#define SEQ_8_GRAB_STRAIGHT 8 /* прыжок вверх и зацеп (прямо) */ +#define SEQ_9_GRAB_WHILE_JUMPING 9 /* зацеп ПРЯМО В ПРЫЖКЕ (улучшение SDLPoP) */ +#define SEQ_14_JUMP_INTO_CEILING 14 /* прыжок вверх: сверху стена/пол */ +#define SEQ_16_JUMP_UP_AND_GRAB 16 /* прыжок вверх и зацеп (нет пола сзади)*/ +#define SEQ_24_GRAB_FORWARD 24 /* прыжок вверх и зацеп (пол сзади) */ +#define SEQ_28_JUMP_NOTHING_ABOVE 28 /* прыжок вверх: сверху пусто */ +#define SEQ_10_CLIMB_UP 10 /* подтягивание из виса */ +#define SEQ_11_RELEASE_LEDGE_LAND 11 /* сорваться и встать (конец спуска) */ +#define SEQ_15_GRAB_LEDGE_MIDAIR 15 /* зацеп за уступ в падении */ +#define SEQ_23_RELEASE_LEDGE_FALL 23 /* отпустить уступ и падать */ +#define SEQ_68_CLIMB_DOWN 68 /* спуск через зацеп (не приседание) */ +#define SEQ_73_CLIMB_UP_TO_GATE 73 /* подъём к закрытой калитке */ +#define SEQ_51_SPIKED 51 /* напороться на пики (смерть) */ +#define SEQ_54_CHOMPED 54 /* попасть в челюсти чомпера (смерть) */ +#define SEQ_22_CRUSHED 22 /* разбиться при падении (смерть) */ +#define SEQ_63_ACTIVE_AFTER_FALL 63 /* стойка С МЕЧОМ после приземления */ +#define SEQ_65_BUMP_FWD_SWORD 65 /* стража прижали к стене (с мечом) */ +#define SEQ_81_FIGHTFALL 81 /* fightfall: падение из боевой стойки */ +#define SEQ_82_GUARD_PUSHED_OFF 82 /* стража спихнули с уступа */ +#define SEQ_83_GUARD_FALL 83 /* страж сам шагнул в провал с мечом */ + +#define TILE_FLOOR 1 +#define FRAME_109_CROUCH 109 +#define FRAME_178_CHOMPED 178 + +/* Кадры анимации для check_spiked (seg006:0658). */ +#define FRAME_7_RUN 7 +#define FRAME_26_STANDJUMP_LAND 26 +#define FRAME_34_RUNJUMP_START 34 +#define FRAME_43_RUNJUMP_LAND 43 + +#define FRAME_NEEDS_FLOOR 0x40 +#define FRAME_WEIGHT_X 0x1F +#define TILE_RIGHTX 13 +#define SCREENSPACE_X 58 +#define FALL_ACCEL 3 +#define FALL_MAX 33 +/* Перо (seg006:057C, types.h:1435): ускорение и потолок скорости падения + * под зельем медленного падения. */ +#define FALL_ACCEL_FEATHER 1 +#define FALL_MAX_FEATHER 4 + +#define TILE_MIDX 7 +#define FRAME_THIN 0x20 + + +static const int8_t dir_front[2] = {-1, 1}; /* [dir+1]: dir=-1->-1, 0->+1 */ +static const int8_t dir_behind[2] = {1, -1}; /* [dir+1]: назад по направлению */ +/* wall_type(tile): gate/doortop=1, mirror=2, chomper=3, wall=4, иначе 0. */ +static const int8_t wall_dl[6] = {0, 10, 0, -1, 0, 0}; /* wall_dist_from_left */ +static const int8_t wall_dr[6] = {0, 0, 10, 13, 0, 0}; /* wall_dist_from_right */ +static int8_t g_infrontx; /* колонка «перед персонажем» (set в get_tile_infrontof) */ + +/* ---- Карта комнаты ------------------------------------------------- */ +uint8_t pop_fell_out; /* 1 = Kid пересёк НИЖНЮЮ границу (см. pop_map.h) */ +uint8_t pop_leave_dir; /* уход вбок/вверх: 0=нет; 1=left,2=right,3=up (side+1) */ +uint8_t pop_leave_timer; /* exit_room_timer (seg002): >0 = leave заблокирован N кадров после входа в комнату */ +uint8_t pop_loose_fell; /* 0=нет; иначе tilepos+1 упавшего loose (кусок улетел вниз) */ +uint8_t pop_kid_dead; /* 1 = Kid мёртв (напоролся на пики и т.п.) */ +uint8_t pop_kid_hurt; /* 1 = в этом кадре убавилось HP → «брызги» */ + +/* is_screaming (seg005:38): крик падения звучит ОДИН раз за полёт, а не + * каждый кадр. Сбрасывается там же, где у оригинала: приземление (land), + * зацеп за карниз (check_grab) и постановка на старт (set_start_pos). */ +static uint8_t is_screaming; +uint8_t hitp_curr; /* текущее HP (0 = мёртв); pop_kid_hp_reset ставит старт */ +uint8_t hitp_max; /* стартовое HP Кида (для индикатора) */ +/* hitp_beg_lev (seg003 init_game/play_level_2): HP, с которым НАЧАТ уровень. + * Смерть/рестарт уровня возвращают ровно его (do_startpos: hitp_max = + * hitp_curr = hitp_beg_lev), а по ПРОХОЖДЕНИИ уровня он подтягивается к + * достигнутому максимуму (hitp_beg_lev = hitp_max) — так «жизнь» из большой + * склянки переносится на следующий уровень, а из мёртвых прогонов — нет. + * Стартовое значение (custom->start_hitp = 3) ставит main через + * pop_kid_hp_newgame — инициализатором нельзя: модуль банкованный. */ +uint8_t hitp_beg_lev; +/* Дельта HP Кида за кадр (порт hitp_delta). Её ставят боёвка (guards.c) и + * физика (пики/падение/плита на голову — через общий pop_take_hp), а + * применяет pop_do_delta_hp раз в кадр, как в оригинале. Раньше путь + * пик/падения правил hitp_curr напрямую; с переходом физики на Char это + * стало невозможно — урон должен уметь уходить и стражу. */ +int8_t hitp_delta; +static uint8_t g_room; /* номер ТЕКУЩЕЙ комнаты (для room_modif пик/ворот) */ +static uint8_t *g_fg; /* fg[30], row*10+col (МУТАБЕЛЬНАЯ — loose→empty) */ +static uint8_t g_curr_tile; /* = curr_tile2: тайл последнего get_tile */ +static uint8_t g_obj_xl; /* позиция внутри тайла (get_tile_div_mod) */ +static uint8_t grab_timer; /* после зацепа (12): блокирует climb-up */ +/* is_feather_fall (seg000:15F8): кадров, пока действует зелье медленного + * падения. У оригинала это флаг + счётчик тиков со сроком по звуку; у нас + * просто обратный отсчёт кадров (POP_FEATHER_FRAMES, pop_tune.h). Публичный: + * его читают play_seq (опкод JMP_IF_FEATHER, pop_kid.c) и приложение. */ +uint8_t pop_feather; +/* upside_down (seg000:15E9 toggle_upside): 1 — картинка перевёрнута вверх + * ногами (зелье типа 4, уровень 9). Держим ЗДЕСЬ, рядом с пером: оба + * ставятся из одной ветки proc_get_object и оба сбрасываются стартом + * уровня. Читает отрисовка (банки 2/4/7) и главный цикл — отсюда + * публичность. Смерть Кида снимает эффект (seg000:1224). */ +uint8_t pop_upside; +/* ЖЕЛАЕМОЕ состояние переворота. Зелье и смерть Кида пишут только сюда, а + * pop_upside переключает главный цикл — ОДИН раз, в начале кадра, вместе с + * перерисовкой (pop_flip_screen). + * + * Зачем разделение. Зелье выпивается из play_seq, то есть В СЕРЕДИНЕ кадра: + * переключи флаг сразу — и остаток кадра рисуется уже зеркально поверх ещё + * неперевёрнутого фона. Хуже всего пламя факела: оно ЗАПЕКАЕТСЯ в ОЗУ-копию + * (pop_torch_draw), поэтому кадр, положенный в зеркальную позицию на старом + * фоне, оставался там навсегда — по комнате рассыпались лишние языки огня + * (нашёл пользователь на глотке зелья; чит U этим не страдал, потому что + * стоит в начале кадра). */ +uint8_t pop_upside_want; +/* need_redraw_because_flipped (seg000:15E9/1226): состояние ОТЛИЧАЕТСЯ от + * желаемого — главный цикл применит его и снимет флаг. */ +uint8_t pop_upside_dirty; +/* Состояние edge-триггера бампа — массивы флагов перекрытия по колонкам + * (порт prev_coll_flags/curr_row_coll_flags, seg004); объявлены ниже, рядом + * с check_collisions. Сброс при смене комнаты — coll_prev_row. */ +static void pop_coll_invalidate(void); +/* Связи комнаты (0 = нет соседа) + ЦЕЛИКОМ fg соседей слева и справа + * (g_lcol/g_rcol, раскладка комнаты [row*10 + col]). Оригинал резолвит + * любую колонку через find_room_of_tile (seg006:005D), гуляя по roomlinks; + * у нас уровень лежит в EMM-странице, маппить её на каждый get_tile (горячий + * путь физики) дорого — поэтому соседи кэшируются на входе в комнату. + * + * Почему именно ВСЯ комната, а не кромочные колонки: col считается из x + * персонажа (floor((x − 7 − 58)/14)), и на всём диапазоне x 0..255 даёт + * col от −5 до 13. Кромок не хватало, а «за кэшем» get_tile отдавал стену — + * мнимую. Так пропадала видимость стража (BUG-SWORD-GHOST-1: страж после + * follow_guard оказывался в col 12, луч упирался в мнимую стену, Кид прятал + * меч и не мог достать обратно) и так же ломались коллизия/разворот у швов. + * Задаются pop_map_set_edges. */ +static uint8_t g_link_l, g_link_r, g_link_u, g_link_d; +static uint8_t g_lcol[30], g_rcol[30]; + +/* ---- Плита-ПОТОЛОК: loose ряда 2 комнаты СВЕРХУ ------------------- * + * В оригинале это тот же loose, просто get_tile ушёл в комнату сверху + * (find_room_of_tile) и tilepos получился отрицательным (set_redraw* уводит + * такие в redraw_frames_above). У нас ряд 2 верхнего соседа живёт отдельной + * копией (above_fg в приложении, наш «ряд −1»), поэтому и состояние + * отдельное — pop_ceil_modif[col] с той же семантикой (0 покой, 0x80.. — + * тряска, 1..11 — отсчёт до падения). */ +uint8_t pop_ceil_modif[10]; /* публично: читает pop_bg при отрисовке */ +static uint8_t *g_above; /* ряд 2 комнаты сверху (fg), мутабельный */ +uint8_t pop_ceil_fell; /* 0 = нет; иначе col+1 провалившейся плиты-потолка */ + +uint8_t pop_debris_at; /* 0 = нет; иначе tilepos+1 нового щебня в ЭТОЙ комнате */ + +void pop_map_set_above(uint8_t *above_row2_fg) __banked { g_above = above_row2_fg; } + +void pop_map_set(uint8_t *fg) __banked { g_fg = fg; } + +void pop_map_set_room(uint8_t room) __banked { g_room = room; } + +/* do_startpos (seg003:0160): HP уровня = hitp_beg_lev, а не константа — + * иначе большая склянка теряется на границе уровня. */ +void pop_kid_hp_reset(void) __banked +{ + hitp_curr = hitp_max = hitp_beg_lev; + hitp_delta = 0; + pop_kid_dead = 0; +} + +/* Бесшовный переход (seamless, seg003:158): уровень стартует, НЕ трогая HP — + * Кид уносит на следующий и потолок, и полученный урон, потому что для него + * это не «новый уровень», а продолжение того же бега. Всё остальное, что + * делает pop_kid_hp_reset, нужно и здесь: дельта кадра и признак смерти. */ +void pop_kid_hp_carry(void) __banked +{ + hitp_delta = 0; + pop_kid_dead = 0; +} + +/* init_game (seg003:0043): начало ИГРЫ, а не уровня — стартовое HP. */ +void pop_kid_hp_newgame(void) __banked { hitp_beg_lev = POP_START_HITP; } + +/* add_life (seg000:11EC) + set_health_life (seg000:1200): большая склянка + * поднимает ПОТОЛОК HP (до max_hitp_allowed = 10) и доливает до него. */ +static void pop_kid_add_life(void) +{ + uint8_t hpmax = (uint8_t)(hitp_max + 1); + if (hpmax > POP_MAX_HITP) hpmax = POP_MAX_HITP; + hitp_max = hpmax; + hitp_delta = (int8_t)(hitp_max - hitp_curr); +} + +/* Тот же add_life наружу: слияние с тенью (уровень 12) даёт ровно ту же + * прибавку, что большая склянка, а живёт ИИ тени в другом банке. */ +void pop_add_life(void) __banked { pop_kid_add_life(); } + +/* Урон — через общий pop_take_hp (резидент pop_guard.c): физика работает + * над Char, значит и HP обязаны убавляться у ТОГО, кто сейчас в Char. Своя + * копия take_hp здесь была Kid-only и правила hitp_curr мимо дельты. */ + +/* Перечитать fg соседей из уровня, НЕ трогая историю коллизий. Отдельно от + * pop_map_set_edges, потому что нужен и посреди жизни в комнате: тайл соседа + * может измениться, пока тот не отрисован (плита, брошенная Кидом на + * полпути, доваливается заочно — animate_loose в pop_trob.c). g_lcol/g_rcol + * до этого были снимком НА МОМЕНТ ВХОДА, и коллизия у шва продолжала считать + * провалившуюся плиту полом. */ +void pop_map_refresh_edges(void) __banked +{ + uint8_t i; + if (!g_link_l && !g_link_r) return; + pop_level_access_begin(); + for (i = 0; i < 30; i++) { + if (g_link_l) g_lcol[i] = pop_level_tile_raw(g_link_l, i); + if (g_link_r) g_rcol[i] = pop_level_tile_raw(g_link_r, i); + } + pop_level_access_end(); +} + +void pop_map_set_edges(uint8_t left, uint8_t right, uint8_t up, uint8_t down) __banked +{ + g_link_l = left; g_link_r = right; g_link_u = up; g_link_d = down; + /* fg соседей — прямо из уровня, одной скобкой доступа на обе комнаты. + * Держать ещё одну копию в приложении незачем: карта — единственный её + * потребитель (отрисовке швов хватает кромочных колонок pop_room_load). */ + pop_map_refresh_edges(); + /* смена комнаты — колонки перепроецированы, прошлые флаги перекрытия + * недействительны (в оригинале prev_coll_room != curr_row_coll_room и + * бампа на кадре входа нет). БОКОВОЙ переход это отменяет: вызывающий + * сразу зовёт pop_coll_shift(∓10) и историю не теряет. */ + pop_coll_invalidate(); +} + +static uint8_t get_tile(int8_t col, int8_t row) +{ + /* Ряд −1 — ряд 2 комнаты СВЕРХУ (порт find_room_of_tile, seg006:005D: + * отрицательный row уходит по links.up). Нужен для зацепа за край + * провалившейся плиты-потолка (колодец наверх) и выбора seq прыжка. */ + if (row == -1 && g_above && g_link_u && col >= 0 && col <= 9) + g_curr_tile = (uint8_t)(g_above[col] & 0x1F); + else if (row < 0 || row > 2) + g_curr_tile = TILE_WALL; + else if (col < 0 && col >= -10 && g_link_l) /* уходим в комнату СЛЕВА */ + g_curr_tile = g_lcol[row * 10 + col + 10] & 0x1F; + else if (col > 9 && col <= 19 && g_link_r) /* и в комнату СПРАВА */ + g_curr_tile = g_rcol[row * 10 + col - 10] & 0x1F; + else if (col < 0 || col > 9) + g_curr_tile = TILE_WALL; /* край уровня / нет соседа = стена */ + else + g_curr_tile = g_fg[row * 10 + col] & 0x1F; + return g_curr_tile; +} + +/* Тайл ТЕКУЩЕЙ комнаты по клетке — наружу (порт get_tile(room,col,row) для + * случая room == текущая). Нужен лучу видимости стража + * (check_can_guard_see_kid, seg003:688): тот идёт по ряду между Кидом и + * стражем, а оба по условию в одной (= отрисованной) комнате. Колонки за + * пределами 0..9 get_tile разрешает через швы — здесь не используются. */ +uint8_t pop_tile_at(int8_t col, int8_t row) __banked +{ + return get_tile(col, row); +} + +/* Тайлы ОТРЕЗКА ряда одним вызовом — для луча видимости стража. + * + * Зачем: pop_tile_at объявлен __banked, а луч живёт в guards.c (банк 1) и + * звал его ПО ОДНОЙ КОЛОНКЕ. На сцене 11/15 (Кид в колонке 2, страж в 8) + * это девять трамплинов банк 1 -> банк 3 за кадр, и весь луч стоил 36 786 + * тактов — 5,6 % работы кадра при том, что делает он девять чтений байта + * (замер 2026-08-19). Цена трамплина здесь та же, что уже измерена у + * pop_clip_char_top (8 892 такта ради одной проверки тайла). + * + * Колонки за пределами 0..9 разрешает сам get_tile (шов с соседней + * комнатой), поэтому диапазон отдаём как есть, без клипа. + * + * out обязан вмещать c1 - c0 + 1 байт; вызывающий даёт буфер на 12 + * (колонки −1..10 плюс запас). */ +void pop_row_tiles(int8_t row, int8_t c0, int8_t c1, uint8_t *out) __banked +{ + int8_t col; + /* БЫСТРЫЙ ПУТЬ — отрезок целиком внутри комнаты. get_tile для ряда + * 0..2 и колонки 0..9 сводится ровно к `g_fg[row*10+col] & 0x1F` + * (см. его тело выше), а всё остальное там — разбор швов и краёв + * уровня. Идём указателем: иначе на каждую колонку заново считается + * row*10 + col. */ + if (row >= 0 && row <= 2 && c0 >= 0 && c1 <= 9 && g_fg) { + const uint8_t *p = g_fg + (int)row * 10 + c0; + for (col = c0; col <= c1; col++) + *out++ = (uint8_t)(*p++ & 0x1F); + return; + } + for (col = c0; col <= c1; col++) + *out++ = get_tile(col, row); +} + +/* can_bump_into_gate (seg004:373): ОТКРЫТЫЕ ворота проходимы — Kid не бампит и + * идёт сквозь них (на шве — уходит в соседнюю комнату; внутри комнаты — просто + * проходит по полу под поднятыми барами). ВАЖНО: ворота остаются тайлом 4 (в + * tile_is_floor это ПОЛ), поэтому Kid НЕ падает на них. Раньше get_tile ради + * проходимости подменял ворота шва на empty — но подмену видел и check_on_floor + * → start_fall (Kid проваливался при проходе через ОТКРЫТЫЕ ворота шва). Теперь + * проходимость живёт ТОЛЬКО в bump-логике (как в SDLPoP), не трогая проверку пола. + * Модификатор: шов слева (col=-1) — сосед g_link_l col9; шов справа (col=10) — + * g_link_r col0; ВНУТРИ комнаты (0..9) — текущая комната g_room (иначе после + * перехода Kid встаёт на ту же створку уже как на внутрикомнатную и его отбивает + * обратно). Открыто ⇔ (modif>>2)+6 >= char_height (бары выше головы). */ +/* Комната и tilepos клетки (col,row) С УЧЁТОМ ШВА — порт того, что в + * оригинале делает сам get_tile: он зовёт find_room_of_tile (seg006:005D) и + * ПЕРЕСТАВЛЯЕТ curr_room/curr_tilepos, а дальше trigger_button работает уже + * с ними, а не с комнатой персонажа. Возвращает 0, если клетки нет. + * + * Кусало 2026-08-11 (нашёл пользователь на уровне 5): Кид стоит на кнопке, + * которая физически в СОСЕДНЕЙ комнате (col = −1, то есть колонка 9 левого + * соседа). Мы читали модификатор как (своя комната, tilepos 9) — там стена + * с bg = 0, то есть индекс связи 0. Цепочка от нуля в комнате 24 уровня 5 + * открывает И нижние ворота, И верхние, хотя настоящая кнопка (индекс 9) + * открывает только нижние. */ +static uint8_t tile_room_of(int8_t col, int8_t row, uint8_t *tp) +{ + if (row < 0 || row > 2) return 0; + if (col >= 0 && col <= 9) { *tp = (uint8_t)(row * 10 + col); return g_room; } + if (col < 0 && col >= -10 && g_link_l) { *tp = (uint8_t)(row * 10 + col + 10); return g_link_l; } + if (col > 9 && col <= 19 && g_link_r) { *tp = (uint8_t)(row * 10 + col - 10); return g_link_r; } + return 0; +} + +/* Живой модификатор (openness) ворот в (col,row): своя комната или соседняя + * через шов. 0xFF — тайла нет (за краем уровня). */ +static uint8_t gate_modif(int8_t col, int8_t row) +{ + if (row < 0 || row > 2) return 0xFF; + if (col >= 0 && col <= 9) return pop_trob_modif(g_room)[row * 10 + col]; + if (col < 0 && col >= -10 && g_link_l) + return pop_trob_modif(g_link_l)[row * 10 + col + 10]; + if (col > 9 && col <= 19 && g_link_r) + return pop_trob_modif(g_link_r)[row * 10 + col - 10]; + return 0xFF; +} + +/* check_skel (seg002:1053): скелет ВСТАЁТ — его тайл стирается в пол. + * Перерисовать надо две клетки (сам тайл и соседа справа, как в оригинале: + * set_redraw_full + set_wipe на curr_tilepos и curr_tilepos+1) и на ОБЕИХ + * страницах дабл-буфера — иначе через кадр проступает старый скелет. */ +void pop_skel_wake_tile(uint8_t tilepos) __banked +{ + g_fg[tilepos] = TILE_FLOOR; + pop_set_redraw(tilepos, POP_RD_FLOOR, 2); + if ((tilepos % 10) < 9) + pop_set_redraw((uint8_t)(tilepos + 1), POP_RD_FLOOR, 2); +} + +/* Тот же модификатор наружу (луч видимости стража, guards.c): ему нужна + * openness ворот в ЛЮБОЙ колонке луча, включая ушедшие за край комнаты. */ +uint8_t pop_gate_modif(int8_t col, int8_t row) __banked +{ + return gate_modif(col, row); +} + +static uint8_t gate_passable(int8_t col, int8_t row) +{ + uint8_t gm = gate_modif(col, row); + /* Ветки `if (gm == 0xFF) return 0;` здесь БЫТЬ НЕ ДОЛЖНО: 0xFF — это не + * только сторожевое «тайла нет» из gate_modif, но и живой модификатор + * «ворота открыты НАСОВСЕМ» (trigger_gate по button_type = щебень, + * seg007:623). Значение это появилось только когда заработал порт + * loose_land, и ворота выходили нарисованными открытыми (отрисовка + * клампит modl>188 -> 188), но непроходимыми. Сторож сюда не приходит: + * зовут нас только там, где тайл ТОЧНО ворота, а (0xFF>>2)+6 = 69 и так + * больше любого fph. */ + return (uint8_t)((gm >> 2) + 6) >= (uint8_t)CD_ACT->fph; +} + +static uint8_t tile_is_floor(uint8_t t) +{ + switch (t) { + case 0: case 9: case 12: case 20: + case 26: case 27: case 28: case 29: + return 0; + default: + return 1; + } +} + +static uint8_t get_tile_at_char(void) +{ + return get_tile(Char.curr_col, Char.curr_row); +} + +static uint8_t get_tile_infrontof_char(void) +{ + g_infrontx = (int8_t)(dir_front[Char.direction + 1] + Char.curr_col); + return get_tile(g_infrontx, Char.curr_row); +} + +/* ---- Тайлы НАД/ЗА персонажем (для зацепа/спуска, seg006) ----------- */ +static uint8_t get_tile_above_char(void) +{ + return get_tile(Char.curr_col, (int8_t)(Char.curr_row - 1)); +} + +static uint8_t get_tile_behind_char(void) +{ + return get_tile((int8_t)(dir_behind[Char.direction + 1] + Char.curr_col), + Char.curr_row); +} + +static uint8_t get_tile_front_above_char(void) +{ + g_infrontx = (int8_t)(dir_front[Char.direction + 1] + Char.curr_col); + return get_tile(g_infrontx, (int8_t)(Char.curr_row - 1)); +} + +static uint8_t get_tile_behind_above_char(void) +{ + return get_tile((int8_t)(dir_behind[Char.direction + 1] + Char.curr_col), + (int8_t)(Char.curr_row - 1)); +} + +/* Таблицей, а не switch: тайл уже маскирован &0x1F, значит 32 байта покрывают + * весь домен, и вместо шести cp+jr выходит одно чтение по индексу. Зовётся + * на каждую колонку перебора коллизии — там это заметно. */ +static const uint8_t wall_type_tbl[32] = { + 0,0,0,0,1,0,0,1, /* 4,7 — стена справа (gate/doortop) */ + 0,0,0,0,1,2,0,0, /* 12 — то же; 13 — стена слева (mirror) */ + 0,0,3,0,4,0,0,0, /* 18 — chomper; 20 — стена с обеих сторон */ + 0,0,0,0,0,0,0,0 +}; +static uint8_t wall_type(uint8_t t) { return wall_type_tbl[t & 0x1F]; } + +/* ---- Координаты ---------------------------------------------------- */ +static int char_dx_forward(int8_t dx) +{ + int d = dx; + if (Char.direction < 0) d = -d; + return (int)Char.x + d; +} + +/* Деление на ширину тайла — таблицами POP_TILE_DIV/POP_TILE_MOD (резидент, + * pop_tile.c: их же читает слой фона). Вне 0..255 (у нас xpos — int, у + * DOS-оригинала байт) остаётся честная арифметика: путь редкий. */ +/* col = (xpos-58)/14 округл. вниз; g_obj_xl = позиция в тайле (0..13). */ +static int8_t get_tile_div_mod(int xpos) +{ + if ((unsigned int)xpos < 256u) { + g_obj_xl = POP_TILE_MOD[xpos]; + return POP_TILE_DIV[xpos]; + } else { + int x = xpos - SCREENSPACE_X; + int xh = x / TILE_SIZEX; + int xl = x % TILE_SIZEX; + if (xl < 0) { --xh; xl += TILE_SIZEX; } /* округление вниз */ + g_obj_xl = (uint8_t)xl; + return (int8_t)xh; + } +} + +static int8_t get_tile_div_mod_m7(int xpos) { return get_tile_div_mod(xpos - 7); } + +/* «весовая» точка кадра (dx с вычетом weight-битов флагов), в направлении. */ +static int dx_weight(void) +{ + int8_t offset = (int8_t)(kid_cur_dx() - (kid_cur_flags() & FRAME_WEIGHT_X)); + return char_dx_forward(offset); +} + +static void determine_col(void) +{ + Char.curr_col = get_tile_div_mod_m7(dx_weight()); +} + +/* determine_col наружу — для pop_load_fram_det_col (pop_kid): порт связки + * load_frame + determine_col (seg006:0144), которую оригинал зовёт перед + * control(). См. разбор там. */ +void pop_determine_col(void) __banked { determine_col(); } + +/* расстояние до края тайла (для in_wall). */ +static int distance_to_edge(int xpos) +{ + int d; + get_tile_div_mod_m7(xpos); /* -> g_obj_xl */ + d = g_obj_xl; + if (Char.direction == 0) d = TILE_RIGHTX - d; /* dir_0_right */ + return d; +} +static int distance_to_edge_weight(void) { return distance_to_edge(dx_weight()); } + +/* ---- ТОЧНАЯ коллизия со стеной (seg004) --------------------------- * + * Передний край персонажа = char_x_right (взгляд вправо) / char_x_left + * (влево) из set_char_collision (seg006:0723): obj_x/2 + 58, где obj_x = + * 2*char_dx_forward(cur_frame.dx) - 116 и load_frame_to_obj (seg008:1728) + * добавляет +1 для кадров «чётного пикселя» (бит 0x80 флагов XOR знак + * направления). Деление obj_x/2 в C усечено К НУЛЮ, поэтому в зоне левого + * шва (e<=57, obj_x<0) поправка +1 сдвигает край на пиксель ВПРАВО: e -> e+1. + * Это НЕ мелочь: Kid, осевший после отскока от ворот шва на x=57, имеет + * char_x_right=58 и НЕ пересекает порог leave-left (<=57) — иначе пинг-понг + * drawn_room у закрытых ворот (BUG-SEAM-PINGPONG; подтверждено потиковой + * трассой живого SDLPoP 1.23). При e>=58 формула сокращается к чистому + * char_dx_forward. ∓4 для THIN-кадров поверх. dist_from_wall_forward — + * расстояние переднего края до грани стены tiletype в колонке tcol: >0 + * зазор, <=0 достал/зашёл (насколько). */ +static int char_x_forward_edge(void) +{ + int e = char_dx_forward(kid_cur_dx()); + uint8_t fl = kid_cur_flags(); + if (Char.direction < 0) fl ^= 0x80; + if (!(fl & 0x80) && e <= 57) e++; /* odd pixel + C-трунк к нулю */ + return e; +} + +static int char_front_coll(void) +{ + int cx = char_x_forward_edge(); + if (kid_cur_flags() & FRAME_THIN) + cx += (Char.direction < 0) ? 4 : -4; /* THIN: край внутрь */ + return cx; +} + +static int dist_from_wall_forward(uint8_t tiletype, int8_t tcol) +{ + uint8_t type = wall_type(tiletype); + int coll_left, cx; + /* ОТКРЫТЫЕ ворота стеной НЕ являются (seg004:0A18: `if (tiletype == + * tiles_4_gate && !can_bump_into_gate()) return -1`). Без этой ветки + * get_edge_distance считал расстояние до створки как до стены и отдавал + * маленькое d с типом EDGE_WALL: осторожный шаг играл короткую + * последовательность (safe_step берёт seq по самому d), и к краю + * приходилось шагать ДВАЖДЫ. Возврат −1 уводит разбор на тайл впереди, + * где открытые ворота опознаются как обычный пол (d = 11, полный шаг). + * Поймано пользователем 2026-08-24 (подход слева направо). */ + if (tiletype == TILE_GATE && gate_passable(tcol, Char.curr_row)) return -1; + if (type == 0) return 127; + coll_left = pop_x_bump[tcol + FIRST_ONSCREEN_COLUMN] + TILE_MIDX; + cx = char_front_coll(); + if (Char.direction < 0) /* лицом влево */ + return cx - (coll_left + TILE_RIGHTX - wall_dr[type]); + else /* лицом вправо */ + return wall_dl[type] + coll_left - cx; +} + +/* in_wall (seg006:1292): персонаж оказался ВНУТРИ стены (тайл под ним = + * стена) — вытолкнуть. Аутентично SDLPoP: если он глубоко (delta>=8) ИЛИ + * перед ним тоже стена — двигаем НАЗАД к грани (6-delta), иначе ВПЕРЁД из + * стены на соседний тайл (delta+4). Вызывать ТОЛЬКО когда + * get_tile_at_char()==стена (проверяет вызывающий). */ +static void in_wall(void) +{ + int d = distance_to_edge_weight(); + if (d >= 8 || get_tile_infrontof_char() == TILE_WALL) + d = 6 - d; + else + d += 4; + Char.x = (uint8_t)char_dx_forward((int8_t)d); + determine_col(); + get_tile_at_char(); +} + +/* ---- Падение ------------------------------------------------------- */ +static void inc_curr_row(void) { Char.curr_row++; } + +/* Пики (определены ниже, перед pop_phys_tick) — нужны в land() для смерти + * при ПАДЕНИИ на выдвинутые пики (seg005 loc_5EE6). */ +static int is_spike_harmful(uint8_t tilepos); +static void spiked(uint8_t tilepos); + +/* fell_on_spikes (seg005 loc_5EE6): при приземлении тайл ПОД персонажем = + * пика (или пика СЗАДИ у самой кромки) и вредна → напороться (смерть). + * Возвращает 1, если умер (land прерывается). */ +static uint8_t fell_on_spikes(void) +{ + uint8_t tp; + if (get_tile_at_char() == TILE_SPIKE) { + tp = (uint8_t)(Char.curr_row * 10 + Char.curr_col); + } else if (distance_to_edge_weight() >= 12 && + get_tile_behind_char() == TILE_SPIKE) { + int8_t bcol = (int8_t)(dir_behind[Char.direction + 1] + Char.curr_col); + if (bcol < 0 || bcol > 9) return 0; + tp = (uint8_t)(Char.curr_row * 10 + bcol); + } else { + return 0; + } + if (is_spike_harmful(tp)) { spiked(tp); return 1; } + return 0; +} + +static void land(void) +{ + uint8_t seq; + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; + if (fell_on_spikes()) return; /* упал на вредные пики — смерть */ + /* к краю пола — чуть назад (как оригинал) */ + if (!tile_is_floor(get_tile_infrontof_char()) && distance_to_edge_weight() < 3) + Char.x = (uint8_t)char_dx_forward(-3); + pop_start_chompers(Char.room, Char.curr_row); /* seg005:0148 */ + /* Урон от падения (seg005:173) — ветвление по ПЕРСОНАЖУ, а не только по + * высоте. Порт построчно: + * 1 этаж (fall_y < 22): не-Кид (charid >= guard) ИЛИ меч вынут → + * меч в руку + seq_63; иначе присед seq_17. Звук/«нашумел» — только + * у Кида (у стража своё падение никого не будит). + * 2 этажа (< 33): тень падает как с одного этажа, СТРАЖ гибнет сразу, + * Кид (и скелет — баг оригинала, сохраняем) теряет 1 HP; + * 3+ этажа: гибнут все. + * + * Ветка «с мечом» на одном этаже — не косметика: seq_17_soft_land + * заканчивается ВЕЧНЫМ циклом на кадре 109, из которого выводит только + * control_crouched(), а при `sword == sword_2_drawn` управление уходит в + * control_with_sword и до него не доходит — Кид садится в присед + * НАВСЕГДА (BUG-LAND-SWORD-1). */ + is_screaming = 0; /* seg005:116 */ + if (Char.fall_y < 22) { + soft_land: + if (Char.charid >= CHARID_2_GUARD || Char.sword == SWORD_2_DRAWN) { + Char.sword = SWORD_2_DRAWN; + seq = SEQ_63_ACTIVE_AFTER_FALL; + } else { + seq = SEQ_17_SOFT_LAND; + } + /* seg005:185 — мягкое приземление слышно И стражам, и игроку. */ + if (Char.charid == CHARID_0_KID) { pop_sfx_play(17); is_guard_notice = 1; } + } else { + uint8_t deadly = (uint8_t)(Char.fall_y >= 33); /* СНЯТЬ до обнуления */ + if (!deadly && Char.charid == CHARID_1_SHADOW) goto soft_land; /* seg005:189 */ + if (!deadly && Char.charid == CHARID_2_GUARD) deadly = 1; /* seg005:190 */ + Char.fall_x = Char.fall_y = 0; + if (deadly || pop_take_hp(1)) { /* 3+ этажа или последнее HP */ + pop_take_hp(100); + if (Char.charid == CHARID_0_KID) pop_sfx_play(0); /* разбился */ + pop_char_set_seq(SEQ_22_CRUSHED); + play_seq(); + determine_col(); + if (Char.charid == CHARID_0_KID) pop_kid_dead = 1; + return; + } + if (Char.charid == CHARID_0_KID) { pop_sfx_play(16); is_guard_notice = 1; } /* seg005:195 */ + seq = SEQ_20_MEDIUM_LAND; + } + Char.fall_x = Char.fall_y = 0; + pop_char_set_seq(seq); + play_seq(); + determine_col(); +} + +static void start_fall(void) +{ + uint8_t frame = Char.frame, seq, tile; + /* seg006:1044 первым делом убирает меч в ножны: дальше падением рулит + * не боевой диспетчер, а обычный. Без этого Kid летит «с клинком», и + * control_with_sword разбирает кадры падения как боевые. */ + Char.sword = SWORD_0_SHEATHED; + inc_curr_row(); + pop_start_chompers(Char.room, Char.curr_row); /* seg006:08C1 */ + /* seg006:1044 start_fall: frame 9 -> seq_7, 13 -> seq_19 (лишний dx(1)) */ + if (frame == 13) seq = SEQ_19_FALL; + else if (frame == 26) seq = SEQ_18_FALL_STANDJUMP; + else if (frame == 44) seq = SEQ_21_FALL_RUNJUMP; + else if (frame >= 81 && frame < 86) { + /* сорвался, приземляясь после прыжка вверх: сдвиг ВПЕРЁД на 5 */ + seq = SEQ_19_FALL; + Char.x = (uint8_t)char_dx_forward(5); + pop_load_fram_det_col(); + } + else if (frame >= 150 && frame < 180) { + /* Кадры С МЕЧОМ (150..179) — ушёл в провал из боевой стойки + * (seg006:1123). Отдельная последовательность, а не seq_7: у той + * дрейф fall_x=1, и персонаж уезжал на ~тайл в сторону за полёт. + * Ветвление по персонажу — как в оригинале: + * СТРАЖ: спихнули (fall_x < 0) → seq_82, сам шагнул → seq_83; + * ПРОЧИЕ (Кид): seq_81 + взвести droppedout — по нему страж + * решает, идти ли следом вниз (guard_follows_kid_down). */ + if (Char.charid == CHARID_2_GUARD) { + if (Char.fall_x < 0) { + if (Char.direction < 0 && distance_to_edge_weight() <= 7) + Char.x = (uint8_t)char_dx_forward(-5); + seq = SEQ_82_GUARD_PUSHED_OFF; + } else { + pop_droppedout = 0; + seq = SEQ_83_GUARD_FALL; + } + } else { + pop_droppedout = 1; + if (Char.direction < 0 && distance_to_edge_weight() <= 7) + Char.x = (uint8_t)char_dx_forward(-5); + seq = SEQ_81_FIGHTFALL; + } + } + else seq = SEQ_7_FALL; /* frame 9 + stand/step/crouch */ + pop_char_set_seq(seq); + play_seq(); + determine_col(); + /* Проверки «упал в стену» — ТОЛЬКО пока персонаж В комнате (curr_row<=2). + * Ниже нижнего ряда (curr_row=3 после inc — падение сквозь дыру loose) + * наш get_tile отдаёт WALL как СЕНТИНЕЛ кромки уровня, но в оригинале там + * КОМНАТА СНИЗУ (у дыры — пол/пусто, не стена). Без гарда in_wall() ложно + * срабатывал и откатывал X назад (6−d) → дайв падения смещался на тайл + * влево (Кид «клевал» рано над колонной вместо дыры). */ + if (Char.curr_row <= 2) { + if (get_tile_at_char() == TILE_WALL) { + in_wall(); + return; + } + /* Хвост seg006:1099: тайл ПЕРЕД персонажем — стена → отодвинуть на 1 + * назад (иначе передний край зайдёт в грань). (seq_104 для frame 44 с + * малым зазором не портирован — в этом тесте не встречается.) */ + tile = get_tile_infrontof_char(); + if (tile == TILE_WALL) { + if (frame != 44 || distance_to_edge_weight() >= 6) + Char.x = (uint8_t)char_dx_forward(-1); + determine_col(); + } + } +} + +/* ---- Зацеп (seg006 can_grab/check_grab) --------------------------- * + * can_grab: можно ли зацепиться за g_curr_tile (curr_tile2) СКВОЗЬ through- + * тайл? Kid-only (без loose/opener-модификаторов). */ +static uint8_t can_grab(uint8_t through) +{ + if (through == TILE_WALL) return 0; /* сквозь стену нельзя */ + if (through == TILE_DOORTOP && Char.direction >= 0) return 0; /* верх двери, лицом вправо */ + if (tile_is_floor(through)) return 0; /* сквозь пол нельзя */ + if (g_curr_tile == TILE_DOORTOP_FLOOR && Char.direction < 0) return 0; + if (!tile_is_floor(g_curr_tile)) return 0; /* не за что цепляться */ + return 1; +} + +static uint8_t can_grab_front_above(void) +{ + uint8_t through = get_tile_above_char(); + get_tile_front_above_char(); /* g_curr_tile = спереди-сверху */ + return can_grab(through); +} + +/* check_grab (seg006:0A28): в падении при зажатом Shift — зацепиться за + * уступ спереди-сверху (seq_15), если скорость падения ещё мала и высота + * подходящая. Выравнивает передний край по кромке, гасит fall_y, взводит + * grab_timer (блок climb-up на несколько кадров). */ +static void check_grab(void) +{ + uint8_t old_x; + if (!pop_ctrl_shift_held()) return; /* Shift не зажат */ + if ((uint8_t)Char.fall_y >= 32) return; /* падает слишком быстро */ + if ((uint16_t)pop_y_land[Char.curr_row + 1] > (uint16_t)(Char.y + 25)) return; + old_x = Char.x; + Char.x = (uint8_t)char_dx_forward(-8); + determine_col(); + if (!can_grab_front_above()) { + Char.x = old_x; /* не за что — назад */ + determine_col(); + } else { + Char.x = (uint8_t)char_dx_forward((int8_t)distance_to_edge_weight()); + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; + Char.fall_y = 0; + pop_char_set_seq(SEQ_15_GRAB_LEDGE_MIDAIR); + play_seq(); + determine_col(); + grab_timer = 12; + pop_sfx_play(9); /* seg006 check_grab */ + is_screaming = 0; /* seg006:1219 */ + } +} + +static void make_loose_fall(int pos, uint8_t modifier); + +/* check_grab_run_jump — ЗАЦЕП ПРЯМО В ПРЫЖКЕ, при Shift, зажатом ЗАРАНЕЕ. + * + * ВАЖНО: в оригинальной игре этого НЕТ. Это улучшение SDLPoP + * (`USE_JUMP_GRAB` + `fixes->enable_jump_grab`, seg006:1228), и там оно + * выключено, пока не включены fixes-and-enhancements. Ванильное поведение: + * зацепиться можно только в НАЧАЛЕ ПАДЕНИЯ (check_grab, кадры 102..105) — + * то есть Shift надо успеть нажать уже в полёте. Пользователь ловил ровно + * это и просил «чтобы работало и при заранее зажатом Shift», поэтому + * улучшение портировано, но за выключателем POP_ENABLE_JUMP_GRAB + * (pop_tune.h): игровая сборка сможет вернуться к оригиналу. + * + * Условия оригинала: действие — прыжок, кадр 22..23 (прыжок с места) или + * 39..41 (разбег-прыжок), «вперёд» ОТПУЩЕНО, «вверх» зажато. Дальше — та + * же проба, что у check_grab, но персонаж не падает, а сразу ставится на + * кромку и играет seq_9. + * + * Упрощение против SDLPoP: там колонка захвата правится, когда тайл + * оказался в ЧУЖОЙ комнате (curr_room != Char.room) — у нас модель + * однокомнатная, get_tile сам резолвит колонки за швом в координатах + * ТЕКУЩЕЙ комнаты, поэтому правка не нужна. Зато кнопка/плита за швом + * (grab_col вне 0..9) не сработают — тайл принадлежит соседу, а + * pop_trob_modif/g_fg у нас про свою комнату; такие случаи пропускаем. */ +static uint8_t check_grab_run_jump(void) +{ + uint8_t frame = Char.frame; + uint8_t grab_tile; + int8_t grab_col, grab_row; + + /* Сам выключатель проверяют ТОЧКИ ВЫЗОВА (иначе SDCC ругается на + * недостижимый код при POP_ENABLE_JUMP_GRAB = 1). */ + if (Char.action != ACT_RUNJUMP) return 0; + if (!((frame >= 22 && frame <= 23) || (frame >= 39 && frame <= 41))) return 0; + if (control_x != POP_CONTROL_RELEASED || control_y != POP_CONTROL_HELD_UP) return 0; + if (!can_grab_front_above()) return 0; + + grab_tile = g_curr_tile; /* тайл, за который взялись */ + grab_col = g_infrontx; + grab_row = (int8_t)(Char.curr_row - 1); /* СНЯТЬ до play_seq: он двигает ряд */ + + Char.x = (uint8_t)(pop_x_bump[grab_col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX); + Char.x = (uint8_t)char_dx_forward(Char.direction < 0 ? -12 : 2); + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; + pop_char_set_seq(SEQ_9_GRAB_WHILE_JUMPING); + play_seq(); + determine_col(); + grab_timer = 12; + + /* Хвост оригинала: за что схватились — то и сработает. Нужен потому, + * что check_press на следующем кадре уже не поможет: кадр сменится на + * 81 (hangdrop), если Shift отпустят сразу. */ + if (grab_col >= 0 && grab_col <= 9 && grab_row >= 0 && grab_row <= 2) { + uint8_t tp = (uint8_t)(grab_row * 10 + grab_col); + if (grab_tile == TILE_OPENER || grab_tile == TILE_CLOSER) + pop_trigger_button(g_room, tp, grab_tile, pop_trob_modif(g_room)[tp], 1); + else if (grab_tile == TILE_LOOSE) { + is_guard_notice = 1; + make_loose_fall(tp, 1); + } + } + return 1; +} + +static void do_fall(void) +{ + uint8_t nrow = (uint8_t)(Char.curr_row + 1); + if (!is_screaming && Char.fall_y >= 31) { + pop_sfx_play(1); /* seg005:39 — крик падения */ + is_screaming = 1; + } + if (nrow > 4) nrow = 4; /* защита pop_y_land[] от выхода */ + if ((uint16_t)pop_y_land[nrow] > (uint16_t)Char.y) { + check_grab(); /* ещё летит — попытка зацепа */ + } else if (Char.curr_row <= 2) { + if (get_tile_at_char() == TILE_WALL) + in_wall(); + if (tile_is_floor(g_curr_tile)) + land(); + else + inc_curr_row(); /* следующий ряд, ВКЛЮЧАЯ 2 -> 3 */ + /* Ряд 3 — «за нижней кромкой комнаты» (в оригинале get_tile уходит по + * links.down и это ряд 0 комнаты снизу, seg006:005D). Нам он нужен + * ровно за тем, за чем нужен оригиналу: пока Kid летит вдоль ряда 3, + * check_grab пробует зацеп за тайл ряда 2 СВОЕЙ комнаты — иначе + * спуск «повис на кромке, отпустил, зацепился этажом ниже» через + * нижнюю границу комнаты невозможен в принципе (BUG-GRAB-SEAM-DOWN). + * Раньше ряд залипал на 2, check_grab не звался ни разу, и Kid + * пролетал мимо кромки, набирая fall_y > 32 — после чего окно + * зацепа закрыто уже по скорости. */ + } + /* curr_row == 3: тайлов своей комнаты там нет (get_tile отдаёт WALL как + * сентинел), поэтому ни in_wall, ни land здесь звать нельзя — до y_land[4] + * дело всё равно не доходит: check_leave_below переводит в комнату снизу + * на y >= 211 (порт leave_room). */ +} + +static void fall_accel(void) +{ + if (Char.action == ACT_FREEFALL) { + int fy; + /* Перо (seg006:057C). Оригинал ловит эффектом ЛЮБОГО персонажа в + * Char — зелье пьёт только Кид, но флаг глобальный, и страж, упавший + * в ту же минуту, тоже парит; SDLPoP чинит это опцией + * fix_feather_fall_affects_guards. Берём поведение С ФИКСОМ (только + * Кид): расхождение осознанное, запись в ../docs/impl_diff.md. */ + if (pop_feather && Char.charid == CHARID_0_KID) { + fy = (uint8_t)Char.fall_y + FALL_ACCEL_FEATHER; + if (fy > FALL_MAX_FEATHER) fy = FALL_MAX_FEATHER; + } else { + fy = (uint8_t)Char.fall_y + FALL_ACCEL; + if (fy > FALL_MAX) fy = FALL_MAX; + } + Char.fall_y = (int8_t)fy; + } +} + +static void fall_speed(void) +{ + Char.y = (uint8_t)(Char.y + (uint8_t)Char.fall_y); + if (Char.action == ACT_FREEFALL) { + Char.x = (uint8_t)char_dx_forward(Char.fall_x); + determine_col(); + } +} + +/* ---- get_edge_distance (seg004:067C) — ТОЧНО ---------------------- * + * Расстояние переднего края до стены/края впереди + тип g_edge_type: WALL + * (стена в пределах тайла), FLOOR (пол — далеко, 11), CLOSER (край уступа + * над ямой — distance_to_edge_weight). Стена сперва под персонажем, потом + * перед ним; dist<=TILE_RIGHTX -> WALL, иначе FLOOR. */ +static uint8_t g_edge_type; + +static int edge_classify(int d) +{ + if (d <= TILE_RIGHTX) g_edge_type = EDGE_WALL; + else { g_edge_type = EDGE_FLOOR; d = 11; } + return d; +} + +int pop_edge_distance(void) __banked +{ + uint8_t t; int d; + determine_col(); + t = get_tile_at_char(); + if (wall_type(t)) { + d = dist_from_wall_forward(t, Char.curr_col); + if (d >= 0) return edge_classify(d); + } + t = get_tile_infrontof_char(); + /* Верх двери лицом ВПРАВО — кромка (в оригинале ПЕРВОЙ проверкой, до + * wall_type: у doortop wall_type != 0). */ + if (t == TILE_DOORTOP && Char.direction >= 0) { + g_edge_type = EDGE_CLOSER; + return distance_to_edge_weight(); + } + if (wall_type(t)) { + d = dist_from_wall_forward(t, g_infrontx); + if (d >= 0) return edge_classify(d); + } + /* ПРОВАЛИВАЮЩИЙСЯ пол впереди — КРОМКА, хотя tile_is_floor считает его + * полом. Это и есть механизм «проверки ногой»: осторожный шаг доводит + * Kid ровно до границы плиты (distance -> 0), там safe_step играет + * seq_44 (testfoot) — Kid щупает плиту ногой, та трясётся (SEQ_KNOCK_DOWN), + * и он остаётся на месте; шаг НА плиту — только со второго нажатия + * (Char.repeat). Без этой ветки плита читалась как обычный пол, и Kid + * заходил на неё первым же осторожным шагом и падал вместе с ней. */ + if (t == TILE_LOOSE) { + g_edge_type = EDGE_CLOSER; + return distance_to_edge_weight(); + } + /* Кнопка-закрывалка/меч/зелье: кромка, пока до границы есть зазор. */ + if (t == TILE_CLOSER || t == TILE_SWORD || t == TILE_POTION) { + d = distance_to_edge_weight(); + if (d) { g_edge_type = EDGE_CLOSER; return d; } + g_edge_type = EDGE_FLOOR; return 11; + } + if (tile_is_floor(t)) { g_edge_type = EDGE_FLOOR; return 11; } + g_edge_type = EDGE_CLOSER; /* яма впереди — до кромки уступа */ + return distance_to_edge_weight(); +} + +uint8_t pop_edge_type(void) __banked { return g_edge_type; } + +/* Тайл, на котором закончился разбор края (порт curr_tile2 после + * get_edge_distance). Нужен control'у: правило «у стены шагаем, а не + * бежим» в оригинале ЯВНО исключает чомпер (seg005:0577). */ +uint8_t pop_edge_tile(void) __banked { return g_curr_tile; } + +/* ---- Прыжок вверх с зацепом (seg005 check_jump_up и производные) --- * + * pop_jump_up_seq выбирает seq для ↑ в стойке: чистый прыжок (seq_28 в + * пустоту / seq_14 в потолок) ЛИБО прыжок-и-зацеп за уступ выше (seq_8/ + * 24 — с полом сзади, seq_16 — без), и выравнивает Char.x. control_up + * гасит caller (pop_ctrl). Порт: check_jump_up/grab_up_with_floor_behind/ + * jump_up_or_grab/grab_up_no_floor_behind/jump_up (без super-high-jump). */ +static uint8_t grab_up_with_floor_behind(void) +{ + int distance = distance_to_edge_weight(); + int edge_distance = pop_edge_distance(); /* побочно ставит g_edge_type */ + if (distance < 4 && edge_distance < 4 && g_edge_type != EDGE_WALL) { + Char.x = (uint8_t)char_dx_forward((int8_t)distance); + return SEQ_8_GRAB_STRAIGHT; + } + Char.x = (uint8_t)char_dx_forward((int8_t)(distance - 4)); + return SEQ_24_GRAB_FORWARD; +} + +static uint8_t jump_up_plain(void) /* seg005 jump_up (без super) */ +{ + int distance = pop_edge_distance(); /* ставит g_edge_type */ + int8_t col; + uint8_t t; + if (distance < 4 && g_edge_type == EDGE_WALL) + Char.x = (uint8_t)char_dx_forward((int8_t)(distance - 3)); + col = get_tile_div_mod(dx_weight() - 6); + t = get_tile(col, (int8_t)(Char.curr_row - 1)); + if (t != TILE_WALL && !tile_is_floor(t)) + return SEQ_28_JUMP_NOTHING_ABOVE; + return SEQ_14_JUMP_INTO_CEILING; +} + +static uint8_t grab_up_no_floor_behind(void) +{ + get_tile_above_char(); + Char.x = (uint8_t)char_dx_forward((int8_t)(distance_to_edge_weight() - 10)); + return SEQ_16_JUMP_UP_AND_GRAB; +} + +static uint8_t jump_up_or_grab(void) +{ + int distance = distance_to_edge_weight(); + if (distance < 6) + return jump_up_plain(); + if (!tile_is_floor(get_tile_behind_char())) + return grab_up_no_floor_behind(); + Char.x = (uint8_t)char_dx_forward((int8_t)(distance - TILE_SIZEX)); + determine_col(); /* load_fram_det_col */ + return grab_up_with_floor_behind(); +} + +uint8_t pop_jump_up_seq(void) __banked +{ + uint8_t through = get_tile_above_char(); + get_tile_front_above_char(); /* g_curr_tile = спереди-сверху */ + if (can_grab(through)) + return grab_up_with_floor_behind(); + through = get_tile_behind_above_char(); + get_tile_above_char(); /* g_curr_tile = сверху */ + if (can_grab(through)) + return jump_up_or_grab(); + return jump_up_plain(); +} + +/* run_jump (seg005:0AA8), часть «выровнять по кромке» ------------------ * + * Оригинал НЕ отталкивается откуда попало: перед разбег-прыжком он смотрит + * на 1–2 тайла вперёд и, если там провал (или пика), подгоняет X так, чтобы + * толчок пришёлся ровно на кромку пола. Иначе прыжок стартует в случайной + * фазе бегового цикла — а запаса у seq_4 почти нет: суммарный dx кадров + * 34..44 равен 62 px при ширине тайла 14, то есть ровно 4 колонки и меньше + * половины тайла сверху. Поэтому без выравнивания провал в ТРИ пустых + * тайла (перелёт с колонки 5 на колонку 1) становится непроходимым — + * BUG-RJUMP-1, уровень 2, комнаты 1 и 9. + * + * Возврат: 1 — прыгать (Char.x уже подогнан), 0 — прыжок ОТМЕНИТЬ. Отмена + * в оригинале НЕ гасит control_up: Кид бежит дальше с зажатой «вверх», и на + * следующем кадре попытка повторяется — так игрок ловит нужную фазу, просто + * удерживая клавишу. */ +uint8_t pop_run_jump_align(void) __banked +{ + int xpos = char_dx_forward(4); + int8_t col = get_tile_div_mod_m7(xpos); + uint8_t tf; + + for (tf = 0; tf < 2; tf++) { + uint8_t t; + col = (int8_t)(col + dir_front[Char.direction + 1]); + t = get_tile(col, Char.curr_row); + if (t != TILE_SPIKE && tile_is_floor(t)) continue; /* тут пол — дальше */ + + { + int adj = distance_to_edge(xpos) + TILE_SIZEX * (int)tf - TILE_SIZEX; + /* Оригинал сравнивает БЕЗЗНАКОВО: + * if ((word)adj < (word)-8 || adj >= 2) { if (adj < 128) return; adj = -3; } + * что означает ровно «adj НЕ попал в [-8,-1] → прыжка нет». + * Ветка с adj = -3 недостижима: distance_to_edge ∈ [0,13] и + * tf ∈ {0,1} дают adj ∈ [-14,13], а туда нужно adj >= 128. */ + if (adj < -8 || adj > -1) return 0; + Char.x = (uint8_t)char_dx_forward((int8_t)(adj + 4)); + } + break; + } + return 1; +} + +/* Стена впереди достаточно близко для стопа бега? Детект (без сдвига — + * позицию держит check_bumped=in_wall). d<=2: передний край почти у грани. */ +int pop_wall_ahead(void) __banked +{ + int d = pop_edge_distance(); + return (g_edge_type == EDGE_WALL && d <= 2); +} + +/* ---- Вис (control_hanging из seg005) — тайловые решения ------------ * + * Управление держится в pop_ctrl (control_*), сюда вынесены только + * тайловые предикаты + выбор seq (+ выравнивание Char.x). */ + +/* grab_timer истёк — можно подтягиваться? */ +uint8_t pop_hang_grab_ready(void) __banked { return grab_timer == 0; } + +/* can_climb_up (seg005:09DF): тайл над персонажем — обычный подъём (seq_10) + * либо подъём к ЗАКРЫТОЙ калитке/зеркалу/чомперу (seq_73 = «влез и съехал + * обратно»). Для ворот условие включает ОТКРЫТОСТЬ: + * curr_tile2 == tiles_4_gate && direction != right && modif >> 2 < 6 + * — под поднятой решёткой (modif >> 2 >= 6) подъём обычный. Без проверки + * модификатора Kid срывался и с открытых ворот. */ +uint8_t pop_hang_climb_up_seq(void) __banked +{ + uint8_t t = get_tile_above_char(); + if (((t == TILE_MIRROR || t == TILE_CHOMPER) && Char.direction == 0) || + (t == TILE_GATE && Char.direction != 0 && + (uint8_t)(gate_modif(Char.curr_col, (int8_t)(Char.curr_row - 1)) >> 2) < 6)) + return SEQ_73_CLIMB_UP_TO_GATE; + return SEQ_10_CLIMB_UP; +} + +/* Shift в висе: 1 = висит у стены/двери (caller играет seq_25); -1 = + * сорваться (hang_fall); 0 = сверху пол, остаётся висеть (ничего). */ +int8_t pop_hang_wall(void) __banked +{ + uint8_t t = get_tile_at_char(); + if (Char.action != ACT_HANGSTRAIGHT && + (t == TILE_WALL || + (Char.direction < 0 && (t == TILE_DOORTOP_FLOOR || t == TILE_DOORTOP)))) + return 1; + if (!tile_is_floor(get_tile_above_char())) + return -1; + return 0; +} + +/* hang_fall (seg005:0A46): сорваться. Нет пола ни сзади, ни под + * персонажем — seq_23 (отпустить и падать); иначе прижать к стене (-7) + * и seq_11 (встать, конец спуска). Меняет Char.x, возвращает seq. */ +uint8_t pop_hang_fall_seq(void) __banked +{ + uint8_t behind = get_tile_behind_char(); + uint8_t at; + if (!tile_is_floor(behind) && !tile_is_floor(get_tile_at_char())) + return SEQ_23_RELEASE_LEDGE_FALL; + at = get_tile_at_char(); + if (at == TILE_WALL || + (Char.direction < 0 && (at == TILE_DOORTOP_FLOOR || at == TILE_DOORTOP))) + Char.x = (uint8_t)char_dx_forward(-7); + return SEQ_11_RELEASE_LEDGE_LAND; +} + +/* down_pressed (seg005:04C7) tile-часть: 1 = обработано (НЕ приседать — + * либо спуск seq_68 через зацеп, либо отступ от передней кромки), 0 = + * приседать. Меняет Char.x. Kid развёрнут от края (край сзади) у кромки + * пола + под соседним тайлом есть за что зацепиться → спуск на этаж вниз. */ +/* Ветка «войти в дверь уровня» из up_pressed (seg005:0482) вместе с + * go_up_leveldoor (seg005:0574). Дверь рядом и открыта → поставить Кида в + * проём, вернуть 1 (последовательность seq_70 ставит вызывающий — это его + * слой); иначе 0 и Кид не тронут. + * + * Геометрию персонажа меняем в Kid, а НЕ в Char: окно Char вокруг + * диспетчера возвращает назад ТОЛЬКО curr_seq и sword + * (pop_savekid_state), поэтому во всём порте позицию правит pop_map — как + * в pop_down_action и pop_jump_up_seq. Запись в Char.x/Char.direction + * отсюда молча теряется (проверено: Кид уходил из проёма влево). + * + * Порядок проб как в оригинале и с той же семантикой `else if`: берём + * ПЕРВУЮ клетку, в которой стоит дверь (под персонажем → за спиной → перед + * лицом), и уже её проверяем на открытость — а не ищем «первую открытую». + * + * Открытость: `modifier >= 42`, то есть створка доехала до верха. Это + * ветка fix_exit_door, а не ванильный глобал leveldoor_open: с ванильным + * условием в дверь можно войти, пока она ещё ползёт вверх, и персонаж + * проходит сквозь опущенную створку. + * + * Стартовая комната исключена (`level.start_room != drawn_room`): в первой + * комнате уровня дверь — та, через которую вошли, и обратно в неё нельзя. */ +uint8_t pop_leveldoor_enter(void) __banked +{ + int8_t cols[3]; + uint8_t i; + + if (pop_level_start_room() == g_room) return 0; + + cols[0] = Char.curr_col; + cols[1] = (int8_t)(dir_behind[Char.direction + 1] + Char.curr_col); + cols[2] = (int8_t)(dir_front[Char.direction + 1] + Char.curr_col); + for (i = 0; i < 3; i++) { + if (get_tile(cols[i], Char.curr_row) != TILE_LEVELDOOR_LEFT) + continue; + /* дверь найдена — дальше решает только её modif (см. выше) */ + if (cols[i] < 0 || cols[i] > 9) return 0; /* дверь в шве: не наша */ + if (pop_trob_modif(g_room)[Char.curr_row * 10 + cols[i]] < 42) + return 0; + /* go_up_leveldoor (seg005:0574): встать РОВНО в проём и развернуться + * ВЛЕВО. Направление тут не косметика: все DX последовательности + * seq_70 ОТРИЦАТЕЛЬНЫЕ (−5,−4,−4,−2,−3,−1,−3,−1,−2), а + * char_dx_forward при взгляде влево меняет им знак — то есть именно + * dir_FF_left и гонит персонажа ВПРАВО-ВВЕРХ по лестнице. При + * взгляде вправо тот же seq уводит его влево, ИЗ проёма. */ + Char.x = (uint8_t)(pop_x_bump[cols[i] + FIRST_ONSCREEN_COLUMN] + 10); + Char.direction = -1; + return 1; + } + return 0; +} + +uint8_t pop_down_action(void) __banked +{ + if (!tile_is_floor(get_tile_infrontof_char()) && distance_to_edge_weight() < 3) { + Char.x = (uint8_t)char_dx_forward(5); /* у передней кромки — отступ */ + determine_col(); + return 1; + } + if (!tile_is_floor(get_tile_behind_char()) && distance_to_edge_weight() >= 8) { + uint8_t through = get_tile_behind_char(); + get_tile_at_char(); /* g_curr_tile = под персонажем */ + /* seg005:482: спуск с тайла-ворот лицом ВЛЕВО запрещён только пока они + * ЗАКРЫТЫ (modif >> 2 < 6); под поднятой решёткой спуск обычный. */ + if (can_grab(through) && + (Char.direction >= 0 || get_tile_at_char() != TILE_GATE || + (uint8_t)(gate_modif(Char.curr_col, Char.curr_row) >> 2) >= 6)) { + Char.x = (uint8_t)char_dx_forward((int8_t)(distance_to_edge_weight() - 9)); + pop_char_set_seq(SEQ_68_CLIMB_DOWN); + return 1; + } + } + return 0; +} + +/* ---- Предметы: меч (seg005 check_get_item/get_item + seg006 do_pickup/ + * proc_get_object) --------------------------------------------------- * + * pop_have_sword — порт глобала have_sword: «меч у Кида». Боевой режим + * (стойка с мечом, бой) НЕ реализован и здесь не нужен: сиквенс подъёма + * seq_91 сам заканчивается убиранием меча в ножны (кадры 230..240 sheathe) + * и возвратом в обычную стойку — как в оригинале до встречи со стражем. */ +uint8_t pop_have_sword; +uint8_t pop_item_taken; /* 0 = нет; иначе tilepos+1 поднятого */ +uint8_t pop_flash_time; /* кадров вспышки фона (flash_time) */ +/* Цвет вспышки: 0 = ярко-жёлтая (меч), 1 = красная (зелье). В оригинале это + * flash_color; нам хватает двух значений, которые реально встречаются. */ +uint8_t pop_flash_color; +static int8_t pickup_obj_type; /* -1 = меч (порт pickup_obj_type) */ + +/* do_pickup (seg006:1671): стереть предмет с карты (тайл → пол) и запомнить + * тип; сам эффект применяет опкод GET_ITEM внутри сиквенса. */ +static void do_pickup(int8_t obj_type, uint8_t tilepos) +{ + pickup_obj_type = obj_type; + g_fg[tilepos] = TILE_FLOOR; + /* ...и В ДАННЫХ УРОВНЯ тоже, ПРЯМО СЕЙЧАС. У оригинала curr_room_tiles — + * это сама таблица уровня, так что `= tiles_1_floor` (seg006:1671) виден + * всем в тот же миг; у нас fg живёт в двух местах (буфер отрисовки g_fg + * и EMM-страница уровня), и раньше страницу правил главный цикл — ПОЗЖЕ + * в кадре, уже после pop_process_trobs. + * + * Из-за этого зазора trob поднятого зелья успевал проснуться: код тайла + * он читает из страницы уровня, там ещё зелье — и animate_potion крутил + * фазу пузырька поверх только что обнулённого модификатора, + * bubble_next_frame(0) = 1. Модификатор оставался ЕДИНИЦЕЙ навсегда + * (room_modif персистентен), а для ПОЛА в паласе modif 1 означает «узор + * не рисовать» (seg008:499, `if (num == !!level_type) return`) — синяя + * лента пропадала на месте выпитого зелья и не возвращалась даже после + * полной перерисовки комнаты (BUG-POTION-STRIPE). Теперь тайл в + * странице — пол сразу, trob того же кадра уходит в default и снимается + * (type = −1), модификатор остаётся нулём. То же лечит и меч: там + * animate_sword делал `--mod[tp]` из нуля и получал 255. */ + pop_level_set_tile(g_room, tilepos, TILE_FLOOR); + /* curr_room_modif[curr_tilepos] = 0 (seg006:1676): у зелья в модификаторе + * лежал тип+фаза пузырька, у меча — счётчик блеска. Тайл стал полом — + * мусор в модификаторе не нужен (в исходное его вернёт только рестарт). */ + { + uint8_t *m = pop_trob_modif(g_room); + if (m) m[tilepos] = 0; + } + pop_item_taken = (uint8_t)(tilepos + 1); +} + +/* proc_get_object (seg006:16CB): эффект поднятого предмета. Зовётся из + * play_seq по опкоду SEQ_GET_ITEM с аргументом 1 (pop_kid). + * + * ПЕРВАЯ СТРОКА ОРИГИНАЛА — `if (Char.charid != charid_0_kid || ...) return`: + * предмет ЗАБИРАЕТ любой персонаж (тайл стирает do_pickup), а ЭФФЕКТ + * достаётся только Киду. На этом стоит всё спецсобытие уровня 5: тень + * выпивает зелье, оно исчезает — и не достаётся никому. Без проверки + * hitp_delta уходил бы Киду, то есть тень его лечила бы (найдено при + * портировании L5-SHADOW 2026-08-11, до живого прогона). */ +void pop_proc_get_object(void) __banked +{ + if (Char.charid != CHARID_0_KID) return; + if (pickup_obj_type == -1) { + pop_have_sword = 1; /* в оригинале have_sword = -1 */ + pop_sfx_play(37); /* победный трек «меч найден» */ + /* Вспышка фона: flash_color = color_14_brightyellow, flash_time = 8 + * (seg006 proc_get_object). do_flash = set_bg_attr(0, color) — + * оригинал подменяет НУЛЕВУЮ запись палитры, т.е. вспыхивает всё + * чёрное поле экрана. У нас так же (gfx_pal_set 0), см. SprPoP. */ + pop_flash_time = 8; + } + else { + /* proc_get_object (seg006:1927), ветка зелий. Типы 3/4/6 + * (перо/переворот/открыть все ворота) — свойства поздних уровней, + * их портируем вместе с этими уровнями. */ + switch (pickup_obj_type) { + case 1: /* здоровье: +1 HP, красная вспышка */ + if (hitp_curr != hitp_max) { + pop_sfx_stop(); /* seg006:1871 — stop_sounds */ + pop_sfx_play(33); + hitp_delta = 1; + pop_flash_color = POP_FLASH_RED; + pop_flash_time = 2; + } + break; + case 2: /* большая склянка: +1 к ПОТОЛКУ HP */ + pop_sfx_stop(); /* seg006:1878 — stop_sounds */ + pop_sfx_play(30); + pop_kid_add_life(); + pop_flash_color = POP_FLASH_RED; + pop_flash_time = 4; /* seg006:1878 — вспышка длиннее */ + break; + case 3: /* ПЕРО: медленное падение */ + /* feather_fall (seg000:15F8): взвести эффект + ЗЕЛЁНАЯ вспышка + * на 3 кадра (flash_color = 2). Оригинал здесь же глушит звуки + * и играет sound_39_low_weight — по нему же меряет срок; у нас + * срок задан кадрами (POP_FEATHER_FRAMES). */ + pop_feather = POP_FEATHER_FRAMES; + pop_sfx_stop(); + pop_sfx_play(39); /* «низкий вес» — весь эффект */ + pop_flash_color = POP_FLASH_GREEN; + pop_flash_time = 3; + break; + case 4: /* ПЕРЕВОРОТ: картинка вверх ногами */ + /* toggle_upside (seg000:15E9): ВЕСЬ эффект — инверсия флага и + * пометка «перерисовать, потому что перевернулись». Ни вспышки, + * ни звука, ни урона у этой ветки нет (seg006:1885). Сам + * переворот уже нарисованного делает приложение + * (pop_flip_screen, sprpop_cold.c). */ + pop_upside_want = (uint8_t)!pop_upside_want; + pop_upside_dirty = 1; + break; + case 5: /* «злое» зелье (синее): −1 HP */ + /* seg006:1892. Своей вспышки у этой ветки НЕТ (в отличие от + * лечебных): красный кадр даёт общий механизм «Кид ранен» + * (flash_if_hurt, seg003:0AFC) — у нас pop_kid_hurt. Раньше мы + * ставили вспышку руками, то есть красили экран дважды. + * Level 15/copy protection отсутствует, поэтому синие склянки + * всегда забирают ровно одну единицу здоровья. */ + pop_sfx_stop(); /* seg006:1893 — stop_sounds */ + pop_sfx_play(13); + hitp_delta = -1; + break; + default: /* 6 «открыть все ворота» — TODO + * вместе с его уровнем (13) */ + break; + } + } + pickup_obj_type = 0; +} + +/* check_get_item + get_item (seg005:061F/073E). Возврат: 0 — предмета нет + * (управление идёт обычной веткой); 1 — присесть (caller зовёт crouch()); + * 2 — поднять меч (caller ставит seq_91). */ +uint8_t pop_get_item_action(void) __banked +{ + uint8_t t; + int8_t icol; + t = get_tile_at_char(); + if (t == TILE_POTION || t == TILE_SWORD) { /* стоим НА предмете */ + if (!tile_is_floor(get_tile_behind_char())) return 0; + Char.x = (uint8_t)char_dx_forward(-TILE_SIZEX); /* отступить на тайл */ + determine_col(); + } + t = get_tile_infrontof_char(); /* заодно ставит g_infrontx */ + if (t != TILE_POTION && t != TILE_SWORD) return 0; + icol = g_infrontx; + if (icol < 0 || icol > 9) return 0; /* предмет за швом — не наш */ + if (Char.frame != FRAME_109_CROUCH) { + /* Ещё стоим: подровняться к предмету и присесть. */ + int d = pop_edge_distance(); /* ставит g_edge_type */ + if (g_edge_type != EDGE_FLOOR) + Char.x = (uint8_t)char_dx_forward((int8_t)d); + if (Char.direction >= 0) + Char.x = (uint8_t)char_dx_forward((int8_t)((t == TILE_POTION) - 2)); + return 1; + } + { + uint8_t tp = (uint8_t)(Char.curr_row * 10 + icol); + if (t == TILE_SWORD) { + do_pickup(-1, tp); + return 2; /* поднять меч (seq_91) */ + } + /* Зелье: тип лежит в СТАРШИХ битах модификатора (get_item, + * seg005: do_pickup(curr_room_modif[curr_tilepos] >> 3)). */ + do_pickup((int8_t)(pop_trob_modif(g_room)[tp] >> 3), tp); + return 3; /* пить (seq_78) */ + } +} + +/* Спецсобытие уровня 12 «появление плит» (seg006:1063, ветка внутри + * check_on_floor). После слияния с тенью (pop_united_shadow == −1) в + * верхнем ряду комнат 2 и 13 пропасти становятся проходимыми: персонаж, + * который ДОЛЖЕН БЫЛ провалиться, вместо этого достраивает под собой пол. + * Плита появляется ровно под ним, шаг за шагом — это не «включили этаж», а + * дорожка, возникающая под ногами. + * + * Возврат 1 = «пол появился, падать не надо». Перерисовываем сам тайл и + * соседа справа на ОБЕИХ страницах — тем же приёмом, что pop_skel_wake_tile + * (у оригинала это пара set_redraw_full/set_wipe на curr_tilepos и +1). + * + * Комната берётся РЕЗОЛВНУТОЙ (tile_room_of): персонаж может стоять в шве, + * и тогда пол обязан появиться в соседней комнате, как и у оригинала — + * там get_tile переставляет curr_room/curr_tilepos до присваивания. */ +#define HIDDEN_FLOOR_ROOM_A 2 +#define HIDDEN_FLOOR_ROOM_B 13 +#define HIDDEN_FLOOR_COL_B 6 /* в комнате 13 — только правая половина */ + +static uint8_t floors_appear(void) +{ + uint8_t room, tp; + if (pop_current_level != SHADOW_FIGHT_LEVEL) return 0; + if (pop_united_shadow >= 0) return 0; /* только ПОСЛЕ слияния */ + if (Char.curr_row != 0) return 0; + if (Char.room != HIDDEN_FLOOR_ROOM_A && + !(Char.room == HIDDEN_FLOOR_ROOM_B && Char.curr_col >= HIDDEN_FLOOR_COL_B)) + return 0; + room = tile_room_of(Char.curr_col, Char.curr_row, &tp); + if (!room) return 0; + pop_level_set_tile(room, tp, TILE_FLOOR); + if (room == g_room) { + g_fg[tp] = TILE_FLOOR; + pop_set_redraw(tp, POP_RD_FLOOR, 2); + if ((tp % 10) < 9) + pop_set_redraw((uint8_t)(tp + 1), POP_RD_FLOOR, 2); + } + return 1; +} + +/* ---- Диспетчер ----------------------------------------------------- */ +static void check_on_floor(void) +{ + if (kid_cur_flags() & FRAME_NEEDS_FLOOR) { + if (get_tile_at_char() == TILE_WALL) + in_wall(); /* забежал в стену -> выровнять */ + if (!tile_is_floor(g_curr_tile) && !floors_appear()) + start_fall(); + } +} + +static void check_action(void) +{ + uint8_t action = Char.action, frame = Char.frame; + /* Зацеп прямо в прыжке (улучшение SDLPoP, seg006:0914) — ДО всего + * остального: если получилось, кадр уже сменён на вис. */ + if (POP_ENABLE_JUMP_GRAB && action == ACT_RUNJUMP && + pop_ctrl_shift_held() && check_grab_run_jump()) + return; + if (action == ACT_BUMPED || action == ACT_HANGSTRAIGHT) { + /* ТОЛЬКО кадр 109 (присед) — как в ванильном seg006:909. Кадры + * вставания 110..119 сюда добавляет ОПЦИОНАЛЬНЫЙ FIX_STAND_ON_THIN_AIR, + * и он состоит из ТРЁХ частей: (1) эта ветка, (2) отмена падения, + * если в 2 px позади ещё пол (seg006:1079), (3) ПРАВКА seqtbl — + * dx(1)→dx(0) и dx(−4)→dx(−3) в standup. У нас seqtbl извлечён из + * данных оригинала, третью часть не применить, а без неё вставание + * уносит весовую точку на 4 px вперёд — в стену. Именно половинчатый + * порт этого фикса ронял Кида сквозь пол: комната 5, приземление с + * уроном на щебень (2,7) у стены (2,8), вставание → колонка 8 = стена + * → start_fall → выпадение в комнату 6 (BUG-STANDUP-1). Ванильное + * поведение (кадр 109) от этого свободно; цена — «встать на воздухе» + * у самой кромки, ровно как в оригинале 1989 года. */ + if (frame == 109) + check_on_floor(); + } else if (action == ACT_FREEFALL) { + do_fall(); + } else if (action == ACT_MIDAIR) { + /* Кадры 102..105 — НАЧАЛО падения (seg006:0619). Здесь fall_y ещё + * не разгоняется (fall_accel работает только в ACT_FREEFALL), то есть + * это САМОЕ ШИРОКОЕ окно зацепа: ветка стояла заглушкой, и первые + * четыре кадра падения зацеп не работал вовсе — отсюда «иногда + * цепляется, иногда нет» на входе в уровень 7. */ + if (frame >= 102 && frame < 106) + check_grab(); + } else if (action != ACT_HANGCLIMB) { + check_on_floor(); + } +} + +/* bumped_fall (seg004:0522): удар, когда под персонажем НЕ пол ИЛИ он высоко + * над полом — отскок назад и падение (seq_45). В свободном падении сиквенс + * не меняем, только гасим горизонтальную составляющую. */ +static void bumped_fall(void) +{ + Char.x = (uint8_t)char_dx_forward(-4); + if (Char.action == ACT_FREEFALL) { + Char.fall_x = 0; + } else { + pop_char_set_seq(SEQ_45_BUMPFALL); + play_seq(); + } + /* bumped_sound (seg004:05F1) — ВНЕ ветвления, как в оригинале: удар о + * стену слышен и в свободном падении. Флаг «Кид нашумел» взводится + * вместе со звуком — см. BUG-GUARD-DEAF-1. */ + pop_sfx_play(8); + is_guard_notice = 1; +} + +/* bumped_floor (seg004:053B): удар о стену, когда под персонажем ПОЛ. + * Портируется целиком — раньше у нас был только хвост (seq_47), из-за чего + * терялись ДВЕ вещи: прижатие Y к полу и «жёсткий» вариант удара. Симптом + * (комната 5, прыжок в решётку): standjump бампится на кадре 25 — том самом, + * где сиквенс уже сделал dy(−6), а парного dy(+6) ещё не было, — и Kid + * оставался стоять на 6 пикселей ВЫШЕ пола, без приземления-приседания. */ +static void bumped_floor(void) +{ + uint8_t frame; + /* Оригинал сравнивает БЕЗЗНАКОВО: если персонаж НИЖЕ уровня пола, + * разность заворачивается в большое число и тоже даёт «падать». */ + if ((uint16_t)(pop_y_land[Char.curr_row + 1] - (int16_t)Char.y) >= 15) { + bumped_fall(); /* высоко над полом — в воздухе */ + return; + } + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; /* ПРИЖАТЬ к полу */ + if (Char.fall_y >= 22) { /* влетел быстро — только отжать */ + Char.x = (uint8_t)char_dx_forward(-5); + return; + } + Char.fall_y = 0; + /* `if (Char.alive)` (seg004:0520) — у ЖИВОГО (alive = −1) сиквенс удара + * ставится ВСЕГДА, даже если персонаж уже в отскоке. Раньше здесь стоял + * наш guard `if (Char.action == ACT_BUMPED) return;` («не рестартить») — и + * он был корнем BUG-STANDUP-1: + * 1) вставание из приседа (medland) двигает Кида на 1 px в стену — + * срабатывает бамп; + * 2) bumped_floor ПРИЖИМАЕТ y к полу (181), отменяя dy(−2), который + * medland сделал в начале; + * 3) наш guard возвращает управление, medland продолжается и делает + * свои dy(+1), dy(+1) — теперь уже ОТ уровня пола, то есть Кид + * оказывается НИЖЕ пола (y = 182); + * 4) следующий бамп видит «персонаж ниже пола» (беззнаковая разность + * заворачивается) и уводит его в bumped_fall → seq_45 → выпадение + * в комнату снизу. + * У оригинала шага (3) нет: seq_47 обрывает medland, и лишних dy не + * случается. */ + if (!Char.alive) return; /* труп — только выравнивание */ + frame = Char.frame; + /* Кадры прыжков и начала падения — «жёсткий» удар с приседанием + * (24/25 = standjump, 40..42 = runjump, 102..106 = start_fall). */ + if (frame == 24 || frame == 25 || (frame >= 40 && frame < 43) || + (frame >= 102 && frame < 107)) + pop_char_set_seq(SEQ_46_HARDBUMP); + else + pop_char_set_seq(SEQ_47_BUMP); + play_seq(); + pop_sfx_play(8); /* bumped_sound (seg004:05F1) */ + is_guard_notice = 1; +} + +/* bump_into_opponent (seg003:08AA). Зовётся из play_kid_frame (seg000:1238) + * между set_char_collision и check_collisions, и ТОЛЬКО у Кида — у стража + * этой ветки нет (см. комментарий к guard_phys ниже). + * + * ЗАЧЕМ. Это единственная защита оригинала от «вошёл в комнату — сразу + * труп». Стража при входе никуда не отбрасывает: enter_guard (seg002:0112) + * ставит его ровно в его тайл из данных уровня, и Кид вполне может войти + * вплотную. Спасает то, что БЕЗОРУЖНЫЙ Кид, столкнувшись с вооружённым + * соперником лицом к лицу, не получает укол, а ОТСКАКИВАЕТ (seq_47). + * Остальные три страховки у нас уже есть: Кид сам достаёт меч + * (control_standing, pop_ctrl.c), страж поднимается в стойке покоя с мечом + * в ножнах (pop_guard_cold.c) и не колет ближе 12 (guards.c). + * + * Расхождений с оригиналом нет. Опциональные фиксы SDLPoP + * (fix_painless_fall_on_guard — падение на стража с высоты перестаёт быть + * безболезненным; fix_jumping_over_guard — нельзя перепрыгнуть) намеренно НЕ + * портированы: базовое поведение движка 1989 года. */ +static void bump_into_opponent(void) +{ + int16_t distance; + if (can_guard_see_kid < 2) return; + if (Char.sword != 0) return; /* Кид уже в боевой стойке */ + if (Opp.sword == 0) return; /* у соперника меч в ножнах */ + if (Opp.action >= 2) return; /* только stand / run_jump */ + if (Char.direction == Opp.direction) return; /* должны быть лицом к лицу */ + distance = pop_char_opp_dist(); + if (distance < 0) distance = -distance; + if (distance > 15) return; + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; + Char.fall_y = 0; + pop_sfx_play(16); /* seg003:0654 — толчок о стража */ + pop_char_set_seq(SEQ_47_BUMP); + play_seq(); +} + +/* ---- check_collisions (seg004:0004) -------------------------------- * + * ПОЛНЫЙ порт: флаги перекрытия считаются для ВСЕХ колонок ряда, а колонка + * удара выбирается обходом 9→0 по переходу флага 0→1. + * + * ПОЧЕМУ НЕ «колонка переднего края» (как было до 2026-08-03, BUG-COLL-1): + * колонка края попадает на стену лишь пока край внутри её габаритного окна, + * а окно узкое. Для решётки в col9 (x_bump[14]=184, coll_left=191, левая + * грань 191+10=201) это ровно 201..204 — четыре пикселя. Беговой кадр + * сдвигает край больше чем на четыре, край перескакивает с ≤200 сразу на + * ≥205 (это уже колонка 10, соседняя комната, не стена) — бампа нет, а + * check_leave тут же видит char_x_right ≥ 201 и уводит Кида СКВОЗЬ + * ЗАКРЫТУЮ РЕШЁТКУ. Флаг колонки 9 в оригинале взводится при любом + * перелёте, потому что сравнивается не колонка края, а КООРДИНАТЫ: + * 0x0F = left_wall_xpos < char_x_right_coll («в левую грань стены») + * 0xF0 = right_wall_xpos > char_x_left_coll («в правую грань») + * Тем же корнем лечится влёт в стену на длинном прыжке (комн. 6) и провал + * сквозь пол после приземления у стены (комн. 5). + * + * ТРИ РЯДА — как в оригинале (было: один, и это оказалось BUG-JUMPWALL-1). + * Оригинал каждый кадр считает флаги для рядов row−1, row, row+1, а + * move_coll_to_prev (seg004:00DF) в начале СЛЕДУЮЩЕГО кадра выбирает из них + * тот, что соответствует ряду прошлого кадра. Так «прошлые» флаги остаются + * настоящими и на кадре смены ряда. + * + * Наш прежний упрощённый вариант (prev = 3 = «уже перекрывал») подавлял бамп + * ровно на этом кадре — а в падении ряд меняется чуть ли не каждый кадр, и + * переход 0→1 на стене приходится как раз на него. Симптом: недолетевший + * прыжок уходит СКВОЗЬ кладку по свободной траектории (ур. 3 комн. 14). + * Цена трёх рядов — 3×14 get_tile на кадр вместо 14; замерено, что это + * дешевле любого из обходных вариантов, а поведение совпадает с оригиналом. + * + * Ряды вне комнаты (−1 и 3) считаются штатно: get_tile сам резолвит ряд −1 + * через комнату сверху (g_above), а ряд 3 отдаёт WALL — то же «через край + * не бампим», что и раньше. */ +/* Колонки считаем от −2 до 11, а не 0..9: колонки за швом — это тайлы + * СОСЕДНЕЙ комнаты (get_tile резолвит их через g_lcol/g_rcol), и решётка + * соседа обязана бампить так же, как своя (иначе Kid уходит сквозь + * закрытые ворота шва — старый BUG-SEAM-PINGPONG). Оригинал делает то же + * самое: get_row_collision_data идёт от col(char_x_left)−1 до + * col(char_x_right)+2 и резолвит комнату через find_room_of_tile, а + * координата грани (coll_tile_left_xpos) остаётся в системе ОТРИСОВАННОЙ + * комнаты — как у нас pop_x_bump[col + FIRST_ONSCREEN_COLUMN]. */ +#define COLL_C0 (-2) /* первая считаемая колонка */ +#define COLL_N 14 /* колонки −2..11 */ +#define COLL_IDX(c) ((c) - COLL_C0) +#define COLL_ROW_NONE ((int8_t)-128) +static uint8_t coll_prev[COLL_N], coll_curr[COLL_N]; +static uint8_t coll_above[COLL_N], coll_below[COLL_N]; +static int8_t coll_prev_row = COLL_ROW_NONE; /* ряд, к которому относится coll_prev */ +/* ОКНО перебора: какие слоты реально посчитаны. Оригинал (seg004:0047) тоже + * считает не всю комнату, а полосу вокруг персонажа — left_checked_col / + * right_checked_col; прочие слоты у него помечены «комнаты нет» + * (row_coll_room_ptr = −1) и в цикл бампа не попадают. + * + * Раньше мы считали все четырнадцать колонок, чтобы не заводить признак + * годности слота. Замер по сгенерированному асму показал, чего это стоит: + * ~940 тактов на колонку (из них 574 — get_tile), 3 ряда × 14 = ~39 600 за + * кадр у Кида плюс ~13 200 у стража (check_chomped_guard) — вместе ~12 % + * кадрового периода. Окно из 4–5 колонок режет это втрое. + * + * Признак годности у нас дешевле оригинального: не массив номеров комнат, + * который надо чистить каждый кадр, а ГРАНИЦЫ окна — четыре байта. Слот + * годен, если лежит в окне; сравнивать prev с curr можно только на + * ПЕРЕСЕЧЕНИИ двух окон. Очистки массивов нет вовсе. */ +static int8_t coll_lo, coll_hi; /* окно coll_curr/above/below */ +static int8_t coll_prev_lo, coll_prev_hi; /* окно coll_prev */ +static int8_t win_lo, win_hi; /* результат calc_coll_window */ +/* «нет колонки»: НЕ −1, потому что −1/−2 — законные колонки шва. */ +#define BUMP_NONE ((int8_t)-128) +static int8_t bump_col_l; /* bump_col_left_of_wall (Kid лицом ВПРАВО) */ +static int8_t bump_col_r; /* bump_col_right_of_wall (Kid лицом ВЛЕВО) */ +static int coll_xl, coll_xr; /* char_x_left_coll / char_x_right_coll */ + +/* Ряд, к которому относилась история ДО последней инвалидации — чтобы + * pop_coll_shift мог её восстановить (см. ниже). */ +static int8_t coll_last_row = COLL_ROW_NONE; + +/* Смена комнаты: колонки перепроецированы, прошлые флаги недействительны. + * В оригинале это несовпадение prev_coll_room и curr_row_coll_room — + * на кадре входа бампа нет. Безопасный дефолт для ЛЮБОГО входа в комнату + * (старт уровня, респавн, чит-навигация, переход вверх/вниз); боковой + * переход тут же отменяет его через pop_coll_shift. */ +static void pop_coll_invalidate(void) +{ + coll_last_row = coll_prev_row; + coll_prev_row = COLL_ROW_NONE; +} + +/* БОКОВОЙ переход: историю не выбрасываем, а СДВИГАЕМ (BUG-GATE-PASS-1). + * + * Оригинал (seg004:0004) индексирует флаги колонкой ВНУТРИ разрешённой + * комнаты и хранит рядом номер этой комнаты (row_coll_room_ptr), сравнивая + * prev/curr только при совпадении номеров. Решётка комнаты 8 и до перехода + * 8->6, и после лежит в слоте 9 с room = 8 — история переживает переход, + * переход флага 0->1 виден, bumped() срабатывает. У нас индекс — колонка + * ОТРИСОВАННОЙ комнаты, поэтому тот же тайл менял слот, история терялась, и + * Кид с разбега проходил сквозь закрытые ворота шва. + * + * Физически колонки сдвигаются ровно на 10 (check_leave двигает x на ∓140 = + * 10 тайлов по 14 px), и координата грани (pop_x_bump) сдвигается на те же + * 140 вместе с габаритом Кида — значит САМИ ФЛАГИ инвариантны, меняется + * только их номер слота. Отсюда весь фикс — перенумерация. + * + * Сдвигаем curr/above/below, а не prev: prev на следующем кадре всё равно + * перезапишет move_coll_to_prev, выбирая источник как раз из этих трёх. + * Освободившиеся слоты — 3 («уже перекрывал»), то есть бампа по ним нет: + * ровно то, что в оригинале даёт несовпадение номера комнаты. + * + * dcol — насколько изменились НОМЕРА колонок: уход вправо −10, влево +10. + * Отличия от дословного порта варианта A — applications/PoP/docs/impl_diff.md. */ +static void coll_shift_one(uint8_t *a, int8_t dcol) +{ + int8_t i, j; + if (dcol < 0) { /* источник правее — идём вперёд */ + for (i = 0; i < COLL_N; i++) { + j = (int8_t)(i - dcol); + a[i] = (j < COLL_N) ? a[j] : 3; + } + } else { /* источник левее — идём назад */ + for (i = COLL_N - 1; i >= 0; --i) { + j = (int8_t)(i - dcol); + a[i] = (j >= 0) ? a[j] : 3; + } + } +} + +void pop_coll_shift(int8_t dcol) __banked +{ + if (coll_last_row == COLL_ROW_NONE) return; /* истории и не было */ + coll_prev_row = coll_last_row; /* отменить инвалидацию */ + coll_shift_one(coll_curr, dcol); + coll_shift_one(coll_above, dcol); + coll_shift_one(coll_below, dcol); + /* окно посчитанных слотов едет вместе с ними; выйти за [COLL_C0..] оно + * может свободно — пересечение с окном следующего кадра само обрежет. */ + coll_lo = (int8_t)(coll_lo + dcol); + coll_hi = (int8_t)(coll_hi + dcol); +} + +/* set_char_collision (seg006:0723), горизонтальная часть: габарит персонажа + * в комнатных координатах. char_x_left = obj_x/2 + 58 (минус полуширина при + * взгляде вправо) — это ровно наш char_x_forward_edge для переднего края. */ +static void set_char_collision(void) +{ + int e = char_x_forward_edge(); + int wh = ((int)CD_ACT->fpw + 1) >> 1; /* char_width_half */ + if (Char.direction < 0) { coll_xl = e; coll_xr = e + wh; } + else { coll_xr = e; coll_xl = e - wh; } + if (kid_cur_flags() & FRAME_THIN) { coll_xl += 4; coll_xr -= 4; } +} + +/* move_coll_to_prev (seg004:00DF): в prev кладём флаги ряда, в котором + * персонаж был НА ПРОШЛОМ кадре. Массивы curr/above/below в этот момент + * ещё хранят прошлый кадр, поэтому нужный ряд просто выбирается из них по + * разнице рядов. Условия — дословно оригинальные, включая ±3: ряд может + * «завернуться» при смене комнаты (2 -> 0 вниз, 0 -> 2 вверх), и тогда + * геометрия колонок та же, что у текущего ряда. + * + * Первый кадр (и кадр после смены комнаты, pop_coll_invalidate) прошлого + * ряда не имеет: prev = «уже перекрывал всё», бампа на нём нет. */ +static void move_coll_to_prev(int8_t row) +{ + const uint8_t *src; + /* Окно prev — то, по которому считались curr/above/below (у всех трёх оно + * одно). Забираем ДО того, как check_collisions пересчитает своё. */ + coll_prev_lo = coll_lo; coll_prev_hi = coll_hi; + if (coll_prev_row == COLL_ROW_NONE) { + memset(coll_prev, 3, COLL_N); + coll_prev_lo = COLL_C0; coll_prev_hi = COLL_C0 + COLL_N - 1; + return; + } + if (row == coll_prev_row || row + 3 == coll_prev_row || row - 3 == coll_prev_row) + src = coll_curr; /* ряд не менялся */ + else if (row + 1 == coll_prev_row || row - 2 == coll_prev_row) + src = coll_above; /* поднялся на ряд */ + else + src = coll_below; /* опустился на ряд */ + /* memcpy, а не цикл: SDCC пересчитывал адрес назначения на КАЖДОЙ + * итерации через слот кадра IX — 14 байт обходились в 5 514 тактов. */ + memcpy(coll_prev, src, COLL_N); +} + + +/* Окно перебора (seg004:0047). Считать ПОСЛЕ set_char_collision — берётся из + * coll_xl/coll_xr. Клампы: справа как в оригинале (не дальше последней + * колонки), слева — по нашему COLL_C0 (оригиналу клампить нечего, у него + * индекс приходит уже разрешённым внутрь комнаты). */ +static void calc_coll_window(void) +{ + int8_t hi = (int8_t)(get_tile_div_mod_m7(coll_xr) + 2); + int8_t lo = (int8_t)(get_tile_div_mod_m7(coll_xl) - 1); + if (hi > COLL_C0 + COLL_N - 1) hi = COLL_C0 + COLL_N - 1; + if (lo < COLL_C0) lo = COLL_C0; + if (hi < lo) hi = lo; + win_lo = lo; win_hi = hi; +} + +/* Тело перебора: src — тайлы ряда ПОДРЯД, dst — куда класть флаги. Всё + * остальное вынесено в статики намеренно: у SDCC z80 каждый лишний + * параметр/локал уезжает в кадр IX, а `ld -n(ix)` стоит 19 тактов номинала + * (у нас с wait-state'ами ~46). + * + * Замер одной итерации в MAME: пустая колонка 750 тактов, колонка-стена + * ~1 700 (там две 16-битные знаковые сверки граней). */ +static uint8_t scan_n; /* сколько колонок осталось */ +static uint8_t scan_left; /* левая грань текущей колонки */ + +/* ПОРОГИ СРАВНЕНИЯ, предпосчитанные на кадр (по типу стены 1..5). + * + * В теле цикла стояло `wall_dl[wt] + scan_left < coll_xr` и + * `scan_left - wall_dr[wt] + TILE_RIGHTX > coll_xl` — четыре 16-битных + * операции на КАЖДУЮ колонку, при том что от колонки зависит ровно один + * операнд (scan_left). Переносим всё остальное в порог: + * + * scan_left < coll_xr - wall_dl[wt] = thr_l[wt] + * scan_left > coll_xl + wall_dr[wt] - TILE_RIGHTX = thr_r[wt] + * + * Оригинал считает это в лоб (get_left_wall_xpos / get_right_wall_xpos, + * seg004:0226) — он писался под 386, где 16-битная арифметика бесплатна. + * + * ПОЧЕМУ 8 БИТ КОРРЕКТНЫ (доказательство относится и к границам, и к + * слагаемым в самом цикле). scan_left = x_bump[col + 5] + TILE_MIDX, а + * колонка окна лежит в COLL_C0 .. COLL_C0+COLL_N-1, то есть −2..11: + * значит scan_left ∈ [x_bump[3]+7, x_bump[16]+7] = [37, 219], и после + * последней колонки максимум 233. Ни одного выхода за uint8_t. + * Сами пороги считаются в int и КЛИПУЮТСЯ к 0..255 — это не приближение, + * а точное сохранение результата: при пороге ниже 37 условие «меньше» не + * выполнится никогда (клип к 0 даёт то же), при пороге выше 219 оно + * выполнится всегда (клип к 255 даёт то же), и симметрично для «больше». */ +static uint8_t coll_xr8, coll_xl8; + +static uint8_t clip_thr(int v) +{ + if (v < 0) return 0; + if (v > 255) return 255; + return (uint8_t)v; +} + +/* Две границы персонажа, приведённые к 8 битам. ТАБЛИЦЫ ПОРОГОВ ПО ТИПУ + * СТЕНЫ ЗДЕСЬ БЫЛА И ОКАЗАЛАСЬ ХУЖЕ: предпосчёт пяти пар порогов стоил + * дороже, чем экономил, потому что колонок в окне всего четыре-пять, а + * типов стен пять (замер 2026-08-19: check_collisions 33 846 -> 36 570, + * синяя фаза +10 269). Классическая ошибка — кэшировать больше, чем + * потребляешь. */ +static void coll_thresholds(void) +{ + coll_xr8 = clip_thr(coll_xr); + coll_xl8 = clip_thr(coll_xl); +} + +static uint8_t *scan_dst; + +static void coll_scan(const uint8_t *src) +{ + do { + uint8_t wt = wall_type_tbl[*src++ & 0x1F]; + uint8_t f = 0; + if (wt) { + /* ВСЯ арифметика в 8 битах — см. доказательство диапазонов + * над coll_thresholds: scan_left ∈ [37, 233], wall_dl ∈ [−1, 10], + * wall_dr ∈ [0, 13], значит обе суммы лежат в [36, 246] и + * переполниться не могут. */ + if ((uint8_t)(scan_left + wall_dl[wt]) < coll_xr8) f = 1; + if ((uint8_t)(scan_left - wall_dr[wt] + TILE_RIGHTX) > coll_xl8) f |= 2; + } + *scan_dst++ = f; + scan_left += TILE_SIZEX; + } while (--scan_n); +} + +/* Всё, что НЕ зависит от ряда, считается один раз на кадр. Раньше это жило + * в теле ряда, и пролог одного ряда стоил тысячи тактов при том, что сам + * цикл по четырём-пяти колонкам — около трёх. */ +static uint8_t scan_n0; +static uint8_t scan_left0; +static uint8_t scan_off; /* индекс первого слота в flags[] */ + +static void coll_scan_prepare(void) +{ + scan_left0 = (uint8_t)(pop_x_bump[win_lo + FIRST_ONSCREEN_COLUMN] + TILE_MIDX); + scan_n0 = (uint8_t)(win_hi - win_lo + 1); + scan_off = (uint8_t)COLL_IDX(win_lo); + coll_thresholds(); /* здесь же, чтобы порог и окно всегда были от + * ОДНОГО персонажа — та же причина, по которой + * тут считается scan_left0 (см. BUG-GUARD-IX-1 + * в шапке get_row_collision_data). */ +} + +/* Базы ряда: указатели, которые индексируются НОМЕРОМ КОЛОНКИ (в том числе + * отрицательным). Ноль = «этой стороны нет» → стена. Ряд разрешается один + * раз на ряд, а не в каждом get_tile: тот на каждый вызов заново решал «ряд + * −1? за комнатой? слева/справа?» и считал row*10+col сдвигами (1 422 такта + * на вызов при 12-15 вызовах за кадр). */ +static const uint8_t *rb_own, *rb_lft, *rb_rgt; + +/* Ряд, целиком состоящий из стены — для стороны, которой нет (край уровня, + * ряд вне комнаты). Дешевле, чем ветка «а тут стена» внутри цикла. */ +static const uint8_t wall_row[COLL_N] = { + TILE_WALL,TILE_WALL,TILE_WALL,TILE_WALL,TILE_WALL,TILE_WALL,TILE_WALL, + TILE_WALL,TILE_WALL,TILE_WALL,TILE_WALL,TILE_WALL,TILE_WALL,TILE_WALL +}; + +/* get_row_collision_data (seg004:0185) для одного ряда: бит0 = «персонаж + * достал левую грань стены колонки», бит1 = «правую». Считаем ОКНО + * win_lo..win_hi (см. calc_coll_window), слоты вне окна не трогаем — их + * годность определяют границы окна, а не содержимое. + * + * Окно режется на НЕПРЕРЫВНЫЕ пробеги: комната слева (колонки < 0), своя + * (0..9), справа (> 9). По каждому идёт тот же coll_scan с шагающим + * указателем, поэтому вопрос «а эта колонка чья?» решается по одному разу + * на пробег, а не на колонку. Первая версия собирала тайлы окна во + * временный буфер тернарником на колонку — 1 340 тактов на колонку, вдвое + * дороже самого перебора; у Кида, стоящего в колонке 0, окно начинается с + * −1, так что это был не редкий, а ОБЫЧНЫЙ путь. + * + * scan_left между пробегами не сбрасывается: coll_scan сам двигает его на + * тайл за колонку — ровно как оригинал (coll_tile_left_xpos += TILE_SIZEX). */ +static void coll_row(uint8_t *flags) +{ + int8_t lo = win_lo, last; + uint8_t n; + uint8_t *dst = flags + scan_off; + scan_left = scan_left0; /* coll_scan сам двигает его дальше */ + if (lo < 0) { /* комната СЛЕВА */ + last = win_hi < -1 ? win_hi : -1; + n = (uint8_t)(last - lo + 1); + scan_n = n; /* coll_scan обнулит */ + scan_dst = dst; coll_scan(rb_lft ? rb_lft + lo : wall_row); + dst += n; + lo = (int8_t)(last + 1); + } + if (lo <= win_hi && lo <= 9) { /* СВОЯ комната */ + last = win_hi < 9 ? win_hi : 9; + n = (uint8_t)(last - lo + 1); + scan_n = n; + scan_dst = dst; coll_scan(rb_own ? rb_own + lo : wall_row); + dst += n; + lo = (int8_t)(last + 1); + } + if (lo <= win_hi) { /* комната СПРАВА */ + scan_n = (uint8_t)(win_hi - lo + 1); + scan_dst = dst; coll_scan(rb_rgt ? rb_rgt + lo : wall_row); + } +} + +/* Разрешить ряд «с нуля» — для одиночного вызова (страж, check_chomped_guard). + * В check_collisions три ряда получаются сдвигом баз на ±10 без пересчёта. */ +static void coll_row_bases(int8_t row) +{ + rb_own = rb_lft = rb_rgt = 0; + if (row >= 0 && row <= 2) { + int ro = (int)row * 10; + rb_own = g_fg + ro; + if (g_link_l) rb_lft = g_lcol + ro + 10; + if (g_link_r) rb_rgt = g_rcol + ro - 10; + } else if (row == -1 && g_above && g_link_u) { + rb_own = g_above; /* ряд 2 комнаты сверху */ + } +} + +/* Одиночный ряд во ВНЕШНИЙ массив (единственный вызывающий — + * check_chomped_guard). + * + * coll_scan_prepare() тут ОБЯЗАТЕЛЕН, и это не перестраховка: coll_row + * пишет по `flags + scan_off`, а число байт берёт из win_lo/win_hi. Без + * prepare scan_off/scan_left0 остаются от ПРОШЛОГО окна (его считает + * check_collisions для Кида), а окно тут уже своё, стражево. Когда + * стражево окно шире или правее кидова, запись уезжает за границу + * flags[COLL_N] — а сразу за ней в кадре check_chomped_guard лежит + * СОХРАНЁННЫЙ IX. Затёртый младший байт превращал кадровый указатель + * main (0xBFFA) в 0xBF00, и дальше main читал свои локали из чужого + * стекового мусора: игра «зависала», потому что отладочный `frozen` + * становился ненулевым (BUG-GUARD-IX-1). Заодно чинится и сам расчёт: + * scan_left0 задаёт x колонок, и страж считался по координатам Кида. */ +static void get_row_collision_data(int8_t row, uint8_t *flags) +{ + coll_scan_prepare(); + coll_row_bases(row); + coll_row(flags); +} + +static void check_collisions(void) +{ + int8_t col; + bump_col_l = bump_col_r = BUMP_NONE; + /* seg004:0044 — на кадрах разворота коллизии не считаются вовсе. */ + if (Char.action == ACT_TURN) return; + /* Вне рядов комнаты (падение мимо пола) get_tile отдаёт WALL во всех + * колонках — считать по нему нечего. Стену в падении держит + * do_fall/in_wall, выход из комнаты — check_leave_below. */ + if (Char.curr_row < 0 || Char.curr_row > 2) { + /* И coll_last_row тоже: curr/above/below остались от старого кадра, + * сдвигать их при боковом переходе нечего (поведение как раньше). */ + coll_prev_row = coll_last_row = COLL_ROW_NONE; + return; + } + + pop_dbg_p2(); /* ЗАМЕР: вход в тело */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + set_char_collision(); + pop_dbg_p3(); /* ЗАМЕР: set_char_collision */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + move_coll_to_prev(Char.curr_row); /* заодно снимет окно prev */ + coll_prev_row = Char.curr_row; + calc_coll_window(); + coll_lo = win_lo; coll_hi = win_hi; + coll_scan_prepare(); + pop_dbg_p4(); /* ЗАМЕР: окно посчитано */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* Порядок важен: prev уже забран, теперь три ряда пересчитываются + * НА ЭТОТ кадр (в оригинале ровно так же, seg004:0004). Ряд здесь + * гарантированно 0..2, поэтому соседние ряды — это те же базы ±10, без + * повторного разрешения. */ + { + int ro = (int)Char.curr_row * 10; + const uint8_t *own = g_fg + ro; + const uint8_t *lft = g_link_l ? g_lcol + ro + 10 : 0; + const uint8_t *rgt = g_link_r ? g_rcol + ro - 10 : 0; + + rb_own = own; rb_lft = lft; rb_rgt = rgt; + coll_row(coll_curr); + + if (Char.curr_row < 2) { /* ряд ниже */ + rb_own = own + 10; + rb_lft = lft ? lft + 10 : 0; + rb_rgt = rgt ? rgt + 10 : 0; + } else { + rb_own = rb_lft = rb_rgt = 0; /* ряд 3 — стена везде */ + } + coll_row(coll_below); + + if (Char.curr_row > 0) { /* ряд выше */ + rb_own = own - 10; + rb_lft = lft ? lft - 10 : 0; + rb_rgt = rgt ? rgt - 10 : 0; + } else if (g_above && g_link_u) { /* ряд −1 = ряд 2 сверху */ + rb_own = g_above; rb_lft = rb_rgt = 0; + } else { + rb_own = rb_lft = rb_rgt = 0; + } + coll_row(coll_above); + } + pop_dbg_p5(); /* ЗАМЕР: три ряда просканированы */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* Обход СВЕРХУ ВНИЗ, как в оригинале (9→0): побеждает МЛАДШАЯ колонка, + * в которой флаг перешёл 0→1. Только по ПЕРЕСЕЧЕНИЮ окон: вне его + * сравнивать нечего (у оригинала там prev_coll_room != curr_row_coll_room + * или curr_row_coll_room < 0 — тот же смысл). */ + { + int8_t lo = coll_lo > coll_prev_lo ? coll_lo : coll_prev_lo; + int8_t hi = coll_hi < coll_prev_hi ? coll_hi : coll_prev_hi; + for (col = hi; col >= lo; --col) { + uint8_t p = coll_prev[COLL_IDX(col)], c = coll_curr[COLL_IDX(col)]; + if (!(p & 1) && (c & 1)) bump_col_l = col; + if (!(p & 2) && (c & 2)) bump_col_r = col; + } + } +} + +/* is_obstacle (seg004:037E): препятствие ли тайл колонки на самом деле. + * Побочно оставляет тайл в g_curr_tile (как curr_tile2 у оригинала). */ +static uint8_t is_obstacle_at_col(int8_t col) +{ + uint8_t t = get_tile(col, Char.curr_row); + if (t == TILE_POTION) return 0; /* зелье не бампит */ + if (t == TILE_GATE) return (uint8_t)!gate_passable(col, Char.curr_row); + if (t == TILE_CHOMPER) /* закрыт только при modif==2 */ + return (uint8_t)(col >= 0 && col <= 9 && + pop_trob_modif(g_room)[Char.curr_row * 10 + col] == 2); + /* Прыжок СКВОЗЬ ЗЕРКАЛО (seg004:0239). Зеркало вообще-то «стена слева» + * (wall_type = 2), но для Кида в БЕГОВОМ ПРЫЖКЕ справа налево оно + * перестаёт быть препятствием: он пролетает насквозь, зеркало «бьётся» + * (modif 0x56) и взводится флаг рождения тени. Условия — все четыре, как + * в оригинале: только Кид, только кадры бегового прыжка 39..43, только + * направление влево. */ + if (t == TILE_MIRROR && Char.charid == CHARID_0_KID && + Char.frame >= 39 && Char.frame < 44 && Char.direction < 0 && + col >= 0 && col <= 9) { + pop_trob_modif(g_room)[Char.curr_row * 10 + col] = 0x56; + pop_jumped_mirror = -1; + return 0; + } + return 1; +} + +/* bumped (seg004:0448). delta_x — сдвиг персонажа В КООРДИНАТАХ (не + * «вперёд»), push_dir — куда толкает: <0 влево, >=0 вправо. Чем кончится + * удар, решает тайл, на котором персонаж оказался после отжатия: сквозь + * стену/верх двери он стоит на СОСЕДНЕЙ клетке (в сторону толчка), для + * прочих препятствий (ворота, зеркало, кусаки) — на клетке препятствия. */ +static void bumped(int delta_x, int8_t push_dir, int8_t tcol) +{ + uint8_t ct; + if (Char.alive >= 0) return; /* труп не бампит */ + Char.x = (uint8_t)((int)Char.x + delta_x); + determine_col(); + ct = g_curr_tile; /* тайл препятствия */ + if (push_dir < 0) { /* толчок ВЛЕВО */ + if (ct == TILE_WALL) + ct = get_tile((int8_t)(tcol - 1), Char.curr_row); + } else { /* толчок ВПРАВО */ + if (ct == TILE_DOORTOP || ct == TILE_DOORTOP_FLOOR || ct == TILE_WALL) + ct = get_tile((int8_t)(tcol + 1), Char.curr_row); + } + if (tile_is_floor(ct)) bumped_floor(); + else bumped_fall(); + determine_col(); +} + +/* check_bumped_look_right (seg004:030A): Kid смотрит ВПРАВО и достал ЛЕВУЮ + * грань стены — отжать влево. */ +static void check_bumped_look_right(void) +{ + int8_t col = bump_col_l; + uint8_t wt; + int coll_left; + if (Char.direction != 0) return; /* (меча в руке у нас нет) */ + if (!is_obstacle_at_col(col)) return; + /* Прежде чем удариться о стену — попытка зацепиться за её верх + * (seg004:0205, то же улучшение). Не вышло — тайл препятствия надо + * перечитать: проба зацепа сбила g_curr_tile. */ + if (POP_ENABLE_JUMP_GRAB && pop_ctrl_shift_held()) { + if (check_grab_run_jump()) return; + if (!is_obstacle_at_col(col)) return; + } + wt = wall_type(g_curr_tile); + if (!wt) return; + coll_left = pop_x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX; + bumped(wall_dl[wt] + coll_left - coll_xr, -1, col); +} + +/* check_bumped_look_left (seg004:02D2): Kid смотрит ВЛЕВО и достал ПРАВУЮ + * грань стены — отжать вправо. */ +static void check_bumped_look_left(void) +{ + int8_t col = bump_col_r; + uint8_t wt; + int coll_left; + if (Char.direction >= 0) return; + if (!is_obstacle_at_col(col)) return; + if (POP_ENABLE_JUMP_GRAB && pop_ctrl_shift_held()) { /* seg004:0186 */ + if (check_grab_run_jump()) return; + if (!is_obstacle_at_col(col)) return; + } + wt = wall_type(g_curr_tile); + if (!wt) return; + coll_left = pop_x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX; + bumped(coll_left - wall_dr[wt] + TILE_RIGHTX - coll_xl, 0, col); +} + +/* dist_from_wall_behind (seg004:0607) — зеркало dist_from_wall_forward: та + * же арифметика, но от ЗАДНЕЙ грани габарита (лицом вправо задняя грань — + * левая, и наоборот). Нужна только check_guard_bumped. */ +static int dist_from_wall_behind(uint8_t tiletype, int8_t tcol) +{ + uint8_t type = wall_type(tiletype); + int coll_left; + if (type == 0) return 99; + coll_left = pop_x_bump[tcol + FIRST_ONSCREEN_COLUMN] + TILE_MIDX; + if (Char.direction >= 0) /* лицом вправо */ + return coll_xl - (coll_left + TILE_RIGHTX - wall_dr[type]); + return wall_dl[type] + coll_left - coll_xr; /* лицом влево */ +} + +/* check_guard_bumped (seg004:0522) — стражья замена check_bumped, и она + * НЕ про «упереться на бегу»: срабатывает, только когда персонажа с + * ВЫНУТЫМ МЕЧОМ теснят спиной в стену (его собственный шаг назад в бою или + * наступающий Кид). Отсюда и проверки: action = run_jump, жив, меч в руке, + * а расстояние меряется от ЗАДНЕЙ грани. + * + * Порядок проб как в оригинале, включая побочный эффект: если тайл под + * персонажем не подошёл и он смотрит вправо, оригинал перечитывает тайл + * СЛЕВА (`--tile_col`), и дальше is_obstacle/dist_from_wall_behind считают + * уже по нему. */ +static void check_guard_bumped(void) +{ + int8_t col = Char.curr_col, row = Char.curr_row; + uint8_t t, hit; + int delta_x; + + if (Char.action != ACT_RUNJUMP || Char.alive >= 0 || + Char.sword < SWORD_2_DRAWN) return; + + t = get_tile_at_char(); + hit = (uint8_t)(t == TILE_WALL || t == TILE_DOORTOP_FLOOR || + (t == TILE_GATE && !gate_passable(col, row))); + if (!hit && Char.direction >= 0) { + col--; + t = get_tile(col, row); + hit = (uint8_t)(t == TILE_DOORTOP_FLOOR || + (t == TILE_GATE && !gate_passable(col, row))); + } + if (!hit) return; + + set_char_collision(); + if (!is_obstacle_at_col(col)) return; + delta_x = dist_from_wall_behind(g_curr_tile, col); + if (delta_x < 0 && delta_x > -13) { + Char.x = (uint8_t)char_dx_forward(-delta_x); + pop_char_set_seq(SEQ_65_BUMP_FWD_SWORD); + play_seq(); + pop_load_fram_det_col(); + } +} + +/* check_bumped (seg004:029D). */ +static void check_bumped(void) +{ + /* Гарды РОВНО как в оригинале: вис и подтягивание. ПАДЕНИЕ И ПОЛЁТ НЕ + * ИСКЛЮЧЕНЫ — bumped_fall (seg004:04E4) специально разбирает случай + * actions_4_in_freefall (гасит fall_x и отжимает на 4). Наш прежний + * guard `action == FREEFALL || MIDAIR -> return` и был вторым корнем + * BUG-COLL-1: длинный прыжок в стену (комната 6, между (0,6) и (0,7)) + * ничем не останавливался, и Кид оказывался ВНУТРИ кладки. */ + if (Char.action == ACT_HANGCLIMB || Char.action == ACT_HANGSTRAIGHT) return; + if (Char.frame >= 135 && Char.frame < 149) return; /* кадры подъёма/спуска */ + /* Кадры виса 87..99: в спуске (seq_68) кадр frame_91 идёт ДО act(hang_climb), + * т.е. action ещё midair — без этого guard'а bump у стены рвёт цепочку + * hang->hang_fall->seq_11 (правильное приземление). */ + if (Char.frame >= 87 && Char.frame < 100) return; + if (bump_col_l != BUMP_NONE) check_bumped_look_right(); + else if (bump_col_r != BUMP_NONE) check_bumped_look_left(); +} + +/* ---- Loose floors (seg007) ---------------------------------------- * + * Состояние на тайл: 0 = покой; 0x80..0x83 = тряска (do_knock); 1..11 = + * отсчёт до падения (на тайле стоят/пробили). Порт animate_loose/ + * make_loose_fall/check_press. Перерисовка — в pop_bg (shake/bake). */ +uint8_t pop_loose_modif[30]; /* публично: читает pop_bg при отрисовке */ + +/* Гейт холостого хода pop_loose_tick. В подавляющем большинстве кадров + * (и в целых комнатах) НИ ОДНА плита не анимируется, а два цикла по 30 и 10 + * позициям всё равно отрабатывают: замер 11/15 — 9 852 такта на комнату, + * где loose-плит нет вовсе. + * + * Ставится ПРИ ВЗВОДЕ фазы — все ШЕСТЬ мест записи ненулевого значения лежат + * в этом файле (make_loose_fall, ветка потолка в check_press, do_knock для + * обоих рядов, восстановление из room_modif в pop_loose_reset и раздача + * отложенного старта в check_fall_flo — уровень 13). + * + * ИМЕННО ЗДЕСЬ УЖЕ ОШИБЛИСЬ ОДИН РАЗ: check_fall_flo забыли, и каскад + * уровня 13 перестал падать — плиты дрожали и застывали. Добавляя новое + * место записи фазы, добавляй и взвод. Снимает его + * САМ цикл, когда прошёл оба массива и не встретил ни одной ненулевой фазы. + * + * Асимметрия намеренная: ложная единица стоит одного холостого прохода, + * ложный ноль — застывшей навсегда плиты. Поэтому взвод обязан стоять + * рядом с КАЖДОЙ записью, а снятие — только по факту пустого прохода. */ +static uint8_t loose_any; + +/* Плита-ПОТОЛОК: состояние/сигналы — см. объявления перед get_tile. */ + +/* make_loose_fall (seg007:0EF6): взвести отсчёт падения, если ещё не идёт. */ +static void make_loose_fall(int pos, uint8_t modifier) +{ + if (pos < 0 || pos >= 30) return; + if ((g_fg[pos] & 0x1F) != TILE_LOOSE) return; + if ((g_fg[pos] & 0x20)) return; /* «solid» loose — не от шага */ + if ((int8_t)pop_loose_modif[pos] <= 0) { /* покой/тряска, не отсчёт */ + pop_loose_modif[pos] = modifier; + loose_any = 1; + } +} + +/* died_on_button (seg007:0776): на кнопке КТО-ТО УМЕР — эффект становится + * ПОСТОЯННЫМ, а сама кнопка с тайла снимается. + * + * Открывалка превращается в обычный пол с нулевым модификатором, а связь + * дёргается типом «щебень» (TILE_DEBRIS) — у trigger_gate это отдельная + * ветка «открыть насовсем»: недооткрытые ворота получают trob-тип 2, уже + * открытые — modif 0xFF (открыты навсегда). Любая другая кнопка становится + * заклиненной (TILE_STUCK): нажатой она и останется. + * + * ЗАЧЕМ ЭТО ВООБЩЕ НУЖНО. Труп не «держит» кнопку — он её ЛОМАЕТ. Разница + * видна сразу: держал бы — ворота закрылись бы, как только тело убрали или + * уровень перезапустили. У Кида перезапуск всё равно перечитывает тайлы, а + * вот когда на кнопке гибнет СТРАЖ, перезапуска нет и кнопка заклинена до + * конца уровня. + * + * Тайл пишем и в страницу уровня (переживёт выход из комнаты, как do_pickup), + * и в живую g_fg отрисованной комнаты — вместе с пометкой на перерисовку + * своей ячейки и правой (у кнопки и пола разные правые грани). */ +static void died_on_button(uint8_t room, uint8_t tp, uint8_t code) +{ + uint8_t *mod = pop_trob_modif(room); + uint8_t modifier = mod[tp]; /* индекс связи — ДО затирания */ + uint8_t button_type = code; + uint8_t newtile; + if (code == TILE_OPENER) { + newtile = TILE_FLOOR; + mod[tp] = 0; + button_type = TILE_DEBRIS; /* trigger_gate: «открыть насовсем» */ + } else { + newtile = TILE_STUCK; + } + pop_level_set_tile(room, tp, newtile); + if (room == g_room) { + g_fg[tp] = newtile; + pop_set_redraw(tp, POP_RD_FLOOR, 2); /* обе страницы */ + if (tp % 10 < 9) + pop_set_redraw((uint8_t)(tp + 1), POP_RD_FLOOR, 2); + } + pop_trigger_button(room, tp, button_type, modifier, 1); +} + +/* check_press (seg006:0EC8, упрощ.): Kid стоит на loose → make_loose_fall; + * frame 79 (прыжок вверх) с loose СВЕРХУ → пробить сверху. */ +static void check_press(void) +{ + uint8_t frame = Char.frame, action = Char.action; + uint8_t above_press = 0; /* «нажимается» тайл СВЕРХУ */ + /* Кадры виса (87..99) и начала подъёма (135..140): нажимается тайл, за + * КОТОРЫЙ ПЕРСОНАЖ ДЕРЖИТСЯ, т.е. верхний (seg006:1688). Отсюда падение + * плиты, за край которой Kid зацепился, — а не только той, по которой он + * ударил снизу прыжком (кадр 79). */ + if ((frame >= 87 && frame < 100) || (frame >= 135 && frame < 141)) { + above_press = 1; + } else if (action == ACT_BUMPED || action == ACT_TURN || action < ACT_HANGCLIMB) { + if (frame == 79) above_press = 1; /* прыжок вверх — удар снизу */ + } else { + return; /* seg006:1704 — прочее не жмёт */ + } + if (above_press) { + if (Char.curr_row == 0) { + /* Ряд 0: «сверху» — ряд 2 комнаты СВЕРХУ (в оригинале get_tile с + * row=−1 уходит по links.up, find_room_of_tile seg006:005D). + * Плита-ПОТОЛОК; кнопок на потолке в этом тесте нет. */ + int8_t c = Char.curr_col; + if (g_above && g_link_u && c >= 0 && c <= 9 && + (g_above[c] & 0x1F) == TILE_LOOSE && !(g_above[c] & 0x20) && + (int8_t)pop_ceil_modif[c] <= 0) { + pop_ceil_modif[c] = 1; /* make_loose_fall(1) */ + loose_any = 1; + is_guard_notice = 1; /* seg006:1734 */ + } + } else if (get_tile_above_char() == TILE_LOOSE) { + make_loose_fall((Char.curr_row - 1) * 10 + Char.curr_col, 1); + is_guard_notice = 1; /* seg006:1734 */ + } + return; + } + /* Стояние: кадр обязан требовать пол; нажимается тайл ПОД персонажем. + * ACT_TURN обязателен (seg006:1691): при развороте на кнопке иначе + * trigger_button не зовётся кадр-два → таймер связи падает <2 → кнопка + * ложно отжимается и жмётся снова. */ + if (!(kid_cur_flags() & FRAME_NEEDS_FLOOR)) return; + { + uint8_t t = get_tile_at_char(); + uint8_t tp = 0; + /* Кнопка может лежать в СОСЕДНЕЙ комнате — Кид стоит в шве + * (curr_col = −1 или 10). Комнату и tilepos берём резолвнутые, + * иначе индекс связи читается из чужого тайла (см. tile_room_of). */ + uint8_t btn_room = tile_room_of(Char.curr_col, Char.curr_row, &tp); + if ((t == TILE_OPENER || t == TILE_CLOSER) && btn_room) { + /* seg006:1707 — ЖИВОЙ жмёт кнопку на 5 кадров, МЁРТВЫЙ ломает её + * насовсем. `alive < 0` = жив (соглашение оригинала). */ + if (Char.alive < 0) + pop_trigger_button(btn_room, tp, t, pop_trob_modif(btn_room)[tp], 1); + else + died_on_button(btn_room, tp, t); + } + else if (t == TILE_LOOSE) { + /* loose живёт в СВОЕЙ комнате (make_loose_fall работает с g_fg), + * поэтому здесь по-прежнему координаты персонажа. */ + make_loose_fall((uint8_t)(Char.curr_row * 10 + Char.curr_col), 1); + is_guard_notice = 1; /* seg006:1734 */ + } + } +} + +/* ---- Звук дрожащей плиты (loose_shake, seg007:0E55) ------------------ * + * Три сэмпла 20/21/22 вперемешку, но НЕ на каждый кадр фазы: гейт-таблица + * loose_sound (data:2734) даёт звук на фазах 1,2,3,5,8, а на остальных + * молчит — иначе тряска трещит без пауз. Повтор подряд одного и того же + * сэмпла оригинал запрещает (цикл do/while), иначе слышно «заедание». + * + * Сид СВОЙ, а не общий с trob/боёвкой: домены prandom у нас разведены + * (../docs/impl_diff.md), и звук не должен сдвигать чужие розыгрыши. */ +static const uint8_t loose_sound[12] = { 0,1,1,1,0,1,0,0,1,0,0,0 }; +static pop_rnd_t loose_seed; +static uint8_t last_loose_snd; + +static void loose_shake(uint8_t force, uint8_t modif) +{ + uint8_t id, prev; + + if (!force && !loose_sound[modif & 0x7F]) return; + /* ЗАПИСЬ ДО СРАВНЕНИЯ — обход sdcc_z80_cmp_store_a_bug. + * + * Прежняя запись (`do { id = ... } while (id == last_loose_snd); + * last_loose_snd = id; pop_sfx_play(id);`) собиралась так: + * ld a, e / add a, #0x14 ; A = 20..22, номер сэмпла + * ld hl, #_last_loose_snd + * sub a, (hl) ; A = РАЗНОСТЬ, номер потерян + * ld (_last_loose_snd), a ; сохраняем разность + * jp _pop_sfx_play ; играем разность + * то есть в звук уходил не сэмпл тряски, а id минус предыдущий id. + * Пока такие «номера» попадали в пустые слоты набора, это молчало; + * с приходом музыки мусор вида 22-251 = 27 стал запускать ТРЕК — + * пользователь слышал музыку на прыжке и на падающей плите. */ + prev = last_loose_snd; + for (;;) { + id = (uint8_t)(pop_prandom(&loose_seed, 2) + 20); + last_loose_snd = id; /* store раньше любого сравнения */ + if (id != prev) break; + } + pop_sfx_play(last_loose_snd); /* читаем из памяти, не из A */ +} + +/* do_knock (seg007:0FE0) + loose_make_shake (seg007:0FB4): сотрясение от + * приземления/удара трясёт ВСЕ loose-полы в ряду tile_row (не роняя их — + * modif 0x80..0x83 = чистая тряска, сбрасывается в pop_loose_tick). Трясём + * только из ПОКОЯ (modif==0): если тайл уже дрожит/падает — не перебиваем. */ +static void do_knock(int tile_row) +{ + int col, pos; + /* СПЕЦКЕЙС УРОВНЯ 13 (loose_make_shake, seg007:949): сотрясение НЕ + * трясёт плиты вовсе. Причина та же, что у первого спецкейса: тряска + * ставит фазу 0x80, а на этом уровне у плит уже лежит СВОЙ отложенный + * старт (0xF0..0xFF), и приземление Кида сбросило бы его на 0x80 — + * гряда посыпалась бы разом вместо вразнобой. */ + if (pop_current_level == POP_LOOSE_TILES_LEVEL) return; + if (tile_row == -1) { /* ряд 2 комнаты сверху — плиты-потолки */ + if (!g_above || !g_link_u) return; + for (col = 0; col < 10; col++) + if ((g_above[col] & 0x1F) == TILE_LOOSE && pop_ceil_modif[col] == 0) { + pop_ceil_modif[col] = 0x80; + loose_any = 1; + } + return; + } + if (tile_row < 0 || tile_row > 2) return; + for (col = 0; col < 10; col++) { + pos = tile_row * 10 + col; + if ((g_fg[pos] & 0x1F) != TILE_LOOSE) continue; + if (pop_loose_modif[pos] == 0) { + pop_loose_modif[pos] = 0x80; + loose_any = 1; + } + } +} + +/* check_knock (seg003:0706): по флагу knock (ставит play_seq на seqtbl- + * командах KNOCK_UP/DOWN) тряхнуть ряд. KNOCK_DOWN (knock=-1, приземление) → + * ряд curr_row; KNOCK_UP (knock=+1) → ряд curr_row-1. */ +void pop_check_knock(void) __banked +{ + if (knock) { + do_knock(Char.curr_row - (knock > 0)); + knock = 0; + } +} + +/* Сдать НЕЗАКОНЧЕННЫЕ loose уходящей комнаты в персистентное хранилище — + * звать ПЕРЕД сменой комнаты, пока g_room/g_fg/g_link_u ещё старые. + * + * pop_loose_modif[30] — фаза тайлов ТЕКУЩЕЙ комнаты, и раньше смена комнаты + * её просто теряла: плита, по которой Кид пробежал и ушёл вправо, оставалась + * целой (уровень 10, комната 2, тайл 2,9). В оригинале терять нечего — + * там loose это trob в общем списке, а фаза лежит в curr_room_modif, то есть + * в самой таблице уровня. Порт того же: фаза уезжает в room_modif (он + * персистентен per-room), а досчитывает её animate_loose в pop_trob.c. + * + * Пишем фазу ВСЕМ loose-тайлам, включая нулевые: room_modif — источник + * истины, а pop_loose_modif лишь его копия на время пребывания в комнате, + * и оставить там прошлое значение значило бы «оживить» уже отдрожавшую + * плиту при следующем входе. trob заводим только на незаконченные. */ +void pop_loose_leave_room(void) __banked +{ + uint8_t *mod; + int i; + if (!g_fg || !g_room) return; + mod = pop_trob_modif(g_room); + if (mod) { + for (i = 0; i < 30; i++) { + if ((g_fg[i] & 0x1F) != TILE_LOOSE) continue; + mod[i] = pop_loose_modif[i]; + if (pop_loose_modif[i]) pop_add_trob(g_room, (uint8_t)i, 0); + } + } + /* Плиты-ПОТОЛКИ — ряд 2 комнаты сверху, tilepos 20+col. */ + if (g_above && g_link_u) { + mod = pop_trob_modif(g_link_u); + if (mod) { + for (i = 0; i < 10; i++) { + if ((g_above[i] & 0x1F) != TILE_LOOSE) continue; + mod[20 + i] = pop_ceil_modif[i]; + if (pop_ceil_modif[i]) + pop_add_trob(g_link_u, (uint8_t)(20 + i), 0); + } + } + } +} + +/* Решётка ворот должна идти ПОВЕРХ персонажа? Порт первого условия + * draw_tile_fore (seg008:0D15): + * + * if (tile_left == tiles_4_gate && Kid.curr_row == drawn_row && + * Kid.curr_col == drawn_col - 1 && Kid.room != room_R) + * draw_gate_fore(); + * + * То есть: рисуя тайл (row, col), оригинал кладёт бары ворот в FOREtable — + * поверх всего, что уже нарисовано, — если тайл СЛЕВА это ворота и персонаж + * стоит ИМЕННО НА НИХ. Графика ворот принадлежит правому краю своего тайла + * и рисуется в проходе СЛЕДУЮЩЕЙ колонки, отсюда сдвиг на единицу. + * + * Живёт в карте, а не в слое фона, по двум причинам: спрашивает она про + * ТАЙЛ и позицию персонажа (это знание карты), и её линкует хост-харнесс — + * решение проверяется тестом без эмулятора (t_char: gate_fore_over_char). + * Слою фона остаётся только рисование (draw_gate_fore в pop_bg.c). + * + * РАСХОЖДЕНИЕ С ОРИГИНАЛОМ (осознанное, см. docs/impl_diff.md): оригинал + * жёстко спрашивает про Kid, потому что foretable у него ОДНА на проход + * тайлов; у нас fore-проход идёт по персонажу, поэтому спрашиваем про того, + * кого рисуем. Наш вариант — надмножество: страж, стоящий в проёме ворот, + * у нас тоже уходит ЗА решётку, а в оригинале остался бы перед ней. */ +uint8_t pop_gate_over_char(const pop_char_t *ch) __banked +{ + if (!ch || ch->room != g_room) return 0; + if (ch->curr_row < 0 || ch->curr_row > 2) return 0; + /* Колонка 9: бары легли бы в колонку 10, то есть в СОСЕДНЮЮ комнату — + * у оригинала это отсекает `Kid.room != room_R`. */ + if (ch->curr_col < 0 || ch->curr_col > 8) return 0; + return (uint8_t)(get_tile(ch->curr_col, ch->curr_row) == TILE_GATE); +} + +/* СТАРТ/РЕСТАРТ уровня: забыть фазы, НЕ сдавая их в room_modif — в + * оригинале всё состояние сносит сам load_level (seg003:57). Без этого + * pop_loose_leave_room у первого же enter_room занесла бы в только что + * восстановленный уровень дрожь из прошлой жизни: pop_trob_reset уже + * отработал, room_modif свежий, а pop_loose_modif — ещё старый. */ +void pop_loose_forget(void) __banked +{ + int i; + for (i = 0; i < 30; i++) pop_loose_modif[i] = 0; + for (i = 0; i < 10; i++) pop_ceil_modif[i] = 0; +} + +/* Сброс состояния loose (при смене комнаты): pop_loose_modif/bake/rest + * индексируются позицией тайла и не должны утечь из старой комнаты в новую. + * Уже ПАДАЮЩИЕ куски при этом не трогаем — они привязаны к своей комнате и + * должны долететь (do_mobs, seg007:1063; см. pop_loose_mob_room_changed). + * + * Вместо нулей забираем фазу НОВОЙ комнаты из room_modif — обратная + * половина pop_loose_leave_room: расшатанная и брошенная плита, к которой + * Кид вернулся, продолжает дрожать с той же фазы, а не с покоя. trob, + * который крутил её заочно, снимет сам animate_loose (room == cur_room). */ +void pop_loose_reset(void) __banked +{ + uint8_t *mod; + int i; + + mod = g_room ? pop_trob_modif(g_room) : 0; + for (i = 0; i < 30; i++) { + pop_loose_modif[i] = (mod && g_fg && (g_fg[i] & 0x1F) == TILE_LOOSE) + ? mod[i] : 0; + if (pop_loose_modif[i]) loose_any = 1; /* взвод гейта — см. loose_any */ + } + mod = (g_above && g_link_u) ? pop_trob_modif(g_link_u) : 0; + for (i = 0; i < 10; i++) { + pop_ceil_modif[i] = (mod && (g_above[i] & 0x1F) == TILE_LOOSE) + ? mod[20 + i] : 0; + if (pop_ceil_modif[i]) loose_any = 1; /* взвод гейта — см. loose_any */ + /* ...и СНЯТЬ заочный trob: плиту-потолок отсюда снова ведёт + * pop_loose_tick. Без этого её крутили бы ДВОЕ — trob по + * room_modif и мы по pop_ceil_modif, — и она проваливалась бы + * дважды (animate_loose снимает сам только trob'ы cur_room, а + * комната сверху — не cur_room). */ + if (pop_ceil_modif[i]) pop_add_trob(g_link_u, (uint8_t)(20 + i), -1); + } + pop_ceil_fell = 0; + pop_debris_at = 0; + pop_loose_landed = 0; + pop_loose_mob_room_changed(); +} + +/* remove_loose (seg007:0EB8) для ОДНОГО тайла, наружу — ради честного + * loose_fall: падающий кусок сбивает плиту НЕМЕДЛЕННО, в том же кадре + * (pop_room.c, mob_break_loose). Здесь, а не в pop_room, потому что тут + * живут и рабочая копия тайлов комнаты (g_fg), и фазы плит. + * + * Перерисовываем ДВЕ клетки — саму и соседа справа. Передний торец пола + * заезжает в соседний тайл (правая грань соседа всегда в backtable), и без + * второй пометки от исчезнувшей плиты оставался висеть торец: тайл пустой, + * а его передняя грань нарисована. Тем же приёмом, что pop_skel_wake_tile + * и что set_redraw_full+set_wipe на curr_tilepos и curr_tilepos+1 в + * оригинале. */ +void pop_loose_remove(uint8_t room, uint8_t tilepos) __banked +{ + uint8_t *mod = pop_trob_modif(room); + if (tilepos >= 30) return; + if (room == g_room) { + g_fg[tilepos] = TILE_EMPTY; + pop_loose_modif[tilepos] = 0; + pop_set_redraw(tilepos, POP_RD_LOOSE_GONE, 2); + if ((tilepos % 10) < 9) + pop_set_redraw((uint8_t)(tilepos + 1), POP_RD_FLOOR, 2); + } else { + /* Комната не на экране, но её кромочная колонка видна в шве — те же + * снимки, что обновляет animate_loose. */ + pop_neigh_dirty = 1; + } + /* Модификатор пустой клетки = ТИП УРОВНЯ (по нему draw_tile_right + * выбирает blueline у дыры), тайл в странице уровня — сразу. */ + if (mod) mod[tilepos] = pop_palace; + pop_level_set_tile(room, tilepos, TILE_EMPTY); +} + +/* check_fall_flo (seg000:1317) — СПЕЦСОБЫТИЕ уровня 13: гряда плит над + * комнатой сыплется на Кида сама, вразнобой. + * + * Как это устроено в оригинале: при входе в комнату 23 или 16 шести плитам + * ряда 2 комнаты СВЕРХУ раздаётся ОТРИЦАТЕЛЬНЫЙ модификатор −(rnd & 15). + * Отрицательное значение — это старший бит, то есть для animate_loose оно + * выглядит как «тряска», а на уровне 13 тряска намеренно не гасится + * (спецкейс в pop_loose_tick). Фаза считает вверх, доходит до нуля и + * дальше идёт обычным отсчётом до провала — получается разная задержка у + * каждой плиты. + * + * Плиты ряда 2 верхней комнаты у нас — это pop_ceil_modif (плиты-потолки), + * так что раздаём фазу именно туда. + * + * ОДНО отличие от оригинала, вынужденное представлением: у нас фаза 0 = «не + * анимируется», плита с такой фазой в цикле пропускается вовсе. Значит + * счёт, дойдя до нуля, оборвался бы — поэтому, во-первых, тик перескакивает + * ноль (см. pop_loose_tick), а во-вторых, стартовое значение берётся на + * единицу «отрицательнее» (−n−1): вместе это даёт ровно ту же суммарную + * задержку n + 11 кадров, что и у оригинала. + * + * Звать при входе в комнату, после pop_loose_reset (тот забирает фазы из + * room_modif) — как check_the_end зовёт его после start_chompers. */ +static pop_rnd_t loose_seed; /* свой домен последовательности, см. pop_geom.h */ + +void pop_check_fall_flo(void) __banked +{ + uint8_t col; + if (pop_current_level != POP_LOOSE_TILES_LEVEL) return; + if (g_room != POP_LOOSE_TILES_ROOM1 && g_room != POP_LOOSE_TILES_ROOM2) return; + if (!g_above || !g_link_u) return; + for (col = POP_LOOSE_TILES_FIRST - 20; col <= POP_LOOSE_TILES_LAST - 20; col++) { + uint8_t t = g_above[col]; + /* Те же отсевы, что в make_loose_fall: не loose и «solid» loose + * пропускаются молча (в комнате 1 тайл 24 пустой — диапазон 22..27 + * намеренно надмножество), уже идущий отсчёт не перебиваем. */ + if ((t & 0x1F) != TILE_LOOSE || (t & 0x20)) continue; + if ((int8_t)pop_ceil_modif[col] > 0) continue; + pop_ceil_modif[col] = + (uint8_t)(-(int8_t)(pop_prandom(&loose_seed, 255) & 0x0F) - 1); + loose_any = 1; /* ШЕСТОЕ место взвода гейта — см. loose_any. + * Пропуск именно здесь стоил каскада на + * уровне 13: плиты получали отложенный + * старт, но цикл их не досчитывал, и они + * дрожали, не падая (нашёл пользователь + * 2026-08-19). */ + pop_set_redraw_above(col, POP_RDA_CEIL, 1); + } +} + +/* fell_on_your_head (seg007:1205): плита прилетела Киду по голове. В беге + * (кадры 5..14) не бьёт (кроме уровня 13 — его тут нет); в висе/подъёме тоже + * (action >= hang_climb, кроме turn). Иначе −1 HP: смерть → seq_22 (crushed), + * живой → seq_52 (плита придавила: Kid приседает), с отступом на 2 назад, + * если сзади пусто. Кадр 109 (присед) последовательность не меняет. */ +#define SEQ_52_LOOSE_FELL_ON_KID 52 +#define FRAME_109_CROUCH 109 + +static void fell_on_your_head(void) +{ + uint8_t frame = Char.frame, action = Char.action; + /* СПЕЦКЕЙС УРОВНЯ 13 (seg007:1218): там плита бьёт и В БЕГЕ. На прочих + * уровнях бегущего она пропускает — иначе гряда падающих плит, сквозь + * которую и надо пробегать, не представляла бы никакой угрозы. */ + if ((pop_current_level == POP_LOOSE_TILES_LEVEL || frame < 5 || frame >= 15) && + (action < ACT_HANGCLIMB || action == ACT_TURN)) { + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; + if (pop_take_hp(1)) { /* «брызги» даст pop_do_delta_hp */ + pop_char_set_seq(SEQ_22_CRUSHED); + play_seq(); + determine_col(); + if (Char.charid == CHARID_0_KID) pop_kid_dead = 1; + } else if (frame != FRAME_109_CROUCH) { + if (get_tile_behind_char() == TILE_EMPTY) + Char.x = (uint8_t)char_dx_forward(-2); + pop_char_set_seq(SEQ_52_LOOSE_FELL_ON_KID); + play_seq(); + determine_col(); + } + } +} + +/* check_loose_fall_on_kid (seg007:1192): падающий кусок в той же колонке и + * его низ уже в полосе 30px над ногами Kid → удар. */ +static void check_loose_fall_on_kid(void) +{ + int mcol, my; + if (pop_kid_dead) return; + /* Ни одного куска в воздухе — не платим за банковый трамплин в + * pop_loose_mob_pos с обходом 14 слотов (замер 11/15: 5 868 тактов на + * комнату, где не падает ничего). */ + if (!pop_mob_busy) return; + if (!pop_loose_mob_pos(&mcol, &my)) return; + /* Своё окно Char, как в оригинале (seg007:1199 — loadkid/savekid внутри + * самой функции): зовут нас из pop_loose_tick, а тот идёт в главном + * цикле вне окон, и в Char к этому моменту лежит СТРАЖ (он тикает + * последним). Плита на голову — про Кида. */ + pop_loadkid(); + if (Char.curr_col == (int8_t)mcol && + my < (int)Char.y && (int)Char.y - 30 < my) { + fell_on_your_head(); + pop_savekid(); + } +} + +/* animate_loose (seg007:0CBA) + перерисовка изменившихся тайлов на ТЕКУЩЕЙ + * (back) странице. Звать из основного цикла ПОСЛЕ set_draw_page(back). */ +void pop_loose_tick(void) __banked +{ + uint8_t pos; + /* Массивы обходим УКАЗАТЕЛЕМ, а не индексом. С `int pos` SDCC держал + * счётчик в IX-фрейме, каждую итерацию заново складывал 16-битный адрес + * элемента, клал его в локал и тут же вычитывал обратно парами pop/push: + * 40 холостых итераций стоили 27 936 тактов (замер 2026-08-13). Ходьба + * указателем убирает и адресную арифметику, и IX-фрейм. */ + uint8_t *lm = pop_loose_modif; + /* row/col ведём бегущими счётчиками, а НЕ через pos/10 и pos%10: у Z80 + * нет аппаратного деления, и SDCC выносил оба деления (__divuchar/ + * __moduchar) в начало КАЖДОЙ итерации (общее подвыражение аргументов + * вызовов), т.е. 30*2=60 вызовов деления за кадр — даже в покое и в + * комнатах без loose-плит (условия if к тому моменту ещё не проверены). + * Инкремент+сравнение считают row/col без деления. */ + uint8_t row = 0, col = 0; + pop_dbg_m9(); /* ЗАМЕР: вход pop_loose_tick */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* Гейт холостого хода: если ни одна фаза не взведена, оба цикла (30 + 10 + * позиций) пропускаем целиком — замер 11/15 дал 9 852 такта на комнату, + * где loose-плит нет вовсе. Флаг ставят места записи фазы, снимаем его + * здесь и только по факту прохода, в котором не осталось ни одной живой + * фазы (см. объявление loose_any). */ + if (loose_any) { + uint8_t found = 0; + for (pos = 0; pos < 30; pos++, lm++) { + if (*lm != 0) { + uint8_t m = ++*lm; + /* Отложенный старт уровня 13 досчитал до нуля. Ноль у нас + * означает «не анимируется», и плита застряла бы навсегда — + * перескакиваем на 1. Стартовое значение выбрано с учётом + * этого перескока (см. pop_check_fall_flo). */ + if (m == 0) m = *lm = 1; + if (m & 0x80) { /* тряска (do_knock) */ + /* СПЕЦКЕЙС УРОВНЯ 13 (seg007:823): фазу со старшим битом НЕ + * гасим и не трясём. На нём check_fall_flo раздаёт плитам + * ОТРИЦАТЕЛЬНЫЙ старт (0xF0..0xFF) как отложенный таймер, и + * обычная ветка «тряска кончилась на 0x84» убила бы его в + * первом же кадре — плита не упала бы никогда. Пропущенная + * инкрементом фаза сама дойдёт до 0x00, там старший бит + * снимется, и дальше пойдёт обычный отсчёт до провала. */ + if (pop_current_level == POP_LOOSE_TILES_LEVEL) { + /* ждём молча */ + } else if (m >= 0x84) { /* конец тряски: сброс + покой */ + *lm = 0; + loose_shake(1, 0); /* последний удар — без гейта */ + /* modif уже 0 → кадр покоя обоих тайлов (col и col+1: + * правая грань loose живёт в соседе). ОБЕ страницы — + * иначе на одной застывает дрожащий кадр → мерцание. */ + pop_set_redraw(pos, POP_RD_LOOSE, 2); + } else { + loose_shake(0, m); + pop_set_redraw(pos, POP_RD_LOOSE, 1); /* дрожащий кадр */ + } + } else if (m >= 11 && pop_loose_fell) { + /* Сигнал прошлого провала ещё не разобран главным циклом (он + * ОДИН на кадр) — придержим плиту кадр, откатив фазу к 10. + * Иначе из двух плит, провалившихся в одном кадре, дыру + * запекала бы только вторая, а первая оставалась нарисованной + * целой (уровень 13: check_fall_flo роняет гряду разом). */ + *lm = 10; + } else if (m >= 11) { /* loose_floor_delay — падение */ + /* remove_loose (seg007:0EB8): тайл → EMPTY (не debris!). + * Debris появляется ТОЛЬКО там, где приземлился падающий + * кусок (mob), т.е. рядом ниже. Loose в нижнем ряду (2,6) + * — кусок улетает из комнаты вниз, на месте остаётся пусто + * (стена-вниз от тайла снизу + чёрный фон). */ + g_fg[pos] = TILE_EMPTY; + /* remove_loose возвращает ТИП УРОВНЯ, и вызывающий кладёт + * его модификатором пустой клетки (seg007:846/1083): по нему + * draw_tile_right выбирает blueline_fram1 у дыры. */ + pop_trob_modif(g_room)[pos] = pop_palace; + *lm = 0; + pop_set_redraw(pos, POP_RD_LOOSE_GONE, 2); /* запечь пусто, обе стр. */ + /* ...и СОСЕДА СПРАВА: передний торец пола заезжает в его + * клетку, и без этой пометки он оставался висеть над пустым + * тайлом. То же, что делает pop_skel_wake_tile. */ + if ((pos % 10) < 9) + pop_set_redraw((uint8_t)(pos + 1), POP_RD_FLOOR, 2); + pop_loose_mob_spawn(row, col); /* отрыв падающего куска */ + pop_loose_fell = (uint8_t)(pos + 1); /* сигнал приложению */ + } else { /* отсчёт 1..10 — дрожащий кадр */ + loose_shake(0, m); + pop_set_redraw(pos, POP_RD_LOOSE, 1); + } + if (*lm) found = 1; /* фаза ещё жива — гейт держим */ + } + if (++col == 10) { col = 0; row++; } /* row/col без деления */ + } + /* Плиты-ПОТОЛКИ (ряд 2 комнаты сверху): тот же animate_loose, но кадры + * рисуются в полосе у потолка, а провалившаяся плита становится EMPTY в + * НАШЕЙ копии ряда (g_above) — сверху открывается колодец. */ + lm = pop_ceil_modif; /* тот же указательный обход */ + for (col = 0; col < 10; col++, lm++) { + if (*lm != 0) { + uint8_t m = ++*lm; + if (m == 0) m = *lm = 1; /* см. pop_loose_tick выше */ + if (m & 0x80) { /* тряска от сотрясения (do_knock) */ + /* Тот же спецкейс уровня 13, и здесь он ГЛАВНЫЙ: плиты, + * которым check_fall_flo раздал отложенный старт, лежат + * именно в ряду 2 комнаты СВЕРХУ, то есть ведёт их этот + * цикл, а не соседний. */ + if (pop_current_level == POP_LOOSE_TILES_LEVEL) { + /* ждём молча */ + } else if (m >= 0x84) { *lm = 0; loose_shake(1, 0); + pop_set_redraw_above(col, POP_RDA_CEIL, 2); } + else { loose_shake(0, m); + pop_set_redraw_above(col, POP_RDA_CEIL, 1); } + } else if (m >= 11 && pop_ceil_fell) { + *lm = 10; /* сигнал занят — кадр подождём */ + } else if (m >= 11) { /* loose_floor_delay — плита рушится */ + *lm = 0; + if (g_above) g_above[col] = TILE_EMPTY; /* remove_loose */ + pop_set_redraw_above(col, POP_RDA_CEIL_GONE, 2); /* колодец, обе стр. */ + pop_loose_mob_spawn(-1, col); /* кусок отрывается от потолка */ + pop_ceil_fell = (uint8_t)(col + 1); /* сигнал приложению */ + } else { + loose_shake(0, m); + pop_set_redraw_above(col, POP_RDA_CEIL, 1); /* отсчёт 1..10 */ + } + if (*lm) found = 1; /* фаза ещё жива — гейт держим */ + } + } + loose_any = found; + } + pop_loose_mob_tick(); /* продвинуть+нарисовать падающий кусок (после тайлов) */ + check_loose_fall_on_kid(); /* seg007:1192 — плита падает Киду на голову */ + /* Отложенная допечка ПРОШЛОГО приземления на вторую страницу — ПЕРЕД + * разбором нового: иначе при двух плитах, севших подряд, land_bake + * затирался новым и на одной странице оставался старый пол. */ + if (pop_loose_landed) { /* loose_land (seg007:11E8): на месте куска — щебень */ + uint8_t pos = (uint8_t)(pop_loose_landed - 1); + uint8_t r = 0, c = pos; + uint8_t tt = (uint8_t)(g_fg[pos] & 0x1F); /* тайл, НА который села плита */ + pop_loose_landed = 0; + while (c >= 10) { c -= 10; r++; } + /* Плита, севшая на КНОПКУ, её нажимает (seg007:11E8, ветки + * tiles_15_opener / tiles_6_closer). Тонкость: на opener оригинал + * подставляет button_type = tiles_14_debris, а это у trigger_gate + * отдельная ветка «открыть НАСОВСЕМ» (возврат 2 -> animate_door + * доводит створку и ставит modifier 0xFF, после чего ворота + * заморожены и обычный opener их больше не трогает). Сама кнопка + * съедается: тайл станет щебнем ниже, нажаться второй раз нечему. + * Модификатор (индекс LINKLOC) тайл не меняет, берём его как есть. */ + if (tt == TILE_OPENER || tt == TILE_CLOSER) + pop_trigger_button(g_room, pos, + (uint8_t)(tt == TILE_OPENER ? TILE_DEBRIS : tt), + pop_trob_modif(g_room)[pos], 1); + /* На факеле — отдельный тайл «факел с щебнем» (seg007:1067). */ + g_fg[pos] = (uint8_t)((tt == TILE_TORCH || tt == TILE_TORCH_DEBRIS) + ? TILE_TORCH_DEBRIS : TILE_DEBRIS); + /* Перерисовка ПО ПЛОЩАДИ ПОЛА (не LOOSE_GONE: та стирает и верх стены + * ряда ниже — оставался чёрный прямоугольник под тайлом). Обе + * страницы дабл-буфера. */ + pop_set_redraw(pos, POP_RD_FLOOR, 2); + do_knock(r); /* удар плиты трясёт loose этого ряда */ + pop_debris_at = (uint8_t)(pos + 1); /* сигнал приложению (персистентность) */ + } +} + + + +/* ---- clip_char (seg006:1749), первый блок: обрезка спрайта СВЕРХУ ----- * + * Оригинал перед add_objtable кладёт в objtable прямоугольник клипа; блиттер + * рисует персонажа только внутри него. Верхняя граница: если тайл НАД + * головой (ряд char_top_row) — стена или пол, всё выше линии своей полосы + * (y_clip[curr_row+1]) не рисуется. Без этого Kid, ушедший головой под пол + * (спуск с кромки/кнопки, вис), просвечивает сквозь щели кладки. + * + * Хитрость исходника: `X == wall || tile_is_floor(curr_tile2)` — curr_tile2 + * ставит сам get_tile, т.е. это просто «тайл X = стена ИЛИ пол». + * + * Метрики габарита — порт set_char_collision (seg006:0723); в отличие от + * check_spike_below колонки берутся get_tile_div_mod БЕЗ −7. + * + * Возврат: КОМНАТНЫЙ y верхней границы (0 = клипа нет). */ +/* Вторая половина ветки двери уровня из clip_char (seg006:1231): + * obj_clip_right = leveldoor_right — ЭКРАННЫЙ X, правее которого спрайт не + * рисуется. 0 = обрезки нет. + * + * Почему обрезка, а не fore-слой: створка и косяк уходят в оригинале + * ЦЕЛИКОМ в backtable (draw_leveldoor, все add_backtable), то есть рисуются + * ПОД персонажем и перекрыть его не могут. Единственный способ спрятать + * поднимающегося по лестнице — срезать сам спрайт. */ +int pop_clip_char_right(void) __banked +{ + if (Char.frame >= 224 && Char.frame < 229) + return pop_leveldoor_right; + return 0; +} + +int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) __banked +{ + static const int16_t y_clip[5] = { -60, 3, 66, 129, 192 }; + int wh = ((int)w + 1) >> 1; /* char_width_half */ + int xl = obj_x / 2 + 58; /* char_x_left */ + int xr, top_y, clip_y; + int8_t cL, cR, trow, clip_row; + uint8_t t; + + /* Кадры подъёма по лестнице двери уровня (clip_char, seg006:1231): + * персонаж режется по низу поднятой створки. ВНИМАНИЕ: комментарий в + * SDLPoP говорит «frames 217..228», а КОД проверяет + * `>= frame_224_exit_stairs_8`, то есть 224..228 — портируем по коду. + * Правую кромку отдаёт pop_clip_char_right (парная половина ветки). */ + if (Char.frame >= 224 && Char.frame < 229) + return pop_leveldoor_ybottom + 1; + + if (Char.direction >= 0) xl -= wh; /* лицом вправо: левый край внутрь */ + xr = xl + wh; /* char_x_right */ + top_y = obj_y - (int)h + 1; /* char_top_y */ + if (top_y >= 192) top_y = 0; /* весь спрайт ниже комнаты */ + trow = pop_y_to_row((int16_t)top_y); /* char_top_row */ + cL = get_tile_div_mod(xl); if (cL < 0) cL = 0; + cR = get_tile_div_mod(xr); if (cR > 9) cR = 9; + + t = get_tile(cL, trow); + if (t != TILE_WALL && !tile_is_floor(t)) return 0; + /* кадр 79 (прыжок вверх) / 81 (зацеп) в стойке клипятся и по одной грани */ + if (!(Char.action == 0 && (Char.frame == 79 || Char.frame == 81))) { + t = get_tile(cR, trow); + if (t != TILE_WALL && !tile_is_floor(t)) return 0; + } + + clip_row = (int8_t)(Char.curr_row + 1); + if (clip_row < 0 || clip_row > 4) return 0; + clip_y = y_clip[clip_row]; + /* ряд 0 — клип безусловный; иначе только когда линия реально режет спрайт */ + if (clip_row == 1 || (clip_y < obj_y && clip_y - 15 < top_y)) + return clip_y; + return 0; +} + +/* check_leave_below (порт leave_room, seg002: leave_dir=3 при chary>=211): + * Kid пересёк нижнюю границу комнаты (провал в дыру ИЛИ спуск-зацеп/climbdown + * — единый триггер по y). Репроекция в кадр комнаты снизу (goto_other_room + * down: y-=189, curr_row=y_to_row_mod4) + сигнал pop_fell_out — приложение + * решит: переход (links.down) или выпал из уровня (нет комнаты снизу). */ +static void check_leave_below(void) +{ + /* leave_room (seg002:0504) проверяет ВВЕРХ ПЕРВЫМ: персонаж не в отскоке/ + * полёте и его y (как signed) в полосе −16..10 у верха комнаты — он вылез + * в комнату сверху (через колодец от упавшей плиты). Порядок важен: при + * подтягивании y ≈ 248, и проверка «ниже комнаты» (y>=211) иначе + * перехватывает подъём и роняет Kid из уровня. + * goto_other_room(up): y += 189, curr_row = y_to_row_mod4. */ + if (g_link_u && Char.action != ACT_BUMPED && Char.action != ACT_FREEFALL && + Char.action != ACT_MIDAIR && + (int8_t)Char.y < 10 && (int8_t)Char.y > -16) { + Char.y = (uint8_t)(Char.y + 189); + Char.curr_row = pop_y_to_row((int16_t)Char.y); + pop_leave_dir = 3; + return; + } + if (Char.y >= 211) { + Char.y = (uint8_t)(Char.y - 189); + Char.curr_row = pop_y_to_row((int16_t)Char.y); + pop_fell_out = 1; + } +} + +/* check_leave вбок (порт leave_room, seg002): передний край персонажа + * (char_dx_forward(cur_frame.dx) = char_x_left при взгляде влево / char_x_right + * вправо) сравнивается с кромками комнаты. Взгляд вправо: >=201 → right, + * <=57 → left; взгляд влево: <=54 → left, >=198 → right. goto_other_room: + * x∓140 (ширина комнаты), пересчёт колонки; сигнал pop_leave_dir (2=right, + * 1=left). Уход только если сосед есть (иначе кромка = стена, check_bumped + * удержит у шва). */ +static void check_leave(void) +{ + int fx; + uint8_t f = Char.frame; + /* seg002 leave_room: комнату НЕ покидаем во время разворота (action turn), + * подъёма-с-зацепа (climb, кадры 135..149), вставания из приседа + * (110..119) и — ОТДЕЛЬНО ВАЖНОЕ — БОЯ МЕЧОМ (150..162 и 166..168). + * + * Без первых трёх разворот у правого шва (взгляд флипается право→лево, + * char_x_left проскакивает >=198) телепортирует Kid сквозь закрытые + * ворота в соседнюю комнату (leave_room возвращает −1). + * + * Без кадров МЕЧА бой у шва превращается в мельтешение: отступая и + * наступая, Kid пересекает границу почти каждый кадр, и комната + * перерисовывается туда-обратно — драться невозможно (найдено + * пользователем 2026-08-18, уровень 8, шов комнат 24/18). Оригинал + * пускает смену комнаты только на «легальном» кадре вроде 170 (стойка + * с мечом), а вся боевая анимация уход запрещает. Побочный эффект в + * оригинале известен и даже эксплуатируется: Trick 35 «retreat without + * leaving the room» — определённым ритмом «назад» из комнаты можно + * пятиться, не переключая её вовсе (в SDLPoP это чинит опциональный + * FIX_RETREAT_WITHOUT_LEAVING_ROOM, по умолчанию ВЫКЛЮЧЕННЫЙ; мы, как + * обычно, портируем оригинал, а не его исправления). + * + * NB: 135..149 и 150..162 смежны, поэтому слиты в один диапазон — + * условие то же, а сравнений на одно меньше. */ + if (Char.action == ACT_TURN || + (f >= 110 && f < 120) || + (f >= 135 && f < 163) || /* подъём-с-зацепа + вся боевая анимация */ + (f >= 166 && f < 169)) /* стойка «замер, меч наготове» */ + return; + /* Передний край — ТОЛЬКО через char_x_forward_edge (obj_x-арифметика + * set_char_collision): в зоне шва (<=57) он на пиксель правее чистого + * Char.x+dx, и Kid, осевший у ворот на x=57, порога leave-left НЕ достигает + * (эталон SDLPoP: экран не переключается, BUG-SEAM-PINGPONG). */ + fx = char_x_forward_edge(); + if (Char.direction >= 0) { /* взгляд вправо: fx = char_x_right */ + uint8_t t9 = get_tile(9, Char.curr_row); + if (g_link_r && fx >= 201 && + t9 != TILE_DOORTOP_FLOOR && t9 != TILE_DOORTOP) { + /* leave_room (seg002): под doortop у правой кромки выхода нет */ + Char.x = (uint8_t)(Char.x - 140); determine_col(); pop_leave_dir = 2; + } else if (g_link_l && fx <= 57) { + Char.x = (uint8_t)(Char.x + 140); determine_col(); pop_leave_dir = 1; + } + } else { /* взгляд влево: fx = char_x_left */ + if (g_link_l && fx <= 54) { + Char.x = (uint8_t)(Char.x + 140); determine_col(); pop_leave_dir = 1; + } else if (g_link_r && fx >= 198) { + Char.x = (uint8_t)(Char.x - 140); determine_col(); pop_leave_dir = 2; + } + } +} + +/* ---- Пики (spike, seg006/seg007) ---------------------------------- * + * Состояние пики в room_modif[g_room][tilepos] (pop_trob), автомат — + * animate_spike (pop_trob). Здесь: триггер выдвижения (check_spike_below), + * решение о смерти (check_spiked) + spiked(). */ + +/* start_anim_spike (seg007:08E3): взвести выдвижение, если пика в покое + * (modif 0 → trob type1) или ушла не полностью (modif<0, кроме 0xFF disabled + * → мгновенно 0x8F «вышли»). */ +static void start_anim_spike(uint8_t tilepos) +{ + uint8_t *mod = pop_trob_modif(g_room); + int8_t old; + if (!mod) return; + old = (int8_t)mod[tilepos]; + if (old <= 0) { + if (old == 0) { + pop_add_trob(g_room, tilepos, 1); + pop_sfx_play(49); /* seg007:08F6 — пики пошли */ + } else if ((uint8_t)old != 0xFF) + mod[tilepos] = 0x8F; + } +} + +/* is_spike_harmful (seg007:1178): 0/-1 → 0 (безопасно); <0 → 1; 1..4 → 2; + * >=5 → 0. */ +static int is_spike_harmful(uint8_t tilepos) +{ + int8_t m = (int8_t)pop_trob_modif(g_room)[tilepos]; + if (m == 0 || m == -1) return 0; + if (m < 0) return 1; + if (m < 5) return 2; + return 0; +} + +/* spiked (seg005:0888): напоролся — пика становится безвредной (0xFF), + * выровнять по колонке, pop_take_hp(100) = смерть, seq_51 (импейл). */ +static void spiked(uint8_t tilepos) +{ + pop_trob_modif(g_room)[tilepos] = 0xFF; + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; + Char.x = (uint8_t)(pop_x_bump[Char.curr_col + FIRST_ONSCREEN_COLUMN] + 10); + Char.x = (uint8_t)char_dx_forward(8); + Char.fall_y = 0; + pop_sfx_play(48); + pop_take_hp(100); + pop_char_set_seq(SEQ_51_SPIKED); + play_seq(); + determine_col(); + /* pop_kid_dead — НАШ флаг респавна, он про Кида. У прочих персонажей + * смерть видна по обнулившимся HP: play_guard ставит alive = 0, а + * дальше их доигрывает общий control() (seq_71_dying). */ + if (Char.charid == CHARID_0_KID) pop_kid_dead = 1; +} + +/* check_spiked (seg006:0658): под персонажем пика И вредна И кадр + * бега(7..14)/старта прыжка(34..39) при harmful>=2, ЛИБО кадр приземления + * (43/26) при harmful!=0 → smert'. */ +static void check_spiked(void) +{ + uint8_t frame = Char.frame, tp; + int h; + if (Char.curr_col < 0 || Char.curr_col > 9 || Char.curr_row < 0 || Char.curr_row > 2) + return; + if (get_tile_at_char() != TILE_SPIKE) return; + tp = (uint8_t)(Char.curr_row * 10 + Char.curr_col); + h = is_spike_harmful(tp); + if ((h >= 2 && ((frame >= FRAME_7_RUN && frame < 15) || + (frame >= FRAME_34_RUNJUMP_START && frame < 40))) || + ((frame == FRAME_43_RUNJUMP_LAND || frame == FRAME_26_STANDJUMP_LAND) && h != 0)) + spiked(tp); +} + +/* check_spike_below (seg006:1199): для каждой колонки РЕАЛЬНОГО габарита + * спрайта (char_x_left..char_x_right, порт set_char_collision) скан ВНИЗ от + * curr_row через НЕ-floor тайлы; встретил пику → start_anim_spike. Габарит, + * а не «curr_col + колонка спереди»: иначе пики выдвигаются на тайл раньше + * (когда тело Кида ещё не над шахтой). Метрики кадра — pop_cd (габарит + * последнего нарисованного кадра; отставание в 1 кадр несущественно, ошибка + * в позднюю сторону). */ +static void check_spike_below(void) +{ + const pop_cdraw_t *cd = CD_ACT; + int wh = ((int)cd->fpw + 1) >> 1; /* char_width_half */ + int xl = cd->fpx / 2 + 58; /* char_x_left */ + int xr; + int8_t c0, c1, col; + if (Char.direction >= 0) xl -= wh; /* лицом вправо: левый край внутрь */ + xr = xl + wh; /* char_x_right */ + c0 = get_tile_div_mod_m7(xl); + c1 = get_tile_div_mod_m7(xr); + for (col = c0; col <= c1; col++) { + int8_t row = Char.curr_row; + if (col < 0 || col > 9) continue; + for (;;) { + uint8_t t = get_tile(col, row); + if (t == TILE_SPIKE) { start_anim_spike((uint8_t)(row * 10 + col)); break; } + if (!tile_is_floor(t) && row <= 2) { row++; continue; } + break; + } + } +} + +/* chomped (seg004:07BF): персонажа перемололо. Тайл помечается кровью + * (0x80 в modif), персонаж выравнивается по колонке чомпера, теряет все HP + * и уходит в seq_54. Условие «кадр ещё не 178» — чтобы не перезапускать + * последовательность каждый кадр, пока труп лежит в челюстях. + * + * Кровь пишется ДАЖЕ у скелета: SDLPoP чинит это опцией + * fix_skeleton_chomper_blood, но в ВАНИЛИ она есть, и мы держим ваниль. */ +static void chomped(uint8_t tilepos, int8_t col) +{ + pop_trob_modif(g_room)[tilepos] |= 0x80; + if (Char.frame == FRAME_178_CHOMPED) return; + Char.x = (uint8_t)(pop_x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX); + Char.x = (uint8_t)char_dx_forward(7 - !Char.direction); + Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; + pop_sfx_play(46); + pop_take_hp(100); + pop_char_set_seq(SEQ_54_CHOMPED); + play_seq(); + if (Char.charid == CHARID_0_KID) pop_kid_dead = 1; +} + +/* check_gate_push (seg004:0833): ОПУСКАЮЩАЯСЯ решётка выталкивает персонажа + * из своего тайла. Без неё Кид, зашедший в тайл ворот при поднятой решётке, + * так и остаётся в нём после опускания — «в суперпозиции»: створка рисуется + * то перед ним, то за ним (слой выбирается по направлению взгляда), и уйти + * он может в обе стороны. Поймано пользователем 2026-08-24; то же и для + * попытки проползти под опускающейся решёткой в присяде. + * + * Условия оригинала — только позы, где персонаж стоит на месте и его можно + * сдвинуть, не ломая анимацию: РАЗВОРОТ (action 7), СТОЙКА (кадр 15) и + * кадры приседа 108..110. Решётка ищется в своём тайле И в тайле СЛЕВА: + * створка нарисована у ПРАВОЙ грани тайла, поэтому левый сосед давит так + * же. Перекрытие обязано быть обеими гранями и в этом кадре, и в прошлом + * (у оригинала `curr_row_coll_flags & prev_coll_flags == 0xFF`) — иначе + * толчок срабатывал бы на пролёте мимо. Сам толчок — 5 пикселей: ВЛЕВО, + * если решётка в тайле персонажа, ВПРАВО — если в левом соседе. */ +static void check_gate_push(void) +{ + int8_t orig_col = Char.curr_col, col, row = Char.curr_row; + uint8_t frame = Char.frame; + + if (Char.action != ACT_TURN && frame != 15 && + !(frame >= 108 && frame < 111)) return; + col = orig_col; + if (get_tile(col, row) != TILE_GATE) { + col--; + if (get_tile(col, row) != TILE_GATE) return; + } + /* Слот флагов годен только внутри ОКОН обоих кадров: вне их лежат + * значения прошлых кадров (та же оговорка, что в check_chomped_flags). */ + if (col < coll_lo || col > coll_hi || + col < coll_prev_lo || col > coll_prev_hi) return; + if ((coll_curr[COLL_IDX(col)] & coll_prev[COLL_IDX(col)]) != 3) return; + if (gate_passable(col, row)) return; /* can_bump_into_gate */ + pop_sfx_play(8); /* bumped_sound (seg004:05F1) */ + Char.x = (uint8_t)((int)Char.x + (orig_col <= col ? -5 : 5)); +} + +/* check_chomped_kid (seg004:076B): по колонкам ряда — там, где персонаж + * перекрывает тайл ОБЕИМИ гранями (у оригинала curr_row_coll_flags == 0xFF, + * у нас оба бита) и этот тайл — СОМКНУТЫЙ чомпер (modif & 0x7F == 2), + * персонажа перемалывает. */ +/* lo..hi — ОКНО, по которому посчитан flags: вне него слоты протухшие (там + * лежат значения прошлых кадров), и «== 3» дал бы фантомный перемол. */ +static void check_chomped_flags(const uint8_t *flags, int8_t lo, int8_t hi) +{ + int8_t row = Char.curr_row, col; + uint8_t tp; + if (row < 0 || row > 2) return; + if (lo < 0) lo = 0; + if (hi > 9) hi = 9; + for (col = lo; col <= hi; col++) { + if (flags[COLL_IDX(col)] != 3) continue; + if (get_tile(col, row) != TILE_CHOMPER) continue; + tp = (uint8_t)(row * 10 + col); + if ((pop_trob_modif(g_room)[tp] & 0x7F) == 2) chomped(tp, col); + } +} + +static void check_chomped_kid(void) { check_chomped_flags(coll_curr, coll_lo, coll_hi); } + +/* check_chomped_guard (seg004:0989) — у оригинала ОТДЕЛЬНОЕ тело: страж не + * проходит через check_collisions, поэтому там перекрытие считается прямо по + * координатам граней (check_chomped_here). У нас та же формула уже живёт в + * get_row_collision_data, поэтому считаем флаги ряда во ВРЕМЕННЫЙ массив и + * идём общим телом. Именно во временный: coll_curr/above/below — это кадр + * КИДА, из них move_coll_to_prev берёт «прошлые» флаги на следующем кадре, и + * затирание сломало бы ему бамп. */ +static void check_chomped_guard(void) +{ + uint8_t flags[COLL_N]; + if (Char.curr_row < 0 || Char.curr_row > 2) return; + set_char_collision(); + calc_coll_window(); /* своё окно; coll_lo/hi Кида не трогаем */ + get_row_collision_data(Char.curr_row, flags); + check_chomped_flags(flags, win_lo, win_hi); +} + +/* ---- Кадровая физика: одна и та же над Char, два входа --------------- * + * Оригинал держит физику ОДНИМ набором функций над Char, а кто именно в + * Char — решают окна loadkid/savekid и loadshad/saveshad (seg006:809). + * Мы теперь так же: kid_phys/guard_phys — это списки вызовов из + * play_kid_frame (seg000:1209) и play_guard_frame (seg000:1246), а сами + * проверки общие. Списки отличаются ровно тем, чем в оригинале. */ + +static void kid_phys(void) +{ + if (grab_timer) grab_timer--; + /* do_timers (seg003:0517): срок действия пера тикает раз в игровой кадр. + * Место — у Кида, а не у стража: эффект в оригинале снимается общим + * таймером главного цикла, а у нас единственный кадровый тик Кида. */ + if (pop_feather) pop_feather--; + fall_accel(); + fall_speed(); + determine_col(); + bump_into_opponent(); /* seg003: безоружный Кид отскакивает от стража */ + check_collisions(); /* seg004: флаги перекрытия по колонкам ряда */ + pop_dbg_p6(); /* ЗАМЕР: check_collisions целиком */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + check_bumped(); /* удержать у стены до check_action (порядок PoP) */ + check_gate_push(); /* seg004: опустившаяся решётка выталкивает из тайла */ + check_action(); + pop_dbg_p7(); /* ЗАМЕР */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + check_press(); /* seg006: стойка/пробой loose → make_loose_fall */ + pop_dbg_p8(); /* ЗАМЕР */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + check_spike_below(); /* seg006: над колонкой с пиками → выдвинуть пики */ + check_spiked(); /* seg006: напоролся на вредные пики → смерть */ + check_chomped_kid(); /* seg004: перемололо в сомкнутых челюстях → смерть */ + /* upside_down снимается смертью (seg000:1224: `if (upside_down && + * Char.alive >= 0)`). У нас признак смерти — pop_kid_dead, ставится + * там же, где оригинал переводит alive в >= 0. */ + if (pop_kid_dead && pop_upside_want) { pop_upside_want = 0; pop_upside_dirty = 1; } + if (pop_kid_dead) return; /* только что умер — без leave/knock в этот кадр */ + pop_check_knock(); /* seg003: сотрясение приземления → тряска loose в ряду */ + /* exit_room_timer (seg002 exit_room): 2 кадра ПОСЛЕ входа в комнату любой + * leave заблокирован. Без него транзиент отскока о ворота шва (seq_47 + * роняет char_x за порог) сразу же выкидывает Kid в соседнюю комнату + * (ping-pong / баг #4). С таймером первый бамп попадает в окно блокировки, + * Kid оседает у грани (char_x стабильно > порога) — экран не перескакивает. */ + if (pop_leave_timer) { + pop_leave_timer--; + } else { + check_leave_below(); /* seg002: y>=211 → переход в комнату снизу */ + check_leave(); /* seg002: уход вбок → переход в соседнюю комнату */ + } +} + +/* play_guard_frame (seg000:1252..1266). Чего у стража НЕТ по сравнению с + * Кидом: bump_into_opponent, check_collisions/check_bumped (у него свой + * check_guard_bumped), check_gate_push, check_knock и весь уход из комнаты. + * check_chomped_guard — то же тело, что у Кида (у нас check_chomped). */ +static void guard_phys(void) +{ + fall_accel(); + fall_speed(); + determine_col(); + check_guard_bumped(); + check_action(); + check_press(); /* страж тоже давит кнопки и проваливает loose */ + check_spike_below(); + check_spiked(); + check_chomped_guard();/* seg000:1265 */ +} + +/* Физика КИДА — своё окно Char: главный цикл зовёт нас вне окон, а физика + * работает с активным персонажем. Сохраняем ПОЛНОСТЬЮ (pop_savekid_and_opp): + * физика меняет персонажу всё — координаты, ряд/колонку, кадр, seq. */ +void pop_phys_tick(void) __banked +{ + /* РАННЕГО ВЫХОДА ПО pop_kid_dead ЗДЕСЬ НЕТ — и это принципиально. + * У оригинала play_kid_frame (seg000:1231) гейт ровно один, `Char.room + * != 0`: вся цепочка крутится и на ТРУПЕ. Стоял `if (pop_kid_dead) + * return;` — дёшево и почти незаметно, но вместе с физикой он выключал + * check_press, а тот на мёртвом персонаже делает совсем не то, что на + * живом (died_on_button — кнопка ломается насовсем). Наблюдалось как + * «Кид разбился на кнопке открытия решётки, а решётка закрылась» + * (уровень 7, комната 3, тайл 2,1): кнопка получала РОВНО ОДНО нажатие — + * в кадре самой смерти, потому что pop_kid_dead ставит land() уже внутри + * цепочки. Разбор — BUGS_CLOSED.md#died-on-button. + * + * «Труп не шевелится» держится не этим выходом, а тем же, чем в + * оригинале: кадр смерти не двигается сам (dx/dy нулевые, sequence + * кончился), а уход из комнаты и сотрясение отсечены внутри kid_phys. */ + pop_loadkid_and_opp(); + pop_dbg_p1(); /* ЗАМЕР: окно Char загружено */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + kid_phys(); + pop_savekid_and_opp(); +} + +/* Физика СТРАЖА. Зовётся из главного цикла (резидент), а не из guards.c: + * цена та же — один трамплин на кадр, — но банк 1 не зовёт банк 3, а окно + * Char стоит копию 16 байт. */ +void pop_guard_phys_tick(void) __banked +{ + /* Тот же гейт, что в pop_guard_tick: dir_56_none = персонаж выключен + * (clear_char), физику ему тоже не крутим. */ + if (Guard.charid == 0 || Guard.direction == DIR_56_NONE || + Guard.room != g_room) return; + pop_loadshad_and_opp(); + /* seg000:1254 — физика работает, ТОЛЬКО пока страж внутри комнаты: за + * пределами полосы персонаж просто перестаёт двигаться, а + * провалившегося ниже убирает check_guard_fallout (seg002:0241). + * + * Это значит «своим ходом из комнаты не уйдёт», но НЕ «остаётся в ней + * всегда»: когда комнату меняет КИД, exit_room (seg002:03C7) может + * перенести стража следом — follow_guard, если тот жив, с вынутым + * мечом, стоит у нужного края (x < 91 при уходе влево, x >= 165 при + * уходе вправо) и в целевой комнате нет своего живого стража. У нас + * follow_guard пока не портирован (GUARD-PHYS в TASKS_OPEN.md). */ + if (Char.x >= 44 && Char.x < 211) { + pop_load_fram_det_col(); /* load_frame_to_obj + load_fram_det_col */ + guard_phys(); + } + pop_saveshad_and_opp(); +} + +/* ---- Зеркало уровня 4 (порт seg003:0798..08A9) ---------------------- * + * + * Тайл зеркала ставится в рантайме (pop_trob.c place_mirror), а «стеной + * слева» он уже был — wall_type = 2 портирован давно. Здесь вторая часть: + * что происходит ПОСЛЕ того, как Кид пролетел сквозь него беговым прыжком + * (флаг pop_jumped_mirror взводит is_obstacle выше по файлу). */ + +/* check_mirror_image (seg003:085B). МУТИРУЕТ Char НА МЕСТЕ: x отражается + * относительно оси зеркала, direction инвертируется. Возвращает + * distance_mirror — насколько персонаж вошёл в тайл (оригинал по нему + * решает, рисовать ли отражение; нам это понадобится на шаге «отражение»). + * + * Мутация Char здесь ЗАДУМАНА, а не побочный эффект: оригинал зовёт эту + * функцию из двух мест, и оба на неё рассчитывают — при рисовании отражения + * (в Char временная копия Кида, обратно она не сохраняется) и при рождении + * тени (мутированный Char уходит в слот Guard). */ +static int8_t mirror_image(void) +{ + /* +10 — смещение оси зеркала внутри тайла; в оригинале так и написано + * комментарием («I think 10 is the offset for mirror collision»). */ + int xpos = pop_x_bump[Char.curr_col + FIRST_ONSCREEN_COLUMN] + 10; + int distance = distance_to_edge_weight(); + if (Char.direction >= 0) distance = ~distance + TILE_SIZEX; + Char.x = (uint8_t)((xpos << 1) - (int)Char.x); + Char.direction = (int8_t)~Char.direction; + return (int8_t)(distance - 2); +} + +/* jump_through_mirror (seg003:080A): рождение ТЕНИ. Кид копируется в слот + * Guard уже отражённым; HP меняются местами — у тени полное здоровье Кида, у + * самого Кида остаётся 1. Полосы HP отдельно перерисовывать не надо: + * pop_hp_draw сравнивает числа и смену увидит сам. + * + * savekid здесь НЕТ и в оригинале: собственные x/direction Кида остаются + * прежними, отражается только копия, уходящая в слот Guard. */ +static void jump_through_mirror(void) +{ + pop_loadkid(); + pop_load_frame(); /* mirror_image смотрит на dx/flags КАДРА */ + (void)mirror_image(); + pop_jumped_mirror = 0; + Char.charid = CHARID_1_SHADOW; + pop_sfx_play(45); /* seg003:0617 — прыжок в зеркало */ + pop_saveshad(); + guardhp_max = guardhp_curr = hitp_max; + hitp_curr = 1; +} + +/* Линия клипа отражения СВЕРХУ — y_clip[curr_row+1] оригинала. Таблица та + * же, что в pop_clip_char_top; держим её здесь копией из четырёх значений, + * а не тащим наружу: ряд у отражения всегда 0..2. */ +static int mirror_clip_top(int8_t row) +{ + static const int16_t y_clip[5] = { -60, 3, 66, 129, 192 }; + int8_t r = (int8_t)(row + 1); + if (r < 0 || r > 4) return 0; + return (int)y_clip[r]; +} + +void pop_check_mirror(void) __banked +{ + if (pop_jumped_mirror == -1) { jump_through_mirror(); return; } + /* Отражение: пока Кид СТОИТ на тайле зеркала, рисуем его зеркальную + * копию (в оригинале — add_objtable(4)). Условия оригинала: тайл под + * персонажем — зеркало, distance_mirror >= 0 (вошёл в тайл достаточно + * глубоко) и его комната = отрисованная. */ + if (Kid.room != g_room) return; + pop_loadkid(); + if (get_tile_at_char() != TILE_MIRROR) return; + pop_load_frame(); + if (mirror_image() >= 0) + pop_mirror_draw(mirror_clip_top(Char.curr_row)); + /* Char остался ОТРАЖЁННЫМ — как в оригинале: обратно он не сохраняется, + * а следующий тик перезагружает окно из Kid. */ +} + +/* Закрытое состояние физики, которое влияет на следующие кадры. Таблицы + * коллизии и кэши соседей намеренно не входят: их пересчитает вход в комнату. */ +void pop_map_qsave(pop_qs_io_t *io) __banked +{ + pop_qs_u8(io, &is_screaming); + pop_qs_u8(io, &grab_timer); + pop_qs_i8(io, &pickup_obj_type); + pop_qs_u16(io, &loose_seed.lo); + pop_qs_u16(io, &loose_seed.hi); + pop_qs_u8(io, &last_loose_snd); + if (io->reading && io->apply) pop_coll_invalidate(); +} diff --git a/applications/SprPoP/src/pop_map.h b/applications/SprPoP/src/pop_map.h new file mode 100644 index 0000000..e34c3d9 --- /dev/null +++ b/applications/SprPoP/src/pop_map.h @@ -0,0 +1,285 @@ +/* + * pop_map.h — коллизия Kid с картой комнаты (Фаза K3): тайловые запросы + * + физика падения + приземление. Порт SDLPoP seg005/seg006 + * (check_on_floor/start_fall/do_fall/fall_accel/fall_speed/land/in_wall). + * + * K3.2 (падение): Kid, сойдя/сбежав с края пола (тайл под ним не floor), + * начинает падать (fall_y-ускорение, Char.y растёт), приземляется на пол + * ниже (soft/medium land) или продолжает вниз. Стена под персонажем — + * in_wall (выталкивание). Бег В стену (check_bumped) — отдельным шагом. + */ +#ifndef POP_MAP_H +#define POP_MAP_H + +#include +#include "pop_char.h" /* pop_char_t — окклюзия ворот спрашивает про персонажа */ +#include "pop_qsave.h" + +/* Задать карту текущей комнаты: fg[30] = коды тайлов (row*10+col, 3×10). + * col<0/>9 или row<0/>2 трактуются как стена (край уровня). Массив + * МУТАБЕЛЬНЫЙ (loose-пол при падении → empty) — передавать ту же копию, + * что и в pop_room_draw, чтобы отрисовка видела изменения. */ +void pop_map_set(uint8_t *fg) __banked; + +/* Связи комнаты (0=нет соседа; иначе номер). Заодно кэширует fg соседей + * слева и справа ЦЕЛИКОМ (читает уровень сам). Оригинал добирается до любой + * колонки через find_room_of_tile, у нас уровень в EMM-странице и маппить её + * на каждый get_tile (горячий путь физики) дорого — поэтому копия соседей. + * Колонка персонажа считается из x и на диапазоне x 0..255 лежит в −5..13, + * то есть заведомо выходит за комнату; кромочных колонок не хватало, и + * get_tile отдавал мнимую стену (BUG-SWORD-GHOST-1). Звать в enter_room. */ +void pop_map_set_edges(uint8_t left, uint8_t right, uint8_t up, uint8_t down) __banked; +/* Перечитать fg соседей (кромочные колонки коллизии) из уровня, не сбрасывая + * историю перекрытий. Нужен, когда тайл соседа изменился, пока тот не + * отрисован — см. pop_neigh_dirty в pop_state.h. */ +void pop_map_refresh_edges(void) __banked; + +/* БОКОВОЙ переход в соседнюю комнату: перенумеровать историю флагов + * перекрытия вместо того, чтобы её выбросить (иначе Кид с разбега проходит + * сквозь закрытые ворота шва — BUG-GATE-PASS-1). dcol — на сколько + * изменились номера колонок: ушли ВПРАВО −10, ВЛЕВО +10. Звать СРАЗУ после + * pop_map_set_edges и только для боковых переходов; вверх/вниз и все прочие + * входы в комнату остаются на полной инвалидации. Разбор — BUGS_CLOSED.md, + * расхождение с дословным портом — ../docs/impl_diff.md. */ +void pop_coll_shift(int8_t dcol) __banked; + +/* check_skel: тайл скелета -> пол + пометка перерисовки (обе страницы). */ +void pop_skel_wake_tile(uint8_t tilepos) __banked; + +/* Openness ворот в (col,row) с резолвом за край комнаты (соседи слева/справа). + * 0xFF — тайла нет. Нужен лучу видимости стража (guards.c). */ +uint8_t pop_gate_modif(int8_t col, int8_t row) __banked; + +/* Номер ТЕКУЩЕЙ комнаты (для доступа к room_modif пик/ворот в pop_trob). + * Звать в enter_room. */ +void pop_map_set_room(uint8_t room) __banked; + +/* Тайл-код текущей комнаты по клетке (col 0..9, row 0..2) — порт + * get_tile(room,col,row) для текущей комнаты. Нужен лучу видимости стража + * (guards.c): он идёт по ряду между Кидом и стражем. */ +uint8_t pop_tile_at(int8_t col, int8_t row) __banked; + +/* Тайлы отрезка ряда c0..c1 одним банковым вызовом (см. тело в pop_map.c): + * луч видимости стража иначе платит по трамплину за КАЖДУЮ колонку. */ +void pop_row_tiles(int8_t row, int8_t c0, int8_t c1, uint8_t *out) __banked; + +/* determine_col (seg006:014D): Kid.curr_col = m7(dx_weight()). Наружу — для + * pop_load_fram_det_col (pop_kid.c), порт load_fram_det_col. */ +void pop_determine_col(void) __banked; + +/* HP/смерть. pop_kid_dead=1 когда Kid убит (пики); hitp_curr — текущее HP. + * pop_kid_hp_reset() ставит старт HP и снимает смерть (звать в kid_init/ + * респавн). */ +extern uint8_t pop_kid_dead; +extern uint8_t hitp_curr; +extern uint8_t hitp_max; /* стартовое HP (для индикатора) */ +extern int8_t hitp_delta; /* урон за кадр от меча; применяет pop_do_delta_hp */ +/* check_fall_flo (seg000:1317) — спецсобытие уровня 13: вход в комнату 23 + * или 16 роняет на Кида гряду плит из комнаты СВЕРХУ, каждую со своей + * задержкой. Звать при входе в комнату ПОСЛЕ pop_loose_reset (порядок + * check_the_end: start_chompers → check_fall_flo → check_shadow). */ +void pop_check_fall_flo(void) __banked; + +void pop_kid_hp_reset(void) __banked; +/* То же для БЕСШОВНОГО старта уровня (pop_seamless): HP не трогаем вовсе. */ +void pop_kid_hp_carry(void) __banked; + +/* hitp_beg_lev (seg003): HP, с которым НАЧАТ текущий уровень. Рестарт + * уровня (смерть/выпадение) откатывает HP ровно к нему; пройденный уровень + * подтягивает его к hitp_max (play_level_2, seg003:410) — так «жизнь» из + * большой склянки переезжает на следующий уровень. Правит главный цикл. */ +extern uint8_t hitp_beg_lev; +#define POP_START_HITP 3 /* custom->start_hitp */ +#define POP_MAX_HITP 10 /* custom->max_hitp_allowed */ +void pop_kid_hp_newgame(void) __banked; /* начало ИГРЫ: hitp_beg_lev = 3 */ + +/* Предметы (seg005 check_get_item/get_item, seg006 do_pickup/proc_get_object). + * pop_have_sword — «меч у Кида» (порт have_sword). pop_item_taken — 0 или + * tilepos+1 только что поднятого предмета: вызывающий обязан запечь тайл + * (он стал полом) на ОБЕИХ страницах и записать override комнаты. */ +extern uint8_t pop_have_sword; +extern uint8_t pop_item_taken; +/* Вспышка фона (do_flash/set_bg_attr, seg001:0908): счётчик кадров, пока + * НУЛЕВАЯ запись палитры подменена ярко-жёлтым. Тикает вызывающий. */ +extern uint8_t pop_flash_time; +/* Цвет вспышки (порт flash_color, seg006 proc_get_object / seg000 feather_fall): + * жёлтая — подобран меч, красная — зелья HP, зелёная — перо. */ +#define POP_FLASH_YELLOW 0 +#define POP_FLASH_RED 1 +#define POP_FLASH_GREEN 2 +#define POP_FLASH_WHITE 3 /* тень: слияние и её смерть (уровень 12) */ +extern uint8_t pop_flash_color; + +/* add_life (seg000:11EC): +1 к ПОТОЛКУ HP и долив до него. Наружу — ради + * слияния с тенью (уровень 12, guards.c в другом банке): большая склянка + * зовёт ту же ветку изнутри модуля. */ +void pop_add_life(void) __banked; + +/* is_feather_fall: кадров, пока действует зелье медленного падения. Читают + * play_seq (опкод JMP_IF_FEATHER — ветки stepfloat/bumpfloat в seqtbl) и + * приложение (сброс на старте уровня, seg003:189). */ +extern uint8_t pop_feather; + +/* upside_down (seg000:15E9): 1 — картинка перевёрнута вверх ногами (зелье + * типа 4 на уровне 9). Читают все слои отрисовки; сбрасывается стартом + * уровня и смертью Кида (seg000:1224). + * pop_upside_dirty — порт need_redraw_because_flipped: флаг «состояние + * переворота ИЗМЕНИЛОСЬ», по нему главный цикл один раз переворачивает уже + * нарисованное (pop_flip_screen) и снимает флаг. */ +extern uint8_t pop_upside; /* ТЕКУЩЕЕ — его читают все слои отрисовки */ +extern uint8_t pop_upside_want; /* ЖЕЛАЕМОЕ — сюда пишут зелье и смерть */ +extern uint8_t pop_upside_dirty; +uint8_t pop_get_item_action(void) __banked; /* 0 нет / 1 присесть / 2 поднять меч / 3 пить */ +void pop_proc_get_object(void) __banked; /* эффект — из play_seq (опкод GET_ITEM) */ + +/* Уход из комнаты ВБОК/ВВЕРХ: 0=нет; 1=left,2=right,3=up (= side+1 для + * pop_room_link). Координаты Kid уже репроецированы (x∓140 / y+189). + * Приложение переключает комнату по соответствующей связи и снимает флаг. */ +extern uint8_t pop_leave_dir; + +/* exit_room_timer (порт seg002 exit_room): пока >0, боковой/нижний leave + * заблокирован (декремент в pop_phys_tick). Приложение ставит =2 при входе + * в комнату (enter_room) — 2 кадра защиты от повторного/транзиентного leave + * (ping-pong у шва, транзиент отскока о ворота, баг #4/BUG-2). */ +extern uint8_t pop_leave_timer; + +/* Loose floors (проваливающиеся полы). pop_loose_modif[30] — состояние на + * тайл (0 покой / 0x80.. тряска / 1..11 отсчёт падения); читает pop_bg при + * отрисовке. pop_loose_tick() — анимация + перерисовка изменившихся тайлов + * на ТЕКУЩЕЙ странице; звать в основном цикле ПОСЛЕ set_draw_page(back), + * рядом с kid-отрисовкой. */ +extern uint8_t pop_loose_modif[30]; +void pop_loose_tick(void) __banked; +void pop_loose_reset(void) __banked; /* сброс loose-состояния при смене комнаты */ +/* Отдать НЕЗАКОНЧЕННЫЕ loose уходящей комнаты в персистентный room_modif + + * список trob, чтобы они досчитались заочно (в оригинале loose и есть trob, + * а do_trobs крутит список независимо от drawn_room). Звать ПЕРЕД любой + * перезагрузкой комнаты, пока карта ещё описывает СТАРУЮ. */ +void pop_loose_leave_room(void) __banked; +/* СТАРТ/РЕСТАРТ уровня: забыть фазы БЕЗ сдачи (load_level сносит всё сам). + * Звать рядом с pop_trob_reset, до первого enter_room. */ +void pop_loose_forget(void) __banked; + +/* Персонаж стоит НА тайле ворот, значит их решётку надо вернуть ПОВЕРХ него + * (порт условия draw_tile_fore, seg008:0D15 — разбор в pop_map.c). Бары + * рисуются в колонке ch->curr_col + 1: графика ворот принадлежит правому + * краю своего тайла. Рисование — pop_fore_over_char (pop_bg.c). */ +uint8_t pop_gate_over_char(const pop_char_t *ch) __banked; + +/* Установлен в tilepos+1 в кадре, когда loose-пол упал (кусок улетел ВНИЗ). + * Приложение: определить debris-приземление в комнате снизу. Снять (=0). */ +extern uint8_t pop_loose_fell; + +/* Плита-ПОТОЛОК: loose ряда 2 комнаты СВЕРХУ (в оригинале — тот же loose с + * отрицательным tilepos, seg006 check_press frame 79 / seg007 animate_loose). + * pop_map_set_above даёт МУТАБЕЛЬНУЮ копию ряда 2 верхнего соседа (ту же, что + * ушла в pop_room_set_above): провалившаяся плита становится в ней EMPTY — + * сверху открывается колодец. pop_ceil_modif[col] — состояние (как + * pop_loose_modif), читает pop_bg. Сигналы приложению (для персистентности + * между входами в комнату): pop_ceil_fell = col+1 провалившейся плиты, + * pop_debris_at = tilepos+1 щебня, оставшегося в ЭТОЙ комнате. Снимать (=0). */ +void pop_map_set_above(uint8_t *above_row2_fg) __banked; +extern uint8_t pop_ceil_modif[10]; +extern uint8_t pop_ceil_fell; +extern uint8_t pop_debris_at; + +/* Взводится в кадре, когда Kid получил урон (hitp_delta<0 оригинала) — + * отрисовка персонажа рисует «брызги» поверх Kid, а главный цикл снимает + * флаг. */ +extern uint8_t pop_kid_hurt; + +/* check_knock (seg003): по флагу knock (play_seq) тряхнуть loose-полы ряда + * приземления/удара. Зовётся из pop_phys_tick. */ +void pop_check_knock(void) __banked; + +/* Установлен в 1, когда Kid пересёк НИЖНЮЮ границу комнаты (провал в дыру + * или спуск-зацеп с кромки). Координаты Kid УЖЕ репроецированы в кадр + * комнаты снизу (y -= 189, curr_row = 0). Приложение решает: если есть + * links.down — сменить комнату (pop_room_load+перерисовка) и продолжить; + * иначе — выпал из уровня (респавн). Снять флаг после обработки. */ +extern uint8_t pop_fell_out; +void pop_map_qsave(pop_qs_io_t *io) __banked; + +/* Пер-кадровая физика ПОСЛЕ kid_tick(play_seq): fall_accel + fall_speed + + * determine_col + check_action (триггер падения / do_fall / приземление). + * Порядок как в оригинале (seg000 play_kid_frame). + * + * Физика внутри работает с АКТИВНЫМ персонажем (Char), как в оригинале, и + * сама открывает окно loadkid/savekid — звать вне окон Char. */ +void pop_phys_tick(void) __banked; + +/* То же для стража — порт хвоста play_guard_frame (seg000:1246). Звать из + * главного цикла СРАЗУ после pop_guard_tick() (тот прокручивает ИИ и + * последовательность), тоже вне окон Char: функция открывает своё. + * + * Физика у стража та же самая (одни функции над Char), отличается только + * список вызовов: нет ухода из комнаты, нет check_knock, вместо check_bumped + * — check_guard_bumped. Тот же вход обслужит скелета/тень/визиря, когда + * они появятся: в оригинале все они живут в слоте Guard. + * + * «Нет ухода из комнаты» — про СВОЙ ход. Следом за Кидом стража переносит + * exit_room/follow_guard (seg002:03C7/039E) — отдельная механика, у нас не + * портирована (GUARD-PHYS). */ +void pop_guard_phys_tick(void) __banked; + +/* clip_char (seg006:1749), верхняя граница клипа спрайта персонажа: если тайл + * над головой — стена/пол, всё выше линии своей полосы не рисуется (Kid, + * ушедший головой под пол при спуске/висе, не просвечивает сквозь щели). + * obj_x — ЛОГИЧЕСКАЯ X кадра (до ×8/7), obj_y — низ спрайта, w/h — размер. + * Возврат: КОМНАТНЫЙ y границы, 0 = клипа нет. Зовёт pop_char_draw. */ +int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) __banked; + +/* Парная половина clip_char: ЭКРАННЫЙ X, правее которого спрайт не рисуется + * (правый косяк двери уровня в кадрах подъёма 224..228). 0 = обрезки нет. */ +int pop_clip_char_right(void) __banked; + +/* Тип препятствия/края ВПЕРЕДИ по направлению (pop_edge_type после + * pop_edge_distance): для control (бежать / осторожный шаг / стоп у стены). */ +#define EDGE_CLOSER 0 /* край уступа над ямой (дистанция = до кромки) */ +#define EDGE_WALL 1 /* стена впереди/под персонажем */ +#define EDGE_FLOOR 2 /* пол (нет препятствия рядом, дистанция = 11) */ + +/* Расстояние до края/стены впереди + побочно g_edge_type (pop_edge_type). */ +int pop_edge_distance(void) __banked; +uint8_t pop_edge_type(void) __banked; +/* Тайл, на котором остановился разбор края (curr_tile2 оригинала). */ +uint8_t pop_edge_tile(void) __banked; + +/* Стена впереди в пределах стопа? Если да — прижимает Kid.x к корректной + * позиции (передний край у стены, симметрично) и возвращает 1. Для + * control_running (чистый стоп у стены). */ +int pop_wall_ahead(void) __banked; + +/* Разбег-прыжок: выравнивание по кромке пола (run_jump, seg005:0AA8). + * Смотрит на 1–2 тайла вперёд; если там провал/пика — подгоняет Kid.x под + * толчок ровно с кромки. 1 = прыгать, 0 = ОТМЕНИТЬ прыжок (Кид не в той + * фазе относительно кромки; control_up гасить НЕЛЬЗЯ — попытка повторится + * на следующем кадре). Без этого провал в три тайла непроходим. */ +uint8_t pop_run_jump_align(void) __banked; + +/* Прыжок вверх в стойке (↑): seq чистого прыжка (seq_28/seq_14) ЛИБО + * прыжка-с-зацепом за уступ выше (seq_8/24/16) + выравнивание Kid.x. + * check_jump_up/grab_up (K4.4). control_up гасит caller. */ +uint8_t pop_jump_up_seq(void) __banked; + +/* ---- Вис/зацеп/спуск (K4.2/K4.3) — тайловые решения для pop_ctrl --- * + * Управление (control_hanging/can_climb_up/hang_fall/down_pressed) в + * pop_ctrl; здесь — предикаты + выбор seq (+ выравнивание Kid.x). */ +uint8_t pop_hang_grab_ready(void) __banked; /* grab_timer истёк — можно climb-up */ +uint8_t pop_hang_climb_up_seq(void) __banked; /* seq_10 / seq_73 (подтягивание) */ +int8_t pop_hang_wall(void) __banked; /* Shift в висе: 1=у стены,-1=fall,0=держ.*/ +uint8_t pop_hang_fall_seq(void) __banked; /* seq_23 / seq_11 (сорваться), +Kid.x */ +uint8_t pop_down_action(void) __banked; /* 1=обработано(спуск/отступ), 0=присед */ + +/* Дверь уровня рядом и ОТКРЫТА ПОЛНОСТЬЮ? → поставить Кида в проём (x + + * разворот влево, go_up_leveldoor) и вернуть 1; иначе 0, Кид не тронут. + * Ветка двери уровня из up_pressed (seg005:0482). Последовательность + * seq_70 ставит вызывающий (pop_ctrl) — тайлы наши, управление его. */ +uint8_t pop_leveldoor_enter(void) __banked; + +/* Зеркало уровня 4 (check_mirror, seg003:0798). Звать раз в кадр ПЕРЕД + * отрисовкой персонажей — в оригинале это первая строка draw_people + * (seg008:228A). Родит тень, если Кид только что пролетел сквозь зеркало. */ +void pop_check_mirror(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_menu.c b/applications/SprPoP/src/pop_menu.c new file mode 100644 index 0000000..5305218 --- /dev/null +++ b/applications/SprPoP/src/pop_menu.c @@ -0,0 +1,767 @@ +/* + * pop_menu.c — pause menu, шрифты и временный UI-слой, БАНК 9. + * + * Отрисовка принципиально идёт GFX_BANK_SPRITE: прозрачный цвет 0xFF + * пропускается, а пауза не становится частью фоновой ОЗУ-копии игры. + * Каждый кадр UI собирается на скрытой + * видеостранице, флипается целиком и затем стирается копией чистого фона. + * Поэтому при переходе между пунктами нет видимой построчной перерисовки. + * + * Шрифты — исходные hc_small_font_data/hc_font_data из SDLPoP, заранее + * переведённые toolchain/pop_extract_font.py в FONT\\FONT.ATL. Атлас в W0 + * нужен ещё и как временное хранилище снимка палитры: затемнение не тратит + * 1 КБ дефицитной W2 и всегда восстанавливает точные цвета уровня. + */ +#include +#include +#include +#include +#include +#include "pop_menu.h" +#include "pop_pal.h" +#include "pop_ui.h" +#include "pop_font.h" /* POP_FONT_*_ASCENT — раскладка считается по шрифту */ +#include "pop_bg.h" /* POP_YOFF/POP_PLAYFIELD_H — границы игровой области */ +#include "pop_cdraw.h" /* POP_HP_Y — нижнюю статус-полосу делим с HP */ +#include "pop_pace.h" +#include "pop_settings.h" +#include "pop_cfg.h" +#include "pop_qsave.h" +#include "pop_status.h" +#include "pop_sfx.h" + +/* --- ОБЩАЯ ВЕРТИКАЛЬНАЯ РАСКЛАДКА ------------------------------------- * + * + * Экран у нас 320x256, но ИГРА занимает только 200 строк: 192 поля тайлов + * плюс 8 строк полосы HP, начиная с POP_YOFF. Всё, что выше и ниже, — + * борта, и на реальном ТВ/мониторе они могут быть срезаны. Поэтому каждый + * экран UI центрируется в этой самой полосе, а не в 256 строках кадра, и + * обязан в неё помещаться целиком. + * + * Блок считается «сверху вниз»: UI_CENTER_TOP отдаёт строку, с которой + * начинается силуэт содержимого (не базовая линия!). Первая базовая линия + * = верх + ascent шрифта, дальше — шаги. */ +#define UI_AREA_TOP POP_YOFF +#define UI_AREA_H (POP_PLAYFIELD_H + 8) /* поле + полоса HP = 200 */ +#define UI_CENTER_TOP(h) (UI_AREA_TOP + (UI_AREA_H - (h)) / 2) +/* Отдельный вариант для экранов, у которых нижняя статус-полоса занята + * своим содержимым: центрируем только по полю тайлов. */ +#define UI_CENTER_FIELD(h) (UI_AREA_TOP + (POP_PLAYFIELD_H - (h)) / 2) + +/* Рамка выделения строки. Заглавные малого шрифта занимают РОВНО пять + * строк [base-ascent .. base-1], поэтому симметричный отступ считается от + * них, а не от базовой линии: SEL_PAD строк воздуха сверху и снизу плюс + * сама линия рамки. Раньше низ рамки брался «на глаз» (base+5), и она + * визуально сидела ниже текста. */ +#define SEL_PAD 3 +#define SEL_TOP_DY (POP_FONT_SMALL_ASCENT + SEL_PAD + 1) /* 9 */ +#define SEL_BOT_DY (SEL_PAD) /* 3 */ + +#define MENU_N_ITEMS 7 +#define MENU_STEP 14 +#define MENU_LIST_H (SEL_TOP_DY + (MENU_N_ITEMS - 1) * MENU_STEP + \ + SEL_BOT_DY + 1) +/* Центрируем СПИСОК по полю тайлов: GAME PAUSED — не пункт меню, а строка + * состояния программы, и в блок центрирования не входит. */ +#define MENU_FIRST_BASE (UI_CENTER_FIELD(MENU_LIST_H) + SEL_TOP_DY) +/* Оригинал пишет её в нижнюю статус-полосу, между делениями HP Кида слева и + * стража справа (SDLPoP rect_bottom_text = {193,70,202,250}, valign_bottom): + * базовая линия на строку ниже полосы, силуэт крупного шрифта ложится ровно + * на неё. */ +#define MENU_PAUSED_BASE (POP_YOFF + POP_HP_Y + 8) +#define MENU_SEL_L 115 +#define MENU_SEL_R 205 +#define MENU_BORDER 0x37 /* светло-серый VGA16, не затемняем */ + +#define UI_LEFT 45 +#define UI_RIGHT 274 +#define UI_VALUE_RIGHT 257 + +#define SETTINGS_N_ITEMS 9 +/* Три секции по одному заголовку: T -> GENERAL, i2 -> GAMEPLAY, + * i6 -> CONTROLS. Между заголовком и строками, как и между строками, — + * фиксированные шаги, поэтому вся высота выражается через них. */ +#define SET_STEP 14 +#define SET_HEAD_GAP 17 +#define SET_BLOCK_H (POP_FONT_BIG_ASCENT + 3 * SET_HEAD_GAP + \ + 9 * SET_STEP + SEL_BOT_DY + 1) +#define SET_TITLE_BASE (UI_CENTER_TOP(SET_BLOCK_H) + POP_FONT_BIG_ASCENT) +#define SET_HEAD1 (SET_TITLE_BASE + SET_HEAD_GAP) +#define SET_HEAD2 (SET_HEAD1 + 4 * SET_STEP + SET_HEAD_GAP) +#define SET_HEAD3 (SET_HEAD2 + 3 * SET_STEP + SET_HEAD_GAP) + +/* CONTROLS: число строк зависит от того, включены ли читы, поэтому верх + * блока считается в рантайме. */ +#define CTRL_TITLE_GAP 20 +#define CTRL_N_BASE 7 /* общие клавиши */ +#define CTRL_N_CHEAT 5 /* дополнительные строки читов */ +#define CTRL_COL_KEY 45 +#define CTRL_COL_DESC 145 + +/* ABOUT = ровно тот же текст, что на стартовом Sprinter screen, минус + * строка про клавиши. ABOUT_H — и высота блока, и смещение ПОСЛЕДНЕЙ + * базовой линии от его верха (совпадают, т.к. последняя строка малая). */ +#define ABOUT_GAP1 30 +#define ABOUT_GAP2 15 +#define ABOUT_H (POP_FONT_BIG_ASCENT + 2 * ABOUT_GAP1 + ABOUT_GAP2) +#define BUILD_KEYS_GAP 48 + +/* Подтверждение: рамка карточки, вопрос крупным, две кнопки. */ +#define CONF_BOX_H 66 +#define CONF_TOP UI_CENTER_TOP(CONF_BOX_H) +#define CONF_Q_BASE (CONF_TOP + 20) +#define CONF_BTN_BASE (CONF_TOP + 50) + +/* Уведомление: крупное сообщение + строка про клавиши. */ +#define NOTICE_GAP 44 +#define NOTICE_H (POP_FONT_BIG_ASCENT + NOTICE_GAP) +#define NOTICE_BASE (UI_CENTER_TOP(NOTICE_H) + POP_FONT_BIG_ASCENT) + +#define UI_KEYS_TEXT "ESC, ENTER OR SPACE: CONTINUE" + +/* SDLPoP menu.c: движение = loose_shake_2, подтверждение/Back = + * loose_shake_3, изменение значения = sword_vs_sword. Открытие pause menu + * намеренно без звука. */ +#define MENU_SND_CHANGE 10 +#define MENU_SND_MOVE 21 +#define MENU_SND_SELECT 22 +#define MENU_SND_NONE 0xFF + +/* Значения приходят из Makefile: ID — короткий git commit, DATE — дата + * этого коммита. Fallback оставляет исходник самостоятельным для host/IDE. */ +#ifndef POP_BUILD_ID +#define POP_BUILD_ID "DEV" +#endif +#ifndef POP_BUILD_DATE +#define POP_BUILD_DATE "UNKNOWN" +#endif + +/* Порядок = план §3. SETTINGS — вложенный экран в этом же bank 9. */ +static const uint8_t menu_cmds[MENU_N_ITEMS] = { + POP_MENU_RESUME, + POP_MENU_QUICKSAVE, + POP_MENU_QUICKLOAD, + POP_MENU_RESTART_LEVEL, + POP_MENU_SETTINGS, + POP_MENU_RESTART_GAME, + POP_MENU_QUIT +}; + +static const char * const menu_text[MENU_N_ITEMS] = { + "RESUME", + "QUICKSAVE (F6)", + "QUICKLOAD (F9)", + "RESTART LEVEL", + "SETTINGS", + "RESTART GAME", + "QUIT GAME" +}; + +/* Через bar, а не rectangle: у игры уже есть быстрый rectfill, тогда как + * rectangle тянет тяжёлый универсальный line-движок в и без того тесный W1. */ +static void menu_box(int left, int top, int right, int bottom, uint8_t color) +{ + setfillstyle(SOLID_FILL, color); + bar(left, top, right, top); + bar(left, bottom, right, bottom); + bar(left, top, left, bottom); + bar(right, top, right, bottom); +} + +/* Рамка выделения СТРОКИ: её вертикаль задаёт только базовая линия текста, + * поэтому ни один экран не считает отступы сам. */ +static void menu_sel_box(int left, int right, int baseline) +{ + menu_box(left, baseline - SEL_TOP_DY, right, baseline + SEL_BOT_DY, + MENU_BORDER); +} + +/* Весь UI собирается на скрытой странице. Источник gfx_copy_page — shadow + * видимой страницы, где оверлей отсутствует: все строки ниже идут через + * GFX_BANK_SPRITE. Один ui_end() = один готовый, tear-free кадр. */ +static uint8_t ui_begin(void) +{ + gfx_rect_t full; + uint8_t back = (uint8_t)(gfx_get_visible_page() ^ 1); + + /* Полностраничная accel-копия держит DI дольше половины CBL-буфера. + * Закрываем вывод, иначе железо повторяет старые 128 байт (скрежет). + * ui_end откроет его уже с заново заполненной тишиной. */ + pop_sfx_pause(); + gfx_set_draw_page(back); + full.x = 0; full.y = 0; full.w = GFX_WIDTH; full.h = GFX_HEIGHT; + gfx_copy_page(&full, GFX_COPY_DIRECT); + gfx_set_bank(GFX_BANK_SPRITE); + return back; +} + +static void ui_end(uint8_t back) +{ + pop_wait_edge(); /* готовый кадр показать только на фронте */ + gfx_set_visible_page(back); + (void)pop_sfx_start(); +} + +static void menu_sound(uint8_t id) +{ + if (id == MENU_SND_NONE) return; + pop_sfx_play(id); + pop_sfx_tick(); /* play_menu_sound: запуск немедленно */ +} + +static void ui_wait_release(void) +{ + while (kbd_raw_down(KBD_ENTER) || kbd_raw_down(KBD_ESC) || + kbd_raw_down(KBD_SPACE) || + kbd_raw_down(KBD_UP) || kbd_raw_down(KBD_DOWN) || + kbd_raw_down(KBD_LEFT) || kbd_raw_down(KBD_RIGHT)) + pop_wait_edge(); +} + +/* Действие = Enter ИЛИ Space: в оригинале выбор подтверждается обеими, и + * держать их врозь незачем — обе читаются одним фронтом. */ +static uint8_t ui_action_down(void) +{ + return (uint8_t)(kbd_raw_down(KBD_ENTER) || kbd_raw_down(KBD_SPACE)); +} + +/* Информационный экран уже нарисован — ждём Esc/Enter/Space по фронту. */ +static void ui_wait_ack(void) +{ + uint8_t esc_prev = 0, act_prev = 0; + + for (;;) { + uint8_t esc = kbd_raw_down(KBD_ESC); + uint8_t act = ui_action_down(); + if ((esc && !esc_prev) || (act && !act_prev)) { + ui_wait_release(); + return; + } + esc_prev = esc; act_prev = act; + pop_wait_edge(); + } +} + +static void menu_draw(uint8_t sel) +{ + uint8_t back = ui_begin(); + uint8_t i; + + /* Как в SDLPoP: только затемнённая замороженная сцена, компактный + * список и тонкий серый контур выбора, без непрозрачной карточки. */ + menu_sel_box(MENU_SEL_L, MENU_SEL_R, + MENU_FIRST_BASE + (int)sel * MENU_STEP); + + pop_text_map(); + for (i = 0; i < MENU_N_ITEMS; i++) + pop_text_center_mapped(POP_TEXT_SMALL, GFX_WIDTH / 2, + MENU_FIRST_BASE + (int)i * MENU_STEP, menu_text[i]); + /* Полосу сначала чистим: там могло висеть сообщение игры («60 MINUTES + * LEFT», «QUICKSAVE»), и две надписи ложились друг на друга. */ + pop_text_unmap(); + pop_status_wipe(); + pop_text_map(); + pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, MENU_PAUSED_BASE, + "GAME PAUSED"); + pop_text_unmap(); + ui_end(back); +} + +/* Явные (uint8_t) — не «на всякий случай»: SDCC 4.5 на константном + * ВЫРАЖЕНИИ больше 127 в инициализаторе uint8_t даёт warning 158, хотя + * значение помещается (на голом литерале 218 — молчит). */ +static const uint8_t settings_y[SETTINGS_N_ITEMS] = { + (uint8_t)(SET_HEAD1 + 1 * SET_STEP), (uint8_t)(SET_HEAD1 + 2 * SET_STEP), + (uint8_t)(SET_HEAD1 + 3 * SET_STEP), (uint8_t)(SET_HEAD1 + 4 * SET_STEP), + (uint8_t)(SET_HEAD2 + 1 * SET_STEP), (uint8_t)(SET_HEAD2 + 2 * SET_STEP), + (uint8_t)(SET_HEAD2 + 3 * SET_STEP), + (uint8_t)(SET_HEAD3 + 1 * SET_STEP), (uint8_t)(SET_HEAD3 + 2 * SET_STEP) +}; + +static const char * const speed_text[] = { "NORMAL", "FAST", "FASTEST" }; + +static void settings_line(uint8_t item, const char *name, const char *value, + uint8_t selected) +{ + int y = settings_y[item]; + if (selected) menu_sel_box(UI_LEFT, UI_RIGHT, y); + pop_text_draw_mapped(POP_TEXT_SMALL, 60, y, name); + if (value) pop_text_right_mapped(POP_TEXT_SMALL, UI_VALUE_RIGHT, y, value); +} + +static void settings_draw(uint8_t sel) +{ + uint8_t back = ui_begin(); + + pop_text_map(); + pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, SET_TITLE_BASE, + "SETTINGS"); + pop_text_draw_mapped(POP_TEXT_SMALL, 60, SET_HEAD1, "GENERAL"); + settings_line(0, "SOUND", pop_settings.sound_enabled ? "ON" : "OFF", sel == 0); + settings_line(1, "MUSIC", pop_settings.music_enabled ? "ON" : "OFF", sel == 1); + settings_line(2, "DEBUG BAR", + pop_settings.show_debug_bar ? "ON" : "OFF", sel == 2); + settings_line(3, "RESTORE DEFAULTS...", 0, sel == 3); + pop_text_draw_mapped(POP_TEXT_SMALL, 60, SET_HEAD2, "GAMEPLAY"); + settings_line(4, "TIME LIMIT", + pop_settings.time_limit_enabled ? "60 MIN" : "UNLIMITED", + sel == 4); + settings_line(5, "SPEED", speed_text[pop_settings.speed_mode], sel == 5); + settings_line(6, "CHEATS", pop_settings.cheats_enabled ? "ON" : "OFF", sel == 6); + pop_text_draw_mapped(POP_TEXT_SMALL, 60, SET_HEAD3, "CONTROLS"); + settings_line(7, "SHOW KEY BINDINGS", 0, sel == 7); + settings_line(8, "ABOUT...", 0, sel == 8); + pop_text_unmap(); + ui_end(back); +} + +/* Две колонки «клавиша — что делает». Первые CTRL_N_BASE строк общие, за + * ними идут читовые: их показываем, только когда читы включены. */ +static const char * const ctrl_key[CTRL_N_BASE + CTRL_N_CHEAT] = { + "ARROWS", "SHIFT", "ESC", "F6 / F9", "CTRL+S", "CTRL+M", "P", + "K", "I", "SHIFT+L", "U", "F7 / F8" +}; + +static const char * const ctrl_desc[CTRL_N_BASE + CTRL_N_CHEAT] = { + "MOVE / JUMP", "ACTION", "PAUSE MENU", "SAVE / LOAD", "SOUND", "MUSIC", + "SPEED", + "KILL GUARD", "IMMORTAL", "NEXT LEVEL", "FLIP SCREEN", "TIME - / +" +}; + +static void controls_draw(void) +{ + uint8_t back = ui_begin(); + /* С выключенными читами вместо пяти строк идёт одна пояснительная — + * список короче, и блок обязан центрироваться по ФАКТИЧЕСКОЙ высоте. */ + uint8_t cheats = pop_settings.cheats_enabled; + uint8_t rows = (uint8_t)(cheats ? CTRL_N_BASE + CTRL_N_CHEAT + : CTRL_N_BASE + 1); + uint8_t i; + int top = UI_CENTER_TOP(POP_FONT_BIG_ASCENT + CTRL_TITLE_GAP + + (int)(rows - 1) * SET_STEP); + int y = top + POP_FONT_BIG_ASCENT; + + pop_text_map(); + pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, y, "CONTROLS"); + y += CTRL_TITLE_GAP; + for (i = 0; i < rows; i++) { + const char *key = ctrl_key[i], *desc = ctrl_desc[i]; + if (!cheats && i == CTRL_N_BASE) { key = "CHEATS"; desc = "DISABLED"; } + pop_text_draw_mapped(POP_TEXT_SMALL, CTRL_COL_KEY, y, key); + pop_text_draw_mapped(POP_TEXT_SMALL, CTRL_COL_DESC, y, desc); + y += SET_STEP; + } + pop_text_unmap(); + ui_end(back); +} + +/* Текст экрана About — он же текст стартового Sprinter screen. Общий код + * ровно затем, чтобы эти два экрана не разъехались: пункт меню обязан + * показывать то же самое, только без строки про клавиши. */ +static void about_text(int top) +{ + int y = top + POP_FONT_BIG_ASCENT; + + pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, y, "PRINCE OF PERSIA"); + y += ABOUT_GAP1; + pop_text_center_mapped(POP_TEXT_SMALL, GFX_WIDTH / 2, y, + "SPRINTER SP2000 BUILD"); + y += ABOUT_GAP1; + pop_text_center_mapped(POP_TEXT_SMALL, GFX_WIDTH / 2, y, + "BUILD " POP_BUILD_ID); + y += ABOUT_GAP2; + pop_text_center_mapped(POP_TEXT_SMALL, GFX_WIDTH / 2, y, POP_BUILD_DATE); +} + +static void about_draw(void) +{ + uint8_t back = ui_begin(); + + pop_text_map(); + about_text(UI_CENTER_TOP(ABOUT_H)); + pop_text_unmap(); + ui_end(back); +} + +static void confirm_draw(const char *question, uint8_t sel) +{ + uint8_t back = ui_begin(); + + menu_box(82, CONF_TOP, 238, CONF_TOP + CONF_BOX_H - 1, MENU_BORDER); + pop_text_map(); + pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, CONF_Q_BASE, question); + if (sel == 0) menu_sel_box(104, 151, CONF_BTN_BASE); + else menu_sel_box(169, 216, CONF_BTN_BASE); + pop_text_center_mapped(POP_TEXT_SMALL, 128, CONF_BTN_BASE, "YES"); + pop_text_center_mapped(POP_TEXT_SMALL, 192, CONF_BTN_BASE, "NO"); + pop_text_unmap(); + ui_end(back); +} + +static uint8_t ui_confirm(const char *question) +{ + uint8_t sel = 1; /* разрушительные действия по умолчанию NO */ + uint8_t up_prev = 0, down_prev = 0, left_prev = 0, right_prev = 0; + uint8_t esc_prev = 0, ent_prev = 0; + + ui_wait_release(); + confirm_draw(question, sel); + menu_sound(MENU_SND_SELECT); + for (;;) { + uint8_t up = kbd_raw_down(KBD_UP); + uint8_t down = kbd_raw_down(KBD_DOWN); + uint8_t left = kbd_raw_down(KBD_LEFT); + uint8_t right = kbd_raw_down(KBD_RIGHT); + uint8_t esc = kbd_raw_down(KBD_ESC); + uint8_t ent = ui_action_down(); + uint8_t redraw = 0; + + if ((up && !up_prev) || (down && !down_prev) || + (left && !left_prev) || (right && !right_prev)) { + sel ^= 1; + redraw = 1; + } + if (esc && !esc_prev) { + ui_wait_release(); + return 0; + } + if (ent && !ent_prev) { + uint8_t yes = (uint8_t)(sel == 0); + ui_wait_release(); + return yes; + } + up_prev = up; down_prev = down; left_prev = left; right_prev = right; + esc_prev = esc; ent_prev = ent; + if (redraw) { + confirm_draw(question, sel); + menu_sound(MENU_SND_MOVE); + } + pop_wait_edge(); + } +} + +static void notice_draw(const char *message) +{ + uint8_t back = ui_begin(); + pop_text_map(); + pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, NOTICE_BASE, message); + pop_text_center_mapped(POP_TEXT_SMALL, GFX_WIDTH / 2, + NOTICE_BASE + NOTICE_GAP, UI_KEYS_TEXT); + pop_text_unmap(); + ui_end(back); +} + +static void ui_notice(const char *message) +{ + ui_wait_release(); + notice_draw(message); + menu_sound(MENU_SND_SELECT); + ui_wait_ack(); +} + +static void controls_process(void) +{ + ui_wait_release(); + controls_draw(); + menu_sound(MENU_SND_SELECT); + ui_wait_ack(); +} + +static void about_process(void) +{ + ui_wait_release(); + about_draw(); + menu_sound(MENU_SND_SELECT); + ui_wait_ack(); +} + +static uint8_t settings_finish(uint8_t changed) +{ + int8_t rc; + if (!changed) return 0; + pop_settings_apply(1); + /* ESTEX может уйти в диск на много CBL-периодов. */ + pop_sfx_pause(); + rc = pop_cfg_save(); + (void)pop_sfx_start(); + ui_notice(rc > 0 ? "SETTINGS SAVED" : "SAVE ERROR"); + return 1; +} + +static uint8_t settings_process(void) +{ + uint8_t sel = 0, changed = 0; + uint8_t up_prev = 0, down_prev = 0, left_prev = 0, right_prev = 0; + uint8_t esc_prev = 0, ent_prev = 0; + + ui_wait_release(); + settings_draw(sel); + menu_sound(MENU_SND_SELECT); /* вход во вложенное меню */ + for (;;) { + uint8_t up = kbd_raw_down(KBD_UP); + uint8_t down = kbd_raw_down(KBD_DOWN); + uint8_t left = kbd_raw_down(KBD_LEFT); + uint8_t right = kbd_raw_down(KBD_RIGHT); + uint8_t esc = kbd_raw_down(KBD_ESC); + uint8_t ent = ui_action_down(); + uint8_t redraw = 0; + uint8_t change = 0; + uint8_t sound = MENU_SND_NONE; + int8_t delta = 0; + + if (up && !up_prev) { + sel = (uint8_t)(sel ? sel - 1 : SETTINGS_N_ITEMS - 1); + redraw = 1; sound = MENU_SND_MOVE; + } + if (down && !down_prev) { + sel = (uint8_t)(sel == SETTINGS_N_ITEMS - 1 ? 0 : sel + 1); + redraw = 1; sound = MENU_SND_MOVE; + } + if (esc && !esc_prev) { + uint8_t saved = settings_finish(changed); + ui_wait_release(); + return saved; + } + if ((left && !left_prev) || (right && !right_prev)) + delta = (right && !right_prev) ? 1 : -1; + /* Left/right принадлежат только числовой настройке скорости; + * остальные строки меняются Enter либо Space, чтобы стрелкой не + * открыть случайно Controls и не сбросить параметры. */ + if ((ent && !ent_prev) || (sel == 5 && delta)) { + if (sel == 0) { + pop_settings.sound_enabled ^= 1; change = 1; + } else if (sel == 1) { + pop_settings.music_enabled ^= 1; change = 1; + } else if (sel == 2) { + pop_settings.show_debug_bar ^= 1; change = 1; + } else if (sel == 3) { + if (ui_confirm("RESTORE DEFAULTS?")) { + pop_settings_defaults(); change = 1; + } + redraw = 1; sound = MENU_SND_SELECT; + } else if (sel == 4) { + pop_settings.time_limit_enabled ^= 1; change = 1; + } else if (sel == 5) { + uint8_t speed = pop_settings.speed_mode; + if (delta < 0) speed = (uint8_t)(speed ? speed - 1 : POP_SPEED_FASTEST); + else speed = (uint8_t)(speed == POP_SPEED_FASTEST ? POP_SPEED_NORMAL : speed + 1); + pop_settings.speed_mode = speed; change = 1; + } else if (sel == 6) { + pop_settings.cheats_enabled ^= 1; change = 1; + } else if (sel == 7) { + controls_process(); redraw = 1; sound = MENU_SND_SELECT; + } else if (sel == 8) { + about_process(); redraw = 1; sound = MENU_SND_SELECT; + } + } + if (change) { + /* Настройки работают сразу, а запись откладывается до выхода. */ + pop_settings_apply(1); + changed = 1; + redraw = 1; + sound = MENU_SND_CHANGE; + } + up_prev = up; down_prev = down; left_prev = left; right_prev = right; + esc_prev = esc; ent_prev = ent; + if (redraw) { + settings_draw(sel); + menu_sound(sound); + } + pop_wait_edge(); + } +} + +static int8_t menu_quickload(void) +{ + uint8_t slots; + /* probe делает open/close на D:, поэтому насос в это время выключен. */ + pop_sfx_pause(); + slots = pop_qsave_probe(); + (void)pop_sfx_start(); + if (slots & POP_QS_SLOT_SAVE) return POP_MENU_QUICKLOAD; + if ((slots & POP_QS_SLOT_BACKUP) && ui_confirm("LOAD BACKUP?")) + return POP_MENU_QUICKLOAD_BACKUP; + if (!(slots & POP_QS_SLOT_BACKUP)) ui_notice("NO QUICKLOAD"); + return POP_MENU_NONE; +} + +/* Вернуть чистую сцену на ОБЕ страницы и показать ту, которая была до + * меню. Это важно и для отладочного single-buffer: главный цикл там + * ожидает страницу 0, а не ту, в которую меню сделало последний флип. */ +static void menu_erase(uint8_t entry_page) +{ + gfx_rect_t full; + uint8_t visible = gfx_get_visible_page(); + uint8_t hidden = (uint8_t)(visible ^ 1); + + full.x = 0; full.y = 0; full.w = GFX_WIDTH; full.h = GFX_HEIGHT; + + /* Из shadow видимой страницы берём кадр без текстов. */ + gfx_set_draw_page(hidden); + gfx_copy_page(&full, GFX_COPY_DIRECT); + pop_wait_edge(); + gfx_set_visible_page(hidden); + + /* Теперь чистая hidden — источник для второй страницы. */ + gfx_set_draw_page(visible); + gfx_copy_page(&full, GFX_COPY_DIRECT); + if (entry_page != hidden) { + pop_wait_edge(); + gfx_set_visible_page(entry_page); + } +} + +static void build_info_draw(void) +{ + uint8_t back = pop_screen_begin(); + int top = UI_CENTER_TOP(ABOUT_H + BUILD_KEYS_GAP); + + pop_screen_fill(BLACK); + gfx_set_bank(GFX_BANK_SPRITE); + pop_text_map(); + about_text(top); + pop_text_center_mapped(POP_TEXT_SMALL, GFX_WIDTH / 2, + top + ABOUT_H + BUILD_KEYS_GAP, UI_KEYS_TEXT); + pop_text_unmap(); + pop_screen_present(back); +} + +/* В SprPoP ещё нет автомата TITLE (это FG1/FG6), поэтому build screen + * показывается перед первым игровым кадром и после выхода возвращает ровно + * ту же сцену. Настройка уже прочитана в pop_boot до initgraph. */ +void pop_build_info_show(void) __banked +{ + uint8_t entry_page, saved_page, saved_bank, frames; + struct fillsettingstype saved_fill; + + if (!pop_ui_ready() || !pop_settings.show_build_info) return; + entry_page = gfx_get_visible_page(); + saved_page = gfx_get_draw_page(); + saved_bank = gfx_get_bank(); + getfillsettings(&saved_fill); + /* Первый реальный пользователь FG5 fade: старая игровая палитра + * запоминается в pop_ui, затем full-screen появляется из чёрного. */ + pop_pal_fade_out(4); + build_info_draw(); + pop_pal_fade_in(4); + for (frames = 0; frames < 150; frames++) { /* около трёх секунд */ + if (kbd_raw_down(KBD_ESC) || kbd_raw_down(KBD_ENTER) || + kbd_raw_down(KBD_SPACE)) + break; + pop_wait_edge(); + } + ui_wait_release(); + pop_pal_fade_out(4); + menu_erase(entry_page); + pop_pal_fade_in(4); + setfillstyle(saved_fill.pattern, saved_fill.color); + gfx_set_bank(saved_bank); + gfx_set_draw_page(saved_page); +} + +int pop_menu_init(void) __banked +{ + return pop_ui_init(); +} + +void pop_menu_free(void) __banked +{ + pop_ui_free(); +} + +int8_t pop_menu_process(void) __banked +{ + uint8_t sel = 0; + uint8_t up_prev = 0, down_prev = 0, esc_prev = 0, ent_prev = 0; + uint8_t entry_page, saved_page, saved_bank; + uint8_t exit_sound = MENU_SND_NONE; + struct fillsettingstype saved_fill; + int8_t result = POP_MENU_NONE; + + if (!pop_menu_request) return POP_MENU_NONE; + pop_menu_request = 0; + if (!pop_ui_ready()) return POP_MENU_RESUME; + + /* Открыли по НАЖАТИЮ Esc: дождаться отпускания, иначе то же нажатие + * закроет меню раньше первого кадра. pop_wait_edge опрашивает FIFO. */ + while (kbd_raw_down(KBD_ESC)) pop_wait_edge(); + + entry_page = gfx_get_visible_page(); + saved_page = gfx_get_draw_page(); + saved_bank = gfx_get_bank(); + getfillsettings(&saved_fill); + pop_sfx_pause(); /* snapshot/dim + первый full redraw */ + pop_pal_snapshot(); + /* 50 % яркости; диапазон UI остаётся ярким. Долю берём от шкалы, а не + * числом: ступеней стало 32 вместо четырёх, и прежняя «2» означала бы + * теперь затемнение на одну шестнадцатую — фон под меню почти не гас + * (поймано пользователем 2026-08-26). */ + pop_pal_menu_dim(POP_PAL_DIM_BLACK / 2); + menu_draw(sel); + + for (;;) { + uint8_t up = kbd_raw_down(KBD_UP); + uint8_t down = kbd_raw_down(KBD_DOWN); + uint8_t esc = kbd_raw_down(KBD_ESC); + uint8_t ent = ui_action_down(); + uint8_t redraw = 0; + uint8_t sound = MENU_SND_NONE; + + if (up && !up_prev) { + sel = (uint8_t)(sel ? sel - 1 : MENU_N_ITEMS - 1); + redraw = 1; sound = MENU_SND_MOVE; + } + if (down && !down_prev) { + sel = (uint8_t)(sel == MENU_N_ITEMS - 1 ? 0 : sel + 1); + redraw = 1; sound = MENU_SND_MOVE; + } + if (esc && !esc_prev) { + result = POP_MENU_RESUME; + break; + } + if (ent && !ent_prev && menu_cmds[sel] != POP_MENU_NONE) { + uint8_t cmd = menu_cmds[sel]; + if (cmd == POP_MENU_SETTINGS) { + (void)settings_process(); + redraw = 1; sound = MENU_SND_SELECT; /* Back -> pause menu */ + } else if (cmd == POP_MENU_QUICKLOAD) { + result = menu_quickload(); + if (result != POP_MENU_NONE) { exit_sound = MENU_SND_SELECT; break; } + redraw = 1; sound = MENU_SND_SELECT; + } else if (cmd == POP_MENU_QUIT) { + if (ui_confirm("QUIT GAME?")) { + result = POP_MENU_QUIT; + exit_sound = MENU_SND_SELECT; + break; + } + redraw = 1; sound = MENU_SND_SELECT; + } else { + result = (int8_t)cmd; + exit_sound = MENU_SND_SELECT; + break; + } + } + up_prev = up; down_prev = down; esc_prev = esc; ent_prev = ent; + + if (redraw) { + menu_draw(sel); + menu_sound(sound); + } + pop_wait_edge(); + pop_pace_end(pop_pace_n(0)); /* игра стоит — обычный темп */ + } + + pop_sfx_pause(); /* две full-page копии menu_erase */ + menu_erase(entry_page); + /* menu_erase вернул обе страницы из тени, снятой ДО меню: всё, что + * статус-полосы нарисовали за это время (или сообщение, дотикавшее в + * паузе), надо выложить заново. */ + pop_status_invalidate(); + pop_pal_apply(0); + setfillstyle(saved_fill.pattern, saved_fill.color); + gfx_set_bank(saved_bank); + gfx_set_draw_page(saved_page); + (void)pop_sfx_start(); + menu_sound(exit_sound); /* Esc main menu остаётся бесшумным */ + return result; +} diff --git a/applications/SprPoP/src/pop_menu.h b/applications/SprPoP/src/pop_menu.h new file mode 100644 index 0000000..9b6bcd6 --- /dev/null +++ b/applications/SprPoP/src/pop_menu.h @@ -0,0 +1,45 @@ +/* + * pop_menu.h — контракт pause menu (план docs/menu_settings_plan.md §10.3, + * этап MS0). + * + * UI НЕ вызывает gameplay-модули напрямую: меню формирует КОМАНДУ, а + * исполнение остаётся в главном цикле (sprpop.c). Вся работа с меню — + * код, шрифты и таблицы строк — живёт в БАНКЕ 9. Флаг запроса принадлежит + * главному циклу (W2): это единственная резидентная граница UI. + */ +#ifndef POP_MENU_H +#define POP_MENU_H + +#include + +typedef enum { + POP_MENU_NONE = 0, /* меню не открывалось / пункт-заглушка */ + POP_MENU_RESUME, /* закрыть меню, продолжить игру */ + POP_MENU_QUICKSAVE, /* то же, что F6 */ + POP_MENU_QUICKLOAD, /* то же, что F9 */ + POP_MENU_RESTART_LEVEL, /* штатный reset текущего уровня (без CFG) */ + POP_MENU_RESTART_GAME, /* новая игра с уровня 1, настройки и SAV */ + POP_MENU_QUIT, /* выход из программы (после подтверждения) */ + POP_MENU_QUICKLOAD_BACKUP, /* загрузить подтверждённый POP.BAK */ + POP_MENU_SETTINGS /* внутренний экран Settings (не возвращается) */ +} pop_menu_cmd_t; + +/* Флаг запроса в sprpop.c (W2); банк читает напрямую. */ +extern uint8_t pop_menu_request; + +/* Загрузить/освободить EMM-атлас исходных шрифтов SDLPoP. Вызывается из + * холодного boot/shutdown, не из кадрового пути. */ +int pop_menu_init(void) __banked; +void pop_menu_free(void) __banked; + +/* Граница кадра. Запроса нет — мгновенный POP_MENU_NONE. Меню МОДАЛЬНО: + * пока открыто, не возвращается (игра стоит — логика, таймер и звуковой + * насос главного цикла просто не выполняются); по выбору пользователя + * отдаёт pop_menu_cmd_t. */ +int8_t pop_menu_process(void) __banked; + +/* Экран Sprinter build, если он включён в POP.CFG. Вызывается после + * открытия raw-клавиатуры и до первого игрового кадра. */ +void pop_build_info_show(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_music.c b/applications/SprPoP/src/pop_music.c new file mode 100644 index 0000000..b6848df --- /dev/null +++ b/applications/SprPoP/src/pop_music.c @@ -0,0 +1,362 @@ +/* + * pop_music.c — загрузка и запуск музыкального трека, БАНК 9. + * + * Здесь только холодная часть: выделить страницы EMM, вычитать в них куски + * m_.bin и выставить курсор. Сам вывод делает насос CBL в + * резиденте (pop_sfx.c): когда эффект не звучит, он берёт байты отсюда, а + * не из блока тишины. Контракт и мотивация — в pop_music.h. + * + * Имя файла собираем вручную, без sprintf: printf-семейство в резидент не + * тянем, а тут и формат-то из двух чисел. + */ +#include +#include +#include +#include +#include "pop_music.h" +#include "pop_music_tbl.h" +#include "pop_sfx.h" +#include "pop_app.h" /* POP_APP_DEMO — в демке музыки нет */ + +uint8_t pop_mus_page[POP_MUS_PAGES]; + +/* Два слота: играющий и подгружаемый. Своей таблицы страниц слот НЕ + * держит — её и так хранит блок EMM, и mem_get_page отдаёт номер по + * индексу. Резидентный pop_mus_page[] заполняется на pop_music_play, для + * одного слота: 40 байт W2 у игры лишних нет. */ +#define MUS_SLOTS 2 +static uint8_t slot_blk[MUS_SLOTS]; /* блок EMM, 0 — слот пуст */ +static uint8_t slot_pages[MUS_SLOTS]; +static uint16_t slot_blocks[MUS_SLOTS]; +static uint8_t slot_id[MUS_SLOTS]; +static uint8_t mus_cur; /* слот, что играет/играл */ +static uint8_t mus_ready; /* слот+1, готовый к запуску */ + +/* "MUS\mNN.bin" — ровно 8.3, как требует DSS. */ +static void mus_name(char *buf, uint8_t id) +{ + buf[0] = 'M'; buf[1] = 'U'; buf[2] = 'S'; buf[3] = '\\'; + buf[4] = 'm'; + buf[5] = (char)('0' + id / 10); + buf[6] = (char)('0' + id % 10); + buf[7] = '.'; buf[8] = 'b'; buf[9] = 'i'; buf[10] = 'n'; + buf[11] = 0; +} + +/* ld_busy отдельно от ld_fd: инициализаторов у статиков в банке нет + * (crt0 зануляет _DATA), а «нет открытого файла» — это не ноль. */ +static uint8_t ld_busy; +static int ld_fd; +static uint8_t ld_slot; +static uint8_t ld_next; /* следующая страница к чтению */ +static uint8_t ld_pages; +static uint8_t ld_id; + +static void slot_release(uint8_t n) +{ + if (slot_blk[n]) { + mem_free_block(slot_blk[n]); + slot_blk[n] = 0; + } + slot_pages[n] = 0; + slot_blocks[n] = 0; +} + +/* --- ПОТОКОВЫЙ ТРЕК: КОЛЬЦО СТРАНИЦ ---------------------------------- * + * + * Финальная тема (`won`, 115 с) — это 1,2 МБ и 78 страниц EMM. Держать её + * в памяти нельзя ни при каком бюджете, поэтому она играется из НЕСКОЛЬКИХ + * страниц по кругу: насос идёт по кольцу (pop_mus_ring), а загрузчик + * дочитывает файл в те страницы, которые насос уже прошёл. + * + * Кто кого догоняет. Страница звучит 1,5 с, а читается 33 мс — запас + * сорокакратный, так что «не успеть» можно только застряв на секунду. + * Дистанцию считаем без отдельных счётчиков: страница ровно 128 блоков + * насоса, поэтому проигранных страниц = (всего блоков - осталось) / 128. + * Пока прочитано меньше, чем проиграно плюс размер кольца, — в кольце есть + * свободный слот, и следующую страницу можно читать. + * + * Файл читаем ПОСЛЕДОВАТЕЛЬНО, без lseek: страницы идут по порядку, и fd + * сам стоит там, где надо. */ +#define RING_PAGES 6 /* 96 КБ = 9 с звучания, запас с лихвой */ + +static uint8_t ring_on; /* идёт потоковый трек */ +static uint8_t ring_blk; /* EMM-блок кольца */ +static uint8_t ring_fd_open; +static int ring_fd; +static uint8_t ring_file_pages; /* страниц в файле */ +static uint8_t ring_loaded; /* сколько уже прочитано */ +static uint16_t ring_total; /* длина трека в блоках насоса */ + +void pop_music_ring_stop(void) __banked; + +void pop_music_stop(void) __banked +{ + pop_mus_left = 0; +} + +void pop_music_free(void) __banked +{ + if (ring_on) pop_music_ring_stop(); + if (ld_busy) { close(ld_fd); ld_busy = 0; } + pop_mus_left = 0; + slot_release(0); + slot_release(1); + mus_ready = 0; +} + +/* --- загрузка --------------------------------------------------------- + * + * ПОСТРАНИЧНО, А НЕ ЦЕЛИКОМ. Реплики сцены с принцессой идут встык, и + * следующую надо читать, пока звучит текущая. Трек — до 320 КБ, целиком + * это 0,7 с диска: на 7,5 кадрах в секунду сцена встала бы на пять кадров. + * Одна страница стоит 33 мс — меньше четверти кадра, и подкачка растворя- + * ется в анимации. Кто может позволить себе паузу (титры, чёрный экран + * между сценами), зовёт pop_music_load — тот же цикл, но целиком. */ +static void load_abort(void) +{ + if (ld_busy) close(ld_fd); + ld_busy = 0; + slot_release(ld_slot); +} + +int8_t pop_music_load_begin(uint8_t id) __banked +{ + char name[12]; + uint8_t pages, n; + + if (ring_on) pop_music_ring_stop(); /* потоковый трек и слоты не дружат */ + if (ld_busy) load_abort(); + if (id >= POP_MUS_COUNT) return -1; + pages = pop_mus_tbl[id].pages; + if (!pages || pages > POP_MUS_PAGES) return -1; + + /* Грузим ВСЕГДА в не текущий слот: играющий трек читает свои страницы + * из прерывания, и отобрать их у него нельзя. */ + n = (uint8_t)(mus_cur ^ 1); + slot_release(n); + mus_name(name, id); + /* Трек лежит ОДНИМ файлом, и fd остаётся открытым до последней + * страницы: `open` стоит дороже самого чтения (51,4 мс против 32,6 мс + * на 16 КБ), и платить его на каждую страницу — чистые потери. */ + ld_fd = open(name, O_RDONLY); + if (ld_fd < 0) return -1; + slot_blk[n] = mem_alloc_pages(pages); + if (!slot_blk[n]) { close(ld_fd); return -1; } + ld_busy = 1; + ld_slot = n; + ld_next = 0; + ld_pages = pages; + ld_id = id; + return 0; +} + +int8_t pop_music_load_step(void) __banked +{ + uint8_t p; + + if (!ld_busy) return 1; /* нечего грузить */ + p = mem_get_page(slot_blk[ld_slot], ld_next); + /* Последняя страница короче — читаем сколько есть; ошибку ловим по + * отрицательному коду, а не по «не 16384». */ + if (!p || bank_read_page(ld_fd, p, 0, 16384) < 0) { + load_abort(); + return -1; + } + if (++ld_next < ld_pages) return 0; + + close(ld_fd); + ld_busy = 0; + slot_pages[ld_slot] = ld_pages; + slot_blocks[ld_slot] = pop_mus_tbl[ld_id].blocks; + slot_id[ld_slot] = ld_id; + mus_ready = (uint8_t)(ld_slot + 1); + return 1; +} + +uint8_t pop_music_loading(void) __banked +{ + return ld_busy; +} + +int8_t pop_music_load(uint8_t id) __banked +{ + int8_t r; + + if (pop_music_load_begin(id) != 0) return -1; + do { + r = pop_music_load_step(); + } while (r == 0); + return (r < 0) ? -1 : 0; +} + +/* ЗАПУСК С ПОДКАЧКОЙ НА ХОДУ — для игровых джинглов (смерть, зелья, меч). + * Ждать полной загрузки нельзя: это фриз на треть секунды посреди игры. + * Зато можно начать сразу после ПЕРВОЙ страницы: она звучит 1,5 с, а + * следующая читается 33 мс — запас сорокакратный. Дальше вызывающий + * доливает по странице за кадр (pop_music_load_step, pop_music_loading). + * + * Номера страниц известны сразу после mem_alloc_pages, поэтому таблицу для + * насоса заполняем целиком: данные в этих страницах появятся раньше, чем + * он до них доберётся. + * 0 — играет, -1 — трека нет или память кончилась. */ +int8_t pop_music_stream(uint8_t id) __banked +{ + uint8_t i, n; + + if (ring_on) pop_music_ring_stop(); /* см. pop_music_play */ + if (pop_music_load_begin(id) != 0) return -1; + if (pop_music_load_step() < 0) return -1; /* первая страница */ + n = ld_slot; + pop_mus_left = 0; /* насос — на тишину */ + for (i = 0; i < ld_pages; i++) + pop_mus_page[i] = mem_get_page(slot_blk[n], i); + if (n != mus_cur) slot_release(mus_cur); + mus_cur = n; + slot_pages[n] = ld_pages; + slot_blocks[n] = pop_mus_tbl[id].blocks; + slot_id[n] = id; + pop_mus_id = id; + pop_mus_pg = 0; + pop_mus_ptr = 0; + pop_mus_left = slot_blocks[n]; + return 0; +} + +void pop_music_play(void) __banked +{ + uint8_t n, i; + + if (!mus_ready) return; + /* СНЯТЬ КОЛЬЦО. Если до этого играл потоковый трек (финал), насос всё + * ещё заворачивал бы страницы по кругу шести — обычный трек звучал бы + * своей первой третью без конца (поймано на титрах после победы). */ + if (ring_on) pop_music_ring_stop(); + n = (uint8_t)(mus_ready - 1); + /* Насос обязан замолчать ДО подмены таблицы страниц: иначе он успеет + * взять порцию по новому индексу из старого курсора. */ + pop_mus_left = 0; + for (i = 0; i < slot_pages[n]; i++) + pop_mus_page[i] = mem_get_page(slot_blk[n], i); + if (n != mus_cur) slot_release(mus_cur); + mus_cur = n; + mus_ready = 0; + /* Порядок важен: сначала позиция, потом длина. Насос стартует ровно + * с той порции, где left стал ненулевым, и промежуточное состояние + * «играем, но курсор от прошлого трека» не возникает. */ + pop_mus_id = slot_id[n]; /* до left: менеджер спросит готовое */ + pop_mus_pg = 0; + pop_mus_ptr = 0; + pop_mus_left = slot_blocks[n]; +} + + +static void ring_close_file(void) +{ + if (ring_fd_open) { close(ring_fd); ring_fd_open = 0; } +} + +void pop_music_ring_stop(void) __banked +{ + pop_mus_left = 0; + pop_mus_ring = 0; + ring_close_file(); + if (ring_blk) { mem_free_block(ring_blk); ring_blk = 0; } + ring_on = 0; +} + +int8_t pop_music_ring_start(uint8_t id) __banked +{ + char name[12]; + uint8_t i, page; + + if (id >= POP_MUS_COUNT || !pop_mus_tbl[id].pages) return -1; + pop_music_free(); /* обычные слоты кольцу не нужны */ + pop_music_ring_stop(); + mus_name(name, id); + ring_fd = open(name, O_RDONLY); + if (ring_fd < 0) return -1; + ring_fd_open = 1; + ring_blk = mem_alloc_pages(RING_PAGES); + if (!ring_blk) { ring_close_file(); return -1; } + ring_file_pages = pop_mus_tbl[id].pages; + ring_total = pop_mus_tbl[id].blocks; + /* Заполняем кольцо целиком: старт с полным буфером даёт девять секунд + * форы, и первая же тяжёлая сцена финала не догонит насос. */ + for (i = 0; i < RING_PAGES; i++) { + page = mem_get_page(ring_blk, i); + pop_mus_page[i] = page; + if (!page || + (i < ring_file_pages && bank_read_page(ring_fd, page, 0, 16384) < 0)) { + pop_music_ring_stop(); + return -1; + } + } + ring_loaded = (uint8_t)(ring_file_pages < RING_PAGES ? ring_file_pages + : RING_PAGES); + if (ring_loaded >= ring_file_pages) ring_close_file(); + ring_on = 1; + pop_mus_id = id; + pop_mus_pg = 0; + pop_mus_ptr = 0; + pop_mus_ring = RING_PAGES; /* насос пойдёт по кругу */ + pop_mus_left = ring_total; + return 0; +} + +/* Один шаг подкачки: страница файла в слот, который насос уже прошёл. */ +static void ring_step(void) +{ + uint16_t played; + uint8_t played_pg, page; + + if (!ring_on) return; + if (!pop_mus_left) { /* трек доиграл — отпустить память */ + pop_music_ring_stop(); + return; + } + if (ring_loaded >= ring_file_pages) return; /* файл прочитан весь */ + played = (uint16_t)(ring_total - pop_mus_left); + played_pg = (uint8_t)(played >> 7); /* 128 блоков на страницу */ + if ((uint8_t)(ring_loaded - played_pg) >= RING_PAGES) return; /* полно */ + page = pop_mus_page[ring_loaded % RING_PAGES]; + if (!page || bank_read_page(ring_fd, page, 0, 16384) < 0) { + pop_music_ring_stop(); + return; + } + ring_loaded++; + if (ring_loaded >= ring_file_pages) ring_close_file(); +} + +void pop_music_service(void) __banked +{ + /* Один вызов на кадр из ЛЮБОГО цикла — игрового, интро, катсцены. + * Заявку кладёт pop_sfx_play (звук без оцифровки = музыка оригинала), + * а диск трогаем только здесь. */ + /* В ЗАСТАВОЧНОЙ ДЕМКЕ МУЗЫКИ НЕТ. Точечные условия оригинала (смерть, + * убитый страж) расставлены по своим местам, но гейт нужен и общий: + * демо проигрывает записанные нажатия, и любой музыкальный повод — + * подобранный меч, зелье — зазвучал бы там, где оригинал играет одни + * эффекты. */ + if (ring_on) { ring_step(); return; } /* потоковый трек — свой путь */ + if (!pop_mus_want || pop_app_state == POP_APP_DEMO) { + /* Выключили на ходу: оборвать поток и замолчать. Эффекты при этом + * продолжают звучать — у них свой тумблер. */ + if (pop_mus_req) pop_mus_req = 0; + if (ld_busy || pop_mus_left) pop_music_free(); + return; + } + if (pop_mus_req) { + uint8_t id = pop_mus_req; + + pop_mus_req = 0; + (void)pop_music_stream(id); + } else if (ld_busy) { + (void)pop_music_load_step(); + } +} + +uint8_t pop_music_busy(void) __banked +{ + return (uint8_t)(pop_mus_left != 0); +} diff --git a/applications/SprPoP/src/pop_music.h b/applications/SprPoP/src/pop_music.h new file mode 100644 index 0000000..9182d4a --- /dev/null +++ b/applications/SprPoP/src/pop_music.h @@ -0,0 +1,105 @@ +/* + * pop_music.h — музыкальный трек через тот же CBL, что и эффекты. + * + * ПОЧЕМУ PCM, А НЕ AY. Оцифровки музыки в игре нет вовсе: digisnd*.dat + * содержит только эффекты, музыка лежит MIDI под Adlib. Зато есть готовые + * записи DOS-версии, и путь «запись -> сырой поток CBL» не требует ни + * синтезатора на хосте, ни аранжировки под три канала AY (разбор вариантов + * — ../docs/sound_plan.md §1). Плата известна: CBL один, поэтому пока + * играет музыка, эффектов нет. Оригинал ведёт себя так же — play_sound + * там глушит предыдущий звук. + * + * ГДЕ УМЕСТНО. Заставки и титры: эффектов там не бывает, картинка + * статична, и загрузка сотни-другой килобайт с диска никому не мешает. + * Музыка ПО ХОДУ игры (джинглы смерти, зелья) — отдельный вопрос, её + * придётся либо гасить эффектами, либо уводить на AY. + * + * ФОРМАТ на диске — один файл MUS\m.bin на трек, читается порциями по + * 16 КБ из одного открытого fd (bank_read_page). Каталог с длинами + * генерирует toolchain/pop_pack_music.py в pop_music_tbl.h. + */ +#ifndef POP_MUSIC_H +#define POP_MUSIC_H + +#include + +/* Сколько страниц EMM держим под трек. Двадцать = 320 КБ, это все + * заставочные треки, кроме финального «won» (115 с, 1,2 МБ): тот придётся + * либо резать, либо стримить, и это отдельная задача. */ +#define POP_MUS_PAGES 20 + +/* Номера треков — нумерация оригинала (SDLPoP data/music/names.txt). */ +#define POP_MUS_INTRO 54 /* тема заставки, звучит на титрах */ +#define POP_MUS_STORY_1 55 /* «In the absence of the Sultan...» */ +#define POP_MUS_STORY_2 50 /* принцесса ждёт */ +#define POP_MUS_STORY_3 53 /* входит Джафар */ +#define POP_MUS_STORY_4 52 /* Джафар уходит */ +#define POP_MUS_WON 56 /* победа: 115 с, только потоком */ + +/* Курсор проигрывания — читает НАСОС (pop_sfx.c, резидент), пишет + * загрузчик. Отдельный от эффектов: они звучат поверх музыки, а не + * вместо неё, и у каждого свой источник. */ +extern uint8_t pop_mus_page[POP_MUS_PAGES]; /* физические страницы EMM */ +extern volatile uint8_t pop_mus_pg; /* индекс текущей страницы */ +extern volatile uint16_t pop_mus_ptr; /* адрес в окне W0 */ +extern volatile uint16_t pop_mus_left; /* порций до конца, 0 — молчим */ +/* Номер играющего трека (нумерация оригинала). Нужен менеджеру звука: + * музыка участвует в таблицах приоритета наравне с эффектами. */ +extern uint8_t pop_mus_id; +/* Размер кольца страниц для потокового трека; 0 — обычный режим. */ +extern uint8_t pop_mus_ring; + +/* ДВА СЛОТА. Заставочные сцены идут цепочкой треков (титры -> история -> + * PV из трёх реплик), и загрузка каждого — это полсекунды-секунда диска. + * Прятать её в паузу между репликами негде: пауз в оригинале нет, там + * следующая реплика начинается сразу. Поэтому слотов два: пока звучит + * один, следующий читается во второй, и переключение стоит копирования + * двадцати байт таблицы страниц. + * + * Загрузить трек в свободный слот (НЕ запускает; играющий трек не трогает). + * 0 — готово, -1 — нет трека/памяти/файла. */ +int8_t pop_music_load(uint8_t id) __banked; + +/* ТА ЖЕ ЗАГРУЗКА ПО ОДНОЙ СТРАНИЦЕ ЗА ВЫЗОВ — для тех, кому нельзя + * замереть на секунду: страница стоит 33 мс, то есть четверть кадра + * заставочной сцены, и подкачка следующей реплики не видна. + * begin — открыть файл и занять страницы (0 / -1); + * step — прочитать очередную: 0 — ещё есть, 1 — трек готов к play, + * -1 — ошибка (слот освобождён); + * loading — идёт ли чтение (звать step имеет смысл, пока 1). + * Оборвать начатое чтение можно pop_music_free. */ +int8_t pop_music_load_begin(uint8_t id) __banked; +int8_t pop_music_load_step(void) __banked; +uint8_t pop_music_loading(void) __banked; + +/* Отпустить ОБА слота. Останавливает проигрывание, если оно шло. */ +void pop_music_free(void) __banked; + +/* Пустить трек СРАЗУ, дочитывая его по ходу звучания: первая страница + * читается здесь (33 мс), остальные — вызывающим, по одной за кадр, пока + * pop_music_loading(). Для игровых джинглов, где паузы на диск быть не + * может. 0 — играет, -1 — трека нет либо не хватило памяти. */ +int8_t pop_music_stream(uint8_t id) __banked; + +/* Пустить последний загруженный трек с начала; слот предыдущего при этом + * освобождается. Без парного load ничего не делает. */ +void pop_music_play(void) __banked; +void pop_music_stop(void) __banked; + +/* ОБСЛУЖИВАНИЕ МУЗЫКИ — один вызов на кадр из любого цикла (игра, интро, + * катсцена): разбирает заявку pop_mus_req и доливает страницы начатого + * стрима. Всё чтение с диска живёт здесь. */ +void pop_music_service(void) __banked; + +/* ПОТОКОВЫЙ ТРЕК ИЗ КОЛЬЦА СТРАНИЦ — для того, что в память не влезает. + * Финал `won` весит 1,2 МБ (78 страниц); играется из шести по кругу, а + * дочитывает его pop_music_service, пока трек звучит. + * 0 — играет, -1 — нет файла/памяти. Останавливает pop_music_ring_stop + * (её же зовёт pop_music_free). */ +int8_t pop_music_ring_start(uint8_t id) __banked; +void pop_music_ring_stop(void) __banked; + +/* Трек ещё звучит? 0 — доиграл или не запускался. */ +uint8_t pop_music_busy(void) __banked; + +#endif /* POP_MUSIC_H */ diff --git a/applications/SprPoP/src/pop_pace.c b/applications/SprPoP/src/pop_pace.c new file mode 100644 index 0000000..0377422 --- /dev/null +++ b/applications/SprPoP/src/pop_pace.c @@ -0,0 +1,100 @@ +/* + * pop_pace.c — фиксированный логический кадр по лучу. Зачем именно так, + * а не по счётчику кадровых прерываний — в шапке pop_pace.h (там же + * условие точности и замеры). + * + * Модуль НЕ банковый (EXTRA_SRCS): pop_beam_sample зовётся из банков + * рисования, а базовый код в W1 доступен им прямым call — трамплин на + * каждую выборку съел бы весь смысл. + */ + +#include +#include +#include +#include "pop_pace.h" + +uint8_t pop_speed_mode; /* crt0 зануляет _DATA → NORMAL */ +volatile uint8_t pop_frame_tick; + +/* Бит 5 порта 0xFE на прошлой выборке: 0 или 0x20. На виду у asm-тела. */ +uint8_t pop_beam_prev; + +static uint8_t pace_anchor; /* тик на начало текущего логического кадра */ +static uint8_t pace_ok; /* 0 — луч не работает, откат на gfx_wait_vsync */ + +/* ---- выборка луча --------------------------------------------------- * + * in a,(0xFE) кладёт A на старший байт адреса (выбор ряда клавиатуры) — + * биты 5/7 от него не зависят, так же читает и gfx_wait_vsync. + * Быстрый путь (состояние не изменилось) — 40 T + вызов. */ +void pop_beam_sample(void) __preserves_regs(b, c, d, e) __naked +{ + __asm + in a, (#0xFE) + and a, #0x20 + ld hl, #_pop_beam_prev + cp a, (hl) + ret Z ; состояние то же — выходим сразу + ld (hl), a + or a, a + ret NZ ; 0→1: вошли в бланк, это не граница + ld hl, #_pop_frame_tick + inc (hl) ; 1→0: vpos = 0, начало кадра + ret + __endasm; +} + +uint8_t pop_pace_arm(void) +{ + uint16_t tries; + uint8_t t0; + + /* Взводит cbl_mode (_cbl_port_ref) и заодно ставит нас на фронт. */ + gfx_wait_vsync(); + + /* Затравка beam_prev без ложного фронта: что бы луч ни показал сейчас, + * первая выборка либо совпадёт (ранний выход), либо запишет 0x20 и + * уйдёт по ветке «вошли в бланк». */ + pop_beam_prev = 0; + pop_beam_sample(); + + t0 = pop_frame_tick; + for (tries = 0; tries < 20000; tries++) { + pop_beam_sample(); + if ((uint8_t)(pop_frame_tick - t0) >= 2) { + pace_anchor = pop_frame_tick; + pace_ok = 1; + return 1; + } + } + pace_ok = 0; /* бит не шевелится — прежнее поведение */ + return 0; +} + +void pop_wait_edge(void) +{ + uint8_t t; + if (!pace_ok) { gfx_wait_vsync(); return; } + t = pop_frame_tick; + do { + pop_beam_sample(); + kbd_raw_poll(); /* KBD-1: опрос обязан быть плотным */ + } while (pop_frame_tick == t); +} + +void pop_pace_end(uint8_t n) +{ + if (!pace_ok) { /* откат: как было — добрать ожиданиями */ + while (--n) gfx_wait_vsync(); + return; + } + while ((uint8_t)(pop_frame_tick - pace_anchor) < n) + pop_wait_edge(); + pace_anchor = pop_frame_tick; /* якорь ПО ФАКТУ — фаза не копится */ +} + +uint8_t pop_pace_n(uint8_t fight) +{ + if (pop_speed_mode == POP_SPEED_NORMAL) return fight ? 5 : 4; + if (pop_speed_mode == POP_SPEED_FAST) return fight ? 4 : 3; + return 3; /* FASTEST: и вне боя, и в бою */ +} diff --git a/applications/SprPoP/src/pop_pace.h b/applications/SprPoP/src/pop_pace.h new file mode 100644 index 0000000..be70a04 --- /dev/null +++ b/applications/SprPoP/src/pop_pace.h @@ -0,0 +1,102 @@ +/* + * pop_pace.h — фиксированный логический кадр (frame pacing) по ЛУЧУ. + * + * Задача: период логического кадра = max(n, ceil(W)) вместо нынешнего + * ceil(W) + 2, где W — работа в растровых кадрах. Сегодня главный цикл + * ждёт три gfx_wait_vsync ПОСЛЕ отрисовки, поэтому реальный бюджет кадра + * — один растр (430 080 тактов), и превышение его на такт стоит целого + * лишнего растра. При n=3 бюджет становится 1 290 240. + * + * ПОЧЕМУ НЕ СЧЁТЧИК КАДРОВЫХ ПРЕРЫВАНИЙ (замеры 2026-08-19, MAME, полный + * разбор в docs/frame_pacing_plan.md). Импульс запроса кадрового + * прерывания живёт 32 такта неразогнанного клока = 9,14 мкс, а ядра + * акселератора держат di на весь чанк блита (~0,29 мс). Импульс, + * попавший в такое окно, теряется насовсем — ядро z80 уровневое, защёлки + * нет (то же и на настоящем Spectrum). Замерено: в сцене 11/15 теряется + * от 0 до 2,8 % прерываний в зависимости от ФАЗЫ рендера относительно + * луча, а на полной перерисовке комнаты — три подряд. Сейчас фаза + * плавает (период то 3 растра, то 4) и потери размазаны; при жёстком + * пейсинге фаза застынет, и сцена может залипнуть в плохой — это ровные + * −25 % скорости, невидимые в профиле тактов. + * + * ЧТО ВМЕСТО. Бит 5 порта 0xFE — это ЧТЕНИЕ ПОЛОЖЕНИЯ ЛУЧА (1, когда + * vpos >= 272; MAME sprinter.cpp kbd_fe_r), а не событие: ни импульса, + * ни очереди, ни защёлки. Такой сигнал невозможно потерять — можно + * только не посмотреть. Считаем фронты 1→0 (это vpos = 0, ровно тот же + * момент, которого ждёт gfx_wait_vsync) программно, выборкой из точек, + * которые код и так проходит. + * + * УСЛОВИЕ ТОЧНОСТИ — ОДНО: между соседними выборками должно проходить + * меньше 64 512 тактов (длина окна «бит 5 = 1»: 48 строк из 320). Тогда + * в каждый бланк попадает хотя бы одна выборка, а в промежуток между + * бланками (365 568 тактов) — тем более; значит каждый фронт засчитан + * ровно один раз. Двойной счёт невозможен: инкремент только на переходе, + * и beam_prev тут же обновляется. Условие не про нагрузку, не про di, + * не про прерывания и не про звук — только про зазор. + * + * Запас: самый длинный неделимый кусок без выборки — одно di-окно, то + * есть один вызов _bgi_blit_rows_raw, а он по контракту режется на чанки + * ≤16 строк. Даже если считать грубо — одна выборка на целый блит, а + * самый дорогой замеренный блит 32 073 такта — зазор вдвое меньше окна. + * + * ВАЖНО: бит 5 читается осмысленно ТОЛЬКО при включённом cbl_mode (в + * MAME `data &= ~0xa0` стоит внутри `if (cbl_mode())`, иначе бит выставлен + * в 1 намертво). Его лениво взводит сам gfx_wait_vsync через + * _cbl_port_ref, поэтому pop_pace_arm обязан один раз позвать + * gfx_wait_vsync и ПРОВЕРИТЬ, что фронты реально идут. + */ +#ifndef POP_PACE_H +#define POP_PACE_H + +#include + +/* Режимы скорости. Растровый кадр Sprinter — 20,48 мс (48,83 Гц). + * Оригинал (SDLPoP/src/seg003.c:363): base_speed 5 тиков при 60 Гц = + * 83,3 мс, fight_speed 6 = 100 мс. Условие боя у оригинала одно и + * буквальное — «у Кида ВЫНУТ МЕЧ» (Kid.sword == sword_2_drawn), не + * «идёт бой» и не «рядом страж»; проверяется в верху главного цикла. */ +/* Нумерация НЕ произвольная: 0 обязан быть дефолтом (crt0 зануляет _DATA), + * а обход по кругу инкрементом даёт ровно NORMAL -> FAST -> FASTEST. + * Заодно номер режима = число палочек в отладочной метке минус один. */ +#define POP_SPEED_NORMAL 0 /* 4/5 — 81,9 / 102,4 мс; повтор оригинала */ +#define POP_SPEED_FAST 1 /* 3/4 — 61,4 / 81,9 мс; бой как в оригинале */ +#define POP_SPEED_FASTEST 2 /* 3/3 — 61,4 мс; не успели — подтормаживаем */ +#define POP_SPEED_MODES 3 + +extern uint8_t pop_speed_mode; /* POP_SPEED_*; дефолт — NORMAL */ + +/* Клавиша смены режима по кругу (PS/2 set 2). P — свободна: заняты + * K/I/S/L/U/[/] (читы, pop_cheat.h) и +/- (обход комнат). */ +#define KBD_SPEED_MODE 0x4D /* P */ + +/* Делитель для текущего режима. fight = «у Кида вынут меч». */ +uint8_t pop_pace_n(uint8_t fight); + +/* Счётчик кадров. volatile: его правит pop_beam_sample, а читают циклы + * ожидания — перечитывать обязаны каждый оборот. */ +extern volatile uint8_t pop_frame_tick; + +/* ВЫБОРКА ЛУЧА. Дёшево (~57 T с вызовом), не трогает bc/de/ix. + * Звать отовсюду, где иначе получился бы зазор длиннее 64 512 тактов: + * из обёрток рисования и с границ фаз. Модуль НЕ банковый, поэтому из + * банков зовётся прямым call, без трамплина. */ +void pop_beam_sample(void) __preserves_regs(b, c, d, e); + +/* Однократная подготовка: взвести cbl_mode, снять первое состояние бита + * и убедиться, что фронты идут. 0 — луч не работает (тогда пейсинг + * молча откатывается на прежнее поведение: n ожиданий gfx_wait_vsync). */ +uint8_t pop_pace_arm(void); + +/* Ждать ОДИН фронт луча. Внутри — тот же плотный опрос клавиатуры, что + * сейчас висит idle-хуком на gfx_wait_vsync (KBD-1: без опроса раз в + * ~0,5 мс теряются нажатия и залипают клавиши). */ +void pop_wait_edge(void); + +/* Добрать до n фронтов ОТ ЯКОРЯ и переставить якорь ПО ФАКТУ. + * Вызывающий обязан перед этим сделать хотя бы один pop_wait_edge() — + * на нём же и происходит своп страниц (tear-free). Якорь ставится + * по факту, а не anchor += n: догонять пропущенное время нельзя, иначе + * после тяжёлого кадра игра рванёт вперёд. */ +void pop_pace_end(uint8_t n); + +#endif diff --git a/applications/SprPoP/src/pop_pal.c b/applications/SprPoP/src/pop_pal.c new file mode 100644 index 0000000..bd13d55 --- /dev/null +++ b/applications/SprPoP/src/pop_pal.c @@ -0,0 +1,133 @@ +/* + * pop_pal.c — владелец порядка «load logical palette -> apply brightness». + * + * Снимок физически хранит pop_ui (в хвосте FONT.ATL), потому что только тот + * модуль владеет страницей шрифта. Здесь остаётся политика: kid.pal всегда + * дополняют живые слоты тайлсета и Тени, а file-load никогда не считается + * показом яркого кадра сам по себе. + */ +#include +#include +#include +#include "pop_bg.h" +#include "pop_shadow.h" +#include "pop_guard.h" +#include "pop_pal.h" +#include "pop_ui.h" /* POP_COL_OUTSIDE */ + +static void pal_file_load(const char *path, const char *fallback) +{ + if (gfx_pal_fload(0, path) < 0) gfx_pal_fload(0, fallback); + gfx_pal_sync(); + pop_ui_palette_snapshot(); +} + +void pop_pal_black(void) __banked +{ + pop_ui_palette_black(); +} + +void pop_pal_snapshot(void) __banked +{ + pop_ui_palette_snapshot(); +} + +void pop_pal_apply(uint8_t fade) __banked +{ + pop_ui_palette_dim(fade, 0); +} + +void pop_pal_fade_out(uint8_t steps) __banked +{ + pop_ui_fade_out(steps); +} + +void pop_pal_fade_in(uint8_t steps) __banked +{ + pop_ui_fade_in(steps); +} + +uint8_t pop_pal_fade_out_skip(uint8_t steps) __banked +{ + return pop_ui_fade_out_skip(steps); +} + +uint8_t pop_pal_fade_in_skip(uint8_t steps) __banked +{ + return pop_ui_fade_in_skip(steps); +} + +/* Слот «вне игрового поля» (POP_COL_OUTSIDE) — всегда ЧЁРНЫЙ. + * + * Это ещё один ДИНАМИЧЕСКИЙ слот поверх файловой палитры: kid.pal содержит + * все 256 записей и кладёт в единицу свой цвет, а нам она нужна чёрной — + * иначе борта и полоса HP станут цветными. Ставим после каждого + * file-load, рядом с тайлсетом/Тенью/соперником (см. pop_pal_game_load). */ +void pop_pal_outside_apply(void) __banked +{ + gfx_pal_set(0, POP_COL_OUTSIDE, 0, 0, 0); + gfx_pal_set(1, POP_COL_OUTSIDE, 0, 0, 0); +} + +void pop_pal_flash_white(uint8_t on) __banked +{ + uint8_t color = on ? 255 : 0; + + gfx_pal_set(0, 0, color, color, color); + gfx_pal_set(1, 0, color, color, color); +} + +void pop_pal_menu_dim(uint8_t fade) __banked +{ + pop_ui_palette_dim(fade, 1); +} + +void pop_pal_game_load(void) __banked +{ + pal_file_load("KID\\kid.pal", "a:\\kid.pal"); + /* kid.pal содержит все 256 записей и намеренно затирает эти три + * динамических диапазона (тайлсет, Тень, набор соперника). + * Восстановить их надо ДО snapshot. */ + pop_bg_pal_apply(); + pop_shadow_pal_apply(); + pop_guard_pal_apply(); + pop_pal_outside_apply(); + pop_ui_palette_snapshot(); +} + +void pop_pal_level_load(uint8_t full) __banked +{ + if (full) { + pop_pal_game_load(); + return; + } + /* При обычной границе уровня меняются только env/wall и набор соперника. */ + pop_bg_pal_apply(); + pop_guard_pal_apply(); + pop_pal_outside_apply(); + pop_ui_palette_snapshot(); +} + +void pop_pal_title_load(void) __banked +{ + pal_file_load("TITLE\\title.pal", "a:\\title.pal"); +} + +/* ЕДИНСТВЕННОЕ ОТЛИЧИЕ ФИНАЛА ОТ ИНТРО — цвет фона текстовой рамки. + * load_title_images(bgcolor) SDLPoP красит его в #100060 на титрах и в + * #800000 в end_sequence (seg001:586); у нас фон запечён отдельным + * индексом POP_PAL_STORY_BG именно ради этой подмены. Она делается ПОСЛЕ + * файла и до нового снимка — как тайлсет/Тень в pop_pal_game_load. */ +void pop_pal_story_load(uint8_t ending) __banked +{ + pal_file_load("PV\\story.pal", "a:\\story.pal"); + if (!ending) return; + gfx_pal_set(0, POP_PAL_STORY_BG, 0x80, 0, 0); + gfx_pal_set(1, POP_PAL_STORY_BG, 0x80, 0, 0); + pop_ui_palette_snapshot(); +} + +void pop_pal_pv_load(void) __banked +{ + pal_file_load("PV\\pv.pal", "a:\\pv.pal"); +} diff --git a/applications/SprPoP/src/pop_pal.h b/applications/SprPoP/src/pop_pal.h new file mode 100644 index 0000000..2ef3968 --- /dev/null +++ b/applications/SprPoP/src/pop_pal.h @@ -0,0 +1,49 @@ +/* + * pop_pal.h — единый lifecycle палитры PoP, банк 10. + * + * Атласы запечены под фиксированные диапазоны: env/wall 0x50..0x6F, + * Kid 0x70..0x8F, страж 0x90..0x9F, Тень 0xA0..0xAF. Поэтому palette + * уровня нельзя «восстановить fade-ом» из старого снимка: сначала надо + * загрузить логический источник, затем только выбрать яркость apply/fade. + */ +#ifndef POP_PAL_H +#define POP_PAL_H + +#include + +/* Все 256 записей обеих графических страниц = чёрный; снимок не меняет. */ +void pop_pal_black(void) __banked; + +/* Логические источники палитры. Они обновляют снимок, но сами не выбирают + * яркость: caller обязан явно вызвать apply()/fade_in(). */ +void pop_pal_game_load(void) __banked; +void pop_pal_level_load(uint8_t full) __banked; +void pop_pal_title_load(void) __banked; +/* Фон текстовой рамки story лежит в отдельном индексе палитры: ending=1 + * красит его тёмно-красным (финал), 0 оставляет файловый тёмно-синий. */ +#define POP_PAL_STORY_BG 16 +void pop_pal_story_load(uint8_t ending) __banked; +void pop_pal_pv_load(void) __banked; + +/* Показ текущего снимка: 0 — исходная яркость, 4 — чёрный. */ +void pop_pal_snapshot(void) __banked; +void pop_pal_apply(uint8_t fade) __banked; +void pop_pal_fade_out(uint8_t steps) __banked; +void pop_pal_fade_in(uint8_t steps) __banked; +/* Прерываемые: заставка и катсцены. 1 — прервано новым нажатием. */ +uint8_t pop_pal_fade_out_skip(uint8_t steps) __banked; +uint8_t pop_pal_fade_in_skip(uint8_t steps) __banked; + +/* Слот POP_COL_OUTSIDE (всё вне игрового поля) — чёрным. Динамический + * слот: file-load палитры кладёт в него своё, поэтому доплачивается после + * каждого источника, как тайлсет/Тень/соперник. */ +void pop_pal_outside_apply(void) __banked; + +/* do_flash/remove_flash SDLPoP: меняется только нулевая запись обеих + * page-палитр, сама картинка и снимок логической палитры не затрагиваются. */ +void pop_pal_flash_white(uint8_t on) __banked; + +/* Меню — единственный пользователь keep_ui (0x37..0x3F не темнеют). */ +void pop_pal_menu_dim(uint8_t fade) __banked; + +#endif /* POP_PAL_H */ diff --git a/applications/SprPoP/src/pop_qsave.c b/applications/SprPoP/src/pop_qsave.c new file mode 100644 index 0000000..634b087 --- /dev/null +++ b/applications/SprPoP/src/pop_qsave.c @@ -0,0 +1,253 @@ +/* + * pop_qsave.c — файловая транзакция и схема игрового снимка. + * Живёт в холодном банке: в обычном кадре исполняется только проверка + * резидентного флага запроса, вся тяжёлая работа бывает по F6/F9. + */ +#include +#include +#include /* rename */ +#include +#include +#include "pop_qsave.h" +#include "pop_status.h" +#include "pop_kid.h" +#include "pop_guard.h" +#include "pop_map.h" +#include "pop_level.h" +#include "pop_trob.h" +#include "pop_bg.h" +#include "pop_state.h" +#include "pop_tile.h" +#include "pop_sfx.h" +#include "pop_pace.h" +#include "pop_timer.h" +#include "sprpop_cold.h" + +#define QS_VERSION 4 +#define QS_SAVE 1 +#define QS_LOAD 2 +#define QS_LOAD_BACKUP 3 +#define QS_LIMIT 4096 +#define QS_BASE 0x100 + +extern uint8_t pop_qsave_request; + +static void qs_char(pop_qs_io_t *io, pop_char_t *c) +{ + pop_qs_u8(io, &c->frame); pop_qs_u8(io, &c->x); pop_qs_u8(io, &c->y); + pop_qs_i8(io, &c->direction); pop_qs_i8(io, &c->curr_col); + pop_qs_i8(io, &c->curr_row); pop_qs_u8(io, &c->action); + pop_qs_i8(io, &c->fall_x); pop_qs_i8(io, &c->fall_y); + pop_qs_u8(io, &c->room); pop_qs_u8(io, &c->repeat); + pop_qs_u8(io, &c->charid); pop_qs_u8(io, &c->sword); + pop_qs_i8(io, &c->alive); pop_qs_u16(io, &c->curr_seq); +} + +static void qs_payload(pop_qs_io_t *io) +{ + uint8_t speed=pop_speed_mode, immortal=pop_immortal, sound=pop_snd_want; + + /* Пользовательские параметры входят в снимок, а не остаются свойством + * текущего запуска. Читаем сначала во временные байты: повреждённые + * значения не должны частично применить остальное игровое состояние. */ + pop_qs_u8(io, &speed); pop_qs_u8(io, &immortal); pop_qs_u8(io, &sound); + if (io->reading && io->apply) { + if (speed >= POP_SPEED_MODES || immortal > 2 || sound > 1) { + io->failed=1; return; + } + pop_speed_mode=speed; pop_immortal=immortal; pop_snd_want=sound; + } + /* Глобальный timer сохраняется до состояния уровня: QuickLoad на том же + * уровне вернёт ровно тот логический кадр, без «бесплатной» минуты. */ + pop_timer_qsave(io); + + qs_char(io, &Kid); qs_char(io, &Guard); + pop_qs_u8(io, &cur_room); pop_qs_u8(io, &kid_room); + pop_qs_u8(io, &pop_checkpoint); pop_qs_u8(io, &pop_next_level); + pop_qs_i8(io, &knock); + + pop_qs_u8(io, &hitp_curr); pop_qs_u8(io, &hitp_max); + pop_qs_u8(io, &hitp_beg_lev); pop_qs_i8(io, &hitp_delta); + pop_qs_u8(io, &guardhp_curr); pop_qs_u8(io, &guardhp_max); + pop_qs_i8(io, &guardhp_delta); pop_qs_u8(io, &guard_skill); + pop_qs_i8(io, &guard_refrac); pop_qs_i8(io, &justblocked); + pop_qs_i8(io, &kid_sword_strike); pop_qs_i8(io, &offguard); + pop_qs_i8(io, &holding_sword); pop_qs_u8(io, &pop_guard_hurt); + pop_qs_i8(io, &can_guard_see_kid); pop_qs_i8(io, &is_guard_notice); + pop_qs_i8(io, &pop_united_shadow); pop_qs_u8(io, &pop_shadow_init); + pop_qs_i8(io, &pop_guard_notice_timer); + pop_qs_u16(io, &pop_fight_seed.lo); pop_qs_u16(io, &pop_fight_seed.hi); + + pop_qs_u8(io, &pop_kid_dead); pop_qs_u8(io, &pop_kid_hurt); + pop_qs_u8(io, &pop_have_sword); pop_qs_u8(io, &pop_item_taken); + pop_qs_u8(io, &pop_flash_time); pop_qs_u8(io, &pop_flash_color); + pop_qs_u8(io, &pop_feather); pop_qs_u8(io, &pop_upside); + pop_qs_u8(io, &pop_upside_want); pop_qs_u8(io, &pop_upside_dirty); + pop_qs_u8(io, &pop_leave_dir); pop_qs_u8(io, &pop_leave_timer); + pop_qs_u8(io, &pop_loose_fell); pop_qs_u8(io, &pop_ceil_fell); + pop_qs_u8(io, &pop_debris_at); pop_qs_u8(io, &pop_fell_out); + pop_qs_bytes(io, pop_loose_modif, sizeof(pop_loose_modif)); + pop_qs_bytes(io, pop_ceil_modif, sizeof(pop_ceil_modif)); + + pop_qs_u8(io, &pop_loose_landed); pop_qs_u8(io, &pop_loose_exit); + pop_qs_u8(io, &pop_loose_exit_room); pop_qs_u8(io, &pop_droppedout); + pop_qs_u16(io, &pop_leveldoor_open); pop_qs_i8(io, &pop_jumped_mirror); + pop_qs_u8(io, &pop_seamless); pop_qs_u8(io, &pop_nav_hold); + + pop_level_qsave(io); + pop_trob_qsave(io); + pop_loose_mob_qsave(io); + pop_map_qsave(io); + pop_guard_ai_qsave(io); +} + +static void qs_init(pop_qs_io_t *io, uint8_t page, uint16_t limit, + uint8_t reading, uint8_t apply) +{ + io->page=page; io->pos=QS_BASE; io->limit=(uint16_t)(QS_BASE+limit); + io->checksum=0; + io->count=0; io->reading=reading; io->apply=apply; io->failed=0; +} + +static uint8_t qs_header_read(pop_qs_io_t *io, uint8_t *level) +{ + uint8_t h[7]; + if (!pop_qs_raw_get(io,h,sizeof(h))) return 0; + if (h[0]!='P' || h[1]!='O' || h[2]!='P' || h[3]!='Q' || + h[4] != QS_VERSION || h[5] < POP_LEVEL_FIRST || + h[5] > POP_LEVEL_LAST || h[6] != 0) return 0; + *level = h[5]; return 1; +} + +static uint8_t qs_validate_page(uint8_t page, uint16_t size, + uint8_t *level, uint16_t *payload_size) +{ + pop_qs_io_t io; + uint8_t tail[4]; + uint16_t n, sum; + if (size < 11) return 0; + qs_init(&io,page,size,1,0); + if (!qs_header_read(&io,level)) return 0; + io.pos=(uint16_t)(QS_BASE+size-4); + if (!pop_qs_raw_get(&io,tail,4)) return 0; + n = (uint16_t)(tail[0] | ((uint16_t)tail[1] << 8)); + sum = (uint16_t)(tail[2] | ((uint16_t)tail[3] << 8)); + if (n != (uint16_t)(size-11) || + sum != pop_qs_checksum_page(page,QS_BASE+7,n)) return 0; + *payload_size=n; return 1; +} + +static uint8_t qs_apply(uint8_t page, uint16_t size, uint16_t payload_size, + uint8_t *level) +{ + pop_qs_io_t io; + qs_init(&io,page,size,1,1); + if (!qs_header_read(&io,level)) return 0; + qs_payload(&io); + return (uint8_t)(!io.failed && io.count == payload_size && + io.pos == (uint16_t)(QS_BASE+7+payload_size)); +} + +static int8_t qs_save(uint8_t page) +{ + pop_qs_io_t io; + uint8_t tail[4]; + const uint8_t h[7]={'P','O','P','Q',QS_VERSION,pop_current_level,0}; + uint16_t size; + qs_init(&io,page,QS_LIMIT,0,0); + pop_qs_raw_put(&io,h,sizeof(h)); + qs_payload(&io); + tail[0]=(uint8_t)io.count; tail[1]=(uint8_t)(io.count >> 8); + io.checksum=pop_qs_checksum_page(page,QS_BASE+7,io.count); + tail[2]=(uint8_t)io.checksum; tail[3]=(uint8_t)(io.checksum >> 8); + pop_qs_raw_put(&io,tail,4); size=(uint16_t)(io.pos-QS_BASE); + if (io.failed) return -1; + unlink("POP.NEW"); + if (bank_save_file(page,QS_BASE,"POP.NEW",size) != size) { + unlink("POP.NEW"); return -1; + } + unlink("POP.BAK"); + (void)rename("POP.SAV", "POP.BAK"); + if (rename("POP.NEW", "POP.SAV") != 0) { + (void)rename("POP.BAK", "POP.SAV"); return -1; + } + return 1; +} + +static int8_t qs_load_one(const char *name, uint8_t page) +{ + uint8_t level; + uint16_t payload_size; + int size=bank_load_file(page,QS_BASE,name,QS_LIMIT); + if (size <= 0 || !qs_validate_page(page,(uint16_t)size,&level, + &payload_size)) return -1; + if (level != pop_current_level) { + pop_next_level = level; + if (pop_level_switch() != 0 || pop_current_level != level) return -1; + } + if (!qs_apply(page,(uint16_t)size,payload_size,&level)) return -1; + Char = Kid; Opp = Guard; + pop_qsave_restore_room(cur_room); + return 1; +} + +/* Проверка слотов для меню. Это именно probe: не вызывает pop_level_switch, + * не применяет payload и не трогает звук. Отдельная EMM-страница делает + * проверку независимой от живой страницы уровня. */ +uint8_t pop_qsave_probe(void) __banked +{ + uint8_t blk, page, level, slots = 0; + uint16_t payload_size; + int size; + + blk = mem_alloc_pages(1); + if (!blk) return 0; + page = mem_get_page(blk, 0); + gfx_w0_page_prepare(page); + + size = bank_load_file(page, QS_BASE, "POP.SAV", QS_LIMIT); + if (size > 0 && qs_validate_page(page, (uint16_t)size, &level, + &payload_size)) + slots |= POP_QS_SLOT_SAVE; + size = bank_load_file(page, QS_BASE, "POP.BAK", QS_LIMIT); + if (size > 0 && qs_validate_page(page, (uint16_t)size, &level, + &payload_size)) + slots |= POP_QS_SLOT_BACKUP; + + mem_free_block(blk); + return slots; +} + +int8_t pop_qsave_process(void) __banked +{ + uint8_t request = pop_qsave_request; + uint8_t blk,page; + int8_t rc=-1; + pop_qsave_request = 0; + if (!request) return 0; + /* Лейбл — ДО дисковой работы: она длится доли секунды, игра на это + * время замирает, и строка обязана объяснить фриз, а не подводить под + * ним итог. Не удалось — ниже перепишем на NO QUICKSAVE/QUICKLOAD. */ + pop_status_show_now((uint8_t)(request == QS_SAVE ? POP_MSG_QUICKSAVE + : POP_MSG_QUICKLOAD)); + blk=mem_alloc_pages(1); + if (!blk) return -1; + page=mem_get_page(blk,0); + gfx_w0_page_prepare(page); + pop_sfx_pause(); + if (request == QS_SAVE) rc = qs_save(page); + else if (request == QS_LOAD_BACKUP) rc = qs_load_one("POP.BAK", page); + else { + rc = qs_load_one("POP.SAV",page); + if (rc < 0) rc = qs_load_one("POP.BAK",page); + } + pop_sfx_start(); + mem_free_block(blk); + /* Успех ничего не меняет: нужная строка висит с начала операции. + * Отказ переписывает её на NO QUICKSAVE/NO QUICKLOAD — обычной + * заявкой, кадр её подхватит (порт seg000:497/514). */ + if (rc <= 0) + pop_status_msg = (uint8_t)(request == QS_SAVE ? POP_MSG_NO_QUICKSAVE + : POP_MSG_NO_QUICKLOAD); + return rc; +} diff --git a/applications/SprPoP/src/pop_qsave.h b/applications/SprPoP/src/pop_qsave.h new file mode 100644 index 0000000..564c296 --- /dev/null +++ b/applications/SprPoP/src/pop_qsave.h @@ -0,0 +1,48 @@ +/* + * pop_qsave.h — потоковый QuickSave/QuickLoad одного игрового слота. + * + * На диске используются POP.NEW, POP.SAV и POP.BAK. Формат не содержит + * указателей, файловых дескрипторов и графических кэшей; многобайтные поля + * записываются явно little-endian. Внутренние функции pop_qs_* нужны + * модулям-владельцам закрытого состояния. + */ +#ifndef POP_QSAVE_H +#define POP_QSAVE_H + +#include + +typedef struct { + uint8_t page; + uint16_t pos; + uint16_t limit; + uint16_t checksum; + uint16_t count; + uint8_t reading; + uint8_t apply; + uint8_t failed; +} pop_qs_io_t; + +void pop_qs_bytes(pop_qs_io_t *io, void *data, uint16_t size); +void pop_qs_u8(pop_qs_io_t *io, uint8_t *value); +void pop_qs_i8(pop_qs_io_t *io, int8_t *value); +void pop_qs_u16(pop_qs_io_t *io, uint16_t *value); +void pop_qs_i16(pop_qs_io_t *io, int *value); +void pop_qs_raw_put(pop_qs_io_t *io, const uint8_t *data, uint8_t size); +uint8_t pop_qs_raw_get(pop_qs_io_t *io, uint8_t *data, uint8_t size); +uint16_t pop_qs_checksum_page(uint8_t page, uint16_t pos, uint16_t size); + +/* Вызывается в безопасной границе кадра. Возвращает: 0 — запроса нет, + * 1 — сохранено/загружено, -1 — ошибка. */ +int8_t pop_qsave_process(void) __banked; +void pop_qsave_request_save(void); +void pop_qsave_request_load(void); +void pop_qsave_request_load_backup(void); + +/* Валидность слотов без изменения состояния игры. Нужна menu для вопроса + * LOAD BACKUP?: основной SAV уже пригоден, backup спрашиваем только если + * основной отсутствует или повреждён. */ +#define POP_QS_SLOT_SAVE 0x01 +#define POP_QS_SLOT_BACKUP 0x02 +uint8_t pop_qsave_probe(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_qsave_coldio.c b/applications/SprPoP/src/pop_qsave_coldio.c new file mode 100644 index 0000000..511db08 --- /dev/null +++ b/applications/SprPoP/src/pop_qsave_coldio.c @@ -0,0 +1,38 @@ +/* + * Холодная часть ввода-вывода QuickSave (банк 8). + * + * Эти функции вызываются только банковым оркестратором pop_qsave.c, поэтому + * им не место в дефицитном резидентном _CODE. Здесь только доступ через W0: + * переключающие W3 DSS-операции обязаны оставаться в pop_qsave_io.c. + */ +#include "pop_qsave.h" +#include + +void pop_qs_raw_put(pop_qs_io_t *io, const uint8_t *data, uint8_t size) +{ + uint8_t *q, i; + if (io->pos + size > io->limit) { io->failed=1; return; } + gfx_w0_map(io->page); q=(uint8_t *)io->pos; + for (i=0; ipos += size; +} + +uint8_t pop_qs_raw_get(pop_qs_io_t *io, uint8_t *data, uint8_t size) +{ + uint8_t *q, i; + if (io->pos + size > io->limit) return 0; + gfx_w0_map(io->page); q=(uint8_t *)io->pos; + for (i=0; ipos += size; return 1; +} + +/* XOR8 достаточно для обнаружения случайной порчи транзакционного файла; + * размер payload проверяется отдельно, старший байт содержит инверсию XOR. */ +uint16_t pop_qs_checksum_page(uint8_t page, uint16_t pos, uint16_t size) +{ + uint8_t *p, sum=0; + gfx_w0_map(page); p=(uint8_t *)pos; + while(size--) sum ^= *p++; + gfx_w0_unmap(); + return (uint16_t)(sum | ((uint16_t)(uint8_t)~sum << 8)); +} diff --git a/applications/SprPoP/src/pop_qsave_io.c b/applications/SprPoP/src/pop_qsave_io.c new file mode 100644 index 0000000..cc4fc28 --- /dev/null +++ b/applications/SprPoP/src/pop_qsave_io.c @@ -0,0 +1,34 @@ +/* Резидентная часть QuickSave: часто вызываемые межбанковые примитивы + * потока и флаг запроса. Файловый обмен с W3 теперь общий в libc. */ +#include "pop_qsave.h" +#include +#include + +uint8_t pop_qsave_request; +void pop_qsave_request_save(void) { pop_qsave_request=1; } +void pop_qsave_request_load(void) { pop_qsave_request=2; } +void pop_qsave_request_load_backup(void) { pop_qsave_request=3; } + +void pop_qs_bytes(pop_qs_io_t *io, void *data, uint16_t size) +{ + uint8_t *p=(uint8_t *)data, *q; + if (io->failed || io->pos + size > io->limit) { io->failed=1; return; } + gfx_w0_map(io->page); q=(uint8_t *)io->pos; + if (!io->reading) memcpy(q,p,size); + else if (io->apply) memcpy(p,q,size); + gfx_w0_unmap(); io->pos += size; io->count += size; +} +void pop_qs_u8(pop_qs_io_t *io, uint8_t *v) { pop_qs_bytes(io,v,1); } +void pop_qs_i8(pop_qs_io_t *io, int8_t *v) { pop_qs_bytes(io,v,1); } +void pop_qs_u16(pop_qs_io_t *io, uint16_t *value) +{ + uint8_t b[2]; b[0]=(uint8_t)*value; b[1]=(uint8_t)(*value >> 8); + pop_qs_bytes(io,b,2); + if (io->reading && io->apply && !io->failed) + *value=(uint16_t)(b[0] | ((uint16_t)b[1] << 8)); +} +void pop_qs_i16(pop_qs_io_t *io, int *value) +{ + uint16_t v=(uint16_t)*value; pop_qs_u16(io,&v); + if (io->reading && io->apply && !io->failed) *value=(int)v; +} diff --git a/applications/SprPoP/src/pop_redraw.c b/applications/SprPoP/src/pop_redraw.c new file mode 100644 index 0000000..d8e3018 --- /dev/null +++ b/applications/SprPoP/src/pop_redraw.c @@ -0,0 +1,156 @@ +/* + * pop_redraw.c — пометки перерисовки тайлов и их разбор. См. pop_redraw.h. + * + * Живёт в W1/W2 (НЕ в резиденте W3): пометки ставит логика, которая должна + * оставаться достижимой из будущих __banked-модулей. Сам разбор зовёт + * pop_bg (резидент W3) — это законно, потому что разбор дёргает только + * главный цикл. + */ +#include +#include "pop_redraw.h" +#include "pop_bg.h" +#include "_pop_bg.h" /* pop_bake_slot_reset — тот же банк, без трамплина */ + +#define NTILES 30 +#define NCOLS 10 + +static uint8_t rd_kind[NTILES], rd_cnt[NTILES]; +static uint8_t rda_kind[NCOLS], rda_cnt[NCOLS]; +/* Сколько всего непустых пометок: без него проход по 40 ячейкам крутился бы + * КАЖДЫЙ кадр впустую (замер 2026-07-27 на прежнем rest-цикле: ~12 % кадра). */ +static uint8_t rd_pending; +/* Пометки ПЕРЕДНЕГО слоя — однокадровые, отдельно от rd_*: те живут по + * страницам дабл-буфера (запекают фон), а эти транзиентны. */ +static uint8_t rdf[NTILES]; +static uint8_t rdf_pending; +/* Передний слой РЯДА −1 (полоса кладки у потолка). Отдельным набором, как и + * сами перерисовки ряда −1 (rda_*): у оригинала это вообще другой проход — + * redraw_needed_tiles сначала обходит ряды 2,1,0, а ПОТОМ ряд 2 комнаты сверху + * (seg008:1B06), и его draw_tile_fore кладёт куски в foretable, то есть поверх + * midtable, где лежат падающие плиты, и поверх объектов. */ +static uint8_t rdfa[NCOLS]; +static uint8_t rdfa_pending; + +void pop_redraw_reset(void) __banked +{ + uint8_t i; + for (i = 0; i < NTILES; i++) { rd_kind[i] = 0; rd_cnt[i] = 0; } + for (i = 0; i < NCOLS; i++) { rda_kind[i] = 0; rda_cnt[i] = 0; } + rd_pending = 0; + for (i = 0; i < NTILES; i++) rdf[i] = 0; + for (i = 0; i < NCOLS; i++) rdfa[i] = 0; + pop_fore_pass_end(); + rdf_pending = 0; + rdfa_pending = 0; +} + +void pop_set_redraw_fore(uint8_t tilepos) __banked +{ + if (tilepos >= NTILES) return; + if (!rdf[tilepos]) rdf_pending++; + rdf[tilepos] = 1; +} + +/* То же для полосы кладки У ПОТОЛКА (ряд −1). Ставит падающий кусок, когда + * его спрайт достаёт до кромки потолка: иначе кусок накрывает эту кромку + * собой, чего быть не может (найдено пользователем 2026-08-17, ур.1 к.6 — + * плита (−1,5) перекрывала потолок (−1,6)). */ +void pop_set_redraw_fore_above(uint8_t col) __banked +{ + if (col >= NCOLS) return; + if (!rdfa[col]) rdfa_pending++; + rdfa[col] = 1; +} + +/* Разбор пометок переднего слоя: тайлы обходятся в том же порядке, что и + * фон, и рисуются ПОСЛЕ всех объектов кадра — как foretable у оригинала + * (draw_tables, seg008:1373: back -> mid -> fore). */ +void pop_fore_needed(void) __banked +{ + uint8_t i, row = 0, col = 0; + if (!rdf_pending && !rdfa_pending) return; + pop_fore_pass_begin(); + for (i = 0; i < NTILES; i++) { + if (rdf[i]) { rdf[i] = 0; pop_fore_tile_b(row, col); } + if (++col == 10) { col = 0; row++; } + } + rdf_pending = 0; + /* Ряд −1 — ПОСЛЕДНИМ, как в redraw_needed_tiles (seg008:1B06). */ + if (rdfa_pending) { + for (i = 0; i < NCOLS; i++) + if (rdfa[i]) { rdfa[i] = 0; pop_ceil_fore_tile_b(i); } + rdfa_pending = 0; + } +} + +void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked +{ + if (tilepos >= NTILES) return; + if (!rd_cnt[tilepos]) rd_pending++; + else pop_bake_slot_reset(tilepos); /* перепометка — копия запечки не годится */ + /* ПРИОРИТЕТ ПОЛНОЙ ПЕРЕРИСОВКИ НАД слоем anim. У оригинала это не + * конфликт видов, а два независимых счётчика, и полная побеждает + * (redraw_needed, seg008:0211: `if (redraw_frames_full) draw_tile(); + * else if (redraw_frames_anim) {...}`). У нас вид ОДИН на тайл, и без + * этой проверки исход решал бы порядок trob'ов в списке: чомпер, который + * СЕЙЧАС смыкает челюсти, ставит POP_RD_CHOMP (ему нужен heal — поза + * меняется), а факел слева тут же метил бы тот же тайл как + * POP_RD_CHOMP_ANIM, и heal пропал бы — новая поза легла бы поверх + * старой. */ + if (!(kind == POP_RD_CHOMP_ANIM && rd_kind[tilepos] == POP_RD_CHOMP && + rd_cnt[tilepos])) + rd_kind[tilepos] = kind; + if (pages > rd_cnt[tilepos]) rd_cnt[tilepos] = pages; +} + +void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked +{ + if (col >= NCOLS) return; + if (!rda_cnt[col]) rd_pending++; + else pop_bake_slot_reset_above(col); /* перепометка — копия запечки не годится */ + rda_kind[col] = kind; + if (pages > rda_cnt[col]) rda_cnt[col] = pages; +} + +/* redraw_needed (seg008:0178): пройти пометки и нарисовать. row/col ведём + * бегущими счётчиками, а не через i/10 и i%10 — у Z80 нет деления, и SDCC + * выносит __divuchar/__moduchar в вершину цикла как общее подвыражение + * аргументов (30*2 делений за кадр даже когда рисовать нечего). */ +void pop_redraw_needed(void) __banked +{ + uint8_t i, row = 0, col = 0, left = 0, cnt = 0; /* cnt — ВРЕМЕННО, замер */ + pop_dbg_rdmax_tot = 0; + if (!rd_pending) return; + for (i = 0; i < NTILES; i++) { + if (rd_cnt[i]) { + pop_dbg_kind(rd_kind[i]); /* ВРЕМЕННО: какой вид перерисовки */ + switch (rd_kind[i]) { + case POP_RD_SPIKE: pop_spike_redraw(row, col); break; + case POP_RD_LOOSE: pop_loose_shake_draw(row, col); break; + case POP_RD_LOOSE_GONE: pop_loose_bake_empty(row, col); break; + case POP_RD_FLOOR: pop_floor_bake(row, col); break; + case POP_RD_LEVELDOOR: pop_leveldoor_redraw(row, col); break; + case POP_RD_GATE: pop_gate_redraw(row, col); break; + case POP_RD_CHOMP: pop_chomp_redraw(row, col); break; + case POP_RD_CHOMP_ANIM: pop_chomp_anim_draw(row, col); break; + default: break; + } + cnt++; /* ВРЕМЕННО: замер */ + if (--rd_cnt[i]) left++; + else rd_kind[i] = 0; + } + if (++col == 10) { col = 0; row++; } + } + for (i = 0; i < NCOLS; i++) { + if (rda_cnt[i]) { + if (rda_kind[i] == POP_RDA_CEIL) pop_ceil_shake_draw(i); + else pop_ceil_bake_empty(i); + cnt++; /* ВРЕМЕННО: замер */ + if (--rda_cnt[i]) left++; + else rda_kind[i] = 0; + } + } + rd_pending = left; + pop_dbg_rdmax_tot = cnt; /* ВРЕМЕННО: замер */ + if (cnt > pop_dbg_rdmax[0]) pop_dbg_rdmax[0] = cnt; +} diff --git a/applications/SprPoP/src/pop_redraw.h b/applications/SprPoP/src/pop_redraw.h new file mode 100644 index 0000000..7ab303f --- /dev/null +++ b/applications/SprPoP/src/pop_redraw.h @@ -0,0 +1,68 @@ +/* + * pop_redraw.h — пометки «перерисовать тайл» и их разбор. + * + * Порт архитектуры оригинала: логика анимации НИЧЕГО не рисует, она ставит + * флаг (`set_redraw_full`/`set_wipe`/`redraw_20h`/`redraw_21h`, seg007), а + * отрисовка идёт отдельным проходом (`redraw_needed`, seg008:0178). У нас + * так же: pop_map/pop_trob зовут pop_set_redraw*, а pop_redraw_needed() + * (главный цикл) разбирает пометки и дёргает pop_bg. + * + * ЗАЧЕМ (кроме сходства с SDLPoP): из __banked-кода резидентная страница W3 + * недостижима — ни напрямую, ни транзитивно (docs/layout_plan_v2.md §2 R2). + * Пока pop_map звал pop_bg, его нельзя было использовать из банка стражей. + * Пометки живут в W1/W2 и зовутся откуда угодно. + * + * ОТЛИЧИЕ ОТ ОРИГИНАЛА: у нас не один экран с dirty-rect, а две страницы + * дабл-буфера со своей ОЗУ-копией фона, поэтому счётчик пометки — это ЧИСЛО + * СТРАНИЦ (обычно 2), а вид перерисовки хранится явно: heal+рисовать поверх + * (транзиент) или запечь новый фон. В оригинале вид кодируется тем, в какой + * из таблиц redraw_frames_* стоит флаг. + */ +#ifndef POP_REDRAW_H +#define POP_REDRAW_H + +#include + +/* Виды перерисовки тайла (row*10+col). */ +#define POP_RD_SPIKE 1 /* пики: heal + оба тайла (redraw_21h) */ +#define POP_RD_LOOSE 2 /* loose: дрожащий кадр / кадр покоя */ +#define POP_RD_LOOSE_GONE 3 /* loose провалился: запечь пусто */ +#define POP_RD_FLOOR 4 /* пол изменился: кнопка/щебень/поднятый меч */ +#define POP_RD_LEVELDOOR 5 /* створка двери уровня (ПРАВАЯ половина) */ +#define POP_RD_GATE 6 /* решётка ворот (tilepos = САМИ ворота) */ +#define POP_RD_CHOMP 7 /* чомпер: кадр смыкания челюстей */ +#define POP_RD_CHOMP_ANIM 8 /* чомпер: вернуть ТОЛЬКО слой anim поверх огня */ + +/* Виды перерисовки полосы у потолка (по колонке). */ +#define POP_RDA_CEIL 1 /* плита-потолок: дрожащий кадр / покой */ +#define POP_RDA_CEIL_GONE 2 /* плита-потолок провалилась: колодец */ + +/* Пометить тайл/колонку: kind — вид, pages — на скольких страницах + * дабл-буфера перерисовать (1 = только текущая, 2 = обе). Повторная + * пометка перекрывает вид и берёт максимум счётчика. */ +void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked; + +/* ПЕРЕДНИЙ СЛОЙ по пометкам — порт redraw_frames_fore/set_redraw_fore + * (seg007:0550). В оригинале эти пометки ставят ОБЪЕКТЫ, каждый вокруг + * себя: персонаж — прямоугольник футпринта (redraw_at_char, seg003:0576), + * падающий кусок — соседа справа и второй тайл на границе рядов (draw_mob, + * seg007:1147), анимация тайла — свой тайл (draw_trob). Пометка + * ОДНОКАДРОВАЯ: передний слой транзиентен, его снимает heal. + * + * Разбор (pop_fore_needed) звать ПОСЛЕ отрисовки всех объектов кадра. */ +void pop_set_redraw_fore(uint8_t tilepos) __banked; + +/* Пометить передний слой полосы у потолка (ряд −1), столбец col. Ставит + * падающий кусок, достающий до кромки потолка. */ +void pop_set_redraw_fore_above(uint8_t col) __banked; +void pop_fore_needed(void) __banked; +void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked; + +/* Разобрать пометки: нарисовать всё помеченное на ТЕКУЩЕЙ (back) странице и + * уменьшить счётчики. Звать из главного цикла в слое ФОНА — до персонажей. */ +void pop_redraw_needed(void) __banked; + +/* Сбросить все пометки (смена комнаты/уровня). */ +void pop_redraw_reset(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_room.c b/applications/SprPoP/src/pop_room.c new file mode 100644 index 0000000..e5dbf69 --- /dev/null +++ b/applications/SprPoP/src/pop_room.c @@ -0,0 +1,2009 @@ +/* + * pop_room.c — ХОЛОДНАЯ половина слоя фона PoP: полная отрисовка комнаты + * (порт seg008.c draw_tile / render_room.py), точечные перерисовки тайлов + * при событиях (пики, ворота, кнопки, дверь уровня, дрожащие плиты) и + * падающие куски loose (mob, seg007 move_loose). Всё это работает раз на + * комнату или раз на событие, поэтому живёт в ОТДЕЛЬНОМ банке и платит + * трамплин за вызовы горячей половины — см. _pop_bg.h, там же граница реза. + * + * Только DUNGEON-стиль (Фаза 1). + */ +/* Имена файлов атласов обоих тайлсетов нужны только здесь; макрос обязан + * стоять ДО первого включения pop_bg_atlas.h (его тянет pop_tile.h). */ +#define POP_BG_ATLAS_NAMES +#include +#include +#include +#include +#include +#include "pop_bg.h" +#include "pop_sfx.h" /* звуковые эффекты (../docs/sound_plan.md) */ +#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */ +#include "_pop_bg.h" /* контракт с горячей половиной (pop_bg.c, банк 2) */ +#include "pop_tile.h" /* общие листья слоя фона (резидент): блит, тайлы, таблица */ +#include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */ +#include "pop_arc.h" +#include "bg_arc.h" +#include "pop_state.h" +#include "pop_redraw.h" /* пометки тайлов: сосед падающего куска (draw_mob) */ +#include "pop_geom.h" +#include "pop_level.h" /* pop_doorlink2 — состояние нажатия кнопки */ +#include "pop_kid.h" /* Kid.curr_col/curr_row — порядок оверлеев */ +#include "pop_cdraw.h" /* pop_cd — габарит кадра Кида (y-порядок плиты) */ +#include "pop_map.h" /* pop_upside — переворот (зелье инверсии) */ +#include +#include + +/* Фазы loose-плит: массив ТЕКУЩЕЙ комнаты (pop_map) и персистентный per-room + * (pop_trob) плюс постановка trob. Объявлены здесь, а не включением обоих + * заголовков: нужна ровно одна ячейка каждому — падающий кусок срывает + * плиту, в которую врезался (loose_fall, см. mob_break_loose). */ +extern uint8_t pop_loose_modif[30]; +uint8_t *pop_trob_modif(uint8_t room) __banked; +void pop_add_trob(uint8_t room, uint8_t tilepos, int8_t type) __banked; +/* remove_loose для ОДНОГО тайла — снять плиту с карты немедленно (честный + * loose_fall, см. mob_break_loose). Живёт в pop_map: там рабочая копия + * тайлов комнаты и фазы плит. */ +void pop_loose_remove(uint8_t room, uint8_t tilepos) __banked; + +/* ---- Тайлсет: подземелье / дворец ----------------------------------- * + * Порт tbl_envir_ki[tbl_level_type[level]] (seg000:1108): оригинал под один + * и тот же набор id грузит РАЗНЫЙ .DAT — VDUNGEON или VPALACE. У нас так + * же: раскладка id -> (страница, idx) общая (упаковщик печёт оба набора по + * одному объединению id), меняются только файлы атласов и 32 записи + * палитры — env 0x50..0x5F и wall 0x60..0x6F. Кид, страж, меч и зелья + * живут в других слотах, поэтому общая kid.pal не трогается. */ +static uint8_t bg_set = 0xFF; /* какой набор сейчас загружен */ + +/* Ресурсы разложены по каталогам образа (8.3, как в DSS): BG — фон, KID — + * персонаж, GUARD — стражи, LEVELS — уровни. Иначе корень диска зарастает + * десятками .atl. Путь склеиваем на месте: держать по строке на каждый из + * двадцати атласов — лишние полкилобайта в банке. */ +/* Шаг набора в архиве: десять env + wall + fore. */ +#define BG_SET_STRIDE (POP_ENV_PAGES + 2) +#if (ARC_BG_PAL_ENV0 - ARC_BG_POP_ENV0) != BG_SET_STRIDE +#error "тайлсеты в архиве фона лежат не по двенадцать атласов" +#endif + +static char bg_path_buf[20]; + +static const char *bg_path(const char *name) +{ + char *d = bg_path_buf; + *d++ = 'B'; *d++ = 'G'; *d++ = '\\'; + while (*name) *d++ = *name++; + *d = 0; + return bg_path_buf; +} + +/* 32 записи (env, затем wall) в ОБЕ палитры: у страниц дабл-буфера они + * раздельные. Буфер — на стеке: gfx_pal_load отдаёт указатель BIOS ($A4), + * а тот читает только #4000-#BFFF, то есть банковую rodata бы не увидел + * (те же грабли, что в pop_guard_set_palette). */ +static int bg_load_tile_pal(const char *name) +{ + uint8_t buf[64]; + int fd = open(bg_path(name), O_RDONLY); + if (fd < 0) return -1; + if (read(fd, buf, 64) != 64) { close(fd); return -1; } + gfx_pal_load(0, POP_PAL_ENV, 16, buf); + gfx_pal_load(1, POP_PAL_ENV, 16, buf); + if (read(fd, buf, 64) != 64) { close(fd); return -1; } + gfx_pal_load(0, POP_PAL_WALL, 16, buf); + gfx_pal_load(1, POP_PAL_WALL, 16, buf); + close(fd); + return 0; +} + +/* ---- Композитный спрайт ПАДАЮЩЕГО КУСКА ------------------------------ * + * Три части (env 70 / 74 / 72, см. блок MOB_X0 ниже) складываются в ОДИН + * getimage-блоб в обычной памяти, и кусок рисуется одним блитом вместо трёх. + * + * Зачем: у блита ~8 800 такта постоянных накладных против ~5 000 на пиксели + * (memory blit_cost_model). Шесть кусков в воздухе давали 18 вызовов и + * 258 708 такта — БОЛЬШЕ ПОЛОВИНЫ цианового блока (замер 2026-08-17, + * docs/perf_cyan_phase.md). Плюс блоб в обычной памяти не требует + * atlas_image и gfx_w0_map/unmap — ещё ~1 350 такта на вызов. + * + * Прозрачность соблюдена: части ПЕРЕКРЫВАЮТСЯ (74 и 70 обе идут от mob_x), + * поэтому копируются попиксельно с пропуском 0xFF — ровно как три блита с + * прозрачностью друг поверх друга. + * + * Размер буфера — по худшему из тайлсетов (дворец даёт 63 в ширину, + * подземелье 19 в высоту), 1 201 Б статики W2. Куча SprPoP не + * используется (malloc'ов в приложении нет), так что это её и не задевает. + * + * Собирается на КАЖДУЮ смену тайлсета: размеры частей у наборов разные. */ +/* Коридор heal падающего куска — по фактической высоте СОБРАННОГО композита + * (у дворца он 16 строк, у подземелья 19). Держать константные 24 «на всякий + * случай» значило платить лишними строками на самой дорогой операции кадра, + * помноженными на число падающих плит. Ставит mob_spr_build; стартовые + * значения — безопасный максимум (см. MOB_HEAL_UP/MOB_HEAL_H ниже). */ +static uint8_t mob_heal_up = 20; +static uint8_t mob_heal_h = 24; + +/* Размер буфера — верхняя граница по обоим тайлсетам с запасом; фактические + * w/h композита считаются при сборке и кладутся в его шапку. */ +#define MOBS_MAXW 64 +#define MOBS_MAXH 20 +static uint8_t mob_spr[4 + MOBS_MAXW * MOBS_MAXH]; +static uint8_t mob_spr_ok; /* 0 — не собрался, рисуем тремя блитами */ + +/* Части композита: id, смещение по x, смещение НИЗА части относительно mob_y. + * Порядок — как в mob_render (низ, левая часть, правая), он важен: части + * перекрываются. */ +static const uint8_t MOBS_ID[3] = {74, 70, 72}; +static const uint8_t MOBS_XO[3] = {0, 0, 32}; +static const int8_t MOBS_YBO[3] = {0, -3, -1}; + +static void mob_spr_build(void) +{ + uint8_t w[3], h[3], k, r, c, v, cw, ch; + const uint8_t *src; + int top = 0, row0; + + mob_spr_ok = 0; + /* Проход 1 — габариты частей. Считать их обязательно, а не брать + * константами: у тайлсетов они РАЗНЫЕ (напр. правая часть 72 — 26 px в + * подземелье и 25 во дворце). */ + for (k = 0; k < 3; k++) { + atlas_t *a = &pop_env[MOBS_ID[k] >> POP_ENV_SHIFT]; + uint8_t idx = (uint8_t)(MOBS_ID[k] & POP_ENV_MASK); + if (idx >= a->count) return; + src = (const uint8_t *)atlas_image(a, idx); + gfx_w0_map(a->page); + w[k] = src[0]; h[k] = src[2]; + v = (uint8_t)(src[1] | src[3]); + gfx_w0_unmap(); + if (v || !w[k] || !h[k]) return; /* кадр >255 или пустой */ + row0 = (int)MOBS_YBO[k] - (int)h[k] + 1; + if (row0 < top) top = row0; + } + /* Габарит композита: низ = mob_y, верх = самая высокая часть. */ + ch = (uint8_t)(1 - top); + cw = 0; + for (k = 0; k < 3; k++) + if ((uint8_t)(MOBS_XO[k] + w[k]) > cw) cw = (uint8_t)(MOBS_XO[k] + w[k]); + if (cw > MOBS_MAXW || ch > MOBS_MAXH) return; + mob_spr[0] = cw; mob_spr[1] = 0; + mob_spr[2] = ch; mob_spr[3] = 0; + /* Пустое место — ПРОЗРАЧНОЕ (0xFF), иначе композит закрасил бы фон вокруг + * частей. Страйд блоба = его ширина, поэтому чистим ровно cw*ch. */ + for (r = 0; r < ch; r++) + for (c = 0; c < cw; c++) mob_spr[4 + (uint16_t)r * cw + c] = 0xFF; + /* Проход 2 — копирование ПОПИКСЕЛЬНО с пропуском 0xFF: части + * ПЕРЕКРЫВАЮТСЯ (74 и 70 обе идут от mob_x), и прозрачные пиксели верхней + * не должны стирать нижнюю — ровно как три прозрачных блита друг поверх + * друга. */ + for (k = 0; k < 3; k++) { + atlas_t *a = &pop_env[MOBS_ID[k] >> POP_ENV_SHIFT]; + src = (const uint8_t *)atlas_image(a, (uint8_t)(MOBS_ID[k] & POP_ENV_MASK)); + gfx_w0_map(a->page); + row0 = (int)MOBS_YBO[k] - (int)h[k] + 1 - top; + for (r = 0; r < h[k]; r++) { + const uint8_t *sp = src + 4 + (uint16_t)r * w[k]; + uint8_t *dp = mob_spr + 4 + (uint16_t)(row0 + r) * cw + MOBS_XO[k]; + for (c = 0; c < w[k]; c++) { v = sp[c]; if (v != 0xFF) dp[c] = v; } + } + gfx_w0_unmap(); + } + /* Коридор heal — по композиту: две строки запаса сверху, одна снизу. */ + mob_heal_up = (uint8_t)(ch + 1); + mob_heal_h = (uint8_t)(ch + 3); + mob_spr_ok = 1; +} + +/* Загрузить набор set (0 подземелье, 1 дворец). Повторный вызов с тем же + * набором ничего не делает; со сменой — сначала выгружает старый, иначе + * EMM-страницы утекут (их по 12 на набор). */ +int pop_bg_load(uint8_t set) __banked +{ + int i; + if (set >= POP_TILESETS) return -1; + if (set == bg_set) return 0; + if (bg_set != 0xFF) { /* смена набора — освободить старый */ + for (i = 0; i < POP_ENV_PAGES; i++) atlas_free(&pop_env[i]); + atlas_free(&pop_wall_at); + atlas_free(&pop_fore_at); + } + /* Двадцать пять атласов фона лежат в одном архиве: двенадцать на + * тайлсет подряд плюс общий chtab_1. Открываем его ОДИН раз на смену + * набора — при цене `open` 51,4 мс это и есть основная экономия. */ + { + uint8_t tbl[ARC_BG_COUNT * POP_ARC_REC]; + pop_arc_t arc; + uint8_t base = (uint8_t)(set * BG_SET_STRIDE); + int8_t bad = 0; + + if (pop_arc_open_id(&arc, POP_ARC_BG, tbl, ARC_BG_COUNT) < 0) return -1; + for (i = 0; i < POP_ENV_PAGES && !bad; i++) + bad = (int8_t)(pop_arc_atlas(&arc, tbl, (uint8_t)(base + i), + &pop_env[i]) != 0); + if (!bad) + bad = (int8_t)(pop_arc_atlas(&arc, tbl, + (uint8_t)(base + POP_ENV_PAGES), + &pop_wall_at) != 0); + if (!bad) + bad = (int8_t)(pop_arc_atlas(&arc, tbl, + (uint8_t)(base + POP_ENV_PAGES + 1), + &pop_fore_at) != 0); + if (!bad && bg_set == 0xFF) /* chtab_1 от набора не зависит */ + bad = (int8_t)(pop_arc_atlas(&arc, tbl, ARC_BG_POP_POT, + &pop_pot_at) != 0); + pop_arc_close(&arc); + if (bad) return -1; + } + bg_set = set; + pop_palace = (uint8_t)(set == 1); + mob_spr_build(); /* композит падающего куска — свой на набор */ + /* Палитра — ПОСЛЕ атласов: если атлас не загрузился, цвета остаются от + * прежнего набора и картинка хотя бы не разъезжается. */ + return bg_load_tile_pal(pop_tile_pal[set]); +} + +/* Пере-применить палитру ТЕКУЩЕГО набора. Нужна отдельно от pop_bg_load + * из-за порядка старта: атласы грузятся ДО initgraph (тот снимает + * DSS-страницу W0), а общая игровая палитра kid.pal читается ПОСЛЕ него и + * затирает слоты 0x50..0x6F — она собрана из VDUNGEON + * (pop_pack_kid.py build_palette). Звать сразу за gfx_pal_fload. */ +int pop_bg_pal_apply(void) __banked +{ + if (bg_set == 0xFF) return -1; + return bg_load_tile_pal(pop_tile_pal[bg_set]); +} + +void pop_bg_free(void) __banked +{ + int i; + if (bg_set == 0xFF) return; + for (i = 0; i < POP_ENV_PAGES; i++) + atlas_free(&pop_env[i]); + atlas_free(&pop_wall_at); + atlas_free(&pop_fore_at); + atlas_free(&pop_pot_at); + bg_set = 0xFF; +} + +static const uint8_t SPIKES_FRAM_RIGHT[10] = {0, 134, 135, 136, 137, 138, 137, 135, 134, 0}; + +/* ---- Чомпер (seg008.c:0B2B draw_tile_anim, ветка tiles_18_chomper) ---- * + * Кадр берётся НЕ прямо из modif: chomper_fram1[min(modif & 0x7F, 6)] даёт + * номер позы 0..4, и уже он индексирует три набора кусков — нижнюю челюсть + * (backtable), верхнюю (backtable, со своим подъёмом) и передний слой + * (foretable, поверх персонажа). Кровь — тот же номер позы + 114 (back) / + * +119 (fore), рисуется mono-силуэтом цветом 12; в атласе она уже такой + * (pop_pack_bg.py, 2026-08-07). + * Старший бит modif (0x80) = «в этом чомпере кого-то перемололо». + * + * Номер позы (pop_chomp_pose) и передние куски живут в РЕЗИДЕНТЕ + * (pop_tile.c): их читает ещё и fore-проход из горячей половины, а + * const-таблицы банка из чужого банка не видны. */ +static const uint8_t CHOMP_FRAM_BOT[5] = {101, 102, 103, 104, 105}; +static const uint8_t CHOMP_FRAM_TOP[5] = {0, 0, 111, 112, 113}; +static const uint8_t CHOMP_FRAM_Y[5] = {0, 0, 0x25, 0x2F, 0x32}; + +static const uint8_t BLUELINE_FRAM1[4] = {0, 124, 125, 126}; +static const int8_t BLUELINE_FRAM_Y[4] = {0, -20, -20, 0}; +static const uint8_t BLUELINE_FRAM3[4] = {44, 44, 45, 45}; + +/* ---- Зелье (tiles_10_potion, seg008 draw_tile_anim/draw_tile_fore) ---- + * Тип = (modif & 0xF8) >> 3: 0 = пусто (ничего не рисуем), 2..4 — «большая» + * склянка (id 13) и pot_size=1, остальные — маленькая (id 12). Спрайты — из + * chtab_1 (атлас pop_pot), поэтому НЕ из env: раньше мы рисовали chtab_6 id 12 + * (res212), которого в VDUNGEON нет — оттуда и «мусорный объект». + * Позиции оригинала: склянка xh = fore_x + draw_xh, xl = 6, ybottom = fore_y + + * draw_main_y; пузырёк/маска xh = draw_xh + 3, xl = 1, + * ybottom = draw_main_y − (pot_size << 2) − 14. */ +#define POT_MASK_ID 23 +static const uint8_t POTION_FRAM_BUBB[8] = {0, 16, 17, 18, 19, 20, 21, 22}; + +static uint8_t potion_type(uint8_t modif) { return (uint8_t)((modif & 0xF8) >> 3); } + + + +/* Дворец? — по ЗАГРУЖЕННОМУ набору (pop_palace ставит pop_bg_load), а не по + * номеру уровня: так ветки отрисовки и картинки заведомо согласованы даже в + * момент смены. */ +#define PALACE (pop_palace) + +/* Декоративная панель над воротами — ТОЛЬКО в паласе (в подземелье над + * воротами не рисуется ничего). Индекс — модификатор тайла doortop. + * seg008:407 doortop_fram_top / seg008:453 doortop_fram_bot. */ +static const uint8_t DOORTOP_FRAM_TOP[4] = {0, 81, 83, 0}; +static const uint8_t DOORTOP_FRAM_BOT[4] = {78, 80, 82, 0}; + +/* Верх ворот: его рисует тайл НАД воротами (draw_tile_anim_topright, + * seg008:0568). Кадр — по открытости соседа снизу-слева; 68 — маска, + * mono-блит чёрным «продавливает» фон под створкой. + * Без этого над воротами оставался чёрный клин — виден у шва во дворце, + * где сверху идёт декоративная панель doortop. */ +static const uint8_t DOOR_FRAM_TOP[8] = {60, 61, 62, 63, 64, 65, 66, 67}; +#define GATE_TOP_MASK 68 + +/* topright тайла снизу-слева (seg008.c:410). */ +static void draw_topright(uint8_t tt, uint8_t tmod, int x, int dby) +{ + if (tt == 7 || tt == 12) { /* верх ворот */ + if (!PALACE) return; /* подземелье: ничего (seg008:413) */ + if (DOORTOP_FRAM_TOP[tmod & 3]) + pop_env_b(DOORTOP_FRAM_TOP[tmod & 3], x, dby); + return; + } + if (tt == 20) { pop_wall_b(2, x, dby); return; } + if (pop_tile_table[tt].topright_id) + pop_env_b(pop_tile_table[tt].topright_id, x, dby); +} + + +/* ---- Loose floors: кадры дрожащей плиты (seg008 loose_fram_*) ------- * + * Состояние pop_loose_modif[] живёт в pop_map. draw_tile рисует грани + * loose АНИМИРОВАННО по modif (как SDLPoP: сам тайл — лево/низ, ПРАВЫЙ + * СОСЕД — правую грань через draw_tile_anim_right). При modif==0 (покой/ + * статич. запекание) get_loose_frame=0 → базовые кадры 41/42/43. */ +static const uint8_t LOOSE_FRAM_RIGHT[12] = {42,71,42,72,72,42,42,42,72,72,72,0}; + +/* Режим ЗАПЕЧКИ статического фона (запись идёт и в ОЗУ-копию акселератора, + * из которой потом восстанавливает heal). В этом режиме дрожащие кадры + * ЧУЖИХ плит рисовать НЕЛЬЗЯ: транзиентный кадр законсервируется в копии, а + * так как запечка идёт двумя кадрами (по одному на страницу дабл-буфера), + * на страницах застынут РАЗНЫЕ кадры — вечное мерцание. Пример: упала плита + * (0,6); её запечка перерисовывает и соседа, а правый угол СОСЕДНЕЙ плиты + * (0,5), который рисуется в ячейке (0,6), был в этот момент в тряске. + * Фон обязан быть состоянием ПОКОЯ — анимация каждый кадр кладётся поверх + * (банк SPRITE, только видео-ОЗУ). */ +/* draw_leveldoor (seg008:1D29) — дверь уровня. Рисуется при обработке + * ПРАВОЙ половины (tile_left = 0x10), все куски со сдвигом +8px (xh+1): + * 99 низ лестницы (всегда), + * 144 марш лестницы за створкой (когда створка приподнята, modif != 0), + * 33 слайс створки — повторяется вниз с шагом 4px, пока не дойдёт до + * y = ybottom − modif: modif 0 = закрыто (створка на всю высоту), + * 43 = открыто (остаётся только верхняя кромка), + * 34 верх коробки двери. + * Куски непрозрачные (blitters_0_no_transp) — как в оригинале. */ +static void draw_leveldoor(uint8_t modif, int xh, int dmy) +{ + int x = (xh + 1) * 8; + int ybottom = dmy - 13; + int ylo = ybottom - (int)(modif & 3) - 48; /* leveldoor_ybottom */ + int y = ybottom - (int)modif; + /* leveldoor_right (seg008:1D29): правая кромка проёма — от xh ТЕКУЩЕГО + * тайла, то есть ПРАВОЙ половины двери (сюда мы попадаем из + * draw_tile_anim_right, разбирая левого соседа). + * + * +8 у дворцовых уровней — дословно seg008:1429 + * (`if (tbl_level_type[current_level]) leveldoor_right += 8;`): проём + * во дворце шире, чем в подземелье. Без этой строки Кид в анимации + * ухода на следующий уровень скрывался за кромкой РАНЬШЕ, чем нужно, + * потому что клип брался по ширине подземного проёма (найдено на + * уровне 4, обход 2026-08-17). pop_palace ставит pop_bg_load из того + * же tbl_level_type, что читает оригинал. */ + pop_leveldoor_right = xh * 8 + 48; + if (pop_palace) pop_leveldoor_right += 8; + pop_env_b(99, x, ybottom); + if (modif) + pop_env_b(144, x, ybottom - 4); /* лестница (комната ≠ стартовой) */ + for (;;) { + pop_env_b(33, x, ylo); + if (y > ylo) ylo += 4; + else break; + } + /* ИМЕННО после цикла: в оригинале clip_char читает уже доведённое + * значение (низ поднятой створки), а не стартовое. */ + pop_leveldoor_ybottom = ylo; + pop_env_b(34, x, dmy - 64); +} + +/* ---- draw_tile (seg008.c:155) — одна клетка ------------------------ */ +/* ---- КОНТЕКСТ ТАЙЛА (порт load_curr_and_left_tile, seg008:0339) ------- + * Не локали draw_tile, а file-scope переменные — ровно как curr_tile / + * curr_modifier / draw_xh / draw_main_y / draw_bottom_y у оригинала. + * + * Зачем: в draw_tile 57 вызовов, и КАЖДОЕ значение, живое через вызов, SDCC + * спиливал в стековый кадр — получались 26 байт кадра и 513 обращений + * `-N(ix)` (замер 2026-08-17: 23 700 тактов накладных на вызов при одном + * блите на тайл, docs/perf_green_phase.md §1.6). У file-scope переменных + * абсолютная адресация (`ld a,(nn)` — 13 тактов против 19) и спиливать + * нечего. Функция от этого не становится реентерабельной, но и не была: + * draw_tile ниоткуда себя не зовёт. */ +static int dt_row, dt_col; +static uint8_t dt_code, dt_lcode, dt_lmod; +static int dt_xh, dt_x, dt_dby, dt_dmy; +static const piece *dt_t, *dt_lt; +static uint8_t dt_rbl, dt_rblm, dt_wmod, dt_base_id; +static int dt_base_yb; + +static void draw_tile(int row, int col) +{ + dt_row = row; dt_col = col; + dt_code = pop_tile_code(dt_row, dt_col); + dt_lcode = pop_tile_code(dt_row, dt_col - 1); + dt_lmod = pop_tile_mod(dt_row, dt_col - 1); + /* Нажатая кнопка (seg008 get_tile_to_draw): при таймере связи >1 opener + * (0x0F) рисуется как ПОЛ (modif 0), closer (0x06) — как stuck (0x05). + * Модификатор кнопки = индекс LINKLOC. И для самого тайла, и для левого + * соседа (его правая грань). */ + if (dt_code == 0x0F) { if ((pop_doorlink2(pop_tile_mod(dt_row, dt_col)) & 0x1F) > 1) dt_code = 1; } + else if (dt_code == 0x06) { if ((pop_doorlink2(pop_tile_mod(dt_row, dt_col)) & 0x1F) > 1) dt_code = 5; } + if (dt_lcode == 0x0F) { if ((pop_doorlink2(dt_lmod) & 0x1F) > 1) { dt_lcode = 1; dt_lmod = 0; } } + else if (dt_lcode == 0x06) { if ((pop_doorlink2(dt_lmod) & 0x1F) > 1) dt_lcode = 5; } + dt_xh = POP_COL_XH[dt_col]; + dt_x = dt_xh * 8; + dt_dby = 63 * dt_row + 65; + dt_dmy = dt_dby - 3; + dt_t = &pop_tile_table[dt_code]; + dt_lt = &pop_tile_table[dt_lcode]; + + /* тайл снизу-слева (для topright). Ниже нижнего ряда — берём ряд 0 + * комнаты снизу (load_rowbelow, seg008:368): row_below_left_[dt_col] = + * room_B(0, dt_col-1); dt_col-1<0 → левая комната снизу = стена уровня. */ + if (dt_row + 1 > 2) { + int lc = dt_col - 1; + /* lc < 0 — угол (2,0): сосед снизу-слева лежит в ЧУЖОЙ комнате + * (room_BL). Её тайл (0,9) приходит одиннадцатым байтом below-ряда; + * «стена» там стоит, только если такой комнаты нет — как в + * load_rowbelow (seg008:368). См. BUG-SEAM-WEDGE-1. */ + if (lc < 0) dt_rbl = pop_t_below ? (uint8_t)(pop_t_below[10] & 0x1F) : 20; + else dt_rbl = pop_t_below ? (uint8_t)(pop_t_below[lc] & 0x1F) : 0; + /* Модификатора ряда снизу у нас нет (pop_t_below — только fg), а он + * нужен паласной панели над воротами. Ставим 0 = «панели нет»: + * случай узкий (ворота в комнате СНИЗУ, по диагонали от пустого + * тайла нижнего ряда), но расхождение с оригиналом честное. */ + dt_rblm = 0; + } else { + dt_rbl = pop_tile_code(dt_row + 1, dt_col - 1); + dt_rblm = pop_tile_mod(dt_row + 1, dt_col - 1); + } + + /* Пометка «фон трогали» — ОДНА на весь тайл, а не на каждый его кусок + * (pop_cd_batch_begin/end). Досрочных выходов у draw_tile нет, так что + * скобка гарантированно закрывается. */ + pop_cd_batch_begin(); + + /* --- draw_tile_anim_topright (seg008:0568): верх ВОРОТ снизу-слева. + * Условие дословно: свой тайл пуст / верх большой колонны / верх двери, + * а снизу-слева — ворота. --- */ + if ((dt_code == 0 || dt_code == 9 || dt_code == 12) && dt_rbl == 4) { + uint8_t om = (dt_rblm > 188) ? 188 : dt_rblm; + pop_env_b(GATE_TOP_MASK, dt_x, dt_dby); /* маска (mono, чёрным) */ + pop_env_b(DOOR_FRAM_TOP[(om >> 2) & 7], dt_x, dt_dby); + } + + /* --- floorright (seg008.c:392) — только can_see_bottomleft --- */ + if (dt_code == 0 || dt_code == 9 || dt_code == 12 || dt_code == 26) { + draw_topright(dt_rbl, dt_rblm, dt_x, dt_dby); + /* floor-right тень (blitters_9_black) на ЧЁРНОМ фоне = no-op + * (чёрный силуэт по чёрному) — не рисуем. Если tile_left имеет + * floor_right: dt_lt->floor_right. */ + } + + /* --- draw_tile_right (seg008.c:456) --- */ + if (dt_code != 20) { + if (dt_lcode == 20) { + /* Полоска на стене (seg008:510) — только палас и только если у + * стены не взведён старший бит модификатора. */ + if (PALACE && (dt_lmod & 0x80) == 0) + pop_env_b(84, dt_x + 3 * 8, dt_dmy - 27); + pop_wall_b(1, dt_x, pop_tile_table[20].right_y + dt_dmy); /* грань стены */ + } else if (dt_lcode == 0) { + if (dt_lmod <= 3 && BLUELINE_FRAM1[dt_lmod]) + pop_env_b(BLUELINE_FRAM1[dt_lmod], dt_x, BLUELINE_FRAM_Y[dt_lmod] + dt_dmy); + } else if (dt_lcode == 1) { + uint8_t num; + pop_env_b(42, dt_x, pop_tile_table[1].right_y + dt_dmy); /* правый треуг. пола */ + num = (dt_lmod <= 3) ? dt_lmod : 0; + /* seg008:501 — `if (num == !!level_type) return;`: в подземелье + * пропускается num==0, в паласе num==1. */ + if (num != (PALACE ? 1 : 0)) + pop_env_b(BLUELINE_FRAM3[num], dt_x, dt_dmy - 20); /* силуэт кладки */ + } else if (dt_lcode == 7 || dt_lcode == 12) { + /* Низ декоративной панели над воротами (seg008:506) — только + * палас; в подземелье у doortop right_id нулевой и ветка пустая. */ + if (PALACE && DOORTOP_FRAM_BOT[dt_lmod & 3]) + pop_env_b(DOORTOP_FRAM_BOT[dt_lmod & 3], dt_x, dt_lt->right_y + dt_dmy); + } else { + if (dt_lt->right_id) + pop_env_b(dt_lt->right_id, dt_x, dt_lt->right_y + dt_dmy); + /* Орнаментная лента соседа (seg008:486) — только палас. Она и + * даёт непрерывную полосу под окнами: stripe_id = 145 у пола, + * кнопок, зелья, loose, чомпера и меча. */ + if (PALACE && dt_lt->stripe_id) + pop_env_b(dt_lt->stripe_id, dt_x, dt_dmy - 27); + /* Основание факела (seg008:489): рисует ПРАВЫЙ сосед. */ + if (dt_lcode == 19 || dt_lcode == 30) + pop_env_b(146, dt_x, dt_dby - 28); + } + } + + /* --- draw_tile_anim_right (seg008.c:530) — статичные части. Для + * верхней полосы (dt_row<0, draw_tile_aboveroom) НЕ рисуется + защита от + * отрицательного индекса в pop_loose_modif. --- */ + if (dt_row >= 0) { + if (dt_lcode == 4) { /* gate: решётка */ + pop_gate_back_b(dt_lmod, dt_xh, dt_dby, dt_dmy); + } else if (dt_lcode == 11) { /* loose: правая грань (анимир. по modif соседа-loose) */ + pop_env_b(LOOSE_FRAM_RIGHT[pop_loose_frame(pop_loose_modif[dt_row * 10 + (dt_col - 1)])], dt_x, dt_dby - 1); + } else if (dt_lcode == 2) { /* spike: статич. кадр */ + uint8_t sf = pop_spike_frame(dt_lmod); + if (sf < 10 && SPIKES_FRAM_RIGHT[sf]) + pop_env_b(SPIKES_FRAM_RIGHT[sf], dt_x, dt_dmy - 7); + } else if (dt_lcode == 16) { /* дверь уровня */ + draw_leveldoor(dt_lmod, dt_xh, dt_dmy); + } + /* Пламя факела (seg008:560, chtab_1 id modifier+1) здесь, в статичной + * отрисовке тайла, не рисуем — его ведёт pop_torch_draw из + * pop_process_trobs каждый кадр. Но ЗАПЕКАЕТСЯ оно всё равно (там + * GFX_BANK_NORMAL): факел — чистый задний план, а самонакрытие кадров + * делает запекание безопасным. Подробности — в шапке pop_torch_draw. */ + } + + /* --- draw_tile_bottom (seg008.c:570) --- */ + if (dt_code == 20) { + dt_wmod = pop_wall_modifier(dt_row, dt_col); + if (!PALACE) /* во дворце тело стены даёт */ + pop_wall_b(POP_WALL_FRAM_BOTTOM[dt_wmod], dt_x, dt_dby); /* wall_pattern */ + /* заливками (seg008:576) */ + pop_wall_pattern_b(dt_row, dt_col, 0); + } else if (dt_t->bottom_id) { + pop_env_b(dt_t->bottom_id, dt_x, dt_dby); + } + + /* --- draw_loose (seg008.c:600) — нижняя грань (анимир. по modif). В + * верхней полосе (dt_row<0) это ПЛИТА-ПОТОЛОК (loose ряда 2 комнаты сверху): + * её состояние живёт отдельно, в pop_ceil_modif[dt_col] (pop_map). --- */ + if (dt_code == 11) + pop_env_b(POP_LOOSE_FRAM_BOTTOM[pop_loose_frame(dt_row >= 0 ? pop_loose_modif[dt_row * 10 + dt_col] + : pop_ceil_modif[dt_col])], dt_x, dt_dby); + + /* --- draw_tile_base (seg008.c:611). Верхняя полоса (dt_row<0) базу НЕ + * рисует (draw_tile_aboveroom) — только нижние/правые грани. --- */ + if (dt_row >= 0) { + dt_base_id = dt_t->base_id; + dt_base_yb = dt_dmy; + if (dt_lcode == 26 && dt_code == 12) { + /* «Lattice + door A» (seg008:622): у doortop своей базы нет + * (dt_base_id 0), и когда слева от него стоит арка (lattice_down), + * пара рисуется ОДНИМ спрайтом 6, опущенным на 3 px. Без этой + * ветки на месте тайла оставалась чёрная дыра — уровень 5, + * комната 12, тайл (0,9) (BUG-LATTICE-DOORTOP). */ + dt_base_id = 6; + dt_base_yb += 3; + } + else if (dt_code == 11) /* loose: левая грань (анимир.) */ + dt_base_id = POP_LOOSE_FRAM_LEFT[pop_loose_frame(pop_loose_modif[dt_row * 10 + dt_col])]; + else if (dt_code == 0x0F && dt_lcode == 0 && !PALACE) /* opener, слева пусто */ + dt_base_id = 148; /* левая половина без пола — только */ + /* подземелье (seg008:628) */ + if (dt_base_id) + pop_env_b(dt_base_id, dt_x, dt_t->base_y + dt_base_yb); + } + + /* --- draw_tile_anim (seg008.c:0B2B) — левый кадр выдвинутых пик в + * СВОЕЙ ячейке (анимир. по своему modif). Без него остриё пики + * рисуется только правым соседом со сдвигом → шахта над самой пикой + * пустая. */ + if (dt_row >= 0 && dt_code == 2) { + uint8_t sf = pop_spike_frame(pop_tile_mod(dt_row, dt_col)); + if (sf > 0 && sf < 10 && POP_SPIKES_FRAM_LEFT[sf]) + pop_env_b(POP_SPIKES_FRAM_LEFT[sf], dt_x, dt_dmy - 2); + } + /* Чомпер (тот же draw_tile_anim, case tiles_18_chomper): нижняя челюсть + * на draw_main_y, поверх неё кровь (если кого-то перемололо), затем + * верхняя челюсть со своим подъёмом. Всё это BACKtable — то есть ПОД + * персонажем; передний слой рисует fore-проход. */ + if (dt_row >= 0 && dt_code == 0x12) { + uint8_t cm = pop_tile_mod(dt_row, dt_col); + uint8_t pose = pop_chomp_pose(cm); + pop_env_b(CHOMP_FRAM_BOT[pose], dt_x, dt_dmy); + if (cm & 0x80) + pop_env_b((uint8_t)(pose + 114), dt_x + 8, dt_dmy - 6); + if (CHOMP_FRAM_TOP[pose]) + pop_env_b(CHOMP_FRAM_TOP[pose], dt_x, dt_dmy - CHOMP_FRAM_Y[pose]); + } + /* Меч-предмет на полу (тот же draw_tile_anim, case tiles_22_sword): + * chtab_1 (наш pot-атлас), id = (modif==1)+10, низ на draw_main_y-3. + * В оригинале это midtable (рисуется вместе с объектами) — у нас статикой + * в фоне: пока меч лежит, он не анимируется, а Kid и так поверх фона. */ + if (dt_row >= 0 && dt_code == 22) + pop_pot_b((uint8_t)((pop_tile_mod(dt_row, dt_col) == 1) + 10), dt_x, dt_dmy - 3); + + /* --- draw_tile_fore (seg008.c:690) --- */ + if (dt_code == 20) { + dt_wmod = pop_wall_modifier(dt_row, dt_col); + if (!PALACE) /* seg008:711 */ + pop_wall_b(POP_WALL_FRAM_MAIN[dt_wmod], dt_x, dt_dmy); /* основная грань */ + pop_wall_pattern_b(dt_row, dt_col, 1); + } + if (dt_code == 0x12) { /* чомпер: передние зубья + кровь. + * Отдельной веткой, как в оригинале: + * у записи 0x12 fore_id == 0, и через + * таблицу этот слой не идёт. */ + uint8_t cm = pop_tile_mod(dt_row, dt_col); + uint8_t pose = pop_chomp_pose(cm); + /* Передние куски чомпера и его кровь лежат в pop_fore.atl + * (FORE_ENV_IDS в pop_pack_bg.py), а НЕ в env-страницах: блит + * обязан быть pop_fore_b, иначе id уходит в чужую страницу. */ + pop_fore_b(POP_CHOMP_FRAM_FOR[pose], dt_x, dt_dmy); + if (cm & 0x80) + pop_fore_b((uint8_t)(pose + 119), dt_x + 8, dt_dmy - 6); + } + if (dt_code == 10) { /* зелье: chtab_1, не env fore_id. + * Пузырёк — динамика (pop_potion_draw). */ + pop_potion_flask(dt_row, dt_col, pop_tile_mod(dt_row, dt_col)); + } else if (dt_t->fore_id) { + pop_fore_b(dt_t->fore_id, dt_t->fore_x * 8 + dt_x, dt_t->fore_y + dt_dmy); + } + pop_cd_batch_end(); /* одна пометка на весь тайл */ +} + + +/* ---- Loose floors — динамическая перерисовка (seg008 draw_loose) ---- * + * Порт SDLPoP redraw_tile_height: каждый кадр анимации перерисовать + * ПОЛНОСТЬЮ и loose-тайл, и ПРАВЫЙ СОСЕД (грани loose анимируются по modif + * внутри draw_tile). */ +void pop_loose_shake_draw(int row, int col) __banked +{ + int x = POP_COL_XH[col] * 8; + /* heal возвращает печёный фон обоих тайлов из ОЗУ-копии, поверх — + * перерисовка обоих тайлов с дрожащим кадром (draw_tile берёт кадр из + * pop_loose_modif). Порт redraw_tile_height (loose + правый сосед). + * NB: печёный кадр-0 может слегка просвечивать по кромкам — чистое + * решение (bake тайла как empty + рисовать loose всегда динамически) + * отложено; wipe-в-чёрный делал плиту невидимой — не годится. */ + /* Ширина 58, а не 64 = «две клетки»: реальный след дрожащей плиты — + * свой тайл (верх/левая грань 32 px) плюс правая грань, которая уходит + * в соседнюю клетку на 26 px (25 во дворце). Габариты сняты из + * каталогов атласов: 41/69/70 = 32x13-14, 43/73/74 = 32x3, + * 42/71/72 = 26x15-16. Задача HEAL-WIDTH. */ + pop_heal_off(x, 63 * row + 46, 58, 24); + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + draw_tile(row, col); /* loose: лево+низ (анимир.) */ + if (col + 1 < 10) + draw_tile(row, col + 1); /* сосед: правая грань loose (анимир.) + его пол */ + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Вернуть ТОЛЬКО слой anim чомпера — порт ветки redraw_frames_anim в + * redraw_needed (seg008:0211). Контракт и разбор — в шапке объявления + * (pop_bg.h). + * + * Чем это отличается от pop_chomp_redraw и почему нужна отдельная функция. + * Пометку ставит АНИМАЦИЯ ФАКЕЛА: пламя лежит в ячейке ПРАВОГО соседа + * (seg008:560), то есть поверх чомпера, и запекается в фон каждый кадр. + * Возвращать после него надо ровно графику чомпера — а pop_chomp_redraw + * делал heal 32x64 плюс ПОЛНЫЙ draw_tile, то есть пересобирал тайл со всеми + * слоями (правая грань, база, низ, loose), которых пламя не касалось. + * Замер 11/15 (2026-08-19): 190 260 тактов на этот единственный тайл, 24 % + * работы кадра. + * + * heal не нужен: поза чомпера здесь НЕ меняется (свою анимацию ведёт + * pop_chomp_redraw по своей пометке), стирать нечего — рисуем ту же + * графику поверх свежего пламени. Оригинал на пометку anim wipe тоже не + * делает: у него это отдельный счётчик wipe_frames. + * + * Передний слой (зубья, POP_CHOMP_FRAM_FOR) сюда НЕ входит — как и в + * оригинале, где fore идёт по своему счётчику redraw_frames_fore. Геометрия + * это подтверждает: пламя занимает 18 строк с низом на 63*row+22, передние + * зубья — на dmy = 63*row+62, то есть на 40 строк ниже; пересекается с огнём + * только ВЕРХНЯЯ челюсть (подъём до 0x32 = 50 px), а она в back-слое. */ +void pop_chomp_anim_draw(int row, int col) __banked +{ + int x = POP_COL_XH[col] * 8; + int dmy = 63 * row + 62; /* dby - 3, как в draw_tile */ + uint8_t cm = pop_tile_mod(row, col); + uint8_t pose = pop_chomp_pose(cm); + + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_cd_batch_begin(); /* одна пометка на все куски */ + pop_env_b(CHOMP_FRAM_BOT[pose], x, dmy); + if (cm & 0x80) /* кого-то перемололо */ + pop_env_b((uint8_t)(pose + 114), x + 8, dmy - 6); + if (CHOMP_FRAM_TOP[pose]) + pop_env_b(CHOMP_FRAM_TOP[pose], x, dmy - CHOMP_FRAM_Y[pose]); + pop_cd_batch_end(); + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Перерисовать пики тайла (row,col) на back-странице по ЖИВОМУ modif + * (pop_trob). Кадр выдвижения рисует ПРАВЫЙ сосед (draw_tile ветка lcode==2 + * читает lmod = pop_t_bg[row*10+col] = живой modif пики). pop_t_bg должен указывать + * на room_modif (enter_room передаёт его как bg). Порт redraw_21h/draw_tile. + * heal берёт печёный статич. фон, поверх — динамич. кадр (шипы над полом). */ +void pop_spike_redraw(int row, int col) __banked +{ + int x = POP_COL_XH[col] * 8; + /* Порт redraw_21h -> redraw_tile_height (seg007:0218): wipe + ПОЛНЫЙ + * redraw ДВУХ тайлов — своего и правого соседа (остриё рисуется в его + * ячейке). Именно ПОЛНЫЙ: рисовать только два спрайта остриёв нельзя — + * порядок слоёв внутри тайла (draw_tile_anim_right идёт ПЕРВЫМ, база и + * fore соседа ложатся ПОВЕРХ) тогда теряется, и остриё лезет на колонну + * справа. Заодно draw_tile сам восстанавливает вклад соседних пик: + * в ячейке col — правое остриё пики col-1 (ветка lcode==2). + * Шипы выдвигаются выше линии пола (dmy-7), поэтому heal с запасом вверх + * (63*row+34 .. +74); ширина 64 = обе ячейки. */ + pop_heal_off(x, 63 * row + 34, 64, 40); + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + draw_tile(row, col); + if (col + 1 < 10) + draw_tile(row, col + 1); + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Чомпер: перерисовка кадра анимации (порт redraw_20h/draw_trob). Челюсти + * поднимаются высоко над линией пола (верхний кусок уходит на 0x32 = 50 px + * вверх от dmy), поэтому heal берём на всю высоту тайла. Правый сосед НЕ + * нужен: в отличие от пик, все куски чомпера рисуются в СВОЕЙ ячейке. */ +void pop_chomp_redraw(int row, int col) __banked +{ + int x = POP_COL_XH[col] * 8; + /* РОВНО 32x60 — весь чомпер помещается в свой тайл, и это его точный + * след (замечание пользователя, проверено по каталогу атласа): + * нижняя челюсть 101/102 = 32x60 низом на dmy = 63*row+62, то есть + * занимает 63*row+3 .. +62; + * верхние челюсти дают ТОТ ЖЕ верх — подъём 0x25 при высоте 23, + * 0x2F при 13 и 0x32 при 10 все три дают 63*row+3; + * кровь 114..118 шириной 6 рисуется на x+8, то есть внутри 32. + * Было 64 «на всю высоту тайла» — четыре лишние строки. */ + pop_heal_off(x, 63 * row + 3, 32, 60); + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + draw_tile(row, col); + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Перерисовать ЛЕВЫЙ шов (col0) на back-странице по живому modif соседа — + * ворота соседней комнаты слева (portcullis room8) видны в col0 текущей + * комнаты через draw_gate_back (draw_tile ветка lcode==4, lmod = pop_t_lbg[0]). + * pop_t_lbg (lcol_bg) должен указывать на живой modif (SprPoP обновляет из + * room_modif соседа перед вызовом). Bar-в-чёрный + redraw (как loose bake): + * heal вернул бы ЗАПЕЧЁННОЕ состояние ворот → при открытии нижние бары + * оставались бы; чёрная плита стирает старые бары в VRAM+ОЗУ-копию. */ +void pop_room_redraw_seam_left(uint8_t rows) __banked +{ + int8_t r; + /* rows — битовая маска рядов 0..2: перерисовываем ТОЛЬКО те, где openness + * реально изменился. Раньше ряд был жёстко нулевым, потому что на уровне 1 + * решётка соседа стояла в (0,9); на уровне 4 она в (1,9), и открытые ворота + * оставались нарисованными закрытыми (BUG-GATE-SEAM-ROW1). */ + for (r = 0; r < 3; r++) { + int yt = POP_YOFF + 63 * r; + if (!(rows & (1 << r))) continue; + gfx_set_bank(GFX_BANK_NORMAL); + /* Полосу кладки НАД рядом НЕ трогаем: её низ на room-space 63*r+2, а + * бары ворот начинаются с 63*r+3 (gate_top_y = dby−62). Раньше bar шёл + * от POP_YOFF и стирал потолок, из-за чего приходилось перерисовывать + * ещё и draw_tile(-1,0) — вдвое дороже на КАЖДОМ кадре анимации. */ + pop_bar_black(0, yt + 3, 32, 62); /* col0 этого ряда (бары ворот) */ + gfx_set_bank(GFX_BANK_TRANSPARENT); + draw_tile(r, 0); /* фон col0 + бары ворот (живой modif) */ + gfx_set_bank(GFX_BANK_NORMAL); + } +} + +/* Ширина прямоугольника запечки, ОБРЕЗАННАЯ по правому краю экрана. + * + * Точечные запечки берут ширину «свой тайл + свес соседа» (40, 60 или 64 при + * шаге тайла 32) — для последней колонки это уезжает за 320. А заливка и + * копия по контракту НЕ КЛИПУЮТ (см. шапку _gfx_recthfill256: «Клиппинга НЕТ, + * координаты обязаны быть валидны») — и правильно, что не клипуют: проверка + * координат стоила бы дороже самой заливки. Значит обрезать обязан + * вызывающий. + * + * Чем это было: падение плиты (1,8) помечает соседа (1,9), pop_floor_bake + * заливал x=288 шириной 60, то есть 28 пикселей за экраном, и эти 28 всплывали + * ЧЁРНОЙ ПОЛОСОЙ у левого края — поверх стража в (1,0), причём только на одной + * странице дабл-буфера, то есть с мерцанием (найдено пользователем + * 2026-08-17, уровень 2 комната 4; трасса заливок: + * BAR x=288 y=117 w=60 h=39, а расхождение страниц — ровно x 0..27, y 117..155). + * + * Обрезка ничего не теряет: правее 320 восстанавливать нечего. */ +static uint8_t bake_w(int x, uint8_t w) +{ + if (x >= 320) return 0; /* весь прямоугольник за экраном */ + return (x + (int)w > 320) ? (uint8_t)(320 - x) : w; +} + +/* ---- ЗАПЕЧКА НА ВТОРУЮ СТРАНИЦУ — копией, а не пересчётом ----------- * + * Точечные запечки ставятся с pages = 2, то есть срабатывают ДВА кадра подряд + * — по разу на каждую страницу дабл-буфера, — и оба раза считают одно и то же. + * А после первого раза нужный прямоугольник уже лежит в ОЗУ-копии первой + * страницы, и его можно просто скопировать: gfx_copy_page берёт источником + * ОЗУ-копию НЕактивной страницы (то есть ЧИСТЫЙ фон — спрайты рисуются банком + * без тени и в копию не попадают), а приёмник обновляет и в видео-ОЗУ, и в + * ОЗУ-копии. + * + * Замер 2026-08-17: полная запечка щебня — 179 914 такта, колодца — 138 318; + * копия прямоугольника 60x39 стоит ~30 000, а 64x9 — ~14 000. + * + * Слот на тайл помнит, НА КАКОЙ странице сделана первая запечка (+1; 0 = не + * было). Копируем только если первая была на ДРУГОЙ странице и дабл-буфер + * включён; иначе честно пересчитываем. Такая проверка выдерживает и + * переплетение двух запечек в одном кадре, и отключённый дабл-буфер (чит + * SPACE), и смену комнаты между кадрами. */ +static uint8_t bake_pg[30], bake_pg_above[10]; + +static uint8_t bake_copy(uint8_t *slot, int x, int ytop, int w, int h) +{ + uint8_t pg = (uint8_t)(gfx_get_draw_page() & 1); + gfx_rect_t r; +#ifdef POP_DBG_NO_BAKE_COPY + (void)x; (void)ytop; (void)w; (void)h; /* A/B: копия выключена целиком */ + *slot = (uint8_t)(pg + 1); + return 0; +#endif + if (!*slot || (uint8_t)(*slot - 1) == pg || + gfx_get_draw_page() == gfx_get_visible_page()) { + *slot = (uint8_t)(pg + 1); /* это ПЕРВАЯ запечка */ + return 0; + } + *slot = 0; + if (pop_upside) ytop = POP_FLIP_TOP(ytop, h); /* копируем ЭКРАННЫЙ прямоугольник */ + r.x = (int16_t)x; r.y = (int16_t)ytop; + r.w = (uint16_t)w; r.h = (uint16_t)h; + gfx_copy_page(&r, GFX_COPY_DIRECT); + pop_cd_touch(x, ytop, w, h); /* область изменилась и на этой странице */ + return 1; +} + +/* Перепометка тайла: содержимое могло измениться (кнопка с идущим таймером + * перерисовывается каждый кадр РАЗНОЙ), поэтому копия со второй страницы + * больше не годится — гасим слот, следующая запечка пойдёт честно. + * Контракт и мотив — в _pop_bg.h. */ +void pop_bake_slot_reset(uint8_t tilepos) +{ + if (tilepos < 30) bake_pg[tilepos] = 0; +} + +void pop_bake_slot_reset_above(uint8_t col) +{ + if (col < 10) bake_pg_above[col] = 0; +} + +/* Смена комнаты/уровня: запечки прошлой комнаты недоделанными не остаются + * (pop_trob_room_changed сбрасывает пометки), но слоты обнулить надо — иначе + * первый же тайл с тем же номером в новой комнате получит копию вместо + * честной запечки. */ +void pop_bake_forget(void) __banked +{ + uint8_t i; + for (i = 0; i < 30; i++) bake_pg[i] = 0; + for (i = 0; i < 10; i++) bake_pg_above[i] = 0; +} + +/* ---- Плита-потолок (loose ряда 2 комнаты СВЕРХУ) ------------------- * + * В полосе кладки у потолка (наш ряд −1) виден только НИЗ плиты + * (draw_tile_aboveroom: bottom+loose, всё выше отрезано клипом POP_YOFF). + * Дрожащий кадр берётся из pop_ceil_modif[col] (см. draw_loose выше). + * shake — на текущей (back) странице поверх фона; bake — «запечь» пустоту + * после провала (NORMAL, в ОЗУ-копию, чтобы heal её сохранял). */ + +/* Полоса кладки у потолка: дрожащая плита ряда −1 — порт redraw_needed_above + * (seg008:0221). + * + * Оригинал рисует ТОЛЬКО СВОЮ колонку: кусок, свисающий вправо, кладёт её же + * draw_tile_right. Здесь дополнительно звался `draw_tile(-1, col + 1)` — + * целый соседний тайл, — и это удваивало работу. Замер 2026-08-13: пик + * зелёного блока 887 000 тактов при кадровом интервале 430 000 состоял ровно + * из шести вызовов этой функции, то есть двенадцати отрисовок тайла. + * + * Батчинг смежных колонок (один heal на диапазон) пробовался и убран: плиты + * стартуют со случайными задержками (check_fall_flo), в кадре дрожат + * разрозненные колонки, пробег почти всегда длиной в одну — механика не + * окупалась, но крутилась каждый кадр. */ +void pop_ceil_shake_draw(int col) __banked +{ + int x = POP_COL_XH[col] * 8; + pop_heal_off(x, 0, 64, CEIL_BAND_H); + /* Пометка «фон трогали» — ОДНА на восстановленный прямоугольник: пока + * стоит окно (ниже), pop_blit_b пометок не ставит. Область та же, что у + * heal'а, — он её и стирает. */ + pop_cd_touch(x, POP_YOFF, 64, CEIL_BAND_H); + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_t_clip_top = POP_YOFF; + pop_t_win_set(x, POP_YOFF, 64, CEIL_BAND_H); /* восстанавливаем ровно полосу */ + draw_tile(-1, col); + pop_t_win_clear(); + pop_t_clip_top = 0; + gfx_set_bank(GFX_BANK_NORMAL); +} + +void pop_ceil_bake_empty(int col) __banked +{ + int x = POP_COL_XH[col] * 8; + uint8_t bw = bake_w(x, 64); /* обрезка по краю экрана — см. bake_w */ + /* Вторая страница — копией с первой (см. bake_copy). */ + if (bake_copy(&bake_pg_above[col], x, POP_YOFF, bw, CEIL_BAND_H + 1)) return; + gfx_set_bank(GFX_BANK_NORMAL); + /* Отдельного pop_cd_touch тут не надо: pop_bar_black метит сам, и ровно + * этот же прямоугольник (было два вызова на одну область). */ + if (bw) pop_bar_black(x, POP_YOFF, bw, CEIL_BAND_H + 1); /* стереть плиту в ОЗУ-копии */ + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_t_clip_top = POP_YOFF; + /* Окно = ровно стёртая полоса. Главный выигрыш здесь не в отсеве, а в + * КЛИПЕ: куски ряда 0 высотой 63 px рисовались целиком (клип был только + * сверху, по pop_t_clip_top), хотя восстановить надо девять строк — + * отсюда 21 447 такта на блит против 14 384 в pop_floor_bake. */ + pop_t_win_set(x, POP_YOFF, bw, CEIL_BAND_H + 1); + pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */ + /* Восстанавливаем ВСЁ, чья графика попадает в стёртую полосу, а не только + * два тайла ряда −1 (BUG-LOOSE-3). Куски тайла рисуются ВВЕРХ от своей + * нижней грани и свисают ВПРАВО, поэтому в полосу над тайлом col лезут: + * • ряд −1, колонки col−1..col+1 — сама полоса кладки у потолка; + * • ряд 0, колонки col−1..col+1 — их верхушки заходят в те же 8 px. + * Раньше перерисовывались только (−1,col) и (−1,col+1), и в полосе + * оставался чёрный прямоугольник ~13×3 px (замер: экранные y 47..52 при + * полосе 44..52), который heal возвращал каждый кадр. Признак, что дело + * именно в запечке: после выхода из комнаты и возврата бара нет. */ + { + int c0 = (col > 0) ? col - 1 : 0; + int c1 = (col + 1 < 9) ? col + 1 : 9; + int c; + for (c = c0; c <= c1; c++) draw_tile(-1, c); + for (c = c0; c <= c1; c++) draw_tile(0, c); + } + pop_t_bake_rest = 0; + pop_t_win_clear(); + pop_t_clip_top = 0; + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* ---- Анимация факела и зелья (динамика, каждый кадр) ---------------- * + * Оба кадра рисуются ПОВЕРХ статического фона и стираются heal'ом своей + * области (фон запечён БЕЗ них). Факел: пламя chtab_1 id modifier+1 в + * ячейке ПРАВОГО соседа (seg008:560: xh = draw_xh + 1, ybottom = + * draw_main_y − 40). Зелье: маска (23, «прорезает» фон) + кадр пузырька + * potion_fram_bubb[modif & 7] на xh+3/xl=1, ybottom = draw_main_y − + * (pot_size<<2) − 14 (seg008:668). */ +void pop_torch_draw(int row, int col, uint8_t modif) __banked +{ + /* Пламя рисует ПРАВЫЙ сосед со своим draw_xh: xh = POP_COL_XH[col+1] + 1 + * (seg008:560), т.е. x = (col+1)*32 + 8 — как и основание факела (env + * 146), которое тоже лежит в ячейке соседа. */ + int x = (col + 1 < 10 ? POP_COL_XH[col + 1] : POP_COL_XH[col] + 4) * 8 + 8; + int yb = 63 * row + 62 - 40; + if (modif >= 9) return; + /* heal НЕ нужен: оригинал рисует пламя blitters_0_no_transp (seg008 + * draw_tile_anim_right), и все 9 кадров лежат на общем канвасе 16x18 — + * непрозрачный блит сам полностью накрывает предыдущий кадр. Кадры + * упакованы с opaque=True (pop_pack_bg POT_FLAME_IDS). + * + * И ровно поэтому пламя ЗАПЕКАЕТСЯ (GFX_BANK_NORMAL: видео-ОЗУ + ОЗУ-копия), + * а не рисуется в SPRITE поверх фона. Запечь его безопасно именно из-за + * самонакрытия — следующий кадр сотрёт предыдущий сам, «протухнуть» в + * копии нечему. А выигрыш принципиальный: точечные перерисовки соседних + * тайлов начинаются с heal из ОЗУ-копии, и незапечённое пламя они стирали + * — чомпер (0,7) комнаты 23 уровня 4 healит x 224..255 / y 30..93 и + * съедал факел (0,6), который рисуется в ячейке правого соседа. С + * запеканием heal возвращает огонь сам, а челюсти ложатся ПОВЕРХ него — + * тот же z-порядок, что у оригинала, где draw_tile_anim_right (пламя + * левого соседа) идёт до draw_tile_anim (сам чомпер). + * + * Пузырёк ЗЕЛЬЯ так нельзя: он ползёт вверх и себя не накрывает, ему + * нужен heal — pop_potion_draw остаётся в SPRITE. */ + gfx_set_bank(GFX_BANK_NORMAL); + pop_pot_b((uint8_t)(modif + 1), x, yb); + gfx_set_bank(GFX_BANK_NORMAL); +} + +void pop_potion_draw(int row, int col, uint8_t modif) __banked +{ + uint8_t type = potion_type(modif); + int pot_size = (type >= 2 && type < 5) ? 1 : 0; + int x = (POP_COL_XH[col] + 3) * 8 + 1; + int yb = 63 * row + 62 - (pot_size << 2) - 14; + uint8_t bubb; + if (type == 0) return; + bubb = POTION_FRAM_BUBB[modif & 7]; + /* Цвет пузырька по ТИПУ зелья (draw_tile_anim, seg008:652): 3/4 (перо / + * переворот) — зелёный, 5/6 (вред / открыть ворота) — синий, остальные + * (1 лечение, 2 склянка жизни) — красный. У нас цвет запечён в спрайт + * при упаковке (mono-блиттера с параметром цвета нет), поэтому «цвет» = + * другой набор кадров: id 30..36 / 40..46 вместо 16..22 (pop_pack_bg.py). + * Кадр 0 («пузырька нет») общий — его подменять нечем и не нужно. */ + if (bubb) { + if (type == 3 || type == 4) + bubb = (uint8_t)(POP_POT_BUBB_GREEN + (bubb - POTION_FRAM_BUBB[1])); + else if (type == 5 || type == 6) + bubb = (uint8_t)(POP_POT_BUBB_BLUE + (bubb - POTION_FRAM_BUBB[1])); + } + pop_heal_off(x - 1, yb - 10, 8, 12); + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_pot_b(POT_MASK_ID, x, yb); /* маска (под пузырьком) */ + pop_pot_b(bubb, x, yb); /* кадр пузырька */ + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Стереть ЗАПЕЧЁННОЕ пламя факела (row,col) с ТЕКУЩЕЙ draw-страницы и + * вернуть на его место фон покоя. + * + * Нужно ровно одному месту — перевороту экрана (pop_flip_screen). Пламя + * рисуется банком 0x50, то есть запекается и в ОЗУ-копию (см. шапку + * pop_torch_draw: heal'а у него нет, каждый следующий кадр непрозрачно + * накрывает предыдущий). Отражение страницы утаскивает его в зеркальную + * позицию, где накрывать уже нечем — и языки огня остаются по комнате + * навсегда. Стерев их ДО отражения, мы отражаем чистый фон и не платим за + * полную перерисовку комнаты (14.5 кадра против 2.1 у копии, замер MAME). + * + * Прямоугольник — канвас пламени 16x18 в ячейке ПРАВОГО соседа (seg008:560, + * те же координаты, что у pop_torch_draw). */ +void pop_torch_wipe(int row, int col) __banked +{ + int x = (col + 1 < 10 ? POP_COL_XH[col + 1] : POP_COL_XH[col] + 4) * 8 + 8; + int yb = POP_YOFF + 63 * row + 62 - 40; + /* У факела в колонке 9 канвас пламени лежит на x = 328, то есть ЦЕЛИКОМ за + * правым краем — заливать нечего, а bar не клипует (см. bake_w). */ + uint8_t tw = bake_w(x, 16); + pop_cd_touch(x, yb - 17, 16, 18); /* bar мимо pop_blit_b */ + gfx_set_bank(GFX_BANK_NORMAL); + if (tw) pop_bar_black(x, yb - 17, tw, 18); + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_t_bake_rest = 1; /* фон = покой */ + draw_tile(row, col); + if (col + 1 < 10) draw_tile(row, col + 1); + pop_t_bake_rest = 0; + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Перерисовать тайл КНОПКИ (row,col) на back-странице: нажатая/отжатая + * (draw_tile подставляет пол/stuck по таймеру связи). Bar-в-чёрный + + * redraw — иначе «шишка» отжатой кнопки (base 147) осталась бы при переходе + * в пол. Звать из pop_process_trobs для кнопок текущей комнаты. */ +void pop_floor_bake(int row, int col) __banked +{ + int x = POP_COL_XH[col] * 8; + int yb = POP_YOFF + 63 * row; + uint8_t bw = bake_w(x, 60); /* обрезка по краю экрана — см. bake_w */ + /* Вторая страница — копией с первой (см. bake_copy). */ + if (bake_copy(&bake_pg[row * 10 + col], x, yb + 26, bw, 39)) return; + /* Пометку ставит сам pop_bar_black — по тому же прямоугольнику; отдельный + * pop_cd_touch тут был вторым вызовом на одну область (4 502 такта). */ + /* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола. + * НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже + * (высокий спрайт), которую тут не перерисовываем → иначе чёрный бар. */ + gfx_set_bank(GFX_BANK_NORMAL); + if (bw) pop_bar_black(x, yb + 26, bw, 39); + gfx_set_bank(GFX_BANK_TRANSPARENT); + /* ОКНО КЛИПА ЗДЕСЬ ОБЯЗАТЕЛЬНО — и не ради скорости. + * + * Без него draw_tile рисует тайлы ЦЕЛИКОМ, то есть пишет ШИРЕ бара, а + * bake_copy переносит на вторую страницу ровно бар — и всё, что легло вне + * него, на второй странице остаётся прежним. Страницы расходятся, и это + * видно как МЕРЦАНИЕ через кадр (найдено пользователем 2026-08-17: + * уровень 1, комната 6, Кид на кнопке (0,2) — дрожала нижняя грань + * переднего торца кнопки). С окном запечка и копия совпадают по + * построению. + * + * По скорости окно САМО ПО СЕБЕ убыточно, и это проверено трижды + * (179 914 -> 188 417): клипованный путь дороже быстрого на ~1 500 такта + * на каждом из 7,6 блитов, а режет он только редкие высокие куски. Но + * оно открывает копию второй страницы, а та экономит ~145 000 — так что + * пара «запечка + копия» всё равно вдвое дешевле двух запечек. + * + * Что бар покрывает ВСЁ изменившееся — это инвариант самого бара (иначе + * запечка не работала бы и без копии). */ + pop_t_win_set(x, yb + 26, bw, 39); + pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */ + draw_tile(row, col); + if (col + 1 < 10) draw_tile(row, col + 1); + pop_t_bake_rest = 0; + pop_t_win_clear(); + gfx_set_bank(GFX_BANK_NORMAL); +} + +void pop_button_redraw(int row, int col) __banked { pop_floor_bake(row, col); } + +/* Перерисовать ПРАВУЮ половину двери уровня (в её ячейке рисуется створка, + * см. draw_leveldoor) на back-странице С ЗАПЕЧКОЙ в ОЗУ-копию: створка едет, + * а heal обязан возвращать её ТЕКУЩЕЕ положение, а не запечённое закрытое. + * Куски двери непрозрачные — wipe не нужен. */ +void pop_leveldoor_redraw(int row, int col) __banked +{ + gfx_set_bank(GFX_BANK_TRANSPARENT); + draw_tile(row, col); + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Ворота ВНУТРИ комнаты: перерисовать бары по живому modif (порт draw_trob, + * seg007:01E6 — им заканчивается animate_door). (row,col) — сами ворота; + * решётку рисует ячейка ПРАВОГО соседа (draw_tile, ветка lcode==4), поэтому + * wipe и redraw идут по ней. Логика ровно как у левого шва + * (pop_room_redraw_seam_left), только колонка произвольная. + * + * Bar-в-чёрный обязателен: бары висят в видео-ОЗУ и ОЗУ-копии, и при + * ПОДЪЁМЕ решётки heal вернул бы запечённые нижние бары. Полоса — ровно + * зона решётки: от gate_top_y (dby−62 = 63*row+3) до низа (63*row+64); + * выше начинается ряд −1, ниже — стена ряда ниже. + * + * Ворота в col9: их бары рисуются уже в СОСЕДНЕЙ комнате, в отрисованной их + * не видно — оригинал такой trob тоже пропускает + * (get_trob_right_pos_in_drawn_room → 30). Для взгляда из комнаты справа + * работает pop_room_redraw_seam_left. */ +void pop_gate_redraw(int row, int col) __banked +{ + int x, yt; + if (col + 1 > 9 || row < 0 || row > 2) return; + x = POP_COL_XH[col + 1] * 8; + yt = POP_YOFF + 63 * row; + pop_bar_black(x, yt + 3, 32, 62); /* зеркалится сама (переворот) */ + gfx_set_bank(GFX_BANK_TRANSPARENT); + draw_tile(row, col + 1); + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Запечь ПУСТОТУ на месте упавшего loose (текущая страница). Порт + * redraw_at_cur_mob (seg007:132C): перерисовать сам тайл (теперь empty) И + * тайл СПРАВА (его левая грань зависит от нашего кода: пока был loose — + * рисовал loose-правый-край pop_env_b(42); теперь пусто → чистая грань). + * pop_t_fg[pos] уже = 0 (pop_map). Чёрная плита (NORMAL) стирает старые + * пиксели пола в видео-ОЗУ + ОЗУ-копию, поверх — новый статич. фон. */ +void pop_loose_bake_empty(int row, int col) __banked +{ + int x = POP_COL_XH[col] * 8; + /* col+1 читаем ТОЛЬКО в границах массива: у последней колонки соседа нет, + * а POP_COL_XH[10] — чтение за концом (10 элементов). */ + int xr = (col + 1 < 10) ? POP_COL_XH[col + 1] * 8 : 0; + int yb = 63 * row + 46; + /* Чёрная плита (opaque NORMAL — пишет в VRAM И ОЗУ-копию акселератора): + * стирает ЗАПЕЧЁННЫЙ пиксель-в-пиксель loose-пол с обоих тайлов, иначе + * heal вернул бы старый пол из ОЗУ-копии. Ширина 40 — кусок пола + * заходит на x+32..+40. */ + /* НИЗ строго 63*row+65 = низ собственной графики плиты (bottom_id 43/73/74 + * стоит ровно на dby; base 41/69/70 — до dmy=62; правая грань 42/66/67 — + * до dby-1). Ниже начинается СТЕНА ряда ниже (высокий спрайт), которую + * здесь не перерисовываем: заливка на 9 строк глубже оставляла ЧЁРНЫЙ БОКС + * под соседним столбом, причём в ОЗУ-копии — то есть heal возвращал его + * снова, и на второй странице дабл-буфера его не было → мерцание. + * Тот же урок уже учтён в pop_floor_bake. */ + yb += POP_YOFF; + /* bar идёт мимо pop_blit_b — пометить область самим (DRAW-COST). */ + pop_cd_touch(x, yb, 72, POP_YOFF + 63 * row + 66 - yb); + gfx_set_bank(GFX_BANK_NORMAL); + setfillstyle(SOLID_FILL, BLACK); + if (bake_w(x, 40)) pop_bar_black(x, yb, bake_w(x, 40), POP_YOFF + 63 * row + 65 - yb + 1); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */ + if (col + 1 < 10) + if (bake_w(xr, 32)) pop_bar_black(xr, yb, bake_w(xr, 32), POP_YOFF + 63 * row + 65 - yb + 1); /* сосед: РОВНО 32 — не залезть в col+2 */ + /* Новый статич. фон: сам тайл (empty → «стена вниз» topright комнаты + * снизу) + правый сосед (его левая грань: был loose-край pop_env_b(42), + * теперь пусто). TRANSPARENT — 0xFF не забивает чёрный, пишет и в + * ОЗУ-копию → heal сохранит. */ + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */ + draw_tile(row, col); + if (col + 1 < 10) + draw_tile(row, col + 1); + pop_t_bake_rest = 0; + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* ---- Падающий кусок loose (mob, seg007 move_loose) ------------------ * + * Упрощ. одиночный кусок (в комнате 1 — один loose). Модель fixed- + * corridor: коридор падения (колонка тайла) heal'ится каждый кадр (стирает + * прошлый кусок, возвращает печёный фон — empty+«стена вниз»), поверх — + * кадр 0 loose (41/42/43) на текущей y. Гравитация: speed 0→+3/кадр + * (cap 29), y+=speed. Двоебуфер: коридор фиксирован → heal чистит любой + * прошлый кадр ЭТОЙ страницы. Ниже поля кусок прячет clip_borders. */ +/* Падающие куски — ТАБЛИЦА (порт mobs[14]/mobs_count, seg007:965). Одного + * куска не хватает: в комнате 7 плиты (0,5) и (0,6) соседние, Kid роняет обе + * подряд, и второй спавн затирал состояние первого — его пиксели оставались + * висеть в воздухе (на каждой странице на своей y → мерцание), а щебень + * появлялся только под одной плитой. + * + * Слотов СТОЛЬКО ЖЕ, СКОЛЬКО В ОРИГИНАЛЕ (14). Было 4 с обоснованием + * «больше одновременно падающих плит в одной комнате практически не + * бывает» — неверным ровно с уровня 13: check_fall_flo роняет ШЕСТЬ разом + * (комнаты 23 и 16), а с учётом плит, которые эти куски сбивают по дороге + * (loose_fall), в воздухе бывает и больше. Лишний кусок молча терялся + * (`if (!m) return`), то есть тайл исчезал, а щебня на месте посадки не + * появлялось — найдено прогоном 2026-08-13, комната 16, тайл (1,5). + * Цена — 150 байт W2. */ +#define MOB_MAX 14 +typedef struct { + uint8_t active; /* 0 нет; 1 падает */ + uint8_t clean; /* кадров дочистки после конца полёта (2 = обе стр.) */ + uint8_t defer; /* кусок ниже Kid → рисуем ПОСЛЕ него (y-порядок) */ + int x; /* левый край (POP_COL_XH*8) */ + int y; /* = curmob.y оригинала (низ куска, dby), растёт вниз */ + /* Где кусок рисуется НА ЭКРАНЕ в этом кадре, MOB_Y_NONE = не рисуется. + * Отдельно от y, потому что кусок виден и из СОСЕДНЕЙ комнаты: порт + * draw_mob (seg007:13E5) сдвигает его на +192 (комната снизу) или на + * −189 (комната сверху). См. mob_tick_one. */ + int draw_y; + int prev_y[2]; /* последняя нарисованная y на КАЖДОЙ странице (heal) */ + int8_t speed; + int8_t row, col; /* тайл-позиция куска (для окклюзии соседа) */ + uint8_t room; /* curmob.room: кусок падает В СВОЕЙ комнате, не в текущей */ +} mob_t; +/* НЕ static намеренно: статики банкового модуля в карту символов не + * попадают, и разобрать зависший кадр по слотам (y каждого куска, порядок, + * defer) в отладчике нечем — адреса просто нет. Снято 2026-08-13 для + * разбора артефактов множественного падения; наружу таблица не объявлена и + * никем не читается. */ +mob_t mobs[MOB_MAX]; +/* Сколько кусков сейчас летит (active). Считает pop_loose_mob_tick. */ +static uint8_t mobs_live; + +/* Кусок «приземлился» (порт loose_land): 0 = нет, иначе tilepos+1 тайла, на + * который он лёг → приложение/pop_map ставит debris + do_knock. */ +/* pop_loose_landed переехал в pop_state.c: его читает pop_map (W1/W2), а + * писучие данные банкового модуля недоступны снаружи (см. pop_state.h). */ + +/* y_loose_land / y_something (seg007): [row+1], row = −1..3. Ряд −1 — + * плита-потолок (ряд 2 комнаты сверху): в оригинале это отрицательный + * tilepos, у нас — свой ряд полосы у потолка. */ +static const int16_t MOB_Y_LAND[5] = {2, 65, 128, 191, 254}; +static const int16_t MOB_Y_BOUND[5] = {-1, 62, 125, 188, 25}; +#define MOB_Y_NONE 0x7FFF + +/* Габарит падающего куска. Части и их места (порт draw_mob, seg007:13E5; + * индексы loose_fram_*[10], seg008:518/596/608): + * + * env 70 левая часть при (mob_x, mob_y-3) + * env 74 низ при (mob_x, mob_y) + * env 72 правая часть при (mob_x+32, mob_y-1) + * + * Размеры (сняты из каталогов атласов; РАСКЛАДКА env: page = id>>4, + * idx = id&15 — POP_ENV_SHIFT равен 4, не 5): + * + * оба тайлсета 70 = 32x13 74 = 32x3 + * 72 = 26x16 в подземелье, 25x16 во дворце + * + * Отсюда след: по x mob_x .. mob_x+57 (32+26−1), по y mob_y-16 .. mob_y + * (правая часть выше всех: 1+16−1). Именно эти числа и стоят в шапке + * mob_render — composite их пересчитывает сам и кладёт в шапку блоба. + * + * Коридор: MOB_X0 = mob_x при MOB_W = 64 даёт mob_x .. mob_x+63, то есть + * шесть пикселей запаса справа. Раньше было mob_x-4 при той же ширине + * (mob_x-4 .. mob_x+59): четыре пикселя слева, где кусок не рисует ничего, + * и всего два справа. Площадь та же, запас переехал туда, где он нужен. */ +#define MOB_X0(mx) (mx) +#define MOB_W 64 /* mob_x .. mob_x+63 */ +/* Коридор heal по высоте: mob_y-20 .. mob_y+3 при следе mob_y-18 .. mob_y. + * Уже НЕ сужать: 24 строки — это след подземелья (19) плюс две строки поля с + * каждой стороны. Сужение до 18 «по палаццовому следу» сломало бы + * подземелье. */ +/* Сами величины коридора — в mob_heal_up / mob_heal_h у начала файла: они + * считаются из высоты собранного композита, а не константами. */ + +/* Свободный слот = не летит И дочистка завершена (иначе затрём хвост, + * который ещё не стёрт со второй страницы). Общий для обычного отрыва и + * для копии из loose_fall — раньше цикл был написан дважды. */ +static mob_t *mob_alloc(void) +{ + uint8_t i; + for (i = 0; i < MOB_MAX; i++) + if (!mobs[i].active && !mobs[i].clean) { + pop_mob_busy = 1; /* гейт холостого хода — см. pop_state.h */ + return &mobs[i]; + } + return 0; +} + +static void mob_spawn(uint8_t room, int row, int col) +{ + mob_t *m = mob_alloc(); + if (!m) return; /* таблица полна — кусок без спрайта (тайл всё равно пуст) */ + m->x = POP_COL_XH[col] * 8; + m->y = MOB_Y_LAND[row + 1]; /* y_loose_land: низ куска в момент отрыва */ + m->row = (int8_t)row; + m->col = (int8_t)col; + m->room = room; /* curmob.room (add_mob, seg007:1010) */ + m->speed = 0; + m->active = 1; + m->clean = 0; + m->defer = 0; + m->prev_y[0] = m->prev_y[1] = MOB_Y_NONE; + m->draw_y = MOB_Y_NONE; +} + +/* Отрыв куска в ТЕКУЩЕЙ комнате (обычный случай — pop_loose_tick). */ +void pop_loose_mob_spawn(int row, int col) __banked +{ + mob_spawn(pop_t_room, row, col); +} + +/* Копия куска ОТ ПОЗИЦИИ УДАРА — порт хвоста loose_fall (seg007:1085): + * curmob.y += 6; mob_down_a_row(); add_mob(); + * то есть новый кусок заводится не из тайла, а из места, где его сбили: + * на 6 пикселей ниже и рядом ниже, со скоростью УЖЕ ПОДЕЛЁННОЙ пополам. + * Через границу комнаты — как mob_down_a_row: y −= 192, ряд 0, комната снизу. */ +static void mob_spawn_copy(const mob_t *src) +{ + mob_t *m; + int16_t y = (int16_t)(src->y + 6); + int8_t row = (int8_t)(src->row + 1); + uint8_t room = src->room; + + if (row >= 3) { + y = (int16_t)(y - 192); + row = 0; + room = pop_room_link(room, 3 /* вниз */); + if (!room) return; /* ушло из уровня — куску негде лететь */ + } + m = mob_alloc(); + if (!m) return; + m->x = src->x; + m->y = y; + m->row = row; + m->col = src->col; + m->room = room; + m->speed = src->speed; /* уже half (loose_fall делит ДО add_mob) */ + m->active = 1; + m->clean = 0; + m->defer = 0; + m->prev_y[0] = m->prev_y[1] = MOB_Y_NONE; + m->draw_y = MOB_Y_NONE; + /* Кусок рождается ВНУТРИ mob_tick_one (loose_fall сбил плиту), и слот ему + * mob_alloc выдаёт с начала таблицы — то есть, скорее всего, УЖЕ + * пройденный циклом pop_loose_mob_tick. Своей пометки «фон трогали» этот + * кусок в текущем кадре не получит, а нарисован будет; помечаем коридор + * здесь — иначе персонаж под ним стирается и не просит перерисовки + * (см. pop_cd_mute в mob_render). */ + pop_cd_touch(MOB_X0(m->x), m->y - 27 + POP_YOFF, MOB_W, 64); +} + +/* Отрыв куска в ЧУЖОЙ комнате: плита, которую Кид расшатал и убежал, всё + * равно проваливается (animate_loose в pop_trob крутит её trob по своей + * комнате, как do_trobs оригинала). Летит и садится кусок дальше сам — + * mob_tick_one давно умеет чужую комнату (mob_tile_at / pop_loose_exit). */ +void pop_loose_mob_spawn_at(uint8_t room, int row, int col) __banked +{ + mob_spawn(room, row, col); +} + + +/* Остановить падающий кусок (при смене комнаты — состояние per-tile не + * должно утечь в новую комнату). */ +static void mob_render(mob_t *m, uint8_t pg); + +void pop_loose_mob_reset(void) __banked +{ + uint8_t i; + for (i = 0; i < MOB_MAX; i++) { + mobs[i].active = 0; mobs[i].clean = 0; mobs[i].defer = 0; + mobs[i].prev_y[0] = mobs[i].prev_y[1] = MOB_Y_NONE; + } +} + +/* Смена комнаты. Куски НЕ останавливаем: в оригинале mobs[] чистится только + * в start_level (seg003:88), а do_mobs (seg007:1063) крутит их все независимо + * от drawn_room — move_loose работает по curmob.room, и плита, за которой Кид + * не досмотрел, всё равно долетает и кладёт щебень (BUG-LOOSE-2: в комнате 12 + * плита 0,1 не успевала упасть до ухода в комнату 15). Сбросить надо только + * heal-историю: новая комната перерисована целиком, старых пикселей нет. */ +/* Соседи ОТРИСОВАННОЙ комнаты сверху/снизу — для draw_mob (см. mob_tick_one). + * Кэш, а не вызов каждый кадр: pop_room_link лезет в страницу уровня, то есть + * мапит и размапливает W0. Инвалидируется сменой комнаты. */ +static uint8_t mob_link_room, mob_link_up, mob_link_dn; + +void pop_loose_mob_qsave(pop_qs_io_t *io) __banked +{ + uint8_t i, live = 0; + for (i = 0; i < MOB_MAX; i++) { + mob_t *m = &mobs[i]; + pop_qs_u8(io, &m->active); + pop_qs_i16(io, &m->x); pop_qs_i16(io, &m->y); + pop_qs_i8(io, &m->speed); pop_qs_i8(io, &m->row); + pop_qs_i8(io, &m->col); pop_qs_u8(io, &m->room); + if (io->reading && io->apply) { + m->clean = m->defer = 0; + m->draw_y = m->prev_y[0] = m->prev_y[1] = MOB_Y_NONE; + if (m->active) live++; + } + } + if (io->reading && io->apply) { + mobs_live = live; + pop_mob_busy = (uint8_t)(live != 0); + mob_link_room = 0; + } +} + +static void mob_links(void) +{ + if (mob_link_room == pop_t_room) return; + mob_link_room = pop_t_room; + mob_link_up = pop_room_link(pop_t_room, 2); + mob_link_dn = pop_room_link(pop_t_room, 3); +} + +void pop_loose_mob_room_changed(void) __banked +{ + uint8_t i; + for (i = 0; i < MOB_MAX; i++) { + mobs[i].defer = 0; + mobs[i].prev_y[0] = mobs[i].prev_y[1] = MOB_Y_NONE; + mobs[i].draw_y = MOB_Y_NONE; + /* дочистка хвоста относилась к покинутой комнате — она уже не нужна */ + if (!mobs[i].active) mobs[i].clean = 0; + } + mob_link_room = 0; /* кэш соседей — под новую комнату */ +} + +/* Тайл под куском. В своей комнате — обычный tile_code (он знает про кромки + * и соседей), в чужой — прямо из данных уровня. */ +static uint8_t mob_tile_at(mob_t *m) +{ + if (m->room == pop_t_room) return (uint8_t)(pop_tile_code(m->row, m->col) & 0x1F); + if (m->row < 0 || m->row > 2 || m->col < 0 || m->col > 9) return 1; /* кромка = пол */ + return (uint8_t)(pop_level_tile(m->room, (uint8_t)(m->row * 10 + m->col)) & 0x1F); +} + +/* loose_fall (seg007:1082) — падающий кусок пробил ЕЩЁ ОДНУ расшатанную + * плиту. Порт дословный, три действия В ОДНОМ КАДРЕ: + * 1. налетевший кусок тормозится вдвое (`curmob.speed >>= 1`); + * 2. сбитый тайл снимается с карты немедленно (`remove_loose`); + * 3. на месте удара заводится ВТОРОЙ кусок — копия налетевшего, сдвинутая + * на 6 пикселей и ряд вниз (`curmob.y += 6; mob_down_a_row; add_mob`). + * То есть в столбце дальше летят ДВА куска, и это правильное поведение + * оригинала, а не дефект. + * + * Здесь стоял обходной путь: плите ставилась фаза «падает следующим кадром», + * а всю работу делал pop_loose_tick. Цена оценивалась как «один кадр + * задержки» и оказалась выше: защита «один сигнал pop_loose_fell на кадр» + * придерживает провал, пока главный цикл не разберёт предыдущий, и на 13-м + * уровне (плиты сыплются пачкой) вторая плита появлялась через несколько + * кадров — уже после того, как первая улетела с экрана, — да ещё и с высоты + * покоя тайла, а не от места удара (найдено прогоном 2026-08-13). */ +static void mob_break_loose(mob_t *m) +{ + m->speed >>= 1; + pop_loose_remove(m->room, (uint8_t)(m->row * 10 + m->col)); + mob_spawn_copy(m); +} + +static mob_t *mt_m; +static uint8_t mt_pg, mt_here; + +static void mob_tick_one(mob_t *m, uint8_t pg) +{ + /* Рабочие переменные — file-scope, не локали: у функции пять вызовов, и + * живой через них указатель на слот SDCC спиливал в кадр (16 байт, 99 + * обращений `-N(ix)`). То же лечение, что у draw_tile и blit_b_clip. + * Реентерабельности нет и не требуется: единственный вызывающий — + * pop_loose_mob_tick, и слоты он обходит последовательно. */ + mt_m = m; mt_pg = pg; + if (!mt_m->active && !mt_m->clean) return; + mt_m->draw_y = MOB_Y_NONE; /* по умолчанию не рисуемся; решим в конце */ + mt_here = (uint8_t)(mt_m->room == pop_t_room); + /* Стереть кусок, нарисованный на ЭТОЙ странице в прошлый раз (у каждой + * страницы своя копия фона). Раньше коридор был фиксирован под ряд 2; + * плита-потолок падает через весь ряд 0, поэтому чистим по фактической + * прошлой позиции. */ + /* Гейта по `mt_here` здесь БЫТЬ НЕ ДОЛЖНО: prev_y[mt_pg] — это след, который + * кусок оставил ИМЕННО НА ЭТОЙ странице, и стереть его надо независимо + * от того, где кусок сейчас. Пока чистка была под `mt_here`, кусок, + * улетевший в комнату снизу (mob_down_a_row), навсегда оставлял себя + * на одной из двух страниц дабл-буфера — «застрявшая плита», видимая + * через кадр (BUG-MOB-STALE-PAGE). */ + if (mt_m->prev_y[mt_pg] != MOB_Y_NONE) { + /* Ширина/высота — по СЛЕДУ, а не по спрайту. Спрайт куска целиком + * влезает в 64x32 (41: 32x13 с y-3, 42: 26x15 с x+32,y-1, 43: 32x3), + * но mob_render рисует ещё и ЦЕЛЫЙ соседний тайл поверх правой части + * (окклюзия грани пола), причём в GFX_BANK_SPRITE — то есть мимо + * теневой копии фона. Всё, что тот тайл нарисовал вне коридора + * heal'а, не стиралось никогда: кусок улетал, а в соседнем столбце + * оставался обрезок пола (найдено прогоном 2026-08-13). Коридор + * расширен до площади, которую реально может тронуть эта + * перерисовка: столбец соседа по ширине и ряд по высоте. + * + * ЗАДЕЛ: правильнее не перерисовывать соседа вовсе, а КЛИПОВАТЬ + * правую часть куска — оригинал так и делает (add_mob_to_objtable, + * seg007:1161: clip.right = 40). Тогда коридор вернётся к 64x32. */ + pop_heal_off(MOB_X0(mt_m->x), mt_m->prev_y[mt_pg] - mob_heal_up, MOB_W, mob_heal_h); + mt_m->prev_y[mt_pg] = MOB_Y_NONE; + } + if (!mt_m->active) { mt_m->clean--; return; } + /* гравитация (move_loose): ускорение до 29 */ + if (mt_m->speed < 29) mt_m->speed += 3; + mt_m->y += mt_m->speed; + /* move_loose (seg007:1126): низ куска пересёк границу своего ряда — + * смотрим тайл этого ряда. Пусто/loose — валится дальше, пол — + * приземление (loose_land: debris + do_knock). Через LOOSE кусок именно + * ПРОЛЕТАЕТ, как через пустоту, — но по дороге СРЫВАЕТ её (loose_fall, + * seg007:1082). + * + * Здесь раньше стояло «порт loose_fall не нужен: в ряду ниже её нет» — + * допущение, верное для уровней 1-11 и сломавшееся на 13-м: комната 16, + * плиты (1,6) и (1,7), на которые сверху сыплется гряда из комнаты 1. + * Куски пролетали сквозь них правильно, а сами плиты оставались целыми и + * нарисованными (найдено прогоном 2026-08-13). */ + if (mt_m->row < 3 && mt_m->y < 226 && MOB_Y_BOUND[mt_m->row + 1] <= mt_m->y) { + uint8_t t = mob_tile_at(mt_m); + if (t == 11) mob_break_loose(mt_m); + if (t == 0 || t == 11) { + /* mob_down_a_row (seg007:1387): ряд ниже, а из НИЖНЕГО ряда — + * в комнату СНИЗУ (y -= 192, ряд 0). Именно так кусок пролетает + * несколько комнат подряд: уровень 6, плита (1,7) комнаты 6 + * проваливается сквозь пустую шахту комнаты 7 и приземляется на + * кнопку (2,7) комнаты 11. Раньше мы кусок на границе комнаты + * ГАСИЛИ и подменяли одним «переходом вниз» из главного цикла — + * дальше первой комнаты он не летел (BUG-MOB-MULTIROOM). */ + mt_m->row++; + if (mt_m->row >= 3) { + mt_m->y = (int16_t)(mt_m->y - 192); + mt_m->row = 0; + mt_m->room = pop_room_link(mt_m->room, 3 /* вниз */); + if (!mt_m->room) { /* улетел из уровня — просто гаснет */ + mt_m->active = 0; mt_m->clean = 2; /* дочистить ОБЕ страницы */ + return; + } + mt_here = (uint8_t)(mt_m->room == pop_t_room); + } + } else if (!mt_here && pop_loose_exit) { + /* Сигнал прошлой посадки в чужой комнате ещё не разобран (он + * ОДИН на кадр) — держим кусок на границе ряда и садим следующим + * кадром, как это уже сделано для pop_loose_landed ниже. Без + * этого второй кусок затирал первый, и из нескольких плит, + * улетевших в одну дыру, щебень оставляла только одна (найдено + * прогоном 2026-08-13, уровень 13). */ + mt_m->y = MOB_Y_BOUND[mt_m->row + 1]; + } else if (!mt_here) { + /* Приземление в ПОКИНУТОЙ комнате: рисовать нечего, но щебень и + * КНОПКА под куском обязаны сработать — сигналим главному циклу + * комнатой и тайлом (loose_land, seg007:11E8, работает с + * curmob.room, а не с отрисованной). */ + pop_sfx_play(2); /* tile crashing, seg007:1041 */ + pop_loose_exit_room = mt_m->room; + pop_loose_exit = (uint8_t)(mt_m->row * 10 + mt_m->col + 1); + /* clean = 2 даже для чужой комнаты: пока кусок летел, он мог + * рисоваться в ЭТОЙ, и след остался на обеих страницах. */ + mt_m->active = 0; mt_m->clean = 2; + return; + } else if (pop_loose_landed) { + /* сигнал прошлого приземления ещё не разобран (один за кадр) — + * подержим кусок на границе и приземлим следующим кадром */ + mt_m->y = MOB_Y_BOUND[mt_m->row + 1]; + } else { + pop_sfx_play(2); /* tile crashing, seg007:1041 */ + mt_m->y = MOB_Y_BOUND[mt_m->row + 1]; + pop_loose_landed = (uint8_t)(mt_m->row * 10 + mt_m->col + 1); + mt_m->active = 0; mt_m->clean = 2; /* кусок исчез — на его месте debris */ + return; + } + } + /* Страховка: кусок ушёл ниже поля, не поймав границу ряда (в норме этого + * не бывает — mob_down_a_row уводит его в комнату снизу). Просто гаснет, + * как у оригинала кусок в комнате 0 (move_loose: `speed = -2`). */ + if (mt_m->y - 27 >= POP_PLAYFIELD_H) { + mt_m->active = 0; mt_m->clean = 2; + return; + } + /* Порт ГОЛОВЫ draw_mob (seg007:13E5). Кусок виден НЕ ТОЛЬКО в своей + * комнате: оригинал рисует его ещё и когда он только что провалился в + * комнату СНИЗУ (пока |y| < 18 — со сдвигом +192, то есть выглядывает из + * нижней кромки) или летит в комнате СВЕРХУ и уже подошёл к её низу + * (y >= 174 — со сдвигом −189, вываливается из верхней кромки). + * + * Без этого плита, падающая из НИЖНЕГО ряда, исчезала мгновенно: она + * уходит в комнату снизу на ПЕРВОМ же move_loose (спавн y = 191 при + * границе ряда 188), и у нас после этого не рисовалась вовсе. Ряды 0 и 1 + * летят внутри комнаты, поэтому баг был виден только на ряду 2 — ровно + * как заметил пользователь (уровень 11, комната 14). */ + if (mt_here) { + if (mt_m->y >= 210) return; /* ниже поля — оригинал не рисует */ + mt_m->draw_y = mt_m->y; + } else { + mob_links(); + if (mt_m->room == mob_link_dn) { + int8_t yb = (int8_t)mt_m->y; /* ABS((sbyte)ypos) < 18 */ + if (yb >= 18 || yb <= -18) return; + mt_m->draw_y = mt_m->y + 192; + } else if (mt_m->room == mob_link_up) { + if (mt_m->y < 174) return; + mt_m->draw_y = mt_m->y - 189; + } else { + return; /* дальше соседей не рисуем */ + } + } + /* Порядок «плита vs Kid». В оригинале объекты рисуются при обходе СВОИХ + * тайлов (redraw_needed_tiles: ряды 2,1,0; колонки 0..9), и только внутри + * ОДНОГО тайла сортируются по obj_y (sort_curr_objs, seg008:1561: кто + * ниже — тот позже, т.е. поверх). Тайл падающего куска — draw_mob + * (seg007:13E5): row = y_to_row_mod4(y), col = xh>>2. Значит кусок + * поверх Kid, если его тайл идёт в обходе ПОЗЖЕ тайла Kid, а при + * совпадении тайлов — если он ниже по y. */ + { + int8_t mrow = pop_y_to_row((int16_t)mt_m->draw_y); /* y_to_row_mod4 */ + uint8_t over; + if (mrow < 0 || mrow > 2) { + /* КОРЗИНА 30 (get_tilepos_nominus, seg006:110). Ряд объекта вне + * комнаты: y_to_row_mod4 берёт остаток по 4, поэтому «выше + * потолка» и «ниже пола» дают одно и то же −1, а оригинал сводит + * оба в тайл 30. Объекты этого тайла рисуются ПЕРВЫМИ — + * draw_objtable_items_at_tile(30) стоит в redraw_needed_tiles до + * всего обхода тайлов. Значит кусок под ВСЕМ, включая Кида; до + * сих пор сравнение рядов давало обратное («−1 обходится + * последним» = поверх). */ + over = 0; + } + else if (mrow != pop_bg_obj_row) over = (mrow < pop_bg_obj_row); /* ряды идут 2,1,0 */ + else if (mt_m->col != pop_bg_obj_col) over = (mt_m->col > pop_bg_obj_col); + else over = (mt_m->draw_y > pop_cd[POP_CH_KID].fpy); + /* РИСОВАНИЯ ЗДЕСЬ БОЛЬШЕ НЕТ — только решение, в какой проход кусок + * пойдёт. Пока heal и вывод шли в одной итерации, heal СЛЕДУЮЩЕГО + * куска срезал верхнюю кромку уже нарисованного предыдущего: на 13-м + * уровне куски идут лесенкой в соседних столбцах с разницей ~20 px + * по y, и перекрытие гарантировано (артефакт «чёрный бар», найден + * прогоном 2026-08-13, комната 23). Теперь сперва тикают ВСЕ куски + * (то есть heal'ятся все), и только потом рисуются. */ + mt_m->defer = over; + } +} + +/* Один проход отрисовки кусков. want_defer: 0 — те, что под Kid (идут сразу + * после фона), 1 — те, что поверх него (после персонажей). + * + * Порядок внутри прохода — ПО y, но ПО УБЫВАНИЮ: сперва нижние, последним + * верхний. Это НЕ описка и не общее правило «ниже = поверх»: сравнение двух + * объектов в оригинале (compare_curr_objs, seg008:1572) разводит два случая, + * + * оба объекта — падающие плиты (тип 0x80): y1 < y2 → по УБЫВАНИЮ + * любая другая пара: y1 > y2 → по возрастанию + * + * то есть между СОБОЙ куски укладываются наоборот, чем всё остальное: верхний + * ложится поверх нижнего. Видно на паре из loose_fall (сбитая плита летит в + * 6 пикселях под налетевшей): при общем правиле нижняя перекрывала верхнюю, + * и это поймал прогон 2026-08-13. + * + * Порядок «кусок против Кида» здесь ни при чём — его решает defer выше, и там + * действует обычное правило. */ +/* Пометки СОСЕДА СПРАВА — порт draw_mob (seg007:1147). Оригинал каждый + * кадр помечает тайл col+1 в ряду куска сразу двумя пометками: + * + * set_redraw2(tilepos, 1); // фон соседа — рисуется ДО куска + * set_redraw_fore(tilepos, 1); // его передние части — ПОСЛЕ куска + * + * и повторяет их для второго ряда, если кусок висит на границе (ypos-18). + * Так правая часть куска (спрайт 72 лежит в столбце col+1) прячется за + * передней гранью соседнего пола, а передние части чужого тайла — колонна, + * например, — ложатся поверх куска. + * + * Здесь была подпорка: целый соседний тайл рисовался ПОВЕРХ куска прямо в + * mob_render. Она тащила на плиту чужой узор и окно и стоила по полной + * отрисовке тайла на кусок за кадр (MOB-CLIP-RIGHT). */ +static void mob_mark_neighbour(const mob_t *m) +{ + int8_t r = pop_y_to_row((int16_t)m->draw_y); + int8_t rt = pop_y_to_row((int16_t)(m->draw_y - 18)); + int8_t c = (int8_t)(m->col + 1); + /* ПОЛОСА У ПОТОЛКА (ряд −1). Её передние куски (нижняя кромка потолка и + * кадр плиты-потолка) живут в трёх верхних строках поля — dby = 2 при + * клипе по POP_YOFF, — а спрайт куска занимает mob_y−16 .. mob_y. Значит + * кусок закрывает кромку, пока mob_y − 16 <= 2, то есть mob_y <= 18: три + * кадра после отрыва (y = 2, 5, 11 при ускорении 3). Помечаем ОБА + * столбца, которые кусок накрывает по x (mob_x .. mob_x+62 — это col и + * col+1). + * + * Без этого кусок оказывался ПОВЕРХ кромки потолка соседней колонки — + * физически невозможно (найдено пользователем 2026-08-17, ур.1 к.6: + * плита (−1,5) перекрывала потолок (−1,6)). В оригинале порядок даёт + * сама структура таблиц: кусок идёт в midtable (draw_mobs), а полоса + * потолка — в foretable, и та рисуется позже (draw_tables, seg008:1373). */ + if (m->draw_y <= 18) { + /* Пустой тайл полосы потолка не рисует В ПЕРЕДНЕМ СЛОЕ ничего + * (bottom_id = 0, FORE_ANY = 0), а пометка стоит потом двух трамплинов + * — в банк 7 за разбором пометок и в банк 2 за самой отрисовкой, + * ~8 900 такта каждый. А пустой он тут как раз чаще всего: кусок + * существует ровно потому, что плита из своей клетки уже выпала. + * Проверяем pop_tile_code — он резидентный, ~1 100 такта. */ + if (pop_tile_code(-1, m->col)) + pop_set_redraw_fore_above((uint8_t)m->col); + if (m->col + 1 <= 9 && pop_tile_code(-1, m->col + 1)) + pop_set_redraw_fore_above((uint8_t)(m->col + 1)); + } + if (c > 9) return; + if (r >= 0 && r <= 2) pop_set_redraw_fore((uint8_t)(r * 10 + c)); + if (rt != r && rt >= 0 && rt <= 2) pop_set_redraw_fore((uint8_t)(rt * 10 + c)); +} + +/* Вторая половина draw_mob (seg007:1149): рядом с set_redraw_fore там стоит + * set_redraw2, то есть draw_other_overlay соседнего тайла — его части уходят в + * midtable и ложатся ПОВЕРХ куска (порядок разобран в pop_mob_overlay_tile). + * Звать СРАЗУ после mob_render: списков у нас нет, порядок эмулируется + * последовательностью вызовов. */ +static void mob_overlay_neighbour(const mob_t *m) +{ + int8_t r = pop_y_to_row((int16_t)m->draw_y); + int8_t rt = pop_y_to_row((int16_t)(m->draw_y - 18)); + /* КОРЗИНА 30 оригинала (get_tilepos_nominus, seg006:110): ряд объекта вне + * комнаты. y_to_row_mod4 берёт остаток по 4, поэтому «выше потолка» и + * «ниже пола» дают одно и то же −1 — оригинал сводит оба случая в тайл 30 + * и рисует такие объекты ПЕРВЫМИ, до всего обхода тайлов + * (draw_objtable_items_at_tile(30) в redraw_needed_tiles). + * + * Отсюда два отличия от обычного куска: + * - ориентир «тайл объекта» = 3, то есть РАНЬШЕ любого ряда обхода + * (2,1,0). Сюда уходил сырой −1, и гейт other_overlay_tile + * `row > pop_bg_obj_row` читал его наоборот — «объект поверх всего» — + * и соседа не возвращал вовсе (тело плиты пропадало, оставался + * только её торец из переднего слоя); + * - перекрыть кусок должна и СВОЯ клетка, а не только сосед справа: + * после объекта у оригинала рисуются ВСЕ тайлы. У куска в обычном + * ряду своя клетка не перерисовывается — там объект вливается в + * midtable уже ПОСЛЕ частей своего тайла. */ + uint8_t b30 = (uint8_t)(r < 0 || r > 2); + int8_t oref = b30 ? (int8_t)3 : r; + int8_t c, cend = (int8_t)(m->col + 1); + int ytop, ybot; + if (cend > 9) cend = 9; + /* Габарит куска — по ЭКРАННОЙ координате: у куска, видимого из соседней + * комнаты, draw_y отличается от y на 192. */ + ytop = m->draw_y - (int)mob_spr[2] + 1; + ybot = m->draw_y; + /* ПРЕДУСЛОВИЯ ПРОВЕРЯЕМ ЗДЕСЬ, до вызова в банк 2. pop_mob_overlay_tile + * живёт в банке 2, то есть каждый вызов из банка 7 платит межбанковый + * трамплин (~8 900 такта); а условий всего два — тайл не пуст и габарит + * тайла пересекается с габаритом куска. Оба дешёвые: pop_tile_code — + * резидент (~1 100). Замер 2026-08-17: без этого гейта двенадцать + * вызовов на шесть кусков стоили ~130 000 в циане, причём большинство + * ничего не рисовало. + * + * Габарит тайла берём ПОЛНЫМ (63*row+2 .. +66) — какая именно часть + * соседа окажется поверх куска, решит уже окно клипа внутри. */ + for (c = b30 ? (int8_t)m->col : cend; c <= cend; c++) { + if (c < 0) continue; + if (r >= 0 && r <= 2 && pop_tile_code(r, c) && + ybot >= 63 * r + 2 && ytop <= 63 * r + 66) + pop_mob_overlay_tile(r, c, oref, (int8_t)m->col, m->x, m->draw_y, mob_spr[2]); + if (rt != r && rt >= 0 && rt <= 2 && pop_tile_code(rt, c) && + ybot >= 63 * rt + 2 && ytop <= 63 * rt + 66) + pop_mob_overlay_tile(rt, c, oref, (int8_t)m->col, m->x, m->draw_y, mob_spr[2]); + } +} + +static void mob_draw_pass(uint8_t pg, uint8_t want_defer) +{ + uint8_t order[MOB_MAX], n = 0, i, j; + /* Ранний выход: проход зовётся ДВАЖДЫ за кадр (под Kid и поверх него), и + * без него мы перебирали бы все MOB_MAX слотов 28 раз в кадре даже + * когда в воздухе пусто. Счётчик ведёт тик, которому проход по слотам + * нужен и так. */ + if (!mobs_live) return; + for (i = 0; i < MOB_MAX; i++) { + mob_t *m = &mobs[i]; + /* Отбор по draw_y, а НЕ по комнате: кусок, только что провалившийся + * в комнату снизу, ещё виден из нашей (см. mob_tick_one). */ + if (!m->active || m->draw_y == MOB_Y_NONE) continue; + if ((uint8_t)(m->defer != 0) != want_defer) continue; + for (j = n; j > 0 && mobs[order[j - 1]].draw_y < m->draw_y; j--) + order[j] = order[j - 1]; + order[j] = i; + n++; + } + for (i = 0; i < n; i++) { + /* Пометки соседа ставятся ЗДЕСЬ, а не в тике: у оригинала draw_mob + * (seg007:1147) считает ряд от той же координаты, по которой кусок + * рисуется. Стояли они в цикле тика — то есть по позиции ДО + * mob_tick_one, а рисовался кусок уже по новой. Пока он летит + * внутри ряда, старый и новый тайл совпадают и разницы не видно; но + * в кадр пересечения границы пометка указывала на прежний тайл, и + * передняя часть колонны кусок не перекрывала — ровно ОДИН кадр, + * как и наблюдалось (прогон 2026-08-13, комната 23, колонка 8). */ + mob_mark_neighbour(&mobs[order[i]]); + mob_render(&mobs[order[i]], pg); + mob_overlay_neighbour(&mobs[order[i]]); + } +} + +void pop_loose_mob_tick(void) __banked +{ + uint8_t pg = gfx_get_draw_page() & 1, i, live = 0, busy = 0; + /* Слоты обходим УКАЗАТЕЛЕМ и пустые отсеиваем ЗДЕСЬ, а не внутри + * mob_tick_one. Индексная запись mobs[i] заставляла SDCC умножать i на + * sizeof(mob_t)=15 заново под каждое поле (.active/.clean/.x/.y), и 14 + * пустых слотов стоили 35 790 тактов в кадре, где не падает НИЧЕГО + * (замер 2026-08-13). Гард в самом mob_tick_one оставлен: функция + * зовётся и из других мест. */ + mob_t *m = mobs; + /* Ни одного занятого слота — обходить 14 штук незачем (замер 11/15: + * 12 090 тактов холостого хода). Гейт снимается ниже, по факту прохода, + * в котором не осталось ни active, ни дочистки. */ + if (!pop_mob_busy) return; + /* Пометки всех кусков — ОДНИМ пакетом: настоящий pop_cd_touch стоит 4 502 + * такта (замер 2026-08-17), а кусков в кадре каскада шесть. В пакете они + * копят общий прямоугольник четырьмя сравнениями, и настоящая пометка одна. + * + * Загрубление осознанное: объединение шести коридоров накрывает и ряды + * между ними. Безопасно в одну сторону — лишняя пометка стоит лишней + * перерисовки персонажа, пропущенная стоит ИСЧЕЗНУВШЕГО персонажа. + * Практически гряда идёт по колонкам 2..7, а Кид от неё убегает — на + * замерах разницы в числе перерисовок нет. */ + pop_cd_batch_begin(); + for (i = 0; i < MOB_MAX; i++, m++) { + /* Летящий кусок и его дочистка ходят по чужим пикселям СВОИМ heal'ом, + * мимо pop_blit_b — пока он в кадре, персонажей пропускать нельзя + * (DRAW-COST). Взводим ДО тика: кусок может исчезнуть прямо в нём, а + * стереть себя ему всё равно надо будет. */ + if (!m->active && !m->clean) continue; /* пустой слот — ни адреса, ни вызова */ + /* Метка «фон трогали вот здесь» обязана покрывать ВЕСЬ коридор + * heal'а, а не габарит спрайта. Иначе персонаж, попавший в + * расширенную часть коридора, стирается heal'ом, но перерисовать + * себя не просит (pop_char_skip_mask его пропускает) — и просто + * исчезает с экрана. Ровно это и случилось, когда коридор + * расширили под транзитную перерисовку соседа (прогон + * 2026-08-13: в комнате 23 после падения гряды пропадал Кид). + * Запас сверху — на путь, пройденный за кадр (speed до 29): heal + * стирает ПРОШЛУЮ позицию, она выше текущей. */ + pop_cd_touch(MOB_X0(m->x), m->y - 27 + POP_YOFF, MOB_W, 64); + /* ...и по ПРОШЛОЙ нарисованной позиции: у куска, видимого из соседней + * комнаты, экранная координата отличается от y на 192 — коридор heal'а + * лежит совсем не там, где коридор по y. В пакете это четыре + * сравнения, настоящая пометка всё равно одна. */ + if (m->room != pop_t_room && m->prev_y[pg] != MOB_Y_NONE) + pop_cd_touch(MOB_X0(m->x), m->prev_y[pg] - 27 + POP_YOFF, MOB_W, 64); + mob_tick_one(m, pg); + if (m->active) live++; + if (m->active || m->clean) busy++; /* слот ещё занят — гейт держим */ + } + pop_cd_batch_end(); + mobs_live = live; + pop_mob_busy = busy; +} + +static void mob_render(mob_t *m, uint8_t pg) +{ + /* Падающий кусок (SDLPoP draw_mobs/add_midtable, case 0x80): ТРИ куска — + * left(41) на obj_y-3, bottom(43) на obj_y, right(42) на obj_x+4 (=col+1, + * заходит в тайл СПРАВА) на obj_y-1. */ + gfx_set_bank(GFX_BANK_SPRITE); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* КЛИПА ОБЪЕКТА ЗДЕСЬ НЕТ. У оригинала он есть — add_mob_to_objtable + * (seg007:1161) ставит куску clip.right = 40, — но единицы этого поля я + * НЕ выяснил: буквальные 40 экранных пикселей срезают правый задний угол + * плиты (прогон 2026-08-13). У оригинала obj_x живёт в удвоенных + * единицах (char_x_left = obj_x / 2 + 58), так что 40 может означать и + * не пиксели. До выяснения рисуем кусок целиком. + * + * Подпорки «перерисовать соседний тайл поверх куска» тоже нет: она тащила + * на плиту чужой узор и окно (MOB-CLIP-RIGHT). Цена отказа — правая + * грань куска не прячется за передней гранью соседнего пола; артефакт + * мельче прежнего и уйдёт вместе с сортируемым midtable. */ + /* Спрайты ПАДАЮЩЕГО куска, а не лежащей плиты. У объекта в objtable + * obj_id = 10 (add_mob_to_objtable, seg007:1170), и три части берутся из + * таблиц по этому индексу (seg008:518/596/608): + * loose_fram_left[10] = 70 при (x, y-3) + * loose_fram_bottom[10] = 74 при (x, y) + * loose_fram_right[10] = 72 при (x+32, y-1) + * Здесь стояли 41/43/42 — это индекс 0, то есть плита В ПОКОЕ. Отсюда и + * «цельная ровная плита» вместо двух частей со сдвигом правой половины + * на пиксель (замечено пользователем 2026-08-13 сравнением с оригиналом). + * Габариты частей — в блоке MOB_X0 выше (правая часть у тайлсетов + * РАЗНАЯ: 26 px в подземелье, 25 во дворце). + * + * Рисуем ОДНИМ блитом по композиту (mob_spr_build): три вызова блита + * стоили 258 708 такта в кадре при шести кусках — больше половины + * цианового блока. Три отдельных блита остались резервным путём на + * случай, если композит не собрался. */ + /* Пометки «фон трогали» на время блитов ПОДАВЛЕНЫ: коридор куска + * (MOB_X0 .. +MOB_W, y−27 .. +37) уже помечен ОДНИМ вызовом — из + * pop_loose_mob_tick для летящих кусков и из mob_spawn_copy для куска, + * рождённого внутри тика. Спрайт целиком внутри коридора: по x он + * занимает m->x .. m->x+57, по y m->y−16 .. m->y, коридор — m->x .. + * m->x+63 и m->y−27 .. m->y+37 (запас на ускорение, speed ≤ 31 за кадр). + * Замер 2026-08-17: три настоящих pop_cd_touch стоили 13 506 тактов на + * кусок, то есть 81 000 в кадре при шести кусках. */ + pop_cd_mute(); + /* ВЫХОД ЗА ПОЛЕ РЕЖЕМ САМИ, а не чистим борта после. Раньше здесь стоял + * pop_clip_sprite, то есть кусок рисовался в борт целиком, взводил + * border_dirty, и pop_room_clip_borders потом стирал ДВЕ полосы во всю + * ширину экрана — 320x28 и 320x28. Замер 2026-08-17: 150 978 тактов в + * КАЖДОМ кадре, пока хоть один кусок торчит выше поля, то есть почти весь + * каскад (гряда на 13-м рождается ровно у потолка, y = 2). Это была + * четверть цианового блока. + * + * Окно ставим только когда кусок реально задевает борт: внутри поля блиты + * идут быстрым путём (10 422 против 14 088). */ + { + int ytop = m->draw_y - 16 + POP_YOFF, ybot = m->draw_y + POP_YOFF; + uint8_t clipped = (uint8_t)(ytop < POP_YOFF || ybot >= POP_CLIP_BOTTOM); + if (clipped) pop_t_win_set(0, POP_YOFF, 320, POP_PLAYFIELD_H); + if (mob_spr_ok) { + pop_mem_b(mob_spr, m->x, m->draw_y); + } else { + pop_env_b(74, m->x, m->draw_y); + pop_env_b(70, m->x, m->draw_y - 3); + pop_env_b(72, m->x + 32, m->draw_y - 1); + } + if (clipped) pop_t_win_clear(); + } + pop_cd_unmute(); + m->prev_y[pg] = m->draw_y; + gfx_set_bank(GFX_BANK_NORMAL); +} + +/* Отрисовка кусков, идущих ПОД Kid. Звать ПОСЛЕ pop_redraw_needed(): фон + * кадра обязан быть полностью готов до любого спрайта. + * + * Раньше куски рисовались внутри pop_loose_mob_tick, то есть ДО разбора + * пометок перерисовки, и перепечатка тайла ложилась поверх уже нарисованного + * куска — окно задней стены, передняя грань пола (найдено прогоном + * 2026-08-13, комнаты 16 и 23). */ +void pop_loose_mob_draw(void) __banked +{ + mob_draw_pass(gfx_get_draw_page() & 1, 0); +} + +/* Отложенная отрисовка куска ПОВЕРХ Kid (он ниже Kid по obj_y). Звать + * после отрисовки персонажей, до fore-слоя. */ +void pop_loose_mob_draw_over(void) __banked +{ + mob_draw_pass(gfx_get_draw_page() & 1, 1); +} + +/* Позиция падающего куска для check_loose_fall_on_kid (pop_map): колонка и + * y (низ куска). 0 — куска нет. */ +uint8_t pop_loose_mob_pos(int *col, int *y) __banked +{ + uint8_t i; + /* check_loose_fall_on_kid (seg007:1192) начинается с Char.room == + * curmob.room: кусок, падающий в покинутой комнате, Киду по голове не + * бьёт. */ + for (i = 0; i < MOB_MAX; i++) + if (mobs[i].active && mobs[i].room == pop_t_room) { + *col = mobs[i].col; *y = mobs[i].y; return 1; + } + return 0; +} + +/* Спрайт вышел за поле? → взвести флаг на 2 кадра (обе страницы дабл- + * буфера). Узкий per-спрайт heal НЕ используем: accel-выравнивание блока + * оставляет 1–2px слайверы правой грани → на одной странице мерцает. + * Вместо этого борта чистит pop_room_clip_borders (полоса, но ТОЛЬКО в + * кадрах падения — гейт по флагу). */ +static uint8_t border_dirty; + +void pop_clip_sprite(int x, int w, int sy_top, int h) __banked +{ + int sy_bot = sy_top + h; + (void)x; (void)w; + if (sy_bot > POP_CLIP_BOTTOM || sy_top < POP_YOFF) + border_dirty = 2; /* 2 кадра = обе страницы */ +} + +/* Клип бортов (см. pop_bg.h): работает ТОЛЬКО пока border_dirty (кто-то + * вышел за поле в последние 2 кадра) — иначе ноль стоимости. Возвращает + * чёрный из ОЗУ-копии (вне поля она чёрная) в верхний борт и статус-полосу + * + нижний борт. Звать КАЖДЫЙ кадр ПОСЛЕ всей отрисовки спрайтов. */ +void pop_room_clip_borders(void) __banked +{ + if (!border_dirty) return; + border_dirty--; + /* ОСТАЁТСЯ клипающим осознанно (CLIP-1, 2026-08-01): полоса шириной 320 + * не лезет в 8-битный параметр noclip-ядра, а разбивать её на два куска + * по 160 нет смысла — гейт border_dirty пускает сюда только в кадрах + * падения/выхода за поле, и выигрыш (~2×5.6 К тактов подготовки) там + * тонет в цене самих 320×28 пикселей. Не «оптимизировать» повторно. */ + gfx_heal(0, 0, 320, POP_YOFF); /* верхний борт */ + gfx_heal(0, POP_CLIP_BOTTOM, 320, 256 - POP_CLIP_BOTTOM);/* статус-полоса + нижний борт */ +} + +void pop_room_draw(uint8_t room_num, + const uint8_t *fg, const uint8_t *bg, + const uint8_t *lcol_fg, const uint8_t *lcol_bg) __banked +{ + int row, col; + pop_t_room = room_num; + pop_wall_pattern_reset_b(); /* кэш кладки привязан к номеру комнаты */ + pop_t_fg = fg; pop_t_bg = bg; + pop_t_lfg = lcol_fg; pop_t_lbg = lcol_bg; + + /* прозрачный блит: 0xFF не пишется (чёрный фон/нижние слои проступают) */ + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */ + for (row = 2; row >= 0; row--) /* снизу вверх (2,1,0) */ + for (col = 0; col < 10; col++) + draw_tile(row, col); + /* Полоса кладки у потолка (seg008 draw_room: доп. ряд из room_A, row2 + * при draw_main_y=-1): нижние/правые грани ряда 2 комнаты сверху. Кадры + * высокие — верх ушёл бы в борт (за поле); клип по верхней границе поля + * (POP_YOFF) рисует ТОЛЬКО срез у потолка, без зачернения борта. */ + pop_t_clip_top = POP_YOFF; + for (col = 0; col < 10; col++) + draw_tile(-1, col); + pop_t_clip_top = 0; + pop_t_bake_rest = 0; + gfx_set_bank(GFX_BANK_NORMAL); +} diff --git a/applications/SprPoP/src/pop_settings.h b/applications/SprPoP/src/pop_settings.h new file mode 100644 index 0000000..727f920 --- /dev/null +++ b/applications/SprPoP/src/pop_settings.h @@ -0,0 +1,40 @@ +/* + * pop_settings.h — рантайм-настройки игры (план docs/menu_settings_plan.md + * §5, этап MS0). Единственный контракт между UI/файлом настроек и игрой: + * gameplay-код читает значения, но не знает про UI-структуры и POP.CFG. + * + * Экран Settings и загрузчик POP.CFG уже используют эти поля; начальные + * значения ставит pop_settings_defaults() из pop_boot() (crt0 зануляет + * _DATA, поэтому ненулевые дефолты литералами в данных запрещены). + */ +#ifndef POP_SETTINGS_H +#define POP_SETTINGS_H + +#include + +typedef enum { + POP_PROFILE_VANILLA = 0, /* текущее поведение SprPoP (план §1) */ + POP_PROFILE_ENHANCED = 1 /* будущий набор fixes (план §12) */ +} pop_gameplay_profile_t; + +typedef struct { + uint8_t sound_enabled; /* 0/1 — зеркало звука (Ctrl+S / pop_sfx) */ + uint8_t music_enabled; /* 0/1 — зеркало музыки (Ctrl+M / pop_mus_want) */ + uint8_t speed_mode; /* POP_SPEED_* (pop_pace.h), 0..2 */ + uint8_t gameplay_profile; /* pop_gameplay_profile_t */ + uint8_t cheats_enabled; /* 0/1 — активны ли чит-клавиши */ + uint8_t show_build_info; /* 0/1 — build info на title (этап MS8) */ + uint16_t enhancement_flags; /* маска будущего ENHANCED (план §12) */ + uint8_t time_limit_enabled;/* 0/1 — 60-минутный лимит / unlimited */ + uint8_t show_debug_bar; /* 0/1 — отладочная полоса в верхнем борте */ +} pop_settings_t; + +extern pop_settings_t pop_settings; +void pop_settings_defaults(void) __banked; + +/* Применить модель к runtime-владельцам (темп, читы, звук). До окончания + * pop_sfx_init() sound_ready=0: звук применится отдельным вызовом после + * инициализации набора. */ +void pop_settings_apply(uint8_t sound_ready) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_sfx.c b/applications/SprPoP/src/pop_sfx.c new file mode 100644 index 0000000..0afa57c --- /dev/null +++ b/applications/SprPoP/src/pop_sfx.c @@ -0,0 +1,310 @@ +/* + * pop_sfx.c — ГОРЯЧАЯ часть звуковых эффектов: насос в прерывании CBL, + * запуск/останов и таблица звуков. Зачем всё это и в каком виде лежат + * данные — в шапке pop_sfx.h и ../docs/sound_plan.md. + * + * ПОЧЕМУ МОДУЛЬ РЕЗИДЕНТНЫЙ (в EXTRA_SRCS, а не в банке). Три причины, + * и каждой хватило бы одной: + * - callback насоса зовётся ИЗ ПРЕРЫВАНИЯ; трамплин перед вызовом + * ставит базовую страницу W1, значит хендлер обязан быть в базе; + * - pop_sfx_play дёргается из горячих мест (play_seq на каждый шаг + * Кида, физика, боёвка) — трамплин там был бы чистой потерей; + * Файловое чтение страниц выполняет общий резидентный bank_load_file(). + * Холодная половина (аллокация страниц, cbl_open) — в pop_sfx_cold.c. + */ + +#include +#include +#include +#include +#include +#include /* IRQ_DISABLE/IRQ_ENABLE вокруг курсора */ +#include "pop_sfx.h" +#include "pop_music.h" +#include "_pop_sfx.h" + +/* Физические страницы набора; заполняет холодная половина. */ +uint8_t pop_snd_page[POP_SND_PAGES]; +uint8_t pop_snd_ok; + +/* ХОЧЕТ ЛИ ЗВУКА ПОЛЬЗОВАТЕЛЬ (Ctrl+S) — отдельно от pop_snd_ok. + * + * Разделять обязательно: pop_snd_ok значит «CBL сейчас открыт», и его + * гасит служебная пауза на время загрузки уровня. Если бы Ctrl+S правил + * только его, то первая же смена уровня позвала бы pop_sfx_start() и + * вернула звук, который пользователь выключил. Поэтому start уважает + * этот флаг, а pause его не трогает. + * + * Живёт в резиденте, а не в банке: метку в борте рисует отладочный ярлык + * каждый кадр, и тащить ради одного байта трамплин незачем. */ +uint8_t pop_snd_want; + +/* Курсор проигрывания. volatile: пишет главный цикл, читает прерывание. + * Всё в 8/16 бит — 32-битной арифметики на Z80 избегаем (см. memory + * avoid_32bit_arith_z80); поэтому и в таблице смещение уже разобрано на + * страницу и адрес в окне. */ +/* Курсор МУЗЫКИ. Лежит здесь, а не в pop_music.c, по той же причине, что + * и курсор эффектов: его читает насос из прерывания, то есть резидент. */ +volatile uint8_t pop_mus_pg; +volatile uint16_t pop_mus_ptr; +volatile uint16_t pop_mus_left; +/* Номер играющего трека в нумерации ОРИГИНАЛА: музыка участвует в тех же + * таблицах приоритета и перебиваемости, что и эффекты (см. pop_sfx_tick). */ +uint8_t pop_mus_id; + +/* Тик насоса: +1 на каждый отданный CBL блок, то есть 85,4 Гц. */ +volatile uint8_t pop_snd_tick; + +/* Размер КОЛЬЦА страниц для потоковой музыки, 0 — обычный трек целиком в + * памяти. Читает насос, ставит загрузчик (pop_music.c). */ +uint8_t pop_mus_ring; + +/* Заявка на музыкальный трек: номер в нумерации оригинала, 0 — нет заявки + * (нулевого трека не бывает). Ставит pop_sfx_play, снимает главный цикл. */ +uint8_t pop_mus_req; + +/* ХОЧЕТ ЛИ ПОЛЬЗОВАТЕЛЬ МУЗЫКУ (Settings / Ctrl+M) — отдельно от звука. + * Оригинал такого разделения не знал (там один тумблер на всё), но у нас + * музыка это ПОТОК С ДИСКА: кому мешает подкачка или просто не нужен + * саундтрек, гасит её, не теряя эффектов. */ +uint8_t pop_mus_want; + +static volatile uint8_t sfx_pg; /* индекс в pop_snd_page[] */ +static volatile uint16_t sfx_ptr; /* адрес в окне W0: 0x0000..0x3FFF */ +static volatile uint16_t sfx_left; /* сколько байт осталось (кратно 128) */ + +/* ---- насос ---------------------------------------------------------- * + * Зовётся из ISR CBL раз в ~11,7 мс за очередными 128 байтами. + * + * ОКНО БЕРЁМ ВЗАЙМЫ У W3, А НЕ У W0 — и это принципиально. + * + * Раньше насос занимал W0. Оказалось, что так звук рвёт ЛЮБОЙ вызов + * BIOS: `rst 8` — это `out ($7C),a`, который включает системное ПЗУ и + * перестраивает окно 0 (в MAME это update_memory: m_pages[0] + + * m_bank_view0.select). ПЗУ ложится ПОВЕРХ страничного регистра, поэтому + * запись в порт 0x82 из прерывания ничего не даёт — OTIR честно + * вычитывает ПЗУ и отдаёт его в CBL. На слух это скрежет; поймано на + * затемнении в PoP (2026-08-25), причём одинаково при чтении палитры, + * записи палитры и вообще любой функции BIOS. + * + * W3 таким свойством не обладает: он управляется ТОЛЬКО портом 0xE2, и + * подмену из прерывания никто не перекрывает. BIOS тоже двигает W3 + * (LP_OPEN_PG, PIC_SET_PAL), но во время нашего ISR он не исполняется — + * мы возвращаем окно до выхода, и для него подмена невидима. Ровно так + * же незаметна она и для фона с accel-блитом: их DI-банды прерывание не + * рвёт, а между бандами W3 всегда восстановлен. + * + * Идея пользователя, 2026-08-25. Тот же приём заимствования окна, что у + * трамплина с W1 (_irq_tramp.c). + * + * Заплатки ISR-стаба в 0x38/0x66 нашим страницам НЕ нужны (в отличие от + * страницы уровня, pop_level_read_file): пока страница замаплена, мы + * внутри прерывания и IFF сброшен — прерваться некому. + * + * ТИШИНУ ЛЬЁМ СВОЮ. Когда эффект кончился, отдавать в CBL всё равно надо: + * иначе железо крутит по кругу хвост своего буфера и это слышно как + * жужжание. Готовый блок 0x80 лежит первым в наборе (упаковщик), поэтому + * libc-шный CBL_UNDERRUN_SILENCE с его malloc нам не нужен — а куча в + * тесном резиденте W2 отказала бы и уронила весь звук. */ +int pop_sfx_fill(uint16_t n) +{ + uint8_t saved, phys; + uint16_t ptr; + + /* ЧАСЫ СЦЕН. Насос зовётся строго по расходу буфера CBL — 85,4 раза в + * секунду, что бы ни делал главный цикл. Это единственный источник времени, + * которому не мешают ни долгие блиты, ни чтение с диска: кадровые + * прерывания теряются в di-окнах акселератора (pop_pace.h), а счёт + * ожиданий vsync считает НАШИ ожидания, а не прошедшее время. + * Катсцены пейсятся по нему — иначе картинка уезжает от музыки. */ + pop_snd_tick++; + + /* Три источника по убыванию важности: эффект, музыка, тишина. Эффект + * перебивает музыку — как у оригинала, где play_sound глушит всё, что + * звучало; голос-то один. Музыка при этом НЕ теряет позицию: её + * курсор стоит, пока эффект доигрывает, и она продолжается с места. */ + if (sfx_left) { + phys = pop_snd_page[sfx_pg]; ptr = sfx_ptr; + } else if (pop_mus_left) { + phys = pop_mus_page[pop_mus_pg]; ptr = pop_mus_ptr; + } else { + phys = pop_snd_page[POP_SND_SILENCE_PAGE]; + ptr = POP_SND_SILENCE_OFF; + } + /* ОКНО БЕРЁМ ВЗАЙМЫ У W3, А НЕ У W0 — см. шапку файла. */ + saved = _io_page_w3; + _io_page_w3 = phys; + cbl_push_otir((const void *)(0xC000u + ptr), n); + _io_page_w3 = saved; + if (!sfx_left) { + if (!pop_mus_left) return 1; /* тишина — курсор не двигаем */ + /* Длину трека считаем ПОРЦИЯМИ насоса, а не байтами: 169 КБ в + * uint16 не влезает, а 1350 блоков — легко (32-бит арифметики на + * Z80 избегаем, тем более в прерывании). */ + pop_mus_left--; + pop_mus_ptr += n; + if (pop_mus_ptr >= 0x4000) { + pop_mus_ptr = 0; + /* КОЛЬЦО. Финальная тема — 1,2 МБ, в память целиком не влезает + * никак, поэтому она играется из нескольких страниц по кругу, а + * загрузчик дочитывает в те, что мы уже прошли (pop_music.c). + * Конец трека определяет left, а не номер страницы. */ + if (pop_mus_ring) { + if ((uint8_t)(pop_mus_pg + 1) < pop_mus_ring) pop_mus_pg++; + else pop_mus_pg = 0; + } else if ((uint8_t)(pop_mus_pg + 1) < POP_MUS_PAGES) { + pop_mus_pg++; + } else { + pop_mus_left = 0; + } + } + return 1; + } + + sfx_ptr += n; + if (sfx_ptr >= 0x4000) { /* граница страницы — ровно по блоку */ + sfx_ptr = 0; + /* Переход на следующую страницу нужен ровно одному эффекту — тому, + * что сам ДЛИННЕЕ страницы (упаковщик с 2026-08-25 кладёт остальные + * целиком в одну). Гард обязателен: без него ошибка в длине увела + * бы насос за pop_snd_page[] и он читал бы чужую память — на слух + * это «играет не тот звук», причём каждый раз другой. */ + if ((uint8_t)(sfx_pg + 1) < POP_SND_PAGES) sfx_pg++; + else sfx_left = n; /* обрываем */ + } + sfx_left -= n; + return 1; +} + +/* ---- ОЧЕРЕДЬ И ПРИОРИТЕТЫ (порт seg000:12C5 + seg000:1304) ------------ * + * + * Оригинал НЕ перебивает играющий звук чем попало, и это слышно. Модель + * ровно такая: + * + * play_sound(id) — только НОМИНИРУЕТ кандидата на кадр; из нескольких + * за кадр остаётся ВАЖНЕЙШИЙ (меньше prio = важнее, + * при равенстве побеждает последний — сравнение <=); + * play_next_sound() — раз в кадр решает, запускать ли: можно, если ничего + * не играет, ЛИБО текущий звук помечен «перебиваемым» + * И новый не менее важен. Иначе номинант ВЫБРАСЫВАЕТСЯ + * (next_sound = -1 безусловно), а не встаёт в очередь. + * + * Отсюда то, что слышно в игре: `gate_closing_fast` (6) неперебиваем и + * доигрывает до звонкого конца, а приземление Кида в этот момент пропадает + * совсем; челюсти (47, prio 0x10) всегда важнее решётки (4, prio 0x32), и + * решётка звучит только в паузах между укусами. + * + * Таблицы — из SDLPoP (sound_prio_table seg000:1528, sound_interruptible + * data.h:433) с правками fix_sound_priorities(): в config.h SDLPoP + * FIX_SOUND_PRIORITIES определён безусловно, то есть сравниваем мы себя + * именно с исправленным вариантом (10 -> 0x0D, 48 -> 0x15, 49 перебиваем). + */ + +/* Приоритет: МЕНЬШЕ значит ВАЖНЕЕ. Музыкальные id тут тоже есть — они + * никогда не номинируются (нет оцифровки), но индексация должна совпадать + * с оригиналом, иначе таблицу не сверить глазами. */ +static const uint8_t snd_prio[POP_SND_COUNT] = { + 0x14, 0x1E, 0x23, 0x66, 0x32, 0x37, 0x30, 0x30, 0x4B, 0x50, /* 0.. 9 */ + 0x0D, 0x12, 0x0C, 0x0B, 0x69, 0x6E, 0x73, 0x78, 0x7D, 0x82, /* 10..19 */ + 0x91, 0x96, 0x9B, 0xA0, 0x01, 0x01, 0x01, 0x01, 0x01, 0x13, /* 20..29 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, /* 30..39 */ + 0x01, 0x01, 0x01, 0x01, 0x87, 0x8C, 0x0F, 0x10, 0x15, 0x16, /* 40..49 */ + 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01 /* 50..56 */ +}; + +/* «Играющий звук можно перебить» — битовая карта (бит id&7 байта id>>3): + * 57 флагов уложены в 8 байт, потому что в резиденте W1/W2 каждая сотня + * байт на счету, а читаем мы их раз в кадр. */ +static const uint8_t snd_intr[8] = { + /* 0.. 7 */ (uint8_t)(0<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<4 | 1<<5 | 0<<6 | 1<<7), + /* 8..15 */ (uint8_t)(1<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<4 | 1<<5 | 0<<6 | 0<<7), + /*16..23 */ (uint8_t)(1<<0 | 1<<1 | 0<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7), + /*24..31 */ (uint8_t)(0<<0 | 0<<1 | 0<<2 | 0<<3 | 0<<4 | 1<<5 | 0<<6 | 0<<7), + /*32..39 */ 0, + /*40..47 */ (uint8_t)(0<<0 | 0<<1 | 0<<2 | 0<<3 | 0<<4 | 0<<5 | 0<<6 | 1<<7), + /*48..55 */ 0, + /*56.. */ 0 +}; + +/* Створка двери уровня (15) — единственная запись, которую оригинал правит + * НА ХОДУ (seg007:442/464): при закрытии её перебивать можно, при открытии + * нельзя (иначе съезд створки обрывается и «конца» не слышно). Держим + * отдельным байтом, чтобы таблица осталась в ПЗУ. */ +uint8_t pop_sfx_slide_intr; + +/* Номинант кадра, ЗАПИСАННЫЙ КАК id+1: ноль значит «никого». Так сделано + * ради crt0 — он зануляет _DATA, а «нет номинанта» у оригинала это -1; + * со смещением на единицу начальное состояние получается даром. */ +static uint8_t snd_next1; +static uint8_t snd_curr; /* что играет — для prio/intr */ + +static uint8_t snd_interruptible(uint8_t id) +{ + if (id == 15) return pop_sfx_slide_intr; + return (uint8_t)(snd_intr[id >> 3] & (uint8_t)(1 << (id & 7))); +} + +/* ---- запуск / останов ------------------------------------------------ */ + +void pop_sfx_play(uint8_t id) +{ + if (!pop_snd_ok || id >= POP_SND_COUNT) return; + /* НЕТ ОЦИФРОВКИ — ЗНАЧИТ ЭТО МУЗЫКА. В оригинале звуки 24..43 играет + * Adlib по MIDI, и в digisnd их нет вовсе; у нас на их месте записи + * DOS-версии, которые надо читать с диска. В прерывании и в горячем + * коде этого делать нельзя, поэтому здесь только ЗАЯВКА одним байтом — + * её разбирает главный цикл (pop_music_stream, банк 9). + * В конкурсе эффектов такой id не участвует: он не глушит живой звук + * (порт проверки `if (NULL == sound_pointers[id]) return;`). */ + if (pop_snd_tbl[id].len == 0) { + /* Заявку принимаем ТОЛЬКО в музыкальном диапазоне оригинала + * (24..43 — Adlib-треки игры). Пустых слотов в наборе больше нет, + * но случайный мусорный id обязан молчать, а не запускать трек на + * минуту: ровно так проявился sdcc_z80_cmp_store_a_bug в звуке + * тряски — разность двух номеров попала в 27 и заиграла музыка. */ + if (pop_mus_want && id >= 24 && id <= 43) pop_mus_req = id; + return; + } + if (!snd_next1 || snd_prio[id] <= snd_prio[snd_next1 - 1]) + snd_next1 = (uint8_t)(id + 1); +} + +void pop_sfx_tick(void) +{ + uint8_t id, pg; + uint16_t off, len; + if (!snd_next1) return; + id = (uint8_t)(snd_next1 - 1); + snd_next1 = 0; /* номинант живёт ровно кадр */ + /* ЧТО СЕЙЧАС ЗВУЧИТ — эффект либо музыка. У оригинала это один и тот + * же вопрос: check_sound_playing() отвечает «играет хоть что-то», без + * различия типа, а музыкальные id лежат в тех же таблицах приоритета и + * перебиваемости (seg000:1672 + data.h:433). Почти вся музыка помечена + * НЕперебиваемой, поэтому пока она играет, эффект просто не стартует — + * ровно это слышно в оригинале. Исключение одно: 29 meet_Jaffar. */ + { + uint8_t cur = sfx_left ? snd_curr : pop_mus_id; + if ((sfx_left != 0 || pop_mus_left != 0) && + (!snd_interruptible(cur) || snd_prio[id] > snd_prio[cur])) + return; /* ...и уступать не обязано */ + } + snd_curr = id; + pg = pop_snd_tbl[id].page; + off = pop_snd_tbl[id].off; + len = pop_snd_tbl[id].len; + /* Курсор из трёх полей, а читает его прерывание — обновляем под DI, + * иначе насос может застать наполовину обновлённое состояние и уехать + * в чужие данные. */ + IRQ_DISABLE(); + sfx_pg = pg; + sfx_ptr = off; + sfx_left = len; + IRQ_ENABLE(); +} + +void pop_sfx_stop(void) +{ + IRQ_DISABLE(); + sfx_left = 0; + IRQ_ENABLE(); +} diff --git a/applications/SprPoP/src/pop_sfx.h b/applications/SprPoP/src/pop_sfx.h new file mode 100644 index 0000000..42adc10 --- /dev/null +++ b/applications/SprPoP/src/pop_sfx.h @@ -0,0 +1,97 @@ +/* + * pop_sfx.h — звуковые ЭФФЕКТЫ PoP через COVOX-Blaster. + * + * Данные — оригинальная оцифровка игры (`digisnd1..3.dat`), приведённая + * упаковщиком к 10 937,5 Гц. Конвертировать в рантайме нечего: у PoP + * сэмплы 8 бит БЕЗЗНАКОВЫЕ МОНО, и это ровно формат CBL (см. и + * ../docs/sound_plan.md). + * + * ОДИН ГОЛОС. Оригинал тоже одноголосый: `play_sound` глушит + * предыдущий звук (`stop_sounds`, seg009.c), поэтому новый эффект просто + * перебивает играющий, и никакого микшера не нужно. + * + * ПОЧЕМУ ЭТО НЕ ЛОМАЕТ КАДР. Насос живёт в прерывании CBL и отдаёт 128 + * байт раз в ~11,7 мс. Кадровые прерывания при этом теряются (ветка CBL + * в трамплине делает приватный RETI), но нам всё равно: темп кадра + * считается ПО ЛУЧУ (pop_pace.h), а не по прерываниям. Если бы пейсинг + * остался на них — звук сломал бы игру. + */ +#ifndef POP_SFX_H +#define POP_SFX_H + +#include + +/* Поднять набор: занять 8 EMM-страниц и вычитать в них данные. ВЫВОД НЕ + * ВКЛЮЧАЕТ — см. pop_sfx_start. 0 — OK, -1 — не получилось (тогда всё + * дальнейшее молча ничего не делает, игра идёт без звука). */ +int pop_sfx_init(void) __banked; + +/* Включить вывод (открыть CBL). Звать ПОСЛЕ всей загрузки: пока идёт + * чтение файлов, ESTEX уходит в диск надолго, насос не успевает долить + * блок, и железо крутит хвост своего буфера — на слух сплошной скрежет. + * Идемпотентно. */ +int pop_sfx_start(void) __banked; + +/* Заглушить вывод на время загрузки (закрывает CBL). Парная к start. */ +void pop_sfx_pause(void) __banked; + +/* Закрыть CBL и отпустить страницы; звать на выходе. */ +void pop_sfx_close(void) __banked; + +/* Вкл/выкл звук по воле пользователя (Ctrl+S, порт SDLPoP seg000:657). + * Правит pop_snd_want и приводит вывод в соответствие. */ +void pop_sfx_toggle(void) __banked; + +/* ХОЧЕТ ЛИ ЗВУКА ПОЛЬЗОВАТЕЛЬ. Не то же самое, что «CBL открыт»: вывод + * ещё гасит служебная пауза на время загрузки уровня. Читать для + * индикации; писать — только через pop_sfx_toggle. 0 и в случае, когда + * набор вообще не поднялся (нет файлов, нет страниц) — то есть флаг + * честно means «звука не будет». */ +extern uint8_t pop_snd_want; + +/* ЧАСЫ ДЛЯ СЦЕН: +1 на каждый блок, отданный CBL, то есть ровно 85,4 Гц + * (10 937,5 / 128), пока звук открыт. Идут от расхода буфера железом, а + * не от нашего кода, поэтому не врут ни на долгих блитах, ни на чтении с + * диска — в отличие от счёта ожиданий vsync (тот считает НАШИ ожидания) и + * от кадровых прерываний (те теряются в di-окнах акселератора, pop_pace.h). + * Переполняется каждые 3 с — разности брать в uint8_t и накапливать. + * Когда CBL закрыт (звук выключён), НЕ ИДУТ: у пейсинга обязан быть + * запасной путь по кадрам луча. */ +extern volatile uint8_t pop_snd_tick; + +/* ЗАЯВКА НА МУЗЫКУ. pop_sfx_play(id) для звука без оцифровки (24..43 — + * в оригинале это Adlib-музыка) кладёт сюда номер трека, а разбирает + * заявку главный цикл: чтение с диска ни в прерывании, ни в горячем коде + * недопустимо. 0 — заявки нет. Снимать обязан тот, кто обработал. */ +extern uint8_t pop_mus_req; + +/* Тумблер МУЗЫКИ (Settings / Ctrl+M), независимый от тумблера эффектов: + * музыка у нас — поток с диска, и выключают её по другим причинам. + * 0 — заявки не принимаются, играющий трек глушится (pop_music_service). */ +extern uint8_t pop_mus_want; + +/* Заявить эффект с номером id (0..56 — нумерация оригинала, см. + * sound_plan.md). НЕ запускает его немедленно и НЕ перебивает играющий: + * это порт play_sound (seg000:12C5), который лишь НОМИНИРУЕТ кандидата на + * текущий кадр. Из нескольких заявок за кадр выживает важнейшая, а решение + * «запускать или нет» принимает pop_sfx_tick. Звуки без оцифровки (музыка) + * игнорируются. + * + * НЕ банковая и НЕ __banked: зовётся из горячих мест (play_seq в + * pop_kid.c, физика, боёвка), и трамплин на каждый шаг Кида не нужен. */ +void pop_sfx_play(uint8_t id); + +/* Раз в кадр: запустить номинанта, если можно (порт play_next_sound, + * seg000:1304). Оригинал зовёт его в конце отрисовки кадра — зовём там же. + * Не запустили — номинант ВЫБРАСЫВАЕТСЯ, очереди у оригинала нет. */ +void pop_sfx_tick(void); + +/* Перебиваемость съезжающей створки двери уровня (звук 15) — единственная + * запись таблицы, которую оригинал правит на ходу (seg007:442/464): + * 1 при закрытии двери, 0 при открытии. */ +extern uint8_t pop_sfx_slide_intr; + +/* Оборвать текущий эффект (порт stop_sounds). */ +void pop_sfx_stop(void); + +#endif diff --git a/applications/SprPoP/src/pop_sfx_cold.c b/applications/SprPoP/src/pop_sfx_cold.c new file mode 100644 index 0000000..425a8e4 --- /dev/null +++ b/applications/SprPoP/src/pop_sfx_cold.c @@ -0,0 +1,114 @@ +/* + * pop_sfx_cold.c — ХОЛОДНАЯ половина звука: поднять набор и открыть CBL. + * Отрабатывает один раз за запуск, поэтому живёт в банке 8; горячая + * половина (насос, запуск эффекта) — в резиденте, см. pop_sfx.c. + */ + +#include +#include +#include +#include "pop_sfx.h" +#include "pop_arc.h" +#define POP_SND_FILES /* имена файлов нужны ТОЛЬКО здесь, см. pop_sound_tbl.h */ +#include "_pop_sfx.h" + +static uint8_t snd_blk; /* блок EMM под весь набор */ + +int pop_sfx_init(void) __banked +{ + uint8_t i, blk; + + if (pop_snd_ok) return 0; + + /* Одним блоком: страницы блока идут подряд по логическому индексу, и + * таблица звуков адресует их именно так (page = смещение >> 14). + * + * Из АРХИВА, а не десятью файлами: `open` стоит 51,4 мс против 32,6 мс + * на чтение 16 КБ (замер 2026-08-25), то есть девять лишних открытий — + * это почти полсекунды на ровном месте. Таблица живёт на стеке, см. + * pop_arc.h. */ + { + pop_arc_t arc; + uint8_t tbl[POP_SND_PAGES * POP_ARC_REC]; + + if (pop_arc_open_id(&arc, POP_ARC_SND, tbl, POP_SND_PAGES) != + POP_SND_PAGES) + return -1; + blk = mem_alloc_pages(POP_SND_PAGES); + if (!blk) { pop_arc_close(&arc); return -1; } + for (i = 0; i < POP_SND_PAGES; i++) { + pop_snd_page[i] = mem_get_page(blk, i); + if (!pop_snd_page[i] || + pop_arc_read(&arc, tbl, i, 0, pop_snd_page[i], 0, 0) != 16384) { + pop_arc_close(&arc); + mem_free_block(blk); + return -1; + } + } + pop_arc_close(&arc); + } + snd_blk = blk; + pop_snd_want = 1; /* набор есть — звук по умолчанию включён */ + return 0; +} + +/* ЗАПУСТИТЬ ВЫВОД — ОТДЕЛЬНО ОТ ЗАГРУЗКИ, и это не косметика. + * + * Пока идёт чтение файлов, CBL держать открытым НЕЛЬЗЯ: ESTEX уходит в + * диск надолго, насос не успевает долить очередные 128 байт, и железо + * крутит по кругу хвост своего 256-байтового буфера. На слух это + * сплошной скрежет (поймано пользователем на старте, 2026-08-20). + * Поэтому загрузка страниц и открытие CBL разведены: грузим молча, + * включаем звук последним действием, а на время загрузки уровня глушим + * (pop_sfx_pause). + * + * Параметры: 10 937,5 Гц, 8 бит моно — формат данных один в один + * (sound_plan.md). OTIR, а не акселератор: тот нужен только для 16 бит. + * cbl_open, а не cbl_open_silence: тишину насос льёт СВОЮ (первый блок + * набора), и просить у libc буфер через malloc не надо — куча в резиденте + * W2 тесная, и её отказ ронял бы весь звук. Заодно так malloc вообще не + * приезжает в резидент (275 Б, см. libc/cbl/_cbl_open_raw.c). */ +int pop_sfx_start(void) __banked +{ + if (pop_snd_ok) return 0; + if (!pop_snd_want) return 0; /* выключено пользователем (Ctrl+S) */ + if (!snd_blk) return -1; /* набор не загружен */ + if (cbl_open(CBL_FREQ_10K9, CBL_FMT_MONO8, CBL_PUMP_OTIR, + pop_sfx_fill) != 0) + return -1; + pop_snd_ok = 1; + return 0; +} + +void pop_sfx_pause(void) __banked +{ + if (!pop_snd_ok) return; + pop_sfx_stop(); + cbl_close(); + pop_snd_ok = 0; +} + +void pop_sfx_close(void) __banked +{ + pop_sfx_pause(); + if (snd_blk) { mem_free_block(snd_blk); snd_blk = 0; } +} + +/* Ctrl+S — порт `turn_sound_on_off((!is_sound_on) * 15)` из SDLPoP + * (seg000:657). Выключение реально ЗАКРЫВАЕТ CBL, а не глушит сэмпл: + * иначе насос продолжал бы отдавать блоки тишины 85 раз в секунду, а это + * 3,3 % процессорного времени ни за что (замер: 8 001 такт на прерывание + * при периоде 245 759). Открытие обратно дешёвое — cbl_open только + * настраивает железо, страницы набора никуда не девались. */ +void pop_sfx_toggle(void) __banked +{ + if (pop_snd_want) { + pop_sfx_pause(); /* pause сам не трогает want... */ + pop_snd_want = 0; /* ...поэтому гасим его следом */ + } else { + pop_snd_want = 1; + /* Набора может не быть вовсе (файлы не прочитались, страницы не + * дались) — тогда откатываем флаг, чтобы индикация не врала. */ + if (pop_sfx_start() != 0) pop_snd_want = 0; + } +} diff --git a/applications/SprPoP/src/pop_shadow.c b/applications/SprPoP/src/pop_shadow.c new file mode 100644 index 0000000..721dcb2 --- /dev/null +++ b/applications/SprPoP/src/pop_shadow.c @@ -0,0 +1,105 @@ +/* + * pop_shadow.c — загрузка запечённого набора спрайтов Тени. Зачем набор + * отдельный и почему половин две — в шапке pop_shadow.h. + * + * Код ХОЛОДНЫЙ (раз на уровень), поэтому живёт в банке 8 вместе с прочей + * загрузкой. Сами atlas_t — в W2: их читает отрисовка из банка 4, а + * данные видны из любого банка. + */ + +#include +#include +#include +#include "pop_arc.h" +#include "shadow_arc.h" +#include "pop_shadow.h" +#include "pop_shadow_atlas.h" + +atlas_t pop_shadow_kid[POP_SHADOW_KID_PAGES]; +atlas_t pop_shadow_fgt[POP_SHADOW_FGT_PAGES]; +uint8_t pop_shadow_kid_n, pop_shadow_fgt_n; + +/* ГРУЗИМ ОДИН РАЗ НА ЗАПУСК, а не по уровням. + * + * Тень бывает на ЧЕТЫРЁХ уровнях: 4 — рождается прыжком сквозь зеркало + * (jump_through_mirror, seg003:0811, ставит Char.charid = charid_1_shadow + * прямо из Кида), 5 — крадёт зелье, 6 — прыгает через пропасть, 12 — + * дерётся. Читается как три, потому что таблиц init_shad_N всего три + * (5/6/12): четвёртой нет — на 4-м Тень копируется с Кида, а не поднимается + * из таблицы. Так вот, напрашивается + * грузить набор только там, как это сделано с перевёрнутыми наборами + * (pop_vflip_load_all зовётся лишь на POP_UPSIDE_LEVEL). Решение + * пользователя — грузить сразу, и оно правильное по двум причинам: + * страниц EMM с запасом (32 из ~215 свободных), а вот забыть перезагрузку + * на границе уровня легко — ровно так и появился BUG-SHADOW-SET. Разовая + * загрузка убирает целый класс ошибок и заодно не тормозит переход между + * уровнями на 32 файла. */ +int pop_shadow_load(void) __banked +{ + uint8_t i; + if (pop_shadow_kid_n || pop_shadow_fgt_n) return 0; /* уже загружен */ + + { + uint8_t tbl[ARC_SHADOW_COUNT * POP_ARC_REC]; + pop_arc_t arc; + + /* Тридцать два атласа — из одного архива: раньше это были тридцать + * два `open` по 51,4 мс, то есть полторы секунды на ровном месте. */ + if (pop_arc_open_id(&arc, POP_ARC_SHADOW, tbl, + ARC_SHADOW_COUNT) < 0) + return -1; + for (i = 0; i < POP_SHADOW_KID_PAGES; i++) { + if (pop_arc_atlas(&arc, tbl, (uint8_t)(ARC_SHADOW_SK0 + i), + &pop_shadow_kid[i]) != 0) { + pop_arc_close(&arc); + pop_shadow_free(); + return -1; + } + pop_shadow_kid_n = (uint8_t)(i + 1); + } + for (i = 0; i < POP_SHADOW_FGT_PAGES; i++) { + if (pop_arc_atlas(&arc, tbl, (uint8_t)(ARC_SHADOW_SF0 + i), + &pop_shadow_fgt[i]) != 0) { + pop_arc_close(&arc); + pop_shadow_free(); + return -1; + } + pop_shadow_fgt_n = (uint8_t)(i + 1); + } + pop_arc_close(&arc); + } + + pop_shadow_pal_apply(); + return 0; +} + +/* ПАЛИТРА ОТДЕЛЬНО ОТ ЗАГРУЗКИ, и это не косметика. + * + * `gfx_pal_fload("KID\\kid.pal")` заливает ВСЕ 256 записей — это единая + * игровая палитра, — и затирает любые слоты, выставленные до него. Ровно + * та же беда уже была с тайлсетом: сразу за ним стоит pop_bg_pal_apply, + * который возвращает цвета env/wall. Наши 0xA0..0xAF из той же оперы, и + * в первом заходе я на это наступил: набор грузился, силуэт рисовался + * правильной формы, но ЦЕЛИКОМ ЧЁРНЫЙ (поймано пользователем на 6-м + * уровне). Поэтому вызов живёт рядом с pop_bg_pal_apply, а не внутри + * загрузки атласов. + * + * Обе страницы дабл-буфера: у них свои палитры. Через буфер в W2 — + * gfx_pal_load отдаёт указатель BIOS ($A4), а тот читает только + * #4000-#BFFF и банковую rodata не увидит. */ +void pop_shadow_pal_apply(void) __banked +{ + uint8_t buf[64], k; + if (!pop_shadow_kid_n) return; /* набора нет — нечего красить */ + for (k = 0; k < 64; k++) buf[k] = pop_shadow_pal[k]; + gfx_pal_load(0, POP_SHADOW_PAL_BASE, 16, buf); + gfx_pal_load(1, POP_SHADOW_PAL_BASE, 16, buf); +} + +void pop_shadow_free(void) __banked +{ + uint8_t i; + for (i = 0; i < pop_shadow_kid_n; i++) atlas_free(&pop_shadow_kid[i]); + for (i = 0; i < pop_shadow_fgt_n; i++) atlas_free(&pop_shadow_fgt[i]); + pop_shadow_kid_n = pop_shadow_fgt_n = 0; +} diff --git a/applications/SprPoP/src/pop_shadow.h b/applications/SprPoP/src/pop_shadow.h new file mode 100644 index 0000000..7463e2f --- /dev/null +++ b/applications/SprPoP/src/pop_shadow.h @@ -0,0 +1,55 @@ +/* + * pop_shadow.h — запечённый набор спрайтов ТЕНИ. + * + * Оригинал спрайтов Тени не хранит: он кладёт обычный спрайт дважды — в x + * прозрачным блитом и в x+1 «блиттером XOR» (draw_objtable_item, + * seg008.c:1600). Пакетный блит так не умеет, поэтому результат запечён + * заранее (toolchain/pop_pack_shadow.py); разбор и замеры — + * ../docs/shadow_atlas_plan.md. + * + * ПОЛОВИН ДВЕ, и это не наша выдумка. Набор спрайтов у оригинала выбирает + * не персонаж, а поле `sword` САМОГО КАДРА (load_frame_to_obj, + * seg008.c:1752: chtab_base жёстко = chtab_2, плюс `sword >> 6`). У всех + * кадров таблицы Кида эти биты нулевые, у всех кадров таблицы стража — + * 0xC0. Тень берёт таблицу стража для кадров 150..189 (seg006.c:533), + * значит идёт через chtab_5 — а chtab_5 на 12-м уровне грузится из + * SHADOW.DAT (tbl_guard_type[12] == 4), и это графика КИДА в боевых позах. + * Отсюда: + * + * pop_shadow_kid[] — кадры вне боя (запечены из спрайтов Кида); + * pop_shadow_fgt[] — кадры 150..189 (запечены из SHADOW.DAT). + * + * Какая половина нужна, отрисовка уже знает — тем же условием, что + * выбирает таблицу кадров (pop_frame_tbl_is_guard). + * + * СПРАЙТ ТЕНИ НА ПИКСЕЛЬ ШИРЕ исходного: второй проход оригинала кладётся + * в x+1, суммарный след занимает [x, x+w]. Рисовать по тем же + * координатам, что и Кида — сдвиг уже внутри спрайта. + */ +#ifndef POP_SHADOW_H +#define POP_SHADOW_H + +#include +#include + +extern atlas_t pop_shadow_kid[]; /* половина «вне боя» */ +extern atlas_t pop_shadow_fgt[]; /* половина «в бою» (SHADOW.DAT) */ +/* Сколько страниц реально загружено; 0 = набора нет (уровень без Тени + * или загрузка не удалась) — отрисовка тогда идёт прежним путём. */ +extern uint8_t pop_shadow_kid_n, pop_shadow_fgt_n; + +/* Загрузить набор. Звать ОДИН РАЗ при старте: набор не зависит от + * уровня, а перезагрузка на границе — лишний повод её забыть (почему + * именно так — в шапке pop_shadow.c). Повторный вызов — no-op. + * Возврат: 0 — OK, -1 — ошибка загрузки. */ +int pop_shadow_load(void) __banked; + +/* Залить 16 слотов палитры Тени. ЗВАТЬ ПОСЛЕ ЛЮБОЙ заливки общей + * палитры (gfx_pal_fload kid.pal): та кладёт все 256 записей и затирает + * наши — см. развёрнуто в pop_shadow.c. Рядом с pop_bg_pal_apply. */ +void pop_shadow_pal_apply(void) __banked; + +/* Освободить страницы; звать на выходе из программы. */ +void pop_shadow_free(void) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_state.c b/applications/SprPoP/src/pop_state.c new file mode 100644 index 0000000..4304f34 --- /dev/null +++ b/applications/SprPoP/src/pop_state.c @@ -0,0 +1,197 @@ +/* + * pop_state.c — данные, которые читают ОБЕ стороны банковой границы. + * + * Зачем отдельный модуль: sprinter-cc собирает банковый модуль с + * --dataseg BANKn, поэтому ВСЯ его писучая память (глобалы, file-static, + * статики внутри функций) линкуется В СТРАНИЦУ БАНКА и снаружи не читается + * — проверено на .map (2026-07-30: глобал банкового модуля лёг по + * 0x0001C000, а не в _DATA). Значит любую переменную, к которой нужен + * доступ и из банка, и снаружи, надо держать в НЕбанковом модуле — здесь. + * + * Модуль обязан оставаться в W1/W2: его читают и главный цикл, и банки. + */ +#include "pop_state.h" +#include "pop_ctrl.h" /* объявления шины control_* (живёт здесь) */ + +uint8_t pop_mob_busy; +uint8_t pop_loose_landed; +/* Кусок loose УШЁЛ ВНИЗ из комнаты (порт хвоста move_loose, seg007:1126: + * mob_down_a_row переносит его в комнату снизу; у нас симуляция там не + * продолжается, и приземление разбирает главный цикл). 0 = нет, иначе + * col+1; комната, ИЗ которой он ушёл, — в pop_loose_exit_room. + * Сигнал отдельный от pop_loose_fell: тот взводится в момент ОТРЫВА плиты + * (там верен только remove_loose), и по нему кнопка внизу нажималась, + * пока кусок ещё летел. */ +uint8_t pop_loose_exit; +uint8_t pop_loose_exit_room; +uint8_t pop_droppedout; +/* leveldoor_open (seg007:456): дверь уровня доехала до верха. Ставит + * анимация двери (pop_trob, банк 6), читает спецсобытие подъёма скелета + * (guards.c, банк 1) — снова случай «данные через границу банков». + * Сбрасывается стартом уровня (seg003:97). + * + * ШИРИНА — word, как в оригинале (data.h:361), а не byte: на уровне 8 + * do_timers СЧИТАЕТ этой же переменной время до появления мыши + * (`++leveldoor_open` каждый кадр, пока Кид в комнате 16), и байт + * переполнился бы через 255 кадров, обнулив признак «дверь открыта». */ +uint16_t pop_leveldoor_open; + +/* ---- Шина управления (seg006 globals) ------------------------------ * + * Объявлены в pop_ctrl.h, а ЛЕЖАТ здесь: к ним обращаются ТРИ банка — + * pop_ctrl (банк 5) пишет с клавиатуры, guards.c (банк 1) подаёт + * синтетический ввод ИИ (autocontrol_*, seg002), pop_map (банк 3) читает + * через pop_ctrl_shift_held. Сегодня оно уцелело бы и в pop_ctrl.c + * (собираем без --bank-data, данные банков остаются в _DATA), но это + * флаг сборки: включат его — и шина уедет в страницу банка 5. + * + * control_x/y/shift — «сырое» состояние осей ЭТОГО кадра (абсолютно: + * X LEFT/-1..RIGHT/+1); *_forward/backward/up/down/shift2 — конечный + * автомат held/released/ignore (авто-повтор). */ +int8_t control_x, control_y, control_shift; +int8_t control_forward, control_backward, control_up, control_down, control_shift2; + +/* Чит «бессмертие» (клавиша I). Отладочный: Кид получает анимацию удара, + * но HP не теряет — иначе проверять боёвку приходится с трёх попаданий. */ +uint8_t pop_immortal; + +/* Габариты последней нарисованной двери уровня (порт leveldoor_right / + * leveldoor_ybottom): пишет draw_leveldoor из банка pop_bg, читает + * clip_char из pop_map. См. pop_state.h. */ +int pop_leveldoor_right; +int pop_leveldoor_ybottom; + +/* ---- Отладочная «пустышка» для брейкпоинтов из БАНКОВ ---------------- * + * Зачем. PC-брейкпоинт видит только логический адрес, а 0xC000+ — это + * окно, куда мапятся ВСЕ банки: точка на адресе банковой функции ловит + * заодно чужой код, случайно легший по тому же смещению (проверено: + * точка на start_fall из банка 3 срабатывала на каждом кадре — попадала + * в pop_bg из банка 2). Условные брейкпоинты этот отладчик MAME не + * поддерживает («error in assignment expression» на `==`). + * + * Приём (идея пользователя, 2026-08-04): позвать ЭТУ функцию ровно из + * того места банкового кода, которое отлаживаем, и поставить брейкпоинт + * на неё — она резидентна в W1, её адрес однозначен. По возврату из неё + * читаем что нужно. Условие «когда именно ловить» пишется обычным `if` + * в C — это гибче любых выражений отладчика. + * + * Счётчик нужен, чтобы вызов не выкинул оптимизатор; заодно видно, сколько + * раз точка прошла, если ловим не останавливаясь. */ +uint8_t pop_dbg_hits; + +void pop_dbg_trap(void) { pop_dbg_hits++; } + +/* ВРЕМЕННО: трасса kidobj — порт вывода, который пользователь добавил в + * SDLPoP add_kid_to_objtable (seg008:1679), чтобы сверить поведение у шва + * кадр в кадр. Буфер РЕЗИДЕНТНЫЙ, потому что читает его брейкпоинт MAME: + * printf из действия точки берёт байты по фиксированному адресу. Поля — + * ровно те же и в том же порядке, плюс наша комната девятым (у оригинала + * смена комнаты видна по скачку tilepos, а нам удобнее явно). */ +int8_t pop_dbg_obj[9]; + +/* ВРЕМЕННО (разбор оверлея падающего куска): по слоту mob. + * pop_dbg_pass[i] — что было в проходе отрисовки: + * bit0 рассмотрен, bit1 active, bit2 draw_y задан, bit3 defer совпал, + * bit4 нарисован, bit5 позван mob_overlay_neighbour. + * pop_dbg_ovl[i*4 + 0..3] — что решил mob_overlay_neighbour: + * [0] r+128, [1] rt+128, [2] флаги, [3] col. + * Флаги: bit0 вошли, bit1 c<=9, bit2 tile(r,c), bit3 габарит r, + * bit4 ВЫЗОВ по r, bit5 tile(rt,c), bit6 габарит rt, bit7 ВЫЗОВ по rt. */ +uint8_t pop_dbg_pass[14]; +uint8_t pop_dbg_ovl[14 * 4]; +/* ВРЕМЕННО: какие тайлы РЕАЛЬНО перекрывают спрайт куска. Бит + * (ряд+1)*2 + (0 своя колонка / 1 соседняя), ряды −1..2. */ +uint8_t pop_dbg_tiles[14]; + +/* Зеркало уровня 4 — см. pop_state.h. */ +int8_t pop_jumped_mirror; + +/* Тайл СОСЕДНЕЙ комнаты изменился, пока она не отрисована (плита, которую + * Кид расшатал и бросил, провалилась заочно — animate_loose в pop_trob). + * Кромочные колонки и у отрисовки (lcol_fg), и у коллизии (g_lcol/g_rcol) — + * СНИМКИ на момент входа в комнату, поэтому их надо перечитать. Ставит + * банк 6, разбирает главный цикл. */ +uint8_t pop_neigh_dirty; + +/* seamless (seg000:900 + seg003:158): переход 12 → 13 идёт БЕЗ двери + * уровня — уровень кончается фактом попадания в комнату 23, и следующий + * начинается в комнате с тем же номером и тем же рядом. Флаг говорит + * старту уровня «не сбрасывай HP» (и, когда появятся заставки, «не + * показывай „LEVEL 13“»): Кид продолжает бежать, не заметив границы. + * Ставит главный цикл, гасит pop_start_level. */ +uint8_t pop_seamless; + +/* «Кид попал в комнату ЧИТОМ навигации» — держит бесшовный переход уровня. + * Отладочная добавка, у оригинала телепорта между комнатами нет вовсе; см. + * pop_dbg_roomnav и ../docs/impl_diff.md. Снимается первой же СМЕНОЙ + * КОМНАТЫ ОБЫЧНЫМ ХОДОМ и стартом уровня. */ +uint8_t pop_nav_hold; + + + +/* ВРЕМЕННО (замер фаз кадра 2026-08-13): четыре маркера-пустышки в + * РЕЗИДЕНТЕ. Брейкпоинт на каждом + printf totalcycles даёт длительность + * блоков: m1..m2 — логика (синий), m2..m3 — фон (зелёный), m3..m4 — + * спрайты (циан). В банк брейкпоинт ставить нельзя: 0xC000 общее окно. */ +void pop_dbg_m1(void) { } +void pop_dbg_m2(void) { } +void pop_dbg_m3(void) { } +void pop_dbg_m4(void) { } + +/* ВРЕМЕННО (разбор зелёного блока 2026-08-13): состав перерисовок в САМОМ + * тяжёлом кадре. pop_redraw_needed считает вызовы по видам, и если кадр + * оказался тяжелее прежнего максимума — сохраняет расклад сюда. + * Индексы 0..7 — POP_RD_*, 8..9 — POP_RDA_* (полоса у потолка). */ +uint8_t pop_dbg_rdmax[10]; +uint8_t pop_dbg_rdmax_tot; + +/* ВРЕМЕННО (замер внутренностей 2026-08-13): m5..m8 — границы внутри + * pop_ceil_shake_draw (heal / draw_tile) и mob_render (heal / блиты). */ +void pop_dbg_m5(void) { } +void pop_dbg_m6(void) { } +void pop_dbg_m7(void) { } +void pop_dbg_m8(void) { } + +/* ВРЕМЕННО (разбор pop_loose_tick 2026-08-13): m9..m12 — границы ВНУТРИ + * самого pop_loose_tick. Он банковый, но зовёт резидент W1 напрямую, + * так что брейкпоинты по этим адресам однозначны. */ +void pop_dbg_m9(void) { } +void pop_dbg_m10(void) { } +void pop_dbg_m11(void) { } +void pop_dbg_m12(void) { } + +/* ВРЕМЕННО (разбор pop_ceil_shake_draw 2026-08-13): heal / draw_tile. */ +void pop_dbg_m13(void) { } +void pop_dbg_m14(void) { } +void pop_dbg_m15(void) { } + +/* ВРЕМЕННО (разбор pop_redraw_needed по ВИДАМ 2026-08-13): вид приезжает в + * A (__sdcccall(1), uint8 arg1), брейкпоинт на первой инструкции логирует + * его вместе с тактами — так видно цену КАЖДОГО вида, а не число пометок. */ +void pop_dbg_kind(uint8_t k) { (void)k; } +void pop_dbg_m16(void) { } + +/* ВРЕМЕННО (разбор pop_blit_b 2026-08-13): сколько в одном блите занимают + * САМИ пиксели, а сколько пооспрайтовые накладные (map/unmap страницы + * атласа, чтение заголовка, пометка грязного прямоугольника). */ +void pop_dbg_b1(void) { } /* вход (спрайт прошёл отсев) */ +void pop_dbg_b2(void) { } /* atlas_image + gfx_w0_map */ +void pop_dbg_b3(void) { } /* сам блит */ +void pop_dbg_b4(void) { } /* pop_cd_touch */ +void pop_dbg_b5(void) { } /* gfx_w0_unmap (конец) */ +void pop_dbg_b6(void) { } /* взведён = пошли в gfx_blit_noclip, а не в _part */ + +/* ВРЕМЕННО (разбор pop_phys_tick 2026-08-19): звенья цепочки kid_phys. */ +void pop_dbg_p1(void) { } /* loadkid_and_opp сделан */ +void pop_dbg_p2(void) { } /* fall_accel + fall_speed */ +void pop_dbg_p3(void) { } /* determine_col */ +void pop_dbg_p4(void) { } /* bump_into_opponent */ +void pop_dbg_p5(void) { } /* check_collisions */ +void pop_dbg_p6(void) { } /* check_bumped */ +void pop_dbg_p7(void) { } /* check_action */ +void pop_dbg_p8(void) { } /* check_press */ + +/* ВРЕМЕННО (регрессия цены блита 2026-08-13): w и h упакованы в один + * 16-битный аргумент (arg1 -> HL при __sdcccall(1)), брейкпоинт логирует + * HL — дальше цена раскладывается как a + b*h + c*w*h, где b и есть + * построчные накладные ядра. */ +void pop_dbg_wh(uint16_t wh) { (void)wh; } diff --git a/applications/SprPoP/src/pop_state.h b/applications/SprPoP/src/pop_state.h new file mode 100644 index 0000000..ae167a6 --- /dev/null +++ b/applications/SprPoP/src/pop_state.h @@ -0,0 +1,133 @@ +/* + * pop_state.h — состояние, общее для банков и W1/W2. См. pop_state.c. + * + * Правило: если переменную читают/пишут по обе стороны банковой границы — + * её место здесь, а НЕ в банковом модуле (там она уедет в страницу банка). + */ +#ifndef POP_STATE_H +#define POP_STATE_H + +#include + +/* Есть ли хоть один ЗАНЯТЫЙ слот падающего куска (active или дочистка + * clean). Гейт холостого хода: пока кусков нет, ни обход 14 слотов в + * pop_loose_mob_tick, ни поиск куска над головой Кида делать не нужно + * (замер 11/15: 12 090 + 5 868 тактов на комнату, где не падает ничего). + * + * Живёт в резиденте, а не в pop_room.c, потому что читает его pop_map + * (банк 3), а писучие статики банкового модуля наружу не видны. Ставит + * отрыв куска, снимает сам обход по факту пустой таблицы. */ +extern uint8_t pop_mob_busy; + +/* Кусок loose приземлился (loose_land, seg007:11E8): 0 = нет, иначе + * tilepos+1 тайла, на который он лёг. Ставит отрисовка mob (банк), + * разбирает логика loose-полов (pop_map, W1/W2). */ +extern uint8_t pop_loose_landed; + +/* Кусок loose ушёл ниже поля своей комнаты: 0 = нет, иначе col+1; комната + * отправления — pop_loose_exit_room. Ставит отрисовка mob (банк 7), + * разбирает главный цикл (посадка в комнате снизу). */ +extern uint8_t pop_loose_exit; +extern uint8_t pop_loose_exit_room; + +/* Тайл СОСЕДНЕЙ (не отрисованной) комнаты изменился — перечитать кромки. + * Подробности — в pop_state.c. */ +extern uint8_t pop_neigh_dirty; + +/* Бесшовный переход между уровнями (seamless): подробности в pop_state.c. + * Ставится главным циклом при попадании в комнату-выход, гасится стартом + * уровня — он же единственный, кто на флаг смотрит. */ +extern uint8_t pop_seamless; + +/* Держатель бесшовного перехода после ЧИТ-телепорта (см. pop_state.c). + * Ставит pop_dbg_roomnav, снимает первая смена комнаты обычным ходом. */ +extern uint8_t pop_nav_hold; + +/* Чит «бессмертие» (I): Кида бьют, но HP не убавляется. Читают пути урона + * (боёвка в банке, пики в pop_map), переключает главный цикл. */ +/* ЧИТ БЕССМЕРТИЯ, три состояния (клавиша I по кругу): + * 0 — выключено; + * 1 — только БОЙ: удары мечом не отнимают HP (pop_take_hp тут ни при + * чём, ветка живёт в hurt_by_sword, guards.c); + * 2 — бой И мелкий урон: не проходят «минус одно деление» от падения с + * двух этажей и от падающей плиты (pop_take_hp гасит count < 100). + * МГНОВЕННАЯ СМЕРТЬ остаётся на любом уровне: пики, чомпер, падение с + * трёх этажей, удар мечом вне боевой стойки — все они приходят с + * count = 100. Чит отменяет потерю HP, а не смерть. */ +extern uint8_t pop_immortal; + +/* Габариты ПОСЛЕДНЕЙ нарисованной двери уровня — порт глобалов + * leveldoor_right / leveldoor_ybottom (seg008 draw_leveldoor). Их ставит + * отрисовка (банк pop_bg), а читает clip_char (pop_map, W1/W2): кадры + * подъёма по лестнице (224..228) обрезаются по правому косяку проёма и по + * низу поднятой створки. Дверь рисуется ПОД персонажем, поэтому перекрыть + * его не может — оригинал именно режет спрайт. + * right — экранный X правой кромки; ybottom — room-space Y (без POP_YOFF). + * 0 = двери в отрисованной комнате нет. */ +extern int pop_leveldoor_right; +extern int pop_leveldoor_ybottom; + +/* droppedout (seg006:1140): Кида СПИХНУЛИ с уступа в бою — он падает из + * боевой стойки. Ставит start_fall (pop_map, банк 3), читает ИИ стража + * (guards.c, банк 1): по этому флагу страж решает, идти ли за упавшим вниз + * (guard_follows_kid_down, seg002:09F8). Отсюда и место в общем модуле — + * банки друг другу данные не отдают. */ +extern uint8_t pop_droppedout; + +/* leveldoor_open (seg007:456): 1 — дверь уровня открыта полностью. Пишет + * анимация двери (банк 6), читает подъём скелета (банк 1). Word, как в + * оригинале: на уровне 8 в него же СЧИТАЕТСЯ задержка до мыши (см. + * pop_state.c и pop_check_mouse в guards.c). */ +extern uint16_t pop_leveldoor_open; + +/* Отладочная «пустышка» для брейкпоинтов из банков — см. pop_state.c. + * Звать из отлаживаемого места под нужным `if`, брейкпоинт ставить на + * _pop_dbg_trap (резидент W1, адрес однозначен). */ +extern uint8_t pop_dbg_hits; +void pop_dbg_trap(void); + +#endif + +/* Зеркало уровня 4 (jumped_through_mirror, seg003). −1 — Кид ТОЛЬКО ЧТО + * пролетел сквозь зеркало (ставит is_obstacle в pop_map, банк 3), и + * следующий pop_check_mirror родит тень; 0 — обычное состояние. + * Межбанковый: пишет pop_map, читает pop_map же, но через главный цикл. */ +extern int8_t pop_jumped_mirror; + + + +/* ВРЕМЕННО: маркеры фаз кадра (замер). */ +void pop_dbg_m1(void); void pop_dbg_m2(void); +void pop_dbg_m3(void); void pop_dbg_m4(void); + +/* ВРЕМЕННО: состав перерисовок в самом тяжёлом кадре (замер). */ +extern uint8_t pop_dbg_rdmax[10]; +extern uint8_t pop_dbg_rdmax_tot; + +/* ВРЕМЕННО: маркеры внутренних замеров. */ +void pop_dbg_m5(void); void pop_dbg_m6(void); +void pop_dbg_m7(void); void pop_dbg_m8(void); +void pop_dbg_m9(void); void pop_dbg_m10(void); +void pop_dbg_m11(void); void pop_dbg_m12(void); +void pop_dbg_m13(void); void pop_dbg_m14(void); +void pop_dbg_m15(void); +void pop_dbg_kind(uint8_t k); void pop_dbg_m16(void); +void pop_dbg_b1(void); void pop_dbg_b2(void); void pop_dbg_b3(void); +void pop_dbg_b4(void); void pop_dbg_b5(void); void pop_dbg_b6(void); +/* ВРЕМЕННО (разбор pop_phys_tick 2026-08-19, позиция P2a): физика Кида — + * 61 266 тактов на НЕПОДВИЖНОМ персонаже. По одному зонду на звено + * цепочки kid_phys. */ +void pop_dbg_p1(void); void pop_dbg_p2(void); void pop_dbg_p3(void); +void pop_dbg_p4(void); void pop_dbg_p5(void); void pop_dbg_p6(void); +void pop_dbg_p7(void); void pop_dbg_p8(void); +void pop_dbg_wh(uint16_t wh); + +/* ВРЕМЕННО: трасса kidobj (см. pop_state.c). Порядок байт: + * 0 obj_tilepos, 1 frame, 2 action, 3 curr_col, 4 char_col_left, + * 5 char_col_right, 6 char_top_row, 7 char_bottom_row, 8 комната. */ +extern int8_t pop_dbg_obj[9]; +/* ВРЕМЕННО: разбор оверлея падающего куска (см. pop_state.c). */ +extern uint8_t pop_dbg_pass[14]; +extern uint8_t pop_dbg_ovl[14 * 4]; +extern uint8_t pop_dbg_tiles[14]; +void pop_dbg_kidobj(void) __banked; diff --git a/applications/SprPoP/src/pop_status.c b/applications/SprPoP/src/pop_status.c new file mode 100644 index 0000000..8371bb8 --- /dev/null +++ b/applications/SprPoP/src/pop_status.c @@ -0,0 +1,585 @@ +/* + * pop_status.c — текст в двух служебных полосах экрана, БАНК 9. + * + * Нижняя полоса (та же, где деления HP) — статус-строка оригинала; + * верхняя — наша отладочная. Обе живут ВНЕ игрового поля, поэтому фон у + * них POP_COL_OUTSIDE, а не цвет 0: вспышки (урон, зелья, молния) сделаны + * подменой нулевого цвета палитры, и полосы не должны мигать вместе с + * игрой (pop_ui.h). + * + * ЦЕНА ВЫВОДА — главное ограничение. Блит одного глифа стоит ~4,6 тыс. + * тактов почти независимо от размера (см. memory blit_cost_model): цена + * вызова, а не пикселей. Полная отладочная строка — это полсотни глифов, + * то есть ПОЛКАДРА. Отсюда три правила, на которых держится модуль: + * + * 1. Всё change-driven. Пока показанное не изменилось, не рисуем вовсе + * — за кадр остаётся только сравнение нескольких байт. + * 2. Отладочная строка перерисовывается ПО ПОЛЯМ. Смена комнаты — это + * две цифры (~9 тыс. тактов, 2% кадра), а не вся строка; подписи + * («Level», «, Room», …) не меняются никогда и рисуются лишь при + * полной инвалидации, которая случается на старте уровня, где кадр и + * так тяжёлый. + * 3. Пробелы не блитятся (pop_ui.c): их глиф целиком прозрачен, и на + * строке из полусотни символов это девять сэкономленных блитов. + * + * ГРЯЗЬ СЧИТАЕТСЯ ПО СТРАНИЦАМ, а не счётчиком «осталось два кадра». У + * каждой страницы дабл-буфера своя видео-ОЗУ, и если поля меняются в + * разных кадрах вперемешку, общий счётчик их путает: маска на страницу + * всегда описывает, что именно этой странице ещё должны. + * + * Рисуем прозрачным банком GFX_BANK_TRANSPARENT (не SPRITE): 0xFF шрифта + * не пишется, но ОЗУ-копия обновляется — и текст переживает полностраничные + * копии (вход в комнату, выход из меню), которые берут картинку из тени. + * + * Данных в W2 — 11 байт, и семь из них просто переехали сюда из прежнего + * ярлыка комнаты в sprpop_cold.c. Кода в резиденте не появляется вовсе: + * заявка сообщения — присваивание одного байта, всё остальное здесь. + */ +#include +#include +#include +#include +#include "pop_status.h" +#include "pop_ui.h" +#include "pop_font.h" +#include "pop_bg.h" /* POP_YOFF */ +#include "pop_cdraw.h" /* POP_HP_Y — полосу делим с делениями жизней */ +#include "pop_settings.h" +#include "pop_app.h" /* start_game после 288 тиков молчания */ +#include "pop_state.h" /* pop_immortal */ +#include "pop_level.h" /* pop_current_level */ +#include "pop_pace.h" /* pop_speed_mode */ +#include "pop_timer.h" /* оставшееся время и флаг «пора показать» */ +#include "pop_sfx.h" /* pop_snd_want */ + +/* ---- нижняя полоса: статус-строка -------------------------------------- */ + +/* Как у оригинала: 24 тика (text_time_total = 24) — около двух секунд. */ +#define MSG_TICKS 24 +/* «Висит, пока не снимут»: у оригинала эту роль играет особое значение + * text_time_total (288 у «Press Button», 1188 у copy protection). */ +#define MSG_HOLD 0xFF +/* Базовая линия та же, что у GAME PAUSED (pop_menu.c): строка ниже делений. */ +#define MSG_BASE (POP_YOFF + POP_HP_Y + 8) +#define MSG_TOP (MSG_BASE - POP_FONT_BIG_ASCENT - 2) +#define MSG_BOTTOM (MSG_BASE) + +/* POP_MSG_LEVEL здесь пустой: его строка собирается с номером уровня. */ +static const char * const status_text[POP_MSG_N] = { + "", + "QUICKSAVE", + "NO QUICKSAVE", + "QUICKLOAD", + "NO QUICKLOAD", + "SOUND ON", + "SOUND OFF", + "MUSIC ON", + "MUSIC OFF", + "", /* LEVEL N — собирается с номером */ + "", /* N MINUTES LEFT */ + "", /* N SECONDS LEFT */ + "TIME HAS EXPIRED!", + "Press Button to Continue" +}; + +uint8_t pop_status_msg; /* заявка (пишут откуда угодно) */ +uint8_t pop_status_ticks; /* сколько кадров ещё висит; читает HP */ +static uint8_t st_shown; /* что реально нарисовано */ +static uint8_t st_dirty; /* бит на страницу дабл-буфера */ +static uint8_t st_arg; /* номер уровня для POP_MSG_LEVEL */ + +/* ---- верхняя полоса: отладочная строка --------------------------------- * + * + * Level ##, Room ##, Speed: ..., Sound: ..., Immortal # + * + * Раскладка фиксированная: у каждого поля свой левый край и своя зона + * стирания, поэтому изменение одного значения не трогает соседей. Ширины + * посчитаны по самому длинному значению (FASTEST, OFF, две цифры). */ +#define DBG_BASE 6 /* базовая линия малого шрифта */ +#define DBG_TOP 0 +/* На строку НИЖЕ базовой линии: у малого шрифта 'p' высотой 7 (и ',' — 6) + * свисает под неё, и при стирании ровно до базовой линии от хвоста «p» в + * «Speed:» оставалась одинокая точка. */ +#define DBG_BOTTOM 7 +#define DBG_LEFT 8 +#define DBG_RIGHT 310 + +/* Значения ПРИЖАТЫ ВПРАВО к границе своей зоны, а подпись следующего поля + * начинается сразу за ней. Так запятая всегда стоит вплотную к значению — + * при выравнивании влево она отрывалась бы на всю ширину зоны («Level 1 , + * Room»). Дырка слева от значения при этом крошечная: варианты близки по + * ширине (NORMAL 30 против FASTEST 32, ON 9 против OFF 14). */ +#define X_LBL_LEVEL 10 /* "Level" 10..30 */ +#define R_VAL_LEVEL 44 /* зона 31..43 */ +#define X_LBL_ROOM 44 /* ", Room" 44..70 */ +#define R_VAL_ROOM 84 /* зона 71..83 */ +#define X_LBL_SPEED 84 /* ", Speed:" 84..116 */ +#define R_VAL_SPEED 152 /* зона 117..151 */ +/* Три тумблера подряд — звук, музыка, бессмертие. Подписи сокращены до + * буквы с двоеточием: полные слова («Sound», «Immortal») занимали столько, + * что на музыку места уже не оставалось, а смысл и так читается. */ +#define X_LBL_SOUND 152 /* ", S:" 152..168 */ +#define R_VAL_SOUND 185 /* зона 169..184 */ +#define X_LBL_MUS 185 /* ", M:" 185..201 */ +#define R_VAL_MUS 218 /* зона 202..217 */ +#define X_LBL_IMM 218 /* ", I:" 218..234 */ +#define R_VAL_IMM 245 /* зона 235..244: цифре нужен зазор в пробел */ +/* Оставшееся время ##:## — отдельным полем у правого края, подписи не + * требует: формат читается сам. */ +#define R_VAL_TIME 310 /* зона 284..309 */ +#define W_VAL_TIME 26 + +/* Ширина зоны значения — под самый длинный вариант плюс воздух. */ +#define W_VAL_NUM 13 /* две цифры */ +#define W_VAL_SPEED 35 /* FASTEST */ +#define W_VAL_SOUND 16 /* OFF */ +#define W_VAL_IMM 9 /* одна цифра */ + +enum { /* биты маски «что перерисовать» */ + DBG_F_LEVEL = 0x01, + DBG_F_ROOM = 0x02, + DBG_F_SPEED = 0x04, + DBG_F_SOUND = 0x08, + DBG_F_IMM = 0x10, + DBG_F_TIME = 0x20, + DBG_F_MUS = 0x40, + DBG_F_ALL = 0x7F /* + подписи: см. dbg_labels */ +}; + +static const char * const speed_text[] = { "NORMAL", "FAST", "FASTEST" }; + +static uint8_t dbg_lvl, dbg_room, dbg_spd, dbg_snd, dbg_mus, dbg_imm; +static uint8_t dbg_tmin, dbg_tsec; /* показанное время ##:## */ +static uint8_t dbg_phase; /* кадры до пересчёта секунд */ +static uint8_t dbg_dirty[2]; /* маска по странице; DBG_F_ALL => и подписи */ +static uint8_t dbg_on; /* показанное состояние тумблера Settings */ + +/* ---- общее ------------------------------------------------------------- */ + +static void wipe(int left, int top, int right, int bottom) +{ + gfx_set_bank(GFX_BANK_NORMAL); + setfillstyle(SOLID_FILL, POP_COL_OUTSIDE); + bar(left, top, right, bottom); +} + +/* Число 0..99 БЕЗ ведущего нуля и без ведущего пробела: значение + * выравнивается вправо, а пробел в шрифте имеет ненулевую ширину и сдвинул + * бы цифру влево от границы зоны. Деления нет намеренно: у SDCC z80 один + * «/» стоит ~5,4 тыс. тактов (memory sdcc_z80_division_hoisting), здесь же + * максимум девять вычитаний. */ +static void dec2(uint8_t v, char *buf) +{ + uint8_t t = 0; + while (v >= 10) { v = (uint8_t)(v - 10); t++; } + if (t) { *buf++ = (char)('0' + t); } + buf[0] = (char)('0' + v); + buf[1] = 0; +} + +/* То же, но ВСЕГДА две цифры: часы-минуты без ведущего нуля не читаются. */ +static void dec2z(uint8_t v, char *buf) +{ + uint8_t t = 0; + while (v >= 10) { v = (uint8_t)(v - 10); t++; } + buf[0] = (char)('0' + t); + buf[1] = (char)('0' + v); + buf[2] = 0; +} + +/* ---- нижняя полоса ----------------------------------------------------- */ + +/* Строки с числом собираются вручную: printf сюда не тянем, а число — + * единственное переменное место во всём наборе сообщений. */ +static char *put(char *buf, const char *s) +{ + while (*s) *buf++ = *s++; + return buf; +} + +/* Число ВПЕРЕДИ («12 MINUTES LEFT»), как у оригинала. */ +static void num_text(char *buf, const char *suffix) +{ + dec2(st_arg, buf); + while (*buf) buf++; + *put(buf, suffix) = 0; +} + +static void build_text(char *buf) +{ + if (st_shown == POP_MSG_LEVEL) { + *put(put(buf, "LEVEL "), "") = 0; + dec2(st_arg, buf + 6); + } else if (st_shown == POP_MSG_MINUTES) { + num_text(buf, " MINUTES LEFT"); + } else if (st_arg == 1) { + *put(buf, "1 SECOND LEFT") = 0; + } else { + num_text(buf, " SECONDS LEFT"); + } +} + +/* Стереть полосу сообщения на ТЕКУЩЕЙ странице. Нужна меню: оно пишет в ту + * же полосу своё «GAME PAUSED», а под ним могло остаться сообщение игры + * («60 MINUTES LEFT», «QUICKSAVE») — две надписи ложились друг на друга. */ +void pop_status_wipe(void) __banked +{ + uint8_t saved; + + if (!pop_ui_ready()) return; + saved = gfx_get_bank(); + wipe(POP_STATUS_L, MSG_TOP, POP_STATUS_R, MSG_BOTTOM); + gfx_set_bank(saved); +} + +static void status_draw(void) +{ + char buf[18]; + const char *text; + + wipe(POP_STATUS_L, MSG_TOP, POP_STATUS_R, MSG_BOTTOM); + if (!st_shown) return; + if (st_shown >= POP_MSG_LEVEL && st_shown <= POP_MSG_SECONDS) { + build_text(buf); + text = buf; + } else { + text = status_text[st_shown]; + } + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_text_map(); + pop_text_center_mapped(POP_TEXT_BIG, (POP_STATUS_L + POP_STATUS_R) / 2, + MSG_BASE, text); + pop_text_unmap(); +} + +/* Заявка сообщения С ЧИСЛОМ. Обычная (байтовая) заявка сравнивает только + * номер сообщения, а здесь меняться может одно лишь число — «12 SECONDS + * LEFT» и «11 SECONDS LEFT» это один и тот же POP_MSG_SECONDS, и по номеру + * их не различить. Поэтому ставим состояние сразу и сами взводим грязь. */ +static void msg_set(uint8_t msg, uint8_t arg, uint8_t ticks) +{ + pop_status_msg = msg; + st_shown = msg; + st_arg = arg; + pop_status_ticks = ticks; + st_dirty = 3; +} + +/* Порт show_level (seg008:25A8). Демо-уровень 0 и номера от 14 не + * показываются вовсе, тринадцатый представляется двенадцатым, а бесшовный + * переход 12->13 молчит — и, как в оригинале, гасит флаг за собой. + * Следом оригинал просит показать и время: за «LEVEL 1» идёт «60 MINUTES + * LEFT», когда первая строка отвисит своё. */ +void pop_status_level(void) __banked +{ + uint8_t n = pop_current_level; + uint8_t quiet = pop_seamless; + + pop_seamless = 0; + if (quiet || n == 0 || n >= POP_LEVEL_LAST) return; + if (n == 13) n = 12; + msg_set(POP_MSG_LEVEL, n, MSG_TICKS); + pop_show_time = 1; +} + +/* Вторая половина show_time (seg008:1812): собственно печать. Флаг + * pop_show_time == 2 означает «перебить текущую строку немедленно» — + * так оригинал ведёт себя в последнюю минуту и после читов времени. */ +static void time_msg(void) +{ + if (!pop_show_time) return; + if (pop_show_time == 2) pop_status_ticks = 0; + else if (pop_status_ticks) return; /* ждём, пока освободится строка */ + pop_show_time = 0; + + if (pop_timer_minutes == 0) { + msg_set(POP_MSG_EXPIRED, 0, MSG_TICKS); + } else if (pop_timer_minutes == 1) { + /* Секунда — 12 тиков; +1 потому, что ticks уже уменьшен. */ + uint8_t sec = (uint8_t)((pop_timer_ticks + 1) / 12); + msg_set(POP_MSG_SECONDS, sec, (uint8_t)(sec == 1 ? 12 : MSG_TICKS)); + } else { + msg_set(POP_MSG_MINUTES, pop_timer_minutes, MSG_TICKS); + } +} + +/* Показать сообщение НЕМЕДЛЕННО, не дожидаясь конца кадра. + * + * Обычный путь (заявка в pop_status_msg + pop_status_frame) печатает строку + * там же, где рисуется кадр, — то есть уже ПОСЛЕ длинной операции. На + * QuickSave это выглядело так: игра замирает на полсекунды, и только потом + * появляется «QUICKSAVE», когда объяснять фриз уже поздно. Здесь строка + * выкладывается сразу, до дисковой работы. + * + * Рисуем в ВИДИМУЮ страницу: игрок смотрит именно на неё, а до следующего + * флипа кадра ещё вся операция. Скрытой странице сообщение остаётся + * должным — её закроет обычный кадровый проход. */ +void pop_status_show_now(uint8_t msg) __banked +{ + uint8_t visible, saved_page, saved_bank; + + pop_status_msg = msg; + if (!pop_ui_ready()) return; /* шрифта нет — останется заявкой */ + st_shown = msg; + pop_status_ticks = (uint8_t)(msg ? MSG_TICKS : 0); + st_dirty = 3; + + visible = (uint8_t)(gfx_get_visible_page() & 1); + saved_page = gfx_get_draw_page(); + saved_bank = gfx_get_bank(); + gfx_set_draw_page(visible); + status_draw(); + /* Бит видимой страницы НЕ снимаем. За время дисковой операции ту же + * страницу успевает переписать восстановление комнаты, и снятый бит + * означал бы «здесь уже нарисовано» — надпись оставалась ровно на одной + * странице и мигала через кадр (поймано на QUICKLOAD). Лишняя + * перерисовка строки стоит дешевле мигания. */ + gfx_set_draw_page(saved_page); + gfx_set_bank(saved_bank); +} + +/* ---- верхняя полоса ---------------------------------------------------- */ + +/* right — граница зоны (первый столбец СЛЕДУЮЩЕЙ подписи), w — её ширина. */ +static void dbg_value(int right, uint8_t w, const char *s) +{ + wipe(right - w, DBG_TOP, right - 1, DBG_BOTTOM); + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_text_map(); + pop_text_right_mapped(POP_TEXT_SMALL, right - 1, DBG_BASE, s); + pop_text_unmap(); +} + +/* Подписи неизменны — рисуем их только при полной перерисовке полосы. */ +static void dbg_labels(void) +{ + wipe(DBG_LEFT, DBG_TOP, DBG_RIGHT, DBG_BOTTOM); + gfx_set_bank(GFX_BANK_TRANSPARENT); + pop_text_map(); + pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_LEVEL, DBG_BASE, "Level"); + pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_ROOM, DBG_BASE, ", Room"); + pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_SPEED, DBG_BASE, ", Speed:"); + pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_SOUND, DBG_BASE, ", S:"); + pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_MUS, DBG_BASE, ", M:"); + pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_IMM, DBG_BASE, ", I:"); + pop_text_unmap(); +} + +static void dbg_draw(uint8_t mask) +{ + char buf[6]; + + /* Полная перерисовка стирает всю полосу разом — значит зоны значений + * стирать по второму разу не надо, но нарисовать их всё равно нужно. */ + if (mask == DBG_F_ALL) dbg_labels(); + if (mask & DBG_F_LEVEL) { + dec2(dbg_lvl, buf); + dbg_value(R_VAL_LEVEL, W_VAL_NUM, buf); + } + if (mask & DBG_F_ROOM) { + dec2(dbg_room, buf); + dbg_value(R_VAL_ROOM, W_VAL_NUM, buf); + } + if (mask & DBG_F_SPEED) + dbg_value(R_VAL_SPEED, W_VAL_SPEED, + speed_text[dbg_spd < 3 ? dbg_spd : 0]); + if (mask & DBG_F_SOUND) + dbg_value(R_VAL_SOUND, W_VAL_SOUND, dbg_snd ? "ON" : "OFF"); + if (mask & DBG_F_MUS) + dbg_value(R_VAL_MUS, W_VAL_SOUND, dbg_mus ? "ON" : "OFF"); + if (mask & DBG_F_IMM) { + buf[0] = (char)('0' + (dbg_imm < 10 ? dbg_imm : 9)); + buf[1] = 0; + dbg_value(R_VAL_IMM, W_VAL_IMM, buf); + } + if (mask & DBG_F_TIME) { + dec2z(dbg_tmin, buf); + buf[2] = ':'; + dec2z(dbg_tsec, buf + 3); + dbg_value(R_VAL_TIME, W_VAL_TIME, buf); + } +} + +/* Снимок ДО сравнения — обход sdcc_z80_cmp_store_a_bug: `if (n != s) s = n;` + * SDCC z80 собирает неверно (SUB затирает A, в снимок уезжает разность). */ +static void dbg_scan(uint8_t room) +{ + uint8_t prev_lvl = dbg_lvl, prev_room = dbg_room, prev_spd = dbg_spd; + uint8_t prev_snd = dbg_snd, prev_mus = dbg_mus, prev_imm = dbg_imm; + uint8_t prev_tmin = dbg_tmin, prev_tsec = dbg_tsec; + uint8_t mask = 0; + + /* Часы-минуты: у оригинала rem_min — это НОМЕР идущей минуты, а не + * остаток целых минут (старт 60 при rem_tick 719 = «почти 60:00»), + * поэтому на табло уходит minutes-1 и секунды из тиков. + * + * Секунды — единственное деление на этом пути, поэтому считаем их не + * каждый кадр, а раз в двенадцать (тик секунды у SDLPoP) и вдобавок + * сразу, если минуты изменились (чит времени, загрузка снимка). */ + { + uint8_t mm = (uint8_t)(pop_timer_minutes ? pop_timer_minutes - 1 : 0); + if (dbg_phase) dbg_phase--; + if (!dbg_phase || dbg_tmin != mm) { + dbg_phase = 12; + dbg_tsec = (uint8_t)(pop_timer_minutes ? pop_timer_ticks / 12 : 0); + } + dbg_tmin = mm; + } + + dbg_lvl = pop_current_level; + dbg_room = room; + dbg_spd = pop_speed_mode; + dbg_snd = pop_snd_want; + dbg_mus = pop_mus_want; + dbg_imm = pop_immortal; + if (dbg_lvl != prev_lvl) mask |= DBG_F_LEVEL; + if (dbg_room != prev_room) mask |= DBG_F_ROOM; + if (dbg_spd != prev_spd) mask |= DBG_F_SPEED; + if (dbg_snd != prev_snd) mask |= DBG_F_SOUND; + if (dbg_mus != prev_mus) mask |= DBG_F_MUS; + if (dbg_imm != prev_imm) mask |= DBG_F_IMM; + if (dbg_tmin != prev_tmin || dbg_tsec != prev_tsec) mask |= DBG_F_TIME; + if (mask) { + dbg_dirty[0] |= mask; + dbg_dirty[1] |= mask; + } +} + +/* ---- смерть Кида ------------------------------------------------------- * + * + * Порт play_kid (seg006:1383): вместо авто-респавна по таймеру уровень + * перезапускает сам игрок кнопкой, а строка висит, пока он этого не сделал + * (MSG_HOLD). Авто-рестарт оригинала по истечении 288 тиков нам не нужен: + * он там начинает игру заново, а мы возвращаем на чекпойнт. */ +#define DEAD_SETTLE 7 /* Kid.alive > 6: труп успел лечь */ +#define DEAD_TICKS 288 /* text_time_total у «Press Button» */ +#define DEAD_BLINK 72 /* последние 72 тика строка мигает */ +#define SND_BLINK 38 /* sound_38_blink на каждом появлении */ + +static uint8_t dead_armed; /* всё отпущено — можно принимать нажатие */ + +uint8_t pop_dead_prompt(uint16_t frames) __banked +{ + uint16_t rem; + + if (frames < DEAD_SETTLE) return 0; + if (frames == DEAD_SETTLE) { + msg_set(POP_MSG_PRESS_BUTTON, 0, MSG_HOLD); + pop_show_time = 0; /* иначе поверх ляжет время (seg006:1365) */ + dead_armed = 0; + return 0; + } + + /* Свой отсчёт, а не pop_status_ticks: тот 8-битный, а здесь нужно 288. + * Кадры смерти считает вызывающий, так что хватает вычитания. */ + rem = (uint16_t)(frames - DEAD_SETTLE); + rem = (uint16_t)(rem >= DEAD_TICKS ? 0 : DEAD_TICKS - rem); + if (rem == 0) { + /* Игрок промолчал все 24 секунды — оригинал зовёт start_game(), + * то есть уходит в title, а НЕ перезапускает уровень. Событие + * шлём отсюда: pop_app живёт в этом же банке, и резиденту остаётся + * один break вместо вызова с проверкой. */ + msg_set(POP_MSG_NONE, 0, 0); + (void)pop_app_dispatch(POP_APP_EV_RESTART_INTRO); + return 2; + } + + /* Мигание последних шести секунд: 4 тика видно, 8 нет; на кадре + * появления оригинал перепечатывает строку и играет звук 38. */ + if (rem < DEAD_BLINK) { + uint8_t phase = (uint8_t)(rem % 12); + if (phase > 3) { + if (st_shown) msg_set(POP_MSG_NONE, 0, 0); + } else if (phase == 3 && !st_shown) { + msg_set(POP_MSG_PRESS_BUTTON, 0, MSG_HOLD); + pop_sfx_play(SND_BLINK); + } + } + + /* Рестарт уровня — Enter или Shift (seg000:584 подменяет их на Ctrl+A), + * а не любая клавиша. Esc сюда не попадает: он открывает меню и + * разобран раньше в этом же кадре. + * + * Отпускания оригинал не ждёт, а мы ждём: Shift у нас — клавиша + * действия, и зажатый в момент смерти он перезапустил бы уровень + * мгновенно, не дав прочитать строку. */ + { + uint8_t any = (uint8_t)(kbd_raw_down(KBD_ENTER) || + kbd_raw_down(KBD_LSHIFT) || + kbd_raw_down(KBD_RSHIFT)); + if (!dead_armed) { + dead_armed = (uint8_t)!any; + return 0; + } + if (!any) return 0; + } + dead_armed = 0; + msg_set(POP_MSG_NONE, 0, 0); + return 1; +} + +/* ---- кадровая точка ---------------------------------------------------- */ + +void pop_status_invalidate(void) __banked +{ + st_dirty = 3; + dbg_dirty[0] = DBG_F_ALL; + dbg_dirty[1] = DBG_F_ALL; +} + +void pop_status_frame(uint8_t room) __banked +{ + uint8_t page, bit, saved; + uint8_t want = pop_settings.show_debug_bar; + + if (!pop_ui_ready()) return; + page = (uint8_t)(gfx_get_draw_page() & 1); + bit = (uint8_t)(1 << page); + saved = gfx_get_bank(); + + /* --- нижняя полоса: заявка, таймер, перерисовка --- */ + time_msg(); + if (pop_status_msg != st_shown) { + st_shown = pop_status_msg; + pop_status_ticks = (uint8_t)(!st_shown ? 0 : + (st_shown == POP_MSG_PRESS_BUTTON ? MSG_HOLD + : MSG_TICKS)); + st_dirty = 3; + } else if (pop_status_ticks && pop_status_ticks != MSG_HOLD) { + pop_status_ticks--; + if (!pop_status_ticks) { + /* Оригинал по истечении зовёт erase_bottom_text(1) — снимаем и + * заявку, иначе то же сообщение никогда не покажется снова. */ + pop_status_msg = 0; + st_shown = 0; + st_dirty = 3; + } + } + if (st_dirty & bit) { + status_draw(); + st_dirty = (uint8_t)(st_dirty & ~bit); + } + + /* --- верхняя полоса --- */ + if (want != dbg_on) { + dbg_on = want; + /* Выключили — полосу надо стереть на ОБЕИХ страницах, иначе её + * остаток висел бы до следующей смены комнаты. */ + dbg_dirty[0] = DBG_F_ALL; + dbg_dirty[1] = DBG_F_ALL; + } + if (!want) { + if (dbg_dirty[page]) { + wipe(DBG_LEFT, DBG_TOP, DBG_RIGHT, DBG_BOTTOM); + dbg_dirty[page] = 0; + } + } else { + dbg_scan(room); + if (dbg_dirty[page]) { + dbg_draw(dbg_dirty[page]); + dbg_dirty[page] = 0; + } + } + + gfx_set_bank(saved); +} diff --git a/applications/SprPoP/src/pop_status.h b/applications/SprPoP/src/pop_status.h new file mode 100644 index 0000000..f75fef0 --- /dev/null +++ b/applications/SprPoP/src/pop_status.h @@ -0,0 +1,87 @@ +/* + * pop_status.h — ТЕКСТ в двух служебных полосах экрана (банк 9). + * + * Нижняя (полоса HP) — статус-строка оригинала: порт display_text_bottom + * (seg008:2644) и пары text_time_total/text_time_remaining. Полный + * разбор всех сообщений SDLPoP — ../docs/status_line_text.md. + * Верхняя — наша отладочная строка (была палочками, теперь текст). + * + * ЗАЯВКА — ОДИН БАЙТ. `pop_status_msg` лежит в W2 и пишется откуда + * угодно простым присваиванием: ни трамплина, ни функции. Всё остальное + * (таймер, перерисовка на обеих страницах, шрифт) делает pop_status_frame() + * из банка 9 — один банковый вызов на кадр, рядом с прежним ярлыком + * комнаты. Так в резиденте W1/W2 не появляется ни кода, ни таблиц строк. + */ +#ifndef POP_STATUS_H +#define POP_STATUS_H + +#include + +/* Сообщения нижней полосы. Порядок = таблица строк в pop_status.c. */ +enum { + POP_MSG_NONE = 0, + POP_MSG_QUICKSAVE, /* F6 успешно */ + POP_MSG_NO_QUICKSAVE, /* F6 не удалось */ + POP_MSG_QUICKLOAD, /* F9 успешно */ + POP_MSG_NO_QUICKLOAD, /* F9 нечего грузить / битый слот */ + POP_MSG_SOUND_ON, /* Ctrl+S */ + POP_MSG_SOUND_OFF, + POP_MSG_MUSIC_ON, /* Ctrl+M */ + POP_MSG_MUSIC_OFF, + POP_MSG_LEVEL, /* «LEVEL N» — строка собирается с номером */ + POP_MSG_MINUTES, /* «N MINUTES LEFT» */ + POP_MSG_SECONDS, /* «N SECONDS LEFT» / «1 SECOND LEFT» */ + POP_MSG_EXPIRED, /* «TIME HAS EXPIRED!» */ + POP_MSG_PRESS_BUTTON, /* Кид мёртв — ждём кнопку (висит бессрочно)*/ + POP_MSG_N +}; + +/* Заявка/текущее сообщение. Пишется откуда угодно: 0 — снять строку, + * POP_MSG_* — показать. Повторная заявка того же номера продлевает показ + * только если строка уже погасла (как у оригинала: он тоже сравнивает). */ +extern uint8_t pop_status_msg; + +/* Показать сообщение НЕМЕДЛЕННО, до конца кадра: строка выкладывается в + * видимую страницу прямо сейчас. Нужна там, где сразу за вызовом идёт + * длинная операция (QuickSave/QuickLoad): обычная заявка напечаталась бы + * уже ПОСЛЕ фриза, когда объяснять его поздно. */ +void pop_status_show_now(uint8_t msg) __banked; + +/* Порт show_level (seg008:25A8): заявить «LEVEL N» на старте уровня. + * Сама решает, показывать ли (демо-уровень и финальные номера молчат) — + * вызывающему условий знать не нужно. */ +void pop_status_level(void) __banked; + +/* Сколько логических кадров сообщение ещё висит; 0 — не висит. Читает + * pop_hp_draw (банк 4), чтобы не затирать чужой текст своим фоном. */ +extern uint8_t pop_status_ticks; + +/* Горизонтальные границы ТЕКСТА в полосе HP. Ровно промежуток между + * делениями Кида (слева) и стража (справа) — как rect_bottom_text + * {193,70,202,250} у оригинала. */ +#define POP_STATUS_L 70 +#define POP_STATUS_R 250 + +/* Раз в логический кадр, ПОСЛЕ pop_hp_draw: тик таймера, перерисовка + * нижней строки и обновление верхней отладочной. Всё change-driven — + * пока ничего не менялось, не рисует вовсе. */ +void pop_status_frame(uint8_t room) __banked; + +/* Кид мёртв: показать «Press Button to Continue» и ждать кнопку. frames — + * сколько кадров прошло со смерти (труп должен успеть лечь). Возвращает 1 + * ОДИН раз, когда игрок нажал — вызывающему остаётся перезапустить уровень. + * Логика живёт в банке, потому что резидент W1 переполнен, а зовут её лишь + * в кадрах смерти. + * + * Возвращает: 0 — ждём дальше; 1 — игрок нажал Enter/Shift, перезапустить + * УРОВЕНЬ; 2 — 288 тиков истекли, уйти в title (порт start_game). */ +uint8_t pop_dead_prompt(uint16_t frames) __banked; + +/* Заставить перерисовать ОБЕ страницы: звать после того, как полосы + * затёрли чужим содержимым (вход в комнату, выход из меню). */ +void pop_status_invalidate(void) __banked; + +/* Стереть полосу сообщения на текущей странице (меню пишет туда своё). */ +void pop_status_wipe(void) __banked; + +#endif /* POP_STATUS_H */ diff --git a/applications/SprPoP/src/pop_tile.c b/applications/SprPoP/src/pop_tile.c new file mode 100644 index 0000000..da9fdab --- /dev/null +++ b/applications/SprPoP/src/pop_tile.c @@ -0,0 +1,624 @@ +/* + * pop_tile.c — общие ЛИСТЬЯ слоя фона: блит куска атласа, чтение тайла + * комнаты, таблица тайлов. РЕЗИДЕНТ W1 (_CODE). + * + * Мотивация раскладки (почему резидент, а не банк) — в шапке pop_tile.h: + * коротко, эти функции и таблицы нужны ОБЕИМ половинам слоя фона (горячей — + * fore-проход каждый кадр, холодной — полная отрисовка комнаты), а код и + * rodata банка видны только пока банк замаплен. W1 замаплено всегда. + */ +#include +#include +#include +#include "pop_bg.h" /* POP_YOFF, POP_PLAYFIELD_H */ +#include "pop_map.h" /* pop_upside — переворот (зелье инверсии) */ +#include "pop_cdraw.h" /* POP_CD_TOUCH — фон тронут, персонажей перерисовать */ +#include "pop_tile.h" +#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */ +#include "pop_level.h" /* pop_doorlink2 — подстановка нажатой кнопки */ +#include "_pop_draw.h" /* pop_heal_fast */ +#include "_pop_tile.h" /* контракт с холодной половиной (pop_tile_cold.c) */ + +/* ---- Состояние (писучее, _DATA/W2 — видно из любого банка) ---------- */ +atlas_t pop_env[POP_ENV_PAGES], pop_wall_at, pop_fore_at, pop_pot_at; + +/* Загруженный тайлсет: 0 подземелье, 1 дворец (порт tbl_level_type в той + * части, что касается ОТРИСОВКИ). Ставит pop_bg_load; резидент — потому + * что по нему расходятся ветки и в банке 7 (полная отрисовка тайла), и в + * банке 2 (fore-проход), и в банке 3 (модификатор пустой клетки от упавшей + * плиты, remove_loose seg007:0EB8). Читать напрямую, без трамплина. */ +uint8_t pop_palace; + +const uint8_t *pop_t_fg, *pop_t_bg, *pop_t_lfg, *pop_t_lbg; +const uint8_t *pop_t_below, *pop_t_above, *pop_t_abovemod; +uint8_t pop_t_room; +pop_rnd_t pop_t_seed; + +int pop_t_clip_top; +int pop_t_fclip_x0, pop_t_fclip_y0, pop_t_fclip_x1, pop_t_fclip_y1; +/* ТО ЖЕ ОКНО В ЛОГИЧЕСКИХ (неперевёрнутых) координатах. Экранная пара нужна + * для клипа самого блита — он режет уже перевёрнутый прямоугольник; а вот + * ОТСЕВ ТАЙЛОВ идёт по позиции тайла в комнате (63*row + …), и она от + * переворота не зависит. Пока пара была одна, при pop_upside отсев сравнивал + * логический y тайла с зеркальным окном и выбрасывал именно те тайлы, что + * должны лечь ПОВЕРХ персонажа: передняя грань колонны переставала рисоваться + * и Кид оказывался перед ней (найдено пользователем на уровне 9). */ +int pop_t_fclip_ly0, pop_t_fclip_ly1; +uint8_t pop_t_fclip_on; +uint8_t pop_t_bake_rest; + +/* Позиция склянки внутри тайла зелья — поля fore_x/fore_y записи 0x0A той же + * таблицы. Вынесены МАКРОСАМИ (одно место правки, дублирования данных нет): + * пока таблица была `static const`, SDCC сворачивала `tile_table[10].fore_x` + * в константу, но глобальную (её читают оба банка) уже не сворачивает — + * чтение полей в рантайме стоило +150 Б и IX-кадр на 13 локалов. */ +#define POT_FORE_X 2 +#define POT_FORE_Y (-3) + +/* ---- Таблица тайлов (piece, seg008.c:27) --------------------------- */ +const piece pop_tile_table[31] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 00 empty */ + { 41, 1, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 01 floor */ + {127, 1, 0, 133, 1, 2, 145, 0, 43, 0, 0, 0}, /* 02 spike */ + { 92, 1, 0, 93, 1, 2, 0, 94, 43, 95, 1, 0}, /* 03 pillar */ + { 46, 1, 0, 47, 1, 2, 0, 48, 43, 49, 3, 0}, /* 04 gate */ + { 41, 1, 1, 35, 1, 3, 145, 0, 36, 0, 0, 0}, /* 05 stuck floor */ + { 41, 1, 0, 42, 1, 2, 145, 0, 96, 0, 0, 0}, /* 06 close button */ + { 46, 1, 0, 0, 0, 2, 0, 0, 43, 49, 3, 0}, /* 07 door top w/floor */ + { 86, 1, 0, 87, 1, 2, 0, 0, 43, 88, 1, 0}, /* 08 big pillar bottom */ + { 0, 0, 0, 89, 0, 3, 0, 90, 0, 91, 1, 3}, /* 09 big pillar top */ + { 41, 1, 0, 42, 1, 2, 145, 0, 43, 12, POT_FORE_X, POT_FORE_Y}, /* 0A potion */ + { 0, 1, 0, 0, 0, 0, 145, 0, 0, 0, 0, 0}, /* 0B loose floor */ + { 0, 0, 0, 0, 0, 2, 0, 0, 85, 49, 3, 0}, /* 0C door top */ + { 75, 1, 0, 42, 1, 2, 0, 0, 43, 77, 0, 0}, /* 0D mirror */ + { 97, 1, 0, 98, 1, 2, 145, 0, 43,100, 0, 0}, /* 0E debris */ + {147, 1, 0, 42, 1, 1, 145, 0,149, 0, 0, 0}, /* 0F open button */ + { 41, 1, 0, 37, 0, 0, 0, 38, 43, 0, 0, 0}, /* 10 leveldoor left */ + { 0, 0, 0, 39, 1, 2, 0, 40, 43, 0, 0, 0}, /* 11 leveldoor right */ + { 0, 0, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 12 chomper */ + { 41, 1, 0, 42, 1, 2, 0, 0, 43, 0, 0, 0}, /* 13 torch */ + { 0, 0, 0, 1, 1, 2, 0, 2, 0, 0, 0, 0}, /* 14 wall */ + { 30, 1, 0, 31, 1, 2, 0, 0, 43, 0, 0, 0}, /* 15 skeleton */ + { 41, 1, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 16 sword */ + { 41, 1, 0, 10, 0, 0, 0, 11, 43, 0, 0, 0}, /* 17 balcony left */ + { 0, 0, 0, 12, 1, 2, 0, 13, 43, 0, 0, 0}, /* 18 balcony right */ + { 92, 1, 0, 42, 1, 2, 145, 0, 43, 95, 1, 0}, /* 19 lattice pillar */ + { 1, 0, 0, 0, 0, 0, 0, 0, 2, 9, 0,-53}, /* 1A lattice down */ + { 3, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1B lattice small */ + { 4, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1C lattice left */ + { 5, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1D lattice right */ + { 97, 1, 0, 98, 1, 2, 0, 0, 43,100, 0, 0}, /* 1E debris w/torch */ +}; + +/* Деление координаты на ширину тайла ТАБЛИЦЕЙ (tile_div_tbl/tile_mod_tbl, + * seg006:702/23A6) — как в оригинале. Живут в резиденте, потому что нужны + * и карте (банк 3, колонка персонажа), и слою фона (банк 2, футпринт), а + * const-таблицу чужого банка не прочитать. + * + * Почему не деление: SDCC разворачивает / и % в __divsint + __modsint, + * причём __modsint внутри снова зовёт __divsint — замер в MAME дал 5 400 + * тактов на один вызов при 13+ вызовах за кадр. Индекс — координата как + * есть (0..255), смещение -58 уже внутри значений: POP_TILE_DIV[x] = + * floor((x - 58) / 14), POP_TILE_MOD[x] — остаток. Сверено с SDLPoP + * побайтно. */ +const int8_t POP_TILE_DIV[256] = { + -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, +}; +const uint8_t POP_TILE_MOD[256] = { + 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, + 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, + 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, +}; + +const uint8_t POP_COL_XH[10] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; +const uint8_t POP_WALL_FRAM_BOTTOM[4] = {7, 9, 5, 3}; +const uint8_t POP_WALL_FRAM_MAIN[4] = {8, 10, 6, 4}; +const uint8_t POP_SPIKES_FRAM_LEFT[10] = {0, 128, 129, 130, 131, 132, 131, 129, 128, 0}; +const uint8_t POP_LOOSE_FRAM_LEFT[12] = {41,69,41,70,70,41,41,41,70,70,70,0}; +const uint8_t POP_LOOSE_FRAM_BOTTOM[12] = {43,73,43,74,74,43,43,43,74,74,74,0}; + +/* ---- Чомпер: номер позы и передние куски (seg008 chomper_fram1/for) --- + * Резидент, а не банк: позу считает и полная отрисовка тайла (холодная + * половина, pop_room.c), и fore-проход поверх персонажа (горячая, + * pop_bg.c) — а const-таблица банка из чужого банка не читается вовсе. */ +static const uint8_t CHOMP_FRAM1[7] = {3, 2, 0, 1, 4, 3, 3}; +const uint8_t POP_CHOMP_FRAM_FOR[5] = {106, 107, 108, 109, 110}; + +uint8_t pop_chomp_pose(uint8_t modif) +{ + uint8_t m = (uint8_t)(modif & 0x7F); + /* В ЗАПЕЧКЕ — поза ПОКОЯ, по той же причине, что у плиты и пик: фаза + * челюстей транзиентна. Покой и есть фаза 0 (она же 6, на которой + * animate_chomper снимает trob): CHOMP_FRAM1[0] == CHOMP_FRAM1[6]. */ + if (pop_t_bake_rest) m = 0; + else if (m > 6) m = 6; + return CHOMP_FRAM1[m]; +} + +/* get_loose_frame (seg008:761): индекс дрожащего кадра по модификатору. + * В режиме ЗАПЕЧКИ фона отдаёт кадр ПОКОЯ — транзиентный кадр нельзя + * консервировать в ОЗУ-копию (см. pop_t_bake_rest в pop_bg.c). */ +uint8_t pop_loose_frame(uint8_t m) +{ + if (pop_t_bake_rest) return 0; + if (m & 0x80) { m &= 0x7F; if (m > 10) return 1; } + return m; +} + +/* Кадр пик по их модификатору: 1..4 — выдвижение, 0x80|n — «вышли целиком» + * (один кадр 5), 6..8 — уход. Как и у плиты, в режиме ЗАПЕЧКИ отдаёт кадр + * ПОКОЯ (0 = пик не видно): выдвинутая пика — транзиент, её место в ОЗУ-копии + * фона делает heal вечным (BUG-SPIKE-BAKED, см. BUGS_CLOSED.md). + * + * Отдельная функция, а не `(m & 0x80) ? 5 : m` по месту: считают кадр ЧЕТЫРЕ + * слоя (draw_tile_anim, draw_tile_anim_right соседа, fore-проход и + * mid-оверлей), и одного забытого хватило, чтобы пики законсервировались. */ +uint8_t pop_spike_frame(uint8_t m) +{ + if (pop_t_bake_rest) return 0; + return (uint8_t)((m & 0x80) ? 5 : m); +} + +/* ---- «Фон трогали ВОТ ЗДЕСЬ» (DRAW-COST; контракт — pop_cdraw.h) ----- * + * + * Метка — БИТОВАЯ МАСКА ТАЙЛОВ, по слову на ряд и по набору на страницу. + * Раньше здесь лежал ОДИН union-прямоугольник на страницу, и это убивало + * пропуск перерисовки персонажа: три факела комнаты трогают по пятнышку + * 16x18 в колонках 1, 6 и 8, а их объединение — полоса x 40..280 на всю + * комнату. Кид, стоящий где угодно между крайними факелами, попадал в неё + * и перерисовывался каждый кадр со всем fore-проходом (замер: циан 231 829 + * тактов против 20 071, период 4 растровых кадра против 3). + * + * ГРАНУЛЯРНОСТЬ: колонка 32 px по горизонтали, ТОЧНЫЙ диапазон y по + * вертикали. Раньше по вертикали стоял номер ряда (три полосы по 63 px), и + * это склеивало касания, разнесённые внутри ряда. Найдено пользователем на + * сцене 11/15 (2026-08-19): пламя факела занимает y 5..22, клинок стоящего + * стража — y 31..37, между ними девять пикселей чистого зазора, а метка + * считала слот задетым, потому что оба попадают в ряд 0 и колонку 7. + * Стоило это 148 302 такта в кадр — 23 % работы — на перерисовку персонажа, + * которого никто не трогал. + * + * Почему диапазон, а не более мелкие полосы: полосы по 16 px эту пару всё + * равно склеивают (пламя кончается в полосе 1, клинок в ней же начинается), + * а 8-пиксельные потребовали бы 24 маски на страницу. Пара ymin/ymax на + * колонку — 40 байт на обе страницы, точнее любых полос и без битовой возни. + * + * У ОРИГИНАЛА такой метки нет вовсе: и Apple II (FRAMEADV.S RedBlockFast с + * шестью буферами wipebuf/redbuf/movebuf/floorbuf/halfbuf/objbuf), и SDLPoP + * (set_redraw_fore в redraw_at_char) метят ЦЕЛЫМИ тайлами, но им это не + * мешает — персонаж у них рисуется каждый кадр безусловно, а пометки нужны + * только фону. Пропуск неизменившегося персонажа — наша добавка, поэтому и + * точность метки нужна выше оригинальной. См. docs/impl_diff.md. */ +uint8_t pop_cd_dirty; +/* [страница][колонка] — диапазон затронутых экранных y. Пусто = ymin > ymax + * (заполняется как ymin = 255, ymax = 0). */ +uint8_t pop_cd_ymin[2][10], pop_cd_ymax[2][10]; + +/* CD_LOW, cd_row_of и cd_cols_of СНЯТЫ вместе с переходом на диапазон y: + * колонка теперь считается прямым сдвигом (x >> 5), а вертикаль сравнением + * отрезков — битовые маски больше не нужны. */ + +/* Обе страницы помечаются сразу (персонаж чинится на каждой в свой кадр), + * поэтому цикл развёрнут: индекс-переменная заставляла SDCC считать адрес + * каждой ячейки сложением. Зовётся с КАЖДОГО блита фона. */ +/* Пакетная пометка (pop_cd_batch_begin/end). Один тайл рисуется пятью-шестью + * спрайтами, и каждый звал pop_cd_touch со своими четырьмя 16-битными + * аргументами — 2 866 тактов на спрайт, 15% всей цены блита (замер + * 2026-08-13). В пакете спрайты копят ОБЩИЙ прямоугольник, а настоящая + * пометка ставится одна на тайл. + * + * Объединение ЗАГРУБЛЯЕТ прямоугольник (накрывает и то, чего между кусками + * не касались), и это безопасно ровно в одну сторону: лишняя пометка стоит + * лишней перерисовки персонажа, пропущенная — стёртого и не восстановленного + * персонажа. Практически загрубление почти нулевое: все куски одного тайла + * и так лежат в тех же 1-2 колонках сетки 32px и том же ряду. */ +uint8_t pop_cd_batch; +static int cdb_x0, cdb_y0, cdb_x1, cdb_y1; + +void pop_cd_batch_begin(void) +{ + pop_cd_batch = 1; + cdb_x0 = cdb_y0 = 32767; + cdb_x1 = cdb_y1 = -32768; +} + +void pop_cd_batch_end(void) +{ + pop_cd_batch = 0; /* СНАЧАЛА снять: ниже настоящий вызов */ + if (cdb_x1 >= cdb_x0 && cdb_y1 >= cdb_y0) + pop_cd_touch(cdb_x0, cdb_y0, cdb_x1 - cdb_x0 + 1, cdb_y1 - cdb_y0 + 1); +} + +/* Подавление на серию блитов — см. шапку в pop_cdraw.h. Отдельным значением + * того же флага, а не вторым флагом: у pop_cd_touch тогда остаётся ОДНА + * проверка на общем пути. */ +#define CD_BATCH_MUTE 2 + +void pop_cd_mute(void) { pop_cd_batch = CD_BATCH_MUTE; } +void pop_cd_unmute(void) { pop_cd_batch = 0; } + +void pop_cd_touch(int x, int y, int w, int h) +{ + if (pop_cd_batch) { /* копим, не разбирая на колонки/ряды */ + if (pop_cd_batch == CD_BATCH_MUTE) return; /* область помечена вызывающим */ + int x1 = x + w - 1, y1 = y + h - 1; + if (x < cdb_x0) cdb_x0 = x; + if (y < cdb_y0) cdb_y0 = y; + if (x1 > cdb_x1) cdb_x1 = x1; + if (y1 > cdb_y1) cdb_y1 = y1; + return; + } + { + int8_t c0, c1, c; + uint8_t y0, y1; + int xr = x + w - 1; + if (xr < 0 || x > 319) return; /* весь прямоугольник вне поля */ + if (x < 0) x = 0; + if (xr > 319) xr = 319; + c0 = (int8_t)(x >> 5); + c1 = (int8_t)(xr >> 5); + /* Клип по вертикали: экранные y не выходят за байт, а всё, что выше + * поля или ниже его, персонажам всё равно не принадлежит. */ + if (y < 0) y = 0; + if (y > 255) return; + y0 = (uint8_t)y; + y1 = (y + (int)h - 1 > 255) ? 255 : (uint8_t)(y + h - 1); + /* Страницы обновляются НЕЗАВИСИМО. Общее условие по странице 0 + * («если ей стало теснее — записать в обе») ломается сразу после + * pop_cd_clear(0): страница 1 хранит свои старые границы, условие по + * нулевой уже не выполняется, и её метка перестаёт расти. */ + for (c = c0; c <= c1; c++) { + if (y0 < pop_cd_ymin[0][c]) pop_cd_ymin[0][c] = y0; + if (y1 > pop_cd_ymax[0][c]) pop_cd_ymax[0][c] = y1; + if (y0 < pop_cd_ymin[1][c]) pop_cd_ymin[1][c] = y0; + if (y1 > pop_cd_ymax[1][c]) pop_cd_ymax[1][c] = y1; + } + } + pop_cd_dirty = 3; +} + +/* Задел ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 ВКЛЮЧИТЕЛЬНО) то, что + * трогали на странице p. Резидент: зовёт pop_cdraw.c из банка 4 прямым + * вызовом, без трамплина. */ + +/* ---- Блит куска атласа ---------------------------------------------- */ +/* Клипованный путь — ОТДЕЛЬНОЙ функцией. У него девять 16-битных локалей, + * и SDCC заводит под них кадр IX; пока он жил в общем теле, за этот кадр + * платили и блиты БЕЗ клипа (`ld -n(ix)` = 19 тактов номинала, у нас ~46). + * А блитов без клипа большинство: весь фон вне fore-прохода — факелы, зелья, + * перерисовка тайлов — идёт с pop_t_fclip_on == 0. */ +#define FLIP_TOP(top, h) POP_FLIP_TOP(top, h) /* формула — в pop_bg.h */ + + +/* Клипованный путь блита. Габарит и смещения внутри куска — БАЙТЫ, а вся + * рабочая переменная — file-scope, НЕ локали. + * + * Корректность байтового габарита по построению: кадры наших атласов не + * крупнее 56x63 (просканированы все 109 .atl, docs/perf_l13_room23.md §5), а + * кадр больше 255 в pop_blit_b уходит в blit_b_oversize и сюда не попадает. + * + * Зачем file-scope: с локалями SDCC заводил 22-байтовый стековый кадр и 211 + * обращений `-N(ix)` — при ~46 замеренных тактах на обращение это ~9 700 + * такта из 14 088 цены клипованного блита (замер 2026-08-17, + * docs/perf_green_phase.md §1.6). Одного байтового габарита НЕ ХВАТИЛО: + * кадр остался, обращений стало 173 — значений, живых через шесть вызовов + * ядер libbgi, всё равно больше, чем регистров у Z80. С file-scope + * спиливать нечего, а адресация абсолютная (13 тактов против 19). + * Тот же приём и по той же причине применён к draw_tile (pop_room.c). + * + * Реентерабельности не было и не требуется: единственный вызывающий — + * pop_blit_b, и он себя не зовёт. + * + * Арифметика клипа считается в int16 (bc_d), в байт уходит только результат — + * иначе вычитание ушло бы в перенос по модулю 256. */ +static const uint8_t *bc_img; +static int bc_dx, bc_dy; +static int16_t bc_d; +static uint8_t bc_w, bc_h, bc_dw, bc_dh, bc_sx, bc_sy; + +void blit_b_clip(const uint8_t *img, int x, int top, uint8_t w, uint8_t h) +{ + bc_img = img; bc_w = w; bc_h = h; + bc_dx = x; bc_dy = top; + bc_dw = w; bc_dh = h; bc_sx = 0; bc_sy = 0; + if (!pop_upside) { + if (pop_t_clip_top && bc_dy < pop_t_clip_top) { /* полоса у потолка */ + bc_d = (int16_t)(pop_t_clip_top - bc_dy); + if (bc_d >= (int16_t)bc_dh) return; + bc_sy = (uint8_t)bc_d; bc_dy = pop_t_clip_top; + bc_dh = (uint8_t)(bc_dh - bc_sy); + } + } else { + /* ПЕРЕВОРОТ: то, что в обычном виде торчало ВЫШЕ поля (полоса кладки + * у потолка), теперь торчит НИЖЕ него — и без обрезки лезет в борт, + * где живёт полоса HP (баг «мусор ниже 192», найден пользователем). + * Режем по границам поля ВСЕГДА, а не по pop_t_clip_top: зеркальный + * спрайт может вылезти и там, где в обычном виде клип не ставили. */ + if (bc_dy < POP_YOFF) { + bc_d = (int16_t)(POP_YOFF - bc_dy); + if (bc_d >= (int16_t)bc_dh) return; + bc_sy = (uint8_t)bc_d; bc_dy = POP_YOFF; + bc_dh = (uint8_t)(bc_dh - bc_sy); + } + if (bc_dy + bc_dh > POP_CLIP_BOTTOM) { + bc_d = (int16_t)(POP_CLIP_BOTTOM - bc_dy); + if (bc_d <= 0) return; + bc_dh = (uint8_t)bc_d; + } + } + if (pop_t_fclip_on) { /* окно fore-слоя / перерисовки */ + if (bc_dx < pop_t_fclip_x0) { + bc_d = (int16_t)(pop_t_fclip_x0 - bc_dx); + if (bc_d >= (int16_t)bc_dw) return; + bc_sx = (uint8_t)(bc_sx + (uint8_t)bc_d); + bc_dx += bc_d; bc_dw = (uint8_t)(bc_dw - (uint8_t)bc_d); + } + if (bc_dy < pop_t_fclip_y0) { + bc_d = (int16_t)(pop_t_fclip_y0 - bc_dy); + if (bc_d >= (int16_t)bc_dh) return; + bc_sy = (uint8_t)(bc_sy + (uint8_t)bc_d); + bc_dy += bc_d; bc_dh = (uint8_t)(bc_dh - (uint8_t)bc_d); + } + if (bc_dx + bc_dw > pop_t_fclip_x1) { + bc_d = (int16_t)(pop_t_fclip_x1 - bc_dx); + if (bc_d <= 0) return; + bc_dw = (uint8_t)bc_d; + } + if (bc_dy + bc_dh > pop_t_fclip_y1) { + bc_d = (int16_t)(pop_t_fclip_y1 - bc_dy); + if (bc_d <= 0) return; + bc_dh = (uint8_t)bc_d; + } + } + if (!bc_dw || !bc_dh) return; + /* ПЕРЕВОРОТ: экранная строка (dy+i) берёт источник (h-1-i), поэтому + * обрезка СВЕРХУ экрана съедает НИЖНИЕ строки источника. В терминах + * куска источника это sy' = h - sy - dh (при обрезке только сверху даёт + * 0, при обрезке только снизу — верхний край). */ + /* Проверки `dw < 256 && dh < 256 && w < 256` убраны как тождественные: + * это уже байты. */ + if (pop_upside) { + bc_d = (int16_t)bc_h - (int16_t)bc_sy - (int16_t)bc_dh; + if (bc_d < 0) bc_d = 0; + if (bc_dx >= 0 && bc_dy >= 0 && + bc_dx + bc_dw <= 320 && bc_dy + bc_dh <= 256) + gfx_blit_part_noclip_vflip(bc_dx, bc_dy, bc_img, bc_sx, + (uint8_t)bc_d, bc_dw, bc_dh); + else + gfx_blit_part_vflip(bc_dx, bc_dy, bc_img, bc_sx, + (uint8_t)bc_d, bc_dw, bc_dh); + } else + if (bc_dx >= 0 && bc_dy >= 0 && + bc_dx + bc_dw <= 320 && bc_dy + bc_dh <= 256) { + if (bc_sx == 0 && bc_sy == 0 && bc_dw == bc_w && bc_dh == bc_h) + gfx_blit_noclip(bc_dx, bc_dy, bc_img); + else + gfx_blit_part_noclip(bc_dx, bc_dy, bc_img, bc_sx, bc_sy, + bc_dw, bc_dh); + } else { + gfx_blit_part(bc_dx, bc_dy, bc_img, bc_sx, bc_sy, bc_dw, bc_dh); + } + /* Кусок ФОНА лёг туда, где может лежать уже нарисованный персонаж — + * пометить область (DRAW-COST, pop_cdraw.h). Признак «это не fore-проход + * поверх персонажа» — окно клипа: его ставит только он. */ + if (!pop_t_fclip_on) pop_cd_touch(bc_dx, bc_dy, bc_dw, bc_dh); +} + +/* Кадр шире или выше 255 — общий (медленный) путь. Вынесен отдельно, чтобы + * 16-битная арифметика габарита не жила ни в горячем pop_blit_b, ни в + * blit_b_clip (у того габарит теперь БАЙТОВЫЙ, см. его шапку). У нас таких + * кадров в игровом кадре нет вовсе (максимум 56x63), но контракт формата + * .atl их допускает, а интро/финал такие подложки принесут (320x200) — + * см. docs/perf_l13_room23.md §5. + * + * Клипа полосы у потолка и окна перерисовки здесь нет намеренно: такой кадр + * — полноэкранная подложка, ей ни то, ни другое не адресовано; за клип по + * экрану отвечает само ядро gfx_blit_part. */ +static void blit_b_oversize(const uint8_t *img, int x, int ybottom) +{ + uint16_t w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8)); + uint16_t h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8)); + int top = ybottom - (int)h + 1 + POP_YOFF; + if (pop_upside) top = FLIP_TOP(top, h); + if (!w || !h) return; + gfx_blit_part(x, top, img, 0, 0, (int)w, (int)h); + if (!pop_t_fclip_on) pop_cd_touch(x, top, (int)w, (int)h); +} + +/* Рабочие переменные pop_blit_b — file-scope, не локали и не стековые + * аргументы (см. пролог функции). Реентерабельности нет и не требуется: + * pop_blit_b себя не зовёт, а blit_b_clip держит СВОЙ набор (bc_*). */ +static atlas_t *pb_a; +static uint8_t pb_idx, pb_w, pb_h; +static int pb_x, pb_yb, pb_top; +static const uint8_t *pb_img; + +void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom) +{ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча, см. pop_pace.h */ + /* Аргументы сразу в file-scope: третий и дальше SDCC передаёт СТЕКОМ, и + * дальше каждое их чтение — `-N(ix)` (замер 2026-08-17: 76 таких + * обращений в теле, ~3 500 такта из 14 500 цены блита). То же лечение, + * что у draw_tile и blit_b_clip. */ + pb_a = a; pb_idx = idx; pb_x = x; pb_yb = ybottom; + if (pb_idx >= pb_a->count) + return; + /* Грубый отсев ДО маппинга страницы: реальные pb_w/pb_h лежат в заголовке + * ленты, то есть в EMM-странице, и чтобы их прочитать, надо сделать + * gfx_w0_map. Берём заведомо больший габарит (кадры фона не крупнее + * 64×64) и, если он не задевает окно, уходим не заплатив ни за + * atlas_image, ни за map/unmap. */ + if (pop_t_fclip_on && + (pb_x >= pop_t_fclip_x1 || pb_x + 64 <= pop_t_fclip_x0 || + pb_yb + POP_YOFF + 1 <= pop_t_fclip_ly0 || + pb_yb + POP_YOFF - 64 >= pop_t_fclip_ly1)) + return; + pb_img = (const uint8_t *)atlas_image(pb_a, pb_idx); + gfx_w0_map(pb_a->page); + /* Габарит читаем БАЙТАМИ. С `uint16_t pb_w = pb_img[0] | (pb_img[1] << 8)` SDCC + * разворачивал каждую такую строку в два десятка IX-относительных + * пересылок: 16-битные pb_w/pb_h не влезали в регистры, и функция целиком + * уезжала в стековый кадр (пролог `ld iy,#-14 / add iy,sp / ld sp,iy`, + * дальше всё через -N(ix) по 19 тактов). Замер 2026-08-13: до вызова + * ядра блита уходило ~6 700 тактов при том, что работы там — прочитать + * четыре байта заголовка и сравнить границы. + * Байтовый габарит корректен по построению: обе ветки ниже И ТАК требуют + * pb_w < 256 && pb_h < 256 (иначе ядро не примет), а кадры наших атласов не + * крупнее 32x63. Формат .atl допускает больше — такой кадр уходит на + * общий путь выше. */ + if (pb_img[1] | pb_img[3]) { /* кадр больше 255 — редкий путь */ + blit_b_oversize(pb_img, pb_x, pb_yb); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + gfx_w0_unmap(); + return; + } + pb_w = pb_img[0]; + pb_h = pb_img[2]; + pb_top = pb_yb - (int)pb_h + 1 + POP_YOFF; /* +YOFF: центрирование */ + /* Переворот применяем ЗДЕСЬ, до клипа: дальше вся геометрия (окно + * fore-слоя, полоса у потолка, отсев по экрану) считается уже в + * экранных координатах результата. */ + if (pop_upside) pb_top = FLIP_TOP(pb_top, pb_h); + if (pb_w && pb_h) { + if (!pop_t_fclip_on && !pop_t_clip_top) { + /* БЫСТРЫЙ путь: клипа нет вовсе. Так идут все блиты фона вне + * fore-прохода. Линейное ядро без клипа — ~2.9× быстрее общего + * (замер: 4617 против 13288 тактов на спрайт 32×3; gfx_blit + * платит за клип/16-бит/split, а не за пиксели). W3-скобку + * ставит сама libbgi: из кода с --w3 её вызывать нельзя. */ + /* В перевёрнутом виде быстрый путь годится, только если спрайт + * целиком внутри поля: иначе он залезет в борт (см. blit_b_clip). */ + if (pop_upside && (pb_top < POP_YOFF || pb_top + (int)pb_h > POP_CLIP_BOTTOM)) { + blit_b_clip(pb_img, pb_x, pb_top, pb_w, pb_h); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + gfx_w0_unmap(); + return; + } + /* pb_w<256 && pb_h<256 больше не проверяем — гарантировано типом. */ + /* «Целиком на экране?» — ДВУМЯ беззнаковыми сравнениями вместо + * четырёх знаковых. Отрицательная координата в беззнаковом виде + * становится очень большой и проваливает то же условие, что + * проверка `>= 0`, а верхняя граница переносится в правую часть, + * так что сложение уходит вместе с ней. Знаковое сравнение у + * SDCC z80 стоит дорого: пара `sbc` плюс `jp PO / xor 0x80 / jp P` + * на каждое (видно в листинге). + * + * Границы правой части неотрицательны по построению: pb_w и pb_h + * не больше 255, значит 320-pb_w >= 65 и 256-pb_h >= 1. */ + if ((unsigned int)pb_x <= (unsigned int)(320 - (int)pb_w) && + (unsigned int)pb_top <= (unsigned int)(256 - (int)pb_h)) { + if (pop_upside) gfx_blit_noclip_vflip(pb_x, pb_top, pb_img); + else gfx_blit_noclip(pb_x, pb_top, pb_img); + } else { + if (pop_upside) gfx_blit_part_vflip(pb_x, pb_top, pb_img, 0, 0, (int)pb_w, (int)pb_h); + else gfx_blit_part(pb_x, pb_top, pb_img, 0, 0, (int)pb_w, (int)pb_h); + } + pop_cd_touch(pb_x, pb_top, (int)pb_w, (int)pb_h); + } else { + blit_b_clip(pb_img, pb_x, pb_top, pb_w, pb_h); + } + } + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + gfx_w0_unmap(); +} + +void pop_env_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_env[id >> POP_ENV_SHIFT], (uint8_t)(id & POP_ENV_MASK), x, yb); } +void pop_wall_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_wall_at, id, x, yb); } +void pop_fore_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_fore_at, id, x, yb); } +void pop_pot_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_pot_at, id, x, yb); } + + + +/* ---- Чтение тайла отрисованной комнаты ------------------------------ */ +uint8_t pop_tile_code(int row, int col) +{ + if (col < 0) { + if (pop_t_lfg && row >= 0 && row < 3) return pop_t_lfg[row]; + return 20; /* уровневая кромка = стена */ + } + if (row < 0) { /* верхняя полоса: ряд 2 комнаты сверху */ + if (row == -1 && pop_t_above && col < 10) return pop_t_above[col]; + return 1; /* нет комнаты сверху → пол (кромка) */ + } + if (row > 2 || col > 9) return 20; + return pop_t_fg[row * 10 + col]; +} + +uint8_t pop_tile_mod(int row, int col) +{ + if (col < 0) { + if (pop_t_lbg && row >= 0 && row < 3) return pop_t_lbg[row]; + return 0; + } + if (row == -1 && pop_t_abovemod && col < 10) return pop_t_abovemod[col]; + if (row < 0 || row > 2 || col > 9) return 0; + return pop_t_bg[row * 10 + col]; +} + +/* Порт подстановки нажатой кнопки из get_tile_to_draw (seg008:240): opener → + * floor, closer → stuck, когда таймер связи > 1. Любой слой, который + * выбирает ветку отрисовки по типу тайла, обязан смотреть на ПОДСТАВЛЕННЫЙ + * тип (иначе кнопка, на которой стоит Kid, ведёт себя как «не пол» — напр. + * climb-оверлей уходил в draw_other_overlay и закрашивал Kid целым тайлом + * вместо узкой кромки floor_left_overlay). */ +uint8_t pop_tile_code_drawn(int row, int col) +{ + uint8_t code = pop_tile_code(row, col); + if (code == 0x0F) { if ((pop_doorlink2(pop_tile_mod(row, col)) & 0x1F) > 1) code = 1; } + else if (code == 0x06) { if ((pop_doorlink2(pop_tile_mod(row, col)) & 0x1F) > 1) code = 5; } + return code; +} + +uint8_t pop_wall_modifier(int row, int col) +{ + uint8_t lw = (col > 0) ? (pop_tile_code(row, col - 1) == 20) : 1; + uint8_t rw = (col < 9) ? (pop_tile_code(row, col + 1) == 20) : 1; + if (lw && rw) return 3; /* WWW */ + if (lw) return 2; /* WWS */ + if (rw) return 1; /* SWW */ + return 0; /* SWS */ +} + + +/* Окно fore-клипа: ставит тот, кто нарисовал персонажа (pop_cdraw, банк 4), + * читает blit_b. Резидент — чтобы вызов из банка шёл без трамплина. */ +void pop_fore_set_clip(int x, int y, int w, int h) +{ + pop_t_fclip_x0 = x; pop_t_fclip_y0 = y; + pop_t_fclip_x1 = x + w; pop_t_fclip_y1 = y + h; + if (pop_upside) { /* вернуть окно в неперевёрнутый вид */ + pop_t_fclip_ly0 = 2 * POP_YOFF + POP_PLAYFIELD_H - (y + h); + pop_t_fclip_ly1 = 2 * POP_YOFF + POP_PLAYFIELD_H - y; + } else { + pop_t_fclip_ly0 = y; pop_t_fclip_ly1 = y + h; + } +} + diff --git a/applications/SprPoP/src/pop_tile.h b/applications/SprPoP/src/pop_tile.h new file mode 100644 index 0000000..8de2a70 --- /dev/null +++ b/applications/SprPoP/src/pop_tile.h @@ -0,0 +1,188 @@ +/* + * pop_tile.h — ОБЩИЕ ЛИСТЬЯ отрисовки фона: блит куска атласа, чтение тайла + * комнаты, таблица тайлов. Внутренний заголовок слоя фона, НЕ публичный + * API (публичный — pop_bg.h). + * + * ПОЧЕМУ ОТДЕЛЬНЫЙ РЕЗИДЕНТНЫЙ МОДУЛЬ (pop_tile.c в _CODE / W1), а не часть + * банка 2. Слой фона режется надвое: горячая половина (fore-проход поверх + * персонажей, каждый кадр) и холодная (полная отрисовка комнаты и точечные + * перерисовки тайлов, раз на комнату/событие). Обе половины зовут одни и те + * же листья — blit_b, tile_code, tile_table. Разложить их по банкам нельзя: + * + * - код в чужом банке зовётся ТОЛЬКО через трамплин (по `__banked` в + * объявлении), и пометка бьёт по ВСЕМ вызывающим, включая горячих; + * - `const`-таблицы банка лежат в его СТРАНИЦЕ (0xC000+) и видны, лишь + * пока банк замаплен — из другого банка их не прочитать вовсе; + * - дублировать листья в оба банка — лишние килобайты и два места правки. + * + * Резидент W1 замаплен ВСЕГДА, поэтому и горячая, и холодная половина зовут + * их обычным `call` без трамплина, а таблицы читают напрямую. Тот же приём, + * что у pop_heal_fast (pop_draw.c) и pop_sword_draw (pop_kid.c). + * + * ПИСУЧИЕ глобалы (атласы, указатели на комнату, окно клипа) лежат в _DATA + * (W2) и видны отовсюду независимо от банка — они здесь ради одного места + * определения, а не ради доступности. + */ +#ifndef POP_TILE_H +#define POP_TILE_H + +#include +#include +#include "pop_geom.h" /* pop_rnd_t — сид кладки комнаты */ + +/* ---- Атласы фона (прямая адресация id) ------------------------------ * + * env[id>>POP_ENV_SHIFT] idx id&POP_ENV_MASK — chtab_6 environment; + * wall — chtab_7; fore — передний слой; pot — chtab_1 (зелья, меч). + * Раскладка и число страниц — из pop_bg_atlas.h (генерит упаковщик). + * Тайлсетов ДВА (подземелье/дворец), но раскладка id у них общая: + * подменяются только файлы и 32 записи палитры (см. pop_bg_set_tileset). */ +#include "pop_bg_atlas.h" +extern atlas_t pop_env[POP_ENV_PAGES], pop_wall_at, pop_fore_at, pop_pot_at; + +/* Загруженный тайлсет: 0 подземелье, 1 дворец. Ставит pop_bg_load. По нему + * расходится с десяток веток отрисовки (seg008) и модификатор пустой клетки + * от упавшей плиты (seg007:0EB8). */ +extern uint8_t pop_palace; + +/* ---- Текущая отрисованная комната ----------------------------------- * + * fg — foretable-коды, bg — модификаторы; lfg/lbg — правый столбец ЛЕВОГО + * соседа (кромка); below — ряд 0 комнаты снизу (11 байт, см. pop_bg.h); + * above/abovemod — ряд 2 комнаты сверху (полоса кладки у потолка). */ +extern const uint8_t *pop_t_fg, *pop_t_bg, *pop_t_lfg, *pop_t_lbg; +extern const uint8_t *pop_t_below, *pop_t_above, *pop_t_abovemod; +extern uint8_t pop_t_room; /* номер комнаты — зерно раскладки стен */ +extern pop_rnd_t pop_t_seed; /* сид кладки (seg009 PRNG) */ + +/* Верхний клип блитов фона (ЭКРАННАЯ y): !=0 — строки кадра выше границы не + * рисуются. Нужен полосе кладки у потолка (ряд −1): высокие куски не должны + * лезть в верхний борт. 0 — без клипа. */ +extern int pop_t_clip_top; + +/* ОКНО FORE-СЛОЯ (экранные координаты, полуинтервал). Пока включено, + * blit_b рисует только ту часть куска, что попадает в окно. + * + * Зачем: fore-слой существует РОВНО для того, чтобы вернуть куски тайлов + * поверх нарисованного персонажа — за пределами его прямоугольника в + * видеопамяти и так лежит правильный фон. А рисовал он тайлы ЦЕЛИКОМ: + * стена (код 20) — блок 64×63 = 4032 пикселя, и таких кусков за проход до + * девяти. Замер в MAME 2026-07-29 (комната 3, Kid + страж): проход поверх + * Kid — 402 816 тактов, поверх стража — 431 964, вместе 78 % всей работы + * кадра при бюджете 430 000. С окном большинство кусков отсекается целиком, + * остальные режутся до пересечения. */ +extern int pop_t_fclip_x0, pop_t_fclip_y0, pop_t_fclip_x1, pop_t_fclip_y1; +/* Та же рамка в ЛОГИЧЕСКИХ координатах (без переворота). Всё, что решает + * «задевает ли ТАЙЛ окно», обязано сравнивать по ней: позиция тайла в + * комнате от переворота не зависит. Экранная пара — только для клипа + * блита, который режет уже перевёрнутый прямоугольник. */ +extern int pop_t_fclip_ly0, pop_t_fclip_ly1; +extern uint8_t pop_t_fclip_on; + +/* Поставить то же окно под ТОЧЕЧНУЮ ПЕРЕРИСОВКУ / ЗАПЕЧКУ. Прямоугольник — + * тот, который восстанавливаем (heal или бар-в-чёрный), в ЛОГИЧЕСКИХ + * экранных координатах (с POP_YOFF, но до переворота); экранную пару + * функция считает сама. + * + * Зачем: точечная перерисовка зовёт draw_tile, а тот честно рисует ВЕСЬ тайл. + * У полосы кладки у потолка это особенно дорого: куски ряда 0 высотой 63 px + * рисуются целиком, хотя восстановить надо девять верхних строк (замер + * 2026-08-17: блит в pop_ceil_bake_empty стоит 21 447 против 14 384 в + * pop_floor_bake — ровно разница в высоте). Окно и отсеивает куски, вовсе не + * попавшие в прямоугольник (предфильтр pop_blit_b — ДО atlas_image и + * gfx_w0_map), и режет остальные по нему. + * + * ВАЖНО: пока окно стоит, pop_blit_b НЕ ставит пометку «фон трогали» (признак + * fore-прохода) — вызывающий обязан пометить прямоугольник САМ (pop_cd_touch + * или pop_bar_black, который метит сам). */ +void pop_t_win_set(int x, int ytop, int w, int h) __banked; +void pop_t_win_clear(void) __banked; + +/* ---- Таблица тайлов (piece, seg008.c:27) ---------------------------- */ +typedef struct { + uint8_t base_id, floor_left; int8_t base_y; + uint8_t right_id, floor_right; int8_t right_y; + uint8_t stripe_id, topright_id, bottom_id, fore_id, fore_x; int8_t fore_y; +} piece; + +extern const piece pop_tile_table[31]; + +/* Таблицы, нужные ОБЕИМ половинам слоя фона (односторонние живут по месту). */ +extern const uint8_t POP_COL_XH[10]; /* x тайла = COL_XH[col]*8 */ +/* Деление координаты на ширину тайла таблицей (оригинал: tile_div_tbl / + * tile_mod_tbl, seg006:702). Индекс — координата 0..255 как есть, смещение + * -58 уже внутри: POP_TILE_DIV[x] = floor((x-58)/14), POP_TILE_MOD[x] — + * остаток. Резидент: нужны и карте (банк 3), и слою фона (банк 2). */ +extern const int8_t POP_TILE_DIV[256]; +extern const uint8_t POP_TILE_MOD[256]; +extern const uint8_t POP_WALL_FRAM_BOTTOM[4]; /* нижняя грань стены по bg */ +extern const uint8_t POP_WALL_FRAM_MAIN[4]; /* основная грань стены */ +extern const uint8_t POP_SPIKES_FRAM_LEFT[10]; /* кадр пик в своей ячейке */ +extern const uint8_t POP_LOOSE_FRAM_LEFT[12]; /* верх дрожащей плиты */ +extern const uint8_t POP_LOOSE_FRAM_BOTTOM[12]; /* низ дрожащей плиты */ +extern const uint8_t POP_CHOMP_FRAM_FOR[5]; /* чомпер: перед (pop_fore!) */ + +/* Режим ЗАПЕЧКИ статического фона (запись идёт и в ОЗУ-копию акселератора, + * из которой потом восстанавливает heal). В этом режиме ТРАНЗИЕНТНЫЕ кадры + * рисовать НЕЛЬЗЯ — ни свои, ни соседские: кадр законсервируется в копии, и + * heal будет возвращать его вечно, даже когда ловушка давно вернулась в + * покой (пики: BUG-SPIKE-BAKED). А так как запечка идёт двумя кадрами (по + * одному на страницу дабл-буфера), на страницах могут застыть ещё и РАЗНЫЕ + * кадры — вечное мерцание. Фон обязан быть состоянием ПОКОЯ; анимация + * каждый кадр кладётся поверх (банк SPRITE). + * + * Флаг читают ВСЕ функции «кадр по модификатору»: pop_loose_frame, + * pop_spike_frame, pop_chomp_pose. Появится новая анимированная ловушка — + * её кадр обязан спрашивать флаг ТАМ ЖЕ, а не в вызывающем слое: слоёв + * отрисовки у тайла четыре, и забыть один слишком легко. */ +extern uint8_t pop_t_bake_rest; + +/* Кадр дрожащей/падающей плиты по её room_modif (seg007 loose_fram_*). */ +uint8_t pop_loose_frame(uint8_t modif); + +/* Кадр пик по их modif (индекс в spikes_fram_*): 0 = не видно, 5 = вышли + * целиком. В режиме запечки — всегда 0 (см. pop_t_bake_rest). */ +uint8_t pop_spike_frame(uint8_t modif); + +/* Номер позы чомпера по его модификатору (seg008 chomper_fram1): 0..4 — + * индекс во ВСЕ три набора кусков (низ/верх/перед) и в кровь (+114/+119). */ +uint8_t pop_chomp_pose(uint8_t modif); + +/* ---- Блит куска атласа ---------------------------------------------- * + * НИЗ спрайта на ybottom (room-space, POP_YOFF добавляется внутри), левый + * край на x, прозрачно (0xFF). w/h читаются из getimage-заголовка ленты (в + * W0). Режется окном fore-клипа и pop_t_clip_top. */ +void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom); +/* Блит getimage-блоба из обычной памяти (W2) — без atlas_image и без + * маппинга страницы; клип полосы у потолка и окно перерисовки те же, что у + * pop_blit_b. Для композитных спрайтов, собранных в рантайме. */ +void pop_mem_b(const uint8_t *img, int x, int ybottom) __banked; + +void pop_env_b(uint8_t id, int x, int yb); +void pop_wall_b(uint8_t id, int x, int yb); +void pop_fore_b(uint8_t id, int x, int yb); +void pop_pot_b(uint8_t id, int x, int yb); + +/* heal в room-space (со сдвигом POP_YOFF) — все heal слоя фона/loose. */ +void pop_heal_off(int x, int y, int w, int h) __banked; + +/* Чёрная плита по ЭКРАННЫМ координатам, с учётом переворота (POP_FLIP_TOP). + * Пишет банком 0x50 — и в видео-ОЗУ, и в ОЗУ-копию, иначе heal вернёт + * стёртое. ВСЕ стирания слоя фона обязаны идти через неё, а не через голый + * bar: иначе при перевороте плита ложится в неотражённое место (так + * ломались решётка и плита-потолок). */ +void pop_bar_black(int x, int top, int w, int h) __banked; + +/* ---- Чтение тайла отрисованной комнаты ------------------------------ */ +uint8_t pop_tile_code(int row, int col); +uint8_t pop_tile_mod(int row, int col); +/* Тип «как он РИСУЕТСЯ» — подстановка нажатой кнопки (get_tile_to_draw, + * seg008:240): opener → floor, closer → stuck при таймере связи > 1. */ +uint8_t pop_tile_code_drawn(int row, int col); +/* seg008.c:1255: сосед — стена, если его тип == 20. 0 SWS / 1 SWW / 2 WWS + * / 3 WWW. На кромке col0 сосед по умолчанию = стена (как render_room). */ +uint8_t pop_wall_modifier(int row, int col); + +/* Склянка зелья (draw_tile_fore, chtab_1): нужна и полной отрисовке тайла, и + * fore-проходу поверх персонажа. */ +void pop_potion_flask(int row, int col, uint8_t modif) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_tile_cold.c b/applications/SprPoP/src/pop_tile_cold.c new file mode 100644 index 0000000..f4daa3f --- /dev/null +++ b/applications/SprPoP/src/pop_tile_cold.c @@ -0,0 +1,240 @@ +/* + * pop_tile_cold.c — ХОЛОДНАЯ половина общих листьев слоя фона (банк 5). + * + * ЗАЧЕМ РАЗРЕЗ. pop_tile.c был самым крупным жильцом резидента (5 972 Б + * кода), а резидент huge-режима — это всё окно 0x4100..0xBB00, и место в нём + * нужно под музыку, заставки и UI. Но целиком pop_tile не уедет: его + * const-таблицы (POP_TILE_DIV/MOD, pop_tile_table, таблицы кадров) читают + * банки 2, 3, 7 и 8, а таблица в чужом банке не видна. + * + * КРИТЕРИЙ ОТБОРА — ЗАМЕР, а не догадка. Каждая функция pop_tile была + * посчитана брейкпоинтом-счётчиком в MAME: сколько вызовов в кадре покоя и + * сколько в кадре ПОЛНОЙ перерисовки комнаты (её форсирует чит +/-). Сюда + * переехало только то, у чего пик <= 3 вызовов на кадр — и проверено это + * на ДВУХ тайлсетах: подземелье (уровень 1) и дворец (уровень 4), потому + * что pop_mem_b рисует композитный кусок и мог оказаться дворцовым. + * Замер: pop_mem_b 0, pop_cd_hit 0, pop_t_win_set 0..1, pop_heal_off 0..1, + * pop_room_set_above 1, pop_bar_black 3, pop_potion_flask 0..1, + * pop_cd_hit_slot 2..3 — при канарейке pop_blit_b 184..214 в том же кадре. + * + * ЦЕНА. Трамплин замерен: 252 такта пролог + 84 эпилог + ~50 на стороне + * вызывающего = ~410 тактов на вызов. На кадр покоя выходит ~1 000 тактов + * (0,2 % работы кадра), на кадр редрава ~3 700 (0,009 растра). + * + * ЧТО ОСТАЛОСЬ В РЕЗИДЕНТЕ и почему: pop_blit_b (184 вызова на редрав) со + * своими статиками blit_b_clip/blit_b_oversize, pop_cd_touch (198), + * pop_tile_code (296), pop_wall_modifier (101), pop_env_b (73), + * pop_tile_mod (70), pop_cd_batch_end (40) — и все const-таблицы. + * pop_fore_set_clip оставлен намеренно: 88 Б не стоят отказа от прямого + * вызова из банка 4, ради которого он и заводился. + * + * ВЫЗОВЫ ОТСЮДА В РЕЗИДЕНТ — прямые, без трамплина: W1/W2 замаплены всегда. + * Поэтому blit_b_clip (статик горячей половины) просто перестал быть static + * и объявлен в _pop_tile.h, а не переехал следом. + */ + +#include +#include +#include +#include "pop_bg.h" /* POP_YOFF, POP_PLAYFIELD_H */ +#include "pop_map.h" /* pop_upside — переворот (зелье инверсии) */ +#include "pop_cdraw.h" /* pop_cd, pop_cd_dirty, pop_cd_ymin/ymax */ +#include "pop_tile.h" +#include "_pop_draw.h" /* pop_heal_fast */ +#include "_pop_tile.h" /* blit_b_clip — из горячей половины */ + +#define FLIP_TOP(top, h) POP_FLIP_TOP(top, h) /* формула — в pop_bg.h */ + +/* Смещение склянки внутри тайла (seg008 draw_tile_fore): те же числа, что в + * tile_table горячей половины — дублируем, потому что там они локальные + * define'ы, а тащить их в публичный заголовок ради одной функции незачем. */ +#define POT_FORE_X 2 +#define POT_FORE_Y (-3) + +/* Прямоугольник слота против метки — БЕЗ передачи пяти аргументов. + * + * pop_cd_hit принимает (p, x0, y0, x1, y1): три последних идут стеком, и + * функция целиком уезжает в IX-фрейм — 45 % её тактов уходит на `-n(ix)` + * (замер asm 2026-08-19). А зовут её из cd_quiet дважды на слот, то есть + * до восьми раз за кадр. Здесь координаты берутся прямо из pop_cd, и + * аргументов остаётся два. + * + * Спрайт и накладной проверяются РАЗДЕЛЬНО — объединённый bbox ловит + * касания углом, которых нет (разбор в cd_quiet, pop_cdraw.c). */ +static int hit_x, hit_y; /* аргументы hit_rect — file-scope, не стек */ +static uint16_t hit_w, hit_h; +static uint8_t hit_p; + +static uint8_t hit_rect(void) +{ + int8_t c0, c1, c; + uint8_t ya, yb; + int x1 = hit_x + (int)hit_w - 1; + int y1 = hit_y + (int)hit_h - 1; + if (!hit_w || !hit_h) return 0; + if (x1 < 0 || hit_x > 319) return 0; + if (y1 < 0 || hit_y > 255) return 0; + ya = (uint8_t)(hit_y < 0 ? 0 : hit_y); + yb = (uint8_t)(y1 > 255 ? 255 : y1); + c0 = (int8_t)((hit_x < 0 ? 0 : hit_x) >> 5); + c1 = (int8_t)((x1 > 319 ? 319 : x1) >> 5); + for (c = c0; c <= c1; c++) + if (ya <= pop_cd_ymax[hit_p][c] && yb >= pop_cd_ymin[hit_p][c]) return 1; + return 0; +} + +uint8_t pop_cd_hit_slot(uint8_t who, uint8_t p) __banked +{ + const pop_cdraw_t *s = &pop_cd[who]; + if (!(pop_cd_dirty & (1 << p))) return 0; + hit_p = p; + hit_x = s->x[p]; hit_y = s->y[p] + POP_YOFF; + hit_w = s->w[p]; hit_h = s->h[p]; + if (hit_rect()) return 1; + if (!s->ovalid[p]) return 0; + hit_x = s->ox[p]; hit_y = s->oy[p] + POP_YOFF; + hit_w = s->ow[p]; hit_h = s->oh[p]; + return hit_rect(); +} + +uint8_t pop_cd_hit(uint8_t p, int x0, int y0, int x1, int y1) __banked +{ + int8_t c0, c1, c; + uint8_t ya, yb; + if (!(pop_cd_dirty & (1 << p))) return 0; + if (x1 < 0 || x0 > 319) return 0; + if (x0 < 0) x0 = 0; + if (x1 > 319) x1 = 319; + if (y1 < 0 || y0 > 255) return 0; + ya = (uint8_t)(y0 < 0 ? 0 : y0); + yb = (uint8_t)(y1 > 255 ? 255 : y1); + c0 = (int8_t)(x0 >> 5); + c1 = (int8_t)(x1 >> 5); + /* Пересечение отрезков [ya,yb] и [ymin,ymax] хотя бы в одной колонке. + * Пустая колонка держит ymin = 255, ymax = 0 — условие ниже её отсеет + * само, отдельной проверки «пусто» не нужно. */ + for (c = c0; c <= c1; c++) + if (ya <= pop_cd_ymax[p][c] && yb >= pop_cd_ymin[p][c]) return 1; + return 0; +} + +/* Страница приведена в порядок — снять с неё метку целиком. */ +/* Начальное состояние обеих страниц — «ничего не трогали». ЯВНО, а не + * расчётом на обнуление _DATA: пустая колонка обозначается ymin = 255, + * ymax = 0, а нули от crt0 читались бы как «затронута строка 0». */ +void pop_cd_init(void) __banked +{ + pop_cd_clear(0); + pop_cd_clear(1); +} + +void pop_cd_clear(uint8_t p) __banked +{ + uint8_t c; + for (c = 0; c < 10; c++) { pop_cd_ymin[p][c] = 255; pop_cd_ymax[p][c] = 0; } + pop_cd_dirty = (uint8_t)(pop_cd_dirty & ~(1 << p)); +} + +/* Окно под точечную перерисовку/запечку — контракт в шапке pop_tile.h. + * Логическая пара нужна для отсева тайлов (он считается из ряда комнаты), + * экранная — для клипа самого блита; при перевороте они разные. */ +void pop_t_win_set(int x, int ytop, int w, int h) __banked +{ + pop_t_fclip_x0 = x; + pop_t_fclip_x1 = x + w; + pop_t_fclip_ly0 = ytop; + pop_t_fclip_ly1 = ytop + h; + if (pop_upside) { + int t = FLIP_TOP(ytop, h); + pop_t_fclip_y0 = t; + pop_t_fclip_y1 = t + h; + } else { + pop_t_fclip_y0 = ytop; + pop_t_fclip_y1 = ytop + h; + } + pop_t_fclip_on = 1; +} + +void pop_t_win_clear(void) __banked +{ pop_t_fclip_on = 0; } + +/* Блит getimage-блоба из ОБЫЧНОЙ памяти (W2), а не из EMM-страницы атласа: + * те же клип полосы у потолка и окно перерисовки, что у pop_blit_b, но без + * atlas_image и без gfx_w0_map/unmap (это ещё ~1 350 такта). Заведено под + * композитный спрайт падающего куска (pop_room.c: три части сложены в один + * блоб, один вызов блита вместо трёх). */ +void pop_mem_b(const uint8_t *img, int x, int ybottom) __banked +{ + uint8_t w = img[0], h = img[2]; + int top; + if (pop_t_fclip_on && + (x >= pop_t_fclip_x1 || x + 64 <= pop_t_fclip_x0 || + ybottom + POP_YOFF + 1 <= pop_t_fclip_ly0 || + ybottom + POP_YOFF - 64 >= pop_t_fclip_ly1)) + return; + if (!w || !h) return; + top = ybottom - (int)h + 1 + POP_YOFF; + if (pop_upside) top = FLIP_TOP(top, h); + if (!pop_t_fclip_on && !pop_t_clip_top && + !(pop_upside && (top < POP_YOFF || top + (int)h > POP_CLIP_BOTTOM)) && + x >= 0 && top >= 0 && x + (int)w <= 320 && top + (int)h <= 256) { + if (pop_upside) gfx_blit_noclip_vflip(x, top, img); + else gfx_blit_noclip(x, top, img); + pop_cd_touch(x, top, (int)w, (int)h); + } else { + blit_b_clip(img, x, top, w, h); + } +} + +/* Heal по КОМНАТНОЙ координате (её и знают точечные перерисовки: чомпер, + * пики, loose, ворота, полоса потолка). При перевороте прямоугольник + * обязан отразиться ровно так же, как сам тайл, — иначе heal чистит одно + * место, а рисует в другое: именно так «ломалась анимация чомперов» + * (найдено пользователем на первом же прогоне зеркального фона). */ +void pop_bar_black(int x, int top, int w, int h) __banked +{ + if (pop_upside) top = FLIP_TOP(top, h); + gfx_set_bank(GFX_BANK_NORMAL); + setfillstyle(SOLID_FILL, BLACK); + bar(x, top, x + w - 1, top + h - 1); + pop_cd_touch(x, top, w, h); /* bar идёт мимо pop_blit_b (DRAW-COST) */ +} + +void pop_heal_off(int x, int y, int w, int h) __banked +{ + int top = y + POP_YOFF; + if (pop_upside) top = FLIP_TOP(top, h); + pop_heal_fast(x, top, w, h); +} + +/* ---- Зелье: склянка (tiles_10_potion, seg008 draw_tile_fore) --------- * + * Тип = (modif & 0xF8) >> 3: 0 = пусто (ничего не рисуем), 2..4 — «большая» + * склянка (id 13), остальные — маленькая (id 12). Спрайты из chtab_1 (атлас + * pop_pot), поэтому НЕ из env: раньше рисовали chtab_6 id 12 (res212), + * которого в VDUNGEON нет — оттуда и «мусорный объект». Позиция оригинала: + * xh = fore_x + draw_xh, xl = 6, ybottom = fore_y + draw_main_y. + * + * Здесь, а не в банке: склянку рисует и полная отрисовка тайла (холодная + * половина), и fore-проход поверх персонажа (горячая). */ +void pop_potion_flask(int row, int col, uint8_t modif) __banked +{ + uint8_t type = (uint8_t)((modif & 0xF8) >> 3); + uint8_t id = 12; + if (type == 0) return; + if (type >= 2 && type < 5) id = 13; /* большая склянка */ + if (pop_palace) id += 2; /* seg008:747: во дворце склянки другие */ + pop_pot_b(id, (POT_FORE_X + POP_COL_XH[col]) * 8 + 6, + POT_FORE_Y + 63 * row + 62); +} + +/* Ряд 0 комнаты СНИЗУ (fg-коды) — нужен draw_tile_topright/floorright, когда + * тайл нижнего ряда стал видимым (empty после падения loose): «стена вниз» = + * топрайт тайла room_B(0, col-1). 0 — нет комнаты снизу (кромка = стена). */ +void pop_room_set_below(const uint8_t *below_row0_fg) __banked +{ pop_t_below = below_row0_fg; } + +/* Ряд 2 (fg-коды + модификаторы) комнаты СВЕРХУ — для полосы кладки у + * потолка (seg008 draw_room: доп. верхний ряд из room_A, draw_tile_aboveroom + * при draw_main_y=-1). 0 — нет комнаты сверху (кромка уровня → пол). */ +void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod) __banked +{ pop_t_above = above_row2_fg; pop_t_abovemod = above_row2_mod; } diff --git a/applications/SprPoP/src/pop_timer.c b/applications/SprPoP/src/pop_timer.c new file mode 100644 index 0000000..58ed531 --- /dev/null +++ b/applications/SprPoP/src/pop_timer.c @@ -0,0 +1,78 @@ +/* + * pop_timer.c — единый владелец глобального таймера (FG4, банк 9). + * + * SDLPoP хранит rem_min/rem_tick и уменьшает rem_tick на каждом логическом + * игровом кадре. Здесь та же схема: после нуля tick возвращается к 719, + * поэтому 720 тиков образуют минуту. Настройки, экранные сообщения и + * переход TIME_EXPIRED остаются снаружи: это делает ядро таймера чистым и + * не привязывает его к ещё не реализованным title/cutscene режимам. + */ +#include +#include "pop_timer.h" + +uint8_t pop_timer_minutes; +uint16_t pop_timer_ticks; +uint8_t pop_show_time; + +void pop_timer_new_game(void) __banked +{ + pop_timer_minutes = POP_TIMER_START_MINUTES; + pop_timer_ticks = POP_TIMER_START_TICKS; + pop_show_time = 0; +} + +uint8_t pop_timer_tick(uint8_t enabled, uint8_t may_run) __banked +{ + if (!enabled || !may_run || pop_timer_minutes == 0) return 0; + + --pop_timer_ticks; + if (pop_timer_ticks != 0) { + /* Последняя минута: оригинал напоминает о времени КАЖДУЮ секунду + * (rem_tick % 12 == 0) и специально гасит текущую строку, чтобы + * новая встала немедленно, не дожидаясь её 24 тиков. */ + if (pop_timer_minutes == 1 && pop_timer_ticks % 12 == 0) + pop_show_time = 2; /* 2 = «показать, перебив текущую» */ + return 0; + } + + pop_timer_ticks = POP_TIMER_START_TICKS; + --pop_timer_minutes; + /* Круглые пятёрки и весь финальный отсчёт: seg008:1795. */ + if (pop_timer_minutes != 0 && + (pop_timer_minutes <= 5 || pop_timer_minutes % 5 == 0)) + pop_show_time = 1; + return (uint8_t)(pop_timer_minutes == 0); +} + +void pop_timer_cheat_subtract(void) __banked +{ + /* Это буквально правило SDLK_KP_MINUS: последнюю минуту читом не + * отнимаем, иначе не получится отличить чит от настоящего expiry. */ + if (pop_timer_minutes > 1) --pop_timer_minutes; + pop_show_time = 2; /* оригинал показывает результат сразу */ +} + +void pop_timer_cheat_add(void) __banked +{ + /* SDLPoP хранит signed short, у нас достаточно byte: 255 минут — + * практический максимум и насыщение не даёт тихо свернуться в нуль. */ + if (pop_timer_minutes != 0xFF) ++pop_timer_minutes; + pop_show_time = 2; +} + +void pop_timer_qsave(pop_qs_io_t *io) __banked +{ + uint8_t minutes = pop_timer_minutes; + uint16_t ticks = pop_timer_ticks; + + pop_qs_u8(io, &minutes); + pop_qs_u16(io, &ticks); + if (io->reading && io->apply) { + if (ticks == 0 || ticks > POP_TIMER_START_TICKS) { + io->failed = 1; + return; + } + pop_timer_minutes = minutes; + pop_timer_ticks = ticks; + } +} diff --git a/applications/SprPoP/src/pop_timer.h b/applications/SprPoP/src/pop_timer.h new file mode 100644 index 0000000..b17c97b --- /dev/null +++ b/applications/SprPoP/src/pop_timer.h @@ -0,0 +1,50 @@ +/* + * pop_timer.h — глобальный игровой таймер (FG4, банк 9). + * + * Время измеряется логическими кадрами SDLPoP: 720 тиков = одна минута. + * Модуль намеренно не знает ни о клавиатуре, ни о графике, ни о состоянии + * Settings: вызывающий передаёт, разрешён ли лимит и можно ли времени идти. + * Благодаря этому тот же код проверяется host-тестом без DSS/MAME. + */ +#ifndef POP_TIMER_H +#define POP_TIMER_H + +#include +#include "pop_qsave.h" + +#define POP_TIMER_START_MINUTES 60 +#define POP_TIMER_TICKS_PER_MINUTE 720 +#define POP_TIMER_START_TICKS (POP_TIMER_TICKS_PER_MINUTE - 1) + +/* Оставшееся время. ticks всегда 1..719 вне короткого участка timer_tick. + * Нуль minutes означает естественное истечение; выключенный в Settings + * лимит состояние не меняет, а просто останавливает отсчёт. */ +extern uint8_t pop_timer_minutes; +extern uint16_t pop_timer_ticks; + +/* «Пора показать оставшееся время» — порт is_show_time (seg008). Взводит + * и сам таймер на круглых минутах, и внешние события: старт уровня, клавиша + * Space, читы +/-, смерть Джафара. Гасит его тот, кто напечатал строку + * (pop_status.c) — ядро таймера про экран по-прежнему ничего не знает. */ +extern uint8_t pop_show_time; + +/* Начать новую игру: ровно исходные 60:00 SDLPoP (60 минут, tick 719). */ +void pop_timer_new_game(void) __banked; + +/* Один логический игровой кадр. enabled — переключатель Settings, + * may_run — правила текущей сцены/уровня. Возвращает 1 ОДИН раз, когда + * отсчёт дошёл до нуля; на паузе, HDD/QuickSave и при выключенном лимите + * вызывать можно — состояние останется неизменным. */ +uint8_t pop_timer_tick(uint8_t enabled, uint8_t may_run) __banked; + +/* Читы, повторяющие +/- SDLPoP: минус не даёт искусственно поставить 0, + * плюс добавляет минуту. Они меняют счётчик и при выключенном лимите — + * после включения пользователь продолжит именно это подготовленное время. */ +void pop_timer_cheat_subtract(void) __banked; +void pop_timer_cheat_add(void) __banked; + +/* Состояние входит в payload QuickSave. Неверный tick помечает поток + * ошибочным, не подменяя живой таймер частично. */ +void pop_timer_qsave(pop_qs_io_t *io) __banked; + +#endif /* POP_TIMER_H */ diff --git a/applications/SprPoP/src/pop_title.c b/applications/SprPoP/src/pop_title.c new file mode 100644 index 0000000..c9a474f --- /dev/null +++ b/applications/SprPoP/src/pop_title.c @@ -0,0 +1,193 @@ +/* + * pop_title.c — оригинальная графическая часть show_title(), банк 9 (FG6). + * + * Готовые композиции создаёт toolchain/pop_pack_title.py непосредственно из + * TITLE/res51..55.png SDLPoP. Каждый 320x200 экран режется на пять лент: + * загружаем одну в EMM, блитим непрозрачно в normal bank, освобождаем. Это + * сознательно не держит title-ресурсы рядом с игровыми атласами. + */ +#include +#include +#include +#include +#include +#include "pop_bg.h" /* POP_YOFF: title занимает 200px по центру 256px */ +#include "pop_sfx.h" +#include "pop_music.h" +#include "pop_arc.h" +#include "title_arc.h" +#include "pop_cutscene.h" /* POP_T60 */ +#include "pop_settings.h" +#include "pop_pal.h" +#include "pop_title.h" +#include "pop_ui.h" + +#define TITLE_W 320 +#define TITLE_H 200 +#define TITLE_Y POP_YOFF +#define TITLE_PARTS 5 +#define TITLE_FADE POP_FADE_FRAMES /* 2,13 с, как fade_*_1 оригинала */ + +enum { + TITLE_MAIN = 0, + TITLE_PRESENTS, + TITLE_GAME, + TITLE_LOGO +}; + +/* Композиции лежат в одном архиве подряд по пять полос 49/49/49/49/4, + * поэтому имён здесь больше нет: индекс = сцена * TITLE_PARTS. */ +#if (ARC_TITLE_T1_0 - ARC_TITLE_T0_0) != TITLE_PARTS +#error "полосы титров в архиве лежат не по пять подряд" +#endif + +/* Последовательность и интервалы буквально из show_title(): + * main --0x82--> presents --0xCD--> main --0x41--> game + * --0x10E--> main --0xEB--> logo+Mechner. + * Счёт — в тиках SDLPoP (60 Гц), в кадры луча переводит POP_T60: под + * заставку играет тема, и разбегание на 20 % было бы слышно. + * + * Последний интервал у оригинала не фиксирован: показав логотип, он ждёт + * ОКОНЧАНИЯ ТЕМЫ (`while (check_sound_playing())`, seg000:2027) и лишь + * потом уходит в историю. 0xEB здесь — нижняя граница на случай, когда + * музыки нет (нет файла, выключен звук). */ +static const uint8_t title_scene[] = { + TITLE_MAIN, TITLE_PRESENTS, TITLE_MAIN, TITLE_GAME, TITLE_MAIN, TITLE_LOGO +}; +static const uint16_t title_wait_ticks[] = { + POP_T60(0x82), POP_T60(0xCD), POP_T60(0x41), + POP_T60(0x10E), POP_T60(0xEB), POP_T60(0xEB) +}; + +static uint8_t title_shown; + +static uint8_t title_skip_pressed(void) +{ + /* Как и в attract-demo, любой удерживаемый raw-код означает «не ждать». + * Это заодно не дублирует список клавиш у title, intro и cutscene. */ + return kbd_raw_any_down(); +} + +/* Нарисовать одну из готовых 320x200 композиций в скрытую страницу. + * Сопоставление W0 охватывает ТОЛЬКО atlas_image/blit: BIOS/vsync сюда + * не попадает, значит не конфликтует с EMM и CBL. Возвращает номер + * подготовленной страницы; show=0 оставляет её скрытой для palette commit. */ +static int title_draw(uint8_t scene, uint8_t show) +{ + uint8_t tbl[ARC_TITLE_COUNT * POP_ARC_REC]; + pop_arc_t arc; + uint8_t page = pop_screen_begin(); + int res; + + pop_screen_fill(BLACK); + if (pop_arc_open_id(&arc, POP_ARC_TITLE, tbl, ARC_TITLE_COUNT) < 0) + return -1; + res = pop_screen_blit_atlas_arc(&arc, tbl, + (uint8_t)(ARC_TITLE_T0_0 + + scene * TITLE_PARTS), + TITLE_PARTS, 0, TITLE_Y); + pop_arc_close(&arc); + if (res != 0) return -1; + if (show) pop_screen_present(page); + return page; +} + +/* draw_full_image(TITLE_MAIN) + transition_ltr() финала (seg001:590 и + * :629): титульный рисунок проявляется полосами поверх текущего экрана. + * Палитру не трогаем — story.pal и title.pal это одни и те же цвета + * (обе сделаны из res51). -1 — архив не прочитан. */ +int pop_title_main_ltr(void) __banked +{ + if (title_draw(TITLE_MAIN, 0) < 0) return -1; + pop_screen_present_ltr(); + return 0; +} + +/* Тот же приём для композиции с логотипом и подписью Jordan Mechner: её + * show_title() показывает МЕЖДУ «свадьбой» и титрами (seg000:2045). */ +int pop_title_logo_ltr(void) __banked +{ + if (title_draw(TITLE_LOGO, 0) < 0) return -1; + pop_screen_present_ltr(); + return 0; +} + +static uint8_t title_wait(uint16_t ticks) +{ + while (ticks--) { + if (title_skip_pressed()) return 1; + gfx_wait_vsync(); + } + return 0; +} + +/* Новый источник не становится видимым сам: cold boot уже чёрный, а + * последующий fade_in выбирает яркость только после сборки t0. */ +static void title_load_palette_dimmed(void) +{ + pop_pal_title_load(); + pop_pal_apply(POP_PAL_DIM_BLACK); +} + +uint8_t pop_title_show(void) __banked +{ + uint8_t step; + uint8_t skipped = 0, music_ok = 0; + int prepared; + uint8_t cold_start = (uint8_t)(!title_shown && + !pop_settings.show_build_info); + + /* Игровые SFX не должны тянуться сюда из прерванной партии: заставка + * начинается с тишины, а через несколько строк её займёт тема титров. */ + pop_sfx_pause(); + if (!cold_start) pop_pal_fade_out(TITLE_FADE); + + /* Видимая VRAM могла сохраниться от предыдущей программы. Обнулить её + * при чёрной палитре, затем собрать t0 СКРЫТО: загрузка title.pal тогда + * освещает только индекс 0, а не старый экран или недорисованный title. */ + gfx_set_draw_page(gfx_get_visible_page()); + gfx_set_bank(GFX_BANK_NORMAL); + pop_screen_fill(BLACK); + prepared = title_draw(TITLE_MAIN, 0); + if (prepared < 0) return 0; + title_load_palette_dimmed(); + pop_screen_present((uint8_t)prepared); + title_shown = 1; + + /* Тему читаем с диска ЗАРАНЕЕ (пока экран ещё тёмный), а пускаем ниже — + * после fade_in, ровно как show_title: fade_in_2, затем + * play_sound(sound_54). */ + music_ok = (uint8_t)(pop_music_load(POP_MUS_INTRO) == 0); + + for (step = 0; step < sizeof(title_scene); step++) { + if (step && title_draw(title_scene[step], 1) < 0) + break; + if (step == 0) { + pop_pal_fade_in(TITLE_FADE); + if (music_ok) { + (void)pop_sfx_start(); + pop_music_play(); + } + } + if (title_wait(title_wait_ticks[step])) { + skipped = 1; + break; + } + /* Логотип держим до конца темы — так оригинал стыкует заставку с + * историей: музыка не обрывается, а сменяется следующей репликой. */ + if (step + 1 == sizeof(title_scene)) { + while (pop_music_busy()) { + if (title_skip_pressed()) { skipped = 1; break; } + gfx_wait_vsync(); + } + } + } + /* Уходим В ИГРУ — заставочная музыка не должна тянуться в уровень. + * Дальше по сценарию (intro) трек, наоборот, ОСТАЁТСЯ звучать: там + * следующая реплика подгружается во второй слот, пока играет эта. */ + if (skipped) { + pop_music_free(); + pop_sfx_pause(); + } + return skipped; +} diff --git a/applications/SprPoP/src/pop_title.h b/applications/SprPoP/src/pop_title.h new file mode 100644 index 0000000..0f15dd9 --- /dev/null +++ b/applications/SprPoP/src/pop_title.h @@ -0,0 +1,27 @@ +/* + * pop_title.h — первая часть оригинальной title sequence, банк 9 (FG6). + * + * Экран живёт отдельно от игрового кадра: он загружает одну ленту TITLE + * за раз, рисует готовую страницу и не оставляет EMM-страниц после выхода. + */ +#ifndef POP_TITLE_H +#define POP_TITLE_H + +#include + +/* Показать TITLE_MAIN/PRESENTS/GAME/POP+MECHNER в порядке show_title(). + * Возврат 1 — пользователь прервал ожидание и надо начать FIRST_LEVEL + * напрямую; 0 — последовательность завершилась штатно, продолжать intro. */ +uint8_t pop_title_show(void) __banked; + +/* Одна титульная картинка (TITLE_MAIN) полосами слева направо — так финал + * возвращается к ней после HAIL и после ввода имени в таблицу рекордов. + * Палитру не меняет: story.pal и title.pal сделаны из одного res51. + * -1 — архив титров не прочитан. */ +int pop_title_main_ltr(void) __banked; + +/* Композиция с логотипом PRINCE OF PERSIA и Jordan Mechner тем же + * переходом — шаг show_title() между «свадьбой» и титрами. */ +int pop_title_logo_ltr(void) __banked; + +#endif /* POP_TITLE_H */ diff --git a/applications/SprPoP/src/pop_trob.c b/applications/SprPoP/src/pop_trob.c new file mode 100644 index 0000000..948a2d2 --- /dev/null +++ b/applications/SprPoP/src/pop_trob.c @@ -0,0 +1,929 @@ +/* + * pop_trob.c — P0-каркас: room_modif + список trob + диспетчер анимаций. + * Порт SDLPoP seg007 (process_trobs/animate_tile/add_trob/animate_spike). + * См. pop_trob.h. + */ +#include "pop_trob.h" +#include "pop_sfx.h" /* звуковые эффекты (../docs/sound_plan.md) */ +#include "pop_state.h" /* pop_leveldoor_open */ +#include "pop_level.h" +#include "pop_geom.h" +#include "pop_redraw.h" /* пометки перерисовки тайлов (вместо прямых вызовов) */ +/* pop_bg нужен ТОЛЬКО для пламени факела и пузырька зелья: это не тайловая + * перерисовка, а покадровый оверлей поверх фона (в оригинале — свои + * add_backtable/add_foretable внутри draw_tile). Всё остальное идёт через + * пометки, поэтому pop_process_trobs — единственная функция модуля, которую + * нельзя звать из __banked-кода. */ +#include "pop_bg.h" +#include "pop_tile.h" /* pop_palace — тип уровня как модификатор дыры */ +#include "pop_guard.h" /* is_guard_notice — нажатая кнопка слышна стражу */ +#include "pop_kid.h" /* Kid.curr_row/alive — условие остановки чомпера */ +#include "pop_tune.h" /* POP_DBG_GATE_HOLD — отладочное растягивание окна */ + +/* Коды тайлов (fg & 0x1F), которые анимируются. */ +#define TILE_SPIKE 0x02 +#define TILE_GATE 0x04 +#define TILE_CLOSER 0x06 +#define TILE_DEBRIS 0x0E /* button_type для «насовсем открыть» (died_on_button) */ +#define TILE_OPENER 0x0F +#define TILE_POTION 0x0A +#define TILE_LOOSE 0x0B /* проваливающийся пол (фаза тряски в modif) */ +#define TILE_SWORD 0x16 /* меч-предмет на полу */ +#define TILE_LEVELDOOR 0x10 /* левая половина двери уровня (на ней modif) */ +#define TILE_CHOMP 0x12 /* чомпер (челюсти) */ +#define TILE_WALL 0x14 /* стена (в модификаторе — флаг «без полоски») */ +#define TILE_TORCH 0x13 +#define TILE_TORCH_DEBRIS 0x1E +#define TILE_EMPTY 0x00 /* remove_loose: на месте провалившейся плиты */ + +/* ---- Персистентное per-room modifier-состояние -------------------- */ +static uint8_t room_modif[POP_NROOMS][POP_ROOMTILES]; +static uint8_t room_seen[POP_NROOMS]; /* modif проинициализирован из уровня */ + +/* ---- Список анимируемых тайлов ------------------------------------ */ +typedef struct { uint8_t room, tilepos; int8_t type; } trob_t; +#define TROBS_MAX 30 /* как в оригинале (types.h) */ +static trob_t trobs[TROBS_MAX]; +static uint8_t trobs_count; +static uint8_t trob_code[TROBS_MAX]; /* префетч кодов тайлов на кадр (1 map W0) */ +/* Код ПРАВОГО соседа — заполняется только у факелов (0 у остальных). Нужен + * потому, что пламя рисуется в ячейке соседа и запекается в фон: если у + * соседа своя anim-графика, её надо вернуть ПОВЕРХ огня. Читается в том же + * префетче, чтобы не свапать W0 второй раз за кадр. */ +static uint8_t trob_rcode[TROBS_MAX]; + +/* Последняя НАРИСОВАННАЯ draw-сигнатура тайла (spike: modifier; button: + * признак «нажата» = таймер связи >1). Редрой пики/кнопки — ТОЛЬКО при её + * изменении (порт redraw_11h-on-change из SDLPoP). Без этого кнопка под + * стоящим Кидом перерисовывалась КАЖДЫЙ кадр (2×draw_tile ≈ 50% кадра в + * статике), т.к. её trob висит, пока Кид стоит (это верно, как в SDLPoP — + * расхождение было именно в безусловном редрое). */ +static uint8_t trob_drawn[POP_ROOMTILES]; + +/* prandom (seg009.c:321, 32-бит LCG) — нужен для стартовой фазы факела и + * выбора следующего кадра пламени. */ +static pop_rnd_t trob_seed; + +#define trob_prandom(maxv) pop_prandom(&trob_seed, (maxv)) + + +void pop_trob_reset(void) __banked +{ + uint8_t i; + for (i = 0; i < POP_NROOMS; i++) room_seen[i] = 0; + for (i = 0; i < POP_ROOMTILES; i++) trob_drawn[i] = 0; + pop_redraw_reset(); + trobs_count = 0; +} + +uint8_t *pop_trob_modif(uint8_t room) __banked +{ + if (room < 1 || room > POP_NROOMS) return 0; + if (!room_seen[room - 1]) { + uint8_t i; + pop_level_room_bg(room, room_modif[room - 1]); + /* load_alter_mod (seg008:198E): модификатор В ФАЙЛЕ уровня и + * модификатор В РАНТАЙМЕ — разные величины, оригинал переводит их + * при загрузке. Порт того, что нас касается: + * + * ворота 1 = «открыты» (Table 8 спецификации DAT) -> 188, то есть + * рабочая высота подъёма; ЛЮБОЕ другое значение -> 0 + * (закрыты). Без этого перевода ворота, которые в + * уровне открыты, стартуют закрытыми, и кнопка-closer + * рядом с ними теряет смысл (ур. 2, комн. 13). + * loose -> 0: фаза тряски всегда начинается с нуля. + * зелье -> <<3: старшие биты = тип, младшие 3 = фаза пузырька. + * стена -> <<7: единственный бит уровня («без полоски», см. ниже) + * переезжает в СТАРШИЙ, где его и ищет отрисовка. + * + * СВЯЗИ кладки (какая грань у стены слева/справа) из этого перевода + * намеренно выкинуты: у нас их считает pop_wall_modifier по типам + * СОСЕДЕЙ прямо при отрисовке. Но второй смысл модификатора стены — + * авторский флаг «не рисовать синюю полоску на моей правой кромке» + * (noblue) — из соседей не выводится, он проставлен вручную в данных + * уровня. Без перевода отрисовка видела сырую единицу вместо 0x80, + * проверка `(mod & 0x80) == 0` проходила, и во дворце вылезал кусок + * узора на стене (BUG BLUELINE-NOBLUE, ур. 6 к. 1 тайл (2,3)). */ + pop_level_access_begin(); + for (i = 0; i < POP_ROOMTILES; i++) { + uint8_t *m = &room_modif[room - 1][i]; + switch (pop_level_tile_raw(room, i) & 0x1F) { + case TILE_GATE: *m = (uint8_t)((*m == 1) ? 188 : 0); break; + case TILE_LOOSE: *m = 0; break; + case TILE_POTION: *m = (uint8_t)(*m << 3); break; + case TILE_WALL: *m = (uint8_t)(*m << 7); break; + default: break; + } + } + pop_level_access_end(); + room_seen[room - 1] = 1; + } + return room_modif[room - 1]; +} + +static int find_trob(uint8_t room, uint8_t tilepos) +{ + uint8_t i; + for (i = 0; i < trobs_count; i++) + if (trobs[i].room == room && trobs[i].tilepos == tilepos) return i; + return -1; +} + +void pop_add_trob(uint8_t room, uint8_t tilepos, int8_t type) __banked +{ + int f = find_trob(room, tilepos); + if (f >= 0) { trobs[f].type = type; return; } + if (trobs_count >= TROBS_MAX) return; + trobs[trobs_count].room = room; + trobs[trobs_count].tilepos = tilepos; + trobs[trobs_count].type = type; + trobs_count++; +} + +void pop_trob_room_changed(void) __banked +{ + uint8_t i; + for (i = 0; i < POP_ROOMTILES; i++) trob_drawn[i] = 0; + pop_redraw_reset(); +} + +/* anim_tile_modif (seg000:0B12) — при входе в комнату завести анимации её + * тайлов: факелам — случайная стартовая фаза пламени (start_anim_torch, + * seg007:0847: modif = prandom(8)), зельям — случайная фаза пузырька + * (start_anim_potion: младшие 3 бита = prandom(6)+1, старшие = тип). */ +void pop_trob_anim_room(uint8_t room) __banked +{ + uint8_t *mod = pop_trob_modif(room); + uint8_t i, code; + if (!mod) return; + pop_level_access_begin(); + /* Освободить список от «декоративных» trob ДРУГИХ комнат: факел, зелье и + * блеск меча анимируются только в ОТРИСОВАННОЙ комнате, а место в списке + * занимают. Оригинал их копит и на 30-й выдаёт «Trobs Overflow»; у нас + * отладочный обход всех 24 комнат упирается в лимит за пару минут, после + * чего новые анимации молча не заводятся. Механику (ворота/пики/кнопки, + * в т.ч. кросс-комнатные) чистка не трогает. */ + { + uint8_t k, wr = 0; + for (k = 0; k < trobs_count; k++) { + uint8_t c = (uint8_t)(pop_level_tile_raw(trobs[k].room, + trobs[k].tilepos) & 0x1F); + if (trobs[k].room != room && + (c == TILE_TORCH || c == TILE_TORCH_DEBRIS || + c == TILE_POTION || c == TILE_SWORD)) + continue; + trobs[wr++] = trobs[k]; + } + trobs_count = wr; + } + for (i = 0; i < POP_ROOMTILES; i++) { + code = (uint8_t)(pop_level_tile_raw(room, i) & 0x1F); + if (code == TILE_TORCH || code == TILE_TORCH_DEBRIS) { + mod[i] = (uint8_t)trob_prandom(8); + pop_add_trob(room, i, 1); + } else if (code == TILE_POTION) { + mod[i] = (uint8_t)((mod[i] & 0xF8) | (trob_prandom(6) + 1)); + pop_add_trob(room, i, 1); + } else if (code == TILE_SWORD) { + /* start_anim_sword (seg007:087C): случайная фаза счётчика блеска. */ + mod[i] = (uint8_t)(trob_prandom(0xFF) & 0x1F); + /* trob_drawn обязан описывать то, что НАРИСУЕТ полная отрисовка + * комнаты, а она идёт ПОСЛЕ этой функции и берёт ЖИВОЙ modif. + * Обнулять его (как делает pop_trob_room_changed для всех тайлов) + * тут нельзя: если случайная фаза попала ровно на 1, комната + * рисуется С БЛЕСКОМ, а trob_drawn говорит «блеска нет» — и на + * следующем кадре счётчик уходит с 1, sig снова 0, СОВПАДАЕТ со + * стоячим trob_drawn, и перерисовка НЕ помечается. Блеск остаётся + * запечённым в фон до конца периода: animate_sword считает вниз и + * выдаёт новый период 0x28..0x67, то есть до 103 логических кадров + * ≈ 6 секунд. Вероятность ровно 1/32 на вход в комнату — отсюда + * «редко воспроизводится» (найдено пользователем 2026-08-17, + * уровень 1 комната 15). Проверено ПРИНУДИТЕЛЬНО: со временной + * подменой фазы на mod[i] = 1 баг ловится на КАЖДОМ входе в + * комнату, а с этой строкой не ловится ни на одном. + * + * У КНОПКИ той же дыры нет, хотя механизм общий: её признак — + * ДЛИТЕЛЬНОЕ состояние (нажата/нет), и рассогласование само + * исправляется на первой же смене. У блеска признак — одиночная + * вспышка в ОДНОМ кадре из 40..103, и пропущенная смена стоит + * целого периода. */ + trob_drawn[i] = (uint8_t)(mod[i] == 1); + pop_add_trob(room, i, 1); + } + } + pop_level_access_end(); +} + +/* animate_spike (seg007:04D3): автомат по modifier. *m — modifier тайла, + * *type — тип trob (>=0 активен, <0 = удалить). Выдвиг: ++modif (1..4), + * на 5 → 0x8F (полностью вышли, безвредны), на 9 → 0 + конец. Убирание + * (флаг 0x80): --modif; когда 0x7F-часть обнулилась → modif=6 (начать + * визуальный уход 6..8). 0xFF = disabled. */ +/* get_torch_frame (seg007:06CD): случайный кадр 0..8, иначе следующий. */ +static uint8_t get_torch_frame(uint8_t curr) +{ + uint8_t next = (uint8_t)trob_prandom(255); + if (next != curr && next < 9) return next; + next = (uint8_t)(curr + 1); + if (next >= 9) next = 0; + return next; +} + +/* bubble_next_frame (seg007:0759): фазы пузырька 1..7 по кругу. */ +static uint8_t bubble_next_frame(uint8_t curr) +{ + uint8_t next = (uint8_t)(curr + 1); + return (next >= 8) ? 1 : next; +} + +/* Разбор номера тайла 0..29 на ряд/колонку без деления (`tp / 10`, `tp % 10` + * — это __divuchar/__moduchar, а в animate_chomper они звались на каждого + * чомпера каждый кадр). Определены ЗДЕСЬ, а не перед pop_process_trobs, где + * лежали раньше: чомперы выше по файлу и оригинала не видели. */ +static const uint8_t TP_ROW[30] = { + 0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2 +}; +static const uint8_t TP_COL[30] = { + 0,1,2,3,4,5,6,7,8,9, 0,1,2,3,4,5,6,7,8,9, 0,1,2,3,4,5,6,7,8,9 +}; + +/* animate_chomper (seg007:0448). Модификатор: младшие 7 бит — фаза 1..N, + * старший — «перемололо кого-то» (кровь). Фаза крутится по кругу до + * POP_CHOMPER_SPEED (у оригинала 15), то есть челюсти смыкаются раз в N + * кадров; фаза 2 — момент удара (в оригинале там звук sound_47). + * + * Остановка (иначе чомперы щёлкали бы всю игру): если Кид ушёл из этой + * комнаты, или ушёл из этого РЯДА, или умер не в этом чомпере — trob + * снимается, но только после фазы 6, чтобы челюсти успели разомкнуться. + * Запускает их обратно start_chompers на смене ряда. Аргументы room/tp + * нужны ровно для этой проверки. */ +static void animate_chomper(uint8_t *m, int8_t *type, + uint8_t room, uint8_t tp, uint8_t drawn_room) +{ + uint8_t blood, frame; + if (*type >= 0) { + blood = (uint8_t)(*m & 0x80); + frame = (uint8_t)((*m & 0x7F) + 1); + if (frame > POP_CHOMPER_SPEED) frame = 1; + *m = (uint8_t)(blood | frame); + if (frame == 2) pop_sfx_play(47); /* щелчок челюстей, seg007:297 */ + if ((room != drawn_room || (int8_t)TP_ROW[tp] != Kid.curr_row || + (Kid.alive >= 0 && blood == 0)) && frame >= 6) + *type = -1; + } +} + +/* next_chomper_timing (seg007:0F9A): 15,12,9,6,13,10,7,14,11,8 по кругу — + * чомперы одного ряда стартуют вразнобой, а не хлопают в унисон. */ +static uint8_t next_chomper_timing(uint8_t timing) +{ + timing -= 3; + if (timing < 6) timing += 10; + return timing; +} + +/* start_anim_chomper (seg007:08C7): запустить, только если стоит (0) или + * уже прошёл фазу размыкания (>=6). */ +static void start_anim_chomper(uint8_t *mod, uint8_t room, uint8_t tp, + uint8_t modifier) +{ + uint8_t old = (uint8_t)(mod[tp] & 0x7F); + if (old == 0 || old >= 6) { + mod[tp] = modifier; + pop_add_trob(room, tp, 1); + } +} + +/* start_chompers (seg007:0F13): все чомперы РЯДА активного персонажа. + * Зовётся отовсюду, где персонаж сменил ряд (SEQ_UP/SEQ_DOWN, start_fall, + * приземление) и на входе в комнату. */ +void pop_start_chompers(uint8_t room, int8_t row) __banked +{ + uint8_t timing = 15, col, tp; + uint8_t *mod; + if ((uint8_t)row >= 3) return; /* вне комнаты — нечего */ + /* pop_trob_modif ДО открытия окна: при первом обращении к комнате он + * сам лезет за её bg в страницу уровня, то есть перемапит W0 и закроет + * его — а мы внутри окна читаем тайлы. */ + mod = pop_trob_modif(room); + pop_level_access_begin(); + for (col = 0; col < 10; col++) { + tp = (uint8_t)(row * 10 + col); + if ((pop_level_tile_raw(room, tp) & 0x1F) == TILE_CHOMP) { + uint8_t m = (uint8_t)(mod[tp] & 0x7F); + if (m == 0 || m >= 6) { + start_anim_chomper(mod, room, tp, + (uint8_t)(timing | (mod[tp] & 0x80))); + timing = next_chomper_timing(timing); + } + } + } + pop_level_access_end(); +} + +static void animate_spike(uint8_t *m, int8_t *type) +{ + uint8_t mod = *m; + if (*type >= 0) { + if (mod == 0xFF) return; /* disabled — не двигать */ + if (mod & 0x80) { + --mod; + if (mod & 0x7F) { *m = mod; return; } + mod = 6; + } else { + ++mod; + if (mod == 5) mod = 0x8F; + else if (mod == 9) { mod = 0; *type = -1; } + } + } + *m = mod; +} + +/* animate_loose (seg007:0CBA) — для плиты в НЕ отрисованной комнате. + * + * ЗАЧЕМ. В оригинале loose-пол — обычный trob, а do_trobs крутит весь + * список независимо от drawn_room: расшатанная плита доваливается, даже если + * Кид уже убежал в соседнюю комнату. У нас фаза живёт в pop_loose_modif[30] + * — массиве ТЕКУЩЕЙ комнаты, — и смена комнаты её обнуляла: плита, + * пробежав по которой Кид уходил вправо, оставалась целой (уровень 10, + * комната 2, тайл 2,9). + * + * Разделение обязанностей: пока комната отрисована, фазой владеет + * pop_loose_tick (ему нужны перерисовка, звук, mob-порядок), и trob здесь + * сразу снимается. На выходе из комнаты pop_loose_leave_room (pop_map.c) + * сбрасывает фазу в ПЕРСИСТЕНТНЫЙ room_modif и заводит trob — дальше плита + * досчитывает здесь, а на входе обратно pop_loose_reset забирает фазу + * назад. Персистентный модификатор и есть curr_room_modif оригинала, так + * что расхождения с SDLPoP нет — только двухуровневое хранение. + * + * Рисовать тут нечего (комната не на экране), поэтому от оригинала остаётся + * ровно счёт фазы и провал: тайл в EMPTY + отрыв падающего куска. */ +static void animate_loose(uint8_t *m, int8_t *type, uint8_t room, uint8_t tp, + uint8_t cur_room) +{ + uint8_t ph; + if (room == cur_room) { *type = -1; return; } /* хозяин — pop_loose_tick */ + ph = (uint8_t)(*m + 1); + if (ph == 0) ph = 1; /* перескок нуля, см. pop_loose_tick */ + if (ph & 0x80) { /* тряска (do_knock): 0x80..0x83 */ + /* Спецкейс уровня 13 (seg007:823) — тот же, что в pop_loose_tick: + * отложенный старт от check_fall_flo живёт в старшем бите, и гасить + * его нельзя. Здесь он тоже нужен: гряда лежит в комнате СВЕРХУ, и + * пока Кид не вошёл в комнату под ней, счёт идёт заочно. */ + if (pop_current_level == POP_LOOSE_TILES_LEVEL) { /* ждём молча */ } + else if (ph >= 0x84) { ph = 0; *type = -1; } + } else if (ph >= 11) { /* loose_floor_delay — провал */ + /* remove_loose (seg007:0EB8): тайл → EMPTY, модификатором пустой + * клетки становится ТИП УРОВНЯ (по нему draw_tile_right выбирает + * blueline у дыры). Тот же порядок, что в pop_loose_tick. */ + pop_level_set_tile(room, tp, TILE_EMPTY); + /* Комната не на экране, но её колонка 9 ВИДНА в шве текущей и лежит + * в кромочных снимках — попросить главный цикл их перечитать. */ + pop_neigh_dirty = 1; + pop_loose_mob_spawn_at(room, TP_ROW[tp], TP_COL[tp]); + ph = pop_palace; + *type = -1; + } + *m = ph; +} + +/* ---- Кнопки/ворота (seg007) --------------------------------------- * + * Таблицы связей LINKLOC/LINKMAP — в pop_level (мутабельная копия в W2). + * doorlink1 (d1) = LINKLOC, doorlink2 (d2) = LINKMAP. */ +static uint8_t get_doorlink_timer(uint8_t i) { return (uint8_t)(pop_doorlink2(i) & 0x1F); } +static void set_doorlink_timer(uint8_t i, uint8_t v) +{ + pop_doorlink2_set(i, (uint8_t)((pop_doorlink2(i) & 0xE0) | (v & 0x1F))); +} +static uint8_t get_doorlink_tile(uint8_t i) { return (uint8_t)(pop_doorlink1(i) & 0x1F); } +static uint8_t get_doorlink_next(uint8_t i) { return (uint8_t)(!(pop_doorlink1(i) & 0x80)); } +static uint8_t get_doorlink_room(uint8_t i) +{ + return (uint8_t)(((pop_doorlink1(i) & 0x60) >> 5) + ((pop_doorlink2(i) & 0xE0) >> 3)); +} + +/* trigger_gate (seg007:092C): изменить openness ворот по типу кнопки. + * Возвращает тип анимации для add_trob (или -1 = ничего). */ +static int8_t trigger_gate(uint8_t room, uint8_t tilepos, uint8_t button_type) +{ + uint8_t *mod = pop_trob_modif(room); + uint8_t modifier = mod[tilepos]; + if (button_type == TILE_OPENER) { + if (modifier == 0xFF) return -1; /* уже насовсем открыт */ + if (modifier >= 188) { mod[tilepos] = 238; return -1; } /* открыт → держать */ + mod[tilepos] = (uint8_t)((modifier + 3) & 0xFC); + return 1; /* обычное открытие */ + } else if (button_type == TILE_DEBRIS) { /* насовсем (died_on_button) */ + if (modifier < 188) return 2; + mod[tilepos] = 0xFF; + return -1; + } else { /* closer */ + if (modifier != 0) return 3; /* быстро закрыть */ + return -1; + } +} + +/* trigger_1 (seg007:0999): цель = ворота → trigger_gate; иначе -1 (leveldoor + * и прочее — отложено). */ +static int8_t trigger_1(uint8_t target_type, uint8_t room, uint8_t tilepos, uint8_t button_type) +{ + if (target_type == TILE_GATE) + return trigger_gate(room, tilepos, button_type); + if (target_type == TILE_LEVELDOOR) { + /* trigger_1 (seg007:0999): дверь уровня открывается ОДИН раз — если + * створка уже тронулась (modif != 0), кнопка ничего не делает. */ + uint8_t *m = pop_trob_modif(room); + if (m && m[tilepos] == 0) return 1; + return -1; + } + return -1; +} + +/* do_trigger_list (seg007:09E5): обойти цепочку LINKLOC от index, для каждой + * цели добавить trob (если trigger_1 >= 0). */ +static void do_trigger_list(uint8_t index, uint8_t button_type) +{ + for (;;) { + uint8_t room = get_doorlink_room(index); + uint8_t tilepos = get_doorlink_tile(index); + uint8_t target_type = pop_level_tile(room, tilepos); + int8_t result = trigger_1(target_type, room, tilepos, button_type); + if (result >= 0) pop_add_trob(room, tilepos, result); + if (get_doorlink_next(index) == 0) break; + index++; + } +} + +void pop_trigger_button(uint8_t room, uint8_t tilepos, + uint8_t button_type, uint8_t modifier, + uint8_t snd) __banked +{ + uint8_t link_timer = get_doorlink_timer(modifier); + if (link_timer != 0x1F) { /* не заклинено */ + /* Оригинал ставит 5; вынесено в pop_tune.h, потому что проверять + * руками проход в закрывающуюся решётку при таком окне почти + * невозможно (см. BUG-GATE-PASS-1). */ + set_doorlink_timer(modifier, POP_DBG_GATE_HOLD); + if (link_timer < 2) { + pop_add_trob(room, tilepos, 1); /* кнопка нажимается */ + /* seg007:766: щелчок кнопки — шум, и страж оборачивается + * (флаг обязателен даже без звука, BUG-GUARD-DEAF-1). + * + * Звук НЕ у всех вызовов: у оригинала первый параметр playsound, + * и в трёх местах он нулевой — вход на уровень (seg003:170), + * выход Джаффара (seg002:520) и зелье «открыть» (seg006:1890, + * у нас пока не портировано). Там кнопка нажимается служебно, + * щёлкать нечему. */ + if (snd) pop_sfx_play(3); + is_guard_notice = 1; + } + do_trigger_list(modifier, button_type); + } +} + +/* animate_button (seg007:0D3A): отсчитать таймер связи; <2 → кнопка + * отжимается (trob кончился). idx = модификатор кнопки (индекс LINKLOC). */ +static void animate_button(uint8_t idx, int8_t *type) +{ + if (*type >= 0) { + uint8_t timer = get_doorlink_timer(idx); + timer = (uint8_t)(timer ? timer - 1 : 0); + set_doorlink_timer(idx, timer); + if (timer < 2) *type = -1; + } +} + +/* animate_leveldoor (seg007:05F1): створка двери уровня. type 0..2 — + * открытие (modif 0 -> 43, по пикселю за кадр), type >= 3 — быстрое + * закрытие со скоростями {0,5,17,99} (после выхода из уровня). Готово -> + * type = -1. */ +static const uint8_t leveldoor_close_speeds[5] = {0, 5, 17, 99, 0}; + +/* ---- Спецсобытие уровня 4: ПОЯВЛЕНИЕ ЗЕРКАЛА ------------------------ * + * + * seg007:0457, хвост animate_leveldoor: когда дверь выхода ДОРИСОВАЛА + * открытие и leveldoor_open переходит 0/2 -> 1, оригинал ставит тайл + * зеркала прямо в данные комнаты: + * + * get_tile(mirror_room, mirror_column, mirror_row); + * curr_room_tiles[curr_tilepos] = tiles_13_mirror; + * + * Ставится он в ЧУЖУЮ комнату (4), а не в ту, где дверь. В статике уровня + * зеркала НЕТ НИ В ОДНОМ файле (проверено перебором всех 15 res200N.bin), + * поэтому его спрайты 75/77 пришлось добавить в атлас явным списком — + * MIRROR_ENV_IDS в pop_pack_bg.py (грабли memory pop_atlas_dynamic_ids). */ +#define TILE_MIRROR 0x0D /* MIRROR_* — в pop_guard.h (нужны и ИИ тени) */ + +static void place_mirror(uint8_t cur_room) +{ + if (pop_current_level != MIRROR_LEVEL) return; + pop_level_set_tile(MIRROR_ROOM, MIRROR_TILEPOS, TILE_MIRROR); + /* Комната зеркала обычно НЕ та, где дверь выхода, — игрок войдёт в неё + * позже и она отрисуется целиком. Но если она уже на экране, тайл надо + * вернуть: зеркало ДОБАВЛЯЕТ графику поверх пустого места, поэтому + * хватает обычной пометки (wipe пола + полный draw_tile). */ + if (cur_room == MIRROR_ROOM) + pop_set_redraw(MIRROR_TILEPOS, POP_RD_FLOOR, 2); +} + +static void animate_leveldoor(uint8_t *m, int8_t *type, uint8_t cur_room) +{ + if (*type < 0) return; + if (*type >= 3) { + uint8_t sp; + if (*type < 7) (*type)++; + sp = leveldoor_close_speeds[*type - 3]; + if (sp >= *m) { *m = 0; *type = -1; pop_sfx_play(14); } /* seg007:437 */ + else { + *m = (uint8_t)(*m - sp); + /* seg007:442: створку НА ЗАКРЫТИИ перебивать можно. */ + if (*type == 4) { pop_sfx_slide_intr = 1; pop_sfx_play(15); } + } + } else { + /* seg007:464: на ОТКРЫТИИ створка неперебиваема — иначе съезд + * обрывается посреди и «конца» не слышно. */ + if (*m < 43) { (*m)++; pop_sfx_slide_intr = 0; pop_sfx_play(15); } + if (*m >= 43) { + *m = 43; *type = -1; + /* seg007:455: дверь открылась — оригинал глушит всё. Без этого + * неперебиваемый съезд (1,6 с) продолжал бы блокировать очередь. */ + pop_sfx_stop(); + /* Условие оригинала — переход ИЗ 0 или 2, то есть ровно один раз + * за открытие; иначе зеркало ставилось бы заново каждый кадр, + * пока дверь стоит открытой. */ + if (pop_leveldoor_open == 0 || pop_leveldoor_open == 2) { + pop_leveldoor_open = 1; + place_mirror(cur_room); + } + pop_leveldoor_open = 1; + } + } +} + +/* animate_door (seg007:0522): openness по типу анимации. *m — modif ворот, + * *type — 0 закрытие / 1 открытие / 2 насовсем / 3..8 быстрое закрытие. */ +static const uint8_t gate_close_speeds[9] = {0, 0, 0, 20, 40, 60, 80, 100, 120}; +static const int8_t door_delta[3] = {-1, 4, 4}; + +/* play_door_sound_if_visible (seg007:1239): СЛЫШНА ли решётка. + * + * Правило не «решётка в текущей комнате», как можно подумать, а такое: + * - решётка в комнате СЛЕВА и стоит в колонке 9 — слышна (это тайл вплотную + * к левому краю экрана, игрок фактически видит её створку в шве); + * - решётка в ОТРИСОВАННОЙ комнате и НЕ в колонке 9 — слышна (колонка 9 + * уезжает за правый край); + * - особый случай оригинала: уровень 3, комната 2 — слышна всегда. + * Всё остальное молчит, иначе на 13-м уровне скрипели бы решётки со всей + * карты. + * + * Берём вариант ПОД FIX_GATE_SOUNDS (условия через ИЛИ, а не тернарником): + * в SDLPoP этот фикс включён по умолчанию, а сравниваемся мы именно с ним. + * Отличие только в вырожденном случае room_L == cur_room. */ +static uint8_t door_audible(uint8_t room, uint8_t tp, + uint8_t room_L, uint8_t cur_room) +{ + uint8_t col = TP_COL[tp]; + if (pop_current_level == 3 && room == 2) return 1; + if (room == room_L && col == 9) return 1; + if (room == cur_room && col != 9) return 1; + return 0; +} + +static void animate_door(uint8_t *m, int8_t *type, uint8_t audible) +{ + int8_t anim_type = *type; + uint8_t mod = *m; + if (anim_type >= 0) { + if (anim_type >= 3) { /* быстрое закрытие */ + if (anim_type < 8) { anim_type++; *type = anim_type; } + { + int new_mod = (int)mod - gate_close_speeds[anim_type]; + if (new_mod < 0) { mod = 0; *type = -1; pop_sfx_play(6); } + else mod = (uint8_t)new_mod; + } + } else if (mod != 0xFF) { + mod = (uint8_t)(mod + door_delta[anim_type]); + if (anim_type == 0) { /* закрытие */ + /* seg007:376: скрип закрывающейся решётки — раз в четыре шага + * модификатора; лязг «встала» (7) — в конце. Оба через + * гейт слышимости (gate_stop, seg007:05E3, тоже через него). */ + if (mod != 0) { + if (mod < 188 && (mod & 3) == 3 && audible) pop_sfx_play(4); + } else { + *type = -1; + if (audible) pop_sfx_play(7); /* gate_stop */ + } + } else { /* открытие */ + if (mod >= 188) { + /* Лязг после ОБЫЧНОГО открытия оригинал даёт без гейта + * (seg007:395 — прямой play_sound), а после открытия + * насовсем — через gate_stop, то есть с гейтом. */ + if (anim_type < 2) { mod = 238; *type = 0; pop_sfx_play(7); } + else { + mod = 0xFF; *type = -1; + if (audible) pop_sfx_play(7); + } + } else if ((mod & 7) == 0) pop_sfx_play(5); /* seg007:386 */ + } + } else { + *type = -1; + if (audible) pop_sfx_play(7); /* gate_stop, seg007:404 */ + } + } + *m = mod; +} + +uint8_t pop_trob_room_active(uint8_t room) __banked +{ + uint8_t i; + for (i = 0; i < trobs_count; i++) + if (trobs[i].room == room) return 1; + return 0; +} + + +/* Темп анимации пламени в ЛОГИЧЕСКИХ кадрах (один логический кадр = 3 + * vsync и соответствует игровому тику оригинала): + * 1 — кадр пламени каждый тик, как в оригинале; кадры при этом ровные по + * тактам, что нужно для счёта бюджета; + * 2 — вдвое реже (так было до 2026-07-30; пламя шло на половинной + * скорости, а бюджет кадра «прыгал» на 6.2 % от того, попал кадр на + * анимацию или нет). */ +#define TORCH_ANIM_DIV 1 + +static uint8_t torch_tick; +#if TORCH_ANIM_DIV > 1 +#define TORCH_ANIM_NOW ((uint8_t)(torch_tick % TORCH_ANIM_DIV) == 0) +#else +#define TORCH_ANIM_NOW 1 +#endif + +void pop_trob_qsave(pop_qs_io_t *io) __banked +{ + uint8_t i; + pop_qs_bytes(io, room_modif, sizeof(room_modif)); + pop_qs_bytes(io, room_seen, sizeof(room_seen)); + pop_qs_u8(io, &trobs_count); + for (i = 0; i < TROBS_MAX; i++) { + pop_qs_u8(io, &trobs[i].room); + pop_qs_u8(io, &trobs[i].tilepos); + pop_qs_i8(io, &trobs[i].type); + } + pop_qs_u16(io, &trob_seed.lo); + pop_qs_u16(io, &trob_seed.hi); + pop_qs_u8(io, &torch_tick); + if (io->reading && io->apply) { + if (trobs_count > TROBS_MAX) { io->failed = 1; return; } + for (i = 0; i < POP_ROOMTILES; i++) trob_drawn[i] = 0; + pop_redraw_reset(); + } +} + +void pop_process_trobs(uint8_t cur_room) __banked +{ + uint8_t i, w = 0; +#if TORCH_ANIM_DIV > 1 + torch_tick++; +#endif + + /* Перерисовка «покоя» законченных пик/кнопок (обе страницы). + * row/col ведём бегущими счётчиками, а НЕ через i/10, i%10: иначе SDCC + * выносит оба деления (__divuchar/__moduchar) в вершину цикла как общее + * подвыражение аргументов redraw — 30*2=60 делений/кадр даже когда нет + * активных пик/кнопок (тот же случай, что был в pop_loose_tick). */ + if (trobs_count == 0) return; + + /* Префетч кодов тайлов всех trob'ов за ОДИН map/unmap окна 0, а не + * pop_level_tile() на каждый trob (тот свапал W0 каждый вызов = N свапов + * за кадр даже без анимации). Дальше цикл (с редроями) идёт с + * размапленным W0 — как раньше. */ + { + uint8_t k; + pop_level_access_begin(); + for (k = 0; k < trobs_count; k++) { + uint8_t tp = trobs[k].tilepos; + uint8_t c = pop_level_tile_raw(trobs[k].room, tp); + trob_code[k] = c; + trob_rcode[k] = 0; + if ((c == TILE_TORCH || c == TILE_TORCH_DEBRIS) && TP_COL[tp] < 9) + trob_rcode[k] = (uint8_t)(pop_level_tile_raw(trobs[k].room, + (uint8_t)(tp + 1)) & 0x1F); + } + pop_level_access_end(); + } + + pop_dbg_m8(); /* ЗАМЕР: префетч кодов тайлов сделан */ + + /* Указатель на модификаторы КОМНАТЫ кэшируется между итерациями. + * + * pop_trob_modif объявлен __banked, и вызов его на КАЖДЫЙ trob — это + * трамплин из банка 6 в банк 6 же... нет: из горячего цикла в банковую + * функцию, то есть полный переход через резидент. А комната у trob'ов + * в подавляющем большинстве кадров ОДНА (все они из cur_room, чужие + * появляются лишь у брошенных плит соседней комнаты). Тот же паттерн + * «трамплин в цикле», что дал −23 784 на луче видимости стража + * (P2b) и −14 118 на guard_over_kid (P16). + * + * Сбрасывается на каждом кадре: room_seen внутри pop_trob_modif живёт + * дольше, но указатель на строку room_modif может смениться при + * перезагрузке комнаты, поэтому кэш локальный для одного прохода. */ + { + uint8_t mod_room = 0; /* 0 = «ещё не брали» (комнаты 1..24) */ + uint8_t *mod = 0; + /* Сосед СЛЕВА нужен гейту слышимости решёток (door_audible). Берём один + * раз на проход: pop_room_link маппит страницу уровня, а решёток в + * списке может быть несколько. */ + uint8_t room_L = pop_room_link(cur_room, 0 /* L */); + + for (i = 0; i < trobs_count; i++) { + uint8_t room = trobs[i].room; + uint8_t tp = trobs[i].tilepos; + uint8_t code = trob_code[i]; + int8_t type = trobs[i].type; + + if (room != mod_room) { mod = pop_trob_modif(room); mod_room = room; } + + switch (code) { + case TILE_SPIKE: + animate_spike(&mod[tp], &type); + break; + case TILE_CHOMP: + animate_chomper(&mod[tp], &type, room, tp, cur_room); + break; + case TILE_OPENER: + case TILE_CLOSER: + animate_button(mod[tp], &type); /* mod[tp] = индекс LINKLOC кнопки */ + break; + case TILE_GATE: + animate_door(&mod[tp], &type, door_audible(room, tp, room_L, cur_room)); + break; + case TILE_LOOSE: + animate_loose(&mod[tp], &type, room, tp, cur_room); + break; + case TILE_LEVELDOOR: + animate_leveldoor(&mod[tp], &type, cur_room); + break; + case TILE_TORCH: + case TILE_TORCH_DEBRIS: + /* animate_torch (seg007:03C1): анимация вечная — type остаётся + * 1, пока комната отрисована. Темп — TORCH_ANIM_DIV. */ + if (room == cur_room && TORCH_ANIM_NOW) + mod[tp] = get_torch_frame(mod[tp]); + break; + case TILE_POTION: + /* animate_potion (seg007:03E9): крутим младшие 3 бита (фаза + * пузырька), старшие 5 — тип зелья. */ + if (room == cur_room) + mod[tp] = (uint8_t)((mod[tp] & 0xF8) | bubble_next_frame(mod[tp] & 7)); + break; + case TILE_SWORD: + /* animate_sword (seg007:0425): счётчик вниз; на 0 — новый период + * 0x28..0x67. Блеск рисуется РОВНО на modif==1 (draw_tile берёт + * кадр (modif==1)+10), т.е. одиночная вспышка раз в 40..103 тика. */ + if (room == cur_room) { + if (--mod[tp] == 0) + mod[tp] = (uint8_t)((trob_prandom(255) & 0x3F) + 0x28); + } + break; + default: + type = -1; + break; + } + trobs[i].type = type; + + if (room == cur_room && + (code == TILE_TORCH || code == TILE_TORCH_DEBRIS || code == TILE_POTION)) { + /* Пламя ЗАПЕКАЕТСЯ в фон (GFX_BANK_NORMAL внутри pop_torch_draw), + * пузырёк зелья — нет, ему нужен heal. Порядок «пламя раньше + * перерисовок тайлов» обязателен: heal чомпера-соседа вернёт + * запечённый огонь из ОЗУ-копии, а его собственная графика ляжет + * ПОВЕРХ — ровно как в оригинале, где draw_tile_anim_right (пламя + * левого соседа) идёт до draw_tile_anim (сам чомпер). Выносить + * отрисовку ПОСЛЕ pop_redraw_needed нельзя: пламя окажется поверх + * челюстей. */ + /* Таблицей, а не tp/10 и tp%10: у Z80 деления нет, SDCC зовёт + * __divuchar + __moduchar — на два факела это четыре вызова за + * кадр (замер MAME). Тайл всегда 0..29. */ + uint8_t trow = TP_ROW[tp], tcol = TP_COL[tp]; + if (code == TILE_POTION) { + pop_potion_draw(trow, tcol, mod[tp]); + } else { + pop_torch_draw(trow, tcol, mod[tp]); + /* Порт set_redraw_anim_right (seg007:0101): animate_torch метит + * ПРАВОГО СОСЕДА, и redraw_needed рисует его слой anim в + * порядке draw_tile_anim_topright -> draw_tile_anim_right + * (пламя) -> draw_tile_anim (СВОЯ графика тайла). То есть в + * оригинале челюсти чомпера возвращаются поверх огня КАЖДЫЙ + * кадр факела, а не только пока жива их собственная анимация. + * + * У нас пламя запекается напрямую (pop_torch_draw), минуя + * механизм пометок, поэтому у ЗАСТЫВШЕГО чомпера возвращать + * его было нечем — огонь накрывал челюсти (баг найден на + * уровне 4). Метим сосед-тайл сами, ОДНУ страницу: факел + * анимируется каждый кадр, значит обе страницы получат свою + * перерисовку по очереди. Порядок сходится: этот блок идёт + * ДО pop_redraw_needed. + * + * РАСХОЖДЕНИЕ С ОРИГИНАЛОМ (осознанное): он метит соседа + * безусловно, мы — только когда там чомпер. Безусловная + * пометка = перерисовка тайла каждый кадр на каждый факел, + * а слой anim рисует ещё пики/зелье/меч (seg008:644). Их + * случай «застыл справа от факела» в BUGS_OPEN.md как открытый: + * пики и меч рядом с факелом на уровнях 1-4 не встретились. */ + if (trob_rcode[i] == TILE_CHOMP) + pop_set_redraw((uint8_t)(tp + 1), POP_RD_CHOMP_ANIM, 1); + } + } + if (room == cur_room && code == TILE_SPIKE) { + /* ПИКИ — редрой КАЖДЫЙ кадр, пока trob жив: animate_spike + * (seg007:0353) зовёт redraw_21h БЕЗУСЛОВНО, а не по смене кадра. + * Это не «лишняя работа оригинала»: выдвинутая пика живёт только в + * видео-ОЗУ (банк SPRITE, в фон не запечена), а heal персонажа каждый кадр + * возвращает под спрайтом Кида ПЕЧЁНЫЙ фон — то есть стирает те + * части остриёв, что попали под его прямоугольник. При редрое + * по смене кадра пика держится 15 кадров (modif 0x8F..0x81) с + * неизменной сигнатурой, и стёртое уже не возвращается: рядом с + * Кидом остриё пропадало, а нижний кусок мерцал (стёрт на одной + * странице дабл-буфера из двух). */ + /* Последний кадр (пика убралась, trob умирает) — пометить ОБЕ + * страницы: живой trob метит каждый кадр по одной. */ + pop_set_redraw(tp, POP_RD_SPIKE, (uint8_t)(type < 0 ? 2 : 1)); + } + if (room == cur_room && code == TILE_CHOMP) { + /* ТОЛЬКО ПОКА ФАЗА < 6 — как в оригинале (animate_chomper, + * seg007:0448 заканчивается `if ((curr_modifier & 0x7F) < 6) + * redraw_at_trob();`). + * + * Это не оптимизация оригинала, а точное следствие таблицы поз: + * CHOMP_FRAM1 = {3,2,0,1,4,3,3}, то есть с фазы 5 и до конца + * круга (POP_CHOMPER_SPEED = 15) поза одна и та же — 3. + * Перерисовывать её десять кадров подряд значит рисовать ровно + * ту же картинку, а стоит это 190 260 тактов НА КАДР: полный + * draw_tile плюс heal 32x64, 24 % работы кадра в 11/15 + * (замер 2026-08-19). + * + * На фазе 5 метим ОБЕ страницы дабл-буфера: она последняя + * рисуемая, и её поза обязана лечь на обе, иначе на второй + * останется поза фазы 4 и чомпер будет дрожать через кадр. + * Сходится и по кадрам: вторую страницу эта пометка догоняет в + * кадре фазы 6, где поза та же самая (CHOMP_FRAM1[6] == 3). + * + * Про type < 0 отдельной ветки больше нет: trob снимается сам + * только при frame >= 6 (см. animate_chomper выше), то есть + * когда перерисовки уже не идут, а на экране с фазы 5 лежит + * финальная поза на обеих страницах. */ + uint8_t ph = (uint8_t)(mod[tp] & 0x7F); + if (ph < 6) + pop_set_redraw(tp, POP_RD_CHOMP, (uint8_t)(ph == 5 ? 2 : 1)); + } + if (room == cur_room && code == TILE_GATE) { + /* draw_trob (seg007:01E6), которым заканчивается animate_door: + * пока trob жив — решётка едет каждый кадр, помечаем ТЕКУЩУЮ + * страницу; последний кадр (type<0, ворота встали) — ОБЕ, иначе + * на второй странице дабл-буфера застынет предыдущее положение + * и решётка дрожит через кадр. Тайлпос — САМИ ворота, соседа + * справа выбирает pop_gate_redraw. */ + pop_set_redraw(tp, POP_RD_GATE, (uint8_t)(type < 0 ? 2 : 1)); + } + if (room == cur_room && code == TILE_LEVELDOOR) { + /* Створка едет каждый кадр — перерисовываем ПРАВУЮ половину (там + * её рисует draw_tile_anim_right) С ЗАПЕЧКОЙ в ОЗУ-копию: иначе + * heal персонажа вернул бы из фона закрытую створку. Куски двери + * непрозрачны, поэтому wipe не нужен. */ + /* Створку рисует ПРАВАЯ половина двери; последний кадр анимации + * помечаем на обе страницы, иначе дверь дрожит через кадр. */ + if (TP_COL[tp] < 9) + pop_set_redraw((uint8_t)(tp + 1), POP_RD_LEVELDOOR, + (uint8_t)(type < 0 ? 2 : 1)); + } + if (room == cur_room && code == TILE_SWORD) { + /* МЕЧ — редрой только на смене видимого кадра (блеск = modif==1): + * спрайт запечён в фон (draw_tile), поэтому перерисовываем тайл + * той же схемой, что и кнопку — текущая страница сразу, вторая + * следующим кадром через rest-цикл. */ + uint8_t sig = (uint8_t)(mod[tp] == 1); + if (sig != trob_drawn[tp]) { + pop_set_redraw(tp, POP_RD_FLOOR, 2); /* обе страницы */ + trob_drawn[tp] = sig; + } + } + if (room == cur_room && (code == TILE_OPENER || code == TILE_CLOSER)) { + /* КНОПКА — редрой только при смене видимого состояния (нажата = + * таймер связи >1): пока Кид её держит, картинка стабильна. + * Рисуем ТЕКУЩУЮ страницу сразу + =1 (вторая страница + * дабл-буфера следующим кадром через rest-цикл). tp/10,tp%10 + * считаем только в момент редроя (редко), а не каждый кадр. */ + uint8_t sig = (uint8_t)(get_doorlink_timer(mod[tp]) > 1); + if (sig != trob_drawn[tp]) { + pop_set_redraw(tp, POP_RD_FLOOR, 2); /* обе страницы */ + trob_drawn[tp] = sig; + } + } + } + } /* конец блока с кэшем mod/mod_room */ + + /* compact: удалить завершённые (type < 0). */ + for (i = 0; i < trobs_count; i++) + if (trobs[i].type >= 0) trobs[w++] = trobs[i]; + trobs_count = w; +} diff --git a/applications/SprPoP/src/pop_trob.h b/applications/SprPoP/src/pop_trob.h new file mode 100644 index 0000000..3be05ce --- /dev/null +++ b/applications/SprPoP/src/pop_trob.h @@ -0,0 +1,74 @@ +/* + * pop_trob.h — P0-каркас: персистентное per-room modifier-состояние + + * список анимируемых тайлов (trob). Порт SDLPoP seg007 (process_trobs/ + * add_trob/animate_tile) + curr_room_modif. + * + * ЗАЧЕМ: тайлы-объекты (пики, ворота, кнопки) имеют состояние (modifier), + * которое живёт МЕЖДУ входами в комнату и может меняться КРОСС-КОМНАТНО + * (кнопка в одной комнате открывает ворота в другой). room_modif[room] + * — единственный источник модификаторов для отрисовки (pop_bg draw_tile + * читает его как bg), как curr_room_modif в оригинале. + * + * Список trob хранит (room, tilepos, type) активных анимаций; каждый кадр + * pop_process_trobs продвигает их (animate_spike/door/button по коду тайла), + * пишет результат в room_modif и перерисовывает изменившиеся тайлы ТЕКУЩЕЙ + * комнаты на back-странице (2-page, как loose). Невидимые комнаты + * анимируются в состоянии (без отрисовки) — при входе рисуются статически. + */ +#ifndef POP_TROB_H +#define POP_TROB_H + +#include "pop_qsave.h" + +#include + +#define POP_NROOMS 24 +#define POP_ROOMTILES 30 + +/* Сброс всего состояния (новый уровень / рестарт): забыть все комнаты и + * очистить список trob. */ +void pop_trob_reset(void) __banked; +void pop_trob_qsave(pop_qs_io_t *io) __banked; + +/* Указатель на persistent modifier-массив комнаты room (1..24), 30 байт. + * При ПЕРВОМ обращении лениво инициализируется из backtable уровня + * (pop_level_room_bg). Далее ЖИВЁТ (не перезагружается). room вне + * диапазона → 0. */ +uint8_t *pop_trob_modif(uint8_t room) __banked; + +/* Добавить/обновить trob (room, tilepos, type). Если уже есть — сменить + * type (как SDLPoP add_trob/find_trob). type<0 = будет удалён. */ +void pop_add_trob(uint8_t room, uint8_t tilepos, int8_t type) __banked; + +/* Продвинуть все trob на кадр (animate_* по коду тайла из уровня), записать + * modif, перерисовать тайлы ТЕКУЩЕЙ (cur_room) комнаты на back-странице. + * Звать в основном цикле ПОСЛЕ set_draw_page(back), рядом с loose. */ +void pop_process_trobs(uint8_t cur_room) __banked; + +/* Кид встал на кнопку (opener/closer): взвести таймер связи + обойти цепочку + * LINKLOC, добавив trob целям (ворота). Порт seg007 trigger_button. + * room/tilepos — кнопка; button_type — её тайл-код; modifier — её modif + * (= индекс в LINKLOC). Звать из check_press (pop_map). */ +void pop_trigger_button(uint8_t room, uint8_t tilepos, + uint8_t button_type, uint8_t modifier, + uint8_t snd) __banked; + +/* Есть ли активные trob в комнате room? Для перерисовки ворот соседа через + * шов (SprPoP не дёргает шов, пока сосед не анимируется). */ +uint8_t pop_trob_room_active(uint8_t room) __banked; + +/* Смена комнаты: сбросить отложенные перерисовки «покоя» текущей комнаты + * (trob-состояние в room_modif персистентно и НЕ трогается). Звать в + * enter_room. */ +void pop_trob_room_changed(void) __banked; + +/* anim_tile_modif (seg000:0B12): завести анимации тайлов комнаты (факелы, + * зелья) со случайной стартовой фазой. Звать в enter_room. */ +void pop_trob_anim_room(uint8_t room) __banked; + +/* start_chompers (seg007:0F13): запустить чомперы РЯДА персонажа. Звать + * там же, где оригинал: смена ряда в play_seq (SEQ_UP/SEQ_DOWN), start_fall, + * приземление и вход в комнату. */ +void pop_start_chompers(uint8_t room, int8_t row) __banked; + +#endif diff --git a/applications/SprPoP/src/pop_tune.h b/applications/SprPoP/src/pop_tune.h new file mode 100644 index 0000000..f2e3730 --- /dev/null +++ b/applications/SprPoP/src/pop_tune.h @@ -0,0 +1,149 @@ +/* + * pop_tune.h — НАСТРАИВАЕМЫЕ КОНСТАНТЫ движка (аналог custom_options + * SDLPoP) в одном месте. + * + * Зачем отдельный заголовок. У оригинала «особые события» уровней зашиты + * магическими числами прямо в код (`current_level == 3 && Char.room == 7`), + * и SDLPoP вынес их в структуру `custom_options_type` (types.h) с + * загрузкой из SDLPoP.ini и правкой через меню Settings/Mods. Нам нужно + * то же самое по двум причинам: + * + * 1. ОТЛАДКА. Некоторые сцены руками почти не проверить (успеть в + * закрывающуюся решётку), и единственный вменяемый способ — временно + * растянуть тайминг. Такие крутилки обязаны жить в одном месте, а не + * прятаться числами по трём модулям. + * 2. МОДЫ. Тот, кто захочет свой набор уровней, меняет ровно эти числа. + * + * Пока это только `#define` — ни INI, ни меню нет (см. TASKS_OPEN.md, запись + * TUNE-1). Но правило уже действует: НОВОЕ «особое событие» или тайминг + * заводится ЗДЕСЬ, а не константой по месту. Имена — как у SDLPoP + * (custom->имя), чтобы сверка с оригиналом оставалась механической. + */ +#ifndef POP_TUNE_H +#define POP_TUNE_H + +/* ---- Чекпойнт уровня 3 (SDLPoP: checkpoint_*) ----------------------- * + * Оригинал: level3_set_chkp (seg002:0665) взводит флаг, когда Кид уходит + * ВЛЕВО ИЗ комнаты 7 (вызов стоит до goto_other_room, поэтому Char.room там + * ещё старая); do_startpos (seg003:0141) подменяет по + * нему стартовую позицию и убирает loose-плиту, через которую туда + * попадают (иначе после респавна дорога назад была бы уже провалена). */ +#define POP_CHKP_LEVEL 3 /* checkpoint_level */ +#define POP_CHKP_ROOM 7 /* комната, ИЗ КОТОРОЙ уходят влево (у + * SDLPoP хардкод; это НЕ комната + * назначения — см. sprpop.c) */ +#define POP_CHKP_RESPAWN_ROOM 2 /* checkpoint_respawn_room */ +#define POP_CHKP_RESPAWN_TILEPOS 6 /* checkpoint_respawn_tilepos */ +#define POP_CHKP_RESPAWN_DIR (-1) /* checkpoint_respawn_dir: влево */ +#define POP_CHKP_CLEAR_ROOM 7 /* checkpoint_clear_tile_room */ +#define POP_CHKP_CLEAR_COL 4 /* checkpoint_clear_tile_col */ +#define POP_CHKP_CLEAR_ROW 0 /* checkpoint_clear_tile_row */ + +/* ---- Отладочные крутилки (в оригинале и в SDLPoP их НЕТ) ------------ * + * POP_DBG_GATE_HOLD — сколько кадров решётка держится поднятой после + * нажатия кнопки, ПРЕЖДЕ чем пойдёт вниз. В оригинале это таймер связи + * дверей: trigger_button ставит 5, animate_button вычитает по единице, и + * решётка стоит, пока таймер > 1 (то есть ~4 кадра после того, как Кид + * сошёл с кнопки). Проверять руками проход в закрывающуюся решётку при + * таком окне почти невозможно, поэтому значение вынесено сюда: поставь + * 30 — и успеешь дойти спокойно. ОБЯЗАН быть 5 в игровой сборке. + * + * ПОТОЛОК 30. Таймер связи — пять бит модификатора кнопки (маска 0x1F), а + * значение 31 у оригинала означает «событие заклинено» (`link_timer != 0x1F` + * в trigger_button). Больше 30 ставить нельзя — кнопка перестанет + * срабатывать вовсе. */ +#define POP_DBG_GATE_HOLD 5 + +/* ---- Улучшения SDLPoP (в ОРИГИНАЛЕ их нет) -------------------------- * + * POP_ENABLE_JUMP_GRAB — зацепиться за кромку можно ПРЯМО В ПРЫЖКЕ, если + * Shift зажат заранее (SDLPoP `enable_jump_grab`, seg006:1228). Ваниль + * разрешает зацеп только в начале падения (`check_grab`, кадры 102..105), + * то есть Shift приходится нажимать уже в полёте — это и раздражало на + * приёмке. 0 — как в оригинале, 1 — как в SDLPoP с включёнными + * enhancements. */ +#define POP_ENABLE_JUMP_GRAB 1 + +/* ---- Чомперы (SDLPoP: custom->chomper_speed) ------------------------ * + * Период смыкания челюстей в кадрах: фаза крутится 1..N и сбрасывается в 1. + * В оригинале 15; SDLPoP выносит это в настройку, потому что на разных + * уровнях моды любят делать чомперы быстрее. Меньше — злее. */ +#define POP_CHOMPER_SPEED 15 + +/* ---- Отладка сцены «тень крадёт зелье» (уровень 5) ------------------ * + * Тень появляется в комнате 24 сразу при входе, но СТОИТ, пока не откроют + * ворота (кол 1, ряд 0, openness >= 80) — так в оригинале + * (autocontrol_shadow_level5, seg002:1157). Чтобы посмотреть саму сцену + * (подход, питьё, уход), не проходя уровень до кнопки, поставь 1: тень + * стартует запись ходов сразу. ОБЯЗАН быть 0 в игровой сборке. */ +#ifndef POP_DBG_SHADOW_NOWAIT +#define POP_DBG_SHADOW_NOWAIT 0 +#endif + +/* ---- Зелье МЕДЛЕННОГО ПАДЕНИЯ (SDLPoP: FEATHER_FALL_LENGTH) ---------- * + * В оригинале длительность эффекта задана НЕ таймером, а звуком: эффект + * держится, пока играет sound_39_low_weight, и снимается принудительно на + * 225-м тике (do_timers, seg003:0517). Звука у нас нет, поэтому берём тот + * же порог напрямую — 225 ИГРОВЫХ КАДРОВ (у оригинала тик do_timers = кадр + * игрового цикла; 225 кадров при его 12 fps и есть FEATHER_FALL_LENGTH = + * 18,75 с из фикса SDLPoP). По ВРЕМЕНИ у нас выйдет короче ровно настолько, + * насколько наша игра быстрее оригинала — это общий вопрос темпа + * (TASKS_OPEN.md, L1-SPEED), а не свойство зелья. */ +#define POP_FEATHER_FRAMES 225 + +/* ---- Спецсобытие «МЫШЬ» (уровень 8, комната 16) ---------------------- * + * Кнопка (0,7) комнаты 16 открывает решётку (0,3) — ту, за которой лежит + * путь через комнату 4 (два чомпера) к кнопке выхода в комнате 3. Сам Кид + * до кнопки с левой площадки не дотягивается (провал в колонках 4..6), + * поэтому, когда дверь уровня уже открыта, а он всё ещё в комнате 16, + * оригинал через 150 тиков присылает мышь: она пробегает справа налево по + * верхнему ряду, наступает на кнопку и убегает обратно (do_timers + * seg003:0545, do_mouse seg003:0ABA, autocontrol_mouse seg002:07EB). + * + * Числа — custom-константы SDLPoP (data.h:821): уровень/комната/задержка/ + * стартовая X. Пороги разворота (166) и исчезновения (200) — литералы + * autocontrol_mouse. Задержка в ИГРОВЫХ КАДРАХ, как POP_FEATHER_FRAMES. */ +#define POP_MOUSE_LEVEL 8 +#define POP_MOUSE_ROOM 16 +#define POP_MOUSE_DELAY 150 +#define POP_MOUSE_START_X 200 +#define POP_MOUSE_TURN_X 166 /* x < этого — развернуться и уйти */ +#define POP_MOUSE_LEAVE_X 200 /* x >= этого в стойке — исчезнуть */ + +/* С какого уровня начинается игра (-DFIRST_LEVEL=N при сборке). Живёт + * здесь, а не в sprpop.c: старт уровня уехал в холодную половину цикла + * (sprpop_cold.c, банк 8), и константа нужна обеим. */ +#ifndef FIRST_LEVEL +#define FIRST_LEVEL 1 +#endif + +/* Уровень с зельем инверсии (переворот экрана). По нему делается + * ПРЕДзагрузка зеркального набора спрайтов персонажей — чтобы пауза на + * чтение 34 страниц пришлась на загрузку уровня, а не на глоток зелья. */ +#define POP_UPSIDE_LEVEL 9 + +/* Уровень ПАДАЮЩИХ ПЛИТ (custom->loose_tiles_*, seg000:1317 check_fall_flo). + * При входе в комнату 23 или 16 тринадцатого уровня вся гряда loose ряда 2 + * комнаты СВЕРХУ получает ОТРИЦАТЕЛЬНУЮ фазу — отложенный старт, — и плиты + * сыплются на Кида вразнобой. Комнаты сверху — 17 и 1 соответственно, и в + * них ряд 2 колонок 2..7 действительно loose (сверено по res2013.bin, + * ../docs/levels_12_15_plan.md §7.1). Диапазон 22..27 — надмножество: в + * комнате 1 тайл 24 пустой, и он молча пропускается. + * + * Комната 23 у этого уровня ещё и СТАРТОВАЯ, то есть плиты сыплются с + * первого кадра уровня — это его открывающая сцена, а не событие в + * середине. */ +#define POP_LOOSE_TILES_LEVEL 13 +#define POP_LOOSE_TILES_ROOM1 23 +#define POP_LOOSE_TILES_ROOM2 16 +#define POP_LOOSE_TILES_FIRST 22 /* ряд 2, колонка 2 */ +#define POP_LOOSE_TILES_LAST 27 /* ряд 2, колонка 7 */ + +/* Бесшовный выход уровня (tbl_seamless_exit, play_level_2 seg000:900). У + * оригинала это таблица по уровням, но заполнена она ровно одной парой: + * уровень 12 кончается, как только Кид попал в комнату 23 — двери уровня у + * него нет. Комната 23 у 13-го уровня СТАРТОВАЯ и совпадает геометрией + * ряда, поэтому шва не видно (../docs/levels_12_15_plan.md §1.7). */ +#define POP_SEAMLESS_LEVEL 12 +#define POP_SEAMLESS_ROOM 23 + +#endif /* POP_TUNE_H */ diff --git a/applications/SprPoP/src/pop_ui.c b/applications/SprPoP/src/pop_ui.c new file mode 100644 index 0000000..66a4f73 --- /dev/null +++ b/applications/SprPoP/src/pop_ui.c @@ -0,0 +1,473 @@ +/* + * pop_ui.c — текст, палитровый fade и full-screen примитивы (FG5, банк 9). + * + * Шрифты извлечены из SDLPoP в FONT\FONT.ATL. Внутри map/unmap разрешены + * лишь обращения к EMM/W0: всё, что ждёт кадр или трогает BIOS-палитру, + * выполняется строго после unmap. Это общий контракт для menu/title/PV. + */ +#include +#include +#include +#include +#include +#include +#include "pop_bg.h" /* POP_YOFF/POP_PLAYFIELD_H — граница игрового поля */ +#include "pop_font.h" +#include "pop_arc.h" +#include "pop_ui.h" +#include "pop_status.h" + +typedef struct { + uint8_t base; + uint8_t ascent; + uint8_t space; + uint8_t first; /* первый код, который есть в наборе */ + uint8_t last; +} pop_font_desc_t; + +/* Тёмный набор — тот же мелкий шрифт другим цветом; глифов в нём меньше + * (см. pop_font.h), поэтому диапазон кодов хранится у каждого набора. */ +static const pop_font_desc_t font_desc[] = { + { POP_FONT_SMALL_BASE, POP_FONT_SMALL_ASCENT, POP_FONT_SMALL_SPACE, + POP_FONT_FIRST, POP_FONT_LAST }, + { POP_FONT_BIG_BASE, POP_FONT_BIG_ASCENT, POP_FONT_BIG_SPACE, + POP_FONT_FIRST, POP_FONT_LAST }, + { POP_FONT_DARK_BASE, POP_FONT_BIG_ASCENT, POP_FONT_BIG_SPACE, + POP_FONT_DARK_FIRST, POP_FONT_DARK_LAST } +}; + +static atlas_t font_at; +static uint8_t font_ready; + +/* БУФЕР ПАЛИТРЫ ПО ФИКСИРОВАННОМУ АДРЕСУ 0x4000. DSS занимает эти 256 + * байт только при загрузке программы, дальше они наши (код начинается с + * 0x4100), и держать буфер здесь дешевле, чем класть 256 байт на стек в + * каждом вызове — идея пользователя, 2026-08-25. */ +__at (0x4000) uint8_t pop_pal_buf[256]; + +static uint8_t text_index(const pop_font_desc_t *font, uint8_t ch) +{ + if (ch < font->first || ch > font->last) ch = '?'; + return (uint8_t)(font->base + ch - font->first); +} + +int pop_ui_init(void) __banked +{ + if (font_ready) return 0; + if (atlas_load(&font_at, "FONT\\font.atl") != 0) return -1; + font_ready = 1; + return 0; +} + +void pop_ui_free(void) __banked +{ + if (!font_ready) return; + atlas_free(&font_at); + font_ready = 0; +} + +uint8_t pop_ui_ready(void) +{ + return font_ready; +} + +void pop_text_map(void) +{ + gfx_w0_map(font_at.page); +} + +void pop_text_unmap(void) +{ + gfx_w0_unmap(); +} + +uint8_t pop_text_width_mapped(pop_text_font_t font, const char *s) +{ + const pop_font_desc_t *desc = &font_desc[font]; + uint8_t width = 0; + + while (*s) { + const uint8_t *img = (const uint8_t *)atlas_image(&font_at, + text_index(desc, (uint8_t)*s++)); + width = (uint8_t)(width + img[0] + desc->space); + } + return width ? (uint8_t)(width - desc->space) : 0; +} + +void pop_text_draw_mapped(pop_text_font_t font, int x, int baseline, + const char *s) +{ + const pop_font_desc_t *desc = &font_desc[font]; + + while (*s) { + const uint8_t *img = (const uint8_t *)atlas_image(&font_at, + text_index(desc, (uint8_t)*s++)); + /* Пробел не рисует НИ ОДНОГО пикселя: его глиф целиком 0xFF, а + * текст всегда идёт прозрачным банком. Блит при этом стоит те же + * ~4,6 тыс. тактов, что и буква, поэтому в строке из полусотни + * символов пропуск пробелов — самая дешёвая экономия, какая тут + * есть (важно для статус-строк, они рисуются в игровом кадре). + * У крупного пробела вдобавок высота 0: для accel это 256 строк, + * и нулевой блит нельзя отдавать ядру даже noclip-вариантом. */ + if (img[2] && s[-1] != ' ') gfx_blit_noclip(x, baseline - desc->ascent, img); + x += img[0] + desc->space; + } +} + +void pop_text_center_mapped(pop_text_font_t font, int center, int baseline, + const char *s) +{ + pop_text_draw_mapped(font, center - pop_text_width_mapped(font, s) / 2, + baseline, s); +} + +void pop_text_right_mapped(pop_text_font_t font, int right, int baseline, + const char *s) +{ + pop_text_draw_mapped(font, right - pop_text_width_mapped(font, s), + baseline, s); +} + +/* W0 свободно: snapshot кладём в зарезервированный хвост страницы шрифта. + * 4 × 256 Б = ровно 1024 Б, [0x3C00,0x4000), и не пересекаются с атласом. */ +/* Снимок палитры лежит в странице шрифта (в W2 килобайта нет). Копируем + * memcpy, а не циклом: у SDCC это LDIR — 256 байт за ~5 400 тактов против + * примерно ста тысяч у побайтового цикла на C. Именно этот цикл и делал + * пересчёт палитры шестикадровым (замер 2026-08-26). */ +static void snapshot_chunk(uint8_t chunk, uint8_t *buf, uint8_t store) +{ + uint8_t *slot; + + gfx_w0_map(font_at.page); + slot = (uint8_t *)(POP_FONT_SNAPSHOT_OFF + (uint16_t)chunk * 256u); + if (store) memcpy(slot, buf, 256); + else memcpy(buf, slot, 256); + gfx_w0_unmap(); +} + +/* Снимок палитры целиком: дробить на мелкие порции больше не нужно — + * причина скрежета была не в длительности BIOS-вызова, а в том, какое окно + * он под себя забирал (см. pop_pal_buf выше). */ + +void pop_ui_palette_snapshot(void) __banked +{ + uint8_t chunk; + + if (!font_ready) return; + for (chunk = 0; chunk < 4; chunk++) { + gfx_pal_get(0, (uint8_t)(chunk << 6), 64, pop_pal_buf); + snapshot_chunk(chunk, pop_pal_buf, 1); + } +} + +/* Из одного исходного снимка приготовить обе экранные палитры. Умножения + * нет: шкала 1/4, 1/2, 3/4 на 8-битном значении не должна тащить делитель + * в постоянно вызываемый (во время fade) путь. */ +/* ВОСЕМЬ СТУПЕНЕЙ, а не четыре. Fade у оригинала идёт 64 шагами по два + * тика — 2,13 с плавного затухания; наши четыре ступени укладывались в + * доли секунды и выглядели как моргание, а растянуть их по времени значило + * получить четыре заметных скачка яркости. + * + * Яркость ступени — простое умножение: (v * (8 - step)) >> 3. Через + * switch с парами сдвигов выходило дороже: 768 каналов на вызов, и цепочка + * сравнений SDCC съедала больше, чем стоит само умножение. + * + * dim_step: 0 — исходная, 8 — чёрная. */ +void pop_ui_palette_dim(uint8_t dim_step, uint8_t keep_ui) __banked +{ + /* ТАБЛИЦА ЯРКОСТИ — НА СТЕКЕ, и это главное здесь по времени. + * + * Замер в MAME (2026-08-26): вызов стоил 2,44 млн тактов, то есть почти + * шесть кадров, из которых на BIOS-заливку палитры уходило всего 136 + * тысяч (8 x 17 000). Остальное съедал пересчёт: 768 умножений + * uint16 в кадровом цикле — на Z80 это вызов библиотечной функции на + * каждый канал. Со ста вызовами подряд (fade кадр за кадром) набегало + * больше десяти секунд — ровно то «очень долгое затемнение», которое + * поймал пользователь. + * + * Теперь таблица строится один раз на ступень (256 сложений, без + * единого умножения), а каналы её просто читают. 256 байт стека — + * дешёвая плата: в этой точке израсходована едва сотня байт из 1279. + * + * Вместе с memcpy для снимка это дало 487 тысяч тактов на вызов против + * 2,44 млн (замер в MAME 2026-08-26). */ + uint8_t lut[256]; + uint8_t chunk, i, start; + uint8_t *buf = pop_pal_buf; + + if (!font_ready) return; + if (dim_step > POP_PAL_DIM_BLACK) dim_step = POP_PAL_DIM_BLACK; + if (dim_step) { + uint8_t mul = (uint8_t)(POP_PAL_DIM_BLACK - dim_step); + uint16_t v = 0; + + /* Без умножения вовсе: значение растёт на mul/32 с каждым шагом, + * дробная часть копится в младших пяти битах аккумулятора. */ + for (i = 0; ; i++) { + lut[i] = (uint8_t)(v >> 5); + v += mul; + if (i == 255) break; + } + } + for (chunk = 0; chunk < 4; chunk++) { + start = (uint8_t)(chunk << 6); + snapshot_chunk(chunk, buf, 0); + if (dim_step) { + uint8_t *p = buf; + + for (i = 0; i < 64; i++, p += 4) { + uint8_t idx = (uint8_t)(start + i); + + if (keep_ui && idx >= 0x37 && idx <= 0x3F) continue; + p[0] = lut[p[0]]; + p[1] = lut[p[1]]; + p[2] = lut[p[2]]; + } + } + gfx_pal_load(0, start, 64, buf); + gfx_pal_load(1, start, 64, buf); + } +} + +void pop_ui_palette_restore(void) __banked +{ + pop_ui_palette_dim(0, 0); +} + +void pop_ui_palette_black(void) __banked +{ + uint8_t chunk; + uint16_t i; + uint8_t *black = pop_pal_buf; + + /* BIOS принимает максимум 64 записи за вызов; одним буфером покрываем + * все четыре четверти палитры, не занимая постоянную память. */ + for (i = 0; i < sizeof(black); i++) black[i] = 0; + for (chunk = 0; chunk < 4; chunk++) { + uint8_t start = (uint8_t)(chunk << 6); + gfx_pal_load(0, start, 64, black); + gfx_pal_load(1, start, 64, black); + } +} + +/* ДЛИТЕЛЬНОСТЬ ЗАДАЁТСЯ КАДРАМИ, А ПАЛИТРА ТРОГАЕТСЯ ВОСЕМЬ РАЗ. + * + * frames — сколько кадров луча длится затухание целиком (у оригинала это + * 2,13 с). Наивный цикл «ступень на каждый кадр» звал pop_ui_palette_dim + * сотню раз, а он не бесплатный: 4 чтения снимка по 256 байт, 768 каналов + * арифметики и 8 вызовов BIOS на палитру — около 9 мс, то есть половина + * кадра сверху на каждой итерации. Fade растягивался втрое против + * заказанного (поймано пользователем: «один fade in секунд восемь»). + * Теперь ступень пересчитывается только когда она СМЕНИЛАСЬ. */ +/* Возвращает 1, если прерван нажатием (только при skip=1). + * + * ЗАЧЕМ ПРЕРЫВАНИЕ. Fade — это 2,13 с, и на каждом стыке экранов заставки + * их два. Пока цикл был глухим, эти секунды нельзя было пропустить ничем: + * пропуск работал в сцене, но не между сценами, и заставка ощущалась + * невыключаемой. + * + * Кромка нажатия считается ОТ ВХОДА, как в сценах (intro_skip_requested): + * клавиша, которой закончили предыдущий экран, в момент входа ещё зажата, и + * принимать её за «пропустить» нельзя — иначе fade схлопывался бы сам. + * + * Прерванный fade всё равно доводит палитру до конечного состояния: экран + * не должен остаться на промежуточной ступени. */ +static uint8_t fade_run(uint8_t frames, uint8_t up, uint8_t skip) +{ + uint8_t i, shown = 0xFF, armed = 0, hit = 0; + + if (!frames) { + pop_ui_palette_dim(up ? 0 : POP_PAL_DIM_BLACK, 0); + return 0; + } + if (skip) { + kbd_raw_sync(); + armed = (uint8_t)!kbd_raw_any_down(); + } + for (i = 1; i <= frames; i++) { + uint8_t step = (uint8_t)((uint16_t)i * POP_PAL_DIM_BLACK / frames); + + if (up) step = (uint8_t)(POP_PAL_DIM_BLACK - step); + if (step != shown) { + pop_ui_palette_dim(step, 0); + shown = step; + } + gfx_wait_vsync(); + if (skip) { + kbd_raw_sync(); + if (!kbd_raw_any_down()) armed = 1; + else if (armed) { hit = 1; break; } + } + } + /* Довести до края: целочисленное деление могло не дойти до 8 (или 0), + * а при прерывании мы вообще ушли с середины. */ + if (hit || shown != (up ? 0 : POP_PAL_DIM_BLACK)) + pop_ui_palette_dim(up ? 0 : POP_PAL_DIM_BLACK, 0); + return hit; +} + +void pop_ui_fade_out(uint8_t steps) __banked +{ + pop_ui_palette_snapshot(); + (void)fade_run(steps, 0, 0); +} + +void pop_ui_fade_in(uint8_t steps) __banked +{ + if (!steps) { pop_ui_palette_restore(); return; } + (void)fade_run(steps, 1, 0); +} + +/* Прерываемые варианты — для заставки и катсцен. Отдельными функциями, а не + * флагом в прежних: в меню и на переходе уровня прерывать нечего, и молча + * менять там поведение не следует. */ +uint8_t pop_ui_fade_out_skip(uint8_t steps) __banked +{ + pop_ui_palette_snapshot(); + return fade_run(steps, 0, 1); +} + +uint8_t pop_ui_fade_in_skip(uint8_t steps) __banked +{ + if (!steps) { pop_ui_palette_restore(); return 0; } + return fade_run(steps, 1, 1); +} + +uint8_t pop_screen_begin(void) __banked +{ + uint8_t page = (uint8_t)(gfx_get_visible_page() ^ 1); + gfx_set_draw_page(page); + gfx_set_bank(GFX_BANK_NORMAL); + return page; +} + +void pop_screen_fill(uint8_t color) __banked +{ + setfillstyle(SOLID_FILL, color); + bar(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1); +} + +/* Экран под ИГРУ: вне поля — цвет POP_COL_OUTSIDE, внутри — цвет 0. + * + * Зачем два разных чёрных. Вспышки (молния Джафара, урон, зелья) сделаны + * подменой ЦВЕТА 0 всей палитры — как в оригинале. Пока борта и полоса HP + * тоже были нулём, мигал весь экран целиком, вместе с рамкой и жизнями. + * Красим всё, что вне поля 320x192, отдельным индексом, а в палитре держим + * его чёрным (pop_pal_outside_apply): картинка та же, но вспышка достаёт + * только игровую область. Идея пользователя, 2026-08-25. + * + * Индексы 1..15 у нас свободны: атласы кладут либо 0xFF (прозрачность), + * либо PAL_BASE+v (0x40 и выше), шрифт — 0x3F. */ +void pop_screen_fill_game(void) __banked +{ + setfillstyle(SOLID_FILL, POP_COL_OUTSIDE); + bar(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1); + setfillstyle(SOLID_FILL, BLACK); + bar(0, POP_YOFF, GFX_WIDTH - 1, POP_CLIP_BOTTOM - 1); + /* Заливка съела и обе служебные полосы — они ВНЕ игрового поля. + * Инвалидируем здесь, а не у вызывающих: точек заливки несколько + * (старт игры, смена уровня), и пропустить одну из них — ровно тот + * баг, из-за которого строка исчезала при переходе между комнатами. */ + pop_status_invalidate(); +} + +/* Только ИГРОВОЕ ПОЛЕ, без бортов. Вход в комнату перерисовывает лишь + * его: борта от комнаты к комнате не меняются, а всё, что на них живёт — + * полоса HP, статус-строка, отладочная строка — обязано вход пережить. + * Заодно это на четверть меньше заливки (192 строки вместо 256). */ +void pop_screen_fill_field(void) __banked +{ + setfillstyle(SOLID_FILL, BLACK); + bar(0, POP_YOFF, GFX_WIDTH - 1, POP_CLIP_BOTTOM - 1); +} + +void pop_screen_present(uint8_t page) __banked +{ + gfx_wait_vsync(); + gfx_set_visible_page(page); +} + +/* transition_ltr() SDLPoP: подготовленная СКРЫТАЯ страница переползает на + * видимую узкими вертикальными полосами слева направо. У оригинала + * 2px/120 FPS; здесь 4px/60 FPS даёт ту же длительность (~1,3 с), не + * удваивая число полных копий 320x256. + * + * Страницы при этом НЕ меняются местами: копируем в видимую, как оригинал + * копирует offscreen в onscreen. Поэтому у вызывающего после перехода на + * ЭКРАНЕ и в скрытой странице лежит одно и то же. */ +void pop_screen_present_ltr(void) __banked +{ + gfx_rect_t stripe; + uint8_t visible = gfx_get_visible_page(); + int x; + + gfx_set_draw_page(visible); /* source gfx_copy_page = target */ + stripe.y = 0; + stripe.h = GFX_HEIGHT; + for (x = 0; x < GFX_WIDTH; x += 4) { + if (kbd_raw_any_down()) { + /* Прервали — дорисовать остаток разом, а не бросить полукадр. */ + stripe.x = 0; stripe.w = GFX_WIDTH; + gfx_copy_page(&stripe, GFX_COPY_DIRECT); + return; + } + stripe.x = x; + stripe.w = (uint16_t)((GFX_WIDTH - x < 4) ? GFX_WIDTH - x : 4); + gfx_copy_page(&stripe, GFX_COPY_DIRECT); + gfx_wait_vsync(); + } +} + +/* Нарисовать одну полосу и отпустить её страницу. Общая часть обоих + * загрузчиков ниже: они отличаются только тем, откуда взялся атлас. */ +static int strip_blit(atlas_t *at, int x, int *y) +{ + const uint8_t *img; + + gfx_w0_map(at->page); + img = (const uint8_t *)atlas_image(at, 0); + /* gfx_blit_noclip ограничен шириной uint8 и обрезает 320px. */ + gfx_blit(x, *y, img); + *y += img[2]; + gfx_w0_unmap(); + atlas_free(at); + return 0; +} + +/* Полноэкранные изображения не помещаются одним getimage в EMM-страницу. + * Общий загрузчик полос держит лишь одну страницу и не трогает page flip: + * caller уже собрал скрытую страницу и сам решает, когда её показать. */ +int pop_screen_blit_atlas_strips(const char * const *parts, + uint8_t count, int x, int y) __banked +{ + uint8_t part; + + gfx_set_bank(GFX_BANK_NORMAL); + for (part = 0; part < count; part++) { + atlas_t at; + + if (atlas_load(&at, parts[part]) != 0) return -1; + strip_blit(&at, x, &y); + } + return 0; +} + +/* То же самое, но полосы лежат подряд в АРХИВЕ (заставка, story, PV): один + * `open` на весь экран вместо пяти — при цене открытия 51,4 мс против + * 32,6 мс на чтение это больше половины времени показа. */ +int pop_screen_blit_atlas_arc(pop_arc_t *arc, const uint8_t *tbl, + uint8_t base, uint8_t count, + int x, int y) __banked +{ + uint8_t part; + + gfx_set_bank(GFX_BANK_NORMAL); + for (part = 0; part < count; part++) { + atlas_t at; + + if (pop_arc_atlas(arc, tbl, (uint8_t)(base + part), &at) != 0) + return -1; + strip_blit(&at, x, &y); + } + return 0; +} diff --git a/applications/SprPoP/src/pop_ui.h b/applications/SprPoP/src/pop_ui.h new file mode 100644 index 0000000..686a87a --- /dev/null +++ b/applications/SprPoP/src/pop_ui.h @@ -0,0 +1,117 @@ +/* + * pop_ui.h — общий экранный слой PoP (FG5, банк 9). + * + * Здесь только примитивы: исходный SDLPoP-шрифт, палитровый fade и сборка + * полной страницы. Menu, title и cutscene держат собственные автоматы и + * строки, но не дублируют формат FONT.ATL или опасную работу с W0. + */ +#ifndef POP_UI_H +#define POP_UI_H + +#include + +typedef enum { + POP_TEXT_SMALL = 0, + POP_TEXT_BIG = 1, + /* Крупный шрифт вторым цветом (POP_FONT_DARK_COLOR): тень строки и + * текст на светлом фоне — так show_hof_text() SDLPoP рисует таблицу + * рекордов дважды (тень со сдвигом (1,1), затем сам текст). Набор + * обрезан по 'Z', см. pop_font.h. */ + POP_TEXT_BIG_DARK = 2 +} pop_text_font_t; + +/* Жизненный цикл единственного FONT\FONT.ATL. Загрузка — в холодном boot, + * освобождение — после закрытия графики; повторные вызовы безопасны. */ +int pop_ui_init(void) __banked; +void pop_ui_free(void) __banked; +uint8_t pop_ui_ready(void); + +/* Группа text_*_mapped требует активного pop_text_map() и не вызывает + * BIOS/ESTEX. Для прозрачных букв установить GFX_BANK_SPRITE до рисования. */ +void pop_text_map(void); +void pop_text_unmap(void); +uint8_t pop_text_width_mapped(pop_text_font_t font, const char *s); +void pop_text_draw_mapped(pop_text_font_t font, int x, int baseline, + const char *s); +void pop_text_center_mapped(pop_text_font_t font, int center, int baseline, + const char *s); +void pop_text_right_mapped(pop_text_font_t font, int right, int baseline, + const char *s); + +/* Снимок обеих (идентичных по контракту) палитр хранится в свободном хвосте + * FONT.ATL. dim_step: 0 — исходная, POP_PAL_DIM_BLACK — чёрная; между ними + * равные ступени яркости (step/32). Тридцать две, а не четыре: fade + * оригинала длится 2,13 с, и на четырёх ступенях это четыре заметных + * скачка вместо затухания. Половина от оригинальных 64 — на глаз разницы + * с ними нет, а вот шестнадцать уже видно. + * keep_ui сохраняет диапазон 0x37..0x3F ярким для текста/рамки меню. */ +void pop_ui_palette_snapshot(void) __banked; +#define POP_PAL_DIM_BLACK 32 + +/* СКОЛЬКО КАДРОВ ЖДАТЬ, чтобы fade длился столько же, сколько у оригинала + * (fade_in_1/fade_out_1 — 64 шага по 2 тика = 128 тиков = 2,13 с, то есть + * 106 кадров луча). + * + * Ждать все 106 нельзя: пересчёт палитры не бесплатен — 487 тысяч тактов + * на ступень, то есть чуть больше кадра (замер в MAME 2026-08-26; из них + * 136 тысяч уходит на заливку через BIOS). Тридцать три ступени съедают + * около сорока кадров, и без вычитания fade растянулся бы в полтора раза + * длиннее заказанного. Ступень при этом меняется каждые 66 мс — на глаз + * это уже слитное затухание, а не лесенка. */ +#define POP_FADE_STEP_FRAMES 1 /* цена одного пересчёта, кадров луча */ +#define POP_FADE_FRAMES (106 - (POP_PAL_DIM_BLACK + 1) * POP_FADE_STEP_FRAMES) + +void pop_ui_palette_dim(uint8_t dim_step, uint8_t keep_ui) __banked; +void pop_ui_palette_restore(void) __banked; + +/* Немедленно погасить все 256 цветов обеих страниц. В отличие от dim(), + * не требует загруженного FONT.ATL: это ранний экран запуска, когда в VRAM + * ещё может лежать картинка предыдущей программы. */ +void pop_ui_palette_black(void) __banked; + +/* Пошаговый fade к снимку: fade_out сначала делает снимок, fade_in требует + * его сохранить. Каждый шаг показывается на следующем фронте. */ +void pop_ui_fade_out(uint8_t steps) __banked; +void pop_ui_fade_in(uint8_t steps) __banked; +/* То же, но прерывается НОВЫМ нажатием; 1 — прервано. Палитру всё равно + * доводит до конца, экран не остаётся на промежуточной ступени. */ +uint8_t pop_ui_fade_out_skip(uint8_t steps) __banked; +uint8_t pop_ui_fade_in_skip(uint8_t steps) __banked; + +/* Полная экранная страница: begin выбирает скрытую страницу, fill рисует + * непрозрачный фон и present делает ГОТОВУЮ страницу видимой на фронте. + * Текст на второй странице надо нарисовать заново — GFX_BANK_SPRITE не + * меняет shadow-копию, и это сознательный контракт временного UI. */ +uint8_t pop_screen_begin(void) __banked; +void pop_screen_fill(uint8_t color) __banked; +/* Индекс палитры для всего, что ВНЕ игрового поля 320x192 (борта, полоса + * HP). В палитре он чёрный, но НЕ нулевой — поэтому вспышки, которые + * подменяют цвет 0, не задевают рамку и жизни. */ +#define POP_COL_OUTSIDE 1 +/* Подготовить экран под игру: вне поля POP_COL_OUTSIDE, внутри — цвет 0. + * Звать на СТАРТЕ и смене уровня; заодно сбрасывает служебные полосы. */ +void pop_screen_fill_game(void) __banked; +/* Только игровое поле (борта и всё, что на них, не трогает) — это путь + * входа в комнату. */ +void pop_screen_fill_field(void) __banked; +void pop_screen_present(uint8_t page) __banked; +/* Показать подготовленную скрытую страницу полосами слева направо + * (transition_ltr оригинала). Любая зажатая клавиша достраивает переход + * мгновенно. Страницы местами не меняются: содержимое копируется в + * видимую, поэтому после возврата обе страницы одинаковы. */ +void pop_screen_present_ltr(void) __banked; + +/* Нарисовать в уже выбранную draw-страницу набор вертикальных полос SPA1. + * Полоса загружается, блитится и тут же освобождается; функция не вызывает + * BIOS/vsync, поэтому её можно безопасно применять в title и cutscene. + * Возвращает -1 при ошибке чтения очередной полосы. */ +int pop_screen_blit_atlas_strips(const char * const *parts, + uint8_t count, int x, int y) __banked; + +/* Те же полосы, но из архива ресурсов: элементы base..base+count-1. + * Один `open` на экран вместо одного на полосу. */ +int pop_screen_blit_atlas_arc(struct pop_arc_s *arc, const uint8_t *tbl, + uint8_t base, uint8_t count, + int x, int y) __banked; + +#endif /* POP_UI_H */ diff --git a/applications/SprPoP/src/pop_vflip.c b/applications/SprPoP/src/pop_vflip.c new file mode 100644 index 0000000..4b8e138 --- /dev/null +++ b/applications/SprPoP/src/pop_vflip.c @@ -0,0 +1,67 @@ +/* + * pop_vflip.c — таблица «страница атласа -> её ВЕРТИКАЛЬНО ЗЕРКАЛЬНАЯ пара» + * (зелье инверсии, уровень 9). См. pop_vflip.h. + * + * Сами зеркальные ленты делает упаковщик (`vflip_cols` в pop_pack_kid.py) и + * кладёт рядом с обычными: kid0_v.atl, sword_v.atl, g0_v.atl… Здесь остаётся + * только связать пары: зеркальный файл грузится обычным atlas_load, а его + * раскладка байт-в-байт совпадает с оригиналом — значит движку достаточно + * подменить НОМЕР СТРАНИЦЫ, которую он мапит в W0. + * + * Почему не считаем в рантайме (замеры MAME 2026-08-12, такты Z80): + * загрузка одной 16-КБ страницы с HDD — 970 003 такта (0.045 с); + * разворот той же страницы на месте — 12 645 582 такта (0.59 с). + * Даже идеальная конвертация (accel-копия + asm-реверс) даёт ~1.05 млн — + * то есть в лучшем случае сравнивается с чтением готового файла, а стоит + * asm-модуля. На все 34 страницы: 1.5 с загрузки против 20 с разворота. + * + * РЕЗИДЕНТ: pop_vflip_page зовёт отрисовка персонажей (банк 4) на каждый + * спрайт — трамплин тут ни к чему, а кода всего ничего. + */ +#include +#include +#include "pop_vflip.h" + +/* Персонажных страниц: 28 (Кид) + 5 (страж) + 1 (меч) = 34; с запасом. */ +#define VF_MAX 40 +static uint8_t vf_src[VF_MAX]; /* страница оригинала */ +static atlas_t vf_atl[VF_MAX]; /* зеркальный атлас (его page) */ +static uint8_t vf_n; + +/* Взять УЖЕ ЗАГРУЖЕННЫЙ зеркальный атлас под своё управление (дальше его + * освобождает pop_vflip_reset). Нужен тем, кто читает зеркала не файлом, а + * куском архива ресурсов: загрузку делает вызывающий, связывание — мы. */ +int pop_vflip_take(const atlas_t *orig, const atlas_t *mirror) +{ + if (!orig || !mirror || vf_n >= VF_MAX) return -1; + vf_atl[vf_n] = *mirror; + vf_src[vf_n] = orig->page; + vf_n++; + return 0; +} + +int pop_vflip_add(const atlas_t *orig, const char *path) +{ + atlas_t m; + + if (!orig || vf_n >= VF_MAX) return -1; + if (atlas_load(&m, path) != 0) return -1; + return pop_vflip_take(orig, &m); +} + +uint8_t pop_vflip_page(uint8_t src) +{ + uint8_t i; + for (i = 0; i < vf_n; i++) + if (vf_src[i] == src) return vf_atl[i].page; + return 0; /* пары нет — рисуем обычным атласом */ +} + +uint8_t pop_vflip_ready(void) { return vf_n != 0; } + +void pop_vflip_reset(void) +{ + uint8_t i; + for (i = 0; i < vf_n; i++) atlas_free(&vf_atl[i]); + vf_n = 0; +} diff --git a/applications/SprPoP/src/pop_vflip.h b/applications/SprPoP/src/pop_vflip.h new file mode 100644 index 0000000..81fefec --- /dev/null +++ b/applications/SprPoP/src/pop_vflip.h @@ -0,0 +1,52 @@ +/* + * pop_vflip.h — ВЕРТИКАЛЬНО ЗЕРКАЛЬНЫЕ наборы спрайтов персонажей + * (зелье инверсии, уровень 9). Реализация и замеры — в pop_vflip.c. + * + * Кому нужно. Фон и все row-major спрайты переворачиваются даром (порядок + * строк = знак страйда, gfx_blit_*_vflip), а персонажи лежат КОЛОНКАМИ — + * у них даром получается горизонтальное зеркало, а вертикальное требует + * развернуть байты внутри колонки. Такой набор готовит упаковщик + * (kid0_v.atl, sword_v.atl, g0_v.atl…), а здесь он только грузится и + * связывается с оригиналом. Правило вызывающего: + * + * страница = pop_upside ? pop_vflip_page(a->page) : a->page; + * if (!страница) страница = a->page; // набор не загружен + * + * Ни смещения лент, ни размеры кадров, ни горизонтальный флип при этом не + * меняются: зеркальный файл имеет ТУ ЖЕ раскладку, отличаются только пиксели. + * + * КОГДА ГРУЗИТЬ — на усмотрение приложения: pop_vflip_load_all можно звать + * где угодно (старт игры, показ интро, вход на уровень с зельем). До + * загрузки движок просто рисует неперевёрнутыми кадрами — это заметно, но + * не ломает ни кадр, ни физику. + */ +#ifndef POP_VFLIP_H +#define POP_VFLIP_H + +#include +#include + +/* Загрузить зеркальный файл path и связать его с атласом orig. + * 0 — OK, -1 — файла нет / не хватило EMM / таблица полна. */ +int pop_vflip_add(const atlas_t *orig, const char *path); + +/* То же, но зеркало УЖЕ загружено (например, прочитано из архива): таблица + * забирает его себе, и дальше он освобождается вместе с остальными. */ +int pop_vflip_take(const atlas_t *orig, const atlas_t *mirror); + +/* Страница-зеркало для страницы src; 0 — пары нет (набор не загружен). */ +uint8_t pop_vflip_page(uint8_t src); + +/* Загружен ли хоть один зеркальный атлас (чтобы не грузить дважды). */ +uint8_t pop_vflip_ready(void); + +/* Освободить весь набор (смена набора спрайтов соперника / уровня). */ +void pop_vflip_reset(void); + +/* Загрузить ВЕСЬ зеркальный набор: страницы Кида, меч и текущий набор + * соперника. Идемпотентна — повторный вызов ничего не делает. 0 — OK, + * -1 — что-то не загрузилось (движок останется на обычных кадрах). + * Живёт в банке 8 (pop_kboot.c): зовётся раз за игру/уровень. */ +int pop_vflip_load_all(void) __banked; + +#endif /* POP_VFLIP_H */ diff --git a/applications/SprPoP/src/sprpop.c b/applications/SprPoP/src/sprpop.c new file mode 100644 index 0000000..f39672d --- /dev/null +++ b/applications/SprPoP/src/sprpop.c @@ -0,0 +1,967 @@ +/* + * sprpop.c — проверка порта статического фона (Шаг 3): рисует комнату 1 + * уровня 1 реальной композицией слоёв (pop_bg.c) и держит на экране. + * Сверять на MAME с эталоном applications/PoP/toolchain/1.1-2.png. + */ +#include +#include +#include +#include +#include +#include +#include /* gfx_copy_page — вход в комнату и переворот */ +#include "pop_bg.h" +#include "pop_kid.h" +#include "pop_ctrl.h" +#include "pop_map.h" +#include "pop_level.h" +#include "pop_trob.h" +#include "pop_redraw.h" +#include "pop_geom.h" /* pop_y_to_row, TILE_SIZEX — тайл объекта */ +#include "pop_sfx.h" +#include "pop_music.h" /* pop_sfx_tick — решение о запуске звука за кадр */ +#include "pop_guard.h" +#include "pop_cdraw.h" /* общая отрисовка персонажей (слоты Кид/соперник) */ +#include "pop_cheat.h" +#include "pop_tune.h" +#include "pop_vflip.h" /* зеркальный набор персонажей (зелье инверсии) */ +#include "pop_pace.h" /* фиксированный логический кадр по лучу */ +#include "sprpop_cold.h" /* холодная половина цикла — в банке */ +#include "pop_qsave.h" +#include "pop_status.h" +#include "pop_timer.h" +#include "pop_menu.h" /* pause menu — банк 9, команды наружу */ +#include "pop_settings.h" /* единая модель скорости/звука/читов */ +#include "pop_app.h" /* автомат приложения: игровой цикл = PLAYING */ +#include "pop_timer.h" /* global 60-minute timer — bank 9 */ +#include "pop_title.h" /* исходная графическая title sequence — bank 9 */ +#include "pop_intro.h" /* story + Princess/Jaffar после title — bank 11 */ +#include "pop_flow.h" /* таблица pre-level cutscene — bank 9 */ +#include "pop_demo.h" /* attract mode level 0 — bank 9 */ +#include "pop_pal.h" /* lifecycle палитры title/intro/level */ +#include "pop_hof.h" /* persistent Hall of Fame — bank 9 */ +#include "pop_ui.h" /* чёрные обе страницы перед сменой палитры */ + +__sfr __at (0xFE) io_border; + +/* ПРОФИЛИРОВАНИЕ ПОЛОСАМИ БОРДЮРА (включено по умолчанию; make PROF=0 — + * выключить). Цвет бордюра держится, пока фаза считает, поэтому высота + * полосы на скриншоте = время фазы в растровых строках. Это единственный + * способ смотреть профиль без дебаггера: выборка PC вырождается (мост MAME + * отвечает из фреймового колбэка, все сэмплы падают в обработчик + * прерывания); точные такты — breakpoint'ы с {printf totalcycles; g}. + * + * ЦВЕТА: у бордюра Sprinter бит 0 (красный) ИГНОРИРУЕТСЯ, работают только + * бит 1 (синий) и бит 2 (зелёный) — значит различимых состояний ровно + * ЧЕТЫРЕ, и значения обязаны быть чётными. Отсюда фазы сгруппированы по + * четыре, а не по семь (нечётные номера раньше сливались с соседями и + * полосы не читались): + * 0 чёрный — ожидание vsync (запас кадра) + * 2 синий — ввод, heal и ЛОГИКА (kid_tick/физика/страж) + * 4 зелёный — слой ФОНА (loose/trobs/redraw_needed/шов) + * 6 циан — СПРАЙТЫ (страж, Kid, fore поверх них) */ +#ifndef PROF_BORDER +#define PROF_BORDER 1 +#endif +/* ВРЕМЕННО: покадровая трасса kidobj для сверки с SDLPoP (см. + * pop_dbg_kidobj в sprpop_cold.c). 0 — выключить. + * СВОЙ #ifndef, а не общий с PROF_BORDER: тот задаётся ключом сборки + * (-DPROF_BORDER=1), и всё, что лежало внутри его блока, не определялось + * вовсе — трасса молча не включалась. */ +#ifndef DBG_KIDOBJ +#define DBG_KIDOBJ 0 +#endif +#if PROF_BORDER +#define PROF(n) io_border = (n) +#else +#define PROF(n) ((void)0) +#endif + +/* Раскладка банков; их число crt0_banked получает автоматически из + * --bank флагов: 1 = guards (ИИ/боёвка), 2 = pop_bg (ГОРЯЧАЯ половина фона: fore-проход, + * оверлеи, кладка), 3 = pop_map (коллизия/физика), 4 = pop_cdraw + * (отрисовка персонажей + полоса HP), 5 = pop_ctrl (диспетчер управления), + * 6 = pop_trob (ловушки/анимация тайлов), 7 = pop_room (ХОЛОДНАЯ половина + * фона: полная отрисовка комнаты, точечные перерисовки, падающие плиты), + * 8 = sprpop_cold (ХОЛОДНАЯ половина главного цикла: старт уровня и + * отладочная навигация — см. sprpop_cold.h), 9 = pop_menu (ВСЯ работа + * с меню: автомат, рендерер, Settings и build screen; наружу только + * команды gameplay). + * Отрисовка персонажей и холодная половина фона вынесены из банка 2 + * отдельно: тот дважды подходил к 16 КБ вплотную, а расти ему ещё есть + * куда — тайлы поздних уровней, чомперы, зеркало. */ + +/* Режим читов (pop_cheat.h): на время разработки включаем в main. */ +uint8_t pop_cheats; +/* Единственная резидентная часть UI: банк 9 вызывается только по запросу, + * поэтому обычный кадр не платит трамплин. */ +uint8_t pop_menu_request; + +/* ПОТЕРЯ НАЖАТИЙ ПРИ ЗАЖАТОМ Shift — лечится ПЛОТНЫМ опросом клавиатуры. + * + * При зажатом Shift PS/2 обрамляет расширенный код «фиктивным шифтом», и + * одно нажатие стрелки превращается в 5 байт вместо 2 (отпускание — тоже + * 5). Приёмный FIFO SIO — 3 байта, а импульс запроса прерывания здесь + * теряется примерно в 44 % случаев, поэтому байты копятся и один из + * полусотни пропадает ДО чтения порта: нажатие «не срабатывает». + * + * Что НЕ помогает (замерено, TASKS_CLOSED.md/KBD-1): редкий опрос (несколько + * вызовов за кадр — они попадают туда, где прерывания и так разрешены, и + * лишь дублируют трамплин) и укорачивание DI-окон графики (в кадре вообще + * без блитов потерь БОЛЬШЕ). Снимать `di` в accel-ядрах нельзя — машина + * уходит в перезагрузку. + * + * Что помогает: опрос раз в ~0.5 мс. Столько времени берётся из ожидания + * кадра — процессор всё равно крутит там ~42 мс из 60, ничего не делая. + * Поэтому kbd_raw_poll вешается idle-хуком на gfx_wait_vsync (см. + * kbd_idle ниже); полезной работы это не отнимает. Потолок приёма + * подтверждён отдельно (tests/kbdpoll: 25 нажатий → 25, 250 байт из 250). */ + +/* Обёртка ради типа: хук — void(*)(void), а kbd_raw_poll возвращает + * «было ли что вычерпано». Приводить указатель кастом не хочется. */ +static void kbd_idle(void) +{ + kbd_raw_poll(); +} + +/* seq_2_stand (types.h seqids) — Kid стоит, ждёт ввода. */ +#define SEQ_STAND 2 +#define ROOM_1 1 +#define LINK_DOWN 3 /* сторона links: 0=L,1=R,2=U,3=D */ + + +/* ОТЛАДОЧНЫЙ ОБХОД КОМНАТ: '+'/'-' (цифровой блок либо '='/'-' основного + * ряда) — следующая/предыдущая комната уровня ПО НОМЕРУ (1..24, с обёрткой), + * Kid ставится на первый попавшийся пол. Нужен, чтобы пройти все комнаты и + * собрать список багов отрисовки, включая недостижимые обычным путём. + * Убирается закомментированием ОДНОЙ строки ниже. */ +#define ROOMNAV 1 + + +/* Рабочая копия ТЕКУЩЕЙ комнаты (W2, мутабельная: loose→empty) + срезы + * соседей для кромок. Заполняются pop_room_load из уровня. File-scope, + * чтобы enter_room переключал комнату без длинного списка параметров. */ +uint8_t room_fg[30]; +uint8_t room_bg[30]; +/* lcol_fg/rcol_fg — ШЕСТЬ записей: [0..2] кромочная колонка соседа (col9/col0), + * [3..5] следующая за ней (col8/col1) для коллизии, когда Kid стоит В шве и + * смотрит наружу (колонка −2 / 11). См. pop_level.h. */ +uint8_t lcol_fg[6], lcol_bg[3], rcol_fg[6], rcol_bg[3], below_fg[11]; +uint8_t above_fg[10], above_mod[10]; /* ряд 2 верхнего соседа (полоса кладки у потолка) */ +uint8_t cur_room; /* номер ОТРИСОВАННОЙ комнаты (drawn_room) */ +uint8_t kid_room; /* РЕАЛЬНАЯ комната Kid (S1: == cur_room; расходятся в S3 straddle) */ + +/* Рендер-смещение Kid по логической X (порт xpos_in_drawn_room): комната Kid + * слева от отрисованной → −140 (спрайт за левой кромкой), справа → +140, та + * же → 0. Звать каждый кадр перед отрисовкой персонажей. */ +static void update_kid_render_dx(void) +{ + int dx = 0; + if (kid_room != cur_room) { + if (kid_room == pop_room_link(cur_room, 0)) dx = -140; /* left neighbour */ + else if (kid_room == pop_room_link(cur_room, 1)) dx = 140; /* right neighbour */ + } + pop_char_set_render_dx(POP_CH_KID, dx); +} + +/* Change-driven редрой ЛЕВОГО шва (ворота соседа room8 видны в col0): тяжёлый + * draw_tile(0,0) в pop_room_redraw_seam_left зовём ТОЛЬКО когда openness гейта + * реально изменился. Пока Кид держит кнопку и гейт открыт — modifier стабилен + * → 0 редроёв (раньше шов рисовался безусловно КАЖДЫЙ кадр, пока сосед активен + * — тот же класс, что был у кнопки). seam_redraw=2 = обе страницы дабл-буфера. + * + * Сигнатура — ВСЕ ТРИ ряда колонки 9 соседа, а не только ряд 0: ворота в шве + * бывают в любом ряду. На уровне 1 решётка соседа стояла в (0,9), и одного + * байта хватало; на уровне 4 плита комнаты 1 открывает ворота (1,9) комнаты 8 + * — изменение приходило в m[19], сигнатура его не видела, редрой не + * заказывался, и открытые ворота оставались нарисованными закрытыми + * (BUG-GATE-SEAM-ROW1). */ +/* Кнопка в шве (SEAM-BUTTON-STALE). Сигнатурой служил ТОЛЬКО modif, а у + * кнопки modif — это индекс LINKLOC, то есть константа уровня: нажатие живёт + * в doorlinks2 (get_doorlink_timer, seg007:0BB6). Изменение в сигнатуру не + * приходило, редрой не заказывался — и шов оставался нарисованным в том + * состоянии, в каком был на входе в комнату: кнопка (1,9) комнаты 11, она же + * (1,−1) комнаты 24, залипала нажатой (или отжатой), хотя связь срабатывала. + * Видимой её делает draw_tile: нажатый opener рисуется как ПОЛ (seg008 + * get_tile_to_draw), а у пола есть правая грань, у кнопки — нет. + * Поэтому в сигнатуру ряда подмешиваем бит «кнопка нажата». */ +uint8_t seam_row_sig(const uint8_t *m, const uint8_t *lfg, uint8_t r) +{ + uint8_t code = (uint8_t)(lfg[r] & 0x1F); + uint8_t mod = m[9 + r * 10]; + if (code == 0x0F || code == 0x06) /* opener / closer */ + return (uint8_t)((pop_doorlink2(mod) & 0x1F) > 1); + return mod; +} + +uint8_t seam_sig[3]; /* последняя нарисованная openness гейта в шве */ +uint8_t seam_rows; /* маска рядов, ждущих редроя (переживает 2 кадра) */ +uint8_t seam_redraw; /* оставшиеся кадры редроя (обе страницы) */ + +/* checkpoint (seg000:270): взводится, когда Кид уходит ВЛЕВО в комнату 7 + * уровня 3 (level3_set_chkp, seg002:0665); по нему do_startpos подменяет + * стартовую позицию. Сбрасывается началом игры и прохождением уровня — + * ровно как в оригинале (seg003:37 и seg003:413). */ +uint8_t pop_checkpoint; + +/* Изменения тайлов (упавшая плита → пусто, щебень на месте приземления + * куска, поднятый предмет → пол) пишутся ПРЯМО В УРОВЕНЬ — + * pop_level_set_tile, как в оригинале (curr_room_tiles[…] = …). Раньше тут + * жила таблица переопределений на 8 записей, и девятая правка молча + * терялась: разбитые плиты и выпитые зелья «возвращались» при возврате в + * комнату (BUG-LVLSTATE-1). */ +#define TILE_DEBRIS 0x0E +#define TILE_CLOSER 0x06 +#define TILE_OPENER 0x0F +#define TILE_TORCH 0x13 +#define TILE_TORCH_DEBRIS 0x1E +#define TILE_EMPTY 0x00 +#define TILE_FLOOR 0x01 + +/* Поднятый предмет (меч): tilepos и счётчик «запечь пол» по страницам. + * flash_on — вспышка фона активна (нулевая запись палитры подменена). */ +static uint8_t item_pos, item_bake, flash_on; + +/* Подмена НУЛЕВОЙ записи палитры на обеих страницах (do_flash/set_bg_attr). + * Через функцию с параметрами, а не пятью литералами в месте вызова: на + * gfx_pal_set(0,0,0,0,0) SDCC 4.5 выдаёт невалидный `ld hl, a` + * (см. build/obj/SprPoP.asm) и ассемблер падает. */ +static void flash_bg(uint8_t r, uint8_t g, uint8_t b) +{ + gfx_pal_set(0, 0, r, g, b); + gfx_pal_set(1, 0, r, g, b); +} + +int main(void) +{ + /* Дабл-буфер: front — видимая страница, back — куда рисуем. Отладочный + * тумблер «однобуфер по SPACE» убран (решение пользователя 2026-08-25): + * дабл-буфер теперь безусловный, а SPACE свободен под игру. */ + uint8_t front = 0, back, frozen = 0; + uint8_t pace_prev = 0; /* edge клавиши смены режима скорости */ + uint8_t skip = 0; /* маска «слот перерисовывать не надо» */ + uint16_t dead_frames = 0; +#ifdef ROOMNAV + uint8_t nav_prev = 0; +#endif + uint8_t cheat_prev = 0, immo_prev = 0, sword_prev = 0, nlvl_prev = 0; + uint8_t xdec_prev = 0, xinc_prev = 0, upsd_prev = 0; + uint8_t failed = 0; + + /* Загрузка ресурсов, палитра, первый экран — В БАНКЕ (sprpop_cold.c): + * отрабатывает ровно один раз за игру, а кода там на полкилобайта. */ + pop_app_reset(); + if (pop_boot() != 0) return 1; + + if (pop_ctrl_init() != 0) { /* raw-канал клавиатуры (забирает ввод у DSS) */ + closegraph(); + puts("kbd_raw open failed"); + return 1; + } + gfx_set_idle_hook(kbd_idle); /* вычерпывать FIFO, пока ждём кадр */ + if (!pop_app_dispatch(POP_APP_EV_BOOT_READY)) { + pop_shutdown(); + return 1; + } + /* MS8: CFG уже применён в pop_boot до первого показа. Экран build + * показывается только по явной пользовательской настройке. */ + pop_build_info_show(); + if (!pop_app_dispatch(POP_APP_EV_BUILD_DONE)) { + pop_shutdown(); + return 1; + } + /* Темп кадра по лучу. Если бит 5 порта 0xFE не шевелится (иное + * железо), pop_pace_* молча откатываются на прежние gfx_wait_vsync. */ + pop_pace_arm(); + + { + uint8_t qs_save_prev = 0, qs_load_prev = 0; + uint8_t quit = 0, esc_prev = 0, menu_restart = 0; + uint8_t demo_new_game, demo_finished; + uint8_t reveal_pending = 0; /* уровень готовится за чёрной палитрой */ + for (;;) { + uint8_t is_demo; + + /* Title -> intro -> demo — три самостоятельных состояния. Сцены не + * вызывают gameplay рекурсивно: после каждого return следующий маршрут + * выбирается только pop_app_dispatch(). */ + if (pop_app_state == POP_APP_TITLE) { + pop_app_event_t ev = pop_title_show() ? POP_APP_EV_NEW_GAME : + POP_APP_EV_INTRO_STARTED; + if (!pop_app_dispatch(ev)) { + failed = 1; + break; + } + } + if (pop_app_state == POP_APP_INTRO) { + /* Таблица рекордов между credits и demo показывается ВНУТРИ + * pop_intro_show — там же, где её показывает show_title(). */ + uint8_t skipped = pop_intro_show(); + pop_app_event_t ev = skipped ? POP_APP_EV_NEW_GAME + : POP_APP_EV_CUTSCENE_FINISHED; + if (!pop_app_dispatch(ev)) { + failed = 1; + break; + } + } + if (pop_app_state == POP_APP_ENDING) { + pop_ending_show(); + if (!pop_app_dispatch(POP_APP_EV_ENDING_FINISHED)) { + failed = 1; + break; + } + continue; + } + if (pop_app_state == POP_APP_HALL_OF_FAME) { + pop_hof_show(); + if (!pop_app_dispatch(POP_APP_EV_HOF_FINISHED)) { + failed = 1; + break; + } + continue; + } + + /* Enter/Esc на title/story/PV/credits и клавиша в attract-demo сходятся + * в ОДИН маршрут LEVEL_LOAD. Начальные уровень/комната берутся из + * FIRST_LEVEL и данных уровня — то есть из параметров текущей сборки. */ + if (pop_app_state == POP_APP_LEVEL_LOAD) { + if (pop_new_game_load() != 0 || + !pop_app_dispatch(POP_APP_EV_LEVEL_READY)) { + failed = 1; + break; + } + } + + /* После intro всегда запускается настоящий level 0. pop_level_switch() + * даёт тот же lifecycle, что и обычной границе уровней: новый tileset, + * актёры, палитра, обе страницы room background. Это важно, поскольку + * девелоперская сборка может до title держать дворец LEVEL=11. */ + if (pop_app_state == POP_APP_DEMO) { + pop_next_level = 0; + pop_seamless = 0; + pop_checkpoint = 0; + if (pop_level_switch() != 0) { + failed = 1; + break; + } + /* custom->demo_hitp = 4. level_switch переносит HP предыдущего + * уровня по обычному правилу, поэтому demo-вариант задаём после + * него и просим полосу HP перепечатать новое число. */ + hitp_beg_lev = 4; + pop_kid_hp_reset(); + pop_hp_invalidate(); + pop_demo_begin(); + /* show_title() SDLPoP начинает attract-demo сразу, без fade-in. + * Credits уже ушли в чёрный, intro_restore_game_palette оставил + * обе страницы чёрным растром, а level_switch успел собрать на них + * level 0. Поэтому яркую game palette можно включить одним commit: + * старый credits-кадр физически уже негде высветить. */ + pop_pal_apply(0); + (void)pop_sfx_start(); + } + is_demo = (uint8_t)(pop_app_state == POP_APP_DEMO); + if (!is_demo && pop_app_state != POP_APP_PLAYING) break; + demo_new_game = 0; + demo_finished = 0; + + while (!quit && (pop_app_state == POP_APP_PLAYING || + pop_app_state == POP_APP_DEMO)) { + PROF(2); /* ввод + heal */ + + /* Demo читает raw-карту первым: любая клавиша начинает новую игру, + * не попадая в Pause/QuickSave/чит. pop_ctrl_auto_move() ниже raw + * вообще не трогает, поэтому карта не будет сброшена между проверкой + * и решением оболочки. */ + if (is_demo) { + kbd_raw_sync(); + if (pop_demo_user_input()) { + demo_new_game = 1; + break; + } + } + /* P — переключить режим скорости по кругу (FASTEST -> FAST -> + * NORMAL). Edge, чтоб одно нажатие = один шаг. Дефолт FASTEST + * (crt0 зануляет _DATA). Что означают режимы — pop_pace.h. */ + if (!is_demo) { + uint8_t pk = kbd_raw_down(KBD_SPEED_MODE); + if (pk && !pace_prev) { + uint8_t m = (uint8_t)(pop_speed_mode + 1); + pop_speed_mode = (uint8_t)(m >= POP_SPEED_MODES ? 0 : m); + pop_settings.speed_mode = pop_speed_mode; + } + pace_prev = pk; + } + + /* Отладочный стоп-кадр: '1' (PS/2 0x16) — заморозить, '2' (0x1E) — + * продолжить. В заморозке пропускаем весь tick/draw/flip: картинка + * стоит, можно спокойно разбирать позу Kid и окклюзию. */ + if (!is_demo) { + if (kbd_raw_down(0x16)) frozen = 1; + if (kbd_raw_down(0x1E)) frozen = 0; + } + /* Читы — В БАНКЕ (sprpop_cold.c): они дёргаются раз в кадр, а места + * в резиденте W1/W2 нет (см. TASKS_OPEN, MEM). */ + if (!is_demo) pop_cheat_tick(); + /* Esc — открыть меню (edge). Меню МОДАЛЬНО: пока пользователь не + * выбрал пункт, pop_menu_process не возвращается и игра стоит. + * Команды исполняет ГЛАВНЫЙ ЦИКЛ — UI не зовёт gameplay internals: + * QuickSave/QuickLoad идут тем же механизмом, что F6/F9 (запрос + * подхватит блок ниже в этой же границе кадра), рестарты исполняются + * после вычисления back-страницы (как респавн), QUIT рвёт цикл. */ + /* Меню, F10 и QuickSave/Load — целиком в банке 8 (pop_frame_ui). + * Всё это события «раз в партию», а кода на них было полсотни строк + * в резиденте; наружу остаются флаги, которые нужны самому циклу. */ + if (!is_demo) { + uint8_t want_restart = 0, dead_reset = 0; + int8_t urc = pop_frame_ui(&want_restart, &dead_reset); + + if (want_restart) menu_restart = 1; + if (dead_reset) dead_frames = 0; + if (urc) { + if (urc < 0) failed = 1; + if (urc != 1) quit = 1; /* 1 — restart game, без quit */ + break; + } + } + if (frozen) { + PROF(0); /* чёрный: ждём vsync */ + /* Заморозка — тоже полноценный логический кадр: иначе якорь + * пейсинга уедет и первый кадр после разморозки будет рваным. */ + pop_wait_edge(); + pop_pace_end(pop_pace_n((uint8_t)(Kid.sword == SWORD_2_DRAWN))); + continue; + } + +#ifdef ROOMNAV + /* Обход комнат по номеру: '+' (0x79 numpad / 0x55 '=') — вперёд, + * '-' (0x7B / 0x4E) — назад. Edge по nav_prev: одно нажатие = одна + * комната. Сама навигация — в банке (sprpop_cold.c): она нужна + * только по клавише, а место в резиденте дефицитно. */ + if (!is_demo) { + uint8_t nav = (uint8_t)((kbd_raw_down(0x79) || kbd_raw_down(0x55)) | + ((kbd_raw_down(0x7B) || kbd_raw_down(0x4E)) << 1)); + if (nav && !nav_prev && pop_dbg_roomnav((uint8_t)(nav & 1 ? 1 : 2))) + dead_frames = 0; + nav_prev = nav; + } +#endif + + /* Рисуем в СКРЫТУЮ страницу дабл-буфера. + * Видимую перечитываем У ЖЕЛЕЗА, а не ведём свой счёт: её меняют и + * вход в комнату (ENTER-ROOM-FAST), и переворот экрана + * (pop_flip_screen), причём оба — из банка, где front недоступен. */ + front = gfx_get_visible_page(); + back = (uint8_t)(front ^ 1); + gfx_set_draw_page(back); + + /* Рестарты из меню — ЗДЕСЬ, с известной back-страницей (тот же + * контракт, что у респавна ниже). */ + if (menu_restart == 1) { /* Restart Level — штатный reset уровня */ + pop_start_level(); + dead_frames = 0; + menu_restart = 0; + } + + /* Переворот только что переключился (зелье инверсии, ур. 9) — + * привести уже нарисованное в новый вид ОДИН раз, до кадра + * (порт need_redraw_because_flipped, seg000:939). Дальше слои + * рисуют зеркально сами. */ + if (pop_upside_dirty) { + pop_upside_dirty = 0; + /* Само переключение — ЗДЕСЬ и только здесь: весь кадр обязан + * рисоваться в одной системе координат (см. pop_upside_want). */ + pop_upside = pop_upside_want; + /* Страховка для ЧИТА U на «неподготовленном» уровне и для + * случая, когда зелье выпито раньше предзагрузки: набор + * поднимается здесь же. На уровне 9 он уже загружен, и вызов + * не стоит ничего (pop_vflip_ready). */ + if (pop_upside && !pop_vflip_ready()) pop_vflip_load_all(); + pop_flip_screen(); + back = (uint8_t)(gfx_get_visible_page() ^ 1); + gfx_set_draw_page(back); + } + pop_dbg_m13(); /* ЗАМЕР: ввод и читы разобраны */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_check_skel(); /* спецсобытие ур.3: скелет встаёт */ + pop_check_mouse(); /* спецсобытие ур.8: мышь жмёт кнопку */ + pop_check_killed_shadow(); /* спецсобытие ур.12: смерть тени = смерть Кида */ + pop_frame_timers(); /* timers (seg003:0735): вспышка слияния */ + pop_dbg_m9(); /* ЗАМЕР: pop_frame_timers сделан */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_check_can_guard_see_kid(); /* луч видимости — ДО логики персонажей */ + pop_dbg_m14(); /* ЗАМЕР: спецсобытия + луч видимости */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + if (is_demo) demo_finished = pop_demo_tick(); + else pop_ctrl_tick(); /* ввод -> control(): смена seq */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* Кто в этом кадре не изменился с прошлой отрисовки ЭТОЙ страницы — + * тот на ней уже нарисован правильно: ни стирать, ни рисовать заново + * не надо (DRAW-COST, контракт в pop_cdraw.h). Решение + * перепроверяется перед самой отрисовкой — тик ещё впереди. */ + skip = pop_char_skip_mask(); + if (!(skip & 1)) pop_char_heal(POP_CH_KID); /* стереть прошлый кадр ЭТОЙ страницы */ + if (!(skip & 2)) pop_char_heal(POP_CH_OPP); /* и прошлый кадр соперника */ + /* Отражение в зеркале — свой heal: у него нет слота pop_cd и в + * skip-маске он не участвует (рисуется/стирается безусловно, живёт + * только пока Кид стоит на тайле зеркала). */ + pop_mirror_heal(); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + PROF(2); /* логика (тот же синий) */ + pop_dbg_m1(); /* ЗАМЕР: начало логики */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + kid_tick(); /* play_seq: следующий кадр */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_phys_tick(); /* K3: падение/приземление/стена */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_guard_tick(); /* логика стража — В БАНКЕ (guards.c) */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_guard_phys_tick(); /* и его физика — та же, что у Кида (play_guard_frame) */ + /* Боёвка, порядок как в play_frame (seg000:872): сначала пометить + * попадания с обеих сторон, потом применить, потом свести HP. */ + pop_check_sword_hurting(); + pop_check_sword_hurt(); + /* check_sword_vs_sword (seg000:1353): кадр 167 — клинки уже + * столкнулись. Раньше сам parry работал, но эта отдельная строка + * оригинального play_frame была потеряна, поэтому звон sound 10 + * не номинировался ни в обычном бою, ни в attract-demo. */ + if (Kid.frame == FRAME_167_BLOCKED || Guard.frame == FRAME_167_BLOCKED) + pop_sfx_play(10); + pop_do_delta_hp(); + /* Порт show_time(): счётчик уменьшается только в живом игровом + * кадре. Пауза, frozen, HDD и QuickSave проходят через continue + * выше, поэтому не создают бесплатных/лишних тиков. На уровне + * Джафара время идёт до открытия двери, но истечение уже не может + * перевести игру в TIME_EXPIRED — буквально правило SDLPoP. */ + if (!is_demo && pop_timer_tick(pop_settings.time_limit_enabled, + (uint8_t)(Kid.alive < 0 && + (pop_current_level < 13 || + (pop_current_level == 13 && pop_leveldoor_open == 0)))) && + pop_current_level < 13) { + /* Время ИДЁТ и на уровне Джафара (до открытия двери), но + * КОНЧИТЬСЯ игра там уже не может: expired() у оригинала стоит + * под отдельным `current_level < 13` (seg000:910). На 13-м + * истечение оставляет только строку «TIME HAS EXPIRED!». */ + if (!pop_app_dispatch(POP_APP_EV_TIME_EXPIRED)) failed = 1; + break; + } + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_guard_fallout(); /* страж провалился ниже комнаты — убрать (seg002:241) */ + if (pop_fell_out) { /* Kid пересёк нижнюю границу комнаты */ + uint8_t down = pop_room_link(cur_room, LINK_DOWN); + pop_fell_out = 0; + /* Спецсобытие «выход падением» (leave_room, seg002:0504 — + * направление вниз даёт особый результат −2; разбор в главном + * цикле, seg000:0893). Уровень 6 не заканчивается дверью: Кид + * проваливается вниз из комнаты 1 и этим переходит на 7-й. + * Проверка ОБЯЗАНА идти раньше связи вниз: у комнаты 1 сосед + * снизу есть (шахта, комната 3), и без спецсобытия Кид улетал + * туда, а оттуда — в рестарт уровня. */ + if (pop_current_level == FALLING_EXIT_LEVEL && + cur_room == FALLING_EXIT_ROOM) { + Kid.y = 0xFF; /* seg000:0896 — Kid.y = −1 */ + pop_next_level = (uint8_t)(pop_current_level + 1); + } + else if (down) { /* L2: переход в комнату снизу — продолжает падать */ + pop_nav_hold = 0; /* сменил комнату НОГАМИ — чит забыт */ + enter_room(down); /* координаты Kid уже репроецированы pop_map */ + gfx_set_draw_page(back); /* вернуть текущую back для отрисовки */ + } else { /* выпал из уровня (нет комнаты снизу) — рестарт */ + pop_start_level(); + gfx_set_draw_page(back); + } + } else if (pop_leave_dir) { /* L3: уход вбок/вверх (координаты уже репроецированы) */ + uint8_t nb = pop_room_link(cur_room, (uint8_t)(pop_leave_dir - 1)); + uint8_t left = (uint8_t)(pop_leave_dir == 1); + uint8_t pop_leave_dir_prev = pop_leave_dir; + pop_leave_dir = 0; + /* level3_set_chkp (seg002:0665). ВНИМАНИЕ на комнату: оригинал + * зовёт его из leave_room ДО goto_other_room, то есть Char.room + * там ещё СТАРАЯ. Значит условие `Char.room == 7` читается как + * «Кид УХОДИТ ИЗ комнаты 7 влево» (в комнату 2), а не «входит в + * неё». Смысл: чекпойнт даётся за то, что комната 7 пройдена + * НАСКВОЗЬ, и респавн ставится туда, куда Кид только что попал. + * + * Сначала я поставил проверку на комнату НАЗНАЧЕНИЯ — и это + * поймал пользователь прогоном в SDLPoP: он умер В комнате 7, но + * влево из неё не выходил, и оригинал вернул его в стартовую + * комнату 9, а плита осталась цела. Заодно тут запоминается + * достигнутый потолок HP: смерть после чекпойнта его не отбирает. */ + if (left && pop_current_level == POP_CHKP_LEVEL && + cur_room == POP_CHKP_ROOM) { + pop_checkpoint = 1; + hitp_beg_lev = hitp_max; + } + /* Соседи чекпойнта по leave_room (seg002:0490): в ветке ухода + * ВПРАВО (pop_leave_dir == 2) — sword_disappears + meet_Jaffar, + * в ветке ВЛЕВО — Jaffar_exit. Комната во всех трёх ещё СТАРАЯ, + * как и в оригинале (goto_other_room идёт после). */ + if (pop_leave_dir_prev == 2) { + pop_sword_disappears(); + pop_meet_jaffar(); + } else if (left) { + pop_jaffar_exit(); + } + if (nb) { + /* side для pop_guard_follow — то же направление, по которому + * ищем соседа (roomleave_result оригинала). */ + pop_nav_hold = 0; /* сменил комнату НОГАМИ — чит забыт */ + enter_room_side(nb, (uint8_t)(pop_leave_dir_prev - 1)); + gfx_set_draw_page(back); + } + } + + PROF(4); /* слой фона */ + pop_dbg_m2(); /* ЗАМЕР: конец логики / начало фона */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + + pop_loose_tick(); /* loose-полы: анимация + перерисовка (СЛОЙ ФОНА */ + /* — ДО Kid, чтобы Kid всегда был поверх плиты) */ + pop_dbg_m5(); /* ЗАМЕР: pop_loose_tick сделан */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_process_trobs(cur_room); /* пики/ворота/дверь: продвинуть СОСТОЯНИЕ (порт */ + /* process_trobs — рисования внутри нет) */ + if (pop_neigh_dirty) { + /* Тайл СОСЕДА изменился заочно (провалилась брошенная плита — + * animate_loose). Обе кромочные копии — снимки на момент входа + * в комнату, обновляем обе: g_lcol/g_rcol (коллизия у шва) и + * lcol_fg (отрисовка: колонка 9 левого соседа видна в col0 + * через draw_tile_anim_right, pop_room.c). Без второй шов + * продолжал рисовать плиту, которой уже нет — LOOSE-ROOM-CHANGE. + * Событие редкое, поэтому по сигналу, а не опросом каждый кадр. */ + uint8_t lft = pop_room_link(cur_room, 0); + pop_neigh_dirty = 0; + pop_map_refresh_edges(); + if (lft) { + uint8_t i; + pop_level_access_begin(); + for (i = 0; i < 3; i++) { + uint8_t c = pop_level_tile_raw(lft, (uint8_t)(9 + i * 10)); + if (c == (uint8_t)(lcol_fg[i] & 0x1F)) continue; + lcol_fg[i] = c; + seam_rows |= (uint8_t)(1 << i); + seam_redraw = 2; /* обе страницы дабл-буфера */ + } + pop_level_access_end(); + } + } + pop_dbg_m6(); /* ЗАМЕР: pop_process_trobs сделан */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_redraw_needed(); + pop_dbg_m7(); /* ЗАМЕР: pop_redraw_needed сделан */ /* и разобрать пометки перерисовки тайлов (порт */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* redraw_needed, seg008:0178) — СЛОЙ ФОНА, до Kid */ + { /* ворота соседа слева (room8) видны в col0 — */ + uint8_t lft = pop_room_link(cur_room, 0); /* редрой шва ТОЛЬКО при изменении openness */ + if (lft) { + uint8_t *m = pop_trob_modif(lft); + uint8_t i, changed = 0; + /* Все ТРИ ряда колонки 9: ворота в шве бывают в любом из них + * (BUG-GATE-SEAM-ROW1, см. объявление seam_sig). */ + for (i = 0; i < 3; i++) { + uint8_t g = seam_row_sig(m, lcol_fg, i), prev = seam_sig[i]; + seam_sig[i] = g; /* СНАЧАЛА сохранить (обход SDCC z80 sub-store */ + /* бага: `if(g!=s)s=g` эмитит store РАЗНОСТИ g-s, */ + /* не g — sdcc_z80_cmp_store_a_bug) */ + if (g != prev) changed |= (uint8_t)(1 << i); + } + if (changed) { /* openness изменился → обе страницы дабл-буфера */ + /* Плюс ряд ВЫШЕ: верх решётки (draw_tile_anim_topright, + * seg008:0568 — маска 68 + кадр DOOR_FRAM_TOP) рисует не + * сам тайл ворот, а тайл над-справа от него, то есть у + * нас (r−1, 0). Он тоже анимируется вместе с openness; + * без этого чёрный треугольник над воротами оставался + * статичным, пока бары ездили (BUG-GATE-SEAM-ROW1). */ + seam_rows |= (uint8_t)(changed | (changed >> 1)); + seam_redraw = 2; + } + if (seam_redraw) { + lcol_bg[0] = m[9]; lcol_bg[1] = m[19]; lcol_bg[2] = m[29]; + pop_room_redraw_seam_left(seam_rows); + if (--seam_redraw == 0) seam_rows = 0; + } + } + } + /* Бесшовный выход уровня 12 (play_level_2, seg000:900): двери уровня + * у него нет — уровень кончается ФАКТОМ попадания в комнату 23. + * Условие «уровень ещё не сменён» обязательно: комната 23 есть и у + * 13-го, причём она там СТАРТОВАЯ, и без гарда переход зациклился бы + * (12 → 13 → 14 в первом же кадре нового уровня). */ + if (pop_current_level == POP_SEAMLESS_LEVEL && + pop_next_level == pop_current_level && + !pop_nav_hold && /* не по чит-телепорту, см. pop_state.c */ + kid_room == POP_SEAMLESS_ROOM) { + pop_next_level = (uint8_t)(pop_current_level + 1); + pop_seamless = 1; + } + /* WIN_LEVEL/WIN_ROOM SDLPoP: скрытый 14-й уровень заканчивается + * входом в room 5. Не подменяем это `next_level=15`: уровня 15 в + * сборке принципиально нет, а ending — самостоятельное состояние. */ + if (!is_demo && pop_flow_is_ending(pop_current_level, kid_room)) { + if (!pop_app_dispatch(POP_APP_EV_ENDING_STARTED)) { + failed = 1; + quit = 1; + } + break; + } + /* В level 0 нет обычной победы: достигли последней комнаты, умерли + * или доиграли запись — показать текущий кадр и затем вернуться в + * title. В частности, не вызываем level_switch(0->1). */ + if (is_demo && (kid_room == 24 || pop_kid_dead)) demo_finished = 1; + /* Смена уровня целиком уехала в банк 8 (pop_level_change): это + * событие раз в несколько минут, а кода на нём было под сотню строк + * в самом горячем месте программы — резидент W1 у нас в дефиците. */ + if (!is_demo && pop_next_level != pop_current_level && + pop_level_change(back) != 0) { + failed = 1; quit = 1; break; + } + if (!is_demo && pop_kid_dead) { /* demo после смерти завершается, без respawn */ + dead_frames++; + /* Просьба нажать кнопку, мигание и таймаут — в банке + * (pop_status.h). Место возрождения выбирает pop_start_level: + * на части уровней это НЕ старт, а уже пройденный чекпойнт. */ + uint8_t dr = pop_dead_prompt(dead_frames); + if (dr == 2) break; /* 288 тиков молчания -> title */ + if (dr) { + dead_frames = 0; + pop_start_level(); + gfx_set_draw_page(back); + } + } + if (pop_loose_fell) { /* remove_loose: сама плита исчезла */ + uint8_t fpos = (uint8_t)(pop_loose_fell - 1); + pop_loose_fell = 0; + pop_level_set_tile(cur_room, fpos, TILE_EMPTY); + } + if (pop_loose_exit) { /* кусок приземлился в НЕ отрисованной комнате */ + /* Комнату и тайл посадки считает сам кусок (pop_room.c): он летит + * через комнаты честным mob_down_a_row, а не «одним переходом + * вниз», как было раньше. Здесь остаётся то, что положено + * loose_land (seg007:11E8): щебень на месте посадки и КНОПКА + * под ним. */ + uint8_t lroom = pop_loose_exit_room; + uint8_t lpos = (uint8_t)(pop_loose_exit - 1); + uint8_t tt = pop_level_tile(lroom, lpos); + pop_loose_exit = 0; + /* На opener подставляется button_type = щебень, то есть «открыть + * НАСОВСЕМ» (modifier 0xFF). Второе место того же порта: своя + * комната — в pop_map.c. */ + if (tt == TILE_OPENER || tt == TILE_CLOSER) + pop_trigger_button(lroom, lpos, + (uint8_t)(tt == TILE_OPENER ? TILE_DEBRIS : tt), + pop_trob_modif(lroom)[lpos], 1); + /* На факеле остаётся отдельный тайл «факел с щебнем» + * (seg007:1067), а не обычный щебень. */ + pop_level_set_tile(lroom, lpos, + (uint8_t)((tt == TILE_TORCH || tt == TILE_TORCH_DEBRIS) + ? TILE_TORCH_DEBRIS : TILE_DEBRIS)); + } + if (pop_ceil_fell) { /* провалилась плита-ПОТОЛОК (ряд 2 комнаты сверху) */ + uint8_t ccol = (uint8_t)(pop_ceil_fell - 1); + uint8_t up = pop_room_link(cur_room, 2); + pop_ceil_fell = 0; + if (up) pop_level_set_tile(up, (uint8_t)(20 + ccol), TILE_EMPTY); /* колодец */ + } + if (pop_debris_at) { /* кусок лёг в ЭТОЙ комнате → щебень */ + pop_level_set_tile(cur_room, (uint8_t)(pop_debris_at - 1), TILE_DEBRIS); + pop_debris_at = 0; + } + if (pop_item_taken) { /* предмет поднят → тайл стал полом */ + item_pos = (uint8_t)(pop_item_taken - 1); + item_bake = 2; /* запечь на ОБЕИХ страницах */ + /* Тайл в странице уровня ставит САМА do_pickup (pop_map.c): здесь + * это было бы слишком поздно — между подбором и этим местом + * успевает пройти pop_process_trobs (BUG-POTION-STRIPE). */ + pop_add_trob(cur_room, item_pos, -1); /* меч убран — блеска больше нет */ + pop_item_taken = 0; + } + if (item_bake) { /* стереть спрайт меча из фона страницы */ + pop_floor_bake(item_pos / 10, item_pos % 10); + item_bake--; + } + /* Вспышка фона — порт flash_if_hurt/remove_flash_if_hurt (seg003:0AFC): + * do_flash(color) = set_bg_attr(0, color), т.е. подмена НУЛЕВОЙ записи + * палитры (вспыхивает всё чёрное поле). Оригинал ставит цвет и СНИМАЕТ + * его В ТОМ ЖЕ кадре, поэтому пока идёт flash_time видно СТРОБ + * «жёлтый/чёрный», а не одну длинную заливку. Снятие — после первого + * gfx_wait_vsync (ниже): цвет держится ~1 растровый кадр из трёх. + * Приоритет как в оригинале: активный flash_time > кадр урона + * (hitp_delta<0 -> ярко-красный, ровно на один кадр). */ + if (pop_flash_time) { + /* Цвет вспышки (flash_color оригинала): меч — ярко-жёлтый, зелья + * HP — красный, перо (медленное падение) — зелёный. */ + if (pop_flash_color == POP_FLASH_RED) flash_bg(255, 0, 0); + else if (pop_flash_color == POP_FLASH_GREEN) flash_bg(85, 255, 85); + else if (pop_flash_color == POP_FLASH_WHITE) flash_bg(255, 255, 255); + else flash_bg(255, 255, 85); + pop_flash_time--; + if (!pop_flash_time) pop_flash_color = POP_FLASH_YELLOW; + flash_on = 1; + } else if (pop_kid_hurt) { + flash_bg(224, 0, 48); /* color_12: игра правит его на */ + /* #E00030 (seg000:164), это не */ + /* стандартный VGA brightred */ + flash_on = 1; + } + update_kid_render_dx(); /* straddle: смещение спрайта, если kid_room≠cur_room */ + PROF(6); /* спрайты: страж, Kid, fore поверх */ + pop_dbg_m3(); /* ЗАМЕР: конец фона / начало спрайтов */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* check_mirror (seg003:0798) — ПЕРВАЯ строка draw_people + * (seg008:228A), до отрисовки кого бы то ни было: если Кид только что + * пролетел сквозь зеркало, тень должна родиться ДО того, как слот + * соперника пойдёт рисоваться, иначе кадр её проглотит. */ + pop_check_mirror(); + /* Падающие плиты, идущие ПОД Kid. Здесь, а не внутри pop_loose_tick: + * фон кадра (перепечатки тайлов, шов) обязан быть готов ДО любого + * спрайта, иначе перепечатка ложится поверх уже нарисованного куска — + * так окно задней стены и передняя грань пола оказывались на плите. */ + pop_dbg_m10(); /* ЗАМЕР: check_mirror сделан */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_loose_mob_draw(); + pop_dbg_m11(); /* ЗАМЕР: pop_loose_mob_draw сделан */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + { /* Кто позже — тот поверх; порядок задаёт обход тайлов, а не роль + * персонажа (см. guard_over_kid). Отрисовка одна на всех Char + * (pop_cdraw.c), различает их только слот. */ + /* Пересчёт после тика: слот, за который heal не платили, мог + * всё-таки сдвинуться — тогда бит снимется, а прошлый кадр + * сотрёт сам pop_char_draw (страховка cd_heal). */ + uint8_t g_after; + skip = pop_char_skip_mask(); + pop_dbg_m12(); /* ЗАМЕР: skip_mask сделан */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + /* Порядок «кто поверх кого» нужен, только если кого-то РИСУЕМ. + * При skip == 3 оба слота тихие, рисовать некого — а вызов стоит + * трамплина в банк 8 и двух objtile_at_char, 14 424 такта + * (замер 11/15, 2026-08-19). Перестановка безопасна: обе + * функции только читают, и читают разное — skip_mask снимок + * cd_sig, guard_over_kid габариты pop_cd прошлого кадра. */ + g_after = (skip == 3) ? 0 : guard_over_kid(); + if (!g_after && !(skip & 2)) { pop_char_draw(POP_CH_OPP); pop_char_fore(POP_CH_OPP); } + PROF(6); /* кадр Кида (тот же циан) */ + if (!(skip & 1)) pop_char_draw(POP_CH_KID); /* спрайт + брызги + клинок */ +#if DBG_KIDOBJ + /* ВРЕМЕННО: трасса для сверки с SDLPoP кадр в кадр. Место — то + * же, что у оригинала (конец add_kid_to_objtable), и ВНЕ skip: + * пропуск перерисовки не должен рвать трассу. */ + pop_dbg_kidobj(); +#endif + if (g_after && !(skip & 2)) { pop_char_draw(POP_CH_OPP); pop_char_fore(POP_CH_OPP); } + /* Брызги урона рисует сама отрисовка персонажа (порядок objtable: + * сразу после него и до клинка), а флаги снимаем здесь: соперника + * могло не оказаться в отрисованной комнате — тогда брызги просто + * пропадают, как в оригинале. Снимаем ПО ФАКТУ, а не безусловно: + * запись в кадре удара — готовая точка останова для отладки в + * MAME (watchpoint), и лишней записи каждый кадр нет. */ + if (pop_guard_hurt) pop_guard_hurt = 0; + if (pop_kid_hurt) pop_kid_hurt = 0; + } + pop_loose_mob_draw_over(); /* падающая плита НИЖЕ Kid → поверх него (y-порядок) */ + /* ПЕРЕДНИЙ СЛОЙ по пометкам — последним в кадре, как foretable у + * оригинала (draw_tables, seg008:1373). Пометки ставят сами + * объекты вокруг себя (pop_redraw.h). */ + pop_fore_needed(); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_hp_draw(); /* полоса HP — только при изменении */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + PROF(6); /* fore поверх Kid + борта */ + /* Fore-слой Кида — В КОНЦЕ, после плиты и полосы HP: окно fore-клипа + * одно на всех, и его ставит каждый, кто рисовал персонажа, поэтому + * pop_char_fore(KID) заодно возвращает окно Кида (при «соперник + * поверх» в нём остался бы соперник, и передние грани у Кида + * отсеклись бы целиком — он рисовался бы поверх столбов). */ + if (!(skip & 1)) pop_char_fore(POP_CH_KID); + /* Персонажи на этой странице приведены в порядок — след «фон трогали» + * для неё снят. ПОСЛЕ отрисовки, а не до: слой фона этого же кадра + * взводит бит и обязан быть учтён. Всё, что рисуется ниже + * (mob поверх Кида, борта), взводит его заново само. */ + pop_cd_clear(back); + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_room_clip_borders(); /* почистить борта — только в кадрах падения (гейт) */ + + /* ФИКСИРОВАННЫЙ ЛОГИЧЕСКИЙ КАДР (pop_pace.h): период = max(n, + * ceil(W)) вместо прежнего ceil(W) + 2. Раньше здесь стояли три + * gfx_wait_vsync ПОДРЯД — они отсчитывались от КОНЦА работы, из-за + * чего бюджет кадра был один растр, а превышение его на такт стоило + * целого лишнего. Теперь ждём от ЯКОРЯ начала кадра. + * + * Первый фронт — всегда: на нём и происходит tear-free своп (до + * начала картинки остаётся верхний бордер, 16 строк). Строб вспышки + * снимается ровно между первым фронтом и остальными, как и было. */ + + PROF(0); /* чёрный: ждём vsync */ + + /* Звук: решить, запускать ли заявленный за кадр эффект (порт + * play_next_sound). Место то же, что у оригинала — конец отрисовки + * кадра, seg000:954: все заявки кадра уже поданы, и из них выбирается + * важнейшая. Раньше по кадру нельзя — потеряются поздние заявки. */ + pop_sfx_tick(); + /* МУЗЫКА ПО ХОДУ ИГРЫ. Заявку положил pop_sfx_play (звук без + * оцифровки — это Adlib-трек оригинала), а диск трогаем здесь: + * первая страница читается сразу и трек уже звучит, остальные + * доливаются по одной за кадр — 33 мс на кадр, пока догоняем. */ + pop_music_service(); + + /* Обе служебные полосы — нижняя статус-строка (сообщения оригинала) + * и верхняя отладочная. Всё change-driven и целиком в банке 9, + * поэтому звать можно каждый кадр: пока показанное не изменилось, + * работы там нет. Место — ПОСЛЕ pop_hp_draw: тот делит нижнюю + * полосу с текстом. */ + pop_status_frame(cur_room); + + pop_dbg_m4(); /* ЗАМЕР: конец спрайтов */ + pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ + pop_wait_edge(); + if (flash_on) { /* строб: цвет держался один кадр растра */ + flash_bg(0, 0, 0); + flash_on = 0; + } + /* Условие боя — буквально как у оригинала (SDLPoP seg003.c:363): + * не «идёт бой» и не «рядом страж», а «у Кида ВЫНУТ МЕЧ». */ + pop_pace_end(pop_pace_n((uint8_t)(Kid.sword == SWORD_2_DRAWN))); + gfx_set_visible_page(back); + front = back; + if (is_demo && demo_finished) break; + } + + if (quit) break; + /* Restart Game завершает только игровой цикл. Следующая итерация + * верхнего автомата начинает title/intro с самого первого экрана. */ + if (pop_app_state == POP_APP_TITLE) continue; + if (pop_app_state == POP_APP_TIME_EXPIRED) { + pop_time_expired_show(); + if (!pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)) { + failed = 1; + break; + } + continue; /* TITLE -> intro -> новый demo */ + } + /* ENDING/HOF тоже завершили внутренний игровой цикл, но это не ошибка + * протокола и не demo-result: следующая итерация верхнего автомата + * вызовет соответствующий экран. */ + if (pop_app_state == POP_APP_ENDING || + pop_app_state == POP_APP_HALL_OF_FAME) + continue; + if (pop_app_state != POP_APP_DEMO) break; + if (demo_new_game) { + /* Чистую инициализацию выполняет общий LEVEL_LOAD в начале следующей + * итерации — тот же маршрут теперь используют skip title/intro. */ + if (!pop_app_dispatch(POP_APP_EV_NEW_GAME)) { + failed = 1; + break; + } + continue; + } else if (!pop_app_dispatch(POP_APP_EV_DEMO_FINISHED)) { + failed = 1; + break; + } + } + } + + PROF(0); /* выйти с ЧЁРНЫМ бордюром (иначе цвет */ + /* последней фазы остаётся в DOS) */ + pop_shutdown(); /* снять хук, закрыть ввод/графику, отдать ресурсы */ + return failed ? 1 : 0; +} diff --git a/applications/SprPoP/src/sprpop_cold.c b/applications/SprPoP/src/sprpop_cold.c new file mode 100644 index 0000000..9ba92b0 --- /dev/null +++ b/applications/SprPoP/src/sprpop_cold.c @@ -0,0 +1,1269 @@ +/* + * sprpop_cold.c — ХОЛОДНАЯ половина главного цикла, собирается в БАНК + * (--bank 8=sprpop_cold.c, окно W3). Здесь всё, что отрабатывает раз на + * уровень или только по отладочной клавише; кадровый путь остался в + * sprpop.c. Зачем разделили и что осталось снаружи — см. sprpop_cold.h. + * + * Правила банка те же, что у guards.c: состояние — в W1/W2 (данные этого + * модуля тоже, --bank-data не включён), звать можно резидент W1/W2 и другие + * __banked-модули через трамплины. + */ +#include +#include +#include "sprpop_cold.h" +#include "pop_bg.h" /* pop_loose_mob_reset — банк 7, зовём через трамплин */ +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_level.h" +#include "pop_trob.h" +#include "pop_guard.h" +#include "pop_state.h" +#include "pop_geom.h" +#include "pop_cdraw.h" /* pop_cd — инвалидация слотов после переворота */ +#include /* gfx_copy_page */ +#include "pop_tune.h" +#include "pop_ctrl.h" /* pop_ctrl_init/close — ввод поднимает и закрывает boot */ +#include "pop_shadow.h" /* запечённый набор Тени */ +#include "pop_sfx.h" /* звуковые эффекты через CBL */ +#include "pop_vflip.h" /* сброс зеркальных страниц на смене уровня */ +#include "pop_cheat.h" /* pop_cheats — режим разработки включает pop_boot */ +#include "pop_pace.h" /* pop_speed_mode — режим скорости в отладочной метке */ +#include "pop_settings.h" /* pop_settings_defaults — стартовый профиль VANILLA */ +#include "pop_cfg.h" /* pop_cfg_load — POP.CFG поверх дефолтов */ +#include "pop_flow.h" /* таблица level-flow: какая сцена перед уровнем */ +#include "pop_intro.h" /* pop_pre_cutscene_show — сцена перед уровнем */ +#include "pop_app.h" /* pop_app_dispatch — состояния приложения */ +#include "pop_pal.h" /* pop_pal_apply — гашение перед загрузкой */ +#include "pop_timer.h" /* pop_timer_minutes — выбор сцены «времени мало» */ +#include "pop_ui.h" /* POP_PAL_DIM_BLACK */ +#include "pop_menu.h" /* пауза-меню: коды выбора */ +#include "pop_qsave.h" /* QuickSave/QuickLoad по F6/F9 */ +#include "pop_status.h" /* pop_status_invalidate после меню */ +#include +#include "pop_status.h" /* статус-строка и отладочная полоса — банк 9 */ +#include "pop_menu.h" /* pop_menu_init/free — атлас UI в bank 9 */ +#include "pop_ui.h" /* ранняя чёрная палитра + snapshot стартового экрана */ +#include "pop_pal.h" /* единый lifecycle game/title/level палитр */ +#include "pop_timer.h" /* global 60-minute timer — core in bank 9 */ +#include /* puts/printf — сообщения об ошибках загрузки */ +#include + +#define SEQ_STAND 2 +#define SEQ_TURN 5 +#define SEQ_FALL 7 +#define SEQ_RUN 84 +#define ENTRY_POSE_FALL 1 /* tbl_entry_pose[1] */ +#define ENTRY_POSE_RUN 2 /* tbl_entry_pose[13] */ +#define TILE_LEVELDOOR_LEFT 16 /* tiles_16_level_door_left */ +#define TILE_EMPTY 0 +#define LINK_DOWN 3 /* сторона links: 0=L,1=R,2=U,3=D */ + +/* Во время смены уровня enter_room должен только СОБРАТЬ новую страницу. + * Физический flip делает pop_level_switch после окончательной палитры. */ +static uint8_t level_prepare; +static uint8_t level_prepared_page; + +/* find_start_level_door (seg003:02E6): дверь уровня В СТАРТОВОЙ КОМНАТЕ — + * это дверь, через которую Кид ВОШЁЛ, поэтому она открыта полностью + * (modif = 43) и заводится trob'ом типа 3 (start_level_door, seg007:0D72). + * Без этого на уровне 2 Кид материализуется внутри глухой закрытой створки: + * его стартовый тайл (комната 5, ряд 1, колонка 3) — правая половина + * той самой двери. */ +static void find_start_level_door(uint8_t room) +{ + uint8_t *m = pop_trob_modif(room); + uint8_t tp; + if (!m) return; + /* Батч-доступ к странице уровня тут не нужен: функция отрабатывает раз + * за уровень, а pop_add_trob сам лезет в W0. */ + for (tp = 0; tp < 30; tp++) { + if (pop_level_tile(room, tp) != TILE_LEVELDOOR_LEFT) continue; + m[tp] = 43; /* створка поднята полностью */ + pop_add_trob(room, tp, 3); /* ...и тут же поедет ВНИЗ (type>=3) */ + } +} + + + +/* МЕНЮ, F10 И QUICKSAVE/LOAD — граница кадра, но не горячий путь. + * + * Всё это события «раз в партию», а в главном цикле они занимали полсотни + * строк резидента W1. Здесь же держим и edge-состояния клавиш: снаружи они + * никому не нужны. + * + * restart_level — «пользователь выбрал Restart Level» (главный цикл сделает + * это сам, после вычисления back-страницы, как респавн); + * dead_reset — «QuickLoad удался, счётчик кадров смерти сбросить». + * Возврат: 0 — продолжаем кадр, 1 — выйти из игрового цикла (Restart Game), + * 2 — выйти и завершить работу (Quit), -1 — сбой диспетчера. */ +int8_t pop_frame_ui(uint8_t *restart_level, uint8_t *dead_reset) __banked +{ + static uint8_t esc_prev, qs_save_prev, qs_load_prev; + uint8_t e = kbd_raw_down(KBD_MENU); + uint8_t menu_opened; + int8_t mrc = POP_MENU_NONE; + + if (e && !esc_prev) pop_menu_request = 1; + esc_prev = e; + menu_opened = pop_menu_request; + if (menu_opened) { + if (!pop_app_dispatch(POP_APP_EV_PAUSE)) return -1; + mrc = pop_menu_process(); + } + if (mrc == POP_MENU_QUICKSAVE) pop_qsave_request_save(); + else if (mrc == POP_MENU_QUICKLOAD) pop_qsave_request_load(); + else if (mrc == POP_MENU_QUICKLOAD_BACKUP) + pop_qsave_request_load_backup(); + else if (mrc == POP_MENU_RESTART_LEVEL) *restart_level = 1; + else if (mrc == POP_MENU_RESTART_GAME) + return (int8_t)(pop_app_dispatch(POP_APP_EV_RESTART_INTRO) ? 1 : -1); + else if (mrc == POP_MENU_QUIT) + return (int8_t)(pop_app_dispatch(POP_APP_EV_QUIT) ? 2 : -1); + if (menu_opened) { + /* Меню переписало обе страницы целиком. Снимки слотов об этом не + * знают и продолжают считать, что персонаж на странице уже + * нарисован, — стоящий Кид оставался бы невидимым до первого своего + * движения. */ + pop_cd_forget(); + pop_status_invalidate(); + if (!pop_app_dispatch(POP_APP_EV_RESUME)) return -1; + } + /* F10 — отладочный немедленный выход (раньше был на Esc, но Esc теперь + * открывает меню; уйдёт вместе с отладкой). */ + if (kbd_raw_down(KBD_QUIT)) + return (int8_t)(pop_app_dispatch(POP_APP_EV_QUIT) ? 2 : -1); + /* F6/F9 только ставят запрос; файловая операция выполняется здесь, на + * границе кадров — вне окон Char и до heal/tick/draw. */ + { + uint8_t s = kbd_raw_down(KBD_QUICKSAVE); + uint8_t l = kbd_raw_down(KBD_QUICKLOAD); + + if (s && !qs_save_prev) pop_qsave_request_save(); + if (l && !qs_load_prev) pop_qsave_request_load(); + qs_save_prev = s; qs_load_prev = l; + if (pop_qsave_process() > 0) *dead_reset = 1; + } + return 0; +} + +/* СМЕНА УРОВНЯ — целиком здесь, а не в главном цикле. + * + * Событие редкое (раз в несколько минут игры), а кода на нём была без + * малого сотня строк — и все они лежали в _main, то есть в резиденте W1, + * где у нас счёт на десятки байт. Наружу остаётся одно условие и один + * вызов. + * + * back — страница, в которую главный цикл рисует кадр: после загрузки + * уровня её надо вернуть себе (level_switch показывает свою). + * Возврат: 0 — уровень сменён, -1 — сбой, главный цикл завершает работу. */ +int8_t pop_level_change(uint8_t back) __banked +{ + uint8_t reveal_pending = 0; + pop_pre_cutscene_t pre = pop_flow_pre_cutscene(pop_next_level, + pop_timer_minutes); + + /* Загрузка файла уровня, спрайтов соперника и тайлсета — в + * pop_level_switch ниже. До 2/4/6/8/9/12 таблица + * flow запускает отдельное состояние CUTSCENE, как play_level() + * SDLPoP: следующий уровень не трогаем до завершения/skip. */ + if (pre != POP_PRE_NONE) { + if (!pop_app_dispatch(POP_APP_EV_CUTSCENE_STARTED)) + return -1; + pop_pre_cutscene_show(pre); + if (!pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)) + return -1; + /* Катсцена оставляет экран чёрным. Новый уровень будет + * открыт одним применением его готовой палитры. */ + reveal_pending = 1; + } else { + /* CBL глушим ДО первой же операции с палитрой. Палитра идёт + * через BIOS ($A4) с запрещёнными прерываниями: насос не + * успевает долить блок, железо крутит хвост своего буфера — + * на слух скрежет. Раньше pop_sfx_pause() звался только + * внутри pop_level_switch, то есть УЖЕ ПОСЛЕ гашения, и + * переход без катсцены (в т.ч. телепорт Shift+L) скрежетал + * почти всегда — поймано пользователем 2026-08-25. Ветка с + * катсценой свободна от этого: pop_pre_cutscene_show глушит + * звук первым действием. */ + pop_sfx_pause(); + /* Без fade: старый кадр гасим сразу и обязательно даём + * показать один чёрный растр ДО диска/перерисовки. */ + pop_pal_apply(POP_PAL_DIM_BLACK); + pop_wait_edge(); + reveal_pending = 1; + if (!pop_app_dispatch(POP_APP_EV_LEVEL_FINISHED)) + return -1; + } + if (pop_level_switch() != 0 || + !pop_app_dispatch(POP_APP_EV_LEVEL_READY)) + return -1; + if (reveal_pending) { + /* Обе страницы и snapshot уже принадлежат новому уровню: + * включить его корректную палитру одним переходом. */ + pop_pal_apply(0); + reveal_pending = 0; + } + /* level_switch оставляет CBL закрытым: palette/fade идут через + * BIOS и при открытом буфере давали скрежет на первом кадре. */ + (void)pop_sfx_start(); + gfx_set_draw_page(back); + + return 0; +} + +/* start_level (seg003:0055) + do_startpos (seg003:01A3) + set_start_pos + * (seg003:028A). + * + * Позиция берётся ИЗ ДАННЫХ уровня (pop_level_start_*), а не из #define: + * start_pos — тайл 0..29 (row*10+col), start_dir — направление, в котором + * персонаж должен ОКАЗАТЬСЯ. + * + * Направление ИНВЕРТИРУЕТСЯ (`Char.direction = ~level.start_dir`): персонаж + * ставится спиной и доворачивается кадрами позы входа. Сама поза — из + * tbl_entry_pose (data.h:848 = {0,1,0,…,2,…}): + * 1 (уровень 1) — падение внутрь (seq_7_fall) + нажатие кнопки room5(0,2); + * это оно захлопывает решётку за спиной в первой комнате; + * 2 (уровень 13) — вбегание (seq_84_run); + * иначе — разворот на месте (seq_5_turn). + * Таблица индексируется НОМЕРОМ УРОВНЯ — pop_level_entry_pose(). */ +void pop_start_level(void) __banked +{ + uint8_t entry_pose, start_room, pos, seq = SEQ_TURN, chkp; + int8_t dir; + + pop_leveldoor_open = 0; /* seg003:97 — дверь прошлого уровня не в счёт */ + pop_music_level_start(); /* одноразовые темы уровня — заново */ + pop_feather = 0; /* seg003:189 — перо не переживает старт уровня */ + /* upside_down тоже сбрасывается стартом уровня (seg003:38/188). Если + * картинка была перевёрнута, отрисовка комнаты ниже уже пойдёт обычной + * стороной, поэтому переворачивать ничего не надо — только снять флаги. */ + pop_upside = 0; + pop_upside_want = 0; + pop_upside_dirty = 0; + entry_pose = pop_level_entry_pose(pop_current_level); + start_room = pop_level_start_room(); + pos = pop_level_start_pos(); + dir = (int8_t)~pop_level_start_dir(); + /* Особое событие: старт с чекпойнта (do_startpos, seg003:0141). + * Числа — в pop_tune.h. Плиту снимаем ПОСЛЕ pop_level_reset_tiles() + * ниже, иначе рестарт её вернёт. */ + chkp = (uint8_t)(pop_current_level == POP_CHKP_LEVEL && pop_checkpoint); + if (chkp) { + start_room = POP_CHKP_RESPAWN_ROOM; + pos = POP_CHKP_RESPAWN_TILEPOS; + dir = (int8_t)~POP_CHKP_RESPAWN_DIR; /* стартует в ПРОТИВОположную + * и разворачивается seq_5 */ + } + +#ifdef DBG_START_ROOM + /* ОТЛАДОЧНЫЙ СТАРТ: сразу в целевую комнату оптимизации, минуя проход + * уровня. Задаётся сборкой (make ROOM=15 POS=2), по умолчанию — сцена + * 11/15 из docs/perf_l11_room15.md: Кид в (0,2), справа чомпер под + * факелом, дальше второй факел и страж. + * + * Ставится ПОСЛЕ чекпойнта намеренно: отладочная позиция должна + * перебивать и его тоже, иначе рестарт уровня уводил бы Кида из + * измеряемой комнаты. Направление и позу входа не трогаем — они из + * данных уровня, как у обычного старта. */ + /* ...НО ТОЛЬКО НА СВОЁМ УРОВНЕ. pop_start_level зовётся ещё и на + * СМЕНЕ уровня, и без этой проверки отладочная позиция перехватывала + * переход: на 6-м Кид проваливается в комнате 1, а на 7-м обязан + * появиться в комнате 17 и продолжить падение вниз (спецсобытие + * «вход падением», см. ниже). Вместо этого он оказывался в + * DBG_START_ROOM/POS, спецсобытие не срабатывало вовсе, и переход 6->7 + * выглядел сломанным (поймано пользователем 2026-08-20 на сборке + * make LEVEL=6 ROOM=1 POS=18). + * + * Рестарт ТОГО ЖЕ уровня (смерть, чекпойнт) отладочную позицию + * сохраняет — ради этого она и перебивает чекпойнт выше. */ + if (pop_current_level == FIRST_LEVEL) { + start_room = DBG_START_ROOM; + pos = DBG_START_POS; + } +#endif + + /* Рестарт уровня = load_level() заново (play_level, seg003:57): в + * исходное возвращаются И модификаторы (пики/ворота), И сами тайлы — + * разбитые плиты целы, выпитые зелья на месте (BUG-RESPAWN-1), и + * убитые стражи снова живы на своих тайлах (BUG-RESPAWN-2). */ + pop_trob_reset(); + pop_level_reset_tiles(); + pop_level_reset_guards(); + pop_guard_reset(); /* start_level: Guard.direction = none */ + /* united_with_shadow = 0 (seg003:95) — состояние ПЕРСОНАЖА уровня, а не + * комнаты: pop_guard_reset зовётся и при каждом входе в комнату (из + * pop_guard_enter), поэтому гасить слияние надо здесь. */ + pop_united_shadow = 0; + pop_shadow_init = 0; + pop_guard_notice_timer = 0; /* фора Джафара — не переживает рестарт */ + pop_nav_hold = 0; /* старт уровня снимает и держатель чита */ + pop_loose_mob_reset(); /* start_level: mobs_count = 0 (seg003:88) */ + pop_loose_forget(); /* ...и фазы плит — уровень восстановлен целиком */ + /* «Особое событие: убрать loose-плиту» (do_startpos): без этого путь в + * комнату-чекпойнт после респавна остался бы непройденным. */ + if (chkp) + pop_level_set_tile(POP_CHKP_CLEAR_ROOM, + (uint8_t)(POP_CHKP_CLEAR_ROW * 10 + POP_CHKP_CLEAR_COL), + TILE_EMPTY); + + /* Кнопку жмём ДО отрисовки комнаты — как в оригинале (do_startpos + * отрабатывает раньше первого draw_room), иначе шов кадр показывал бы + * ещё не тронутое состояние решётки. */ + if (entry_pose == ENTRY_POSE_FALL) { + /* get_tile(5,2,0) + trigger_button(0,0,-1) — «нажать» кнопку room5, + * col2, row0 её же типом и модификатором (в оригинале и то и другое + * берётся из curr_* после get_tile). */ + uint8_t tp = 2; /* row0*10 + col2 */ + pop_trigger_button(5, tp, pop_level_tile(5, tp), pop_trob_modif(5)[tp], + 0 /* seg003:170 — playsound=0 */); + seq = SEQ_FALL; + } else if (entry_pose == ENTRY_POSE_RUN) { + seq = SEQ_RUN; + } + + /* Тоже ДО отрисовки комнаты: дверь, через которую вошли, обязана быть + * открыта уже в первом кадре (в оригинале find_start_level_door стоит + * между do_startpos и draw_level_first). */ + find_start_level_door(start_room); + + enter_room(start_room); + kid_init(seq, (int8_t)(pos % 10), (int8_t)(pos / 10), dir); +#ifdef DBG_START_ROOM + /* ...и поправить x: kid_init ставит `x_bump[col] + TILE_SIZEX`, а это + * ЛЕВАЯ ГРАНИЦА СЛЕДУЮЩЕЙ колонки (порт set_start_pos — в данных уровня + * стартовые позиции подобраны под такую договорённость). Отладочный + * старт называет тайл, В КОТОРОМ Кид должен оказаться, поэтому сдвигаем + * его внутрь названного: с границы физика относит Кида к колонке правее, + * и в 11/15 он стартовал прямо в челюстях (найдено пользователем + * 2026-08-19). + * + * Отступ ровно 2, а не «половина тайла»: колонку определяет не сам x, а + * ВЕСОВАЯ ТОЧКА кадра (determine_col -> dx_weight: dx позы минус + * weight-биты, с учётом направления), поэтому геометрически ровной + * середины тут нет. Число снято замером живой сцены: x = 98 при позе + * стойки даёт curr_col = 2, x = 94 — уже 1. */ + if (pop_current_level == FIRST_LEVEL) { /* см. условие выше */ + Kid.x = (uint8_t)(pop_x_bump[(pos % 10) + FIRST_ONSCREEN_COLUMN] + + TILE_SIZEX - 2); + Kid.curr_col = (int8_t)(pos % 10); + } +#endif + /* Спецсобытие «вход падением» (set_start_pos, seg003:0196): на 7-м + * уровне Кид ставится в комнату 17, а экран тут же переводится на + * комнату ПОД ней (`goto_other_room(3)`: y −= 189, ряд пересчитать) — + * он влетает в неё сверху, продолжая падение с шестого уровня. */ + if (pop_current_level == FALLING_ENTRY_LEVEL && + start_room == FALLING_ENTRY_ROOM) { + uint8_t below = pop_room_link(start_room, LINK_DOWN); + if (below) { + Kid.y = (uint8_t)(Kid.y - 189); + Kid.curr_row = pop_y_to_row((int16_t)Kid.y); + enter_room(below); + } + } + Kid.sword = 0; /* set_start_pos: sword_0_sheathed */ + /* Остальные одноразовые флаги того же set_start_pos (seg003:028A). Они + * НЕ косметика: droppedout переживал границу уровня и на следующем + * уровне заставлял стража идти «за упавшим Кидом» вниз + * (guard_follows_kid_down) — на 4-м уровне в комнате 22 страж по этому + * флагу шёл прямо в челюсти, хотя Кида он даже не видел. Взводится он + * на ПРОШЛОМ уровне: Кид сорвался с уступа в боевой позе (кадры + * 150..179, pop_map.c start_fall) — обычная концовка боя со скелетом на + * 3-м. Поймано пользователем 2026-08-24 (переход 3 -> 4 ногами, через + * дверь уровня; читом Shift+L старт чистый, и баг не воспроизводился). */ + pop_droppedout = 0; + offguard = 0; + knock = 0; + /* play_level (seg003:106): have_sword = level >= have_sword_from_level + * (=2); исключение — demo level 0, где меч уже выдан. */ + pop_have_sword = (uint8_t)(pop_current_level == 0 || pop_current_level >= 2); + /* seamless (seg003:158): пришли БЕЗ двери уровня, прямо на бегу — HP не + * трогаем, оно переезжает как есть. Флаг одноразовый: у оригинала его + * гасит заставка «LEVEL N», которую этот же переход и пропускает. */ + if (pop_seamless) { pop_seamless = 0; pop_kid_hp_carry(); } + else pop_kid_hp_reset(); +} + +/* ---- ОТЛАДКА: обход комнат по номеру -------------------------------- * + * + * Нужен, чтобы пройти все комнаты уровня и собрать список багов отрисовки, + * включая недостижимые обычным путём. + * + * Здесь БЫЛ pop_trob_reset() перед enter_room — «чтобы каждая комната + * смотрелась в исходном состоянии». Убран: reset обнуляет room_seen, и + * модификаторы перечитываются из уровня заново, то есть чит ОТМАТЫВАЕТ МИР + * — открытые/закрытые ворота, выдвинутые пики, нажатые кнопки. Навигация + * обязана только телепортировать; исходное состояние даёт перезапуск. */ +uint8_t pop_dbg_roomnav(uint8_t dir) __banked +{ + uint8_t r, i, c, pos = 20, any = 0xFF; + + /* Кид МЁРТВ: сначала честный рестарт уровня, потом телепорт. Навигация + * только переставляет Кида (kid_init + сброс HP), то есть «оживляет» его + * мимо start_level — а тот обязан вернуть тайлы, модификаторы и ОЧИСТИТЬ + * слот соперника (seg003:100: Guard.direction = dir_56_none). Без этого + * после смерти оставался живой скелет рядом с вернувшейся кучей костей. */ + if (pop_kid_dead) pop_start_level(); + + r = cur_room; + r = (dir == 1) ? (uint8_t)(r == 24 ? 1 : r + 1) + : (uint8_t)(r == 1 ? 24 : r - 1); + enter_room(r); + + /* Куда поставить Кида в новой комнате: пол С МАКСИМАЛЬНЫМ Y — сначала + * НИЖНИЙ ряд (2), потом 1, и только потом 0 (пользователь 2026-08-11). + * Раньше брался первый пол подряд, то есть почти всегда (0,0) — верхний + * левый угол: это и «прилёт» в проём/ворота, и, на уровне 5, посадка + * вплотную к тени спецсобытия, которая ждёт в колонке −1 ряда 0. Вместо + * сцены кражи зелья начиналась схватка, которой в этой комнате быть не + * должно. Снизу вверх нужно и самой навигации: в верхнем ряду пола чаще + * нет, и Кид проваливается из комнаты сразу после телепорта. + * + * ИСКЛЮЧЕНИЕ — комната 14 уровня 7 (пользователь 2026-08-12): её проходят + * СВЕРХУ ВНИЗ (с ряда 0 на ряд 2 через зацеп за кромки), и с нижнего ряда + * этот спуск не проверить вовсе. Пол в ряду 0 там есть, так что Кид не + * проваливается. + * + * В каждом ряду сначала ищем ОБЫЧНЫЙ пол, и только если его там нет — + * любой проходимый tile_is_floor (seg001: всё, кроме пустоты, верха + * колонны, верха двери, стены и «половинок» 26..29). */ + { + int8_t row, step = -1, first = 2; + if (pop_current_level == GRAB_DOWN_LEVEL && + r == GRAB_DOWN_ROOM) { first = 0; step = 1; } + for (row = first; row >= 0 && row <= 2 && any == 0xFF; + row = (int8_t)(row + step)) { + uint8_t fallback = 0xFF; + for (i = (uint8_t)(row * 10); i < (uint8_t)(row * 10 + 10); i++) { + c = (uint8_t)(room_fg[i] & 0x1F); + if (c == 1) { any = i; break; } + if (fallback == 0xFF && c && c != 9 && c != 12 && + c != 20 && !(c >= 26 && c <= 29)) fallback = i; + } + if (any == 0xFF) any = fallback; + } + } + if (any != 0xFF) pos = any; + kid_init(SEQ_STAND, (int8_t)(pos % 10), (int8_t)(pos / 10), 0); + pop_kid_hp_reset(); + + /* ВЫХОД ИЗ БОЯ (BUG-CHEAT-FIGHT-1). Телепорт с вынутым мечом заклинивал + * управление намертво: pop_control при Kid.sword == SWORD_2_DRAWN уходит + * в control_with_sword, а там единственный выход — ветка «убрать меч» на + * кадре FRAME_171_STAND_WITH_SWORD. После kid_init(SEQ_STAND) кадр + * обычный, стража в новой комнате нет, swordfight не запускается — и + * диспетчер каждый кадр не делает НИЧЕГО. Это болезнь ЧИТА: в оригинале + * телепорта между комнатами нет, попасть в режим боя без соперника + * нечем. Меч в инвентаре (pop_have_sword) НЕ теряем — только состояние + * схватки. */ + Kid.sword = SWORD_0_SHEATHED; + holding_sword = 0; + offguard = 0; + guard_refrac = 0; + + /* ПРОВАЛ НА СЛЕДУЮЩИЙ УРОВЕНЬ ПО ЧИТУ (та же болезнь, что выше). + * Уровень 12 кончается ФАКТОМ попадания в комнату 23 — двери у него нет + * (pop_seamless, seg000:0900). А навигация перебирает комнаты ПО + * НОМЕРУ, то есть любой обход рано или поздно наступает на 23-ю, и + * уровень молча меняется на 13-й: комнату 23 двенадцатого уровня читом + * не посмотреть в принципе, а прогон «съезжает» на соседний уровень + * незаметно для проверяющего (поймано 2026-08-13 на первом же прогоне). + * Держим триггер, пока Кид не сменит комнату НОГАМИ. */ + pop_nav_hold = 1; + return 1; +} + + +/* ---- ЧИТЫ (seg000: switch под if (cheats_enabled)) -------------------- * + * Переехали из главного цикла в банк 8: дёргаются раз в кадр, то есть + * один трамплин на кадр, а места в резиденте W1/W2 не осталось (звук, + * атлас Тени и пейсинг съели остаток; см. TASKS_OPEN, MEM). + * + * Флаги «фронта» стали статиками модуля — раньше они были локалями main. + * Смысл прежний: одно нажатие = одно срабатывание. + */ +static uint8_t cheat_prev, immo_prev, sound_prev, music_prev, nlvl_prev; +static uint8_t upsd_prev, xdec_prev, xinc_prev, cfg_prev; +static uint8_t time_sub_prev, time_add_prev; + +void pop_cheat_tick(void) __banked +{ + /* Читы (seg000: switch под if (cheats_enabled)). Все — по + * ФРОНТУ нажатия, иначе срабатывали бы каждый кадр. + * K — убить стража; + * I — бессмертие Кида (наш отладочный, в оригинале его нет: + * иначе боёвку проверять получается ровно три раза); + * Ctrl+S — вкл/выкл звук (порт Ctrl+S из SDLPoP, seg000:657). + * НЕ чит: работает и без pop_cheats, как в оригинале. + * Ctrl обязателен — голая S у нас свободна, но менять + * пользовательскую раскладку оригинала незачем; + * Shift+L — следующий уровень (seg000:698, ветка next_level = + * current_level + 1). Единственный вменяемый способ + * тестировать уровни 2..14. NB: L без Shift — отладочный + * «осторожный шаг вправо» (pop_ctrl KBD_DBG_STEPR); с + * Shift шаг тоже пройдёт, но уровень тут же сменится, так + * что конфликта на практике нет; + * F7 / F8 — отнять / добавить минуту. Это точный аналог + * keypad −/+ SDLPoP, но +/- заняты обходом комнат; + * [ / ] — подвинуть Кида на ПИКСЕЛЬ влево/вправо (seg000:1828, + * `--Char.x` / `++Char.x`). Нужен потому, что мост MAME + * теряет нажатия при быстрой отправке, и подогнать Кида в + * нужную позу скриптом нельзя (на это упёрлись BUG-LOOSE-2 + * и BUG-GATE-PASS-1). Пишем прямо в Kid.x: геометрию + * персонажа в этом порте задаёт не диспетчер (грабли + * L1-EXIT). Колонку не трогаем — её пересчитает физика + * следующего кадра. */ + uint8_t k = kbd_raw_down(KBD_CHEAT_KILL); + uint8_t i = kbd_raw_down(KBD_CHEAT_IMMO); + uint8_t s = (uint8_t)(kbd_raw_down(KBD_SOUND_TOGGLE) && + (kbd_raw_down(KBD_LCTRL) || kbd_raw_down(KBD_RCTRL))); + uint8_t m = (uint8_t)(kbd_raw_down(KBD_MUSIC_TOGGLE) && + (kbd_raw_down(KBD_LCTRL) || kbd_raw_down(KBD_RCTRL))); + uint8_t nl = (uint8_t)(kbd_raw_down(KBD_CHEAT_NEXTLVL) && + (kbd_raw_down(KBD_LSHIFT) || kbd_raw_down(KBD_RSHIFT))); + uint8_t up = kbd_raw_down(KBD_CHEAT_UPSIDE); + uint8_t xd = kbd_raw_down(KBD_CHEAT_XDEC); + uint8_t xi = kbd_raw_down(KBD_CHEAT_XINC); + uint8_t ts = kbd_raw_down(KBD_CHEAT_TIME_SUB); + uint8_t ta = kbd_raw_down(KBD_CHEAT_TIME_ADD); + /* C — записать POP.CFG сейчас (отладка MS2, до экрана Settings: + * там сохранять будет Apply/выход). */ + uint8_t cf = kbd_raw_down(0x21); /* C (PS/2 set 2) */ + if (pop_cheats && k && !cheat_prev) pop_guard_kill(); + /* Бессмертие по кругу: 0 выкл -> 1 «только бой» -> 2 «бой и + * мелкий урон» -> 0. Что означают уровни — pop_state.h. */ + if (pop_cheats && i && !immo_prev) + pop_immortal = (uint8_t)(pop_immortal >= 2 ? 0 : pop_immortal + 1); + if (s && !sound_prev) { + pop_sfx_toggle(); /* не под pop_cheats */ + pop_settings.sound_enabled = pop_snd_want; + /* Порт seg000:657: оригинал отвечает строкой в статус-полосе. */ + pop_status_msg = (uint8_t)(pop_snd_want ? POP_MSG_SOUND_ON + : POP_MSG_SOUND_OFF); + } + if (m && !music_prev) { + pop_mus_want ^= 1; /* не под pop_cheats */ + pop_settings.music_enabled = pop_mus_want; + pop_status_msg = (uint8_t)(pop_mus_want ? POP_MSG_MUSIC_ON + : POP_MSG_MUSIC_OFF); + } + if (pop_cheats && nl && !nlvl_prev && pop_current_level < POP_LAST_LEVEL) + pop_next_level = (uint8_t)(pop_current_level + 1); + /* U — переворот экрана (seg000:0793 — чит и в оригинале). + * Тот же путь, что у зелья типа 4: флаг + пометка «перевернулись». */ + if (pop_cheats && up && !upsd_prev) { /* тот же путь, что у зелья */ + pop_upside_want = (uint8_t)!pop_upside_want; + pop_upside_dirty = 1; + } + if (pop_cheats && xd && !xdec_prev) Kid.x = (uint8_t)(Kid.x - 1); + if (pop_cheats && xi && !xinc_prev) Kid.x = (uint8_t)(Kid.x + 1); + if (pop_cheats && ts && !time_sub_prev) pop_timer_cheat_subtract(); + if (pop_cheats && ta && !time_add_prev) pop_timer_cheat_add(); + if (pop_cheats && cf && !cfg_prev) (void)pop_cfg_save(); + cheat_prev = k; + immo_prev = i; + sound_prev = s; + music_prev = m; + nlvl_prev = nl; + upsd_prev = up; + xdec_prev = xd; + xinc_prev = xi; + time_sub_prev = ts; + time_add_prev = ta; + cfg_prev = cf; +} + +/* Отладочная строка в верхнем борте переехала в pop_status.c (банк 9): + * она теперь ТЕКСТОВАЯ, а шрифт живёт там же. Здесь не осталось ничего — + * см. заголовок pop_status.h про то, почему заявка сообщения стоит один + * байт, а весь рендер сидит в банке. */ + +/* Загрузить комнату room в рабочие массивы и отрисовать её фон в ОБЕ + * страницы дабл-буфера (у каждой своя видео-ОЗУ/теневая копия). Оставляет + * активной страницу 1 — вызывающий восстанавливает нужную draw-страницу. */ +/* side — куда ушёл КИД (0 left, 1 right, 2 up, 3 down); 0xFF = «не переход + * за Кидом» (старт уровня, респавн, чит-навигация). От него зависит ровно + * одно: пойдёт ли страж следом (pop_guard_follow, порт exit_room+ + * follow_guard) или останется в прежней комнате. */ +void enter_room_side(uint8_t room, uint8_t side) __banked +{ + uint8_t pg; + /* Страж идёт за Кидом? — решаем ДО того, как что-либо загрузим: функции + * нужны и старая комната (Guard.room), и целевая. Пошёл — ни leave, ни + * enter звать нельзя: он уже перенесён и живёт дальше как есть. */ + uint8_t follow = (uint8_t)(side <= 3 ? pop_guard_follow(room, side) : 0); + if (!follow) pop_guard_leave(); /* запомнить стража уходящей комнаты */ + /* ...и так же запомнить НЕДОСЧИТАННЫЕ loose: пока карта ещё описывает + * СТАРУЮ комнату, фаза уезжает в персистентный room_modif + trob, и + * плита доваливается заочно (см. pop_loose_leave_room / animate_loose). */ + pop_loose_leave_room(); + uint8_t *room_mod; + pop_room_load(room, room_fg, room_bg, lcol_fg, lcol_bg, rcol_fg, rcol_bg, below_fg); + pop_map_set(room_fg); + /* Соседей слева/справа карта забирает из уровня САМА (целиком, см. + * pop_map_set_edges) — отдельной копии в приложении нет. */ + pop_map_set_edges(pop_room_link(room, 0), pop_room_link(room, 1), + pop_room_link(room, 2), pop_room_link(room, 3)); + /* Боковой переход: колонки сдвинулись ровно на 10 (x ∓140), флаги + * перекрытия при этом инвариантны — историю перенумеровываем, а не + * теряем. Без этого решётка шва не бампит на кадре входа и Кид с + * разбега проходит сквозь закрытые ворота (BUG-GATE-PASS-1). */ + if (side == 0) pop_coll_shift(+10); /* ушёл ВЛЕВО: col += 10 */ + else if (side == 1) pop_coll_shift(-10); /* ушёл ВПРАВО: col −= 10 */ + pop_room_set_below(below_fg); + /* Ряд 2 верхнего соседа → полоса кладки у потолка (row0). fg — статика + * уровня, mod — живой room_modif (ленивая инициализация). */ + { + uint8_t up = pop_room_link(room, 2), c; + if (up) { + uint8_t *um = pop_trob_modif(up); + pop_level_access_begin(); + for (c = 0; c < 10; c++) above_fg[c] = pop_level_tile_raw(up, (uint8_t)(20 + c)); + pop_level_access_end(); + for (c = 0; c < 10; c++) above_mod[c] = um[20 + c]; + pop_room_set_above(above_fg, above_mod); + pop_map_set_above(above_fg); /* плита-потолок: пробой снизу + провал */ + } else { + pop_room_set_above(0, 0); /* нет комнаты сверху → пол (кромка) */ + pop_map_set_above(0); + } + } + pop_map_set_room(room); + /* Kid.room — как в оригинале: «комната персонажа». Полная модель + * (Kid.room ≠ отрисованной у шва) ещё не портирована, но сравнение + * Kid.room == Guard.room нужно уже сейчас: без него луч видимости + * стража никогда не срабатывает. */ + Kid.room = room; + pop_loose_reset(); /* loose-состояние не тянем из старой комнаты */ + pop_trob_room_changed(); /* отложенные перерисовки пик — не из старой комнаты */ + pop_bake_forget(); /* и слоты «первая запечка была на странице N» */ + pop_trob_anim_room(room); /* факелы/зелья: стартовая фаза + trob */ + pop_start_chompers(room, Kid.curr_row); /* seg000:1288: чомперы РЯДА Кида */ + pop_check_fall_flo(); /* спецсобытие ур.13: гряда плит сверху */ + /* check_shadow (seg002:0064) стоит ПЕРЕД enter_guard и на «шадоу»- + * уровнях забирает слот соперника себе: вернула 1 — стража поднимать + * НЕЛЬЗЯ (в комнате 24 уровня 5 он есть в данных, но оригинал его туда + * не пускает). Порядок ровно как в оригинале, не «сначала страж, потом + * заменим». */ + if (!follow && !pop_check_shadow(room)) + pop_guard_enter(room); /* страж комнаты (порт enter_guard) */ + pop_hp_invalidate(); /* фон перерисован — полоса HP тоже */ + /* Модификаторы тайлов (openness ворот, состояние пик) — ПЕРСИСТЕНТНЫЕ + * per-room (room_modif живёт между входами); рисуем и коллидируем по ним, + * а не по свежему backtable. Фаза P0. */ + room_mod = pop_trob_modif(room); + /* Модификаторы швов — из ЖИВОГО room_modif соседей (openness ворот, + * видимых через шов, персистентен: ворота room8(0,9) видны в col0 room6). + * pop_room_load заполнил lcol_bg/rcol_bg статикой уровня — перекрываем. */ + { + uint8_t lft = pop_room_link(room, 0), rgt = pop_room_link(room, 1); + if (lft) { uint8_t *m = pop_trob_modif(lft), i; + for (i = 0; i < 3; i++) { + lcol_bg[i] = m[9 + i * 10]; + /* шов рисуется в обе страницы ниже — синхр. сигнатуру */ + seam_sig[i] = seam_row_sig(m, lcol_fg, i); + } } + if (rgt) { uint8_t *m = pop_trob_modif(rgt); + rcol_bg[0] = m[0]; rcol_bg[1] = m[10]; rcol_bg[2] = m[20]; } + } + seam_redraw = 0; seam_rows = 0; /* шов свежий на обеих страницах — редрой не нужен */ + /* ENTER-ROOM-FAST: комната рисуется ОДИН раз — в скрытую страницу, — и + * обычно тут же показывается флипом; вторая страница дабл-буфера получает её + * КОПИЕЙ акселератора (gfx_copy_page, ~384 burst-строки против почти + * полумиллиона тактов на вторую отрисовку). Побочный эффект важнее + * тактов: игрок больше не видит, КАК рисуются тайлы — только готовый + * кадр. + * + * Копия идёт банком 0x50, то есть обновляет и VRAM, и ОЗУ-копию + * страницы-приёмника — иначе heal на ней восстанавливал бы фон прошлой + * комнаты (инвариант gfx_copy_page, регресс tests/pageflip). + * + * Видимая страница здесь МЕНЯЕТСЯ, поэтому главный цикл перечитывает её + * в начале каждого кадра (front = gfx_get_visible_page()), а не ведёт + * свой счёт. */ + { + gfx_rect_t full; + pg = (uint8_t)(gfx_get_visible_page() ^ 1); /* скрытая */ + gfx_set_draw_page(pg); + pop_screen_fill_field(); + pop_room_draw(room, room_fg, room_mod, lcol_fg, lcol_bg); + if (level_prepare) { + /* Межуровневый staging: пока pop_pal_level_load последовательно + * пишет KID.PAL и env/wall, видима нулевая чёрная страница. + * Показывать эту комнату разрешено только после финальной + * палитры — иначе level 4 успевает мелькнуть цветами level 3. */ + level_prepared_page = pg; + } else { + gfx_set_visible_page(pg); /* показать готовое */ + gfx_set_draw_page((uint8_t)(pg ^ 1)); + full.x = 0; full.y = 0; full.w = 320; full.h = 256; + gfx_copy_page(&full, GFX_COPY_DIRECT); /* вторую — копией */ + } + } + cur_room = room; + kid_room = room; /* S1: комната Kid синхронна отрисованной (S3 разведёт) */ + pop_leave_timer = 2; /* exit_room_timer: 2 кадра блокировки leave (анти-ping-pong/#4) */ +} + +/* Вход в комнату БЕЗ перехода за Кидом: старт уровня, респавн, чит-обход. + * Страж такой смены не «видит» — он остаётся в своей комнате. */ +void enter_room(uint8_t room) __banked { enter_room_side(room, 0xFF); } + +void pop_qsave_restore_room(uint8_t room) __banked +{ + uint8_t i, pg, *room_mod; + gfx_rect_t full; + uint8_t lft, rgt, up; + + pop_room_load(room, room_fg, room_bg, lcol_fg, lcol_bg, + rcol_fg, rcol_bg, below_fg); + pop_map_set(room_fg); + lft = pop_room_link(room, 0); rgt = pop_room_link(room, 1); + up = pop_room_link(room, 2); + pop_map_set_edges(lft, rgt, up, pop_room_link(room, 3)); + pop_room_set_below(below_fg); + if (up) { + uint8_t *um = pop_trob_modif(up); + pop_level_access_begin(); + for (i = 0; i < 10; i++) + above_fg[i] = pop_level_tile_raw(up, (uint8_t)(20 + i)); + pop_level_access_end(); + for (i = 0; i < 10; i++) above_mod[i] = um[20 + i]; + pop_room_set_above(above_fg, above_mod); + pop_map_set_above(above_fg); + } else { + pop_room_set_above(0, 0); pop_map_set_above(0); + } + pop_map_set_room(room); + pop_loose_reset(); + pop_trob_room_changed(); + pop_bake_forget(); + pop_loose_mob_room_changed(); + room_mod = pop_trob_modif(room); + if (lft) { + uint8_t *m = pop_trob_modif(lft); + for (i = 0; i < 3; i++) { + lcol_bg[i] = m[9 + i * 10]; + seam_sig[i] = seam_row_sig(m, lcol_fg, i); + } + } + if (rgt) { + uint8_t *m = pop_trob_modif(rgt); + rcol_bg[0] = m[0]; rcol_bg[1] = m[10]; rcol_bg[2] = m[20]; + } + seam_redraw = seam_rows = 0; + pg = (uint8_t)(gfx_get_visible_page() ^ 1); + gfx_set_draw_page(pg); + pop_screen_fill_field(); + pop_room_draw(room, room_fg, room_mod, lcol_fg, lcol_bg); + gfx_set_visible_page(pg); + gfx_set_draw_page((uint8_t)(pg ^ 1)); + full.x = 0; full.y = 0; full.w = 320; full.h = 256; + gfx_copy_page(&full, GFX_COPY_DIRECT); + cur_room = room; kid_room = Kid.room; + /* Обе страницы теперь содержат только свежий фон. Старые габариты + * персонажей относятся к кадру до QuickLoad и не должны запускать heal + * поверх восстановленной комнаты. */ + pop_cd_forget(); + pop_cd_init(); + pop_hp_invalidate(); + /* Полоса состояния тоже переписана заново — «QUICKLOAD» обязан лечь + * поверх свежего фона на ОБЕИХ страницах, иначе он виден через кадр и + * мигает. */ + pop_status_invalidate(); +} + +/* start_level (seg003:0055) + do_startpos (seg003:01A3) + set_start_pos + * (seg003:028A) — поставить Кида в стартовую позицию УРОВНЯ. Один вход и + * для запуска, и для рестарта после смерти / выпадения из уровня: в + * оригинале это буквально один и тот же путь (play_level_2 зовёт + * start_level заново), поэтому и у нас не три копии четырёх строк. + * + * Позиция берётся ИЗ ДАННЫХ уровня (pop_level_start_*), а не из #define: + * start_pos — тайл 0..29 (row*10+col), start_dir — направление, в котором + * персонаж должен ОКАЗАТЬСЯ. + * + * Направление ИНВЕРТИРУЕТСЯ (`Char.direction = ~level.start_dir`): персонаж + * ставится спиной и доворачивается кадрами позы входа. Сама поза — из + * tbl_entry_pose (data.h:848 = {0,1,0,…,2,…}): + * 1 (уровень 1) — падение внутрь (seq_7_fall) + нажатие кнопки room5(0,2); + * это оно захлопывает решётку за спиной в первой комнате; + * 2 (уровень 13) — вбегание (seq_84_run); + * иначе — разворот на месте (seq_5_turn). + * Таблица индексируется НОМЕРОМ УРОВНЯ — pop_level_entry_pose(). */ +#define SEQ_TURN 5 +#define SEQ_FALL 7 +#define SEQ_RUN 84 +#define ENTRY_POSE_FALL 1 /* tbl_entry_pose[1] */ +#define ENTRY_POSE_RUN 2 /* tbl_entry_pose[13] */ +#define TILE_LEVELDOOR_LEFT 16 /* tiles_16_level_door_left */ + + +/* ---- Порядок «Kid vs страж» (BUG-DRAWORDER-1) ----------------------- + * + * В оригинале никто не «поверх» по определению: и Кид, и страж попадают в + * midtable при обработке СВОЕГО тайла (set_objtile_at_char, seg006:13F3), а + * тайлы обходятся в фиксированном порядке — redraw_needed_tiles (seg008:1B06) + * идёт рядами 2,1,0 и внутри ряда колонками 0..9. Кто нарисован ПОЗЖЕ — тот + * поверх. Отсюда «Кид проходит ЗА телом стража»: тело лежит правее и/или + * выше, его тайл в обходе позже. Совпали тайлы — решает sort_curr_objs + * (seg008:203C): у обычных объектов (не тень, не плита) ниже по obj_y = позже. + * + * Мы рисовали страж→Кид всегда, то есть Кид был поверх безусловно. + * + * Ключ обхода: чем больше, тем позже. Ряды идут 2,1,0 → (2 - row). */ +#define OBJ_ORDER_KEY(row, col) ((int)(2 - (row)) * 10 + (int)(col)) + +/* get_tile_div_mod (seg006:750): колонка комнаты по X (БЕЗ поправки −7). */ +static int8_t tile_div_mod(int x) +{ + int v = x - 58; /* pop_x_bump[FIRST_ONSCREEN_COLUMN] */ + int c = v / TILE_SIZEX; + if (v % TILE_SIZEX < 0) --c; + return (int8_t)c; +} + +/* char_x_left из set_char_collision (seg006:0723) — левая граница габарита. + * ВАЖНО: величина НЕ утоньшённая; поправку THIN оригинал применяет только к + * *_coll, а колонки char_col_left/right считает по этой. */ +static int char_x_left_of(uint8_t x, int8_t dir, int8_t dx, + uint8_t flags, uint16_t w) +{ + int e = (int)x + ((dir < 0) ? -dx : dx); /* char_dx_forward */ + uint8_t fl = flags; + if (dir < 0) fl ^= 0x80; + /* odd-pixel + усечение деления к нулю — та же поправка, что в pop_map */ + if (!(fl & 0x80) && e <= 57) e++; + if (dir >= 0) e -= (int)((w + 1) >> 1); /* char_width_half */ + return e; +} + +/* set_objtile_at_char (seg006:13F3) — ТАЙЛ ОБЪЕКТА персонажа. + * + * Ветка actions_1_run_jump обязательна, а не «упрощаемая» (как я решил + * сначала): в беге оригинал берёт НЕ curr_row/curr_col, а нижний ряд и ЛЕВУЮ + * колонку ГАБАРИТА. При беге вправо левая колонка меньше curr_col, поэтому + * бегущий Кид уходит ЗА объекты справа от себя. Без этой ветки часть кадров + * бега рисовала Кида поверх лежащего стража. Считаем одинаково для обоих: + * enter_guard (seg002) тоже ставит стражу action = 1, и односторонний учёт + * дал бы перекос в другую сторону. */ +static void objtile_at_char(int8_t *row, int8_t *col, + const pop_char_t *ch, int8_t dx, int8_t dy, + uint8_t flags, uint16_t w) +{ + int8_t r = ch->curr_row, c = ch->curr_col; + if (ch->action == 1) { /* actions_1_run_jump */ + int8_t br = pop_y_to_row((int16_t)((int)ch->y + dy)); + if (br == -1) br = 3; /* char_bottom_row */ + r = br; + c = tile_div_mod(char_x_left_of(ch->x, ch->direction, dx, flags, w)); + if (c < 0) c = 0; /* MAX(char_col_left, 0) */ + } + if ((ch->frame >= 135 && ch->frame < 149) || + ch->action == 2 || ch->action == 3 || + ch->action == 4 || ch->action == 6) c--; + *row = r; *col = c; +} + +/* ВРЕМЕННО: трасса kidobj — порт отладочного вывода, добавленного + * пользователем в SDLPoP add_kid_to_objtable (seg008:1679): + * + * DBG kidobj tilepos=%d frame=%d act=%d col=%d cols=%d..%d rows=%d..%d + * + * Считаем ровно то же, что set_char_collision (seg006:0723) + + * set_objtile_at_char (seg006:13F3): габарит НЕутоньшённый (THIN правит + * только *_coll, а колонки берутся по «толстой» границе), колонки + * клампятся в 0..9, нижний ряд −1 читается как 3. Звать ПОСЛЕ отрисовки + * Кида: ширину/высоту кадра (fpw/fph) заполняет она. + * + * Вывод забирает брейкпоинт MAME на резидентном pop_dbg_trap — сама + * функция банковая, её адрес в W3 неоднозначен. */ +void pop_dbg_kidobj(void) __banked +{ + int8_t dx = kid_cur_dx(), dy = kid_cur_dy(); + uint8_t fl = kid_cur_flags(); + uint16_t w = pop_cd[POP_CH_KID].fpw; + uint16_t h = pop_cd[POP_CH_KID].fph; + int xl = char_x_left_of(Kid.x, Kid.direction, dx, fl, w); + int xr = xl + (int)((w + 1) >> 1); /* + char_width_half */ + int oy = (int)Kid.y + dy; + int ty = oy - (int)h + 1; + int8_t r, c, tr, br, cl, cr; + objtile_at_char(&r, &c, &Kid, dx, dy, fl, w); + br = pop_y_to_row((int16_t)oy); + if (br == -1) br = 3; + if (ty >= 192) ty = 0; + tr = pop_y_to_row((int16_t)ty); + cl = tile_div_mod(xl); if (cl < 0) cl = 0; + cr = tile_div_mod(xr); if (cr > 9) cr = 9; + pop_dbg_obj[0] = (int8_t)(r * 10 + c); + pop_dbg_obj[1] = (int8_t)Kid.frame; + pop_dbg_obj[2] = (int8_t)Kid.action; + pop_dbg_obj[3] = Kid.curr_col; + pop_dbg_obj[4] = cl; + pop_dbg_obj[5] = cr; + pop_dbg_obj[6] = tr; + pop_dbg_obj[7] = br; + pop_dbg_obj[8] = (int8_t)cur_room; + pop_dbg_trap(); +} + +/* 1 = страж рисуется ПОСЛЕ Кида (поверх). __banked: зовётся раз в кадр, + * то есть один трамплин — а 500 Б кода в резиденте дороже. */ +uint8_t guard_over_kid(void) __banked +{ + int8_t kr, kc, gr, gc; + int kk, gk; + if (Guard.charid == 0 || Guard.room != cur_room) return 0; + objtile_at_char(&kr, &kc, &Kid, kid_cur_dx(), kid_cur_dy(), + kid_cur_flags(), pop_cd[POP_CH_KID].fpw); + objtile_at_char(&gr, &gc, &Guard, pop_gframe.dx, pop_gframe.dy, + pop_gframe.flags, pop_cd[POP_CH_OPP].fpw); + kk = OBJ_ORDER_KEY(kr, kc); + gk = OBJ_ORDER_KEY(gr, gc); + if (gk != kk) return (uint8_t)(gk > kk); + /* один тайл — ниже по obj_y рисуется позже (sort_curr_objs) */ + return (uint8_t)((int)Guard.y + pop_gframe.dy > pop_cd[POP_CH_KID].fpy); +} + + +/* ---- Переворот уже нарисованного (зелье инверсии, уровень 9) --------- * + * + * Порт need_redraw_because_flipped (seg000:15E9 + 939/946). Оригинал + * держит готовый offscreen и переворачивает его на выводе; у нас страниц + * ровно две, рабочей третьей нет, поэтому переворачиваем то, что уже + * нарисовано, — построчной копией акселератора (gfx_copy_page). + * + * Почему это законно и почему heal после не ломается: accel ЧИТАЕТ + * ОЗУ-копию, значит копируется ЧИСТЫЙ ФОН без персонажей, а пишет банком + * 0x50 — и в видео, и в копию приёмника. После двух проходов обе страницы + * дабл-буфера содержат перевёрнутый фон в обеих плоскостях, и heal + * восстанавливает уже его (memory accel_port_y_between_triggers, + * регресс — tests/pageflip). + * + * Порядок: перевернуть в СКРЫТУЮ, показать её, скопировать оттуда во + * вторую (теперь скрытую) — прямой копией. Так игрок видит результат + * сразу, а не после второго прохода. + * + * Переворачиваем ТОЛЬКО игровое поле (POP_PLAYFIELD_H): полоса HP лежит + * ниже него и в оригинале тоже не переворачивается. */ +void pop_flip_screen(void) __banked +{ + gfx_rect_t area; + uint8_t front = gfx_get_visible_page(); + uint8_t back = (uint8_t)(front ^ 1); + uint8_t i; + + area.x = 0; area.y = POP_YOFF; + area.w = 320; area.h = POP_PLAYFIELD_H; + + /* СНАЧАЛА убрать из фона то, что в нём запечено, но фоном не является: + * пламя факелов (pop_torch_wipe объясняет, почему оно вообще там). + * Отражённое вместе с фоном, оно оседало в зеркальных позициях, где его + * никто не перерисовывает — по комнате оставались лишние языки огня + * (нашёл пользователь на глотке зелья). + * + * Чистим ОБЕ страницы: источник отражения — видимая, а вторая получит + * прямую копию уже чистой картинки. Дешевле полной перерисовки комнаты + * на порядок: 0..4 тайла против 6.2 млн тактов (14.5 кадра, замер MAME + * 2026-08-12) — а копия отражения стоит всего 2.1 кадра. */ + /* ВНИМАНИЕ на систему координат: pop_upside к этому моменту УЖЕ + * переключён (главный цикл делает это перед вызовом), а картинка на + * экране нарисована ещё в СТАРОЙ. Чистить её надо по старым + * координатам, иначе чёрный бар ложится в зеркальное место — на экране + * оставался чёрный квадрат, а пламя так и не стиралось. */ + pop_upside = (uint8_t)!pop_upside; /* временно: система ЭКРАНА */ + gfx_set_draw_page(front); + for (i = 0; i < 30; i++) { + uint8_t t = room_fg[i]; + if (t == 19 || t == 30) /* факел / факел с щебнем */ + pop_torch_wipe(i / 10, i % 10); + } + pop_upside = (uint8_t)!pop_upside; /* вернуть новое состояние */ + + gfx_set_draw_page(back); + gfx_copy_page(&area, GFX_COPY_VFLIP); /* источник — видимая страница */ + gfx_set_visible_page(back); + + gfx_set_draw_page(front); + gfx_copy_page(&area, GFX_COPY_DIRECT); /* вторую страницу — прямой копией */ + + /* Всё, что рисуется поверх фона, надо положить заново: прошлые + * прямоугольники персонажей относятся к СТАРОЙ (неперевёрнутой) + * картинке, и heal по ним стёр бы не то место. */ + pop_cd_forget(); + POP_CD_TOUCH_ALL(); /* фон трогали целиком */ + pop_hp_invalidate(); /* полосу HP перерисовать */ +} + +/* ---- Запуск, смена уровня, завершение ------------------------------- * + * Три куска главного цикла, которые отрабатывают раз за игру (или раз на + * уровень). В резиденте от них оставались только вызовы — сам код здесь. + */ + +int pop_boot(void) __banked +{ + /* Настройки: дефолты, затем POP.CFG, если он есть и корректен. */ + pop_settings_defaults(); + pop_cfg_load(); + pop_timer_new_game(); /* default 60:00, Settings may freeze it */ + + /* Экран Sprinter хранит VRAM и палитры между программами. Перейти в + * графику надо ДО долгих чтений, но первой же операцией погасить ОБЕ + * палитры: тогда ни старый экран, ни частично собранный первый уровень + * не могут мелькнуть перед первым title fade-in. */ + /* PIC-палитры графики живут и в text mode. Предзапись чёрного сокращает + * окно между аппаратным SETVMOD и первой строкой нашего кода, в котором + * иначе успевает мелькнуть VRAM предыдущей программы. */ + pop_pal_black(); + initgraph_black(); + /* initgraph_black уже обнулил VRAM до SETVMOD. Повторная заливка после + * него фиксирует и shadow-копии, откуда затем работают heal/page-copy. */ + gfx_set_bank(GFX_BANK_NORMAL); + setfillstyle(SOLID_FILL, BLACK); + gfx_set_draw_page(0); + bar(0, 0, 319, 255); + gfx_set_draw_page(1); + bar(0, 0, 319, 255); + gfx_set_visible_page(0); + pop_pal_black(); + + /* Набор фона — по типу СТАРТОВОГО уровня; atlas_load безопасен и после + * initgraph: при первом вызове он сохраняет страницу DSS W0. Уровень к + * этому моменту ещё не прочитан, поэтому тип берём напрямую от номера. */ + pop_current_level = FIRST_LEVEL; + if (pop_bg_load(pop_level_type()) != 0) { + puts("pop_bg_load failed"); + return -1; + } + /* pop_bg_load установил только свои слоты палитры. До конца загрузки + * всё равно держим экран чёрным. */ + pop_pal_black(); + pop_cd_init(); /* метка «фон трогали» — пустые диапазоны */ + /* pop_cheats/speed применит pop_settings_apply после pop_sfx_init. */ + pop_guard_reset(); + if (pop_kid_data_load("KID\\kid.ani") != 0) { /* кадры+seqtbl в EMM-странице */ + printf("kid.ani load failed\n"); + return -1; + } + pop_guard_load(); /* атласы стража (GUARD\\g0..g4) */ + pop_shadow_load(); /* запечённый набор Тени — один раз на запуск */ + pop_sfx_init(); /* звук: 8 страниц оцифровки (вывод пока молчит) */ + pop_settings_apply(1); /* CFG -> speed/cheats/CBL */ + if (pop_kid_load(28) != 0) { /* ВСЕ страницы: бег+стоп+разворот+присед+прыжок */ + puts("kid atlas load failed"); + return -1; + } + if (pop_level_load_num(FIRST_LEVEL) != 0) { /* сырой уровень в EMM-страницу */ + puts("level load failed"); + return -1; + } + pop_next_level = pop_current_level; /* current_level = next_level (load_lev_spr) */ + pop_kid_hp_newgame(); /* init_game: hitp_beg_lev = start_hitp */ + pop_checkpoint = 0; /* init_game (seg003:37) */ + + /* FONT.ATL нужен palette snapshot/dim; загрузка идёт ещё на чёрной + * палитре и поэтому не открывает игроку старое содержимое VRAM. */ + if (pop_menu_init() != 0) { + puts("menu font atlas load failed"); + return -1; + } + /* kid.pal + динамические slots tileset/Shadow — один логический + * источник. pop_pal_game_load делает снимок ПОСЛЕ всех доплат. */ + pop_pal_game_load(); + /* Сохраняем ИМЕННО игровую палитру: после стартового title/intro она + * понадобится как источник обычного fade-in. Для build-info экран + * остаётся штатный яркий игровой фон. */ + if (!pop_settings.show_build_info) { + pop_pal_apply(POP_PAL_DIM_BLACK); + } + + /* Стартовая комната до title НЕ НУЖНА: LEVEL_LOAD всё равно заново + * выполняет pop_level_switch. Если нарисовать её сейчас в обе страницы, + * она становится сохранённой VRAM и может мелькнуть перед первым title + * при следующем запуске приложения. Единственное исключение — явно + * включённый build-info: его фон по контракту является игровой сценой. */ + if (pop_settings.show_build_info) { + pop_start_level(); + if (pop_current_level == POP_UPSIDE_LEVEL) pop_vflip_load_all(); + gfx_set_draw_page(0); + pop_char_draw(POP_CH_KID); + pop_char_fore(POP_CH_KID); + } + /* Звук включаем ПОСЛЕДНИМ действием инициализации — когда все атласы, + * уровень и палитры уже прочитаны. Раньше нельзя: ESTEX уходит в диск + * надолго, насос не успевает долить блок, и на слух это скрежет + * (pop_sfx.h). Парная к pop_sfx_pause в pop_level_switch. */ + pop_sfx_start(); + return 0; +} + +/* Порт play_level_2 (seg003:0386..0410) + play_level (seg003:81): цикл + * уровня кончается, когда next_level разошёлся с current_level (END_LEVEL + * после seq_70 в двери уровня либо чит Shift+L), и play_level грузит + * следующий. Возврат -1 — фатально (не удалось сменить набор фона). */ +int pop_level_switch(void) __banked +{ + uint8_t old_page; + gfx_rect_t full; + + /* Граница FG0: повреждённый snapshot или старый внешний вызов не должен + * даже пытаться открыть res2015.bin. Финальный маршрут 14 -> ENDING + * добавит автомат приложения (FG11); пока просто оставляем уровень. */ + if (pop_next_level > POP_LEVEL_LAST) { + pop_next_level = pop_current_level; + return 0; + } + + /* Порт play_level_2 (seg003:0386..0410) + play_level (seg003:81): + * цикл уровня кончается, когда next_level разошёлся с + * current_level (END_LEVEL после seq_70 в двери уровня либо чит + * Shift+L), и play_level грузит следующий. + * + * HP переносится: hitp_beg_lev = hitp_max ДО загрузки — так + * поднятая на уровне «жизнь» (большая склянка) переезжает + * дальше, а урон — нет (do_startpos дольёт до потолка). */ + hitp_beg_lev = hitp_max; + pop_checkpoint = 0; /* уровень пройден (seg003:413) */ + pop_sfx_pause(); /* загрузка уровня — ESTEX надолго, звук глушим */ + if (pop_level_load_num(pop_next_level) != 0) { + pop_next_level = pop_current_level; /* файла нет — остаёмся */ + } else { + /* С этого места видим только индекс 0. Это одновременно + * скрывает дисковую подготовку и даёт безопасный экран, + * пока loaders физически меняют отдельные диапазоны palette. */ + pop_pal_black(); + old_page = gfx_get_visible_page(); + gfx_set_bank(GFX_BANK_NORMAL); + /* Обнуляем ОБЕ страницы, а не только software-visible: + * аппаратный SCREEN_SWITCH и сохранённое BGI-состояние не + * должны оставлять старые sprites на соседней странице. */ + gfx_set_draw_page(0); + pop_screen_fill_game(); + gfx_set_draw_page(1); + pop_screen_fill_game(); + pop_wait_edge(); + /* Набор спрайтов соперника у каждого уровня СВОЙ + * (load_lev_spr, seg000:1092): уровень 3 — скелет, у него и + * кадров меньше, чем у стража. Без перезагрузки соперник + * рисуется чужим атласом: часть индексов уходит за его + * пределы, и персонаж просто не виден. */ + pop_guard_free(); + pop_guard_load(); + /* guard loader применяет свои цветовые слоты физически; + * пока остальной уровень читается, экран обязан быть чёрным. */ + pop_pal_black(); + /* Зеркальные копии атласов сделаны из СТАРЫХ страниц — + * после перезагрузки спрайтов они мусор (pop_vflip.h). */ + pop_vflip_reset(); + /* Тайлсет: подземелье <-> дворец (порт tbl_envir_ki, + * seg000:1108). Внутри — no-op, если тип не менялся; + * при смене выгружает старые атласы и правит 32 записи + * палитры. Только на границе уровня, не в кадре. */ + if (pop_bg_load(pop_level_type()) != 0) { + puts("bg set load failed"); + return -1; + } + /* Смена tileset тоже пишет env/wall slots. Не открывать их + * до готовности обеих страниц нового уровня. */ + pop_pal_black(); + /* Всё состояние прошлого уровня сносит сам pop_start_level: + * pop_trob_reset обнуляет room_seen (модификаторы комнат + * перечитаются из НОВОЙ страницы), тайлы/стражи приезжают + * из неё же, loose чистит enter_room. */ + level_prepare = 1; + pop_start_level(); + level_prepare = 0; + /* pop_bg_load мог заменить только env/wall, а до этого + * экран жил после fade на чёрной палитре заставки. Полный + * game source обязателен даже для dungeon->dungeon: иначе + * fade_in восстановит старый (либо уже чёрный) snapshot. + * Яркость caller выберет сам: gameplay может показать кадр + * сразу, lifecycle title/demo — сначала apply(4)+fade_in. */ + /* Уровень с зельем инверсии — заранее поднять зеркальный + * набор персонажей (pop_vflip.h): 34 страницы читаются ~1.5 с, + * и делать это после открытия палитры либо в момент глотка + * нельзя. Звать можно откуда угодно — функция идемпотентна. */ + if (pop_current_level == POP_UPSIDE_LEVEL) pop_vflip_load_all(); + /* Последнее действие подготовки: собрать полный logical + * source новой палитры. Пока он собирается физическими + * записями, видимая old_page заполнена нулём. */ + pop_pal_level_load(1); + /* Атомарный commit на границе растра: впервые показать + * готовую комнату уже с окончательной палитрой уровня. + * Вторую страницу синхронизировать аппаратной копией. */ + pop_wait_edge(); + gfx_set_visible_page(level_prepared_page); + gfx_set_draw_page(old_page); + full.x = 0; full.y = 0; full.w = 320; full.h = 256; + gfx_copy_page(&full, GFX_COPY_DIRECT); + /* Уровень готов и показан — заявить «LEVEL N» (порт + * show_level из draw_level_first, seg003:222). */ + pop_status_level(); + } + /* CBL намеренно НЕ открываем здесь. После загрузки caller ещё + * может делать BIOS fade/palette либо восстанавливать QuickSave; + * открытый буфер за это время успевает повторить старую половину + * и даёт скрежет. Caller запускает звук последним действием. */ + return 0; +} + +/* Общий холодный маршрут title/intro/credits/demo -> настоящая игра. + * Держим его в банке 8: это разовый lifecycle-код, а не часть кадрового + * пути, и перенос возвращает резиденту W1 место под стек/heap. */ +int pop_new_game_load(void) __banked +{ + /* Клавиша, которой пропустили заставку/demo, не должна стать первым + * движением Кида или тут же открыть pause menu на уровне. */ + while (kbd_raw_any_down()) { + kbd_raw_sync(); + gfx_wait_vsync(); + } + + pop_kid_hp_newgame(); + hitp_max = POP_START_HITP; + pop_timer_new_game(); + pop_checkpoint = 0; + pop_next_level = FIRST_LEVEL; + + /* Title мог завершиться на яркой title-палитре. Сначала погасить её, + * затем восстановить полный game snapshot; level_switch собирает обе + * страницы уже невидимо, и первый игровой кадр проявляется. */ + pop_pal_apply(POP_PAL_DIM_BLACK); + gfx_set_draw_page(0); + gfx_set_bank(GFX_BANK_NORMAL); + pop_screen_fill_game(); + gfx_set_draw_page(1); + pop_screen_fill_game(); + gfx_set_visible_page(1); + pop_pal_game_load(); + pop_pal_apply(POP_PAL_DIM_BLACK); + if (pop_level_switch() != 0) return -1; + /* Первый уровень появляется сразу, без fade, но только после того как + * обе страницы и его собственная палитра полностью готовы. */ + pop_pal_apply(0); + (void)pop_sfx_start(); + return 0; +} + +void pop_shutdown(void) __banked +{ + gfx_set_idle_hook(0); /* снять хук ДО закрытия raw-канала */ + /* ЗВУК ГАСИМ ПЕРВЫМ, до closegraph и освобождения страниц. Дальше идут + * ESTEX-вызовы (mem_free_block на каждый атлас), а они уходят надолго — + * насос не успевает долить очередной блок, и железо доигрывает по кругу + * несвежую половину буфера. Замер записи MAME: на выходе по ESC ровно + * 11 мс шума на полной громкости, то есть ОДИН пропущенный блок (128 + * сэмплов = 11,7 мс). Та же причина, по которой звук глушится на время + * загрузки уровня (pop_sfx.h). */ + pop_sfx_close(); + pop_ctrl_close(); + closegraph(); + pop_bg_free(); + pop_kid_free(); + pop_shadow_free(); + pop_menu_free(); + pop_guard_free(); + pop_kid_data_free(); + pop_level_free(); +} diff --git a/applications/SprPoP/src/sprpop_cold.h b/applications/SprPoP/src/sprpop_cold.h new file mode 100644 index 0000000..98e1f08 --- /dev/null +++ b/applications/SprPoP/src/sprpop_cold.h @@ -0,0 +1,121 @@ +/* + * sprpop_cold.h — контракт между главным циклом (резидент W1/W2) и его + * ХОЛОДНОЙ половиной, вынесенной в банк (sprpop_cold.c). + * + * Зачем. В режиме huge под кучу остаётся то, что не занял _CODE+_DATA, и к + * уровню 8 запас упал ниже килобайта. Главный цикл при этом наполовину + * состоит из кода, который отрабатывает РАЗ НА УРОВЕНЬ (старт/рестарт) или + * вообще только по отладочной клавише (обход комнат, лейбл номера) — держать + * его в дефицитном окне незачем. Кадровый путь (tick/draw/флип) остался в + * резиденте целиком: цена выноса — один трамплин на вызов, а он тут ноль раз + * за обычный кадр. + * + * Что ОСТАЛОСЬ в sprpop.c и почему: enter_room_side (её зовут и холодная + * половина, и переходы между комнатами в кадре падения) и рабочие массивы + * комнаты — они же и есть то состояние, ради которого функция вообще + * существует; банковый модуль читает их как обычные глобалы (данные банков + * линкуются в общий _DATA, --bank-data мы не включаем). + */ +#ifndef ROOMTEST_COLD_H +#define ROOMTEST_COLD_H + +#include + +/* ---- то, что холодная половина берёт у главного цикла ---------------- */ + +/* Рабочие массивы комнаты: заполняет вход в комнату (банк), читают и он, и + * главный цикл (шов, straddle, чит-навигация). Данные банковых модулей + * линкуются в общий _DATA, поэтому обе половины видят их как обычные + * глобалы — в банк уехал только КОД. */ +extern uint8_t room_fg[30], room_bg[30]; +extern uint8_t lcol_fg[6], lcol_bg[3], rcol_fg[6], rcol_bg[3], below_fg[11]; +extern uint8_t above_fg[10], above_mod[10]; +extern uint8_t kid_room; /* РЕАЛЬНАЯ комната Kid (straddle) */ +extern uint8_t seam_sig[3]; /* последняя нарисованная openness в шве */ +extern uint8_t seam_rows, seam_redraw; + +/* Сигнатура ряда шва: openness ворот соседа либо «кнопка нажата». Живёт в + * резиденте — её зовёт и кадровый путь главного цикла, и вход в комнату. */ +uint8_t seam_row_sig(const uint8_t *m, const uint8_t *lfg, uint8_t r); +extern uint8_t cur_room; /* её номер (drawn_room) */ +extern uint8_t pop_checkpoint; /* checkpoint (seg000:270) — ставит уход */ + /* влево из комнаты 7 уровня 3 */ + +/* Вход в комнату. side — куда ушёл КИД (0 L, 1 R, 2 U, 3 D); 0xFF = «не + * переход за Кидом» (старт уровня, респавн, чит-навигация): от этого + * зависит, пойдёт ли страж следом. enter_room — обёртка с 0xFF. + * Обе в банке 8: зовутся раз на комнату, а кода там на полтора килобайта. */ +void enter_room_side(uint8_t room, uint8_t side) __banked; +void enter_room(uint8_t room) __banked; +/* Пересобрать только производные кэши и обе страницы после QuickLoad. + * Игровые таймеры, trobs, mobs и персонажей не изменяет. */ +void pop_qsave_restore_room(uint8_t room) __banked; + +/* ---- холодная половина ----------------------------------------------- */ + +/* start_level (seg003:0055) + do_startpos + set_start_pos: поставить Кида в + * стартовую позицию уровня. Один вход и для запуска, и для рестарта после + * смерти / выпадения из уровня — в оригинале это буквально один путь. */ +void pop_start_level(void) __banked; + +/* ОТЛАДОЧНЫЙ обход комнат по номеру ('+' / '−'). dir: 1 — следующая, + * 2 — предыдущая. Ставит Кида на подходящий пол новой комнаты и выводит + * его из боя. Возвращает 1, если комната сменилась (вызывающий обязан + * вернуть себе draw-страницу и обнулить счётчик кадров смерти). */ +uint8_t pop_dbg_roomnav(uint8_t dir) __banked; + +/* Читы по фронту нажатия (K/I/S/Shift+L/U/[/]/F7/F8). В банке: раз в кадр, + * а резидент W1/W2 переполнен — см. шапку функции. */ +void pop_cheat_tick(void) __banked; + +/* Кто рисуется ПОЗЖЕ — Кид или соперник (порядок обхода тайлов оригинала, + * set_objtile_at_char + sort_curr_objs). 1 = соперник поверх. Зовётся раз + * в кадр, поэтому живёт в банке: трамплин на кадр дешевле полукилобайта + * резидента. */ +uint8_t guard_over_kid(void) __banked; + +/* Перевернуть уже нарисованное (зелье инверсии, уровень 9): две копии + * акселератором + инвалидация слотов персонажей и полосы HP. Звать РАЗ на + * переключение, по флагу pop_upside_dirty. */ +void pop_flip_screen(void) __banked; + +/* ---- Запуск / смена уровня / завершение ------------------------------ * + * Три куска главного цикла с частотой «раз за игру» или «раз на уровень». + * В резиденте от них остались только вызовы. */ + +/* Загрузить ресурсы, поднять графику и палитру, поставить Кида на старт и + * нарисовать первый экран. 0 — OK, -1 — фатально (сообщение уже выведено). */ +int pop_boot(void) __banked; + +/* Перейти на pop_next_level: файл уровня, спрайты соперника, тайлсет, + * старт. На время I/O закрывает CBL и оставляет его закрытым: caller обязан + * вызвать pop_sfx_start ПОСЛЕ palette/fade/восстановления snapshot. Если + * файла нет — молча остаться на текущем. -1 — фатально. */ +int pop_level_switch(void) __banked; + +/* Смена уровня целиком (катсцена, гашение, загрузка, палитра, звук). + * back — страница кадра главного цикла. 0 — ок, -1 — сбой. */ +int8_t pop_level_change(uint8_t back) __banked; + +/* Клавиши служебного слоя. Живут здесь, а не в sprpop.c: сам обработчик + * (pop_frame_ui) переехал в банк, а главный цикл о них больше не знает. */ +#define KBD_QUICKSAVE 0x0B /* F6, PS/2 set 2 */ +#define KBD_QUICKLOAD 0x01 /* F9, PS/2 set 2 */ +#define KBD_MENU 0x76 /* Esc — открыть pause menu (edge) */ +#define KBD_QUIT 0x09 /* F10 — отладочный немедленный выход (был на Esc, + * пока меню не готово рисовать — см. план MS4) */ + +/* Меню, F10 и QuickSave/Load на границе кадра. restart_level/dead_reset — + * флаги для главного цикла. 0 — продолжаем, 1 — Restart Game, 2 — Quit, + * -1 — сбой. */ +int8_t pop_frame_ui(uint8_t *restart_level, uint8_t *dead_reset) __banked; + +/* Чистый старт настоящей игры после Enter/Esc на title/intro/credits либо + * клавиши в attract-demo. Ждёт отпускания клавиши, сбрасывает состояние + * новой игры, загружает FIRST_LEVEL и проявляет его первый кадр. */ +int pop_new_game_load(void) __banked; + +/* Снять idle-хук, закрыть ввод и графику, отдать все ресурсы. */ +void pop_shutdown(void) __banked; + +#endif /* ROOMTEST_COLD_H */ diff --git a/applications/SprPoP/tests/host/Makefile b/applications/SprPoP/tests/host/Makefile new file mode 100644 index 0000000..28b1542 --- /dev/null +++ b/applications/SprPoP/tests/host/Makefile @@ -0,0 +1,123 @@ +# Модульные тесты движка SprPoP под ucsim_z80. Обвязка общая (testkit/), +# здесь — только сами наборы и список модулей, которые в них линкуются. +# Наружу тесты знают тот же единственный путь, что и само приложение, — +# корень тулчейна (см. ../../Makefile). Всё остальное считается от него. +SPRINTER_ROOT ?= $(abspath $(CURDIR)/../../../..) +TESTKIT := $(SPRINTER_ROOT)/testkit +APP_DIR := $(abspath $(CURDIR)/../..) +ENGINE_DIR := $(APP_DIR)/src + +# t_phys гоняет НАСТОЯЩУЮ физику, а она ходит по таблицам анимации +# абсолютными адресами от KD_DATA_OFF (0x100) — там, куда на устройстве +# мапится страница W0. Значит блоб kid_data.bin лежит по 0x100, и код +# набора обязан начинаться выше его конца. С добавлением таблицы кадров +# КАТСЦЕН блоб вырос до 4150 Б (0x100 + 4150 = 0x1136), и прежние 0x1000 +# уже резали его хвост — набор падал как «main() не позвал ни одной +# проверки». Держим запас: следующая таблица снова сдвинет границу. +CODE_LOC := 0x1400 +# Данные — выше 0x8000: набор t_phys линкует уже полдвижка (геометрия, Kid, +# карта/физика, управление, страж) и его код перевалил за 0x8000. Раньше +# это проявлялось как «тест зациклился»: хвост кода ложился поверх _DATA. +# 0x9000 оставляет ~24 КБ под код и ~28 КБ до стека (0xFEF0). +DATA_LOC := 0xA000 + +# Заголовки libc/libbgi нужны потому, что pop_kid.c включает gfx/sprite/ +# sprinter: под тестом эти функции заглушены (stubs.c), но объявления +# берутся настоящие — иначе разъедутся сигнатуры. +PROJ_ROOT := $(abspath $(TESTKIT)/..) +EXTRA_INC := -I$(PROJ_ROOT)/libc/include -I$(PROJ_ROOT)/libbgi/include -I$(CURDIR) \ + -I$(APP_DIR)/gen # генерируемые заголовки (pop_bg_atlas.h и прочие) + +OBJS_geom := build/eng_pop_geom.rel + +# pop_guard.c линкуется НАСТОЯЩИЙ, а не заглушкой: после перевода физики на +# Char урон идёт через pop_take_hp, и кому именно он достанется (hitp_* или +# guardhp_*) решает charid — то есть ровно та логика, которую тесты обязаны +# проверять, а не имитировать. +# eng_pop_kboot — загрузчик таблиц анимации: с 2026-08-12 он живёт отдельным +# модулем (в продукте — банк 8), а сцене он нужен: sc_init зовёт +# pop_kid_data_load, и стабы open/read отдают ему настоящий блоб. +OBJS_phys := build/eng_pop_geom.rel build/eng_pop_kid.rel \ + build/eng_pop_kboot.rel \ + build/eng_pop_map.rel build/eng_pop_ctrl.rel \ + build/eng_pop_state.rel build/eng_pop_guard.rel \ + build/eng_pop_guard_cold.rel \ + build/stubs.rel build/scene.rel build/kid_blob.rel \ + build/bank_stub.rel + +# Пробный набор (разведка, без эталонов) линкуется тем же составом. +OBJS_grab := $(OBJS_phys) +# t_wall — удар о стену в воздухе (BUG-JUMPWALL-1), состав тот же. +OBJS_wall := $(OBJS_phys) +# t_gate — открытая решётка не должна считаться стеной у осторожного шага +# (нужны pop_map + физика, то есть тот же состав). +OBJS_gate := $(OBJS_phys) +# t_char — физика над Char для РАЗНЫХ персонажей (Кид / страж / тень). +OBJS_char := $(OBJS_phys) +# t_mouse — спецсобытие уровня 8 (мышь). ЕДИНСТВЕННЫЙ набор, куда линкуется +# guards.c: здесь проверяется сам ИИ (autocontrol_mouse) вместе с рождением +# (pop_check_mouse) и общей физикой соперника — то есть ровно то, что в +# остальных наборах заменено ручным прогоном play_guard_frame. +OBJS_mouse := $(OBJS_phys) build/eng_guards.rel +# t_shadow — спецсобытия уровня 12 (встреча с тенью, общий урон, слияние). +# guards.c нужен по той же причине, что и мыши: check_shadow/ +# check_killed_shadow/sword_disappears живут в нём. +OBJS_shadow := $(OBJS_phys) build/eng_guards.rel +# t_jaffar — спецсобытия уровня 13 (встреча, победа, выход, падающая гряда). +OBJS_jaffar := $(OBJS_phys) build/eng_guards.rel +# t_cfg — весь codec POP.CFG из единственного прикладного модуля. +OBJS_cfg := build/eng_pop_config.rel build/bank_stub.rel +# pop_app — чистая таблица переходов, без DSS/графики. +OBJS_app := build/eng_pop_app.rel build/bank_stub.rel +# pop_demo — запись управления; t_demo подменяет выход control и raw input. +OBJS_demo := build/eng_pop_demo.rel build/bank_stub.rel +# pop_timer — чистое ядро логического времени; поток QuickSave заглушен +# memory-stream'ом в t_timer.c. +OBJS_timer := build/eng_pop_timer.rel build/bank_stub.rel +# pop_cutscene — общий интерпретатор статических сценариев. Он не знает +# графики, поэтому проверяется отдельно от PV/title-ресурсов. +OBJS_cutscene := build/eng_pop_cutscene.rel build/bank_stub.rel +# pop_flow — чистая таблица маршрута перед загрузкой следующего уровня. +OBJS_flow := build/eng_pop_flow.rel build/bank_stub.rel + +include $(TESTKIT)/host-tests.mk + +# У host-теста нет DSS/EMM: ему нужен тот же codec/state, но не target-I/O. +build/eng_pop_config.rel: $(ENGINE_DIR)/pop_config.c | $(BUILD) + @$(SDCC) $(CFLAGS) -DPOP_CONFIG_HOST_TEST -c -o $@ $< + +build/eng_pop_timer.rel: $(ENGINE_DIR)/pop_timer.c | $(BUILD) + @$(SDCC) $(CFLAGS) -c -o $@ $< + +build/eng_pop_demo.rel: $(ENGINE_DIR)/pop_demo.c | $(BUILD) + @$(SDCC) $(CFLAGS) -c -o $@ $< + +build/eng_pop_cutscene.rel: $(ENGINE_DIR)/pop_cutscene.c | $(BUILD) + @$(SDCC) $(CFLAGS) -c -o $@ $< + +build/eng_pop_flow.rel: $(ENGINE_DIR)/pop_flow.c | $(BUILD) + @$(SDCC) $(CFLAGS) -c -o $@ $< + +# --- t_phys: блоб таблиц анимации ------------------------------------ +# Берём дисковый ресурс из assets/packed (он в репозитории), а не выхлоп +# упаковщика из build/: тесты обязаны собираться и без assets/orig/. +# Прежнее имя kid_data.bin устарело — файл давно называется kid.ani. +KID_BIN := $(APP_DIR)/assets/packed/KID/kid.ani + +build/kid_blob.c: gen_kid_blob.py $(KID_BIN) | $(BUILD) + @python3 gen_kid_blob.py $(KID_BIN) $@ + +build/kid_blob.rel: build/kid_blob.c kidblob.h | $(BUILD) + @$(SDCC) $(CFLAGS) -c -o $@ $< + +build/stubs.rel: stubs.c stubs.h kidblob.h | $(BUILD) + @$(SDCC) $(CFLAGS) -c -o $@ $< + +build/scene.rel: scene.c scene.h stubs.h kidblob.h | $(BUILD) + @$(SDCC) $(CFLAGS) -c -o $@ $< + +build/bank_stub.rel: bank_stub.s | $(BUILD) + @$(SDASZ80) -plosgff $@ $< + +# Эталоны трасс: пересобрать набор, если их правили (см. шапку t_phys.c). +build/phys.rel: $(wildcard golden/*.h) diff --git a/applications/SprPoP/tests/host/README.md b/applications/SprPoP/tests/host/README.md new file mode 100644 index 0000000..db81c9a --- /dev/null +++ b/applications/SprPoP/tests/host/README.md @@ -0,0 +1,121 @@ +# tests-host — модульные тесты движка SprPoP + +Прогоняются под `ucsim_z80`, обвязка общая — `testkit/` (там же объяснение, +почему под z80, а не хостовым gcc, и грабли). + +``` +make host-tests # из корня, вместе с остальными +make -C applications/SprPoP/tests/host # только эти +make -C applications/SprPoP/tests/host t_geom +``` + +## Что покрыто + +| набор | модуль | что проверяет | +|-------|--------|---------------| +| `t_geom` | `pop_geom.c` | геометрия комнаты (`x_bump`, `y_land`, `y_to_row`) и PRNG оригинала | +| `t_phys` | `pop_map.c` + `pop_kid.c` + `pop_ctrl.c` | покадровые трассы физики Кида: стойка, бег, шаг с Shift, разворот, присед, прыжок вверх, падение с кромки, loose-плита, разбег-прыжок через провал | +| `t_char` | те же + `pop_guard.c` | физика над `Char` для РАЗНЫХ персонажей: кому достаётся урон (`take_hp` по charid), ветки `land()` (Кид −1 HP с двух рядов, страж гибнет, тень цела), падение и гард по X у слота Guard, `check_guard_fallout`, «окна Char не путают персонажей» | + +Главный тест в `t_geom` — `geom_lcg_matches_reference`. LCG оригинала +(`s = s*214013 + 2531011`) написан в `pop_geom.c` на ассемблере по схеме +Горнера ради обхода `__mullong`, и заявка «бит-в-бит как в SDLPoP» до +появления теста держалась только на комментарии. Тест сверяет рукописный +asm с наивной 32-битной формулой на 128 шагах — и по возвращаемому +значению, и по обеим половинам сида. + +## t_phys: характеризация, а не проверка правильности + +Набор существует ради **страховки под рефакторинг**. Эталоны сняты с +текущего билда, то есть консервируют и текущие баги — правильность +по-прежнему проверяется сверкой с `SDLPoP/src/`. Ценность в другом: +ближайший крупный шаг (перенос физики с `Kid.` на `Char.`, чтобы её мог +переиспользовать страж) обязан быть **поведение-сохраняющим**, и «трасса до +== трасса после» ловит ровно тот класс ошибок, который там возможен. + +Поэтому эталон **переснимается только осознанно**, и в коммите должно быть +написано, что и почему поменялось. Молча обновлённый эталон обесценивает +весь набор. + +Своё дело набор сделал: перенос физики на `Char` (GUARD-PHYS, 2026-08-07) +прошёл при **неизменных трассах** — 1723 проверки без единого расхождения. + +## Грабли раскладки: `DATA_LOC` + +Набор линкует уже полдвижка, и его код перевалил за `--data-loc 0x8000`. +Линкер `sdld` это НЕ ошибка: хвост кода (`_HOME`, деления из libsdcc) он +молча кладёт ПОВЕРХ `_DATA`, после чего тест ведёт себя как «зациклился» +— хотя логика ни при чём. Час на поиск стоило именно это. + +Поэтому: `DATA_LOC` в Makefile набора (сейчас `0x9000`), а `host-tests.mk` +перед каждым прогоном проверяет `CODE_LOC + l__CODE <= DATA_LOC` и падает +с внятным сообщением. Упёрлись снова — поднять `DATA_LOC` (стек в +`crt0_ucsim` начинается с 0xFEF0). + +``` +python3 record_golden.py # переснять все трассы +python3 record_golden.py 7 9 # только сценарии 7 и 9 +``` + +Трасса — по одной записи на кадр: `frame, x, y, dir, col, row, action` и +смещение `curr_seq` от `SEQTBL_BASE`. Кадр гоняется ровно в том порядке, +что и в главном цикле `sprpop.c`: + +``` +pop_ctrl_tick(); // ввод -> control(): смена последовательности +kid_tick(); // play_seq: следующий кадр +pop_phys_tick(); // падение/приземление/стена +pop_loose_tick(); // досчёт тряски и снятие провалившейся плиты +``` + +Покадровость здесь принципиальна: порт seg005/seg006 — конечный автомат, и +ошибка почти всегда проявляется не в одной функции, а в расхождении +траектории через несколько кадров. Плюс главный риск переноса на `Char` — +не арифметика, а то, **кто владеет окном `Char` внутри кадра**; поймать это +можно только прогоном полного кадра. + +Проверка, что харнесс воспроизводит устройство: сценарий `stand` даёт +`frame=15, x=114, y=118, curr_seq=0x19A3` — те же значения, что читаются из +`_Kid` в живом MAME. + +### Как это заработало под ucsim + +Три шва, каждый закрыт без правок продуктового кода: + +1. **Таблицы анимации.** `pop_kid.c` ходит по ним абсолютными адресами от + `KD_DATA_OFF` (0x100) — там, куда на устройстве их приводит маппинг W0. + В тесте `kid_data.bin` просто кладётся по 0x100 (`gen_kid_blob.py`), а + `gfx_w0_map` заглушен. Следствие: код набора линкуется с + `--code-loc 0x1000` (`CODE_LOC` в Makefile), чтобы не налезть на блоб. +2. **Окружение.** `stubs.c` — libc/libbgi (графика, память, файлы), соседние + модули (`pop_bg`/`pop_trob`/`pop_level`/`pop_redraw`) и состояние стража. + Заглушки не пустые там, где это меняет смысл: файловое чтение реально + отдаёт `kid_data.bin` (иначе `pop_kid_data_load` тихо сдаётся, `kdat_ok` + остаётся нулём и `play_seq` не делает ничего — трасса выходит из одного + застывшего кадра), клавиатура отвечает по набору «нажатых» скан-кодов + (так под тест попадают `read_input` и `read_user_control`, а не только + диспетчер), а обращения к соседям **журналируются** (`tk_log`) — можно + проверять «плита отвалилась и попросила перерисовку», а не только + координаты. +3. **`__banked`.** Модули физики помечены как банковые, SDCC генерирует на + них трамплин `___sdcc_bcall_ehl`, которому нужны `set_bank`/`get_bank`. + В тесте память плоская — `bank_stub.s` отдаёт нулевой банк и пустое + переключение, соблюдая контракт по регистрам. + +Что харнесс НЕ покрывает и остаётся за MAME: отрисовка и окклюзия, реальные +переключения банков, тайминги кадра, настоящая клавиатура. + +## Что нужно, чтобы двинуться дальше + +- **Доступ к странице уровня.** `pop_level.c` ходит по абсолютным адресам + (`(uint8_t *)(LVL_DATA_OFF + …)` после `gfx_w0_map`). Для `pop_map` это не + потребовалось — карту комнаты в него ИНЪЕКТИРУЮТ через `pop_map_set`, — + но набор на сам `pop_level`/`pop_trob` в это упрётся. Решение то же, что + для `kid_data`: положить страницу по нужному адресу и поднять `CODE_LOC`. +- **Сценарии из `BUGS_CLOSED.md`.** У каждой закрытой записи есть симптом и + ожидаемое поведение — готовый список регрессионных кейсов, который стоит + переносить в `t_phys` по мере надобности. +- **BUG-LOOSE-2** (гонка «уйти из комнаты раньше, чем долетит плита») — + через мост MAME воспроизвести не удалось, а на уровне логики это + несколько строк: заспавнить кусок, сменить комнату, тикать до + приземления, проверить щебень. diff --git a/applications/SprPoP/tests/host/bank_stub.s b/applications/SprPoP/tests/host/bank_stub.s new file mode 100644 index 0000000..5e7921a --- /dev/null +++ b/applications/SprPoP/tests/host/bank_stub.s @@ -0,0 +1,25 @@ +;-------------------------------------------------------------------------- +; bank_stub.s — set_bank/get_bank для прогона под ucsim. +; +; Модули движка помечены __banked (в продукте они лежат в страницах W3), и +; SDCC генерирует на такие вызовы трамплин ___sdcc_bcall_ehl из своей +; библиотеки. Трамплин обязан найти две пользовательские подпрограммы: +; +; get_bank — вернуть в A номер текущего банка, прочие регистры не трогать +; set_bank — переключить банк на A, прочие регистры не трогать +; +; В тесте банков нет: вся память плоская, все модули слинкованы вместе. +; Поэтому «банк» всегда нулевой, а переключение — пустое. Важно только +; соблюсти контракт по регистрам, иначе трамплин испортит аргументы вызова. +;-------------------------------------------------------------------------- + .area _CODE + + .globl set_bank + .globl get_bank + +get_bank:: + ld a, #0x00 + ret + +set_bank:: + ret diff --git a/applications/SprPoP/tests/host/gen_kid_blob.py b/applications/SprPoP/tests/host/gen_kid_blob.py new file mode 100644 index 0000000..1974d6b --- /dev/null +++ b/applications/SprPoP/tests/host/gen_kid_blob.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +""" +gen_kid_blob.py — kid_data.bin как C-массив для тестов под ucsim. + +Зачем. Таблицы кадров и seqtbl живут в EMM-странице, замапленной в W0, и +pop_kid ходит по ним АБСОЛЮТНЫМИ адресами от KD_DATA_OFF (0x100). В ucsim +никакого W0 нет, зато есть ровная 64-КБ память, поэтому тест просто кладёт +файл по 0x100 и глушит gfx_w0_map. Единственное следствие для сборки — +код набора линкуется с --code-loc выше конца блоба (см. Makefile). + +Файл берётся ровно тот, что уходит на устройство, — pop_extract_kid_data.py +делает его из оригинального PRINCE.EXE, так что тест проверяет порт против +НАСТОЯЩИХ данных игры, а не против переписанной от руки таблицы. +""" +import sys +from pathlib import Path + +src, dst = Path(sys.argv[1]), Path(sys.argv[2]) +data = src.read_bytes() + +out = [ + "/* Сгенерировано gen_kid_blob.py — не править руками. */", + '#include "kidblob.h"', + "", + f"const unsigned char kid_blob[{len(data)}] = {{", +] +for i in range(0, len(data), 16): + out.append(" " + ",".join(str(b) for b in data[i:i + 16]) + ",") +out += [ + "};", + "", + f"const unsigned int kid_blob_size = {len(data)};", + "", + "/* Разложить блоб по KD_DATA_OFF. Без memcpy: тестовый бинарь не", + " * линкуется с libc, а тянуть ради этого z80-рантайм незачем. */", + "void kid_blob_install(void)", + "{", + " unsigned char *d = (unsigned char *)KID_BLOB_ADDR;", + " const unsigned char *s = kid_blob;", + f" unsigned int n = {len(data)};", + " while (n--) *d++ = *s++;", + "}", + "", +] +dst.write_text("\n".join(out)) +print(f"gen_kid_blob: {src.name} -> {dst.name} ({len(data)} Б)") diff --git a/applications/SprPoP/tests/host/golden/crouch.h b/applications/SprPoP/tests/host/golden/crouch.h new file mode 100644 index 0000000..62fdebf --- /dev/null +++ b/applications/SprPoP/tests/host/golden/crouch.h @@ -0,0 +1,22 @@ +/* Эталон трассы: crouch. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x6B, 0x81, 0x76, 0x00, 0x04, 0x01, 0x01, 0x3E, 0x06, + 0x6C, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x41, 0x06, + 0x6D, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x42, 0x06, + 0x6D, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x42, 0x06, + 0x6D, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x42, 0x06, + 0x6D, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x42, 0x06, + 0x6D, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x42, 0x06, + 0x6D, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x42, 0x06, + 0x6E, 0x84, 0x76, 0x00, 0x04, 0x01, 0x05, 0x4A, 0x06, + 0x6F, 0x84, 0x76, 0x00, 0x04, 0x01, 0x05, 0x4B, 0x06, + 0x70, 0x86, 0x76, 0x00, 0x04, 0x01, 0x05, 0x4E, 0x06, + 0x71, 0x86, 0x76, 0x00, 0x04, 0x01, 0x05, 0x4F, 0x06, + 0x72, 0x87, 0x76, 0x00, 0x04, 0x01, 0x05, 0x52, 0x06, + 0x73, 0x87, 0x76, 0x00, 0x04, 0x01, 0x05, 0x53, 0x06, + 0x74, 0x87, 0x76, 0x00, 0x04, 0x01, 0x05, 0x54, 0x06, + 0x75, 0x83, 0x76, 0x00, 0x04, 0x01, 0x05, 0x57, 0x06, + 0x76, 0x83, 0x76, 0x00, 0x04, 0x01, 0x05, 0x58, 0x06, + 0x77, 0x83, 0x76, 0x00, 0x04, 0x01, 0x05, 0x59, 0x06, + 0x0F, 0x83, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x83, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, diff --git a/applications/SprPoP/tests/host/golden/fall_ledge.h b/applications/SprPoP/tests/host/golden/fall_ledge.h new file mode 100644 index 0000000..450567d --- /dev/null +++ b/applications/SprPoP/tests/host/golden/fall_ledge.h @@ -0,0 +1,36 @@ +/* Эталон трассы: fall_ledge. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x01, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x08, 0x00, + 0x02, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x09, 0x00, + 0x03, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0A, 0x00, + 0x04, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0B, 0x00, + 0x05, 0x5E, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0E, 0x00, + 0x06, 0x61, 0x76, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, + 0x07, 0x64, 0x76, 0x00, 0x01, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x69, 0x76, 0x00, 0x02, 0x01, 0x01, 0x17, 0x00, + 0x09, 0x6A, 0x76, 0x00, 0x02, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0x6C, 0x76, 0x00, 0x02, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0x70, 0x76, 0x00, 0x02, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0x75, 0x76, 0x00, 0x03, 0x01, 0x01, 0x25, 0x00, + 0x0D, 0x77, 0x76, 0x00, 0x03, 0x01, 0x01, 0x2A, 0x00, + 0x0E, 0x7A, 0x76, 0x00, 0x03, 0x01, 0x01, 0x2D, 0x00, + 0x07, 0x7E, 0x76, 0x00, 0x03, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x17, 0x00, + 0x09, 0x84, 0x76, 0x00, 0x04, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0x86, 0x76, 0x00, 0x04, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0x8A, 0x76, 0x00, 0x04, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0x8F, 0x76, 0x00, 0x05, 0x01, 0x01, 0x25, 0x00, + 0x0D, 0x91, 0x76, 0x00, 0x05, 0x01, 0x01, 0x2A, 0x00, + 0x0E, 0x94, 0x76, 0x00, 0x05, 0x01, 0x01, 0x2D, 0x00, + 0x07, 0x98, 0x76, 0x00, 0x05, 0x01, 0x01, 0x14, 0x00, + 0x66, 0x9E, 0x79, 0x00, 0x06, 0x02, 0x03, 0x77, 0x02, + 0x67, 0xA0, 0x7F, 0x00, 0x06, 0x02, 0x03, 0x7C, 0x02, + 0x68, 0x9F, 0x88, 0x00, 0x06, 0x02, 0x03, 0x81, 0x02, + 0x69, 0x9F, 0x94, 0x00, 0x06, 0x02, 0x03, 0x84, 0x02, + 0x6A, 0x9E, 0xA6, 0x00, 0x06, 0x02, 0x04, 0xDE, 0x03, + 0x6B, 0xA0, 0xB5, 0x00, 0x06, 0x02, 0x05, 0xC3, 0x06, + 0x6C, 0xA2, 0xB5, 0x00, 0x06, 0x02, 0x05, 0xC6, 0x06, + 0x6D, 0xA2, 0xB5, 0x00, 0x06, 0x02, 0x01, 0xC9, 0x06, + 0x6E, 0xA3, 0xB5, 0x00, 0x06, 0x02, 0x05, 0x4A, 0x06, + 0x6F, 0xA3, 0xB5, 0x00, 0x06, 0x02, 0x05, 0x4B, 0x06, + 0x70, 0xA5, 0xB5, 0x00, 0x06, 0x02, 0x05, 0x4E, 0x06, diff --git a/applications/SprPoP/tests/host/golden/jump_up.h b/applications/SprPoP/tests/host/golden/jump_up.h new file mode 100644 index 0000000..98ddae4 --- /dev/null +++ b/applications/SprPoP/tests/host/golden/jump_up.h @@ -0,0 +1,22 @@ +/* Эталон трассы: jump_up. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x43, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x12, 0x04, + 0x44, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x13, 0x04, + 0x45, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x14, 0x04, + 0x46, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x15, 0x04, + 0x47, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x16, 0x04, + 0x48, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x17, 0x04, + 0x49, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x18, 0x04, + 0x4A, 0x80, 0x76, 0xFF, 0x05, 0x01, 0x01, 0x19, 0x04, + 0x4B, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x1A, 0x04, + 0x4C, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x1B, 0x04, + 0x4D, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x1C, 0x04, + 0x4E, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x1D, 0x04, + 0x4F, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x1E, 0x04, + 0x4F, 0x80, 0x72, 0xFF, 0x04, 0x01, 0x01, 0x21, 0x04, + 0x4F, 0x80, 0x70, 0xFF, 0x04, 0x01, 0x01, 0x24, 0x04, + 0x4F, 0x80, 0x70, 0xFF, 0x04, 0x01, 0x01, 0x25, 0x04, + 0x4F, 0x80, 0x72, 0xFF, 0x04, 0x01, 0x01, 0x28, 0x04, + 0x51, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0xB8, 0x03, + 0x52, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x01, 0xB9, 0x03, + 0x53, 0x80, 0x76, 0xFF, 0x04, 0x01, 0x05, 0xBC, 0x03, diff --git a/applications/SprPoP/tests/host/golden/loose.h b/applications/SprPoP/tests/host/golden/loose.h new file mode 100644 index 0000000..d1df6d1 --- /dev/null +++ b/applications/SprPoP/tests/host/golden/loose.h @@ -0,0 +1,36 @@ +/* Эталон трассы: loose. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x01, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x08, 0x00, + 0x02, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x09, 0x00, + 0x03, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0A, 0x00, + 0x04, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0B, 0x00, + 0x05, 0x5E, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0E, 0x00, + 0x06, 0x61, 0x76, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, + 0x07, 0x64, 0x76, 0x00, 0x01, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x69, 0x76, 0x00, 0x02, 0x01, 0x01, 0x17, 0x00, + 0x09, 0x6A, 0x76, 0x00, 0x02, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0x6C, 0x76, 0x00, 0x02, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0x70, 0x76, 0x00, 0x02, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0x75, 0x76, 0x00, 0x03, 0x01, 0x01, 0x25, 0x00, + 0x0D, 0x77, 0x76, 0x00, 0x03, 0x01, 0x01, 0x2A, 0x00, + 0x0E, 0x7A, 0x76, 0x00, 0x03, 0x01, 0x01, 0x2D, 0x00, + 0x07, 0x7E, 0x76, 0x00, 0x03, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x17, 0x00, + 0x09, 0x84, 0x76, 0x00, 0x04, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0x86, 0x76, 0x00, 0x04, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0x8A, 0x76, 0x00, 0x04, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0x8F, 0x76, 0x00, 0x05, 0x01, 0x01, 0x25, 0x00, + 0x0D, 0x91, 0x76, 0x00, 0x05, 0x01, 0x01, 0x2A, 0x00, + 0x0E, 0x94, 0x76, 0x00, 0x05, 0x01, 0x01, 0x2D, 0x00, + 0x07, 0x98, 0x76, 0x00, 0x05, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x9D, 0x76, 0x00, 0x06, 0x01, 0x01, 0x17, 0x00, + 0x09, 0x9E, 0x76, 0x00, 0x06, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0xA0, 0x76, 0x00, 0x06, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0xA4, 0x76, 0x00, 0x06, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0xA9, 0x76, 0x00, 0x07, 0x01, 0x01, 0x25, 0x00, + 0x0D, 0xAB, 0x76, 0x00, 0x07, 0x01, 0x01, 0x2A, 0x00, + 0x0E, 0xAE, 0x76, 0x00, 0x07, 0x01, 0x01, 0x2D, 0x00, + 0x07, 0xB2, 0x76, 0x00, 0x07, 0x01, 0x01, 0x14, 0x00, + 0x08, 0xB7, 0x76, 0x00, 0x08, 0x01, 0x01, 0x17, 0x00, + 0x09, 0xB8, 0x76, 0x00, 0x08, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0xBA, 0x76, 0x00, 0x08, 0x01, 0x01, 0x1F, 0x00, diff --git a/applications/SprPoP/tests/host/golden/rjump_gap3.h b/applications/SprPoP/tests/host/golden/rjump_gap3.h new file mode 100644 index 0000000..d2c141c --- /dev/null +++ b/applications/SprPoP/tests/host/golden/rjump_gap3.h @@ -0,0 +1,36 @@ +/* Эталон трассы: rjump_gap3. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x01, 0xB8, 0x76, 0xFF, 0x08, 0x01, 0x01, 0x08, 0x00, + 0x02, 0xB8, 0x76, 0xFF, 0x08, 0x01, 0x01, 0x09, 0x00, + 0x03, 0xB8, 0x76, 0xFF, 0x08, 0x01, 0x01, 0x0A, 0x00, + 0x04, 0xB8, 0x76, 0xFF, 0x08, 0x01, 0x01, 0x0B, 0x00, + 0x05, 0xB0, 0x76, 0xFF, 0x08, 0x01, 0x01, 0x0E, 0x00, + 0x06, 0xAD, 0x76, 0xFF, 0x08, 0x01, 0x01, 0x11, 0x00, + 0x07, 0xAA, 0x76, 0xFF, 0x08, 0x01, 0x01, 0x14, 0x00, + 0x08, 0xA5, 0x76, 0xFF, 0x07, 0x01, 0x01, 0x17, 0x00, + 0x09, 0xA4, 0x76, 0xFF, 0x07, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0xA2, 0x76, 0xFF, 0x07, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0x9E, 0x76, 0xFF, 0x07, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0x99, 0x76, 0xFF, 0x06, 0x01, 0x01, 0x25, 0x00, + 0x0D, 0x97, 0x76, 0xFF, 0x06, 0x01, 0x01, 0x2A, 0x00, + 0x22, 0x95, 0x76, 0xFF, 0x06, 0x01, 0x01, 0x64, 0x01, + 0x23, 0x90, 0x76, 0xFF, 0x06, 0x01, 0x01, 0x67, 0x01, + 0x24, 0x8A, 0x76, 0xFF, 0x06, 0x01, 0x01, 0x6A, 0x01, + 0x25, 0x87, 0x76, 0xFF, 0x05, 0x01, 0x01, 0x6D, 0x01, + 0x26, 0x82, 0x76, 0xFF, 0x05, 0x01, 0x01, 0x72, 0x01, + 0x27, 0x7B, 0x76, 0xFF, 0x05, 0x01, 0x01, 0x75, 0x01, + 0x28, 0x6F, 0x73, 0xFF, 0x04, 0x01, 0x01, 0x7A, 0x01, + 0x29, 0x67, 0x6A, 0xFF, 0x03, 0x01, 0x01, 0x7F, 0x01, + 0x2A, 0x5F, 0x68, 0xFF, 0x02, 0x01, 0x01, 0x84, 0x01, + 0x2B, 0x5B, 0x73, 0xFF, 0x01, 0x01, 0x01, 0x89, 0x01, + 0x2C, 0x57, 0x76, 0xFF, 0x01, 0x01, 0x01, 0x8E, 0x01, + 0x07, 0x52, 0x76, 0xFF, 0x01, 0x01, 0x01, 0x14, 0x00, + 0x32, 0x52, 0x76, 0xFF, 0x01, 0x01, 0x05, 0x93, 0x04, + 0x33, 0x52, 0x76, 0xFF, 0x01, 0x01, 0x05, 0x94, 0x04, + 0x34, 0x52, 0x76, 0xFF, 0x01, 0x01, 0x05, 0x95, 0x04, + 0x0F, 0x52, 0x76, 0xFF, 0x01, 0x01, 0x00, 0x35, 0x00, + 0x10, 0x52, 0x76, 0xFF, 0x01, 0x01, 0x01, 0x28, 0x01, + 0x11, 0x52, 0x76, 0xFF, 0x01, 0x01, 0x01, 0x29, 0x01, + 0x12, 0x50, 0x76, 0xFF, 0x01, 0x01, 0x01, 0x2C, 0x01, + 0x13, 0x4E, 0x76, 0xFF, 0x01, 0x01, 0x01, 0x2F, 0x01, + 0x32, 0x52, 0x76, 0xFF, 0x01, 0x01, 0x05, 0x93, 0x04, diff --git a/applications/SprPoP/tests/host/golden/run_right.h b/applications/SprPoP/tests/host/golden/run_right.h new file mode 100644 index 0000000..96c4fd6 --- /dev/null +++ b/applications/SprPoP/tests/host/golden/run_right.h @@ -0,0 +1,26 @@ +/* Эталон трассы: run_right. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x01, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x08, 0x00, + 0x02, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x09, 0x00, + 0x03, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0A, 0x00, + 0x04, 0x56, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0B, 0x00, + 0x05, 0x5E, 0x76, 0x00, 0x01, 0x01, 0x01, 0x0E, 0x00, + 0x06, 0x61, 0x76, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, + 0x07, 0x64, 0x76, 0x00, 0x01, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x69, 0x76, 0x00, 0x02, 0x01, 0x01, 0x17, 0x00, + 0x09, 0x6A, 0x76, 0x00, 0x02, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0x6C, 0x76, 0x00, 0x02, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0x70, 0x76, 0x00, 0x02, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0x75, 0x76, 0x00, 0x03, 0x01, 0x01, 0x25, 0x00, + 0x0D, 0x77, 0x76, 0x00, 0x03, 0x01, 0x01, 0x2A, 0x00, + 0x0E, 0x7A, 0x76, 0x00, 0x03, 0x01, 0x01, 0x2D, 0x00, + 0x07, 0x7E, 0x76, 0x00, 0x03, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x83, 0x76, 0x00, 0x04, 0x01, 0x01, 0x17, 0x00, + 0x09, 0x84, 0x76, 0x00, 0x04, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0x86, 0x76, 0x00, 0x04, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0x8A, 0x76, 0x00, 0x04, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0x8F, 0x76, 0x00, 0x05, 0x01, 0x01, 0x25, 0x00, + 0x0D, 0x91, 0x76, 0x00, 0x05, 0x01, 0x01, 0x2A, 0x00, + 0x0E, 0x94, 0x76, 0x00, 0x05, 0x01, 0x01, 0x2D, 0x00, + 0x07, 0x98, 0x76, 0x00, 0x05, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x9D, 0x76, 0x00, 0x06, 0x01, 0x01, 0x17, 0x00, diff --git a/applications/SprPoP/tests/host/golden/stand.h b/applications/SprPoP/tests/host/golden/stand.h new file mode 100644 index 0000000..8610c7c --- /dev/null +++ b/applications/SprPoP/tests/host/golden/stand.h @@ -0,0 +1,14 @@ +/* Эталон трассы: stand. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x72, 0x76, 0xFF, 0x03, 0x01, 0x00, 0x35, 0x00, diff --git a/applications/SprPoP/tests/host/golden/step_shift.h b/applications/SprPoP/tests/host/golden/step_shift.h new file mode 100644 index 0000000..c6c0891 --- /dev/null +++ b/applications/SprPoP/tests/host/golden/step_shift.h @@ -0,0 +1,22 @@ +/* Эталон трассы: step_shift. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x79, 0x72, 0x76, 0x00, 0x03, 0x01, 0x01, 0x6F, 0x05, + 0x7A, 0x73, 0x76, 0x00, 0x03, 0x01, 0x01, 0x72, 0x05, + 0x7B, 0x74, 0x76, 0x00, 0x03, 0x01, 0x01, 0x75, 0x05, + 0x7C, 0x77, 0x76, 0x00, 0x03, 0x01, 0x01, 0x78, 0x05, + 0x7D, 0x7B, 0x76, 0x00, 0x03, 0x01, 0x01, 0x7B, 0x05, + 0x7E, 0x7E, 0x76, 0x00, 0x03, 0x01, 0x01, 0x7E, 0x05, + 0x7F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x01, 0x81, 0x05, + 0x80, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x01, 0x82, 0x05, + 0x81, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x01, 0x83, 0x05, + 0x82, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x01, 0x84, 0x05, + 0x83, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x01, 0x85, 0x05, + 0x84, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x01, 0x86, 0x05, + 0x0F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, + 0x0F, 0x7D, 0x76, 0x00, 0x04, 0x01, 0x00, 0x35, 0x00, diff --git a/applications/SprPoP/tests/host/golden/turn.h b/applications/SprPoP/tests/host/golden/turn.h new file mode 100644 index 0000000..66f5dbd --- /dev/null +++ b/applications/SprPoP/tests/host/golden/turn.h @@ -0,0 +1,18 @@ +/* Эталон трассы: turn. Снят record_golden.py — не править руками. + * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */ + 0x2D, 0x7A, 0x76, 0xFF, 0x04, 0x01, 0x07, 0xD1, 0x01, + 0x2E, 0x79, 0x76, 0xFF, 0x04, 0x01, 0x07, 0xD4, 0x01, + 0x2F, 0x77, 0x76, 0xFF, 0x04, 0x01, 0x07, 0xD7, 0x01, + 0x30, 0x78, 0x76, 0xFF, 0x04, 0x01, 0x07, 0xDA, 0x01, + 0x01, 0x79, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x08, 0x00, + 0x02, 0x79, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x09, 0x00, + 0x03, 0x79, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x0A, 0x00, + 0x04, 0x79, 0x76, 0xFF, 0x04, 0x01, 0x01, 0x0B, 0x00, + 0x05, 0x71, 0x76, 0xFF, 0x03, 0x01, 0x01, 0x0E, 0x00, + 0x06, 0x6E, 0x76, 0xFF, 0x03, 0x01, 0x01, 0x11, 0x00, + 0x07, 0x6B, 0x76, 0xFF, 0x03, 0x01, 0x01, 0x14, 0x00, + 0x08, 0x66, 0x76, 0xFF, 0x03, 0x01, 0x01, 0x17, 0x00, + 0x09, 0x65, 0x76, 0xFF, 0x02, 0x01, 0x01, 0x1C, 0x00, + 0x0A, 0x63, 0x76, 0xFF, 0x02, 0x01, 0x01, 0x1F, 0x00, + 0x0B, 0x5F, 0x76, 0xFF, 0x02, 0x01, 0x01, 0x22, 0x00, + 0x0C, 0x5A, 0x76, 0xFF, 0x02, 0x01, 0x01, 0x25, 0x00, diff --git a/applications/SprPoP/tests/host/kidblob.h b/applications/SprPoP/tests/host/kidblob.h new file mode 100644 index 0000000..11f58aa --- /dev/null +++ b/applications/SprPoP/tests/host/kidblob.h @@ -0,0 +1,18 @@ +/* + * kidblob.h — таблицы анимации Кида в памяти теста. + * + * KID_BLOB_ADDR обязан совпадать с KD_DATA_OFF из pop_kid.c: продукт + * адресует таблицы именно так, и в этом весь смысл — под тестом работает + * тот же код доступа, что на устройстве, просто без маппинга W0. + */ +#ifndef POP_TESTS_KIDBLOB_H +#define POP_TESTS_KIDBLOB_H + +#define KID_BLOB_ADDR 0x0100 + +extern const unsigned char kid_blob[]; +extern const unsigned int kid_blob_size; + +void kid_blob_install(void); + +#endif diff --git a/applications/SprPoP/tests/host/record_golden.py b/applications/SprPoP/tests/host/record_golden.py new file mode 100644 index 0000000..b21db80 --- /dev/null +++ b/applications/SprPoP/tests/host/record_golden.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +""" +record_golden.py — снять эталонные трассы для t_phys. + +Зачем скрипт, а не один прогон. Отчёт tcheck ограничен 768 байтами лога, а +трасс на все сценарии сильно больше. Поэтому набор умеет снимать РОВНО +ОДИН сценарий за прогон (-DSC_RECORD=), а перебор делает этот скрипт: +собирает набор N раз, каждый раз забирает из отчёта имя сценария и его +трассу и пишет golden/<имя>.h. + +Эталон фиксирует ТЕКУЩЕЕ поведение. Это осознанно: набор страхует +поведение-сохраняющие правки (перенос физики Kid -> Char), а не доказывает +правильность. Правильность — сверка с SDLPoP. Поэтому переснимать эталоны +можно только вместе с внятным объяснением, что и почему поменялось. + +Использование: + python3 record_golden.py # снять все сценарии + python3 record_golden.py 7 9 # только эти номера +""" +import re +import subprocess +import sys +from pathlib import Path + +HERE = Path(__file__).resolve().parent +GOLDEN = HERE / "golden" +# Число сценариев = число TC_RUN в t_phys.c: список ведётся там, здесь его +# дублировать незачем. +NSCEN = len(re.findall(r"^\s*TC_RUN\(", (HERE / "t_phys.c").read_text(), re.M)) + + +def record(n: int) -> str: + # make не видит смены -DSC_RECORD (в зависимостях только файлы), поэтому + # объектник набора сносим руками — иначе следующий прогон молча снимет + # трассу предыдущего сценария под новым именем. + for f in ("phys.rel", "phys.ihx"): + (HERE / "build" / f).unlink(missing_ok=True) + subprocess.run(["make", "-s", "build/phys.ihx", f"EXTRA_CFLAGS=-DSC_RECORD={n}"], + cwd=HERE, check=True) + p = subprocess.run(["make", "-s", "run-phys", f"EXTRA_CFLAGS=-DSC_RECORD={n}"], + cwd=HERE, capture_output=True, text=True) + out = p.stdout + m = re.search(r"^\s*### (\S+)\s*$", out, re.M) + if not m: + raise SystemExit(f"сценарий {n}: в отчёте нет строки '### имя'\n{out}") + name = m.group(1) + rows = re.findall(r"^\s*([0-9A-F]{18})\s*$", out, re.M) + if not rows: + raise SystemExit(f"сценарий {n} ({name}): трасса пустая\n{out}") + + lines = [f"/* Эталон трассы: {name}. Снят record_golden.py — не править руками.", + " * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */"] + for r in rows: + lines.append(" " + ", ".join(f"0x{r[i:i+2]}" for i in range(0, 18, 2)) + ",") + GOLDEN.mkdir(exist_ok=True) + (GOLDEN / f"{name}.h").write_text("\n".join(lines) + "\n") + return f"{name}: {len(rows)} кадров" + + +def main() -> int: + nums = [int(a) for a in sys.argv[1:]] or list(range(1, NSCEN + 1)) + for n in nums: + print(f"[{n}/{NSCEN}] " + record(n)) + # Итоговая пересборка в обычном режиме, чтобы каталог не остался с + # объектниками, собранными под SC_RECORD. + subprocess.run(["make", "-s", "clean"], cwd=HERE, check=True) + print("эталоны сняты; проверить: make -C", HERE.name, "t_phys") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/applications/SprPoP/tests/host/scene.c b/applications/SprPoP/tests/host/scene.c new file mode 100644 index 0000000..9202153 --- /dev/null +++ b/applications/SprPoP/tests/host/scene.c @@ -0,0 +1,213 @@ +/* + * scene.c — реализация покадрового раннера (см. scene.h). + */ +#include +#include + +#include "tcheck.h" +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_ctrl.h" +#include "pop_geom.h" +#include "pop_guard.h" +#include "kid_data.h" +#include "scene.h" +#include "stubs.h" +#include "kidblob.h" + +sc_rec sc_trace[SC_TRACE_MAX]; +uint8_t sc_len; + +static uint8_t sc_fg[30]; +/* Соседей у сцены нет (links = 0) — по краям комнаты стена: карта сама + * решает так, когда соседняя комната не задана. */ + +void sc_init(void) +{ + kid_blob_install(); /* таблицы туда, куда смотрит pop_kid */ + pop_kid_data_load("KID\\KID.ANI"); + pop_kid_hp_newgame(); +} + +void sc_room(const uint8_t *fg, uint8_t room) +{ + uint8_t i; + for (i = 0; i < 30; i++) { sc_fg[i] = fg[i]; tk_modif[i] = 0; } + pop_map_set(sc_fg); + pop_map_set_room(room); + /* Изолированная сцена: соседей нет. Тип TILE_WALL (20) с обеих сторон + * и сверху/снизу — тогда решения физики зависят только от sc_fg, и + * трасса не поедет, если когда-нибудь поменяется загрузка кромок. */ + pop_map_set_edges(0, 0, 0, 0); + tk_start_room = room; + tk_log_clear(); +} + +void sc_kid_at(uint8_t col, uint8_t row, int8_t dir) +{ + kid_init(2 /* seq_2_stand */, (int8_t)col, (int8_t)row, dir); + /* kid_init комнату не трогает (в продукте её ставит enter_room_side), а + * сцена обязана быть согласованной: логика, спрашивающая «персонаж в + * ОТРИСОВАННОЙ комнате?» (pop_gate_over_char), иначе всегда отвечает + * «нет». Тест, которому нужна другая комната, задаёт её сам. */ + Kid.room = tk_start_room; + pop_kid_hp_reset(); +} + +void sc_kid_at_x(uint8_t col, uint8_t row, uint8_t x, int8_t dir) +{ + kid_init(2 /* seq_2_stand */, (int8_t)col, (int8_t)row, dir); + Kid.room = tk_start_room; + Kid.x = x; /* тайл задаёт ряд, X — фазу внутри тайла */ + /* Колонку пересчитываем В ОКНЕ Char: determine_col — часть физики, а та + * работает с активным персонажем (порт load_fram_det_col). */ + pop_loadkid(); + pop_determine_col(); + pop_savekid(); + pop_kid_hp_reset(); +} + +/* ---- слот Guard ------------------------------------------------------ */ + +void sc_guard_at(uint8_t charid, uint8_t col, uint8_t row, int8_t dir, uint8_t hp) +{ + /* Поля — как в enter_guard (seg002:0112): X из колонки, Y с пола ряда. */ + Guard.charid = charid; + Guard.curr_col = (int8_t)col; + Guard.curr_row = (int8_t)row; + Guard.x = (uint8_t)(pop_x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX); + Guard.y = (uint8_t)pop_y_land[row + 1]; + Guard.direction = dir; + Guard.alive = -1; + Guard.action = 1; /* actions_1_run_jump */ + Guard.sword = 2; /* у живого стража меч в руке */ + Guard.fall_x = Guard.fall_y = 0; + Guard.repeat = 0; + Guard.room = 1; + guardhp_max = guardhp_curr = hp; + guardhp_delta = 0; + /* Стойка с мечом (seq_77 у неактивного стража — но нам нужна поза, из + * которой персонаж просто стоит и может упасть). */ + pop_loadshad(); + pop_char_set_seq(77 /* seq_77_guard_stand_inactive */); + play_seq(); + pop_saveshad(); +} + +void sc_guard_run(uint8_t n) +{ + uint8_t i; + for (i = 0; i < n; i++) { + /* play_guard_frame (seg000:1246) без ИИ: окно, кадр, seq, физика. */ + pop_loadshad_and_opp(); + pop_load_fram_det_col(); + play_seq(); + pop_saveshad_and_opp(); + pop_guard_phys_tick(); + } +} + +void sc_trace_clear(void) { sc_len = 0; } + +static void sc_record(void) +{ + sc_rec *r; + if (sc_len >= SC_TRACE_MAX) return; + r = &sc_trace[sc_len++]; + r->frame = Kid.frame; + r->x = Kid.x; + r->y = Kid.y; + r->dir = Kid.direction; + r->col = Kid.curr_col; + r->row = Kid.curr_row; + r->action = Kid.action; + r->seq = (uint16_t)(Kid.curr_seq - SEQTBL_BASE); +} + +uint8_t sc_run(uint8_t keys, uint8_t n) +{ + uint8_t i, done = 0; + for (i = 0; i < n; i++) { + tk_keys_clear(); + if (keys & SC_L) tk_key_down(KBD_LEFT); + if (keys & SC_R) tk_key_down(KBD_RIGHT); + if (keys & SC_U) tk_key_down(KBD_UP); + if (keys & SC_D) tk_key_down(KBD_DOWN); + if (keys & SC_SHIFT) tk_key_down(KBD_LSHIFT); + + /* Порядок ровно как в главном цикле sprpop.c (kid_heal и + * отрисовка выпадают — они ничего не решают). pop_loose_tick + * оставлен: он не рисование, а МЕХАНИКА — досчитывает тряску и + * убирает провалившуюся плиту из карты, то есть меняет то, по чему + * физика в следующем кадре принимает решения. */ + pop_ctrl_tick(); + kid_tick(); + pop_phys_tick(); + pop_loose_tick(); + + sc_record(); + done++; + if (sc_len >= SC_TRACE_MAX) break; + } + return done; +} + +/* ---- сверка с эталоном ---------------------------------------------- */ + +void sc_expect(const uint8_t *golden, uint8_t nrec) +{ + uint8_t i; + const uint8_t *g = golden; + tc_check((int16_t)sc_len, (int16_t)nrec, "sc_len", __LINE__); + for (i = 0; i < nrec && i < sc_len; i++, g += SC_REC_BYTES) { + const sc_rec *r = &sc_trace[i]; + /* Номер кадра идёт первым полем каждой проверки: в отчёте видно, + * на каком именно кадре трасса разошлась. */ + tc_check((int16_t)r->frame, (int16_t)g[0], "frame", i); + tc_check((int16_t)r->x, (int16_t)g[1], "x", i); + tc_check((int16_t)r->y, (int16_t)g[2], "y", i); + tc_check((int16_t)r->dir, (int16_t)(int8_t)g[3], "dir", i); + tc_check((int16_t)r->col, (int16_t)(int8_t)g[4], "col", i); + tc_check((int16_t)r->row, (int16_t)(int8_t)g[5], "row", i); + tc_check((int16_t)r->action, (int16_t)g[6], "action", i); + tc_check((int16_t)r->seq, + (int16_t)((uint16_t)g[7] | ((uint16_t)g[8] << 8)), "seq", i); + } +} + +/* ---- снятие эталона -------------------------------------------------- */ + +static void put(char c) +{ + if (tc_result.logn < TC_LOGSZ) tc_result.log[tc_result.logn++] = (uint8_t)c; +} + +static void put_byte(uint8_t v) +{ + static const char hex[] = "0123456789ABCDEF"; + put(hex[v >> 4]); put(hex[v & 15]); +} + +void sc_dump(const char *name) +{ + uint8_t i; + tc_result.magic = TC_MAGIC; + tc_result.total = 1; + tc_result.failed = 1; /* иначе раннер лог не напечатает */ + put('#'); put('#'); put('#'); put(' '); + while (*name) put(*name++); + put('\n'); + for (i = 0; i < sc_len; i++) { + const sc_rec *r = &sc_trace[i]; + put_byte(r->frame); + put_byte(r->x); + put_byte(r->y); + put_byte((uint8_t)r->dir); + put_byte((uint8_t)r->col); + put_byte((uint8_t)r->row); + put_byte(r->action); + put_byte((uint8_t)r->seq); + put_byte((uint8_t)(r->seq >> 8)); + put('\n'); + } +} diff --git a/applications/SprPoP/tests/host/scene.h b/applications/SprPoP/tests/host/scene.h new file mode 100644 index 0000000..ba2dafd --- /dev/null +++ b/applications/SprPoP/tests/host/scene.h @@ -0,0 +1,102 @@ +/* + * scene.h — покадровый сценарный раннер для тестов физики Кида. + * + * Почему покадровый, а не «вызвать функцию и сверить возврат». Порт + * seg005/seg006 — это конечный автомат: диспетчер выбирает + * последовательность, интерпретатор проигрывает её кадр за кадром, физика + * между кадрами двигает персонажа и решает, не пора ли падать. Ошибка + * почти всегда проявляется не в одной функции, а в РАСХОЖДЕНИИ ТРАЕКТОРИИ + * через несколько кадров. Плюс главный риск ближайшего рефакторинга + * (Kid -> Char) — не арифметика, а то, КТО владеет окном Char внутри кадра; + * поймать это можно только прогоном полного кадра, как в главном цикле: + * + * pop_ctrl_tick(); // ввод -> control(): смена последовательности + * kid_tick(); // play_seq: следующий кадр + * pop_phys_tick(); // падение/приземление/стена + * + * Отсюда формат: сцена (комната + стартовая поза) + скрипт ввода, на + * выходе — трасса состояния Кида по кадрам. Сравнение трассы с эталоном + * и есть тест. + */ +#ifndef POP_TESTS_SCENE_H +#define POP_TESTS_SCENE_H + +#include + +/* Одна запись трассы: то, чем оперируют решения движка. curr_seq — + * СМЕЩЕНИЕ от SEQTBL_BASE (адрес зависит от базы, смещение — нет). */ +typedef struct { + uint8_t frame; + uint8_t x, y; + int8_t dir; + int8_t col, row; + uint8_t action; + uint16_t seq; +} sc_rec; + +#define SC_TRACE_MAX 48 +extern sc_rec sc_trace[SC_TRACE_MAX]; +extern uint8_t sc_len; + +/* Кнопки скрипта ввода — битовая маска на кадр. */ +#define SC_L 0x01 +#define SC_R 0x02 +#define SC_U 0x04 +#define SC_D 0x08 +#define SC_SHIFT 0x10 + +/* Инициализация окружения набора: таблицы анимации, обнуление состояния. + * Звать один раз в начале прогона. */ +void sc_init(void); + +/* Задать комнату: fg[30] — коды тайлов по рядам (row*10+col), room — + * номер (нужен trob'ам). Модификаторы обнуляются; ставить их тест может + * сам через tk_modif. Соседи комнаты — стены (изолированная сцена). */ +void sc_room(const uint8_t *fg, uint8_t room); + +/* Поставить Кида: колонка/ряд задают позицию по центру тайла, как это + * делает do_startpos (seg003) — так сцена описывается в терминах карты, а + * не в пикселях. */ +void sc_kid_at(uint8_t col, uint8_t row, int8_t dir); + +/* То же, но с точным X: тайл задаёт ряд и пол под ногами, X — фазу внутри + * тайла (нужно, когда исход зависит от того, где именно Кид стоит). */ +void sc_kid_at_x(uint8_t col, uint8_t row, uint8_t x, int8_t dir); + +/* ---- Персонажи слота Guard (страж/скелет/тень/визирь) ---------------- * + * Физика у них ОБЩАЯ с Кидом (одни функции над Char, разные окна), поэтому + * тестировать её надо на обоих слотах. Поднимаем персонажа руками, как + * enter_guard (seg002:0112): данных уровня под тестом нет. + * charid — CHARID_2_GUARD / CHARID_1_SHADOW / CHARID_4_SKELETON; + * hp — сколько HP выдать (guardhp_max = guardhp_curr = hp). */ +void sc_guard_at(uint8_t charid, uint8_t col, uint8_t row, int8_t dir, uint8_t hp); + +/* Прогнать n кадров ПЕРСОНАЖА СЛОТА GUARD — порт play_guard_frame без ИИ: + * окно loadshad → load_fram_det_col → play_seq → saveshad, затем + * pop_guard_phys_tick(). ИИ (guards.c, банк 1) в набор не линкуется: он + * решает «куда идти», а тут проверяется «что с ним делает физика». */ +void sc_guard_run(uint8_t n); + +/* Прогнать n кадров с ЗАЖАТОЙ маской keys, записывая трассу. + * Возвращает число записанных кадров (обрезается по SC_TRACE_MAX). */ +uint8_t sc_run(uint8_t keys, uint8_t n); + +/* Сбросить трассу (перед новым сценарием). */ +void sc_trace_clear(void); + +/* Сверить трассу с эталоном: golden — плоский массив по 8 байт на кадр + * (frame,x,y,dir,col,row,action,seq_lo,seq_hi — 9), nrec записей. + * Расхождения уходят в отчёт tcheck с номером кадра и поля. */ +void sc_expect(const uint8_t *golden, uint8_t nrec); + +#define SC_REC_BYTES 9 + +/* Снятие эталона. Лог tcheck всего 768 байт — на все сценарии сразу трасс + * не хватит, поэтому за прогон снимается РОВНО ОДИН сценарий (его номер + * задаёт -DSC_RECORD=), а перебором занимается record_golden.py. + * Формат: строка «### имя», дальше по строке на кадр, по два хекс-символа + * на байт. Раннер печатает лог только при провале — sc_dump ставит + * failed=1 намеренно. */ +void sc_dump(const char *name); + +#endif diff --git a/applications/SprPoP/tests/host/stubs.c b/applications/SprPoP/tests/host/stubs.c new file mode 100644 index 0000000..93b5b62 --- /dev/null +++ b/applications/SprPoP/tests/host/stubs.c @@ -0,0 +1,413 @@ +/* + * stubs.c — окружение движка для прогона под ucsim_z80. + * + * Под тестом крутится НАСТОЯЩАЯ физика (pop_map) с настоящим + * интерпретатором последовательностей (pop_kid): это единственный способ + * проверить порт seg005/seg006, потому что вся его суть — в точных + * величинах кадров и seqtbl. А вот всё, что вокруг, в ucsim не существует: + * видеопамять, EMM-страницы, файлы, клавиатура. Здесь они заглушены. + * + * Три категории: + * 1. libc/libbgi (графика, память, файлы, клавиатура) — пустышки. Код + * отрисовки из pop_kid.c линкуется, но не вызывается. + * 2. Соседние модули движка (pop_bg/pop_trob/pop_level/pop_redraw) — + * минимальные модели: room_modif живёт здесь массивом, пометки + * перерисовки и падающие куски ЖУРНАЛИРУЮТСЯ (тест может проверить, + * что физика их дёрнула), уровень отдаёт стартовую комнату. + * 3. Состояние стража — просто данные: страж в тестах физики не тикает, + * но pop_map/pop_ctrl на его поля смотрят. + * + * Клавиатура сделана НЕ пустышкой: `tk_keys` — набор «нажатых» скан-кодов, + * и `kbd_raw_down` отвечает по нему. Так под тест попадают и read_input, и + * read_user_control, то есть весь путь ввода, а не только диспетчер. + */ +#include +#include +#include +#include +#include +#include +#include "pop_qsave.h" /* из src/: ENGINE_DIR стоит в -I */ + +/* Сериализаторы приватного состояния входят в те же крупные модули, что и + * физика/ИИ. Сами QuickSave-тесты работают на target с EMM; обычным + * ucsim-сценам нужны только символы, и вызывать их они не должны. */ +void pop_qs_bytes(pop_qs_io_t *io, void *p, uint16_t n) +{ (void)io; (void)p; (void)n; } +void pop_qs_u8(pop_qs_io_t *io, uint8_t *p) { (void)io; (void)p; } +void pop_qs_i8(pop_qs_io_t *io, int8_t *p) { (void)io; (void)p; } +void pop_qs_u16(pop_qs_io_t *io, uint16_t *p) { (void)io; (void)p; } +void pop_qs_i16(pop_qs_io_t *io, int *p) { (void)io; (void)p; } + +#include "pop_kid.h" +#include "pop_guard.h" +#include "pop_bg.h" +#include "pop_map.h" +#include "pop_trob.h" +#include "pop_level.h" +#include "pop_redraw.h" +#include "pop_pace.h" +#include "pop_sfx.h" +#include "_pop_draw.h" +#include "pop_cdraw.h" +#include "stubs.h" +#include "kidblob.h" + +/* ---- 1. libc / libbgi ---------------------------------------------- */ + +uint8_t _gfx_visible_page, _gfx_draw_page, _gfx_bank; +/* ISR-стаб W0-страницы: продукт берёт его АДРЕС и вписывает в страницу. + * Здесь достаточно, чтобы символ существовал. */ +void _gfx_w0_isr(void) { } + +void gfx_w0_map(uint8_t page) { (void)page; } +void gfx_w0_unmap(void) { } +void gfx_w0_page_prepare(uint8_t page) { (void)page; } + +void gfx_blit_cols(int x, int y, const void *img, uint8_t flip) +{ (void)x; (void)y; (void)img; (void)flip; } +void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip, + int skip, int rows) +{ (void)x; (void)y; (void)img; (void)flip; (void)skip; (void)rows; } +void gfx_blit_cols_part_w(int x, int y, const void *img, uint8_t flip, + int skip, int rows, uint8_t maxw) +{ (void)x; (void)y; (void)img; (void)flip; (void)skip; (void)rows; (void)maxw; } +void gfx_blit_cols_part_noclip(int x, int y, const void *img, uint8_t flip, + uint8_t skip, uint8_t rows) +{ (void)x; (void)y; (void)img; (void)flip; (void)skip; (void)rows; } + +int atlas_load(atlas_t *a, const char *path) { (void)a; (void)path; return -1; } +void atlas_free(atlas_t *a) { (void)a; } +const void *atlas_image(const atlas_t *a, uint8_t idx) +{ (void)a; (void)idx; return 0; } + +/* EMM: страниц нет, но блок обязан выделиться — иначе pop_kid_data_load + * сдастся на первом же шаге и молча оставит kdat_ok = 0, а play_seq тогда + * просто ничего не делает (и трасса выходит из одного застывшего кадра). */ +uint8_t mem_alloc_pages_bios(uint8_t n) { (void)n; return 1; } +void mem_free_block_bios(uint8_t blk) { (void)blk; } +uint8_t mem_get_page_bios(uint8_t blk, uint8_t idx) { (void)blk; (void)idx; return 1; } + +/* Файловая система: единственный «файл» — таблицы анимации. Сделано не + * пустышкой, а рабочим чтением, чтобы pop_kid_data_load отработал целиком + * (включая правку ISR-стаба страницы) и сам взвёл свой kdat_ok — тест не + * лезет во внутренние статики продукта. Данные, по которым потом ходят + * аксессоры, кладёт kid_blob_install по KID_BLOB_ADDR: на устройстве туда + * же их приводит маппинг W0. */ +int open(const char *p, int f) { (void)p; (void)f; return 3; } + +int read(int fd, void *b, int n) +{ + unsigned char *d = (unsigned char *)b; + const unsigned char *s = kid_blob; + unsigned int i, cnt = kid_blob_size; + if (fd != 3) return -1; + if ((unsigned int)n < cnt) cnt = (unsigned int)n; + for (i = 0; i < cnt; i++) d[i] = s[i]; + return (int)cnt; +} + +int close(int fd) { (void)fd; return 0; } + +int bank_load_file(uint8_t page, uint16_t off, const char *path, uint16_t max) +{ + (void)page; (void)off; (void)path; + return kid_blob_size <= max ? (int)kid_blob_size : (int)max; +} + +/* ---- клавиатура: управляемая тестом --------------------------------- */ + +uint16_t tk_keys[TK_KEYS_MAX]; + +int kbd_raw_open(void) { return 0; } +void kbd_raw_close(void) { } +void kbd_raw_sync(void) { } + +uint8_t kbd_raw_down(uint16_t code) +{ + uint8_t i; + for (i = 0; i < TK_KEYS_MAX; i++) + if (tk_keys[i] == code) return 1; + return 0; +} + +uint8_t kbd_raw_any_down(void) +{ + uint8_t i; + for (i = 0; i < TK_KEYS_MAX; i++) if (tk_keys[i]) return 1; + return 0; +} + +void tk_keys_clear(void) +{ + uint8_t i; + for (i = 0; i < TK_KEYS_MAX; i++) tk_keys[i] = 0; +} + +void tk_key_down(uint16_t code) +{ + uint8_t i; + for (i = 0; i < TK_KEYS_MAX; i++) + if (tk_keys[i] == 0) { tk_keys[i] = code; return; } +} + +/* Темп кадра и звук относятся к окружению реальной машины. Физика и + * управление только оставляют выборки луча и заявки эффектов; host-тесты + * проверяют их игровую логику, поэтому здесь достаточно no-op контрактов. */ +void pop_beam_sample(void) __preserves_regs(b, c, d, e) { } +void pop_sfx_play(uint8_t id) { (void)id; } +/* Курсор насоса: в тестах звука нет вовсе, значит «ничего не звучит» — и + * автомат вступительной темы первого уровня сразу отпускает управление. */ +volatile uint16_t pop_mus_left; +void pop_sfx_stop(void) { } + +/* ---- 2. соседние модули движка -------------------------------------- */ + +/* room_modif текущей комнаты: пики (счётчик фазы), ворота (высота), + * зелья (тип). Физика читает и пишет его напрямую через pop_trob_modif. */ +uint8_t tk_modif[30]; +uint8_t tk_start_room; + +uint8_t *pop_trob_modif(uint8_t room) __banked { (void)room; return tk_modif; } +uint8_t pop_level_start_room(void) __banked { return tk_start_room; } + +/* Журнал: что физика попросила у соседей. Тест сверяет ФАКТ и параметры + * (какой тайл помечен, где заспавнен кусок) — заглушка-пустышка этого не + * дала бы, а именно такие баги (BUG-GATE-ANIM-1) мы и ловим. */ +tk_log_t tk_log; + +static void log_put(uint8_t kind, uint8_t a, uint8_t b, uint8_t c) +{ + if (tk_log.n >= TK_LOG_MAX) { tk_log.overflow = 1; return; } + tk_log.e[tk_log.n].kind = kind; + tk_log.e[tk_log.n].a = a; + tk_log.e[tk_log.n].b = b; + tk_log.e[tk_log.n].c = c; + tk_log.n++; +} + +void tk_log_clear(void) { tk_log.n = 0; tk_log.overflow = 0; } + +uint8_t tk_log_count(uint8_t kind) +{ + uint8_t i, c = 0; + for (i = 0; i < tk_log.n; i++) if (tk_log.e[i].kind == kind) c++; + return c; +} + +void pop_add_trob(uint8_t room, uint8_t tilepos, int8_t type) __banked +{ log_put(TK_TROB, room, tilepos, (uint8_t)type); } + +/* Чомперы будит смена ряда (SEQ_UP/SEQ_DOWN, падение, приземление) — в + * хост-тестах физики их состояние не проверяется, достаточно не падать. */ +void pop_start_chompers(uint8_t room, int8_t row) __banked +{ (void)room; (void)row; } + +void pop_trigger_button(uint8_t room, uint8_t tilepos, + uint8_t button_type, uint8_t modifier, + uint8_t snd) __banked +{ (void)modifier; (void)snd; log_put(TK_BUTTON, room, tilepos, button_type); } + +void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked +{ log_put(TK_REDRAW, tilepos, kind, pages); } + +void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked +{ log_put(TK_REDRAW_ABOVE, col, kind, pages); } + +void pop_loose_mob_spawn(int row, int col) __banked +{ log_put(TK_MOB_SPAWN, (uint8_t)row, (uint8_t)col, 0); } + +void pop_loose_mob_tick(void) __banked { } +void pop_loose_mob_room_changed(void) __banked { } + +uint8_t pop_loose_mob_pos(int *col, int *y) __banked +{ (void)col; (void)y; return 0; } + +/* Отрисовочные хвосты, которые физика дёргает напрямую. */ +void pop_fore_set_clip(int x, int y, int w, int h) +{ (void)x; (void)y; (void)w; (void)h; } +void pop_clip_sprite(int x, int w, int sy_top, int h) __banked +{ (void)x; (void)w; (void)sy_top; (void)h; } +void pop_heal_fast(int x, int y, int w, int h) +{ (void)x; (void)y; (void)w; (void)h; } + +/* ---- 3. страж ------------------------------------------------------- * + * Само состояние (Guard, guardhp_*, can_guard_see_kid, is_guard_notice…) и + * pop_take_hp даёт НАСТОЯЩИЙ pop_guard.c — он линкуется в набор. Здесь + * только то, что он тянет из уровня: данных уровня под тестом нет, страж + * поднимается вручную (tk_guard_spawn в scene.c). */ + +uint8_t pop_current_level; /* pop_level.c под тест не линкуется; sc_init ставит 1 */ + +/* Цвет стража — подмена палитры, живёт в pop_cdraw (отрисовка, в набор не + * линкуется). Здесь важен только факт вызова из pop_guard_enter. */ +void pop_guard_set_palette(uint8_t color) __banked { (void)color; } + +/* Габарит последнего нарисованного кадра по слотам (pop_cdraw.c): физика + * читает из него char_width_half. Под тестом никто не рисует, поэтому все + * поля остаются нулями — ровно как было до DRAW-CHAR, когда kid_fp_width() + * возвращал 0. Тест, которому ширина важна, выставляет её сам. */ +pop_cdraw_t pop_cd[POP_CH_N]; + +/* Связи комнат: нужны check_guard_fallout (возрождение скелета в комнате + * снизу). Тест задаёт их через tk_link_down. */ +uint8_t tk_link_down; +uint8_t pop_room_link(uint8_t room, uint8_t side) +{ (void)room; return side == 3 ? tk_link_down : 0; } + +int8_t pop_level_guard_type(uint8_t level) __banked { (void)level; return 0; } +uint8_t pop_level_guard_hp(uint8_t level) __banked { (void)level; return 3; } + +/* Тайлы «уровня» для соседних комнат: pop_map_set_edges забирает fg соседей + * слева/справа сам (кэш на входе в комнату), значит тесту нужен источник. + * Заполняет сам тест — по умолчанию комнат нет и всё стена. */ +uint8_t tk_room_fg[TK_ROOMS + 1][30]; /* индекс = НОМЕР комнаты (1..TK_ROOMS) */ +void pop_level_access_begin(void) { } +void pop_level_access_end(void) { } +uint8_t pop_level_tile_raw(uint8_t room, uint8_t tilepos) +{ + if (room == 0 || room > TK_ROOMS || tilepos >= 30) return 20; /* стена */ + return tk_room_fg[room][tilepos]; +} + +/* pop_level_tile (то же самое, но с маппингом окна W0 внутри) — её зовёт + * спецсобытие подъёма скелета из guards.c; тому единственному набору, где + * guards.c линкуется (t_mouse), достаточно чтения из tk_room_fg. */ +uint8_t pop_level_tile(uint8_t room, uint8_t tilepos) +{ + return pop_level_tile_raw(room, tilepos); +} + +/* Запись в «данные уровня» — у теста это тот же tk_room_fg. Нужна с тех + * пор, как do_pickup правит страницу уровня сразу (BUG-POTION-STRIPE). */ +void pop_level_set_tile(uint8_t room, uint8_t tilepos, uint8_t tile) +{ + if (room == 0 || room > TK_ROOMS || tilepos >= 30) return; + tk_room_fg[room][tilepos] = tile; +} + +/* Живое состояние стражей по комнатам — как gstate в pop_level.c, только на + * 8 комнат и без данных уровня: тесту нужно ровно то, на что смотрит + * follow_guard (есть ли в целевой комнате свой страж и не труп ли он). + * tk_guard_room != 0 — комната, в которой «по данным уровня» страж есть. */ +uint8_t tk_guard_room; +uint8_t tk_gs_tile[TK_ROOMS]; /* 30 = стража нет (как в pop_level) */ +uint16_t tk_gs_seq[TK_ROOMS]; /* != 0 — запомнен ТРУП */ + +void tk_guard_state_clear(void) +{ + uint8_t i; + for (i = 0; i < TK_ROOMS; i++) { tk_gs_tile[i] = 30; tk_gs_seq[i] = 0; } + tk_guard_room = 0; +} + +/* ВНИМАНИЕ на форму возврата: результат собирается в res и отдаётся ОДНИМ + * выходом. С `return 1;` прямо в ветке SDCC 4.5 не кладёт единицу в A — + * функция возвращает мусор (docs/bugs/sdcc-z80-ret-const-lost). Без + * __banked баг маскировался: в A случайно оставался ненулевой байт от + * `*tile = 11`, и проверка «истина» проходила. */ +uint8_t pop_level_guard(uint8_t room, uint8_t *tile, int8_t *dir, + uint8_t *color, uint8_t *skill) __banked +{ + uint8_t res = 0; + if (room < 1 || room > TK_ROOMS) return 0; + if (tk_gs_tile[room - 1] < 30) { /* запомненный (в т.ч. труп) */ + *tile = tk_gs_tile[room - 1]; *dir = 0; *color = 0; *skill = 1; + res = 1; + } else if (room == tk_guard_room) { /* страж из данных уровня */ + *tile = 11; *dir = 0; *color = 0; *skill = 1; + res = 1; + } + return res; +} +void pop_guard_state_save(uint8_t room, uint8_t tile, int8_t dir, uint8_t x, + uint8_t skill, uint16_t seq, uint8_t dead) +{ + (void)dir; (void)x; (void)skill; + if (room < 1 || room > TK_ROOMS) return; + tk_gs_tile[room - 1] = tile; + tk_gs_seq[room - 1] = dead ? seq : 0; + if (tile >= 30) tk_guard_room = (tk_guard_room == room) ? 0 : tk_guard_room; +} +uint16_t pop_guard_state_seq(uint8_t room) +{ return (room >= 1 && room <= TK_ROOMS) ? tk_gs_seq[room - 1] : 0; } +uint8_t pop_guard_state_x(uint8_t room) { (void)room; return 0; } + +/* Таблицы деления на ширину тайла — настоящие (резидент pop_tile.c в тест + * не линкуется). Не заглушка: физика считает по ним колонку, подменять + * нельзя. Значения сгенерированы той же формулой, что в pop_tile.c. */ +const int8_t POP_TILE_DIV[256] = { + -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, +}; +const uint8_t POP_TILE_MOD[256] = { + 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, + 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, + 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, +}; + +/* Тайлсет (0 подземелье, 1 дворец) — в приложении его ставит pop_bg_load; + * тесты гоняют физику подземелья, поэтому 0. Не заглушка-функция, а та же + * переменная, что и в резиденте: карта читает её напрямую. */ +uint8_t pop_palace; + +/* Отражение в зеркале (уровень 4) — чистая графика, в тестах физики её нет. + * pop_map зовёт pop_mirror_draw из pop_check_mirror, поэтому символ нужен. */ +void pop_mirror_draw(int clip_top) __banked { (void)clip_top; } +void pop_mirror_heal(void) __banked { } + +/* ---- Зеркальный набор персонажей (зелье инверсии) -------------------- * + * В host-тестах графики нет: pop_kboot линкуется ради pop_kid_data_load, а + * его хвост (загрузка *_v.atl) заглушен — проверять там нечего, атласов в + * тесте не существует вовсе. */ +uint8_t pop_vflip_ready(void) { return 1; } /* «уже загружено» — грузить не станет */ +int pop_vflip_add(const void *orig, const char *path) +{ (void)orig; (void)path; return 0; } +int pop_vflip_take(const void *orig, const void *mirror) +{ (void)orig; (void)mirror; return 0; } + +/* Архив ресурсов: на устройстве из него читаются атласы Кида, в тестах + * графики нет вовсе — pop_kboot линкуется только ради pop_kid_data_load, + * а тот по-прежнему ходит в файл (заглушка open/read выше). */ +signed char pop_arc_open(void *a, const char *path, unsigned char *tbl, + unsigned char max) __banked +{ (void)a; (void)path; (void)tbl; (void)max; return -1; } +signed char pop_arc_open_id(void *a, unsigned char id, unsigned char *tbl, + unsigned char max) __banked +{ (void)a; (void)id; (void)tbl; (void)max; return -1; } +signed char pop_arc_atlas(void *a, const unsigned char *tbl, + unsigned char idx, void *at) __banked +{ (void)a; (void)tbl; (void)idx; (void)at; return -1; } +void pop_arc_close(void *a) __banked { (void)a; } +uint8_t pop_vflip_page(uint8_t src) { return src; } +void pop_vflip_reset(void) { } +int pop_sword_take(const atlas_t *a) __banked { (void)a; return 0; } +int pop_sword_vflip_take(const atlas_t *m) __banked { (void)m; return 0; } +int pop_guard_vflip_load(void) __banked { return 0; } diff --git a/applications/SprPoP/tests/host/stubs.h b/applications/SprPoP/tests/host/stubs.h new file mode 100644 index 0000000..ed71b8e --- /dev/null +++ b/applications/SprPoP/tests/host/stubs.h @@ -0,0 +1,61 @@ +/* + * stubs.h — интерфейс тестового окружения (см. stubs.c). + * + * Что тест может задавать: «нажатые» клавиши (tk_key_down) и модификаторы + * тайлов комнаты (tk_modif). Что может проверять: журнал обращений + * физики к соседним модулям (tk_log). + */ +#ifndef POP_TESTS_STUBS_H +#define POP_TESTS_STUBS_H + +#include + +/* ---- клавиатура ------------------------------------------------------ */ +#define TK_KEYS_MAX 6 +extern uint16_t tk_keys[TK_KEYS_MAX]; +void tk_keys_clear(void); +void tk_key_down(uint16_t code); + +/* ---- комната --------------------------------------------------------- */ +extern uint8_t tk_modif[30]; /* room_modif текущей комнаты */ +extern uint8_t tk_start_room; /* что вернёт pop_level_start_room */ + +/* ---- журнал обращений ------------------------------------------------ */ +#define TK_LOG_MAX 24 + +enum { + TK_TROB = 1, /* a=room b=tilepos c=type */ + TK_BUTTON, /* a=room b=tilepos c=type */ + TK_REDRAW, /* a=tilepos b=kind c=pages */ + TK_REDRAW_ABOVE, /* a=col b=kind c=pages */ + TK_MOB_SPAWN /* a=row b=col */ +}; + +typedef struct { uint8_t kind, a, b, c; } tk_log_e; +typedef struct { uint8_t n, overflow; tk_log_e e[TK_LOG_MAX]; } tk_log_t; + +extern tk_log_t tk_log; +void tk_log_clear(void); + +/* Сколько записей данного вида в журнале (0 = не было). */ +uint8_t tk_log_count(uint8_t kind); + + +/* Состояние стражей по комнатам для follow_guard (см. stubs.c). */ +/* Комнат в таблице «уровня». 24 = столько же, сколько в настоящем уровне + * PoP: спецсобытия поздних уровней адресуют комнаты по их РЕАЛЬНЫМ номерам + * (меч тени лежит в 15-й, уход за ним — из 18-й), и обрезать их до восьми + * значило бы тестировать не тот адрес. Цена — 720 байт тестового ОЗУ. */ +#define TK_ROOMS 24 +extern uint8_t tk_guard_room; /* комната, где страж есть «по данным уровня» */ +extern uint8_t tk_gs_tile[TK_ROOMS]; +extern uint16_t tk_gs_seq[TK_ROOMS]; +void tk_guard_state_clear(void); + +/* Связь «вниз» для check_guard_fallout (скелет возрождается в комнате 3). */ +extern uint8_t tk_link_down; + +/* fg комнат «уровня» — источник для кэша соседей в pop_map_set_edges. */ +extern uint8_t tk_room_fg[TK_ROOMS + 1][30]; /* индекс = номер комнаты */ + +#endif diff --git a/applications/SprPoP/tests/host/t_app.c b/applications/SprPoP/tests/host/t_app.c new file mode 100644 index 0000000..997ac99 --- /dev/null +++ b/applications/SprPoP/tests/host/t_app.c @@ -0,0 +1,135 @@ +/* pop_app — протокол верхнего автомата: допустимые и запрещённые переходы. */ +#include "tcheck.h" +#include "pop_app.h" + +TC_TEST(boot_to_title) +{ + pop_app_reset(); + TC_EQ(pop_app_state, POP_APP_BOOT); + TC_TRUE(pop_app_dispatch(POP_APP_EV_BOOT_READY)); + TC_EQ(pop_app_state, POP_APP_BUILD_INFO); + TC_TRUE(pop_app_dispatch(POP_APP_EV_BUILD_DONE)); + TC_EQ(pop_app_state, POP_APP_TITLE); +} + +TC_TEST(level_load_roundtrip) +{ + pop_app_reset(); + pop_app_dispatch(POP_APP_EV_BOOT_READY); + pop_app_dispatch(POP_APP_EV_BUILD_DONE); + TC_TRUE(pop_app_dispatch(POP_APP_EV_NEW_GAME)); + TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); + TC_TRUE(pop_app_dispatch(POP_APP_EV_LEVEL_READY)); + TC_EQ(pop_app_state, POP_APP_PLAYING); + TC_TRUE(pop_app_dispatch(POP_APP_EV_PAUSE)); + TC_EQ(pop_app_state, POP_APP_PAUSE_MENU); + TC_TRUE(pop_app_dispatch(POP_APP_EV_RESUME)); + TC_EQ(pop_app_state, POP_APP_PLAYING); + TC_TRUE(pop_app_dispatch(POP_APP_EV_LEVEL_FINISHED)); + TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); + TC_TRUE(pop_app_dispatch(POP_APP_EV_LEVEL_READY)); + TC_EQ(pop_app_state, POP_APP_PLAYING); +} + +TC_TEST(rejects_wrong_event) +{ + pop_app_reset(); + TC_FALSE(pop_app_dispatch(POP_APP_EV_LEVEL_READY)); + TC_EQ(pop_app_state, POP_APP_BOOT); + TC_TRUE(pop_app_dispatch(POP_APP_EV_QUIT)); + TC_EQ(pop_app_state, POP_APP_QUIT); + TC_FALSE(pop_app_dispatch(POP_APP_EV_BOOT_READY)); +} + +TC_TEST(future_routes_are_declared) +{ + pop_app_reset(); + pop_app_state = POP_APP_TITLE; + TC_TRUE(pop_app_dispatch(POP_APP_EV_NEW_GAME)); + TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); + TC_TRUE(pop_app_dispatch(POP_APP_EV_LEVEL_READY)); + TC_EQ(pop_app_state, POP_APP_PLAYING); + TC_TRUE(pop_app_dispatch(POP_APP_EV_TIME_EXPIRED)); + TC_EQ(pop_app_state, POP_APP_TIME_EXPIRED); + TC_TRUE(pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)); + TC_EQ(pop_app_state, POP_APP_TITLE); +} + +TC_TEST(prelevel_cutscene_route) +{ + pop_app_state = POP_APP_PLAYING; + TC_TRUE(pop_app_dispatch(POP_APP_EV_CUTSCENE_STARTED)); + TC_EQ(pop_app_state, POP_APP_CUTSCENE); + TC_TRUE(pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)); + TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); +} + +TC_TEST(demo_routes) +{ + pop_app_reset(); + pop_app_dispatch(POP_APP_EV_BOOT_READY); + pop_app_dispatch(POP_APP_EV_BUILD_DONE); + TC_TRUE(pop_app_dispatch(POP_APP_EV_INTRO_STARTED)); + TC_EQ(pop_app_state, POP_APP_INTRO); + TC_TRUE(pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)); + TC_EQ(pop_app_state, POP_APP_DEMO); + TC_TRUE(pop_app_dispatch(POP_APP_EV_DEMO_FINISHED)); + TC_EQ(pop_app_state, POP_APP_TITLE); + TC_TRUE(pop_app_dispatch(POP_APP_EV_DEMO_STARTED)); + TC_TRUE(pop_app_dispatch(POP_APP_EV_NEW_GAME)); + TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); +} + +TC_TEST(intro_skip_starts_new_game) +{ + pop_app_reset(); + pop_app_dispatch(POP_APP_EV_BOOT_READY); + pop_app_dispatch(POP_APP_EV_BUILD_DONE); + pop_app_dispatch(POP_APP_EV_INTRO_STARTED); + TC_EQ(pop_app_state, POP_APP_INTRO); + TC_TRUE(pop_app_dispatch(POP_APP_EV_NEW_GAME)); + TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); +} + +TC_TEST(restart_game_returns_to_intro) +{ + pop_app_state = POP_APP_PLAYING; + TC_TRUE(pop_app_dispatch(POP_APP_EV_PAUSE)); + TC_EQ(pop_app_state, POP_APP_PAUSE_MENU); + TC_TRUE(pop_app_dispatch(POP_APP_EV_RESTART_INTRO)); + TC_EQ(pop_app_state, POP_APP_TITLE); +} + +/* Финал уходит в таблицу рекордов: она же дослушивает победную тему и + * гасит экран, как хвост end_sequence (seg001:5F1). */ +TC_TEST(ending_to_title_route) +{ + pop_app_state = POP_APP_PLAYING; + TC_TRUE(pop_app_dispatch(POP_APP_EV_ENDING_STARTED)); + TC_EQ(pop_app_state, POP_APP_ENDING); + TC_TRUE(pop_app_dispatch(POP_APP_EV_ENDING_FINISHED)); + TC_EQ(pop_app_state, POP_APP_HALL_OF_FAME); + TC_TRUE(pop_app_dispatch(POP_APP_EV_HOF_FINISHED)); + TC_EQ(pop_app_state, POP_APP_TITLE); +} + +TC_TEST(hof_route_still_exits_to_title) +{ + pop_app_state = POP_APP_HALL_OF_FAME; + TC_TRUE(pop_app_dispatch(POP_APP_EV_HOF_FINISHED)); + TC_EQ(pop_app_state, POP_APP_TITLE); +} + +void main(void) +{ + TC_RUN(boot_to_title); + TC_RUN(level_load_roundtrip); + TC_RUN(rejects_wrong_event); + TC_RUN(future_routes_are_declared); + TC_RUN(prelevel_cutscene_route); + TC_RUN(demo_routes); + TC_RUN(intro_skip_starts_new_game); + TC_RUN(restart_game_returns_to_intro); + TC_RUN(ending_to_title_route); + TC_RUN(hof_route_still_exits_to_title); +} diff --git a/applications/SprPoP/tests/host/t_cfg.c b/applications/SprPoP/tests/host/t_cfg.c new file mode 100644 index 0000000..761648b --- /dev/null +++ b/applications/SprPoP/tests/host/t_cfg.c @@ -0,0 +1,197 @@ +/* + * t_cfg — полный codec POP.CFG: заголовок, размер, checksum и payload. + * + * DSS/EMM под host не живут, но pure codec лежит в том же pop_config.c, что + * и target-I/O. Поэтому проверяем весь формат, а не только payload. + */ +#include +#include + +#include "tcheck.h" +#include "pop_settings.h" +#include "pop_pace.h" /* POP_SPEED_MODES */ +#include "_pop_config.h" + +/* Runtime-владельцы нужны только для линковки pop_settings_apply(); codec + * их не вызывает. */ +uint8_t pop_speed_mode, pop_cheats, pop_snd_want, pop_mus_want; +void pop_sfx_toggle(void) __banked { pop_snd_want = (uint8_t)!pop_snd_want; } + +static void set_all(uint8_t snd, uint8_t spd, uint8_t prof, uint8_t cheats, + uint8_t info, uint16_t flags, uint8_t time_limit, + uint8_t debug_bar) +{ + pop_settings.sound_enabled = snd; + pop_settings.speed_mode = spd; + pop_settings.gameplay_profile = prof; + pop_settings.cheats_enabled = cheats; + pop_settings.show_build_info = info; + pop_settings.enhancement_flags = flags; + pop_settings.time_limit_enabled = time_limit; + pop_settings.show_debug_bar = debug_bar; +} + +/* ser→parse должен возвращать ровно те же значения (кроме профиля: + * ENHANCED осознанно садится в VANILLA). */ +TC_TEST(cfg_roundtrip) +{ + uint8_t buf[POP_CFG_SIZE]; + set_all(1, POP_SPEED_FAST, POP_PROFILE_VANILLA, 1, 1, 0xBEEF, 0, 0); + pop_cfg_encode(buf); + memset(&pop_settings, 0, sizeof(pop_settings)); /* стереть */ + TC_TRUE(pop_cfg_decode(buf, sizeof(buf))); + TC_EQ(pop_settings.sound_enabled, 1); + TC_EQ(pop_settings.speed_mode, POP_SPEED_FAST); + TC_EQ(pop_settings.cheats_enabled, 1); + TC_EQ(pop_settings.show_build_info, 1); + TC_EQ(pop_settings.enhancement_flags, 0xBEEF); + TC_EQ(pop_settings.time_limit_enabled, 0); + TC_EQ(pop_settings.show_debug_bar, 0); +} + +/* ENHANCED из файла применяется как VANILLA (режим ещё не реализован). */ +TC_TEST(cfg_enhanced_downgrades_to_vanilla) +{ + uint8_t buf[POP_CFG_SIZE]; + set_all(1, POP_SPEED_NORMAL, POP_PROFILE_ENHANCED, 0, 0, 0x0003, 1, 1); + pop_cfg_encode(buf); + memset(&pop_settings, 0, sizeof(pop_settings)); + TC_TRUE(pop_cfg_decode(buf, sizeof(buf))); + TC_EQ(pop_settings.gameplay_profile, POP_PROFILE_VANILLA); + /* Остальные поля при этом применяются. */ + TC_EQ(pop_settings.enhancement_flags, 0x0003); +} + +/* Мусор в ЛЮБОМ поле => отказ без частичного применения. */ +TC_TEST(cfg_garbage_rejected_whole) +{ + uint8_t buf[POP_CFG_SIZE]; + uint8_t i; + + for (i = 0; i < POP_CFG_PAYLOAD_SIZE; i++) { + set_all(1, POP_SPEED_FASTEST, POP_PROFILE_VANILLA, 1, 0, 0x0001, 1, 1); + pop_cfg_encode(buf); + switch (i) { + case 0: buf[7 + i] = 2; break; /* sound > 1 */ + case 1: buf[7 + i] = (uint8_t)POP_SPEED_MODES; break; + case 2: buf[7 + i] = 2; break; /* profile > ENHANCED */ + case 3: buf[7 + i] = 2; break; /* cheats > 1 */ + case 4: buf[7 + i] = 2; break; /* buildinfo > 1 */ + case 7: buf[7 + i] = 2; break; /* timer > 1 */ + case 8: buf[7 + i] = 2; break; /* debug bar > 1 */ + default: buf[7 + i] = 0xFF; break; /* флаги — любые */ + } + /* Изменённый payload обязан иметь корректную checksum: иначе мы + * тестируем checksum, а не диапазон поля. */ + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE] = 0; + { + uint8_t j; + for (j = 0; j < POP_CFG_PAYLOAD_SIZE; j++) + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE] ^= buf[7 + j]; + } + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE + 1] = + (uint8_t)~buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE]; + if (i == 5 || i == 6) { /* flags не валидируются */ + memset(&pop_settings, 0, sizeof(pop_settings)); + TC_TRUE(pop_cfg_decode(buf, sizeof(buf))); + continue; + } + memset(&pop_settings, 0, sizeof(pop_settings)); + TC_FALSE(pop_cfg_decode(buf, sizeof(buf))); + /* Настройки не тронуты — нули после memset остались нулями. */ + TC_EQ(pop_settings.sound_enabled, 0); + TC_EQ(pop_settings.speed_mode, 0); + } +} + +TC_TEST(cfg_header_checksum_and_future_tail) +{ + uint8_t buf[POP_CFG_MAX_SIZE]; + uint8_t i, sum; + set_all(1, POP_SPEED_FAST, POP_PROFILE_VANILLA, 0, 1, 0xA55A, 1, 1); + pop_cfg_encode(buf); + + buf[0] = 'X'; + TC_FALSE(pop_cfg_decode(buf, POP_CFG_SIZE)); + pop_cfg_encode(buf); + buf[4] = 2; + TC_FALSE(pop_cfg_decode(buf, POP_CFG_SIZE)); + pop_cfg_encode(buf); + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE] ^= 1; + TC_FALSE(pop_cfg_decode(buf, POP_CFG_SIZE)); + pop_cfg_encode(buf); + TC_FALSE(pop_cfg_decode(buf, (uint16_t)(POP_CFG_SIZE - 1))); + + /* Будущий v1: два незнакомых байта перед checksum должны пропуститься. */ + pop_cfg_encode(buf); + buf[5] = POP_CFG_PAYLOAD_SIZE + 2; + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE] = 0x12; + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE + 1] = 0x34; + sum = 0; + for (i = 0; i < POP_CFG_PAYLOAD_SIZE + 2; i++) sum ^= buf[7 + i]; + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE + 2] = sum; + buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE + 3] = (uint8_t)~sum; + memset(&pop_settings, 0, sizeof(pop_settings)); + TC_TRUE(pop_cfg_decode(buf, (uint16_t)(POP_CFG_SIZE + 2))); + TC_EQ(pop_settings.enhancement_flags, 0xA55A); +} + +/* Созданный до FG4 файл (семь payload-байтов) остаётся валиден и обязан + * включить лимит: это исторический дефолт 60 минут, не значение в _DATA. */ +TC_TEST(cfg_legacy_enables_time_limit) +{ + uint8_t buf[POP_CFG_SIZE]; + uint8_t sum = 0, i; + + set_all(1, POP_SPEED_NORMAL, POP_PROFILE_VANILLA, 1, 0, 0, 0, 0); + pop_cfg_encode(buf); + buf[5] = POP_CFG_LEGACY_PAYLOAD_SIZE; + for (i = 0; i < POP_CFG_LEGACY_PAYLOAD_SIZE; i++) sum ^= buf[7 + i]; + buf[POP_CFG_HEADER_SIZE + POP_CFG_LEGACY_PAYLOAD_SIZE] = sum; + buf[POP_CFG_HEADER_SIZE + POP_CFG_LEGACY_PAYLOAD_SIZE + 1] = (uint8_t)~sum; + pop_settings.time_limit_enabled = 0; + TC_TRUE(pop_cfg_decode(buf, POP_CFG_MIN_SIZE)); + TC_EQ(pop_settings.time_limit_enabled, 1); +} + +/* Файл, записанный ДО тумблера отладочной полосы (восемь payload-байтов), + * обязан остаться валидным и включить полосу: это её исторический дефолт. */ +TC_TEST(cfg_legacy_enables_debug_bar) +{ + uint8_t buf[POP_CFG_SIZE]; + uint8_t sum = 0, i; + + set_all(1, POP_SPEED_NORMAL, POP_PROFILE_VANILLA, 1, 0, 0, 1, 0); + pop_cfg_encode(buf); + buf[5] = POP_CFG_TIMELIMIT_PAYLOAD_SIZE; + for (i = 0; i < POP_CFG_TIMELIMIT_PAYLOAD_SIZE; i++) sum ^= buf[7 + i]; + buf[POP_CFG_HEADER_SIZE + POP_CFG_TIMELIMIT_PAYLOAD_SIZE] = sum; + buf[POP_CFG_HEADER_SIZE + POP_CFG_TIMELIMIT_PAYLOAD_SIZE + 1] = (uint8_t)~sum; + pop_settings.show_debug_bar = 0; + TC_TRUE(pop_cfg_decode(buf, (uint16_t)(POP_CFG_HEADER_SIZE + + POP_CFG_TIMELIMIT_PAYLOAD_SIZE + + POP_CFG_TAIL_SIZE))); + TC_EQ(pop_settings.show_debug_bar, 1); + TC_EQ(pop_settings.time_limit_enabled, 1); +} + +TC_TEST(settings_apply_syncs_runtime) +{ + set_all(0, POP_SPEED_FASTEST, POP_PROFILE_VANILLA, 0, 0, 0, 1, 1); + pop_snd_want = 1; + pop_settings_apply(1); + TC_EQ(pop_speed_mode, POP_SPEED_FASTEST); + TC_EQ(pop_cheats, 0); + TC_EQ(pop_snd_want, 0); +} + +void main(void) +{ + TC_RUN(cfg_roundtrip); + TC_RUN(cfg_enhanced_downgrades_to_vanilla); + TC_RUN(cfg_garbage_rejected_whole); + TC_RUN(cfg_header_checksum_and_future_tail); + TC_RUN(cfg_legacy_enables_time_limit); + TC_RUN(cfg_legacy_enables_debug_bar); + TC_RUN(settings_apply_syncs_runtime); +} diff --git a/applications/SprPoP/tests/host/t_char.c b/applications/SprPoP/tests/host/t_char.c new file mode 100644 index 0000000..f213c60 --- /dev/null +++ b/applications/SprPoP/tests/host/t_char.c @@ -0,0 +1,525 @@ +/* + * t_char — физика над Char для РАЗНЫХ ПЕРСОНАЖЕЙ. + * + * Зачем отдельный набор. После GUARD-PHYS физика — один набор функций над + * глобалом `Char`, а кто именно в Char, решают окна loadkid/savekid и + * loadshad/saveshad (как в оригинале, seg006:809). В слоте Guard живёт не + * только страж: тем же кодом ходят скелет (ур. 3), тень (4/5/6/12), визирь + * (13) и мышь (8) — `autocontrol_opponent` (seg002:628) разводит их только + * по ИИ, а play_guard_frame у всех общий. Значит расхождения «у Кида так, + * у прочих иначе» — это ВЕТКИ ПО charid внутри общих функций, и проверять + * их надо именно так: одна сцена, разные персонажи. + * + * t_phys страхует, что перенос ничего не сломал КИДУ (характеризационные + * трассы). Здесь — противоположное: что не-Кид получает СВОЮ ветку. + * + * Проверки точечные (значение поля после N кадров), а не трассы: ветки + * различаются исходом, а не траекторией, и эталон тут только запутал бы. + */ +#include + +#include "tcheck.h" +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_guard.h" +#include "pop_geom.h" +#include "pop_state.h" +#include "scene.h" +#include "stubs.h" + +#define E 0 /* empty */ +#define F 1 /* floor */ +#define G 4 /* gate (portcullis) */ +#define W 20 /* wall */ + +/* Ровный пол на ряду 1 (как room_flat в t_phys). */ +static const uint8_t room_flat[30] = { + W, E, E, E, E, E, E, E, E, E, + W, F, F, F, F, F, F, F, F, F, + W, W, W, W, W, W, W, W, W, W, +}; + +/* Пол ряда 0 обрывается после колонки 3; ряд 2 — пол. Персонаж, сошедший + * с кромки (0,3), падает ДВА ряда: ровно та высота, на которой ветки + * land() по charid расходятся (seg005:189). */ +static const uint8_t room_drop2[30] = { + W, F, F, F, E, E, E, E, E, E, + W, E, E, E, E, E, E, E, E, E, + W, F, F, F, F, F, F, F, F, F, +}; + +/* Решётка ворот в (2,6) — сцена под окклюзию персонажа, стоящего в проёме. */ +static const uint8_t room_gate[30] = { + W, E, E, E, E, E, E, E, E, E, + W, E, E, E, E, E, E, E, E, E, + W, F, F, F, F, F, G, F, F, F, +}; + +/* ---- take_hp: кому достаётся урон ----------------------------------- */ + +TC_TEST(char_take_hp_goes_to_active) +{ + sc_room(room_flat, 1); + sc_kid_at(3, 1, -1); + sc_guard_at(CHARID_2_GUARD, 6, 1, -1, 3); + + /* Окно Кида: урон обязан уйти в hitp_delta и не тронуть стража. */ + pop_loadkid(); + hitp_delta = 0; guardhp_delta = 0; + tc_check(pop_take_hp(1), 0, "kid: не последнее HP", __LINE__); + tc_check(hitp_delta, -1, "kid: hitp_delta", __LINE__); + tc_check(guardhp_delta, 0, "kid: страж не тронут", __LINE__); + pop_savekid(); + + /* Окно стража: тот же вызов — уже в guardhp_delta. */ + pop_loadshad(); + hitp_delta = 0; guardhp_delta = 0; + tc_check(pop_take_hp(1), 0, "guard: не последнее HP", __LINE__); + tc_check(guardhp_delta, -1, "guard: guardhp_delta", __LINE__); + tc_check(hitp_delta, 0, "guard: Кид не тронут", __LINE__); + /* Смертельный удар: возврат 1 и дельта на всё оставшееся. */ + guardhp_delta = 0; + tc_check(pop_take_hp(100), 1, "guard: смертельный удар", __LINE__); + tc_check(guardhp_delta, -3, "guard: дельта на все HP", __LINE__); + pop_saveshad(); +} + +/* Уровень 2 чита гасит только мелкий урон Киду. Мгновенная смерть + * (count=100) проходит всегда, а на стража чит не действует вовсе. */ +TC_TEST(char_immortal_level2_covers_kid_only) +{ + sc_room(room_flat, 1); + sc_kid_at(3, 1, -1); + sc_guard_at(CHARID_2_GUARD, 6, 1, -1, 3); + pop_immortal = 2; + + pop_loadkid(); + hitp_delta = 0; + tc_check(pop_take_hp(1), 0, "kid: мелкий урон погашен", __LINE__); + tc_check(hitp_delta, 0, "kid: HP не тронуто", __LINE__); + tc_check(pop_take_hp(100), 1, "kid: мгновенная смерть проходит", __LINE__); + tc_check(hitp_delta, -3, "kid: смертельная дельта", __LINE__); + pop_savekid(); + + pop_loadshad(); + guardhp_delta = 0; + tc_check(pop_take_hp(100), 1, "guard: чит на него не действует", __LINE__); + pop_saveshad(); + + pop_immortal = 0; +} + +/* ---- do_delta_hp: дельты применяются каждому своему счётчику --------- */ + +TC_TEST(char_delta_hp_applies_per_character) +{ + sc_room(room_flat, 1); + sc_kid_at(3, 1, -1); + sc_guard_at(CHARID_2_GUARD, 6, 1, -1, 3); + + hitp_delta = -1; + guardhp_delta = -2; + pop_kid_hurt = 0; + pop_guard_hurt = 0; + pop_do_delta_hp(); + + tc_check(hitp_curr, 2, "kid: HP 3-1", __LINE__); + tc_check(guardhp_curr, 1, "guard: HP 3-2", __LINE__); + /* Флаги «брызг» — по одному на персонажа, и только на УРОН. */ + tc_check(pop_kid_hurt, 1, "kid: брызги", __LINE__); + tc_check(pop_guard_hurt, 1, "guard: брызги", __LINE__); + + /* Плюсовая дельта (зелье) брызг и вспышки давать НЕ должна. */ + pop_kid_hurt = 0; + hitp_delta = 1; + pop_do_delta_hp(); + tc_check(hitp_curr, 3, "kid: зелье вернуло HP", __LINE__); + tc_check(pop_kid_hurt, 0, "kid: зелье — не урон", __LINE__); +} + +/* ---- land(): ветки по charid (seg005:173) --------------------------- */ + +/* Кид с двух рядов теряет 1 HP и остаётся жив. */ +TC_TEST(char_kid_survives_two_row_fall) +{ + sc_room(room_drop2, 1); + sc_kid_at(3, 0, 0); + sc_trace_clear(); + sc_run(SC_R, 30); /* шагнуть с кромки и упасть */ + + pop_do_delta_hp(); + tc_check(Kid.curr_row, 2, "kid: приземлился на ряд 2", __LINE__); + tc_check(hitp_curr, 2, "kid: −1 HP за два ряда", __LINE__); + tc_check(pop_kid_dead, 0, "kid: жив", __LINE__); +} + +/* СТРАЖ с той же высоты гибнет: seg005:190 уводит charid_2_guard прямо в + * take_hp(100). Это не «баланс», а буквальная ветка оригинала. */ +TC_TEST(char_guard_dies_from_two_row_fall) +{ + sc_room(room_drop2, 1); + sc_kid_at(1, 2, 0); /* Кид в стороне, не мешает */ + /* Ставим сразу НАД ПУСТОТОЙ: ИИ в наборе нет, сам с кромки страж не + * сойдёт, а физике довольно того, что под ним не пол. Колонка 5, а не + * 4: решение принимается по ВЕСОВОЙ ТОЧКЕ кадра (dx_weight, seg006), + * а она у стойки стража уходит на 9 px назад — с колонки 4 точка + * попадала бы ещё на пол колонки 3. */ + sc_guard_at(CHARID_2_GUARD, 5, 0, 0, 3); + sc_guard_run(30); + + pop_do_delta_hp(); + tc_check(Guard.curr_row, 2, "guard: приземлился на ряд 2", __LINE__); + tc_check(guardhp_curr, 0, "guard: два ряда = смерть", __LINE__); +} + +/* ТЕНЬ (charid_1_shadow) с тех же двух рядов не теряет ничего: seg005:189 + * уводит её в ветку «упал с одного ряда». */ +TC_TEST(char_shadow_survives_two_row_fall) +{ + sc_room(room_drop2, 1); + sc_kid_at(1, 2, 0); + sc_guard_at(CHARID_1_SHADOW, 5, 0, 0, 3); + sc_guard_run(30); + + pop_do_delta_hp(); + tc_check(Guard.curr_row, 2, "shadow: приземлилась на ряд 2", __LINE__); + tc_check(guardhp_curr, 3, "shadow: HP не тронуто", __LINE__); +} + +/* ---- физика слота Guard вообще работает ----------------------------- */ + +/* До GUARD-PHYS у стража не было ни fall_accel, ни check_action: сойдя с + * кромки, он просто висел в воздухе. Тест ловит именно это. */ +TC_TEST(char_guard_falls_at_all) +{ + uint8_t y0; + sc_room(room_drop2, 1); + sc_kid_at(1, 2, 0); + sc_guard_at(CHARID_2_GUARD, 5, 0, 0, 3); + y0 = Guard.y; + + sc_guard_run(6); + tc_check(Guard.y > y0, 1, "guard: пошёл вниз", __LINE__); +} + +/* Гард по X (seg000:1254): за пределами комнаты физика молчит — именно так + * оригинал не выпускает стража из комнаты. */ +TC_TEST(char_guard_physics_gated_by_x) +{ + uint8_t y0; + sc_room(room_drop2, 1); + sc_kid_at(1, 2, 0); + sc_guard_at(CHARID_2_GUARD, 5, 0, 0, 3); + Guard.x = 220; /* правее 211 — вне комнаты */ + y0 = Guard.y; + + sc_guard_run(6); + tc_check(Guard.y, y0, "guard вне комнаты: физика не трогает", __LINE__); +} + +/* check_guard_fallout (seg002:0241): провалившегося ниже комнаты убирают + * совсем — в комнату снизу он не переходит. */ +TC_TEST(char_guard_fallout_removes_him) +{ + sc_room(room_drop2, 1); + sc_kid_at(1, 2, 0); + sc_guard_at(CHARID_2_GUARD, 5, 0, 0, 3); + + Guard.y = 211; + pop_guard_fallout(); + tc_check(Guard.charid, 0, "guard: убран из комнаты", __LINE__); + tc_check(guardhp_curr, 0, "guard: HP обнулены", __LINE__); +} + +/* ---- окна Char не путают персонажей ---------------------------------- */ + +/* Регресс на класс ошибок, ради которого затевался перенос: физика Кида не + * должна трогать стража, и наоборот. Гоняем оба слота в одной сцене. */ +TC_TEST(char_windows_do_not_leak) +{ + uint8_t gx, gy; + sc_room(room_flat, 1); + sc_kid_at(3, 1, 0); + sc_guard_at(CHARID_2_GUARD, 7, 1, -1, 3); + gx = Guard.x; gy = Guard.y; + + sc_run(SC_R, 10); /* Кид бежит вправо */ + + tc_check(Guard.x, gx, "страж не поехал за Кидом", __LINE__); + tc_check(Guard.y, gy, "страж остался на своей высоте", __LINE__); + tc_check(Guard.charid, CHARID_2_GUARD, "страж не подменён Кидом", __LINE__); + tc_check(Kid.charid, CHARID_0_KID, "Кид не подменён стражем", __LINE__); +} + + +/* ---- follow_guard: страж идёт ЗА КИДОМ вбок (seg002:0340/039E) ------- * + * Своим ходом персонаж слота Guard комнату не меняет, но когда её меняет + * КИД, exit_room может перенести стража следом. Здесь проверяются все + * условия отбора — они и есть механика. Порт зовётся из enter_room, а + * тестируем прямо pop_guard_follow: сцена комнат в наборе одна. */ + +/* Базовый случай: бой, Кид ушёл ВЛЕВО, страж прижат к левому краю (x < 91), + * в комнате 2 своего стража нет → идёт следом, X переносится через шов. */ +TC_TEST(char_follow_guard_left) +{ + tk_guard_state_clear(); + sc_room(room_flat, 3); + sc_kid_at(1, 1, -1); + sc_guard_at(CHARID_2_GUARD, 2, 1, -1, 3); + Guard.x = 80; /* < 91: у левого края */ + Guard.room = 3; + + tc_check(pop_guard_follow(2, 0), 1, "страж пошёл следом", __LINE__); + tc_check(Guard.room, 2, "страж в новой комнате", __LINE__); + tc_check(Guard.x, 80 + 140, "X перенесён через шов (+140)", __LINE__); + /* follow_guard стирает guards_tile ОБЕИХ комнат — иначе страж + * раздвоится: старую поднимут при возврате, новую при входе. */ + tc_check(tk_gs_tile[3 - 1], 30, "в старой комнате стража нет", __LINE__); + tc_check(tk_gs_tile[2 - 1], 30, "в новой комнате нет второго", __LINE__); +} + +/* Тот же бой, но страж ДАЛЕКО от края (x >= 91) — остаётся. */ +TC_TEST(char_follow_guard_needs_edge) +{ + tk_guard_state_clear(); + sc_room(room_flat, 3); + sc_kid_at(1, 1, -1); + sc_guard_at(CHARID_2_GUARD, 5, 1, -1, 3); + Guard.x = 120; /* >= 91 */ + Guard.room = 3; + + tc_check(pop_guard_follow(2, 0), 0, "далеко от края — остаётся", __LINE__); + tc_check(Guard.room, 3, "комната не менялась", __LINE__); +} + +/* Порог для ухода ВПРАВО — свой (x >= 165). */ +TC_TEST(char_follow_guard_right_threshold) +{ + tk_guard_state_clear(); + sc_room(room_flat, 3); + sc_kid_at(8, 1, 0); + sc_guard_at(CHARID_2_GUARD, 7, 1, 0, 3); + Guard.room = 3; + + Guard.x = 160; /* < 165 — не идёт */ + tc_check(pop_guard_follow(4, 1), 0, "x=160: остаётся", __LINE__); + Guard.x = 170; /* >= 165 — идёт */ + tc_check(pop_guard_follow(4, 1), 1, "x=170: идёт следом", __LINE__); + tc_check(Guard.x, 170 - 140, "X перенесён через шов (−140)", __LINE__); +} + +/* Не бой — не идёт. Меч в ножнах = страж Кида не преследует. */ +TC_TEST(char_follow_guard_needs_sword_drawn) +{ + tk_guard_state_clear(); + sc_room(room_flat, 3); + sc_kid_at(1, 1, -1); + sc_guard_at(CHARID_2_GUARD, 2, 1, -1, 3); + Guard.x = 80; + Guard.room = 3; + Guard.sword = 0; /* sword_0_sheathed */ + + tc_check(pop_guard_follow(2, 0), 0, "без меча — остаётся", __LINE__); +} + +/* Мёртвый (и умирающий) не идёт никуда. */ +TC_TEST(char_follow_guard_dead_stays) +{ + tk_guard_state_clear(); + sc_room(room_flat, 3); + sc_kid_at(1, 1, -1); + sc_guard_at(CHARID_2_GUARD, 2, 1, -1, 3); + Guard.x = 80; + Guard.room = 3; + Guard.alive = 0; /* alive >= 0 — умирает/мёртв */ + + tc_check(pop_guard_follow(2, 0), 0, "мёртвый остаётся", __LINE__); +} + +/* Вверх и вниз оригинал не пускает никогда (условия leave всегда истинны). */ +TC_TEST(char_follow_guard_never_vertical) +{ + tk_guard_state_clear(); + sc_room(room_flat, 3); + sc_kid_at(1, 1, -1); + sc_guard_at(CHARID_2_GUARD, 2, 1, -1, 3); + Guard.x = 80; + Guard.room = 3; + + tc_check(pop_guard_follow(2, 2), 0, "вверх — не идёт", __LINE__); + tc_check(pop_guard_follow(2, 3), 0, "вниз — не идёт", __LINE__); + tc_check(Guard.room, 3, "комната не менялась", __LINE__); +} + +/* В целевой комнате СВОЙ живой страж — перехода нет (иначе их станет два). */ +TC_TEST(char_follow_guard_blocked_by_own_guard) +{ + tk_guard_state_clear(); + sc_room(room_flat, 3); + sc_kid_at(1, 1, -1); + sc_guard_at(CHARID_2_GUARD, 2, 1, -1, 3); + Guard.x = 80; + Guard.room = 3; + tk_guard_room = 2; /* в комнате 2 есть свой страж */ + + tc_check(pop_guard_follow(2, 0), 0, "чужая комната занята", __LINE__); + tc_check(Guard.room, 3, "остался у себя", __LINE__); + + /* А если там ТРУП (запомнена последовательность) — идти можно. */ + tk_gs_tile[2 - 1] = 11; + tk_gs_seq[2 - 1] = 0x1234; + tc_check(pop_guard_follow(2, 0), 1, "к трупу — можно", __LINE__); +} + +/* Тайлы СОСЕДНИХ комнат: колонка персонажа считается из x и на диапазоне + * x 0..255 лежит в −5..13, то есть регулярно уходит за комнату глубже кромки. + * Там get_tile обязан отдавать настоящий тайл соседа — мнимая стена стоила + * BUG-SWORD-GHOST-1 (страж после follow_guard оказывался в col 12, луч + * видимости упирался в неё, и Кид посреди боя прятал меч, а достать обратно + * уже не мог: draw_sword требует can_guard_see_kid >= 2). */ +static void char_tiles_resolve_across_rooms(void) +{ + static const uint8_t floor30[30] = { + E,E,E,E,E,E,E,E,E,E, + E,E,E,E,E,E,E,E,E,E, + F,F,F,F,F,F,F,F,F,F, + }; + uint8_t i; + /* Комнаты-соседи «уровня»: карта заберёт их fg сама (pop_map_set_edges). */ + for (i = 0; i < 30; i++) { tk_room_fg[4][i] = F; tk_room_fg[6][i] = F; } + tk_room_fg[4][10 + 5] = W; /* сосед слева: ряд 1, col 5 */ + tk_room_fg[6][10 + 2] = W; /* сосед справа: ряд 1, col 2 */ + + sc_room(floor30, 5); + pop_map_set_edges(4, 6, 0, 0); + tc_check(pop_tile_at(-1, 1), F, "col -1 = col9 соседа слева", __LINE__); + tc_check(pop_tile_at(-5, 1), W, "col -5 = col5 соседа слева", __LINE__); + tc_check(pop_tile_at(10, 1), F, "col 10 = col0 соседа справа", __LINE__); + tc_check(pop_tile_at(12, 1), W, "col 12 = col2 соседа справа", __LINE__); + tc_check(pop_tile_at(13, 1), F, "col 13 = col3 соседа справа", __LINE__); + /* Дальше комнаты соседа оригинал ушёл бы ещё через roomlink; у нас + * кэш на одну комнату в каждую сторону, и это край — стена. */ + tc_check(pop_tile_at(20, 1), W, "за комнатой соседа — стена", __LINE__); + tc_check(pop_tile_at(-11, 1), W, "и слева тоже", __LINE__); + + /* Соседа нет (край уровня) — стена в обе стороны, как раньше. */ + pop_map_set_edges(0, 0, 0, 0); + tc_check(pop_tile_at(12, 1), W, "без соседа справа — стена", __LINE__); + tc_check(pop_tile_at(-1, 1), W, "без соседа слева — стена", __LINE__); +} + +/* ---- скелет: падение его не убивает (check_guard_fallout, seg002:252) --- */ + +/* Если под комнатой лежит комната 3, упавший скелет ВОЗРОЖДАЕТСЯ там же — + * это единственный способ «убрать» его с экрана, убить его нельзя. */ +TC_TEST(char_skeleton_reappears_below) +{ + sc_room(room_flat, 1); + sc_kid_at(1, 2, 0); + sc_guard_at(CHARID_4_SKELETON, 5, 1, -1, 3); + Guard.room = 5; + Guard.y = 220; /* ниже нижней границы комнаты (211) */ + tk_link_down = 3; /* под комнатой 5 — комната 3 */ + + pop_guard_fallout(); + + tc_check(Guard.charid, CHARID_4_SKELETON, "скелет остался в слоте", __LINE__); + tc_check(Guard.room, 3, "перенесён в комнату 3", __LINE__); + tc_check(Guard.x, 133, "и на свою позицию", __LINE__); + tc_check(Guard.curr_row, 1, "ряд 1", __LINE__); + tc_check(Guard.alive, -1, "жив", __LINE__); +} + +/* А если снизу НЕ комната 3 — обычная судьба выпавшего: слот освобождается. */ +TC_TEST(char_skeleton_falls_out_elsewhere) +{ + sc_room(room_flat, 1); + sc_kid_at(1, 2, 0); + sc_guard_at(CHARID_4_SKELETON, 5, 1, -1, 3); + Guard.room = 5; + Guard.y = 220; + tk_link_down = 7; /* снизу другая комната */ + + pop_guard_fallout(); + tc_check(Guard.charid, 0, "скелет убран, как обычный выпавший", __LINE__); +} + +/* ---- Окклюзия воротами (GATE-FORE-KID) ------------------------------ * + * Персонаж, стоящий В ПРОЁМЕ ворот, обязан уходить ЗА решётку: оригинал + * кладёт её бары в foretable (draw_tile_fore, seg008:0D15). Решение — + * pop_gate_over_char; сама отрисовка живёт в слое фона и в харнесс не + * линкуется, поэтому проверяем именно РЕШЕНИЕ. */ + +TC_TEST(char_gate_covers_char_standing_in_it) +{ + sc_room(room_gate, 1); + sc_kid_at(6, 2, 0); /* ровно на тайле ворот */ + pop_loadkid(); + TC_TRUE(pop_gate_over_char(&Char)); +} + +TC_TEST(char_gate_ignores_neighbour_tiles) +{ + sc_room(room_gate, 1); + sc_kid_at(5, 2, 0); /* колонка слева от ворот */ + pop_loadkid(); + TC_EQ(pop_gate_over_char(&Char), 0); + sc_kid_at(7, 2, 0); /* и справа */ + pop_loadkid(); + TC_EQ(pop_gate_over_char(&Char), 0); +} + +TC_TEST(char_gate_ignores_other_room) +{ + /* Персонаж той же клетки, но ДРУГОЙ комнаты (страж, оставшийся в + * соседней): его решётка текущей комнаты не касается. */ + sc_room(room_gate, 1); + sc_kid_at(6, 2, 0); + pop_loadkid(); + Char.room = 5; + TC_EQ(pop_gate_over_char(&Char), 0); +} + +TC_TEST(char_gate_never_in_last_column) +{ + /* Колонка 9: бары легли бы в колонку 10, то есть в СОСЕДНЮЮ комнату — + * у оригинала это отсекает `Kid.room != room_R`. */ + static const uint8_t room_gate9[30] = { + W, E, E, E, E, E, E, E, E, E, + W, E, E, E, E, E, E, E, E, E, + W, F, F, F, F, F, F, F, F, G, + }; + sc_room(room_gate9, 1); + sc_kid_at(9, 2, 0); + pop_loadkid(); + TC_EQ(pop_gate_over_char(&Char), 0); +} + +void main(void) +{ + sc_init(); + TC_RUN(char_take_hp_goes_to_active); + TC_RUN(char_immortal_level2_covers_kid_only); + TC_RUN(char_delta_hp_applies_per_character); + TC_RUN(char_kid_survives_two_row_fall); + TC_RUN(char_guard_dies_from_two_row_fall); + TC_RUN(char_shadow_survives_two_row_fall); + TC_RUN(char_guard_falls_at_all); + TC_RUN(char_guard_physics_gated_by_x); + TC_RUN(char_guard_fallout_removes_him); + TC_RUN(char_windows_do_not_leak); + TC_RUN(char_follow_guard_left); + TC_RUN(char_follow_guard_needs_edge); + TC_RUN(char_follow_guard_right_threshold); + TC_RUN(char_follow_guard_needs_sword_drawn); + TC_RUN(char_follow_guard_dead_stays); + TC_RUN(char_follow_guard_never_vertical); + TC_RUN(char_follow_guard_blocked_by_own_guard); + TC_RUN(char_tiles_resolve_across_rooms); + TC_RUN(char_skeleton_reappears_below); + TC_RUN(char_skeleton_falls_out_elsewhere); + TC_RUN(char_gate_covers_char_standing_in_it); + TC_RUN(char_gate_ignores_neighbour_tiles); + TC_RUN(char_gate_ignores_other_room); + TC_RUN(char_gate_never_in_last_column); +} diff --git a/applications/SprPoP/tests/host/t_cutscene.c b/applications/SprPoP/tests/host/t_cutscene.c new file mode 100644 index 0000000..e36ede6 --- /dev/null +++ b/applications/SprPoP/tests/host/t_cutscene.c @@ -0,0 +1,46 @@ +/* pop_cutscene — порядок событий, точная семантика WAIT и прерывание. */ +#include +#include "tcheck.h" +#include "pop_cutscene.h" + +static const pop_cs_cmd_t script[] = { + { POP_CS_PALETTE, 1 }, { POP_CS_SHOW, 7 }, { POP_CS_WAIT, 2 }, + { POP_CS_FLASH, 3 }, { POP_CS_WAIT, 1 }, { POP_CS_FADE_OUT, 0 }, + { POP_CS_END, 0 } +}; + +TC_TEST(script_events_and_waits) +{ + pop_cutscene_t cs; + uint16_t arg; + + pop_cutscene_begin(&cs, script); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_PALETTE); TC_EQ(arg, 1); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_SHOW); TC_EQ(arg, 7); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_NONE); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_NONE); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_FLASH); TC_EQ(arg, 3); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_NONE); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_FADE_OUT); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_END); +} + +TC_TEST(skip_aborts_at_any_wait) +{ + pop_cutscene_t cs; + uint16_t arg; + + pop_cutscene_begin(&cs, script); + pop_cutscene_tick(&cs, &arg); + pop_cutscene_tick(&cs, &arg); + pop_cutscene_tick(&cs, &arg); /* WAIT(2), ещё есть один кадр */ + pop_cutscene_abort(&cs); + TC_EQ(pop_cutscene_tick(&cs, &arg), POP_CS_EVENT_END); + TC_EQ(cs.ended, 1); +} + +void main(void) +{ + TC_RUN(script_events_and_waits); + TC_RUN(skip_aborts_at_any_wait); +} diff --git a/applications/SprPoP/tests/host/t_demo.c b/applications/SprPoP/tests/host/t_demo.c new file mode 100644 index 0000000..85d28c3 --- /dev/null +++ b/applications/SprPoP/tests/host/t_demo.c @@ -0,0 +1,89 @@ +/* pop_demo — запись level 0: смена команд и прерывание любым вводом. */ +#include +#include "tcheck.h" +#include "pop_demo.h" + +static int8_t moves[255]; +static uint8_t moves_n; +static uint8_t any_key; +static uint8_t fight_ai; +uint8_t hitp_curr; + +uint8_t pop_demo_kid_ai(void) __banked +{ + return fight_ai; +} + +void pop_ctrl_auto_move(int8_t move) __banked +{ + moves[moves_n++] = move; +} + +uint8_t kbd_raw_any_down(void) +{ + return any_key; +} + +TC_TEST(replays_sdlpop_move_table) +{ + uint8_t done = 0; + + moves_n = 0; + fight_ai = 0; + hitp_curr = 4; + pop_demo_begin(); + TC_FALSE(pop_demo_tick()); + TC_EQ(moves_n, 1); + TC_EQ(moves[0], 0); /* t=1, запись {0, nothing} */ + while (moves_n < 14) { + TC_FALSE(pop_demo_tick()); + } + TC_EQ(moves[1], 1); /* t=2, запись {1, forward} */ + TC_EQ(moves[11], 1); /* t=12 всё ещё forward */ + TC_EQ(moves[12], 0); /* t=13, запись {0x0D, nothing} */ + + while (!done) done = pop_demo_tick(); + TC_EQ(moves_n, 254); /* SDLPoP останавливает demo_time на FE */ + TC_EQ(moves[253], -1); /* хвост после записи {0xE9, -1} */ +} + +TC_TEST(fight_ai_pauses_scripted_moves) +{ + moves_n = 0; + fight_ai = 1; + hitp_curr = 4; + pop_demo_begin(); + TC_FALSE(pop_demo_tick()); + TC_EQ(moves_n, 0); /* во время боя demo_time не идёт */ + + fight_ai = 0; + TC_FALSE(pop_demo_tick()); + TC_EQ(moves_n, 1); + TC_EQ(moves[0], 0); /* первый scripted тик не потерян */ +} + +TC_TEST(death_stops_demo_before_combat_ai) +{ + moves_n = 0; + fight_ai = 1; + hitp_curr = 0; + pop_demo_begin(); + TC_TRUE(pop_demo_tick()); + TC_EQ(moves_n, 0); +} + +TC_TEST(any_raw_key_interrupts_demo) +{ + any_key = 0; + TC_FALSE(pop_demo_user_input()); + any_key = 1; + TC_TRUE(pop_demo_user_input()); +} + +void main(void) +{ + TC_RUN(replays_sdlpop_move_table); + TC_RUN(fight_ai_pauses_scripted_moves); + TC_RUN(death_stops_demo_before_combat_ai); + TC_RUN(any_raw_key_interrupts_demo); +} diff --git a/applications/SprPoP/tests/host/t_flow.c b/applications/SprPoP/tests/host/t_flow.c new file mode 100644 index 0000000..36a9aad --- /dev/null +++ b/applications/SprPoP/tests/host/t_flow.c @@ -0,0 +1,52 @@ +/* pop_flow — только канонические pre-level маршруты и ветка scene 12. */ +#include +#include "tcheck.h" +#include "pop_flow.h" + +TC_TEST(only_original_levels_have_pre_scenes) +{ + uint8_t level; + + for (level = 0; level <= 14; level++) { + pop_pre_cutscene_t scene = pop_flow_pre_cutscene(level, 60); + if (level == 2 || level == 6) TC_EQ(scene, POP_PRE_2_6); + else if (level == 4) TC_EQ(scene, POP_PRE_4); + else if (level == 8) TC_EQ(scene, POP_PRE_8); + else if (level == 9) TC_EQ(scene, POP_PRE_9); + else if (level == 12) TC_EQ(scene, POP_PRE_12); + else TC_EQ(scene, POP_PRE_NONE); + } + TC_EQ(pop_flow_pre_cutscene(15, 60), POP_PRE_NONE); +} + +TC_TEST(scene_12_short_time_boundary) +{ + TC_EQ(pop_flow_pre_cutscene(12, 6), POP_PRE_12); + TC_EQ(pop_flow_pre_cutscene(12, 5), POP_PRE_12_SHORT); + TC_EQ(pop_flow_pre_cutscene(12, 0), POP_PRE_12_SHORT); +} + +TC_TEST(level_14_is_ending_without_number) +{ + const pop_level_flow_t *flow = pop_flow_level(14); + TC_TRUE(flow != 0); + TC_FALSE(flow->show_level_number); + TC_TRUE(flow->ending_rule); + TC_TRUE(pop_flow_level(15) == 0); +} + +TC_TEST(ending_room_is_exact) +{ + TC_TRUE(pop_flow_is_ending(14, 5)); + TC_FALSE(pop_flow_is_ending(14, 4)); + TC_FALSE(pop_flow_is_ending(13, 5)); + TC_FALSE(pop_flow_is_ending(15, 5)); +} + +void main(void) +{ + TC_RUN(only_original_levels_have_pre_scenes); + TC_RUN(scene_12_short_time_boundary); + TC_RUN(level_14_is_ending_without_number); + TC_RUN(ending_room_is_exact); +} diff --git a/applications/SprPoP/tests/host/t_gate.c b/applications/SprPoP/tests/host/t_gate.c new file mode 100644 index 0000000..0340fdf --- /dev/null +++ b/applications/SprPoP/tests/host/t_gate.c @@ -0,0 +1,202 @@ +/* + * t_gate.c — ОТКРЫТЫЕ ворота не должны быть «стеной» для осторожного шага. + * + * Наблюдение (пользователь, 2026-08-24): перед ОТКРЫТОЙ решёткой Кид, + * идущий слева направо, делает лишний мелкий шаг — чтобы подойти к краю, + * Shift+вперёд надо нажимать дважды. + * + * Механика: длину осторожного шага выбирает сам safe_step (seg005:0604) по + * числу, которое вернул get_edge_distance — seq 29..42 это «шаг ровно на + * distance пикселей». А get_edge_distance для тайла-стены зовёт + * dist_from_wall_forward, и та у ОРИГИНАЛА первой строкой отвечает −1, если + * тайл — ворота, сквозь которые можно пройти (seg004:0A18: + * `if (tiletype == tiles_4_gate && !can_bump_into_gate()) return -1`). + * Минус единица уводит разбор дальше, где открытые ворота опознаются как + * обычный ПОЛ: distance = 11, полный шаг. Без этой ветки расстояние + * считается до створки, тип края — EDGE_WALL, и шаг выходит коротким. + * + * Проверяем не поведение через кадры, а сам ответ get_edge_distance: он и + * есть источник длины шага, и утверждение «открытая решётка == обычный + * пол» переживёт любую перенастройку последовательностей. + */ +#include "tcheck.h" +#include "scene.h" +#include "stubs.h" +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_cdraw.h" /* pop_cd[].fph — высоту кадра ставит отрисовка */ + +#define F 1 /* floor */ +#define G 4 /* gate */ +#define W 20 /* wall */ + +/* Ряд 0: пол до колонки 4, решётка в колонке 5, пол дальше — «коридор с + * решёткой» уровня 1 (комната 5) в чистом виде. */ +static const uint8_t room_gate[30] = { + F, F, F, F, F, G, F, F, F, F, + W, W, W, W, W, W, W, W, W, W, + W, W, W, W, W, W, W, W, W, W, +}; + +/* Тот же ряд, но вместо решётки обычный пол — эталон «свободного» края. */ +static const uint8_t room_floor[30] = { + F, F, F, F, F, F, F, F, F, F, + W, W, W, W, W, W, W, W, W, W, + W, W, W, W, W, W, W, W, W, W, +}; + +/* Кид на колонке 4 лицом вправо, решётка (или пол) — следующим тайлом. + * modif — «открытость» ворот: 188 = подняты (проход открыт), 0 = опущены. + * Возврат — distance, тип края остаётся в pop_edge_type(). */ +static int edge_at_gate_x(const uint8_t *fg, uint8_t modif, uint8_t x) +{ + int d; + sc_room(fg, 1); + tk_modif[5] = modif; + if (x) sc_kid_at_x(4, 0, x, 0 /* лицом вправо */); + else sc_kid_at(4, 0, 0); + /* «Открыты ли ворота» решается сравнением подъёма створки с ВЫСОТОЙ + * КАДРА персонажа (can_bump_into_gate, seg004:0373). В продукте её + * пишет отрисовка (pop_cdraw), которой в наборе нет, — значит задаём + * сами. Порог важен только тем, что 6 (опущенная створка) под + * высотой кадра, а 53 (поднятая) над ней. */ + pop_cd[POP_CH_KID].fpw = 12; + pop_cd[POP_CH_KID].fph = 41; /* габарит стойки, снят в MAME */ + pop_loadkid(); + d = pop_edge_distance(); + pop_savekid(); + return d; +} + +static int edge_at_gate(const uint8_t *fg, uint8_t modif) +{ + return edge_at_gate_x(fg, modif, 0); +} + +/* ОТКРЫТАЯ решётка — не препятствие: и расстояние, и тип края обязаны + * совпасть с обычным полом, иначе осторожный шаг окажется коротким. */ +TC_TEST(open_gate_is_not_a_wall) +{ + int d_floor, d_gate; + uint8_t t_floor, t_gate; + + d_floor = edge_at_gate(room_floor, 0); + t_floor = pop_edge_type(); + d_gate = edge_at_gate(room_gate, 188); + t_gate = pop_edge_type(); + + TC_EQ(d_gate, d_floor); + TC_EQ(t_gate, t_floor); +} + +/* ЗАКРЫТАЯ решётка обязана остаться стеной — иначе фикс выше открыл бы + * проход сквозь опущенные ворота. */ +/* ЗАКРЫТАЯ решётка обязана остаться стеной: фикс выше не должен открывать + * проход сквозь опущенные ворота. X берём у самой створки — в центре тайла + * она ещё «далеко» и честно классифицируется как пол. */ +TC_TEST(closed_gate_is_a_wall) +{ + int d = edge_at_gate_x(room_gate, 0, 136); + TC_EQ(pop_edge_type(), EDGE_WALL); + TC_EQ(d < 11, 1); +} + +/* ...а ОТКРЫТАЯ на том же X ведёт себя как чистый пол — это и есть длина + * осторожного шага: 11, а не обрезок до створки. */ +TC_TEST(open_gate_steps_like_floor) +{ + int d_gate, d_floor; + uint8_t t_gate, t_floor; + + d_gate = edge_at_gate_x(room_gate, 188, 136); + t_gate = pop_edge_type(); + d_floor = edge_at_gate_x(room_floor, 0, 136); + t_floor = pop_edge_type(); + + TC_EQ(d_gate, d_floor); + TC_EQ(t_gate, t_floor); +} + +/* ---- check_gate_push (seg004:0833) --------------------------------- * + * Кид стоит в тайле решётки, решётка опускается — оригинал ВЫТАЛКИВАЕТ его + * из тайла (иначе Кид «в суперпозиции»: створка рисуется то перед ним, то + * за ним, и уйти он может в обе стороны). Прогоняем кадр физики и смотрим, + * сдвинулся ли X. */ +static int8_t push_col; /* curr_col, с которым сработал толчок */ + +static int push_from_dir(uint8_t x, uint8_t modif, int8_t dir) +{ + sc_room(room_gate, 1); + tk_modif[5] = modif; + /* Габарит ПОСЛЕДНЕГО НАРИСОВАННОГО кадра — его в продукте пишет + * отрисовка (pop_cdraw), которой в наборе нет. Значения сняты с живой + * сцены в MAME для стойки Кида: ширина 12, высота 41. */ + pop_cd[POP_CH_KID].fpw = 12; + pop_cd[POP_CH_KID].fph = 41; + sc_kid_at_x(5, 0, x, dir); + sc_run(0, 1); /* кадр «стоим» — флаги перекрытия за прошлый */ + push_col = Kid.curr_col; + sc_run(0, 1); /* ...и за этот: check_gate_push смотрит оба */ + return (int)Kid.x - (int)x; +} + +static int push_from(uint8_t x, uint8_t modif) +{ + return push_from_dir(x, modif, 0); +} + +/* СТОРОНА толчка — формула оригинала `Char.x += 5 - (orig_col <= tile_col) * 10`: + * пока тайл ПЕРСОНАЖА и есть решётка, его выталкивает ВЛЕВО (−5); как только + * персонаж отнесён к следующей колонке (решётка стала левым соседом) — ВПРАВО + * (+5). Граница между сторонами, стало быть, ровно там, где меняется + * curr_col, и проверяется здесь на обоих направлениях взгляда. + * + * Решётка — колонка 5, значит push_col == 5 обязан давать −5, push_col == 6 + * (+5), а вне зоны касания створки толчка нет вовсе. */ +static uint8_t check_side(int8_t dir, uint8_t *nleft, uint8_t *nright) +{ + uint8_t x, bad = 0; + for (x = 130; x <= 162; x++) { + int dx = push_from_dir(x, 0, dir); + if (dx == 0) continue; + /* Величина — кратная 5: пока персонаж остаётся в зоне створки, + * толчок повторяется каждый кадр (так же и в оригинале), поэтому + * проверяем СТОРОНУ и шаг, а не одно конкретное число. */ + if (push_col == 5) { if (dx >= 0 || dx % 5) bad++; else (*nleft)++; } + else if (push_col == 6) { if (dx <= 0 || dx % 5) bad++; else (*nright)++; } + else bad++; /* толчок вне тайлов решётки — ошибка */ + } + return bad; +} + +TC_TEST(closing_gate_pushes_kid) +{ + uint8_t nleft = 0, nright = 0, bad; + + bad = check_side(0, &nleft, &nright); /* лицом вправо */ + bad += check_side(-1, &nleft, &nright); /* лицом влево */ + + TC_EQ(bad, 0); + TC_EQ(nleft > 0, 1); /* обе стороны реально есть */ + TC_EQ(nright > 0, 1); +} + +/* ОТКРЫТУЮ решётку выталкивание не трогает вовсе (can_bump_into_gate). */ +TC_TEST(open_gate_does_not_push) +{ + uint8_t x, moved = 0; + for (x = 130; x <= 162; x++) + if (push_from_dir(x, 188, 0) != 0) moved++; + TC_EQ(moved, 0); +} + +int main(void) +{ + sc_init(); + TC_RUN(open_gate_is_not_a_wall); + TC_RUN(closed_gate_is_a_wall); + TC_RUN(open_gate_steps_like_floor); + TC_RUN(closing_gate_pushes_kid); + TC_RUN(open_gate_does_not_push); + return 0; +} diff --git a/applications/SprPoP/tests/host/t_geom.c b/applications/SprPoP/tests/host/t_geom.c new file mode 100644 index 0000000..0702db5 --- /dev/null +++ b/applications/SprPoP/tests/host/t_geom.c @@ -0,0 +1,184 @@ +/* + * t_geom.c — тесты pop_geom.c (геометрия комнаты + PRNG оригинала). + * + * Модуль выбран первым, потому что не тянет за собой ничего: ни графики, + * ни данных уровня, ни клавиатуры — только свой заголовок. + * + * Главный тест здесь — geom_lcg_matches_reference. В pop_geom.c LCG + * оригинала (s = s*214013 + 2531011) написан на ассемблере по схеме + * Горнера ради обхода __mullong, и заявка «бит-в-бит как в SDLPoP» до сих + * пор держалась только на комментарии. Тест сверяет рукописный asm с + * наивной 32-битной формулой на каждом шаге — и по возвращаемому значению, + * и по обеим половинам сида. + */ +#include "tcheck.h" +#include "pop_geom.h" + +/* ---- геометрия ------------------------------------------------------ */ + +TC_TEST(geom_xbump_layout) +{ + /* Шаг колонок равномерный и равен TILE_SIZEX — на этом стоит вся + * арифметика колонок (x_bump[col + FIRST_ONSCREEN_COLUMN]). */ + uint8_t i; + for (i = 1; i < 20; i++) + TC_EQ(pop_x_bump[i] - pop_x_bump[i - 1], TILE_SIZEX); + + /* Центр тайла колонки 0 — та самая величина, которую кладут в Char.x + * do_startpos и pos_guards (seg003). */ + TC_EQ(pop_x_bump[0 + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX, 72); + TC_EQ(pop_x_bump[9 + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX, 198); +} + +TC_TEST(geom_yland_rows) +{ + /* y_land[row + 1]; [0] — ряд «над комнатой». */ + TC_EQ(pop_y_land[0], -8); + TC_EQ(pop_y_land[1], 55); + TC_EQ(pop_y_land[2], 118); + TC_EQ(pop_y_land[3], 181); + TC_EQ(pop_y_land[4], 244); + /* Шаг между рядами — TILE_SIZEY. */ + TC_EQ(pop_y_land[2] - pop_y_land[1], TILE_SIZEY); + TC_EQ(pop_y_land[3] - pop_y_land[2], TILE_SIZEY); +} + +TC_TEST(geom_y_to_row) +{ + /* Пол каждого ряда должен давать номер этого ряда. */ + TC_EQ(pop_y_to_row(pop_y_land[1]), 0); + TC_EQ(pop_y_to_row(pop_y_land[2]), 1); + TC_EQ(pop_y_to_row(pop_y_land[3]), 2); + /* Ряд над комнатой. */ + TC_EQ(pop_y_to_row(pop_y_land[0]), -1); + /* Оборот mod 4: ряд «под комнатой» сворачивается обратно в -1 — + * ровно на это опирается спавн падающего куска у нижней кромки. */ + TC_EQ(pop_y_to_row(pop_y_land[4]), -1); +} + +/* Цепочка сравнений в pop_y_to_row обязана совпадать с ИСХОДНОЙ формулой + * `(y + 60) / 63 % 4 - 1` во всём рабочем диапазоне — включая усечение + * деления К НУЛЮ для отрицательных. Тест держит инвариант: вызовы + * разбросаны по банкам (pop_map, pop_bg, pop_room), и соблазн написать + * деление «по месту» возвращается. */ +TC_TEST(geom_y_to_row_matches_formula) +{ + int16_t y; + /* −400..400 с запасом накрывает всё: кусок loose стартует у потолка + * (ряд −1) и улетает ниже поля в комнату снизу. */ + for (y = -400; y <= 400; y++) { + int16_t t = (int16_t)(y + 60); + TC_EQ(pop_y_to_row(y), (int8_t)(t / 63 % 4 - 1)); + } +} + +/* calc_screen_x_coord (seg008:2602) считается в pop_cdraw.c таблицей SCRX7 + * (банк 4, сюда не линкуется): хранится x/7 байтом, результат = x + SCRX7[..]. + * Таблица сгенерирована питоном, поэтому проверяем два правила, на которых + * держится генерация — ИМЕННО на целевом компиляторе: + * 1) тождество 8x/7 == x + x/7 (7x делится нацело); + * 2) усечение К НУЛЮ, а не к минус бесконечности. + * Округляй SDCC иначе — вся отрицательная половина уехала бы на пиксель, и + * поймалось бы это только глазами на левой кромке комнаты. */ +TC_TEST(geom_mul8div7_table_rules) +{ + int16_t x; + /* Полный диапазон obj_x по данным кадров: −416..695 (см. шапку scr_x). */ + for (x = -448; x <= 703; x++) { + int16_t want = (int16_t)(((x < 0) ? -x : x) * 8 / 7); + if (x < 0) want = (int16_t)-want; + TC_EQ((int16_t)(x * 8 / 7), want); /* усечение к нулю */ + TC_EQ((int16_t)(x * 8 / 7), (int16_t)(x + x / 7)); /* тождество */ + } +} + +/* ---- PRNG: сверка asm-LCG с эталонной формулой ---------------------- */ + +#if POP_PRANDOM_EXACT + +#define LCG_ITERS 128 + +TC_TEST(geom_lcg_matches_reference) +{ + pop_rnd_t s; + unsigned long ref = 12345UL; /* произвольный ненулевой старт */ + uint16_t i, got, want; + + pop_prandom_set(s, 12345u); + + for (i = 0; i < LCG_ITERS; i++) { + got = pop_prandom(&s, 255u); + ref = ref * 214013UL + 2531011UL; + want = (uint16_t)((uint16_t)(ref >> 16) % 256u); + + /* Обрываемся на первом расхождении: иначе одна ошибка в умножении + * забила бы весь буфер отчёта однотипными строками. */ + if (got != want || + s.lo != (uint16_t)ref || + s.hi != (uint16_t)(ref >> 16)) { + TC_EQ(got, want); + TC_EQ(s.lo, (uint16_t)ref); + TC_EQ(s.hi, (uint16_t)(ref >> 16)); + break; + } + } + TC_EQ(i, LCG_ITERS); /* прошли всю дистанцию без расхождений */ +} + +TC_TEST(geom_lcg_seed_zero) +{ + /* Ноль — законный сид (кладку сеют номером комнаты + ряд + колонка, + * что вполне даёт 0). У LCG ноль не является неподвижной точкой — + * убеждаемся, что asm это воспроизводит. */ + pop_rnd_t s; + unsigned long ref = 0UL; + pop_prandom_set(s, 0u); + pop_prandom(&s, 255u); + ref = ref * 214013UL + 2531011UL; + TC_EQ(s.lo, (uint16_t)ref); + TC_EQ(s.hi, (uint16_t)(ref >> 16)); +} + +#endif /* POP_PRANDOM_EXACT */ + +TC_TEST(geom_prandom_range) +{ + /* Оба генератора обязаны укладываться в 0..maxv, в том числе когда + * maxv+1 НЕ степень двойки (там другая ветка pop_rnd_fit). */ + pop_rnd_t s; + uint16_t i, v, seen_hi = 0; + + uint16_t out_of_range = 0; + + pop_prandom_set(s, 1u); + for (i = 0; i < 200; i++) { + v = pop_prandom(&s, 4u); /* n = 5, не степень двойки */ + if (v > 4) out_of_range++; + if (v == 4) seen_hi = 1; + } + TC_EQ(out_of_range, 0); + TC_TRUE(seen_hi); /* верхняя граница достижима */ + + pop_prandom_set(s, 7u); + out_of_range = 0; + for (i = 0; i < 64; i++) { + v = pop_prandom(&s, 1u); /* n = 2, маска */ + if (v > 1) out_of_range++; + } + TC_EQ(out_of_range, 0); +} + +int main(void) +{ + TC_RUN(geom_xbump_layout); + TC_RUN(geom_yland_rows); + TC_RUN(geom_y_to_row); + TC_RUN(geom_y_to_row_matches_formula); + TC_RUN(geom_mul8div7_table_rules); +#if POP_PRANDOM_EXACT + TC_RUN(geom_lcg_matches_reference); + TC_RUN(geom_lcg_seed_zero); +#endif + TC_RUN(geom_prandom_range); + return 0; +} diff --git a/applications/SprPoP/tests/host/t_grab.c b/applications/SprPoP/tests/host/t_grab.c new file mode 100644 index 0000000..66d1c02 --- /dev/null +++ b/applications/SprPoP/tests/host/t_grab.c @@ -0,0 +1,283 @@ +/* + * t_grab.c — прыжок с места через провал в три тайла с зацепом за дальнюю + * кромку (уровень 2, комната 9: Кид на краю (1,1) прыгает и должен + * повиснуть на (1,5)). + * + * Зачем отдельный набор. Трюк требует точной фазы и вручную выходит далеко + * не всегда — в SDLPoP тоже. Поэтому проверяем не «получилось с первого + * раза», а то, что действительно важно: **окно существует и достижимо**. + * Разница между «сложно» и «невозможно» — это разница между «так в + * оригинале» и «баг у нас», и глазами в MAME её не различить. + * + * Два утверждения: + * 1. есть хотя бы одна стартовая X, из которой зацеп удаётся; + * 2. короткий шаг (Shift+вперёд) приводит Кида В ЭТО окно — иначе окно + * было бы недостижимо для игрока, что равно его отсутствию. + * + * При провале в отчёт уходит карта исходов по всей ширине плиты: + * G — зацепился, L — приземлился на дальнюю плиту, . — сорвался. + * + * Механику зацепа задаёт check_grab (seg006:0A28): она работает ТОЛЬКО на + * кадрах 102..105 (начало падения) и только при зажатом Shift. mininim, + * для сравнения, разрешает повиснуть прямо в дуге прыжка (kid-jump.c, + * кадры 7..10) — но это переработка, а не порт, и источник истины у нас + * SDLPoP. + */ +#include "tcheck.h" +#include "scene.h" +#include "stubs.h" +#include "pop_kid.h" +#include "pop_map.h" + +#define E 0 +#define F 1 +#define P 3 +#define W 20 + +/* Уровень 2, комната 9, ряд 1: факел(0), кнопка(1), провал(2,3,4), + * колонна(5). Ряд 2 под провалом пуст — падение уходит из комнаты. */ +static const uint8_t room9[30] = { + E, E, E, E, E, W, W, W, W, W, + F, F, E, E, E, P, F, F, F, P, + W, W, E, E, E, P, F, W, W, W, +}; + +/* Судить по curr_row нельзя: у улетающего вниз Кида ряд заворачивается + * (0,1,2,0,…), и падение выглядит как «остался наверху». Надёжный признак + * — ДЕЙСТВИЕ: 2 = вис/подтягивание, 6 = вис у стены. */ +#define ACT_STAND 0 +#define ACT_RUN_JUMP 1 +#define ACT_HANG_CLIMB 2 +#define ACT_HANG_STRAIGHT 6 + +#define X_LO 76 +#define X_HI 99 + +static char probe(uint8_t x, uint8_t k1, uint8_t k2) +{ + uint8_t i; + sc_room(room9, 9); + sc_kid_at_x(1, 1, x, 0 /* лицом вправо */); + sc_trace_clear(); + sc_run(k1, 4); /* фаза 1: ↑+вперёд — прыжок с места */ + sc_run(k2, 26); /* фаза 2: Shift — попытка зацепа */ + + for (i = 0; i < sc_len; i++) { + uint8_t a = sc_trace[i].action; + if (a == ACT_HANG_CLIMB || a == ACT_HANG_STRAIGHT) return 'G'; + } + i = sc_len ? (uint8_t)(sc_len - 1) : 0; + if (sc_trace[i].col >= 5 && + (sc_trace[i].action == ACT_STAND || sc_trace[i].action == ACT_RUN_JUMP)) + return 'L'; + return '.'; +} + +static void put(char c) +{ + if (tc_result.logn < TC_LOGSZ) tc_result.log[tc_result.logn++] = (uint8_t)c; +} + +static void puts_(const char *s) { while (*s) put(*s++); } + +/* Карта исходов — только когда что-то сломалось: по ней сразу видно, + * окно съехало или исчезло. */ +static void dump_map(const char *tag, uint8_t k1, uint8_t k2) +{ + uint8_t x; + puts_(tag); + for (x = X_LO; x <= X_HI; x++) put(probe(x, k1, k2)); + put('\n'); +} + +/* Куда встаёт Кид после n-го короткого шага от x0. */ +static uint8_t step_x(uint8_t x0, uint8_t nsteps) +{ + uint8_t n, fx = x0; + sc_room(room9, 9); + sc_kid_at_x(1, 1, x0, 0); + for (n = 0; n < nsteps; n++) { + sc_trace_clear(); + sc_run(SC_SHIFT | SC_R, 4); /* шаг = нажатие, не удержание */ + sc_run(0, 6); /* отпустить, дать доиграть */ + if (sc_len) fx = sc_trace[sc_len - 1].x; + } + return fx; +} + +TC_TEST(grab_window_exists) +{ + uint8_t x, n = 0, lo = 0, hi = 0; + for (x = X_LO; x <= X_HI; x++) + if (probe(x, SC_U | SC_R, SC_SHIFT) == 'G') { + if (!n) lo = x; + hi = x; + n++; + } + /* Окно обязано существовать: без него трюк непроходим в принципе, а + * уровень 2 без него не пройти. */ + TC_TRUE(n > 0); + if (!n) { + dump_map("no-window: ", SC_U | SC_R, SC_SHIFT); + } else { + /* Границы фиксируем: если поедут — это осознанное изменение физики, + * а не случайность. */ + TC_EQ(lo, 91); + TC_EQ(hi, 95); + } +} + +TC_TEST(grab_window_reachable_by_safe_step) +{ + /* Игрок доходит до кромки короткими шагами. Если они не попадают в + * окно, существование окна ничего не стоит. */ + TC_EQ(step_x(80, 1), 91); + TC_EQ(step_x(80, 2), 95); +} + +TC_TEST(grab_same_for_all_key_patterns) +{ + /* Живой игрок держит «вперёд» и жмёт Shift по-разному. Исход не должен + * зависеть от того, отпущена ли стрелка и когда нажат Shift. */ + uint8_t x; + for (x = X_LO; x <= X_HI; x++) { + char a = probe(x, SC_U | SC_R, SC_SHIFT); + char b = probe(x, SC_U | SC_R, SC_SHIFT | SC_R); + char c = probe(x, SC_U | SC_R | SC_SHIFT, SC_SHIFT | SC_R); + TC_EQ(b, a); + TC_EQ(c, a); + } +} + +/* ---- Спуск через НИЖНЮЮ КРОМКУ комнаты (BUG-GRAB-SEAM-DOWN) ---------- * + * Уровень 7, комната 14 — по ней уровень и проходят: Кид на ряду 0 висит на + * кромке кнопки (0,2), отпускает и в падении должен зацепиться за кромку + * (2,2). Тайл-цель лежит В НИЖНЕМ ряду, то есть в момент зацепа персонаж + * уже НИЖЕ комнаты — в оригинале это curr_row == 3, а get_tile для него + * уходит по links.down в комнату снизу (find_room_of_tile, seg006:005D). + * + * Пока do_fall не пускал ряд дальше 2, check_grab на этом отрезке не звался + * НИ РАЗУ: Кид пролетал мимо кромки, набирал fall_y > 32, и дальше окно + * зацепа было закрыто уже по скорости — спуск становился непроходимым. + * Тест держит именно это: окно существует и достижимо. */ +#define C 6 /* closer */ +#define O 15 /* opener */ + +static const uint8_t room14[30] = { + C, F, O, E, E, E, E, E, F, F, + W, W, W, E, E, E, E, E, W, W, + F, F, P, E, E, E, E, E, W, W, +}; +static const uint8_t room15[30] = { + W, W, W, E, E, E, E, E, W, W, + F, F, P, E, E, E, E, E, W, W, + W, W, W, E, E, E, E, E, W, W, +}; +/* Ряд 2 комнаты 14 = «ряд −1» комнаты 15 (pop_map_set_above): после перехода + * зацеп продолжает целиться в ту же кромку, уже как в потолок. */ +static uint8_t above14[10]; + +#define ACT_MIDAIR 3 +#define ACT_FREEFALL 4 + +static uint8_t went_below; /* Kid уже пересёк нижнюю границу комнаты 14 */ + +/* Один кадр + переход в комнату снизу — ровно то, что делает главный цикл + * sprpop.c по pop_fell_out (enter_room(down) + exit_room_timer). */ +static void step14(uint8_t keys) +{ + sc_trace_clear(); /* трасса тут не нужна, важен исход */ + sc_run(keys, 1); + if (pop_fell_out) { + uint8_t i; + pop_fell_out = 0; + went_below = 1; + for (i = 0; i < 10; i++) above14[i] = room14[20 + i]; + sc_room(room15, 15); + pop_map_set_edges(0, 0, 14, 0); /* сверху — комната 14 */ + pop_map_set_above(above14); + pop_leave_timer = 2; + } +} + +/* Исход попытки: + * 'G' — повис на НИЖНЕЙ кромке (то, ради чего тест: ряд 2 комнаты 14); + * 'U' — зацепился обратно за ту кромку, с которой сорвался (ряд 0) — + * это другой, всегда работавший случай, и засчитывать его нельзя; + * '.' — пролетел мимо; 'x' — так и не сорвался (проба не состоялась). */ +static char probe_down(uint8_t x, uint8_t delay) +{ + uint8_t i; + sc_room(room14, 14); + pop_map_set_edges(0, 0, 0, 15); /* links комнаты 14: снизу — 15 */ + pop_map_set_above(0); + pop_leave_timer = 0; + pop_fell_out = 0; + went_below = 0; + /* Лицом ВЛЕВО у правой кромки кнопки: обрыв позади — это и есть поза + * спуска (pop_down_action, seg005:482). */ + sc_kid_at_x(2, 0, x, -1); + + /* ↓ + Shift — спуск с уступа (pop_down_action, seq_68) и вис на кромке. + * Shift держим, иначе Кид сорвётся, не успев повиснуть. */ + for (i = 0; i < 30; i++) { + step14(SC_D | SC_SHIFT); + if (Kid.action == ACT_HANG_CLIMB || Kid.action == ACT_HANG_STRAIGHT) break; + } + if (Kid.action != ACT_HANG_CLIMB && Kid.action != ACT_HANG_STRAIGHT) return 'x'; + /* отпустить Shift — сорваться с виса (hang_fall, seg005) */ + for (i = 0; i < 12; i++) { + step14(0); + if (Kid.action == ACT_MIDAIR || Kid.action == ACT_FREEFALL) break; + } + if (Kid.action != ACT_MIDAIR && Kid.action != ACT_FREEFALL) return 'x'; + + for (i = 0; i < delay; i++) step14(0); /* пауза перед Shift */ + for (i = 0; i < 30; i++) { + step14(SC_SHIFT); + if (Kid.action == ACT_HANG_CLIMB || Kid.action == ACT_HANG_STRAIGHT) { + if (!went_below) return 'U'; + /* За ЧТО взялся: после перехода вниз ряд 0 = кромка ряда 2 + * комнаты 14 (наша цель), ряд 2 = кромка ряда 1 комнаты 15 + * (этажом ниже — этот случай работал и раньше). */ + return Kid.curr_row == 0 ? 'G' : 'g'; + } + } + return '.'; +} + +#define XD_LO 90 +#define XD_HI 113 + +#define XD_DELAY_MAX 8 + +TC_TEST(grab_below_room_edge_window_exists) +{ + uint8_t x, d, n = 0, n0 = 0; + for (x = XD_LO; x <= XD_HI; x++) + for (d = 0; d <= XD_DELAY_MAX; d++) + if (probe_down(x, d) == 'G') { n++; if (!d) n0++; } + TC_TRUE(n > 0); + /* И играбельность: зацеп обязан удаваться при самом естественном вводе — + * отпустил вис и сразу зажал Shift (d = 0). Окно, доступное только с + * точной задержкой, для игрока почти равно отсутствующему. */ + TC_TRUE(n0 > 0); + if (!n) { /* карта исходов: строка = задержка Shift */ + uint8_t i; + for (i = 0; i <= XD_DELAY_MAX; i++) { + put('d'); put((char)('0' + i)); put(':'); put(' '); + for (x = XD_LO; x <= XD_HI; x++) put(probe_down(x, i)); + put('\n'); + } + } +} + +int main(void) +{ + sc_init(); + TC_RUN(grab_window_exists); + TC_RUN(grab_window_reachable_by_safe_step); + TC_RUN(grab_same_for_all_key_patterns); + TC_RUN(grab_below_room_edge_window_exists); + return 0; +} diff --git a/applications/SprPoP/tests/host/t_jaffar.c b/applications/SprPoP/tests/host/t_jaffar.c new file mode 100644 index 0000000..08bf622 --- /dev/null +++ b/applications/SprPoP/tests/host/t_jaffar.c @@ -0,0 +1,349 @@ +/* + * t_jaffar — спецсобытия УРОВНЯ 13: встреча с Джафаром, победа, выход и + * три исключения loose-механики (падающая гряда плит). + * + * Своего ИИ у Джафара нет — это обычный страж (seg002:085A), поэтому + * проверять тут нечего кроме СОБЫТИЙ и ветвлений по номеру уровня. А + * именно они и опасны: три из них — это `if (level == 13)` посреди чужого + * кода (тряска плит, удар плитой в беге, «страж не встаёт в стойку»), и + * сломать их можно правкой, вообще не думая про 13-й уровень. + * + * Порт: seg002:0544 meet_Jaffar, seg002:0517 Jaffar_exit, + * seg006:1927 on_guard_killed, seg000:1317 check_fall_flo, + * seg007:823/949 исключения animate_loose/loose_make_shake. + */ +#include + +#include "tcheck.h" +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_guard.h" +#include "pop_geom.h" +#include "pop_state.h" +#include "pop_level.h" +#include "pop_tune.h" +#include "scene.h" +#include "stubs.h" + +#define E 0 /* empty */ +#define F 1 /* floor */ +#define L 11 /* loose */ +#define W 20 /* wall */ + +/* Комната с полом внизу и грядой loose в ряду 2 — она же будет играть роль + * «комнаты сверху» для check_fall_flo. */ +static const uint8_t room_loose[30] = { + F, F, F, F, F, F, F, F, F, F, + W, W, W, W, W, W, W, W, W, W, + F, F, L, L, L, L, L, L, F, F, +}; + +/* Комната, в которую Кид входит снизу: ровный пол во всех рядах. */ +static const uint8_t room_flat[30] = { + F, F, F, F, F, F, F, F, F, F, + F, F, F, F, F, F, F, F, F, F, + F, F, F, F, F, F, F, F, F, F, +}; + +static uint8_t above_row[10]; + +static void scene_level13(uint8_t room) +{ + pop_guard_reset(); + pop_current_level = JAFFAR_LEVEL; + pop_leveldoor_open = 0; + pop_guard_notice_timer = 0; + sc_room(room_flat, room); + sc_kid_at(1, 2, 1); + tk_log_clear(); +} + +/* ---- meet_Jaffar ----------------------------------------------------- */ + +TC_TEST(jaffar_notice_timer_set_leaving_room3_right) +{ + scene_level13(JAFFAR_MEET_ROOM); + Kid.room = JAFFAR_MEET_ROOM; + pop_meet_jaffar(); + TC_EQ(pop_guard_notice_timer, JAFFAR_NOTICE_TIME); +} + +/* Дверь уровня уже открыта — значит Джафар мёртв и Кид возвращается. + * Второй форы не даётся. */ +TC_TEST(jaffar_notice_not_given_after_victory) +{ + scene_level13(JAFFAR_MEET_ROOM); + Kid.room = JAFFAR_MEET_ROOM; + pop_leveldoor_open = 2; + pop_meet_jaffar(); + TC_EQ(pop_guard_notice_timer, 0); +} + +TC_TEST(jaffar_notice_not_given_from_other_room) +{ + scene_level13(JAFFAR_MEET_ROOM); + Kid.room = (uint8_t)(JAFFAR_MEET_ROOM + 1); + pop_meet_jaffar(); + TC_EQ(pop_guard_notice_timer, 0); +} + +TC_TEST(jaffar_notice_is_level_gated) +{ + scene_level13(JAFFAR_MEET_ROOM); + pop_current_level = 12; + Kid.room = JAFFAR_MEET_ROOM; + pop_meet_jaffar(); + TC_EQ(pop_guard_notice_timer, 0); +} + +/* Счётчик убывает по кадру и останавливается на нуле. */ +TC_TEST(jaffar_notice_timer_counts_down) +{ + uint8_t i; + pop_united_shadow = 0; + pop_guard_notice_timer = 3; + for (i = 0; i < 5; i++) pop_frame_timers(); + TC_EQ(pop_guard_notice_timer, 0); +} + +/* ---- Джафар ЖДЁТ, пока идёт фора ------------------------------------- */ + +/* Поднять соперника с УБРАННЫМ мечом в одном ряду с Кидом и «показать» его: + * дальше решение принимает autocontrol_guard_inactive. */ +static void jaffar_faces_kid(void) +{ + sc_guard_at(CHARID_2_GUARD, 6, 2, -1, 6); + Guard.sword = 0; + can_guard_see_kid = 2; + is_guard_notice = 0; +} + +TC_TEST(jaffar_holds_still_while_notice_timer_runs) +{ + uint8_t i; + scene_level13(1); + jaffar_faces_kid(); + pop_guard_notice_timer = JAFFAR_NOTICE_TIME; + for (i = 0; i < 6; i++) { can_guard_see_kid = 2; pop_guard_tick(); } + /* Стойку не сменил: меч так и в ножнах. */ + TC_EQ(Guard.sword, 0); +} + +/* Фора кончилась — обычное поведение стража возвращается. */ +TC_TEST(jaffar_draws_sword_after_notice_timer) +{ + uint8_t i; + scene_level13(1); + jaffar_faces_kid(); + pop_guard_notice_timer = 0; + for (i = 0; i < 12; i++) { can_guard_see_kid = 2; pop_guard_tick(); } + TC_EQ(Guard.sword, SWORD_2_DRAWN); +} + +/* На прочих уровнях таймер ни на что не влияет: условие в оригинале + * двойное (`level != 13 || timer == 0`), и потерять первую половину легко. */ +TC_TEST(jaffar_notice_timer_ignored_on_other_levels) +{ + uint8_t i; + scene_level13(1); + pop_current_level = 12; + jaffar_faces_kid(); + pop_guard_notice_timer = JAFFAR_NOTICE_TIME; + for (i = 0; i < 12; i++) { can_guard_see_kid = 2; pop_guard_tick(); } + TC_EQ(Guard.sword, SWORD_2_DRAWN); +} + +/* ---- победа и выход --------------------------------------------------- */ + +/* Смерть Джафара не открывает дверь, а ставит флаг «выход разрешён». */ +TC_TEST(jaffar_death_opens_the_exit_flag) +{ + scene_level13(1); + /* charid у Джафара обычный: своим charid оригинал метит только скелета + * (enter_guard, seg002:196), а визиря отличают спрайты и события. */ + sc_guard_at(CHARID_2_GUARD, 6, 2, -1, 6); + Guard.sword = SWORD_2_DRAWN; + guardhp_curr = 0; /* последнее HP только что снято */ + pop_flash_time = 0; + pop_guard_tick(); + TC_EQ(pop_leveldoor_open, 2); + TC_EQ(pop_flash_time, JAFFAR_FLASH_TIME); + TC_EQ(pop_flash_color, POP_FLASH_WHITE); +} + +/* На других уровнях смерть стража флага не ставит (иначе дверь открывалась + * бы от любого убитого). */ +TC_TEST(jaffar_death_flag_is_level_gated) +{ + scene_level13(1); + pop_current_level = 12; + sc_guard_at(CHARID_2_GUARD, 6, 2, -1, 6); + Guard.sword = SWORD_2_DRAWN; + guardhp_curr = 0; + pop_guard_tick(); + TC_EQ(pop_leveldoor_open, 0); +} + +/* Уход ВЛЕВО при поднятом флаге жмёт кнопку комнаты 24. */ +TC_TEST(jaffar_exit_presses_button_in_room24) +{ + scene_level13(1); + tk_room_fg[JAFFAR_EXIT_ROOM][JAFFAR_EXIT_TILEPOS] = 15; /* opener */ + pop_leveldoor_open = 2; + tk_log_clear(); + pop_jaffar_exit(); + TC_TRUE(tk_log_count(TK_BUTTON) >= 1); +} + +TC_TEST(jaffar_exit_silent_before_victory) +{ + scene_level13(1); + tk_room_fg[JAFFAR_EXIT_ROOM][JAFFAR_EXIT_TILEPOS] = 15; + pop_leveldoor_open = 0; + tk_log_clear(); + pop_jaffar_exit(); + TC_EQ(tk_log_count(TK_BUTTON), 0); +} + +/* ---- три исключения loose-механики ----------------------------------- */ + +/* Сотрясение (приземление Кида) на 13-м уровне плиты НЕ трясёт: иначе оно + * сбило бы им отложенный старт, и гряда посыпалась бы разом. */ +TC_TEST(jaffar_knock_does_not_shake_tiles) +{ + scene_level13(1); + sc_room(room_loose, 1); + sc_kid_at(1, 2, 1); + Kid.curr_row = 2; + pop_loadkid(); + knock = -1; /* KNOCK_DOWN: ряд Кида */ + pop_check_knock(); + TC_EQ(pop_loose_modif[22], 0); + TC_EQ(pop_loose_modif[27], 0); +} + +/* А на прочих уровнях трясёт — контроль, что выключен именно 13-й. */ +TC_TEST(jaffar_knock_still_shakes_on_other_levels) +{ + scene_level13(1); + pop_current_level = 12; + sc_room(room_loose, 1); + sc_kid_at(1, 2, 1); + Kid.curr_row = 2; + pop_loadkid(); + knock = -1; + pop_check_knock(); + TC_EQ(pop_loose_modif[22], 0x80); +} + +/* Фаза с ОТРИЦАТЕЛЬНЫМ стартом на 13-м уровне не гасится, а досчитывает + * до провала: это и есть падающая гряда. Проверяем на плите ПОЛА (тот же + * код, что у плит-потолков, но без mob-обвязки, которая в набор не + * линкуется). */ +TC_TEST(jaffar_negative_phase_counts_through_to_fall) +{ + uint8_t i; + scene_level13(1); + sc_room(room_loose, 1); + sc_kid_at(1, 0, 1); + pop_loose_modif[22] = (uint8_t)-4; /* старт «через 4 кадра» */ + /* 4 кадра — на выход из отрицательной зоны, дальше 10 — обычный отсчёт; + * 30 с запасом. Ванильная ветка «тряска кончилась» обнулила бы фазу на + * первом же кадре, и плита осталась бы стоять. */ + for (i = 0; i < 30; i++) pop_loose_tick(); + TC_EQ(pop_tile_at(2, 2), 0); /* провалилась: тайл стал пустым */ +} + +/* На прочих уровнях та же фаза читается как тряска и гаснет — плита цела. */ +TC_TEST(jaffar_negative_phase_is_shake_on_other_levels) +{ + uint8_t i; + scene_level13(1); + pop_current_level = 12; + sc_room(room_loose, 1); + sc_kid_at(1, 0, 1); + pop_loose_modif[22] = (uint8_t)-4; + for (i = 0; i < 30; i++) pop_loose_tick(); + TC_EQ(pop_tile_at(2, 2), L); + TC_EQ(pop_loose_modif[22], 0); +} + +/* ---- check_fall_flo --------------------------------------------------- */ + +/* Вход в комнату 23: шести плитам ряда 2 комнаты СВЕРХУ раздаётся + * отрицательная фаза, остальным — ничего. */ +static void fall_flo_scene(uint8_t room) +{ + uint8_t i; + scene_level13(room); + for (i = 0; i < 10; i++) above_row[i] = room_loose[20 + i]; + pop_map_set_above(above_row); + pop_map_set_edges(0, 0, 1 /* up */, 0); + pop_map_set_above(above_row); + pop_loose_reset(); +} + +TC_TEST(jaffar_fall_flo_seeds_the_row_above) +{ + uint8_t c, seeded = 0; + fall_flo_scene(POP_LOOSE_TILES_ROOM1); + pop_check_fall_flo(); + for (c = 2; c <= 7; c++) + if ((int8_t)pop_ceil_modif[c] < 0) seeded++; + TC_EQ(seeded, 6); + /* Колонки вне диапазона 2..7 не тронуты (там в комнате пол, не loose). */ + TC_EQ(pop_ceil_modif[0], 0); + TC_EQ(pop_ceil_modif[9], 0); +} + +TC_TEST(jaffar_fall_flo_ignores_other_rooms) +{ + uint8_t c; + fall_flo_scene((uint8_t)(POP_LOOSE_TILES_ROOM1 + 1)); + pop_check_fall_flo(); + for (c = 0; c < 10; c++) TC_EQ(pop_ceil_modif[c], 0); +} + +TC_TEST(jaffar_fall_flo_is_level_gated) +{ + uint8_t c; + fall_flo_scene(POP_LOOSE_TILES_ROOM2); + pop_current_level = 12; + pop_check_fall_flo(); + for (c = 0; c < 10; c++) TC_EQ(pop_ceil_modif[c], 0); +} + +/* Повторный вход не перебивает уже идущий отсчёт (иначе гряда «зависала» + * бы, пока игрок бегает туда-сюда). */ +TC_TEST(jaffar_fall_flo_does_not_restart_running_countdown) +{ + fall_flo_scene(POP_LOOSE_TILES_ROOM2); + pop_ceil_modif[3] = 5; /* плита уже падает */ + pop_check_fall_flo(); + TC_EQ(pop_ceil_modif[3], 5); +} + +void main(void) +{ + sc_init(); + TC_RUN(jaffar_notice_timer_set_leaving_room3_right); + TC_RUN(jaffar_notice_not_given_after_victory); + TC_RUN(jaffar_notice_not_given_from_other_room); + TC_RUN(jaffar_notice_is_level_gated); + TC_RUN(jaffar_notice_timer_counts_down); + TC_RUN(jaffar_holds_still_while_notice_timer_runs); + TC_RUN(jaffar_draws_sword_after_notice_timer); + TC_RUN(jaffar_notice_timer_ignored_on_other_levels); + TC_RUN(jaffar_death_opens_the_exit_flag); + TC_RUN(jaffar_death_flag_is_level_gated); + TC_RUN(jaffar_exit_presses_button_in_room24); + TC_RUN(jaffar_exit_silent_before_victory); + TC_RUN(jaffar_knock_does_not_shake_tiles); + TC_RUN(jaffar_knock_still_shakes_on_other_levels); + TC_RUN(jaffar_negative_phase_counts_through_to_fall); + TC_RUN(jaffar_negative_phase_is_shake_on_other_levels); + TC_RUN(jaffar_fall_flo_seeds_the_row_above); + TC_RUN(jaffar_fall_flo_ignores_other_rooms); + TC_RUN(jaffar_fall_flo_is_level_gated); + TC_RUN(jaffar_fall_flo_does_not_restart_running_countdown); +} diff --git a/applications/SprPoP/tests/host/t_mouse.c b/applications/SprPoP/tests/host/t_mouse.c new file mode 100644 index 0000000..24afb09 --- /dev/null +++ b/applications/SprPoP/tests/host/t_mouse.c @@ -0,0 +1,178 @@ +/* + * t_mouse — спецсобытие уровня 8: МЫШЬ (do_timers seg003:0545, + * do_mouse seg003:0ABA, autocontrol_mouse seg002:07EB). + * + * Единственный набор, куда линкуется guards.c: проверяется не физика (её + * держат t_phys/t_char), а связка «условие запуска → рождение → ИИ → + * нажатие кнопки → уход». Всё это ветки по charid внутри общего кода + * соперника, и сломать их можно, ничего не трогая в самой мыши. + * + * Кадр соперника здесь полный, с ИИ — pop_guard_tick() + pop_guard_phys_tick(), + * как в главном цикле; sc_guard_run для этого не годится (он намеренно без ИИ). + */ +#include + +#include "tcheck.h" +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_guard.h" +#include "pop_geom.h" +#include "pop_state.h" +#include "pop_level.h" /* pop_current_level (под тестом — из stubs.c) */ +#include "pop_tune.h" +#include "scene.h" +#include "stubs.h" + +#define E 0 /* empty */ +#define F 1 /* floor */ +#define O 15 /* opener */ +#define W 20 /* wall */ + +/* Верхний ряд комнаты 16 в упрощённом виде: сплошной пол и кнопка на + * колонке 7 — там же, где она стоит в данных уровня 8. Провал колонок + * 4..6 намеренно засыпан: он мешает Киду, а не мыши, и в тест на ИИ + * притащил бы падение соперника. */ +static const uint8_t room_mouse[30] = { + F, F, F, F, F, F, F, O, F, F, + W, W, W, W, W, W, W, W, W, W, + W, W, W, W, W, W, W, W, W, W, +}; + +/* Поставить сцену уровня 8: Кид в комнате 16, дверь уровня открыта. */ +static void scene_level8(void) +{ + sc_room(room_mouse, POP_MOUSE_ROOM); + sc_kid_at(1, 0, 1); + Kid.room = POP_MOUSE_ROOM; + pop_guard_reset(); + pop_current_level = POP_MOUSE_LEVEL; + pop_leveldoor_open = 1; + tk_log_clear(); +} + +/* Один полный кадр соперника, как в главном цикле sprpop.c. */ +static void mouse_frame(void) +{ + pop_guard_tick(); + pop_guard_phys_tick(); +} + +/* ---- рождение -------------------------------------------------------- */ + +TC_TEST(mouse_appears_after_delay) +{ + uint16_t i; + scene_level8(); + + /* Счётчик стартует с 1 («дверь открыта») и растёт по кадру, срабатывая + * на значении POP_MOUSE_DELAY — то есть кадров до мыши на два меньше. + * Прогоняем всё, кроме последнего: слот обязан быть ещё пуст. */ + for (i = 0; i < POP_MOUSE_DELAY - 2; i++) pop_check_mouse(); + TC_EQ(Guard.charid, 0); + + pop_check_mouse(); /* тот самый кадр */ + TC_EQ(Guard.charid, CHARID_24_MOUSE); + TC_EQ(Guard.x, POP_MOUSE_START_X); + TC_EQ(Guard.curr_row, 0); + TC_EQ(Guard.direction, -1); /* dir_FF_left */ + TC_EQ(guardhp_curr, 1); + TC_EQ(Guard.room, POP_MOUSE_ROOM); /* унаследована от Кида (loadkid) */ +} + +/* Счётчик тикает ТОЛЬКО пока Кид в комнате мыши: в оригинале это то же + * условие, что и запуск (Char.room == mouse_room), поэтому 12,5 секунды + * отсчитываются с возвращения игрока, а не с открытия двери. */ +TC_TEST(mouse_timer_frozen_outside_room) +{ + uint16_t i; + scene_level8(); + Kid.room = POP_MOUSE_ROOM + 1; + for (i = 0; i < POP_MOUSE_DELAY * 2; i++) pop_check_mouse(); + TC_EQ(Guard.charid, 0); + TC_EQ(pop_leveldoor_open, 1); /* не сдвинулся ни на кадр */ +} + +/* Закрытая дверь уровня — мыши нет вовсе (она подсказка на обратном пути). */ +TC_TEST(mouse_needs_open_leveldoor) +{ + uint16_t i; + scene_level8(); + pop_leveldoor_open = 0; + for (i = 0; i < POP_MOUSE_DELAY * 2; i++) pop_check_mouse(); + TC_EQ(Guard.charid, 0); +} + +/* ---- ИИ: пробежала, нажала, ушла ------------------------------------- */ + +/* Родить мышь, не гоняя POP_MOUSE_DELAY пустых кадров. */ +static void spawn_mouse(void) +{ + scene_level8(); + pop_leveldoor_open = POP_MOUSE_DELAY - 1; + pop_check_mouse(); +} + +TC_TEST(mouse_runs_left_and_presses_button) +{ + uint8_t i; + spawn_mouse(); + TC_EQ(Guard.charid, CHARID_24_MOUSE); + + /* Бежит влево: до порога разворота ей ~35 пикселей по 3-5 за кадр. */ + for (i = 0; i < 24; i++) mouse_frame(); + TC_TRUE(Guard.x < POP_MOUSE_START_X); + + /* По дороге наступила на кнопку (0,7) — общий check_press физики + * соперника, тот же, которым кнопки жмут страж и тень. */ + TC_TRUE(tk_log_count(TK_BUTTON) >= 1); +} + +TC_TEST(mouse_turns_back_and_disappears) +{ + uint8_t i; + spawn_mouse(); + + /* 120 кадров с запасом покрывают путь туда (200→166), стойку seq_107 + * (11 кадров) и путь обратно (166→200). */ + for (i = 0; i < 120; i++) { + mouse_frame(); + if (Guard.direction == DIR_56_NONE) break; + } + TC_EQ(Guard.direction, DIR_56_NONE); /* clear_char: ушла со сцены */ + TC_EQ(guardhp_curr, 0); +} + +/* ---- ветки по charid у соседнего кода -------------------------------- */ + +/* Мышь Кида «не видит»: иначе он выхватит на неё меч (seg003:0965). */ +TC_TEST(mouse_is_not_seen_by_kid) +{ + spawn_mouse(); + Guard.curr_row = Kid.curr_row; /* один ряд — условие луча */ + pop_check_can_guard_see_kid(); + TC_EQ(can_guard_see_kid, 0); +} + +/* leave_guard мышь не записывает в данные уровня (seg002:02F5): вернувшись + * в комнату, игрок не должен встретить «стража» с её параметрами. + * Комнату берём из диапазона таблицы заглушки (TK_ROOMS = 8). */ +TC_TEST(mouse_is_not_saved_as_room_guard) +{ + spawn_mouse(); + Guard.room = 1; + tk_gs_tile[0] = 0xEE; /* метка «никто сюда не писал» */ + pop_guard_leave(); + TC_EQ(tk_gs_tile[0], 0xEE); +} + +void main(void) +{ + sc_init(); + TC_RUN(mouse_appears_after_delay); + TC_RUN(mouse_timer_frozen_outside_room); + TC_RUN(mouse_needs_open_leveldoor); + TC_RUN(mouse_runs_left_and_presses_button); + TC_RUN(mouse_turns_back_and_disappears); + TC_RUN(mouse_is_not_seen_by_kid); + TC_RUN(mouse_is_not_saved_as_room_guard); +} diff --git a/applications/SprPoP/tests/host/t_phys.c b/applications/SprPoP/tests/host/t_phys.c new file mode 100644 index 0000000..a3c91a0 --- /dev/null +++ b/applications/SprPoP/tests/host/t_phys.c @@ -0,0 +1,380 @@ +/* + * t_phys.c — характеризация физики Кида (pop_map + pop_kid + pop_ctrl). + * + * Назначение набора — СТРАХОВКА ПОД РЕФАКТОРИНГ, а не проверка + * правильности. Эталоны здесь сняты с текущего билда, то есть + * консервируют и текущие баги; правильность по-прежнему проверяется + * против SDLPoP. Смысл в другом: перенос физики с `Kid.` на `Char.` + * обязан быть поведение-сохраняющим, и «трасса до == трасса после» ловит + * ровно тот класс ошибок, который там возможен. + * + * Поэтому при осознанном изменении поведения эталон ПЕРЕСНИМАЕТСЯ (см. + * SC_RECORD ниже), а в коммите должно быть написано, почему трасса + * поменялась. Молча обновлённый эталон обесценивает весь набор. + * + * Снятие эталона: + * make -C applications/SprPoP/tests/host t_phys SC_RECORD=1 + * Трассы уходят в отчёт tcheck готовыми C-строками — вставить в golden_*. + */ +#include "tcheck.h" +#include "scene.h" +#include "stubs.h" +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_ctrl.h" +#include "pop_guard.h" /* pop_do_delta_hp — урон сводится дельтой */ + +/* Коды тайлов (pop_map): 0 пусто, 1 пол, 3 колонна, 11 loose, 20 стена. */ +#define E 0 +#define F 1 +#define P 3 +#define L 11 +#define W 20 + +/* Ровный пол во всю комнату: базовая сцена для ходьбы/бега/разворота. */ +static const uint8_t room_flat[30] = { + W, E, E, E, E, E, E, E, E, E, + W, F, F, F, F, F, F, F, F, F, + W, W, W, W, W, W, W, W, W, W, +}; + +/* Пол с обрывом после колонки 5: падение с ряда 1 на ряд 2. */ +static const uint8_t room_ledge[30] = { + W, E, E, E, E, E, E, E, E, E, + W, F, F, F, F, F, E, E, E, E, + W, F, F, F, F, F, F, F, F, W, +}; + +/* Проваливающаяся плита на (1,4) — сцена под loose-механику. */ +static const uint8_t room_loose[30] = { + W, E, E, E, E, E, E, E, E, E, + W, F, F, F, L, F, F, F, F, F, + W, F, F, F, F, F, F, F, F, W, +}; + +/* Провал в ТРИ пустых тайла (колонки 2,3,4) — геометрия из BUG-RJUMP-1 + * (ур. 2, комнаты 1 и 9). Разбег-прыжок влево с колонки 5 обязан + * доставать до колонки 1; у нас Кид сваливается в провал. */ +static const uint8_t room_gap3[30] = { + W, E, E, E, E, E, E, E, E, E, + W, F, E, E, E, F, F, F, F, F, + W, F, F, F, F, F, F, F, F, W, +}; + +/* SC_RECORD= — снять эталон СЦЕНАРИЯ n (нумерация в порядке вызовов в + * main). По одному за прогон: лог tcheck 768 байт, на все трассы сразу + * его не хватает. Перебором занимается record_golden.py. */ +#ifndef SC_RECORD +#define SC_RECORD 0 +#endif + +/* Снять или сверить — одной строкой в каждом сценарии, чтобы режимы не + * разъезжались. Имя сценария = имя файла эталона. */ +#if SC_RECORD +static uint8_t sc_idx; +#define SC_CHECK(g, nm) \ + do { if (++sc_idx == (SC_RECORD)) sc_dump(nm); } while (0) +#else +#define SC_CHECK(g, nm) sc_expect((g), (uint8_t)(sizeof(g) / SC_REC_BYTES)) +#endif + +/* ---- эталоны (сняты SC_RECORD=1) ------------------------------------ * + * В режиме снятия эталонов файлов golden/ ещё нет — и не должно быть: + * первый прогон их и порождает. */ +#if !SC_RECORD + +static const uint8_t golden_stand[] = { +#include "golden/stand.h" +}; +static const uint8_t golden_run_right[] = { +#include "golden/run_right.h" +}; +static const uint8_t golden_step_shift[] = { +#include "golden/step_shift.h" +}; +static const uint8_t golden_turn[] = { +#include "golden/turn.h" +}; +static const uint8_t golden_crouch[] = { +#include "golden/crouch.h" +}; +static const uint8_t golden_jump_up[] = { +#include "golden/jump_up.h" +}; +static const uint8_t golden_fall_ledge[] = { +#include "golden/fall_ledge.h" +}; +static const uint8_t golden_loose[] = { +#include "golden/loose.h" +}; +static const uint8_t golden_rjump_gap3[] = { +#include "golden/rjump_gap3.h" +}; + +#else +/* SC_RECORD: имена нужны только чтобы SC_CHECK() раскрылся. */ +#define golden_stand 0 +#define golden_run_right 0 +#define golden_step_shift 0 +#define golden_turn 0 +#define golden_crouch 0 +#define golden_jump_up 0 +#define golden_fall_ledge 0 +#define golden_loose 0 +#define golden_rjump_gap3 0 +#endif + +/* ---- сценарии -------------------------------------------------------- */ + +TC_TEST(phys_stand_is_stable) +{ + /* Никакого ввода: Кид на ровном полу обязан стоять неподвижно. Тест + * дешёвый, но ловит самое обидное — «сам поехал» после правки + * геометрии или окна Char. */ + sc_room(room_flat, 1); + sc_kid_at(3, 1, -1); + sc_trace_clear(); + sc_run(0, 12); + SC_CHECK(golden_stand, "stand"); +} + +TC_TEST(phys_run_right) +{ + sc_room(room_flat, 1); + sc_kid_at(1, 1, 0); + sc_trace_clear(); + sc_run(SC_R, 24); + SC_CHECK(golden_run_right, "run_right"); +} + +TC_TEST(phys_step_with_shift) +{ + /* Shift + вперёд — осторожный шаг: одна из самых чувствительных к + * distance_to_edge веток. */ + sc_room(room_flat, 1); + sc_kid_at(3, 1, 0); + sc_trace_clear(); + sc_run(SC_R | SC_SHIFT, 20); + SC_CHECK(golden_step_shift, "step_shift"); +} + +TC_TEST(phys_turn_around) +{ + sc_room(room_flat, 1); + sc_kid_at(4, 1, 0); + sc_trace_clear(); + sc_run(SC_L, 16); + SC_CHECK(golden_turn, "turn"); +} + +TC_TEST(demo_backward_is_not_screen_mirrored) +{ + /* demo_moves уже задаёт forward/backward относительно Кида. На плите + * 0,6 он смотрит вправо, и move=2 обязан начать seq_5 turn, а не + * превратиться через user_control() в seq_1 run вправо к пропасти. */ + sc_room(room_flat, 1); + sc_kid_at(4, 1, 0); + pop_ctrl_auto_move(0); + pop_ctrl_auto_move(2); + /* SEQTBL_BASE+0x01CB — начало seq_5 в исходном kid_data.bin. */ + TC_EQ((uint16_t)(Kid.curr_seq - SEQTBL_BASE), 0x01CB); +} + +TC_TEST(phys_crouch_and_stand_up) +{ + sc_room(room_flat, 1); + sc_kid_at(4, 1, 0); + sc_trace_clear(); + sc_run(SC_D, 8); + sc_run(0, 12); + SC_CHECK(golden_crouch, "crouch"); +} + +TC_TEST(phys_jump_up_nothing_above) +{ + /* Над головой пусто — seq_28; проверяем и смещение по x, которое + * оригинал делает перед прыжком. */ + sc_room(room_flat, 1); + sc_kid_at(4, 1, -1); + sc_trace_clear(); + sc_run(SC_U, 20); + SC_CHECK(golden_jump_up, "jump_up"); +} + +TC_TEST(phys_run_off_ledge) +{ + /* Бег в обрыв: check_on_floor -> start_fall -> land на ряду 2. + * Сценарий, вокруг которого крутился BUG-FALL-SWORD-1. */ + sc_room(room_ledge, 1); + sc_kid_at(1, 1, 0); + sc_trace_clear(); + sc_run(SC_R, 34); + SC_CHECK(golden_fall_ledge, "fall_ledge"); +} + +TC_TEST(phys_loose_floor_breaks) +{ + /* Пробежка по loose-плите: тайл обязан затрястись и провалиться, а + * физика — сообщить об этом соседям (trob + перерисовка). */ + sc_room(room_loose, 1); + sc_kid_at(1, 1, 0); + sc_trace_clear(); + sc_run(SC_R, 34); + SC_CHECK(golden_loose, "loose"); +#if !SC_RECORD + /* Журнал заглушек: плита обязана была отвалиться (кусок отрывается — + * pop_loose_mob_spawn) и попросить перерисовку. Это уже не + * характеризация, а содержательная проверка — заглушка-пустышка её не + * дала бы. Пока плита ЖИВЁТ в отрисованной комнате, фазой владеет + * pop_loose_modif, а не trob — поэтому спрашиваем именно про кусок. */ + TC_TRUE(tk_log_count(TK_MOB_SPAWN) > 0); + TC_TRUE(tk_log_count(TK_REDRAW) > 0); +#endif +} + +TC_TEST(phys_loose_survives_room_change) +{ + /* GUARD против бага «плита не успевает провалиться»: Кид расшатал плиту + * и ушёл в соседнюю комнату — отсчёт обязан ПЕРЕЖИТЬ смену комнаты. + * В оригинале терять нечего (loose — обычный trob в общем списке, фаза + * в curr_room_modif); у нас фаза живёт в pop_loose_modif[] текущей + * комнаты, и её сдаёт/забирает пара leave_room + reset. */ + uint8_t pos = 1 * 10 + 4; /* loose-плита сцены room_loose */ + uint8_t phase = 3; /* середина отсчёта до провала (1..10) */ + + /* Саму анимацию проверяет phys_loose_floor_breaks выше; здесь нужна + * ровно передача фазы, поэтому ставим её явно — прогон бы привязал тест + * к тому, на каком кадре Кид добежит до плиты. */ + sc_room(room_loose, 1); + pop_loose_modif[pos] = phase; + + /* Уход из комнаты: фаза уезжает в room_modif + trob... */ + tk_log_clear(); + pop_loose_leave_room(); + TC_TRUE(tk_log_count(TK_TROB) > 0); /* плита сдана в общий список */ + + /* ...и возвращается при входе обратно — а не обнуляется, как раньше. */ + pop_loose_reset(); + TC_EQ(pop_loose_modif[pos], phase); + + /* Старт уровня, наоборот, обязан фазу ЗАБЫТЬ (load_level восстанавливает + * уровень целиком) — иначе дрожь протекала бы в следующую жизнь. */ + pop_loose_forget(); + TC_EQ(pop_loose_modif[pos], 0); +} + +TC_TEST(phys_loose_gate_survives_room_change) +{ + /* GUARD против гейта холостого хода (loose_any, pop_map.c). Гейт + * пропускает оба цикла pop_loose_tick, пока ни одна фаза не взведена, и + * взводится ПЯТЬЮ местами записи. Четыре из них — взвод дрожи от шага и + * сотрясения — уже прогоняет phys_loose_floor_breaks; пятое (фаза + * ВОССТАНОВЛЕНА входом в комнату) не покрывал никто, а именно оно даёт + * самый тихий из возможных отказов: плита, к которой Кид вернулся, + * застыла бы на полудроже навсегда. + * + * Поэтому проверяем не флаг (он статик модуля), а НАБЛЮДАЕМОЕ следствие: + * после возврата в комнату тик обязан ДВИГАТЬ фазу. */ + uint8_t pos = 1 * 10 + 4; /* loose-плита сцены room_loose */ + uint8_t phase = 3; /* середина отсчёта до провала (1..10) */ + + /* Начинаем со снятого гейта: старт уровня забывает все фазы, и ни одно + * место взвода после этого не срабатывает. */ + sc_room(room_loose, 1); + pop_loose_forget(); + pop_loose_tick(); /* холостой проход — гейт снят */ + + /* Плита осталась недодрожавшей в room_modif, Кид возвращается в комнату. */ + pop_loose_modif[pos] = phase; + pop_loose_leave_room(); + pop_loose_reset(); + TC_EQ(pop_loose_modif[pos], phase); + + /* И вот теперь тик обязан её ДВИНУТЬ, а не пропустить по снятому гейту. */ + pop_loose_tick(); + TC_EQ(pop_loose_modif[pos], (uint8_t)(phase + 1)); +} + +TC_TEST(phys_running_jump_over_3tile_gap) +{ + /* BUG-RJUMP-1. Разбег влево от колонки 8, дальше Up — разбег-прыжок. + * Ожидание оригинала: перелёт провала в три тайла и посадка на (1,1). + * Сейчас эталон фиксирует НАШЕ поведение (падение в провал) — когда + * баг починим, трасса поменяется осознанно и эталон переснимется. */ + sc_room(room_gap3, 1); + sc_kid_at(8, 1, -1); + sc_trace_clear(); + sc_run(SC_L, 8); /* разбег */ + sc_run(SC_L | SC_U, 26); /* прыжок и посадка */ + SC_CHECK(golden_rjump_gap3, "rjump_gap3"); +} + +/* Падение с ДВУХ рядов: пол только внизу, Кид стартует на кромке ряда 0. + * В обычном падении это −1 HP (land, seg005:173), под пером — ни урона, ни + * разгона. */ +static const uint8_t room_drop2[30] = { + W, F, E, E, E, E, E, E, E, E, + W, E, E, E, E, E, E, E, E, E, + W, F, F, F, F, F, F, F, F, W, +}; + +/* Максимальная скорость падения за прогон и итоговое HP — через сцену не + * видно (в трассу пишется поза), поэтому меряем прямо по Kid. */ +static uint8_t drop2_max_fall_y(uint8_t frames) +{ + uint8_t i, mx = 0; + for (i = 0; i < frames; i++) { + sc_trace_clear(); + sc_run(SC_R, 1); + if ((uint8_t)Kid.fall_y > mx) mx = (uint8_t)Kid.fall_y; + } + return mx; +} + +TC_TEST(phys_feather_fall_is_slow_and_harmless) +{ + uint8_t mx_normal, mx_feather, hp_normal, hp_feather; + + /* (1) как обычно: разгон до FALL_MAX и −1 HP за два ряда */ + sc_room(room_drop2, 1); + sc_kid_at(1, 0, 0); + pop_feather = 0; + mx_normal = drop2_max_fall_y(40); + pop_do_delta_hp(); /* урон копится дельтой, сводит её цикл */ + hp_normal = hitp_curr; + + /* (2) под зельем медленного падения (seg006:057C: ускорение 1, потолок 4; + * seqtbl уводит приземление в ветку stepfloat — без урона) */ + sc_room(room_drop2, 1); + sc_kid_at(1, 0, 0); + pop_feather = 200; + mx_feather = drop2_max_fall_y(40); + pop_do_delta_hp(); + hp_feather = hitp_curr; + pop_feather = 0; + + TC_TRUE(mx_normal > 4); /* обычное падение разгоняется */ + TC_TRUE(mx_feather <= 4); /* под пером потолок скорости — 4 */ + TC_TRUE(hp_normal < 3); /* два ряда обычно стоят HP */ + TC_EQ(hp_feather, 3); /* под пером — целым */ +} + +/* ---- точка входа ----------------------------------------------------- */ + +void main(void) +{ + sc_init(); + TC_RUN(phys_stand_is_stable); + TC_RUN(phys_run_right); + TC_RUN(phys_step_with_shift); + TC_RUN(phys_turn_around); + TC_RUN(demo_backward_is_not_screen_mirrored); + TC_RUN(phys_crouch_and_stand_up); + TC_RUN(phys_jump_up_nothing_above); + TC_RUN(phys_run_off_ledge); + TC_RUN(phys_loose_floor_breaks); + TC_RUN(phys_loose_survives_room_change); + TC_RUN(phys_loose_gate_survives_room_change); + TC_RUN(phys_running_jump_over_3tile_gap); + TC_RUN(phys_feather_fall_is_slow_and_harmless); +} diff --git a/applications/SprPoP/tests/host/t_shadow.c b/applications/SprPoP/tests/host/t_shadow.c new file mode 100644 index 0000000..2c96934 --- /dev/null +++ b/applications/SprPoP/tests/host/t_shadow.c @@ -0,0 +1,332 @@ +/* + * t_shadow — спецсобытия УРОВНЯ 12: встреча с тенью, общий урон, слияние. + * + * Что здесь проверяется и почему именно тестом, а не прогоном в MAME. Вся + * сцена уровня 12 — это набор УСЛОВИЙ, каждое из которых легко сломать, + * ничего не трогая в самой тени: тайл меча, знак `pop_united_shadow`, + * charid соперника, номер уровня. Проверить их в эмуляторе — значит + * дойти до 12-го уровня и разыграть бой; здесь это шесть вызовов. + * + * Порт: seg002:0064 check_shadow, seg002:0536 sword_disappears, + * seg006:2033 check_killed_shadow, seg000:1518 do_delta_hp, + * seg003:0735 timers, seg006:1063 «появление плит». + * + * Сам ИИ тени (autocontrol_shadow_level12) сюда НЕ вынесен: он решает + * «куда идти», и его исход — траектория, то есть предмет прогона со + * сценой и эталоном, а не одиночной проверки. Набор линкует guards.c + * (как t_mouse), поэтому добавить такой сценарий можно здесь же. + */ +#include + +#include "tcheck.h" +#include "pop_kid.h" +#include "pop_map.h" +#include "pop_guard.h" +#include "pop_geom.h" +#include "pop_state.h" +#include "pop_level.h" +#include "pop_tune.h" +#include "scene.h" +#include "stubs.h" + +#define E 0 /* empty */ +#define F 1 /* floor */ +#define W 20 /* wall */ +#define S 22 /* sword */ + +/* Комната 15 в объёме, который нужен событию: меч в (0,1), пол под ним. */ +static const uint8_t room15[30] = { + F, S, F, F, F, F, F, F, E, E, + W, W, W, W, W, W, W, W, F, F, + W, W, W, W, W, W, W, W, W, W, +}; + +/* Комната 2: верхний ряд с ПРОПАСТЬЮ в колонках 4..6 — те самые плиты, + * которые появляются под ногами после слияния. */ +static const uint8_t room2[30] = { + F, F, F, F, E, E, E, F, F, F, + W, W, W, W, W, W, W, W, W, W, + W, W, W, W, W, W, W, W, W, W, +}; + +/* Общая точка входа: уровень 12, слот соперника пуст, слияния не было. */ +static void scene_level12(void) +{ + uint8_t i; + pop_guard_reset(); + pop_current_level = SHADOW_FIGHT_LEVEL; + pop_united_shadow = 0; + pop_shadow_init = 0; + for (i = 0; i < 30; i++) tk_room_fg[SHADOW_FIGHT_ROOM][i] = room15[i]; + tk_log_clear(); +} + +/* ---- check_shadow: когда тень поднимается ---------------------------- */ + +/* Меч ещё лежит — тени нет. Условие ОБРАТНО уровню 5 (там тень приходит, + * пока зелье на месте), и перепутать их — самая вероятная ошибка порта. */ +TC_TEST(shadow12_waits_until_sword_taken) +{ + scene_level12(); + TC_EQ(pop_check_shadow(SHADOW_FIGHT_ROOM), 0); + TC_EQ(Guard.charid, 0); +} + +/* Меч подобран — тень падает в комнату (init_shad_12 + seq_7_fall). */ +TC_TEST(shadow12_rises_when_sword_gone) +{ + scene_level12(); + tk_room_fg[SHADOW_FIGHT_ROOM][SHADOW_SWORD_TILEPOS] = F; + TC_EQ(pop_check_shadow(SHADOW_FIGHT_ROOM), 1); + TC_EQ(Guard.charid, CHARID_1_SHADOW); + TC_EQ(Guard.x, 0x51); + /* y = 232 — НИЖЕ поля (192), и это не ошибка данных: поле беззнаковое, + * тень «падает» из-под комнаты и появляется сверху. А curr_row при + * этом РОВНО ИЗ ТАБЛИЦЫ (0), а не пересчитанный из y (тот дал бы −1): + * do_init_shad копирует семь полей как есть и ряд не трогает. Обе + * величины закреплены тестом — «починить» их выглядит соблазнительно. */ + TC_EQ(Guard.y, 0xE8); + TC_EQ(Guard.curr_row, 0); + TC_EQ(Guard.direction, 0); /* dir_0_right */ + TC_EQ(Guard.room, SHADOW_FIGHT_ROOM); + TC_EQ(guardhp_curr, 4); +} + +/* После слияния тень не поднимается больше НИКОГДА (флаг ушёл в −1). */ +TC_TEST(shadow12_never_rises_after_uniting) +{ + scene_level12(); + tk_room_fg[SHADOW_FIGHT_ROOM][SHADOW_SWORD_TILEPOS] = F; + pop_united_shadow = -1; + TC_EQ(pop_check_shadow(SHADOW_FIGHT_ROOM), 0); + TC_EQ(Guard.charid, 0); +} + +/* В чужой комнате событие не срабатывает — там поднимается обычный страж + * (возврат 0 = «занимайся слотом сам»). */ +TC_TEST(shadow12_other_room_leaves_slot_alone) +{ + scene_level12(); + TC_EQ(pop_check_shadow((uint8_t)(SHADOW_FIGHT_ROOM - 1)), 0); + TC_EQ(Guard.charid, 0); +} + +/* ---- sword_disappears ------------------------------------------------ */ + +TC_TEST(shadow12_sword_vanishes_leaving_room18) +{ + scene_level12(); + Kid.room = SWORD_GONE_ROOM; + tk_modif[SHADOW_SWORD_TILEPOS] = 5; /* блеск меча */ + pop_sword_disappears(); + TC_EQ(tk_room_fg[SHADOW_FIGHT_ROOM][SHADOW_SWORD_TILEPOS], F); + /* Модификатор гасится вместе с тайлом: ненулевой modif рисует на полу + * «фальшивый» узор (та же причина, что у поднятого зелья). */ + TC_EQ(tk_modif[SHADOW_SWORD_TILEPOS], 0); +} + +TC_TEST(shadow12_sword_stays_leaving_other_room) +{ + scene_level12(); + Kid.room = (uint8_t)(SWORD_GONE_ROOM - 1); + pop_sword_disappears(); + TC_EQ(tk_room_fg[SHADOW_FIGHT_ROOM][SHADOW_SWORD_TILEPOS], S); +} + +/* На других уровнях комната 18 — обычная комната. */ +TC_TEST(shadow12_sword_event_is_level_gated) +{ + scene_level12(); + pop_current_level = 11; + Kid.room = SWORD_GONE_ROOM; + pop_sword_disappears(); + TC_EQ(tk_room_fg[SHADOW_FIGHT_ROOM][SHADOW_SWORD_TILEPOS], S); +} + +/* ---- таймер вспышки -------------------------------------------------- */ + +/* Счётчик досчитывает до нуля и уходит в −1 — «уже слились». Ноль как + * конечное состояние был бы неотличим от «слияния не было». */ +TC_TEST(shadow12_timer_ends_at_minus_one) +{ + pop_united_shadow = 3; + pop_frame_timers(); TC_EQ(pop_united_shadow, 2); + pop_frame_timers(); TC_EQ(pop_united_shadow, 1); + pop_frame_timers(); TC_EQ(pop_united_shadow, -1); + pop_frame_timers(); TC_EQ(pop_united_shadow, -1); /* и больше не меняется */ +} + +TC_TEST(shadow12_timer_idle_when_zero) +{ + pop_united_shadow = 0; + pop_frame_timers(); + TC_EQ(pop_united_shadow, 0); +} + +/* ---- общий урон (do_delta_hp) ---------------------------------------- */ + +static void hp_scene(uint8_t charid, uint8_t level) +{ + scene_level12(); + pop_current_level = level; + Guard.charid = charid; + guardhp_curr = guardhp_max = 4; + hitp_beg_lev = 3; + pop_kid_hp_reset(); + guardhp_delta = -1; + hitp_delta = 0; +} + +/* Ранил тень — ранил себя: урон КОПИРУЕТСЯ, а не делится. */ +TC_TEST(shadow12_damage_is_shared) +{ + hp_scene(CHARID_1_SHADOW, SHADOW_FIGHT_LEVEL); + pop_do_delta_hp(); + TC_EQ(guardhp_curr, 3); + TC_EQ(hitp_curr, 2); +} + +TC_TEST(shadow12_damage_not_shared_with_guard) +{ + hp_scene(CHARID_2_GUARD, SHADOW_FIGHT_LEVEL); + pop_do_delta_hp(); + TC_EQ(guardhp_curr, 3); + TC_EQ(hitp_curr, 3); +} + +/* Тень уровней 4/5/6 боевой не бывает, но проверка обязана быть по УРОВНЮ, + * а не только по charid: иначе безобидная тень уровня 5 делилась бы HP. */ +TC_TEST(shadow12_damage_not_shared_on_other_level) +{ + hp_scene(CHARID_1_SHADOW, 5); + pop_do_delta_hp(); + TC_EQ(guardhp_curr, 3); + TC_EQ(hitp_curr, 3); +} + +/* ---- check_killed_shadow --------------------------------------------- */ + +static void kill_scene(uint8_t charid, int8_t kid_alive, int8_t guard_alive) +{ + scene_level12(); + Guard.charid = charid; + Guard.alive = guard_alive; + guardhp_curr = guardhp_max = 4; + hitp_beg_lev = 3; + pop_kid_hp_reset(); + Kid.charid = CHARID_0_KID; + Kid.alive = kid_alive; + guardhp_delta = hitp_delta = 0; + pop_flash_time = 0; + pop_immortal = 0; +} + +/* Тень мертва — Киду отсчитывается 100 урона, то есть гарантированно всё. */ +TC_TEST(shadow12_killing_shadow_kills_kid) +{ + kill_scene(CHARID_1_SHADOW, -1, 0); + pop_check_killed_shadow(); + TC_EQ(hitp_delta, -3); + TC_TRUE(pop_flash_time != 0); + TC_EQ(pop_flash_color, POP_FLASH_WHITE); +} + +/* И симметрично: смерть Кида добивает тень. */ +TC_TEST(shadow12_killing_kid_kills_shadow) +{ + kill_scene(CHARID_1_SHADOW, 0, -1); + pop_check_killed_shadow(); + TC_EQ(guardhp_delta, -4); +} + +/* Со стражем связи нет — условие оригинала `(charid | charid) == shadow` + * проходит только для пары Кид/тень. */ +TC_TEST(shadow12_kill_link_ignores_guard) +{ + kill_scene(CHARID_2_GUARD, -1, 0); + pop_check_killed_shadow(); + TC_EQ(hitp_delta, 0); + TC_EQ(pop_flash_time, 0); +} + +/* Оба живы — событие молчит (иначе бой убивал бы обоих в первом же кадре). */ +TC_TEST(shadow12_kill_link_silent_while_both_alive) +{ + kill_scene(CHARID_1_SHADOW, -1, -1); + pop_check_killed_shadow(); + TC_EQ(hitp_delta, 0); + TC_EQ(guardhp_delta, 0); +} + +/* ---- «появление плит» ------------------------------------------------ */ + +/* Сцена: Кид стоит в комнате 2 на колонке 4 верхнего ряда — прямо над + * пропастью. До слияния он обязан провалиться, после — достроить пол. */ +static void floors_scene(void) +{ + uint8_t i; + scene_level12(); + for (i = 0; i < 30; i++) tk_room_fg[2][i] = room2[i]; + sc_room(room2, 2); + sc_kid_at(4, 0, 1); + sc_trace_clear(); +} + +TC_TEST(shadow12_floors_appear_after_uniting) +{ + floors_scene(); + pop_united_shadow = -1; + sc_run(0, 2); + /* Не упал: действие осталось стоечным, ряд тот же. */ + TC_EQ(Kid.curr_row, 0); + TC_TRUE(Kid.action != 4); /* actions_4_in_freefall */ + /* Плита появилась И в карте комнаты, И в данных уровня. */ + TC_EQ(pop_tile_at(4, 0), 1); + TC_EQ(tk_room_fg[2][4], 1); + /* Перерисованы две клетки — сама и соседняя справа, на обеих страницах. */ + TC_TRUE(tk_log_count(TK_REDRAW) >= 2); +} + +/* До слияния пропасть остаётся пропастью. */ +TC_TEST(shadow12_floors_hidden_before_uniting) +{ + floors_scene(); + pop_united_shadow = 0; + sc_run(0, 2); + TC_EQ(pop_tile_at(4, 0), 0); + TC_TRUE(Kid.curr_row != 0 || Kid.action == 4); +} + +/* Событие привязано к КОМНАТЕ: в комнате 3 того же уровня пола нет. */ +TC_TEST(shadow12_floors_only_in_their_rooms) +{ + floors_scene(); + pop_united_shadow = -1; + Kid.room = 3; + sc_run(0, 2); + TC_EQ(pop_tile_at(4, 0), 0); +} + +void main(void) +{ + sc_init(); + TC_RUN(shadow12_waits_until_sword_taken); + TC_RUN(shadow12_rises_when_sword_gone); + TC_RUN(shadow12_never_rises_after_uniting); + TC_RUN(shadow12_other_room_leaves_slot_alone); + TC_RUN(shadow12_sword_vanishes_leaving_room18); + TC_RUN(shadow12_sword_stays_leaving_other_room); + TC_RUN(shadow12_sword_event_is_level_gated); + TC_RUN(shadow12_timer_ends_at_minus_one); + TC_RUN(shadow12_timer_idle_when_zero); + TC_RUN(shadow12_damage_is_shared); + TC_RUN(shadow12_damage_not_shared_with_guard); + TC_RUN(shadow12_damage_not_shared_on_other_level); + TC_RUN(shadow12_killing_shadow_kills_kid); + TC_RUN(shadow12_killing_kid_kills_shadow); + TC_RUN(shadow12_kill_link_ignores_guard); + TC_RUN(shadow12_kill_link_silent_while_both_alive); + TC_RUN(shadow12_floors_appear_after_uniting); + TC_RUN(shadow12_floors_hidden_before_uniting); + TC_RUN(shadow12_floors_only_in_their_rooms); +} diff --git a/applications/SprPoP/tests/host/t_timer.c b/applications/SprPoP/tests/host/t_timer.c new file mode 100644 index 0000000..b28ad27 --- /dev/null +++ b/applications/SprPoP/tests/host/t_timer.c @@ -0,0 +1,116 @@ +/* pop_timer — логические тики, pause/unlimited, читы и QuickSave state. */ +#include +#include "tcheck.h" +#include "pop_timer.h" + +/* Минимальный memory-stream вместо EMM. pop_timer_qsave использует ровно + * u8/u16, поэтому этот stub одновременно проверяет его endian-контракт. */ +static uint8_t stream[3]; +static uint8_t stream_pos; + +void pop_qs_u8(pop_qs_io_t *io, uint8_t *value) +{ + if (io->reading) { + if (io->apply) *value = stream[stream_pos]; + } else { + stream[stream_pos] = *value; + } + ++stream_pos; +} + +void pop_qs_u16(pop_qs_io_t *io, uint16_t *value) +{ + uint8_t lo, hi; + if (io->reading) { + lo = stream[stream_pos++]; hi = stream[stream_pos++]; + if (io->apply) *value = (uint16_t)(lo | ((uint16_t)hi << 8)); + } else { + stream[stream_pos++] = (uint8_t)*value; + stream[stream_pos++] = (uint8_t)(*value >> 8); + } +} + +TC_TEST(timer_starts_and_only_runs_when_allowed) +{ + pop_timer_new_game(); + TC_EQ(pop_timer_minutes, POP_TIMER_START_MINUTES); + TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); + TC_FALSE(pop_timer_tick(0, 1)); /* Settings: unlimited */ + TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); + TC_FALSE(pop_timer_tick(1, 0)); /* pause/HDD/cutscene */ + TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); + + pop_timer_minutes = 2; + pop_timer_ticks = 2; + TC_FALSE(pop_timer_tick(1, 1)); + TC_EQ(pop_timer_minutes, 2); + TC_EQ(pop_timer_ticks, 1); + TC_FALSE(pop_timer_tick(1, 1)); + TC_EQ(pop_timer_minutes, 1); + TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); +} + +TC_TEST(timer_expiry_is_one_shot) +{ + pop_timer_minutes = 1; + pop_timer_ticks = 1; + TC_TRUE(pop_timer_tick(1, 1)); + TC_EQ(pop_timer_minutes, 0); + TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); + TC_FALSE(pop_timer_tick(1, 1)); + TC_FALSE(pop_timer_tick(0, 1)); +} + +TC_TEST(timer_cheats_match_sdlpop_rules) +{ + pop_timer_minutes = 5; + pop_timer_cheat_subtract(); + TC_EQ(pop_timer_minutes, 4); + pop_timer_minutes = 1; + pop_timer_cheat_subtract(); + TC_EQ(pop_timer_minutes, 1); /* minus не создаёт expiry */ + pop_timer_minutes = 0; + pop_timer_cheat_add(); + TC_EQ(pop_timer_minutes, 1); + pop_timer_minutes = 0xFF; + pop_timer_cheat_add(); + TC_EQ(pop_timer_minutes, 0xFF); /* не сворачиваемся в 0 */ +} + +TC_TEST(timer_qsave_restores_exact_state_and_rejects_bad_tick) +{ + pop_qs_io_t io; + + pop_timer_minutes = 42; + pop_timer_ticks = 103; + stream_pos = 0; + io.reading = io.apply = io.failed = 0; + pop_timer_qsave(&io); + TC_EQ(stream_pos, 3); + + pop_timer_minutes = 7; + pop_timer_ticks = 400; + stream_pos = 0; + io.reading = io.apply = 1; io.failed = 0; + pop_timer_qsave(&io); + TC_FALSE(io.failed); + TC_EQ(pop_timer_minutes, 42); + TC_EQ(pop_timer_ticks, 103); + + stream[0] = 9; stream[1] = 0; stream[2] = 0; /* tick 0 — impossible */ + pop_timer_minutes = 33; + pop_timer_ticks = 444; + stream_pos = 0; io.failed = 0; + pop_timer_qsave(&io); + TC_TRUE(io.failed); + TC_EQ(pop_timer_minutes, 33); + TC_EQ(pop_timer_ticks, 444); +} + +void main(void) +{ + TC_RUN(timer_starts_and_only_runs_when_allowed); + TC_RUN(timer_expiry_is_one_shot); + TC_RUN(timer_cheats_match_sdlpop_rules); + TC_RUN(timer_qsave_restores_exact_state_and_rejects_bad_tick); +} diff --git a/applications/SprPoP/tests/host/t_wall.c b/applications/SprPoP/tests/host/t_wall.c new file mode 100644 index 0000000..8086372 --- /dev/null +++ b/applications/SprPoP/tests/host/t_wall.c @@ -0,0 +1,95 @@ +/* + * t_wall.c — удар о стену В ВОЗДУХЕ (BUG-JUMPWALL-1). + * + * Наблюдение с приёмки уровня 3 (комната 14): Кид прыгает, не долетает и + * вместо удара о кладку с последующим сползанием ВДОЛЬ неё пролетает + * сквозь стену «по свободной траектории». Класс отказа шире одной + * комнаты, поэтому проверяем его отдельным набором, а не сценарием в + * t_phys: там характеризация (трасса == эталон), здесь — утверждение о + * поведении («сквозь кладку нельзя»), которое не должно молча + * переснашиваться вместе с эталонами. + * + * Оригинал: bumped_fall (seg004:04E4) на ударе гасит `Char.fall_x`, то есть + * горизонтальный снос обнуляется и дальше Кид падает вертикально. Кто + * решает, что удар был, — check_collisions (seg004:0004) + check_bumped. + * + * Проверка — свипом по стартовому X, а не одним прыжком: исход зависит от + * фазы, и «с одной X работает» ничего не доказывает (ровно так и вышло с + * первым сценарием — из центра плиты стена держит). + */ +#include "tcheck.h" +#include "scene.h" +#include "stubs.h" +#include "pop_kid.h" +#include "pop_map.h" + +#define E 0 +#define F 1 +#define W 20 + +/* Комната 14 уровня 3 с одной правкой: шахте колонок 6–7 подложено дно + * (ряд 2), потому что соседних комнат у сцены нет, а в игре Кид улетает + * вниз в комнату 13. Остальное — как в данных: площадка (0,8), провал + * (0,6)/(0,7), СТЕНА в колонке 5 рядов 1–2, площадка (0,5) за ней. */ +static const uint8_t room14[30] = { + F, F, F, E, E, F, E, E, F, E, + W, W, W, E, E, W, E, E, W, E, + W, W, W, F, F, W, F, F, W, W, +}; + +/* Плита (0,8) по X: колонка 8 — это [177, 191), плюс шов с пустой (0,9). */ +#define X_LO 177 +#define X_HI 196 + +/* Прыгнуть влево со стартовой X. Возврат — код исхода: + * 'L' — долетел до площадки (0,5) и стоит на ней (ряд 0) — законно; + * '.' — не долетел и упал на дно шахты колонок 6–7 — тоже законно; + * цифра — колонка, в которой Кид оказался ЛЕВЕЕ стены, то есть прошёл + * сквозь кладку колонки 5 (это и есть баг). */ +static char jump_left_from(uint8_t x) +{ + sc_room(room14, 14); + sc_kid_at_x(8, 0, x, -1 /* лицом влево */); + sc_trace_clear(); + /* Клавиши держим только на сам прыжок: если удерживать их всю сцену, + * Кид, ДОЛЕТЕВШИЙ до (0,5), тут же прыгает с неё второй раз — и исход + * говорит уже не о стене, а о втором прыжке. */ + sc_run(SC_L | SC_U, 6); + sc_run(0, 34); + if (!sc_len) return '?'; + { + int8_t col = sc_trace[sc_len - 1].col; + int8_t row = sc_trace[sc_len - 1].row; + if (row == 0) return 'L'; /* остался на верхнем ряду */ + if (col >= 6) return '.'; /* сполз вдоль стены в шахту */ + return (char)('0' + (col < 0 ? 0 : col)); + } +} + +static void put(char c) +{ + if (tc_result.logn < TC_LOGSZ) tc_result.log[tc_result.logn++] = (uint8_t)c; +} +static void puts_(const char *s) { while (*s) put(*s++); } + +TC_TEST(wall_stops_undershot_jump) +{ + uint8_t x, bad = 0; + /* Карта исходов по всей ширине плиты: 'L' — долетел до (0,5), '.' — + * сполз вдоль стены в шахту, цифра — просочился сквозь кладку. */ + puts_("x=177..196: "); + for (x = X_LO; x <= X_HI; x++) { + char r = jump_left_from(x); + put(r); + if (r != 'L' && r != '.') bad++; + } + put('\n'); + TC_EQ(bad, 0); +} + +int main(void) +{ + sc_init(); + TC_RUN(wall_stops_undershot_jump); + return 0; +} diff --git a/applications/SprPoP/tools/paths.py b/applications/SprPoP/tools/paths.py new file mode 100644 index 0000000..7577cdc --- /dev/null +++ b/applications/SprPoP/tools/paths.py @@ -0,0 +1,72 @@ +"""paths.py — единственное место, где инструменты SprPoP знают раскладку +каталогов приложения. Все упаковщики берут пути отсюда, поэтому папку +SprPoP можно перенести куда угодно: важно лишь, что рядом с tools/ лежат +assets/, gen/ и build/. + +Раскладка: + + assets/orig/ ОРИГИНАЛЬНЫЕ данные (SDLPoP, MSDOS, записи музыки). + В репозиторий не идут — копирайт; откуда их взять, + написано в assets/orig/README.md. + build/atl// промежуточный выхлоп упаковщиков: отдельные атласы, + из которых потом склеиваются архивы. Пересоздаётся, + в репозиторий не идёт. + assets/packed/ ровно то, что ложится на диск игры, уже в раскладке + каталогов диска (BG/, KID/, ...). В репозитории — + иначе из чистого клона без assets/orig/ ничего не + собрать. + gen/ генерируемые C-заголовки. Тоже в репозитории и по + той же причине: это такой же производный артефакт, + как .arc, и без него не скомпилировать src/. +""" + +from pathlib import Path + +TOOLS = Path(__file__).resolve().parent +APP = TOOLS.parent + +# --- вход: оригинальные данные ------------------------------------------- +ORIG = APP / "assets" / "orig" +SDLPOP = ORIG / "SDLPoP" +SDLPOP_DATA = SDLPOP / "data" +SDLPOP_SRC = SDLPOP / "src" +MSDOS = ORIG / "MSDOS" +MUSIC_ROOT = ORIG / "PoP1_DOS_music" +# Записи DOS-версии лежат в четырёх вариантах. Имена треков во всех наборах +# одинаковые, различаются каталог и расширение; ogg_MT-32 — та же музыка в +# исполнении Roland MT-32, а не другой кодек. Ключ = имя формата в Makefile. +MUSIC_FORMATS = { + "flac": ("flac", ".flac"), + "mp3": ("mp3", ".mp3"), + "ogg": ("ogg", ".ogg"), + "mt32": ("ogg_MT-32", ".ogg"), +} + + +def music_src(fmt="flac"): + """(каталог, расширение) исходников музыки для формата.""" + if fmt not in MUSIC_FORMATS: + raise SystemExit( + "неизвестный формат музыки %r; есть: %s" + % (fmt, ", ".join(sorted(MUSIC_FORMATS)))) + sub, ext = MUSIC_FORMATS[fmt] + return MUSIC_ROOT / sub, ext + +# --- выход ---------------------------------------------------------------- +ATL = APP / "build" / "atl" # промежуточные атласы, по набору в папке +PACKED = APP / "assets" / "packed" # раскладка диска игры +GEN = APP / "gen" # генерируемые заголовки + + +def atl(name): + """Каталог промежуточного выхлопа набора; создаётся при обращении.""" + p = ATL / name + p.mkdir(parents=True, exist_ok=True) + return p + + +def packed(name): + """Каталог диска игры (BG, KID, ...); создаётся при обращении.""" + p = PACKED / name + p.mkdir(parents=True, exist_ok=True) + return p diff --git a/applications/SprPoP/tools/pop_extract_font.py b/applications/SprPoP/tools/pop_extract_font.py new file mode 100644 index 0000000..6f394ba --- /dev/null +++ b/applications/SprPoP/tools/pop_extract_font.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +"""pop_extract_font.py — SDLPoP-шрифты в один Sprinter .atl-ресурс. + +Источник истины — оригинальные встроенные шрифты SDLPoP: + * hc_small_font_data[] из menu.c — пункты меню; + * hc_font_data[] из seg009.c — крупные сообщения/заголовки. + +В исходнике глиф — IMAGE_DATA(height, width, flags) и 1-битные строки. +Здесь его силуэт переводится в getimage (0xFF = прозрачность) и пакуется в +FONT\\FONT.ATL. Это именно шрифтовой атлас, а не заранее нарисованные +строки: номер ленты = код ASCII, поэтому runtime сохраняет произвольный +текст и пропорциональную ширину оригинала. + +Хвост страницы [0x3C00..0x3FFF] намеренно оставлен свободным: pop_menu.c +кладёт туда снимок игровой палитры на время паузы. Тогда 1 КБ не занимает +дефицитную W2. +""" +import re +from pathlib import Path + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +ROOT = Path(__file__).resolve().parent.parent +SRC_SMALL = P.SDLPOP_SRC / "menu.c" +SRC_BIG = P.SDLPOP_SRC / "seg009.c" +OUT_DIR = P.atl("font") +OUT_ATL = OUT_DIR / "font.atl" +OUT_H = P.GEN / "pop_font.h" + +FIRST, LAST = 32, 126 +FONT_COLOR = 0x3F +# ВТОРОЙ ЦВЕТ КРУПНОГО ШРИФТА. У SDLPoP шрифт — маска, и цвет задаётся +# параметром рисования (show_hof_text рисует текст дважды: тень цветом 0 со +# сдвигом (1,1) и сам текст цветом 0xB7). У нас цвет запечён в пиксели +# глифа, поэтому второй цвет — это второй набор глифов. Он нужен таблице +# рекордов: чёрная тень строк и тёмное имя на золотой полосе ввода. +# Набор крупный, потому что draw_text() рисует hc_font (seg009:1594). +FONT_DARK_COLOR = 0x3E +# Каталог SPA1 хранит число картинок одним байтом, а три полных набора по +# 95 глифов — это 285. Тёмному набору полный ASCII и не нужен: им пишется +# только таблица рекордов (имена A..Z, время вида 59:43 и курсор ввода +# '_'), поэтому он обрезан по '_'. 190 + 64 = 254 картинки — потолок. +DARK_FIRST, DARK_LAST = 32, 95 +ATL_MAGIC = b"SPA1" +ATL_DIR_OFF = 0x68 +SNAPSHOT_OFF = 0x3C00 +PAGE_SIZE = 16384 + + +def extract_array(path: Path, name: str) -> list[int]: + text = path.read_text() + match = re.search(rf"byte {name}\[\] = \{{(.*?)\n\}};", text, re.S) + assert match, f"{name} not found in {path}" + body = re.sub(r"//[^\n]*", "", match.group(1)) + body = re.sub(r"/\*.*?\*/", "", body, flags=re.S) + + def word(mm: re.Match) -> str: + value = int(mm.group(1), 0) + return f"{value & 0xFF}, {(value >> 8) & 0xFF}" + + def bin4(mm: re.Match) -> str: + b7, b6, b5, b4 = (int(v, 0) if v != "_" else 0 for v in mm.groups()) + return str((b4 << 4) | (b5 << 5) | (b6 << 6) | (b7 << 7)) + + def bin8(mm: re.Match) -> str: + bits = [int(v, 0) if v != "_" else 0 for v in mm.groups()] + return str(sum(bit << (7 - i) for i, bit in enumerate(bits))) + + def image_data(mm: re.Match) -> str: + h, w, flags = (int(v, 0) for v in mm.groups()) + return (f"{h & 0xFF}, {(h >> 8) & 0xFF}, " + f"{w & 0xFF}, {(w >> 8) & 0xFF}, " + f"{flags & 0xFF}, {(flags >> 8) & 0xFF}") + + body = re.sub(r"\bWORD\s*\(\s*(\w+)\s*\)", word, body) + body = re.sub(r"\bIMAGE_DATA\s*\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\)", + image_data, body) + body = re.sub(r"\bBINARY_4\s*\(\s*([_\dxXa-fA-F]+)\s*,\s*([_\dxXa-fA-F]+)\s*,\s*" + r"([_\dxXa-fA-F]+)\s*,\s*([_\dxXa-fA-F]+)\s*\)", bin4, body) + body = re.sub(r"\bBINARY_8\s*\(\s*([_\dxXa-fA-F]+)\s*,\s*([_\dxXa-fA-F]+)\s*,\s*" + r"([_\dxXa-fA-F]+)\s*,\s*([_\dxXa-fA-F]+)\s*,\s*([_\dxXa-fA-F]+)\s*,\s*" + r"([_\dxXa-fA-F]+)\s*,\s*([_\dxXa-fA-F]+)\s*,\s*([_\dxXa-fA-F]+)\s*\)", bin8, body) + body = re.sub(r"\b_\b", "0", body) + # Hex обязан идти первым: иначе regexp съедает в `0x20` только `0`. + return [int(v, 0) for v in re.findall(r"0[xX][0-9a-fA-F]+|\d+", body)] + + +def parse_glyphs(vals: list[int]) -> tuple[dict, list[dict]]: + first, last = vals[0], vals[1] + font = { + "first": first, + "last": last, + "ascent": vals[2] | (vals[3] << 8), + "below": vals[4] | (vals[5] << 8), + "space_lines": vals[6] | (vals[7] << 8), + "space_chars": vals[8] | (vals[9] << 8), + } + pos = 10 + (last - first + 1) * 2 # offsets у SDLPoP вычисляются в runtime + glyphs = [] + while pos < len(vals): + h = vals[pos] | (vals[pos + 1] << 8) + w = vals[pos + 2] | (vals[pos + 3] << 8) + pos += 6 # height, width, flags + stride = (w + 7) // 8 + rows = [] + for _ in range(h): + rows.append(vals[pos:pos + stride]) + pos += stride + glyphs.append({"h": h, "w": w, "rows": rows}) + assert len(glyphs) == last - first + 1, f"glyph count {len(glyphs)}" + return font, glyphs + + +def glyph_pixels(glyph: dict, color: int) -> bytes: + """1 бит/пиксель SDLPoP -> 8bpp getimage: 1 = color.""" + out = bytearray() + for row in glyph["rows"]: + for x in range(glyph["w"]): + byte = row[x // 8] + on = byte & (1 << (7 - (x & 7))) + out.append(color if on else 0xFF) + return bytes(out) + + +def pack_atlas(fonts: list[tuple[dict, list[dict], int]]) -> int: + entries = [] + for font, glyphs, color, first, last in fonts: + assert font["first"] <= first <= last <= font["last"] + entries.extend((glyph, color) for glyph in + glyphs[first - font["first"]:last - font["first"] + 1]) + assert len(entries) <= 255 + + data_off = ATL_DIR_OFF + len(entries) * 8 + out = bytearray(data_off) + out[0:4] = ATL_MAGIC + out[4] = len(entries) + for index, (glyph, color) in enumerate(entries): + offset = len(out) + pixels = glyph_pixels(glyph, color) + out += glyph["w"].to_bytes(2, "little") + out += glyph["h"].to_bytes(2, "little") + out += pixels + directory = ATL_DIR_OFF + index * 8 + out[directory:directory + 2] = offset.to_bytes(2, "little") + out[directory + 2] = glyph["w"] + out[directory + 3] = glyph["h"] + out[directory + 4] = 1 + out[directory + 5] = 1 + + if len(out) > SNAPSHOT_OFF: + raise SystemExit(f"font atlas: {len(out)} Б, нет места для palette snapshot @ {SNAPSHOT_OFF:#x}") + OUT_DIR.mkdir(parents=True, exist_ok=True) + OUT_ATL.write_bytes(out) + return len(out) + + +def emit_header(small: dict, big: dict, atlas_size: int) -> None: + small_n = LAST - FIRST + 1 + text = f"""/* + * pop_font.h — контракт FONT\\FONT.ATL, СГЕНЕРИРОВАН. + * Источник: SDLPoP hc_small_font_data[] и hc_font_data[]. + * Генератор: toolchain/pop_extract_font.py. Руками не править. + */ +#ifndef POP_FONT_H +#define POP_FONT_H + +#include + +#define POP_FONT_COLOR 0x{FONT_COLOR:02X} +#define POP_FONT_DARK_COLOR 0x{FONT_DARK_COLOR:02X} +#define POP_FONT_FIRST {FIRST} +#define POP_FONT_LAST {LAST} +#define POP_FONT_SMALL_BASE 0 +#define POP_FONT_SMALL_ASCENT {small['ascent']} +#define POP_FONT_SMALL_SPACE {small['space_chars']} +#define POP_FONT_BIG_BASE {small_n} +#define POP_FONT_BIG_ASCENT {big['ascent']} +#define POP_FONT_BIG_SPACE {big['space_chars']} +/* Тот же КРУПНЫЙ шрифт цветом POP_FONT_DARK_COLOR: тень и текст на светлом. + * Набор обрезан по '_' — им пишется только таблица рекордов. */ +#define POP_FONT_DARK_BASE {2 * small_n} +#define POP_FONT_DARK_FIRST {DARK_FIRST} +#define POP_FONT_DARK_LAST {DARK_LAST} +#define POP_FONT_SNAPSHOT_OFF 0x{SNAPSHOT_OFF:04X} +#define POP_FONT_ATLAS_SIZE {atlas_size} + +#endif +""" + OUT_H.write_text(text) + + +def main() -> None: + parsed = [] + for path, name in ((SRC_SMALL, "hc_small_font_data"), (SRC_BIG, "hc_font_data")): + font, glyphs = parse_glyphs(extract_array(path, name)) + print(f"{name}: chars={font['first']}..{font['last']} ascent={font['ascent']} " + f"space={font['space_chars']}") + parsed.append((font, glyphs)) + sets = [(parsed[0][0], parsed[0][1], FONT_COLOR, FIRST, LAST), + (parsed[1][0], parsed[1][1], FONT_COLOR, FIRST, LAST), + (parsed[1][0], parsed[1][1], FONT_DARK_COLOR, DARK_FIRST, DARK_LAST)] + atlas_size = pack_atlas(sets) + emit_header(parsed[0][0], parsed[1][0], atlas_size) + print(f"{OUT_ATL}: 2 x {LAST - FIRST + 1} + {DARK_LAST - DARK_FIRST + 1} glyphs, " + f"{atlas_size} Б; " + f"snapshot from 0x{SNAPSHOT_OFF:04X}") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_extract_kid_data.py b/applications/SprPoP/tools/pop_extract_kid_data.py new file mode 100644 index 0000000..585fbc6 --- /dev/null +++ b/applications/SprPoP/tools/pop_extract_kid_data.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +""" +pop_extract_kid_data.py — извлекает данные анимации Kid из исходников +SDLPoP в C-заголовок gen/kid_data.h (Фаза K1). + +Извлекает: + frame_table_kid[] (seg006.c) -> kframe kid_frames[] {image,dx,dy,flags} + original_seqtbl[] (seqtbl.c) -> byte kid_seqtbl[] (2310 Б, база 0x196E) + original_seqtbl_offsets[] (seqtbl.c) -> word kid_seq_off[] (seq_id -> адрес) + +seqtbl адресуется базой SEQTBL_BASE=0x196E: элемент = kid_seqtbl[addr-0x196E]. +Автогенерация — чтобы не переписывать ~180 кадров + 2КБ байткода руками. +""" +import os +import re +import sys as _sys +_sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +SRC = str(P.SDLPOP_SRC) +OUT_BIN_DIR = str(P.atl("kid")) +OUT = str(P.GEN / "kid_data.h") +SEQTBL_BASE = 0x196E + + +def read(name): + return open(os.path.join(SRC, name)).read() + + +def parse_frame_table(txt, name="frame_table_kid"): + """frame_table_kid[]/frame_tbl_guard[] -> список (image, dx, dy, flags). + Записи вида { IMG, 0xC0| 4, DX, DY, 0x40| 4 } — sword/flags как + OR-выражения.""" + m = re.search(r'const frame_type ' + name + r'\[\]\s*=\s*\{(.*?)\n\};', txt, re.S) + body = m.group(1) + frames = [] + for row in re.findall(r'\{([^}]*)\}', body): + parts = [p.strip() for p in row.split(',')] + image = int(parts[0], 0) + sword = eval(parts[1], {}, {}) # напр. "0x00| 9" + dx = int(parts[2], 0) + dy = int(parts[3], 0) + flags = eval(parts[4], {}, {}) + frames.append((image & 0xFF, dx & 0xFF, dy & 0xFF, flags & 0xFF, sword & 0xFF)) + return frames + + +def parse_sword_tbl(txt): + """sword_tbl[] (seg006:2047) -> список (id, x, y). id 255 = клинка нет. + Индекс — sword_frame (cur_frame.sword & 0x3F); один и тот же chtab_0 и у + Кида, и у стража.""" + m = re.search(r'const sword_table_type sword_tbl\[\]\s*=\s*\{(.*?)\n\};', txt, re.S) + rows = [] + for row in re.findall(r'\{([^}]*)\}', m.group(1)): + a, b, c = [int(p.strip(), 0) for p in row.split(',')] + rows.append((a & 0xFF, b & 0xFF, c & 0xFF)) + return rows + + +def parse_byte_array(txt, name): + m = re.search(r'const byte %s\[\]\s*=\s*\{(.*?)\}' % name, txt, re.S) + return [int(x, 0) for x in re.findall(r'0x[0-9A-Fa-f]+|\d+', m.group(1))] + + +def parse_word_array(txt, name): + m = re.search(r'const word %s\[\]\s*=\s*\{(.*?)\}' % name, txt, re.S) + return [int(x, 0) for x in re.findall(r'0x[0-9A-Fa-f]+|\d+', m.group(1))] + + +def emit_c(vals, per_line=12, fmt="0x%02X"): + out = [] + for i in range(0, len(vals), per_line): + out.append(" " + ",".join(fmt % v for v in vals[i:i + per_line]) + ",") + return "\n".join(out) + + +def main(): + seg006 = read("seg006.c") + seqc = read("seqtbl.c") + + frames = parse_frame_table(seg006) + gframes = parse_frame_table(seg006, "frame_tbl_guard") + # Кадры КАТСЦЕН (frame_tbl_cuts, seg006:417) — Princess и Vizier. Нужны + # сценам 8/9 и финалу: там персонажи ходят теми же последовательностями + # seqtbl, что и в игре, но кадры адресуют свои chtab (sword>>6: 1 = набор + # PV1 ресурса 800, 2 = PV2 ресурса 900). + cframes = parse_frame_table(seg006, "frame_tbl_cuts") + seqtbl = parse_byte_array(seqc, "original_seqtbl") + offs = parse_word_array(seqc, "original_seqtbl_offsets") + swtbl = parse_sword_tbl(seg006) + + with open(OUT, "w") as f: + f.write("/* kid_data.h — данные анимации Kid (frame_table + seqtbl +\n" + " * offsets), извлечено toolchain/pop_extract_kid_data.py из\n" + " * SDLPoP. НЕ править вручную. seqtbl адресуется базой\n" + " * SEQTBL_BASE: kid_seqtbl[addr - SEQTBL_BASE]. */\n") + f.write("#ifndef KID_DATA_H\n#define KID_DATA_H\n#include \n\n") + f.write("#define SEQTBL_BASE 0x%04Xu\n\n" % SEQTBL_BASE) + + # frame table и seqtbl В .h НЕ ПОПАДАЮТ — они уезжают в kid.ani + # (EMM-страница): 3.5 КБ в _CODE окна W1/W2 нам дороже, чем один + # gfx_w0_map за тик. См. applications/PoP/docs/layout_plan_v2.md §5.1. + f.write("/* {image, dx, dy, flags, sword}; chtab = sword>>6 (0=kid).\n" + " * САМА таблица кадров и seqtbl лежат в kid.ani (EMM-\n" + " * страница); здесь только тип и размеры. */\n") + f.write("typedef struct { uint8_t image; int8_t dx, dy; " + "uint8_t flags, sword; } kframe;\n") + f.write("#define KID_NFRAMES %d\n" % len(frames)) + f.write("#define KID_SEQTBL_LEN %d\n\n" % len(seqtbl)) + + # раскладка kid.ani (смещения ОТ НАЧАЛА ДАННЫХ страницы) + f.write("/* Раскладка kid.ani: кадры (KID_NFRAMES*5), затем seqtbl. */\n") + f.write("#define KID_BIN_FRAMES_OFF 0\n") + f.write("#define KID_BIN_SEQTBL_OFF %d\n" % (len(frames) * 5)) + f.write("/* Таблица кадров СТРАЖА (frame_tbl_guard, seg006:372): индекс\n" + " * frame + add_frame - 149 (add_frame=70 для кадров 102..106). */\n") + f.write("#define KID_NGFRAMES %d\n" % len(gframes)) + f.write("#define KID_BIN_GFRAMES_OFF %d\n" % (len(frames) * 5 + len(seqtbl))) + f.write("/* Таблица кадров КАТСЦЕН (frame_tbl_cuts, seg006:417):\n" + " * Princess/Vizier, индекс — номер кадра как есть. Набор\n" + " * спрайтов выбирает sword>>6 (1 = PV1 res800, 2 = PV2 res900). */\n") + f.write("#define KID_NCFRAMES %d\n" % len(cframes)) + f.write("#define KID_BIN_CFRAMES_OFF %d\n\n" + % (len(frames) * 5 + len(seqtbl) + len(gframes) * 5)) + + # seq offsets + # kid_seq_off — МАКРО-инициализатором, не готовым массивом: заголовок + # включают почти все модули PoP, и `static const` размножал таблицу по + # каждому TU мёртвой копией (замер 2026-08-08: 230 Б × 9 модулей = + # 1840 Б впустую, в т.ч. 460 Б резидента W1/W2). Сам массив + # определяет ОДИН pop_kid.c — та же причина, что у sword_tbl ниже. + f.write("#define KID_NSEQ %d\n" % len(offs)) + f.write("#define KID_SEQ_OFF_INIT { \\\n") + f.write(emit_c(offs, 8, "0x%04X").replace("\n", " \\\n") + " \\\n}\n\n") + + # sword_tbl — макро-инициализаторами, а не готовыми массивами: так + # 159 байт таблицы лягут в ОДИН TU (pop_kid.c), а не в каждый, кто + # включил заголовок. + f.write("/* sword_tbl (seg006:2047): клинок отдельным спрайтом chtab_0.\n" + " * Индекс — sword_frame (кадр.sword & 0x3F), id 255 = клинка нет.\n" + " * Смещения x/y — ЭКРАННЫЕ (применяются после calc_screen_x_coord),\n" + " * x — «вперёд» по направлению персонажа. */\n") + f.write("#define KID_NSWORD %d\n" % len(swtbl)) + for i, (nm, fmt) in enumerate((("ID", "0x%02X"), ("DX", "%d"), ("DY", "%d"))): + # dx/dy — знаковые: печатаем со знаком, иначе int8_t = overflow + vals = [r[i] if i == 0 else (r[i] - 256 if r[i] > 127 else r[i]) + for r in swtbl] + f.write("#define KID_SWORD_%s_INIT { \\\n" % nm) + f.write(emit_c(vals, 12, fmt).replace("\n", " \\\n") + " \\\n}\n") + f.write("\n#endif\n") + + # kid.ani: кадры (по 5 байт) + seqtbl подряд + os.makedirs(OUT_BIN_DIR, exist_ok=True) + binpath = os.path.join(OUT_BIN_DIR, "kid.ani") + with open(binpath, "wb") as b: + for (img, dx, dy, fl, sw) in frames: + b.write(bytes((img & 0xFF, dx & 0xFF, dy & 0xFF, fl & 0xFF, sw & 0xFF))) + b.write(bytes(seqtbl)) + for (img, dx, dy, fl, sw) in gframes: + b.write(bytes((img & 0xFF, dx & 0xFF, dy & 0xFF, fl & 0xFF, sw & 0xFF))) + for (img, dx, dy, fl, sw) in cframes: + b.write(bytes((img & 0xFF, dx & 0xFF, dy & 0xFF, fl & 0xFF, sw & 0xFF))) + print(f"kid.ani: {len(frames)*5 + len(seqtbl) + len(gframes)*5 + len(cframes)*5} Б " + f"(кадры Kid + seqtbl + кадры стража + кадры катсцен) -> {binpath}") + + print(f"kid_data.h: {len(frames)} кадров, seqtbl {len(seqtbl)} Б, " + f"{len(offs)} seq-оффсетов -> {OUT}") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_pack_arc.py b/applications/SprPoP/tools/pop_pack_arc.py new file mode 100644 index 0000000..fd12db3 --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_arc.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""pop_pack_arc.py — сложить несколько ресурсов в один архив PBA1. + +ЗАЧЕМ. На диске игры лежит больше трёхсот отдельных файлов, и дело не в +беспорядке: замер в MAME (2026-08-25) показал, что ОДИН `open` стоит +51,4 мс, а чтение 16 КБ из уже открытого файла — 32,6 мс. То есть 61 % +времени загрузки уходит на открытие, а не на данные. Одна только стартовая +пачка (kid + shadow + bg + sound + guard, ~142 файла) — это 7,3 секунды +чистого `open`. Сложив группу в архив и открыв его ОДИН раз, мы эти +секунды возвращаем. + +ФОРМАТ (512-байтовый заголовок, дальше данные): + + 0..3 'PBA1' магия и версия — как SPA1 у атласов, PCFG, POPQ + 4 count число записей, 1..126 + 5..7 резерв (нули) + 8.. записи по 4 байта: + +0..1 offset в СЕКТОРАХ по 512 от начала файла (uint16 LE) + +2..3 size в байтах (uint16 LE) + +Оффсет именно в секторах: архив `pv` — мегабайт, в байтах uint16 не хватило +бы. В секторах uint16 покрывает 32 МБ, то есть весь наш образ. Размер при +этом спокойно живёт в байтах — ни один ресурс не крупнее 16 КБ, потому что +каждый обязан помещаться в EMM-страницу. + +Данные каждого элемента выровнены на 512, поэтому его смещение всегда +целое число секторов, а чтение попадает в границу сектора DSS. +""" +import re +import sys +from pathlib import Path + +MAGIC = b"PBA1" +HEADER = 512 +SECTOR = 512 +MAX_ENTRIES = (HEADER - 8) // 4 # 126 + + +def build(out_path: Path, files: list[Path]) -> None: + if len(files) > MAX_ENTRIES: + raise SystemExit(f"{out_path.name}: {len(files)} файлов, " + f"в заголовок влезает {MAX_ENTRIES}") + header = bytearray(HEADER) + header[0:4] = MAGIC + header[4] = len(files) + + blob = bytearray() + for i, f in enumerate(files): + data = f.read_bytes() + if len(data) > 0xFFFF: + raise SystemExit(f"{f}: {len(data)} Б — размер не влезает в uint16") + offset = HEADER + len(blob) + assert offset % SECTOR == 0 + rec = 8 + i * 4 + header[rec:rec + 2] = (offset // SECTOR).to_bytes(2, "little") + header[rec + 2:rec + 4] = len(data).to_bytes(2, "little") + blob += data + pad = -len(blob) % SECTOR + blob += bytes(pad) + + out_path.parent.mkdir(parents=True, exist_ok=True) + out_path.write_bytes(bytes(header) + bytes(blob)) + + +def const_name(prefix: str, path: Path) -> str: + """kid12.atl -> ARC_KID_KID12; kid.pal -> ARC_KID_KID_PAL.""" + stem = path.name + if stem.lower().endswith(".atl"): + stem = stem[:-4] + return f"ARC_{prefix}_" + re.sub(r"[^A-Za-z0-9]", "_", stem).upper() + + +def write_header(path: Path, prefix: str, files: list[Path]) -> None: + """Индексы элементов — константами, чтобы код не считал их на память. + + Порядок задаёт Makefile, и он же единственный источник истины: сдвинулся + список — сдвинулись константы, а серии вроде kid0..kid27 код проверяет + статически (`#if ARC_KID_KID27 - ARC_KID_KID0 != 27`). + """ + lines = [f"/* {path.name} — индексы в архиве {prefix}. СГЕНЕРИРОВАН", + " * (toolchain/pop_pack_arc.py), руками не править. */", + f"#ifndef {path.stem.upper()}_H", + f"#define {path.stem.upper()}_H", + "", + f"#define ARC_{prefix}_COUNT {len(files)}", + ""] + width = max(len(const_name(prefix, f)) for f in files) + for i, f in enumerate(files): + lines.append(f"#define {const_name(prefix, f):<{width}} {i}") + lines += ["", "#endif", ""] + path.write_text("\n".join(lines)) + + +def main() -> None: + args = sys.argv[1:] + header = prefix = None + while args and args[0].startswith("--"): + opt = args.pop(0) + if opt == "--header": + header = Path(args.pop(0)) + elif opt == "--prefix": + prefix = args.pop(0) + else: + raise SystemExit(f"неизвестный ключ {opt}") + if len(args) < 2: + raise SystemExit("usage: pop_pack_arc.py [--header H.h --prefix NAME] " + "OUT.ARC file [file ...]") + out = Path(args[0]) + files = [Path(a) for a in args[1:]] + for f in files: + if not f.exists(): + raise SystemExit(f"нет файла: {f}") + build(out, files) + if header: + if not prefix: + raise SystemExit("--header требует --prefix") + write_header(header, prefix, files) + total = out.stat().st_size + print(f"{out.name}: {len(files)} записей, {total / 1024:.0f} КБ") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_pack_bg.py b/applications/SprPoP/tools/pop_pack_bg.py new file mode 100644 index 0000000..d20e9cc --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_bg.py @@ -0,0 +1,632 @@ +#!/usr/bin/env python3 +""" +pop_pack_bg.py — упаковщик спрайтов СТАТИЧЕСКОГО ФОНА PoP (Шаг 2 порта). + +Что делает: + 1. Определяет РЕАЛЬНО используемый набор спрайтов фона (прогоняет + render_room.py по всем комнатам всех уровней, собирает (chtab,id)). + 2. Грузит исходные PNG (VDUNGEON→VPALACE каскад, как в игре), переводит + 16-цветный индексированный пиксель в 8bpp getimage-блоб: + индекс 0 -> 0xFF (прозрачность getimage) + индекс i -> 0x50+i (env, chtab_6) / 0x60+i (wall, chtab_7) + 3. Пакует в атласы .atl с ПРЯМОЙ адресацией (idx = id), каталог любого + размера (блобы кладутся ПОСЛЕ каталога — загрузчик atlas_image читает + offset явно, лимит 19 из libbgi/mkatlas.py не действует). libbgi НЕ + трогаем. Дырки в каталоге (неиспользуемые id) = фейковые записи, + ссылающиеся на общий пустой 0×0-блоб (осознанная трата места в EMM + ради нулевых remap-таблиц в W2 — требование пользователя). + 4. Собирает палитру Sprinter (.pal, записи B,G,R,0) из res200.pal (env + -> слоты 0x50..0x5F) и res360.pal (wall -> 0x60..0x6F). VGA 6-бит + каналы масштабируются в 8-бит. + 5. Пишет C-заголовок с раскладкой (имена файлов, SHIFT, число страниц, + набор fore-id). + +Раскладка (прямая адресация, SHIFT=5): + ENV фон : 5 страниц, страница = id>>5, idx = id&31 + WALL : 1 страница, idx = id + FORE : 1 страница (спрайты «перед персонажем»: фронты столбов/ + ворот/дебриса), idx = id — отдельный атлас (жизненный цикл + другой: рисуются каждый кадр как sprite_t, не запекаются). +W2-цена: 7 хэндлов atlas_t (~21 Б), ноль таблиц id->позиция. + +Выход: в build/atl/bg/ — *.atl + pop_bg.pal + pop_bg_atlas.h +""" +import os +import sys +from PIL import Image + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +import render_room as R # noqa: E402 (экстрактор + каскад загрузки) + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +OUT_DIR = str(P.atl("bg")) + +# Спрайты FORE-слоя (add_foretable в seg008 — «перед персонажем»): фронты +# столбов, ворот, дебриса. См. tile_table.fore_id + анализ pop_background_strategy. +# 119..123 — фронт чомпера и его кровь; 77 — фронт ЗЕРКАЛА (MIRROR_ENV_IDS). +# +# id 12 ЗДЕСЬ НЕТ намеренно. В tile_table он стоит как fore_id ЗЕЛЬЯ +# (0x0A), но зелье рисуется из chtab_1 (`add_foretable(id_chtab_1_...)`, +# seg008 draw_tile_fore; у нас — pop_potion_flask), а в chtab_6 под этим +# номером лежит ПРАВАЯ ЧАСТЬ АРКИ БАЛКОНА (32x62), и она идёт в backtable: +# `BT room=18 r=1 c=7 ch=6 id=12` в трассе оригинала. Пока 12 был в этом +# списке, он уезжал в fore-атлас, а env-страница получала дырку — правая +# половина портала не рисовалась вовсе (BUG-BALCONY-RIGHT). +FORE_ENV_IDS = {9, 49, 88, 91, 95, 100} | {106, 107, 108, 109, 110} \ + | {119, 120, 121, 122, 123} | {77} + +# Пол-оверлей подъёма (floor_left_overlay, draw_floor_overlay seg008): рисуется +# рантаймом поверх нижней части Kid при подтягивании на пол у кромки — НЕ +# попадает в used render_room, добавляем явно в env-фон. (32 обычно уже есть.) +CLIMB_OVERLAY_ENV_IDS = {32, 150, 151} + +# Дрожащие/падающие кадры loose-плиты (loose_fram_left/right/bottom, seg008): +# 41/42/43 = покой (уже в used из render_room), 69-74 = вибрация/раздвоение — +# используются ТОЛЬКО динамикой (shake/fall), в статический render_room НЕ +# попадают, поэтому добавляем явно. Без них дрожащая плита невидима. +LOOSE_ANIM_ENV_IDS = {69, 70, 71, 72, 73, 74} + +# Кадры ВЫДВИЖЕНИЯ пик (seg008): три части — left (128-132, своя ячейка, +# draw_tile_anim), right (134-138, правый сосед, draw_tile_anim_right), fore +# (139-143, поверх Kid, draw_tile_fore). Рисуются ТОЛЬКО динамикой +# (animate_spike); в уровнях пики статически убраны (modif=0 → кадр 0), +# поэтому в used render_room НЕ попадают. Без них выдвинутые пики невидимы. +SPIKE_ANIM_ENV_IDS = {128, 129, 130, 131, 132, # left + 134, 135, 136, 137, 138, # right + 139, 140, 141, 142, 143} # fore + +# Кадры ЧОМПЕРА (seg008:638 chomper_fram_bot/top/for + кровь). В уровнях +# чомпер стоит с modif = 0, а chomper_fram1[0] = 3 — значит render_room +# (он анимацию пропускает вовсе, см. render_room.py:611) не кладёт в атлас +# НИ ОДНОГО кадра, и чомпер не виден даже в покое. Поэтому весь набор — +# явно. Низ и верх идут в backtable (ПОД персонажем), фронт — в foretable +# (ПОВЕРХ), поэтому фронтовые id продублированы в FORE_ENV_IDS. +CHOMPER_BOT_IDS = {101, 102, 103, 104, 105} # chomper_fram_bot +CHOMPER_TOP_IDS = {111, 112, 113} # chomper_fram_top (фазы 2..4) +CHOMPER_FORE_IDS = {106, 107, 108, 109, 110} # chomper_fram_for + +# Кровь на сомкнутых челюстях (modif & 0x80): chomper_num + 114 в backtable и +# + 119 в foretable, обе — MONO-блитом цветом 12 (blitters_4Ch_mono_12), то +# есть силуэтом, как пузырёк зелья. Появляется после смерти Кида в чомпере. +CHOMPER_BLOOD_IDS = {114, 115, 116, 117, 118} +CHOMPER_BLOOD_FORE_IDS = {119, 120, 121, 122, 123} +CHOMPER_BLOOD_COLOR = None # ставится ниже, когда известен VGA16_PAL_BASE + +# Кадры ОТКРЫТИЯ ворот (portcullis, DOOR_FRAM_SLICE + gate pieces 50/51, +# seg008 draw_gate): 50-59 + 67. В уровнях ворота статически ЗАКРЫТЫ +# (низкий modif → используются только нижние срезы 50-53); срезы более +# открытых состояний (54-59, 67) нужны анимации открытия — добавляем явно. +GATE_ANIM_ENV_IDS = {50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 67} +# ВЕРХ ворот, который рисует тайл НАД ними (draw_tile_anim_topright, +# seg008:0568): маска 68 (mono, чёрным — «продавить» фон под створкой) и +# кадр door_fram_top[(modifier>>2) % 8] = 60..67 по открытости. Без них +# над воротами остаётся чёрный треугольник (видно у шва во дворце). +GATE_TOP_ENV_IDS = {60, 61, 62, 63, 64, 65, 66, 67} +GATE_TOP_MASK_ID = 68 + + +# НАЖАТАЯ кнопка-closer (get_tile_to_draw seg008:2FE: при таймере связи >1 +# tiles_6_closer рисуется как tiles_5_stuck). tile_table[5] = база 41 (уже +# есть), правая грань 35, нижняя 36 — оба id встречаются ТОЛЬКО в этом +# динамическом состоянии, статически тайла 5 в уровнях нет, значит в used +# render_room они не попадают. Без них у нажатой кнопки чёрный провал на +# месте нижней грани и потерянная правая грань. +STUCK_ENV_IDS = {35, 36} + +# Левая половина кнопки-opener, когда СЛЕВА пусто (draw_tile_base seg008:0A8E: +# curr_tile==15 && tile_left==0 && level_type==0 → id 148 вместо базы 147). +# render_room этой ветки не знает, поэтому добавляем явно. +OPENER_NOFLOOR_ENV_IDS = {148} + +# ---- chtab_1 (flame/sword/potion), база ресурса 150, папка PRINCE ---- +# Зелье (seg008 draw_tile_fore/draw_tile_anim): склянка id 12 (малая) / 13 +# (большая, типы 2..4), кадры пузырька 16..22 (potion_fram_bubb) и его маска +# 23. Пузырёк и маска рисуются MONO-блитом (одним цветом), поэтому пакуем их +# силуэтами уже в нужном цвете: маска — чёрный env[0], пузырёк — env[12] +# (красный, тип «лечение»). Цвета других типов (9 синий / 10 зелёный) — +# TODO, когда появятся такие зелья. +POT_DIR_NAME = "PRINCE" +POT_FLAME_IDS = tuple(range(1, 10)) # пламя факела: modifier+1 (seg008:560) +POT_FLASK_IDS = (12, 13) +POT_BUBBLE_IDS = (16, 17, 18, 19, 20, 21, 22) +POT_MASK_ID = 23 +POT_PAL_BASE = 0x40 # 16 цветов chtab_1 -> слоты 0x40..0x4F +# MONO-блит (method_3_blit_mono, seg009:3040) красит спрайт цветом из ОБЩЕЙ +# 16-цветной палитры экрана, а не из палитры chtab. Кладём её в 0x30..0x3F. +VGA16_PAL_BASE = 0x30 +# Два цвета игра ПРАВИТ на старте (init_game_main, seg000:164) — стандартные +# VGA-значения там не годятся: +# 6 -> #C09850 (0x30,0x26,0x14) «palace wall pattern» — швы дворцовой кладки +# 12 -> #E00030 (0x38,0x00,0x0C) «blood, hurt flash» — кровь и вспышка урона +# Проверено дампом палитры живого SDLPoP: PAL[6]=48,38,20 PAL[12]=56,0,12. +VGA16 = [(0,0,0),(0,0,42),(0,42,0),(0,42,42),(42,0,0),(42,0,42),(48,38,20), + (42,42,42),(21,21,21),(21,21,63),(21,63,21),(21,63,63),(56,0,12), + (63,21,63),(63,63,21),(63,63,63)] # 6-битные каналы, как в .pal +# ЧЁРНЫЙ ФОН АНИМАЦИЙ — ИМЕННО ЦВЕТ 0, а не «чёрный из палитры набора». +# Вспышки (молния, урон, зелья) подменяют НУЛЕВУЮ запись палитры, и всё, +# что закрашено 0x30/0x40, остаётся чёрным островом посреди вспышки — +# пламя факелов и склянки «не мигали» (поймано пользователем 2026-08-25). +POT_MASK_COLOR = 0 # маска mono-блита = фон вспышки +POT_BUBBLE_COLOR = VGA16_PAL_BASE + 12 # ярко-красный (color 12: «лечение») +# Цвет пузырька зависит от ТИПА зелья (draw_tile_anim, seg008:652): типы 3/4 +# (перо / переворот) — зелёный, 5/6 (вред / открыть) — синий, остальные — +# красный. У нас спрайт красится при УПАКОВКЕ (mono-блиттера с параметром +# цвета в libbgi нет), поэтому каждый цвет — свой набор кадров: те же семь +# силуэтов под сдвинутыми id. Синий появится вместе с зельями 5/6. +POT_BUBBLE_GREEN_COLOR = VGA16_PAL_BASE + 10 # color 10 (seg008:663) +POT_BUBBLE_GREEN_BASE = 30 # id 30..36 = кадры 16..22 +POT_BUBBLE_BLUE_COLOR = VGA16_PAL_BASE + 9 # color 9 (seg008:658) +POT_BUBBLE_BLUE_BASE = 40 # id 40..46 = кадры 16..22 +CHOMPER_BLOOD_COLOR = VGA16_PAL_BASE + 12 # тот же mono-цвет 12 (seg008:678) + +# Кладка ДВОРЦА (wall_pattern, seg008:1928, ветка palace): тело стены — +# сплошные заливки цветом из palace_wall_colors, а поверх идут ПЯТЬ +# разделителей chtab_7 id 3..17 (prandom(2) + 3/6/9/12/15), и рисуются они +# blitters_46h_mono_6 — mono-блитом ОДНИМ цветом 6 общей 16-цветной палитры +# экрана. В подземелье те же id — обычные спрайты кирпичей (wall_fram_main/ +# bottom), поэтому силуэтом пакуем ТОЛЬКО дворцовый набор. +PALACE_WALL_MONO_IDS = set(range(3, 18)) +PALACE_WALL_MONO_COLOR = VGA16_PAL_BASE + 6 + + +# Основание факела (seg008:489 draw_tile_right: при левом соседе-факеле +# рисуется chtab_6 id 146). В статический render_room не попадает. +TORCH_ENV_IDS = {146} + +# ---- Два тайлсета: подземелье и дворец -------------------------------- +# Оригинал грузит РАЗНЫЙ .DAT под один и тот же набор id: +# tbl_envir_ki[tbl_level_type[level]] -> VDUNGEON / VPALACE (seg000:1108), +# то есть id спрайта от набора НЕ зависит — меняется только картинка и +# палитра (env 0x50..0x5F, wall 0x60..0x6F). Поэтому оба набора пакуются +# по ОДНОМУ И ТОМУ ЖЕ объединению id: раскладка id -> (страница, idx) +# остаётся одна, и коду достаточно подменить ИМЕНА файлов. +# +# Порядок каталогов — каскад: сначала «свой» набор, потом чужой как +# фолбэк (в распакованном data/ несколько id есть только в одном из них: +# res230/231/348 только в VDUNGEON, два десятка — только в VPALACE). +SETS = { + "dungeon": ("pop_", lambda: (R.VDUNGEON_DIR, R.VPALACE_DIR)), + "palace": ("pal_", lambda: (R.VPALACE_DIR, R.VDUNGEON_DIR)), +} +SRC_DIRS = None # ставит main() по выбранному набору + +# Спрайты, которые рисуются ТОЛЬКО на паласных уровнях (seg008): +# 78, 80, 82 doortop_fram_bot (низ верха двери, draw_tile_right) +# 81, 83 doortop_fram_top (draw_tile_topright) +# 84 полоска на стене (draw_tile_right, tiles_20_wall) +# 145 stripe_id соседнего тайла (draw_tile_right) +# render_room про них не знает (он рисует по подземельной ветке), поэтому +# добавляем явно — иначе при переключении набора в этих местах будет дыра. +PALACE_ENV_IDS = {78, 80, 81, 82, 83, 84, 145} + +# ЗЕРКАЛО (tile_table[0x0D] = база 75, фронт 77; 42/43 — общие грани пола). +# Тайла 13 НЕТ НИ В ОДНОМ уровне статически (проверено перебором всех 15 +# res200N.bin): оригинал СТАВИТ его в рантайме, когда дверь выхода уровня 4 +# дорисовала открытие — animate_leveldoor, seg007:0457: +# curr_room_tiles[curr_tilepos] = tiles_13_mirror; +# Значит в used от render_room эти id не попадают и без явного добавления на +# месте зеркала был бы чёрный провал (те же грабли, что у нажатой кнопки и +# дрожащей плиты — memory pop_atlas_dynamic_ids). +MIRROR_ENV_IDS = {75, 77} + +ENV_SHIFT = 4 # env фон: страница = id>>SHIFT, idx = id&MASK. + # Было 5: паласный набор добавляет куски верха + # двери (78..84), и страница 2 переваливала за + # 16 КБ. При 4 обе раскладки укладываются, цена + # — 10 страниц EMM на набор вместо 5. +ENV_MASK = (1 << ENV_SHIFT) - 1 + +# ---- .atl формат (== libbgi, но каталог не ограничен 19) -------------- +ATL_MAGIC = b"SPA1" +ATL_DIR_OFF = 0x68 # _ATL_DIR_OFF +ATL_MIN_SIZE = 0x100 # atlas_load требует n >= _ATL_DATA_OFF +PAGE_SIZE = 16384 + + +def die(msg): + sys.exit(f"pop_pack_bg: {msg}") + + +# ---- 1. Набор используемых спрайтов ---------------------------------- +def collect_used(): + """Прогон render_room по всем комнатам -> множество (chtab, id).""" + used = set() + orig = R.load_sprite + + def spy(id, chtab=R.CHTAB_ENV, reason=""): + img = orig(id, chtab, reason) + if img is not None and id > 0: + used.add((chtab, id)) + return img + + R.load_sprite = spy + for lvl in range(1, 15): + fn = os.path.join(R.LEVEL_DIR, f"res{2000 + lvl}.bin") + if not os.path.exists(fn): + continue + lv = R.Level(open(fn, "rb").read()) + for room in range(1, 25): + rm = R.Room(lv, room) + L = lv.links[room][0] + if L: + rm.left_room = R.Room(lv, L) + canvas = Image.new("RGBA", (320, 192), (0, 0, 0, 0)) + for dr in (2, 1, 0): + for c in range(10): + R.draw_tile(canvas, rm, dr, c) + R.load_sprite = orig + return used + + +# ---- 2. Загрузка спрайта в 8bpp-блоб --------------------------------- +# Спрайты chtab_6, которые оригинал рисует БЕЗ прозрачности +# (blitters_0_no_transp): нижние грани тайлов (draw_tile_bottom, seg008:570) и +# нижние кадры loose (draw_loose, seg008:600). У 96/149 (грани кнопок) и 73/74 +# (кадры loose) ПОСЛЕДНЯЯ строка — пиксель 0; в оригинале она заливается ЧЁРНЫМ +# поверх того, что сзади, а у нас 0 -> 0xFF делал её прозрачной, и сквозь неё +# просвечивал Kid (спуск/подъём у кромки кнопки). 43/36 полностью непрозрачны — +# для них флаг ничего не меняет, оставлены для полноты списка. +# Дверь уровня (draw_leveldoor, seg008:1D29): решётка створки (33 — слайс 4px, +# 34 — верх), лестница за ней (99 — низ, 144 — марш). render_room.py +# draw_leveldoor не реализует, поэтому в автосбор эти id не попадают. +LEVELDOOR_ENV_IDS = {33, 34, 99, 144} + +# Куски двери уровня (draw_leveldoor) оригинал рисует blitters_0_no_transp: +# марш лестницы 144 обязан ЗАКРАШИВАТЬ поднявшуюся створку, иначе слева от +# лестницы сквозь него видна решётка (0 -> 0xFF делал фон спрайта прозрачным). +NO_TRANSP_ENV_IDS = {36, 43, 73, 74, 96, 149} | LEVELDOOR_ENV_IDS + + +def load_indexed(chtab, id, opaque=False, mono=None): + """PNG (mode P, 16 цветов) -> (w, h, bytes 8bpp). Каскад по SRC_DIRS + (порядок задаёт выбранный тайлсет, см. SETS). + Пиксель 0 -> 0xFF (прозрач); i -> base+i (env 0x50 / wall 0x60). + + opaque=True — пиксель 0 остаётся ЦВЕТОМ (base+0 = чёрный), а не ключом + прозрачности: порт blitters_0_no_transp (см. NO_TRANSP_ENV_IDS).""" + base_res = 360 if chtab == R.CHTAB_WALL else 200 + pal_base = 0x60 if chtab == R.CHTAB_WALL else 0x50 + fname = f"res{base_res + id}.png" + path = None + for d in SRC_DIRS: + p = os.path.join(d, fname) + if os.path.exists(p): + path = p + break + if path is None: + die(f"нет файла {fname}") + im = Image.open(path) + if im.mode != "P": + die(f"{fname}: mode {im.mode}, ожидался P (16-цветный)") + src = im.tobytes() + if mono is not None: + # MONO-блит (blitters_4Ch_mono_12): спрайт красится ОДНИМ цветом из + # 16-цветной палитры экрана — пакуем силуэтом, как пузырёк зелья. + out = bytes((0xFF if v == 0 else mono) for v in src) + elif opaque: + out = bytes((pal_base + v) for v in src) + else: + out = bytes((0xFF if v == 0 else pal_base + v) for v in src) + return im.width, im.height, out + + +def load_potion(id, mono=None, opaque=False): + """chtab_1: res(150+id).png из data/PRINCE. mono=None — обычный спрайт + (палитра chtab_1 -> слоты POT_PAL_BASE+i); mono=цвет — силуэт этим цветом + (порт blitters_40h_mono); opaque=True — пиксель 0 остаётся ЦВЕТОМ + (порт blitters_0_no_transp, см. POT_FLAME_IDS).""" + path = os.path.join(os.path.dirname(R.VDUNGEON_DIR), POT_DIR_NAME, + f"res{150 + id}.png") + if not os.path.exists(path): + die(f"нет файла {path}") + im = Image.open(path) + if im.mode != "P": + die(f"res{150+id}: mode {im.mode}, ожидался P") + src = im.tobytes() + if mono is not None: + out = bytes((0xFF if v == 0 else mono) for v in src) + elif opaque: + # Пиксель 0 — ЦВЕТ 0 (общий фон игрового поля), остальные — палитра + # chtab_1. Ноль здесь принципиален: непрозрачный бокс пламени иначе + # закрашивал бы фон «своим» чёрным (0x40) и не участвовал во вспышке. + out = bytes((0 if v == 0 else POT_PAL_BASE + v) for v in src) + else: + out = bytes((0xFF if v == 0 else POT_PAL_BASE + v) for v in src) + return im.width, im.height, out + + +def blob(w, h, pix): + """getimage-блоб: u16 w, u16 h (LE) + пиксели.""" + return w.to_bytes(2, "little") + h.to_bytes(2, "little") + pix + + +# ---- 3. Пакер .atl (каталог любого размера) -------------------------- +def pack_atlas(path, entries): + """entries: dict idx -> (w, h, pix). Пустые idx (0..maxidx) заполняются + фейковой ссылкой на общий 0×0-блоб. Каталог count записей с 0x68, + блобы — сразу после каталога.""" + if not entries: + die(f"{path}: пустой атлас") + count = max(entries) + 1 + if count > 255: + die(f"{path}: count {count} > 255") + data_off = ATL_DIR_OFF + count * 8 + + blobs = bytearray() + empty_off = data_off + len(blobs) # общий 0×0-блоб для дырок + blobs += blob(0, 0, b"") + dir_bytes = bytearray(count * 8) + for idx in range(count): + e = entries.get(idx) + if e is None: + off = empty_off + fw = fh = 0 + else: + w, h, pix = e + off = data_off + len(blobs) + blobs += blob(w, h, pix) + fw = min(w, 255) + fh = min(h, 255) + d = idx * 8 + dir_bytes[d:d + 2] = off.to_bytes(2, "little") + dir_bytes[d + 2] = fw + dir_bytes[d + 3] = fh + dir_bytes[d + 4] = 1 # nx + dir_bytes[d + 5] = 1 # ny + # d+6,d+7 резерв + + out = bytearray(data_off) + out[0:4] = ATL_MAGIC + out[4] = count + out[ATL_DIR_OFF:ATL_DIR_OFF + len(dir_bytes)] = dir_bytes + out += blobs + if len(out) < ATL_MIN_SIZE: + out += bytes(ATL_MIN_SIZE - len(out)) # atlas_load: n >= 0x100 + if len(out) > PAGE_SIZE: + die(f"{path}: {len(out)} Б > {PAGE_SIZE} (одна EMM-страница)") + with open(path, "wb") as f: + f.write(out) + return count, len(out) + + +# ---- 4. Палитра ------------------------------------------------------ +def scale6to8(v): + return (v * 255) // 63 + + +def read_pal16(res, d): + b = open(os.path.join(d, res), "rb").read() + return [(b[4 + i * 3], b[4 + i * 3 + 1], b[4 + i * 3 + 2]) for i in range(16)] + + +def build_tileset_palette(out_path): + """Палитра ТОЛЬКО тайлсета: 32 записи B,G,R,0 — env (16) затем wall (16). + Ею подменяются слоты 0x50..0x6F при смене типа уровня; полная игровая + палитра (kid.pal) при этом не трогается, потому что Кид, страж, меч и + зелья живут в других слотах (levels_plan §3). Порядок каталогов — + SRC_DIRS, то есть файл соответствует выбранному набору.""" + pal = bytearray(32 * 4) + src = read_pal16("res200.pal", SRC_DIRS[0]) + read_pal16("res360.pal", SRC_DIRS[0]) + for i, (r, g, b) in enumerate(src): + pal[i * 4:i * 4 + 4] = bytes((scale6to8(b), scale6to8(g), scale6to8(r), 0)) + with open(out_path, "wb") as f: + f.write(pal) + + +def build_palette(out_path): + """256-записная палитра B,G,R,0: env@0x50..0x5F, wall@0x60..0x6F.""" + def read_pal(res, d): + return read_pal16(res, d) + + env = read_pal("res200.pal", R.VDUNGEON_DIR) + wall = read_pal("res360.pal", R.VDUNGEON_DIR) + pot = read_pal("res150.pal", os.path.join(os.path.dirname(R.VDUNGEON_DIR), + POT_DIR_NAME)) + pal = bytearray(256 * 4) + for i, (r, g, b) in enumerate(env): + o = (0x50 + i) * 4 + pal[o:o + 4] = bytes((scale6to8(b), scale6to8(g), scale6to8(r), 0)) + for i, (r, g, b) in enumerate(wall): + o = (0x60 + i) * 4 + pal[o:o + 4] = bytes((scale6to8(b), scale6to8(g), scale6to8(r), 0)) + for i, (r, g, b) in enumerate(pot): + o = (POT_PAL_BASE + i) * 4 + pal[o:o + 4] = bytes((scale6to8(b), scale6to8(g), scale6to8(r), 0)) + for i, (r, g, b) in enumerate(VGA16): + o = (VGA16_PAL_BASE + i) * 4 + pal[o:o + 4] = bytes((scale6to8(b), scale6to8(g), scale6to8(r), 0)) + with open(out_path, "wb") as f: + f.write(pal) + + +# ---- 5. C-заголовок -------------------------------------------------- +def write_header(out_path, env_pages): + """Заголовок ОДИН на оба тайлсета: раскладка id -> (страница, idx) у них + общая (пакуются по одному объединению id), различаются только имена + файлов и палитра. Поэтому здесь два массива имён и один индекс.""" + lines = [ + "/* pop_bg_atlas.h — раскладка атласов статического фона PoP.", + " * Сгенерировано toolchain/pop_pack_bg.py — НЕ править вручную.", + " *", + " * Прямая адресация (ноль remap-таблиц в W2):", + " * ENV фон id N -> atlas env_bg[N>>%d], idx N&%d" % (ENV_SHIFT, ENV_MASK), + " * WALL id N -> atlas wall, idx N", + " * FORE id N -> atlas fore, idx N", + " *", + " * ДВА ТАЙЛСЕТА (порт tbl_envir_ki[tbl_level_type], seg000:1108):", + " * 0 = подземелье (pop_*), 1 = дворец (pal_*). Набор id и раскладка", + " * у них ОДНИ И ТЕ ЖЕ — меняются только файлы и 32 записи палитры", + " * (env 0x50..0x5F, wall 0x60..0x6F), см. *tile.pal.", + " */", + "#ifndef POP_BG_ATLAS_H", + "#define POP_BG_ATLAS_H", + "", + "#define POP_ENV_SHIFT %d" % ENV_SHIFT, + "#define POP_ENV_MASK %d" % ENV_MASK, + "#define POP_ENV_PAGES %d" % len(env_pages), + "#define POP_TILESETS 2", + "", + "/* Палитра: env-слоты, wall-слоты (спрайт-пиксель i -> база+i). */", + "#define POP_PAL_ENV 0x50", + "#define POP_PAL_WALL 0x60", + "", + "/* Имена файлов атласов по тайлсету (грузятся atlas_load). Таблицы", + " * видны только тому, кто попросил POP_BG_ATLAS_NAMES: иначе копия", + " * строк уедет в каждый включивший заголовок модуль. */", + "#ifdef POP_BG_ATLAS_NAMES", + "static const char *const pop_env_atl[POP_TILESETS][POP_ENV_PAGES] = {", + ] + for prefix, _ in SETS.values(): + row = ", ".join(('"%s%s"' % (prefix, nm.split("_", 1)[1]) if nm else "0") + for nm in env_pages) + lines.append(" { %s }," % row) + lines += [ + "};", + 'static const char *const pop_wall_atl[POP_TILESETS] = { %s };' + % ", ".join('"%swall.atl"' % p for p, _ in SETS.values()), + 'static const char *const pop_fore_atl[POP_TILESETS] = { %s };' + % ", ".join('"%sfore.atl"' % p for p, _ in SETS.values()), + 'static const char *const pop_tile_pal[POP_TILESETS] = { %s };' + % ", ".join('"%stile.pal"' % p for p, _ in SETS.values()), + "#endif /* POP_BG_ATLAS_NAMES */", + '#define POP_POT_ATL "pop_pot.atl" /* chtab_1: зелья, от набора не зависит */', + "#define POP_PAL_POT 0x%02X" % POT_PAL_BASE, + "/* Пузырёк зелья: красный набор = id 16..22 (кадры оригинала),", + " зелёный (перо/переворот) и синий (вред/открыть) = те же кадры", + " под id %d..%d и %d..%d (draw_tile_anim, seg008:652). */" + % (POT_BUBBLE_GREEN_BASE, POT_BUBBLE_GREEN_BASE + len(POT_BUBBLE_IDS) - 1, + POT_BUBBLE_BLUE_BASE, POT_BUBBLE_BLUE_BASE + len(POT_BUBBLE_IDS) - 1), + "#define POP_POT_BUBB_GREEN %d" % POT_BUBBLE_GREEN_BASE, + "#define POP_POT_BUBB_BLUE %d" % POT_BUBBLE_BLUE_BASE, + '#define POP_BG_PAL "pop_bg.pal"', + "", + "#endif", + "", + ] + with open(out_path, "w") as f: + f.write("\n".join(lines)) + + +def main(): + global SRC_DIRS + set_name = sys.argv[1] if len(sys.argv) > 1 else "dungeon" + if set_name not in SETS: + die(f"набор '{set_name}': ожидался один из {', '.join(SETS)}") + prefix, dirs = SETS[set_name] + SRC_DIRS = dirs() + print(f"набор: {set_name} (источник {os.path.basename(SRC_DIRS[0])}, " + f"фолбэк {os.path.basename(SRC_DIRS[1])}), префикс {prefix}") + os.makedirs(OUT_DIR, exist_ok=True) + used = collect_used() + env_ids = sorted(set(id for ch, id in used if ch != R.CHTAB_WALL) + | CLIMB_OVERLAY_ENV_IDS # + пол-оверлей подъёма + | LOOSE_ANIM_ENV_IDS # + дрожащие кадры loose + | SPIKE_ANIM_ENV_IDS # + кадры выдвижения пик + | GATE_ANIM_ENV_IDS # + кадры открытия ворот + | GATE_TOP_ENV_IDS # + верх ворот (тайл сверху) + | {GATE_TOP_MASK_ID} # и его маска (mono) + | TORCH_ENV_IDS # + основание факела + | STUCK_ENV_IDS # + нажатая кнопка-closer + | CHOMPER_BOT_IDS # + чомпер: низ + | CHOMPER_TOP_IDS # верх + | CHOMPER_FORE_IDS # фронт (поверх Kid) + | CHOMPER_BLOOD_IDS # кровь (mono) + | CHOMPER_BLOOD_FORE_IDS + | OPENER_NOFLOOR_ENV_IDS # + opener без пола слева + | LEVELDOOR_ENV_IDS # + дверь уровня (створка/лестница) + | PALACE_ENV_IDS # + куски, что рисует только палас + | MIRROR_ENV_IDS) # + зеркало (ставится в рантайме) + wall_ids = sorted(id for ch, id in used if ch == R.CHTAB_WALL) + + env_bg = [i for i in env_ids if i not in FORE_ENV_IDS] + env_fore = [i for i in env_ids if i in FORE_ENV_IDS] + + # ENV фон -> страницы по id>>SHIFT + pages = {} + for id in env_bg: + w, h, pix = load_indexed(R.CHTAB_ENV, id, id in NO_TRANSP_ENV_IDS, + CHOMPER_BLOOD_COLOR if id in CHOMPER_BLOOD_IDS + else (POT_MASK_COLOR if id == GATE_TOP_MASK_ID + else None)) + pages.setdefault(id >> ENV_SHIFT, {})[id & ENV_MASK] = (w, h, pix) + max_pg = max(pages) if pages else -1 + env_page_names = [] + total_bytes = 0 + for pg in range(max_pg + 1): + if pg in pages: + nm = f"{prefix}env{pg}.atl" + cnt, sz = pack_atlas(os.path.join(OUT_DIR, nm), pages[pg]) + total_bytes += sz + env_page_names.append(nm) + print(f" {nm}: {len(pages[pg])} спрайтов, каталог {cnt}, {sz} Б") + else: + env_page_names.append(None) + print(f" (env стр {pg}: пусто)") + + # WALL -> idx=id + wall_entries = {id: load_indexed( + R.CHTAB_WALL, id, False, + PALACE_WALL_MONO_COLOR + if (set_name == "palace" and id in PALACE_WALL_MONO_IDS) + else None) + for id in wall_ids} + cnt, sz = pack_atlas(os.path.join(OUT_DIR, prefix + "wall.atl"), wall_entries) + total_bytes += sz + print(f" {prefix}wall.atl: {len(wall_ids)} спрайтов, каталог {cnt}, {sz} Б") + + # FORE -> idx=id + fore_entries = {id: load_indexed(R.CHTAB_ENV, id, False, + CHOMPER_BLOOD_COLOR + if id in CHOMPER_BLOOD_FORE_IDS else None) + for id in env_fore} + cnt, sz = pack_atlas(os.path.join(OUT_DIR, prefix + "fore.atl"), fore_entries) + total_bytes += sz + print(f" {prefix}fore.atl: {len(env_fore)} спрайтов, каталог {cnt}, {sz} Б") + + # POTION (chtab_1) -> idx=id: склянки + кадры пузырька + маска + # chtab_1 целиком (res151..res173 = id 1..23): пламя факелов, меч-предмет, + # склянки зелий. Пузырьки и их маска рисуются MONO-блитом — пакуем + # силуэтами в нужном цвете (см. POT_*_COLOR). + pot_entries = {} + for id in range(1, 24): + if id in POT_BUBBLE_IDS: + pot_entries[id] = load_potion(id, POT_BUBBLE_COLOR) + elif id == POT_MASK_ID: + pot_entries[id] = load_potion(id, POT_MASK_COLOR) + elif id in POT_FLAME_IDS: + # Пламя оригинал рисует blitters_0_no_transp (seg008 + # draw_tile_anim_right) — непрозрачным боксом поверх всего, что + # сзади. Все 9 кадров лежат на одном канвасе 16x18, поэтому + # непрозрачный блит сам стирает предыдущий кадр: heal не нужен. + pot_entries[id] = load_potion(id, opaque=True) + else: + pot_entries[id] = load_potion(id) + # Наборы пузырьков ДРУГИХ цветов: те же кадры другим mono-цветом. + # зелёный — типы 3/4 (перо, переворот), синий — типы 5/6 (вред, открыть). + for i, id in enumerate(POT_BUBBLE_IDS): + pot_entries[POT_BUBBLE_GREEN_BASE + i] = load_potion(id, POT_BUBBLE_GREEN_COLOR) + pot_entries[POT_BUBBLE_BLUE_BASE + i] = load_potion(id, POT_BUBBLE_BLUE_COLOR) + # chtab_1 (пламя/меч/зелья) от тайлсета НЕ зависит — пакуем один раз. + cnt, sz = pack_atlas(os.path.join(OUT_DIR, "pop_pot.atl"), pot_entries) + total_bytes += sz + print(f" pop_pot.atl (chtab_1: факелы+зелья): {len(pot_entries)} спрайтов, каталог {cnt}, {sz} Б") + + build_tileset_palette(os.path.join(OUT_DIR, prefix + "tile.pal")) + print(f" {prefix}tile.pal: 32 записи (env 0x50..0x5F + wall 0x60..0x6F)") + build_palette(os.path.join(OUT_DIR, "pop_bg.pal")) + write_header(str(P.GEN / "pop_bg_atlas.h"), env_page_names) + + n_pages = sum(1 for n in env_page_names if n) + 2 + print(f"ИТОГО: {len(env_bg)} env-фон + {len(wall_ids)} wall + " + f"{len(env_fore)} fore = {len(env_bg)+len(wall_ids)+len(env_fore)} спрайтов") + print(f" {n_pages} EMM-страниц, {total_bytes} Б на диске " + f"(+1КБ палитра), W2: {n_pages}×atlas_t = {n_pages*3} Б хэндлов") + print(f" -> {OUT_DIR}") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_pack_guard.py b/applications/SprPoP/tools/pop_pack_guard.py new file mode 100644 index 0000000..33b4d99 --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_guard.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +""" +pop_pack_guard.py — упаковщик спрайтов СТРАЖА (chtab_5_guard, база 750). + +Оригинал грузит стражей из отдельного .DAT по типу уровня +(GUARD/FAT/SKEL/VIZIER/SHADOW, seg000:1092); уровень 1 — обычный «guard», +то есть data/GUARD/res(750+id).png. Палитра — НЕ из PNG: оригинал +подменяет её из res10.bin (guard_palettes, 7 палитр по 16 цветов, 6-бит) по +level.guards_color; на уровне 1 у обоих стражей color = 2. + +Выход: build/atl/guard/g0..gN.atl (по 8 спрайтов на страницу, адресация +id>>3 / id&7 — как у Kid) + группа палитры 0x90..0x9F в общий kid.pal +(его собирает pop_pack_kid.py, см. GUARD_PAL_BASE там). +""" +import os +import sys +from PIL import Image + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +import pop_pack_bg as B +from pop_pack_kid import transpose_cols, vflip_page + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +# Набор спрайтов соперника выбирается ТИПОМ УРОВНЯ (load_lev_spr, seg000:1092): +# tbl_guard_type = {0 guard, 1 fat, 2 skeleton, 3 vizier, 4 shadow, −1 нет}. +# У обычного стража палитра подменяется из res10.bin по guards_color, у +# остальных наборов curr_guard_color = 0 (seg002:186) — значит цвет берётся +# из ФАЙЛА набора (res750.pal), и подменять его не надо. +SETS = { + "GUARD": ("GUARD", "guard", None), + "SKEL": ("SKEL", "skel", "res750.pal"), + "VIZIER": ("VIZIER", "vizier", "res750.pal"), +} +SET_NAME = "GUARD" +GUARD_DIR = str(P.SDLPOP_DATA / "GUARD") +PAL_BIN = str(P.SDLPOP_DATA / "PRINCE" / "res10.bin") +OUT_DIR = str(P.atl("guard")) +PAL_BASE = 0x90 # слоты палитры Sprinter под стража +COLOR = 2 # level.guards_color обоих стражей уровня 1 +SHIFT = 3 # g[id>>3], idx id&7 (спрайты крупные) +MASK = (1 << SHIFT) - 1 +MAX_ID = 33 # атлас-индексы 0..33 = res751..res784 + + +def select_set(name): + """Переключить упаковщик на набор (GUARD/SKEL/VIZIER): каталоги и число + кадров.""" + global SET_NAME, GUARD_DIR, OUT_DIR, MAX_ID + if name not in SETS: + B.die("неизвестный набор %s (есть: %s)" % (name, ", ".join(SETS))) + SET_NAME = name + src, out, _ = SETS[name] + GUARD_DIR = str(P.SDLPOP_DATA / src) + OUT_DIR = str(P.atl(out)) + # Сколько кадров реально есть в наборе (у скелета их меньше, чем у стража). + n = 0 + while os.path.exists(os.path.join(GUARD_DIR, f"res{751 + n}.png")): + n += 1 + MAX_ID = n - 1 + + +def set_palette(): + """16 цветов набора из его res750.pal (для наборов, где цвет НЕ + подменяется из res10.bin). None — у набора своей палитры нет.""" + fname = SETS[SET_NAME][2] + if fname is None: + return None + b = open(os.path.join(GUARD_DIR, fname), "rb").read() + return [(b[4 + i * 3], b[4 + i * 3 + 1], b[4 + i * 3 + 2]) for i in range(16)] + + +def load_guard(id): + """Атлас-индекс id (== frame.image) -> res(751+id).png -> (w, h, 8bpp + COLUMN-MAJOR); 0 -> 0xFF (прозрачность). + + Off-by-one как у Kid (см. pop_pack_kid.load_kid): load_chtab_from_file + зовётся с ресурсом 750, а images[i-1] = res(750+i), то есть images[0] = + res751. Рисование берёт get_image(chtab, obj_id+1 − 1) = images[image], + значит frame.image=N -> res(751+N). С res(750+N) страж стоял в позе + предыдущего кадра (замечено на frame_166_stand_inactive: image 17 + рисовался как res767 — выпад — вместо res768). """ + path = os.path.join(GUARD_DIR, f"res{751 + id}.png") + if not os.path.exists(path): + return None + im = Image.open(path) + if im.mode != "P": + B.die(f"res{750+id}: mode {im.mode}, ожидался P") + src = im.tobytes() + rowmajor = bytes((0xFF if v == 0 else PAL_BASE + v) for v in src) + return im.width, im.height, transpose_cols(im.width, im.height, rowmajor) + + +NPAL = 7 # guard_palettes: 7 палитр по 16 цветов (res10.bin) + + +def guard_palette(color=None): + """16 цветов (r,g,b 0..63) из res10.bin для палитры color (1..7). + Индексация как в оригинале (seg003:257): &guard_palettes[0x30*color-0x30]. + color=None — палитра COLOR (та, что запекается в kid.pal по умолчанию).""" + if color is None: + color = COLOR + b = open(PAL_BIN, "rb").read() + off = 0x30 * color - 0x30 + return [(b[off + i * 3], b[off + i * 3 + 1], b[off + i * 3 + 2]) for i in range(16)] + + +def write_pal_header(out_path): + """Таблица ВСЕХ палитр стражей -> заголовок для приложения. + + Оригинал держит спрайты стража в одной палитре, а цвет подменяет на лету: + redraw_screen (seg003:255) зовёт set_chtab_palette(chtab_5_guard, + &guard_palettes[0x30*curr_guard_color - 0x30], 16) ПЕРЕД отрисовкой + комнаты, где curr_guard_color = level.guards_color[room-1] & 0x0F + (enter_guard, seg002:184). У нас спрайты уже лежат в индексах + PAL_BASE + v, поэтому достаточно залить 16 слотов палитры. + + Формат записи — родной для gfx_pal_load: 4 байта (B, G, R, 0).""" + rows = [] + for color in range(1, NPAL + 1): + vals = [] + for (r, g, b) in guard_palette(color): + # res10.bin хранит 6-битные каналы, палитра Sprinter — 8-битные + # (так же делает pop_pack_kid при записи kid.pal). + vals += [B.scale6to8(b), B.scale6to8(g), B.scale6to8(r), 0] + rows.append(" { " + ",".join("0x%02X" % v for v in vals) + " },") + lines = [ + "/* pop_guard_pal.h — палитры стражей (guard_palettes из res10.bin).", + " * СГЕНЕРЕНО toolchain/pop_pack_guard.py — НЕ править вручную.", + " *", + " * Индексация как в оригинале: цвет из level.guards_color[room-1]", + " * (младшие 4 бита), 1..%d -> строка [color-1]; 0 = палитру НЕ менять" % NPAL, + " * (seg003:255 при curr_guard_color == 0 set_chtab_palette не зовёт).", + " * Запись — 4 байта (B, G, R, 0), формат gfx_pal_load. */", + "#ifndef POP_GUARD_PAL_H", + "#define POP_GUARD_PAL_H", + "", + "#include ", + "", + "#define POP_GUARD_PAL_BASE 0x%02X /* слоты Sprinter под chtab_5 */" % PAL_BASE, + "#define POP_GUARD_NPAL %d" % NPAL, + "", + "static const uint8_t pop_guard_pal[POP_GUARD_NPAL][64] = {", + ] + rows + [ + "};", + "", + ] + # Палитры наборов со СВОИМ цветом (скелет, визирь) — из их res750.pal, а + # не из res10.bin: на таких уровнях curr_guard_color = 0 (seg002:186), и + # оригинал палитру не подменяет вовсе. + saved = SET_NAME + for name, cname, human in (("SKEL", "pop_skel_pal", "Скелет"), + ("VIZIER", "pop_vizier_pal", "Джафар")): + select_set(name) + pal = set_palette() + select_set(saved) + if not pal: + continue + vals = [] + for (r, g, b) in pal: + vals += [B.scale6to8(b), B.scale6to8(g), B.scale6to8(r), 0] + lines += [ + "/* %s (%s/res750.pal): свой набор — свой цвет. */" % (human, name), + "static const uint8_t %s[64] = {" % cname, + " " + ",".join("0x%02X" % v for v in vals) + ",", + "};", + "", + ] + lines += [ + "#endif", + "", + ] + with open(out_path, "w") as f: + f.write("\n".join(lines)) + print(f" pop_guard_pal.h: {NPAL} палитр x 16 цветов = {NPAL*64} Б -> {out_path}") + + +def main(): + os.makedirs(OUT_DIR, exist_ok=True) + pages = {} + n = 0 + for id in range(0, MAX_ID + 1): + g = load_guard(id) + if g is None: + continue + pages.setdefault(id >> SHIFT, {})[id & MASK] = g + n += 1 + if not pages: + B.die("нет спрайтов стража") + total = 0 + for pg in range(max(pages) + 1): + nm = f"g{pg}.atl" + cnt, sz = B.pack_atlas(os.path.join(OUT_DIR, nm), pages.get(pg, {})) + total += sz + # Зеркальный по вертикали набор — пара к kid*_v.atl (см. vflip_cols). + # ТОЛЬКО для обычного стража: зелье инверсии есть на уровне 9, а там + # tbl_guard_type = 0, то есть страж; скелет (уровень 3), визирь и + # жирный с переворотом не встречаются. Если чит U всё-таки включат + # на таком уровне, соперник нарисуется неперевёрнутым кадром — + # pop_vflip_page вернёт 0 (pop_vflip.h). + if SET_NAME == "GUARD": + _, szv = B.pack_atlas(os.path.join(OUT_DIR, f"g{pg}_v.atl"), + vflip_page(pages.get(pg, {}))) + total += szv + print(f" {nm}: {len(pages.get(pg, {}))} спрайтов, каталог {cnt}, {sz} Б") + write_pal_header(str(P.GEN / "pop_guard_pal.h")) + print(f"ИТОГО: {n} спрайтов стража, {max(pages)+1} EMM-страниц, {total} Б -> {OUT_DIR}") + + +if __name__ == "__main__": + if len(sys.argv) > 1: + select_set(sys.argv[1].upper()) + main() diff --git a/applications/SprPoP/tools/pop_pack_intro.py b/applications/SprPoP/tools/pop_pack_intro.py new file mode 100644 index 0000000..7e78394 --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_intro.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python3 +"""Упаковка story/PV intro SDLPoP в полосы SPA1 для Sprinter. + +Исходники остаются единственным источником истины: TITLE/res41..45, res51 и +PV/res800/850/950/980. Runtime получает только готовые 320x200 композиции +и 256-цветные палитры. Это намеренно переносит смешивание палитров Princess, +Jaffar, спальни и кровати из ограниченного Z80 в build-time. +""" +from pathlib import Path + +from PIL import Image + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +ROOT = Path(__file__).resolve().parent.parent +TITLE = P.SDLPOP_DATA / "TITLE" +PV = P.SDLPOP_DATA / "PV" +PRINCE = P.SDLPOP_DATA / "PRINCE" +KID = P.SDLPOP_DATA / "KID" +OUT = P.atl("pv") + +ATL_MAGIC = b"SPA1" +ATL_DIR_OFF = 0x68 +ATL_MIN_SIZE = 0x100 +PAGE_SIZE = 16384 +STRIP_H = 49 + +# ФОН ТЕКСТОВОЙ РАМКИ — ОТДЕЛЬНЫЙ ИНДЕКС. У оригинала это индекс 14 в +# строке палитры спрайтов title40, и load_title_images() красит его либо в +# #100060 (интро), либо в #800000 (финал, seg001:586). Раньше мы +# ремапили 14 в 9 — «тёмно-синий» палитры res51; подменить его нельзя, +# потому что тем же индексом нарисована сама титульная картинка (8265 +# пикселей). Поэтому фон уезжает в свободный индекс, и финал красит +# только его (pop_pal_story_ending). +STORY_BG_INDEX = 16 +STORY_BG_COLOR = (0x10, 0x00, 0x60) +# Цвет текста таблицы рекордов: 0xB7 оригинала = седьмой цвет палитры +# title (224,160,0). Глифы шрифта запечены индексом POP_FONT_COLOR, так +# что цвет им даёт именно эта запись; тёмный набор (POP_FONT_DARK_COLOR) +# остаётся чёрным — старшие записи story.pal и так чёрные. +STORY_FONT_INDEX = 0x3F + + +def transpose_cols(width: int, height: int, rowmajor: bytes) -> bytes: + """Row-major -> column-major для бесплатного horizontal flip в BGI.""" + out = bytearray(width * height) + for column in range(width): + base = column * height + for row in range(height): + out[base + row] = rowmajor[row * width + column] + return bytes(out) + + +def atlas_blob(pixels: bytes, width: int, height: int) -> bytes: + """Один непрозрачный getimage в минимальном SPA1-атласе.""" + data_off = ATL_DIR_OFF + 8 + out = bytearray(data_off) + out[:4] = ATL_MAGIC + out[4] = 1 + out[ATL_DIR_OFF:ATL_DIR_OFF + 2] = data_off.to_bytes(2, "little") + out[ATL_DIR_OFF + 2] = min(width, 255) + out[ATL_DIR_OFF + 3] = height + out[ATL_DIR_OFF + 4] = out[ATL_DIR_OFF + 5] = 1 + out += width.to_bytes(2, "little") + height.to_bytes(2, "little") + out += pixels + if len(out) < ATL_MIN_SIZE: + out += bytes(ATL_MIN_SIZE - len(out)) + if len(out) > PAGE_SIZE: + raise SystemExit(f"intro atlas {len(out)} B > {PAGE_SIZE} B") + return bytes(out) + + +def sprite_atlas(path: Path, entries: dict[int, tuple[int, int, bytes]]) -> None: + """Атлас небольших прозрачных кадров, целиком живущий в одной EMM. + + В отличие от atlas_blob() для полноэкранной полосы, здесь нужен каталог + нескольких картинок: runtime держит страницу открытой всю PV-сцену и + переключает только idx. Пустые индексы не требуются — все наборы ниже + нумеруются подряд с нуля. + """ + count = len(entries) + if not count or count > 255: + raise SystemExit(f"{path}: bad sprite atlas count {count}") + data_off = ATL_DIR_OFF + count * 8 + directory = bytearray(count * 8) + blobs = bytearray() + for idx in range(count): + width, height, pixels = entries[idx] + offset = data_off + len(blobs) + blobs += width.to_bytes(2, "little") + height.to_bytes(2, "little") + blobs += pixels + d = idx * 8 + directory[d:d + 2] = offset.to_bytes(2, "little") + directory[d + 2] = width + directory[d + 3] = height + directory[d + 4] = directory[d + 5] = 1 + out = bytearray(data_off) + out[:4] = ATL_MAGIC + out[4] = count + out[ATL_DIR_OFF:ATL_DIR_OFF + len(directory)] = directory + out += blobs + if len(out) < ATL_MIN_SIZE: + out += bytes(ATL_MIN_SIZE - len(out)) + if len(out) > PAGE_SIZE: + raise SystemExit(f"{path}: {len(out)} B > one EMM page") + path.write_bytes(out) + + +def sprite_frames(directory: Path, first: int, last: int, + color_base: int, column_major: bool = False + ) -> dict[int, tuple[int, int, bytes]]: + """PNG-кадры -> getimage. Ноль PNG становится прозрачным 0xFF BGI.""" + result = {} + for index, number in enumerate(range(first, last + 1)): + image = png(directory, number) + pixels = bytes(0xFF if pixel == 0 else color_base + pixel + for pixel in image.tobytes()) + if column_major: + pixels = transpose_cols(image.width, image.height, pixels) + result[index] = image.width, image.height, pixels + return result + + +def png(directory: Path, number: int) -> Image.Image: + image = Image.open(directory / f"res{number}.png") + if image.mode != "P": + raise SystemExit(f"res{number}.png: expected indexed PNG, got {image.mode}") + return image.copy() + + +def pal_file(path: Path) -> list[tuple[int, int, int]]: + """16 цветов из .pal ресурса (4 байта заголовка, дальше RGB по 6 бит).""" + raw = path.read_bytes() + return [(raw[4 + i * 3] << 2, raw[5 + i * 3] << 2, raw[6 + i * 3] << 2) + for i in range(16)] + + +def palette(image: Image.Image) -> list[tuple[int, int, int]]: + raw = image.getpalette() + return [tuple(raw[index * 3:index * 3 + 3]) for index in range(16)] + + +def write_palette(path: Path, entries: list[tuple[int, int, int]], + extra=None) -> None: + """B,G,R,0 — ровно формат gfx_pal_fload; незадействованное чёрное.""" + out = bytearray() + entries = (entries + [(0, 0, 0)] * 256)[:256] + for index, color in (extra or {}).items(): + entries[index] = color + for red, green, blue in entries: + out += bytes((blue, green, red, 0)) + path.write_bytes(out) + + +def write_scene(prefix: str, number: int, canvas: Image.Image) -> None: + if canvas.size != (320, 200): + raise SystemExit(f"{prefix}{number}: expected 320x200, got {canvas.size}") + for part, top in enumerate(range(0, 200, STRIP_H)): + band = canvas.crop((0, top, 320, min(top + STRIP_H, 200))) + (OUT / f"{prefix}{number}_{part}.atl").write_bytes( + atlas_blob(band.tobytes(), band.width, band.height)) + + +def story_scenes() -> list[Image.Image]: + """show_title/end_sequence в единой с title палитре. + + res41 кодирует тёмный фон индексом 14, а его палитра делает этот индекс + чёрным; show_title() SDLPoP подменяет его #100060 либо #800000. Тексты + res42..45 имеют индекс 1, но этот индекс у res41 тоже тёмный; ремап на + индекс title/res51 15 (белый) позволяет проявлять историю прямо поверх + последнего title-экрана без смены палитры. Фон уезжает в + STORY_BG_INDEX, чтобы финал мог покрасить его отдельно. + + Последний экран — фон таблицы рекордов: та же рамка плюс логотип + PRINCE OF PERSIA на y=24 (HOF_POP оригинала — тот же спрайт res54, что + и в титрах, только выше). + """ + frame_raw = bytearray(png(TITLE, 41).tobytes()) + for offset, pixel in enumerate(frame_raw): + if pixel == 14: + frame_raw[offset] = STORY_BG_INDEX + frame = Image.frombytes("P", (320, 200), bytes(frame_raw)) + result = [] + for text_number, ypos in ((42, 25), (43, 25), (45, 26), (44, 25)): + canvas = frame.copy() + raw = bytearray(canvas.tobytes()) + text = png(TITLE, text_number) + for sy in range(text.height): + for sx in range(text.width): + if text.getpixel((sx, sy)): + raw[(ypos + sy) * 320 + 24 + sx] = 15 + canvas.frombytes(bytes(raw)) + result.append(canvas) + result.append(frame) + + hof = frame.copy() + raw = bytearray(hof.tobytes()) + logo = png(TITLE, 54) + for sy in range(logo.height): + for sx in range(logo.width): + pixel = logo.getpixel((sx, sy)) + if pixel: # blitters_10h_transp: 0 сквозной + raw[(24 + sy) * 320 + 24 + sx] = pixel + hof.frombytes(bytes(raw)) + result.append(hof) + return result + + +def pv_palette_canvas() -> tuple[Image.Image, list[tuple[int, int, int]]]: + """Спальня + bed. Номера 0..15=room, 16..31=Princess, + 32..47=Jaffar, 48..63=bed. Благодаря этому у смешанных PNG не теряются + исходные цвета при одном gfx_pal_fload() на весь PV.""" + room = png(PV, 951) + bed = png(PV, 981) + raw = bytearray(room.tobytes()) + bed_raw = bed.tobytes() + for sy in range(bed.height): + for sx in range(bed.width): + pixel = bed_raw[sy * bed.width + sx] + if pixel: + raw[(142 + sy) * 320 + sx] = 48 + pixel + canvas = Image.frombytes("P", room.size, bytes(raw)) + # 80..95 — пламя факелов. Это отдельный chtab PRINCE в оригинале; без + # отдельной строки цвета динамические кадры становились бы цветами стены. + # 112..127 (0x70) — палитра КИДА: на PV-экране финала и сцен 8/9 он и + # мышь рисуются СВОИМИ атласами (chtab_2), а их индексы запечены под + # игровые слоты 0x70. Без этой строки они выходили бы чёрными. + return (canvas, palette(room) + palette(png(PV, 801)) + + palette(png(PV, 851)) + palette(bed) + palette(png(PV, 901)) + + palette(png(PRINCE, 151)) + [(0, 0, 0)] * 16 + + pal_file(KID / "res400.pal")) + + +def sprite_paste(dst: Image.Image, sprite: Image.Image, x: int, floor: int, + color_base: int) -> None: + """Положить спрайт ногами на floor, remap его палитру в отдельную строку.""" + raw = bytearray(dst.tobytes()) + src = sprite.tobytes() + y = floor - sprite.height + for sy in range(sprite.height): + dy = y + sy + if dy < 0 or dy >= 200: + continue + for sx in range(sprite.width): + dx = x + sx + pixel = src[sy * sprite.width + sx] + if pixel and 0 <= dx < 320: + raw[dy * 320 + dx] = color_base + pixel + dst.frombytes(bytes(raw)) + + +def pv_scenes() -> tuple[list[Image.Image], list[tuple[int, int, int]]]: + base, colors = pv_palette_canvas() + + # Позиции Char из init_princess()/init_vizier(): x=120/198, y=166. + # В оригинале точный offset задаёт frame table; статические композиции + # выравнивают персонажей по полу y=166, не хранят таблицы PV-анимации. + waiting = base.copy() + sprite_paste(waiting, png(PV, 801), 110, 166, 16) + + jaffar = waiting.copy() + sprite_paste(jaffar, png(PV, 851), 188, 166, 32) + + magic = base.copy() + sprite_paste(magic, png(PV, 805), 110, 166, 16) + sprite_paste(magic, png(PV, 870), 188, 166, 32) + # res953 — первый полный кадр песочных часов; он использует палитру room. + sprite_paste(magic, png(PV, 953), 18, 166, 0) + + alone = base.copy() + sprite_paste(alone, png(PV, 816), 110, 166, 16) + sprite_paste(alone, png(PV, 959), 18, 166, 0) + + # FG9: статические ключевые позы тех же scene 2/4/6/8/9/12. PV2 + # использует второй набор Princess (res900), поэтому он лежит в строке + # палитры 64..79. Мышь в оригинале меняет только короткую позу рядом с + # Princess; здесь соответствующая стадия выделена отдельным кадром. + stand = base.copy() + sprite_paste(stand, png(PV, 801), 134, 166, 16) + + lying = base.copy() + sprite_paste(lying, png(PV, 901), 78, 162, 64) + # Часы для cutscene_4 выбираются runtime по оставшимся минутам и + # анимируются вместе с песком; в статической основе их быть не должно. + + mouse = base.copy() + sprite_paste(mouse, png(PV, 920), 120, 169, 64) + sprite_paste(mouse, png(PV, 959), 18, 166, 0) + + short_time = base.copy() + sprite_paste(short_time, png(PV, 816), 146, 166, 16) + sprite_paste(short_time, png(PV, 953), 18, 166, 0) + + return [waiting, jaffar, magic, alone, stand, lying, mouse, short_time], colors + + +def pv_animation_assets() -> None: + """Ресурсы покадрового intro. + + `b0_*` — единственный чистый фон. Он копируется accelerator-ом между + обеими экранными страницами; спрайты рисуются банком без shadow, так что + следующий кадр автоматически начинает с фона. Каждый actor-атлас <=16K, + поэтому нет чтения 320x200 с HDD в анимационном цикле. + """ + base, _ = pv_palette_canvas() + write_scene("b", 0, base) + # frame_tbl_cuts из SDLPoP адресует Princess как image 0..16 ресурса + # 800 (то есть PNG res801..817). Для Jaffar PV1 адресуется ресурс 850: + # image 0..37 = res851..888. Все 38 кадров не помещаются в одну EMM, + # поэтому сохраняем их тремя последовательными атласами. Runtime знает + # границы, но номер кадра остаётся исходным image-id 0..37. + sprite_atlas(OUT / "a0.atl", sprite_frames(PV, 801, 817, 16, True)) + sprite_atlas(OUT / "j0.atl", sprite_frames(PV, 851, 863, 32, True)) + sprite_atlas(OUT / "j1.atl", sprite_frames(PV, 864, 874, 32, True)) + sprite_atlas(OUT / "j2.atl", sprite_frames(PV, 875, 888, 32, True)) + # ВТОРОЙ набор Princess (PV2, res901..930 = image 0..29 ресурса 900). + # Он нужен сценам 8/9 (сидит и гладит мышь, встаёт, приседает) и финалу + # (поворачивается и обнимает Кида) — поз PV1 для этого не хватает. + # 26.7 КБ не влезают в одну EMM, поэтому два атласа; номер кадра при + # этом остаётся исходным image-id 0..29, границу знает runtime. + # Палитровая строка 64..79 — та же, что у PV2 в статических сценах. + sprite_atlas(OUT / "a1.atl", sprite_frames(PV, 901, 917, 64, True)) + sprite_atlas(OUT / "a2.atl", sprite_frames(PV, 918, 930, 64, True)) + # res953/res954 — состояния часов, res960..962 — три фазы песка. + sprite_atlas(OUT / "h0.atl", sprite_frames(PV, 952, 962, 0)) + sprite_atlas(OUT / "t0.atl", sprite_frames(PRINCE, 151, 159, 80)) + + +def main() -> None: + OUT.mkdir(parents=True, exist_ok=True) + + stories = story_scenes() + write_palette(OUT / "story.pal", palette(png(TITLE, 51)), + {STORY_BG_INDEX: STORY_BG_COLOR, + STORY_FONT_INDEX: (224, 160, 0)}) + for number, scene in enumerate(stories): + write_scene("s", number, scene) + + scenes, colors = pv_scenes() + write_palette(OUT / "pv.pal", colors) + for number, scene in enumerate(scenes): + write_scene("p", number, scene) + + pv_animation_assets() + + print(f"{OUT}: {len(stories)} story + 8 PV compositions + base x 5 strips, " + f"6 animated actor atlases, one strip/page <= {STRIP_H} rows") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_pack_kid.py b/applications/SprPoP/tools/pop_pack_kid.py new file mode 100644 index 0000000..bd0c8ce --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_kid.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +""" +pop_pack_kid.py — упаковщик спрайтов персонажа Kid (Фаза K0 порта). + +Аналог pop_pack_bg.py, но для chtab_2_kid (база ресурса 400): image id N += res(400+N).png (data/KID), палитра res400.pal (16 цветов) -> слоты +Sprinter 0x70-0x7F (env 0x50 / wall 0x60 заняты фоном). Пиксель i: +0->0xFF (прозрач), i->0x70+i. + +Прямая адресация (ноль remap в W2): kid[img>>5], idx img&31. Дырки — +фейковые записи (как в фоне). Переиспользует pack_atlas/blob/scale6to8 +из pop_pack_bg. + +Выход: build/atl/kid/kid0..N.atl + kid.pal + kid_atlas.h +""" +import os +import sys +from PIL import Image + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +import pop_pack_bg as B # pack_atlas, blob, scale6to8, ENV_SHIFT + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +KID_DIR = str(P.SDLPOP_DATA / "KID") +OUT_DIR = str(P.atl("kid")) +PAL_BASE = 0x70 # слоты палитры Sprinter под Kid +SWORD_PAL_BASE = 0x80 # chtab_0 (меч в руке) -> слоты 0x80..0x8F +GUARD_PAL_BASE = 0x90 # chtab_5 (страж) -> слоты 0x90..0x9F +# chtab_0 целиком: sword_tbl (seg006:2047) адресует id 0..33 — подъём меча, +# ножны И вся боёвка. Меч в оригинале ОДИН на всех: и Кид, и страж рисуют +# клинок из этого же chtab_0 (add_sword_to_objtable, seg006:1798). +SWORD_IDS = tuple(range(0, 34)) +SHIFT = 3 # kid[img>>3], idx img&7 (Kid-кадры крупные: 8/стр ≤16КБ) +MASK = (1 << SHIFT) - 1 + + +def transpose_cols(w, h, rowmajor): + """row-major (pix[row*w+col]) -> column-major (out[col*h+row]). + Спрайты Kid хранятся КОЛОНКАМИ: gfx_blit_cols рисует вертикальным accel- + проходом (даёт бесплатный горизонтальный флип реверсом порядка колонок; + см. memory/accel_vertical_copy). Заголовок getimage (w,h) не меняется.""" + out = bytearray(w * h) + for col in range(w): + base = col * h + for row in range(h): + out[base + row] = rowmajor[row * w + col] + return bytes(out) + + +def vflip_cols(w, h, colmajor): + """COLUMN-MAJOR (out[col*h + row]) -> тот же формат, но кадр ЗЕРКАЛЕН ПО + ВЕРТИКАЛИ: внутри каждой колонки байты идут снизу вверх. + + Зачем отдельный набор. Спрайты персонажей лежат колонками ради + БЕСПЛАТНОГО горизонтального флипа (accel копирует колонки, направление + задаётся знаком страйда). Вертикальное зеркало у такой раскладки — это + разворот байтов ВНУТРИ колонки, а accel копирует блок только вперёд: + в рантайме это стоит ~0.6 с на страницу (замер MAME 2026-08-12), тогда + как ГОТОВАЯ страница читается с диска за 0.045 с. Поэтому зелье + инверсии (уровень 9) обслуживается вторым набором файлов *_v.atl. + Раскладка каталога у него та же — движку достаточно подменить номер + EMM-страницы (см. src/pop_vflip.h).""" + out = bytearray(len(colmajor)) + for col in range(w): + b = col * h + out[b:b + h] = colmajor[b:b + h][::-1] + return bytes(out) + + +def vflip_page(page): + """{idx: (w,h,pix)} -> то же с зеркальными по вертикали пикселями.""" + return {i: (w, h, vflip_cols(w, h, pix)) for i, (w, h, pix) in page.items()} + + +def load_kid(aidx): + """Атлас-индекс aidx (== frame.image) -> res(401+aidx).png -> (w, h, 8bpp + COLUMN-MAJOR). 0->0xFF, i->0x70+i. Пиксели транспонированы в колонки. + + МАППИНГ (seg008.c:1589): персонаж рисуется add_midtable(chtab, obj_id+1), + внутри get_image(chtab, id-1)=get_image(chtab, obj_id) -> chtab-индекс = + frame.image; первая картинка chtab_2_kid = res401 (res400 = палитра). + Значит frame.image=N -> res(401+N). Раньше грузили res(400+N) — off-by-one: + стойка frame_15 (image 14) рисовалась res414 (беговой наклон) вместо res415 + (прямая стойка).""" + path = os.path.join(KID_DIR, f"res{401 + aidx}.png") + if not os.path.exists(path): + return None + im = Image.open(path) + if im.mode != "P": + B.die(f"res{401+aidx}: mode {im.mode}, ожидался P") + src = im.tobytes() + rowmajor = bytes((0xFF if v == 0 else PAL_BASE + v) for v in src) + return im.width, im.height, transpose_cols(im.width, im.height, rowmajor) + + +def load_sword(id): + """chtab_0 (меч в руке): sword_tbl[].id = N -> res(700+N+1).png в data/PRINCE + (в оригинале add_midtable(chtab, obj_id+1) -> get_image(chtab, obj_id)). + Пиксели, как у Kid, COLUMN-MAJOR (gfx_blit_cols + бесплатный флип).""" + path = os.path.join(os.path.dirname(B.R.VDUNGEON_DIR), B.POT_DIR_NAME, + f"res{700 + id + 1}.png") + if not os.path.exists(path): + B.die(f"нет файла {path}") + im = Image.open(path) + if im.mode != "P": + B.die(f"res{700+id+1}: mode {im.mode}, ожидался P") + src = im.tobytes() + rowmajor = bytes((0xFF if v == 0 else SWORD_PAL_BASE + v) for v in src) + return im.width, im.height, transpose_cols(im.width, im.height, rowmajor) + + +def _read16(path): + b = open(path, "rb").read() + return [(b[4 + i * 3], b[4 + i * 3 + 1], b[4 + i * 3 + 2]) for i in range(16)] + + +def build_palette(out_path): + """ЕДИНАЯ игровая палитра (gfx_pal_fload пишет с индекса 0, перезатирая + всё — bg и Kid обязаны жить в одном файле): chtab_1@0x40 (пламя/зелья), + env@0x50, wall@0x60, kid@0x70. + """ + pal = bytearray(256 * 4) + groups = [ + (0x50, os.path.join(B.R.VDUNGEON_DIR, "res200.pal")), # env + (0x60, os.path.join(B.R.VDUNGEON_DIR, "res360.pal")), # wall + (PAL_BASE, os.path.join(KID_DIR, "res400.pal")), # kid 0x70 + (B.POT_PAL_BASE, os.path.join(os.path.dirname(B.R.VDUNGEON_DIR), + B.POT_DIR_NAME, "res150.pal")), # chtab_1 0x40 + (SWORD_PAL_BASE, os.path.join(os.path.dirname(B.R.VDUNGEON_DIR), + B.POT_DIR_NAME, "res700.pal")), # chtab_0 0x80 + ] + # 16 базовых цветов экрана (mono-блит красит ими: пламя, пузырьки зелья) + for i, (r, g, bl) in enumerate(B.VGA16): + o = (B.VGA16_PAL_BASE + i) * 4 + pal[o:o + 4] = bytes((B.scale6to8(bl), B.scale6to8(g), B.scale6to8(r), 0)) + for base, path in groups: + for i, (r, g, bl) in enumerate(_read16(path)): + o = (base + i) * 4 + pal[o:o + 4] = bytes((B.scale6to8(bl), B.scale6to8(g), B.scale6to8(r), 0)) + # Палитра стража берётся НЕ из .pal, а из res10.bin (guard_palettes: + # 7 палитр по 16 цветов, 6-бит) по level.guards_color; на уровне 1 у + # обоих стражей color = 2. См. toolchain/pop_pack_guard.py. + import pop_pack_guard as G + for i, (r, g, bl) in enumerate(G.guard_palette()): + o = (GUARD_PAL_BASE + i) * 4 + pal[o:o + 4] = bytes((B.scale6to8(bl), B.scale6to8(g), B.scale6to8(r), 0)) + + with open(out_path, "wb") as f: + f.write(pal) + + +def main(): + os.makedirs(OUT_DIR, exist_ok=True) + # атлас-индексы aidx (== frame.image), для которых есть res(401+aidx).png + ids = [] + for aidx in range(0, 255): + if os.path.exists(os.path.join(KID_DIR, f"res{401 + aidx}.png")): + ids.append(aidx) + if not ids: + B.die("нет спрайтов KID") + + pages = {} + for id in ids: + w, h, pix = load_kid(id) + pages.setdefault(id >> SHIFT, {})[id & MASK] = (w, h, pix) + + max_pg = max(pages) + names = [] + total = 0 + for pg in range(max_pg + 1): + if pg in pages: + nm = f"kid{pg}.atl" + cnt, sz = B.pack_atlas(os.path.join(OUT_DIR, nm), pages[pg]) + total += sz + names.append(nm) + print(f" {nm}: {len(pages[pg])} спрайтов, каталог {cnt}, {sz} Б") + # Зеркальный по вертикали набор (зелье инверсии, уровень 9). + nmv = f"kid{pg}_v.atl" + _, szv = B.pack_atlas(os.path.join(OUT_DIR, nmv), vflip_page(pages[pg])) + total += szv + else: + names.append(None) + print(f" (kid стр {pg}: пусто)") + + # chtab_0: меч в руке — весь chtab (id 0..33), индекс в атласе = id. + sw = {} + for i, id in enumerate(SWORD_IDS): + w, h, pix = load_sword(id) + sw[i] = (w, h, pix) + cnt, sz = B.pack_atlas(os.path.join(OUT_DIR, "sword.atl"), sw) + total += sz + _, szv = B.pack_atlas(os.path.join(OUT_DIR, "sword_v.atl"), vflip_page(sw)) + total += szv + print(f" sword.atl (chtab_0, id {SWORD_IDS[0]}..{SWORD_IDS[-1]}): " + f"{len(sw)} спрайтов, каталог {cnt}, {sz} Б") + + build_palette(os.path.join(OUT_DIR, "kid.pal")) + + # C-заголовок + hdr = str(P.GEN / "kid_atlas.h") + with open(hdr, "w") as f: + f.write("/* kid_atlas.h — раскладка атласов Kid. Сгенерировано " + "pop_pack_kid.py. */\n#ifndef KID_ATLAS_H\n#define KID_ATLAS_H\n") + f.write(f"#define KID_SHIFT {SHIFT}\n#define KID_MASK {MASK}\n") + f.write(f"#define KID_PAGES {len(names)}\n#define KID_PAL 0x{PAL_BASE:02X}\n") + f.write("static const char *const kid_atl[KID_PAGES] = {\n") + for nm in names: + f.write(" %s,\n" % ('"%s"' % nm if nm else "0")) + f.write('};\n#define KID_PAL_FILE "kid.pal"\n') + f.write('#define KID_SWORD_ATL "sword.atl" /* chtab_0: меч в руке */\n') + f.write(f"#define KID_SWORD_ID0 {SWORD_IDS[0]} /* индекс в атласе = sword_tbl.id - ID0 */\n") + f.write("#endif\n") + + print(f"ИТОГО: {len(ids)} спрайтов Kid, {len(names)} EMM-страниц, " + f"{total} Б диск (+1КБ палитра) -> {OUT_DIR}") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_pack_music.py b/applications/SprPoP/tools/pop_pack_music.py new file mode 100644 index 0000000..1466e13 --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_music.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +"""pop_pack_music.py — музыка PoP в сырой поток для CBL. + +Источник — готовые записи DOS-версии (assets/orig/PoP1_DOS_music). +Набор выбирается ключом --format: flac (по умолчанию), mp3, ogg, mt32. Оцифровки музыки в самой игре НЕТ: в digisnd*.dat лежат +только эффекты, а музыка хранится как MIDI под Adlib. Поэтому здесь мы +не «конвертируем ресурс игры», а укладываем готовую запись в формат, +который наше железо играет без единого преобразования в рантайме. + +ФОРМАТ — тот же, что у эффектов (см. pop_pack_sound.py и sound_plan.md): +8 бит БЕЗЗНАКОВЫЙ моно на 10 937,5 Гц — ровно CBL_FREQ_10K9. Частоту +берём не 11 000, а частоту железа: тогда CBL открывается ОДИН раз за +запуск и не переключается ни между эффектами, ни при переходе на музыку. + +ОДИН ФАЙЛ НА ТРЕК. Резать его на куски не нужно: читаем порциями по +16 КБ из ОДНОГО открытого файла (`bank_read_page` + `lseek`), и «дорогой» +open платится раз на трек, а не на каждую страницу — замер 2026-08-25: +open 51,4 мс против 32,6 мс на чтение 16 КБ. Этот же путь работает для +финального `won` (1,2 МБ), который в память целиком не помещается и +обязан подгружаться по ходу. + +Хвост дополняется тишиной (0x80) до кратности 128: насос отдаёт CBL +блоками по 128 байт, и остаток «не полного блока» ему пришлось бы +досчитывать в прерывании. + +Длина трека уходит в pop_music_tbl.h блоками по 128 байт, а не байтами: +самый длинный трек (won, 115 с) — это 1,2 МБ, что не влезает в uint16, а +в блоках даёт 9855. 32-битной арифметики на Z80 избегаем (см. memory +avoid_32bit_arith_z80). +""" +import subprocess +import sys +from pathlib import Path + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +# Формат источника выбирается ключом --format (по умолчанию flac — он же +# лучший по качеству исходник). Каталог и расширение знает tools/paths.py. +FMT = "flac" +SRC_DIR, SRC_EXT = P.music_src(FMT) +OUT_DIR = P.atl("music") +OUT_H = P.GEN / "pop_music_tbl.h" +OUT_TICKS = P.GEN / "pop_music_ticks.h" + +CBL_RATE = 10937.5 # CBL_FREQ_10K9 — частота железа, не 11 000 +PAGE = 16384 # EMM-страница +BLOCK = 128 # порция насоса CBL +SILENCE = 0x80 # центр беззнакового 8-битного сэмпла + +# id — нумерация звуков ОРИГИНАЛА (SDLPoP data/music/names.txt). Держим её, +# чтобы игровой код просил трек тем же номером, каким оригинал зовёт +# play_sound, и таблицу можно было сверить глазами. +TRACKS = { + 24: "regular_death", + 25: "find_mirror_and_find_shadow", + 26: "embrace", + 27: "cutscene_before_2_4_6_12", + 28: "fight_death", + 29: "meet_Jaffar", + 30: "life_potion", + 32: "end_level_4_and_killed_by_shadow", + 33: "potion", + 35: "cutscene_before_8_9", + 36: "out_of_time", + 37: "guard_death_and_obtaining_the_sword", + 39: "floating", + 40: "cutscene_not_much_time_left", + 41: "story_5_level_end_and_merge_with_shadow", + 43: "killed_Jaffar", + 50: "story_2_princess", + 52: "story_4_Jaffar_leaves", + 53: "story_3_Jaffar_enters", + 54: "intro_theme", + 55: "story_1_absence", + 56: "won", +} + + +def decode(path: Path) -> bytes: + """запись любого формата -> сырой u8 моно на частоте CBL.""" + out = subprocess.run( + ["ffmpeg", "-v", "error", "-i", str(path), + "-ac", "1", "-ar", str(int(CBL_RATE)), "-f", "u8", "-"], + capture_output=True, check=True) + return out.stdout + + +def pack(track_id: int, name: str) -> tuple[int, int]: + src = SRC_DIR / f"{name}{SRC_EXT}" + if not src.exists(): + raise SystemExit(f"нет источника: {src}") + data = bytearray(decode(src)) + + # Добить до кратности блока насоса — хвост тишиной, а не обрезкой: + # обрезать значило бы щёлкнуть на последнем сэмпле. + tail = len(data) % BLOCK + if tail: + data += bytes([SILENCE]) * (BLOCK - tail) + + chunks = (len(data) + PAGE - 1) // PAGE + OUT_DIR.mkdir(parents=True, exist_ok=True) + (OUT_DIR / f"m{track_id:02d}.bin").write_bytes(bytes(data)) + return chunks, len(data) // BLOCK + + +def main() -> None: + global FMT, SRC_DIR, SRC_EXT + args = sys.argv[1:] + if args and args[0] == "--format": + FMT = args[1] + SRC_DIR, SRC_EXT = P.music_src(FMT) + args = args[2:] + wanted = [int(a) for a in args] or sorted(TRACKS) + print(f"музыка из набора {FMT}: {SRC_DIR}") + rows = [] + total = 0 + for tid in wanted: + if tid not in TRACKS: + raise SystemExit(f"неизвестный трек {tid}") + chunks, blocks = pack(tid, TRACKS[tid]) + rows.append((tid, TRACKS[tid], chunks, blocks)) + total += blocks * BLOCK + print(f" {tid:2d} {TRACKS[tid]:<40} {chunks:2d} стр, " + f"{blocks * BLOCK / 1024:7.1f} КБ, {blocks * BLOCK / CBL_RATE:5.1f} с") + + max_id = max(t for t, *_ in rows) + lines = [ + "/*", + " * pop_music_tbl.h — каталог музыкальных треков, СГЕНЕРИРОВАН.", + f" * Источник: assets/orig/PoP1_DOS_music/{P.MUSIC_FORMATS[FMT][0]}" + " (записи DOS-версии).", + " * Генератор: tools/pop_pack_music.py. Руками не править.", + " *", + " * Нумерация id — оригинала (SDLPoP data/music/names.txt). pages —", + " * сколько кусков m_.bin лежит на диске, blocks — длина трека", + " * в порциях насоса по 128 байт (в байтах не влезло бы в uint16).", + " *", + " * Длительности реплик в тиках вынесены в pop_music_ticks.h: таблица", + " * ниже — 171 байт ДАННЫХ, и модулю, которому нужна одна константа,", + " * платить за неё копией таблицы незачем (поймано на pop_intro.c:", + " * +172 байта в банк 11).", + " *", + " * POP_MUS_TICKS_ — та же длина в ТИКАХ СЦЕНЫ (60 Гц оригинала).", + " * Нужна там, где сценарий ждёт окончания реплики: у оригинала это", + " * `do { frame(1) } while (check_sound_playing())`, у нас — пауза", + " * длиной в нашу запись. Считается ЗДЕСЬ, а не в C, по двум", + " * причинам: blocks*40 переполняет uint16 на won (9855 порций), и", + " * число обязано соответствовать НАБОРУ, которым собрали (flac и", + " * mt32 — разные исполнения разной длины).", + " *", + " * Отношение 40/57 — обратное к тому, которым шкала сцены переводит", + " * тики в порции насоса (57/40, см. pop_intro.c). Берём именно его,", + " * а не точное 10937,5/128/60, чтобы длина реплики и ход шкалы", + " * считались одной и той же арифметикой. Округление — к ближайшему", + " * (+28 перед делением): отбрасывание вниз теряло по тику на реплику.", + " */", + "#ifndef POP_MUSIC_TBL_H", + "#define POP_MUSIC_TBL_H", + "", + "#include ", + "", + f"#define POP_MUS_COUNT {max_id + 1}", + f"#define POP_MUS_BLOCK {BLOCK}", + f"#define POP_MUS_MAX_PAGES {max(c for _, _, c, _ in rows)}", + "", + ] + lines += [ + "", + "typedef struct {", + " uint8_t pages; /* 0 — трека нет */", + " uint16_t blocks; /* длина в порциях по 128 байт */", + "} pop_mus_ent_t;", + "", + "static const pop_mus_ent_t pop_mus_tbl[POP_MUS_COUNT] = {", + ] + by_id = {t: (c, b, n) for t, n, c, b in rows} + for i in range(max_id + 1): + if i in by_id: + c, b, n = by_id[i] + lines.append(f" {{ {c:2d}, {b:5d} }}, /* {i:2d} {n} */") + else: + lines.append(f" {{ 0, 0 }}, /* {i:2d} */") + lines += ["};", "", "#endif /* POP_MUSIC_TBL_H */", ""] + OUT_H.write_text("\n".join(lines)) + + # Отдельный заголовок ТОЛЬКО с длительностями реплик. Данных в нём нет, + # поэтому его может включать кто угодно, ничего не утаскивая в свой банк. + tk = [ + "/*", + " * pop_music_ticks.h — длительности треков в ТИКАХ СЦЕНЫ, СГЕНЕРИРОВАН.", + f" * Набор: {P.MUSIC_FORMATS[FMT][0]}. Генератор: tools/pop_pack_music.py.", + " * Руками не править.", + " *", + " * Нужны там, где сценарий ждёт окончания реплики: у оригинала это", + " * `do { frame(1) } while (check_sound_playing())`, у нас — пауза длиной", + " * в НАШУ запись. Число обязано соответствовать набору, которым собрана", + " * сборка: одна и та же реплика на flac и mt32 разной длины (m53 — 985", + " * тиков против 1044), и захардкоженная константа молча ломает сцену.", + " *", + " * Считается здесь, а не в C: blocks*40 переполняет uint16 на won", + " * (9855 порций). Отношение 40/57 — обратное к тому, которым шкала", + " * сцены переводит тики в порции насоса (57/40, pop_intro.c); берём", + " * именно его, чтобы длина реплики и ход шкалы считались одной", + " * арифметикой. Округление к ближайшему: вниз теряло по тику.", + " */", + "#ifndef POP_MUSIC_TICKS_H", + "#define POP_MUSIC_TICKS_H", + "", + ] + for tid, name, _c, blocks in rows: + tk.append(f"#define POP_MUS_TICKS_{tid:<3d} {(blocks * 40 + 28) // 57:5d}" + f" /* {name} */") + tk += ["", "#endif /* POP_MUSIC_TICKS_H */", ""] + OUT_TICKS.write_text("\n".join(tk)) + print(f"итого {total / 1024:.0f} КБ в {OUT_DIR}") + print(f"каталог: {OUT_H}") + print(f"тики: {OUT_TICKS}") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_pack_shadow.py b/applications/SprPoP/tools/pop_pack_shadow.py new file mode 100644 index 0000000..6b80a3a --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_shadow.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python3 +""" +pop_pack_shadow.py — упаковщик спрайтов ТЕНИ. + +ЗАЧЕМ ОТДЕЛЬНЫЙ НАБОР. Оригинал не хранит спрайтов Тени вовсе: он кладёт +обычный спрайт ДВАЖДЫ — в x обычным прозрачным блитом, и в x+1 «блиттером +XOR» (draw_objtable_item, seg008.c:1600). Наш пакетный блит так не умеет, +поэтому результат запекается заранее, здесь. + +ЧТО ИМЕННО ДЕЛАЕТ ОРИГИНАЛ (проверено по исходнику, не по памяти): + + * `blitters_2_or` — НЕ побитовое ИЛИ. В SDLPoP это обычный блит с + colour key = индекс 0 (method_6_blit_img_to_scr, seg009.c:3306), то + есть один в один наш прозрачный блит. + * `blitters_3_xor` работает по 24-битному RGB, а не по индексам палитры + (blit_xor, seg009.c:3190: конвертация в 24 бита, затем `*p_dest ^= + *p_src` побайтно). Прозрачности у него нет — XOR'ится весь + прямоугольник, но прозрачный пиксель это чёрный 0x000000, а XOR с + нулём ничего не меняет. + +Отсюда своя палитра: XOR двух цветов игровой палитры даёт цвет, которого в +ней нет. + +ЕДИНСТВЕННОЕ РАСХОЖДЕНИЕ. XOR считается по тому, что УЖЕ на экране, то +есть зависит от фона. Разбор по пикселям: от фона зависит только кайма в +один пиксель по ЛЕВЫМ кромкам силуэта (там, где спрайт прозрачен в x, но +непрозрачен в x−1). На чёрном фоне `фон XOR цвет == цвет`, и запечка +точна; Тень почти всегда на чёрном. Мы печём именно этот случай. + +ДВА НАБОРА В ОДНОМ АТЛАСЕ. Набор спрайтов у оригинала выбирает НЕ +персонаж, а поле `sword` самого кадра: load_frame_to_obj (seg008.c:1752) +держит chtab_base жёстко равным chtab_2 и прибавляет `sword >> 6`. У всех +кадров frame_table_kid эти биты нулевые, у всех кадров frame_tbl_guard — +0xC0. Тень берёт таблицу стража для кадров 150..189 (seg006.c:533), то +есть идёт через chtab_5, а chtab_5 на 12-м уровне грузится из SHADOW.DAT +(tbl_guard_type[12] == 4, seg000.c:1117) — и это графика КИДА в боевых +позах, а не стража. Поэтому печём обе половины: + + KID — data/KID/res(401+id).png, id 0..218 -> sk0..skN.atl + SHADOW — data/SHADOW/res(751+id).png, id 0..31 -> sf0..sfM.atl + +Выход: build/atl/shadow/sk*.atl + sf*.atl + pop_shadow_atlas.h (палитра +внутри заголовка — 16 записей, её заливает приложение через gfx_pal_load). +""" +import os +import sys +from PIL import Image + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +import pop_pack_bg as B +from pop_pack_kid import transpose_cols + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +DATA = str(P.SDLPOP_DATA) +OUT_DIR = str(P.atl("shadow")) +PAL_BASE = 0xA0 # слоты Sprinter под Тень: 0xA0..0xAF +NCOL = 16 # столько цветов в палитре Тени +SHIFT = 3 # sk[id>>3], idx id&7 — как у Kid (кадры крупные) +MASK = (1 << SHIFT) - 1 + +# Половины набора: (каталог, база ресурса, префикс выходных файлов). +# Off-by-one тот же, что у Kid и стража: images[0] = res(база+1), значит +# frame.image = N -> res(база+1+N) (см. pop_pack_kid.load_kid). +HALVES = (("KID", 400, "sk"), + ("SHADOW", 750, "sf")) + + +def frames(dirname, base): + """Все кадры половины: [(id, w, h, [[rgb|None]...])] — уже после + наложения XOR, в честном RGB.""" + out = [] + id = 0 + while True: + path = os.path.join(DATA, dirname, f"res{base + 1 + id}.png") + if not os.path.exists(path): + break + im = Image.open(path) + if im.mode != "P": + B.die(f"{path}: mode {im.mode}, ожидался P") + pal = im.getpalette()[:NCOL * 3] + rgb = [tuple(pal[i * 3:i * 3 + 3]) for i in range(NCOL)] + w, h = im.size + px = im.load() + grid = [] + for y in range(h): + # Ширина на один больше: второй проход кладётся в x+1, и + # суммарный след занимает [x, x+w]. + row = [None] * (w + 1) + for x in range(w): # проход 1: обычный блит + i = px[x, y] + if i: + row[x] = rgb[i] + for x in range(w): # проход 2: XOR в x+1 + i = px[x, y] + if not i: + continue # чёрный: XOR — не операция + s = rgb[i] + t = x + 1 + row[t] = s if row[t] is None else tuple(a ^ b for a, b in zip(row[t], s)) + grid.append(row) + out.append((id, w + 1, h, grid)) + id += 1 + return out + + +def histogram(halves): + h = {} + for _, fr in halves: + for _, _, _, grid in fr: + for row in grid: + for c in row: + if c is not None: + h[c] = h.get(c, 0) + 1 + return h + + +def _d2(a, b): + return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2 + + +def _kmeans(pts, k, init): + """Взвешенный Ллойд по СПИСКУ ЦВЕТОВ с весами (их полсотни, не пиксели).""" + cent = list(init) + for _ in range(80): + groups = [[] for _ in cent] + for c, n in pts: + groups[min(range(len(cent)), key=lambda j: _d2(c, cent[j]))].append((c, n)) + new = [] + for j, g in enumerate(groups): + if not g: + new.append(cent[j]) + continue + w = sum(n for _, n in g) + new.append(tuple(int(round(sum(c[i] * n for c, n in g) / w)) for i in range(3))) + if new == cent: + break + cent = new + return cent + + +def build_palette(hist): + """16 цветов ГИБРИДОМ: часть слотов — самые частые цвета ТОЧНО, остаток + — взвешенные кластеры хвоста. + + Почему не «просто самые частые»: тогда редкие цвета уезжают в далёкий + ближайший, и грубых промахов (сдвиг >90 по евклиду в RGB при максимуме + 441) получается впятеро больше — 381 пиксель против 76 на все 251 + кадр (замер 2026-08-20). Почему не чистый k-means: он двигает вообще + все цвета, включая массовые. + + Разделение слотов подбираем перебором по критерию «сначала минимум + грубых промахов, потом заметных, потом худшая ошибка».""" + items = sorted(hist.items(), key=lambda kv: -kv[1]) + + def stats(pal): + bad = worst = mid = big = 0 + for c, n in items: + e = min(_d2(c, p) for p in pal) ** 0.5 + if e > 0: + bad += n + worst = max(worst, e) + if e >= 90: + big += n + elif e >= 40: + mid += n + return bad, mid, big, worst + + best = None + for m in range(1, NCOL): # m слотов под хвост + keep = [c for c, _ in items[:NCOL - m]] + rest = [(c, n) for c, n in items[NCOL - m:]] + if not rest: + continue + pal = keep + _kmeans(rest, m, [c for c, _ in rest[:m]]) + bad, mid, big, worst = stats(pal) + score = (big, mid, worst) + if best is None or score < best[0]: + best = (score, m, pal, (bad, mid, big, worst)) + _, m, pal, st = best + # Чёрный обязан быть ТОЧНЫМ и лежать в слоте 0: это XOR-погашенная + # середина силуэта, больше половины всех пикселей Тени. + j = min(range(len(pal)), key=lambda i: _d2((0, 0, 0), pal[i])) + pal[j] = (0, 0, 0) + pal[0], pal[j] = pal[j], pal[0] + bad, mid, big, worst = st + tot = sum(hist.values()) + print(f" палитра: {NCOL - m} частых точно + {m} кластеров хвоста; " + f"из {len(items)} цветов изменено {bad} px из {tot} " + f"({100.0 * bad / tot:.2f} %), заметно {mid}, сильно {big}, худшая {worst:.0f}") + return pal + + +def encode(grid, w, h, pal): + """RGB-сетка -> байты атласа: нет пикселя -> 0xFF, цвет -> PAL_BASE + k. + Чёрный — НАСТОЯЩИЙ цвет Тени (слот 0), а не прозрачность.""" + row = bytearray() + for y in range(h): + for x in range(w): + c = grid[y][x] + if c is None: + row.append(0xFF) + else: + row.append(PAL_BASE + min(range(len(pal)), key=lambda k: _d2(c, pal[k]))) + return transpose_cols(w, h, bytes(row)) + + +def write_header(path, counts, pal): + vals = [] + for (r, g, b) in pal: + vals += [b, g, r, 0] # формат gfx_pal_load: B, G, R, 0 + lines = [ + "/* pop_shadow_atlas.h — раскладка атласа ТЕНИ.", + " * СГЕНЕРЕНО toolchain/pop_pack_shadow.py — НЕ править вручную.", + " *", + " * Две половины, потому что и у оригинала их две (см. шапку", + " * упаковщика): кадры вне боя адресуют chtab_2 (спрайты Кида),", + " * кадры 150..189 — chtab_5, а он на 12-м уровне это SHADOW.DAT.", + " * Какая половина нужна, приложение уже знает — тем же условием,", + " * что выбирает таблицу кадров (pop_frame_tbl_is_guard).", + " *", + " * Спрайт Тени НА ОДИН ПИКСЕЛЬ ШИРЕ исходного: второй проход", + " * оригинала кладётся в x+1, и суммарный след занимает [x, x+w].", + " * Рисовать по тем же координатам, что и Кида — сдвиг уже внутри.", + " */", + "#ifndef POP_SHADOW_ATLAS_H", + "#define POP_SHADOW_ATLAS_H", + "", + "#include ", + "", + "#define POP_SHADOW_SHIFT %d" % SHIFT, + "#define POP_SHADOW_MASK %d" % MASK, + "#define POP_SHADOW_PAL_BASE 0x%02X /* слоты Sprinter под Тень */" % PAL_BASE, + "", + "/* Половина «вне боя» — кадры Кида (chtab_2). */", + "#define POP_SHADOW_KID_PAGES %d" % counts["sk"], + "/* Половина «в бою» — SHADOW.DAT (chtab_5, кадры 150..189). */", + "#define POP_SHADOW_FGT_PAGES %d" % counts["sf"], + "", + "static const char *const pop_shadow_kid_atl[POP_SHADOW_KID_PAGES] = {", + ] + lines += [' "SHADOW\\\\sk%d.atl",' % i for i in range(counts["sk"])] + lines += [ + "};", + "", + "static const char *const pop_shadow_fgt_atl[POP_SHADOW_FGT_PAGES] = {", + ] + lines += [' "SHADOW\\\\sf%d.atl",' % i for i in range(counts["sf"])] + lines += [ + "};", + "", + "/* 16 цветов, запись 4 байта (B, G, R, 0) — формат gfx_pal_load. */", + "static const uint8_t pop_shadow_pal[64] = {", + " " + ",".join("0x%02X" % v for v in vals) + ",", + "};", + "", + "#endif", + "", + ] + with open(path, "w") as f: + f.write("\n".join(lines)) + print(f" pop_shadow_atlas.h -> {path}") + + +def main(): + os.makedirs(OUT_DIR, exist_ok=True) + halves = [(prefix, frames(d, base)) for d, base, prefix in HALVES] + for (d, base, prefix), (_, fr) in zip(HALVES, halves): + if not fr: + B.die(f"нет спрайтов в data/{d} (SHADOW.DAT распакован?)") + print(f" {d}: {len(fr)} кадров") + pal = build_palette(histogram(halves)) + + counts, total, nspr = {}, 0, 0 + for prefix, fr in halves: + pages = {} + for id, w, h, grid in fr: + pages.setdefault(id >> SHIFT, {})[id & MASK] = (w, h, encode(grid, w, h, pal)) + nspr += 1 + npg = max(pages) + 1 + counts[prefix] = npg + for pg in range(npg): + nm = f"{prefix}{pg}.atl" + cnt, sz = B.pack_atlas(os.path.join(OUT_DIR, nm), pages.get(pg, {})) + total += sz + print(f" {prefix}*.atl: {len(fr)} спрайтов, {npg} EMM-страниц") + write_header(str(P.GEN / "pop_shadow_atlas.h"), counts, pal) + print(f"ИТОГО: {nspr} спрайтов Тени, " + f"{counts['sk'] + counts['sf']} EMM-страниц, {total} Б -> {OUT_DIR}") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_pack_sound.py b/applications/SprPoP/tools/pop_pack_sound.py new file mode 100644 index 0000000..303d5bf --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_sound.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +""" +pop_pack_sound.py — упаковщик ЗВУКОВЫХ ЭФФЕКТОВ (оцифровка, chtab нет). + +ИСТОЧНИК. `MSDOS/digisnd1..3.dat` — оригинальные контейнеры. Разбор: +индекс по 8 байт (id, offset, size), **первый байт ресурса — контрольная +сумма**, тело за ней (спецификация `docs/POP-DAT-FormatSpecifications`, +§3.1.2). Тело — один из двух вариантов заголовка: + + digi_type (1.0/1.1): word rate, word count, word unk, byte size=8 + digi_new_type (1.3/1.4): word rate, byte size=8, word count, word, word + +Различаем по позиции байта размера: у нового он на смещении 3, у старого +на 7. Сэмплы — 8 бит БЕЗЗНАКОВЫЕ, МОНО (проверено по `convert_digi_sound`, +seg009.c:2358: один байт на кадр, `(b | b<<8) - 32768`; стерео в данных +нет вовсе, каналы размножаются уже на выходе). Это ровно формат нашего +CBL — конвертировать нечего. + +ЧАСТОТА. Приводим ВСЁ к 10 937,5 Гц — это `CBL_FREQ_10K9`, то есть +частота железа. Не к 11 000: проигрывание 11 000-данных на 10 937,5 дало +бы сдвиг тона −9,9 цента, а пересчитывать три файла (2 750, 8 200, +14 000 Гц) всё равно надо. Взамен CBL открывается ОДИН раз и частота не +меняется никогда — ни между эффектами, ни при переходе на музыку. + +РАСКЛАДКА. Всё склеивается в EMM-страницы по 16 384 Б, и начало каждого +звука ВЫРАВНИВАЕТСЯ НА 128. Это не косметика: блок запроса CBL — ровно +128 байт, а 16 384 кратно 128, поэтому при таком выравнивании ни один +блок не пересекает границу страницы. Проигрыватель за счёт этого не +знает слова «стык»: замапил страницу, отдал 128 байт, сдвинул указатель. +Плата — до 127 байт на звук, всего ~2 КБ. + +Выход: build/atl/sound/s0..sN.bin (по 16 384 Б) + gen/pop_sound_tbl.h +""" +import os +import struct +import sys + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +HERE = os.path.dirname(os.path.abspath(__file__)) +DAT_DIR = str(P.MSDOS) +OUT_DIR = str(P.atl("sound")) +HDR = str(P.GEN / "pop_sound_tbl.h") + +PAGE = 16384 +BLOCK = 128 # блок запроса CBL при 8 бит (см. cbl.h) +RATE = 10937.5 # CBL_FREQ_10K9 +NSOUND = 57 # id 0..56; у музыки оцифровки нет — длина 0 + + +def dat_resources(path): + """id -> тело ресурса (без байта контрольной суммы).""" + d = open(path, "rb").read() + off, _ = struct.unpack(" 4 and body[3] == 8: # digi_new_type + rate = struct.unpack(" 7 and body[7] == 8: # digi_type + rate, cnt = struct.unpack("= len(src) - 1: + out[i] = src[-1] + else: + a = x - i0 + out[i] = int(round(src[i0] * (1.0 - a) + src[i0 + 1] * a)) + return bytes(out) + + +def main(): + res = {} + for i in (1, 2, 3): + p = os.path.join(DAT_DIR, f"digisnd{i}.dat") + if not os.path.exists(p): + sys.exit(f"нет файла {p}") + res.update(dat_resources(p)) + + sounds = {} + for sid, body in sorted(res.items()): + g = digi(body) + if g is None: + print(f" звук {sid}: не оцифровка, пропуск") + continue + rate, samples = g + pcm = resample(samples, rate, RATE) + sounds[sid] = pcm + if rate != 11000: + print(f" звук {sid}: {rate} Гц -> {RATE:g} Гц, {len(samples)} -> {len(pcm)} сэмплов") + + # Раскладка: начало каждого звука кратно BLOCK, страницы по PAGE. + # ПЕРВЫЙ БЛОК — ТИШИНА. Он нужен насосу: когда эффект кончился, в CBL + # всё равно надо что-то отдавать, иначе железо доигрывает по кругу хвост + # своего буфера (жужжание). Держим тишину СВОЮ, а не просим у libc + # (CBL_UNDERRUN_SILENCE делает malloc), потому что куча в резиденте W2 + # тесная и её отказ ронял бы весь звук. + blob = bytearray([0x80] * BLOCK) + table = {} + for sid in sorted(sounds): + pad = (-len(blob)) % BLOCK + blob += bytes([0x80]) * pad # 0x80 — тишина в беззнаковом 8-бит + # Длину округляем ВВЕРХ до блока: хвост последнего блока — это + # выравнивающая тишина следующего звука, и проигрыватель за счёт + # этого не знает слова «неполный блок». + need = (len(sounds[sid]) + BLOCK - 1) // BLOCK * BLOCK + # ЗВУК ЦЕЛИКОМ В ОДНОЙ СТРАНИЦЕ (решение пользователя 2026-08-25). + # Раньше длинные эффекты перетекали в следующую страницу, и насос + # переключал её прямо посреди воспроизведения — лишняя логика в ISR + # и целый класс отказов «играет не тот звук». Теперь страница — + # как атлас: не влезает в остаток — начинаем новую, хвост добиваем + # тишиной. Платим страницами EMM, их у нас с запасом. + if len(blob) % PAGE + need > PAGE: + blob += bytes([0x80]) * ((-len(blob)) % PAGE) + table[sid] = (len(blob), need) + blob += sounds[sid] + blob += bytes([0x80]) * ((-len(blob)) % PAGE) + + os.makedirs(OUT_DIR, exist_ok=True) + npages = len(blob) // PAGE + for i in range(npages): + with open(os.path.join(OUT_DIR, f"s{i}.bin"), "wb") as f: + f.write(blob[i * PAGE:(i + 1) * PAGE]) + + lines = [ + "/* pop_sound_tbl.h — раскладка оцифрованных эффектов PoP.", + " * СГЕНЕРЕНО toolchain/pop_pack_sound.py — НЕ править вручную.", + " *", + " * Данные: 8 бит беззнаковые, МОНО, %g Гц (CBL_FREQ_10K9) — формат" % RATE, + " * CBL один в один, конвертировать в рантайме нечего.", + " *", + " * Начало каждого звука кратно %d — размеру блока запроса CBL, а" % BLOCK, + " * страница (%d) кратна ему же. Поэтому НИ ОДИН блок не пересекает" % PAGE, + " * границу страницы. Больше того: с 2026-08-25 каждый звук лежит\n * ЦЕЛИКОМ в одной странице — не влезает в остаток, значит начинаем\n * новую (страницы EMM у нас с запасом). Единственное исключение —\n * эффект, который сам ДЛИННЕЕ страницы: для него насос переключает\n * страницу на ходу, и эта ветка в pop_sfx_fill остаётся ради него.", + " *", + " * Смещение хранится РАЗОБРАННЫМ на страницу и адрес в окне: на Z80", + " * 32-битная арифметика дорога, а так всё влезает в 8/16 бит.", + " * Длина округлена вверх до блока — неполных блоков не бывает.", + " */", + "#ifndef POP_SOUND_TBL_H", + "#define POP_SOUND_TBL_H", + "", + "#include ", + "", + "#define POP_SND_PAGES %d" % npages, + "#define POP_SND_COUNT %d" % NSOUND, + "#define POP_SND_BLOCK %d" % BLOCK, + "/* Первый блок страницы 0 — тишина для насоса (см. упаковщик). */", + "#define POP_SND_SILENCE_PAGE 0", + "#define POP_SND_SILENCE_OFF 0x0000", + "", + "typedef struct { uint8_t page; uint16_t off; uint16_t len; } pop_snd_ent_t;", + "", + "/* len == 0 — у звука нет оцифровки (это музыка, см. sound_plan.md). */", + "static const pop_snd_ent_t pop_snd_tbl[POP_SND_COUNT] = {", + ] + for sid in range(NSOUND): + off, ln = table.get(sid, (0, 0)) + lines.append(" { %d, 0x%04X, %5d }, /* %2d */" % (off >> 14, off & 0x3FFF, ln, sid)) + lines += [ + "};", + "", + "/* Имена файлов видит ТОЛЬКО тот, кто попросил POP_SND_FILES (холодная", + " * половина): иначе копия строк уезжает в каждый включивший заголовок", + " * модуль, а в резиденте W1/W2 каждая сотня байт на счету. Тот же", + " * приём, что у POP_BG_ATLAS_NAMES в pop_bg_atlas.h. */", + "#ifdef POP_SND_FILES", + "static const char *const pop_snd_files[POP_SND_PAGES] = {", + ] + lines += [' "SND\\\\s%d.bin",' % i for i in range(npages)] + lines += ["};", "#endif", "", "#endif", ""] + with open(HDR, "w") as f: + f.write("\n".join(lines)) + + have = sum(1 for s in sounds.values() if s) + print(f"ИТОГО: {have} эффектов, {len(blob)} Б = {npages} EMM-страниц -> {OUT_DIR}") + print(f" заголовок -> {HDR}") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/pop_pack_title.py b/applications/SprPoP/tools/pop_pack_title.py new file mode 100644 index 0000000..5c1852b --- /dev/null +++ b/applications/SprPoP/tools/pop_pack_title.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +"""pop_pack_title.py — титульные композиции SDLPoP для Sprinter. + +Исходник — PNG-ресурсы ``data/TITLE/res51..55`` из локального SDLPoP. +Runtime не должен держать 320x200 картинку в W2 и не разбирает PNG: каждая +готовая композиция разрезана на пять getimage-лент высотой не более 49 +строк. Лента вместе с заголовком атласа занимает одну EMM-страницу; экран +рисуется лента за лентой, и страница сразу освобождается. + +Композиции в точности повторяют ``show_title()`` до story/intro: + t0 — основной рисунок; + t1 — рисунок + PRESENTS; + t2 — рисунок + название игры; + t3 — рисунок + логотип PRINCE OF PERSIA и Jordan Mechner. +""" +from pathlib import Path + +from PIL import Image + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +ROOT = Path(__file__).resolve().parent.parent +SRC = P.SDLPOP_DATA / "TITLE" +OUT = P.atl("title") + +ATL_MAGIC = b"SPA1" +ATL_DIR_OFF = 0x68 +ATL_MIN_SIZE = 0x100 +PAGE_SIZE = 16384 +STRIP_H = 49 + + +def atlas_blob(pixels: bytes, width: int, height: int) -> bytes: + """Один непрозрачный getimage в минимальном SPA1-атласе.""" + data_off = ATL_DIR_OFF + 8 + out = bytearray(data_off) + out[:4] = ATL_MAGIC + out[4] = 1 + out[ATL_DIR_OFF:ATL_DIR_OFF + 2] = data_off.to_bytes(2, "little") + # Каталог хранит preview-габарит в uint8, но atlas_image читает только + # offset; реальный getimage-width — u16 в блобе. Так уже пакуются + # широкие 320-пиксельные полосы фона. + out[ATL_DIR_OFF + 2] = min(width, 255) + out[ATL_DIR_OFF + 3] = height + out[ATL_DIR_OFF + 4] = out[ATL_DIR_OFF + 5] = 1 + out += width.to_bytes(2, "little") + height.to_bytes(2, "little") + out += pixels + if len(out) < ATL_MIN_SIZE: + out += bytes(ATL_MIN_SIZE - len(out)) + if len(out) > PAGE_SIZE: + raise SystemExit(f"title atlas {len(out)} B > {PAGE_SIZE} B") + return bytes(out) + + +def image(n: int) -> Image.Image: + im = Image.open(SRC / f"res{n}.png") + if im.mode != "P": + raise SystemExit(f"res{n}.png: expected indexed PNG, got {im.mode}") + return im.copy() + + +def assert_palette(base: Image.Image, overlay: Image.Image, n: int) -> None: + if base.getpalette()[:48] != overlay.getpalette()[:48]: + raise SystemExit(f"res{n}.png uses a palette different from res51.png") + + +def paste_opaque(dst: Image.Image, src: Image.Image, x: int, y: int, n: int) -> None: + assert_palette(dst, src, n) + dst.paste(src, (x, y)) + + +def paste_transparent(dst: Image.Image, src: Image.Image, x: int, y: int, n: int) -> None: + assert_palette(dst, src, n) + # TITLE_POP — единственный из пяти элементов, который SDLPoP рисует + # blitters_10h_transp. В PNG тот же факт записан colour-key index 0. + # Image.point() у P-изображения возвращает снова P, а paste() принимает + # маску только L/1/RGBA. Строим L явно, не меняя индексы самой картинки. + mask = Image.frombytes("L", src.size, + bytes(0 if p == 0 else 255 for p in src.tobytes())) + dst.paste(src, (x, y), mask) + + +# Семнадцатая запись — фон текстовой рамки story (STORY_BG_INDEX в +# pop_pack_intro.py). Она нужна здесь потому, что «In the absence…» +# проявляется полосами ПОВЕРХ последнего кадра титров, то есть при ещё +# загруженной title.pal: без этой записи фон рамки был бы чёрным. +STORY_BG_INDEX = 16 +STORY_BG_COLOR = (0x10, 0x00, 0x60) + + +def write_palette(base: Image.Image) -> None: + rgb = base.getpalette()[:48] + pal = bytearray() + for i in range(16): + r, g, b = rgb[i * 3:i * 3 + 3] + pal += bytes((b, g, r, 0)) # родной формат gfx_pal_fload + assert len(pal) == STORY_BG_INDEX * 4 + r, g, b = STORY_BG_COLOR + pal += bytes((b, g, r, 0)) + (OUT / "title.pal").write_bytes(pal) + + +def write_scene(scene: int, canvas: Image.Image) -> int: + count = 0 + for part, top in enumerate(range(0, canvas.height, STRIP_H)): + band = canvas.crop((0, top, canvas.width, + min(top + STRIP_H, canvas.height))) + path = OUT / f"t{scene}_{part}.atl" + path.write_bytes(atlas_blob(band.tobytes(), band.width, band.height)) + count += 1 + return count + + +def main() -> None: + OUT.mkdir(parents=True, exist_ok=True) + main_image = image(51) + if main_image.size != (320, 200): + raise SystemExit(f"res51.png: expected 320x200, got {main_image.size}") + + presents = main_image.copy() + paste_opaque(presents, image(52), 96, 106, 52) + + game = main_image.copy() + paste_opaque(game, image(53), 96, 122, 53) + + logo = main_image.copy() + paste_transparent(logo, image(54), 24, 107, 54) + paste_opaque(logo, image(55), 48, 184, 55) + + write_palette(main_image) + scenes = (main_image, presents, game, logo) + strips = [write_scene(i, scene) for i, scene in enumerate(scenes)] + assert strips == [5, 5, 5, 5] + print(f"{OUT}: {len(scenes)} title compositions x {strips[0]} strips, " + f"one strip <= {STRIP_H} rows / one EMM page") + + +if __name__ == "__main__": + main() diff --git a/applications/SprPoP/tools/render_room.py b/applications/SprPoP/tools/render_room.py new file mode 100755 index 0000000..6d4ca85 --- /dev/null +++ b/applications/SprPoP/tools/render_room.py @@ -0,0 +1,853 @@ +#!/usr/bin/env python3 +""" +Parse SDLPoP level file (res2001.bin) and render the first room by +faithfully replaying the tile-drawing logic of seg008.c (draw_room/draw_tile). + +Pixel model (from seg008.c): + - screen is 320px wide, 10 columns. + - col_xh[col] = {0,4,8,12,16,20,24,28,32,36}; x_px = xh*8 -> 0,32,64,...,288 + (tile column is 32px; the 4px offset handles overlap of floor/wall edges) + - rows drawn 2,1,0 (bottom..top). For each row: + draw_bottom_y = 63*drawn_row + 65 + draw_main_y = draw_bottom_y - 3 + row2 -> bottom_y=191, row1 -> 128, row0 -> 65 + - a tile sprite of height h drawn at ybottom is placed with its BOTTOM at ybottom: + y = ybottom - h + 1 + +We replay draw_tile() which composites (in order): + floorright, anim_topright, right, anim_right, bottom, loose, base, anim, fore +Dependencies on NEIGHBOURS: + - tile_left = tile in the column to the left (same room, same row) + - row_below_left_[col] = tile in (room_below, col-1, row_below) -> for topright/floorright + - For a single room with no links we treat out-of-range neighbours as walls + (level edges), matching SDLPoP's custom->drawn_tile_*_level_edge defaults + (left = wall, top = floor). + +We ignore animated variants (spike frames, chomper, torch flames, gate +animation) for this first static render; they use curr_modifier/backtable. +""" + +import os +from PIL import Image + +import sys as _sys, os as _os +_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__))) +import paths as P # раскладка каталогов SprPoP (tools/paths.py) + +LEVEL_DIR = str(P.SDLPOP_DATA / "LEVELS") +VPALACE_DIR = str(P.SDLPOP_DATA / "VPALACE") +VDUNGEON_DIR = str(P.SDLPOP_DATA / "VDUNGEON") + +# For dungeon levels (tbl_level_type == 0) the environment sprites come from +# VDUNGEON where present, otherwise fall back to the shared VPALACE set +# (PRINCE.DAT common images). For palace levels use VPALACE only. +IS_DUNGEON = True # room 1 (res2001) is the dungeon + +# tile_table[code] = (base_id, floor_left, base_y, right_id, floor_right, +# right_y, stripe_id, topright_id, bottom_id, fore_id, +# fore_x, fore_y) +TILE_TABLE = [ + ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), # 0x00 empty + ( 41, 1, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0), # 0x01 floor + (127, 1, 0, 133, 1, 2, 145, 0, 43, 0, 0, 0), # 0x02 spike + ( 92, 1, 0, 93, 1, 2, 0, 94, 43, 95, 1, 0), # 0x03 pillar + ( 46, 1, 0, 47, 1, 2, 0, 48, 43, 49, 3, 0), # 0x04 gate + ( 41, 1, 1, 35, 1, 3, 145, 0, 36, 0, 0, 0), # 0x05 stuck floor + ( 41, 1, 0, 42, 1, 2, 145, 0, 96, 0, 0, 0), # 0x06 close button + ( 46, 1, 0, 0, 0, 2, 0, 0, 43, 49, 3, 0), # 0x07 door top with floor + ( 86, 1, 0, 87, 1, 2, 0, 0, 43, 88, 1, 0), # 0x08 big pillar bottom + ( 0, 0, 0, 89, 0, 3, 0, 90, 0, 91, 1, 3), # 0x09 big pillar top + ( 41, 1, 0, 42, 1, 2, 145, 0, 43, 12, 2, -3), # 0x0A potion + ( 0, 1, 0, 0, 0, 0, 145, 0, 0, 0, 0, 0), # 0x0B loose floor + ( 0, 0, 0, 0, 0, 2, 0, 0, 85, 49, 3, 0), # 0x0C door top + ( 75, 1, 0, 42, 1, 2, 0, 0, 43, 77, 0, 0), # 0x0D mirror + ( 97, 1, 0, 98, 1, 2, 145, 0, 43, 100, 0, 0), # 0x0E debris + (147, 1, 0, 42, 1, 1, 145, 0, 149, 0, 0, 0), # 0x0F open button + ( 41, 1, 0, 37, 0, 0, 0, 38, 43, 0, 0, 0), # 0x10 leveldoor left + ( 0, 0, 0, 39, 1, 2, 0, 40, 43, 0, 0, 0), # 0x11 leveldoor right + ( 0, 0, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0), # 0x12 chomper + ( 41, 1, 0, 42, 1, 2, 0, 0, 43, 0, 0, 0), # 0x13 torch + ( 0, 0, 0, 1, 1, 2, 0, 2, 0, 0, 0, 0), # 0x14 wall + ( 30, 1, 0, 31, 1, 2, 0, 0, 43, 0, 0, 0), # 0x15 skeleton + ( 41, 1, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0), # 0x16 sword + ( 41, 1, 0, 10, 0, 0, 0, 11, 43, 0, 0, 0), # 0x17 balcony left + ( 0, 0, 0, 12, 1, 2, 0, 13, 43, 0, 0, 0), # 0x18 balcony right + ( 92, 1, 0, 42, 1, 2, 145, 0, 43, 95, 1, 0), # 0x19 lattice pillar + ( 1, 0, 0, 0, 0, 0, 0, 0, 2, 9, 0, -53), # 0x1A lattice down + ( 3, 0, -10, 0, 0, 0, 0, 0, 0, 9, 0, -53), # 0x1B lattice small + ( 4, 0, -10, 0, 0, 0, 0, 0, 0, 9, 0, -53), # 0x1C lattice left + ( 5, 0, -10, 0, 0, 0, 0, 0, 0, 9, 0, -53), # 0x1D lattice right + ( 97, 1, 0, 98, 1, 2, 0, 0, 43, 100, 0, 0), # 0x1E debris with torch +] + +TILE_NAMES = { + 0x00: "empty", 0x01: "floor", 0x02: "spike", 0x03: "pillar", + 0x04: "gate", 0x05: "stuck_floor", 0x06: "close_button", 0x07: "door_top_floor", + 0x08: "big_pillar_bottom", 0x09: "big_pillar_top", 0x0A: "potion", + 0x0B: "loose_floor", 0x0C: "door_top", 0x0D: "mirror", 0x0E: "debris", + 0x0F: "open_button", 0x10: "leveldoor_left", 0x11: "leveldoor_right", + 0x12: "chomper", 0x13: "torch", 0x14: "wall", 0x15: "skeleton", + 0x16: "sword", 0x17: "balcony_left", 0x18: "balcony_right", + 0x19: "lattice_pillar", 0x1A: "lattice_down", 0x1B: "lattice_small", + 0x1C: "lattice_left", 0x1D: "lattice_right", 0x1E: "debris_torch", +} + +COL_XH = [0, 4, 8, 12, 16, 20, 24, 28, 32, 36] + +# chtab -> directory/subfolder + id offset +# id_chtab_6_environment -> VPALACE, res(200+id).png +# id_chtab_7_environmentwall -> VPALACE (wall images also here, res(200+id)) +# id_chtab_1_flameswordpotion -> flames etc (skipped in static render) +CHTAB_ENV = 6 +CHTAB_WALL = 7 +CHTAB_FSP = 1 # flames/sword/potion (animated, skipped) + + +# ---- POP1 level file structure (see POP-DAT-FormatSpecifications.txt, sec 3.4) ---- +# Block layout (for the standard 2305-byte level): +# 0 .. 720 pop1 foretable (24 rooms x 30 bytes) +# 720 .. 1440 pop1 backtable (24 rooms x 30 bytes) +# 1440 .. 1696 door I (256 bytes, one per event line) +# 1696 .. 1952 door II (256 bytes) +# 1952 .. 2048 links (24 rooms x 4 bytes: L,R,Up,Dn) +# 2048 .. 2112 unknown I +# 2112 .. 2115 start position (room, location, direction) +# 2115 .. 2116 unknown II +# 2116 .. 2119 unknown III +# 2119 .. 2143 guard location (24) +# 2143 .. 2167 guard direction (24) +# 2167 .. 2191 unknown IV (a) +# 2191 .. 2215 unknown IV (b) +# 2215 .. 2239 guard skill (24) +# 2239 .. 2263 unknown IV (c) +# 2263 .. 2287 guard colour (24) +# 2287 .. 2303 unknown IV (d) +# 2303 .. 2305 0F 09 + +# foretable byte format: rrmccccc (2 random bits, 1 modifier bit, 5 code bits) +FG_RAND_MASK = 0xC0 +FG_MODBIT_MASK = 0x20 +FG_CODE_MASK = 0x1F + +# backtable: a tile modifier that depends on the group of the foretable code. +# group ids map foretable code -> "group" used to interpret backtable value. +# (see Table 7/8). We only need the group for door/event interpretation. +GROUP_OF_CODE = { + 0x00: "free", 0x01: "free", 0x02: "spike", 0x03: "none", + 0x04: "gate", 0x05: "none", 0x06: "event", 0x07: "tapest", + 0x08: "none", 0x09: "none", 0x0A: "potion", 0x0B: "none", + 0x0C: "ttop", 0x0D: "none", 0x0E: "none", 0x0F: "event", + 0x10: "none", 0x11: "none", 0x12: "chomp", 0x13: "none", + 0x14: "wall", 0x15: "none", 0x16: "none", 0x17: "none", + 0x18: "none", 0x19: "none", 0x1A: "none", 0x1B: "none", + 0x1C: "none", 0x1D: "none", 0x1E: "none", 0x1F: "none", +} + +# backtable modifier meanings per group (subset, from Table 8): +GATE_MOD = {0: "closed", 1: "open", 2: "with_lattice", 3: "alt_design"} +TTOP_MOD = {0: "normal", 1: "black", 2: "empty", 3: "?3", 4: "?4", 5: "?5", 6: "?6", 7: "?7"} +SPIKE_MOD = {0: "normal", 1: "barely_out", 2: "half_out", 3: "fully_out"} +CHOMP_MOD = {0: "normal", 1: "barely_out", 2: "half_out", 3: "fully_out", 4: "out?"} +POTION_MOD = {0: "blue", 1: "no_blue", 2: "window", 3: "spot?"} +TAPEST_MOD = {0: "normal", 1: "alt_design", 2: "black", 3: "empty"} +EVENT_MOD_DESC = { # group "event": the backtable byte IS the event id + 0: "closer", 1: "opener", +} + + +class Level: + """Full parsed POP1 level file.""" + def __init__(self, data): + self.data = data + self.rooms_fg = {} # room -> list[30] of (fg_byte, code, modbit) + self.rooms_bg = {} # room -> list[30] of backtable byte + for room in range(1, 25): + fg = []; bg = [] + for off in range(30): + i = (room - 1) * 30 + off + fb = data[i]; bb = data[720 + i] + fg.append((fb, fb & FG_CODE_MASK, (fb & FG_MODBIT_MASK) >> 5)) + bg.append(bb) + self.rooms_fg[room] = fg + self.rooms_bg[room] = bg + # door I / II (event lines 0..255) + self.door_i = data[1440:1696] + self.door_ii = data[1696:1952] + # links: 24 rooms x (L,R,Up,Dn) + self.links = {} + for room in range(1, 25): + b = data[1952 + (room - 1) * 4: 1952 + (room - 1) * 4 + 4] + self.links[room] = tuple(b) # (left, right, up, down) + # start position + self.start = tuple(data[2112:2115]) + # guards + self.guard_loc = list(data[2119:2143]) + self.guard_dir = list(data[2143:2167]) + self.guard_skill = list(data[2215:2239]) + self.guard_colour = list(data[2263:2287]) + + def door_for_event(self, event_id): + """Decode door I/II for an event line -> (room, location, trigger_next).""" + bi = self.door_i[event_id] + bii = self.door_ii[event_id] + # Byte I: t1 s4 s5 l1 l2 l3 l4 l5 + t1 = (bi >> 7) & 1 + s4 = (bi >> 6) & 1 + s5 = (bi >> 5) & 1 + l1 = (bi >> 4) & 1 + l2 = (bi >> 3) & 1 + l3 = (bi >> 2) & 1 + l4 = (bi >> 1) & 1 + l5 = bi & 1 + # Byte II: s1 s2 s3 00000 + s1 = (bii >> 7) & 1 + s2 = (bii >> 6) & 1 + s3 = (bii >> 5) & 1 + screen = (s1 << 4) | (s2 << 3) | (s3 << 2) | (s4 << 1) | s5 + loc = (l1 << 4) | (l2 << 3) | (l3 << 2) | (l4 << 1) | l5 + return screen, loc, t1 + + def backtable_desc(self, code, bb): + grp = GROUP_OF_CODE.get(code, "none") + tbl = {"gate": GATE_MOD, "ttop": TTOP_MOD, "spike": SPIKE_MOD, + "chomp": CHOMP_MOD, "potion": POTION_MOD, "tapest": TAPEST_MOD} + if code == 0x06 or code == 0x0F: # event group + act = EVENT_MOD_DESC.get(bb & 1, "?") + return f"event(id={bb}, {act})" + if grp in tbl: + return tbl[grp].get(bb, f"?0x{bb:02X}") + return f"0x{bb:02X}" + + +class Room: + def __init__(self, level, room_num): + self.level = level + self.room_num = room_num + self.fg = level.rooms_fg[room_num] + self.bg = level.rooms_bg[room_num] + self.left_room = None # соседняя комната слева (link L); load_leftroom + + def tile(self, row, col): + """Return (code, fg_byte, modbit, backtable_byte) for an in-room tile. + Out-of-range neighbours follow seg008 defaults: + col<0 -> берётся из ЛЕВОЙ комнаты (col 9), как load_leftroom(); + если левой комнаты нет — уровневая кромка = стена (20). + row<0 -> top level edge = floor (code 1, mod 0) + """ + if col < 0: + if self.left_room is not None and 0 <= row < 3: + return self.left_room.tile(row, 9) + return 20, 0, 0, 0 + if row < 0: + return 1, 0, 0, 0 + if not (0 <= row < 3 and 0 <= col < 10): + return 20, 0, 0, 0 + i = row * 10 + col + fb, code, mbit = self.fg[i] + return code, fb, mbit, self.bg[i] + + +# Log of every sprite loaded during a render, for the per-tile report. +SPRITE_LOG = [] + + +def load_sprite(id, chtab=CHTAB_ENV, reason=""): + """Load resource PNG для картинки id из набора chtab. + + База ресурса зависит от chtab (seg000.c:1109/1122): + chtab_6 environment -> res(200+id) + chtab_7 environmentwall -> res(360+id) + chtab_1 flame/sword/pot -> res(150+id) (анимация, статикой не грузим) + + Dungeon (IS_DUNGEON) cascade: try VDUNGEON first (dungeon-specific + overrides), then fall back to VPALACE (shared base set). + """ + if id <= 0: + return None + base = {CHTAB_ENV: 200, CHTAB_WALL: 360, CHTAB_FSP: 150}.get(chtab, 200) + fname = f"res{base + id}.png" + if IS_DUNGEON: + vd = os.path.join(VDUNGEON_DIR, fname) + if os.path.exists(vd): + try: + img = Image.open(vd).convert("RGBA") + CURRENT_TILE_LOG.append((id, "VDUNGEON", reason)) + return img + except Exception: + pass + vp = os.path.join(VPALACE_DIR, fname) + try: + img = Image.open(vp).convert("RGBA") + CURRENT_TILE_LOG.append((id, "VPALACE", reason)) + return img + except FileNotFoundError: + CURRENT_TILE_LOG.append((id, "MISSING", reason)) + return None + + +def blit(canvas, img, x, y): + """Composite img onto canvas at (x,y) using its alpha as mask (draw_main style).""" + if img is None: + return + w, h = img.size + if x + w <= 0 or y + h <= 0 or x >= canvas.width or y >= canvas.height: + return + canvas.alpha_composite(img, (x, y)) + + +def blit_black(canvas, img, x, y): + """blitters_9_black (seg008.c:1054): рисует МАСКУ спрайта ЧЁРНЫМ — + непрозрачная область формы заливается чёрным (тень-силуэт), а не + пикселями картинки. Используется для floor-right в draw_tile_floorright + (тень уступа тайла снизу-слева).""" + if img is None: + return + w, h = img.size + if x + w <= 0 or y + h <= 0 or x >= canvas.width or y >= canvas.height: + return + alpha = img.split()[3] + solid = Image.new("RGBA", img.size, (0, 0, 0, 255)) + canvas.alpha_composite(Image.composite(solid, Image.new("RGBA", img.size, (0, 0, 0, 0)), alpha), (x, y)) + + +# ---- Dungeon wall_pattern() (seg008.c:1928) ---- +# PRNG: seg009.c:321 random_seed = random_seed*214013 + 2531011; return (random_seed>>16) % (max+1) +_random_seed = 0 + + +def prandom(maxval): + global _random_seed + _random_seed = (_random_seed * 214013 + 2531011) & 0xFFFFFFFF + return (_random_seed >> 16) % (maxval + 1) + + +# wall sprite ids (chtab_7 / RSET_WALL = 7) res(200+id).png +RES_WALL_FACE_MAIN, RES_WALL_FACE_TOP, RES_WALL_CENTRE_BASE, RES_WALL_CENTRE_MAIN = 1, 2, 3, 4 +RES_WALL_RIGHT_BASE, RES_WALL_RIGHT_MAIN, RES_WALL_SINGLE_BASE, RES_WALL_SINGLE_MAIN = 5, 6, 7, 8 +RES_WALL_LEFT_BASE, RES_WALL_LEFT_MAIN, RES_WALL_DIVIDER1, RES_WALL_DIVIDER2 = 9, 10, 11, 12 +RES_WALL_RNDBLOCK = 13 +RES_WALL_MARK_TL, RES_WALL_MARK_BL, RES_WALL_MARK_TR, RES_WALL_MARK_BR = 14, 15, 16, 17 + +WALL_MODIFIER_SWS, WALL_MODIFIER_SWW, WALL_MODIFIER_WWS, WALL_MODIFIER_WWW = 0, 1, 2, 3 + +# Сплошная грань стены (chtab_7), индекс = wall_modifier (seg008.c:567/687): +WALL_FRAM_BOTTOM = [7, 9, 5, 3] # нижняя часть (draw_tile_bottom) +WALL_FRAM_MAIN = [8, 10, 6, 4] # основная часть (draw_tile_fore) + +LPOS = [58, 41, 37, 20, 16] # draw_left_mark vertical offsets +RPOS = [52, 42, 31, 21] # draw_right_mark vertical offsets + +TBL_LINE = [0, 10, 20] # смещение строки в 30-тайловом массиве комнаты + + +def wall_modifier(room, drawn_row, col): + """Модификатор стены (SWS/SWW/WWS/WWW = 0..3) по соседям слева/справа. + seg008.c:1255-1305 (без USE_FAKE_TILES): сосед считается стеной, если + его тип == wall(20). На кромке комнаты без линка сосед по умолчанию + считается стеной (wall_to_left/right = 1).""" + left_code = room.tile(drawn_row, col - 1)[0] if col > 0 else 20 + right_code = room.tile(drawn_row, col + 1)[0] if col < 9 else 20 + wall_left = (left_code == 20) + wall_right = (right_code == 20) + if wall_left and wall_right: + return WALL_MODIFIER_WWW # 3 + if wall_left: + return WALL_MODIFIER_WWS # 2 + if wall_right: + return WALL_MODIFIER_SWW # 1 + return WALL_MODIFIER_SWS # 0 + + +def _wall_add(canvas, which_table, id, xh, xl, ybottom, reason): + """Emulate ptr_add_table(RSET_WALL, id, xh, xl, ybottom, blit, 0). + which_table 0 -> backtable (blit transparent), 1 -> foretable. + RSET_WALL id -> res(200+id).png, cascade VDUNGEON->VPALACE.""" + img = load_sprite(id, CHTAB_WALL, reason=reason) + if img is None: + return + y = ybottom - img.height + 1 + x = xh * 8 + xl + blit(canvas, img, x, y) + + +def draw_left_mark(canvas, decal_variant, arg2, arg1, drawn_row, col): + image_id = RES_WALL_MARK_TL + if decal_variant % 2: + image_id = RES_WALL_MARK_BL + lv2 = 0 + if decal_variant > 3: + lv2 = arg1 + 6 + elif decal_variant > 1: + lv2 = arg2 + 6 + xh = COL_XH[col] + draw_bottom_y = 63 * drawn_row + 65 + _wall_add(canvas, 1, image_id, xh + (1 if decal_variant in (2, 3) else 0), + lv2, draw_bottom_y - LPOS[decal_variant], + reason=f"wall left-mark var{decal_variant}") + + +def draw_right_mark(canvas, decal_variant, arg1, drawn_row, col): + image_id = RES_WALL_MARK_TR + if decal_variant % 2: + image_id = RES_WALL_MARK_BR + a = 24 if decal_variant < 2 else arg1 - 3 + xh = COL_XH[col] + draw_bottom_y = 63 * drawn_row + 65 + _wall_add(canvas, 1, image_id, xh + (1 if decal_variant > 1 else 0), + a, draw_bottom_y - RPOS[decal_variant], + reason=f"wall right-mark var{decal_variant}") + + +def wall_pattern(canvas, room, drawn_row, col, which_part, which_table): + """Replay seg008.c wall_pattern() for a dungeon wall tile. + which_part: 0 = bottom part (draw_tile_bottom), 1 = fore part (draw_tile_fore). + which_table: 0 = backtable, 1 = foretable.""" + global _random_seed + xh = COL_XH[col] + draw_bottom_y = 63 * drawn_row + 65 + draw_main_y = draw_bottom_y - 3 + + saved_seed = _random_seed + # seg008.c:1941 — зерно зависит от НОМЕРА КОМНАТЫ (не строки!): + # random_seed = drawn_room + tbl_line[drawn_row] + drawn_col + room_num = getattr(room, "room_num", 1) + _random_seed = room_num + TBL_LINE[drawn_row] + col + prandom(1) # discard + is_dungeon = IS_DUNGEON + + middle_divider = prandom(1) + middle_divider_offset = prandom(4) + bottom_divider = prandom(1) + bottom_divider_offset = prandom(4) + + # Модификатор стены (какие соседи — стены) — тот же, что для сплошной грани. + bg = wall_modifier(room, drawn_row, col) + + if bg == WALL_MODIFIER_WWW: + if which_part != 0: + if prandom(4) == 0: + _wall_add(canvas, which_table, RES_WALL_RNDBLOCK, xh, 0, + draw_bottom_y - 42, reason="wall rndblock") + _wall_add(canvas, which_table, RES_WALL_DIVIDER1 + middle_divider, xh + 1, + middle_divider_offset, draw_bottom_y - 21, reason="wall middle-divider") + _wall_add(canvas, which_table, RES_WALL_DIVIDER1 + bottom_divider, xh, + bottom_divider_offset, draw_bottom_y, reason="wall bottom-divider") + if which_part != 0 and is_dungeon: + if prandom(4) == 0: + draw_right_mark(canvas, prandom(3), middle_divider_offset, drawn_row, col) + if prandom(4) == 0: + draw_left_mark(canvas, prandom(4), + middle_divider_offset - middle_divider, + bottom_divider_offset - bottom_divider, drawn_row, col) + elif bg == WALL_MODIFIER_SWS: + if is_dungeon and which_part != 0: + if prandom(6) == 0: + draw_left_mark(canvas, prandom(1), + middle_divider_offset - middle_divider, + bottom_divider_offset - bottom_divider, drawn_row, col) + elif bg == WALL_MODIFIER_SWW: + if which_part != 0: + if prandom(4) == 0: + _wall_add(canvas, which_table, RES_WALL_RNDBLOCK, xh, 0, + draw_bottom_y - 42, reason="wall rndblock") + _wall_add(canvas, which_table, RES_WALL_DIVIDER1 + middle_divider, xh + 1, + middle_divider_offset, draw_bottom_y - 21, reason="wall middle-divider") + if is_dungeon: + if prandom(4) == 0: + draw_right_mark(canvas, prandom(3), middle_divider_offset, drawn_row, col) + if prandom(4) == 0: + draw_left_mark(canvas, prandom(3), + middle_divider_offset - middle_divider, + bottom_divider_offset - bottom_divider, drawn_row, col) + elif bg == WALL_MODIFIER_WWS: + if which_part != 0: + _wall_add(canvas, which_table, RES_WALL_DIVIDER1 + middle_divider, xh + 1, + middle_divider_offset, draw_bottom_y - 21, reason="wall middle-divider") + _wall_add(canvas, which_table, RES_WALL_DIVIDER1 + bottom_divider, xh, + bottom_divider_offset, draw_bottom_y, reason="wall bottom-divider") + if which_part != 0 and is_dungeon: + if prandom(4) == 0: + draw_right_mark(canvas, prandom(1) + 2, middle_divider_offset, drawn_row, col) + if prandom(4) == 0: + draw_left_mark(canvas, prandom(4), + middle_divider_offset - middle_divider, + bottom_divider_offset - bottom_divider, drawn_row, col) + + _random_seed = saved_seed + + +# Per-tile sprite log: draw_tile resets CURRENT_TILE_LOG, load_sprite appends to it. +CURRENT_TILE_LOG = [] +TILE_LOGS = {} # (row, col) -> list of (id, source, reason) + +# seg008.c:1108 — вертикальные слайсы решётки ворот (portcullis). +DOOR_FRAM_SLICE = [67, 59, 58, 57, 56, 55, 54, 53, 52] +# seg008.c:517 — правый край шипов по кадру get_spike_frame. +SPIKES_FRAM_RIGHT = [0, 134, 135, 136, 137, 138, 137, 135, 134, 0] +# seg008.c:450 — «blueline»-декали на ЗАДНЕЙ стене (силуэты кладки/окна), +# рисуются правым соседом по МОДИФИКАТОРУ соседа слева: +# tile_left == empty -> blueline_fram1[mod] (окно/решётка-фрагменты 124..126) +# на высоте blueline_fram_y[mod] + main_y; +# tile_left == floor -> blueline_fram3[mod] (кирпичные силуэты 44/45) +# на main_y - 20 (если mod != !!level_type). +BLUELINE_FRAM1 = [0, 124, 125, 126] +BLUELINE_FRAM_Y = [0, -20, -20, 0] +BLUELINE_FRAM3 = [44, 44, 45, 45] + + +def draw_gate_back(canvas, modifier_left, draw_xh, draw_bottom_y, draw_main_y): + """seg008.c:1110 draw_gate_back — портикулис (решётка) ворот. Рисуется + на тайле СПРАВА от ворот (draw_tile_anim_right, tile_left==gate). У нас + это col 0 room1, где сосед слева — ворота из room 5. modifier_left — + модификатор тайла-ворот (степень поднятия решётки).""" + x = draw_xh * 8 + gate_top_y = draw_bottom_y - 62 + gate_openness = (min(modifier_left, 188) >> 2) + 1 + gate_bottom_y = draw_main_y - gate_openness + if gate_bottom_y + 12 < draw_main_y: + img = load_sprite(50, reason="gate bottom+B") # gate поднята высоко + if img: + blit(canvas, img, x, gate_bottom_y - img.height + 1) + else: + # id 47 (правая грань ворот) уже рисует draw_tile_right — не дублируем. + img = load_sprite(51, reason="gate bottom") + if img: + blit(canvas, img, x, (gate_bottom_y - 2) - img.height + 1) + # Стек 8px-слайсов решётки снизу вверх. + ybottom = gate_bottom_y - 12 + if ybottom < 192: + while ybottom >= 0 and ybottom > 7 and (ybottom - 7) > gate_top_y: + img = load_sprite(52, reason="gate slice 8px") + if img: + blit(canvas, img, x, ybottom - img.height + 1) + ybottom -= 8 + gate_frame = ybottom - gate_top_y + 1 + if 0 < gate_frame < 9: + img = load_sprite(DOOR_FRAM_SLICE[gate_frame], reason="gate top slice") + if img: + blit(canvas, img, x, ybottom - img.height + 1) + + +def draw_tile(canvas, room, drawn_row, col, left_edge_code=20): + global CURRENT_TILE_LOG + CURRENT_TILE_LOG = [] + code, fg_byte, modbit, bg_mod = room.tile(drawn_row, col) + # tile_left: колонка слева. col-1 == -1 -> Room.tile берёт из левой + # комнаты (или уровневая кромка = стена), см. Room.tile / load_leftroom. + left_code, _, _, left_mod = room.tile(drawn_row, col - 1) + draw_xh = COL_XH[col] + x = draw_xh * 8 + draw_bottom_y = 63 * drawn_row + 65 + draw_main_y = draw_bottom_y - 3 + + # For topright/floorright we need the tile below-left. + # Room below = same room (no room links) -> row_below = drawn_row+1 if <3 else treat as empty/floor top edge. + row_below = drawn_row + 1 + if row_below > 2: + # below the bottom row of the level: floor top edge + rbl_code, rbl_mod = 1, 0 + else: + rbl_code, _, _, rbl_mod = room.tile(row_below, col - 1) + row_below_left_code = rbl_code + + t = TILE_TABLE[code] + lt = TILE_TABLE[left_code] + + # ---- draw_tile_floorright (seg008.c:392) ---- + # Рисуется ТОЛЬКО когда can_see_bottomleft(): текущий тайл — + # empty/bigpillar_top/doortop/lattice_down. Иначе topright соседа + # снизу-слева перекрыт и рисовать его нельзя (иначе «арки» повсюду — + # это была главная причина замусоренного рендера). + if code in (0, 9, 12, 26): # can_see_bottomleft + _draw_topright(canvas, row_below_left_code, rbl_mod, x, draw_bottom_y, + reason=f"topright(below-left tile {row_below_left_code:02X})") + if lt[4]: # tile_table[tile_left].floor_right + # blitters_9_black: ЧЁРНЫЙ силуэт (тень уступа), не светлый спрайт. + img = load_sprite(42, reason=f"floor-right shadow(left tile {left_code:02X})") + blit_black(canvas, img, x, draw_main_y + TILE_TABLE[1][5] - (img.height if img else 0) + 1) + + # ---- draw_tile_anim_topright ---- (gate top mask) - skipped: no anim + # ---- draw_tile_right (seg008.c:456) ---- + if code != 20: # у самой стены правый край не рисуется + level_type = 0 if IS_DUNGEON else 1 + if left_code == 20: + # сосед слева — стена: её правая грань в текущую колонку (chtab_7 id 1) + img = load_sprite(1, CHTAB_WALL, reason="wall right-face(left tile 14)") + ry = TILE_TABLE[20][5] # right_y стены = 2 + blit(canvas, img, x, ry + draw_main_y - (img.height if img else 0) + 1) + elif left_code == 0: + # сосед слева — empty: фрагмент окна/решётки по его модификатору + if left_mod <= 3: + bid = BLUELINE_FRAM1[left_mod] + if bid: + img = load_sprite(bid, reason=f"blueline-window(left empty mod{left_mod})") + blit(canvas, img, x, + BLUELINE_FRAM_Y[left_mod] + draw_main_y - (img.height if img else 0) + 1) + elif left_code == 1: + # сосед слева — floor: правый треугольник (42) + СИЛУЭТ КЛАДКИ (44/45) + # на задней стене, если модификатор пола != !!level_type. + img = load_sprite(42, reason="floor-right(part of left tile 01)") + ry = TILE_TABLE[1][5] # right_y пола = 2 + blit(canvas, img, x, ry + draw_main_y - (img.height if img else 0) + 1) + num = left_mod if left_mod <= 3 else 0 + if num != level_type: + bid = BLUELINE_FRAM3[num] + img = load_sprite(bid, reason=f"brick-silhouette(left floor mod{num})") + blit(canvas, img, x, draw_main_y - 20 - (img.height if img else 0) + 1) + else: + rid = lt[3] # tile_left.right_id + if rid: + ry = lt[5] + img = load_sprite(rid, reason=f"right(part of left tile {left_code:02X})") + blit(canvas, img, x, ry + draw_main_y - (img.height if img else 0) + 1) + # stripe: only drawn when tbl_level_type != 0 (palace). Dungeon: skip. + sid = lt[6] + if sid and not IS_DUNGEON: + img = load_sprite(sid, reason=f"stripe(left tile {left_code:02X})") + blit(canvas, img, x, draw_main_y - 27 - (img.height if img else 0) + 1) + + # ---- draw_tile_anim_right (seg008.c:530) ---- + # «Анимированные» части соседа слева, проецируемые в текущий тайл. + # Статикой (кадр покоя) рисуем те, что формируют ГЕОМЕТРИЮ фона: + # gate -> решётка (portcullis) на границе с левой комнатой; + # loose -> правый треугольный край шаткой плиты (иначе провал в полу); + # spike -> статичный кадр шипов по модификатору. + # torch-flame/chomper — чистая анимация, пропускаем (отдельные sprite_t). + if left_code == 4: # tiles_4_gate + draw_gate_back(canvas, left_mod, draw_xh, draw_bottom_y, draw_main_y) + elif left_code == 11: # tiles_11_loose — правый край плиты (loose_fram_right[0]=42) + img = load_sprite(42, reason="loose right (part of left tile 0B)") + if img: + blit(canvas, img, x, (draw_bottom_y - 1) - img.height + 1) + elif left_code == 2: # tiles_2_spike — статичный кадр (spikes_fram_right) + sf = 5 if (left_mod & 0x80) else left_mod + sid = SPIKES_FRAM_RIGHT[sf] if 0 <= sf < len(SPIKES_FRAM_RIGHT) else 0 + if sid: + img = load_sprite(sid, reason="spike right (part of left tile 02)") + if img: + blit(canvas, img, x, (draw_main_y - 7) - img.height + 1) + + # ---- draw_tile_bottom (seg008.c:570) ---- + if code == 20: + # Сплошная НИЖНЯЯ грань стены (chtab_7 wall_fram_bottom) + декали. + wmod = wall_modifier(room, drawn_row, col) + img = load_sprite(WALL_FRAM_BOTTOM[wmod], CHTAB_WALL, reason="wall bottom-face") + if img: + blit(canvas, img, x, draw_bottom_y - img.height + 1) + wall_pattern(canvas, room, drawn_row, col, 0, 0) + else: + bid = t[8] + img = load_sprite(bid, reason=f"bottom({TILE_NAMES.get(code,'?')})") + if img: + blit(canvas, img, x, draw_bottom_y - img.height + 1) + + # ---- draw_loose (seg008.c:600) — статический кадр loose floor ---- + # Нижняя часть шаткой плиты (loose_fram_bottom[0]=43) на draw_bottom_y. + if code == 11: # 0x0B loose floor + img = load_sprite(43, reason="loose bottom (draw_loose)") + if img: + blit(canvas, img, x, draw_bottom_y - img.height + 1) + + # ---- draw_tile_base (seg008.c:611) ---- + base_id = t[0] + base_y = t[2] + ybottom = draw_main_y + if left_code == 0x1A and code == 0x0C: + # seg008:622 «Lattice + door A»: у doortop своей базы нет (base_id 0), + # и рядом с аркой (lattice_down слева) он рисуется ОДНИМ спрайтом 6, + # опущенным на 3 px. Без этой ветки на месте тайла чёрная дыра. + base_id = 6 + ybottom += 3 + elif code == 11: # loose floor: базовая (верхняя) часть = loose_fram_left[0] = 41 + base_id = 41 + img = load_sprite(base_id, reason=f"base({TILE_NAMES.get(code,'?')})") + if img: + blit(canvas, img, x, base_y + ybottom - img.height + 1) + + # ---- draw_tile_anim ---- (spike/potion/chomper animated) skipped + + # ---- draw_tile_fore (seg008.c:690) ---- + if code == 20: + # Основная (средняя) грань стены (chtab_7 wall_fram_main) + декали/марки. + wmod = wall_modifier(room, drawn_row, col) + img = load_sprite(WALL_FRAM_MAIN[wmod], CHTAB_WALL, reason="wall main-face") + if img: + blit(canvas, img, x, draw_main_y - img.height + 1) + wall_pattern(canvas, room, drawn_row, col, 1, 1) + fid = t[9] + if fid: + # fore_x — в единицах xh (×8 пикс!), не в пикселях: seg008.c:743 + # xh = fore_x + draw_xh, затем x = xh*8. fore_y — в пикселях. + fx = t[10] * 8 + x + fy = t[11] + draw_main_y + img = load_sprite(fid, reason=f"fore({TILE_NAMES.get(code,'?')})") + if img: + blit(canvas, img, fx, fy - img.height + 1) + + # Save per-tile log (keyed by logical room position: row=drawn_row, col) + TILE_LOGS[(drawn_row, col)] = list(CURRENT_TILE_LOG) + + +def _draw_topright(canvas, tiletype, modifier, x, draw_bottom_y, reason=""): + if tiletype in (7, 12): # doortop_with_floor / doortop -> palace uses frame + return + elif tiletype == 20: # wall + img = load_sprite(2, CHTAB_WALL, reason=reason) + else: + tid = TILE_TABLE[tiletype][7] + if tid == 0: + return + img = load_sprite(tid, reason=reason) + if img: + blit(canvas, img, x, draw_bottom_y - img.height + 1) + + +def render_room(room_num): + with open(os.path.join(LEVEL_DIR, f"res{2000 + room_num}.bin"), "rb") as f: + data = f.read() + level = Level(data) + room = Room(level, room_num) + # load_leftroom(): левая комната по линку L — её col 9 виден на границе + # (col 0) текущей комнаты (напр. решётка ворот из соседней комнаты). + left_link = level.links[room_num][0] + if left_link: + room.left_room = Room(level, left_link) + + canvas = Image.new("RGBA", (320, 192), (0, 0, 0, 0)) + # rows drawn 2,1,0 (bottom first) + for drawn_row in (2, 1, 0): + for col in range(10): + draw_tile(canvas, room, drawn_row, col) + + return canvas, room, level + + +def render_single_tile(code, modifier=0, left_code=20, below_left_code=1): + """Render one tile in isolation on a 40x64 canvas (matching SDLPoP tile box).""" + TILE_W, TILE_H = 40, 64 + canvas = Image.new("RGBA", (TILE_W, TILE_H), (0, 0, 0, 0)) + # Place tile as if it were at drawn_row=2 (bottom), col=0 + drawn_row = 2 + col = 0 + # Build a fake Room backed by single values + class FakeRoom: + def tile(self, r, c): + if c < 0: + return left_code, 0, 0, 0 + if r < 0: + return 1, 0, 0, 0 + if r == drawn_row and c == 0: + return code, 0, 0, modifier + return below_left_code, 0, 0, 0 + room = FakeRoom() + draw_tile(canvas, room, drawn_row, col, left_edge_code=left_code) + return canvas + + +def main(): + import sys + room_num = 1 + if len(sys.argv) > 1: + room_num = int(sys.argv[1]) + + canvas, room, level = render_room(room_num) + out = f"./room{room_num}_render.png" + # Прозрачный фон (задняя чёрная стена, которую мы не рисуем) -> ЧЁРНЫМ, + # а не белым как показывает вьюер прозрачность. Композитим по альфе. + flat = Image.new("RGB", canvas.size, (0, 0, 0)) + flat.paste(canvas, (0, 0), canvas) + flat.save(out) + print(f"Saved {out} ({canvas.width}x{canvas.height})") + + # debug grid with code labels (на чёрном фоне) + from PIL import ImageDraw + dbg = flat.copy() + d = ImageDraw.Draw(dbg) + for r in range(3): + by = 63 * r + 65 + for c in range(10): + x = COL_XH[c] * 8 + d.rectangle([x, by - 63, x + 31, by], outline=(255, 0, 0), width=1) + code, _, _, _ = room.tile(r, c) + d.text((x + 2, by - 60), f"{code:02X}", fill=(255, 255, 0)) + dbg.save(f"./room{room_num}_debug.png") + print(f"Saved room{room_num}_debug.png") + + # Per-tile individual renders (the 30 tiles of the room) + # For proper neighbour context, render each tile inside its real room, + # then crop its 32x64 (40x64) column box. + tiles_dir = f"./tiles_room{room_num}" + os.makedirs(tiles_dir, exist_ok=True) + for r in range(3): + by = 63 * r + 65 + for c in range(10): + x = COL_XH[c] * 8 + code, _, _, _ = room.tile(r, c) + # crop the column box (40 wide to include left overlap, 64 tall above bottom) + box = (x, by - 64, x + 40, by) + tile_img = canvas.crop(box) + tile_img.save(os.path.join(tiles_dir, f"r{r}c{c}_{code:02X}.png")) + print(f"Saved 30 tile crops to {tiles_dir}/") + # Per-tile report: which images each tile pulls and why. + print() + print("=" * 78) + print(f"PER-TILE SPRITE REPORT (room {room_num}, {'DUNGEON' if IS_DUNGEON else 'PALACE'})") + print("=" * 78) + for r in range(3): + for c in range(10): + code, fg_byte, mbit, bb = room.tile(r, c) + name = TILE_NAMES.get(code, f"?0x{code:02X}") + btd = level.backtable_desc(code, bb) + line = (f"\n[{r},{c}] code=0x{code:02X} ({name}) " + f"fg=0x{fg_byte:02X} mbit={mbit} backtable=0x{bb:02X} ({btd})") + # If this tile is an event activator (group 'event'), show the door it drives. + if code in (0x06, 0x0F): + scr, loc, trig = level.door_for_event(bb) + line += f"\n -> DOOR EVENT id={bb}: screen={scr} loc={loc} trigger_next={trig}" + print(line) + logs = TILE_LOGS.get((r, c), []) + if not logs: + print(" (no sprites drawn)") + for sid, source, reason in logs: + print(f" res{200+sid:03d}.png <- {source:8s} {reason}") + print() + print("=" * 78) + + # Level-level summary: start position, links, door events, guards. + print() + print("LEVEL STRUCTURE SUMMARY") + print("-" * 78) + print(f"start position: room={level.start[0]} loc={level.start[1]} dir={level.start[2]}") + links = level.links.get(room_num, (0, 0, 0, 0)) + print(f"room {room_num} links: L={links[0]} R={links[1]} Up={links[2]} Dn={links[3]}") + # guard in this room + gi = room_num - 1 + gloc = level.guard_loc[gi] + if gloc < 30: + gr, gc = gloc // 10, gloc % 10 + print(f"guard: loc=tile(r{gr},c{gc}) dir={level.guard_dir[gi]} " + f"skill={level.guard_skill[gi]} colour={level.guard_colour[gi]}") + else: + print("guard: none") + # door events driven by any tile in this room + print("door events referenced by this room's tiles:") + seen = set() + for off in range(30): + fb, code, mbit = room.fg[off] + bb = room.bg[off] + if code in (0x06, 0x0F): + if bb not in seen: + seen.add(bb) + scr, loc, trig = level.door_for_event(bb) + print(f" event id={bb} (tile off {off}): screen={scr} loc={loc} trigger_next={trig}") + if not seen: + print(" (none)") + + print() + print("Room", room_num, "foreground codes (3 rows x 10 cols):") + for r in range(3): + print(" ", " ".join(f"{room.tile(r,c)[0]:02X}" for c in range(10))) + + +if __name__ == "__main__": + main()